@vigilbase/cli 0.2.4 → 0.2.6
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/CLOUDFLARE.md +9 -2
- package/COMMANDS.md +5 -5
- package/LOCAL.md +87 -12
- package/ONE.md +2 -2
- package/README.md +13 -10
- package/dist/commands/cloudflare.js +79 -13
- package/dist/commands/helpers.d.ts +4 -1
- package/dist/commands/local.js +44 -23
- package/dist/lib/checkup-report-limits.d.ts +16 -0
- package/dist/lib/checkup-report-limits.js +16 -0
- package/dist/lib/checkup-report.d.ts +118 -0
- package/dist/lib/checkup-report.js +503 -0
- package/dist/lib/format.d.ts +2 -0
- package/dist/lib/format.js +1 -1
- package/dist/lib/one-error-hints.d.ts +1 -0
- package/dist/lib/one-error-hints.js +10 -0
- package/dist/lib/tui.d.ts +4 -0
- package/dist/lib/tui.js +2 -2
- package/dist/local/cloudflare-checkup/cf/allowlist.js +5 -2
- package/dist/local/cloudflare-checkup/collector.js +5 -5
- package/dist/local/cloudflare-checkup/contract.d.ts +481 -20
- package/dist/local/cloudflare-checkup/contract.js +111 -27
- package/dist/local/cloudflare-checkup/email-verification.d.ts +8 -1
- package/dist/local/cloudflare-checkup/email-verification.js +0 -5
- package/dist/local/cloudflare-checkup/identity-verification.d.ts +11 -0
- package/dist/local/cloudflare-checkup/identity-verification.js +65 -0
- package/dist/local/cloudflare-checkup/snapshot-file.js +13 -1
- package/dist/local/cloudflare-checkup/submit-client.d.ts +6 -3
- package/dist/local/cloudflare-checkup/submit-client.js +17 -4
- package/dist/local/cloudflare-checkup/terminal-ui.d.ts +10 -3
- package/dist/local/cloudflare-checkup/terminal-ui.js +13 -3
- package/package.json +1 -1
package/dist/commands/local.js
CHANGED
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
* ≡ `vigilbase local cloudflare-checkup [run] [options]`, plus the advanced
|
|
4
4
|
* `login|doctor|accounts|collect|inspect|submit|logout` subcommands.
|
|
5
5
|
*
|
|
6
|
-
* The one command
|
|
7
|
-
* 6-digit code
|
|
8
|
-
* in to Cloudflare with a read-only cf login, lets the user choose the
|
|
6
|
+
* The one command uses the signed-in Vigilbase identity, or verifies a work
|
|
7
|
+
* email with a 6-digit code, then finds (or, after asking, installs) Cloudflare's
|
|
8
|
+
* cf CLI, signs in to Cloudflare with a read-only cf login, lets the user choose the
|
|
9
9
|
* account, collects a minimised snapshot with a fixed allowlist of read-only
|
|
10
10
|
* cf commands, shows what it contains, uploads exactly that file to
|
|
11
11
|
* vigilbase.com after an explicit confirmation and opens the results page.
|
|
@@ -17,9 +17,12 @@ import { homedir } from 'node:os';
|
|
|
17
17
|
import { dirname, isAbsolute, join, resolve, sep } from 'node:path';
|
|
18
18
|
import { z } from 'zod';
|
|
19
19
|
import { CLI_VERSION } from '../version.js';
|
|
20
|
+
import { verifySignedInRequester } from '../local/cloudflare-checkup/identity-verification.js';
|
|
21
|
+
import { localResultPlainLines, readCheckupReport } from '../lib/checkup-report.js';
|
|
20
22
|
import { resolveConfigDir } from '../lib/config.js';
|
|
21
23
|
import { isHeadlessEnvironment, openUrlInBrowser } from '../lib/open-url.js';
|
|
22
24
|
import { shouldShowProgress } from '../lib/progress.js';
|
|
25
|
+
import { UNICODE_SYMBOLS, createStyles } from '../lib/tui.js';
|
|
23
26
|
import { CF_SUPPORTED, NOT_COLLECTABLE_ZONE_KINDS, PRODUCTION_ORIGIN, RECOMMENDED_CF_SCOPES, SNAPSHOT_COLLECTOR_NAME, SNAPSHOT_LIMITS, SNAPSHOT_SCHEMA_VERSION, } from '../local/cloudflare-checkup/contract.js';
|
|
24
27
|
import { CF_DAMAGED_HINT, CF_NOT_FOUND_ADVANCED_HINT, ENV_TOKEN_AUTH_HINT, LOCAL_ERROR_HINTS, LocalCheckupError, SNAPSHOT_STALE_FILE_HINT, } from '../local/cloudflare-checkup/errors.js';
|
|
25
28
|
import { CF_TOKEN_ENV, buildChildEnv, childEnvVariableNames, hasEnvToken, missingEnvTokenMessage, strippedTokenNotice, } from '../local/cloudflare-checkup/cf/child-env.js';
|
|
@@ -53,15 +56,17 @@ Runs the Vigilbase Cloudflare Checkup on this machine, in one command:
|
|
|
53
56
|
|
|
54
57
|
${NPX_COMMAND} cloudflare-checkup
|
|
55
58
|
|
|
56
|
-
It
|
|
57
|
-
|
|
59
|
+
It uses your Vigilbase login, skipping email prompts and codes. Signed out,
|
|
60
|
+
it verifies your work email with a 6-digit code. It installs Cloudflare's cf
|
|
61
|
+
CLI if it is missing (asks first), signs in to Cloudflare with
|
|
58
62
|
a read-only cf login, lets you choose the account, collects a snapshot, shows
|
|
59
63
|
what it contains, uploads it only after you confirm and opens the results.
|
|
60
64
|
cf owns your Cloudflare login; this CLI never reads, prints, stores or
|
|
61
65
|
uploads it.
|
|
62
66
|
|
|
63
67
|
Options (the default command, also called run):
|
|
64
|
-
--email <address>
|
|
68
|
+
--email <address> Override the signed-in email; a different
|
|
69
|
+
address requires email verification
|
|
65
70
|
--account-id <id> Skip the account picker
|
|
66
71
|
--out <file> Where to save the snapshot (default:
|
|
67
72
|
<config dir>/checkups/…); --overwrite
|
|
@@ -81,8 +86,8 @@ Options (the default command, also called run):
|
|
|
81
86
|
(default) or a loopback origin
|
|
82
87
|
--timeout <milliseconds> · --json
|
|
83
88
|
|
|
84
|
-
Without a terminal, add --yes
|
|
85
|
-
code are read from stdin
|
|
89
|
+
Without a terminal, add --yes. When email verification is needed, the email
|
|
90
|
+
(unless --email is given) and code are read from stdin after their prompts.
|
|
86
91
|
|
|
87
92
|
Advanced commands:
|
|
88
93
|
login Create or refresh the read-only cf profile
|
|
@@ -93,7 +98,7 @@ Advanced commands:
|
|
|
93
98
|
collect --out <file> Collect only [--overwrite] [--account-id];
|
|
94
99
|
--dry-run shows every cf command (offline)
|
|
95
100
|
inspect <file> Validate a snapshot and show what it contains
|
|
96
|
-
submit <file>
|
|
101
|
+
submit <file> Identify yourself, then upload a snapshot
|
|
97
102
|
[--email] [--yes|--confirm] [--no-open]
|
|
98
103
|
[--base-url] [--timeout]
|
|
99
104
|
logout Delete the read-only cf profile and the cf
|
|
@@ -1272,14 +1277,23 @@ function assertWithinLimits(server, loaded) {
|
|
|
1272
1277
|
throw new LocalCheckupError('SNAPSHOT_TOO_LARGE', `The snapshot is over the vigilbase.com limit (${formatBytes(maxBytes)}, ${maxZones} zones). Nothing was uploaded.`);
|
|
1273
1278
|
}
|
|
1274
1279
|
}
|
|
1275
|
-
/**
|
|
1276
|
-
* The email verification (prompts on stderr, answers from piped stdin
|
|
1277
|
-
* without a terminal), then `✓ Verified j***@acme.com`. No address is sent
|
|
1278
|
-
* before one is entered, and cf is not touched before it is verified.
|
|
1279
|
-
*/
|
|
1280
|
+
/** Use the live signed-in identity, or verify an address when no matching login is available. */
|
|
1280
1281
|
async function verifyRequester(ctx, options, origin) {
|
|
1282
|
+
const client = new VerificationClient(clientOptions(ctx, origin));
|
|
1283
|
+
const configDir = options.configDir;
|
|
1284
|
+
const identity = configDir ? await busy(ctx, 'Using your Vigilbase identity', () => verifySignedInRequester({
|
|
1285
|
+
config: { ...ctx.config, configDir }, client, locale: emailLocale(ctx.env), email: options.email,
|
|
1286
|
+
warn: (message) => ctx.writer.stderr(`Warning: ${message}`),
|
|
1287
|
+
})) : null;
|
|
1288
|
+
if (identity) {
|
|
1289
|
+
if (ctx.tui)
|
|
1290
|
+
ctx.tui.step(`Signed in as ${identity.requester}`);
|
|
1291
|
+
else
|
|
1292
|
+
say(ctx, `✓ Signed in as ${identity.requester}`);
|
|
1293
|
+
return identity;
|
|
1294
|
+
}
|
|
1281
1295
|
const verified = await verifyEmail({
|
|
1282
|
-
client
|
|
1296
|
+
client,
|
|
1283
1297
|
io: promptIO(ctx),
|
|
1284
1298
|
locale: emailLocale(ctx.env),
|
|
1285
1299
|
...(options.email !== undefined ? { email: options.email } : {}),
|
|
@@ -1293,10 +1307,10 @@ async function verifyRequester(ctx, options, origin) {
|
|
|
1293
1307
|
return verified;
|
|
1294
1308
|
}
|
|
1295
1309
|
/**
|
|
1296
|
-
* The checkup the verified
|
|
1297
|
-
*
|
|
1298
|
-
*
|
|
1299
|
-
*
|
|
1310
|
+
* The checkup the verified requester started. An expired session is renewed
|
|
1311
|
+
* once, using a fresh identity proof or the email token and code still in
|
|
1312
|
+
* memory. vigilbase.com mints the next session code for the same checkup.
|
|
1313
|
+
* The snapshot file is kept either way.
|
|
1300
1314
|
*/
|
|
1301
1315
|
class CheckupSession {
|
|
1302
1316
|
ctx;
|
|
@@ -1323,7 +1337,7 @@ class CheckupSession {
|
|
|
1323
1337
|
}
|
|
1324
1338
|
catch (error) {
|
|
1325
1339
|
if (error instanceof LocalCheckupError && (error.code === 'VERIFICATION_EXPIRED' || error.code === 'VERIFICATION_INVALID')) {
|
|
1326
|
-
throw new LocalCheckupError('SESSION_EXPIRED', 'The checkup session expired, and
|
|
1340
|
+
throw new LocalCheckupError('SESSION_EXPIRED', 'The checkup session expired, and its identity verification can no longer renew it.', error.status !== undefined ? { status: error.status } : {});
|
|
1327
1341
|
}
|
|
1328
1342
|
throw error;
|
|
1329
1343
|
}
|
|
@@ -1365,7 +1379,7 @@ function noteSavedSnapshot(ctx, error, path, origin) {
|
|
|
1365
1379
|
if (ctx.tui)
|
|
1366
1380
|
ctx.tui.uploadLater(submit);
|
|
1367
1381
|
else
|
|
1368
|
-
ctx.writer.stderr(`The snapshot stays saved. To upload it later without collecting again, run ${submit} (it
|
|
1382
|
+
ctx.writer.stderr(`The snapshot stays saved. To upload it later without collecting again, run ${submit} (it checks your identity again).`);
|
|
1369
1383
|
}
|
|
1370
1384
|
/** Session checks that need no upload: CLI version, single use, and the bound account. */
|
|
1371
1385
|
function assertSessionUsable(session, accountId) {
|
|
@@ -1515,7 +1529,7 @@ async function runInspect(ctx, options) {
|
|
|
1515
1529
|
return ` ${kind.padEnd(34)} ${String(kindCounts.total).padStart(5)}${notOk ? ` (${notOk} not ok)` : ''}`;
|
|
1516
1530
|
}),
|
|
1517
1531
|
'Free text and identifiers in this file:',
|
|
1518
|
-
` member emails ${categories.memberEmails} · access-rule values ${categories.accessRuleValues} ·
|
|
1532
|
+
` member emails ${categories.memberEmails} · access-rule values ${categories.accessRuleValues} · DNS and certificate hostnames ${categories.dnsHostnames} · pseudonymised IPs ${categories.pseudonymizedIps} · rule descriptions ${categories.ruleDescriptions}`,
|
|
1519
1533
|
'No credentials are included, and origin IPs are pseudonymised.',
|
|
1520
1534
|
];
|
|
1521
1535
|
for (const line of lines)
|
|
@@ -1635,6 +1649,10 @@ async function uploadLoaded(ctx, input) {
|
|
|
1635
1649
|
].filter((part) => part !== null);
|
|
1636
1650
|
const summary = `${result.replayed ? '✓ Already uploaded (same file); nothing changed.' : '✓ Uploaded.'} Free summary: ${parts.join(' · ')}`;
|
|
1637
1651
|
const emailed = result.resultsUrl !== undefined ? `A link has also been emailed to ${requester}.` : `A link to the results has been emailed to ${requester}.`;
|
|
1652
|
+
// The free checks the server sent (names and zone counts, never zone names) and its premium count.
|
|
1653
|
+
const report = readCheckupReport(result.report);
|
|
1654
|
+
// Appended after the lines above, which stay as they were for scripts.
|
|
1655
|
+
const more = report ? localResultPlainLines(report, { style: createStyles(false), symbols: UNICODE_SYMBOLS, color: false, columns: null }) : [];
|
|
1638
1656
|
if (ctx.tui) {
|
|
1639
1657
|
const opening = result.resultsUrl !== undefined && openResults(ctx, result.resultsUrl, origin);
|
|
1640
1658
|
ctx.tui.uploaded({
|
|
@@ -1643,7 +1661,8 @@ async function uploadLoaded(ctx, input) {
|
|
|
1643
1661
|
...(warnings !== undefined ? { warnings } : {}),
|
|
1644
1662
|
...(failedChecks !== undefined ? { failed: failedChecks } : {}),
|
|
1645
1663
|
...(result.resultsUrl !== undefined ? { resultsUrl: result.resultsUrl } : {}),
|
|
1646
|
-
|
|
1664
|
+
...(report ? { report } : {}),
|
|
1665
|
+
}, [summary, ...(result.resultsUrl !== undefined ? [`Results: ${result.resultsUrl}`] : []), emailed, ...more]);
|
|
1647
1666
|
}
|
|
1648
1667
|
else {
|
|
1649
1668
|
ctx.writer.stdout(summary);
|
|
@@ -1652,6 +1671,8 @@ async function uploadLoaded(ctx, input) {
|
|
|
1652
1671
|
openResults(ctx, result.resultsUrl, origin);
|
|
1653
1672
|
}
|
|
1654
1673
|
ctx.writer.stdout(emailed);
|
|
1674
|
+
for (const line of more)
|
|
1675
|
+
ctx.writer.stdout(line);
|
|
1655
1676
|
}
|
|
1656
1677
|
}
|
|
1657
1678
|
return {
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Cloudflare Checkup list caps the CLI prints, from `CHECKUP_REPORT_LIMITS`
|
|
3
|
+
* in packages/tools/src/pdf/cloudflare-checkup-report-limits.ts, so the
|
|
4
|
+
* terminal cuts long lists where the web report, the PDF and the emails do.
|
|
5
|
+
* The CLI is published to npm and cannot depend on the private
|
|
6
|
+
* `@vigilbase/tools` package; checkup-report-limits.test.ts keeps these values
|
|
7
|
+
* equal to the source.
|
|
8
|
+
*/
|
|
9
|
+
export declare const CLI_CHECKUP_LIMITS: {
|
|
10
|
+
/** Top priorities of `cf checkup result` and `cf checkup` (`priorities.cli`). */
|
|
11
|
+
readonly priorities: 8;
|
|
12
|
+
/** Their domain table rows (`cliDomains`). */
|
|
13
|
+
readonly domains: 15;
|
|
14
|
+
/** Top findings on the local checkup's result card (`priorities.cliCard`). */
|
|
15
|
+
readonly card: 5;
|
|
16
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Cloudflare Checkup list caps the CLI prints, from `CHECKUP_REPORT_LIMITS`
|
|
3
|
+
* in packages/tools/src/pdf/cloudflare-checkup-report-limits.ts, so the
|
|
4
|
+
* terminal cuts long lists where the web report, the PDF and the emails do.
|
|
5
|
+
* The CLI is published to npm and cannot depend on the private
|
|
6
|
+
* `@vigilbase/tools` package; checkup-report-limits.test.ts keeps these values
|
|
7
|
+
* equal to the source.
|
|
8
|
+
*/
|
|
9
|
+
export const CLI_CHECKUP_LIMITS = {
|
|
10
|
+
/** Top priorities of `cf checkup result` and `cf checkup` (`priorities.cli`). */
|
|
11
|
+
priorities: 8,
|
|
12
|
+
/** Their domain table rows (`cliDomains`). */
|
|
13
|
+
domains: 15,
|
|
14
|
+
/** Top findings on the local checkup's result card (`priorities.cliCard`). */
|
|
15
|
+
card: 5,
|
|
16
|
+
};
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { type Styles, type Symbols } from './tui.js';
|
|
2
|
+
export type CheckupCounts = {
|
|
3
|
+
total: number | null;
|
|
4
|
+
passed: number | null;
|
|
5
|
+
warnings: number | null;
|
|
6
|
+
failed: number | null;
|
|
7
|
+
skipped: number | null;
|
|
8
|
+
};
|
|
9
|
+
/** One check ID's failing or warning instances, across the account and its zones. */
|
|
10
|
+
export type CheckupFinding = {
|
|
11
|
+
name: string;
|
|
12
|
+
/** The server's label (`critical`, `high`, …), as sent. */
|
|
13
|
+
severity: string | null;
|
|
14
|
+
/** `failed` (fail or error) or `warning`; null for a legacy report's issue, which has no status. */
|
|
15
|
+
status: 'failed' | 'warning' | null;
|
|
16
|
+
/** Distinct zones with this finding. */
|
|
17
|
+
zones: number;
|
|
18
|
+
/** An account-level check. */
|
|
19
|
+
account: boolean;
|
|
20
|
+
/** The first line of the server's recommendation. */
|
|
21
|
+
recommendation: string | null;
|
|
22
|
+
};
|
|
23
|
+
export type CheckupZoneRow = {
|
|
24
|
+
name: string;
|
|
25
|
+
plan: string | null;
|
|
26
|
+
status: string | null;
|
|
27
|
+
grade: string | null;
|
|
28
|
+
score: number | null;
|
|
29
|
+
passed: number | null;
|
|
30
|
+
warnings: number | null;
|
|
31
|
+
failed: number | null;
|
|
32
|
+
/** A legacy report's issue count (it has no pass/warning/fail summary). */
|
|
33
|
+
issues: number | null;
|
|
34
|
+
};
|
|
35
|
+
export type CheckupReportView = {
|
|
36
|
+
accountName: string | null;
|
|
37
|
+
zoneCount: number | null;
|
|
38
|
+
scannedZones: number | null;
|
|
39
|
+
grade: string | null;
|
|
40
|
+
score: number | null;
|
|
41
|
+
summary: CheckupCounts | null;
|
|
42
|
+
zones: CheckupZoneRow[];
|
|
43
|
+
/** Ordered: failed before warnings, then by the server's severity, then the widest. */
|
|
44
|
+
findings: CheckupFinding[];
|
|
45
|
+
/** Premium checks the locked projection holds back. */
|
|
46
|
+
lockedPremiumCount: number;
|
|
47
|
+
/** The results page, only when it is an https://vigilbase.com URL. */
|
|
48
|
+
shareableUrl: string | null;
|
|
49
|
+
scannedAt: string | null;
|
|
50
|
+
};
|
|
51
|
+
/**
|
|
52
|
+
* The view of a checkup result (`CheckupResult`, its locked projection, or a
|
|
53
|
+
* legacy One report), or null when `value` is not an object. Malformed parts
|
|
54
|
+
* are left out rather than failing the whole view.
|
|
55
|
+
*/
|
|
56
|
+
export declare function readCheckupReport(value: unknown): CheckupReportView | null;
|
|
57
|
+
/** How the lines are drawn: styles (identity without colour), symbols (ASCII on legacy consoles) and the width to fit, if any. */
|
|
58
|
+
export type ReportPalette = {
|
|
59
|
+
style: Styles;
|
|
60
|
+
symbols: Symbols;
|
|
61
|
+
/** Colours on: the bar shows warnings and failed checks in their colours. */
|
|
62
|
+
color: boolean;
|
|
63
|
+
/** Terminal columns; null for plain output (pipes, files), which is never cut. */
|
|
64
|
+
columns: number | null;
|
|
65
|
+
};
|
|
66
|
+
export declare function joinParts(palette: ReportPalette, parts: readonly string[]): string;
|
|
67
|
+
/** `account-wide`, `on 2 domains`, or '' (a legacy issue without a zone list). */
|
|
68
|
+
export declare function findingScope(finding: CheckupFinding): string;
|
|
69
|
+
/** A grade in the colour of its band, bold: A green, B and C yellow, D and F red (the server's letter, coloured). */
|
|
70
|
+
export declare function styleGrade(style: Styles, grade: string): string;
|
|
71
|
+
/**
|
|
72
|
+
* Cells per segment for a stacked bar: proportional, summing to `width`, and
|
|
73
|
+
* at least one cell for every non-zero count while the width allows.
|
|
74
|
+
*/
|
|
75
|
+
export declare function segmentWidths(values: readonly number[], width: number): number[];
|
|
76
|
+
/**
|
|
77
|
+
* `██████░░`: passed, then warnings and failed (yellow and red with colour),
|
|
78
|
+
* then checks with any other status. Without colour, everything but passed is
|
|
79
|
+
* the empty cell, so the bar still reads as the share that passed.
|
|
80
|
+
*/
|
|
81
|
+
export declare function checksBar(palette: ReportPalette, summary: CheckupCounts, width: number): string | null;
|
|
82
|
+
/**
|
|
83
|
+
* Finding lines, aligned: `✗ critical Account MFA Enforcement account-wide`.
|
|
84
|
+
* `indent` prefixes every line; with `recommendations`, the first line of the
|
|
85
|
+
* recommendation follows each finding, dim, under its name.
|
|
86
|
+
*/
|
|
87
|
+
export declare function findingLines(palette: ReportPalette, findings: readonly CheckupFinding[], options: {
|
|
88
|
+
indent: string;
|
|
89
|
+
recommendations?: boolean;
|
|
90
|
+
}): string[];
|
|
91
|
+
/** `+87 more checks in the full report` */
|
|
92
|
+
export declare function lockedTeaser(palette: ReportPalette, locked: number): string | null;
|
|
93
|
+
/** Findings shown at the end of a local checkup. */
|
|
94
|
+
export declare const LOCAL_FINDINGS_SHOWN: 5;
|
|
95
|
+
/**
|
|
96
|
+
* The result card of the terminal UI, under `└ ✓ Done. …`: the free checks as
|
|
97
|
+
* a bar, the top findings (failed, then warnings; names and zone counts, never
|
|
98
|
+
* zone names) and how many premium checks the full report adds. Lines are
|
|
99
|
+
* indented to the end of the timeline and fit the width.
|
|
100
|
+
*/
|
|
101
|
+
export declare function localResultCard(palette: ReportPalette, view: CheckupReportView): string[];
|
|
102
|
+
/**
|
|
103
|
+
* The same, as plain lines appended after the plain result lines:
|
|
104
|
+
* `Top findings:`, one indented line each, then the premium teaser. Empty when
|
|
105
|
+
* there is nothing to add.
|
|
106
|
+
*/
|
|
107
|
+
export declare function localResultPlainLines(view: CheckupReportView, palette: ReportPalette): string[];
|
|
108
|
+
/** Priorities shown by `cf checkup result` and `cf checkup`. */
|
|
109
|
+
export declare const PRIORITIES_SHOWN: 8;
|
|
110
|
+
/** Domain rows shown before `… N more domains`. */
|
|
111
|
+
export declare const ZONES_SHOWN: 15;
|
|
112
|
+
/**
|
|
113
|
+
* The body of `cf checkup result` and `cf checkup` in text mode, after their
|
|
114
|
+
* `checkup` and `grade` lines: the account, the counts (a bar on a terminal),
|
|
115
|
+
* the domains, the top priorities with the first line of each recommendation,
|
|
116
|
+
* the premium teaser of a locked result and the full report link.
|
|
117
|
+
*/
|
|
118
|
+
export declare function checkupReportLines(palette: ReportPalette, view: CheckupReportView): string[];
|