@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
|
@@ -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,
|
|
@@ -9,6 +10,7 @@ import {
|
|
|
9
10
|
RESEARCH_RUN_SCOPE,
|
|
10
11
|
SKILL_MANIFEST_FILENAME,
|
|
11
12
|
SkillsClients,
|
|
13
|
+
USAGE_TELEMETRY_HEADERS,
|
|
12
14
|
adsActivateTreeRequestSchema,
|
|
13
15
|
adsAdCreateRequestSchema,
|
|
14
16
|
adsAdGroupCreateRequestSchema,
|
|
@@ -27,9 +29,11 @@ import {
|
|
|
27
29
|
canonicalizeGtmResourceSelection,
|
|
28
30
|
classifySkillFile,
|
|
29
31
|
coerceSkillManifest,
|
|
32
|
+
compareSemver,
|
|
30
33
|
competitorBatchRequestSchema,
|
|
31
34
|
competitorLandscapeQuerySchema,
|
|
32
35
|
describeError,
|
|
36
|
+
detectAgentRuntime,
|
|
33
37
|
discoveryBucketSchema,
|
|
34
38
|
discoveryCompetitorTypeSchema,
|
|
35
39
|
discoveryPromoteRequestSchema,
|
|
@@ -38,8 +42,10 @@ import {
|
|
|
38
42
|
gaMeasurementHostScopeSchema,
|
|
39
43
|
googleAdsMetricsWindowSchema,
|
|
40
44
|
gscPerformanceOrderBySchema,
|
|
45
|
+
isInstallMethod,
|
|
41
46
|
isReadOnlyKey,
|
|
42
47
|
isSensitiveDiagnosticQueryKey,
|
|
48
|
+
isStrictSemver,
|
|
43
49
|
keywordBatchRequestSchema,
|
|
44
50
|
keywordGenerateRequestSchema,
|
|
45
51
|
logQuerySchema,
|
|
@@ -82,6 +88,7 @@ import {
|
|
|
82
88
|
measurementQueryTemplateApplyRequestSchema,
|
|
83
89
|
measurementQueryTemplateUpsertRequestSchema,
|
|
84
90
|
measurementQuestionResultQuerySchema,
|
|
91
|
+
normalizeAgentSlug,
|
|
85
92
|
normalizeTelemetryStatus,
|
|
86
93
|
notificationCreateRequestSchema,
|
|
87
94
|
notificationEventSchema,
|
|
@@ -114,8 +121,10 @@ import {
|
|
|
114
121
|
trafficConnectWordpressRequestSchema,
|
|
115
122
|
trafficEventKindSchema,
|
|
116
123
|
trafficSeriesGranularitySchema,
|
|
124
|
+
upgradeCaveatFor,
|
|
125
|
+
upgradeCommandFor,
|
|
117
126
|
visibilityReportRequestSchema
|
|
118
|
-
} from "./chunk-
|
|
127
|
+
} from "./chunk-MMSPU72Z.js";
|
|
119
128
|
|
|
120
129
|
// src/cli-error.ts
|
|
121
130
|
function isMachineFormat(format) {
|
|
@@ -609,6 +618,9 @@ function configExists() {
|
|
|
609
618
|
return fs.existsSync(getConfigPath());
|
|
610
619
|
}
|
|
611
620
|
|
|
621
|
+
// src/client.ts
|
|
622
|
+
import { AsyncLocalStorage } from "async_hooks";
|
|
623
|
+
|
|
612
624
|
// src/client-reliability.ts
|
|
613
625
|
function parseRetryAfterMs(header, now = Date.now()) {
|
|
614
626
|
if (!header) return void 0;
|
|
@@ -6021,6 +6033,21 @@ function createClient2(opts) {
|
|
|
6021
6033
|
}
|
|
6022
6034
|
|
|
6023
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
|
+
}
|
|
6046
|
+
var usageTagStore = new AsyncLocalStorage();
|
|
6047
|
+
function runWithUsageTags(client2, tags, fn) {
|
|
6048
|
+
if (!(client2 instanceof ApiClient)) return fn();
|
|
6049
|
+
return usageTagStore.run(tags, fn);
|
|
6050
|
+
}
|
|
6024
6051
|
var SAFE_CLIENT_NAME = /^\w[\w./-]{0,127}$/;
|
|
6025
6052
|
var SAFE_ACTOR_SESSION = /^\w[\w.:-]{0,127}$/;
|
|
6026
6053
|
function safeClientName(value) {
|
|
@@ -6049,12 +6076,24 @@ var ApiClient = class {
|
|
|
6049
6076
|
this.probeSkipped = opts?.skipProbe ?? false;
|
|
6050
6077
|
this.heyClientBaseUrl = this.originUrl;
|
|
6051
6078
|
const actorSession = safeActorSession(opts?.actorSession);
|
|
6079
|
+
const surface = opts?.surface ?? "cli";
|
|
6080
|
+
const serverHosted = surface === "mcp-http" || surface === "aero";
|
|
6052
6081
|
this.requestHeaders = {
|
|
6053
6082
|
authorization: `Bearer ${this.apiKey}`,
|
|
6054
6083
|
"user-agent": safeClientName(opts?.clientName),
|
|
6055
|
-
...actorSession ? { "x-canonry-actor-session": actorSession } : {}
|
|
6084
|
+
...actorSession ? { "x-canonry-actor-session": actorSession } : {},
|
|
6085
|
+
[USAGE_TELEMETRY_HEADERS.surface]: surface,
|
|
6086
|
+
...serverHosted ? {} : { [USAGE_TELEMETRY_HEADERS.agent]: detectAgentRuntime(process.env) }
|
|
6056
6087
|
};
|
|
6057
6088
|
this.heyClient = createClient2({ baseUrl: this.originUrl, apiKey: this.apiKey, headers: this.requestHeaders });
|
|
6089
|
+
this.heyClient.interceptors.request.use((request) => {
|
|
6090
|
+
const tags = usageTagStore.getStore();
|
|
6091
|
+
if (tags?.mcpTool) request.headers.set(USAGE_TELEMETRY_HEADERS.mcpTool, tags.mcpTool);
|
|
6092
|
+
if (tags?.mcpCall) request.headers.set(USAGE_TELEMETRY_HEADERS.mcpCall, tags.mcpCall);
|
|
6093
|
+
const mcpClient = normalizeAgentSlug(tags?.mcpClient);
|
|
6094
|
+
if (mcpClient) request.headers.set(USAGE_TELEMETRY_HEADERS.mcpClient, mcpClient);
|
|
6095
|
+
return request;
|
|
6096
|
+
});
|
|
6058
6097
|
}
|
|
6059
6098
|
/**
|
|
6060
6099
|
* On first API call, probe /health to auto-discover basePath when the user
|
|
@@ -6330,7 +6369,7 @@ var ApiClient = class {
|
|
|
6330
6369
|
let res;
|
|
6331
6370
|
try {
|
|
6332
6371
|
res = await fetch(url, {
|
|
6333
|
-
headers: {
|
|
6372
|
+
headers: { ...this.requestHeaders }
|
|
6334
6373
|
});
|
|
6335
6374
|
} catch (err) {
|
|
6336
6375
|
const message = describeError(err);
|
|
@@ -7072,6 +7111,22 @@ var ApiClient = class {
|
|
|
7072
7111
|
async getApiKeySelf() {
|
|
7073
7112
|
return this.invoke(() => getApiV1KeysSelf({ client: this.heyClient }));
|
|
7074
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
|
+
}
|
|
7075
7130
|
async createApiKey(body) {
|
|
7076
7131
|
return this.invoke(() => postApiV1Keys({ client: this.heyClient, body }));
|
|
7077
7132
|
}
|
|
@@ -8939,6 +8994,7 @@ var ApiClient = class {
|
|
|
8939
8994
|
};
|
|
8940
8995
|
|
|
8941
8996
|
// src/mcp/server.ts
|
|
8997
|
+
import { randomUUID } from "crypto";
|
|
8942
8998
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
8943
8999
|
import { z as z5 } from "zod";
|
|
8944
9000
|
|
|
@@ -13115,15 +13171,34 @@ function createCanonryMcpServer(options = {}) {
|
|
|
13115
13171
|
return createCanonryMcpServerWithCatalog(options).server;
|
|
13116
13172
|
}
|
|
13117
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
|
+
}
|
|
13118
13190
|
function createCanonryMcpServerWithCatalog(options = {}) {
|
|
13119
|
-
const clientFactory = options.clientFactory ?? (() => createApiClient({ clientName: "canonry-mcp" }));
|
|
13191
|
+
const clientFactory = options.clientFactory ?? (() => createApiClient({ clientName: "canonry-mcp", surface: "mcp-stdio", actorSession: randomUUID() }));
|
|
13120
13192
|
const client2 = clientFactory();
|
|
13121
13193
|
const scope = options.scope ?? "all";
|
|
13194
|
+
const update = readUpdateAvailable(options.updateAvailable);
|
|
13122
13195
|
const server = new McpServer({
|
|
13123
13196
|
name: "canonry",
|
|
13124
13197
|
version: PACKAGE_VERSION
|
|
13125
13198
|
}, {
|
|
13126
|
-
instructions: SERVER_INSTRUCTIONS
|
|
13199
|
+
instructions: update ? `${SERVER_INSTRUCTIONS.trimEnd()}
|
|
13200
|
+
|
|
13201
|
+
${updateNoticeInstructions(update)}` : SERVER_INSTRUCTIONS
|
|
13127
13202
|
});
|
|
13128
13203
|
server.validateToolInput = async (_tool, args) => args;
|
|
13129
13204
|
const entries = [];
|
|
@@ -13141,7 +13216,11 @@ function createCanonryMcpServerWithCatalog(options = {}) {
|
|
|
13141
13216
|
},
|
|
13142
13217
|
async (input) => withToolErrors(async () => {
|
|
13143
13218
|
const parsed = tool.inputSchema.parse(input ?? {});
|
|
13144
|
-
return
|
|
13219
|
+
return runWithUsageTags(client2, {
|
|
13220
|
+
mcpTool: tool.name,
|
|
13221
|
+
mcpCall: randomUUID(),
|
|
13222
|
+
mcpClient: server.server.getClientVersion()?.name
|
|
13223
|
+
}, () => handler(client2, parsed));
|
|
13145
13224
|
})
|
|
13146
13225
|
);
|
|
13147
13226
|
entries.push({ tool, registered });
|
|
@@ -13150,7 +13229,7 @@ function createCanonryMcpServerWithCatalog(options = {}) {
|
|
|
13150
13229
|
const catalog = new DynamicToolCatalog(server, entries, scope, { eager });
|
|
13151
13230
|
catalog.applyInitialEnablement();
|
|
13152
13231
|
const mode = options.tiers !== void 0 ? "hosted-fixed-catalog" : eager ? "stdio-fixed-catalog" : "stdio-progressive";
|
|
13153
|
-
registerMetaTools(server, catalog, { includeToolkitLoader: options.tiers === void 0, mode });
|
|
13232
|
+
registerMetaTools(server, catalog, { includeToolkitLoader: options.tiers === void 0, mode, updateAvailable: options.updateAvailable });
|
|
13154
13233
|
server.registerResource("canonry-agent-operations-v1", OPERATIONS_GUIDE.resourceUri, {
|
|
13155
13234
|
title: "Canonry Operations Guide v1",
|
|
13156
13235
|
description: "Optional public operations guidance. Use canonry_help when resources are unavailable.",
|
|
@@ -13177,7 +13256,11 @@ function registerMetaTools(server, catalog, opts) {
|
|
|
13177
13256
|
async (input) => {
|
|
13178
13257
|
try {
|
|
13179
13258
|
const parsed = helpInputSchema.parse(input ?? {});
|
|
13180
|
-
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
|
+
};
|
|
13181
13264
|
return { ...jsonToolResult(result), structuredContent: result };
|
|
13182
13265
|
} catch (error) {
|
|
13183
13266
|
return errorToolResult(error);
|
|
@@ -13618,6 +13701,7 @@ export {
|
|
|
13618
13701
|
emitInstallSummary,
|
|
13619
13702
|
getMissingUserSkillsNudge,
|
|
13620
13703
|
parseSkillsClient,
|
|
13704
|
+
runWithUsageTags,
|
|
13621
13705
|
createApiClient,
|
|
13622
13706
|
ApiClient,
|
|
13623
13707
|
measurementDraftOperationSchema,
|
|
@@ -124,6 +124,7 @@ import {
|
|
|
124
124
|
TrafficSourceAuthModes,
|
|
125
125
|
TrafficSourceStatuses,
|
|
126
126
|
TrafficSourceTypes,
|
|
127
|
+
USAGE_TELEMETRY_HEADERS,
|
|
127
128
|
UserRoles,
|
|
128
129
|
VerificationStatuses,
|
|
129
130
|
WILDCARD_SCOPE,
|
|
@@ -240,6 +241,7 @@ import {
|
|
|
240
241
|
clusterByCosine,
|
|
241
242
|
coerceSkillManifest,
|
|
242
243
|
compactDateToIso,
|
|
244
|
+
compareSemver,
|
|
243
245
|
competitorBatchRequestSchema,
|
|
244
246
|
competitorDtoSchema,
|
|
245
247
|
competitorLandscapeQuerySchema,
|
|
@@ -390,6 +392,7 @@ import {
|
|
|
390
392
|
isLocationRedirectStatus,
|
|
391
393
|
isReadOnlyKey,
|
|
392
394
|
isRetryableHttpError,
|
|
395
|
+
isStrictSemver,
|
|
393
396
|
isTemplateLinkRatio,
|
|
394
397
|
isTrendBaseline,
|
|
395
398
|
isVertexGroundingRedirect,
|
|
@@ -677,6 +680,7 @@ import {
|
|
|
677
680
|
trafficStatusResponseSchema,
|
|
678
681
|
trafficSyncResponseSchema,
|
|
679
682
|
unsupportedKind,
|
|
683
|
+
upgradeCaveatFor,
|
|
680
684
|
userDtoSchema,
|
|
681
685
|
userListDtoSchema,
|
|
682
686
|
validationError,
|
|
@@ -702,7 +706,7 @@ import {
|
|
|
702
706
|
wordpressSchemaDeployResultDtoSchema,
|
|
703
707
|
wordpressSchemaStatusResultDtoSchema,
|
|
704
708
|
wordpressStatusDtoSchema
|
|
705
|
-
} from "./chunk-
|
|
709
|
+
} from "./chunk-MMSPU72Z.js";
|
|
706
710
|
|
|
707
711
|
// src/intelligence-service.ts
|
|
708
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";
|
|
@@ -11551,7 +11555,11 @@ function requestAuditIdentity(request) {
|
|
|
11551
11555
|
credentialId: request.apiKey?.id ?? principal.id
|
|
11552
11556
|
};
|
|
11553
11557
|
}
|
|
11554
|
-
function
|
|
11558
|
+
function headerValue(value) {
|
|
11559
|
+
const first = Array.isArray(value) ? value[0] : value;
|
|
11560
|
+
return first ? first.slice(0, MAX_REQUEST_CONTEXT_LENGTH) : void 0;
|
|
11561
|
+
}
|
|
11562
|
+
function registerRequestContext(app, options = {}) {
|
|
11555
11563
|
app.addHook("onRequest", (request, reply, done) => {
|
|
11556
11564
|
reply.header("x-request-id", request.id);
|
|
11557
11565
|
requestContext.run({
|
|
@@ -11575,6 +11583,29 @@ function registerRequestContext(app) {
|
|
|
11575
11583
|
populateIdentity(request);
|
|
11576
11584
|
const context = requestContext.getStore();
|
|
11577
11585
|
if (context) context.statusCode = reply.statusCode;
|
|
11586
|
+
const route = request.routeOptions.url;
|
|
11587
|
+
if (options.onRequestCompleted && route) {
|
|
11588
|
+
try {
|
|
11589
|
+
const kind = request.principal?.kind;
|
|
11590
|
+
options.onRequestCompleted({
|
|
11591
|
+
method: request.method,
|
|
11592
|
+
route,
|
|
11593
|
+
statusCode: reply.statusCode,
|
|
11594
|
+
durationMs: reply.elapsedTime,
|
|
11595
|
+
userAgent: headerValue(request.headers["user-agent"]),
|
|
11596
|
+
actorSession: headerValue(request.headers["x-canonry-actor-session"]),
|
|
11597
|
+
principalKind: kind === "user" ? "user" : kind ? "api-key" : void 0,
|
|
11598
|
+
usageLabels: {
|
|
11599
|
+
surface: headerValue(request.headers[USAGE_TELEMETRY_HEADERS.surface]),
|
|
11600
|
+
agent: headerValue(request.headers[USAGE_TELEMETRY_HEADERS.agent]),
|
|
11601
|
+
mcpClient: headerValue(request.headers[USAGE_TELEMETRY_HEADERS.mcpClient]),
|
|
11602
|
+
mcpTool: headerValue(request.headers[USAGE_TELEMETRY_HEADERS.mcpTool]),
|
|
11603
|
+
mcpCall: headerValue(request.headers[USAGE_TELEMETRY_HEADERS.mcpCall])
|
|
11604
|
+
}
|
|
11605
|
+
});
|
|
11606
|
+
} catch {
|
|
11607
|
+
}
|
|
11608
|
+
}
|
|
11578
11609
|
done();
|
|
11579
11610
|
if (context) context.completed = true;
|
|
11580
11611
|
});
|
|
@@ -12885,13 +12916,13 @@ function activePlanVersionRow(db, projectId) {
|
|
|
12885
12916
|
if (!version) throw new Error(`Measurement plan ${projectId} points to missing version ${pointer.activeVersionId}`);
|
|
12886
12917
|
return version;
|
|
12887
12918
|
}
|
|
12888
|
-
function
|
|
12919
|
+
function headerValue2(request, name) {
|
|
12889
12920
|
const raw = request.headers[name];
|
|
12890
12921
|
const value = Array.isArray(raw) ? raw[0] : raw;
|
|
12891
12922
|
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
12892
12923
|
}
|
|
12893
12924
|
function requireIfMatch(request) {
|
|
12894
|
-
const value =
|
|
12925
|
+
const value = headerValue2(request, "if-match");
|
|
12895
12926
|
if (!value) throw measurementDraftEtagRequired();
|
|
12896
12927
|
return value;
|
|
12897
12928
|
}
|
|
@@ -12902,7 +12933,7 @@ function assertDraftEtag(row, ifMatch) {
|
|
|
12902
12933
|
}
|
|
12903
12934
|
}
|
|
12904
12935
|
function requireIdempotencyKey(request, operation) {
|
|
12905
|
-
const value =
|
|
12936
|
+
const value = headerValue2(request, "idempotency-key");
|
|
12906
12937
|
if (!value) throw measurementIdempotencyKeyRequired(operation);
|
|
12907
12938
|
return value;
|
|
12908
12939
|
}
|
|
@@ -21091,20 +21122,20 @@ function loadDraft(app, projectId, projectName) {
|
|
|
21091
21122
|
}
|
|
21092
21123
|
return { row, authoring: parsed.data };
|
|
21093
21124
|
}
|
|
21094
|
-
function
|
|
21125
|
+
function headerValue3(request, name) {
|
|
21095
21126
|
const value = request.headers[name];
|
|
21096
21127
|
const first = Array.isArray(value) ? value[0] : value;
|
|
21097
21128
|
return typeof first === "string" && first.trim() ? first.trim() : void 0;
|
|
21098
21129
|
}
|
|
21099
21130
|
function requireCurrentEtag(request, draft) {
|
|
21100
|
-
const supplied =
|
|
21131
|
+
const supplied = headerValue3(request, "if-match");
|
|
21101
21132
|
if (!supplied) throw measurementDraftEtagRequired();
|
|
21102
21133
|
if (parseMeasurementDraftEtagVersion(supplied) !== draft.row.etagVersion) {
|
|
21103
21134
|
throw measurementDraftEtagStale(measurementDraftEtag(draft.row.etagVersion), supplied);
|
|
21104
21135
|
}
|
|
21105
21136
|
}
|
|
21106
21137
|
function requireIdempotencyKey2(request, operation) {
|
|
21107
|
-
const supplied =
|
|
21138
|
+
const supplied = headerValue3(request, "idempotency-key");
|
|
21108
21139
|
if (!supplied) throw measurementIdempotencyKeyRequired(operation);
|
|
21109
21140
|
return supplied;
|
|
21110
21141
|
}
|
|
@@ -44501,9 +44532,9 @@ async function withGa4Limit(fn) {
|
|
|
44501
44532
|
releaseGa4Slot();
|
|
44502
44533
|
}
|
|
44503
44534
|
}
|
|
44504
|
-
function parseRetryAfter(
|
|
44505
|
-
if (!
|
|
44506
|
-
const trimmed =
|
|
44535
|
+
function parseRetryAfter(headerValue4) {
|
|
44536
|
+
if (!headerValue4) return void 0;
|
|
44537
|
+
const trimmed = headerValue4.trim();
|
|
44507
44538
|
const asNum = Number(trimmed);
|
|
44508
44539
|
if (Number.isFinite(asNum) && asNum >= 0) return asNum;
|
|
44509
44540
|
const asDate = Date.parse(trimmed);
|
|
@@ -73847,9 +73878,9 @@ var VercelLogsApiError = class extends Error {
|
|
|
73847
73878
|
body;
|
|
73848
73879
|
retryAfterSeconds;
|
|
73849
73880
|
};
|
|
73850
|
-
function parseRetryAfter2(
|
|
73851
|
-
if (!
|
|
73852
|
-
const trimmed =
|
|
73881
|
+
function parseRetryAfter2(headerValue4) {
|
|
73882
|
+
if (!headerValue4) return void 0;
|
|
73883
|
+
const trimmed = headerValue4.trim();
|
|
73853
73884
|
const asNum = Number(trimmed);
|
|
73854
73885
|
if (Number.isFinite(asNum) && asNum >= 0) return asNum;
|
|
73855
73886
|
const asDate = Date.parse(trimmed);
|
|
@@ -81274,11 +81305,72 @@ var GOOGLE_MARKETING_DOCTOR_CHECKS = createGoogleMarketingDoctorChecks(
|
|
|
81274
81305
|
(ctx) => ctx.getGoogleMarketingDoctorInput?.(ctx)
|
|
81275
81306
|
);
|
|
81276
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
|
+
|
|
81277
81368
|
// ../api-routes/src/doctor/registry.ts
|
|
81278
81369
|
var ALL_CHECKS = [
|
|
81279
81370
|
// Runtime-state checks run first so file-system gone errors surface
|
|
81280
81371
|
// before any auth/integration checks try to touch the (orphaned) DB.
|
|
81281
81372
|
...RUNTIME_STATE_CHECKS,
|
|
81373
|
+
...VERSION_CHECKS,
|
|
81282
81374
|
...GOOGLE_AUTH_CHECKS,
|
|
81283
81375
|
...GBP_AUTH_CHECKS,
|
|
81284
81376
|
...PLACES_CHECKS,
|
|
@@ -81384,6 +81476,7 @@ async function doctorRoutes(app, opts) {
|
|
|
81384
81476
|
runtimeStatePaths: opts.runtimeStatePaths,
|
|
81385
81477
|
bundledSkills: opts.bundledSkills,
|
|
81386
81478
|
getAgentPluginState: opts.getAgentPluginState,
|
|
81479
|
+
getUpdateStatus: opts.getUpdateStatus,
|
|
81387
81480
|
getGoogleMarketingDoctorInput: opts.getGoogleMarketingDoctorInput
|
|
81388
81481
|
};
|
|
81389
81482
|
return runChecks(ctx, ALL_CHECKS, { checkIds });
|
|
@@ -81413,6 +81506,7 @@ async function doctorRoutes(app, opts) {
|
|
|
81413
81506
|
runtimeStatePaths: opts.runtimeStatePaths,
|
|
81414
81507
|
bundledSkills: opts.bundledSkills,
|
|
81415
81508
|
getAgentPluginState: opts.getAgentPluginState,
|
|
81509
|
+
getUpdateStatus: opts.getUpdateStatus,
|
|
81416
81510
|
getGoogleMarketingDoctorInput: opts.getGoogleMarketingDoctorInput
|
|
81417
81511
|
};
|
|
81418
81512
|
return runChecks(ctx, ALL_CHECKS, { checkIds });
|
|
@@ -84469,7 +84563,7 @@ async function apiRoutes(app, opts) {
|
|
|
84469
84563
|
});
|
|
84470
84564
|
}
|
|
84471
84565
|
await app.register(async (api) => {
|
|
84472
|
-
registerRequestContext(api);
|
|
84566
|
+
registerRequestContext(api, { onRequestCompleted: opts.onRequestCompleted });
|
|
84473
84567
|
await api.register(rateLimit, {
|
|
84474
84568
|
global: false,
|
|
84475
84569
|
hook: "preHandler",
|
|
@@ -84722,6 +84816,7 @@ async function apiRoutes(app, opts) {
|
|
|
84722
84816
|
runtimeStatePaths: opts.runtimeStatePaths,
|
|
84723
84817
|
bundledSkills: opts.bundledSkills,
|
|
84724
84818
|
getAgentPluginState: opts.getAgentPluginState,
|
|
84819
|
+
getUpdateStatus: opts.getUpdateStatus,
|
|
84725
84820
|
getGoogleMarketingDoctorInput: opts.getGoogleMarketingDoctorInput
|
|
84726
84821
|
});
|
|
84727
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";
|
|
@@ -17782,6 +17782,49 @@ function isGhostTelemetryEvent(eventName, properties) {
|
|
|
17782
17782
|
const location = typeof properties.location === "string" ? properties.location.trim().toLowerCase() : "";
|
|
17783
17783
|
return GHOST_TELEMETRY_TEST_LOCATIONS.has(location);
|
|
17784
17784
|
}
|
|
17785
|
+
var usageSurfaceSchema = z60.enum(["cli", "mcp-stdio", "mcp-http", "aero", "api", "dashboard"]);
|
|
17786
|
+
var USAGE_TELEMETRY_HEADERS = {
|
|
17787
|
+
surface: "x-canonry-surface",
|
|
17788
|
+
agent: "x-canonry-agent",
|
|
17789
|
+
mcpClient: "x-canonry-mcp-client",
|
|
17790
|
+
mcpTool: "x-canonry-mcp-tool",
|
|
17791
|
+
mcpCall: "x-canonry-mcp-call"
|
|
17792
|
+
};
|
|
17793
|
+
var AGENT_NONE = "none";
|
|
17794
|
+
var AGENT_SLUG_PATTERN = /^[a-z0-9][a-z0-9._-]{0,39}$/;
|
|
17795
|
+
function normalizeAgentSlug(value) {
|
|
17796
|
+
if (!value) return null;
|
|
17797
|
+
const slug = value.trim().toLowerCase().replace(/[^a-z0-9._-]+/g, "-").slice(0, 40).replace(/^[-._]+|[-._]+$/g, "");
|
|
17798
|
+
return AGENT_SLUG_PATTERN.test(slug) ? slug : null;
|
|
17799
|
+
}
|
|
17800
|
+
var AI_AGENT_ALIASES = {
|
|
17801
|
+
"claude-code": "claude",
|
|
17802
|
+
"github-copilot-cli": "github-copilot"
|
|
17803
|
+
};
|
|
17804
|
+
function agentFromAiAgentVariable(value) {
|
|
17805
|
+
const name = value.trim().toLowerCase().replace(/_agent$/, "").replace(/_\d[\d._-]*$/, "");
|
|
17806
|
+
const slug = normalizeAgentSlug(name);
|
|
17807
|
+
return slug ? AI_AGENT_ALIASES[slug] ?? slug : null;
|
|
17808
|
+
}
|
|
17809
|
+
function detectAgentRuntime(env) {
|
|
17810
|
+
const explicit = normalizeAgentSlug(env.CANONRY_AGENT);
|
|
17811
|
+
if (explicit) return explicit;
|
|
17812
|
+
if (env.AI_AGENT?.trim()) {
|
|
17813
|
+
const named = agentFromAiAgentVariable(env.AI_AGENT);
|
|
17814
|
+
if (named) return named;
|
|
17815
|
+
}
|
|
17816
|
+
if (env.CURSOR_TRACE_ID) return "cursor";
|
|
17817
|
+
if (env.CURSOR_AGENT || env.CURSOR_EXTENSION_HOST_ROLE === "agent-exec") return "cursor-cli";
|
|
17818
|
+
if (env.GEMINI_CLI) return "gemini";
|
|
17819
|
+
if (env.CODEX_SANDBOX || env.CODEX_CI || env.CODEX_THREAD_ID) return "codex";
|
|
17820
|
+
if (env.ANTIGRAVITY_AGENT) return "antigravity";
|
|
17821
|
+
if (env.AUGMENT_AGENT) return "augment-cli";
|
|
17822
|
+
if (env.OPENCODE_CLIENT) return "opencode";
|
|
17823
|
+
if (env.CLAUDECODE || env.CLAUDE_CODE) return env.CLAUDE_CODE_IS_COWORK ? "cowork" : "claude";
|
|
17824
|
+
if (env.REPL_ID) return "replit";
|
|
17825
|
+
if (env.COPILOT_MODEL || env.COPILOT_ALLOW_ALL || env.COPILOT_GITHUB_TOKEN) return "github-copilot";
|
|
17826
|
+
return AGENT_NONE;
|
|
17827
|
+
}
|
|
17785
17828
|
|
|
17786
17829
|
// ../contracts/src/trend-stability.ts
|
|
17787
17830
|
var MIN_TREND_POINTS = 4;
|
|
@@ -18011,6 +18054,63 @@ function redactEmbeddedUrls(value) {
|
|
|
18011
18054
|
});
|
|
18012
18055
|
}
|
|
18013
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
|
+
|
|
18014
18114
|
export {
|
|
18015
18115
|
__export,
|
|
18016
18116
|
apiKeyDtoSchema,
|
|
@@ -18793,6 +18893,11 @@ export {
|
|
|
18793
18893
|
normalizeOnboardingEventForCollection,
|
|
18794
18894
|
telemetryEventAcceptedDtoSchema,
|
|
18795
18895
|
isGhostTelemetryEvent,
|
|
18896
|
+
usageSurfaceSchema,
|
|
18897
|
+
USAGE_TELEMETRY_HEADERS,
|
|
18898
|
+
AGENT_NONE,
|
|
18899
|
+
normalizeAgentSlug,
|
|
18900
|
+
detectAgentRuntime,
|
|
18796
18901
|
MIN_TREND_POINTS,
|
|
18797
18902
|
isTrendBaseline,
|
|
18798
18903
|
operationalLogEntryDtoSchema,
|
|
@@ -18801,5 +18906,12 @@ export {
|
|
|
18801
18906
|
isSensitiveDiagnosticQueryKey,
|
|
18802
18907
|
redactLogValue,
|
|
18803
18908
|
redactLogString,
|
|
18804
|
-
diagnosticIdentity
|
|
18909
|
+
diagnosticIdentity,
|
|
18910
|
+
isStrictSemver,
|
|
18911
|
+
compareSemver,
|
|
18912
|
+
CANONRY_NPM_PACKAGE_URL,
|
|
18913
|
+
isInstallMethod,
|
|
18914
|
+
upgradeCommandFor,
|
|
18915
|
+
upgradeCaveatFor,
|
|
18916
|
+
updateCheckEnvOptOut
|
|
18805
18917
|
};
|