@canonry/canonry 6.2.0 → 6.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/assets/agent-workspace/skills/canonry/references/canonry-cli.md +12 -0
- package/assets/assets/{AuditHistoryPanel-CuEMfkaA.js → AuditHistoryPanel-BUYWIjlG.js} +1 -1
- package/assets/assets/{BacklinksPage-CftadZ1-.js → BacklinksPage-aBcxpN_9.js} +1 -1
- package/assets/assets/{HistoryPage-DouRgshN.js → HistoryPage-PoBuxiY_.js} +1 -1
- package/assets/assets/{MeasurementPropertyPage-qfqv2qs9.js → MeasurementPropertyPage-DyjyqHac.js} +1 -1
- package/assets/assets/{ProjectPage-DqEVwe4t.js → ProjectPage-Mmv5YW-K.js} +1 -1
- package/assets/assets/{RunsPage-CUkMs0gB.js → RunsPage-CHHxOpzo.js} +1 -1
- package/assets/assets/{SettingsPage-LLQ40tSh.js → SettingsPage-CNKnrLXC.js} +1 -1
- package/assets/assets/{SiteHealthSection-Ba7foDiU.js → SiteHealthSection-C9RivT7G.js} +3 -3
- package/assets/assets/{TrafficPage-Cu_ZR3--.js → TrafficPage-CY-ibXnb.js} +1 -1
- package/assets/assets/{TrafficSourceDetailPage-NKZq7jf9.js → TrafficSourceDetailPage-BgiPXubs.js} +1 -1
- package/assets/assets/{extract-error-message-BdQYrz9N.js → extract-error-message-H1dzzfBP.js} +1 -1
- package/assets/assets/{index-HOeLz1ZL.js → index-DSCun3I9.js} +21 -21
- package/assets/assets/{react-sigma_core.esm.min-CZaoE5cU.js → react-sigma_core.esm.min-uiYtR_DU.js} +1 -1
- package/assets/assets/{v2-overview-adapter-BVx4nI49.js → v2-overview-adapter-BQokN2eb.js} +1 -1
- package/assets/index.html +1 -1
- package/dist/{chunk-2RFHQ6XT.js → chunk-4WZUR7XX.js} +240 -119
- package/dist/{chunk-4EKOKWQT.js → chunk-5FYTFE5T.js} +2 -2
- package/dist/{chunk-Y2B7JRSB.js → chunk-5GY5WBAF.js} +3 -3
- package/dist/{chunk-ENI3GOWW.js → chunk-7CVTTQRV.js} +95 -26
- package/dist/{chunk-W3GDZZNF.js → chunk-DERWXLHX.js} +15 -1
- package/dist/{chunk-6EPN4XY5.js → chunk-EV5G5QRR.js} +324 -280
- package/dist/{chunk-V6JUBTNL.js → chunk-ZZEPF3A5.js} +23 -3
- package/dist/cli.js +53 -8
- package/dist/{demo-server-CH7MOBXW.js → demo-server-AIFR77TP.js} +4 -4
- package/dist/index.js +6 -6
- package/dist/{intelligence-service-4IONMOFB.js → intelligence-service-SYISPW25.js} +2 -2
- package/dist/mcp.js +4 -4
- package/package.json +13 -13
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
siteCrawlGraphNodes,
|
|
6
6
|
siteCrawlPages,
|
|
7
7
|
siteCrawlSnapshots
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-7CVTTQRV.js";
|
|
9
9
|
import {
|
|
10
10
|
SITE_CRAWL_GRAPH_MAX_EDGES,
|
|
11
11
|
SITE_CRAWL_GRAPH_MAX_NODES,
|
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
TEMPLATE_LINK_RATIO_THRESHOLD,
|
|
14
14
|
factorStatusFromScore,
|
|
15
15
|
roundPreservingTotal
|
|
16
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-EV5G5QRR.js";
|
|
17
17
|
|
|
18
18
|
// src/site-audit-factors.ts
|
|
19
19
|
function recordedSharePct(factor) {
|
|
@@ -4,14 +4,14 @@ import {
|
|
|
4
4
|
installSkills,
|
|
5
5
|
loadConfigRaw,
|
|
6
6
|
saveConfigPatch
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-ZZEPF3A5.js";
|
|
8
8
|
import {
|
|
9
9
|
PACKAGE_VERSION
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-DERWXLHX.js";
|
|
11
11
|
import {
|
|
12
12
|
SKILL_MANIFEST_FILENAME,
|
|
13
13
|
SkillsClients
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-EV5G5QRR.js";
|
|
15
15
|
|
|
16
16
|
// src/skills-autosync.ts
|
|
17
17
|
import fs from "fs";
|
|
@@ -354,6 +354,8 @@ import {
|
|
|
354
354
|
extractAnswerMentions,
|
|
355
355
|
extractDomainsFromText,
|
|
356
356
|
factorStatusFromScore,
|
|
357
|
+
feedbackAcceptedDtoSchema,
|
|
358
|
+
feedbackSubmissionSchema,
|
|
357
359
|
filterBrandedSeedCandidates,
|
|
358
360
|
filterCapturedCitedUrls,
|
|
359
361
|
findDuplicateLocationLabels,
|
|
@@ -869,7 +871,7 @@ import {
|
|
|
869
871
|
wordpressSchemaDeployResultDtoSchema,
|
|
870
872
|
wordpressSchemaStatusResultDtoSchema,
|
|
871
873
|
wordpressStatusDtoSchema
|
|
872
|
-
} from "./chunk-
|
|
874
|
+
} from "./chunk-EV5G5QRR.js";
|
|
873
875
|
|
|
874
876
|
// src/intelligence-service.ts
|
|
875
877
|
import { eq as eq82, desc as desc36, asc as asc15, and as and69, ne as ne14, or as or20, inArray as inArray32, gte as gte22, lte as lte20, isNull as isNull16, sql as sql31, exists } from "drizzle-orm";
|
|
@@ -9001,6 +9003,10 @@ function cancelProject(db, projectId, now, reason) {
|
|
|
9001
9003
|
)).run();
|
|
9002
9004
|
recountJobs(db, affected, now);
|
|
9003
9005
|
}
|
|
9006
|
+
function dispatchedInLastMinute(db, now) {
|
|
9007
|
+
const since = new Date(Date.parse(now) - 6e4).toISOString();
|
|
9008
|
+
return db.select({ requests: sql4`count(*)`, tokens: sql4`coalesce(sum(${sentimentAttempts.estimatedInputTokens}), 0)` }).from(sentimentAttempts).where(gt(sentimentAttempts.dispatchedAt, since)).get();
|
|
9009
|
+
}
|
|
9004
9010
|
var SentimentRepository = class {
|
|
9005
9011
|
constructor(db) {
|
|
9006
9012
|
this.db = db;
|
|
@@ -9209,6 +9215,10 @@ var SentimentRepository = class {
|
|
|
9209
9215
|
return claimed;
|
|
9210
9216
|
}, { behavior: "immediate" });
|
|
9211
9217
|
}
|
|
9218
|
+
/** Requests and estimated input tokens dispatched in the minute before `now`: the window `startAttempt` enforces. */
|
|
9219
|
+
recentDispatch(now) {
|
|
9220
|
+
return dispatchedInLastMinute(this.db, now);
|
|
9221
|
+
}
|
|
9212
9222
|
startAttempt(input) {
|
|
9213
9223
|
return this.db.transaction((tx) => {
|
|
9214
9224
|
const work = tx.select().from(sentimentWorkItems).where(and(
|
|
@@ -9223,8 +9233,7 @@ var SentimentRepository = class {
|
|
|
9223
9233
|
if (input.maxAttempts !== void 0 && work.attemptCount - work.attemptBudgetStart >= input.maxAttempts) return void 0;
|
|
9224
9234
|
const estimate = input.estimatedInputTokens ?? 0;
|
|
9225
9235
|
if (!Number.isSafeInteger(estimate) || estimate < 0) throw new Error("Invalid sentiment token estimate");
|
|
9226
|
-
const
|
|
9227
|
-
const used = tx.select({ requests: sql4`count(*)`, tokens: sql4`coalesce(sum(${sentimentAttempts.estimatedInputTokens}), 0)` }).from(sentimentAttempts).where(gt(sentimentAttempts.dispatchedAt, since)).get();
|
|
9236
|
+
const used = dispatchedInLastMinute(tx, input.now);
|
|
9228
9237
|
if (input.maxRequestsPerMinute !== void 0 && used.requests >= input.maxRequestsPerMinute) return void 0;
|
|
9229
9238
|
if (input.maxInputTokensPerMinute !== void 0 && used.tokens + estimate > input.maxInputTokensPerMinute) return void 0;
|
|
9230
9239
|
const attemptNumber = work.attemptCount + 1;
|
|
@@ -9299,6 +9308,14 @@ var SentimentRepository = class {
|
|
|
9299
9308
|
return true;
|
|
9300
9309
|
}, { behavior: "immediate" });
|
|
9301
9310
|
}
|
|
9311
|
+
/** Hand claimed work back to the queue untouched: no error, no retry time, no attempt counted. */
|
|
9312
|
+
releaseWork(input) {
|
|
9313
|
+
return this.db.transaction((tx) => {
|
|
9314
|
+
const changed = tx.update(sentimentWorkItems).set({ status: "pending", leaseOwner: null, leaseExpiresAt: null, updatedAt: input.now }).where(and(eq2(sentimentWorkItems.id, input.workItemId), eq2(sentimentWorkItems.leaseOwner, input.owner), eq2(sentimentWorkItems.status, "running"))).returning().all();
|
|
9315
|
+
if (changed.length > 0) shiftJobs(tx, changed[0].id, "running", "pending", input.now);
|
|
9316
|
+
return changed.length > 0;
|
|
9317
|
+
}, { behavior: "immediate" });
|
|
9318
|
+
}
|
|
9302
9319
|
failWork(input) {
|
|
9303
9320
|
return this.db.transaction((tx) => {
|
|
9304
9321
|
const changed = tx.update(sentimentWorkItems).set({
|
|
@@ -12466,7 +12483,7 @@ function resolveTrustProxy(raw) {
|
|
|
12466
12483
|
}
|
|
12467
12484
|
var FORWARDED_HEADERS = ["x-forwarded-for", "forwarded", "x-real-ip"];
|
|
12468
12485
|
function hasForwardedHeaders(request) {
|
|
12469
|
-
return FORWARDED_HEADERS.some((
|
|
12486
|
+
return FORWARDED_HEADERS.some((header2) => request.headers[header2] !== void 0);
|
|
12470
12487
|
}
|
|
12471
12488
|
function resolveCallerKey(request, trustProxyConfigured) {
|
|
12472
12489
|
const socketAddress = request.socket.remoteAddress ?? request.ip;
|
|
@@ -12496,9 +12513,9 @@ function cookieIsSecure(request, configured) {
|
|
|
12496
12513
|
function anyUsersExist(db) {
|
|
12497
12514
|
return db.select({ id: users.id }).from(users).limit(1).get() !== void 0;
|
|
12498
12515
|
}
|
|
12499
|
-
function parseCookieHeader(
|
|
12500
|
-
if (!
|
|
12501
|
-
return
|
|
12516
|
+
function parseCookieHeader(header2) {
|
|
12517
|
+
if (!header2) return {};
|
|
12518
|
+
return header2.split(";").map((part) => part.trim()).filter(Boolean).reduce((cookies, part) => {
|
|
12502
12519
|
const eqIdx = part.indexOf("=");
|
|
12503
12520
|
if (eqIdx <= 0) return cookies;
|
|
12504
12521
|
const name = part.slice(0, eqIdx).trim();
|
|
@@ -13018,11 +13035,11 @@ async function authPlugin(app, opts = {}) {
|
|
|
13018
13035
|
app.addHook("onRequest", async (request, reply) => {
|
|
13019
13036
|
const url = request.url.split("?")[0];
|
|
13020
13037
|
if (shouldSkipAuth(url)) return;
|
|
13021
|
-
const
|
|
13038
|
+
const header2 = request.headers.authorization;
|
|
13022
13039
|
let key;
|
|
13023
13040
|
let keyArrivedInCookie = false;
|
|
13024
|
-
if (
|
|
13025
|
-
const parts =
|
|
13041
|
+
if (header2) {
|
|
13042
|
+
const parts = header2.split(" ");
|
|
13026
13043
|
if (parts.length !== 2 || parts[0] !== "Bearer") {
|
|
13027
13044
|
throw authRequired();
|
|
13028
13045
|
}
|
|
@@ -22569,11 +22586,11 @@ function parseCsvRecords(value) {
|
|
|
22569
22586
|
return records.filter((record) => !record.physicalBlank);
|
|
22570
22587
|
}
|
|
22571
22588
|
function parseHeader(records) {
|
|
22572
|
-
const
|
|
22573
|
-
if (!
|
|
22589
|
+
const header2 = records.at(0);
|
|
22590
|
+
if (!header2) csvError("csv-header-missing", "CSV must include a property and group header row.");
|
|
22574
22591
|
const allowed = /* @__PURE__ */ new Set(["property", "group", "url"]);
|
|
22575
22592
|
const indices = /* @__PURE__ */ new Map();
|
|
22576
|
-
for (const [index2, raw] of
|
|
22593
|
+
for (const [index2, raw] of header2.fields.entries()) {
|
|
22577
22594
|
const name = normalizeGroupMembershipLabel(raw);
|
|
22578
22595
|
if (!allowed.has(name)) {
|
|
22579
22596
|
csvError("csv-header-invalid", `CSV header "${raw}" is not supported. Use property, group, and optional url.`, 400, { header: raw });
|
|
@@ -22588,7 +22605,7 @@ function parseHeader(records) {
|
|
|
22588
22605
|
csvError("csv-header-missing", `CSV is missing the required "${required2}" header.`, 400, { header: required2 });
|
|
22589
22606
|
}
|
|
22590
22607
|
}
|
|
22591
|
-
return { indices, length:
|
|
22608
|
+
return { indices, length: header2.fields.length };
|
|
22592
22609
|
}
|
|
22593
22610
|
function parseGroupMembershipCsv(csv) {
|
|
22594
22611
|
const byteLength = Buffer.byteLength(csv, "utf8");
|
|
@@ -22601,7 +22618,7 @@ function parseGroupMembershipCsv(csv) {
|
|
|
22601
22618
|
);
|
|
22602
22619
|
}
|
|
22603
22620
|
const records = parseCsvRecords(csv);
|
|
22604
|
-
const
|
|
22621
|
+
const header2 = parseHeader(records);
|
|
22605
22622
|
const dataRecords = records.slice(1);
|
|
22606
22623
|
if (dataRecords.length > MEASUREMENT_GROUP_MEMBERSHIP_CSV_MAX_DATA_ROWS) {
|
|
22607
22624
|
csvError(
|
|
@@ -22611,9 +22628,9 @@ function parseGroupMembershipCsv(csv) {
|
|
|
22611
22628
|
{ rowCount: dataRecords.length, maxRows: MEASUREMENT_GROUP_MEMBERSHIP_CSV_MAX_DATA_ROWS }
|
|
22612
22629
|
);
|
|
22613
22630
|
}
|
|
22614
|
-
const propertyIndex =
|
|
22615
|
-
const groupIndex =
|
|
22616
|
-
const urlIndex =
|
|
22631
|
+
const propertyIndex = header2.indices.get("property");
|
|
22632
|
+
const groupIndex = header2.indices.get("group");
|
|
22633
|
+
const urlIndex = header2.indices.get("url");
|
|
22617
22634
|
const rows = dataRecords.map((record, index2) => {
|
|
22618
22635
|
const property = record.fields[propertyIndex] ?? "";
|
|
22619
22636
|
const group = record.fields[groupIndex] ?? "";
|
|
@@ -22626,7 +22643,7 @@ function parseGroupMembershipCsv(csv) {
|
|
|
22626
22643
|
url,
|
|
22627
22644
|
normalizedProperty: normalizeGroupMembershipLabel(property),
|
|
22628
22645
|
normalizedGroupLabel: normalizeGroupMembershipLabel(group),
|
|
22629
|
-
columnCountValid: record.fields.length ===
|
|
22646
|
+
columnCountValid: record.fields.length === header2.length
|
|
22630
22647
|
};
|
|
22631
22648
|
});
|
|
22632
22649
|
return { sourceChecksum: sourceChecksum(csv), rows };
|
|
@@ -33766,7 +33783,7 @@ function renderWinsLosses(insights2, heading, emptyMessage, audience) {
|
|
|
33766
33783
|
</tr>`;
|
|
33767
33784
|
}).join("");
|
|
33768
33785
|
const headerCells = (isClient ? REPORT_SECTION_COPY["whats-changed"].client : REPORT_SECTION_COPY["whats-changed"].agency).insightHeaders;
|
|
33769
|
-
const headers = `<tr>${headerCells.map((
|
|
33786
|
+
const headers = `<tr>${headerCells.map((header2) => `<th>${header2}</th>`).join("")}</tr>`;
|
|
33770
33787
|
return `<div class="chart-card"><h3>${escapeHtml2(heading)}</h3>
|
|
33771
33788
|
<div class="table-scroll"><table class="report-table">
|
|
33772
33789
|
<thead>${headers}</thead>
|
|
@@ -39924,6 +39941,8 @@ var SCHEMA_TABLE = {
|
|
|
39924
39941
|
SnapshotRequest: z.object(snapshotRequestInputSchema.shape).omit({ phrases: true }),
|
|
39925
39942
|
SourceBreakdownDto: sourceBreakdownDtoSchema,
|
|
39926
39943
|
TelemetryEventAcceptedDto: telemetryEventAcceptedDtoSchema,
|
|
39944
|
+
FeedbackSubmission: feedbackSubmissionSchema,
|
|
39945
|
+
FeedbackAcceptedDto: feedbackAcceptedDtoSchema,
|
|
39927
39946
|
TelemetryStatusDto: telemetryStatusDtoSchema,
|
|
39928
39947
|
OperationalLogListDto: operationalLogListDtoSchema,
|
|
39929
39948
|
CloudflareWorkerIngestRequest: cloudflareWorkerIngestRequestSchema,
|
|
@@ -43053,6 +43072,28 @@ var routeCatalog = [
|
|
|
43053
43072
|
400: errorResponse("Invalid onboarding telemetry event.")
|
|
43054
43073
|
}
|
|
43055
43074
|
},
|
|
43075
|
+
{
|
|
43076
|
+
method: "post",
|
|
43077
|
+
path: "/api/v1/feedback",
|
|
43078
|
+
summary: "Send product feedback about Canonry",
|
|
43079
|
+
description: "Forwards a struggle, bug, improvement, or other note about Canonry itself to the Canonry team (canonry.ai). Free text is redacted for credentials before it leaves the machine; never include customer data. The server adds version, OS, and the calling surface and agent; the anonymous install id is attached only while telemetry is enabled.",
|
|
43080
|
+
tags: ["feedback"],
|
|
43081
|
+
requestBody: {
|
|
43082
|
+
required: true,
|
|
43083
|
+
content: {
|
|
43084
|
+
"application/json": {
|
|
43085
|
+
schema: { $ref: "#/components/schemas/FeedbackSubmission" }
|
|
43086
|
+
}
|
|
43087
|
+
}
|
|
43088
|
+
},
|
|
43089
|
+
responses: {
|
|
43090
|
+
202: jsonResponse("Feedback accepted by the collector.", "FeedbackAcceptedDto"),
|
|
43091
|
+
400: errorResponse("Invalid feedback."),
|
|
43092
|
+
429: errorResponse("Feedback rate limit reached; try again later."),
|
|
43093
|
+
501: errorResponse("Feedback is not available in this deployment."),
|
|
43094
|
+
502: errorResponse("The feedback collector could not be reached or rejected the submission.")
|
|
43095
|
+
}
|
|
43096
|
+
},
|
|
43056
43097
|
{
|
|
43057
43098
|
method: "get",
|
|
43058
43099
|
path: "/api/v1/screenshots/{snapshotId}",
|
|
@@ -47791,6 +47832,31 @@ async function telemetryRoutes(app, opts) {
|
|
|
47791
47832
|
});
|
|
47792
47833
|
}
|
|
47793
47834
|
|
|
47835
|
+
// ../api-routes/src/feedback.ts
|
|
47836
|
+
function header(value) {
|
|
47837
|
+
return Array.isArray(value) ? value[0] : value;
|
|
47838
|
+
}
|
|
47839
|
+
async function feedbackRoutes(app, opts) {
|
|
47840
|
+
app.post("/feedback", async (request, reply) => {
|
|
47841
|
+
const parsed = feedbackSubmissionSchema.safeParse(request.body);
|
|
47842
|
+
if (!parsed.success) {
|
|
47843
|
+
throw validationError("Invalid feedback", {
|
|
47844
|
+
issues: parsed.error.issues.map((issue) => ({ code: issue.code, path: issue.path.join(".") }))
|
|
47845
|
+
});
|
|
47846
|
+
}
|
|
47847
|
+
if (!opts.submitFeedback) {
|
|
47848
|
+
throw notImplemented("Feedback is not available in this deployment");
|
|
47849
|
+
}
|
|
47850
|
+
const accepted = await opts.submitFeedback(parsed.data, {
|
|
47851
|
+
userAgent: header(request.headers["user-agent"]),
|
|
47852
|
+
surface: header(request.headers[USAGE_TELEMETRY_HEADERS.surface]),
|
|
47853
|
+
agent: header(request.headers[USAGE_TELEMETRY_HEADERS.agent]),
|
|
47854
|
+
mcpClient: header(request.headers[USAGE_TELEMETRY_HEADERS.mcpClient])
|
|
47855
|
+
});
|
|
47856
|
+
return reply.status(202).send(accepted);
|
|
47857
|
+
});
|
|
47858
|
+
}
|
|
47859
|
+
|
|
47794
47860
|
// ../api-routes/src/operational-logs.ts
|
|
47795
47861
|
async function operationalLogsRoutes(app, opts = {}) {
|
|
47796
47862
|
app.get("/operations/logs", async (request, reply) => {
|
|
@@ -47811,8 +47877,8 @@ async function operationalLogsRoutes(app, opts = {}) {
|
|
|
47811
47877
|
return withoutUnrequestedFields(page3, requestedFields(request.headers[OPERATIONAL_LOG_FIELDS_HEADER]));
|
|
47812
47878
|
});
|
|
47813
47879
|
}
|
|
47814
|
-
function requestedFields(
|
|
47815
|
-
const names = (Array.isArray(
|
|
47880
|
+
function requestedFields(header2) {
|
|
47881
|
+
const names = (Array.isArray(header2) ? header2 : [header2 ?? ""]).flatMap((value) => value.split(",")).map((name) => name.trim());
|
|
47816
47882
|
return new Set(OPERATIONAL_LOG_OPT_IN_CONTEXT_FIELDS.filter((field) => names.includes(field)));
|
|
47817
47883
|
}
|
|
47818
47884
|
function withoutUnrequestedFields(page3, requested) {
|
|
@@ -49250,7 +49316,7 @@ function createServiceAccountJwt(clientEmail, privateKey, scope) {
|
|
|
49250
49316
|
validatePrivateKey(privateKey);
|
|
49251
49317
|
validateScope(scope);
|
|
49252
49318
|
const now = Math.floor(Date.now() / 1e3);
|
|
49253
|
-
const
|
|
49319
|
+
const header2 = { alg: "RS256", typ: "JWT" };
|
|
49254
49320
|
const payload = {
|
|
49255
49321
|
iss: clientEmail,
|
|
49256
49322
|
scope,
|
|
@@ -49260,7 +49326,7 @@ function createServiceAccountJwt(clientEmail, privateKey, scope) {
|
|
|
49260
49326
|
// 1 hour
|
|
49261
49327
|
};
|
|
49262
49328
|
const encode = (obj) => Buffer.from(JSON.stringify(obj)).toString("base64url");
|
|
49263
|
-
const headerB64 = encode(
|
|
49329
|
+
const headerB64 = encode(header2);
|
|
49264
49330
|
const payloadB64 = encode(payload);
|
|
49265
49331
|
const signingInput = `${headerB64}.${payloadB64}`;
|
|
49266
49332
|
const sign = crypto30.createSign("RSA-SHA256");
|
|
@@ -63466,7 +63532,7 @@ function createServiceAccountJwt2(clientEmail, privateKey, scope) {
|
|
|
63466
63532
|
if (!privateKey) throw new CloudRunAuthError("privateKey is required");
|
|
63467
63533
|
if (!scope) throw new CloudRunAuthError("scope is required");
|
|
63468
63534
|
const now = Math.floor(Date.now() / 1e3);
|
|
63469
|
-
const
|
|
63535
|
+
const header2 = { alg: "RS256", typ: "JWT" };
|
|
63470
63536
|
const payload = {
|
|
63471
63537
|
iss: clientEmail,
|
|
63472
63538
|
scope,
|
|
@@ -63475,7 +63541,7 @@ function createServiceAccountJwt2(clientEmail, privateKey, scope) {
|
|
|
63475
63541
|
exp: now + 3600
|
|
63476
63542
|
};
|
|
63477
63543
|
const encode = (obj) => Buffer.from(JSON.stringify(obj)).toString("base64url");
|
|
63478
|
-
const headerB64 = encode(
|
|
63544
|
+
const headerB64 = encode(header2);
|
|
63479
63545
|
const payloadB64 = encode(payload);
|
|
63480
63546
|
const signingInput = `${headerB64}.${payloadB64}`;
|
|
63481
63547
|
const sign = crypto43.createSign("RSA-SHA256");
|
|
@@ -90767,6 +90833,9 @@ async function apiRoutes(app, opts) {
|
|
|
90767
90833
|
setTelemetryEnabled: opts.setTelemetryEnabled,
|
|
90768
90834
|
recordOnboardingEvent: opts.recordOnboardingEvent
|
|
90769
90835
|
});
|
|
90836
|
+
await api.register(feedbackRoutes, {
|
|
90837
|
+
submitFeedback: opts.submitFeedback
|
|
90838
|
+
});
|
|
90770
90839
|
await api.register(adsRoutes, {
|
|
90771
90840
|
adsCredentialStore: opts.adsCredentialStore,
|
|
90772
90841
|
verifyAdsAccount: opts.verifyAdsAccount,
|
|
@@ -33,6 +33,8 @@ import {
|
|
|
33
33
|
discoveryCompetitorTypeSchema,
|
|
34
34
|
discoveryPromoteRequestSchema,
|
|
35
35
|
discoveryRunRequestSchema,
|
|
36
|
+
feedbackAcceptedDtoSchema,
|
|
37
|
+
feedbackSubmissionSchema,
|
|
36
38
|
gaMeasurementAnalysisWindowSchema,
|
|
37
39
|
gaMeasurementHostScopeSchema,
|
|
38
40
|
googleAdsMetricsWindowSchema,
|
|
@@ -128,7 +130,7 @@ import {
|
|
|
128
130
|
trafficSeriesGranularitySchema,
|
|
129
131
|
visibilityCompareSelectionSchema,
|
|
130
132
|
visibilityReportRequestSchema
|
|
131
|
-
} from "./chunk-
|
|
133
|
+
} from "./chunk-EV5G5QRR.js";
|
|
132
134
|
|
|
133
135
|
// src/cli-error.ts
|
|
134
136
|
function isMachineFormat(format) {
|
|
@@ -3122,6 +3124,18 @@ var canonryMcpTools = [
|
|
|
3122
3124
|
openApiOperations: ["POST /api/v1/projects/{name}/runs"],
|
|
3123
3125
|
handler: (client, input) => client.triggerRun(input.project, input.request)
|
|
3124
3126
|
}),
|
|
3127
|
+
defineTool({
|
|
3128
|
+
name: "canonry_feedback",
|
|
3129
|
+
title: "Send feedback about Canonry",
|
|
3130
|
+
description: "Send feedback about Canonry itself to the Canonry team (canonry.ai). Use it when you uncover a struggle (a failure, confusing output, or something you had to work around) or have a concrete improvement. Tell the user what you are sending first, and send one report per issue. Be specific: the command or tool, the error code, and what you did instead. Never include secrets, API keys, or customer data; credentials are redacted before sending anyway. Not for questions about the user's own project data.",
|
|
3131
|
+
access: "write",
|
|
3132
|
+
tier: "core",
|
|
3133
|
+
inputSchema: feedbackSubmissionSchema,
|
|
3134
|
+
outputSchema: feedbackAcceptedDtoSchema,
|
|
3135
|
+
annotations: writeAnnotations({ idempotentHint: false, openWorldHint: true }),
|
|
3136
|
+
openApiOperations: ["POST /api/v1/feedback"],
|
|
3137
|
+
handler: (client, input) => client.sendFeedback(input)
|
|
3138
|
+
}),
|
|
3125
3139
|
defineTool({
|
|
3126
3140
|
name: "canonry_run_cancel",
|
|
3127
3141
|
title: "Cancel run",
|