@canonry/canonry 6.3.1 → 7.1.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/README.md +3 -3
- package/assets/agent-workspace/skills/aero/SKILL.md +2 -2
- package/assets/agent-workspace/skills/aero/references/reporting.md +26 -23
- package/assets/agent-workspace/skills/canonry/references/canonry-cli.md +55 -22
- package/assets/agent-workspace/skills/canonry/references/server-side-traffic.md +17 -6
- package/assets/assets/AuditHistoryPanel-CZWzgyrD.js +1 -0
- package/assets/assets/{BacklinksPage-aBcxpN_9.js → BacklinksPage-D23A-ahd.js} +1 -1
- package/assets/assets/ChartPrimitives-CYlrwLJs.js +1 -0
- package/assets/assets/{HistoryPage-PoBuxiY_.js → HistoryPage-BKGVxmNz.js} +1 -1
- package/assets/assets/MeasurementPropertyPage-BSxn6_RB.js +1 -0
- package/assets/assets/ProjectPage-D9W8G_1m.js +8 -0
- package/assets/assets/{RunsPage-CHHxOpzo.js → RunsPage-CeBBjLSE.js} +1 -1
- package/assets/assets/{SettingsPage-CNKnrLXC.js → SettingsPage-Jp5Yp4cz.js} +1 -1
- package/assets/assets/SiteHealthSection-DzVegBeN.js +4 -0
- package/assets/assets/{TrafficPage-CY-ibXnb.js → TrafficPage-B66AYwWk.js} +2 -2
- package/assets/assets/{TrafficSourceDetailPage-BgiPXubs.js → TrafficSourceDetailPage-CXtq1yES.js} +1 -1
- package/assets/assets/{extract-error-message-H1dzzfBP.js → extract-error-message-BVa0lOze.js} +1 -1
- package/assets/assets/index-CMe_-fAB.css +1 -0
- package/assets/assets/index-DuhS0WLM.js +86 -0
- package/assets/assets/{react-sigma_core.esm.min-uiYtR_DU.js → react-sigma_core.esm.min-60drsJa6.js} +1 -1
- package/assets/assets/v2-overview-adapter-CLUi2Lkv.js +1 -0
- package/assets/assets/{vendor-recharts-llkUFBGR.js → vendor-recharts-D-cUuLZo.js} +1 -1
- package/assets/index.html +3 -3
- package/dist/{chunk-4WZUR7XX.js → chunk-22R5E5CY.js} +101 -192
- package/dist/{chunk-DERWXLHX.js → chunk-2OQ6D26U.js} +66 -20
- package/dist/{chunk-5GY5WBAF.js → chunk-IAMECH2N.js} +3 -3
- package/dist/{chunk-ZZEPF3A5.js → chunk-MTSFSXCG.js} +83 -19
- package/dist/{chunk-5FYTFE5T.js → chunk-U2S5ABOR.js} +2 -2
- package/dist/{chunk-7CVTTQRV.js → chunk-VDX3LYKL.js} +4460 -8332
- package/dist/{chunk-EV5G5QRR.js → chunk-YGHPT5B2.js} +1475 -3002
- package/dist/cli.js +240 -178
- package/dist/{demo-server-AIFR77TP.js → demo-server-TIMYQLXS.js} +5 -14
- package/dist/index.js +6 -6
- package/dist/{intelligence-service-SYISPW25.js → intelligence-service-YCMRTVCI.js} +2 -2
- package/dist/mcp.js +4 -4
- package/package.json +15 -15
- package/assets/assets/AuditHistoryPanel-BUYWIjlG.js +0 -1
- package/assets/assets/ChartPrimitives-B5okB5g-.js +0 -1
- package/assets/assets/MeasurementPropertyPage-DyjyqHac.js +0 -1
- package/assets/assets/ProjectPage-Mmv5YW-K.js +0 -8
- package/assets/assets/SiteHealthSection-C9RivT7G.js +0 -4
- package/assets/assets/index-C17E5ZcA.css +0 -1
- package/assets/assets/index-DSCun3I9.js +0 -86
- package/assets/assets/v2-overview-adapter-BQokN2eb.js +0 -1
package/dist/cli.js
CHANGED
|
@@ -35,11 +35,11 @@ import {
|
|
|
35
35
|
trackCliCommandFinished,
|
|
36
36
|
trackEvent,
|
|
37
37
|
waitForServerRuntimeStartup
|
|
38
|
-
} from "./chunk-
|
|
38
|
+
} from "./chunk-22R5E5CY.js";
|
|
39
39
|
import {
|
|
40
40
|
autoSyncSkills,
|
|
41
41
|
formatAutoSyncNotice
|
|
42
|
-
} from "./chunk-
|
|
42
|
+
} from "./chunk-IAMECH2N.js";
|
|
43
43
|
import {
|
|
44
44
|
configExists,
|
|
45
45
|
createApiClient,
|
|
@@ -56,13 +56,13 @@ import {
|
|
|
56
56
|
saveConfig,
|
|
57
57
|
saveConfigPatch,
|
|
58
58
|
shouldPrintServeSkillsNudge
|
|
59
|
-
} from "./chunk-
|
|
59
|
+
} from "./chunk-MTSFSXCG.js";
|
|
60
60
|
import {
|
|
61
61
|
DEFAULT_DEMO_TRUSTED_PROXIES,
|
|
62
62
|
trustedProxiesProblem,
|
|
63
63
|
trustedProxyProblem
|
|
64
64
|
} from "./chunk-RB2F4D3Z.js";
|
|
65
|
-
import "./chunk-
|
|
65
|
+
import "./chunk-U2S5ABOR.js";
|
|
66
66
|
import {
|
|
67
67
|
CliError,
|
|
68
68
|
EXIT_SYSTEM_ERROR,
|
|
@@ -74,7 +74,7 @@ import {
|
|
|
74
74
|
runMeasurementDraftAction,
|
|
75
75
|
systemError,
|
|
76
76
|
usageError
|
|
77
|
-
} from "./chunk-
|
|
77
|
+
} from "./chunk-2OQ6D26U.js";
|
|
78
78
|
import {
|
|
79
79
|
CLOUDFLARE_WORKER_BINDINGS,
|
|
80
80
|
CLOUDFLARE_WORKER_GENERATED_MARKER,
|
|
@@ -88,9 +88,8 @@ import {
|
|
|
88
88
|
migrate,
|
|
89
89
|
projects,
|
|
90
90
|
queries,
|
|
91
|
-
renderReportHtml,
|
|
92
91
|
runs
|
|
93
|
-
} from "./chunk-
|
|
92
|
+
} from "./chunk-VDX3LYKL.js";
|
|
94
93
|
import {
|
|
95
94
|
AdsDeliverySnapshotStatuses,
|
|
96
95
|
AdsHistoricalCampaignRollupStatuses,
|
|
@@ -109,7 +108,6 @@ import {
|
|
|
109
108
|
OUTSTANDING_PROVIDER_BATCH_STATUSES,
|
|
110
109
|
ProviderBatchStatuses,
|
|
111
110
|
READ_ONLY_SCOPE,
|
|
112
|
-
REPORT_PERIOD_OPTIONS,
|
|
113
111
|
RatioUnits,
|
|
114
112
|
ResearchRunStatuses,
|
|
115
113
|
RunKinds,
|
|
@@ -139,6 +137,7 @@ import {
|
|
|
139
137
|
buildModelChangeNotice,
|
|
140
138
|
canonicalizeGtmAccountId,
|
|
141
139
|
canonicalizeGtmResourceSelection,
|
|
140
|
+
contentTargetDismissRequestSchema,
|
|
142
141
|
conversionTrackingContractWriteRequestSchema,
|
|
143
142
|
deduplicateResearchQueries,
|
|
144
143
|
describeError,
|
|
@@ -176,12 +175,12 @@ import {
|
|
|
176
175
|
normalizeProjectAliases,
|
|
177
176
|
normalizeTelemetryStatus,
|
|
178
177
|
notificationEventSchema,
|
|
178
|
+
parseTrafficAnalyticsPeriod,
|
|
179
179
|
providerDispatchModeSchema,
|
|
180
180
|
queryClassFilterSchema,
|
|
181
181
|
queryTrackingCommitRequestSchema,
|
|
182
182
|
queryTrackingPreviewRequestSchema,
|
|
183
183
|
referralAssessmentQuerySchema,
|
|
184
|
-
reportUnattributedAnswers,
|
|
185
184
|
researchBatchCreateSchema,
|
|
186
185
|
resolveNegativeReviewMaxStars,
|
|
187
186
|
resolveProjectQualifiedAliases,
|
|
@@ -197,10 +196,11 @@ import {
|
|
|
197
196
|
shareOfVoiceLabel,
|
|
198
197
|
shareOfVoiceReason,
|
|
199
198
|
snapshotProviderModeSchema,
|
|
199
|
+
unattributedAnswersLabel,
|
|
200
200
|
visibilityCompareSelectionSchema,
|
|
201
201
|
visibilityReportRequestSchema,
|
|
202
202
|
winnabilityClassSchema
|
|
203
|
-
} from "./chunk-
|
|
203
|
+
} from "./chunk-YGHPT5B2.js";
|
|
204
204
|
|
|
205
205
|
// src/cli.ts
|
|
206
206
|
import { pathToFileURL } from "url";
|
|
@@ -253,9 +253,9 @@ Usage: ${spec.usage}
|
|
|
253
253
|
if (spec.help) console.log(`${spec.help}
|
|
254
254
|
`);
|
|
255
255
|
}
|
|
256
|
-
function matchesPath(args,
|
|
257
|
-
if (args.length <
|
|
258
|
-
return
|
|
256
|
+
function matchesPath(args, path12) {
|
|
257
|
+
if (args.length < path12.length) return false;
|
|
258
|
+
return path12.every((segment, index) => args[index] === segment);
|
|
259
259
|
}
|
|
260
260
|
function withGlobalOptions(options) {
|
|
261
261
|
const base = options ? { ...options } : {};
|
|
@@ -734,7 +734,7 @@ function parseDemoTrustedProxies(values) {
|
|
|
734
734
|
async function demoCommand(options) {
|
|
735
735
|
const { host, port } = parseDemoListenOptions(options);
|
|
736
736
|
const trustProxy = parseDemoTrustedProxies(options.trustProxy);
|
|
737
|
-
const { createDemoServer } = await import("./demo-server-
|
|
737
|
+
const { createDemoServer } = await import("./demo-server-TIMYQLXS.js");
|
|
738
738
|
const app = await createDemoServer({ trustProxy });
|
|
739
739
|
let stopping = false;
|
|
740
740
|
const stop = () => {
|
|
@@ -4369,9 +4369,9 @@ async function gaConnect(project2, opts) {
|
|
|
4369
4369
|
propertyId: opts.propertyId
|
|
4370
4370
|
};
|
|
4371
4371
|
if (opts.keyFile) {
|
|
4372
|
-
const
|
|
4372
|
+
const fs19 = await import("fs");
|
|
4373
4373
|
try {
|
|
4374
|
-
const content =
|
|
4374
|
+
const content = fs19.readFileSync(opts.keyFile, "utf-8");
|
|
4375
4375
|
JSON.parse(content);
|
|
4376
4376
|
body.keyJson = content;
|
|
4377
4377
|
} catch (e) {
|
|
@@ -6500,9 +6500,9 @@ async function getCommand(opts) {
|
|
|
6500
6500
|
console.log(JSON.stringify(leaf, null, 2));
|
|
6501
6501
|
}
|
|
6502
6502
|
}
|
|
6503
|
-
function walkPath(value,
|
|
6504
|
-
if (!
|
|
6505
|
-
const segments =
|
|
6503
|
+
function walkPath(value, path12) {
|
|
6504
|
+
if (!path12 || path12 === ".") return value;
|
|
6505
|
+
const segments = path12.split(".").flatMap((part) => {
|
|
6506
6506
|
const tokens = [];
|
|
6507
6507
|
let i = 0;
|
|
6508
6508
|
const bracketStart = part.indexOf("[");
|
|
@@ -6550,13 +6550,13 @@ var GET_CLI_COMMANDS = [
|
|
|
6550
6550
|
},
|
|
6551
6551
|
run: async (input) => {
|
|
6552
6552
|
const project2 = requireProject(input, "get", USAGE2);
|
|
6553
|
-
const
|
|
6553
|
+
const path12 = requirePositional(input, 1, {
|
|
6554
6554
|
command: "get",
|
|
6555
6555
|
usage: USAGE2,
|
|
6556
6556
|
message: 'path is required (e.g. "scores.mentionShare.value")'
|
|
6557
6557
|
});
|
|
6558
6558
|
const from = getString(input.values, "from");
|
|
6559
|
-
await getCommand({ project: project2, path:
|
|
6559
|
+
await getCommand({ project: project2, path: path12, from, format: input.format });
|
|
6560
6560
|
}
|
|
6561
6561
|
}
|
|
6562
6562
|
];
|
|
@@ -7225,9 +7225,9 @@ async function trafficConnectWordpress(project2, opts) {
|
|
|
7225
7225
|
}
|
|
7226
7226
|
let applicationPassword = opts.appPassword?.trim() ?? "";
|
|
7227
7227
|
if (!applicationPassword && opts.appPasswordFile) {
|
|
7228
|
-
const
|
|
7228
|
+
const fs19 = await import("fs");
|
|
7229
7229
|
try {
|
|
7230
|
-
applicationPassword =
|
|
7230
|
+
applicationPassword = fs19.readFileSync(opts.appPasswordFile, "utf-8").trim();
|
|
7231
7231
|
} catch (e) {
|
|
7232
7232
|
const msg = describeError(e);
|
|
7233
7233
|
throw new CliError({
|
|
@@ -7283,10 +7283,10 @@ async function trafficConnectCloudRun(project2, opts) {
|
|
|
7283
7283
|
details: { project: project2 }
|
|
7284
7284
|
});
|
|
7285
7285
|
}
|
|
7286
|
-
const
|
|
7286
|
+
const fs19 = await import("fs");
|
|
7287
7287
|
let keyJson;
|
|
7288
7288
|
try {
|
|
7289
|
-
keyJson =
|
|
7289
|
+
keyJson = fs19.readFileSync(opts.serviceAccountKey, "utf-8");
|
|
7290
7290
|
JSON.parse(keyJson);
|
|
7291
7291
|
} catch (e) {
|
|
7292
7292
|
const msg = describeError(e);
|
|
@@ -7346,9 +7346,9 @@ async function trafficConnectVercel(project2, opts) {
|
|
|
7346
7346
|
}
|
|
7347
7347
|
let token = opts.token?.trim() ?? "";
|
|
7348
7348
|
if (!token && opts.tokenFile) {
|
|
7349
|
-
const
|
|
7349
|
+
const fs19 = await import("fs");
|
|
7350
7350
|
try {
|
|
7351
|
-
token =
|
|
7351
|
+
token = fs19.readFileSync(opts.tokenFile, "utf-8").trim();
|
|
7352
7352
|
} catch (e) {
|
|
7353
7353
|
const msg = describeError(e);
|
|
7354
7354
|
throw new CliError({
|
|
@@ -7560,6 +7560,51 @@ async function trafficSources(project2, opts) {
|
|
|
7560
7560
|
console.log(` ${formatSourceLine(source)}`);
|
|
7561
7561
|
}
|
|
7562
7562
|
}
|
|
7563
|
+
async function trafficAnalytics(project2, opts) {
|
|
7564
|
+
let period;
|
|
7565
|
+
try {
|
|
7566
|
+
period = parseTrafficAnalyticsPeriod(opts.period);
|
|
7567
|
+
} catch (error) {
|
|
7568
|
+
throw usageError(`Error: ${describeError(error)}
|
|
7569
|
+
|
|
7570
|
+
Usage: canonry traffic analytics <project> [--period 7|14|30|90] [--format json]`);
|
|
7571
|
+
}
|
|
7572
|
+
const result = await createApiClient().getTrafficAnalytics(project2, period);
|
|
7573
|
+
if (isMachineFormat(opts.format)) {
|
|
7574
|
+
console.log(JSON.stringify(result, null, 2));
|
|
7575
|
+
return;
|
|
7576
|
+
}
|
|
7577
|
+
const activity = result.activity;
|
|
7578
|
+
if (!activity) {
|
|
7579
|
+
console.log(`No traffic sources connected for project "${project2}".`);
|
|
7580
|
+
return;
|
|
7581
|
+
}
|
|
7582
|
+
console.log(`Traffic analytics for "${project2}": ${activity.windowStart} to ${activity.windowEnd}`);
|
|
7583
|
+
console.log(` First stored observation: ${activity.coverageStart ?? "none"}`);
|
|
7584
|
+
if (!activity.priorWindowComplete) console.log(" Prior recording window is incomplete; percentage changes are unavailable.");
|
|
7585
|
+
for (const [label, counts] of [
|
|
7586
|
+
["Verified crawler hits", activity.verifiedCrawlerHits],
|
|
7587
|
+
["Unverified crawler hits", activity.unverifiedCrawlerHits],
|
|
7588
|
+
["AI user-fetch hits", activity.aiUserFetchHits],
|
|
7589
|
+
["AI-referral arrivals", activity.referralArrivals],
|
|
7590
|
+
["Paid AI-referral arrivals", activity.referralArrivalsByClass.paid],
|
|
7591
|
+
["Organic AI-referral arrivals", activity.referralArrivalsByClass.organic],
|
|
7592
|
+
["Unclassified AI-referral arrivals", activity.referralArrivalsByClass.unclassified]
|
|
7593
|
+
]) {
|
|
7594
|
+
console.log(` ${label}: ${counts.current} (prior ${counts.prior}; change ${counts.deltaPct === null ? "unavailable" : formatPercent(counts.deltaPct, "percent")})`);
|
|
7595
|
+
}
|
|
7596
|
+
console.log(` AI-referral redirect hops: ${activity.referralRedirects}`);
|
|
7597
|
+
console.log("Operators: OPERATOR VERIFIED UNVERIFIED USER_FETCH REFERRALS CHANGE");
|
|
7598
|
+
for (const row of activity.byOperator) console.log(` ${row.operator} ${row.verifiedHits} ${row.unverifiedHits} ${row.userFetchHits} ${row.referralArrivals} ${row.deltaPct === null ? "unavailable" : formatPercent(row.deltaPct, "percent")}`);
|
|
7599
|
+
console.log("Crawled paths: PATH VERIFIED UNVERIFIED OPERATORS");
|
|
7600
|
+
for (const row of activity.topCrawledPaths) console.log(` ${row.path} ${row.verifiedHits} ${row.unverifiedHits} ${row.distinctOperators}`);
|
|
7601
|
+
console.log("AI-referral products: PRODUCT ARRIVALS LANDING_PATHS");
|
|
7602
|
+
for (const row of activity.referralProducts) console.log(` ${row.product} ${row.arrivals} ${row.distinctLandingPaths}`);
|
|
7603
|
+
console.log("Daily history: DATE VERIFIED UNVERIFIED USER_FETCH REFERRALS");
|
|
7604
|
+
for (const row of activity.dailyTrend) console.log(` ${row.date} ${row.verifiedCrawlerHits} ${row.unverifiedCrawlerHits} ${row.userFetchHits} ${row.referralArrivals}`);
|
|
7605
|
+
console.log("AI-referral landing paths: PATH ARRIVALS PRODUCTS");
|
|
7606
|
+
for (const row of activity.topReferralLandingPaths) console.log(` ${row.path} ${row.arrivals} ${row.distinctProducts}`);
|
|
7607
|
+
}
|
|
7563
7608
|
async function trafficStatus(project2, opts) {
|
|
7564
7609
|
const client = createApiClient();
|
|
7565
7610
|
const result = await client.trafficStatus(project2);
|
|
@@ -7732,6 +7777,16 @@ async function trafficReferralAssessment(project2, options) {
|
|
|
7732
7777
|
|
|
7733
7778
|
// src/cli-commands/traffic.ts
|
|
7734
7779
|
var TRAFFIC_CLI_COMMANDS = [
|
|
7780
|
+
{
|
|
7781
|
+
path: ["traffic", "analytics"],
|
|
7782
|
+
usage: "canonry traffic analytics <project> [--period 7|14|30|90] [--format json]",
|
|
7783
|
+
help: "Complete stored traffic totals and breakdowns for the selected window. Project-wide for Simple and Advanced portfolios; no provider calls or sync.",
|
|
7784
|
+
options: { period: stringOption() },
|
|
7785
|
+
run: async (input) => {
|
|
7786
|
+
const project2 = requireProject(input, "traffic.analytics", "canonry traffic analytics <project> [--period 7|14|30|90]");
|
|
7787
|
+
await trafficAnalytics(project2, { period: getString(input.values, "period"), format: input.format });
|
|
7788
|
+
}
|
|
7789
|
+
},
|
|
7735
7790
|
{
|
|
7736
7791
|
path: ["traffic", "referral-assessment"],
|
|
7737
7792
|
usage: "canonry traffic referral-assessment <project> --start-date YYYY-MM-DD --end-date YYYY-MM-DD [--source <id>] [--burst-threshold 100] [--ratio-threshold 3] [--limit 100] [--format json]",
|
|
@@ -8068,7 +8123,7 @@ var TRAFFIC_CLI_COMMANDS = [
|
|
|
8068
8123
|
unknownSubcommand(input.positionals[0], {
|
|
8069
8124
|
command: "traffic",
|
|
8070
8125
|
usage: "canonry traffic <subcommand> <project> [args]",
|
|
8071
|
-
available: ["connect", "activate", "sync", "backfill", "status", "sources", "events"]
|
|
8126
|
+
available: ["connect", "activate", "sync", "backfill", "reset", "status", "sources", "events", "analytics", "referral-assessment"]
|
|
8072
8127
|
});
|
|
8073
8128
|
}
|
|
8074
8129
|
}
|
|
@@ -10858,7 +10913,7 @@ var METRIC_REASONS = {
|
|
|
10858
10913
|
not_applicable: "not measured (not applicable)"
|
|
10859
10914
|
};
|
|
10860
10915
|
function unattributedText(metric) {
|
|
10861
|
-
return metric.state === "available" ?
|
|
10916
|
+
return metric.state === "available" ? unattributedAnswersLabel(metric) : null;
|
|
10862
10917
|
}
|
|
10863
10918
|
function metricText(metric) {
|
|
10864
10919
|
if (metric.state === "unavailable") return METRIC_REASONS[metric.reason] ?? `not measured (${metric.reason})`;
|
|
@@ -12977,71 +13032,6 @@ function parseNegativeReviewMaxStars(raw) {
|
|
|
12977
13032
|
return parsed.data;
|
|
12978
13033
|
}
|
|
12979
13034
|
|
|
12980
|
-
// src/commands/report.ts
|
|
12981
|
-
import fs13 from "fs";
|
|
12982
|
-
import path6 from "path";
|
|
12983
|
-
function defaultOutputPath(project2, audience) {
|
|
12984
|
-
const date = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
12985
|
-
return path6.resolve(process.cwd(), `canonry-report-${project2}-${audience}-${date}.html`);
|
|
12986
|
-
}
|
|
12987
|
-
async function runReportCommand(project2, opts = {}) {
|
|
12988
|
-
const client = createApiClient();
|
|
12989
|
-
const report = await client.getReport(project2, opts.period !== void 0 ? { period: opts.period } : void 0);
|
|
12990
|
-
const audience = opts.audience ?? "agency";
|
|
12991
|
-
if (isMachineFormat(opts.format)) {
|
|
12992
|
-
console.log(JSON.stringify(report, null, 2));
|
|
12993
|
-
return;
|
|
12994
|
-
}
|
|
12995
|
-
const html = renderReportHtml(report, { audience });
|
|
12996
|
-
const targetPath = opts.output ? path6.resolve(opts.output) : defaultOutputPath(project2, audience);
|
|
12997
|
-
const dir = path6.dirname(targetPath);
|
|
12998
|
-
if (!fs13.existsSync(dir)) {
|
|
12999
|
-
fs13.mkdirSync(dir, { recursive: true });
|
|
13000
|
-
}
|
|
13001
|
-
fs13.writeFileSync(targetPath, html, "utf-8");
|
|
13002
|
-
console.log(`Report written to ${targetPath}`);
|
|
13003
|
-
}
|
|
13004
|
-
|
|
13005
|
-
// src/cli-commands/report.ts
|
|
13006
|
-
var USAGE3 = "canonry report <project> [--audience agency|client] [--period 7|14|30|90] [--output <path>] [--format json]";
|
|
13007
|
-
function parseAudience(value) {
|
|
13008
|
-
if (value === void 0) return void 0;
|
|
13009
|
-
if (value === "agency" || value === "client") return value;
|
|
13010
|
-
throw usageError(`Error: --audience must be "agency" or "client"
|
|
13011
|
-
|
|
13012
|
-
Usage: ${USAGE3}`);
|
|
13013
|
-
}
|
|
13014
|
-
function parsePeriod(value) {
|
|
13015
|
-
if (value === void 0) return void 0;
|
|
13016
|
-
const n = Number(value);
|
|
13017
|
-
if (Number.isInteger(n) && REPORT_PERIOD_OPTIONS.includes(n)) {
|
|
13018
|
-
return n;
|
|
13019
|
-
}
|
|
13020
|
-
throw usageError(`Error: --period must be one of ${REPORT_PERIOD_OPTIONS.join(", ")}
|
|
13021
|
-
|
|
13022
|
-
Usage: ${USAGE3}`);
|
|
13023
|
-
}
|
|
13024
|
-
var REPORT_CLI_COMMANDS = [
|
|
13025
|
-
{
|
|
13026
|
-
path: ["report"],
|
|
13027
|
-
usage: USAGE3,
|
|
13028
|
-
options: {
|
|
13029
|
-
audience: { type: "string" },
|
|
13030
|
-
period: { type: "string" },
|
|
13031
|
-
output: { type: "string", short: "o" }
|
|
13032
|
-
},
|
|
13033
|
-
run: async (input) => {
|
|
13034
|
-
const project2 = requireProject(input, "report", USAGE3);
|
|
13035
|
-
await runReportCommand(project2, {
|
|
13036
|
-
format: input.format,
|
|
13037
|
-
audience: parseAudience(getString(input.values, "audience")),
|
|
13038
|
-
period: parsePeriod(getString(input.values, "period")),
|
|
13039
|
-
output: getString(input.values, "output")
|
|
13040
|
-
});
|
|
13041
|
-
}
|
|
13042
|
-
}
|
|
13043
|
-
];
|
|
13044
|
-
|
|
13045
13035
|
// src/commands/organic-evidence.ts
|
|
13046
13036
|
async function showOrganicEvidence(project2, opts) {
|
|
13047
13037
|
const data = await createApiClient().getOrganicEvidence(project2, opts.period);
|
|
@@ -13083,23 +13073,23 @@ function printOrganicEvidence(project2, data) {
|
|
|
13083
13073
|
}
|
|
13084
13074
|
|
|
13085
13075
|
// src/cli-commands/organic-evidence.ts
|
|
13086
|
-
var
|
|
13087
|
-
function
|
|
13076
|
+
var USAGE3 = "canonry organic-evidence <project> [--period 60|90] [--format json|jsonl]";
|
|
13077
|
+
function parsePeriod(value) {
|
|
13088
13078
|
if (value === void 0) return void 0;
|
|
13089
13079
|
if (value === "60" || value === "90") return Number(value);
|
|
13090
13080
|
throw usageError(`Error: --period must be 60 or 90
|
|
13091
13081
|
|
|
13092
|
-
Usage: ${
|
|
13082
|
+
Usage: ${USAGE3}`);
|
|
13093
13083
|
}
|
|
13094
13084
|
var ORGANIC_EVIDENCE_CLI_COMMANDS = [
|
|
13095
13085
|
{
|
|
13096
13086
|
path: ["organic-evidence"],
|
|
13097
|
-
usage:
|
|
13087
|
+
usage: USAGE3,
|
|
13098
13088
|
options: { period: stringOption() },
|
|
13099
13089
|
run: async (input) => {
|
|
13100
|
-
const project2 = requireProject(input, "organic-evidence",
|
|
13090
|
+
const project2 = requireProject(input, "organic-evidence", USAGE3);
|
|
13101
13091
|
await showOrganicEvidence(project2, {
|
|
13102
|
-
period:
|
|
13092
|
+
period: parsePeriod(getString(input.values, "period")),
|
|
13103
13093
|
format: input.format
|
|
13104
13094
|
});
|
|
13105
13095
|
}
|
|
@@ -14354,12 +14344,12 @@ var SKILLS_CLI_COMMANDS = [
|
|
|
14354
14344
|
];
|
|
14355
14345
|
|
|
14356
14346
|
// src/commands/snapshot.ts
|
|
14357
|
-
import fs15 from "fs";
|
|
14358
|
-
import path8 from "path";
|
|
14359
|
-
|
|
14360
|
-
// src/snapshot-pdf.ts
|
|
14361
14347
|
import fs14 from "fs";
|
|
14362
14348
|
import path7 from "path";
|
|
14349
|
+
|
|
14350
|
+
// src/snapshot-pdf.ts
|
|
14351
|
+
import fs13 from "fs";
|
|
14352
|
+
import path6 from "path";
|
|
14363
14353
|
import { PDFDocument, StandardFonts, rgb } from "pdf-lib";
|
|
14364
14354
|
var PAGE_WIDTH = 612;
|
|
14365
14355
|
var PAGE_HEIGHT = 792;
|
|
@@ -14571,9 +14561,9 @@ async function writeSnapshotPdf(report, outputPath) {
|
|
|
14571
14561
|
renderCompetitors(pdf, report);
|
|
14572
14562
|
renderQueries(pdf, report);
|
|
14573
14563
|
const bytes = await doc.save();
|
|
14574
|
-
const resolvedPath =
|
|
14575
|
-
|
|
14576
|
-
|
|
14564
|
+
const resolvedPath = path6.resolve(outputPath);
|
|
14565
|
+
fs13.mkdirSync(path6.dirname(resolvedPath), { recursive: true });
|
|
14566
|
+
fs13.writeFileSync(resolvedPath, bytes);
|
|
14577
14567
|
return resolvedPath;
|
|
14578
14568
|
}
|
|
14579
14569
|
function renderCover(pdf, report) {
|
|
@@ -14733,9 +14723,9 @@ Markdown saved: ${savedMdPath}`);
|
|
|
14733
14723
|
PDF saved: ${savedPdfPath}`);
|
|
14734
14724
|
}
|
|
14735
14725
|
function writeSnapshotMarkdown(report, outputPath) {
|
|
14736
|
-
const resolvedPath =
|
|
14737
|
-
|
|
14738
|
-
|
|
14726
|
+
const resolvedPath = path7.resolve(outputPath);
|
|
14727
|
+
fs14.mkdirSync(path7.dirname(resolvedPath), { recursive: true });
|
|
14728
|
+
fs14.writeFileSync(resolvedPath, formatSnapshotMarkdown(report), "utf-8");
|
|
14739
14729
|
return resolvedPath;
|
|
14740
14730
|
}
|
|
14741
14731
|
function formatSnapshotMarkdown(report) {
|
|
@@ -15447,6 +15437,44 @@ var INTELLIGENCE_CLI_COMMANDS = [
|
|
|
15447
15437
|
|
|
15448
15438
|
// src/commands/content.ts
|
|
15449
15439
|
var WINNABILITY_COVERAGE_CHECK_ID = "content.winnability.coverage";
|
|
15440
|
+
async function listContentDismissals(project2, opts) {
|
|
15441
|
+
const response = await createApiClient().getContentDismissals(project2);
|
|
15442
|
+
if (opts.format === "jsonl") {
|
|
15443
|
+
emitJsonl(response.dismissals.map((row) => ({ project: project2, ...row })));
|
|
15444
|
+
return;
|
|
15445
|
+
}
|
|
15446
|
+
if (isMachineFormat(opts.format)) {
|
|
15447
|
+
console.log(JSON.stringify(response, null, 2));
|
|
15448
|
+
return;
|
|
15449
|
+
}
|
|
15450
|
+
if (response.dismissals.length === 0) {
|
|
15451
|
+
console.log("No addressed content targets.");
|
|
15452
|
+
return;
|
|
15453
|
+
}
|
|
15454
|
+
for (const row of response.dismissals) {
|
|
15455
|
+
console.log(`${row.targetRef} \xB7 ${row.dismissedAt}`);
|
|
15456
|
+
if (row.addressedUrl) console.log(` Addressed URL: ${row.addressedUrl}`);
|
|
15457
|
+
if (row.note) console.log(` Note: ${row.note}`);
|
|
15458
|
+
}
|
|
15459
|
+
}
|
|
15460
|
+
async function dismissContentTarget(project2, request, opts) {
|
|
15461
|
+
const parsed = contentTargetDismissRequestSchema.safeParse(request);
|
|
15462
|
+
if (!parsed.success) throw usageError(parsed.error.issues[0]?.message ?? "Invalid content dismissal.");
|
|
15463
|
+
const response = await createApiClient().dismissContentTarget(project2, parsed.data);
|
|
15464
|
+
if (isMachineFormat(opts.format)) {
|
|
15465
|
+
console.log(JSON.stringify(response, null, 2));
|
|
15466
|
+
return;
|
|
15467
|
+
}
|
|
15468
|
+
console.log(`Marked ${response.targetRef} addressed. Restore with canonry content restore ${project2} ${response.targetRef}.`);
|
|
15469
|
+
}
|
|
15470
|
+
async function restoreContentTarget(project2, targetRef, opts) {
|
|
15471
|
+
await createApiClient().restoreContentTarget(project2, targetRef);
|
|
15472
|
+
if (isMachineFormat(opts.format)) {
|
|
15473
|
+
console.log(JSON.stringify({ targetRef, restored: true }, null, 2));
|
|
15474
|
+
return;
|
|
15475
|
+
}
|
|
15476
|
+
console.log(`Restored ${targetRef}. It can appear in content targets when still eligible.`);
|
|
15477
|
+
}
|
|
15450
15478
|
async function listContentTargets(project2, opts) {
|
|
15451
15479
|
const client = createApiClient();
|
|
15452
15480
|
const response = await client.getContentTargets(project2, {
|
|
@@ -15483,6 +15511,7 @@ async function listContentTargets(project2, opts) {
|
|
|
15483
15511
|
const conf = target.actionConfidence.padEnd(6);
|
|
15484
15512
|
const surface = target.winnabilityClass === "ceded" ? "CEDED " : "ownable";
|
|
15485
15513
|
console.log(`${action} ${score} conf=${conf} [${surface}] ${target.query}`);
|
|
15514
|
+
console.log(` target ref: ${target.targetRef}`);
|
|
15486
15515
|
if (target.ourBestPage) {
|
|
15487
15516
|
const posLabel = target.ourBestPage.gscAvgPosition !== null ? `pos #${target.ourBestPage.gscAvgPosition}` : "no GSC ranking";
|
|
15488
15517
|
console.log(` our page: ${target.ourBestPage.url} (${posLabel})`);
|
|
@@ -15637,6 +15666,40 @@ async function warnIfWinnabilityCoverageIsLow(client, project2, format) {
|
|
|
15637
15666
|
|
|
15638
15667
|
// src/cli-commands/content.ts
|
|
15639
15668
|
var CONTENT_CLI_COMMANDS = [
|
|
15669
|
+
{
|
|
15670
|
+
path: ["content", "dismissals"],
|
|
15671
|
+
usage: "canonry content dismissals <project> [--format json|jsonl]",
|
|
15672
|
+
help: "List persisted addressed content targets, including those hidden from content targets.",
|
|
15673
|
+
options: {},
|
|
15674
|
+
run: async (input) => {
|
|
15675
|
+
const project2 = requireProject(input, "content dismissals", "canonry content dismissals <project> [--format json|jsonl]");
|
|
15676
|
+
await listContentDismissals(project2, { format: input.format });
|
|
15677
|
+
}
|
|
15678
|
+
},
|
|
15679
|
+
{
|
|
15680
|
+
path: ["content", "dismiss"],
|
|
15681
|
+
usage: "canonry content dismiss <project> <targetRef> [--addressed-url <url>] [--note <text>] [--format json]",
|
|
15682
|
+
help: "Mark a content target addressed. Restore it with content restore.",
|
|
15683
|
+
options: { "addressed-url": { type: "string" }, note: { type: "string" } },
|
|
15684
|
+
run: async (input) => {
|
|
15685
|
+
const usage = "canonry content dismiss <project> <targetRef> [--addressed-url <url>] [--note <text>] [--format json]";
|
|
15686
|
+
const project2 = requireProject(input, "content dismiss", usage);
|
|
15687
|
+
const targetRef = requirePositional(input, 1, { command: "content dismiss", usage, message: "targetRef is required (from content targets)" });
|
|
15688
|
+
await dismissContentTarget(project2, { targetRef, addressedUrl: getString(input.values, "addressed-url"), note: getString(input.values, "note") }, { format: input.format });
|
|
15689
|
+
}
|
|
15690
|
+
},
|
|
15691
|
+
{
|
|
15692
|
+
path: ["content", "restore"],
|
|
15693
|
+
usage: "canonry content restore <project> <targetRef> [--format json]",
|
|
15694
|
+
help: "Remove a persisted dismissal so an eligible target can appear again.",
|
|
15695
|
+
options: {},
|
|
15696
|
+
run: async (input) => {
|
|
15697
|
+
const usage = "canonry content restore <project> <targetRef> [--format json]";
|
|
15698
|
+
const project2 = requireProject(input, "content restore", usage);
|
|
15699
|
+
const targetRef = requirePositional(input, 1, { command: "content restore", usage, message: "targetRef is required (from content dismissals)" });
|
|
15700
|
+
await restoreContentTarget(project2, targetRef, { format: input.format });
|
|
15701
|
+
}
|
|
15702
|
+
},
|
|
15640
15703
|
{
|
|
15641
15704
|
path: ["content", "targets"],
|
|
15642
15705
|
usage: "canonry content targets <project> [--limit <n>] [--include-in-progress] [--ownable] [--winnability-class <ownable|ceded>] [--format json]",
|
|
@@ -15730,7 +15793,7 @@ var CONTENT_CLI_COMMANDS = [
|
|
|
15730
15793
|
|
|
15731
15794
|
// src/commands/bootstrap.ts
|
|
15732
15795
|
import crypto2 from "crypto";
|
|
15733
|
-
import
|
|
15796
|
+
import path8 from "path";
|
|
15734
15797
|
import { isDeepStrictEqual } from "util";
|
|
15735
15798
|
import { eq } from "drizzle-orm";
|
|
15736
15799
|
function persistedValue(value) {
|
|
@@ -15750,7 +15813,7 @@ async function bootstrapCommand(opts) {
|
|
|
15750
15813
|
const existing = configExists();
|
|
15751
15814
|
const existingConfig = existing ? loadConfig() : void 0;
|
|
15752
15815
|
const existingRaw = existing ? loadConfigRaw() : null;
|
|
15753
|
-
const databasePath = env.databasePath || existingRaw?.database ||
|
|
15816
|
+
const databasePath = env.databasePath || existingRaw?.database || path8.join(configDir, "data.db");
|
|
15754
15817
|
let rawApiKey;
|
|
15755
15818
|
let generatedApiKey;
|
|
15756
15819
|
if (env.apiKey) {
|
|
@@ -15866,8 +15929,8 @@ async function bootstrapCommand(opts) {
|
|
|
15866
15929
|
|
|
15867
15930
|
// src/commands/daemon.ts
|
|
15868
15931
|
import { spawn as spawn2 } from "child_process";
|
|
15869
|
-
import
|
|
15870
|
-
import
|
|
15932
|
+
import fs15 from "fs";
|
|
15933
|
+
import path9 from "path";
|
|
15871
15934
|
|
|
15872
15935
|
// src/operator-url.ts
|
|
15873
15936
|
function operatorHost(bindHost) {
|
|
@@ -15890,7 +15953,7 @@ function resolveServePort(envPort, configPort) {
|
|
|
15890
15953
|
|
|
15891
15954
|
// src/commands/daemon.ts
|
|
15892
15955
|
function getPidPath() {
|
|
15893
|
-
return
|
|
15956
|
+
return path9.join(getConfigDir(), "canonry.pid");
|
|
15894
15957
|
}
|
|
15895
15958
|
function isProcessAlive(pid) {
|
|
15896
15959
|
try {
|
|
@@ -15937,8 +16000,8 @@ function buildServeForwardArgs(opts) {
|
|
|
15937
16000
|
async function startDaemon(opts) {
|
|
15938
16001
|
const pidPath = getPidPath();
|
|
15939
16002
|
const format = opts.format ?? "text";
|
|
15940
|
-
if (
|
|
15941
|
-
const existingPid = parseInt(
|
|
16003
|
+
if (fs15.existsSync(pidPath)) {
|
|
16004
|
+
const existingPid = parseInt(fs15.readFileSync(pidPath, "utf-8").trim(), 10);
|
|
15942
16005
|
if (!isNaN(existingPid) && isProcessAlive(existingPid)) {
|
|
15943
16006
|
throw new CliError({
|
|
15944
16007
|
code: "DAEMON_ALREADY_RUNNING",
|
|
@@ -15949,13 +16012,13 @@ async function startDaemon(opts) {
|
|
|
15949
16012
|
}
|
|
15950
16013
|
});
|
|
15951
16014
|
}
|
|
15952
|
-
|
|
16015
|
+
fs15.unlinkSync(pidPath);
|
|
15953
16016
|
}
|
|
15954
16017
|
const config = loadConfig();
|
|
15955
16018
|
const endpoint = resolveDaemonEndpoint(opts, process.env, config.port);
|
|
15956
16019
|
const host = endpoint.host;
|
|
15957
16020
|
const port = String(endpoint.port);
|
|
15958
|
-
const cliPath =
|
|
16021
|
+
const cliPath = path9.resolve(new URL(import.meta.url).pathname);
|
|
15959
16022
|
const inSourceMode = new URL(import.meta.url).pathname.endsWith(".ts");
|
|
15960
16023
|
const args = inSourceMode ? ["--import", "tsx", cliPath, "serve"] : [cliPath, "serve"];
|
|
15961
16024
|
args.push(...buildServeForwardArgs({ ...opts, host, port }));
|
|
@@ -15972,10 +16035,10 @@ async function startDaemon(opts) {
|
|
|
15972
16035
|
});
|
|
15973
16036
|
}
|
|
15974
16037
|
const configDir = getConfigDir();
|
|
15975
|
-
if (!
|
|
15976
|
-
|
|
16038
|
+
if (!fs15.existsSync(configDir)) {
|
|
16039
|
+
fs15.mkdirSync(configDir, { recursive: true });
|
|
15977
16040
|
}
|
|
15978
|
-
|
|
16041
|
+
fs15.writeFileSync(pidPath, String(child.pid), "utf-8");
|
|
15979
16042
|
if (!isMachineFormat(format)) {
|
|
15980
16043
|
process.stderr.write("Waiting for server to start...");
|
|
15981
16044
|
}
|
|
@@ -15986,7 +16049,7 @@ async function startDaemon(opts) {
|
|
|
15986
16049
|
} catch {
|
|
15987
16050
|
}
|
|
15988
16051
|
try {
|
|
15989
|
-
|
|
16052
|
+
fs15.unlinkSync(pidPath);
|
|
15990
16053
|
} catch {
|
|
15991
16054
|
}
|
|
15992
16055
|
throw new CliError({
|
|
@@ -16018,7 +16081,7 @@ async function startDaemon(opts) {
|
|
|
16018
16081
|
}
|
|
16019
16082
|
function stopDaemon(format = "text") {
|
|
16020
16083
|
const pidPath = getPidPath();
|
|
16021
|
-
if (!
|
|
16084
|
+
if (!fs15.existsSync(pidPath)) {
|
|
16022
16085
|
if (isMachineFormat(format)) {
|
|
16023
16086
|
console.log(JSON.stringify({
|
|
16024
16087
|
stopped: false,
|
|
@@ -16029,7 +16092,7 @@ function stopDaemon(format = "text") {
|
|
|
16029
16092
|
console.log("Canonry is not running (no PID file found)");
|
|
16030
16093
|
return;
|
|
16031
16094
|
}
|
|
16032
|
-
const pid = parseInt(
|
|
16095
|
+
const pid = parseInt(fs15.readFileSync(pidPath, "utf-8").trim(), 10);
|
|
16033
16096
|
if (isNaN(pid)) {
|
|
16034
16097
|
if (isMachineFormat(format)) {
|
|
16035
16098
|
console.log(JSON.stringify({
|
|
@@ -16040,7 +16103,7 @@ function stopDaemon(format = "text") {
|
|
|
16040
16103
|
} else {
|
|
16041
16104
|
console.error("Invalid PID file. Removing it.");
|
|
16042
16105
|
}
|
|
16043
|
-
|
|
16106
|
+
fs15.unlinkSync(pidPath);
|
|
16044
16107
|
return;
|
|
16045
16108
|
}
|
|
16046
16109
|
if (!isProcessAlive(pid)) {
|
|
@@ -16054,12 +16117,12 @@ function stopDaemon(format = "text") {
|
|
|
16054
16117
|
} else {
|
|
16055
16118
|
console.log(`Canonry is not running (stale PID: ${pid}). Cleaning up.`);
|
|
16056
16119
|
}
|
|
16057
|
-
|
|
16120
|
+
fs15.unlinkSync(pidPath);
|
|
16058
16121
|
return;
|
|
16059
16122
|
}
|
|
16060
16123
|
try {
|
|
16061
16124
|
process.kill(pid, "SIGTERM");
|
|
16062
|
-
|
|
16125
|
+
fs15.unlinkSync(pidPath);
|
|
16063
16126
|
if (isMachineFormat(format)) {
|
|
16064
16127
|
console.log(JSON.stringify({
|
|
16065
16128
|
stopped: true,
|
|
@@ -16083,9 +16146,9 @@ function stopDaemon(format = "text") {
|
|
|
16083
16146
|
|
|
16084
16147
|
// src/commands/init.ts
|
|
16085
16148
|
import crypto3 from "crypto";
|
|
16086
|
-
import
|
|
16149
|
+
import fs16 from "fs";
|
|
16087
16150
|
import readline2 from "readline";
|
|
16088
|
-
import
|
|
16151
|
+
import path10 from "path";
|
|
16089
16152
|
var pendingServeHandoff = false;
|
|
16090
16153
|
function consumePendingServeHandoff() {
|
|
16091
16154
|
const pending = pendingServeHandoff;
|
|
@@ -16120,8 +16183,8 @@ var DEFAULT_QUOTA = {
|
|
|
16120
16183
|
var PROJECT_MARKERS = [".git", "canonry.yaml", "canonry.yml", "package.json"];
|
|
16121
16184
|
function cwdLooksLikeProject(dir) {
|
|
16122
16185
|
const home = process.env.HOME ?? "";
|
|
16123
|
-
if (home &&
|
|
16124
|
-
return PROJECT_MARKERS.some((marker) =>
|
|
16186
|
+
if (home && path10.resolve(dir) === path10.resolve(home)) return false;
|
|
16187
|
+
return PROJECT_MARKERS.some((marker) => fs16.existsSync(path10.join(dir, marker)));
|
|
16125
16188
|
}
|
|
16126
16189
|
var DEFAULT_AGENT_MODELS = {
|
|
16127
16190
|
anthropic: "anthropic/claude-sonnet-4-6",
|
|
@@ -16152,8 +16215,8 @@ async function initCommand(opts) {
|
|
|
16152
16215
|
return void 0;
|
|
16153
16216
|
}
|
|
16154
16217
|
const configDir = getConfigDir();
|
|
16155
|
-
if (!
|
|
16156
|
-
|
|
16218
|
+
if (!fs16.existsSync(configDir)) {
|
|
16219
|
+
fs16.mkdirSync(configDir, { recursive: true });
|
|
16157
16220
|
}
|
|
16158
16221
|
const bootstrapEnv = getBootstrapEnv(process.env, {
|
|
16159
16222
|
GEMINI_API_KEY: opts?.geminiKey,
|
|
@@ -16275,7 +16338,7 @@ async function initCommand(opts) {
|
|
|
16275
16338
|
const rawApiKey = `cnry_${crypto3.randomBytes(16).toString("hex")}`;
|
|
16276
16339
|
const keyHash = crypto3.createHash("sha256").update(rawApiKey).digest("hex");
|
|
16277
16340
|
const keyPrefix = rawApiKey.slice(0, 9);
|
|
16278
|
-
const databasePath =
|
|
16341
|
+
const databasePath = path10.join(configDir, "data.db");
|
|
16279
16342
|
const db = createClient(databasePath);
|
|
16280
16343
|
migrate(db);
|
|
16281
16344
|
db.insert(apiKeys).values({
|
|
@@ -16503,8 +16566,8 @@ async function repairPathsOnce(db, name, repair) {
|
|
|
16503
16566
|
}
|
|
16504
16567
|
|
|
16505
16568
|
// src/agent-plugin.ts
|
|
16506
|
-
import
|
|
16507
|
-
import
|
|
16569
|
+
import fs17 from "fs";
|
|
16570
|
+
import path11 from "path";
|
|
16508
16571
|
var CANONRY_PLUGIN_ID = "canonry@canonry";
|
|
16509
16572
|
var REQUIRED_SKILLS = ["canonry", "aero"];
|
|
16510
16573
|
var AGENT_PLUGINS_SCHEMA = "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json";
|
|
@@ -16548,7 +16611,7 @@ function isCanonryPluginId(value) {
|
|
|
16548
16611
|
}
|
|
16549
16612
|
function readJson(filePath) {
|
|
16550
16613
|
try {
|
|
16551
|
-
return JSON.parse(
|
|
16614
|
+
return JSON.parse(fs17.readFileSync(filePath, "utf8"));
|
|
16552
16615
|
} catch {
|
|
16553
16616
|
return null;
|
|
16554
16617
|
}
|
|
@@ -16567,7 +16630,7 @@ function readCodexPluginConfigured(filePath) {
|
|
|
16567
16630
|
try {
|
|
16568
16631
|
let currentPlugin = null;
|
|
16569
16632
|
let inPluginsTable = false;
|
|
16570
|
-
for (const line of
|
|
16633
|
+
for (const line of fs17.readFileSync(filePath, "utf8").split(/\r?\n/)) {
|
|
16571
16634
|
const section = /^\s*\[plugins\.(?:"([^"]+)"|'([^']+)'|(\S+))\]\s*(?:#.*)?$/.exec(line);
|
|
16572
16635
|
if (section) {
|
|
16573
16636
|
const captures = section.slice(1, 4);
|
|
@@ -16600,21 +16663,21 @@ function readCodexPluginConfigured(filePath) {
|
|
|
16600
16663
|
}
|
|
16601
16664
|
}
|
|
16602
16665
|
function verifiedPluginVersion(root, client) {
|
|
16603
|
-
if (client === "codex" &&
|
|
16604
|
-
const manifest2 = readJson(
|
|
16666
|
+
if (client === "codex" && fs17.existsSync(path11.join(root, "plugin.json"))) {
|
|
16667
|
+
const manifest2 = readJson(path11.join(root, "plugin.json"));
|
|
16605
16668
|
if (manifest2 && typeof manifest2 === "object") {
|
|
16606
16669
|
const plugin2 = manifest2;
|
|
16607
16670
|
if (plugin2.$schema === AGENT_PLUGINS_SCHEMA) {
|
|
16608
16671
|
if (plugin2.name !== "canonry" || typeof plugin2.version !== "string" || !parseSemver(plugin2.version)) {
|
|
16609
16672
|
return null;
|
|
16610
16673
|
}
|
|
16611
|
-
const mcp2 = readJson(
|
|
16674
|
+
const mcp2 = readJson(path11.join(root, "mcp.json"));
|
|
16612
16675
|
if (!mcp2 || typeof mcp2 !== "object") return null;
|
|
16613
16676
|
const portableMcp = mcp2;
|
|
16614
16677
|
if (portableMcp.$schema !== AGENT_PLUGINS_MCP_SCHEMA || portableMcp.mcpServers?.canonry?.type !== "stdio" || portableMcp.mcpServers.canonry.command !== "canonry-mcp") {
|
|
16615
16678
|
return null;
|
|
16616
16679
|
}
|
|
16617
|
-
return REQUIRED_SKILLS.every((name) =>
|
|
16680
|
+
return REQUIRED_SKILLS.every((name) => fs17.existsSync(path11.join(root, "skills", name, "SKILL.md"))) ? plugin2.version : null;
|
|
16618
16681
|
}
|
|
16619
16682
|
if (typeof plugin2.$schema === "string" && plugin2.$schema.startsWith("https://agent-plugins.org/schemas/")) {
|
|
16620
16683
|
return null;
|
|
@@ -16622,20 +16685,20 @@ function verifiedPluginVersion(root, client) {
|
|
|
16622
16685
|
}
|
|
16623
16686
|
}
|
|
16624
16687
|
const manifestDir = client === "claude-code" ? ".claude-plugin" : ".codex-plugin";
|
|
16625
|
-
const manifest = readJson(
|
|
16688
|
+
const manifest = readJson(path11.join(root, manifestDir, "plugin.json"));
|
|
16626
16689
|
if (!manifest || typeof manifest !== "object") return null;
|
|
16627
16690
|
const plugin = manifest;
|
|
16628
16691
|
if (plugin.name !== "canonry" || typeof plugin.version !== "string" || !parseSemver(plugin.version) || plugin.skills !== "./skills/" || plugin.mcpServers !== "./.mcp.json") {
|
|
16629
16692
|
return null;
|
|
16630
16693
|
}
|
|
16631
|
-
const mcp = readJson(
|
|
16694
|
+
const mcp = readJson(path11.join(root, ".mcp.json"));
|
|
16632
16695
|
if (!mcp || typeof mcp !== "object") return null;
|
|
16633
16696
|
const canonryServer = mcp.mcpServers?.canonry;
|
|
16634
16697
|
if (canonryServer?.command !== "canonry-mcp") return null;
|
|
16635
|
-
return REQUIRED_SKILLS.every((name) =>
|
|
16698
|
+
return REQUIRED_SKILLS.every((name) => fs17.existsSync(path11.join(root, "skills", name, "SKILL.md"))) ? plugin.version : null;
|
|
16636
16699
|
}
|
|
16637
16700
|
function readClaudeUserInstallPaths(claudeConfigDir) {
|
|
16638
|
-
const parsed = readJson(
|
|
16701
|
+
const parsed = readJson(path11.join(claudeConfigDir, "plugins", "installed_plugins.json"));
|
|
16639
16702
|
if (!parsed || typeof parsed !== "object") return [];
|
|
16640
16703
|
const plugins = parsed.plugins;
|
|
16641
16704
|
if (!plugins || typeof plugins !== "object") return [];
|
|
@@ -16649,7 +16712,7 @@ function readClaudeUserInstallPaths(claudeConfigDir) {
|
|
|
16649
16712
|
}
|
|
16650
16713
|
function listDirectories(dir) {
|
|
16651
16714
|
try {
|
|
16652
|
-
return
|
|
16715
|
+
return fs17.readdirSync(dir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => path11.join(dir, entry.name));
|
|
16653
16716
|
} catch {
|
|
16654
16717
|
return [];
|
|
16655
16718
|
}
|
|
@@ -16661,8 +16724,8 @@ function verifiedClaudeVersion(claudeConfigDir) {
|
|
|
16661
16724
|
return newestVersion(readClaudeUserInstallPaths(claudeConfigDir).map((installPath) => verifiedPluginVersion(installPath, "claude-code")).filter((version) => version !== null));
|
|
16662
16725
|
}
|
|
16663
16726
|
function verifiedCodexVersion(codexHome) {
|
|
16664
|
-
const pluginCache =
|
|
16665
|
-
const versionedRoots = listDirectories(pluginCache).map((installPath) => ({ installPath, version:
|
|
16727
|
+
const pluginCache = path11.join(codexHome, "plugins", "cache", "canonry", "canonry");
|
|
16728
|
+
const versionedRoots = listDirectories(pluginCache).map((installPath) => ({ installPath, version: path11.basename(installPath) })).filter((candidate) => parseSemver(candidate.version) !== null).sort((left, right) => compareSemver(left.version, right.version));
|
|
16666
16729
|
const active = versionedRoots.at(-1);
|
|
16667
16730
|
if (!active) return verifiedPluginVersion(pluginCache, "codex");
|
|
16668
16731
|
const manifestVersion = verifiedPluginVersion(active.installPath, "codex");
|
|
@@ -16670,12 +16733,12 @@ function verifiedCodexVersion(codexHome) {
|
|
|
16670
16733
|
}
|
|
16671
16734
|
function detectCanonryAgentPlugin(opts = {}) {
|
|
16672
16735
|
const home = opts.home?.trim() || null;
|
|
16673
|
-
const claudeConfigDir = opts.claudeConfigDir?.trim() || (home ?
|
|
16674
|
-
const codexHome = opts.codexHome?.trim() || (home ?
|
|
16736
|
+
const claudeConfigDir = opts.claudeConfigDir?.trim() || (home ? path11.join(home, ".claude") : null);
|
|
16737
|
+
const codexHome = opts.codexHome?.trim() || (home ? path11.join(home, ".codex") : null);
|
|
16675
16738
|
const configuredClients = [];
|
|
16676
16739
|
const verifiedClients = [];
|
|
16677
16740
|
const verifiedClientVersions = {};
|
|
16678
|
-
const claudeConfigured = claudeConfigDir ? readClaudePluginConfigured(
|
|
16741
|
+
const claudeConfigured = claudeConfigDir ? readClaudePluginConfigured(path11.join(claudeConfigDir, "settings.json")) : false;
|
|
16679
16742
|
if (claudeConfigured) {
|
|
16680
16743
|
configuredClients.push("claude-code");
|
|
16681
16744
|
const version = claudeConfigDir ? verifiedClaudeVersion(claudeConfigDir) : null;
|
|
@@ -16684,7 +16747,7 @@ function detectCanonryAgentPlugin(opts = {}) {
|
|
|
16684
16747
|
verifiedClientVersions["claude-code"] = version;
|
|
16685
16748
|
}
|
|
16686
16749
|
}
|
|
16687
|
-
const codexConfigured = codexHome ? readCodexPluginConfigured(
|
|
16750
|
+
const codexConfigured = codexHome ? readCodexPluginConfigured(path11.join(codexHome, "config.toml")) : false;
|
|
16688
16751
|
if (codexConfigured) {
|
|
16689
16752
|
configuredClients.push("codex");
|
|
16690
16753
|
const version = codexHome ? verifiedCodexVersion(codexHome) : null;
|
|
@@ -18122,14 +18185,14 @@ Usage: ${usage}`, {
|
|
|
18122
18185
|
}
|
|
18123
18186
|
}
|
|
18124
18187
|
];
|
|
18125
|
-
function siteHealthAlias(sourcePath,
|
|
18188
|
+
function siteHealthAlias(sourcePath, path12, usage) {
|
|
18126
18189
|
const source = TECHNICAL_AEO_CLI_COMMANDS_BASE.find(
|
|
18127
18190
|
(candidate) => candidate.path.join(" ") === sourcePath.join(" ")
|
|
18128
18191
|
);
|
|
18129
18192
|
if (!source) throw new Error(`Missing Site Health compatibility source: ${sourcePath.join(" ")}`);
|
|
18130
18193
|
return {
|
|
18131
18194
|
...source,
|
|
18132
|
-
path:
|
|
18195
|
+
path: path12,
|
|
18133
18196
|
usage,
|
|
18134
18197
|
run: async (input) => {
|
|
18135
18198
|
try {
|
|
@@ -18140,7 +18203,7 @@ function siteHealthAlias(sourcePath, path13, usage) {
|
|
|
18140
18203
|
throw usageError(`${firstLine}
|
|
18141
18204
|
Usage: ${usage}`, {
|
|
18142
18205
|
message: error.message,
|
|
18143
|
-
details: { ...error.details, command:
|
|
18206
|
+
details: { ...error.details, command: path12.join("."), usage }
|
|
18144
18207
|
});
|
|
18145
18208
|
}
|
|
18146
18209
|
}
|
|
@@ -18423,7 +18486,7 @@ function printVisibilityStats(data) {
|
|
|
18423
18486
|
}
|
|
18424
18487
|
|
|
18425
18488
|
// src/cli-commands/visibility-stats.ts
|
|
18426
|
-
var
|
|
18489
|
+
var USAGE4 = "canonry visibility-stats <project> [--since <iso>] [--until <iso>] [--month <YYYY-MM>] [--last-runs <n>] [--by-provider] [--share-of-voice] [--query-class branded|non-brand] [--format json|jsonl]";
|
|
18427
18490
|
function parseQueryClass(value) {
|
|
18428
18491
|
if (value === void 0 || value === "") return void 0;
|
|
18429
18492
|
if (value !== "branded" && value !== "non-brand") {
|
|
@@ -18460,7 +18523,7 @@ Usage: ${COMPARE_USAGE}`, {
|
|
|
18460
18523
|
var VISIBILITY_STATS_CLI_COMMANDS = [
|
|
18461
18524
|
{
|
|
18462
18525
|
path: ["visibility-stats"],
|
|
18463
|
-
usage:
|
|
18526
|
+
usage: USAGE4,
|
|
18464
18527
|
options: {
|
|
18465
18528
|
since: stringOption(),
|
|
18466
18529
|
until: stringOption(),
|
|
@@ -18471,14 +18534,14 @@ var VISIBILITY_STATS_CLI_COMMANDS = [
|
|
|
18471
18534
|
"query-class": stringOption()
|
|
18472
18535
|
},
|
|
18473
18536
|
run: async (input) => {
|
|
18474
|
-
const project2 = requireProject(input, "visibility-stats",
|
|
18537
|
+
const project2 = requireProject(input, "visibility-stats", USAGE4);
|
|
18475
18538
|
await showVisibilityStats(project2, {
|
|
18476
18539
|
since: getString(input.values, "since"),
|
|
18477
18540
|
until: getString(input.values, "until"),
|
|
18478
18541
|
month: getString(input.values, "month"),
|
|
18479
18542
|
lastRuns: parseIntegerOption(input, "last-runs", {
|
|
18480
18543
|
command: "visibility-stats",
|
|
18481
|
-
usage:
|
|
18544
|
+
usage: USAGE4,
|
|
18482
18545
|
message: "--last-runs must be an integer"
|
|
18483
18546
|
}),
|
|
18484
18547
|
byProvider: getBoolean(input.values, "by-provider"),
|
|
@@ -18513,7 +18576,7 @@ var VISIBILITY_STATS_CLI_COMMANDS = [
|
|
|
18513
18576
|
];
|
|
18514
18577
|
|
|
18515
18578
|
// src/cli-commands/wordpress.ts
|
|
18516
|
-
import
|
|
18579
|
+
import fs18 from "fs";
|
|
18517
18580
|
|
|
18518
18581
|
// src/commands/wordpress.ts
|
|
18519
18582
|
function getClient28() {
|
|
@@ -18752,12 +18815,12 @@ async function wordpressSetMeta(project2, body) {
|
|
|
18752
18815
|
printPageDetail(result);
|
|
18753
18816
|
}
|
|
18754
18817
|
async function wordpressBulkSetMeta(project2, opts) {
|
|
18755
|
-
const
|
|
18756
|
-
const
|
|
18757
|
-
const filePath =
|
|
18818
|
+
const fs19 = await import("fs/promises");
|
|
18819
|
+
const path12 = await import("path");
|
|
18820
|
+
const filePath = path12.resolve(opts.from);
|
|
18758
18821
|
let raw;
|
|
18759
18822
|
try {
|
|
18760
|
-
raw = await
|
|
18823
|
+
raw = await fs19.readFile(filePath, "utf8");
|
|
18761
18824
|
} catch {
|
|
18762
18825
|
throw new CliError({
|
|
18763
18826
|
code: "FILE_READ_ERROR",
|
|
@@ -18854,13 +18917,13 @@ async function wordpressSetSchema(project2, body) {
|
|
|
18854
18917
|
printManualAssist(`Schema update for "${body.slug}"`, result);
|
|
18855
18918
|
}
|
|
18856
18919
|
async function wordpressSchemaDeploy(project2, opts) {
|
|
18857
|
-
const
|
|
18858
|
-
const
|
|
18920
|
+
const fs19 = await import("fs/promises");
|
|
18921
|
+
const path12 = await import("path");
|
|
18859
18922
|
const yaml = await loadYamlModule();
|
|
18860
|
-
const filePath =
|
|
18923
|
+
const filePath = path12.resolve(opts.profile);
|
|
18861
18924
|
let raw;
|
|
18862
18925
|
try {
|
|
18863
|
-
raw = await
|
|
18926
|
+
raw = await fs19.readFile(filePath, "utf8");
|
|
18864
18927
|
} catch {
|
|
18865
18928
|
throw new CliError({
|
|
18866
18929
|
code: "FILE_READ_ERROR",
|
|
@@ -18965,13 +19028,13 @@ async function wordpressOnboard(project2, opts) {
|
|
|
18965
19028
|
}
|
|
18966
19029
|
let profileData;
|
|
18967
19030
|
if (opts.profile) {
|
|
18968
|
-
const
|
|
18969
|
-
const
|
|
19031
|
+
const fs19 = await import("fs/promises");
|
|
19032
|
+
const path12 = await import("path");
|
|
18970
19033
|
const yaml = await loadYamlModule();
|
|
18971
|
-
const filePath =
|
|
19034
|
+
const filePath = path12.resolve(opts.profile);
|
|
18972
19035
|
let raw;
|
|
18973
19036
|
try {
|
|
18974
|
-
raw = await
|
|
19037
|
+
raw = await fs19.readFile(filePath, "utf8");
|
|
18975
19038
|
} catch {
|
|
18976
19039
|
throw new CliError({
|
|
18977
19040
|
code: "FILE_READ_ERROR",
|
|
@@ -19120,7 +19183,7 @@ function resolveContent(input, command, usage, options) {
|
|
|
19120
19183
|
}
|
|
19121
19184
|
if (contentFile) {
|
|
19122
19185
|
try {
|
|
19123
|
-
return
|
|
19186
|
+
return fs18.readFileSync(contentFile, "utf-8");
|
|
19124
19187
|
} catch (error) {
|
|
19125
19188
|
const message = describeError(error);
|
|
19126
19189
|
throw usageError(`Error: could not read --content-file "${contentFile}": ${message}`, {
|
|
@@ -20429,7 +20492,6 @@ var REGISTERED_CLI_COMMANDS = [
|
|
|
20429
20492
|
...BACKLINKS_CLI_COMMANDS,
|
|
20430
20493
|
...SYSTEM_CLI_COMMANDS,
|
|
20431
20494
|
...PROJECT_CLI_COMMANDS,
|
|
20432
|
-
...REPORT_CLI_COMMANDS,
|
|
20433
20495
|
...ORGANIC_EVIDENCE_CLI_COMMANDS,
|
|
20434
20496
|
...QUERY_CLI_COMMANDS,
|
|
20435
20497
|
...KEYWORD_CLI_COMMANDS,
|
|
@@ -20494,7 +20556,7 @@ function buildSetupNudgeLine(input) {
|
|
|
20494
20556
|
|
|
20495
20557
|
// src/cli.ts
|
|
20496
20558
|
import { createRequire as createRequire2 } from "module";
|
|
20497
|
-
var
|
|
20559
|
+
var USAGE5 = `
|
|
20498
20560
|
cnry \u2014 Page Health and AI visibility CLI ('canonry' also works)
|
|
20499
20561
|
|
|
20500
20562
|
Usage: cnry <command> [options]
|
|
@@ -20608,7 +20670,7 @@ function telemetryErrorCode(err) {
|
|
|
20608
20670
|
}
|
|
20609
20671
|
async function runCli(args = process.argv.slice(2)) {
|
|
20610
20672
|
if (args.length === 0 || args[0] === "--help" || args[0] === "-h") {
|
|
20611
|
-
console.log(
|
|
20673
|
+
console.log(USAGE5);
|
|
20612
20674
|
return 0;
|
|
20613
20675
|
}
|
|
20614
20676
|
if (args.includes("--version") || args.includes("-v")) {
|