@vertaaux/cli 0.3.1 → 0.3.3

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 CHANGED
@@ -147,7 +147,7 @@ The `--machine` global flag enables strict machine-readable mode:
147
147
  ```json
148
148
  {
149
149
  "meta": {
150
- "version": "0.1.0",
150
+ "version": "0.3.3",
151
151
  "timestamp": "2026-02-08T12:00:00.000Z",
152
152
  "command": "audit",
153
153
  "args": ["https://example.com", "--format", "json"]
@@ -210,6 +210,10 @@ These options work with any command:
210
210
  | `-q, --quiet` | Suppress banner and non-essential output |
211
211
  | `--no-banner` | Hide the V-mark banner |
212
212
  | `--machine` | Strict machine-readable output mode |
213
+ | `--color` | Force colored output |
214
+ | `--no-color` | Disable colored output |
215
+ | `--dashboard` | Force live dashboard during audit --wait |
216
+ | `--no-dashboard` | Disable live dashboard (use spinner instead) |
213
217
  | `--dry-run` | Show what would happen without executing |
214
218
  | `-y, --yes` | Auto-confirm all interactive prompts |
215
219
  | `--verbose` | Expand output with additional details |
@@ -338,6 +342,9 @@ JSON envelope output automatically filters CLI arguments containing API keys or
338
342
  | `VERTAAUX_API_KEY` | API authentication key |
339
343
  | `VERTAAUX_TOKEN` | Alternative auth token (checked first) |
340
344
  | `VERTAAUX_API_BASE` | API base URL override |
345
+ | `VERTAAUX_AUTH_BASE` | Auth endpoint override (default: `https://vertaaux.ai`) |
346
+ | `VERTAAUX_LOG_LEVEL` | Log verbosity: `debug\|info\|warn\|error` (default: `info`) |
347
+ | `VERTAAUX_LOG_JSON` | Structured JSON logs (default: `false`) |
341
348
  | `NO_COLOR` | Disable colored output |
342
349
  | `FORCE_COLOR` | Force colored output |
343
350
 
