@canonry/canonry 7.32.0 → 7.44.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.
Files changed (43) hide show
  1. package/assets/agent-workspace/skills/aero/references/agent-operations.md +9 -0
  2. package/assets/agent-workspace/skills/aero/references/site-health.md +12 -0
  3. package/assets/agent-workspace/skills/canonry/SKILL.md +9 -0
  4. package/assets/agent-workspace/skills/canonry/references/aeo-analysis.md +24 -0
  5. package/assets/agent-workspace/skills/canonry/references/canonry-cli.md +207 -4
  6. package/assets/agent-workspace/skills/canonry/references/server-side-traffic.md +14 -2
  7. package/assets/assets/{AuditHistoryPanel-cQyVA85P.js → AuditHistoryPanel-CvANw3ds.js} +1 -1
  8. package/assets/assets/{BacklinksPage-UGZ_9N7F.js → BacklinksPage-C6x9ELjk.js} +1 -1
  9. package/assets/assets/{HistoryPage-UJVFZ5LK.js → HistoryPage-r36HX1cC.js} +1 -1
  10. package/assets/assets/{MeasurementPropertyPage-ChpOxQfx.js → MeasurementPropertyPage-ComoCzR4.js} +3 -3
  11. package/assets/assets/ProjectPage-CIeU-buk.js +8 -0
  12. package/assets/assets/RunsPage-DI6eJATZ.js +1 -0
  13. package/assets/assets/{SettingsPage-DeKxjuTr.js → SettingsPage-Bw7GdXxs.js} +1 -1
  14. package/assets/assets/{SiteHealthSection-COeVsgop.js → SiteHealthSection-C-JaNKpS.js} +3 -3
  15. package/assets/assets/{TrafficPage-DmMmeJKK.js → TrafficPage-Cnsp276v.js} +1 -1
  16. package/assets/assets/{TrafficSourceDetailPage-CUehseZe.js → TrafficSourceDetailPage-CBIaJk7e.js} +1 -1
  17. package/assets/assets/{extract-error-message-BsxdS7Po.js → extract-error-message-C_Owa0n9.js} +1 -1
  18. package/assets/assets/index-ZS9sSTMS.js +86 -0
  19. package/assets/assets/{react-sigma_core.esm.min--MbbBWzJ.js → react-sigma_core.esm.min-ugyh-ooq.js} +1 -1
  20. package/assets/assets/{v2-overview-adapter-D-IScpdp.js → v2-overview-adapter-BLSrVQ7K.js} +1 -1
  21. package/assets/index.html +1 -1
  22. package/dist/chunk-3JVF3ULD.js +550 -0
  23. package/dist/chunk-45Z5RJZW.js +562 -0
  24. package/dist/{chunk-QFXD7GR5.js → chunk-5LY7EG4U.js} +238 -20
  25. package/dist/chunk-EJRV7RBZ.js +23066 -0
  26. package/dist/{chunk-WRHA54BN.js → chunk-G47XJVAS.js} +5885 -3900
  27. package/dist/{chunk-42XUY243.js → chunk-GZ2KIONF.js} +11507 -10391
  28. package/dist/chunk-HGMYEDAQ.js +79 -0
  29. package/dist/{chunk-2C6NWFN4.js → chunk-JVBI6VBT.js} +2 -2
  30. package/dist/{chunk-ZIETXJYU.js → chunk-MPWBFKGK.js} +201 -104
  31. package/dist/{chunk-K5EGSSXC.js → chunk-QQYQDTFZ.js} +142 -47
  32. package/dist/{chunk-FW4GX2LO.js → chunk-SW54HXSZ.js} +3 -3
  33. package/dist/cli-catalog-PNJEQV5H.js +15 -0
  34. package/dist/cli-commands-4UT2CRXX.js +15 -0
  35. package/dist/cli.js +136 -22525
  36. package/dist/{demo-server-G5OK7SWE.js → demo-server-WRHR25HF.js} +5 -4
  37. package/dist/index.js +7 -6
  38. package/dist/{intelligence-service-3ZUVTIO7.js → intelligence-service-QXXRUB2G.js} +2 -2
  39. package/dist/mcp.js +30 -15
  40. package/package.json +10 -10
  41. package/assets/assets/ProjectPage-CvxuPSiC.js +0 -8
  42. package/assets/assets/RunsPage-C6U_YcDY.js +0 -1
  43. package/assets/assets/index-ahEr6Oc-.js +0 -86
