@ricsam/r5d-browser 0.0.66 → 0.0.68
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -0
- package/dist/cjs/browser-runtime.cjs +39 -7
- package/dist/cjs/main.cjs +1 -1
- package/dist/cjs/package.json +1 -1
- package/dist/mjs/browser-runtime.mjs +39 -7
- package/dist/mjs/main.mjs +1 -1
- package/dist/mjs/package.json +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -22,6 +22,8 @@ r5d-browser start --chrome-path "/Applications/Google Chrome.app/Contents/MacOS/
|
|
|
22
22
|
|
|
23
23
|
The profile is stored under `~/.r5d/browser/chrome-profile`. The previous Chrome for Testing profile at `~/.r5d/browser/profile` is preserved but is not migrated automatically, so sign in again the first time you use the new profile. Browser binaries previously installed under `~/.r5d/browser/browsers` are also left untouched, but new versions of `r5d-browser` do not use them. Completed downloads are retained under `~/.r5d/browser/downloads`; agents can list them and copy a selected file into the active session's artifacts. That artifact is then synchronized to every connected worker. Browser screenshots are session artifacts in r5d.dev and are not retained by the browser process.
|
|
24
24
|
|
|
25
|
+
Agents running JavaScript in a page can save captured text straight into the active session's artifacts by calling `r5dCreateArtifact(filename, content)`, which takes two strings and must be called before the agent's code returns. Each captured file is written to the session artifacts in r5d.dev, synchronized to every connected worker, and reported back as a `$R5D_ARTIFACTS_DIR` path; a single call may create at most 32 files totalling 4 MiB. The function is passed into the agent's code rather than assigned to `window`, so pages can neither call it nor use it to detect automation.
|
|
26
|
+
|
|
25
27
|
Agents can also create loopback-only TCP port forwards from this Mac to the worker bound to a chat. For example, a forward can map Chrome's `localhost:4323` to `127.0.0.1:3232` on that worker. The relay preserves raw TCP traffic, including HTTP, HTTPS, WebSockets, HMR, and SSE; it never exposes a public listener or permits an arbitrary target host.
|
|
26
28
|
|
|
27
29
|
Port forwards remain active across agent turns and r5d.dev control-connection reconnects. They are owned by the running `r5d-browser` process, so restarting that process ends them. Agents can list and stop forwards, and the user can inspect or stop them from Settings.
|
|
@@ -34,6 +34,9 @@ module.exports = __toCommonJS(browser_runtime_exports);
|
|
|
34
34
|
var import_node_fs = __toESM(require("node:fs"), 1);
|
|
35
35
|
var import_node_path = __toESM(require("node:path"), 1);
|
|
36
36
|
const MAX_DOWNLOAD_CHUNK_BYTES = 1024 * 1024;
|
|
37
|
+
const MAX_RUN_JS_RESULT_BYTES = 1024 * 1024;
|
|
38
|
+
const MAX_RUN_JS_ARTIFACT_BYTES = 4 * 1024 * 1024;
|
|
39
|
+
const MAX_RUN_JS_ARTIFACT_COUNT = 32;
|
|
37
40
|
class BrowserRuntime {
|
|
38
41
|
constructor(context, downloadsPath) {
|
|
39
42
|
this.context = context;
|
|
@@ -272,17 +275,46 @@ class BrowserRuntime {
|
|
|
272
275
|
return { tabId, characters: input.text.length };
|
|
273
276
|
case "run_js": {
|
|
274
277
|
if (typeof input.code !== "string") throw new Error("run_js requires code.");
|
|
275
|
-
const
|
|
276
|
-
|
|
278
|
+
const evaluated = await page.evaluate(
|
|
279
|
+
async ({ code, maxBytes, maxCount }) => {
|
|
280
|
+
const artifacts = [];
|
|
281
|
+
let totalBytes = 0;
|
|
282
|
+
const r5dCreateArtifact = (filename, content) => {
|
|
283
|
+
if (typeof filename !== "string" || !filename.trim()) {
|
|
284
|
+
throw new TypeError("r5dCreateArtifact(filename, content): filename must be a non-empty string.");
|
|
285
|
+
}
|
|
286
|
+
if (typeof content !== "string") {
|
|
287
|
+
throw new TypeError(
|
|
288
|
+
`r5dCreateArtifact(${JSON.stringify(filename)}, content): content must be a string. Use JSON.stringify(value) for objects.`
|
|
289
|
+
);
|
|
290
|
+
}
|
|
291
|
+
if (artifacts.length >= maxCount) {
|
|
292
|
+
throw new RangeError(`r5dCreateArtifact: at most ${maxCount} artifacts can be created in one browser_run_js call.`);
|
|
293
|
+
}
|
|
294
|
+
totalBytes += new TextEncoder().encode(content).length;
|
|
295
|
+
if (totalBytes > maxBytes) {
|
|
296
|
+
throw new RangeError(`r5dCreateArtifact: artifact content exceeded the ${maxBytes} byte budget for one browser_run_js call.`);
|
|
297
|
+
}
|
|
298
|
+
artifacts.push({ filename, content });
|
|
299
|
+
return filename;
|
|
300
|
+
};
|
|
301
|
+
const invoke = new Function("r5dCreateArtifact", `"use strict"; return (async () => {
|
|
277
302
|
${code}
|
|
278
303
|
})()`);
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
304
|
+
try {
|
|
305
|
+
return { result: await invoke(r5dCreateArtifact), artifacts };
|
|
306
|
+
} catch (error) {
|
|
307
|
+
if (artifacts.length === 0) throw error;
|
|
308
|
+
return { artifacts, failure: error instanceof Error ? error.message : String(error) };
|
|
309
|
+
}
|
|
310
|
+
},
|
|
311
|
+
{ code: input.code, maxBytes: MAX_RUN_JS_ARTIFACT_BYTES, maxCount: MAX_RUN_JS_ARTIFACT_COUNT }
|
|
312
|
+
);
|
|
313
|
+
const serialized = JSON.stringify(evaluated.result);
|
|
314
|
+
if (serialized && Buffer.byteLength(serialized) > MAX_RUN_JS_RESULT_BYTES) {
|
|
283
315
|
throw new Error("Browser JavaScript result exceeded the 1 MiB limit.");
|
|
284
316
|
}
|
|
285
|
-
return { tabId, result };
|
|
317
|
+
return { tabId, result: evaluated.result, artifacts: evaluated.artifacts, failure: evaluated.failure };
|
|
286
318
|
}
|
|
287
319
|
default:
|
|
288
320
|
throw new Error(`Unsupported browser operation: ${action}`);
|
package/dist/cjs/main.cjs
CHANGED
|
@@ -150,7 +150,7 @@ async function connectLoop(params) {
|
|
|
150
150
|
version: findVersion(),
|
|
151
151
|
chromiumVersion: params.chromiumVersion,
|
|
152
152
|
profilePath: params.profilePath,
|
|
153
|
-
capabilities: { portForwarding: true }
|
|
153
|
+
capabilities: { portForwarding: true, runJsArtifacts: true }
|
|
154
154
|
},
|
|
155
155
|
tabs: await params.runtime.listTabs(),
|
|
156
156
|
portForwards: params.portForwards.list()
|
package/dist/cjs/package.json
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
const MAX_DOWNLOAD_CHUNK_BYTES = 1024 * 1024;
|
|
4
|
+
const MAX_RUN_JS_RESULT_BYTES = 1024 * 1024;
|
|
5
|
+
const MAX_RUN_JS_ARTIFACT_BYTES = 4 * 1024 * 1024;
|
|
6
|
+
const MAX_RUN_JS_ARTIFACT_COUNT = 32;
|
|
4
7
|
class BrowserRuntime {
|
|
5
8
|
constructor(context, downloadsPath) {
|
|
6
9
|
this.context = context;
|
|
@@ -239,17 +242,46 @@ class BrowserRuntime {
|
|
|
239
242
|
return { tabId, characters: input.text.length };
|
|
240
243
|
case "run_js": {
|
|
241
244
|
if (typeof input.code !== "string") throw new Error("run_js requires code.");
|
|
242
|
-
const
|
|
243
|
-
|
|
245
|
+
const evaluated = await page.evaluate(
|
|
246
|
+
async ({ code, maxBytes, maxCount }) => {
|
|
247
|
+
const artifacts = [];
|
|
248
|
+
let totalBytes = 0;
|
|
249
|
+
const r5dCreateArtifact = (filename, content) => {
|
|
250
|
+
if (typeof filename !== "string" || !filename.trim()) {
|
|
251
|
+
throw new TypeError("r5dCreateArtifact(filename, content): filename must be a non-empty string.");
|
|
252
|
+
}
|
|
253
|
+
if (typeof content !== "string") {
|
|
254
|
+
throw new TypeError(
|
|
255
|
+
`r5dCreateArtifact(${JSON.stringify(filename)}, content): content must be a string. Use JSON.stringify(value) for objects.`
|
|
256
|
+
);
|
|
257
|
+
}
|
|
258
|
+
if (artifacts.length >= maxCount) {
|
|
259
|
+
throw new RangeError(`r5dCreateArtifact: at most ${maxCount} artifacts can be created in one browser_run_js call.`);
|
|
260
|
+
}
|
|
261
|
+
totalBytes += new TextEncoder().encode(content).length;
|
|
262
|
+
if (totalBytes > maxBytes) {
|
|
263
|
+
throw new RangeError(`r5dCreateArtifact: artifact content exceeded the ${maxBytes} byte budget for one browser_run_js call.`);
|
|
264
|
+
}
|
|
265
|
+
artifacts.push({ filename, content });
|
|
266
|
+
return filename;
|
|
267
|
+
};
|
|
268
|
+
const invoke = new Function("r5dCreateArtifact", `"use strict"; return (async () => {
|
|
244
269
|
${code}
|
|
245
270
|
})()`);
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
271
|
+
try {
|
|
272
|
+
return { result: await invoke(r5dCreateArtifact), artifacts };
|
|
273
|
+
} catch (error) {
|
|
274
|
+
if (artifacts.length === 0) throw error;
|
|
275
|
+
return { artifacts, failure: error instanceof Error ? error.message : String(error) };
|
|
276
|
+
}
|
|
277
|
+
},
|
|
278
|
+
{ code: input.code, maxBytes: MAX_RUN_JS_ARTIFACT_BYTES, maxCount: MAX_RUN_JS_ARTIFACT_COUNT }
|
|
279
|
+
);
|
|
280
|
+
const serialized = JSON.stringify(evaluated.result);
|
|
281
|
+
if (serialized && Buffer.byteLength(serialized) > MAX_RUN_JS_RESULT_BYTES) {
|
|
250
282
|
throw new Error("Browser JavaScript result exceeded the 1 MiB limit.");
|
|
251
283
|
}
|
|
252
|
-
return { tabId, result };
|
|
284
|
+
return { tabId, result: evaluated.result, artifacts: evaluated.artifacts, failure: evaluated.failure };
|
|
253
285
|
}
|
|
254
286
|
default:
|
|
255
287
|
throw new Error(`Unsupported browser operation: ${action}`);
|
package/dist/mjs/main.mjs
CHANGED
|
@@ -127,7 +127,7 @@ async function connectLoop(params) {
|
|
|
127
127
|
version: findVersion(),
|
|
128
128
|
chromiumVersion: params.chromiumVersion,
|
|
129
129
|
profilePath: params.profilePath,
|
|
130
|
-
capabilities: { portForwarding: true }
|
|
130
|
+
capabilities: { portForwarding: true, runJsArtifacts: true }
|
|
131
131
|
},
|
|
132
132
|
tabs: await params.runtime.listTabs(),
|
|
133
133
|
portForwards: params.portForwards.list()
|
package/dist/mjs/package.json
CHANGED