@ainyc/canonry 4.154.1 → 4.156.0
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/AGENTS.md +2 -2
- package/assets/agent-workspace/skills/aero/references/orchestration.md +3 -3
- package/assets/agent-workspace/skills/canonry/SKILL.md +2 -2
- package/assets/agent-workspace/skills/canonry/references/canonry-cli.md +27 -7
- package/assets/assets/{AuditHistoryPanel-WKf-vvmg.js → AuditHistoryPanel-gmJMVlXX.js} +1 -1
- package/assets/assets/{BacklinksPage-By06DbHN.js → BacklinksPage-Bv2sTCyN.js} +1 -1
- package/assets/assets/{HistoryPage-C_PcPiyX.js → HistoryPage-13q7GkiI.js} +1 -1
- package/assets/assets/{MeasurementPropertyPage-BHnNegEa.js → MeasurementPropertyPage-BgBqXT1p.js} +1 -1
- package/assets/assets/ProjectPage-EsGwKhot.js +15 -0
- package/assets/assets/{RunRow-9qn5Eer_.js → RunRow-bPzapIGJ.js} +1 -1
- package/assets/assets/{RunsPage-FxMTprK1.js → RunsPage-Dqfz18lH.js} +1 -1
- package/assets/assets/{SettingsPage-Cr7_PrNW.js → SettingsPage-_wMecn-V.js} +1 -1
- package/assets/assets/{TrafficPage-D0qk9uMN.js → TrafficPage-RYBdsOLB.js} +1 -1
- package/assets/assets/{TrafficSourceDetailPage-CXa_3_Gy.js → TrafficSourceDetailPage-DpeMMyKU.js} +1 -1
- package/assets/assets/{extract-error-message-RkTEH1Vb.js → extract-error-message-DUarAlyD.js} +1 -1
- package/assets/assets/index-BxegEwic.css +1 -0
- package/assets/assets/index-DP-fT2uI.js +83 -0
- package/assets/assets/react-sigma_core.esm.min-DnypJl5w.js +312 -0
- package/assets/assets/vendor-lucide-BtYdosYk.js +1 -0
- package/assets/assets/{vendor-markdown-CpDJch4G.js → vendor-markdown-D274_wsI.js} +1 -1
- package/assets/assets/{vendor-radix-DnzzbllM.js → vendor-radix-BxMXSwbC.js} +1 -1
- package/assets/assets/{vendor-recharts-33ACLxsD.js → vendor-recharts-C4ktw_aE.js} +1 -1
- package/assets/assets/{vendor-tanstack-BjS7iY3g.js → vendor-tanstack-pFxw0eGP.js} +1 -1
- package/assets/index.html +7 -7
- package/dist/{chunk-KRMLGFY3.js → chunk-ABNN2ZVR.js} +2438 -114
- package/dist/{chunk-NNW7TJUZ.js → chunk-AMPT2EP7.js} +527 -5
- package/dist/{chunk-ANSQ6JNO.js → chunk-CPASKGUE.js} +563 -4
- package/dist/{chunk-JOP2DGXL.js → chunk-ZWESOFA5.js} +1230 -310
- package/dist/cli.js +742 -8
- package/dist/index.js +4 -4
- package/dist/{intelligence-service-ZY5KOSZV.js → intelligence-service-WHC7B32V.js} +2 -2
- package/dist/mcp.js +4 -4
- package/package.json +11 -9
- package/assets/assets/ProjectPage-VTbW21dz.js +0 -12
- package/assets/assets/index-DyL54s8x.css +0 -1
- package/assets/assets/index-nSWJ9yka.js +0 -83
- package/assets/assets/vendor-lucide-ByU7vBHt.js +0 -1
package/dist/cli.js
CHANGED
|
@@ -30,7 +30,7 @@ import {
|
|
|
30
30
|
showFirstRunNotice,
|
|
31
31
|
trackCliCommandFinished,
|
|
32
32
|
trackEvent
|
|
33
|
-
} from "./chunk-
|
|
33
|
+
} from "./chunk-ZWESOFA5.js";
|
|
34
34
|
import {
|
|
35
35
|
CliError,
|
|
36
36
|
EXIT_SYSTEM_ERROR,
|
|
@@ -50,7 +50,7 @@ import {
|
|
|
50
50
|
saveConfigPatch,
|
|
51
51
|
systemError,
|
|
52
52
|
usageError
|
|
53
|
-
} from "./chunk-
|
|
53
|
+
} from "./chunk-CPASKGUE.js";
|
|
54
54
|
import {
|
|
55
55
|
apiKeys,
|
|
56
56
|
createClient,
|
|
@@ -58,7 +58,7 @@ import {
|
|
|
58
58
|
projects,
|
|
59
59
|
queries,
|
|
60
60
|
renderReportHtml
|
|
61
|
-
} from "./chunk-
|
|
61
|
+
} from "./chunk-ABNN2ZVR.js";
|
|
62
62
|
import {
|
|
63
63
|
AdsDeliverySnapshotStatuses,
|
|
64
64
|
AdsHistoricalCampaignRollupStatuses,
|
|
@@ -122,7 +122,7 @@ import {
|
|
|
122
122
|
providerQuotaPolicySchema,
|
|
123
123
|
resolveProviderInput,
|
|
124
124
|
winnabilityClassSchema
|
|
125
|
-
} from "./chunk-
|
|
125
|
+
} from "./chunk-AMPT2EP7.js";
|
|
126
126
|
|
|
127
127
|
// src/cli.ts
|
|
128
128
|
import { pathToFileURL } from "url";
|
|
@@ -13829,11 +13829,339 @@ async function technicalAeoTrend(project, opts) {
|
|
|
13829
13829
|
}
|
|
13830
13830
|
console.log(lines.join("\n"));
|
|
13831
13831
|
}
|
|
13832
|
+
async function technicalAeoCrawl(project, opts) {
|
|
13833
|
+
const crawl = await getClient27().getTechnicalAeoCrawl(project, { runId: opts.runId });
|
|
13834
|
+
if (isMachineFormat(opts.format)) {
|
|
13835
|
+
console.log(JSON.stringify(crawl, null, 2));
|
|
13836
|
+
return;
|
|
13837
|
+
}
|
|
13838
|
+
if (!crawl.hasCrawlData) {
|
|
13839
|
+
console.log(`No persisted site crawl for "${project}". Run \`canonry technical-aeo run ${project}\` first.`);
|
|
13840
|
+
return;
|
|
13841
|
+
}
|
|
13842
|
+
const lines = [
|
|
13843
|
+
`Site crawl: ${crawl.complete ? "complete" : "partial"} (${crawl.runStatus ?? "unknown"})`,
|
|
13844
|
+
`Root: ${crawl.rootUrl ?? "-"}`,
|
|
13845
|
+
`Pages: ${crawl.counts.pagesFetched} fetched \xB7 ${crawl.counts.pagesDiscovered} discovered \xB7 ${crawl.counts.pagesEligible} eligible`,
|
|
13846
|
+
`Links: ${crawl.counts.edges} internal/external observations \xB7 ${crawl.counts.findings} findings`,
|
|
13847
|
+
`Dead links: ${formatDeadLinkState(crawl.deadLinks)}`
|
|
13848
|
+
];
|
|
13849
|
+
if (crawl.termination) lines.push(`Stopped: ${crawl.termination}`);
|
|
13850
|
+
console.log(lines.join("\n"));
|
|
13851
|
+
}
|
|
13852
|
+
async function technicalAeoSubgraph(project, opts) {
|
|
13853
|
+
const res = await getClient27().getSiteHealthSubgraph(project, opts);
|
|
13854
|
+
if (isMachineFormat(opts.format)) {
|
|
13855
|
+
console.log(JSON.stringify(res, null, 2));
|
|
13856
|
+
return;
|
|
13857
|
+
}
|
|
13858
|
+
if (!res.hasCrawlData) {
|
|
13859
|
+
console.log(`No persisted Site Health crawl for "${project}". Run \`canonry technical-aeo run ${project}\` first.`);
|
|
13860
|
+
return;
|
|
13861
|
+
}
|
|
13862
|
+
if (res.state !== "ready") {
|
|
13863
|
+
console.log(`Site Health subgraph details are unavailable for crawl ${res.runId ?? "-"}.`);
|
|
13864
|
+
return;
|
|
13865
|
+
}
|
|
13866
|
+
const focus = res.focusUrl ?? res.focusNodeKey ?? "crawl root";
|
|
13867
|
+
const totalQualifier = res.countAccuracy === "lower-bound" ? "at least " : "";
|
|
13868
|
+
const lines = [
|
|
13869
|
+
`Site Health subgraph: ${focus} (${res.hops} hop${res.hops === 1 ? "" : "s"})`,
|
|
13870
|
+
`${res.nodes.length} of ${totalQualifier}${res.totalNodes} node(s) \xB7 ${res.edges.length} of ${totalQualifier}${res.totalEdges} edge(s)`,
|
|
13871
|
+
`Crawl: ${res.complete ? "complete" : `partial (${res.termination ?? "incomplete"})`}`
|
|
13872
|
+
];
|
|
13873
|
+
if (res.countAccuracy === "lower-bound") lines.push("Count accuracy: lower bound; traversal reached a cap, so totals and omissions may be higher.");
|
|
13874
|
+
if (res.truncated) lines.push(`Truncated: at least ${res.omittedNodes} node(s) and ${res.omittedEdges} edge(s) omitted. Narrow or refocus the request.`);
|
|
13875
|
+
if (!res.complete) lines.push(`Partial crawl: ${res.termination ?? "incomplete"}. Results describe only persisted observations.`);
|
|
13876
|
+
console.log(lines.join("\n"));
|
|
13877
|
+
}
|
|
13878
|
+
async function technicalAeoPath(project, opts) {
|
|
13879
|
+
const res = await getClient27().getSiteHealthPath(project, opts);
|
|
13880
|
+
if (isMachineFormat(opts.format)) {
|
|
13881
|
+
console.log(JSON.stringify(res, null, 2));
|
|
13882
|
+
return;
|
|
13883
|
+
}
|
|
13884
|
+
if (res.state === "no-crawl") {
|
|
13885
|
+
console.log(`No persisted Site Health crawl for "${project}". Run \`canonry technical-aeo run ${project}\` first.`);
|
|
13886
|
+
return;
|
|
13887
|
+
}
|
|
13888
|
+
if (res.state === "details-unavailable") {
|
|
13889
|
+
console.log(`Site Health path details are unavailable for crawl ${res.runId ?? "-"}.`);
|
|
13890
|
+
return;
|
|
13891
|
+
}
|
|
13892
|
+
if (res.state === "unreachable") {
|
|
13893
|
+
const qualification = res.complete ? "" : ` in the partial crawl (${res.termination ?? "incomplete"}); this is not a site-wide conclusion`;
|
|
13894
|
+
console.log(`No directed followable path found${qualification} to ${res.to?.url ?? "the requested destination"} within ${res.maxDepth} hop(s).`);
|
|
13895
|
+
return;
|
|
13896
|
+
}
|
|
13897
|
+
if (res.state === "truncated") {
|
|
13898
|
+
const qualification = res.complete ? "" : ` The crawl is partial (${res.termination ?? "incomplete"}), so this is not a site-wide conclusion.`;
|
|
13899
|
+
console.log(`Path search reached its exploration cap before resolving ${res.to?.url ?? "the requested destination"}. Narrow the route or reduce max depth.${qualification}`);
|
|
13900
|
+
return;
|
|
13901
|
+
}
|
|
13902
|
+
const lines = [res.nodes.map((node) => node.url).join("\n\u2192 ")];
|
|
13903
|
+
if (!res.complete) lines.push("", `Partial crawl: ${res.termination ?? "incomplete"}. This path covers only persisted observations.`);
|
|
13904
|
+
console.log(lines.join("\n"));
|
|
13905
|
+
}
|
|
13906
|
+
async function technicalAeoChanges(project, opts) {
|
|
13907
|
+
const res = await getClient27().getSiteHealthChanges(project, opts);
|
|
13908
|
+
if (opts.format === "jsonl") {
|
|
13909
|
+
if (res.state === "ready") {
|
|
13910
|
+
const { changes, ...header } = res;
|
|
13911
|
+
emitJsonl([{ kind: "site-health-changes-header", ...header }]);
|
|
13912
|
+
emitJsonl(changes.map((record) => ({ project, fromRunId: res.fromRunId, toRunId: res.toRunId, ...record })));
|
|
13913
|
+
} else {
|
|
13914
|
+
emitJsonl([{ kind: "site-health-changes-header", ...res }]);
|
|
13915
|
+
}
|
|
13916
|
+
return;
|
|
13917
|
+
}
|
|
13918
|
+
if (isMachineFormat(opts.format)) {
|
|
13919
|
+
console.log(JSON.stringify(res, null, 2));
|
|
13920
|
+
return;
|
|
13921
|
+
}
|
|
13922
|
+
if (res.state === "unavailable") {
|
|
13923
|
+
console.log(`Site Health changes unavailable: ${res.reason.replaceAll("-", " ")}.`);
|
|
13924
|
+
return;
|
|
13925
|
+
}
|
|
13926
|
+
if (res.state === "incompatible") {
|
|
13927
|
+
console.log(`Site Health changes unavailable: snapshots use incompatible ${res.mismatchedVersions.join(", ")} versions.`);
|
|
13928
|
+
return;
|
|
13929
|
+
}
|
|
13930
|
+
const lines = [
|
|
13931
|
+
`Site Health changes: ${res.fromRunId} \u2192 ${res.toRunId}`,
|
|
13932
|
+
`Filters: ${res.filters.scope} \xB7 ${res.filters.change}`
|
|
13933
|
+
];
|
|
13934
|
+
if (res.summaryState === "exact" && res.summary !== null && res.total !== null) {
|
|
13935
|
+
lines.push(`Pages: +${res.summary.pages.added} / -${res.summary.pages.removed} / ${res.summary.pages.changed} changed`);
|
|
13936
|
+
lines.push(`Links: +${res.summary.links.added} / -${res.summary.links.removed} / ${res.summary.links.changed}`);
|
|
13937
|
+
lines.push(`${res.changes.length} of ${res.total} change(s)${res.nextCursor ? " (more available)" : ""}`);
|
|
13938
|
+
} else {
|
|
13939
|
+
lines.push(`Summary: omitted on continuation; ${res.changes.length} returned change(s)${res.nextCursor ? " (more available)" : ""}`);
|
|
13940
|
+
}
|
|
13941
|
+
if (res.changes.length > 0) {
|
|
13942
|
+
lines.push("", "Changes:");
|
|
13943
|
+
for (const record of res.changes) lines.push(` ${formatSiteHealthChangeRecord(record)}`);
|
|
13944
|
+
}
|
|
13945
|
+
if (res.nextCursor) lines.push(`Next cursor: ${res.nextCursor}`);
|
|
13946
|
+
console.log(lines.join("\n"));
|
|
13947
|
+
}
|
|
13948
|
+
function formatSiteHealthChangeRecord(record) {
|
|
13949
|
+
const target = record.entity === "page" ? record.after?.url ?? record.before?.url ?? record.key : (() => {
|
|
13950
|
+
const edge = record.after ?? record.before;
|
|
13951
|
+
return edge ? `${edge.sourceUrl} \u2192 ${edge.targetUrl}` : record.key;
|
|
13952
|
+
})();
|
|
13953
|
+
const fields = record.changedFields.length > 0 ? ` (${record.changedFields.join(", ")})` : "";
|
|
13954
|
+
return `${record.entity} ${record.change}: ${target}${fields}`;
|
|
13955
|
+
}
|
|
13956
|
+
async function technicalAeoCrawlPages(project, opts) {
|
|
13957
|
+
const res = await getClient27().getTechnicalAeoCrawlPages(project, opts);
|
|
13958
|
+
if (opts.format === "jsonl") {
|
|
13959
|
+
emitJsonl([{
|
|
13960
|
+
kind: "technical-aeo-crawl-pages-header",
|
|
13961
|
+
project,
|
|
13962
|
+
runId: res.runId,
|
|
13963
|
+
total: res.total,
|
|
13964
|
+
nextCursor: res.nextCursor
|
|
13965
|
+
}]);
|
|
13966
|
+
emitJsonl(res.pages.map((page) => ({ project, runId: res.runId, ...page })));
|
|
13967
|
+
return;
|
|
13968
|
+
}
|
|
13969
|
+
if (opts.format === "json") {
|
|
13970
|
+
console.log(JSON.stringify(res, null, 2));
|
|
13971
|
+
return;
|
|
13972
|
+
}
|
|
13973
|
+
if (res.pages.length === 0) {
|
|
13974
|
+
console.log(`No persisted crawl pages for "${project}".`);
|
|
13975
|
+
return;
|
|
13976
|
+
}
|
|
13977
|
+
const lines = [`${res.pages.length} of ${res.total} crawl page(s)${res.nextCursor ? " (more available)" : ""}:`, ""];
|
|
13978
|
+
lines.push(`${"Depth".padStart(5)} ${"Score".padStart(5)} ${"Indexability".padEnd(15)} URL`);
|
|
13979
|
+
for (const page of res.pages) {
|
|
13980
|
+
lines.push(`${String(page.depth ?? "-").padStart(5)} ${String(page.auditScore ?? "-").padStart(5)} ${page.indexabilityState.slice(0, 15).padEnd(15)} ${page.url}`);
|
|
13981
|
+
}
|
|
13982
|
+
if (res.nextCursor) lines.push(`
|
|
13983
|
+
Next cursor: ${res.nextCursor}`);
|
|
13984
|
+
console.log(lines.join("\n"));
|
|
13985
|
+
}
|
|
13986
|
+
async function technicalAeoPageAudit(project, opts) {
|
|
13987
|
+
const res = await getClient27().getTechnicalAeoPageAudit(project, {
|
|
13988
|
+
runId: opts.runId,
|
|
13989
|
+
nodeKey: opts.nodeKey,
|
|
13990
|
+
url: opts.url
|
|
13991
|
+
});
|
|
13992
|
+
if (isMachineFormat(opts.format)) {
|
|
13993
|
+
console.log(JSON.stringify(res, null, 2));
|
|
13994
|
+
return;
|
|
13995
|
+
}
|
|
13996
|
+
if (res.state === "no-crawl") {
|
|
13997
|
+
console.log(`No persisted Site Health crawl for "${project}". Run \`canonry technical-aeo run ${project}\` first.`);
|
|
13998
|
+
return;
|
|
13999
|
+
}
|
|
14000
|
+
const provenance = [`Run: ${res.runId} \xB7 ${res.complete ? "complete" : "partial"}`];
|
|
14001
|
+
if (!res.complete) provenance.push(`Partial crawl: ${res.termination ?? "termination reason unavailable"}`);
|
|
14002
|
+
if (res.state === "details-unavailable") {
|
|
14003
|
+
console.log([
|
|
14004
|
+
"Page audit details are unavailable for this retained crawl.",
|
|
14005
|
+
...provenance
|
|
14006
|
+
].join("\n"));
|
|
14007
|
+
return;
|
|
14008
|
+
}
|
|
14009
|
+
if (res.state === "not-found") {
|
|
14010
|
+
console.log([
|
|
14011
|
+
"The selected page was not found in this crawl.",
|
|
14012
|
+
...provenance
|
|
14013
|
+
].join("\n"));
|
|
14014
|
+
return;
|
|
14015
|
+
}
|
|
14016
|
+
const lines = [
|
|
14017
|
+
`Page audit: ${res.auditScore == null ? "not scored" : `${res.auditScore}/100`} \xB7 ${res.url}`,
|
|
14018
|
+
...provenance
|
|
14019
|
+
];
|
|
14020
|
+
if (res.state === "not-audited") {
|
|
14021
|
+
lines.push(`Audit evidence unavailable: page state is ${res.auditState}.`);
|
|
14022
|
+
console.log(lines.join("\n"));
|
|
14023
|
+
return;
|
|
14024
|
+
}
|
|
14025
|
+
if (res.evidenceState === "scores-only") {
|
|
14026
|
+
lines.push("Evidence: scores only; this historical row predates persisted finding prose.");
|
|
14027
|
+
}
|
|
14028
|
+
if (res.criticalDefects.length > 0) {
|
|
14029
|
+
lines.push("", "Score-independent defects:");
|
|
14030
|
+
for (const defect of res.criticalDefects) {
|
|
14031
|
+
lines.push(` [${defect.severity}] [${defect.id}] ${defect.detail}`);
|
|
14032
|
+
lines.push(` Fix: ${defect.recommendation}`);
|
|
14033
|
+
}
|
|
14034
|
+
}
|
|
14035
|
+
if (res.factors.length > 0) {
|
|
14036
|
+
lines.push("", "Factors:");
|
|
14037
|
+
for (const factor of res.factors) {
|
|
14038
|
+
lines.push(` ${factor.name}: ${factor.score}/100 (${factor.status})`);
|
|
14039
|
+
for (const finding of factor.findings) lines.push(` [${finding.code}] ${finding.message}`);
|
|
14040
|
+
for (const recommendation of factor.recommendations) lines.push(` Fix: ${recommendation}`);
|
|
14041
|
+
}
|
|
14042
|
+
}
|
|
14043
|
+
console.log(lines.join("\n"));
|
|
14044
|
+
}
|
|
14045
|
+
async function technicalAeoStructure(project, opts) {
|
|
14046
|
+
const res = await getClient27().getTechnicalAeoStructure(project, opts);
|
|
14047
|
+
if (opts.format === "jsonl") {
|
|
14048
|
+
emitJsonl([{
|
|
14049
|
+
kind: "technical-aeo-structure-header",
|
|
14050
|
+
project,
|
|
14051
|
+
runId: res.runId,
|
|
14052
|
+
parentPath: res.parentPath,
|
|
14053
|
+
returned: res.children.length,
|
|
14054
|
+
nextCursor: res.nextCursor
|
|
14055
|
+
}]);
|
|
14056
|
+
emitJsonl(res.children.map((child) => ({ project, runId: res.runId, parentPath: res.parentPath, ...child })));
|
|
14057
|
+
return;
|
|
14058
|
+
}
|
|
14059
|
+
if (opts.format === "json") {
|
|
14060
|
+
console.log(JSON.stringify(res, null, 2));
|
|
14061
|
+
return;
|
|
14062
|
+
}
|
|
14063
|
+
if (res.children.length === 0) {
|
|
14064
|
+
console.log(`No crawl structure below ${res.parentPath} for "${project}".`);
|
|
14065
|
+
return;
|
|
14066
|
+
}
|
|
14067
|
+
const lines = [`${res.children.length} child path(s) below ${res.parentPath}${res.nextCursor ? " (more available)" : ""}:`, ""];
|
|
14068
|
+
for (const child of res.children) {
|
|
14069
|
+
lines.push(`${String(child.pageCount).padStart(5)} pages ${String(child.inventoryEligibleCount).padStart(5)} eligible ${child.path}`);
|
|
14070
|
+
}
|
|
14071
|
+
if (res.nextCursor) lines.push(`
|
|
14072
|
+
Next cursor: ${res.nextCursor}`);
|
|
14073
|
+
console.log(lines.join("\n"));
|
|
14074
|
+
}
|
|
14075
|
+
async function technicalAeoInternalLinks(project, opts) {
|
|
14076
|
+
const res = await getClient27().getTechnicalAeoInternalLinks(project, opts);
|
|
14077
|
+
if (opts.format === "jsonl") {
|
|
14078
|
+
emitJsonl([{
|
|
14079
|
+
kind: "technical-aeo-internal-links-header",
|
|
14080
|
+
project,
|
|
14081
|
+
runId: res.runId,
|
|
14082
|
+
total: res.total,
|
|
14083
|
+
nextCursor: res.nextCursor
|
|
14084
|
+
}]);
|
|
14085
|
+
emitJsonl(res.edges.map((edge) => ({ project, runId: res.runId, ...edge })));
|
|
14086
|
+
return;
|
|
14087
|
+
}
|
|
14088
|
+
if (opts.format === "json") {
|
|
14089
|
+
console.log(JSON.stringify(res, null, 2));
|
|
14090
|
+
return;
|
|
14091
|
+
}
|
|
14092
|
+
if (res.edges.length === 0) {
|
|
14093
|
+
console.log(`No persisted internal links for "${project}".`);
|
|
14094
|
+
return;
|
|
14095
|
+
}
|
|
14096
|
+
const lines = [`${res.edges.length} of ${res.total} internal link(s)${res.nextCursor ? " (more available)" : ""}:`, ""];
|
|
14097
|
+
for (const edge of res.edges) {
|
|
14098
|
+
lines.push(`${edge.followable ? "follow" : "nofollow"} \xD7${edge.occurrences} ${edge.sourceUrl} \u2192 ${edge.targetUrl}`);
|
|
14099
|
+
}
|
|
14100
|
+
if (res.nextCursor) lines.push(`
|
|
14101
|
+
Next cursor: ${res.nextCursor}`);
|
|
14102
|
+
console.log(lines.join("\n"));
|
|
14103
|
+
}
|
|
14104
|
+
async function technicalAeoLinkNeighbors(project, opts) {
|
|
14105
|
+
const res = await getClient27().getTechnicalAeoInternalLinkNeighbors(project, opts);
|
|
14106
|
+
if (isMachineFormat(opts.format)) {
|
|
14107
|
+
console.log(JSON.stringify(res, null, 2));
|
|
14108
|
+
return;
|
|
14109
|
+
}
|
|
14110
|
+
const title = res.url ?? res.nodeKey ?? "page";
|
|
14111
|
+
const lines = [`Internal links for ${title}:`, "", "Inbound:"];
|
|
14112
|
+
for (const edge of res.inbound) lines.push(` ${edge.sourceUrl} \u2192 ${edge.targetUrl}`);
|
|
14113
|
+
if (res.inbound.length === 0) lines.push(" (none)");
|
|
14114
|
+
if (res.inboundTruncated) lines.push(" (truncated)");
|
|
14115
|
+
lines.push("", "Outbound:");
|
|
14116
|
+
for (const edge of res.outbound) lines.push(` ${edge.sourceUrl} \u2192 ${edge.targetUrl}`);
|
|
14117
|
+
if (res.outbound.length === 0) lines.push(" (none)");
|
|
14118
|
+
if (res.outboundTruncated) lines.push(" (truncated)");
|
|
14119
|
+
console.log(lines.join("\n"));
|
|
14120
|
+
}
|
|
14121
|
+
async function technicalAeoDeadLinks(project, opts) {
|
|
14122
|
+
const res = await getClient27().getTechnicalAeoDeadLinks(project, opts);
|
|
14123
|
+
if (opts.format === "jsonl") {
|
|
14124
|
+
if ("deadLinks" in res) {
|
|
14125
|
+
const { deadLinks, ...header } = res;
|
|
14126
|
+
emitJsonl([{ kind: "technical-aeo-dead-links-header", ...header }]);
|
|
14127
|
+
emitJsonl(deadLinks.map((finding) => ({ project, runId: res.runId, state: res.state, ...finding })));
|
|
14128
|
+
} else {
|
|
14129
|
+
emitJsonl([{ kind: "technical-aeo-dead-links-header", ...res }]);
|
|
14130
|
+
}
|
|
14131
|
+
return;
|
|
14132
|
+
}
|
|
14133
|
+
if (isMachineFormat(opts.format)) {
|
|
14134
|
+
console.log(JSON.stringify(res, null, 2));
|
|
14135
|
+
return;
|
|
14136
|
+
}
|
|
14137
|
+
if (res.state === "disabled") {
|
|
14138
|
+
console.log(`Dead-link checks were disabled for crawl ${res.runId}. Re-run with \`--check-dead-links\` to enable them.`);
|
|
14139
|
+
return;
|
|
14140
|
+
}
|
|
14141
|
+
if (res.state === "unavailable") {
|
|
14142
|
+
console.log(`No dead-link check is available for "${project}".`);
|
|
14143
|
+
return;
|
|
14144
|
+
}
|
|
14145
|
+
const lines = [`Dead links: ${res.found} found from ${res.checked} checked (${res.state})`];
|
|
14146
|
+
for (const finding of res.deadLinks) lines.push(`${finding.sourceUrl ?? "-"} \u2192 ${finding.targetUrl ?? "-"}`);
|
|
14147
|
+
if (res.nextCursor) lines.push(`Next cursor: ${res.nextCursor}`);
|
|
14148
|
+
console.log(lines.join("\n"));
|
|
14149
|
+
}
|
|
14150
|
+
function formatDeadLinkState(value) {
|
|
14151
|
+
if (value.state === "disabled") return "disabled (opt in with --check-dead-links)";
|
|
14152
|
+
if (value.state === "unavailable") return "unavailable";
|
|
14153
|
+
return `${value.found ?? 0} found / ${value.checked ?? 0} checked (${value.state})`;
|
|
14154
|
+
}
|
|
13832
14155
|
async function technicalAeoRun(project, opts) {
|
|
13833
14156
|
const client = getClient27();
|
|
13834
14157
|
const { runId, status } = await client.triggerSiteAudit(project, {
|
|
13835
14158
|
sitemapUrl: opts.sitemapUrl,
|
|
13836
|
-
limit: opts.limit
|
|
14159
|
+
limit: opts.limit,
|
|
14160
|
+
maxPages: opts.maxPages,
|
|
14161
|
+
maxEdges: opts.maxEdges,
|
|
14162
|
+
maxDepth: opts.maxDepth,
|
|
14163
|
+
// Explicit opt-in: absence and false are both intentionally disabled.
|
|
14164
|
+
checkDeadLinks: opts.checkDeadLinks === true
|
|
13837
14165
|
});
|
|
13838
14166
|
if (!opts.wait) {
|
|
13839
14167
|
if (isMachineFormat(opts.format)) {
|
|
@@ -13865,7 +14193,24 @@ async function technicalAeoRun(project, opts) {
|
|
|
13865
14193
|
}
|
|
13866
14194
|
|
|
13867
14195
|
// src/cli-commands/technical-aeo.ts
|
|
13868
|
-
|
|
14196
|
+
function parseOptionalBoolean(input, key, config) {
|
|
14197
|
+
const value = getString(input.values, key);
|
|
14198
|
+
if (value === void 0) return void 0;
|
|
14199
|
+
if (value === "true") return true;
|
|
14200
|
+
if (value === "false") return false;
|
|
14201
|
+
throw usageError(`Error: --${key} must be true or false
|
|
14202
|
+
Usage: ${config.usage}`, {
|
|
14203
|
+
message: `--${key} must be true or false`,
|
|
14204
|
+
details: { command: config.command, usage: config.usage, option: key, value }
|
|
14205
|
+
});
|
|
14206
|
+
}
|
|
14207
|
+
function isSiteHealthScope(value) {
|
|
14208
|
+
return value === "all" || value === "pages" || value === "links";
|
|
14209
|
+
}
|
|
14210
|
+
function isSiteHealthChange(value) {
|
|
14211
|
+
return value === "all" || value === "added" || value === "removed" || value === "changed";
|
|
14212
|
+
}
|
|
14213
|
+
var TECHNICAL_AEO_CLI_COMMANDS_BASE = [
|
|
13869
14214
|
{
|
|
13870
14215
|
path: ["technical-aeo", "score"],
|
|
13871
14216
|
usage: "canonry technical-aeo score <project> [--run-id <id>] [--format json]",
|
|
@@ -13927,14 +14272,18 @@ var TECHNICAL_AEO_CLI_COMMANDS = [
|
|
|
13927
14272
|
},
|
|
13928
14273
|
{
|
|
13929
14274
|
path: ["technical-aeo", "run"],
|
|
13930
|
-
usage: "canonry technical-aeo run <project> [--sitemap-url <url>] [--
|
|
14275
|
+
usage: "canonry technical-aeo run <project> [--sitemap-url <url>] [--max-pages <n>] [--max-edges <n>] [--max-depth <n>] [--check-dead-links] [--wait] [--format json]",
|
|
13931
14276
|
options: {
|
|
13932
14277
|
"sitemap-url": stringOption(),
|
|
13933
14278
|
limit: stringOption(),
|
|
14279
|
+
"max-pages": stringOption(),
|
|
14280
|
+
"max-edges": stringOption(),
|
|
14281
|
+
"max-depth": stringOption(),
|
|
14282
|
+
"check-dead-links": { type: "boolean", default: false },
|
|
13934
14283
|
wait: { type: "boolean", default: false }
|
|
13935
14284
|
},
|
|
13936
14285
|
run: async (input) => {
|
|
13937
|
-
const usage = "canonry technical-aeo run <project> [--sitemap-url <url>] [--
|
|
14286
|
+
const usage = "canonry technical-aeo run <project> [--sitemap-url <url>] [--max-pages <n>] [--max-edges <n>] [--max-depth <n>] [--check-dead-links] [--wait] [--format json]";
|
|
13938
14287
|
const project = requireProject(input, "technical-aeo.run", usage);
|
|
13939
14288
|
await technicalAeoRun(project, {
|
|
13940
14289
|
sitemapUrl: getString(input.values, "sitemap-url"),
|
|
@@ -13943,12 +14292,397 @@ var TECHNICAL_AEO_CLI_COMMANDS = [
|
|
|
13943
14292
|
usage,
|
|
13944
14293
|
message: "--limit must be an integer"
|
|
13945
14294
|
}),
|
|
14295
|
+
maxPages: parseIntegerOption(input, "max-pages", {
|
|
14296
|
+
command: "technical-aeo.run",
|
|
14297
|
+
usage,
|
|
14298
|
+
message: "--max-pages must be an integer"
|
|
14299
|
+
}),
|
|
14300
|
+
maxEdges: parseIntegerOption(input, "max-edges", {
|
|
14301
|
+
command: "technical-aeo.run",
|
|
14302
|
+
usage,
|
|
14303
|
+
message: "--max-edges must be an integer"
|
|
14304
|
+
}),
|
|
14305
|
+
maxDepth: parseIntegerOption(input, "max-depth", {
|
|
14306
|
+
command: "technical-aeo.run",
|
|
14307
|
+
usage,
|
|
14308
|
+
message: "--max-depth must be an integer"
|
|
14309
|
+
}),
|
|
14310
|
+
checkDeadLinks: getBoolean(input.values, "check-dead-links"),
|
|
13946
14311
|
wait: getBoolean(input.values, "wait"),
|
|
13947
14312
|
format: input.format
|
|
13948
14313
|
});
|
|
13949
14314
|
}
|
|
14315
|
+
},
|
|
14316
|
+
{
|
|
14317
|
+
path: ["technical-aeo", "crawl"],
|
|
14318
|
+
usage: "canonry technical-aeo crawl <project> [--run-id <id>] [--format json]",
|
|
14319
|
+
options: { "run-id": stringOption() },
|
|
14320
|
+
run: async (input) => {
|
|
14321
|
+
const usage = "canonry technical-aeo crawl <project> [--run-id <id>] [--format json]";
|
|
14322
|
+
const project = requireProject(input, "technical-aeo.crawl", usage);
|
|
14323
|
+
await technicalAeoCrawl(project, { runId: getString(input.values, "run-id"), format: input.format });
|
|
14324
|
+
}
|
|
14325
|
+
},
|
|
14326
|
+
{
|
|
14327
|
+
path: ["technical-aeo", "subgraph"],
|
|
14328
|
+
usage: "canonry technical-aeo subgraph <project> [--run-id <id>] [--node-key <key>|--url <url>] [--hops <n>] [--max-nodes <n>] [--max-edges <n>] [--format json]",
|
|
14329
|
+
options: {
|
|
14330
|
+
"run-id": stringOption(),
|
|
14331
|
+
"node-key": stringOption(),
|
|
14332
|
+
url: stringOption(),
|
|
14333
|
+
hops: stringOption(),
|
|
14334
|
+
"max-nodes": stringOption(),
|
|
14335
|
+
"max-edges": stringOption()
|
|
14336
|
+
},
|
|
14337
|
+
run: async (input) => {
|
|
14338
|
+
const usage = "canonry technical-aeo subgraph <project> [--run-id <id>] [--node-key <key>|--url <url>] [--hops <n>] [--max-nodes <n>] [--max-edges <n>] [--format json]";
|
|
14339
|
+
const project = requireProject(input, "technical-aeo.subgraph", usage);
|
|
14340
|
+
const nodeKey = getString(input.values, "node-key");
|
|
14341
|
+
const url = getString(input.values, "url");
|
|
14342
|
+
if (nodeKey && url) {
|
|
14343
|
+
throw usageError(`Error: --node-key and --url cannot be combined
|
|
14344
|
+
Usage: ${usage}`, {
|
|
14345
|
+
message: "--node-key and --url cannot be combined",
|
|
14346
|
+
details: { command: "technical-aeo.subgraph", usage }
|
|
14347
|
+
});
|
|
14348
|
+
}
|
|
14349
|
+
await technicalAeoSubgraph(project, {
|
|
14350
|
+
runId: getString(input.values, "run-id"),
|
|
14351
|
+
nodeKey,
|
|
14352
|
+
url,
|
|
14353
|
+
hops: parseIntegerOption(input, "hops", { command: "technical-aeo.subgraph", usage, message: "--hops must be an integer" }),
|
|
14354
|
+
maxNodes: parseIntegerOption(input, "max-nodes", { command: "technical-aeo.subgraph", usage, message: "--max-nodes must be an integer" }),
|
|
14355
|
+
maxEdges: parseIntegerOption(input, "max-edges", { command: "technical-aeo.subgraph", usage, message: "--max-edges must be an integer" }),
|
|
14356
|
+
format: input.format
|
|
14357
|
+
});
|
|
14358
|
+
}
|
|
14359
|
+
},
|
|
14360
|
+
{
|
|
14361
|
+
path: ["technical-aeo", "path"],
|
|
14362
|
+
usage: "canonry technical-aeo path <project> (--to-node-key <key>|--to-url <url>) [--run-id <id>] [--from-node-key <key>|--from-url <url>] [--max-depth <n>] [--format json]",
|
|
14363
|
+
options: {
|
|
14364
|
+
"run-id": stringOption(),
|
|
14365
|
+
"from-node-key": stringOption(),
|
|
14366
|
+
"from-url": stringOption(),
|
|
14367
|
+
"to-node-key": stringOption(),
|
|
14368
|
+
"to-url": stringOption(),
|
|
14369
|
+
"max-depth": stringOption()
|
|
14370
|
+
},
|
|
14371
|
+
run: async (input) => {
|
|
14372
|
+
const usage = "canonry technical-aeo path <project> (--to-node-key <key>|--to-url <url>) [--run-id <id>] [--from-node-key <key>|--from-url <url>] [--max-depth <n>] [--format json]";
|
|
14373
|
+
const project = requireProject(input, "technical-aeo.path", usage);
|
|
14374
|
+
const fromNodeKey = getString(input.values, "from-node-key");
|
|
14375
|
+
const fromUrl = getString(input.values, "from-url");
|
|
14376
|
+
const toNodeKey = getString(input.values, "to-node-key");
|
|
14377
|
+
const toUrl = getString(input.values, "to-url");
|
|
14378
|
+
if (!toNodeKey && !toUrl) {
|
|
14379
|
+
throw usageError(`Error: --to-node-key or --to-url is required
|
|
14380
|
+
Usage: ${usage}`, {
|
|
14381
|
+
message: "--to-node-key or --to-url is required",
|
|
14382
|
+
details: { command: "technical-aeo.path", usage }
|
|
14383
|
+
});
|
|
14384
|
+
}
|
|
14385
|
+
if (fromNodeKey && fromUrl || toNodeKey && toUrl) {
|
|
14386
|
+
throw usageError(`Error: node-key and URL selectors cannot be combined
|
|
14387
|
+
Usage: ${usage}`, {
|
|
14388
|
+
message: "node-key and URL selectors cannot be combined",
|
|
14389
|
+
details: { command: "technical-aeo.path", usage }
|
|
14390
|
+
});
|
|
14391
|
+
}
|
|
14392
|
+
await technicalAeoPath(project, {
|
|
14393
|
+
runId: getString(input.values, "run-id"),
|
|
14394
|
+
fromNodeKey,
|
|
14395
|
+
fromUrl,
|
|
14396
|
+
toNodeKey,
|
|
14397
|
+
toUrl,
|
|
14398
|
+
maxDepth: parseIntegerOption(input, "max-depth", { command: "technical-aeo.path", usage, message: "--max-depth must be an integer" }),
|
|
14399
|
+
format: input.format
|
|
14400
|
+
});
|
|
14401
|
+
}
|
|
14402
|
+
},
|
|
14403
|
+
{
|
|
14404
|
+
path: ["technical-aeo", "changes"],
|
|
14405
|
+
usage: "canonry technical-aeo changes <project> [--from-run-id <id>] [--to-run-id <id>] [--scope all|pages|links] [--change all|added|removed|changed] [--cursor <cursor>] [--limit <n>] [--format json|jsonl]",
|
|
14406
|
+
options: {
|
|
14407
|
+
"from-run-id": stringOption(),
|
|
14408
|
+
"to-run-id": stringOption(),
|
|
14409
|
+
scope: stringOption(),
|
|
14410
|
+
change: stringOption(),
|
|
14411
|
+
cursor: stringOption(),
|
|
14412
|
+
limit: stringOption()
|
|
14413
|
+
},
|
|
14414
|
+
run: async (input) => {
|
|
14415
|
+
const usage = "canonry technical-aeo changes <project> [--from-run-id <id>] [--to-run-id <id>] [--scope all|pages|links] [--change all|added|removed|changed] [--cursor <cursor>] [--limit <n>] [--format json|jsonl]";
|
|
14416
|
+
const project = requireProject(input, "technical-aeo.changes", usage);
|
|
14417
|
+
const fromRunId = getString(input.values, "from-run-id");
|
|
14418
|
+
const toRunId = getString(input.values, "to-run-id");
|
|
14419
|
+
const scope = getString(input.values, "scope");
|
|
14420
|
+
const change = getString(input.values, "change");
|
|
14421
|
+
if (scope !== void 0 && !isSiteHealthScope(scope)) {
|
|
14422
|
+
throw usageError(`Error: --scope must be all, pages, or links
|
|
14423
|
+
Usage: ${usage}`, {
|
|
14424
|
+
message: "--scope must be all, pages, or links",
|
|
14425
|
+
details: { command: "technical-aeo.changes", usage, option: "scope", value: scope }
|
|
14426
|
+
});
|
|
14427
|
+
}
|
|
14428
|
+
if (change !== void 0 && !isSiteHealthChange(change)) {
|
|
14429
|
+
throw usageError(`Error: --change must be all, added, removed, or changed
|
|
14430
|
+
Usage: ${usage}`, {
|
|
14431
|
+
message: "--change must be all, added, removed, or changed",
|
|
14432
|
+
details: { command: "technical-aeo.changes", usage, option: "change", value: change }
|
|
14433
|
+
});
|
|
14434
|
+
}
|
|
14435
|
+
await technicalAeoChanges(project, {
|
|
14436
|
+
fromRunId,
|
|
14437
|
+
toRunId,
|
|
14438
|
+
scope,
|
|
14439
|
+
change,
|
|
14440
|
+
cursor: getString(input.values, "cursor"),
|
|
14441
|
+
limit: parseIntegerOption(input, "limit", { command: "technical-aeo.changes", usage, message: "--limit must be an integer" }),
|
|
14442
|
+
format: input.format
|
|
14443
|
+
});
|
|
14444
|
+
}
|
|
14445
|
+
},
|
|
14446
|
+
{
|
|
14447
|
+
path: ["technical-aeo", "crawl-pages"],
|
|
14448
|
+
usage: "canonry technical-aeo crawl-pages <project> [--run-id <id>] [--inventory-eligible true|false] [--fetch-state <state>] [--indexability-state <state>] [--audit-state <state>] [--sort url|path|score-asc|score-desc] [--cursor <cursor>] [--limit <n>] [--format json|jsonl]",
|
|
14449
|
+
options: {
|
|
14450
|
+
"run-id": stringOption(),
|
|
14451
|
+
"inventory-eligible": stringOption(),
|
|
14452
|
+
"fetch-state": stringOption(),
|
|
14453
|
+
"indexability-state": stringOption(),
|
|
14454
|
+
"audit-state": stringOption(),
|
|
14455
|
+
sort: stringOption(),
|
|
14456
|
+
cursor: stringOption(),
|
|
14457
|
+
limit: stringOption()
|
|
14458
|
+
},
|
|
14459
|
+
run: async (input) => {
|
|
14460
|
+
const usage = "canonry technical-aeo crawl-pages <project> [--run-id <id>] [--inventory-eligible true|false] [--fetch-state <state>] [--indexability-state <state>] [--audit-state <state>] [--sort url|path|score-asc|score-desc] [--cursor <cursor>] [--limit <n>] [--format json|jsonl]";
|
|
14461
|
+
const project = requireProject(input, "technical-aeo.crawl-pages", usage);
|
|
14462
|
+
const sort = getString(input.values, "sort");
|
|
14463
|
+
if (sort !== void 0 && !["url", "path", "score-asc", "score-desc"].includes(sort)) {
|
|
14464
|
+
throw usageError(`Error: --sort must be url, path, score-asc, or score-desc
|
|
14465
|
+
Usage: ${usage}`, {
|
|
14466
|
+
message: "--sort must be url, path, score-asc, or score-desc",
|
|
14467
|
+
details: { command: "technical-aeo.crawl-pages", usage, option: "sort", value: sort }
|
|
14468
|
+
});
|
|
14469
|
+
}
|
|
14470
|
+
await technicalAeoCrawlPages(project, {
|
|
14471
|
+
runId: getString(input.values, "run-id"),
|
|
14472
|
+
inventoryEligible: parseOptionalBoolean(input, "inventory-eligible", { command: "technical-aeo.crawl-pages", usage }),
|
|
14473
|
+
fetchState: getString(input.values, "fetch-state"),
|
|
14474
|
+
indexabilityState: getString(input.values, "indexability-state"),
|
|
14475
|
+
auditState: getString(input.values, "audit-state"),
|
|
14476
|
+
sort,
|
|
14477
|
+
cursor: getString(input.values, "cursor"),
|
|
14478
|
+
limit: parseIntegerOption(input, "limit", { command: "technical-aeo.crawl-pages", usage, message: "--limit must be an integer" }),
|
|
14479
|
+
format: input.format
|
|
14480
|
+
});
|
|
14481
|
+
}
|
|
14482
|
+
},
|
|
14483
|
+
{
|
|
14484
|
+
path: ["technical-aeo", "page-audit"],
|
|
14485
|
+
usage: "canonry technical-aeo page-audit <project> (--node-key <key>|--url <url>) [--run-id <id>] [--format json]",
|
|
14486
|
+
options: { "run-id": stringOption(), "node-key": stringOption(), url: stringOption() },
|
|
14487
|
+
run: async (input) => {
|
|
14488
|
+
const usage = "canonry technical-aeo page-audit <project> (--node-key <key>|--url <url>) [--run-id <id>] [--format json]";
|
|
14489
|
+
const project = requireProject(input, "technical-aeo.page-audit", usage);
|
|
14490
|
+
const nodeKey = getString(input.values, "node-key");
|
|
14491
|
+
const url = getString(input.values, "url");
|
|
14492
|
+
if (!nodeKey && !url) {
|
|
14493
|
+
throw usageError(`Error: --node-key or --url is required
|
|
14494
|
+
Usage: ${usage}`, {
|
|
14495
|
+
message: "--node-key or --url is required",
|
|
14496
|
+
details: { command: "technical-aeo.page-audit", usage }
|
|
14497
|
+
});
|
|
14498
|
+
}
|
|
14499
|
+
if (nodeKey && url) {
|
|
14500
|
+
throw usageError(`Error: --node-key and --url cannot be combined
|
|
14501
|
+
Usage: ${usage}`, {
|
|
14502
|
+
message: "--node-key and --url cannot be combined",
|
|
14503
|
+
details: { command: "technical-aeo.page-audit", usage }
|
|
14504
|
+
});
|
|
14505
|
+
}
|
|
14506
|
+
await technicalAeoPageAudit(project, {
|
|
14507
|
+
runId: getString(input.values, "run-id"),
|
|
14508
|
+
nodeKey,
|
|
14509
|
+
url,
|
|
14510
|
+
format: input.format
|
|
14511
|
+
});
|
|
14512
|
+
}
|
|
14513
|
+
},
|
|
14514
|
+
{
|
|
14515
|
+
path: ["technical-aeo", "structure"],
|
|
14516
|
+
usage: "canonry technical-aeo structure <project> [--run-id <id>] [--parent-path <path>] [--cursor <cursor>] [--limit <n>] [--format json|jsonl]",
|
|
14517
|
+
options: { "run-id": stringOption(), "parent-path": stringOption(), cursor: stringOption(), limit: stringOption() },
|
|
14518
|
+
run: async (input) => {
|
|
14519
|
+
const usage = "canonry technical-aeo structure <project> [--run-id <id>] [--parent-path <path>] [--cursor <cursor>] [--limit <n>] [--format json|jsonl]";
|
|
14520
|
+
const project = requireProject(input, "technical-aeo.structure", usage);
|
|
14521
|
+
await technicalAeoStructure(project, {
|
|
14522
|
+
runId: getString(input.values, "run-id"),
|
|
14523
|
+
parentPath: getString(input.values, "parent-path"),
|
|
14524
|
+
cursor: getString(input.values, "cursor"),
|
|
14525
|
+
limit: parseIntegerOption(input, "limit", { command: "technical-aeo.structure", usage, message: "--limit must be an integer" }),
|
|
14526
|
+
format: input.format
|
|
14527
|
+
});
|
|
14528
|
+
}
|
|
14529
|
+
},
|
|
14530
|
+
{
|
|
14531
|
+
path: ["technical-aeo", "links"],
|
|
14532
|
+
usage: "canonry technical-aeo links <project> [--run-id <id>] [--source-url <url>] [--target-url <url>] [--followable|--nofollow] [--cursor <cursor>] [--limit <n>] [--format json|jsonl]",
|
|
14533
|
+
options: {
|
|
14534
|
+
"run-id": stringOption(),
|
|
14535
|
+
"source-url": stringOption(),
|
|
14536
|
+
"target-url": stringOption(),
|
|
14537
|
+
followable: { type: "boolean", default: false },
|
|
14538
|
+
nofollow: { type: "boolean", default: false },
|
|
14539
|
+
cursor: stringOption(),
|
|
14540
|
+
limit: stringOption()
|
|
14541
|
+
},
|
|
14542
|
+
run: async (input) => {
|
|
14543
|
+
const usage = "canonry technical-aeo links <project> [--run-id <id>] [--source-url <url>] [--target-url <url>] [--followable|--nofollow] [--cursor <cursor>] [--limit <n>] [--format json|jsonl]";
|
|
14544
|
+
const project = requireProject(input, "technical-aeo.links", usage);
|
|
14545
|
+
const followable = getBoolean(input.values, "followable");
|
|
14546
|
+
const nofollow = getBoolean(input.values, "nofollow");
|
|
14547
|
+
if (followable && nofollow) {
|
|
14548
|
+
throw usageError(`Error: --followable and --nofollow cannot be combined
|
|
14549
|
+
Usage: ${usage}`, {
|
|
14550
|
+
message: "--followable and --nofollow cannot be combined",
|
|
14551
|
+
details: { command: "technical-aeo.links", usage }
|
|
14552
|
+
});
|
|
14553
|
+
}
|
|
14554
|
+
await technicalAeoInternalLinks(project, {
|
|
14555
|
+
runId: getString(input.values, "run-id"),
|
|
14556
|
+
sourceUrl: getString(input.values, "source-url"),
|
|
14557
|
+
targetUrl: getString(input.values, "target-url"),
|
|
14558
|
+
followable: followable ? true : nofollow ? false : void 0,
|
|
14559
|
+
cursor: getString(input.values, "cursor"),
|
|
14560
|
+
limit: parseIntegerOption(input, "limit", { command: "technical-aeo.links", usage, message: "--limit must be an integer" }),
|
|
14561
|
+
format: input.format
|
|
14562
|
+
});
|
|
14563
|
+
}
|
|
14564
|
+
},
|
|
14565
|
+
{
|
|
14566
|
+
path: ["technical-aeo", "links", "neighbors"],
|
|
14567
|
+
usage: "canonry technical-aeo links neighbors <project> (--node-key <key>|--url <url>) [--run-id <id>] [--limit <n>] [--format json]",
|
|
14568
|
+
options: { "run-id": stringOption(), "node-key": stringOption(), url: stringOption(), limit: stringOption() },
|
|
14569
|
+
run: async (input) => {
|
|
14570
|
+
const usage = "canonry technical-aeo links neighbors <project> (--node-key <key>|--url <url>) [--run-id <id>] [--limit <n>] [--format json]";
|
|
14571
|
+
const project = requireProject(input, "technical-aeo.links.neighbors", usage);
|
|
14572
|
+
const nodeKey = getString(input.values, "node-key");
|
|
14573
|
+
const url = getString(input.values, "url");
|
|
14574
|
+
if (!nodeKey && !url) {
|
|
14575
|
+
throw usageError(`Error: --node-key or --url is required
|
|
14576
|
+
Usage: ${usage}`, {
|
|
14577
|
+
message: "--node-key or --url is required",
|
|
14578
|
+
details: { command: "technical-aeo.links.neighbors", usage }
|
|
14579
|
+
});
|
|
14580
|
+
}
|
|
14581
|
+
await technicalAeoLinkNeighbors(project, {
|
|
14582
|
+
runId: getString(input.values, "run-id"),
|
|
14583
|
+
nodeKey,
|
|
14584
|
+
url,
|
|
14585
|
+
limit: parseIntegerOption(input, "limit", { command: "technical-aeo.links.neighbors", usage, message: "--limit must be an integer" }),
|
|
14586
|
+
format: input.format
|
|
14587
|
+
});
|
|
14588
|
+
}
|
|
14589
|
+
},
|
|
14590
|
+
{
|
|
14591
|
+
path: ["technical-aeo", "dead-links"],
|
|
14592
|
+
usage: "canonry technical-aeo dead-links <project> [--run-id <id>] [--cursor <cursor>] [--limit <n>] [--format json|jsonl]",
|
|
14593
|
+
options: { "run-id": stringOption(), cursor: stringOption(), limit: stringOption() },
|
|
14594
|
+
run: async (input) => {
|
|
14595
|
+
const usage = "canonry technical-aeo dead-links <project> [--run-id <id>] [--cursor <cursor>] [--limit <n>] [--format json|jsonl]";
|
|
14596
|
+
const project = requireProject(input, "technical-aeo.dead-links", usage);
|
|
14597
|
+
await technicalAeoDeadLinks(project, {
|
|
14598
|
+
runId: getString(input.values, "run-id"),
|
|
14599
|
+
cursor: getString(input.values, "cursor"),
|
|
14600
|
+
limit: parseIntegerOption(input, "limit", { command: "technical-aeo.dead-links", usage, message: "--limit must be an integer" }),
|
|
14601
|
+
format: input.format
|
|
14602
|
+
});
|
|
14603
|
+
}
|
|
13950
14604
|
}
|
|
13951
14605
|
];
|
|
14606
|
+
function siteHealthAlias(sourcePath, path12, usage) {
|
|
14607
|
+
const source = TECHNICAL_AEO_CLI_COMMANDS_BASE.find(
|
|
14608
|
+
(candidate) => candidate.path.join(" ") === sourcePath.join(" ")
|
|
14609
|
+
);
|
|
14610
|
+
if (!source) throw new Error(`Missing Site Health compatibility source: ${sourcePath.join(" ")}`);
|
|
14611
|
+
return {
|
|
14612
|
+
...source,
|
|
14613
|
+
path: path12,
|
|
14614
|
+
usage,
|
|
14615
|
+
run: async (input) => {
|
|
14616
|
+
try {
|
|
14617
|
+
await source.run(input);
|
|
14618
|
+
} catch (error) {
|
|
14619
|
+
if (!(error instanceof CliError) || error.code !== "CLI_USAGE_ERROR") throw error;
|
|
14620
|
+
const firstLine = error.displayMessage?.split("\n", 1)[0] ?? `Error: ${error.message}`;
|
|
14621
|
+
throw usageError(`${firstLine}
|
|
14622
|
+
Usage: ${usage}`, {
|
|
14623
|
+
message: error.message,
|
|
14624
|
+
details: { ...error.details, command: path12.join("."), usage }
|
|
14625
|
+
});
|
|
14626
|
+
}
|
|
14627
|
+
}
|
|
14628
|
+
};
|
|
14629
|
+
}
|
|
14630
|
+
var SITE_HEALTH_COMPATIBILITY_COMMANDS = [
|
|
14631
|
+
siteHealthAlias(
|
|
14632
|
+
["technical-aeo", "crawl"],
|
|
14633
|
+
["site-health", "overview"],
|
|
14634
|
+
"canonry site-health overview <project> [--run-id <id>] [--format json]"
|
|
14635
|
+
),
|
|
14636
|
+
siteHealthAlias(
|
|
14637
|
+
["technical-aeo", "crawl-pages"],
|
|
14638
|
+
["site-health", "pages"],
|
|
14639
|
+
"canonry site-health pages <project> [--run-id <id>] [--inventory-eligible true|false] [--fetch-state <state>] [--indexability-state <state>] [--audit-state <state>] [--sort url|path|score-asc|score-desc] [--cursor <cursor>] [--limit <n>] [--format json|jsonl]"
|
|
14640
|
+
),
|
|
14641
|
+
siteHealthAlias(
|
|
14642
|
+
["technical-aeo", "page-audit"],
|
|
14643
|
+
["site-health", "page-audit"],
|
|
14644
|
+
"canonry site-health page-audit <project> (--node-key <key>|--url <url>) [--run-id <id>] [--format json]"
|
|
14645
|
+
),
|
|
14646
|
+
siteHealthAlias(
|
|
14647
|
+
["technical-aeo", "structure"],
|
|
14648
|
+
["site-health", "structure"],
|
|
14649
|
+
"canonry site-health structure <project> [--run-id <id>] [--parent-path <path>] [--cursor <cursor>] [--limit <n>] [--format json|jsonl]"
|
|
14650
|
+
),
|
|
14651
|
+
siteHealthAlias(
|
|
14652
|
+
["technical-aeo", "links"],
|
|
14653
|
+
["site-health", "links"],
|
|
14654
|
+
"canonry site-health links <project> [--run-id <id>] [--source-url <url>] [--target-url <url>] [--followable|--nofollow] [--cursor <cursor>] [--limit <n>] [--format json|jsonl]"
|
|
14655
|
+
),
|
|
14656
|
+
siteHealthAlias(
|
|
14657
|
+
["technical-aeo", "links", "neighbors"],
|
|
14658
|
+
["site-health", "neighbors"],
|
|
14659
|
+
"canonry site-health neighbors <project> (--node-key <key>|--url <url>) [--run-id <id>] [--limit <n>] [--format json]"
|
|
14660
|
+
),
|
|
14661
|
+
siteHealthAlias(
|
|
14662
|
+
["technical-aeo", "dead-links"],
|
|
14663
|
+
["site-health", "dead-links"],
|
|
14664
|
+
"canonry site-health dead-links <project> [--run-id <id>] [--cursor <cursor>] [--limit <n>] [--format json|jsonl]"
|
|
14665
|
+
),
|
|
14666
|
+
siteHealthAlias(
|
|
14667
|
+
["technical-aeo", "subgraph"],
|
|
14668
|
+
["site-health", "subgraph"],
|
|
14669
|
+
"canonry site-health subgraph <project> [--run-id <id>] [--node-key <key>|--url <url>] [--hops <n>] [--max-nodes <n>] [--max-edges <n>] [--format json]"
|
|
14670
|
+
),
|
|
14671
|
+
siteHealthAlias(
|
|
14672
|
+
["technical-aeo", "path"],
|
|
14673
|
+
["site-health", "path"],
|
|
14674
|
+
"canonry site-health path <project> (--to-node-key <key>|--to-url <url>) [--run-id <id>] [--from-node-key <key>|--from-url <url>] [--max-depth <n>] [--format json]"
|
|
14675
|
+
),
|
|
14676
|
+
siteHealthAlias(
|
|
14677
|
+
["technical-aeo", "changes"],
|
|
14678
|
+
["site-health", "changes"],
|
|
14679
|
+
"canonry site-health changes <project> [--from-run-id <id>] [--to-run-id <id>] [--scope all|pages|links] [--change all|added|removed|changed] [--cursor <cursor>] [--limit <n>] [--format json|jsonl]"
|
|
14680
|
+
)
|
|
14681
|
+
];
|
|
14682
|
+
var TECHNICAL_AEO_CLI_COMMANDS = [
|
|
14683
|
+
...TECHNICAL_AEO_CLI_COMMANDS_BASE,
|
|
14684
|
+
...SITE_HEALTH_COMPATIBILITY_COMMANDS
|
|
14685
|
+
];
|
|
13952
14686
|
|
|
13953
14687
|
// src/commands/visibility-stats.ts
|
|
13954
14688
|
async function showVisibilityStats(project, opts) {
|