@tangle-network/browser-agent-driver 0.12.1 → 0.14.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 +81 -0
- package/dist/brain/index.d.ts.map +1 -1
- package/dist/brain/index.js +88 -6
- package/dist/brain/index.js.map +1 -1
- package/dist/cli-design-audit.d.ts +2 -0
- package/dist/cli-design-audit.d.ts.map +1 -1
- package/dist/cli-design-audit.js +111 -303
- package/dist/cli-design-audit.js.map +1 -1
- package/dist/cli-view.d.ts +83 -0
- package/dist/cli-view.d.ts.map +1 -0
- package/dist/cli-view.js +397 -0
- package/dist/cli-view.js.map +1 -0
- package/dist/cli.js +32 -1
- package/dist/cli.js.map +1 -1
- package/dist/design/audit/classify.d.ts +20 -0
- package/dist/design/audit/classify.d.ts.map +1 -0
- package/dist/design/audit/classify.js +148 -0
- package/dist/design/audit/classify.js.map +1 -0
- package/dist/design/audit/evaluate.d.ts +49 -0
- package/dist/design/audit/evaluate.d.ts.map +1 -0
- package/dist/design/audit/evaluate.js +355 -0
- package/dist/design/audit/evaluate.js.map +1 -0
- package/dist/design/audit/measure/a11y.d.ts +25 -0
- package/dist/design/audit/measure/a11y.d.ts.map +1 -0
- package/dist/design/audit/measure/a11y.js +144 -0
- package/dist/design/audit/measure/a11y.js.map +1 -0
- package/dist/design/audit/measure/contrast.d.ts +18 -0
- package/dist/design/audit/measure/contrast.d.ts.map +1 -0
- package/dist/design/audit/measure/contrast.js +201 -0
- package/dist/design/audit/measure/contrast.js.map +1 -0
- package/dist/design/audit/measure/index.d.ts +14 -0
- package/dist/design/audit/measure/index.d.ts.map +1 -0
- package/dist/design/audit/measure/index.js +38 -0
- package/dist/design/audit/measure/index.js.map +1 -0
- package/dist/design/audit/pipeline.d.ts +33 -0
- package/dist/design/audit/pipeline.d.ts.map +1 -0
- package/dist/design/audit/pipeline.js +109 -0
- package/dist/design/audit/pipeline.js.map +1 -0
- package/dist/design/audit/roi.d.ts +49 -0
- package/dist/design/audit/roi.d.ts.map +1 -0
- package/dist/design/audit/roi.js +157 -0
- package/dist/design/audit/roi.js.map +1 -0
- package/dist/design/audit/rubric/fragments/domain-ai.md +19 -0
- package/dist/design/audit/rubric/fragments/domain-crypto.md +24 -0
- package/dist/design/audit/rubric/fragments/domain-devtools.md +21 -0
- package/dist/design/audit/rubric/fragments/domain-fintech.md +19 -0
- package/dist/design/audit/rubric/fragments/maturity-prototype.md +36 -0
- package/dist/design/audit/rubric/fragments/type-blog.md +22 -0
- package/dist/design/audit/rubric/fragments/type-docs.md +23 -0
- package/dist/design/audit/rubric/fragments/type-ecommerce.md +23 -0
- package/dist/design/audit/rubric/fragments/type-marketing.md +22 -0
- package/dist/design/audit/rubric/fragments/type-saas-app.md +20 -0
- package/dist/design/audit/rubric/fragments/universal-calibration.md +16 -0
- package/dist/design/audit/rubric/fragments/universal-effort-anchor.md +46 -0
- package/dist/design/audit/rubric/fragments/universal-foundation.md +49 -0
- package/dist/design/audit/rubric/loader.d.ts +49 -0
- package/dist/design/audit/rubric/loader.d.ts.map +1 -0
- package/dist/design/audit/rubric/loader.js +216 -0
- package/dist/design/audit/rubric/loader.js.map +1 -0
- package/dist/design/audit/types.d.ts +166 -0
- package/dist/design/audit/types.d.ts.map +1 -0
- package/dist/design/audit/types.js +8 -0
- package/dist/design/audit/types.js.map +1 -0
- package/dist/drivers/cursor-overlay.d.ts +26 -0
- package/dist/drivers/cursor-overlay.d.ts.map +1 -0
- package/dist/drivers/cursor-overlay.js +235 -0
- package/dist/drivers/cursor-overlay.js.map +1 -0
- package/dist/drivers/playwright.d.ts +32 -0
- package/dist/drivers/playwright.d.ts.map +1 -1
- package/dist/drivers/playwright.js +79 -0
- package/dist/drivers/playwright.js.map +1 -1
- package/dist/drivers/steel.d.ts +135 -0
- package/dist/drivers/steel.d.ts.map +1 -0
- package/dist/drivers/steel.js +236 -0
- package/dist/drivers/steel.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/provider-defaults.d.ts +19 -1
- package/dist/provider-defaults.d.ts.map +1 -1
- package/dist/provider-defaults.js +34 -0
- package/dist/provider-defaults.js.map +1 -1
- package/dist/test-runner.d.ts.map +1 -1
- package/dist/test-runner.js +51 -14
- package/dist/test-runner.js.map +1 -1
- package/dist/types.d.ts +10 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/viewer/viewer.html +940 -0
- package/package.json +3 -2
package/dist/cli-view.js
ADDED
|
@@ -0,0 +1,397 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLI handler for `bad view <run-dir>`.
|
|
3
|
+
*
|
|
4
|
+
* Spins up a tiny static HTTP server in front of the run's artifact
|
|
5
|
+
* directory and opens the viewer in the user's default browser.
|
|
6
|
+
*
|
|
7
|
+
* No build pipeline, no React, no separate package — viewer.html is a
|
|
8
|
+
* single self-contained file shipped in the package and copied to dist
|
|
9
|
+
* at build time by `scripts/copy-static-assets.mjs`.
|
|
10
|
+
*
|
|
11
|
+
* Library-friendly: this module throws typed errors instead of calling
|
|
12
|
+
* `process.exit`. The CLI dispatcher catches and exits.
|
|
13
|
+
*/
|
|
14
|
+
import * as fs from 'node:fs';
|
|
15
|
+
import * as path from 'node:path';
|
|
16
|
+
import * as http from 'node:http';
|
|
17
|
+
import { fileURLToPath } from 'node:url';
|
|
18
|
+
import { spawn } from 'node:child_process';
|
|
19
|
+
import chalk from 'chalk';
|
|
20
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
21
|
+
/** Resolved at module load — single canonical path, no runtime probing. */
|
|
22
|
+
const VIEWER_HTML_PATH = (() => {
|
|
23
|
+
// Production: dist/cli-view.js + dist/viewer/viewer.html
|
|
24
|
+
const distPath = path.join(__dirname, 'viewer/viewer.html');
|
|
25
|
+
if (fs.existsSync(distPath))
|
|
26
|
+
return distPath;
|
|
27
|
+
// Source dev mode: src/cli-view.ts + src/viewer/viewer.html
|
|
28
|
+
const srcPath = path.join(__dirname, '../viewer/viewer.html');
|
|
29
|
+
if (fs.existsSync(srcPath))
|
|
30
|
+
return srcPath;
|
|
31
|
+
// Last-resort: walking up one more level (for unusual layouts)
|
|
32
|
+
const upPath = path.join(__dirname, '../../src/viewer/viewer.html');
|
|
33
|
+
if (fs.existsSync(upPath))
|
|
34
|
+
return upPath;
|
|
35
|
+
return '';
|
|
36
|
+
})();
|
|
37
|
+
export class ViewError extends Error {
|
|
38
|
+
constructor(message) {
|
|
39
|
+
super(message);
|
|
40
|
+
this.name = 'ViewError';
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Resolves a `report.json` file inside a run directory. Searches the
|
|
45
|
+
* run directory itself first, then one level deep.
|
|
46
|
+
*/
|
|
47
|
+
export function findReportJson(runDir) {
|
|
48
|
+
const direct = path.join(runDir, 'report.json');
|
|
49
|
+
if (fs.existsSync(direct))
|
|
50
|
+
return direct;
|
|
51
|
+
try {
|
|
52
|
+
for (const entry of fs.readdirSync(runDir, { withFileTypes: true })) {
|
|
53
|
+
if (entry.isDirectory()) {
|
|
54
|
+
const sub = path.join(runDir, entry.name, 'report.json');
|
|
55
|
+
if (fs.existsSync(sub))
|
|
56
|
+
return sub;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
/* ignore unreadable dir */
|
|
62
|
+
}
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Find all recording.webm files anywhere under the report root (one level
|
|
67
|
+
* deep — agent runs put them in `<run>/<test-id>/recording.webm`). Returns
|
|
68
|
+
* an array of `{ testId, relPath }` so the viewer can show one player per
|
|
69
|
+
* test case in a multi-test suite run.
|
|
70
|
+
*
|
|
71
|
+
* Skips 0-byte files (Playwright sometimes writes the path before flushing
|
|
72
|
+
* the video on context close, leaving an empty placeholder) — the viewer
|
|
73
|
+
* will fall back to per-turn screenshots when no usable recording exists.
|
|
74
|
+
*/
|
|
75
|
+
export function findRecordings(reportRoot) {
|
|
76
|
+
const out = [];
|
|
77
|
+
const isUsableRecording = (filePath) => {
|
|
78
|
+
try {
|
|
79
|
+
const stats = fs.statSync(filePath);
|
|
80
|
+
return stats.isFile() && stats.size > 0;
|
|
81
|
+
}
|
|
82
|
+
catch {
|
|
83
|
+
return false;
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
try {
|
|
87
|
+
for (const entry of fs.readdirSync(reportRoot, { withFileTypes: true })) {
|
|
88
|
+
if (entry.isDirectory()) {
|
|
89
|
+
const recording = path.join(reportRoot, entry.name, 'recording.webm');
|
|
90
|
+
if (isUsableRecording(recording)) {
|
|
91
|
+
out.push({ testId: entry.name, relPath: `${entry.name}/recording.webm` });
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
else if (entry.name === 'recording.webm') {
|
|
95
|
+
const recording = path.join(reportRoot, entry.name);
|
|
96
|
+
if (isUsableRecording(recording)) {
|
|
97
|
+
out.push({ testId: 'default', relPath: 'recording.webm' });
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
catch {
|
|
103
|
+
/* ignore */
|
|
104
|
+
}
|
|
105
|
+
return out;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Normalize the report shape so the viewer sees a consistent format.
|
|
109
|
+
*
|
|
110
|
+
* Two distinct shapes flow through here:
|
|
111
|
+
* - **Design audit** (cli-design-audit.ts): top-level `pages[]`, each
|
|
112
|
+
* with screenshot file paths and findings. Native shape — passed through.
|
|
113
|
+
* - **Agent run** (test-runner.ts → FilesystemSink): top-level
|
|
114
|
+
* `results[].agentResult.turns[]` where each turn has `state.screenshot`
|
|
115
|
+
* as a raw base64 string. Unwrapped to a normalized `{ tests[]: { ...,
|
|
116
|
+
* turns[] } }` shape with screenshots converted to data URLs.
|
|
117
|
+
*
|
|
118
|
+
* Recordings discovered on disk are attached to the matching test by id.
|
|
119
|
+
*/
|
|
120
|
+
export function normalizeReport(raw, recordings) {
|
|
121
|
+
const data = raw;
|
|
122
|
+
if (!data || typeof data !== 'object')
|
|
123
|
+
return { error: 'invalid report' };
|
|
124
|
+
// Design audit shape — has top-level `pages[]`. Pass through.
|
|
125
|
+
if (Array.isArray(data.pages)) {
|
|
126
|
+
return data;
|
|
127
|
+
}
|
|
128
|
+
// Agent run shape — has top-level `results[]` with `.agentResult.turns[]`.
|
|
129
|
+
if (Array.isArray(data.results)) {
|
|
130
|
+
const results = data.results;
|
|
131
|
+
const tests = results.map(r => {
|
|
132
|
+
const agentResult = (r.agentResult ?? {});
|
|
133
|
+
const turns = (agentResult.turns ?? []).map(t => {
|
|
134
|
+
const state = (t.state ?? {});
|
|
135
|
+
const screenshot = state.screenshot;
|
|
136
|
+
// Convert raw base64 → data URL so the <img> tag renders it.
|
|
137
|
+
// NOTE: JPEG base64 starts with `/9j/` (first byte 0xFF), so the
|
|
138
|
+
// leading slash is NOT a path indicator. Detect format properly:
|
|
139
|
+
// - already a data: URL → leave it
|
|
140
|
+
// - absolute file path or http(s) URL → leave it (legacy on-disk runs)
|
|
141
|
+
// - everything else → assume base64, prefix as JPEG data URL
|
|
142
|
+
const screenshotDataUrl = !screenshot
|
|
143
|
+
? screenshot
|
|
144
|
+
: screenshot.startsWith('data:')
|
|
145
|
+
? screenshot
|
|
146
|
+
: screenshot.startsWith('http://') || screenshot.startsWith('https://')
|
|
147
|
+
? screenshot
|
|
148
|
+
: screenshot.startsWith('/') && (screenshot.endsWith('.png') || screenshot.endsWith('.jpg') || screenshot.endsWith('.jpeg') || screenshot.endsWith('.webp'))
|
|
149
|
+
? screenshot
|
|
150
|
+
: `data:image/jpeg;base64,${screenshot}`;
|
|
151
|
+
return {
|
|
152
|
+
...t,
|
|
153
|
+
state: { ...state, screenshot: screenshotDataUrl },
|
|
154
|
+
};
|
|
155
|
+
});
|
|
156
|
+
const testCase = (r.testCase ?? {});
|
|
157
|
+
const testId = (testCase.id ?? testCase.name ?? 'cli-task');
|
|
158
|
+
const recording = recordings.find(rec => rec.testId === testId)
|
|
159
|
+
?? recordings.find(rec => rec.testId === 'default');
|
|
160
|
+
return {
|
|
161
|
+
id: testId,
|
|
162
|
+
name: testCase.name ?? testId,
|
|
163
|
+
success: agentResult.success ?? r.agentSuccess,
|
|
164
|
+
verdict: r.verdict,
|
|
165
|
+
turnsUsed: r.turnsUsed,
|
|
166
|
+
durationMs: r.durationMs,
|
|
167
|
+
estimatedCostUsd: r.estimatedCostUsd,
|
|
168
|
+
recording: recording ? recording.relPath : null,
|
|
169
|
+
turns,
|
|
170
|
+
};
|
|
171
|
+
});
|
|
172
|
+
return {
|
|
173
|
+
...data,
|
|
174
|
+
// Both fields present so existing readers still work; the viewer
|
|
175
|
+
// prefers `tests[]` for agent runs.
|
|
176
|
+
tests,
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
// Single agent result shape (no suite wrapper) — `turns[]` directly.
|
|
180
|
+
if (Array.isArray(data.turns)) {
|
|
181
|
+
const turns = (data.turns).map(t => {
|
|
182
|
+
const state = (t.state ?? {});
|
|
183
|
+
const screenshot = state.screenshot;
|
|
184
|
+
const screenshotDataUrl = !screenshot
|
|
185
|
+
? screenshot
|
|
186
|
+
: screenshot.startsWith('data:')
|
|
187
|
+
? screenshot
|
|
188
|
+
: screenshot.startsWith('http://') || screenshot.startsWith('https://')
|
|
189
|
+
? screenshot
|
|
190
|
+
: screenshot.startsWith('/') && (screenshot.endsWith('.png') || screenshot.endsWith('.jpg') || screenshot.endsWith('.jpeg') || screenshot.endsWith('.webp'))
|
|
191
|
+
? screenshot
|
|
192
|
+
: `data:image/jpeg;base64,${screenshot}`;
|
|
193
|
+
return { ...t, state: { ...state, screenshot: screenshotDataUrl } };
|
|
194
|
+
});
|
|
195
|
+
const recording = recordings.find(rec => rec.testId === 'default') ?? recordings[0];
|
|
196
|
+
return {
|
|
197
|
+
...data,
|
|
198
|
+
tests: [{
|
|
199
|
+
id: 'default',
|
|
200
|
+
name: 'run',
|
|
201
|
+
recording: recording ? recording.relPath : null,
|
|
202
|
+
turns,
|
|
203
|
+
}],
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
return data;
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Escape a JSON string for safe inlining inside a `<script>` block.
|
|
210
|
+
*
|
|
211
|
+
* The hazards:
|
|
212
|
+
* - `</script` (any case): closes the script tag mid-string
|
|
213
|
+
* - `<!--`: opens an HTML comment, toggles parser modes
|
|
214
|
+
* - U+2028 / U+2029: legal in JSON, illegal in pre-ES2019 JS string literals
|
|
215
|
+
*
|
|
216
|
+
* Exported for testing.
|
|
217
|
+
*/
|
|
218
|
+
export function escapeJsonForScript(json) {
|
|
219
|
+
return json
|
|
220
|
+
.replace(/<\/(script)/gi, '<\\/$1')
|
|
221
|
+
.replace(/<!--/g, '<\\!--')
|
|
222
|
+
.replace(/\u2028/g, '\\u2028')
|
|
223
|
+
.replace(/\u2029/g, '\\u2029');
|
|
224
|
+
}
|
|
225
|
+
const MIME = {
|
|
226
|
+
'.html': 'text/html; charset=utf-8',
|
|
227
|
+
'.htm': 'text/html; charset=utf-8',
|
|
228
|
+
'.json': 'application/json; charset=utf-8',
|
|
229
|
+
'.txt': 'text/plain; charset=utf-8',
|
|
230
|
+
'.md': 'text/markdown; charset=utf-8',
|
|
231
|
+
'.png': 'image/png',
|
|
232
|
+
'.jpg': 'image/jpeg',
|
|
233
|
+
'.jpeg': 'image/jpeg',
|
|
234
|
+
'.webp': 'image/webp',
|
|
235
|
+
'.gif': 'image/gif',
|
|
236
|
+
'.svg': 'image/svg+xml',
|
|
237
|
+
'.ico': 'image/x-icon',
|
|
238
|
+
'.css': 'text/css',
|
|
239
|
+
'.js': 'application/javascript',
|
|
240
|
+
'.mjs': 'application/javascript',
|
|
241
|
+
'.map': 'application/json',
|
|
242
|
+
'.webm': 'video/webm',
|
|
243
|
+
'.mp4': 'video/mp4',
|
|
244
|
+
'.mov': 'video/quicktime',
|
|
245
|
+
};
|
|
246
|
+
function openInBrowser(url) {
|
|
247
|
+
const cmd = process.platform === 'darwin' ? 'open'
|
|
248
|
+
: process.platform === 'win32' ? 'start'
|
|
249
|
+
: 'xdg-open';
|
|
250
|
+
try {
|
|
251
|
+
spawn(cmd, [url], { detached: true, stdio: 'ignore' }).unref();
|
|
252
|
+
}
|
|
253
|
+
catch {
|
|
254
|
+
// Best-effort — user can copy the URL from the terminal
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
258
|
+
* Listen on a port, auto-incrementing on EADDRINUSE up to `maxRetries` times.
|
|
259
|
+
*/
|
|
260
|
+
async function listenWithRetry(server, startPort, maxRetries) {
|
|
261
|
+
for (let i = 0; i <= maxRetries; i++) {
|
|
262
|
+
const port = startPort + i;
|
|
263
|
+
try {
|
|
264
|
+
await new Promise((resolve, reject) => {
|
|
265
|
+
const onError = (err) => {
|
|
266
|
+
server.removeListener('listening', onListening);
|
|
267
|
+
reject(err);
|
|
268
|
+
};
|
|
269
|
+
const onListening = () => {
|
|
270
|
+
server.removeListener('error', onError);
|
|
271
|
+
resolve();
|
|
272
|
+
};
|
|
273
|
+
server.once('error', onError);
|
|
274
|
+
server.once('listening', onListening);
|
|
275
|
+
server.listen(port, '127.0.0.1');
|
|
276
|
+
});
|
|
277
|
+
return port;
|
|
278
|
+
}
|
|
279
|
+
catch (err) {
|
|
280
|
+
const code = err.code;
|
|
281
|
+
if (code !== 'EADDRINUSE')
|
|
282
|
+
throw err;
|
|
283
|
+
// Try the next port
|
|
284
|
+
continue;
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
throw new ViewError(`could not bind to any port in range ${startPort}–${startPort + maxRetries} (all in use)`);
|
|
288
|
+
}
|
|
289
|
+
export async function runView(opts) {
|
|
290
|
+
const runDir = path.resolve(opts.runDir);
|
|
291
|
+
if (!fs.existsSync(runDir) || !fs.statSync(runDir).isDirectory()) {
|
|
292
|
+
throw new ViewError(`run directory not found: ${runDir}`);
|
|
293
|
+
}
|
|
294
|
+
const reportPath = findReportJson(runDir);
|
|
295
|
+
if (!reportPath) {
|
|
296
|
+
throw new ViewError(`no report.json found in ${runDir} or its subdirectories`);
|
|
297
|
+
}
|
|
298
|
+
if (!VIEWER_HTML_PATH) {
|
|
299
|
+
throw new ViewError('viewer.html not found. The package may be missing dist/viewer/viewer.html — try reinstalling.');
|
|
300
|
+
}
|
|
301
|
+
const reportRoot = path.dirname(reportPath);
|
|
302
|
+
const viewerSource = fs.readFileSync(VIEWER_HTML_PATH, 'utf-8');
|
|
303
|
+
const reportRaw = fs.readFileSync(reportPath, 'utf-8');
|
|
304
|
+
// Parse the report, discover any recording.webm files alongside it, and
|
|
305
|
+
// normalize the shape so the viewer sees a consistent format whether
|
|
306
|
+
// this is a design-audit run or an agent suite run.
|
|
307
|
+
let reportObj;
|
|
308
|
+
try {
|
|
309
|
+
reportObj = JSON.parse(reportRaw);
|
|
310
|
+
}
|
|
311
|
+
catch {
|
|
312
|
+
throw new ViewError(`report.json is not valid JSON: ${reportPath}`);
|
|
313
|
+
}
|
|
314
|
+
const recordings = findRecordings(reportRoot);
|
|
315
|
+
const normalized = normalizeReport(reportObj, recordings);
|
|
316
|
+
const reportJson = JSON.stringify(normalized);
|
|
317
|
+
const safeJson = escapeJsonForScript(reportJson);
|
|
318
|
+
const inlinedHtml = viewerSource.replace('const runData = window.__bad_runData || null;', `const runData = ${safeJson};`);
|
|
319
|
+
const server = http.createServer((req, res) => {
|
|
320
|
+
const urlPath = (req.url ?? '/').split('?')[0];
|
|
321
|
+
if (urlPath === '/' || urlPath === '/index.html') {
|
|
322
|
+
res.writeHead(200, { 'Content-Type': MIME['.html'] });
|
|
323
|
+
res.end(inlinedHtml);
|
|
324
|
+
return;
|
|
325
|
+
}
|
|
326
|
+
// Path-traversal protection: resolve, check the path stays inside
|
|
327
|
+
// reportRoot via path.relative (startsWith on the prefix is buggy
|
|
328
|
+
// because /tmp/runA-evil shares a prefix with /tmp/runA), and
|
|
329
|
+
// resolve symlinks so they can't escape the sandbox.
|
|
330
|
+
const decoded = decodeURIComponent(urlPath);
|
|
331
|
+
const candidate = path.resolve(reportRoot, decoded.replace(/^\/+/, ''));
|
|
332
|
+
const rel = path.relative(reportRoot, candidate);
|
|
333
|
+
if (rel.startsWith('..') || path.isAbsolute(rel)) {
|
|
334
|
+
res.writeHead(403);
|
|
335
|
+
res.end('forbidden');
|
|
336
|
+
return;
|
|
337
|
+
}
|
|
338
|
+
let realPath;
|
|
339
|
+
try {
|
|
340
|
+
realPath = fs.realpathSync(candidate);
|
|
341
|
+
}
|
|
342
|
+
catch {
|
|
343
|
+
res.writeHead(404);
|
|
344
|
+
res.end('not found');
|
|
345
|
+
return;
|
|
346
|
+
}
|
|
347
|
+
const realRel = path.relative(reportRoot, realPath);
|
|
348
|
+
if (realRel.startsWith('..') || path.isAbsolute(realRel)) {
|
|
349
|
+
// Symlink escaped the sandbox
|
|
350
|
+
res.writeHead(403);
|
|
351
|
+
res.end('forbidden');
|
|
352
|
+
return;
|
|
353
|
+
}
|
|
354
|
+
if (!fs.statSync(realPath).isFile()) {
|
|
355
|
+
res.writeHead(404);
|
|
356
|
+
res.end('not found');
|
|
357
|
+
return;
|
|
358
|
+
}
|
|
359
|
+
const ext = path.extname(realPath).toLowerCase();
|
|
360
|
+
res.writeHead(200, {
|
|
361
|
+
'Content-Type': MIME[ext] ?? 'application/octet-stream',
|
|
362
|
+
'Cache-Control': 'no-cache',
|
|
363
|
+
});
|
|
364
|
+
fs.createReadStream(realPath).pipe(res);
|
|
365
|
+
});
|
|
366
|
+
const port = await listenWithRetry(server, opts.port ?? 7777, opts.portRetries ?? 10);
|
|
367
|
+
const url = `http://localhost:${port}`;
|
|
368
|
+
console.log('');
|
|
369
|
+
console.log(` ${chalk.bold('bad view')}`);
|
|
370
|
+
console.log(` ${chalk.dim('Run:')} ${reportRoot}`);
|
|
371
|
+
console.log(` ${chalk.dim('URL:')} ${chalk.cyan(url)}`);
|
|
372
|
+
console.log(` ${chalk.dim('Press Ctrl+C to stop')}`);
|
|
373
|
+
console.log('');
|
|
374
|
+
if (!opts.noOpen) {
|
|
375
|
+
openInBrowser(url);
|
|
376
|
+
}
|
|
377
|
+
const close = () => new Promise(resolve => {
|
|
378
|
+
server.close(() => resolve());
|
|
379
|
+
});
|
|
380
|
+
return { url, close };
|
|
381
|
+
}
|
|
382
|
+
/**
|
|
383
|
+
* CLI entrypoint that calls runView and keeps the process alive until SIGINT.
|
|
384
|
+
* Throws ViewError on failure — the dispatcher in cli.ts catches and exits.
|
|
385
|
+
*/
|
|
386
|
+
export async function runViewCli(opts) {
|
|
387
|
+
const { close } = await runView(opts);
|
|
388
|
+
// Keep alive until SIGINT
|
|
389
|
+
await new Promise(resolve => {
|
|
390
|
+
const onSigint = () => {
|
|
391
|
+
console.log('\n closing viewer…');
|
|
392
|
+
void close().then(resolve);
|
|
393
|
+
};
|
|
394
|
+
process.once('SIGINT', onSigint);
|
|
395
|
+
});
|
|
396
|
+
}
|
|
397
|
+
//# sourceMappingURL=cli-view.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli-view.js","sourceRoot":"","sources":["../src/cli-view.ts"],"names":[],"mappings":"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"}
|
package/dist/cli.js
CHANGED
|
@@ -108,6 +108,12 @@ async function main() {
|
|
|
108
108
|
'evolve-rounds': { type: 'string' },
|
|
109
109
|
'project-dir': { type: 'string' },
|
|
110
110
|
reproducibility: { type: 'boolean' },
|
|
111
|
+
'rubrics-dir': { type: 'string' },
|
|
112
|
+
// bad view
|
|
113
|
+
port: { type: 'string' },
|
|
114
|
+
'no-open': { type: 'boolean' },
|
|
115
|
+
// bad run --show-cursor (overlay)
|
|
116
|
+
'show-cursor': { type: 'boolean' },
|
|
111
117
|
// showcase
|
|
112
118
|
script: { type: 'string' },
|
|
113
119
|
capture: { type: 'string' },
|
|
@@ -178,6 +184,29 @@ async function main() {
|
|
|
178
184
|
process.exit(0);
|
|
179
185
|
}
|
|
180
186
|
const command = positionals[0];
|
|
187
|
+
if (command === 'view') {
|
|
188
|
+
const runDir = positionals[1];
|
|
189
|
+
if (!runDir) {
|
|
190
|
+
cliError('usage: bad view <run-directory>');
|
|
191
|
+
process.exit(1);
|
|
192
|
+
}
|
|
193
|
+
const { runViewCli, ViewError } = await import('./cli-view.js');
|
|
194
|
+
try {
|
|
195
|
+
await runViewCli({
|
|
196
|
+
runDir,
|
|
197
|
+
port: values.port ? parseInt(values.port) : undefined,
|
|
198
|
+
noOpen: values['no-open'],
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
catch (err) {
|
|
202
|
+
if (err instanceof ViewError) {
|
|
203
|
+
cliError(err.message);
|
|
204
|
+
process.exit(1);
|
|
205
|
+
}
|
|
206
|
+
throw err;
|
|
207
|
+
}
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
181
210
|
if (command === 'design-audit') {
|
|
182
211
|
if (!values.url) {
|
|
183
212
|
cliError('--url is required for design-audit.');
|
|
@@ -226,6 +255,7 @@ async function main() {
|
|
|
226
255
|
evolveRounds: values['evolve-rounds'] ? parseInt(values['evolve-rounds']) : undefined,
|
|
227
256
|
projectDir: values['project-dir'],
|
|
228
257
|
reproducibility: values.reproducibility,
|
|
258
|
+
rubricsDir: values['rubrics-dir'],
|
|
229
259
|
});
|
|
230
260
|
process.exit(0);
|
|
231
261
|
}
|
|
@@ -315,7 +345,7 @@ async function main() {
|
|
|
315
345
|
process.exit(1);
|
|
316
346
|
}
|
|
317
347
|
if (command !== 'run') {
|
|
318
|
-
cliError(`Unknown command: ${command}. Use "run", "runs", "design-audit", "showcase", or "auth".`);
|
|
348
|
+
cliError(`Unknown command: ${command}. Use "run", "runs", "design-audit", "view", "showcase", or "auth".`);
|
|
319
349
|
process.exit(1);
|
|
320
350
|
}
|
|
321
351
|
// Validate inputs
|
|
@@ -1146,6 +1176,7 @@ async function main() {
|
|
|
1146
1176
|
timeout: actionTimeout,
|
|
1147
1177
|
visionStrategy: config.visionStrategy,
|
|
1148
1178
|
screenshotInterval,
|
|
1179
|
+
showCursor: values['show-cursor'],
|
|
1149
1180
|
});
|
|
1150
1181
|
// Apply resource blocking if configured
|
|
1151
1182
|
const resourceBlockingStartedAt = Date.now();
|