@vertaaux/cli 0.2.2 → 0.3.0
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/LICENSE +21 -0
- package/README.md +58 -2
- package/dist/auth/device-flow.d.ts.map +1 -1
- package/dist/auth/device-flow.js +46 -14
- package/dist/commands/audit.d.ts +2 -0
- package/dist/commands/audit.d.ts.map +1 -1
- package/dist/commands/audit.js +167 -8
- package/dist/commands/client.d.ts +14 -0
- package/dist/commands/client.d.ts.map +1 -0
- package/dist/commands/client.js +362 -0
- package/dist/commands/compare.d.ts +20 -0
- package/dist/commands/compare.d.ts.map +1 -0
- package/dist/commands/compare.js +335 -0
- package/dist/commands/doc.d.ts +18 -0
- package/dist/commands/doc.d.ts.map +1 -0
- package/dist/commands/doc.js +161 -0
- package/dist/commands/download.d.ts.map +1 -1
- package/dist/commands/download.js +9 -8
- package/dist/commands/drift.d.ts +15 -0
- package/dist/commands/drift.d.ts.map +1 -0
- package/dist/commands/drift.js +309 -0
- package/dist/commands/explain.d.ts +14 -33
- package/dist/commands/explain.d.ts.map +1 -1
- package/dist/commands/explain.js +277 -179
- package/dist/commands/fix-plan.d.ts +15 -0
- package/dist/commands/fix-plan.d.ts.map +1 -0
- package/dist/commands/fix-plan.js +182 -0
- package/dist/commands/patch-review.d.ts +14 -0
- package/dist/commands/patch-review.d.ts.map +1 -0
- package/dist/commands/patch-review.js +200 -0
- package/dist/commands/protect.d.ts +16 -0
- package/dist/commands/protect.d.ts.map +1 -0
- package/dist/commands/protect.js +323 -0
- package/dist/commands/release-notes.d.ts +17 -0
- package/dist/commands/release-notes.d.ts.map +1 -0
- package/dist/commands/release-notes.js +145 -0
- package/dist/commands/report.d.ts +15 -0
- package/dist/commands/report.d.ts.map +1 -0
- package/dist/commands/report.js +214 -0
- package/dist/commands/suggest.d.ts +18 -0
- package/dist/commands/suggest.d.ts.map +1 -0
- package/dist/commands/suggest.js +152 -0
- package/dist/commands/triage.d.ts +17 -0
- package/dist/commands/triage.d.ts.map +1 -0
- package/dist/commands/triage.js +205 -0
- package/dist/commands/upload.d.ts.map +1 -1
- package/dist/commands/upload.js +8 -7
- package/dist/index.js +62 -25
- package/dist/output/formats.d.ts.map +1 -1
- package/dist/output/formats.js +18 -2
- package/dist/output/human.d.ts +1 -10
- package/dist/output/human.d.ts.map +1 -1
- package/dist/output/human.js +26 -98
- package/dist/policy/sync.d.ts +67 -0
- package/dist/policy/sync.d.ts.map +1 -0
- package/dist/policy/sync.js +147 -0
- package/dist/prompts/command-catalog.d.ts +46 -0
- package/dist/prompts/command-catalog.d.ts.map +1 -0
- package/dist/prompts/command-catalog.js +187 -0
- package/dist/ui/spinner.d.ts +10 -35
- package/dist/ui/spinner.d.ts.map +1 -1
- package/dist/ui/spinner.js +11 -58
- package/dist/ui/table.d.ts +1 -18
- package/dist/ui/table.d.ts.map +1 -1
- package/dist/ui/table.js +56 -163
- package/dist/utils/ai-error.d.ts +48 -0
- package/dist/utils/ai-error.d.ts.map +1 -0
- package/dist/utils/ai-error.js +190 -0
- package/dist/utils/detect-env.d.ts +6 -8
- package/dist/utils/detect-env.d.ts.map +1 -1
- package/dist/utils/detect-env.js +6 -25
- package/dist/utils/stdin.d.ts +50 -0
- package/dist/utils/stdin.d.ts.map +1 -0
- package/dist/utils/stdin.js +93 -0
- package/package.json +11 -7
package/dist/index.js
CHANGED
|
@@ -10,6 +10,7 @@ import fs from "fs";
|
|
|
10
10
|
import path from "path";
|
|
11
11
|
import { fileURLToPath } from "url";
|
|
12
12
|
import { Command } from "commander";
|
|
13
|
+
import { setColorEnabled, shouldUseColor } from "@vertaaux/tui";
|
|
13
14
|
import { showBanner, getVersion } from "./ui/banner.js";
|
|
14
15
|
import { registerAuditCommand } from "./commands/audit.js";
|
|
15
16
|
import { registerBaselineCommand } from "./commands/baseline.js";
|
|
@@ -22,6 +23,13 @@ import { registerUploadCommand } from "./commands/upload.js";
|
|
|
22
23
|
import { registerDownloadCommand } from "./commands/download.js";
|
|
23
24
|
import { registerPolicyCommand } from "./commands/policy.js";
|
|
24
25
|
import { registerDoctorCommand } from "./commands/doctor.js";
|
|
26
|
+
import { registerSuggestCommand } from "./commands/suggest.js";
|
|
27
|
+
import { registerTriageCommand } from "./commands/triage.js";
|
|
28
|
+
import { registerFixPlanCommand } from "./commands/fix-plan.js";
|
|
29
|
+
import { registerPatchReviewCommand } from "./commands/patch-review.js";
|
|
30
|
+
import { registerCompareCommand } from "./commands/compare.js";
|
|
31
|
+
import { registerReleaseNotesCommand } from "./commands/release-notes.js";
|
|
32
|
+
import { registerDocCommand } from "./commands/doc.js";
|
|
25
33
|
import { ExitCode } from "./utils/exit-codes.js";
|
|
26
34
|
import { formatCommanderError } from "./ui/diagnostics.js";
|
|
27
35
|
import { parseMode, parseTimeout, parseInterval, parseScore } from "./utils/validators.js";
|
|
@@ -625,7 +633,7 @@ async function runCompareCommand(base, urls, flags) {
|
|
|
625
633
|
}
|
|
626
634
|
printOutput(format, compare, formatCompareMarkdown(compare));
|
|
627
635
|
}
|
|
628
|
-
async function runFixCommand(base, jobId, issueId, flags) {
|
|
636
|
+
async function runFixCommand(base, jobId, issueId, flags, globalFlags = {}) {
|
|
629
637
|
const fileContent = getString(flags, "file-content");
|
|
630
638
|
const format = resolveFormat(flags);
|
|
631
639
|
if (!fileContent) {
|
|
@@ -649,8 +657,12 @@ async function runFixCommand(base, jobId, issueId, flags) {
|
|
|
649
657
|
if (!result.success) {
|
|
650
658
|
process.exitCode = 1;
|
|
651
659
|
}
|
|
660
|
+
if (globalFlags.dryRun && isTTYOutput) {
|
|
661
|
+
process.stderr.write("[dry-run] Showing patch preview — no changes applied.\n\n");
|
|
662
|
+
}
|
|
652
663
|
if (format === "json") {
|
|
653
|
-
|
|
664
|
+
const output = globalFlags.dryRun ? { ...result, dry_run: true } : result;
|
|
665
|
+
console.log(JSON.stringify(output, null, 2));
|
|
654
666
|
}
|
|
655
667
|
else {
|
|
656
668
|
if (result.success && result.patch) {
|
|
@@ -708,7 +720,7 @@ async function runVerifyCommand(base, flags) {
|
|
|
708
720
|
}
|
|
709
721
|
}
|
|
710
722
|
const BATCH_LIMIT = 10;
|
|
711
|
-
async function runFixAllCommand(base, jobId, flags) {
|
|
723
|
+
async function runFixAllCommand(base, jobId, flags, globalFlags = {}) {
|
|
712
724
|
const fileContent = getString(flags, "file-content");
|
|
713
725
|
const autoFixOnly = getBool(flags, "auto-fix-only");
|
|
714
726
|
const format = resolveFormat(flags);
|
|
@@ -741,6 +753,21 @@ async function runFixAllCommand(base, jobId, flags) {
|
|
|
741
753
|
if (isTTYOutput) {
|
|
742
754
|
process.stderr.write(" \r");
|
|
743
755
|
}
|
|
756
|
+
// --dry-run: show what would be processed and exit
|
|
757
|
+
if (globalFlags.dryRun) {
|
|
758
|
+
process.stderr.write(`[dry-run] Would generate patches for ${issuesToProcess.length} issues:\n`);
|
|
759
|
+
for (const issue of issuesToProcess) {
|
|
760
|
+
process.stderr.write(` - ${issue.id || "unknown"}: ${issue.title || issue.description || "(no title)"}\n`);
|
|
761
|
+
}
|
|
762
|
+
if (totalSkipped > 0) {
|
|
763
|
+
process.stderr.write(` (${totalSkipped} additional issues skipped — batch limit ${BATCH_LIMIT})\n`);
|
|
764
|
+
}
|
|
765
|
+
process.stderr.write("\nNo patches generated. Remove --dry-run to execute.\n");
|
|
766
|
+
return;
|
|
767
|
+
}
|
|
768
|
+
// --yes is available for future interactive confirmation steps
|
|
769
|
+
// Currently fix-all runs non-interactively, but --yes suppresses any
|
|
770
|
+
// confirmation prompts that may be added (e.g., "Apply all N patches?")
|
|
744
771
|
const results = {
|
|
745
772
|
successes: [],
|
|
746
773
|
failures: [],
|
|
@@ -845,6 +872,13 @@ program
|
|
|
845
872
|
.option("-q, --quiet", "Suppress banner and non-essential output")
|
|
846
873
|
.option("--no-banner", "Hide the V-mark banner")
|
|
847
874
|
.option("--machine", "Strict machine-readable output (JSON stdout, diagnostics stderr)")
|
|
875
|
+
.option("--color", "Force color output")
|
|
876
|
+
.option("--no-color", "Disable color output")
|
|
877
|
+
.option("--dashboard", "Force live dashboard during audit --wait")
|
|
878
|
+
.option("--no-dashboard", "Disable live dashboard (use spinner instead)")
|
|
879
|
+
.option("--dry-run", "Show what would happen without executing")
|
|
880
|
+
.option("-y, --yes", "Auto-confirm all interactive prompts")
|
|
881
|
+
.option("--verbose", "Expand output with additional details")
|
|
848
882
|
.configureOutput({
|
|
849
883
|
outputError: (str, _write) => {
|
|
850
884
|
const formatted = formatCommanderError(str);
|
|
@@ -864,9 +898,20 @@ program
|
|
|
864
898
|
.hook("preAction", (thisCommand) => {
|
|
865
899
|
const opts = thisCommand.optsWithGlobals();
|
|
866
900
|
const machineMode = opts.machine || false;
|
|
901
|
+
// Apply color settings from flags or environment
|
|
902
|
+
if (opts.color === false || process.env.NO_COLOR !== undefined) {
|
|
903
|
+
setColorEnabled(false);
|
|
904
|
+
}
|
|
905
|
+
else if (opts.color === true || process.env.FORCE_COLOR !== undefined) {
|
|
906
|
+
setColorEnabled(true);
|
|
907
|
+
}
|
|
908
|
+
else {
|
|
909
|
+
setColorEnabled(shouldUseColor());
|
|
910
|
+
}
|
|
867
911
|
if (machineMode) {
|
|
868
912
|
opts.quiet = true;
|
|
869
913
|
opts.banner = false;
|
|
914
|
+
setColorEnabled(false);
|
|
870
915
|
}
|
|
871
916
|
showBanner({
|
|
872
917
|
version,
|
|
@@ -886,6 +931,13 @@ registerUploadCommand(program);
|
|
|
886
931
|
registerDownloadCommand(program);
|
|
887
932
|
registerPolicyCommand(program);
|
|
888
933
|
registerDoctorCommand(program);
|
|
934
|
+
registerSuggestCommand(program);
|
|
935
|
+
registerTriageCommand(program);
|
|
936
|
+
registerFixPlanCommand(program);
|
|
937
|
+
registerPatchReviewCommand(program);
|
|
938
|
+
registerCompareCommand(program);
|
|
939
|
+
registerReleaseNotesCommand(program);
|
|
940
|
+
registerDocCommand(program);
|
|
889
941
|
// Legacy commands using old argument parsing
|
|
890
942
|
// These will be migrated in subsequent plans
|
|
891
943
|
program
|
|
@@ -924,24 +976,7 @@ program
|
|
|
924
976
|
process.exit(ExitCode.ERROR);
|
|
925
977
|
}
|
|
926
978
|
});
|
|
927
|
-
|
|
928
|
-
.command("compare <urlA> <urlB>")
|
|
929
|
-
.description("Compare audits of two URLs")
|
|
930
|
-
.option("--mode <mode>", "Audit depth: basic|standard|deep", parseMode, "basic")
|
|
931
|
-
.option("--wait", "Wait for audits to complete")
|
|
932
|
-
.option("--timeout <ms>", "Wait timeout in milliseconds (1-300000)", parseTimeout, 60000)
|
|
933
|
-
.option("--interval <ms>", "Poll interval in milliseconds (1-300000)", parseInterval, 5000)
|
|
934
|
-
.option("--fail-on-score <n>", "Exit non-zero if score below n (0-100)", parseScore)
|
|
935
|
-
.action(async (urlA, urlB, cmdOptions) => {
|
|
936
|
-
try {
|
|
937
|
-
const base = resolveApiBase(cmdOptions);
|
|
938
|
-
await runCompareCommand(base, [urlA, urlB], cmdOptions);
|
|
939
|
-
}
|
|
940
|
-
catch (error) {
|
|
941
|
-
process.stderr.write(`Error: ${error instanceof Error ? error.message : String(error)}\n`);
|
|
942
|
-
process.exit(ExitCode.ERROR);
|
|
943
|
-
}
|
|
944
|
-
});
|
|
979
|
+
// compare is now registered via registerCompareCommand (Phase 60)
|
|
945
980
|
program
|
|
946
981
|
.command("status <jobId>")
|
|
947
982
|
.description("Get status of an audit job")
|
|
@@ -973,13 +1008,14 @@ program
|
|
|
973
1008
|
.description("Generate a fix patch for an issue")
|
|
974
1009
|
.requiredOption("--issue <id>", "Issue ID to fix")
|
|
975
1010
|
.requiredOption("--file-content <code>", "Source code content")
|
|
976
|
-
.action(async (jobId, cmdOptions) => {
|
|
1011
|
+
.action(async (jobId, cmdOptions, command) => {
|
|
977
1012
|
try {
|
|
1013
|
+
const globalOpts = command.optsWithGlobals();
|
|
978
1014
|
const base = resolveApiBase(cmdOptions);
|
|
979
1015
|
const issueId = getString(cmdOptions, "issue");
|
|
980
1016
|
if (!issueId)
|
|
981
1017
|
throw new Error("--issue is required");
|
|
982
|
-
await runFixCommand(base, jobId, issueId, cmdOptions);
|
|
1018
|
+
await runFixCommand(base, jobId, issueId, cmdOptions, { dryRun: !!globalOpts.dryRun });
|
|
983
1019
|
}
|
|
984
1020
|
catch (error) {
|
|
985
1021
|
process.stderr.write(`Error: ${error instanceof Error ? error.message : String(error)}\n`);
|
|
@@ -991,10 +1027,11 @@ program
|
|
|
991
1027
|
.description("Generate fix patches for all issues in an audit")
|
|
992
1028
|
.requiredOption("--file-content <code>", "Source code content")
|
|
993
1029
|
.option("--auto-fix-only", "Only process auto-fixable issues")
|
|
994
|
-
.action(async (jobId, cmdOptions) => {
|
|
1030
|
+
.action(async (jobId, cmdOptions, command) => {
|
|
995
1031
|
try {
|
|
1032
|
+
const globalOpts = command.optsWithGlobals();
|
|
996
1033
|
const base = resolveApiBase(cmdOptions);
|
|
997
|
-
await runFixAllCommand(base, jobId, cmdOptions);
|
|
1034
|
+
await runFixAllCommand(base, jobId, cmdOptions, { dryRun: !!globalOpts.dryRun, yes: !!globalOpts.yes });
|
|
998
1035
|
}
|
|
999
1036
|
catch (error) {
|
|
1000
1037
|
process.stderr.write(`Error: ${error instanceof Error ? error.message : String(error)}\n`);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"formats.d.ts","sourceRoot":"","sources":["../../src/output/formats.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,eAAO,MAAM,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,
|
|
1
|
+
{"version":3,"file":"formats.d.ts","sourceRoot":"","sources":["../../src/output/formats.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,eAAO,MAAM,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAa7D,CAAC;AAEF,eAAO,MAAM,sBAAsB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAazD,CAAC;AAEF,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAclF;AAED,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,MAAM,EACf,cAAc,EAAE,MAAM,GAAG,SAAS,EAClC,WAAW,EAAE,OAAO,GACnB,MAAM,CASR"}
|
package/dist/output/formats.js
CHANGED
|
@@ -10,6 +10,13 @@ export const COMMAND_FORMATS = {
|
|
|
10
10
|
explain: ["json", "human"],
|
|
11
11
|
"policy-show": ["json", "yaml"],
|
|
12
12
|
diff: ["json", "human"],
|
|
13
|
+
suggest: ["json", "human"],
|
|
14
|
+
triage: ["json", "human"],
|
|
15
|
+
"fix-plan": ["json", "human"],
|
|
16
|
+
"patch-review": ["json", "human"],
|
|
17
|
+
"release-notes": ["json", "human", "markdown"],
|
|
18
|
+
compare: ["json", "human"],
|
|
19
|
+
doc: ["json", "markdown"],
|
|
13
20
|
};
|
|
14
21
|
export const COMMAND_DEFAULT_FORMAT = {
|
|
15
22
|
audit: "human",
|
|
@@ -17,6 +24,13 @@ export const COMMAND_DEFAULT_FORMAT = {
|
|
|
17
24
|
explain: "human",
|
|
18
25
|
"policy-show": "yaml",
|
|
19
26
|
diff: "human",
|
|
27
|
+
suggest: "human",
|
|
28
|
+
triage: "human",
|
|
29
|
+
"fix-plan": "human",
|
|
30
|
+
"patch-review": "human",
|
|
31
|
+
"release-notes": "markdown",
|
|
32
|
+
compare: "human",
|
|
33
|
+
doc: "markdown",
|
|
20
34
|
};
|
|
21
35
|
export function validateFormat(command, format) {
|
|
22
36
|
const allowed = COMMAND_FORMATS[command];
|
|
@@ -33,9 +47,11 @@ export function validateFormat(command, format) {
|
|
|
33
47
|
return format;
|
|
34
48
|
}
|
|
35
49
|
export function resolveCommandFormat(command, explicitFormat, machineMode) {
|
|
50
|
+
// Treat "auto" as unset — let per-command defaults or machine mode decide
|
|
51
|
+
const format = explicitFormat === "auto" ? undefined : explicitFormat;
|
|
36
52
|
// Machine mode defaults to JSON unless explicitly overridden
|
|
37
|
-
if (machineMode && !
|
|
53
|
+
if (machineMode && !format) {
|
|
38
54
|
return "json";
|
|
39
55
|
}
|
|
40
|
-
return validateFormat(command,
|
|
56
|
+
return validateFormat(command, format);
|
|
41
57
|
}
|
package/dist/output/human.d.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Human-readable output formatter for CLI.
|
|
3
3
|
*
|
|
4
4
|
* Outputs audit results as colorful, formatted text for terminal display.
|
|
5
|
-
*
|
|
5
|
+
* Delegates to @vertaaux/tui for consistent rendering.
|
|
6
6
|
*/
|
|
7
7
|
import { type FormatTableOptions } from "../ui/table.js";
|
|
8
8
|
export interface AuditResult {
|
|
@@ -19,23 +19,14 @@ export interface AuditResult {
|
|
|
19
19
|
error?: string;
|
|
20
20
|
}
|
|
21
21
|
export interface FormatHumanOptions {
|
|
22
|
-
/** How to group issues: severity (default), category, route */
|
|
23
22
|
groupBy?: "severity" | "category" | "route";
|
|
24
|
-
/** Whether to show detailed scores table */
|
|
25
23
|
showScores?: boolean;
|
|
26
|
-
/** Whether to show the summary at the end */
|
|
27
24
|
showSummary?: boolean;
|
|
28
|
-
/** Maximum issues to display per group */
|
|
29
25
|
maxIssuesPerGroup?: number;
|
|
30
|
-
/** Table formatting options */
|
|
31
26
|
tableOptions?: FormatTableOptions;
|
|
32
27
|
}
|
|
33
28
|
/**
|
|
34
29
|
* Format audit result as human-readable string.
|
|
35
|
-
*
|
|
36
|
-
* @param result - Audit result object
|
|
37
|
-
* @param options - Formatting options
|
|
38
|
-
* @returns Formatted string for terminal display
|
|
39
30
|
*/
|
|
40
31
|
export declare function formatAuditHuman(result: AuditResult, options?: FormatHumanOptions): string;
|
|
41
32
|
//# sourceMappingURL=human.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"human.d.ts","sourceRoot":"","sources":["../../src/output/human.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;
|
|
1
|
+
{"version":3,"file":"human.d.ts","sourceRoot":"","sources":["../../src/output/human.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AASH,OAAO,EAOL,KAAK,kBAAkB,EACxB,MAAM,gBAAgB,CAAC;AAExB,MAAM,WAAW,WAAW;IAC1B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,kBAAkB;IACjC,OAAO,CAAC,EAAE,UAAU,GAAG,UAAU,GAAG,OAAO,CAAC;IAC5C,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,YAAY,CAAC,EAAE,kBAAkB,CAAC;CACnC;AAuND;;GAEG;AACH,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,WAAW,EACnB,OAAO,GAAE,kBAAuB,GAC/B,MAAM,CAuCR"}
|
package/dist/output/human.js
CHANGED
|
@@ -2,11 +2,10 @@
|
|
|
2
2
|
* Human-readable output formatter for CLI.
|
|
3
3
|
*
|
|
4
4
|
* Outputs audit results as colorful, formatted text for terminal display.
|
|
5
|
-
*
|
|
5
|
+
* Delegates to @vertaaux/tui for consistent rendering.
|
|
6
6
|
*/
|
|
7
|
-
import
|
|
7
|
+
import { text, getTerminalWidth, scoreColor, boldColor, } from "@vertaaux/tui";
|
|
8
8
|
import { formatIssuesTable, formatScoresTable, formatIssueSummary, groupBySeverity, groupByCategory, } from "../ui/table.js";
|
|
9
|
-
import { shouldUseColor, getTerminalWidth } from "../utils/detect-env.js";
|
|
10
9
|
/**
|
|
11
10
|
* Normalize issues from various API response formats.
|
|
12
11
|
*/
|
|
@@ -34,41 +33,22 @@ function getOverallScore(scores) {
|
|
|
34
33
|
return null;
|
|
35
34
|
return Math.round(numeric.reduce((a, b) => a + b, 0) / numeric.length);
|
|
36
35
|
}
|
|
37
|
-
/**
|
|
38
|
-
* Format a horizontal rule.
|
|
39
|
-
*/
|
|
40
|
-
function hr() {
|
|
41
|
-
const width = Math.min(getTerminalWidth(), 80);
|
|
42
|
-
return shouldUseColor()
|
|
43
|
-
? chalk.dim("-".repeat(width))
|
|
44
|
-
: "-".repeat(width);
|
|
45
|
-
}
|
|
46
36
|
/**
|
|
47
37
|
* Format the header section.
|
|
48
38
|
*/
|
|
49
39
|
function formatHeader(result) {
|
|
50
|
-
const useColor = shouldUseColor();
|
|
51
40
|
const lines = [];
|
|
52
|
-
|
|
53
|
-
const title = useColor
|
|
54
|
-
? chalk.bold.cyan("Audit Complete")
|
|
55
|
-
: "AUDIT COMPLETE";
|
|
56
|
-
lines.push(title);
|
|
57
|
-
// URL
|
|
41
|
+
lines.push(text.heading("Audit Complete"));
|
|
58
42
|
if (result.url) {
|
|
59
|
-
|
|
60
|
-
? chalk.dim("URL: ") + chalk.white(result.url)
|
|
61
|
-
: `URL: ${result.url}`;
|
|
62
|
-
lines.push(urlLine);
|
|
43
|
+
lines.push(`${text.dim("URL:")} ${text.link(result.url)}`);
|
|
63
44
|
}
|
|
64
|
-
// Mode and Job ID
|
|
65
45
|
const meta = [];
|
|
66
46
|
if (result.mode)
|
|
67
47
|
meta.push(`Mode: ${result.mode}`);
|
|
68
48
|
if (result.job_id)
|
|
69
49
|
meta.push(`Job: ${result.job_id}`);
|
|
70
50
|
if (meta.length) {
|
|
71
|
-
lines.push(
|
|
51
|
+
lines.push(text.dim(meta.join(" | ")));
|
|
72
52
|
}
|
|
73
53
|
return lines.join("\n");
|
|
74
54
|
}
|
|
@@ -76,34 +56,20 @@ function formatHeader(result) {
|
|
|
76
56
|
* Format the scores section.
|
|
77
57
|
*/
|
|
78
58
|
function formatScoresSection(scores) {
|
|
79
|
-
const useColor = shouldUseColor();
|
|
80
59
|
const lines = [];
|
|
81
60
|
lines.push("");
|
|
82
|
-
lines.push(
|
|
83
|
-
lines.push(hr());
|
|
61
|
+
lines.push(text.bold("Scores"));
|
|
62
|
+
lines.push(text.hr(Math.min(getTerminalWidth(), 80)));
|
|
84
63
|
if (!scores || Object.keys(scores).length === 0) {
|
|
85
|
-
lines.push(
|
|
64
|
+
lines.push(text.dim("No scores available."));
|
|
86
65
|
return lines.join("\n");
|
|
87
66
|
}
|
|
88
|
-
// Overall score with color coding
|
|
89
67
|
const overall = getOverallScore(scores);
|
|
90
68
|
if (overall !== null) {
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
if (overall >= 90) {
|
|
94
|
-
scoreDisplay = chalk.green.bold(scoreDisplay);
|
|
95
|
-
}
|
|
96
|
-
else if (overall >= 70) {
|
|
97
|
-
scoreDisplay = chalk.yellow.bold(scoreDisplay);
|
|
98
|
-
}
|
|
99
|
-
else {
|
|
100
|
-
scoreDisplay = chalk.red.bold(scoreDisplay);
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
lines.push(`Overall: ${scoreDisplay}/100`);
|
|
69
|
+
const scoreStr = boldColor(String(overall), scoreColor(overall));
|
|
70
|
+
lines.push(`Overall: ${scoreStr}/100`);
|
|
104
71
|
lines.push("");
|
|
105
72
|
}
|
|
106
|
-
// Detailed scores table
|
|
107
73
|
const numericScores = {};
|
|
108
74
|
for (const [key, value] of Object.entries(scores)) {
|
|
109
75
|
if (typeof value === "number" && key !== "overall") {
|
|
@@ -119,40 +85,29 @@ function formatScoresSection(scores) {
|
|
|
119
85
|
* Format issues section grouped by severity.
|
|
120
86
|
*/
|
|
121
87
|
function formatIssuesBySeverity(issues, maxPerGroup, tableOptions) {
|
|
122
|
-
const useColor = shouldUseColor();
|
|
123
88
|
const lines = [];
|
|
124
89
|
lines.push("");
|
|
125
|
-
lines.push(
|
|
126
|
-
lines.push(hr());
|
|
90
|
+
lines.push(text.bold("Issues"));
|
|
91
|
+
lines.push(text.hr(Math.min(getTerminalWidth(), 80)));
|
|
127
92
|
if (issues.length === 0) {
|
|
128
|
-
lines.push(
|
|
93
|
+
lines.push(text.success("No issues found!"));
|
|
129
94
|
return lines.join("\n");
|
|
130
95
|
}
|
|
131
|
-
// Group by severity
|
|
132
96
|
const groups = groupBySeverity(issues);
|
|
133
|
-
const
|
|
134
|
-
for (const
|
|
135
|
-
const groupIssues = groups.get(
|
|
97
|
+
const order = ["critical", "error", "serious", "warning", "minor", "moderate", "info"];
|
|
98
|
+
for (const sev of order) {
|
|
99
|
+
const groupIssues = groups.get(sev);
|
|
136
100
|
if (!groupIssues || groupIssues.length === 0)
|
|
137
101
|
continue;
|
|
138
102
|
const displayIssues = maxPerGroup
|
|
139
103
|
? groupIssues.slice(0, maxPerGroup)
|
|
140
104
|
: groupIssues;
|
|
141
|
-
// Section header
|
|
142
|
-
const label = severity.toUpperCase();
|
|
143
|
-
const count = groupIssues.length;
|
|
144
|
-
const header = useColor
|
|
145
|
-
? chalk.bold(`${label} (${count})`)
|
|
146
|
-
: `${label} (${count})`;
|
|
147
105
|
lines.push("");
|
|
148
|
-
lines.push(
|
|
149
|
-
// Issues table for this severity
|
|
106
|
+
lines.push(text.bold(`${text.severityBadge(sev)} (${groupIssues.length})`));
|
|
150
107
|
lines.push(formatIssuesTable(displayIssues, tableOptions));
|
|
151
|
-
// Show truncation message if needed
|
|
152
108
|
if (maxPerGroup && groupIssues.length > maxPerGroup) {
|
|
153
109
|
const remaining = groupIssues.length - maxPerGroup;
|
|
154
|
-
|
|
155
|
-
lines.push(useColor ? chalk.dim(msg) : msg);
|
|
110
|
+
lines.push(text.dim(`...and ${remaining} more ${sev} issues`));
|
|
156
111
|
}
|
|
157
112
|
}
|
|
158
113
|
return lines.join("\n");
|
|
@@ -161,34 +116,25 @@ function formatIssuesBySeverity(issues, maxPerGroup, tableOptions) {
|
|
|
161
116
|
* Format issues section grouped by category.
|
|
162
117
|
*/
|
|
163
118
|
function formatIssuesByCategory(issues, maxPerGroup, tableOptions) {
|
|
164
|
-
const useColor = shouldUseColor();
|
|
165
119
|
const lines = [];
|
|
166
120
|
lines.push("");
|
|
167
|
-
lines.push(
|
|
168
|
-
lines.push(hr());
|
|
121
|
+
lines.push(text.bold("Issues by Category"));
|
|
122
|
+
lines.push(text.hr(Math.min(getTerminalWidth(), 80)));
|
|
169
123
|
if (issues.length === 0) {
|
|
170
|
-
lines.push(
|
|
124
|
+
lines.push(text.success("No issues found!"));
|
|
171
125
|
return lines.join("\n");
|
|
172
126
|
}
|
|
173
|
-
// Group by category
|
|
174
127
|
const groups = groupByCategory(issues);
|
|
175
128
|
for (const [category, groupIssues] of groups) {
|
|
176
129
|
const displayIssues = maxPerGroup
|
|
177
130
|
? groupIssues.slice(0, maxPerGroup)
|
|
178
131
|
: groupIssues;
|
|
179
|
-
// Section header
|
|
180
|
-
const header = useColor
|
|
181
|
-
? chalk.bold(`${category} (${groupIssues.length})`)
|
|
182
|
-
: `${category} (${groupIssues.length})`;
|
|
183
132
|
lines.push("");
|
|
184
|
-
lines.push(
|
|
185
|
-
// Issues table for this category
|
|
133
|
+
lines.push(text.bold(`${category} (${groupIssues.length})`));
|
|
186
134
|
lines.push(formatIssuesTable(displayIssues, tableOptions));
|
|
187
|
-
// Show truncation message if needed
|
|
188
135
|
if (maxPerGroup && groupIssues.length > maxPerGroup) {
|
|
189
136
|
const remaining = groupIssues.length - maxPerGroup;
|
|
190
|
-
|
|
191
|
-
lines.push(useColor ? chalk.dim(msg) : msg);
|
|
137
|
+
lines.push(text.dim(`...and ${remaining} more issues`));
|
|
192
138
|
}
|
|
193
139
|
}
|
|
194
140
|
return lines.join("\n");
|
|
@@ -197,14 +143,11 @@ function formatIssuesByCategory(issues, maxPerGroup, tableOptions) {
|
|
|
197
143
|
* Format the summary section.
|
|
198
144
|
*/
|
|
199
145
|
function formatSummary(issues, scores, threshold) {
|
|
200
|
-
const useColor = shouldUseColor();
|
|
201
146
|
const lines = [];
|
|
202
147
|
lines.push("");
|
|
203
|
-
lines.push(hr());
|
|
204
|
-
// Summary line
|
|
148
|
+
lines.push(text.hr(Math.min(getTerminalWidth(), 80)));
|
|
205
149
|
const summary = formatIssueSummary(issues);
|
|
206
150
|
lines.push(summary);
|
|
207
|
-
// Pass/fail status
|
|
208
151
|
const overall = getOverallScore(scores);
|
|
209
152
|
const hasErrors = issues.some((i) => i.severity?.toLowerCase() === "critical" || i.severity?.toLowerCase() === "error");
|
|
210
153
|
let status;
|
|
@@ -223,50 +166,35 @@ function formatSummary(issues, scores, threshold) {
|
|
|
223
166
|
passed = !hasErrors;
|
|
224
167
|
status = passed ? "PASSED" : "FAILED";
|
|
225
168
|
}
|
|
226
|
-
|
|
227
|
-
status = passed ? chalk.green.bold(status) : chalk.red.bold(status);
|
|
228
|
-
}
|
|
169
|
+
status = passed ? text.success(status) : text.error(status);
|
|
229
170
|
lines.push(status);
|
|
230
171
|
return lines.join("\n");
|
|
231
172
|
}
|
|
232
173
|
/**
|
|
233
174
|
* Format audit result as human-readable string.
|
|
234
|
-
*
|
|
235
|
-
* @param result - Audit result object
|
|
236
|
-
* @param options - Formatting options
|
|
237
|
-
* @returns Formatted string for terminal display
|
|
238
175
|
*/
|
|
239
176
|
export function formatAuditHuman(result, options = {}) {
|
|
240
177
|
const { groupBy = "severity", showScores = true, showSummary = true, maxIssuesPerGroup = 10, tableOptions, } = options;
|
|
241
178
|
const sections = [];
|
|
242
|
-
// Header
|
|
243
179
|
sections.push(formatHeader(result));
|
|
244
|
-
// Handle non-completed audits
|
|
245
180
|
if (result.status !== "completed") {
|
|
246
|
-
const useColor = shouldUseColor();
|
|
247
181
|
sections.push("");
|
|
248
|
-
sections.push(
|
|
249
|
-
? chalk.yellow(`Status: ${result.status || "unknown"}`)
|
|
250
|
-
: `Status: ${result.status || "unknown"}`);
|
|
182
|
+
sections.push(text.warning(`Status: ${result.status || "unknown"}`));
|
|
251
183
|
if (typeof result.progress === "number") {
|
|
252
184
|
sections.push(`Progress: ${result.progress}%`);
|
|
253
185
|
}
|
|
254
186
|
return sections.join("\n");
|
|
255
187
|
}
|
|
256
|
-
// Scores section
|
|
257
188
|
if (showScores) {
|
|
258
189
|
sections.push(formatScoresSection(result.scores));
|
|
259
190
|
}
|
|
260
|
-
// Issues section
|
|
261
191
|
const issues = normalizeIssues(result.issues);
|
|
262
192
|
if (groupBy === "category") {
|
|
263
193
|
sections.push(formatIssuesByCategory(issues, maxIssuesPerGroup, tableOptions));
|
|
264
194
|
}
|
|
265
195
|
else {
|
|
266
|
-
// Default: group by severity
|
|
267
196
|
sections.push(formatIssuesBySeverity(issues, maxIssuesPerGroup, tableOptions));
|
|
268
197
|
}
|
|
269
|
-
// Summary
|
|
270
198
|
if (showSummary) {
|
|
271
199
|
sections.push(formatSummary(issues, result.scores));
|
|
272
200
|
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Policy push/pull sync module for VertaaUX CLI.
|
|
3
|
+
*
|
|
4
|
+
* Provides bidirectional sync between local policy files and the server API.
|
|
5
|
+
* Uses the established CLI API patterns (resolveApiBase, getApiKey, apiRequest).
|
|
6
|
+
*
|
|
7
|
+
* Implements POL-10: Policy push/pull sync.
|
|
8
|
+
*/
|
|
9
|
+
import type { PolicyFile } from "@vertaaux/quality-control";
|
|
10
|
+
import type { VertaauxConfig } from "../config/schema.js";
|
|
11
|
+
export interface SyncConfig {
|
|
12
|
+
apiBase: string;
|
|
13
|
+
apiKey: string;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Build sync config from CLI config and environment.
|
|
17
|
+
* Uses the established CLI pattern: resolveApiBase + getApiKey.
|
|
18
|
+
*/
|
|
19
|
+
export declare function buildSyncConfig(cliConfig?: VertaauxConfig): SyncConfig;
|
|
20
|
+
export interface PushResult {
|
|
21
|
+
success: boolean;
|
|
22
|
+
policyId: string;
|
|
23
|
+
version: number;
|
|
24
|
+
created: boolean;
|
|
25
|
+
error?: string;
|
|
26
|
+
}
|
|
27
|
+
export interface PullResult {
|
|
28
|
+
success: boolean;
|
|
29
|
+
policy?: PolicyFile;
|
|
30
|
+
policyId?: string;
|
|
31
|
+
version?: number;
|
|
32
|
+
name?: string;
|
|
33
|
+
error?: string;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Push a local policy file to the server.
|
|
37
|
+
*
|
|
38
|
+
* If a policy with the given name already exists, updates it (with optional
|
|
39
|
+
* optimistic locking via expectedVersion). Otherwise creates a new policy.
|
|
40
|
+
*
|
|
41
|
+
* @param policy - The policy content to push
|
|
42
|
+
* @param config - API connection config
|
|
43
|
+
* @param options - Push options (name, description, force, etc.)
|
|
44
|
+
* @returns Push result with success status, policy ID, and version
|
|
45
|
+
*/
|
|
46
|
+
export declare function pushPolicy(policy: PolicyFile, config: SyncConfig, options: {
|
|
47
|
+
name: string;
|
|
48
|
+
description?: string;
|
|
49
|
+
changeMessage?: string;
|
|
50
|
+
expectedVersion?: number;
|
|
51
|
+
force?: boolean;
|
|
52
|
+
}): Promise<PushResult>;
|
|
53
|
+
/**
|
|
54
|
+
* Pull a policy from the server.
|
|
55
|
+
*
|
|
56
|
+
* Finds the policy by ID, name, or defaults to the default/first policy.
|
|
57
|
+
* Returns the full policy content for writing to a local file.
|
|
58
|
+
*
|
|
59
|
+
* @param config - API connection config
|
|
60
|
+
* @param options - Pull options (policyId or policyName)
|
|
61
|
+
* @returns Pull result with policy content and metadata
|
|
62
|
+
*/
|
|
63
|
+
export declare function pullPolicy(config: SyncConfig, options: {
|
|
64
|
+
policyId?: string;
|
|
65
|
+
policyName?: string;
|
|
66
|
+
}): Promise<PullResult>;
|
|
67
|
+
//# sourceMappingURL=sync.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sync.d.ts","sourceRoot":"","sources":["../../src/policy/sync.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AAE5D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAE1D,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,SAAS,CAAC,EAAE,cAAc,GAAG,UAAU,CAKtE;AAED,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,UAAU,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AA4BD;;;;;;;;;;GAUG;AACH,wBAAsB,UAAU,CAC9B,MAAM,EAAE,UAAU,EAClB,MAAM,EAAE,UAAU,EAClB,OAAO,EAAE;IACP,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB,GACA,OAAO,CAAC,UAAU,CAAC,CA+ErB;AAED;;;;;;;;;GASG;AACH,wBAAsB,UAAU,CAC9B,MAAM,EAAE,UAAU,EAClB,OAAO,EAAE;IACP,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,GACA,OAAO,CAAC,UAAU,CAAC,CAuDrB"}
|