@veraxhq/verax 0.1.0 → 0.2.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/README.md +123 -88
- package/bin/verax.js +11 -452
- package/package.json +14 -36
- package/src/cli/commands/default.js +523 -0
- package/src/cli/commands/doctor.js +165 -0
- package/src/cli/commands/inspect.js +109 -0
- package/src/cli/commands/run.js +402 -0
- package/src/cli/entry.js +196 -0
- package/src/cli/util/atomic-write.js +37 -0
- package/src/cli/util/detection-engine.js +296 -0
- package/src/cli/util/env-url.js +33 -0
- package/src/cli/util/errors.js +44 -0
- package/src/cli/util/events.js +34 -0
- package/src/cli/util/expectation-extractor.js +378 -0
- package/src/cli/util/findings-writer.js +31 -0
- package/src/cli/util/idgen.js +87 -0
- package/src/cli/util/learn-writer.js +39 -0
- package/src/cli/util/observation-engine.js +366 -0
- package/src/cli/util/observe-writer.js +25 -0
- package/src/cli/util/paths.js +29 -0
- package/src/cli/util/project-discovery.js +277 -0
- package/src/cli/util/project-writer.js +26 -0
- package/src/cli/util/redact.js +128 -0
- package/src/cli/util/run-id.js +30 -0
- package/src/cli/util/summary-writer.js +32 -0
- package/src/verax/cli/ci-summary.js +35 -0
- package/src/verax/cli/context-explanation.js +89 -0
- package/src/verax/cli/doctor.js +277 -0
- package/src/verax/cli/error-normalizer.js +154 -0
- package/src/verax/cli/explain-output.js +105 -0
- package/src/verax/cli/finding-explainer.js +130 -0
- package/src/verax/cli/init.js +237 -0
- package/src/verax/cli/run-overview.js +163 -0
- package/src/verax/cli/url-safety.js +101 -0
- package/src/verax/cli/wizard.js +98 -0
- package/src/verax/cli/zero-findings-explainer.js +57 -0
- package/src/verax/cli/zero-interaction-explainer.js +127 -0
- package/src/verax/core/action-classifier.js +86 -0
- package/src/verax/core/budget-engine.js +218 -0
- package/src/verax/core/canonical-outcomes.js +157 -0
- package/src/verax/core/decision-snapshot.js +335 -0
- package/src/verax/core/determinism-model.js +403 -0
- package/src/verax/core/incremental-store.js +237 -0
- package/src/verax/core/invariants.js +356 -0
- package/src/verax/core/promise-model.js +230 -0
- package/src/verax/core/replay-validator.js +350 -0
- package/src/verax/core/replay.js +222 -0
- package/src/verax/core/run-id.js +175 -0
- package/src/verax/core/run-manifest.js +99 -0
- package/src/verax/core/silence-impact.js +369 -0
- package/src/verax/core/silence-model.js +521 -0
- package/src/verax/detect/comparison.js +2 -34
- package/src/verax/detect/confidence-engine.js +764 -329
- package/src/verax/detect/detection-engine.js +293 -0
- package/src/verax/detect/evidence-index.js +177 -0
- package/src/verax/detect/expectation-model.js +194 -172
- package/src/verax/detect/explanation-helpers.js +187 -0
- package/src/verax/detect/finding-detector.js +450 -0
- package/src/verax/detect/findings-writer.js +44 -8
- package/src/verax/detect/flow-detector.js +366 -0
- package/src/verax/detect/index.js +172 -286
- package/src/verax/detect/interactive-findings.js +613 -0
- package/src/verax/detect/signal-mapper.js +308 -0
- package/src/verax/detect/verdict-engine.js +563 -0
- package/src/verax/evidence-index-writer.js +61 -0
- package/src/verax/index.js +90 -14
- package/src/verax/intel/effect-detector.js +368 -0
- package/src/verax/intel/handler-mapper.js +249 -0
- package/src/verax/intel/index.js +281 -0
- package/src/verax/intel/route-extractor.js +280 -0
- package/src/verax/intel/ts-program.js +256 -0
- package/src/verax/intel/vue-navigation-extractor.js +579 -0
- package/src/verax/intel/vue-router-extractor.js +323 -0
- package/src/verax/learn/action-contract-extractor.js +335 -101
- package/src/verax/learn/ast-contract-extractor.js +95 -5
- package/src/verax/learn/flow-extractor.js +172 -0
- package/src/verax/learn/manifest-writer.js +97 -47
- package/src/verax/learn/project-detector.js +40 -0
- package/src/verax/learn/route-extractor.js +27 -96
- package/src/verax/learn/state-extractor.js +212 -0
- package/src/verax/learn/static-extractor-navigation.js +114 -0
- package/src/verax/learn/static-extractor-validation.js +88 -0
- package/src/verax/learn/static-extractor.js +112 -4
- package/src/verax/learn/truth-assessor.js +24 -21
- package/src/verax/observe/aria-sensor.js +211 -0
- package/src/verax/observe/browser.js +10 -5
- package/src/verax/observe/console-sensor.js +1 -17
- package/src/verax/observe/domain-boundary.js +10 -1
- package/src/verax/observe/expectation-executor.js +512 -0
- package/src/verax/observe/flow-matcher.js +143 -0
- package/src/verax/observe/focus-sensor.js +196 -0
- package/src/verax/observe/human-driver.js +643 -275
- package/src/verax/observe/index.js +908 -27
- package/src/verax/observe/index.js.backup +1 -0
- package/src/verax/observe/interaction-discovery.js +365 -14
- package/src/verax/observe/interaction-runner.js +563 -198
- package/src/verax/observe/loading-sensor.js +139 -0
- package/src/verax/observe/navigation-sensor.js +255 -0
- package/src/verax/observe/network-sensor.js +55 -7
- package/src/verax/observe/observed-expectation-deriver.js +186 -0
- package/src/verax/observe/observed-expectation.js +305 -0
- package/src/verax/observe/page-frontier.js +234 -0
- package/src/verax/observe/settle.js +37 -17
- package/src/verax/observe/state-sensor.js +389 -0
- package/src/verax/observe/timing-sensor.js +228 -0
- package/src/verax/observe/traces-writer.js +61 -20
- package/src/verax/observe/ui-signal-sensor.js +136 -17
- package/src/verax/scan-summary-writer.js +77 -15
- package/src/verax/shared/artifact-manager.js +110 -8
- package/src/verax/shared/budget-profiles.js +136 -0
- package/src/verax/shared/ci-detection.js +39 -0
- package/src/verax/shared/config-loader.js +170 -0
- package/src/verax/shared/dynamic-route-utils.js +218 -0
- package/src/verax/shared/expectation-coverage.js +44 -0
- package/src/verax/shared/expectation-prover.js +81 -0
- package/src/verax/shared/expectation-tracker.js +201 -0
- package/src/verax/shared/expectations-writer.js +60 -0
- package/src/verax/shared/first-run.js +44 -0
- package/src/verax/shared/progress-reporter.js +171 -0
- package/src/verax/shared/retry-policy.js +14 -1
- package/src/verax/shared/root-artifacts.js +49 -0
- package/src/verax/shared/scan-budget.js +86 -0
- package/src/verax/shared/url-normalizer.js +162 -0
- package/src/verax/shared/zip-artifacts.js +65 -0
- package/src/verax/validate/context-validator.js +244 -0
- package/src/verax/validate/context-validator.js.bak +0 -0
package/bin/verax.js
CHANGED
|
@@ -1,452 +1,11 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
import
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
14
|
-
const __dirname = dirname(__filename);
|
|
15
|
-
|
|
16
|
-
async function main() {
|
|
17
|
-
const args = process.argv.slice(2);
|
|
18
|
-
const command = args[0];
|
|
19
|
-
|
|
20
|
-
// Handle Wave 9 CLI commands
|
|
21
|
-
if (command === 'scan') {
|
|
22
|
-
await handleScan(args.slice(1));
|
|
23
|
-
return;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
if (command === 'flow') {
|
|
27
|
-
await handleFlow(args.slice(1));
|
|
28
|
-
return;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
if (['--help', '-h', 'help'].includes(command)) {
|
|
32
|
-
printHelp();
|
|
33
|
-
process.exit(0);
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
// Default: interactive mode (existing behavior)
|
|
37
|
-
console.log('VERAX\n');
|
|
38
|
-
|
|
39
|
-
// Reuse parsed args from above for optional flags
|
|
40
|
-
let projectDir = null;
|
|
41
|
-
let url = null;
|
|
42
|
-
let manifestPath = null;
|
|
43
|
-
|
|
44
|
-
const projectDirIndex = args.indexOf('--project-dir');
|
|
45
|
-
const projectDirArg = projectDirIndex !== -1 && args[projectDirIndex + 1] ? args[projectDirIndex + 1] : null;
|
|
46
|
-
|
|
47
|
-
const urlIndex = args.indexOf('--url');
|
|
48
|
-
if (urlIndex !== -1 && urlIndex + 1 < args.length) {
|
|
49
|
-
url = args[urlIndex + 1];
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
const manifestIndex = args.indexOf('--manifest');
|
|
53
|
-
if (manifestIndex !== -1 && manifestIndex + 1 < args.length) {
|
|
54
|
-
manifestPath = resolve(args[manifestIndex + 1]);
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
// Resolve workspace root using the new function
|
|
58
|
-
try {
|
|
59
|
-
const resolved = resolveWorkspaceRoot(projectDirArg, process.cwd());
|
|
60
|
-
projectDir = resolved.workspaceRoot;
|
|
61
|
-
|
|
62
|
-
if (resolved.isRepoRoot) {
|
|
63
|
-
console.error(
|
|
64
|
-
'VERAX: Refusing to write artifacts in repository root.\n' +
|
|
65
|
-
'Use --project-dir to specify the target project directory.'
|
|
66
|
-
);
|
|
67
|
-
process.exit(2);
|
|
68
|
-
}
|
|
69
|
-
} catch (error) {
|
|
70
|
-
console.error(`Error: ${error.message}`);
|
|
71
|
-
process.exit(2);
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
const actions = ['Scan my website'];
|
|
75
|
-
let action;
|
|
76
|
-
|
|
77
|
-
if (actions.length === 1) {
|
|
78
|
-
action = actions[0];
|
|
79
|
-
} else {
|
|
80
|
-
const result = await inquirer.prompt([
|
|
81
|
-
{
|
|
82
|
-
type: 'list',
|
|
83
|
-
name: 'action',
|
|
84
|
-
message: 'What would you like to do?',
|
|
85
|
-
choices: actions,
|
|
86
|
-
default: actions[0]
|
|
87
|
-
}
|
|
88
|
-
]);
|
|
89
|
-
action = result.action;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
if (action === 'Scan my website') {
|
|
93
|
-
try {
|
|
94
|
-
console.log('Understanding your website...');
|
|
95
|
-
const manifest = await learn(projectDir);
|
|
96
|
-
|
|
97
|
-
console.log(`\nProject type: ${manifest.projectType}`);
|
|
98
|
-
console.log(`Total routes: ${manifest.routes.length}`);
|
|
99
|
-
console.log(`Public routes: ${manifest.publicRoutes.length}`);
|
|
100
|
-
console.log(`Internal routes: ${manifest.internalRoutes.length}`);
|
|
101
|
-
|
|
102
|
-
if (url) {
|
|
103
|
-
const { scan } = await import('../src/verax/index.js');
|
|
104
|
-
const result = await scan(projectDir, url, manifestPath);
|
|
105
|
-
const { observation, findings, scanSummary } = result;
|
|
106
|
-
|
|
107
|
-
console.log('\nObserving real user interactions...');
|
|
108
|
-
console.log('Comparing expectations with reality...');
|
|
109
|
-
|
|
110
|
-
console.log('\nScan complete.\n');
|
|
111
|
-
console.log(`Total interactions observed: ${observation.traces.length}`);
|
|
112
|
-
console.log(`Silent failures detected: ${findings.findings.length}`);
|
|
113
|
-
|
|
114
|
-
if (findings.findings.length > 0) {
|
|
115
|
-
console.log(`\nFindings report: ${findings.findingsPath}\n`);
|
|
116
|
-
} else {
|
|
117
|
-
console.log('\nNo silent user failures were detected.');
|
|
118
|
-
console.log(`\nFindings report: ${findings.findingsPath}\n`);
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
if (scanSummary) {
|
|
122
|
-
const truth = scanSummary.truth;
|
|
123
|
-
console.log('Truth Summary:');
|
|
124
|
-
console.log(`- Learn: routes=${truth.learn.routesDiscovered} (confidence: ${truth.learn.routesConfidence}, source: ${truth.learn.routesSource}), expectations=${truth.learn.expectationsDiscovered} (strong=${truth.learn.expectationsStrong}, weak=${truth.learn.expectationsWeak})`);
|
|
125
|
-
if (truth.learn.validation) {
|
|
126
|
-
console.log(`- Learn validation: validated=${truth.learn.validation.routesValidated}, reachable=${truth.learn.validation.routesReachable}, unreachable=${truth.learn.validation.routesUnreachable}`);
|
|
127
|
-
}
|
|
128
|
-
const coverage = truth.observe.coverage;
|
|
129
|
-
const coverageLine = coverage
|
|
130
|
-
? `Coverage: selected=${coverage.candidatesSelected}/${coverage.candidatesDiscovered} (cap=${coverage.cap})${coverage.capped ? ' — capped' : ''}`
|
|
131
|
-
: null;
|
|
132
|
-
console.log(`- Observe: interactions=${truth.observe.interactionsObserved}, external-blocked=${truth.observe.externalNavigationBlockedCount}, timeouts=${truth.observe.timeoutsCount}`);
|
|
133
|
-
if (coverageLine) {
|
|
134
|
-
console.log(` - ${coverageLine}`);
|
|
135
|
-
}
|
|
136
|
-
console.log(`- Detect: analyzed=${truth.detect.interactionsAnalyzed}, skipped(no expectation)=${truth.detect.interactionsSkippedNoExpectation}, findings=${truth.detect.findingsCount}`);
|
|
137
|
-
if (truth.detect.skips && truth.detect.skips.total > 0) {
|
|
138
|
-
const topReasons = truth.detect.skips.reasons.slice(0, 3).map(r => `${r.code}=${r.count}`).join(', ');
|
|
139
|
-
console.log(`- Detect skips: ${truth.detect.skips.total} (top: ${topReasons})`);
|
|
140
|
-
}
|
|
141
|
-
console.log(`- Scan summary: ${scanSummary.summaryPath}\n`);
|
|
142
|
-
}
|
|
143
|
-
} else {
|
|
144
|
-
console.log('\nNote: Provide --url to observe website interactions\n');
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
process.exit(0);
|
|
148
|
-
} catch (error) {
|
|
149
|
-
console.error(`\nError: ${error.message}`);
|
|
150
|
-
if (error.stack && process.env.DEBUG) {
|
|
151
|
-
console.error(error.stack);
|
|
152
|
-
}
|
|
153
|
-
process.exit(2);
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
/**
|
|
159
|
-
* Parse command-line arguments into key-value object
|
|
160
|
-
*/
|
|
161
|
-
function parseArgs(argArray) {
|
|
162
|
-
const opts = {};
|
|
163
|
-
for (let i = 0; i < argArray.length; i++) {
|
|
164
|
-
if (argArray[i].startsWith('--')) {
|
|
165
|
-
const key = argArray[i].substring(2);
|
|
166
|
-
if (i + 1 < argArray.length && !argArray[i + 1].startsWith('--')) {
|
|
167
|
-
opts[key] = argArray[i + 1];
|
|
168
|
-
i++;
|
|
169
|
-
} else {
|
|
170
|
-
opts[key] = true;
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
return opts;
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
/**
|
|
178
|
-
* Print help message
|
|
179
|
-
*/
|
|
180
|
-
function printHelp() {
|
|
181
|
-
console.log(`
|
|
182
|
-
VERAX — Web Application Verification Platform
|
|
183
|
-
|
|
184
|
-
Usage:
|
|
185
|
-
verax [interactive] Interactive mode
|
|
186
|
-
verax scan --url <url> [--projectRoot <path>] [--json] [--out <dir>]
|
|
187
|
-
verax flow --flow <path> [--url <url>] [--json] [--out <dir>]
|
|
188
|
-
verax --help Show this message
|
|
189
|
-
|
|
190
|
-
Commands:
|
|
191
|
-
scan Run full scan on a live URL
|
|
192
|
-
flow Execute a user flow against a URL
|
|
193
|
-
|
|
194
|
-
Options:
|
|
195
|
-
--url <url> Target URL
|
|
196
|
-
--flow <path> Path to flow definition file
|
|
197
|
-
--projectRoot <p> Project root directory (defaults to cwd)
|
|
198
|
-
--json Output machine-readable JSON
|
|
199
|
-
--out <dir> Output directory (defaults to .verax/runs)
|
|
200
|
-
--help, -h Show this help
|
|
201
|
-
|
|
202
|
-
Exit Codes:
|
|
203
|
-
0 No HIGH findings
|
|
204
|
-
1 MEDIUM/LOW findings only
|
|
205
|
-
2 At least one HIGH finding
|
|
206
|
-
3 Fatal error
|
|
207
|
-
`);
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
/**
|
|
211
|
-
* Handle 'verax scan' command - integrated with main pipeline
|
|
212
|
-
*/
|
|
213
|
-
async function handleScan(scanArgs) {
|
|
214
|
-
const opts = parseArgs(scanArgs);
|
|
215
|
-
|
|
216
|
-
if (!opts.url) {
|
|
217
|
-
console.error('Error: --url is required');
|
|
218
|
-
process.exit(3);
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
const projectRoot = opts.projectRoot || process.cwd();
|
|
222
|
-
const url = opts.url;
|
|
223
|
-
const jsonOutput = opts.json === true || opts.json === 'true';
|
|
224
|
-
const outDir = opts.out;
|
|
225
|
-
|
|
226
|
-
try {
|
|
227
|
-
initMetrics();
|
|
228
|
-
const startTime = Date.now();
|
|
229
|
-
|
|
230
|
-
// Initialize artifact paths
|
|
231
|
-
const artifactPaths = outDir
|
|
232
|
-
? { runDir: outDir, summary: `${outDir}/summary.json`, findings: `${outDir}/findings.json`, traces: `${outDir}/traces.jsonl` }
|
|
233
|
-
: initArtifactPaths(projectRoot);
|
|
234
|
-
|
|
235
|
-
// Ensure artifact directories exist
|
|
236
|
-
mkdirSync(artifactPaths.runDir, { recursive: true });
|
|
237
|
-
|
|
238
|
-
// Run the full VERAX pipeline using the scan() orchestrator
|
|
239
|
-
console.error(`[VERAX] Starting scan for ${url}`);
|
|
240
|
-
const resolvedRootResult = await resolveWorkspaceRoot(projectRoot);
|
|
241
|
-
const resolvedRoot = resolvedRootResult.workspaceRoot;
|
|
242
|
-
|
|
243
|
-
const scanStart = Date.now();
|
|
244
|
-
const { scan } = await import('../src/verax/index.js');
|
|
245
|
-
const result = await scan(resolvedRoot, url, null, artifactPaths.runId);
|
|
246
|
-
recordMetric('totalMs', Date.now() - scanStart);
|
|
247
|
-
|
|
248
|
-
const manifest = result.manifest;
|
|
249
|
-
const observation = result.observation;
|
|
250
|
-
const findings = result.findings;
|
|
251
|
-
|
|
252
|
-
const metrics = getMetrics();
|
|
253
|
-
metrics.totalMs = Date.now() - startTime;
|
|
254
|
-
|
|
255
|
-
// Count findings by level
|
|
256
|
-
const findingsList = findings?.findings || [];
|
|
257
|
-
const findingsCounts = {
|
|
258
|
-
HIGH: findingsList.filter(f => f.confidence?.level === 'HIGH').length,
|
|
259
|
-
MEDIUM: findingsList.filter(f => f.confidence?.level === 'MEDIUM').length,
|
|
260
|
-
LOW: findingsList.filter(f => f.confidence?.level === 'LOW').length,
|
|
261
|
-
UNKNOWN: findingsList.filter(f => !f.confidence?.level || f.confidence?.level === 'UNKNOWN').length
|
|
262
|
-
};
|
|
263
|
-
|
|
264
|
-
// Get top findings
|
|
265
|
-
const topFindings = findingsList
|
|
266
|
-
.slice(0, 3)
|
|
267
|
-
.map(f => ({
|
|
268
|
-
type: f.type,
|
|
269
|
-
reason: f.reason,
|
|
270
|
-
confidence: f.confidence?.score || 0
|
|
271
|
-
}));
|
|
272
|
-
|
|
273
|
-
// Write summary with metrics to artifact paths
|
|
274
|
-
writeSummary(artifactPaths, {
|
|
275
|
-
url,
|
|
276
|
-
projectRoot: resolvedRoot,
|
|
277
|
-
metrics,
|
|
278
|
-
findingsCounts,
|
|
279
|
-
topFindings
|
|
280
|
-
});
|
|
281
|
-
|
|
282
|
-
// Determine exit code
|
|
283
|
-
const HIGH_COUNT = findingsCounts.HIGH;
|
|
284
|
-
const hasAny = Object.values(findingsCounts).reduce((a, b) => a + b, 0) > 0;
|
|
285
|
-
|
|
286
|
-
if (jsonOutput) {
|
|
287
|
-
const summary = {
|
|
288
|
-
runId: artifactPaths.runId,
|
|
289
|
-
url,
|
|
290
|
-
metrics,
|
|
291
|
-
findingsCounts,
|
|
292
|
-
topFindings,
|
|
293
|
-
total: findingsList.length
|
|
294
|
-
};
|
|
295
|
-
console.log(JSON.stringify(summary, null, 2));
|
|
296
|
-
} else {
|
|
297
|
-
console.error(`[VERAX] Scan complete in ${(metrics.totalMs / 1000).toFixed(2)}s`);
|
|
298
|
-
console.error(`[VERAX] Findings: HIGH=${HIGH_COUNT}, MEDIUM=${findingsCounts.MEDIUM}, LOW=${findingsCounts.LOW}`);
|
|
299
|
-
if (topFindings.length > 0) {
|
|
300
|
-
console.error(`[VERAX] Top findings:`);
|
|
301
|
-
topFindings.forEach(f => {
|
|
302
|
-
console.error(` - [${f.confidence}%] ${f.type}: ${f.reason}`);
|
|
303
|
-
});
|
|
304
|
-
}
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
// Exit with appropriate code
|
|
308
|
-
if (HIGH_COUNT > 0) {
|
|
309
|
-
process.exit(2);
|
|
310
|
-
} else if (hasAny) {
|
|
311
|
-
process.exit(1);
|
|
312
|
-
} else {
|
|
313
|
-
process.exit(0);
|
|
314
|
-
}
|
|
315
|
-
} catch (error) {
|
|
316
|
-
if (!jsonOutput) {
|
|
317
|
-
console.error(`[VERAX] Scan failed: ${error.message}`);
|
|
318
|
-
}
|
|
319
|
-
process.exit(3);
|
|
320
|
-
}
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
/**
|
|
324
|
-
* Handle 'verax flow' command - execute a user flow
|
|
325
|
-
*/
|
|
326
|
-
async function handleFlow(flowArgs) {
|
|
327
|
-
const opts = parseArgs(flowArgs);
|
|
328
|
-
|
|
329
|
-
if (!opts.flow) {
|
|
330
|
-
console.error('Error: --flow is required');
|
|
331
|
-
process.exit(3);
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
const flowPath = resolve(opts.flow);
|
|
335
|
-
if (!existsSync(flowPath)) {
|
|
336
|
-
console.error(`Error: Flow file not found: ${flowPath}`);
|
|
337
|
-
process.exit(3);
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
const url = opts.url;
|
|
341
|
-
|
|
342
|
-
const jsonOutput = opts.json === true || opts.json === 'true';
|
|
343
|
-
const projectRoot = opts.projectRoot || process.cwd();
|
|
344
|
-
const outDir = opts.out;
|
|
345
|
-
|
|
346
|
-
try {
|
|
347
|
-
initMetrics();
|
|
348
|
-
const startTime = Date.now();
|
|
349
|
-
|
|
350
|
-
// Initialize artifact paths
|
|
351
|
-
const artifactPaths = outDir
|
|
352
|
-
? { runDir: outDir, flows: `${outDir}/flows` }
|
|
353
|
-
: initArtifactPaths(projectRoot);
|
|
354
|
-
mkdirSync(artifactPaths.runDir, { recursive: true });
|
|
355
|
-
if (artifactPaths.flows) {
|
|
356
|
-
mkdirSync(artifactPaths.flows, { recursive: true });
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
// Load and validate flow spec
|
|
360
|
-
const { readFileSync } = await import('fs');
|
|
361
|
-
const flowContent = readFileSync(flowPath, 'utf-8');
|
|
362
|
-
const flowSpec = JSON.parse(flowContent);
|
|
363
|
-
|
|
364
|
-
// Override baseUrl with --url if provided
|
|
365
|
-
if (url) {
|
|
366
|
-
flowSpec.baseUrl = url;
|
|
367
|
-
}
|
|
368
|
-
|
|
369
|
-
const { validateFlowSpec } = await import('../src/verax/flow/flow-spec.js');
|
|
370
|
-
const validatedSpec = validateFlowSpec(flowSpec);
|
|
371
|
-
|
|
372
|
-
// Use baseUrl from spec for navigation
|
|
373
|
-
const targetUrl = validatedSpec.baseUrl;
|
|
374
|
-
|
|
375
|
-
// Create browser and sensors
|
|
376
|
-
const { createBrowser, navigateToUrl, closeBrowser } = await import('../src/verax/observe/browser.js');
|
|
377
|
-
const { NetworkSensor } = await import('../src/verax/observe/network-sensor.js');
|
|
378
|
-
const { ConsoleSensor } = await import('../src/verax/observe/console-sensor.js');
|
|
379
|
-
const { UISignalSensor } = await import('../src/verax/observe/ui-signal-sensor.js');
|
|
380
|
-
|
|
381
|
-
const { browser, page } = await createBrowser();
|
|
382
|
-
const sensors = {
|
|
383
|
-
network: new NetworkSensor(),
|
|
384
|
-
console: new ConsoleSensor(),
|
|
385
|
-
uiSignals: new UISignalSensor()
|
|
386
|
-
};
|
|
387
|
-
|
|
388
|
-
try {
|
|
389
|
-
await navigateToUrl(page, targetUrl);
|
|
390
|
-
|
|
391
|
-
// Execute flow
|
|
392
|
-
const { executeFlow } = await import('../src/verax/flow/flow-engine.js');
|
|
393
|
-
const flowResult = await executeFlow(page, validatedSpec, sensors);
|
|
394
|
-
|
|
395
|
-
await closeBrowser(browser);
|
|
396
|
-
|
|
397
|
-
recordMetric('totalMs', Date.now() - startTime);
|
|
398
|
-
const metrics = getMetrics();
|
|
399
|
-
|
|
400
|
-
// Write flow results
|
|
401
|
-
const flowResultsPath = resolve(artifactPaths.flows || artifactPaths.runDir, 'flow-results.json');
|
|
402
|
-
writeFileSync(flowResultsPath, JSON.stringify({
|
|
403
|
-
flow: validatedSpec.name,
|
|
404
|
-
url: targetUrl,
|
|
405
|
-
success: flowResult.success,
|
|
406
|
-
findings: flowResult.findings,
|
|
407
|
-
stepResults: flowResult.stepResults,
|
|
408
|
-
metrics
|
|
409
|
-
}, null, 2) + '\n');
|
|
410
|
-
|
|
411
|
-
if (jsonOutput) {
|
|
412
|
-
console.log(JSON.stringify({
|
|
413
|
-
flow: validatedSpec.name,
|
|
414
|
-
url: targetUrl,
|
|
415
|
-
success: flowResult.success,
|
|
416
|
-
findingsCount: flowResult.findings.length,
|
|
417
|
-
metrics
|
|
418
|
-
}, null, 2));
|
|
419
|
-
} else {
|
|
420
|
-
console.error(`[VERAX] Flow execution complete in ${(metrics.totalMs / 1000).toFixed(2)}s`);
|
|
421
|
-
console.error(`[VERAX] Flow: ${validatedSpec.name}`);
|
|
422
|
-
console.error(`[VERAX] Success: ${flowResult.success}`);
|
|
423
|
-
console.error(`[VERAX] Findings: ${flowResult.findings.length}`);
|
|
424
|
-
if (flowResult.findings.length > 0) {
|
|
425
|
-
flowResult.findings.forEach((f, i) => {
|
|
426
|
-
console.error(` ${i + 1}. [Step ${f.stepIndex}] ${f.type}: ${f.reason}`);
|
|
427
|
-
});
|
|
428
|
-
}
|
|
429
|
-
}
|
|
430
|
-
|
|
431
|
-
// Exit code: 0 if success, 2 if findings, 3 if error
|
|
432
|
-
if (!flowResult.success) {
|
|
433
|
-
process.exit(2);
|
|
434
|
-
} else {
|
|
435
|
-
process.exit(0);
|
|
436
|
-
}
|
|
437
|
-
} catch (error) {
|
|
438
|
-
await closeBrowser(browser);
|
|
439
|
-
throw error;
|
|
440
|
-
}
|
|
441
|
-
} catch (error) {
|
|
442
|
-
if (!jsonOutput) {
|
|
443
|
-
console.error(`[VERAX] Flow execution failed: ${error.message}`);
|
|
444
|
-
}
|
|
445
|
-
process.exit(3);
|
|
446
|
-
}
|
|
447
|
-
}
|
|
448
|
-
|
|
449
|
-
main().catch((error) => {
|
|
450
|
-
console.error(`Fatal error: ${error.message}`);
|
|
451
|
-
process.exit(2);
|
|
452
|
-
});
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* VERAX CLI Shim
|
|
5
|
+
* Delegates to src/cli/entry.js
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import('../src/cli/entry.js').catch((error) => {
|
|
9
|
+
console.error(`Failed to load CLI: ${error.message}`);
|
|
10
|
+
process.exit(2);
|
|
11
|
+
});
|
package/package.json
CHANGED
|
@@ -1,57 +1,35 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@veraxhq/verax",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "VERAX - Silent failure detection for websites",
|
|
5
|
+
"license": "MIT",
|
|
5
6
|
"type": "module",
|
|
6
|
-
"main": "./src/verax/index.js",
|
|
7
7
|
"bin": {
|
|
8
|
-
"verax": "bin/verax.js"
|
|
8
|
+
"verax": "./bin/verax.js"
|
|
9
9
|
},
|
|
10
10
|
"files": [
|
|
11
|
-
"bin",
|
|
12
|
-
"src",
|
|
11
|
+
"bin/",
|
|
12
|
+
"src/",
|
|
13
|
+
"README.md",
|
|
13
14
|
"LICENSE"
|
|
14
15
|
],
|
|
15
|
-
"exports": {
|
|
16
|
-
".": {
|
|
17
|
-
"import": "./src/verax/index.js"
|
|
18
|
-
},
|
|
19
|
-
"./learn": {
|
|
20
|
-
"import": "./src/verax/learn/index.js"
|
|
21
|
-
},
|
|
22
|
-
"./observe": {
|
|
23
|
-
"import": "./src/verax/observe/index.js"
|
|
24
|
-
},
|
|
25
|
-
"./detect": {
|
|
26
|
-
"import": "./src/verax/detect/index.js"
|
|
27
|
-
}
|
|
28
|
-
},
|
|
29
|
-
"publishConfig": {
|
|
30
|
-
"access": "public"
|
|
31
|
-
},
|
|
32
16
|
"scripts": {
|
|
33
|
-
"test": "node --test
|
|
17
|
+
"test": "node --test",
|
|
18
|
+
"test:pack": "node scripts/test-pack.js"
|
|
34
19
|
},
|
|
35
20
|
"dependencies": {
|
|
36
|
-
"@babel/generator": "^7.28.5",
|
|
37
|
-
"@babel/parser": "^7.28.5",
|
|
38
|
-
"@babel/traverse": "^7.28.5",
|
|
39
|
-
"@babel/types": "^7.28.5",
|
|
40
21
|
"glob": "^10.3.10",
|
|
41
22
|
"inquirer": "^9.2.15",
|
|
42
23
|
"node-html-parser": "^7.0.1",
|
|
43
24
|
"playwright": "^1.40.0",
|
|
44
|
-
"typescript": "^5.
|
|
25
|
+
"typescript": "^5.9.3"
|
|
45
26
|
},
|
|
46
27
|
"engines": {
|
|
47
28
|
"node": ">=18.0.0"
|
|
48
29
|
},
|
|
49
|
-
"
|
|
50
|
-
"
|
|
51
|
-
"
|
|
52
|
-
"
|
|
53
|
-
|
|
54
|
-
"verification"
|
|
55
|
-
],
|
|
56
|
-
"license": "MIT"
|
|
30
|
+
"devDependencies": {
|
|
31
|
+
"@babel/parser": "^7.28.5",
|
|
32
|
+
"@babel/traverse": "^7.28.5",
|
|
33
|
+
"@veraxhq/verax": "file:veraxhq-verax-0.2.0.tgz"
|
|
34
|
+
}
|
|
57
35
|
}
|