sverka 0.2.14 → 0.2.16
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/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +190 -32
- package/dist/index.mjs.map +1 -1
- package/package.json +10 -10
package/dist/index.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/types.ts","../src/main.ts","../src/output.ts"],"mappings":";;iBAGiB;;EAEf;;EAEA;;EAEA;;EAEA;;EAEA;;;iBAIe;EACf,MAAM;EACN,UAAU;EACV,MAAM;EACN,UAAU;;EAEV,MAAM;;;qBAIK;;;;;;YAOD,mBAAmB,uBAAuB;;YAG1C;;qBAWC,iBAAiB;WACnB,MAAM;WACN,UAAU;WACD;EAEhB,YAAA,iBACA,MAAM,cACN,UAAU,UACV;;;;;UChCa;EACf,SAAS;;;;;;;
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/types.ts","../src/main.ts","../src/output.ts"],"mappings":";;iBAGiB;;EAEf;;EAEA;;EAEA;;EAEA;;EAEA;;;iBAIe;EACf,MAAM;EACN,UAAU;EACV,MAAM;EACN,UAAU;;EAEV,MAAM;;;qBAIK;;;;;;YAOD,mBAAmB,uBAAuB;;YAG1C;;qBAWC,iBAAiB;WACnB,MAAM;WACN,UAAU;WACD;EAEhB,YAAA,iBACA,MAAM,cACN,UAAU,UACV;;;;;UChCa;EACf,SAAS;;;;;;;wBAuZW,KAAK,gBAAgB,OAAO,WAAW;;;;KC7ajD,aAAa;;;;;;qBAOZ,+BAA+B;;EAM9B,YAAA,KAAK,WAAW,KAAK,WAAW,iBAAe;EAO3D,MAAM;EAKN,UAAU;EAKV,MAAM;EAIN,UAAU;;EAKV,MAAM;;;;;;wBAwDQ,mBACd,QAAQ,aACR,KAAK,WACL,KAAK,YACJ;;;;;wBAYa,iBACd,QAAQ,aACR,MAAM,eACL"}
|
package/dist/index.mjs
CHANGED
|
@@ -3,11 +3,11 @@ import * as nodeModule from "node:module";
|
|
|
3
3
|
import { register } from "node:module";
|
|
4
4
|
import process$1 from "node:process";
|
|
5
5
|
import yargs from "yargs";
|
|
6
|
-
import { constants, existsSync, mkdirSync, readFileSync, realpathSync, writeFileSync } from "node:fs";
|
|
6
|
+
import { constants, existsSync, mkdirSync, readFileSync, readdirSync, realpathSync, statSync, writeFileSync } from "node:fs";
|
|
7
7
|
import { dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
8
8
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
9
9
|
import { mkdir, open, readFile, realpath, writeFile } from "node:fs/promises";
|
|
10
|
-
import { collectConstructWarnings, synthesize, validateGraph } from "@sverka/workflow";
|
|
10
|
+
import { Entry, Pipeline, Project, ShellStep, collectConstructWarnings, manual, synthesize, validateGraph } from "@sverka/workflow";
|
|
11
11
|
import { bindRunPlan, createPlanner } from "@sverka/sdk";
|
|
12
12
|
import { DEFAULT_POLICY, createBuiltinResolver, evaluatePolicy, loadBaseline, normalizeSarif, serializeSarif, synthesizeCheckSteps } from "@sverka/verification";
|
|
13
13
|
import { createDockerDriver, createEngine, createHostDriver } from "@sverka/runtime";
|
|
@@ -720,6 +720,48 @@ async function graphCommand(global, output, start) {
|
|
|
720
720
|
return ExitCode.Success;
|
|
721
721
|
}
|
|
722
722
|
//#endregion
|
|
723
|
+
//#region src/internal/implicit.ts
|
|
724
|
+
/** Load the graph `sverka run` should execute: the real config when one
|
|
725
|
+
* exists on disk (or --config names one), else the implicit detected
|
|
726
|
+
* pipeline. */
|
|
727
|
+
async function loadRunGraph(global) {
|
|
728
|
+
if (global.config === null && await findConfig(global.root) === null) return implicitGraph(global.root);
|
|
729
|
+
const { graph, warnings } = await loadProjectGraph(global);
|
|
730
|
+
return {
|
|
731
|
+
graph,
|
|
732
|
+
warnings
|
|
733
|
+
};
|
|
734
|
+
}
|
|
735
|
+
async function implicitGraph(root) {
|
|
736
|
+
const checks = await detectProjectChecks(root);
|
|
737
|
+
if (checks.length === 0) throw new CliError("no config found and no checks detected (run `sverka init` to create one)", "MISSING_ARG", ExitCode.UsageError);
|
|
738
|
+
const project = new Project("default");
|
|
739
|
+
const pipeline = new Pipeline(project, "default");
|
|
740
|
+
const steps = checks.map((check) => new ShellStep(pipeline, check.checkId, shellStepProps(check)));
|
|
741
|
+
const entry = new Entry(pipeline, "run", {
|
|
742
|
+
trigger: manual(),
|
|
743
|
+
roots: steps.map((step) => step.node.id)
|
|
744
|
+
});
|
|
745
|
+
return {
|
|
746
|
+
graph: synthesize(project),
|
|
747
|
+
warnings: collectConstructWarnings(project),
|
|
748
|
+
detected: entry.roots
|
|
749
|
+
};
|
|
750
|
+
}
|
|
751
|
+
/** Detected check → ShellStep props. A SARIF-on-stdout check keeps the
|
|
752
|
+
* `fromStdout` artifact declaration `init --detect` generates, so the
|
|
753
|
+
* implicit run feeds findings collection the same way. */
|
|
754
|
+
function shellStepProps(check) {
|
|
755
|
+
return {
|
|
756
|
+
command: check.command,
|
|
757
|
+
runtime: { mode: "host" },
|
|
758
|
+
...check.sarifOutput !== void 0 ? { outputs: { [check.sarifOutput]: {
|
|
759
|
+
type: "artifact",
|
|
760
|
+
fromStdout: true
|
|
761
|
+
} } } : {}
|
|
762
|
+
};
|
|
763
|
+
}
|
|
764
|
+
//#endregion
|
|
723
765
|
//#region src/internal/runtime-check.ts
|
|
724
766
|
/**
|
|
725
767
|
* Check if a binary is available on PATH by spawning `<binary> --version`.
|
|
@@ -922,7 +964,8 @@ async function runCommand(args, global, output, start) {
|
|
|
922
964
|
const fmt = resolveFormats(args, global);
|
|
923
965
|
output.debug(`run: root=${global.root} executor=${executor} entry=${args.entryId ?? "(first)"} format=${global.format}`);
|
|
924
966
|
assertExecutorAvailable(executor);
|
|
925
|
-
const { graph, warnings } = await
|
|
967
|
+
const { graph, warnings, detected } = await loadRunGraph(global);
|
|
968
|
+
if (detected !== void 0) output.errorLine(`no sverka.config — running ${detected.length} detected checks (${detected.join(", ")})`);
|
|
926
969
|
for (const warning of warnings) output.errorLine(`warning: ${warning}`);
|
|
927
970
|
const entryId = resolveEntryId(graph, args.entryId);
|
|
928
971
|
const plan = bindRunPlan({
|
|
@@ -947,13 +990,11 @@ async function runCommand(args, global, output, start) {
|
|
|
947
990
|
durationMs,
|
|
948
991
|
evalResult: evaluation.summary,
|
|
949
992
|
artifactDir,
|
|
950
|
-
sinceMs: start
|
|
993
|
+
sinceMs: start,
|
|
994
|
+
...detected !== void 0 ? { detected } : {},
|
|
995
|
+
...args.report !== void 0 ? { report: args.report } : {}
|
|
951
996
|
}, events, output);
|
|
952
|
-
|
|
953
|
-
const reportPath = args.output ?? join(global.root, ".sverka", "report.html");
|
|
954
|
-
output.writeLine(`Wrote HTML report to ${reportPath}`);
|
|
955
|
-
}
|
|
956
|
-
if (renderer && "waitUntilExit" in renderer) await renderer.waitUntilExit();
|
|
997
|
+
await finalizeRenderer(fmt, renderer, args, global, output);
|
|
957
998
|
if (evaluation.collectionFailed) return evaluation.exitCode;
|
|
958
999
|
writeRunOutput({
|
|
959
1000
|
planId: plan.id,
|
|
@@ -963,12 +1004,36 @@ async function runCommand(args, global, output, start) {
|
|
|
963
1004
|
global,
|
|
964
1005
|
output,
|
|
965
1006
|
evalResult: evaluation.summary,
|
|
966
|
-
report
|
|
1007
|
+
report,
|
|
1008
|
+
...detected !== void 0 ? { detected } : {}
|
|
967
1009
|
});
|
|
968
|
-
|
|
1010
|
+
printReportTail(report, args.report, global, output);
|
|
969
1011
|
if (evaluation.exitCode !== 0) return evaluation.exitCode;
|
|
970
1012
|
return exitCodeForStatus(runStatus);
|
|
971
1013
|
}
|
|
1014
|
+
/** Post-run: announce the HTML report path (sarif/web print their own),
|
|
1015
|
+
* then keep interactive renderers mounted until the user quits. */
|
|
1016
|
+
async function finalizeRenderer(fmt, renderer, args, global, output) {
|
|
1017
|
+
if (fmt.isHtml && renderer) {
|
|
1018
|
+
const reportPath = args.output ?? join(global.root, ".sverka", "report.html");
|
|
1019
|
+
output.writeLine(`Wrote HTML report to ${reportPath}`);
|
|
1020
|
+
}
|
|
1021
|
+
if (renderer && "waitUntilExit" in renderer) await renderer.waitUntilExit();
|
|
1022
|
+
}
|
|
1023
|
+
/** Human-mode tail (Spec 53): findings summary, then the report location —
|
|
1024
|
+
* the report is discoverable, not hidden. The `sverka view` hint applies
|
|
1025
|
+
* only when report.html sits at the default path — view resolves
|
|
1026
|
+
* .sverka/runs/<latest>/report.html, not a --report relocation. */
|
|
1027
|
+
function printReportTail(report, reportFlag, global, output) {
|
|
1028
|
+
if (report === void 0 || global.format !== "text") return;
|
|
1029
|
+
output.writeLine(` findings: ${report.findings}`);
|
|
1030
|
+
if (report.html === null) {
|
|
1031
|
+
output.writeLine(` report: ${report.json}`);
|
|
1032
|
+
return;
|
|
1033
|
+
}
|
|
1034
|
+
const hint = reportFlag !== void 0 ? "(open in a browser)" : "(sverka view to open)";
|
|
1035
|
+
output.writeLine(` report: ${report.html} ${hint}`);
|
|
1036
|
+
}
|
|
972
1037
|
/** Derive output modes — `--format html`, or `--output` without
|
|
973
1038
|
* sarif/web, implies HTML; evaluation runs for any structured format. */
|
|
974
1039
|
function resolveFormats(args, global) {
|
|
@@ -1018,11 +1083,14 @@ async function writeReportSafe(ctx, events, output) {
|
|
|
1018
1083
|
durationMs: ctx.durationMs,
|
|
1019
1084
|
evalResult: ctx.evalResult,
|
|
1020
1085
|
artifactDir: ctx.artifactDir,
|
|
1021
|
-
sinceMs: ctx.sinceMs
|
|
1086
|
+
sinceMs: ctx.sinceMs,
|
|
1087
|
+
...ctx.detected !== void 0 ? { detected: ctx.detected } : {},
|
|
1088
|
+
...ctx.report !== void 0 ? { reportPath: ctx.report } : {}
|
|
1022
1089
|
});
|
|
1023
1090
|
return {
|
|
1024
1091
|
html: artifacts.htmlPath,
|
|
1025
|
-
json: join(artifacts.dir, "report.json")
|
|
1092
|
+
json: join(artifacts.dir, "report.json"),
|
|
1093
|
+
findings: artifacts.findingsCount
|
|
1026
1094
|
};
|
|
1027
1095
|
} catch (err) {
|
|
1028
1096
|
if (err instanceof CliError) throw err;
|
|
@@ -1049,9 +1117,10 @@ async function runWatch(args, global, output) {
|
|
|
1049
1117
|
const configPath = global.config === null ? null : resolve(global.root, global.config);
|
|
1050
1118
|
const extraPaths = configPath !== null && !configPath.startsWith(`${resolve(global.root)}/`) ? [configPath] : [];
|
|
1051
1119
|
const ignored = [];
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1120
|
+
for (const file of [args.output, args.report]) {
|
|
1121
|
+
if (file === void 0) continue;
|
|
1122
|
+
const abs = resolve(global.root, file);
|
|
1123
|
+
if (abs.startsWith(`${resolve(global.root)}/`)) ignored.push(abs);
|
|
1055
1124
|
}
|
|
1056
1125
|
return await watchLoop({
|
|
1057
1126
|
root: global.root,
|
|
@@ -1259,6 +1328,7 @@ function writeRunOutput(opts) {
|
|
|
1259
1328
|
planId,
|
|
1260
1329
|
status: runStatus,
|
|
1261
1330
|
steps,
|
|
1331
|
+
...opts.detected !== void 0 ? { detected: opts.detected } : {},
|
|
1262
1332
|
...evalResult ? {
|
|
1263
1333
|
findings: evalResult.findings.length,
|
|
1264
1334
|
verdict: evalResult.verdict,
|
|
@@ -1266,7 +1336,8 @@ function writeRunOutput(opts) {
|
|
|
1266
1336
|
} : {},
|
|
1267
1337
|
...report !== void 0 ? { report: {
|
|
1268
1338
|
json: report.json,
|
|
1269
|
-
...report.html !== null ? { html: report.html } : {}
|
|
1339
|
+
...report.html !== null ? { html: report.html } : {},
|
|
1340
|
+
findings: report.findings
|
|
1270
1341
|
} } : {}
|
|
1271
1342
|
},
|
|
1272
1343
|
durationMs
|
|
@@ -1341,26 +1412,27 @@ async function collectRunFindings(opts, warnings) {
|
|
|
1341
1412
|
}
|
|
1342
1413
|
/** Best-effort report.html — a render failure never fails the run; it is
|
|
1343
1414
|
* recorded in the report's `warnings` instead of being dropped silently. */
|
|
1344
|
-
async function writeReportHtml(
|
|
1415
|
+
async function writeReportHtml(htmlPath, findings, warnings) {
|
|
1345
1416
|
try {
|
|
1346
1417
|
const { generateSarifHtml } = await import("@sverka/sarif-viewer-web");
|
|
1347
|
-
|
|
1348
|
-
writeFileSync(
|
|
1349
|
-
return
|
|
1418
|
+
mkdirSync(dirname(htmlPath), { recursive: true });
|
|
1419
|
+
writeFileSync(htmlPath, generateSarifHtml(findings), "utf-8");
|
|
1420
|
+
return htmlPath;
|
|
1350
1421
|
} catch (e) {
|
|
1351
1422
|
warnings.push(`report.html generation failed: ${e instanceof Error ? e.message : String(e)}`);
|
|
1352
1423
|
return null;
|
|
1353
1424
|
}
|
|
1354
1425
|
}
|
|
1355
|
-
function runReportPayload(opts, warnings) {
|
|
1426
|
+
function runReportPayload(opts, findingsCount, warnings) {
|
|
1356
1427
|
return {
|
|
1357
1428
|
schema: "sverka.run/v1",
|
|
1358
1429
|
data: {
|
|
1359
1430
|
planId: opts.planId,
|
|
1360
1431
|
status: opts.runStatus,
|
|
1361
1432
|
steps: boundStepOutput(summarizeSteps(opts.events)),
|
|
1433
|
+
...opts.detected !== void 0 ? { detected: opts.detected } : {},
|
|
1434
|
+
findings: findingsCount,
|
|
1362
1435
|
...opts.evalResult ? {
|
|
1363
|
-
findings: opts.evalResult.findings.length,
|
|
1364
1436
|
verdict: opts.evalResult.verdict,
|
|
1365
1437
|
summary: opts.evalResult.summary
|
|
1366
1438
|
} : {},
|
|
@@ -1375,11 +1447,13 @@ function runReportPayload(opts, warnings) {
|
|
|
1375
1447
|
async function writeRunArtifacts(opts) {
|
|
1376
1448
|
const dir = reportDir(opts.root, opts.runId);
|
|
1377
1449
|
const warnings = [];
|
|
1378
|
-
const
|
|
1379
|
-
|
|
1450
|
+
const findings = await collectRunFindings(opts, warnings);
|
|
1451
|
+
const htmlPath = await writeReportHtml(opts.reportPath !== void 0 ? resolveUnderRoot(opts.root, opts.reportPath) : join(dir, "report.html"), findings, warnings);
|
|
1452
|
+
writeFileSync(join(dir, "report.json"), JSON.stringify(runReportPayload(opts, findings.length, warnings), null, 2), "utf-8");
|
|
1380
1453
|
return {
|
|
1381
1454
|
dir,
|
|
1382
|
-
htmlPath
|
|
1455
|
+
htmlPath,
|
|
1456
|
+
findingsCount: findings.length
|
|
1383
1457
|
};
|
|
1384
1458
|
}
|
|
1385
1459
|
function exitCodeForStatus(runStatus) {
|
|
@@ -2007,6 +2081,77 @@ function runCheck(name, binary, required) {
|
|
|
2007
2081
|
}
|
|
2008
2082
|
}
|
|
2009
2083
|
//#endregion
|
|
2084
|
+
//#region src/internal/open-report.ts
|
|
2085
|
+
/** Newest `.sverka/runs/<runId>/report.html` under root, by file mtime.
|
|
2086
|
+
* Returns undefined when no run report exists. */
|
|
2087
|
+
function findLatestReportHtml(root) {
|
|
2088
|
+
let names;
|
|
2089
|
+
try {
|
|
2090
|
+
names = readdirSync(join(root, ".sverka", "runs"));
|
|
2091
|
+
} catch {
|
|
2092
|
+
return;
|
|
2093
|
+
}
|
|
2094
|
+
let best;
|
|
2095
|
+
for (const name of names) {
|
|
2096
|
+
const candidate = join(root, ".sverka", "runs", name, "report.html");
|
|
2097
|
+
let stat;
|
|
2098
|
+
try {
|
|
2099
|
+
stat = statSync(candidate);
|
|
2100
|
+
} catch (e) {
|
|
2101
|
+
const code = e.code;
|
|
2102
|
+
if (code === "ENOENT" || code === "ENOTDIR") continue;
|
|
2103
|
+
throw e;
|
|
2104
|
+
}
|
|
2105
|
+
if (stat.isFile() && (best === void 0 || stat.mtimeMs > best.mtimeMs)) best = {
|
|
2106
|
+
path: candidate,
|
|
2107
|
+
mtimeMs: stat.mtimeMs
|
|
2108
|
+
};
|
|
2109
|
+
}
|
|
2110
|
+
return best?.path;
|
|
2111
|
+
}
|
|
2112
|
+
/** WSL exposes Windows browsers through wslu, not xdg-open. */
|
|
2113
|
+
function isWsl() {
|
|
2114
|
+
if (process$1.platform !== "linux") return false;
|
|
2115
|
+
try {
|
|
2116
|
+
return readFileSync("/proc/sys/kernel/osrelease", "utf8").toLowerCase().includes("microsoft");
|
|
2117
|
+
} catch {
|
|
2118
|
+
return false;
|
|
2119
|
+
}
|
|
2120
|
+
}
|
|
2121
|
+
function openerCandidates(path) {
|
|
2122
|
+
if (process$1.platform === "darwin") return [["open", [path]]];
|
|
2123
|
+
if (process$1.platform === "win32") return [[
|
|
2124
|
+
"cmd",
|
|
2125
|
+
[
|
|
2126
|
+
"/c",
|
|
2127
|
+
"start",
|
|
2128
|
+
"\"\"",
|
|
2129
|
+
"\"%SVERKA_VIEW_TARGET%\""
|
|
2130
|
+
],
|
|
2131
|
+
{
|
|
2132
|
+
windowsVerbatimArguments: true,
|
|
2133
|
+
env: {
|
|
2134
|
+
...process$1.env,
|
|
2135
|
+
SVERKA_VIEW_TARGET: path
|
|
2136
|
+
}
|
|
2137
|
+
}
|
|
2138
|
+
]];
|
|
2139
|
+
return isWsl() ? [["wslview", [path]], ["xdg-open", [path]]] : [["xdg-open", [path]]];
|
|
2140
|
+
}
|
|
2141
|
+
/** Open a file with the platform's default handler. Returns false when no
|
|
2142
|
+
* opener binary exists or every attempt failed — callers print the path. */
|
|
2143
|
+
function openReportInBrowser(path) {
|
|
2144
|
+
for (const [cmd, args, options] of openerCandidates(path)) {
|
|
2145
|
+
const res = spawnSync(cmd, args, {
|
|
2146
|
+
stdio: "ignore",
|
|
2147
|
+
timeout: 5e3,
|
|
2148
|
+
...options
|
|
2149
|
+
});
|
|
2150
|
+
if (res.error === void 0 && res.status === 0) return true;
|
|
2151
|
+
}
|
|
2152
|
+
return false;
|
|
2153
|
+
}
|
|
2154
|
+
//#endregion
|
|
2010
2155
|
//#region src/commands/view.ts
|
|
2011
2156
|
/**
|
|
2012
2157
|
* View SARIF findings in the terminal TUI or generate an HTML report.
|
|
@@ -2024,10 +2169,7 @@ async function viewCommand(args, global, output, start) {
|
|
|
2024
2169
|
output.errorLine("sverka view: stdin is empty.");
|
|
2025
2170
|
return ExitCode.RuntimeError;
|
|
2026
2171
|
}
|
|
2027
|
-
} else
|
|
2028
|
-
output.errorLine("sverka view: no input. Provide a SARIF file argument or pipe SARIF via stdin.");
|
|
2029
|
-
return ExitCode.UsageError;
|
|
2030
|
-
}
|
|
2172
|
+
} else return openLatestReport(global, output);
|
|
2031
2173
|
let sarif;
|
|
2032
2174
|
try {
|
|
2033
2175
|
sarif = JSON.parse(sarifJson);
|
|
@@ -2038,6 +2180,18 @@ async function viewCommand(args, global, output, start) {
|
|
|
2038
2180
|
if (args.format === "web") return renderWeb(sarif, args, output);
|
|
2039
2181
|
return renderTui(sarif, output);
|
|
2040
2182
|
}
|
|
2183
|
+
/** Interactive with no input — open the latest run report (Spec 53:
|
|
2184
|
+
* `sverka run` tails with `report: <path> (sverka view to open)`). */
|
|
2185
|
+
function openLatestReport(global, output) {
|
|
2186
|
+
const report = findLatestReportHtml(global.root);
|
|
2187
|
+
if (report === void 0) {
|
|
2188
|
+
output.errorLine("sverka view: no input. Provide a SARIF file argument, pipe SARIF via stdin, or run `sverka run` first — no run report found under .sverka/runs/.");
|
|
2189
|
+
return ExitCode.UsageError;
|
|
2190
|
+
}
|
|
2191
|
+
output.writeLine(`report: ${report}`);
|
|
2192
|
+
if (!openReportInBrowser(report)) output.errorLine("sverka view: could not open a browser — open the report path above manually.");
|
|
2193
|
+
return ExitCode.Success;
|
|
2194
|
+
}
|
|
2041
2195
|
/** Launch the TUI viewer using @sverka/sarif-viewer-tui. */
|
|
2042
2196
|
async function renderTui(sarif, output) {
|
|
2043
2197
|
try {
|
|
@@ -2148,6 +2302,9 @@ function addRunCommand(y) {
|
|
|
2148
2302
|
type: "string",
|
|
2149
2303
|
alias: "o",
|
|
2150
2304
|
describe: "Output file path for HTML report (implies --format html)"
|
|
2305
|
+
}).option("report", {
|
|
2306
|
+
type: "string",
|
|
2307
|
+
describe: "Relocate the per-run HTML report (report.json stays under .sverka/runs/)"
|
|
2151
2308
|
}).option("tui", {
|
|
2152
2309
|
type: "boolean",
|
|
2153
2310
|
describe: "Interactive terminal UI (off by default; opt-in only)"
|
|
@@ -2206,7 +2363,7 @@ function addCompileCommand(y) {
|
|
|
2206
2363
|
function addViewCommand(y) {
|
|
2207
2364
|
return y.positional("file", {
|
|
2208
2365
|
type: "string",
|
|
2209
|
-
describe: "Path to a .sarif file (
|
|
2366
|
+
describe: "Path to a .sarif file (piped stdin when omitted; bare `view` opens the latest run report)"
|
|
2210
2367
|
}).option("format", {
|
|
2211
2368
|
type: "string",
|
|
2212
2369
|
alias: "f",
|
|
@@ -2261,7 +2418,7 @@ function buildParser() {
|
|
|
2261
2418
|
type: "boolean",
|
|
2262
2419
|
alias: "v",
|
|
2263
2420
|
default: false
|
|
2264
|
-
}).command("init", "Create a sverka.config.ts", addInitCommand).command("validate", "Validate a sverka config").command("plan", "Bind Entry + inputs → Run Plan", addPlanCommand).command("graph", "Display the Definition Graph").command("run", "Execute the workflow locally", addRunCommand).command("discover", "Discover and display project context").command("check", "Resolve proposed checks → StepDefinitions").command("policy", "Evaluate findings against policy", addPolicyCommand).command("synth", "Alias for compile — compile to a target CI YAML", addSynthCommand).command("compile", "Compile to a target CI YAML", addCompileCommand).command("mcp-server", "Expose Sverka as an MCP server (stdio)").command("doctor", "Diagnose environment and dependencies").command("view [file]", "
|
|
2421
|
+
}).command("init", "Create a sverka.config.ts", addInitCommand).command("validate", "Validate a sverka config").command("plan", "Bind Entry + inputs → Run Plan", addPlanCommand).command("graph", "Display the Definition Graph").command("run", "Execute the workflow locally", addRunCommand).command("discover", "Discover and display project context").command("check", "Resolve proposed checks → StepDefinitions").command("policy", "Evaluate findings against policy", addPolicyCommand).command("synth", "Alias for compile — compile to a target CI YAML", addSynthCommand).command("compile", "Compile to a target CI YAML", addCompileCommand).command("mcp-server", "Expose Sverka as an MCP server (stdio)").command("doctor", "Diagnose environment and dependencies").command("view [file]", "Open the latest run report, or view SARIF findings (TUI/HTML)", addViewCommand).command("ui", "Start local web dashboard server", addUiCommand).demandCommand(1, "No command given").strict().fail((msg, err) => {
|
|
2265
2422
|
if (err) throw err;
|
|
2266
2423
|
throw new CliError(msg, "UNKNOWN_COMMAND", ExitCode.UsageError);
|
|
2267
2424
|
});
|
|
@@ -2315,6 +2472,7 @@ function dispatchRun(parsed, global, output, start) {
|
|
|
2315
2472
|
}
|
|
2316
2473
|
if (typeof parsed.entry === "string") args.entryId = parsed.entry;
|
|
2317
2474
|
if (typeof parsed.output === "string") args.output = parsed.output;
|
|
2475
|
+
if (typeof parsed.report === "string") args.report = parsed.report;
|
|
2318
2476
|
if (parsed.tui === true) args.tui = true;
|
|
2319
2477
|
if (parsed.tui === false) args.tui = false;
|
|
2320
2478
|
if (parsed.watch === true) args.watch = true;
|