@canonry/canonry 7.98.0 → 7.99.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/aero/references/site-health.md +11 -1
- package/assets/assets/{AgentSetupPage-h1kJhy9q.js → AgentSetupPage-yOqSVJ-e.js} +1 -1
- package/assets/assets/{AuditHistoryPanel-CNUApNiJ.js → AuditHistoryPanel-CiTzFgbw.js} +1 -1
- package/assets/assets/{BacklinksPage-CpKmlAek.js → BacklinksPage-DmRdQsFQ.js} +1 -1
- package/assets/assets/{HistoryPage-DeN79d3c.js → HistoryPage-DNE76LNh.js} +1 -1
- package/assets/assets/{MeasurementPropertyPage-CcHnYn81.js → MeasurementPropertyPage-Cx64Fnmz.js} +1 -1
- package/assets/assets/{ProjectPage-Tmuqmust.js → ProjectPage-DNfgC82V.js} +1 -1
- package/assets/assets/{RunsPage-BD4k3MjX.js → RunsPage-BmPKBKKr.js} +1 -1
- package/assets/assets/{SettingsPage-BHy30JP5.js → SettingsPage-Dl0TNeVK.js} +1 -1
- package/assets/assets/{SiteHealthSection-SYdtuGjq.js → SiteHealthSection-DxQPaR--.js} +2 -2
- package/assets/assets/{ToneBadge-Cii5KNtg.js → ToneBadge-BhjTBHi-.js} +1 -1
- package/assets/assets/{TrafficPage-DuxtRqnc.js → TrafficPage-CJp0pS01.js} +1 -1
- package/assets/assets/{TrafficSourceDetailPage-DiX1bB02.js → TrafficSourceDetailPage-DKE8tSw5.js} +1 -1
- package/assets/assets/{account-context-CdEHcI5S.js → account-context-j3asaevn.js} +1 -1
- package/assets/assets/{agent-trust-copy-N6WacCSg.js → agent-trust-copy--U_DFYQE.js} +1 -1
- package/assets/assets/{card-B1O0SunG.js → card-CQAQ-c3g.js} +1 -1
- package/assets/assets/{format-helpers-V8Pyc7cR.js → format-helpers-BPugHeIX.js} +1 -1
- package/assets/assets/{index-BTz-B9hV.js → index-xlDLvgRC.js} +4 -4
- package/assets/assets/{query-client-C1ycmW1c.js → query-client-CwZpfiiD.js} +1 -1
- package/assets/assets/{react-query-DI7VfOND.js → react-query-CBpNFR4t.js} +1 -1
- package/assets/assets/{react-sigma_core.esm.min-f4uuOgrw.js → react-sigma_core.esm.min-BAq_-iP1.js} +1 -1
- package/assets/assets/{server-traffic-CkSCFMnL.js → server-traffic-jKXLZyeC.js} +1 -1
- package/assets/assets/{service-DSgXWhNq.js → service-DFumlmXt.js} +1 -1
- package/assets/index.html +7 -7
- package/dist/{ads-PQ4OP6OH.js → ads-EYDUTG4U.js} +3 -3
- package/dist/{agent-B6ZPL2HH.js → agent-KTK2IDB3.js} +7 -7
- package/dist/{agent-ask-ZUKNXRAX.js → agent-ask-KPZOH6RY.js} +3 -3
- package/dist/{agent-conversations-DJ7WU7PL.js → agent-conversations-BCWA4JJL.js} +3 -3
- package/dist/{agent-journeys-LGUI3SQG.js → agent-journeys-UBVLQ7JV.js} +3 -3
- package/dist/{agent-memory-PWZJ7G5Q.js → agent-memory-24SYEGEX.js} +3 -3
- package/dist/{agent-providers-TEERDNJT.js → agent-providers-E7EWFE5P.js} +3 -3
- package/dist/{agent-transcript-KDPOPK5Z.js → agent-transcript-QGJ45NW5.js} +3 -3
- package/dist/{analytics-NFW5RQRT.js → analytics-GHVG5CCT.js} +3 -3
- package/dist/{apply-EP4V4UF5.js → apply-7YLB2DPH.js} +3 -3
- package/dist/{backfill-6UTNNVFO.js → backfill-WNQ6O5WP.js} +10 -10
- package/dist/{backlinks-SG5GV5X5.js → backlinks-ZBZR4D27.js} +3 -3
- package/dist/{bing-Q4W24JNC.js → bing-4VBSKPSX.js} +3 -3
- package/dist/{bootstrap-WEMERVNZ.js → bootstrap-7ESCLP2G.js} +10 -10
- package/dist/{cdp-3XKBJGHO.js → cdp-WBL6FZTL.js} +7 -7
- package/dist/{chunk-ER5ZHAB2.js → chunk-2RW5D55K.js} +2 -2
- package/dist/{chunk-BLW327AU.js → chunk-3MFYM2IS.js} +1 -1
- package/dist/{chunk-RLKI2QF7.js → chunk-3MKJ4LP6.js} +6 -6
- package/dist/{chunk-3BA75QYU.js → chunk-3VPBRGPN.js} +1 -1
- package/dist/{chunk-X3UXMHWC.js → chunk-3WDQ5GPO.js} +1 -1
- package/dist/{chunk-ZBTUISPG.js → chunk-47X24IJG.js} +13 -1
- package/dist/{chunk-EJZ444VV.js → chunk-5ZOM5BJS.js} +1 -1
- package/dist/{chunk-3JWY65XO.js → chunk-74V5EPZH.js} +7 -7
- package/dist/{chunk-OL2L5ANJ.js → chunk-AJD252SX.js} +2 -2
- package/dist/{chunk-VMGZBH5L.js → chunk-AY7SDX6G.js} +3 -3
- package/dist/{chunk-2XCYEBQV.js → chunk-C55P75G4.js} +2 -2
- package/dist/{chunk-3GMBHGQ5.js → chunk-DLOWRO5R.js} +3 -3
- package/dist/{chunk-Q5SNOMRB.js → chunk-DMPSA7ZS.js} +4 -4
- package/dist/{chunk-KK3Z34AN.js → chunk-L53ZSQN5.js} +1 -1
- package/dist/{chunk-SAKH2RRF.js → chunk-LM7HTNUO.js} +5 -5
- package/dist/{chunk-FQ3L2TIX.js → chunk-O6RXNSNW.js} +1 -1
- package/dist/{chunk-RTH5JVC3.js → chunk-P764TJKQ.js} +1 -1
- package/dist/{chunk-J6MYB3F4.js → chunk-PT5HNVCM.js} +1 -1
- package/dist/{chunk-TNOYUE2L.js → chunk-RRXLJPBN.js} +5 -5
- package/dist/{chunk-2Q7JEC6N.js → chunk-SY3CWJR4.js} +2 -2
- package/dist/{chunk-QSL3FXE4.js → chunk-TVBOK4P2.js} +4 -4
- package/dist/{chunk-IAGC2MGP.js → chunk-UPHHKO3L.js} +357 -357
- package/dist/{chunk-UXXZXXNG.js → chunk-UY6SKAKW.js} +2 -2
- package/dist/{chunk-YJITDEZR.js → chunk-VADVPARO.js} +4 -4
- package/dist/{chunk-64KB6YTG.js → chunk-VOVT2H25.js} +3 -3
- package/dist/{chunk-64R66MWZ.js → chunk-WA6APXI6.js} +3 -3
- package/dist/{chunk-ZADRESLB.js → chunk-WXAF7AMC.js} +2 -2
- package/dist/{chunk-IX5AC3QB.js → chunk-XPIMVXAI.js} +388 -338
- package/dist/{chunk-RGFIRJ7F.js → chunk-XQP3XHLJ.js} +2 -2
- package/dist/{chunk-52M2JWCB.js → chunk-YACKJGY4.js} +1 -1
- package/dist/{chunk-ISIJ2NS4.js → chunk-YBWATBX4.js} +1 -1
- package/dist/{chunk-CN6MVUY3.js → chunk-YEUULP44.js} +1 -1
- package/dist/{chunk-WZSDGC45.js → chunk-YMHN4UIT.js} +1 -1
- package/dist/{chunk-O22WSFWL.js → chunk-ZGBLNKFN.js} +8 -5
- package/dist/{chunk-MSI4BGFN.js → chunk-ZN2TEBBX.js} +3 -3
- package/dist/{citations-5OSQ3ZYU.js → citations-N5MGNKJN.js} +3 -3
- package/dist/{cli-catalog-T5OAVKGS.js → cli-catalog-FFU7PHW7.js} +3 -3
- package/dist/{cli-commands-VATCVJKW.js → cli-commands-AMIDEUO2.js} +7 -7
- package/dist/cli.js +13 -13
- package/dist/{competitor-YIKTMTHY.js → competitor-GRA2GMZR.js} +3 -3
- package/dist/{content-DS2LKR7J.js → content-YVAPTIV4.js} +3 -3
- package/dist/{dashboard-HLVRRRI4.js → dashboard-33ACBR5A.js} +6 -6
- package/dist/{demo-A5DTVJGK.js → demo-AGXWBMR6.js} +1 -1
- package/dist/{demo-server-P2JGAI6C.js → demo-server-W4YAC7GT.js} +12 -12
- package/dist/{discover-VNZDO2MU.js → discover-BH33TO4Z.js} +3 -3
- package/dist/{discover-eval-ON77LUEN.js → discover-eval-3CEA6DEE.js} +3 -3
- package/dist/{doctor-7TNNTI7H.js → doctor-ZAYTD5PC.js} +3 -3
- package/dist/{evidence-E54CNGXS.js → evidence-247GL7UK.js} +3 -3
- package/dist/{export-cmd-XXZ6KLQQ.js → export-cmd-HLKWFI5Q.js} +3 -3
- package/dist/{ga-ZQB4EJI3.js → ga-BMING57Z.js} +3 -3
- package/dist/{gbp-ZQL6NQEK.js → gbp-MIB5CXSD.js} +3 -3
- package/dist/{google-7EIHQGSV.js → google-5JP6RYAW.js} +4 -4
- package/dist/{google-marketing-RSAO4PG7.js → google-marketing-25OBMPS6.js} +1 -1
- package/dist/{health-cmd-R7WPKGWX.js → health-cmd-D5XSEL7H.js} +3 -3
- package/dist/{history-2CSWSVCY.js → history-6MDRFPQ3.js} +3 -3
- package/dist/index.d.ts +7 -7
- package/dist/index.js +25 -25
- package/dist/{init-FXZRMXZ3.js → init-RDR75N2Y.js} +12 -12
- package/dist/{insights-EJUL3W6D.js → insights-QZVMBGVM.js} +3 -3
- package/dist/intelligence-service-KQE7RO5I.js +16 -0
- package/dist/{journeys-YUMBANFH.js → journeys-TABTZDFZ.js} +3 -3
- package/dist/{keys-KLOTGT4H.js → keys-SL5YF7NQ.js} +3 -3
- package/dist/{keyword-5NSJFEOI.js → keyword-OCDEXRSW.js} +3 -3
- package/dist/{logs-WW3RLCRG.js → logs-CSC7O364.js} +3 -3
- package/dist/{mcp-NTBEIEVX.js → mcp-P725OHAJ.js} +4 -4
- package/dist/mcp.js +9 -9
- package/dist/{measurement-plan-BLPAZEXS.js → measurement-plan-Z42GBJBO.js} +5 -5
- package/dist/{notify-KAGE6UEN.js → notify-E5JBES4I.js} +3 -3
- package/dist/{organic-evidence-HTYRXRML.js → organic-evidence-CUA3T64O.js} +3 -3
- package/dist/{overview-GSJ4GP2T.js → overview-UKQ52I72.js} +4 -4
- package/dist/{project-NYUWICVC.js → project-2ULM64TV.js} +3 -3
- package/dist/{query-3CRRDOHD.js → query-Y47IEFKJ.js} +3 -3
- package/dist/{research-ZULVD3TW.js → research-DPFQZ3TE.js} +3 -3
- package/dist/{results-export-STM7SQ4J.js → results-export-KBHOBQAH.js} +3 -3
- package/dist/{run-S4MFU4MK.js → run-4CYHVIIU.js} +4 -4
- package/dist/{schedule-6W3Q2FUL.js → schedule-GY4P3RON.js} +3 -3
- package/dist/{search-EKXXN7C4.js → search-AGVNZMJQ.js} +3 -3
- package/dist/{serve-TFBFPFAQ.js → serve-5LFKIXNL.js} +26 -26
- package/dist/{session-4B5V3AH6.js → session-YAYEDM3P.js} +13 -13
- package/dist/{settings-VRTDPFR3.js → settings-HUKMWRHY.js} +3 -3
- package/dist/{sitemaps-R7XFCH6E.js → sitemaps-APTY7U6H.js} +3 -3
- package/dist/{skills-44XYCTVD.js → skills-4BUONEIZ.js} +2 -2
- package/dist/{snapshot-ROAOJI4W.js → snapshot-BXCWSF5P.js} +3 -3
- package/dist/{src-ZJRIKEDB.js → src-R7XZUX3D.js} +2 -2
- package/dist/{status-EN3A2TX5.js → status-SB6HPBYJ.js} +4 -4
- package/dist/{technical-aeo-ZV466F65.js → technical-aeo-G7UEMWZD.js} +3 -3
- package/dist/{telemetry-EZHPNZ2A.js → telemetry-H4S4Y7YV.js} +4 -4
- package/dist/{tools-IH4M7AAG.js → tools-46XX6I4R.js} +11 -11
- package/dist/{traffic-VFLXWB3Z.js → traffic-I6WRJN3W.js} +5 -5
- package/dist/{trends-ZXJIX3Z3.js → trends-AQK3GLWF.js} +3 -3
- package/dist/{users-J2BYEWOE.js → users-SNTVY6OB.js} +3 -3
- package/dist/{visibility-stats-AJ7NY2BP.js → visibility-stats-44QAD2XS.js} +3 -3
- package/dist/{wordpress-U6MS35VE.js → wordpress-N7LTOW46.js} +3 -3
- package/package.json +2 -2
- package/dist/intelligence-service-TKK7O3Z5.js +0 -16
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
answerServiceAdapters,
|
|
3
3
|
answerServiceDescriptors
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-5ZOM5BJS.js";
|
|
5
5
|
import {
|
|
6
6
|
AeroLlmUsageFeatures,
|
|
7
7
|
aeroTurnStatus,
|
|
@@ -27,14 +27,14 @@ import {
|
|
|
27
27
|
resolveConfiguredAgentProvider,
|
|
28
28
|
resolveSessionProviderAndModel,
|
|
29
29
|
setAeroSystemPrompt
|
|
30
|
-
} from "./chunk-
|
|
30
|
+
} from "./chunk-3MKJ4LP6.js";
|
|
31
31
|
import {
|
|
32
32
|
AeroToolProfiles,
|
|
33
33
|
AeroToolScopes,
|
|
34
34
|
buildAeroStateTools,
|
|
35
35
|
isAeroToolProfile,
|
|
36
36
|
truncateToolResult
|
|
37
|
-
} from "./chunk-
|
|
37
|
+
} from "./chunk-DMPSA7ZS.js";
|
|
38
38
|
import {
|
|
39
39
|
getGoogleAuthConfig,
|
|
40
40
|
getGoogleConnection,
|
|
@@ -53,7 +53,7 @@ import {
|
|
|
53
53
|
removeCloudflareTrafficConnection,
|
|
54
54
|
removeCloudflareTrafficConnectionBySourceId,
|
|
55
55
|
upsertCloudflareTrafficConnection
|
|
56
|
-
} from "./chunk-
|
|
56
|
+
} from "./chunk-PT5HNVCM.js";
|
|
57
57
|
import {
|
|
58
58
|
claudeAdapter,
|
|
59
59
|
embedQueries,
|
|
@@ -65,7 +65,7 @@ import {
|
|
|
65
65
|
openrouterAdapter,
|
|
66
66
|
perplexityAdapter,
|
|
67
67
|
startTrafficReclassification
|
|
68
|
-
} from "./chunk-
|
|
68
|
+
} from "./chunk-RRXLJPBN.js";
|
|
69
69
|
import {
|
|
70
70
|
classifyUsageSurface,
|
|
71
71
|
createApiUsageTelemetry,
|
|
@@ -77,7 +77,7 @@ import {
|
|
|
77
77
|
startOutcomeTimer,
|
|
78
78
|
trackFeatureCompleted,
|
|
79
79
|
trackInstallState
|
|
80
|
-
} from "./chunk-
|
|
80
|
+
} from "./chunk-VADVPARO.js";
|
|
81
81
|
import {
|
|
82
82
|
isLoopbackBindHost
|
|
83
83
|
} from "./chunk-TSENU4GG.js";
|
|
@@ -85,7 +85,7 @@ import {
|
|
|
85
85
|
isApiProviderRegistrable,
|
|
86
86
|
isCdpProviderRegistrable,
|
|
87
87
|
registeredProviderNames
|
|
88
|
-
} from "./chunk-
|
|
88
|
+
} from "./chunk-O6RXNSNW.js";
|
|
89
89
|
import {
|
|
90
90
|
checkLatestVersionForServer,
|
|
91
91
|
getOrCreateAnonymousId,
|
|
@@ -97,17 +97,17 @@ import {
|
|
|
97
97
|
recordDashboardEvent,
|
|
98
98
|
setTelemetryPreference,
|
|
99
99
|
trackEvent
|
|
100
|
-
} from "./chunk-
|
|
100
|
+
} from "./chunk-C55P75G4.js";
|
|
101
101
|
import {
|
|
102
102
|
createCanonryMcpServer
|
|
103
|
-
} from "./chunk-
|
|
103
|
+
} from "./chunk-WA6APXI6.js";
|
|
104
104
|
import {
|
|
105
105
|
CANONRY_MCP_TIERS,
|
|
106
106
|
CANONRY_MCP_TOOLKIT_NAMES
|
|
107
107
|
} from "./chunk-MTW37GXF.js";
|
|
108
108
|
import {
|
|
109
109
|
ApiClient
|
|
110
|
-
} from "./chunk-
|
|
110
|
+
} from "./chunk-AJD252SX.js";
|
|
111
111
|
import {
|
|
112
112
|
AGENT_PROVIDERS,
|
|
113
113
|
adapterSupportsBatch,
|
|
@@ -139,21 +139,21 @@ import {
|
|
|
139
139
|
saveConfigPatch,
|
|
140
140
|
sentimentInstallReadiness,
|
|
141
141
|
updateSharedProviderExecutionGate
|
|
142
|
-
} from "./chunk-
|
|
142
|
+
} from "./chunk-3MFYM2IS.js";
|
|
143
143
|
import {
|
|
144
144
|
getBundledSkillSnapshots
|
|
145
|
-
} from "./chunk-
|
|
145
|
+
} from "./chunk-P764TJKQ.js";
|
|
146
146
|
import {
|
|
147
147
|
computeFactorAverages,
|
|
148
148
|
deleteSiteCrawlGraphLayout,
|
|
149
149
|
persistSiteCrawlGraphLayout,
|
|
150
150
|
prepareSiteCrawlGraphLayout,
|
|
151
151
|
recordedSharePct
|
|
152
|
-
} from "./chunk-
|
|
152
|
+
} from "./chunk-SY3CWJR4.js";
|
|
153
153
|
import {
|
|
154
154
|
CanonryMcpToolNames,
|
|
155
155
|
canonryMcpTools
|
|
156
|
-
} from "./chunk-
|
|
156
|
+
} from "./chunk-DLOWRO5R.js";
|
|
157
157
|
import {
|
|
158
158
|
PACKAGE_VERSION
|
|
159
159
|
} from "./chunk-AOJU5LKX.js";
|
|
@@ -162,16 +162,16 @@ import {
|
|
|
162
162
|
} from "./chunk-HGMYEDAQ.js";
|
|
163
163
|
import {
|
|
164
164
|
IntelligenceService
|
|
165
|
-
} from "./chunk-
|
|
165
|
+
} from "./chunk-LM7HTNUO.js";
|
|
166
166
|
import {
|
|
167
167
|
computeCitedCompetitorDomains,
|
|
168
168
|
determineCitationState
|
|
169
|
-
} from "./chunk-
|
|
169
|
+
} from "./chunk-YBWATBX4.js";
|
|
170
170
|
import {
|
|
171
171
|
addLogListener,
|
|
172
172
|
createFastifyLogger,
|
|
173
173
|
createLogger
|
|
174
|
-
} from "./chunk-
|
|
174
|
+
} from "./chunk-UY6SKAKW.js";
|
|
175
175
|
import {
|
|
176
176
|
BingApiError,
|
|
177
177
|
CC_CACHE_DIR,
|
|
@@ -316,7 +316,7 @@ import {
|
|
|
316
316
|
webhookResponseReason,
|
|
317
317
|
webhookTargetRefusalReason,
|
|
318
318
|
writeAuditLog
|
|
319
|
-
} from "./chunk-
|
|
319
|
+
} from "./chunk-ZGBLNKFN.js";
|
|
320
320
|
import {
|
|
321
321
|
OperationalLogStore,
|
|
322
322
|
SentimentIdempotencyConflict,
|
|
@@ -414,7 +414,7 @@ import {
|
|
|
414
414
|
unrecognizedAgentEventsHourly,
|
|
415
415
|
unrecognizedAgentNotifyState,
|
|
416
416
|
usageCounters
|
|
417
|
-
} from "./chunk-
|
|
417
|
+
} from "./chunk-L53ZSQN5.js";
|
|
418
418
|
import {
|
|
419
419
|
AGENT_MEMORY_VALUE_MAX_BYTES,
|
|
420
420
|
AI_ENGINE_DOMAINS,
|
|
@@ -629,6 +629,7 @@ import {
|
|
|
629
629
|
parseMeasurementRunManifestV1,
|
|
630
630
|
parseOriginList,
|
|
631
631
|
parseRunError,
|
|
632
|
+
parseStoredMeasurementPlanAnyVersion,
|
|
632
633
|
percentOf,
|
|
633
634
|
providerConnectionName,
|
|
634
635
|
providerError,
|
|
@@ -674,7 +675,7 @@ import {
|
|
|
674
675
|
winnabilityClassLabel,
|
|
675
676
|
withRetry,
|
|
676
677
|
withSkippedProviders
|
|
677
|
-
} from "./chunk-
|
|
678
|
+
} from "./chunk-47X24IJG.js";
|
|
678
679
|
|
|
679
680
|
// src/google-marketing-runtime.ts
|
|
680
681
|
import crypto2 from "crypto";
|
|
@@ -3364,7 +3365,7 @@ import path5 from "path";
|
|
|
3364
3365
|
import { isIP as isIP2 } from "net";
|
|
3365
3366
|
import { fileURLToPath } from "url";
|
|
3366
3367
|
import { setImmediate as yieldToEventLoop4 } from "timers/promises";
|
|
3367
|
-
import { and as
|
|
3368
|
+
import { and as and31, eq as eq37, isNotNull as isNotNull5, isNull as isNull8, lte as lte3, max, ne as ne4, or as or4, sql as sql15 } from "drizzle-orm";
|
|
3368
3369
|
|
|
3369
3370
|
// src/seo-runtime.ts
|
|
3370
3371
|
import { eq, inArray } from "drizzle-orm";
|
|
@@ -6002,7 +6003,7 @@ function nonBlank(value) {
|
|
|
6002
6003
|
return trimmed ? trimmed : void 0;
|
|
6003
6004
|
}
|
|
6004
6005
|
function resolveBuildCommit(env = process.env) {
|
|
6005
|
-
const embedded = true ? "
|
|
6006
|
+
const embedded = true ? "74e5d2930f44211335b0da42dd2200f8ac58ae90" : void 0;
|
|
6006
6007
|
return nonBlank(embedded) ?? nonBlank(env.CANONRY_COMMIT);
|
|
6007
6008
|
}
|
|
6008
6009
|
function resolveInstanceIdentity(env = process.env) {
|
|
@@ -12234,7 +12235,7 @@ function buildDiscoveryInsightTitle(input) {
|
|
|
12234
12235
|
|
|
12235
12236
|
// src/execute-site-audit.ts
|
|
12236
12237
|
import crypto20 from "crypto";
|
|
12237
|
-
import { and as
|
|
12238
|
+
import { and as and16, eq as eq20, ne as ne2, sql as sql7 } from "drizzle-orm";
|
|
12238
12239
|
import { runSiteCrawl } from "@canonry/agent-readiness";
|
|
12239
12240
|
|
|
12240
12241
|
// src/site-audit-root.ts
|
|
@@ -12545,6 +12546,45 @@ function buildSiteCrawlIssueEvidence(siteIssues, pages) {
|
|
|
12545
12546
|
return { schemaVersion: "1.0", groupIdentityVersion: GROUP_IDENTITY_VERSION, siteIssues: groups };
|
|
12546
12547
|
}
|
|
12547
12548
|
|
|
12549
|
+
// src/site-audit-entity-registry.ts
|
|
12550
|
+
import { and as and15, eq as eq19 } from "drizzle-orm";
|
|
12551
|
+
function entityMatcher(matcher) {
|
|
12552
|
+
switch (matcher.kind) {
|
|
12553
|
+
case "prefix":
|
|
12554
|
+
return { host: matcher.host, pathPrefix: matcher.pathPrefix };
|
|
12555
|
+
case "host":
|
|
12556
|
+
return { host: matcher.host, pathPrefix: "/" };
|
|
12557
|
+
case "exact": {
|
|
12558
|
+
try {
|
|
12559
|
+
const url = new URL(matcher.url);
|
|
12560
|
+
return { host: url.hostname, pathPrefix: url.pathname };
|
|
12561
|
+
} catch {
|
|
12562
|
+
return null;
|
|
12563
|
+
}
|
|
12564
|
+
}
|
|
12565
|
+
}
|
|
12566
|
+
}
|
|
12567
|
+
function planEntityRegistry(plan) {
|
|
12568
|
+
if (plan.targets.length === 0) return null;
|
|
12569
|
+
return {
|
|
12570
|
+
entities: plan.targets.map((target) => ({
|
|
12571
|
+
key: target.stableKey,
|
|
12572
|
+
names: [.../* @__PURE__ */ new Set([target.label, ...target.aliases, ...target.identityAliases ?? []])],
|
|
12573
|
+
urlMatchers: target.urlMatchers.map(entityMatcher).filter((matcher) => matcher !== null)
|
|
12574
|
+
}))
|
|
12575
|
+
};
|
|
12576
|
+
}
|
|
12577
|
+
function activePlanEntityRegistry(db, projectId) {
|
|
12578
|
+
const pointer = db.select({ versionId: measurementPlans.activeVersionId }).from(measurementPlans).where(eq19(measurementPlans.projectId, projectId)).get();
|
|
12579
|
+
if (!pointer) return null;
|
|
12580
|
+
const version = db.select({ revision: measurementPlanVersions.revision, canonicalJson: measurementPlanVersions.canonicalJson }).from(measurementPlanVersions).where(and15(eq19(measurementPlanVersions.projectId, projectId), eq19(measurementPlanVersions.id, pointer.versionId))).get();
|
|
12581
|
+
if (!version) return null;
|
|
12582
|
+
const plan = parseStoredMeasurementPlanAnyVersion(version.canonicalJson);
|
|
12583
|
+
if (plan.schemaVersion !== MEASUREMENT_PLAN_V2_SCHEMA_VERSION) return null;
|
|
12584
|
+
const registry = planEntityRegistry(plan);
|
|
12585
|
+
return registry ? { planRevision: version.revision, registry } : null;
|
|
12586
|
+
}
|
|
12587
|
+
|
|
12548
12588
|
// src/execute-site-audit.ts
|
|
12549
12589
|
var log15 = createLogger("SiteAudit");
|
|
12550
12590
|
var SiteAuditCancelledError = class extends Error {
|
|
@@ -12698,7 +12738,7 @@ function trackSiteAuditOutcome(db, input) {
|
|
|
12698
12738
|
);
|
|
12699
12739
|
const pagesAudited = input.crawl?.pagesAudited ?? 0;
|
|
12700
12740
|
if (pagesAudited === 0 || input.trigger === RunTriggers.probe) return;
|
|
12701
|
-
const prior = db.select({ id: siteAuditSnapshots.id }).from(siteAuditSnapshots).where(
|
|
12741
|
+
const prior = db.select({ id: siteAuditSnapshots.id }).from(siteAuditSnapshots).where(and16(eq20(siteAuditSnapshots.projectId, input.projectId), ne2(siteAuditSnapshots.runId, input.runId))).limit(1).get();
|
|
12702
12742
|
if (prior) return;
|
|
12703
12743
|
trackEvent("activation.completed", {
|
|
12704
12744
|
flowVersion: ONBOARDING_FLOW_VERSION,
|
|
@@ -12712,12 +12752,12 @@ function trackSiteAuditOutcome(db, input) {
|
|
|
12712
12752
|
}
|
|
12713
12753
|
async function executeSiteAudit(db, runId, projectId, opts = {}) {
|
|
12714
12754
|
const startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
12715
|
-
const claim = db.update(runs).set({ status: "running", startedAt }).where(
|
|
12755
|
+
const claim = db.update(runs).set({ status: "running", startedAt }).where(and16(eq20(runs.id, runId), eq20(runs.projectId, projectId), eq20(runs.status, "queued"))).run();
|
|
12716
12756
|
if (claim.changes === 0) return;
|
|
12717
12757
|
const startTime = Date.now();
|
|
12718
|
-
const trigger = db.select({ trigger: runs.trigger }).from(runs).where(
|
|
12758
|
+
const trigger = db.select({ trigger: runs.trigger }).from(runs).where(eq20(runs.id, runId)).get()?.trigger ?? null;
|
|
12719
12759
|
let canonicalDomain = null;
|
|
12720
|
-
const attemptNumber = (db.select({ value: sql7`coalesce(max(${siteCrawlAttempts.attemptNumber}), 0)` }).from(siteCrawlAttempts).where(
|
|
12760
|
+
const attemptNumber = (db.select({ value: sql7`coalesce(max(${siteCrawlAttempts.attemptNumber}), 0)` }).from(siteCrawlAttempts).where(eq20(siteCrawlAttempts.runId, runId)).get()?.value ?? 0) + 1;
|
|
12721
12761
|
const attemptId = crypto20.randomUUID();
|
|
12722
12762
|
db.insert(siteCrawlAttempts).values({
|
|
12723
12763
|
id: attemptId,
|
|
@@ -12752,22 +12792,22 @@ async function executeSiteAudit(db, runId, projectId, opts = {}) {
|
|
|
12752
12792
|
const nodeKey = nodeKeyByUrl.get(url);
|
|
12753
12793
|
if (!nodeKey) continue;
|
|
12754
12794
|
const scope = [
|
|
12755
|
-
|
|
12756
|
-
|
|
12757
|
-
|
|
12795
|
+
eq20(siteCrawlEdges.projectId, projectId),
|
|
12796
|
+
eq20(siteCrawlEdges.runId, runId),
|
|
12797
|
+
eq20(siteCrawlEdges.attemptId, attemptId)
|
|
12758
12798
|
];
|
|
12759
|
-
tx.update(siteCrawlEdges).set({ sourceNodeKey: nodeKey, updatedAt: now }).where(
|
|
12799
|
+
tx.update(siteCrawlEdges).set({ sourceNodeKey: nodeKey, updatedAt: now }).where(and16(
|
|
12760
12800
|
...scope,
|
|
12761
|
-
|
|
12801
|
+
eq20(siteCrawlEdges.sourceUrl, url)
|
|
12762
12802
|
)).run();
|
|
12763
|
-
tx.update(siteCrawlEdges).set({ targetNodeKey: nodeKey, updatedAt: now }).where(
|
|
12803
|
+
tx.update(siteCrawlEdges).set({ targetNodeKey: nodeKey, updatedAt: now }).where(and16(
|
|
12764
12804
|
...scope,
|
|
12765
|
-
|
|
12805
|
+
eq20(siteCrawlEdges.targetUrl, url)
|
|
12766
12806
|
)).run();
|
|
12767
12807
|
const pageScope = [
|
|
12768
|
-
|
|
12769
|
-
|
|
12770
|
-
|
|
12808
|
+
eq20(siteCrawlPages.projectId, projectId),
|
|
12809
|
+
eq20(siteCrawlPages.runId, runId),
|
|
12810
|
+
eq20(siteCrawlPages.attemptId, attemptId)
|
|
12771
12811
|
];
|
|
12772
12812
|
const canonicalTargets = tx.select({
|
|
12773
12813
|
id: siteCrawlPages.id,
|
|
@@ -12775,13 +12815,13 @@ async function executeSiteAudit(db, runId, projectId, opts = {}) {
|
|
|
12775
12815
|
fetchState: siteCrawlPages.fetchState,
|
|
12776
12816
|
indexabilityState: siteCrawlPages.indexabilityState,
|
|
12777
12817
|
indexabilityReasons: siteCrawlPages.indexabilityReasons
|
|
12778
|
-
}).from(siteCrawlPages).where(
|
|
12818
|
+
}).from(siteCrawlPages).where(and16(...pageScope, eq20(siteCrawlPages.canonicalUrl, url))).all();
|
|
12779
12819
|
for (const target of canonicalTargets) {
|
|
12780
12820
|
tx.update(siteCrawlPages).set({
|
|
12781
12821
|
canonicalNodeKey: nodeKey,
|
|
12782
12822
|
healthState: deriveSiteHealthState({ ...target, canonicalNodeKey: nodeKey }),
|
|
12783
12823
|
updatedAt: now
|
|
12784
|
-
}).where(
|
|
12824
|
+
}).where(eq20(siteCrawlPages.id, target.id)).run();
|
|
12785
12825
|
}
|
|
12786
12826
|
}
|
|
12787
12827
|
};
|
|
@@ -12940,20 +12980,20 @@ async function executeSiteAudit(db, runId, projectId, opts = {}) {
|
|
|
12940
12980
|
linkScoreRaw: metrics.linkScoreRaw,
|
|
12941
12981
|
linkScoreNormalized: metrics.linkScore,
|
|
12942
12982
|
updatedAt: now
|
|
12943
|
-
}).where(
|
|
12944
|
-
|
|
12945
|
-
|
|
12946
|
-
|
|
12947
|
-
|
|
12983
|
+
}).where(and16(
|
|
12984
|
+
eq20(siteCrawlPages.projectId, projectId),
|
|
12985
|
+
eq20(siteCrawlPages.runId, runId),
|
|
12986
|
+
eq20(siteCrawlPages.attemptId, attemptId),
|
|
12987
|
+
eq20(siteCrawlPages.nodeKey, key)
|
|
12948
12988
|
)).run();
|
|
12949
12989
|
};
|
|
12950
12990
|
const persistEvent = async (event) => {
|
|
12951
12991
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
12952
12992
|
db.transaction((tx) => {
|
|
12953
|
-
const receipt = tx.select({ checksum: siteCrawlEventReceipts.checksum }).from(siteCrawlEventReceipts).where(
|
|
12954
|
-
|
|
12955
|
-
|
|
12956
|
-
|
|
12993
|
+
const receipt = tx.select({ checksum: siteCrawlEventReceipts.checksum }).from(siteCrawlEventReceipts).where(and16(
|
|
12994
|
+
eq20(siteCrawlEventReceipts.attemptId, attemptId),
|
|
12995
|
+
eq20(siteCrawlEventReceipts.sequence, event.sequence),
|
|
12996
|
+
eq20(siteCrawlEventReceipts.batchId, event.batchId)
|
|
12957
12997
|
)).get();
|
|
12958
12998
|
if (receipt) {
|
|
12959
12999
|
if (receipt.checksum !== event.checksum) {
|
|
@@ -12988,18 +13028,18 @@ async function executeSiteAudit(db, runId, projectId, opts = {}) {
|
|
|
12988
13028
|
urlVersionsSkipped: event.type === "summary" ? event.summary.urlVersions?.skipped ?? 0 : sql7`${siteCrawlAttempts.urlVersionsSkipped}`,
|
|
12989
13029
|
edgesDiscovered: progress.edgesObserved,
|
|
12990
13030
|
updatedAt: now
|
|
12991
|
-
}).where(
|
|
13031
|
+
}).where(eq20(siteCrawlAttempts.id, attemptId)).run();
|
|
12992
13032
|
} else {
|
|
12993
13033
|
tx.update(siteCrawlAttempts).set({
|
|
12994
13034
|
lastEventSequence: event.sequence,
|
|
12995
13035
|
lastEventChecksum: event.checksum,
|
|
12996
13036
|
updatedAt: now
|
|
12997
|
-
}).where(
|
|
13037
|
+
}).where(eq20(siteCrawlAttempts.id, attemptId)).run();
|
|
12998
13038
|
}
|
|
12999
13039
|
});
|
|
13000
13040
|
};
|
|
13001
13041
|
try {
|
|
13002
|
-
const project = db.select().from(projects).where(
|
|
13042
|
+
const project = db.select().from(projects).where(eq20(projects.id, projectId)).get();
|
|
13003
13043
|
if (!project) throw new Error(`Project not found: ${projectId}`);
|
|
13004
13044
|
canonicalDomain = project.canonicalDomain;
|
|
13005
13045
|
const homepageUrl = toHomepageUrl(project.canonicalDomain);
|
|
@@ -13011,6 +13051,12 @@ async function executeSiteAudit(db, runId, projectId, opts = {}) {
|
|
|
13011
13051
|
});
|
|
13012
13052
|
const crawlRootUrl = resolvedRoot.effectiveUrl;
|
|
13013
13053
|
if (opts.sitemapUrl) await assertSiteAuditUrlAllowed(opts.sitemapUrl, "sitemapUrl");
|
|
13054
|
+
let entities = null;
|
|
13055
|
+
try {
|
|
13056
|
+
entities = activePlanEntityRegistry(db, projectId);
|
|
13057
|
+
} catch (error) {
|
|
13058
|
+
log15.warn("entity-registry.unavailable", { runId, projectId, error: describeError(error) });
|
|
13059
|
+
}
|
|
13014
13060
|
log15.info("start", {
|
|
13015
13061
|
runId,
|
|
13016
13062
|
projectId,
|
|
@@ -13019,7 +13065,8 @@ async function executeSiteAudit(db, runId, projectId, opts = {}) {
|
|
|
13019
13065
|
redirects: resolvedRoot.redirects.length,
|
|
13020
13066
|
maxPages,
|
|
13021
13067
|
maxEdges,
|
|
13022
|
-
maxDepth: opts.maxDepth ?? null
|
|
13068
|
+
maxDepth: opts.maxDepth ?? null,
|
|
13069
|
+
entityPlanRevision: entities?.planRevision ?? null
|
|
13023
13070
|
});
|
|
13024
13071
|
const report = await runSiteCrawl(crawlRootUrl, {
|
|
13025
13072
|
mode: "summary",
|
|
@@ -13031,6 +13078,7 @@ async function executeSiteAudit(db, runId, projectId, opts = {}) {
|
|
|
13031
13078
|
includeGeo: opts.includeGeo ?? false,
|
|
13032
13079
|
includeAgentSkills: opts.includeAgentSkills ?? true,
|
|
13033
13080
|
skipUrlVersionsAfter: SITE_AUDIT_SKIP_URL_VERSIONS_AFTER,
|
|
13081
|
+
...entities ? { entityRegistry: entities.registry } : {},
|
|
13034
13082
|
signal: opts.signal,
|
|
13035
13083
|
onEvent: persistEvent
|
|
13036
13084
|
});
|
|
@@ -13103,7 +13151,7 @@ async function executeSiteAudit(db, runId, projectId, opts = {}) {
|
|
|
13103
13151
|
}
|
|
13104
13152
|
}
|
|
13105
13153
|
const published = db.transaction((tx) => {
|
|
13106
|
-
const claim2 = tx.update(runs).set({ status: terminalStatus, finishedAt }).where(
|
|
13154
|
+
const claim2 = tx.update(runs).set({ status: terminalStatus, finishedAt }).where(and16(eq20(runs.id, runId), eq20(runs.status, "running"))).run();
|
|
13107
13155
|
if (claim2.changes === 0) return false;
|
|
13108
13156
|
if (aggregateScore !== null) {
|
|
13109
13157
|
tx.insert(siteAuditSnapshots).values({
|
|
@@ -13159,7 +13207,9 @@ async function executeSiteAudit(db, runId, projectId, opts = {}) {
|
|
|
13159
13207
|
maxDepth: opts.maxDepth ?? null,
|
|
13160
13208
|
checkDeadLinks: opts.checkDeadLinks ?? false,
|
|
13161
13209
|
includeGeo: opts.includeGeo ?? false,
|
|
13162
|
-
includeAgentSkills: opts.includeAgentSkills ?? true
|
|
13210
|
+
includeAgentSkills: opts.includeAgentSkills ?? true,
|
|
13211
|
+
// The plan revision whose Properties pages were checked against; null when none were.
|
|
13212
|
+
entityPlanRevision: entities?.planRevision ?? null
|
|
13163
13213
|
},
|
|
13164
13214
|
pageBudget: maxPages,
|
|
13165
13215
|
edgeBudget: maxEdges ?? null,
|
|
@@ -13213,7 +13263,7 @@ async function executeSiteAudit(db, runId, projectId, opts = {}) {
|
|
|
13213
13263
|
edgesDiscovered: crawlSummary.edgesObserved,
|
|
13214
13264
|
finishedAt,
|
|
13215
13265
|
updatedAt: finishedAt
|
|
13216
|
-
}).where(
|
|
13266
|
+
}).where(eq20(siteCrawlAttempts.id, attemptId)).run();
|
|
13217
13267
|
return true;
|
|
13218
13268
|
});
|
|
13219
13269
|
if (!published) {
|
|
@@ -13226,7 +13276,7 @@ async function executeSiteAudit(db, runId, projectId, opts = {}) {
|
|
|
13226
13276
|
error: describeError(cleanupError)
|
|
13227
13277
|
});
|
|
13228
13278
|
}
|
|
13229
|
-
const current = db.select({ status: runs.status }).from(runs).where(
|
|
13279
|
+
const current = db.select({ status: runs.status }).from(runs).where(eq20(runs.id, runId)).get();
|
|
13230
13280
|
if (current?.status === "cancelled") throw new SiteAuditCancelledError(`Site audit cancelled before publication: ${runId}`);
|
|
13231
13281
|
throw new Error(`Site audit run was no longer running before publication: ${runId}`);
|
|
13232
13282
|
}
|
|
@@ -13274,12 +13324,12 @@ async function executeSiteAudit(db, runId, projectId, opts = {}) {
|
|
|
13274
13324
|
error: message,
|
|
13275
13325
|
finishedAt,
|
|
13276
13326
|
updatedAt: finishedAt
|
|
13277
|
-
}).where(
|
|
13327
|
+
}).where(eq20(siteCrawlAttempts.id, attemptId)).run();
|
|
13278
13328
|
tx.update(runs).set({
|
|
13279
13329
|
status: cancelled ? "cancelled" : "failed",
|
|
13280
13330
|
error: message,
|
|
13281
13331
|
finishedAt
|
|
13282
|
-
}).where(
|
|
13332
|
+
}).where(and16(eq20(runs.id, runId), eq20(runs.status, "running"))).run();
|
|
13283
13333
|
});
|
|
13284
13334
|
log15.error("failed", { runId, projectId, cancelled, error: message });
|
|
13285
13335
|
trackSiteAuditOutcome(db, {
|
|
@@ -13477,7 +13527,7 @@ function derivedRouteAdapter(adapter, route) {
|
|
|
13477
13527
|
}
|
|
13478
13528
|
|
|
13479
13529
|
// src/install-state-telemetry.ts
|
|
13480
|
-
import { and as
|
|
13530
|
+
import { and as and17, count as count4, eq as eq21, gte as gte2, inArray as inArray8, isNotNull as isNotNull2, isNull as isNull2, ne as ne3, sql as sql8 } from "drizzle-orm";
|
|
13481
13531
|
var log17 = createLogger("InstallStateTelemetry");
|
|
13482
13532
|
var INSTALL_STATE_FIRST_DELAY_MS = 5 * 6e4;
|
|
13483
13533
|
var INSTALL_STATE_INTERVAL_MS = 24 * 60 * 6e4;
|
|
@@ -13502,8 +13552,8 @@ function buildInstallState(sources, now, startedAt) {
|
|
|
13502
13552
|
const total = (query) => whole(query.get()?.value);
|
|
13503
13553
|
const googleByType = new Map(db.select({ type: googleConnections.connectionType, value: count4() }).from(googleConnections).groupBy(googleConnections.connectionType).all().map((row) => [row.type, whole(row.value)]));
|
|
13504
13554
|
const gaServiceAccounts = total(db.select({ value: count4() }).from(gaConnections));
|
|
13505
|
-
const trafficByType = db.select({ type: trafficSources.sourceType, value: count4() }).from(trafficSources).where(
|
|
13506
|
-
const webhookSources = db.select({ config: notifications.config }).from(notifications).where(
|
|
13555
|
+
const trafficByType = db.select({ type: trafficSources.sourceType, value: count4() }).from(trafficSources).where(and17(isNull2(trafficSources.archivedAt), ne3(trafficSources.status, TrafficSourceStatuses.archived))).groupBy(trafficSources.sourceType).all();
|
|
13556
|
+
const webhookSources = db.select({ config: notifications.config }).from(notifications).where(eq21(notifications.enabled, true)).all().map((row) => row.config.source);
|
|
13507
13557
|
const agentWebhooks = webhookSources.filter((source) => source === AGENT_WEBHOOK_SOURCE).length;
|
|
13508
13558
|
const webhooks = webhookSources.length - agentWebhooks;
|
|
13509
13559
|
const bing = total(db.select({ value: count4() }).from(bingConnections));
|
|
@@ -13517,7 +13567,7 @@ function buildInstallState(sources, now, startedAt) {
|
|
|
13517
13567
|
queries: total(db.select({ value: count4() }).from(queries)),
|
|
13518
13568
|
competitors: total(db.select({ value: count4() }).from(competitors)),
|
|
13519
13569
|
locations: total(db.select({ value: sql8`coalesce(sum(json_array_length(${projects.locations})), 0)` }).from(projects)),
|
|
13520
|
-
schedules: total(db.select({ value: count4() }).from(schedules).where(
|
|
13570
|
+
schedules: total(db.select({ value: count4() }).from(schedules).where(eq21(schedules.enabled, true))),
|
|
13521
13571
|
webhooks,
|
|
13522
13572
|
agentWebhooks,
|
|
13523
13573
|
trafficSources: trafficByType.reduce((sum, row) => sum + whole(row.value), 0),
|
|
@@ -13561,14 +13611,14 @@ function buildInstallState(sources, now, startedAt) {
|
|
|
13561
13611
|
costMicros: sql8`coalesce(sum(${usagePath("estimatedCostMicros")}), 0)`
|
|
13562
13612
|
}).from(querySnapshots).where(gte2(querySnapshots.createdAt, since)).groupBy(sql8`+${querySnapshots.provider}`).all();
|
|
13563
13613
|
for (const row of answers) addCalls(row.provider, whole(row.answers));
|
|
13564
|
-
for (const row of db.select({ provider: researchRuns.provider, answered: sql8`coalesce(sum(${researchRuns.completedQueries}), 0)` }).from(researchRuns).where(
|
|
13614
|
+
for (const row of db.select({ provider: researchRuns.provider, answered: sql8`coalesce(sum(${researchRuns.completedQueries}), 0)` }).from(researchRuns).where(and17(inArray8(researchRuns.projectId, projectIds), gte2(researchRuns.createdAt, since))).groupBy(researchRuns.provider).all()) {
|
|
13565
13615
|
addCalls(row.provider, whole(row.answered));
|
|
13566
13616
|
}
|
|
13567
13617
|
for (const session of db.select({
|
|
13568
13618
|
seedProviders: discoverySessions.seedProviders,
|
|
13569
13619
|
seedCountRaw: discoverySessions.seedCountRaw,
|
|
13570
13620
|
probeCount: discoverySessions.probeCount
|
|
13571
|
-
}).from(discoverySessions).where(
|
|
13621
|
+
}).from(discoverySessions).where(and17(inArray8(discoverySessions.projectId, projectIds), gte2(discoverySessions.createdAt, since))).all()) {
|
|
13572
13622
|
if (session.seedCountRaw !== null) {
|
|
13573
13623
|
for (const provider of session.seedProviders?.length ? session.seedProviders : [ProviderNames.gemini]) addCalls(provider, 1);
|
|
13574
13624
|
}
|
|
@@ -13577,15 +13627,15 @@ function buildInstallState(sources, now, startedAt) {
|
|
|
13577
13627
|
const answered = answers.reduce((sum, row) => sum + whole(row.answers), 0);
|
|
13578
13628
|
const measured = answers.reduce((sum, row) => sum + whole(row.measured), 0);
|
|
13579
13629
|
const usage24h = {
|
|
13580
|
-
sweeps: total(db.select({ value: count4() }).from(runs).where(
|
|
13630
|
+
sweeps: total(db.select({ value: count4() }).from(runs).where(and17(
|
|
13581
13631
|
inArray8(runs.projectId, projectIds),
|
|
13582
|
-
|
|
13632
|
+
eq21(runs.kind, RunKinds["answer-visibility"]),
|
|
13583
13633
|
gte2(runs.createdAt, since),
|
|
13584
13634
|
ne3(runs.trigger, RunTriggers.probe)
|
|
13585
13635
|
))),
|
|
13586
|
-
audits: total(db.select({ value: count4() }).from(runs).where(
|
|
13636
|
+
audits: total(db.select({ value: count4() }).from(runs).where(and17(
|
|
13587
13637
|
inArray8(runs.projectId, projectIds),
|
|
13588
|
-
|
|
13638
|
+
eq21(runs.kind, RunKinds["site-audit"]),
|
|
13589
13639
|
gte2(runs.createdAt, since)
|
|
13590
13640
|
))),
|
|
13591
13641
|
providerCalls: [...providerCalls.values()].reduce((sum, calls) => sum + calls, 0)
|
|
@@ -13602,14 +13652,14 @@ function buildInstallState(sources, now, startedAt) {
|
|
|
13602
13652
|
inputTokens: sql8`coalesce(sum(${llmUsageEvents.inputTokens}), 0)`,
|
|
13603
13653
|
outputTokens: sql8`coalesce(sum(${llmUsageEvents.outputTokens}), 0)`,
|
|
13604
13654
|
costMillicents: sql8`coalesce(sum(${llmUsageEvents.costMillicents}), 0)`
|
|
13605
|
-
}).from(llmUsageEvents).where(
|
|
13655
|
+
}).from(llmUsageEvents).where(and17(eq21(llmUsageEvents.feature, AeroLlmUsageFeatures.turn), gte2(llmUsageEvents.createdAt, since))).get();
|
|
13606
13656
|
const aeroCalls = whole(aero?.calls);
|
|
13607
13657
|
usage24h.aeroModelCalls = aeroCalls;
|
|
13608
13658
|
usage24h.aeroInputTokens = whole(aero?.inputTokens);
|
|
13609
13659
|
usage24h.aeroOutputTokens = whole(aero?.outputTokens);
|
|
13610
13660
|
const aeroMillicents = whole(aero?.costMillicents);
|
|
13611
13661
|
if (aeroCalls === 0 || aeroMillicents > 0) usage24h.aeroCostMicros = aeroMillicents * 10;
|
|
13612
|
-
usage24h.webhookDeliveries = total(db.select({ value: count4() }).from(auditLog).where(
|
|
13662
|
+
usage24h.webhookDeliveries = total(db.select({ value: count4() }).from(auditLog).where(and17(gte2(auditLog.createdAt, since), eq21(auditLog.action, WEBHOOK_DELIVERED_ACTION))));
|
|
13613
13663
|
const providerCalls24h = Object.fromEntries(PROVIDER_NAMES.flatMap((name) => providerCalls.has(name) ? [[name, providerCalls.get(name)]] : []));
|
|
13614
13664
|
return {
|
|
13615
13665
|
providers: registeredProviderNames(config).filter((name) => KNOWN_PROVIDERS2.has(name)),
|
|
@@ -13739,7 +13789,7 @@ function configuredProviderEntries(config, apiAdapters, cdpAdapter) {
|
|
|
13739
13789
|
|
|
13740
13790
|
// src/provider-reload-batch-guard.ts
|
|
13741
13791
|
import { isDeepStrictEqual } from "util";
|
|
13742
|
-
import { and as
|
|
13792
|
+
import { and as and18, inArray as inArray9 } from "drizzle-orm";
|
|
13743
13793
|
function pendingBatchIdentity(provider) {
|
|
13744
13794
|
const { quotaPolicy: _quotaPolicy, ...identity } = resolveRegistration(provider.adapter, provider.config).config;
|
|
13745
13795
|
return identity;
|
|
@@ -13753,7 +13803,7 @@ function assertProviderReloadKeepsPendingBatches(db, previous, next, executing =
|
|
|
13753
13803
|
const changed = previous.filter(changedRegistration).map((provider) => provider.adapter.name);
|
|
13754
13804
|
const active = executing.filter((snapshot) => changedRegistration(snapshot.registration));
|
|
13755
13805
|
if (changed.length === 0 && active.length === 0) return;
|
|
13756
|
-
const pending = changed.length === 0 ? [] : db.select({ id: providerBatches.id, provider: providerBatches.provider, runId: providerBatches.runId }).from(providerBatches).where(
|
|
13806
|
+
const pending = changed.length === 0 ? [] : db.select({ id: providerBatches.id, provider: providerBatches.provider, runId: providerBatches.runId }).from(providerBatches).where(and18(
|
|
13757
13807
|
inArray9(providerBatches.provider, changed),
|
|
13758
13808
|
inArray9(providerBatches.status, [ProviderBatchStatuses.submitting, ProviderBatchStatuses.submitted, ProviderBatchStatuses.ended])
|
|
13759
13809
|
)).all();
|
|
@@ -13835,7 +13885,7 @@ function createProviderModelCatalog(registry) {
|
|
|
13835
13885
|
// src/scheduler.ts
|
|
13836
13886
|
import crypto21 from "crypto";
|
|
13837
13887
|
import cron from "node-cron";
|
|
13838
|
-
import { and as
|
|
13888
|
+
import { and as and19, eq as eq22, inArray as inArray10, notExists, sql as sql9 } from "drizzle-orm";
|
|
13839
13889
|
var log18 = createLogger("Scheduler");
|
|
13840
13890
|
var SLOT_FIRED = { status: OutcomeStatuses.succeeded };
|
|
13841
13891
|
var slotSkipped = (reasonCode) => ({ status: OutcomeStatuses.skipped, reasonCode });
|
|
@@ -13914,9 +13964,9 @@ var Scheduler = class {
|
|
|
13914
13964
|
*/
|
|
13915
13965
|
ensureHealthSchedules() {
|
|
13916
13966
|
const projectsWithoutHealth = this.db.select({ id: projects.id }).from(projects).where(notExists(
|
|
13917
|
-
this.db.select({ one: sql9`1` }).from(schedules).where(
|
|
13918
|
-
|
|
13919
|
-
|
|
13967
|
+
this.db.select({ one: sql9`1` }).from(schedules).where(and19(
|
|
13968
|
+
eq22(schedules.projectId, projects.id),
|
|
13969
|
+
eq22(schedules.kind, SchedulableRunKinds.doctor)
|
|
13920
13970
|
))
|
|
13921
13971
|
)).all();
|
|
13922
13972
|
if (projectsWithoutHealth.length === 0) return;
|
|
@@ -13959,7 +14009,7 @@ var Scheduler = class {
|
|
|
13959
14009
|
start() {
|
|
13960
14010
|
this.ensureHealthSchedules();
|
|
13961
14011
|
this.ensureQueryBaskets();
|
|
13962
|
-
const allSchedules = this.db.select().from(schedules).where(
|
|
14012
|
+
const allSchedules = this.db.select().from(schedules).where(eq22(schedules.enabled, true)).all();
|
|
13963
14013
|
for (const schedule of allSchedules) {
|
|
13964
14014
|
const missedRunAt = schedule.nextRunAt;
|
|
13965
14015
|
const recurrence = scheduleRecurrence(schedule);
|
|
@@ -13993,7 +14043,7 @@ var Scheduler = class {
|
|
|
13993
14043
|
this.stopTask(key, existing, "Stopped");
|
|
13994
14044
|
this.tasks.delete(key);
|
|
13995
14045
|
}
|
|
13996
|
-
const schedule = this.db.select().from(schedules).where(
|
|
14046
|
+
const schedule = this.db.select().from(schedules).where(and19(eq22(schedules.projectId, projectId), eq22(schedules.kind, kind))).get();
|
|
13997
14047
|
if (schedule && schedule.enabled) {
|
|
13998
14048
|
this.registerCronTask(schedule);
|
|
13999
14049
|
}
|
|
@@ -14026,12 +14076,12 @@ var Scheduler = class {
|
|
|
14026
14076
|
/** Runtime timing writes must not restore a version already issued to a settings caller. */
|
|
14027
14077
|
updateScheduleTiming(scheduleId, timing) {
|
|
14028
14078
|
this.db.transaction((tx) => {
|
|
14029
|
-
const current = tx.select({ updatedAt: schedules.updatedAt }).from(schedules).where(
|
|
14079
|
+
const current = tx.select({ updatedAt: schedules.updatedAt }).from(schedules).where(eq22(schedules.id, scheduleId)).get();
|
|
14030
14080
|
if (!current) return;
|
|
14031
14081
|
tx.update(schedules).set({
|
|
14032
14082
|
...timing,
|
|
14033
14083
|
updatedAt: nextScheduleUpdatedAt(current.updatedAt)
|
|
14034
|
-
}).where(
|
|
14084
|
+
}).where(eq22(schedules.id, scheduleId)).run();
|
|
14035
14085
|
});
|
|
14036
14086
|
}
|
|
14037
14087
|
registerCronTask(schedule, options = {}) {
|
|
@@ -14107,7 +14157,7 @@ var Scheduler = class {
|
|
|
14107
14157
|
};
|
|
14108
14158
|
const tick = () => {
|
|
14109
14159
|
if (stopped) return;
|
|
14110
|
-
const current = this.db.select().from(schedules).where(
|
|
14160
|
+
const current = this.db.select().from(schedules).where(eq22(schedules.id, scheduleId)).get();
|
|
14111
14161
|
if (!current || !current.enabled || !scheduleRecurrence(current)) {
|
|
14112
14162
|
this.remove(projectId, kind);
|
|
14113
14163
|
return;
|
|
@@ -14125,7 +14175,7 @@ var Scheduler = class {
|
|
|
14125
14175
|
this.triggerRun(scheduleId, projectId, kind);
|
|
14126
14176
|
}
|
|
14127
14177
|
}
|
|
14128
|
-
arm(this.db.select({ nextRunAt: schedules.nextRunAt }).from(schedules).where(
|
|
14178
|
+
arm(this.db.select({ nextRunAt: schedules.nextRunAt }).from(schedules).where(eq22(schedules.id, scheduleId)).get()?.nextRunAt);
|
|
14129
14179
|
};
|
|
14130
14180
|
this.tasks.set(key, task);
|
|
14131
14181
|
let registeredNextRunAt = schedule.nextRunAt;
|
|
@@ -14140,7 +14190,7 @@ var Scheduler = class {
|
|
|
14140
14190
|
/** Atomically advance a due callback-only calendar occurrence before dispatch. */
|
|
14141
14191
|
claimCalendarOccurrence(schedule, dueAt, now) {
|
|
14142
14192
|
return this.db.transaction((tx) => {
|
|
14143
|
-
const current = tx.select().from(schedules).where(
|
|
14193
|
+
const current = tx.select().from(schedules).where(eq22(schedules.id, schedule.id)).get();
|
|
14144
14194
|
if (!current || !current.enabled || current.updatedAt !== schedule.updatedAt || current.nextRunAt !== dueAt) return false;
|
|
14145
14195
|
const recurrence = scheduleRecurrence(current);
|
|
14146
14196
|
if (!recurrence) return false;
|
|
@@ -14149,11 +14199,11 @@ var Scheduler = class {
|
|
|
14149
14199
|
const result = tx.update(schedules).set({
|
|
14150
14200
|
nextRunAt,
|
|
14151
14201
|
updatedAt: nextScheduleUpdatedAt(current.updatedAt)
|
|
14152
|
-
}).where(
|
|
14153
|
-
|
|
14154
|
-
|
|
14155
|
-
|
|
14156
|
-
|
|
14202
|
+
}).where(and19(
|
|
14203
|
+
eq22(schedules.id, current.id),
|
|
14204
|
+
eq22(schedules.enabled, true),
|
|
14205
|
+
eq22(schedules.nextRunAt, dueAt),
|
|
14206
|
+
eq22(schedules.updatedAt, schedule.updatedAt)
|
|
14157
14207
|
)).run();
|
|
14158
14208
|
return result.changes === 1;
|
|
14159
14209
|
});
|
|
@@ -14175,7 +14225,7 @@ var Scheduler = class {
|
|
|
14175
14225
|
fireSlot(scheduleId, projectId, kind, claimedOccurrence) {
|
|
14176
14226
|
try {
|
|
14177
14227
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
14178
|
-
const currentSchedule = this.db.select().from(schedules).where(
|
|
14228
|
+
const currentSchedule = this.db.select().from(schedules).where(eq22(schedules.id, scheduleId)).get();
|
|
14179
14229
|
if (!currentSchedule || !currentSchedule.enabled) {
|
|
14180
14230
|
log18.warn("schedule.stale", { scheduleId, projectId, kind, msg: "schedule no longer exists or is disabled" });
|
|
14181
14231
|
this.remove(projectId, kind);
|
|
@@ -14191,7 +14241,7 @@ var Scheduler = class {
|
|
|
14191
14241
|
timezone: currentSchedule.timezone,
|
|
14192
14242
|
recurrence
|
|
14193
14243
|
});
|
|
14194
|
-
const project = this.db.select().from(projects).where(
|
|
14244
|
+
const project = this.db.select().from(projects).where(eq22(projects.id, projectId)).get();
|
|
14195
14245
|
if (!project) {
|
|
14196
14246
|
log18.error("project.not-found", { projectId, kind, msg: "skipping scheduled run" });
|
|
14197
14247
|
this.remove(projectId, kind);
|
|
@@ -14242,9 +14292,9 @@ var Scheduler = class {
|
|
|
14242
14292
|
log18.warn("ads-sync.no-callback", { scheduleId, projectId, msg: "host did not register onAdsSyncRequested" });
|
|
14243
14293
|
return slotFailed(OutcomeReasonCodes.UNSUPPORTED);
|
|
14244
14294
|
}
|
|
14245
|
-
const activeAdsRun = this.db.select({ id: runs.id }).from(runs).where(
|
|
14246
|
-
|
|
14247
|
-
|
|
14295
|
+
const activeAdsRun = this.db.select({ id: runs.id }).from(runs).where(and19(
|
|
14296
|
+
eq22(runs.projectId, projectId),
|
|
14297
|
+
eq22(runs.kind, RunKinds["ads-sync"]),
|
|
14248
14298
|
inArray10(runs.status, [RunStatuses.queued, RunStatuses.running])
|
|
14249
14299
|
)).get();
|
|
14250
14300
|
if (activeAdsRun) {
|
|
@@ -14274,9 +14324,9 @@ var Scheduler = class {
|
|
|
14274
14324
|
log18.warn("seo-sync.no-callback", { scheduleId, projectId, msg: "host did not register onSeoSyncRequested" });
|
|
14275
14325
|
return slotFailed(OutcomeReasonCodes.UNSUPPORTED);
|
|
14276
14326
|
}
|
|
14277
|
-
const activeSeoRun = this.db.select({ id: runs.id }).from(runs).where(
|
|
14278
|
-
|
|
14279
|
-
|
|
14327
|
+
const activeSeoRun = this.db.select({ id: runs.id }).from(runs).where(and19(
|
|
14328
|
+
eq22(runs.projectId, projectId),
|
|
14329
|
+
eq22(runs.kind, RunKinds["seo-sync"]),
|
|
14280
14330
|
inArray10(runs.status, [RunStatuses.queued, RunStatuses.running])
|
|
14281
14331
|
)).get();
|
|
14282
14332
|
if (activeSeoRun) {
|
|
@@ -14336,9 +14386,9 @@ var Scheduler = class {
|
|
|
14336
14386
|
log18.warn("site-audit.no-callback", { scheduleId, projectId, msg: "host did not register onSiteAuditRequested" });
|
|
14337
14387
|
return slotFailed(OutcomeReasonCodes.UNSUPPORTED);
|
|
14338
14388
|
}
|
|
14339
|
-
const active = this.db.select({ id: runs.id }).from(runs).where(
|
|
14340
|
-
|
|
14341
|
-
|
|
14389
|
+
const active = this.db.select({ id: runs.id }).from(runs).where(and19(
|
|
14390
|
+
eq22(runs.projectId, projectId),
|
|
14391
|
+
eq22(runs.kind, RunKinds["site-audit"]),
|
|
14342
14392
|
inArray10(runs.status, [RunStatuses.queued, RunStatuses.running])
|
|
14343
14393
|
)).get();
|
|
14344
14394
|
if (active) {
|
|
@@ -14554,7 +14604,7 @@ function startSiteLivenessLoop(deps, intervalMs = SITE_LIVENESS_INTERVAL_MS) {
|
|
|
14554
14604
|
|
|
14555
14605
|
// src/review-webhook-delivery.ts
|
|
14556
14606
|
import { createHash as createHash7 } from "crypto";
|
|
14557
|
-
import { and as
|
|
14607
|
+
import { and as and20, asc as asc4, eq as eq23, isNull as isNull3, lte as lte2, or } from "drizzle-orm";
|
|
14558
14608
|
var log20 = createLogger("ReviewDelivery");
|
|
14559
14609
|
var hash = (value) => createHash7("sha256").update(JSON.stringify(value)).digest("hex");
|
|
14560
14610
|
var REVIEW_DELIVERY_MAX_ATTEMPTS = 10;
|
|
@@ -14582,20 +14632,20 @@ function reviewEventId(projectId, review) {
|
|
|
14582
14632
|
function reviewEventRefusal(db, projectId, payload, notification, targetDigest, now) {
|
|
14583
14633
|
if (!notification?.enabled || !notification.config.events.includes("review.negative")) return "destination_disabled";
|
|
14584
14634
|
if (targetDigest !== void 0 && hash(notification.config.url) !== targetDigest) return "destination_changed";
|
|
14585
|
-
const project = db.select().from(projects).where(
|
|
14635
|
+
const project = db.select().from(projects).where(eq23(projects.id, projectId)).get();
|
|
14586
14636
|
if (!project || project.name !== payload.project.name || project.canonicalDomain !== payload.project.canonicalDomain) return "project_changed";
|
|
14587
14637
|
const review = payload.reviews[0];
|
|
14588
|
-
const location = db.select().from(gbpLocations).where(
|
|
14589
|
-
|
|
14590
|
-
|
|
14591
|
-
|
|
14638
|
+
const location = db.select().from(gbpLocations).where(and20(
|
|
14639
|
+
eq23(gbpLocations.projectId, projectId),
|
|
14640
|
+
eq23(gbpLocations.locationName, review.location.name),
|
|
14641
|
+
eq23(gbpLocations.selected, true)
|
|
14592
14642
|
)).get();
|
|
14593
14643
|
if (!location || review.origin === "gbp" && !review.reviewName.startsWith(`${location.accountName}/${location.locationName}/reviews/`)) return "location_removed_or_deselected";
|
|
14594
|
-
const current = db.select().from(gbpReviews).where(
|
|
14595
|
-
|
|
14596
|
-
|
|
14597
|
-
|
|
14598
|
-
|
|
14644
|
+
const current = db.select().from(gbpReviews).where(and20(
|
|
14645
|
+
eq23(gbpReviews.projectId, projectId),
|
|
14646
|
+
eq23(gbpReviews.origin, review.origin),
|
|
14647
|
+
eq23(gbpReviews.reviewName, review.reviewName),
|
|
14648
|
+
eq23(gbpReviews.locationName, review.location.name)
|
|
14599
14649
|
)).get();
|
|
14600
14650
|
if (!current) return "review_removed";
|
|
14601
14651
|
if (!isWithinAlertWindow(review.updateTime, now) || !isWithinAlertWindow(current.updateTime, now)) return "review_expired";
|
|
@@ -14603,10 +14653,10 @@ function reviewEventRefusal(db, projectId, payload, notification, targetDigest,
|
|
|
14603
14653
|
if (!isNegativeReviewRating(current.starRating, threshold) || !isNegativeReviewRating(review.starRating, threshold)) return "review_no_longer_negative";
|
|
14604
14654
|
if (["baseline", "skipped", "suppressed", "stale", "none"].includes(current.alertState)) return "review_suppressed";
|
|
14605
14655
|
if (review.origin === "places") {
|
|
14606
|
-
const v4HasWorked = db.select({ id: gbpReviewRatings.id }).from(gbpReviewRatings).where(
|
|
14607
|
-
|
|
14608
|
-
|
|
14609
|
-
|
|
14656
|
+
const v4HasWorked = db.select({ id: gbpReviewRatings.id }).from(gbpReviewRatings).where(and20(
|
|
14657
|
+
eq23(gbpReviewRatings.projectId, projectId),
|
|
14658
|
+
eq23(gbpReviewRatings.locationName, review.location.name),
|
|
14659
|
+
eq23(gbpReviewRatings.origin, "gbp")
|
|
14610
14660
|
)).get() !== void 0;
|
|
14611
14661
|
if (businessProfileReviewsCover(location.reviewsAccess, v4HasWorked)) return "review_origin_superseded";
|
|
14612
14662
|
}
|
|
@@ -14614,9 +14664,9 @@ function reviewEventRefusal(db, projectId, payload, notification, targetDigest,
|
|
|
14614
14664
|
}
|
|
14615
14665
|
function cancelReviewDelivery(db, id, reason) {
|
|
14616
14666
|
db.transaction((tx) => {
|
|
14617
|
-
const canceled = tx.update(reviewWebhookDeliveries).set({ state: ReviewDeliveryStates.Canceled, retryAt: null }).where(
|
|
14618
|
-
|
|
14619
|
-
|
|
14667
|
+
const canceled = tx.update(reviewWebhookDeliveries).set({ state: ReviewDeliveryStates.Canceled, retryAt: null }).where(and20(
|
|
14668
|
+
eq23(reviewWebhookDeliveries.id, id),
|
|
14669
|
+
eq23(reviewWebhookDeliveries.state, ReviewDeliveryStates.Pending)
|
|
14620
14670
|
)).returning({ projectId: reviewWebhookDeliveries.projectId, eventId: reviewWebhookDeliveries.eventId, notificationId: reviewWebhookDeliveries.notificationId, targetDigest: reviewWebhookDeliveries.targetDigest }).get();
|
|
14621
14671
|
if (canceled) writeAuditLog(tx, {
|
|
14622
14672
|
projectId: canceled.projectId,
|
|
@@ -14632,10 +14682,10 @@ function deferReviewDelivery(db, saved, now) {
|
|
|
14632
14682
|
if (saved.state !== ReviewDeliveryStates.Pending && saved.state !== ReviewDeliveryStates.Attempting) return;
|
|
14633
14683
|
const waited = now - Date.parse(saved.createdAt);
|
|
14634
14684
|
const delay = Math.min(Math.max(RETRY_BASE_MS, Number.isFinite(waited) ? waited / 4 : RETRY_BASE_MS), RETRY_MAX_MS);
|
|
14635
|
-
db.update(reviewWebhookDeliveries).set({ retryAt: new Date(now + delay).toISOString() }).where(
|
|
14636
|
-
|
|
14637
|
-
|
|
14638
|
-
|
|
14685
|
+
db.update(reviewWebhookDeliveries).set({ retryAt: new Date(now + delay).toISOString() }).where(and20(
|
|
14686
|
+
eq23(reviewWebhookDeliveries.id, saved.id),
|
|
14687
|
+
eq23(reviewWebhookDeliveries.state, saved.state),
|
|
14688
|
+
eq23(reviewWebhookDeliveries.attempts, saved.attempts)
|
|
14639
14689
|
)).run();
|
|
14640
14690
|
}
|
|
14641
14691
|
function auditUndelivered(db, saved, outcome) {
|
|
@@ -14652,8 +14702,8 @@ function dueReviewDeliveryProjects(db, now) {
|
|
|
14652
14702
|
const at = new Date(now).toISOString();
|
|
14653
14703
|
const staleAttempt = new Date(now - 6e4).toISOString();
|
|
14654
14704
|
return db.selectDistinct({ projectId: reviewWebhookDeliveries.projectId }).from(reviewWebhookDeliveries).where(or(
|
|
14655
|
-
|
|
14656
|
-
|
|
14705
|
+
and20(eq23(reviewWebhookDeliveries.state, ReviewDeliveryStates.Pending), or(isNull3(reviewWebhookDeliveries.retryAt), lte2(reviewWebhookDeliveries.retryAt, at))),
|
|
14706
|
+
and20(eq23(reviewWebhookDeliveries.state, ReviewDeliveryStates.Attempting), lte2(reviewWebhookDeliveries.attemptedAt, staleAttempt))
|
|
14657
14707
|
)).all().map((row) => row.projectId);
|
|
14658
14708
|
}
|
|
14659
14709
|
function startReviewDeliveryLoop(drain, intervalMs = REVIEW_DELIVERY_INTERVAL_MS) {
|
|
@@ -14674,12 +14724,12 @@ async function drainReviewDeliveries(options) {
|
|
|
14674
14724
|
const now = (options.now ?? Date.now)();
|
|
14675
14725
|
const at = new Date(now).toISOString();
|
|
14676
14726
|
const staleAttempt = new Date(now - 6e4).toISOString();
|
|
14677
|
-
const ready = options.db.select().from(reviewWebhookDeliveries).where(
|
|
14678
|
-
|
|
14727
|
+
const ready = options.db.select().from(reviewWebhookDeliveries).where(and20(
|
|
14728
|
+
eq23(reviewWebhookDeliveries.projectId, options.projectId),
|
|
14679
14729
|
or(isNull3(reviewWebhookDeliveries.retryAt), lte2(reviewWebhookDeliveries.retryAt, at)),
|
|
14680
14730
|
or(
|
|
14681
|
-
|
|
14682
|
-
|
|
14731
|
+
eq23(reviewWebhookDeliveries.state, ReviewDeliveryStates.Pending),
|
|
14732
|
+
and20(eq23(reviewWebhookDeliveries.state, ReviewDeliveryStates.Attempting), lte2(reviewWebhookDeliveries.attemptedAt, staleAttempt))
|
|
14683
14733
|
)
|
|
14684
14734
|
)).orderBy(asc4(reviewWebhookDeliveries.retryAt), asc4(reviewWebhookDeliveries.attemptedAt), asc4(reviewWebhookDeliveries.createdAt), asc4(reviewWebhookDeliveries.id)).limit(100).all();
|
|
14685
14735
|
let delivered = 0;
|
|
@@ -14703,15 +14753,15 @@ async function deliverReviewEvent(options) {
|
|
|
14703
14753
|
if (!payload.eventId || !/^evt_[a-f0-9]{64}$/u.test(payload.eventId) || payload.reviews.length !== 1)
|
|
14704
14754
|
return false;
|
|
14705
14755
|
const notification = db.select().from(notifications).where(
|
|
14706
|
-
|
|
14707
|
-
|
|
14708
|
-
|
|
14756
|
+
and20(
|
|
14757
|
+
eq23(notifications.id, notificationId),
|
|
14758
|
+
eq23(notifications.projectId, projectId)
|
|
14709
14759
|
)
|
|
14710
14760
|
).get();
|
|
14711
14761
|
const id = hash([projectId, notificationId, payload.eventId]);
|
|
14712
14762
|
const now = options.now ?? Date.now;
|
|
14713
14763
|
const at = new Date(now()).toISOString();
|
|
14714
|
-
let saved = db.select().from(reviewWebhookDeliveries).where(
|
|
14764
|
+
let saved = db.select().from(reviewWebhookDeliveries).where(eq23(reviewWebhookDeliveries.id, id)).get();
|
|
14715
14765
|
const event = saved?.payload ?? payload;
|
|
14716
14766
|
const refusal = reviewEventRefusal(db, projectId, event, notification, saved?.targetDigest, new Date(now()));
|
|
14717
14767
|
if (refusal) {
|
|
@@ -14730,7 +14780,7 @@ async function deliverReviewEvent(options) {
|
|
|
14730
14780
|
payload,
|
|
14731
14781
|
createdAt: at
|
|
14732
14782
|
}).onConflictDoNothing().run();
|
|
14733
|
-
saved = db.select().from(reviewWebhookDeliveries).where(
|
|
14783
|
+
saved = db.select().from(reviewWebhookDeliveries).where(eq23(reviewWebhookDeliveries.id, id)).get();
|
|
14734
14784
|
if (!saved || saved.targetDigest !== targetDigest) return false;
|
|
14735
14785
|
if (saved.state === ReviewDeliveryStates.Sent) return true;
|
|
14736
14786
|
if (saved.state === ReviewDeliveryStates.Uncertain || saved.state === ReviewDeliveryStates.Canceled) return false;
|
|
@@ -14745,17 +14795,17 @@ async function deliverReviewEvent(options) {
|
|
|
14745
14795
|
return false;
|
|
14746
14796
|
const state2 = destination.signed ? ReviewDeliveryStates.Pending : ReviewDeliveryStates.Uncertain;
|
|
14747
14797
|
const moved = db.update(reviewWebhookDeliveries).set({ state: state2 }).where(
|
|
14748
|
-
|
|
14749
|
-
|
|
14750
|
-
|
|
14751
|
-
|
|
14752
|
-
|
|
14798
|
+
and20(
|
|
14799
|
+
eq23(reviewWebhookDeliveries.id, id),
|
|
14800
|
+
eq23(reviewWebhookDeliveries.state, ReviewDeliveryStates.Attempting),
|
|
14801
|
+
eq23(reviewWebhookDeliveries.attempts, saved.attempts),
|
|
14802
|
+
eq23(reviewWebhookDeliveries.attemptedAt, saved.attemptedAt)
|
|
14753
14803
|
)
|
|
14754
14804
|
).returning({ id: reviewWebhookDeliveries.id }).get();
|
|
14755
14805
|
if (moved && state2 === ReviewDeliveryStates.Uncertain) {
|
|
14756
14806
|
auditUndelivered(db, saved, { state: state2, reason: "interrupted_attempt", attempts: saved.attempts });
|
|
14757
14807
|
}
|
|
14758
|
-
saved = db.select().from(reviewWebhookDeliveries).where(
|
|
14808
|
+
saved = db.select().from(reviewWebhookDeliveries).where(eq23(reviewWebhookDeliveries.id, id)).get();
|
|
14759
14809
|
if (!saved || saved.state !== ReviewDeliveryStates.Pending) return false;
|
|
14760
14810
|
}
|
|
14761
14811
|
if (saved.retryAt && Date.parse(saved.retryAt) > now()) return false;
|
|
@@ -14777,9 +14827,9 @@ async function deliverReviewEvent(options) {
|
|
|
14777
14827
|
return false;
|
|
14778
14828
|
}
|
|
14779
14829
|
const latest = db.select().from(notifications).where(
|
|
14780
|
-
|
|
14781
|
-
|
|
14782
|
-
|
|
14830
|
+
and20(
|
|
14831
|
+
eq23(notifications.id, notificationId),
|
|
14832
|
+
eq23(notifications.projectId, projectId)
|
|
14783
14833
|
)
|
|
14784
14834
|
).get();
|
|
14785
14835
|
const latestRefusal = reviewEventRefusal(db, projectId, saved.payload, latest, targetDigest, new Date(now()));
|
|
@@ -14792,10 +14842,10 @@ async function deliverReviewEvent(options) {
|
|
|
14792
14842
|
attempts: saved.attempts + 1,
|
|
14793
14843
|
attemptedAt: new Date(now()).toISOString()
|
|
14794
14844
|
}).where(
|
|
14795
|
-
|
|
14796
|
-
|
|
14797
|
-
|
|
14798
|
-
|
|
14845
|
+
and20(
|
|
14846
|
+
eq23(reviewWebhookDeliveries.id, id),
|
|
14847
|
+
eq23(reviewWebhookDeliveries.state, ReviewDeliveryStates.Pending),
|
|
14848
|
+
eq23(reviewWebhookDeliveries.attempts, saved.attempts)
|
|
14799
14849
|
)
|
|
14800
14850
|
).returning({ id: reviewWebhookDeliveries.id }).get();
|
|
14801
14851
|
if (!claimed) return false;
|
|
@@ -14822,10 +14872,10 @@ async function deliverReviewEvent(options) {
|
|
|
14822
14872
|
deliveredAt: sent ? new Date(now()).toISOString() : null,
|
|
14823
14873
|
retryAt: state === ReviewDeliveryStates.Pending ? new Date(now() + retryDelayMs(attempts, result.retryAfterSeconds)).toISOString() : null
|
|
14824
14874
|
}).where(
|
|
14825
|
-
|
|
14826
|
-
|
|
14827
|
-
|
|
14828
|
-
|
|
14875
|
+
and20(
|
|
14876
|
+
eq23(reviewWebhookDeliveries.id, id),
|
|
14877
|
+
eq23(reviewWebhookDeliveries.state, ReviewDeliveryStates.Attempting),
|
|
14878
|
+
eq23(reviewWebhookDeliveries.attempts, attempts)
|
|
14829
14879
|
)
|
|
14830
14880
|
).run();
|
|
14831
14881
|
if (state === ReviewDeliveryStates.Uncertain || state === ReviewDeliveryStates.Failed) {
|
|
@@ -14837,7 +14887,7 @@ async function deliverReviewEvent(options) {
|
|
|
14837
14887
|
}
|
|
14838
14888
|
|
|
14839
14889
|
// src/unrecognized-agent-alerts.ts
|
|
14840
|
-
import { and as
|
|
14890
|
+
import { and as and21, desc as desc8, eq as eq24, gte as gte3, sql as sql10 } from "drizzle-orm";
|
|
14841
14891
|
var log21 = createLogger("UnrecognizedAgentAlerts");
|
|
14842
14892
|
var UNRECOGNIZED_AGENT_ALERT_WINDOW_MS = 7 * 864e5;
|
|
14843
14893
|
var UNRECOGNIZED_AGENT_ALERT_MIN_HITS = 3;
|
|
@@ -14853,13 +14903,13 @@ function projectsWithRecentUnrecognizedAgents(db, now) {
|
|
|
14853
14903
|
return db.selectDistinct({ projectId: unrecognizedAgentEventsHourly.projectId }).from(unrecognizedAgentEventsHourly).where(gte3(unrecognizedAgentEventsHourly.tsHour, since)).all().map((row) => row.projectId);
|
|
14854
14904
|
}
|
|
14855
14905
|
function undecidedUnrecognizedAgents(db, projectId, now) {
|
|
14856
|
-
const decided = new Set(db.select({ agentName: unrecognizedAgentNotifyState.agentName }).from(unrecognizedAgentNotifyState).where(
|
|
14906
|
+
const decided = new Set(db.select({ agentName: unrecognizedAgentNotifyState.agentName }).from(unrecognizedAgentNotifyState).where(eq24(unrecognizedAgentNotifyState.projectId, projectId)).all().map((row) => row.agentName));
|
|
14857
14907
|
const totals = db.select({
|
|
14858
14908
|
name: unrecognizedAgentEventsHourly.agentName,
|
|
14859
14909
|
hits: sql10`SUM(${unrecognizedAgentEventsHourly.hits})`,
|
|
14860
14910
|
firstSeenAt: sql10`MIN(${unrecognizedAgentEventsHourly.tsHour})`,
|
|
14861
14911
|
lastSeenAt: sql10`MAX(${unrecognizedAgentEventsHourly.tsHour})`
|
|
14862
|
-
}).from(unrecognizedAgentEventsHourly).where(
|
|
14912
|
+
}).from(unrecognizedAgentEventsHourly).where(eq24(unrecognizedAgentEventsHourly.projectId, projectId)).groupBy(unrecognizedAgentEventsHourly.agentName).all();
|
|
14863
14913
|
const windowStart = new Date(now - UNRECOGNIZED_AGENT_ALERT_WINDOW_MS).toISOString();
|
|
14864
14914
|
const recordingStart = totals.reduce((earliest, total) => earliest === null || total.firstSeenAt < earliest ? total.firstSeenAt : earliest, null);
|
|
14865
14915
|
const baselineEnd = recordingStart === null ? null : new Date(Date.parse(recordingStart) + UNRECOGNIZED_AGENT_ALERT_WINDOW_MS).toISOString();
|
|
@@ -14877,7 +14927,7 @@ function undecidedUnrecognizedAgents(db, projectId, now) {
|
|
|
14877
14927
|
continue;
|
|
14878
14928
|
}
|
|
14879
14929
|
if (Number(total.hits) < UNRECOGNIZED_AGENT_ALERT_MIN_HITS) continue;
|
|
14880
|
-
const latest = db.select({ sampledUserAgent: unrecognizedAgentEventsHourly.sampledUserAgent, sampledPath: unrecognizedAgentEventsHourly.sampledPath }).from(unrecognizedAgentEventsHourly).where(
|
|
14930
|
+
const latest = db.select({ sampledUserAgent: unrecognizedAgentEventsHourly.sampledUserAgent, sampledPath: unrecognizedAgentEventsHourly.sampledPath }).from(unrecognizedAgentEventsHourly).where(and21(eq24(unrecognizedAgentEventsHourly.projectId, projectId), eq24(unrecognizedAgentEventsHourly.agentName, total.name))).orderBy(desc8(unrecognizedAgentEventsHourly.tsHour)).limit(1).get();
|
|
14881
14931
|
due.push({
|
|
14882
14932
|
name: total.name,
|
|
14883
14933
|
hits: Number(total.hits),
|
|
@@ -14912,7 +14962,7 @@ function startUnrecognizedAgentAlertLoop(dispatch, intervalMs = UNRECOGNIZED_AGE
|
|
|
14912
14962
|
}
|
|
14913
14963
|
|
|
14914
14964
|
// src/provider-batch-poller.ts
|
|
14915
|
-
import { and as
|
|
14965
|
+
import { and as and22, eq as eq25, inArray as inArray11, isNotNull as isNotNull3, isNull as isNull4, notExists as notExists2, sql as sql11 } from "drizzle-orm";
|
|
14916
14966
|
var log22 = createLogger("ProviderBatchPoller");
|
|
14917
14967
|
var PROVIDER_BATCH_POLL_INITIAL_MS = 3e4;
|
|
14918
14968
|
var PROVIDER_BATCH_POLL_MAX_MS = 10 * 6e4;
|
|
@@ -14955,7 +15005,7 @@ var ProviderBatchPoller = class {
|
|
|
14955
15005
|
* outstanding (ingested or cancelled), so its run may now finalize.
|
|
14956
15006
|
*/
|
|
14957
15007
|
async advance(row) {
|
|
14958
|
-
const runStatus = this.deps.db.select({ status: runs.status }).from(runs).where(
|
|
15008
|
+
const runStatus = this.deps.db.select({ status: runs.status }).from(runs).where(eq25(runs.id, row.runId)).get()?.status;
|
|
14959
15009
|
if (runStatus !== RunStatuses.running) {
|
|
14960
15010
|
await this.deps.runner.abandonProviderBatches([row.id], "Cancelled because its run is no longer running.");
|
|
14961
15011
|
this.schedule.delete(row.id);
|
|
@@ -14972,7 +15022,7 @@ var ProviderBatchPoller = class {
|
|
|
14972
15022
|
status = poll.status;
|
|
14973
15023
|
if (poll.status === "ended") {
|
|
14974
15024
|
const endedAt = poll.endedAt ?? new Date(this.now()).toISOString();
|
|
14975
|
-
const ended = this.deps.db.update(providerBatches).set({ status: ProviderBatchStatuses.ended, endedAt, resultsExpireAt: poll.resultsExpireAt ?? null, updatedAt: endedAt }).where(
|
|
15025
|
+
const ended = this.deps.db.update(providerBatches).set({ status: ProviderBatchStatuses.ended, endedAt, resultsExpireAt: poll.resultsExpireAt ?? null, updatedAt: endedAt }).where(and22(eq25(providerBatches.id, row.id), eq25(providerBatches.status, ProviderBatchStatuses.submitted))).run().changes === 1;
|
|
14976
15026
|
this.schedule.delete(row.id);
|
|
14977
15027
|
return ended ? this.ingest({ ...row, status: ProviderBatchStatuses.ended, resultsExpireAt: poll.resultsExpireAt ?? null }) : false;
|
|
14978
15028
|
}
|
|
@@ -14996,9 +15046,9 @@ var ProviderBatchPoller = class {
|
|
|
14996
15046
|
const at = new Date(now).toISOString();
|
|
14997
15047
|
if (row.cancelRequestedAt === null) {
|
|
14998
15048
|
if (now < Date.parse(row.deadlineAt)) return false;
|
|
14999
|
-
const claimed = this.deps.db.update(providerBatches).set({ cancelRequestedAt: at, updatedAt: at }).where(
|
|
15000
|
-
|
|
15001
|
-
|
|
15049
|
+
const claimed = this.deps.db.update(providerBatches).set({ cancelRequestedAt: at, updatedAt: at }).where(and22(
|
|
15050
|
+
eq25(providerBatches.id, row.id),
|
|
15051
|
+
eq25(providerBatches.status, ProviderBatchStatuses.submitted),
|
|
15002
15052
|
isNull4(providerBatches.cancelRequestedAt)
|
|
15003
15053
|
)).run().changes === 1;
|
|
15004
15054
|
if (!claimed) return false;
|
|
@@ -15017,7 +15067,7 @@ var ProviderBatchPoller = class {
|
|
|
15017
15067
|
status: ProviderBatchStatuses.cancelled,
|
|
15018
15068
|
error: `The provider batch did not end within an hour of being cancelled at its deadline; its ${row.requestCount} answer(s) were not recorded.`,
|
|
15019
15069
|
updatedAt: at
|
|
15020
|
-
}).where(
|
|
15070
|
+
}).where(and22(eq25(providerBatches.id, row.id), eq25(providerBatches.status, ProviderBatchStatuses.submitted))).run().changes === 1;
|
|
15021
15071
|
this.schedule.delete(row.id);
|
|
15022
15072
|
if (gaveUp) log22.warn("batch.abandoned-after-deadline", { batchId: row.id, runId: row.runId, provider: row.provider });
|
|
15023
15073
|
return gaveUp;
|
|
@@ -15057,10 +15107,10 @@ var ProviderBatchPoller = class {
|
|
|
15057
15107
|
/** Runs the sweep handed off whose batches have all settled. */
|
|
15058
15108
|
finalizableRuns() {
|
|
15059
15109
|
const holding = [ProviderBatchStatuses.submitting, ...OUTSTANDING_PROVIDER_BATCH_STATUSES];
|
|
15060
|
-
return this.deps.db.select({ id: runs.id, projectId: runs.projectId }).from(runs).where(
|
|
15061
|
-
|
|
15110
|
+
return this.deps.db.select({ id: runs.id, projectId: runs.projectId }).from(runs).where(and22(
|
|
15111
|
+
eq25(runs.status, RunStatuses.running),
|
|
15062
15112
|
isNotNull3(runs.pendingProviderErrors),
|
|
15063
|
-
notExists2(this.deps.db.select({ one: sql11`1` }).from(providerBatches).where(
|
|
15113
|
+
notExists2(this.deps.db.select({ one: sql11`1` }).from(providerBatches).where(and22(eq25(providerBatches.runId, runs.id), inArray11(providerBatches.status, holding))))
|
|
15064
15114
|
)).all();
|
|
15065
15115
|
}
|
|
15066
15116
|
};
|
|
@@ -15140,12 +15190,12 @@ async function refreshAllIntegrations(client, projectName, now = /* @__PURE__ */
|
|
|
15140
15190
|
}
|
|
15141
15191
|
|
|
15142
15192
|
// src/notifier.ts
|
|
15143
|
-
import { eq as
|
|
15193
|
+
import { eq as eq27, desc as desc10, and as and24, inArray as inArray12, or as or2, isNull as isNull5 } from "drizzle-orm";
|
|
15144
15194
|
import crypto23 from "crypto";
|
|
15145
15195
|
|
|
15146
15196
|
// src/site-health-alerts.ts
|
|
15147
15197
|
import crypto22 from "crypto";
|
|
15148
|
-
import { and as
|
|
15198
|
+
import { and as and23, asc as asc5, desc as desc9, eq as eq26, gt as gt3 } from "drizzle-orm";
|
|
15149
15199
|
var ISSUE_TITLES = {
|
|
15150
15200
|
"missing-h1": "Missing H1",
|
|
15151
15201
|
"multiple-h1": "Multiple H1 tags",
|
|
@@ -15158,18 +15208,18 @@ function siteHealthProblemKey(code, url) {
|
|
|
15158
15208
|
return `${code}|${crypto22.createHash("sha256").update(url).digest("hex").slice(0, 16)}`;
|
|
15159
15209
|
}
|
|
15160
15210
|
function readSiteHealthScan(db, projectId, runId) {
|
|
15161
|
-
const snapshot = db.select().from(siteCrawlSnapshots).where(
|
|
15211
|
+
const snapshot = db.select().from(siteCrawlSnapshots).where(and23(eq26(siteCrawlSnapshots.projectId, projectId), eq26(siteCrawlSnapshots.runId, runId))).orderBy(desc9(siteCrawlSnapshots.updatedAt)).limit(1).get();
|
|
15162
15212
|
if (!snapshot?.detailsAvailable || !snapshot.attemptId) return null;
|
|
15163
|
-
const scope =
|
|
15164
|
-
|
|
15165
|
-
|
|
15166
|
-
|
|
15213
|
+
const scope = and23(
|
|
15214
|
+
eq26(siteCrawlPages.projectId, projectId),
|
|
15215
|
+
eq26(siteCrawlPages.runId, runId),
|
|
15216
|
+
eq26(siteCrawlPages.attemptId, snapshot.attemptId)
|
|
15167
15217
|
);
|
|
15168
15218
|
const pagesByCode = /* @__PURE__ */ new Map();
|
|
15169
15219
|
const keys = /* @__PURE__ */ new Set();
|
|
15170
15220
|
let after;
|
|
15171
15221
|
for (; ; ) {
|
|
15172
|
-
const pages = db.select({ nodeKey: siteCrawlPages.nodeKey, url: siteCrawlPages.url, auditFields: siteCrawlPages.auditFields }).from(siteCrawlPages).where(after === void 0 ? scope :
|
|
15222
|
+
const pages = db.select({ nodeKey: siteCrawlPages.nodeKey, url: siteCrawlPages.url, auditFields: siteCrawlPages.auditFields }).from(siteCrawlPages).where(after === void 0 ? scope : and23(scope, gt3(siteCrawlPages.nodeKey, after))).orderBy(asc5(siteCrawlPages.nodeKey)).limit(PAGE_BATCH).all();
|
|
15173
15223
|
for (const page of pages) {
|
|
15174
15224
|
const defects = page.auditFields.criticalDefects;
|
|
15175
15225
|
for (const defect of Array.isArray(defects) ? defects : []) {
|
|
@@ -15211,7 +15261,7 @@ function settledSiteHealthKeys(scan, reported) {
|
|
|
15211
15261
|
return [...scan.complete ? scan.keys : /* @__PURE__ */ new Set([...reported, ...scan.keys])].sort(compareText);
|
|
15212
15262
|
}
|
|
15213
15263
|
function readSiteHealthNotifyState(db, projectId) {
|
|
15214
|
-
return db.select().from(siteHealthNotifyState).where(
|
|
15264
|
+
return db.select().from(siteHealthNotifyState).where(eq26(siteHealthNotifyState.projectId, projectId)).get() ?? null;
|
|
15215
15265
|
}
|
|
15216
15266
|
function writeSiteHealthNotifyState(db, input) {
|
|
15217
15267
|
const values = { reportedKeys: input.reportedKeys, runId: input.runId, updatedAt: input.updatedAt, notifiedAt: input.notifiedAt };
|
|
@@ -15340,18 +15390,18 @@ var Notifier = class {
|
|
|
15340
15390
|
/** Called after a run completes (success, partial, or failed). */
|
|
15341
15391
|
async onRunCompleted(runId, projectId, opts) {
|
|
15342
15392
|
log24.info("run.completed", { runId, projectId, origin: opts?.origin ?? "sweep" });
|
|
15343
|
-
const notifs = this.db.select().from(notifications).where(
|
|
15393
|
+
const notifs = this.db.select().from(notifications).where(eq27(notifications.projectId, projectId)).all().filter((n) => n.enabled);
|
|
15344
15394
|
if (notifs.length === 0) {
|
|
15345
15395
|
log24.info("notifications.none-enabled", { projectId });
|
|
15346
15396
|
return;
|
|
15347
15397
|
}
|
|
15348
15398
|
log24.info("notifications.found", { projectId, count: notifs.length });
|
|
15349
|
-
const run = this.db.select().from(runs).where(
|
|
15399
|
+
const run = this.db.select().from(runs).where(eq27(runs.id, runId)).get();
|
|
15350
15400
|
if (!run) {
|
|
15351
15401
|
log24.error("run.not-found", { runId, msg: "skipping notification dispatch" });
|
|
15352
15402
|
return;
|
|
15353
15403
|
}
|
|
15354
|
-
const project = this.db.select().from(projects).where(
|
|
15404
|
+
const project = this.db.select().from(projects).where(eq27(projects.id, projectId)).get();
|
|
15355
15405
|
if (!project) {
|
|
15356
15406
|
log24.error("project.not-found", { projectId, msg: "skipping notification dispatch" });
|
|
15357
15407
|
return;
|
|
@@ -15430,7 +15480,7 @@ var Notifier = class {
|
|
|
15430
15480
|
* than inferred from the absence of a webhook.
|
|
15431
15481
|
*/
|
|
15432
15482
|
async onHealthChecked(projectId, report) {
|
|
15433
|
-
const project = this.db.select().from(projects).where(
|
|
15483
|
+
const project = this.db.select().from(projects).where(eq27(projects.id, projectId)).get();
|
|
15434
15484
|
if (!project) {
|
|
15435
15485
|
log24.error("project.not-found", { projectId, msg: "skipping health notification" });
|
|
15436
15486
|
return null;
|
|
@@ -15463,7 +15513,7 @@ var Notifier = class {
|
|
|
15463
15513
|
const status = noSignal ? "warn" : worst ? worst.status : "ok";
|
|
15464
15514
|
const code = noSignal ? HEALTH_NO_SIGNAL_CODE : worst?.code ?? "health.ok";
|
|
15465
15515
|
const summary = noSignal ? `No health check produced a result (${healthChecks.length} skipped) \u2014 health is unknown, not confirmed.` : worst?.summary ?? `All ${graded.length} health check(s) passing.`;
|
|
15466
|
-
const previous = this.db.select().from(doctorHealthState).where(
|
|
15516
|
+
const previous = this.db.select().from(doctorHealthState).where(eq27(doctorHealthState.projectId, projectId)).get();
|
|
15467
15517
|
const previousStatus = previous?.status ?? null;
|
|
15468
15518
|
const failingSignature = failing.map((c) => `${c.status}:${c.code}`).join(",");
|
|
15469
15519
|
const failingNow = failing.map((c) => ({ id: c.id, code: c.code, rank: rank(c.status) }));
|
|
@@ -15533,7 +15583,7 @@ var Notifier = class {
|
|
|
15533
15583
|
const urgentById = new Map(urgentItems.map((item) => [item.id, item]));
|
|
15534
15584
|
const generalEvent = event;
|
|
15535
15585
|
const urgentRecord = previous?.urgentNotified ?? null;
|
|
15536
|
-
const enabled = generalEvent || urgentItems.length > 0 || urgentRecord ? this.db.select().from(notifications).where(
|
|
15586
|
+
const enabled = generalEvent || urgentItems.length > 0 || urgentRecord ? this.db.select().from(notifications).where(eq27(notifications.projectId, projectId)).all().filter((n) => n.enabled) : [];
|
|
15537
15587
|
const listedEvents = (notif) => notif.config.events;
|
|
15538
15588
|
const hasUrl = (notif) => Boolean(notif.config.url);
|
|
15539
15589
|
const urgentSubscribers = enabled.filter((n) => hasUrl(n) && (listedEvents(n) ?? []).some((e) => URGENT_HEALTH_EVENTS.has(e)));
|
|
@@ -15581,7 +15631,7 @@ var Notifier = class {
|
|
|
15581
15631
|
if (previous === void 0) {
|
|
15582
15632
|
this.db.insert(doctorHealthState).values({ ...observation, notifiedAt: null }).run();
|
|
15583
15633
|
} else {
|
|
15584
|
-
this.db.update(doctorHealthState).set(observation).where(
|
|
15634
|
+
this.db.update(doctorHealthState).set(observation).where(eq27(doctorHealthState.projectId, projectId)).run();
|
|
15585
15635
|
}
|
|
15586
15636
|
for (const group of urgentGroups) {
|
|
15587
15637
|
const { urgentEvent, urgentTold, urgentNew, urgentCleared, urgentNotifs, urgentSettled } = group;
|
|
@@ -15625,7 +15675,7 @@ var Notifier = class {
|
|
|
15625
15675
|
}
|
|
15626
15676
|
if (accepted > 0) {
|
|
15627
15677
|
recordUrgentReceipts(urgentReceipts, group.subscribers, formatUrgentBreaches(urgentSettled) || null);
|
|
15628
|
-
this.db.update(doctorHealthState).set({ urgentNotified: doctorUrgentSummary(urgentReceipts), urgentReceipts }).where(
|
|
15678
|
+
this.db.update(doctorHealthState).set({ urgentNotified: doctorUrgentSummary(urgentReceipts), urgentReceipts }).where(eq27(doctorHealthState.projectId, projectId)).run();
|
|
15629
15679
|
}
|
|
15630
15680
|
if (urgentNotifs.length > 0) {
|
|
15631
15681
|
log24.info("health.urgent-notified", {
|
|
@@ -15669,7 +15719,7 @@ var Notifier = class {
|
|
|
15669
15719
|
if (await this.sendWebhook(config.url, payload, notif.id, projectId, notif.webhookSecret ?? null)) delivered += 1;
|
|
15670
15720
|
}
|
|
15671
15721
|
if (delivered > 0) {
|
|
15672
|
-
this.db.update(doctorHealthState).set({ notifiedAt: now, notifiedSignature: formatBreaches(failingNow), pendingSignature: null }).where(
|
|
15722
|
+
this.db.update(doctorHealthState).set({ notifiedAt: now, notifiedSignature: formatBreaches(failingNow), pendingSignature: null }).where(eq27(doctorHealthState.projectId, projectId)).run();
|
|
15673
15723
|
}
|
|
15674
15724
|
log24.info("health.notified", {
|
|
15675
15725
|
projectId,
|
|
@@ -15699,12 +15749,12 @@ var Notifier = class {
|
|
|
15699
15749
|
async onSiteLivenessChecked(projectId, result) {
|
|
15700
15750
|
const { check, checkedAt } = result;
|
|
15701
15751
|
if (check.status !== "ok" && check.status !== "fail") return null;
|
|
15702
|
-
const project = this.db.select().from(projects).where(
|
|
15752
|
+
const project = this.db.select().from(projects).where(eq27(projects.id, projectId)).get();
|
|
15703
15753
|
if (!project) {
|
|
15704
15754
|
log24.error("project.not-found", { projectId, msg: "skipping site liveness notification" });
|
|
15705
15755
|
return null;
|
|
15706
15756
|
}
|
|
15707
|
-
const previous = this.db.select().from(siteLivenessState).where(
|
|
15757
|
+
const previous = this.db.select().from(siteLivenessState).where(eq27(siteLivenessState.projectId, projectId)).get();
|
|
15708
15758
|
const sincePrevious = previous ? Date.parse(checkedAt) - Date.parse(previous.checkedAt) : Number.NaN;
|
|
15709
15759
|
const countsAsRepeat = Number.isFinite(sincePrevious) && sincePrevious >= SITE_LIVENESS_MIN_PASS_GAP_MS && sincePrevious <= SITE_LIVENESS_MAX_PASS_GAP_MS;
|
|
15710
15760
|
const failures = check.status === "fail" ? countsAsRepeat ? (previous?.consecutiveFailures ?? 0) + 1 : 1 : 0;
|
|
@@ -15714,7 +15764,7 @@ var Notifier = class {
|
|
|
15714
15764
|
if (check.status === "ok" && outagePaged) event = "health.recovered";
|
|
15715
15765
|
const confirmed = check.status === "fail" && failures >= SITE_LIVENESS_FAILURES_TO_PAGE;
|
|
15716
15766
|
const urgentRecord = previous?.urgentNotifiedAt ?? null;
|
|
15717
|
-
const enabled = event !== null || confirmed || urgentRecord !== null ? this.db.select().from(notifications).where(
|
|
15767
|
+
const enabled = event !== null || confirmed || urgentRecord !== null ? this.db.select().from(notifications).where(eq27(notifications.projectId, projectId)).all().filter((n) => n.enabled) : [];
|
|
15718
15768
|
const listedEvents = (notif) => notif.config.events;
|
|
15719
15769
|
const hasUrl = (notif) => Boolean(notif.config.url);
|
|
15720
15770
|
const scoped = enabled.filter((n) => hasUrl(n) && (listedEvents(n) ?? []).some((e) => URGENT_HEALTH_EVENTS.has(e)));
|
|
@@ -15747,7 +15797,7 @@ var Notifier = class {
|
|
|
15747
15797
|
if (previous === void 0) {
|
|
15748
15798
|
this.db.insert(siteLivenessState).values(observation).run();
|
|
15749
15799
|
} else {
|
|
15750
|
-
this.db.update(siteLivenessState).set(observation).where(
|
|
15800
|
+
this.db.update(siteLivenessState).set(observation).where(eq27(siteLivenessState.projectId, projectId)).run();
|
|
15751
15801
|
}
|
|
15752
15802
|
if (!event && urgentGroups.every((group) => group.urgentEvent === null)) return null;
|
|
15753
15803
|
const payloadFor = (name) => {
|
|
@@ -15783,7 +15833,7 @@ var Notifier = class {
|
|
|
15783
15833
|
}
|
|
15784
15834
|
}
|
|
15785
15835
|
if (event === "health.degraded" && delivered > 0) {
|
|
15786
|
-
this.db.update(siteLivenessState).set({ notifiedAt: checkedAt }).where(
|
|
15836
|
+
this.db.update(siteLivenessState).set({ notifiedAt: checkedAt }).where(eq27(siteLivenessState.projectId, projectId)).run();
|
|
15787
15837
|
}
|
|
15788
15838
|
log24.info("site-liveness.notified", { projectId, event, code: check.code, subscribers: notifs.length, delivered });
|
|
15789
15839
|
}
|
|
@@ -15797,7 +15847,7 @@ var Notifier = class {
|
|
|
15797
15847
|
}
|
|
15798
15848
|
if (accepted > 0) {
|
|
15799
15849
|
recordUrgentReceipts(urgentReceipts, group.subscribers, group.settled);
|
|
15800
|
-
this.db.update(siteLivenessState).set({ urgentNotifiedAt: siteUrgentSummary(urgentReceipts), urgentReceipts }).where(
|
|
15850
|
+
this.db.update(siteLivenessState).set({ urgentNotifiedAt: siteUrgentSummary(urgentReceipts), urgentReceipts }).where(eq27(siteLivenessState.projectId, projectId)).run();
|
|
15801
15851
|
}
|
|
15802
15852
|
if (urgentNotifs.length > 0 || generalIds.size > 0) {
|
|
15803
15853
|
log24.info("site-liveness.urgent-notified", { projectId, event: urgentEvent, code: check.code, subscribers: urgentNotifs.length + generalIds.size, delivered: accepted });
|
|
@@ -15823,7 +15873,7 @@ var Notifier = class {
|
|
|
15823
15873
|
*/
|
|
15824
15874
|
isNewInsightNews(projectId, insight) {
|
|
15825
15875
|
const key = insightNotifyKey(projectId, insight);
|
|
15826
|
-
const previous = this.db.select().from(insightNotifyState).where(
|
|
15876
|
+
const previous = this.db.select().from(insightNotifyState).where(eq27(insightNotifyState.key, key)).get();
|
|
15827
15877
|
if (!previous) return true;
|
|
15828
15878
|
if (severityRank(insight.severity) > severityRank(previous.severity)) return true;
|
|
15829
15879
|
const magnitude = insightMagnitude(insight);
|
|
@@ -15845,9 +15895,9 @@ var Notifier = class {
|
|
|
15845
15895
|
magnitude: insightMagnitude(insight),
|
|
15846
15896
|
notifiedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
15847
15897
|
};
|
|
15848
|
-
const existing = this.db.select().from(insightNotifyState).where(
|
|
15898
|
+
const existing = this.db.select().from(insightNotifyState).where(eq27(insightNotifyState.key, key)).get();
|
|
15849
15899
|
if (existing) {
|
|
15850
|
-
this.db.update(insightNotifyState).set(row).where(
|
|
15900
|
+
this.db.update(insightNotifyState).set(row).where(eq27(insightNotifyState.key, key)).run();
|
|
15851
15901
|
} else {
|
|
15852
15902
|
this.db.insert(insightNotifyState).values(row).run();
|
|
15853
15903
|
}
|
|
@@ -15861,11 +15911,11 @@ var Notifier = class {
|
|
|
15861
15911
|
if (criticalInsights.length > 0) insightEvents.push("insight.critical");
|
|
15862
15912
|
if (highInsights.length > 0) insightEvents.push("insight.high");
|
|
15863
15913
|
if (insightEvents.length === 0) return;
|
|
15864
|
-
const notifs = this.db.select().from(notifications).where(
|
|
15914
|
+
const notifs = this.db.select().from(notifications).where(eq27(notifications.projectId, projectId)).all().filter((n) => n.enabled);
|
|
15865
15915
|
if (notifs.length === 0) return;
|
|
15866
|
-
const run = this.db.select().from(runs).where(
|
|
15916
|
+
const run = this.db.select().from(runs).where(eq27(runs.id, runId)).get();
|
|
15867
15917
|
if (!run) return;
|
|
15868
|
-
const project = this.db.select().from(projects).where(
|
|
15918
|
+
const project = this.db.select().from(projects).where(eq27(projects.id, projectId)).get();
|
|
15869
15919
|
if (!project) return;
|
|
15870
15920
|
for (const notif of notifs) {
|
|
15871
15921
|
const config = notif.config;
|
|
@@ -15946,9 +15996,9 @@ var Notifier = class {
|
|
|
15946
15996
|
return delivered;
|
|
15947
15997
|
}
|
|
15948
15998
|
async dispatchQueuedReviewAlerts(projectId) {
|
|
15949
|
-
const pendingReviews = this.db.select().from(gbpReviews).where(
|
|
15950
|
-
const pendingRatings = this.db.select().from(gbpReviewRatings).where(
|
|
15951
|
-
const project = this.db.select().from(projects).where(
|
|
15999
|
+
const pendingReviews = this.db.select().from(gbpReviews).where(and24(eq27(gbpReviews.projectId, projectId), eq27(gbpReviews.alertState, "pending"))).all();
|
|
16000
|
+
const pendingRatings = this.db.select().from(gbpReviewRatings).where(and24(eq27(gbpReviewRatings.projectId, projectId), eq27(gbpReviewRatings.alertState, "pending"))).all();
|
|
16001
|
+
const project = this.db.select().from(projects).where(eq27(projects.id, projectId)).get();
|
|
15952
16002
|
if (!project) return { reviews: 0, ratingDrops: 0, delivered: 0 };
|
|
15953
16003
|
const chatSendsLeft = /* @__PURE__ */ new Map();
|
|
15954
16004
|
const now = /* @__PURE__ */ new Date();
|
|
@@ -15960,10 +16010,10 @@ var Notifier = class {
|
|
|
15960
16010
|
displayName: gbpLocations.displayName,
|
|
15961
16011
|
mapsUri: gbpLocations.mapsUri,
|
|
15962
16012
|
reviewsAccess: gbpLocations.reviewsAccess
|
|
15963
|
-
}).from(gbpLocations).where(
|
|
16013
|
+
}).from(gbpLocations).where(eq27(gbpLocations.projectId, projectId)).all().map((loc) => [loc.locationName, loc])
|
|
15964
16014
|
);
|
|
15965
16015
|
const v4HasWorked = new Set(
|
|
15966
|
-
this.db.select({ locationName: gbpReviewRatings.locationName }).from(gbpReviewRatings).where(
|
|
16016
|
+
this.db.select({ locationName: gbpReviewRatings.locationName }).from(gbpReviewRatings).where(and24(eq27(gbpReviewRatings.projectId, projectId), eq27(gbpReviewRatings.origin, "gbp"))).all().map((row) => row.locationName)
|
|
15967
16017
|
);
|
|
15968
16018
|
const coveredByBusinessProfile = (locationName) => businessProfileReviewsCover(locations.get(locationName)?.reviewsAccess ?? null, v4HasWorked.has(locationName));
|
|
15969
16019
|
const locationRef = (locationName) => {
|
|
@@ -15992,7 +16042,7 @@ var Notifier = class {
|
|
|
15992
16042
|
else if (row.rating === null || row.previousRating === null) setRatingState([row.id], "none");
|
|
15993
16043
|
else ratingsToSend.push(row);
|
|
15994
16044
|
}
|
|
15995
|
-
const enabled = this.db.select().from(notifications).where(
|
|
16045
|
+
const enabled = this.db.select().from(notifications).where(eq27(notifications.projectId, projectId)).all().filter((n) => n.enabled && n.config.url);
|
|
15996
16046
|
const subscribedTo = (event) => enabled.filter((n) => (n.config.events ?? []).includes(event));
|
|
15997
16047
|
const projectRef = { name: project.name, canonicalDomain: project.canonicalDomain };
|
|
15998
16048
|
const dashboardUrl = `${this.serverUrl}/projects/${project.name}`;
|
|
@@ -16046,13 +16096,13 @@ var Notifier = class {
|
|
|
16046
16096
|
if (!sent) all = false;
|
|
16047
16097
|
}
|
|
16048
16098
|
if (all) {
|
|
16049
|
-
this.db.update(gbpReviews).set({ alertState: "sent", alertStateAt: nowIso }).where(
|
|
16050
|
-
|
|
16051
|
-
|
|
16052
|
-
|
|
16053
|
-
review.alertStateAt === null ? isNull5(gbpReviews.alertStateAt) :
|
|
16054
|
-
review.comment === null ? isNull5(gbpReviews.comment) :
|
|
16055
|
-
review.starRating === null ? isNull5(gbpReviews.starRating) :
|
|
16099
|
+
this.db.update(gbpReviews).set({ alertState: "sent", alertStateAt: nowIso }).where(and24(
|
|
16100
|
+
eq27(gbpReviews.id, review.id),
|
|
16101
|
+
eq27(gbpReviews.alertState, "pending"),
|
|
16102
|
+
eq27(gbpReviews.updateTime, review.updateTime),
|
|
16103
|
+
review.alertStateAt === null ? isNull5(gbpReviews.alertStateAt) : eq27(gbpReviews.alertStateAt, review.alertStateAt),
|
|
16104
|
+
review.comment === null ? isNull5(gbpReviews.comment) : eq27(gbpReviews.comment, review.comment),
|
|
16105
|
+
review.starRating === null ? isNull5(gbpReviews.starRating) : eq27(gbpReviews.starRating, review.starRating)
|
|
16056
16106
|
)).run();
|
|
16057
16107
|
}
|
|
16058
16108
|
}
|
|
@@ -16093,7 +16143,7 @@ var Notifier = class {
|
|
|
16093
16143
|
return { reviews: reviewsToSend.length, ratingDrops: ratingsToSend.length, delivered };
|
|
16094
16144
|
}
|
|
16095
16145
|
computeTransitions(runId, projectId) {
|
|
16096
|
-
const thisRun = this.db.select().from(runs).where(
|
|
16146
|
+
const thisRun = this.db.select().from(runs).where(eq27(runs.id, runId)).get();
|
|
16097
16147
|
if (!thisRun) return [];
|
|
16098
16148
|
const completeness = measurementRunCompleteness(this.db, runId);
|
|
16099
16149
|
if (completeness.planned && !completeness.complete) {
|
|
@@ -16104,10 +16154,10 @@ var Notifier = class {
|
|
|
16104
16154
|
});
|
|
16105
16155
|
return [];
|
|
16106
16156
|
}
|
|
16107
|
-
const groupSiblings = this.db.select().from(runs).where(
|
|
16108
|
-
|
|
16109
|
-
|
|
16110
|
-
|
|
16157
|
+
const groupSiblings = this.db.select().from(runs).where(and24(
|
|
16158
|
+
eq27(runs.projectId, projectId),
|
|
16159
|
+
eq27(runs.kind, thisRun.kind),
|
|
16160
|
+
eq27(runs.createdAt, thisRun.createdAt),
|
|
16111
16161
|
notProbeRun()
|
|
16112
16162
|
)).all();
|
|
16113
16163
|
const stillPending = groupSiblings.some((r) => r.status === "queued" || r.status === "running");
|
|
@@ -16124,17 +16174,17 @@ var Notifier = class {
|
|
|
16124
16174
|
return candidate.id > best.id ? candidate : best;
|
|
16125
16175
|
});
|
|
16126
16176
|
if (winner.id !== runId) return [];
|
|
16127
|
-
const projectLocations = this.db.select({ locations: projects.locations }).from(projects).where(
|
|
16177
|
+
const projectLocations = this.db.select({ locations: projects.locations }).from(projects).where(eq27(projects.id, projectId)).get();
|
|
16128
16178
|
const locationCount = Math.max(
|
|
16129
16179
|
1,
|
|
16130
16180
|
(projectLocations?.locations ?? []).length
|
|
16131
16181
|
);
|
|
16132
16182
|
const RECENT_FETCH_LIMIT = Math.max(8, locationCount * 4);
|
|
16133
16183
|
const recentRuns = this.db.select().from(runs).where(
|
|
16134
|
-
|
|
16135
|
-
|
|
16136
|
-
|
|
16137
|
-
or2(
|
|
16184
|
+
and24(
|
|
16185
|
+
eq27(runs.projectId, projectId),
|
|
16186
|
+
eq27(runs.kind, thisRun.kind),
|
|
16187
|
+
or2(eq27(runs.status, "completed"), eq27(runs.status, "partial")),
|
|
16138
16188
|
notProbeRun()
|
|
16139
16189
|
)
|
|
16140
16190
|
).orderBy(desc10(runs.createdAt), desc10(runs.id)).limit(RECENT_FETCH_LIMIT).all();
|
|
@@ -16152,7 +16202,7 @@ var Notifier = class {
|
|
|
16152
16202
|
provider: querySnapshots.provider,
|
|
16153
16203
|
location: querySnapshots.location,
|
|
16154
16204
|
citationState: querySnapshots.citationState
|
|
16155
|
-
}).from(querySnapshots).leftJoin(queries,
|
|
16205
|
+
}).from(querySnapshots).leftJoin(queries, eq27(querySnapshots.queryId, queries.id)).where(inArray12(querySnapshots.runId, currentRunIds)).all();
|
|
16156
16206
|
const previousSnapshots = this.db.select({
|
|
16157
16207
|
queryId: querySnapshots.queryId,
|
|
16158
16208
|
provider: querySnapshots.provider,
|
|
@@ -16203,12 +16253,12 @@ var Notifier = class {
|
|
|
16203
16253
|
recordUnrecognizedAgentDecisions(this.db, projectId, baseline, UnrecognizedAgentNotifyStates.baseline, now);
|
|
16204
16254
|
if (due.length === 0) continue;
|
|
16205
16255
|
const names = due.map((agent) => agent.name);
|
|
16206
|
-
const subscribed = this.db.select().from(notifications).where(
|
|
16256
|
+
const subscribed = this.db.select().from(notifications).where(eq27(notifications.projectId, projectId)).all().filter((n) => n.enabled && n.config.url && n.config.events.includes("traffic.agent.discovered"));
|
|
16207
16257
|
if (subscribed.length === 0) {
|
|
16208
16258
|
recordUnrecognizedAgentDecisions(this.db, projectId, names, UnrecognizedAgentNotifyStates.skipped, now);
|
|
16209
16259
|
continue;
|
|
16210
16260
|
}
|
|
16211
|
-
const project = this.db.select().from(projects).where(
|
|
16261
|
+
const project = this.db.select().from(projects).where(eq27(projects.id, projectId)).get();
|
|
16212
16262
|
if (!project) continue;
|
|
16213
16263
|
const payload = {
|
|
16214
16264
|
source: "canonry",
|
|
@@ -16247,12 +16297,12 @@ var Notifier = class {
|
|
|
16247
16297
|
* observable in tests.
|
|
16248
16298
|
*/
|
|
16249
16299
|
async onSiteAuditCompleted(runId, projectId) {
|
|
16250
|
-
const run = this.db.select().from(runs).where(
|
|
16300
|
+
const run = this.db.select().from(runs).where(eq27(runs.id, runId)).get();
|
|
16251
16301
|
if (!run || run.kind !== RunKinds["site-audit"]) return null;
|
|
16252
16302
|
if (run.status !== RunStatuses.completed && run.status !== RunStatuses.partial) return null;
|
|
16253
|
-
const subscribed = this.db.select().from(notifications).where(
|
|
16303
|
+
const subscribed = this.db.select().from(notifications).where(eq27(notifications.projectId, projectId)).all().filter((n) => n.enabled && n.config.url && n.config.events.includes("site-health.issue.new"));
|
|
16254
16304
|
if (subscribed.length === 0) return null;
|
|
16255
|
-
const project = this.db.select().from(projects).where(
|
|
16305
|
+
const project = this.db.select().from(projects).where(eq27(projects.id, projectId)).get();
|
|
16256
16306
|
if (!project) return null;
|
|
16257
16307
|
const scan = readSiteHealthScan(this.db, projectId, runId);
|
|
16258
16308
|
if (!scan) return null;
|
|
@@ -16378,7 +16428,7 @@ var Notifier = class {
|
|
|
16378
16428
|
};
|
|
16379
16429
|
|
|
16380
16430
|
// src/run-coordinator.ts
|
|
16381
|
-
import { eq as
|
|
16431
|
+
import { eq as eq28 } from "drizzle-orm";
|
|
16382
16432
|
var log25 = createLogger("RunCoordinator");
|
|
16383
16433
|
function reportInsightsGenerated(runTrigger, outcome) {
|
|
16384
16434
|
trackFeatureCompleted({
|
|
@@ -16412,7 +16462,7 @@ var RunCoordinator = class {
|
|
|
16412
16462
|
*/
|
|
16413
16463
|
onAnswersRecorded;
|
|
16414
16464
|
async onRunCompleted(runId, projectId, opts) {
|
|
16415
|
-
const runRow = this.db.select().from(runs).where(
|
|
16465
|
+
const runRow = this.db.select().from(runs).where(eq28(runs.id, runId)).get();
|
|
16416
16466
|
const kind = runRow?.kind ?? RunKinds["answer-visibility"];
|
|
16417
16467
|
if (runRow?.trigger === RunTriggers.probe) {
|
|
16418
16468
|
log25.info("probe.skip-side-effects", { runId, projectId, kind });
|
|
@@ -16539,7 +16589,7 @@ var RunCoordinator = class {
|
|
|
16539
16589
|
* so the Aero queue is never starved of a follow-up.
|
|
16540
16590
|
*/
|
|
16541
16591
|
buildDiscoveryAeroContext(runId, projectId, status, error) {
|
|
16542
|
-
const session = this.db.select().from(discoverySessions).where(
|
|
16592
|
+
const session = this.db.select().from(discoverySessions).where(eq28(discoverySessions.runId, runId)).get();
|
|
16543
16593
|
const competitorMap = session ? session.competitorMap : [];
|
|
16544
16594
|
return {
|
|
16545
16595
|
kind: RunKinds["aeo-discover-probe"],
|
|
@@ -16582,7 +16632,7 @@ function analysisResultFromInsights(insights2) {
|
|
|
16582
16632
|
|
|
16583
16633
|
// src/agent/session-registry.ts
|
|
16584
16634
|
import crypto25 from "crypto";
|
|
16585
|
-
import { eq as
|
|
16635
|
+
import { eq as eq31 } from "drizzle-orm";
|
|
16586
16636
|
|
|
16587
16637
|
// src/agent/remote-mcp.ts
|
|
16588
16638
|
import { Type } from "@earendil-works/pi-ai";
|
|
@@ -16687,7 +16737,7 @@ async function loadExternalMcpTools(servers, opts = {}) {
|
|
|
16687
16737
|
|
|
16688
16738
|
// src/agent/memory-store.ts
|
|
16689
16739
|
import crypto24 from "crypto";
|
|
16690
|
-
import { and as
|
|
16740
|
+
import { and as and25, desc as desc11, eq as eq29, like, sql as sql12, or as or3 } from "drizzle-orm";
|
|
16691
16741
|
var COMPACTION_KEY_PREFIX = "compaction:";
|
|
16692
16742
|
var COMPACTION_NOTES_PER_SESSION = 3;
|
|
16693
16743
|
function rowToDto(row) {
|
|
@@ -16701,7 +16751,7 @@ function rowToDto(row) {
|
|
|
16701
16751
|
};
|
|
16702
16752
|
}
|
|
16703
16753
|
function listMemoryEntries(db, projectId, opts = {}) {
|
|
16704
|
-
const query = db.select().from(agentMemory).where(
|
|
16754
|
+
const query = db.select().from(agentMemory).where(eq29(agentMemory.projectId, projectId)).orderBy(desc11(agentMemory.updatedAt));
|
|
16705
16755
|
const rows = opts.limit === void 0 ? query.all() : query.limit(opts.limit).all();
|
|
16706
16756
|
return rows.map(rowToDto);
|
|
16707
16757
|
}
|
|
@@ -16732,20 +16782,20 @@ function upsertMemoryEntry(db, args) {
|
|
|
16732
16782
|
updatedAt: now
|
|
16733
16783
|
}
|
|
16734
16784
|
}).run();
|
|
16735
|
-
const row = db.select().from(agentMemory).where(
|
|
16785
|
+
const row = db.select().from(agentMemory).where(and25(eq29(agentMemory.projectId, args.projectId), eq29(agentMemory.key, args.key))).get();
|
|
16736
16786
|
if (!row) throw new Error("memory upsert produced no row");
|
|
16737
16787
|
return rowToDto(row);
|
|
16738
16788
|
}
|
|
16739
16789
|
function deleteMemoryEntry(db, projectId, key) {
|
|
16740
|
-
const result = db.delete(agentMemory).where(
|
|
16790
|
+
const result = db.delete(agentMemory).where(and25(eq29(agentMemory.projectId, projectId), eq29(agentMemory.key, key))).run();
|
|
16741
16791
|
const changes = result.changes ?? 0;
|
|
16742
16792
|
return changes > 0;
|
|
16743
16793
|
}
|
|
16744
16794
|
function loadRecentForHydrate(db, projectId, limit, sessionId) {
|
|
16745
16795
|
if (sessionId === void 0) return listMemoryEntries(db, projectId, { limit });
|
|
16746
16796
|
const sessionPrefix = `compaction:${sessionId ?? ""}:`;
|
|
16747
|
-
return db.select().from(agentMemory).where(
|
|
16748
|
-
|
|
16797
|
+
return db.select().from(agentMemory).where(and25(
|
|
16798
|
+
eq29(agentMemory.projectId, projectId),
|
|
16749
16799
|
or3(sql12`substr(${agentMemory.key}, 1, ${COMPACTION_KEY_PREFIX.length}) <> ${COMPACTION_KEY_PREFIX}`, sessionId ? sql12`substr(${agentMemory.key}, 1, ${sessionPrefix.length}) = ${sessionPrefix}` : void 0)
|
|
16750
16800
|
)).orderBy(desc11(agentMemory.updatedAt)).limit(limit).all().map(rowToDto);
|
|
16751
16801
|
}
|
|
@@ -16771,8 +16821,8 @@ function writeCompactionNote(db, args) {
|
|
|
16771
16821
|
}).run();
|
|
16772
16822
|
const sessionPrefix = `${COMPACTION_KEY_PREFIX}${args.sessionId}:`;
|
|
16773
16823
|
const existing = tx.select({ id: agentMemory.id, updatedAt: agentMemory.updatedAt }).from(agentMemory).where(
|
|
16774
|
-
|
|
16775
|
-
|
|
16824
|
+
and25(
|
|
16825
|
+
eq29(agentMemory.projectId, args.projectId),
|
|
16776
16826
|
like(agentMemory.key, `${sessionPrefix}%`)
|
|
16777
16827
|
)
|
|
16778
16828
|
).orderBy(desc11(agentMemory.updatedAt)).all();
|
|
@@ -16780,7 +16830,7 @@ function writeCompactionNote(db, args) {
|
|
|
16780
16830
|
if (stale.length > 0) {
|
|
16781
16831
|
tx.delete(agentMemory).where(sql12`${agentMemory.id} IN (${sql12.join(stale.map((s) => sql12`${s}`), sql12`, `)})`).run();
|
|
16782
16832
|
}
|
|
16783
|
-
const row = tx.select().from(agentMemory).where(
|
|
16833
|
+
const row = tx.select().from(agentMemory).where(and25(eq29(agentMemory.projectId, args.projectId), eq29(agentMemory.key, key))).get();
|
|
16784
16834
|
if (row) inserted = rowToDto(row);
|
|
16785
16835
|
});
|
|
16786
16836
|
if (!inserted) throw new Error("compaction note write produced no row");
|
|
@@ -16854,7 +16904,7 @@ function trackAeroTurn(agent, turn) {
|
|
|
16854
16904
|
}
|
|
16855
16905
|
|
|
16856
16906
|
// src/agent/project-shape.ts
|
|
16857
|
-
import { and as
|
|
16907
|
+
import { and as and26, count as count5, eq as eq30 } from "drizzle-orm";
|
|
16858
16908
|
var ADVANCED_TOOLS = [
|
|
16859
16909
|
"canonry_measurement_portfolio_summary",
|
|
16860
16910
|
"canonry_measurement_overview",
|
|
@@ -16881,10 +16931,10 @@ var ADVANCED_ROUTES = [
|
|
|
16881
16931
|
];
|
|
16882
16932
|
function aeroProjectShape(db, projectId) {
|
|
16883
16933
|
try {
|
|
16884
|
-
const pointer = db.select().from(measurementPlans).where(
|
|
16885
|
-
const version = pointer ? db.select().from(measurementPlanVersions).where(
|
|
16886
|
-
|
|
16887
|
-
|
|
16934
|
+
const pointer = db.select().from(measurementPlans).where(eq30(measurementPlans.projectId, projectId)).get();
|
|
16935
|
+
const version = pointer ? db.select().from(measurementPlanVersions).where(and26(
|
|
16936
|
+
eq30(measurementPlanVersions.projectId, projectId),
|
|
16937
|
+
eq30(measurementPlanVersions.id, pointer.activeVersionId)
|
|
16888
16938
|
)).get() : void 0;
|
|
16889
16939
|
if (version) {
|
|
16890
16940
|
const plan = JSON.parse(version.canonicalJson);
|
|
@@ -16911,7 +16961,7 @@ canonry_visibility_report describes the whole project, not one Property. canonry
|
|
|
16911
16961
|
pinned: ADVANCED_TOOLS
|
|
16912
16962
|
};
|
|
16913
16963
|
}
|
|
16914
|
-
const tracked = db.select({ total: count5() }).from(queries).where(
|
|
16964
|
+
const tracked = db.select({ total: count5() }).from(queries).where(eq30(queries.projectId, projectId)).get()?.total ?? 0;
|
|
16915
16965
|
return {
|
|
16916
16966
|
prompt: `
|
|
16917
16967
|
|
|
@@ -17170,7 +17220,7 @@ var SessionRegistry = class {
|
|
|
17170
17220
|
modelProvider: effectiveProvider,
|
|
17171
17221
|
modelId: effectiveModelId,
|
|
17172
17222
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
17173
|
-
}).where(
|
|
17223
|
+
}).where(eq31(agentSessions.projectId, projectId)).run();
|
|
17174
17224
|
}
|
|
17175
17225
|
const agent2 = createAeroSession({
|
|
17176
17226
|
projectName,
|
|
@@ -17489,7 +17539,7 @@ ${lines.join("\n")}
|
|
|
17489
17539
|
modelProvider: nextProvider,
|
|
17490
17540
|
modelId: nextModelId,
|
|
17491
17541
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
17492
|
-
}).where(
|
|
17542
|
+
}).where(eq31(agentSessions.projectId, projectId)).run();
|
|
17493
17543
|
}
|
|
17494
17544
|
/** Snapshot pending work without consuming it before an atomic conversation switch. */
|
|
17495
17545
|
prepareConversationChange(projectName) {
|
|
@@ -17714,11 +17764,11 @@ ${lines.join("\n")}
|
|
|
17714
17764
|
return id;
|
|
17715
17765
|
}
|
|
17716
17766
|
tryResolveProjectId(projectName) {
|
|
17717
|
-
const row = this.opts.db.select({ id: projects.id }).from(projects).where(
|
|
17767
|
+
const row = this.opts.db.select({ id: projects.id }).from(projects).where(eq31(projects.name, projectName)).get();
|
|
17718
17768
|
return row?.id;
|
|
17719
17769
|
}
|
|
17720
17770
|
loadRow(projectId) {
|
|
17721
|
-
const row = this.opts.db.select().from(agentSessions).where(
|
|
17771
|
+
const row = this.opts.db.select().from(agentSessions).where(eq31(agentSessions.projectId, projectId)).get();
|
|
17722
17772
|
return row ?? null;
|
|
17723
17773
|
}
|
|
17724
17774
|
insertRow(params) {
|
|
@@ -17738,7 +17788,7 @@ ${lines.join("\n")}
|
|
|
17738
17788
|
}
|
|
17739
17789
|
updateRow(projectId, patch) {
|
|
17740
17790
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
17741
|
-
this.opts.db.update(agentSessions).set({ ...patch, updatedAt: now }).where(
|
|
17791
|
+
this.opts.db.update(agentSessions).set({ ...patch, updatedAt: now }).where(eq31(agentSessions.projectId, projectId)).run();
|
|
17742
17792
|
}
|
|
17743
17793
|
/**
|
|
17744
17794
|
* Store a refreshed prompt snapshot without touching `updatedAt`, which the
|
|
@@ -17747,13 +17797,13 @@ ${lines.join("\n")}
|
|
|
17747
17797
|
* recently used right after an upgrade.
|
|
17748
17798
|
*/
|
|
17749
17799
|
persistPromptSnapshot(projectId, systemPrompt) {
|
|
17750
|
-
this.opts.db.update(agentSessions).set({ systemPrompt }).where(
|
|
17800
|
+
this.opts.db.update(agentSessions).set({ systemPrompt }).where(eq31(agentSessions.projectId, projectId)).run();
|
|
17751
17801
|
}
|
|
17752
17802
|
};
|
|
17753
17803
|
|
|
17754
17804
|
// src/agent/viewer-sessions.ts
|
|
17755
17805
|
import crypto26 from "crypto";
|
|
17756
|
-
import { and as
|
|
17806
|
+
import { and as and27, eq as eq32, isNotNull as isNotNull4, like as like2 } from "drizzle-orm";
|
|
17757
17807
|
var AERO_VIEWER_EXCLUDED_MCP_TOOLS = /* @__PURE__ */ new Set([
|
|
17758
17808
|
CanonryMcpToolNames.canonry_ads_account,
|
|
17759
17809
|
CanonryMcpToolNames.canonry_ads_geo_search,
|
|
@@ -17930,12 +17980,12 @@ var ViewerAeroSessions = class {
|
|
|
17930
17980
|
*/
|
|
17931
17981
|
removeKey(id) {
|
|
17932
17982
|
try {
|
|
17933
|
-
this.opts.db.delete(apiKeys).where(
|
|
17983
|
+
this.opts.db.delete(apiKeys).where(eq32(apiKeys.id, id)).run();
|
|
17934
17984
|
} catch {
|
|
17935
17985
|
try {
|
|
17936
17986
|
this.opts.db.transaction((tx) => {
|
|
17937
|
-
tx.delete(dashboardSessions).where(
|
|
17938
|
-
tx.update(apiKeys).set({ revokedAt: new Date(this.now()).toISOString() }).where(
|
|
17987
|
+
tx.delete(dashboardSessions).where(eq32(dashboardSessions.apiKeyId, id)).run();
|
|
17988
|
+
tx.update(apiKeys).set({ revokedAt: new Date(this.now()).toISOString() }).where(eq32(apiKeys.id, id)).run();
|
|
17939
17989
|
});
|
|
17940
17990
|
} catch {
|
|
17941
17991
|
}
|
|
@@ -17948,7 +17998,7 @@ var ViewerAeroSessions = class {
|
|
|
17948
17998
|
*/
|
|
17949
17999
|
revokeOrphanedKeys() {
|
|
17950
18000
|
try {
|
|
17951
|
-
this.opts.db.delete(apiKeys).where(
|
|
18001
|
+
this.opts.db.delete(apiKeys).where(and27(like2(apiKeys.name, `${VIEWER_AERO_KEY_PREFIX}%`), isNotNull4(apiKeys.delegatedUserId))).run();
|
|
17952
18002
|
} catch {
|
|
17953
18003
|
}
|
|
17954
18004
|
}
|
|
@@ -17965,7 +18015,7 @@ var ViewerAeroSessions = class {
|
|
|
17965
18015
|
// src/mcp-http.ts
|
|
17966
18016
|
import crypto27 from "crypto";
|
|
17967
18017
|
import { isIP } from "net";
|
|
17968
|
-
import { and as
|
|
18018
|
+
import { and as and28, eq as eq33, isNull as isNull6 } from "drizzle-orm";
|
|
17969
18019
|
import { NodeStreamableHTTPServerTransport, toNodeHandler, toWebRequest } from "@modelcontextprotocol/node";
|
|
17970
18020
|
import { McpServer, createMcpHandler, isInitializeRequest, isLegacyRequest } from "@modelcontextprotocol/server";
|
|
17971
18021
|
var SESSION_IDLE_MS = 10 * 60 * 1e3;
|
|
@@ -18071,12 +18121,12 @@ function registerMcpHttpRoutes(scope, opts) {
|
|
|
18071
18121
|
try {
|
|
18072
18122
|
withoutLockWait(() => {
|
|
18073
18123
|
opts.db.transaction((tx) => {
|
|
18074
|
-
tx.delete(dashboardSessions).where(
|
|
18075
|
-
tx.update(apiKeys).set({ revokedAt: new Date(now()).toISOString() }).where(
|
|
18124
|
+
tx.delete(dashboardSessions).where(eq33(dashboardSessions.apiKeyId, id)).run();
|
|
18125
|
+
tx.update(apiKeys).set({ revokedAt: new Date(now()).toISOString() }).where(and28(eq33(apiKeys.id, id), isNull6(apiKeys.revokedAt))).run();
|
|
18076
18126
|
});
|
|
18077
18127
|
if (remove) {
|
|
18078
18128
|
try {
|
|
18079
|
-
opts.db.delete(apiKeys).where(
|
|
18129
|
+
opts.db.delete(apiKeys).where(eq33(apiKeys.id, id)).run();
|
|
18080
18130
|
} catch (error) {
|
|
18081
18131
|
scope.log.warn({ err: error, keyId: id }, "Retained revoked MCP request key after deletion failed");
|
|
18082
18132
|
}
|
|
@@ -18127,14 +18177,14 @@ function registerMcpHttpRoutes(scope, opts) {
|
|
|
18127
18177
|
withoutLockWait(() => {
|
|
18128
18178
|
const cutoff = now() - SESSION_IDLE_MS;
|
|
18129
18179
|
for (const session of legacyServers) {
|
|
18130
|
-
const key = session.sessionKeyId ? opts.db.select({ revokedAt: apiKeys.revokedAt }).from(apiKeys).where(
|
|
18180
|
+
const key = session.sessionKeyId ? opts.db.select({ revokedAt: apiKeys.revokedAt }).from(apiKeys).where(eq33(apiKeys.id, session.sessionKeyId)).get() : void 0;
|
|
18131
18181
|
if (session.lastSeenAt < cutoff || session.expiresAt !== void 0 && session.expiresAt <= now() || session.sessionKeyId && (!key || key.revokedAt)) {
|
|
18132
18182
|
void session.close().catch((error) => scope.log.error({ err: error }, "Failed to close expired MCP session"));
|
|
18133
18183
|
}
|
|
18134
18184
|
}
|
|
18135
18185
|
for (const exchange of modernExchanges) {
|
|
18136
18186
|
if (!exchange.sessionKey) continue;
|
|
18137
|
-
const key = opts.db.select({ revokedAt: apiKeys.revokedAt }).from(apiKeys).where(
|
|
18187
|
+
const key = opts.db.select({ revokedAt: apiKeys.revokedAt }).from(apiKeys).where(eq33(apiKeys.id, exchange.sessionKey.id)).get();
|
|
18138
18188
|
if (!key || key.revokedAt || exchange.sessionKey.expiresAt <= now()) {
|
|
18139
18189
|
void exchange.close().catch((error) => scope.log.error({ err: error }, "Failed to close expired MCP request"));
|
|
18140
18190
|
}
|
|
@@ -18384,7 +18434,7 @@ function registerMcpHttpRoutes(scope, opts) {
|
|
|
18384
18434
|
}
|
|
18385
18435
|
|
|
18386
18436
|
// src/agent/agent-routes.ts
|
|
18387
|
-
import { eq as
|
|
18437
|
+
import { eq as eq34 } from "drizzle-orm";
|
|
18388
18438
|
function viewerAeroCaller(request, opts) {
|
|
18389
18439
|
if (!opts.viewerSessions) return null;
|
|
18390
18440
|
const principal = request.principal;
|
|
@@ -18424,7 +18474,7 @@ function turnAttribution(request) {
|
|
|
18424
18474
|
};
|
|
18425
18475
|
}
|
|
18426
18476
|
function resolveProject(db, name) {
|
|
18427
|
-
const row = db.select({ id: projects.id, name: projects.name }).from(projects).where(
|
|
18477
|
+
const row = db.select({ id: projects.id, name: projects.name }).from(projects).where(eq34(projects.name, name)).get();
|
|
18428
18478
|
if (!row) throw notFound("project", name);
|
|
18429
18479
|
return row;
|
|
18430
18480
|
}
|
|
@@ -18543,7 +18593,7 @@ function registerAgentRoutes(app, opts) {
|
|
|
18543
18593
|
}
|
|
18544
18594
|
requireInstanceAdministrator(request);
|
|
18545
18595
|
const project = resolveProject(opts.db, request.params.name);
|
|
18546
|
-
const row = opts.db.select().from(agentSessions).where(
|
|
18596
|
+
const row = opts.db.select().from(agentSessions).where(eq34(agentSessions.projectId, project.id)).get();
|
|
18547
18597
|
const nextTurn = opts.sessionRegistry.nextTurnModel(project.name);
|
|
18548
18598
|
if (!row) {
|
|
18549
18599
|
return { conversationId: null, messages: [], modelProvider: null, modelId: null, nextTurn, updatedAt: null };
|
|
@@ -18585,7 +18635,7 @@ function registerAgentRoutes(app, opts) {
|
|
|
18585
18635
|
const project = resolveProject(opts.db, request.params.name);
|
|
18586
18636
|
if (opts.sessionRegistry.isBusy(project.name)) throw agentBusy(project.name);
|
|
18587
18637
|
opts.sessionRegistry.reset(project.name);
|
|
18588
|
-
opts.db.update(agentSessions).set({ messages: "[]", followUpQueue: "[]", updatedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(
|
|
18638
|
+
opts.db.update(agentSessions).set({ messages: "[]", followUpQueue: "[]", updatedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(eq34(agentSessions.projectId, project.id)).run();
|
|
18589
18639
|
return { status: "reset" };
|
|
18590
18640
|
}
|
|
18591
18641
|
);
|
|
@@ -18609,7 +18659,7 @@ function registerAgentRoutes(app, opts) {
|
|
|
18609
18659
|
return streamViewerTurn(opts, project, viewerId, body, reply, turnAttribution(request));
|
|
18610
18660
|
}
|
|
18611
18661
|
if (body.conversationId !== void 0) {
|
|
18612
|
-
const current = opts.db.select({ id: agentSessions.id }).from(agentSessions).where(
|
|
18662
|
+
const current = opts.db.select({ id: agentSessions.id }).from(agentSessions).where(eq34(agentSessions.projectId, project.id)).get();
|
|
18613
18663
|
if ((current?.id ?? null) !== body.conversationId) throw validationError("The active conversation changed. Reload the conversation before sending.");
|
|
18614
18664
|
}
|
|
18615
18665
|
const promptText = body.prompt;
|
|
@@ -19569,7 +19619,7 @@ function clipText(value, length) {
|
|
|
19569
19619
|
}
|
|
19570
19620
|
|
|
19571
19621
|
// src/research-runner.ts
|
|
19572
|
-
import { and as
|
|
19622
|
+
import { and as and29, eq as eq35, inArray as inArray13, sql as sql13 } from "drizzle-orm";
|
|
19573
19623
|
var unfinishedResearchQueryStatuses = [ResearchQueryStatuses.queued, ResearchQueryStatuses.running];
|
|
19574
19624
|
function finalResearchRunStatus(completed, failed, waiting) {
|
|
19575
19625
|
if (waiting > 0) return ResearchRunStatuses["waiting-upstream"];
|
|
@@ -19577,13 +19627,13 @@ function finalResearchRunStatus(completed, failed, waiting) {
|
|
|
19577
19627
|
return completed > 0 ? ResearchRunStatuses.partial : ResearchRunStatuses.failed;
|
|
19578
19628
|
}
|
|
19579
19629
|
async function executeResearchRun(db, registry, runId, projectId, options = {}) {
|
|
19580
|
-
const run = db.select().from(researchRuns).where(
|
|
19630
|
+
const run = db.select().from(researchRuns).where(and29(eq35(researchRuns.id, runId), eq35(researchRuns.projectId, projectId))).get();
|
|
19581
19631
|
if (!run || run.status !== ResearchRunStatuses.queued) return;
|
|
19582
|
-
const project = db.select().from(projects).where(
|
|
19632
|
+
const project = db.select().from(projects).where(eq35(projects.id, projectId)).get();
|
|
19583
19633
|
if (!project) return;
|
|
19584
19634
|
const provider = registry.get(run.provider);
|
|
19585
19635
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
19586
|
-
const claim = db.update(researchRuns).set({ status: ResearchRunStatuses.running, startedAt: now }).where(
|
|
19636
|
+
const claim = db.update(researchRuns).set({ status: ResearchRunStatuses.running, startedAt: now }).where(and29(eq35(researchRuns.id, runId), eq35(researchRuns.status, ResearchRunStatuses.queued))).run();
|
|
19587
19637
|
if (claim.changes !== 1) return;
|
|
19588
19638
|
const elapsed = startOutcomeTimer();
|
|
19589
19639
|
const requested = currentOutcomeOrigin();
|
|
@@ -19617,8 +19667,8 @@ async function executeResearchRun(db, registry, runId, projectId, options = {})
|
|
|
19617
19667
|
}
|
|
19618
19668
|
reserved = run.totalQueries;
|
|
19619
19669
|
reservation = { scope, period };
|
|
19620
|
-
const rows = db.select().from(researchRunQueries).where(
|
|
19621
|
-
const competitorRows = db.select(competitorIdentityColumns).from(competitors).where(
|
|
19670
|
+
const rows = db.select().from(researchRunQueries).where(eq35(researchRunQueries.researchRunId, runId)).orderBy(researchRunQueries.position).all();
|
|
19671
|
+
const competitorRows = db.select(competitorIdentityColumns).from(competitors).where(eq35(competitors.projectId, projectId)).orderBy(competitors.domain).all();
|
|
19622
19672
|
const competitorDomains = competitorRows.map((row) => row.domain);
|
|
19623
19673
|
const competitorAliases = new Map(competitorRows.map((row) => [row.domain, competitorIdentityAliases(row)]).filter(([, aliases]) => aliases.length > 0));
|
|
19624
19674
|
const domains = effectiveDomains(project);
|
|
@@ -19628,7 +19678,7 @@ async function executeResearchRun(db, registry, runId, projectId, options = {})
|
|
|
19628
19678
|
await mapWithConcurrency(rows, Math.max(1, provider.config.quotaPolicy.maxConcurrency), async (row) => {
|
|
19629
19679
|
try {
|
|
19630
19680
|
const startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
19631
|
-
db.update(researchRunQueries).set({ status: ResearchQueryStatuses.running, startedAt }).where(
|
|
19681
|
+
db.update(researchRunQueries).set({ status: ResearchQueryStatuses.running, startedAt }).where(and29(eq35(researchRunQueries.id, row.id), eq35(researchRunQueries.status, ResearchQueryStatuses.queued))).run();
|
|
19632
19682
|
const raw = await gate.run(async () => {
|
|
19633
19683
|
dispatched++;
|
|
19634
19684
|
return provider.adapter.executeTrackedQuery({ query: row.queryText, canonicalDomains: domains, competitorDomains, ...run.location ? { location: run.location } : {} }, config);
|
|
@@ -19659,7 +19709,7 @@ async function executeResearchRun(db, registry, runId, projectId, options = {})
|
|
|
19659
19709
|
rawResponse: raw.rawResponse,
|
|
19660
19710
|
answerEvidence: raw.evidence ?? null,
|
|
19661
19711
|
finishedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
19662
|
-
}).where(
|
|
19712
|
+
}).where(and29(eq35(researchRunQueries.id, row.id), eq35(researchRunQueries.status, ResearchQueryStatuses.running))).run();
|
|
19663
19713
|
if (completed.changes === 1) incrementResearchProgress(db, runId, "completedQueries");
|
|
19664
19714
|
} catch (error) {
|
|
19665
19715
|
if (!(error instanceof IncompleteAnswerError && error.resumable)) queryFailure ??= error;
|
|
@@ -19710,11 +19760,11 @@ function researchOutcomeStatus(status) {
|
|
|
19710
19760
|
}
|
|
19711
19761
|
}
|
|
19712
19762
|
function incrementResearchProgress(db, runId, column) {
|
|
19713
|
-
db.update(researchRuns).set({ [column]: sql13`${researchRuns[column]} + 1` }).where(
|
|
19763
|
+
db.update(researchRuns).set({ [column]: sql13`${researchRuns[column]} + 1` }).where(eq35(researchRuns.id, runId)).run();
|
|
19714
19764
|
}
|
|
19715
19765
|
function markResearchQueryFailed(db, runId, queryId, error) {
|
|
19716
19766
|
const message = describeError(error);
|
|
19717
|
-
const unfinished =
|
|
19767
|
+
const unfinished = and29(eq35(researchRunQueries.id, queryId), inArray13(researchRunQueries.status, unfinishedResearchQueryStatuses));
|
|
19718
19768
|
if (error instanceof IncompleteAnswerError && error.resumable) {
|
|
19719
19769
|
db.update(researchRunQueries).set({
|
|
19720
19770
|
status: ResearchQueryStatuses["waiting-upstream"],
|
|
@@ -19730,10 +19780,10 @@ function markResearchQueryFailed(db, runId, queryId, error) {
|
|
|
19730
19780
|
if (failed.changes === 1) incrementResearchProgress(db, runId, "failedQueries");
|
|
19731
19781
|
}
|
|
19732
19782
|
function markUnfinishedResearchQueriesFailed(db, runId, error) {
|
|
19733
|
-
db.update(researchRunQueries).set({ status: ResearchQueryStatuses.failed, error, finishedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(
|
|
19783
|
+
db.update(researchRunQueries).set({ status: ResearchQueryStatuses.failed, error, finishedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(and29(eq35(researchRunQueries.researchRunId, runId), inArray13(researchRunQueries.status, unfinishedResearchQueryStatuses))).run();
|
|
19734
19784
|
}
|
|
19735
19785
|
function finalizeResearchRun(db, runId, fatalError) {
|
|
19736
|
-
const rows = db.select({ status: researchRunQueries.status }).from(researchRunQueries).where(
|
|
19786
|
+
const rows = db.select({ status: researchRunQueries.status }).from(researchRunQueries).where(eq35(researchRunQueries.researchRunId, runId)).all();
|
|
19737
19787
|
const completed = rows.filter((row) => row.status === ResearchQueryStatuses.completed).length;
|
|
19738
19788
|
const failed = rows.filter((row) => row.status === ResearchQueryStatuses.failed).length;
|
|
19739
19789
|
const waiting = rows.filter((row) => row.status === ResearchQueryStatuses["waiting-upstream"]).length;
|
|
@@ -19745,12 +19795,12 @@ function finalizeResearchRun(db, runId, fatalError) {
|
|
|
19745
19795
|
error: fatalError ?? (failed === rows.length ? "Every research query failed." : null),
|
|
19746
19796
|
// A run waiting on upstream jobs has not finished.
|
|
19747
19797
|
finishedAt: waiting > 0 ? null : (/* @__PURE__ */ new Date()).toISOString()
|
|
19748
|
-
}).where(
|
|
19798
|
+
}).where(eq35(researchRuns.id, runId)).run();
|
|
19749
19799
|
return { status, total: rows.length, completed, failed };
|
|
19750
19800
|
}
|
|
19751
19801
|
|
|
19752
19802
|
// src/google-marketing-sync.ts
|
|
19753
|
-
import { and as
|
|
19803
|
+
import { and as and30, eq as eq36, isNull as isNull7, sql as sql14 } from "drizzle-orm";
|
|
19754
19804
|
var RUNTIME_FAILURE_REASONS = {
|
|
19755
19805
|
INVALID_INPUT: OutcomeReasonCodes.VALIDATION,
|
|
19756
19806
|
NOT_CONFIGURED: OutcomeReasonCodes.NOT_CONNECTED,
|
|
@@ -19787,10 +19837,10 @@ function assertSnapshotContext(metadata, expected) {
|
|
|
19787
19837
|
}
|
|
19788
19838
|
}
|
|
19789
19839
|
function requireSyncRun(db, runId, projectId, kind) {
|
|
19790
|
-
const run = db.select().from(runs).where(
|
|
19791
|
-
|
|
19792
|
-
|
|
19793
|
-
|
|
19840
|
+
const run = db.select().from(runs).where(and30(
|
|
19841
|
+
eq36(runs.id, runId),
|
|
19842
|
+
eq36(runs.projectId, projectId),
|
|
19843
|
+
eq36(runs.kind, kind)
|
|
19794
19844
|
)).get();
|
|
19795
19845
|
if (!run) throw withOutcomeReason(new Error(`Run ${runId} is not a ${kind} run for this project.`), OutcomeReasonCodes.NOT_FOUND);
|
|
19796
19846
|
if (run.status !== RunStatuses.queued) {
|
|
@@ -19806,19 +19856,19 @@ function markFailed(db, runId, projectId, kind, expectedStatus, error) {
|
|
|
19806
19856
|
status: RunStatuses.failed,
|
|
19807
19857
|
error: serializeRunError({ message: safeErrorMessage(error) }),
|
|
19808
19858
|
finishedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
19809
|
-
}).where(
|
|
19810
|
-
|
|
19811
|
-
|
|
19812
|
-
|
|
19813
|
-
|
|
19859
|
+
}).where(and30(
|
|
19860
|
+
eq36(runs.id, runId),
|
|
19861
|
+
eq36(runs.projectId, projectId),
|
|
19862
|
+
eq36(runs.kind, kind),
|
|
19863
|
+
eq36(runs.status, expectedStatus)
|
|
19814
19864
|
)).run();
|
|
19815
19865
|
}
|
|
19816
19866
|
function claimSyncRun(db, runId, projectId, kind, startedAt) {
|
|
19817
|
-
const claim = db.update(runs).set({ status: RunStatuses.running, startedAt, error: null }).where(
|
|
19818
|
-
|
|
19819
|
-
|
|
19820
|
-
|
|
19821
|
-
|
|
19867
|
+
const claim = db.update(runs).set({ status: RunStatuses.running, startedAt, error: null }).where(and30(
|
|
19868
|
+
eq36(runs.id, runId),
|
|
19869
|
+
eq36(runs.projectId, projectId),
|
|
19870
|
+
eq36(runs.kind, kind),
|
|
19871
|
+
eq36(runs.status, RunStatuses.queued)
|
|
19822
19872
|
)).run();
|
|
19823
19873
|
return claim.changes === 1;
|
|
19824
19874
|
}
|
|
@@ -19828,9 +19878,9 @@ async function executeGoogleAdsMarketingSync(db, runtime, runId, projectId) {
|
|
|
19828
19878
|
let evidenceCollected = false;
|
|
19829
19879
|
try {
|
|
19830
19880
|
requireSyncRun(db, runId, projectId, RunKinds["google-ads-sync"]);
|
|
19831
|
-
const project = db.select().from(projects).where(
|
|
19881
|
+
const project = db.select().from(projects).where(eq36(projects.id, projectId)).get();
|
|
19832
19882
|
if (!project) throw withOutcomeReason(new Error(`Project ${projectId} was not found.`), OutcomeReasonCodes.NOT_FOUND);
|
|
19833
|
-
const connection = db.select().from(googleAdsConnections).where(
|
|
19883
|
+
const connection = db.select().from(googleAdsConnections).where(eq36(googleAdsConnections.projectId, projectId)).get();
|
|
19834
19884
|
if (!connection?.selectedCustomerId) {
|
|
19835
19885
|
throw withOutcomeReason(new Error("Google Ads needs a selected customer before it can sync."), OutcomeReasonCodes.ACCOUNT_NOT_FOUND);
|
|
19836
19886
|
}
|
|
@@ -19882,11 +19932,11 @@ async function executeGoogleAdsMarketingSync(db, runtime, runId, projectId) {
|
|
|
19882
19932
|
status: RunStatuses.completed,
|
|
19883
19933
|
finishedAt,
|
|
19884
19934
|
error: null
|
|
19885
|
-
}).where(
|
|
19886
|
-
|
|
19887
|
-
|
|
19888
|
-
|
|
19889
|
-
|
|
19935
|
+
}).where(and30(
|
|
19936
|
+
eq36(runs.id, runId),
|
|
19937
|
+
eq36(runs.projectId, projectId),
|
|
19938
|
+
eq36(runs.kind, RunKinds["google-ads-sync"]),
|
|
19939
|
+
eq36(runs.status, RunStatuses.running)
|
|
19890
19940
|
)).run();
|
|
19891
19941
|
if (terminalClaim.changes !== 1) {
|
|
19892
19942
|
throw withOutcomeReason(
|
|
@@ -19906,12 +19956,12 @@ async function executeGoogleAdsMarketingSync(db, runtime, runId, projectId) {
|
|
|
19906
19956
|
lastMetricsSnapshotAt: result.metrics?.metadata.capturedAt ?? connection.lastMetricsSnapshotAt,
|
|
19907
19957
|
lastMetricsSnapshotId: result.metrics?.metadata.id ?? connection.lastMetricsSnapshotId,
|
|
19908
19958
|
updatedAt: finishedAt
|
|
19909
|
-
}).where(
|
|
19910
|
-
|
|
19911
|
-
|
|
19912
|
-
|
|
19913
|
-
connection.selectedLoginCustomerId === null ? isNull7(googleAdsConnections.selectedLoginCustomerId) :
|
|
19914
|
-
|
|
19959
|
+
}).where(and30(
|
|
19960
|
+
eq36(googleAdsConnections.id, connection.id),
|
|
19961
|
+
eq36(googleAdsConnections.projectId, projectId),
|
|
19962
|
+
eq36(googleAdsConnections.selectedCustomerId, connection.selectedCustomerId),
|
|
19963
|
+
connection.selectedLoginCustomerId === null ? isNull7(googleAdsConnections.selectedLoginCustomerId) : eq36(googleAdsConnections.selectedLoginCustomerId, connection.selectedLoginCustomerId),
|
|
19964
|
+
eq36(googleAdsConnections.selectionGeneration, connection.selectionGeneration)
|
|
19915
19965
|
)).run();
|
|
19916
19966
|
if (connectionClaim.changes !== 1) {
|
|
19917
19967
|
throw withOutcomeReason(
|
|
@@ -19951,9 +20001,9 @@ async function executeGtmMarketingSync(db, runtime, runId, projectId) {
|
|
|
19951
20001
|
let evidenceCollected = false;
|
|
19952
20002
|
try {
|
|
19953
20003
|
requireSyncRun(db, runId, projectId, RunKinds["gtm-sync"]);
|
|
19954
|
-
const project = db.select().from(projects).where(
|
|
20004
|
+
const project = db.select().from(projects).where(eq36(projects.id, projectId)).get();
|
|
19955
20005
|
if (!project) throw withOutcomeReason(new Error(`Project ${projectId} was not found.`), OutcomeReasonCodes.NOT_FOUND);
|
|
19956
|
-
const connection = db.select().from(gtmConnections).where(
|
|
20006
|
+
const connection = db.select().from(gtmConnections).where(eq36(gtmConnections.projectId, projectId)).get();
|
|
19957
20007
|
if (!connection?.selectedAccountId || !connection.selectedContainerId) {
|
|
19958
20008
|
throw withOutcomeReason(new Error("GTM needs a selected account and container before it can sync."), OutcomeReasonCodes.ACCOUNT_NOT_FOUND);
|
|
19959
20009
|
}
|
|
@@ -19991,11 +20041,11 @@ async function executeGtmMarketingSync(db, runtime, runId, projectId) {
|
|
|
19991
20041
|
status: RunStatuses.completed,
|
|
19992
20042
|
finishedAt,
|
|
19993
20043
|
error: null
|
|
19994
|
-
}).where(
|
|
19995
|
-
|
|
19996
|
-
|
|
19997
|
-
|
|
19998
|
-
|
|
20044
|
+
}).where(and30(
|
|
20045
|
+
eq36(runs.id, runId),
|
|
20046
|
+
eq36(runs.projectId, projectId),
|
|
20047
|
+
eq36(runs.kind, RunKinds["gtm-sync"]),
|
|
20048
|
+
eq36(runs.status, RunStatuses.running)
|
|
19999
20049
|
)).run();
|
|
20000
20050
|
if (terminalClaim.changes !== 1) {
|
|
20001
20051
|
throw withOutcomeReason(
|
|
@@ -20016,13 +20066,13 @@ async function executeGtmMarketingSync(db, runtime, runId, projectId) {
|
|
|
20016
20066
|
lastSnapshotAt: snapshot.metadata.capturedAt,
|
|
20017
20067
|
lastSnapshotId: snapshot.metadata.id,
|
|
20018
20068
|
updatedAt: finishedAt
|
|
20019
|
-
}).where(
|
|
20020
|
-
|
|
20021
|
-
|
|
20022
|
-
|
|
20023
|
-
|
|
20024
|
-
connection.selectedWorkspaceId === null ? isNull7(gtmConnections.selectedWorkspaceId) :
|
|
20025
|
-
|
|
20069
|
+
}).where(and30(
|
|
20070
|
+
eq36(gtmConnections.id, connection.id),
|
|
20071
|
+
eq36(gtmConnections.projectId, projectId),
|
|
20072
|
+
eq36(gtmConnections.selectedAccountId, connection.selectedAccountId),
|
|
20073
|
+
eq36(gtmConnections.selectedContainerId, connection.selectedContainerId),
|
|
20074
|
+
connection.selectedWorkspaceId === null ? isNull7(gtmConnections.selectedWorkspaceId) : eq36(gtmConnections.selectedWorkspaceId, connection.selectedWorkspaceId),
|
|
20075
|
+
eq36(gtmConnections.selectionGeneration, connection.selectionGeneration)
|
|
20026
20076
|
)).run();
|
|
20027
20077
|
if (connectionClaim.changes !== 1) {
|
|
20028
20078
|
throw withOutcomeReason(
|
|
@@ -20678,7 +20728,7 @@ async function createServer(opts) {
|
|
|
20678
20728
|
async (ctx) => {
|
|
20679
20729
|
if (!sessionRegistry) return;
|
|
20680
20730
|
if (!agentProactive) return;
|
|
20681
|
-
const project = opts.db.select({ name: projects.name }).from(projects).where(
|
|
20731
|
+
const project = opts.db.select({ name: projects.name }).from(projects).where(eq37(projects.id, ctx.projectId)).get();
|
|
20682
20732
|
if (!project) return;
|
|
20683
20733
|
let content;
|
|
20684
20734
|
if (ctx.kind === RunKinds["aeo-discover-probe"]) {
|
|
@@ -21152,7 +21202,7 @@ async function createServer(opts) {
|
|
|
21152
21202
|
void (async () => {
|
|
21153
21203
|
try {
|
|
21154
21204
|
const report = await schedulerClient.runDoctor({ project: projectName, checkIds: scheduledHealthCheckIds() });
|
|
21155
|
-
const project = opts.db.select().from(projects).where(
|
|
21205
|
+
const project = opts.db.select().from(projects).where(eq37(projects.name, projectName)).get();
|
|
21156
21206
|
if (!project) {
|
|
21157
21207
|
app.log.warn({ projectName }, "doctor schedule fired for an unknown project");
|
|
21158
21208
|
return;
|
|
@@ -21444,7 +21494,7 @@ async function createServer(opts) {
|
|
|
21444
21494
|
})();
|
|
21445
21495
|
if (opts.config.apiKey) {
|
|
21446
21496
|
const keyHash = hashApiKey(opts.config.apiKey);
|
|
21447
|
-
const existing = opts.db.select().from(apiKeys).where(
|
|
21497
|
+
const existing = opts.db.select().from(apiKeys).where(eq37(apiKeys.keyHash, keyHash)).get();
|
|
21448
21498
|
if (!existing) {
|
|
21449
21499
|
const prefix = opts.config.apiKey.slice(0, 12);
|
|
21450
21500
|
opts.db.insert(apiKeys).values({
|
|
@@ -21463,14 +21513,14 @@ async function createServer(opts) {
|
|
|
21463
21513
|
);
|
|
21464
21514
|
const currentPasswordFingerprint = () => hashSessionToken(opts.config.dashboardPasswordHash ?? "");
|
|
21465
21515
|
opts.db.delete(dashboardSessions).where(or4(
|
|
21466
|
-
|
|
21467
|
-
|
|
21516
|
+
eq37(dashboardSessions.apiKeyHash, ""),
|
|
21517
|
+
and31(
|
|
21468
21518
|
isNotNull5(dashboardSessions.passwordFingerprint),
|
|
21469
21519
|
ne4(dashboardSessions.passwordFingerprint, currentPasswordFingerprint())
|
|
21470
21520
|
)
|
|
21471
21521
|
)).run();
|
|
21472
21522
|
if (!opts.config.dashboardPasswordHash) {
|
|
21473
|
-
const lastPasswordEventAt = (action) => opts.db.select({ at: max(auditLog.createdAt) }).from(auditLog).where(
|
|
21523
|
+
const lastPasswordEventAt = (action) => opts.db.select({ at: max(auditLog.createdAt) }).from(auditLog).where(and31(eq37(auditLog.entityType, "dashboard-password"), eq37(auditLog.action, action))).get()?.at ?? null;
|
|
21474
21524
|
const createdAt = lastPasswordEventAt("dashboard-password.created");
|
|
21475
21525
|
const clearedAt = lastPasswordEventAt("dashboard-password.cleared");
|
|
21476
21526
|
if (createdAt !== null && (clearedAt === null || clearedAt < createdAt)) {
|
|
@@ -21504,23 +21554,23 @@ async function createServer(opts) {
|
|
|
21504
21554
|
currentApiKeyHash: apiKeys.keyHash,
|
|
21505
21555
|
passwordFingerprint: dashboardSessions.passwordFingerprint,
|
|
21506
21556
|
expiresAt: dashboardSessions.expiresAt
|
|
21507
|
-
}).from(dashboardSessions).innerJoin(apiKeys,
|
|
21557
|
+
}).from(dashboardSessions).innerJoin(apiKeys, eq37(apiKeys.id, dashboardSessions.apiKeyId)).where(and31(eq37(dashboardSessions.tokenHash, tokenHash), isNull8(apiKeys.revokedAt))).get();
|
|
21508
21558
|
if (!session) return null;
|
|
21509
21559
|
const expiresAt = Date.parse(session.expiresAt);
|
|
21510
21560
|
if (!Number.isFinite(expiresAt) || expiresAt <= Date.now() || session.apiKeyHash !== session.currentApiKeyHash || session.passwordFingerprint !== null && session.passwordFingerprint !== currentPasswordFingerprint()) {
|
|
21511
|
-
opts.db.delete(dashboardSessions).where(
|
|
21561
|
+
opts.db.delete(dashboardSessions).where(eq37(dashboardSessions.tokenHash, tokenHash)).run();
|
|
21512
21562
|
return null;
|
|
21513
21563
|
}
|
|
21514
21564
|
return session.apiKeyId;
|
|
21515
21565
|
};
|
|
21516
21566
|
const clearSession = (sessionId) => {
|
|
21517
21567
|
if (sessionId) {
|
|
21518
|
-
opts.db.delete(dashboardSessions).where(
|
|
21568
|
+
opts.db.delete(dashboardSessions).where(eq37(dashboardSessions.tokenHash, hashSessionToken(sessionId))).run();
|
|
21519
21569
|
}
|
|
21520
21570
|
};
|
|
21521
21571
|
const getDefaultApiKey = () => {
|
|
21522
21572
|
if (!opts.config.apiKey) return void 0;
|
|
21523
|
-
return opts.db.select().from(apiKeys).where(
|
|
21573
|
+
return opts.db.select().from(apiKeys).where(eq37(apiKeys.keyHash, hashApiKey(opts.config.apiKey))).get();
|
|
21524
21574
|
};
|
|
21525
21575
|
const setSessionCookie = (reply, sessionId) => {
|
|
21526
21576
|
reply.header(
|
|
@@ -21681,12 +21731,12 @@ async function createServer(opts) {
|
|
|
21681
21731
|
return reply.send({ authenticated: true });
|
|
21682
21732
|
}
|
|
21683
21733
|
if (apiKey) {
|
|
21684
|
-
const key = opts.db.select().from(apiKeys).where(
|
|
21734
|
+
const key = opts.db.select().from(apiKeys).where(eq37(apiKeys.keyHash, hashApiKey(apiKey))).get();
|
|
21685
21735
|
if (!key || key.revokedAt) {
|
|
21686
21736
|
const err2 = authInvalid();
|
|
21687
21737
|
return reply.status(err2.statusCode).send(err2.toJSON());
|
|
21688
21738
|
}
|
|
21689
|
-
opts.db.update(apiKeys).set({ lastUsedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(
|
|
21739
|
+
opts.db.update(apiKeys).set({ lastUsedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(eq37(apiKeys.id, key.id)).run();
|
|
21690
21740
|
setSessionCookie(reply, createSession(key));
|
|
21691
21741
|
return reply.send({ authenticated: true });
|
|
21692
21742
|
}
|
|
@@ -21764,7 +21814,7 @@ async function createServer(opts) {
|
|
|
21764
21814
|
const recomputeDebts = /* @__PURE__ */ new Map();
|
|
21765
21815
|
let recomputeSequence = 0;
|
|
21766
21816
|
const markAnswerFieldRecompute = (projectId, full) => {
|
|
21767
|
-
opts.db.update(projects).set({ answerFieldsRecompute: full ? "full" : sql15`CASE WHEN ${projects.answerFieldsRecompute} = 'full' THEN 'full' ELSE 'competitors' END` }).where(
|
|
21817
|
+
opts.db.update(projects).set({ answerFieldsRecompute: full ? "full" : sql15`CASE WHEN ${projects.answerFieldsRecompute} = 'full' THEN 'full' ELSE 'competitors' END` }).where(eq37(projects.id, projectId)).run();
|
|
21768
21818
|
};
|
|
21769
21819
|
const requestAnswerFieldRecompute = (projectId, projectName, scope, reason) => {
|
|
21770
21820
|
const sequence = ++recomputeSequence;
|
|
@@ -21782,10 +21832,10 @@ async function createServer(opts) {
|
|
|
21782
21832
|
if (pass.full && debt.latestFull <= sequence) debt.latestFull = 0;
|
|
21783
21833
|
try {
|
|
21784
21834
|
if (debt.latest === sequence && debt.latestFull === 0) {
|
|
21785
|
-
opts.db.update(projects).set({ answerFieldsRecompute: null }).where(
|
|
21835
|
+
opts.db.update(projects).set({ answerFieldsRecompute: null }).where(eq37(projects.id, projectId)).run();
|
|
21786
21836
|
if (recomputeDebts.get(projectId) === debt) recomputeDebts.delete(projectId);
|
|
21787
21837
|
} else if (debt.latestFull === 0) {
|
|
21788
|
-
opts.db.update(projects).set({ answerFieldsRecompute: "competitors" }).where(
|
|
21838
|
+
opts.db.update(projects).set({ answerFieldsRecompute: "competitors" }).where(and31(eq37(projects.id, projectId), eq37(projects.answerFieldsRecompute, "full"))).run();
|
|
21789
21839
|
}
|
|
21790
21840
|
} catch (err) {
|
|
21791
21841
|
app.log.error({ err, projectId }, "could not clear the owed answer field recompute");
|
|
@@ -21822,7 +21872,7 @@ async function createServer(opts) {
|
|
|
21822
21872
|
await yieldToEventLoop4();
|
|
21823
21873
|
const reason = (competitorAutoAliasReasons.get(projectId) ?? []).join(",");
|
|
21824
21874
|
competitorAutoAliasReasons.delete(projectId);
|
|
21825
|
-
const project = opts.db.select().from(projects).where(
|
|
21875
|
+
const project = opts.db.select().from(projects).where(eq37(projects.id, projectId)).get();
|
|
21826
21876
|
if (!project || project.competitorAutoAliases === "off") return;
|
|
21827
21877
|
if (project.competitorAutoAliases === "apply") {
|
|
21828
21878
|
await competitorAutoAliases.request(projectId, reason).catch(() => void 0);
|
|
@@ -22022,8 +22072,8 @@ async function createServer(opts) {
|
|
|
22022
22072
|
getAgentPin: () => describeAgentPin(opts.config),
|
|
22023
22073
|
getGoogleMarketingDoctorInput: (ctx) => {
|
|
22024
22074
|
if (!ctx.project) return null;
|
|
22025
|
-
const googleAds = opts.db.select().from(googleAdsConnections).where(
|
|
22026
|
-
const gtm = opts.db.select().from(gtmConnections).where(
|
|
22075
|
+
const googleAds = opts.db.select().from(googleAdsConnections).where(eq37(googleAdsConnections.projectId, ctx.project.id)).get();
|
|
22076
|
+
const gtm = opts.db.select().from(gtmConnections).where(eq37(gtmConnections.projectId, ctx.project.id)).get();
|
|
22027
22077
|
const googleAdsCredential = getGoogleAdsConnection(opts.config, ctx.project.id);
|
|
22028
22078
|
const gtmCredential = getGtmConnection(opts.config, ctx.project.id);
|
|
22029
22079
|
const googleAdsConnected = Boolean(googleAdsCredential?.accessToken);
|
|
@@ -22273,7 +22323,7 @@ async function createServer(opts) {
|
|
|
22273
22323
|
...inspectOpts,
|
|
22274
22324
|
config: opts.config
|
|
22275
22325
|
}).then(() => {
|
|
22276
|
-
const finished = opts.db.select({ status: runs.status }).from(runs).where(
|
|
22326
|
+
const finished = opts.db.select({ status: runs.status }).from(runs).where(eq37(runs.id, runId)).get();
|
|
22277
22327
|
if (finished?.status === RunStatuses.completed || finished?.status === RunStatuses.partial) {
|
|
22278
22328
|
return maybeRefreshGscCoverage(
|
|
22279
22329
|
opts.db,
|
|
@@ -22785,7 +22835,7 @@ async function createServer(opts) {
|
|
|
22785
22835
|
isBacklinksInstalled: () => isDuckdbInstalled()
|
|
22786
22836
|
});
|
|
22787
22837
|
}
|
|
22788
|
-
for (const run of opts.db.select({ id: researchRuns.id, projectId: researchRuns.projectId }).from(researchRuns).where(
|
|
22838
|
+
for (const run of opts.db.select({ id: researchRuns.id, projectId: researchRuns.projectId }).from(researchRuns).where(eq37(researchRuns.status, ResearchRunStatuses.queued)).all()) {
|
|
22789
22839
|
dispatchResearchRun(run.id, run.projectId, OutcomeTriggers.startup);
|
|
22790
22840
|
}
|
|
22791
22841
|
runtimeStartupSettled = true;
|