@vigilbase/cli 0.2.2 → 0.2.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/CLOUDFLARE.md +2 -2
- package/COMMANDS.md +128 -42
- package/LOCAL.md +99 -38
- package/ONE.md +1 -1
- package/README.md +217 -66
- package/SECURITY.md +1 -1
- package/dist/auth/acl.d.ts +6 -0
- package/dist/auth/acl.js +45 -0
- package/dist/auth/device-auth.d.ts +3 -0
- package/dist/auth/device-auth.js +13 -3
- package/dist/auth/provider.js +4 -4
- package/dist/auth/refresh.d.ts +5 -4
- package/dist/auth/refresh.js +82 -41
- package/dist/auth/store.d.ts +25 -0
- package/dist/auth/store.js +91 -11
- package/dist/bin.js +13 -1
- package/dist/cli.js +205 -81
- package/dist/commands/account.js +11 -7
- package/dist/commands/auth.d.ts +6 -0
- package/dist/commands/auth.js +75 -24
- package/dist/commands/billing.js +2 -2
- package/dist/commands/cloudflare.d.ts +1 -1
- package/dist/commands/cloudflare.js +31 -17
- package/dist/commands/helpers.js +3 -3
- package/dist/commands/index.d.ts +1 -0
- package/dist/commands/index.js +2 -1
- package/dist/commands/legacy-validation.d.ts +20 -0
- package/dist/commands/legacy-validation.js +77 -3
- package/dist/commands/local.js +186 -65
- package/dist/commands/one.js +16 -13
- package/dist/commands/public-tools.js +66 -35
- package/dist/commands/runs.js +5 -5
- package/dist/commands/security.d.ts +1 -1
- package/dist/commands/security.js +41 -37
- package/dist/commands/watchdog.js +5 -5
- package/dist/lib/banner.d.ts +5 -11
- package/dist/lib/banner.js +11 -31
- package/dist/lib/client.d.ts +9 -0
- package/dist/lib/client.js +31 -2
- package/dist/lib/cloudflare-client.d.ts +2 -2
- package/dist/lib/config.d.ts +16 -0
- package/dist/lib/config.js +77 -19
- package/dist/lib/format.d.ts +5 -1
- package/dist/lib/format.js +110 -35
- package/dist/lib/help.js +57 -6
- package/dist/lib/network.d.ts +12 -3
- package/dist/lib/network.js +73 -9
- package/dist/lib/one-client.d.ts +2 -0
- package/dist/lib/one-client.js +86 -12
- package/dist/lib/open-url.js +2 -1
- package/dist/lib/platform.d.ts +2 -0
- package/dist/lib/platform.js +5 -0
- package/dist/lib/progress.d.ts +18 -3
- package/dist/lib/progress.js +72 -10
- package/dist/lib/system-trust.d.ts +2 -0
- package/dist/lib/system-trust.js +6 -0
- package/dist/lib/tui.d.ts +1 -1
- package/dist/lib/tui.js +5 -7
- package/dist/local/cloudflare-checkup/cf/child-env.js +2 -4
- package/dist/local/cloudflare-checkup/cf/install.d.ts +4 -2
- package/dist/local/cloudflare-checkup/cf/install.js +55 -24
- package/dist/local/cloudflare-checkup/cf/locate.d.ts +2 -0
- package/dist/local/cloudflare-checkup/cf/locate.js +10 -1
- package/dist/local/cloudflare-checkup/cf/run-cf.d.ts +18 -3
- package/dist/local/cloudflare-checkup/cf/run-cf.js +75 -17
- package/dist/local/cloudflare-checkup/contract.d.ts +16 -9
- package/dist/local/cloudflare-checkup/contract.js +80 -14
- package/dist/local/cloudflare-checkup/errors.d.ts +34 -1
- package/dist/local/cloudflare-checkup/errors.js +36 -5
- package/dist/local/cloudflare-checkup/output.d.ts +6 -0
- package/dist/local/cloudflare-checkup/output.js +9 -0
- package/dist/local/cloudflare-checkup/snapshot-file.d.ts +6 -2
- package/dist/local/cloudflare-checkup/snapshot-file.js +25 -11
- package/dist/local/cloudflare-checkup/submit-client.js +7 -1
- package/dist/types.d.ts +6 -1
- package/esm-only.cjs +3 -0
- package/package.json +7 -4
package/dist/cli.js
CHANGED
|
@@ -6,79 +6,81 @@ import { renderBanner, shouldShowBanner } from './lib/banner.js';
|
|
|
6
6
|
import { createWriter, formatError } from './lib/format.js';
|
|
7
7
|
import { HELP_TAGLINE, HELP_TEXT, HELP_USAGE, helpForCommand, isCatalogTool } from './lib/help.js';
|
|
8
8
|
import { wasRendered } from './lib/tui.js';
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
9
|
+
import { isOneAuthRequiredTool } from './lib/one-client.js';
|
|
10
|
+
import { COMMAND_HANDLERS, getCommandHandler } from './commands/index.js';
|
|
11
|
+
import { CheckupCancelledError, LocalCheckupError } from './local/cloudflare-checkup/errors.js';
|
|
11
12
|
import { EXIT } from './types.js';
|
|
12
13
|
import { CLI_VERSION } from './version.js';
|
|
13
14
|
export { CLI_VERSION };
|
|
15
|
+
const OPTIONS = {
|
|
16
|
+
json: { type: 'boolean', short: 'j', default: false },
|
|
17
|
+
help: { type: 'boolean', short: 'h', default: false },
|
|
18
|
+
version: { type: 'boolean', short: 'V', default: false },
|
|
19
|
+
'base-url': { type: 'string' },
|
|
20
|
+
'one-url': { type: 'string' },
|
|
21
|
+
'auth-url': { type: 'string' },
|
|
22
|
+
'client-id': { type: 'string' },
|
|
23
|
+
timeout: { type: 'string' },
|
|
24
|
+
style: { type: 'string' },
|
|
25
|
+
'no-skill': { type: 'boolean', default: false },
|
|
26
|
+
via: { type: 'string' },
|
|
27
|
+
public: { type: 'boolean', default: false },
|
|
28
|
+
token: { type: 'string' },
|
|
29
|
+
'token-stdin': { type: 'boolean', default: false },
|
|
30
|
+
'no-open': { type: 'boolean', default: false },
|
|
31
|
+
email: { type: 'string' },
|
|
32
|
+
role: { type: 'string' },
|
|
33
|
+
confirm: { type: 'boolean', default: false },
|
|
34
|
+
'account-id': { type: 'string' },
|
|
35
|
+
tool: { type: 'string' },
|
|
36
|
+
limit: { type: 'string' },
|
|
37
|
+
'target-id': { type: 'string' },
|
|
38
|
+
'customer-id': { type: 'string' },
|
|
39
|
+
'zone-id': { type: 'string' },
|
|
40
|
+
'days': { type: 'string' },
|
|
41
|
+
'offset': { type: 'string' },
|
|
42
|
+
'status': { type: 'string' },
|
|
43
|
+
'severity': { type: 'string' },
|
|
44
|
+
'attack-category': { type: 'string' },
|
|
45
|
+
'order-by': { type: 'string' },
|
|
46
|
+
'order-direction': { type: 'string' },
|
|
47
|
+
'asset-id': { type: 'string' },
|
|
48
|
+
'provider': { type: 'string' },
|
|
49
|
+
'dataset': { type: 'string' },
|
|
50
|
+
'search': { type: 'string' },
|
|
51
|
+
'start': { type: 'string' },
|
|
52
|
+
'end': { type: 'string' },
|
|
53
|
+
'sort': { type: 'string' },
|
|
54
|
+
'frs-min': { type: 'string' },
|
|
55
|
+
'before': { type: 'string' },
|
|
56
|
+
'note': { type: 'string' },
|
|
57
|
+
'cursor': { type: 'string' },
|
|
58
|
+
'action-prefix': { type: 'string' },
|
|
59
|
+
'outcome': { type: 'string' },
|
|
60
|
+
'hours': { type: 'string' },
|
|
61
|
+
'metrics': { type: 'string' },
|
|
62
|
+
'min-weight': { type: 'string' },
|
|
63
|
+
'range': { type: 'string' },
|
|
64
|
+
'region': { type: 'string' },
|
|
65
|
+
'issue-id': { type: 'string' },
|
|
66
|
+
'revision': { type: 'string' },
|
|
67
|
+
all: { type: 'boolean', default: false },
|
|
68
|
+
light: { type: 'boolean', default: false },
|
|
69
|
+
profile: { type: 'string' },
|
|
70
|
+
'cf-auth': { type: 'string' },
|
|
71
|
+
'cf-bin': { type: 'string' },
|
|
72
|
+
out: { type: 'string' },
|
|
73
|
+
overwrite: { type: 'boolean', default: false },
|
|
74
|
+
'dry-run': { type: 'boolean', default: false },
|
|
75
|
+
'max-requests-per-5min': { type: 'string' },
|
|
76
|
+
yes: { type: 'boolean', default: false },
|
|
77
|
+
};
|
|
14
78
|
function parseCli(argv) {
|
|
15
79
|
const { values, positionals } = parseArgs({
|
|
16
80
|
args: argv,
|
|
17
81
|
allowPositionals: true,
|
|
18
82
|
strict: true,
|
|
19
|
-
options:
|
|
20
|
-
json: { type: 'boolean', short: 'j', default: false },
|
|
21
|
-
help: { type: 'boolean', short: 'h', default: false },
|
|
22
|
-
version: { type: 'boolean', short: 'V', default: false },
|
|
23
|
-
'base-url': { type: 'string' },
|
|
24
|
-
'one-url': { type: 'string' },
|
|
25
|
-
'auth-url': { type: 'string' },
|
|
26
|
-
'client-id': { type: 'string' },
|
|
27
|
-
timeout: { type: 'string' },
|
|
28
|
-
style: { type: 'string' },
|
|
29
|
-
'no-skill': { type: 'boolean', default: false },
|
|
30
|
-
via: { type: 'string' },
|
|
31
|
-
public: { type: 'boolean', default: false },
|
|
32
|
-
token: { type: 'string' },
|
|
33
|
-
'token-stdin': { type: 'boolean', default: false },
|
|
34
|
-
'no-open': { type: 'boolean', default: false },
|
|
35
|
-
email: { type: 'string' },
|
|
36
|
-
role: { type: 'string' },
|
|
37
|
-
confirm: { type: 'boolean', default: false },
|
|
38
|
-
'account-id': { type: 'string' },
|
|
39
|
-
tool: { type: 'string' },
|
|
40
|
-
limit: { type: 'string' },
|
|
41
|
-
'target-id': { type: 'string' },
|
|
42
|
-
'customer-id': { type: 'string' },
|
|
43
|
-
'zone-id': { type: 'string' },
|
|
44
|
-
'days': { type: 'string' },
|
|
45
|
-
'offset': { type: 'string' },
|
|
46
|
-
'status': { type: 'string' },
|
|
47
|
-
'severity': { type: 'string' },
|
|
48
|
-
'attack-category': { type: 'string' },
|
|
49
|
-
'order-by': { type: 'string' },
|
|
50
|
-
'order-direction': { type: 'string' },
|
|
51
|
-
'asset-id': { type: 'string' },
|
|
52
|
-
'provider': { type: 'string' },
|
|
53
|
-
'dataset': { type: 'string' },
|
|
54
|
-
'search': { type: 'string' },
|
|
55
|
-
'start': { type: 'string' },
|
|
56
|
-
'end': { type: 'string' },
|
|
57
|
-
'sort': { type: 'string' },
|
|
58
|
-
'frs-min': { type: 'string' },
|
|
59
|
-
'before': { type: 'string' },
|
|
60
|
-
'note': { type: 'string' },
|
|
61
|
-
'cursor': { type: 'string' },
|
|
62
|
-
'action-prefix': { type: 'string' },
|
|
63
|
-
'outcome': { type: 'string' },
|
|
64
|
-
'hours': { type: 'string' },
|
|
65
|
-
'metrics': { type: 'string' },
|
|
66
|
-
'min-weight': { type: 'string' },
|
|
67
|
-
'range': { type: 'string' },
|
|
68
|
-
'region': { type: 'string' },
|
|
69
|
-
'issue-id': { type: 'string' },
|
|
70
|
-
'revision': { type: 'string' },
|
|
71
|
-
all: { type: 'boolean', default: false },
|
|
72
|
-
light: { type: 'boolean', default: false },
|
|
73
|
-
profile: { type: 'string' },
|
|
74
|
-
'cf-auth': { type: 'string' },
|
|
75
|
-
'cf-bin': { type: 'string' },
|
|
76
|
-
out: { type: 'string' },
|
|
77
|
-
overwrite: { type: 'boolean', default: false },
|
|
78
|
-
'dry-run': { type: 'boolean', default: false },
|
|
79
|
-
'max-requests-per-5min': { type: 'string' },
|
|
80
|
-
yes: { type: 'boolean', default: false },
|
|
81
|
-
},
|
|
83
|
+
options: OPTIONS,
|
|
82
84
|
});
|
|
83
85
|
return {
|
|
84
86
|
positionals,
|
|
@@ -161,6 +163,111 @@ function parseTimeout(raw) {
|
|
|
161
163
|
function resolveMode(jsonFlag) {
|
|
162
164
|
return jsonFlag ? 'json' : 'text';
|
|
163
165
|
}
|
|
166
|
+
/** Arguments before `--`: everything after it is a positional. */
|
|
167
|
+
function optionArguments(argv) {
|
|
168
|
+
const end = argv.indexOf('--');
|
|
169
|
+
return end === -1 ? argv : argv.slice(0, end);
|
|
170
|
+
}
|
|
171
|
+
/** JSON mode for an error from parsing itself: --json, --json=… or a short-flag cluster with j. */
|
|
172
|
+
function requestsJson(argv) {
|
|
173
|
+
return optionArguments(argv).some((arg) => /^--json(=|$)/.test(arg) || /^-[A-Za-z]*j[A-Za-z]*$/.test(arg));
|
|
174
|
+
}
|
|
175
|
+
/** Edit distance counting a swap of two neighbouring letters as one edit. */
|
|
176
|
+
function editDistance(a, b) {
|
|
177
|
+
const d = Array.from({ length: a.length + 1 }, (_, i) => Array.from({ length: b.length + 1 }, (_, j) => (i ? (j ? 0 : i) : j)));
|
|
178
|
+
for (let i = 1; i <= a.length; i++) {
|
|
179
|
+
for (let j = 1; j <= b.length; j++) {
|
|
180
|
+
d[i][j] = Math.min(d[i - 1][j] + 1, d[i][j - 1] + 1, d[i - 1][j - 1] + (a[i - 1] === b[j - 1] ? 0 : 1));
|
|
181
|
+
if (i > 1 && j > 1 && a[i - 1] === b[j - 2] && a[i - 2] === b[j - 1])
|
|
182
|
+
d[i][j] = Math.min(d[i][j], d[i - 2][j - 2] + 1);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
return d[a.length][b.length];
|
|
186
|
+
}
|
|
187
|
+
/** The nearest candidate when it is close enough to be a typo: one edit for short words, two from six letters. */
|
|
188
|
+
function didYouMean(input, candidates) {
|
|
189
|
+
let best;
|
|
190
|
+
let bestDistance = Math.min(2, Math.max(1, Math.floor(input.length / 3))) + 1;
|
|
191
|
+
for (const candidate of candidates) {
|
|
192
|
+
const distance = editDistance(input, candidate);
|
|
193
|
+
if (distance < bestDistance)
|
|
194
|
+
[best, bestDistance] = [candidate, distance];
|
|
195
|
+
}
|
|
196
|
+
return best;
|
|
197
|
+
}
|
|
198
|
+
/** Commands with subcommands, whose help covers them all. */
|
|
199
|
+
const COMMAND_GROUPS = new Set(['account', 'billing', 'runs', 'runhistory', 'cf', 'cloudflare', 'watchdog', 'security',
|
|
200
|
+
'customers', 'orgs', 'monitoring', 'support', 'local', 'cloudflare-checkup']);
|
|
201
|
+
const USAGE_POINTER = 'Run `vigilbase help` for usage.';
|
|
202
|
+
function cliError(message, code) {
|
|
203
|
+
return Object.assign(new Error(message), { code });
|
|
204
|
+
}
|
|
205
|
+
function unknownCommand(typed) {
|
|
206
|
+
const name = typed.toLowerCase();
|
|
207
|
+
const command = didYouMean(name, [...Object.keys(COMMAND_HANDLERS), 'help']);
|
|
208
|
+
const suggestion = command ?? (name === 'version' || name === 'json' ? `--${name}` : undefined);
|
|
209
|
+
return new Error(`Unknown command "${typed}".${suggestion ? ` Did you mean ${suggestion}?` : ''} ${USAGE_POINTER}`);
|
|
210
|
+
}
|
|
211
|
+
function unknownOption(argv, reported) {
|
|
212
|
+
const longNames = Object.keys(OPTIONS);
|
|
213
|
+
let name = reported;
|
|
214
|
+
let suggestion;
|
|
215
|
+
// parseArgs reads -help as -h -e -l -p: name the whole word and suggest the long option.
|
|
216
|
+
const word = optionArguments(argv).find((arg) => /^-[^-]{2,}$/.test(arg) && arg.includes(reported.slice(1)) &&
|
|
217
|
+
didYouMean(arg.slice(1), longNames));
|
|
218
|
+
if (word) {
|
|
219
|
+
name = word;
|
|
220
|
+
suggestion = `--${didYouMean(word.slice(1), longNames)}`;
|
|
221
|
+
}
|
|
222
|
+
else if (reported.startsWith('--')) {
|
|
223
|
+
const option = didYouMean(reported.slice(2).split('=')[0], longNames);
|
|
224
|
+
if (option)
|
|
225
|
+
suggestion = `--${option}`;
|
|
226
|
+
}
|
|
227
|
+
else {
|
|
228
|
+
const letter = reported.slice(1);
|
|
229
|
+
const short = Object.values(OPTIONS).find((option) => 'short' in option && option.short.toLowerCase() === letter.toLowerCase());
|
|
230
|
+
if (short && 'short' in short)
|
|
231
|
+
suggestion = `-${short.short}`;
|
|
232
|
+
}
|
|
233
|
+
return cliError(`Unknown option ${name}.${suggestion ? ` Did you mean ${suggestion}?` : ''} ${USAGE_POINTER}`, 'UNKNOWN_OPTION');
|
|
234
|
+
}
|
|
235
|
+
/** node:util parseArgs errors in the CLI's words, with the CLI's own codes. */
|
|
236
|
+
function argumentError(error, argv) {
|
|
237
|
+
const code = error && typeof error === 'object' && 'code' in error ? error.code : undefined;
|
|
238
|
+
if (typeof code !== 'string' || !code.startsWith('ERR_PARSE_ARGS_'))
|
|
239
|
+
return error;
|
|
240
|
+
const message = error instanceof Error ? error.message : '';
|
|
241
|
+
if (code === 'ERR_PARSE_ARGS_UNKNOWN_OPTION')
|
|
242
|
+
return unknownOption(argv, /'([^']+)'/.exec(message)?.[1] ?? '');
|
|
243
|
+
const flag = /--[a-z0-9-]+/.exec(message)?.[0];
|
|
244
|
+
if (!flag)
|
|
245
|
+
return cliError(`Invalid arguments. ${USAGE_POINTER}`, 'INVALID_ARGUMENT');
|
|
246
|
+
if (message.includes('does not take an argument'))
|
|
247
|
+
return cliError(`${flag} does not take a value.`, 'INVALID_ARGUMENT');
|
|
248
|
+
if (message.includes('argument missing'))
|
|
249
|
+
return cliError(`${flag} needs a value.`, 'INVALID_ARGUMENT');
|
|
250
|
+
if (message.includes('ambiguous')) {
|
|
251
|
+
return cliError(`${flag} needs a value. To pass one that starts with a dash, use ${flag}=<value>.`, 'INVALID_ARGUMENT');
|
|
252
|
+
}
|
|
253
|
+
return cliError(`Invalid value for ${flag}. ${USAGE_POINTER}`, 'INVALID_ARGUMENT');
|
|
254
|
+
}
|
|
255
|
+
/** Public tool commands: they contact One only to run a tool through it. */
|
|
256
|
+
const PUBLIC_TOOL_COMMANDS = new Set(['list', 'tools', 'run', 'headers', 'email', 'describe', 'skill', 'badge']);
|
|
257
|
+
/**
|
|
258
|
+
* The origins a command contacts, so settings for the others can't fail it. Doctor checks both. `list` reads a
|
|
259
|
+
* saved sign-in offline and only warns about one it can't use.
|
|
260
|
+
*/
|
|
261
|
+
function originsUsed(command, rest, values) {
|
|
262
|
+
if (!PUBLIC_TOOL_COMMANDS.has(command))
|
|
263
|
+
return { public: command === 'doctor', one: true };
|
|
264
|
+
if (command !== 'run' && command !== 'headers' && command !== 'email')
|
|
265
|
+
return { public: true, one: false };
|
|
266
|
+
const toolName = command === 'headers' ? 'security-headers' : command === 'email' ? 'email-security' : rest[0] ?? '';
|
|
267
|
+
const isForcedPublic = values.isPublic || values.via === 'public';
|
|
268
|
+
const isViaOne = !isForcedPublic && (values.via === 'one' || isOneAuthRequiredTool(toolName));
|
|
269
|
+
return { public: !isViaOne, one: isViaOne };
|
|
270
|
+
}
|
|
164
271
|
export async function runCli(options = {}) {
|
|
165
272
|
const argv = options.argv ?? process.argv.slice(2);
|
|
166
273
|
const stdoutStream = options.stdout ?? process.stdout;
|
|
@@ -169,10 +276,20 @@ export async function runCli(options = {}) {
|
|
|
169
276
|
if (options.env) {
|
|
170
277
|
process.env = options.env;
|
|
171
278
|
}
|
|
279
|
+
let mode;
|
|
172
280
|
try {
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
281
|
+
let parsed;
|
|
282
|
+
try {
|
|
283
|
+
parsed = parseCli(argv);
|
|
284
|
+
}
|
|
285
|
+
catch (error) {
|
|
286
|
+
throw argumentError(error, argv);
|
|
287
|
+
}
|
|
288
|
+
mode = resolveMode(parsed.values.isJson);
|
|
289
|
+
// Commands are matched in any case, as subcommands are.
|
|
290
|
+
const [typed, ...rest] = parsed.positionals;
|
|
291
|
+
const command = typed?.toLowerCase();
|
|
292
|
+
if (parsed.values.isVersion || parsed.values.isHelp || !command || command === 'help') {
|
|
176
293
|
const informational = new Set(['isJson', 'isHelp', 'isVersion', 'base-url', 'one-url', 'auth-url', 'client-id', 'timeout']);
|
|
177
294
|
for (const [key, value] of Object.entries(parsed.values)) {
|
|
178
295
|
if (value === undefined || value === false || informational.has(key))
|
|
@@ -182,7 +299,8 @@ export async function runCli(options = {}) {
|
|
|
182
299
|
}
|
|
183
300
|
if (parsed.values.isVersion && parsed.positionals.length)
|
|
184
301
|
throw new Error('Usage: vigilbase --version');
|
|
185
|
-
|
|
302
|
+
// `help <group> <subcommand>` shows the group's help, as `<group> <subcommand> --help` does.
|
|
303
|
+
if (command === 'help' && rest.length > 1 && !COMMAND_GROUPS.has(rest[0].toLowerCase()))
|
|
186
304
|
throw new Error(HELP_USAGE);
|
|
187
305
|
}
|
|
188
306
|
if (parsed.values.isVersion) {
|
|
@@ -201,15 +319,16 @@ export async function runCli(options = {}) {
|
|
|
201
319
|
}
|
|
202
320
|
return EXIT.OK;
|
|
203
321
|
}
|
|
204
|
-
|
|
205
|
-
const
|
|
322
|
+
// `help --help` is help's own help, like `help help`.
|
|
323
|
+
const helpTyped = command === 'help' ? rest[0] ?? (parsed.values.isHelp ? 'help' : undefined) : parsed.values.isHelp ? typed : undefined;
|
|
324
|
+
const helpCommand = helpTyped?.toLowerCase();
|
|
206
325
|
if (parsed.values.isHelp ||
|
|
207
326
|
!command ||
|
|
208
327
|
command === 'help' ||
|
|
209
328
|
command === '--help') {
|
|
210
329
|
const specific = helpCommand ? helpForCommand(helpCommand) : null;
|
|
211
330
|
if (helpCommand && !specific) {
|
|
212
|
-
throw
|
|
331
|
+
throw unknownCommand(helpTyped);
|
|
213
332
|
}
|
|
214
333
|
if (mode === 'json') {
|
|
215
334
|
writer.stdout(JSON.stringify({
|
|
@@ -223,13 +342,21 @@ export async function runCli(options = {}) {
|
|
|
223
342
|
writer.stdout(specific);
|
|
224
343
|
}
|
|
225
344
|
else {
|
|
226
|
-
|
|
345
|
+
// Like --version, pipes and files get no banner.
|
|
346
|
+
const prefix = shouldShowBanner(mode) && stdoutStream.isTTY
|
|
227
347
|
? `${renderBanner({ version: CLI_VERSION, tagline: HELP_TAGLINE })}\n\n`
|
|
228
348
|
: '';
|
|
229
349
|
writer.stdout(`${prefix}${HELP_TEXT}`);
|
|
230
350
|
}
|
|
231
351
|
return EXIT.OK;
|
|
232
352
|
}
|
|
353
|
+
const handler = getCommandHandler(command);
|
|
354
|
+
if (!handler) {
|
|
355
|
+
// Tools are not commands of their own (no aliases): point to how they run.
|
|
356
|
+
throw isCatalogTool(command)
|
|
357
|
+
? new Error(`Unknown command "${typed}". Run the tool with \`vigilbase run ${command} <target>\`.`)
|
|
358
|
+
: unknownCommand(typed);
|
|
359
|
+
}
|
|
233
360
|
// Local checkups validate --base-url themselves (resolveSubmitOrigin) and ignore VIGILBASE_* URLs.
|
|
234
361
|
const config = command === 'local' || command === 'cloudflare-checkup'
|
|
235
362
|
? createLocalCommandConfig(parseTimeout(parsed.values.timeout))
|
|
@@ -239,15 +366,9 @@ export async function runCli(options = {}) {
|
|
|
239
366
|
identityBaseUrl: parsed.values['auth-url'],
|
|
240
367
|
clientId: parsed.values['client-id'],
|
|
241
368
|
timeoutMs: parseTimeout(parsed.values.timeout),
|
|
369
|
+
checkOrigins: originsUsed(command, rest, parsed.values),
|
|
242
370
|
});
|
|
243
371
|
const publicClient = new VigilbaseClient(config);
|
|
244
|
-
const handler = getCommandHandler(command);
|
|
245
|
-
if (!handler) {
|
|
246
|
-
// Tools are not commands of their own (no aliases): point to how they run.
|
|
247
|
-
throw new Error(isCatalogTool(command)
|
|
248
|
-
? `Unknown command "${command}". Run the tool with \`vigilbase run ${command} <target>\`.`
|
|
249
|
-
: `Unknown command "${command}". Run \`vigilbase help\` for usage.`);
|
|
250
|
-
}
|
|
251
372
|
return await handler({
|
|
252
373
|
command,
|
|
253
374
|
rest,
|
|
@@ -264,7 +385,7 @@ export async function runCli(options = {}) {
|
|
|
264
385
|
});
|
|
265
386
|
}
|
|
266
387
|
catch (error) {
|
|
267
|
-
|
|
388
|
+
mode ??= requestsJson(argv) ? 'json' : 'text';
|
|
268
389
|
// The terminal UI already showed it (■ and the hint) in its timeline.
|
|
269
390
|
if (mode === 'json' || !wasRendered(error))
|
|
270
391
|
formatError(error, mode, writer);
|
|
@@ -274,6 +395,9 @@ export async function runCli(options = {}) {
|
|
|
274
395
|
if (error instanceof LocalCheckupError && error.isRateLimited) {
|
|
275
396
|
return EXIT.RATE_LIMITED;
|
|
276
397
|
}
|
|
398
|
+
// A cf run or install stopped by a signal: 128 + the signal number.
|
|
399
|
+
if (error instanceof CheckupCancelledError)
|
|
400
|
+
return error.exitCode;
|
|
277
401
|
return EXIT.ERROR;
|
|
278
402
|
}
|
|
279
403
|
finally {
|
package/dist/commands/account.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { InvalidArgumentError, invitationValue, unknownSubcommand, validateArguments } from './legacy-validation.js';
|
|
2
2
|
import { asArray, asRecord, ok, outputJson, outputLines, requireOneClient, withSpinner, } from './helpers.js';
|
|
3
3
|
import { handleBilling } from './billing.js';
|
|
4
4
|
export const handleAccount = async (ctx) => {
|
|
@@ -16,19 +16,23 @@ export const handleAccount = async (ctx) => {
|
|
|
16
16
|
if (action === 'list' || action === 'ls')
|
|
17
17
|
validateArguments(ctx, ctx.rest[1] === undefined ? 1 : 2);
|
|
18
18
|
else if (action === 'create' || action === 'add') {
|
|
19
|
+
// The email may be positional or --email; help and both errors show the same usage.
|
|
20
|
+
const usage = 'Usage: vigilbase account invites create <email>|--email <email> [--role <role>] --confirm';
|
|
19
21
|
if (ctx.values.email !== undefined && ctx.rest[2] !== undefined)
|
|
20
|
-
throw new
|
|
21
|
-
|
|
22
|
-
|
|
22
|
+
throw new InvalidArgumentError(`Give the email once, as <email> or --email <email>. ${usage}`);
|
|
23
|
+
if (!(ctx.values.email ?? ctx.rest[2] ?? '').trim())
|
|
24
|
+
throw new Error(`Missing <email>. ${usage}`);
|
|
25
|
+
validateArguments(ctx, ctx.values.email === undefined ? 3 : 2, ['email', 'role', 'isConfirm']);
|
|
26
|
+
invitation = invitationValue(ctx.values.email ?? ctx.rest[2], ctx.values.role);
|
|
23
27
|
if (!ctx.values.isConfirm)
|
|
24
28
|
throw new Error('Sending an organization invitation requires --confirm.');
|
|
25
29
|
}
|
|
26
30
|
else
|
|
27
|
-
throw
|
|
31
|
+
throw unknownSubcommand(`account ${subcommand}`, ctx.rest[1] ?? '');
|
|
28
32
|
}
|
|
29
33
|
else {
|
|
30
34
|
if (!['show', 'me', 'overview', 'services', 'service', 'org', 'organization', 'members', 'endpoints', 'endpoint', 'sessions', 'session'].includes(subcommand)) {
|
|
31
|
-
throw
|
|
35
|
+
throw unknownSubcommand('account', ctx.rest[0] ?? '');
|
|
32
36
|
}
|
|
33
37
|
validateArguments(ctx, ctx.rest[0] === undefined ? 0 : 1);
|
|
34
38
|
}
|
|
@@ -143,7 +147,7 @@ export const handleAccount = async (ctx) => {
|
|
|
143
147
|
const session = asRecord(raw);
|
|
144
148
|
if (!session)
|
|
145
149
|
continue;
|
|
146
|
-
ctx.writer.stdout(`${String(session.id)
|
|
150
|
+
ctx.writer.stdout(`${String(session.id)} ${String(session.ipAddress || '-')} ${String(session.createdAt || '')}`);
|
|
147
151
|
}
|
|
148
152
|
return ok();
|
|
149
153
|
}
|
package/dist/commands/auth.d.ts
CHANGED
|
@@ -1,4 +1,10 @@
|
|
|
1
1
|
import { type CommandHandler } from './helpers.js';
|
|
2
|
+
/**
|
|
3
|
+
* A bearer token from --token, VIGILBASE_ONE_TOKEN or --token-stdin, checked before any request: fetch refuses
|
|
4
|
+
* header values with other characters, which would read as a connection failure.
|
|
5
|
+
*/
|
|
6
|
+
export declare function validateBearerToken(token: string): string;
|
|
7
|
+
/** A piped token fails with the same messages as --token. Reading stops past 16 KiB, with room for a line ending. */
|
|
2
8
|
export declare function readTokenFromStdin(input?: NodeJS.ReadableStream): Promise<string>;
|
|
3
9
|
export declare const handleLogin: CommandHandler;
|
|
4
10
|
export declare const handleLogout: CommandHandler;
|
package/dist/commands/auth.js
CHANGED
|
@@ -1,13 +1,28 @@
|
|
|
1
1
|
import { readIdentityTokenMetadata } from '../auth/token-metadata.js';
|
|
2
2
|
import { discoverAuthProvider } from '../auth/provider.js';
|
|
3
|
-
import { revokeIdentityCredentials, revokeIdentityGrant, revokeSavedCredentials, withCredentialsLock } from '../auth/refresh.js';
|
|
4
|
-
import { checkCredentialsLocation, clearIdentityCredentials, clearOneCredentials, hasIgnoredIdentityCredentials, readReplacedIdentityGrant, readStoredCredentials } from '../auth/store.js';
|
|
3
|
+
import { boundIdentityCredentials, revokeIdentityCredentials, revokeIdentityGrant, revokeSavedCredentials, withCredentialsLock } from '../auth/refresh.js';
|
|
4
|
+
import { checkCredentialsLocation, clearIdentityCredentials, clearOneCredentials, hasCredentialsDirectory, hasIgnoredIdentityCredentials, readReplacedIdentityGrant, readStoredCredentials, savedForAnotherConfiguration, SignInEndedError } from '../auth/store.js';
|
|
5
5
|
import { getOneAccessToken, readCredentials, requestDeviceCode, waitForDeviceAuthorization, writeOneCredentials, } from '../auth/index.js';
|
|
6
6
|
import { VigilbaseOneClient, ONE_AUTH_REQUIRED_TOOLS } from '../lib/one-client.js';
|
|
7
7
|
import { VigilbaseApiError } from '../lib/client.js';
|
|
8
8
|
import { formatUserCode, isHeadlessEnvironment, openUrlInBrowser, resolveDeviceVerificationUrl, } from '../lib/open-url.js';
|
|
9
9
|
import { shouldShowProgress, startProgress } from '../lib/progress.js';
|
|
10
10
|
import { assertCommandFlags, ok, outputJson, outputLines, withSpinner, } from './helpers.js';
|
|
11
|
+
import { InvalidArgumentError, validateArguments } from './legacy-validation.js';
|
|
12
|
+
/**
|
|
13
|
+
* A bearer token from --token, VIGILBASE_ONE_TOKEN or --token-stdin, checked before any request: fetch refuses
|
|
14
|
+
* header values with other characters, which would read as a connection failure.
|
|
15
|
+
*/
|
|
16
|
+
export function validateBearerToken(token) {
|
|
17
|
+
if (!token)
|
|
18
|
+
throw new InvalidArgumentError('The token must not be empty.');
|
|
19
|
+
if (token.length > 16384)
|
|
20
|
+
throw new InvalidArgumentError('The token exceeds 16 KiB.');
|
|
21
|
+
if (!/^[\x21-\x7E]+$/.test(token))
|
|
22
|
+
throw new InvalidArgumentError('The token must be a single value of printable ASCII characters.');
|
|
23
|
+
return token;
|
|
24
|
+
}
|
|
25
|
+
/** A piped token fails with the same messages as --token. Reading stops past 16 KiB, with room for a line ending. */
|
|
11
26
|
export async function readTokenFromStdin(input = process.stdin) {
|
|
12
27
|
if ('isTTY' in input && input.isTTY) {
|
|
13
28
|
throw new Error('--token-stdin requires piped input. Pipe the token from your secret manager to vigilbase login --token-stdin.');
|
|
@@ -15,13 +30,21 @@ export async function readTokenFromStdin(input = process.stdin) {
|
|
|
15
30
|
let token = '';
|
|
16
31
|
for await (const chunk of input) {
|
|
17
32
|
token += Buffer.isBuffer(chunk) ? chunk.toString('utf8') : String(chunk);
|
|
18
|
-
if (Buffer.byteLength(token, 'utf8') >
|
|
19
|
-
throw new
|
|
33
|
+
if (Buffer.byteLength(token, 'utf8') > 16386)
|
|
34
|
+
throw new InvalidArgumentError('The token exceeds 16 KiB.');
|
|
20
35
|
}
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
36
|
+
return validateBearerToken(token.trim());
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Token and legacy logins revoke saved Identity grants before saving their session, and a grant bound to another
|
|
40
|
+
* One, issuer or client can't be revoked with this configuration. Stop before the token or device code is used.
|
|
41
|
+
*/
|
|
42
|
+
async function assertSavedGrantsReplaceable(ctx) {
|
|
43
|
+
const { identity, shared } = await readStoredCredentials(ctx.config, () => { });
|
|
44
|
+
const other = [identity, shared].find((saved) => saved?.version === 2 && saved.one && !boundIdentityCredentials(ctx.config, saved.one))?.one;
|
|
45
|
+
if (!other)
|
|
46
|
+
return;
|
|
47
|
+
throw new Error(`A saved Identity sign-in belongs to ${other.issuer} for ${other.oneBaseUrl ?? other.resource} (client ID ${other.clientId}). Pass the matching --auth-url, --one-url and --client-id, or log out with them first.`);
|
|
25
48
|
}
|
|
26
49
|
/**
|
|
27
50
|
* Revoke the Identity grant a new login replaces, before the new grant exists. Identity treats a revoked refresh
|
|
@@ -57,16 +80,16 @@ export const handleLogin = async (ctx) => {
|
|
|
57
80
|
// Fail on Windows or an unsafe credentials directory or file before validating a token or starting device
|
|
58
81
|
// authorization: once a device grant is approved, a failed save would leave it unsaved and unrevoked.
|
|
59
82
|
await checkCredentialsLocation(ctx.config);
|
|
60
|
-
if (ctx.values.isTokenStdin && (ctx.values.token !== undefined || ctx.env.VIGILBASE_ONE_TOKEN
|
|
83
|
+
if (ctx.values.isTokenStdin && (ctx.values.token !== undefined || ctx.env.VIGILBASE_ONE_TOKEN?.trim())) {
|
|
61
84
|
throw new Error('Use --token-stdin without --token or VIGILBASE_ONE_TOKEN.');
|
|
62
85
|
}
|
|
86
|
+
// An empty --token fails like any other invalid token; an empty VIGILBASE_ONE_TOKEN is unset.
|
|
63
87
|
const explicitToken = ctx.values.isTokenStdin
|
|
64
88
|
? await readTokenFromStdin(ctx.stdin)
|
|
65
|
-
: ctx.values.token
|
|
66
|
-
if (ctx.values.token !== undefined && !ctx.values.token.trim()) {
|
|
67
|
-
throw new Error('The --token value must not be empty.');
|
|
68
|
-
}
|
|
89
|
+
: ctx.values.token !== undefined ? validateBearerToken(ctx.values.token.trim()) : ctx.env.VIGILBASE_ONE_TOKEN?.trim() || '';
|
|
69
90
|
if (explicitToken) {
|
|
91
|
+
validateBearerToken(explicitToken);
|
|
92
|
+
await assertSavedGrantsReplaceable(ctx);
|
|
70
93
|
const oneClient = new VigilbaseOneClient(ctx.config, explicitToken);
|
|
71
94
|
let user;
|
|
72
95
|
const session = await withSpinner(ctx, 'Validating token', () => oneClient.getSession());
|
|
@@ -110,6 +133,8 @@ export const handleLogin = async (ctx) => {
|
|
|
110
133
|
ctx.config.authProvider = await discoverAuthProvider(ctx.config);
|
|
111
134
|
if (ctx.config.authProvider)
|
|
112
135
|
await retirePreviousIdentityGrant(ctx);
|
|
136
|
+
else
|
|
137
|
+
await assertSavedGrantsReplaceable(ctx);
|
|
113
138
|
const device = await withSpinner(ctx, 'Requesting device code', () => requestDeviceCode(ctx.config));
|
|
114
139
|
const verifyUrl = resolveDeviceVerificationUrl(ctx.config.authProvider?.issuer ?? ctx.config.oneBaseUrl, device);
|
|
115
140
|
const prettyCode = formatUserCode(device.user_code);
|
|
@@ -199,12 +224,14 @@ export const handleLogout = async (ctx) => {
|
|
|
199
224
|
if (ctx.rest.length)
|
|
200
225
|
throw new Error('Usage: vigilbase logout');
|
|
201
226
|
assertCommandFlags(ctx, []);
|
|
227
|
+
const warnings = [];
|
|
202
228
|
const cleared = await revokeSavedCredentials(ctx.config, (warning) => {
|
|
229
|
+
warnings.push(warning);
|
|
203
230
|
if (ctx.mode !== 'json')
|
|
204
231
|
ctx.writer.stderr(`Warning: ${warning}`);
|
|
205
232
|
});
|
|
206
233
|
if (ctx.mode === 'json') {
|
|
207
|
-
outputJson(ctx, { success: true, cleared });
|
|
234
|
+
outputJson(ctx, { success: true, cleared, warnings });
|
|
208
235
|
}
|
|
209
236
|
else if (cleared) {
|
|
210
237
|
ctx.writer.stdout('Signed out. Local One credentials removed.');
|
|
@@ -214,12 +241,28 @@ export const handleLogout = async (ctx) => {
|
|
|
214
241
|
}
|
|
215
242
|
return ok();
|
|
216
243
|
};
|
|
244
|
+
/** "One <origin>, Identity <issuer>, client ID <id>" for a sign-in saved for another configuration. */
|
|
245
|
+
function describeSavedFor(savedFor) {
|
|
246
|
+
return [`One ${savedFor.oneBaseUrl}`, ...(savedFor.issuer ? [`Identity ${savedFor.issuer}`] : []), ...(savedFor.clientId ? [`client ID ${savedFor.clientId}`] : [])].join(', ');
|
|
247
|
+
}
|
|
217
248
|
export const handleWhoami = async (ctx) => {
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
const legacyIdentityFileIgnored = await hasIgnoredIdentityCredentials(ctx.config);
|
|
222
|
-
const
|
|
249
|
+
validateArguments(ctx, 0);
|
|
250
|
+
// Like list and doctor, a directory that fails the checks with no credentials in it is signed out.
|
|
251
|
+
const hasDirectory = await hasCredentialsDirectory(ctx.config);
|
|
252
|
+
const legacyIdentityFileIgnored = hasDirectory && await hasIgnoredIdentityCredentials(ctx.config);
|
|
253
|
+
const savedFor = hasDirectory ? await savedForAnotherConfiguration(ctx.config, await readCredentials(ctx.config)) : null;
|
|
254
|
+
let accessToken = null;
|
|
255
|
+
let signInEnded = false;
|
|
256
|
+
try {
|
|
257
|
+
if (hasDirectory)
|
|
258
|
+
accessToken = await getOneAccessToken(ctx.config);
|
|
259
|
+
}
|
|
260
|
+
catch (error) {
|
|
261
|
+
// Identity ended the grant and its file was removed: signed out, which whoami reports as success.
|
|
262
|
+
if (!(error instanceof SignInEndedError))
|
|
263
|
+
throw error;
|
|
264
|
+
signInEnded = true;
|
|
265
|
+
}
|
|
223
266
|
let liveUser = null;
|
|
224
267
|
if (accessToken) {
|
|
225
268
|
try {
|
|
@@ -233,16 +276,18 @@ export const handleWhoami = async (ctx) => {
|
|
|
233
276
|
}
|
|
234
277
|
}
|
|
235
278
|
catch (error) {
|
|
236
|
-
// A rejected session is signed out
|
|
237
|
-
// must remain errors rather than claiming the cached session is valid.
|
|
238
|
-
if (
|
|
279
|
+
// A rejected session is signed out, and so is a grant Identity ended when the 401 retry refreshed it.
|
|
280
|
+
// Connectivity and server failures must remain errors rather than claiming the cached session is valid.
|
|
281
|
+
if (error instanceof SignInEndedError)
|
|
282
|
+
signInEnded = true;
|
|
283
|
+
else if (!(error instanceof VigilbaseApiError) || ![401, 403].includes(error.status)) {
|
|
239
284
|
throw error;
|
|
240
285
|
}
|
|
241
286
|
}
|
|
242
287
|
}
|
|
243
288
|
const signedIn = Boolean(liveUser);
|
|
244
289
|
// Read after the token and session calls: either may have refreshed the sign-in, which saves a new expiry and scope.
|
|
245
|
-
const credentials = await readCredentials(ctx.config);
|
|
290
|
+
const credentials = signedIn ? await readCredentials(ctx.config) : { one: null };
|
|
246
291
|
const payload = {
|
|
247
292
|
authenticated: signedIn,
|
|
248
293
|
oneBaseUrl: ctx.config.oneBaseUrl,
|
|
@@ -250,6 +295,8 @@ export const handleWhoami = async (ctx) => {
|
|
|
250
295
|
clientId: credentials.one?.clientId ?? null,
|
|
251
296
|
resource: credentials.one?.resource ?? null,
|
|
252
297
|
organizationId: credentials.one?.orgId ?? null,
|
|
298
|
+
savedFor,
|
|
299
|
+
signInEnded,
|
|
253
300
|
user: liveUser,
|
|
254
301
|
expiresAt: signedIn ? credentials.one?.expiresAt ?? null : null,
|
|
255
302
|
scope: signedIn ? credentials.one?.scope ?? null : null,
|
|
@@ -263,9 +310,13 @@ export const handleWhoami = async (ctx) => {
|
|
|
263
310
|
outputJson(ctx, payload);
|
|
264
311
|
}
|
|
265
312
|
else if (!signedIn) {
|
|
313
|
+
if (signInEnded)
|
|
314
|
+
ctx.writer.stderr('Your saved sign-in was ended by Vigilbase Identity and has been removed.');
|
|
266
315
|
outputLines(ctx, [
|
|
267
316
|
'Not signed in. Public tools still work.',
|
|
268
|
-
|
|
317
|
+
savedFor
|
|
318
|
+
? `A saved sign-in exists for ${describeSavedFor(savedFor)}; pass the matching options or run \`vigilbase login\`.`
|
|
319
|
+
: 'Run `vigilbase login` to unlock auth-required tools (website-security, react2shell, fortibleed-check).',
|
|
269
320
|
...(legacyIdentityFileIgnored ? [legacyNote] : []),
|
|
270
321
|
]);
|
|
271
322
|
}
|
package/dist/commands/billing.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { validateArguments } from './legacy-validation.js';
|
|
1
|
+
import { unknownSubcommand, validateArguments } from './legacy-validation.js';
|
|
2
2
|
import { openUrlInBrowser } from '../lib/open-url.js';
|
|
3
3
|
import { asRecord, ok, outputJson, requireOneClient, withSpinner, } from './helpers.js';
|
|
4
4
|
export const handleBilling = async (ctx) => {
|
|
5
5
|
const subcommand = (ctx.rest[0] ?? 'summary').toLowerCase();
|
|
6
6
|
if (!['summary', 'show', 'status', 'portal', 'open'].includes(subcommand))
|
|
7
|
-
throw
|
|
7
|
+
throw unknownSubcommand('billing', ctx.rest[0] ?? '');
|
|
8
8
|
validateArguments(ctx, ctx.rest[0] === undefined ? 0 : 1, ['portal', 'open'].includes(subcommand) ? ['isNoOpen'] : []);
|
|
9
9
|
const one = await requireOneClient(ctx);
|
|
10
10
|
if (subcommand === 'summary' ||
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import { type CommandHandler } from './helpers.js';
|
|
2
|
-
export declare const CLOUDFLARE_HELP = "Usage: vigilbase cf <command> [options]\nAlias: vigilbase cloudflare\n\nInventory and workspace:\n zones List zones (default)
|
|
2
|
+
export declare const CLOUDFLARE_HELP = "Usage: vigilbase cf <command> [options]\nAlias: vigilbase cloudflare\n\nInventory and workspace:\n zones List zones (default)\n --customer-id --account-id --zone-id\n zone <zone-id> Zone detail; --customer-id for live details,\n optional --account-id\n accounts List accessible accounts\n --customer-id --account-id\n account <account-id> Stored account detail; --customer-id\n workspace [section] Workspace bootstrap or one section\n --customer-id --account-id --zone-id\n Sections: overview, reports, zones, rule-analyzer,\n accounts, checkups, settings\n\nZone reads (--customer-id <uuid> required; IDs can use --zone-id):\n dns <zone-id> DNS records, TTL and proxy state\n waf <zone-id> WAF rules, expressions, analytics and\n suggestions\n speed-settings <zone-id> Performance settings (internal users only)\n\nAccount reads (optional --customer-id; IDs can use --account-id):\n audit-logs <account-id> Latest provider audit events (up to 100)\n Needs service-read permission.\n zero-trust <account-id> Users, Access applications, tunnels and\n authentication analytics\n developer-platform <account-id>\n Workers, Workers analytics and Pages\n magic-transit <account-id> Prefixes, routes and traffic analytics\n Zero Trust, Developer Platform and Magic Transit require deployment-read permission.\n\nAnalytics and checkups:\n analytics --customer-id <uuid> [--account-id <id> | --zone-id <id>]\n [--days 1..30]\n Default 7 days; live zone analytics uses the most recent 23 hours.\n checkup [status] Latest completed checkup\n --customer-id --account-id\n checkup start Start and persist a scan\n --customer-id --account-id\n checkup progress <run-uuid> Read saved progress once\n checkup result <run-uuid> Read the stored Cloudflare checkup result\n checkups aliases checkup; list/show alias status; run aliases start.\n\nCommon options: --json, --one-url <origin>, --timeout <milliseconds>, --help\nCustomer IDs are UUIDs; account and zone IDs are 32 hexadecimal characters.\nOne login, tenant bindings and permissions apply to every command.\nCheckup start creates a scan run. These commands do not edit provider configuration.\nSee CLOUDFLARE.md for full grammar, API mapping, data limits and examples.";
|
|
3
3
|
export declare const handleCloudflare: CommandHandler;
|