@@ -16,6 +16,7 @@ export interface AuditCommandOptions {
16
16
  timeout?: number;
17
17
  interval?: number;
18
18
  format?: string;
19
+ json?: boolean;
19
20
  output?: string;
20
21
  groupBy?: "severity" | "category" | "route";
21
22
  severity?: string;
@@ -1 +1 @@
1
- {"version":3,"file":"audit.d.ts","sourceRoot":"","sources":["../../src/commands/audit.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAyFpC,MAAM,WAAW,mBAAmB;IAElC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IAGrB,IAAI,CAAC,EAAE,OAAO,GAAG,UAAU,GAAG,MAAM,CAAC;IACrC,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAGlB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,UAAU,GAAG,UAAU,GAAG,OAAO,CAAC;IAG5C,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAGlB,MAAM,CAAC,EAAE,OAAO,GAAG,SAAS,GAAG,MAAM,GAAG,MAAM,CAAC;IAC/C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAGlB,WAAW,CAAC,EAAE,OAAO,CAAC;IAGtB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,YAAY,CAAC,EAAE,OAAO,CAAC;IAGvB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,OAAO,CAAC;IAGhB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,KAAK,CAAC,EAAE,MAAM,CAAC;IAGf,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,CAAC;IAGpB,MAAM,CAAC,EAAE,OAAO,GAAG,UAAU,GAAG,MAAM,CAAC;IAGvC,MAAM,CAAC,EAAE,MAAM,CAAC;IAGhB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,YAAY,CAAC,EAAE,OAAO,CAAC;IAGvB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAGlB,QAAQ,CAAC,EAAE,OAAO,CAAC;IAGnB,OAAO,CAAC,EAAE,OAAO,CAAC;IAGlB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAu4BD;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAkT3D"}
1
+ {"version":3,"file":"audit.d.ts","sourceRoot":"","sources":["../../src/commands/audit.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAyFpC,MAAM,WAAW,mBAAmB;IAElC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IAGrB,IAAI,CAAC,EAAE,OAAO,GAAG,UAAU,GAAG,MAAM,CAAC;IACrC,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAGlB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,UAAU,GAAG,UAAU,GAAG,OAAO,CAAC;IAG5C,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAGlB,MAAM,CAAC,EAAE,OAAO,GAAG,SAAS,GAAG,MAAM,GAAG,MAAM,CAAC;IAC/C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAGlB,WAAW,CAAC,EAAE,OAAO,CAAC;IAGtB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,YAAY,CAAC,EAAE,OAAO,CAAC;IAGvB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,OAAO,CAAC;IAGhB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,KAAK,CAAC,EAAE,MAAM,CAAC;IAGf,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,CAAC;IAGpB,MAAM,CAAC,EAAE,OAAO,GAAG,UAAU,GAAG,MAAM,CAAC;IAGvC,MAAM,CAAC,EAAE,MAAM,CAAC;IAGhB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,YAAY,CAAC,EAAE,OAAO,CAAC;IAGvB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAGlB,QAAQ,CAAC,EAAE,OAAO,CAAC;IAGnB,OAAO,CAAC,EAAE,OAAO,CAAC;IAGlB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAw5BD;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAmT3D"}
@@ -380,8 +380,9 @@ async function executeAudit(targetUrl, options, config) {
380
380
  const base = resolveApiBase(options.base);
381
381
  const apiKey = getApiKey(config.apiKey);
382
382
  // Resolve output format using per-command registry
383
+ // --json flag is a shorthand for --format json (convenient for piping)
383
384
  const machineMode = options.machine || false;
384
- const explicitFormat = options.format || config.output?.format;
385
+ const explicitFormat = options.json ? "json" : (options.format || config.output?.format);
385
386
  const validatedFormat = resolveCommandFormat("audit", explicitFormat, machineMode);
386
387
  const format = validatedFormat;
387
388
  const formatter = createOutput(format);
@@ -412,7 +413,7 @@ async function executeAudit(targetUrl, options, config) {
412
413
  try {
413
414
  // Start spinner (dashboard renders on first update)
414
415
  spinner?.start();
415
- // Create audit job
416
+ // Create audit job (server runs audit synchronously and returns results)
416
417
  const created = await apiRequest(base, "/audit", {
417
418
  method: "POST",
418
419
  body: { url: targetUrl, mode },
@@ -447,34 +448,48 @@ async function executeAudit(targetUrl, options, config) {
447
448
  }
448
449
  return;
449
450
  }
450
- // Wait for completion with progress updates
451
- if (!created.job_id) {
452
- throw new Error("Audit response missing job_id");
453
- }
454
- const result = await waitForAudit(base, created.job_id, timeout, interval, apiKey, (progress, status) => {
455
- if (aborted)
456
- return;
457
- if (renderer) {
458
- const phase = mapStatusToPhase(status);
459
- const state = {
460
- phase,
461
- phaseIndex: phaseIndex(phase),
462
- phaseTotal: phaseTotal(),
463
- url: targetUrl,
464
- mode,
465
- progress: { audit: progress },
466
- totals: { audit: 100 },
467
- issueCount: 0,
468
- scorePreview: null,
469
- verbose: false,
470
- elapsed: Date.now() - auditStartTime,
471
- };
472
- renderer.update(state);
473
- }
474
- if (spinner) {
475
- updateSpinner(spinner, `Auditing ${targetUrl}`, progress, 100);
451
+ // Determine if the server returned results synchronously (status 200)
452
+ // or queued the job for background processing (status 202, legacy servers)
453
+ const isSyncResponse = created.status === "completed" && created.scores;
454
+ let result;
455
+ if (created.status === "failed") {
456
+ // Server ran the audit synchronously but it failed
457
+ throw new Error(created.error || "Audit failed on server");
458
+ }
459
+ if (isSyncResponse) {
460
+ // Server already ran the audit — use the response directly
461
+ result = created;
462
+ }
463
+ else {
464
+ // Legacy/async path: poll for completion
465
+ if (!created.job_id) {
466
+ throw new Error("Audit response missing job_id");
476
467
  }
477
- });
468
+ result = await waitForAudit(base, created.job_id, timeout, interval, apiKey, (progress, status) => {
469
+ if (aborted)
470
+ return;
471
+ if (renderer) {
472
+ const phase = mapStatusToPhase(status);
473
+ const state = {
474
+ phase,
475
+ phaseIndex: phaseIndex(phase),
476
+ phaseTotal: phaseTotal(),
477
+ url: targetUrl,
478
+ mode,
479
+ progress: { audit: progress },
480
+ totals: { audit: 100 },
481
+ issueCount: 0,
482
+ scorePreview: null,
483
+ verbose: false,
484
+ elapsed: Date.now() - auditStartTime,
485
+ };
486
+ renderer.update(state);
487
+ }
488
+ if (spinner) {
489
+ updateSpinner(spinner, `Auditing ${targetUrl}`, progress, 100);
490
+ }
491
+ });
492
+ }
478
493
  // Finish dashboard or spinner
479
494
  if (renderer) {
480
495
  const overallScore = getOverallScoreFromResult(result);
@@ -774,6 +789,7 @@ export function registerAuditCommand(program) {
774
789
  .option("--auth-profile <profile>", "Authentication profile for protected pages")
775
790
  .option("--mode <mode>", "Audit depth: basic|standard|deep", parseMode, "basic")
776
791
  .option("--format <format>", "Output format: json|sarif|junit|html|human (default: human in terminal, auto-detected in CI)")
792
+ .option("--json", "Shorthand for --format json (convenient for piping)")
777
793
  .option("-o, --output <path>", "Output file path")
778
794
  .option("--group-by <field>", "Group issues by: severity|category|route", parseGroupBy)
779
795
  .option("--wait", "Wait for audit completion (default)")
@@ -925,7 +941,7 @@ export function registerAuditCommand(program) {
925
941
  // Aggregate and output results
926
942
  const aggregated = aggregateResults(results);
927
943
  aggregated.totalDuration = Date.now() - startTime;
928
- if (cmdOptions.format === "json") {
944
+ if (cmdOptions.json || cmdOptions.format === "json") {
929
945
  writeJsonOutput(aggregated, "audit");
930
946
  }
931
947
  else {
@@ -1,7 +1,7 @@
1
1
  /**
2
- * V-mark ASCII banner for VertaaUX CLI.
2
+ * Arrow-mark ASCII banner for VertaaUX CLI.
3
3
  *
4
- * Displays a subtle gradient from lime to teal/cyan.
4
+ * Displays a compact arrow wedge in teal/mint gradient.
5
5
  * Can be suppressed via --quiet, --no-banner, or non-TTY environments.
6
6
  */
7
7
  export interface BannerOptions {
@@ -13,7 +13,7 @@ export interface BannerOptions {
13
13
  noBanner?: boolean;
14
14
  }
15
15
  /**
16
- * Show the V-mark banner with version and working directory.
16
+ * Show the arrow-mark banner with version and working directory.
17
17
  *
18
18
  * Banner is suppressed if:
19
19
  * - --quiet or -q flag is set
@@ -1 +1 @@
1
- {"version":3,"file":"banner.d.ts","sourceRoot":"","sources":["../../src/ui/banner.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAwCH,MAAM,WAAW,aAAa;IAC5B,6BAA6B;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,uCAAuC;IACvC,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,qCAAqC;IACrC,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED;;;;;;;;;GASG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,aAAa,GAAG,IAAI,CA0BvD;AAED;;;GAGG;AACH,wBAAgB,UAAU,IAAI,MAAM,CAOnC"}
1
+ {"version":3,"file":"banner.d.ts","sourceRoot":"","sources":["../../src/ui/banner.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAqBH,MAAM,WAAW,aAAa;IAC5B,6BAA6B;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,uCAAuC;IACvC,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,qCAAqC;IACrC,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED;;;;;;;;;GASG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,aAAa,GAAG,IAAI,CA+BvD;AAED;;;GAGG;AACH,wBAAgB,UAAU,IAAI,MAAM,CAOnC"}
package/dist/ui/banner.js CHANGED
@@ -1,7 +1,7 @@
1
1
  /**
2
- * V-mark ASCII banner for VertaaUX CLI.
2
+ * Arrow-mark ASCII banner for VertaaUX CLI.
3
3
  *
4
- * Displays a subtle gradient from lime to teal/cyan.
4
+ * Displays a compact arrow wedge in teal/mint gradient.
5
5
  * Can be suppressed via --quiet, --no-banner, or non-TTY environments.
6
6
  */
7
7
  import chalk from "chalk";
@@ -9,35 +9,18 @@ import { createRequire } from "module";
9
9
  import { isTTY, shouldUseColor } from "../utils/detect-env.js";
10
10
  // For JSON imports in ESM
11
11
  const require = createRequire(import.meta.url);
12
- // V-mark ASCII art (3 lines)
13
- const V_MARK = [" \u2588\u2588\u2557 \u2588\u2588\u2557", " \u255a\u2588\u2588\u2557 \u2588\u2588\u2554\u255d", " \u255a\u2588\u2588\u2588\u2588\u2554\u255d"];
14
- // Gradient colors: lime -> teal/cyan
15
- const GRADIENT_COLORS = [
16
- "#78FFB4", // Lime
17
- "#6BEEBD", // Light teal
18
- "#5EDDC6", // Teal
19
- "#51CCCE", // Cyan-ish teal
12
+ // Arrow-mark ASCII art (compact 5-line wedge matching brand)
13
+ const ARROW_MARK = [
14
+ " ██████████▀",
15
+ " ████████▀",
16
+ " ▀██████████▀",
17
+ " ▀██████▀",
18
+ " ▀██▀",
20
19
  ];
20
+ const TEAL = "#5EE4C4";
21
+ const MINT = "#90EECE";
21
22
  /**
22
- * Apply gradient to text by coloring each character.
23
- */
24
- function applyGradient(text, colors) {
25
- const colorCount = colors.length;
26
- let result = "";
27
- let colorIndex = 0;
28
- for (const char of text) {
29
- if (char !== " ") {
30
- result += chalk.hex(colors[colorIndex % colorCount])(char);
31
- colorIndex++;
32
- }
33
- else {
34
- result += char;
35
- }
36
- }
37
- return result;
38
- }
39
- /**
40
- * Show the V-mark banner with version and working directory.
23
+ * Show the arrow-mark banner with version and working directory.
41
24
  *
42
25
  * Banner is suppressed if:
43
26
  * - --quiet or -q flag is set
@@ -54,20 +37,27 @@ export function showBanner(options) {
54
37
  }
55
38
  const useColor = shouldUseColor();
56
39
  const cwd = process.cwd();
57
- // Build banner lines
58
- const lines = useColor
59
- ? [
60
- `${applyGradient(V_MARK[0], GRADIENT_COLORS)} ${chalk.bold("VertaaUX")} v${version}`,
61
- `${applyGradient(V_MARK[1], GRADIENT_COLORS)} ${chalk.dim(cwd)}`,
62
- `${applyGradient(V_MARK[2], GRADIENT_COLORS)}`,
63
- ]
64
- : [
65
- `${V_MARK[0]} VertaaUX v${version}`,
66
- `${V_MARK[1]} ${cwd}`,
67
- `${V_MARK[2]}`,
40
+ if (useColor) {
41
+ const logo = ARROW_MARK.map((line) => chalk.hex(TEAL)(line));
42
+ const lines = [
43
+ `${logo[0]} ${chalk.hex(MINT).bold("VertaaUX")} v${version}`,
44
+ `${logo[1]} ${chalk.dim(cwd)}`,
45
+ `${logo[2]}`,
46
+ `${logo[3]}`,
47
+ `${logo[4]}`,
68
48
  ];
69
- // Write to stderr to avoid mixing with command output
70
- process.stderr.write(lines.join("\n") + "\n\n");
49
+ process.stderr.write(lines.join("\n") + "\n\n");
50
+ }
51
+ else {
52
+ const lines = [
53
+ `${ARROW_MARK[0]} VertaaUX v${version}`,
54
+ `${ARROW_MARK[1]} ${cwd}`,
55
+ `${ARROW_MARK[2]}`,
56
+ `${ARROW_MARK[3]}`,
57
+ `${ARROW_MARK[4]}`,
58
+ ];
59
+ process.stderr.write(lines.join("\n") + "\n\n");
60
+ }
71
61
  }
72
62
  /**
73
63
  * Get version from package.json.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vertaaux/cli",
3
- "version": "0.3.1",
3
+ "version": "0.3.3",
4
4
  "description": "Run automated UX audits, accessibility checks, and performance analysis from the terminal or CI pipelines. Supports policy gating, SARIF output, and multi-page crawling. See https://github.com/PetriLahdelma/vertaa/tree/main/cli#readme for full docs.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",