@agentorg/computer 0.0.16 → 0.0.18
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 +8 -6
- package/dist/agentcomputer.js +155 -49
- package/dist/types/generated/schema.d.ts +2 -0
- package/dist/types/sdk.d.ts +4 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -28,10 +28,12 @@ Isolation is chosen for each computer when it is made, and it keeps it until it
|
|
|
28
28
|
A computer in a container:
|
|
29
29
|
|
|
30
30
|
- Its 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.
|
|
31
|
-
- `--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.
|
|
31
|
+
- `--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, and the data folder, where its home is. Without `--workspace`, the service makes one.
|
|
32
32
|
- The image is fetched the first time it is needed, which can take minutes: when the service starts if its own isolation is `container`, and otherwise when the first computer in a container is made, a call that then waits for it. `docker pull` it beforehand to spare that wait.
|
|
33
33
|
- It runs as the user who started the service, with no capabilities.
|
|
34
|
-
-
|
|
34
|
+
- Its home, `/home/computer`, is a folder of the machine's, in the computer's own folder in the data folder, mounted as the workspace is. What the agent keeps there stays, the browser's profile with its sign-ins among it. `stop` stops the container and keeps it, and `kill` removes it with the computer's folder.
|
|
35
|
+
- **After an update,** a computer in a container is made again at its first start under a new version of the service, or under another `--image`: its container is removed and made again from the service's image. It keeps its workspace, its home, and its id, token, endpoints and settings. It loses its running commands, as at any restart, and everything else in the container: software installed there, and files outside `/workspace` and `/home/computer`, such as `/tmp`. Keep what must last in those two folders, or in an image of your own. Every computer's answer says the `version` of the program it runs.
|
|
36
|
+
- A container made before 0.0.17 keeps its home inside it. At its first remake the home is copied out. If that fails, the container is kept and started as it was, its `logs` say why, and the copy is tried again at its next start. Until it is made again, its `version` is `null`.
|
|
35
37
|
|
|
36
38
|
## Access
|
|
37
39
|
|
|
@@ -42,7 +44,7 @@ A computer in a container:
|
|
|
42
44
|
| `terminal` | Those, and `exec_command`, `write_stdin`, `list_sessions` |
|
|
43
45
|
| `none` | No `computer` endpoint; needs `--browser` |
|
|
44
46
|
|
|
45
|
-
`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, but not one of more than 3 MiB, which a `fullPage` screenshot of a long page can be many times over: it answers an error that gives the size and says to take it again without `fullPage`, or of one element. The most counts bytes only: a model may still refuse a smaller picture for its height or width.
|
|
47
|
+
`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, but not one of more than 3 MiB, which a `fullPage` screenshot of a long page can be many times over: it answers an error that gives the size and says to take it again without `fullPage`, or of one element. The most counts bytes only: a model may still refuse a smaller picture for its height or width.
|
|
46
48
|
|
|
47
49
|
`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:
|
|
48
50
|
|
|
@@ -58,13 +60,13 @@ A computer in a container:
|
|
|
58
60
|
|
|
59
61
|
- **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.
|
|
60
62
|
- **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.
|
|
61
|
-
- **Not every computer has it.** One with no `computer` endpoint has no `about`.
|
|
63
|
+
- **Not every computer has it.** One with no `computer` endpoint has no `about`.
|
|
62
64
|
|
|
63
|
-
A client that calls a computer's tools can put the commands it starts in a group, and stop them together. At `--access terminal` it sends a word as `agentexec/group` in the `_meta` of an `exec_command` call, and the request `agentexec/group/stop` with `{"group": "<word>"}` on the `computer` endpoint, which ends every running command of that group and answers `{"stopped": <how many>}`. Giving up a call does not stop its command; only that request does. A shell a person opened is in no group. This is AgentExec's, described in [its README](https://github.com/uniac-ai/AgentExec#sessions), and the service passes both through, with no isolation and in a container.
|
|
65
|
+
A client that calls a computer's tools can put the commands it starts in a group, and stop them together. At `--access terminal` it sends a word as `agentexec/group` in the `_meta` of an `exec_command` call, and the request `agentexec/group/stop` with `{"group": "<word>"}` on the `computer` endpoint, which ends every running command of that group and answers `{"stopped": <how many>}`. Giving up a call does not stop its command; only that request does. A shell a person opened is in no group. This is AgentExec's, described in [its README](https://github.com/uniac-ai/AgentExec#sessions), and the service passes both through, with no isolation and in a container.
|
|
64
66
|
|
|
65
67
|
The file tools stay inside the workspace. That's a check, not a boundary. Below `terminal`, the browser's `browser_run_code_unsafe` tool is hidden and refused, because it would otherwise be a terminal. The browser's endpoint takes one JSON-RPC request in each POST, as a JSON object, and refuses anything else with 400, a batch among them: it checks each request before passing it on, and passes on only what it read. The service enforces all of this, whatever the client does.
|
|
66
68
|
|
|
67
|
-
Before 0.0.14 the browser's endpoint passed on a body it couldn't read as one request, unchecked. So on a computer made with `--access none`, `read` or `files` and `--browser`, a caller holding that computer's token could run code in its browser by sending the call in a batch or behind a byte order mark. It needed the computer's own token and a client that sends such a body, which MCP clients don't. 0.0.14 fixes it; a computer in a container made by an earlier version keeps the fault until
|
|
69
|
+
Before 0.0.14 the browser's endpoint passed on a body it couldn't read as one request, unchecked. So on a computer made with `--access none`, `read` or `files` and `--browser`, a caller holding that computer's token could run code in its browser by sending the call in a batch or behind a byte order mark. It needed the computer's own token and a client that sends such a body, which MCP clients don't. 0.0.14 fixes it; a computer in a container made by an earlier version keeps the fault until its container is made again.
|
|
68
70
|
|
|
69
71
|
`--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. Playwright MCP, which serves the tools, listens on a Unix socket in a second such folder, and no port either. Only the service's user can reach them; callers go through the computer's `browser` endpoint and its token. 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.
|
|
70
72
|
|
package/dist/agentcomputer.js
CHANGED
|
@@ -3875,7 +3875,7 @@ async function serveBrowserMcp(options) {
|
|
|
3875
3875
|
|
|
3876
3876
|
// src/container.ts
|
|
3877
3877
|
import { execFile as execFile2, spawn as spawn3 } from "node:child_process";
|
|
3878
|
-
import { readFileSync as readFileSync3 } from "node:fs";
|
|
3878
|
+
import { chmodSync as chmodSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync3, renameSync as renameSync2, rmSync as rmSync4 } from "node:fs";
|
|
3879
3879
|
import { connect } from "node:net";
|
|
3880
3880
|
import { hostname as hostname4 } from "node:os";
|
|
3881
3881
|
import { join as join8 } from "node:path";
|
|
@@ -67031,6 +67031,7 @@ var BrowserLive = class {
|
|
|
67031
67031
|
} catch {
|
|
67032
67032
|
return;
|
|
67033
67033
|
}
|
|
67034
|
+
if (typeof message !== "object" || message === null) return;
|
|
67034
67035
|
this.#input(ws, viewer, message);
|
|
67035
67036
|
});
|
|
67036
67037
|
this.#begin().then(
|
|
@@ -67957,6 +67958,33 @@ var ProcessRuntime = class {
|
|
|
67957
67958
|
}
|
|
67958
67959
|
};
|
|
67959
67960
|
|
|
67961
|
+
// src/handlers.ts
|
|
67962
|
+
var VERSION2 = "0.0.18";
|
|
67963
|
+
function createHandlers(computers) {
|
|
67964
|
+
return {
|
|
67965
|
+
getInfo: async () => ({
|
|
67966
|
+
version: VERSION2,
|
|
67967
|
+
isolation: computers.isolation,
|
|
67968
|
+
isolationsAvailable: await computers.available()
|
|
67969
|
+
}),
|
|
67970
|
+
listComputers: async ({ state }) => computers.list(state),
|
|
67971
|
+
createComputer: (input2) => computers.create(input2),
|
|
67972
|
+
getComputer: async ({ computerID }) => computers.get(computerID),
|
|
67973
|
+
killComputer: async ({ computerID }) => {
|
|
67974
|
+
await computers.kill(computerID);
|
|
67975
|
+
return void 0;
|
|
67976
|
+
},
|
|
67977
|
+
setComputerTimeout: async ({ computerID, timeout }) => computers.setTimeout(computerID, timeout),
|
|
67978
|
+
stopComputer: ({ computerID }) => computers.stop(computerID),
|
|
67979
|
+
startComputer: ({ computerID, timeout }) => computers.start(computerID, timeout),
|
|
67980
|
+
getComputerLogs: async ({ computerID }) => ({ logs: computers.logs(computerID) }),
|
|
67981
|
+
createLiveSession: async ({ computerID, surface, mode }) => computers.liveSession(computerID, surface, mode ?? "watch"),
|
|
67982
|
+
openLiveSession: async () => {
|
|
67983
|
+
throw new ApiError(426, "Open it as a WebSocket.");
|
|
67984
|
+
}
|
|
67985
|
+
};
|
|
67986
|
+
}
|
|
67987
|
+
|
|
67960
67988
|
// src/inside.ts
|
|
67961
67989
|
var INNER_PORT = 8e3;
|
|
67962
67990
|
var INNER_TOKEN_VARIABLE = "AGENTCOMPUTER_INNER_TOKEN";
|
|
@@ -67997,7 +68025,7 @@ async function serveInside(options) {
|
|
|
67997
68025
|
if (pathname === "/ready") {
|
|
67998
68026
|
const { browserTools, surfaces } = await running2;
|
|
67999
68027
|
response.writeHead(200, { "Content-Type": "application/json" });
|
|
68000
|
-
return void response.end(JSON.stringify({ browserTools, surfaces }));
|
|
68028
|
+
return void response.end(JSON.stringify({ browserTools, surfaces, version: VERSION2 }));
|
|
68001
68029
|
}
|
|
68002
68030
|
const endpoint = /^\/mcp\/(computer|browser)$/.exec(pathname)?.[1];
|
|
68003
68031
|
if (!endpoint) return void response.writeHead(404).end();
|
|
@@ -68012,9 +68040,9 @@ async function serveInside(options) {
|
|
|
68012
68040
|
server.on("upgrade", (request, socket, head) => {
|
|
68013
68041
|
socket.on("error", () => socket.destroy());
|
|
68014
68042
|
const given = /^Bearer (\S+)$/.exec(request.headers.authorization ?? "")?.[1];
|
|
68015
|
-
const url3 =
|
|
68016
|
-
const surface = /^\/live\/(terminal|files|browser|load)$/.exec(url3
|
|
68017
|
-
const mode = url3
|
|
68043
|
+
const url3 = URL.parse(request.url ?? "/", "http://localhost");
|
|
68044
|
+
const surface = /^\/live\/(terminal|files|browser|load)$/.exec(url3?.pathname ?? "")?.[1];
|
|
68045
|
+
const mode = url3?.searchParams.get("mode");
|
|
68018
68046
|
if (!given || !sameToken(given, digest) || !surface || mode !== "watch" && mode !== "operate")
|
|
68019
68047
|
return void socket.end("HTTP/1.1 401 Unauthorized\r\nConnection: close\r\n\r\n");
|
|
68020
68048
|
running2.then((computer) => computer.openLive(surface, mode, request, socket, head)).catch(() => socket.destroy());
|
|
@@ -68056,10 +68084,15 @@ var UPGRADE_HEADERS = [
|
|
|
68056
68084
|
"sec-websocket-extensions",
|
|
68057
68085
|
"sec-websocket-protocol"
|
|
68058
68086
|
];
|
|
68087
|
+
var messageOf = (error62) => error62 instanceof Error ? error62.message : String(error62);
|
|
68059
68088
|
var nameOf = (id) => `agentcomputer-${id}`;
|
|
68089
|
+
var HOME = "/home/computer";
|
|
68090
|
+
var homeOf = (folder) => join8(folder, "home");
|
|
68060
68091
|
var sleep5 = (ms) => new Promise((done) => setTimeout(done, ms));
|
|
68061
68092
|
var ContainerRuntime = class {
|
|
68062
68093
|
#image;
|
|
68094
|
+
/** The id of that image, as it is now on this machine. */
|
|
68095
|
+
#imageId;
|
|
68063
68096
|
constructor(image) {
|
|
68064
68097
|
this.#image = image;
|
|
68065
68098
|
}
|
|
@@ -68083,12 +68116,14 @@ var ContainerRuntime = class {
|
|
|
68083
68116
|
}
|
|
68084
68117
|
async prepare() {
|
|
68085
68118
|
await this.#docker();
|
|
68119
|
+
const id = () => docker(["image", "inspect", "--format", "{{.Id}}", this.#image]);
|
|
68086
68120
|
try {
|
|
68087
|
-
await
|
|
68121
|
+
this.#imageId = await id();
|
|
68088
68122
|
} catch {
|
|
68089
68123
|
process.stdout.write(`Pulling ${this.#image}
|
|
68090
68124
|
`);
|
|
68091
68125
|
await docker(["pull", this.#image]);
|
|
68126
|
+
this.#imageId = await id();
|
|
68092
68127
|
}
|
|
68093
68128
|
}
|
|
68094
68129
|
async #inspect(name) {
|
|
@@ -68098,7 +68133,39 @@ var ContainerRuntime = class {
|
|
|
68098
68133
|
return void 0;
|
|
68099
68134
|
}
|
|
68100
68135
|
}
|
|
68101
|
-
|
|
68136
|
+
/**
|
|
68137
|
+
* Remove a container made from another image, for it to be made again. What it keeps is outside
|
|
68138
|
+
* it: its workspace, and its home. One made before its home was outside has its home copied out
|
|
68139
|
+
* first, and if that can't be done it is kept as it is, to try again at its next start.
|
|
68140
|
+
*/
|
|
68141
|
+
async #remake(name, made, folder, events) {
|
|
68142
|
+
if (!made.Mounts.some((mount) => mount.Destination === HOME)) {
|
|
68143
|
+
const home = homeOf(folder);
|
|
68144
|
+
const copying = `${home}.copying`;
|
|
68145
|
+
rmSync4(copying, { recursive: true, force: true });
|
|
68146
|
+
try {
|
|
68147
|
+
const owners = process.getuid?.() ? [] : ["--archive"];
|
|
68148
|
+
await docker(["cp", ...owners, `${name}:${HOME}`, copying]);
|
|
68149
|
+
rmSync4(home, { recursive: true, force: true });
|
|
68150
|
+
renameSync2(copying, home);
|
|
68151
|
+
} catch (error62) {
|
|
68152
|
+
rmSync4(copying, { recursive: true, force: true });
|
|
68153
|
+
events.log(
|
|
68154
|
+
`Its home couldn't be copied out of its container (${messageOf(error62)}), so the container is kept and started as it was made. The copy is tried again at its next start.`
|
|
68155
|
+
);
|
|
68156
|
+
return;
|
|
68157
|
+
}
|
|
68158
|
+
events.log("Its home was copied out of its container, to keep.");
|
|
68159
|
+
}
|
|
68160
|
+
await docker(["rm", "--force", name]);
|
|
68161
|
+
events.log(
|
|
68162
|
+
`Its container was made from another image, so it is made again from ${this.#image}.`
|
|
68163
|
+
);
|
|
68164
|
+
}
|
|
68165
|
+
async #create(name, spec, token, folder) {
|
|
68166
|
+
const home = homeOf(folder);
|
|
68167
|
+
mkdirSync2(home, { recursive: true });
|
|
68168
|
+
chmodSync3(home, 1023);
|
|
68102
68169
|
const uid = process.getuid?.();
|
|
68103
68170
|
const told = {
|
|
68104
68171
|
[INNER_TOKEN_VARIABLE]: token,
|
|
@@ -68122,6 +68189,8 @@ var ContainerRuntime = class {
|
|
|
68122
68189
|
...Object.keys(told).flatMap((name2) => ["--env", name2]),
|
|
68123
68190
|
// Files it writes in the workspace belong to whoever runs the service.
|
|
68124
68191
|
...uid ? ["--user", `${uid}:${process.getgid?.()}`] : [],
|
|
68192
|
+
"--mount",
|
|
68193
|
+
`type=bind,source=${home},target=${HOME}`,
|
|
68125
68194
|
...spec.workspace ? [
|
|
68126
68195
|
"--mount",
|
|
68127
68196
|
`type=bind,source=${spec.workspace},target=/workspace${spec.access === "read" ? ",readonly" : ""}`
|
|
@@ -68149,7 +68218,10 @@ var ContainerRuntime = class {
|
|
|
68149
68218
|
async start(spec, folder, events) {
|
|
68150
68219
|
const name = nameOf(spec.id);
|
|
68151
68220
|
const token = this.#tokenIn(folder);
|
|
68152
|
-
|
|
68221
|
+
const made = await this.#inspect(name);
|
|
68222
|
+
if (made && this.#imageId && made.Image !== this.#imageId)
|
|
68223
|
+
await this.#remake(name, made, folder, events);
|
|
68224
|
+
if (!await this.#inspect(name)) await this.#create(name, spec, token, folder);
|
|
68153
68225
|
await docker(["start", name]);
|
|
68154
68226
|
const inspected = await this.#inspect(name);
|
|
68155
68227
|
const port = inspected?.NetworkSettings.Ports[`${INNER_PORT}/tcp`]?.[0]?.HostPort;
|
|
@@ -68186,6 +68258,7 @@ var ContainerRuntime = class {
|
|
|
68186
68258
|
}
|
|
68187
68259
|
return {
|
|
68188
68260
|
browserTools: ready.browserTools,
|
|
68261
|
+
version: ready.version,
|
|
68189
68262
|
/** As they are now: nothing of a container that has stopped can be opened. */
|
|
68190
68263
|
get surfaces() {
|
|
68191
68264
|
return ready.surfaces.map(
|
|
@@ -68278,33 +68351,6 @@ async function forward(url3, authorization, request, response) {
|
|
|
68278
68351
|
sendAnswer(upstream, response);
|
|
68279
68352
|
}
|
|
68280
68353
|
|
|
68281
|
-
// src/handlers.ts
|
|
68282
|
-
var VERSION2 = "0.0.16";
|
|
68283
|
-
function createHandlers(computers) {
|
|
68284
|
-
return {
|
|
68285
|
-
getInfo: async () => ({
|
|
68286
|
-
version: VERSION2,
|
|
68287
|
-
isolation: computers.isolation,
|
|
68288
|
-
isolationsAvailable: await computers.available()
|
|
68289
|
-
}),
|
|
68290
|
-
listComputers: async ({ state }) => computers.list(state),
|
|
68291
|
-
createComputer: (input2) => computers.create(input2),
|
|
68292
|
-
getComputer: async ({ computerID }) => computers.get(computerID),
|
|
68293
|
-
killComputer: async ({ computerID }) => {
|
|
68294
|
-
await computers.kill(computerID);
|
|
68295
|
-
return void 0;
|
|
68296
|
-
},
|
|
68297
|
-
setComputerTimeout: async ({ computerID, timeout }) => computers.setTimeout(computerID, timeout),
|
|
68298
|
-
stopComputer: ({ computerID }) => computers.stop(computerID),
|
|
68299
|
-
startComputer: ({ computerID, timeout }) => computers.start(computerID, timeout),
|
|
68300
|
-
getComputerLogs: async ({ computerID }) => ({ logs: computers.logs(computerID) }),
|
|
68301
|
-
createLiveSession: async ({ computerID, surface, mode }) => computers.liveSession(computerID, surface, mode ?? "watch"),
|
|
68302
|
-
openLiveSession: async () => {
|
|
68303
|
-
throw new ApiError(426, "Open it as a WebSocket.");
|
|
68304
|
-
}
|
|
68305
|
-
};
|
|
68306
|
-
}
|
|
68307
|
-
|
|
68308
68354
|
// src/running.ts
|
|
68309
68355
|
import { spawn as spawn4 } from "node:child_process";
|
|
68310
68356
|
import { closeSync, openSync, readFileSync as readFileSync4, statSync as statSync3, writeFileSync as writeFileSync2 } from "node:fs";
|
|
@@ -69177,7 +69223,7 @@ var Computer = class _Computer {
|
|
|
69177
69223
|
};
|
|
69178
69224
|
|
|
69179
69225
|
// src/service.ts
|
|
69180
|
-
import { readFileSync as readFileSync6, rmSync as
|
|
69226
|
+
import { readFileSync as readFileSync6, rmSync as rmSync6 } from "node:fs";
|
|
69181
69227
|
import {
|
|
69182
69228
|
createServer as createServer5,
|
|
69183
69229
|
STATUS_CODES as STATUS_CODES3
|
|
@@ -84997,7 +85043,7 @@ var NodeStreamableHTTPServerTransport2 = class {
|
|
|
84997
85043
|
|
|
84998
85044
|
// src/computers.ts
|
|
84999
85045
|
import { randomBytes as randomBytes5 } from "node:crypto";
|
|
85000
|
-
import { appendFileSync, existsSync as existsSync2, mkdirSync as
|
|
85046
|
+
import { appendFileSync, existsSync as existsSync2, mkdirSync as mkdirSync3, readFileSync as readFileSync5, rmSync as rmSync5, statSync as statSync4 } from "node:fs";
|
|
85001
85047
|
import { isAbsolute, join as join10, resolve as resolve4 } from "node:path";
|
|
85002
85048
|
var READ = ["about", "read", "list", "search"];
|
|
85003
85049
|
var FILES = [...READ, "edit"];
|
|
@@ -85010,8 +85056,8 @@ var MAX_LOG2 = 256 * 1024;
|
|
|
85010
85056
|
var MAX_TIMER_S = 2e6;
|
|
85011
85057
|
var TICKET_MS = 6e4;
|
|
85012
85058
|
var expiry = (seconds2) => new Date(Date.now() + seconds2 * 1e3).toISOString();
|
|
85013
|
-
var
|
|
85014
|
-
var refusalOf = (error62) => error62 instanceof ApiError ? error62 : new ApiError(500,
|
|
85059
|
+
var messageOf2 = (error62) => error62 instanceof Error ? error62.message : String(error62);
|
|
85060
|
+
var refusalOf = (error62) => error62 instanceof ApiError ? error62 : new ApiError(500, messageOf2(error62));
|
|
85015
85061
|
var Computers = class {
|
|
85016
85062
|
#data;
|
|
85017
85063
|
#base;
|
|
@@ -85045,7 +85091,7 @@ var Computers = class {
|
|
|
85045
85091
|
if (stored.endAt && Date.parse(stored.endAt) <= Date.now()) {
|
|
85046
85092
|
stored.state = "stopped";
|
|
85047
85093
|
await this.#runtimes[stored.isolation].stopLeftover(stored.id).catch(
|
|
85048
|
-
(error62) => this.#log(stored.id, `What it left running wasn't stopped: ${
|
|
85094
|
+
(error62) => this.#log(stored.id, `What it left running wasn't stopped: ${messageOf2(error62)}`)
|
|
85049
85095
|
);
|
|
85050
85096
|
continue;
|
|
85051
85097
|
}
|
|
@@ -85053,7 +85099,7 @@ var Computers = class {
|
|
|
85053
85099
|
const live = this.#live.get(stored.id);
|
|
85054
85100
|
if (live) live.failed = true;
|
|
85055
85101
|
this.#arm(stored);
|
|
85056
|
-
this.#log(stored.id, `It didn't start again: ${
|
|
85102
|
+
this.#log(stored.id, `It didn't start again: ${messageOf2(error62)}`);
|
|
85057
85103
|
});
|
|
85058
85104
|
}
|
|
85059
85105
|
this.#save();
|
|
@@ -85092,7 +85138,7 @@ var Computers = class {
|
|
|
85092
85138
|
};
|
|
85093
85139
|
if (!workspace && access !== "none") {
|
|
85094
85140
|
stored.workspace = join10(this.#folder(id), "workspace");
|
|
85095
|
-
|
|
85141
|
+
mkdirSync3(stored.workspace, { recursive: true, mode: 448 });
|
|
85096
85142
|
}
|
|
85097
85143
|
this.#stored.set(id, stored);
|
|
85098
85144
|
try {
|
|
@@ -85102,7 +85148,7 @@ var Computers = class {
|
|
|
85102
85148
|
this.#stored.delete(id);
|
|
85103
85149
|
await this.#runtimes[stored.isolation].remove(id).catch(() => {
|
|
85104
85150
|
});
|
|
85105
|
-
|
|
85151
|
+
rmSync5(this.#folder(id), { recursive: true, force: true });
|
|
85106
85152
|
throw refusalOf(error62);
|
|
85107
85153
|
}
|
|
85108
85154
|
this.#save();
|
|
@@ -85176,7 +85222,7 @@ var Computers = class {
|
|
|
85176
85222
|
}
|
|
85177
85223
|
this.#stored.delete(id);
|
|
85178
85224
|
this.#save();
|
|
85179
|
-
|
|
85225
|
+
rmSync5(this.#folder(id), { recursive: true, force: true });
|
|
85180
85226
|
}
|
|
85181
85227
|
logs(id) {
|
|
85182
85228
|
this.#found(id);
|
|
@@ -85277,6 +85323,7 @@ var Computers = class {
|
|
|
85277
85323
|
state: stored.state === "running" && down2 ? "failed" : stored.state,
|
|
85278
85324
|
access: stored.access,
|
|
85279
85325
|
isolation: stored.isolation,
|
|
85326
|
+
version: stored.version ?? null,
|
|
85280
85327
|
browser: stored.browser,
|
|
85281
85328
|
workspace: stored.workspace ?? null,
|
|
85282
85329
|
startedAt: stored.startedAt,
|
|
@@ -85310,6 +85357,9 @@ var Computers = class {
|
|
|
85310
85357
|
}
|
|
85311
85358
|
}
|
|
85312
85359
|
);
|
|
85360
|
+
const version3 = stored.isolation === "none" ? VERSION2 : live.running.version;
|
|
85361
|
+
if (version3) stored.version = version3;
|
|
85362
|
+
else delete stored.version;
|
|
85313
85363
|
this.#arm(stored);
|
|
85314
85364
|
}
|
|
85315
85365
|
#arm(stored) {
|
|
@@ -85347,7 +85397,7 @@ var OPENAPI = {
|
|
|
85347
85397
|
"openapi": "3.1.0",
|
|
85348
85398
|
"info": {
|
|
85349
85399
|
"title": "AgentComputer",
|
|
85350
|
-
"version": "0.0.
|
|
85400
|
+
"version": "0.0.18",
|
|
85351
85401
|
"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.",
|
|
85352
85402
|
"license": {
|
|
85353
85403
|
"name": "Apache-2.0",
|
|
@@ -85901,6 +85951,7 @@ var OPENAPI = {
|
|
|
85901
85951
|
"state",
|
|
85902
85952
|
"access",
|
|
85903
85953
|
"isolation",
|
|
85954
|
+
"version",
|
|
85904
85955
|
"browser",
|
|
85905
85956
|
"workspace",
|
|
85906
85957
|
"startedAt",
|
|
@@ -85925,6 +85976,13 @@ var OPENAPI = {
|
|
|
85925
85976
|
"$ref": "#/components/schemas/Isolation",
|
|
85926
85977
|
"description": "The isolation it was made in. It is never run in another."
|
|
85927
85978
|
},
|
|
85979
|
+
"version": {
|
|
85980
|
+
"type": [
|
|
85981
|
+
"string",
|
|
85982
|
+
"null"
|
|
85983
|
+
],
|
|
85984
|
+
"description": "The version of the program it ran at its last start. With no isolation it is the service's `version` (see `getInfo`). In a container it is the version inside its image, and null for a container made before 0.0.17, which doesn't say, until it is made again. It is kept from its last start, so a stopped computer's can be older than the service's until it starts. Compare it with the service's to show that a computer runs an older program."
|
|
85985
|
+
},
|
|
85928
85986
|
"browser": {
|
|
85929
85987
|
"type": "boolean"
|
|
85930
85988
|
},
|
|
@@ -87196,6 +87254,7 @@ var OPERATIONS = [
|
|
|
87196
87254
|
"state",
|
|
87197
87255
|
"access",
|
|
87198
87256
|
"isolation",
|
|
87257
|
+
"version",
|
|
87199
87258
|
"browser",
|
|
87200
87259
|
"workspace",
|
|
87201
87260
|
"startedAt",
|
|
@@ -87237,6 +87296,13 @@ var OPERATIONS = [
|
|
|
87237
87296
|
],
|
|
87238
87297
|
"description": "The isolation it was made in. It is never run in another."
|
|
87239
87298
|
},
|
|
87299
|
+
"version": {
|
|
87300
|
+
"type": [
|
|
87301
|
+
"string",
|
|
87302
|
+
"null"
|
|
87303
|
+
],
|
|
87304
|
+
"description": "The version of the program it ran at its last start. With no isolation it is the service's `version` (see `getInfo`). In a container it is the version inside its image, and null for a container made before 0.0.17, which doesn't say, until it is made again. It is kept from its last start, so a stopped computer's can be older than the service's until it starts. Compare it with the service's to show that a computer runs an older program."
|
|
87305
|
+
},
|
|
87240
87306
|
"browser": {
|
|
87241
87307
|
"type": "boolean"
|
|
87242
87308
|
},
|
|
@@ -87373,6 +87439,7 @@ var OPERATIONS = [
|
|
|
87373
87439
|
"state",
|
|
87374
87440
|
"access",
|
|
87375
87441
|
"isolation",
|
|
87442
|
+
"version",
|
|
87376
87443
|
"browser",
|
|
87377
87444
|
"workspace",
|
|
87378
87445
|
"startedAt",
|
|
@@ -87414,6 +87481,13 @@ var OPERATIONS = [
|
|
|
87414
87481
|
],
|
|
87415
87482
|
"description": "The isolation it was made in. It is never run in another."
|
|
87416
87483
|
},
|
|
87484
|
+
"version": {
|
|
87485
|
+
"type": [
|
|
87486
|
+
"string",
|
|
87487
|
+
"null"
|
|
87488
|
+
],
|
|
87489
|
+
"description": "The version of the program it ran at its last start. With no isolation it is the service's `version` (see `getInfo`). In a container it is the version inside its image, and null for a container made before 0.0.17, which doesn't say, until it is made again. It is kept from its last start, so a stopped computer's can be older than the service's until it starts. Compare it with the service's to show that a computer runs an older program."
|
|
87490
|
+
},
|
|
87417
87491
|
"browser": {
|
|
87418
87492
|
"type": "boolean"
|
|
87419
87493
|
},
|
|
@@ -87523,6 +87597,7 @@ var OPERATIONS = [
|
|
|
87523
87597
|
"state",
|
|
87524
87598
|
"access",
|
|
87525
87599
|
"isolation",
|
|
87600
|
+
"version",
|
|
87526
87601
|
"browser",
|
|
87527
87602
|
"workspace",
|
|
87528
87603
|
"startedAt",
|
|
@@ -87564,6 +87639,13 @@ var OPERATIONS = [
|
|
|
87564
87639
|
],
|
|
87565
87640
|
"description": "The isolation it was made in. It is never run in another."
|
|
87566
87641
|
},
|
|
87642
|
+
"version": {
|
|
87643
|
+
"type": [
|
|
87644
|
+
"string",
|
|
87645
|
+
"null"
|
|
87646
|
+
],
|
|
87647
|
+
"description": "The version of the program it ran at its last start. With no isolation it is the service's `version` (see `getInfo`). In a container it is the version inside its image, and null for a container made before 0.0.17, which doesn't say, until it is made again. It is kept from its last start, so a stopped computer's can be older than the service's until it starts. Compare it with the service's to show that a computer runs an older program."
|
|
87648
|
+
},
|
|
87567
87649
|
"browser": {
|
|
87568
87650
|
"type": "boolean"
|
|
87569
87651
|
},
|
|
@@ -87764,6 +87846,7 @@ var OPERATIONS = [
|
|
|
87764
87846
|
"state",
|
|
87765
87847
|
"access",
|
|
87766
87848
|
"isolation",
|
|
87849
|
+
"version",
|
|
87767
87850
|
"browser",
|
|
87768
87851
|
"workspace",
|
|
87769
87852
|
"startedAt",
|
|
@@ -87805,6 +87888,13 @@ var OPERATIONS = [
|
|
|
87805
87888
|
],
|
|
87806
87889
|
"description": "The isolation it was made in. It is never run in another."
|
|
87807
87890
|
},
|
|
87891
|
+
"version": {
|
|
87892
|
+
"type": [
|
|
87893
|
+
"string",
|
|
87894
|
+
"null"
|
|
87895
|
+
],
|
|
87896
|
+
"description": "The version of the program it ran at its last start. With no isolation it is the service's `version` (see `getInfo`). In a container it is the version inside its image, and null for a container made before 0.0.17, which doesn't say, until it is made again. It is kept from its last start, so a stopped computer's can be older than the service's until it starts. Compare it with the service's to show that a computer runs an older program."
|
|
87897
|
+
},
|
|
87808
87898
|
"browser": {
|
|
87809
87899
|
"type": "boolean"
|
|
87810
87900
|
},
|
|
@@ -87898,6 +87988,7 @@ var OPERATIONS = [
|
|
|
87898
87988
|
"state",
|
|
87899
87989
|
"access",
|
|
87900
87990
|
"isolation",
|
|
87991
|
+
"version",
|
|
87901
87992
|
"browser",
|
|
87902
87993
|
"workspace",
|
|
87903
87994
|
"startedAt",
|
|
@@ -87939,6 +88030,13 @@ var OPERATIONS = [
|
|
|
87939
88030
|
],
|
|
87940
88031
|
"description": "The isolation it was made in. It is never run in another."
|
|
87941
88032
|
},
|
|
88033
|
+
"version": {
|
|
88034
|
+
"type": [
|
|
88035
|
+
"string",
|
|
88036
|
+
"null"
|
|
88037
|
+
],
|
|
88038
|
+
"description": "The version of the program it ran at its last start. With no isolation it is the service's `version` (see `getInfo`). In a container it is the version inside its image, and null for a container made before 0.0.17, which doesn't say, until it is made again. It is kept from its last start, so a stopped computer's can be older than the service's until it starts. Compare it with the service's to show that a computer runs an older program."
|
|
88039
|
+
},
|
|
87942
88040
|
"browser": {
|
|
87943
88041
|
"type": "boolean"
|
|
87944
88042
|
},
|
|
@@ -88037,6 +88135,7 @@ var OPERATIONS = [
|
|
|
88037
88135
|
"state",
|
|
88038
88136
|
"access",
|
|
88039
88137
|
"isolation",
|
|
88138
|
+
"version",
|
|
88040
88139
|
"browser",
|
|
88041
88140
|
"workspace",
|
|
88042
88141
|
"startedAt",
|
|
@@ -88078,6 +88177,13 @@ var OPERATIONS = [
|
|
|
88078
88177
|
],
|
|
88079
88178
|
"description": "The isolation it was made in. It is never run in another."
|
|
88080
88179
|
},
|
|
88180
|
+
"version": {
|
|
88181
|
+
"type": [
|
|
88182
|
+
"string",
|
|
88183
|
+
"null"
|
|
88184
|
+
],
|
|
88185
|
+
"description": "The version of the program it ran at its last start. With no isolation it is the service's `version` (see `getInfo`). In a container it is the version inside its image, and null for a container made before 0.0.17, which doesn't say, until it is made again. It is kept from its last start, so a stopped computer's can be older than the service's until it starts. Compare it with the service's to show that a computer runs an older program."
|
|
88186
|
+
},
|
|
88081
88187
|
"browser": {
|
|
88082
88188
|
"type": "boolean"
|
|
88083
88189
|
},
|
|
@@ -88472,13 +88578,13 @@ Connection: close\r
|
|
|
88472
88578
|
${JSON.stringify({ code: status, message })}
|
|
88473
88579
|
`
|
|
88474
88580
|
);
|
|
88475
|
-
const
|
|
88581
|
+
const pathname = URL.parse(request.url ?? "/", "http://localhost")?.pathname ?? "";
|
|
88476
88582
|
const ticket = /^\/v1\/live\/([^/]+)$/.exec(pathname)?.[1];
|
|
88477
88583
|
if (!ticket) return refuse(404, "Not found.");
|
|
88478
88584
|
const origin = request.headers.origin;
|
|
88479
88585
|
if (origin !== void 0 && !allowed.has(origin))
|
|
88480
88586
|
return refuse(403, `A live session isn't opened from ${origin.slice(0, 200)}.`);
|
|
88481
|
-
computers.openLive(
|
|
88587
|
+
computers.openLive(ticket, request, socket, head).catch((error62) => {
|
|
88482
88588
|
if (error62 instanceof ApiError) refuse(error62.status, error62.message);
|
|
88483
88589
|
else socket.destroy();
|
|
88484
88590
|
});
|
|
@@ -88491,7 +88597,7 @@ ${JSON.stringify({ code: status, message })}
|
|
|
88491
88597
|
const scheme = options.tls ? "https" : "http";
|
|
88492
88598
|
base = options.publicUrl?.replace(/\/+$/, "") ?? `${scheme}://${bound.family === "IPv6" ? `[${bound.address}]` : bound.address}:${bound.port}`;
|
|
88493
88599
|
const record3 = recordOf(data);
|
|
88494
|
-
|
|
88600
|
+
rmSync6(record3, { force: true });
|
|
88495
88601
|
await computers.resume();
|
|
88496
88602
|
writePrivate(
|
|
88497
88603
|
record3,
|
|
@@ -88507,7 +88613,7 @@ ${JSON.stringify({ code: status, message })}
|
|
|
88507
88613
|
process.stderr.write(`${String(error62)}
|
|
88508
88614
|
`);
|
|
88509
88615
|
});
|
|
88510
|
-
|
|
88616
|
+
rmSync6(record3, { force: true });
|
|
88511
88617
|
process.exit(0);
|
|
88512
88618
|
};
|
|
88513
88619
|
for (const signal of ["SIGINT", "SIGTERM"]) process.once(signal, () => void stop());
|
|
@@ -231,6 +231,8 @@ export interface components {
|
|
|
231
231
|
access: components["schemas"]["Access"];
|
|
232
232
|
/** @description The isolation it was made in. It is never run in another. */
|
|
233
233
|
isolation: components["schemas"]["Isolation"];
|
|
234
|
+
/** @description The version of the program it ran at its last start. With no isolation it is the service's `version` (see `getInfo`). In a container it is the version inside its image, and null for a container made before 0.0.17, which doesn't say, until it is made again. It is kept from its last start, so a stopped computer's can be older than the service's until it starts. Compare it with the service's to show that a computer runs an older program. */
|
|
235
|
+
version: string | null;
|
|
234
236
|
browser: boolean;
|
|
235
237
|
workspace: string | null;
|
|
236
238
|
/**
|
package/dist/types/sdk.d.ts
CHANGED
|
@@ -174,6 +174,7 @@ export declare class Computer {
|
|
|
174
174
|
state: components["schemas"]["ComputerState"];
|
|
175
175
|
access: components["schemas"]["Access"];
|
|
176
176
|
isolation: components["schemas"]["Isolation"];
|
|
177
|
+
version: string | null;
|
|
177
178
|
browser: boolean;
|
|
178
179
|
workspace: string | null;
|
|
179
180
|
startedAt: string;
|
|
@@ -195,6 +196,7 @@ export declare class Computer {
|
|
|
195
196
|
state: components["schemas"]["ComputerState"];
|
|
196
197
|
access: components["schemas"]["Access"];
|
|
197
198
|
isolation: components["schemas"]["Isolation"];
|
|
199
|
+
version: string | null;
|
|
198
200
|
browser: boolean;
|
|
199
201
|
workspace: string | null;
|
|
200
202
|
startedAt: string;
|
|
@@ -208,6 +210,7 @@ export declare class Computer {
|
|
|
208
210
|
state: components["schemas"]["ComputerState"];
|
|
209
211
|
access: components["schemas"]["Access"];
|
|
210
212
|
isolation: components["schemas"]["Isolation"];
|
|
213
|
+
version: string | null;
|
|
211
214
|
browser: boolean;
|
|
212
215
|
workspace: string | null;
|
|
213
216
|
startedAt: string;
|
|
@@ -223,6 +226,7 @@ export declare class Computer {
|
|
|
223
226
|
state: components["schemas"]["ComputerState"];
|
|
224
227
|
access: components["schemas"]["Access"];
|
|
225
228
|
isolation: components["schemas"]["Isolation"];
|
|
229
|
+
version: string | null;
|
|
226
230
|
browser: boolean;
|
|
227
231
|
workspace: string | null;
|
|
228
232
|
startedAt: string;
|