@canonry/canonry 7.19.0 → 7.20.1
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/aero/references/agent-operations.md +14 -0
- package/assets/agent-workspace/skills/canonry/SKILL.md +14 -0
- package/assets/agent-workspace/skills/canonry/references/canonry-cli.md +33 -1
- package/assets/assets/{AuditHistoryPanel-DEEWm-4t.js → AuditHistoryPanel-DVtz1ZuT.js} +1 -1
- package/assets/assets/{BacklinksPage-F5QBgXbF.js → BacklinksPage-Tvt0-ppX.js} +1 -1
- package/assets/assets/{HistoryPage-CNp42sUO.js → HistoryPage-DqXwPH3k.js} +1 -1
- package/assets/assets/{MeasurementPropertyPage-XpI9hVMb.js → MeasurementPropertyPage-CsbfFEjB.js} +1 -1
- package/assets/assets/{ProjectPage-B_V4QpyI.js → ProjectPage-Cyvt96AV.js} +2 -2
- package/assets/assets/{RunsPage-PTHloZl3.js → RunsPage-Io7ZFw4-.js} +1 -1
- package/assets/assets/{SettingsPage-C-aDo2zf.js → SettingsPage-BPYxgchP.js} +1 -1
- package/assets/assets/{SiteHealthSection-DvoT8IuX.js → SiteHealthSection-CwffU3tv.js} +3 -3
- package/assets/assets/{TrafficPage-DahJoMKF.js → TrafficPage-DIm60m1J.js} +1 -1
- package/assets/assets/{TrafficSourceDetailPage-BGlHXOlk.js → TrafficSourceDetailPage-DWT1Ip5P.js} +1 -1
- package/assets/assets/{extract-error-message-CP_fyJLM.js → extract-error-message-BVYchXhT.js} +1 -1
- package/assets/assets/{index-C5V8xlr0.js → index-DrQLR1jE.js} +20 -20
- package/assets/assets/{react-sigma_core.esm.min-DUS8srfg.js → react-sigma_core.esm.min-f31sqfAC.js} +1 -1
- package/assets/assets/{v2-overview-adapter-ycJ1guZb.js → v2-overview-adapter-By0930UY.js} +1 -1
- package/assets/index.html +1 -1
- package/dist/{chunk-A75L7CUV.js → chunk-222NAFTY.js} +2 -2
- package/dist/{chunk-UUI4HOEL.js → chunk-46JZR2IV.js} +46 -8
- package/dist/{chunk-VSTKTQPS.js → chunk-5IXQAOVK.js} +16 -1
- package/dist/{chunk-PBTTPZZE.js → chunk-6H2BC5EY.js} +3 -3
- package/dist/{chunk-OMP5OLBK.js → chunk-CIPWGVX6.js} +368 -211
- package/dist/{chunk-CYHUZ2JQ.js → chunk-MRKV43TV.js} +19 -0
- package/dist/{chunk-35IIFDIT.js → chunk-NDQOCFJQ.js} +61 -43
- package/dist/cli.js +213 -39
- package/dist/{demo-server-K5G54GOE.js → demo-server-32PU7BM6.js} +4 -4
- package/dist/index.d.ts +1 -1
- package/dist/index.js +6 -6
- package/dist/{intelligence-service-LV2TQMMR.js → intelligence-service-256DQBVR.js} +2 -2
- package/dist/mcp.js +4 -4
- package/package.json +12 -12
|
@@ -20,20 +20,20 @@ import {
|
|
|
20
20
|
saveConfig,
|
|
21
21
|
saveConfigPatch,
|
|
22
22
|
sentimentInstallReadiness
|
|
23
|
-
} from "./chunk-
|
|
23
|
+
} from "./chunk-NDQOCFJQ.js";
|
|
24
24
|
import {
|
|
25
25
|
computeFactorAverages,
|
|
26
26
|
deleteSiteCrawlGraphLayout,
|
|
27
27
|
persistSiteCrawlGraphLayout,
|
|
28
28
|
prepareSiteCrawlGraphLayout,
|
|
29
29
|
recordedSharePct
|
|
30
|
-
} from "./chunk-
|
|
30
|
+
} from "./chunk-222NAFTY.js";
|
|
31
31
|
import {
|
|
32
32
|
CanonryMcpToolNames,
|
|
33
33
|
CliError,
|
|
34
34
|
PACKAGE_VERSION,
|
|
35
35
|
canonryMcpTools
|
|
36
|
-
} from "./chunk-
|
|
36
|
+
} from "./chunk-5IXQAOVK.js";
|
|
37
37
|
import {
|
|
38
38
|
CC_CACHE_DIR,
|
|
39
39
|
DUCKDB_SPEC,
|
|
@@ -223,13 +223,14 @@ import {
|
|
|
223
223
|
toAlertView,
|
|
224
224
|
usageCounters,
|
|
225
225
|
writeAuditLog
|
|
226
|
-
} from "./chunk-
|
|
226
|
+
} from "./chunk-46JZR2IV.js";
|
|
227
227
|
import {
|
|
228
228
|
AGENT_MEMORY_VALUE_MAX_BYTES,
|
|
229
229
|
AGENT_NONE,
|
|
230
230
|
AGENT_PROVIDER_IDS,
|
|
231
231
|
AI_ENGINE_DOMAINS,
|
|
232
232
|
AI_ENGINE_SELF_DOMAINS,
|
|
233
|
+
API_PROVIDER_NAMES,
|
|
233
234
|
AdsAdGroupBillingEventTypes,
|
|
234
235
|
AdsCampaignBiddingTypes,
|
|
235
236
|
AgentProviderIds,
|
|
@@ -269,6 +270,7 @@ import {
|
|
|
269
270
|
ProviderBatchSubmitError,
|
|
270
271
|
ProviderDispatchModes,
|
|
271
272
|
ProviderNames,
|
|
273
|
+
ProviderReloadErrorReasons,
|
|
272
274
|
RATIO_UNIT_META_KEY,
|
|
273
275
|
ROUTE_CAPABLE_CITED_URL_PROVIDERS,
|
|
274
276
|
RUN_FILL_PROVIDER_BREAKER,
|
|
@@ -398,6 +400,7 @@ import {
|
|
|
398
400
|
notFound,
|
|
399
401
|
observeTemplateLinkDetection,
|
|
400
402
|
observeTemplateLinkEdges,
|
|
403
|
+
operationInProgress,
|
|
401
404
|
parseMeasurementRunManifestV1,
|
|
402
405
|
parseOriginList,
|
|
403
406
|
parseRunError,
|
|
@@ -439,7 +442,7 @@ import {
|
|
|
439
442
|
winnabilityClassLabel,
|
|
440
443
|
withRetry,
|
|
441
444
|
withSkippedProviders
|
|
442
|
-
} from "./chunk-
|
|
445
|
+
} from "./chunk-MRKV43TV.js";
|
|
443
446
|
|
|
444
447
|
// src/runtime-context.ts
|
|
445
448
|
function cliRuntimeContext(env = process.env, stdio = process) {
|
|
@@ -3569,7 +3572,7 @@ import path8 from "path";
|
|
|
3569
3572
|
import { isIP } from "net";
|
|
3570
3573
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
3571
3574
|
import { setImmediate as yieldToEventLoop4 } from "timers/promises";
|
|
3572
|
-
import { and as
|
|
3575
|
+
import { and as and24, eq as eq29, isNotNull as isNotNull4, isNull as isNull4, lte as lte2, ne as ne3, or as or3, sql as sql11 } from "drizzle-orm";
|
|
3573
3576
|
|
|
3574
3577
|
// ../integration-typesafe/src/classifier.ts
|
|
3575
3578
|
import { createHash } from "crypto";
|
|
@@ -8049,7 +8052,7 @@ function nonBlank(value) {
|
|
|
8049
8052
|
return trimmed ? trimmed : void 0;
|
|
8050
8053
|
}
|
|
8051
8054
|
function resolveBuildCommit(env = process.env) {
|
|
8052
|
-
const embedded = true ? "
|
|
8055
|
+
const embedded = true ? "29f2d7cd1ba516a39c9321b0399aaa3b5311ef46" : void 0;
|
|
8053
8056
|
return nonBlank(embedded) ?? nonBlank(env.CANONRY_COMMIT);
|
|
8054
8057
|
}
|
|
8055
8058
|
function resolveInstanceIdentity(env = process.env) {
|
|
@@ -8205,48 +8208,57 @@ var ProviderExecutionGate = class {
|
|
|
8205
8208
|
maxPerMinute;
|
|
8206
8209
|
window = [];
|
|
8207
8210
|
waiters = [];
|
|
8208
|
-
|
|
8211
|
+
rateTimer;
|
|
8212
|
+
dispatching = false;
|
|
8209
8213
|
inFlight = 0;
|
|
8214
|
+
updatePolicy(maxConcurrency, maxPerMinute) {
|
|
8215
|
+
this.maxConcurrency = maxConcurrency;
|
|
8216
|
+
this.maxPerMinute = maxPerMinute;
|
|
8217
|
+
this.dispatchQueued();
|
|
8218
|
+
}
|
|
8210
8219
|
async run(task) {
|
|
8211
|
-
|
|
8220
|
+
return new Promise((resolve2, reject) => {
|
|
8221
|
+
this.waiters.push(() => {
|
|
8222
|
+
void this.execute(task, resolve2, reject);
|
|
8223
|
+
});
|
|
8224
|
+
this.dispatchQueued();
|
|
8225
|
+
});
|
|
8226
|
+
}
|
|
8227
|
+
async execute(task, resolve2, reject) {
|
|
8212
8228
|
try {
|
|
8213
|
-
await
|
|
8214
|
-
|
|
8229
|
+
resolve2(await task());
|
|
8230
|
+
} catch (error) {
|
|
8231
|
+
reject(error);
|
|
8215
8232
|
} finally {
|
|
8216
|
-
this.
|
|
8233
|
+
this.inFlight--;
|
|
8234
|
+
this.dispatchQueued();
|
|
8217
8235
|
}
|
|
8218
8236
|
}
|
|
8219
|
-
|
|
8220
|
-
if (this.
|
|
8221
|
-
|
|
8222
|
-
|
|
8237
|
+
dispatchQueued() {
|
|
8238
|
+
if (this.dispatching) return;
|
|
8239
|
+
if (this.rateTimer !== void 0) {
|
|
8240
|
+
clearTimeout(this.rateTimer);
|
|
8241
|
+
this.rateTimer = void 0;
|
|
8223
8242
|
}
|
|
8224
|
-
|
|
8225
|
-
this.inFlight++;
|
|
8226
|
-
}
|
|
8227
|
-
release() {
|
|
8228
|
-
this.inFlight = Math.max(0, this.inFlight - 1);
|
|
8229
|
-
this.waiters.shift()?.();
|
|
8230
|
-
}
|
|
8231
|
-
async waitForRateLimit() {
|
|
8232
|
-
let releaseChain;
|
|
8233
|
-
const previousChain = this.rateLimitChain;
|
|
8234
|
-
this.rateLimitChain = new Promise((resolve2) => {
|
|
8235
|
-
releaseChain = resolve2;
|
|
8236
|
-
});
|
|
8237
|
-
await previousChain;
|
|
8243
|
+
this.dispatching = true;
|
|
8238
8244
|
try {
|
|
8239
|
-
|
|
8240
|
-
|
|
8241
|
-
|
|
8242
|
-
|
|
8243
|
-
|
|
8244
|
-
|
|
8245
|
-
|
|
8246
|
-
|
|
8247
|
-
|
|
8245
|
+
while (this.waiters.length > 0 && this.inFlight < Math.max(1, this.maxConcurrency)) {
|
|
8246
|
+
const now = Date.now();
|
|
8247
|
+
const windowStart = now - 6e4;
|
|
8248
|
+
while (this.window.length > 0 && this.window[0] < windowStart) this.window.shift();
|
|
8249
|
+
if (this.window.length >= this.maxPerMinute) break;
|
|
8250
|
+
this.window.push(now);
|
|
8251
|
+
this.inFlight++;
|
|
8252
|
+
this.waiters.shift()();
|
|
8253
|
+
}
|
|
8248
8254
|
} finally {
|
|
8249
|
-
|
|
8255
|
+
this.dispatching = false;
|
|
8256
|
+
}
|
|
8257
|
+
if (this.waiters.length > 0 && this.inFlight < Math.max(1, this.maxConcurrency)) {
|
|
8258
|
+
this.rateTimer = setTimeout(() => {
|
|
8259
|
+
this.rateTimer = void 0;
|
|
8260
|
+
this.dispatchQueued();
|
|
8261
|
+
}, this.window[0] + 6e4 - Date.now() + 50);
|
|
8250
8262
|
}
|
|
8251
8263
|
}
|
|
8252
8264
|
};
|
|
@@ -8259,6 +8271,9 @@ function getSharedProviderExecutionGate(providerName, maxConcurrency, maxPerMinu
|
|
|
8259
8271
|
sharedGates.set(key, gate);
|
|
8260
8272
|
return gate;
|
|
8261
8273
|
}
|
|
8274
|
+
function updateSharedProviderExecutionGate(providerName, maxConcurrency, maxPerMinute) {
|
|
8275
|
+
getSharedProviderExecutionGate(providerName, maxConcurrency, maxPerMinute).updatePolicy(maxConcurrency, maxPerMinute);
|
|
8276
|
+
}
|
|
8262
8277
|
|
|
8263
8278
|
// src/usage-quota.ts
|
|
8264
8279
|
import crypto6 from "crypto";
|
|
@@ -8576,6 +8591,7 @@ var JobRunner = class {
|
|
|
8576
8591
|
* handoff across a restart.
|
|
8577
8592
|
*/
|
|
8578
8593
|
executing = /* @__PURE__ */ new Map();
|
|
8594
|
+
executingBatchRegistrations = /* @__PURE__ */ new Map();
|
|
8579
8595
|
constructor(db, registry, opts) {
|
|
8580
8596
|
this.onFirstActivation = opts?.onFirstActivation;
|
|
8581
8597
|
this.db = db;
|
|
@@ -8596,6 +8612,12 @@ var JobRunner = class {
|
|
|
8596
8612
|
isExecuting(runId) {
|
|
8597
8613
|
return this.executing.has(runId);
|
|
8598
8614
|
}
|
|
8615
|
+
/** Reload must preserve credentials captured by a batch sweep until it settles. */
|
|
8616
|
+
getExecutingBatchRegistrations() {
|
|
8617
|
+
return [...this.executingBatchRegistrations.values()].flatMap(
|
|
8618
|
+
({ runId, registrations }) => registrations.map((registration) => ({ runId, registration }))
|
|
8619
|
+
);
|
|
8620
|
+
}
|
|
8599
8621
|
hasProviderBatches(runId) {
|
|
8600
8622
|
return this.db.select({ id: providerBatches.id }).from(providerBatches).where(eq3(providerBatches.runId, runId)).limit(1).get() !== void 0;
|
|
8601
8623
|
}
|
|
@@ -8711,6 +8733,7 @@ var JobRunner = class {
|
|
|
8711
8733
|
const providerReservations = /* @__PURE__ */ new Map();
|
|
8712
8734
|
const batchRowIds = [];
|
|
8713
8735
|
const releaseExecution = this.enterExecution(runId);
|
|
8736
|
+
const batchExecution = Symbol(runId);
|
|
8714
8737
|
try {
|
|
8715
8738
|
const existingRun = this.getRunState(runId);
|
|
8716
8739
|
if (!existingRun) {
|
|
@@ -8829,6 +8852,18 @@ var JobRunner = class {
|
|
|
8829
8852
|
let totalSnapshotsInserted = 0;
|
|
8830
8853
|
const apiProviders = calledProviders.filter((p) => !isBrowserProvider(p.adapter.name));
|
|
8831
8854
|
const browserProviders = calledProviders.filter((p) => isBrowserProvider(p.adapter.name));
|
|
8855
|
+
const providerKey = (provider) => provider.adapter.name.trim().toLocaleLowerCase("en");
|
|
8856
|
+
const batchProviders = /* @__PURE__ */ new Set();
|
|
8857
|
+
if (planExecution) {
|
|
8858
|
+
const frozenModes = existingRun.providerDispatchModes ?? {};
|
|
8859
|
+
for (const provider of apiProviders) {
|
|
8860
|
+
if (frozenModes[providerKey(provider)] !== ProviderDispatchModes.batch) continue;
|
|
8861
|
+
if (adapterSupportsBatch(provider.adapter)) batchProviders.add(provider.adapter.name);
|
|
8862
|
+
else log2.warn("run.batch-unsupported", { runId, provider: provider.adapter.name });
|
|
8863
|
+
}
|
|
8864
|
+
const registrations = apiProviders.filter((provider) => batchProviders.has(provider.adapter.name));
|
|
8865
|
+
if (registrations.length > 0) this.executingBatchRegistrations.set(batchExecution, { runId, registrations });
|
|
8866
|
+
}
|
|
8832
8867
|
const processQueryForProvider = async (registeredProvider, q) => {
|
|
8833
8868
|
const { adapter, config } = registeredProvider;
|
|
8834
8869
|
const providerName = adapter.name;
|
|
@@ -9040,15 +9075,7 @@ var JobRunner = class {
|
|
|
9040
9075
|
providerCallStart = Date.now();
|
|
9041
9076
|
if (planExecution) {
|
|
9042
9077
|
const plan = planExecution;
|
|
9043
|
-
const providerKey = (provider) => provider.adapter.name.trim().toLocaleLowerCase("en");
|
|
9044
9078
|
const unitsFor = (provider) => plan.unitsByProvider.get(providerKey(provider)) ?? [];
|
|
9045
|
-
const frozenModes = existingRun.providerDispatchModes ?? {};
|
|
9046
|
-
const batchProviders = /* @__PURE__ */ new Set();
|
|
9047
|
-
for (const provider of apiProviders) {
|
|
9048
|
-
if (frozenModes[providerKey(provider)] !== ProviderDispatchModes.batch) continue;
|
|
9049
|
-
if (adapterSupportsBatch(provider.adapter)) batchProviders.add(provider.adapter.name);
|
|
9050
|
-
else log2.warn("run.batch-unsupported", { runId, provider: provider.adapter.name });
|
|
9051
|
-
}
|
|
9052
9079
|
log2.info("run.plan-dispatch", {
|
|
9053
9080
|
runId,
|
|
9054
9081
|
expectedSlots: plan.manifest.expectedSlots.length,
|
|
@@ -9065,7 +9092,16 @@ var JobRunner = class {
|
|
|
9065
9092
|
batchRowIds
|
|
9066
9093
|
};
|
|
9067
9094
|
await runWithConcurrency(apiProviders, resolveProviderFanout(), async (registeredProvider) => {
|
|
9068
|
-
|
|
9095
|
+
let units;
|
|
9096
|
+
if (batchProviders.has(registeredProvider.adapter.name)) {
|
|
9097
|
+
const submitExecution = Symbol(runId);
|
|
9098
|
+
this.executingBatchRegistrations.set(submitExecution, { runId, registrations: [registeredProvider] });
|
|
9099
|
+
try {
|
|
9100
|
+
units = await this.submitPlanBatches(submission, registeredProvider, unitsFor(registeredProvider));
|
|
9101
|
+
} finally {
|
|
9102
|
+
this.executingBatchRegistrations.delete(submitExecution);
|
|
9103
|
+
}
|
|
9104
|
+
} else units = unitsFor(registeredProvider);
|
|
9069
9105
|
await Promise.all(units.map(async (unit) => {
|
|
9070
9106
|
await processNodeForProvider(registeredProvider, unit);
|
|
9071
9107
|
}));
|
|
@@ -9197,6 +9233,7 @@ var JobRunner = class {
|
|
|
9197
9233
|
}
|
|
9198
9234
|
await this.cancelAtProvider(abandoned);
|
|
9199
9235
|
} finally {
|
|
9236
|
+
this.executingBatchRegistrations.delete(batchExecution);
|
|
9200
9237
|
releaseExecution();
|
|
9201
9238
|
}
|
|
9202
9239
|
}
|
|
@@ -9647,9 +9684,9 @@ var JobRunner = class {
|
|
|
9647
9684
|
return marked;
|
|
9648
9685
|
}
|
|
9649
9686
|
async cancelAtProvider(rows) {
|
|
9650
|
-
|
|
9687
|
+
const registrations = rows.map((row) => ({ row, registered: this.registry.get(row.provider) }));
|
|
9688
|
+
for (const { row, registered } of registrations) {
|
|
9651
9689
|
if (!row.providerBatchId) continue;
|
|
9652
|
-
const registered = this.registry.get(row.provider);
|
|
9653
9690
|
if (!registered?.adapter.batch) {
|
|
9654
9691
|
log2.warn("batch.cancel-unavailable", { batchId: row.id, provider: row.provider });
|
|
9655
9692
|
continue;
|
|
@@ -14568,11 +14605,29 @@ function extractStoredAnswerAnchors(provider, rawResponse) {
|
|
|
14568
14605
|
}
|
|
14569
14606
|
|
|
14570
14607
|
// src/provider-registry.ts
|
|
14608
|
+
function resolveRegistration(adapter, config) {
|
|
14609
|
+
return {
|
|
14610
|
+
adapter,
|
|
14611
|
+
config: config.model === void 0 ? config : { ...config, model: resolveProviderModel(adapter.name, config.model) }
|
|
14612
|
+
};
|
|
14613
|
+
}
|
|
14571
14614
|
var ProviderRegistry = class {
|
|
14572
14615
|
providers = /* @__PURE__ */ new Map();
|
|
14573
14616
|
register(adapter, config) {
|
|
14574
|
-
const
|
|
14575
|
-
this.providers.set(adapter.name,
|
|
14617
|
+
const provider = resolveRegistration(adapter, config);
|
|
14618
|
+
this.providers.set(adapter.name, provider);
|
|
14619
|
+
updateSharedProviderExecutionGate(adapter.name, provider.config.quotaPolicy.maxConcurrency, provider.config.quotaPolicy.maxRequestsPerMinute);
|
|
14620
|
+
}
|
|
14621
|
+
/** Publish a complete registration set without mutating configs captured by active runs. */
|
|
14622
|
+
replace(providers) {
|
|
14623
|
+
const next = /* @__PURE__ */ new Map();
|
|
14624
|
+
for (const { adapter, config } of providers) {
|
|
14625
|
+
next.set(adapter.name, resolveRegistration(adapter, config));
|
|
14626
|
+
}
|
|
14627
|
+
this.providers = next;
|
|
14628
|
+
for (const { adapter, config } of next.values()) {
|
|
14629
|
+
updateSharedProviderExecutionGate(adapter.name, config.quotaPolicy.maxConcurrency, config.quotaPolicy.maxRequestsPerMinute);
|
|
14630
|
+
}
|
|
14576
14631
|
}
|
|
14577
14632
|
get(name) {
|
|
14578
14633
|
return this.providers.get(name);
|
|
@@ -14619,6 +14674,96 @@ var ProviderRegistry = class {
|
|
|
14619
14674
|
}
|
|
14620
14675
|
};
|
|
14621
14676
|
|
|
14677
|
+
// src/provider-registration.ts
|
|
14678
|
+
function isApiProviderRegistrable(name, entry) {
|
|
14679
|
+
if (!entry) return false;
|
|
14680
|
+
if (name === ProviderNames.local) return Boolean(entry.baseUrl);
|
|
14681
|
+
if (name === ProviderNames.gemini) return Boolean(entry.apiKey || entry.vertexProject);
|
|
14682
|
+
return Boolean(entry.apiKey);
|
|
14683
|
+
}
|
|
14684
|
+
function isCdpProviderRegistrable(cdp) {
|
|
14685
|
+
return Boolean(cdp?.host || cdp?.port);
|
|
14686
|
+
}
|
|
14687
|
+
function registeredProviderNames(config) {
|
|
14688
|
+
const names = API_PROVIDER_NAMES.filter((name) => {
|
|
14689
|
+
const entry = config.providers?.[name];
|
|
14690
|
+
if (name === ProviderNames.gemini && !entry && config.geminiApiKey) return true;
|
|
14691
|
+
return isApiProviderRegistrable(name, entry);
|
|
14692
|
+
});
|
|
14693
|
+
if (isCdpProviderRegistrable(config.cdp)) names.push(ProviderNames.cdpChatgpt);
|
|
14694
|
+
return names;
|
|
14695
|
+
}
|
|
14696
|
+
|
|
14697
|
+
// src/provider-runtime-config.ts
|
|
14698
|
+
var DEFAULT_PROVIDER_QUOTA = {
|
|
14699
|
+
maxConcurrency: 2,
|
|
14700
|
+
maxRequestsPerMinute: 10,
|
|
14701
|
+
maxRequestsPerDay: 1e3
|
|
14702
|
+
};
|
|
14703
|
+
var DEFAULT_CDP_QUOTA = {
|
|
14704
|
+
maxConcurrency: 1,
|
|
14705
|
+
maxRequestsPerMinute: 4,
|
|
14706
|
+
maxRequestsPerDay: 200
|
|
14707
|
+
};
|
|
14708
|
+
function resolveProviderQuotaPolicy(quota, defaults) {
|
|
14709
|
+
const saved = quota && typeof quota === "object" && !Array.isArray(quota) ? quota : {};
|
|
14710
|
+
return {
|
|
14711
|
+
maxConcurrency: saved.maxConcurrency ?? defaults.maxConcurrency,
|
|
14712
|
+
maxRequestsPerMinute: saved.maxRequestsPerMinute ?? defaults.maxRequestsPerMinute,
|
|
14713
|
+
maxRequestsPerDay: saved.maxRequestsPerDay ?? defaults.maxRequestsPerDay
|
|
14714
|
+
};
|
|
14715
|
+
}
|
|
14716
|
+
function configuredProviderEntries(config, apiAdapters, cdpAdapter) {
|
|
14717
|
+
const registered = [];
|
|
14718
|
+
for (const adapter of apiAdapters) {
|
|
14719
|
+
const entry = config.providers?.[adapter.name];
|
|
14720
|
+
if (!entry) continue;
|
|
14721
|
+
if (isApiProviderRegistrable(adapter.name, entry)) {
|
|
14722
|
+
registered.push(resolveRegistration(adapter, providerConfigFromEntry(
|
|
14723
|
+
adapter.name,
|
|
14724
|
+
entry,
|
|
14725
|
+
resolveProviderQuotaPolicy(entry.quota, DEFAULT_PROVIDER_QUOTA)
|
|
14726
|
+
)));
|
|
14727
|
+
}
|
|
14728
|
+
}
|
|
14729
|
+
if (isCdpProviderRegistrable(config.cdp)) {
|
|
14730
|
+
registered.push(resolveRegistration(cdpAdapter, {
|
|
14731
|
+
provider: cdpAdapter.name,
|
|
14732
|
+
cdpEndpoint: `ws://${config.cdp?.host ?? "localhost"}:${config.cdp?.port ?? 9222}`,
|
|
14733
|
+
quotaPolicy: resolveProviderQuotaPolicy(config.cdp?.quota, DEFAULT_CDP_QUOTA)
|
|
14734
|
+
}));
|
|
14735
|
+
}
|
|
14736
|
+
return registered;
|
|
14737
|
+
}
|
|
14738
|
+
|
|
14739
|
+
// src/provider-reload-batch-guard.ts
|
|
14740
|
+
import { isDeepStrictEqual } from "util";
|
|
14741
|
+
import { and as and15, inArray as inArray7 } from "drizzle-orm";
|
|
14742
|
+
function pendingBatchIdentity(provider) {
|
|
14743
|
+
const { quotaPolicy: _quotaPolicy, ...identity } = resolveRegistration(provider.adapter, provider.config).config;
|
|
14744
|
+
return identity;
|
|
14745
|
+
}
|
|
14746
|
+
function assertProviderReloadKeepsPendingBatches(db, previous, next, executing = []) {
|
|
14747
|
+
const nextByName = new Map(next.map((provider) => [provider.adapter.name, provider]));
|
|
14748
|
+
const changedRegistration = (provider) => {
|
|
14749
|
+
const replacement = nextByName.get(provider.adapter.name);
|
|
14750
|
+
return !replacement || !isDeepStrictEqual(pendingBatchIdentity(provider), pendingBatchIdentity(replacement));
|
|
14751
|
+
};
|
|
14752
|
+
const changed = previous.filter(changedRegistration).map((provider) => provider.adapter.name);
|
|
14753
|
+
const active = executing.filter((snapshot) => changedRegistration(snapshot.registration));
|
|
14754
|
+
if (changed.length === 0 && active.length === 0) return;
|
|
14755
|
+
const pending = changed.length === 0 ? [] : db.select({ id: providerBatches.id, provider: providerBatches.provider, runId: providerBatches.runId }).from(providerBatches).where(and15(
|
|
14756
|
+
inArray7(providerBatches.provider, changed),
|
|
14757
|
+
inArray7(providerBatches.status, [ProviderBatchStatuses.submitting, ProviderBatchStatuses.submitted, ProviderBatchStatuses.ended])
|
|
14758
|
+
)).all();
|
|
14759
|
+
if (pending.length === 0 && active.length === 0) return;
|
|
14760
|
+
throw operationInProgress("Changes to provider credentials or execution settings must wait for outstanding batch work to settle.", {
|
|
14761
|
+
providers: [.../* @__PURE__ */ new Set([...pending.map((batch) => batch.provider), ...active.map((snapshot) => snapshot.registration.adapter.name)])].sort(),
|
|
14762
|
+
batchIds: pending.map((batch) => batch.id).sort(),
|
|
14763
|
+
runIds: [.../* @__PURE__ */ new Set([...pending.map((batch) => batch.runId), ...active.map((snapshot) => snapshot.runId)])].sort()
|
|
14764
|
+
});
|
|
14765
|
+
}
|
|
14766
|
+
|
|
14622
14767
|
// src/provider-model-catalog.ts
|
|
14623
14768
|
import { createHash as createHash4 } from "crypto";
|
|
14624
14769
|
var TTL_MS = 60 * 60 * 1e3;
|
|
@@ -14682,7 +14827,7 @@ function createProviderModelCatalog(registry) {
|
|
|
14682
14827
|
// src/scheduler.ts
|
|
14683
14828
|
import crypto21 from "crypto";
|
|
14684
14829
|
import cron from "node-cron";
|
|
14685
|
-
import { and as
|
|
14830
|
+
import { and as and16, eq as eq17, inArray as inArray8, notExists, sql as sql7 } from "drizzle-orm";
|
|
14686
14831
|
var log16 = createLogger("Scheduler");
|
|
14687
14832
|
var DEFAULT_HEALTH_CRON = "0 */6 * * *";
|
|
14688
14833
|
function ensureDefaultHealthSchedule(db, projectId, now = (/* @__PURE__ */ new Date()).toISOString()) {
|
|
@@ -14733,7 +14878,7 @@ var Scheduler = class {
|
|
|
14733
14878
|
*/
|
|
14734
14879
|
ensureHealthSchedules() {
|
|
14735
14880
|
const projectsWithoutHealth = this.db.select({ id: projects.id }).from(projects).where(notExists(
|
|
14736
|
-
this.db.select({ one: sql7`1` }).from(schedules).where(
|
|
14881
|
+
this.db.select({ one: sql7`1` }).from(schedules).where(and16(
|
|
14737
14882
|
eq17(schedules.projectId, projects.id),
|
|
14738
14883
|
eq17(schedules.kind, SchedulableRunKinds.doctor)
|
|
14739
14884
|
))
|
|
@@ -14812,7 +14957,7 @@ var Scheduler = class {
|
|
|
14812
14957
|
this.stopTask(key, existing, "Stopped");
|
|
14813
14958
|
this.tasks.delete(key);
|
|
14814
14959
|
}
|
|
14815
|
-
const schedule = this.db.select().from(schedules).where(
|
|
14960
|
+
const schedule = this.db.select().from(schedules).where(and16(eq17(schedules.projectId, projectId), eq17(schedules.kind, kind))).get();
|
|
14816
14961
|
if (schedule && schedule.enabled) {
|
|
14817
14962
|
this.registerCronTask(schedule);
|
|
14818
14963
|
}
|
|
@@ -14948,7 +15093,7 @@ var Scheduler = class {
|
|
|
14948
15093
|
const result2 = tx.update(schedules).set({
|
|
14949
15094
|
nextRunAt,
|
|
14950
15095
|
updatedAt: nextScheduleUpdatedAt(current.updatedAt)
|
|
14951
|
-
}).where(
|
|
15096
|
+
}).where(and16(
|
|
14952
15097
|
eq17(schedules.id, current.id),
|
|
14953
15098
|
eq17(schedules.enabled, true),
|
|
14954
15099
|
eq17(schedules.nextRunAt, dueAt),
|
|
@@ -15027,10 +15172,10 @@ var Scheduler = class {
|
|
|
15027
15172
|
log16.warn("ads-sync.no-callback", { scheduleId, projectId, msg: "host did not register onAdsSyncRequested" });
|
|
15028
15173
|
return;
|
|
15029
15174
|
}
|
|
15030
|
-
const activeAdsRun = this.db.select({ id: runs.id }).from(runs).where(
|
|
15175
|
+
const activeAdsRun = this.db.select({ id: runs.id }).from(runs).where(and16(
|
|
15031
15176
|
eq17(runs.projectId, projectId),
|
|
15032
15177
|
eq17(runs.kind, RunKinds["ads-sync"]),
|
|
15033
|
-
|
|
15178
|
+
inArray8(runs.status, [RunStatuses.queued, RunStatuses.running])
|
|
15034
15179
|
)).get();
|
|
15035
15180
|
if (activeAdsRun) {
|
|
15036
15181
|
log16.info("ads-sync.skipped-active", { projectName: project.name, activeRunId: activeAdsRun.id });
|
|
@@ -15098,10 +15243,10 @@ var Scheduler = class {
|
|
|
15098
15243
|
log16.warn("site-audit.no-callback", { scheduleId, projectId, msg: "host did not register onSiteAuditRequested" });
|
|
15099
15244
|
return;
|
|
15100
15245
|
}
|
|
15101
|
-
const active = this.db.select({ id: runs.id }).from(runs).where(
|
|
15246
|
+
const active = this.db.select({ id: runs.id }).from(runs).where(and16(
|
|
15102
15247
|
eq17(runs.projectId, projectId),
|
|
15103
15248
|
eq17(runs.kind, RunKinds["site-audit"]),
|
|
15104
|
-
|
|
15249
|
+
inArray8(runs.status, [RunStatuses.queued, RunStatuses.running])
|
|
15105
15250
|
)).get();
|
|
15106
15251
|
if (active) {
|
|
15107
15252
|
log16.info("site-audit.skipped-active", { projectName: project.name, activeRunId: active.id });
|
|
@@ -15276,7 +15421,7 @@ function startSiteLivenessLoop(deps, intervalMs = SITE_LIVENESS_INTERVAL_MS) {
|
|
|
15276
15421
|
}
|
|
15277
15422
|
|
|
15278
15423
|
// src/provider-batch-poller.ts
|
|
15279
|
-
import { and as
|
|
15424
|
+
import { and as and17, eq as eq18, inArray as inArray9, isNotNull as isNotNull2, isNull as isNull2, notExists as notExists2, sql as sql8 } from "drizzle-orm";
|
|
15280
15425
|
var log18 = createLogger("ProviderBatchPoller");
|
|
15281
15426
|
var PROVIDER_BATCH_POLL_INITIAL_MS = 3e4;
|
|
15282
15427
|
var PROVIDER_BATCH_POLL_MAX_MS = 10 * 6e4;
|
|
@@ -15293,7 +15438,7 @@ var ProviderBatchPoller = class {
|
|
|
15293
15438
|
/** One pass: advance every outstanding batch that is due, then finalize what that settled. */
|
|
15294
15439
|
async tick() {
|
|
15295
15440
|
const { db } = this.deps;
|
|
15296
|
-
const rows = db.select().from(providerBatches).where(
|
|
15441
|
+
const rows = db.select().from(providerBatches).where(inArray9(providerBatches.status, [...OUTSTANDING_PROVIDER_BATCH_STATUSES])).all();
|
|
15297
15442
|
const live = new Set(rows.map((row) => row.id));
|
|
15298
15443
|
for (const id of this.schedule.keys()) if (!live.has(id)) this.schedule.delete(id);
|
|
15299
15444
|
const settledRuns = /* @__PURE__ */ new Map();
|
|
@@ -15336,7 +15481,7 @@ var ProviderBatchPoller = class {
|
|
|
15336
15481
|
status = poll.status;
|
|
15337
15482
|
if (poll.status === "ended") {
|
|
15338
15483
|
const endedAt = poll.endedAt ?? new Date(this.now()).toISOString();
|
|
15339
|
-
const ended = this.deps.db.update(providerBatches).set({ status: ProviderBatchStatuses.ended, endedAt, resultsExpireAt: poll.resultsExpireAt ?? null, updatedAt: endedAt }).where(
|
|
15484
|
+
const ended = this.deps.db.update(providerBatches).set({ status: ProviderBatchStatuses.ended, endedAt, resultsExpireAt: poll.resultsExpireAt ?? null, updatedAt: endedAt }).where(and17(eq18(providerBatches.id, row.id), eq18(providerBatches.status, ProviderBatchStatuses.submitted))).run().changes === 1;
|
|
15340
15485
|
this.schedule.delete(row.id);
|
|
15341
15486
|
return ended ? this.ingest({ ...row, status: ProviderBatchStatuses.ended, resultsExpireAt: poll.resultsExpireAt ?? null }) : false;
|
|
15342
15487
|
}
|
|
@@ -15360,7 +15505,7 @@ var ProviderBatchPoller = class {
|
|
|
15360
15505
|
const at = new Date(now).toISOString();
|
|
15361
15506
|
if (row.cancelRequestedAt === null) {
|
|
15362
15507
|
if (now < Date.parse(row.deadlineAt)) return false;
|
|
15363
|
-
const claimed = this.deps.db.update(providerBatches).set({ cancelRequestedAt: at, updatedAt: at }).where(
|
|
15508
|
+
const claimed = this.deps.db.update(providerBatches).set({ cancelRequestedAt: at, updatedAt: at }).where(and17(
|
|
15364
15509
|
eq18(providerBatches.id, row.id),
|
|
15365
15510
|
eq18(providerBatches.status, ProviderBatchStatuses.submitted),
|
|
15366
15511
|
isNull2(providerBatches.cancelRequestedAt)
|
|
@@ -15381,7 +15526,7 @@ var ProviderBatchPoller = class {
|
|
|
15381
15526
|
status: ProviderBatchStatuses.cancelled,
|
|
15382
15527
|
error: `The provider batch did not end within an hour of being cancelled at its deadline; its ${row.requestCount} answer(s) were not recorded.`,
|
|
15383
15528
|
updatedAt: at
|
|
15384
|
-
}).where(
|
|
15529
|
+
}).where(and17(eq18(providerBatches.id, row.id), eq18(providerBatches.status, ProviderBatchStatuses.submitted))).run().changes === 1;
|
|
15385
15530
|
this.schedule.delete(row.id);
|
|
15386
15531
|
if (gaveUp) log18.warn("batch.abandoned-after-deadline", { batchId: row.id, runId: row.runId, provider: row.provider });
|
|
15387
15532
|
return gaveUp;
|
|
@@ -15421,10 +15566,10 @@ var ProviderBatchPoller = class {
|
|
|
15421
15566
|
/** Runs the sweep handed off whose batches have all settled. */
|
|
15422
15567
|
finalizableRuns() {
|
|
15423
15568
|
const holding = [ProviderBatchStatuses.submitting, ...OUTSTANDING_PROVIDER_BATCH_STATUSES];
|
|
15424
|
-
return this.deps.db.select({ id: runs.id, projectId: runs.projectId }).from(runs).where(
|
|
15569
|
+
return this.deps.db.select({ id: runs.id, projectId: runs.projectId }).from(runs).where(and17(
|
|
15425
15570
|
eq18(runs.status, RunStatuses.running),
|
|
15426
15571
|
isNotNull2(runs.pendingProviderErrors),
|
|
15427
|
-
notExists2(this.deps.db.select({ one: sql8`1` }).from(providerBatches).where(
|
|
15572
|
+
notExists2(this.deps.db.select({ one: sql8`1` }).from(providerBatches).where(and17(eq18(providerBatches.runId, runs.id), inArray9(providerBatches.status, holding))))
|
|
15428
15573
|
)).all();
|
|
15429
15574
|
}
|
|
15430
15575
|
};
|
|
@@ -15495,7 +15640,7 @@ async function refreshAllIntegrations(client, projectName, now = /* @__PURE__ */
|
|
|
15495
15640
|
}
|
|
15496
15641
|
|
|
15497
15642
|
// src/notifier.ts
|
|
15498
|
-
import { eq as eq19, desc as desc8, and as
|
|
15643
|
+
import { eq as eq19, desc as desc8, and as and18, inArray as inArray10, or } from "drizzle-orm";
|
|
15499
15644
|
import crypto22 from "crypto";
|
|
15500
15645
|
var log20 = createLogger("Notifier");
|
|
15501
15646
|
var MATERIAL_MAGNITUDE_DELTA = 20;
|
|
@@ -15880,8 +16025,8 @@ var Notifier = class {
|
|
|
15880
16025
|
}
|
|
15881
16026
|
}
|
|
15882
16027
|
async dispatchQueuedReviewAlerts(projectId) {
|
|
15883
|
-
const pendingReviews = this.db.select().from(gbpReviews).where(
|
|
15884
|
-
const pendingRatings = this.db.select().from(gbpReviewRatings).where(
|
|
16028
|
+
const pendingReviews = this.db.select().from(gbpReviews).where(and18(eq19(gbpReviews.projectId, projectId), eq19(gbpReviews.alertState, "pending"))).all();
|
|
16029
|
+
const pendingRatings = this.db.select().from(gbpReviewRatings).where(and18(eq19(gbpReviewRatings.projectId, projectId), eq19(gbpReviewRatings.alertState, "pending"))).all();
|
|
15885
16030
|
if (pendingReviews.length === 0 && pendingRatings.length === 0) return { reviews: 0, ratingDrops: 0, delivered: 0 };
|
|
15886
16031
|
const project = this.db.select().from(projects).where(eq19(projects.id, projectId)).get();
|
|
15887
16032
|
if (!project) return { reviews: 0, ratingDrops: 0, delivered: 0 };
|
|
@@ -15897,7 +16042,7 @@ var Notifier = class {
|
|
|
15897
16042
|
}).from(gbpLocations).where(eq19(gbpLocations.projectId, projectId)).all().map((loc) => [loc.locationName, loc])
|
|
15898
16043
|
);
|
|
15899
16044
|
const v4HasWorked = new Set(
|
|
15900
|
-
this.db.select({ locationName: gbpReviewRatings.locationName }).from(gbpReviewRatings).where(
|
|
16045
|
+
this.db.select({ locationName: gbpReviewRatings.locationName }).from(gbpReviewRatings).where(and18(eq19(gbpReviewRatings.projectId, projectId), eq19(gbpReviewRatings.origin, "gbp"))).all().map((row) => row.locationName)
|
|
15901
16046
|
);
|
|
15902
16047
|
const coveredByBusinessProfile = (locationName) => businessProfileReviewsCover(locations.get(locationName)?.reviewsAccess ?? null, v4HasWorked.has(locationName));
|
|
15903
16048
|
const locationRef = (locationName) => {
|
|
@@ -15906,11 +16051,11 @@ var Notifier = class {
|
|
|
15906
16051
|
};
|
|
15907
16052
|
const setReviewState = (ids, alertState) => {
|
|
15908
16053
|
if (ids.length === 0) return;
|
|
15909
|
-
this.db.update(gbpReviews).set({ alertState, alertStateAt: nowIso }).where(
|
|
16054
|
+
this.db.update(gbpReviews).set({ alertState, alertStateAt: nowIso }).where(inArray10(gbpReviews.id, ids)).run();
|
|
15910
16055
|
};
|
|
15911
16056
|
const setRatingState = (ids, alertState) => {
|
|
15912
16057
|
if (ids.length === 0) return;
|
|
15913
|
-
this.db.update(gbpReviewRatings).set({ alertState, alertStateAt: nowIso }).where(
|
|
16058
|
+
this.db.update(gbpReviewRatings).set({ alertState, alertStateAt: nowIso }).where(inArray10(gbpReviewRatings.id, ids)).run();
|
|
15914
16059
|
};
|
|
15915
16060
|
const reviewsToSend = [];
|
|
15916
16061
|
for (const row of pendingReviews) {
|
|
@@ -16012,7 +16157,7 @@ var Notifier = class {
|
|
|
16012
16157
|
});
|
|
16013
16158
|
return [];
|
|
16014
16159
|
}
|
|
16015
|
-
const groupSiblings = this.db.select().from(runs).where(
|
|
16160
|
+
const groupSiblings = this.db.select().from(runs).where(and18(
|
|
16016
16161
|
eq19(runs.projectId, projectId),
|
|
16017
16162
|
eq19(runs.kind, thisRun.kind),
|
|
16018
16163
|
eq19(runs.createdAt, thisRun.createdAt),
|
|
@@ -16039,7 +16184,7 @@ var Notifier = class {
|
|
|
16039
16184
|
);
|
|
16040
16185
|
const RECENT_FETCH_LIMIT = Math.max(8, locationCount * 4);
|
|
16041
16186
|
const recentRuns = this.db.select().from(runs).where(
|
|
16042
|
-
|
|
16187
|
+
and18(
|
|
16043
16188
|
eq19(runs.projectId, projectId),
|
|
16044
16189
|
eq19(runs.kind, thisRun.kind),
|
|
16045
16190
|
or(eq19(runs.status, "completed"), eq19(runs.status, "partial")),
|
|
@@ -16060,13 +16205,13 @@ var Notifier = class {
|
|
|
16060
16205
|
provider: querySnapshots.provider,
|
|
16061
16206
|
location: querySnapshots.location,
|
|
16062
16207
|
citationState: querySnapshots.citationState
|
|
16063
|
-
}).from(querySnapshots).leftJoin(queries, eq19(querySnapshots.queryId, queries.id)).where(
|
|
16208
|
+
}).from(querySnapshots).leftJoin(queries, eq19(querySnapshots.queryId, queries.id)).where(inArray10(querySnapshots.runId, currentRunIds)).all();
|
|
16064
16209
|
const previousSnapshots = this.db.select({
|
|
16065
16210
|
queryId: querySnapshots.queryId,
|
|
16066
16211
|
provider: querySnapshots.provider,
|
|
16067
16212
|
location: querySnapshots.location,
|
|
16068
16213
|
citationState: querySnapshots.citationState
|
|
16069
|
-
}).from(querySnapshots).where(
|
|
16214
|
+
}).from(querySnapshots).where(inArray10(querySnapshots.runId, previousRunIds)).all();
|
|
16070
16215
|
const prevMap = /* @__PURE__ */ new Map();
|
|
16071
16216
|
for (const s of previousSnapshots) {
|
|
16072
16217
|
if (s.queryId == null) continue;
|
|
@@ -17377,6 +17522,8 @@ function mcpToAgentTool(tool, ctx) {
|
|
|
17377
17522
|
return adapted;
|
|
17378
17523
|
}
|
|
17379
17524
|
var AERO_EXCLUDED_MCP_TOOLS = /* @__PURE__ */ new Set([
|
|
17525
|
+
// Reloading install-wide credentials remains an explicit operator action.
|
|
17526
|
+
CanonryMcpToolNames.canonry_providers_reload,
|
|
17380
17527
|
// Aero reads stored sentiment. Configuration and backfill submission can
|
|
17381
17528
|
// start paid classifier work and remain operator actions.
|
|
17382
17529
|
"canonry_sentiment_configure",
|
|
@@ -17838,7 +17985,7 @@ function splitAeroAnthropicSystemCachePayload(payload, model) {
|
|
|
17838
17985
|
// src/agent/runtime.ts
|
|
17839
17986
|
import { z } from "zod";
|
|
17840
17987
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
17841
|
-
import { isDeepStrictEqual } from "util";
|
|
17988
|
+
import { isDeepStrictEqual as isDeepStrictEqual2 } from "util";
|
|
17842
17989
|
import {
|
|
17843
17990
|
Type as Type4,
|
|
17844
17991
|
createInitialSystemMessage,
|
|
@@ -17901,7 +18048,7 @@ function resolvePageReferences(runtime, tool, params) {
|
|
|
17901
18048
|
const page = runtime.pageReferences.get(value);
|
|
17902
18049
|
if (!page) throw new Error("This Aero page reference is no longer available. Read the first page again with the original filters.");
|
|
17903
18050
|
const inherited = { ...page.params, ...resolved };
|
|
17904
|
-
if (page.tool !== tool || page.parameter !== parameter || !
|
|
18051
|
+
if (page.tool !== tool || page.parameter !== parameter || !isDeepStrictEqual2(page.selection, pageSelection(tool, inherited, parameter))) {
|
|
17905
18052
|
throw new Error("Use this Aero page reference with the same tool and original filters; only limit, queryLimit or attemptLimit may change.");
|
|
17906
18053
|
}
|
|
17907
18054
|
resolved = { ...inherited, [parameter]: page.token };
|
|
@@ -18663,7 +18810,7 @@ async function loadExternalMcpTools(servers, opts = {}) {
|
|
|
18663
18810
|
|
|
18664
18811
|
// src/agent/memory-store.ts
|
|
18665
18812
|
import crypto25 from "crypto";
|
|
18666
|
-
import { and as
|
|
18813
|
+
import { and as and19, desc as desc9, eq as eq21, like, sql as sql9, or as or2 } from "drizzle-orm";
|
|
18667
18814
|
var COMPACTION_KEY_PREFIX = "compaction:";
|
|
18668
18815
|
var COMPACTION_NOTES_PER_SESSION = 3;
|
|
18669
18816
|
function rowToDto(row) {
|
|
@@ -18708,19 +18855,19 @@ function upsertMemoryEntry(db, args) {
|
|
|
18708
18855
|
updatedAt: now
|
|
18709
18856
|
}
|
|
18710
18857
|
}).run();
|
|
18711
|
-
const row = db.select().from(agentMemory).where(
|
|
18858
|
+
const row = db.select().from(agentMemory).where(and19(eq21(agentMemory.projectId, args.projectId), eq21(agentMemory.key, args.key))).get();
|
|
18712
18859
|
if (!row) throw new Error("memory upsert produced no row");
|
|
18713
18860
|
return rowToDto(row);
|
|
18714
18861
|
}
|
|
18715
18862
|
function deleteMemoryEntry(db, projectId, key) {
|
|
18716
|
-
const result2 = db.delete(agentMemory).where(
|
|
18863
|
+
const result2 = db.delete(agentMemory).where(and19(eq21(agentMemory.projectId, projectId), eq21(agentMemory.key, key))).run();
|
|
18717
18864
|
const changes = result2.changes ?? 0;
|
|
18718
18865
|
return changes > 0;
|
|
18719
18866
|
}
|
|
18720
18867
|
function loadRecentForHydrate(db, projectId, limit, sessionId) {
|
|
18721
18868
|
if (sessionId === void 0) return listMemoryEntries(db, projectId, { limit });
|
|
18722
18869
|
const sessionPrefix = `compaction:${sessionId ?? ""}:`;
|
|
18723
|
-
return db.select().from(agentMemory).where(
|
|
18870
|
+
return db.select().from(agentMemory).where(and19(
|
|
18724
18871
|
eq21(agentMemory.projectId, projectId),
|
|
18725
18872
|
or2(sql9`substr(${agentMemory.key}, 1, ${COMPACTION_KEY_PREFIX.length}) <> ${COMPACTION_KEY_PREFIX}`, sessionId ? sql9`substr(${agentMemory.key}, 1, ${sessionPrefix.length}) = ${sessionPrefix}` : void 0)
|
|
18726
18873
|
)).orderBy(desc9(agentMemory.updatedAt)).limit(limit).all().map(rowToDto);
|
|
@@ -18747,7 +18894,7 @@ function writeCompactionNote(db, args) {
|
|
|
18747
18894
|
}).run();
|
|
18748
18895
|
const sessionPrefix = `${COMPACTION_KEY_PREFIX}${args.sessionId}:`;
|
|
18749
18896
|
const existing = tx.select({ id: agentMemory.id, updatedAt: agentMemory.updatedAt }).from(agentMemory).where(
|
|
18750
|
-
|
|
18897
|
+
and19(
|
|
18751
18898
|
eq21(agentMemory.projectId, args.projectId),
|
|
18752
18899
|
like(agentMemory.key, `${sessionPrefix}%`)
|
|
18753
18900
|
)
|
|
@@ -18756,7 +18903,7 @@ function writeCompactionNote(db, args) {
|
|
|
18756
18903
|
if (stale.length > 0) {
|
|
18757
18904
|
tx.delete(agentMemory).where(sql9`${agentMemory.id} IN (${sql9.join(stale.map((s) => sql9`${s}`), sql9`, `)})`).run();
|
|
18758
18905
|
}
|
|
18759
|
-
const row = tx.select().from(agentMemory).where(
|
|
18906
|
+
const row = tx.select().from(agentMemory).where(and19(eq21(agentMemory.projectId, args.projectId), eq21(agentMemory.key, key))).get();
|
|
18760
18907
|
if (row) inserted = rowToDto(row);
|
|
18761
18908
|
});
|
|
18762
18909
|
if (!inserted) throw new Error("compaction note write produced no row");
|
|
@@ -18764,7 +18911,7 @@ function writeCompactionNote(db, args) {
|
|
|
18764
18911
|
}
|
|
18765
18912
|
|
|
18766
18913
|
// src/agent/project-shape.ts
|
|
18767
|
-
import { and as
|
|
18914
|
+
import { and as and20, count as count2, eq as eq22 } from "drizzle-orm";
|
|
18768
18915
|
var ADVANCED_TOOLS = [
|
|
18769
18916
|
"canonry_measurement_portfolio_summary",
|
|
18770
18917
|
"canonry_measurement_overview",
|
|
@@ -18792,7 +18939,7 @@ var ADVANCED_ROUTES = [
|
|
|
18792
18939
|
function aeroProjectShape(db, projectId) {
|
|
18793
18940
|
try {
|
|
18794
18941
|
const pointer = db.select().from(measurementPlans).where(eq22(measurementPlans.projectId, projectId)).get();
|
|
18795
|
-
const version = pointer ? db.select().from(measurementPlanVersions).where(
|
|
18942
|
+
const version = pointer ? db.select().from(measurementPlanVersions).where(and20(
|
|
18796
18943
|
eq22(measurementPlanVersions.projectId, projectId),
|
|
18797
18944
|
eq22(measurementPlanVersions.id, pointer.activeVersionId)
|
|
18798
18945
|
)).get() : void 0;
|
|
@@ -19586,7 +19733,7 @@ ${lines.join("\n")}
|
|
|
19586
19733
|
|
|
19587
19734
|
// src/agent/viewer-sessions.ts
|
|
19588
19735
|
import crypto27 from "crypto";
|
|
19589
|
-
import { and as
|
|
19736
|
+
import { and as and21, eq as eq24, isNotNull as isNotNull3, like as like2 } from "drizzle-orm";
|
|
19590
19737
|
var AERO_VIEWER_EXCLUDED_MCP_TOOLS = /* @__PURE__ */ new Set([
|
|
19591
19738
|
CanonryMcpToolNames.canonry_ads_account,
|
|
19592
19739
|
CanonryMcpToolNames.canonry_ads_geo_search,
|
|
@@ -19780,7 +19927,7 @@ var ViewerAeroSessions = class {
|
|
|
19780
19927
|
*/
|
|
19781
19928
|
revokeOrphanedKeys() {
|
|
19782
19929
|
try {
|
|
19783
|
-
this.opts.db.delete(apiKeys).where(
|
|
19930
|
+
this.opts.db.delete(apiKeys).where(and21(like2(apiKeys.name, `${VIEWER_AERO_KEY_PREFIX}%`), isNotNull3(apiKeys.delegatedUserId))).run();
|
|
19784
19931
|
} catch {
|
|
19785
19932
|
}
|
|
19786
19933
|
}
|
|
@@ -21147,20 +21294,20 @@ function clipText(value, length) {
|
|
|
21147
21294
|
}
|
|
21148
21295
|
|
|
21149
21296
|
// src/research-runner.ts
|
|
21150
|
-
import { and as
|
|
21297
|
+
import { and as and22, eq as eq27, inArray as inArray11, sql as sql10 } from "drizzle-orm";
|
|
21151
21298
|
var unfinishedResearchQueryStatuses = [ResearchQueryStatuses.queued, ResearchQueryStatuses.running];
|
|
21152
21299
|
function finalResearchRunStatus(completed, failed) {
|
|
21153
21300
|
if (failed === 0) return ResearchRunStatuses.completed;
|
|
21154
21301
|
return completed > 0 ? ResearchRunStatuses.partial : ResearchRunStatuses.failed;
|
|
21155
21302
|
}
|
|
21156
21303
|
async function executeResearchRun(db, registry, runId, projectId) {
|
|
21157
|
-
const run = db.select().from(researchRuns).where(
|
|
21304
|
+
const run = db.select().from(researchRuns).where(and22(eq27(researchRuns.id, runId), eq27(researchRuns.projectId, projectId))).get();
|
|
21158
21305
|
if (!run || run.status !== ResearchRunStatuses.queued) return;
|
|
21159
21306
|
const project = db.select().from(projects).where(eq27(projects.id, projectId)).get();
|
|
21160
21307
|
if (!project) return;
|
|
21161
21308
|
const provider = registry.get(run.provider);
|
|
21162
21309
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
21163
|
-
const claim = db.update(researchRuns).set({ status: ResearchRunStatuses.running, startedAt: now }).where(
|
|
21310
|
+
const claim = db.update(researchRuns).set({ status: ResearchRunStatuses.running, startedAt: now }).where(and22(eq27(researchRuns.id, runId), eq27(researchRuns.status, ResearchRunStatuses.queued))).run();
|
|
21164
21311
|
if (claim.changes !== 1) return;
|
|
21165
21312
|
let reserved = 0;
|
|
21166
21313
|
let dispatched = 0;
|
|
@@ -21195,7 +21342,7 @@ async function executeResearchRun(db, registry, runId, projectId) {
|
|
|
21195
21342
|
await mapWithConcurrency(rows, Math.max(1, provider.config.quotaPolicy.maxConcurrency), async (row) => {
|
|
21196
21343
|
try {
|
|
21197
21344
|
const startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
21198
|
-
db.update(researchRunQueries).set({ status: ResearchQueryStatuses.running, startedAt }).where(
|
|
21345
|
+
db.update(researchRunQueries).set({ status: ResearchQueryStatuses.running, startedAt }).where(and22(eq27(researchRunQueries.id, row.id), eq27(researchRunQueries.status, ResearchQueryStatuses.queued))).run();
|
|
21199
21346
|
const raw = await gate.run(async () => {
|
|
21200
21347
|
dispatched++;
|
|
21201
21348
|
return provider.adapter.executeTrackedQuery({ query: row.queryText, canonicalDomains: domains, competitorDomains, ...run.location ? { location: run.location } : {} }, config);
|
|
@@ -21223,7 +21370,7 @@ async function executeResearchRun(db, registry, runId, projectId) {
|
|
|
21223
21370
|
citationState: determineCitationState(normalized, domains),
|
|
21224
21371
|
rawResponse: raw.rawResponse,
|
|
21225
21372
|
finishedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
21226
|
-
}).where(
|
|
21373
|
+
}).where(and22(eq27(researchRunQueries.id, row.id), eq27(researchRunQueries.status, ResearchQueryStatuses.running))).run();
|
|
21227
21374
|
if (completed.changes === 1) incrementResearchProgress(db, runId, "completedQueries");
|
|
21228
21375
|
} catch (error) {
|
|
21229
21376
|
try {
|
|
@@ -21251,11 +21398,11 @@ function incrementResearchProgress(db, runId, column) {
|
|
|
21251
21398
|
}
|
|
21252
21399
|
function markResearchQueryFailed(db, runId, queryId, error) {
|
|
21253
21400
|
const message = describeError(error);
|
|
21254
|
-
const failed = db.update(researchRunQueries).set({ status: ResearchQueryStatuses.failed, error: message, finishedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(
|
|
21401
|
+
const failed = db.update(researchRunQueries).set({ status: ResearchQueryStatuses.failed, error: message, finishedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(and22(eq27(researchRunQueries.id, queryId), inArray11(researchRunQueries.status, unfinishedResearchQueryStatuses))).run();
|
|
21255
21402
|
if (failed.changes === 1) incrementResearchProgress(db, runId, "failedQueries");
|
|
21256
21403
|
}
|
|
21257
21404
|
function markUnfinishedResearchQueriesFailed(db, runId, error) {
|
|
21258
|
-
db.update(researchRunQueries).set({ status: ResearchQueryStatuses.failed, error, finishedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(
|
|
21405
|
+
db.update(researchRunQueries).set({ status: ResearchQueryStatuses.failed, error, finishedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(and22(eq27(researchRunQueries.researchRunId, runId), inArray11(researchRunQueries.status, unfinishedResearchQueryStatuses))).run();
|
|
21259
21406
|
}
|
|
21260
21407
|
function finalizeResearchRun(db, runId, fatalError) {
|
|
21261
21408
|
const rows = db.select({ status: researchRunQueries.status }).from(researchRunQueries).where(eq27(researchRunQueries.researchRunId, runId)).all();
|
|
@@ -21271,7 +21418,7 @@ function finalizeResearchRun(db, runId, fatalError) {
|
|
|
21271
21418
|
}
|
|
21272
21419
|
|
|
21273
21420
|
// src/google-marketing-sync.ts
|
|
21274
|
-
import { and as
|
|
21421
|
+
import { and as and23, eq as eq28, isNull as isNull3 } from "drizzle-orm";
|
|
21275
21422
|
function safeErrorMessage(error) {
|
|
21276
21423
|
const raw = describeError(error);
|
|
21277
21424
|
return raw.replace(/(["']?authorization["']?\s*[:=]\s*["']?bearer\s+)[^\s,"'}]+/gi, "$1[redacted]").replace(/(["']?(?:developer|access|refresh|client)[_-]?(?:token|secret)["']?\s*[:=]\s*["']?)[^\s,"'}]+/gi, "$1[redacted]").replace(/[\r\n\t]+/g, " ").slice(0, 1e3);
|
|
@@ -21294,7 +21441,7 @@ function assertSnapshotContext(metadata2, expected) {
|
|
|
21294
21441
|
}
|
|
21295
21442
|
}
|
|
21296
21443
|
function requireSyncRun(db, runId, projectId, kind) {
|
|
21297
|
-
const run = db.select().from(runs).where(
|
|
21444
|
+
const run = db.select().from(runs).where(and23(
|
|
21298
21445
|
eq28(runs.id, runId),
|
|
21299
21446
|
eq28(runs.projectId, projectId),
|
|
21300
21447
|
eq28(runs.kind, kind)
|
|
@@ -21310,7 +21457,7 @@ function markFailed(db, runId, projectId, kind, expectedStatus, error) {
|
|
|
21310
21457
|
status: RunStatuses.failed,
|
|
21311
21458
|
error: serializeRunError({ message: safeErrorMessage(error) }),
|
|
21312
21459
|
finishedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
21313
|
-
}).where(
|
|
21460
|
+
}).where(and23(
|
|
21314
21461
|
eq28(runs.id, runId),
|
|
21315
21462
|
eq28(runs.projectId, projectId),
|
|
21316
21463
|
eq28(runs.kind, kind),
|
|
@@ -21318,7 +21465,7 @@ function markFailed(db, runId, projectId, kind, expectedStatus, error) {
|
|
|
21318
21465
|
)).run();
|
|
21319
21466
|
}
|
|
21320
21467
|
function claimSyncRun(db, runId, projectId, kind, startedAt) {
|
|
21321
|
-
const claim = db.update(runs).set({ status: RunStatuses.running, startedAt, error: null }).where(
|
|
21468
|
+
const claim = db.update(runs).set({ status: RunStatuses.running, startedAt, error: null }).where(and23(
|
|
21322
21469
|
eq28(runs.id, runId),
|
|
21323
21470
|
eq28(runs.projectId, projectId),
|
|
21324
21471
|
eq28(runs.kind, kind),
|
|
@@ -21383,7 +21530,7 @@ async function executeGoogleAdsMarketingSync(db, runtime, runId, projectId) {
|
|
|
21383
21530
|
status: RunStatuses.completed,
|
|
21384
21531
|
finishedAt,
|
|
21385
21532
|
error: null
|
|
21386
|
-
}).where(
|
|
21533
|
+
}).where(and23(
|
|
21387
21534
|
eq28(runs.id, runId),
|
|
21388
21535
|
eq28(runs.projectId, projectId),
|
|
21389
21536
|
eq28(runs.kind, RunKinds["google-ads-sync"]),
|
|
@@ -21404,7 +21551,7 @@ async function executeGoogleAdsMarketingSync(db, runtime, runId, projectId) {
|
|
|
21404
21551
|
lastMetricsSnapshotAt: result2.metrics?.metadata.capturedAt ?? connection.lastMetricsSnapshotAt,
|
|
21405
21552
|
lastMetricsSnapshotId: result2.metrics?.metadata.id ?? connection.lastMetricsSnapshotId,
|
|
21406
21553
|
updatedAt: finishedAt
|
|
21407
|
-
}).where(
|
|
21554
|
+
}).where(and23(
|
|
21408
21555
|
eq28(googleAdsConnections.id, connection.id),
|
|
21409
21556
|
eq28(googleAdsConnections.projectId, projectId),
|
|
21410
21557
|
eq28(googleAdsConnections.selectedCustomerId, connection.selectedCustomerId),
|
|
@@ -21473,7 +21620,7 @@ async function executeGtmMarketingSync(db, runtime, runId, projectId) {
|
|
|
21473
21620
|
status: RunStatuses.completed,
|
|
21474
21621
|
finishedAt,
|
|
21475
21622
|
error: null
|
|
21476
|
-
}).where(
|
|
21623
|
+
}).where(and23(
|
|
21477
21624
|
eq28(runs.id, runId),
|
|
21478
21625
|
eq28(runs.projectId, projectId),
|
|
21479
21626
|
eq28(runs.kind, RunKinds["gtm-sync"]),
|
|
@@ -21491,7 +21638,7 @@ async function executeGtmMarketingSync(db, runtime, runId, projectId) {
|
|
|
21491
21638
|
lastSnapshotAt: snapshot.metadata.capturedAt,
|
|
21492
21639
|
lastSnapshotId: snapshot.metadata.id,
|
|
21493
21640
|
updatedAt: finishedAt
|
|
21494
|
-
}).where(
|
|
21641
|
+
}).where(and23(
|
|
21495
21642
|
eq28(gtmConnections.id, connection.id),
|
|
21496
21643
|
eq28(gtmConnections.projectId, projectId),
|
|
21497
21644
|
eq28(gtmConnections.selectedAccountId, connection.selectedAccountId),
|
|
@@ -21530,11 +21677,7 @@ var keylessFirstRunSetupByServer = /* @__PURE__ */ new WeakMap();
|
|
|
21530
21677
|
function allowsKeylessFirstRunSetup(app) {
|
|
21531
21678
|
return keylessFirstRunSetupByServer.get(app) ?? false;
|
|
21532
21679
|
}
|
|
21533
|
-
var DEFAULT_QUOTA =
|
|
21534
|
-
maxConcurrency: 2,
|
|
21535
|
-
maxRequestsPerMinute: 10,
|
|
21536
|
-
maxRequestsPerDay: 1e3
|
|
21537
|
-
};
|
|
21680
|
+
var DEFAULT_QUOTA = DEFAULT_PROVIDER_QUOTA;
|
|
21538
21681
|
var SESSION_COOKIE_NAME = "canonry_session";
|
|
21539
21682
|
var SESSION_TTL_MS = 12 * 60 * 60 * 1e3;
|
|
21540
21683
|
function effectiveProviderModels(registry) {
|
|
@@ -21609,19 +21752,47 @@ var BROWSER_ADAPTERS = [cdpChatgptAdapter];
|
|
|
21609
21752
|
var adapterMap = Object.fromEntries(
|
|
21610
21753
|
API_ADAPTERS.map((a) => [a.name, a])
|
|
21611
21754
|
);
|
|
21612
|
-
function summarizeProviderConfig(
|
|
21755
|
+
function summarizeProviderConfig(provider) {
|
|
21756
|
+
const config = provider?.config;
|
|
21613
21757
|
return {
|
|
21614
|
-
configured: Boolean(config?.apiKey || config?.baseUrl),
|
|
21758
|
+
configured: Boolean(config?.apiKey || config?.baseUrl || config?.vertexProject || config?.cdpEndpoint),
|
|
21615
21759
|
model: config?.model ?? null,
|
|
21616
21760
|
// baseUrl is surfaced for ALL providers, not just local — gemini/openai now
|
|
21617
21761
|
// honor a custom endpoint, so repointing one must show in the settings
|
|
21618
21762
|
// summary AND produce an audit diff. Omitting it for API providers would let
|
|
21619
21763
|
// an endpoint redirect (a credential-exfiltration vector on a box where the
|
|
21620
21764
|
// provider key is the carrier) happen with no audit trail.
|
|
21621
|
-
baseUrl: config?.baseUrl ?? null,
|
|
21622
|
-
quota: { ...config?.
|
|
21765
|
+
baseUrl: config?.baseUrl ?? config?.cdpEndpoint ?? null,
|
|
21766
|
+
quota: { ...config?.quotaPolicy ?? DEFAULT_QUOTA },
|
|
21767
|
+
vertexProject: config?.vertexProject ?? null,
|
|
21768
|
+
vertexRegion: config?.vertexRegion ?? null,
|
|
21769
|
+
batch: config?.batch ?? null,
|
|
21770
|
+
pricing: config?.pricing ?? null
|
|
21623
21771
|
};
|
|
21624
21772
|
}
|
|
21773
|
+
function providerSummaries(registry, config, includeBrowser = false) {
|
|
21774
|
+
return [...API_ADAPTERS, ...includeBrowser ? BROWSER_ADAPTERS : []].map((adapter) => ({
|
|
21775
|
+
name: adapter.name,
|
|
21776
|
+
displayName: adapter.displayName,
|
|
21777
|
+
keyUrl: adapter.keyUrl,
|
|
21778
|
+
modelHint: `e.g. ${adapter.modelRegistry.defaultModel}`,
|
|
21779
|
+
model: registry.get(adapter.name)?.config.model ?? void 0,
|
|
21780
|
+
defaultModel: adapter.modelRegistry.defaultModel,
|
|
21781
|
+
configured: Boolean(registry.get(adapter.name)),
|
|
21782
|
+
quota: registry.get(adapter.name)?.config.quotaPolicy,
|
|
21783
|
+
vertexConfigured: adapter.name === ProviderNames.gemini ? Boolean(config.providers?.gemini?.vertexProject) : void 0
|
|
21784
|
+
}));
|
|
21785
|
+
}
|
|
21786
|
+
function runtimePathIdentity(runtimePath) {
|
|
21787
|
+
if (runtimePath === ":memory:") return runtimePath;
|
|
21788
|
+
const resolved = path8.resolve(runtimePath);
|
|
21789
|
+
let directory = path8.dirname(resolved);
|
|
21790
|
+
try {
|
|
21791
|
+
directory = fs9.realpathSync(directory);
|
|
21792
|
+
} catch {
|
|
21793
|
+
}
|
|
21794
|
+
return path8.join(directory, path8.basename(resolved));
|
|
21795
|
+
}
|
|
21625
21796
|
var DASHBOARD_SCRYPT_KEYLEN = 64;
|
|
21626
21797
|
var DASHBOARD_SCRYPT_COST = 1 << 15;
|
|
21627
21798
|
var DASHBOARD_SCRYPT_MAXMEM = 64 * 1024 * 1024;
|
|
@@ -21932,6 +22103,10 @@ function createGoogleMarketingConfigCredentialStore(input) {
|
|
|
21932
22103
|
};
|
|
21933
22104
|
}
|
|
21934
22105
|
async function createServer(opts) {
|
|
22106
|
+
const runtimeConfigPath = path8.resolve(getConfigPath());
|
|
22107
|
+
const runtimeConfigIdentity = runtimePathIdentity(runtimeConfigPath);
|
|
22108
|
+
const runtimeDatabasePath = opts.config.database;
|
|
22109
|
+
const runtimeDatabaseIdentity = runtimePathIdentity(runtimeDatabasePath);
|
|
21935
22110
|
const embed = resolveEmbedConfig(process.env, opts.config);
|
|
21936
22111
|
const operatorApiKeyIds = resolveOperatorApiKeyIds(process.env);
|
|
21937
22112
|
const dashboardManagedRunKinds = resolveDashboardManagedRunKinds(process.env, opts.config);
|
|
@@ -21993,6 +22168,7 @@ async function createServer(opts) {
|
|
|
21993
22168
|
quota: opts.config.geminiQuota
|
|
21994
22169
|
};
|
|
21995
22170
|
}
|
|
22171
|
+
opts.config.providers = providers;
|
|
21996
22172
|
try {
|
|
21997
22173
|
const legacyRows = extractLegacyCredentials(opts.db);
|
|
21998
22174
|
applyLegacyCredentials(legacyRows, opts.config);
|
|
@@ -22003,39 +22179,15 @@ async function createServer(opts) {
|
|
|
22003
22179
|
});
|
|
22004
22180
|
}
|
|
22005
22181
|
log25.info("providers.configured", {
|
|
22006
|
-
providers:
|
|
22007
|
-
const p = providers[k];
|
|
22008
|
-
return p?.apiKey || p?.baseUrl || p?.vertexProject;
|
|
22009
|
-
})
|
|
22182
|
+
providers: registeredProviderNames({ providers, cdp: opts.config.cdp })
|
|
22010
22183
|
});
|
|
22011
|
-
|
|
22012
|
-
const entry = providers[adapter.name];
|
|
22013
|
-
if (!entry) continue;
|
|
22014
|
-
const isConfigured = adapter.name === "local" ? !!entry.baseUrl : adapter.name === "gemini" ? !!(entry.apiKey || entry.vertexProject) : !!entry.apiKey;
|
|
22015
|
-
if (isConfigured) {
|
|
22016
|
-
registry.register(adapter, providerConfigFromEntry(adapter.name, entry, entry.quota ?? DEFAULT_QUOTA));
|
|
22017
|
-
}
|
|
22018
|
-
}
|
|
22184
|
+
registry.replace(configuredProviderEntries(opts.config, API_ADAPTERS, cdpChatgptAdapter));
|
|
22019
22185
|
for (const name of providersWithUnsupportedBatch(providers, (provider) => adapterMap[provider])) {
|
|
22020
22186
|
log25.warn("provider.batch.unsupported", {
|
|
22021
22187
|
provider: name,
|
|
22022
22188
|
message: `providers.${name}.batch.enabled is true, but the ${name} adapter has no batch API; its sweeps run sync.`
|
|
22023
22189
|
});
|
|
22024
22190
|
}
|
|
22025
|
-
const cdpConfig = opts.config.cdp;
|
|
22026
|
-
if (cdpConfig?.host || cdpConfig?.port) {
|
|
22027
|
-
const CDP_DEFAULT_QUOTA = {
|
|
22028
|
-
maxConcurrency: 1,
|
|
22029
|
-
maxRequestsPerMinute: 4,
|
|
22030
|
-
maxRequestsPerDay: 200
|
|
22031
|
-
};
|
|
22032
|
-
const cdpEndpoint = `ws://${cdpConfig.host ?? "localhost"}:${cdpConfig.port ?? 9222}`;
|
|
22033
|
-
registry.register(cdpChatgptAdapter, {
|
|
22034
|
-
provider: "cdp:chatgpt",
|
|
22035
|
-
cdpEndpoint,
|
|
22036
|
-
quotaPolicy: cdpConfig.quota ?? CDP_DEFAULT_QUOTA
|
|
22037
|
-
});
|
|
22038
|
-
}
|
|
22039
22191
|
const port = opts.config.port ?? 4100;
|
|
22040
22192
|
const serverUrl = `http://localhost:${port}`;
|
|
22041
22193
|
const jobRunner = new JobRunner(opts.db, registry, {
|
|
@@ -22566,7 +22718,7 @@ async function createServer(opts) {
|
|
|
22566
22718
|
});
|
|
22567
22719
|
if (!probed) return;
|
|
22568
22720
|
const alreadySynced = opts.db.select().from(ccReleaseSyncs).where(
|
|
22569
|
-
|
|
22721
|
+
and24(
|
|
22570
22722
|
eq29(ccReleaseSyncs.release, probed.release),
|
|
22571
22723
|
eq29(ccReleaseSyncs.status, CcReleaseSyncStatuses.ready)
|
|
22572
22724
|
)
|
|
@@ -22594,17 +22746,7 @@ async function createServer(opts) {
|
|
|
22594
22746
|
runSiteAudit(runId, projectId, auditOpts);
|
|
22595
22747
|
}
|
|
22596
22748
|
});
|
|
22597
|
-
const providerSummary =
|
|
22598
|
-
name: adapter.name,
|
|
22599
|
-
displayName: adapter.displayName,
|
|
22600
|
-
keyUrl: adapter.keyUrl,
|
|
22601
|
-
modelHint: `e.g. ${adapter.modelRegistry.defaultModel}`,
|
|
22602
|
-
model: registry.get(adapter.name)?.config.model,
|
|
22603
|
-
defaultModel: adapter.modelRegistry.defaultModel,
|
|
22604
|
-
configured: !!registry.get(adapter.name),
|
|
22605
|
-
quota: registry.get(adapter.name)?.config.quotaPolicy,
|
|
22606
|
-
vertexConfigured: adapter.name === "gemini" ? !!opts.config.providers?.gemini?.vertexProject : void 0
|
|
22607
|
-
}));
|
|
22749
|
+
const providerSummary = providerSummaries(registry, opts.config);
|
|
22608
22750
|
const googleSettingsSummary = {
|
|
22609
22751
|
configured: Boolean(
|
|
22610
22752
|
opts.config.google?.clientId && opts.config.google?.clientSecret
|
|
@@ -22858,7 +23000,7 @@ async function createServer(opts) {
|
|
|
22858
23000
|
const currentPasswordFingerprint = () => hashSessionToken(opts.config.dashboardPasswordHash ?? "");
|
|
22859
23001
|
opts.db.delete(dashboardSessions).where(or3(
|
|
22860
23002
|
eq29(dashboardSessions.apiKeyHash, ""),
|
|
22861
|
-
|
|
23003
|
+
and24(
|
|
22862
23004
|
isNotNull4(dashboardSessions.passwordFingerprint),
|
|
22863
23005
|
ne3(dashboardSessions.passwordFingerprint, currentPasswordFingerprint())
|
|
22864
23006
|
)
|
|
@@ -22885,7 +23027,7 @@ async function createServer(opts) {
|
|
|
22885
23027
|
currentApiKeyHash: apiKeys.keyHash,
|
|
22886
23028
|
passwordFingerprint: dashboardSessions.passwordFingerprint,
|
|
22887
23029
|
expiresAt: dashboardSessions.expiresAt
|
|
22888
|
-
}).from(dashboardSessions).innerJoin(apiKeys, eq29(apiKeys.id, dashboardSessions.apiKeyId)).where(
|
|
23030
|
+
}).from(dashboardSessions).innerJoin(apiKeys, eq29(apiKeys.id, dashboardSessions.apiKeyId)).where(and24(eq29(dashboardSessions.tokenHash, tokenHash), isNull4(apiKeys.revokedAt))).get();
|
|
22889
23031
|
if (!session) return null;
|
|
22890
23032
|
const expiresAt = Date.parse(session.expiresAt);
|
|
22891
23033
|
if (!Number.isFinite(expiresAt) || expiresAt <= Date.now() || session.apiKeyHash !== session.currentApiKeyHash || session.passwordFingerprint !== null && session.passwordFingerprint !== currentPasswordFingerprint()) {
|
|
@@ -23148,7 +23290,7 @@ async function createServer(opts) {
|
|
|
23148
23290
|
opts.db.update(projects).set({ answerFieldsRecompute: null }).where(eq29(projects.id, projectId)).run();
|
|
23149
23291
|
if (recomputeDebts.get(projectId) === debt) recomputeDebts.delete(projectId);
|
|
23150
23292
|
} else if (debt.latestFull === 0) {
|
|
23151
|
-
opts.db.update(projects).set({ answerFieldsRecompute: "competitors" }).where(
|
|
23293
|
+
opts.db.update(projects).set({ answerFieldsRecompute: "competitors" }).where(and24(eq29(projects.id, projectId), eq29(projects.answerFieldsRecompute, "full"))).run();
|
|
23152
23294
|
}
|
|
23153
23295
|
} catch (err) {
|
|
23154
23296
|
app.log.error({ err, projectId }, "could not clear the owed answer field recompute");
|
|
@@ -23238,6 +23380,34 @@ async function createServer(opts) {
|
|
|
23238
23380
|
await sentimentPoller.settled();
|
|
23239
23381
|
});
|
|
23240
23382
|
const providerModelCatalog = createProviderModelCatalog(registry);
|
|
23383
|
+
const recordProviderChange = (name, before, after, auditContext) => {
|
|
23384
|
+
const beforeConfig = summarizeProviderConfig(before);
|
|
23385
|
+
const afterConfig = summarizeProviderConfig(after);
|
|
23386
|
+
const apiKeyRotated = Boolean(before) && Boolean(after?.config.apiKey) && before?.config.apiKey !== after?.config.apiKey;
|
|
23387
|
+
const credentialsChanged = before?.config.vertexCredentials !== after?.config.vertexCredentials;
|
|
23388
|
+
if (!apiKeyRotated && !credentialsChanged && JSON.stringify(beforeConfig) === JSON.stringify(afterConfig)) return;
|
|
23389
|
+
const affectedProjects = opts.db.select({ id: projects.id, providers: projects.providers }).from(projects).all().filter((project) => project.providers.length === 0 || project.providers.includes(name));
|
|
23390
|
+
const projectIds = affectedProjects.length ? affectedProjects.map((project) => project.id) : [null];
|
|
23391
|
+
for (const projectId of projectIds) {
|
|
23392
|
+
writeAuditLog(opts.db, {
|
|
23393
|
+
projectId,
|
|
23394
|
+
actor: auditContext?.actor ?? "api",
|
|
23395
|
+
userAgent: auditContext?.userAgent,
|
|
23396
|
+
actorSession: auditContext?.actorSession,
|
|
23397
|
+
credentialId: auditContext?.credentialId,
|
|
23398
|
+
requestId: auditContext?.requestId,
|
|
23399
|
+
action: after ? before ? "provider.updated" : "provider.created" : "provider.removed",
|
|
23400
|
+
entityType: "provider",
|
|
23401
|
+
entityId: name,
|
|
23402
|
+
diff: {
|
|
23403
|
+
before: before ? beforeConfig : null,
|
|
23404
|
+
after: afterConfig,
|
|
23405
|
+
...apiKeyRotated ? { apiKeyRotated: true } : {},
|
|
23406
|
+
...credentialsChanged ? { credentialsChanged: true } : {}
|
|
23407
|
+
}
|
|
23408
|
+
});
|
|
23409
|
+
}
|
|
23410
|
+
};
|
|
23241
23411
|
await app.register(apiRoutes, {
|
|
23242
23412
|
db: opts.db,
|
|
23243
23413
|
sentiment: {
|
|
@@ -23288,13 +23458,10 @@ async function createServer(opts) {
|
|
|
23288
23458
|
// and would have thrown if the file were missing; tests that construct
|
|
23289
23459
|
// `createServer` directly (bypassing `loadConfig`) won't have written
|
|
23290
23460
|
// a config and shouldn't get 503s from a stub-missing file.
|
|
23291
|
-
runtimeStatePaths:
|
|
23292
|
-
|
|
23293
|
-
|
|
23294
|
-
|
|
23295
|
-
configPath: fs9.existsSync(configPath) ? configPath : null
|
|
23296
|
-
};
|
|
23297
|
-
})(),
|
|
23461
|
+
runtimeStatePaths: {
|
|
23462
|
+
databasePath: runtimeDatabasePath,
|
|
23463
|
+
configPath: fs9.existsSync(runtimeConfigPath) ? runtimeConfigPath : null
|
|
23464
|
+
},
|
|
23298
23465
|
// Snapshot the bundled skill trees (version + file hashes) so the
|
|
23299
23466
|
// `agent.skills.current` doctor check can flag a `~/.claude/skills/` install
|
|
23300
23467
|
// that has drifted behind this build. Best-effort: if the bundled assets
|
|
@@ -23657,12 +23824,43 @@ async function createServer(opts) {
|
|
|
23657
23824
|
getRunnableProviderNames: () => registry.getAll().map((provider) => provider.adapter.name),
|
|
23658
23825
|
getEffectiveProviderModels: () => effectiveProviderModels(registry),
|
|
23659
23826
|
getBatchEligibleProviderNames: () => batchEligibleProviderNames(registry),
|
|
23827
|
+
onProviderReload: (input, auditContext) => {
|
|
23828
|
+
if (input.configPath !== void 0 && (runtimePathIdentity(input.configPath) !== runtimeConfigIdentity || runtimePathIdentity(input.databasePath) !== runtimeDatabaseIdentity)) {
|
|
23829
|
+
throw validationError("Provider reload identity does not match this running server.", {
|
|
23830
|
+
reason: ProviderReloadErrorReasons["install-identity-mismatch"]
|
|
23831
|
+
});
|
|
23832
|
+
}
|
|
23833
|
+
let loaded;
|
|
23834
|
+
try {
|
|
23835
|
+
loaded = loadConfig(runtimeConfigPath);
|
|
23836
|
+
} catch {
|
|
23837
|
+
throw validationError("Provider configuration could not be loaded; repair config.yaml and retry.");
|
|
23838
|
+
}
|
|
23839
|
+
const next = configuredProviderEntries(loaded, API_ADAPTERS, cdpChatgptAdapter);
|
|
23840
|
+
assertProviderReloadKeepsPendingBatches(opts.db, registry.getAll(), next, jobRunner.getExecutingBatchRegistrations());
|
|
23841
|
+
const nextByName = new Map(next.map((provider) => [provider.adapter.name, provider]));
|
|
23842
|
+
const names = /* @__PURE__ */ new Set([...registry.getAll().map((provider) => provider.adapter.name), ...nextByName.keys()]);
|
|
23843
|
+
opts.db.transaction(() => {
|
|
23844
|
+
for (const name of names) recordProviderChange(name, registry.get(name), nextByName.get(name), auditContext);
|
|
23845
|
+
});
|
|
23846
|
+
registry.replace(next);
|
|
23847
|
+
opts.config.providers = loaded.providers ?? {};
|
|
23848
|
+
opts.config.cdp = loaded.cdp;
|
|
23849
|
+
opts.config.geminiApiKey = loaded.geminiApiKey;
|
|
23850
|
+
opts.config.geminiModel = loaded.geminiModel;
|
|
23851
|
+
opts.config.geminiQuota = loaded.geminiQuota;
|
|
23852
|
+
providerSummary.splice(0, providerSummary.length, ...providerSummaries(registry, opts.config));
|
|
23853
|
+
return providerSummaries(registry, opts.config, true);
|
|
23854
|
+
},
|
|
23660
23855
|
onProviderUpdate: (providerName, apiKey, model, baseUrl, incomingQuota, auditContext) => {
|
|
23661
23856
|
const name = providerName;
|
|
23662
23857
|
if (!adapterMap[name]) return null;
|
|
23663
23858
|
if (!opts.config.providers) opts.config.providers = {};
|
|
23664
23859
|
const existing = opts.config.providers[name];
|
|
23665
|
-
const
|
|
23860
|
+
const previousProvider = registry.get(name) ?? (existing ? {
|
|
23861
|
+
adapter: adapterMap[name],
|
|
23862
|
+
config: providerConfigFromEntry(name, existing, resolveProviderQuotaPolicy(existing.quota, DEFAULT_QUOTA))
|
|
23863
|
+
} : void 0);
|
|
23666
23864
|
const mergedQuota = incomingQuota ? { ...existing?.quota ?? DEFAULT_QUOTA, ...incomingQuota } : existing?.quota;
|
|
23667
23865
|
opts.config.providers[name] = {
|
|
23668
23866
|
apiKey: apiKey || existing?.apiKey,
|
|
@@ -23680,53 +23878,16 @@ async function createServer(opts) {
|
|
|
23680
23878
|
...existing?.pricing ? { pricing: existing.pricing } : {}
|
|
23681
23879
|
};
|
|
23682
23880
|
try {
|
|
23683
|
-
saveConfigPatch(opts.config);
|
|
23881
|
+
saveConfigPatch(fs9.existsSync(runtimeConfigPath) ? { providers: { [name]: opts.config.providers[name] } } : opts.config);
|
|
23684
23882
|
} catch (err) {
|
|
23685
23883
|
app.log.error({ err }, "Failed to save config");
|
|
23686
23884
|
return null;
|
|
23687
23885
|
}
|
|
23688
|
-
const quota = opts.config.providers[name].quota
|
|
23886
|
+
const quota = resolveProviderQuotaPolicy(opts.config.providers[name].quota, DEFAULT_QUOTA);
|
|
23689
23887
|
registry.register(adapterMap[name], providerConfigFromEntry(name, opts.config.providers[name], quota));
|
|
23690
|
-
|
|
23691
|
-
|
|
23692
|
-
|
|
23693
|
-
entry.model = registry.get(name)?.config.model;
|
|
23694
|
-
entry.quota = quota;
|
|
23695
|
-
if (name === "gemini") {
|
|
23696
|
-
entry.vertexConfigured = !!opts.config.providers?.[name]?.vertexProject;
|
|
23697
|
-
}
|
|
23698
|
-
}
|
|
23699
|
-
const afterConfig = summarizeProviderConfig(opts.config.providers[name]);
|
|
23700
|
-
const apiKeyRotated = Boolean(existing) && Boolean(apiKey) && apiKey !== existing?.apiKey;
|
|
23701
|
-
if (apiKeyRotated || JSON.stringify(beforeConfig) !== JSON.stringify(afterConfig)) {
|
|
23702
|
-
const diff = JSON.stringify({
|
|
23703
|
-
before: existing ? beforeConfig : null,
|
|
23704
|
-
after: afterConfig,
|
|
23705
|
-
...apiKeyRotated ? { apiKeyRotated: true } : {}
|
|
23706
|
-
});
|
|
23707
|
-
const affectedProjectIds = opts.db.select({ id: projects.id, providers: projects.providers }).from(projects).all().filter((project) => {
|
|
23708
|
-
const configuredProviders = project.providers;
|
|
23709
|
-
return configuredProviders.length === 0 || configuredProviders.includes(name);
|
|
23710
|
-
}).map((project) => project.id);
|
|
23711
|
-
const targetProjectIds = affectedProjectIds.length > 0 ? affectedProjectIds : [null];
|
|
23712
|
-
const createdAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
23713
|
-
opts.db.insert(auditLog).values(
|
|
23714
|
-
targetProjectIds.map((projectId) => ({
|
|
23715
|
-
id: crypto29.randomUUID(),
|
|
23716
|
-
projectId,
|
|
23717
|
-
actor: auditContext?.actor ?? "api",
|
|
23718
|
-
userAgent: auditContext?.userAgent ?? null,
|
|
23719
|
-
actorSession: auditContext?.actorSession ?? null,
|
|
23720
|
-
credentialId: auditContext?.credentialId ?? null,
|
|
23721
|
-
requestId: auditContext?.requestId ?? null,
|
|
23722
|
-
action: existing ? "provider.updated" : "provider.created",
|
|
23723
|
-
entityType: "provider",
|
|
23724
|
-
entityId: name,
|
|
23725
|
-
diff,
|
|
23726
|
-
createdAt
|
|
23727
|
-
}))
|
|
23728
|
-
).run();
|
|
23729
|
-
}
|
|
23888
|
+
providerSummary.splice(0, providerSummary.length, ...providerSummaries(registry, opts.config));
|
|
23889
|
+
const entry = providerSummary.find((provider) => provider.name === name);
|
|
23890
|
+
opts.db.transaction(() => recordProviderChange(name, previousProvider, registry.get(name), auditContext));
|
|
23730
23891
|
return {
|
|
23731
23892
|
name,
|
|
23732
23893
|
model: entry?.model,
|
|
@@ -23813,15 +23974,10 @@ async function createServer(opts) {
|
|
|
23813
23974
|
app.log.error({ err }, "Failed to save CDP config");
|
|
23814
23975
|
throw err;
|
|
23815
23976
|
}
|
|
23816
|
-
const CDP_DEFAULT_QUOTA = {
|
|
23817
|
-
maxConcurrency: 1,
|
|
23818
|
-
maxRequestsPerMinute: 4,
|
|
23819
|
-
maxRequestsPerDay: 200
|
|
23820
|
-
};
|
|
23821
23977
|
registry.register(cdpChatgptAdapter, {
|
|
23822
23978
|
provider: "cdp:chatgpt",
|
|
23823
23979
|
cdpEndpoint: `ws://${host}:${port2}`,
|
|
23824
|
-
quotaPolicy: opts.config.cdp.quota
|
|
23980
|
+
quotaPolicy: resolveProviderQuotaPolicy(opts.config.cdp.quota, DEFAULT_CDP_QUOTA)
|
|
23825
23981
|
});
|
|
23826
23982
|
},
|
|
23827
23983
|
getCdpStatus: async () => {
|
|
@@ -24168,6 +24324,7 @@ export {
|
|
|
24168
24324
|
detectAndTrackUpgrade,
|
|
24169
24325
|
trackEvent,
|
|
24170
24326
|
setTelemetryPreference,
|
|
24327
|
+
registeredProviderNames,
|
|
24171
24328
|
OPENAI_RETRIEVAL_CONTRACT,
|
|
24172
24329
|
reparseStoredResult2 as reparseStoredResult,
|
|
24173
24330
|
reparseStoredResult3 as reparseStoredResult2,
|