@agentorg/computer 0.0.15 → 0.0.17
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 +9 -8
- package/dist/agentcomputer.js +273 -92
- 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,15 +60,15 @@ 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
|
-
`--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.
|
|
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
|
|
|
71
73
|
## Commands
|
|
72
74
|
|
|
@@ -214,7 +216,6 @@ A shell of your own needs [node-pty](https://github.com/microsoft/node-pty), an
|
|
|
214
216
|
|
|
215
217
|
## Known limits
|
|
216
218
|
|
|
217
|
-
- With no isolation, each browser's Playwright MCP listens on a loopback port with no token of its own, so another user on the same machine could reach it directly. Don't run that on a machine you share.
|
|
218
219
|
- A container has no CPU or memory limit, and its network is Docker's default, so it reaches the internet and anything else Docker's network does.
|
|
219
220
|
- In a container the browser runs without its own sandbox, which needs privileges a computer doesn't have. The container is the boundary.
|
|
220
221
|
- A browser that stops, or a container that ends by itself, shows the computer as `failed`, and what can no longer be opened live says so in `surfaces`. So does a computer that couldn't be brought back when the service started, for one in a container because Docker didn't answer; its `logs` say why. Stop the computer and start it again.
|
package/dist/agentcomputer.js
CHANGED
|
@@ -3804,49 +3804,78 @@ async function hostBrowser(options) {
|
|
|
3804
3804
|
}
|
|
3805
3805
|
|
|
3806
3806
|
// src/browser-mcp.ts
|
|
3807
|
+
import { mkdtempSync as mkdtempSync2, rmSync as rmSync2 } from "node:fs";
|
|
3807
3808
|
import { createRequire as createRequire2 } from "node:module";
|
|
3809
|
+
import { join as join2 } from "node:path";
|
|
3808
3810
|
var require2 = createRequire2(import.meta.url);
|
|
3809
3811
|
var mcpParts = () => require2("playwright-core/lib/coreBundle").tools;
|
|
3812
|
+
function listenOnSocket(socket) {
|
|
3813
|
+
const http = require2("node:http");
|
|
3814
|
+
const make = http.createServer;
|
|
3815
|
+
http.createServer = ((...args) => {
|
|
3816
|
+
http.createServer = make;
|
|
3817
|
+
const server = make(...args);
|
|
3818
|
+
const listen = server.listen.bind(server);
|
|
3819
|
+
server.listen = (() => listen(socket));
|
|
3820
|
+
server.address = () => ({ address: "127.0.0.1", family: "IPv4", port: 0 });
|
|
3821
|
+
return server;
|
|
3822
|
+
});
|
|
3823
|
+
}
|
|
3810
3824
|
async function serveBrowserMcp(options) {
|
|
3811
3825
|
const parts = mcpParts();
|
|
3812
3826
|
parts.setupExitWatchdog();
|
|
3827
|
+
const folder = mkdtempSync2(join2("/tmp", "agentcomputer-"));
|
|
3828
|
+
process.once("exit", () => rmSync2(folder, { recursive: true, force: true }));
|
|
3829
|
+
const socket = join2(folder, "mcp.sock");
|
|
3813
3830
|
const config3 = await parts.resolveCLIConfigForMCP({
|
|
3814
3831
|
endpoint: options.endpoint,
|
|
3815
3832
|
outputDir: options.outputDir,
|
|
3816
3833
|
host: "127.0.0.1",
|
|
3817
3834
|
port: 0,
|
|
3835
|
+
// What the proxy sends, which is all that can reach the socket.
|
|
3836
|
+
allowedHosts: ["localhost"],
|
|
3818
3837
|
// The tool that closes the browser refuses, as it does for any browser its clients share.
|
|
3819
3838
|
sharedBrowserContext: true
|
|
3820
3839
|
});
|
|
3821
3840
|
const tools = parts.filteredTools(config3);
|
|
3822
|
-
|
|
3823
|
-
|
|
3824
|
-
|
|
3825
|
-
|
|
3826
|
-
|
|
3827
|
-
|
|
3828
|
-
|
|
3829
|
-
|
|
3830
|
-
|
|
3831
|
-
|
|
3832
|
-
|
|
3833
|
-
|
|
3834
|
-
|
|
3835
|
-
|
|
3836
|
-
|
|
3837
|
-
|
|
3838
|
-
|
|
3839
|
-
|
|
3840
|
-
|
|
3841
|
-
|
|
3842
|
-
|
|
3843
|
-
|
|
3844
|
-
|
|
3841
|
+
listenOnSocket(socket);
|
|
3842
|
+
const say = console.error;
|
|
3843
|
+
console.error = () => {
|
|
3844
|
+
};
|
|
3845
|
+
try {
|
|
3846
|
+
await parts.start(
|
|
3847
|
+
{
|
|
3848
|
+
name: "Playwright",
|
|
3849
|
+
nameInConfig: "playwright",
|
|
3850
|
+
// playwright-core's, as Playwright MCP's program gives it.
|
|
3851
|
+
version: require2("playwright-core/package.json").version,
|
|
3852
|
+
toolSchemas: tools.map((tool) => tool.schema),
|
|
3853
|
+
ignoreRoots: config3.ignoreRoots,
|
|
3854
|
+
/** One for each client's session: a connection of its own to the one browser. */
|
|
3855
|
+
create: async () => {
|
|
3856
|
+
const browser = await playwright().chromium.connect(options.endpoint);
|
|
3857
|
+
const context = browser.contexts()[0];
|
|
3858
|
+
if (!context) {
|
|
3859
|
+
await browser.close();
|
|
3860
|
+
throw new Error("The browser has no context to work in.");
|
|
3861
|
+
}
|
|
3862
|
+
return new parts.BrowserBackend(config3, context, tools, {
|
|
3863
|
+
dispose: () => browser.close()
|
|
3864
|
+
});
|
|
3865
|
+
}
|
|
3866
|
+
},
|
|
3867
|
+
config3.server
|
|
3868
|
+
);
|
|
3869
|
+
} finally {
|
|
3870
|
+
console.error = say;
|
|
3871
|
+
}
|
|
3872
|
+
process.stdout.write(`${JSON.stringify({ socket })}
|
|
3873
|
+
`);
|
|
3845
3874
|
}
|
|
3846
3875
|
|
|
3847
3876
|
// src/container.ts
|
|
3848
3877
|
import { execFile as execFile2, spawn as spawn3 } from "node:child_process";
|
|
3849
|
-
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";
|
|
3850
3879
|
import { connect } from "node:net";
|
|
3851
3880
|
import { hostname as hostname4 } from "node:os";
|
|
3852
3881
|
import { join as join8 } from "node:path";
|
|
@@ -3856,7 +3885,9 @@ import { promisify as promisify2 } from "node:util";
|
|
|
3856
3885
|
|
|
3857
3886
|
// src/browser.ts
|
|
3858
3887
|
import { spawn } from "node:child_process";
|
|
3859
|
-
import {
|
|
3888
|
+
import { rmSync as rmSync3 } from "node:fs";
|
|
3889
|
+
import { request as ask } from "node:http";
|
|
3890
|
+
import { dirname, join as join3 } from "node:path";
|
|
3860
3891
|
import { createInterface } from "node:readline";
|
|
3861
3892
|
import { pipeline, Readable } from "node:stream";
|
|
3862
3893
|
import { fileURLToPath } from "node:url";
|
|
@@ -27982,6 +28013,42 @@ function sendAnswer(upstream, response) {
|
|
|
27982
28013
|
pipeline(Readable.fromWeb(upstream.body), response, () => {
|
|
27983
28014
|
});
|
|
27984
28015
|
}
|
|
28016
|
+
function socketFetch(socket) {
|
|
28017
|
+
return (url3, init = {}) => new Promise((done, failed) => {
|
|
28018
|
+
const { body } = init;
|
|
28019
|
+
if (body != null && typeof body !== "string" && !(body instanceof Uint8Array))
|
|
28020
|
+
return failed(new TypeError("A body to the browser's server is text or bytes."));
|
|
28021
|
+
const { pathname, search: search2 } = new URL(url3);
|
|
28022
|
+
const headers = Object.fromEntries(new Headers(init.headers));
|
|
28023
|
+
if (body != null) headers["content-length"] = String(Buffer.byteLength(body));
|
|
28024
|
+
const sent = ask(
|
|
28025
|
+
{
|
|
28026
|
+
socketPath: socket,
|
|
28027
|
+
path: pathname + search2,
|
|
28028
|
+
method: init.method ?? "GET",
|
|
28029
|
+
headers,
|
|
28030
|
+
signal: init.signal ?? void 0
|
|
28031
|
+
},
|
|
28032
|
+
(answer) => {
|
|
28033
|
+
const answered2 = new Headers();
|
|
28034
|
+
for (const [name, value] of Object.entries(answer.headers))
|
|
28035
|
+
for (const one of [value ?? []].flat()) answered2.append(name, one);
|
|
28036
|
+
const status = answer.statusCode ?? 502;
|
|
28037
|
+
const none2 = [204, 205, 304].includes(status);
|
|
28038
|
+
if (none2) answer.resume();
|
|
28039
|
+
done(
|
|
28040
|
+
new Response(none2 ? null : Readable.toWeb(answer), {
|
|
28041
|
+
status,
|
|
28042
|
+
headers: answered2
|
|
28043
|
+
})
|
|
28044
|
+
);
|
|
28045
|
+
}
|
|
28046
|
+
);
|
|
28047
|
+
sent.on("error", failed);
|
|
28048
|
+
sent.end(body ?? void 0);
|
|
28049
|
+
});
|
|
28050
|
+
}
|
|
28051
|
+
var MCP = "http://localhost/mcp";
|
|
27985
28052
|
var self = fileURLToPath(import.meta.url);
|
|
27986
28053
|
function changed(text3, change) {
|
|
27987
28054
|
const one = (json4) => {
|
|
@@ -28016,7 +28083,10 @@ async function end(child) {
|
|
|
28016
28083
|
var Browser = class {
|
|
28017
28084
|
#host;
|
|
28018
28085
|
#mcp;
|
|
28019
|
-
|
|
28086
|
+
/** Where its server answers, by way of its Unix socket. */
|
|
28087
|
+
#served;
|
|
28088
|
+
/** The folder its server made for its socket. */
|
|
28089
|
+
#socketFolder;
|
|
28020
28090
|
/** The bound browser's socket, for the live viewer. */
|
|
28021
28091
|
endpoint;
|
|
28022
28092
|
#hidden;
|
|
@@ -28038,7 +28108,7 @@ var Browser = class {
|
|
|
28038
28108
|
self,
|
|
28039
28109
|
"browser-host",
|
|
28040
28110
|
"--profile",
|
|
28041
|
-
|
|
28111
|
+
join3(folder, "profile"),
|
|
28042
28112
|
...config3.path ? ["--executable", config3.path] : ["--channel", config3.browser ?? "chrome"],
|
|
28043
28113
|
...config3.sandbox === false ? ["--no-sandbox"] : []
|
|
28044
28114
|
],
|
|
@@ -28061,12 +28131,12 @@ var Browser = class {
|
|
|
28061
28131
|
});
|
|
28062
28132
|
this.endpoint.catch(() => {
|
|
28063
28133
|
});
|
|
28064
|
-
this.#
|
|
28134
|
+
this.#served = this.endpoint.then(
|
|
28065
28135
|
(endpoint) => new Promise((done, failed) => {
|
|
28066
28136
|
if (this.#stopping) return failed(new Error("It stopped."));
|
|
28067
28137
|
const mcp = spawn(
|
|
28068
28138
|
process.execPath,
|
|
28069
|
-
[self, "browser-mcp", "--endpoint", endpoint, "--output",
|
|
28139
|
+
[self, "browser-mcp", "--endpoint", endpoint, "--output", join3(folder, "output")],
|
|
28070
28140
|
// Its own limit on which files it touches is the folder it starts in.
|
|
28071
28141
|
{ cwd, stdio: ["ignore", "pipe", "pipe"] }
|
|
28072
28142
|
);
|
|
@@ -28077,34 +28147,39 @@ var Browser = class {
|
|
|
28077
28147
|
(code) => failed(new Error(`The browser's server exited with ${code}.`))
|
|
28078
28148
|
);
|
|
28079
28149
|
mcp.once("exit", ended("The browser's server"));
|
|
28080
|
-
|
|
28081
|
-
|
|
28150
|
+
createInterface({ input: mcp.stdout }).on("line", (line) => {
|
|
28151
|
+
try {
|
|
28152
|
+
const { socket } = JSON.parse(line);
|
|
28153
|
+
log(`The browser's server is on ${socket}.`);
|
|
28154
|
+
this.#socketFolder = dirname(socket);
|
|
28155
|
+
done(socketFetch(socket));
|
|
28156
|
+
} catch {
|
|
28082
28157
|
log(line);
|
|
28083
|
-
|
|
28084
|
-
|
|
28085
|
-
|
|
28158
|
+
}
|
|
28159
|
+
});
|
|
28160
|
+
createInterface({ input: mcp.stderr }).on("line", log);
|
|
28086
28161
|
})
|
|
28087
28162
|
);
|
|
28088
|
-
this.#
|
|
28163
|
+
this.#served.catch(() => {
|
|
28089
28164
|
});
|
|
28090
28165
|
}
|
|
28091
28166
|
/** Wait until it answers, and read its tools. */
|
|
28092
28167
|
async ready() {
|
|
28093
|
-
const
|
|
28094
|
-
this.#
|
|
28168
|
+
const served = await Promise.race([
|
|
28169
|
+
this.#served,
|
|
28095
28170
|
new Promise(
|
|
28096
28171
|
(_, failed) => setTimeout(() => failed(new Error("The browser's server didn't start.")), START_MS)
|
|
28097
28172
|
)
|
|
28098
28173
|
]);
|
|
28099
28174
|
const client = new Client({ name: "agentcomputer", version: "0" });
|
|
28100
|
-
await client.connect(new StreamableHTTPClientTransport(new URL(
|
|
28175
|
+
await client.connect(new StreamableHTTPClientTransport(new URL(MCP), { fetch: served }));
|
|
28101
28176
|
const { tools } = await client.listTools();
|
|
28102
28177
|
await client.close();
|
|
28103
28178
|
this.tools = tools.map((tool) => tool.name).filter((name) => !this.#hidden.has(name));
|
|
28104
28179
|
}
|
|
28105
28180
|
/** Answer one MCP request by passing it on, enforcing what this computer may not do. */
|
|
28106
28181
|
async handle(request, response) {
|
|
28107
|
-
const
|
|
28182
|
+
const served = await this.#served;
|
|
28108
28183
|
const chunks = [];
|
|
28109
28184
|
let size = 0;
|
|
28110
28185
|
for await (const chunk of request) {
|
|
@@ -28148,7 +28223,7 @@ var Browser = class {
|
|
|
28148
28223
|
const session = headers["mcp-session-id"] ?? "";
|
|
28149
28224
|
if (request.method === "DELETE") this.gate?.sessionEnded(session);
|
|
28150
28225
|
if (tool && this.gate?.takeAnsweredChooser(session) && tool !== "browser_file_upload")
|
|
28151
|
-
await
|
|
28226
|
+
await served(MCP, {
|
|
28152
28227
|
method: "POST",
|
|
28153
28228
|
headers,
|
|
28154
28229
|
body: JSON.stringify({
|
|
@@ -28161,7 +28236,7 @@ var Browser = class {
|
|
|
28161
28236
|
});
|
|
28162
28237
|
if (tool === "browser_file_upload") this.gate?.agentAnsweredChooser();
|
|
28163
28238
|
if (message) headers["content-type"] = "application/json";
|
|
28164
|
-
const upstream = await
|
|
28239
|
+
const upstream = await served(MCP, {
|
|
28165
28240
|
method: request.method,
|
|
28166
28241
|
headers,
|
|
28167
28242
|
...message ? { body: JSON.stringify(message) } : {}
|
|
@@ -28176,6 +28251,7 @@ var Browser = class {
|
|
|
28176
28251
|
async stop() {
|
|
28177
28252
|
this.#stopping = true;
|
|
28178
28253
|
await Promise.all([this.#mcp, this.#host].map((child) => child ? end(child) : void 0));
|
|
28254
|
+
if (this.#socketFolder) rmSync3(this.#socketFolder, { recursive: true, force: true });
|
|
28179
28255
|
}
|
|
28180
28256
|
};
|
|
28181
28257
|
|
|
@@ -28188,7 +28264,7 @@ import { join as join7 } from "node:path";
|
|
|
28188
28264
|
import { chmodSync as chmodSync2, existsSync, statSync as statSync2 } from "node:fs";
|
|
28189
28265
|
import { createRequire as createRequire3 } from "node:module";
|
|
28190
28266
|
import { hostname as hostname3 } from "node:os";
|
|
28191
|
-
import { basename as basename2, dirname as
|
|
28267
|
+
import { basename as basename2, dirname as dirname4, join as join6 } from "node:path";
|
|
28192
28268
|
|
|
28193
28269
|
// node_modules/@agentorg/exec/dist/lib.js
|
|
28194
28270
|
import { realpathSync } from "node:fs";
|
|
@@ -28198,7 +28274,7 @@ import { Http2ServerRequest, constants } from "node:http2";
|
|
|
28198
28274
|
import { Readable as Readable2 } from "node:stream";
|
|
28199
28275
|
import { randomBytes as randomBytes3 } from "node:crypto";
|
|
28200
28276
|
import { lstat as lstat2, open as open3, readdir as readdir2, rename as rename2, rm as rm2, stat as stat2 } from "node:fs/promises";
|
|
28201
|
-
import { dirname as dirname2, join as
|
|
28277
|
+
import { dirname as dirname2, join as join32 } from "node:path";
|
|
28202
28278
|
import { randomBytes as randomBytes2 } from "node:crypto";
|
|
28203
28279
|
import {
|
|
28204
28280
|
chmod,
|
|
@@ -28212,7 +28288,7 @@ import {
|
|
|
28212
28288
|
stat,
|
|
28213
28289
|
writeFile
|
|
28214
28290
|
} from "node:fs/promises";
|
|
28215
|
-
import { basename, dirname, join as join4, resolve, sep as sep2 } from "node:path";
|
|
28291
|
+
import { basename, dirname as dirname3, join as join4, resolve, sep as sep2 } from "node:path";
|
|
28216
28292
|
import { spawn as spawn2 } from "node:child_process";
|
|
28217
28293
|
import { randomBytes } from "node:crypto";
|
|
28218
28294
|
import { StringDecoder } from "node:string_decoder";
|
|
@@ -66245,7 +66321,7 @@ async function inside(root, path) {
|
|
|
66245
66321
|
() => false
|
|
66246
66322
|
))
|
|
66247
66323
|
throw refused2();
|
|
66248
|
-
const above =
|
|
66324
|
+
const above = dirname3(at);
|
|
66249
66325
|
if (above === at) throw refused2();
|
|
66250
66326
|
return join4(await real(above), basename(at));
|
|
66251
66327
|
}
|
|
@@ -66333,7 +66409,7 @@ ${cut(shown.join("\n"), MAX_READ_CHARS)}`;
|
|
|
66333
66409
|
after = before.split(input2.old_text).join(input2.new_text);
|
|
66334
66410
|
}
|
|
66335
66411
|
if (after === before) throw new ExecRefused("That edit changes nothing.");
|
|
66336
|
-
const temporary = join4(
|
|
66412
|
+
const temporary = join4(dirname3(path), `.agentexec-${randomBytes2(6).toString("hex")}`);
|
|
66337
66413
|
try {
|
|
66338
66414
|
await writeFile(temporary, after, found ? { mode: found.mode } : {});
|
|
66339
66415
|
if (found) await chmod(temporary, found.mode & 511);
|
|
@@ -66472,7 +66548,7 @@ function createHostFiles(root, level) {
|
|
|
66472
66548
|
found.sort(foldersFirst);
|
|
66473
66549
|
const entries = await Promise.all(
|
|
66474
66550
|
found.slice(0, MAX_ENTRIES).map(async (entry) => {
|
|
66475
|
-
const own22 = await lstat2(
|
|
66551
|
+
const own22 = await lstat2(join32(folder, entry.name)).catch(() => void 0);
|
|
66476
66552
|
return own22 && {
|
|
66477
66553
|
name: entry.name,
|
|
66478
66554
|
kind: entry.isDirectory() ? "folder" : entry.isSymbolicLink() ? "link" : "file",
|
|
@@ -66519,7 +66595,7 @@ function createHostFiles(root, level) {
|
|
|
66519
66595
|
});
|
|
66520
66596
|
if (before?.isDirectory()) throw new ExecRefused(`${path} is a folder.`);
|
|
66521
66597
|
await stat2(dirname2(target));
|
|
66522
|
-
const temporary =
|
|
66598
|
+
const temporary = join32(dirname2(target), `.agentexec-${randomBytes3(6).toString("hex")}`);
|
|
66523
66599
|
const file2 = await open3(temporary, "wx");
|
|
66524
66600
|
const discard = async () => {
|
|
66525
66601
|
await file2.close().catch(() => {
|
|
@@ -67773,7 +67849,7 @@ var loadPty = () => {
|
|
|
67773
67849
|
} catch {
|
|
67774
67850
|
return fails("node-pty isn't installed with this service.");
|
|
67775
67851
|
}
|
|
67776
|
-
const root =
|
|
67852
|
+
const root = dirname4(require3.resolve("node-pty/package.json"));
|
|
67777
67853
|
for (const helper of [
|
|
67778
67854
|
join6(root, "prebuilds", `${process.platform}-${process.arch}`, "spawn-helper"),
|
|
67779
67855
|
join6(root, "build", "Release", "spawn-helper")
|
|
@@ -67881,6 +67957,33 @@ var ProcessRuntime = class {
|
|
|
67881
67957
|
}
|
|
67882
67958
|
};
|
|
67883
67959
|
|
|
67960
|
+
// src/handlers.ts
|
|
67961
|
+
var VERSION2 = "0.0.17";
|
|
67962
|
+
function createHandlers(computers) {
|
|
67963
|
+
return {
|
|
67964
|
+
getInfo: async () => ({
|
|
67965
|
+
version: VERSION2,
|
|
67966
|
+
isolation: computers.isolation,
|
|
67967
|
+
isolationsAvailable: await computers.available()
|
|
67968
|
+
}),
|
|
67969
|
+
listComputers: async ({ state }) => computers.list(state),
|
|
67970
|
+
createComputer: (input2) => computers.create(input2),
|
|
67971
|
+
getComputer: async ({ computerID }) => computers.get(computerID),
|
|
67972
|
+
killComputer: async ({ computerID }) => {
|
|
67973
|
+
await computers.kill(computerID);
|
|
67974
|
+
return void 0;
|
|
67975
|
+
},
|
|
67976
|
+
setComputerTimeout: async ({ computerID, timeout }) => computers.setTimeout(computerID, timeout),
|
|
67977
|
+
stopComputer: ({ computerID }) => computers.stop(computerID),
|
|
67978
|
+
startComputer: ({ computerID, timeout }) => computers.start(computerID, timeout),
|
|
67979
|
+
getComputerLogs: async ({ computerID }) => ({ logs: computers.logs(computerID) }),
|
|
67980
|
+
createLiveSession: async ({ computerID, surface, mode }) => computers.liveSession(computerID, surface, mode ?? "watch"),
|
|
67981
|
+
openLiveSession: async () => {
|
|
67982
|
+
throw new ApiError(426, "Open it as a WebSocket.");
|
|
67983
|
+
}
|
|
67984
|
+
};
|
|
67985
|
+
}
|
|
67986
|
+
|
|
67884
67987
|
// src/inside.ts
|
|
67885
67988
|
var INNER_PORT = 8e3;
|
|
67886
67989
|
var INNER_TOKEN_VARIABLE = "AGENTCOMPUTER_INNER_TOKEN";
|
|
@@ -67921,7 +68024,7 @@ async function serveInside(options) {
|
|
|
67921
68024
|
if (pathname === "/ready") {
|
|
67922
68025
|
const { browserTools, surfaces } = await running2;
|
|
67923
68026
|
response.writeHead(200, { "Content-Type": "application/json" });
|
|
67924
|
-
return void response.end(JSON.stringify({ browserTools, surfaces }));
|
|
68027
|
+
return void response.end(JSON.stringify({ browserTools, surfaces, version: VERSION2 }));
|
|
67925
68028
|
}
|
|
67926
68029
|
const endpoint = /^\/mcp\/(computer|browser)$/.exec(pathname)?.[1];
|
|
67927
68030
|
if (!endpoint) return void response.writeHead(404).end();
|
|
@@ -67980,10 +68083,15 @@ var UPGRADE_HEADERS = [
|
|
|
67980
68083
|
"sec-websocket-extensions",
|
|
67981
68084
|
"sec-websocket-protocol"
|
|
67982
68085
|
];
|
|
68086
|
+
var messageOf = (error62) => error62 instanceof Error ? error62.message : String(error62);
|
|
67983
68087
|
var nameOf = (id) => `agentcomputer-${id}`;
|
|
68088
|
+
var HOME = "/home/computer";
|
|
68089
|
+
var homeOf = (folder) => join8(folder, "home");
|
|
67984
68090
|
var sleep5 = (ms) => new Promise((done) => setTimeout(done, ms));
|
|
67985
68091
|
var ContainerRuntime = class {
|
|
67986
68092
|
#image;
|
|
68093
|
+
/** The id of that image, as it is now on this machine. */
|
|
68094
|
+
#imageId;
|
|
67987
68095
|
constructor(image) {
|
|
67988
68096
|
this.#image = image;
|
|
67989
68097
|
}
|
|
@@ -68007,12 +68115,14 @@ var ContainerRuntime = class {
|
|
|
68007
68115
|
}
|
|
68008
68116
|
async prepare() {
|
|
68009
68117
|
await this.#docker();
|
|
68118
|
+
const id = () => docker(["image", "inspect", "--format", "{{.Id}}", this.#image]);
|
|
68010
68119
|
try {
|
|
68011
|
-
await
|
|
68120
|
+
this.#imageId = await id();
|
|
68012
68121
|
} catch {
|
|
68013
68122
|
process.stdout.write(`Pulling ${this.#image}
|
|
68014
68123
|
`);
|
|
68015
68124
|
await docker(["pull", this.#image]);
|
|
68125
|
+
this.#imageId = await id();
|
|
68016
68126
|
}
|
|
68017
68127
|
}
|
|
68018
68128
|
async #inspect(name) {
|
|
@@ -68022,7 +68132,39 @@ var ContainerRuntime = class {
|
|
|
68022
68132
|
return void 0;
|
|
68023
68133
|
}
|
|
68024
68134
|
}
|
|
68025
|
-
|
|
68135
|
+
/**
|
|
68136
|
+
* Remove a container made from another image, for it to be made again. What it keeps is outside
|
|
68137
|
+
* it: its workspace, and its home. One made before its home was outside has its home copied out
|
|
68138
|
+
* first, and if that can't be done it is kept as it is, to try again at its next start.
|
|
68139
|
+
*/
|
|
68140
|
+
async #remake(name, made, folder, events) {
|
|
68141
|
+
if (!made.Mounts.some((mount) => mount.Destination === HOME)) {
|
|
68142
|
+
const home = homeOf(folder);
|
|
68143
|
+
const copying = `${home}.copying`;
|
|
68144
|
+
rmSync4(copying, { recursive: true, force: true });
|
|
68145
|
+
try {
|
|
68146
|
+
const owners = process.getuid?.() ? [] : ["--archive"];
|
|
68147
|
+
await docker(["cp", ...owners, `${name}:${HOME}`, copying]);
|
|
68148
|
+
rmSync4(home, { recursive: true, force: true });
|
|
68149
|
+
renameSync2(copying, home);
|
|
68150
|
+
} catch (error62) {
|
|
68151
|
+
rmSync4(copying, { recursive: true, force: true });
|
|
68152
|
+
events.log(
|
|
68153
|
+
`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.`
|
|
68154
|
+
);
|
|
68155
|
+
return;
|
|
68156
|
+
}
|
|
68157
|
+
events.log("Its home was copied out of its container, to keep.");
|
|
68158
|
+
}
|
|
68159
|
+
await docker(["rm", "--force", name]);
|
|
68160
|
+
events.log(
|
|
68161
|
+
`Its container was made from another image, so it is made again from ${this.#image}.`
|
|
68162
|
+
);
|
|
68163
|
+
}
|
|
68164
|
+
async #create(name, spec, token, folder) {
|
|
68165
|
+
const home = homeOf(folder);
|
|
68166
|
+
mkdirSync2(home, { recursive: true });
|
|
68167
|
+
chmodSync3(home, 1023);
|
|
68026
68168
|
const uid = process.getuid?.();
|
|
68027
68169
|
const told = {
|
|
68028
68170
|
[INNER_TOKEN_VARIABLE]: token,
|
|
@@ -68046,6 +68188,8 @@ var ContainerRuntime = class {
|
|
|
68046
68188
|
...Object.keys(told).flatMap((name2) => ["--env", name2]),
|
|
68047
68189
|
// Files it writes in the workspace belong to whoever runs the service.
|
|
68048
68190
|
...uid ? ["--user", `${uid}:${process.getgid?.()}`] : [],
|
|
68191
|
+
"--mount",
|
|
68192
|
+
`type=bind,source=${home},target=${HOME}`,
|
|
68049
68193
|
...spec.workspace ? [
|
|
68050
68194
|
"--mount",
|
|
68051
68195
|
`type=bind,source=${spec.workspace},target=/workspace${spec.access === "read" ? ",readonly" : ""}`
|
|
@@ -68073,7 +68217,10 @@ var ContainerRuntime = class {
|
|
|
68073
68217
|
async start(spec, folder, events) {
|
|
68074
68218
|
const name = nameOf(spec.id);
|
|
68075
68219
|
const token = this.#tokenIn(folder);
|
|
68076
|
-
|
|
68220
|
+
const made = await this.#inspect(name);
|
|
68221
|
+
if (made && this.#imageId && made.Image !== this.#imageId)
|
|
68222
|
+
await this.#remake(name, made, folder, events);
|
|
68223
|
+
if (!await this.#inspect(name)) await this.#create(name, spec, token, folder);
|
|
68077
68224
|
await docker(["start", name]);
|
|
68078
68225
|
const inspected = await this.#inspect(name);
|
|
68079
68226
|
const port = inspected?.NetworkSettings.Ports[`${INNER_PORT}/tcp`]?.[0]?.HostPort;
|
|
@@ -68110,6 +68257,7 @@ var ContainerRuntime = class {
|
|
|
68110
68257
|
}
|
|
68111
68258
|
return {
|
|
68112
68259
|
browserTools: ready.browserTools,
|
|
68260
|
+
version: ready.version,
|
|
68113
68261
|
/** As they are now: nothing of a container that has stopped can be opened. */
|
|
68114
68262
|
get surfaces() {
|
|
68115
68263
|
return ready.surfaces.map(
|
|
@@ -68202,33 +68350,6 @@ async function forward(url3, authorization, request, response) {
|
|
|
68202
68350
|
sendAnswer(upstream, response);
|
|
68203
68351
|
}
|
|
68204
68352
|
|
|
68205
|
-
// src/handlers.ts
|
|
68206
|
-
var VERSION2 = "0.0.15";
|
|
68207
|
-
function createHandlers(computers) {
|
|
68208
|
-
return {
|
|
68209
|
-
getInfo: async () => ({
|
|
68210
|
-
version: VERSION2,
|
|
68211
|
-
isolation: computers.isolation,
|
|
68212
|
-
isolationsAvailable: await computers.available()
|
|
68213
|
-
}),
|
|
68214
|
-
listComputers: async ({ state }) => computers.list(state),
|
|
68215
|
-
createComputer: (input2) => computers.create(input2),
|
|
68216
|
-
getComputer: async ({ computerID }) => computers.get(computerID),
|
|
68217
|
-
killComputer: async ({ computerID }) => {
|
|
68218
|
-
await computers.kill(computerID);
|
|
68219
|
-
return void 0;
|
|
68220
|
-
},
|
|
68221
|
-
setComputerTimeout: async ({ computerID, timeout }) => computers.setTimeout(computerID, timeout),
|
|
68222
|
-
stopComputer: ({ computerID }) => computers.stop(computerID),
|
|
68223
|
-
startComputer: ({ computerID, timeout }) => computers.start(computerID, timeout),
|
|
68224
|
-
getComputerLogs: async ({ computerID }) => ({ logs: computers.logs(computerID) }),
|
|
68225
|
-
createLiveSession: async ({ computerID, surface, mode }) => computers.liveSession(computerID, surface, mode ?? "watch"),
|
|
68226
|
-
openLiveSession: async () => {
|
|
68227
|
-
throw new ApiError(426, "Open it as a WebSocket.");
|
|
68228
|
-
}
|
|
68229
|
-
};
|
|
68230
|
-
}
|
|
68231
|
-
|
|
68232
68353
|
// src/running.ts
|
|
68233
68354
|
import { spawn as spawn4 } from "node:child_process";
|
|
68234
68355
|
import { closeSync, openSync, readFileSync as readFileSync4, statSync as statSync3, writeFileSync as writeFileSync2 } from "node:fs";
|
|
@@ -69101,7 +69222,7 @@ var Computer = class _Computer {
|
|
|
69101
69222
|
};
|
|
69102
69223
|
|
|
69103
69224
|
// src/service.ts
|
|
69104
|
-
import { readFileSync as readFileSync6, rmSync as
|
|
69225
|
+
import { readFileSync as readFileSync6, rmSync as rmSync6 } from "node:fs";
|
|
69105
69226
|
import {
|
|
69106
69227
|
createServer as createServer5,
|
|
69107
69228
|
STATUS_CODES as STATUS_CODES3
|
|
@@ -84921,7 +85042,7 @@ var NodeStreamableHTTPServerTransport2 = class {
|
|
|
84921
85042
|
|
|
84922
85043
|
// src/computers.ts
|
|
84923
85044
|
import { randomBytes as randomBytes5 } from "node:crypto";
|
|
84924
|
-
import { appendFileSync, existsSync as existsSync2, mkdirSync as
|
|
85045
|
+
import { appendFileSync, existsSync as existsSync2, mkdirSync as mkdirSync3, readFileSync as readFileSync5, rmSync as rmSync5, statSync as statSync4 } from "node:fs";
|
|
84925
85046
|
import { isAbsolute, join as join10, resolve as resolve4 } from "node:path";
|
|
84926
85047
|
var READ = ["about", "read", "list", "search"];
|
|
84927
85048
|
var FILES = [...READ, "edit"];
|
|
@@ -84934,8 +85055,8 @@ var MAX_LOG2 = 256 * 1024;
|
|
|
84934
85055
|
var MAX_TIMER_S = 2e6;
|
|
84935
85056
|
var TICKET_MS = 6e4;
|
|
84936
85057
|
var expiry = (seconds2) => new Date(Date.now() + seconds2 * 1e3).toISOString();
|
|
84937
|
-
var
|
|
84938
|
-
var refusalOf = (error62) => error62 instanceof ApiError ? error62 : new ApiError(500,
|
|
85058
|
+
var messageOf2 = (error62) => error62 instanceof Error ? error62.message : String(error62);
|
|
85059
|
+
var refusalOf = (error62) => error62 instanceof ApiError ? error62 : new ApiError(500, messageOf2(error62));
|
|
84939
85060
|
var Computers = class {
|
|
84940
85061
|
#data;
|
|
84941
85062
|
#base;
|
|
@@ -84969,7 +85090,7 @@ var Computers = class {
|
|
|
84969
85090
|
if (stored.endAt && Date.parse(stored.endAt) <= Date.now()) {
|
|
84970
85091
|
stored.state = "stopped";
|
|
84971
85092
|
await this.#runtimes[stored.isolation].stopLeftover(stored.id).catch(
|
|
84972
|
-
(error62) => this.#log(stored.id, `What it left running wasn't stopped: ${
|
|
85093
|
+
(error62) => this.#log(stored.id, `What it left running wasn't stopped: ${messageOf2(error62)}`)
|
|
84973
85094
|
);
|
|
84974
85095
|
continue;
|
|
84975
85096
|
}
|
|
@@ -84977,7 +85098,7 @@ var Computers = class {
|
|
|
84977
85098
|
const live = this.#live.get(stored.id);
|
|
84978
85099
|
if (live) live.failed = true;
|
|
84979
85100
|
this.#arm(stored);
|
|
84980
|
-
this.#log(stored.id, `It didn't start again: ${
|
|
85101
|
+
this.#log(stored.id, `It didn't start again: ${messageOf2(error62)}`);
|
|
84981
85102
|
});
|
|
84982
85103
|
}
|
|
84983
85104
|
this.#save();
|
|
@@ -85016,7 +85137,7 @@ var Computers = class {
|
|
|
85016
85137
|
};
|
|
85017
85138
|
if (!workspace && access !== "none") {
|
|
85018
85139
|
stored.workspace = join10(this.#folder(id), "workspace");
|
|
85019
|
-
|
|
85140
|
+
mkdirSync3(stored.workspace, { recursive: true, mode: 448 });
|
|
85020
85141
|
}
|
|
85021
85142
|
this.#stored.set(id, stored);
|
|
85022
85143
|
try {
|
|
@@ -85026,7 +85147,7 @@ var Computers = class {
|
|
|
85026
85147
|
this.#stored.delete(id);
|
|
85027
85148
|
await this.#runtimes[stored.isolation].remove(id).catch(() => {
|
|
85028
85149
|
});
|
|
85029
|
-
|
|
85150
|
+
rmSync5(this.#folder(id), { recursive: true, force: true });
|
|
85030
85151
|
throw refusalOf(error62);
|
|
85031
85152
|
}
|
|
85032
85153
|
this.#save();
|
|
@@ -85100,7 +85221,7 @@ var Computers = class {
|
|
|
85100
85221
|
}
|
|
85101
85222
|
this.#stored.delete(id);
|
|
85102
85223
|
this.#save();
|
|
85103
|
-
|
|
85224
|
+
rmSync5(this.#folder(id), { recursive: true, force: true });
|
|
85104
85225
|
}
|
|
85105
85226
|
logs(id) {
|
|
85106
85227
|
this.#found(id);
|
|
@@ -85201,6 +85322,7 @@ var Computers = class {
|
|
|
85201
85322
|
state: stored.state === "running" && down2 ? "failed" : stored.state,
|
|
85202
85323
|
access: stored.access,
|
|
85203
85324
|
isolation: stored.isolation,
|
|
85325
|
+
version: stored.version ?? null,
|
|
85204
85326
|
browser: stored.browser,
|
|
85205
85327
|
workspace: stored.workspace ?? null,
|
|
85206
85328
|
startedAt: stored.startedAt,
|
|
@@ -85234,6 +85356,9 @@ var Computers = class {
|
|
|
85234
85356
|
}
|
|
85235
85357
|
}
|
|
85236
85358
|
);
|
|
85359
|
+
const version3 = stored.isolation === "none" ? VERSION2 : live.running.version;
|
|
85360
|
+
if (version3) stored.version = version3;
|
|
85361
|
+
else delete stored.version;
|
|
85237
85362
|
this.#arm(stored);
|
|
85238
85363
|
}
|
|
85239
85364
|
#arm(stored) {
|
|
@@ -85271,7 +85396,7 @@ var OPENAPI = {
|
|
|
85271
85396
|
"openapi": "3.1.0",
|
|
85272
85397
|
"info": {
|
|
85273
85398
|
"title": "AgentComputer",
|
|
85274
|
-
"version": "0.0.
|
|
85399
|
+
"version": "0.0.17",
|
|
85275
85400
|
"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.",
|
|
85276
85401
|
"license": {
|
|
85277
85402
|
"name": "Apache-2.0",
|
|
@@ -85825,6 +85950,7 @@ var OPENAPI = {
|
|
|
85825
85950
|
"state",
|
|
85826
85951
|
"access",
|
|
85827
85952
|
"isolation",
|
|
85953
|
+
"version",
|
|
85828
85954
|
"browser",
|
|
85829
85955
|
"workspace",
|
|
85830
85956
|
"startedAt",
|
|
@@ -85849,6 +85975,13 @@ var OPENAPI = {
|
|
|
85849
85975
|
"$ref": "#/components/schemas/Isolation",
|
|
85850
85976
|
"description": "The isolation it was made in. It is never run in another."
|
|
85851
85977
|
},
|
|
85978
|
+
"version": {
|
|
85979
|
+
"type": [
|
|
85980
|
+
"string",
|
|
85981
|
+
"null"
|
|
85982
|
+
],
|
|
85983
|
+
"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."
|
|
85984
|
+
},
|
|
85852
85985
|
"browser": {
|
|
85853
85986
|
"type": "boolean"
|
|
85854
85987
|
},
|
|
@@ -87120,6 +87253,7 @@ var OPERATIONS = [
|
|
|
87120
87253
|
"state",
|
|
87121
87254
|
"access",
|
|
87122
87255
|
"isolation",
|
|
87256
|
+
"version",
|
|
87123
87257
|
"browser",
|
|
87124
87258
|
"workspace",
|
|
87125
87259
|
"startedAt",
|
|
@@ -87161,6 +87295,13 @@ var OPERATIONS = [
|
|
|
87161
87295
|
],
|
|
87162
87296
|
"description": "The isolation it was made in. It is never run in another."
|
|
87163
87297
|
},
|
|
87298
|
+
"version": {
|
|
87299
|
+
"type": [
|
|
87300
|
+
"string",
|
|
87301
|
+
"null"
|
|
87302
|
+
],
|
|
87303
|
+
"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."
|
|
87304
|
+
},
|
|
87164
87305
|
"browser": {
|
|
87165
87306
|
"type": "boolean"
|
|
87166
87307
|
},
|
|
@@ -87297,6 +87438,7 @@ var OPERATIONS = [
|
|
|
87297
87438
|
"state",
|
|
87298
87439
|
"access",
|
|
87299
87440
|
"isolation",
|
|
87441
|
+
"version",
|
|
87300
87442
|
"browser",
|
|
87301
87443
|
"workspace",
|
|
87302
87444
|
"startedAt",
|
|
@@ -87338,6 +87480,13 @@ var OPERATIONS = [
|
|
|
87338
87480
|
],
|
|
87339
87481
|
"description": "The isolation it was made in. It is never run in another."
|
|
87340
87482
|
},
|
|
87483
|
+
"version": {
|
|
87484
|
+
"type": [
|
|
87485
|
+
"string",
|
|
87486
|
+
"null"
|
|
87487
|
+
],
|
|
87488
|
+
"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."
|
|
87489
|
+
},
|
|
87341
87490
|
"browser": {
|
|
87342
87491
|
"type": "boolean"
|
|
87343
87492
|
},
|
|
@@ -87447,6 +87596,7 @@ var OPERATIONS = [
|
|
|
87447
87596
|
"state",
|
|
87448
87597
|
"access",
|
|
87449
87598
|
"isolation",
|
|
87599
|
+
"version",
|
|
87450
87600
|
"browser",
|
|
87451
87601
|
"workspace",
|
|
87452
87602
|
"startedAt",
|
|
@@ -87488,6 +87638,13 @@ var OPERATIONS = [
|
|
|
87488
87638
|
],
|
|
87489
87639
|
"description": "The isolation it was made in. It is never run in another."
|
|
87490
87640
|
},
|
|
87641
|
+
"version": {
|
|
87642
|
+
"type": [
|
|
87643
|
+
"string",
|
|
87644
|
+
"null"
|
|
87645
|
+
],
|
|
87646
|
+
"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."
|
|
87647
|
+
},
|
|
87491
87648
|
"browser": {
|
|
87492
87649
|
"type": "boolean"
|
|
87493
87650
|
},
|
|
@@ -87688,6 +87845,7 @@ var OPERATIONS = [
|
|
|
87688
87845
|
"state",
|
|
87689
87846
|
"access",
|
|
87690
87847
|
"isolation",
|
|
87848
|
+
"version",
|
|
87691
87849
|
"browser",
|
|
87692
87850
|
"workspace",
|
|
87693
87851
|
"startedAt",
|
|
@@ -87729,6 +87887,13 @@ var OPERATIONS = [
|
|
|
87729
87887
|
],
|
|
87730
87888
|
"description": "The isolation it was made in. It is never run in another."
|
|
87731
87889
|
},
|
|
87890
|
+
"version": {
|
|
87891
|
+
"type": [
|
|
87892
|
+
"string",
|
|
87893
|
+
"null"
|
|
87894
|
+
],
|
|
87895
|
+
"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."
|
|
87896
|
+
},
|
|
87732
87897
|
"browser": {
|
|
87733
87898
|
"type": "boolean"
|
|
87734
87899
|
},
|
|
@@ -87822,6 +87987,7 @@ var OPERATIONS = [
|
|
|
87822
87987
|
"state",
|
|
87823
87988
|
"access",
|
|
87824
87989
|
"isolation",
|
|
87990
|
+
"version",
|
|
87825
87991
|
"browser",
|
|
87826
87992
|
"workspace",
|
|
87827
87993
|
"startedAt",
|
|
@@ -87863,6 +88029,13 @@ var OPERATIONS = [
|
|
|
87863
88029
|
],
|
|
87864
88030
|
"description": "The isolation it was made in. It is never run in another."
|
|
87865
88031
|
},
|
|
88032
|
+
"version": {
|
|
88033
|
+
"type": [
|
|
88034
|
+
"string",
|
|
88035
|
+
"null"
|
|
88036
|
+
],
|
|
88037
|
+
"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."
|
|
88038
|
+
},
|
|
87866
88039
|
"browser": {
|
|
87867
88040
|
"type": "boolean"
|
|
87868
88041
|
},
|
|
@@ -87961,6 +88134,7 @@ var OPERATIONS = [
|
|
|
87961
88134
|
"state",
|
|
87962
88135
|
"access",
|
|
87963
88136
|
"isolation",
|
|
88137
|
+
"version",
|
|
87964
88138
|
"browser",
|
|
87965
88139
|
"workspace",
|
|
87966
88140
|
"startedAt",
|
|
@@ -88002,6 +88176,13 @@ var OPERATIONS = [
|
|
|
88002
88176
|
],
|
|
88003
88177
|
"description": "The isolation it was made in. It is never run in another."
|
|
88004
88178
|
},
|
|
88179
|
+
"version": {
|
|
88180
|
+
"type": [
|
|
88181
|
+
"string",
|
|
88182
|
+
"null"
|
|
88183
|
+
],
|
|
88184
|
+
"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."
|
|
88185
|
+
},
|
|
88005
88186
|
"browser": {
|
|
88006
88187
|
"type": "boolean"
|
|
88007
88188
|
},
|
|
@@ -88415,7 +88596,7 @@ ${JSON.stringify({ code: status, message })}
|
|
|
88415
88596
|
const scheme = options.tls ? "https" : "http";
|
|
88416
88597
|
base = options.publicUrl?.replace(/\/+$/, "") ?? `${scheme}://${bound.family === "IPv6" ? `[${bound.address}]` : bound.address}:${bound.port}`;
|
|
88417
88598
|
const record3 = recordOf(data);
|
|
88418
|
-
|
|
88599
|
+
rmSync6(record3, { force: true });
|
|
88419
88600
|
await computers.resume();
|
|
88420
88601
|
writePrivate(
|
|
88421
88602
|
record3,
|
|
@@ -88431,7 +88612,7 @@ ${JSON.stringify({ code: status, message })}
|
|
|
88431
88612
|
process.stderr.write(`${String(error62)}
|
|
88432
88613
|
`);
|
|
88433
88614
|
});
|
|
88434
|
-
|
|
88615
|
+
rmSync6(record3, { force: true });
|
|
88435
88616
|
process.exit(0);
|
|
88436
88617
|
};
|
|
88437
88618
|
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;
|