@vertaaux/cli 0.4.0 → 0.5.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/CHANGELOG.md +97 -0
- package/MIGRATION.md +239 -0
- package/README.md +34 -16
- package/dist/app/interactive-app.d.ts +101 -0
- package/dist/app/interactive-app.d.ts.map +1 -0
- package/dist/app/interactive-app.js +309 -0
- package/dist/app/layout/canvas.d.ts +23 -0
- package/dist/app/layout/canvas.d.ts.map +1 -0
- package/dist/app/layout/canvas.js +36 -0
- package/dist/app/layout/footer.d.ts +31 -0
- package/dist/app/layout/footer.d.ts.map +1 -0
- package/dist/app/layout/footer.js +41 -0
- package/dist/app/layout/header.d.ts +20 -0
- package/dist/app/layout/header.d.ts.map +1 -0
- package/dist/app/layout/header.js +27 -0
- package/dist/app/menu/categories.d.ts +20 -0
- package/dist/app/menu/categories.d.ts.map +1 -0
- package/dist/app/menu/categories.js +181 -0
- package/dist/app/menu/filter.d.ts +17 -0
- package/dist/app/menu/filter.d.ts.map +1 -0
- package/dist/app/menu/filter.js +33 -0
- package/dist/app/menu/menu-view.d.ts +35 -0
- package/dist/app/menu/menu-view.d.ts.map +1 -0
- package/dist/app/menu/menu-view.js +230 -0
- package/dist/app/menu/recent.d.ts +24 -0
- package/dist/app/menu/recent.d.ts.map +1 -0
- package/dist/app/menu/recent.js +49 -0
- package/dist/app/types.d.ts +43 -0
- package/dist/app/types.d.ts.map +1 -0
- package/dist/app/types.js +7 -0
- package/dist/app/views/command-runner.d.ts +36 -0
- package/dist/app/views/command-runner.d.ts.map +1 -0
- package/dist/app/views/command-runner.js +372 -0
- package/dist/app/views/help-overlay.d.ts +21 -0
- package/dist/app/views/help-overlay.d.ts.map +1 -0
- package/dist/app/views/help-overlay.js +45 -0
- package/dist/auth/ci-token.d.ts +8 -2
- package/dist/auth/ci-token.d.ts.map +1 -1
- package/dist/auth/ci-token.js +15 -30
- package/dist/auth/device-flow.d.ts +2 -1
- package/dist/auth/device-flow.d.ts.map +1 -1
- package/dist/auth/device-flow.js +13 -10
- package/dist/auth/token-store.d.ts.map +1 -1
- package/dist/auth/token-store.js +12 -2
- package/dist/baseline/diff.d.ts +2 -2
- package/dist/baseline/diff.d.ts.map +1 -1
- package/dist/baseline/diff.js +15 -34
- package/dist/commands/a11y.d.ts +9 -0
- package/dist/commands/a11y.d.ts.map +1 -0
- package/dist/commands/a11y.js +76 -0
- package/dist/commands/audit/artifacts.d.ts +27 -0
- package/dist/commands/audit/artifacts.d.ts.map +1 -0
- package/dist/commands/audit/artifacts.js +158 -0
- package/dist/commands/audit/ci-detection.d.ts +18 -0
- package/dist/commands/audit/ci-detection.d.ts.map +1 -0
- package/dist/commands/audit/ci-detection.js +71 -0
- package/dist/commands/audit/explain.d.ts +11 -0
- package/dist/commands/audit/explain.d.ts.map +1 -0
- package/dist/commands/audit/explain.js +45 -0
- package/dist/commands/audit/filters.d.ts +17 -0
- package/dist/commands/audit/filters.d.ts.map +1 -0
- package/dist/commands/audit/filters.js +40 -0
- package/dist/commands/audit/index.d.ts +18 -0
- package/dist/commands/audit/index.d.ts.map +1 -0
- package/dist/commands/audit/index.js +564 -0
- package/dist/commands/audit/output.d.ts +32 -0
- package/dist/commands/audit/output.d.ts.map +1 -0
- package/dist/commands/audit/output.js +130 -0
- package/dist/commands/audit/policy.d.ts +19 -0
- package/dist/commands/audit/policy.d.ts.map +1 -0
- package/dist/commands/audit/policy.js +102 -0
- package/dist/commands/audit/scoring.d.ts +23 -0
- package/dist/commands/audit/scoring.d.ts.map +1 -0
- package/dist/commands/audit/scoring.js +70 -0
- package/dist/commands/audit/types.d.ts +88 -0
- package/dist/commands/audit/types.d.ts.map +1 -0
- package/dist/commands/audit/types.js +8 -0
- package/dist/commands/audit.d.ts +2 -60
- package/dist/commands/audit.d.ts.map +1 -1
- package/dist/commands/audit.js +2 -1097
- package/dist/commands/baseline.d.ts +1 -0
- package/dist/commands/baseline.d.ts.map +1 -1
- package/dist/commands/baseline.js +205 -121
- package/dist/commands/comment.d.ts +22 -0
- package/dist/commands/comment.d.ts.map +1 -1
- package/dist/commands/comment.js +122 -58
- package/dist/commands/compare.d.ts +17 -0
- package/dist/commands/compare.d.ts.map +1 -1
- package/dist/commands/compare.js +287 -180
- package/dist/commands/diff.d.ts +5 -0
- package/dist/commands/diff.d.ts.map +1 -1
- package/dist/commands/diff.js +168 -141
- package/dist/commands/doc.d.ts +10 -0
- package/dist/commands/doc.d.ts.map +1 -1
- package/dist/commands/doc.js +134 -76
- package/dist/commands/doctor.d.ts +2 -0
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/doctor.js +164 -17
- package/dist/commands/download.d.ts +10 -0
- package/dist/commands/download.d.ts.map +1 -1
- package/dist/commands/download.js +169 -112
- package/dist/commands/explain.d.ts +5 -0
- package/dist/commands/explain.d.ts.map +1 -1
- package/dist/commands/explain.js +241 -155
- package/dist/commands/fix-all.d.ts +25 -0
- package/dist/commands/fix-all.d.ts.map +1 -0
- package/dist/commands/fix-all.js +206 -0
- package/dist/commands/fix-plan.d.ts +9 -0
- package/dist/commands/fix-plan.d.ts.map +1 -1
- package/dist/commands/fix-plan.js +152 -89
- package/dist/commands/fix.d.ts +17 -0
- package/dist/commands/fix.d.ts.map +1 -0
- package/dist/commands/fix.js +111 -0
- package/dist/commands/init.d.ts +11 -0
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +94 -42
- package/dist/commands/login.d.ts +18 -0
- package/dist/commands/login.d.ts.map +1 -1
- package/dist/commands/login.js +263 -92
- package/dist/commands/patch-review.d.ts +11 -0
- package/dist/commands/patch-review.d.ts.map +1 -1
- package/dist/commands/patch-review.js +159 -97
- package/dist/commands/policy.d.ts +31 -0
- package/dist/commands/policy.d.ts.map +1 -1
- package/dist/commands/policy.js +269 -124
- package/dist/commands/release-notes.d.ts +10 -0
- package/dist/commands/release-notes.d.ts.map +1 -1
- package/dist/commands/release-notes.js +127 -73
- package/dist/commands/scan.d.ts +13 -0
- package/dist/commands/scan.d.ts.map +1 -0
- package/dist/commands/scan.js +133 -0
- package/dist/commands/status.d.ts +9 -0
- package/dist/commands/status.d.ts.map +1 -0
- package/dist/commands/status.js +81 -0
- package/dist/commands/suggest.d.ts +10 -0
- package/dist/commands/suggest.d.ts.map +1 -1
- package/dist/commands/suggest.js +153 -82
- package/dist/commands/triage.d.ts +35 -0
- package/dist/commands/triage.d.ts.map +1 -1
- package/dist/commands/triage.js +206 -81
- package/dist/commands/upload.d.ts +9 -0
- package/dist/commands/upload.d.ts.map +1 -1
- package/dist/commands/upload.js +140 -101
- package/dist/commands/verify.d.ts +13 -0
- package/dist/commands/verify.d.ts.map +1 -0
- package/dist/commands/verify.js +118 -0
- package/dist/index.d.ts +3 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +125 -990
- package/dist/interactive/fix-wizard.d.ts +3 -0
- package/dist/interactive/fix-wizard.d.ts.map +1 -1
- package/dist/interactive/fix-wizard.js +130 -112
- package/dist/interactive/init-wizard.d.ts +3 -1
- package/dist/interactive/init-wizard.d.ts.map +1 -1
- package/dist/interactive/init-wizard.js +207 -138
- package/dist/interactive/prompts.d.ts +7 -3
- package/dist/interactive/prompts.d.ts.map +1 -1
- package/dist/interactive/prompts.js +44 -23
- package/dist/output/envelope.d.ts +2 -0
- package/dist/output/envelope.d.ts.map +1 -1
- package/dist/output/envelope.js +18 -2
- package/dist/output/factory.d.ts +2 -1
- package/dist/output/factory.d.ts.map +1 -1
- package/dist/output/html.d.ts +2 -1
- package/dist/output/html.d.ts.map +1 -1
- package/dist/output/html.js +3 -2
- package/dist/output/human.d.ts +2 -1
- package/dist/output/human.d.ts.map +1 -1
- package/dist/output/human.js +3 -2
- package/dist/output/json.d.ts +2 -1
- package/dist/output/json.d.ts.map +1 -1
- package/dist/output/junit.d.ts +2 -1
- package/dist/output/junit.d.ts.map +1 -1
- package/dist/output/sarif.d.ts +2 -1
- package/dist/output/sarif.d.ts.map +1 -1
- package/dist/types.d.ts +74 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +5 -0
- package/dist/ui/banner.d.ts +34 -0
- package/dist/ui/banner.d.ts.map +1 -1
- package/dist/ui/banner.js +97 -5
- package/dist/ui/diagnostics.d.ts +9 -4
- package/dist/ui/diagnostics.d.ts.map +1 -1
- package/dist/ui/diagnostics.js +32 -82
- package/dist/ui/strings.d.ts +373 -0
- package/dist/ui/strings.d.ts.map +1 -0
- package/dist/ui/strings.js +499 -0
- package/dist/ui/table.d.ts +0 -2
- package/dist/ui/table.d.ts.map +1 -1
- package/dist/ui/table.js +3 -4
- package/dist/utils/api-client.d.ts +46 -0
- package/dist/utils/api-client.d.ts.map +1 -0
- package/dist/utils/api-client.js +170 -0
- package/dist/utils/client.d.ts +29 -18
- package/dist/utils/client.d.ts.map +1 -1
- package/dist/utils/client.js +102 -12
- package/dist/utils/formatters.d.ts +38 -0
- package/dist/utils/formatters.d.ts.map +1 -0
- package/dist/utils/formatters.js +277 -0
- package/dist/utils/url-classify.d.ts.map +1 -1
- package/dist/utils/url-classify.js +24 -3
- package/node_modules/@vertaaux/tui/dist/index.cjs +713 -20
- package/node_modules/@vertaaux/tui/dist/index.cjs.map +1 -1
- package/node_modules/@vertaaux/tui/dist/index.d.cts +361 -4
- package/node_modules/@vertaaux/tui/dist/index.d.ts +361 -4
- package/node_modules/@vertaaux/tui/dist/index.js +689 -21
- package/node_modules/@vertaaux/tui/dist/index.js.map +1 -1
- package/package.json +13 -5
- package/dist/commands/client.d.ts +0 -14
- package/dist/commands/client.d.ts.map +0 -1
- package/dist/commands/client.js +0 -362
- package/dist/commands/drift.d.ts +0 -15
- package/dist/commands/drift.d.ts.map +0 -1
- package/dist/commands/drift.js +0 -309
- package/dist/commands/protect.d.ts +0 -16
- package/dist/commands/protect.d.ts.map +0 -1
- package/dist/commands/protect.js +0 -323
- package/dist/commands/report.d.ts +0 -15
- package/dist/commands/report.d.ts.map +0 -1
- package/dist/commands/report.js +0 -214
- package/dist/policy/sync.d.ts +0 -67
- package/dist/policy/sync.d.ts.map +0 -1
- package/dist/policy/sync.js +0 -147
|
@@ -16,5 +16,22 @@
|
|
|
16
16
|
* vertaa compare --before baseline.json --after current.json --verbose
|
|
17
17
|
*/
|
|
18
18
|
import { Command } from "commander";
|
|
19
|
+
export interface CompareCommandOptions {
|
|
20
|
+
urlA?: string;
|
|
21
|
+
urlB?: string;
|
|
22
|
+
before?: string;
|
|
23
|
+
after?: string;
|
|
24
|
+
mode?: string;
|
|
25
|
+
wait?: boolean;
|
|
26
|
+
timeout?: string;
|
|
27
|
+
interval?: string;
|
|
28
|
+
failOnScore?: string;
|
|
29
|
+
format?: string;
|
|
30
|
+
base?: string;
|
|
31
|
+
verbose?: boolean;
|
|
32
|
+
machine?: boolean;
|
|
33
|
+
apiKey?: string;
|
|
34
|
+
}
|
|
35
|
+
export declare function handleCompare(opts: CompareCommandOptions): Promise<void>;
|
|
19
36
|
export declare function registerCompareCommand(program: Command): void;
|
|
20
37
|
//# sourceMappingURL=compare.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"compare.d.ts","sourceRoot":"","sources":["../../src/commands/compare.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"compare.d.ts","sourceRoot":"","sources":["../../src/commands/compare.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA6MpC,MAAM,WAAW,qBAAqB;IACpC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,wBAAsB,aAAa,CAAC,IAAI,EAAE,qBAAqB,GAAG,OAAO,CAAC,IAAI,CAAC,CAmR9E;AAMD,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAuE7D"}
|
package/dist/commands/compare.js
CHANGED
|
@@ -15,15 +15,15 @@
|
|
|
15
15
|
* vertaa compare --before baseline.json --after current.json
|
|
16
16
|
* vertaa compare --before baseline.json --after current.json --verbose
|
|
17
17
|
*/
|
|
18
|
-
import
|
|
18
|
+
import { bold, dim, colorize, boldColor, scoreColor, brand, severity as severityPalette, renderError, createRenderer, runSteps } from "@vertaaux/tui";
|
|
19
19
|
import { ExitCode } from "../utils/exit-codes.js";
|
|
20
|
-
import { resolveApiBase, getApiKey, apiRequest, waitForAudit, } from "../utils/client.js";
|
|
20
|
+
import { resolveApiBase, getApiKey, apiRequest, waitForAudit, createClient, } from "../utils/client.js";
|
|
21
21
|
import { resolveConfig } from "../config/loader.js";
|
|
22
22
|
import { writeJsonOutput, writeOutput } from "../output/envelope.js";
|
|
23
23
|
import { resolveCommandFormat } from "../output/formats.js";
|
|
24
|
-
import { createSpinner, succeedSpinner, failSpinner } from "../ui/spinner.js";
|
|
25
24
|
import { readJsonInput } from "../utils/stdin.js";
|
|
26
|
-
import {
|
|
25
|
+
import { AI_TIMEOUT_MS } from "../utils/ai-error.js";
|
|
26
|
+
import { strings } from "../ui/strings.js";
|
|
27
27
|
// ---------------------------------------------------------------------------
|
|
28
28
|
// Helpers
|
|
29
29
|
// ---------------------------------------------------------------------------
|
|
@@ -60,10 +60,11 @@ function toNumber(value) {
|
|
|
60
60
|
function getOverallScore(scores) {
|
|
61
61
|
if (!scores)
|
|
62
62
|
return null;
|
|
63
|
-
const
|
|
63
|
+
const s = scores;
|
|
64
|
+
const direct = toNumber(s.overall ?? s.ux ?? s.total);
|
|
64
65
|
if (direct !== null)
|
|
65
66
|
return direct;
|
|
66
|
-
const numeric = Object.values(
|
|
67
|
+
const numeric = Object.values(s)
|
|
67
68
|
.map((v) => toNumber(v))
|
|
68
69
|
.filter((v) => v !== null);
|
|
69
70
|
if (numeric.length === 0)
|
|
@@ -71,45 +72,38 @@ function getOverallScore(scores) {
|
|
|
71
72
|
const avg = numeric.reduce((sum, v) => sum + v, 0) / numeric.length;
|
|
72
73
|
return Math.round(avg);
|
|
73
74
|
}
|
|
74
|
-
function buildAuditPayload(result, fallbackJobId) {
|
|
75
|
-
const issues = normalizeIssues(result.issues);
|
|
76
|
-
return {
|
|
77
|
-
job_id: result.job_id || fallbackJobId || null,
|
|
78
|
-
url: result.url || null,
|
|
79
|
-
scores: result.scores || null,
|
|
80
|
-
issues,
|
|
81
|
-
};
|
|
82
|
-
}
|
|
83
75
|
// ---------------------------------------------------------------------------
|
|
84
76
|
// Formatters — Legacy URL comparison
|
|
85
77
|
// ---------------------------------------------------------------------------
|
|
86
78
|
function formatLegacyCompareHuman(data) {
|
|
87
79
|
const lines = [];
|
|
88
|
-
lines.push(
|
|
89
|
-
lines.push(` URL A: ${
|
|
90
|
-
lines.push(` URL B: ${
|
|
80
|
+
lines.push(bold("Audit Comparison"));
|
|
81
|
+
lines.push(` URL A: ${colorize(data.urlA, brand.cyan)}`);
|
|
82
|
+
lines.push(` URL B: ${colorize(data.urlB, brand.cyan)}`);
|
|
91
83
|
lines.push("");
|
|
92
84
|
// Overall scores
|
|
93
85
|
const deltaStr = data.delta !== null
|
|
94
|
-
? (data.delta >= 0 ?
|
|
95
|
-
:
|
|
96
|
-
lines.push(
|
|
97
|
-
|
|
86
|
+
? (data.delta >= 0 ? colorize(`+${data.delta}`, brand.lime) : colorize(`${data.delta}`, severityPalette.error))
|
|
87
|
+
: dim("n/a");
|
|
88
|
+
lines.push(bold("Overall Scores"));
|
|
89
|
+
const scoreA = data.overallA !== null ? boldColor(String(data.overallA), scoreColor(data.overallA)) : dim("n/a");
|
|
90
|
+
const scoreB = data.overallB !== null ? boldColor(String(data.overallB), scoreColor(data.overallB)) : dim("n/a");
|
|
91
|
+
lines.push(` A: ${scoreA} B: ${scoreB} Delta: ${deltaStr}`);
|
|
98
92
|
lines.push("");
|
|
99
93
|
// Category deltas
|
|
100
94
|
const entries = Object.entries(data.categoryDeltas);
|
|
101
95
|
if (entries.length > 0) {
|
|
102
|
-
lines.push(
|
|
96
|
+
lines.push(bold("Category Scores"));
|
|
103
97
|
for (const [key, vals] of entries) {
|
|
104
98
|
const d = vals.delta !== null
|
|
105
|
-
? (vals.delta >= 0 ?
|
|
106
|
-
:
|
|
99
|
+
? (vals.delta >= 0 ? colorize(`+${vals.delta}`, brand.lime) : colorize(`${vals.delta}`, severityPalette.error))
|
|
100
|
+
: dim("n/a");
|
|
107
101
|
lines.push(` ${key}: ${vals.a ?? "n/a"} → ${vals.b ?? "n/a"} (${d})`);
|
|
108
102
|
}
|
|
109
103
|
lines.push("");
|
|
110
104
|
}
|
|
111
105
|
// Issue counts
|
|
112
|
-
lines.push(
|
|
106
|
+
lines.push(bold("Issue Counts"));
|
|
113
107
|
lines.push(` A: ${data.issuesA} B: ${data.issuesB}`);
|
|
114
108
|
return lines.join("\n");
|
|
115
109
|
}
|
|
@@ -118,43 +112,280 @@ function formatLegacyCompareHuman(data) {
|
|
|
118
112
|
// ---------------------------------------------------------------------------
|
|
119
113
|
function formatLlmCompareHuman(data, verbose) {
|
|
120
114
|
const lines = [];
|
|
121
|
-
lines.push(
|
|
115
|
+
lines.push(bold(data.headline));
|
|
122
116
|
lines.push("");
|
|
123
117
|
// Score delta
|
|
124
118
|
const d = data.score_delta.overall;
|
|
125
|
-
const deltaStr = d >= 0 ?
|
|
119
|
+
const deltaStr = d >= 0 ? colorize(`+${d}`, brand.lime) : colorize(`${d}`, severityPalette.error);
|
|
126
120
|
lines.push(`Overall delta: ${deltaStr}`);
|
|
127
121
|
if (data.score_delta.categories && verbose) {
|
|
128
122
|
for (const [cat, val] of Object.entries(data.score_delta.categories)) {
|
|
129
|
-
const catDelta = val >= 0 ?
|
|
123
|
+
const catDelta = val >= 0 ? colorize(`+${val}`, brand.lime) : colorize(`${val}`, severityPalette.error);
|
|
130
124
|
lines.push(` ${cat}: ${catDelta}`);
|
|
131
125
|
}
|
|
132
126
|
}
|
|
133
127
|
lines.push("");
|
|
134
128
|
// Improvements
|
|
135
129
|
if (data.improvements.length > 0) {
|
|
136
|
-
lines.push(
|
|
130
|
+
lines.push(boldColor(`Improvements (${data.improvements.length})`, brand.lime));
|
|
137
131
|
for (const item of data.improvements) {
|
|
138
|
-
lines.push(` ${
|
|
132
|
+
lines.push(` ${colorize("+", brand.lime)} ${item}`);
|
|
139
133
|
}
|
|
140
134
|
lines.push("");
|
|
141
135
|
}
|
|
142
136
|
// Regressions
|
|
143
137
|
if (data.regressions.length > 0) {
|
|
144
|
-
lines.push(
|
|
138
|
+
lines.push(boldColor(`Regressions (${data.regressions.length})`, severityPalette.error));
|
|
145
139
|
for (const item of data.regressions) {
|
|
146
|
-
lines.push(` ${
|
|
140
|
+
lines.push(` ${colorize("-", severityPalette.error)} ${item}`);
|
|
147
141
|
}
|
|
148
142
|
lines.push("");
|
|
149
143
|
}
|
|
150
|
-
lines.push(
|
|
144
|
+
lines.push(dim(`Unchanged: ${data.unchanged}`));
|
|
151
145
|
if (verbose) {
|
|
152
146
|
lines.push("");
|
|
153
|
-
lines.push(
|
|
147
|
+
lines.push(bold("Analysis"));
|
|
154
148
|
lines.push(data.narrative);
|
|
155
149
|
}
|
|
156
150
|
return lines.join("\n");
|
|
157
151
|
}
|
|
152
|
+
export async function handleCompare(opts) {
|
|
153
|
+
const config = { apiKey: opts.apiKey };
|
|
154
|
+
const format = resolveCommandFormat("compare", opts.format, opts.machine || false);
|
|
155
|
+
const verbose = opts.verbose || false;
|
|
156
|
+
const isFileMode = opts.before || opts.after;
|
|
157
|
+
const renderer = createRenderer("auto");
|
|
158
|
+
const baseState = {
|
|
159
|
+
phase: "compare",
|
|
160
|
+
phaseIndex: 1,
|
|
161
|
+
phaseTotal: isFileMode ? 1 : 3,
|
|
162
|
+
url: opts.urlA || "",
|
|
163
|
+
mode: "compare",
|
|
164
|
+
progress: {},
|
|
165
|
+
totals: {},
|
|
166
|
+
issueCount: 0,
|
|
167
|
+
scorePreview: null,
|
|
168
|
+
verbose: false,
|
|
169
|
+
elapsed: 0,
|
|
170
|
+
};
|
|
171
|
+
const startTime = Date.now();
|
|
172
|
+
if (isFileMode) {
|
|
173
|
+
// File-based LLM comparison
|
|
174
|
+
let responseData = null;
|
|
175
|
+
const steps = [
|
|
176
|
+
{
|
|
177
|
+
id: "fetch-a",
|
|
178
|
+
actionText: "Loading audit files...",
|
|
179
|
+
summaryText: "Audit files loaded",
|
|
180
|
+
run: async () => {
|
|
181
|
+
if (!opts.before || !opts.after) {
|
|
182
|
+
throw new Error(strings.compare.errors.beforeAfterRequired);
|
|
183
|
+
}
|
|
184
|
+
const beforeInput = await readJsonInput(opts.before);
|
|
185
|
+
const afterInput = await readJsonInput(opts.after);
|
|
186
|
+
if (!beforeInput || !afterInput) {
|
|
187
|
+
throw new Error(strings.compare.errors.readFailed);
|
|
188
|
+
}
|
|
189
|
+
function extractPayload(input) {
|
|
190
|
+
const data = input;
|
|
191
|
+
const inner = (data.data && typeof data.data === "object" ? data.data : data);
|
|
192
|
+
const issues = normalizeIssues(inner.issues);
|
|
193
|
+
return {
|
|
194
|
+
job_id: inner.job_id || null,
|
|
195
|
+
url: inner.url || null,
|
|
196
|
+
scores: inner.scores || null,
|
|
197
|
+
issues,
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
const beforePayload = extractPayload(beforeInput);
|
|
201
|
+
const afterPayload = extractPayload(afterInput);
|
|
202
|
+
const base = resolveApiBase(opts.base);
|
|
203
|
+
const apiKey = getApiKey(config.apiKey);
|
|
204
|
+
const response = await Promise.race([
|
|
205
|
+
apiRequest(base, "/cli/ai/compare", { method: "POST", body: { before: beforePayload, after: afterPayload } }, apiKey),
|
|
206
|
+
new Promise((_, reject) => setTimeout(() => reject(new Error("LLM request timed out")), AI_TIMEOUT_MS)),
|
|
207
|
+
]);
|
|
208
|
+
responseData = response.data;
|
|
209
|
+
},
|
|
210
|
+
},
|
|
211
|
+
{
|
|
212
|
+
id: "compare",
|
|
213
|
+
actionText: strings.compare.analyze.action,
|
|
214
|
+
summaryText: strings.compare.analyze.done(),
|
|
215
|
+
run: async () => {
|
|
216
|
+
// Analysis was performed in fetch-a step; this step finalizes output
|
|
217
|
+
},
|
|
218
|
+
},
|
|
219
|
+
];
|
|
220
|
+
const { success, states } = await runSteps(steps, {
|
|
221
|
+
onStateChange: (stepStates) => {
|
|
222
|
+
renderer.update({ ...baseState, stepStates, elapsed: Date.now() - startTime });
|
|
223
|
+
},
|
|
224
|
+
});
|
|
225
|
+
renderer.finish({
|
|
226
|
+
url: opts.urlA || "",
|
|
227
|
+
mode: "compare",
|
|
228
|
+
overallScore: success ? 100 : 0,
|
|
229
|
+
scores: {},
|
|
230
|
+
issueCount: 0,
|
|
231
|
+
passed: success,
|
|
232
|
+
elapsed: Date.now() - startTime,
|
|
233
|
+
});
|
|
234
|
+
if (!success) {
|
|
235
|
+
const failed = states.find(s => s.status === "failed");
|
|
236
|
+
process.stderr.write(renderError({
|
|
237
|
+
message: failed?.failReason || "Command failed",
|
|
238
|
+
suggestion: "vertaa doctor",
|
|
239
|
+
}) + "\n");
|
|
240
|
+
process.exitCode = ExitCode.ERROR;
|
|
241
|
+
}
|
|
242
|
+
if (success && responseData) {
|
|
243
|
+
if (format === "json") {
|
|
244
|
+
writeJsonOutput(responseData, "compare");
|
|
245
|
+
}
|
|
246
|
+
else {
|
|
247
|
+
writeOutput(formatLlmCompareHuman(responseData, verbose));
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
else if (opts.urlA && opts.urlB) {
|
|
252
|
+
const urlA = opts.urlA;
|
|
253
|
+
const urlB = opts.urlB;
|
|
254
|
+
let compareOutput = null;
|
|
255
|
+
let compareJsonPayload = null;
|
|
256
|
+
let jobARef = null;
|
|
257
|
+
let jobBRef = null;
|
|
258
|
+
const steps = [
|
|
259
|
+
{
|
|
260
|
+
id: "fetch-a",
|
|
261
|
+
actionText: strings.compare.auditA.action,
|
|
262
|
+
summaryText: strings.compare.auditA.done(),
|
|
263
|
+
run: async () => {
|
|
264
|
+
const sdkClient = createClient({ base: opts.base, apiKey: config.apiKey });
|
|
265
|
+
const mode = opts.mode || "basic";
|
|
266
|
+
jobARef = await sdkClient.audits.create({ url: urlA, mode: mode });
|
|
267
|
+
},
|
|
268
|
+
},
|
|
269
|
+
{
|
|
270
|
+
id: "fetch-b",
|
|
271
|
+
actionText: strings.compare.auditB.action,
|
|
272
|
+
summaryText: strings.compare.auditB.done(),
|
|
273
|
+
run: async () => {
|
|
274
|
+
const sdkClient = createClient({ base: opts.base, apiKey: config.apiKey });
|
|
275
|
+
const mode = opts.mode || "basic";
|
|
276
|
+
jobBRef = await sdkClient.audits.create({ url: urlB, mode: mode });
|
|
277
|
+
},
|
|
278
|
+
},
|
|
279
|
+
{
|
|
280
|
+
id: "compare",
|
|
281
|
+
actionText: strings.compare.analyze.action,
|
|
282
|
+
summaryText: strings.compare.analyze.done(),
|
|
283
|
+
run: async () => {
|
|
284
|
+
if (!jobARef || !jobBRef) {
|
|
285
|
+
throw new Error(strings.compare.errors.missingJobIds);
|
|
286
|
+
}
|
|
287
|
+
const jobA = jobARef;
|
|
288
|
+
const jobB = jobBRef;
|
|
289
|
+
if (!opts.wait) {
|
|
290
|
+
const payload = { job_a: jobA, job_b: jobB };
|
|
291
|
+
if (format === "json") {
|
|
292
|
+
compareJsonPayload = payload;
|
|
293
|
+
}
|
|
294
|
+
else {
|
|
295
|
+
compareOutput = `Audit comparison queued:\n Job A: ${jobA.job_id}\n Job B: ${jobB.job_id}`;
|
|
296
|
+
}
|
|
297
|
+
return;
|
|
298
|
+
}
|
|
299
|
+
if (!jobA.job_id || !jobB.job_id) {
|
|
300
|
+
throw new Error(strings.compare.errors.missingJobIds);
|
|
301
|
+
}
|
|
302
|
+
const timeout = parseInt(opts.timeout || "60000", 10);
|
|
303
|
+
const interval = parseInt(opts.interval || "5000", 10);
|
|
304
|
+
const sdkClientWait = createClient({ base: opts.base, apiKey: config.apiKey });
|
|
305
|
+
const [resultA, resultB] = await Promise.all([
|
|
306
|
+
waitForAudit(sdkClientWait, jobA.job_id, timeout, interval),
|
|
307
|
+
waitForAudit(sdkClientWait, jobB.job_id, timeout, interval),
|
|
308
|
+
]);
|
|
309
|
+
const overallA = getOverallScore(resultA.scores);
|
|
310
|
+
const overallB = getOverallScore(resultB.scores);
|
|
311
|
+
const delta = overallA !== null && overallB !== null ? overallB - overallA : null;
|
|
312
|
+
const scoresA = (resultA.scores || {});
|
|
313
|
+
const scoresB = (resultB.scores || {});
|
|
314
|
+
const keys = new Set([...Object.keys(scoresA), ...Object.keys(scoresB)]);
|
|
315
|
+
const categoryDeltas = {};
|
|
316
|
+
for (const key of keys) {
|
|
317
|
+
const a = toNumber(scoresA[key]);
|
|
318
|
+
const b = toNumber(scoresB[key]);
|
|
319
|
+
categoryDeltas[key] = {
|
|
320
|
+
a,
|
|
321
|
+
b,
|
|
322
|
+
delta: a !== null && b !== null ? b - a : null,
|
|
323
|
+
};
|
|
324
|
+
}
|
|
325
|
+
const issuesA = normalizeIssues(resultA.issues).length;
|
|
326
|
+
const issuesB = normalizeIssues(resultB.issues).length;
|
|
327
|
+
const data = {
|
|
328
|
+
urlA,
|
|
329
|
+
urlB,
|
|
330
|
+
jobA: resultA.job_id || "",
|
|
331
|
+
jobB: resultB.job_id || "",
|
|
332
|
+
overallA,
|
|
333
|
+
overallB,
|
|
334
|
+
delta,
|
|
335
|
+
categoryDeltas,
|
|
336
|
+
issuesA,
|
|
337
|
+
issuesB,
|
|
338
|
+
};
|
|
339
|
+
const failOnScore = opts.failOnScore ? parseInt(opts.failOnScore, 10) : undefined;
|
|
340
|
+
if (failOnScore !== undefined &&
|
|
341
|
+
((overallA !== null && overallA < failOnScore) ||
|
|
342
|
+
(overallB !== null && overallB < failOnScore))) {
|
|
343
|
+
process.exitCode = 1;
|
|
344
|
+
}
|
|
345
|
+
if (format === "json") {
|
|
346
|
+
compareJsonPayload = data;
|
|
347
|
+
}
|
|
348
|
+
else {
|
|
349
|
+
compareOutput = formatLegacyCompareHuman(data);
|
|
350
|
+
}
|
|
351
|
+
},
|
|
352
|
+
},
|
|
353
|
+
];
|
|
354
|
+
const { success: urlSuccess, states: urlStates } = await runSteps(steps, {
|
|
355
|
+
onStateChange: (stepStates) => {
|
|
356
|
+
renderer.update({ ...baseState, stepStates, elapsed: Date.now() - startTime });
|
|
357
|
+
},
|
|
358
|
+
});
|
|
359
|
+
renderer.finish({
|
|
360
|
+
url: urlA,
|
|
361
|
+
mode: "compare",
|
|
362
|
+
overallScore: urlSuccess ? 100 : 0,
|
|
363
|
+
scores: {},
|
|
364
|
+
issueCount: 0,
|
|
365
|
+
passed: urlSuccess,
|
|
366
|
+
elapsed: Date.now() - startTime,
|
|
367
|
+
});
|
|
368
|
+
if (!urlSuccess) {
|
|
369
|
+
const failed = urlStates.find(s => s.status === "failed");
|
|
370
|
+
process.stderr.write(renderError({
|
|
371
|
+
message: failed?.failReason || "Command failed",
|
|
372
|
+
suggestion: "vertaa doctor",
|
|
373
|
+
}) + "\n");
|
|
374
|
+
process.exitCode = ExitCode.ERROR;
|
|
375
|
+
}
|
|
376
|
+
if (urlSuccess) {
|
|
377
|
+
if (format === "json" && compareJsonPayload !== null) {
|
|
378
|
+
writeJsonOutput(compareJsonPayload, "compare");
|
|
379
|
+
}
|
|
380
|
+
else if (compareOutput !== null) {
|
|
381
|
+
writeOutput(compareOutput);
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
else {
|
|
386
|
+
throw new Error(strings.compare.errors.urlsOrFilesRequired);
|
|
387
|
+
}
|
|
388
|
+
}
|
|
158
389
|
// ---------------------------------------------------------------------------
|
|
159
390
|
// Command Registration
|
|
160
391
|
// ---------------------------------------------------------------------------
|
|
@@ -182,154 +413,30 @@ Examples:
|
|
|
182
413
|
const config = await resolveConfig(globalOpts.config);
|
|
183
414
|
const machineMode = globalOpts.machine || false;
|
|
184
415
|
const verbose = globalOpts.verbose || false;
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
}
|
|
416
|
+
await handleCompare({
|
|
417
|
+
urlA,
|
|
418
|
+
urlB,
|
|
419
|
+
before: options.before,
|
|
420
|
+
after: options.after,
|
|
421
|
+
mode: options.mode,
|
|
422
|
+
wait: options.wait,
|
|
423
|
+
timeout: options.timeout,
|
|
424
|
+
interval: options.interval,
|
|
425
|
+
failOnScore: options.failOnScore,
|
|
426
|
+
format: options.format,
|
|
427
|
+
base: globalOpts.base,
|
|
428
|
+
verbose,
|
|
429
|
+
machine: machineMode,
|
|
430
|
+
apiKey: config.apiKey,
|
|
431
|
+
});
|
|
201
432
|
}
|
|
202
433
|
catch (error) {
|
|
203
|
-
|
|
434
|
+
process.stderr.write(renderError({
|
|
435
|
+
message: error instanceof Error ? error.message : String(error),
|
|
436
|
+
suggestion: "vertaa audit <url>",
|
|
437
|
+
exitCode: ExitCode.ERROR,
|
|
438
|
+
}) + "\n");
|
|
204
439
|
process.exit(ExitCode.ERROR);
|
|
205
440
|
}
|
|
206
441
|
});
|
|
207
442
|
}
|
|
208
|
-
// ---------------------------------------------------------------------------
|
|
209
|
-
// LLM-based comparison (new)
|
|
210
|
-
// ---------------------------------------------------------------------------
|
|
211
|
-
async function runLlmCompare(options, globalOpts, config, format, verbose) {
|
|
212
|
-
if (!options.before || !options.after) {
|
|
213
|
-
console.error("Error: Both --before and --after are required for file-based comparison.");
|
|
214
|
-
process.exit(ExitCode.ERROR);
|
|
215
|
-
}
|
|
216
|
-
const beforeInput = await readJsonInput(options.before);
|
|
217
|
-
const afterInput = await readJsonInput(options.after);
|
|
218
|
-
if (!beforeInput || !afterInput) {
|
|
219
|
-
console.error("Error: Could not read audit files.");
|
|
220
|
-
process.exit(ExitCode.ERROR);
|
|
221
|
-
}
|
|
222
|
-
function extractPayload(input) {
|
|
223
|
-
const data = input;
|
|
224
|
-
const inner = (data.data && typeof data.data === "object" ? data.data : data);
|
|
225
|
-
const issues = normalizeIssues(inner.issues);
|
|
226
|
-
return {
|
|
227
|
-
job_id: inner.job_id || null,
|
|
228
|
-
url: inner.url || null,
|
|
229
|
-
scores: inner.scores || null,
|
|
230
|
-
issues,
|
|
231
|
-
};
|
|
232
|
-
}
|
|
233
|
-
const beforePayload = extractPayload(beforeInput);
|
|
234
|
-
const afterPayload = extractPayload(afterInput);
|
|
235
|
-
const base = resolveApiBase(globalOpts.base);
|
|
236
|
-
const apiKey = getApiKey(config.apiKey);
|
|
237
|
-
const spinner = createSpinner("Comparing audits...");
|
|
238
|
-
try {
|
|
239
|
-
const response = await Promise.race([
|
|
240
|
-
apiRequest(base, "/cli/ai/compare", { method: "POST", body: { before: beforePayload, after: afterPayload } }, apiKey),
|
|
241
|
-
new Promise((_, reject) => setTimeout(() => reject(new Error("LLM request timed out")), AI_TIMEOUT_MS)),
|
|
242
|
-
]);
|
|
243
|
-
succeedSpinner(spinner, "Comparison complete");
|
|
244
|
-
if (format === "json") {
|
|
245
|
-
writeJsonOutput(response.data, "compare");
|
|
246
|
-
}
|
|
247
|
-
else {
|
|
248
|
-
writeOutput(formatLlmCompareHuman(response.data, verbose));
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
catch (error) {
|
|
252
|
-
handleAiCommandError(error, "compare", spinner);
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
// ---------------------------------------------------------------------------
|
|
256
|
-
// URL-based comparison (legacy backward compat)
|
|
257
|
-
// ---------------------------------------------------------------------------
|
|
258
|
-
async function runUrlCompare(urlA, urlB, options, globalOpts, config, format) {
|
|
259
|
-
const base = resolveApiBase(globalOpts.base);
|
|
260
|
-
const apiKey = getApiKey(config.apiKey);
|
|
261
|
-
const mode = options.mode || "basic";
|
|
262
|
-
const spinner = createSpinner("Starting audits...");
|
|
263
|
-
const jobA = await apiRequest(base, "/audit", {
|
|
264
|
-
method: "POST",
|
|
265
|
-
body: { url: urlA, mode },
|
|
266
|
-
}, apiKey);
|
|
267
|
-
const jobB = await apiRequest(base, "/audit", {
|
|
268
|
-
method: "POST",
|
|
269
|
-
body: { url: urlB, mode },
|
|
270
|
-
}, apiKey);
|
|
271
|
-
if (!options.wait) {
|
|
272
|
-
succeedSpinner(spinner, "Audits queued");
|
|
273
|
-
const payload = { job_a: jobA, job_b: jobB };
|
|
274
|
-
if (format === "json") {
|
|
275
|
-
writeJsonOutput(payload, "compare");
|
|
276
|
-
}
|
|
277
|
-
else {
|
|
278
|
-
writeOutput(`Audit comparison queued:\n Job A: ${jobA.job_id}\n Job B: ${jobB.job_id}`);
|
|
279
|
-
}
|
|
280
|
-
return;
|
|
281
|
-
}
|
|
282
|
-
if (!jobA.job_id || !jobB.job_id) {
|
|
283
|
-
failSpinner(spinner, "Missing job IDs");
|
|
284
|
-
throw new Error("Compare response missing job_id");
|
|
285
|
-
}
|
|
286
|
-
const timeout = parseInt(options.timeout || "60000", 10);
|
|
287
|
-
const interval = parseInt(options.interval || "5000", 10);
|
|
288
|
-
const [resultA, resultB] = await Promise.all([
|
|
289
|
-
waitForAudit(base, jobA.job_id, timeout, interval, apiKey),
|
|
290
|
-
waitForAudit(base, jobB.job_id, timeout, interval, apiKey),
|
|
291
|
-
]);
|
|
292
|
-
succeedSpinner(spinner, "Audits complete");
|
|
293
|
-
const overallA = getOverallScore(resultA.scores);
|
|
294
|
-
const overallB = getOverallScore(resultB.scores);
|
|
295
|
-
const delta = overallA !== null && overallB !== null ? overallB - overallA : null;
|
|
296
|
-
const scoresA = (resultA.scores || {});
|
|
297
|
-
const scoresB = (resultB.scores || {});
|
|
298
|
-
const keys = new Set([...Object.keys(scoresA), ...Object.keys(scoresB)]);
|
|
299
|
-
const categoryDeltas = {};
|
|
300
|
-
for (const key of keys) {
|
|
301
|
-
const a = toNumber(scoresA[key]);
|
|
302
|
-
const b = toNumber(scoresB[key]);
|
|
303
|
-
categoryDeltas[key] = {
|
|
304
|
-
a,
|
|
305
|
-
b,
|
|
306
|
-
delta: a !== null && b !== null ? b - a : null,
|
|
307
|
-
};
|
|
308
|
-
}
|
|
309
|
-
const issuesA = normalizeIssues(resultA.issues).length;
|
|
310
|
-
const issuesB = normalizeIssues(resultB.issues).length;
|
|
311
|
-
const compareData = {
|
|
312
|
-
urlA,
|
|
313
|
-
urlB,
|
|
314
|
-
jobA: resultA.job_id || "",
|
|
315
|
-
jobB: resultB.job_id || "",
|
|
316
|
-
overallA,
|
|
317
|
-
overallB,
|
|
318
|
-
delta,
|
|
319
|
-
categoryDeltas,
|
|
320
|
-
issuesA,
|
|
321
|
-
issuesB,
|
|
322
|
-
};
|
|
323
|
-
const failOnScore = options.failOnScore ? parseInt(options.failOnScore, 10) : undefined;
|
|
324
|
-
if (failOnScore !== undefined &&
|
|
325
|
-
((overallA !== null && overallA < failOnScore) ||
|
|
326
|
-
(overallB !== null && overallB < failOnScore))) {
|
|
327
|
-
process.exitCode = 1;
|
|
328
|
-
}
|
|
329
|
-
if (format === "json") {
|
|
330
|
-
writeJsonOutput(compareData, "compare");
|
|
331
|
-
}
|
|
332
|
-
else {
|
|
333
|
-
writeOutput(formatLegacyCompareHuman(compareData));
|
|
334
|
-
}
|
|
335
|
-
}
|
package/dist/commands/diff.d.ts
CHANGED
|
@@ -16,7 +16,12 @@ export interface DiffCommandOptions {
|
|
|
16
16
|
format?: string;
|
|
17
17
|
verbose?: boolean;
|
|
18
18
|
fromFile?: string;
|
|
19
|
+
strict?: boolean;
|
|
20
|
+
continueOnError?: boolean;
|
|
21
|
+
machine?: boolean;
|
|
22
|
+
jobIdArg?: string;
|
|
19
23
|
}
|
|
24
|
+
export declare function handleDiff(opts: DiffCommandOptions): Promise<void>;
|
|
20
25
|
/**
|
|
21
26
|
* Register the diff command with the Commander program.
|
|
22
27
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"diff.d.ts","sourceRoot":"","sources":["../../src/commands/diff.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"diff.d.ts","sourceRoot":"","sources":["../../src/commands/diff.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAkCzC,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAMD,wBAAsB,UAAU,CAAC,IAAI,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,CAmKxE;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CA8B1D"}
|