@agentorg/computer 0.0.10 → 0.0.12
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 +20 -4
- package/dist/THIRD_PARTY_LICENSES.txt +1 -1
- package/dist/agentcomputer.js +201 -73
- package/dist/types/generated/schema.d.ts +5 -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,27 @@ 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
|
+
`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
|
+
|
|
40
|
+
| `label` | `value` |
|
|
41
|
+
| ----------- | ----------------------------------------------------------------------------------------- |
|
|
42
|
+
| `Where` | The machine's own name |
|
|
43
|
+
| `Folder` | The workspace's path on that machine, as a path |
|
|
44
|
+
| `Reaches` | `Read-only files`, `Files`, or `Files and terminal`; with a browser, `, Browser` after it |
|
|
45
|
+
| `Isolation` | `None: it acts as you`, as a caution, or `A container of its own` |
|
|
46
|
+
| `CPU` | Used, of all the machine's CPUs, as a whole percentage: `37%` |
|
|
47
|
+
| `Memory` | Used of the machine's total, counted in 1024s and written GB: `9.2 of 16 GB` |
|
|
48
|
+
| `Disk` | Used of total, of the volume the workspace is on, counted in 1000s: `212 of 494 GB` |
|
|
49
|
+
|
|
50
|
+
- **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.
|
|
51
|
+
- **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
|
+
- **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
|
+
|
|
38
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.
|
|
39
55
|
|
|
40
56
|
`--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.
|
|
@@ -148,7 +164,7 @@ A computer at `--access terminal` has a surface you can open live, as a WebSocke
|
|
|
148
164
|
A computer at `--access read` or above has another, and at `read` it is the only one:
|
|
149
165
|
|
|
150
166
|
- **`files`** (protocol `files@1`): its workspace's files, for a file window. Text messages are JSON, and each request has an `id` of your choosing, which its answer repeats. `openapi.yaml` has each one's fields.
|
|
151
|
-
- **`list`** is answered with `entries`: a folder's entries with their kind, size and date, folders first, the first thousand.
|
|
167
|
+
- **`list`** is answered with `entries`: a folder's entries with their kind, size and date, folders first, the first thousand. With them come `path`, the folder as it was asked for, and `workspace`, what the workspace is called: its folder's own name, and nothing of where it is. The two together name the folder, `Research/src`; a workspace the service made is called `workspace`, and one at the top of a disk has an empty name. A computer in a container made before `workspace` was added sends none, and then `path` alone names the folder.
|
|
152
168
|
- **`read`** is answered with `data` and then one binary message, a range of a file's bytes. A file larger than one range is read by asking again from where the last ended.
|
|
153
169
|
- **`changed`** is answered with `changes`: the files modified after a time, the newest thousand, the newest first. It skips what `.gitignore` files ignore, `.git`, `node_modules` and links, and a file that was removed isn't one of them. Each one asked walks the workspace once, a computer walks for one at a time, and nothing is walked that wasn't asked for.
|
|
154
170
|
- **`write`**, on an `operate` socket, puts a file there a part at a time, each followed at once by one binary message, its bytes. Nothing is at the path until the part marked `last`, which puts the file there whole and is answered with `written`; the parts before it are answered only if one is refused. It makes no folders, and nothing removes or renames a file. A file not finished when the socket closes is given up.
|
|
@@ -169,7 +185,7 @@ Every running computer has one more, to watch only:
|
|
|
169
185
|
- **`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
186
|
- **`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
187
|
- **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.
|
|
188
|
+
- **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
189
|
|
|
174
190
|
`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
191
|
|
package/dist/agentcomputer.js
CHANGED
|
@@ -3849,6 +3849,7 @@ 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 { hostname as hostname4 } from "node:os";
|
|
3852
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";
|
|
@@ -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,7 +28159,8 @@ 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";
|
|
28161
|
-
import {
|
|
28162
|
+
import { hostname as hostname3 } from "node:os";
|
|
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
|
|
28164
28166
|
import { realpathSync } from "node:fs";
|
|
@@ -28915,11 +28917,11 @@ var require_ignore = __commonJS2({
|
|
|
28915
28917
|
const rules = this._rules;
|
|
28916
28918
|
const { length } = rules;
|
|
28917
28919
|
const shortcut = this._basenameCount * 2 >= length;
|
|
28918
|
-
const
|
|
28920
|
+
const basename22 = shortcut ? basenameOf(path) : path;
|
|
28919
28921
|
for (let index = length - 1; index >= 0; index--) {
|
|
28920
28922
|
const rule = rules[index];
|
|
28921
28923
|
if (rule[mode].test(
|
|
28922
|
-
shortcut && rule._basenameOnly ?
|
|
28924
|
+
shortcut && rule._basenameOnly ? basename22 : path
|
|
28923
28925
|
)) {
|
|
28924
28926
|
const { negative } = rule;
|
|
28925
28927
|
const ret = {
|
|
@@ -31928,9 +31930,9 @@ var asciiTabOrNewline2 = /[\t\n\r]/g;
|
|
|
31928
31930
|
function stripTabAndNewline2(value) {
|
|
31929
31931
|
return value.replace(asciiTabOrNewline2, "");
|
|
31930
31932
|
}
|
|
31931
|
-
function urlHostnameOk2(url22,
|
|
31932
|
-
|
|
31933
|
-
return
|
|
31933
|
+
function urlHostnameOk2(url22, hostname32) {
|
|
31934
|
+
hostname32.lastIndex = 0;
|
|
31935
|
+
return hostname32.test(url22.hostname);
|
|
31934
31936
|
}
|
|
31935
31937
|
function urlProtocolOk2(url22, protocol) {
|
|
31936
31938
|
protocol.lastIndex = 0;
|
|
@@ -66541,9 +66543,30 @@ var Answered = {
|
|
|
66541
66543
|
var YieldTime = external_exports.number().int().positive().optional();
|
|
66542
66544
|
var MaxTokens = external_exports.number().int().positive().optional();
|
|
66543
66545
|
var READ_ONLY = { readOnlyHint: true };
|
|
66544
|
-
|
|
66546
|
+
var TOOLS = [
|
|
66547
|
+
"exec_command",
|
|
66548
|
+
"write_stdin",
|
|
66549
|
+
"list_sessions",
|
|
66550
|
+
"read",
|
|
66551
|
+
"list",
|
|
66552
|
+
"search",
|
|
66553
|
+
"edit"
|
|
66554
|
+
];
|
|
66555
|
+
function createServer2(root, sessions, version22, level, extraTools = []) {
|
|
66545
66556
|
const server = new McpServer({ name: "agentexec", version: version22 });
|
|
66546
66557
|
const files = createFiles(root);
|
|
66558
|
+
for (const { name, description, readOnly, call } of extraTools)
|
|
66559
|
+
server.registerTool(
|
|
66560
|
+
name,
|
|
66561
|
+
{ description, ...readOnly ? { annotations: READ_ONLY } : {} },
|
|
66562
|
+
async () => {
|
|
66563
|
+
try {
|
|
66564
|
+
return text(String(await call()));
|
|
66565
|
+
} catch (error62) {
|
|
66566
|
+
return { isError: true, ...text(error62 instanceof Error ? error62.message : String(error62)) };
|
|
66567
|
+
}
|
|
66568
|
+
}
|
|
66569
|
+
);
|
|
66547
66570
|
if (level === "terminal") {
|
|
66548
66571
|
server.registerTool(
|
|
66549
66572
|
"exec_command",
|
|
@@ -66654,7 +66677,7 @@ function createServer2(root, sessions, version22, level) {
|
|
|
66654
66677
|
);
|
|
66655
66678
|
return server;
|
|
66656
66679
|
}
|
|
66657
|
-
var VERSION = "0.0.
|
|
66680
|
+
var VERSION = "0.0.6";
|
|
66658
66681
|
function createAgentExec(options) {
|
|
66659
66682
|
const root = realpathSync(resolve3(options.workspace));
|
|
66660
66683
|
const level = options.level ?? "read";
|
|
@@ -66671,9 +66694,15 @@ function createAgentExec(options) {
|
|
|
66671
66694
|
options.pty
|
|
66672
66695
|
);
|
|
66673
66696
|
const host = createHostFiles(root, level);
|
|
66697
|
+
const extraTools = [...options.extraTools ?? []];
|
|
66698
|
+
const names = new Set(TOOLS);
|
|
66699
|
+
for (const { name } of extraTools) {
|
|
66700
|
+
if (names.has(name)) throw new Error(`There is already a tool called ${name}.`);
|
|
66701
|
+
names.add(name);
|
|
66702
|
+
}
|
|
66674
66703
|
return {
|
|
66675
66704
|
async handle(request, response, body) {
|
|
66676
|
-
const server = createServer2(root, sessions, VERSION, level);
|
|
66705
|
+
const server = createServer2(root, sessions, VERSION, level, extraTools);
|
|
66677
66706
|
const transport = new NodeStreamableHTTPServerTransport({ sessionIdGenerator: void 0 });
|
|
66678
66707
|
response.once("close", () => {
|
|
66679
66708
|
void transport.close();
|
|
@@ -66703,6 +66732,48 @@ function createAgentExec(options) {
|
|
|
66703
66732
|
};
|
|
66704
66733
|
}
|
|
66705
66734
|
|
|
66735
|
+
// src/about.ts
|
|
66736
|
+
var REACHES = {
|
|
66737
|
+
read: "Read-only files",
|
|
66738
|
+
files: "Files",
|
|
66739
|
+
terminal: "Files and terminal"
|
|
66740
|
+
};
|
|
66741
|
+
var GIB = 1024 ** 3;
|
|
66742
|
+
var GB = 1e3 ** 3;
|
|
66743
|
+
var tenths = (count) => String(Math.round(count * 10) / 10);
|
|
66744
|
+
var whole = (count) => String(Math.round(count));
|
|
66745
|
+
function aboutTool(facts, load) {
|
|
66746
|
+
return {
|
|
66747
|
+
name: "about",
|
|
66748
|
+
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.',
|
|
66749
|
+
readOnly: true,
|
|
66750
|
+
async call() {
|
|
66751
|
+
const { outside, inside: inside2 } = facts.workspace;
|
|
66752
|
+
const rows = [
|
|
66753
|
+
{ label: "Where", value: facts.machine },
|
|
66754
|
+
// In a container its commands find the folder somewhere else, so the row says both.
|
|
66755
|
+
inside2 === outside ? { label: "Folder", value: outside, path: true } : { label: "Folder", value: `${outside} on the machine, mounted at ${inside2} in it` },
|
|
66756
|
+
{ label: "Reaches", value: REACHES[facts.access] + (facts.browser ? ", Browser" : "") },
|
|
66757
|
+
facts.contained ? { label: "Isolation", value: "A container of its own" } : { label: "Isolation", value: "None: it acts as you", caution: true }
|
|
66758
|
+
];
|
|
66759
|
+
const reading = await load.now();
|
|
66760
|
+
if (reading)
|
|
66761
|
+
rows.push(
|
|
66762
|
+
{ label: "CPU", value: `${whole(reading.cpuUsedPct)}%` },
|
|
66763
|
+
{
|
|
66764
|
+
label: "Memory",
|
|
66765
|
+
value: `${tenths(reading.memUsed / GIB)} of ${tenths(reading.memTotal / GIB)} GB`
|
|
66766
|
+
},
|
|
66767
|
+
{
|
|
66768
|
+
label: "Disk",
|
|
66769
|
+
value: `${whole(reading.diskUsed / GB)} of ${whole(reading.diskTotal / GB)} GB`
|
|
66770
|
+
}
|
|
66771
|
+
);
|
|
66772
|
+
return JSON.stringify({ rows });
|
|
66773
|
+
}
|
|
66774
|
+
};
|
|
66775
|
+
}
|
|
66776
|
+
|
|
66706
66777
|
// src/screencast.ts
|
|
66707
66778
|
var FIRST_FRAME_MS = 500;
|
|
66708
66779
|
var RESTARTS = 3;
|
|
@@ -67117,33 +67188,26 @@ var BrowserLive = class {
|
|
|
67117
67188
|
}
|
|
67118
67189
|
};
|
|
67119
67190
|
|
|
67120
|
-
// node_modules/ws/wrapper.mjs
|
|
67121
|
-
var import_stream2 = __toESM(require_stream(), 1);
|
|
67122
|
-
var import_extension = __toESM(require_extension(), 1);
|
|
67123
|
-
var import_permessage_deflate = __toESM(require_permessage_deflate(), 1);
|
|
67124
|
-
var import_receiver = __toESM(require_receiver(), 1);
|
|
67125
|
-
var import_sender = __toESM(require_sender(), 1);
|
|
67126
|
-
var import_subprotocol = __toESM(require_subprotocol(), 1);
|
|
67127
|
-
var import_websocket = __toESM(require_websocket(), 1);
|
|
67128
|
-
var import_websocket_server = __toESM(require_websocket_server(), 1);
|
|
67129
|
-
|
|
67130
67191
|
// src/files-live.ts
|
|
67131
67192
|
var MAX_PART = 1024 * 1024;
|
|
67132
67193
|
var MAX_BUFFERED2 = 4 * 1024 * 1024;
|
|
67133
67194
|
var MAX_UPLOADS = 64;
|
|
67134
67195
|
var reason2 = (error62) => error62 instanceof Error ? error62.message : String(error62);
|
|
67135
67196
|
var text2 = (value) => typeof value === "string";
|
|
67136
|
-
var
|
|
67197
|
+
var whole2 = (value) => Number.isSafeInteger(value);
|
|
67137
67198
|
var sleep4 = (ms) => new Promise((done) => setTimeout(done, ms));
|
|
67138
67199
|
var FilesLive = class {
|
|
67139
67200
|
#files;
|
|
67201
|
+
#workspace;
|
|
67140
67202
|
#sockets = /* @__PURE__ */ new Set();
|
|
67141
67203
|
/** One walk of the workspace at a time, however many ask. */
|
|
67142
67204
|
#walking = Promise.resolve();
|
|
67143
67205
|
/** How many files are part written, over every socket. */
|
|
67144
67206
|
#uploads = 0;
|
|
67145
|
-
|
|
67207
|
+
/** `workspace` is what the workspace is called: its folder's own name, not where it is. */
|
|
67208
|
+
constructor(files, workspace) {
|
|
67146
67209
|
this.#files = files;
|
|
67210
|
+
this.#workspace = workspace;
|
|
67147
67211
|
}
|
|
67148
67212
|
close() {
|
|
67149
67213
|
for (const ws of this.#sockets) ws.close(1001, "It stopped.");
|
|
@@ -67213,7 +67277,13 @@ var FilesLive = class {
|
|
|
67213
67277
|
const path = request.path ?? ".";
|
|
67214
67278
|
if (!text2(path)) throw new Error("A path is text.");
|
|
67215
67279
|
return inTurn(
|
|
67216
|
-
async () => send2({
|
|
67280
|
+
async () => send2({
|
|
67281
|
+
type: "entries",
|
|
67282
|
+
id,
|
|
67283
|
+
workspace: this.#workspace,
|
|
67284
|
+
path,
|
|
67285
|
+
...await this.#files.list(path)
|
|
67286
|
+
})
|
|
67217
67287
|
);
|
|
67218
67288
|
}
|
|
67219
67289
|
case "read": {
|
|
@@ -67243,7 +67313,7 @@ var FilesLive = class {
|
|
|
67243
67313
|
awaited = "refused";
|
|
67244
67314
|
if (mode !== "operate") throw new Error("This socket watches: it can't write.");
|
|
67245
67315
|
const { path, offset, length, last } = request;
|
|
67246
|
-
if (!text2(path) || !
|
|
67316
|
+
if (!text2(path) || !whole2(offset) || offset < 0 || !whole2(length) || length < 0 || length > MAX_PART || typeof last !== "boolean")
|
|
67247
67317
|
throw new Error(
|
|
67248
67318
|
"A write names a path, where it starts, 0 to 1,048,576 bytes, and last."
|
|
67249
67319
|
);
|
|
@@ -67294,6 +67364,16 @@ var FilesLive = class {
|
|
|
67294
67364
|
}
|
|
67295
67365
|
};
|
|
67296
67366
|
|
|
67367
|
+
// node_modules/ws/wrapper.mjs
|
|
67368
|
+
var import_stream2 = __toESM(require_stream(), 1);
|
|
67369
|
+
var import_extension = __toESM(require_extension(), 1);
|
|
67370
|
+
var import_permessage_deflate = __toESM(require_permessage_deflate(), 1);
|
|
67371
|
+
var import_receiver = __toESM(require_receiver(), 1);
|
|
67372
|
+
var import_sender = __toESM(require_sender(), 1);
|
|
67373
|
+
var import_subprotocol = __toESM(require_subprotocol(), 1);
|
|
67374
|
+
var import_websocket = __toESM(require_websocket(), 1);
|
|
67375
|
+
var import_websocket_server = __toESM(require_websocket_server(), 1);
|
|
67376
|
+
|
|
67297
67377
|
// src/text.ts
|
|
67298
67378
|
var MAX_TEXT = 1e4;
|
|
67299
67379
|
function* pieces(text3, most = MAX_TEXT) {
|
|
@@ -67346,7 +67426,7 @@ var Live = class {
|
|
|
67346
67426
|
/** Whether a person may write files here: not where the agent can only read them. */
|
|
67347
67427
|
#writes;
|
|
67348
67428
|
#load;
|
|
67349
|
-
constructor(exec, access, load, browser) {
|
|
67429
|
+
constructor(exec, access, load, files, browser) {
|
|
67350
67430
|
this.#load = load;
|
|
67351
67431
|
if (exec && access === "terminal") {
|
|
67352
67432
|
this.#terminals = exec.terminals;
|
|
@@ -67354,7 +67434,7 @@ var Live = class {
|
|
|
67354
67434
|
for (const message of messages(event)) for (const ws of this.#viewers) deliver(ws, message);
|
|
67355
67435
|
});
|
|
67356
67436
|
}
|
|
67357
|
-
this.#files =
|
|
67437
|
+
this.#files = files;
|
|
67358
67438
|
this.#writes = access !== "read";
|
|
67359
67439
|
this.#browser = browser;
|
|
67360
67440
|
}
|
|
@@ -67430,6 +67510,7 @@ import { readFile as readFile2 } from "node:fs/promises";
|
|
|
67430
67510
|
import { availableParallelism, cpus, freemem, totalmem } from "node:os";
|
|
67431
67511
|
import { promisify } from "node:util";
|
|
67432
67512
|
var EVERY_MS = 2e3;
|
|
67513
|
+
var ONCE_MS = 500;
|
|
67433
67514
|
var run = promisify(execFile);
|
|
67434
67515
|
var numberOf = (text3, name) => Number(new RegExp(`^${name}\\s+(\\d+)`, "m").exec(text3)?.[1]);
|
|
67435
67516
|
async function disk(folder) {
|
|
@@ -67468,6 +67549,18 @@ async function cgroup() {
|
|
|
67468
67549
|
memUsed: Number(current) - numberOf(memory, "inactive_file")
|
|
67469
67550
|
};
|
|
67470
67551
|
}
|
|
67552
|
+
function readingOf(before, sample, volume) {
|
|
67553
|
+
const share = (sample.cpu.used - before.used) / (sample.cpu.total - before.total) || 0;
|
|
67554
|
+
const reading = {
|
|
67555
|
+
type: "reading",
|
|
67556
|
+
scope: sample.scope,
|
|
67557
|
+
cpuUsedPct: Math.round(Math.min(Math.max(share, 0), 1) * 1e3) / 10,
|
|
67558
|
+
memUsed: sample.memUsed,
|
|
67559
|
+
memTotal: totalmem(),
|
|
67560
|
+
...volume
|
|
67561
|
+
};
|
|
67562
|
+
return Number.isFinite(reading.cpuUsedPct + reading.memUsed) ? reading : void 0;
|
|
67563
|
+
}
|
|
67471
67564
|
var LoadLive = class {
|
|
67472
67565
|
#folder;
|
|
67473
67566
|
#contained;
|
|
@@ -67501,28 +67594,33 @@ var LoadLive = class {
|
|
|
67501
67594
|
this.#timer = this.#before = this.#last = void 0;
|
|
67502
67595
|
for (const ws of this.#sockets) ws.close(1001, "It stopped.");
|
|
67503
67596
|
}
|
|
67597
|
+
/** Its own, inside a container that can read it; else the machine's. */
|
|
67598
|
+
#sample() {
|
|
67599
|
+
return this.#contained ? cgroup().catch(machine) : machine();
|
|
67600
|
+
}
|
|
67601
|
+
/** One reading for whoever asks once, taken over half a second. None where it can't be. */
|
|
67602
|
+
async now() {
|
|
67603
|
+
try {
|
|
67604
|
+
const before = await this.#sample();
|
|
67605
|
+
await new Promise((done) => setTimeout(done, ONCE_MS));
|
|
67606
|
+
return readingOf(before.cpu, await this.#sample(), await disk(this.#folder));
|
|
67607
|
+
} catch {
|
|
67608
|
+
return void 0;
|
|
67609
|
+
}
|
|
67610
|
+
}
|
|
67504
67611
|
/** One at a time, and for the watching it began under: a reading taken too late is left. */
|
|
67505
67612
|
async #read() {
|
|
67506
67613
|
const timer = this.#timer;
|
|
67507
67614
|
if (this.#reading) return;
|
|
67508
67615
|
this.#reading = true;
|
|
67509
67616
|
try {
|
|
67510
|
-
const sample = await
|
|
67617
|
+
const sample = await this.#sample();
|
|
67511
67618
|
const volume = await disk(this.#folder);
|
|
67512
67619
|
if (this.#timer !== timer) return;
|
|
67513
67620
|
const before = this.#before;
|
|
67514
67621
|
this.#before = sample.cpu;
|
|
67515
|
-
|
|
67516
|
-
|
|
67517
|
-
const reading = {
|
|
67518
|
-
type: "reading",
|
|
67519
|
-
scope: sample.scope,
|
|
67520
|
-
cpuUsedPct: Math.round(Math.min(Math.max(share, 0), 1) * 1e3) / 10,
|
|
67521
|
-
memUsed: sample.memUsed,
|
|
67522
|
-
memTotal: totalmem(),
|
|
67523
|
-
...volume
|
|
67524
|
-
};
|
|
67525
|
-
if (!Number.isFinite(reading.cpuUsedPct + reading.memUsed)) return;
|
|
67622
|
+
const reading = before && readingOf(before, sample, volume);
|
|
67623
|
+
if (!reading) return;
|
|
67526
67624
|
this.#last = JSON.stringify(reading);
|
|
67527
67625
|
for (const ws of this.#sockets) if (ws.bufferedAmount === 0) ws.send(this.#last);
|
|
67528
67626
|
} catch {
|
|
@@ -67601,7 +67699,7 @@ var ProcessRuntime = class {
|
|
|
67601
67699
|
isolation = "none";
|
|
67602
67700
|
#config;
|
|
67603
67701
|
#contained;
|
|
67604
|
-
/** `contained` is for inside a container, where load
|
|
67702
|
+
/** `contained` is for inside a container, where the computer and its load are its own. */
|
|
67605
67703
|
constructor(config3, contained = false) {
|
|
67606
67704
|
this.#config = config3;
|
|
67607
67705
|
this.#contained = contained;
|
|
@@ -67613,13 +67711,27 @@ var ProcessRuntime = class {
|
|
|
67613
67711
|
async remove() {
|
|
67614
67712
|
}
|
|
67615
67713
|
async start(spec, folder, events) {
|
|
67714
|
+
const load = new LoadLive(spec.workspace ?? folder, this.#contained);
|
|
67616
67715
|
let exec;
|
|
67617
|
-
if (spec.access !== "none")
|
|
67716
|
+
if (spec.access !== "none") {
|
|
67717
|
+
const workspace = spec.workspace;
|
|
67718
|
+
const about = aboutTool(
|
|
67719
|
+
{
|
|
67720
|
+
machine: spec.outside?.machine ?? hostname3(),
|
|
67721
|
+
workspace: { outside: spec.outside?.workspace ?? workspace, inside: workspace },
|
|
67722
|
+
access: spec.access,
|
|
67723
|
+
browser: spec.browser,
|
|
67724
|
+
contained: this.#contained
|
|
67725
|
+
},
|
|
67726
|
+
load
|
|
67727
|
+
);
|
|
67618
67728
|
exec = createAgentExec({
|
|
67619
|
-
workspace
|
|
67729
|
+
workspace,
|
|
67620
67730
|
level: spec.access,
|
|
67621
|
-
pty: await loadPty()
|
|
67731
|
+
pty: await loadPty(),
|
|
67732
|
+
extraTools: [about]
|
|
67622
67733
|
});
|
|
67734
|
+
}
|
|
67623
67735
|
let browser;
|
|
67624
67736
|
let browserDown = false;
|
|
67625
67737
|
if (spec.browser) {
|
|
@@ -67647,8 +67759,8 @@ var ProcessRuntime = class {
|
|
|
67647
67759
|
browserLive = new BrowserLive(browser.endpoint);
|
|
67648
67760
|
browser.gate = browserLive;
|
|
67649
67761
|
}
|
|
67650
|
-
const
|
|
67651
|
-
const live = new Live(exec, spec.access, load, browserLive);
|
|
67762
|
+
const files = exec && new FilesLive(exec.files, basename2(spec.outside?.workspace ?? spec.workspace));
|
|
67763
|
+
const live = new Live(exec, spec.access, load, files, browserLive);
|
|
67652
67764
|
const surfaces = live.surfaces();
|
|
67653
67765
|
return {
|
|
67654
67766
|
browserTools: browser?.tools ?? [],
|
|
@@ -67678,6 +67790,8 @@ var ProcessRuntime = class {
|
|
|
67678
67790
|
// src/inside.ts
|
|
67679
67791
|
var INNER_PORT = 8e3;
|
|
67680
67792
|
var INNER_TOKEN_VARIABLE = "AGENTCOMPUTER_INNER_TOKEN";
|
|
67793
|
+
var OUTSIDE_WORKSPACE_VARIABLE = "AGENTCOMPUTER_OUTSIDE_WORKSPACE";
|
|
67794
|
+
var OUTSIDE_MACHINE_VARIABLE = "AGENTCOMPUTER_OUTSIDE_MACHINE";
|
|
67681
67795
|
var BROWSER = "/opt/browsers/headless-shell";
|
|
67682
67796
|
async function serveInside(options) {
|
|
67683
67797
|
const token = process.env[INNER_TOKEN_VARIABLE];
|
|
@@ -67688,7 +67802,11 @@ async function serveInside(options) {
|
|
|
67688
67802
|
{
|
|
67689
67803
|
id: "inside",
|
|
67690
67804
|
...options,
|
|
67691
|
-
...options.access === "none" ? {} : { workspace: "/workspace" }
|
|
67805
|
+
...options.access === "none" ? {} : { workspace: "/workspace" },
|
|
67806
|
+
outside: {
|
|
67807
|
+
workspace: process.env[OUTSIDE_WORKSPACE_VARIABLE],
|
|
67808
|
+
machine: process.env[OUTSIDE_MACHINE_VARIABLE]
|
|
67809
|
+
}
|
|
67692
67810
|
},
|
|
67693
67811
|
privateFolder(join7(homedir(), ".agentcomputer")),
|
|
67694
67812
|
{
|
|
@@ -67799,6 +67917,10 @@ var ContainerRuntime = class {
|
|
|
67799
67917
|
}
|
|
67800
67918
|
async #create(name, spec, token) {
|
|
67801
67919
|
const uid = process.getuid?.();
|
|
67920
|
+
const told = {
|
|
67921
|
+
[INNER_TOKEN_VARIABLE]: token,
|
|
67922
|
+
...spec.workspace ? { [OUTSIDE_WORKSPACE_VARIABLE]: spec.workspace, [OUTSIDE_MACHINE_VARIABLE]: hostname4() } : {}
|
|
67923
|
+
};
|
|
67802
67924
|
await docker(
|
|
67803
67925
|
[
|
|
67804
67926
|
"create",
|
|
@@ -67813,9 +67935,8 @@ var ContainerRuntime = class {
|
|
|
67813
67935
|
"no-new-privileges",
|
|
67814
67936
|
"--publish",
|
|
67815
67937
|
`127.0.0.1::${INNER_PORT}`,
|
|
67816
|
-
// By name, so the
|
|
67817
|
-
"--env",
|
|
67818
|
-
INNER_TOKEN_VARIABLE,
|
|
67938
|
+
// By name, so the values stay out of the command line other users can see.
|
|
67939
|
+
...Object.keys(told).flatMap((name2) => ["--env", name2]),
|
|
67819
67940
|
// Files it writes in the workspace belong to whoever runs the service.
|
|
67820
67941
|
...uid ? ["--user", `${uid}:${process.getgid?.()}`] : [],
|
|
67821
67942
|
...spec.workspace ? [
|
|
@@ -67828,7 +67949,7 @@ var ContainerRuntime = class {
|
|
|
67828
67949
|
spec.access,
|
|
67829
67950
|
...spec.browser ? ["--browser"] : []
|
|
67830
67951
|
],
|
|
67831
|
-
{ ...process.env,
|
|
67952
|
+
{ ...process.env, ...told }
|
|
67832
67953
|
);
|
|
67833
67954
|
}
|
|
67834
67955
|
/** The token between the service and this container, which a restart has to find again. */
|
|
@@ -67972,7 +68093,7 @@ async function forward(url3, authorization, request, response) {
|
|
|
67972
68093
|
}
|
|
67973
68094
|
|
|
67974
68095
|
// src/handlers.ts
|
|
67975
|
-
var VERSION2 = "0.0.
|
|
68096
|
+
var VERSION2 = "0.0.12";
|
|
67976
68097
|
function createHandlers(computers) {
|
|
67977
68098
|
return {
|
|
67978
68099
|
getInfo: async () => ({ version: VERSION2, isolation: computers.isolation }),
|
|
@@ -84613,10 +84734,12 @@ var NodeStreamableHTTPServerTransport2 = class {
|
|
|
84613
84734
|
import { randomBytes as randomBytes5 } from "node:crypto";
|
|
84614
84735
|
import { appendFileSync, existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync as readFileSync4, rmSync as rmSync2, statSync as statSync3 } from "node:fs";
|
|
84615
84736
|
import { isAbsolute, join as join9, resolve as resolve4 } from "node:path";
|
|
84616
|
-
var
|
|
84617
|
-
|
|
84618
|
-
|
|
84619
|
-
|
|
84737
|
+
var READ = ["about", "read", "list", "search"];
|
|
84738
|
+
var FILES = [...READ, "edit"];
|
|
84739
|
+
var TOOLS2 = {
|
|
84740
|
+
read: READ,
|
|
84741
|
+
files: FILES,
|
|
84742
|
+
terminal: [...FILES, "exec_command", "write_stdin", "list_sessions"]
|
|
84620
84743
|
};
|
|
84621
84744
|
var MAX_LOG = 256 * 1024;
|
|
84622
84745
|
var MAX_TIMER_S = 2e6;
|
|
@@ -84715,7 +84838,7 @@ var Computers = class {
|
|
|
84715
84838
|
...this.#describe(stored),
|
|
84716
84839
|
uptimeSeconds: stored.state === "running" ? Math.round((Date.now() - Date.parse(stored.startedAt)) / 1e3) : 0,
|
|
84717
84840
|
tools: {
|
|
84718
|
-
computer: stored.access === "none" ? [] :
|
|
84841
|
+
computer: stored.access === "none" ? [] : TOOLS2[stored.access],
|
|
84719
84842
|
browser: live?.running?.browserTools ?? []
|
|
84720
84843
|
},
|
|
84721
84844
|
surfaces: live?.running?.surfaces ?? []
|
|
@@ -84935,7 +85058,7 @@ var OPENAPI = {
|
|
|
84935
85058
|
"openapi": "3.1.0",
|
|
84936
85059
|
"info": {
|
|
84937
85060
|
"title": "AgentComputer",
|
|
84938
|
-
"version": "0.0.
|
|
85061
|
+
"version": "0.0.12",
|
|
84939
85062
|
"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.",
|
|
84940
85063
|
"license": {
|
|
84941
85064
|
"name": "Apache-2.0",
|
|
@@ -85387,7 +85510,7 @@ var OPENAPI = {
|
|
|
85387
85510
|
"files",
|
|
85388
85511
|
"terminal"
|
|
85389
85512
|
],
|
|
85390
|
-
"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."
|
|
85513
|
+
"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."
|
|
85391
85514
|
},
|
|
85392
85515
|
"ComputerState": {
|
|
85393
85516
|
"type": "string",
|
|
@@ -85600,7 +85723,7 @@ var OPENAPI = {
|
|
|
85600
85723
|
"browser",
|
|
85601
85724
|
"load"
|
|
85602
85725
|
],
|
|
85603
|
-
"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."
|
|
85726
|
+
"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."
|
|
85604
85727
|
},
|
|
85605
85728
|
"LiveMode": {
|
|
85606
85729
|
"type": "string",
|
|
@@ -86442,8 +86565,13 @@ var OPENAPI = {
|
|
|
86442
86565
|
"id": {
|
|
86443
86566
|
"type": "string"
|
|
86444
86567
|
},
|
|
86568
|
+
"workspace": {
|
|
86569
|
+
"type": "string",
|
|
86570
|
+
"description": "What the workspace is called: its folder's own name, and nothing of where it is. It comes with every entries answer, whichever folder was asked for, so that with path it names the folder. A computer in a container made before `workspace` was added sends none, and there path alone names the folder. It is `workspace` for a workspace the service made, and empty for one at the top of a disk."
|
|
86571
|
+
},
|
|
86445
86572
|
"path": {
|
|
86446
|
-
"type": "string"
|
|
86573
|
+
"type": "string",
|
|
86574
|
+
"description": "The folder as it was asked for: `.` for the workspace itself."
|
|
86447
86575
|
},
|
|
86448
86576
|
"entries": {
|
|
86449
86577
|
"type": "array",
|
|
@@ -86772,7 +86900,7 @@ var OPERATIONS = [
|
|
|
86772
86900
|
"files",
|
|
86773
86901
|
"terminal"
|
|
86774
86902
|
],
|
|
86775
|
-
"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."
|
|
86903
|
+
"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."
|
|
86776
86904
|
},
|
|
86777
86905
|
"browser": {
|
|
86778
86906
|
"type": "boolean"
|
|
@@ -86861,7 +86989,7 @@ var OPERATIONS = [
|
|
|
86861
86989
|
"files",
|
|
86862
86990
|
"terminal"
|
|
86863
86991
|
],
|
|
86864
|
-
"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."
|
|
86992
|
+
"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."
|
|
86865
86993
|
},
|
|
86866
86994
|
"browser": {
|
|
86867
86995
|
"type": "boolean",
|
|
@@ -86931,7 +87059,7 @@ var OPERATIONS = [
|
|
|
86931
87059
|
"files",
|
|
86932
87060
|
"terminal"
|
|
86933
87061
|
],
|
|
86934
|
-
"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."
|
|
87062
|
+
"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."
|
|
86935
87063
|
},
|
|
86936
87064
|
"browser": {
|
|
86937
87065
|
"type": "boolean"
|
|
@@ -87071,7 +87199,7 @@ var OPERATIONS = [
|
|
|
87071
87199
|
"files",
|
|
87072
87200
|
"terminal"
|
|
87073
87201
|
],
|
|
87074
|
-
"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."
|
|
87202
|
+
"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."
|
|
87075
87203
|
},
|
|
87076
87204
|
"browser": {
|
|
87077
87205
|
"type": "boolean"
|
|
@@ -87160,7 +87288,7 @@ var OPERATIONS = [
|
|
|
87160
87288
|
"browser",
|
|
87161
87289
|
"load"
|
|
87162
87290
|
],
|
|
87163
|
-
"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."
|
|
87291
|
+
"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."
|
|
87164
87292
|
},
|
|
87165
87293
|
"protocol": {
|
|
87166
87294
|
"type": "string"
|
|
@@ -87302,7 +87430,7 @@ var OPERATIONS = [
|
|
|
87302
87430
|
"files",
|
|
87303
87431
|
"terminal"
|
|
87304
87432
|
],
|
|
87305
|
-
"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."
|
|
87433
|
+
"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."
|
|
87306
87434
|
},
|
|
87307
87435
|
"browser": {
|
|
87308
87436
|
"type": "boolean"
|
|
@@ -87426,7 +87554,7 @@ var OPERATIONS = [
|
|
|
87426
87554
|
"files",
|
|
87427
87555
|
"terminal"
|
|
87428
87556
|
],
|
|
87429
|
-
"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."
|
|
87557
|
+
"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."
|
|
87430
87558
|
},
|
|
87431
87559
|
"browser": {
|
|
87432
87560
|
"type": "boolean"
|
|
@@ -87555,7 +87683,7 @@ var OPERATIONS = [
|
|
|
87555
87683
|
"files",
|
|
87556
87684
|
"terminal"
|
|
87557
87685
|
],
|
|
87558
|
-
"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."
|
|
87686
|
+
"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."
|
|
87559
87687
|
},
|
|
87560
87688
|
"browser": {
|
|
87561
87689
|
"type": "boolean"
|
|
@@ -87700,7 +87828,7 @@ var OPERATIONS = [
|
|
|
87700
87828
|
"browser",
|
|
87701
87829
|
"load"
|
|
87702
87830
|
],
|
|
87703
|
-
"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."
|
|
87831
|
+
"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."
|
|
87704
87832
|
},
|
|
87705
87833
|
"mode": {
|
|
87706
87834
|
"allOf": [
|
|
@@ -87747,7 +87875,7 @@ var OPERATIONS = [
|
|
|
87747
87875
|
"browser",
|
|
87748
87876
|
"load"
|
|
87749
87877
|
],
|
|
87750
|
-
"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."
|
|
87878
|
+
"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."
|
|
87751
87879
|
},
|
|
87752
87880
|
"mode": {
|
|
87753
87881
|
"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";
|
|
@@ -487,6 +487,9 @@ export interface components {
|
|
|
487
487
|
/** @constant */
|
|
488
488
|
type: "entries";
|
|
489
489
|
id: string;
|
|
490
|
+
/** @description What the workspace is called: its folder's own name, and nothing of where it is. It comes with every entries answer, whichever folder was asked for, so that with path it names the folder. A computer in a container made before `workspace` was added sends none, and there path alone names the folder. It is `workspace` for a workspace the service made, and empty for one at the top of a disk. */
|
|
491
|
+
workspace?: string;
|
|
492
|
+
/** @description The folder as it was asked for: `.` for the workspace itself. */
|
|
490
493
|
path: string;
|
|
491
494
|
entries: {
|
|
492
495
|
name: string;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agentorg/computer",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.12",
|
|
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.6",
|
|
35
35
|
"@biomejs/biome": "2.6.5",
|
|
36
36
|
"@modelcontextprotocol/client": "2.4.0",
|
|
37
37
|
"@modelcontextprotocol/node": "2.1.2",
|