@nuxtseo/cli 0.1.3 → 0.1.4

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 CHANGED
@@ -123,12 +123,14 @@ to stdout and writes the readable recovery message to stderr.
123
123
 
124
124
  Local outcomes have no server protocol body. With `--json`, commands such as
125
125
  bare `nuxtseo`, `config`, `logout`, and `sites use` emit a tagged CLI-owned JSON
126
- value with `schemaVersion: 1`. Local failures emit `CliError` with a stable code
127
- and exit code. These tags are separate from protocol envelopes.
128
-
129
- `--help --json` returns the selected command, global options, arguments, and
130
- subcommands as `CliHelp`. Option names are strict; an unknown option exits `2`
131
- before authentication or network work.
126
+ value with `schemaVersion: 1`. `CliHelp` uses `schemaVersion: 2`. Local failures
127
+ emit `CliError` with a stable code and exit code. These tags are separate from
128
+ protocol envelopes.
129
+
130
+ `--help --json` returns the selected command, inherited `globalOptions`,
131
+ positional `arguments`, command-specific `localOptions`, and `subcommands` as
132
+ `CliHelp`. Option names are strict. An unknown option exits `2` before
133
+ authentication or network work.
132
134
 
133
135
  ## Automation controls
134
136
 
@@ -16,12 +16,13 @@ export interface CliHelpArgument {
16
16
  }
