@agentorg/computer 0.0.11 → 0.0.13
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 +21 -3
- package/dist/THIRD_PARTY_LICENSES.txt +1 -1
- package/dist/agentcomputer.js +223 -79
- package/dist/types/generated/schema.d.ts +2 -2
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -22,7 +22,7 @@ That prints the computer's `computerID`, its endpoints and its `accessToken`, on
|
|
|
22
22
|
`agentcomputer serve --isolation container` makes each computer a container of its own, which needs Docker on the same machine. The service follows your `DOCKER_HOST` or current context and never changes them.
|
|
23
23
|
|
|
24
24
|
- The image holds AgentExec, Playwright MCP and a headless Chromium. It is `ghcr.io/uniac-ai/agentcomputer/computer` at the service's version; `--image` names another.
|
|
25
|
-
- `--workspace` is mounted at `/workspace`, read-only at `--access read`, and nothing else of the machine is visible. Docker has to be able to share that folder. Without `--workspace`, the service makes one.
|
|
25
|
+
- `--workspace` is mounted at `/workspace`, read-only at `--access read`, and nothing else of the machine is visible. The computer is told that folder's path on the machine and the machine's name, which `about` answers. Docker has to be able to share that folder. Without `--workspace`, the service makes one.
|
|
26
26
|
- It runs as the user who started the service, with no capabilities.
|
|
27
27
|
- `stop` stops the container and keeps it, so what was installed in it is there at `start`. `kill` removes it. Starting a stopped computer uses the image it was made from.
|
|
28
28
|
|
|
@@ -30,11 +30,29 @@ That prints the computer's `computerID`, its endpoints and its `accessToken`, on
|
|
|
30
30
|
|
|
31
31
|
| `--access` | Tools at the `computer` endpoint |
|
|
32
32
|
| ---------- | --------------------------------------------------------- |
|
|
33
|
-
| `read` | `read`, `list`, `search`
|
|
33
|
+
| `read` | `about`, `read`, `list`, `search` |
|
|
34
34
|
| `files` | Those, and `edit` |
|
|
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
|
+
|
|
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:
|
|
41
|
+
|
|
42
|
+
| `label` | `value` |
|
|
43
|
+
| ----------- | ----------------------------------------------------------------------------------------- |
|
|
44
|
+
| `Where` | The machine's own name |
|
|
45
|
+
| `Folder` | The workspace's path on that machine, as a path |
|
|
46
|
+
| `Reaches` | `Read-only files`, `Files`, or `Files and terminal`; with a browser, `, Browser` after it |
|
|
47
|
+
| `Isolation` | `None: it acts as you`, as a caution, or `A container of its own` |
|
|
48
|
+
| `CPU` | Used, of all the machine's CPUs, as a whole percentage: `37%` |
|
|
49
|
+
| `Memory` | Used of the machine's total, counted in 1024s and written GB: `9.2 of 16 GB` |
|
|
50
|
+
| `Disk` | Used of total, of the volume the workspace is on, counted in 1000s: `212 of 494 GB` |
|
|
51
|
+
|
|
52
|
+
- **In a container** the agent's commands find the folder somewhere else, so `Folder` says both and is not marked as a path: `/home/me/Research on the machine, mounted at /workspace in it`. CPU and Memory there are the computer's own; with no isolation they are the whole machine's.
|
|
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.
|
|
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.
|
|
55
|
+
|
|
38
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 service enforces all of this, whatever the client does.
|
|
39
57
|
|
|
40
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.
|
|
@@ -169,7 +187,7 @@ Every running computer has one more, to watch only:
|
|
|
169
187
|
- **`load`** (protocol `metrics@1`): how busy it is. The service sends a reading every two seconds while a socket is open, the first two seconds after the first socket opens, and the last one at once to a socket that opens later: `{"type":"reading","scope":"machine","cpuUsedPct":37.2,"memUsed":…,"memTotal":…,"diskUsed":…,"diskTotal":…}`, in bytes. `openapi.yaml` has each field, as `LoadReading`.
|
|
170
188
|
- **`scope` says whose numbers they are.** A computer in a container reads its own (`computer`), against the machine's totals, since a container has no limit of its own. A computer with no isolation has no edge to measure at, so it reads the whole machine's (`machine`), as every other computer of that service does; so does a container that can't read its own.
|
|
171
189
|
- **The disk is the volume its workspace is on,** not the workspace's own size.
|
|
172
|
-
- **Nothing depends on it.** Nothing is read while nobody watches, a reading that can't be taken is left out, and a socket that hasn't read the one before is not sent the next.
|
|
190
|
+
- **Nothing depends on it.** Nothing is read while nobody watches or asks, a reading that can't be taken is left out, and a socket that hasn't read the one before is not sent the next.
|
|
173
191
|
|
|
174
192
|
`POST /v1/computers/{computerID}/live` with `{ "surface": "terminal", "mode": "operate" }` gives a ticket: a `url` to open once within a minute, which needs no token. A request with an `Origin` header is refused unless `serve --allow-origin` names that origin, so a page can't open one. The SDK does the same with `computer.terminals.connect({ onEvent })`, or gives the ticket alone with `computer.liveSession("files")` or `computer.liveSession("browser", "operate")`, and the CLI with `agentcomputer live <id> --surface terminal --mode operate`.
|
|
175
193
|
|
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;
|
|
@@ -3849,7 +3849,8 @@ async function serveBrowserMcp(options) {
|
|
|
3849
3849
|
import { execFile as execFile2, spawn as spawn3 } from "node:child_process";
|
|
3850
3850
|
import { readFileSync as readFileSync3 } from "node:fs";
|
|
3851
3851
|
import { connect } from "node:net";
|
|
3852
|
-
import {
|
|
3852
|
+
import { hostname as hostname4 } from "node:os";
|
|
3853
|
+
import { join as join8 } from "node:path";
|
|
3853
3854
|
import { createInterface as createInterface2 } from "node:readline";
|
|
3854
3855
|
import { Readable as Readable3 } from "node:stream";
|
|
3855
3856
|
import { promisify as promisify2 } from "node:util";
|
|
@@ -5806,9 +5807,9 @@ var asciiTabOrNewline = /[\t\n\r]/g;
|
|
|
5806
5807
|
function stripTabAndNewline(value) {
|
|
5807
5808
|
return value.replace(asciiTabOrNewline, "");
|
|
5808
5809
|
}
|
|
5809
|
-
function urlHostnameOk(url3,
|
|
5810
|
-
|
|
5811
|
-
return
|
|
5810
|
+
function urlHostnameOk(url3, hostname5) {
|
|
5811
|
+
hostname5.lastIndex = 0;
|
|
5812
|
+
return hostname5.test(url3.hostname);
|
|
5812
5813
|
}
|
|
5813
5814
|
function urlProtocolOk(url3, protocol) {
|
|
5814
5815
|
protocol.lastIndex = 0;
|
|
@@ -24148,8 +24149,8 @@ function applyPostAuth(clientId, clientSecret, params) {
|
|
|
24148
24149
|
function applyPublicAuth(clientId, params) {
|
|
24149
24150
|
params.set("client_id", clientId);
|
|
24150
24151
|
}
|
|
24151
|
-
function isLoopbackHost(
|
|
24152
|
-
return
|
|
24152
|
+
function isLoopbackHost(hostname5) {
|
|
24153
|
+
return hostname5 === "localhost" || hostname5.endsWith(".localhost") || hostname5 === "127.0.0.1" || hostname5 === "[::1]" || hostname5 === "::1";
|
|
24153
24154
|
}
|
|
24154
24155
|
function assertSecureTokenEndpoint(tokenEndpoint) {
|
|
24155
24156
|
const url3 = new URL(String(tokenEndpoint));
|
|
@@ -28158,6 +28159,7 @@ import { join as join7 } from "node:path";
|
|
|
28158
28159
|
// src/runtime.ts
|
|
28159
28160
|
import { chmodSync as chmodSync2, existsSync, statSync as statSync2 } from "node:fs";
|
|
28160
28161
|
import { createRequire as createRequire3 } from "node:module";
|
|
28162
|
+
import { hostname as hostname3 } from "node:os";
|
|
28161
28163
|
import { basename as basename2, dirname as dirname3, join as join6 } from "node:path";
|
|
28162
28164
|
|
|
28163
28165
|
// node_modules/@agentorg/exec/dist/lib.js
|
|
@@ -28167,12 +28169,13 @@ import { STATUS_CODES, ServerResponse, createServer } from "node:http";
|
|
|
28167
28169
|
import { Http2ServerRequest, constants } from "node:http2";
|
|
28168
28170
|
import { Readable as Readable2 } from "node:stream";
|
|
28169
28171
|
import { randomBytes as randomBytes3 } from "node:crypto";
|
|
28170
|
-
import { lstat as lstat2, open as
|
|
28172
|
+
import { lstat as lstat2, open as open3, readdir as readdir2, rename as rename2, rm as rm2, stat as stat2 } from "node:fs/promises";
|
|
28171
28173
|
import { dirname as dirname2, join as join3 } from "node:path";
|
|
28172
28174
|
import { randomBytes as randomBytes2 } from "node:crypto";
|
|
28173
28175
|
import {
|
|
28174
28176
|
chmod,
|
|
28175
28177
|
lstat,
|
|
28178
|
+
open as open2,
|
|
28176
28179
|
readdir,
|
|
28177
28180
|
readFile,
|
|
28178
28181
|
realpath,
|
|
@@ -31928,9 +31931,9 @@ var asciiTabOrNewline2 = /[\t\n\r]/g;
|
|
|
31928
31931
|
function stripTabAndNewline2(value) {
|
|
31929
31932
|
return value.replace(asciiTabOrNewline2, "");
|
|
31930
31933
|
}
|
|
31931
|
-
function urlHostnameOk2(url22,
|
|
31932
|
-
|
|
31933
|
-
return
|
|
31934
|
+
function urlHostnameOk2(url22, hostname32) {
|
|
31935
|
+
hostname32.lastIndex = 0;
|
|
31936
|
+
return hostname32.test(url22.hostname);
|
|
31934
31937
|
}
|
|
31935
31938
|
function urlProtocolOk2(url22, protocol) {
|
|
31936
31939
|
protocol.lastIndex = 0;
|
|
@@ -34316,7 +34319,7 @@ function bucketFor2(state, inst) {
|
|
|
34316
34319
|
return bucket;
|
|
34317
34320
|
}
|
|
34318
34321
|
var handoff2;
|
|
34319
|
-
var
|
|
34322
|
+
var open4 = [];
|
|
34320
34323
|
var memo3 = {
|
|
34321
34324
|
alloc(_inst, payload, empty) {
|
|
34322
34325
|
const bucket = handoff2;
|
|
@@ -34325,7 +34328,7 @@ var memo3 = {
|
|
|
34325
34328
|
handoff2 = void 0;
|
|
34326
34329
|
const entry = { value: empty, issues: null };
|
|
34327
34330
|
bucket.set(payload.value, entry);
|
|
34328
|
-
|
|
34331
|
+
open4.push(entry);
|
|
34329
34332
|
return empty;
|
|
34330
34333
|
},
|
|
34331
34334
|
guard(inst) {
|
|
@@ -34396,10 +34399,10 @@ var memo3 = {
|
|
|
34396
34399
|
return payload;
|
|
34397
34400
|
}
|
|
34398
34401
|
handoff2 = bucket;
|
|
34399
|
-
const depth =
|
|
34402
|
+
const depth = open4.length;
|
|
34400
34403
|
const result = base(payload, ctx);
|
|
34401
34404
|
handoff2 = void 0;
|
|
34402
|
-
const entry =
|
|
34405
|
+
const entry = open4.length > depth ? open4.pop() : void 0;
|
|
34403
34406
|
if (result instanceof Promise) {
|
|
34404
34407
|
return result.then((r) => {
|
|
34405
34408
|
if (entry)
|
|
@@ -66156,6 +66159,21 @@ var MAX_READ_CHARS = 28e3;
|
|
|
66156
66159
|
var MAX_LINE_CHARS = 2e3;
|
|
66157
66160
|
var MAX_ENTRIES = 1e3;
|
|
66158
66161
|
var MAX_EDIT_BYTES = 8 * 1024 * 1024;
|
|
66162
|
+
var MAX_PICTURE_BYTES = 3 * 1024 * 1024;
|
|
66163
|
+
async function pictureKind(path) {
|
|
66164
|
+
const file2 = await open2(path);
|
|
66165
|
+
try {
|
|
66166
|
+
const { buffer, bytesRead } = await file2.read(Buffer.alloc(12), 0, 12, 0);
|
|
66167
|
+
const start = buffer.toString("latin1", 0, bytesRead);
|
|
66168
|
+
if (start.startsWith("\x89PNG\r\n\n")) return "image/png";
|
|
66169
|
+
if (start.startsWith("\xFF\xD8\xFF")) return "image/jpeg";
|
|
66170
|
+
if (start.startsWith("GIF87a") || start.startsWith("GIF89a")) return "image/gif";
|
|
66171
|
+
if (start.startsWith("RIFF") && start.slice(8) === "WEBP") return "image/webp";
|
|
66172
|
+
return void 0;
|
|
66173
|
+
} finally {
|
|
66174
|
+
await file2.close();
|
|
66175
|
+
}
|
|
66176
|
+
}
|
|
66159
66177
|
var reason = (error62) => {
|
|
66160
66178
|
const { code, path } = error62;
|
|
66161
66179
|
if (code === "ENOENT") return `No such file or folder: ${path}`;
|
|
@@ -66211,9 +66229,19 @@ function createFiles(root) {
|
|
|
66211
66229
|
return bytes.toString("utf8");
|
|
66212
66230
|
};
|
|
66213
66231
|
return {
|
|
66214
|
-
/** Numbered lines of a file. */
|
|
66232
|
+
/** Numbered lines of a file, or the file itself where it is a picture. */
|
|
66215
66233
|
read: (input2) => refusing(async () => {
|
|
66216
|
-
const
|
|
66234
|
+
const path = await at(input2.path);
|
|
66235
|
+
const { size } = await aFile(path, input2.path);
|
|
66236
|
+
const mimeType = await pictureKind(path);
|
|
66237
|
+
if (mimeType) {
|
|
66238
|
+
if (size > MAX_PICTURE_BYTES)
|
|
66239
|
+
throw new ExecRefused(
|
|
66240
|
+
`${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")}.`
|
|
66241
|
+
);
|
|
66242
|
+
return { mimeType, bytes: await readFile(path) };
|
|
66243
|
+
}
|
|
66244
|
+
const lines = (await text22(path, input2.path)).split("\n");
|
|
66217
66245
|
const first2 = Math.max(input2.offset ?? 1, 1);
|
|
66218
66246
|
const shown = lines.slice(first2 - 1, first2 - 1 + (input2.limit ?? DEFAULT_LINES)).map((line, i) => `${first2 + i} ${cut(line, MAX_LINE_CHARS)}`);
|
|
66219
66247
|
const last = first2 + shown.length - 1;
|
|
@@ -66423,7 +66451,7 @@ function createHostFiles(root, level) {
|
|
|
66423
66451
|
throw new ExecRefused("A read starts at 0 or later and takes 1 to 1,048,576 bytes.");
|
|
66424
66452
|
const real = await inside(root, path);
|
|
66425
66453
|
const found = await aFile(real, path);
|
|
66426
|
-
const file2 = await
|
|
66454
|
+
const file2 = await open3(real, "r");
|
|
66427
66455
|
try {
|
|
66428
66456
|
const bytes = Buffer.alloc(Math.max(0, Math.min(length, found.size - offset)));
|
|
66429
66457
|
const { bytesRead } = await file2.read(bytes, 0, bytes.length, offset);
|
|
@@ -66452,7 +66480,7 @@ function createHostFiles(root, level) {
|
|
|
66452
66480
|
if (before?.isDirectory()) throw new ExecRefused(`${path} is a folder.`);
|
|
66453
66481
|
await stat2(dirname2(target));
|
|
66454
66482
|
const temporary = join3(dirname2(target), `.agentexec-${randomBytes3(6).toString("hex")}`);
|
|
66455
|
-
const file2 = await
|
|
66483
|
+
const file2 = await open3(temporary, "wx");
|
|
66456
66484
|
const discard = async () => {
|
|
66457
66485
|
await file2.close().catch(() => {
|
|
66458
66486
|
});
|
|
@@ -66525,6 +66553,12 @@ var answering = async (work) => {
|
|
|
66525
66553
|
return refused(error62);
|
|
66526
66554
|
}
|
|
66527
66555
|
};
|
|
66556
|
+
var pictured = (path, { mimeType, bytes }) => ({
|
|
66557
|
+
content: [
|
|
66558
|
+
{ type: "text", text: `${path}: ${mimeType}` },
|
|
66559
|
+
{ type: "image", mimeType, data: bytes.toString("base64") }
|
|
66560
|
+
]
|
|
66561
|
+
});
|
|
66528
66562
|
var saying = async (work) => {
|
|
66529
66563
|
try {
|
|
66530
66564
|
return text(await work());
|
|
@@ -66541,9 +66575,30 @@ var Answered = {
|
|
|
66541
66575
|
var YieldTime = external_exports.number().int().positive().optional();
|
|
66542
66576
|
var MaxTokens = external_exports.number().int().positive().optional();
|
|
66543
66577
|
var READ_ONLY = { readOnlyHint: true };
|
|
66544
|
-
|
|
66578
|
+
var TOOLS = [
|
|
66579
|
+
"exec_command",
|
|
66580
|
+
"write_stdin",
|
|
66581
|
+
"list_sessions",
|
|
66582
|
+
"read",
|
|
66583
|
+
"list",
|
|
66584
|
+
"search",
|
|
66585
|
+
"edit"
|
|
66586
|
+
];
|
|
66587
|
+
function createServer2(root, sessions, version22, level, extraTools = []) {
|
|
66545
66588
|
const server = new McpServer({ name: "agentexec", version: version22 });
|
|
66546
66589
|
const files = createFiles(root);
|
|
66590
|
+
for (const { name, description, readOnly, call } of extraTools)
|
|
66591
|
+
server.registerTool(
|
|
66592
|
+
name,
|
|
66593
|
+
{ description, ...readOnly ? { annotations: READ_ONLY } : {} },
|
|
66594
|
+
async () => {
|
|
66595
|
+
try {
|
|
66596
|
+
return text(String(await call()));
|
|
66597
|
+
} catch (error62) {
|
|
66598
|
+
return { isError: true, ...text(error62 instanceof Error ? error62.message : String(error62)) };
|
|
66599
|
+
}
|
|
66600
|
+
}
|
|
66601
|
+
);
|
|
66547
66602
|
if (level === "terminal") {
|
|
66548
66603
|
server.registerTool(
|
|
66549
66604
|
"exec_command",
|
|
@@ -66603,7 +66658,7 @@ function createServer2(root, sessions, version22, level) {
|
|
|
66603
66658
|
server.registerTool(
|
|
66604
66659
|
"read",
|
|
66605
66660
|
{
|
|
66606
|
-
description: "Reads a text file as numbered lines.",
|
|
66661
|
+
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.",
|
|
66607
66662
|
inputSchema: {
|
|
66608
66663
|
path: external_exports.string().min(1).describe("Inside the workspace; relative to it, or absolute."),
|
|
66609
66664
|
offset: external_exports.number().int().positive().optional().describe("The first line, from 1."),
|
|
@@ -66611,7 +66666,14 @@ function createServer2(root, sessions, version22, level) {
|
|
|
66611
66666
|
},
|
|
66612
66667
|
annotations: READ_ONLY
|
|
66613
66668
|
},
|
|
66614
|
-
(input2) =>
|
|
66669
|
+
async (input2) => {
|
|
66670
|
+
try {
|
|
66671
|
+
const read = await files.read(input2);
|
|
66672
|
+
return typeof read === "string" ? text(read) : pictured(input2.path, read);
|
|
66673
|
+
} catch (error62) {
|
|
66674
|
+
return refused(error62);
|
|
66675
|
+
}
|
|
66676
|
+
}
|
|
66615
66677
|
);
|
|
66616
66678
|
server.registerTool(
|
|
66617
66679
|
"list",
|
|
@@ -66654,7 +66716,7 @@ function createServer2(root, sessions, version22, level) {
|
|
|
66654
66716
|
);
|
|
66655
66717
|
return server;
|
|
66656
66718
|
}
|
|
66657
|
-
var VERSION = "0.0.
|
|
66719
|
+
var VERSION = "0.0.7";
|
|
66658
66720
|
function createAgentExec(options) {
|
|
66659
66721
|
const root = realpathSync(resolve3(options.workspace));
|
|
66660
66722
|
const level = options.level ?? "read";
|
|
@@ -66671,9 +66733,15 @@ function createAgentExec(options) {
|
|
|
66671
66733
|
options.pty
|
|
66672
66734
|
);
|
|
66673
66735
|
const host = createHostFiles(root, level);
|
|
66736
|
+
const extraTools = [...options.extraTools ?? []];
|
|
66737
|
+
const names = new Set(TOOLS);
|
|
66738
|
+
for (const { name } of extraTools) {
|
|
66739
|
+
if (names.has(name)) throw new Error(`There is already a tool called ${name}.`);
|
|
66740
|
+
names.add(name);
|
|
66741
|
+
}
|
|
66674
66742
|
return {
|
|
66675
66743
|
async handle(request, response, body) {
|
|
66676
|
-
const server = createServer2(root, sessions, VERSION, level);
|
|
66744
|
+
const server = createServer2(root, sessions, VERSION, level, extraTools);
|
|
66677
66745
|
const transport = new NodeStreamableHTTPServerTransport({ sessionIdGenerator: void 0 });
|
|
66678
66746
|
response.once("close", () => {
|
|
66679
66747
|
void transport.close();
|
|
@@ -66703,6 +66771,48 @@ function createAgentExec(options) {
|
|
|
66703
66771
|
};
|
|
66704
66772
|
}
|
|
66705
66773
|
|
|
66774
|
+
// src/about.ts
|
|
66775
|
+
var REACHES = {
|
|
66776
|
+
read: "Read-only files",
|
|
66777
|
+
files: "Files",
|
|
66778
|
+
terminal: "Files and terminal"
|
|
66779
|
+
};
|
|
66780
|
+
var GIB = 1024 ** 3;
|
|
66781
|
+
var GB = 1e3 ** 3;
|
|
66782
|
+
var tenths = (count) => String(Math.round(count * 10) / 10);
|
|
66783
|
+
var whole = (count) => String(Math.round(count));
|
|
66784
|
+
function aboutTool(facts, load) {
|
|
66785
|
+
return {
|
|
66786
|
+
name: "about",
|
|
66787
|
+
description: 'What this computer is, in the words a person looking at it reads: where it is, its folder, what it reaches, how it is isolated and how loaded it is. Takes nothing. Answers JSON text: {"rows": [{"label": "Where", "value": "..."}]}, with "path": true on a value that is a path and "caution": true on one to be read as a caution.',
|
|
66788
|
+
readOnly: true,
|
|
66789
|
+
async call() {
|
|
66790
|
+
const { outside, inside: inside2 } = facts.workspace;
|
|
66791
|
+
const rows = [
|
|
66792
|
+
{ label: "Where", value: facts.machine },
|
|
66793
|
+
// In a container its commands find the folder somewhere else, so the row says both.
|
|
66794
|
+
inside2 === outside ? { label: "Folder", value: outside, path: true } : { label: "Folder", value: `${outside} on the machine, mounted at ${inside2} in it` },
|
|
66795
|
+
{ label: "Reaches", value: REACHES[facts.access] + (facts.browser ? ", Browser" : "") },
|
|
66796
|
+
facts.contained ? { label: "Isolation", value: "A container of its own" } : { label: "Isolation", value: "None: it acts as you", caution: true }
|
|
66797
|
+
];
|
|
66798
|
+
const reading = await load.now();
|
|
66799
|
+
if (reading)
|
|
66800
|
+
rows.push(
|
|
66801
|
+
{ label: "CPU", value: `${whole(reading.cpuUsedPct)}%` },
|
|
66802
|
+
{
|
|
66803
|
+
label: "Memory",
|
|
66804
|
+
value: `${tenths(reading.memUsed / GIB)} of ${tenths(reading.memTotal / GIB)} GB`
|
|
66805
|
+
},
|
|
66806
|
+
{
|
|
66807
|
+
label: "Disk",
|
|
66808
|
+
value: `${whole(reading.diskUsed / GB)} of ${whole(reading.diskTotal / GB)} GB`
|
|
66809
|
+
}
|
|
66810
|
+
);
|
|
66811
|
+
return JSON.stringify({ rows });
|
|
66812
|
+
}
|
|
66813
|
+
};
|
|
66814
|
+
}
|
|
66815
|
+
|
|
66706
66816
|
// src/screencast.ts
|
|
66707
66817
|
var FIRST_FRAME_MS = 500;
|
|
66708
66818
|
var RESTARTS = 3;
|
|
@@ -67123,7 +67233,7 @@ var MAX_BUFFERED2 = 4 * 1024 * 1024;
|
|
|
67123
67233
|
var MAX_UPLOADS = 64;
|
|
67124
67234
|
var reason2 = (error62) => error62 instanceof Error ? error62.message : String(error62);
|
|
67125
67235
|
var text2 = (value) => typeof value === "string";
|
|
67126
|
-
var
|
|
67236
|
+
var whole2 = (value) => Number.isSafeInteger(value);
|
|
67127
67237
|
var sleep4 = (ms) => new Promise((done) => setTimeout(done, ms));
|
|
67128
67238
|
var FilesLive = class {
|
|
67129
67239
|
#files;
|
|
@@ -67242,7 +67352,7 @@ var FilesLive = class {
|
|
|
67242
67352
|
awaited = "refused";
|
|
67243
67353
|
if (mode !== "operate") throw new Error("This socket watches: it can't write.");
|
|
67244
67354
|
const { path, offset, length, last } = request;
|
|
67245
|
-
if (!text2(path) || !
|
|
67355
|
+
if (!text2(path) || !whole2(offset) || offset < 0 || !whole2(length) || length < 0 || length > MAX_PART || typeof last !== "boolean")
|
|
67246
67356
|
throw new Error(
|
|
67247
67357
|
"A write names a path, where it starts, 0 to 1,048,576 bytes, and last."
|
|
67248
67358
|
);
|
|
@@ -67439,6 +67549,7 @@ import { readFile as readFile2 } from "node:fs/promises";
|
|
|
67439
67549
|
import { availableParallelism, cpus, freemem, totalmem } from "node:os";
|
|
67440
67550
|
import { promisify } from "node:util";
|
|
67441
67551
|
var EVERY_MS = 2e3;
|
|
67552
|
+
var ONCE_MS = 500;
|
|
67442
67553
|
var run = promisify(execFile);
|
|
67443
67554
|
var numberOf = (text3, name) => Number(new RegExp(`^${name}\\s+(\\d+)`, "m").exec(text3)?.[1]);
|
|
67444
67555
|
async function disk(folder) {
|
|
@@ -67477,6 +67588,18 @@ async function cgroup() {
|
|
|
67477
67588
|
memUsed: Number(current) - numberOf(memory, "inactive_file")
|
|
67478
67589
|
};
|
|
67479
67590
|
}
|
|
67591
|
+
function readingOf(before, sample, volume) {
|
|
67592
|
+
const share = (sample.cpu.used - before.used) / (sample.cpu.total - before.total) || 0;
|
|
67593
|
+
const reading = {
|
|
67594
|
+
type: "reading",
|
|
67595
|
+
scope: sample.scope,
|
|
67596
|
+
cpuUsedPct: Math.round(Math.min(Math.max(share, 0), 1) * 1e3) / 10,
|
|
67597
|
+
memUsed: sample.memUsed,
|
|
67598
|
+
memTotal: totalmem(),
|
|
67599
|
+
...volume
|
|
67600
|
+
};
|
|
67601
|
+
return Number.isFinite(reading.cpuUsedPct + reading.memUsed) ? reading : void 0;
|
|
67602
|
+
}
|
|
67480
67603
|
var LoadLive = class {
|
|
67481
67604
|
#folder;
|
|
67482
67605
|
#contained;
|
|
@@ -67510,28 +67633,33 @@ var LoadLive = class {
|
|
|
67510
67633
|
this.#timer = this.#before = this.#last = void 0;
|
|
67511
67634
|
for (const ws of this.#sockets) ws.close(1001, "It stopped.");
|
|
67512
67635
|
}
|
|
67636
|
+
/** Its own, inside a container that can read it; else the machine's. */
|
|
67637
|
+
#sample() {
|
|
67638
|
+
return this.#contained ? cgroup().catch(machine) : machine();
|
|
67639
|
+
}
|
|
67640
|
+
/** One reading for whoever asks once, taken over half a second. None where it can't be. */
|
|
67641
|
+
async now() {
|
|
67642
|
+
try {
|
|
67643
|
+
const before = await this.#sample();
|
|
67644
|
+
await new Promise((done) => setTimeout(done, ONCE_MS));
|
|
67645
|
+
return readingOf(before.cpu, await this.#sample(), await disk(this.#folder));
|
|
67646
|
+
} catch {
|
|
67647
|
+
return void 0;
|
|
67648
|
+
}
|
|
67649
|
+
}
|
|
67513
67650
|
/** One at a time, and for the watching it began under: a reading taken too late is left. */
|
|
67514
67651
|
async #read() {
|
|
67515
67652
|
const timer = this.#timer;
|
|
67516
67653
|
if (this.#reading) return;
|
|
67517
67654
|
this.#reading = true;
|
|
67518
67655
|
try {
|
|
67519
|
-
const sample = await
|
|
67656
|
+
const sample = await this.#sample();
|
|
67520
67657
|
const volume = await disk(this.#folder);
|
|
67521
67658
|
if (this.#timer !== timer) return;
|
|
67522
67659
|
const before = this.#before;
|
|
67523
67660
|
this.#before = sample.cpu;
|
|
67524
|
-
|
|
67525
|
-
|
|
67526
|
-
const reading = {
|
|
67527
|
-
type: "reading",
|
|
67528
|
-
scope: sample.scope,
|
|
67529
|
-
cpuUsedPct: Math.round(Math.min(Math.max(share, 0), 1) * 1e3) / 10,
|
|
67530
|
-
memUsed: sample.memUsed,
|
|
67531
|
-
memTotal: totalmem(),
|
|
67532
|
-
...volume
|
|
67533
|
-
};
|
|
67534
|
-
if (!Number.isFinite(reading.cpuUsedPct + reading.memUsed)) return;
|
|
67661
|
+
const reading = before && readingOf(before, sample, volume);
|
|
67662
|
+
if (!reading) return;
|
|
67535
67663
|
this.#last = JSON.stringify(reading);
|
|
67536
67664
|
for (const ws of this.#sockets) if (ws.bufferedAmount === 0) ws.send(this.#last);
|
|
67537
67665
|
} catch {
|
|
@@ -67610,7 +67738,7 @@ var ProcessRuntime = class {
|
|
|
67610
67738
|
isolation = "none";
|
|
67611
67739
|
#config;
|
|
67612
67740
|
#contained;
|
|
67613
|
-
/** `contained` is for inside a container, where load
|
|
67741
|
+
/** `contained` is for inside a container, where the computer and its load are its own. */
|
|
67614
67742
|
constructor(config3, contained = false) {
|
|
67615
67743
|
this.#config = config3;
|
|
67616
67744
|
this.#contained = contained;
|
|
@@ -67622,13 +67750,27 @@ var ProcessRuntime = class {
|
|
|
67622
67750
|
async remove() {
|
|
67623
67751
|
}
|
|
67624
67752
|
async start(spec, folder, events) {
|
|
67753
|
+
const load = new LoadLive(spec.workspace ?? folder, this.#contained);
|
|
67625
67754
|
let exec;
|
|
67626
|
-
if (spec.access !== "none")
|
|
67755
|
+
if (spec.access !== "none") {
|
|
67756
|
+
const workspace = spec.workspace;
|
|
67757
|
+
const about = aboutTool(
|
|
67758
|
+
{
|
|
67759
|
+
machine: spec.outside?.machine ?? hostname3(),
|
|
67760
|
+
workspace: { outside: spec.outside?.workspace ?? workspace, inside: workspace },
|
|
67761
|
+
access: spec.access,
|
|
67762
|
+
browser: spec.browser,
|
|
67763
|
+
contained: this.#contained
|
|
67764
|
+
},
|
|
67765
|
+
load
|
|
67766
|
+
);
|
|
67627
67767
|
exec = createAgentExec({
|
|
67628
|
-
workspace
|
|
67768
|
+
workspace,
|
|
67629
67769
|
level: spec.access,
|
|
67630
|
-
pty: await loadPty()
|
|
67770
|
+
pty: await loadPty(),
|
|
67771
|
+
extraTools: [about]
|
|
67631
67772
|
});
|
|
67773
|
+
}
|
|
67632
67774
|
let browser;
|
|
67633
67775
|
let browserDown = false;
|
|
67634
67776
|
if (spec.browser) {
|
|
@@ -67656,8 +67798,7 @@ var ProcessRuntime = class {
|
|
|
67656
67798
|
browserLive = new BrowserLive(browser.endpoint);
|
|
67657
67799
|
browser.gate = browserLive;
|
|
67658
67800
|
}
|
|
67659
|
-
const
|
|
67660
|
-
const files = exec && new FilesLive(exec.files, spec.workspaceName ?? basename2(spec.workspace));
|
|
67801
|
+
const files = exec && new FilesLive(exec.files, basename2(spec.outside?.workspace ?? spec.workspace));
|
|
67661
67802
|
const live = new Live(exec, spec.access, load, files, browserLive);
|
|
67662
67803
|
const surfaces = live.surfaces();
|
|
67663
67804
|
return {
|
|
@@ -67688,7 +67829,8 @@ var ProcessRuntime = class {
|
|
|
67688
67829
|
// src/inside.ts
|
|
67689
67830
|
var INNER_PORT = 8e3;
|
|
67690
67831
|
var INNER_TOKEN_VARIABLE = "AGENTCOMPUTER_INNER_TOKEN";
|
|
67691
|
-
var
|
|
67832
|
+
var OUTSIDE_WORKSPACE_VARIABLE = "AGENTCOMPUTER_OUTSIDE_WORKSPACE";
|
|
67833
|
+
var OUTSIDE_MACHINE_VARIABLE = "AGENTCOMPUTER_OUTSIDE_MACHINE";
|
|
67692
67834
|
var BROWSER = "/opt/browsers/headless-shell";
|
|
67693
67835
|
async function serveInside(options) {
|
|
67694
67836
|
const token = process.env[INNER_TOKEN_VARIABLE];
|
|
@@ -67699,7 +67841,11 @@ async function serveInside(options) {
|
|
|
67699
67841
|
{
|
|
67700
67842
|
id: "inside",
|
|
67701
67843
|
...options,
|
|
67702
|
-
...options.access === "none" ? {} : { workspace: "/workspace"
|
|
67844
|
+
...options.access === "none" ? {} : { workspace: "/workspace" },
|
|
67845
|
+
outside: {
|
|
67846
|
+
workspace: process.env[OUTSIDE_WORKSPACE_VARIABLE],
|
|
67847
|
+
machine: process.env[OUTSIDE_MACHINE_VARIABLE]
|
|
67848
|
+
}
|
|
67703
67849
|
},
|
|
67704
67850
|
privateFolder(join7(homedir(), ".agentcomputer")),
|
|
67705
67851
|
{
|
|
@@ -67810,6 +67956,10 @@ var ContainerRuntime = class {
|
|
|
67810
67956
|
}
|
|
67811
67957
|
async #create(name, spec, token) {
|
|
67812
67958
|
const uid = process.getuid?.();
|
|
67959
|
+
const told = {
|
|
67960
|
+
[INNER_TOKEN_VARIABLE]: token,
|
|
67961
|
+
...spec.workspace ? { [OUTSIDE_WORKSPACE_VARIABLE]: spec.workspace, [OUTSIDE_MACHINE_VARIABLE]: hostname4() } : {}
|
|
67962
|
+
};
|
|
67813
67963
|
await docker(
|
|
67814
67964
|
[
|
|
67815
67965
|
"create",
|
|
@@ -67824,12 +67974,8 @@ var ContainerRuntime = class {
|
|
|
67824
67974
|
"no-new-privileges",
|
|
67825
67975
|
"--publish",
|
|
67826
67976
|
`127.0.0.1::${INNER_PORT}`,
|
|
67827
|
-
// By name, so the
|
|
67828
|
-
"--env",
|
|
67829
|
-
INNER_TOKEN_VARIABLE,
|
|
67830
|
-
// What its workspace is called, which in there is only /workspace. An image from before
|
|
67831
|
-
// this was told leaves it.
|
|
67832
|
-
...spec.workspace ? ["--env", WORKSPACE_NAME_VARIABLE] : [],
|
|
67977
|
+
// By name, so the values stay out of the command line other users can see.
|
|
67978
|
+
...Object.keys(told).flatMap((name2) => ["--env", name2]),
|
|
67833
67979
|
// Files it writes in the workspace belong to whoever runs the service.
|
|
67834
67980
|
...uid ? ["--user", `${uid}:${process.getgid?.()}`] : [],
|
|
67835
67981
|
...spec.workspace ? [
|
|
@@ -67842,11 +67988,7 @@ var ContainerRuntime = class {
|
|
|
67842
67988
|
spec.access,
|
|
67843
67989
|
...spec.browser ? ["--browser"] : []
|
|
67844
67990
|
],
|
|
67845
|
-
{
|
|
67846
|
-
...process.env,
|
|
67847
|
-
[INNER_TOKEN_VARIABLE]: token,
|
|
67848
|
-
...spec.workspace ? { [WORKSPACE_NAME_VARIABLE]: basename3(spec.workspace) } : {}
|
|
67849
|
-
}
|
|
67991
|
+
{ ...process.env, ...told }
|
|
67850
67992
|
);
|
|
67851
67993
|
}
|
|
67852
67994
|
/** The token between the service and this container, which a restart has to find again. */
|
|
@@ -67990,7 +68132,7 @@ async function forward(url3, authorization, request, response) {
|
|
|
67990
68132
|
}
|
|
67991
68133
|
|
|
67992
68134
|
// src/handlers.ts
|
|
67993
|
-
var VERSION2 = "0.0.
|
|
68135
|
+
var VERSION2 = "0.0.13";
|
|
67994
68136
|
function createHandlers(computers) {
|
|
67995
68137
|
return {
|
|
67996
68138
|
getInfo: async () => ({ version: VERSION2, isolation: computers.isolation }),
|
|
@@ -68574,8 +68716,8 @@ async function openLive(session, onMessage) {
|
|
|
68574
68716
|
}
|
|
68575
68717
|
var Terminals = class {
|
|
68576
68718
|
#open;
|
|
68577
|
-
constructor(
|
|
68578
|
-
this.#open =
|
|
68719
|
+
constructor(open5) {
|
|
68720
|
+
this.#open = open5;
|
|
68579
68721
|
}
|
|
68580
68722
|
/**
|
|
68581
68723
|
* Each terminal as it starts, writes and exits: those already there first, each with the end of
|
|
@@ -84631,10 +84773,12 @@ var NodeStreamableHTTPServerTransport2 = class {
|
|
|
84631
84773
|
import { randomBytes as randomBytes5 } from "node:crypto";
|
|
84632
84774
|
import { appendFileSync, existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync as readFileSync4, rmSync as rmSync2, statSync as statSync3 } from "node:fs";
|
|
84633
84775
|
import { isAbsolute, join as join9, resolve as resolve4 } from "node:path";
|
|
84634
|
-
var
|
|
84635
|
-
|
|
84636
|
-
|
|
84637
|
-
|
|
84776
|
+
var READ = ["about", "read", "list", "search"];
|
|
84777
|
+
var FILES = [...READ, "edit"];
|
|
84778
|
+
var TOOLS2 = {
|
|
84779
|
+
read: READ,
|
|
84780
|
+
files: FILES,
|
|
84781
|
+
terminal: [...FILES, "exec_command", "write_stdin", "list_sessions"]
|
|
84638
84782
|
};
|
|
84639
84783
|
var MAX_LOG = 256 * 1024;
|
|
84640
84784
|
var MAX_TIMER_S = 2e6;
|
|
@@ -84733,7 +84877,7 @@ var Computers = class {
|
|
|
84733
84877
|
...this.#describe(stored),
|
|
84734
84878
|
uptimeSeconds: stored.state === "running" ? Math.round((Date.now() - Date.parse(stored.startedAt)) / 1e3) : 0,
|
|
84735
84879
|
tools: {
|
|
84736
|
-
computer: stored.access === "none" ? [] :
|
|
84880
|
+
computer: stored.access === "none" ? [] : TOOLS2[stored.access],
|
|
84737
84881
|
browser: live?.running?.browserTools ?? []
|
|
84738
84882
|
},
|
|
84739
84883
|
surfaces: live?.running?.surfaces ?? []
|
|
@@ -84953,7 +85097,7 @@ var OPENAPI = {
|
|
|
84953
85097
|
"openapi": "3.1.0",
|
|
84954
85098
|
"info": {
|
|
84955
85099
|
"title": "AgentComputer",
|
|
84956
|
-
"version": "0.0.
|
|
85100
|
+
"version": "0.0.13",
|
|
84957
85101
|
"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.",
|
|
84958
85102
|
"license": {
|
|
84959
85103
|
"name": "Apache-2.0",
|
|
@@ -85405,7 +85549,7 @@ var OPENAPI = {
|
|
|
85405
85549
|
"files",
|
|
85406
85550
|
"terminal"
|
|
85407
85551
|
],
|
|
85408
|
-
"description": "How far it reaches into files and commands, each level containing the one before: `read` has read, list and search; `files` adds edit; `terminal` adds exec_command, write_stdin and list_sessions. Below terminal, the browser's code-running tool is hidden."
|
|
85552
|
+
"description": "How far it reaches into files and commands, each level containing the one before: `read` has about, read, list and search; `files` adds edit; `terminal` adds exec_command, write_stdin and list_sessions. Below terminal, the browser's code-running tool is hidden."
|
|
85409
85553
|
},
|
|
85410
85554
|
"ComputerState": {
|
|
85411
85555
|
"type": "string",
|
|
@@ -85618,7 +85762,7 @@ var OPENAPI = {
|
|
|
85618
85762
|
"browser",
|
|
85619
85763
|
"load"
|
|
85620
85764
|
],
|
|
85621
|
-
"description": "terminal: every terminal on it, the agent's commands and a person's shells alike, on one socket in terminal@1: text messages are TerminalEvent one way and TerminalInput the other. Watching reads them all; operating also opens a shell, types into any of them, the agent's included, sizes and stops them. files: its workspace's files, in files@1: text messages are FilesRequest one way and FilesAnswer the other, and a file's bytes travel as binary messages of at most 1,048,576 bytes. Watching lists and reads; operating also writes, which a computer with read access doesn't offer. browser: its browser, in screencast@1: binary messages are JPEG frames of the page shown, text messages are ScreencastEvent one way and ScreencastInput the other. Nobody waits for anybody: the agent's tools and a person's input act on the same browser at once. The page shown is each socket's own. A socket is shown each new tab as it opens, until it sends a tab input to show or open one; from then its page changes only when that page closes. load: how busy it is, in metrics@1, to watch only: a LoadReading every two seconds while a socket is open, the first two seconds after the first socket opens, and the last one at once to a socket that opens later. One that can't be taken is left out, and a socket that hasn't read the one before is not sent the next. Nothing is read while nobody watches."
|
|
85765
|
+
"description": "terminal: every terminal on it, the agent's commands and a person's shells alike, on one socket in terminal@1: text messages are TerminalEvent one way and TerminalInput the other. Watching reads them all; operating also opens a shell, types into any of them, the agent's included, sizes and stops them. files: its workspace's files, in files@1: text messages are FilesRequest one way and FilesAnswer the other, and a file's bytes travel as binary messages of at most 1,048,576 bytes. Watching lists and reads; operating also writes, which a computer with read access doesn't offer. browser: its browser, in screencast@1: binary messages are JPEG frames of the page shown, text messages are ScreencastEvent one way and ScreencastInput the other. Nobody waits for anybody: the agent's tools and a person's input act on the same browser at once. The page shown is each socket's own. A socket is shown each new tab as it opens, until it sends a tab input to show or open one; from then its page changes only when that page closes. load: how busy it is, in metrics@1, to watch only: a LoadReading every two seconds while a socket is open, the first two seconds after the first socket opens, and the last one at once to a socket that opens later. One that can't be taken is left out, and a socket that hasn't read the one before is not sent the next. Nothing is read while nobody watches or asks."
|
|
85622
85766
|
},
|
|
85623
85767
|
"LiveMode": {
|
|
85624
85768
|
"type": "string",
|
|
@@ -86795,7 +86939,7 @@ var OPERATIONS = [
|
|
|
86795
86939
|
"files",
|
|
86796
86940
|
"terminal"
|
|
86797
86941
|
],
|
|
86798
|
-
"description": "How far it reaches into files and commands, each level containing the one before: `read` has read, list and search; `files` adds edit; `terminal` adds exec_command, write_stdin and list_sessions. Below terminal, the browser's code-running tool is hidden."
|
|
86942
|
+
"description": "How far it reaches into files and commands, each level containing the one before: `read` has about, read, list and search; `files` adds edit; `terminal` adds exec_command, write_stdin and list_sessions. Below terminal, the browser's code-running tool is hidden."
|
|
86799
86943
|
},
|
|
86800
86944
|
"browser": {
|
|
86801
86945
|
"type": "boolean"
|
|
@@ -86884,7 +87028,7 @@ var OPERATIONS = [
|
|
|
86884
87028
|
"files",
|
|
86885
87029
|
"terminal"
|
|
86886
87030
|
],
|
|
86887
|
-
"description": "How far it reaches into files and commands, each level containing the one before: `read` has read, list and search; `files` adds edit; `terminal` adds exec_command, write_stdin and list_sessions. Below terminal, the browser's code-running tool is hidden."
|
|
87031
|
+
"description": "How far it reaches into files and commands, each level containing the one before: `read` has about, read, list and search; `files` adds edit; `terminal` adds exec_command, write_stdin and list_sessions. Below terminal, the browser's code-running tool is hidden."
|
|
86888
87032
|
},
|
|
86889
87033
|
"browser": {
|
|
86890
87034
|
"type": "boolean",
|
|
@@ -86954,7 +87098,7 @@ var OPERATIONS = [
|
|
|
86954
87098
|
"files",
|
|
86955
87099
|
"terminal"
|
|
86956
87100
|
],
|
|
86957
|
-
"description": "How far it reaches into files and commands, each level containing the one before: `read` has read, list and search; `files` adds edit; `terminal` adds exec_command, write_stdin and list_sessions. Below terminal, the browser's code-running tool is hidden."
|
|
87101
|
+
"description": "How far it reaches into files and commands, each level containing the one before: `read` has about, read, list and search; `files` adds edit; `terminal` adds exec_command, write_stdin and list_sessions. Below terminal, the browser's code-running tool is hidden."
|
|
86958
87102
|
},
|
|
86959
87103
|
"browser": {
|
|
86960
87104
|
"type": "boolean"
|
|
@@ -87094,7 +87238,7 @@ var OPERATIONS = [
|
|
|
87094
87238
|
"files",
|
|
87095
87239
|
"terminal"
|
|
87096
87240
|
],
|
|
87097
|
-
"description": "How far it reaches into files and commands, each level containing the one before: `read` has read, list and search; `files` adds edit; `terminal` adds exec_command, write_stdin and list_sessions. Below terminal, the browser's code-running tool is hidden."
|
|
87241
|
+
"description": "How far it reaches into files and commands, each level containing the one before: `read` has about, read, list and search; `files` adds edit; `terminal` adds exec_command, write_stdin and list_sessions. Below terminal, the browser's code-running tool is hidden."
|
|
87098
87242
|
},
|
|
87099
87243
|
"browser": {
|
|
87100
87244
|
"type": "boolean"
|
|
@@ -87183,7 +87327,7 @@ var OPERATIONS = [
|
|
|
87183
87327
|
"browser",
|
|
87184
87328
|
"load"
|
|
87185
87329
|
],
|
|
87186
|
-
"description": "terminal: every terminal on it, the agent's commands and a person's shells alike, on one socket in terminal@1: text messages are TerminalEvent one way and TerminalInput the other. Watching reads them all; operating also opens a shell, types into any of them, the agent's included, sizes and stops them. files: its workspace's files, in files@1: text messages are FilesRequest one way and FilesAnswer the other, and a file's bytes travel as binary messages of at most 1,048,576 bytes. Watching lists and reads; operating also writes, which a computer with read access doesn't offer. browser: its browser, in screencast@1: binary messages are JPEG frames of the page shown, text messages are ScreencastEvent one way and ScreencastInput the other. Nobody waits for anybody: the agent's tools and a person's input act on the same browser at once. The page shown is each socket's own. A socket is shown each new tab as it opens, until it sends a tab input to show or open one; from then its page changes only when that page closes. load: how busy it is, in metrics@1, to watch only: a LoadReading every two seconds while a socket is open, the first two seconds after the first socket opens, and the last one at once to a socket that opens later. One that can't be taken is left out, and a socket that hasn't read the one before is not sent the next. Nothing is read while nobody watches."
|
|
87330
|
+
"description": "terminal: every terminal on it, the agent's commands and a person's shells alike, on one socket in terminal@1: text messages are TerminalEvent one way and TerminalInput the other. Watching reads them all; operating also opens a shell, types into any of them, the agent's included, sizes and stops them. files: its workspace's files, in files@1: text messages are FilesRequest one way and FilesAnswer the other, and a file's bytes travel as binary messages of at most 1,048,576 bytes. Watching lists and reads; operating also writes, which a computer with read access doesn't offer. browser: its browser, in screencast@1: binary messages are JPEG frames of the page shown, text messages are ScreencastEvent one way and ScreencastInput the other. Nobody waits for anybody: the agent's tools and a person's input act on the same browser at once. The page shown is each socket's own. A socket is shown each new tab as it opens, until it sends a tab input to show or open one; from then its page changes only when that page closes. load: how busy it is, in metrics@1, to watch only: a LoadReading every two seconds while a socket is open, the first two seconds after the first socket opens, and the last one at once to a socket that opens later. One that can't be taken is left out, and a socket that hasn't read the one before is not sent the next. Nothing is read while nobody watches or asks."
|
|
87187
87331
|
},
|
|
87188
87332
|
"protocol": {
|
|
87189
87333
|
"type": "string"
|
|
@@ -87325,7 +87469,7 @@ var OPERATIONS = [
|
|
|
87325
87469
|
"files",
|
|
87326
87470
|
"terminal"
|
|
87327
87471
|
],
|
|
87328
|
-
"description": "How far it reaches into files and commands, each level containing the one before: `read` has read, list and search; `files` adds edit; `terminal` adds exec_command, write_stdin and list_sessions. Below terminal, the browser's code-running tool is hidden."
|
|
87472
|
+
"description": "How far it reaches into files and commands, each level containing the one before: `read` has about, read, list and search; `files` adds edit; `terminal` adds exec_command, write_stdin and list_sessions. Below terminal, the browser's code-running tool is hidden."
|
|
87329
87473
|
},
|
|
87330
87474
|
"browser": {
|
|
87331
87475
|
"type": "boolean"
|
|
@@ -87449,7 +87593,7 @@ var OPERATIONS = [
|
|
|
87449
87593
|
"files",
|
|
87450
87594
|
"terminal"
|
|
87451
87595
|
],
|
|
87452
|
-
"description": "How far it reaches into files and commands, each level containing the one before: `read` has read, list and search; `files` adds edit; `terminal` adds exec_command, write_stdin and list_sessions. Below terminal, the browser's code-running tool is hidden."
|
|
87596
|
+
"description": "How far it reaches into files and commands, each level containing the one before: `read` has about, read, list and search; `files` adds edit; `terminal` adds exec_command, write_stdin and list_sessions. Below terminal, the browser's code-running tool is hidden."
|
|
87453
87597
|
},
|
|
87454
87598
|
"browser": {
|
|
87455
87599
|
"type": "boolean"
|
|
@@ -87578,7 +87722,7 @@ var OPERATIONS = [
|
|
|
87578
87722
|
"files",
|
|
87579
87723
|
"terminal"
|
|
87580
87724
|
],
|
|
87581
|
-
"description": "How far it reaches into files and commands, each level containing the one before: `read` has read, list and search; `files` adds edit; `terminal` adds exec_command, write_stdin and list_sessions. Below terminal, the browser's code-running tool is hidden."
|
|
87725
|
+
"description": "How far it reaches into files and commands, each level containing the one before: `read` has about, read, list and search; `files` adds edit; `terminal` adds exec_command, write_stdin and list_sessions. Below terminal, the browser's code-running tool is hidden."
|
|
87582
87726
|
},
|
|
87583
87727
|
"browser": {
|
|
87584
87728
|
"type": "boolean"
|
|
@@ -87723,7 +87867,7 @@ var OPERATIONS = [
|
|
|
87723
87867
|
"browser",
|
|
87724
87868
|
"load"
|
|
87725
87869
|
],
|
|
87726
|
-
"description": "terminal: every terminal on it, the agent's commands and a person's shells alike, on one socket in terminal@1: text messages are TerminalEvent one way and TerminalInput the other. Watching reads them all; operating also opens a shell, types into any of them, the agent's included, sizes and stops them. files: its workspace's files, in files@1: text messages are FilesRequest one way and FilesAnswer the other, and a file's bytes travel as binary messages of at most 1,048,576 bytes. Watching lists and reads; operating also writes, which a computer with read access doesn't offer. browser: its browser, in screencast@1: binary messages are JPEG frames of the page shown, text messages are ScreencastEvent one way and ScreencastInput the other. Nobody waits for anybody: the agent's tools and a person's input act on the same browser at once. The page shown is each socket's own. A socket is shown each new tab as it opens, until it sends a tab input to show or open one; from then its page changes only when that page closes. load: how busy it is, in metrics@1, to watch only: a LoadReading every two seconds while a socket is open, the first two seconds after the first socket opens, and the last one at once to a socket that opens later. One that can't be taken is left out, and a socket that hasn't read the one before is not sent the next. Nothing is read while nobody watches."
|
|
87870
|
+
"description": "terminal: every terminal on it, the agent's commands and a person's shells alike, on one socket in terminal@1: text messages are TerminalEvent one way and TerminalInput the other. Watching reads them all; operating also opens a shell, types into any of them, the agent's included, sizes and stops them. files: its workspace's files, in files@1: text messages are FilesRequest one way and FilesAnswer the other, and a file's bytes travel as binary messages of at most 1,048,576 bytes. Watching lists and reads; operating also writes, which a computer with read access doesn't offer. browser: its browser, in screencast@1: binary messages are JPEG frames of the page shown, text messages are ScreencastEvent one way and ScreencastInput the other. Nobody waits for anybody: the agent's tools and a person's input act on the same browser at once. The page shown is each socket's own. A socket is shown each new tab as it opens, until it sends a tab input to show or open one; from then its page changes only when that page closes. load: how busy it is, in metrics@1, to watch only: a LoadReading every two seconds while a socket is open, the first two seconds after the first socket opens, and the last one at once to a socket that opens later. One that can't be taken is left out, and a socket that hasn't read the one before is not sent the next. Nothing is read while nobody watches or asks."
|
|
87727
87871
|
},
|
|
87728
87872
|
"mode": {
|
|
87729
87873
|
"allOf": [
|
|
@@ -87770,7 +87914,7 @@ var OPERATIONS = [
|
|
|
87770
87914
|
"browser",
|
|
87771
87915
|
"load"
|
|
87772
87916
|
],
|
|
87773
|
-
"description": "terminal: every terminal on it, the agent's commands and a person's shells alike, on one socket in terminal@1: text messages are TerminalEvent one way and TerminalInput the other. Watching reads them all; operating also opens a shell, types into any of them, the agent's included, sizes and stops them. files: its workspace's files, in files@1: text messages are FilesRequest one way and FilesAnswer the other, and a file's bytes travel as binary messages of at most 1,048,576 bytes. Watching lists and reads; operating also writes, which a computer with read access doesn't offer. browser: its browser, in screencast@1: binary messages are JPEG frames of the page shown, text messages are ScreencastEvent one way and ScreencastInput the other. Nobody waits for anybody: the agent's tools and a person's input act on the same browser at once. The page shown is each socket's own. A socket is shown each new tab as it opens, until it sends a tab input to show or open one; from then its page changes only when that page closes. load: how busy it is, in metrics@1, to watch only: a LoadReading every two seconds while a socket is open, the first two seconds after the first socket opens, and the last one at once to a socket that opens later. One that can't be taken is left out, and a socket that hasn't read the one before is not sent the next. Nothing is read while nobody watches."
|
|
87917
|
+
"description": "terminal: every terminal on it, the agent's commands and a person's shells alike, on one socket in terminal@1: text messages are TerminalEvent one way and TerminalInput the other. Watching reads them all; operating also opens a shell, types into any of them, the agent's included, sizes and stops them. files: its workspace's files, in files@1: text messages are FilesRequest one way and FilesAnswer the other, and a file's bytes travel as binary messages of at most 1,048,576 bytes. Watching lists and reads; operating also writes, which a computer with read access doesn't offer. browser: its browser, in screencast@1: binary messages are JPEG frames of the page shown, text messages are ScreencastEvent one way and ScreencastInput the other. Nobody waits for anybody: the agent's tools and a person's input act on the same browser at once. The page shown is each socket's own. A socket is shown each new tab as it opens, until it sends a tab input to show or open one; from then its page changes only when that page closes. load: how busy it is, in metrics@1, to watch only: a LoadReading every two seconds while a socket is open, the first two seconds after the first socket opens, and the last one at once to a socket that opens later. One that can't be taken is left out, and a socket that hasn't read the one before is not sent the next. Nothing is read while nobody watches or asks."
|
|
87774
87918
|
},
|
|
87775
87919
|
"mode": {
|
|
87776
87920
|
"type": "string",
|
|
@@ -182,7 +182,7 @@ export interface components {
|
|
|
182
182
|
message: string;
|
|
183
183
|
};
|
|
184
184
|
/**
|
|
185
|
-
* @description How far it reaches into files and commands, each level containing the one before: `read` has read, list and search; `files` adds edit; `terminal` adds exec_command, write_stdin and list_sessions. Below terminal, the browser's code-running tool is hidden.
|
|
185
|
+
* @description How far it reaches into files and commands, each level containing the one before: `read` has about, read, list and search; `files` adds edit; `terminal` adds exec_command, write_stdin and list_sessions. Below terminal, the browser's code-running tool is hidden.
|
|
186
186
|
* @enum {string}
|
|
187
187
|
*/
|
|
188
188
|
Access: "none" | "read" | "files" | "terminal";
|
|
@@ -248,7 +248,7 @@ export interface components {
|
|
|
248
248
|
};
|
|
249
249
|
};
|
|
250
250
|
/**
|
|
251
|
-
* @description terminal: every terminal on it, the agent's commands and a person's shells alike, on one socket in terminal@1: text messages are TerminalEvent one way and TerminalInput the other. Watching reads them all; operating also opens a shell, types into any of them, the agent's included, sizes and stops them. files: its workspace's files, in files@1: text messages are FilesRequest one way and FilesAnswer the other, and a file's bytes travel as binary messages of at most 1,048,576 bytes. Watching lists and reads; operating also writes, which a computer with read access doesn't offer. browser: its browser, in screencast@1: binary messages are JPEG frames of the page shown, text messages are ScreencastEvent one way and ScreencastInput the other. Nobody waits for anybody: the agent's tools and a person's input act on the same browser at once. The page shown is each socket's own. A socket is shown each new tab as it opens, until it sends a tab input to show or open one; from then its page changes only when that page closes. load: how busy it is, in metrics@1, to watch only: a LoadReading every two seconds while a socket is open, the first two seconds after the first socket opens, and the last one at once to a socket that opens later. One that can't be taken is left out, and a socket that hasn't read the one before is not sent the next. Nothing is read while nobody watches.
|
|
251
|
+
* @description terminal: every terminal on it, the agent's commands and a person's shells alike, on one socket in terminal@1: text messages are TerminalEvent one way and TerminalInput the other. Watching reads them all; operating also opens a shell, types into any of them, the agent's included, sizes and stops them. files: its workspace's files, in files@1: text messages are FilesRequest one way and FilesAnswer the other, and a file's bytes travel as binary messages of at most 1,048,576 bytes. Watching lists and reads; operating also writes, which a computer with read access doesn't offer. browser: its browser, in screencast@1: binary messages are JPEG frames of the page shown, text messages are ScreencastEvent one way and ScreencastInput the other. Nobody waits for anybody: the agent's tools and a person's input act on the same browser at once. The page shown is each socket's own. A socket is shown each new tab as it opens, until it sends a tab input to show or open one; from then its page changes only when that page closes. load: how busy it is, in metrics@1, to watch only: a LoadReading every two seconds while a socket is open, the first two seconds after the first socket opens, and the last one at once to a socket that opens later. One that can't be taken is left out, and a socket that hasn't read the one before is not sent the next. Nothing is read while nobody watches or asks.
|
|
252
252
|
* @enum {string}
|
|
253
253
|
*/
|
|
254
254
|
Surface: "terminal" | "files" | "browser" | "load";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agentorg/computer",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.13",
|
|
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",
|