@ainyc/canonry 5.1.2 → 5.1.3
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/canonry/references/canonry-cli.md +2 -0
- package/dist/{chunk-BS4ZC7FG.js → chunk-5ZOLPHTO.js} +150 -76
- package/dist/{chunk-5K2NUQOV.js → chunk-AEE5DGTE.js} +59 -4
- package/dist/{chunk-WSYJ4IT7.js → chunk-EBUX3C5H.js} +68 -1
- package/dist/{chunk-NKEKDWGX.js → chunk-KJM4DRZN.js} +2 -2
- package/dist/{chunk-5ZIUROAQ.js → chunk-MMSPU72Z.js} +65 -1
- package/dist/cli.js +13 -16
- package/dist/index.js +4 -4
- package/dist/{intelligence-service-D2HY2S5F.js → intelligence-service-25NQCETC.js} +2 -2
- package/dist/mcp.js +47 -6
- package/package.json +10 -10
|
@@ -1258,6 +1258,8 @@ Every command takes `--format`:
|
|
|
1258
1258
|
- **`json`** — one pretty-printed JSON document (the full envelope). Stable contract.
|
|
1259
1259
|
- **`jsonl`** — newline-delimited JSON: the command's **primary collection**, one self-contained record per line. The agent-friendly machine format — no envelope key to guess (`.checks` vs `.results` vs `.rows`), no `jq` flattening, greppable line by line.
|
|
1260
1260
|
|
|
1261
|
+
**Update notice.** When a newer canonry is published, the CLI writes one line to **stderr** before the command runs (stdout is never touched): `[canonry] UPDATE_AVAILABLE: ...` in text mode, or `{"notice":{"code":"UPDATE_AVAILABLE","current":"…","latest":"…","installMethod":"npm|homebrew|docker","upgradeCommand":"…","url":"…"}}` with `--format json|jsonl`. Tell the operator, or run the `upgradeCommand` (it already matches how canonry was installed) and restart `canonry serve`. `cnry doctor --check canonry.version.current` reports the same for the running server (`version.outdated` warns). Silence with `CANONRY_DISABLE_UPDATE_CHECK=1`. Over MCP the same notice arrives in the initialize instructions and as `updateAvailable` in `canonry_help`.
|
|
1262
|
+
|
|
1261
1263
|
`jsonl` is supported by every **collection** command — one whose primary output is a list: `insights`, `runs`, `evidence`, `history`, `query/keyword/competitor list`, `notify list/events`, `google` reads (`performance`, `performance-daily`, `inspections`, `coverage-history`, `deindexed`, `status`, `properties`, `list-sitemaps`), `bing` reads (`coverage-history`, `inspections`, `performance`, `sites`), `ga` reads (`ai-referral-daily`, `ai-referral-history`, `social-referral-history`, `session-history`, `coverage`), `google-ads` customer/snapshot reads, `gtm` account/container/workspace/snapshot reads, conversion-tracking contracts and integrity findings, `ads geo search` and `ads conversions` reads, `traffic events/sources/status`, `discover list/show`, `content targets/sources/gaps/map`, `backlinks list/releases`, `project list/locations`, `key list`, `agent memory list`, `agent providers`, `sources` (streams the ranked cited-domain list), and `doctor`. (`content brief` is an object command — `jsonl` degrades to its JSON document.)
|
|
1262
1264
|
|
|
1263
1265
|
Each `jsonl` line re-injects the envelope context it would otherwise lose, so a line lifted out still self-describes:
|
|
@@ -17,7 +17,7 @@ import {
|
|
|
17
17
|
resolveOperatorApiKeyIds,
|
|
18
18
|
runWithUsageTags,
|
|
19
19
|
saveConfigPatch
|
|
20
|
-
} from "./chunk-
|
|
20
|
+
} from "./chunk-AEE5DGTE.js";
|
|
21
21
|
import {
|
|
22
22
|
CC_CACHE_DIR,
|
|
23
23
|
DUCKDB_SPEC,
|
|
@@ -168,7 +168,7 @@ import {
|
|
|
168
168
|
siteCrawlSnapshots,
|
|
169
169
|
toAlertView,
|
|
170
170
|
usageCounters
|
|
171
|
-
} from "./chunk-
|
|
171
|
+
} from "./chunk-EBUX3C5H.js";
|
|
172
172
|
import {
|
|
173
173
|
AGENT_MEMORY_VALUE_MAX_BYTES,
|
|
174
174
|
AGENT_NONE,
|
|
@@ -179,6 +179,7 @@ import {
|
|
|
179
179
|
AdsCampaignBiddingTypes,
|
|
180
180
|
AgentProviderIds,
|
|
181
181
|
BacklinkSources,
|
|
182
|
+
CANONRY_NPM_PACKAGE_URL,
|
|
182
183
|
CITED_URL_CAPTURE_VERSION,
|
|
183
184
|
CcReleaseSyncStatuses,
|
|
184
185
|
DEFAULT_VIEWER_RESEARCH_DAILY_RUN_LIMIT,
|
|
@@ -239,6 +240,7 @@ import {
|
|
|
239
240
|
classifyProviderErrorMessages,
|
|
240
241
|
classifyTemplateLinkEdge,
|
|
241
242
|
cloudflareQueueNameSchema,
|
|
243
|
+
compareSemver,
|
|
242
244
|
computeBacklinkSummaryMetrics,
|
|
243
245
|
contentActionLabel,
|
|
244
246
|
contentBriefDtoSchema,
|
|
@@ -279,9 +281,11 @@ import {
|
|
|
279
281
|
isAgentProviderId,
|
|
280
282
|
isBrowserProvider,
|
|
281
283
|
isGhostTelemetryEvent,
|
|
284
|
+
isInstallMethod,
|
|
282
285
|
isReadOnlyKey,
|
|
283
286
|
isRetryableHttpError,
|
|
284
287
|
isSelfLink,
|
|
288
|
+
isStrictSemver,
|
|
285
289
|
isVertexGroundingRedirect,
|
|
286
290
|
mapWithConcurrency,
|
|
287
291
|
maskTelemetryAnonymousId,
|
|
@@ -313,11 +317,14 @@ import {
|
|
|
313
317
|
templateLinkUbiquityAvailable,
|
|
314
318
|
textContainsBrandAlias,
|
|
315
319
|
textContainsDomain,
|
|
320
|
+
updateCheckEnvOptOut,
|
|
321
|
+
upgradeCaveatFor,
|
|
322
|
+
upgradeCommandFor,
|
|
316
323
|
usageSurfaceSchema,
|
|
317
324
|
validationError,
|
|
318
325
|
winnabilityClassLabel,
|
|
319
326
|
withRetry
|
|
320
|
-
} from "./chunk-
|
|
327
|
+
} from "./chunk-MMSPU72Z.js";
|
|
321
328
|
|
|
322
329
|
// src/runtime-context.ts
|
|
323
330
|
function cliRuntimeContext(env = process.env, stdio = process) {
|
|
@@ -569,47 +576,46 @@ function deliverEvent(event, properties, options, delivery = {}) {
|
|
|
569
576
|
}
|
|
570
577
|
|
|
571
578
|
// src/update-check.ts
|
|
579
|
+
import fs from "fs";
|
|
572
580
|
import { createRequire as createRequire2 } from "module";
|
|
581
|
+
import { fileURLToPath } from "url";
|
|
573
582
|
var _require2 = createRequire2(import.meta.url);
|
|
574
583
|
var { version: PKG_VERSION } = _require2("../package.json");
|
|
575
584
|
var PKG_NAME = "@canonry/canonry";
|
|
576
585
|
var NPM_DIST_TAGS_URL = `https://registry.npmjs.org/-/package/${PKG_NAME}/dist-tags`;
|
|
577
|
-
var NPM_PACKAGE_URL =
|
|
586
|
+
var NPM_PACKAGE_URL = CANONRY_NPM_PACKAGE_URL;
|
|
578
587
|
var FETCH_TIMEOUT_MS = 1500;
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
if (
|
|
583
|
-
|
|
588
|
+
var CONTAINER_MARKERS = ["/.dockerenv", "/run/.containerenv"];
|
|
589
|
+
function detectInstallMethod(opts) {
|
|
590
|
+
const declared = (opts?.env ?? process.env).CANONRY_INSTALL_METHOD;
|
|
591
|
+
if (isInstallMethod(declared)) return declared;
|
|
592
|
+
let modulePath = opts?.modulePath ?? fileURLToPath(import.meta.url);
|
|
584
593
|
try {
|
|
585
|
-
|
|
586
|
-
return raw?.updateCheck !== false;
|
|
594
|
+
modulePath = fs.realpathSync(modulePath);
|
|
587
595
|
} catch {
|
|
588
|
-
return true;
|
|
589
596
|
}
|
|
597
|
+
if (modulePath.replace(/\\/g, "/").includes("/Cellar/canonry/")) return "homebrew";
|
|
598
|
+
const exists = opts?.exists ?? fs.existsSync;
|
|
599
|
+
if (CONTAINER_MARKERS.some((marker) => exists(marker))) return "docker";
|
|
600
|
+
return "npm";
|
|
590
601
|
}
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
if (!pa || !pb) return 0;
|
|
608
|
-
for (let i = 0; i < 3; i++) {
|
|
609
|
-
if (pa[i] > pb[i]) return 1;
|
|
610
|
-
if (pa[i] < pb[i]) return -1;
|
|
602
|
+
var detectedInstallMethod;
|
|
603
|
+
function currentInstallMethod() {
|
|
604
|
+
detectedInstallMethod ??= detectInstallMethod();
|
|
605
|
+
return detectedInstallMethod;
|
|
606
|
+
}
|
|
607
|
+
function isUpdateCheckEnabled() {
|
|
608
|
+
return updateCheckDisabledReason() === null;
|
|
609
|
+
}
|
|
610
|
+
function updateCheckDisabledReason() {
|
|
611
|
+
const envOptOut = updateCheckEnvOptOut(process.env);
|
|
612
|
+
if (envOptOut) return envOptOut;
|
|
613
|
+
if (!configExists()) return null;
|
|
614
|
+
try {
|
|
615
|
+
return loadConfigRaw()?.updateCheck === false ? "config" : null;
|
|
616
|
+
} catch {
|
|
617
|
+
return null;
|
|
611
618
|
}
|
|
612
|
-
return 0;
|
|
613
619
|
}
|
|
614
620
|
async function fetchLatestVersion(opts) {
|
|
615
621
|
const controller = new AbortController();
|
|
@@ -622,7 +628,7 @@ async function fetchLatestVersion(opts) {
|
|
|
622
628
|
});
|
|
623
629
|
if (!res.ok) return null;
|
|
624
630
|
const data = await res.json();
|
|
625
|
-
if (typeof data.latest !== "string") return null;
|
|
631
|
+
if (typeof data.latest !== "string" || !isStrictSemver(data.latest)) return null;
|
|
626
632
|
return data.latest;
|
|
627
633
|
} catch {
|
|
628
634
|
return null;
|
|
@@ -630,13 +636,15 @@ async function fetchLatestVersion(opts) {
|
|
|
630
636
|
clearTimeout(timeout);
|
|
631
637
|
}
|
|
632
638
|
}
|
|
633
|
-
function buildUpdateAvailable(current, latest) {
|
|
639
|
+
function buildUpdateAvailable(current, latest, installMethod = currentInstallMethod()) {
|
|
640
|
+
if (!isStrictSemver(latest)) return null;
|
|
634
641
|
if (compareSemver(latest, current) <= 0) return null;
|
|
635
642
|
return {
|
|
636
643
|
current,
|
|
637
644
|
latest,
|
|
638
645
|
url: NPM_PACKAGE_URL,
|
|
639
|
-
upgradeCommand:
|
|
646
|
+
upgradeCommand: upgradeCommandFor(installMethod),
|
|
647
|
+
installMethod
|
|
640
648
|
};
|
|
641
649
|
}
|
|
642
650
|
async function checkLatestVersionForCli(opts) {
|
|
@@ -674,6 +682,45 @@ async function checkLatestVersionForCli(opts) {
|
|
|
674
682
|
}
|
|
675
683
|
return buildUpdateAvailable(PKG_VERSION, latest);
|
|
676
684
|
}
|
|
685
|
+
function readCachedUpdateAvailable() {
|
|
686
|
+
if (!isUpdateCheckEnabled()) return null;
|
|
687
|
+
if (!configExists()) return null;
|
|
688
|
+
try {
|
|
689
|
+
const cachedLatest = loadConfigRaw()?.lastKnownLatestVersion;
|
|
690
|
+
if (typeof cachedLatest !== "string") return null;
|
|
691
|
+
return buildUpdateAvailable(PKG_VERSION, cachedLatest);
|
|
692
|
+
} catch {
|
|
693
|
+
return null;
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
var UPDATE_AVAILABLE_NOTICE_CODE = "UPDATE_AVAILABLE";
|
|
697
|
+
function formatUpdateNotice(update, opts) {
|
|
698
|
+
const caveat = upgradeCaveatFor(update.installMethod);
|
|
699
|
+
if (opts.format === "json" || opts.format === "jsonl") {
|
|
700
|
+
return `${JSON.stringify({
|
|
701
|
+
notice: {
|
|
702
|
+
code: UPDATE_AVAILABLE_NOTICE_CODE,
|
|
703
|
+
current: update.current,
|
|
704
|
+
latest: update.latest,
|
|
705
|
+
installMethod: update.installMethod,
|
|
706
|
+
upgradeCommand: update.upgradeCommand,
|
|
707
|
+
url: update.url,
|
|
708
|
+
...caveat ? { note: caveat } : {}
|
|
709
|
+
}
|
|
710
|
+
})}
|
|
711
|
+
`;
|
|
712
|
+
}
|
|
713
|
+
if (opts.interactive) {
|
|
714
|
+
return `
|
|
715
|
+
\u2192 canonry ${update.latest} is available (you have ${update.current}).
|
|
716
|
+
Upgrade: ${update.upgradeCommand}
|
|
717
|
+
` + (caveat ? ` ${caveat}
|
|
718
|
+
` : "") + "\n";
|
|
719
|
+
}
|
|
720
|
+
const upgrade = update.installMethod === "docker" ? `Upgrade: ${update.upgradeCommand}.` : `Upgrade with \`${update.upgradeCommand}\`, then restart any running \`canonry serve\`.`;
|
|
721
|
+
return `[canonry] ${UPDATE_AVAILABLE_NOTICE_CODE}: canonry ${update.latest} is available (installed ${update.current}). ${upgrade} ${caveat ? `${caveat} ` : ""}Silence with CANONRY_DISABLE_UPDATE_CHECK=1.
|
|
722
|
+
`;
|
|
723
|
+
}
|
|
677
724
|
var memoryCache = null;
|
|
678
725
|
var inFlight = null;
|
|
679
726
|
function startBackgroundRefresh(getNow) {
|
|
@@ -700,6 +747,22 @@ function checkLatestVersionForServer(opts) {
|
|
|
700
747
|
if (!memoryCache || !memoryCache.latest) return null;
|
|
701
748
|
return buildUpdateAvailable(PKG_VERSION, memoryCache.latest);
|
|
702
749
|
}
|
|
750
|
+
function getServerUpdateStatus(opts) {
|
|
751
|
+
const installMethod = currentInstallMethod();
|
|
752
|
+
const base = { current: PKG_VERSION, installMethod, upgradeCommand: upgradeCommandFor(installMethod), url: NPM_PACKAGE_URL };
|
|
753
|
+
const disabledBy = updateCheckDisabledReason();
|
|
754
|
+
if (disabledBy) return { ...base, enabled: false, disabledBy, latest: null };
|
|
755
|
+
checkLatestVersionForServer(opts);
|
|
756
|
+
let latest = memoryCache?.latest ?? null;
|
|
757
|
+
if (!latest && configExists()) {
|
|
758
|
+
try {
|
|
759
|
+
const cached = loadConfigRaw()?.lastKnownLatestVersion;
|
|
760
|
+
if (typeof cached === "string") latest = cached;
|
|
761
|
+
} catch {
|
|
762
|
+
}
|
|
763
|
+
}
|
|
764
|
+
return { ...base, enabled: true, latest: latest && isStrictSemver(latest) ? latest : null };
|
|
765
|
+
}
|
|
703
766
|
|
|
704
767
|
// src/google-marketing-runtime.ts
|
|
705
768
|
import crypto3 from "crypto";
|
|
@@ -3470,9 +3533,9 @@ function createGoogleMarketingRuntime(options) {
|
|
|
3470
3533
|
// src/server.ts
|
|
3471
3534
|
import { createRequire as createRequire4 } from "module";
|
|
3472
3535
|
import crypto27 from "crypto";
|
|
3473
|
-
import
|
|
3536
|
+
import fs8 from "fs";
|
|
3474
3537
|
import path8 from "path";
|
|
3475
|
-
import { fileURLToPath as
|
|
3538
|
+
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
3476
3539
|
import { and as and20, eq as eq26 } from "drizzle-orm";
|
|
3477
3540
|
import Fastify from "fastify";
|
|
3478
3541
|
import os4 from "os";
|
|
@@ -5393,12 +5456,12 @@ function sleep2(ms) {
|
|
|
5393
5456
|
}
|
|
5394
5457
|
|
|
5395
5458
|
// ../provider-cdp/src/screenshot.ts
|
|
5396
|
-
import
|
|
5459
|
+
import fs2 from "fs";
|
|
5397
5460
|
import path from "path";
|
|
5398
5461
|
async function captureElementScreenshot(client, selector, outputPath) {
|
|
5399
5462
|
const dir = path.dirname(outputPath);
|
|
5400
|
-
if (!
|
|
5401
|
-
|
|
5463
|
+
if (!fs2.existsSync(dir)) {
|
|
5464
|
+
fs2.mkdirSync(dir, { recursive: true });
|
|
5402
5465
|
}
|
|
5403
5466
|
let clip;
|
|
5404
5467
|
try {
|
|
@@ -5432,7 +5495,7 @@ async function captureElementScreenshot(client, selector, outputPath) {
|
|
|
5432
5495
|
}
|
|
5433
5496
|
const { data } = await client.Page.captureScreenshot(screenshotParams);
|
|
5434
5497
|
const buffer = Buffer.from(data, "base64");
|
|
5435
|
-
|
|
5498
|
+
fs2.writeFileSync(outputPath, buffer);
|
|
5436
5499
|
return outputPath;
|
|
5437
5500
|
}
|
|
5438
5501
|
|
|
@@ -6393,7 +6456,7 @@ function nonBlank(value) {
|
|
|
6393
6456
|
return trimmed ? trimmed : void 0;
|
|
6394
6457
|
}
|
|
6395
6458
|
function resolveBuildCommit(env = process.env) {
|
|
6396
|
-
const embedded = true ? "
|
|
6459
|
+
const embedded = true ? "1f10658e1c5f2b615dfbee110d3f8cf68dbfbda9" : void 0;
|
|
6397
6460
|
return nonBlank(embedded) ?? nonBlank(env.CANONRY_COMMIT);
|
|
6398
6461
|
}
|
|
6399
6462
|
function resolveInstanceIdentity(env = process.env) {
|
|
@@ -6405,7 +6468,7 @@ function resolveInstanceIdentity(env = process.env) {
|
|
|
6405
6468
|
|
|
6406
6469
|
// src/job-runner.ts
|
|
6407
6470
|
import crypto6 from "crypto";
|
|
6408
|
-
import
|
|
6471
|
+
import fs3 from "fs";
|
|
6409
6472
|
import path3 from "path";
|
|
6410
6473
|
import os3 from "os";
|
|
6411
6474
|
import { and as and2, eq as eq2, inArray, ne, sql as sql2 } from "drizzle-orm";
|
|
@@ -6973,12 +7036,12 @@ var JobRunner = class {
|
|
|
6973
7036
|
allBrandNames
|
|
6974
7037
|
);
|
|
6975
7038
|
let screenshotRelPath = null;
|
|
6976
|
-
if (raw.screenshotPath &&
|
|
7039
|
+
if (raw.screenshotPath && fs3.existsSync(raw.screenshotPath)) {
|
|
6977
7040
|
const snapshotId = crypto6.randomUUID();
|
|
6978
7041
|
const screenshotDir = path3.join(os3.homedir(), ".canonry", "screenshots", runId);
|
|
6979
|
-
if (!
|
|
7042
|
+
if (!fs3.existsSync(screenshotDir)) fs3.mkdirSync(screenshotDir, { recursive: true });
|
|
6980
7043
|
const destPath = path3.join(screenshotDir, `${snapshotId}.png`);
|
|
6981
|
-
|
|
7044
|
+
fs3.renameSync(raw.screenshotPath, destPath);
|
|
6982
7045
|
screenshotRelPath = `${runId}/${snapshotId}.png`;
|
|
6983
7046
|
this.db.insert(querySnapshots).values({
|
|
6984
7047
|
id: snapshotId,
|
|
@@ -7131,11 +7194,11 @@ var JobRunner = class {
|
|
|
7131
7194
|
);
|
|
7132
7195
|
const snapshotId = crypto6.randomUUID();
|
|
7133
7196
|
let screenshotRelPath = null;
|
|
7134
|
-
if (raw.screenshotPath &&
|
|
7197
|
+
if (raw.screenshotPath && fs3.existsSync(raw.screenshotPath)) {
|
|
7135
7198
|
const screenshotDir = path3.join(os3.homedir(), ".canonry", "screenshots", runId);
|
|
7136
|
-
if (!
|
|
7199
|
+
if (!fs3.existsSync(screenshotDir)) fs3.mkdirSync(screenshotDir, { recursive: true });
|
|
7137
7200
|
const destPath = path3.join(screenshotDir, `${snapshotId}.png`);
|
|
7138
|
-
|
|
7201
|
+
fs3.renameSync(raw.screenshotPath, destPath);
|
|
7139
7202
|
screenshotRelPath = `${runId}/${snapshotId}.png`;
|
|
7140
7203
|
}
|
|
7141
7204
|
this.db.insert(querySnapshots).values({
|
|
@@ -9695,7 +9758,7 @@ function computeSummary(rows) {
|
|
|
9695
9758
|
|
|
9696
9759
|
// src/backlink-extract.ts
|
|
9697
9760
|
import crypto16 from "crypto";
|
|
9698
|
-
import
|
|
9761
|
+
import fs4 from "fs";
|
|
9699
9762
|
import { and as and9, desc as desc5, eq as eq11 } from "drizzle-orm";
|
|
9700
9763
|
var log11 = createLogger("BacklinkExtract");
|
|
9701
9764
|
function defaultDeps3() {
|
|
@@ -9724,7 +9787,7 @@ async function executeBacklinkExtract(db, runId, projectId, opts = {}) {
|
|
|
9724
9787
|
if (!sync.vertexPath || !sync.edgesPath) {
|
|
9725
9788
|
throw new Error(`Release ${sync.release} is missing cached file paths`);
|
|
9726
9789
|
}
|
|
9727
|
-
if (!
|
|
9790
|
+
if (!fs4.existsSync(sync.vertexPath) || !fs4.existsSync(sync.edgesPath)) {
|
|
9728
9791
|
throw new Error(
|
|
9729
9792
|
`Cache for release ${sync.release} is missing from disk (expected at ${sync.vertexPath}). The sync record exists in the database, but the ~16 GB dump was deleted or never present on this machine. Re-sync this release from the Backlinks admin page to restore the cache.`
|
|
9730
9793
|
);
|
|
@@ -12319,7 +12382,7 @@ function readStoredGroundingSources(rawResponse) {
|
|
|
12319
12382
|
return result;
|
|
12320
12383
|
}
|
|
12321
12384
|
async function backfillInsightsCommand(project, opts) {
|
|
12322
|
-
const { IntelligenceService: IntelligenceService2 } = await import("./intelligence-service-
|
|
12385
|
+
const { IntelligenceService: IntelligenceService2 } = await import("./intelligence-service-25NQCETC.js");
|
|
12323
12386
|
const config = loadConfig();
|
|
12324
12387
|
const db = createClient(config.database);
|
|
12325
12388
|
migrate(db);
|
|
@@ -14054,7 +14117,7 @@ import crypto25 from "crypto";
|
|
|
14054
14117
|
import { eq as eq21 } from "drizzle-orm";
|
|
14055
14118
|
|
|
14056
14119
|
// src/agent/session.ts
|
|
14057
|
-
import
|
|
14120
|
+
import fs7 from "fs";
|
|
14058
14121
|
import path7 from "path";
|
|
14059
14122
|
import { Agent } from "@mariozechner/pi-agent-core";
|
|
14060
14123
|
import { registerBuiltInApiProviders } from "@mariozechner/pi-ai";
|
|
@@ -14298,25 +14361,25 @@ function buildAgentProvidersResponse(config) {
|
|
|
14298
14361
|
}
|
|
14299
14362
|
|
|
14300
14363
|
// src/agent/skill-paths.ts
|
|
14301
|
-
import
|
|
14364
|
+
import fs5 from "fs";
|
|
14302
14365
|
import path5 from "path";
|
|
14303
|
-
import { fileURLToPath } from "url";
|
|
14366
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
14304
14367
|
function resolveAeroSkillDir(pkgDir) {
|
|
14305
|
-
const here = pkgDir ?? path5.dirname(
|
|
14368
|
+
const here = pkgDir ?? path5.dirname(fileURLToPath2(import.meta.url));
|
|
14306
14369
|
const candidates = [
|
|
14307
14370
|
path5.join(here, "../assets/agent-workspace/skills/aero"),
|
|
14308
14371
|
path5.join(here, "../../assets/agent-workspace/skills/aero"),
|
|
14309
14372
|
path5.join(here, "../../../../skills/aero")
|
|
14310
14373
|
];
|
|
14311
14374
|
for (const candidate of candidates) {
|
|
14312
|
-
if (
|
|
14375
|
+
if (fs5.existsSync(path5.join(candidate, "SKILL.md"))) return candidate;
|
|
14313
14376
|
}
|
|
14314
14377
|
throw new Error(`Aero skill not found. Searched:
|
|
14315
14378
|
${candidates.join("\n ")}`);
|
|
14316
14379
|
}
|
|
14317
14380
|
|
|
14318
14381
|
// src/agent/skill-tools.ts
|
|
14319
|
-
import
|
|
14382
|
+
import fs6 from "fs";
|
|
14320
14383
|
import path6 from "path";
|
|
14321
14384
|
import { Type } from "@sinclair/typebox";
|
|
14322
14385
|
var MAX_DOC_CHARS = 2e4;
|
|
@@ -14339,12 +14402,12 @@ function parseDescription(body) {
|
|
|
14339
14402
|
}
|
|
14340
14403
|
function scanSkillDocs(skillDir) {
|
|
14341
14404
|
const refsDir = path6.join(skillDir ?? resolveAeroSkillDir(), "references");
|
|
14342
|
-
if (!
|
|
14405
|
+
if (!fs6.existsSync(refsDir)) return [];
|
|
14343
14406
|
const entries = [];
|
|
14344
|
-
for (const file of
|
|
14407
|
+
for (const file of fs6.readdirSync(refsDir)) {
|
|
14345
14408
|
if (!file.endsWith(".md")) continue;
|
|
14346
14409
|
const filePath = path6.join(refsDir, file);
|
|
14347
|
-
const body =
|
|
14410
|
+
const body = fs6.readFileSync(filePath, "utf-8");
|
|
14348
14411
|
entries.push({
|
|
14349
14412
|
slug: file.replace(/\.md$/, ""),
|
|
14350
14413
|
description: parseDescription(body),
|
|
@@ -14388,7 +14451,7 @@ function buildReadSkillDocTool() {
|
|
|
14388
14451
|
});
|
|
14389
14452
|
}
|
|
14390
14453
|
const filePath = path6.join(skillDir, "references", `${match.slug}.md`);
|
|
14391
|
-
const content =
|
|
14454
|
+
const content = fs6.readFileSync(filePath, "utf-8");
|
|
14392
14455
|
if (content.length > MAX_DOC_CHARS) {
|
|
14393
14456
|
return textResult({
|
|
14394
14457
|
slug: match.slug,
|
|
@@ -15049,9 +15112,9 @@ function ensureBuiltinsRegistered() {
|
|
|
15049
15112
|
}
|
|
15050
15113
|
function loadAeroSystemPrompt(pkgDir) {
|
|
15051
15114
|
const skillDir = resolveAeroSkillDir(pkgDir);
|
|
15052
|
-
const skillBody =
|
|
15115
|
+
const skillBody = fs7.readFileSync(path7.join(skillDir, "SKILL.md"), "utf-8");
|
|
15053
15116
|
const soulPath = path7.join(skillDir, "soul.md");
|
|
15054
|
-
const base =
|
|
15117
|
+
const base = fs7.existsSync(soulPath) ? `${fs7.readFileSync(soulPath, "utf-8").trimEnd()}
|
|
15055
15118
|
|
|
15056
15119
|
---
|
|
15057
15120
|
|
|
@@ -15064,7 +15127,7 @@ function appendSystemPromptExtras(base, env = process.env) {
|
|
|
15064
15127
|
const filePath = env.AERO_SYSTEM_PROMPT_FILE?.trim();
|
|
15065
15128
|
if (filePath) {
|
|
15066
15129
|
try {
|
|
15067
|
-
fileBody =
|
|
15130
|
+
fileBody = fs7.readFileSync(filePath, "utf-8").trim();
|
|
15068
15131
|
} catch {
|
|
15069
15132
|
fileBody = "";
|
|
15070
15133
|
}
|
|
@@ -16088,7 +16151,8 @@ function registerMcpHttpRoutes(scope, opts) {
|
|
|
16088
16151
|
credentialScopes: scopes,
|
|
16089
16152
|
operator: request.operatorAccess === true,
|
|
16090
16153
|
tiers: segment.tiers,
|
|
16091
|
-
clientFactory: () => client
|
|
16154
|
+
clientFactory: () => client,
|
|
16155
|
+
updateAvailable: opts.getUpdateAvailable
|
|
16092
16156
|
});
|
|
16093
16157
|
} catch (error) {
|
|
16094
16158
|
if (sessionKey) revokeSessionKey(opts.db, sessionKey.id);
|
|
@@ -18141,7 +18205,7 @@ async function createServer(opts) {
|
|
|
18141
18205
|
});
|
|
18142
18206
|
};
|
|
18143
18207
|
const orphanedOpenClawDir = path8.join(os4.homedir(), ".openclaw-aero");
|
|
18144
|
-
if (
|
|
18208
|
+
if (fs8.existsSync(orphanedOpenClawDir)) {
|
|
18145
18209
|
app.log.warn(
|
|
18146
18210
|
{ path: orphanedOpenClawDir },
|
|
18147
18211
|
"OpenClaw gateway is no longer used. Remove ~/.openclaw-aero/ manually to reclaim the directory."
|
|
@@ -19051,7 +19115,7 @@ async function createServer(opts) {
|
|
|
19051
19115
|
const configPath = getConfigPath();
|
|
19052
19116
|
return {
|
|
19053
19117
|
databasePath: opts.config.database,
|
|
19054
|
-
configPath:
|
|
19118
|
+
configPath: fs8.existsSync(configPath) ? configPath : null
|
|
19055
19119
|
};
|
|
19056
19120
|
})(),
|
|
19057
19121
|
// Snapshot the bundled skill trees (version + file hashes) so the
|
|
@@ -19066,6 +19130,8 @@ async function createServer(opts) {
|
|
|
19066
19130
|
}
|
|
19067
19131
|
})(),
|
|
19068
19132
|
getAgentPluginState: opts.getAgentPluginState,
|
|
19133
|
+
// Powers the `canonry.version.current` doctor check. Non-blocking.
|
|
19134
|
+
getUpdateStatus: () => getServerUpdateStatus(),
|
|
19069
19135
|
// Local canonry serve runs on the operator's machine, where pointing a
|
|
19070
19136
|
// webhook at localhost (Discord test container, Pipedream-mock dev server,
|
|
19071
19137
|
// etc.) is a legitimate workflow. Default to allowing it for the local
|
|
@@ -19084,7 +19150,13 @@ async function createServer(opts) {
|
|
|
19084
19150
|
credentials: credentialChecker,
|
|
19085
19151
|
oauthResourceUrl: publicOrigin ? `${publicOrigin}${`${basePath ?? "/"}api/v1/mcp`.replace("//", "/")}` : void 0,
|
|
19086
19152
|
registerAuthenticatedRoutes: async (scope) => {
|
|
19087
|
-
registerMcpHttpRoutes(scope, {
|
|
19153
|
+
registerMcpHttpRoutes(scope, {
|
|
19154
|
+
selfApiUrl: opts.config.apiUrl,
|
|
19155
|
+
issuer: publicOrigin,
|
|
19156
|
+
db: opts.db,
|
|
19157
|
+
// Same non-blocking cache as /health, so hosted agents see the notice too.
|
|
19158
|
+
getUpdateAvailable: () => checkLatestVersionForServer()
|
|
19159
|
+
});
|
|
19088
19160
|
registerOAuthAdminRoutes(scope, { db: opts.db });
|
|
19089
19161
|
if (!sessionRegistry) return;
|
|
19090
19162
|
registerAgentRoutes(scope, { db: opts.db, sessionRegistry });
|
|
@@ -19627,9 +19699,9 @@ async function createServer(opts) {
|
|
|
19627
19699
|
return snapshotService.createReport(input);
|
|
19628
19700
|
}
|
|
19629
19701
|
});
|
|
19630
|
-
const dirname = path8.dirname(
|
|
19702
|
+
const dirname = path8.dirname(fileURLToPath3(import.meta.url));
|
|
19631
19703
|
const assetsDir = opts.assetsDir ?? path8.join(dirname, "..", "assets");
|
|
19632
|
-
if (
|
|
19704
|
+
if (fs8.existsSync(assetsDir)) {
|
|
19633
19705
|
const indexPath = path8.join(assetsDir, "index.html");
|
|
19634
19706
|
const injectConfig = (html, projectTabsOverride, themeOverride, renderTokenOverride) => {
|
|
19635
19707
|
const clientConfig = {};
|
|
@@ -19703,8 +19775,8 @@ async function createServer(opts) {
|
|
|
19703
19775
|
}
|
|
19704
19776
|
});
|
|
19705
19777
|
const serveIndex = (_request, reply) => {
|
|
19706
|
-
if (
|
|
19707
|
-
const html =
|
|
19778
|
+
if (fs8.existsSync(indexPath)) {
|
|
19779
|
+
const html = fs8.readFileSync(indexPath, "utf-8");
|
|
19708
19780
|
return sendSpaDocument(reply, html);
|
|
19709
19781
|
}
|
|
19710
19782
|
return reply.status(404).send({ error: "Dashboard not built" });
|
|
@@ -19735,8 +19807,8 @@ async function createServer(opts) {
|
|
|
19735
19807
|
if (basePath && !url.startsWith(basePath)) {
|
|
19736
19808
|
return reply.status(404).send({ error: "Not found", path: request.url });
|
|
19737
19809
|
}
|
|
19738
|
-
if (
|
|
19739
|
-
const html =
|
|
19810
|
+
if (fs8.existsSync(indexPath)) {
|
|
19811
|
+
const html = fs8.readFileSync(indexPath, "utf-8");
|
|
19740
19812
|
return sendSpaDocument(reply, html);
|
|
19741
19813
|
}
|
|
19742
19814
|
return reply.status(404).send({ error: "Not found" });
|
|
@@ -19900,6 +19972,8 @@ export {
|
|
|
19900
19972
|
setGoogleAuthConfig,
|
|
19901
19973
|
formatAuditFactorScore,
|
|
19902
19974
|
checkLatestVersionForCli,
|
|
19975
|
+
readCachedUpdateAvailable,
|
|
19976
|
+
formatUpdateNotice,
|
|
19903
19977
|
listAgentProviders,
|
|
19904
19978
|
coerceAgentProvider,
|
|
19905
19979
|
AERO_TOOL_PROFILES,
|
|
@@ -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-
|
|
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
|
|
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-
|
|
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-
|
|
8
|
+
} from "./chunk-AEE5DGTE.js";
|
|
9
9
|
import {
|
|
10
10
|
SKILL_MANIFEST_FILENAME,
|
|
11
11
|
SkillsClients
|
|
12
|
-
} from "./chunk-
|
|
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-
|
|
36
|
+
} from "./chunk-5ZOLPHTO.js";
|
|
35
37
|
import {
|
|
36
38
|
autoSyncSkills,
|
|
37
39
|
formatAutoSyncNotice
|
|
38
|
-
} from "./chunk-
|
|
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-
|
|
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-
|
|
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-
|
|
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"
|
|
19308
|
-
|
|
19309
|
-
|
|
19310
|
-
process.stderr.write(
|
|
19311
|
-
|
|
19312
|
-
|
|
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-
|
|
6
|
+
} from "./chunk-5ZOLPHTO.js";
|
|
7
7
|
import {
|
|
8
8
|
loadConfig
|
|
9
|
-
} from "./chunk-
|
|
10
|
-
import "./chunk-
|
|
11
|
-
import "./chunk-
|
|
9
|
+
} from "./chunk-AEE5DGTE.js";
|
|
10
|
+
import "./chunk-EBUX3C5H.js";
|
|
11
|
+
import "./chunk-MMSPU72Z.js";
|
|
12
12
|
export {
|
|
13
13
|
GoogleMarketingRuntimeError,
|
|
14
14
|
createGoogleMarketingCredentialStore,
|
package/dist/mcp.js
CHANGED
|
@@ -1,17 +1,49 @@
|
|
|
1
1
|
import {
|
|
2
2
|
autoSyncSkills
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-KJM4DRZN.js";
|
|
4
4
|
import {
|
|
5
5
|
createApiClient,
|
|
6
6
|
createCanonryMcpServer
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-AEE5DGTE.js";
|
|
8
8
|
import {
|
|
9
|
-
isReadOnlyKey
|
|
10
|
-
|
|
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
|
|
51
|
-
const
|
|
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.
|
|
3
|
+
"version": "5.1.3",
|
|
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,28 +87,28 @@
|
|
|
87
87
|
"tsup": "^8.5.1",
|
|
88
88
|
"tsx": "^4.19.0",
|
|
89
89
|
"@ainyc/canonry-web": "0.0.0",
|
|
90
|
-
"@ainyc/canonry-api-client": "0.0.0",
|
|
91
90
|
"@ainyc/canonry-api-routes": "0.0.0",
|
|
91
|
+
"@ainyc/canonry-api-client": "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-
|
|
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-
|
|
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",
|
|
103
101
|
"@ainyc/canonry-integration-google-business-profile": "0.0.0",
|
|
102
|
+
"@ainyc/canonry-integration-google-tag-manager": "0.0.0",
|
|
103
|
+
"@ainyc/canonry-integration-google-places": "0.0.0",
|
|
104
104
|
"@ainyc/canonry-integration-openai-ads": "0.0.0",
|
|
105
|
-
"@ainyc/canonry-integration-
|
|
106
|
-
"@ainyc/canonry-intelligence": "0.0.0",
|
|
105
|
+
"@ainyc/canonry-integration-google-ads": "0.0.0",
|
|
107
106
|
"@ainyc/canonry-integration-traffic": "0.0.0",
|
|
107
|
+
"@ainyc/canonry-integration-wordpress": "0.0.0",
|
|
108
108
|
"@ainyc/canonry-provider-cdp": "0.0.0",
|
|
109
|
-
"@ainyc/canonry-integration-google-tag-manager": "0.0.0",
|
|
110
|
-
"@ainyc/canonry-provider-claude": "0.0.0",
|
|
111
109
|
"@ainyc/canonry-provider-gemini": "0.0.0",
|
|
110
|
+
"@ainyc/canonry-intelligence": "0.0.0",
|
|
111
|
+
"@ainyc/canonry-provider-claude": "0.0.0",
|
|
112
112
|
"@ainyc/canonry-provider-local": "0.0.0",
|
|
113
113
|
"@ainyc/canonry-provider-openai": "0.0.0",
|
|
114
114
|
"@ainyc/canonry-provider-perplexity": "0.0.0"
|