@nuxtseo/cli 0.5.8 → 0.5.10
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/dist/cli.js
CHANGED
|
@@ -376,7 +376,7 @@ async function interactiveBare(runtime, globals) {
|
|
|
376
376
|
message: 'Account and setup',
|
|
377
377
|
options: [
|
|
378
378
|
{ value: 'site', label: 'Switch Site', hint: config.value.siteId ?? 'none selected' },
|
|
379
|
-
{ value: 'usage', label: '
|
|
379
|
+
{ value: 'usage', label: 'Team usage' },
|
|
380
380
|
{ value: 'whoami', label: 'Credential and scopes' },
|
|
381
381
|
{ value: 'config', label: 'Configuration' },
|
|
382
382
|
{ value: 'logout', label: 'Log out' },
|
package/dist/commands.js
CHANGED
|
@@ -1374,11 +1374,10 @@ async function actionDismiss(runtime, globals, args) {
|
|
|
1374
1374
|
return fail(EXIT_CODE.invalidInput, 'Pass the action id to dismiss.', undefined, 'invalid_cli_input');
|
|
1375
1375
|
if (!args.artifactVersion)
|
|
1376
1376
|
return fail(EXIT_CODE.invalidInput, 'Pass --artifact-version, the artifactVersion returned with this action.', undefined, 'invalid_cli_input');
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
: `Dismiss action ${args.actionId}? The page stays removed.`);
|
|
1377
|
+
const note = args.note?.trim();
|
|
1378
|
+
if (!note || note.length > 2000)
|
|
1379
|
+
return fail(EXIT_CODE.invalidInput, 'Pass --note with 1 to 2000 characters that say why this action is intentional or incorrect.', undefined, 'invalid_cli_input');
|
|
1380
|
+
const confirmed = await confirmMutation(runtime, globals, `Dismiss action ${args.actionId}? Your note records this decision. No fix is verified.`);
|
|
1382
1381
|
if (confirmed._tag === 'Err')
|
|
1383
1382
|
return confirmed;
|
|
1384
1383
|
if (!confirmed.value) {
|
|
@@ -1390,7 +1389,7 @@ async function actionDismiss(runtime, globals, args) {
|
|
|
1390
1389
|
return resolved;
|
|
1391
1390
|
const response = await withSpinner(runtime, 'Dismissing action', () => resolved.value.api.client.actions.dismiss({
|
|
1392
1391
|
params: { siteId: resolved.value.siteId, actionId: args.actionId },
|
|
1393
|
-
body: { artifactVersion: args.artifactVersion,
|
|
1392
|
+
body: { artifactVersion: args.artifactVersion, note },
|
|
1394
1393
|
}, { signal: runtime.requestSignal }));
|
|
1395
1394
|
return present(runtime, globals, response, value => renderActionDismissal(value.data));
|
|
1396
1395
|
}
|
|
@@ -1714,11 +1713,11 @@ export function createRootCommand(runtime, globals, execution) {
|
|
|
1714
1713
|
run: ({ args }) => capture(execution, () => actionsResolve(runtime, globals, args.actionId), args._, 1)(),
|
|
1715
1714
|
}),
|
|
1716
1715
|
dismiss: defineCommand({
|
|
1717
|
-
meta: { name: 'dismiss', description: 'Dismiss reviewed work with a note
|
|
1716
|
+
meta: { name: 'dismiss', description: 'Dismiss reviewed work with a note that says why' },
|
|
1718
1717
|
args: {
|
|
1719
1718
|
'actionId': positional('action-id', 'Action ID'),
|
|
1720
1719
|
'artifact-version': { type: 'string', description: 'The artifactVersion returned with this action' },
|
|
1721
|
-
'note': { type: 'string', description: 'Why this action is intentional or incorrect, 1 to 2000 characters' },
|
|
1720
|
+
'note': { type: 'string', description: 'Required. Why this action is intentional or incorrect, 1 to 2000 characters' },
|
|
1722
1721
|
},
|
|
1723
1722
|
run: ({ args }) => capture(execution, () => actionDismiss(runtime, globals, {
|
|
1724
1723
|
actionId: args.actionId,
|
|
@@ -2369,7 +2368,7 @@ export function createRootCommand(runtime, globals, execution) {
|
|
|
2369
2368
|
},
|
|
2370
2369
|
}),
|
|
2371
2370
|
usage: defineCommand({
|
|
2372
|
-
meta: { name: 'usage', description: 'Read
|
|
2371
|
+
meta: { name: 'usage', description: 'Read Team usage' },
|
|
2373
2372
|
args: {
|
|
2374
2373
|
group: {
|
|
2375
2374
|
type: 'enum',
|
package/dist/render.js
CHANGED
|
@@ -106,20 +106,56 @@ export function renderPageScan(data) {
|
|
|
106
106
|
...data.scans.map(scan => `${scan.strategy}: ${scan.scanId}`),
|
|
107
107
|
].join('\n');
|
|
108
108
|
}
|
|
109
|
+
/** A lab duration in milliseconds. Null is unmeasured, never zero. */
|
|
110
|
+
function labMs(value) {
|
|
111
|
+
return value === null ? 'not measured' : `${value}ms`;
|
|
112
|
+
}
|
|
113
|
+
function labScanLine(label, scan) {
|
|
114
|
+
if (!scan?.completedAt)
|
|
115
|
+
return `${label} no complete Scan`;
|
|
116
|
+
return `${label} perf ${scan.perf ?? 'none'} LCP ${labMs(scan.lcp)} TBT ${labMs(scan.tbt)} CLS ${scan.cls ?? 'not measured'} scanned ${scan.completedAt}`;
|
|
117
|
+
}
|
|
109
118
|
export function renderPerformance(data) {
|
|
110
|
-
|
|
119
|
+
const blocks = [];
|
|
120
|
+
if (data.health) {
|
|
121
|
+
blocks.push([
|
|
122
|
+
'Mobile medians across monitored Pages',
|
|
123
|
+
fields([
|
|
124
|
+
['Scanned pages', data.health.scannedPageCount],
|
|
125
|
+
['Last scan', data.health.lastScanAt],
|
|
126
|
+
['Performance median', data.health.perf.median],
|
|
127
|
+
['Performance worst', data.health.perf.worst],
|
|
128
|
+
['Accessibility median', data.health.a11y.median],
|
|
129
|
+
['SEO median', data.health.seo.median],
|
|
130
|
+
['LCP median', data.health.lcp.median],
|
|
131
|
+
['TBT median', data.health.tbt.median],
|
|
132
|
+
['CLS median', data.health.cls.median],
|
|
133
|
+
]),
|
|
134
|
+
].join('\n'));
|
|
135
|
+
}
|
|
136
|
+
if (data.homepage || data.homepageDesktop) {
|
|
137
|
+
blocks.push([
|
|
138
|
+
'Homepage',
|
|
139
|
+
labScanLine(' mobile ', data.homepage),
|
|
140
|
+
labScanLine(' desktop', data.homepageDesktop),
|
|
141
|
+
].join('\n'));
|
|
142
|
+
}
|
|
143
|
+
if (data.routes.length || data.routesDesktop.length) {
|
|
144
|
+
const desktopByPage = new Map(data.routesDesktop.map(route => [route.pageId, route]));
|
|
145
|
+
const mobileByPage = new Map(data.routes.map(route => [route.pageId, route]));
|
|
146
|
+
const pages = [...data.routes, ...data.routesDesktop.filter(route => !mobileByPage.has(route.pageId))];
|
|
147
|
+
blocks.push([
|
|
148
|
+
`Monitored Pages (${pages.length})`,
|
|
149
|
+
...pages.flatMap(page => [
|
|
150
|
+
` ${page.path}`,
|
|
151
|
+
labScanLine(' mobile ', mobileByPage.get(page.pageId)),
|
|
152
|
+
labScanLine(' desktop', desktopByPage.get(page.pageId)),
|
|
153
|
+
]),
|
|
154
|
+
].join('\n'));
|
|
155
|
+
}
|
|
156
|
+
if (!blocks.length)
|
|
111
157
|
return `No stored performance overview. Monitored routes: ${data.routes.length}.`;
|
|
112
|
-
return
|
|
113
|
-
['Scanned pages', data.health.scannedPageCount],
|
|
114
|
-
['Last scan', data.health.lastScanAt],
|
|
115
|
-
['Performance median', data.health.perf.median],
|
|
116
|
-
['Performance worst', data.health.perf.worst],
|
|
117
|
-
['Accessibility median', data.health.a11y.median],
|
|
118
|
-
['SEO median', data.health.seo.median],
|
|
119
|
-
['LCP median', data.health.lcp.median],
|
|
120
|
-
['TBT median', data.health.tbt.median],
|
|
121
|
-
['CLS median', data.health.cls.median],
|
|
122
|
-
]);
|
|
158
|
+
return blocks.join('\n\n');
|
|
123
159
|
}
|
|
124
160
|
export function renderSearchStatus(data) {
|
|
125
161
|
if (data._tag === 'Connected')
|
|
@@ -669,8 +705,9 @@ export function renderFieldVitalFindings(data) {
|
|
|
669
705
|
if (data.findings.length === 0)
|
|
670
706
|
return data.message ?? 'No failing field vitals attributed to an element.';
|
|
671
707
|
const shown = `${data.offset + 1}-${data.offset + data.findings.length} of ${data.total}`;
|
|
708
|
+
const window = data.window ? `, ${data.window.from} to ${data.window.to}` : '';
|
|
672
709
|
return [
|
|
673
|
-
`Field vital findings (${shown}, source ${data.source ?? 'unknown'})`,
|
|
710
|
+
`Field vital findings (${shown}, source ${data.source ?? 'unknown'}${window})`,
|
|
674
711
|
...data.findings.map(finding => [
|
|
675
712
|
`${finding.metric.toUpperCase()} ${finding.severity} ${vitalValue(finding.metric, finding.p75)} ${finding.route}`,
|
|
676
713
|
` selector ${finding.selector}`,
|
|
@@ -898,6 +935,12 @@ export function renderPageIssues(data) {
|
|
|
898
935
|
].filter(Boolean).join('\n')),
|
|
899
936
|
].join('\n') + truncated;
|
|
900
937
|
}
|
|
938
|
+
/** A failed Scan names its kind and message. Other states carry no failure row. */
|
|
939
|
+
function scanFailureRows(scan) {
|
|
940
|
+
if (scan.status !== 'failed')
|
|
941
|
+
return [];
|
|
942
|
+
return [['failure', `${scan.failureKind ?? 'unknown kind'}: ${scan.error ?? 'no message recorded'}`]];
|
|
943
|
+
}
|
|
901
944
|
export function renderScans(data) {
|
|
902
945
|
if (data.scans.length === 0)
|
|
903
946
|
return 'No Lighthouse Scans retained for this Site.';
|
|
@@ -907,6 +950,9 @@ export function renderScans(data) {
|
|
|
907
950
|
`${scan.completedAt ?? 'pending'} ${scan.strategy} ${scan.status} ${scan.url}`,
|
|
908
951
|
` ${fields([
|
|
909
952
|
['id', scan.id],
|
|
953
|
+
['trigger', scan.trigger],
|
|
954
|
+
['lighthouse', scan.lighthouseVersion],
|
|
955
|
+
...scanFailureRows(scan),
|
|
910
956
|
['performance', scan.scores.perf],
|
|
911
957
|
['accessibility', scan.scores.a11y],
|
|
912
958
|
['best practices', scan.scores.bestPractices],
|
|
@@ -914,17 +960,28 @@ export function renderScans(data) {
|
|
|
914
960
|
['lcp', scan.lcp],
|
|
915
961
|
['tbt', scan.tbt],
|
|
916
962
|
['cls', scan.cls],
|
|
917
|
-
['error', scan.error],
|
|
918
963
|
]).split('\n').join('\n ')}`,
|
|
919
964
|
].join('\n')),
|
|
920
965
|
].join('\n');
|
|
921
966
|
}
|
|
967
|
+
function renderScanComparison(comparison) {
|
|
968
|
+
if (!comparison || comparison._tag === 'none')
|
|
969
|
+
return 'Previous Scan none compared';
|
|
970
|
+
if (comparison._tag === 'incompatible')
|
|
971
|
+
return `Previous Scan ${comparison.previousScanId ?? 'unknown'} not comparable: ${comparison.reasons.join(', ')}`;
|
|
972
|
+
return [
|
|
973
|
+
`Since previous Scan ${comparison.previousScanId ?? 'unknown'} ${comparison.severity}`,
|
|
974
|
+
...comparison.changes.map(change => ` ${change.metric} ${change.previous} to ${change.current} ${change.delta > 0 ? '+' : ''}${change.delta} ${change.severity}`),
|
|
975
|
+
].join('\n');
|
|
976
|
+
}
|
|
922
977
|
export function renderMonitoredPages(data) {
|
|
923
978
|
if (data.pages.length === 0)
|
|
924
979
|
return 'No monitored Pages. Start one with `nuxtseo page scan <url>`.';
|
|
925
980
|
return [
|
|
926
981
|
`Monitored Pages (${data.pages.length})`,
|
|
927
|
-
...data.pages.map(page =>
|
|
982
|
+
...data.pages.map(page => page.pauseReason
|
|
983
|
+
? ` paused ${page.path} ${page.pauseReason} last scan ${page.lastScanAt ?? 'never'}`
|
|
984
|
+
: ` ${page.enabled ? 'on ' : 'off '} ${page.path} last scan ${page.lastScanAt ?? 'never'}`),
|
|
928
985
|
].join('\n');
|
|
929
986
|
}
|
|
930
987
|
export function renderScanDetail(data) {
|
|
@@ -934,6 +991,9 @@ export function renderScanDetail(data) {
|
|
|
934
991
|
['URL', data.url],
|
|
935
992
|
['Strategy', data.strategy],
|
|
936
993
|
['Status', data.status],
|
|
994
|
+
...scanFailureRows(data).map(([, failure]) => ['Failure', failure]),
|
|
995
|
+
['Trigger', data.trigger],
|
|
996
|
+
['Lighthouse', data.lighthouseVersion],
|
|
937
997
|
['Completed', data.completedAt],
|
|
938
998
|
['Performance', data.scores.perf],
|
|
939
999
|
['Accessibility', data.scores.a11y],
|
|
@@ -947,6 +1007,8 @@ export function renderScanDetail(data) {
|
|
|
947
1007
|
['TTFB', data.ttfb],
|
|
948
1008
|
]),
|
|
949
1009
|
];
|
|
1010
|
+
if (data.status === 'complete')
|
|
1011
|
+
blocks.push(renderScanComparison(data.comparison));
|
|
950
1012
|
if (data.lcpElement)
|
|
951
1013
|
blocks.push(`LCP element ${data.lcpElement.selector ?? data.lcpElement.nodeLabel ?? 'unknown'}`);
|
|
952
1014
|
if (data.observedTrace?._tag === 'available') {
|
|
@@ -968,13 +1030,15 @@ export function renderScanDetail(data) {
|
|
|
968
1030
|
if (waste.length) {
|
|
969
1031
|
blocks.push([
|
|
970
1032
|
`Wasteful assets (${waste.length})`,
|
|
971
|
-
...waste.map(issue => ` ${issue.wastedMs
|
|
1033
|
+
...waste.map(issue => ` ${issue.wastedMs === null ? 'time not measured' : `${issue.wastedMs}ms`} ${issue.wastedBytes === null ? 'bytes not measured' : `${issue.wastedBytes}b`} ${issue.subtype ?? issue.type} ${issue.url ?? ''}`.trimEnd()),
|
|
972
1034
|
].join('\n'));
|
|
973
1035
|
}
|
|
974
1036
|
if (data.thirdParties.length) {
|
|
1037
|
+
// A null blocking time is unmeasured. Printing it as 0ms read as "this
|
|
1038
|
+
// script never blocks".
|
|
975
1039
|
blocks.push([
|
|
976
1040
|
`Third parties (${data.thirdParties.length})`,
|
|
977
|
-
...data.thirdParties.map(script => ` ${script.blockingTime
|
|
1041
|
+
...data.thirdParties.map(script => ` ${script.blockingTime === null ? 'blocking not measured' : `${script.blockingTime}ms blocking`} ${script.entity ?? 'unknown'}`),
|
|
978
1042
|
].join('\n'));
|
|
979
1043
|
}
|
|
980
1044
|
for (const [label, checks] of [['Accessibility failures', data.accessibility], ['Best-practices failures', data.bestPractices]]) {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nuxtseo/cli",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.5.
|
|
4
|
+
"version": "0.5.10",
|
|
5
5
|
"description": "Command line interface for the Nuxt SEO public API.",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"homepage": "https://nuxtseo.com/pro",
|
|
@@ -33,8 +33,8 @@
|
|
|
33
33
|
},
|
|
34
34
|
"dependencies": {
|
|
35
35
|
"@clack/prompts": "^1.8.0",
|
|
36
|
-
"@nuxtseo/protocol": "^0.5.
|
|
37
|
-
"@nuxtseo/sdk": "^0.5.
|
|
36
|
+
"@nuxtseo/protocol": "^0.5.10",
|
|
37
|
+
"@nuxtseo/sdk": "^0.5.10",
|
|
38
38
|
"citty": "^0.2.2",
|
|
39
39
|
"pathe": "^2.0.3"
|
|
40
40
|
},
|
|
@@ -51,8 +51,16 @@ Read the matching file before you act on its subject:
|
|
|
51
51
|
instead.
|
|
52
52
|
|
|
53
53
|
3. **Token.** Run `nuxtseo whoami --json`. It returns the Team, role, scopes,
|
|
54
|
-
and token expiry. Exit `3` means no working token.
|
|
55
|
-
|
|
54
|
+
and token expiry. Exit `3` means no working token.
|
|
55
|
+
If authentication is missing, invalid, or expired, run `nuxtseo login --no-browser`.
|
|
56
|
+
A missing referenced keychain credential can instead exit `7` with `state_error`; use the same login flow.
|
|
57
|
+
If browser automation is authorized, open the printed URL in the intended signed-in account.
|
|
58
|
+
Verify the account, Team, machine, and matching pairing code before approving.
|
|
59
|
+
Select the least access needed. Editor supports Scans and Action claims.
|
|
60
|
+
Keep the login process running until it saves the credential, then repeat `whoami --json`.
|
|
61
|
+
Honor browser connection approval. Never copy cookies or bypass sign-in.
|
|
62
|
+
If browser access or approval is unavailable, ask the user to approve the printed URL.
|
|
63
|
+
If login reports a transport failure, investigate it before starting another pairing.
|
|
56
64
|
The other route is a token from
|
|
57
65
|
<https://nuxtseo.com/pro/dashboard/settings/api-tokens>, exported as
|
|
58
66
|
`NUXTSEO_TOKEN`. Never put a token in an argument, a file, or a commit.
|
|
@@ -211,21 +219,21 @@ The server verifies the claim; the CLI never marks anything fixed. Resolve
|
|
|
211
219
|
sends the current `artifactVersion` for you. If the evidence moved, it exits
|
|
212
220
|
`5` with `stale_evidence`: re-run step 3 and decide again.
|
|
213
221
|
|
|
214
|
-
If the
|
|
215
|
-
from `actions show`, then:
|
|
222
|
+
If the owner confirms an action is intentional or incorrect, dismiss it. Read
|
|
223
|
+
`artifactVersion` from `actions show`, then record the reason:
|
|
216
224
|
|
|
217
225
|
```sh
|
|
218
|
-
nuxtseo actions dismiss <action-id> --site <site-id> --artifact-version <v> --yes --json
|
|
226
|
+
nuxtseo actions dismiss <action-id> --site <site-id> --artifact-version <v> --note "<reason>" --yes --json
|
|
219
227
|
```
|
|
220
228
|
|
|
221
|
-
|
|
222
|
-
|
|
229
|
+
The note is required. Review the exact scope first. A note dismisses the whole
|
|
230
|
+
action, so never use it to hide a mix of intentional and broken URLs. Dismissal
|
|
231
|
+
keeps the observations and does not verify a fix. The dashboard can restore the
|
|
232
|
+
action later.
|
|
223
233
|
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
the
|
|
227
|
-
Dismissal keeps the observations and does not verify a fix. The dashboard can
|
|
228
|
-
restore the action later.
|
|
234
|
+
A 404 or 410 is a gone URL. An action names one only when a link, a sitemap
|
|
235
|
+
entry, or search demand still points at it, or when the crawl cannot confirm
|
|
236
|
+
the removal.
|
|
229
237
|
|
|
230
238
|
**7. Mark the day.**
|
|
231
239
|
|
|
@@ -264,6 +272,10 @@ and do not route around it with `pull` or another read.
|
|
|
264
272
|
`coverage.bestPracticesChecksTotal` from `scans show`. `possiblyTruncated:
|
|
265
273
|
true` marks the list as a floor.
|
|
266
274
|
|
|
275
|
+
**A failed Scan has no scores.** `status: failed` with `failureKind` says why.
|
|
276
|
+
Its null scores and vitals are not zeros. A null `blockingTime` or `wastedMs`
|
|
277
|
+
is unmeasured, never zero.
|
|
278
|
+
|
|
267
279
|
For `search indexing urls`, read `totalSemantics` before quoting `total`.
|
|
268
280
|
`lower-bound` means the provider used lookahead instead of counting every URL.
|
|
269
281
|
The total can rise between pages without a filter change. Follow `hasMore` or
|
|
@@ -108,7 +108,7 @@ lines.
|
|
|
108
108
|
| --- | --- |
|
|
109
109
|
| `whoami` | Credential, Team, role, granted scopes, token expiry. The cheapest health check. Reads no Site |
|
|
110
110
|
| `feedback submit` | Submit sanitized agent feedback. Requires `--command` (1..200 characters), `--comment` (1..2000), `--agent` (1..100), and `--yes`. Optional `--intent bug\|improvement` (default `bug`) and `--request-id` (1..128). Adds the CLI version. Needs no Site. Returns the feedback ID and status |
|
|
111
|
-
| `login` | Pairs the CLI with a Team through the browser.
|
|
111
|
+
| `login` | Pairs the CLI with a Team through the browser. An authorized agent can run `--no-browser` and approve through the intended signed-in account. Verify the account, Team, machine, and matching code first. If approval is unavailable, ask the user to approve the printed URL. `--with-token` reads a token from a hidden prompt or stdin. `--no-browser` skips opening the browser |
|
|
112
112
|
| `logout` | Discards the stored credential. Returns a `CliLogout` value |
|
|
113
113
|
| `sites list` | Every accessible Site. The source of Site IDs |
|
|
114
114
|
| `sites use <site-id>` | Persists a default Site in local config, for the human, not for you |
|
|
@@ -124,7 +124,7 @@ lines.
|
|
|
124
124
|
| `actions list` | Server-ranked issues and opportunities. `--limit 1..25` (default 10), `--offset`. Each row carries `evidence.freshness`; `verdict: "aged"` means the observation is over a day old, so live-check before fixing |
|
|
125
125
|
| `actions show <action-id>` | One issue or opportunity, plus its evidence. `--limit 1..100` (default 50), `--group-id`, `--cursor` |
|
|
126
126
|
| `actions resolve <action-id>` | Mutation. Claims the issue or opportunity and starts server verification. Reads the action first and sends `artifactVersion` for you |
|
|
127
|
-
| `actions dismiss <action-id>` | Mutation. Needs `--artifact-version`, read from `actions show
|
|
127
|
+
| `actions dismiss <action-id>` | Mutation. Needs `--artifact-version`, read from `actions show`, and `--note`, which records why the owner considers the action intentional or incorrect. Dismissal preserves observations and never verifies a fix |
|
|
128
128
|
|
|
129
129
|
## Pages, Scans, and lab performance
|
|
130
130
|
|
|
@@ -133,11 +133,24 @@ lines.
|
|
|
133
133
|
| `page inspect <url>` | Stored observations, Lighthouse rows and keywords for one Page. `--limit 1..200`, `--offset`, `--include-resolved`. Carries `observations.coverage`: `never-scanned` means no source recorded this Page, `scanned-clear` means recorded and all clear |
|
|
134
134
|
| `page issues` | Raw Page Issue rows. Pass `--action-id`, or pass both `--source` and `--issue`. Passing both selectors exits `2`. `--url`, `--path-prefix`, `--include-resolved`, `--limit 1..200`, `--offset`. When the selector matches nothing, `availableKeys` lists the pairs this Site does have. Paused Sites exit `4` or `5`. Removed Sites return `not_found` with exit `8` |
|
|
135
135
|
| `page scan <url>` | Mutation. Adds or re-enables the URL as a monitored Page, then starts mobile and desktop Scans |
|
|
136
|
-
| `performance` | Stored Lighthouse lab overview for the Site: medians for perf, a11y, SEO, LCP, TBT, CLS. Lab Scans, never real-user field data |
|
|
137
|
-
| `scans list` | Retained Lighthouse Scans, newest first, with scores, lab vitals, and
|
|
138
|
-
| `scans show <scan-id>` | One Scan, and the evidence behind its scores: wasteful assets, repeat-visit cache policy, third-party scripts, the LCP element, and every failing accessibility and best-practices check with its CSS selectors and DOM snippets. Read `coverage` before quoting a total |
|
|
136
|
+
| `performance` | Stored Lighthouse lab overview for the Site: mobile medians for perf, a11y, SEO, LCP, TBT, CLS, then the homepage and each monitored Page on mobile and desktop. Lab Scans, never real-user field data |
|
|
137
|
+
| `scans list` | Retained Lighthouse Scans, newest first, with scores, lab vitals, `trigger`, and `lighthouseVersion`. A failed Scan carries `failureKind` and `error`, and its scores are null. `--limit 1..100` (default 25) |
|
|
138
|
+
| `scans show <scan-id>` | One Scan, and the evidence behind its scores: wasteful assets, repeat-visit cache policy, third-party scripts, the LCP element, and every failing accessibility and best-practices check with its CSS selectors and DOM snippets. Also `trigger`, `lighthouseVersion`, and `comparison` with the previous Scan of the same Page. Read `coverage` before quoting a total |
|
|
139
139
|
| `scans pages` | The Pages this Site scans on a schedule. Add one with `page scan <url>` |
|
|
140
140
|
|
|
141
|
+
`scans list` and `scans show` return the same Scan shape as the `lighthouse_audits` MCP tool.
|
|
142
|
+
`failureKind` says why a Scan failed, for example `timeout`, `rejected`, or `quota`.
|
|
143
|
+
It is null on a Scan that did not fail, and on a Scan that failed before kinds were recorded.
|
|
144
|
+
|
|
145
|
+
`comparison` has one of three tags.
|
|
146
|
+
`compared` lists each metric both Scans measured, with `previous`, `current`, `delta`, and `severity`.
|
|
147
|
+
`incompatible` names why the two Scans differ, for example `lighthouse-version`. Do not compare their numbers.
|
|
148
|
+
`none` means no previous Scan was compared. The homepage slot always reads `none`.
|
|
149
|
+
A lab `regression` compares one Lighthouse result per Scan. Lab scores vary by a few points between Scans.
|
|
150
|
+
|
|
151
|
+
A null `blockingTime` or `wastedMs` is unmeasured. It is never zero.
|
|
152
|
+
If a Scan could not measure blocking time for a third party, text output prints "blocking not measured".
|
|
153
|
+
|
|
141
154
|
`scans show` can include `observedTrace` from the retained Lighthouse projection.
|
|
142
155
|
If its tag is `available`, its phases describe `observedLcpMs` from the browser trace.
|
|
143
156
|
`throttlingMethod` names how Lighthouse computed the reported metrics.
|
|
@@ -185,7 +198,7 @@ The monitored Page then follows its own recurring schedule.
|
|
|
185
198
|
| --- | --- |
|
|
186
199
|
| `vitals summary` | Field Core Web Vitals from CrUX: the latest p75, plus its change over the window. `--url` for one Page, `--form-factor phone\|desktop\|all`. `available: false` means the target is under the CrUX traffic floor |
|
|
187
200
|
| `vitals trend` | A 25-week field p75 series. Same inputs as `vitals summary` |
|
|
188
|
-
| `vitals findings` | Failing field vitals attributed to a DOM element. `--metric lcp\|inp\|cls`, `--limit 1..50` (default 20), `--offset`, `--no-only-failing`. Each finding carries the CSS selector and a ready-to-apply `fixPrompt`. Needs a connected Web Analytics provider |
|
|
201
|
+
| `vitals findings` | Failing field vitals attributed to a DOM element. `--metric lcp\|inp\|cls`, `--limit 1..50` (default 20), `--offset`, `--no-only-failing`. Each finding carries the CSS selector, `poorFraction`, `labHint`, and a ready-to-apply `fixPrompt`. An LCP finding adds `resource`, `fetchPriority`, `initiatorType`, and `phases`. An INP finding adds `interaction`. `window` gives the dates the provider aggregated. Needs a connected Web Analytics provider |
|
|
189
202
|
|
|
190
203
|
## Search Console and sitemaps
|
|
191
204
|
|
|
@@ -167,7 +167,7 @@ Branch on the exit code, not message text.
|
|
|
167
167
|
| ---: | --- | --- |
|
|
168
168
|
| `0` | Success, including a closed stdout pipe | Continue |
|
|
169
169
|
| `2` | Invalid input, or mutation without `--yes` | Fix arguments. Do not retry unchanged |
|
|
170
|
-
| `3` | Missing, invalid, or expired authentication |
|
|
170
|
+
| `3` | Missing, invalid, or expired authentication | Run `nuxtseo login --no-browser`. Follow Setup's authorized browser pairing flow, then repeat `whoami --json` |
|
|
171
171
|
| `4` | Forbidden, scope, or entitlement failure | Report the plan or token blocker |
|
|
172
172
|
| `5` | Conflict, stale evidence, or ambiguous Site | Re-read. Pass `--site` if ambiguous |
|
|
173
173
|
| `6` | Rate limit, `quota_exhausted`, provider outage, or timeout | Read retry metadata, then wait |
|