personal-agent-benchmark 0.0.0-stage → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (81) hide show
  1. package/README.md +128 -2
  2. package/bin/pab.js +5 -0
  3. package/dist/src/browser.d.ts +29 -0
  4. package/dist/src/browser.js +37 -0
  5. package/dist/src/browser.js.map +1 -0
  6. package/dist/src/cli.d.ts +20 -0
  7. package/dist/src/cli.js +331 -0
  8. package/dist/src/cli.js.map +1 -0
  9. package/dist/src/core/archive.d.ts +49 -0
  10. package/dist/src/core/archive.js +213 -0
  11. package/dist/src/core/archive.js.map +1 -0
  12. package/dist/src/core/audit.d.ts +15 -0
  13. package/dist/src/core/audit.js +85 -0
  14. package/dist/src/core/audit.js.map +1 -0
  15. package/dist/src/core/bundle.d.ts +42 -0
  16. package/dist/src/core/bundle.js +80 -0
  17. package/dist/src/core/bundle.js.map +1 -0
  18. package/dist/src/core/lookup.d.ts +15 -0
  19. package/dist/src/core/lookup.js +82 -0
  20. package/dist/src/core/lookup.js.map +1 -0
  21. package/dist/src/core/normalize.d.ts +45 -0
  22. package/dist/src/core/normalize.js +143 -0
  23. package/dist/src/core/normalize.js.map +1 -0
  24. package/dist/src/core/rules.d.ts +38 -0
  25. package/dist/src/core/rules.js +187 -0
  26. package/dist/src/core/rules.js.map +1 -0
  27. package/dist/src/core/store.d.ts +26 -0
  28. package/dist/src/core/store.js +78 -0
  29. package/dist/src/core/store.js.map +1 -0
  30. package/dist/src/core/types.d.ts +125 -0
  31. package/dist/src/core/types.js +3 -0
  32. package/dist/src/core/types.js.map +1 -0
  33. package/dist/src/core/util.d.ts +21 -0
  34. package/dist/src/core/util.js +150 -0
  35. package/dist/src/core/util.js.map +1 -0
  36. package/dist/src/core/warc.d.ts +55 -0
  37. package/dist/src/core/warc.js +258 -0
  38. package/dist/src/core/warc.js.map +1 -0
  39. package/dist/src/core/writer.d.ts +26 -0
  40. package/dist/src/core/writer.js +119 -0
  41. package/dist/src/core/writer.js.map +1 -0
  42. package/dist/src/grading.d.ts +63 -0
  43. package/dist/src/grading.js +173 -0
  44. package/dist/src/grading.js.map +1 -0
  45. package/dist/src/judge.d.ts +25 -0
  46. package/dist/src/judge.js +119 -0
  47. package/dist/src/judge.js.map +1 -0
  48. package/dist/src/paths.d.ts +2 -0
  49. package/dist/src/paths.js +40 -0
  50. package/dist/src/paths.js.map +1 -0
  51. package/dist/src/replay/clock.d.ts +19 -0
  52. package/dist/src/replay/clock.js +60 -0
  53. package/dist/src/replay/clock.js.map +1 -0
  54. package/dist/src/replay/engine.d.ts +53 -0
  55. package/dist/src/replay/engine.js +280 -0
  56. package/dist/src/replay/engine.js.map +1 -0
  57. package/dist/src/replay/launcher.d.ts +36 -0
  58. package/dist/src/replay/launcher.js +163 -0
  59. package/dist/src/replay/launcher.js.map +1 -0
  60. package/dist/src/replay/whistle-plugin.d.ts +24 -0
  61. package/dist/src/replay/whistle-plugin.js +82 -0
  62. package/dist/src/replay/whistle-plugin.js.map +1 -0
  63. package/dist/src/report.d.ts +3 -0
  64. package/dist/src/report.js +143 -0
  65. package/dist/src/report.js.map +1 -0
  66. package/dist/src/scoring.d.ts +87 -0
  67. package/dist/src/scoring.js +232 -0
  68. package/dist/src/scoring.js.map +1 -0
  69. package/dist/src/tasks.d.ts +61 -0
  70. package/dist/src/tasks.js +152 -0
  71. package/dist/src/tasks.js.map +1 -0
  72. package/docs/bundle-format.md +114 -0
  73. package/docs/deploy.md +78 -0
  74. package/docs/grading.md +81 -0
  75. package/docs/site-rules.md +114 -0
  76. package/docs/tasks.md +63 -0
  77. package/package.json +121 -4
  78. package/plugins/whistle.bench-replay/index.js +1 -0
  79. package/plugins/whistle.bench-replay/package.json +7 -0
  80. package/sites/_default.yaml +80 -0
  81. package/sites/amazon.com.yaml +125 -0
