@canonry/canonry 7.65.1 → 7.67.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/seo-analysis.md +15 -1
- package/assets/agent-workspace/skills/canonry/references/canonry-cli.md +2 -2
- package/assets/assets/{AgentSetupPage-BDLMjn1I.js → AgentSetupPage-ORsy3QEa.js} +3 -3
- package/assets/assets/{AuditHistoryPanel-qhIB8KRK.js → AuditHistoryPanel-vTHZDbfs.js} +1 -1
- package/assets/assets/{BacklinksPage-iWrZ0-Pp.js → BacklinksPage-FqADB5sO.js} +1 -1
- package/assets/assets/{HistoryPage-BP1_GGlT.js → HistoryPage-C4l5Toqa.js} +1 -1
- package/assets/assets/MeasurementPropertyPage-x8f7xanT.js +4 -0
- package/assets/assets/ProjectPage-CG9I2-B4.js +9 -0
- package/assets/assets/{RunsPage-Ddd2oE1Y.js → RunsPage-oyhhwAPq.js} +1 -1
- package/assets/assets/{SettingsPage-D_QdYWDO.js → SettingsPage-DJc4Di1M.js} +1 -1
- package/assets/assets/SiteHealthSection-BvqFg3rG.js +2 -0
- package/assets/assets/ToneBadge-CTWpeKu8.js +60 -0
- package/assets/assets/{TrafficPage-VnBUE1S6.js → TrafficPage-DAD0FFkL.js} +2 -2
- package/assets/assets/TrafficSourceDetailPage-BBEzbawh.js +1 -0
- package/assets/assets/{account-context-CWRd3KeS.js → account-context-DMHOefRt.js} +1 -1
- package/assets/assets/{card-Cb_fM36G.js → card-Brp6fFNG.js} +1 -1
- package/assets/assets/{extract-error-message-DSIAoHTR.js → extract-error-message-5QpQCsGb.js} +1 -1
- package/assets/assets/{format-helpers-CxNDa1oa.js → format-helpers-BFM0FRjT.js} +1 -1
- package/assets/assets/index-BBNs-RhI.js +60 -0
- package/assets/assets/{query-client-DLdAMlsX.js → query-client-BMTW-xss.js} +1 -1
- package/assets/assets/react-query-DGmY6hJa.js +1 -0
- package/assets/assets/{react-sigma_core.esm.min-DEG8vFYD.js → react-sigma_core.esm.min-C0VjhgDo.js} +1 -1
- package/assets/assets/service-DW9u_0aZ.js +1 -0
- package/assets/index.html +7 -7
- package/dist/{ads-D4WQOT5K.js → ads-BXGEL4WI.js} +3 -3
- package/dist/{agent-E3IYC7OJ.js → agent-LFX7GHWD.js} +7 -7
- package/dist/{agent-ask-ZSKRPB2Z.js → agent-ask-QJV225AT.js} +3 -3
- package/dist/{agent-conversations-YHPYPQE2.js → agent-conversations-2TZDIGA6.js} +3 -3
- package/dist/{agent-memory-6OWOOD5O.js → agent-memory-XCY3ZG5C.js} +3 -3
- package/dist/{agent-providers-7KZK37UO.js → agent-providers-VS6PH55Y.js} +3 -3
- package/dist/{agent-transcript-3O7GPML6.js → agent-transcript-BUUHOTZM.js} +3 -3
- package/dist/{analytics-P2LLIUYK.js → analytics-YWJWPUNF.js} +3 -3
- package/dist/{apply-VBNNJOCT.js → apply-BMCYFBEJ.js} +3 -3
- package/dist/{backfill-F5SSURCH.js → backfill-YBOKT5LT.js} +9 -9
- package/dist/{backlinks-4RE3TNNA.js → backlinks-QNNABB4N.js} +3 -3
- package/dist/{bing-V6UN7D44.js → bing-KEMXBABP.js} +3 -3
- package/dist/{bootstrap-SHTSPAI5.js → bootstrap-X2AADK4M.js} +10 -10
- package/dist/{cdp-GDL2VFI3.js → cdp-WVYUEV4U.js} +7 -7
- package/dist/{chunk-3QKKHU7D.js → chunk-2X7DFLMD.js} +8 -8
- package/dist/{chunk-2AROR73F.js → chunk-32BTTG5N.js} +5 -5
- package/dist/{chunk-BV6NXSAH.js → chunk-37R6GTU7.js} +21 -3
- package/dist/{chunk-HPUZMBS6.js → chunk-5DMESZDF.js} +2 -2
- package/dist/{chunk-XOMUSIM7.js → chunk-6DJN7HCK.js} +83 -11
- package/dist/{chunk-SRTX6XYR.js → chunk-AFRCTLUL.js} +370 -337
- package/dist/{chunk-Z2EENWM3.js → chunk-AVISYPUU.js} +877 -478
- package/dist/{chunk-HI2HCYLB.js → chunk-CBYKQRDH.js} +2 -2
- package/dist/{chunk-S4VCXCRQ.js → chunk-EKSXR7RJ.js} +1 -1
- package/dist/{chunk-66DPEVLJ.js → chunk-G6PZEDTH.js} +3 -3
- package/dist/{chunk-GZPGDAAF.js → chunk-GTPGYFZ4.js} +2 -2
- package/dist/{chunk-Q2WZAD5W.js → chunk-HX3WLTZU.js} +1 -1
- package/dist/{chunk-JSAYC36T.js → chunk-IEBMODUI.js} +193 -47
- package/dist/{chunk-2UFA3DYM.js → chunk-KJLICF53.js} +4 -4
- package/dist/{chunk-ZPRJCSSR.js → chunk-LU3M54IY.js} +1 -1
- package/dist/{chunk-HO5W6QC7.js → chunk-MD5PHJBX.js} +2 -1
- package/dist/{chunk-SL3Y7IOX.js → chunk-MLOGEXIO.js} +1 -1
- package/dist/{chunk-LTX3TJ3E.js → chunk-MOJ2XZXK.js} +1 -1
- package/dist/{chunk-IYB3EGSD.js → chunk-NHHZQWMI.js} +1 -1
- package/dist/{chunk-H6HIJAIU.js → chunk-P74TPIWZ.js} +2 -2
- package/dist/{chunk-M3OZL6PQ.js → chunk-RRS7U6HQ.js} +4 -4
- package/dist/{chunk-X4AHYJCE.js → chunk-UHLWNU6I.js} +5 -5
- package/dist/{chunk-C2PYKUD3.js → chunk-UNCOK6DA.js} +1 -1
- package/dist/{chunk-IJKKK7F4.js → chunk-UXTNDZKM.js} +1 -1
- package/dist/{chunk-GM5ZOWBK.js → chunk-VR4MYSEH.js} +4 -4
- package/dist/{chunk-ZWZOQNLD.js → chunk-WSYLDPYF.js} +1 -1
- package/dist/{chunk-AE6HJZBU.js → chunk-WZ4LUM26.js} +3 -3
- package/dist/{chunk-DQVJB6CZ.js → chunk-XEGYSZEV.js} +99 -9
- package/dist/{chunk-F7NZEUXP.js → chunk-XEPEMON2.js} +14 -2
- package/dist/{chunk-CRKOGLKJ.js → chunk-XIA6NWIF.js} +4 -4
- package/dist/{chunk-737TOG2F.js → chunk-XMIKSCAO.js} +1 -1
- package/dist/{chunk-2Y7RN5HG.js → chunk-Y54PMWBH.js} +3 -3
- package/dist/{chunk-QNRMYDK6.js → chunk-YP743QX5.js} +1 -1
- package/dist/{chunk-XMKXXWMO.js → chunk-ZGUR4AZM.js} +2 -2
- package/dist/{citations-GO4SPZYS.js → citations-ZSI5BOGD.js} +3 -3
- package/dist/{cli-catalog-6XYQY3PI.js → cli-catalog-J2HUCGJL.js} +3 -3
- package/dist/{cli-commands-2TZKVGK6.js → cli-commands-XFMVFREG.js} +7 -7
- package/dist/cli.js +13 -13
- package/dist/{competitor-JBTKRJ6Y.js → competitor-WJDN3DLP.js} +3 -3
- package/dist/{content-LKXXQ7X4.js → content-K52YV4BK.js} +3 -3
- package/dist/{dashboard-LUA7UX6B.js → dashboard-L6RKTP35.js} +6 -6
- package/dist/{demo-I7JDZJUW.js → demo-Y7XRD4X4.js} +1 -1
- package/dist/{demo-server-GNGGWERV.js → demo-server-QNQX5CNP.js} +73 -11
- package/dist/{discover-TODAYYJ2.js → discover-UO5M7HQZ.js} +3 -3
- package/dist/{discover-eval-FWQESEYB.js → discover-eval-DN4PS6VN.js} +3 -3
- package/dist/{doctor-AXNJT2UC.js → doctor-7UYUXG7J.js} +3 -3
- package/dist/{evidence-2XGEY4ZC.js → evidence-DGKN5SGQ.js} +3 -3
- package/dist/{export-cmd-7RMYBIBZ.js → export-cmd-QAQWWIRL.js} +3 -3
- package/dist/{ga-HZDIZGCO.js → ga-4LGTEA6J.js} +3 -3
- package/dist/{gbp-SMRBWJEL.js → gbp-XOLYZ4XC.js} +3 -3
- package/dist/{google-2CN345XS.js → google-CAQDCFA6.js} +4 -4
- package/dist/{google-marketing-W7T2VTVW.js → google-marketing-HU5I3XPP.js} +1 -1
- package/dist/{health-cmd-W7OWZ4ZI.js → health-cmd-3PEB3M45.js} +3 -3
- package/dist/{history-UVNCBHTE.js → history-UGUWYPOY.js} +3 -3
- package/dist/index.d.ts +342 -161
- package/dist/index.js +24 -24
- package/dist/{init-2IWS2R7G.js → init-DEW65BLN.js} +11 -11
- package/dist/{insights-PQ52G5LU.js → insights-ZC3R4ZYG.js} +3 -3
- package/dist/intelligence-service-XRJVXZYI.js +15 -0
- package/dist/{journeys-3PP5GYVJ.js → journeys-W6F4BCXF.js} +3 -3
- package/dist/{keys-CWZUX7IQ.js → keys-ME7VRF73.js} +3 -3
- package/dist/{keyword-5OMMULLY.js → keyword-R3PLGSO5.js} +3 -3
- package/dist/{logs-3FTE42E3.js → logs-UM7CWHYB.js} +3 -3
- package/dist/{mcp-FK5DTI7I.js → mcp-BUPP5HC6.js} +2 -2
- package/dist/mcp.js +9 -9
- package/dist/{measurement-plan-ICO2BQNT.js → measurement-plan-R4KZHJII.js} +5 -5
- package/dist/{notify-ML4PYD3L.js → notify-4C26D2MY.js} +3 -3
- package/dist/{organic-evidence-4U4H3DCR.js → organic-evidence-SLIISYHW.js} +3 -3
- package/dist/{overview-NF427VFO.js → overview-JJZZOYQU.js} +4 -4
- package/dist/{project-AAO65OF7.js → project-XJH2OECP.js} +3 -3
- package/dist/{query-7ZOUQNF5.js → query-ZWAIWIYB.js} +3 -3
- package/dist/{research-UQ7AF5N3.js → research-GTVWVZBI.js} +3 -3
- package/dist/{results-export-CZCLC4ZH.js → results-export-CPAWO5AV.js} +3 -3
- package/dist/{run-4XJZW52R.js → run-RI2BMD7U.js} +4 -4
- package/dist/{schedule-D3E5FDBE.js → schedule-HDDWULBX.js} +3 -3
- package/dist/{search-DYET2IL4.js → search-TT3Q2BBP.js} +3 -3
- package/dist/{serve-QWMTR3SN.js → serve-6JYPUU24.js} +25 -25
- package/dist/{session-7CW2V5EH.js → session-DDESFV3N.js} +12 -12
- package/dist/{settings-ZISHACFX.js → settings-4AYXYJVA.js} +3 -3
- package/dist/{skills-WU6IN3NL.js → skills-UFMLMMY5.js} +2 -2
- package/dist/{snapshot-KCKQDJ2W.js → snapshot-KLWQGZB6.js} +3 -3
- package/dist/{src-WIOU5G4V.js → src-6IHGEMT2.js} +6 -2
- package/dist/{status-CYZFVMYA.js → status-HRIUPGUM.js} +4 -4
- package/dist/{technical-aeo-P5CS22AR.js → technical-aeo-HKJQHMMS.js} +3 -3
- package/dist/{telemetry-LPK5F2WU.js → telemetry-IS6KZK32.js} +4 -4
- package/dist/{tools-E3B2GZ6Q.js → tools-ZNVQFEAX.js} +11 -11
- package/dist/{traffic-5KZYXXIQ.js → traffic-PFHFGN44.js} +5 -5
- package/dist/{users-2F346HIW.js → users-BSLUSU6W.js} +3 -3
- package/dist/{visibility-stats-ITA6DNF3.js → visibility-stats-MPRNHPZW.js} +3 -3
- package/dist/{wordpress-ZHCFT6V3.js → wordpress-U5XKHURL.js} +3 -3
- package/package.json +1 -1
- package/assets/assets/MeasurementPropertyPage-dxGJ1GF9.js +0 -4
- package/assets/assets/ProjectPage-W6oYA85b.js +0 -9
- package/assets/assets/SiteHealthSection-DT20rNw-.js +0 -2
- package/assets/assets/ToneBadge-CJ6ObMRe.js +0 -60
- package/assets/assets/TrafficSourceDetailPage-CdoL5Dt9.js +0 -1
- package/assets/assets/index-D6WlecSB.js +0 -60
- package/assets/assets/react-query-BktZZtSC.js +0 -1
- package/assets/assets/service-DKP-BIBy.js +0 -1
- package/dist/intelligence-service-IAUGKMDO.js +0 -15
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
isStoredPageReadTool,
|
|
6
6
|
isStoredReadTool,
|
|
7
7
|
truncateToolResult
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-XIA6NWIF.js";
|
|
9
9
|
import {
|
|
10
10
|
CANONRY_MCP_TOOLKITS
|
|
11
11
|
} from "./chunk-NDTU7FGL.js";
|
|
@@ -22,15 +22,15 @@ import {
|
|
|
22
22
|
resolveApiKeyFor,
|
|
23
23
|
resolveModelForProvider,
|
|
24
24
|
validateAgentProviderRegistry
|
|
25
|
-
} from "./chunk-
|
|
25
|
+
} from "./chunk-UNCOK6DA.js";
|
|
26
26
|
import {
|
|
27
27
|
CanonryMcpToolNames,
|
|
28
28
|
canonryMcpTools
|
|
29
|
-
} from "./chunk-
|
|
29
|
+
} from "./chunk-37R6GTU7.js";
|
|
30
30
|
import {
|
|
31
31
|
agentToolEvents,
|
|
32
32
|
llmUsageEvents
|
|
33
|
-
} from "./chunk-
|
|
33
|
+
} from "./chunk-6DJN7HCK.js";
|
|
34
34
|
import {
|
|
35
35
|
MeasurementPortfolioLists,
|
|
36
36
|
agentTurnLimitsSchema,
|
|
@@ -38,7 +38,7 @@ import {
|
|
|
38
38
|
describeError,
|
|
39
39
|
validationError,
|
|
40
40
|
visibilityReportRequestSchema
|
|
41
|
-
} from "./chunk-
|
|
41
|
+
} from "./chunk-XEGYSZEV.js";
|
|
42
42
|
|
|
43
43
|
// src/agent/session.ts
|
|
44
44
|
import fs3 from "fs";
|
|
@@ -2,11 +2,11 @@ import {
|
|
|
2
2
|
dedupeGscSitemapUrls,
|
|
3
3
|
resolveDiscoveredGscSitemapUrls,
|
|
4
4
|
submitGscSitemapBatches
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-WSYLDPYF.js";
|
|
6
6
|
import {
|
|
7
7
|
measurementDraftOperationSchema,
|
|
8
8
|
runMeasurementDraftAction
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-LU3M54IY.js";
|
|
10
10
|
import {
|
|
11
11
|
CliError
|
|
12
12
|
} from "./chunk-HGMYEDAQ.js";
|
|
@@ -141,6 +141,8 @@ import {
|
|
|
141
141
|
seoDomainIntelligenceListSchema,
|
|
142
142
|
seoEvidenceQuerySchema,
|
|
143
143
|
seoKeywordMetricsListSchema,
|
|
144
|
+
seoMarketRankingListSchema,
|
|
145
|
+
seoMarketRankingQuerySchema,
|
|
144
146
|
seoOperatorTaskSchema,
|
|
145
147
|
seoOverviewSchema,
|
|
146
148
|
seoProfileInputSchema,
|
|
@@ -173,7 +175,7 @@ import {
|
|
|
173
175
|
trafficSeriesGranularitySchema,
|
|
174
176
|
visibilityCompareSelectionSchema,
|
|
175
177
|
visibilityReportRequestSchema
|
|
176
|
-
} from "./chunk-
|
|
178
|
+
} from "./chunk-XEGYSZEV.js";
|
|
177
179
|
|
|
178
180
|
// src/mcp/tool-registry.ts
|
|
179
181
|
import { z as z2 } from "zod";
|
|
@@ -1123,6 +1125,7 @@ var sentimentPreviewInputSchema = sentimentBackfillSelectionSchema.safeExtend({
|
|
|
1123
1125
|
var sentimentBackfillInputSchema = sentimentBackfillRequestSchema.extend({ project: projectNameSchema });
|
|
1124
1126
|
var seoIdInputSchema = projectInputSchema.extend({ id: z2.string().min(1) });
|
|
1125
1127
|
var seoEvidenceInputSchema = seoEvidenceQuerySchema.extend({ project: projectNameSchema });
|
|
1128
|
+
var seoMarketRankingInputSchema = seoMarketRankingQuerySchema.extend({ project: projectNameSchema });
|
|
1126
1129
|
var rawCanonryMcpTools = [
|
|
1127
1130
|
defineTool({
|
|
1128
1131
|
name: "canonry_seo_operator_task",
|
|
@@ -1334,6 +1337,21 @@ var rawCanonryMcpTools = [
|
|
|
1334
1337
|
return client.getSeoRankedKeywords(project, query);
|
|
1335
1338
|
}
|
|
1336
1339
|
}),
|
|
1340
|
+
defineTool({
|
|
1341
|
+
name: "canonry_seo_market_rankings",
|
|
1342
|
+
title: "SEO market rankings",
|
|
1343
|
+
description: "Read stored SEO data only. No provider calls. The site's Google position for each tracked non-brand query in each market, searched from that market's location; these are the same tracked queries AI visibility measures, and searchText is the exact text searched. searchStatus says how each search ended: a null rank in a completed or partial row means not in the checked results (depth, lower when only part of the results came back); a failed or cancelled row has no rank because the search returned nothing. Filter with marketKey or regionKey and keep runId fixed while paging.",
|
|
1344
|
+
access: "read",
|
|
1345
|
+
tier: "seo",
|
|
1346
|
+
inputSchema: seoMarketRankingInputSchema,
|
|
1347
|
+
outputSchema: seoMarketRankingListSchema,
|
|
1348
|
+
annotations: readAnnotations(),
|
|
1349
|
+
openApiOperations: ["GET /api/v1/projects/{name}/seo/market-rankings"],
|
|
1350
|
+
handler: (client, input) => {
|
|
1351
|
+
const { project, ...query } = input;
|
|
1352
|
+
return client.getSeoMarketRankings(project, query);
|
|
1353
|
+
}
|
|
1354
|
+
}),
|
|
1337
1355
|
defineTool({
|
|
1338
1356
|
name: "canonry_sentiment_settings",
|
|
1339
1357
|
title: "Read sentiment readiness and settings",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
parseJsonColumn
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-6DJN7HCK.js";
|
|
4
4
|
import {
|
|
5
5
|
AI_ENGINE_DOMAINS,
|
|
6
6
|
AI_ENGINE_SELF_DOMAINS,
|
|
@@ -26,7 +26,7 @@ import {
|
|
|
26
26
|
retryAfterDelayMs,
|
|
27
27
|
usageCount,
|
|
28
28
|
withRetry
|
|
29
|
-
} from "./chunk-
|
|
29
|
+
} from "./chunk-XEGYSZEV.js";
|
|
30
30
|
|
|
31
31
|
// ../provider-openai/src/normalize.ts
|
|
32
32
|
import OpenAI from "openai";
|
|
@@ -20,7 +20,7 @@ import {
|
|
|
20
20
|
templateLinkRatio,
|
|
21
21
|
templateLinkUbiquityAvailable,
|
|
22
22
|
validationError
|
|
23
|
-
} from "./chunk-
|
|
23
|
+
} from "./chunk-XEGYSZEV.js";
|
|
24
24
|
import {
|
|
25
25
|
__export
|
|
26
26
|
} from "./chunk-MLKGABMK.js";
|
|
@@ -227,8 +227,10 @@ var seoTasks = sqliteTable("seo_tasks", {
|
|
|
227
227
|
reconciliation: text("reconciliation", { mode: "json" }).$type(),
|
|
228
228
|
startedAt: text("started_at"),
|
|
229
229
|
finishedAt: text("finished_at"),
|
|
230
|
-
error: text("error")
|
|
231
|
-
|
|
230
|
+
error: text("error"),
|
|
231
|
+
/** Set on a second attempt: the task whose search-engine error it repeats. A retry is never retried. */
|
|
232
|
+
retryOf: text("retry_of")
|
|
233
|
+
}, (t) => [index("idx_seo_tasks_job").on(t.jobId), index("idx_seo_tasks_retry").on(t.retryOf)]);
|
|
232
234
|
var seoBudgets = sqliteTable("seo_budgets", {
|
|
233
235
|
scope: text("scope").notNull(),
|
|
234
236
|
period: text("period").notNull(),
|
|
@@ -971,6 +973,12 @@ var schedules = sqliteTable2("schedules", {
|
|
|
971
973
|
sourceId: text2("source_id"),
|
|
972
974
|
lastRunAt: text2("last_run_at"),
|
|
973
975
|
nextRunAt: text2("next_run_at"),
|
|
976
|
+
/**
|
|
977
|
+
* When the schedule last turned on (created enabled, or changed from disabled to enabled);
|
|
978
|
+
* null while it is disabled. Rows written before migration 196 hold null while enabled:
|
|
979
|
+
* read those as enabled since `createdAt`.
|
|
980
|
+
*/
|
|
981
|
+
enabledAt: text2("enabled_at"),
|
|
974
982
|
createdAt: text2("created_at").notNull(),
|
|
975
983
|
updatedAt: text2("updated_at").notNull()
|
|
976
984
|
}, (table) => [
|
|
@@ -8224,6 +8232,28 @@ var MIGRATION_VERSIONS = [
|
|
|
8224
8232
|
`ALTER TABLE ga_measurement_sync_state ADD COLUMN key_event_names TEXT`,
|
|
8225
8233
|
`ALTER TABLE ga_measurement_sync_state ADD COLUMN key_events_error TEXT`
|
|
8226
8234
|
]
|
|
8235
|
+
},
|
|
8236
|
+
{
|
|
8237
|
+
// A market-rankings search that hit a search-engine error is sent once
|
|
8238
|
+
// more at the end of its refresh, as a new task that points at the first
|
|
8239
|
+
// attempt. Nullable with no default: existing tasks and every task an
|
|
8240
|
+
// older writer adds are first attempts.
|
|
8241
|
+
version: 195,
|
|
8242
|
+
name: "seo-task-retry",
|
|
8243
|
+
statements: [
|
|
8244
|
+
`ALTER TABLE seo_tasks ADD COLUMN retry_of TEXT`,
|
|
8245
|
+
`CREATE INDEX IF NOT EXISTS idx_seo_tasks_retry ON seo_tasks(retry_of)`
|
|
8246
|
+
]
|
|
8247
|
+
},
|
|
8248
|
+
{
|
|
8249
|
+
// When a schedule last turned on, so a schedule enabled long after it was
|
|
8250
|
+
// created is graded from that point. Nullable with no default: existing
|
|
8251
|
+
// rows read as enabled since creation, and an older writer leaves it null.
|
|
8252
|
+
version: 196,
|
|
8253
|
+
name: "schedules-enabled-at",
|
|
8254
|
+
statements: [
|
|
8255
|
+
`ALTER TABLE schedules ADD COLUMN enabled_at TEXT`
|
|
8256
|
+
]
|
|
8227
8257
|
}
|
|
8228
8258
|
];
|
|
8229
8259
|
var AERO_LEDGER_REBUILDS = [
|
|
@@ -9431,6 +9461,20 @@ var SentimentRepository = class {
|
|
|
9431
9461
|
// ../db/src/seo-repository.ts
|
|
9432
9462
|
import crypto from "crypto";
|
|
9433
9463
|
import { and as and2, asc as asc2, eq as eq3, inArray as inArray2, sql as sql5 } from "drizzle-orm";
|
|
9464
|
+
function currentSeoTasks(tasks) {
|
|
9465
|
+
const replaced = new Set(tasks.flatMap((task) => task.retryOf && task.status !== SeoRunStatuses.cancelled ? [task.retryOf] : []));
|
|
9466
|
+
return tasks.filter((task) => task.retryOf ? task.status !== SeoRunStatuses.cancelled : !replaced.has(task.id));
|
|
9467
|
+
}
|
|
9468
|
+
function currentSeoTaskCondition() {
|
|
9469
|
+
return sql5`(${seoTasks.retryOf} IS NULL OR ${seoTasks.status} <> ${SeoRunStatuses.cancelled}) AND NOT EXISTS (SELECT 1 FROM seo_tasks AS retry WHERE retry.retry_of = ${seoTasks.id} AND retry.status <> ${SeoRunStatuses.cancelled})`;
|
|
9470
|
+
}
|
|
9471
|
+
function bucketLimits(job, policy) {
|
|
9472
|
+
return /* @__PURE__ */ new Map([
|
|
9473
|
+
[`account-day:${job.accountKey}`, policy.accountDailyLimitMicros],
|
|
9474
|
+
[`account-month:${job.accountKey}`, policy.accountMonthlyLimitMicros],
|
|
9475
|
+
[`project-month:${job.projectKey}`, policy.projectMonthlyLimitMicros]
|
|
9476
|
+
]);
|
|
9477
|
+
}
|
|
9434
9478
|
var SeoRepository = class {
|
|
9435
9479
|
constructor(db) {
|
|
9436
9480
|
this.db = db;
|
|
@@ -9481,15 +9525,11 @@ var SeoRepository = class {
|
|
|
9481
9525
|
}
|
|
9482
9526
|
if (tx.select({ id: seoTasks.id }).from(seoTasks).innerJoin(seoJobs, eq3(seoTasks.jobId, seoJobs.id)).where(and2(eq3(seoJobs.accountKey, job.accountKey), inArray2(seoTasks.status, [SeoRunStatuses.running, SeoRunStatuses["reconciliation-required"]]))).get()) return null;
|
|
9483
9527
|
const allTasks = tx.select().from(seoTasks).where(eq3(seoTasks.jobId, jobId)).orderBy(asc2(seoTasks.id)).all();
|
|
9484
|
-
const task = allTasks.find((row) => row.status === SeoRunStatuses.queued);
|
|
9528
|
+
const task = allTasks.find((row) => row.status === SeoRunStatuses.queued && !row.retryOf) ?? allTasks.find((row) => row.status === SeoRunStatuses.queued);
|
|
9485
9529
|
if (!task) return null;
|
|
9486
9530
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
9487
9531
|
const outstanding = allTasks.filter((row) => row.status === SeoRunStatuses.queued).reduce((sum, row) => sum + row.reservedMicros, 0);
|
|
9488
|
-
const limits =
|
|
9489
|
-
[`account-day:${job.accountKey}`, policy.accountDailyLimitMicros],
|
|
9490
|
-
[`account-month:${job.accountKey}`, policy.accountMonthlyLimitMicros],
|
|
9491
|
-
[`project-month:${job.projectKey}`, policy.projectMonthlyLimitMicros]
|
|
9492
|
-
]);
|
|
9532
|
+
const limits = bucketLimits(job, policy);
|
|
9493
9533
|
const buckets = job.buckets.map((bucket) => ({ ...bucket, limit: limits.get(bucket.scope) ?? 0, period: now.slice(0, bucket.scope.startsWith("account-day:") ? 10 : 7) }));
|
|
9494
9534
|
const runMaximum = allTasks.reduce((sum, row) => sum + (row.actualMicros ?? row.reservedMicros), 0);
|
|
9495
9535
|
const allowed = runMaximum <= policy.maxRunCostMicros && buckets.every((bucket) => {
|
|
@@ -9520,7 +9560,11 @@ var SeoRepository = class {
|
|
|
9520
9560
|
renew(taskId, owner) {
|
|
9521
9561
|
this.db.update(seoTasks).set({ leaseUntil: new Date(Date.now() + 12e4).toISOString() }).where(and2(eq3(seoTasks.id, taskId), eq3(seoTasks.leaseOwner, owner), eq3(seoTasks.status, SeoRunStatuses.running))).run();
|
|
9522
9562
|
}
|
|
9523
|
-
|
|
9563
|
+
/**
|
|
9564
|
+
* Store a task's outcome. With `retryPolicy`, a search the adapter marks `retryable` gets one
|
|
9565
|
+
* more attempt, queued in the same transaction (see `queueRetry`).
|
|
9566
|
+
*/
|
|
9567
|
+
settle(taskId, result, owner, retryPolicy) {
|
|
9524
9568
|
return this.db.transaction((tx) => {
|
|
9525
9569
|
const task = tx.select().from(seoTasks).where(eq3(seoTasks.id, taskId)).get();
|
|
9526
9570
|
if (!task || task.leaseOwner !== owner || task.reconciliation || task.status !== SeoRunStatuses.running && task.status !== SeoRunStatuses["reconciliation-required"]) return false;
|
|
@@ -9532,10 +9576,36 @@ var SeoRepository = class {
|
|
|
9532
9576
|
if (known) for (const bucket of job.buckets) tx.update(seoBudgets).set({ reservedMicros: sql5`${seoBudgets.reservedMicros} - ${task.reservedMicros}`, spentMicros: sql5`${seoBudgets.spentMicros} + ${actual}` }).where(and2(eq3(seoBudgets.scope, bucket.scope), eq3(seoBudgets.period, bucket.period))).run();
|
|
9533
9577
|
const error = status === SeoRunStatuses["reconciliation-required"] ? "The upstream outcome needs operator reconciliation. This request will not be retried." : status === SeoRunStatuses.failed ? "The SEO provider could not complete this task." : null;
|
|
9534
9578
|
tx.update(seoTasks).set({ status, collectionOutcome: result.outcome, actualMicros: known ? actual : null, receipts: result.tasks, results: result.results, error, leaseOwner: null, leaseUntil: null, finishedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(eq3(seoTasks.id, taskId)).run();
|
|
9579
|
+
if (status === SeoRunStatuses.failed && result.retryable === true && retryPolicy) this.queueRetry(tx, task, job, retryPolicy);
|
|
9535
9580
|
this.finish(job.id);
|
|
9536
9581
|
return true;
|
|
9537
9582
|
}, { behavior: "immediate" });
|
|
9538
9583
|
}
|
|
9584
|
+
/**
|
|
9585
|
+
* Queue the one retry a search gets after a search-engine error. It reserves the first attempt's
|
|
9586
|
+
* amount again, inside the same limits, and claims take it after every first attempt, so it runs
|
|
9587
|
+
* at the end of the refresh. A retry is never retried, a cancelled refresh gets none, and one that
|
|
9588
|
+
* would pass a limit is not queued, leaving the search failed.
|
|
9589
|
+
*/
|
|
9590
|
+
queueRetry(tx, task, job, policy) {
|
|
9591
|
+
if (job.cancelRequested || task.retryOf || tx.select({ id: seoTasks.id }).from(seoTasks).where(eq3(seoTasks.retryOf, task.id)).get()) return;
|
|
9592
|
+
const amount = task.reservedMicros;
|
|
9593
|
+
const runCost = tx.select({ actualMicros: seoTasks.actualMicros, reservedMicros: seoTasks.reservedMicros }).from(seoTasks).where(eq3(seoTasks.jobId, job.id)).all().reduce((sum, row) => sum + (row.actualMicros ?? row.reservedMicros), 0);
|
|
9594
|
+
if (runCost + amount > policy.maxRunCostMicros) return;
|
|
9595
|
+
const limits = bucketLimits(job, policy);
|
|
9596
|
+
const fits = job.buckets.every((bucket) => {
|
|
9597
|
+
const current = tx.select({ spentMicros: seoBudgets.spentMicros }).from(seoBudgets).where(and2(eq3(seoBudgets.scope, bucket.scope), eq3(seoBudgets.period, bucket.period))).get();
|
|
9598
|
+
const held = tx.select({ total: sql5`coalesce(sum(${seoBudgets.reservedMicros}),0)` }).from(seoBudgets).where(eq3(seoBudgets.scope, bucket.scope)).get().total;
|
|
9599
|
+
return (current?.spentMicros ?? 0) + held + amount <= (limits.get(bucket.scope) ?? 0);
|
|
9600
|
+
});
|
|
9601
|
+
if (!fits) return;
|
|
9602
|
+
for (const bucket of job.buckets) {
|
|
9603
|
+
tx.insert(seoBudgets).values({ scope: bucket.scope, period: bucket.period }).onConflictDoNothing().run();
|
|
9604
|
+
tx.update(seoBudgets).set({ reservedMicros: sql5`${seoBudgets.reservedMicros} + ${amount}` }).where(and2(eq3(seoBudgets.scope, bucket.scope), eq3(seoBudgets.period, bucket.period))).run();
|
|
9605
|
+
}
|
|
9606
|
+
const id = crypto.randomUUID();
|
|
9607
|
+
tx.insert(seoTasks).values({ id, jobId: job.id, request: { ...task.request, tag: id }, status: SeoRunStatuses.queued, reservedMicros: amount, catalogRevision: task.catalogRevision, results: emptySeoResults(), retryOf: task.id }).run();
|
|
9608
|
+
}
|
|
9539
9609
|
cancel(jobId) {
|
|
9540
9610
|
return this.db.transaction((tx) => {
|
|
9541
9611
|
const job = tx.select().from(seoJobs).where(eq3(seoJobs.id, jobId)).get();
|
|
@@ -9581,7 +9651,7 @@ var SeoRepository = class {
|
|
|
9581
9651
|
}
|
|
9582
9652
|
finish(jobId) {
|
|
9583
9653
|
const job = this.db.select().from(seoJobs).where(eq3(seoJobs.id, jobId)).get();
|
|
9584
|
-
const tasks = this.db.select().from(seoTasks).where(eq3(seoTasks.jobId, jobId)).all();
|
|
9654
|
+
const tasks = currentSeoTasks(this.db.select().from(seoTasks).where(eq3(seoTasks.jobId, jobId)).all());
|
|
9585
9655
|
const pending = tasks.some((task) => task.status === SeoRunStatuses.queued || task.status === SeoRunStatuses.running);
|
|
9586
9656
|
const unknown = tasks.some((task) => task.status === SeoRunStatuses["reconciliation-required"]);
|
|
9587
9657
|
const completed = tasks.some((task) => task.status === SeoRunStatuses.completed || task.status === SeoRunStatuses.partial);
|
|
@@ -9758,5 +9828,7 @@ export {
|
|
|
9758
9828
|
SentimentIdempotencyConflict,
|
|
9759
9829
|
recordSentimentCompletion,
|
|
9760
9830
|
SentimentRepository,
|
|
9831
|
+
currentSeoTasks,
|
|
9832
|
+
currentSeoTaskCondition,
|
|
9761
9833
|
SeoRepository
|
|
9762
9834
|
};
|