@canonry/canonry 7.87.2 → 7.89.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/skills/canonry/references/canonry-cli.md +31 -5
- package/assets/assets/{AgentSetupPage-_ZP3F_zA.js → AgentSetupPage-BU1supL8.js} +6 -6
- package/assets/assets/AuditHistoryPanel-D7or_J3j.js +1 -0
- package/assets/assets/{BacklinksPage-CsvVgXTZ.js → BacklinksPage-SOls3ew4.js} +1 -1
- package/assets/assets/{ChartPrimitives-4D5wgAn5.js → ChartPrimitives-Cs2iavxU.js} +1 -1
- package/assets/assets/{Hint-CfeZmZig.js → Hint-uoVNfr0U.js} +1 -1
- package/assets/assets/{HistoryPage-BCxpCul0.js → HistoryPage-LNKtQ6a8.js} +1 -1
- package/assets/assets/{InfoTooltip-DrBIPuSU.js → InfoTooltip-CNLlIWeP.js} +1 -1
- package/assets/assets/MeasurementPropertyPage-CTZzlHWE.js +4 -0
- package/assets/assets/ProjectPage-D79zuY-M.js +11 -0
- package/assets/assets/{RunsPage-CfQ_kqlu.js → RunsPage-B8zUD2gr.js} +1 -1
- package/assets/assets/{SettingsPage-PIgRvqJc.js → SettingsPage-BOQgT_7N.js} +1 -1
- package/assets/assets/SiteHealthSection-G3_MShnD.js +2 -0
- package/assets/assets/{ToneBadge-BgTLSTKp.js → ToneBadge-DawS8vMp.js} +6 -6
- package/assets/assets/TrafficPage-DJUqxtrQ.js +40 -0
- package/assets/assets/TrafficSourceDetailPage-OVY_L73m.js +1 -0
- package/assets/assets/{account-context-CVjb3ZSn.js → account-context-DoI9S9qY.js} +1 -1
- package/assets/assets/agent-trust-copy-CZpfPr4M.js +1 -0
- package/assets/assets/{card-BnVez9nG.js → card-ClURYmZN.js} +1 -1
- package/assets/assets/{format-helpers-DVXZD864.js → format-helpers-CscAFAMJ.js} +2 -2
- package/assets/assets/{index-BOUrrC9J.css → index-Bhpq6fUo.css} +1 -1
- package/assets/assets/index-y0Wx0ZWq.js +60 -0
- package/assets/assets/{query-client-CVc7wMKJ.js → query-client-Dm_GqsTu.js} +1 -1
- package/assets/assets/react-query-BwhgbuS1.js +1 -0
- package/assets/assets/{react-sigma_core.esm.min-CVfGIEIE.js → react-sigma_core.esm.min-6J0MSvMJ.js} +1 -1
- package/assets/assets/server-traffic-C-_D6pdo.js +1 -0
- package/assets/assets/{service-BGqeyqhZ.js → service-C7qDx_qB.js} +1 -1
- package/assets/assets/{vendor-lucide-CoTHJwai.js → vendor-lucide-Bo69m9KC.js} +1 -1
- package/assets/assets/{vendor-markdown-cQgSk2i0.js → vendor-markdown-BiRP51B0.js} +1 -1
- package/assets/assets/{vendor-radix-wS8sWIWE.js → vendor-radix-PQeN8gOe.js} +1 -1
- package/assets/assets/{vendor-recharts-AnNqECZW.js → vendor-recharts-CY6tH-r6.js} +1 -1
- package/assets/assets/{vendor-tanstack-q5SvZtcv.js → vendor-tanstack-tBjrfzKu.js} +1 -1
- package/assets/index.html +15 -15
- package/dist/{ads-ZUZWSIBV.js → ads-XEW2LBKV.js} +3 -3
- package/dist/{agent-YDS3WY7B.js → agent-VYADMMPT.js} +7 -7
- package/dist/{agent-ask-7VUPXTXL.js → agent-ask-4G57GM3S.js} +3 -3
- package/dist/{agent-conversations-WPEBK3BS.js → agent-conversations-MRKRCXQI.js} +3 -3
- package/dist/{agent-journeys-H5CCYLNB.js → agent-journeys-WEUEYRMK.js} +3 -3
- package/dist/{agent-memory-CNPKPIWD.js → agent-memory-YLAOXQQ6.js} +3 -3
- package/dist/{agent-providers-U7FLJO5I.js → agent-providers-AWKQRJDZ.js} +3 -3
- package/dist/{agent-transcript-VY3BQNPU.js → agent-transcript-HXYFVCUD.js} +3 -3
- package/dist/{analytics-PYUNO7C2.js → analytics-5G5G5OJU.js} +3 -3
- package/dist/{apply-YNBEYC2G.js → apply-FCKZG7A2.js} +3 -3
- package/dist/{backfill-6AYHWEQX.js → backfill-3CTZHCJS.js} +10 -10
- package/dist/{backlinks-LVHZ2MQ7.js → backlinks-Q53JT63C.js} +3 -3
- package/dist/bing-FOY2UVIM.js +785 -0
- package/dist/{bootstrap-27NOBWTP.js → bootstrap-FXIT7LDW.js} +10 -10
- package/dist/{cdp-HCYY4SXR.js → cdp-WWQW3LIZ.js} +7 -7
- package/dist/{chunk-INH773FB.js → chunk-2BZJ4W27.js} +3 -3
- package/dist/{chunk-3KK6FWDF.js → chunk-3H5VURTU.js} +1 -1
- package/dist/{chunk-IK7QEKKD.js → chunk-3IC7PRTW.js} +1 -1
- package/dist/{chunk-BVJA3TV2.js → chunk-4INNOSWU.js} +5 -5
- package/dist/{chunk-4LNZTL7M.js → chunk-5GQ2AGWS.js} +3 -3
- package/dist/{chunk-3FHBPJ5R.js → chunk-7522CQ2E.js} +4 -4
- package/dist/{chunk-2FJB3CSS.js → chunk-A6DKZ4KQ.js} +1 -1
- package/dist/{chunk-JAYUWQAV.js → chunk-DPKA5CWK.js} +1 -1
- package/dist/{chunk-MNLAIB67.js → chunk-DX46HM7S.js} +1 -1
- package/dist/{chunk-ZJVQ72VC.js → chunk-EWR25OVQ.js} +2 -2
- package/dist/{chunk-TFB2S6TQ.js → chunk-GDEYX6E3.js} +4 -4
- package/dist/{chunk-T2MGGW6Y.js → chunk-GSNKBXGN.js} +6 -6
- package/dist/{chunk-AI75KS7G.js → chunk-HSOZSLIZ.js} +2574 -1585
- package/dist/{chunk-IVGVPPQT.js → chunk-IK3PYAQU.js} +1088 -646
- package/dist/{chunk-GDU6IMGB.js → chunk-J7V7TYX7.js} +2 -2
- package/dist/{chunk-GXTCDNX7.js → chunk-JLBJEQPV.js} +1 -1
- package/dist/{chunk-ZCNKGBIV.js → chunk-KAGMYJYG.js} +5 -5
- package/dist/{chunk-2AXFKYJU.js → chunk-MNLQY5WG.js} +2 -2
- package/dist/{chunk-FAMCDCVK.js → chunk-MXGME7NG.js} +1 -1
- package/dist/{chunk-6F5ULQDY.js → chunk-RE24YYIY.js} +4 -4
- package/dist/{chunk-ICS2WJ7H.js → chunk-S7D2ELTC.js} +3 -3
- package/dist/{chunk-TEQF4ZJA.js → chunk-TCANF22P.js} +162 -2
- package/dist/{chunk-BJHHD7VE.js → chunk-UIULMWJX.js} +2 -2
- package/dist/{chunk-TN53B5AB.js → chunk-VBM6T5ZP.js} +218 -3
- package/dist/{chunk-POMLL2OZ.js → chunk-VL4AZGUA.js} +1 -1
- package/dist/{chunk-CBWGUGME.js → chunk-WE6FSG7J.js} +18 -2
- package/dist/{chunk-LYTBHEID.js → chunk-WHN4RF2Y.js} +534 -342
- package/dist/{chunk-DOPUEXMC.js → chunk-WPTCWNA6.js} +2 -2
- package/dist/{chunk-IE4D47JE.js → chunk-X6XFWQCE.js} +7 -7
- package/dist/{chunk-IBGZ6D6R.js → chunk-Y35D3ZCH.js} +2 -2
- package/dist/{chunk-F643WELJ.js → chunk-YKLYWVLL.js} +341 -5
- package/dist/{chunk-GBWCEOZR.js → chunk-YW2IQEWS.js} +1 -1
- package/dist/{chunk-AYYZ4HKJ.js → chunk-ZEWOPHTA.js} +230 -1
- package/dist/{chunk-4FWDITQG.js → chunk-ZX4TZNP4.js} +1 -1
- package/dist/{chunk-JDTKG7KJ.js → chunk-ZYEWB5BK.js} +3 -3
- package/dist/{citations-3EZTHA65.js → citations-ZZ6ODCGZ.js} +3 -3
- package/dist/{cli-catalog-CBTM3JKP.js → cli-catalog-HEX47BKL.js} +3 -3
- package/dist/{cli-commands-H7BFVUGC.js → cli-commands-6OLN355M.js} +7 -7
- package/dist/cli.js +13 -13
- package/dist/{competitor-VH4TXJLT.js → competitor-6MRHORU2.js} +3 -3
- package/dist/{content-TSMNHXJZ.js → content-S2UOGTLN.js} +3 -3
- package/dist/{dashboard-KT2ACPI5.js → dashboard-J4H3FVKH.js} +6 -6
- package/dist/{demo-LWSLULEZ.js → demo-7PJJQMLO.js} +1 -1
- package/dist/{demo-server-UENMDVMG.js → demo-server-55I5LNNS.js} +12 -12
- package/dist/{discover-XTJXTKCI.js → discover-O4NFDHGP.js} +3 -3
- package/dist/{discover-eval-IVQI5YEW.js → discover-eval-PE2MW42B.js} +3 -3
- package/dist/{doctor-SJ4SHSQO.js → doctor-GT4MMJQJ.js} +3 -3
- package/dist/{evidence-C6T556YH.js → evidence-Z344KVJX.js} +3 -3
- package/dist/{export-cmd-JQZ5ZRSI.js → export-cmd-Z6WWLI6H.js} +3 -3
- package/dist/{ga-W2WHVRHN.js → ga-SXIGAF5F.js} +3 -3
- package/dist/{gbp-3AZIV5WF.js → gbp-CSLWSEKF.js} +3 -3
- package/dist/{google-3CKWPZT7.js → google-EAQJ46AR.js} +4 -4
- package/dist/{google-marketing-PJBGQYYW.js → google-marketing-NVKB4NLG.js} +1 -1
- package/dist/{health-cmd-4JTQMCXM.js → health-cmd-6IYRMZS3.js} +3 -3
- package/dist/{history-TXUJFEO2.js → history-KLZI7IKJ.js} +3 -3
- package/dist/index.d.ts +1891 -399
- package/dist/index.js +25 -25
- package/dist/{init-TF3OPCEC.js → init-LFLCM6KJ.js} +12 -12
- package/dist/{insights-XZQO4B45.js → insights-C7NN64E2.js} +3 -3
- package/dist/intelligence-service-ENI35JFR.js +16 -0
- package/dist/{journeys-VFIV5JYV.js → journeys-C34YDOM6.js} +3 -3
- package/dist/{keys-PV23G4LW.js → keys-4RDLFHXO.js} +3 -3
- package/dist/{keyword-D76KBIV5.js → keyword-BZEJEYAG.js} +3 -3
- package/dist/{logs-DYDZ6IEJ.js → logs-5FPZRSZM.js} +3 -3
- package/dist/{mcp-GAR5LDQD.js → mcp-574UA5Y3.js} +4 -4
- package/dist/mcp.js +9 -9
- package/dist/{measurement-plan-K24EOGMI.js → measurement-plan-2H4KHD2Y.js} +5 -5
- package/dist/{notify-XQHMPDQQ.js → notify-TFXL4CSH.js} +3 -3
- package/dist/{organic-evidence-UHHE7AO4.js → organic-evidence-R55KSYFN.js} +3 -3
- package/dist/{overview-AUZ3XRS5.js → overview-NJTCLHAB.js} +4 -4
- package/dist/{project-FUCPU2BD.js → project-PV36DZDG.js} +3 -3
- package/dist/{query-HRMSXDKL.js → query-5SVITJ4K.js} +3 -3
- package/dist/{research-R23XLZJ3.js → research-JSET5FNY.js} +3 -3
- package/dist/{results-export-N6IV3KG4.js → results-export-5EFMXTPG.js} +3 -3
- package/dist/{run-CJLG5EXH.js → run-NOSOG3YH.js} +4 -4
- package/dist/{schedule-IPDCQ2I5.js → schedule-I3ACQC2C.js} +3 -3
- package/dist/{search-ZU4AKJ76.js → search-EE53K7J5.js} +3 -3
- package/dist/{serve-LLIUILD2.js → serve-SDU77HVC.js} +26 -26
- package/dist/{session-2NQKSF2N.js → session-FYZCUPVX.js} +13 -13
- package/dist/{settings-4FS5NRKP.js → settings-CEI7EKPO.js} +3 -3
- package/dist/{skills-GSFHACGN.js → skills-TRTFFZU3.js} +2 -2
- package/dist/{snapshot-4NUVPDK3.js → snapshot-2RXVTDTZ.js} +3 -3
- package/dist/{src-JQYZ2TP6.js → src-DYJHW2HG.js} +16 -2
- package/dist/{status-ASBLMFP2.js → status-2VHBSIRY.js} +4 -4
- package/dist/{technical-aeo-QXHRBGYY.js → technical-aeo-3OMGT4ZT.js} +3 -3
- package/dist/{telemetry-J2I577I3.js → telemetry-5F4EHGR2.js} +4 -4
- package/dist/{tools-UPJRW4DN.js → tools-C3IIMXBJ.js} +11 -11
- package/dist/{traffic-GWBZKYR5.js → traffic-S3VUYBG6.js} +5 -5
- package/dist/{trends-YOLCJBNM.js → trends-RQ4B2QWA.js} +3 -3
- package/dist/{users-LPSCZV3M.js → users-T6OOG5YB.js} +3 -3
- package/dist/{visibility-stats-JJQTZSSK.js → visibility-stats-2O24MQCV.js} +3 -3
- package/dist/{wordpress-4IKDT5KT.js → wordpress-SDENOBFI.js} +3 -3
- package/package.json +1 -1
- package/assets/assets/AuditHistoryPanel-9B0XpYsN.js +0 -1
- package/assets/assets/MeasurementPropertyPage-BObvWjVx.js +0 -4
- package/assets/assets/ProjectPage-y378tOaT.js +0 -11
- package/assets/assets/SiteHealthSection-DuUE1cB0.js +0 -2
- package/assets/assets/TrafficPage-B_YunwjH.js +0 -40
- package/assets/assets/TrafficSourceDetailPage-C51W87fU.js +0 -1
- package/assets/assets/agent-trust-copy-BlpC8jo5.js +0 -1
- package/assets/assets/extract-error-message-Dqirbrl0.js +0 -1
- package/assets/assets/index-CsvEqhK4.js +0 -60
- package/assets/assets/react-query-CSlTSOjz.js +0 -1
- package/dist/bing-A2MUG6AV.js +0 -418
- package/dist/intelligence-service-LEH5UVIC.js +0 -16
|
@@ -0,0 +1,785 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createApiClient
|
|
3
|
+
} from "./chunk-TCANF22P.js";
|
|
4
|
+
import "./chunk-A6DKZ4KQ.js";
|
|
5
|
+
import {
|
|
6
|
+
emitJsonl
|
|
7
|
+
} from "./chunk-Z7IUAJOF.js";
|
|
8
|
+
import "./chunk-AOJU5LKX.js";
|
|
9
|
+
import {
|
|
10
|
+
CliError,
|
|
11
|
+
isMachineFormat
|
|
12
|
+
} from "./chunk-HGMYEDAQ.js";
|
|
13
|
+
import {
|
|
14
|
+
describeError,
|
|
15
|
+
formatPercent,
|
|
16
|
+
formatRunErrorOneLine
|
|
17
|
+
} from "./chunk-YKLYWVLL.js";
|
|
18
|
+
import "./chunk-MLKGABMK.js";
|
|
19
|
+
|
|
20
|
+
// src/commands/bing.ts
|
|
21
|
+
function getClient() {
|
|
22
|
+
return createApiClient();
|
|
23
|
+
}
|
|
24
|
+
async function waitForRunStatus(client, runId, config) {
|
|
25
|
+
const start = Date.now();
|
|
26
|
+
process.stderr.write(config.progressLabel);
|
|
27
|
+
while (Date.now() - start < config.timeoutMs) {
|
|
28
|
+
await new Promise((resolve) => setTimeout(resolve, config.intervalMs));
|
|
29
|
+
const current = await client.getRun(runId);
|
|
30
|
+
process.stderr.write(".");
|
|
31
|
+
if (config.successStatuses.includes(current.status)) {
|
|
32
|
+
process.stderr.write("\n");
|
|
33
|
+
return current;
|
|
34
|
+
}
|
|
35
|
+
if (config.failureStatuses.includes(current.status)) {
|
|
36
|
+
process.stderr.write("\n");
|
|
37
|
+
throw new CliError({
|
|
38
|
+
code: config.failureCode,
|
|
39
|
+
message: config.failureMessage,
|
|
40
|
+
displayMessage: config.failureMessage,
|
|
41
|
+
details: { runId, status: current.status, ...config.details ?? {} }
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
process.stderr.write("\n");
|
|
46
|
+
throw new CliError({
|
|
47
|
+
code: config.timeoutCode,
|
|
48
|
+
message: config.timeoutMessage,
|
|
49
|
+
displayMessage: config.timeoutMessage,
|
|
50
|
+
details: { runId, ...config.details ?? {} }
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
async function bingConnect(project, opts) {
|
|
54
|
+
const apiKey = opts?.apiKey;
|
|
55
|
+
if (!apiKey) {
|
|
56
|
+
throw new CliError({
|
|
57
|
+
code: "BING_API_KEY_REQUIRED",
|
|
58
|
+
message: "API key is required. Pass --api-key <key>.",
|
|
59
|
+
displayMessage: "Error: API key is required. Pass --api-key <key>.",
|
|
60
|
+
details: {
|
|
61
|
+
project
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
const client = getClient();
|
|
66
|
+
const result = await client.bingConnect(project, { apiKey });
|
|
67
|
+
if (isMachineFormat(opts?.format)) {
|
|
68
|
+
console.log(JSON.stringify(result, null, 2));
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
console.log(`Bing Webmaster Tools connected for project "${project}".`);
|
|
72
|
+
if (result.availableSites.length > 0) {
|
|
73
|
+
console.log(`
|
|
74
|
+
Registered sites:`);
|
|
75
|
+
for (const site of result.availableSites) {
|
|
76
|
+
const verified = site.verified ? " (verified)" : "";
|
|
77
|
+
console.log(` ${site.url}${verified}`);
|
|
78
|
+
}
|
|
79
|
+
console.log(`
|
|
80
|
+
Set the active site with: canonry bing set-site ${project} <url>`);
|
|
81
|
+
} else {
|
|
82
|
+
console.log("\nNo sites found. Register your site at https://www.bing.com/webmasters/");
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
async function bingDisconnect(project, format) {
|
|
86
|
+
const client = getClient();
|
|
87
|
+
await client.bingDisconnect(project);
|
|
88
|
+
if (isMachineFormat(format)) {
|
|
89
|
+
console.log(JSON.stringify({ project, disconnected: true }, null, 2));
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
console.log(`Bing Webmaster Tools disconnected from project "${project}".`);
|
|
93
|
+
}
|
|
94
|
+
async function bingStatus(project, format) {
|
|
95
|
+
const client = getClient();
|
|
96
|
+
const result = await client.bingStatus(project);
|
|
97
|
+
if (isMachineFormat(format)) {
|
|
98
|
+
console.log(JSON.stringify(result, null, 2));
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
if (!result.connected) {
|
|
102
|
+
console.log(`No Bing connection for project "${project}".`);
|
|
103
|
+
console.log('Run "canonry bing connect <project>" to connect.');
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
console.log(`Bing Webmaster Tools for "${project}":
|
|
107
|
+
`);
|
|
108
|
+
console.log(` Site URL: ${result.siteUrl ?? "(not set)"}`);
|
|
109
|
+
console.log(` Connected: ${result.createdAt}`);
|
|
110
|
+
console.log(` Updated: ${result.updatedAt}`);
|
|
111
|
+
}
|
|
112
|
+
async function bingSites(project, format) {
|
|
113
|
+
const client = getClient();
|
|
114
|
+
const result = await client.bingSites(project);
|
|
115
|
+
if (format === "json") {
|
|
116
|
+
console.log(JSON.stringify(result, null, 2));
|
|
117
|
+
return;
|
|
118
|
+
} else if (format === "jsonl") {
|
|
119
|
+
emitJsonl(result.sites.map((site) => ({ project, ...site })));
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
if (result.sites.length === 0) {
|
|
123
|
+
console.log("No sites registered in Bing Webmaster Tools.");
|
|
124
|
+
console.log("Register your site at https://www.bing.com/webmasters/");
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
console.log("Bing Webmaster Tools sites:\n");
|
|
128
|
+
const urlWidth = Math.max(10, ...result.sites.map((s) => s.url.length));
|
|
129
|
+
console.log(` ${"URL".padEnd(urlWidth)} VERIFIED`);
|
|
130
|
+
console.log(` ${"\u2500".repeat(urlWidth)} ${"\u2500".repeat(8)}`);
|
|
131
|
+
for (const site of result.sites) {
|
|
132
|
+
console.log(` ${site.url.padEnd(urlWidth)} ${site.verified ? "Yes" : "No"}`);
|
|
133
|
+
}
|
|
134
|
+
console.log(`
|
|
135
|
+
Use "canonry bing set-site <project> <url>" to select a site.`);
|
|
136
|
+
}
|
|
137
|
+
async function bingSetSite(project, siteUrl, format) {
|
|
138
|
+
const client = getClient();
|
|
139
|
+
await client.bingSetSite(project, siteUrl);
|
|
140
|
+
if (isMachineFormat(format)) {
|
|
141
|
+
console.log(JSON.stringify({ project, siteUrl }, null, 2));
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
console.log(`Bing site set to "${siteUrl}" for project "${project}".`);
|
|
145
|
+
}
|
|
146
|
+
async function bingCoverage(project, format) {
|
|
147
|
+
const client = getClient();
|
|
148
|
+
const result = await client.bingCoverage(project);
|
|
149
|
+
if (isMachineFormat(format)) {
|
|
150
|
+
console.log(JSON.stringify(result, null, 2));
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
const { summary } = result;
|
|
154
|
+
if (summary.total === 0) {
|
|
155
|
+
if ((summary.unknown ?? 0) > 0) {
|
|
156
|
+
console.log("No URLs have a definitive Bing index status yet.");
|
|
157
|
+
console.log("Run more inspections or use --format json to review the unknown responses.");
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
console.log('No URL inspections found. Run "canonry bing inspect <project> <url>" first.');
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
const reset = "\x1B[0m";
|
|
164
|
+
let pctColor;
|
|
165
|
+
if (summary.percentage >= 80) pctColor = "\x1B[32m";
|
|
166
|
+
else if (summary.percentage >= 50) pctColor = "\x1B[33m";
|
|
167
|
+
else pctColor = "\x1B[31m";
|
|
168
|
+
const unknownNote = (summary.unknown ?? 0) > 0 ? `, ${summary.unknown} unknown` : "";
|
|
169
|
+
console.log(`
|
|
170
|
+
Bing Index Coverage for "${project}"
|
|
171
|
+
`);
|
|
172
|
+
console.log(` SUMMARY: ${pctColor}${summary.indexed} / ${summary.total} pages indexed (${formatPercent(summary.percentage, "percent")})${reset}${unknownNote}
|
|
173
|
+
`);
|
|
174
|
+
if (result.indexed.length > 0) {
|
|
175
|
+
console.log(` INDEXED (${result.indexed.length}):`);
|
|
176
|
+
for (const page of result.indexed) {
|
|
177
|
+
const crawl = page.lastCrawledDate ? ` (crawled: ${page.lastCrawledDate.split("T")[0]})` : "";
|
|
178
|
+
console.log(` ${page.url}${crawl}`);
|
|
179
|
+
}
|
|
180
|
+
console.log();
|
|
181
|
+
}
|
|
182
|
+
if (result.notIndexed.length > 0) {
|
|
183
|
+
console.log(` NOT INDEXED (${result.notIndexed.length}):`);
|
|
184
|
+
for (const page of result.notIndexed) {
|
|
185
|
+
const code = page.httpCode ? ` \u2014 HTTP ${page.httpCode}` : "";
|
|
186
|
+
console.log(` ${page.url}${code}`);
|
|
187
|
+
}
|
|
188
|
+
console.log();
|
|
189
|
+
}
|
|
190
|
+
if (result.lastInspectedAt) {
|
|
191
|
+
console.log(` Last inspected: ${result.lastInspectedAt}`);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
async function bingCoverageHistory(project, opts) {
|
|
195
|
+
const client = getClient();
|
|
196
|
+
const rows = await client.bingCoverageHistory(project, { limit: opts.limit });
|
|
197
|
+
if (opts.format === "json") {
|
|
198
|
+
console.log(JSON.stringify(rows, null, 2));
|
|
199
|
+
return;
|
|
200
|
+
} else if (opts.format === "jsonl") {
|
|
201
|
+
emitJsonl(rows.map((row) => ({ project, ...row })));
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
if (rows.length === 0) {
|
|
205
|
+
console.log('No coverage history found. Run "canonry bing coverage" or "canonry bing refresh" first to capture a snapshot.');
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
console.log(`
|
|
209
|
+
Bing Coverage History for "${project}" (${rows.length} snapshots):
|
|
210
|
+
`);
|
|
211
|
+
console.log(` ${"DATE".padEnd(12)}${"INDEXED".padEnd(10)}${"NOT INDEXED".padEnd(14)}UNKNOWN`);
|
|
212
|
+
console.log(` ${"\u2500".repeat(12)}${"\u2500".repeat(10)}${"\u2500".repeat(14)}${"\u2500".repeat(10)}`);
|
|
213
|
+
for (const row of rows) {
|
|
214
|
+
console.log(` ${row.date.padEnd(12)}${String(row.indexed).padEnd(10)}${String(row.notIndexed).padEnd(14)}${String(row.unknown)}`);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
async function bingInspect(project, url, format) {
|
|
218
|
+
const client = getClient();
|
|
219
|
+
const result = await client.bingInspectUrl(project, url);
|
|
220
|
+
if (isMachineFormat(format)) {
|
|
221
|
+
console.log(JSON.stringify(result, null, 2));
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
const indexLabel = result.inIndex === true ? `yes${result.documentSize != null ? ` (document size: ${result.documentSize.toLocaleString()} bytes)` : ""}` : result.inIndex === false ? "no" : "unknown";
|
|
225
|
+
console.log(`
|
|
226
|
+
Bing URL Inspection: ${result.url}
|
|
227
|
+
`);
|
|
228
|
+
console.log(` In Index: ${indexLabel}`);
|
|
229
|
+
console.log(` Last Crawled: ${result.lastCrawledDate ? result.lastCrawledDate.split("T")[0] : "never"}`);
|
|
230
|
+
console.log(` Discovery Date: ${result.discoveryDate ? result.discoveryDate.split("T")[0] : "unknown"}`);
|
|
231
|
+
if (result.anchorCount != null) {
|
|
232
|
+
console.log(` Inbound Links: ${result.anchorCount}`);
|
|
233
|
+
}
|
|
234
|
+
console.log(` Inspected At: ${result.inspectedAt}`);
|
|
235
|
+
}
|
|
236
|
+
async function bingInspections(project, opts) {
|
|
237
|
+
const client = getClient();
|
|
238
|
+
const params = {};
|
|
239
|
+
if (opts.url) params.url = opts.url;
|
|
240
|
+
const rows = await client.bingInspections(project, Object.keys(params).length > 0 ? params : void 0);
|
|
241
|
+
if (opts.format === "json") {
|
|
242
|
+
console.log(JSON.stringify(rows, null, 2));
|
|
243
|
+
return;
|
|
244
|
+
} else if (opts.format === "jsonl") {
|
|
245
|
+
emitJsonl(rows.map((row) => ({ project, ...row })));
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
if (rows.length === 0) {
|
|
249
|
+
console.log("No Bing URL inspections found.");
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
console.log(`Bing URL inspection history (${rows.length} records):
|
|
253
|
+
`);
|
|
254
|
+
const urlWidth = Math.min(50, Math.max(10, ...rows.map((r) => r.url.length)));
|
|
255
|
+
console.log(` ${"URL".padEnd(urlWidth)} ${"INDEXED".padEnd(9)}${"HTTP".padEnd(6)}${"INSPECTED".padEnd(22)}`);
|
|
256
|
+
console.log(` ${"\u2500".repeat(urlWidth)} ${"\u2500".repeat(9)}${"\u2500".repeat(6)}${"\u2500".repeat(22)}`);
|
|
257
|
+
for (const row of rows) {
|
|
258
|
+
const url = row.url.length > urlWidth ? row.url.slice(0, urlWidth - 3) + "..." : row.url;
|
|
259
|
+
const indexed = row.inIndex === true ? "Yes" : row.inIndex === false ? "No" : "?";
|
|
260
|
+
console.log(
|
|
261
|
+
` ${url.padEnd(urlWidth)} ${indexed.padEnd(9)}${String(row.httpCode ?? "?").padEnd(6)}${row.inspectedAt}`
|
|
262
|
+
);
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
async function bingRefresh(project, format) {
|
|
266
|
+
const client = getClient();
|
|
267
|
+
const rows = await client.bingInspections(project);
|
|
268
|
+
const uniqueUrls = [...new Set(rows.map((r) => r.url))];
|
|
269
|
+
if (uniqueUrls.length === 0) {
|
|
270
|
+
if (isMachineFormat(format)) {
|
|
271
|
+
console.log(JSON.stringify({ refreshed: 0, message: "No previously inspected URLs to refresh." }, null, 2));
|
|
272
|
+
return;
|
|
273
|
+
}
|
|
274
|
+
console.log('No previously inspected URLs to refresh. Run "canonry bing inspect <project> <url>" first.');
|
|
275
|
+
return;
|
|
276
|
+
}
|
|
277
|
+
if (!isMachineFormat(format)) {
|
|
278
|
+
process.stderr.write(`Re-inspecting ${uniqueUrls.length} URL(s) via Bing`);
|
|
279
|
+
}
|
|
280
|
+
const CONCURRENCY = 10;
|
|
281
|
+
const errors = [];
|
|
282
|
+
for (let i = 0; i < uniqueUrls.length; i += CONCURRENCY) {
|
|
283
|
+
const batch = uniqueUrls.slice(i, i + CONCURRENCY);
|
|
284
|
+
const results = await Promise.allSettled(
|
|
285
|
+
batch.map((url) => client.bingInspectUrl(project, url))
|
|
286
|
+
);
|
|
287
|
+
for (const r of results) {
|
|
288
|
+
if (r.status === "rejected") {
|
|
289
|
+
errors.push(describeError(r.reason));
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
if (!isMachineFormat(format)) {
|
|
293
|
+
process.stderr.write(".");
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
if (!isMachineFormat(format)) {
|
|
297
|
+
process.stderr.write("\n");
|
|
298
|
+
}
|
|
299
|
+
if (errors.length > 0) {
|
|
300
|
+
process.stderr.write(`${errors.length} URL(s) failed to re-inspect.
|
|
301
|
+
`);
|
|
302
|
+
}
|
|
303
|
+
await bingCoverage(project, format);
|
|
304
|
+
}
|
|
305
|
+
async function bingRequestIndexing(project, opts) {
|
|
306
|
+
const client = getClient();
|
|
307
|
+
const body = {};
|
|
308
|
+
if (opts.allUnindexed) {
|
|
309
|
+
body.allUnindexed = true;
|
|
310
|
+
} else if (opts.url) {
|
|
311
|
+
body.urls = [opts.url];
|
|
312
|
+
} else {
|
|
313
|
+
throw new CliError({
|
|
314
|
+
code: "CLI_USAGE_ERROR",
|
|
315
|
+
message: "provide a URL or use --all-unindexed",
|
|
316
|
+
displayMessage: "Error: provide a URL or use --all-unindexed",
|
|
317
|
+
details: { command: "bing.request-indexing" }
|
|
318
|
+
});
|
|
319
|
+
}
|
|
320
|
+
const result = await client.bingRequestIndexing(project, body);
|
|
321
|
+
if (isMachineFormat(opts.format)) {
|
|
322
|
+
console.log(JSON.stringify(result, null, 2));
|
|
323
|
+
return;
|
|
324
|
+
}
|
|
325
|
+
for (const r of result.results) {
|
|
326
|
+
if (r.status === "success") {
|
|
327
|
+
console.log(`Submitted to Bing: ${r.url}`);
|
|
328
|
+
console.log(` Submitted at: ${r.submittedAt}`);
|
|
329
|
+
console.log();
|
|
330
|
+
} else {
|
|
331
|
+
console.error(`Failed: ${r.url}`);
|
|
332
|
+
console.error(` Error: ${r.error}`);
|
|
333
|
+
console.log();
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
if (result.results.length > 1) {
|
|
337
|
+
console.log(`Summary: ${result.summary.succeeded} succeeded, ${result.summary.failed} failed (${result.summary.total} total)`);
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
async function bingInspectSitemap(project, opts) {
|
|
341
|
+
const client = getClient();
|
|
342
|
+
const run = await client.bingInspectSitemap(project, {
|
|
343
|
+
sitemapUrl: opts.sitemapUrl
|
|
344
|
+
});
|
|
345
|
+
if (!opts.wait && isMachineFormat(opts.format)) {
|
|
346
|
+
console.log(JSON.stringify(run, null, 2));
|
|
347
|
+
return;
|
|
348
|
+
}
|
|
349
|
+
if (!isMachineFormat(opts.format)) {
|
|
350
|
+
console.log(`Bing sitemap inspection started (run ${run.id})`);
|
|
351
|
+
}
|
|
352
|
+
if (opts.wait) {
|
|
353
|
+
const current = await waitForRunStatus(client, run.id, {
|
|
354
|
+
timeoutMs: 30 * 60 * 1e3,
|
|
355
|
+
intervalMs: 3e3,
|
|
356
|
+
progressLabel: "Waiting for Bing sitemap inspection to complete",
|
|
357
|
+
successStatuses: ["completed", "partial"],
|
|
358
|
+
failureStatuses: ["failed"],
|
|
359
|
+
timeoutCode: "BING_INSPECT_SITEMAP_TIMEOUT",
|
|
360
|
+
failureCode: "BING_INSPECT_SITEMAP_FAILED",
|
|
361
|
+
timeoutMessage: "Timed out waiting for Bing sitemap inspection to complete.",
|
|
362
|
+
failureMessage: "Bing sitemap inspection failed.",
|
|
363
|
+
details: { project }
|
|
364
|
+
});
|
|
365
|
+
if (isMachineFormat(opts.format)) {
|
|
366
|
+
console.log(JSON.stringify(current, null, 2));
|
|
367
|
+
return;
|
|
368
|
+
}
|
|
369
|
+
if (current.status === "partial") {
|
|
370
|
+
console.log("Bing sitemap inspection completed with some errors.");
|
|
371
|
+
return;
|
|
372
|
+
}
|
|
373
|
+
console.log("Bing sitemap inspection completed successfully.");
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
async function bingPerformance(project, format) {
|
|
377
|
+
const client = getClient();
|
|
378
|
+
const rows = await client.bingPerformance(project);
|
|
379
|
+
if (format === "json") {
|
|
380
|
+
console.log(JSON.stringify(rows, null, 2));
|
|
381
|
+
return;
|
|
382
|
+
} else if (format === "jsonl") {
|
|
383
|
+
emitJsonl(rows.map((row) => ({ project, ...row })));
|
|
384
|
+
return;
|
|
385
|
+
}
|
|
386
|
+
if (rows.length === 0) {
|
|
387
|
+
console.log("No Bing performance data found.");
|
|
388
|
+
return;
|
|
389
|
+
}
|
|
390
|
+
console.log(`Bing search performance (${rows.length} keywords):
|
|
391
|
+
`);
|
|
392
|
+
console.log(` ${"QUERY".padEnd(40)}${"CLICKS".padEnd(8)}${"IMPR".padEnd(8)}${"CTR".padEnd(8)}${"POS".padEnd(6)}`);
|
|
393
|
+
console.log(` ${"\u2500".repeat(40)}${"\u2500".repeat(8)}${"\u2500".repeat(8)}${"\u2500".repeat(8)}${"\u2500".repeat(6)}`);
|
|
394
|
+
for (const row of rows.slice(0, 50)) {
|
|
395
|
+
const query = row.query.length > 38 ? row.query.slice(0, 35) + "..." : row.query;
|
|
396
|
+
console.log(
|
|
397
|
+
` ${query.padEnd(40)}${String(row.clicks).padEnd(8)}${String(row.impressions).padEnd(8)}${formatPercent(row.ctr).padStart(6)} ${row.averagePosition.toFixed(1).padStart(5)}`
|
|
398
|
+
);
|
|
399
|
+
}
|
|
400
|
+
if (rows.length > 50) {
|
|
401
|
+
console.log(`
|
|
402
|
+
... and ${rows.length - 50} more rows (use --format json for full output)`);
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
function pad(value, width) {
|
|
406
|
+
const text = String(value);
|
|
407
|
+
return text.length > width - 1 ? `${text.slice(0, width - 4)}...`.padEnd(width) : text.padEnd(width);
|
|
408
|
+
}
|
|
409
|
+
function position(value) {
|
|
410
|
+
return value === null ? "-" : value.toFixed(1);
|
|
411
|
+
}
|
|
412
|
+
function windowLabel(window) {
|
|
413
|
+
return window.startDate && window.endDate ? `${window.startDate} to ${window.endDate}` : "no stored data yet";
|
|
414
|
+
}
|
|
415
|
+
function printJson(format, payload, jsonlRows) {
|
|
416
|
+
if (format === "json") {
|
|
417
|
+
console.log(JSON.stringify(payload, null, 2));
|
|
418
|
+
return true;
|
|
419
|
+
}
|
|
420
|
+
if (format === "jsonl") {
|
|
421
|
+
emitJsonl(jsonlRows ?? [payload]);
|
|
422
|
+
return true;
|
|
423
|
+
}
|
|
424
|
+
return false;
|
|
425
|
+
}
|
|
426
|
+
async function bingSync(project, opts) {
|
|
427
|
+
const client = getClient();
|
|
428
|
+
const run = await client.bingSync(project);
|
|
429
|
+
if (!opts.wait) {
|
|
430
|
+
if (isMachineFormat(opts.format)) {
|
|
431
|
+
console.log(JSON.stringify(run, null, 2));
|
|
432
|
+
return;
|
|
433
|
+
}
|
|
434
|
+
console.log(`Bing data sync started (run ${run.id}). Check it with "canonry runs get ${run.id}".`);
|
|
435
|
+
return;
|
|
436
|
+
}
|
|
437
|
+
const current = await waitForRunStatus(client, run.id, {
|
|
438
|
+
timeoutMs: 30 * 60 * 1e3,
|
|
439
|
+
intervalMs: 3e3,
|
|
440
|
+
progressLabel: "Waiting for the Bing data sync",
|
|
441
|
+
successStatuses: ["completed", "partial"],
|
|
442
|
+
failureStatuses: ["failed"],
|
|
443
|
+
timeoutCode: "BING_SYNC_TIMEOUT",
|
|
444
|
+
failureCode: "BING_SYNC_FAILED",
|
|
445
|
+
timeoutMessage: "Timed out waiting for the Bing data sync to finish.",
|
|
446
|
+
failureMessage: "The Bing data sync failed.",
|
|
447
|
+
details: { project }
|
|
448
|
+
});
|
|
449
|
+
if (isMachineFormat(opts.format)) {
|
|
450
|
+
console.log(JSON.stringify(current, null, 2));
|
|
451
|
+
return;
|
|
452
|
+
}
|
|
453
|
+
console.log(current.status === "partial" ? `Bing data sync finished with some sections missing: ${current.error ? formatRunErrorOneLine(current.error) : "see the run for details"}` : "Bing data sync completed.");
|
|
454
|
+
}
|
|
455
|
+
function windowOption(value) {
|
|
456
|
+
return value === void 0 ? void 0 : value;
|
|
457
|
+
}
|
|
458
|
+
function trafficComparisonLines(comparison) {
|
|
459
|
+
if (!comparison) return [" No comparison period."];
|
|
460
|
+
const basis = comparison.basis === "split-window" ? `last ${comparison.days} days vs the ${comparison.days} days before, inside this window` : `vs prior ${comparison.days} days`;
|
|
461
|
+
const pct = (ratio) => ratio === null ? "n/a" : `${ratio >= 0 ? "+" : ""}${formatPercent(ratio)}`;
|
|
462
|
+
return [
|
|
463
|
+
` Change, ${basis}:`,
|
|
464
|
+
` clicks ${pct(comparison.change.clicks)} (${comparison.prior.clicks} -> ${comparison.trailing.clicks})`,
|
|
465
|
+
` impressions ${pct(comparison.change.impressions)} (${comparison.prior.impressions} -> ${comparison.trailing.impressions})`
|
|
466
|
+
];
|
|
467
|
+
}
|
|
468
|
+
async function bingTraffic(project, opts) {
|
|
469
|
+
const client = getClient();
|
|
470
|
+
const result = await client.bingTraffic(project, { startDate: opts.startDate, endDate: opts.endDate, days: opts.days, window: windowOption(opts.window) });
|
|
471
|
+
if (printJson(opts.format, result, result.rows.map((row) => ({ project, ...row })))) return;
|
|
472
|
+
if (result.rows.length === 0) {
|
|
473
|
+
console.log('No stored Bing traffic. Run "canonry bing sync <project>" first.');
|
|
474
|
+
return;
|
|
475
|
+
}
|
|
476
|
+
console.log(`Bing search traffic, ${windowLabel(result.window)}: ${result.totals.clicks} clicks, ${result.totals.impressions} impressions, ${formatPercent(result.totals.ctr)} CTR`);
|
|
477
|
+
for (const line of trafficComparisonLines(result.periodComparison)) console.log(line);
|
|
478
|
+
console.log("");
|
|
479
|
+
console.log(` ${pad("DATE", 12)}${pad("CLICKS", 9)}${pad("IMPR", 10)}CTR`);
|
|
480
|
+
for (const row of result.rows) {
|
|
481
|
+
console.log(` ${pad(row.date, 12)}${pad(row.clicks, 9)}${pad(row.impressions, 10)}${formatPercent(row.ctr)}`);
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
async function bingQueries(project, opts) {
|
|
485
|
+
const client = getClient();
|
|
486
|
+
const result = await client.bingQueries(project, {
|
|
487
|
+
startDate: opts.startDate,
|
|
488
|
+
endDate: opts.endDate,
|
|
489
|
+
days: opts.days,
|
|
490
|
+
window: windowOption(opts.window),
|
|
491
|
+
query: opts.query,
|
|
492
|
+
match: opts.exact ? "exact" : void 0,
|
|
493
|
+
groupBy: opts.groupBy,
|
|
494
|
+
limit: opts.limit,
|
|
495
|
+
offset: opts.offset
|
|
496
|
+
});
|
|
497
|
+
const rows = result.groupBy === "week" ? result.weekly : result.rows;
|
|
498
|
+
if (printJson(opts.format, result, rows.map((row) => ({ project, ...row })))) return;
|
|
499
|
+
printWeeklyTable("query", result.groupBy, result.rows.map(({ query, ...rest }) => ({ key: query, ...rest })), result.weekly, result);
|
|
500
|
+
}
|
|
501
|
+
async function bingPages(project, opts) {
|
|
502
|
+
const client = getClient();
|
|
503
|
+
const result = await client.bingPages(project, {
|
|
504
|
+
startDate: opts.startDate,
|
|
505
|
+
endDate: opts.endDate,
|
|
506
|
+
days: opts.days,
|
|
507
|
+
window: windowOption(opts.window),
|
|
508
|
+
page: opts.page,
|
|
509
|
+
match: opts.exact ? "exact" : void 0,
|
|
510
|
+
groupBy: opts.groupBy,
|
|
511
|
+
limit: opts.limit,
|
|
512
|
+
offset: opts.offset
|
|
513
|
+
});
|
|
514
|
+
const rows = result.groupBy === "week" ? result.weekly : result.rows;
|
|
515
|
+
if (printJson(opts.format, result, rows.map((row) => ({ project, ...row })))) return;
|
|
516
|
+
printWeeklyTable("page", result.groupBy, result.rows.map(({ page, ...rest }) => ({ key: page, ...rest })), result.weekly, result);
|
|
517
|
+
}
|
|
518
|
+
function printWeeklyTable(noun, groupBy, keyed, weekly, meta) {
|
|
519
|
+
if (groupBy === "week") {
|
|
520
|
+
if (weekly.length === 0) {
|
|
521
|
+
console.log('No stored Bing stats for this window. Run "canonry bing sync <project>" first.');
|
|
522
|
+
return;
|
|
523
|
+
}
|
|
524
|
+
console.log(`Bing ${noun} stats by week, ${windowLabel(meta.window)}:
|
|
525
|
+
`);
|
|
526
|
+
console.log(` ${pad("WEEK", 12)}${pad("CLICKS", 9)}${pad("IMPR", 10)}${pad("CTR", 9)}POS`);
|
|
527
|
+
for (const row of weekly) {
|
|
528
|
+
console.log(` ${pad(row.date, 12)}${pad(row.clicks, 9)}${pad(row.impressions, 10)}${pad(formatPercent(row.ctr), 9)}${position(row.avgImpressionPosition)}`);
|
|
529
|
+
}
|
|
530
|
+
return;
|
|
531
|
+
}
|
|
532
|
+
if (keyed.length === 0) {
|
|
533
|
+
console.log('No stored Bing stats for this window. Run "canonry bing sync <project>" first.');
|
|
534
|
+
return;
|
|
535
|
+
}
|
|
536
|
+
const width = noun === "page" ? 60 : 42;
|
|
537
|
+
console.log(`Bing ${noun} stats, ${windowLabel(meta.window)} (${meta.totalMatching} ${noun === "page" ? "pages" : "queries"}; Bing reports these per week):`);
|
|
538
|
+
if (meta.totals) {
|
|
539
|
+
console.log(` All matching: ${meta.totals.clicks} clicks, ${meta.totals.impressions} impressions, position ${position(meta.totals.avgImpressionPosition)}`);
|
|
540
|
+
}
|
|
541
|
+
if (meta.comparison) {
|
|
542
|
+
const change = meta.comparison.difference;
|
|
543
|
+
const signed = (n) => `${n >= 0 ? "+" : ""}${n}`;
|
|
544
|
+
console.log(` vs prior ${meta.comparison.days} days: clicks ${signed(change.clicks)}, impressions ${signed(change.impressions)}, position ${change.avgImpressionPosition === null ? "n/a" : signed(change.avgImpressionPosition)}`);
|
|
545
|
+
}
|
|
546
|
+
console.log("");
|
|
547
|
+
console.log(` ${pad(noun.toUpperCase(), width)}${pad("CLICKS", 9)}${pad("IMPR", 10)}${pad("CTR", 9)}${pad("POS", 7)}WEEKS`);
|
|
548
|
+
for (const row of keyed) {
|
|
549
|
+
console.log(` ${pad(row.key, width)}${pad(row.clicks, 9)}${pad(row.impressions, 10)}${pad(formatPercent(row.ctr), 9)}${pad(position(row.avgImpressionPosition), 7)}${row.weeks}`);
|
|
550
|
+
}
|
|
551
|
+
if (meta.truncated) console.log(`
|
|
552
|
+
More rows follow; page with --offset, or use --format json.`);
|
|
553
|
+
}
|
|
554
|
+
async function bingCrawlStats(project, opts) {
|
|
555
|
+
const client = getClient();
|
|
556
|
+
const result = await client.bingCrawlStats(project, { startDate: opts.startDate, endDate: opts.endDate, days: opts.days });
|
|
557
|
+
if (printJson(opts.format, result, result.rows.map((row) => ({ project, ...row })))) return;
|
|
558
|
+
if (result.rows.length === 0) {
|
|
559
|
+
console.log('No stored Bing crawl stats. Run "canonry bing sync <project>" first.');
|
|
560
|
+
return;
|
|
561
|
+
}
|
|
562
|
+
console.log(`Bingbot crawl stats, ${windowLabel(result.window)}:
|
|
563
|
+
`);
|
|
564
|
+
console.log(` ${pad("DATE", 12)}${pad("CRAWLED", 9)}${pad("INDEXED", 9)}${pad("ERRORS", 8)}${pad("2XX", 6)}${pad("301", 6)}${pad("4XX", 6)}${pad("5XX", 6)}ROBOTS`);
|
|
565
|
+
for (const row of result.rows) {
|
|
566
|
+
console.log(` ${pad(row.date, 12)}${pad(row.crawledPages, 9)}${pad(row.inIndex, 9)}${pad(row.crawlErrors, 8)}${pad(row.code2xx, 6)}${pad(row.code301, 6)}${pad(row.code4xx, 6)}${pad(row.code5xx, 6)}${row.blockedByRobotsTxt}`);
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
async function bingSitemaps(project, format) {
|
|
570
|
+
const client = getClient();
|
|
571
|
+
const result = await client.bingSitemaps(project);
|
|
572
|
+
if (printJson(format, result, result.sitemaps.map((row) => ({ project, ...row })))) return;
|
|
573
|
+
if (result.sitemaps.length === 0) {
|
|
574
|
+
console.log('Bing has no sitemaps stored for this project. Run "canonry bing sync <project>", or submit one in Bing Webmaster Tools.');
|
|
575
|
+
return;
|
|
576
|
+
}
|
|
577
|
+
console.log(`Bing sitemaps (as of ${result.syncedAt}):
|
|
578
|
+
`);
|
|
579
|
+
for (const row of result.sitemaps) {
|
|
580
|
+
const indent = row.parentUrl ? " " : " ";
|
|
581
|
+
console.log(`${indent}${row.url}`);
|
|
582
|
+
console.log(`${indent} ${row.type}, ${row.status}, ${row.urlCount} URLs, last crawled ${row.lastCrawledAt ?? "never"}`);
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
function describeSnapshot(kind, payload) {
|
|
586
|
+
if (payload === null || payload === void 0) return "no data";
|
|
587
|
+
if (Array.isArray(payload)) return payload.length === 0 ? "none" : `${payload.length} item(s)`;
|
|
588
|
+
if (typeof payload === "object") {
|
|
589
|
+
const record = payload;
|
|
590
|
+
if (kind.endsWith("submission-quota")) return `${String(record.dailyQuota)} left today, ${String(record.monthlyQuota)} left this month`;
|
|
591
|
+
if (kind === "crawl-settings") {
|
|
592
|
+
const rates = Array.isArray(record.crawlRate) ? record.crawlRate : [];
|
|
593
|
+
return `crawl boost ${record.crawlBoostEnabled ? "on" : "off"}${record.crawlBoostAvailable ? "" : " (not available)"}, hourly rate ${rates.length ? `${Math.min(...rates)} to ${Math.max(...rates)}` : "unknown"}`;
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
return JSON.stringify(payload);
|
|
597
|
+
}
|
|
598
|
+
async function bingSite(project, opts) {
|
|
599
|
+
const client = getClient();
|
|
600
|
+
const result = await client.bingSite(project, { kind: opts.kind });
|
|
601
|
+
if (printJson(opts.format, result, result.snapshots.map((row) => ({ project, ...row })))) return;
|
|
602
|
+
if (result.snapshots.length === 0) {
|
|
603
|
+
console.log('No stored Bing site settings. Run "canonry bing sync <project>" first.');
|
|
604
|
+
return;
|
|
605
|
+
}
|
|
606
|
+
console.log(`Bing site settings (as of ${result.syncedAt}):
|
|
607
|
+
`);
|
|
608
|
+
for (const snap of result.snapshots) {
|
|
609
|
+
const state = snap.status === "ok" ? describeSnapshot(snap.kind, snap.payload) : snap.status === "unauthorized" ? "not readable with this key (owner-only)" : `could not be read: ${snap.error ?? "unknown error"}`;
|
|
610
|
+
console.log(` ${pad(snap.kind, 27)}${state}`);
|
|
611
|
+
if (snap.status === "ok" && Array.isArray(snap.payload)) {
|
|
612
|
+
for (const item of snap.payload.slice(0, 10)) console.log(` ${JSON.stringify(item)}`);
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
async function bingKeywordDemand(project, opts) {
|
|
617
|
+
const client = getClient();
|
|
618
|
+
const result = await client.bingKeywordDemand(project, { query: opts.query });
|
|
619
|
+
if (printJson(opts.format, result, (opts.query ? result.history : result.rows).map((row) => ({ project, ...row })))) return;
|
|
620
|
+
if (opts.query) {
|
|
621
|
+
if (result.history.length === 0) {
|
|
622
|
+
console.log(`No stored Bing demand for "${opts.query}". It is read during "canonry bing sync" for tracked queries.`);
|
|
623
|
+
return;
|
|
624
|
+
}
|
|
625
|
+
console.log(`Weekly Bing searches for "${opts.query}" (${result.market.country}, ${result.market.language}):
|
|
626
|
+
`);
|
|
627
|
+
console.log(` ${pad("WEEK", 12)}${pad("EXACT", 9)}BROAD`);
|
|
628
|
+
for (const week of result.history) console.log(` ${pad(week.date, 12)}${pad(week.impressions, 9)}${week.broadImpressions}`);
|
|
629
|
+
return;
|
|
630
|
+
}
|
|
631
|
+
if (result.rows.length === 0) {
|
|
632
|
+
console.log("No tracked queries.");
|
|
633
|
+
return;
|
|
634
|
+
}
|
|
635
|
+
console.log(`Bing search demand for tracked queries (${result.market.country}, ${result.market.language}; exact-match searches per week):
|
|
636
|
+
`);
|
|
637
|
+
console.log(` ${pad("QUERY", 46)}${pad("AVG/WK", 9)}${pad("LATEST", 9)}WEEKS`);
|
|
638
|
+
for (const row of result.rows) {
|
|
639
|
+
console.log(` ${pad(row.query, 46)}${pad(row.avgWeeklyImpressions ?? "-", 9)}${pad(row.latestImpressions ?? "-", 9)}${row.weeks}`);
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
async function bingQueryPages(project, searchQuery, format) {
|
|
643
|
+
const client = getClient();
|
|
644
|
+
const result = await client.bingQueryPages(project, searchQuery);
|
|
645
|
+
if (printJson(format, result, result.rows.map((row) => ({ project, query: searchQuery, ...row })))) return;
|
|
646
|
+
printStatsRows(`Pages Bing showed for "${searchQuery}" (per week, live from Bing):`, "PAGE", result.rows);
|
|
647
|
+
}
|
|
648
|
+
async function bingPageQueries(project, page, format) {
|
|
649
|
+
const client = getClient();
|
|
650
|
+
const result = await client.bingPageQueries(project, page);
|
|
651
|
+
if (printJson(format, result, result.rows.map((row) => ({ project, page, ...row })))) return;
|
|
652
|
+
printStatsRows(`Queries ${page} showed for on Bing (per week, live from Bing):`, "QUERY", result.rows);
|
|
653
|
+
}
|
|
654
|
+
function printStatsRows(title, keyLabel, rows) {
|
|
655
|
+
if (rows.length === 0) {
|
|
656
|
+
console.log("Bing returned no rows.");
|
|
657
|
+
return;
|
|
658
|
+
}
|
|
659
|
+
console.log(`${title}
|
|
660
|
+
`);
|
|
661
|
+
console.log(` ${pad("WEEK", 12)}${pad(keyLabel, 52)}${pad("CLICKS", 9)}${pad("IMPR", 9)}POS`);
|
|
662
|
+
for (const row of rows) {
|
|
663
|
+
console.log(` ${pad(row.date ?? "-", 12)}${pad(row.key, 52)}${pad(row.clicks, 9)}${pad(row.impressions, 9)}${position(row.avgImpressionPosition)}`);
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
async function bingQueryHistory(project, opts) {
|
|
667
|
+
const client = getClient();
|
|
668
|
+
const result = await client.bingQueryHistory(project, { query: opts.query, page: opts.page });
|
|
669
|
+
if (printJson(opts.format, result, result.rows.map((row) => ({ project, query: opts.query, page: opts.page ?? null, ...row })))) return;
|
|
670
|
+
if (result.rows.length === 0) {
|
|
671
|
+
console.log("Bing returned no rows.");
|
|
672
|
+
return;
|
|
673
|
+
}
|
|
674
|
+
console.log(`"${opts.query}"${opts.page ? ` on ${opts.page}` : ""} on Bing, per ${result.granularity} (live from Bing):
|
|
675
|
+
`);
|
|
676
|
+
console.log(` ${pad(result.granularity.toUpperCase(), 12)}${pad("CLICKS", 9)}${pad("IMPR", 9)}${pad("CTR", 9)}POS`);
|
|
677
|
+
for (const row of result.rows) {
|
|
678
|
+
console.log(` ${pad(row.date ?? "-", 12)}${pad(row.clicks, 9)}${pad(row.impressions, 9)}${pad(formatPercent(row.ctr), 9)}${position(row.position)}`);
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
async function bingUrlTraffic(project, opts) {
|
|
682
|
+
const client = getClient();
|
|
683
|
+
const result = await client.bingUrlTraffic(project, { url: opts.url, children: opts.children ? "true" : void 0, page: opts.page === void 0 ? void 0 : String(opts.page) });
|
|
684
|
+
if (printJson(opts.format, result, [result.url, ...result.children].map((row) => ({ project, ...row })))) return;
|
|
685
|
+
console.log(`${result.url.url}: ${result.url.clicks} clicks, ${result.url.impressions} impressions on Bing`);
|
|
686
|
+
if (opts.children) {
|
|
687
|
+
if (result.children.length === 0) console.log(" Bing listed no URLs under it.");
|
|
688
|
+
for (const child of result.children) console.log(` ${pad(child.url, 70)}${pad(child.clicks, 9)}${child.impressions}`);
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
async function bingUrlChildren(project, opts) {
|
|
692
|
+
const client = getClient();
|
|
693
|
+
const result = await client.bingUrlChildren(project, { url: opts.url, page: opts.page === void 0 ? void 0 : String(opts.page) });
|
|
694
|
+
if (printJson(opts.format, result, result.rows.map((row) => ({ project, ...row })))) return;
|
|
695
|
+
if (result.rows.length === 0) {
|
|
696
|
+
console.log("Bing listed no URLs under this one.");
|
|
697
|
+
return;
|
|
698
|
+
}
|
|
699
|
+
for (const row of result.rows) {
|
|
700
|
+
console.log(` ${pad(row.url, 70)}HTTP ${row.httpStatus ?? "-"}, last crawled ${row.lastCrawledDate ?? "never"}`);
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
async function bingLinks(project, opts) {
|
|
704
|
+
const client = getClient();
|
|
705
|
+
const result = await client.bingLinks(project, { url: opts.url, page: opts.page === void 0 ? void 0 : String(opts.page) });
|
|
706
|
+
if (printJson(opts.format, result, result.rows.map((row) => ({ project, ...row })))) return;
|
|
707
|
+
if (result.rows.length === 0) {
|
|
708
|
+
console.log('Bing reported no inbound links. Bing often returns none for smaller sites; Common Crawl backlinks ("canonry backlinks") cover more.');
|
|
709
|
+
return;
|
|
710
|
+
}
|
|
711
|
+
console.log(opts.url ? `External pages linking to ${opts.url} (page ${result.page + 1} of ${result.totalPages}):
|
|
712
|
+
` : `Pages with inbound links (page ${result.page + 1} of ${result.totalPages}):
|
|
713
|
+
`);
|
|
714
|
+
for (const row of result.rows) {
|
|
715
|
+
console.log(opts.url ? ` ${row.url}${row.anchorText ? ` "${row.anchorText}"` : ""}` : ` ${pad(row.url, 70)}${row.count ?? "-"}`);
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
async function bingFetchedUrls(project, opts) {
|
|
719
|
+
const client = getClient();
|
|
720
|
+
const result = await client.bingFetchedUrls(project, { url: opts.url });
|
|
721
|
+
if (printJson(opts.format, result, opts.url ? [result.details] : result.rows.map((row) => ({ project, ...row })))) return;
|
|
722
|
+
if (opts.url) {
|
|
723
|
+
if (!result.details) {
|
|
724
|
+
console.log("Bing has no Fetch as Bingbot result for this URL.");
|
|
725
|
+
return;
|
|
726
|
+
}
|
|
727
|
+
console.log(`${result.details.url}: ${result.details.status ?? "unknown status"} (${result.details.date ?? "no date"})`);
|
|
728
|
+
if (result.details.headers) console.log(`
|
|
729
|
+
${result.details.headers}`);
|
|
730
|
+
return;
|
|
731
|
+
}
|
|
732
|
+
if (result.rows.length === 0) {
|
|
733
|
+
console.log("No Fetch as Bingbot history.");
|
|
734
|
+
return;
|
|
735
|
+
}
|
|
736
|
+
for (const row of result.rows) console.log(` ${pad(row.url, 70)}${row.date ?? "-"}${row.fetched === false ? " (pending)" : ""}`);
|
|
737
|
+
}
|
|
738
|
+
async function bingKeywords(project, opts) {
|
|
739
|
+
const client = getClient();
|
|
740
|
+
const result = await client.bingKeywords(project, opts);
|
|
741
|
+
if (printJson(opts.format, result, result.related.map((row) => ({ project, keyword: opts.keyword, ...row })))) return;
|
|
742
|
+
console.log(`"${result.keyword}" on Bing (${result.market.country}, ${result.market.language}), ${result.window.startDate} to ${result.window.endDate}:`);
|
|
743
|
+
console.log(result.volume ? ` ${result.volume.impressions} exact-match searches, ${result.volume.broadImpressions} broad-match` : " Bing has no volume for this keyword.");
|
|
744
|
+
if (result.history.length > 0) {
|
|
745
|
+
const recent = result.history.slice(-8);
|
|
746
|
+
console.log(`
|
|
747
|
+
Weekly exact-match searches (last ${recent.length} weeks): ${recent.map((week) => week.impressions).join(", ")}`);
|
|
748
|
+
}
|
|
749
|
+
if (result.related.length > 0) {
|
|
750
|
+
console.log(`
|
|
751
|
+
${pad("RELATED KEYWORD", 46)}${pad("EXACT", 9)}BROAD`);
|
|
752
|
+
for (const row of result.related.slice(0, 25)) console.log(` ${pad(row.query, 46)}${pad(row.impressions, 9)}${row.broadImpressions}`);
|
|
753
|
+
}
|
|
754
|
+
}
|
|
755
|
+
export {
|
|
756
|
+
bingConnect,
|
|
757
|
+
bingCoverage,
|
|
758
|
+
bingCoverageHistory,
|
|
759
|
+
bingCrawlStats,
|
|
760
|
+
bingDisconnect,
|
|
761
|
+
bingFetchedUrls,
|
|
762
|
+
bingInspect,
|
|
763
|
+
bingInspectSitemap,
|
|
764
|
+
bingInspections,
|
|
765
|
+
bingKeywordDemand,
|
|
766
|
+
bingKeywords,
|
|
767
|
+
bingLinks,
|
|
768
|
+
bingPageQueries,
|
|
769
|
+
bingPages,
|
|
770
|
+
bingPerformance,
|
|
771
|
+
bingQueries,
|
|
772
|
+
bingQueryHistory,
|
|
773
|
+
bingQueryPages,
|
|
774
|
+
bingRefresh,
|
|
775
|
+
bingRequestIndexing,
|
|
776
|
+
bingSetSite,
|
|
777
|
+
bingSite,
|
|
778
|
+
bingSitemaps,
|
|
779
|
+
bingSites,
|
|
780
|
+
bingStatus,
|
|
781
|
+
bingSync,
|
|
782
|
+
bingTraffic,
|
|
783
|
+
bingUrlChildren,
|
|
784
|
+
bingUrlTraffic
|
|
785
|
+
};
|