@veraxhq/verax 0.2.1 → 0.4.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.
Files changed (213) hide show
  1. package/README.md +10 -6
  2. package/bin/verax.js +11 -11
  3. package/package.json +29 -8
  4. package/src/cli/commands/baseline.js +103 -0
  5. package/src/cli/commands/default.js +51 -6
  6. package/src/cli/commands/doctor.js +29 -0
  7. package/src/cli/commands/ga.js +246 -0
  8. package/src/cli/commands/gates.js +95 -0
  9. package/src/cli/commands/inspect.js +4 -2
  10. package/src/cli/commands/release-check.js +215 -0
  11. package/src/cli/commands/run.js +45 -6
  12. package/src/cli/commands/security-check.js +212 -0
  13. package/src/cli/commands/truth.js +113 -0
  14. package/src/cli/entry.js +30 -20
  15. package/src/cli/util/angular-component-extractor.js +179 -0
  16. package/src/cli/util/angular-navigation-detector.js +141 -0
  17. package/src/cli/util/angular-network-detector.js +161 -0
  18. package/src/cli/util/angular-state-detector.js +162 -0
  19. package/src/cli/util/ast-interactive-detector.js +544 -0
  20. package/src/cli/util/ast-network-detector.js +603 -0
  21. package/src/cli/util/ast-promise-extractor.js +581 -0
  22. package/src/cli/util/ast-usestate-detector.js +602 -0
  23. package/src/cli/util/atomic-write.js +12 -1
  24. package/src/cli/util/bootstrap-guard.js +86 -0
  25. package/src/cli/util/console-reporter.js +72 -0
  26. package/src/cli/util/detection-engine.js +105 -41
  27. package/src/cli/util/determinism-runner.js +124 -0
  28. package/src/cli/util/determinism-writer.js +129 -0
  29. package/src/cli/util/digest-engine.js +359 -0
  30. package/src/cli/util/dom-diff.js +226 -0
  31. package/src/cli/util/evidence-engine.js +287 -0
  32. package/src/cli/util/expectation-extractor.js +151 -5
  33. package/src/cli/util/findings-writer.js +3 -0
  34. package/src/cli/util/framework-detector.js +572 -0
  35. package/src/cli/util/idgen.js +1 -1
  36. package/src/cli/util/interaction-planner.js +529 -0
  37. package/src/cli/util/learn-writer.js +2 -0
  38. package/src/cli/util/ledger-writer.js +110 -0
  39. package/src/cli/util/monorepo-resolver.js +162 -0
  40. package/src/cli/util/observation-engine.js +127 -278
  41. package/src/cli/util/observe-writer.js +2 -0
  42. package/src/cli/util/project-discovery.js +284 -0
  43. package/src/cli/util/project-writer.js +2 -0
  44. package/src/cli/util/run-id.js +23 -27
  45. package/src/cli/util/run-resolver.js +64 -0
  46. package/src/cli/util/run-result.js +778 -0
  47. package/src/cli/util/selector-resolver.js +235 -0
  48. package/src/cli/util/source-requirement.js +55 -0
  49. package/src/cli/util/summary-writer.js +2 -0
  50. package/src/cli/util/svelte-navigation-detector.js +163 -0
  51. package/src/cli/util/svelte-network-detector.js +80 -0
  52. package/src/cli/util/svelte-sfc-extractor.js +146 -0
  53. package/src/cli/util/svelte-state-detector.js +242 -0
  54. package/src/cli/util/trust-activation-integration.js +496 -0
  55. package/src/cli/util/trust-activation-wrapper.js +85 -0
  56. package/src/cli/util/trust-integration-hooks.js +164 -0
  57. package/src/cli/util/types.js +153 -0
  58. package/src/cli/util/url-validation.js +40 -0
  59. package/src/cli/util/vue-navigation-detector.js +178 -0
  60. package/src/cli/util/vue-sfc-extractor.js +161 -0
  61. package/src/cli/util/vue-state-detector.js +215 -0
  62. package/src/types/fs-augment.d.ts +23 -0
  63. package/src/types/global.d.ts +137 -0
  64. package/src/types/internal-types.d.ts +35 -0
  65. package/src/verax/cli/init.js +4 -18
  66. package/src/verax/core/action-classifier.js +4 -3
  67. package/src/verax/core/artifacts/registry.js +139 -0
  68. package/src/verax/core/artifacts/verifier.js +990 -0
  69. package/src/verax/core/baseline/baseline.enforcer.js +137 -0
  70. package/src/verax/core/baseline/baseline.snapshot.js +233 -0
  71. package/src/verax/core/capabilities/gates.js +505 -0
  72. package/src/verax/core/capabilities/registry.js +475 -0
  73. package/src/verax/core/confidence/confidence-compute.js +144 -0
  74. package/src/verax/core/confidence/confidence-invariants.js +234 -0
  75. package/src/verax/core/confidence/confidence-report-writer.js +112 -0
  76. package/src/verax/core/confidence/confidence-weights.js +44 -0
  77. package/src/verax/core/confidence/confidence.defaults.js +65 -0
  78. package/src/verax/core/confidence/confidence.loader.js +80 -0
  79. package/src/verax/core/confidence/confidence.schema.js +94 -0
  80. package/src/verax/core/confidence-engine-refactor.js +489 -0
  81. package/src/verax/core/confidence-engine.js +625 -0
  82. package/src/verax/core/contracts/index.js +29 -0
  83. package/src/verax/core/contracts/types.js +186 -0
  84. package/src/verax/core/contracts/validators.js +456 -0
  85. package/src/verax/core/decisions/decision.trace.js +278 -0
  86. package/src/verax/core/determinism/contract-writer.js +89 -0
  87. package/src/verax/core/determinism/contract.js +139 -0
  88. package/src/verax/core/determinism/diff.js +405 -0
  89. package/src/verax/core/determinism/engine.js +222 -0
  90. package/src/verax/core/determinism/finding-identity.js +149 -0
  91. package/src/verax/core/determinism/normalize.js +466 -0
  92. package/src/verax/core/determinism/report-writer.js +93 -0
  93. package/src/verax/core/determinism/run-fingerprint.js +123 -0
  94. package/src/verax/core/dynamic-route-intelligence.js +529 -0
  95. package/src/verax/core/evidence/evidence-capture-service.js +308 -0
  96. package/src/verax/core/evidence/evidence-intent-ledger.js +166 -0
  97. package/src/verax/core/evidence-builder.js +487 -0
  98. package/src/verax/core/execution-mode-context.js +77 -0
  99. package/src/verax/core/execution-mode-detector.js +192 -0
  100. package/src/verax/core/failures/exit-codes.js +88 -0
  101. package/src/verax/core/failures/failure-summary.js +76 -0
  102. package/src/verax/core/failures/failure.factory.js +225 -0
  103. package/src/verax/core/failures/failure.ledger.js +133 -0
  104. package/src/verax/core/failures/failure.types.js +196 -0
  105. package/src/verax/core/failures/index.js +10 -0
  106. package/src/verax/core/ga/ga-report-writer.js +43 -0
  107. package/src/verax/core/ga/ga.artifact.js +49 -0
  108. package/src/verax/core/ga/ga.contract.js +435 -0
  109. package/src/verax/core/ga/ga.enforcer.js +87 -0
  110. package/src/verax/core/guardrails/guardrails-report-writer.js +109 -0
  111. package/src/verax/core/guardrails/policy.defaults.js +210 -0
  112. package/src/verax/core/guardrails/policy.loader.js +84 -0
  113. package/src/verax/core/guardrails/policy.schema.js +110 -0
  114. package/src/verax/core/guardrails/truth-reconciliation.js +136 -0
  115. package/src/verax/core/guardrails-engine.js +505 -0
  116. package/src/verax/core/incremental-store.js +1 -0
  117. package/src/verax/core/integrity/budget.js +138 -0
  118. package/src/verax/core/integrity/determinism.js +342 -0
  119. package/src/verax/core/integrity/integrity.js +208 -0
  120. package/src/verax/core/integrity/poisoning.js +108 -0
  121. package/src/verax/core/integrity/transaction.js +140 -0
  122. package/src/verax/core/observe/run-timeline.js +318 -0
  123. package/src/verax/core/perf/perf.contract.js +186 -0
  124. package/src/verax/core/perf/perf.display.js +65 -0
  125. package/src/verax/core/perf/perf.enforcer.js +91 -0
  126. package/src/verax/core/perf/perf.monitor.js +209 -0
  127. package/src/verax/core/perf/perf.report.js +200 -0
  128. package/src/verax/core/pipeline-tracker.js +243 -0
  129. package/src/verax/core/product-definition.js +127 -0
  130. package/src/verax/core/release/provenance.builder.js +130 -0
  131. package/src/verax/core/release/release-report-writer.js +40 -0
  132. package/src/verax/core/release/release.enforcer.js +164 -0
  133. package/src/verax/core/release/reproducibility.check.js +222 -0
  134. package/src/verax/core/release/sbom.builder.js +292 -0
  135. package/src/verax/core/replay-validator.js +2 -0
  136. package/src/verax/core/replay.js +4 -0
  137. package/src/verax/core/report/cross-index.js +195 -0
  138. package/src/verax/core/report/human-summary.js +362 -0
  139. package/src/verax/core/route-intelligence.js +420 -0
  140. package/src/verax/core/run-id.js +6 -3
  141. package/src/verax/core/run-manifest.js +4 -3
  142. package/src/verax/core/security/secrets.scan.js +329 -0
  143. package/src/verax/core/security/security-report.js +50 -0
  144. package/src/verax/core/security/security.enforcer.js +128 -0
  145. package/src/verax/core/security/supplychain.defaults.json +38 -0
  146. package/src/verax/core/security/supplychain.policy.js +334 -0
  147. package/src/verax/core/security/vuln.scan.js +265 -0
  148. package/src/verax/core/truth/truth.certificate.js +252 -0
  149. package/src/verax/core/ui-feedback-intelligence.js +481 -0
  150. package/src/verax/detect/conditional-ui-silent-failure.js +84 -0
  151. package/src/verax/detect/confidence-engine.js +62 -34
  152. package/src/verax/detect/confidence-helper.js +34 -0
  153. package/src/verax/detect/dynamic-route-findings.js +338 -0
  154. package/src/verax/detect/expectation-chain-detector.js +417 -0
  155. package/src/verax/detect/expectation-model.js +2 -2
  156. package/src/verax/detect/failure-cause-inference.js +293 -0
  157. package/src/verax/detect/findings-writer.js +131 -35
  158. package/src/verax/detect/flow-detector.js +2 -2
  159. package/src/verax/detect/form-silent-failure.js +98 -0
  160. package/src/verax/detect/index.js +46 -5
  161. package/src/verax/detect/invariants-enforcer.js +147 -0
  162. package/src/verax/detect/journey-stall-detector.js +558 -0
  163. package/src/verax/detect/navigation-silent-failure.js +82 -0
  164. package/src/verax/detect/problem-aggregator.js +361 -0
  165. package/src/verax/detect/route-findings.js +219 -0
  166. package/src/verax/detect/summary-writer.js +477 -0
  167. package/src/verax/detect/test-failure-cause-inference.js +314 -0
  168. package/src/verax/detect/ui-feedback-findings.js +207 -0
  169. package/src/verax/detect/view-switch-correlator.js +242 -0
  170. package/src/verax/flow/flow-engine.js +2 -1
  171. package/src/verax/flow/flow-spec.js +0 -6
  172. package/src/verax/index.js +4 -0
  173. package/src/verax/intel/ts-program.js +1 -0
  174. package/src/verax/intel/vue-navigation-extractor.js +3 -0
  175. package/src/verax/learn/action-contract-extractor.js +3 -0
  176. package/src/verax/learn/ast-contract-extractor.js +1 -1
  177. package/src/verax/learn/flow-extractor.js +1 -0
  178. package/src/verax/learn/project-detector.js +5 -0
  179. package/src/verax/learn/react-router-extractor.js +2 -0
  180. package/src/verax/learn/source-instrumenter.js +1 -0
  181. package/src/verax/learn/state-extractor.js +2 -1
  182. package/src/verax/learn/static-extractor.js +1 -0
  183. package/src/verax/observe/coverage-gaps.js +132 -0
  184. package/src/verax/observe/expectation-handler.js +126 -0
  185. package/src/verax/observe/incremental-skip.js +46 -0
  186. package/src/verax/observe/index.js +51 -155
  187. package/src/verax/observe/interaction-executor.js +192 -0
  188. package/src/verax/observe/interaction-runner.js +782 -513
  189. package/src/verax/observe/network-firewall.js +86 -0
  190. package/src/verax/observe/observation-builder.js +169 -0
  191. package/src/verax/observe/observe-context.js +205 -0
  192. package/src/verax/observe/observe-helpers.js +192 -0
  193. package/src/verax/observe/observe-runner.js +230 -0
  194. package/src/verax/observe/observers/budget-observer.js +185 -0
  195. package/src/verax/observe/observers/console-observer.js +102 -0
  196. package/src/verax/observe/observers/coverage-observer.js +107 -0
  197. package/src/verax/observe/observers/interaction-observer.js +471 -0
  198. package/src/verax/observe/observers/navigation-observer.js +132 -0
  199. package/src/verax/observe/observers/network-observer.js +87 -0
  200. package/src/verax/observe/observers/safety-observer.js +82 -0
  201. package/src/verax/observe/observers/ui-feedback-observer.js +99 -0
  202. package/src/verax/observe/page-traversal.js +138 -0
  203. package/src/verax/observe/snapshot-ops.js +94 -0
  204. package/src/verax/observe/ui-feedback-detector.js +742 -0
  205. package/src/verax/scan-summary-writer.js +2 -0
  206. package/src/verax/shared/artifact-manager.js +25 -5
  207. package/src/verax/shared/caching.js +1 -0
  208. package/src/verax/shared/css-spinner-rules.js +204 -0
  209. package/src/verax/shared/expectation-tracker.js +1 -0
  210. package/src/verax/shared/view-switch-rules.js +208 -0
  211. package/src/verax/shared/zip-artifacts.js +6 -0
  212. package/src/verax/shared/config-loader.js +0 -169
  213. /package/src/verax/shared/{expectation-proof.js → expectation-validation.js} +0 -0
