@ainyc/canonry 4.56.0 → 4.57.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/assets/agent-workspace/skills/aero/references/orchestration.md +1 -1
- package/assets/agent-workspace/skills/canonry/SKILL.md +1 -1
- package/assets/assets/{BacklinksPage-yWx_BBLG.js → BacklinksPage-CmeFZ8UJ.js} +1 -1
- package/assets/assets/{ChartPrimitives-gqioJ07n.js → ChartPrimitives-D7C1Cp8w.js} +1 -1
- package/assets/assets/{ProjectPage-D1tnXJ2L.js → ProjectPage-Y6uCyjGb.js} +4 -4
- package/assets/assets/{RunRow-DEr_yQLw.js → RunRow-BntNdrgM.js} +1 -1
- package/assets/assets/{RunsPage-DMrl5Fhn.js → RunsPage-Btp6qn10.js} +1 -1
- package/assets/assets/{SettingsPage-Dp0TXf34.js → SettingsPage-DkyNiU2i.js} +1 -1
- package/assets/assets/{TrafficPage-POy4iHHt.js → TrafficPage-CBl4Mwdc.js} +1 -1
- package/assets/assets/{TrafficSourceDetailPage-BewjZ53n.js → TrafficSourceDetailPage-BZzuWCn-.js} +1 -1
- package/assets/assets/{extract-error-message-C0GGpK4T.js → extract-error-message-De8_qAzs.js} +1 -1
- package/assets/assets/index-9NRlymgj.css +1 -0
- package/assets/assets/{index-D0A-UvNH.js → index-XUKhruAg.js} +5 -5
- package/assets/assets/{server-traffic-B5rtrB-q.js → server-traffic-bn9LSZN9.js} +1 -1
- package/assets/assets/{trash-2-CUczQ2Yl.js → trash-2-B5clF2rU.js} +1 -1
- package/assets/index.html +2 -2
- package/dist/{chunk-I2LAM5IM.js → chunk-HL6JZUEW.js} +273 -212
- package/dist/cli.js +1 -1
- package/dist/index.js +1 -1
- package/package.json +9 -9
- package/assets/assets/index-_jdnW4nh.css +0 -1
|
@@ -577,11 +577,11 @@ function checkLatestVersionForServer(opts) {
|
|
|
577
577
|
|
|
578
578
|
// src/server.ts
|
|
579
579
|
import { createRequire as createRequire4 } from "module";
|
|
580
|
-
import
|
|
580
|
+
import crypto36 from "crypto";
|
|
581
581
|
import fs15 from "fs";
|
|
582
582
|
import path15 from "path";
|
|
583
583
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
584
|
-
import { eq as
|
|
584
|
+
import { eq as eq43 } from "drizzle-orm";
|
|
585
585
|
import Fastify from "fastify";
|
|
586
586
|
|
|
587
587
|
// ../api-routes/src/index.ts
|
|
@@ -29079,13 +29079,68 @@ async function executeBingInspectSitemap(db, runId, projectId, opts) {
|
|
|
29079
29079
|
}
|
|
29080
29080
|
}
|
|
29081
29081
|
|
|
29082
|
-
// src/
|
|
29082
|
+
// src/coverage-refresh.ts
|
|
29083
29083
|
import crypto29 from "crypto";
|
|
29084
|
+
import { and as and25, desc as desc16, eq as eq32, inArray as inArray11 } from "drizzle-orm";
|
|
29085
|
+
var log6 = createLogger("CoverageRefresh");
|
|
29086
|
+
var COVERAGE_REFRESH_MIN_INTERVAL_MS = 60 * 60 * 1e3;
|
|
29087
|
+
var ACTIVE_OR_DONE_STATUSES = [
|
|
29088
|
+
RunStatuses.queued,
|
|
29089
|
+
RunStatuses.running,
|
|
29090
|
+
RunStatuses.completed,
|
|
29091
|
+
RunStatuses.partial
|
|
29092
|
+
];
|
|
29093
|
+
var defaultDeps = { executeInspectSitemap };
|
|
29094
|
+
async function maybeRefreshGscCoverage(db, config, projectId, deps = defaultDeps, nowMs = Date.now()) {
|
|
29095
|
+
const project = db.select({ canonicalDomain: projects.canonicalDomain }).from(projects).where(eq32(projects.id, projectId)).get();
|
|
29096
|
+
if (!project) return null;
|
|
29097
|
+
const { clientId, clientSecret } = getGoogleAuthConfig(config);
|
|
29098
|
+
if (!clientId || !clientSecret) return null;
|
|
29099
|
+
const conn = getGoogleConnection(config, project.canonicalDomain, "gsc");
|
|
29100
|
+
if (!conn?.refreshToken || !conn.propertyId) return null;
|
|
29101
|
+
const recent = db.select({ createdAt: runs.createdAt }).from(runs).where(
|
|
29102
|
+
and25(
|
|
29103
|
+
eq32(runs.projectId, projectId),
|
|
29104
|
+
eq32(runs.kind, RunKinds["inspect-sitemap"]),
|
|
29105
|
+
inArray11(runs.status, ACTIVE_OR_DONE_STATUSES)
|
|
29106
|
+
)
|
|
29107
|
+
).orderBy(desc16(runs.createdAt)).limit(1).get();
|
|
29108
|
+
if (recent) {
|
|
29109
|
+
const ageMs = nowMs - Date.parse(recent.createdAt);
|
|
29110
|
+
if (Number.isFinite(ageMs) && ageMs < COVERAGE_REFRESH_MIN_INTERVAL_MS) {
|
|
29111
|
+
log6.info("skip.recent", { projectId, ageMs });
|
|
29112
|
+
return null;
|
|
29113
|
+
}
|
|
29114
|
+
}
|
|
29115
|
+
const runId = crypto29.randomUUID();
|
|
29116
|
+
db.insert(runs).values({
|
|
29117
|
+
id: runId,
|
|
29118
|
+
projectId,
|
|
29119
|
+
kind: RunKinds["inspect-sitemap"],
|
|
29120
|
+
status: RunStatuses.queued,
|
|
29121
|
+
trigger: RunTriggers.scheduled,
|
|
29122
|
+
createdAt: new Date(nowMs).toISOString()
|
|
29123
|
+
}).run();
|
|
29124
|
+
log6.info("refresh.start", { projectId, runId });
|
|
29125
|
+
try {
|
|
29126
|
+
await deps.executeInspectSitemap(db, runId, projectId, { config });
|
|
29127
|
+
} catch (err) {
|
|
29128
|
+
log6.error("refresh.failed", {
|
|
29129
|
+
projectId,
|
|
29130
|
+
runId,
|
|
29131
|
+
error: err instanceof Error ? err.message : String(err)
|
|
29132
|
+
});
|
|
29133
|
+
}
|
|
29134
|
+
return runId;
|
|
29135
|
+
}
|
|
29136
|
+
|
|
29137
|
+
// src/commoncrawl-sync.ts
|
|
29138
|
+
import crypto30 from "crypto";
|
|
29084
29139
|
import path11 from "path";
|
|
29085
|
-
import { and as
|
|
29086
|
-
var
|
|
29140
|
+
import { and as and26, eq as eq33, sql as sql14 } from "drizzle-orm";
|
|
29141
|
+
var log7 = createLogger("CommonCrawlSync");
|
|
29087
29142
|
var INSERT_CHUNK_SIZE = 1e4;
|
|
29088
|
-
function
|
|
29143
|
+
function defaultDeps2() {
|
|
29089
29144
|
return {
|
|
29090
29145
|
downloadFile,
|
|
29091
29146
|
queryBacklinks,
|
|
@@ -29095,7 +29150,7 @@ function defaultDeps() {
|
|
|
29095
29150
|
};
|
|
29096
29151
|
}
|
|
29097
29152
|
async function executeReleaseSync(db, syncId, opts) {
|
|
29098
|
-
const deps = { ...
|
|
29153
|
+
const deps = { ...defaultDeps2(), ...opts.deps };
|
|
29099
29154
|
const release = opts.release;
|
|
29100
29155
|
try {
|
|
29101
29156
|
if (!isValidReleaseId(release)) {
|
|
@@ -29108,7 +29163,7 @@ async function executeReleaseSync(db, syncId, opts) {
|
|
|
29108
29163
|
phaseDetail: "downloading vertices + edges",
|
|
29109
29164
|
updatedAt: downloadStartedAt,
|
|
29110
29165
|
error: null
|
|
29111
|
-
}).where(
|
|
29166
|
+
}).where(eq33(ccReleaseSyncs.id, syncId)).run();
|
|
29112
29167
|
const paths = ccReleasePaths(release);
|
|
29113
29168
|
const releaseCacheDir = path11.join(deps.cacheDir, release);
|
|
29114
29169
|
const vertexPath = path11.join(releaseCacheDir, paths.vertexFilename);
|
|
@@ -29131,7 +29186,7 @@ async function executeReleaseSync(db, syncId, opts) {
|
|
|
29131
29186
|
vertexSha256: vertex.sha256,
|
|
29132
29187
|
edgesSha256: edges.sha256,
|
|
29133
29188
|
updatedAt: downloadFinishedAt
|
|
29134
|
-
}).where(
|
|
29189
|
+
}).where(eq33(ccReleaseSyncs.id, syncId)).run();
|
|
29135
29190
|
const allProjects = db.select().from(projects).all();
|
|
29136
29191
|
const targets = Array.from(new Set(allProjects.map((p) => p.canonicalDomain)));
|
|
29137
29192
|
let rows = [];
|
|
@@ -29147,15 +29202,15 @@ async function executeReleaseSync(db, syncId, opts) {
|
|
|
29147
29202
|
}
|
|
29148
29203
|
const queriedAt = deps.now().toISOString();
|
|
29149
29204
|
db.transaction((tx) => {
|
|
29150
|
-
tx.delete(backlinkDomains).where(
|
|
29151
|
-
tx.delete(backlinkSummaries).where(
|
|
29205
|
+
tx.delete(backlinkDomains).where(eq33(backlinkDomains.releaseSyncId, syncId)).run();
|
|
29206
|
+
tx.delete(backlinkSummaries).where(eq33(backlinkSummaries.releaseSyncId, syncId)).run();
|
|
29152
29207
|
const expanded = [];
|
|
29153
29208
|
for (const r of rows) {
|
|
29154
29209
|
const projectIds = projectsByDomain.get(r.targetDomain);
|
|
29155
29210
|
if (!projectIds) continue;
|
|
29156
29211
|
for (const projectId of projectIds) {
|
|
29157
29212
|
expanded.push({
|
|
29158
|
-
id:
|
|
29213
|
+
id: crypto30.randomUUID(),
|
|
29159
29214
|
projectId,
|
|
29160
29215
|
releaseSyncId: syncId,
|
|
29161
29216
|
release,
|
|
@@ -29175,7 +29230,7 @@ async function executeReleaseSync(db, syncId, opts) {
|
|
|
29175
29230
|
const projectRows = rowsByProject.get(p.id) ?? [];
|
|
29176
29231
|
const summary = computeSummary(projectRows);
|
|
29177
29232
|
tx.insert(backlinkSummaries).values({
|
|
29178
|
-
id:
|
|
29233
|
+
id: crypto30.randomUUID(),
|
|
29179
29234
|
projectId: p.id,
|
|
29180
29235
|
releaseSyncId: syncId,
|
|
29181
29236
|
release,
|
|
@@ -29207,8 +29262,8 @@ async function executeReleaseSync(db, syncId, opts) {
|
|
|
29207
29262
|
domainsDiscovered: rows.length,
|
|
29208
29263
|
updatedAt: finishedAt,
|
|
29209
29264
|
error: null
|
|
29210
|
-
}).where(
|
|
29211
|
-
|
|
29265
|
+
}).where(eq33(ccReleaseSyncs.id, syncId)).run();
|
|
29266
|
+
log7.info("sync.completed", {
|
|
29212
29267
|
syncId,
|
|
29213
29268
|
release,
|
|
29214
29269
|
projectsProcessed: allProjects.length,
|
|
@@ -29220,7 +29275,7 @@ async function executeReleaseSync(db, syncId, opts) {
|
|
|
29220
29275
|
try {
|
|
29221
29276
|
deps.enqueueAutoExtract({ projectId: p.id, release });
|
|
29222
29277
|
} catch (err) {
|
|
29223
|
-
|
|
29278
|
+
log7.error("auto-extract.enqueue-failed", {
|
|
29224
29279
|
syncId,
|
|
29225
29280
|
release,
|
|
29226
29281
|
projectId: p.id,
|
|
@@ -29237,8 +29292,8 @@ async function executeReleaseSync(db, syncId, opts) {
|
|
|
29237
29292
|
error: errorMsg,
|
|
29238
29293
|
phaseDetail: null,
|
|
29239
29294
|
updatedAt: finishedAt
|
|
29240
|
-
}).where(
|
|
29241
|
-
|
|
29295
|
+
}).where(eq33(ccReleaseSyncs.id, syncId)).run();
|
|
29296
|
+
log7.error("sync.failed", { syncId, release, error: errorMsg });
|
|
29242
29297
|
throw err;
|
|
29243
29298
|
}
|
|
29244
29299
|
}
|
|
@@ -29271,11 +29326,11 @@ function computeSummary(rows) {
|
|
|
29271
29326
|
}
|
|
29272
29327
|
|
|
29273
29328
|
// src/backlink-extract.ts
|
|
29274
|
-
import
|
|
29329
|
+
import crypto31 from "crypto";
|
|
29275
29330
|
import fs11 from "fs";
|
|
29276
|
-
import { and as
|
|
29277
|
-
var
|
|
29278
|
-
function
|
|
29331
|
+
import { and as and27, desc as desc17, eq as eq34 } from "drizzle-orm";
|
|
29332
|
+
var log8 = createLogger("BacklinkExtract");
|
|
29333
|
+
function defaultDeps3() {
|
|
29279
29334
|
return {
|
|
29280
29335
|
queryBacklinks,
|
|
29281
29336
|
loadDuckdb,
|
|
@@ -29283,15 +29338,15 @@ function defaultDeps2() {
|
|
|
29283
29338
|
};
|
|
29284
29339
|
}
|
|
29285
29340
|
async function executeBacklinkExtract(db, runId, projectId, opts = {}) {
|
|
29286
|
-
const deps = { ...
|
|
29341
|
+
const deps = { ...defaultDeps3(), ...opts.deps };
|
|
29287
29342
|
const startedAt = deps.now().toISOString();
|
|
29288
|
-
db.update(runs).set({ status: RunStatuses.running, startedAt }).where(
|
|
29343
|
+
db.update(runs).set({ status: RunStatuses.running, startedAt }).where(eq34(runs.id, runId)).run();
|
|
29289
29344
|
try {
|
|
29290
|
-
const project = db.select().from(projects).where(
|
|
29345
|
+
const project = db.select().from(projects).where(eq34(projects.id, projectId)).get();
|
|
29291
29346
|
if (!project) {
|
|
29292
29347
|
throw new Error(`Project not found: ${projectId}`);
|
|
29293
29348
|
}
|
|
29294
|
-
const sync = opts.release ? db.select().from(ccReleaseSyncs).where(
|
|
29349
|
+
const sync = opts.release ? db.select().from(ccReleaseSyncs).where(eq34(ccReleaseSyncs.release, opts.release)).get() : db.select().from(ccReleaseSyncs).where(eq34(ccReleaseSyncs.status, CcReleaseSyncStatuses.ready)).orderBy(desc17(ccReleaseSyncs.createdAt)).limit(1).get();
|
|
29295
29350
|
if (!sync) {
|
|
29296
29351
|
throw new Error("No ready release sync available \u2014 run `canonry backlinks sync` first");
|
|
29297
29352
|
}
|
|
@@ -29319,11 +29374,11 @@ async function executeBacklinkExtract(db, runId, projectId, opts = {}) {
|
|
|
29319
29374
|
const targetDomain = project.canonicalDomain;
|
|
29320
29375
|
db.transaction((tx) => {
|
|
29321
29376
|
tx.delete(backlinkDomains).where(
|
|
29322
|
-
|
|
29377
|
+
and27(eq34(backlinkDomains.projectId, projectId), eq34(backlinkDomains.release, release))
|
|
29323
29378
|
).run();
|
|
29324
29379
|
if (rows.length > 0) {
|
|
29325
29380
|
const values = rows.map((r) => ({
|
|
29326
|
-
id:
|
|
29381
|
+
id: crypto31.randomUUID(),
|
|
29327
29382
|
projectId,
|
|
29328
29383
|
releaseSyncId: syncId,
|
|
29329
29384
|
release,
|
|
@@ -29336,7 +29391,7 @@ async function executeBacklinkExtract(db, runId, projectId, opts = {}) {
|
|
|
29336
29391
|
}
|
|
29337
29392
|
const summary = computeSummary2(rows);
|
|
29338
29393
|
tx.insert(backlinkSummaries).values({
|
|
29339
|
-
id:
|
|
29394
|
+
id: crypto31.randomUUID(),
|
|
29340
29395
|
projectId,
|
|
29341
29396
|
releaseSyncId: syncId,
|
|
29342
29397
|
release,
|
|
@@ -29359,8 +29414,8 @@ async function executeBacklinkExtract(db, runId, projectId, opts = {}) {
|
|
|
29359
29414
|
}).run();
|
|
29360
29415
|
});
|
|
29361
29416
|
const finishedAt = deps.now().toISOString();
|
|
29362
|
-
db.update(runs).set({ status: RunStatuses.completed, finishedAt }).where(
|
|
29363
|
-
|
|
29417
|
+
db.update(runs).set({ status: RunStatuses.completed, finishedAt }).where(eq34(runs.id, runId)).run();
|
|
29418
|
+
log8.info("extract.completed", { runId, projectId, release, rows: rows.length });
|
|
29364
29419
|
} catch (err) {
|
|
29365
29420
|
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
29366
29421
|
const finishedAt = deps.now().toISOString();
|
|
@@ -29368,8 +29423,8 @@ async function executeBacklinkExtract(db, runId, projectId, opts = {}) {
|
|
|
29368
29423
|
status: RunStatuses.failed,
|
|
29369
29424
|
error: errorMsg,
|
|
29370
29425
|
finishedAt
|
|
29371
|
-
}).where(
|
|
29372
|
-
|
|
29426
|
+
}).where(eq34(runs.id, runId)).run();
|
|
29427
|
+
log8.error("extract.failed", { runId, projectId, error: errorMsg });
|
|
29373
29428
|
throw err;
|
|
29374
29429
|
}
|
|
29375
29430
|
}
|
|
@@ -29389,18 +29444,18 @@ function computeSummary2(rows) {
|
|
|
29389
29444
|
}
|
|
29390
29445
|
|
|
29391
29446
|
// src/discovery-run.ts
|
|
29392
|
-
import
|
|
29393
|
-
import { and as
|
|
29394
|
-
var
|
|
29447
|
+
import crypto32 from "crypto";
|
|
29448
|
+
import { and as and28, eq as eq35 } from "drizzle-orm";
|
|
29449
|
+
var log9 = createLogger("DiscoveryRun");
|
|
29395
29450
|
var DEFAULT_SEED_COUNT = 30;
|
|
29396
29451
|
var QUERIES_PER_INTENT_BUCKET = 6;
|
|
29397
29452
|
async function executeDiscoveryRun(opts) {
|
|
29398
29453
|
const startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
29399
|
-
opts.db.update(runs).set({ status: RunStatuses.running, startedAt }).where(
|
|
29454
|
+
opts.db.update(runs).set({ status: RunStatuses.running, startedAt }).where(eq35(runs.id, opts.runId)).run();
|
|
29400
29455
|
try {
|
|
29401
|
-
const projectRow = opts.db.select().from(projects).where(
|
|
29456
|
+
const projectRow = opts.db.select().from(projects).where(eq35(projects.id, opts.projectId)).get();
|
|
29402
29457
|
if (!projectRow) throw new Error(`Project ${opts.projectId} not found`);
|
|
29403
|
-
const projectCompetitors = opts.db.select({ domain: competitors.domain }).from(competitors).where(
|
|
29458
|
+
const projectCompetitors = opts.db.select({ domain: competitors.domain }).from(competitors).where(eq35(competitors.projectId, opts.projectId)).all().map((r) => r.domain.toLowerCase());
|
|
29404
29459
|
const canonicalDomains = effectiveDomains({
|
|
29405
29460
|
canonicalDomain: projectRow.canonicalDomain,
|
|
29406
29461
|
ownedDomains: projectRow.ownedDomains
|
|
@@ -29430,8 +29485,8 @@ async function executeDiscoveryRun(opts) {
|
|
|
29430
29485
|
seedProvider: result.seedProvider,
|
|
29431
29486
|
result
|
|
29432
29487
|
});
|
|
29433
|
-
opts.db.update(runs).set({ status: RunStatuses.completed, finishedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(
|
|
29434
|
-
|
|
29488
|
+
opts.db.update(runs).set({ status: RunStatuses.completed, finishedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(eq35(runs.id, opts.runId)).run();
|
|
29489
|
+
log9.info("discovery.completed", {
|
|
29435
29490
|
runId: opts.runId,
|
|
29436
29491
|
sessionId: opts.sessionId,
|
|
29437
29492
|
buckets: result.buckets,
|
|
@@ -29439,13 +29494,13 @@ async function executeDiscoveryRun(opts) {
|
|
|
29439
29494
|
});
|
|
29440
29495
|
} catch (err) {
|
|
29441
29496
|
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
29442
|
-
|
|
29497
|
+
log9.error("discovery.failed", { runId: opts.runId, sessionId: opts.sessionId, error: errorMsg });
|
|
29443
29498
|
markSessionFailed(opts.db, opts.sessionId, errorMsg);
|
|
29444
29499
|
opts.db.update(runs).set({
|
|
29445
29500
|
status: RunStatuses.failed,
|
|
29446
29501
|
finishedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
29447
29502
|
error: errorMsg
|
|
29448
|
-
}).where(
|
|
29503
|
+
}).where(eq35(runs.id, opts.runId)).run();
|
|
29449
29504
|
}
|
|
29450
29505
|
}
|
|
29451
29506
|
function buildDefaultDeps(registry) {
|
|
@@ -29650,13 +29705,13 @@ function writeDiscoveryInsight(db, input) {
|
|
|
29650
29705
|
totalProbes
|
|
29651
29706
|
});
|
|
29652
29707
|
db.transaction((tx) => {
|
|
29653
|
-
tx.update(insights).set({ dismissed: true }).where(
|
|
29654
|
-
|
|
29655
|
-
|
|
29656
|
-
|
|
29708
|
+
tx.update(insights).set({ dismissed: true }).where(and28(
|
|
29709
|
+
eq35(insights.projectId, input.projectId),
|
|
29710
|
+
eq35(insights.type, "discovery.basket-divergence"),
|
|
29711
|
+
eq35(insights.dismissed, false)
|
|
29657
29712
|
)).run();
|
|
29658
29713
|
tx.insert(insights).values({
|
|
29659
|
-
id:
|
|
29714
|
+
id: crypto32.randomUUID(),
|
|
29660
29715
|
projectId: input.projectId,
|
|
29661
29716
|
runId: input.runId,
|
|
29662
29717
|
type: "discovery.basket-divergence",
|
|
@@ -29692,7 +29747,7 @@ function buildDiscoveryInsightTitle(input) {
|
|
|
29692
29747
|
}
|
|
29693
29748
|
|
|
29694
29749
|
// src/commands/backfill.ts
|
|
29695
|
-
import { and as
|
|
29750
|
+
import { and as and29, eq as eq36, inArray as inArray12, isNull, sql as sql15 } from "drizzle-orm";
|
|
29696
29751
|
var SNAPSHOT_BATCH_SIZE = 500;
|
|
29697
29752
|
async function backfillAnswerVisibilityCommand(opts) {
|
|
29698
29753
|
const config = loadConfig();
|
|
@@ -29700,7 +29755,7 @@ async function backfillAnswerVisibilityCommand(opts) {
|
|
|
29700
29755
|
migrate(db);
|
|
29701
29756
|
const projectFilter = opts?.project?.trim();
|
|
29702
29757
|
const isDryRun = opts?.dryRun === true;
|
|
29703
|
-
const scopedProjects = projectFilter ? db.select().from(projects).where(
|
|
29758
|
+
const scopedProjects = projectFilter ? db.select().from(projects).where(eq36(projects.name, projectFilter)).all() : db.select().from(projects).all();
|
|
29704
29759
|
let examined = 0;
|
|
29705
29760
|
let updated = 0;
|
|
29706
29761
|
let wouldUpdate = 0;
|
|
@@ -29708,10 +29763,10 @@ async function backfillAnswerVisibilityCommand(opts) {
|
|
|
29708
29763
|
let reparsed = 0;
|
|
29709
29764
|
let providerErrors = 0;
|
|
29710
29765
|
if (scopedProjects.length > 0) {
|
|
29711
|
-
const runRows = projectFilter ? db.select({ id: runs.id, projectId: runs.projectId }).from(runs).where(
|
|
29712
|
-
|
|
29713
|
-
|
|
29714
|
-
)).all() : db.select({ id: runs.id, projectId: runs.projectId }).from(runs).where(
|
|
29766
|
+
const runRows = projectFilter ? db.select({ id: runs.id, projectId: runs.projectId }).from(runs).where(and29(
|
|
29767
|
+
eq36(runs.kind, RunKinds["answer-visibility"]),
|
|
29768
|
+
inArray12(runs.projectId, scopedProjects.map((project) => project.id))
|
|
29769
|
+
)).all() : db.select({ id: runs.id, projectId: runs.projectId }).from(runs).where(eq36(runs.kind, RunKinds["answer-visibility"])).all();
|
|
29715
29770
|
const runIdsByProject = /* @__PURE__ */ new Map();
|
|
29716
29771
|
for (const run of runRows) {
|
|
29717
29772
|
const existing = runIdsByProject.get(run.projectId);
|
|
@@ -29719,7 +29774,7 @@ async function backfillAnswerVisibilityCommand(opts) {
|
|
|
29719
29774
|
else runIdsByProject.set(run.projectId, [run.id]);
|
|
29720
29775
|
}
|
|
29721
29776
|
for (const project of scopedProjects) {
|
|
29722
|
-
const competitorDomains = db.select({ domain: competitors.domain }).from(competitors).where(
|
|
29777
|
+
const competitorDomains = db.select({ domain: competitors.domain }).from(competitors).where(eq36(competitors.projectId, project.id)).all().map((row) => row.domain);
|
|
29723
29778
|
const runIds = runIdsByProject.get(project.id) ?? [];
|
|
29724
29779
|
if (runIds.length === 0) continue;
|
|
29725
29780
|
const projectDomains = effectiveDomains({
|
|
@@ -29742,7 +29797,7 @@ async function backfillAnswerVisibilityCommand(opts) {
|
|
|
29742
29797
|
competitorOverlap: querySnapshots.competitorOverlap,
|
|
29743
29798
|
recommendedCompetitors: querySnapshots.recommendedCompetitors,
|
|
29744
29799
|
rawResponse: querySnapshots.rawResponse
|
|
29745
|
-
}).from(querySnapshots).where(
|
|
29800
|
+
}).from(querySnapshots).where(inArray12(querySnapshots.runId, batchRunIds)).all();
|
|
29746
29801
|
const pendingUpdates = [];
|
|
29747
29802
|
for (const snapshot of snapshotRows) {
|
|
29748
29803
|
examined++;
|
|
@@ -29807,7 +29862,7 @@ async function backfillAnswerVisibilityCommand(opts) {
|
|
|
29807
29862
|
} else {
|
|
29808
29863
|
db.transaction((tx) => {
|
|
29809
29864
|
for (const update of pendingUpdates) {
|
|
29810
|
-
tx.update(querySnapshots).set(update.patch).where(
|
|
29865
|
+
tx.update(querySnapshots).set(update.patch).where(eq36(querySnapshots.id, update.id)).run();
|
|
29811
29866
|
}
|
|
29812
29867
|
});
|
|
29813
29868
|
updated += pendingUpdates.length;
|
|
@@ -29856,13 +29911,13 @@ No DB writes performed. Re-run without --dry-run to apply.`);
|
|
|
29856
29911
|
function backfillNormalizedPaths(db, opts) {
|
|
29857
29912
|
const baseConditions = [];
|
|
29858
29913
|
if (opts?.projectId) {
|
|
29859
|
-
baseConditions.push(
|
|
29914
|
+
baseConditions.push(eq36(gaTrafficSnapshots.projectId, opts.projectId));
|
|
29860
29915
|
}
|
|
29861
29916
|
const rows = db.select({
|
|
29862
29917
|
id: gaTrafficSnapshots.id,
|
|
29863
29918
|
landingPage: gaTrafficSnapshots.landingPage,
|
|
29864
29919
|
landingPageNormalized: gaTrafficSnapshots.landingPageNormalized
|
|
29865
|
-
}).from(gaTrafficSnapshots).where(baseConditions.length > 0 ?
|
|
29920
|
+
}).from(gaTrafficSnapshots).where(baseConditions.length > 0 ? and29(...baseConditions) : void 0).all();
|
|
29866
29921
|
let updated = 0;
|
|
29867
29922
|
let unchanged = 0;
|
|
29868
29923
|
if (rows.length > 0) {
|
|
@@ -29877,7 +29932,7 @@ function backfillNormalizedPaths(db, opts) {
|
|
|
29877
29932
|
unchanged++;
|
|
29878
29933
|
continue;
|
|
29879
29934
|
}
|
|
29880
|
-
tx.update(gaTrafficSnapshots).set({ landingPageNormalized: next }).where(
|
|
29935
|
+
tx.update(gaTrafficSnapshots).set({ landingPageNormalized: next }).where(eq36(gaTrafficSnapshots.id, row.id)).run();
|
|
29881
29936
|
updated++;
|
|
29882
29937
|
}
|
|
29883
29938
|
});
|
|
@@ -29891,7 +29946,7 @@ async function backfillNormalizedPathsCommand(opts) {
|
|
|
29891
29946
|
const projectFilter = opts?.project?.trim();
|
|
29892
29947
|
let projectId;
|
|
29893
29948
|
if (projectFilter) {
|
|
29894
|
-
const project = db.select({ id: projects.id }).from(projects).where(
|
|
29949
|
+
const project = db.select({ id: projects.id }).from(projects).where(eq36(projects.name, projectFilter)).get();
|
|
29895
29950
|
if (!project) {
|
|
29896
29951
|
const result2 = {
|
|
29897
29952
|
project: projectFilter,
|
|
@@ -29928,13 +29983,13 @@ async function backfillNormalizedPathsCommand(opts) {
|
|
|
29928
29983
|
function backfillAiReferralPaths(db, opts) {
|
|
29929
29984
|
const baseConditions = [];
|
|
29930
29985
|
if (opts?.projectId) {
|
|
29931
|
-
baseConditions.push(
|
|
29986
|
+
baseConditions.push(eq36(gaAiReferrals.projectId, opts.projectId));
|
|
29932
29987
|
}
|
|
29933
29988
|
const rows = db.select({
|
|
29934
29989
|
id: gaAiReferrals.id,
|
|
29935
29990
|
landingPage: gaAiReferrals.landingPage,
|
|
29936
29991
|
landingPageNormalized: gaAiReferrals.landingPageNormalized
|
|
29937
|
-
}).from(gaAiReferrals).where(baseConditions.length > 0 ?
|
|
29992
|
+
}).from(gaAiReferrals).where(baseConditions.length > 0 ? and29(...baseConditions) : void 0).all();
|
|
29938
29993
|
let updated = 0;
|
|
29939
29994
|
let unchanged = 0;
|
|
29940
29995
|
if (rows.length > 0) {
|
|
@@ -29949,7 +30004,7 @@ function backfillAiReferralPaths(db, opts) {
|
|
|
29949
30004
|
unchanged++;
|
|
29950
30005
|
continue;
|
|
29951
30006
|
}
|
|
29952
|
-
tx.update(gaAiReferrals).set({ landingPageNormalized: next }).where(
|
|
30007
|
+
tx.update(gaAiReferrals).set({ landingPageNormalized: next }).where(eq36(gaAiReferrals.id, row.id)).run();
|
|
29953
30008
|
updated++;
|
|
29954
30009
|
}
|
|
29955
30010
|
});
|
|
@@ -29963,7 +30018,7 @@ async function backfillAiReferralPathsCommand(opts) {
|
|
|
29963
30018
|
const projectFilter = opts?.project?.trim();
|
|
29964
30019
|
let projectId;
|
|
29965
30020
|
if (projectFilter) {
|
|
29966
|
-
const project = db.select({ id: projects.id }).from(projects).where(
|
|
30021
|
+
const project = db.select({ id: projects.id }).from(projects).where(eq36(projects.name, projectFilter)).get();
|
|
29967
30022
|
if (!project) {
|
|
29968
30023
|
const result2 = {
|
|
29969
30024
|
project: projectFilter,
|
|
@@ -29999,10 +30054,10 @@ async function backfillAiReferralPathsCommand(opts) {
|
|
|
29999
30054
|
}
|
|
30000
30055
|
function backfillProjectAnswerMentions(db, projectId, opts) {
|
|
30001
30056
|
const isDryRun = opts?.dryRun === true;
|
|
30002
|
-
const project = db.select().from(projects).where(
|
|
30057
|
+
const project = db.select().from(projects).where(eq36(projects.id, projectId)).get();
|
|
30003
30058
|
if (!project) return { examined: 0, updated: 0, mentioned: 0 };
|
|
30004
|
-
const competitorDomains = db.select({ domain: competitors.domain }).from(competitors).where(
|
|
30005
|
-
const runRows = db.select({ id: runs.id }).from(runs).where(
|
|
30059
|
+
const competitorDomains = db.select({ domain: competitors.domain }).from(competitors).where(eq36(competitors.projectId, projectId)).all().map((row) => row.domain);
|
|
30060
|
+
const runRows = db.select({ id: runs.id }).from(runs).where(and29(eq36(runs.kind, RunKinds["answer-visibility"]), eq36(runs.projectId, projectId))).all();
|
|
30006
30061
|
const runIds = runRows.map((r) => r.id);
|
|
30007
30062
|
let examined = 0;
|
|
30008
30063
|
let updated = 0;
|
|
@@ -30030,7 +30085,7 @@ function backfillProjectAnswerMentions(db, projectId, opts) {
|
|
|
30030
30085
|
competitorOverlap: querySnapshots.competitorOverlap,
|
|
30031
30086
|
recommendedCompetitors: querySnapshots.recommendedCompetitors,
|
|
30032
30087
|
rawResponse: querySnapshots.rawResponse
|
|
30033
|
-
}).from(querySnapshots).where(
|
|
30088
|
+
}).from(querySnapshots).where(inArray12(querySnapshots.runId, batchRunIds)).all();
|
|
30034
30089
|
const pendingUpdates = [];
|
|
30035
30090
|
for (const snapshot of snapshotRows) {
|
|
30036
30091
|
examined++;
|
|
@@ -30074,7 +30129,7 @@ function backfillProjectAnswerMentions(db, projectId, opts) {
|
|
|
30074
30129
|
} else {
|
|
30075
30130
|
db.transaction((tx) => {
|
|
30076
30131
|
for (const update of pendingUpdates) {
|
|
30077
|
-
tx.update(querySnapshots).set(update.patch).where(
|
|
30132
|
+
tx.update(querySnapshots).set(update.patch).where(eq36(querySnapshots.id, update.id)).run();
|
|
30078
30133
|
}
|
|
30079
30134
|
});
|
|
30080
30135
|
updated += pendingUpdates.length;
|
|
@@ -30089,7 +30144,7 @@ async function backfillAnswerMentionsCommand(opts) {
|
|
|
30089
30144
|
migrate(db);
|
|
30090
30145
|
const projectFilter = opts?.project?.trim();
|
|
30091
30146
|
const isDryRun = opts?.dryRun === true;
|
|
30092
|
-
const scopedProjects = projectFilter ? db.select().from(projects).where(
|
|
30147
|
+
const scopedProjects = projectFilter ? db.select().from(projects).where(eq36(projects.name, projectFilter)).all() : db.select().from(projects).all();
|
|
30093
30148
|
let examined = 0;
|
|
30094
30149
|
let updated = 0;
|
|
30095
30150
|
let wouldUpdate = 0;
|
|
@@ -30308,7 +30363,7 @@ async function backfillSnapshotAttributionCommand(opts) {
|
|
|
30308
30363
|
const config = loadConfig();
|
|
30309
30364
|
const db = createClient(config.database);
|
|
30310
30365
|
migrate(db);
|
|
30311
|
-
const project = db.select().from(projects).where(
|
|
30366
|
+
const project = db.select().from(projects).where(eq36(projects.name, opts.project)).get();
|
|
30312
30367
|
if (!project) {
|
|
30313
30368
|
throw new Error(`Project "${opts.project}" not found`);
|
|
30314
30369
|
}
|
|
@@ -30319,17 +30374,17 @@ async function backfillSnapshotAttributionCommand(opts) {
|
|
|
30319
30374
|
process.stderr.write(`Recovering orphan snapshot attribution for "${project.name}"${mode}...
|
|
30320
30375
|
`);
|
|
30321
30376
|
}
|
|
30322
|
-
const events = db.select({ createdAt: auditLog.createdAt, action: auditLog.action, diff: auditLog.diff }).from(auditLog).where(
|
|
30323
|
-
|
|
30324
|
-
|
|
30377
|
+
const events = db.select({ createdAt: auditLog.createdAt, action: auditLog.action, diff: auditLog.diff }).from(auditLog).where(and29(
|
|
30378
|
+
eq36(auditLog.projectId, project.id),
|
|
30379
|
+
inArray12(auditLog.action, ["keywords.appended", "keywords.deleted", "queries.appended", "queries.deleted", "queries.replaced"])
|
|
30325
30380
|
)).orderBy(auditLog.createdAt).all();
|
|
30326
30381
|
const history = replayQueryAuditLog(events);
|
|
30327
30382
|
const orphanRuns = db.select({
|
|
30328
30383
|
runId: runs.id,
|
|
30329
30384
|
createdAt: runs.createdAt,
|
|
30330
30385
|
location: runs.location
|
|
30331
|
-
}).from(runs).innerJoin(querySnapshots,
|
|
30332
|
-
|
|
30386
|
+
}).from(runs).innerJoin(querySnapshots, eq36(querySnapshots.runId, runs.id)).where(and29(
|
|
30387
|
+
eq36(runs.projectId, project.id),
|
|
30333
30388
|
isNull(querySnapshots.queryId),
|
|
30334
30389
|
isNull(querySnapshots.queryText)
|
|
30335
30390
|
)).groupBy(runs.id).orderBy(runs.createdAt).all();
|
|
@@ -30351,8 +30406,8 @@ async function backfillSnapshotAttributionCommand(opts) {
|
|
|
30351
30406
|
provider: querySnapshots.provider,
|
|
30352
30407
|
createdAt: querySnapshots.createdAt,
|
|
30353
30408
|
answerText: querySnapshots.answerText
|
|
30354
|
-
}).from(querySnapshots).where(
|
|
30355
|
-
|
|
30409
|
+
}).from(querySnapshots).where(and29(
|
|
30410
|
+
eq36(querySnapshots.runId, run.runId),
|
|
30356
30411
|
isNull(querySnapshots.queryId),
|
|
30357
30412
|
isNull(querySnapshots.queryText)
|
|
30358
30413
|
)).orderBy(querySnapshots.provider, querySnapshots.createdAt).all();
|
|
@@ -30418,7 +30473,7 @@ async function backfillSnapshotAttributionCommand(opts) {
|
|
|
30418
30473
|
if (!isDryRun && updates.length > 0) {
|
|
30419
30474
|
db.transaction((tx) => {
|
|
30420
30475
|
for (const u of updates) {
|
|
30421
|
-
tx.update(querySnapshots).set({ queryText: u.queryText }).where(
|
|
30476
|
+
tx.update(querySnapshots).set({ queryText: u.queryText }).where(eq36(querySnapshots.id, u.id)).run();
|
|
30422
30477
|
}
|
|
30423
30478
|
});
|
|
30424
30479
|
}
|
|
@@ -30492,7 +30547,7 @@ async function backfillTrafficClassificationCommand(opts) {
|
|
|
30492
30547
|
const projectFilter = opts?.project?.trim();
|
|
30493
30548
|
const isDryRun = opts?.dryRun === true;
|
|
30494
30549
|
const isJson = opts?.format === "json";
|
|
30495
|
-
const scopedProjects = projectFilter ? db.select().from(projects).where(
|
|
30550
|
+
const scopedProjects = projectFilter ? db.select().from(projects).where(eq36(projects.name, projectFilter)).all() : db.select().from(projects).all();
|
|
30496
30551
|
if (scopedProjects.length === 0) {
|
|
30497
30552
|
if (projectFilter && !isJson) {
|
|
30498
30553
|
process.stderr.write(`No project named "${projectFilter}".
|
|
@@ -30517,9 +30572,9 @@ async function backfillTrafficClassificationCommand(opts) {
|
|
|
30517
30572
|
dryRun: isDryRun,
|
|
30518
30573
|
byBot: {}
|
|
30519
30574
|
};
|
|
30520
|
-
const unknownCountRow = db.select({ n: sql15`count(*)` }).from(rawEventSamples).where(
|
|
30521
|
-
|
|
30522
|
-
|
|
30575
|
+
const unknownCountRow = db.select({ n: sql15`count(*)` }).from(rawEventSamples).where(and29(
|
|
30576
|
+
eq36(rawEventSamples.eventType, "unknown"),
|
|
30577
|
+
inArray12(rawEventSamples.projectId, projectIds)
|
|
30523
30578
|
)).get();
|
|
30524
30579
|
result.unknownBefore = Number(unknownCountRow?.n ?? 0);
|
|
30525
30580
|
const unknownSamples = db.select({
|
|
@@ -30530,9 +30585,9 @@ async function backfillTrafficClassificationCommand(opts) {
|
|
|
30530
30585
|
userAgent: rawEventSamples.userAgent,
|
|
30531
30586
|
pathNormalized: rawEventSamples.pathNormalized,
|
|
30532
30587
|
status: rawEventSamples.status
|
|
30533
|
-
}).from(rawEventSamples).where(
|
|
30534
|
-
|
|
30535
|
-
|
|
30588
|
+
}).from(rawEventSamples).where(and29(
|
|
30589
|
+
eq36(rawEventSamples.eventType, "unknown"),
|
|
30590
|
+
inArray12(rawEventSamples.projectId, projectIds)
|
|
30536
30591
|
)).all();
|
|
30537
30592
|
result.examined = unknownSamples.length;
|
|
30538
30593
|
if (unknownSamples.length === 0) {
|
|
@@ -30570,7 +30625,7 @@ async function backfillTrafficClassificationCommand(opts) {
|
|
|
30570
30625
|
result.reclassified++;
|
|
30571
30626
|
result.byBot[classified.botId] = (result.byBot[classified.botId] ?? 0) + 1;
|
|
30572
30627
|
if (isDryRun) continue;
|
|
30573
|
-
db.update(rawEventSamples).set({ eventType: userFetch ? TrafficEventKinds["ai-user-fetch"] : TrafficEventKinds.crawler }).where(
|
|
30628
|
+
db.update(rawEventSamples).set({ eventType: userFetch ? TrafficEventKinds["ai-user-fetch"] : TrafficEventKinds.crawler }).where(eq36(rawEventSamples.id, snap.id)).run();
|
|
30574
30629
|
const tsHour = new Date(snap.ts);
|
|
30575
30630
|
tsHour.setUTCMinutes(0, 0, 0);
|
|
30576
30631
|
if (userFetch) {
|
|
@@ -30634,9 +30689,9 @@ async function backfillTrafficClassificationCommand(opts) {
|
|
|
30634
30689
|
}
|
|
30635
30690
|
}
|
|
30636
30691
|
if (!isDryRun) {
|
|
30637
|
-
const afterRow = db.select({ n: sql15`count(*)` }).from(rawEventSamples).where(
|
|
30638
|
-
|
|
30639
|
-
|
|
30692
|
+
const afterRow = db.select({ n: sql15`count(*)` }).from(rawEventSamples).where(and29(
|
|
30693
|
+
eq36(rawEventSamples.eventType, "unknown"),
|
|
30694
|
+
inArray12(rawEventSamples.projectId, projectIds)
|
|
30640
30695
|
)).get();
|
|
30641
30696
|
result.unknownAfter = Number(afterRow?.n ?? 0);
|
|
30642
30697
|
} else {
|
|
@@ -30722,8 +30777,8 @@ var ProviderRegistry = class {
|
|
|
30722
30777
|
|
|
30723
30778
|
// src/scheduler.ts
|
|
30724
30779
|
import cron from "node-cron";
|
|
30725
|
-
import { and as
|
|
30726
|
-
var
|
|
30780
|
+
import { and as and30, eq as eq37 } from "drizzle-orm";
|
|
30781
|
+
var log10 = createLogger("Scheduler");
|
|
30727
30782
|
function taskKey(projectId, kind) {
|
|
30728
30783
|
return `${projectId}::${kind}`;
|
|
30729
30784
|
}
|
|
@@ -30737,16 +30792,16 @@ var Scheduler = class {
|
|
|
30737
30792
|
}
|
|
30738
30793
|
/** Load all enabled schedules from DB and register cron jobs. */
|
|
30739
30794
|
start() {
|
|
30740
|
-
const allSchedules = this.db.select().from(schedules).where(
|
|
30795
|
+
const allSchedules = this.db.select().from(schedules).where(eq37(schedules.enabled, true)).all();
|
|
30741
30796
|
for (const schedule of allSchedules) {
|
|
30742
30797
|
const missedRunAt = schedule.nextRunAt;
|
|
30743
30798
|
this.registerCronTask(schedule);
|
|
30744
30799
|
if (missedRunAt && new Date(missedRunAt) < /* @__PURE__ */ new Date()) {
|
|
30745
|
-
|
|
30800
|
+
log10.info("run.catch-up", { projectId: schedule.projectId, kind: schedule.kind, missedRunAt });
|
|
30746
30801
|
this.triggerRun(schedule.id, schedule.projectId, schedule.kind);
|
|
30747
30802
|
}
|
|
30748
30803
|
}
|
|
30749
|
-
|
|
30804
|
+
log10.info("started", { scheduleCount: allSchedules.length });
|
|
30750
30805
|
}
|
|
30751
30806
|
/** Stop all cron tasks for graceful shutdown. */
|
|
30752
30807
|
stop() {
|
|
@@ -30767,7 +30822,7 @@ var Scheduler = class {
|
|
|
30767
30822
|
this.stopTask(key, existing, "Stopped");
|
|
30768
30823
|
this.tasks.delete(key);
|
|
30769
30824
|
}
|
|
30770
|
-
const schedule = this.db.select().from(schedules).where(
|
|
30825
|
+
const schedule = this.db.select().from(schedules).where(and30(eq37(schedules.projectId, projectId), eq37(schedules.kind, kind))).get();
|
|
30771
30826
|
if (schedule && schedule.enabled) {
|
|
30772
30827
|
this.registerCronTask(schedule);
|
|
30773
30828
|
}
|
|
@@ -30790,13 +30845,13 @@ var Scheduler = class {
|
|
|
30790
30845
|
stopTask(key, task, verb) {
|
|
30791
30846
|
void task.stop();
|
|
30792
30847
|
void task.destroy();
|
|
30793
|
-
|
|
30848
|
+
log10.info(`task.${verb.toLowerCase()}`, { key });
|
|
30794
30849
|
}
|
|
30795
30850
|
registerCronTask(schedule) {
|
|
30796
30851
|
const { id: scheduleId, projectId, cronExpr, timezone } = schedule;
|
|
30797
30852
|
const kind = schedule.kind;
|
|
30798
30853
|
if (!cron.validate(cronExpr)) {
|
|
30799
|
-
|
|
30854
|
+
log10.error("cron.invalid", { projectId, kind, cronExpr });
|
|
30800
30855
|
return;
|
|
30801
30856
|
}
|
|
30802
30857
|
const task = cron.schedule(cronExpr, () => {
|
|
@@ -30808,43 +30863,43 @@ var Scheduler = class {
|
|
|
30808
30863
|
this.db.update(schedules).set({
|
|
30809
30864
|
nextRunAt: task.getNextRun()?.toISOString() ?? null,
|
|
30810
30865
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
30811
|
-
}).where(
|
|
30866
|
+
}).where(eq37(schedules.id, scheduleId)).run();
|
|
30812
30867
|
const label = schedule.preset ?? cronExpr;
|
|
30813
|
-
|
|
30868
|
+
log10.info("cron.registered", { projectId, kind, schedule: label, timezone });
|
|
30814
30869
|
}
|
|
30815
30870
|
triggerRun(scheduleId, projectId, kind) {
|
|
30816
30871
|
try {
|
|
30817
30872
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
30818
|
-
const currentSchedule = this.db.select().from(schedules).where(
|
|
30873
|
+
const currentSchedule = this.db.select().from(schedules).where(eq37(schedules.id, scheduleId)).get();
|
|
30819
30874
|
if (!currentSchedule || !currentSchedule.enabled) {
|
|
30820
|
-
|
|
30875
|
+
log10.warn("schedule.stale", { scheduleId, projectId, kind, msg: "schedule no longer exists or is disabled" });
|
|
30821
30876
|
this.remove(projectId, kind);
|
|
30822
30877
|
return;
|
|
30823
30878
|
}
|
|
30824
30879
|
const task = this.tasks.get(taskKey(projectId, kind));
|
|
30825
30880
|
const nextRunAt = task?.getNextRun()?.toISOString() ?? null;
|
|
30826
|
-
const project = this.db.select().from(projects).where(
|
|
30881
|
+
const project = this.db.select().from(projects).where(eq37(projects.id, projectId)).get();
|
|
30827
30882
|
if (!project) {
|
|
30828
|
-
|
|
30883
|
+
log10.error("project.not-found", { projectId, kind, msg: "skipping scheduled run" });
|
|
30829
30884
|
this.remove(projectId, kind);
|
|
30830
30885
|
return;
|
|
30831
30886
|
}
|
|
30832
30887
|
if (kind === SchedulableRunKinds["traffic-sync"]) {
|
|
30833
30888
|
const sourceId = currentSchedule.sourceId;
|
|
30834
30889
|
if (!sourceId) {
|
|
30835
|
-
|
|
30890
|
+
log10.warn("traffic-sync.missing-source", { scheduleId, projectId });
|
|
30836
30891
|
return;
|
|
30837
30892
|
}
|
|
30838
30893
|
if (!this.callbacks.onTrafficSyncRequested) {
|
|
30839
|
-
|
|
30894
|
+
log10.warn("traffic-sync.no-callback", { scheduleId, projectId, msg: "host did not register onTrafficSyncRequested" });
|
|
30840
30895
|
return;
|
|
30841
30896
|
}
|
|
30842
30897
|
this.db.update(schedules).set({
|
|
30843
30898
|
lastRunAt: now,
|
|
30844
30899
|
nextRunAt,
|
|
30845
30900
|
updatedAt: now
|
|
30846
|
-
}).where(
|
|
30847
|
-
|
|
30901
|
+
}).where(eq37(schedules.id, currentSchedule.id)).run();
|
|
30902
|
+
log10.info("traffic-sync.triggered", { projectName: project.name, sourceId });
|
|
30848
30903
|
this.callbacks.onTrafficSyncRequested(project.name, sourceId);
|
|
30849
30904
|
return;
|
|
30850
30905
|
}
|
|
@@ -30853,7 +30908,7 @@ var Scheduler = class {
|
|
|
30853
30908
|
if (project.defaultLocation) {
|
|
30854
30909
|
const loc = projectLocations.find((l) => l.label === project.defaultLocation);
|
|
30855
30910
|
if (!loc) {
|
|
30856
|
-
|
|
30911
|
+
log10.warn("default-location.stale", { scheduleId, projectId, label: project.defaultLocation });
|
|
30857
30912
|
return;
|
|
30858
30913
|
}
|
|
30859
30914
|
resolvedLocation = loc;
|
|
@@ -30867,11 +30922,11 @@ var Scheduler = class {
|
|
|
30867
30922
|
location: locationLabel
|
|
30868
30923
|
});
|
|
30869
30924
|
if (queueResult.conflict) {
|
|
30870
|
-
|
|
30925
|
+
log10.info("run.skipped-active", { projectName: project.name, activeRunId: queueResult.activeRunId });
|
|
30871
30926
|
this.db.update(schedules).set({
|
|
30872
30927
|
nextRunAt,
|
|
30873
30928
|
updatedAt: now
|
|
30874
|
-
}).where(
|
|
30929
|
+
}).where(eq37(schedules.id, currentSchedule.id)).run();
|
|
30875
30930
|
return;
|
|
30876
30931
|
}
|
|
30877
30932
|
const runId = queueResult.runId;
|
|
@@ -30879,21 +30934,21 @@ var Scheduler = class {
|
|
|
30879
30934
|
lastRunAt: now,
|
|
30880
30935
|
nextRunAt,
|
|
30881
30936
|
updatedAt: now
|
|
30882
|
-
}).where(
|
|
30937
|
+
}).where(eq37(schedules.id, currentSchedule.id)).run();
|
|
30883
30938
|
const scheduleProviders = currentSchedule.providers;
|
|
30884
30939
|
const providers = scheduleProviders.length > 0 ? scheduleProviders : void 0;
|
|
30885
|
-
|
|
30940
|
+
log10.info("run.triggered", { runId, projectName: project.name, providers: providers ?? "all" });
|
|
30886
30941
|
this.callbacks.onRunCreated(runId, projectId, providers, resolvedLocation);
|
|
30887
30942
|
} catch (err) {
|
|
30888
|
-
|
|
30943
|
+
log10.error("trigger.error", { scheduleId, projectId, kind, error: err instanceof Error ? err.message : String(err) });
|
|
30889
30944
|
}
|
|
30890
30945
|
}
|
|
30891
30946
|
};
|
|
30892
30947
|
|
|
30893
30948
|
// src/notifier.ts
|
|
30894
|
-
import { eq as
|
|
30895
|
-
import
|
|
30896
|
-
var
|
|
30949
|
+
import { eq as eq38, desc as desc18, and as and31, inArray as inArray13, or as or5 } from "drizzle-orm";
|
|
30950
|
+
import crypto33 from "crypto";
|
|
30951
|
+
var log11 = createLogger("Notifier");
|
|
30897
30952
|
var Notifier = class {
|
|
30898
30953
|
db;
|
|
30899
30954
|
serverUrl;
|
|
@@ -30903,26 +30958,26 @@ var Notifier = class {
|
|
|
30903
30958
|
}
|
|
30904
30959
|
/** Called after a run completes (success, partial, or failed). */
|
|
30905
30960
|
async onRunCompleted(runId, projectId) {
|
|
30906
|
-
|
|
30907
|
-
const notifs = this.db.select().from(notifications).where(
|
|
30961
|
+
log11.info("run.completed", { runId, projectId });
|
|
30962
|
+
const notifs = this.db.select().from(notifications).where(eq38(notifications.projectId, projectId)).all().filter((n) => n.enabled);
|
|
30908
30963
|
if (notifs.length === 0) {
|
|
30909
|
-
|
|
30964
|
+
log11.info("notifications.none-enabled", { projectId });
|
|
30910
30965
|
return;
|
|
30911
30966
|
}
|
|
30912
|
-
|
|
30913
|
-
const run = this.db.select().from(runs).where(
|
|
30967
|
+
log11.info("notifications.found", { projectId, count: notifs.length });
|
|
30968
|
+
const run = this.db.select().from(runs).where(eq38(runs.id, runId)).get();
|
|
30914
30969
|
if (!run) {
|
|
30915
|
-
|
|
30970
|
+
log11.error("run.not-found", { runId, msg: "skipping notification dispatch" });
|
|
30916
30971
|
return;
|
|
30917
30972
|
}
|
|
30918
|
-
const project = this.db.select().from(projects).where(
|
|
30973
|
+
const project = this.db.select().from(projects).where(eq38(projects.id, projectId)).get();
|
|
30919
30974
|
if (!project) {
|
|
30920
|
-
|
|
30975
|
+
log11.error("project.not-found", { projectId, msg: "skipping notification dispatch" });
|
|
30921
30976
|
return;
|
|
30922
30977
|
}
|
|
30923
30978
|
const transitions = this.computeTransitions(runId, projectId);
|
|
30924
30979
|
const events = [];
|
|
30925
|
-
|
|
30980
|
+
log11.info("run.status", { runId: run.id, status: run.status, projectId });
|
|
30926
30981
|
if (run.status === "completed" || run.status === "partial") {
|
|
30927
30982
|
events.push("run.completed");
|
|
30928
30983
|
}
|
|
@@ -30938,7 +30993,7 @@ var Notifier = class {
|
|
|
30938
30993
|
if (!config.url) continue;
|
|
30939
30994
|
const subscribedEvents = config.events;
|
|
30940
30995
|
const matchingEvents = events.filter((e) => subscribedEvents.includes(e));
|
|
30941
|
-
|
|
30996
|
+
log11.info("notification.match", { notificationId: notif.id, subscribedEvents, matchedEvents: matchingEvents });
|
|
30942
30997
|
if (matchingEvents.length === 0) continue;
|
|
30943
30998
|
for (const event of matchingEvents) {
|
|
30944
30999
|
const relevantTransitions = event === "citation.lost" ? lostTransitions : event === "citation.gained" ? gainedTransitions : transitions;
|
|
@@ -30962,11 +31017,11 @@ var Notifier = class {
|
|
|
30962
31017
|
if (criticalInsights.length > 0) insightEvents.push("insight.critical");
|
|
30963
31018
|
if (highInsights.length > 0) insightEvents.push("insight.high");
|
|
30964
31019
|
if (insightEvents.length === 0) return;
|
|
30965
|
-
const notifs = this.db.select().from(notifications).where(
|
|
31020
|
+
const notifs = this.db.select().from(notifications).where(eq38(notifications.projectId, projectId)).all().filter((n) => n.enabled);
|
|
30966
31021
|
if (notifs.length === 0) return;
|
|
30967
|
-
const run = this.db.select().from(runs).where(
|
|
31022
|
+
const run = this.db.select().from(runs).where(eq38(runs.id, runId)).get();
|
|
30968
31023
|
if (!run) return;
|
|
30969
|
-
const project = this.db.select().from(projects).where(
|
|
31024
|
+
const project = this.db.select().from(projects).where(eq38(projects.id, projectId)).get();
|
|
30970
31025
|
if (!project) return;
|
|
30971
31026
|
for (const notif of notifs) {
|
|
30972
31027
|
const config = notif.config;
|
|
@@ -30996,12 +31051,12 @@ var Notifier = class {
|
|
|
30996
31051
|
}
|
|
30997
31052
|
}
|
|
30998
31053
|
computeTransitions(runId, projectId) {
|
|
30999
|
-
const thisRun = this.db.select().from(runs).where(
|
|
31054
|
+
const thisRun = this.db.select().from(runs).where(eq38(runs.id, runId)).get();
|
|
31000
31055
|
if (!thisRun) return [];
|
|
31001
|
-
const groupSiblings = this.db.select().from(runs).where(
|
|
31002
|
-
|
|
31003
|
-
|
|
31004
|
-
|
|
31056
|
+
const groupSiblings = this.db.select().from(runs).where(and31(
|
|
31057
|
+
eq38(runs.projectId, projectId),
|
|
31058
|
+
eq38(runs.kind, thisRun.kind),
|
|
31059
|
+
eq38(runs.createdAt, thisRun.createdAt)
|
|
31005
31060
|
)).all();
|
|
31006
31061
|
const stillPending = groupSiblings.some((r) => r.status === "queued" || r.status === "running");
|
|
31007
31062
|
if (stillPending) return [];
|
|
@@ -31017,19 +31072,19 @@ var Notifier = class {
|
|
|
31017
31072
|
return candidate.id > best.id ? candidate : best;
|
|
31018
31073
|
});
|
|
31019
31074
|
if (winner.id !== runId) return [];
|
|
31020
|
-
const projectLocations = this.db.select({ locations: projects.locations }).from(projects).where(
|
|
31075
|
+
const projectLocations = this.db.select({ locations: projects.locations }).from(projects).where(eq38(projects.id, projectId)).get();
|
|
31021
31076
|
const locationCount = Math.max(
|
|
31022
31077
|
1,
|
|
31023
31078
|
(projectLocations?.locations ?? []).length
|
|
31024
31079
|
);
|
|
31025
31080
|
const RECENT_FETCH_LIMIT = Math.max(8, locationCount * 4);
|
|
31026
31081
|
const recentRuns = this.db.select().from(runs).where(
|
|
31027
|
-
|
|
31028
|
-
|
|
31029
|
-
|
|
31030
|
-
or5(
|
|
31082
|
+
and31(
|
|
31083
|
+
eq38(runs.projectId, projectId),
|
|
31084
|
+
eq38(runs.kind, thisRun.kind),
|
|
31085
|
+
or5(eq38(runs.status, "completed"), eq38(runs.status, "partial"))
|
|
31031
31086
|
)
|
|
31032
|
-
).orderBy(
|
|
31087
|
+
).orderBy(desc18(runs.createdAt), desc18(runs.id)).limit(RECENT_FETCH_LIMIT).all();
|
|
31033
31088
|
const groups = groupRunsByCreatedAt(recentRuns);
|
|
31034
31089
|
const currentGroupIdx = groups.findIndex((g) => g[0]?.createdAt === thisRun.createdAt);
|
|
31035
31090
|
if (currentGroupIdx < 0) return [];
|
|
@@ -31044,13 +31099,13 @@ var Notifier = class {
|
|
|
31044
31099
|
provider: querySnapshots.provider,
|
|
31045
31100
|
location: querySnapshots.location,
|
|
31046
31101
|
citationState: querySnapshots.citationState
|
|
31047
|
-
}).from(querySnapshots).leftJoin(queries,
|
|
31102
|
+
}).from(querySnapshots).leftJoin(queries, eq38(querySnapshots.queryId, queries.id)).where(inArray13(querySnapshots.runId, currentRunIds)).all();
|
|
31048
31103
|
const previousSnapshots = this.db.select({
|
|
31049
31104
|
queryId: querySnapshots.queryId,
|
|
31050
31105
|
provider: querySnapshots.provider,
|
|
31051
31106
|
location: querySnapshots.location,
|
|
31052
31107
|
citationState: querySnapshots.citationState
|
|
31053
|
-
}).from(querySnapshots).where(
|
|
31108
|
+
}).from(querySnapshots).where(inArray13(querySnapshots.runId, previousRunIds)).all();
|
|
31054
31109
|
const prevMap = /* @__PURE__ */ new Map();
|
|
31055
31110
|
for (const s of previousSnapshots) {
|
|
31056
31111
|
if (s.queryId == null) continue;
|
|
@@ -31077,23 +31132,23 @@ var Notifier = class {
|
|
|
31077
31132
|
const targetLabel = redactNotificationUrl(url).urlDisplay;
|
|
31078
31133
|
const targetCheck = await resolveWebhookTarget(url);
|
|
31079
31134
|
if (!targetCheck.ok) {
|
|
31080
|
-
|
|
31135
|
+
log11.error("webhook.ssrf-blocked", { url: targetLabel, reason: targetCheck.message });
|
|
31081
31136
|
this.logDelivery(projectId, notificationId, payload.event, "failed", `SSRF: ${targetCheck.message}`);
|
|
31082
31137
|
return;
|
|
31083
31138
|
}
|
|
31084
|
-
|
|
31139
|
+
log11.info("webhook.send", { event: payload.event, url: targetLabel });
|
|
31085
31140
|
const maxRetries = 3;
|
|
31086
31141
|
const delays = [1e3, 4e3, 16e3];
|
|
31087
31142
|
for (let attempt = 0; attempt < maxRetries; attempt++) {
|
|
31088
31143
|
try {
|
|
31089
31144
|
const response = await deliverWebhook(targetCheck.target, payload, webhookSecret);
|
|
31090
31145
|
if (response.status >= 200 && response.status < 300) {
|
|
31091
|
-
|
|
31146
|
+
log11.info("webhook.delivered", { event: payload.event, url: targetLabel, httpStatus: response.status });
|
|
31092
31147
|
this.logDelivery(projectId, notificationId, payload.event, "sent", null);
|
|
31093
31148
|
return;
|
|
31094
31149
|
}
|
|
31095
31150
|
const errorDetail = response.error ?? `HTTP ${response.status}`;
|
|
31096
|
-
|
|
31151
|
+
log11.warn("webhook.attempt-failed", { event: payload.event, url: targetLabel, attempt: attempt + 1, maxRetries, httpStatus: response.status, error: errorDetail });
|
|
31097
31152
|
if (attempt === maxRetries - 1) {
|
|
31098
31153
|
this.logDelivery(projectId, notificationId, payload.event, "failed", errorDetail);
|
|
31099
31154
|
}
|
|
@@ -31101,7 +31156,7 @@ var Notifier = class {
|
|
|
31101
31156
|
const errorDetail = err instanceof Error ? err.message : String(err);
|
|
31102
31157
|
if (attempt === maxRetries - 1) {
|
|
31103
31158
|
this.logDelivery(projectId, notificationId, payload.event, "failed", errorDetail);
|
|
31104
|
-
|
|
31159
|
+
log11.error("webhook.exhausted", { event: payload.event, url: targetLabel, maxRetries, error: errorDetail });
|
|
31105
31160
|
}
|
|
31106
31161
|
}
|
|
31107
31162
|
if (attempt < maxRetries - 1) {
|
|
@@ -31111,7 +31166,7 @@ var Notifier = class {
|
|
|
31111
31166
|
}
|
|
31112
31167
|
logDelivery(projectId, notificationId, event, status, error) {
|
|
31113
31168
|
this.db.insert(auditLog).values({
|
|
31114
|
-
id:
|
|
31169
|
+
id: crypto33.randomUUID(),
|
|
31115
31170
|
projectId,
|
|
31116
31171
|
actor: "scheduler",
|
|
31117
31172
|
action: `notification.${status}`,
|
|
@@ -31124,8 +31179,8 @@ var Notifier = class {
|
|
|
31124
31179
|
};
|
|
31125
31180
|
|
|
31126
31181
|
// src/run-coordinator.ts
|
|
31127
|
-
import { eq as
|
|
31128
|
-
var
|
|
31182
|
+
import { eq as eq39 } from "drizzle-orm";
|
|
31183
|
+
var log12 = createLogger("RunCoordinator");
|
|
31129
31184
|
var RunCoordinator = class {
|
|
31130
31185
|
constructor(db, notifier, intelligenceService, onInsightsGenerated, onAeroEvent) {
|
|
31131
31186
|
this.db = db;
|
|
@@ -31135,10 +31190,10 @@ var RunCoordinator = class {
|
|
|
31135
31190
|
this.onAeroEvent = onAeroEvent;
|
|
31136
31191
|
}
|
|
31137
31192
|
async onRunCompleted(runId, projectId) {
|
|
31138
|
-
const runRow = this.db.select().from(runs).where(
|
|
31193
|
+
const runRow = this.db.select().from(runs).where(eq39(runs.id, runId)).get();
|
|
31139
31194
|
const kind = runRow?.kind ?? RunKinds["answer-visibility"];
|
|
31140
31195
|
if (runRow?.trigger === RunTriggers.probe) {
|
|
31141
|
-
|
|
31196
|
+
log12.info("probe.skip-side-effects", { runId, projectId, kind });
|
|
31142
31197
|
return;
|
|
31143
31198
|
}
|
|
31144
31199
|
let insightCount = 0;
|
|
@@ -31155,18 +31210,18 @@ var RunCoordinator = class {
|
|
|
31155
31210
|
try {
|
|
31156
31211
|
await this.onInsightsGenerated(runId, projectId, result);
|
|
31157
31212
|
} catch (err) {
|
|
31158
|
-
|
|
31213
|
+
log12.error("insight-webhook.failed", { runId, error: err instanceof Error ? err.message : String(err) });
|
|
31159
31214
|
}
|
|
31160
31215
|
}
|
|
31161
31216
|
}
|
|
31162
31217
|
} catch (err) {
|
|
31163
|
-
|
|
31218
|
+
log12.error("intelligence.failed", { runId, error: err instanceof Error ? err.message : String(err) });
|
|
31164
31219
|
}
|
|
31165
31220
|
}
|
|
31166
31221
|
try {
|
|
31167
31222
|
await this.notifier.onRunCompleted(runId, projectId);
|
|
31168
31223
|
} catch (err) {
|
|
31169
|
-
|
|
31224
|
+
log12.error("notifier.failed", { runId, error: err instanceof Error ? err.message : String(err) });
|
|
31170
31225
|
}
|
|
31171
31226
|
if (this.onAeroEvent) {
|
|
31172
31227
|
try {
|
|
@@ -31179,7 +31234,7 @@ var RunCoordinator = class {
|
|
|
31179
31234
|
};
|
|
31180
31235
|
await this.onAeroEvent(ctx);
|
|
31181
31236
|
} catch (err) {
|
|
31182
|
-
|
|
31237
|
+
log12.error("aero.failed", { runId, error: err instanceof Error ? err.message : String(err) });
|
|
31183
31238
|
}
|
|
31184
31239
|
}
|
|
31185
31240
|
}
|
|
@@ -31194,7 +31249,7 @@ var RunCoordinator = class {
|
|
|
31194
31249
|
* so the Aero queue is never starved of a follow-up.
|
|
31195
31250
|
*/
|
|
31196
31251
|
buildDiscoveryAeroContext(runId, projectId, status, error) {
|
|
31197
|
-
const session = this.db.select().from(discoverySessions).where(
|
|
31252
|
+
const session = this.db.select().from(discoverySessions).where(eq39(discoverySessions.runId, runId)).get();
|
|
31198
31253
|
const competitorMap = session ? session.competitorMap : [];
|
|
31199
31254
|
return {
|
|
31200
31255
|
kind: RunKinds["aeo-discover-probe"],
|
|
@@ -31216,8 +31271,8 @@ var RunCoordinator = class {
|
|
|
31216
31271
|
};
|
|
31217
31272
|
|
|
31218
31273
|
// src/agent/session-registry.ts
|
|
31219
|
-
import
|
|
31220
|
-
import { eq as
|
|
31274
|
+
import crypto35 from "crypto";
|
|
31275
|
+
import { eq as eq41 } from "drizzle-orm";
|
|
31221
31276
|
|
|
31222
31277
|
// src/agent/session.ts
|
|
31223
31278
|
import fs14 from "fs";
|
|
@@ -31605,8 +31660,8 @@ function resolveSessionProviderAndModel(config, opts) {
|
|
|
31605
31660
|
}
|
|
31606
31661
|
|
|
31607
31662
|
// src/agent/memory-store.ts
|
|
31608
|
-
import
|
|
31609
|
-
import { and as
|
|
31663
|
+
import crypto34 from "crypto";
|
|
31664
|
+
import { and as and32, desc as desc19, eq as eq40, like as like2, sql as sql16 } from "drizzle-orm";
|
|
31610
31665
|
var COMPACTION_KEY_PREFIX = "compaction:";
|
|
31611
31666
|
var COMPACTION_NOTES_PER_SESSION = 3;
|
|
31612
31667
|
function rowToDto2(row) {
|
|
@@ -31620,7 +31675,7 @@ function rowToDto2(row) {
|
|
|
31620
31675
|
};
|
|
31621
31676
|
}
|
|
31622
31677
|
function listMemoryEntries(db, projectId, opts = {}) {
|
|
31623
|
-
const query = db.select().from(agentMemory).where(
|
|
31678
|
+
const query = db.select().from(agentMemory).where(eq40(agentMemory.projectId, projectId)).orderBy(desc19(agentMemory.updatedAt));
|
|
31624
31679
|
const rows = opts.limit === void 0 ? query.all() : query.limit(opts.limit).all();
|
|
31625
31680
|
return rows.map(rowToDto2);
|
|
31626
31681
|
}
|
|
@@ -31634,7 +31689,7 @@ function upsertMemoryEntry(db, args) {
|
|
|
31634
31689
|
throw new Error(`memory key prefix "${COMPACTION_KEY_PREFIX}" is reserved for compaction notes`);
|
|
31635
31690
|
}
|
|
31636
31691
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
31637
|
-
const id =
|
|
31692
|
+
const id = crypto34.randomUUID();
|
|
31638
31693
|
db.insert(agentMemory).values({
|
|
31639
31694
|
id,
|
|
31640
31695
|
projectId: args.projectId,
|
|
@@ -31651,12 +31706,12 @@ function upsertMemoryEntry(db, args) {
|
|
|
31651
31706
|
updatedAt: now
|
|
31652
31707
|
}
|
|
31653
31708
|
}).run();
|
|
31654
|
-
const row = db.select().from(agentMemory).where(
|
|
31709
|
+
const row = db.select().from(agentMemory).where(and32(eq40(agentMemory.projectId, args.projectId), eq40(agentMemory.key, args.key))).get();
|
|
31655
31710
|
if (!row) throw new Error("memory upsert produced no row");
|
|
31656
31711
|
return rowToDto2(row);
|
|
31657
31712
|
}
|
|
31658
31713
|
function deleteMemoryEntry(db, projectId, key) {
|
|
31659
|
-
const result = db.delete(agentMemory).where(
|
|
31714
|
+
const result = db.delete(agentMemory).where(and32(eq40(agentMemory.projectId, projectId), eq40(agentMemory.key, key))).run();
|
|
31660
31715
|
const changes = result.changes ?? 0;
|
|
31661
31716
|
return changes > 0;
|
|
31662
31717
|
}
|
|
@@ -31671,7 +31726,7 @@ function writeCompactionNote(db, args) {
|
|
|
31671
31726
|
}
|
|
31672
31727
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
31673
31728
|
const key = `${COMPACTION_KEY_PREFIX}${args.sessionId}:${now}`;
|
|
31674
|
-
const id =
|
|
31729
|
+
const id = crypto34.randomUUID();
|
|
31675
31730
|
let inserted;
|
|
31676
31731
|
db.transaction((tx) => {
|
|
31677
31732
|
tx.insert(agentMemory).values({
|
|
@@ -31685,16 +31740,16 @@ function writeCompactionNote(db, args) {
|
|
|
31685
31740
|
}).run();
|
|
31686
31741
|
const sessionPrefix = `${COMPACTION_KEY_PREFIX}${args.sessionId}:`;
|
|
31687
31742
|
const existing = tx.select({ id: agentMemory.id, updatedAt: agentMemory.updatedAt }).from(agentMemory).where(
|
|
31688
|
-
|
|
31689
|
-
|
|
31743
|
+
and32(
|
|
31744
|
+
eq40(agentMemory.projectId, args.projectId),
|
|
31690
31745
|
like2(agentMemory.key, `${sessionPrefix}%`)
|
|
31691
31746
|
)
|
|
31692
|
-
).orderBy(
|
|
31747
|
+
).orderBy(desc19(agentMemory.updatedAt)).all();
|
|
31693
31748
|
const stale = existing.slice(COMPACTION_NOTES_PER_SESSION).map((r) => r.id);
|
|
31694
31749
|
if (stale.length > 0) {
|
|
31695
31750
|
tx.delete(agentMemory).where(sql16`${agentMemory.id} IN (${sql16.join(stale.map((s) => sql16`${s}`), sql16`, `)})`).run();
|
|
31696
31751
|
}
|
|
31697
|
-
const row = tx.select().from(agentMemory).where(
|
|
31752
|
+
const row = tx.select().from(agentMemory).where(and32(eq40(agentMemory.projectId, args.projectId), eq40(agentMemory.key, key))).get();
|
|
31698
31753
|
if (row) inserted = rowToDto2(row);
|
|
31699
31754
|
});
|
|
31700
31755
|
if (!inserted) throw new Error("compaction note write produced no row");
|
|
@@ -31827,7 +31882,7 @@ async function compactMessages(args) {
|
|
|
31827
31882
|
}
|
|
31828
31883
|
|
|
31829
31884
|
// src/agent/session-registry.ts
|
|
31830
|
-
var
|
|
31885
|
+
var log13 = createLogger("SessionRegistry");
|
|
31831
31886
|
var MAX_HYDRATE_NOTES = 20;
|
|
31832
31887
|
var MAX_HYDRATE_BYTES = 32 * 1024;
|
|
31833
31888
|
function escapeMemoryFragment(value) {
|
|
@@ -31876,7 +31931,7 @@ var SessionRegistry = class {
|
|
|
31876
31931
|
modelProvider: effectiveProvider,
|
|
31877
31932
|
modelId: effectiveModelId,
|
|
31878
31933
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
31879
|
-
}).where(
|
|
31934
|
+
}).where(eq41(agentSessions.projectId, projectId)).run();
|
|
31880
31935
|
}
|
|
31881
31936
|
const agent2 = createAeroSession({
|
|
31882
31937
|
projectName,
|
|
@@ -32054,13 +32109,13 @@ ${lines.join("\n")}
|
|
|
32054
32109
|
agent.state.messages = result.messages;
|
|
32055
32110
|
agent.state.systemPrompt = this.buildHydratedSystemPrompt(projectId, row.systemPrompt);
|
|
32056
32111
|
this.save(projectName);
|
|
32057
|
-
|
|
32112
|
+
log13.info("compaction.completed", {
|
|
32058
32113
|
projectName,
|
|
32059
32114
|
removedCount: result.removedCount,
|
|
32060
32115
|
summaryBytes: Buffer.byteLength(result.summary, "utf8")
|
|
32061
32116
|
});
|
|
32062
32117
|
} catch (err) {
|
|
32063
|
-
|
|
32118
|
+
log13.error("compaction.failed", {
|
|
32064
32119
|
projectName,
|
|
32065
32120
|
error: err instanceof Error ? err.message : String(err)
|
|
32066
32121
|
});
|
|
@@ -32090,7 +32145,7 @@ ${lines.join("\n")}
|
|
|
32090
32145
|
modelProvider: nextProvider,
|
|
32091
32146
|
modelId: nextModelId,
|
|
32092
32147
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
32093
|
-
}).where(
|
|
32148
|
+
}).where(eq41(agentSessions.projectId, projectId)).run();
|
|
32094
32149
|
}
|
|
32095
32150
|
/** Persist a session's transcript back to the DB. Call after any run settles. */
|
|
32096
32151
|
save(projectName) {
|
|
@@ -32157,7 +32212,7 @@ ${lines.join("\n")}
|
|
|
32157
32212
|
await agent.prompt(msgs);
|
|
32158
32213
|
this.save(projectName);
|
|
32159
32214
|
} catch (err) {
|
|
32160
|
-
|
|
32215
|
+
log13.error("drain.failed", {
|
|
32161
32216
|
projectName,
|
|
32162
32217
|
error: err instanceof Error ? err.message : String(err)
|
|
32163
32218
|
});
|
|
@@ -32252,17 +32307,17 @@ ${lines.join("\n")}
|
|
|
32252
32307
|
return id;
|
|
32253
32308
|
}
|
|
32254
32309
|
tryResolveProjectId(projectName) {
|
|
32255
|
-
const row = this.opts.db.select({ id: projects.id }).from(projects).where(
|
|
32310
|
+
const row = this.opts.db.select({ id: projects.id }).from(projects).where(eq41(projects.name, projectName)).get();
|
|
32256
32311
|
return row?.id;
|
|
32257
32312
|
}
|
|
32258
32313
|
loadRow(projectId) {
|
|
32259
|
-
const row = this.opts.db.select().from(agentSessions).where(
|
|
32314
|
+
const row = this.opts.db.select().from(agentSessions).where(eq41(agentSessions.projectId, projectId)).get();
|
|
32260
32315
|
return row ?? null;
|
|
32261
32316
|
}
|
|
32262
32317
|
insertRow(params) {
|
|
32263
32318
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
32264
32319
|
this.opts.db.insert(agentSessions).values({
|
|
32265
|
-
id:
|
|
32320
|
+
id: crypto35.randomUUID(),
|
|
32266
32321
|
projectId: params.projectId,
|
|
32267
32322
|
systemPrompt: params.systemPrompt,
|
|
32268
32323
|
modelProvider: params.provider ?? params.modelProvider ?? AgentProviderIds.claude,
|
|
@@ -32275,14 +32330,14 @@ ${lines.join("\n")}
|
|
|
32275
32330
|
}
|
|
32276
32331
|
updateRow(projectId, patch) {
|
|
32277
32332
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
32278
|
-
this.opts.db.update(agentSessions).set({ ...patch, updatedAt: now }).where(
|
|
32333
|
+
this.opts.db.update(agentSessions).set({ ...patch, updatedAt: now }).where(eq41(agentSessions.projectId, projectId)).run();
|
|
32279
32334
|
}
|
|
32280
32335
|
};
|
|
32281
32336
|
|
|
32282
32337
|
// src/agent/agent-routes.ts
|
|
32283
|
-
import { eq as
|
|
32338
|
+
import { eq as eq42 } from "drizzle-orm";
|
|
32284
32339
|
function resolveProject2(db, name) {
|
|
32285
|
-
const row = db.select({ id: projects.id, name: projects.name }).from(projects).where(
|
|
32340
|
+
const row = db.select({ id: projects.id, name: projects.name }).from(projects).where(eq42(projects.name, name)).get();
|
|
32286
32341
|
if (!row) throw notFound("project", name);
|
|
32287
32342
|
return row;
|
|
32288
32343
|
}
|
|
@@ -32291,7 +32346,7 @@ function registerAgentRoutes(app, opts) {
|
|
|
32291
32346
|
"/projects/:name/agent/transcript",
|
|
32292
32347
|
async (request) => {
|
|
32293
32348
|
const project = resolveProject2(opts.db, request.params.name);
|
|
32294
|
-
const row = opts.db.select().from(agentSessions).where(
|
|
32349
|
+
const row = opts.db.select().from(agentSessions).where(eq42(agentSessions.projectId, project.id)).get();
|
|
32295
32350
|
if (!row) {
|
|
32296
32351
|
return { messages: [], modelProvider: null, modelId: null, updatedAt: null };
|
|
32297
32352
|
}
|
|
@@ -32315,7 +32370,7 @@ function registerAgentRoutes(app, opts) {
|
|
|
32315
32370
|
async (request) => {
|
|
32316
32371
|
const project = resolveProject2(opts.db, request.params.name);
|
|
32317
32372
|
opts.sessionRegistry.reset(project.name);
|
|
32318
|
-
opts.db.update(agentSessions).set({ messages: "[]", followUpQueue: "[]", updatedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(
|
|
32373
|
+
opts.db.update(agentSessions).set({ messages: "[]", followUpQueue: "[]", updatedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(eq42(agentSessions.projectId, project.id)).run();
|
|
32319
32374
|
return { status: "reset" };
|
|
32320
32375
|
}
|
|
32321
32376
|
);
|
|
@@ -32672,7 +32727,7 @@ function formatAuditFactorScore(factor) {
|
|
|
32672
32727
|
}
|
|
32673
32728
|
|
|
32674
32729
|
// src/snapshot-service.ts
|
|
32675
|
-
var
|
|
32730
|
+
var log14 = createLogger("Snapshot");
|
|
32676
32731
|
var ANALYSIS_PROVIDER_PRIORITY = ["openai", "claude", "gemini", "perplexity", "local"];
|
|
32677
32732
|
var SNAPSHOT_QUERY_COUNT = 6;
|
|
32678
32733
|
var ProviderExecutionGate2 = class {
|
|
@@ -32815,7 +32870,7 @@ var SnapshotService = class {
|
|
|
32815
32870
|
return mapAuditReport(report);
|
|
32816
32871
|
} catch (err) {
|
|
32817
32872
|
const message = err instanceof Error ? err.message : String(err);
|
|
32818
|
-
|
|
32873
|
+
log14.warn("audit.failed", { homepageUrl, error: message });
|
|
32819
32874
|
return {
|
|
32820
32875
|
url: homepageUrl,
|
|
32821
32876
|
finalUrl: homepageUrl,
|
|
@@ -32845,7 +32900,7 @@ var SnapshotService = class {
|
|
|
32845
32900
|
queries: parsedQueries
|
|
32846
32901
|
};
|
|
32847
32902
|
} catch (err) {
|
|
32848
|
-
|
|
32903
|
+
log14.warn("profile.generation-failed", {
|
|
32849
32904
|
domain: ctx.domain,
|
|
32850
32905
|
provider: ctx.analysisProvider.adapter.name,
|
|
32851
32906
|
error: err instanceof Error ? err.message : String(err)
|
|
@@ -32987,7 +33042,7 @@ var SnapshotService = class {
|
|
|
32987
33042
|
recommendedActions: uniqueStrings(parsed.recommendedActions ?? []).slice(0, 4)
|
|
32988
33043
|
};
|
|
32989
33044
|
} catch (err) {
|
|
32990
|
-
|
|
33045
|
+
log14.warn("response.analysis-failed", {
|
|
32991
33046
|
provider: ctx.analysisProvider.adapter.name,
|
|
32992
33047
|
error: err instanceof Error ? err.message : String(err)
|
|
32993
33048
|
});
|
|
@@ -33272,7 +33327,7 @@ function clipText(value, length) {
|
|
|
33272
33327
|
// src/server.ts
|
|
33273
33328
|
var _require3 = createRequire4(import.meta.url);
|
|
33274
33329
|
var { version: PKG_VERSION2 } = _require3("../package.json");
|
|
33275
|
-
var
|
|
33330
|
+
var log15 = createLogger("Server");
|
|
33276
33331
|
var DEFAULT_QUOTA = {
|
|
33277
33332
|
maxConcurrency: 2,
|
|
33278
33333
|
maxRequestsPerMinute: 10,
|
|
@@ -33302,14 +33357,14 @@ function summarizeProviderConfig(provider, config) {
|
|
|
33302
33357
|
};
|
|
33303
33358
|
}
|
|
33304
33359
|
function hashApiKey(key) {
|
|
33305
|
-
return
|
|
33360
|
+
return crypto36.createHash("sha256").update(key).digest("hex");
|
|
33306
33361
|
}
|
|
33307
33362
|
var DASHBOARD_SCRYPT_KEYLEN = 64;
|
|
33308
33363
|
var DASHBOARD_SCRYPT_COST = 1 << 15;
|
|
33309
33364
|
var DASHBOARD_SCRYPT_MAXMEM = 64 * 1024 * 1024;
|
|
33310
33365
|
function hashDashboardPassword(password) {
|
|
33311
|
-
const salt =
|
|
33312
|
-
const derived =
|
|
33366
|
+
const salt = crypto36.randomBytes(16);
|
|
33367
|
+
const derived = crypto36.scryptSync(password, salt, DASHBOARD_SCRYPT_KEYLEN, {
|
|
33313
33368
|
N: DASHBOARD_SCRYPT_COST,
|
|
33314
33369
|
maxmem: DASHBOARD_SCRYPT_MAXMEM
|
|
33315
33370
|
});
|
|
@@ -33330,18 +33385,18 @@ function verifyDashboardPassword(password, storedHash) {
|
|
|
33330
33385
|
} catch {
|
|
33331
33386
|
return { ok: false, needsRehash: false };
|
|
33332
33387
|
}
|
|
33333
|
-
const derived =
|
|
33388
|
+
const derived = crypto36.scryptSync(password, salt, expected.length, {
|
|
33334
33389
|
N: DASHBOARD_SCRYPT_COST,
|
|
33335
33390
|
maxmem: DASHBOARD_SCRYPT_MAXMEM
|
|
33336
33391
|
});
|
|
33337
33392
|
if (derived.length !== expected.length) return { ok: false, needsRehash: false };
|
|
33338
|
-
return { ok:
|
|
33393
|
+
return { ok: crypto36.timingSafeEqual(derived, expected), needsRehash: false };
|
|
33339
33394
|
}
|
|
33340
33395
|
if (/^[a-f0-9]{64}$/i.test(storedHash)) {
|
|
33341
33396
|
const candidate = Buffer.from(hashApiKey(password), "hex");
|
|
33342
33397
|
const expected = Buffer.from(storedHash, "hex");
|
|
33343
33398
|
if (candidate.length !== expected.length) return { ok: false, needsRehash: false };
|
|
33344
|
-
const ok =
|
|
33399
|
+
const ok = crypto36.timingSafeEqual(candidate, expected);
|
|
33345
33400
|
return { ok, needsRehash: ok };
|
|
33346
33401
|
}
|
|
33347
33402
|
return { ok: false, needsRehash: false };
|
|
@@ -33400,7 +33455,7 @@ function applyLegacyCredentials(rows, config) {
|
|
|
33400
33455
|
}
|
|
33401
33456
|
if (migratedGoogle > 0) {
|
|
33402
33457
|
saveConfigPatch({ google: config.google });
|
|
33403
|
-
|
|
33458
|
+
log15.info("credentials.migrated", { type: "google", count: migratedGoogle });
|
|
33404
33459
|
}
|
|
33405
33460
|
let migratedGa4 = 0;
|
|
33406
33461
|
for (const row of rows.ga4) {
|
|
@@ -33418,7 +33473,7 @@ function applyLegacyCredentials(rows, config) {
|
|
|
33418
33473
|
}
|
|
33419
33474
|
if (migratedGa4 > 0) {
|
|
33420
33475
|
saveConfigPatch({ ga4: config.ga4 });
|
|
33421
|
-
|
|
33476
|
+
log15.info("credentials.migrated", { type: "ga4", count: migratedGa4 });
|
|
33422
33477
|
}
|
|
33423
33478
|
}
|
|
33424
33479
|
async function createServer(opts) {
|
|
@@ -33450,11 +33505,11 @@ async function createServer(opts) {
|
|
|
33450
33505
|
applyLegacyCredentials(legacyRows, opts.config);
|
|
33451
33506
|
dropLegacyCredentialColumns(opts.db);
|
|
33452
33507
|
} catch (err) {
|
|
33453
|
-
|
|
33508
|
+
log15.warn("credentials.migration.failed", {
|
|
33454
33509
|
error: err instanceof Error ? err.message : String(err)
|
|
33455
33510
|
});
|
|
33456
33511
|
}
|
|
33457
|
-
|
|
33512
|
+
log15.info("providers.configured", { providers: Object.keys(providers).filter((k) => {
|
|
33458
33513
|
const p = providers[k];
|
|
33459
33514
|
return p?.apiKey || p?.baseUrl || p?.vertexProject;
|
|
33460
33515
|
}) });
|
|
@@ -33503,7 +33558,7 @@ async function createServer(opts) {
|
|
|
33503
33558
|
intelligenceService,
|
|
33504
33559
|
(runId, projectId, result) => notifier.dispatchInsightWebhooks(runId, projectId, result),
|
|
33505
33560
|
async (ctx) => {
|
|
33506
|
-
const project = opts.db.select({ name: projects.name }).from(projects).where(
|
|
33561
|
+
const project = opts.db.select({ name: projects.name }).from(projects).where(eq43(projects.id, ctx.projectId)).get();
|
|
33507
33562
|
if (!project) return;
|
|
33508
33563
|
let content;
|
|
33509
33564
|
if (ctx.kind === RunKinds["aeo-discover-probe"]) {
|
|
@@ -33660,7 +33715,7 @@ async function createServer(opts) {
|
|
|
33660
33715
|
return removed;
|
|
33661
33716
|
}
|
|
33662
33717
|
};
|
|
33663
|
-
const googleStateSecret = process.env.GOOGLE_STATE_SECRET ??
|
|
33718
|
+
const googleStateSecret = process.env.GOOGLE_STATE_SECRET ?? crypto36.randomBytes(32).toString("hex");
|
|
33664
33719
|
const googleConnectionStore = {
|
|
33665
33720
|
listConnections: (domain) => listGoogleConnections(opts.config, domain),
|
|
33666
33721
|
getConnection: (domain, connectionType) => getGoogleConnection(opts.config, domain, connectionType),
|
|
@@ -33706,11 +33761,11 @@ async function createServer(opts) {
|
|
|
33706
33761
|
const apiPrefix = basePath ? `${basePath}api/v1` : "/api/v1";
|
|
33707
33762
|
if (opts.config.apiKey) {
|
|
33708
33763
|
const keyHash = hashApiKey(opts.config.apiKey);
|
|
33709
|
-
const existing = opts.db.select().from(apiKeys).where(
|
|
33764
|
+
const existing = opts.db.select().from(apiKeys).where(eq43(apiKeys.keyHash, keyHash)).get();
|
|
33710
33765
|
if (!existing) {
|
|
33711
33766
|
const prefix = opts.config.apiKey.slice(0, 12);
|
|
33712
33767
|
opts.db.insert(apiKeys).values({
|
|
33713
|
-
id: `key_${
|
|
33768
|
+
id: `key_${crypto36.randomBytes(8).toString("hex")}`,
|
|
33714
33769
|
name: "default",
|
|
33715
33770
|
keyHash,
|
|
33716
33771
|
keyPrefix: prefix,
|
|
@@ -33734,7 +33789,7 @@ async function createServer(opts) {
|
|
|
33734
33789
|
};
|
|
33735
33790
|
const createSession = (apiKeyId) => {
|
|
33736
33791
|
pruneExpiredSessions();
|
|
33737
|
-
const sessionId =
|
|
33792
|
+
const sessionId = crypto36.randomBytes(32).toString("hex");
|
|
33738
33793
|
sessions.set(sessionId, {
|
|
33739
33794
|
apiKeyId,
|
|
33740
33795
|
expiresAt: Date.now() + SESSION_TTL_MS
|
|
@@ -33758,7 +33813,7 @@ async function createServer(opts) {
|
|
|
33758
33813
|
};
|
|
33759
33814
|
const getDefaultApiKey = () => {
|
|
33760
33815
|
if (!opts.config.apiKey) return void 0;
|
|
33761
|
-
return opts.db.select().from(apiKeys).where(
|
|
33816
|
+
return opts.db.select().from(apiKeys).where(eq43(apiKeys.keyHash, hashApiKey(opts.config.apiKey))).get();
|
|
33762
33817
|
};
|
|
33763
33818
|
const createPasswordSession = (reply) => {
|
|
33764
33819
|
const key = getDefaultApiKey();
|
|
@@ -33820,12 +33875,12 @@ async function createServer(opts) {
|
|
|
33820
33875
|
return reply.send({ authenticated: true });
|
|
33821
33876
|
}
|
|
33822
33877
|
if (apiKey) {
|
|
33823
|
-
const key = opts.db.select().from(apiKeys).where(
|
|
33878
|
+
const key = opts.db.select().from(apiKeys).where(eq43(apiKeys.keyHash, hashApiKey(apiKey))).get();
|
|
33824
33879
|
if (!key || key.revokedAt) {
|
|
33825
33880
|
const err2 = authInvalid();
|
|
33826
33881
|
return reply.status(err2.statusCode).send(err2.toJSON());
|
|
33827
33882
|
}
|
|
33828
|
-
opts.db.update(apiKeys).set({ lastUsedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(
|
|
33883
|
+
opts.db.update(apiKeys).set({ lastUsedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(eq43(apiKeys.id, key.id)).run();
|
|
33829
33884
|
const sessionId = createSession(key.id);
|
|
33830
33885
|
reply.header("set-cookie", serializeSessionCookie({
|
|
33831
33886
|
name: SESSION_COOKIE_NAME,
|
|
@@ -33923,7 +33978,7 @@ async function createServer(opts) {
|
|
|
33923
33978
|
executeGscSync(opts.db, runId, projectId, {
|
|
33924
33979
|
...syncOpts,
|
|
33925
33980
|
config: opts.config
|
|
33926
|
-
}).catch((err) => {
|
|
33981
|
+
}).then(() => maybeRefreshGscCoverage(opts.db, opts.config, projectId)).catch((err) => {
|
|
33927
33982
|
app.log.error({ runId, err }, "GSC sync failed");
|
|
33928
33983
|
});
|
|
33929
33984
|
},
|
|
@@ -33961,7 +34016,7 @@ async function createServer(opts) {
|
|
|
33961
34016
|
deps: {
|
|
33962
34017
|
enqueueAutoExtract: ({ projectId, release: r }) => {
|
|
33963
34018
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
33964
|
-
const runId =
|
|
34019
|
+
const runId = crypto36.randomUUID();
|
|
33965
34020
|
opts.db.insert(runs).values({
|
|
33966
34021
|
id: runId,
|
|
33967
34022
|
projectId,
|
|
@@ -34043,6 +34098,12 @@ async function createServer(opts) {
|
|
|
34043
34098
|
executeBingInspectSitemap(opts.db, runId, projectId, {
|
|
34044
34099
|
...inspectOpts,
|
|
34045
34100
|
config: opts.config
|
|
34101
|
+
}).then(() => {
|
|
34102
|
+
const finished = opts.db.select({ status: runs.status }).from(runs).where(eq43(runs.id, runId)).get();
|
|
34103
|
+
if (finished?.status === RunStatuses.completed || finished?.status === RunStatuses.partial) {
|
|
34104
|
+
return maybeRefreshGscCoverage(opts.db, opts.config, projectId);
|
|
34105
|
+
}
|
|
34106
|
+
return null;
|
|
34046
34107
|
}).catch((err) => {
|
|
34047
34108
|
app.log.error({ runId, err }, "Bing inspect sitemap failed");
|
|
34048
34109
|
});
|
|
@@ -34125,7 +34186,7 @@ async function createServer(opts) {
|
|
|
34125
34186
|
const targetProjectIds = affectedProjectIds.length > 0 ? affectedProjectIds : [null];
|
|
34126
34187
|
const createdAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
34127
34188
|
opts.db.insert(auditLog).values(targetProjectIds.map((projectId) => ({
|
|
34128
|
-
id:
|
|
34189
|
+
id: crypto36.randomUUID(),
|
|
34129
34190
|
projectId,
|
|
34130
34191
|
actor: "api",
|
|
34131
34192
|
action: existing ? "provider.updated" : "provider.created",
|