@ainyc/canonry 5.1.2 → 5.1.4

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.
@@ -1,6 +1,7 @@
1
1
  import {
2
2
  AGENT_MEMORY_KEY_MAX_LENGTH,
3
3
  AGENT_MEMORY_VALUE_MAX_BYTES,
4
+ CANONRY_NPM_PACKAGE_URL,
4
5
  CodingAgents,
5
6
  DEFAULT_VIEWER_RESEARCH_DAILY_RUN_LIMIT,
6
7
  DISCOVERY_MAX_PROBES_CAP,
@@ -28,6 +29,7 @@ import {
28
29
  canonicalizeGtmResourceSelection,
29
30
  classifySkillFile,
30
31
  coerceSkillManifest,
32
+ compareSemver,
31
33
  competitorBatchRequestSchema,
32
34
  competitorLandscapeQuerySchema,
33
35
  describeError,
@@ -40,8 +42,10 @@ import {
40
42
  gaMeasurementHostScopeSchema,
41
43
  googleAdsMetricsWindowSchema,
42
44
  gscPerformanceOrderBySchema,
45
+ isInstallMethod,
43
46
  isReadOnlyKey,
44
47
  isSensitiveDiagnosticQueryKey,
48
+ isStrictSemver,
45
49
  keywordBatchRequestSchema,
46
50
  keywordGenerateRequestSchema,
47
51
  logQuerySchema,
@@ -117,8 +121,10 @@ import {
117
121
  trafficConnectWordpressRequestSchema,
118
122
  trafficEventKindSchema,
119
123
  trafficSeriesGranularitySchema,
124
+ upgradeCaveatFor,
125
+ upgradeCommandFor,
120
126
  visibilityReportRequestSchema
121
- } from "./chunk-5ZIUROAQ.js";
127
+ } from "./chunk-MMSPU72Z.js";
122
128
 
123
129
  // src/cli-error.ts
124
130
  function isMachineFormat(format) {
@@ -6027,6 +6033,16 @@ function createClient2(opts) {
6027
6033
  }
6028
6034
 
6029
6035
  // src/client.ts
6036
+ function parseServerUpdateAvailable(value) {
6037
+ if (!value || typeof value !== "object") return null;
6038
+ const { current, latest, installMethod } = value;
6039
+ if (typeof current !== "string" || current.length > 32 || !isStrictSemver(current)) return null;
6040
+ if (typeof latest !== "string" || latest.length > 32 || !isStrictSemver(latest)) return null;
6041
+ if (compareSemver(latest, current) <= 0) return null;
6042
+ const method = installMethod === void 0 ? "npm" : installMethod;
6043
+ if (!isInstallMethod(method)) return null;
6044
+ return { current, latest, installMethod: method, upgradeCommand: upgradeCommandFor(method), url: CANONRY_NPM_PACKAGE_URL };
6045
+ }
6030
6046
  var usageTagStore = new AsyncLocalStorage();
6031
6047
  function runWithUsageTags(client2, tags, fn) {
6032
6048
  if (!(client2 instanceof ApiClient)) return fn();
@@ -7095,6 +7111,22 @@ var ApiClient = class {
7095
7111
  async getApiKeySelf() {
7096
7112
  return this.invoke(() => getApiV1KeysSelf({ client: this.heyClient }));
7097
7113
  }
7114
+ /**
7115
+ * The connected server's update notice from `/health`, or null. Best-effort
7116
+ * and bounded (2s), never throws: `canonry-mcp` uses it to tell agents that
7117
+ * never run the CLI about a newer release.
7118
+ */
7119
+ async getServerUpdateAvailable() {
7120
+ try {
7121
+ await this.probeBasePath();
7122
+ const res = await fetch(`${this.originUrl}/health`, { signal: AbortSignal.timeout(2e3) });
7123
+ if (!res.ok) return null;
7124
+ const body = await res.json();
7125
+ return parseServerUpdateAvailable(body?.updateAvailable);
7126
+ } catch {
7127
+ return null;
7128
+ }
7129
+ }
7098
7130
  async createApiKey(body) {
7099
7131
  return this.invoke(() => postApiV1Keys({ client: this.heyClient, body }));
7100
7132
  }
@@ -13139,15 +13171,34 @@ function createCanonryMcpServer(options = {}) {
13139
13171
  return createCanonryMcpServerWithCatalog(options).server;
13140
13172
  }
13141
13173
  var SERVER_INSTRUCTIONS = OPERATIONS_GUIDE.initialize;
13174
+ function updateNoticeInstructions(update) {
13175
+ const upgrade = update.installMethod === "docker" ? update.upgradeCommand : `${update.upgradeCommand}, then restart the Canonry server`;
13176
+ const caveat = upgradeCaveatFor(update.installMethod);
13177
+ return `Update available (UPDATE_AVAILABLE): canonry ${update.latest} is published; the connected Canonry server runs ${update.current}. Tell the operator. Upgrade: ${upgrade}. ${caveat ? `${caveat} ` : ""}Only upgrade with the operator's approval.`;
13178
+ }
13179
+ function updateNoticePayload(update) {
13180
+ const caveat = upgradeCaveatFor(update.installMethod);
13181
+ return { code: "UPDATE_AVAILABLE", ...update, ...caveat ? { note: caveat } : {} };
13182
+ }
13183
+ function readUpdateAvailable(getter) {
13184
+ try {
13185
+ return getter?.() ?? null;
13186
+ } catch {
13187
+ return null;
13188
+ }
13189
+ }
13142
13190
  function createCanonryMcpServerWithCatalog(options = {}) {
13143
13191
  const clientFactory = options.clientFactory ?? (() => createApiClient({ clientName: "canonry-mcp", surface: "mcp-stdio", actorSession: randomUUID() }));
13144
13192
  const client2 = clientFactory();
13145
13193
  const scope = options.scope ?? "all";
13194
+ const update = readUpdateAvailable(options.updateAvailable);
13146
13195
  const server = new McpServer({
13147
13196
  name: "canonry",
13148
13197
  version: PACKAGE_VERSION
13149
13198
  }, {
13150
- instructions: SERVER_INSTRUCTIONS
13199
+ instructions: update ? `${SERVER_INSTRUCTIONS.trimEnd()}
13200
+
13201
+ ${updateNoticeInstructions(update)}` : SERVER_INSTRUCTIONS
13151
13202
  });
13152
13203
  server.validateToolInput = async (_tool, args) => args;
13153
13204
  const entries = [];
@@ -13178,7 +13229,7 @@ function createCanonryMcpServerWithCatalog(options = {}) {
13178
13229
  const catalog = new DynamicToolCatalog(server, entries, scope, { eager });
13179
13230
  catalog.applyInitialEnablement();
13180
13231
  const mode = options.tiers !== void 0 ? "hosted-fixed-catalog" : eager ? "stdio-fixed-catalog" : "stdio-progressive";
13181
- registerMetaTools(server, catalog, { includeToolkitLoader: options.tiers === void 0, mode });
13232
+ registerMetaTools(server, catalog, { includeToolkitLoader: options.tiers === void 0, mode, updateAvailable: options.updateAvailable });
13182
13233
  server.registerResource("canonry-agent-operations-v1", OPERATIONS_GUIDE.resourceUri, {
13183
13234
  title: "Canonry Operations Guide v1",
13184
13235
  description: "Optional public operations guidance. Use canonry_help when resources are unavailable.",
@@ -13205,7 +13256,11 @@ function registerMetaTools(server, catalog, opts) {
13205
13256
  async (input) => {
13206
13257
  try {
13207
13258
  const parsed = helpInputSchema.parse(input ?? {});
13208
- const result = operationsHelp(catalog.helpResult(), opts.mode, parsed.intent, parsed.includeCatalog);
13259
+ const update = readUpdateAvailable(opts.updateAvailable);
13260
+ const result = {
13261
+ ...operationsHelp(catalog.helpResult(), opts.mode, parsed.intent, parsed.includeCatalog),
13262
+ ...update ? { updateAvailable: updateNoticePayload(update) } : {}
13263
+ };
13209
13264
  return { ...jsonToolResult(result), structuredContent: result };
13210
13265
  } catch (error) {
13211
13266
  return errorToolResult(error);
@@ -241,6 +241,7 @@ import {
241
241
  clusterByCosine,
242
242
  coerceSkillManifest,
243
243
  compactDateToIso,
244
+ compareSemver,
244
245
  competitorBatchRequestSchema,
245
246
  competitorDtoSchema,
246
247
  competitorLandscapeQuerySchema,
@@ -391,6 +392,7 @@ import {
391
392
  isLocationRedirectStatus,
392
393
  isReadOnlyKey,
393
394
  isRetryableHttpError,
395
+ isStrictSemver,
394
396
  isTemplateLinkRatio,
395
397
  isTrendBaseline,
396
398
  isVertexGroundingRedirect,
@@ -678,6 +680,7 @@ import {
678
680
  trafficStatusResponseSchema,
679
681
  trafficSyncResponseSchema,
680
682
  unsupportedKind,
683
+ upgradeCaveatFor,
681
684
  userDtoSchema,
682
685
  userListDtoSchema,
683
686
  validationError,
@@ -703,7 +706,7 @@ import {
703
706
  wordpressSchemaDeployResultDtoSchema,
704
707
  wordpressSchemaStatusResultDtoSchema,
705
708
  wordpressStatusDtoSchema
706
- } from "./chunk-5ZIUROAQ.js";
709
+ } from "./chunk-MMSPU72Z.js";
707
710
 
708
711
  // src/intelligence-service.ts
709
712
  import { eq as eq67, desc as desc29, asc as asc12, and as and57, ne as ne11, or as or16, inArray as inArray25, gte as gte18, lte as lte15, isNull as isNull12, sql as sql27, exists } from "drizzle-orm";
@@ -81302,11 +81305,72 @@ var GOOGLE_MARKETING_DOCTOR_CHECKS = createGoogleMarketingDoctorChecks(
81302
81305
  (ctx) => ctx.getGoogleMarketingDoctorInput?.(ctx)
81303
81306
  );
81304
81307
 
81308
+ // ../api-routes/src/doctor/checks/version.ts
81309
+ var versionCurrentCheck = {
81310
+ id: "canonry.version.current",
81311
+ category: CheckCategories.config,
81312
+ scope: CheckScopes.global,
81313
+ title: "Canonry version",
81314
+ run: (ctx) => {
81315
+ if (!ctx.getUpdateStatus) {
81316
+ return {
81317
+ status: CheckStatuses.skipped,
81318
+ code: "version.status-unavailable",
81319
+ summary: "This deployment does not report update status."
81320
+ };
81321
+ }
81322
+ const status = ctx.getUpdateStatus();
81323
+ if (!status.enabled) {
81324
+ return {
81325
+ status: CheckStatuses.skipped,
81326
+ code: "version.check-disabled",
81327
+ summary: `Update check is off (${status.disabledBy ?? "opted out"}); running ${status.current}.`,
81328
+ details: { current: status.current, ...status.disabledBy ? { disabledBy: status.disabledBy } : {} }
81329
+ };
81330
+ }
81331
+ if (!status.latest || !isStrictSemver(status.latest)) {
81332
+ return {
81333
+ status: CheckStatuses.skipped,
81334
+ code: "version.latest-unknown",
81335
+ summary: `Latest published version is not known yet (running ${status.current}).`,
81336
+ remediation: "The npm registry has not been reached yet. Re-run doctor in a moment; offline hosts stay skipped.",
81337
+ details: { current: status.current }
81338
+ };
81339
+ }
81340
+ if (compareSemver(status.latest, status.current) > 0) {
81341
+ const caveat = upgradeCaveatFor(status.installMethod);
81342
+ return {
81343
+ status: CheckStatuses.warn,
81344
+ code: "version.outdated",
81345
+ summary: `canonry ${status.latest} is available; this server runs ${status.current}.`,
81346
+ remediation: status.installMethod === "docker" ? `Upgrade the container: ${status.upgradeCommand}.` : `Run \`${status.upgradeCommand}\`, then restart the server (\`canonry stop && canonry start\`, or restart \`canonry serve\`).${caveat ? ` ${caveat}` : ""}`,
81347
+ details: {
81348
+ current: status.current,
81349
+ latest: status.latest,
81350
+ installMethod: status.installMethod,
81351
+ upgradeCommand: status.upgradeCommand,
81352
+ url: status.url
81353
+ }
81354
+ };
81355
+ }
81356
+ return {
81357
+ status: CheckStatuses.ok,
81358
+ code: "version.current",
81359
+ // "latest" would overstate it: when the registry is unreachable this can
81360
+ // come from an older on-disk cache, which proves only that nothing newer is known.
81361
+ summary: `No newer canonry known (running ${status.current}; latest seen ${status.latest}).`,
81362
+ details: { current: status.current, latest: status.latest }
81363
+ };
81364
+ }
81365
+ };
81366
+ var VERSION_CHECKS = [versionCurrentCheck];
81367
+
81305
81368
  // ../api-routes/src/doctor/registry.ts
81306
81369
  var ALL_CHECKS = [
81307
81370
  // Runtime-state checks run first so file-system gone errors surface
81308
81371
  // before any auth/integration checks try to touch the (orphaned) DB.
81309
81372
  ...RUNTIME_STATE_CHECKS,
81373
+ ...VERSION_CHECKS,
81310
81374
  ...GOOGLE_AUTH_CHECKS,
81311
81375
  ...GBP_AUTH_CHECKS,
81312
81376
  ...PLACES_CHECKS,
@@ -81412,6 +81476,7 @@ async function doctorRoutes(app, opts) {
81412
81476
  runtimeStatePaths: opts.runtimeStatePaths,
81413
81477
  bundledSkills: opts.bundledSkills,
81414
81478
  getAgentPluginState: opts.getAgentPluginState,
81479
+ getUpdateStatus: opts.getUpdateStatus,
81415
81480
  getGoogleMarketingDoctorInput: opts.getGoogleMarketingDoctorInput
81416
81481
  };
81417
81482
  return runChecks(ctx, ALL_CHECKS, { checkIds });
@@ -81441,6 +81506,7 @@ async function doctorRoutes(app, opts) {
81441
81506
  runtimeStatePaths: opts.runtimeStatePaths,
81442
81507
  bundledSkills: opts.bundledSkills,
81443
81508
  getAgentPluginState: opts.getAgentPluginState,
81509
+ getUpdateStatus: opts.getUpdateStatus,
81444
81510
  getGoogleMarketingDoctorInput: opts.getGoogleMarketingDoctorInput
81445
81511
  };
81446
81512
  return runChecks(ctx, ALL_CHECKS, { checkIds });
@@ -84750,6 +84816,7 @@ async function apiRoutes(app, opts) {
84750
84816
  runtimeStatePaths: opts.runtimeStatePaths,
84751
84817
  bundledSkills: opts.bundledSkills,
84752
84818
  getAgentPluginState: opts.getAgentPluginState,
84819
+ getUpdateStatus: opts.getUpdateStatus,
84753
84820
  getGoogleMarketingDoctorInput: opts.getGoogleMarketingDoctorInput
84754
84821
  });
84755
84822
  if (opts.registerAuthenticatedRoutes) {
@@ -5,11 +5,11 @@ import {
5
5
  installSkills,
6
6
  loadConfigRaw,
7
7
  saveConfigPatch
8
- } from "./chunk-5K2NUQOV.js";
8
+ } from "./chunk-AEE5DGTE.js";
9
9
  import {
10
10
  SKILL_MANIFEST_FILENAME,
11
11
  SkillsClients
12
- } from "./chunk-5ZIUROAQ.js";
12
+ } from "./chunk-MMSPU72Z.js";
13
13
 
14
14
  // src/skills-autosync.ts
15
15
  import fs from "fs";
@@ -18054,6 +18054,63 @@ function redactEmbeddedUrls(value) {
18054
18054
  });
18055
18055
  }
18056
18056
 
18057
+ // ../contracts/src/semver.ts
18058
+ var STRICT_SEMVER = /^(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)(?:-[\da-z-]+(?:\.[\da-z-]+)*)?(?:\+[\da-z-]+(?:\.[\da-z-]+)*)?$/i;
18059
+ var MAX_SEMVER_LENGTH = 128;
18060
+ function isStrictSemver(value) {
18061
+ return value.length <= MAX_SEMVER_LENGTH && STRICT_SEMVER.test(value);
18062
+ }
18063
+ function compareSemver(a, b) {
18064
+ const parse = (v) => {
18065
+ const parts = (v.split(/[-+]/)[0] ?? "").split(".");
18066
+ if (parts.length < 3) return null;
18067
+ const nums = parts.slice(0, 3).map(Number);
18068
+ if (!nums.every((n) => Number.isInteger(n) && n >= 0)) return null;
18069
+ return [nums[0], nums[1], nums[2]];
18070
+ };
18071
+ const pa = parse(a);
18072
+ const pb = parse(b);
18073
+ if (!pa || !pb) return 0;
18074
+ for (let i = 0; i < 3; i++) {
18075
+ if (pa[i] > pb[i]) return 1;
18076
+ if (pa[i] < pb[i]) return -1;
18077
+ }
18078
+ return 0;
18079
+ }
18080
+
18081
+ // ../contracts/src/update-notice.ts
18082
+ var CANONRY_NPM_PACKAGE = "@canonry/canonry";
18083
+ var CANONRY_NPM_PACKAGE_URL = `https://www.npmjs.com/package/${CANONRY_NPM_PACKAGE}`;
18084
+ var INSTALL_METHODS = ["npm", "homebrew", "docker"];
18085
+ function isInstallMethod(value) {
18086
+ return typeof value === "string" && INSTALL_METHODS.includes(value);
18087
+ }
18088
+ function upgradeCommandFor(method) {
18089
+ switch (method) {
18090
+ case "npm":
18091
+ return `npm install -g ${CANONRY_NPM_PACKAGE}`;
18092
+ case "homebrew":
18093
+ return "brew upgrade canonry";
18094
+ case "docker":
18095
+ return "pull or rebuild your canonry image, then recreate the container";
18096
+ }
18097
+ }
18098
+ function upgradeCaveatFor(method) {
18099
+ switch (method) {
18100
+ case "homebrew":
18101
+ return "Homebrew can trail npm briefly; if brew says canonry is up to date, retry later.";
18102
+ case "npm":
18103
+ case "docker":
18104
+ return null;
18105
+ }
18106
+ }
18107
+ function updateCheckEnvOptOut(env) {
18108
+ if (env.CANONRY_DISABLE_UPDATE_CHECK === "1") return "CANONRY_DISABLE_UPDATE_CHECK";
18109
+ if (env.DO_NOT_TRACK === "1") return "DO_NOT_TRACK";
18110
+ if (env.CI) return "CI";
18111
+ return null;
18112
+ }
18113
+
18057
18114
  export {
18058
18115
  __export,
18059
18116
  apiKeyDtoSchema,
@@ -18849,5 +18906,12 @@ export {
18849
18906
  isSensitiveDiagnosticQueryKey,
18850
18907
  redactLogValue,
18851
18908
  redactLogString,
18852
- diagnosticIdentity
18909
+ diagnosticIdentity,
18910
+ isStrictSemver,
18911
+ compareSemver,
18912
+ CANONRY_NPM_PACKAGE_URL,
18913
+ isInstallMethod,
18914
+ upgradeCommandFor,
18915
+ upgradeCaveatFor,
18916
+ updateCheckEnvOptOut
18853
18917
  };
package/dist/cli.js CHANGED
@@ -16,6 +16,7 @@ import {
16
16
  createServer,
17
17
  detectAndTrackUpgrade,
18
18
  formatAuditFactorScore,
19
+ formatUpdateNotice,
19
20
  getCloudflareTrafficConnectionBySourceId,
20
21
  getOrCreateAnonymousId,
21
22
  getTelemetryStatus,
@@ -24,6 +25,7 @@ import {
24
25
  isLoopbackBindHost,
25
26
  isTelemetryEnabled,
26
27
  listAgentProviders,
28
+ readCachedUpdateAvailable,
27
29
  setGoogleAuthConfig,
28
30
  setTelemetryPreference,
29
31
  setTelemetrySource,
@@ -31,11 +33,11 @@ import {
31
33
  trackCliCommandFinished,
32
34
  trackEvent,
33
35
  waitForServerRuntimeStartup
34
- } from "./chunk-BS4ZC7FG.js";
36
+ } from "./chunk-3KXI4C3S.js";
35
37
  import {
36
38
  autoSyncSkills,
37
39
  formatAutoSyncNotice
38
- } from "./chunk-NKEKDWGX.js";
40
+ } from "./chunk-KJM4DRZN.js";
39
41
  import {
40
42
  CliError,
41
43
  EXIT_SYSTEM_ERROR,
@@ -61,7 +63,7 @@ import {
61
63
  saveConfigPatch,
62
64
  systemError,
63
65
  usageError
64
- } from "./chunk-5K2NUQOV.js";
66
+ } from "./chunk-AEE5DGTE.js";
65
67
  import {
66
68
  CLOUDFLARE_WORKER_BINDINGS,
67
69
  CLOUDFLARE_WORKER_GENERATED_MARKER,
@@ -75,7 +77,7 @@ import {
75
77
  projects,
76
78
  queries,
77
79
  renderReportHtml
78
- } from "./chunk-WSYJ4IT7.js";
80
+ } from "./chunk-EBUX3C5H.js";
79
81
  import {
80
82
  AdsDeliverySnapshotStatuses,
81
83
  AdsHistoricalCampaignRollupStatuses,
@@ -154,7 +156,7 @@ import {
154
156
  snapshotProviderModeSchema,
155
157
  visibilityReportRequestSchema,
156
158
  winnabilityClassSchema
157
- } from "./chunk-5ZIUROAQ.js";
159
+ } from "./chunk-MMSPU72Z.js";
158
160
 
159
161
  // src/cli.ts
160
162
  import { pathToFileURL } from "url";
@@ -19304,17 +19306,12 @@ async function runCli(args = process.argv.slice(2)) {
19304
19306
  ...cliRuntimeContext()
19305
19307
  });
19306
19308
  }
19307
- if (!isHelpRequest && command !== "telemetry" && process.stderr.isTTY) {
19308
- void checkLatestVersionForCli().then((update) => {
19309
- if (!update) return;
19310
- process.stderr.write(
19311
- `
19312
- \u2192 canonry ${update.latest} is available (you have ${update.current}).
19313
- Upgrade: ${update.upgradeCommand}
19314
-
19315
- `
19316
- );
19317
- });
19309
+ if (!isHelpRequest && command !== "telemetry") {
19310
+ const update = readCachedUpdateAvailable();
19311
+ if (update) {
19312
+ process.stderr.write(formatUpdateNotice(update, { format, interactive: Boolean(process.stderr.isTTY) }));
19313
+ }
19314
+ void checkLatestVersionForCli();
19318
19315
  }
19319
19316
  const commandStartedAt = Date.now();
19320
19317
  try {
package/dist/index.js CHANGED
@@ -3,12 +3,12 @@ import {
3
3
  createGoogleMarketingCredentialStore,
4
4
  createGoogleMarketingRuntime,
5
5
  createServer
6
- } from "./chunk-BS4ZC7FG.js";
6
+ } from "./chunk-3KXI4C3S.js";
7
7
  import {
8
8
  loadConfig
9
- } from "./chunk-5K2NUQOV.js";
10
- import "./chunk-WSYJ4IT7.js";
11
- import "./chunk-5ZIUROAQ.js";
9
+ } from "./chunk-AEE5DGTE.js";
10
+ import "./chunk-EBUX3C5H.js";
11
+ import "./chunk-MMSPU72Z.js";
12
12
  export {
13
13
  GoogleMarketingRuntimeError,
14
14
  createGoogleMarketingCredentialStore,
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  IntelligenceService
3
- } from "./chunk-WSYJ4IT7.js";
4
- import "./chunk-5ZIUROAQ.js";
3
+ } from "./chunk-EBUX3C5H.js";
4
+ import "./chunk-MMSPU72Z.js";
5
5
  export {
6
6
  IntelligenceService
7
7
  };
package/dist/mcp.js CHANGED
@@ -1,17 +1,49 @@
1
1
  import {
2
2
  autoSyncSkills
3
- } from "./chunk-NKEKDWGX.js";
3
+ } from "./chunk-KJM4DRZN.js";
4
4
  import {
5
5
  createApiClient,
6
6
  createCanonryMcpServer
7
- } from "./chunk-5K2NUQOV.js";
7
+ } from "./chunk-AEE5DGTE.js";
8
8
  import {
9
- isReadOnlyKey
10
- } from "./chunk-5ZIUROAQ.js";
9
+ isReadOnlyKey,
10
+ updateCheckEnvOptOut
11
+ } from "./chunk-MMSPU72Z.js";
11
12
 
12
13
  // src/mcp/cli.ts
13
14
  import { randomUUID } from "crypto";
14
15
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
16
+
17
+ // src/mcp/update-notice.ts
18
+ function createUpdateNoticeSource(client, opts = {}) {
19
+ const disabled = updateCheckEnvOptOut(opts.env ?? process.env) !== null;
20
+ const ttlMs = opts.ttlMs ?? 60 * 60 * 1e3;
21
+ const now = opts.now ?? Date.now;
22
+ let value = null;
23
+ let fetchedAt = Number.NEGATIVE_INFINITY;
24
+ let inFlight = null;
25
+ const refresh = () => {
26
+ if (disabled) return Promise.resolve();
27
+ inFlight ??= client.getServerUpdateAvailable().then((next) => {
28
+ value = next;
29
+ }, () => {
30
+ value = null;
31
+ }).finally(() => {
32
+ fetchedAt = now();
33
+ inFlight = null;
34
+ });
35
+ return inFlight;
36
+ };
37
+ return {
38
+ refresh,
39
+ get: () => {
40
+ if (!disabled && !inFlight && now() - fetchedAt >= ttlMs) void refresh();
41
+ return value;
42
+ }
43
+ };
44
+ }
45
+
46
+ // src/mcp/cli.ts
15
47
  var HELP_TEXT = `Usage: canonry-mcp [--read-only | --scope=<all|read-only>] [--eager]
16
48
 
17
49
  Stdio MCP adapter over the Canonry public API. Inherits config from
@@ -47,8 +79,17 @@ async function main(argv = process.argv.slice(2)) {
47
79
  }
48
80
  void autoSyncSkills();
49
81
  const client = createApiClient({ clientName: "canonry-mcp", surface: "mcp-stdio", actorSession: randomUUID() });
50
- const authorization = await resolveEffectiveAuthorization(client, options.scope);
51
- const server = createCanonryMcpServer({ ...authorization, eager: options.eager, clientFactory: () => client });
82
+ const updateNotice = createUpdateNoticeSource(client);
83
+ const [authorization] = await Promise.all([
84
+ resolveEffectiveAuthorization(client, options.scope),
85
+ updateNotice.refresh()
86
+ ]);
87
+ const server = createCanonryMcpServer({
88
+ ...authorization,
89
+ eager: options.eager,
90
+ clientFactory: () => client,
91
+ updateAvailable: updateNotice.get
92
+ });
52
93
  await server.connect(new StdioServerTransport());
53
94
  }
54
95
  async function resolveEffectiveScope(client, flagScope) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ainyc/canonry",
3
- "version": "5.1.2",
3
+ "version": "5.1.4",
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": [
@@ -91,27 +91,27 @@
91
91
  "@ainyc/canonry-api-routes": "0.0.0",
92
92
  "@ainyc/canonry-config": "0.0.0",
93
93
  "@ainyc/canonry-contracts": "0.0.0",
94
- "@ainyc/canonry-integration-bing": "0.0.0",
95
94
  "@ainyc/canonry-db": "0.0.0",
96
- "@ainyc/canonry-integration-cloud-run": "0.0.0",
95
+ "@ainyc/canonry-integration-bing": "0.0.0",
97
96
  "@ainyc/canonry-integration-cloudflare-queue": "0.0.0",
98
97
  "@ainyc/canonry-integration-commoncrawl": "0.0.0",
99
- "@ainyc/canonry-integration-google-ads": "0.0.0",
98
+ "@ainyc/canonry-integration-cloud-run": "0.0.0",
100
99
  "@ainyc/canonry-integration-google": "0.0.0",
101
100
  "@ainyc/canonry-integration-cloudflare-worker": "0.0.0",
102
- "@ainyc/canonry-integration-google-places": "0.0.0",
101
+ "@ainyc/canonry-integration-google-ads": "0.0.0",
103
102
  "@ainyc/canonry-integration-google-business-profile": "0.0.0",
103
+ "@ainyc/canonry-integration-google-tag-manager": "0.0.0",
104
+ "@ainyc/canonry-integration-google-places": "0.0.0",
104
105
  "@ainyc/canonry-integration-openai-ads": "0.0.0",
105
- "@ainyc/canonry-integration-wordpress": "0.0.0",
106
- "@ainyc/canonry-intelligence": "0.0.0",
107
106
  "@ainyc/canonry-integration-traffic": "0.0.0",
108
- "@ainyc/canonry-provider-cdp": "0.0.0",
109
- "@ainyc/canonry-integration-google-tag-manager": "0.0.0",
107
+ "@ainyc/canonry-intelligence": "0.0.0",
108
+ "@ainyc/canonry-integration-wordpress": "0.0.0",
110
109
  "@ainyc/canonry-provider-claude": "0.0.0",
111
- "@ainyc/canonry-provider-gemini": "0.0.0",
112
110
  "@ainyc/canonry-provider-local": "0.0.0",
111
+ "@ainyc/canonry-provider-cdp": "0.0.0",
113
112
  "@ainyc/canonry-provider-openai": "0.0.0",
114
- "@ainyc/canonry-provider-perplexity": "0.0.0"
113
+ "@ainyc/canonry-provider-perplexity": "0.0.0",
114
+ "@ainyc/canonry-provider-gemini": "0.0.0"
115
115
  },
116
116
  "scripts": {
117
117
  "build": "pnpm run build:cli && pnpm run build:web",