@nuxtseo/cli 0.1.0 → 0.1.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 +39 -7
- package/dist/cli.js +205 -31
- package/dist/commands.js +377 -4
- package/dist/failures.js +18 -2
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1 -0
- package/dist/parse.d.ts +5 -0
- package/dist/parse.js +7 -0
- package/dist/render.d.ts +15 -1
- package/dist/render.js +134 -9
- package/dist/runtime.d.ts +1 -0
- package/dist/state/paths.d.ts +2 -0
- package/dist/state/paths.js +2 -0
- package/dist/update-check.d.ts +25 -0
- package/dist/update-check.js +107 -0
- package/package.json +5 -8
- package/skills/nuxtseo-cli/SKILL.md +41 -31
- package/skills/nuxtseo-cli/references/protocol.md +17 -1
package/README.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# NuxtSEO CLI
|
|
2
2
|
|
|
3
3
|
Use NuxtSEO public Site operations from a terminal, script, or coding agent.
|
|
4
|
-
The CLI sends feature requests through `@nuxtseo/sdk`. It does not call MCP
|
|
5
|
-
private routes
|
|
4
|
+
The CLI sends feature requests through `@nuxtseo/sdk`. It does not call MCP or
|
|
5
|
+
private routes. Live research uses declared public API operations.
|
|
6
6
|
|
|
7
7
|
## Install
|
|
8
8
|
|
|
@@ -148,7 +148,8 @@ Agent controls:
|
|
|
148
148
|
| `--no-input` | Disable every prompt even when stdin and stdout are TTYs |
|
|
149
149
|
| `--timeout-ms <milliseconds>` | Apply one deadline to Site resolution, SDK retries, and the operation; default `30000`, maximum `300000` |
|
|
150
150
|
|
|
151
|
-
Request timeouts return `CliError` code `request_timeout` and exit `6`.
|
|
151
|
+
Request timeouts return `CliError` code `request_timeout` and exit `6`. The
|
|
152
|
+
error prints an exact retry command with a longer deadline.
|
|
152
153
|
`SIGINT` and `SIGTERM` abort the active request and return exit `130`.
|
|
153
154
|
|
|
154
155
|
## Commands and paging
|
|
@@ -170,8 +171,28 @@ nuxtseo page inspect <url>
|
|
|
170
171
|
nuxtseo page scan <url>
|
|
171
172
|
nuxtseo performance
|
|
172
173
|
nuxtseo search status
|
|
174
|
+
nuxtseo search analytics <pages|keywords>
|
|
175
|
+
nuxtseo research overview
|
|
176
|
+
nuxtseo research keywords <topic>
|
|
177
|
+
nuxtseo research serp <keyword>
|
|
178
|
+
nuxtseo research rankings <domain>
|
|
179
|
+
nuxtseo audit link-opportunities
|
|
180
|
+
nuxtseo audit content-decay
|
|
181
|
+
nuxtseo audit duplicates
|
|
182
|
+
nuxtseo content briefs list
|
|
183
|
+
nuxtseo content briefs show <brief-id>
|
|
184
|
+
nuxtseo content briefs create <keyword>
|
|
173
185
|
```
|
|
174
186
|
|
|
187
|
+
Run bare `nuxtseo` in a terminal for a task-based menu. It groups Site fixes,
|
|
188
|
+
performance, research, content, and account setup. The menu prints the direct
|
|
189
|
+
command it runs.
|
|
190
|
+
|
|
191
|
+
`search status` reads stored connection state. It never waits for Google.
|
|
192
|
+
`research keywords`, `research serp`, and `research rankings` can use the Team
|
|
193
|
+
research allowance. Keyword responses report cache use in `evidence`. SERP and
|
|
194
|
+
ranking responses report `cached: true`. Cached responses use no unit.
|
|
195
|
+
|
|
175
196
|
The CLI fetches one page per invocation. It does not auto-page or merge
|
|
176
197
|
responses.
|
|
177
198
|
|
|
@@ -182,18 +203,29 @@ responses.
|
|
|
182
203
|
| `page inspect` | `--limit 1..200`, `--offset >=0` | `--limit 100 --offset 0` |
|
|
183
204
|
| `backlinks recoverable` | `--limit 1..200`, `--offset >=0` | `--limit 100 --offset 0` |
|
|
184
205
|
| `mentions list` | `--limit 1..200` | `--limit 100` |
|
|
206
|
+
| `search analytics` | `--limit 1..100`, `--page >=1` | `--limit 25 --page 1` |
|
|
207
|
+
| `content briefs list` | `--limit 1..100`, `--offset >=0` | `--limit 25 --offset 0` |
|
|
185
208
|
|
|
186
209
|
Keep the server order for actions. For another page, pass the next offset or
|
|
187
210
|
cursor reported by the response. A cursor is opaque; do not edit or infer it.
|
|
188
211
|
|
|
189
212
|
## Coding agents
|
|
190
213
|
|
|
191
|
-
The package ships an agent skill that teaches
|
|
192
|
-
Copy it into a
|
|
214
|
+
The package ships an agent skill that teaches coding agents how to drive the
|
|
215
|
+
CLI. Copy it into a global skill directory after installation.
|
|
216
|
+
|
|
217
|
+
Claude Code:
|
|
218
|
+
|
|
219
|
+
```sh
|
|
220
|
+
mkdir -p ~/.claude/skills
|
|
221
|
+
cp -R node_modules/@nuxtseo/cli/skills/nuxtseo-cli ~/.claude/skills/
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
Codex:
|
|
193
225
|
|
|
194
226
|
```sh
|
|
195
|
-
mkdir -p
|
|
196
|
-
cp -R node_modules/@nuxtseo/cli/skills/nuxtseo-cli
|
|
227
|
+
mkdir -p ~/.codex/skills
|
|
228
|
+
cp -R node_modules/@nuxtseo/cli/skills/nuxtseo-cli ~/.codex/skills/
|
|
197
229
|
```
|
|
198
230
|
|
|
199
231
|
The skill covers the JSON contract, Site selection, paging, mutation consent,
|
package/dist/cli.js
CHANGED
|
@@ -7,16 +7,28 @@ import { EXIT_CODE, fail, fromSdkFailure, ok, unexpectedFailure } from './failur
|
|
|
7
7
|
import { extractGlobalOptions } from './parse.js';
|
|
8
8
|
import { writeCliResponse, writeDiagnostic, writeOutput, writeProtocolResponse } from './runtime.js';
|
|
9
9
|
import { getCredentialStatus, readConfig, resolveApiUrl, resolveSiteId, updateConfig } from './state/index.js';
|
|
10
|
+
import { checkForUpdate, updateNoticeLine } from './update-check.js';
|
|
10
11
|
import { VERSION } from './version.js';
|
|
11
|
-
function
|
|
12
|
+
function writeUpdateNotice(runtime, notice) {
|
|
13
|
+
if (notice)
|
|
14
|
+
writeDiagnostic(runtime, updateNoticeLine(notice));
|
|
15
|
+
}
|
|
16
|
+
function report(runtime, failure, json = false, notice = null) {
|
|
12
17
|
if (json) {
|
|
13
18
|
if (failure.protocolResponse !== undefined) {
|
|
14
19
|
writeProtocolResponse(runtime, failure.protocolResponse);
|
|
15
20
|
}
|
|
16
21
|
else {
|
|
22
|
+
// Exit 2 and exit 7 are where a stale binary masquerades as broken docs
|
|
23
|
+
// or a broken server. Version skew is a plausible cause there, so the
|
|
24
|
+
// envelope names both versions and stderr carries the update command.
|
|
25
|
+
const carriesVersionSkew = failure.exitCode === EXIT_CODE.invalidInput || failure.exitCode === EXIT_CODE.infrastructure;
|
|
17
26
|
writeCliResponse(runtime, {
|
|
18
27
|
_tag: 'CliError',
|
|
19
28
|
schemaVersion: 1,
|
|
29
|
+
...(carriesVersionSkew
|
|
30
|
+
? { cliVersion: VERSION, ...(notice ? { latestKnownVersion: notice.latest } : {}) }
|
|
31
|
+
: {}),
|
|
20
32
|
error: {
|
|
21
33
|
code: failure.code,
|
|
22
34
|
exitCode: failure.exitCode,
|
|
@@ -135,6 +147,45 @@ async function promptPageUrl(runtime, action) {
|
|
|
135
147
|
? fail(EXIT_CODE.interrupted, `interrupted: ${action} cancelled.`)
|
|
136
148
|
: ok(url);
|
|
137
149
|
}
|
|
150
|
+
async function promptTextValue(runtime, options) {
|
|
151
|
+
const value = await prompts.text({
|
|
152
|
+
message: options.message,
|
|
153
|
+
placeholder: options.placeholder,
|
|
154
|
+
input: runtime.input,
|
|
155
|
+
output: runtime.error,
|
|
156
|
+
signal: runtime.signal,
|
|
157
|
+
validate: input => typeof input === 'string' && input.trim().length > 1
|
|
158
|
+
? undefined
|
|
159
|
+
: `${options.label} is required.`,
|
|
160
|
+
});
|
|
161
|
+
return prompts.isCancel(value)
|
|
162
|
+
? fail(EXIT_CODE.interrupted, `interrupted: ${options.label} entry cancelled.`)
|
|
163
|
+
: ok(value.trim());
|
|
164
|
+
}
|
|
165
|
+
function displayCommand(args) {
|
|
166
|
+
return `nuxtseo ${args.map(argument => /\s/.test(argument) ? JSON.stringify(argument) : argument).join(' ')}`;
|
|
167
|
+
}
|
|
168
|
+
function timeoutRetryFailure(failure, rawArgs, timeoutMs) {
|
|
169
|
+
if (failure.code !== 'request_timeout')
|
|
170
|
+
return failure;
|
|
171
|
+
const args = [];
|
|
172
|
+
for (let index = 0; index < rawArgs.length; index++) {
|
|
173
|
+
const argument = rawArgs[index];
|
|
174
|
+
if (argument === '--timeout-ms') {
|
|
175
|
+
index++;
|
|
176
|
+
continue;
|
|
177
|
+
}
|
|
178
|
+
if (argument.startsWith('--timeout-ms='))
|
|
179
|
+
continue;
|
|
180
|
+
args.push(argument);
|
|
181
|
+
}
|
|
182
|
+
args.push('--timeout-ms', String(Math.min(timeoutMs * 2, 300_000)));
|
|
183
|
+
return { ...failure, message: `${failure.message}\nRetry: ${displayCommand(args)}` };
|
|
184
|
+
}
|
|
185
|
+
async function runInteractiveCommand(args, runtime, globals) {
|
|
186
|
+
writeDiagnostic(runtime, `Command: ${displayCommand(args)}`);
|
|
187
|
+
return runExplicit(args, runtime, globals);
|
|
188
|
+
}
|
|
138
189
|
async function interactiveBare(runtime, globals) {
|
|
139
190
|
const [auth, apiUrl, config] = await Promise.all([
|
|
140
191
|
getCredentialStatus({ env: runtime.env, paths: runtime.paths }),
|
|
@@ -153,49 +204,160 @@ async function interactiveBare(runtime, globals) {
|
|
|
153
204
|
`API: ${apiUrl.value.apiUrl}`,
|
|
154
205
|
`Current Site: ${config.value.siteId ?? 'none'}`,
|
|
155
206
|
].join('\n'), 'Status', { input: runtime.input, output: runtime.error });
|
|
156
|
-
const
|
|
157
|
-
message: '
|
|
207
|
+
const group = await prompts.select({
|
|
208
|
+
message: 'Choose a task',
|
|
158
209
|
options: [
|
|
159
210
|
...(!auth.value.authenticated ? [{ value: 'login', label: 'Log in', hint: 'Team API token' }] : []),
|
|
211
|
+
{ value: 'fix', label: 'Fix my Site', hint: 'Issues, Pages, and scans' },
|
|
212
|
+
{ value: 'performance', label: 'Understand performance', hint: 'Performance and Search Console' },
|
|
213
|
+
{ value: 'research', label: 'Research growth', hint: 'Keywords, SERPs, and competitors' },
|
|
214
|
+
{ value: 'content', label: 'Plan content', hint: 'Briefs, decay, and duplicates' },
|
|
215
|
+
{ value: 'account', label: 'Account and setup', hint: config.value.siteId ?? 'no Site selected' },
|
|
216
|
+
{ value: 'exit', label: 'Exit' },
|
|
217
|
+
],
|
|
218
|
+
input: runtime.input,
|
|
219
|
+
output: runtime.error,
|
|
220
|
+
signal: runtime.signal,
|
|
221
|
+
});
|
|
222
|
+
if (prompts.isCancel(group) || group === 'exit')
|
|
223
|
+
return ok(undefined);
|
|
224
|
+
if (group === 'login')
|
|
225
|
+
return runInteractiveCommand(['login'], runtime, globals);
|
|
226
|
+
if (group === 'fix') {
|
|
227
|
+
const task = await prompts.select({
|
|
228
|
+
message: 'Fix my Site',
|
|
229
|
+
options: [
|
|
230
|
+
{ value: 'actions', label: 'Issues and opportunities' },
|
|
231
|
+
{ value: 'inspect', label: 'Inspect a Page' },
|
|
232
|
+
{ value: 'scan', label: 'Scan a Page' },
|
|
233
|
+
{ value: 'backlinks', label: 'Recover broken Backlinks' },
|
|
234
|
+
],
|
|
235
|
+
input: runtime.input,
|
|
236
|
+
output: runtime.error,
|
|
237
|
+
signal: runtime.signal,
|
|
238
|
+
});
|
|
239
|
+
if (prompts.isCancel(task))
|
|
240
|
+
return ok(undefined);
|
|
241
|
+
if (task === 'inspect' || task === 'scan') {
|
|
242
|
+
const url = await promptPageUrl(runtime, task === 'inspect' ? 'Inspect' : 'Scan');
|
|
243
|
+
return url._tag === 'Err'
|
|
244
|
+
? url
|
|
245
|
+
: runInteractiveCommand(['page', task, url.value], runtime, globals);
|
|
246
|
+
}
|
|
247
|
+
return runInteractiveCommand(task === 'actions' ? ['actions', 'list'] : ['backlinks', 'recoverable'], runtime, globals);
|
|
248
|
+
}
|
|
249
|
+
if (group === 'performance') {
|
|
250
|
+
const task = await prompts.select({
|
|
251
|
+
message: 'Understand performance',
|
|
252
|
+
options: [
|
|
253
|
+
{ value: 'performance', label: 'Performance overview' },
|
|
254
|
+
{ value: 'connection', label: 'Search Console connection' },
|
|
255
|
+
{ value: 'pages', label: 'Search Console Pages' },
|
|
256
|
+
{ value: 'keywords', label: 'Search Console queries' },
|
|
257
|
+
],
|
|
258
|
+
input: runtime.input,
|
|
259
|
+
output: runtime.error,
|
|
260
|
+
signal: runtime.signal,
|
|
261
|
+
});
|
|
262
|
+
if (prompts.isCancel(task))
|
|
263
|
+
return ok(undefined);
|
|
264
|
+
const command = task === 'performance'
|
|
265
|
+
? ['performance']
|
|
266
|
+
: task === 'connection'
|
|
267
|
+
? ['search', 'status']
|
|
268
|
+
: ['search', 'analytics', task];
|
|
269
|
+
return runInteractiveCommand(command, runtime, globals);
|
|
270
|
+
}
|
|
271
|
+
if (group === 'research') {
|
|
272
|
+
const task = await prompts.select({
|
|
273
|
+
message: 'Research growth',
|
|
274
|
+
options: [
|
|
275
|
+
{ value: 'overview', label: 'Site and competitors', hint: 'stored data' },
|
|
276
|
+
{ value: 'keywords', label: 'Keyword ideas', hint: 'uses live research allowance' },
|
|
277
|
+
{ value: 'serp', label: 'SERP analysis', hint: 'uses live research allowance' },
|
|
278
|
+
{ value: 'rankings', label: 'Domain rankings', hint: 'uses live research allowance' },
|
|
279
|
+
{ value: 'links', label: 'Link opportunities', hint: 'stored data' },
|
|
280
|
+
],
|
|
281
|
+
input: runtime.input,
|
|
282
|
+
output: runtime.error,
|
|
283
|
+
signal: runtime.signal,
|
|
284
|
+
});
|
|
285
|
+
if (prompts.isCancel(task))
|
|
286
|
+
return ok(undefined);
|
|
287
|
+
if (task === 'overview')
|
|
288
|
+
return runInteractiveCommand(['research', 'overview'], runtime, globals);
|
|
289
|
+
if (task === 'links')
|
|
290
|
+
return runInteractiveCommand(['audit', 'link-opportunities'], runtime, globals);
|
|
291
|
+
const label = task === 'rankings' ? 'Domain' : 'Keyword';
|
|
292
|
+
const value = await promptTextValue(runtime, {
|
|
293
|
+
message: task === 'rankings' ? 'Which domain?' : 'Which keyword or topic?',
|
|
294
|
+
placeholder: task === 'rankings' ? 'example.com' : 'nuxt seo',
|
|
295
|
+
label,
|
|
296
|
+
});
|
|
297
|
+
if (value._tag === 'Err')
|
|
298
|
+
return value;
|
|
299
|
+
return runInteractiveCommand(['research', task, value.value], runtime, globals);
|
|
300
|
+
}
|
|
301
|
+
if (group === 'content') {
|
|
302
|
+
const task = await prompts.select({
|
|
303
|
+
message: 'Plan content',
|
|
304
|
+
options: [
|
|
305
|
+
{ value: 'briefs', label: 'Content Briefs' },
|
|
306
|
+
{ value: 'create', label: 'Create a Content Brief' },
|
|
307
|
+
{ value: 'decay', label: 'Content decay' },
|
|
308
|
+
{ value: 'duplicates', label: 'Duplicate clusters' },
|
|
309
|
+
],
|
|
310
|
+
input: runtime.input,
|
|
311
|
+
output: runtime.error,
|
|
312
|
+
signal: runtime.signal,
|
|
313
|
+
});
|
|
314
|
+
if (prompts.isCancel(task))
|
|
315
|
+
return ok(undefined);
|
|
316
|
+
if (task === 'briefs')
|
|
317
|
+
return runInteractiveCommand(['content', 'briefs', 'list'], runtime, globals);
|
|
318
|
+
if (task === 'decay')
|
|
319
|
+
return runInteractiveCommand(['audit', 'content-decay'], runtime, globals);
|
|
320
|
+
if (task === 'duplicates')
|
|
321
|
+
return runInteractiveCommand(['audit', 'duplicates'], runtime, globals);
|
|
322
|
+
const keyword = await promptTextValue(runtime, {
|
|
323
|
+
message: 'Which target keyword?',
|
|
324
|
+
placeholder: 'nuxt seo',
|
|
325
|
+
label: 'Keyword',
|
|
326
|
+
});
|
|
327
|
+
return keyword._tag === 'Err'
|
|
328
|
+
? keyword
|
|
329
|
+
: runInteractiveCommand(['content', 'briefs', 'create', keyword.value], runtime, globals);
|
|
330
|
+
}
|
|
331
|
+
const task = await prompts.select({
|
|
332
|
+
message: 'Account and setup',
|
|
333
|
+
options: [
|
|
160
334
|
{ value: 'site', label: 'Switch Site', hint: config.value.siteId ?? 'none selected' },
|
|
161
|
-
{ value: 'actions', label: 'Next actions' },
|
|
162
|
-
{ value: 'inspect', label: 'Inspect a Page' },
|
|
163
|
-
{ value: 'scan', label: 'Scan a Page' },
|
|
164
|
-
{ value: 'performance', label: 'Performance overview' },
|
|
165
|
-
{ value: 'search', label: 'Search Console status' },
|
|
166
335
|
{ value: 'usage', label: 'Account usage' },
|
|
167
336
|
{ value: 'config', label: 'Configuration' },
|
|
168
|
-
{ value: '
|
|
337
|
+
{ value: 'logout', label: 'Log out' },
|
|
169
338
|
],
|
|
170
339
|
input: runtime.input,
|
|
171
340
|
output: runtime.error,
|
|
172
341
|
signal: runtime.signal,
|
|
173
342
|
});
|
|
174
|
-
if (prompts.isCancel(
|
|
343
|
+
if (prompts.isCancel(task))
|
|
175
344
|
return ok(undefined);
|
|
176
|
-
if (
|
|
345
|
+
if (task === 'site')
|
|
177
346
|
return switchSite(runtime, globals);
|
|
178
|
-
|
|
179
|
-
const url = await promptPageUrl(runtime, selected === 'inspect' ? 'Inspect' : 'Scan');
|
|
180
|
-
return url._tag === 'Err'
|
|
181
|
-
? url
|
|
182
|
-
: runExplicit(['page', selected, url.value], runtime, globals);
|
|
183
|
-
}
|
|
184
|
-
const commands = {
|
|
185
|
-
login: ['login'],
|
|
186
|
-
actions: ['actions', 'list'],
|
|
187
|
-
performance: ['performance'],
|
|
188
|
-
search: ['search', 'status'],
|
|
189
|
-
usage: ['usage'],
|
|
190
|
-
config: ['config'],
|
|
191
|
-
};
|
|
192
|
-
return runExplicit(commands[selected], runtime, globals);
|
|
347
|
+
return runInteractiveCommand([task], runtime, globals);
|
|
193
348
|
}
|
|
194
349
|
export async function runCli(rawArgs, runtime) {
|
|
195
350
|
const jsonRequested = rawArgs.includes('--json');
|
|
351
|
+
// Started before parsing so the registry round trip overlaps the command. The
|
|
352
|
+
// check reads a local cache and never rejects; a slow registry only delays
|
|
353
|
+
// this line, never the command result itself.
|
|
354
|
+
const updateCheck = checkForUpdate({ paths: runtime.paths, env: runtime.env });
|
|
196
355
|
const parsed = extractGlobalOptions(rawArgs);
|
|
197
|
-
if (parsed._tag === 'Err')
|
|
198
|
-
|
|
356
|
+
if (parsed._tag === 'Err') {
|
|
357
|
+
const notice = await updateCheck;
|
|
358
|
+
writeUpdateNotice(runtime, notice);
|
|
359
|
+
return report(runtime, parsed.error, jsonRequested, notice);
|
|
360
|
+
}
|
|
199
361
|
const { args, options: globals } = parsed.value;
|
|
200
362
|
const effectiveRuntime = {
|
|
201
363
|
...runtime,
|
|
@@ -204,23 +366,31 @@ export async function runCli(rawArgs, runtime) {
|
|
|
204
366
|
runtime.requestSignal,
|
|
205
367
|
AbortSignal.timeout(globals.timeoutMs),
|
|
206
368
|
]),
|
|
369
|
+
requestTimeoutMs: globals.timeoutMs,
|
|
207
370
|
};
|
|
208
371
|
if (args.length === 1 && (args[0] === '--version' || args[0] === '-v')) {
|
|
372
|
+
const notice = await updateCheck;
|
|
209
373
|
if (globals.json)
|
|
210
374
|
writeCliResponse(effectiveRuntime, { _tag: 'CliVersion', schemaVersion: 1, version: VERSION });
|
|
211
375
|
else
|
|
212
376
|
writeOutput(effectiveRuntime, VERSION);
|
|
377
|
+
writeUpdateNotice(effectiveRuntime, notice);
|
|
213
378
|
return EXIT_CODE.success;
|
|
214
379
|
}
|
|
215
380
|
if (args.includes('--help') || args.includes('-h')) {
|
|
216
381
|
const command = createRootCommand(effectiveRuntime, globals, { result: null });
|
|
217
382
|
const validOptions = validateCommandOptions(command, args);
|
|
218
|
-
if (validOptions._tag === 'Err')
|
|
219
|
-
|
|
383
|
+
if (validOptions._tag === 'Err') {
|
|
384
|
+
const notice = await updateCheck;
|
|
385
|
+
writeUpdateNotice(effectiveRuntime, notice);
|
|
386
|
+
return report(effectiveRuntime, validOptions.error, globals.json, notice);
|
|
387
|
+
}
|
|
220
388
|
if (globals.json)
|
|
221
389
|
writeCliResponse(effectiveRuntime, describeCommand(command, args, VERSION));
|
|
222
390
|
else
|
|
223
391
|
writeOutput(effectiveRuntime, await requestedUsage(effectiveRuntime, globals, args));
|
|
392
|
+
const notice = await updateCheck;
|
|
393
|
+
writeUpdateNotice(effectiveRuntime, notice);
|
|
224
394
|
return EXIT_CODE.success;
|
|
225
395
|
}
|
|
226
396
|
const result = args.length === 0
|
|
@@ -228,5 +398,9 @@ export async function runCli(rawArgs, runtime) {
|
|
|
228
398
|
? await interactiveBare(effectiveRuntime, globals)
|
|
229
399
|
: await nonInteractiveBare(effectiveRuntime, globals)
|
|
230
400
|
: await runExplicit(args, effectiveRuntime, globals);
|
|
231
|
-
|
|
401
|
+
const notice = await updateCheck;
|
|
402
|
+
writeUpdateNotice(effectiveRuntime, notice);
|
|
403
|
+
return result._tag === 'Ok'
|
|
404
|
+
? EXIT_CODE.success
|
|
405
|
+
: report(effectiveRuntime, timeoutRetryFailure(result.error, rawArgs, globals.timeoutMs), globals.json, notice);
|
|
232
406
|
}
|