@canonry/canonry 7.72.1 → 7.73.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/assets/{AgentSetupPage-D39M9CRK.js → AgentSetupPage-D7YVdPWO.js} +1 -1
- package/assets/assets/{AuditHistoryPanel-BS-iFyHX.js → AuditHistoryPanel-yrgfS5B_.js} +1 -1
- package/assets/assets/{BacklinksPage-BW2bEGF4.js → BacklinksPage-DJM6z03g.js} +1 -1
- package/assets/assets/{HistoryPage-Dh6o2iVd.js → HistoryPage-CNvAVJ1S.js} +1 -1
- package/assets/assets/{MeasurementPropertyPage-DL9cbtVz.js → MeasurementPropertyPage-BVVYhOxR.js} +1 -1
- package/assets/assets/{ProjectPage-DXh97uDN.js → ProjectPage-CPRD8kR9.js} +1 -1
- package/assets/assets/{RunsPage-BM4ETLsm.js → RunsPage-Ck33dcjz.js} +1 -1
- package/assets/assets/{SettingsPage-DpJTB2-Q.js → SettingsPage-CS_t4Dai.js} +1 -1
- package/assets/assets/{SiteHealthSection-CmlMQ17V.js → SiteHealthSection-BurKnHpn.js} +2 -2
- package/assets/assets/ToneBadge-Df8oiKsQ.js +60 -0
- package/assets/assets/{TrafficPage-ClH3Dxnm.js → TrafficPage-DAiY698E.js} +1 -1
- package/assets/assets/{TrafficSourceDetailPage-DE-NyNGz.js → TrafficSourceDetailPage-clJSX2rr.js} +1 -1
- package/assets/assets/{account-context-BwV6ox8O.js → account-context-DDEhuoP2.js} +1 -1
- package/assets/assets/{card-DhkoBNyY.js → card-DbR-lBY3.js} +1 -1
- package/assets/assets/{extract-error-message-dNDtiSBo.js → extract-error-message-7_Vm2DQF.js} +1 -1
- package/assets/assets/{format-helpers-Bt4tx-f3.js → format-helpers-vtahpXTQ.js} +1 -1
- package/assets/assets/{index-MbApCekj.js → index-BIhCeXvc.js} +4 -4
- package/assets/assets/{query-client-D-zMw63p.js → query-client-TlFXYJ9o.js} +1 -1
- package/assets/assets/{react-query-EdiRRB2q.js → react-query-C5Mv8xfD.js} +1 -1
- package/assets/assets/{react-sigma_core.esm.min-Bidr1Gxk.js → react-sigma_core.esm.min-DmggJ7YC.js} +1 -1
- package/assets/assets/{service-BUi8JgGI.js → service-CLFURPuM.js} +1 -1
- package/assets/index.html +7 -7
- package/dist/{ads-FFUUMS4J.js → ads-TX5622SS.js} +3 -3
- package/dist/{agent-LCD2VG7C.js → agent-AIOQL66U.js} +7 -7
- package/dist/{agent-ask-OBHTCYFR.js → agent-ask-A6GIJTBX.js} +3 -3
- package/dist/{agent-conversations-VMZ4NFIX.js → agent-conversations-LHWLKOPQ.js} +3 -3
- package/dist/{agent-memory-UPFJFNWC.js → agent-memory-F54BPEYN.js} +3 -3
- package/dist/{agent-providers-4GOWLBZK.js → agent-providers-L74KZOSG.js} +3 -3
- package/dist/{agent-transcript-RGXIVZUP.js → agent-transcript-IH65H755.js} +3 -3
- package/dist/{analytics-FBK4XV5P.js → analytics-YWCY2KSY.js} +3 -3
- package/dist/{apply-Y2DVE3YB.js → apply-BLUUYG2N.js} +3 -3
- package/dist/{backfill-7SGCPH6J.js → backfill-6WO5CH6Z.js} +9 -9
- package/dist/{backlinks-YWHFC7DZ.js → backlinks-NC7AUGTK.js} +3 -3
- package/dist/{bing-7BP3DWPK.js → bing-WQUEYHVC.js} +3 -3
- package/dist/{bootstrap-Z7QSKVPL.js → bootstrap-AXZV4BBC.js} +10 -10
- package/dist/{cdp-OL6RENO3.js → cdp-DUG5PMUD.js} +7 -7
- package/dist/{chunk-J523Y7X2.js → chunk-2Y6MSB4J.js} +1 -1
- package/dist/{chunk-YFITKHCV.js → chunk-3ANSG6YT.js} +345 -337
- package/dist/{chunk-GA2EHZTI.js → chunk-3WPTMLQI.js} +22 -6
- package/dist/{chunk-QDN3FL2A.js → chunk-3YIKSZHN.js} +2 -1
- package/dist/{chunk-H4HNXCDF.js → chunk-4B6DE7BB.js} +2 -2
- package/dist/{chunk-UWBJWKDQ.js → chunk-4JMTDLFV.js} +2 -2
- package/dist/{chunk-QKMGGM4B.js → chunk-5PA7SJ2M.js} +8 -271
- package/dist/{chunk-JO73L677.js → chunk-6ATKY252.js} +2 -2
- package/dist/{chunk-XR6ZVMYK.js → chunk-6W5AR4IW.js} +1 -1
- package/dist/{chunk-IZMT4WLZ.js → chunk-6YW24AUN.js} +2 -2
- package/dist/chunk-AZQNJX3T.js +269 -0
- package/dist/{chunk-TUVG7BIF.js → chunk-B6R223HQ.js} +8 -8
- package/dist/{chunk-6MYHGP46.js → chunk-DLVGMRM5.js} +1 -1
- package/dist/{chunk-P3UGNR2Y.js → chunk-ETVM3T7K.js} +5 -5
- package/dist/{chunk-IV6VZ63E.js → chunk-F5D4GPZQ.js} +18 -14
- package/dist/{chunk-WJ2JU32O.js → chunk-GNBATQS3.js} +23 -6
- package/dist/{chunk-TEDY3MHN.js → chunk-GUFKUPKZ.js} +3 -3
- package/dist/{chunk-BADJCT6U.js → chunk-GWDMWKM6.js} +1 -1
- package/dist/{chunk-PPM6Z7CM.js → chunk-HEL6DKDN.js} +1 -1
- package/dist/{chunk-DFCKSJQI.js → chunk-HGMTGC34.js} +4 -4
- package/dist/{chunk-GPTRCOKV.js → chunk-HIGBV77K.js} +259 -95
- package/dist/{chunk-NZZWJCKS.js → chunk-HJHFW455.js} +3 -3
- package/dist/{chunk-JE5QURTR.js → chunk-HKV4UWG5.js} +1 -1
- package/dist/{chunk-DRGCVTYH.js → chunk-JPM3VC6A.js} +1 -1
- package/dist/{chunk-TOX6HAQJ.js → chunk-JQNVNB63.js} +3 -3
- package/dist/{chunk-3AG3LPZX.js → chunk-MRSNLKSM.js} +1 -1
- package/dist/{chunk-O7WOCTBD.js → chunk-PAIYFNNY.js} +71 -3
- package/dist/{chunk-MJYO6UK2.js → chunk-PRSQ4R47.js} +2 -2
- package/dist/{chunk-JEBY6MS6.js → chunk-QS6RIP37.js} +1 -1
- package/dist/{chunk-4LTL5RNY.js → chunk-S77W2XSI.js} +4 -4
- package/dist/{chunk-YXAKL5HM.js → chunk-SF5JLMLG.js} +6791 -6777
- package/dist/{chunk-Q25JQANW.js → chunk-T5PWWDMJ.js} +17 -2
- package/dist/{chunk-ATT3ZCSS.js → chunk-WH2M2MQF.js} +38 -16
- package/dist/{chunk-7KOAP47B.js → chunk-WLWOGBLJ.js} +363 -248
- package/dist/{chunk-BZZDFIY2.js → chunk-XDQJOZKJ.js} +1 -1
- package/dist/{citations-RVSC67GE.js → citations-FUZ6DGP5.js} +3 -3
- package/dist/{cli-catalog-NEZE5QU5.js → cli-catalog-BXHG544D.js} +3 -3
- package/dist/{cli-commands-HGCYU4MQ.js → cli-commands-GMOR67F5.js} +7 -7
- package/dist/cli.js +13 -13
- package/dist/{competitor-BX3M3OYS.js → competitor-QDCH7PLT.js} +3 -3
- package/dist/{content-PKJMVMHJ.js → content-JX2CPIPR.js} +3 -3
- package/dist/{dashboard-SVHZBVM6.js → dashboard-OOWMLMKV.js} +6 -6
- package/dist/{demo-HYDPECCK.js → demo-GKSMSKOY.js} +1 -1
- package/dist/{demo-server-KEOO7WFO.js → demo-server-UQO4XKHN.js} +12 -11
- package/dist/{discover-JDP44ZHX.js → discover-YQSVQRUV.js} +3 -3
- package/dist/{discover-eval-6ORCVYYR.js → discover-eval-FN5RL6QI.js} +3 -3
- package/dist/{doctor-TVZRMBSS.js → doctor-YHJLAEJJ.js} +3 -3
- package/dist/{evidence-FNIAFBBG.js → evidence-KUPZMBIK.js} +3 -3
- package/dist/{export-cmd-HQNF2SSD.js → export-cmd-YSEPTUUF.js} +3 -3
- package/dist/{ga-PIO5OXMK.js → ga-WNS7Q7TF.js} +3 -3
- package/dist/{gbp-3R5FME43.js → gbp-5NYFK4TW.js} +3 -3
- package/dist/{google-BYZREX2O.js → google-T4CF4WCI.js} +4 -4
- package/dist/{google-marketing-SPQ2GW6X.js → google-marketing-IXPMUBXZ.js} +1 -1
- package/dist/{health-cmd-HH5RYMSV.js → health-cmd-2W37CF6J.js} +3 -3
- package/dist/{history-Q35NLLMJ.js → history-PA3O6ZHX.js} +3 -3
- package/dist/index.d.ts +248 -71
- package/dist/index.js +25 -24
- package/dist/{init-TV4ZKAAW.js → init-I3P7FINF.js} +12 -11
- package/dist/{insights-SH73B4PD.js → insights-O5PUVHTW.js} +3 -3
- package/dist/intelligence-service-EJQQNO65.js +16 -0
- package/dist/{journeys-SIV7HPW7.js → journeys-GU34O3HE.js} +3 -3
- package/dist/{keys-O656YWSW.js → keys-RXBFRNUS.js} +3 -3
- package/dist/{keyword-FZ2N4EW7.js → keyword-MASR36DK.js} +3 -3
- package/dist/{logs-2NPN7HBI.js → logs-OTA7MV4P.js} +3 -3
- package/dist/mcp-ZQYN2NET.js +19 -0
- package/dist/mcp.js +20 -14
- package/dist/{measurement-plan-JQKVSJRL.js → measurement-plan-ZGVO36TN.js} +5 -5
- package/dist/{notify-VTAYBHC6.js → notify-KS5CR3S2.js} +3 -3
- package/dist/{organic-evidence-HPXXEPOW.js → organic-evidence-CTH52AC5.js} +3 -3
- package/dist/{overview-FSBNDYZJ.js → overview-IRCIDE5H.js} +4 -4
- package/dist/{project-MAPPXURS.js → project-H5XPI5CT.js} +3 -3
- package/dist/{query-R6DLPIPJ.js → query-2R6EZG5T.js} +3 -3
- package/dist/{research-FXKOEUQB.js → research-OA3UQAV3.js} +3 -3
- package/dist/{results-export-X2PUM63S.js → results-export-XUXU3WMX.js} +3 -3
- package/dist/{run-6ENTG4IO.js → run-7IIOB6AS.js} +4 -4
- package/dist/{schedule-DMMVRT7P.js → schedule-DUTPZB5C.js} +3 -3
- package/dist/{search-FHI6APT7.js → search-TWUJD7KH.js} +3 -3
- package/dist/{serve-76BGQFPW.js → serve-BYHHULUU.js} +26 -25
- package/dist/{session-JBTIV7SS.js → session-M5XSTGPI.js} +13 -12
- package/dist/{settings-FMB4QED4.js → settings-GQXU5M3H.js} +3 -3
- package/dist/{skills-HBIMHSOI.js → skills-F6AXQHTO.js} +2 -2
- package/dist/{snapshot-RBA2EL7R.js → snapshot-QFKUO4QK.js} +3 -3
- package/dist/{src-BXXGJOQB.js → src-52USCS7O.js} +4 -2
- package/dist/{status-ATFDFAVG.js → status-42LPR7YH.js} +4 -4
- package/dist/{technical-aeo-JWQTGCTB.js → technical-aeo-SUILL4MY.js} +3 -3
- package/dist/{telemetry-6QFGJL2I.js → telemetry-RITB7YZH.js} +4 -4
- package/dist/{tools-4VRHKUBE.js → tools-DGWJOLNS.js} +11 -11
- package/dist/{traffic-5R7M4CK5.js → traffic-3YC3HUNT.js} +5 -5
- package/dist/{users-24TIUYJY.js → users-BST5DQ5R.js} +3 -3
- package/dist/{visibility-stats-YLQPPBJJ.js → visibility-stats-NCNP4AL2.js} +3 -3
- package/dist/{wordpress-7VDPHMUZ.js → wordpress-4HQDLQVE.js} +3 -3
- package/package.json +10 -6
- package/assets/assets/ToneBadge-CmvL5mFG.js +0 -60
- package/dist/intelligence-service-YNHDEY4M.js +0 -15
- package/dist/mcp-RHQY3DV6.js +0 -14
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
answerServiceAdapters,
|
|
3
3
|
answerServiceDescriptors
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-6W5AR4IW.js";
|
|
5
5
|
import {
|
|
6
6
|
AeroLlmUsageFeatures,
|
|
7
7
|
aeroTurnStatus,
|
|
@@ -18,7 +18,7 @@ import {
|
|
|
18
18
|
isSystemMessage,
|
|
19
19
|
loadAeroSystemPrompt,
|
|
20
20
|
missingProviderMessage,
|
|
21
|
-
|
|
21
|
+
readAeroViewEvidenceForTurn,
|
|
22
22
|
resolveAeroModel,
|
|
23
23
|
resolveAgentAllowViewers,
|
|
24
24
|
resolveAgentEnabled,
|
|
@@ -27,14 +27,14 @@ import {
|
|
|
27
27
|
resolveConfiguredAgentProvider,
|
|
28
28
|
resolveSessionProviderAndModel,
|
|
29
29
|
setAeroSystemPrompt
|
|
30
|
-
} from "./chunk-
|
|
30
|
+
} from "./chunk-GNBATQS3.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-ETVM3T7K.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-XDQJOZKJ.js";
|
|
57
57
|
import {
|
|
58
58
|
claudeAdapter,
|
|
59
59
|
embedQueries,
|
|
@@ -64,7 +64,7 @@ import {
|
|
|
64
64
|
openaiAdapter,
|
|
65
65
|
openrouterAdapter,
|
|
66
66
|
perplexityAdapter
|
|
67
|
-
} from "./chunk-
|
|
67
|
+
} from "./chunk-6ATKY252.js";
|
|
68
68
|
import {
|
|
69
69
|
classifyUsageSurface,
|
|
70
70
|
createApiUsageTelemetry,
|
|
@@ -76,7 +76,7 @@ import {
|
|
|
76
76
|
startOutcomeTimer,
|
|
77
77
|
trackFeatureCompleted,
|
|
78
78
|
trackInstallState
|
|
79
|
-
} from "./chunk-
|
|
79
|
+
} from "./chunk-S77W2XSI.js";
|
|
80
80
|
import {
|
|
81
81
|
isLoopbackBindHost
|
|
82
82
|
} from "./chunk-TSENU4GG.js";
|
|
@@ -84,7 +84,7 @@ import {
|
|
|
84
84
|
isApiProviderRegistrable,
|
|
85
85
|
isCdpProviderRegistrable,
|
|
86
86
|
registeredProviderNames
|
|
87
|
-
} from "./chunk-
|
|
87
|
+
} from "./chunk-DLVGMRM5.js";
|
|
88
88
|
import {
|
|
89
89
|
checkLatestVersionForServer,
|
|
90
90
|
getOrCreateAnonymousId,
|
|
@@ -95,17 +95,17 @@ import {
|
|
|
95
95
|
recordDashboardEvent,
|
|
96
96
|
setTelemetryPreference,
|
|
97
97
|
trackEvent
|
|
98
|
-
} from "./chunk-
|
|
98
|
+
} from "./chunk-PRSQ4R47.js";
|
|
99
99
|
import {
|
|
100
100
|
createCanonryMcpServer
|
|
101
|
-
} from "./chunk-
|
|
101
|
+
} from "./chunk-F5D4GPZQ.js";
|
|
102
102
|
import {
|
|
103
103
|
CANONRY_MCP_TIERS,
|
|
104
104
|
CANONRY_MCP_TOOLKIT_NAMES
|
|
105
105
|
} from "./chunk-NDTU7FGL.js";
|
|
106
106
|
import {
|
|
107
107
|
ApiClient
|
|
108
|
-
} from "./chunk-
|
|
108
|
+
} from "./chunk-WH2M2MQF.js";
|
|
109
109
|
import {
|
|
110
110
|
AGENT_PROVIDERS,
|
|
111
111
|
adapterSupportsBatch,
|
|
@@ -136,21 +136,21 @@ import {
|
|
|
136
136
|
saveConfigPatch,
|
|
137
137
|
sentimentInstallReadiness,
|
|
138
138
|
updateSharedProviderExecutionGate
|
|
139
|
-
} from "./chunk-
|
|
139
|
+
} from "./chunk-HKV4UWG5.js";
|
|
140
140
|
import {
|
|
141
141
|
getBundledSkillSnapshots
|
|
142
|
-
} from "./chunk-
|
|
142
|
+
} from "./chunk-HEL6DKDN.js";
|
|
143
143
|
import {
|
|
144
144
|
computeFactorAverages,
|
|
145
145
|
deleteSiteCrawlGraphLayout,
|
|
146
146
|
persistSiteCrawlGraphLayout,
|
|
147
147
|
prepareSiteCrawlGraphLayout,
|
|
148
148
|
recordedSharePct
|
|
149
|
-
} from "./chunk-
|
|
149
|
+
} from "./chunk-6YW24AUN.js";
|
|
150
150
|
import {
|
|
151
151
|
CanonryMcpToolNames,
|
|
152
152
|
canonryMcpTools
|
|
153
|
-
} from "./chunk-
|
|
153
|
+
} from "./chunk-3WPTMLQI.js";
|
|
154
154
|
import {
|
|
155
155
|
PACKAGE_VERSION
|
|
156
156
|
} from "./chunk-AOJU5LKX.js";
|
|
@@ -158,15 +158,17 @@ import {
|
|
|
158
158
|
CliError
|
|
159
159
|
} from "./chunk-HGMYEDAQ.js";
|
|
160
160
|
import {
|
|
161
|
-
IntelligenceService
|
|
161
|
+
IntelligenceService
|
|
162
|
+
} from "./chunk-5PA7SJ2M.js";
|
|
163
|
+
import {
|
|
162
164
|
addLogListener,
|
|
163
165
|
createFastifyLogger,
|
|
164
166
|
createLogger
|
|
165
|
-
} from "./chunk-
|
|
167
|
+
} from "./chunk-AZQNJX3T.js";
|
|
166
168
|
import {
|
|
167
169
|
computeCitedCompetitorDomains,
|
|
168
170
|
determineCitationState
|
|
169
|
-
} from "./chunk-
|
|
171
|
+
} from "./chunk-JPM3VC6A.js";
|
|
170
172
|
import {
|
|
171
173
|
BingApiError,
|
|
172
174
|
CC_CACHE_DIR,
|
|
@@ -179,6 +181,7 @@ import {
|
|
|
179
181
|
GbpApiError,
|
|
180
182
|
GoogleApiError,
|
|
181
183
|
GoogleAuthError,
|
|
184
|
+
MCP_DELEGATED_KEY_MAX_AGE_MS,
|
|
182
185
|
PLUGIN_DIR,
|
|
183
186
|
SITE_REACHABILITY_CHECKS,
|
|
184
187
|
SITE_REACHABILITY_CHECK_ID,
|
|
@@ -286,7 +289,7 @@ import {
|
|
|
286
289
|
webhookResponseReason,
|
|
287
290
|
webhookTargetRefusalReason,
|
|
288
291
|
writeAuditLog
|
|
289
|
-
} from "./chunk-
|
|
292
|
+
} from "./chunk-WLWOGBLJ.js";
|
|
290
293
|
import {
|
|
291
294
|
OperationalLogStore,
|
|
292
295
|
SentimentIdempotencyConflict,
|
|
@@ -372,7 +375,7 @@ import {
|
|
|
372
375
|
siteLivenessState,
|
|
373
376
|
trafficSources,
|
|
374
377
|
usageCounters
|
|
375
|
-
} from "./chunk-
|
|
378
|
+
} from "./chunk-PAIYFNNY.js";
|
|
376
379
|
import {
|
|
377
380
|
AGENT_MEMORY_VALUE_MAX_BYTES,
|
|
378
381
|
AI_ENGINE_DOMAINS,
|
|
@@ -624,7 +627,7 @@ import {
|
|
|
624
627
|
winnabilityClassLabel,
|
|
625
628
|
withRetry,
|
|
626
629
|
withSkippedProviders
|
|
627
|
-
} from "./chunk-
|
|
630
|
+
} from "./chunk-SF5JLMLG.js";
|
|
628
631
|
|
|
629
632
|
// src/google-marketing-runtime.ts
|
|
630
633
|
import crypto2 from "crypto";
|
|
@@ -3314,7 +3317,7 @@ import path5 from "path";
|
|
|
3314
3317
|
import { isIP as isIP2 } from "net";
|
|
3315
3318
|
import { fileURLToPath } from "url";
|
|
3316
3319
|
import { setImmediate as yieldToEventLoop4 } from "timers/promises";
|
|
3317
|
-
import { and as
|
|
3320
|
+
import { and as and27, eq as eq33, isNotNull as isNotNull5, isNull as isNull8, lte as lte3, max, ne as ne4, or as or4, sql as sql13 } from "drizzle-orm";
|
|
3318
3321
|
|
|
3319
3322
|
// src/seo-runtime.ts
|
|
3320
3323
|
import { eq, inArray } from "drizzle-orm";
|
|
@@ -5807,7 +5810,7 @@ function nonBlank(value) {
|
|
|
5807
5810
|
return trimmed ? trimmed : void 0;
|
|
5808
5811
|
}
|
|
5809
5812
|
function resolveBuildCommit(env = process.env) {
|
|
5810
|
-
const embedded = true ? "
|
|
5813
|
+
const embedded = true ? "a9c886130b3b0c74de10313ac5ebd1c0853664d0" : void 0;
|
|
5811
5814
|
return nonBlank(embedded) ?? nonBlank(env.CANONRY_COMMIT);
|
|
5812
5815
|
}
|
|
5813
5816
|
function resolveInstanceIdentity(env = process.env) {
|
|
@@ -15636,8 +15639,7 @@ import { eq as eq27 } from "drizzle-orm";
|
|
|
15636
15639
|
|
|
15637
15640
|
// src/agent/remote-mcp.ts
|
|
15638
15641
|
import { Type } from "@earendil-works/pi-ai";
|
|
15639
|
-
import { Client } from "@modelcontextprotocol/
|
|
15640
|
-
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
|
|
15642
|
+
import { Client, StreamableHTTPClientTransport } from "@modelcontextprotocol/client";
|
|
15641
15643
|
var log24 = createLogger("RemoteMcp");
|
|
15642
15644
|
var AERO_EXCLUDED_MCP_TOOLS = /* @__PURE__ */ new Set([]);
|
|
15643
15645
|
async function connectStreamableHttp(server) {
|
|
@@ -15648,7 +15650,7 @@ async function connectStreamableHttp(server) {
|
|
|
15648
15650
|
});
|
|
15649
15651
|
const client = new Client(
|
|
15650
15652
|
{ name: "canonry-aero", version: "1.0.0" },
|
|
15651
|
-
{ capabilities: {} }
|
|
15653
|
+
{ capabilities: {}, versionNegotiation: { mode: "auto" } }
|
|
15652
15654
|
);
|
|
15653
15655
|
await client.connect(transport);
|
|
15654
15656
|
return client;
|
|
@@ -16381,7 +16383,7 @@ ${lines.join("\n")}
|
|
|
16381
16383
|
this.assertTurnProviderUsable(projectId);
|
|
16382
16384
|
await this.mergeExternalTools(agent);
|
|
16383
16385
|
const view = { client: this.opts.client, projectName, basePath: this.opts.config.basePath, context: preferences?.context };
|
|
16384
|
-
const evidence = preferences?.context ? await
|
|
16386
|
+
const evidence = preferences?.context ? await readAeroViewEvidenceForTurn(view, preferences?.signal) : void 0;
|
|
16385
16387
|
preferences?.signal?.throwIfAborted();
|
|
16386
16388
|
await this.maybeCompact(projectName, agent);
|
|
16387
16389
|
preferences?.signal?.throwIfAborted();
|
|
@@ -16880,7 +16882,7 @@ var ViewerAeroSessions = class {
|
|
|
16880
16882
|
const agent = this.sessions.get(key)?.agent ?? await this.createAgent(project, client, preferences.signal);
|
|
16881
16883
|
agent.state.messages = trimViewerTranscript(agent.state.messages.filter((message) => message.role !== "system"));
|
|
16882
16884
|
const view = { client, projectName: project.name, basePath: this.opts.config.basePath, context: preferences.context };
|
|
16883
|
-
const evidence = preferences.context ? await
|
|
16885
|
+
const evidence = preferences.context ? await readAeroViewEvidenceForTurn(view, preferences.signal) : void 0;
|
|
16884
16886
|
preferences.signal?.throwIfAborted();
|
|
16885
16887
|
const shape = aeroProjectShape(this.opts.db, project.id);
|
|
16886
16888
|
setAeroSystemPrompt(agent, loadAeroSystemPrompt(void 0, { extras: false }) + VIEWER_AERO_PROMPT + shape.prompt + aeroViewPrompt(preferences.context));
|
|
@@ -16969,12 +16971,15 @@ var ViewerAeroSessions = class {
|
|
|
16969
16971
|
// src/mcp-http.ts
|
|
16970
16972
|
import crypto25 from "crypto";
|
|
16971
16973
|
import { isIP } from "net";
|
|
16972
|
-
import { eq as eq29 } from "drizzle-orm";
|
|
16973
|
-
import {
|
|
16974
|
-
import { isInitializeRequest } from "@modelcontextprotocol/
|
|
16974
|
+
import { and as and24, eq as eq29, isNull as isNull6 } from "drizzle-orm";
|
|
16975
|
+
import { NodeStreamableHTTPServerTransport, toNodeHandler, toWebRequest } from "@modelcontextprotocol/node";
|
|
16976
|
+
import { McpServer, createMcpHandler, isInitializeRequest, isLegacyRequest } from "@modelcontextprotocol/server";
|
|
16975
16977
|
var SESSION_IDLE_MS = 10 * 60 * 1e3;
|
|
16976
16978
|
var SWEEP_INTERVAL_MS = 60 * 1e3;
|
|
16977
|
-
|
|
16979
|
+
var MAX_CLEANUP_RETRIES_PER_SWEEP = 8;
|
|
16980
|
+
var MAX_PENDING_KEY_RETIREMENTS = 256;
|
|
16981
|
+
var MAX_CLEANUP_BACKOFF_MS = 10 * 60 * 1e3;
|
|
16982
|
+
function mintSessionKey(db, scopes, userId, oauthGrantId, createdAt) {
|
|
16978
16983
|
const raw = `cnry_${crypto25.randomBytes(24).toString("hex")}`;
|
|
16979
16984
|
const id = crypto25.randomUUID();
|
|
16980
16985
|
db.insert(apiKeys).values({
|
|
@@ -16984,18 +16989,10 @@ function mintSessionKey(db, scopes, userId) {
|
|
|
16984
16989
|
keyPrefix: raw.slice(0, 9),
|
|
16985
16990
|
scopes: [...scopes],
|
|
16986
16991
|
delegatedUserId: userId,
|
|
16987
|
-
|
|
16992
|
+
delegatedOAuthGrantId: oauthGrantId,
|
|
16993
|
+
createdAt: new Date(createdAt).toISOString()
|
|
16988
16994
|
}).run();
|
|
16989
|
-
return { id, raw };
|
|
16990
|
-
}
|
|
16991
|
-
function revokeSessionKey(db, id) {
|
|
16992
|
-
try {
|
|
16993
|
-
db.transaction((tx) => {
|
|
16994
|
-
tx.delete(dashboardSessions).where(eq29(dashboardSessions.apiKeyId, id)).run();
|
|
16995
|
-
tx.update(apiKeys).set({ revokedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(eq29(apiKeys.id, id)).run();
|
|
16996
|
-
});
|
|
16997
|
-
} catch {
|
|
16998
|
-
}
|
|
16995
|
+
return { id, raw, expiresAt: createdAt + MCP_DELEGATED_KEY_MAX_AGE_MS };
|
|
16999
16996
|
}
|
|
17000
16997
|
function mcpTransportPaths() {
|
|
17001
16998
|
const paths = ["/mcp", "/mcp/readonly"];
|
|
@@ -17050,18 +17047,95 @@ function mcpHttpHealth(app, apiPrefix, hosted) {
|
|
|
17050
17047
|
}
|
|
17051
17048
|
function registerMcpHttpRoutes(scope, opts) {
|
|
17052
17049
|
const sessions = /* @__PURE__ */ new Map();
|
|
17050
|
+
const legacyServers = /* @__PURE__ */ new Set();
|
|
17051
|
+
const modernExchanges = /* @__PURE__ */ new Set();
|
|
17053
17052
|
const now = opts.now ?? (() => Date.now());
|
|
17053
|
+
const pendingKeyRetirements = /* @__PURE__ */ new Map();
|
|
17054
|
+
let shuttingDown = false;
|
|
17055
|
+
function withoutLockWait(work) {
|
|
17056
|
+
const timeout = opts.db.$client.pragma("busy_timeout", { simple: true });
|
|
17057
|
+
try {
|
|
17058
|
+
opts.db.$client.pragma("busy_timeout = 0");
|
|
17059
|
+
return work();
|
|
17060
|
+
} finally {
|
|
17061
|
+
opts.db.$client.pragma(`busy_timeout = ${timeout}`);
|
|
17062
|
+
}
|
|
17063
|
+
}
|
|
17064
|
+
function attemptKeyRetirement(id, remove) {
|
|
17065
|
+
try {
|
|
17066
|
+
withoutLockWait(() => {
|
|
17067
|
+
opts.db.transaction((tx) => {
|
|
17068
|
+
tx.delete(dashboardSessions).where(eq29(dashboardSessions.apiKeyId, id)).run();
|
|
17069
|
+
tx.update(apiKeys).set({ revokedAt: new Date(now()).toISOString() }).where(and24(eq29(apiKeys.id, id), isNull6(apiKeys.revokedAt))).run();
|
|
17070
|
+
});
|
|
17071
|
+
if (remove) {
|
|
17072
|
+
try {
|
|
17073
|
+
opts.db.delete(apiKeys).where(eq29(apiKeys.id, id)).run();
|
|
17074
|
+
} catch (error) {
|
|
17075
|
+
scope.log.warn({ err: error, keyId: id }, "Retained revoked MCP request key after deletion failed");
|
|
17076
|
+
}
|
|
17077
|
+
}
|
|
17078
|
+
});
|
|
17079
|
+
return true;
|
|
17080
|
+
} catch (error) {
|
|
17081
|
+
scope.log.error({ err: error, keyId: id }, "Failed to retire MCP key; cleanup remains bounded by its expiry");
|
|
17082
|
+
return false;
|
|
17083
|
+
}
|
|
17084
|
+
}
|
|
17085
|
+
function retireSessionKey(key, remove = false) {
|
|
17086
|
+
const queued = pendingKeyRetirements.get(key.id);
|
|
17087
|
+
if (queued) {
|
|
17088
|
+
queued.remove ||= remove;
|
|
17089
|
+
return;
|
|
17090
|
+
}
|
|
17091
|
+
if (attemptKeyRetirement(key.id, remove)) return;
|
|
17092
|
+
if (pendingKeyRetirements.size >= MAX_PENDING_KEY_RETIREMENTS) {
|
|
17093
|
+
const oldest = pendingKeyRetirements.keys().next().value;
|
|
17094
|
+
if (oldest !== void 0) pendingKeyRetirements.delete(oldest);
|
|
17095
|
+
scope.log.warn("MCP cleanup queue is full; oldest credential remains bounded by its expiry");
|
|
17096
|
+
}
|
|
17097
|
+
pendingKeyRetirements.set(key.id, { remove, expiresAt: key.expiresAt, nextAttemptAt: now() + SWEEP_INTERVAL_MS, attempts: 1 });
|
|
17098
|
+
}
|
|
17099
|
+
function retryKeyRetirements() {
|
|
17100
|
+
let attempted = 0;
|
|
17101
|
+
for (const [id, queued] of pendingKeyRetirements) {
|
|
17102
|
+
if (queued.expiresAt <= now()) {
|
|
17103
|
+
pendingKeyRetirements.delete(id);
|
|
17104
|
+
continue;
|
|
17105
|
+
}
|
|
17106
|
+
if (queued.nextAttemptAt > now()) continue;
|
|
17107
|
+
if (attempted++ >= MAX_CLEANUP_RETRIES_PER_SWEEP) break;
|
|
17108
|
+
if (attemptKeyRetirement(id, queued.remove)) pendingKeyRetirements.delete(id);
|
|
17109
|
+
else {
|
|
17110
|
+
queued.nextAttemptAt = now() + Math.min(SWEEP_INTERVAL_MS * 2 ** queued.attempts, MAX_CLEANUP_BACKOFF_MS);
|
|
17111
|
+
queued.attempts++;
|
|
17112
|
+
}
|
|
17113
|
+
}
|
|
17114
|
+
}
|
|
17054
17115
|
async function dropSession(id) {
|
|
17055
|
-
|
|
17056
|
-
if (!session) return;
|
|
17057
|
-
sessions.delete(id);
|
|
17058
|
-
await session.close().catch(() => {
|
|
17059
|
-
});
|
|
17116
|
+
await sessions.get(id)?.close().catch((error) => scope.log.error({ err: error }, "Failed to close legacy MCP session"));
|
|
17060
17117
|
}
|
|
17061
17118
|
const sweep = setInterval(() => {
|
|
17062
|
-
|
|
17063
|
-
|
|
17064
|
-
|
|
17119
|
+
try {
|
|
17120
|
+
retryKeyRetirements();
|
|
17121
|
+
withoutLockWait(() => {
|
|
17122
|
+
const cutoff = now() - SESSION_IDLE_MS;
|
|
17123
|
+
for (const session of legacyServers) {
|
|
17124
|
+
const key = session.sessionKeyId ? opts.db.select({ revokedAt: apiKeys.revokedAt }).from(apiKeys).where(eq29(apiKeys.id, session.sessionKeyId)).get() : void 0;
|
|
17125
|
+
if (session.lastSeenAt < cutoff || session.expiresAt !== void 0 && session.expiresAt <= now() || session.sessionKeyId && (!key || key.revokedAt)) {
|
|
17126
|
+
void session.close().catch((error) => scope.log.error({ err: error }, "Failed to close expired MCP session"));
|
|
17127
|
+
}
|
|
17128
|
+
}
|
|
17129
|
+
for (const exchange of modernExchanges) {
|
|
17130
|
+
if (!exchange.sessionKey) continue;
|
|
17131
|
+
const key = opts.db.select({ revokedAt: apiKeys.revokedAt }).from(apiKeys).where(eq29(apiKeys.id, exchange.sessionKey.id)).get();
|
|
17132
|
+
if (!key || key.revokedAt || exchange.sessionKey.expiresAt <= now()) {
|
|
17133
|
+
void exchange.close().catch((error) => scope.log.error({ err: error }, "Failed to close expired MCP request"));
|
|
17134
|
+
}
|
|
17135
|
+
}
|
|
17136
|
+
});
|
|
17137
|
+
} catch (error) {
|
|
17138
|
+
scope.log.error({ err: error }, "MCP lifecycle sweep failed; will retry on the next interval");
|
|
17065
17139
|
}
|
|
17066
17140
|
}, SWEEP_INTERVAL_MS);
|
|
17067
17141
|
sweep.unref();
|
|
@@ -17075,9 +17149,15 @@ function registerMcpHttpRoutes(scope, opts) {
|
|
|
17075
17149
|
return payload;
|
|
17076
17150
|
});
|
|
17077
17151
|
}
|
|
17078
|
-
scope.addHook("
|
|
17152
|
+
scope.addHook("preClose", async () => {
|
|
17153
|
+
shuttingDown = true;
|
|
17079
17154
|
clearInterval(sweep);
|
|
17080
|
-
await Promise.all([...
|
|
17155
|
+
await Promise.all([...legacyServers].map((session) => session.close().catch((error) => scope.log.error({ err: error }, "Failed to close MCP session during shutdown"))));
|
|
17156
|
+
await Promise.all([...modernExchanges].map((exchange) => exchange.close().catch((error) => scope.log.error({ err: error }, "Failed to close MCP request during shutdown"))));
|
|
17157
|
+
retryKeyRetirements();
|
|
17158
|
+
if (pendingKeyRetirements.size > 0) {
|
|
17159
|
+
scope.log.warn({ count: pendingKeyRetirements.size }, "MCP credential cleanup remains pending at shutdown; credentials remain bounded by expiry");
|
|
17160
|
+
}
|
|
17081
17161
|
});
|
|
17082
17162
|
function callerBearer(request) {
|
|
17083
17163
|
const header = request.headers.authorization;
|
|
@@ -17088,53 +17168,74 @@ function registerMcpHttpRoutes(scope, opts) {
|
|
|
17088
17168
|
if (trimmed.slice(0, prefix.length).toLowerCase() !== prefix) return null;
|
|
17089
17169
|
return trimmed.slice(prefix.length).trim() || null;
|
|
17090
17170
|
}
|
|
17091
|
-
|
|
17092
|
-
|
|
17093
|
-
if (!bearer) return null;
|
|
17171
|
+
function createAuthorizedServer(request, segment, bearer, signal, modern = false, needsApiCredential = true) {
|
|
17172
|
+
if (shuttingDown) throw new Error("MCP server is shutting down");
|
|
17094
17173
|
const scopes = request.principal?.scopes ?? request.apiKey?.scopes ?? [];
|
|
17095
17174
|
let sessionKey = null;
|
|
17096
|
-
if (!request.apiKey && request.principal?.kind === "user") {
|
|
17097
|
-
|
|
17175
|
+
if (needsApiCredential && !request.apiKey && request.principal?.kind === "user") {
|
|
17176
|
+
if (!request.principal.oauthGrantId) throw forbidden("MCP OAuth credentials require an active authorization grant.");
|
|
17177
|
+
sessionKey = mintSessionKey(opts.db, segment.readOnly ? ["read"] : scopes, request.principal.id, request.principal.oauthGrantId, now());
|
|
17098
17178
|
}
|
|
17099
17179
|
const client = new ApiClient(opts.selfApiUrl, sessionKey?.raw ?? bearer, {
|
|
17100
17180
|
skipProbe: true,
|
|
17181
|
+
storedOnly: segment.readOnly,
|
|
17182
|
+
signal,
|
|
17101
17183
|
clientName: `canonry-mcp/${PACKAGE_VERSION}`,
|
|
17102
17184
|
surface: "mcp-http",
|
|
17103
17185
|
// Correlation only: this is never accepted as caller identity or authority.
|
|
17104
|
-
actorSession: crypto25.randomUUID()
|
|
17186
|
+
...modern ? {} : { actorSession: crypto25.randomUUID() }
|
|
17105
17187
|
});
|
|
17106
17188
|
let server;
|
|
17107
17189
|
try {
|
|
17108
17190
|
server = createCanonryMcpServer({
|
|
17109
|
-
scope: segment.readOnly
|
|
17191
|
+
scope: segment.readOnly ? "stored-only" : isReadOnlyKey(scopes) ? "read-only" : "all",
|
|
17110
17192
|
credentialScopes: scopes,
|
|
17111
17193
|
operator: request.operatorAccess === true,
|
|
17194
|
+
connectionProfile: Boolean(opts.issuer && !request.principal?.projectId && (request.principal?.kind === "user" || request.principal?.delegatedUser) && scopes.some((scope2) => scope2 === "read" || scope2 === "*")),
|
|
17112
17195
|
tiers: segment.tiers,
|
|
17113
17196
|
clientFactory: () => client,
|
|
17114
17197
|
updateAvailable: opts.getUpdateAvailable
|
|
17115
17198
|
});
|
|
17199
|
+
server.server.registerCapabilities({ tools: { listChanged: false }, resources: { listChanged: false, subscribe: false } });
|
|
17116
17200
|
} catch (error) {
|
|
17117
|
-
if (sessionKey)
|
|
17201
|
+
if (sessionKey) retireSessionKey(sessionKey, modern);
|
|
17118
17202
|
throw error;
|
|
17119
17203
|
}
|
|
17120
|
-
|
|
17204
|
+
return { server, sessionKey };
|
|
17205
|
+
}
|
|
17206
|
+
async function openSession(request, authorizationId, segment) {
|
|
17207
|
+
const bearer = callerBearer(request);
|
|
17208
|
+
if (!bearer) return null;
|
|
17209
|
+
const { server, sessionKey } = createAuthorizedServer(request, segment, bearer);
|
|
17210
|
+
const transport = new NodeStreamableHTTPServerTransport({
|
|
17121
17211
|
sessionIdGenerator: () => crypto25.randomUUID(),
|
|
17122
17212
|
onsessioninitialized: (id) => {
|
|
17123
|
-
|
|
17213
|
+
if (!shuttingDown && !closing) sessions.set(id, session);
|
|
17124
17214
|
}
|
|
17125
17215
|
});
|
|
17126
|
-
|
|
17127
|
-
|
|
17128
|
-
|
|
17129
|
-
|
|
17130
|
-
|
|
17131
|
-
|
|
17132
|
-
|
|
17216
|
+
let closing;
|
|
17217
|
+
const session = { transport, close, authorizationId, segmentId: segment.id, sessionKeyId: sessionKey?.id, expiresAt: sessionKey?.expiresAt, lastSeenAt: now() };
|
|
17218
|
+
legacyServers.add(session);
|
|
17219
|
+
function close() {
|
|
17220
|
+
if (closing) return closing;
|
|
17221
|
+
closing = Promise.resolve().then(async () => {
|
|
17222
|
+
if (sessionKey) retireSessionKey(sessionKey);
|
|
17223
|
+
const id = transport.sessionId;
|
|
17224
|
+
if (id) sessions.delete(id);
|
|
17225
|
+
try {
|
|
17226
|
+
await transport.close();
|
|
17227
|
+
} finally {
|
|
17228
|
+
try {
|
|
17229
|
+
await server.close();
|
|
17230
|
+
} finally {
|
|
17231
|
+
legacyServers.delete(session);
|
|
17232
|
+
}
|
|
17233
|
+
}
|
|
17234
|
+
});
|
|
17235
|
+
return closing;
|
|
17133
17236
|
}
|
|
17134
17237
|
transport.onclose = () => {
|
|
17135
|
-
|
|
17136
|
-
if (id) sessions.delete(id);
|
|
17137
|
-
if (sessionKey) revokeSessionKey(opts.db, sessionKey.id);
|
|
17238
|
+
void close().catch((error) => scope.log.error({ err: error }, "Failed to close legacy MCP transport"));
|
|
17138
17239
|
};
|
|
17139
17240
|
try {
|
|
17140
17241
|
await server.connect(transport);
|
|
@@ -17143,15 +17244,68 @@ function registerMcpHttpRoutes(scope, opts) {
|
|
|
17143
17244
|
});
|
|
17144
17245
|
throw error;
|
|
17145
17246
|
}
|
|
17146
|
-
return
|
|
17247
|
+
return session;
|
|
17248
|
+
}
|
|
17249
|
+
async function handleModern(request, reply, segment, bearer) {
|
|
17250
|
+
const abort = new AbortController();
|
|
17251
|
+
let closing;
|
|
17252
|
+
const exchange = {
|
|
17253
|
+
close: () => {
|
|
17254
|
+
if (closing) return closing;
|
|
17255
|
+
abort.abort();
|
|
17256
|
+
if (exchange.sessionKey) retireSessionKey(exchange.sessionKey, true);
|
|
17257
|
+
modernExchanges.delete(exchange);
|
|
17258
|
+
closing = handler.close().catch((error) => {
|
|
17259
|
+
scope.log.error({ err: error }, "Failed to close MCP request transport");
|
|
17260
|
+
throw error;
|
|
17261
|
+
});
|
|
17262
|
+
return closing;
|
|
17263
|
+
}
|
|
17264
|
+
};
|
|
17265
|
+
const reportError = (error) => scope.log.error({ err: error }, "Modern MCP request failed");
|
|
17266
|
+
const handler = createMcpHandler(({ requestInfo }) => {
|
|
17267
|
+
if (shuttingDown) throw new Error("MCP server is shutting down");
|
|
17268
|
+
const body = request.body;
|
|
17269
|
+
const method = body && typeof body === "object" ? body.method : void 0;
|
|
17270
|
+
if (method === "subscriptions/listen") {
|
|
17271
|
+
return new McpServer({ name: "canonry", version: PACKAGE_VERSION });
|
|
17272
|
+
}
|
|
17273
|
+
const signal = requestInfo ? AbortSignal.any([abort.signal, requestInfo.signal]) : abort.signal;
|
|
17274
|
+
const opened = createAuthorizedServer(request, segment, bearer, signal, true, method === "tools/call");
|
|
17275
|
+
exchange.sessionKey = opened.sessionKey ?? void 0;
|
|
17276
|
+
return opened.server;
|
|
17277
|
+
}, { legacy: "reject", onerror: reportError });
|
|
17278
|
+
modernExchanges.add(exchange);
|
|
17279
|
+
try {
|
|
17280
|
+
await toNodeHandler(handler, { onerror: reportError })(request.raw, reply.raw, request.body);
|
|
17281
|
+
} finally {
|
|
17282
|
+
await exchange.close();
|
|
17283
|
+
}
|
|
17147
17284
|
}
|
|
17148
17285
|
async function handle(request, reply, segment) {
|
|
17286
|
+
if (shuttingDown) {
|
|
17287
|
+
await reply.status(503).send({ jsonrpc: "2.0", id: null, error: { code: -32e3, message: "MCP server is shutting down." } });
|
|
17288
|
+
return;
|
|
17289
|
+
}
|
|
17149
17290
|
const principal = request.principal ?? request.apiKey;
|
|
17150
17291
|
const bearer = callerBearer(request);
|
|
17151
17292
|
if (!principal || !bearer) {
|
|
17152
17293
|
await reply.status(401).send({ error: { code: "AUTH_REQUIRED", message: "Authentication required." } });
|
|
17153
17294
|
return;
|
|
17154
17295
|
}
|
|
17296
|
+
if (segment.readOnly && !request.apiKey && request.principal?.kind === "user" && !principal.scopes.some((scope2) => scope2 === "read" || scope2 === "*")) {
|
|
17297
|
+
throw forbidden('The hosted read-only MCP endpoint requires the "read" scope.');
|
|
17298
|
+
}
|
|
17299
|
+
const webRequest = await toWebRequest(request.raw, request.body);
|
|
17300
|
+
const legacy = await isLegacyRequest(webRequest, request.body);
|
|
17301
|
+
if (shuttingDown) {
|
|
17302
|
+
await reply.status(503).send({ jsonrpc: "2.0", id: null, error: { code: -32e3, message: "MCP server is shutting down." } });
|
|
17303
|
+
return;
|
|
17304
|
+
}
|
|
17305
|
+
if (!legacy) {
|
|
17306
|
+
await handleModern(request, reply, segment, bearer);
|
|
17307
|
+
return;
|
|
17308
|
+
}
|
|
17155
17309
|
const authorizationId = hashApiKey(JSON.stringify({
|
|
17156
17310
|
bearerHash: hashApiKey(bearer),
|
|
17157
17311
|
kind: request.principal?.kind ?? "api-key",
|
|
@@ -17163,6 +17317,11 @@ function registerMcpHttpRoutes(scope, opts) {
|
|
|
17163
17317
|
const sessionId = request.headers["mcp-session-id"];
|
|
17164
17318
|
const existing = typeof sessionId === "string" ? sessions.get(sessionId) : void 0;
|
|
17165
17319
|
if (existing) {
|
|
17320
|
+
if (existing.expiresAt !== void 0 && existing.expiresAt <= now() && typeof sessionId === "string") {
|
|
17321
|
+
await dropSession(sessionId);
|
|
17322
|
+
await reply.status(404).send({ error: { code: "NOT_FOUND", message: "Unknown MCP session." } });
|
|
17323
|
+
return;
|
|
17324
|
+
}
|
|
17166
17325
|
if (existing.authorizationId !== authorizationId || existing.segmentId !== segment.id) {
|
|
17167
17326
|
await reply.status(404).send({ error: { code: "NOT_FOUND", message: "Unknown MCP session." } });
|
|
17168
17327
|
return;
|
|
@@ -17184,6 +17343,11 @@ function registerMcpHttpRoutes(scope, opts) {
|
|
|
17184
17343
|
await reply.status(401).send({ error: { code: "AUTH_REQUIRED", message: "Authentication required." } });
|
|
17185
17344
|
return;
|
|
17186
17345
|
}
|
|
17346
|
+
if (shuttingDown) {
|
|
17347
|
+
await opened.close();
|
|
17348
|
+
await reply.status(503).send({ jsonrpc: "2.0", id: null, error: { code: -32e3, message: "MCP server is shutting down." } });
|
|
17349
|
+
return;
|
|
17350
|
+
}
|
|
17187
17351
|
let handled = false;
|
|
17188
17352
|
try {
|
|
17189
17353
|
await opened.transport.handleRequest(request.raw, reply.raw, request.body);
|
|
@@ -18399,7 +18563,7 @@ function clipText(value, length) {
|
|
|
18399
18563
|
}
|
|
18400
18564
|
|
|
18401
18565
|
// src/research-runner.ts
|
|
18402
|
-
import { and as
|
|
18566
|
+
import { and as and25, eq as eq31, inArray as inArray13, sql as sql11 } from "drizzle-orm";
|
|
18403
18567
|
var unfinishedResearchQueryStatuses = [ResearchQueryStatuses.queued, ResearchQueryStatuses.running];
|
|
18404
18568
|
function finalResearchRunStatus(completed, failed, waiting) {
|
|
18405
18569
|
if (waiting > 0) return ResearchRunStatuses["waiting-upstream"];
|
|
@@ -18407,13 +18571,13 @@ function finalResearchRunStatus(completed, failed, waiting) {
|
|
|
18407
18571
|
return completed > 0 ? ResearchRunStatuses.partial : ResearchRunStatuses.failed;
|
|
18408
18572
|
}
|
|
18409
18573
|
async function executeResearchRun(db, registry, runId, projectId, options = {}) {
|
|
18410
|
-
const run = db.select().from(researchRuns).where(
|
|
18574
|
+
const run = db.select().from(researchRuns).where(and25(eq31(researchRuns.id, runId), eq31(researchRuns.projectId, projectId))).get();
|
|
18411
18575
|
if (!run || run.status !== ResearchRunStatuses.queued) return;
|
|
18412
18576
|
const project = db.select().from(projects).where(eq31(projects.id, projectId)).get();
|
|
18413
18577
|
if (!project) return;
|
|
18414
18578
|
const provider = registry.get(run.provider);
|
|
18415
18579
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
18416
|
-
const claim = db.update(researchRuns).set({ status: ResearchRunStatuses.running, startedAt: now }).where(
|
|
18580
|
+
const claim = db.update(researchRuns).set({ status: ResearchRunStatuses.running, startedAt: now }).where(and25(eq31(researchRuns.id, runId), eq31(researchRuns.status, ResearchRunStatuses.queued))).run();
|
|
18417
18581
|
if (claim.changes !== 1) return;
|
|
18418
18582
|
const elapsed = startOutcomeTimer();
|
|
18419
18583
|
const requested = currentOutcomeOrigin();
|
|
@@ -18458,7 +18622,7 @@ async function executeResearchRun(db, registry, runId, projectId, options = {})
|
|
|
18458
18622
|
await mapWithConcurrency(rows, Math.max(1, provider.config.quotaPolicy.maxConcurrency), async (row) => {
|
|
18459
18623
|
try {
|
|
18460
18624
|
const startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
18461
|
-
db.update(researchRunQueries).set({ status: ResearchQueryStatuses.running, startedAt }).where(
|
|
18625
|
+
db.update(researchRunQueries).set({ status: ResearchQueryStatuses.running, startedAt }).where(and25(eq31(researchRunQueries.id, row.id), eq31(researchRunQueries.status, ResearchQueryStatuses.queued))).run();
|
|
18462
18626
|
const raw = await gate.run(async () => {
|
|
18463
18627
|
dispatched++;
|
|
18464
18628
|
return provider.adapter.executeTrackedQuery({ query: row.queryText, canonicalDomains: domains, competitorDomains, ...run.location ? { location: run.location } : {} }, config);
|
|
@@ -18489,7 +18653,7 @@ async function executeResearchRun(db, registry, runId, projectId, options = {})
|
|
|
18489
18653
|
rawResponse: raw.rawResponse,
|
|
18490
18654
|
answerEvidence: raw.evidence ?? null,
|
|
18491
18655
|
finishedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
18492
|
-
}).where(
|
|
18656
|
+
}).where(and25(eq31(researchRunQueries.id, row.id), eq31(researchRunQueries.status, ResearchQueryStatuses.running))).run();
|
|
18493
18657
|
if (completed.changes === 1) incrementResearchProgress(db, runId, "completedQueries");
|
|
18494
18658
|
} catch (error) {
|
|
18495
18659
|
if (!(error instanceof IncompleteAnswerError && error.resumable)) queryFailure ??= error;
|
|
@@ -18544,7 +18708,7 @@ function incrementResearchProgress(db, runId, column) {
|
|
|
18544
18708
|
}
|
|
18545
18709
|
function markResearchQueryFailed(db, runId, queryId, error) {
|
|
18546
18710
|
const message = describeError(error);
|
|
18547
|
-
const unfinished =
|
|
18711
|
+
const unfinished = and25(eq31(researchRunQueries.id, queryId), inArray13(researchRunQueries.status, unfinishedResearchQueryStatuses));
|
|
18548
18712
|
if (error instanceof IncompleteAnswerError && error.resumable) {
|
|
18549
18713
|
db.update(researchRunQueries).set({
|
|
18550
18714
|
status: ResearchQueryStatuses["waiting-upstream"],
|
|
@@ -18560,7 +18724,7 @@ function markResearchQueryFailed(db, runId, queryId, error) {
|
|
|
18560
18724
|
if (failed.changes === 1) incrementResearchProgress(db, runId, "failedQueries");
|
|
18561
18725
|
}
|
|
18562
18726
|
function markUnfinishedResearchQueriesFailed(db, runId, error) {
|
|
18563
|
-
db.update(researchRunQueries).set({ status: ResearchQueryStatuses.failed, error, finishedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(
|
|
18727
|
+
db.update(researchRunQueries).set({ status: ResearchQueryStatuses.failed, error, finishedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(and25(eq31(researchRunQueries.researchRunId, runId), inArray13(researchRunQueries.status, unfinishedResearchQueryStatuses))).run();
|
|
18564
18728
|
}
|
|
18565
18729
|
function finalizeResearchRun(db, runId, fatalError) {
|
|
18566
18730
|
const rows = db.select({ status: researchRunQueries.status }).from(researchRunQueries).where(eq31(researchRunQueries.researchRunId, runId)).all();
|
|
@@ -18580,7 +18744,7 @@ function finalizeResearchRun(db, runId, fatalError) {
|
|
|
18580
18744
|
}
|
|
18581
18745
|
|
|
18582
18746
|
// src/google-marketing-sync.ts
|
|
18583
|
-
import { and as
|
|
18747
|
+
import { and as and26, eq as eq32, isNull as isNull7, sql as sql12 } from "drizzle-orm";
|
|
18584
18748
|
var RUNTIME_FAILURE_REASONS = {
|
|
18585
18749
|
INVALID_INPUT: OutcomeReasonCodes.VALIDATION,
|
|
18586
18750
|
NOT_CONFIGURED: OutcomeReasonCodes.NOT_CONNECTED,
|
|
@@ -18617,7 +18781,7 @@ function assertSnapshotContext(metadata, expected) {
|
|
|
18617
18781
|
}
|
|
18618
18782
|
}
|
|
18619
18783
|
function requireSyncRun(db, runId, projectId, kind) {
|
|
18620
|
-
const run = db.select().from(runs).where(
|
|
18784
|
+
const run = db.select().from(runs).where(and26(
|
|
18621
18785
|
eq32(runs.id, runId),
|
|
18622
18786
|
eq32(runs.projectId, projectId),
|
|
18623
18787
|
eq32(runs.kind, kind)
|
|
@@ -18636,7 +18800,7 @@ function markFailed(db, runId, projectId, kind, expectedStatus, error) {
|
|
|
18636
18800
|
status: RunStatuses.failed,
|
|
18637
18801
|
error: serializeRunError({ message: safeErrorMessage(error) }),
|
|
18638
18802
|
finishedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
18639
|
-
}).where(
|
|
18803
|
+
}).where(and26(
|
|
18640
18804
|
eq32(runs.id, runId),
|
|
18641
18805
|
eq32(runs.projectId, projectId),
|
|
18642
18806
|
eq32(runs.kind, kind),
|
|
@@ -18644,7 +18808,7 @@ function markFailed(db, runId, projectId, kind, expectedStatus, error) {
|
|
|
18644
18808
|
)).run();
|
|
18645
18809
|
}
|
|
18646
18810
|
function claimSyncRun(db, runId, projectId, kind, startedAt) {
|
|
18647
|
-
const claim = db.update(runs).set({ status: RunStatuses.running, startedAt, error: null }).where(
|
|
18811
|
+
const claim = db.update(runs).set({ status: RunStatuses.running, startedAt, error: null }).where(and26(
|
|
18648
18812
|
eq32(runs.id, runId),
|
|
18649
18813
|
eq32(runs.projectId, projectId),
|
|
18650
18814
|
eq32(runs.kind, kind),
|
|
@@ -18712,7 +18876,7 @@ async function executeGoogleAdsMarketingSync(db, runtime, runId, projectId) {
|
|
|
18712
18876
|
status: RunStatuses.completed,
|
|
18713
18877
|
finishedAt,
|
|
18714
18878
|
error: null
|
|
18715
|
-
}).where(
|
|
18879
|
+
}).where(and26(
|
|
18716
18880
|
eq32(runs.id, runId),
|
|
18717
18881
|
eq32(runs.projectId, projectId),
|
|
18718
18882
|
eq32(runs.kind, RunKinds["google-ads-sync"]),
|
|
@@ -18736,11 +18900,11 @@ async function executeGoogleAdsMarketingSync(db, runtime, runId, projectId) {
|
|
|
18736
18900
|
lastMetricsSnapshotAt: result.metrics?.metadata.capturedAt ?? connection.lastMetricsSnapshotAt,
|
|
18737
18901
|
lastMetricsSnapshotId: result.metrics?.metadata.id ?? connection.lastMetricsSnapshotId,
|
|
18738
18902
|
updatedAt: finishedAt
|
|
18739
|
-
}).where(
|
|
18903
|
+
}).where(and26(
|
|
18740
18904
|
eq32(googleAdsConnections.id, connection.id),
|
|
18741
18905
|
eq32(googleAdsConnections.projectId, projectId),
|
|
18742
18906
|
eq32(googleAdsConnections.selectedCustomerId, connection.selectedCustomerId),
|
|
18743
|
-
connection.selectedLoginCustomerId === null ?
|
|
18907
|
+
connection.selectedLoginCustomerId === null ? isNull7(googleAdsConnections.selectedLoginCustomerId) : eq32(googleAdsConnections.selectedLoginCustomerId, connection.selectedLoginCustomerId),
|
|
18744
18908
|
eq32(googleAdsConnections.selectionGeneration, connection.selectionGeneration)
|
|
18745
18909
|
)).run();
|
|
18746
18910
|
if (connectionClaim.changes !== 1) {
|
|
@@ -18821,7 +18985,7 @@ async function executeGtmMarketingSync(db, runtime, runId, projectId) {
|
|
|
18821
18985
|
status: RunStatuses.completed,
|
|
18822
18986
|
finishedAt,
|
|
18823
18987
|
error: null
|
|
18824
|
-
}).where(
|
|
18988
|
+
}).where(and26(
|
|
18825
18989
|
eq32(runs.id, runId),
|
|
18826
18990
|
eq32(runs.projectId, projectId),
|
|
18827
18991
|
eq32(runs.kind, RunKinds["gtm-sync"]),
|
|
@@ -18846,12 +19010,12 @@ async function executeGtmMarketingSync(db, runtime, runId, projectId) {
|
|
|
18846
19010
|
lastSnapshotAt: snapshot.metadata.capturedAt,
|
|
18847
19011
|
lastSnapshotId: snapshot.metadata.id,
|
|
18848
19012
|
updatedAt: finishedAt
|
|
18849
|
-
}).where(
|
|
19013
|
+
}).where(and26(
|
|
18850
19014
|
eq32(gtmConnections.id, connection.id),
|
|
18851
19015
|
eq32(gtmConnections.projectId, projectId),
|
|
18852
19016
|
eq32(gtmConnections.selectedAccountId, connection.selectedAccountId),
|
|
18853
19017
|
eq32(gtmConnections.selectedContainerId, connection.selectedContainerId),
|
|
18854
|
-
connection.selectedWorkspaceId === null ?
|
|
19018
|
+
connection.selectedWorkspaceId === null ? isNull7(gtmConnections.selectedWorkspaceId) : eq32(gtmConnections.selectedWorkspaceId, connection.selectedWorkspaceId),
|
|
18855
19019
|
eq32(gtmConnections.selectionGeneration, connection.selectionGeneration)
|
|
18856
19020
|
)).run();
|
|
18857
19021
|
if (connectionClaim.changes !== 1) {
|
|
@@ -20260,13 +20424,13 @@ async function createServer(opts) {
|
|
|
20260
20424
|
const currentPasswordFingerprint = () => hashSessionToken(opts.config.dashboardPasswordHash ?? "");
|
|
20261
20425
|
opts.db.delete(dashboardSessions).where(or4(
|
|
20262
20426
|
eq33(dashboardSessions.apiKeyHash, ""),
|
|
20263
|
-
|
|
20427
|
+
and27(
|
|
20264
20428
|
isNotNull5(dashboardSessions.passwordFingerprint),
|
|
20265
20429
|
ne4(dashboardSessions.passwordFingerprint, currentPasswordFingerprint())
|
|
20266
20430
|
)
|
|
20267
20431
|
)).run();
|
|
20268
20432
|
if (!opts.config.dashboardPasswordHash) {
|
|
20269
|
-
const lastPasswordEventAt = (action) => opts.db.select({ at: max(auditLog.createdAt) }).from(auditLog).where(
|
|
20433
|
+
const lastPasswordEventAt = (action) => opts.db.select({ at: max(auditLog.createdAt) }).from(auditLog).where(and27(eq33(auditLog.entityType, "dashboard-password"), eq33(auditLog.action, action))).get()?.at ?? null;
|
|
20270
20434
|
const createdAt = lastPasswordEventAt("dashboard-password.created");
|
|
20271
20435
|
const clearedAt = lastPasswordEventAt("dashboard-password.cleared");
|
|
20272
20436
|
if (createdAt !== null && (clearedAt === null || clearedAt < createdAt)) {
|
|
@@ -20300,7 +20464,7 @@ async function createServer(opts) {
|
|
|
20300
20464
|
currentApiKeyHash: apiKeys.keyHash,
|
|
20301
20465
|
passwordFingerprint: dashboardSessions.passwordFingerprint,
|
|
20302
20466
|
expiresAt: dashboardSessions.expiresAt
|
|
20303
|
-
}).from(dashboardSessions).innerJoin(apiKeys, eq33(apiKeys.id, dashboardSessions.apiKeyId)).where(
|
|
20467
|
+
}).from(dashboardSessions).innerJoin(apiKeys, eq33(apiKeys.id, dashboardSessions.apiKeyId)).where(and27(eq33(dashboardSessions.tokenHash, tokenHash), isNull8(apiKeys.revokedAt))).get();
|
|
20304
20468
|
if (!session) return null;
|
|
20305
20469
|
const expiresAt = Date.parse(session.expiresAt);
|
|
20306
20470
|
if (!Number.isFinite(expiresAt) || expiresAt <= Date.now() || session.apiKeyHash !== session.currentApiKeyHash || session.passwordFingerprint !== null && session.passwordFingerprint !== currentPasswordFingerprint()) {
|
|
@@ -20579,7 +20743,7 @@ async function createServer(opts) {
|
|
|
20579
20743
|
opts.db.update(projects).set({ answerFieldsRecompute: null }).where(eq33(projects.id, projectId)).run();
|
|
20580
20744
|
if (recomputeDebts.get(projectId) === debt) recomputeDebts.delete(projectId);
|
|
20581
20745
|
} else if (debt.latestFull === 0) {
|
|
20582
|
-
opts.db.update(projects).set({ answerFieldsRecompute: "competitors" }).where(
|
|
20746
|
+
opts.db.update(projects).set({ answerFieldsRecompute: "competitors" }).where(and27(eq33(projects.id, projectId), eq33(projects.answerFieldsRecompute, "full"))).run();
|
|
20583
20747
|
}
|
|
20584
20748
|
} catch (err) {
|
|
20585
20749
|
app.log.error({ err, projectId }, "could not clear the owed answer field recompute");
|