@ainyc/canonry 5.1.1 → 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/assets/assets/{AuditHistoryPanel-9F_74jKS.js → AuditHistoryPanel-Dc_pymFg.js} +1 -1
- package/assets/assets/{BacklinksPage-DmF2rbwy.js → BacklinksPage-BkSo48YX.js} +1 -1
- package/assets/assets/{HistoryPage-DKopwAjo.js → HistoryPage-BbswsdU7.js} +1 -1
- package/assets/assets/{MeasurementPropertyPage-Dpd_DYQv.js → MeasurementPropertyPage-BbgtalkM.js} +1 -1
- package/assets/assets/{ProjectPage-b1-57SzP.js → ProjectPage-CAcRQp-T.js} +1 -1
- package/assets/assets/{RunRow-BjrSRp6X.js → RunRow-2cNW8FHz.js} +1 -1
- package/assets/assets/{RunsPage-BFrymZ7D.js → RunsPage-Cz3lJ6VJ.js} +1 -1
- package/assets/assets/{SettingsPage-DdVa6pLK.js → SettingsPage-BQwB0r9k.js} +1 -1
- package/assets/assets/{SiteHealthSection-CizmiBjA.js → SiteHealthSection-C0aJ_UhN.js} +3 -3
- package/assets/assets/{TrafficPage-N7sZgb9x.js → TrafficPage-DTic3zhO.js} +1 -1
- package/assets/assets/{TrafficSourceDetailPage-BNQmMJ5D.js → TrafficSourceDetailPage-BKfabnCw.js} +1 -1
- package/assets/assets/{extract-error-message-Bh9mRIz9.js → extract-error-message-BzB8j3fC.js} +1 -1
- package/assets/assets/{index-BnR1Tu9j.js → index-DQGEzxrN.js} +3 -3
- package/assets/assets/{react-sigma_core.esm.min-DGaYwjLZ.js → react-sigma_core.esm.min-rYv0aKKx.js} +1 -1
- package/assets/assets/{v2-overview-adapter-eqN-8OK5.js → v2-overview-adapter-CWWPfsm-.js} +1 -1
- package/assets/index.html +1 -1
- package/dist/{chunk-ZWVXUPLP.js → chunk-5ZOLPHTO.js} +286 -96
- package/dist/{chunk-MEQGGODM.js → chunk-AEE5DGTE.js} +92 -8
- package/dist/{chunk-LHHJVGH7.js → chunk-EBUX3C5H.js} +110 -15
- package/dist/{chunk-66KGNC4U.js → chunk-KJM4DRZN.js} +2 -2
- package/dist/{chunk-3DL3KV6C.js → chunk-MMSPU72Z.js} +113 -1
- package/dist/cli.js +25 -26
- package/dist/index.js +4 -4
- package/dist/{intelligence-service-7XX5ZRYE.js → intelligence-service-25NQCETC.js} +2 -2
- package/dist/mcp.js +49 -7
- package/package.json +7 -7
|
@@ -15,8 +15,9 @@ import {
|
|
|
15
15
|
loadConfig,
|
|
16
16
|
loadConfigRaw,
|
|
17
17
|
resolveOperatorApiKeyIds,
|
|
18
|
+
runWithUsageTags,
|
|
18
19
|
saveConfigPatch
|
|
19
|
-
} from "./chunk-
|
|
20
|
+
} from "./chunk-AEE5DGTE.js";
|
|
20
21
|
import {
|
|
21
22
|
CC_CACHE_DIR,
|
|
22
23
|
DUCKDB_SPEC,
|
|
@@ -167,9 +168,10 @@ import {
|
|
|
167
168
|
siteCrawlSnapshots,
|
|
168
169
|
toAlertView,
|
|
169
170
|
usageCounters
|
|
170
|
-
} from "./chunk-
|
|
171
|
+
} from "./chunk-EBUX3C5H.js";
|
|
171
172
|
import {
|
|
172
173
|
AGENT_MEMORY_VALUE_MAX_BYTES,
|
|
174
|
+
AGENT_NONE,
|
|
173
175
|
AGENT_PROVIDER_IDS,
|
|
174
176
|
AI_ENGINE_DOMAINS,
|
|
175
177
|
AI_ENGINE_SELF_DOMAINS,
|
|
@@ -177,6 +179,7 @@ import {
|
|
|
177
179
|
AdsCampaignBiddingTypes,
|
|
178
180
|
AgentProviderIds,
|
|
179
181
|
BacklinkSources,
|
|
182
|
+
CANONRY_NPM_PACKAGE_URL,
|
|
180
183
|
CITED_URL_CAPTURE_VERSION,
|
|
181
184
|
CcReleaseSyncStatuses,
|
|
182
185
|
DEFAULT_VIEWER_RESEARCH_DAILY_RUN_LIMIT,
|
|
@@ -237,6 +240,7 @@ import {
|
|
|
237
240
|
classifyProviderErrorMessages,
|
|
238
241
|
classifyTemplateLinkEdge,
|
|
239
242
|
cloudflareQueueNameSchema,
|
|
243
|
+
compareSemver,
|
|
240
244
|
computeBacklinkSummaryMetrics,
|
|
241
245
|
contentActionLabel,
|
|
242
246
|
contentBriefDtoSchema,
|
|
@@ -247,6 +251,7 @@ import {
|
|
|
247
251
|
deriveIndexCoverage,
|
|
248
252
|
deriveSiteHealthState,
|
|
249
253
|
describeError,
|
|
254
|
+
detectAgentRuntime,
|
|
250
255
|
determineAnswerMentioned,
|
|
251
256
|
dollarsToMicros,
|
|
252
257
|
effectiveBrandNames,
|
|
@@ -276,13 +281,16 @@ import {
|
|
|
276
281
|
isAgentProviderId,
|
|
277
282
|
isBrowserProvider,
|
|
278
283
|
isGhostTelemetryEvent,
|
|
284
|
+
isInstallMethod,
|
|
279
285
|
isReadOnlyKey,
|
|
280
286
|
isRetryableHttpError,
|
|
281
287
|
isSelfLink,
|
|
288
|
+
isStrictSemver,
|
|
282
289
|
isVertexGroundingRedirect,
|
|
283
290
|
mapWithConcurrency,
|
|
284
291
|
maskTelemetryAnonymousId,
|
|
285
292
|
nextScheduleUpdatedAt,
|
|
293
|
+
normalizeAgentSlug,
|
|
286
294
|
normalizeIdTokens,
|
|
287
295
|
normalizeMeasurementExecutionQueryText,
|
|
288
296
|
normalizeServedModel,
|
|
@@ -309,10 +317,22 @@ import {
|
|
|
309
317
|
templateLinkUbiquityAvailable,
|
|
310
318
|
textContainsBrandAlias,
|
|
311
319
|
textContainsDomain,
|
|
320
|
+
updateCheckEnvOptOut,
|
|
321
|
+
upgradeCaveatFor,
|
|
322
|
+
upgradeCommandFor,
|
|
323
|
+
usageSurfaceSchema,
|
|
312
324
|
validationError,
|
|
313
325
|
winnabilityClassLabel,
|
|
314
326
|
withRetry
|
|
315
|
-
} from "./chunk-
|
|
327
|
+
} from "./chunk-MMSPU72Z.js";
|
|
328
|
+
|
|
329
|
+
// src/runtime-context.ts
|
|
330
|
+
function cliRuntimeContext(env = process.env, stdio = process) {
|
|
331
|
+
return {
|
|
332
|
+
agent: detectAgentRuntime(env),
|
|
333
|
+
interactive: Boolean(stdio.stdin.isTTY && stdio.stdout.isTTY)
|
|
334
|
+
};
|
|
335
|
+
}
|
|
316
336
|
|
|
317
337
|
// src/telemetry.ts
|
|
318
338
|
import crypto from "crypto";
|
|
@@ -343,7 +363,8 @@ function trackCliCommandFinished(input) {
|
|
|
343
363
|
command: input.command,
|
|
344
364
|
success: input.success,
|
|
345
365
|
duration_bucket: bucketCliCommandDuration(input.durationMs),
|
|
346
|
-
...input.setupState ? { setup_state: input.setupState } : {}
|
|
366
|
+
...input.setupState ? { setup_state: input.setupState } : {},
|
|
367
|
+
...cliRuntimeContext()
|
|
347
368
|
},
|
|
348
369
|
input.errorCode ? { errorCode: input.errorCode } : void 0
|
|
349
370
|
);
|
|
@@ -418,7 +439,8 @@ function getOrCreateAnonymousId() {
|
|
|
418
439
|
try {
|
|
419
440
|
const config = loadConfig();
|
|
420
441
|
if (config.anonymousId) return config.anonymousId;
|
|
421
|
-
const
|
|
442
|
+
const carried = preConfigAnonymousId?.configPath === getConfigPath() ? preConfigAnonymousId.id : void 0;
|
|
443
|
+
const id = carried ?? crypto.randomUUID();
|
|
422
444
|
config.anonymousId = id;
|
|
423
445
|
try {
|
|
424
446
|
saveConfigPatch(config);
|
|
@@ -430,8 +452,11 @@ function getOrCreateAnonymousId() {
|
|
|
430
452
|
return getDeterministicAnonymousId();
|
|
431
453
|
}
|
|
432
454
|
}
|
|
433
|
-
|
|
455
|
+
const fallback = getDeterministicAnonymousId();
|
|
456
|
+
if (fallback) preConfigAnonymousId = { configPath: getConfigPath(), id: fallback };
|
|
457
|
+
return fallback;
|
|
434
458
|
}
|
|
459
|
+
var preConfigAnonymousId;
|
|
435
460
|
function readEnvAnonymousId() {
|
|
436
461
|
const raw = process.env[ANON_ID_ENV_VAR]?.trim();
|
|
437
462
|
if (!raw) return void 0;
|
|
@@ -506,10 +531,19 @@ function detectAndTrackUpgrade() {
|
|
|
506
531
|
trackEvent("cli.upgraded", { fromVersion: lastSeen, toVersion: VERSION });
|
|
507
532
|
}
|
|
508
533
|
function trackEvent(event, properties, options) {
|
|
509
|
-
|
|
510
|
-
|
|
534
|
+
void deliverEvent(event, properties, options);
|
|
535
|
+
}
|
|
536
|
+
function setTelemetryPreference(enabled, method) {
|
|
537
|
+
loadConfig();
|
|
538
|
+
const announce = !enabled && isTelemetryEnabled();
|
|
539
|
+
saveConfigPatch({ telemetry: enabled });
|
|
540
|
+
return announce ? deliverEvent("telemetry.disabled", { method }, void 0, { preferenceChecked: true }) : Promise.resolve();
|
|
541
|
+
}
|
|
542
|
+
function deliverEvent(event, properties, options, delivery = {}) {
|
|
543
|
+
if (!delivery.preferenceChecked && !isTelemetryEnabled()) return Promise.resolve();
|
|
544
|
+
if (shouldDropTelemetryEvent(event, properties)) return Promise.resolve();
|
|
511
545
|
const anonymousId = getOrCreateAnonymousId();
|
|
512
|
-
if (!anonymousId) return;
|
|
546
|
+
if (!anonymousId) return Promise.resolve();
|
|
513
547
|
const payload = {
|
|
514
548
|
eventId: options?.eventId ?? crypto.randomUUID(),
|
|
515
549
|
anonymousId,
|
|
@@ -529,60 +563,59 @@ function trackEvent(event, properties, options) {
|
|
|
529
563
|
const timeout = setTimeout(() => controller.abort(), TIMEOUT_MS);
|
|
530
564
|
timeout.unref();
|
|
531
565
|
try {
|
|
532
|
-
|
|
566
|
+
return fetch(TELEMETRY_ENDPOINT, {
|
|
533
567
|
method: "POST",
|
|
534
568
|
headers: { "Content-Type": "application/json" },
|
|
535
569
|
body: JSON.stringify(payload),
|
|
536
570
|
signal: controller.signal
|
|
537
|
-
}).
|
|
538
|
-
}).finally(() => clearTimeout(timeout));
|
|
571
|
+
}).then(() => void 0, () => void 0).finally(() => clearTimeout(timeout));
|
|
539
572
|
} catch {
|
|
540
573
|
clearTimeout(timeout);
|
|
574
|
+
return Promise.resolve();
|
|
541
575
|
}
|
|
542
576
|
}
|
|
543
577
|
|
|
544
578
|
// src/update-check.ts
|
|
579
|
+
import fs from "fs";
|
|
545
580
|
import { createRequire as createRequire2 } from "module";
|
|
581
|
+
import { fileURLToPath } from "url";
|
|
546
582
|
var _require2 = createRequire2(import.meta.url);
|
|
547
583
|
var { version: PKG_VERSION } = _require2("../package.json");
|
|
548
584
|
var PKG_NAME = "@canonry/canonry";
|
|
549
585
|
var NPM_DIST_TAGS_URL = `https://registry.npmjs.org/-/package/${PKG_NAME}/dist-tags`;
|
|
550
|
-
var NPM_PACKAGE_URL =
|
|
586
|
+
var NPM_PACKAGE_URL = CANONRY_NPM_PACKAGE_URL;
|
|
551
587
|
var FETCH_TIMEOUT_MS = 1500;
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
if (
|
|
556
|
-
|
|
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);
|
|
557
593
|
try {
|
|
558
|
-
|
|
559
|
-
return raw?.updateCheck !== false;
|
|
594
|
+
modulePath = fs.realpathSync(modulePath);
|
|
560
595
|
} catch {
|
|
561
|
-
return true;
|
|
562
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";
|
|
563
601
|
}
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
if (!pa || !pb) return 0;
|
|
581
|
-
for (let i = 0; i < 3; i++) {
|
|
582
|
-
if (pa[i] > pb[i]) return 1;
|
|
583
|
-
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;
|
|
584
618
|
}
|
|
585
|
-
return 0;
|
|
586
619
|
}
|
|
587
620
|
async function fetchLatestVersion(opts) {
|
|
588
621
|
const controller = new AbortController();
|
|
@@ -595,7 +628,7 @@ async function fetchLatestVersion(opts) {
|
|
|
595
628
|
});
|
|
596
629
|
if (!res.ok) return null;
|
|
597
630
|
const data = await res.json();
|
|
598
|
-
if (typeof data.latest !== "string") return null;
|
|
631
|
+
if (typeof data.latest !== "string" || !isStrictSemver(data.latest)) return null;
|
|
599
632
|
return data.latest;
|
|
600
633
|
} catch {
|
|
601
634
|
return null;
|
|
@@ -603,13 +636,15 @@ async function fetchLatestVersion(opts) {
|
|
|
603
636
|
clearTimeout(timeout);
|
|
604
637
|
}
|
|
605
638
|
}
|
|
606
|
-
function buildUpdateAvailable(current, latest) {
|
|
639
|
+
function buildUpdateAvailable(current, latest, installMethod = currentInstallMethod()) {
|
|
640
|
+
if (!isStrictSemver(latest)) return null;
|
|
607
641
|
if (compareSemver(latest, current) <= 0) return null;
|
|
608
642
|
return {
|
|
609
643
|
current,
|
|
610
644
|
latest,
|
|
611
645
|
url: NPM_PACKAGE_URL,
|
|
612
|
-
upgradeCommand:
|
|
646
|
+
upgradeCommand: upgradeCommandFor(installMethod),
|
|
647
|
+
installMethod
|
|
613
648
|
};
|
|
614
649
|
}
|
|
615
650
|
async function checkLatestVersionForCli(opts) {
|
|
@@ -647,6 +682,45 @@ async function checkLatestVersionForCli(opts) {
|
|
|
647
682
|
}
|
|
648
683
|
return buildUpdateAvailable(PKG_VERSION, latest);
|
|
649
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
|
+
}
|
|
650
724
|
var memoryCache = null;
|
|
651
725
|
var inFlight = null;
|
|
652
726
|
function startBackgroundRefresh(getNow) {
|
|
@@ -673,6 +747,22 @@ function checkLatestVersionForServer(opts) {
|
|
|
673
747
|
if (!memoryCache || !memoryCache.latest) return null;
|
|
674
748
|
return buildUpdateAvailable(PKG_VERSION, memoryCache.latest);
|
|
675
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
|
+
}
|
|
676
766
|
|
|
677
767
|
// src/google-marketing-runtime.ts
|
|
678
768
|
import crypto3 from "crypto";
|
|
@@ -3443,9 +3533,9 @@ function createGoogleMarketingRuntime(options) {
|
|
|
3443
3533
|
// src/server.ts
|
|
3444
3534
|
import { createRequire as createRequire4 } from "module";
|
|
3445
3535
|
import crypto27 from "crypto";
|
|
3446
|
-
import
|
|
3536
|
+
import fs8 from "fs";
|
|
3447
3537
|
import path8 from "path";
|
|
3448
|
-
import { fileURLToPath as
|
|
3538
|
+
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
3449
3539
|
import { and as and20, eq as eq26 } from "drizzle-orm";
|
|
3450
3540
|
import Fastify from "fastify";
|
|
3451
3541
|
import os4 from "os";
|
|
@@ -5366,12 +5456,12 @@ function sleep2(ms) {
|
|
|
5366
5456
|
}
|
|
5367
5457
|
|
|
5368
5458
|
// ../provider-cdp/src/screenshot.ts
|
|
5369
|
-
import
|
|
5459
|
+
import fs2 from "fs";
|
|
5370
5460
|
import path from "path";
|
|
5371
5461
|
async function captureElementScreenshot(client, selector, outputPath) {
|
|
5372
5462
|
const dir = path.dirname(outputPath);
|
|
5373
|
-
if (!
|
|
5374
|
-
|
|
5463
|
+
if (!fs2.existsSync(dir)) {
|
|
5464
|
+
fs2.mkdirSync(dir, { recursive: true });
|
|
5375
5465
|
}
|
|
5376
5466
|
let clip;
|
|
5377
5467
|
try {
|
|
@@ -5405,7 +5495,7 @@ async function captureElementScreenshot(client, selector, outputPath) {
|
|
|
5405
5495
|
}
|
|
5406
5496
|
const { data } = await client.Page.captureScreenshot(screenshotParams);
|
|
5407
5497
|
const buffer = Buffer.from(data, "base64");
|
|
5408
|
-
|
|
5498
|
+
fs2.writeFileSync(outputPath, buffer);
|
|
5409
5499
|
return outputPath;
|
|
5410
5500
|
}
|
|
5411
5501
|
|
|
@@ -6280,13 +6370,93 @@ function removeWordpressConnection(config, projectName) {
|
|
|
6280
6370
|
return true;
|
|
6281
6371
|
}
|
|
6282
6372
|
|
|
6373
|
+
// src/usage-telemetry.ts
|
|
6374
|
+
var API_REQUEST_BUCKET_CAPACITY = 20;
|
|
6375
|
+
var API_REQUEST_REFILL_PER_MS = 1 / 1e4;
|
|
6376
|
+
var MAX_TRACKED_MCP_SESSIONS = 2e3;
|
|
6377
|
+
var MAX_ROUTE_LENGTH = 200;
|
|
6378
|
+
var SKIPPED_ROUTE_PATTERN = /(?:^|\/)(?:health|openapi\.json|telemetry)(?:\/|$)/;
|
|
6379
|
+
var MCP_CALL_ID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
6380
|
+
var MCP_TOOL_PATTERN = /^[a-z][a-z0-9_]{0,79}$/;
|
|
6381
|
+
function classifyUsageSurface(info) {
|
|
6382
|
+
const labelled = usageSurfaceSchema.safeParse(info.usageLabels.surface);
|
|
6383
|
+
if (labelled.success) return labelled.data;
|
|
6384
|
+
const ua = info.userAgent ?? "";
|
|
6385
|
+
if (ua.startsWith("canonry-cli")) return "cli";
|
|
6386
|
+
if (ua.startsWith("canonry-mcp/")) return "mcp-http";
|
|
6387
|
+
if (ua === "canonry-mcp") return "mcp-stdio";
|
|
6388
|
+
if (ua.startsWith("Mozilla/")) return "dashboard";
|
|
6389
|
+
return "api";
|
|
6390
|
+
}
|
|
6391
|
+
function statusClass(statusCode) {
|
|
6392
|
+
if (statusCode >= 500) return "5xx";
|
|
6393
|
+
if (statusCode >= 400) return "4xx";
|
|
6394
|
+
if (statusCode >= 300) return "3xx";
|
|
6395
|
+
return "2xx";
|
|
6396
|
+
}
|
|
6397
|
+
function createApiUsageTelemetry(options = {}) {
|
|
6398
|
+
const emit = options.emit ?? trackEvent;
|
|
6399
|
+
const now = options.now ?? Date.now;
|
|
6400
|
+
let tokens = API_REQUEST_BUCKET_CAPACITY;
|
|
6401
|
+
let refilledAt = now();
|
|
6402
|
+
let droppedBefore = 0;
|
|
6403
|
+
const seenSessions = /* @__PURE__ */ new Set();
|
|
6404
|
+
const takeToken = () => {
|
|
6405
|
+
const at = now();
|
|
6406
|
+
tokens = Math.min(API_REQUEST_BUCKET_CAPACITY, tokens + (at - refilledAt) * API_REQUEST_REFILL_PER_MS);
|
|
6407
|
+
refilledAt = at;
|
|
6408
|
+
if (tokens < 1) return false;
|
|
6409
|
+
tokens -= 1;
|
|
6410
|
+
return true;
|
|
6411
|
+
};
|
|
6412
|
+
return (info) => {
|
|
6413
|
+
if (info.method === "OPTIONS" || info.method === "HEAD") return;
|
|
6414
|
+
if (SKIPPED_ROUTE_PATTERN.test(info.route)) return;
|
|
6415
|
+
const surface = classifyUsageSurface(info);
|
|
6416
|
+
if (surface === "cli" || surface === "dashboard") return;
|
|
6417
|
+
const labels = info.usageLabels;
|
|
6418
|
+
const mcpClient = normalizeAgentSlug(labels.mcpClient);
|
|
6419
|
+
const detected = normalizeAgentSlug(labels.agent);
|
|
6420
|
+
const agent = detected && detected !== AGENT_NONE ? detected : mcpClient ?? detected ?? AGENT_NONE;
|
|
6421
|
+
const mcpTool = labels.mcpTool && MCP_TOOL_PATTERN.test(labels.mcpTool) ? labels.mcpTool : void 0;
|
|
6422
|
+
const mcpCallId = labels.mcpCall && MCP_CALL_ID_PATTERN.test(labels.mcpCall) ? labels.mcpCall.toLowerCase() : void 0;
|
|
6423
|
+
const isMcp = surface === "mcp-stdio" || surface === "mcp-http";
|
|
6424
|
+
if (isMcp && mcpTool && info.actorSession && !seenSessions.has(info.actorSession)) {
|
|
6425
|
+
if (seenSessions.size >= MAX_TRACKED_MCP_SESSIONS) {
|
|
6426
|
+
const oldest = seenSessions.values().next().value;
|
|
6427
|
+
if (oldest !== void 0) seenSessions.delete(oldest);
|
|
6428
|
+
}
|
|
6429
|
+
seenSessions.add(info.actorSession);
|
|
6430
|
+
emit("mcp.session.started", { surface, agent, ...mcpClient ? { mcpClient } : {} }, { source: "cli-server" });
|
|
6431
|
+
}
|
|
6432
|
+
if (!takeToken()) {
|
|
6433
|
+
droppedBefore += 1;
|
|
6434
|
+
return;
|
|
6435
|
+
}
|
|
6436
|
+
const properties = {
|
|
6437
|
+
surface,
|
|
6438
|
+
agent,
|
|
6439
|
+
method: info.method,
|
|
6440
|
+
route: info.route.slice(0, MAX_ROUTE_LENGTH),
|
|
6441
|
+
statusClass: statusClass(info.statusCode),
|
|
6442
|
+
durationBucket: bucketCliCommandDuration(info.durationMs),
|
|
6443
|
+
...mcpClient ? { mcpClient } : {},
|
|
6444
|
+
...mcpTool ? { mcpTool } : {},
|
|
6445
|
+
...mcpCallId ? { mcpCallId } : {},
|
|
6446
|
+
...droppedBefore > 0 ? { droppedBefore } : {}
|
|
6447
|
+
};
|
|
6448
|
+
droppedBefore = 0;
|
|
6449
|
+
emit("api.request", properties, { source: "cli-server" });
|
|
6450
|
+
};
|
|
6451
|
+
}
|
|
6452
|
+
|
|
6283
6453
|
// src/instance-identity.ts
|
|
6284
6454
|
function nonBlank(value) {
|
|
6285
6455
|
const trimmed = value?.trim();
|
|
6286
6456
|
return trimmed ? trimmed : void 0;
|
|
6287
6457
|
}
|
|
6288
6458
|
function resolveBuildCommit(env = process.env) {
|
|
6289
|
-
const embedded = true ? "
|
|
6459
|
+
const embedded = true ? "1f10658e1c5f2b615dfbee110d3f8cf68dbfbda9" : void 0;
|
|
6290
6460
|
return nonBlank(embedded) ?? nonBlank(env.CANONRY_COMMIT);
|
|
6291
6461
|
}
|
|
6292
6462
|
function resolveInstanceIdentity(env = process.env) {
|
|
@@ -6298,7 +6468,7 @@ function resolveInstanceIdentity(env = process.env) {
|
|
|
6298
6468
|
|
|
6299
6469
|
// src/job-runner.ts
|
|
6300
6470
|
import crypto6 from "crypto";
|
|
6301
|
-
import
|
|
6471
|
+
import fs3 from "fs";
|
|
6302
6472
|
import path3 from "path";
|
|
6303
6473
|
import os3 from "os";
|
|
6304
6474
|
import { and as and2, eq as eq2, inArray, ne, sql as sql2 } from "drizzle-orm";
|
|
@@ -6866,12 +7036,12 @@ var JobRunner = class {
|
|
|
6866
7036
|
allBrandNames
|
|
6867
7037
|
);
|
|
6868
7038
|
let screenshotRelPath = null;
|
|
6869
|
-
if (raw.screenshotPath &&
|
|
7039
|
+
if (raw.screenshotPath && fs3.existsSync(raw.screenshotPath)) {
|
|
6870
7040
|
const snapshotId = crypto6.randomUUID();
|
|
6871
7041
|
const screenshotDir = path3.join(os3.homedir(), ".canonry", "screenshots", runId);
|
|
6872
|
-
if (!
|
|
7042
|
+
if (!fs3.existsSync(screenshotDir)) fs3.mkdirSync(screenshotDir, { recursive: true });
|
|
6873
7043
|
const destPath = path3.join(screenshotDir, `${snapshotId}.png`);
|
|
6874
|
-
|
|
7044
|
+
fs3.renameSync(raw.screenshotPath, destPath);
|
|
6875
7045
|
screenshotRelPath = `${runId}/${snapshotId}.png`;
|
|
6876
7046
|
this.db.insert(querySnapshots).values({
|
|
6877
7047
|
id: snapshotId,
|
|
@@ -7024,11 +7194,11 @@ var JobRunner = class {
|
|
|
7024
7194
|
);
|
|
7025
7195
|
const snapshotId = crypto6.randomUUID();
|
|
7026
7196
|
let screenshotRelPath = null;
|
|
7027
|
-
if (raw.screenshotPath &&
|
|
7197
|
+
if (raw.screenshotPath && fs3.existsSync(raw.screenshotPath)) {
|
|
7028
7198
|
const screenshotDir = path3.join(os3.homedir(), ".canonry", "screenshots", runId);
|
|
7029
|
-
if (!
|
|
7199
|
+
if (!fs3.existsSync(screenshotDir)) fs3.mkdirSync(screenshotDir, { recursive: true });
|
|
7030
7200
|
const destPath = path3.join(screenshotDir, `${snapshotId}.png`);
|
|
7031
|
-
|
|
7201
|
+
fs3.renameSync(raw.screenshotPath, destPath);
|
|
7032
7202
|
screenshotRelPath = `${runId}/${snapshotId}.png`;
|
|
7033
7203
|
}
|
|
7034
7204
|
this.db.insert(querySnapshots).values({
|
|
@@ -9588,7 +9758,7 @@ function computeSummary(rows) {
|
|
|
9588
9758
|
|
|
9589
9759
|
// src/backlink-extract.ts
|
|
9590
9760
|
import crypto16 from "crypto";
|
|
9591
|
-
import
|
|
9761
|
+
import fs4 from "fs";
|
|
9592
9762
|
import { and as and9, desc as desc5, eq as eq11 } from "drizzle-orm";
|
|
9593
9763
|
var log11 = createLogger("BacklinkExtract");
|
|
9594
9764
|
function defaultDeps3() {
|
|
@@ -9617,7 +9787,7 @@ async function executeBacklinkExtract(db, runId, projectId, opts = {}) {
|
|
|
9617
9787
|
if (!sync.vertexPath || !sync.edgesPath) {
|
|
9618
9788
|
throw new Error(`Release ${sync.release} is missing cached file paths`);
|
|
9619
9789
|
}
|
|
9620
|
-
if (!
|
|
9790
|
+
if (!fs4.existsSync(sync.vertexPath) || !fs4.existsSync(sync.edgesPath)) {
|
|
9621
9791
|
throw new Error(
|
|
9622
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.`
|
|
9623
9793
|
);
|
|
@@ -12212,7 +12382,7 @@ function readStoredGroundingSources(rawResponse) {
|
|
|
12212
12382
|
return result;
|
|
12213
12383
|
}
|
|
12214
12384
|
async function backfillInsightsCommand(project, opts) {
|
|
12215
|
-
const { IntelligenceService: IntelligenceService2 } = await import("./intelligence-service-
|
|
12385
|
+
const { IntelligenceService: IntelligenceService2 } = await import("./intelligence-service-25NQCETC.js");
|
|
12216
12386
|
const config = loadConfig();
|
|
12217
12387
|
const db = createClient(config.database);
|
|
12218
12388
|
migrate(db);
|
|
@@ -13947,7 +14117,7 @@ import crypto25 from "crypto";
|
|
|
13947
14117
|
import { eq as eq21 } from "drizzle-orm";
|
|
13948
14118
|
|
|
13949
14119
|
// src/agent/session.ts
|
|
13950
|
-
import
|
|
14120
|
+
import fs7 from "fs";
|
|
13951
14121
|
import path7 from "path";
|
|
13952
14122
|
import { Agent } from "@mariozechner/pi-agent-core";
|
|
13953
14123
|
import { registerBuiltInApiProviders } from "@mariozechner/pi-ai";
|
|
@@ -14191,25 +14361,25 @@ function buildAgentProvidersResponse(config) {
|
|
|
14191
14361
|
}
|
|
14192
14362
|
|
|
14193
14363
|
// src/agent/skill-paths.ts
|
|
14194
|
-
import
|
|
14364
|
+
import fs5 from "fs";
|
|
14195
14365
|
import path5 from "path";
|
|
14196
|
-
import { fileURLToPath } from "url";
|
|
14366
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
14197
14367
|
function resolveAeroSkillDir(pkgDir) {
|
|
14198
|
-
const here = pkgDir ?? path5.dirname(
|
|
14368
|
+
const here = pkgDir ?? path5.dirname(fileURLToPath2(import.meta.url));
|
|
14199
14369
|
const candidates = [
|
|
14200
14370
|
path5.join(here, "../assets/agent-workspace/skills/aero"),
|
|
14201
14371
|
path5.join(here, "../../assets/agent-workspace/skills/aero"),
|
|
14202
14372
|
path5.join(here, "../../../../skills/aero")
|
|
14203
14373
|
];
|
|
14204
14374
|
for (const candidate of candidates) {
|
|
14205
|
-
if (
|
|
14375
|
+
if (fs5.existsSync(path5.join(candidate, "SKILL.md"))) return candidate;
|
|
14206
14376
|
}
|
|
14207
14377
|
throw new Error(`Aero skill not found. Searched:
|
|
14208
14378
|
${candidates.join("\n ")}`);
|
|
14209
14379
|
}
|
|
14210
14380
|
|
|
14211
14381
|
// src/agent/skill-tools.ts
|
|
14212
|
-
import
|
|
14382
|
+
import fs6 from "fs";
|
|
14213
14383
|
import path6 from "path";
|
|
14214
14384
|
import { Type } from "@sinclair/typebox";
|
|
14215
14385
|
var MAX_DOC_CHARS = 2e4;
|
|
@@ -14232,12 +14402,12 @@ function parseDescription(body) {
|
|
|
14232
14402
|
}
|
|
14233
14403
|
function scanSkillDocs(skillDir) {
|
|
14234
14404
|
const refsDir = path6.join(skillDir ?? resolveAeroSkillDir(), "references");
|
|
14235
|
-
if (!
|
|
14405
|
+
if (!fs6.existsSync(refsDir)) return [];
|
|
14236
14406
|
const entries = [];
|
|
14237
|
-
for (const file of
|
|
14407
|
+
for (const file of fs6.readdirSync(refsDir)) {
|
|
14238
14408
|
if (!file.endsWith(".md")) continue;
|
|
14239
14409
|
const filePath = path6.join(refsDir, file);
|
|
14240
|
-
const body =
|
|
14410
|
+
const body = fs6.readFileSync(filePath, "utf-8");
|
|
14241
14411
|
entries.push({
|
|
14242
14412
|
slug: file.replace(/\.md$/, ""),
|
|
14243
14413
|
description: parseDescription(body),
|
|
@@ -14281,7 +14451,7 @@ function buildReadSkillDocTool() {
|
|
|
14281
14451
|
});
|
|
14282
14452
|
}
|
|
14283
14453
|
const filePath = path6.join(skillDir, "references", `${match.slug}.md`);
|
|
14284
|
-
const content =
|
|
14454
|
+
const content = fs6.readFileSync(filePath, "utf-8");
|
|
14285
14455
|
if (content.length > MAX_DOC_CHARS) {
|
|
14286
14456
|
return textResult({
|
|
14287
14457
|
slug: match.slug,
|
|
@@ -14306,6 +14476,7 @@ import { Type as Type3 } from "@sinclair/typebox";
|
|
|
14306
14476
|
|
|
14307
14477
|
// src/agent/mcp-to-agent-tool.ts
|
|
14308
14478
|
import { Type as Type2 } from "@sinclair/typebox";
|
|
14479
|
+
import { randomUUID } from "crypto";
|
|
14309
14480
|
var MAX_TOOL_RESULT_CHARS = 2e4;
|
|
14310
14481
|
var TRUNCATION_NOTE = "... (truncated \u2014 result too large)";
|
|
14311
14482
|
function serializeResult(value) {
|
|
@@ -14455,7 +14626,7 @@ function mcpToAgentTool(tool, ctx) {
|
|
|
14455
14626
|
const parameters = Type2.Unsafe(visibleSchema);
|
|
14456
14627
|
const execute = async (_toolCallId, params) => {
|
|
14457
14628
|
const handlerInput = hadProject ? { ...params, project: ctx.projectName } : params;
|
|
14458
|
-
const result = await tool.handler(ctx.client, handlerInput);
|
|
14629
|
+
const result = await runWithUsageTags(ctx.client, { mcpTool: tool.name, mcpCall: randomUUID() }, () => tool.handler(ctx.client, handlerInput));
|
|
14459
14630
|
return textResult2(result);
|
|
14460
14631
|
};
|
|
14461
14632
|
return {
|
|
@@ -14941,9 +15112,9 @@ function ensureBuiltinsRegistered() {
|
|
|
14941
15112
|
}
|
|
14942
15113
|
function loadAeroSystemPrompt(pkgDir) {
|
|
14943
15114
|
const skillDir = resolveAeroSkillDir(pkgDir);
|
|
14944
|
-
const skillBody =
|
|
15115
|
+
const skillBody = fs7.readFileSync(path7.join(skillDir, "SKILL.md"), "utf-8");
|
|
14945
15116
|
const soulPath = path7.join(skillDir, "soul.md");
|
|
14946
|
-
const base =
|
|
15117
|
+
const base = fs7.existsSync(soulPath) ? `${fs7.readFileSync(soulPath, "utf-8").trimEnd()}
|
|
14947
15118
|
|
|
14948
15119
|
---
|
|
14949
15120
|
|
|
@@ -14956,7 +15127,7 @@ function appendSystemPromptExtras(base, env = process.env) {
|
|
|
14956
15127
|
const filePath = env.AERO_SYSTEM_PROMPT_FILE?.trim();
|
|
14957
15128
|
if (filePath) {
|
|
14958
15129
|
try {
|
|
14959
|
-
fileBody =
|
|
15130
|
+
fileBody = fs7.readFileSync(filePath, "utf-8").trim();
|
|
14960
15131
|
} catch {
|
|
14961
15132
|
fileBody = "";
|
|
14962
15133
|
}
|
|
@@ -15969,6 +16140,7 @@ function registerMcpHttpRoutes(scope, opts) {
|
|
|
15969
16140
|
const client = new ApiClient(opts.selfApiUrl, sessionKey?.raw ?? bearer, {
|
|
15970
16141
|
skipProbe: true,
|
|
15971
16142
|
clientName: `canonry-mcp/${PACKAGE_VERSION}`,
|
|
16143
|
+
surface: "mcp-http",
|
|
15972
16144
|
// Correlation only: this is never accepted as caller identity or authority.
|
|
15973
16145
|
actorSession: crypto26.randomUUID()
|
|
15974
16146
|
});
|
|
@@ -15979,7 +16151,8 @@ function registerMcpHttpRoutes(scope, opts) {
|
|
|
15979
16151
|
credentialScopes: scopes,
|
|
15980
16152
|
operator: request.operatorAccess === true,
|
|
15981
16153
|
tiers: segment.tiers,
|
|
15982
|
-
clientFactory: () => client
|
|
16154
|
+
clientFactory: () => client,
|
|
16155
|
+
updateAvailable: opts.getUpdateAvailable
|
|
15983
16156
|
});
|
|
15984
16157
|
} catch (error) {
|
|
15985
16158
|
if (sessionKey) revokeSessionKey(opts.db, sessionKey.id);
|
|
@@ -17723,7 +17896,7 @@ function cloneGtmConfig(config) {
|
|
|
17723
17896
|
function createGoogleMarketingConfigCredentialStore(input) {
|
|
17724
17897
|
const persistConfigPatch = input.saveConfigPatch ?? saveConfigPatch;
|
|
17725
17898
|
const env = input.env ?? process.env;
|
|
17726
|
-
const
|
|
17899
|
+
const randomUUID2 = input.randomUUID ?? crypto27.randomUUID;
|
|
17727
17900
|
const snapshot = () => ({
|
|
17728
17901
|
googleAds: cloneGoogleAdsConfig(input.config.googleAds),
|
|
17729
17902
|
gtm: cloneGtmConfig(input.config.gtm)
|
|
@@ -17771,7 +17944,7 @@ function createGoogleMarketingConfigCredentialStore(input) {
|
|
|
17771
17944
|
upsertGoogleAdsConnection(input.config, {
|
|
17772
17945
|
projectId: project.id,
|
|
17773
17946
|
projectName: project.name,
|
|
17774
|
-
credentialGeneration:
|
|
17947
|
+
credentialGeneration: randomUUID2(),
|
|
17775
17948
|
...credential.accessToken ? { accessToken: credential.accessToken } : {},
|
|
17776
17949
|
refreshToken: credential.refreshToken ?? null,
|
|
17777
17950
|
tokenExpiresAt: credential.expiresAt ?? null,
|
|
@@ -17783,7 +17956,7 @@ function createGoogleMarketingConfigCredentialStore(input) {
|
|
|
17783
17956
|
upsertGtmConnection(input.config, {
|
|
17784
17957
|
projectId: project.id,
|
|
17785
17958
|
projectName: project.name,
|
|
17786
|
-
credentialGeneration:
|
|
17959
|
+
credentialGeneration: randomUUID2(),
|
|
17787
17960
|
...credential.accessToken ? { accessToken: credential.accessToken } : {},
|
|
17788
17961
|
refreshToken: credential.refreshToken ?? null,
|
|
17789
17962
|
tokenExpiresAt: credential.expiresAt ?? null,
|
|
@@ -17896,6 +18069,10 @@ async function createServer(opts) {
|
|
|
17896
18069
|
const notifier = new Notifier(opts.db, serverUrl);
|
|
17897
18070
|
const intelligenceService = new IntelligenceService(opts.db);
|
|
17898
18071
|
const aeroClient = new ApiClient(opts.config.apiUrl, opts.config.apiKey, {
|
|
18072
|
+
skipProbe: true,
|
|
18073
|
+
surface: "aero"
|
|
18074
|
+
});
|
|
18075
|
+
const schedulerClient = new ApiClient(opts.config.apiUrl, opts.config.apiKey, {
|
|
17899
18076
|
skipProbe: true
|
|
17900
18077
|
});
|
|
17901
18078
|
const agentEnabled = resolveAgentEnabled(process.env, opts.config);
|
|
@@ -18028,7 +18205,7 @@ async function createServer(opts) {
|
|
|
18028
18205
|
});
|
|
18029
18206
|
};
|
|
18030
18207
|
const orphanedOpenClawDir = path8.join(os4.homedir(), ".openclaw-aero");
|
|
18031
|
-
if (
|
|
18208
|
+
if (fs8.existsSync(orphanedOpenClawDir)) {
|
|
18032
18209
|
app.log.warn(
|
|
18033
18210
|
{ path: orphanedOpenClawDir },
|
|
18034
18211
|
"OpenClaw gateway is no longer used. Remove ~/.openclaw-aero/ manually to reclaim the directory."
|
|
@@ -18336,7 +18513,7 @@ async function createServer(opts) {
|
|
|
18336
18513
|
getRunnableProviderNames: () => registry.getAll().map((provider) => provider.adapter.name),
|
|
18337
18514
|
getEffectiveProviderModels: () => effectiveProviderModels(registry),
|
|
18338
18515
|
onTrafficSyncRequested: (projectName, sourceId) => {
|
|
18339
|
-
|
|
18516
|
+
schedulerClient.trafficSync(projectName, sourceId).catch((err) => {
|
|
18340
18517
|
app.log.error(
|
|
18341
18518
|
{
|
|
18342
18519
|
projectName,
|
|
@@ -18356,7 +18533,7 @@ async function createServer(opts) {
|
|
|
18356
18533
|
onDoctorRequested: (projectName) => {
|
|
18357
18534
|
void (async () => {
|
|
18358
18535
|
try {
|
|
18359
|
-
const report = await
|
|
18536
|
+
const report = await schedulerClient.runDoctor({ project: projectName });
|
|
18360
18537
|
const project = opts.db.select().from(projects).where(eq26(projects.name, projectName)).get();
|
|
18361
18538
|
if (!project) {
|
|
18362
18539
|
app.log.warn({ projectName }, "doctor schedule fired for an unknown project");
|
|
@@ -18372,7 +18549,7 @@ async function createServer(opts) {
|
|
|
18372
18549
|
})();
|
|
18373
18550
|
},
|
|
18374
18551
|
onDataRefreshRequested: (projectName) => {
|
|
18375
|
-
void refreshAllIntegrations(
|
|
18552
|
+
void refreshAllIntegrations(schedulerClient, projectName);
|
|
18376
18553
|
},
|
|
18377
18554
|
onBacklinksSyncRequested: (projectName) => {
|
|
18378
18555
|
void (async () => {
|
|
@@ -18397,7 +18574,7 @@ async function createServer(opts) {
|
|
|
18397
18574
|
);
|
|
18398
18575
|
return;
|
|
18399
18576
|
}
|
|
18400
|
-
|
|
18577
|
+
schedulerClient.backlinksTriggerSync(probed.release).catch((err) => {
|
|
18401
18578
|
app.log.error(
|
|
18402
18579
|
{
|
|
18403
18580
|
projectName,
|
|
@@ -18898,6 +19075,9 @@ async function createServer(opts) {
|
|
|
18898
19075
|
await app.register(apiRoutes, {
|
|
18899
19076
|
db: opts.db,
|
|
18900
19077
|
routePrefix: apiPrefix,
|
|
19078
|
+
// Agent-surface usage (MCP, Aero, raw API). CLI and dashboard requests are
|
|
19079
|
+
// measured elsewhere and skipped inside.
|
|
19080
|
+
onRequestCompleted: createApiUsageTelemetry(),
|
|
18901
19081
|
skipAuth: false,
|
|
18902
19082
|
sessionCookieName: SESSION_COOKIE_NAME,
|
|
18903
19083
|
resolveSessionApiKeyId,
|
|
@@ -18935,7 +19115,7 @@ async function createServer(opts) {
|
|
|
18935
19115
|
const configPath = getConfigPath();
|
|
18936
19116
|
return {
|
|
18937
19117
|
databasePath: opts.config.database,
|
|
18938
|
-
configPath:
|
|
19118
|
+
configPath: fs8.existsSync(configPath) ? configPath : null
|
|
18939
19119
|
};
|
|
18940
19120
|
})(),
|
|
18941
19121
|
// Snapshot the bundled skill trees (version + file hashes) so the
|
|
@@ -18950,6 +19130,8 @@ async function createServer(opts) {
|
|
|
18950
19130
|
}
|
|
18951
19131
|
})(),
|
|
18952
19132
|
getAgentPluginState: opts.getAgentPluginState,
|
|
19133
|
+
// Powers the `canonry.version.current` doctor check. Non-blocking.
|
|
19134
|
+
getUpdateStatus: () => getServerUpdateStatus(),
|
|
18953
19135
|
// Local canonry serve runs on the operator's machine, where pointing a
|
|
18954
19136
|
// webhook at localhost (Discord test container, Pipedream-mock dev server,
|
|
18955
19137
|
// etc.) is a legitimate workflow. Default to allowing it for the local
|
|
@@ -18968,7 +19150,13 @@ async function createServer(opts) {
|
|
|
18968
19150
|
credentials: credentialChecker,
|
|
18969
19151
|
oauthResourceUrl: publicOrigin ? `${publicOrigin}${`${basePath ?? "/"}api/v1/mcp`.replace("//", "/")}` : void 0,
|
|
18970
19152
|
registerAuthenticatedRoutes: async (scope) => {
|
|
18971
|
-
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
|
+
});
|
|
18972
19160
|
registerOAuthAdminRoutes(scope, { db: opts.db });
|
|
18973
19161
|
if (!sessionRegistry) return;
|
|
18974
19162
|
registerAgentRoutes(scope, { db: opts.db, sessionRegistry });
|
|
@@ -19428,9 +19616,7 @@ async function createServer(opts) {
|
|
|
19428
19616
|
listOperationalLogs: (query) => operationalLogs.list(query),
|
|
19429
19617
|
getTelemetryStatus,
|
|
19430
19618
|
setTelemetryEnabled: (enabled) => {
|
|
19431
|
-
|
|
19432
|
-
config.telemetry = enabled;
|
|
19433
|
-
saveConfigPatch(config);
|
|
19619
|
+
void setTelemetryPreference(enabled, "api");
|
|
19434
19620
|
opts.config.telemetry = enabled;
|
|
19435
19621
|
},
|
|
19436
19622
|
recordOnboardingEvent: (event) => {
|
|
@@ -19513,9 +19699,9 @@ async function createServer(opts) {
|
|
|
19513
19699
|
return snapshotService.createReport(input);
|
|
19514
19700
|
}
|
|
19515
19701
|
});
|
|
19516
|
-
const dirname = path8.dirname(
|
|
19702
|
+
const dirname = path8.dirname(fileURLToPath3(import.meta.url));
|
|
19517
19703
|
const assetsDir = opts.assetsDir ?? path8.join(dirname, "..", "assets");
|
|
19518
|
-
if (
|
|
19704
|
+
if (fs8.existsSync(assetsDir)) {
|
|
19519
19705
|
const indexPath = path8.join(assetsDir, "index.html");
|
|
19520
19706
|
const injectConfig = (html, projectTabsOverride, themeOverride, renderTokenOverride) => {
|
|
19521
19707
|
const clientConfig = {};
|
|
@@ -19589,8 +19775,8 @@ async function createServer(opts) {
|
|
|
19589
19775
|
}
|
|
19590
19776
|
});
|
|
19591
19777
|
const serveIndex = (_request, reply) => {
|
|
19592
|
-
if (
|
|
19593
|
-
const html =
|
|
19778
|
+
if (fs8.existsSync(indexPath)) {
|
|
19779
|
+
const html = fs8.readFileSync(indexPath, "utf-8");
|
|
19594
19780
|
return sendSpaDocument(reply, html);
|
|
19595
19781
|
}
|
|
19596
19782
|
return reply.status(404).send({ error: "Dashboard not built" });
|
|
@@ -19621,8 +19807,8 @@ async function createServer(opts) {
|
|
|
19621
19807
|
if (basePath && !url.startsWith(basePath)) {
|
|
19622
19808
|
return reply.status(404).send({ error: "Not found", path: request.url });
|
|
19623
19809
|
}
|
|
19624
|
-
if (
|
|
19625
|
-
const html =
|
|
19810
|
+
if (fs8.existsSync(indexPath)) {
|
|
19811
|
+
const html = fs8.readFileSync(indexPath, "utf-8");
|
|
19626
19812
|
return sendSpaDocument(reply, html);
|
|
19627
19813
|
}
|
|
19628
19814
|
return reply.status(404).send({ error: "Not found" });
|
|
@@ -19762,6 +19948,7 @@ function parseQueryResponse(raw, count2) {
|
|
|
19762
19948
|
}
|
|
19763
19949
|
|
|
19764
19950
|
export {
|
|
19951
|
+
cliRuntimeContext,
|
|
19765
19952
|
trackCliCommandFinished,
|
|
19766
19953
|
setTelemetrySource,
|
|
19767
19954
|
getTelemetryStatus,
|
|
@@ -19771,6 +19958,7 @@ export {
|
|
|
19771
19958
|
showFirstRunNotice,
|
|
19772
19959
|
detectAndTrackUpgrade,
|
|
19773
19960
|
trackEvent,
|
|
19961
|
+
setTelemetryPreference,
|
|
19774
19962
|
backfillAnswerVisibilityCommand,
|
|
19775
19963
|
backfillNormalizedPaths,
|
|
19776
19964
|
backfillNormalizedPathsCommand,
|
|
@@ -19784,6 +19972,8 @@ export {
|
|
|
19784
19972
|
setGoogleAuthConfig,
|
|
19785
19973
|
formatAuditFactorScore,
|
|
19786
19974
|
checkLatestVersionForCli,
|
|
19975
|
+
readCachedUpdateAvailable,
|
|
19976
|
+
formatUpdateNotice,
|
|
19787
19977
|
listAgentProviders,
|
|
19788
19978
|
coerceAgentProvider,
|
|
19789
19979
|
AERO_TOOL_PROFILES,
|