17
17
  export interface CliHelpResponse {
18
18
  _tag: 'CliHelp';
19
- schemaVersion: 1;
19
+ schemaVersion: 2;
20
20
  cliVersion: string;
21
21
  command: string;
22
22
  description?: string;
23
23
  globalOptions: CliHelpArgument[];
24
24
  arguments: CliHelpArgument[];
25
+ localOptions: CliHelpArgument[];
25
26
  subcommands: Array<{
26
27
  name: string;
27
28
  description?: string;
@@ -54,14 +54,16 @@ function argumentsFor(command) {
54
54
  export function describeCommand(root, rawArgs, cliVersion) {
55
55
  const resolved = resolveCommand(root, rawArgs.filter(argument => argument !== '--help' && argument !== '-h'));
56
56
  const meta = commandMeta(resolved.command);
57
+ const localArguments = resolved.command === root ? [] : argumentsFor(resolved.command);
57
58
  return {
58
59
  _tag: 'CliHelp',
59
- schemaVersion: 1,
60
+ schemaVersion: 2,
60
61
  cliVersion,
61
62
  command: resolved.path.join(' '),
62
63
  ...(meta.description === undefined ? {} : { description: meta.description }),
63
64
  globalOptions: argumentsFor(root).filter(argument => argument.type !== 'positional'),
64
- arguments: resolved.command === root ? [] : argumentsFor(resolved.command),
65
+ arguments: localArguments.filter(argument => argument.type === 'positional'),
66
+ localOptions: localArguments.filter(argument => argument.type !== 'positional'),
65
67
  subcommands: Object.entries(commandChildren(resolved.command)).flatMap(([name, value]) => {
66
68
  const child = childCommand(value);
67
69
  if (!child)
package/dist/commands.js CHANGED
@@ -5,7 +5,7 @@ import { openBrowser } from './browser.js';
5
5
  import { EXIT_CODE, fail, fromSdkFailure, ok } from './failures.js';
6
6
  import { awaitPairingApproval, createCliPairingClient, startPairing } from './pairing.js';
7
7
  import { parseAbsolutePageUrl, parseChoice, parseInteger } from './parse.js';
8
- import { renderAction, renderActionResolution, renderActions, renderContentBrief, renderContentBriefCreated, renderContentBriefs, renderContentDecay, renderDuplicateClusters, renderIndexingDiagnostics, renderKeywordResearch, renderLinkOpportunities, renderMentions, renderPageInspection, renderPageScan, renderPerformance, renderRankingsResearch, renderRecoverableBacklinks, renderResearchOverview, renderSearchAnalytics, renderSearchStatus, renderSerpResearch, renderSites, renderUsage, } from './render.js';
8
+ import { renderAction, renderActionResolution, renderActions, renderContentBrief, renderContentBriefCreated, renderContentBriefs, renderContentDecay, renderDuplicateClusters, renderIndexingDiagnostics, renderKeywordResearch, renderLinkOpportunities, renderMentions, renderPageInspection, renderPageScan, renderPerformance, renderRankingsResearch, renderRecoverableBacklinks, renderResearchOverview, renderSearchAnalytics, renderSearchStatus, renderSerpResearch, renderSites, renderTimeline, renderUsage, } from './render.js';
9
9
  import { writeCliResponse, writeDiagnostic, writeOutput, writeProtocolResponse } from './runtime.js';
10
10
  import { resolveSite } from './site.js';
11
11
  import { clearCredential, getCredentialStatus, readConfig, resolveApiUrl, saveCredential, updateConfig, } from './state/index.js';
@@ -606,6 +606,27 @@ async function auditLinkOpportunities(runtime, globals) {
606
606
  }, { signal: runtime.requestSignal }));
607
607
  return present(runtime, globals, response, value => renderLinkOpportunities(value.data));
608
608
  }
609
+ async function timelineEntries(runtime, globals, args) {
610
+ const limit = parseInteger(args.limit, { name: '--limit', minimum: 1, maximum: 100, defaultValue: 25 });
611
+ if (limit._tag === 'Err')
612
+ return limit;
613
+ const resolved = await apiAndSite(runtime, globals);
614
+ if (resolved._tag === 'Err')
615
+ return resolved;
616
+ const response = await withSpinner(runtime, 'Loading Timeline', () => resolved.value.api.client.timeline.entries({
617
+ params: { siteId: resolved.value.siteId },
618
+ query: {
619
+ kinds: args.kind,
620
+ feature: args.feature,
621
+ severity: args.severity,
622
+ since: args.since,
623
+ limit: limit.value,
624
+ cursor: args.cursor,
625
+ includeClosed: args.includeClosed,
626
+ },
627
+ }, { signal: runtime.requestSignal }));
628
+ return present(runtime, globals, response, value => renderTimeline(value.data));
629
+ }
609
630
  async function contentBriefList(runtime, globals, args) {
610
631
  const status = args.status === undefined
611
632
  ? ok(undefined)
@@ -977,6 +998,32 @@ export function createRootCommand(runtime, globals, execution) {
977
998
  }),
978
999
  },
979
1000
  });
1001
+ const timeline = defineCommand({
1002
+ meta: { name: 'timeline', description: 'Read the Timeline — what changed on the Site' },
1003
+ subCommands: {
1004
+ list: defineCommand({
1005
+ meta: { name: 'list', description: 'List Timeline Entries' },
1006
+ args: {
1007
+ 'kind': { type: 'string', description: 'Comma-separated Timeline Entry kinds, e.g. traffic.dropped,indexing.changed' },
1008
+ 'feature': { type: 'string', description: 'Filter to the kinds registered under this feature' },
1009
+ 'severity': { type: 'string', description: 'Comma-separated severities: info, notable, high' },
1010
+ 'since': { type: 'string', description: 'Only entries at or after this ISO timestamp' },
1011
+ 'limit': { type: 'string', description: 'Maximum Timeline Entries, 1 to 100' },
1012
+ 'cursor': { type: 'string', description: 'Opaque pagination cursor from a previous page' },
1013
+ 'include-closed': { type: 'boolean', description: 'Include closed episodes' },
1014
+ },
1015
+ run: ({ args }) => capture(execution, () => timelineEntries(runtime, globals, {
1016
+ kind: args.kind,
1017
+ feature: args.feature,
1018
+ severity: args.severity,
1019
+ since: args.since,
1020
+ limit: args.limit,
1021
+ cursor: args.cursor,
1022
+ includeClosed: args['include-closed'],
1023
+ }), args._, 0)(),
1024
+ }),
1025
+ },
1026
+ });
980
1027
  return defineCommand({
981
1028
  meta: {
982
1029
  name: 'nuxtseo',
@@ -1041,6 +1088,7 @@ export function createRootCommand(runtime, globals, execution) {
1041
1088
  }),
1042
1089
  research,
1043
1090
  search,
1091
+ timeline,
1044
1092
  },
1045
1093
  });
1046
1094
  }
package/dist/render.d.ts CHANGED
@@ -7,6 +7,7 @@ import type { IndexingDiagnosticsData, SearchAnalyticsData, SearchStatusData } f
7
7
  import type { PageInspect, PageScan } from '@nuxtseo/protocol/v1/pages';
8
8
  import type { SitePerformanceOverview } from '@nuxtseo/protocol/v1/performance';
9
9
  import type { KeywordResearchData, RankingsResearchData, SerpResearchData, StoredResearchOverview } from '@nuxtseo/protocol/v1/research';
10
+ import type { TimelineEntries } from '@nuxtseo/protocol/v1/timeline';
10
11
  export declare function renderSites(response: SitesListResponse, selectedSiteId?: string): string;
11
12
  export declare function renderUsage(response: AccountUsageResponse): string;
12
13
  export declare function renderActions(data: ActionList): string;
