@browser_use/pi 0.1.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/.env.example +6 -0
- package/LICENSE +21 -0
- package/README.md +68 -0
- package/dist/agent.d.ts +15 -0
- package/dist/agent.js +381 -0
- package/dist/agent.js.map +1 -0
- package/dist/browser.d.ts +52 -0
- package/dist/browser.js +264 -0
- package/dist/browser.js.map +1 -0
- package/dist/cdp.d.ts +39 -0
- package/dist/cdp.js +291 -0
- package/dist/cdp.js.map +1 -0
- package/dist/context.d.ts +24 -0
- package/dist/context.js +143 -0
- package/dist/context.js.map +1 -0
- package/dist/control.d.ts +18 -0
- package/dist/control.js +84 -0
- package/dist/control.js.map +1 -0
- package/dist/events.d.ts +40 -0
- package/dist/events.js +79 -0
- package/dist/events.js.map +1 -0
- package/dist/highlight.d.ts +3 -0
- package/dist/highlight.js +102 -0
- package/dist/highlight.js.map +1 -0
- package/dist/history.d.ts +22 -0
- package/dist/history.js +126 -0
- package/dist/history.js.map +1 -0
- package/dist/images.d.ts +14 -0
- package/dist/images.js +104 -0
- package/dist/images.js.map +1 -0
- package/dist/index.d.ts +77 -0
- package/dist/index.js +422 -0
- package/dist/index.js.map +1 -0
- package/dist/model-stream.d.ts +3 -0
- package/dist/model-stream.js +78 -0
- package/dist/model-stream.js.map +1 -0
- package/dist/observer.d.ts +16 -0
- package/dist/observer.js +56 -0
- package/dist/observer.js.map +1 -0
- package/dist/page.d.ts +58 -0
- package/dist/page.js +189 -0
- package/dist/page.js.map +1 -0
- package/dist/policy.d.ts +18 -0
- package/dist/policy.js +195 -0
- package/dist/policy.js.map +1 -0
- package/dist/prompt.d.ts +1 -0
- package/dist/prompt.js +41 -0
- package/dist/prompt.js.map +1 -0
- package/dist/protocol.d.ts +70 -0
- package/dist/protocol.js +7 -0
- package/dist/protocol.js.map +1 -0
- package/dist/recording.d.ts +44 -0
- package/dist/recording.js +120 -0
- package/dist/recording.js.map +1 -0
- package/dist/research-tools.d.ts +3 -0
- package/dist/research-tools.js +67 -0
- package/dist/research-tools.js.map +1 -0
- package/dist/runtime.d.ts +39 -0
- package/dist/runtime.js +268 -0
- package/dist/runtime.js.map +1 -0
- package/dist/server.d.ts +2 -0
- package/dist/server.js +251 -0
- package/dist/server.js.map +1 -0
- package/dist/telemetry.d.ts +3 -0
- package/dist/telemetry.js +41 -0
- package/dist/telemetry.js.map +1 -0
- package/dist/types.d.ts +93 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/video.d.ts +18 -0
- package/dist/video.js +177 -0
- package/dist/video.js.map +1 -0
- package/dist/worker.d.ts +1 -0
- package/dist/worker.js +329 -0
- package/dist/worker.js.map +1 -0
- package/examples/README.md +58 -0
- package/examples/apply-to-job.ts +57 -0
- package/examples/ehr.ts +53 -0
- package/examples/extract.ts +50 -0
- package/examples/form.ts +36 -0
- package/examples/onepassword.ts +66 -0
- package/examples/qa.ts +72 -0
- package/examples/research.ts +35 -0
- package/examples/stripe-link.ts +166 -0
- package/package.json +66 -0
package/dist/runtime.js
ADDED
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
import { mkdir, readFile, writeFile } from 'node:fs/promises';
|
|
2
|
+
import { isAbsolute, join } from 'node:path';
|
|
3
|
+
import { randomUUID } from 'node:crypto';
|
|
4
|
+
import { promisify } from 'node:util';
|
|
5
|
+
import { CDP } from './cdp.js';
|
|
6
|
+
import { execFile, fork } from 'node:child_process';
|
|
7
|
+
import { positiveInteger } from './protocol.js';
|
|
8
|
+
/** Bun hosts use the same V8 worker as Node hosts, including its cancellation boundary. */
|
|
9
|
+
export async function workerExecutable() {
|
|
10
|
+
if (!process.versions.bun && !process.env.BROWSER_USE_NODE)
|
|
11
|
+
return process.execPath;
|
|
12
|
+
try {
|
|
13
|
+
const { stdout } = await promisify(execFile)(process.env.BROWSER_USE_NODE || 'node', [
|
|
14
|
+
'-p',
|
|
15
|
+
'JSON.stringify({path:process.execPath,node:process.versions.node,bun:!!process.versions.bun})',
|
|
16
|
+
], { env: { PATH: process.env.PATH ?? '' }, timeout: 5_000 });
|
|
17
|
+
const runtime = JSON.parse(stdout);
|
|
18
|
+
const [major, minor] = runtime.node.split('.').map(Number);
|
|
19
|
+
if (!runtime.bun && isAbsolute(runtime.path) && (major > 22 || (major === 22 && minor >= 19)))
|
|
20
|
+
return runtime.path;
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
// Do not expose subprocess output or inherit provider keys and preload flags.
|
|
24
|
+
}
|
|
25
|
+
throw new Error('Browser Use Pi needs Node.js 22.19+ for its JavaScript worker, including when your app runs in Bun. Install Node on PATH or set BROWSER_USE_NODE to its absolute executable path.');
|
|
26
|
+
}
|
|
27
|
+
export class CellError extends Error {
|
|
28
|
+
result;
|
|
29
|
+
stateReset;
|
|
30
|
+
constructor(message, result, stateReset) {
|
|
31
|
+
super(message);
|
|
32
|
+
this.result = result;
|
|
33
|
+
this.stateReset = stateReset;
|
|
34
|
+
this.name = 'CellError';
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
/** One worker, one active cell. Termination is the cancellation boundary. */
|
|
38
|
+
export class BrowserRuntime {
|
|
39
|
+
config;
|
|
40
|
+
executable;
|
|
41
|
+
onAction;
|
|
42
|
+
runId;
|
|
43
|
+
partial;
|
|
44
|
+
beginRun() {
|
|
45
|
+
this.runId = randomUUID();
|
|
46
|
+
this.partial = undefined;
|
|
47
|
+
}
|
|
48
|
+
get currentTarget() {
|
|
49
|
+
return this.targetId;
|
|
50
|
+
}
|
|
51
|
+
async initialize(signal) {
|
|
52
|
+
await this.execute('await page.info()', 4 * this.config.operationTimeoutMs, signal);
|
|
53
|
+
return this.targetId;
|
|
54
|
+
}
|
|
55
|
+
worker;
|
|
56
|
+
owned = new Set();
|
|
57
|
+
targetId;
|
|
58
|
+
busy = false;
|
|
59
|
+
workerLoss;
|
|
60
|
+
closed = false;
|
|
61
|
+
settled = Promise.resolve();
|
|
62
|
+
release;
|
|
63
|
+
pending;
|
|
64
|
+
constructor(config, executable) {
|
|
65
|
+
this.config = config;
|
|
66
|
+
this.executable = executable;
|
|
67
|
+
}
|
|
68
|
+
async start(signal) {
|
|
69
|
+
if (this.worker)
|
|
70
|
+
return this.worker;
|
|
71
|
+
const execPath = this.executable ?? (await workerExecutable());
|
|
72
|
+
const worker = fork(new URL('./worker.js', import.meta.url), [], {
|
|
73
|
+
execPath,
|
|
74
|
+
execArgv: ['--max-old-space-size=256'], // Never inherit host loaders/preloads/inspectors.
|
|
75
|
+
env: {}, // Provider keys stay in the agent process; this is not an OS sandbox.
|
|
76
|
+
stdio: ['ignore', 'ignore', 'ignore', 'ipc'],
|
|
77
|
+
serialization: 'json', // Node and Bun use different advanced IPC formats.
|
|
78
|
+
});
|
|
79
|
+
this.worker = worker;
|
|
80
|
+
worker.on('message', (message) => {
|
|
81
|
+
if (message.type === 'partial' && this.runId && message.runId === this.runId)
|
|
82
|
+
this.partial = { path: message.path, value: JSON.parse(message.valueJson) };
|
|
83
|
+
if (message.type === 'action')
|
|
84
|
+
this.onAction?.(message.action);
|
|
85
|
+
if (message.type === 'owned')
|
|
86
|
+
this.owned.add(message.targetId);
|
|
87
|
+
});
|
|
88
|
+
worker.on('error', (error) => this.pending?.(error));
|
|
89
|
+
worker.on('exit', (code, signal) => {
|
|
90
|
+
if (this.worker !== worker)
|
|
91
|
+
return;
|
|
92
|
+
this.worker = undefined;
|
|
93
|
+
const error = new Error(`Browser worker exited (${signal ?? code}). JavaScript state was reset; check browser state before retrying an action.`);
|
|
94
|
+
if (this.pending)
|
|
95
|
+
this.pending(error);
|
|
96
|
+
else
|
|
97
|
+
this.workerLoss = error;
|
|
98
|
+
});
|
|
99
|
+
try {
|
|
100
|
+
const response = this.receive(worker, 20_000, signal);
|
|
101
|
+
worker.send({ ...this.config, ...(this.targetId ? { targetId: this.targetId } : {}) }, (error) => {
|
|
102
|
+
if (error)
|
|
103
|
+
this.pending?.(error);
|
|
104
|
+
});
|
|
105
|
+
const ready = await response;
|
|
106
|
+
if (ready.type !== 'ready')
|
|
107
|
+
throw new Error('Browser worker did not initialize.');
|
|
108
|
+
this.targetId = ready.targetId;
|
|
109
|
+
return worker;
|
|
110
|
+
}
|
|
111
|
+
catch (error) {
|
|
112
|
+
await this.terminate();
|
|
113
|
+
throw error;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
receive(worker, timeoutMs, signal) {
|
|
117
|
+
return new Promise((resolve, reject) => {
|
|
118
|
+
const finish = (value) => {
|
|
119
|
+
clearTimeout(timer);
|
|
120
|
+
signal?.removeEventListener('abort', abort);
|
|
121
|
+
worker.off('message', message);
|
|
122
|
+
this.pending = undefined;
|
|
123
|
+
if (value instanceof Error)
|
|
124
|
+
reject(value);
|
|
125
|
+
else
|
|
126
|
+
resolve(value);
|
|
127
|
+
};
|
|
128
|
+
const message = (value) => {
|
|
129
|
+
if (value.type !== 'owned' && value.type !== 'action' && value.type !== 'partial')
|
|
130
|
+
finish(value);
|
|
131
|
+
};
|
|
132
|
+
const abort = () => finish(new Error('Execution cancelled. JavaScript state was reset; browser actions may already have happened.'));
|
|
133
|
+
const timer = setTimeout(() => finish(new Error(`Cell exceeded ${timeoutMs} ms. JavaScript state was reset; inspect the page before retrying actions.`)), timeoutMs);
|
|
134
|
+
this.pending = (error) => finish(error);
|
|
135
|
+
worker.on('message', message);
|
|
136
|
+
signal?.addEventListener('abort', abort, { once: true });
|
|
137
|
+
if (signal?.aborted)
|
|
138
|
+
abort();
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
async readResult(expression, timeoutMs = 30_000, signal) {
|
|
142
|
+
const result = await this.executeCell(expression, timeoutMs, signal, true);
|
|
143
|
+
if (result.valueJson === undefined)
|
|
144
|
+
throw new Error('Worker returned no result value.');
|
|
145
|
+
return JSON.parse(result.valueJson);
|
|
146
|
+
}
|
|
147
|
+
execute(code, timeoutMs = 30_000, signal) {
|
|
148
|
+
return this.executeCell(code, timeoutMs, signal, false);
|
|
149
|
+
}
|
|
150
|
+
async executeCell(code, timeoutMs, signal, captureJson) {
|
|
151
|
+
positiveInteger('timeoutMs', timeoutMs);
|
|
152
|
+
if (this.closed)
|
|
153
|
+
throw new Error('BrowserUse is closed.');
|
|
154
|
+
if (this.busy)
|
|
155
|
+
throw new Error('A browser cell is already running. Await it before starting another.');
|
|
156
|
+
if (signal?.aborted)
|
|
157
|
+
throw new Error('Execution cancelled.');
|
|
158
|
+
if (!code.trim())
|
|
159
|
+
throw new Error('Provide JavaScript code to execute.');
|
|
160
|
+
if (this.workerLoss) {
|
|
161
|
+
const error = this.workerLoss;
|
|
162
|
+
this.workerLoss = undefined;
|
|
163
|
+
// No new cell ran. Expose the same reset contract as a crash during execution.
|
|
164
|
+
throw new CellError(error.message, { text: '', images: [] }, true);
|
|
165
|
+
}
|
|
166
|
+
this.busy = true;
|
|
167
|
+
this.settled = new Promise((resolve) => {
|
|
168
|
+
this.release = resolve;
|
|
169
|
+
});
|
|
170
|
+
try {
|
|
171
|
+
const worker = await this.start(signal);
|
|
172
|
+
if (signal?.aborted)
|
|
173
|
+
throw new Error('Execution cancelled.');
|
|
174
|
+
const directory = join(this.config.workspace, '.browser-use', 'cells');
|
|
175
|
+
await mkdir(directory, { recursive: true, mode: 0o700 });
|
|
176
|
+
const outputFile = join(directory, `${randomUUID()}.txt`);
|
|
177
|
+
await writeFile(outputFile, '', { flag: 'wx', mode: 0o600 });
|
|
178
|
+
const response = this.receive(worker, timeoutMs, signal);
|
|
179
|
+
worker.send({ type: 'execute', code, captureJson, outputFile, runId: this.runId }, (error) => {
|
|
180
|
+
if (error)
|
|
181
|
+
this.pending?.(error);
|
|
182
|
+
});
|
|
183
|
+
let message;
|
|
184
|
+
try {
|
|
185
|
+
message = await response;
|
|
186
|
+
}
|
|
187
|
+
catch (error) {
|
|
188
|
+
await this.terminate();
|
|
189
|
+
const text = (await readFile(outputFile, 'utf8')).slice(0, this.config.maxOutputChars);
|
|
190
|
+
throw new CellError(String(error instanceof Error ? error.message : error), { text, images: [], outputFile }, true);
|
|
191
|
+
}
|
|
192
|
+
if ((message.type === 'result' || message.type === 'error') && message.result?.targetId)
|
|
193
|
+
this.targetId = message.result.targetId;
|
|
194
|
+
if (message.type === 'error')
|
|
195
|
+
throw new CellError(message.message, message.result ?? { text: '', images: [] }, false);
|
|
196
|
+
if (message.type !== 'result')
|
|
197
|
+
throw new Error('Unexpected browser worker response.');
|
|
198
|
+
return message.result;
|
|
199
|
+
}
|
|
200
|
+
finally {
|
|
201
|
+
this.busy = false;
|
|
202
|
+
this.release?.();
|
|
203
|
+
this.release = undefined;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
async terminate() {
|
|
207
|
+
const worker = this.worker;
|
|
208
|
+
this.worker = undefined;
|
|
209
|
+
if (worker && worker.exitCode === null && worker.signalCode === null) {
|
|
210
|
+
const exited = new Promise((resolve) => worker.once('exit', () => resolve()));
|
|
211
|
+
worker.kill('SIGKILL');
|
|
212
|
+
await exited;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
async close() {
|
|
216
|
+
if (this.closed)
|
|
217
|
+
return;
|
|
218
|
+
this.closed = true;
|
|
219
|
+
if (this.busy) {
|
|
220
|
+
this.pending?.(new Error('BrowserUse closed during execution.'));
|
|
221
|
+
await this.terminate();
|
|
222
|
+
await this.settled;
|
|
223
|
+
}
|
|
224
|
+
if (this.worker) {
|
|
225
|
+
const response = this.receive(this.worker, 2_000);
|
|
226
|
+
this.worker.send({ type: 'close' }, (error) => {
|
|
227
|
+
if (error)
|
|
228
|
+
this.pending?.(error);
|
|
229
|
+
});
|
|
230
|
+
await response.catch(() => { });
|
|
231
|
+
}
|
|
232
|
+
await this.terminate();
|
|
233
|
+
if (this.owned.size) {
|
|
234
|
+
const cdp = await CDP.connect(this.config.endpoint, this.config.operationTimeoutMs, this.config.approveConnection);
|
|
235
|
+
try {
|
|
236
|
+
const { targetInfos } = await cdp.send('Target.getTargets');
|
|
237
|
+
// Include popups recursively, but never a pre-existing caller tab.
|
|
238
|
+
let previous = -1;
|
|
239
|
+
while (previous !== this.owned.size) {
|
|
240
|
+
previous = this.owned.size;
|
|
241
|
+
for (const target of targetInfos)
|
|
242
|
+
if (target.openerId && this.owned.has(target.openerId))
|
|
243
|
+
this.owned.add(target.targetId);
|
|
244
|
+
}
|
|
245
|
+
for (const targetId of this.owned) {
|
|
246
|
+
if (targetInfos.some((t) => t.targetId === targetId))
|
|
247
|
+
await cdp.send('Target.closeTarget', { targetId });
|
|
248
|
+
}
|
|
249
|
+
// closeTarget acknowledges the request before Chrome necessarily removes the tab.
|
|
250
|
+
// Verify only; never replay closure or touch a caller-owned target.
|
|
251
|
+
const deadline = Date.now() + this.config.operationTimeoutMs;
|
|
252
|
+
for (;;) {
|
|
253
|
+
const { targetInfos: current } = await cdp.send('Target.getTargets');
|
|
254
|
+
const remaining = current.filter((target) => this.owned.has(target.targetId));
|
|
255
|
+
if (!remaining.length)
|
|
256
|
+
break;
|
|
257
|
+
if (Date.now() >= deadline)
|
|
258
|
+
throw new Error(`SDK-owned tabs remain after cleanup deadline: ${remaining.map((target) => target.targetId).join(', ')}`);
|
|
259
|
+
await new Promise((resolve) => setTimeout(resolve, Math.min(50, Math.max(0, deadline - Date.now()))));
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
finally {
|
|
263
|
+
cdp.close();
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
//# sourceMappingURL=runtime.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtime.js","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"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","sourcesContent":["import { mkdir, readFile, writeFile } from 'node:fs/promises';\nimport { isAbsolute, join } from 'node:path';\nimport { randomUUID } from 'node:crypto';\nimport { promisify } from 'node:util';\nimport { CDP } from './cdp.js';\nimport { execFile, fork, type ChildProcess } from 'node:child_process';\nimport type { BrowserAction, CellResult, WorkerConfig, WorkerResponse } from './protocol.js';\nimport { positiveInteger } from './protocol.js';\n\n/** Bun hosts use the same V8 worker as Node hosts, including its cancellation boundary. */\nexport async function workerExecutable(): Promise<string> {\n if (!process.versions.bun && !process.env.BROWSER_USE_NODE) return process.execPath;\n try {\n const { stdout } = await promisify(execFile)(\n process.env.BROWSER_USE_NODE || 'node',\n [\n '-p',\n 'JSON.stringify({path:process.execPath,node:process.versions.node,bun:!!process.versions.bun})',\n ],\n { env: { PATH: process.env.PATH ?? '' }, timeout: 5_000 },\n );\n const runtime = JSON.parse(stdout);\n const [major, minor] = runtime.node.split('.').map(Number);\n if (!runtime.bun && isAbsolute(runtime.path) && (major > 22 || (major === 22 && minor >= 19)))\n return runtime.path;\n } catch {\n // Do not expose subprocess output or inherit provider keys and preload flags.\n }\n throw new Error(\n 'Browser Use Pi needs Node.js 22.19+ for its JavaScript worker, including when your app runs in Bun. Install Node on PATH or set BROWSER_USE_NODE to its absolute executable path.',\n );\n}\n\nexport class CellError extends Error {\n constructor(\n message: string,\n readonly result: CellResult,\n readonly stateReset: boolean,\n ) {\n super(message);\n this.name = 'CellError';\n }\n}\n\n/** One worker, one active cell. Termination is the cancellation boundary. */\nexport class BrowserRuntime {\n onAction: ((event: BrowserAction) => void) | undefined;\n private runId: string | undefined;\n partial: { path: string; value: unknown } | undefined;\n beginRun() {\n this.runId = randomUUID();\n this.partial = undefined;\n }\n get currentTarget() {\n return this.targetId;\n }\n async initialize(signal?: AbortSignal) {\n await this.execute('await page.info()', 4 * this.config.operationTimeoutMs, signal);\n return this.targetId!;\n }\n private worker: ChildProcess | undefined;\n private owned = new Set<string>();\n private targetId: string | undefined;\n private busy = false;\n private workerLoss: Error | undefined;\n private closed = false;\n private settled: Promise<void> = Promise.resolve();\n private release: (() => void) | undefined;\n private pending: ((error: Error) => void) | undefined;\n\n constructor(\n private readonly config: WorkerConfig,\n private readonly executable?: string,\n ) {}\n\n private async start(signal?: AbortSignal): Promise<ChildProcess> {\n if (this.worker) return this.worker;\n const execPath = this.executable ?? (await workerExecutable());\n const worker = fork(new URL('./worker.js', import.meta.url), [], {\n execPath,\n execArgv: ['--max-old-space-size=256'], // Never inherit host loaders/preloads/inspectors.\n env: {}, // Provider keys stay in the agent process; this is not an OS sandbox.\n stdio: ['ignore', 'ignore', 'ignore', 'ipc'],\n serialization: 'json', // Node and Bun use different advanced IPC formats.\n });\n this.worker = worker;\n worker.on('message', (message: WorkerResponse) => {\n if (message.type === 'partial' && this.runId && message.runId === this.runId)\n this.partial = { path: message.path, value: JSON.parse(message.valueJson) };\n if (message.type === 'action') this.onAction?.(message.action);\n if (message.type === 'owned') this.owned.add(message.targetId);\n });\n worker.on('error', (error) => this.pending?.(error));\n worker.on('exit', (code, signal) => {\n if (this.worker !== worker) return;\n this.worker = undefined;\n const error = new Error(\n `Browser worker exited (${signal ?? code}). JavaScript state was reset; check browser state before retrying an action.`,\n );\n if (this.pending) this.pending(error);\n else this.workerLoss = error;\n });\n try {\n const response = this.receive(worker, 20_000, signal);\n worker.send(\n { ...this.config, ...(this.targetId ? { targetId: this.targetId } : {}) },\n (error) => {\n if (error) this.pending?.(error);\n },\n );\n const ready = await response;\n if (ready.type !== 'ready') throw new Error('Browser worker did not initialize.');\n this.targetId = ready.targetId;\n return worker;\n } catch (error) {\n await this.terminate();\n throw error;\n }\n }\n\n private receive(\n worker: ChildProcess,\n timeoutMs: number,\n signal?: AbortSignal,\n ): Promise<WorkerResponse> {\n return new Promise((resolve, reject) => {\n const finish = (value: WorkerResponse | Error) => {\n clearTimeout(timer);\n signal?.removeEventListener('abort', abort);\n worker.off('message', message);\n this.pending = undefined;\n if (value instanceof Error) reject(value);\n else resolve(value);\n };\n const message = (value: WorkerResponse) => {\n if (value.type !== 'owned' && value.type !== 'action' && value.type !== 'partial')\n finish(value);\n };\n const abort = () =>\n finish(\n new Error(\n 'Execution cancelled. JavaScript state was reset; browser actions may already have happened.',\n ),\n );\n const timer = setTimeout(\n () =>\n finish(\n new Error(\n `Cell exceeded ${timeoutMs} ms. JavaScript state was reset; inspect the page before retrying actions.`,\n ),\n ),\n timeoutMs,\n );\n this.pending = (error) => finish(error);\n worker.on('message', message);\n signal?.addEventListener('abort', abort, { once: true });\n if (signal?.aborted) abort();\n });\n }\n\n async readResult(expression: string, timeoutMs = 30_000, signal?: AbortSignal): Promise<unknown> {\n const result = await this.executeCell(expression, timeoutMs, signal, true);\n if (result.valueJson === undefined) throw new Error('Worker returned no result value.');\n return JSON.parse(result.valueJson);\n }\n\n execute(code: string, timeoutMs = 30_000, signal?: AbortSignal): Promise<CellResult> {\n return this.executeCell(code, timeoutMs, signal, false);\n }\n\n private async executeCell(\n code: string,\n timeoutMs: number,\n signal: AbortSignal | undefined,\n captureJson: boolean,\n ): Promise<CellResult> {\n positiveInteger('timeoutMs', timeoutMs);\n if (this.closed) throw new Error('BrowserUse is closed.');\n if (this.busy)\n throw new Error('A browser cell is already running. Await it before starting another.');\n if (signal?.aborted) throw new Error('Execution cancelled.');\n if (!code.trim()) throw new Error('Provide JavaScript code to execute.');\n if (this.workerLoss) {\n const error = this.workerLoss;\n this.workerLoss = undefined;\n // No new cell ran. Expose the same reset contract as a crash during execution.\n throw new CellError(error.message, { text: '', images: [] }, true);\n }\n this.busy = true;\n this.settled = new Promise((resolve) => {\n this.release = resolve;\n });\n try {\n const worker = await this.start(signal);\n if (signal?.aborted) throw new Error('Execution cancelled.');\n const directory = join(this.config.workspace, '.browser-use', 'cells');\n await mkdir(directory, { recursive: true, mode: 0o700 });\n const outputFile = join(directory, `${randomUUID()}.txt`);\n await writeFile(outputFile, '', { flag: 'wx', mode: 0o600 });\n const response = this.receive(worker, timeoutMs, signal);\n worker.send(\n { type: 'execute', code, captureJson, outputFile, runId: this.runId },\n (error) => {\n if (error) this.pending?.(error);\n },\n );\n let message: WorkerResponse;\n try {\n message = await response;\n } catch (error) {\n await this.terminate();\n const text = (await readFile(outputFile, 'utf8')).slice(0, this.config.maxOutputChars);\n throw new CellError(\n String(error instanceof Error ? error.message : error),\n { text, images: [], outputFile },\n true,\n );\n }\n if ((message.type === 'result' || message.type === 'error') && message.result?.targetId)\n this.targetId = message.result.targetId;\n if (message.type === 'error')\n throw new CellError(message.message, message.result ?? { text: '', images: [] }, false);\n if (message.type !== 'result') throw new Error('Unexpected browser worker response.');\n return message.result;\n } finally {\n this.busy = false;\n this.release?.();\n this.release = undefined;\n }\n }\n\n private async terminate() {\n const worker = this.worker;\n this.worker = undefined;\n if (worker && worker.exitCode === null && worker.signalCode === null) {\n const exited = new Promise<void>((resolve) => worker.once('exit', () => resolve()));\n worker.kill('SIGKILL');\n await exited;\n }\n }\n\n async close() {\n if (this.closed) return;\n this.closed = true;\n if (this.busy) {\n this.pending?.(new Error('BrowserUse closed during execution.'));\n await this.terminate();\n await this.settled;\n }\n if (this.worker) {\n const response = this.receive(this.worker, 2_000);\n this.worker.send({ type: 'close' }, (error) => {\n if (error) this.pending?.(error);\n });\n await response.catch(() => {});\n }\n await this.terminate();\n if (this.owned.size) {\n const cdp = await CDP.connect(\n this.config.endpoint,\n this.config.operationTimeoutMs,\n this.config.approveConnection,\n );\n try {\n const { targetInfos } = await cdp.send('Target.getTargets');\n // Include popups recursively, but never a pre-existing caller tab.\n let previous = -1;\n while (previous !== this.owned.size) {\n previous = this.owned.size;\n for (const target of targetInfos)\n if (target.openerId && this.owned.has(target.openerId)) this.owned.add(target.targetId);\n }\n for (const targetId of this.owned) {\n if (targetInfos.some((t) => t.targetId === targetId))\n await cdp.send('Target.closeTarget', { targetId });\n }\n // closeTarget acknowledges the request before Chrome necessarily removes the tab.\n // Verify only; never replay closure or touch a caller-owned target.\n const deadline = Date.now() + this.config.operationTimeoutMs;\n for (;;) {\n const { targetInfos: current } = await cdp.send('Target.getTargets');\n const remaining = current.filter((target) => this.owned.has(target.targetId));\n if (!remaining.length) break;\n if (Date.now() >= deadline)\n throw new Error(\n `SDK-owned tabs remain after cleanup deadline: ${remaining.map((target) => target.targetId).join(', ')}`,\n );\n await new Promise((resolve) =>\n setTimeout(resolve, Math.min(50, Math.max(0, deadline - Date.now()))),\n );\n }\n } finally {\n cdp.close();\n }\n }\n }\n}\n"]}
|
package/dist/server.d.ts
ADDED
package/dist/server.js
ADDED
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/** Private versioned stdio bridge. stdout is protocol-only; no shell or HTTP server. */
|
|
3
|
+
import { BrowserUse, exportRecording } from './index.js';
|
|
4
|
+
import { builtinModels } from '@earendil-works/pi-ai/providers/all';
|
|
5
|
+
import { Type } from 'typebox';
|
|
6
|
+
let agent;
|
|
7
|
+
let creating = false;
|
|
8
|
+
let closing = false;
|
|
9
|
+
let nextTool = 0;
|
|
10
|
+
let queuedBytes = 0;
|
|
11
|
+
const toolCalls = new Map();
|
|
12
|
+
function send(value) {
|
|
13
|
+
const line = JSON.stringify(value) + '\n';
|
|
14
|
+
if (line.length > 16000000 || queuedBytes + line.length > 32000000)
|
|
15
|
+
return Promise.reject(new Error('Bridge output exceeded 32 MB; consumer must drain events.'));
|
|
16
|
+
queuedBytes += line.length;
|
|
17
|
+
return new Promise((resolve, reject) => process.stdout.write(line, (error) => {
|
|
18
|
+
queuedBytes -= line.length;
|
|
19
|
+
error ? reject(error) : resolve();
|
|
20
|
+
}));
|
|
21
|
+
}
|
|
22
|
+
function invokePython(name, args, signal) {
|
|
23
|
+
const id = `tool-${++nextTool}`;
|
|
24
|
+
return new Promise((resolve, reject) => {
|
|
25
|
+
let settled = false;
|
|
26
|
+
const finish = (value, error) => {
|
|
27
|
+
if (settled)
|
|
28
|
+
return;
|
|
29
|
+
settled = true;
|
|
30
|
+
clearTimeout(timer);
|
|
31
|
+
signal?.removeEventListener('abort', abort);
|
|
32
|
+
toolCalls.delete(id);
|
|
33
|
+
error ? reject(error) : resolve(value);
|
|
34
|
+
};
|
|
35
|
+
const abort = () => {
|
|
36
|
+
void send({ method: 'tool_cancel', params: { id } }).catch(() => { });
|
|
37
|
+
finish(undefined, new Error('Python tool cancelled.'));
|
|
38
|
+
};
|
|
39
|
+
const timer = setTimeout(abort, 120000);
|
|
40
|
+
toolCalls.set(id, {
|
|
41
|
+
resolve: (value) => finish(value),
|
|
42
|
+
reject: (error) => finish(undefined, error),
|
|
43
|
+
});
|
|
44
|
+
signal?.addEventListener('abort', abort, { once: true });
|
|
45
|
+
if (signal?.aborted) {
|
|
46
|
+
abort();
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
void send({ method: 'tool_call', params: { id, name, args } }).catch((error) => finish(undefined, error));
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
const CREATE_KEYS = new Set([
|
|
53
|
+
'model',
|
|
54
|
+
'browser',
|
|
55
|
+
'workspace',
|
|
56
|
+
'reasoning',
|
|
57
|
+
'instructions',
|
|
58
|
+
'operationTimeoutMs',
|
|
59
|
+
'cellTimeoutMs',
|
|
60
|
+
'modelTimeoutMs',
|
|
61
|
+
'compactionTimeoutMs',
|
|
62
|
+
'maxOutputChars',
|
|
63
|
+
'hookTimeoutMs',
|
|
64
|
+
'redact',
|
|
65
|
+
'log',
|
|
66
|
+
'historyFile',
|
|
67
|
+
'recording',
|
|
68
|
+
'highlightActions',
|
|
69
|
+
'allowedDomains',
|
|
70
|
+
'prohibitedDomains',
|
|
71
|
+
'sensitiveData',
|
|
72
|
+
'telemetry',
|
|
73
|
+
'researchTools',
|
|
74
|
+
'tools',
|
|
75
|
+
'apiKey',
|
|
76
|
+
'baseUrl',
|
|
77
|
+
]);
|
|
78
|
+
async function dispatch(method, params) {
|
|
79
|
+
if (method === 'ping')
|
|
80
|
+
return { protocol: 1, node: process.versions.node };
|
|
81
|
+
if (method === 'tool_result') {
|
|
82
|
+
const pending = toolCalls.get(String(params.id));
|
|
83
|
+
if (!pending)
|
|
84
|
+
return null; // Late cancelled callback; never reexecute it.
|
|
85
|
+
if (typeof params.error === 'string')
|
|
86
|
+
pending.reject(new Error(params.error));
|
|
87
|
+
else
|
|
88
|
+
pending.resolve({
|
|
89
|
+
content: [
|
|
90
|
+
{
|
|
91
|
+
type: 'text',
|
|
92
|
+
text: typeof params.result === 'string'
|
|
93
|
+
? params.result
|
|
94
|
+
: JSON.stringify(params.result ?? null),
|
|
95
|
+
},
|
|
96
|
+
],
|
|
97
|
+
details: params.result,
|
|
98
|
+
});
|
|
99
|
+
return null;
|
|
100
|
+
}
|
|
101
|
+
if (method === 'create') {
|
|
102
|
+
if (agent || creating || closing)
|
|
103
|
+
throw new Error('One session per bridge process.');
|
|
104
|
+
for (const key of Object.keys(params))
|
|
105
|
+
if (!CREATE_KEYS.has(key))
|
|
106
|
+
throw new Error(`Unsupported create option: ${key}`);
|
|
107
|
+
creating = true;
|
|
108
|
+
try {
|
|
109
|
+
if (typeof params.model !== 'string')
|
|
110
|
+
throw new Error('model must be provider/model.');
|
|
111
|
+
const { apiKey, baseUrl, tools: rawTools, ...options } = params;
|
|
112
|
+
if (apiKey !== undefined && typeof apiKey !== 'string')
|
|
113
|
+
throw new Error('apiKey must be a string.');
|
|
114
|
+
if (baseUrl !== undefined && typeof baseUrl !== 'string')
|
|
115
|
+
throw new Error('baseUrl must be a string.');
|
|
116
|
+
const models = builtinModels();
|
|
117
|
+
const toolSpecs = (rawTools ?? []);
|
|
118
|
+
if (!Array.isArray(toolSpecs) ||
|
|
119
|
+
toolSpecs.some((t) => typeof t.name !== 'string' ||
|
|
120
|
+
typeof t.description !== 'string' ||
|
|
121
|
+
!t.parameters ||
|
|
122
|
+
typeof t.parameters !== 'object'))
|
|
123
|
+
throw new Error('Invalid Python tool specifications.');
|
|
124
|
+
agent = await BrowserUse.create({
|
|
125
|
+
...options,
|
|
126
|
+
tools: toolSpecs.map((tool) => ({
|
|
127
|
+
...tool,
|
|
128
|
+
label: tool.name,
|
|
129
|
+
parameters: Type.Unsafe(tool.parameters),
|
|
130
|
+
executionMode: 'sequential',
|
|
131
|
+
execute: async (_id, args, signal) => invokePython(tool.name, args, signal),
|
|
132
|
+
})),
|
|
133
|
+
streamFn: (model, context, settings) => models.streamSimple(typeof baseUrl === 'string' ? { ...model, baseUrl } : model, context, { ...settings, ...(typeof apiKey === 'string' ? { apiKey } : {}) }),
|
|
134
|
+
});
|
|
135
|
+
const current = agent;
|
|
136
|
+
void (async () => {
|
|
137
|
+
for await (const event of current.events())
|
|
138
|
+
await send({ method: 'event', params: event });
|
|
139
|
+
})().catch((error) => {
|
|
140
|
+
current.cancel();
|
|
141
|
+
void send({ method: 'stream_error', params: { message: String(error) } }).catch(() => { });
|
|
142
|
+
});
|
|
143
|
+
return { workspace: agent.workspace };
|
|
144
|
+
}
|
|
145
|
+
finally {
|
|
146
|
+
creating = false;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
if (!agent)
|
|
150
|
+
throw new Error('Create a session first.');
|
|
151
|
+
switch (method) {
|
|
152
|
+
case 'run':
|
|
153
|
+
case 'followUp': {
|
|
154
|
+
if (typeof params.task !== 'string')
|
|
155
|
+
throw new Error('task must be a string.');
|
|
156
|
+
const options = (params.options ?? {});
|
|
157
|
+
for (const key of Object.keys(options))
|
|
158
|
+
if (![
|
|
159
|
+
'maxSteps',
|
|
160
|
+
'timeoutMs',
|
|
161
|
+
'maxCostUsd',
|
|
162
|
+
'maxContextChars',
|
|
163
|
+
'compaction',
|
|
164
|
+
'schema',
|
|
165
|
+
].includes(key))
|
|
166
|
+
throw new Error(`Unsupported run option: ${key}`);
|
|
167
|
+
return method === 'run'
|
|
168
|
+
? agent.run(params.task, options)
|
|
169
|
+
: agent.followUp(params.task, options);
|
|
170
|
+
}
|
|
171
|
+
case 'execute':
|
|
172
|
+
if (typeof params.code !== 'string')
|
|
173
|
+
throw new Error('code must be a string.');
|
|
174
|
+
return agent.execute(params.code, (params.options ?? {}));
|
|
175
|
+
case 'pause':
|
|
176
|
+
await agent.pause();
|
|
177
|
+
return { paused: agent.isPaused };
|
|
178
|
+
case 'resume':
|
|
179
|
+
await agent.resume();
|
|
180
|
+
return null;
|
|
181
|
+
case 'steer':
|
|
182
|
+
if (typeof params.text !== 'string')
|
|
183
|
+
throw new Error('text must be a string.');
|
|
184
|
+
agent.steer(params.text);
|
|
185
|
+
return null;
|
|
186
|
+
case 'cancel':
|
|
187
|
+
agent.cancel();
|
|
188
|
+
return null;
|
|
189
|
+
case 'files':
|
|
190
|
+
return agent.files();
|
|
191
|
+
case 'history':
|
|
192
|
+
return agent.history;
|
|
193
|
+
case 'saveHistory':
|
|
194
|
+
return agent.saveHistory(typeof params.path === 'string' ? params.path : undefined);
|
|
195
|
+
case 'exportRecording':
|
|
196
|
+
return exportRecording(String(params.path), params.options);
|
|
197
|
+
case 'close':
|
|
198
|
+
closing = true;
|
|
199
|
+
await agent.close();
|
|
200
|
+
return null;
|
|
201
|
+
default:
|
|
202
|
+
throw new Error(`Unknown bridge method: ${method}`);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
async function handle(line) {
|
|
206
|
+
let id;
|
|
207
|
+
try {
|
|
208
|
+
const request = JSON.parse(line);
|
|
209
|
+
id = request.id;
|
|
210
|
+
if (!['string', 'number'].includes(typeof id) ||
|
|
211
|
+
typeof request.method !== 'string' ||
|
|
212
|
+
(request.params && (typeof request.params !== 'object' || Array.isArray(request.params))))
|
|
213
|
+
throw new Error('Invalid RPC request.');
|
|
214
|
+
const result = await dispatch(request.method, request.params ?? {});
|
|
215
|
+
await send({ id, result });
|
|
216
|
+
}
|
|
217
|
+
catch (error) {
|
|
218
|
+
await send({
|
|
219
|
+
id: id ?? null,
|
|
220
|
+
error: { message: error instanceof Error ? error.message : String(error) },
|
|
221
|
+
}).catch(() => { });
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
let buffer = '';
|
|
225
|
+
process.stdin.setEncoding('utf8');
|
|
226
|
+
process.stdin.on('data', (chunk) => {
|
|
227
|
+
buffer += chunk;
|
|
228
|
+
if (buffer.length > 16000000) {
|
|
229
|
+
process.stdin.destroy();
|
|
230
|
+
agent?.cancel();
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
let end;
|
|
234
|
+
while ((end = buffer.indexOf('\n')) >= 0) {
|
|
235
|
+
const line = buffer.slice(0, end);
|
|
236
|
+
buffer = buffer.slice(end + 1);
|
|
237
|
+
if (line.trim())
|
|
238
|
+
void handle(line);
|
|
239
|
+
}
|
|
240
|
+
});
|
|
241
|
+
process.stdin.on('end', () => {
|
|
242
|
+
void agent?.close().finally(() => process.exit(0));
|
|
243
|
+
if (!agent)
|
|
244
|
+
process.exit(0);
|
|
245
|
+
});
|
|
246
|
+
process.on('SIGTERM', () => {
|
|
247
|
+
void agent?.close().finally(() => process.exit(0));
|
|
248
|
+
if (!agent)
|
|
249
|
+
process.exit(0);
|
|
250
|
+
});
|
|
251
|
+
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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","sourcesContent":["#!/usr/bin/env node\n/** Private versioned stdio bridge. stdout is protocol-only; no shell or HTTP server. */\nimport { BrowserUse, exportRecording, type BrowserUseOptions } from './index.js';\nimport { builtinModels } from '@earendil-works/pi-ai/providers/all';\nimport { Type, type TSchema } from 'typebox';\nimport type { AgentToolResult } from '@earendil-works/pi-agent-core';\n\nlet agent: BrowserUse | undefined;\nlet creating = false;\nlet closing = false;\nlet nextTool = 0;\nlet queuedBytes = 0;\nconst toolCalls = new Map<\n string,\n { resolve: (value: AgentToolResult<unknown>) => void; reject: (error: Error) => void }\n>();\n\nfunction send(value: unknown): Promise<void> {\n const line = JSON.stringify(value) + '\\n';\n if (line.length > 16000000 || queuedBytes + line.length > 32000000)\n return Promise.reject(new Error('Bridge output exceeded 32 MB; consumer must drain events.'));\n queuedBytes += line.length;\n return new Promise((resolve, reject) =>\n process.stdout.write(line, (error) => {\n queuedBytes -= line.length;\n error ? reject(error) : resolve();\n }),\n );\n}\n\nfunction invokePython(\n name: string,\n args: unknown,\n signal?: AbortSignal,\n): Promise<AgentToolResult<unknown>> {\n const id = `tool-${++nextTool}`;\n return new Promise((resolve, reject) => {\n let settled = false;\n const finish = (value?: AgentToolResult<unknown>, error?: Error) => {\n if (settled) return;\n settled = true;\n clearTimeout(timer);\n signal?.removeEventListener('abort', abort);\n toolCalls.delete(id);\n error ? reject(error) : resolve(value!);\n };\n const abort = () => {\n void send({ method: 'tool_cancel', params: { id } }).catch(() => {});\n finish(undefined, new Error('Python tool cancelled.'));\n };\n const timer = setTimeout(abort, 120000);\n toolCalls.set(id, {\n resolve: (value) => finish(value),\n reject: (error) => finish(undefined, error),\n });\n signal?.addEventListener('abort', abort, { once: true });\n if (signal?.aborted) {\n abort();\n return;\n }\n void send({ method: 'tool_call', params: { id, name, args } }).catch((error) =>\n finish(undefined, error),\n );\n });\n}\n\nconst CREATE_KEYS = new Set([\n 'model',\n 'browser',\n 'workspace',\n 'reasoning',\n 'instructions',\n 'operationTimeoutMs',\n 'cellTimeoutMs',\n 'modelTimeoutMs',\n 'compactionTimeoutMs',\n 'maxOutputChars',\n 'hookTimeoutMs',\n 'redact',\n 'log',\n 'historyFile',\n 'recording',\n 'highlightActions',\n 'allowedDomains',\n 'prohibitedDomains',\n 'sensitiveData',\n 'telemetry',\n 'researchTools',\n 'tools',\n 'apiKey',\n 'baseUrl',\n]);\nasync function dispatch(method: string, params: Record<string, unknown>): Promise<unknown> {\n if (method === 'ping') return { protocol: 1, node: process.versions.node };\n if (method === 'tool_result') {\n const pending = toolCalls.get(String(params.id));\n if (!pending) return null; // Late cancelled callback; never reexecute it.\n if (typeof params.error === 'string') pending.reject(new Error(params.error));\n else\n pending.resolve({\n content: [\n {\n type: 'text',\n text:\n typeof params.result === 'string'\n ? params.result\n : JSON.stringify(params.result ?? null),\n },\n ],\n details: params.result,\n });\n return null;\n }\n if (method === 'create') {\n if (agent || creating || closing) throw new Error('One session per bridge process.');\n for (const key of Object.keys(params))\n if (!CREATE_KEYS.has(key)) throw new Error(`Unsupported create option: ${key}`);\n creating = true;\n try {\n if (typeof params.model !== 'string') throw new Error('model must be provider/model.');\n const { apiKey, baseUrl, tools: rawTools, ...options } = params;\n if (apiKey !== undefined && typeof apiKey !== 'string')\n throw new Error('apiKey must be a string.');\n if (baseUrl !== undefined && typeof baseUrl !== 'string')\n throw new Error('baseUrl must be a string.');\n const models = builtinModels();\n const toolSpecs = (rawTools ?? []) as {\n name: string;\n description: string;\n parameters: TSchema;\n }[];\n if (\n !Array.isArray(toolSpecs) ||\n toolSpecs.some(\n (t) =>\n typeof t.name !== 'string' ||\n typeof t.description !== 'string' ||\n !t.parameters ||\n typeof t.parameters !== 'object',\n )\n )\n throw new Error('Invalid Python tool specifications.');\n agent = await BrowserUse.create({\n ...(options as unknown as BrowserUseOptions),\n tools: toolSpecs.map((tool) => ({\n ...tool,\n label: tool.name,\n parameters: Type.Unsafe(tool.parameters),\n executionMode: 'sequential' as const,\n execute: async (_id: string, args: unknown, signal?: AbortSignal) =>\n invokePython(tool.name, args, signal),\n })),\n streamFn: (model, context, settings) =>\n models.streamSimple(\n typeof baseUrl === 'string' ? { ...model, baseUrl } : model,\n context,\n { ...settings, ...(typeof apiKey === 'string' ? { apiKey } : {}) },\n ),\n });\n const current = agent;\n void (async () => {\n for await (const event of current.events()) await send({ method: 'event', params: event });\n })().catch((error) => {\n current.cancel();\n void send({ method: 'stream_error', params: { message: String(error) } }).catch(() => {});\n });\n return { workspace: agent.workspace };\n } finally {\n creating = false;\n }\n }\n if (!agent) throw new Error('Create a session first.');\n switch (method) {\n case 'run':\n case 'followUp': {\n if (typeof params.task !== 'string') throw new Error('task must be a string.');\n const options = (params.options ?? {}) as Record<string, unknown>;\n for (const key of Object.keys(options))\n if (\n ![\n 'maxSteps',\n 'timeoutMs',\n 'maxCostUsd',\n 'maxContextChars',\n 'compaction',\n 'schema',\n ].includes(key)\n )\n throw new Error(`Unsupported run option: ${key}`);\n return method === 'run'\n ? agent.run(params.task, options)\n : agent.followUp(params.task, options);\n }\n case 'execute':\n if (typeof params.code !== 'string') throw new Error('code must be a string.');\n return agent.execute(params.code, (params.options ?? {}) as { timeoutMs?: number });\n case 'pause':\n await agent.pause();\n return { paused: agent.isPaused };\n case 'resume':\n await agent.resume();\n return null;\n case 'steer':\n if (typeof params.text !== 'string') throw new Error('text must be a string.');\n agent.steer(params.text);\n return null;\n case 'cancel':\n agent.cancel();\n return null;\n case 'files':\n return agent.files();\n case 'history':\n return agent.history;\n case 'saveHistory':\n return agent.saveHistory(typeof params.path === 'string' ? params.path : undefined);\n case 'exportRecording':\n return exportRecording(\n String(params.path),\n params.options as Parameters<typeof exportRecording>[1],\n );\n case 'close':\n closing = true;\n await agent.close();\n return null;\n default:\n throw new Error(`Unknown bridge method: ${method}`);\n }\n}\n\nasync function handle(line: string) {\n let id: string | number | undefined;\n try {\n const request = JSON.parse(line);\n id = request.id;\n if (\n !['string', 'number'].includes(typeof id) ||\n typeof request.method !== 'string' ||\n (request.params && (typeof request.params !== 'object' || Array.isArray(request.params)))\n )\n throw new Error('Invalid RPC request.');\n const result = await dispatch(request.method, request.params ?? {});\n await send({ id, result });\n } catch (error) {\n await send({\n id: id ?? null,\n error: { message: error instanceof Error ? error.message : String(error) },\n }).catch(() => {});\n }\n}\nlet buffer = '';\nprocess.stdin.setEncoding('utf8');\nprocess.stdin.on('data', (chunk: string) => {\n buffer += chunk;\n if (buffer.length > 16000000) {\n process.stdin.destroy();\n agent?.cancel();\n return;\n }\n let end: number;\n while ((end = buffer.indexOf('\\n')) >= 0) {\n const line = buffer.slice(0, end);\n buffer = buffer.slice(end + 1);\n if (line.trim()) void handle(line);\n }\n});\nprocess.stdin.on('end', () => {\n void agent?.close().finally(() => process.exit(0));\n if (!agent) process.exit(0);\n});\nprocess.on('SIGTERM', () => {\n void agent?.close().finally(() => process.exit(0));\n if (!agent) process.exit(0);\n});\n"]}
|