@agentorg/computer 0.0.12 → 0.0.14
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 +3 -1
- package/dist/THIRD_PARTY_LICENSES.txt +1 -1
- package/dist/agentcomputer.js +76 -24
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -35,6 +35,8 @@ That prints the computer's `computerID`, its endpoints and its `accessToken`, on
|
|
|
35
35
|
| `terminal` | Those, and `exec_command`, `write_stdin`, `list_sessions` |
|
|
36
36
|
| `none` | No `computer` endpoint; needs `--browser` |
|
|
37
37
|
|
|
38
|
+
`read` answers a text file as numbered lines, and a PNG, JPEG, GIF or WebP of up to 3 MiB as the picture itself: an image an MCP client can pass on to a model. The browser's `browser_take_screenshot` answers an image too, of whatever size it comes to: a `fullPage` one of a long page can be many megabytes. A container made by an earlier version keeps the program it was made with, and refuses a picture as a binary file until it is killed and made again.
|
|
39
|
+
|
|
38
40
|
`about` tells the agent what a person looking at the computer reads. It takes nothing and answers JSON text, `{"rows": [...]}`, each row a `label` and a `value`, with `"path": true` on a value that is a path and `"caution": true` on one to be read as a caution; a row that is neither has neither. The rows come in this order:
|
|
39
41
|
|
|
40
42
|
| `label` | `value` |
|
|
@@ -51,7 +53,7 @@ That prints the computer's `computerID`, its endpoints and its `accessToken`, on
|
|
|
51
53
|
- **The last three** are a reading of its load taken over half a second when it is asked, in the same way the `load` surface reads. They come together, or not at all where a reading can't be taken.
|
|
52
54
|
- **Not every computer has it.** One with no `computer` endpoint has no `about`. One in a container made by an earlier version keeps the program it was made with: `about` is listed among its tools and isn't there until it is killed and made again.
|
|
53
55
|
|
|
54
|
-
The file tools stay inside the workspace. That's a check, not a boundary. Below `terminal`, the browser's `browser_run_code_unsafe` tool is hidden and refused, because it would otherwise be a terminal. The service enforces all of this, whatever the client does.
|
|
56
|
+
The file tools stay inside the workspace. That's a check, not a boundary. Below `terminal`, the browser's `browser_run_code_unsafe` tool is hidden and refused, because it would otherwise be a terminal. The browser's endpoint takes one JSON-RPC request in each POST, as a JSON object, and refuses anything else with 400, a batch among them: it checks each request before passing it on, and passes on only what it read. The service enforces all of this, whatever the client does.
|
|
55
57
|
|
|
56
58
|
`--browser` drives the Google Chrome installed on the machine, in a profile of the computer's own. `serve --browser-name` and `--browser-path` choose another. The browser has no debug port: a small host process starts it and shares it on a Unix socket in a folder only its user can enter, which the agent's tools and the live view both use. It lives as long as its computer does: a client that ends its session or goes away leaves the browser and its pages as they are, and the agent's `browser_close` is refused. Only an agent with `--access terminal` can stop it, with `browser_run_code_unsafe` or from its terminal.
|
|
57
59
|
|
package/dist/agentcomputer.js
CHANGED
|
@@ -3268,7 +3268,7 @@ var require_stream = __commonJS({
|
|
|
3268
3268
|
};
|
|
3269
3269
|
duplex._final = function(callback) {
|
|
3270
3270
|
if (ws.readyState === ws.CONNECTING) {
|
|
3271
|
-
ws.once("open", function
|
|
3271
|
+
ws.once("open", function open5() {
|
|
3272
3272
|
duplex._final(callback);
|
|
3273
3273
|
});
|
|
3274
3274
|
return;
|
|
@@ -3289,7 +3289,7 @@ var require_stream = __commonJS({
|
|
|
3289
3289
|
};
|
|
3290
3290
|
duplex._write = function(chunk, encoding, callback) {
|
|
3291
3291
|
if (ws.readyState === ws.CONNECTING) {
|
|
3292
|
-
ws.once("open", function
|
|
3292
|
+
ws.once("open", function open5() {
|
|
3293
3293
|
duplex._write(chunk, encoding, callback);
|
|
3294
3294
|
});
|
|
3295
3295
|
return;
|
|
@@ -27961,6 +27961,7 @@ var RUNS_CODE = "browser_run_code_unsafe";
|
|
|
27961
27961
|
var MAX_BODY = 8 * 1024 * 1024;
|
|
27962
27962
|
var START_MS = 3e4;
|
|
27963
27963
|
var STOP_MS = 2e3;
|
|
27964
|
+
var ONE_REQUEST = "A body is one JSON-RPC request, as a JSON object: send each request in its own POST.";
|
|
27964
27965
|
var FORWARDED = [
|
|
27965
27966
|
"accept",
|
|
27966
27967
|
"content-type",
|
|
@@ -28098,18 +28099,29 @@ var Browser = class {
|
|
|
28098
28099
|
chunks.push(chunk);
|
|
28099
28100
|
}
|
|
28100
28101
|
const body = Buffer.concat(chunks);
|
|
28101
|
-
let message
|
|
28102
|
+
let message;
|
|
28102
28103
|
try {
|
|
28103
|
-
|
|
28104
|
+
const read = body.length > 0 ? JSON.parse(body.toString("utf8")) : void 0;
|
|
28105
|
+
if (typeof read === "object" && read !== null && !Array.isArray(read)) message = read;
|
|
28104
28106
|
} catch {
|
|
28105
28107
|
}
|
|
28106
|
-
|
|
28108
|
+
if (body.length > 0 && !message) {
|
|
28109
|
+
response.writeHead(400, { "Content-Type": "application/json" });
|
|
28110
|
+
return void response.end(
|
|
28111
|
+
JSON.stringify({
|
|
28112
|
+
jsonrpc: "2.0",
|
|
28113
|
+
id: null,
|
|
28114
|
+
error: { code: -32600, message: ONE_REQUEST }
|
|
28115
|
+
})
|
|
28116
|
+
);
|
|
28117
|
+
}
|
|
28118
|
+
const tool = message?.method === "tools/call" ? String(message.params?.name) : void 0;
|
|
28107
28119
|
if (tool && this.#hidden.has(tool)) {
|
|
28108
28120
|
response.writeHead(200, { "Content-Type": "application/json" });
|
|
28109
28121
|
return void response.end(
|
|
28110
28122
|
JSON.stringify({
|
|
28111
28123
|
jsonrpc: "2.0",
|
|
28112
|
-
id: message
|
|
28124
|
+
id: message?.id ?? null,
|
|
28113
28125
|
error: { code: -32602, message: `Unknown tool: ${tool}` }
|
|
28114
28126
|
})
|
|
28115
28127
|
);
|
|
@@ -28134,12 +28146,13 @@ var Browser = class {
|
|
|
28134
28146
|
}).then((answer) => answer.text()).catch(() => {
|
|
28135
28147
|
});
|
|
28136
28148
|
if (tool === "browser_file_upload") this.gate?.agentAnsweredChooser();
|
|
28149
|
+
if (message) headers["content-type"] = "application/json";
|
|
28137
28150
|
const upstream = await fetch(`${origin}/mcp`, {
|
|
28138
28151
|
method: request.method,
|
|
28139
28152
|
headers,
|
|
28140
|
-
...
|
|
28153
|
+
...message ? { body: JSON.stringify(message) } : {}
|
|
28141
28154
|
});
|
|
28142
|
-
if (message
|
|
28155
|
+
if (message?.method === "tools/list" && this.#hidden.size > 0) {
|
|
28143
28156
|
const text3 = withoutHidden(await upstream.text(), this.#hidden);
|
|
28144
28157
|
return sendAnswer(new Response(text3, upstream), response);
|
|
28145
28158
|
}
|
|
@@ -28169,12 +28182,13 @@ import { STATUS_CODES, ServerResponse, createServer } from "node:http";
|
|
|
28169
28182
|
import { Http2ServerRequest, constants } from "node:http2";
|
|
28170
28183
|
import { Readable as Readable2 } from "node:stream";
|
|
28171
28184
|
import { randomBytes as randomBytes3 } from "node:crypto";
|
|
28172
|
-
import { lstat as lstat2, open as
|
|
28185
|
+
import { lstat as lstat2, open as open3, readdir as readdir2, rename as rename2, rm as rm2, stat as stat2 } from "node:fs/promises";
|
|
28173
28186
|
import { dirname as dirname2, join as join3 } from "node:path";
|
|
28174
28187
|
import { randomBytes as randomBytes2 } from "node:crypto";
|
|
28175
28188
|
import {
|
|
28176
28189
|
chmod,
|
|
28177
28190
|
lstat,
|
|
28191
|
+
open as open2,
|
|
28178
28192
|
readdir,
|
|
28179
28193
|
readFile,
|
|
28180
28194
|
realpath,
|
|
@@ -34318,7 +34332,7 @@ function bucketFor2(state, inst) {
|
|
|
34318
34332
|
return bucket;
|
|
34319
34333
|
}
|
|
34320
34334
|
var handoff2;
|
|
34321
|
-
var
|
|
34335
|
+
var open4 = [];
|
|
34322
34336
|
var memo3 = {
|
|
34323
34337
|
alloc(_inst, payload, empty) {
|
|
34324
34338
|
const bucket = handoff2;
|
|
@@ -34327,7 +34341,7 @@ var memo3 = {
|
|
|
34327
34341
|
handoff2 = void 0;
|
|
34328
34342
|
const entry = { value: empty, issues: null };
|
|
34329
34343
|
bucket.set(payload.value, entry);
|
|
34330
|
-
|
|
34344
|
+
open4.push(entry);
|
|
34331
34345
|
return empty;
|
|
34332
34346
|
},
|
|
34333
34347
|
guard(inst) {
|
|
@@ -34398,10 +34412,10 @@ var memo3 = {
|
|
|
34398
34412
|
return payload;
|
|
34399
34413
|
}
|
|
34400
34414
|
handoff2 = bucket;
|
|
34401
|
-
const depth =
|
|
34415
|
+
const depth = open4.length;
|
|
34402
34416
|
const result = base(payload, ctx);
|
|
34403
34417
|
handoff2 = void 0;
|
|
34404
|
-
const entry =
|
|
34418
|
+
const entry = open4.length > depth ? open4.pop() : void 0;
|
|
34405
34419
|
if (result instanceof Promise) {
|
|
34406
34420
|
return result.then((r) => {
|
|
34407
34421
|
if (entry)
|
|
@@ -66158,6 +66172,21 @@ var MAX_READ_CHARS = 28e3;
|
|
|
66158
66172
|
var MAX_LINE_CHARS = 2e3;
|
|
66159
66173
|
var MAX_ENTRIES = 1e3;
|
|
66160
66174
|
var MAX_EDIT_BYTES = 8 * 1024 * 1024;
|
|
66175
|
+
var MAX_PICTURE_BYTES = 3 * 1024 * 1024;
|
|
66176
|
+
async function pictureKind(path) {
|
|
66177
|
+
const file2 = await open2(path);
|
|
66178
|
+
try {
|
|
66179
|
+
const { buffer, bytesRead } = await file2.read(Buffer.alloc(12), 0, 12, 0);
|
|
66180
|
+
const start = buffer.toString("latin1", 0, bytesRead);
|
|
66181
|
+
if (start.startsWith("\x89PNG\r\n\n")) return "image/png";
|
|
66182
|
+
if (start.startsWith("\xFF\xD8\xFF")) return "image/jpeg";
|
|
66183
|
+
if (start.startsWith("GIF87a") || start.startsWith("GIF89a")) return "image/gif";
|
|
66184
|
+
if (start.startsWith("RIFF") && start.slice(8) === "WEBP") return "image/webp";
|
|
66185
|
+
return void 0;
|
|
66186
|
+
} finally {
|
|
66187
|
+
await file2.close();
|
|
66188
|
+
}
|
|
66189
|
+
}
|
|
66161
66190
|
var reason = (error62) => {
|
|
66162
66191
|
const { code, path } = error62;
|
|
66163
66192
|
if (code === "ENOENT") return `No such file or folder: ${path}`;
|
|
@@ -66213,9 +66242,19 @@ function createFiles(root) {
|
|
|
66213
66242
|
return bytes.toString("utf8");
|
|
66214
66243
|
};
|
|
66215
66244
|
return {
|
|
66216
|
-
/** Numbered lines of a file. */
|
|
66245
|
+
/** Numbered lines of a file, or the file itself where it is a picture. */
|
|
66217
66246
|
read: (input2) => refusing(async () => {
|
|
66218
|
-
const
|
|
66247
|
+
const path = await at(input2.path);
|
|
66248
|
+
const { size } = await aFile(path, input2.path);
|
|
66249
|
+
const mimeType = await pictureKind(path);
|
|
66250
|
+
if (mimeType) {
|
|
66251
|
+
if (size > MAX_PICTURE_BYTES)
|
|
66252
|
+
throw new ExecRefused(
|
|
66253
|
+
`${input2.path} is too large to read as a picture: ${size.toLocaleString("en-US")} bytes, and the most is ${MAX_PICTURE_BYTES.toLocaleString("en-US")}.`
|
|
66254
|
+
);
|
|
66255
|
+
return { mimeType, bytes: await readFile(path) };
|
|
66256
|
+
}
|
|
66257
|
+
const lines = (await text22(path, input2.path)).split("\n");
|
|
66219
66258
|
const first2 = Math.max(input2.offset ?? 1, 1);
|
|
66220
66259
|
const shown = lines.slice(first2 - 1, first2 - 1 + (input2.limit ?? DEFAULT_LINES)).map((line, i) => `${first2 + i} ${cut(line, MAX_LINE_CHARS)}`);
|
|
66221
66260
|
const last = first2 + shown.length - 1;
|
|
@@ -66425,7 +66464,7 @@ function createHostFiles(root, level) {
|
|
|
66425
66464
|
throw new ExecRefused("A read starts at 0 or later and takes 1 to 1,048,576 bytes.");
|
|
66426
66465
|
const real = await inside(root, path);
|
|
66427
66466
|
const found = await aFile(real, path);
|
|
66428
|
-
const file2 = await
|
|
66467
|
+
const file2 = await open3(real, "r");
|
|
66429
66468
|
try {
|
|
66430
66469
|
const bytes = Buffer.alloc(Math.max(0, Math.min(length, found.size - offset)));
|
|
66431
66470
|
const { bytesRead } = await file2.read(bytes, 0, bytes.length, offset);
|
|
@@ -66454,7 +66493,7 @@ function createHostFiles(root, level) {
|
|
|
66454
66493
|
if (before?.isDirectory()) throw new ExecRefused(`${path} is a folder.`);
|
|
66455
66494
|
await stat2(dirname2(target));
|
|
66456
66495
|
const temporary = join3(dirname2(target), `.agentexec-${randomBytes3(6).toString("hex")}`);
|
|
66457
|
-
const file2 = await
|
|
66496
|
+
const file2 = await open3(temporary, "wx");
|
|
66458
66497
|
const discard = async () => {
|
|
66459
66498
|
await file2.close().catch(() => {
|
|
66460
66499
|
});
|
|
@@ -66527,6 +66566,12 @@ var answering = async (work) => {
|
|
|
66527
66566
|
return refused(error62);
|
|
66528
66567
|
}
|
|
66529
66568
|
};
|
|
66569
|
+
var pictured = (path, { mimeType, bytes }) => ({
|
|
66570
|
+
content: [
|
|
66571
|
+
{ type: "text", text: `${path}: ${mimeType}` },
|
|
66572
|
+
{ type: "image", mimeType, data: bytes.toString("base64") }
|
|
66573
|
+
]
|
|
66574
|
+
});
|
|
66530
66575
|
var saying = async (work) => {
|
|
66531
66576
|
try {
|
|
66532
66577
|
return text(await work());
|
|
@@ -66626,7 +66671,7 @@ function createServer2(root, sessions, version22, level, extraTools = []) {
|
|
|
66626
66671
|
server.registerTool(
|
|
66627
66672
|
"read",
|
|
66628
66673
|
{
|
|
66629
|
-
description: "Reads a text file as numbered lines.",
|
|
66674
|
+
description: "Reads a text file as numbered lines, or shows a PNG, JPEG, GIF or WebP of up to 3 MiB as an image; offset and limit are for text.",
|
|
66630
66675
|
inputSchema: {
|
|
66631
66676
|
path: external_exports.string().min(1).describe("Inside the workspace; relative to it, or absolute."),
|
|
66632
66677
|
offset: external_exports.number().int().positive().optional().describe("The first line, from 1."),
|
|
@@ -66634,7 +66679,14 @@ function createServer2(root, sessions, version22, level, extraTools = []) {
|
|
|
66634
66679
|
},
|
|
66635
66680
|
annotations: READ_ONLY
|
|
66636
66681
|
},
|
|
66637
|
-
(input2) =>
|
|
66682
|
+
async (input2) => {
|
|
66683
|
+
try {
|
|
66684
|
+
const read = await files.read(input2);
|
|
66685
|
+
return typeof read === "string" ? text(read) : pictured(input2.path, read);
|
|
66686
|
+
} catch (error62) {
|
|
66687
|
+
return refused(error62);
|
|
66688
|
+
}
|
|
66689
|
+
}
|
|
66638
66690
|
);
|
|
66639
66691
|
server.registerTool(
|
|
66640
66692
|
"list",
|
|
@@ -66677,7 +66729,7 @@ function createServer2(root, sessions, version22, level, extraTools = []) {
|
|
|
66677
66729
|
);
|
|
66678
66730
|
return server;
|
|
66679
66731
|
}
|
|
66680
|
-
var VERSION = "0.0.
|
|
66732
|
+
var VERSION = "0.0.7";
|
|
66681
66733
|
function createAgentExec(options) {
|
|
66682
66734
|
const root = realpathSync(resolve3(options.workspace));
|
|
66683
66735
|
const level = options.level ?? "read";
|
|
@@ -68093,7 +68145,7 @@ async function forward(url3, authorization, request, response) {
|
|
|
68093
68145
|
}
|
|
68094
68146
|
|
|
68095
68147
|
// src/handlers.ts
|
|
68096
|
-
var VERSION2 = "0.0.
|
|
68148
|
+
var VERSION2 = "0.0.14";
|
|
68097
68149
|
function createHandlers(computers) {
|
|
68098
68150
|
return {
|
|
68099
68151
|
getInfo: async () => ({ version: VERSION2, isolation: computers.isolation }),
|
|
@@ -68677,8 +68729,8 @@ async function openLive(session, onMessage) {
|
|
|
68677
68729
|
}
|
|
68678
68730
|
var Terminals = class {
|
|
68679
68731
|
#open;
|
|
68680
|
-
constructor(
|
|
68681
|
-
this.#open =
|
|
68732
|
+
constructor(open5) {
|
|
68733
|
+
this.#open = open5;
|
|
68682
68734
|
}
|
|
68683
68735
|
/**
|
|
68684
68736
|
* Each terminal as it starts, writes and exits: those already there first, each with the end of
|
|
@@ -85058,7 +85110,7 @@ var OPENAPI = {
|
|
|
85058
85110
|
"openapi": "3.1.0",
|
|
85059
85111
|
"info": {
|
|
85060
85112
|
"title": "AgentComputer",
|
|
85061
|
-
"version": "0.0.
|
|
85113
|
+
"version": "0.0.14",
|
|
85062
85114
|
"description": "Computers for agents. This file is the source of truth: the HTTP API, the MCP control tools and the SDK are all generated from it. An operation with `x-mcp-tool` is also an MCP tool of that name.",
|
|
85063
85115
|
"license": {
|
|
85064
85116
|
"name": "Apache-2.0",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agentorg/computer",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.14",
|
|
4
4
|
"description": "Computers for agents, as a service: a terminal, files and a browser over MCP",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
"playwright-core": "1.65.0-alpha-2026-10-30"
|
|
32
32
|
},
|
|
33
33
|
"devDependencies": {
|
|
34
|
-
"@agentorg/exec": "0.0.
|
|
34
|
+
"@agentorg/exec": "0.0.7",
|
|
35
35
|
"@biomejs/biome": "2.6.5",
|
|
36
36
|
"@modelcontextprotocol/client": "2.4.0",
|
|
37
37
|
"@modelcontextprotocol/node": "2.1.2",
|