@@ -21,20 +21,22 @@ import {
21
21
  saveConfig,
22
22
  saveConfigPatch,
23
23
  sentimentInstallReadiness
24
- } from "./chunk-QFXD7GR5.js";
24
+ } from "./chunk-5LY7EG4U.js";
25
25
  import {
26
26
  computeFactorAverages,
27
27
  deleteSiteCrawlGraphLayout,
28
28
  persistSiteCrawlGraphLayout,
29
29
  prepareSiteCrawlGraphLayout,
30
30
  recordedSharePct
31
- } from "./chunk-2C6NWFN4.js";
31
+ } from "./chunk-JVBI6VBT.js";
32
32
  import {
33
33
  CanonryMcpToolNames,
34
- CliError,
35
34
  PACKAGE_VERSION,
36
35
  canonryMcpTools
37
- } from "./chunk-ZIETXJYU.js";
36
+ } from "./chunk-MPWBFKGK.js";
37
+ import {
38
+ CliError
39
+ } from "./chunk-HGMYEDAQ.js";
38
40
  import {
39
41
  BingApiError,
40
42
  CC_CACHE_DIR,
@@ -230,6 +232,7 @@ import {
230
232
  siteCrawlRunRequests,
231
233
  siteCrawlSnapshots,
232
234
  siteLivenessState,
235
+ syncRunClaimKey,
233
236
  toAlertView,
234
237
  trafficSources,
235
238
  usageCounters,
@@ -237,7 +240,7 @@ import {
237
240
  webhookResponseReason,
238
241
  webhookTargetRefusalReason,
239
242
  writeAuditLog
240
- } from "./chunk-WRHA54BN.js";
243
+ } from "./chunk-G47XJVAS.js";
241
244
  import {
242
245
  AGENT_MEMORY_VALUE_MAX_BYTES,
243
246
  AGENT_NONE,
@@ -281,7 +284,9 @@ import {
281
284
  MeasurementPortfolioLists,
282
285
  MemorySources,
283
286
  ONBOARDING_FLOW_VERSION,
287
+ OPERATION_ACCESS_HEADER,
284
288
  OUTSTANDING_PROVIDER_BATCH_STATUSES,
289
+ OperationAccesses,
285
290
  OutcomeReasonCodes,
286
291
  OutcomeStatuses,
287
292
  OutcomeSurfaces,
@@ -307,6 +312,7 @@ import {
307
312
  SITE_AUDIT_DEFAULT_PAGE_LIMIT,
308
313
  SITE_AUDIT_MAX_EDGE_LIMIT,
309
314
  SITE_AUDIT_MAX_PAGE_LIMIT,
315
+ STORED_ONLY_HEADER,
310
316
  SchedulableRunKinds,
311
317
  SentimentOutcomes,
312
318
  SiteHealthLinkClassificationSources,
@@ -462,6 +468,7 @@ import {
462
468
  splitList,
463
469
  startOfDayHourInTimeZone,
464
470
  statusClassOf,
471
+ storedOnlyRefused,
465
472
  storedSentimentClassifierInputSchema,
466
473
  summarizeRunUsage,
467
474
  templateLinkDetection,
@@ -480,7 +487,7 @@ import {
480
487
  winnabilityClassLabel,
481
488
  withRetry,
482
489
  withSkippedProviders
483
- } from "./chunk-42XUY243.js";
490
+ } from "./chunk-GZ2KIONF.js";
484
491
 
485
492
  // src/runtime-context.ts
486
493
  function cliRuntimeContext(env = process.env, stdio = process) {
@@ -659,6 +666,7 @@ function startBackgroundRefresh(getNow) {
659
666
  })();
660
667
  }
661
668
  function checkLatestVersionForServer(opts) {
669
+ if (opts?.storedOnly) return memoryCache?.latest ? buildUpdateAvailable(PKG_VERSION, memoryCache.latest) : null;
662
670
  if (!isUpdateCheckEnabled()) return null;
663
671
  const getNow = opts?.now ?? Date.now;
664
672
  const ttl = opts?.ttlMs ?? 60 * 60 * 1e3;
@@ -1863,14 +1871,19 @@ async function fetchOnce(url, accessToken, options) {
1863
1871
  const controller = new AbortController();
1864
1872
  const timeout = setTimeout(() => controller.abort(), options.timeoutMs ?? GTM_REQUEST_TIMEOUT_MS);
1865
1873
  try {
1866
- const response = await fetchImpl(url, {
1867
- method: "GET",
1868
- headers: {
1869
- authorization: `Bearer ${accessToken}`,
1870
- accept: "application/json"
1871
- },
1872
- signal: controller.signal
1873
- });
1874
+ let response;
1875
+ try {
1876
+ response = await fetchImpl(url, {
1877
+ method: "GET",
1878
+ headers: {
1879
+ authorization: `Bearer ${accessToken}`,
1880
+ accept: "application/json"
1881
+ },
1882
+ signal: controller.signal
1883
+ });
1884
+ } catch (error) {
1885
+ throw new Error(describeFetchError(error, url), { cause: error });
1886
+ }
1874
1887
  const text = await response.text();
1875
1888
  let body = void 0;
1876
1889
  let invalidJson = false;
@@ -8344,7 +8357,7 @@ function nonBlank(value) {
8344
8357
  return trimmed ? trimmed : void 0;
8345
8358
  }
8346
8359
  function resolveBuildCommit(env = process.env) {
8347
- const embedded = true ? "8df46cfa2e6d818351a792096390ac2a8bd59dbe" : void 0;
8360
+ const embedded = true ? "89a7b4bb64371e151e3acebd92b4250075933dce" : void 0;
8348
8361
  return nonBlank(embedded) ?? nonBlank(env.CANONRY_COMMIT);
8349
8362
  }
8350
8363
  function resolveInstanceIdentity(env = process.env) {
@@ -12110,15 +12123,20 @@ function buildUrl(path11, queryPairs) {
12110
12123
  }
12111
12124
  async function adsFetch(apiKey, path11, queryPairs = [], method = "GET", body) {
12112
12125
  const url = buildUrl(path11, queryPairs);
12113
- const res = await fetch(url, {
12114
- method,
12115
- headers: {
12116
- Authorization: `Bearer ${apiKey}`,
12117
- "Content-Type": "application/json"
12118
- },
12119
- body: body === void 0 ? void 0 : JSON.stringify(body),
12120
- signal: AbortSignal.timeout(OPENAI_ADS_REQUEST_TIMEOUT_MS)
12121
- });
12126
+ let res;
12127
+ try {
12128
+ res = await fetch(url, {
12129
+ method,
12130
+ headers: {
12131
+ Authorization: `Bearer ${apiKey}`,
12132
+ "Content-Type": "application/json"
12133
+ },
12134
+ body: body === void 0 ? void 0 : JSON.stringify(body),
12135
+ signal: AbortSignal.timeout(OPENAI_ADS_REQUEST_TIMEOUT_MS)
12136
+ });
12137
+ } catch (error) {
12138
+ throw new Error(describeFetchError(error, url), { cause: error });
12139
+ }
12122
12140
  if (res.status === 401 || res.status === 403) {
12123
12141
  const { code } = parseErrorEnvelope(await res.text());
12124
12142
  adsClientLog("error", "http.auth-failed", { path: path11, httpStatus: res.status, code });
@@ -14403,6 +14421,31 @@ function classifySiteCrawlTemplateLinks(db, scope, pagesFetched, placementRulese
14403
14421
  };
14404
14422
  }
14405
14423
 
14424
+ // src/site-audit-issues.ts
14425
+ import { createHash as createHash4 } from "crypto";
14426
+ var GROUP_IDENTITY_VERSION = "duplicate-metadata-v1";
14427
+ function buildSiteCrawlIssueEvidence(siteIssues, pages) {
14428
+ if (siteIssues === void 0) return null;
14429
+ const metadataByUrl = /* @__PURE__ */ new Map();
14430
+ if (siteIssues.length > 0) {
14431
+ for (const page of pages) {
14432
+ if (page.state === "html" && page.indexability.state === "indexable" && page.finalUrl && page.audit) {
14433
+ metadataByUrl.set(page.finalUrl, page.audit.metadata);
14434
+ }
14435
+ }
14436
+ }
14437
+ const groups = siteIssues.map((issue) => {
14438
+ const affectedUrls = [...new Set(issue.affectedUrls)].sort();
14439
+ const field = issue.code === "site.duplicate-title" ? "pageTitle" : issue.code === "site.duplicate-meta-description" ? "metaDescription" : null;
14440
+ const values = field ? affectedUrls.map((url) => metadataByUrl.get(url)?.[field]?.trim()) : [];
14441
+ const value = values[0];
14442
+ const key = value && values.every((candidate) => candidate === value) ? `site-issue:${createHash4("sha256").update(JSON.stringify([GROUP_IDENTITY_VERSION, issue.code, value])).digest("hex")}` : null;
14443
+ return { key, code: issue.code, message: issue.message, affectedUrls };
14444
+ });
14445
+ groups.sort((left, right) => left.code.localeCompare(right.code) || (left.key ?? "").localeCompare(right.key ?? "") || JSON.stringify(left.affectedUrls).localeCompare(JSON.stringify(right.affectedUrls)));
14446
+ return { schemaVersion: "1.0", groupIdentityVersion: GROUP_IDENTITY_VERSION, siteIssues: groups };
14447
+ }
14448
+
14406
14449
  // src/execute-site-audit.ts
14407
14450
  var log14 = createLogger("SiteAudit");
14408
14451
  var SiteAuditCancelledError = class extends Error {
@@ -14443,7 +14486,8 @@ function toCrawlAuditFactor(factor) {
14443
14486
  status: factorStatusFromScore(factor.score),
14444
14487
  applicable: factor.applicable ?? null,
14445
14488
  findings: factor.findings,
14446
- recommendations: factor.recommendations
14489
+ recommendations: factor.recommendations,
14490
+ ...factor.advisoryRecommendations === void 0 ? {} : { advisoryRecommendations: factor.advisoryRecommendations }
14447
14491
  };
14448
14492
  }
14449
14493
  function crawlAuditFields(audit) {
@@ -14911,6 +14955,7 @@ async function executeSiteAudit(db, runId, projectId, opts = {}) {
14911
14955
  const deadLinksFound = deadLinkFindings.length;
14912
14956
  const deadLinksUnverified = new Set(unverifiedLinks.map((finding) => finding.to)).size;
14913
14957
  const legacyIssues = factors.map((factor) => toLegacyIssue(factor, crawlSummary.auditRollup.auditedPages)).filter((issue) => issue !== null);
14958
+ const issueEvidence = buildSiteCrawlIssueEvidence(crawlSummary.siteIssues, observedPages.values());
14914
14959
  try {
14915
14960
  persistSiteCrawlGraphLayout(db, { projectId, runId, attemptId }, graphLayout, finishedAt);
14916
14961
  } catch (error) {
@@ -15009,6 +15054,7 @@ async function executeSiteAudit(db, runId, projectId, opts = {}) {
15009
15054
  pagesErrored: errorCount,
15010
15055
  edgesDiscovered: crawlSummary.edgesObserved,
15011
15056
  findingsCount: deadLinksFound,
15057
+ issueEvidence,
15012
15058
  deadLinkState: report.deadLinks.state,
15013
15059
  deadLinksChecked,
15014
15060
  deadLinksFound,
@@ -15682,7 +15728,7 @@ import { Type as Type3 } from "@earendil-works/pi-ai";
15682
15728
 
15683
15729
  // src/agent/mcp-to-agent-tool.ts
15684
15730
  import { Type as Type2 } from "@earendil-works/pi-ai";
15685
- import { createHash as createHash4, randomUUID as randomUUID2 } from "crypto";
15731
+ import { createHash as createHash5, randomUUID as randomUUID2 } from "crypto";
15686
15732
 
15687
15733
  // src/agent/tool-result-units.ts
15688
15734
  var SUCCESS_STATUSES = ["200", "201", "202"];
@@ -16161,7 +16207,7 @@ function hasOtherLists(value, primary) {
16161
16207
  }
16162
16208
  function projectionPath(path11) {
16163
16209
  if (path11.length <= MAX_SUMMARY_PATH_CHARS) return path11;
16164
- const digest = createHash4("sha256").update(path11).digest("hex").slice(0, 12);
16210
+ const digest = createHash5("sha256").update(path11).digest("hex").slice(0, 12);
16165
16211
  return `${truncateUtf16(path11, 48)}...#${digest}${path11.slice(-24)}`;
16166
16212
  }
16167
16213
  function boundedProjection(full, partialRows, reserve = 0) {
@@ -16455,6 +16501,7 @@ function refuseManagedSweepCancel(tool, ctx) {
16455
16501
  var COMPETITOR_AUTO_ALIAS_MODE_REFUSAL = "Aero cannot change a project's competitorAutoAliases mode. Omit competitorAutoAliases to keep the stored mode, or ask the operator to change it.";
16456
16502
  var COMPETITOR_AUTO_ALIAS_MODE_WRITES = /* @__PURE__ */ new Set([
16457
16503
  CanonryMcpToolNames.canonry_project_upsert,
16504
+ CanonryMcpToolNames.canonry_project_update,
16458
16505
  CanonryMcpToolNames.canonry_apply_config
16459
16506
  ]);
16460
16507
  function requestedCompetitorAutoAliasMode(toolName, params, ctx) {
@@ -16463,6 +16510,10 @@ function requestedCompetitorAutoAliasMode(toolName, params, ctx) {
16463
16510
  const request = isRecord8(params.request) ? params.request : {};
16464
16511
  return { project: ctx.projectName, mode: request.competitorAutoAliases };
16465
16512
  }
16513
+ if (toolName === CanonryMcpToolNames.canonry_project_update) {
16514
+ const request = isRecord8(params.request) ? params.request : {};
16515
+ return { project: params.project, mode: request.competitorAutoAliases };
16516
+ }
16466
16517
  const config = isRecord8(params.config) ? params.config : {};
16467
16518
  const metadata2 = isRecord8(config.metadata) ? config.metadata : {};
16468
16519
  const spec = isRecord8(config.spec) ? config.spec : {};
@@ -16490,10 +16541,24 @@ function refuseCompetitorAutoAliasModeChange(tool, ctx) {
16490
16541
  }
16491
16542
  };
16492
16543
  }
16544
+ var COMPETITOR_BULK_ADD_REFUSAL = "Aero cannot acknowledge measured bulk competitor growth. Review the stored evidence and acknowledge this operator action explicitly.";
16545
+ function refuseCompetitorBulkAddAcknowledgement(tool) {
16546
+ const write = tool.execute;
16547
+ return {
16548
+ ...tool,
16549
+ execute: async (toolCallId, params, ...rest) => {
16550
+ const input = typeof params === "object" && params !== null ? params : void 0;
16551
+ const request = typeof input?.request === "object" && input.request !== null ? input.request : void 0;
16552
+ if (request?.acknowledgeBulkAdd === true) throw new Error(COMPETITOR_BULK_ADD_REFUSAL);
16553
+ return write(toolCallId, params, ...rest);
16554
+ }
16555
+ };
16556
+ }
16493
16557
  function buildMcpAgentTools(registry, ctx, opts = {}) {
16494
16558
  return registry.filter((tool) => !AERO_EXCLUDED_MCP_TOOLS.has(tool.name)).filter((tool) => opts.includeNames ? opts.includeNames.has(tool.name) : true).filter((tool) => opts.readOnly ? tool.access === "read" : true).filter((tool) => opts.managedSweeps ? !AERO_MANAGED_SWEEP_MCP_TOOLS.has(tool.name) : true).map((tool) => {
16495
16559
  const agentTool = mcpToAgentTool(tool, ctx);
16496
16560
  if (COMPETITOR_AUTO_ALIAS_MODE_WRITES.has(tool.name)) return refuseCompetitorAutoAliasModeChange(agentTool, ctx);
16561
+ if (tool.name === "canonry_competitors_add") return refuseCompetitorBulkAddAcknowledgement(agentTool);
16497
16562
  return opts.managedSweeps && tool.name === CanonryMcpToolNames.canonry_run_cancel ? refuseManagedSweepCancel(agentTool, ctx) : agentTool;
16498
16563
  });
16499
16564
  }
@@ -17864,7 +17929,7 @@ function assertProviderReloadKeepsPendingBatches(db, previous, next, executing =
17864
17929
  }
17865
17930
 
17866
17931
  // src/provider-model-catalog.ts
17867
- import { createHash as createHash5 } from "crypto";
17932
+ import { createHash as createHash6 } from "crypto";
17868
17933
  var TTL_MS = 60 * 60 * 1e3;
17869
17934
  var RETRY_MS = 60 * 1e3;
17870
17935
  var TIMEOUT_MS3 = 3e3;
@@ -17878,7 +17943,7 @@ function createProviderModelCatalog(registry) {
17878
17943
  }
17879
17944
  const fallback = provider.adapter.modelRegistry.knownModels;
17880
17945
  if (!provider.adapter.listModels) return fallback;
17881
- const identity = createHash5("sha256").update(JSON.stringify(provider.config)).digest("hex");
17946
+ const identity = createHash6("sha256").update(JSON.stringify(provider.config)).digest("hex");
17882
17947
  let entry = cache.get(name);
17883
17948
  if (entry?.identity !== identity) {
17884
17949
  entry = { identity, expiresAt: 0 };
@@ -17917,7 +17982,7 @@ function createProviderModelCatalog(registry) {
17917
17982
  const provider = registry.get(name);
17918
17983
  if (!provider) return [];
17919
17984
  const entry = cache.get(name);
17920
- const identity = createHash5("sha256").update(JSON.stringify(provider.config)).digest("hex");
17985
+ const identity = createHash6("sha256").update(JSON.stringify(provider.config)).digest("hex");
17921
17986
  return (entry?.identity === identity ? entry.models : void 0) ?? provider.adapter.modelRegistry.knownModels;
17922
17987
  };
17923
17988
  return Object.assign(read, { cached: cached2 });
@@ -18753,19 +18818,27 @@ function queuedRun(result2) {
18753
18818
  runStatus: typeof runStatus === "string" ? runStatus : null
18754
18819
  };
18755
18820
  }
18756
- async function refreshAllIntegrations(client, projectName, now = /* @__PURE__ */ new Date()) {
18821
+ async function refreshAllIntegrations(client, projectName, now = /* @__PURE__ */ new Date(), claims) {
18757
18822
  const integrations = [
18758
- { name: "gsc", completion: "queued", run: () => client.gscSync(projectName, {}) },
18759
- { name: "bing", completion: "queued", run: () => client.bingInspectSitemap(projectName, {}) },
18760
- { name: "ga", completion: "synchronous", run: () => client.gaSync(projectName, { days: gaRefreshDays(now) }) },
18761
- { name: "gbp", completion: "queued", run: () => client.triggerGbpSync(projectName, {}) },
18823
+ { name: "gsc", kind: RunKinds["gsc-sync"], completion: "queued", run: () => client.gscSync(projectName, {}) },
18824
+ { name: "bing", kind: RunKinds["bing-inspect-sitemap"], completion: "queued", run: () => client.bingInspectSitemap(projectName, {}) },
18825
+ { name: "ga", kind: RunKinds["ga-sync"], completion: "synchronous", run: () => client.gaSync(projectName, { days: gaRefreshDays(now) }) },
18826
+ { name: "gbp", kind: RunKinds["gbp-sync"], completion: "queued", run: () => client.triggerGbpSync(projectName, {}) },
18762
18827
  // `ads` remains the OpenAI / ChatGPT Ads integration. For scheduled
18763
18828
  // refreshes, Google providers have explicit names and fan out only here.
18764
- { name: "ads", completion: "queued", run: () => client.triggerAdsSync(projectName) },
18765
- { name: "google-ads", completion: "queued", run: () => client.triggerGoogleAdsSync(projectName) },
18766
- { name: "gtm", completion: "queued", run: () => client.triggerGtmSync(projectName) }
18829
+ { name: "ads", kind: RunKinds["ads-sync"], completion: "queued", run: () => client.triggerAdsSync(projectName) },
18830
+ { name: "google-ads", kind: RunKinds["google-ads-sync"], completion: "queued", run: () => client.triggerGoogleAdsSync(projectName) },
18831
+ { name: "gtm", kind: RunKinds["gtm-sync"], completion: "queued", run: () => client.triggerGtmSync(projectName) }
18767
18832
  ];
18768
- const results = await Promise.allSettled(integrations.map((i) => i.run()));
18833
+ const results = await Promise.allSettled(integrations.map(async (integration) => {
18834
+ const key = syncRunClaimKey(projectName, integration.kind);
18835
+ claims?.add(key);
18836
+ try {
18837
+ return await integration.run();
18838
+ } finally {
18839
+ claims?.delete(key);
18840
+ }
18841
+ }));
18769
18842
  results.forEach((result2, idx) => {
18770
18843
  const { name: integration, completion } = integrations[idx];
18771
18844
  if (result2.status === "rejected") {
@@ -23465,6 +23538,8 @@ async function createServer(opts) {
23465
23538
  surface: OutcomeSurfaces.system
23466
23539
  });
23467
23540
  const scheduledBacklinkReleases = /* @__PURE__ */ new Set();
23541
+ const scheduledSyncClaims = /* @__PURE__ */ new Set();
23542
+ const claimScheduledSync = (projectName, kind, sourceId) => scheduledSyncClaims.delete(syncRunClaimKey(projectName, kind, sourceId));
23468
23543
  const agentEnabled = resolveAgentEnabled(process.env, opts.config);
23469
23544
  const agentProactive = resolveAgentProactiveEnabled(process.env, opts.config);
23470
23545
  const sessionRegistry = agentEnabled ? new SessionRegistry({
@@ -23929,7 +24004,10 @@ async function createServer(opts) {
23929
24004
  getEffectiveProviderModels: () => effectiveProviderModels(registry),
23930
24005
  getBatchEligibleProviderNames: () => batchEligibleProviderNames(registry),
23931
24006
  onTrafficSyncRequested: (projectName, sourceId) => {
23932
- schedulerClient.trafficSync(projectName, sourceId).catch((err) => {
24007
+ scheduledSyncClaims.add(syncRunClaimKey(projectName, RunKinds["traffic-sync"], sourceId));
24008
+ schedulerClient.trafficSync(projectName, sourceId).finally(() => {
24009
+ scheduledSyncClaims.delete(syncRunClaimKey(projectName, RunKinds["traffic-sync"], sourceId));
24010
+ }).catch((err) => {
23933
24011
  app.log.error(
23934
24012
  {
23935
24013
  projectName,
@@ -23965,7 +24043,7 @@ async function createServer(opts) {
23965
24043
  })();
23966
24044
  },
23967
24045
  onDataRefreshRequested: (projectName) => {
23968
- void refreshAllIntegrations(schedulerClient, projectName);
24046
+ void refreshAllIntegrations(schedulerClient, projectName, /* @__PURE__ */ new Date(), scheduledSyncClaims);
23969
24047
  },
23970
24048
  onBacklinksSyncRequested: (projectName) => {
23971
24049
  void syncLatestReleaseOnSchedule(projectName, {
@@ -24325,7 +24403,22 @@ async function createServer(opts) {
24325
24403
  const namedAccountsInUse = () => anyUsersExist(opts.db);
24326
24404
  const NAMED_ACCOUNTS_MESSAGE = "This install uses named accounts. Sign in with your name and password.";
24327
24405
  const serverApiKeyMissing = () => new AppError("AUTH_INVALID", "Server API key not found \u2014 run canonry bootstrap", 401);
24328
- app.get(apiPrefix + "/session", async (request, reply) => {
24406
+ const rejectLegacySessionStoredOnly = async (request, reply) => {
24407
+ const header = request.headers[STORED_ONLY_HEADER];
24408
+ if (header !== "1" && header !== "true") return;
24409
+ const route = request.routeOptions.url ?? request.url;
24410
+ const operation = route.endsWith("/session/setup") ? "POST /api/v1/session/setup" : `${request.method.toUpperCase()} /api/v1/session`;
24411
+ const error = storedOnlyRefused("Stored-only mode refuses this operation", { operation, access: OperationAccesses.write });
24412
+ return reply.status(error.statusCode).send(error.toJSON());
24413
+ };
24414
+ const legacySessionRouteOptions = {
24415
+ onSend: async (_request, reply, payload) => {
24416
+ reply.header(OPERATION_ACCESS_HEADER, OperationAccesses.write);
24417
+ return payload;
24418
+ },
24419
+ preHandler: rejectLegacySessionStoredOnly
24420
+ };
24421
+ app.get(apiPrefix + "/session", legacySessionRouteOptions, async (request, reply) => {
24329
24422
  if (namedAccountsInUse()) {
24330
24423
  return reply.send({ authenticated: false, setupRequired: false });
24331
24424
  }
@@ -24338,7 +24431,7 @@ async function createServer(opts) {
24338
24431
  setupRequired: !opts.config.dashboardPasswordHash
24339
24432
  });
24340
24433
  });
24341
- app.post(apiPrefix + "/session/setup", async (request, reply) => {
24434
+ app.post(apiPrefix + "/session/setup", legacySessionRouteOptions, async (request, reply) => {
24342
24435
  if (namedAccountsInUse()) {
24343
24436
  const err = forbidden(NAMED_ACCOUNTS_MESSAGE);
24344
24437
  return reply.status(err.statusCode).send(err.toJSON());
@@ -24393,7 +24486,7 @@ async function createServer(opts) {
24393
24486
  setSessionCookie(reply, sessionId);
24394
24487
  return reply.send({ authenticated: true });
24395
24488
  });
24396
- app.post(apiPrefix + "/session", async (request, reply) => {
24489
+ app.post(apiPrefix + "/session", legacySessionRouteOptions, async (request, reply) => {
24397
24490
  if (namedAccountsInUse()) {
24398
24491
  const err2 = forbidden(NAMED_ACCOUNTS_MESSAGE);
24399
24492
  return reply.status(err2.statusCode).send(err2.toJSON());
@@ -24447,7 +24540,7 @@ async function createServer(opts) {
24447
24540
  const err = validationError("Either password or apiKey is required");
24448
24541
  return reply.status(err.statusCode).send(err.toJSON());
24449
24542
  });
24450
- app.delete(apiPrefix + "/session", async (request, reply) => {
24543
+ app.delete(apiPrefix + "/session", legacySessionRouteOptions, async (request, reply) => {
24451
24544
  const sessionId = parseCookieHeader(request.headers.cookie)[SESSION_COOKIE_NAME];
24452
24545
  clearSession(sessionId);
24453
24546
  reply.header(
@@ -24661,6 +24754,7 @@ async function createServer(opts) {
24661
24754
  // Outcome telemetry reported from route code (`app.emitOutcome`).
24662
24755
  onOutcome: handleRouteOutcome,
24663
24756
  db: opts.db,
24757
+ claimScheduledSync,
24664
24758
  sentiment: {
24665
24759
  install: () => {
24666
24760
  const configuration = loadSentimentInstallConfig();
@@ -25397,8 +25491,9 @@ async function createServer(opts) {
25397
25491
  }
25398
25492
  const buildCommit = resolveBuildCommit();
25399
25493
  const instance = resolveInstanceIdentity();
25400
- const healthHandler = () => {
25401
- const update = checkLatestVersionForServer();
25494
+ const healthHandler = (request) => {
25495
+ const storedOnly = request.headers[STORED_ONLY_HEADER] === "1" || request.headers[STORED_ONLY_HEADER] === "true";
25496
+ const update = checkLatestVersionForServer({ storedOnly });
25402
25497
  return {
25403
25498
  status: "ok",
25404
25499
  service: "canonry",
@@ -4,14 +4,14 @@ import {
4
4
  installSkills,
5
5
  loadConfigRaw,
6
6
  saveConfigPatch
7
- } from "./chunk-QFXD7GR5.js";
7
+ } from "./chunk-5LY7EG4U.js";
8
8
  import {
9
9
  PACKAGE_VERSION
10
- } from "./chunk-ZIETXJYU.js";
10
+ } from "./chunk-MPWBFKGK.js";
11
11
  import {
12
12
  SKILL_MANIFEST_FILENAME,
13
13
  SkillsClients
14
- } from "./chunk-42XUY243.js";
14
+ } from "./chunk-GZ2KIONF.js";
15
15
 
16
16
  // src/skills-autosync.ts
17
17
  import fs from "fs";
@@ -0,0 +1,15 @@
1
+ import {
2
+ CLI_ROOT_SUMMARIES,
3
+ buildCliCatalog,
4
+ printCliCatalog,
5
+ renderTopLevelHelp
6
+ } from "./chunk-45Z5RJZW.js";
7
+ import "./chunk-3JVF3ULD.js";
8
+ import "./chunk-HGMYEDAQ.js";
9
+ import "./chunk-GZ2KIONF.js";
10
+ export {
11
+ CLI_ROOT_SUMMARIES,
12
+ buildCliCatalog,
13
+ printCliCatalog,
14
+ renderTopLevelHelp
15
+ };
@@ -0,0 +1,15 @@
1
+ import {
2
+ REGISTERED_CLI_COMMANDS
3
+ } from "./chunk-EJRV7RBZ.js";
4
+ import "./chunk-QQYQDTFZ.js";
5
+ import "./chunk-5LY7EG4U.js";
6
+ import "./chunk-3JVF3ULD.js";
7
+ import "./chunk-RB2F4D3Z.js";
8
+ import "./chunk-JVBI6VBT.js";
9
+ import "./chunk-MPWBFKGK.js";
10
+ import "./chunk-HGMYEDAQ.js";
11
+ import "./chunk-G47XJVAS.js";
12
+ import "./chunk-GZ2KIONF.js";
13
+ export {
14
+ REGISTERED_CLI_COMMANDS
15
+ };