@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.
Files changed (75) hide show
  1. package/CLOUDFLARE.md +2 -2
  2. package/COMMANDS.md +111 -44
  3. package/LOCAL.md +99 -38
  4. package/ONE.md +2 -2
  5. package/README.md +215 -67
  6. package/dist/auth/acl.d.ts +6 -0
  7. package/dist/auth/acl.js +45 -0
  8. package/dist/auth/device-auth.js +1 -0
  9. package/dist/auth/provider.js +4 -4
  10. package/dist/auth/refresh.d.ts +5 -4
  11. package/dist/auth/refresh.js +82 -41
  12. package/dist/auth/store.d.ts +25 -0
  13. package/dist/auth/store.js +91 -11
  14. package/dist/bin.js +13 -1
  15. package/dist/cli.js +205 -81
  16. package/dist/commands/account.js +11 -7
  17. package/dist/commands/auth.d.ts +6 -0
  18. package/dist/commands/auth.js +75 -24
  19. package/dist/commands/billing.js +2 -2
  20. package/dist/commands/cloudflare.d.ts +1 -1
  21. package/dist/commands/cloudflare.js +31 -17
  22. package/dist/commands/helpers.js +3 -3
  23. package/dist/commands/index.d.ts +1 -0
  24. package/dist/commands/index.js +2 -1
  25. package/dist/commands/legacy-validation.d.ts +20 -0
  26. package/dist/commands/legacy-validation.js +77 -3
  27. package/dist/commands/local.js +186 -65
  28. package/dist/commands/one.js +16 -13
  29. package/dist/commands/public-tools.js +52 -27
  30. package/dist/commands/runs.js +5 -5
  31. package/dist/commands/security.d.ts +1 -1
  32. package/dist/commands/security.js +41 -37
  33. package/dist/commands/watchdog.js +5 -5
  34. package/dist/lib/banner.d.ts +5 -11
  35. package/dist/lib/banner.js +11 -31
  36. package/dist/lib/client.js +19 -1
  37. package/dist/lib/cloudflare-client.d.ts +2 -2
  38. package/dist/lib/config.d.ts +16 -0
  39. package/dist/lib/config.js +77 -19
  40. package/dist/lib/format.d.ts +1 -1
  41. package/dist/lib/format.js +75 -24
  42. package/dist/lib/help.js +57 -6
  43. package/dist/lib/network.d.ts +12 -3
  44. package/dist/lib/network.js +73 -9
  45. package/dist/lib/one-client.js +9 -2
  46. package/dist/lib/one-error-hints.d.ts +1 -0
  47. package/dist/lib/one-error-hints.js +10 -0
  48. package/dist/lib/open-url.js +2 -1
  49. package/dist/lib/platform.d.ts +2 -0
  50. package/dist/lib/platform.js +5 -0
  51. package/dist/lib/progress.d.ts +18 -3
  52. package/dist/lib/progress.js +72 -10
  53. package/dist/lib/system-trust.d.ts +2 -0
  54. package/dist/lib/system-trust.js +6 -0
  55. package/dist/lib/tui.d.ts +1 -1
  56. package/dist/lib/tui.js +5 -7
  57. package/dist/local/cloudflare-checkup/cf/child-env.js +2 -4
  58. package/dist/local/cloudflare-checkup/cf/install.d.ts +4 -2
  59. package/dist/local/cloudflare-checkup/cf/install.js +55 -24
  60. package/dist/local/cloudflare-checkup/cf/locate.d.ts +2 -0
  61. package/dist/local/cloudflare-checkup/cf/locate.js +10 -1
  62. package/dist/local/cloudflare-checkup/cf/run-cf.d.ts +18 -3
  63. package/dist/local/cloudflare-checkup/cf/run-cf.js +75 -17
  64. package/dist/local/cloudflare-checkup/contract.d.ts +16 -9
  65. package/dist/local/cloudflare-checkup/contract.js +80 -14
  66. package/dist/local/cloudflare-checkup/errors.d.ts +34 -1
  67. package/dist/local/cloudflare-checkup/errors.js +36 -5
  68. package/dist/local/cloudflare-checkup/output.d.ts +6 -0
  69. package/dist/local/cloudflare-checkup/output.js +9 -0
  70. package/dist/local/cloudflare-checkup/snapshot-file.d.ts +6 -2
  71. package/dist/local/cloudflare-checkup/snapshot-file.js +25 -11
  72. package/dist/local/cloudflare-checkup/submit-client.js +7 -1
  73. package/dist/types.d.ts +2 -1
  74. package/esm-only.cjs +3 -0
  75. 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
- /** Map a failed contract parse to the CLI error (no values, issue codes only). */
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
- }>): LocalCheckupError;
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
- /** Map a failed contract parse to the CLI error (no values, issue codes only). */
82
- export function snapshotParseError(issues) {
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(() => null);
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 (!server.accepts.schemaVersions.includes(schemaVersion) || compareSemver(cliVersion, server.minCliVersion) < 0) {
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
- export type ExitCode = 0 | 1 | 2;
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
@@ -0,0 +1,3 @@
1
+ 'use strict'
2
+ // require('@vigilbase/cli') resolves here: the library is an ES module (dist/index.js).
3
+ throw Object.assign(new Error('@vigilbase/cli is ESM-only. Use import() or the vigilbase command.'), { code: 'ERR_REQUIRE_ESM' })
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vigilbase/cli",
3
- "version": "0.2.3",
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",