@@ -28,5 +29,6 @@ export declare function renderContentBriefCreated(data: ContentBriefCreateData):
28
29
  export declare function renderContentDecay(data: AuditContentDecay): string;
29
30
  export declare function renderDuplicateClusters(data: AuditDuplicateClusters): string;
30
31
  export declare function renderLinkOpportunities(data: AuditLinkOpportunities): string;
32
+ export declare function renderTimeline(data: TimelineEntries): string;
31
33
  export declare function renderRecoverableBacklinks(data: RecoverableBacklinks): string;
32
34
  export declare function renderMentions(data: Mentions): string;
package/dist/render.js CHANGED
@@ -270,6 +270,18 @@ export function renderLinkOpportunities(data) {
270
270
  ...data.rows.map(row => `${row.phrase}\n From: ${row.sourceUrl}\n To: ${row.targetUrl}`),
271
271
  ].join('\n');
272
272
  }
273
+ export function renderTimeline(data) {
274
+ if (data.entries.length === 0)
275
+ return 'No Timeline Entries match these filters.';
276
+ return [
277
+ `Timeline (${data.entries.length})`,
278
+ ...data.entries.map((entry) => {
279
+ const label = typeof entry.rendered?.label === 'string' ? entry.rendered.label : entry.kind;
280
+ return `${entry.occurredAt} ${entry.severity ?? 'unrated'} ${entry.kind}\n ${label}`;
281
+ }),
282
+ data.nextCursor ? `More Timeline Entries available — pass --cursor ${JSON.stringify(data.nextCursor)}.` : '',
283
+ ].filter(Boolean).join('\n');
284
+ }
273
285
  export function renderRecoverableBacklinks(data) {
274
286
  if (data.items.length === 0)
275
287
  return 'No recoverable Backlinks. Every stored inbound link resolves.';
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@nuxtseo/cli",
3
3
  "type": "module",
4
- "version": "0.1.3",
4
+ "version": "0.1.4",
5
5
  "description": "Command line interface for the NuxtSEO public API.",
6
6
  "license": "MIT",
7
7
  "homepage": "https://nuxtseo.com/pro",
@@ -34,7 +34,7 @@
34
34
  "@clack/prompts": "^1.7.0",
35
35
  "citty": "^0.2.2",
36
36
  "pathe": "^2.0.3",
37
- "@nuxtseo/sdk": "^0.1.3"
37
+ "@nuxtseo/sdk": "^0.1.4"
38
38
  },
39
39
  "optionalDependencies": {
40
40
  "@napi-rs/keyring": "^1.3.0"
@@ -44,7 +44,7 @@
44
44
  "@types/node": "^26.2.0",
45
45
  "publint": "^0.3.24",
46
46
  "typescript": "npm:typescript-native-bridge@6.0.3-bridge.10.tsgo.7.0.2",
47
- "@nuxtseo/protocol": "0.1.3"
47
+ "@nuxtseo/protocol": "0.1.4"
48
48
  },
49
49
  "publishConfig": {
50
50
  "access": "public"
@@ -132,11 +132,11 @@ handling failures, read [CLI protocol](references/protocol.md).
132
132
  `page inspect` and `page scan` take an absolute URL, for example
133
133
  `https://example.com/about`.
134
134
 
135
- `--help --json` returns a `CliHelp` value for one command level: its
136
- `arguments`, the inherited `globalOptions`, and its `subcommands` as names and
137
- descriptions only. To learn a subcommand's own flags, ask it directly, for
138
- example `nuxtseo actions list --help --json`. Use this instead of guessing a
139
- flag, and prefer the table above for anything it already answers.
135
+ `--help --json` returns a `CliHelp` value for one command level. It separates
136
+ positional `arguments`, command-specific `localOptions`, inherited
137
+ `globalOptions`, and `subcommands`. To learn a subcommand's own flags, ask it
138
+ directly, for example `nuxtseo actions list --help --json`. Use this instead of
139
+ guessing a flag, and prefer the table above for anything it already answers.
140
140
 
141
141
  ## The triage loop
142
142
 
@@ -18,7 +18,8 @@ Protocol failure, still on stdout, with recovery details on stderr:
18
18
 
19
19
  Read `data`. The CLI does not unwrap, rename, rank, or enrich fields.
20
20
 
21
- Local outcomes have no server body. They carry `schemaVersion: 1` and one tag:
21
+ Local outcomes have no server body. They carry one tag and their own schema
22
+ version. `CliHelp` uses version 2. The other values use version 1.
22
23
 
23
24
  | `_tag` | Written by |
24
25
  | --- | --- |