@empiricalrun/playwright-utils 0.58.5 → 0.59.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/CHANGELOG.md +25 -0
- package/dist/api-worker-client.d.ts +17 -0
- package/dist/api-worker-client.d.ts.map +1 -1
- package/dist/api-worker-client.js +34 -2
- package/dist/generated/api-worker-openapi.d.ts +348 -97
- package/dist/generated/api-worker-openapi.d.ts.map +1 -1
- package/dist/overlay-tests/cache.spec.js +28 -4
- package/dist/reporter/test-case-sync.d.ts +2 -25
- package/dist/reporter/test-case-sync.d.ts.map +1 -1
- package/dist/reporter/test-case-sync.js +82 -7
- package/dist/test/expect/visual.d.ts.map +1 -1
- package/dist/test/expect/visual.js +4 -1
- package/dist/test/scripts/pw-locator-patch/dismiss-overlays/index.d.ts.map +1 -1
- package/dist/test/scripts/pw-locator-patch/dismiss-overlays/index.js +5 -0
- package/dist/test/scripts/pw-locator-patch/vision/query.d.ts.map +1 -1
- package/dist/test/scripts/pw-locator-patch/vision/query.js +4 -1
- package/package.json +5 -5
|
@@ -20,6 +20,25 @@ const useCache = (data) => {
|
|
|
20
20
|
}
|
|
21
21
|
fs_1.default.writeFileSync(cache_1.CACHE_FILE, JSON.stringify(cacheValue, null, 2), "utf8");
|
|
22
22
|
};
|
|
23
|
+
// Unsets env vars for the duration of a test and puts them back exactly as they
|
|
24
|
+
// were — assigning `undefined` to process.env would store the string
|
|
25
|
+
// "undefined", which reads as a present (and truthy) credential.
|
|
26
|
+
function clearEnv(names) {
|
|
27
|
+
const saved = names.map((name) => [name, process.env[name]]);
|
|
28
|
+
for (const name of names) {
|
|
29
|
+
delete process.env[name];
|
|
30
|
+
}
|
|
31
|
+
return () => {
|
|
32
|
+
for (const [name, value] of saved) {
|
|
33
|
+
if (value === undefined) {
|
|
34
|
+
delete process.env[name];
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
process.env[name] = value;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
}
|
|
23
42
|
fixtures_1.test.beforeEach(async ({ page }) => {
|
|
24
43
|
(0, test_1.injectLocatorHighlightScripts)(page, fixtures_1.test);
|
|
25
44
|
});
|
|
@@ -39,9 +58,12 @@ fixtures_1.test.afterEach(() => {
|
|
|
39
58
|
]);
|
|
40
59
|
await page.goto(`${server.baseURL}/glass-pane.html`);
|
|
41
60
|
let error;
|
|
42
|
-
//
|
|
43
|
-
|
|
44
|
-
|
|
61
|
+
// The only two routes the browser agent has to a model: the project API key
|
|
62
|
+
// (api-worker's proxy) and a sandbox's OPENAI_BASE_URL. Clearing both makes
|
|
63
|
+
// llmProxyOpenAIConfig throw before any request — which matters here more
|
|
64
|
+
// than elsewhere, because this is the recursive case and every extra
|
|
65
|
+
// dismissal would spawn another agent and another set of screenshots.
|
|
66
|
+
const restoreEnv = clearEnv(["EMPIRICALRUN_API_KEY", "OPENAI_BASE_URL"]);
|
|
45
67
|
try {
|
|
46
68
|
// Target element click will trigger the cache - which will invoke another
|
|
47
69
|
// click to the same element (recursive overlay dismissal)
|
|
@@ -50,7 +72,9 @@ fixtures_1.test.afterEach(() => {
|
|
|
50
72
|
catch (e) {
|
|
51
73
|
error = e;
|
|
52
74
|
}
|
|
75
|
+
finally {
|
|
76
|
+
restoreEnv();
|
|
77
|
+
}
|
|
53
78
|
(0, fixtures_1.expect)(error).toBeDefined();
|
|
54
|
-
process.env.OPENAI_API_KEY = originalApiKey;
|
|
55
79
|
(0, fixtures_1.expect)(error.message).toContain('<div class="glass-pane"></div> intercepts pointer events');
|
|
56
80
|
});
|
|
@@ -1,28 +1,5 @@
|
|
|
1
|
+
import type { TestCaseSyncManifest } from "@empiricalrun/shared-types/playwright-utils";
|
|
1
2
|
import type { FullConfig, Suite } from "@playwright/test/reporter";
|
|
2
|
-
export
|
|
3
|
-
id: string;
|
|
4
|
-
name: string;
|
|
5
|
-
file_path: string;
|
|
6
|
-
suites: string[];
|
|
7
|
-
playwright_project: string;
|
|
8
|
-
tags: string[];
|
|
9
|
-
}
|
|
10
|
-
export interface TestCaseDependencyGroupMember {
|
|
11
|
-
test_case_id: string;
|
|
12
|
-
dependency_order: number;
|
|
13
|
-
}
|
|
14
|
-
export interface TestCaseDependencyGroup {
|
|
15
|
-
id: string;
|
|
16
|
-
kind: "playwright_serial";
|
|
17
|
-
file_path: string;
|
|
18
|
-
title_path: string[];
|
|
19
|
-
playwright_project: string;
|
|
20
|
-
members: TestCaseDependencyGroupMember[];
|
|
21
|
-
}
|
|
22
|
-
export interface TestCaseSyncManifest {
|
|
23
|
-
version: 1;
|
|
24
|
-
test_cases: TestCaseSyncTestCase[];
|
|
25
|
-
dependency_groups: TestCaseDependencyGroup[];
|
|
26
|
-
}
|
|
3
|
+
export type { TestCaseDependencyGroup, TestCaseDependencyGroupMember, TestCaseSyncManifest, TestCaseSyncTestCase, } from "@empiricalrun/shared-types/playwright-utils";
|
|
27
4
|
export declare function collectTestCaseSyncManifest(config: FullConfig, rootSuite: Suite): TestCaseSyncManifest;
|
|
28
5
|
//# sourceMappingURL=test-case-sync.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"test-case-sync.d.ts","sourceRoot":"","sources":["../../src/reporter/test-case-sync.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"test-case-sync.d.ts","sourceRoot":"","sources":["../../src/reporter/test-case-sync.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEV,oBAAoB,EAErB,MAAM,6CAA6C,CAAC;AACrD,OAAO,KAAK,EACV,UAAU,EAEV,KAAK,EAEN,MAAM,2BAA2B,CAAC;AAUnC,YAAY,EACV,uBAAuB,EACvB,6BAA6B,EAC7B,oBAAoB,EACpB,oBAAoB,GACrB,MAAM,6CAA6C,CAAC;AAwRrD,wBAAgB,2BAA2B,CACzC,MAAM,EAAE,UAAU,EAClB,SAAS,EAAE,KAAK,GACf,oBAAoB,CAyBtB"}
|
|
@@ -41,7 +41,7 @@ function stableGroupId(input) {
|
|
|
41
41
|
input.column,
|
|
42
42
|
]))
|
|
43
43
|
.digest("hex");
|
|
44
|
-
return
|
|
44
|
+
return `${input.kind}:${hash}`;
|
|
45
45
|
}
|
|
46
46
|
function collectDirectTestCases(rootDir, suite, state) {
|
|
47
47
|
return suite.tests.map((test) => ({
|
|
@@ -53,11 +53,7 @@ function collectDirectTestCases(rootDir, suite, state) {
|
|
|
53
53
|
tags: normalizeTags(test.tags),
|
|
54
54
|
}));
|
|
55
55
|
}
|
|
56
|
-
function
|
|
57
|
-
if (state.serialAncestor || getSuiteParallelMode(suite) !== "serial") {
|
|
58
|
-
return null;
|
|
59
|
-
}
|
|
60
|
-
const tests = suite.allTests();
|
|
56
|
+
function buildDependencyGroup(rootDir, suite, state, kind, tests) {
|
|
61
57
|
if (tests.length < 2) {
|
|
62
58
|
return null;
|
|
63
59
|
}
|
|
@@ -71,13 +67,14 @@ function collectOutermostSerialGroup(rootDir, suite, state) {
|
|
|
71
67
|
const location = suite.location ?? firstTest.location;
|
|
72
68
|
return {
|
|
73
69
|
id: stableGroupId({
|
|
70
|
+
kind,
|
|
74
71
|
projectName,
|
|
75
72
|
filePath,
|
|
76
73
|
titlePath,
|
|
77
74
|
line: location.line,
|
|
78
75
|
column: location.column,
|
|
79
76
|
}),
|
|
80
|
-
kind
|
|
77
|
+
kind,
|
|
81
78
|
file_path: filePath,
|
|
82
79
|
title_path: titlePath,
|
|
83
80
|
playwright_project: projectName,
|
|
@@ -87,14 +84,81 @@ function collectOutermostSerialGroup(rootDir, suite, state) {
|
|
|
87
84
|
})),
|
|
88
85
|
};
|
|
89
86
|
}
|
|
87
|
+
function collectOutermostSerialGroup(rootDir, suite, state) {
|
|
88
|
+
if (state.serialAncestor || getSuiteParallelMode(suite) !== "serial") {
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
91
|
+
return buildDependencyGroup(rootDir, suite, state, "playwright_serial", suite.allTests());
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Tests under this suite that Playwright would place in its sequential
|
|
95
|
+
* ("general") group: everything except subtrees that opt back into
|
|
96
|
+
* parallelism. Order is declaration order, which is the order the group
|
|
97
|
+
* executes in.
|
|
98
|
+
*/
|
|
99
|
+
function sequentialTests(suite) {
|
|
100
|
+
const tests = [...suite.tests];
|
|
101
|
+
for (const child of suite.suites) {
|
|
102
|
+
if (getSuiteParallelMode(child) !== "parallel") {
|
|
103
|
+
tests.push(...sequentialTests(child));
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
return tests;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* The `playwright_default` counterpart of the serial collector: the outermost
|
|
110
|
+
* scope whose tests Playwright schedules as one ordered worker group without
|
|
111
|
+
* serial semantics.
|
|
112
|
+
*
|
|
113
|
+
* Two shapes qualify (mirroring `createTestGroups` in Playwright's
|
|
114
|
+
* `runner/testGroups.ts`):
|
|
115
|
+
*
|
|
116
|
+
* - a `test.describe.configure({ mode: "default" })` suite — grouped whole,
|
|
117
|
+
* including any nested parallel subtree, because Playwright keys the group
|
|
118
|
+
* on the outermost sequential suite;
|
|
119
|
+
* - a file with no explicit mode in a non-fullyParallel project — Playwright's
|
|
120
|
+
* per-file "general" group, which excludes subtrees that opt back into
|
|
121
|
+
* `parallel`.
|
|
122
|
+
*/
|
|
123
|
+
function collectOutermostDefaultGroup(rootDir, suite, state) {
|
|
124
|
+
if (state.sequentialAncestor) {
|
|
125
|
+
return null;
|
|
126
|
+
}
|
|
127
|
+
const mode = getSuiteParallelMode(suite);
|
|
128
|
+
if (mode === "default") {
|
|
129
|
+
return buildDependencyGroup(rootDir, suite, state, "playwright_default", suite.allTests());
|
|
130
|
+
}
|
|
131
|
+
if (suite.type === "file" && !state.insideParallel && mode === "none") {
|
|
132
|
+
return buildDependencyGroup(rootDir, suite, state, "playwright_default", sequentialTests(suite));
|
|
133
|
+
}
|
|
134
|
+
return null;
|
|
135
|
+
}
|
|
90
136
|
function walkSuite(rootDir, suite, state, testCases, dependencyGroups) {
|
|
91
137
|
const isDescribe = suite.type === "describe";
|
|
92
138
|
const suites = isDescribe ? [...state.suites, suite.title] : state.suites;
|
|
93
139
|
const nextState = { ...state, suites };
|
|
140
|
+
const mode = getSuiteParallelMode(suite);
|
|
141
|
+
// A serial group is emitted even inside a default scope: the outer default
|
|
142
|
+
// group wins for scheduling, but serial membership carries extra semantics
|
|
143
|
+
// (failure cascade, re-expansion) its consumers still need.
|
|
94
144
|
const serialGroup = collectOutermostSerialGroup(rootDir, suite, nextState);
|
|
95
145
|
if (serialGroup) {
|
|
96
146
|
dependencyGroups.push(serialGroup);
|
|
97
147
|
nextState.serialAncestor = suite;
|
|
148
|
+
if (!nextState.sequentialAncestor) {
|
|
149
|
+
nextState.sequentialAncestor = suite;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
const defaultGroup = collectOutermostDefaultGroup(rootDir, suite, nextState);
|
|
153
|
+
if (defaultGroup) {
|
|
154
|
+
dependencyGroups.push(defaultGroup);
|
|
155
|
+
nextState.sequentialAncestor = suite;
|
|
156
|
+
}
|
|
157
|
+
// fullyParallel (project suite) or an explicit `mode: "parallel"` — every
|
|
158
|
+
// descendant is independently schedulable unless a sequential suite below
|
|
159
|
+
// says otherwise.
|
|
160
|
+
if (mode === "parallel") {
|
|
161
|
+
nextState.insideParallel = true;
|
|
98
162
|
}
|
|
99
163
|
testCases.push(...collectDirectTestCases(rootDir, suite, nextState));
|
|
100
164
|
for (const child of suite.suites) {
|
|
@@ -112,6 +176,15 @@ function walkSuite(rootDir, suite, state, testCases, dependencyGroups) {
|
|
|
112
176
|
: "";
|
|
113
177
|
childState.suites = [];
|
|
114
178
|
childState.serialAncestor = null;
|
|
179
|
+
childState.sequentialAncestor = null;
|
|
180
|
+
}
|
|
181
|
+
else if (childState.sequentialAncestor &&
|
|
182
|
+
getSuiteParallelMode(childState.sequentialAncestor) === "none" &&
|
|
183
|
+
getSuiteParallelMode(child) === "parallel") {
|
|
184
|
+
// The scope was a plain file's "general" group, which excludes subtrees
|
|
185
|
+
// that opt back into parallel — so below this child, sequential suites
|
|
186
|
+
// start their own groups again.
|
|
187
|
+
childState.sequentialAncestor = null;
|
|
115
188
|
}
|
|
116
189
|
walkSuite(rootDir, child, childState, testCases, dependencyGroups);
|
|
117
190
|
}
|
|
@@ -125,6 +198,8 @@ function collectTestCaseSyncManifest(config, rootSuite) {
|
|
|
125
198
|
filePath: "",
|
|
126
199
|
suites: [],
|
|
127
200
|
serialAncestor: null,
|
|
201
|
+
sequentialAncestor: null,
|
|
202
|
+
insideParallel: false,
|
|
128
203
|
}, testCases, dependencyGroups);
|
|
129
204
|
return {
|
|
130
205
|
version: 1,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"visual.d.ts","sourceRoot":"","sources":["../../../src/test/expect/visual.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,OAAO,EAAE,IAAI,EAAY,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"visual.d.ts","sourceRoot":"","sources":["../../../src/test/expect/visual.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,OAAO,EAAE,IAAI,EAAY,MAAM,kBAAkB,CAAC;AAUhE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AA0E7C,wBAAsB,WAAW,CAC/B,MAAM,EAAE,IAAI,GAAG,OAAO,EACtB,eAAe,EAAE,MAAM,GACtB,OAAO,CAAC,aAAa,CAAC,CA2FxB"}
|
|
@@ -10,6 +10,7 @@ const fs_1 = __importDefault(require("fs"));
|
|
|
10
10
|
const openai_1 = __importDefault(require("openai"));
|
|
11
11
|
const os_1 = __importDefault(require("os"));
|
|
12
12
|
const path_1 = __importDefault(require("path"));
|
|
13
|
+
const api_worker_client_1 = require("../../api-worker-client");
|
|
13
14
|
const mouse_pointer_1 = require("../scripts/mouse-pointer");
|
|
14
15
|
function getCallerLine() {
|
|
15
16
|
const stack = new Error().stack;
|
|
@@ -85,7 +86,9 @@ async function toLookRight(target, pageDescription) {
|
|
|
85
86
|
const base64Image = screenshot.toString("base64");
|
|
86
87
|
await (0, mouse_pointer_1.addHighlighterScriptsToPage)(page);
|
|
87
88
|
const model = "gpt-4o";
|
|
88
|
-
|
|
89
|
+
// Same api-worker proxy the overlay-dismissal agent uses, so a test run needs
|
|
90
|
+
// no OpenAI key of its own; undefined falls back to the SDK's env defaults.
|
|
91
|
+
const openai = new openai_1.default({ maxRetries: 5, ...(0, api_worker_client_1.llmProxyOpenAIConfig)() });
|
|
89
92
|
const completion = await openai.chat.completions.create({
|
|
90
93
|
model,
|
|
91
94
|
messages: [
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/test/scripts/pw-locator-patch/dismiss-overlays/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/test/scripts/pw-locator-patch/dismiss-overlays/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAIxC,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAG1C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAK9C,wBAAgB,gBAAgB,CAAC,YAAY,EAAE,MAAM,GAAG,SAAS,WAKhE;AAED,qBAAa,mBAAmB;IAOlB,OAAO,CAAC,QAAQ,CAAC,IAAI;IANjC,OAAO,CAAC,iBAAiB,CAIhB;IAET,YAA6B,IAAI,EAAE,IAAI,EAAI;IAErC,WAAW,kBAIhB;IAEK,GAAG,CACP,aAAa,EAAE,GAAG,EAClB,QAAQ,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,IAAI,EACvC,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,IAAI,CAAC,CAuDf;YAEa,QAAQ;CAqCvB;AAED,wBAAgB,0BAA0B,CACxC,YAAY,EAAE,MAAM,GACnB,cAAc,GAAG,SAAS,CAiC5B"}
|
|
@@ -4,6 +4,7 @@ exports.OverlayDismissAgent = void 0;
|
|
|
4
4
|
exports.isErrorSupported = isErrorSupported;
|
|
5
5
|
exports.extractInterceptingElement = extractInterceptingElement;
|
|
6
6
|
const cua_1 = require("@empiricalrun/cua");
|
|
7
|
+
const api_worker_client_1 = require("../../../../api-worker-client");
|
|
7
8
|
const telemetry_1 = require("../../../../telemetry");
|
|
8
9
|
const cache_1 = require("./cache");
|
|
9
10
|
const prompt_1 = require("./prompt");
|
|
@@ -99,6 +100,10 @@ class OverlayDismissAgent {
|
|
|
99
100
|
// already on the current page. Including page_goto causes the model
|
|
100
101
|
// to waste turns trying to navigate instead of clicking dismiss.
|
|
101
102
|
includePageGotoTool: false,
|
|
103
|
+
// Route the model call through api-worker (Cloudflare AI Gateway) on the
|
|
104
|
+
// project API key, so test-run containers carry no OpenAI key. Undefined
|
|
105
|
+
// on a dev machine or in a pi sandbox — see the helper.
|
|
106
|
+
openAIClientConfig: (0, api_worker_client_1.llmProxyOpenAIConfig)(),
|
|
102
107
|
});
|
|
103
108
|
this.cachingCandidates.push({
|
|
104
109
|
dom: element?.interceptor,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"query.d.ts","sourceRoot":"","sources":["../../../../../src/test/scripts/pw-locator-patch/vision/query.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"query.d.ts","sourceRoot":"","sources":["../../../../../src/test/scripts/pw-locator-patch/vision/query.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAIhD,wBAAgB,UAAU,CAAC,YAAY,EAAE,QAAQ,GAAG;IAAE,SAAS,EAAE,OAAO,CAAA;CAAE,QASzE"}
|
|
@@ -2,11 +2,14 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.patchQuery = patchQuery;
|
|
4
4
|
const vision_1 = require("@empiricalrun/llm/vision");
|
|
5
|
+
const api_worker_client_1 = require("../../../../api-worker-client");
|
|
5
6
|
function patchQuery(LocatorClass) {
|
|
6
7
|
LocatorClass.prototype.query = async function (instruction) {
|
|
7
8
|
await this.waitFor({ state: "visible" });
|
|
8
9
|
const screenshot = await this.screenshot();
|
|
9
|
-
const answer = await (0, vision_1.query)(screenshot.toString("base64"), instruction
|
|
10
|
+
const answer = await (0, vision_1.query)(screenshot.toString("base64"), instruction, {
|
|
11
|
+
clientConfig: (0, api_worker_client_1.llmProxyOpenAIConfig)(),
|
|
12
|
+
});
|
|
10
13
|
return answer;
|
|
11
14
|
};
|
|
12
15
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@empiricalrun/playwright-utils",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.59.0",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"registry": "https://registry.npmjs.org/",
|
|
6
6
|
"access": "public"
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
"openapi-typescript": "7.6.1",
|
|
42
42
|
"playwright-core": "1.62.1",
|
|
43
43
|
"serve-handler": "^6.1.6",
|
|
44
|
-
"@empiricalrun/shared-types": "0.
|
|
44
|
+
"@empiricalrun/shared-types": "0.16.0"
|
|
45
45
|
},
|
|
46
46
|
"dependencies": {
|
|
47
47
|
"@babel/code-frame": "^7.24.7",
|
|
@@ -54,10 +54,10 @@
|
|
|
54
54
|
"puppeteer-extra-plugin-recaptcha": "^3.6.8",
|
|
55
55
|
"rimraf": "^6.0.1",
|
|
56
56
|
"ts-morph": "^23.0.0",
|
|
57
|
-
"@empiricalrun/cua": "^0.4.
|
|
58
|
-
"@empiricalrun/llm": "^0.27.
|
|
57
|
+
"@empiricalrun/cua": "^0.4.2",
|
|
58
|
+
"@empiricalrun/llm": "^0.27.1",
|
|
59
59
|
"@empiricalrun/r2-uploader": "^0.10.2",
|
|
60
|
-
"@empiricalrun/reporter": "^0.
|
|
60
|
+
"@empiricalrun/reporter": "^0.30.0"
|
|
61
61
|
},
|
|
62
62
|
"scripts": {
|
|
63
63
|
"dev": "pnpm gen:api-worker-openapi && tsgo --build --watch",
|