@canonry/canonry 6.3.0 → 6.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -26,7 +26,7 @@ import {
26
26
  persistSiteCrawlGraphLayout,
27
27
  prepareSiteCrawlGraphLayout,
28
28
  recordedSharePct
29
- } from "./chunk-FSRVG2H7.js";
29
+ } from "./chunk-5FYTFE5T.js";
30
30
  import {
31
31
  CanonryMcpToolNames,
32
32
  CliError,
@@ -214,7 +214,7 @@ import {
214
214
  siteLivenessState,
215
215
  toAlertView,
216
216
  usageCounters
217
- } from "./chunk-QVGG76UW.js";
217
+ } from "./chunk-7CVTTQRV.js";
218
218
  import {
219
219
  AGENT_MEMORY_VALUE_MAX_BYTES,
220
220
  AGENT_NONE,
@@ -3941,14 +3941,17 @@ var SentimentWorker = class {
3941
3941
  this.reconcile();
3942
3942
  const gate = this.repository.dispatchGate({ now: this.now(), credentialFingerprint: credentialFingerprint(config.apiKey) });
3943
3943
  if (gate === "closed") return 0;
3944
+ const used = this.repository.recentDispatch(this.now());
3945
+ if (used.tokens >= config.maxInputTokensPerMinute) return 0;
3946
+ const allowed = Math.min(gate === "probe" ? 1 : config.maxConcurrency, config.maxRequestsPerMinute - used.requests);
3944
3947
  const claims = [];
3945
- for (let index = 0; index < (gate === "probe" ? 1 : config.maxConcurrency); index++) {
3948
+ for (let index = 0; index < allowed; index++) {
3946
3949
  const claim = this.repository.claim({ owner: randomUUID(), now: this.now(), leaseMs: 12e4, maxConcurrent: config.maxConcurrency });
3947
3950
  if (!claim) break;
3948
3951
  claims.push(claim);
3949
3952
  }
3950
- await Promise.all(claims.map((work) => this.execute(work)));
3951
- return claims.length;
3953
+ const outcomes = await Promise.all(claims.map((work) => this.execute(work)));
3954
+ return outcomes.filter((outcome) => outcome !== "released").length;
3952
3955
  }
3953
3956
  async execute(work) {
3954
3957
  const owner = work.leaseOwner;
@@ -3990,6 +3993,11 @@ var SentimentWorker = class {
3990
3993
  }
3991
3994
  const attempt = this.repository.startAttempt({ workItemId: work.id, owner, requestedModel: input.definition.requestedModel, now: this.now(), estimatedInputTokens: estimate, maxRequestsPerMinute: config.maxRequestsPerMinute, maxAttempts: config.maxAttempts, maxInputTokensPerMinute: config.maxInputTokensPerMinute });
3992
3995
  if (!attempt) {
3996
+ const used = this.repository.recentDispatch(this.now());
3997
+ if (used.requests >= config.maxRequestsPerMinute || used.tokens + estimate > config.maxInputTokensPerMinute) {
3998
+ this.repository.releaseWork({ workItemId: work.id, owner, now: this.now() });
3999
+ return "released";
4000
+ }
3993
4001
  this.repository.failWork({ workItemId: work.id, owner, now: this.now(), errorCode: "RATE_LIMIT_WAIT", retryAt: new Date(Date.parse(this.now()) + 6e4).toISOString() });
3994
4002
  return;
3995
4003
  }
@@ -4030,6 +4038,28 @@ var SentimentWorker = class {
4030
4038
  }
4031
4039
  }
4032
4040
  };
4041
+ function createSentimentPoller(tick, onError, schedule = setImmediate) {
4042
+ let inFlight2 = null;
4043
+ let stopped = false;
4044
+ const poll = () => {
4045
+ if (inFlight2 || stopped) return;
4046
+ inFlight2 = tick().catch(() => {
4047
+ onError();
4048
+ return 0;
4049
+ }).then((claimed) => {
4050
+ inFlight2 = null;
4051
+ if (claimed > 0 && !stopped) schedule(poll);
4052
+ });
4053
+ };
4054
+ return {
4055
+ poll,
4056
+ /** The tick in flight, if any. */
4057
+ settled: () => inFlight2,
4058
+ stop: () => {
4059
+ stopped = true;
4060
+ }
4061
+ };
4062
+ }
4033
4063
 
4034
4064
  // src/sentiment-config.ts
4035
4065
  import fs2 from "fs";
@@ -7952,7 +7982,7 @@ function nonBlank(value) {
7952
7982
  return trimmed ? trimmed : void 0;
7953
7983
  }
7954
7984
  function resolveBuildCommit(env = process.env) {
7955
- const embedded = true ? "a0351a3c7ac5eb8e0e4028ee004ddd5a12874575" : void 0;
7985
+ const embedded = true ? "a9545eefb45af8bb1a00366b6e75ce25bb3f4c2b" : void 0;
7956
7986
  return nonBlank(embedded) ?? nonBlank(env.CANONRY_COMMIT);
7957
7987
  }
7958
7988
  function resolveInstanceIdentity(env = process.env) {
@@ -14765,7 +14795,7 @@ function readStoredGroundingSources(rawResponse) {
14765
14795
  return result2;
14766
14796
  }
14767
14797
  async function backfillInsightsCommand(project, opts) {
14768
- const { IntelligenceService: IntelligenceService2 } = await import("./intelligence-service-5J36RBEM.js");
14798
+ const { IntelligenceService: IntelligenceService2 } = await import("./intelligence-service-SYISPW25.js");
14769
14799
  const config = loadConfig();
14770
14800
  const db = createClient(config.database);
14771
14801
  migrate(db);
@@ -23277,16 +23307,13 @@ async function createServer(opts) {
23277
23307
  return built.ok ? { ok: true, estimatedInputTokens: built.estimate.inputTokens } : built;
23278
23308
  }
23279
23309
  });
23280
- let sentimentTick = null;
23281
- const pollSentiment = () => {
23282
- if (sentimentTick) return;
23283
- sentimentTick = sentimentWorker.tick().catch(() => {
23310
+ const sentimentPoller = createSentimentPoller(
23311
+ () => sentimentWorker.tick(),
23312
+ () => {
23284
23313
  log24.error("sentiment.worker-failed", { reason: "Stored sentiment worker could not finish its tick." });
23285
- return 0;
23286
- }).finally(() => {
23287
- sentimentTick = null;
23288
- });
23289
- };
23314
+ }
23315
+ );
23316
+ const pollSentiment = sentimentPoller.poll;
23290
23317
  let sentimentTimer;
23291
23318
  runCoordinator.onSentimentCompleted = async () => {
23292
23319
  pollSentiment();
@@ -23298,7 +23325,8 @@ async function createServer(opts) {
23298
23325
  });
23299
23326
  app.addHook("onClose", async () => {
23300
23327
  clearInterval(sentimentTimer);
23301
- await sentimentTick;
23328
+ sentimentPoller.stop();
23329
+ await sentimentPoller.settled();
23302
23330
  });
23303
23331
  const providerModelCatalog = createProviderModelCatalog(registry);
23304
23332
  await app.register(apiRoutes, {
@@ -5,7 +5,7 @@ import {
5
5
  siteCrawlGraphNodes,
6
6
  siteCrawlPages,
7
7
  siteCrawlSnapshots
8
- } from "./chunk-QVGG76UW.js";
8
+ } from "./chunk-7CVTTQRV.js";
9
9
  import {
10
10
  SITE_CRAWL_GRAPH_MAX_EDGES,
11
11
  SITE_CRAWL_GRAPH_MAX_NODES,
@@ -9003,6 +9003,10 @@ function cancelProject(db, projectId, now, reason) {
9003
9003
  )).run();
9004
9004
  recountJobs(db, affected, now);
9005
9005
  }
9006
+ function dispatchedInLastMinute(db, now) {
9007
+ const since = new Date(Date.parse(now) - 6e4).toISOString();
9008
+ return db.select({ requests: sql4`count(*)`, tokens: sql4`coalesce(sum(${sentimentAttempts.estimatedInputTokens}), 0)` }).from(sentimentAttempts).where(gt(sentimentAttempts.dispatchedAt, since)).get();
9009
+ }
9006
9010
  var SentimentRepository = class {
9007
9011
  constructor(db) {
9008
9012
  this.db = db;
@@ -9211,6 +9215,10 @@ var SentimentRepository = class {
9211
9215
  return claimed;
9212
9216
  }, { behavior: "immediate" });
9213
9217
  }
9218
+ /** Requests and estimated input tokens dispatched in the minute before `now`: the window `startAttempt` enforces. */
9219
+ recentDispatch(now) {
9220
+ return dispatchedInLastMinute(this.db, now);
9221
+ }
9214
9222
  startAttempt(input) {
9215
9223
  return this.db.transaction((tx) => {
9216
9224
  const work = tx.select().from(sentimentWorkItems).where(and(
@@ -9225,8 +9233,7 @@ var SentimentRepository = class {
9225
9233
  if (input.maxAttempts !== void 0 && work.attemptCount - work.attemptBudgetStart >= input.maxAttempts) return void 0;
9226
9234
  const estimate = input.estimatedInputTokens ?? 0;
9227
9235
  if (!Number.isSafeInteger(estimate) || estimate < 0) throw new Error("Invalid sentiment token estimate");
9228
- const since = new Date(Date.parse(input.now) - 6e4).toISOString();
9229
- const used = tx.select({ requests: sql4`count(*)`, tokens: sql4`coalesce(sum(${sentimentAttempts.estimatedInputTokens}), 0)` }).from(sentimentAttempts).where(gt(sentimentAttempts.dispatchedAt, since)).get();
9236
+ const used = dispatchedInLastMinute(tx, input.now);
9230
9237
  if (input.maxRequestsPerMinute !== void 0 && used.requests >= input.maxRequestsPerMinute) return void 0;
9231
9238
  if (input.maxInputTokensPerMinute !== void 0 && used.tokens + estimate > input.maxInputTokensPerMinute) return void 0;
9232
9239
  const attemptNumber = work.attemptCount + 1;
@@ -9301,6 +9308,14 @@ var SentimentRepository = class {
9301
9308
  return true;
9302
9309
  }, { behavior: "immediate" });
9303
9310
  }
9311
+ /** Hand claimed work back to the queue untouched: no error, no retry time, no attempt counted. */
9312
+ releaseWork(input) {
9313
+ return this.db.transaction((tx) => {
9314
+ const changed = tx.update(sentimentWorkItems).set({ status: "pending", leaseOwner: null, leaseExpiresAt: null, updatedAt: input.now }).where(and(eq2(sentimentWorkItems.id, input.workItemId), eq2(sentimentWorkItems.leaseOwner, input.owner), eq2(sentimentWorkItems.status, "running"))).returning().all();
9315
+ if (changed.length > 0) shiftJobs(tx, changed[0].id, "running", "pending", input.now);
9316
+ return changed.length > 0;
9317
+ }, { behavior: "immediate" });
9318
+ }
9304
9319
  failWork(input) {
9305
9320
  return this.db.transaction((tx) => {
9306
9321
  const changed = tx.update(sentimentWorkItems).set({
package/dist/cli.js CHANGED
@@ -35,7 +35,7 @@ import {
35
35
  trackCliCommandFinished,
36
36
  trackEvent,
37
37
  waitForServerRuntimeStartup
38
- } from "./chunk-OTROA7CN.js";
38
+ } from "./chunk-4WZUR7XX.js";
39
39
  import {
40
40
  autoSyncSkills,
41
41
  formatAutoSyncNotice
@@ -62,7 +62,7 @@ import {
62
62
  trustedProxiesProblem,
63
63
  trustedProxyProblem
64
64
  } from "./chunk-RB2F4D3Z.js";
65
- import "./chunk-FSRVG2H7.js";
65
+ import "./chunk-5FYTFE5T.js";
66
66
  import {
67
67
  CliError,
68
68
  EXIT_SYSTEM_ERROR,
@@ -90,7 +90,7 @@ import {
90
90
  queries,
91
91
  renderReportHtml,
92
92
  runs
93
- } from "./chunk-QVGG76UW.js";
93
+ } from "./chunk-7CVTTQRV.js";
94
94
  import {
95
95
  AdsDeliverySnapshotStatuses,
96
96
  AdsHistoricalCampaignRollupStatuses,
@@ -734,7 +734,7 @@ function parseDemoTrustedProxies(values) {
734
734
  async function demoCommand(options) {
735
735
  const { host, port } = parseDemoListenOptions(options);
736
736
  const trustProxy = parseDemoTrustedProxies(options.trustProxy);
737
- const { createDemoServer } = await import("./demo-server-5MNLI6GC.js");
737
+ const { createDemoServer } = await import("./demo-server-AIFR77TP.js");
738
738
  const app = await createDemoServer({ trustProxy });
739
739
  let stopping = false;
740
740
  const stop = () => {
@@ -5,7 +5,7 @@ import {
5
5
  computeFactorAverages,
6
6
  layoutSiteCrawlGraphInput,
7
7
  persistSiteCrawlGraphLayout
8
- } from "./chunk-FSRVG2H7.js";
8
+ } from "./chunk-5FYTFE5T.js";
9
9
  import {
10
10
  PACKAGE_VERSION,
11
11
  canonryMcpTools
@@ -85,7 +85,7 @@ import {
85
85
  siteCrawlPages,
86
86
  siteCrawlSnapshots,
87
87
  trafficSources
88
- } from "./chunk-QVGG76UW.js";
88
+ } from "./chunk-7CVTTQRV.js";
89
89
  import {
90
90
  RunKinds,
91
91
  SiteCrawlFetchStates,
package/dist/index.js CHANGED
@@ -3,13 +3,13 @@ import {
3
3
  createGoogleMarketingCredentialStore,
4
4
  createGoogleMarketingRuntime,
5
5
  createServer
6
- } from "./chunk-OTROA7CN.js";
6
+ } from "./chunk-4WZUR7XX.js";
7
7
  import {
8
8
  loadConfig
9
9
  } from "./chunk-ZZEPF3A5.js";
10
- import "./chunk-FSRVG2H7.js";
10
+ import "./chunk-5FYTFE5T.js";
11
11
  import "./chunk-DERWXLHX.js";
12
- import "./chunk-QVGG76UW.js";
12
+ import "./chunk-7CVTTQRV.js";
13
13
  import "./chunk-EV5G5QRR.js";
14
14
  export {
15
15
  GoogleMarketingRuntimeError,
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  HISTORY_WINDOW_RUNS,
3
3
  IntelligenceService
4
- } from "./chunk-QVGG76UW.js";
4
+ } from "./chunk-7CVTTQRV.js";
5
5
  import "./chunk-EV5G5QRR.js";
6
6
  export {
7
7
  HISTORY_WINDOW_RUNS,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@canonry/canonry",
3
- "version": "6.3.0",
3
+ "version": "6.3.1",
4
4
  "type": "module",
5
5
  "description": "Self-hosted AI visibility (AEO) platform: track how ChatGPT, Claude, Gemini, and Perplexity cite your domain, join it with Search Console, GA4, server-side traffic, and paid media, and fix what you find through agent tools (CLI, REST, MCP). Local SQLite.",
6
6
  "keywords": [
@@ -87,32 +87,32 @@
87
87
  "tsx": "^4.19.0",
88
88
  "@ainyc/canonry-web": "0.0.0",
89
89
  "@ainyc/canonry-api-routes": "0.0.0",
90
- "@ainyc/canonry-contracts": "0.0.0",
91
- "@ainyc/canonry-config": "0.0.0",
92
90
  "@ainyc/canonry-api-client": "0.0.0",
91
+ "@ainyc/canonry-config": "0.0.0",
92
+ "@ainyc/canonry-contracts": "0.0.0",
93
93
  "@ainyc/canonry-db": "0.0.0",
94
- "@ainyc/canonry-integration-bing": "0.0.0",
95
- "@ainyc/canonry-integration-cloudflare-queue": "0.0.0",
96
94
  "@ainyc/canonry-integration-cloud-run": "0.0.0",
95
+ "@ainyc/canonry-integration-cloudflare-queue": "0.0.0",
96
+ "@ainyc/canonry-integration-bing": "0.0.0",
97
97
  "@ainyc/canonry-integration-cloudflare-worker": "0.0.0",
98
- "@ainyc/canonry-integration-commoncrawl": "0.0.0",
98
+ "@ainyc/canonry-integration-google-business-profile": "0.0.0",
99
99
  "@ainyc/canonry-integration-google": "0.0.0",
100
100
  "@ainyc/canonry-integration-google-ads": "0.0.0",
101
- "@ainyc/canonry-integration-google-business-profile": "0.0.0",
102
- "@ainyc/canonry-integration-google-places": "0.0.0",
101
+ "@ainyc/canonry-integration-commoncrawl": "0.0.0",
103
102
  "@ainyc/canonry-integration-google-tag-manager": "0.0.0",
103
+ "@ainyc/canonry-integration-google-places": "0.0.0",
104
+ "@ainyc/canonry-integration-openai-ads": "0.0.0",
104
105
  "@ainyc/canonry-integration-traffic": "0.0.0",
105
106
  "@ainyc/canonry-intelligence": "0.0.0",
106
- "@ainyc/canonry-integration-openai-ads": "0.0.0",
107
+ "@ainyc/canonry-provider-cdp": "0.0.0",
107
108
  "@ainyc/canonry-integration-wordpress": "0.0.0",
108
- "@ainyc/canonry-provider-claude": "0.0.0",
109
109
  "@ainyc/canonry-provider-gemini": "0.0.0",
110
+ "@ainyc/canonry-provider-claude": "0.0.0",
110
111
  "@ainyc/canonry-provider-local": "0.0.0",
111
- "@ainyc/canonry-provider-muse": "0.0.0",
112
- "@ainyc/canonry-provider-openai": "0.0.0",
113
112
  "@ainyc/canonry-provider-perplexity": "0.0.0",
114
113
  "@ainyc/canonry-integration-typesafe": "0.0.0",
115
- "@ainyc/canonry-provider-cdp": "0.0.0"
114
+ "@ainyc/canonry-provider-muse": "0.0.0",
115
+ "@ainyc/canonry-provider-openai": "0.0.0"
116
116
  },
117
117
  "scripts": {
118
118
  "build": "pnpm run build:cli && pnpm run build:web",