@@ -33,6 +33,7 @@ export async function discoverProject(srcPath) {
33
33
  let packageJson = null;
34
34
  if (packageJsonPath && existsSync(packageJsonPath)) {
35
35
  try {
36
+ // @ts-expect-error - readFileSync with encoding returns string
36
37
  packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf8'));
37
38
  } catch (error) {
38
39
  packageJson = null;
@@ -64,6 +65,44 @@ export async function discoverProject(srcPath) {
64
65
  };
65
66
  }
66
67
 
68
+ /**
69
+ * Synchronous project profile resolver used by zero-config helpers
70
+ */
71
+ export function getProjectProfile(projectPath) {
72
+ const projectRoot = resolve(projectPath);
73
+ const packageJsonPath = findPackageJson(projectRoot);
74
+ const projectDir = packageJsonPath ? dirname(packageJsonPath) : projectRoot;
75
+
76
+ let packageJson = null;
77
+ if (packageJsonPath && existsSync(packageJsonPath)) {
78
+ try {
79
+ // @ts-expect-error - readFileSync with encoding returns string
80
+ packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf8'));
81
+ } catch {
82
+ packageJson = null;
83
+ }
84
+ }
85
+
86
+ const framework = detectFramework(projectDir, packageJson);
87
+ const router = detectRouter(framework, projectDir);
88
+ const packageManager = detectPackageManager(projectDir);
89
+ const scripts = {
90
+ dev: packageJson?.scripts?.dev || null,
91
+ build: packageJson?.scripts?.build || null,
92
+ start: packageJson?.scripts?.start || null,
93
+ };
94
+
95
+ return {
96
+ framework,
97
+ router,
98
+ sourceRoot: projectDir,
99
+ packageManager,
100
+ scripts,
101
+ detectedAt: new Date().toISOString(),
102
+ packageJsonPath,
103
+ };
104
+ }
105
+
67
106
  /**
68
107
  * Find the nearest package.json by walking up directories
69
108
  */
@@ -295,3 +334,248 @@ export function extractPortFromScript(script) {
295
334
 
296
335
  return null;
297
336
  }
337
+
338
+ /**
339
+ * Detect if running in CI environment (H5: Zero-Config)
340
+ */
341
+ export function isCI() {
342
+ return !!(
343
+ process.env.CI ||
344
+ process.env.CONTINUOUS_INTEGRATION ||
345
+ process.env.GITHUB_ACTIONS ||
346
+ process.env.GITLAB_CI ||
347
+ process.env.CIRCLECI ||
348
+ process.env.TRAVIS ||
349
+ process.env.BUILDKITE
350
+ );
351
+ }
352
+
353
+ /**
354
+ * Check if a port is accessible via HTTP (H5: Zero-Config)
355
+ */
356
+ export async function isPortAccessible(port, host = 'localhost', timeout = 3000) {
357
+ return new Promise(resolve => {
358
+ const _startTime = Date.now();
359
+ const timeoutId = setTimeout(() => {
360
+ resolve(false);
361
+ }, timeout);
362
+
363
+ try {
364
+ const http = require('http');
365
+ const req = http.get(
366
+ {
367
+ hostname: host,
368
+ port,
369
+ path: '/',
370
+ timeout: 1000,
371
+ },
372
+ () => {
373
+ clearTimeout(timeoutId);
374
+ resolve(true);
375
+ }
376
+ );
377
+
378
+ req.on('error', () => {
379
+ clearTimeout(timeoutId);
380
+ resolve(false);
381
+ });
382
+
383
+ req.on('timeout', () => {
384
+ clearTimeout(timeoutId);
385
+ req.destroy();
386
+ resolve(false);
387
+ });
388
+ } catch (e) {
389
+ clearTimeout(timeoutId);
390
+ resolve(false);
391
+ }
392
+ });
393
+ }
394
+
395
+ /**
396
+ * Infer URL from existing framework detection (H5: Zero-Config)
397
+ */
398
+ export async function inferURL(projectPath, profile, preferredPort = null) {
399
+ const result = {
400
+ url: null,
401
+ port: preferredPort || 3000,
402
+ discovered: false,
403
+ };
404
+
405
+ // Extract port from scripts if available
406
+ const profile2 = profile || getProjectProfile(projectPath);
407
+ if (profile2?.scripts?.dev && !preferredPort) {
408
+ const portFromScript = extractPortFromScript(profile2.scripts.dev);
409
+ if (portFromScript) {
410
+ result.port = portFromScript;
411
+ }
412
+ }
413
+
414
+ // Try preferred port
415
+ if (await isPortAccessible(result.port, 'localhost', 1000)) {
416
+ result.url = `http://localhost:${result.port}`;
417
+ result.discovered = true;
418
+ return result;
419
+ }
420
+
421
+ // Try common ports
422
+ const commonPorts = [3000, 5173, 4200, 8080, 8000];
423
+ for (const port of commonPorts) {
424
+ if (await isPortAccessible(port, 'localhost', 1000)) {
425
+ result.url = `http://localhost:${port}`;
426
+ result.port = port;
427
+ result.discovered = true;
428
+ return result;
429
+ }
430
+ }
431
+
432
+ // No port found
433
+ return result;
434
+ }
435
+
436
+ /**
437
+ * Full zero-config setup (H5: Zero-Config)
438
+ * Returns: { url, discovery }
439
+ */
440
+ export async function setupZeroConfig(projectPath, providedURL = null) {
441
+ const discovery = {
442
+ method: null,
443
+ framework: null,
444
+ serverAutoStarted: false,
445
+ details: {},
446
+ };
447
+
448
+ // If URL provided, use it immediately
449
+ if (providedURL) {
450
+ discovery.method = 'provided';
451
+ return {
452
+ url: providedURL,
453
+ discovery,
454
+ };
455
+ }
456
+
457
+ // Detect framework
458
+ const profile = getProjectProfile(projectPath);
459
+ discovery.framework = profile?.framework || 'unknown';
460
+
461
+ // Try to infer URL from existing ports
462
+ const inference = await inferURL(projectPath, profile);
463
+
464
+ if (inference.discovered) {
465
+ discovery.method = 'inferred';
466
+ discovery.details = {
467
+ port: inference.port,
468
+ framework: profile?.framework,
469
+ };
470
+ return {
471
+ url: inference.url,
472
+ discovery,
473
+ };
474
+ }
475
+
476
+ // Server not running and auto-start is disabled in CI
477
+ if (isCI()) {
478
+ throw new Error(
479
+ `No running server detected on port ${inference.port}. ` +
480
+ 'In CI environment, use --url to specify server URL.'
481
+ );
482
+ }
483
+
484
+ // H5: Auto-start dev server (max 30s startup)
485
+ try {
486
+ const started = await autoStartDevServer(projectPath, profile, inference.port);
487
+ if (started.success) {
488
+ discovery.method = 'auto-started';
489
+ discovery.serverAutoStarted = true;
490
+ discovery.details = {
491
+ port: started.port,
492
+ framework: profile?.framework,
493
+ startupTime: started.startupTime,
494
+ };
495
+ return {
496
+ url: `http://localhost:${started.port}`,
497
+ discovery,
498
+ };
499
+ }
500
+ } catch (e) {
501
+ // Fall through to error
502
+ }
503
+
504
+ // Could not start server
505
+ throw new Error(
506
+ `No running server detected on port ${inference.port}. ` +
507
+ 'Start your dev server or use --url to specify the server URL.'
508
+ );
509
+ }
510
+
511
+ /**
512
+ * H5: Auto-start development server
513
+ * Supports: Next.js, Vite, CRA, Vue, Svelte, Angular
514
+ * Max 30 second startup with automatic port detection
515
+ */
516
+ export async function autoStartDevServer(projectPath, profile = null, preferredPort = 3000) {
517
+ const prof = profile || getProjectProfile(projectPath);
518
+
519
+ if (!prof?.scripts?.dev) {
520
+ return { success: false, reason: 'no-dev-script' };
521
+ }
522
+
523
+ // Extract port from dev script
524
+ const port = extractPortFromScript(prof.scripts.dev) || preferredPort;
525
+
526
+ // Check if port is already accessible (server already running)
527
+ if (await isPortAccessible(port, 'localhost', 1000)) {
528
+ return { success: true, port, startupTime: 0 };
529
+ }
530
+
531
+ // Import required modules for spawning process
532
+ const { spawn } = await import('child_process');
533
+ const startTime = Date.now();
534
+ const MAX_STARTUP_TIME = 30 * 1000; // 30 seconds
535
+
536
+ return new Promise((resolve) => {
537
+ let resolved = false;
538
+
539
+ const process = spawn('npm', ['run', 'dev'], {
540
+ cwd: projectPath,
541
+ stdio: 'ignore',
542
+ detached: true,
543
+ });
544
+
545
+ // Detach the process so it doesn't keep this process alive
546
+ process.unref();
547
+
548
+ // Poll for port accessibility
549
+ const pollInterval = setInterval(async () => {
550
+ const elapsed = Date.now() - startTime;
551
+
552
+ if (elapsed > MAX_STARTUP_TIME) {
553
+ clearInterval(pollInterval);
554
+ if (!resolved) {
555
+ resolved = true;
556
+ resolve({ success: false, reason: 'startup-timeout', elapsed });
557
+ }
558
+ return;
559
+ }
560
+
561
+ if (await isPortAccessible(port, 'localhost', 1000)) {
562
+ clearInterval(pollInterval);
563
+ if (!resolved) {
564
+ resolved = true;
565
+ const startupTime = Date.now() - startTime;
566
+ resolve({ success: true, port, startupTime });
567
+ }
568
+ }
569
+ }, 500); // Check every 500ms
570
+
571
+ // Timeout safety
572
+ setTimeout(() => {
573
+ clearInterval(pollInterval);
574
+ if (!resolved) {
575
+ resolved = true;
576
+ resolve({ success: false, reason: 'startup-timeout', elapsed: MAX_STARTUP_TIME });
577
+ }
578
+ }, MAX_STARTUP_TIME + 1000);
579
+ });
580
+ }
581
+
@@ -1,5 +1,6 @@
1
1
  import { atomicWriteJson } from './atomic-write.js';
2
2
  import { resolve } from 'path';
3
+ import { ARTIFACT_REGISTRY } from '../../verax/core/artifacts/registry.js';
3
4
 
4
5
  /**
5
6
  * Write project profile artifact
@@ -8,6 +9,7 @@ export function writeProjectJson(runPaths, projectProfile) {
8
9
  const projectJsonPath = resolve(runPaths.baseDir, 'project.json');
9
10
 
10
11
  const projectJson = {
12
+ contractVersion: ARTIFACT_REGISTRY.project.contractVersion,
11
13
  framework: projectProfile.framework,
12
14
  router: projectProfile.router,
13
15
  sourceRoot: projectProfile.sourceRoot,
@@ -1,30 +1,26 @@
1
- import crypto from 'crypto';
1
+ import { generateRunId as generateDeterministicRunId } from '../../verax/core/run-id.js';
2
2
 
3
- /**
4
- * Generate a run ID in format: <ISO_TIMESTAMP_UTC>_<6-8 char short hash>
5
- * Example: 2026-01-11T00-59-12Z_4f2a9c
6
- */
7
- export function generateRunId() {
8
- // Create ISO timestamp with colons replaced by dashes for filesystem compatibility
9
- const now = new Date();
10
- const isoString = now.toISOString();
11
- // Format: 2026-01-11T00:59:12.123Z -> 2026-01-11T00-59-12Z
12
- const timestamp = isoString.replace(/:/g, '-').replace(/\.\d+Z/, 'Z');
13
-
14
- // Generate a short hash (6-8 chars)
15
- const hash = crypto
16
- .randomBytes(4)
17
- .toString('hex')
18
- .substring(0, 6);
19
-
20
- return `${timestamp}_${hash}`;
21
- }
3
+ const ZERO_BUDGET = Object.freeze({
4
+ maxScanDurationMs: 0,
5
+ maxInteractionsPerPage: 0,
6
+ maxUniqueUrls: 0,
7
+ interactionTimeoutMs: 0,
8
+ navigationTimeoutMs: 0,
9
+ });
22
10
 
23
- /**
24
- * Validate a run ID format
25
- */
26
- export function isValidRunId(runId) {
27
- // Pattern: YYYY-MM-DDTHH-MM-SSZ_hexchars
28
- const pattern = /^\d{4}-\d{2}-\d{2}T\d{2}-\d{2}-\d{2}Z_[a-f0-9]{6,8}$/;
29
- return pattern.test(runId);
11
+ // Deterministic run ID wrapper to align CLI with core generator (no time/randomness)
12
+ export function generateRunId(url = 'about:blank') {
13
+ let baseOrigin = 'about:blank';
14
+ try {
15
+ baseOrigin = new URL(url).origin;
16
+ } catch {
17
+ baseOrigin = url;
18
+ }
19
+ return generateDeterministicRunId({
20
+ url,
21
+ safetyFlags: {},
22
+ baseOrigin,
23
+ scanBudget: ZERO_BUDGET,
24
+ manifestPath: null,
25
+ });
30
26
  }
@@ -0,0 +1,64 @@
1
+ /**
2
+ * PHASE 21.6.1 — Run Resolver
3
+ *
4
+ * Pure filesystem logic to resolve run IDs.
5
+ * No side effects, no execution dependencies.
6
+ */
7
+
8
+ import { readdirSync, statSync, existsSync } from 'fs';
9
+ import { resolve } from 'path';
10
+
11
+ /**
12
+ * Find the latest run ID from .verax/runs/
13
+ *
14
+ * @param {string} projectDir - Project directory
15
+ * @returns {string|null} Latest run ID or null if no runs found
16
+ */
17
+ export function findLatestRunId(projectDir) {
18
+ const runsDir = resolve(projectDir, '.verax', 'runs');
19
+
20
+ if (!existsSync(runsDir)) {
21
+ return null;
22
+ }
23
+
24
+ try {
25
+ const runs = readdirSync(runsDir, { withFileTypes: true })
26
+ .filter(dirent => dirent.isDirectory())
27
+ .map(dirent => {
28
+ const runPath = resolve(runsDir, dirent.name);
29
+ try {
30
+ const stats = statSync(runPath);
31
+ return {
32
+ name: dirent.name,
33
+ mtimeMs: stats.mtimeMs
34
+ };
35
+ } catch {
36
+ return null;
37
+ }
38
+ })
39
+ .filter(run => run !== null);
40
+
41
+ if (runs.length === 0) {
42
+ return null;
43
+ }
44
+
45
+ // Sort by modification time (descending) and return latest
46
+ runs.sort((a, b) => b.mtimeMs - a.mtimeMs);
47
+ return runs[0].name;
48
+ } catch (error) {
49
+ return null;
50
+ }
51
+ }
52
+
53
+ /**
54
+ * Validate that a run ID exists
55
+ *
56
+ * @param {string} projectDir - Project directory
57
+ * @param {string} runId - Run ID to validate
58
+ * @returns {boolean} Whether run exists
59
+ */
60
+ export function validateRunId(projectDir, runId) {
61
+ const runDir = resolve(projectDir, '.verax', 'runs', runId);
62
+ return existsSync(runDir);
63
+ }
64
+