@canonry/canonry 5.12.0 → 5.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/assets/agent-workspace/skills/canonry/references/canonry-cli.md +5 -0
- package/assets/assets/{AuditHistoryPanel-DL_NSpfm.js → AuditHistoryPanel-DWDOPf8c.js} +1 -1
- package/assets/assets/{BacklinksPage-BtcBybtX.js → BacklinksPage-DGxq-iU6.js} +1 -1
- package/assets/assets/{HistoryPage-jXj-qi9c.js → HistoryPage-DR91m-gT.js} +1 -1
- package/assets/assets/{MeasurementPropertyPage-DKw401ok.js → MeasurementPropertyPage-a8poltfM.js} +1 -1
- package/assets/assets/{ProjectPage-og4qfFFD.js → ProjectPage-CCCJE7rA.js} +1 -1
- package/assets/assets/{RunRow-nK5iaZe1.js → RunRow-Xe63DyPK.js} +1 -1
- package/assets/assets/{RunsPage-CMgr3YSF.js → RunsPage-DK119ZZw.js} +1 -1
- package/assets/assets/{SettingsPage-QtElCcew.js → SettingsPage-G461foD-.js} +1 -1
- package/assets/assets/{SiteHealthSection-DNdVVl30.js → SiteHealthSection-CJnHkGM2.js} +3 -3
- package/assets/assets/{TrafficPage-C0WgvfRj.js → TrafficPage-AsWgc7Ot.js} +1 -1
- package/assets/assets/{TrafficSourceDetailPage-jOTedQtt.js → TrafficSourceDetailPage-D10yBQoD.js} +1 -1
- package/assets/assets/{extract-error-message-DO_053o0.js → extract-error-message-PP4HD_Dp.js} +1 -1
- package/assets/assets/{index-CnVwWE3V.js → index-CCIeAE4J.js} +22 -22
- package/assets/assets/{react-sigma_core.esm.min-DJttq2UK.js → react-sigma_core.esm.min-CkyyYkKU.js} +1 -1
- package/assets/assets/{v2-overview-adapter-BC_oa__K.js → v2-overview-adapter-DW-In1hp.js} +1 -1
- package/assets/index.html +1 -1
- package/dist/{chunk-S4BIBUCK.js → chunk-6YLNO2T5.js} +2 -2
- package/dist/{chunk-LWGYRN3T.js → chunk-IDZQ5H3J.js} +2 -2
- package/dist/{chunk-WJGUBZSW.js → chunk-IPGBK7XR.js} +65 -1
- package/dist/{chunk-DOC4QQLS.js → chunk-Q35D2R2P.js} +2490 -2094
- package/dist/{chunk-KBSAEALE.js → chunk-TDHRUXN3.js} +398 -151
- package/dist/{chunk-NYRRWWEE.js → chunk-UWJAATGC.js} +3015 -2923
- package/dist/cli.js +133 -7
- package/dist/{demo-server-OTXS6ZHS.js → demo-server-IZFTJI7E.js} +3 -3
- package/dist/index.js +5 -5
- package/dist/{intelligence-service-K4U55A2J.js → intelligence-service-PM7MC5VC.js} +2 -2
- package/dist/mcp.js +3 -3
- package/package.json +9 -9
|
@@ -17,12 +17,12 @@ import {
|
|
|
17
17
|
resolveOperatorApiKeyIds,
|
|
18
18
|
runWithUsageTags,
|
|
19
19
|
saveConfigPatch
|
|
20
|
-
} from "./chunk-
|
|
20
|
+
} from "./chunk-IPGBK7XR.js";
|
|
21
21
|
import {
|
|
22
22
|
deleteSiteCrawlGraphLayout,
|
|
23
23
|
persistSiteCrawlGraphLayout,
|
|
24
24
|
prepareSiteCrawlGraphLayout
|
|
25
|
-
} from "./chunk-
|
|
25
|
+
} from "./chunk-IDZQ5H3J.js";
|
|
26
26
|
import {
|
|
27
27
|
PACKAGE_VERSION
|
|
28
28
|
} from "./chunk-AOJU5LKX.js";
|
|
@@ -134,7 +134,10 @@ import {
|
|
|
134
134
|
loadDuckdb,
|
|
135
135
|
markSessionFailed,
|
|
136
136
|
measurementRunCompleteness,
|
|
137
|
+
measurementRunSlotState,
|
|
138
|
+
measurementSlotKey,
|
|
137
139
|
migrate,
|
|
140
|
+
newerFullSweep,
|
|
138
141
|
nextRunFromCron,
|
|
139
142
|
nextRunFromSchedule,
|
|
140
143
|
notifications,
|
|
@@ -164,6 +167,7 @@ import {
|
|
|
164
167
|
resolveVercelSyncDeadlineMs,
|
|
165
168
|
resolveWebhookTarget,
|
|
166
169
|
runChecks,
|
|
170
|
+
runFills,
|
|
167
171
|
runs,
|
|
168
172
|
schedules,
|
|
169
173
|
serializeUserSessionCookie,
|
|
@@ -179,7 +183,7 @@ import {
|
|
|
179
183
|
siteLivenessState,
|
|
180
184
|
toAlertView,
|
|
181
185
|
usageCounters
|
|
182
|
-
} from "./chunk-
|
|
186
|
+
} from "./chunk-Q35D2R2P.js";
|
|
183
187
|
import {
|
|
184
188
|
AGENT_MEMORY_VALUE_MAX_BYTES,
|
|
185
189
|
AGENT_NONE,
|
|
@@ -211,6 +215,7 @@ import {
|
|
|
211
215
|
ONBOARDING_FLOW_VERSION,
|
|
212
216
|
ProviderNames,
|
|
213
217
|
ROUTE_CAPABLE_CITED_URL_PROVIDERS,
|
|
218
|
+
RUN_FILL_PROVIDER_BREAKER,
|
|
214
219
|
ResearchQueryStatuses,
|
|
215
220
|
ResearchRunStatuses,
|
|
216
221
|
RunKinds,
|
|
@@ -272,6 +277,7 @@ import {
|
|
|
272
277
|
filterCapturedCitedUrls,
|
|
273
278
|
forbidden,
|
|
274
279
|
formatIsoDateInTimeZone,
|
|
280
|
+
formatRunErrorOneLine,
|
|
275
281
|
frameAncestorsHeaderValue,
|
|
276
282
|
googleAdsAccessibleCustomerDtoSchema,
|
|
277
283
|
googleAdsAccessibleCustomersResponseSchema,
|
|
@@ -313,6 +319,7 @@ import {
|
|
|
313
319
|
observeTemplateLinkEdges,
|
|
314
320
|
parseMeasurementRunManifestV1,
|
|
315
321
|
parseOriginList,
|
|
322
|
+
parseRunError,
|
|
316
323
|
providerError,
|
|
317
324
|
providerSupportsLocationContext,
|
|
318
325
|
registrableDomain,
|
|
@@ -337,7 +344,7 @@ import {
|
|
|
337
344
|
visibilityReportRequestSchema,
|
|
338
345
|
winnabilityClassLabel,
|
|
339
346
|
withRetry
|
|
340
|
-
} from "./chunk-
|
|
347
|
+
} from "./chunk-UWJAATGC.js";
|
|
341
348
|
|
|
342
349
|
// src/runtime-context.ts
|
|
343
350
|
function cliRuntimeContext(env = process.env, stdio = process) {
|
|
@@ -6483,7 +6490,7 @@ function nonBlank(value) {
|
|
|
6483
6490
|
return trimmed ? trimmed : void 0;
|
|
6484
6491
|
}
|
|
6485
6492
|
function resolveBuildCommit(env = process.env) {
|
|
6486
|
-
const embedded = true ? "
|
|
6493
|
+
const embedded = true ? "894086f6a60be75c0ade892884adf57ef7a38291" : void 0;
|
|
6487
6494
|
return nonBlank(embedded) ?? nonBlank(env.CANONRY_COMMIT);
|
|
6488
6495
|
}
|
|
6489
6496
|
function resolveInstanceIdentity(env = process.env) {
|
|
@@ -6831,6 +6838,8 @@ var JobRunner = class {
|
|
|
6831
6838
|
this.registry = registry;
|
|
6832
6839
|
}
|
|
6833
6840
|
recoverStaleRuns() {
|
|
6841
|
+
const staleFills = this.db.update(runFills).set({ status: "failed", finishedAt: (/* @__PURE__ */ new Date()).toISOString(), error: "Server restarted while the fill was in progress" }).where(inArray(runFills.status, ["queued", "running"])).run();
|
|
6842
|
+
if (staleFills.changes > 0) log2.warn("fill.recovered-stale", { count: staleFills.changes });
|
|
6834
6843
|
const stale = this.db.select({
|
|
6835
6844
|
id: runs.id,
|
|
6836
6845
|
projectId: runs.projectId,
|
|
@@ -7156,146 +7165,19 @@ var JobRunner = class {
|
|
|
7156
7165
|
}
|
|
7157
7166
|
}
|
|
7158
7167
|
};
|
|
7159
|
-
const
|
|
7160
|
-
|
|
7161
|
-
|
|
7162
|
-
|
|
7163
|
-
|
|
7164
|
-
|
|
7165
|
-
|
|
7166
|
-
|
|
7167
|
-
|
|
7168
|
-
|
|
7169
|
-
try {
|
|
7170
|
-
await gate.run(async () => {
|
|
7171
|
-
this.throwIfRunCancelled(runId);
|
|
7172
|
-
providerDispatchCounts.set(providerName, (providerDispatchCounts.get(providerName) ?? 0) + 1);
|
|
7173
|
-
const raw = await adapter.executeTrackedQuery(
|
|
7174
|
-
{
|
|
7175
|
-
query: unit.queryText,
|
|
7176
|
-
canonicalDomains: allDomains,
|
|
7177
|
-
competitorDomains,
|
|
7178
|
-
location: requestedContext ?? void 0
|
|
7179
|
-
},
|
|
7180
|
-
config
|
|
7181
|
-
);
|
|
7182
|
-
this.throwIfRunCancelled(runId);
|
|
7183
|
-
const providerResult = adapter.normalizeResult(raw);
|
|
7184
|
-
const rawGroundingSources = providerResult.groundingSources;
|
|
7185
|
-
const normalized = {
|
|
7186
|
-
...providerResult,
|
|
7187
|
-
groundingSources: Array.isArray(rawGroundingSources) ? rawGroundingSources : []
|
|
7188
|
-
};
|
|
7189
|
-
let citedUrlCapture;
|
|
7190
|
-
try {
|
|
7191
|
-
citedUrlCapture = await captureCitedUrls(providerName, rawGroundingSources);
|
|
7192
|
-
} catch (err) {
|
|
7193
|
-
citedUrlCapture = {
|
|
7194
|
-
citedUrls: [],
|
|
7195
|
-
captureStatus: "failed",
|
|
7196
|
-
sourceCount: normalized.groundingSources.length,
|
|
7197
|
-
resolvedCount: 0,
|
|
7198
|
-
captureVersion: CITED_URL_CAPTURE_VERSION
|
|
7199
|
-
};
|
|
7200
|
-
log2.warn("query.cited-url-capture-failed", {
|
|
7201
|
-
runId,
|
|
7202
|
-
provider: providerName,
|
|
7203
|
-
query: unit.queryText,
|
|
7204
|
-
error: describeError(err)
|
|
7205
|
-
});
|
|
7206
|
-
}
|
|
7207
|
-
this.throwIfRunCancelled(runId);
|
|
7208
|
-
const citationState = determineCitationState(normalized, allDomains);
|
|
7209
|
-
const answerMentioned = determineAnswerMentioned(
|
|
7210
|
-
normalized.answerText,
|
|
7211
|
-
allBrandNames,
|
|
7212
|
-
allDomains
|
|
7213
|
-
);
|
|
7214
|
-
const overlap = computeCompetitorOverlap(normalized, competitorDomains);
|
|
7215
|
-
const extractedCompetitors = extractRecommendedCompetitors(
|
|
7216
|
-
normalized.answerText,
|
|
7217
|
-
allDomains,
|
|
7218
|
-
normalized.citedDomains,
|
|
7219
|
-
competitorDomains,
|
|
7220
|
-
allBrandNames
|
|
7221
|
-
);
|
|
7222
|
-
const snapshotId = crypto6.randomUUID();
|
|
7223
|
-
let screenshotRelPath = null;
|
|
7224
|
-
if (raw.screenshotPath && fs3.existsSync(raw.screenshotPath)) {
|
|
7225
|
-
const screenshotDir = path3.join(os3.homedir(), ".canonry", "screenshots", runId);
|
|
7226
|
-
if (!fs3.existsSync(screenshotDir)) fs3.mkdirSync(screenshotDir, { recursive: true });
|
|
7227
|
-
const destPath = path3.join(screenshotDir, `${snapshotId}.png`);
|
|
7228
|
-
fs3.renameSync(raw.screenshotPath, destPath);
|
|
7229
|
-
screenshotRelPath = `${runId}/${snapshotId}.png`;
|
|
7230
|
-
}
|
|
7231
|
-
this.db.insert(querySnapshots).values({
|
|
7232
|
-
id: snapshotId,
|
|
7233
|
-
runId,
|
|
7234
|
-
queryId: unit.queryId,
|
|
7235
|
-
queryText: unit.queryText,
|
|
7236
|
-
provider: providerName,
|
|
7237
|
-
// `model` is what was REQUESTED and `served_model` is what
|
|
7238
|
-
// answered. The manifest froze the request, so it is the
|
|
7239
|
-
// authority here: an adapter that reports its own default rather
|
|
7240
|
-
// than what it was handed would otherwise overwrite the identity
|
|
7241
|
-
// the revision recorded, and nothing would show that it moved.
|
|
7242
|
-
model: unit.requestedModel ?? raw.model,
|
|
7243
|
-
servedModel: raw.servedModel ?? null,
|
|
7244
|
-
citationState,
|
|
7245
|
-
answerMentioned,
|
|
7246
|
-
answerText: normalized.answerText,
|
|
7247
|
-
citedDomains: normalized.citedDomains,
|
|
7248
|
-
citedUrls: citedUrlCapture.citedUrls,
|
|
7249
|
-
captureStatus: citedUrlCapture.captureStatus,
|
|
7250
|
-
sourceCount: citedUrlCapture.sourceCount,
|
|
7251
|
-
resolvedCount: citedUrlCapture.resolvedCount,
|
|
7252
|
-
captureVersion: citedUrlCapture.captureVersion,
|
|
7253
|
-
retrievalStatus: normalized.retrievalStatus,
|
|
7254
|
-
retrievalContract: raw.retrievalContract,
|
|
7255
|
-
competitorOverlap: overlap,
|
|
7256
|
-
recommendedCompetitors: extractedCompetitors,
|
|
7257
|
-
// Only claim the geography the provider actually honoured. A
|
|
7258
|
-
// requested-but-unsupported context stores `location: null` —
|
|
7259
|
-
// "no claim" — rather than the label we asked for, mirroring
|
|
7260
|
-
// `supportedContext` itself: this field is never non-null when
|
|
7261
|
-
// that one is null.
|
|
7262
|
-
location: supportedContext ? requestedContext?.label ?? null : null,
|
|
7263
|
-
measurementExecutionId: unit.executionId,
|
|
7264
|
-
requestedContext,
|
|
7265
|
-
supportedContext,
|
|
7266
|
-
screenshotPath: screenshotRelPath,
|
|
7267
|
-
rawResponse: JSON.stringify({
|
|
7268
|
-
model: raw.model,
|
|
7269
|
-
servedModel: raw.servedModel ?? null,
|
|
7270
|
-
groundingSources: normalized.groundingSources,
|
|
7271
|
-
searchQueries: normalized.searchQueries,
|
|
7272
|
-
apiResponse: raw.rawResponse
|
|
7273
|
-
}),
|
|
7274
|
-
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
7275
|
-
}).run();
|
|
7276
|
-
totalSnapshotsInserted++;
|
|
7277
|
-
log2.info("query.citation", {
|
|
7278
|
-
runId,
|
|
7279
|
-
provider: providerName,
|
|
7280
|
-
query: unit.queryText,
|
|
7281
|
-
executionId: unit.executionId,
|
|
7282
|
-
location: requestedContext?.label ?? null,
|
|
7283
|
-
citationState,
|
|
7284
|
-
answerMentioned
|
|
7285
|
-
});
|
|
7286
|
-
});
|
|
7287
|
-
} catch (err) {
|
|
7288
|
-
if (err instanceof RunCancelledError) {
|
|
7289
|
-
throw err;
|
|
7290
|
-
}
|
|
7291
|
-
const msg = describeError(err);
|
|
7292
|
-
const stack = err instanceof Error ? err.stack : void 0;
|
|
7293
|
-
log2.error("query.failed", { runId, provider: providerName, query: unit.queryText, executionId: unit.executionId, error: msg, stack });
|
|
7294
|
-
if (!providerErrors.has(providerName)) {
|
|
7295
|
-
providerErrors.set(providerName, msg);
|
|
7296
|
-
}
|
|
7168
|
+
const slotContext = {
|
|
7169
|
+
runId,
|
|
7170
|
+
allDomains,
|
|
7171
|
+
competitorDomains,
|
|
7172
|
+
allBrandNames,
|
|
7173
|
+
executionGates,
|
|
7174
|
+
providerDispatchCounts,
|
|
7175
|
+
providerErrors,
|
|
7176
|
+
onInserted: () => {
|
|
7177
|
+
totalSnapshotsInserted++;
|
|
7297
7178
|
}
|
|
7298
7179
|
};
|
|
7180
|
+
const processNodeForProvider = (registeredProvider, unit) => this.executePlanSlot(slotContext, registeredProvider, unit);
|
|
7299
7181
|
if (existingRun.kind === RunKinds["answer-visibility"] && existingRun.trigger !== RunTriggers.probe && existingRun.measurementPlanVersionId === null) {
|
|
7300
7182
|
const definition = buildSimpleMeasurementDefinition({
|
|
7301
7183
|
capturedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -7442,7 +7324,7 @@ var JobRunner = class {
|
|
|
7442
7324
|
status: "failed",
|
|
7443
7325
|
finishedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
7444
7326
|
error: errorMessage
|
|
7445
|
-
}).where(eq2(runs.id, runId)).run();
|
|
7327
|
+
}).where(and2(eq2(runs.id, runId), inArray(runs.status, ["queued", "running"]))).run();
|
|
7446
7328
|
this.flushProviderUsage(providerDispatchCounts, providerReservations);
|
|
7447
7329
|
const abortReason2 = classifyRunAbortReason(errorMessage);
|
|
7448
7330
|
const phases = buildPhases({ startTime, providerCallStart, providerCallEnd });
|
|
@@ -7483,6 +7365,363 @@ var JobRunner = class {
|
|
|
7483
7365
|
}
|
|
7484
7366
|
}
|
|
7485
7367
|
}
|
|
7368
|
+
/**
|
|
7369
|
+
* Complete a partial plan run in place: record only the expected slots it
|
|
7370
|
+
* never answered, under the same run id, then finalize the run once.
|
|
7371
|
+
*
|
|
7372
|
+
* The parent run is never written from an error path and only ever leaves
|
|
7373
|
+
* `partial` through the completion compare-and-set, so a failed or
|
|
7374
|
+
* interrupted fill leaves the run as the sweep left it, plus any answers the
|
|
7375
|
+
* fill did record. Its timestamps, manifest and identity are never touched:
|
|
7376
|
+
* it stays the same sweep, with the same place in every history.
|
|
7377
|
+
*/
|
|
7378
|
+
async executeRunFill(fillId) {
|
|
7379
|
+
const claim = this.db.update(runFills).set({ status: "running", startedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(and2(eq2(runFills.id, fillId), eq2(runFills.status, "queued"))).run();
|
|
7380
|
+
if (claim.changes !== 1) {
|
|
7381
|
+
log2.warn("fill.not-claimed", { fillId });
|
|
7382
|
+
return;
|
|
7383
|
+
}
|
|
7384
|
+
try {
|
|
7385
|
+
await this.runClaimedFill(fillId);
|
|
7386
|
+
} catch (err) {
|
|
7387
|
+
const error = describeError(err);
|
|
7388
|
+
log2.error("fill.aborted", { fillId, error });
|
|
7389
|
+
this.db.update(runFills).set({ status: "failed", finishedAt: (/* @__PURE__ */ new Date()).toISOString(), error }).where(and2(eq2(runFills.id, fillId), eq2(runFills.status, "running"))).run();
|
|
7390
|
+
}
|
|
7391
|
+
}
|
|
7392
|
+
async runClaimedFill(fillId) {
|
|
7393
|
+
const fill = this.db.select().from(runFills).where(eq2(runFills.id, fillId)).get();
|
|
7394
|
+
if (!fill) return;
|
|
7395
|
+
const { runId, projectId } = fill;
|
|
7396
|
+
const requested = new Set(parseJsonColumn(fill.providers, []).map((provider) => provider.trim().toLocaleLowerCase("en")));
|
|
7397
|
+
const providerDispatchCounts = /* @__PURE__ */ new Map();
|
|
7398
|
+
const providerReservations = /* @__PURE__ */ new Map();
|
|
7399
|
+
const providerErrors = /* @__PURE__ */ new Map();
|
|
7400
|
+
let filled = 0;
|
|
7401
|
+
let fatal = null;
|
|
7402
|
+
let superseded = false;
|
|
7403
|
+
try {
|
|
7404
|
+
const run = this.getRunState(runId);
|
|
7405
|
+
if (!run) throw new Error(`Run ${runId} not found`);
|
|
7406
|
+
if (run.status !== "partial") throw new Error(`Run ${runId} is ${run.status}; only a partial run can be filled`);
|
|
7407
|
+
const runCreatedAt = this.db.select({ createdAt: runs.createdAt }).from(runs).where(eq2(runs.id, runId)).get().createdAt;
|
|
7408
|
+
const project = this.db.select().from(projects).where(eq2(projects.id, projectId)).get();
|
|
7409
|
+
if (!project) throw new Error(`Project ${projectId} not found`);
|
|
7410
|
+
const projectQueries = this.db.select().from(queries).where(eq2(queries.projectId, projectId)).all();
|
|
7411
|
+
const plan = resolvePlanExecution(run, projectQueries);
|
|
7412
|
+
if (!plan) throw new Error(`Run ${runId} did not measure a published plan`);
|
|
7413
|
+
const recorded = new Set(
|
|
7414
|
+
this.db.select({ executionId: querySnapshots.measurementExecutionId, provider: querySnapshots.provider }).from(querySnapshots).where(eq2(querySnapshots.runId, runId)).all().flatMap((row) => row.executionId ? [measurementSlotKey(row.executionId, row.provider)] : [])
|
|
7415
|
+
);
|
|
7416
|
+
const unitsByProvider = /* @__PURE__ */ new Map();
|
|
7417
|
+
for (const [provider, units] of plan.unitsByProvider) {
|
|
7418
|
+
if (requested.size > 0 && !requested.has(provider)) continue;
|
|
7419
|
+
const missing = units.filter((unit) => !recorded.has(measurementSlotKey(unit.executionId, provider)));
|
|
7420
|
+
if (missing.length > 0) unitsByProvider.set(provider, missing);
|
|
7421
|
+
}
|
|
7422
|
+
const activeProviders = [];
|
|
7423
|
+
for (const provider of unitsByProvider.keys()) {
|
|
7424
|
+
const registered = this.registry.get(provider);
|
|
7425
|
+
if (registered) activeProviders.push(registered);
|
|
7426
|
+
else providerErrors.set(provider, `No ${provider} provider is configured on this instance, so its missing answers did not run.`);
|
|
7427
|
+
}
|
|
7428
|
+
const projectCompetitors = this.db.select().from(competitors).where(eq2(competitors.projectId, projectId)).all();
|
|
7429
|
+
const competitorDomains = projectCompetitors.map((c) => c.domain);
|
|
7430
|
+
const allDomains = effectiveDomains({ canonicalDomain: project.canonicalDomain, ownedDomains: project.ownedDomains });
|
|
7431
|
+
const allBrandNames = effectiveBrandNames({ displayName: project.displayName, aliases: project.aliases });
|
|
7432
|
+
const todayPeriod = getCurrentUsageDay();
|
|
7433
|
+
const dispatchable = [];
|
|
7434
|
+
for (const registered of activeProviders) {
|
|
7435
|
+
const name = registered.adapter.name;
|
|
7436
|
+
const count = unitsByProvider.get(name)?.length ?? 0;
|
|
7437
|
+
const scope = `${projectId}:${name}`;
|
|
7438
|
+
const limit = registered.config.quotaPolicy.maxRequestsPerDay;
|
|
7439
|
+
const quota = reserveDailyQueryQuota(this.db, { scope, period: todayPeriod, count, limit });
|
|
7440
|
+
if (!quota.reserved) {
|
|
7441
|
+
providerErrors.set(name, `Daily quota exceeded for ${name}: ${quota.used} queries used today, limit is ${limit}. This fill needs ${count} more.`);
|
|
7442
|
+
continue;
|
|
7443
|
+
}
|
|
7444
|
+
providerReservations.set(name, { scope, period: todayPeriod, reserved: count });
|
|
7445
|
+
dispatchable.push(registered);
|
|
7446
|
+
}
|
|
7447
|
+
const executionGates = /* @__PURE__ */ new Map();
|
|
7448
|
+
for (const registered of dispatchable) {
|
|
7449
|
+
executionGates.set(
|
|
7450
|
+
registered.adapter.name,
|
|
7451
|
+
getSharedProviderExecutionGate(
|
|
7452
|
+
registered.adapter.name,
|
|
7453
|
+
registered.config.quotaPolicy.maxConcurrency,
|
|
7454
|
+
registered.config.quotaPolicy.maxRequestsPerMinute
|
|
7455
|
+
)
|
|
7456
|
+
);
|
|
7457
|
+
}
|
|
7458
|
+
const consecutiveFailures = /* @__PURE__ */ new Map();
|
|
7459
|
+
const stopped = /* @__PURE__ */ new Set();
|
|
7460
|
+
const newerSweepExists = () => {
|
|
7461
|
+
if (!superseded && newerFullSweep(this.db, { id: runId, projectId, createdAt: runCreatedAt })) {
|
|
7462
|
+
superseded = true;
|
|
7463
|
+
log2.warn("fill.superseded", { fillId, runId });
|
|
7464
|
+
}
|
|
7465
|
+
return superseded;
|
|
7466
|
+
};
|
|
7467
|
+
let removed = false;
|
|
7468
|
+
const attemptRemoved = () => {
|
|
7469
|
+
if (!removed && this.db.select({ status: runFills.status }).from(runFills).where(eq2(runFills.id, fillId)).get()?.status !== "running") {
|
|
7470
|
+
removed = true;
|
|
7471
|
+
log2.warn("fill.attempt-removed", { fillId, runId });
|
|
7472
|
+
}
|
|
7473
|
+
return removed;
|
|
7474
|
+
};
|
|
7475
|
+
const slotRecorded = (provider, executionId) => this.db.select({ id: querySnapshots.id }).from(querySnapshots).where(and2(
|
|
7476
|
+
eq2(querySnapshots.runId, runId),
|
|
7477
|
+
eq2(querySnapshots.measurementExecutionId, executionId),
|
|
7478
|
+
eq2(querySnapshots.provider, provider)
|
|
7479
|
+
)).get() !== void 0;
|
|
7480
|
+
const ctx = {
|
|
7481
|
+
runId,
|
|
7482
|
+
allDomains,
|
|
7483
|
+
competitorDomains,
|
|
7484
|
+
allBrandNames,
|
|
7485
|
+
executionGates,
|
|
7486
|
+
providerDispatchCounts,
|
|
7487
|
+
providerErrors,
|
|
7488
|
+
onInserted: () => {
|
|
7489
|
+
filled++;
|
|
7490
|
+
},
|
|
7491
|
+
fill: {
|
|
7492
|
+
shouldSkip: (provider, executionId) => stopped.has(provider) || attemptRemoved() || newerSweepExists() || slotRecorded(provider, executionId),
|
|
7493
|
+
onOutcome: (provider, ok) => {
|
|
7494
|
+
if (ok) {
|
|
7495
|
+
consecutiveFailures.set(provider, 0);
|
|
7496
|
+
return;
|
|
7497
|
+
}
|
|
7498
|
+
const failures = (consecutiveFailures.get(provider) ?? 0) + 1;
|
|
7499
|
+
consecutiveFailures.set(provider, failures);
|
|
7500
|
+
if (failures >= RUN_FILL_PROVIDER_BREAKER && !stopped.has(provider)) {
|
|
7501
|
+
stopped.add(provider);
|
|
7502
|
+
log2.warn("fill.provider-stopped", { fillId, runId, provider, failures });
|
|
7503
|
+
}
|
|
7504
|
+
}
|
|
7505
|
+
}
|
|
7506
|
+
};
|
|
7507
|
+
log2.info("fill.dispatch", {
|
|
7508
|
+
fillId,
|
|
7509
|
+
runId,
|
|
7510
|
+
missing: Object.fromEntries([...unitsByProvider].map(([provider, units]) => [provider, units.length]))
|
|
7511
|
+
});
|
|
7512
|
+
const unitsFor = (registered) => unitsByProvider.get(registered.adapter.name) ?? [];
|
|
7513
|
+
const apiProviders = dispatchable.filter((p) => !isBrowserProvider(p.adapter.name));
|
|
7514
|
+
const browserProviders = dispatchable.filter((p) => isBrowserProvider(p.adapter.name));
|
|
7515
|
+
await runWithConcurrency(apiProviders, resolveProviderFanout(), async (registered) => {
|
|
7516
|
+
await runWithConcurrency(
|
|
7517
|
+
unitsFor(registered),
|
|
7518
|
+
Math.max(1, registered.config.quotaPolicy.maxConcurrency),
|
|
7519
|
+
(unit) => this.executePlanSlot(ctx, registered, unit)
|
|
7520
|
+
);
|
|
7521
|
+
});
|
|
7522
|
+
for (const registered of browserProviders) {
|
|
7523
|
+
for (const unit of unitsFor(registered)) await this.executePlanSlot(ctx, registered, unit);
|
|
7524
|
+
}
|
|
7525
|
+
} catch (err) {
|
|
7526
|
+
fatal = describeError(err);
|
|
7527
|
+
log2.error("fill.failed", { fillId, runId, error: fatal });
|
|
7528
|
+
} finally {
|
|
7529
|
+
this.flushProviderUsage(providerDispatchCounts, providerReservations);
|
|
7530
|
+
}
|
|
7531
|
+
const outcome = this.finalizeRunFill({ fillId, runId, filled, fatal, providerErrors, superseded });
|
|
7532
|
+
log2.info("fill.finished", { fillId, runId, filled, ...outcome });
|
|
7533
|
+
if (outcome.justCompleted && !outcome.superseded && this.onRunCompleted) {
|
|
7534
|
+
this.onRunCompleted(runId, projectId, { origin: "fill" }).catch((err) => {
|
|
7535
|
+
log2.error("notification.callback-failed", { runId, error: describeError(err) });
|
|
7536
|
+
});
|
|
7537
|
+
}
|
|
7538
|
+
}
|
|
7539
|
+
/** Record the fill's outcome and, if the run is now whole, complete it exactly once. */
|
|
7540
|
+
finalizeRunFill(input) {
|
|
7541
|
+
const finishedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
7542
|
+
return this.db.transaction((tx) => {
|
|
7543
|
+
const txDb = tx;
|
|
7544
|
+
const parent = txDb.select({ projectId: runs.projectId, createdAt: runs.createdAt, error: runs.error }).from(runs).where(eq2(runs.id, input.runId)).get();
|
|
7545
|
+
const superseded = input.superseded || parent !== void 0 && newerFullSweep(txDb, { id: input.runId, projectId: parent.projectId, createdAt: parent.createdAt }) !== void 0;
|
|
7546
|
+
const state = measurementRunSlotState(txDb, input.runId);
|
|
7547
|
+
const complete3 = state.planned && state.readable && state.missing.length === 0 && !state.hasUnboundSnapshot;
|
|
7548
|
+
let justCompleted = false;
|
|
7549
|
+
if (complete3) {
|
|
7550
|
+
justCompleted = txDb.update(runs).set({ status: "completed", error: null }).where(and2(eq2(runs.id, input.runId), eq2(runs.status, "partial"))).run().changes === 1;
|
|
7551
|
+
} else if (state.readable && !input.fatal) {
|
|
7552
|
+
const previous = parseRunError(parent?.error)?.providers ?? {};
|
|
7553
|
+
const fresh = buildRunErrorFromMessages(input.providerErrors).providers ?? {};
|
|
7554
|
+
const remaining = /* @__PURE__ */ new Map();
|
|
7555
|
+
for (const slot of state.missing) remaining.set(slot.provider, (remaining.get(slot.provider) ?? 0) + 1);
|
|
7556
|
+
const providers = {};
|
|
7557
|
+
for (const [provider, count] of remaining) {
|
|
7558
|
+
providers[provider] = fresh[provider] ?? previous[provider] ?? { message: `${count} expected measurement(s) have not run.` };
|
|
7559
|
+
}
|
|
7560
|
+
txDb.update(runs).set({ error: serializeRunError({ providers }) }).where(and2(eq2(runs.id, input.runId), eq2(runs.status, "partial"))).run();
|
|
7561
|
+
}
|
|
7562
|
+
const status = complete3 ? "completed" : input.filled > 0 ? "partial" : "failed";
|
|
7563
|
+
const reason = input.fatal ?? (superseded ? "Stopped because a newer sweep started; no answers were added behind it." : null) ?? (input.providerErrors.size > 0 ? formatRunErrorOneLine(buildRunErrorFromMessages(input.providerErrors)) : null);
|
|
7564
|
+
txDb.update(runFills).set({ status, filled: input.filled, error: complete3 ? null : reason, finishedAt }).where(eq2(runFills.id, input.fillId)).run();
|
|
7565
|
+
return { justCompleted, superseded };
|
|
7566
|
+
});
|
|
7567
|
+
}
|
|
7568
|
+
/**
|
|
7569
|
+
* The plan-aware unit of work: one execution node, one provider.
|
|
7570
|
+
*
|
|
7571
|
+
* Deliberately a separate worker from the legacy query-by-query path rather
|
|
7572
|
+
* than a generalization of it. The legacy path is the behaviour every
|
|
7573
|
+
* planless project already depends on; leaving its body alone is what
|
|
7574
|
+
* makes "planless is byte-identical" a fact rather than a hope.
|
|
7575
|
+
*
|
|
7576
|
+
* A method rather than a closure inside `executeRun` so a sweep and a fill
|
|
7577
|
+
* record a slot through the same code: a filled answer is indistinguishable
|
|
7578
|
+
* from one the sweep recorded itself, which is what lets it count.
|
|
7579
|
+
*/
|
|
7580
|
+
async executePlanSlot(ctx, registeredProvider, unit) {
|
|
7581
|
+
const { adapter, config: providerConfig } = registeredProvider;
|
|
7582
|
+
const providerName = adapter.name;
|
|
7583
|
+
const gate = ctx.executionGates.get(providerName);
|
|
7584
|
+
if (!gate) {
|
|
7585
|
+
throw new Error(`Missing execution gate for provider ${providerName}`);
|
|
7586
|
+
}
|
|
7587
|
+
const config = unit.requestedModel ? { ...providerConfig, model: unit.requestedModel } : providerConfig;
|
|
7588
|
+
const requestedContext = unit.context;
|
|
7589
|
+
const supportedContext = requestedContext && providerSupportsLocationContext(adapter) ? { status: "applied", resolved: requestedContext } : null;
|
|
7590
|
+
if (ctx.fill?.shouldSkip(providerName, unit.executionId)) return;
|
|
7591
|
+
try {
|
|
7592
|
+
await gate.run(async () => {
|
|
7593
|
+
this.throwIfRunCancelled(ctx.runId);
|
|
7594
|
+
if (ctx.fill?.shouldSkip(providerName, unit.executionId)) return;
|
|
7595
|
+
ctx.providerDispatchCounts.set(providerName, (ctx.providerDispatchCounts.get(providerName) ?? 0) + 1);
|
|
7596
|
+
const raw = await adapter.executeTrackedQuery(
|
|
7597
|
+
{
|
|
7598
|
+
query: unit.queryText,
|
|
7599
|
+
canonicalDomains: ctx.allDomains,
|
|
7600
|
+
competitorDomains: ctx.competitorDomains,
|
|
7601
|
+
location: requestedContext ?? void 0
|
|
7602
|
+
},
|
|
7603
|
+
config
|
|
7604
|
+
);
|
|
7605
|
+
this.throwIfRunCancelled(ctx.runId);
|
|
7606
|
+
const providerResult = adapter.normalizeResult(raw);
|
|
7607
|
+
const rawGroundingSources = providerResult.groundingSources;
|
|
7608
|
+
const normalized = {
|
|
7609
|
+
...providerResult,
|
|
7610
|
+
groundingSources: Array.isArray(rawGroundingSources) ? rawGroundingSources : []
|
|
7611
|
+
};
|
|
7612
|
+
let citedUrlCapture;
|
|
7613
|
+
try {
|
|
7614
|
+
citedUrlCapture = await captureCitedUrls(providerName, rawGroundingSources);
|
|
7615
|
+
} catch (err) {
|
|
7616
|
+
citedUrlCapture = {
|
|
7617
|
+
citedUrls: [],
|
|
7618
|
+
captureStatus: "failed",
|
|
7619
|
+
sourceCount: normalized.groundingSources.length,
|
|
7620
|
+
resolvedCount: 0,
|
|
7621
|
+
captureVersion: CITED_URL_CAPTURE_VERSION
|
|
7622
|
+
};
|
|
7623
|
+
log2.warn("query.cited-url-capture-failed", {
|
|
7624
|
+
runId: ctx.runId,
|
|
7625
|
+
provider: providerName,
|
|
7626
|
+
query: unit.queryText,
|
|
7627
|
+
error: describeError(err)
|
|
7628
|
+
});
|
|
7629
|
+
}
|
|
7630
|
+
this.throwIfRunCancelled(ctx.runId);
|
|
7631
|
+
const citationState = determineCitationState(normalized, ctx.allDomains);
|
|
7632
|
+
const answerMentioned = determineAnswerMentioned(
|
|
7633
|
+
normalized.answerText,
|
|
7634
|
+
ctx.allBrandNames,
|
|
7635
|
+
ctx.allDomains
|
|
7636
|
+
);
|
|
7637
|
+
const overlap = computeCompetitorOverlap(normalized, ctx.competitorDomains);
|
|
7638
|
+
const extractedCompetitors = extractRecommendedCompetitors(
|
|
7639
|
+
normalized.answerText,
|
|
7640
|
+
ctx.allDomains,
|
|
7641
|
+
normalized.citedDomains,
|
|
7642
|
+
ctx.competitorDomains,
|
|
7643
|
+
ctx.allBrandNames
|
|
7644
|
+
);
|
|
7645
|
+
const snapshotId = crypto6.randomUUID();
|
|
7646
|
+
let screenshotRelPath = null;
|
|
7647
|
+
if (raw.screenshotPath && fs3.existsSync(raw.screenshotPath)) {
|
|
7648
|
+
const screenshotDir = path3.join(os3.homedir(), ".canonry", "screenshots", ctx.runId);
|
|
7649
|
+
if (!fs3.existsSync(screenshotDir)) fs3.mkdirSync(screenshotDir, { recursive: true });
|
|
7650
|
+
const destPath = path3.join(screenshotDir, `${snapshotId}.png`);
|
|
7651
|
+
fs3.renameSync(raw.screenshotPath, destPath);
|
|
7652
|
+
screenshotRelPath = `${ctx.runId}/${snapshotId}.png`;
|
|
7653
|
+
}
|
|
7654
|
+
const insert = this.db.insert(querySnapshots).values({
|
|
7655
|
+
id: snapshotId,
|
|
7656
|
+
runId: ctx.runId,
|
|
7657
|
+
queryId: unit.queryId,
|
|
7658
|
+
queryText: unit.queryText,
|
|
7659
|
+
provider: providerName,
|
|
7660
|
+
// `model` is what was REQUESTED and `served_model` is what
|
|
7661
|
+
// answered. The manifest froze the request, so it is the
|
|
7662
|
+
// authority here: an adapter that reports its own default rather
|
|
7663
|
+
// than what it was handed would otherwise overwrite the identity
|
|
7664
|
+
// the revision recorded, and nothing would show that it moved.
|
|
7665
|
+
model: unit.requestedModel ?? raw.model,
|
|
7666
|
+
servedModel: raw.servedModel ?? null,
|
|
7667
|
+
citationState,
|
|
7668
|
+
answerMentioned,
|
|
7669
|
+
answerText: normalized.answerText,
|
|
7670
|
+
citedDomains: normalized.citedDomains,
|
|
7671
|
+
citedUrls: citedUrlCapture.citedUrls,
|
|
7672
|
+
captureStatus: citedUrlCapture.captureStatus,
|
|
7673
|
+
sourceCount: citedUrlCapture.sourceCount,
|
|
7674
|
+
resolvedCount: citedUrlCapture.resolvedCount,
|
|
7675
|
+
captureVersion: citedUrlCapture.captureVersion,
|
|
7676
|
+
retrievalStatus: normalized.retrievalStatus,
|
|
7677
|
+
retrievalContract: raw.retrievalContract,
|
|
7678
|
+
competitorOverlap: overlap,
|
|
7679
|
+
recommendedCompetitors: extractedCompetitors,
|
|
7680
|
+
// Only claim the geography the provider actually honoured. A
|
|
7681
|
+
// requested-but-unsupported context stores `location: null` —
|
|
7682
|
+
// "no claim" — rather than the label we asked for, mirroring
|
|
7683
|
+
// `supportedContext` itself: this field is never non-null when
|
|
7684
|
+
// that one is null.
|
|
7685
|
+
location: supportedContext ? requestedContext?.label ?? null : null,
|
|
7686
|
+
measurementExecutionId: unit.executionId,
|
|
7687
|
+
requestedContext,
|
|
7688
|
+
supportedContext,
|
|
7689
|
+
screenshotPath: screenshotRelPath,
|
|
7690
|
+
rawResponse: JSON.stringify({
|
|
7691
|
+
model: raw.model,
|
|
7692
|
+
servedModel: raw.servedModel ?? null,
|
|
7693
|
+
groundingSources: normalized.groundingSources,
|
|
7694
|
+
searchQueries: normalized.searchQueries,
|
|
7695
|
+
apiResponse: raw.rawResponse
|
|
7696
|
+
}),
|
|
7697
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
7698
|
+
});
|
|
7699
|
+
const written = ctx.fill ? insert.onConflictDoNothing().run() : insert.run();
|
|
7700
|
+
if (written.changes > 0) ctx.onInserted();
|
|
7701
|
+
ctx.fill?.onOutcome(providerName, true);
|
|
7702
|
+
log2.info("query.citation", {
|
|
7703
|
+
runId: ctx.runId,
|
|
7704
|
+
provider: providerName,
|
|
7705
|
+
query: unit.queryText,
|
|
7706
|
+
executionId: unit.executionId,
|
|
7707
|
+
location: requestedContext?.label ?? null,
|
|
7708
|
+
citationState,
|
|
7709
|
+
answerMentioned
|
|
7710
|
+
});
|
|
7711
|
+
});
|
|
7712
|
+
} catch (err) {
|
|
7713
|
+
if (err instanceof RunCancelledError) {
|
|
7714
|
+
throw err;
|
|
7715
|
+
}
|
|
7716
|
+
const msg = describeError(err);
|
|
7717
|
+
const stack = err instanceof Error ? err.stack : void 0;
|
|
7718
|
+
log2.error("query.failed", { runId: ctx.runId, provider: providerName, query: unit.queryText, executionId: unit.executionId, error: msg, stack });
|
|
7719
|
+
if (!ctx.providerErrors.has(providerName)) {
|
|
7720
|
+
ctx.providerErrors.set(providerName, msg);
|
|
7721
|
+
}
|
|
7722
|
+
ctx.fill?.onOutcome(providerName, false);
|
|
7723
|
+
}
|
|
7724
|
+
}
|
|
7486
7725
|
incrementUsage(scope, metric, count) {
|
|
7487
7726
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
7488
7727
|
const period = now.slice(0, 10);
|
|
@@ -11777,7 +12016,7 @@ function readStoredGroundingSources(rawResponse) {
|
|
|
11777
12016
|
return result2;
|
|
11778
12017
|
}
|
|
11779
12018
|
async function backfillInsightsCommand(project, opts) {
|
|
11780
|
-
const { IntelligenceService: IntelligenceService2 } = await import("./intelligence-service-
|
|
12019
|
+
const { IntelligenceService: IntelligenceService2 } = await import("./intelligence-service-PM7MC5VC.js");
|
|
11781
12020
|
const config = loadConfig();
|
|
11782
12021
|
const db = createClient(config.database);
|
|
11783
12022
|
migrate(db);
|
|
@@ -13024,8 +13263,8 @@ var Notifier = class {
|
|
|
13024
13263
|
this.serverUrl = serverUrl;
|
|
13025
13264
|
}
|
|
13026
13265
|
/** Called after a run completes (success, partial, or failed). */
|
|
13027
|
-
async onRunCompleted(runId, projectId) {
|
|
13028
|
-
log17.info("run.completed", { runId, projectId });
|
|
13266
|
+
async onRunCompleted(runId, projectId, opts) {
|
|
13267
|
+
log17.info("run.completed", { runId, projectId, origin: opts?.origin ?? "sweep" });
|
|
13029
13268
|
const notifs = this.db.select().from(notifications).where(eq17(notifications.projectId, projectId)).all().filter((n) => n.enabled);
|
|
13030
13269
|
if (notifs.length === 0) {
|
|
13031
13270
|
log17.info("notifications.none-enabled", { projectId });
|
|
@@ -13045,7 +13284,7 @@ var Notifier = class {
|
|
|
13045
13284
|
const transitions = this.computeTransitions(runId, projectId);
|
|
13046
13285
|
const events = [];
|
|
13047
13286
|
log17.info("run.status", { runId: run.id, status: run.status, projectId });
|
|
13048
|
-
if (run.status === "completed" || run.status === "partial") {
|
|
13287
|
+
if ((run.status === "completed" || run.status === "partial") && opts?.origin !== "fill") {
|
|
13049
13288
|
events.push("run.completed");
|
|
13050
13289
|
}
|
|
13051
13290
|
if (run.status === "failed") {
|
|
@@ -13496,7 +13735,7 @@ var RunCoordinator = class {
|
|
|
13496
13735
|
intelligenceService;
|
|
13497
13736
|
onInsightsGenerated;
|
|
13498
13737
|
onAeroEvent;
|
|
13499
|
-
async onRunCompleted(runId, projectId) {
|
|
13738
|
+
async onRunCompleted(runId, projectId, opts) {
|
|
13500
13739
|
const runRow = this.db.select().from(runs).where(eq18(runs.id, runId)).get();
|
|
13501
13740
|
const kind = runRow?.kind ?? RunKinds["answer-visibility"];
|
|
13502
13741
|
if (runRow?.trigger === RunTriggers.probe) {
|
|
@@ -13558,7 +13797,7 @@ var RunCoordinator = class {
|
|
|
13558
13797
|
}
|
|
13559
13798
|
}
|
|
13560
13799
|
try {
|
|
13561
|
-
await this.notifier.onRunCompleted(runId, projectId);
|
|
13800
|
+
await (opts ? this.notifier.onRunCompleted(runId, projectId, opts) : this.notifier.onRunCompleted(runId, projectId));
|
|
13562
13801
|
} catch (err) {
|
|
13563
13802
|
log18.error("notifier.failed", { runId, error: describeError(err) });
|
|
13564
13803
|
}
|
|
@@ -17990,7 +18229,7 @@ async function createServer(opts) {
|
|
|
17990
18229
|
void sessionRegistry.drainNow(project.name);
|
|
17991
18230
|
}
|
|
17992
18231
|
);
|
|
17993
|
-
jobRunner.onRunCompleted = (runId, projectId) => runCoordinator.onRunCompleted(runId, projectId);
|
|
18232
|
+
jobRunner.onRunCompleted = (runId, projectId, opts2) => opts2 ? runCoordinator.onRunCompleted(runId, projectId, opts2) : runCoordinator.onRunCompleted(runId, projectId);
|
|
17994
18233
|
const snapshotService = new SnapshotService(registry);
|
|
17995
18234
|
const liveProjectIds = new Set(
|
|
17996
18235
|
opts.db.select({ id: projects.id }).from(projects).all().map((project) => project.id)
|
|
@@ -19347,6 +19586,14 @@ async function createServer(opts) {
|
|
|
19347
19586
|
app.log.error({ runId, err }, "Job runner failed");
|
|
19348
19587
|
});
|
|
19349
19588
|
},
|
|
19589
|
+
getProviderDailyLimits: () => Object.fromEntries(
|
|
19590
|
+
registry.getAll().map((provider) => [provider.adapter.name, provider.config.quotaPolicy.maxRequestsPerDay])
|
|
19591
|
+
),
|
|
19592
|
+
onRunFillCreated: (fillId, runId) => {
|
|
19593
|
+
jobRunner.executeRunFill(fillId).catch((err) => {
|
|
19594
|
+
app.log.error({ fillId, runId, err: describeError(err) }, "Run fill failed");
|
|
19595
|
+
});
|
|
19596
|
+
},
|
|
19350
19597
|
onRunCancelled: (runId) => {
|
|
19351
19598
|
const controller = siteAuditAbortControllers.get(runId);
|
|
19352
19599
|
if (controller && !controller.signal.aborted) {
|