@tangle-network/browser-agent-driver 0.30.0 → 0.31.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/dist/cli-ack-patch.d.ts +39 -0
- package/dist/cli-ack-patch.d.ts.map +1 -0
- package/dist/cli-ack-patch.js +47 -0
- package/dist/cli-ack-patch.js.map +1 -0
- package/dist/cli-design-audit.d.ts +12 -0
- package/dist/cli-design-audit.d.ts.map +1 -1
- package/dist/cli-design-audit.js +88 -0
- package/dist/cli-design-audit.js.map +1 -1
- package/dist/cli-jobs.d.ts +13 -0
- package/dist/cli-jobs.d.ts.map +1 -0
- package/dist/cli-jobs.js +262 -0
- package/dist/cli-jobs.js.map +1 -0
- package/dist/cli-patterns.d.ts +26 -0
- package/dist/cli-patterns.d.ts.map +1 -0
- package/dist/cli-patterns.js +49 -0
- package/dist/cli-patterns.js.map +1 -0
- package/dist/cli-reports.d.ts +12 -0
- package/dist/cli-reports.d.ts.map +1 -0
- package/dist/cli-reports.js +97 -0
- package/dist/cli-reports.js.map +1 -0
- package/dist/cli.js +29 -1
- package/dist/cli.js.map +1 -1
- package/dist/design/audit/attribution/aggregate.d.ts +16 -0
- package/dist/design/audit/attribution/aggregate.d.ts.map +1 -0
- package/dist/design/audit/attribution/aggregate.js +81 -0
- package/dist/design/audit/attribution/aggregate.js.map +1 -0
- package/dist/design/audit/attribution/store.d.ts +36 -0
- package/dist/design/audit/attribution/store.d.ts.map +1 -0
- package/dist/design/audit/attribution/store.js +108 -0
- package/dist/design/audit/attribution/store.js.map +1 -0
- package/dist/design/audit/attribution/types.d.ts +13 -0
- package/dist/design/audit/attribution/types.d.ts.map +1 -0
- package/dist/design/audit/attribution/types.js +10 -0
- package/dist/design/audit/attribution/types.js.map +1 -0
- package/dist/design/audit/classify-ensemble.d.ts +46 -0
- package/dist/design/audit/classify-ensemble.d.ts.map +1 -0
- package/dist/design/audit/classify-ensemble.js +207 -0
- package/dist/design/audit/classify-ensemble.js.map +1 -0
- package/dist/design/audit/ethics/check.d.ts +59 -0
- package/dist/design/audit/ethics/check.d.ts.map +1 -0
- package/dist/design/audit/ethics/check.js +137 -0
- package/dist/design/audit/ethics/check.js.map +1 -0
- package/dist/design/audit/ethics/loader.d.ts +21 -0
- package/dist/design/audit/ethics/loader.d.ts.map +1 -0
- package/dist/design/audit/ethics/loader.js +207 -0
- package/dist/design/audit/ethics/loader.js.map +1 -0
- package/dist/design/audit/ethics/rules/finance.yaml +37 -0
- package/dist/design/audit/ethics/rules/kids.yaml +37 -0
- package/dist/design/audit/ethics/rules/legal.yaml +35 -0
- package/dist/design/audit/ethics/rules/medical.yaml +36 -0
- package/dist/design/audit/evaluate.d.ts.map +1 -1
- package/dist/design/audit/evaluate.js +4 -1
- package/dist/design/audit/evaluate.js.map +1 -1
- package/dist/design/audit/first-principles-mode.d.ts +42 -0
- package/dist/design/audit/first-principles-mode.d.ts.map +1 -0
- package/dist/design/audit/first-principles-mode.js +77 -0
- package/dist/design/audit/first-principles-mode.js.map +1 -0
- package/dist/design/audit/modality/android.d.ts +14 -0
- package/dist/design/audit/modality/android.d.ts.map +1 -0
- package/dist/design/audit/modality/android.js +18 -0
- package/dist/design/audit/modality/android.js.map +1 -0
- package/dist/design/audit/modality/html.d.ts +22 -0
- package/dist/design/audit/modality/html.d.ts.map +1 -0
- package/dist/design/audit/modality/html.js +51 -0
- package/dist/design/audit/modality/html.js.map +1 -0
- package/dist/design/audit/modality/index.d.ts +8 -0
- package/dist/design/audit/modality/index.d.ts.map +1 -0
- package/dist/design/audit/modality/index.js +15 -0
- package/dist/design/audit/modality/index.js.map +1 -0
- package/dist/design/audit/modality/ios.d.ts +16 -0
- package/dist/design/audit/modality/ios.d.ts.map +1 -0
- package/dist/design/audit/modality/ios.js +19 -0
- package/dist/design/audit/modality/ios.js.map +1 -0
- package/dist/design/audit/modality/types.d.ts +9 -0
- package/dist/design/audit/modality/types.d.ts.map +1 -0
- package/dist/design/audit/modality/types.js +9 -0
- package/dist/design/audit/modality/types.js.map +1 -0
- package/dist/design/audit/patches/index.d.ts +8 -0
- package/dist/design/audit/patches/index.d.ts.map +1 -0
- package/dist/design/audit/patches/index.js +5 -0
- package/dist/design/audit/patches/index.js.map +1 -0
- package/dist/design/audit/patches/parse.d.ts +29 -0
- package/dist/design/audit/patches/parse.d.ts.map +1 -0
- package/dist/design/audit/patches/parse.js +153 -0
- package/dist/design/audit/patches/parse.js.map +1 -0
- package/dist/design/audit/patches/render.d.ts +20 -0
- package/dist/design/audit/patches/render.d.ts.map +1 -0
- package/dist/design/audit/patches/render.js +55 -0
- package/dist/design/audit/patches/render.js.map +1 -0
- package/dist/design/audit/patches/severity-enforcement.d.ts +23 -0
- package/dist/design/audit/patches/severity-enforcement.d.ts.map +1 -0
- package/dist/design/audit/patches/severity-enforcement.js +42 -0
- package/dist/design/audit/patches/severity-enforcement.js.map +1 -0
- package/dist/design/audit/patches/validate.d.ts +34 -0
- package/dist/design/audit/patches/validate.d.ts.map +1 -0
- package/dist/design/audit/patches/validate.js +53 -0
- package/dist/design/audit/patches/validate.js.map +1 -0
- package/dist/design/audit/patterns/match.d.ts +24 -0
- package/dist/design/audit/patterns/match.d.ts.map +1 -0
- package/dist/design/audit/patterns/match.js +48 -0
- package/dist/design/audit/patterns/match.js.map +1 -0
- package/dist/design/audit/patterns/mine.d.ts +31 -0
- package/dist/design/audit/patterns/mine.d.ts.map +1 -0
- package/dist/design/audit/patterns/mine.js +35 -0
- package/dist/design/audit/patterns/mine.js.map +1 -0
- package/dist/design/audit/patterns/store.d.ts +14 -0
- package/dist/design/audit/patterns/store.d.ts.map +1 -0
- package/dist/design/audit/patterns/store.js +53 -0
- package/dist/design/audit/patterns/store.js.map +1 -0
- package/dist/design/audit/patterns/types.d.ts +53 -0
- package/dist/design/audit/patterns/types.d.ts.map +1 -0
- package/dist/design/audit/patterns/types.js +13 -0
- package/dist/design/audit/patterns/types.js.map +1 -0
- package/dist/design/audit/pipeline.d.ts +12 -0
- package/dist/design/audit/pipeline.d.ts.map +1 -1
- package/dist/design/audit/pipeline.js +65 -3
- package/dist/design/audit/pipeline.js.map +1 -1
- package/dist/design/audit/rubric/anchor-loader.d.ts +44 -0
- package/dist/design/audit/rubric/anchor-loader.d.ts.map +1 -0
- package/dist/design/audit/rubric/anchor-loader.js +210 -0
- package/dist/design/audit/rubric/anchor-loader.js.map +1 -0
- package/dist/design/audit/rubric/anchors/blog.yaml +33 -0
- package/dist/design/audit/rubric/anchors/dashboard.yaml +35 -0
- package/dist/design/audit/rubric/anchors/docs.yaml +35 -0
- package/dist/design/audit/rubric/anchors/ecommerce.yaml +33 -0
- package/dist/design/audit/rubric/anchors/marketing.yaml +34 -0
- package/dist/design/audit/rubric/anchors/saas-app.yaml +38 -0
- package/dist/design/audit/rubric/anchors/social.yaml +33 -0
- package/dist/design/audit/rubric/anchors/tool.yaml +33 -0
- package/dist/design/audit/rubric/anchors/utility.yaml +33 -0
- package/dist/design/audit/rubric/fragments/audience-clinician.md +42 -0
- package/dist/design/audit/rubric/fragments/audience-developer.md +40 -0
- package/dist/design/audit/rubric/fragments/audience-kids.md +35 -0
- package/dist/design/audit/rubric/fragments/audience-vulnerability-minor-facing.md +37 -0
- package/dist/design/audit/rubric/fragments/first-principles.md +65 -0
- package/dist/design/audit/rubric/fragments/modality-mobile.md +39 -0
- package/dist/design/audit/rubric/fragments/modality-tablet.md +35 -0
- package/dist/design/audit/rubric/fragments/regulatory-coppa.md +32 -0
- package/dist/design/audit/rubric/fragments/regulatory-gdpr.md +33 -0
- package/dist/design/audit/rubric/fragments/regulatory-hipaa.md +36 -0
- package/dist/design/audit/rubric/loader.d.ts +26 -6
- package/dist/design/audit/rubric/loader.d.ts.map +1 -1
- package/dist/design/audit/rubric/loader.js +45 -26
- package/dist/design/audit/rubric/loader.js.map +1 -1
- package/dist/design/audit/rubric/rollup-weights.d.ts +25 -0
- package/dist/design/audit/rubric/rollup-weights.d.ts.map +1 -0
- package/dist/design/audit/rubric/rollup-weights.js +51 -0
- package/dist/design/audit/rubric/rollup-weights.js.map +1 -0
- package/dist/design/audit/types.d.ts +28 -0
- package/dist/design/audit/types.d.ts.map +1 -1
- package/dist/design/audit/v2/build-result.d.ts +47 -0
- package/dist/design/audit/v2/build-result.d.ts.map +1 -0
- package/dist/design/audit/v2/build-result.js +163 -0
- package/dist/design/audit/v2/build-result.js.map +1 -0
- package/dist/design/audit/v2/score.d.ts +49 -0
- package/dist/design/audit/v2/score.d.ts.map +1 -0
- package/dist/design/audit/v2/score.js +211 -0
- package/dist/design/audit/v2/score.js.map +1 -0
- package/dist/design/audit/v2/types.d.ts +431 -0
- package/dist/design/audit/v2/types.d.ts.map +1 -0
- package/dist/design/audit/v2/types.js +26 -0
- package/dist/design/audit/v2/types.js.map +1 -0
- package/dist/discover/index.d.ts +16 -0
- package/dist/discover/index.d.ts.map +1 -0
- package/dist/discover/index.js +25 -0
- package/dist/discover/index.js.map +1 -0
- package/dist/discover/wayback.d.ts +49 -0
- package/dist/discover/wayback.d.ts.map +1 -0
- package/dist/discover/wayback.js +123 -0
- package/dist/discover/wayback.js.map +1 -0
- package/dist/jobs/anti-bot.d.ts +19 -0
- package/dist/jobs/anti-bot.d.ts.map +1 -0
- package/dist/jobs/anti-bot.js +64 -0
- package/dist/jobs/anti-bot.js.map +1 -0
- package/dist/jobs/cost-estimate.d.ts +12 -0
- package/dist/jobs/cost-estimate.d.ts.map +1 -0
- package/dist/jobs/cost-estimate.js +21 -0
- package/dist/jobs/cost-estimate.js.map +1 -0
- package/dist/jobs/cost-history.d.ts +19 -0
- package/dist/jobs/cost-history.d.ts.map +1 -0
- package/dist/jobs/cost-history.js +42 -0
- package/dist/jobs/cost-history.js.map +1 -0
- package/dist/jobs/index.d.ts +31 -0
- package/dist/jobs/index.d.ts.map +1 -0
- package/dist/jobs/index.js +37 -0
- package/dist/jobs/index.js.map +1 -0
- package/dist/jobs/orchestrator.d.ts +42 -0
- package/dist/jobs/orchestrator.d.ts.map +1 -0
- package/dist/jobs/orchestrator.js +282 -0
- package/dist/jobs/orchestrator.js.map +1 -0
- package/dist/jobs/queue.d.ts +45 -0
- package/dist/jobs/queue.d.ts.map +1 -0
- package/dist/jobs/queue.js +114 -0
- package/dist/jobs/queue.js.map +1 -0
- package/dist/jobs/retry.d.ts +36 -0
- package/dist/jobs/retry.d.ts.map +1 -0
- package/dist/jobs/retry.js +77 -0
- package/dist/jobs/retry.js.map +1 -0
- package/dist/jobs/store.d.ts +31 -0
- package/dist/jobs/store.d.ts.map +1 -0
- package/dist/jobs/store.js +92 -0
- package/dist/jobs/store.js.map +1 -0
- package/dist/jobs/types.d.ts +106 -0
- package/dist/jobs/types.d.ts.map +1 -0
- package/dist/jobs/types.js +13 -0
- package/dist/jobs/types.js.map +1 -0
- package/dist/reports/aggregate.d.ts +38 -0
- package/dist/reports/aggregate.d.ts.map +1 -0
- package/dist/reports/aggregate.js +145 -0
- package/dist/reports/aggregate.js.map +1 -0
- package/dist/reports/index.d.ts +21 -0
- package/dist/reports/index.d.ts.map +1 -0
- package/dist/reports/index.js +15 -0
- package/dist/reports/index.js.map +1 -0
- package/dist/reports/narrate.d.ts +19 -0
- package/dist/reports/narrate.d.ts.map +1 -0
- package/dist/reports/narrate.js +40 -0
- package/dist/reports/narrate.js.map +1 -0
- package/dist/reports/templates.d.ts +43 -0
- package/dist/reports/templates.d.ts.map +1 -0
- package/dist/reports/templates.js +200 -0
- package/dist/reports/templates.js.map +1 -0
- package/dist/reports/tokens.d.ts +77 -0
- package/dist/reports/tokens.d.ts.map +1 -0
- package/dist/reports/tokens.js +88 -0
- package/dist/reports/tokens.js.map +1 -0
- package/dist/reports/tools.d.ts +76 -0
- package/dist/reports/tools.d.ts.map +1 -0
- package/dist/reports/tools.js +205 -0
- package/dist/reports/tools.js.map +1 -0
- package/dist/reports/types.d.ts +51 -0
- package/dist/reports/types.d.ts.map +1 -0
- package/dist/reports/types.js +9 -0
- package/dist/reports/types.js.map +1 -0
- package/package.json +2 -1
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Job store — whole-file JSON per job + an append-only index for listing.
|
|
3
|
+
*
|
|
4
|
+
* Layout:
|
|
5
|
+
* ~/.bad/jobs/<jobId>.json — Job record (atomic write via tmp + rename)
|
|
6
|
+
* ~/.bad/jobs/index.jsonl — append-only one line per job ({jobId, status, createdAt, label})
|
|
7
|
+
*
|
|
8
|
+
* Whole-file rewrite is intentional: a job is a single coherent record with
|
|
9
|
+
* monotonically-extending `results[]`. Append-only at the entry level would
|
|
10
|
+
* duplicate target rows; append-only at the file level would force readers
|
|
11
|
+
* to fold N updates into one logical record. Atomic rename gives us crash
|
|
12
|
+
* safety without that complexity.
|
|
13
|
+
*/
|
|
14
|
+
import * as fs from 'node:fs';
|
|
15
|
+
import * as path from 'node:path';
|
|
16
|
+
import * as os from 'node:os';
|
|
17
|
+
const DEFAULT_DIR = path.join(os.homedir(), '.bad', 'jobs');
|
|
18
|
+
export function jobsDir(override) {
|
|
19
|
+
return override ?? DEFAULT_DIR;
|
|
20
|
+
}
|
|
21
|
+
function ensureDir(dir) {
|
|
22
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
23
|
+
}
|
|
24
|
+
export function jobPath(jobId, dir) {
|
|
25
|
+
return path.join(jobsDir(dir), `${jobId}.json`);
|
|
26
|
+
}
|
|
27
|
+
function indexPath(dir) {
|
|
28
|
+
return path.join(jobsDir(dir), 'index.jsonl');
|
|
29
|
+
}
|
|
30
|
+
export function newJobId(now = new Date()) {
|
|
31
|
+
const stamp = now.toISOString().replace(/[-:.]/g, '').slice(0, 15); // YYYYMMDDTHHMMSS
|
|
32
|
+
const rand = Math.random().toString(36).slice(2, 8);
|
|
33
|
+
return `job_${stamp}_${rand}`;
|
|
34
|
+
}
|
|
35
|
+
export function saveJob(job, dir) {
|
|
36
|
+
const target = jobPath(job.jobId, dir);
|
|
37
|
+
ensureDir(path.dirname(target));
|
|
38
|
+
const tmp = `${target}.tmp`;
|
|
39
|
+
fs.writeFileSync(tmp, JSON.stringify(job, null, 2));
|
|
40
|
+
fs.renameSync(tmp, target);
|
|
41
|
+
}
|
|
42
|
+
export function loadJob(jobId, dir) {
|
|
43
|
+
const target = jobPath(jobId, dir);
|
|
44
|
+
if (!fs.existsSync(target))
|
|
45
|
+
return null;
|
|
46
|
+
return JSON.parse(fs.readFileSync(target, 'utf-8'));
|
|
47
|
+
}
|
|
48
|
+
export function appendIndexEntry(job, dir) {
|
|
49
|
+
const file = indexPath(dir);
|
|
50
|
+
ensureDir(path.dirname(file));
|
|
51
|
+
const line = JSON.stringify({
|
|
52
|
+
jobId: job.jobId,
|
|
53
|
+
status: job.status,
|
|
54
|
+
createdAt: job.createdAt,
|
|
55
|
+
label: job.spec.label ?? null,
|
|
56
|
+
targetCount: job.targets.length,
|
|
57
|
+
});
|
|
58
|
+
fs.appendFileSync(file, line + '\n');
|
|
59
|
+
}
|
|
60
|
+
/** Reads the index file. Later entries for the same jobId override earlier ones. */
|
|
61
|
+
export function listJobs(dir) {
|
|
62
|
+
const file = indexPath(dir);
|
|
63
|
+
if (!fs.existsSync(file))
|
|
64
|
+
return [];
|
|
65
|
+
const lines = fs.readFileSync(file, 'utf-8').split('\n').filter(Boolean);
|
|
66
|
+
const map = new Map();
|
|
67
|
+
for (const line of lines) {
|
|
68
|
+
try {
|
|
69
|
+
const entry = JSON.parse(line);
|
|
70
|
+
map.set(entry.jobId, entry);
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
// Skip malformed lines — index is best-effort.
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return Array.from(map.values()).sort((a, b) => b.createdAt.localeCompare(a.createdAt));
|
|
77
|
+
}
|
|
78
|
+
export function updateJobStatus(jobId, status, dir) {
|
|
79
|
+
const job = loadJob(jobId, dir);
|
|
80
|
+
if (!job)
|
|
81
|
+
return null;
|
|
82
|
+
job.status = status;
|
|
83
|
+
if (status === 'running' && !job.startedAt)
|
|
84
|
+
job.startedAt = new Date().toISOString();
|
|
85
|
+
if (status === 'completed' || status === 'failed' || status === 'cancelled' || status === 'partial') {
|
|
86
|
+
job.completedAt = new Date().toISOString();
|
|
87
|
+
}
|
|
88
|
+
saveJob(job, dir);
|
|
89
|
+
appendIndexEntry(job, dir);
|
|
90
|
+
return job;
|
|
91
|
+
}
|
|
92
|
+
//# sourceMappingURL=store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store.js","sourceRoot":"","sources":["../../src/jobs/store.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Job orchestration types — RFC-003: comparative-audit jobs.
|
|
3
|
+
*
|
|
4
|
+
* A job is a declarative spec ("audit these N URLs, optionally with M historical
|
|
5
|
+
* snapshots each") that fans out to the existing design-audit pipeline and
|
|
6
|
+
* persists the aggregate result so report generation can run later.
|
|
7
|
+
*
|
|
8
|
+
* Persistence: append-only JSONL at `~/.bad/jobs/<jobId>.json` (whole-file
|
|
9
|
+
* rewrites are fine — jobs are small) plus a one-line index entry at
|
|
10
|
+
* `~/.bad/jobs/index.jsonl` for fast listing.
|
|
11
|
+
*/
|
|
12
|
+
import type { AudienceTag, ModalityTag, RegulatoryContextTag, AudienceVulnerabilityTag } from '../design/audit/v2/types.js';
|
|
13
|
+
export type JobKind = 'comparative-audit';
|
|
14
|
+
export type JobStatus = 'queued' | 'running' | 'partial' | 'completed' | 'failed' | 'cancelled';
|
|
15
|
+
export type DiscoverSource = 'list' | 'wayback';
|
|
16
|
+
/** What targets to audit. `list` = explicit URLs; `wayback` = expand each URL into historical snapshots. */
|
|
17
|
+
export interface DiscoverSpec {
|
|
18
|
+
source: DiscoverSource;
|
|
19
|
+
/** Explicit list of URLs (always required — it's the seed even for wayback). */
|
|
20
|
+
urls: string[];
|
|
21
|
+
/** wayback only: how many evenly-spaced snapshots to fetch per URL. Default 5. */
|
|
22
|
+
snapshotsPerUrl?: number;
|
|
23
|
+
/** wayback only: ISO date lower bound (e.g. "2010-01-01"). */
|
|
24
|
+
since?: string;
|
|
25
|
+
/** wayback only: ISO date upper bound (e.g. "2026-01-01"). */
|
|
26
|
+
until?: string;
|
|
27
|
+
}
|
|
28
|
+
/** Pass-through audit options — mirrors the design-audit CLI flags. */
|
|
29
|
+
export interface AuditOptions {
|
|
30
|
+
pages?: number;
|
|
31
|
+
modality?: 'html' | 'ios' | 'android';
|
|
32
|
+
audience?: AudienceTag[];
|
|
33
|
+
audienceVulnerability?: AudienceVulnerabilityTag[];
|
|
34
|
+
modalityTag?: ModalityTag[];
|
|
35
|
+
regulatoryContext?: RegulatoryContextTag[];
|
|
36
|
+
headless?: boolean;
|
|
37
|
+
skipEthics?: boolean;
|
|
38
|
+
/**
|
|
39
|
+
* Layer 8 add-on: also run the deterministic brand/design-token extractor at
|
|
40
|
+
* every target. Adds ~10s/target (no LLM). Output lands at
|
|
41
|
+
* `<resultPath dir>/tokens.json` and is surfaced via `JobResultEntry.tokensPath`.
|
|
42
|
+
*/
|
|
43
|
+
extractTokens?: boolean;
|
|
44
|
+
}
|
|
45
|
+
export interface JobSpec {
|
|
46
|
+
kind: JobKind;
|
|
47
|
+
discover: DiscoverSpec;
|
|
48
|
+
audit?: AuditOptions;
|
|
49
|
+
/** Bounded concurrency for the audit fan-out. Default 2 — Playwright + LLM rate limits cap real-world throughput. */
|
|
50
|
+
concurrency?: number;
|
|
51
|
+
/** Hard cost cap. The job aborts pre-flight if estimated cost exceeds this. */
|
|
52
|
+
maxCostUSD?: number;
|
|
53
|
+
/** Free-form label for humans (shows up in `bad jobs list`). */
|
|
54
|
+
label?: string;
|
|
55
|
+
}
|
|
56
|
+
/** One target = one (url, optional snapshot) pair the auditor will run on. */
|
|
57
|
+
export interface JobTarget {
|
|
58
|
+
/** The original seed URL. For non-wayback discovery, this is also the audit URL. */
|
|
59
|
+
url: string;
|
|
60
|
+
/** For wayback targets: the actual snapshot URL passed to the audit. */
|
|
61
|
+
snapshotUrl?: string;
|
|
62
|
+
/** For wayback targets: ISO datetime when the snapshot was captured. */
|
|
63
|
+
capturedAt?: string;
|
|
64
|
+
}
|
|
65
|
+
export type JobResultStatus = 'ok' | 'failed' | 'skipped';
|
|
66
|
+
export interface JobResultEntry extends JobTarget {
|
|
67
|
+
status: JobResultStatus;
|
|
68
|
+
/** runId of the design-audit run when status === 'ok'. */
|
|
69
|
+
runId?: string;
|
|
70
|
+
/** Path to the run's report.json (relative to cwd or absolute). */
|
|
71
|
+
resultPath?: string;
|
|
72
|
+
/** Failure reason when status === 'failed' or 'skipped'. */
|
|
73
|
+
error?: string;
|
|
74
|
+
/** Estimated USD cost of this audit (if available from telemetry). */
|
|
75
|
+
costUSD?: number;
|
|
76
|
+
/** Roll-up score copied out of the report for fast aggregation queries. */
|
|
77
|
+
rollupScore?: number;
|
|
78
|
+
/** Page-type classification. */
|
|
79
|
+
pageType?: string;
|
|
80
|
+
/** Path to the tokens.json from the brand-kit extractor (when extractTokens=true). */
|
|
81
|
+
tokensPath?: string;
|
|
82
|
+
}
|
|
83
|
+
export interface Job {
|
|
84
|
+
jobId: string;
|
|
85
|
+
spec: JobSpec;
|
|
86
|
+
status: JobStatus;
|
|
87
|
+
createdAt: string;
|
|
88
|
+
startedAt?: string;
|
|
89
|
+
completedAt?: string;
|
|
90
|
+
/** All discovered targets (length = number of audits the job will / did run). */
|
|
91
|
+
targets: JobTarget[];
|
|
92
|
+
/** Per-target outcome. Length matches `targets` once the job has progressed past discovery. */
|
|
93
|
+
results: JobResultEntry[];
|
|
94
|
+
/** Sum of `results[*].costUSD` for completed entries. */
|
|
95
|
+
totalCostUSD: number;
|
|
96
|
+
/** Free-form notes (errors, warnings, telemetry summary). */
|
|
97
|
+
notes?: string[];
|
|
98
|
+
}
|
|
99
|
+
export interface CostEstimate {
|
|
100
|
+
targetCount: number;
|
|
101
|
+
estimatedTotalUSD: number;
|
|
102
|
+
perAuditUSD: number;
|
|
103
|
+
/** Whether the estimate is above `spec.maxCostUSD`. */
|
|
104
|
+
exceedsCap: boolean;
|
|
105
|
+
}
|
|
106
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/jobs/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Job orchestration types — RFC-003: comparative-audit jobs.
|
|
3
|
+
*
|
|
4
|
+
* A job is a declarative spec ("audit these N URLs, optionally with M historical
|
|
5
|
+
* snapshots each") that fans out to the existing design-audit pipeline and
|
|
6
|
+
* persists the aggregate result so report generation can run later.
|
|
7
|
+
*
|
|
8
|
+
* Persistence: append-only JSONL at `~/.bad/jobs/<jobId>.json` (whole-file
|
|
9
|
+
* rewrites are fine — jobs are small) plus a one-line index entry at
|
|
10
|
+
* `~/.bad/jobs/index.jsonl` for fast listing.
|
|
11
|
+
*/
|
|
12
|
+
export {};
|
|
13
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/jobs/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deterministic aggregation over a job's per-target audit results.
|
|
3
|
+
*
|
|
4
|
+
* Every number that shows up in a report flows through here — never through
|
|
5
|
+
* an LLM. The narration layer can describe / contextualize / dramatize, but
|
|
6
|
+
* counts, scores, deltas, and rankings are all computed here. Same pattern
|
|
7
|
+
* we use for the audit patches contract: agent narrates, code computes.
|
|
8
|
+
*/
|
|
9
|
+
import type { Job } from '../jobs/types.js';
|
|
10
|
+
import type { AggregateRow, CompareRunsResult, LongitudinalRow } from './types.js';
|
|
11
|
+
/**
|
|
12
|
+
* Read each ok result's `report.json` from disk and project to AggregateRow.
|
|
13
|
+
* Skipped/failed entries become rows with `rollupScore: NaN` so callers can
|
|
14
|
+
* filter them — keeping them in the list preserves index alignment with
|
|
15
|
+
* `job.results` for tools that want to drill in.
|
|
16
|
+
*/
|
|
17
|
+
export declare function aggregateJob(job: Job): AggregateRow[];
|
|
18
|
+
export interface LeaderboardOptions {
|
|
19
|
+
/** Filter by page type (e.g. only saas-app rows). */
|
|
20
|
+
byType?: string;
|
|
21
|
+
/** Top N entries. Default = no cap. */
|
|
22
|
+
topN?: number;
|
|
23
|
+
/** Sort direction. Default 'desc' (highest first). */
|
|
24
|
+
direction?: 'asc' | 'desc';
|
|
25
|
+
}
|
|
26
|
+
export declare function leaderboard(rows: AggregateRow[], opts?: LeaderboardOptions): AggregateRow[];
|
|
27
|
+
/** Longitudinal view: all snapshots for one URL, sorted by capture time. */
|
|
28
|
+
export declare function longitudinalFor(rows: AggregateRow[], url: string): LongitudinalRow[];
|
|
29
|
+
export declare function compareRuns(a: AggregateRow, b: AggregateRow): CompareRunsResult;
|
|
30
|
+
/** Group rows into tier buckets for "top 10 vs 100-200" style reports. */
|
|
31
|
+
export interface TierBucket {
|
|
32
|
+
label: string;
|
|
33
|
+
rows: AggregateRow[];
|
|
34
|
+
meanScore: number;
|
|
35
|
+
medianScore: number;
|
|
36
|
+
}
|
|
37
|
+
export declare function tierBuckets(rows: AggregateRow[], boundaries: number[]): TierBucket[];
|
|
38
|
+
//# sourceMappingURL=aggregate.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"aggregate.d.ts","sourceRoot":"","sources":["../../src/reports/aggregate.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,KAAK,EAAE,GAAG,EAAkB,MAAM,kBAAkB,CAAA;AAE3D,OAAO,KAAK,EAAE,YAAY,EAAE,iBAAiB,EAAkB,eAAe,EAAE,MAAM,YAAY,CAAA;AAkBlG;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,GAAG,GAAG,YAAY,EAAE,CAMrD;AAwCD,MAAM,WAAW,kBAAkB;IACjC,qDAAqD;IACrD,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,uCAAuC;IACvC,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,sDAAsD;IACtD,SAAS,CAAC,EAAE,KAAK,GAAG,MAAM,CAAA;CAC3B;AAED,wBAAgB,WAAW,CAAC,IAAI,EAAE,YAAY,EAAE,EAAE,IAAI,GAAE,kBAAuB,GAAG,YAAY,EAAE,CAQ/F;AAED,4EAA4E;AAC5E,wBAAgB,eAAe,CAAC,IAAI,EAAE,YAAY,EAAE,EAAE,GAAG,EAAE,MAAM,GAAG,eAAe,EAAE,CAUpF;AAED,wBAAgB,WAAW,CAAC,CAAC,EAAE,YAAY,EAAE,CAAC,EAAE,YAAY,GAAG,iBAAiB,CAkB/E;AAED,0EAA0E;AAC1E,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,EAAE,YAAY,EAAE,CAAA;IACpB,SAAS,EAAE,MAAM,CAAA;IACjB,WAAW,EAAE,MAAM,CAAA;CACpB;AAED,wBAAgB,WAAW,CAAC,IAAI,EAAE,YAAY,EAAE,EAAE,UAAU,EAAE,MAAM,EAAE,GAAG,UAAU,EAAE,CAoBpF"}
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deterministic aggregation over a job's per-target audit results.
|
|
3
|
+
*
|
|
4
|
+
* Every number that shows up in a report flows through here — never through
|
|
5
|
+
* an LLM. The narration layer can describe / contextualize / dramatize, but
|
|
6
|
+
* counts, scores, deltas, and rankings are all computed here. Same pattern
|
|
7
|
+
* we use for the audit patches contract: agent narrates, code computes.
|
|
8
|
+
*/
|
|
9
|
+
import * as fs from 'node:fs';
|
|
10
|
+
/**
|
|
11
|
+
* Read each ok result's `report.json` from disk and project to AggregateRow.
|
|
12
|
+
* Skipped/failed entries become rows with `rollupScore: NaN` so callers can
|
|
13
|
+
* filter them — keeping them in the list preserves index alignment with
|
|
14
|
+
* `job.results` for tools that want to drill in.
|
|
15
|
+
*/
|
|
16
|
+
export function aggregateJob(job) {
|
|
17
|
+
const out = [];
|
|
18
|
+
for (const r of job.results) {
|
|
19
|
+
out.push(toRow(r));
|
|
20
|
+
}
|
|
21
|
+
return out;
|
|
22
|
+
}
|
|
23
|
+
function toRow(r) {
|
|
24
|
+
const base = {
|
|
25
|
+
url: r.url,
|
|
26
|
+
snapshotUrl: r.snapshotUrl,
|
|
27
|
+
capturedAt: r.capturedAt,
|
|
28
|
+
runId: r.runId ?? '',
|
|
29
|
+
rollupScore: typeof r.rollupScore === 'number' ? r.rollupScore : NaN,
|
|
30
|
+
dimensions: {},
|
|
31
|
+
ethicsViolations: 0,
|
|
32
|
+
resultPath: r.resultPath,
|
|
33
|
+
pageType: r.pageType,
|
|
34
|
+
};
|
|
35
|
+
if (r.status !== 'ok' || !r.resultPath || !fs.existsSync(r.resultPath)) {
|
|
36
|
+
return base;
|
|
37
|
+
}
|
|
38
|
+
try {
|
|
39
|
+
const json = JSON.parse(fs.readFileSync(r.resultPath, 'utf-8'));
|
|
40
|
+
const page = json.pages?.[0];
|
|
41
|
+
if (!page)
|
|
42
|
+
return base;
|
|
43
|
+
const v2 = page.auditResultV2;
|
|
44
|
+
const cls = v2?.classification ?? page.classification ?? {};
|
|
45
|
+
base.pageType = base.pageType ?? cls.type;
|
|
46
|
+
base.domain = cls.domain;
|
|
47
|
+
if (v2?.rollup?.score !== undefined)
|
|
48
|
+
base.rollupScore = v2.rollup.score;
|
|
49
|
+
else if (page.rollup?.score !== undefined)
|
|
50
|
+
base.rollupScore = page.rollup.score;
|
|
51
|
+
else if (typeof page.score === 'number')
|
|
52
|
+
base.rollupScore = page.score;
|
|
53
|
+
if (v2?.scores) {
|
|
54
|
+
for (const [dim, ds] of Object.entries(v2.scores)) {
|
|
55
|
+
if (ds && typeof ds.score === 'number')
|
|
56
|
+
base.dimensions[dim] = ds.score;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
base.ethicsViolations = Array.isArray(page.ethicsViolations) ? page.ethicsViolations.length : 0;
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
// Corrupt or partial report.json — leave as-is. Don't pretend we have data we don't.
|
|
63
|
+
}
|
|
64
|
+
return base;
|
|
65
|
+
}
|
|
66
|
+
export function leaderboard(rows, opts = {}) {
|
|
67
|
+
const dir = opts.direction ?? 'desc';
|
|
68
|
+
const filtered = rows
|
|
69
|
+
.filter(r => Number.isFinite(r.rollupScore))
|
|
70
|
+
.filter(r => !opts.byType || r.pageType === opts.byType);
|
|
71
|
+
filtered.sort((a, b) => (dir === 'desc' ? b.rollupScore - a.rollupScore : a.rollupScore - b.rollupScore));
|
|
72
|
+
if (opts.topN && opts.topN > 0)
|
|
73
|
+
return filtered.slice(0, opts.topN);
|
|
74
|
+
return filtered;
|
|
75
|
+
}
|
|
76
|
+
/** Longitudinal view: all snapshots for one URL, sorted by capture time. */
|
|
77
|
+
export function longitudinalFor(rows, url) {
|
|
78
|
+
return rows
|
|
79
|
+
.filter(r => r.url === url && r.capturedAt && Number.isFinite(r.rollupScore))
|
|
80
|
+
.map(r => ({
|
|
81
|
+
url: r.url,
|
|
82
|
+
capturedAt: r.capturedAt,
|
|
83
|
+
rollupScore: r.rollupScore,
|
|
84
|
+
pageType: r.pageType,
|
|
85
|
+
}))
|
|
86
|
+
.sort((a, b) => a.capturedAt.localeCompare(b.capturedAt));
|
|
87
|
+
}
|
|
88
|
+
export function compareRuns(a, b) {
|
|
89
|
+
const dims = new Set([
|
|
90
|
+
...Object.keys(a.dimensions),
|
|
91
|
+
...Object.keys(b.dimensions),
|
|
92
|
+
]);
|
|
93
|
+
const perDimension = [];
|
|
94
|
+
for (const dim of dims) {
|
|
95
|
+
const ax = a.dimensions[dim];
|
|
96
|
+
const bx = b.dimensions[dim];
|
|
97
|
+
if (typeof ax !== 'number' || typeof bx !== 'number')
|
|
98
|
+
continue;
|
|
99
|
+
perDimension.push({ dim, beforeScore: bx, afterScore: ax, delta: round2(ax - bx) });
|
|
100
|
+
}
|
|
101
|
+
return {
|
|
102
|
+
a,
|
|
103
|
+
b,
|
|
104
|
+
rollupDelta: round2(a.rollupScore - b.rollupScore),
|
|
105
|
+
perDimension,
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
export function tierBuckets(rows, boundaries) {
|
|
109
|
+
const ranked = leaderboard(rows);
|
|
110
|
+
const buckets = [];
|
|
111
|
+
const sorted = [...new Set(boundaries)].sort((a, b) => a - b);
|
|
112
|
+
let prev = 0;
|
|
113
|
+
for (const upper of sorted) {
|
|
114
|
+
const slice = ranked.slice(prev, upper);
|
|
115
|
+
if (slice.length === 0) {
|
|
116
|
+
prev = upper;
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
const label = prev === 0 ? `top ${upper}` : `${prev + 1}–${upper}`;
|
|
120
|
+
buckets.push({ label, rows: slice, ...stats(slice) });
|
|
121
|
+
prev = upper;
|
|
122
|
+
}
|
|
123
|
+
if (prev < ranked.length) {
|
|
124
|
+
const slice = ranked.slice(prev);
|
|
125
|
+
buckets.push({ label: `${prev + 1}+`, rows: slice, ...stats(slice) });
|
|
126
|
+
}
|
|
127
|
+
return buckets;
|
|
128
|
+
}
|
|
129
|
+
function stats(rows) {
|
|
130
|
+
if (rows.length === 0)
|
|
131
|
+
return { meanScore: NaN, medianScore: NaN };
|
|
132
|
+
const scores = rows.map(r => r.rollupScore).filter(Number.isFinite);
|
|
133
|
+
const sum = scores.reduce((a, b) => a + b, 0);
|
|
134
|
+
const sorted = [...scores].sort((a, b) => a - b);
|
|
135
|
+
const mid = Math.floor(sorted.length / 2);
|
|
136
|
+
const median = sorted.length % 2 === 0 ? (sorted[mid - 1] + sorted[mid]) / 2 : sorted[mid];
|
|
137
|
+
return {
|
|
138
|
+
meanScore: round2(sum / scores.length),
|
|
139
|
+
medianScore: round2(median),
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
function round2(n) {
|
|
143
|
+
return Math.round(n * 100) / 100;
|
|
144
|
+
}
|
|
145
|
+
//# sourceMappingURL=aggregate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"aggregate.js","sourceRoot":"","sources":["../../src/reports/aggregate.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reports — turn a job's audit results into shareable artifacts.
|
|
3
|
+
*
|
|
4
|
+
* Two surfaces:
|
|
5
|
+
* 1. Static templates (deterministic markdown rendering — see templates.ts)
|
|
6
|
+
* 2. AI SDK tools (agentic chat over the same data — see tools.ts)
|
|
7
|
+
*
|
|
8
|
+
* Both flow through the same aggregate.ts functions, so numbers are consistent.
|
|
9
|
+
*/
|
|
10
|
+
export type { AggregateRow, CompareRunsResult, DimensionDelta, LongitudinalRow, ReportTemplate } from './types.js';
|
|
11
|
+
export { aggregateJob, leaderboard, longitudinalFor, compareRuns, tierBuckets } from './aggregate.js';
|
|
12
|
+
export type { LeaderboardOptions, TierBucket } from './aggregate.js';
|
|
13
|
+
export { renderLeaderboard, renderLongitudinal, renderBatchComparison, renderBrandEvolution, renderJobHeader, } from './templates.js';
|
|
14
|
+
export type { LeaderboardRenderOpts, LongitudinalRenderOpts, BatchComparisonRenderOpts, BrandEvolutionRenderOpts, } from './templates.js';
|
|
15
|
+
export { aggregateTokens, diffTokens, groupByUrl } from './tokens.js';
|
|
16
|
+
export type { TokenSummary, TokenDiff, TokenSeries } from './tokens.js';
|
|
17
|
+
export { buildReportTools } from './tools.js';
|
|
18
|
+
export type { ReportToolsContext, ReportToolSet } from './tools.js';
|
|
19
|
+
export { narrateReport } from './narrate.js';
|
|
20
|
+
export type { NarrateOptions } from './narrate.js';
|
|
21
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/reports/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,YAAY,EAAE,YAAY,EAAE,iBAAiB,EAAE,cAAc,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAClH,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,eAAe,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;AACrG,YAAY,EAAE,kBAAkB,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAA;AACpE,OAAO,EACL,iBAAiB,EACjB,kBAAkB,EAClB,qBAAqB,EACrB,oBAAoB,EACpB,eAAe,GAChB,MAAM,gBAAgB,CAAA;AACvB,YAAY,EACV,qBAAqB,EACrB,sBAAsB,EACtB,yBAAyB,EACzB,wBAAwB,GACzB,MAAM,gBAAgB,CAAA;AACvB,OAAO,EAAE,eAAe,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AACrE,YAAY,EAAE,YAAY,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AACvE,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA;AAC7C,YAAY,EAAE,kBAAkB,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AACnE,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAA;AAC5C,YAAY,EAAE,cAAc,EAAE,MAAM,cAAc,CAAA"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reports — turn a job's audit results into shareable artifacts.
|
|
3
|
+
*
|
|
4
|
+
* Two surfaces:
|
|
5
|
+
* 1. Static templates (deterministic markdown rendering — see templates.ts)
|
|
6
|
+
* 2. AI SDK tools (agentic chat over the same data — see tools.ts)
|
|
7
|
+
*
|
|
8
|
+
* Both flow through the same aggregate.ts functions, so numbers are consistent.
|
|
9
|
+
*/
|
|
10
|
+
export { aggregateJob, leaderboard, longitudinalFor, compareRuns, tierBuckets } from './aggregate.js';
|
|
11
|
+
export { renderLeaderboard, renderLongitudinal, renderBatchComparison, renderBrandEvolution, renderJobHeader, } from './templates.js';
|
|
12
|
+
export { aggregateTokens, diffTokens, groupByUrl } from './tokens.js';
|
|
13
|
+
export { buildReportTools } from './tools.js';
|
|
14
|
+
export { narrateReport } from './narrate.js';
|
|
15
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/reports/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,eAAe,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;AAErG,OAAO,EACL,iBAAiB,EACjB,kBAAkB,EAClB,qBAAqB,EACrB,oBAAoB,EACpB,eAAe,GAChB,MAAM,gBAAgB,CAAA;AAOvB,OAAO,EAAE,eAAe,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAErE,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA;AAE7C,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAA"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LLM narration around deterministic data.
|
|
3
|
+
*
|
|
4
|
+
* The contract: numbers come from the templates (which call the aggregate
|
|
5
|
+
* functions). The LLM only writes prose context — top-line takeaways, an
|
|
6
|
+
* angle on what's surprising. Same pattern as the patches contract.
|
|
7
|
+
*
|
|
8
|
+
* If a Brain isn't supplied, the deterministic markdown is returned as-is.
|
|
9
|
+
* That's the safe default — never silently fabricate prose.
|
|
10
|
+
*/
|
|
11
|
+
import type { Brain } from '../brain/index.js';
|
|
12
|
+
export interface NarrateOptions {
|
|
13
|
+
/** When supplied, prepends an LLM-written executive summary above the deterministic body. */
|
|
14
|
+
brain?: Brain;
|
|
15
|
+
/** Free-form context passed to the LLM (e.g. "this is the YC W25 cohort"). */
|
|
16
|
+
context?: string;
|
|
17
|
+
}
|
|
18
|
+
export declare function narrateReport(deterministicMarkdown: string, opts?: NarrateOptions): Promise<string>;
|
|
19
|
+
//# sourceMappingURL=narrate.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"narrate.d.ts","sourceRoot":"","sources":["../../src/reports/narrate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAA;AAU9C,MAAM,WAAW,cAAc;IAC7B,6FAA6F;IAC7F,KAAK,CAAC,EAAE,KAAK,CAAA;IACb,8EAA8E;IAC9E,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB;AAED,wBAAsB,aAAa,CAAC,qBAAqB,EAAE,MAAM,EAAE,IAAI,GAAE,cAAmB,GAAG,OAAO,CAAC,MAAM,CAAC,CAkB7G"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LLM narration around deterministic data.
|
|
3
|
+
*
|
|
4
|
+
* The contract: numbers come from the templates (which call the aggregate
|
|
5
|
+
* functions). The LLM only writes prose context — top-line takeaways, an
|
|
6
|
+
* angle on what's surprising. Same pattern as the patches contract.
|
|
7
|
+
*
|
|
8
|
+
* If a Brain isn't supplied, the deterministic markdown is returned as-is.
|
|
9
|
+
* That's the safe default — never silently fabricate prose.
|
|
10
|
+
*/
|
|
11
|
+
const SYSTEM = `You are an analyst writing a one-paragraph executive summary at the top of a design-audit report.
|
|
12
|
+
|
|
13
|
+
Rules:
|
|
14
|
+
- Use ONLY the numbers and facts in the provided markdown report. Do not invent rankings, scores, or claims.
|
|
15
|
+
- Surface the most surprising or load-bearing finding (e.g. a tier-vs-tier gap, a dramatic longitudinal swing, an outlier).
|
|
16
|
+
- Two to four sentences. No bullet lists.
|
|
17
|
+
- Do not preface with "Here is your summary" or similar.`;
|
|
18
|
+
export async function narrateReport(deterministicMarkdown, opts = {}) {
|
|
19
|
+
if (!opts.brain)
|
|
20
|
+
return deterministicMarkdown;
|
|
21
|
+
const user = [
|
|
22
|
+
opts.context ? `Context: ${opts.context}` : undefined,
|
|
23
|
+
'REPORT:',
|
|
24
|
+
deterministicMarkdown,
|
|
25
|
+
'',
|
|
26
|
+
'Write the executive summary now.',
|
|
27
|
+
].filter(Boolean).join('\n\n');
|
|
28
|
+
try {
|
|
29
|
+
const { text } = await opts.brain.complete(SYSTEM, user, { maxOutputTokens: 320 });
|
|
30
|
+
const summary = text.trim();
|
|
31
|
+
if (!summary)
|
|
32
|
+
return deterministicMarkdown;
|
|
33
|
+
return `## Executive summary\n\n${summary}\n\n${deterministicMarkdown}`;
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
// Don't let narration failures block the artifact. Ship the data.
|
|
37
|
+
return deterministicMarkdown;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=narrate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"narrate.js","sourceRoot":"","sources":["../../src/reports/narrate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAIH,MAAM,MAAM,GAAG;;;;;;yDAM0C,CAAA;AASzD,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,qBAA6B,EAAE,OAAuB,EAAE;IAC1F,IAAI,CAAC,IAAI,CAAC,KAAK;QAAE,OAAO,qBAAqB,CAAA;IAC7C,MAAM,IAAI,GAAG;QACX,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,YAAY,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,SAAS;QACrD,SAAS;QACT,qBAAqB;QACrB,EAAE;QACF,kCAAkC;KACnC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;IAC9B,IAAI,CAAC;QACH,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,MAAM,EAAE,IAAI,EAAE,EAAE,eAAe,EAAE,GAAG,EAAE,CAAC,CAAA;QAClF,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,CAAA;QAC3B,IAAI,CAAC,OAAO;YAAE,OAAO,qBAAqB,CAAA;QAC1C,OAAO,2BAA2B,OAAO,OAAO,qBAAqB,EAAE,CAAA;IACzE,CAAC;IAAC,MAAM,CAAC;QACP,kEAAkE;QAClE,OAAO,qBAAqB,CAAA;IAC9B,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Static report templates — markdown rendering of pre-aggregated data.
|
|
3
|
+
*
|
|
4
|
+
* No LLM. The narration layer (narrate.ts) wraps these with prose; the
|
|
5
|
+
* templates themselves are pure functions of data so they're snapshot-testable
|
|
6
|
+
* and deterministic.
|
|
7
|
+
*/
|
|
8
|
+
import type { Job } from '../jobs/types.js';
|
|
9
|
+
import type { AggregateRow } from './types.js';
|
|
10
|
+
export interface LeaderboardRenderOpts {
|
|
11
|
+
title?: string;
|
|
12
|
+
topN?: number;
|
|
13
|
+
byType?: string;
|
|
14
|
+
/** Tier bucket boundaries — e.g. [10, 100, 200] → "top 10", "11–100", "101–200", "201+". */
|
|
15
|
+
buckets?: number[];
|
|
16
|
+
}
|
|
17
|
+
export declare function renderLeaderboard(rows: AggregateRow[], opts?: LeaderboardRenderOpts): string;
|
|
18
|
+
export interface LongitudinalRenderOpts {
|
|
19
|
+
title?: string;
|
|
20
|
+
/** If multiple URLs are in scope, render one section per URL. */
|
|
21
|
+
urls?: string[];
|
|
22
|
+
}
|
|
23
|
+
export declare function renderLongitudinal(rows: AggregateRow[], opts?: LongitudinalRenderOpts): string;
|
|
24
|
+
export interface BatchComparisonRenderOpts {
|
|
25
|
+
title?: string;
|
|
26
|
+
/** Pairs of (a, b) URLs to diff. If omitted, the first two URLs encountered are paired. */
|
|
27
|
+
pairs?: Array<[string, string]>;
|
|
28
|
+
}
|
|
29
|
+
export declare function renderBatchComparison(rows: AggregateRow[], opts?: BatchComparisonRenderOpts): string;
|
|
30
|
+
export interface BrandEvolutionRenderOpts {
|
|
31
|
+
title?: string;
|
|
32
|
+
/** Max colors to render per snapshot (top by usage count). Default 6. */
|
|
33
|
+
topColors?: number;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Render a per-URL brand-kit evolution: for each URL, list the snapshots in
|
|
37
|
+
* chronological order with their distinct color palette, font families, and
|
|
38
|
+
* detected libraries. Between consecutive snapshots, surface the delta
|
|
39
|
+
* (colors added/removed, families added/removed, brand-meta changes).
|
|
40
|
+
*/
|
|
41
|
+
export declare function renderBrandEvolution(job: Job, opts?: BrandEvolutionRenderOpts): string;
|
|
42
|
+
export declare function renderJobHeader(job: Job): string;
|
|
43
|
+
//# sourceMappingURL=templates.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"templates.d.ts","sourceRoot":"","sources":["../../src/reports/templates.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,kBAAkB,CAAA;AAC3C,OAAO,KAAK,EAAE,YAAY,EAAmB,MAAM,YAAY,CAAA;AAI/D,MAAM,WAAW,qBAAqB;IACpC,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,4FAA4F;IAC5F,OAAO,CAAC,EAAE,MAAM,EAAE,CAAA;CACnB;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,YAAY,EAAE,EAAE,IAAI,GAAE,qBAA0B,GAAG,MAAM,CAwChG;AAED,MAAM,WAAW,sBAAsB;IACrC,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,iEAAiE;IACjE,IAAI,CAAC,EAAE,MAAM,EAAE,CAAA;CAChB;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,YAAY,EAAE,EAAE,IAAI,GAAE,sBAA2B,GAAG,MAAM,CA6BlG;AAED,MAAM,WAAW,yBAAyB;IACxC,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,2FAA2F;IAC3F,KAAK,CAAC,EAAE,KAAK,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAA;CAChC;AAED,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,YAAY,EAAE,EAAE,IAAI,GAAE,yBAA8B,GAAG,MAAM,CA+BxG;AAED,MAAM,WAAW,wBAAwB;IACvC,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,yEAAyE;IACzE,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,GAAE,wBAA6B,GAAG,MAAM,CAqD1F;AAED,wBAAgB,eAAe,CAAC,GAAG,EAAE,GAAG,GAAG,MAAM,CAWhD"}
|