@vigilbase/cli 0.2.3 → 0.2.5
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 +111 -44
- package/LOCAL.md +99 -38
- package/ONE.md +2 -2
- package/README.md +215 -67
- package/dist/auth/acl.d.ts +6 -0
- package/dist/auth/acl.js +45 -0
- package/dist/auth/device-auth.js +1 -0
- 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 +52 -27
- 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.js +19 -1
- 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 +1 -1
- package/dist/lib/format.js +75 -24
- 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.js +9 -2
- package/dist/lib/one-error-hints.d.ts +1 -0
- package/dist/lib/one-error-hints.js +10 -0
- 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 +2 -1
- package/esm-only.cjs +3 -0
- package/package.json +7 -4
|
@@ -5,6 +5,12 @@ import type { EntryCounts } from './snapshot-file.js';
|
|
|
5
5
|
* (`...` on an ASCII-only console).
|
|
6
6
|
*/
|
|
7
7
|
export declare function displayText(value: string, max: number, ellipsis?: string): string;
|
|
8
|
+
/**
|
|
9
|
+
* A file name the user passed, in a line of plain output: controls, line
|
|
10
|
+
* breaks and invisible format characters become visible escapes (`\n`,
|
|
11
|
+
* `\u001b`), so a crafted name cannot add or rewrite lines.
|
|
12
|
+
*/
|
|
13
|
+
export declare function displayFileName(value: string): string;
|
|
8
14
|
/** `0123…cdef` (`0123...cdef` with the ASCII `ellipsis` of the terminal UI). */
|
|
9
15
|
export declare function maskId(id: string, ellipsis?: string): string;
|
|
10
16
|
/** `9f2c…a71e` */
|
|
@@ -14,6 +14,15 @@ export function displayText(value, max, ellipsis = '…') {
|
|
|
14
14
|
const flat = sanitizeTerminal(value.replace(/[\s\u0085\u2028\u2029]+/g, ' ')).trim();
|
|
15
15
|
return flat.length > max ? `${flat.slice(0, Math.max(0, max - ellipsis.length))}${ellipsis}` : flat;
|
|
16
16
|
}
|
|
17
|
+
/**
|
|
18
|
+
* A file name the user passed, in a line of plain output: controls, line
|
|
19
|
+
* breaks and invisible format characters become visible escapes (`\n`,
|
|
20
|
+
* `\u001b`), so a crafted name cannot add or rewrite lines.
|
|
21
|
+
*/
|
|
22
|
+
export function displayFileName(value) {
|
|
23
|
+
const named = { '\n': '\\n', '\r': '\\r', '\t': '\\t' };
|
|
24
|
+
return value.replace(/[\p{Cc}\p{Cf}\u2028\u2029]/gu, (char) => named[char] ?? `\\u${char.codePointAt(0).toString(16).padStart(4, '0')}`);
|
|
25
|
+
}
|
|
17
26
|
/** `0123…cdef` (`0123...cdef` with the ASCII `ellipsis` of the terminal UI). */
|
|
18
27
|
export function maskId(id, ellipsis = '…') {
|
|
19
28
|
return id.length > 8 ? `${id.slice(0, 4)}${ellipsis}${id.slice(-4)}` : id;
|
|
@@ -14,13 +14,17 @@ export type LoadedSnapshot = {
|
|
|
14
14
|
sha256: string;
|
|
15
15
|
snapshot: CloudflareCheckupSnapshotV1;
|
|
16
16
|
};
|
|
17
|
-
/**
|
|
17
|
+
/**
|
|
18
|
+
* Map a failed contract parse to the CLI error (no values, issue codes only).
|
|
19
|
+
* `file`: the snapshot was read from a file, so the hint says how to collect
|
|
20
|
+
* a fresh one instead of running the same command again.
|
|
21
|
+
*/
|
|
18
22
|
export declare function snapshotParseError(issues: ReadonlyArray<{
|
|
19
23
|
code?: string;
|
|
20
24
|
message?: string;
|
|
21
25
|
path?: ReadonlyArray<PropertyKey>;
|
|
22
26
|
params?: unknown;
|
|
23
|
-
}
|
|
27
|
+
}>, file?: boolean): LocalCheckupError;
|
|
24
28
|
/** Read, hash and validate a snapshot file against the vendored contract. */
|
|
25
29
|
export declare function loadSnapshotFile(path: string): Promise<LoadedSnapshot>;
|
|
26
30
|
export type EntryCounts = {
|
|
@@ -13,7 +13,7 @@ import { createHash, randomBytes } from 'node:crypto';
|
|
|
13
13
|
import { link, open, readFile, rename, rm, stat } from 'node:fs/promises';
|
|
14
14
|
import { basename, dirname, join } from 'node:path';
|
|
15
15
|
import { CloudflareCheckupSnapshotV1Schema, SNAPSHOT_LIMITS, SNAPSHOT_SCHEMA_VERSION, expandRuleRuns, snapshotIssueList, snapshotRejectReason, } from './contract.js';
|
|
16
|
-
import { LocalCheckupError, SNAPSHOT_MISSING_HINT } from './errors.js';
|
|
16
|
+
import { LocalCheckupError, SNAPSHOT_INVALID_FILE_HINT, SNAPSHOT_MISSING_HINT, SNAPSHOT_SECRET_FILE_HINT, SNAPSHOT_UNREADABLE_HINT, } from './errors.js';
|
|
17
17
|
export function sha256Hex(bytes) {
|
|
18
18
|
return createHash('sha256').update(bytes).digest('hex');
|
|
19
19
|
}
|
|
@@ -78,27 +78,41 @@ export async function writeSnapshotFile(path, text, options) {
|
|
|
78
78
|
}
|
|
79
79
|
return { bytes: bytes.byteLength, sha256: sha256Hex(bytes) };
|
|
80
80
|
}
|
|
81
|
-
/**
|
|
82
|
-
|
|
81
|
+
/**
|
|
82
|
+
* Map a failed contract parse to the CLI error (no values, issue codes only).
|
|
83
|
+
* `file`: the snapshot was read from a file, so the hint says how to collect
|
|
84
|
+
* a fresh one instead of running the same command again.
|
|
85
|
+
*/
|
|
86
|
+
export function snapshotParseError(issues, file = false) {
|
|
83
87
|
const reason = snapshotRejectReason(issues);
|
|
84
88
|
const codes = [...new Set(snapshotIssueList(issues).map((issue) => issue.code))].slice(0, 5).join(', ');
|
|
85
89
|
if (reason === 'secret_like_value') {
|
|
86
|
-
return new LocalCheckupError('SNAPSHOT_SECRET_LIKE_VALUE', 'The snapshot contains a value that looks like a credential, so it will not be written or uploaded.');
|
|
90
|
+
return new LocalCheckupError('SNAPSHOT_SECRET_LIKE_VALUE', 'The snapshot contains a value that looks like a credential, so it will not be written or uploaded.', file ? { hint: SNAPSHOT_SECRET_FILE_HINT } : {});
|
|
87
91
|
}
|
|
88
|
-
return new LocalCheckupError('SNAPSHOT_INVALID', `The snapshot does not match ${SNAPSHOT_SCHEMA_VERSION} (${codes || 'invalid'})
|
|
92
|
+
return new LocalCheckupError('SNAPSHOT_INVALID', `The snapshot does not match ${SNAPSHOT_SCHEMA_VERSION} (${codes || 'invalid'}).`, file ? { hint: SNAPSHOT_INVALID_FILE_HINT } : {});
|
|
93
|
+
}
|
|
94
|
+
/** A stat or read failure of the snapshot path, without Node's raw error text. */
|
|
95
|
+
function unreadable(error) {
|
|
96
|
+
const code = error.code;
|
|
97
|
+
if (code === 'ENOENT' || code === 'ENOTDIR')
|
|
98
|
+
return new LocalCheckupError('SNAPSHOT_INVALID', 'The snapshot file does not exist.', { hint: SNAPSHOT_MISSING_HINT });
|
|
99
|
+
if (code === 'ELOOP')
|
|
100
|
+
return new LocalCheckupError('SNAPSHOT_INVALID', 'The snapshot path is a symbolic-link loop.', { hint: SNAPSHOT_MISSING_HINT });
|
|
101
|
+
if (code === 'EACCES' || code === 'EPERM') {
|
|
102
|
+
return new LocalCheckupError('SNAPSHOT_INVALID', 'The snapshot file cannot be read (permission denied).', { hint: SNAPSHOT_UNREADABLE_HINT });
|
|
103
|
+
}
|
|
104
|
+
return new LocalCheckupError('SNAPSHOT_INVALID', 'The snapshot file cannot be read.', { hint: SNAPSHOT_UNREADABLE_HINT });
|
|
89
105
|
}
|
|
90
106
|
/** Read, hash and validate a snapshot file against the vendored contract. */
|
|
91
107
|
export async function loadSnapshotFile(path) {
|
|
92
|
-
const info = await stat(path).catch(() =>
|
|
93
|
-
if (!info)
|
|
94
|
-
throw new LocalCheckupError('SNAPSHOT_INVALID', 'The snapshot file does not exist.', { hint: SNAPSHOT_MISSING_HINT });
|
|
108
|
+
const info = await stat(path).catch((error) => { throw unreadable(error); });
|
|
95
109
|
if (!info.isFile())
|
|
96
110
|
throw new LocalCheckupError('SNAPSHOT_INVALID', 'The snapshot path is not a file.', { hint: SNAPSHOT_MISSING_HINT });
|
|
97
111
|
if (info.size > SNAPSHOT_LIMITS.maxBytes) {
|
|
98
112
|
throw new LocalCheckupError('SNAPSHOT_TOO_LARGE', `The snapshot is larger than the ${SNAPSHOT_LIMITS.maxBytes / 1024 / 1024} MiB contract limit.`);
|
|
99
113
|
}
|
|
100
114
|
// no-dd-sa:javascript-node-security/detect-non-literal-fs-filename — the user's own snapshot path.
|
|
101
|
-
const bytes = await readFile(path);
|
|
115
|
+
const bytes = await readFile(path).catch((error) => { throw unreadable(error); });
|
|
102
116
|
if (bytes.byteLength > SNAPSHOT_LIMITS.maxBytes)
|
|
103
117
|
throw new LocalCheckupError('SNAPSHOT_TOO_LARGE', 'The snapshot is larger than the contract limit.');
|
|
104
118
|
let raw;
|
|
@@ -106,11 +120,11 @@ export async function loadSnapshotFile(path) {
|
|
|
106
120
|
raw = JSON.parse(bytes.toString('utf8'));
|
|
107
121
|
}
|
|
108
122
|
catch {
|
|
109
|
-
throw new LocalCheckupError('SNAPSHOT_INVALID', 'The snapshot file is not valid JSON.');
|
|
123
|
+
throw new LocalCheckupError('SNAPSHOT_INVALID', 'The snapshot file is not valid JSON.', { hint: SNAPSHOT_INVALID_FILE_HINT });
|
|
110
124
|
}
|
|
111
125
|
const parsed = CloudflareCheckupSnapshotV1Schema.safeParse(raw);
|
|
112
126
|
if (!parsed.success)
|
|
113
|
-
throw snapshotParseError(parsed.error.issues);
|
|
127
|
+
throw snapshotParseError(parsed.error.issues, true);
|
|
114
128
|
return { path, bytes, sha256: sha256Hex(bytes), snapshot: parsed.data };
|
|
115
129
|
}
|
|
116
130
|
export function countEntries(entries) {
|
|
@@ -382,9 +382,15 @@ export class SubmitClient extends LocalHttpClient {
|
|
|
382
382
|
}
|
|
383
383
|
/** Refuse a server (its session, or its availability answer) that requires a newer CLI, or does not accept this schema version. */
|
|
384
384
|
export function assertServerAccepts(server, cliVersion, schemaVersion) {
|
|
385
|
-
if (
|
|
385
|
+
if (compareSemver(cliVersion, server.minCliVersion) < 0) {
|
|
386
386
|
throw new LocalCheckupError('CLI_UPGRADE_REQUIRED', `vigilbase.com requires @vigilbase/cli ${server.minCliVersion.slice(0, 40)} or newer for this snapshot.`);
|
|
387
387
|
}
|
|
388
|
+
// This CLI is new enough, so the file's format is the problem (an old snapshot, or a server that moved on).
|
|
389
|
+
if (!server.accepts.schemaVersions.includes(schemaVersion)) {
|
|
390
|
+
throw new LocalCheckupError('CLI_UPGRADE_REQUIRED', `vigilbase.com does not accept snapshots in the ${schemaVersion.slice(0, 80)} format.`, {
|
|
391
|
+
hint: 'Collect a fresh snapshot with the latest CLI: `npx @vigilbase/cli@latest cloudflare-checkup`.',
|
|
392
|
+
});
|
|
393
|
+
}
|
|
388
394
|
}
|
|
389
395
|
/**
|
|
390
396
|
* The masked address as the server masks it (first character, `***`, the
|
package/dist/types.d.ts
CHANGED
|
@@ -62,7 +62,8 @@ export type AgentToolExecuteResponse = {
|
|
|
62
62
|
skillsIndex?: string;
|
|
63
63
|
};
|
|
64
64
|
export type CliOutputMode = 'text' | 'json';
|
|
65
|
-
|
|
65
|
+
/** 129, 130 and 143: a local checkup cancelled by SIGHUP, SIGINT or SIGTERM. */
|
|
66
|
+
export type ExitCode = 0 | 1 | 2 | 129 | 130 | 143;
|
|
66
67
|
/** Exit codes used by the CLI. Documented for agents. */
|
|
67
68
|
export declare const EXIT: {
|
|
68
69
|
OK: 0;
|
package/esm-only.cjs
ADDED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vigilbase/cli",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.5",
|
|
4
4
|
"description": "Vigilbase CLI — security scanners, agent tools, and Vigilbase One from the terminal.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"vigilbase",
|
|
@@ -28,8 +28,10 @@
|
|
|
28
28
|
"exports": {
|
|
29
29
|
".": {
|
|
30
30
|
"types": "./dist/index.d.ts",
|
|
31
|
-
"import": "./dist/index.js"
|
|
32
|
-
|
|
31
|
+
"import": "./dist/index.js",
|
|
32
|
+
"require": "./esm-only.cjs"
|
|
33
|
+
},
|
|
34
|
+
"./package.json": "./package.json"
|
|
33
35
|
},
|
|
34
36
|
"files": [
|
|
35
37
|
"dist",
|
|
@@ -39,7 +41,8 @@
|
|
|
39
41
|
"SECURITY.md",
|
|
40
42
|
"ONE.md",
|
|
41
43
|
"LOCAL.md",
|
|
42
|
-
"LICENSE"
|
|
44
|
+
"LICENSE",
|
|
45
|
+
"esm-only.cjs"
|
|
43
46
|
],
|
|
44
47
|
"publishConfig": {
|
|
45
48
|
"access": "public",
|