@canonry/canonry 7.25.0 → 7.43.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -2
- package/assets/agent-workspace/skills/canonry/references/aeo-analysis.md +24 -0
- package/assets/agent-workspace/skills/canonry/references/canonry-cli.md +219 -6
- package/assets/agent-workspace/skills/canonry/references/server-side-traffic.md +14 -2
- package/assets/assets/{AuditHistoryPanel-B-9DLSmD.js → AuditHistoryPanel-C5oZsvsR.js} +1 -1
- package/assets/assets/{BacklinksPage-DPssWU5-.js → BacklinksPage-CF5sGSTQ.js} +1 -1
- package/assets/assets/{HistoryPage-BAPUoHjI.js → HistoryPage-DumL6cl5.js} +1 -1
- package/assets/assets/{MeasurementPropertyPage-aTs5l1OV.js → MeasurementPropertyPage-DCegOp_w.js} +3 -3
- package/assets/assets/ProjectPage-CWDk48Y6.js +8 -0
- package/assets/assets/RunsPage-Bb1VMWYu.js +1 -0
- package/assets/assets/{SettingsPage-Oo861eRm.js → SettingsPage-CHGh169l.js} +1 -1
- package/assets/assets/{SiteHealthSection-BMdk3Och.js → SiteHealthSection-bGzH9m9-.js} +3 -3
- package/assets/assets/{TrafficPage-Ciot_0lN.js → TrafficPage-cfnEZ2Mu.js} +1 -1
- package/assets/assets/{TrafficSourceDetailPage-D7tWadfJ.js → TrafficSourceDetailPage-BQS0MAXE.js} +1 -1
- package/assets/assets/{extract-error-message-Bb4azvlK.js → extract-error-message-IWhACkO_.js} +1 -1
- package/assets/assets/index-BDY3-V1S.js +86 -0
- package/assets/assets/{react-sigma_core.esm.min-C-Y9Z74U.js → react-sigma_core.esm.min-BQt189AI.js} +1 -1
- package/assets/assets/{v2-overview-adapter-D2S25ivE.js → v2-overview-adapter-ZC3VuoTT.js} +1 -1
- package/assets/index.html +1 -1
- package/dist/{chunk-PKQF7BV2.js → chunk-65BHDVYO.js} +5650 -4002
- package/dist/{chunk-NOA7KJDB.js → chunk-BPFRGC7K.js} +3 -3
- package/dist/{chunk-I3546GTK.js → chunk-BWR5MNL5.js} +2 -2
- package/dist/chunk-C2I7M3B2.js +22948 -0
- package/dist/{chunk-PFEQWGJN.js → chunk-E43JM3QV.js} +11444 -10397
- package/dist/chunk-HGMYEDAQ.js +79 -0
- package/dist/{chunk-BMBHREYM.js → chunk-KOIOZ7QW.js} +195 -20
- package/dist/{chunk-6AOIFHC6.js → chunk-LYKDNJXR.js} +163 -107
- package/dist/chunk-WRUKKEGR.js +562 -0
- package/dist/chunk-WU2BRWXY.js +546 -0
- package/dist/{chunk-NX257AKC.js → chunk-WXX3NL4C.js} +108 -41
- package/dist/cli-catalog-J3O6W726.js +15 -0
- package/dist/cli-commands-QST2CPDG.js +15 -0
- package/dist/cli.js +136 -22502
- package/dist/{demo-server-RUZWYXSW.js → demo-server-KA7DWTY6.js} +5 -4
- package/dist/index.js +7 -6
- package/dist/{intelligence-service-TYSVXO3S.js → intelligence-service-LHZZTJPH.js} +2 -2
- package/dist/mcp.js +30 -15
- package/package.json +6 -6
- package/assets/assets/ProjectPage-sVY1ETmt.js +0 -8
- package/assets/assets/RunsPage-D0q7ifK3.js +0 -1
- package/assets/assets/index-ClA5Nf2V.js +0 -86
|
@@ -0,0 +1,562 @@
|
|
|
1
|
+
import {
|
|
2
|
+
emitJsonl,
|
|
3
|
+
readWindowOptions,
|
|
4
|
+
resolveCliAccess,
|
|
5
|
+
resolveCliAccessForInvocation,
|
|
6
|
+
windowOptions
|
|
7
|
+
} from "./chunk-WU2BRWXY.js";
|
|
8
|
+
import {
|
|
9
|
+
CliError,
|
|
10
|
+
EXIT_USER_ERROR,
|
|
11
|
+
usageError
|
|
12
|
+
} from "./chunk-HGMYEDAQ.js";
|
|
13
|
+
import {
|
|
14
|
+
OperationAccesses
|
|
15
|
+
} from "./chunk-E43JM3QV.js";
|
|
16
|
+
|
|
17
|
+
// src/cli-catalog.ts
|
|
18
|
+
import { createRequire } from "module";
|
|
19
|
+
|
|
20
|
+
// src/cli-dispatch.ts
|
|
21
|
+
import { parseArgs } from "util";
|
|
22
|
+
function commandId(spec) {
|
|
23
|
+
return spec.path.join(".");
|
|
24
|
+
}
|
|
25
|
+
function commandUsage(spec) {
|
|
26
|
+
let usage = spec.usage;
|
|
27
|
+
if (spec.windows) {
|
|
28
|
+
const selection = `[--window ${spec.windows.join("|")}]`;
|
|
29
|
+
if (usage.includes("[--window ")) usage = usage.replace(/\[--window [^\]]+\]/, selection);
|
|
30
|
+
else if (spec.windowAliases?.window === "period") usage = usage.replace(/\[--period [^\]]+\]/, selection);
|
|
31
|
+
else usage += ` ${selection}`;
|
|
32
|
+
}
|
|
33
|
+
for (const key of ["start", "end"]) {
|
|
34
|
+
const alias = spec.windowAliases?.[key];
|
|
35
|
+
if (alias) usage = usage.replace(`--${alias} `, `--${key} `);
|
|
36
|
+
}
|
|
37
|
+
return usage;
|
|
38
|
+
}
|
|
39
|
+
function printCommandHelp(spec) {
|
|
40
|
+
console.log(`
|
|
41
|
+
Usage: ${commandUsage(spec)}
|
|
42
|
+
`);
|
|
43
|
+
const access = spec.access ?? OperationAccesses.stored;
|
|
44
|
+
console.log(`Access: ${access} (${access === OperationAccesses.live ? "calls a provider now" : access === OperationAccesses.write ? "changes server state" : "uses stored data"})
|
|
45
|
+
`);
|
|
46
|
+
if (spec.help) console.log(`${spec.help}
|
|
47
|
+
`);
|
|
48
|
+
const aliases = Object.entries(spec.windowAliases ?? {}).map(([canonical, alias]) => `--${alias} for --${canonical}`);
|
|
49
|
+
if (aliases.length) console.log(`Aliases: ${aliases.join("; ")}. Do not combine an alias with its canonical flag.
|
|
50
|
+
`);
|
|
51
|
+
}
|
|
52
|
+
function matchesPath(args, path) {
|
|
53
|
+
if (args.length < path.length) return false;
|
|
54
|
+
return path.every((segment, index) => args[index] === segment);
|
|
55
|
+
}
|
|
56
|
+
var CLI_GLOBAL_OPTIONS = {
|
|
57
|
+
format: { type: "string" },
|
|
58
|
+
"dry-run": { type: "boolean" },
|
|
59
|
+
trace: { type: "boolean" },
|
|
60
|
+
access: { type: "string" },
|
|
61
|
+
"stored-only": { type: "boolean" }
|
|
62
|
+
};
|
|
63
|
+
function commandOptions(spec) {
|
|
64
|
+
return { ...CLI_GLOBAL_OPTIONS, ...spec.options, ...windowOptions({ window: Boolean(spec.windows), dates: Boolean(spec.options?.start || spec.windowAliases?.start), aliases: spec.windowAliases }) };
|
|
65
|
+
}
|
|
66
|
+
function accessFilterFromArgs(args) {
|
|
67
|
+
const index = args.findIndex((arg2) => arg2 === "--access" || arg2.startsWith("--access="));
|
|
68
|
+
if (index < 0) return void 0;
|
|
69
|
+
const arg = args[index];
|
|
70
|
+
const value = arg.startsWith("--access=") ? arg.slice("--access=".length) : args[index + 1];
|
|
71
|
+
if (!value || value.startsWith("--")) throw usageError("--access requires stored, live, or write", { details: { option: "access" } });
|
|
72
|
+
return value;
|
|
73
|
+
}
|
|
74
|
+
function validateAccessFilter(value) {
|
|
75
|
+
if (value !== void 0 && value !== OperationAccesses.stored && value !== OperationAccesses.live && value !== OperationAccesses.write) {
|
|
76
|
+
throw usageError(`Unknown access filter: ${value}`, { details: { access: value } });
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
function toFormat(value, fallbackFormat) {
|
|
80
|
+
if (value === "json") return "json";
|
|
81
|
+
if (value === "jsonl") return "jsonl";
|
|
82
|
+
return fallbackFormat;
|
|
83
|
+
}
|
|
84
|
+
function printGroupHelp(group, specs, accessFilter) {
|
|
85
|
+
const parentSpec = specs.find((s) => s.path.length === 1 && s.path[0] === group);
|
|
86
|
+
const groupSpecs = specs.filter((s) => s.path[0] === group && s.path.length > 1 && (!accessFilter || (s.access ?? OperationAccesses.stored) === accessFilter)).sort((a, b) => a.path.join(" ").localeCompare(b.path.join(" ")));
|
|
87
|
+
if (groupSpecs.length === 0) {
|
|
88
|
+
if (parentSpec && (!accessFilter || (parentSpec.access ?? OperationAccesses.stored) === accessFilter)) printCommandHelp(parentSpec);
|
|
89
|
+
else if (accessFilter) console.log(`No commands in group "${group}" match access "${accessFilter}".`);
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
console.log(`
|
|
93
|
+
Usage: canonry ${group} <command> [options]
|
|
94
|
+
`);
|
|
95
|
+
if (parentSpec && (!accessFilter || (parentSpec.access ?? OperationAccesses.stored) === accessFilter)) {
|
|
96
|
+
const access = parentSpec.access ?? OperationAccesses.stored;
|
|
97
|
+
console.log(` [${access}] ${commandUsage(parentSpec)}
|
|
98
|
+
`);
|
|
99
|
+
}
|
|
100
|
+
console.log("Subcommands:");
|
|
101
|
+
for (const spec of groupSpecs) {
|
|
102
|
+
console.log(` [${spec.access ?? OperationAccesses.stored}] ${commandUsage(spec)}`);
|
|
103
|
+
}
|
|
104
|
+
console.log();
|
|
105
|
+
}
|
|
106
|
+
async function dispatchRegisteredCommand(args, fallbackFormat, specs) {
|
|
107
|
+
if (args.length >= 1 && (args.includes("--help") || args.includes("-h"))) {
|
|
108
|
+
const argsWithoutHelp = args.filter((a) => a !== "--help" && a !== "-h");
|
|
109
|
+
const accessValue = accessFilterFromArgs(argsWithoutHelp);
|
|
110
|
+
validateAccessFilter(accessValue);
|
|
111
|
+
if (fallbackFormat !== "text") {
|
|
112
|
+
const { printCliCatalog: printCliCatalog2 } = await import("./cli-catalog-J3O6W726.js");
|
|
113
|
+
const group2 = argsWithoutHelp.find((arg) => !arg.startsWith("--"));
|
|
114
|
+
printCliCatalog2(specs, fallbackFormat, group2, accessValue);
|
|
115
|
+
return true;
|
|
116
|
+
}
|
|
117
|
+
const exactMatch = [...specs].sort((a, b) => b.path.length - a.path.length).find((candidate) => matchesPath(argsWithoutHelp, candidate.path));
|
|
118
|
+
if (exactMatch && exactMatch.path.length > 1) {
|
|
119
|
+
if (accessValue !== void 0 && (exactMatch.access ?? OperationAccesses.stored) !== accessValue) {
|
|
120
|
+
console.log(`No command "${exactMatch.path.join(" ")}" matches access "${accessValue}".`);
|
|
121
|
+
} else {
|
|
122
|
+
printCommandHelp(exactMatch);
|
|
123
|
+
}
|
|
124
|
+
return true;
|
|
125
|
+
}
|
|
126
|
+
const group = argsWithoutHelp[0];
|
|
127
|
+
if (group) {
|
|
128
|
+
const groupSpecs = specs.filter((s) => s.path[0] === group && s.path.length > 1);
|
|
129
|
+
if (groupSpecs.length > 0) {
|
|
130
|
+
printGroupHelp(group, specs, accessValue);
|
|
131
|
+
return true;
|
|
132
|
+
}
|
|
133
|
+
const single = specs.find((s) => s.path.length === 1 && s.path[0] === group);
|
|
134
|
+
if (single) {
|
|
135
|
+
printGroupHelp(group, specs, accessValue);
|
|
136
|
+
return true;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
const spec = [...specs].sort((a, b) => b.path.length - a.path.length).find((candidate) => matchesPath(args, candidate.path));
|
|
141
|
+
if (!spec) {
|
|
142
|
+
const root = args[0];
|
|
143
|
+
if (root && specs.some((candidate) => candidate.path[0] === root)) {
|
|
144
|
+
if (args.length === 1) {
|
|
145
|
+
printGroupHelp(root, specs);
|
|
146
|
+
return true;
|
|
147
|
+
}
|
|
148
|
+
throw usageError(`Error: unknown ${root} subcommand: ${args[1]}`, {
|
|
149
|
+
message: `unknown ${root} subcommand`,
|
|
150
|
+
details: { command: root, subcommand: args[1] }
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
return false;
|
|
154
|
+
}
|
|
155
|
+
const remainingArgs = args.slice(spec.path.length);
|
|
156
|
+
let values;
|
|
157
|
+
let positionals;
|
|
158
|
+
try {
|
|
159
|
+
const parsed = parseArgs({
|
|
160
|
+
args: remainingArgs,
|
|
161
|
+
options: commandOptions(spec),
|
|
162
|
+
allowPositionals: spec.allowPositionals ?? true
|
|
163
|
+
});
|
|
164
|
+
values = parsed.values;
|
|
165
|
+
positionals = [...parsed.positionals];
|
|
166
|
+
} catch (err) {
|
|
167
|
+
const message = err instanceof Error ? err.message : "invalid command usage";
|
|
168
|
+
throw usageError(`Error: ${message}
|
|
169
|
+
Usage: ${spec.usage}`, {
|
|
170
|
+
message,
|
|
171
|
+
details: {
|
|
172
|
+
command: commandId(spec),
|
|
173
|
+
usage: spec.usage
|
|
174
|
+
}
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
if (values.access !== void 0 && spec.path[0] !== "help") {
|
|
178
|
+
throw usageError("--access is supported only with help or --help", {
|
|
179
|
+
message: "--access is supported only with help or --help",
|
|
180
|
+
details: { command: commandId(spec), usage: commandUsage(spec) }
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
if (values.trace === true) {
|
|
184
|
+
process.env.CANONRY_TRACE = "1";
|
|
185
|
+
}
|
|
186
|
+
const dryRunRequested = values["dry-run"] === true;
|
|
187
|
+
const access = resolveCliAccessForInvocation(spec, { positionals, values, dryRun: dryRunRequested });
|
|
188
|
+
const storedOnly = values["stored-only"] === true || ["1", "true"].includes((process.env.CANONRY_STORED_ONLY ?? "").toLowerCase());
|
|
189
|
+
if (storedOnly) {
|
|
190
|
+
process.env.CANONRY_STORED_ONLY = "1";
|
|
191
|
+
if (access !== OperationAccesses.stored) {
|
|
192
|
+
throw new CliError({
|
|
193
|
+
code: "STORED_ONLY_REFUSED",
|
|
194
|
+
message: `Command "${spec.path.join(" ")}" is ${access}; --stored-only allows saved-data reads only`,
|
|
195
|
+
details: { command: commandId(spec), access },
|
|
196
|
+
exitCode: EXIT_USER_ERROR
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
if (dryRunRequested && !spec.supportsDryRun) {
|
|
201
|
+
throw usageError(
|
|
202
|
+
`Command "${spec.path.join(" ")}" does not support --dry-run. Either drop the flag, or run a different command that supports preview mode (e.g. \`canonry doctor\` for health checks, \`canonry status <project>\` for read-only state).`,
|
|
203
|
+
{
|
|
204
|
+
message: `Command "${spec.path.join(" ")}" does not support --dry-run`,
|
|
205
|
+
details: { command: commandId(spec), usage: spec.usage }
|
|
206
|
+
}
|
|
207
|
+
);
|
|
208
|
+
}
|
|
209
|
+
if (spec.windows || spec.windowAliases) {
|
|
210
|
+
const selection = readWindowOptions({ positionals, values, format: toFormat(values.format, fallbackFormat), dryRun: dryRunRequested }, { allowed: spec.windows, aliases: spec.windowAliases, command: commandId(spec), usage: commandUsage(spec) });
|
|
211
|
+
values.window = selection.window;
|
|
212
|
+
values.start = selection.startDate;
|
|
213
|
+
values.end = selection.endDate;
|
|
214
|
+
}
|
|
215
|
+
await spec.run({
|
|
216
|
+
positionals,
|
|
217
|
+
values,
|
|
218
|
+
format: toFormat(values.format, fallbackFormat),
|
|
219
|
+
dryRun: dryRunRequested
|
|
220
|
+
});
|
|
221
|
+
return true;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// src/cli-catalog.ts
|
|
225
|
+
var { version } = createRequire(import.meta.url)("../package.json");
|
|
226
|
+
var CLI_ROOT_SUMMARIES = {
|
|
227
|
+
"bootstrap": {
|
|
228
|
+
"section": "Setup",
|
|
229
|
+
"summary": "Create local config/database (no provider required)"
|
|
230
|
+
},
|
|
231
|
+
"serve": {
|
|
232
|
+
"section": "Setup",
|
|
233
|
+
"summary": "Start the local server (foreground)"
|
|
234
|
+
},
|
|
235
|
+
"demo": {
|
|
236
|
+
"section": "Setup",
|
|
237
|
+
"summary": "Serve a public view-only sample dashboard"
|
|
238
|
+
},
|
|
239
|
+
"init": {
|
|
240
|
+
"section": "Setup",
|
|
241
|
+
"summary": "Optional interactive provider/OAuth provisioning"
|
|
242
|
+
},
|
|
243
|
+
"skills": {
|
|
244
|
+
"section": "Setup",
|
|
245
|
+
"summary": "List or install bundled agent skills (claude/codex)"
|
|
246
|
+
},
|
|
247
|
+
"technical-aeo": {
|
|
248
|
+
"section": "Page Health",
|
|
249
|
+
"summary": "Crawl, score, and inspect site health evidence"
|
|
250
|
+
},
|
|
251
|
+
"doctor": {
|
|
252
|
+
"section": "Page Health",
|
|
253
|
+
"summary": "Diagnose runtime and integration health"
|
|
254
|
+
},
|
|
255
|
+
"project": {
|
|
256
|
+
"section": "Projects",
|
|
257
|
+
"summary": "Create, update, list, show, delete projects"
|
|
258
|
+
},
|
|
259
|
+
"query": {
|
|
260
|
+
"section": "Projects",
|
|
261
|
+
"summary": "Add, replace, remove, list, import, generate queries"
|
|
262
|
+
},
|
|
263
|
+
"competitor": {
|
|
264
|
+
"section": "Projects",
|
|
265
|
+
"summary": "Add, remove, list competitors and their aliases"
|
|
266
|
+
},
|
|
267
|
+
"run": {
|
|
268
|
+
"section": "Monitoring",
|
|
269
|
+
"summary": "Trigger visibility sweeps"
|
|
270
|
+
},
|
|
271
|
+
"runs": {
|
|
272
|
+
"section": "Monitoring",
|
|
273
|
+
"summary": "List runs (filter by --kind, --status, --since)"
|
|
274
|
+
},
|
|
275
|
+
"snapshot": {
|
|
276
|
+
"section": "Monitoring",
|
|
277
|
+
"summary": "One-shot AI perception report"
|
|
278
|
+
},
|
|
279
|
+
"status": {
|
|
280
|
+
"section": "Monitoring",
|
|
281
|
+
"summary": "Show project summary"
|
|
282
|
+
},
|
|
283
|
+
"evidence": {
|
|
284
|
+
"section": "Monitoring",
|
|
285
|
+
"summary": "Show per-query results"
|
|
286
|
+
},
|
|
287
|
+
"analytics": {
|
|
288
|
+
"section": "Monitoring",
|
|
289
|
+
"summary": "Show analytics (metrics, gaps, sources)"
|
|
290
|
+
},
|
|
291
|
+
"organic-evidence": {
|
|
292
|
+
"section": "Monitoring",
|
|
293
|
+
"summary": "Reconcile 60/90d search, GA4, and server AI evidence"
|
|
294
|
+
},
|
|
295
|
+
"insights": {
|
|
296
|
+
"section": "Monitoring",
|
|
297
|
+
"summary": "Show intelligence insights"
|
|
298
|
+
},
|
|
299
|
+
"health": {
|
|
300
|
+
"section": "Monitoring",
|
|
301
|
+
"summary": "Show citation health"
|
|
302
|
+
},
|
|
303
|
+
"apply": {
|
|
304
|
+
"section": "Config-as-Code",
|
|
305
|
+
"summary": "Apply declarative config (YAML)"
|
|
306
|
+
},
|
|
307
|
+
"export": {
|
|
308
|
+
"section": "Config-as-Code",
|
|
309
|
+
"summary": "Export project as YAML"
|
|
310
|
+
},
|
|
311
|
+
"google": {
|
|
312
|
+
"section": "Integrations",
|
|
313
|
+
"summary": "Google Search Console / Analytics"
|
|
314
|
+
},
|
|
315
|
+
"bing": {
|
|
316
|
+
"section": "Integrations",
|
|
317
|
+
"summary": "Bing Webmaster Tools"
|
|
318
|
+
},
|
|
319
|
+
"wordpress": {
|
|
320
|
+
"section": "Integrations",
|
|
321
|
+
"summary": "WordPress REST API"
|
|
322
|
+
},
|
|
323
|
+
"traffic": {
|
|
324
|
+
"section": "Integrations",
|
|
325
|
+
"summary": "Server-side traffic ingestion (Cloud Run)"
|
|
326
|
+
},
|
|
327
|
+
"schedule": {
|
|
328
|
+
"section": "Automation",
|
|
329
|
+
"summary": "Manage scheduled runs"
|
|
330
|
+
},
|
|
331
|
+
"notify": {
|
|
332
|
+
"section": "Automation",
|
|
333
|
+
"summary": "Manage webhook notifications"
|
|
334
|
+
},
|
|
335
|
+
"settings": {
|
|
336
|
+
"section": "Admin",
|
|
337
|
+
"summary": "Show/update provider and quota settings"
|
|
338
|
+
},
|
|
339
|
+
"dashboard": {
|
|
340
|
+
"section": "Admin",
|
|
341
|
+
"summary": "Reset the dashboard password"
|
|
342
|
+
},
|
|
343
|
+
"backfill": {
|
|
344
|
+
"section": "Admin",
|
|
345
|
+
"summary": "Backfill answer visibility or insights"
|
|
346
|
+
},
|
|
347
|
+
"telemetry": {
|
|
348
|
+
"section": "Admin",
|
|
349
|
+
"summary": "Manage anonymous telemetry"
|
|
350
|
+
},
|
|
351
|
+
"history": {
|
|
352
|
+
"section": "Admin",
|
|
353
|
+
"summary": "Show audit trail"
|
|
354
|
+
},
|
|
355
|
+
"support": {
|
|
356
|
+
"section": "Admin",
|
|
357
|
+
"summary": "Get help: Canonry Discord and issue tracker"
|
|
358
|
+
},
|
|
359
|
+
"start": {
|
|
360
|
+
"section": "Setup",
|
|
361
|
+
"summary": "Start/stop as a background daemon"
|
|
362
|
+
},
|
|
363
|
+
"stop": {
|
|
364
|
+
"section": "Setup",
|
|
365
|
+
"summary": "Start/stop as a background daemon"
|
|
366
|
+
},
|
|
367
|
+
"help": {
|
|
368
|
+
"section": "Setup",
|
|
369
|
+
"summary": "Find commands and inspect the CLI catalog"
|
|
370
|
+
},
|
|
371
|
+
"mcp": {
|
|
372
|
+
"section": "Setup",
|
|
373
|
+
"summary": "Install and manage MCP client connections"
|
|
374
|
+
},
|
|
375
|
+
"site-health": {
|
|
376
|
+
"section": "Page Health",
|
|
377
|
+
"summary": "Crawl, score, and inspect site health evidence"
|
|
378
|
+
},
|
|
379
|
+
"keyword": {
|
|
380
|
+
"section": "Projects",
|
|
381
|
+
"summary": "Inspect and maintain tracked query evidence"
|
|
382
|
+
},
|
|
383
|
+
"measurement-plan": {
|
|
384
|
+
"section": "Projects",
|
|
385
|
+
"summary": "Configure properties, targets, markets and measurement plans"
|
|
386
|
+
},
|
|
387
|
+
"overview": {
|
|
388
|
+
"section": "Monitoring",
|
|
389
|
+
"summary": "Show the stored project overview"
|
|
390
|
+
},
|
|
391
|
+
"results": {
|
|
392
|
+
"section": "Monitoring",
|
|
393
|
+
"summary": "Read stored run and query results"
|
|
394
|
+
},
|
|
395
|
+
"sources": {
|
|
396
|
+
"section": "Monitoring",
|
|
397
|
+
"summary": "Read stored citation sources"
|
|
398
|
+
},
|
|
399
|
+
"citations": {
|
|
400
|
+
"section": "Monitoring",
|
|
401
|
+
"summary": "Read stored citation evidence"
|
|
402
|
+
},
|
|
403
|
+
"visibility-stats": {
|
|
404
|
+
"section": "Monitoring",
|
|
405
|
+
"summary": "Show stored mention and citation statistics"
|
|
406
|
+
},
|
|
407
|
+
"visibility-compare": {
|
|
408
|
+
"section": "Monitoring",
|
|
409
|
+
"summary": "Compare stored mention and citation results"
|
|
410
|
+
},
|
|
411
|
+
"sentiment": {
|
|
412
|
+
"section": "Monitoring",
|
|
413
|
+
"summary": "Inspect and manage experimental sentiment measurement"
|
|
414
|
+
},
|
|
415
|
+
"ga": {
|
|
416
|
+
"section": "Integrations",
|
|
417
|
+
"summary": "Read and manage Google Analytics evidence"
|
|
418
|
+
},
|
|
419
|
+
"gbp": {
|
|
420
|
+
"section": "Integrations",
|
|
421
|
+
"summary": "Manage business profiles and stored evidence"
|
|
422
|
+
},
|
|
423
|
+
"google-ads": {
|
|
424
|
+
"section": "Integrations",
|
|
425
|
+
"summary": "Manage advertising connections and stored performance"
|
|
426
|
+
},
|
|
427
|
+
"gtm": {
|
|
428
|
+
"section": "Integrations",
|
|
429
|
+
"summary": "Manage tag manager accounts, containers and workspaces"
|
|
430
|
+
},
|
|
431
|
+
"conversion-tracking": {
|
|
432
|
+
"section": "Integrations",
|
|
433
|
+
"summary": "Configure and inspect conversion tracking"
|
|
434
|
+
},
|
|
435
|
+
"backlinks": {
|
|
436
|
+
"section": "Integrations",
|
|
437
|
+
"summary": "Inspect and sync stored backlink evidence"
|
|
438
|
+
},
|
|
439
|
+
"cdp": {
|
|
440
|
+
"section": "Integrations",
|
|
441
|
+
"summary": "Manage browser-backed provider connections"
|
|
442
|
+
},
|
|
443
|
+
"ads": {
|
|
444
|
+
"section": "Integrations",
|
|
445
|
+
"summary": "Manage advertising accounts, campaigns and performance"
|
|
446
|
+
},
|
|
447
|
+
"agent": {
|
|
448
|
+
"section": "Automation",
|
|
449
|
+
"summary": "Manage analyst agents and conversations"
|
|
450
|
+
},
|
|
451
|
+
"content": {
|
|
452
|
+
"section": "Automation",
|
|
453
|
+
"summary": "Create and manage content work"
|
|
454
|
+
},
|
|
455
|
+
"discover": {
|
|
456
|
+
"section": "Automation",
|
|
457
|
+
"summary": "Discover, inspect and promote query candidates"
|
|
458
|
+
},
|
|
459
|
+
"research": {
|
|
460
|
+
"section": "Automation",
|
|
461
|
+
"summary": "Research queries and inspect stored results"
|
|
462
|
+
},
|
|
463
|
+
"search": {
|
|
464
|
+
"section": "Automation",
|
|
465
|
+
"summary": "Search stored project evidence"
|
|
466
|
+
},
|
|
467
|
+
"get": {
|
|
468
|
+
"section": "Automation",
|
|
469
|
+
"summary": "Read a field from stored project evidence"
|
|
470
|
+
},
|
|
471
|
+
"key": {
|
|
472
|
+
"section": "Admin",
|
|
473
|
+
"summary": "Manage API keys and project scopes"
|
|
474
|
+
},
|
|
475
|
+
"user": {
|
|
476
|
+
"section": "Admin",
|
|
477
|
+
"summary": "Manage dashboard users"
|
|
478
|
+
},
|
|
479
|
+
"logs": {
|
|
480
|
+
"section": "Admin",
|
|
481
|
+
"summary": "Read stored runtime logs"
|
|
482
|
+
},
|
|
483
|
+
"feedback": {
|
|
484
|
+
"section": "Admin",
|
|
485
|
+
"summary": "Send product feedback"
|
|
486
|
+
}
|
|
487
|
+
};
|
|
488
|
+
function catalogOptions(options) {
|
|
489
|
+
return Object.entries(options).map(([name, option]) => ({ name, type: option.type, multiple: option.multiple ?? false }));
|
|
490
|
+
}
|
|
491
|
+
function buildCliCatalog(specs) {
|
|
492
|
+
const roots = [...new Set(specs.map((spec) => spec.path[0]))];
|
|
493
|
+
const groups = roots.map((root) => {
|
|
494
|
+
const summary = CLI_ROOT_SUMMARIES[root];
|
|
495
|
+
if (!summary) throw new Error(`Missing CLI root summary: ${root}`);
|
|
496
|
+
return {
|
|
497
|
+
root,
|
|
498
|
+
...summary,
|
|
499
|
+
commands: specs.filter((spec) => spec.path[0] === root).map((spec) => ({
|
|
500
|
+
id: spec.path.join("."),
|
|
501
|
+
path: [...spec.path],
|
|
502
|
+
usage: commandUsage(spec),
|
|
503
|
+
supportsDryRun: spec.supportsDryRun ?? false,
|
|
504
|
+
...spec.windows ? { windows: [...spec.windows] } : {},
|
|
505
|
+
options: catalogOptions(commandOptions(spec)),
|
|
506
|
+
access: resolveCliAccess(spec)
|
|
507
|
+
}))
|
|
508
|
+
};
|
|
509
|
+
});
|
|
510
|
+
return { version, globalOptions: catalogOptions({ ...CLI_GLOBAL_OPTIONS, help: { type: "boolean" }, version: { type: "boolean" } }), groups };
|
|
511
|
+
}
|
|
512
|
+
function renderTopLevelHelp(catalog) {
|
|
513
|
+
const sections = ["Setup", "Page Health", "Projects", "Monitoring", "Config-as-Code", "Integrations", "Automation", "Admin"];
|
|
514
|
+
const lines = ["cnry - Page Health and AI visibility CLI ('canonry' also works)", "", "Usage: cnry <command> [options]", ""];
|
|
515
|
+
for (const section of [.../* @__PURE__ */ new Set([...sections, ...catalog.groups.map((group) => group.section)])]) {
|
|
516
|
+
const groups = catalog.groups.filter((group) => group.section === section);
|
|
517
|
+
if (!groups.length) continue;
|
|
518
|
+
lines.push(`${section}:`, ...groups.map((group) => ` ${group.root.padEnd(22)}${group.summary}`), "");
|
|
519
|
+
}
|
|
520
|
+
lines.push(
|
|
521
|
+
"Global options:",
|
|
522
|
+
" --format json Machine-readable output: one JSON document (all commands)",
|
|
523
|
+
" --format jsonl Machine-readable output: one record per line, no jq needed",
|
|
524
|
+
" --dry-run Preview commands that support it",
|
|
525
|
+
" --stored-only Refuse provider and write operations",
|
|
526
|
+
" --access <mode> Filter help/catalog by stored, live, or write access",
|
|
527
|
+
" --trace Trace API requests to stderr",
|
|
528
|
+
" --help, -h Show help (use with any command group)",
|
|
529
|
+
" --version, -v Show version",
|
|
530
|
+
"",
|
|
531
|
+
"Run 'cnry <command> --help' for details on a specific command."
|
|
532
|
+
);
|
|
533
|
+
return lines.join("\n");
|
|
534
|
+
}
|
|
535
|
+
function printCliCatalog(specs, format, group, access) {
|
|
536
|
+
const catalog = buildCliCatalog(specs);
|
|
537
|
+
if (access !== void 0 && access !== OperationAccesses.stored && access !== OperationAccesses.live && access !== OperationAccesses.write) {
|
|
538
|
+
throw usageError(`Unknown access filter: ${access}`, { details: { access } });
|
|
539
|
+
}
|
|
540
|
+
const knownGroup = group ? catalog.groups.find((entry) => entry.root === group) : void 0;
|
|
541
|
+
if (group && !knownGroup) throw usageError(`Unknown command group: ${group}`, { details: { group, usage: "canonry help [<group>] [--format json|jsonl]" } });
|
|
542
|
+
if (access) {
|
|
543
|
+
for (const entry of catalog.groups) entry.commands = entry.commands.filter((command) => command.access === access);
|
|
544
|
+
catalog.groups = catalog.groups.filter((entry) => entry.commands.length > 0 || entry.root === group);
|
|
545
|
+
}
|
|
546
|
+
if (group) {
|
|
547
|
+
catalog.groups = catalog.groups.filter((entry) => entry.root === group);
|
|
548
|
+
}
|
|
549
|
+
if (format === "json") console.log(JSON.stringify(catalog, null, 2));
|
|
550
|
+
else if (format === "jsonl") emitJsonl(catalog.groups.flatMap((entry) => entry.commands.map((command) => ({ root: entry.root, ...command }))));
|
|
551
|
+
else if (group) printGroupHelp(group, specs, access);
|
|
552
|
+
else console.log(renderTopLevelHelp(catalog));
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
export {
|
|
556
|
+
accessFilterFromArgs,
|
|
557
|
+
dispatchRegisteredCommand,
|
|
558
|
+
CLI_ROOT_SUMMARIES,
|
|
559
|
+
buildCliCatalog,
|
|
560
|
+
renderTopLevelHelp,
|
|
561
|
+
printCliCatalog
|
|
562
|
+
};
|