package/README.md CHANGED
@@ -1,3 +1,129 @@
1
- # Temporary Holding Version
1
+ # Personal Agent Benchmark
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ A benchmark for personal agents on real websites: shopping, posting, booking and more. Each task
4
+ comes with a network-level recording of the sites it needs. During a run the agent's browser goes
5
+ through a local replay proxy, so the agent sees the real URLs and real pages, while no request
6
+ leaves the machine. Runs are repeatable, cannot trip a site's bot defenses, and can be repeated as
7
+ often as needed.
8
+
9
+ ## How it works
10
+
11
+ ```text
12
+ agent ─▶ browser ─▶ replay proxy (whistle + replay plugin) ─▶ recording of the task
13
+ │
14
+ └─ nothing is forwarded; misses are answered locally
15
+ ```
16
+
17
+ - **Recording**: a person browses the task in a dedicated Chrome profile. Every HTTP exchange is
18
+ stored in a WARC file, with cookies and credentials removed.
19
+ - **Matching**: each request is reduced to a key (method, host, path, the query parameters that
20
+ matter, the request body fields that matter). Site rules say which parameters matter, which
21
+ paths are the same page, which requests are searches, and which are trackers.
22
+ - **Carts**: there is no cart arithmetic. Adding to the cart works for what was added while
23
+ recording; anything else misses. Cart and checkout pages answer with their last recording,
24
+ which is where the task left them.
25
+ - **Clock**: pages see the time of the recording, so "deal ends in 2 hours" behaves as it did.
26
+
27
+ ## Repository layout
28
+
29
+ ```text
30
+ tasks/<id>/ one task: task.yaml, recording/, rules/, grading.yaml
31
+ sites/ site rules shared by all tasks (_default.yaml applies everywhere)
32
+ src/ replay engine, whistle plugin, bundle format, CLI (pab)
33
+ plugins/ whistle plugin package that loads the replay engine
34
+ deploy/ replay and task images (built locally), a local Docker check
35
+ docs/ task format, bundle format, site rules, deployment
36
+ ```
37
+
38
+ ## Quick start
39
+
40
+ Needs Node 22+ and Google Chrome.
41
+
42
+ ```bash
43
+ npm install
44
+ npm run build
45
+ node bin/pab.js list
46
+ node bin/pab.js replay <task>
47
+ ```
48
+
49
+ `replay` unpacks the task's recording into `.cache/`, starts the replay proxy and opens a Chrome
50
+ window that uses it, at the first page opened while recording. Close the window to stop; hit rates are printed at the
51
+ end.
52
+
53
+ Connecting an agent:
54
+
55
+ ```bash
56
+ node bin/pab.js replay <task> --cdp-port 9333 # the agent drives this Chrome over CDP
57
+ node bin/pab.js replay <task> --no-browser # proxy only; bring your own browser
58
+ node bin/pab.js serve <task> --port 8899 --host 0.0.0.0 # in a container
59
+ ```
60
+
61
+ A browser of your own needs `--proxy-server=http://127.0.0.1:<port> --ignore-certificate-errors
62
+ --disable-quic`. The proxy also answers `http://rr.control/{health,stats,reset}`: `stats` counts
63
+ hits and misses, `reset` starts a new run.
64
+
65
+ Scoring a run: give the request log of the replay and the agent's final answer.
66
+
67
+ ```bash
68
+ node bin/pab.js grade <task> --log .cache/logs/replay-<task>-<time>.jsonl --answer answer.md
69
+ ```
70
+
71
+ It writes `result.json` and `report.html` (rendered from it) to `.cache/runs/<task>-<time>/`
72
+ (`--out` to choose): the score, each graded item with its reason, and the pages the agent opened
73
+ that came back 404 (see [docs/grading.md](docs/grading.md#run-report)).
74
+
75
+ Other commands:
76
+
77
+ ```bash
78
+ node bin/pab.js inspect <task> "https://www.amazon.com/s?k=airpods+pro" # key and match of one request
79
+ node bin/pab.js audit # check recordings are safe to publish
80
+ node bin/pab.js stats .cache/logs/replay-*.jsonl
81
+ ```
82
+
83
+ ## As a package
84
+
85
+ The replay engine, the recording format and the `pab` command are published to npm as
86
+ `personal-agent-benchmark`; tasks are not part of the package.
87
+
88
+ ```bash
89
+ npm install personal-agent-benchmark@0.2.0
90
+ npx pab replay path/to/tasks/<id>
91
+ ```
92
+
93
+ Modules are imported by path, for example `personal-agent-benchmark/scoring` or
94
+ `personal-agent-benchmark/core/rules`; `exports` in `package.json` lists them. While the version
95
+ is 0.x, a new minor version may change them. 0.1.x and 0.2.x read and write recordings in format 2
96
+ ([docs/bundle-format.md](docs/bundle-format.md)); a new format comes with a new minor version.
97
+
98
+ ## Tasks
99
+
100
+ No task is published yet; `node bin/pab.js list` shows the tasks of a checkout.
101
+
102
+ See [docs/tasks.md](docs/tasks.md) for the task format and how tasks are added.
103
+
104
+ ## Isolation
105
+
106
+ - The proxy is a dedicated whistle instance with one rule that hands every request, HTTPS
107
+ included, to the replay plugin. The plugin only reads the recording.
108
+ - Its DNS points at a dead address and proxy variables are removed, so even a request that slipped
109
+ past the plugin could not resolve a host.
110
+ - The platform that runs the replay keeps it off the network and the agent's browser to the
111
+ proxy; [docs/deploy.md](docs/deploy.md) lists what it must ensure.
112
+
113
+ ## Recordings
114
+
115
+ Recordings contain pages of third-party websites as they were served on the recording date, with
116
+ cookies, credentials and personal data removed (`pab audit` checks this). They are published here
117
+ for evaluating agents. To have content removed, open an issue.
118
+
119
+ ## Documentation
120
+
121
+ - [docs/tasks.md](docs/tasks.md): task directory, task card, how a task is added
122
+ - [docs/bundle-format.md](docs/bundle-format.md): what is inside `recording/`, and its format version
123
+ - [docs/site-rules.md](docs/site-rules.md): request matching and site rules
124
+ - [docs/grading.md](docs/grading.md): how answers to a task are scored
125
+ - [docs/deploy.md](docs/deploy.md): replay and task images, what a platform running replays must ensure
126
+
127
+ ## License
128
+
129
+ Not decided yet.
package/bin/pab.js ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env node
2
+ require('../dist/src/cli.js').main(process.argv.slice(2)).catch((err) => {
3
+ console.error(err && err.stack ? err.stack : err);
4
+ process.exit(1);
5
+ });
@@ -0,0 +1,29 @@
1
+ import { type BrowserContext } from 'playwright-core';
2
+ export interface BrowserOptions {
3
+ profileDir: string;
4
+ headless?: boolean;
5
+ /** Playwright channel; defaults to the locally installed Google Chrome. */
6
+ channel?: string;
7
+ executablePath?: string;
8
+ proxy?: {
9
+ server: string;
10
+ bypass?: string;
11
+ };
12
+ ignoreHTTPSErrors?: boolean;
13
+ locale?: string;
14
+ timezoneId?: string;
15
+ recordHarPath?: string;
16
+ /** Record a video of every page (tab) into this directory; saved when the page closes. */
17
+ recordVideo?: {
18
+ dir: string;
19
+ size?: {
20
+ width: number;
21
+ height: number;
22
+ };
23
+ };
24
+ /** Expose CDP on this port so an external agent can drive the same browser. */
25
+ cdpPort?: number;
26
+ args?: string[];
27
+ }
28
+ /** A dedicated, persistent Chrome profile; service workers blocked so every request hits the network. */
29
+ export declare function launchBrowser(o: BrowserOptions): Promise<BrowserContext>;
@@ -0,0 +1,37 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.launchBrowser = launchBrowser;
4
+ const playwright_core_1 = require("playwright-core");
5
+ /** A dedicated, persistent Chrome profile; service workers blocked so every request hits the network. */
6
+ async function launchBrowser(o) {
7
+ const args = [
8
+ '--disable-quic',
9
+ '--no-first-run',
10
+ '--no-default-browser-check',
11
+ // keep Chrome's own background traffic (sync, autofill, updates) out of recordings and replays
12
+ '--disable-background-networking',
13
+ '--disable-component-update',
14
+ '--disable-sync',
15
+ '--disable-domain-reliability',
16
+ '--disable-client-side-phishing-detection',
17
+ '--disable-features=AutofillServerCommunication,OptimizationHints,MediaRouter,Translate,InterestFeedContentSuggestions',
18
+ ...(o.args ?? []),
19
+ ];
20
+ if (o.cdpPort)
21
+ args.push(`--remote-debugging-port=${o.cdpPort}`);
22
+ return playwright_core_1.chromium.launchPersistentContext(o.profileDir, {
23
+ channel: o.executablePath ? undefined : o.channel ?? 'chrome',
24
+ executablePath: o.executablePath,
25
+ headless: o.headless ?? false,
26
+ proxy: o.proxy,
27
+ ignoreHTTPSErrors: o.ignoreHTTPSErrors,
28
+ locale: o.locale,
29
+ timezoneId: o.timezoneId,
30
+ viewport: { width: 1440, height: 900 },
31
+ serviceWorkers: 'block',
32
+ recordHar: o.recordHarPath ? { path: o.recordHarPath, content: 'embed', mode: 'full' } : undefined,
33
+ recordVideo: o.recordVideo,
34
+ args,
35
+ });
36
+ }
37
+ //# sourceMappingURL=browser.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"browser.js","sourceRoot":"","sources":["../../src/browser.ts"],"names":[],"mappings":";;;AAAA,qDAAgE;AAoBhE,yGAAyG;AAClG,KAAK,wBAAwB,CAAiB;IACnD,MAAM,IAAI,GAAG;QACX,gBAAgB;QAChB,gBAAgB;QAChB,4BAA4B;QAC5B,+FAA+F;QAC/F,iCAAiC;QACjC,4BAA4B;QAC5B,gBAAgB;QAChB,8BAA8B;QAC9B,0CAA0C;QAC1C,uHAAuH;QACvH,GAAG,CAAC,CAAC,CAAC,IAAI,IAAI,EAAE,CAAC;KAClB,CAAC;IACF,IAAI,CAAC,CAAC,OAAO;QAAE,IAAI,CAAC,IAAI,CAAC,2BAA2B,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC;IACjE,OAAO,0BAAQ,CAAC,uBAAuB,CAAC,CAAC,CAAC,UAAU,EAAE;QACpD,OAAO,EAAE,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,IAAI,QAAQ;QAC7D,cAAc,EAAE,CAAC,CAAC,cAAc;QAChC,QAAQ,EAAE,CAAC,CAAC,QAAQ,IAAI,KAAK;QAC7B,KAAK,EAAE,CAAC,CAAC,KAAK;QACd,iBAAiB,EAAE,CAAC,CAAC,iBAAiB;QACtC,MAAM,EAAE,CAAC,CAAC,MAAM;QAChB,UAAU,EAAE,CAAC,CAAC,UAAU;QACxB,QAAQ,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE;QACtC,cAAc,EAAE,OAAO;QACvB,SAAS,EAAE,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,aAAa,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,SAAS;QAClG,WAAW,EAAE,CAAC,CAAC,WAAW;QAC1B,IAAI;KACL,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,20 @@
1
+ import { Bundle } from './core/bundle';
2
+ import { RuleSet } from './core/rules';
3
+ import { RecordStore } from './core/store';
4
+ import type { ReplayStats } from './replay/engine';
5
+ import { type ReplayTarget } from './tasks';
6
+ export interface Args {
7
+ positional: string[];
8
+ flags: Record<string, string | boolean>;
9
+ }
10
+ export declare function parseArgs(argv: string[]): Args;
11
+ export declare const str: (v: string | boolean | undefined) => string | undefined;
12
+ export declare const num: (v: string | boolean | undefined, d: number) => number;
13
+ export declare function main(argv: string[]): Promise<void>;
14
+ /** Rules and recordings of a target, as the replay engine loads them. */
15
+ export declare function loadTarget(target: ReplayTarget): {
16
+ bundle: Bundle;
17
+ rules: RuleSet;
18
+ store: RecordStore;
19
+ };
20
+ export declare function printStats(stats: ReplayStats): void;
@@ -0,0 +1,331 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.num = exports.str = void 0;
37
+ exports.parseArgs = parseArgs;
38
+ exports.main = main;
39
+ exports.loadTarget = loadTarget;
40
+ exports.printStats = printStats;
41
+ const fs = __importStar(require("node:fs"));
42
+ const os = __importStar(require("node:os"));
43
+ const path = __importStar(require("node:path"));
44
+ const browser_1 = require("./browser");
45
+ const audit_1 = require("./core/audit");
46
+ const bundle_1 = require("./core/bundle");
47
+ const lookup_1 = require("./core/lookup");
48
+ const normalize_1 = require("./core/normalize");
49
+ const rules_1 = require("./core/rules");
50
+ const store_1 = require("./core/store");
51
+ const util_1 = require("./core/util");
52
+ const grading_1 = require("./grading");
53
+ const judge_1 = require("./judge");
54
+ const launcher_1 = require("./replay/launcher");
55
+ const scoring_1 = require("./scoring");
56
+ const tasks_1 = require("./tasks");
57
+ const HELP = `pab - Personal Agent Benchmark: replay real-site tasks offline
58
+
59
+ pab list tasks in this repository
60
+ pab replay <task> [--port 18899] [--url URL] [--cdp-port 9333] [--headless] [--no-browser] [--log FILE]
61
+ replay proxy plus a Chrome window that uses it
62
+ pab serve <task> [--port 8899] [--host 127.0.0.1] [--log FILE]
63
+ replay proxy only (containers, external browsers)
64
+ pab inspect <task> <url> [--method GET] normalized key of a request and the recording it hits
65
+ pab audit [<task>...] check recordings are safe to publish (all tasks by default)
66
+ pab unpack <task> unpack the recording and print its directory
67
+ pab stats <replay-log.jsonl> summarize a replay log
68
+ pab grade <task> --log <replay-log.jsonl> [--answer FILE] [--out DIR] [--judge api|cli] [--model M]
69
+ score a run and list the pages that returned 404;
70
+ writes result.json and report.html
71
+
72
+ <task> is a task id, a task directory, or an unpacked bundle directory.
73
+ --repo DIR (or PAB_REPO) points at another checkout of this repository.
74
+ `;
75
+ function parseArgs(argv) {
76
+ const positional = [];
77
+ const flags = {};
78
+ for (let i = 0; i < argv.length; i++) {
79
+ const a = argv[i];
80
+ if (a.startsWith('--')) {
81
+ const name = a.slice(2);
82
+ const next = argv[i + 1];
83
+ if (next !== undefined && !next.startsWith('--')) {
84
+ flags[name] = next;
85
+ i++;
86
+ }
87
+ else {
88
+ flags[name] = true;
89
+ }
90
+ }
91
+ else {
92
+ positional.push(a);
93
+ }
94
+ }
95
+ return { positional, flags };
96
+ }
97
+ const str = (v) => (typeof v === 'string' ? v : undefined);
98
+ exports.str = str;
99
+ const num = (v, d) => (typeof v === 'string' ? Number(v) : d);
100
+ exports.num = num;
101
+ const repoOf = (flags) => ((0, exports.str)(flags.repo) ? new tasks_1.Repo(path.resolve((0, exports.str)(flags.repo))) : tasks_1.Repo.default());
102
+ async function main(argv) {
103
+ const [cmd, ...rest] = argv;
104
+ const { positional, flags } = parseArgs(rest);
105
+ switch (cmd) {
106
+ case 'list':
107
+ return cmdList(flags);
108
+ case 'replay':
109
+ return cmdReplay(positional, flags);
110
+ case 'serve':
111
+ return cmdServe(positional, flags);
112
+ case 'inspect':
113
+ return cmdInspect(positional, flags);
114
+ case 'audit':
115
+ return cmdAudit(positional, flags);
116
+ case 'unpack': {
117
+ const [arg] = positional;
118
+ if (!arg)
119
+ throw new Error('usage: pab unpack <task>');
120
+ console.log((await (0, tasks_1.resolveTarget)(arg, repoOf(flags))).bundleDir);
121
+ return;
122
+ }
123
+ case 'stats':
124
+ return cmdStats(positional);
125
+ case 'grade':
126
+ return cmdGrade(positional, flags);
127
+ default:
128
+ console.log(HELP);
129
+ }
130
+ }
131
+ function cmdList(flags) {
132
+ const repo = repoOf(flags);
133
+ for (const id of repo.taskIds()) {
134
+ const task = repo.task(id);
135
+ const card = task.card();
136
+ const recorded = task.recordingFiles().length ? '' : ' (no recording yet)';
137
+ console.log(`${id.padEnd(28)} ${card.title ?? ''}${recorded}`);
138
+ }
139
+ }
140
+ /** Rules and recordings of a target, as the replay engine loads them. */
141
+ function loadTarget(target) {
142
+ const bundle = new bundle_1.Bundle(target.bundleDir);
143
+ const rules = rules_1.RuleSet.load([bundle.rulesDir, ...target.rulesDirs]);
144
+ const store = new store_1.RecordStore(bundle, rules);
145
+ return { bundle, rules, store };
146
+ }
147
+ async function cmdReplay(positional, flags) {
148
+ const [arg] = positional;
149
+ if (!arg)
150
+ throw new Error('usage: pab replay <task>');
151
+ const repo = repoOf(flags);
152
+ const target = await (0, tasks_1.resolveTarget)(arg, repo);
153
+ const bundle = new bundle_1.Bundle(target.bundleDir);
154
+ const manifest = bundle.manifest();
155
+ const port = (0, exports.num)(flags.port, 18899);
156
+ const logFile = (0, exports.str)(flags.log) ?? path.join(repo.cacheDir, 'logs', `replay-${target.name}-${Date.now()}.jsonl`);
157
+ const server = await (0, launcher_1.startReplayServer)({ bundleDir: target.bundleDir, rulesDirs: target.rulesDirs, port, logFile });
158
+ console.log(`replay proxy: ${server.proxy} (${target.name}, recorded ${manifest.captured_from})`);
159
+ console.log(`request log: ${logFile}`);
160
+ const stop = async () => {
161
+ const stats = await server.control('/stats').catch(() => undefined);
162
+ await server.stop();
163
+ if (stats)
164
+ printStats(stats);
165
+ };
166
+ if (flags['no-browser'] === true) {
167
+ console.log(`point a browser at it with: --proxy-server=${server.proxy} --ignore-certificate-errors --disable-quic`);
168
+ await new Promise((resolve) => process.once('SIGINT', () => resolve()));
169
+ await stop();
170
+ return;
171
+ }
172
+ const profile = fs.mkdtempSync(path.join(os.tmpdir(), 'pab-replay-'));
173
+ const cdpPort = flags['cdp-port'] ? (0, exports.num)(flags['cdp-port'], 9333) : undefined;
174
+ const context = await (0, browser_1.launchBrowser)({
175
+ profileDir: profile,
176
+ headless: flags.headless === true,
177
+ proxy: { server: server.proxy },
178
+ ignoreHTTPSErrors: true,
179
+ locale: manifest.locale,
180
+ timezoneId: manifest.timezone,
181
+ cdpPort,
182
+ });
183
+ await context.clock.install({ time: new Date(manifest.captured_from) });
184
+ if (cdpPort)
185
+ console.log(`CDP: http://127.0.0.1:${cdpPort} (an agent can drive this browser)`);
186
+ const page = context.pages()[0] ?? (await context.newPage());
187
+ // the first page opened while recording, unless told otherwise
188
+ const url = (0, exports.str)(flags.url) ?? bundle.pages().find((p) => p.note !== 'not-found-probe')?.url;
189
+ if (url)
190
+ await page.goto(url, { waitUntil: 'domcontentloaded' }).catch((e) => console.error(String(e)));
191
+ console.log('Close the browser window or press Ctrl-C to stop.');
192
+ await new Promise((resolve) => {
193
+ context.on('close', () => resolve());
194
+ process.once('SIGINT', () => resolve());
195
+ });
196
+ await context.close().catch(() => undefined);
197
+ fs.rmSync(profile, { recursive: true, force: true });
198
+ await stop();
199
+ }
200
+ async function cmdServe(positional, flags) {
201
+ const [arg] = positional;
202
+ if (!arg)
203
+ throw new Error('usage: pab serve <task>');
204
+ const target = await (0, tasks_1.resolveTarget)(arg, repoOf(flags));
205
+ const host = (0, exports.str)(flags.host) ?? '127.0.0.1';
206
+ const server = await (0, launcher_1.startReplayServer)({
207
+ bundleDir: target.bundleDir,
208
+ rulesDirs: target.rulesDirs,
209
+ port: (0, exports.num)(flags.port, 8899),
210
+ host,
211
+ logFile: (0, exports.str)(flags.log),
212
+ });
213
+ console.log(`replay proxy for ${target.name} listening on ${host}:${server.port}`);
214
+ await new Promise((resolve) => {
215
+ process.once('SIGINT', () => resolve());
216
+ process.once('SIGTERM', () => resolve());
217
+ server.child.once('exit', () => resolve());
218
+ });
219
+ await server.stop();
220
+ }
221
+ async function cmdInspect(positional, flags) {
222
+ const [arg, url] = positional;
223
+ if (!arg || !url)
224
+ throw new Error('usage: pab inspect <task> <url>');
225
+ const { rules, store } = loadTarget(await (0, tasks_1.resolveTarget)(arg, repoOf(flags)));
226
+ const norm = (0, normalize_1.normalizeRequest)({ method: (0, exports.str)(flags.method) ?? 'GET', url }, rules);
227
+ const found = (0, lookup_1.lookup)(store, norm);
228
+ console.log(JSON.stringify({ key: norm.key, kind: norm.kind, endpoint: norm.endpoint?.name, site: norm.site?.site, text: norm.text, match: found ? { source: found.source, entry: found.ix.entry.id, url: found.ix.entry.url, detail: found.detail } : null }, null, 2));
229
+ }
230
+ async function cmdAudit(positional, flags) {
231
+ const repo = repoOf(flags);
232
+ const args = positional.length ? positional : repo.taskIds().filter((id) => repo.task(id).recordingFiles().length);
233
+ let failed = 0;
234
+ for (const arg of args) {
235
+ const target = await (0, tasks_1.resolveTarget)(arg, repo);
236
+ const { bundle, rules } = loadTarget(target);
237
+ const findings = await (0, audit_1.auditBundle)(bundle, rules);
238
+ for (const part of target.task?.recordingFiles() ?? []) {
239
+ const mb = fs.statSync(part).size / 1024 / 1024;
240
+ if (mb > 50)
241
+ findings.push({ check: 'size', detail: `${path.basename(part)} is ${mb.toFixed(1)} MB (git hosts warn above 50 MB)` });
242
+ }
243
+ console.log(`${findings.length ? 'FAIL' : 'ok '} ${target.name}`);
244
+ for (const f of findings)
245
+ console.log(` [${f.check}] ${f.detail}`);
246
+ if (findings.length)
247
+ failed++;
248
+ }
249
+ if (failed)
250
+ process.exitCode = 1;
251
+ }
252
+ function cmdStats(positional) {
253
+ const [file] = positional;
254
+ if (!file)
255
+ throw new Error('usage: pab stats <replay-log.jsonl>');
256
+ const rows = (0, util_1.readJsonl)(file);
257
+ const byClass = {};
258
+ const misses = {};
259
+ const bySource = {};
260
+ for (const r of rows) {
261
+ const c = (byClass[r.class] ??= { total: 0, hit: 0, miss: 0 });
262
+ c.total++;
263
+ bySource[r.source] = (bySource[r.source] ?? 0) + 1;
264
+ if (r.source === 'miss') {
265
+ c.miss++;
266
+ const m = (misses[r.url] ??= { count: 0, resourceClass: r.class, url: r.url });
267
+ m.count++;
268
+ }
269
+ else
270
+ c.hit++;
271
+ }
272
+ printStats({ requests: rows.length, byClass: byClass, bySource, misses: misses });
273
+ }
274
+ async function cmdGrade(positional, flags) {
275
+ const [arg] = positional;
276
+ const logFile = (0, exports.str)(flags.log);
277
+ if (!arg || !logFile)
278
+ throw new Error('usage: pab grade <task> --log <replay-log.jsonl> [--answer FILE] [--out DIR]');
279
+ const repo = repoOf(flags);
280
+ const target = await (0, tasks_1.resolveTarget)(arg, repo);
281
+ const { bundle, rules } = loadTarget(target);
282
+ const task = target.task;
283
+ const card = task?.card();
284
+ let grading;
285
+ if (task && fs.existsSync(task.gradingPath)) {
286
+ grading = (0, grading_1.parseGrading)(fs.readFileSync(task.gradingPath, 'utf8'));
287
+ const errors = (0, grading_1.validateGrading)(grading);
288
+ if (errors.length)
289
+ throw new Error(`${task.gradingPath}:\n ${errors.join('\n ')}`);
290
+ }
291
+ const answer = (0, exports.str)(flags.answer) ? fs.readFileSync((0, exports.str)(flags.answer), 'utf8') : undefined;
292
+ const result = await (0, scoring_1.scoreRun)({
293
+ task: { id: card?.id ?? target.name, title: card?.title, prompt: card?.prompt },
294
+ grading,
295
+ answer,
296
+ log: (0, util_1.readJsonl)(logFile),
297
+ logFile: path.resolve(logFile),
298
+ rules,
299
+ probes: Object.values(bundle.manifest().not_found ?? {}),
300
+ judge: grading && answer !== undefined ? (0, judge_1.pickJudge)((0, exports.str)(flags.judge), (0, exports.str)(flags.model)) : undefined,
301
+ });
302
+ const out = (0, exports.str)(flags.out) ?? path.join(repo.cacheDir, 'runs', `${target.name}-${new Date().toISOString().replace(/[:.]/g, '-')}`);
303
+ const files = (0, scoring_1.writeRun)(result, out);
304
+ if (result.score !== undefined)
305
+ console.log(`score ${(result.score * 100).toFixed(1)}%${result.grader ? ` (graded by ${result.grader.model})` : ''}`);
306
+ for (const n of result.notes)
307
+ console.log(n);
308
+ console.log(`pages that returned 404: ${result.pages.not_found.length}`);
309
+ for (const p of result.pages.not_found)
310
+ console.log(` [${p.source === 'miss' ? 'not recorded' : 'recorded as 404'}] x${p.count} ${p.route.slice(0, 140)}`);
311
+ console.log(`result: ${files.json}`);
312
+ console.log(`report: ${files.html}`);
313
+ if (grading && answer !== undefined && result.score === undefined)
314
+ process.exitCode = 1;
315
+ }
316
+ function printStats(stats) {
317
+ console.log(`requests ${stats.requests}`);
318
+ for (const [cls, c] of Object.entries(stats.byClass)) {
319
+ if (!c.total)
320
+ continue;
321
+ console.log(` ${cls.padEnd(9)} hit ${c.hit}/${c.total} (${((c.hit / c.total) * 100).toFixed(1)}%)`);
322
+ }
323
+ console.log(` sources ${JSON.stringify(stats.bySource)}`);
324
+ const top = Object.values(stats.misses).sort((a, b) => b.count - a.count).slice(0, 15);
325
+ if (top.length) {
326
+ console.log(' misses (top 15):');
327
+ for (const m of top)
328
+ console.log(` [${m.resourceClass}] x${m.count} ${m.url.slice(0, 140)}`);
329
+ }
330
+ }
331
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../../src/cli.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Large files travel as gzip-compressed tar files: the WARC of a published recording (below) and
3
+ * the recording tool's hand-off archive. Git hosts reject large files (GitHub: 100 MB, with a
4
+ * warning from 50 MB), so archives above PART_BYTES are split into numbered parts:
5
+ * warc.tar.gz.000, warc.tar.gz.001, ... which are read back in order as one stream.
6
+ */
7
+ export declare const PART_BYTES: number;
8
+ /** Files of a bundle that can be replayed and published; everything else stays with the recorder. */
9
+ export declare const PUBLIC_BUNDLE_FILES: string[];
10
+ export declare const isBundleFile: (p: string) => boolean;
11
+ /**
12
+ * Writes `files` (paths relative to `srcDir`, in this order) to `outFile`, a .tar.gz, replacing
13
+ * any earlier archive or parts there. Returns the files written: `outFile` or its parts.
14
+ */
15
+ export declare function createArchive(srcDir: string, files: string[], outFile: string, partBytes?: number): Promise<string[]>;
16
+ /** The archive at `file` as stored: the file itself, or its numbered parts in order. */
17
+ export declare function archiveParts(file: string): string[];
18
+ export declare function hashParts(parts: string[]): string;
19
+ /**
20
+ * Extracts an archive (one file or its parts) into `destDir`. Only regular files and directories
21
+ * are extracted, and only paths accepted by `allow`; tar itself refuses absolute paths and "..".
22
+ */
23
+ export declare function extractArchive(parts: string[], destDir: string, allow?: (p: string) => boolean): Promise<string[]>;
24
+ /**
25
+ * Layout version of a recording published in a task. Replay reads only this version: a change to
26
+ * the layout or to the files in it bumps the number, and existing tasks are rewritten to match.
27
+ */
28
+ export declare const RECORDING_FORMAT = 2;
29
+ /**
30
+ * A bundle as published in a task (tasks/<id>/recording/):
31
+ * manifest.json the bundle's manifest plus "format"
32
+ * pages.jsonl pages/pages.jsonl
33
+ * entries.jsonl.gz indexes/entries.jsonl, gzip-compressed
34
+ * warc.tar.gz archive/data.warc (warc.tar.gz.000, .001, ... above PART_BYTES)
35
+ * The WARC sits apart from the files whose layout may change, so a new format rewrites only the
36
+ * small files and leaves the large archive as it was.
37
+ */
38
+ export declare const RECORDING_FILES: {
39
+ readonly manifest: 'manifest.json';
40
+ readonly pages: 'pages.jsonl';
41
+ readonly entries: 'entries.jsonl.gz';
42
+ readonly warc: 'warc.tar.gz';
43
+ };
44
+ /** Writes the bundle at `bundleDir` to `outDir` in the published layout, replacing what is there. */
45
+ export declare function publishRecording(bundleDir: string, outDir: string): Promise<string[]>;
46
+ /** Files of the recording published in `dir`, in a fixed order; empty when there is none. */
47
+ export declare function recordingFiles(dir: string): string[];
48
+ /** Turns the recording published in `dir` back into a bundle at `destDir`; refuses other formats. */
49
+ export declare function unpackRecording(dir: string, destDir: string): Promise<void>;