@agentorg/computer 0.0.14 → 0.0.16
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 +37 -19
- package/dist/THIRD_PARTY_LICENSES.txt +1 -1
- package/dist/agentcomputer.js +586 -215
- package/dist/sdk.js +2 -1
- package/dist/types/generated/schema.d.ts +17 -6
- package/dist/types/sdk.d.ts +6 -0
- package/package.json +2 -2
package/dist/agentcomputer.js
CHANGED
|
@@ -3751,7 +3751,6 @@ var require_websocket_server = __commonJS({
|
|
|
3751
3751
|
});
|
|
3752
3752
|
|
|
3753
3753
|
// src/main.ts
|
|
3754
|
-
import { readFileSync as readFileSync6 } from "node:fs";
|
|
3755
3754
|
import { homedir as homedir2 } from "node:os";
|
|
3756
3755
|
import { isAbsolute as isAbsolute2, join as join11, resolve as resolve5 } from "node:path";
|
|
3757
3756
|
import { parseArgs } from "node:util";
|
|
@@ -3805,44 +3804,73 @@ async function hostBrowser(options) {
|
|
|
3805
3804
|
}
|
|
3806
3805
|
|
|
3807
3806
|
// src/browser-mcp.ts
|
|
3807
|
+
import { mkdtempSync as mkdtempSync2, rmSync as rmSync2 } from "node:fs";
|
|
3808
3808
|
import { createRequire as createRequire2 } from "node:module";
|
|
3809
|
+
import { join as join2 } from "node:path";
|
|
3809
3810
|
var require2 = createRequire2(import.meta.url);
|
|
3810
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
|
+
}
|
|
3811
3824
|
async function serveBrowserMcp(options) {
|
|
3812
3825
|
const parts = mcpParts();
|
|
3813
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");
|
|
3814
3830
|
const config3 = await parts.resolveCLIConfigForMCP({
|
|
3815
3831
|
endpoint: options.endpoint,
|
|
3816
3832
|
outputDir: options.outputDir,
|
|
3817
3833
|
host: "127.0.0.1",
|
|
3818
3834
|
port: 0,
|
|
3835
|
+
// What the proxy sends, which is all that can reach the socket.
|
|
3836
|
+
allowedHosts: ["localhost"],
|
|
3819
3837
|
// The tool that closes the browser refuses, as it does for any browser its clients share.
|
|
3820
3838
|
sharedBrowserContext: true
|
|
3821
3839
|
});
|
|
3822
3840
|
const tools = parts.filteredTools(config3);
|
|
3823
|
-
|
|
3824
|
-
|
|
3825
|
-
|
|
3826
|
-
|
|
3827
|
-
|
|
3828
|
-
|
|
3829
|
-
|
|
3830
|
-
|
|
3831
|
-
|
|
3832
|
-
|
|
3833
|
-
|
|
3834
|
-
|
|
3835
|
-
|
|
3836
|
-
|
|
3837
|
-
|
|
3838
|
-
|
|
3839
|
-
|
|
3840
|
-
|
|
3841
|
-
|
|
3842
|
-
|
|
3843
|
-
|
|
3844
|
-
|
|
3845
|
-
|
|
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
|
+
`);
|
|
3846
3874
|
}
|
|
3847
3875
|
|
|
3848
3876
|
// src/container.ts
|
|
@@ -3857,7 +3885,9 @@ import { promisify as promisify2 } from "node:util";
|
|
|
3857
3885
|
|
|
3858
3886
|
// src/browser.ts
|
|
3859
3887
|
import { spawn } from "node:child_process";
|
|
3860
|
-
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";
|
|
3861
3891
|
import { createInterface } from "node:readline";
|
|
3862
3892
|
import { pipeline, Readable } from "node:stream";
|
|
3863
3893
|
import { fileURLToPath } from "node:url";
|
|
@@ -4829,10 +4859,10 @@ function $constructor(name, initializer4, proto, params) {
|
|
|
4829
4859
|
if (initialized) {
|
|
4830
4860
|
const own3 = Object.getPrototypeOf(inst);
|
|
4831
4861
|
const ctorProto = inst._zod.constr.prototype;
|
|
4832
|
-
let
|
|
4833
|
-
while (
|
|
4834
|
-
|
|
4835
|
-
const target =
|
|
4862
|
+
let up2 = own3;
|
|
4863
|
+
while (up2 && up2 !== ctorProto)
|
|
4864
|
+
up2 = Object.getPrototypeOf(up2);
|
|
4865
|
+
const target = up2 ?? own3;
|
|
4836
4866
|
if (!initialized.has(target)) {
|
|
4837
4867
|
initialized.add(target);
|
|
4838
4868
|
members(target, protoMembers);
|
|
@@ -18888,14 +18918,14 @@ var require_validate = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
|
18888
18918
|
} else {
|
|
18889
18919
|
const matches = RELATIVE_JSON_POINTER.exec($data);
|
|
18890
18920
|
if (!matches) throw new Error(`Invalid JSON-pointer: ${$data}`);
|
|
18891
|
-
const
|
|
18921
|
+
const up2 = +matches[1];
|
|
18892
18922
|
jsonPointer = matches[2];
|
|
18893
18923
|
if (jsonPointer === "#") {
|
|
18894
|
-
if (
|
|
18895
|
-
return dataPathArr[dataLevel -
|
|
18924
|
+
if (up2 >= dataLevel) throw new Error(errorMsg("property/index", up2));
|
|
18925
|
+
return dataPathArr[dataLevel - up2];
|
|
18896
18926
|
}
|
|
18897
|
-
if (
|
|
18898
|
-
data = dataNames[dataLevel -
|
|
18927
|
+
if (up2 > dataLevel) throw new Error(errorMsg("data", up2));
|
|
18928
|
+
data = dataNames[dataLevel - up2];
|
|
18899
18929
|
if (!jsonPointer) return data;
|
|
18900
18930
|
}
|
|
18901
18931
|
let expr = data;
|
|
@@ -18905,8 +18935,8 @@ var require_validate = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
|
18905
18935
|
expr = (0, codegen_1._)`${expr} && ${data}`;
|
|
18906
18936
|
}
|
|
18907
18937
|
return expr;
|
|
18908
|
-
function errorMsg(pointerType,
|
|
18909
|
-
return `Cannot access ${pointerType} ${
|
|
18938
|
+
function errorMsg(pointerType, up2) {
|
|
18939
|
+
return `Cannot access ${pointerType} ${up2} levels up, current level is ${dataLevel}`;
|
|
18910
18940
|
}
|
|
18911
18941
|
}
|
|
18912
18942
|
exports.getData = getData;
|
|
@@ -27958,7 +27988,10 @@ var StreamableHTTPClientTransport = class {
|
|
|
27958
27988
|
|
|
27959
27989
|
// src/browser.ts
|
|
27960
27990
|
var RUNS_CODE = "browser_run_code_unsafe";
|
|
27961
|
-
var
|
|
27991
|
+
var SCREENSHOT = "browser_take_screenshot";
|
|
27992
|
+
var MIB = 1024 * 1024;
|
|
27993
|
+
var MAX_BODY = 8 * MIB;
|
|
27994
|
+
var MAX_PICTURE = 3 * MIB;
|
|
27962
27995
|
var START_MS = 3e4;
|
|
27963
27996
|
var STOP_MS = 2e3;
|
|
27964
27997
|
var ONE_REQUEST = "A body is one JSON-RPC request, as a JSON object: send each request in its own POST.";
|
|
@@ -27980,16 +28013,64 @@ function sendAnswer(upstream, response) {
|
|
|
27980
28013
|
pipeline(Readable.fromWeb(upstream.body), response, () => {
|
|
27981
28014
|
});
|
|
27982
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";
|
|
27983
28052
|
var self = fileURLToPath(import.meta.url);
|
|
27984
|
-
function
|
|
27985
|
-
const
|
|
28053
|
+
function changed(text3, change) {
|
|
28054
|
+
const one = (json4) => {
|
|
27986
28055
|
const message = JSON.parse(json4);
|
|
27987
|
-
|
|
27988
|
-
message.result.tools = message.result.tools.filter((tool) => !hidden.has(tool.name));
|
|
28056
|
+
change(message);
|
|
27989
28057
|
return JSON.stringify(message);
|
|
27990
28058
|
};
|
|
27991
|
-
if (text3.trimStart().startsWith("{")) return
|
|
27992
|
-
return text3.replace(/^data: (\{
|
|
28059
|
+
if (text3.trimStart().startsWith("{")) return one(text3);
|
|
28060
|
+
return text3.replace(/^data: (\{[^\n\r]*\})$/gm, (_, json4) => `data: ${one(json4)}`);
|
|
28061
|
+
}
|
|
28062
|
+
var withoutHidden = (hidden) => (message) => {
|
|
28063
|
+
if (message.result?.tools)
|
|
28064
|
+
message.result.tools = message.result.tools.filter((tool) => !hidden.has(tool.name));
|
|
28065
|
+
};
|
|
28066
|
+
function withoutLarge(message) {
|
|
28067
|
+
const sizes = (message.result?.content ?? []).map(
|
|
28068
|
+
(block) => block.type === "image" ? Buffer.byteLength(block.data ?? "", "base64") : 0
|
|
28069
|
+
);
|
|
28070
|
+
const size = Math.max(0, ...sizes);
|
|
28071
|
+
if (size <= MAX_PICTURE) return;
|
|
28072
|
+
const text3 = `The screenshot is ${(Math.ceil(size / MIB * 10) / 10).toFixed(1)} MiB, and the most a screenshot may be is ${MAX_PICTURE / MIB} MiB. Take it again without fullPage for what is on screen, or with element and target for one part of the page.`;
|
|
28073
|
+
message.result = { isError: true, content: [{ type: "text", text: text3 }] };
|
|
27993
28074
|
}
|
|
27994
28075
|
async function end(child) {
|
|
27995
28076
|
if (child.exitCode !== null || child.signalCode !== null) return;
|
|
@@ -28002,7 +28083,10 @@ async function end(child) {
|
|
|
28002
28083
|
var Browser = class {
|
|
28003
28084
|
#host;
|
|
28004
28085
|
#mcp;
|
|
28005
|
-
|
|
28086
|
+
/** Where its server answers, by way of its Unix socket. */
|
|
28087
|
+
#served;
|
|
28088
|
+
/** The folder its server made for its socket. */
|
|
28089
|
+
#socketFolder;
|
|
28006
28090
|
/** The bound browser's socket, for the live viewer. */
|
|
28007
28091
|
endpoint;
|
|
28008
28092
|
#hidden;
|
|
@@ -28024,7 +28108,7 @@ var Browser = class {
|
|
|
28024
28108
|
self,
|
|
28025
28109
|
"browser-host",
|
|
28026
28110
|
"--profile",
|
|
28027
|
-
|
|
28111
|
+
join3(folder, "profile"),
|
|
28028
28112
|
...config3.path ? ["--executable", config3.path] : ["--channel", config3.browser ?? "chrome"],
|
|
28029
28113
|
...config3.sandbox === false ? ["--no-sandbox"] : []
|
|
28030
28114
|
],
|
|
@@ -28047,12 +28131,12 @@ var Browser = class {
|
|
|
28047
28131
|
});
|
|
28048
28132
|
this.endpoint.catch(() => {
|
|
28049
28133
|
});
|
|
28050
|
-
this.#
|
|
28134
|
+
this.#served = this.endpoint.then(
|
|
28051
28135
|
(endpoint) => new Promise((done, failed) => {
|
|
28052
28136
|
if (this.#stopping) return failed(new Error("It stopped."));
|
|
28053
28137
|
const mcp = spawn(
|
|
28054
28138
|
process.execPath,
|
|
28055
|
-
[self, "browser-mcp", "--endpoint", endpoint, "--output",
|
|
28139
|
+
[self, "browser-mcp", "--endpoint", endpoint, "--output", join3(folder, "output")],
|
|
28056
28140
|
// Its own limit on which files it touches is the folder it starts in.
|
|
28057
28141
|
{ cwd, stdio: ["ignore", "pipe", "pipe"] }
|
|
28058
28142
|
);
|
|
@@ -28063,34 +28147,39 @@ var Browser = class {
|
|
|
28063
28147
|
(code) => failed(new Error(`The browser's server exited with ${code}.`))
|
|
28064
28148
|
);
|
|
28065
28149
|
mcp.once("exit", ended("The browser's server"));
|
|
28066
|
-
|
|
28067
|
-
|
|
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 {
|
|
28068
28157
|
log(line);
|
|
28069
|
-
|
|
28070
|
-
|
|
28071
|
-
|
|
28158
|
+
}
|
|
28159
|
+
});
|
|
28160
|
+
createInterface({ input: mcp.stderr }).on("line", log);
|
|
28072
28161
|
})
|
|
28073
28162
|
);
|
|
28074
|
-
this.#
|
|
28163
|
+
this.#served.catch(() => {
|
|
28075
28164
|
});
|
|
28076
28165
|
}
|
|
28077
28166
|
/** Wait until it answers, and read its tools. */
|
|
28078
28167
|
async ready() {
|
|
28079
|
-
const
|
|
28080
|
-
this.#
|
|
28168
|
+
const served = await Promise.race([
|
|
28169
|
+
this.#served,
|
|
28081
28170
|
new Promise(
|
|
28082
28171
|
(_, failed) => setTimeout(() => failed(new Error("The browser's server didn't start.")), START_MS)
|
|
28083
28172
|
)
|
|
28084
28173
|
]);
|
|
28085
28174
|
const client = new Client({ name: "agentcomputer", version: "0" });
|
|
28086
|
-
await client.connect(new StreamableHTTPClientTransport(new URL(
|
|
28175
|
+
await client.connect(new StreamableHTTPClientTransport(new URL(MCP), { fetch: served }));
|
|
28087
28176
|
const { tools } = await client.listTools();
|
|
28088
28177
|
await client.close();
|
|
28089
28178
|
this.tools = tools.map((tool) => tool.name).filter((name) => !this.#hidden.has(name));
|
|
28090
28179
|
}
|
|
28091
28180
|
/** Answer one MCP request by passing it on, enforcing what this computer may not do. */
|
|
28092
28181
|
async handle(request, response) {
|
|
28093
|
-
const
|
|
28182
|
+
const served = await this.#served;
|
|
28094
28183
|
const chunks = [];
|
|
28095
28184
|
let size = 0;
|
|
28096
28185
|
for await (const chunk of request) {
|
|
@@ -28134,7 +28223,7 @@ var Browser = class {
|
|
|
28134
28223
|
const session = headers["mcp-session-id"] ?? "";
|
|
28135
28224
|
if (request.method === "DELETE") this.gate?.sessionEnded(session);
|
|
28136
28225
|
if (tool && this.gate?.takeAnsweredChooser(session) && tool !== "browser_file_upload")
|
|
28137
|
-
await
|
|
28226
|
+
await served(MCP, {
|
|
28138
28227
|
method: "POST",
|
|
28139
28228
|
headers,
|
|
28140
28229
|
body: JSON.stringify({
|
|
@@ -28147,20 +28236,22 @@ var Browser = class {
|
|
|
28147
28236
|
});
|
|
28148
28237
|
if (tool === "browser_file_upload") this.gate?.agentAnsweredChooser();
|
|
28149
28238
|
if (message) headers["content-type"] = "application/json";
|
|
28150
|
-
const upstream = await
|
|
28239
|
+
const upstream = await served(MCP, {
|
|
28151
28240
|
method: request.method,
|
|
28152
28241
|
headers,
|
|
28153
28242
|
...message ? { body: JSON.stringify(message) } : {}
|
|
28154
28243
|
});
|
|
28155
|
-
|
|
28156
|
-
|
|
28157
|
-
|
|
28158
|
-
|
|
28159
|
-
sendAnswer(upstream, response);
|
|
28244
|
+
let change;
|
|
28245
|
+
if (message?.method === "tools/list" && this.#hidden.size > 0)
|
|
28246
|
+
change = withoutHidden(this.#hidden);
|
|
28247
|
+
else if (tool === SCREENSHOT) change = withoutLarge;
|
|
28248
|
+
if (!change) return sendAnswer(upstream, response);
|
|
28249
|
+
sendAnswer(new Response(changed(await upstream.text(), change), upstream), response);
|
|
28160
28250
|
}
|
|
28161
28251
|
async stop() {
|
|
28162
28252
|
this.#stopping = true;
|
|
28163
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 });
|
|
28164
28255
|
}
|
|
28165
28256
|
};
|
|
28166
28257
|
|
|
@@ -28173,7 +28264,7 @@ import { join as join7 } from "node:path";
|
|
|
28173
28264
|
import { chmodSync as chmodSync2, existsSync, statSync as statSync2 } from "node:fs";
|
|
28174
28265
|
import { createRequire as createRequire3 } from "node:module";
|
|
28175
28266
|
import { hostname as hostname3 } from "node:os";
|
|
28176
|
-
import { basename as basename2, dirname as
|
|
28267
|
+
import { basename as basename2, dirname as dirname4, join as join6 } from "node:path";
|
|
28177
28268
|
|
|
28178
28269
|
// node_modules/@agentorg/exec/dist/lib.js
|
|
28179
28270
|
import { realpathSync } from "node:fs";
|
|
@@ -28183,7 +28274,7 @@ import { Http2ServerRequest, constants } from "node:http2";
|
|
|
28183
28274
|
import { Readable as Readable2 } from "node:stream";
|
|
28184
28275
|
import { randomBytes as randomBytes3 } from "node:crypto";
|
|
28185
28276
|
import { lstat as lstat2, open as open3, readdir as readdir2, rename as rename2, rm as rm2, stat as stat2 } from "node:fs/promises";
|
|
28186
|
-
import { dirname as dirname2, join as
|
|
28277
|
+
import { dirname as dirname2, join as join32 } from "node:path";
|
|
28187
28278
|
import { randomBytes as randomBytes2 } from "node:crypto";
|
|
28188
28279
|
import {
|
|
28189
28280
|
chmod,
|
|
@@ -28197,7 +28288,7 @@ import {
|
|
|
28197
28288
|
stat,
|
|
28198
28289
|
writeFile
|
|
28199
28290
|
} from "node:fs/promises";
|
|
28200
|
-
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";
|
|
28201
28292
|
import { spawn as spawn2 } from "node:child_process";
|
|
28202
28293
|
import { randomBytes } from "node:crypto";
|
|
28203
28294
|
import { StringDecoder } from "node:string_decoder";
|
|
@@ -30622,10 +30713,10 @@ function $constructor2(name, initializer32, proto, params) {
|
|
|
30622
30713
|
if (initialized) {
|
|
30623
30714
|
const own22 = Object.getPrototypeOf(inst);
|
|
30624
30715
|
const ctorProto = inst._zod.constr.prototype;
|
|
30625
|
-
let
|
|
30626
|
-
while (
|
|
30627
|
-
|
|
30628
|
-
const target =
|
|
30716
|
+
let up2 = own22;
|
|
30717
|
+
while (up2 && up2 !== ctorProto)
|
|
30718
|
+
up2 = Object.getPrototypeOf(up2);
|
|
30719
|
+
const target = up2 ?? own22;
|
|
30629
30720
|
if (!initialized.has(target)) {
|
|
30630
30721
|
initialized.add(target);
|
|
30631
30722
|
members2(target, protoMembers);
|
|
@@ -48482,21 +48573,21 @@ function visit(schema, fnOrHandlers) {
|
|
|
48482
48573
|
case "object": {
|
|
48483
48574
|
const oldShape = def.shape;
|
|
48484
48575
|
const keys = Object.keys(oldShape);
|
|
48485
|
-
let
|
|
48576
|
+
let changed22 = false;
|
|
48486
48577
|
const newShape = {};
|
|
48487
48578
|
for (const k of keys) {
|
|
48488
48579
|
const mapped = run3(oldShape[k]);
|
|
48489
48580
|
if (mapped !== oldShape[k])
|
|
48490
|
-
|
|
48581
|
+
changed22 = true;
|
|
48491
48582
|
newShape[k] = mapped;
|
|
48492
48583
|
}
|
|
48493
48584
|
let newCatchall = def.catchall;
|
|
48494
48585
|
if (def.catchall) {
|
|
48495
48586
|
newCatchall = run3(def.catchall);
|
|
48496
48587
|
if (newCatchall !== def.catchall)
|
|
48497
|
-
|
|
48588
|
+
changed22 = true;
|
|
48498
48589
|
}
|
|
48499
|
-
return
|
|
48590
|
+
return changed22 ? clone2(s, { ...def, shape: newShape, catchall: newCatchall }) : s;
|
|
48500
48591
|
}
|
|
48501
48592
|
case "array": {
|
|
48502
48593
|
const mapped = run3(def.element);
|
|
@@ -48504,21 +48595,21 @@ function visit(schema, fnOrHandlers) {
|
|
|
48504
48595
|
}
|
|
48505
48596
|
case "tuple": {
|
|
48506
48597
|
const oldItems = def.items;
|
|
48507
|
-
let
|
|
48598
|
+
let changed22 = false;
|
|
48508
48599
|
const newItems = [];
|
|
48509
48600
|
for (const item of oldItems) {
|
|
48510
48601
|
const mapped = run3(item);
|
|
48511
48602
|
if (mapped !== item)
|
|
48512
|
-
|
|
48603
|
+
changed22 = true;
|
|
48513
48604
|
newItems.push(mapped);
|
|
48514
48605
|
}
|
|
48515
48606
|
let newRest = def.rest;
|
|
48516
48607
|
if (def.rest) {
|
|
48517
48608
|
newRest = run3(def.rest);
|
|
48518
48609
|
if (newRest !== def.rest)
|
|
48519
|
-
|
|
48610
|
+
changed22 = true;
|
|
48520
48611
|
}
|
|
48521
|
-
return
|
|
48612
|
+
return changed22 ? clone2(s, { ...def, items: newItems, rest: newRest }) : s;
|
|
48522
48613
|
}
|
|
48523
48614
|
case "record":
|
|
48524
48615
|
case "map": {
|
|
@@ -48532,15 +48623,15 @@ function visit(schema, fnOrHandlers) {
|
|
|
48532
48623
|
}
|
|
48533
48624
|
case "union": {
|
|
48534
48625
|
const oldOptions = def.options;
|
|
48535
|
-
let
|
|
48626
|
+
let changed22 = false;
|
|
48536
48627
|
const newOptions = [];
|
|
48537
48628
|
for (const opt of oldOptions) {
|
|
48538
48629
|
const mapped = run3(opt);
|
|
48539
48630
|
if (mapped !== opt)
|
|
48540
|
-
|
|
48631
|
+
changed22 = true;
|
|
48541
48632
|
newOptions.push(mapped);
|
|
48542
48633
|
}
|
|
48543
|
-
return
|
|
48634
|
+
return changed22 ? clone2(s, { ...def, options: newOptions }) : s;
|
|
48544
48635
|
}
|
|
48545
48636
|
case "intersection": {
|
|
48546
48637
|
const newLeft = run3(def.left);
|
|
@@ -56621,14 +56712,14 @@ var require_validate2 = /* @__PURE__ */ __commonJSMin2(((exports) => {
|
|
|
56621
56712
|
} else {
|
|
56622
56713
|
const matches = RELATIVE_JSON_POINTER.exec($data);
|
|
56623
56714
|
if (!matches) throw new Error(`Invalid JSON-pointer: ${$data}`);
|
|
56624
|
-
const
|
|
56715
|
+
const up2 = +matches[1];
|
|
56625
56716
|
jsonPointer = matches[2];
|
|
56626
56717
|
if (jsonPointer === "#") {
|
|
56627
|
-
if (
|
|
56628
|
-
return dataPathArr[dataLevel -
|
|
56718
|
+
if (up2 >= dataLevel) throw new Error(errorMsg("property/index", up2));
|
|
56719
|
+
return dataPathArr[dataLevel - up2];
|
|
56629
56720
|
}
|
|
56630
|
-
if (
|
|
56631
|
-
data = dataNames[dataLevel -
|
|
56721
|
+
if (up2 > dataLevel) throw new Error(errorMsg("data", up2));
|
|
56722
|
+
data = dataNames[dataLevel - up2];
|
|
56632
56723
|
if (!jsonPointer) return data;
|
|
56633
56724
|
}
|
|
56634
56725
|
let expr = data;
|
|
@@ -56638,8 +56729,8 @@ var require_validate2 = /* @__PURE__ */ __commonJSMin2(((exports) => {
|
|
|
56638
56729
|
expr = (0, codegen_1._)`${expr} && ${data}`;
|
|
56639
56730
|
}
|
|
56640
56731
|
return expr;
|
|
56641
|
-
function errorMsg(pointerType,
|
|
56642
|
-
return `Cannot access ${pointerType} ${
|
|
56732
|
+
function errorMsg(pointerType, up2) {
|
|
56733
|
+
return `Cannot access ${pointerType} ${up2} levels up, current level is ${dataLevel}`;
|
|
56643
56734
|
}
|
|
56644
56735
|
}
|
|
56645
56736
|
exports.getData = getData;
|
|
@@ -65904,6 +65995,7 @@ var Sessions = class {
|
|
|
65904
65995
|
this.#room();
|
|
65905
65996
|
const started = Date.now();
|
|
65906
65997
|
const session = this.#start(input2.cmd, input2.workdir ?? this.#root);
|
|
65998
|
+
if (input2.group) session.group = input2.group;
|
|
65907
65999
|
const wait = clamp(input2.yield_time_ms ?? DEFAULT_YIELD_MS, MIN_YIELD_MS, MAX_YIELD_MS);
|
|
65908
66000
|
return this.#after(session, wait, started, input2.max_output_tokens);
|
|
65909
66001
|
}
|
|
@@ -65988,26 +66080,37 @@ var Sessions = class {
|
|
|
65988
66080
|
if (!session) throw new ExecRefused(`No terminal ${id.slice(0, 64)} is here.`);
|
|
65989
66081
|
await this.#end([session], "SIGINT");
|
|
65990
66082
|
}
|
|
66083
|
+
/**
|
|
66084
|
+
* End the terminals still running of one group, as `stop` ends one, and say how many. A shell a
|
|
66085
|
+
* person opened is in no group.
|
|
66086
|
+
*/
|
|
66087
|
+
async stopGroup(group) {
|
|
66088
|
+
const running2 = [...this.#sessions.values()].filter(
|
|
66089
|
+
(session) => session.group === group && session.exitCode === void 0
|
|
66090
|
+
);
|
|
66091
|
+
await this.#end(running2, "SIGINT");
|
|
66092
|
+
return running2.length;
|
|
66093
|
+
}
|
|
65991
66094
|
/** Stop every terminal: a polite signal, then a certain one. */
|
|
65992
66095
|
async stopAll() {
|
|
65993
66096
|
await this.#end([...this.#sessions.values()], "SIGTERM");
|
|
65994
66097
|
}
|
|
65995
66098
|
async #end(sessions, polite) {
|
|
65996
|
-
const
|
|
65997
|
-
for (const session of
|
|
66099
|
+
const running2 = sessions.filter((s) => s.exitCode === void 0);
|
|
66100
|
+
for (const session of running2) session.signal(session.tty ? "SIGHUP" : polite);
|
|
65998
66101
|
let timer;
|
|
65999
66102
|
await Promise.race([
|
|
66000
|
-
Promise.all(
|
|
66103
|
+
Promise.all(running2.map((s) => s.ended)),
|
|
66001
66104
|
new Promise((done) => {
|
|
66002
66105
|
timer = setTimeout(done, KILL_AFTER_MS);
|
|
66003
66106
|
})
|
|
66004
66107
|
]);
|
|
66005
66108
|
clearTimeout(timer);
|
|
66006
|
-
for (const session of
|
|
66109
|
+
for (const session of running2) if (session.exitCode === void 0) session.signal("SIGKILL");
|
|
66007
66110
|
}
|
|
66008
66111
|
#room() {
|
|
66009
|
-
const
|
|
66010
|
-
if (
|
|
66112
|
+
const running2 = [...this.#sessions.values()].filter((s) => s.exitCode === void 0);
|
|
66113
|
+
if (running2.length >= MAX_RUNNING)
|
|
66011
66114
|
throw new ExecRefused(`${MAX_RUNNING} commands are running. Wait for one, or stop one.`);
|
|
66012
66115
|
}
|
|
66013
66116
|
#running(id) {
|
|
@@ -66218,7 +66321,7 @@ async function inside(root, path) {
|
|
|
66218
66321
|
() => false
|
|
66219
66322
|
))
|
|
66220
66323
|
throw refused2();
|
|
66221
|
-
const above =
|
|
66324
|
+
const above = dirname3(at);
|
|
66222
66325
|
if (above === at) throw refused2();
|
|
66223
66326
|
return join4(await real(above), basename(at));
|
|
66224
66327
|
}
|
|
@@ -66306,7 +66409,7 @@ ${cut(shown.join("\n"), MAX_READ_CHARS)}`;
|
|
|
66306
66409
|
after = before.split(input2.old_text).join(input2.new_text);
|
|
66307
66410
|
}
|
|
66308
66411
|
if (after === before) throw new ExecRefused("That edit changes nothing.");
|
|
66309
|
-
const temporary = join4(
|
|
66412
|
+
const temporary = join4(dirname3(path), `.agentexec-${randomBytes2(6).toString("hex")}`);
|
|
66310
66413
|
try {
|
|
66311
66414
|
await writeFile(temporary, after, found ? { mode: found.mode } : {});
|
|
66312
66415
|
if (found) await chmod(temporary, found.mode & 511);
|
|
@@ -66379,7 +66482,7 @@ function searched(job) {
|
|
|
66379
66482
|
const more = out.length > limit;
|
|
66380
66483
|
return (more ? [...out.slice(0, limit), `\u2026 more than ${limit} matches`] : out).join("\n");
|
|
66381
66484
|
}
|
|
66382
|
-
function
|
|
66485
|
+
function changed2(job) {
|
|
66383
66486
|
const changes = [];
|
|
66384
66487
|
let more = false;
|
|
66385
66488
|
const newest = () => {
|
|
@@ -66432,7 +66535,7 @@ function changedSince(root, since) {
|
|
|
66432
66535
|
}
|
|
66433
66536
|
if (!isMainThread && workerData?.agentexecSearch) {
|
|
66434
66537
|
const job = workerData;
|
|
66435
|
-
parentPort?.postMessage("since" in job ?
|
|
66538
|
+
parentPort?.postMessage("since" in job ? changed2(job) : searched(job));
|
|
66436
66539
|
}
|
|
66437
66540
|
var MAX_READ_BYTES = 1024 * 1024;
|
|
66438
66541
|
function createHostFiles(root, level) {
|
|
@@ -66445,7 +66548,7 @@ function createHostFiles(root, level) {
|
|
|
66445
66548
|
found.sort(foldersFirst);
|
|
66446
66549
|
const entries = await Promise.all(
|
|
66447
66550
|
found.slice(0, MAX_ENTRIES).map(async (entry) => {
|
|
66448
|
-
const own22 = await lstat2(
|
|
66551
|
+
const own22 = await lstat2(join32(folder, entry.name)).catch(() => void 0);
|
|
66449
66552
|
return own22 && {
|
|
66450
66553
|
name: entry.name,
|
|
66451
66554
|
kind: entry.isDirectory() ? "folder" : entry.isSymbolicLink() ? "link" : "file",
|
|
@@ -66492,7 +66595,7 @@ function createHostFiles(root, level) {
|
|
|
66492
66595
|
});
|
|
66493
66596
|
if (before?.isDirectory()) throw new ExecRefused(`${path} is a folder.`);
|
|
66494
66597
|
await stat2(dirname2(target));
|
|
66495
|
-
const temporary =
|
|
66598
|
+
const temporary = join32(dirname2(target), `.agentexec-${randomBytes3(6).toString("hex")}`);
|
|
66496
66599
|
const file2 = await open3(temporary, "wx");
|
|
66497
66600
|
const discard = async () => {
|
|
66498
66601
|
await file2.close().catch(() => {
|
|
@@ -66585,6 +66688,14 @@ var Answered = {
|
|
|
66585
66688
|
session_id: external_exports.string().nullable().describe("For write_stdin, while it is still running."),
|
|
66586
66689
|
text: external_exports.string().describe("What it wrote since the last call, cut in the middle if long.")
|
|
66587
66690
|
};
|
|
66691
|
+
var GROUP = "agentexec/group";
|
|
66692
|
+
var STOP_GROUP = "agentexec/group/stop";
|
|
66693
|
+
var Group = external_exports.string().min(1).max(200);
|
|
66694
|
+
var groupOf = (meta3) => {
|
|
66695
|
+
const word = Group.optional().safeParse(meta3?.[GROUP]);
|
|
66696
|
+
if (!word.success) throw new ExecRefused(`${GROUP} in _meta is a string of 1 to 200 characters.`);
|
|
66697
|
+
return word.data;
|
|
66698
|
+
};
|
|
66588
66699
|
var YieldTime = external_exports.number().int().positive().optional();
|
|
66589
66700
|
var MaxTokens = external_exports.number().int().positive().optional();
|
|
66590
66701
|
var READ_ONLY = { readOnlyHint: true };
|
|
@@ -66613,6 +66724,11 @@ function createServer2(root, sessions, version22, level, extraTools = []) {
|
|
|
66613
66724
|
}
|
|
66614
66725
|
);
|
|
66615
66726
|
if (level === "terminal") {
|
|
66727
|
+
server.server.setRequestHandler(
|
|
66728
|
+
STOP_GROUP,
|
|
66729
|
+
{ params: external_exports.object({ group: Group }), result: external_exports.object({ stopped: external_exports.number() }) },
|
|
66730
|
+
async ({ group }) => ({ stopped: await sessions.stopGroup(group) })
|
|
66731
|
+
);
|
|
66616
66732
|
server.registerTool(
|
|
66617
66733
|
"exec_command",
|
|
66618
66734
|
{
|
|
@@ -66626,7 +66742,7 @@ function createServer2(root, sessions, version22, level, extraTools = []) {
|
|
|
66626
66742
|
outputSchema: Answered,
|
|
66627
66743
|
annotations: { destructiveHint: true }
|
|
66628
66744
|
},
|
|
66629
|
-
(input2) => answering(() => sessions.exec(input2))
|
|
66745
|
+
(input2, ctx) => answering(async () => sessions.exec({ ...input2, group: groupOf(ctx.mcpReq._meta) }))
|
|
66630
66746
|
);
|
|
66631
66747
|
server.registerTool(
|
|
66632
66748
|
"write_stdin",
|
|
@@ -66729,7 +66845,7 @@ function createServer2(root, sessions, version22, level, extraTools = []) {
|
|
|
66729
66845
|
);
|
|
66730
66846
|
return server;
|
|
66731
66847
|
}
|
|
66732
|
-
var VERSION = "0.0.
|
|
66848
|
+
var VERSION = "0.0.8";
|
|
66733
66849
|
function createAgentExec(options) {
|
|
66734
66850
|
const root = realpathSync(resolve3(options.workspace));
|
|
66735
66851
|
const level = options.level ?? "read";
|
|
@@ -67034,8 +67150,8 @@ var BrowserLive = class {
|
|
|
67034
67150
|
}
|
|
67035
67151
|
/** The cast of a page, started if no socket was shown it yet. */
|
|
67036
67152
|
#cast(page) {
|
|
67037
|
-
const
|
|
67038
|
-
if (
|
|
67153
|
+
const running2 = this.#casts.get(page);
|
|
67154
|
+
if (running2) return running2;
|
|
67039
67155
|
const onFrame = (frame) => {
|
|
67040
67156
|
const watching = this.#shownTo(page);
|
|
67041
67157
|
if (frame.width !== cast.size.width || frame.height !== cast.size.height) {
|
|
@@ -67733,7 +67849,7 @@ var loadPty = () => {
|
|
|
67733
67849
|
} catch {
|
|
67734
67850
|
return fails("node-pty isn't installed with this service.");
|
|
67735
67851
|
}
|
|
67736
|
-
const root =
|
|
67852
|
+
const root = dirname4(require3.resolve("node-pty/package.json"));
|
|
67737
67853
|
for (const helper of [
|
|
67738
67854
|
join6(root, "prebuilds", `${process.platform}-${process.arch}`, "spawn-helper"),
|
|
67739
67855
|
join6(root, "build", "Release", "spawn-helper")
|
|
@@ -67748,7 +67864,6 @@ var loadPty = () => {
|
|
|
67748
67864
|
return pty;
|
|
67749
67865
|
};
|
|
67750
67866
|
var ProcessRuntime = class {
|
|
67751
|
-
isolation = "none";
|
|
67752
67867
|
#config;
|
|
67753
67868
|
#contained;
|
|
67754
67869
|
/** `contained` is for inside a container, where the computer and its load are its own. */
|
|
@@ -67756,6 +67871,9 @@ var ProcessRuntime = class {
|
|
|
67756
67871
|
this.#config = config3;
|
|
67757
67872
|
this.#contained = contained;
|
|
67758
67873
|
}
|
|
67874
|
+
async available() {
|
|
67875
|
+
return true;
|
|
67876
|
+
}
|
|
67759
67877
|
async prepare() {
|
|
67760
67878
|
}
|
|
67761
67879
|
async stopLeftover() {
|
|
@@ -67850,7 +67968,7 @@ async function serveInside(options) {
|
|
|
67850
67968
|
if (!token) throw new Error(`${INNER_TOKEN_VARIABLE} is not set.`);
|
|
67851
67969
|
delete process.env[INNER_TOKEN_VARIABLE];
|
|
67852
67970
|
const digest = digestOf(token);
|
|
67853
|
-
const
|
|
67971
|
+
const running2 = new ProcessRuntime({ path: BROWSER, sandbox: false }, true).start(
|
|
67854
67972
|
{
|
|
67855
67973
|
id: "inside",
|
|
67856
67974
|
...options,
|
|
@@ -67877,13 +67995,13 @@ async function serveInside(options) {
|
|
|
67877
67995
|
if (!given || !sameToken(given, digest)) return void response.writeHead(401).end();
|
|
67878
67996
|
const { pathname } = new URL(request.url ?? "/", "http://localhost");
|
|
67879
67997
|
if (pathname === "/ready") {
|
|
67880
|
-
const { browserTools, surfaces } = await
|
|
67998
|
+
const { browserTools, surfaces } = await running2;
|
|
67881
67999
|
response.writeHead(200, { "Content-Type": "application/json" });
|
|
67882
68000
|
return void response.end(JSON.stringify({ browserTools, surfaces }));
|
|
67883
68001
|
}
|
|
67884
68002
|
const endpoint = /^\/mcp\/(computer|browser)$/.exec(pathname)?.[1];
|
|
67885
68003
|
if (!endpoint) return void response.writeHead(404).end();
|
|
67886
|
-
return (await
|
|
68004
|
+
return (await running2).handle(endpoint, request, response);
|
|
67887
68005
|
};
|
|
67888
68006
|
const server = createServer3((request, response) => {
|
|
67889
68007
|
route(request, response).catch((error62) => {
|
|
@@ -67899,14 +68017,14 @@ async function serveInside(options) {
|
|
|
67899
68017
|
const mode = url3.searchParams.get("mode");
|
|
67900
68018
|
if (!given || !sameToken(given, digest) || !surface || mode !== "watch" && mode !== "operate")
|
|
67901
68019
|
return void socket.end("HTTP/1.1 401 Unauthorized\r\nConnection: close\r\n\r\n");
|
|
67902
|
-
|
|
68020
|
+
running2.then((computer) => computer.openLive(surface, mode, request, socket, head)).catch(() => socket.destroy());
|
|
67903
68021
|
});
|
|
67904
68022
|
server.listen(INNER_PORT, "0.0.0.0");
|
|
67905
68023
|
for (const signal of ["SIGINT", "SIGTERM"])
|
|
67906
68024
|
process.once(signal, () => {
|
|
67907
|
-
void
|
|
68025
|
+
void running2.then((computer) => computer.stop()).finally(() => process.exit(0));
|
|
67908
68026
|
});
|
|
67909
|
-
await
|
|
68027
|
+
await running2;
|
|
67910
68028
|
}
|
|
67911
68029
|
|
|
67912
68030
|
// src/container.ts
|
|
@@ -67941,17 +68059,30 @@ var UPGRADE_HEADERS = [
|
|
|
67941
68059
|
var nameOf = (id) => `agentcomputer-${id}`;
|
|
67942
68060
|
var sleep5 = (ms) => new Promise((done) => setTimeout(done, ms));
|
|
67943
68061
|
var ContainerRuntime = class {
|
|
67944
|
-
isolation = "container";
|
|
67945
68062
|
#image;
|
|
67946
68063
|
constructor(image) {
|
|
67947
68064
|
this.#image = image;
|
|
67948
68065
|
}
|
|
67949
|
-
|
|
68066
|
+
/** Whether Docker answers, asked for no longer than this. */
|
|
68067
|
+
async #answers(ms) {
|
|
67950
68068
|
try {
|
|
67951
|
-
await docker
|
|
67952
|
-
|
|
67953
|
-
|
|
68069
|
+
await run2("docker", ["version", "--format", "{{.Server.Version}}"], { timeout: ms });
|
|
68070
|
+
return true;
|
|
68071
|
+
} catch {
|
|
68072
|
+
return false;
|
|
67954
68073
|
}
|
|
68074
|
+
}
|
|
68075
|
+
// Asked each time the service says what it can make, so it doesn't wait on a Docker that hangs.
|
|
68076
|
+
available() {
|
|
68077
|
+
return this.#answers(1e3);
|
|
68078
|
+
}
|
|
68079
|
+
/** Docker, or the refusal of what can't be done without it. */
|
|
68080
|
+
async #docker() {
|
|
68081
|
+
if (!await this.#answers(1e4))
|
|
68082
|
+
throw new ApiError(409, "A container needs Docker, which isn't answering on this machine.");
|
|
68083
|
+
}
|
|
68084
|
+
async prepare() {
|
|
68085
|
+
await this.#docker();
|
|
67955
68086
|
try {
|
|
67956
68087
|
await docker(["image", "inspect", "--format", "{{.Id}}", this.#image]);
|
|
67957
68088
|
} catch {
|
|
@@ -68092,11 +68223,14 @@ var ContainerRuntime = class {
|
|
|
68092
68223
|
}
|
|
68093
68224
|
throw new Error("Its container didn't start. Its logs say why.");
|
|
68094
68225
|
}
|
|
68226
|
+
// Neither is said to be done where Docker doesn't answer: the container would be left as it is.
|
|
68095
68227
|
async stopLeftover(id) {
|
|
68228
|
+
await this.#docker();
|
|
68096
68229
|
await docker(["stop", "--time", STOP_SECONDS, nameOf(id)]).catch(() => {
|
|
68097
68230
|
});
|
|
68098
68231
|
}
|
|
68099
68232
|
async remove(id) {
|
|
68233
|
+
await this.#docker();
|
|
68100
68234
|
await docker(["rm", "--force", nameOf(id)]).catch(() => {
|
|
68101
68235
|
});
|
|
68102
68236
|
}
|
|
@@ -68145,10 +68279,14 @@ async function forward(url3, authorization, request, response) {
|
|
|
68145
68279
|
}
|
|
68146
68280
|
|
|
68147
68281
|
// src/handlers.ts
|
|
68148
|
-
var VERSION2 = "0.0.
|
|
68282
|
+
var VERSION2 = "0.0.16";
|
|
68149
68283
|
function createHandlers(computers) {
|
|
68150
68284
|
return {
|
|
68151
|
-
getInfo: async () => ({
|
|
68285
|
+
getInfo: async () => ({
|
|
68286
|
+
version: VERSION2,
|
|
68287
|
+
isolation: computers.isolation,
|
|
68288
|
+
isolationsAvailable: await computers.available()
|
|
68289
|
+
}),
|
|
68152
68290
|
listComputers: async ({ state }) => computers.list(state),
|
|
68153
68291
|
createComputer: (input2) => computers.create(input2),
|
|
68154
68292
|
getComputer: async ({ computerID }) => computers.get(computerID),
|
|
@@ -68167,6 +68305,81 @@ function createHandlers(computers) {
|
|
|
68167
68305
|
};
|
|
68168
68306
|
}
|
|
68169
68307
|
|
|
68308
|
+
// src/running.ts
|
|
68309
|
+
import { spawn as spawn4 } from "node:child_process";
|
|
68310
|
+
import { closeSync, openSync, readFileSync as readFileSync4, statSync as statSync3, writeFileSync as writeFileSync2 } from "node:fs";
|
|
68311
|
+
import { join as join9 } from "node:path";
|
|
68312
|
+
var MAX_LOG = 1024 * 1024;
|
|
68313
|
+
var STOP_MS2 = 3e4;
|
|
68314
|
+
var sleep6 = (ms) => new Promise((done) => setTimeout(done, ms));
|
|
68315
|
+
var recordOf = (data) => join9(data, "server.json");
|
|
68316
|
+
function recorded(data) {
|
|
68317
|
+
try {
|
|
68318
|
+
const { url: url3, pid } = JSON.parse(readFileSync4(recordOf(data), "utf8"));
|
|
68319
|
+
return { url: url3, pid, token: readFileSync4(join9(data, "control-token"), "utf8").trim() };
|
|
68320
|
+
} catch {
|
|
68321
|
+
return void 0;
|
|
68322
|
+
}
|
|
68323
|
+
}
|
|
68324
|
+
async function running(data, said = recorded(data)) {
|
|
68325
|
+
if (!said) return void 0;
|
|
68326
|
+
try {
|
|
68327
|
+
const answer = await fetch(`${said.url}/v1/info`, {
|
|
68328
|
+
headers: { Authorization: `Bearer ${said.token}` },
|
|
68329
|
+
signal: AbortSignal.timeout(3e3)
|
|
68330
|
+
});
|
|
68331
|
+
if (!answer.ok) return void 0;
|
|
68332
|
+
const { version: version3, isolation } = await answer.json();
|
|
68333
|
+
return { url: said.url, pid: said.pid, version: version3, isolation };
|
|
68334
|
+
} catch {
|
|
68335
|
+
return void 0;
|
|
68336
|
+
}
|
|
68337
|
+
}
|
|
68338
|
+
async function up(data, program, args) {
|
|
68339
|
+
const already = await running(data);
|
|
68340
|
+
if (already) return already;
|
|
68341
|
+
const log = join9(privateFolder(data), "service.log");
|
|
68342
|
+
try {
|
|
68343
|
+
const before = readFileSync4(log);
|
|
68344
|
+
if (before.length > MAX_LOG) writeFileSync2(log, before.subarray(before.length - MAX_LOG));
|
|
68345
|
+
} catch {
|
|
68346
|
+
}
|
|
68347
|
+
const from = statSync3(log, { throwIfNoEntry: false })?.size ?? 0;
|
|
68348
|
+
const out = openSync(log, "a", 384);
|
|
68349
|
+
const child = spawn4(process.execPath, [program, "serve", ...args], {
|
|
68350
|
+
detached: true,
|
|
68351
|
+
stdio: ["ignore", out, out]
|
|
68352
|
+
});
|
|
68353
|
+
closeSync(out);
|
|
68354
|
+
child.unref();
|
|
68355
|
+
let ended = false;
|
|
68356
|
+
child.once("exit", () => {
|
|
68357
|
+
ended = true;
|
|
68358
|
+
});
|
|
68359
|
+
for (; ; await sleep6(100)) {
|
|
68360
|
+
const said = recorded(data);
|
|
68361
|
+
const up2 = await running(data, said);
|
|
68362
|
+
const own3 = said?.pid === child.pid;
|
|
68363
|
+
if (up2 && (own3 || ended)) return up2;
|
|
68364
|
+
if (!ended && !own3) continue;
|
|
68365
|
+
if (!ended) {
|
|
68366
|
+
child.kill();
|
|
68367
|
+
throw new Error(`The service doesn't answer at ${said?.url} from here, so it was stopped.`);
|
|
68368
|
+
}
|
|
68369
|
+
const wrote = readFileSync4(log).subarray(from).toString().trim();
|
|
68370
|
+
throw new Error(`The service didn't start.${wrote ? ` It said:
|
|
68371
|
+
${wrote}` : ""}`);
|
|
68372
|
+
}
|
|
68373
|
+
}
|
|
68374
|
+
async function down(data) {
|
|
68375
|
+
const found = await running(data);
|
|
68376
|
+
if (!found) return;
|
|
68377
|
+
process.kill(found.pid, "SIGTERM");
|
|
68378
|
+
for (const deadline = Date.now() + STOP_MS2; Date.now() < deadline; await sleep6(100))
|
|
68379
|
+
if (!await running(data)) return;
|
|
68380
|
+
throw new Error(`The service at ${found.url} didn't stop.`);
|
|
68381
|
+
}
|
|
68382
|
+
|
|
68170
68383
|
// node_modules/openapi-fetch/dist/index.mjs
|
|
68171
68384
|
var PATH_PARAM_RE = /\{[^{}]+\}/g;
|
|
68172
68385
|
var supportsRequestInitExt = () => {
|
|
@@ -68852,13 +69065,14 @@ var Computer = class _Computer {
|
|
|
68852
69065
|
this.files = new Files(this.#computer);
|
|
68853
69066
|
}
|
|
68854
69067
|
static async create(opts) {
|
|
68855
|
-
const { access, browser, workspace, name, timeoutMs, metadata, apiKey, apiUrl } = opts;
|
|
69068
|
+
const { access, browser, isolation, workspace, name, timeoutMs, metadata, apiKey, apiUrl } = opts;
|
|
68856
69069
|
const connection = { ...apiKey ? { apiKey } : {}, ...apiUrl ? { apiUrl } : {} };
|
|
68857
69070
|
const created = answered(
|
|
68858
69071
|
await apiOf(connection).POST("/v1/computers", {
|
|
68859
69072
|
body: {
|
|
68860
69073
|
access,
|
|
68861
69074
|
browser: browser ?? false,
|
|
69075
|
+
...isolation === void 0 ? {} : { isolation },
|
|
68862
69076
|
...workspace === void 0 ? {} : { workspace },
|
|
68863
69077
|
...name === void 0 ? {} : { name },
|
|
68864
69078
|
...timeoutMs === void 0 ? {} : { timeout: seconds(timeoutMs) },
|
|
@@ -68963,13 +69177,12 @@ var Computer = class _Computer {
|
|
|
68963
69177
|
};
|
|
68964
69178
|
|
|
68965
69179
|
// src/service.ts
|
|
68966
|
-
import { readFileSync as
|
|
69180
|
+
import { readFileSync as readFileSync6, rmSync as rmSync5 } from "node:fs";
|
|
68967
69181
|
import {
|
|
68968
69182
|
createServer as createServer5,
|
|
68969
69183
|
STATUS_CODES as STATUS_CODES3
|
|
68970
69184
|
} from "node:http";
|
|
68971
69185
|
import { createServer as createTlsServer } from "node:https";
|
|
68972
|
-
import { join as join10 } from "node:path";
|
|
68973
69186
|
|
|
68974
69187
|
// node_modules/@modelcontextprotocol/server/dist/chunk-vYduTEsO.mjs
|
|
68975
69188
|
var __create4 = Object.create;
|
|
@@ -72430,7 +72643,7 @@ var REQUEST_STATE_ONLY_LEG_PACING_MS3 = 250;
|
|
|
72430
72643
|
function inputRequiredRoundsExceededMessage3(method, maxRounds) {
|
|
72431
72644
|
return `Multi-round-trip request '${method}' still required input after ${maxRounds} rounds (inputRequired.maxRounds)`;
|
|
72432
72645
|
}
|
|
72433
|
-
function
|
|
72646
|
+
function sleep7(ms, signal) {
|
|
72434
72647
|
return new Promise((resolve6, reject) => {
|
|
72435
72648
|
if (signal?.aborted) {
|
|
72436
72649
|
reject(signal.reason instanceof SdkError3 ? signal.reason : new SdkError3(SdkErrorCode3.RequestTimeout, String(signal.reason)));
|
|
@@ -76061,14 +76274,14 @@ var require_validate3 = /* @__PURE__ */ __commonJSMin3(((exports) => {
|
|
|
76061
76274
|
} else {
|
|
76062
76275
|
const matches = RELATIVE_JSON_POINTER.exec($data);
|
|
76063
76276
|
if (!matches) throw new Error(`Invalid JSON-pointer: ${$data}`);
|
|
76064
|
-
const
|
|
76277
|
+
const up2 = +matches[1];
|
|
76065
76278
|
jsonPointer = matches[2];
|
|
76066
76279
|
if (jsonPointer === "#") {
|
|
76067
|
-
if (
|
|
76068
|
-
return dataPathArr[dataLevel -
|
|
76280
|
+
if (up2 >= dataLevel) throw new Error(errorMsg("property/index", up2));
|
|
76281
|
+
return dataPathArr[dataLevel - up2];
|
|
76069
76282
|
}
|
|
76070
|
-
if (
|
|
76071
|
-
data = dataNames[dataLevel -
|
|
76283
|
+
if (up2 > dataLevel) throw new Error(errorMsg("data", up2));
|
|
76284
|
+
data = dataNames[dataLevel - up2];
|
|
76072
76285
|
if (!jsonPointer) return data;
|
|
76073
76286
|
}
|
|
76074
76287
|
let expr = data;
|
|
@@ -76078,8 +76291,8 @@ var require_validate3 = /* @__PURE__ */ __commonJSMin3(((exports) => {
|
|
|
76078
76291
|
expr = (0, codegen_1._)`${expr} && ${data}`;
|
|
76079
76292
|
}
|
|
76080
76293
|
return expr;
|
|
76081
|
-
function errorMsg(pointerType,
|
|
76082
|
-
return `Cannot access ${pointerType} ${
|
|
76294
|
+
function errorMsg(pointerType, up2) {
|
|
76295
|
+
return `Cannot access ${pointerType} ${up2} levels up, current level is ${dataLevel}`;
|
|
76083
76296
|
}
|
|
76084
76297
|
}
|
|
76085
76298
|
exports.getData = getData;
|
|
@@ -81427,7 +81640,7 @@ var LegacyInputRequiredShim2 = class {
|
|
|
81427
81640
|
} finally {
|
|
81428
81641
|
roundAbort.dispose();
|
|
81429
81642
|
}
|
|
81430
|
-
} else await
|
|
81643
|
+
} else await sleep7(REQUEST_STATE_ONLY_LEG_PACING_MS3, outerSignal);
|
|
81431
81644
|
let ctxNext = {
|
|
81432
81645
|
...ctx,
|
|
81433
81646
|
mcpReq: {
|
|
@@ -84784,8 +84997,8 @@ var NodeStreamableHTTPServerTransport2 = class {
|
|
|
84784
84997
|
|
|
84785
84998
|
// src/computers.ts
|
|
84786
84999
|
import { randomBytes as randomBytes5 } from "node:crypto";
|
|
84787
|
-
import { appendFileSync, existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync as
|
|
84788
|
-
import { isAbsolute, join as
|
|
85000
|
+
import { appendFileSync, existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync as readFileSync5, rmSync as rmSync4, statSync as statSync4 } from "node:fs";
|
|
85001
|
+
import { isAbsolute, join as join10, resolve as resolve4 } from "node:path";
|
|
84789
85002
|
var READ = ["about", "read", "list", "search"];
|
|
84790
85003
|
var FILES = [...READ, "edit"];
|
|
84791
85004
|
var TOOLS2 = {
|
|
@@ -84793,29 +85006,37 @@ var TOOLS2 = {
|
|
|
84793
85006
|
files: FILES,
|
|
84794
85007
|
terminal: [...FILES, "exec_command", "write_stdin", "list_sessions"]
|
|
84795
85008
|
};
|
|
84796
|
-
var
|
|
85009
|
+
var MAX_LOG2 = 256 * 1024;
|
|
84797
85010
|
var MAX_TIMER_S = 2e6;
|
|
84798
85011
|
var TICKET_MS = 6e4;
|
|
84799
85012
|
var expiry = (seconds2) => new Date(Date.now() + seconds2 * 1e3).toISOString();
|
|
85013
|
+
var messageOf = (error62) => error62 instanceof Error ? error62.message : String(error62);
|
|
85014
|
+
var refusalOf = (error62) => error62 instanceof ApiError ? error62 : new ApiError(500, messageOf(error62));
|
|
84800
85015
|
var Computers = class {
|
|
84801
85016
|
#data;
|
|
84802
85017
|
#base;
|
|
84803
|
-
#
|
|
85018
|
+
#runtimes;
|
|
85019
|
+
/** The isolation of a computer made without saying which. */
|
|
85020
|
+
isolation;
|
|
84804
85021
|
#stored = /* @__PURE__ */ new Map();
|
|
84805
85022
|
#live = /* @__PURE__ */ new Map();
|
|
84806
85023
|
#tickets = /* @__PURE__ */ new Map();
|
|
84807
85024
|
#file;
|
|
84808
|
-
constructor(data, base,
|
|
85025
|
+
constructor(data, base, runtimes, isolation) {
|
|
84809
85026
|
this.#data = data;
|
|
84810
85027
|
this.#base = base;
|
|
84811
|
-
this.#
|
|
84812
|
-
this
|
|
85028
|
+
this.#runtimes = runtimes;
|
|
85029
|
+
this.isolation = isolation;
|
|
85030
|
+
this.#file = join10(data, "computers.json");
|
|
84813
85031
|
if (existsSync2(this.#file))
|
|
84814
|
-
for (const entry of JSON.parse(
|
|
84815
|
-
this.#stored.set(entry.id, entry);
|
|
85032
|
+
for (const entry of JSON.parse(readFileSync5(this.#file, "utf8")))
|
|
85033
|
+
this.#stored.set(entry.id, { ...entry, isolation: entry.isolation ?? isolation });
|
|
84816
85034
|
}
|
|
84817
|
-
|
|
84818
|
-
|
|
85035
|
+
/** The isolations a computer can be made in now. */
|
|
85036
|
+
async available() {
|
|
85037
|
+
const all = Object.keys(this.#runtimes);
|
|
85038
|
+
const can = await Promise.all(all.map((one) => this.#runtimes[one].available()));
|
|
85039
|
+
return all.filter((_, index) => can[index]);
|
|
84819
85040
|
}
|
|
84820
85041
|
/** Bring back what was running when the service last stopped. */
|
|
84821
85042
|
async resume() {
|
|
@@ -84823,12 +85044,17 @@ var Computers = class {
|
|
|
84823
85044
|
if (stored.state !== "running") continue;
|
|
84824
85045
|
if (stored.endAt && Date.parse(stored.endAt) <= Date.now()) {
|
|
84825
85046
|
stored.state = "stopped";
|
|
84826
|
-
await this.#
|
|
85047
|
+
await this.#runtimes[stored.isolation].stopLeftover(stored.id).catch(
|
|
85048
|
+
(error62) => this.#log(stored.id, `What it left running wasn't stopped: ${messageOf(error62)}`)
|
|
85049
|
+
);
|
|
84827
85050
|
continue;
|
|
84828
85051
|
}
|
|
84829
|
-
await this.#start(stored).catch(
|
|
84830
|
-
|
|
84831
|
-
|
|
85052
|
+
await this.#start(stored).catch((error62) => {
|
|
85053
|
+
const live = this.#live.get(stored.id);
|
|
85054
|
+
if (live) live.failed = true;
|
|
85055
|
+
this.#arm(stored);
|
|
85056
|
+
this.#log(stored.id, `It didn't start again: ${messageOf(error62)}`);
|
|
85057
|
+
});
|
|
84832
85058
|
}
|
|
84833
85059
|
this.#save();
|
|
84834
85060
|
}
|
|
@@ -84841,7 +85067,7 @@ var Computers = class {
|
|
|
84841
85067
|
if (input2.workspace !== void 0) {
|
|
84842
85068
|
let folder = false;
|
|
84843
85069
|
try {
|
|
84844
|
-
folder = isAbsolute(input2.workspace) &&
|
|
85070
|
+
folder = isAbsolute(input2.workspace) && statSync4(input2.workspace).isDirectory();
|
|
84845
85071
|
} catch {
|
|
84846
85072
|
}
|
|
84847
85073
|
if (!folder) throw new ApiError(400, `There's no folder at ${input2.workspace}.`);
|
|
@@ -84854,6 +85080,7 @@ var Computers = class {
|
|
|
84854
85080
|
id,
|
|
84855
85081
|
name: name ?? id,
|
|
84856
85082
|
access,
|
|
85083
|
+
isolation: input2.isolation ?? this.isolation,
|
|
84857
85084
|
browser,
|
|
84858
85085
|
...workspace ? { workspace } : {},
|
|
84859
85086
|
metadata: metadata ?? {},
|
|
@@ -84864,7 +85091,7 @@ var Computers = class {
|
|
|
84864
85091
|
...timeout === void 0 ? {} : { endAt: expiry(timeout) }
|
|
84865
85092
|
};
|
|
84866
85093
|
if (!workspace && access !== "none") {
|
|
84867
|
-
stored.workspace =
|
|
85094
|
+
stored.workspace = join10(this.#folder(id), "workspace");
|
|
84868
85095
|
mkdirSync2(stored.workspace, { recursive: true, mode: 448 });
|
|
84869
85096
|
}
|
|
84870
85097
|
this.#stored.set(id, stored);
|
|
@@ -84873,9 +85100,10 @@ var Computers = class {
|
|
|
84873
85100
|
} catch (error62) {
|
|
84874
85101
|
await this.#halt(id);
|
|
84875
85102
|
this.#stored.delete(id);
|
|
84876
|
-
await this.#
|
|
84877
|
-
|
|
84878
|
-
|
|
85103
|
+
await this.#runtimes[stored.isolation].remove(id).catch(() => {
|
|
85104
|
+
});
|
|
85105
|
+
rmSync4(this.#folder(id), { recursive: true, force: true });
|
|
85106
|
+
throw refusalOf(error62);
|
|
84879
85107
|
}
|
|
84880
85108
|
this.#save();
|
|
84881
85109
|
return { ...this.#describe(stored), accessToken: token };
|
|
@@ -84930,25 +85158,31 @@ var Computers = class {
|
|
|
84930
85158
|
await this.#halt(id);
|
|
84931
85159
|
stored.state = "stopped";
|
|
84932
85160
|
this.#save();
|
|
84933
|
-
throw
|
|
85161
|
+
throw refusalOf(error62);
|
|
84934
85162
|
}
|
|
84935
85163
|
this.#save();
|
|
84936
85164
|
return this.#describe(stored);
|
|
84937
85165
|
}
|
|
84938
85166
|
/** Stop everything and remove what the service made. A folder someone named stays. */
|
|
84939
85167
|
async kill(id) {
|
|
84940
|
-
this.#found(id);
|
|
85168
|
+
const stored = this.#found(id);
|
|
84941
85169
|
await this.#halt(id);
|
|
84942
|
-
|
|
85170
|
+
try {
|
|
85171
|
+
await this.#runtimes[stored.isolation].remove(id);
|
|
85172
|
+
} catch (error62) {
|
|
85173
|
+
stored.state = "stopped";
|
|
85174
|
+
this.#save();
|
|
85175
|
+
throw refusalOf(error62);
|
|
85176
|
+
}
|
|
84943
85177
|
this.#stored.delete(id);
|
|
84944
85178
|
this.#save();
|
|
84945
|
-
|
|
85179
|
+
rmSync4(this.#folder(id), { recursive: true, force: true });
|
|
84946
85180
|
}
|
|
84947
85181
|
logs(id) {
|
|
84948
85182
|
this.#found(id);
|
|
84949
85183
|
let text3 = "";
|
|
84950
85184
|
try {
|
|
84951
|
-
text3 =
|
|
85185
|
+
text3 = readFileSync5(this.#logFile(id), "utf8");
|
|
84952
85186
|
} catch {
|
|
84953
85187
|
}
|
|
84954
85188
|
return text3.split("\n").filter(Boolean).map((entry) => ({
|
|
@@ -84965,17 +85199,17 @@ var Computers = class {
|
|
|
84965
85199
|
throw new ApiError(404, "This computer has no files or terminal.");
|
|
84966
85200
|
if (endpoint === "browser" && !stored.browser)
|
|
84967
85201
|
throw new ApiError(404, "This computer has no browser.");
|
|
84968
|
-
const
|
|
84969
|
-
if (!
|
|
85202
|
+
const running2 = this.#live.get(id)?.running;
|
|
85203
|
+
if (!running2) throw new ApiError(409, "It has stopped.");
|
|
84970
85204
|
this.#log(id, `${request.method} mcp/${endpoint}`);
|
|
84971
|
-
return
|
|
85205
|
+
return running2.handle(endpoint, request, response);
|
|
84972
85206
|
}
|
|
84973
85207
|
/** A one-time ticket to open one of its surfaces live. */
|
|
84974
85208
|
liveSession(id, surface, mode) {
|
|
84975
85209
|
this.#found(id);
|
|
84976
|
-
const
|
|
84977
|
-
if (!
|
|
84978
|
-
const info =
|
|
85210
|
+
const running2 = this.#live.get(id)?.running;
|
|
85211
|
+
if (!running2) throw new ApiError(409, "It has stopped.");
|
|
85212
|
+
const info = running2.surfaces.find((one) => one.surface === surface);
|
|
84979
85213
|
if (!info) throw new ApiError(409, `This computer has no ${surface} to open.`);
|
|
84980
85214
|
if (info.unavailable) throw new ApiError(409, info.unavailable);
|
|
84981
85215
|
if (mode === "operate" && !info.operate)
|
|
@@ -85000,10 +85234,10 @@ var Computers = class {
|
|
|
85000
85234
|
this.#tickets.delete(ticket);
|
|
85001
85235
|
if (!found || found.expiresAt <= Date.now())
|
|
85002
85236
|
throw new ApiError(404, "There's no such live session: it was used, or it expired.");
|
|
85003
|
-
const
|
|
85004
|
-
if (!
|
|
85237
|
+
const running2 = this.#live.get(found.computerID)?.running;
|
|
85238
|
+
if (!running2) throw new ApiError(409, "It has stopped.");
|
|
85005
85239
|
this.#log(found.computerID, `live ${found.surface} ${found.mode}`);
|
|
85006
|
-
await
|
|
85240
|
+
await running2.openLive(found.surface, found.mode, request, socket, head);
|
|
85007
85241
|
}
|
|
85008
85242
|
/** Stop every computer, leaving their records to resume at the next start. */
|
|
85009
85243
|
async close() {
|
|
@@ -85015,17 +85249,17 @@ var Computers = class {
|
|
|
85015
85249
|
return stored;
|
|
85016
85250
|
}
|
|
85017
85251
|
#folder(id) {
|
|
85018
|
-
return privateFolder(
|
|
85252
|
+
return privateFolder(join10(this.#data, "computers", id));
|
|
85019
85253
|
}
|
|
85020
85254
|
#logFile(id) {
|
|
85021
|
-
return
|
|
85255
|
+
return join10(this.#folder(id), "log");
|
|
85022
85256
|
}
|
|
85023
85257
|
#log(id, line) {
|
|
85024
85258
|
if (!this.#stored.has(id)) return;
|
|
85025
85259
|
try {
|
|
85026
85260
|
const file2 = this.#logFile(id);
|
|
85027
|
-
if (existsSync2(file2) &&
|
|
85028
|
-
writePrivate(file2,
|
|
85261
|
+
if (existsSync2(file2) && statSync4(file2).size > MAX_LOG2)
|
|
85262
|
+
writePrivate(file2, readFileSync5(file2, "utf8").slice(-MAX_LOG2 / 2));
|
|
85029
85263
|
appendFileSync(file2, `${(/* @__PURE__ */ new Date()).toISOString()} ${line}
|
|
85030
85264
|
`, { mode: 384 });
|
|
85031
85265
|
} catch {
|
|
@@ -85036,12 +85270,13 @@ var Computers = class {
|
|
|
85036
85270
|
}
|
|
85037
85271
|
#describe(stored) {
|
|
85038
85272
|
const path = `${this.#base()}/v1/computers/${stored.id}/mcp`;
|
|
85039
|
-
const
|
|
85273
|
+
const down2 = this.#live.get(stored.id)?.failed === true;
|
|
85040
85274
|
return {
|
|
85041
85275
|
computerID: stored.id,
|
|
85042
85276
|
name: stored.name,
|
|
85043
|
-
state: stored.state === "running" &&
|
|
85277
|
+
state: stored.state === "running" && down2 ? "failed" : stored.state,
|
|
85044
85278
|
access: stored.access,
|
|
85279
|
+
isolation: stored.isolation,
|
|
85045
85280
|
browser: stored.browser,
|
|
85046
85281
|
workspace: stored.workspace ?? null,
|
|
85047
85282
|
startedAt: stored.startedAt,
|
|
@@ -85057,7 +85292,9 @@ var Computers = class {
|
|
|
85057
85292
|
const live = {};
|
|
85058
85293
|
this.#live.set(stored.id, live);
|
|
85059
85294
|
stored.startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
85060
|
-
|
|
85295
|
+
const runtime = this.#runtimes[stored.isolation];
|
|
85296
|
+
await runtime.prepare();
|
|
85297
|
+
live.running = await runtime.start(
|
|
85061
85298
|
{
|
|
85062
85299
|
id: stored.id,
|
|
85063
85300
|
access: stored.access,
|
|
@@ -85110,7 +85347,7 @@ var OPENAPI = {
|
|
|
85110
85347
|
"openapi": "3.1.0",
|
|
85111
85348
|
"info": {
|
|
85112
85349
|
"title": "AgentComputer",
|
|
85113
|
-
"version": "0.0.
|
|
85350
|
+
"version": "0.0.16",
|
|
85114
85351
|
"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.",
|
|
85115
85352
|
"license": {
|
|
85116
85353
|
"name": "Apache-2.0",
|
|
@@ -85570,7 +85807,8 @@ var OPENAPI = {
|
|
|
85570
85807
|
"running",
|
|
85571
85808
|
"stopped",
|
|
85572
85809
|
"failed"
|
|
85573
|
-
]
|
|
85810
|
+
],
|
|
85811
|
+
"description": "failed: it was to be running and isn't, because its browser or container ended by itself, or because it couldn't be brought back when the service started. Its logs say why. Stop it and start it again."
|
|
85574
85812
|
},
|
|
85575
85813
|
"Timeout": {
|
|
85576
85814
|
"type": "integer",
|
|
@@ -85611,6 +85849,10 @@ var OPENAPI = {
|
|
|
85611
85849
|
"maxLength": 4096,
|
|
85612
85850
|
"description": "The full path of a folder that exists, on the service's machine. With none, the service makes a folder, which goes when the computer is killed. A folder named here never does."
|
|
85613
85851
|
},
|
|
85852
|
+
"isolation": {
|
|
85853
|
+
"$ref": "#/components/schemas/Isolation",
|
|
85854
|
+
"description": "How it is isolated, which it keeps for as long as it exists. Left out, it is the service's own, the `isolation` that `getInfo` answers."
|
|
85855
|
+
},
|
|
85614
85856
|
"timeout": {
|
|
85615
85857
|
"$ref": "#/components/schemas/Timeout"
|
|
85616
85858
|
},
|
|
@@ -85619,6 +85861,14 @@ var OPENAPI = {
|
|
|
85619
85861
|
}
|
|
85620
85862
|
}
|
|
85621
85863
|
},
|
|
85864
|
+
"Isolation": {
|
|
85865
|
+
"type": "string",
|
|
85866
|
+
"enum": [
|
|
85867
|
+
"none",
|
|
85868
|
+
"container"
|
|
85869
|
+
],
|
|
85870
|
+
"description": "none: processes of whoever started the service. container: a container of its own, which needs Docker on the service's machine."
|
|
85871
|
+
},
|
|
85622
85872
|
"Endpoints": {
|
|
85623
85873
|
"type": "object",
|
|
85624
85874
|
"required": [
|
|
@@ -85650,6 +85900,7 @@ var OPENAPI = {
|
|
|
85650
85900
|
"name",
|
|
85651
85901
|
"state",
|
|
85652
85902
|
"access",
|
|
85903
|
+
"isolation",
|
|
85653
85904
|
"browser",
|
|
85654
85905
|
"workspace",
|
|
85655
85906
|
"startedAt",
|
|
@@ -85670,6 +85921,10 @@ var OPENAPI = {
|
|
|
85670
85921
|
"access": {
|
|
85671
85922
|
"$ref": "#/components/schemas/Access"
|
|
85672
85923
|
},
|
|
85924
|
+
"isolation": {
|
|
85925
|
+
"$ref": "#/components/schemas/Isolation",
|
|
85926
|
+
"description": "The isolation it was made in. It is never run in another."
|
|
85927
|
+
},
|
|
85673
85928
|
"browser": {
|
|
85674
85929
|
"type": "boolean"
|
|
85675
85930
|
},
|
|
@@ -86828,19 +87083,23 @@ var OPENAPI = {
|
|
|
86828
87083
|
"type": "object",
|
|
86829
87084
|
"required": [
|
|
86830
87085
|
"version",
|
|
86831
|
-
"isolation"
|
|
87086
|
+
"isolation",
|
|
87087
|
+
"isolationsAvailable"
|
|
86832
87088
|
],
|
|
86833
87089
|
"properties": {
|
|
86834
87090
|
"version": {
|
|
86835
87091
|
"type": "string"
|
|
86836
87092
|
},
|
|
86837
87093
|
"isolation": {
|
|
86838
|
-
"
|
|
86839
|
-
"
|
|
86840
|
-
|
|
86841
|
-
|
|
86842
|
-
|
|
86843
|
-
"
|
|
87094
|
+
"$ref": "#/components/schemas/Isolation",
|
|
87095
|
+
"description": "The isolation of a computer made without saying which, chosen when the service is started."
|
|
87096
|
+
},
|
|
87097
|
+
"isolationsAvailable": {
|
|
87098
|
+
"type": "array",
|
|
87099
|
+
"items": {
|
|
87100
|
+
"$ref": "#/components/schemas/Isolation"
|
|
87101
|
+
},
|
|
87102
|
+
"description": "The isolations a computer can be made in now, `none` first. `container` is among them while Docker answers on the service's machine. It is what to offer, not a promise: Docker is asked for a second here and for ten when a container is made, so one that is slow can be missing here and still be made. If `isolation` itself is missing, a computer made without saying which is refused with 409."
|
|
86844
87103
|
}
|
|
86845
87104
|
}
|
|
86846
87105
|
}
|
|
@@ -86868,7 +87127,8 @@ var OPERATIONS = [
|
|
|
86868
87127
|
"type": "object",
|
|
86869
87128
|
"required": [
|
|
86870
87129
|
"version",
|
|
86871
|
-
"isolation"
|
|
87130
|
+
"isolation",
|
|
87131
|
+
"isolationsAvailable"
|
|
86872
87132
|
],
|
|
86873
87133
|
"properties": {
|
|
86874
87134
|
"version": {
|
|
@@ -86880,7 +87140,19 @@ var OPERATIONS = [
|
|
|
86880
87140
|
"none",
|
|
86881
87141
|
"container"
|
|
86882
87142
|
],
|
|
86883
|
-
"description": "
|
|
87143
|
+
"description": "The isolation of a computer made without saying which, chosen when the service is started."
|
|
87144
|
+
},
|
|
87145
|
+
"isolationsAvailable": {
|
|
87146
|
+
"type": "array",
|
|
87147
|
+
"items": {
|
|
87148
|
+
"type": "string",
|
|
87149
|
+
"enum": [
|
|
87150
|
+
"none",
|
|
87151
|
+
"container"
|
|
87152
|
+
],
|
|
87153
|
+
"description": "none: processes of whoever started the service. container: a container of its own, which needs Docker on the service's machine."
|
|
87154
|
+
},
|
|
87155
|
+
"description": "The isolations a computer can be made in now, `none` first. `container` is among them while Docker answers on the service's machine. It is what to offer, not a promise: Docker is asked for a second here and for ten when a container is made, so one that is slow can be missing here and still be made. If `isolation` itself is missing, a computer made without saying which is refused with 409."
|
|
86884
87156
|
}
|
|
86885
87157
|
}
|
|
86886
87158
|
},
|
|
@@ -86908,7 +87180,8 @@ var OPERATIONS = [
|
|
|
86908
87180
|
"running",
|
|
86909
87181
|
"stopped",
|
|
86910
87182
|
"failed"
|
|
86911
|
-
]
|
|
87183
|
+
],
|
|
87184
|
+
"description": "failed: it was to be running and isn't, because its browser or container ended by itself, or because it couldn't be brought back when the service started. Its logs say why. Stop it and start it again."
|
|
86912
87185
|
}
|
|
86913
87186
|
},
|
|
86914
87187
|
"required": []
|
|
@@ -86922,6 +87195,7 @@ var OPERATIONS = [
|
|
|
86922
87195
|
"name",
|
|
86923
87196
|
"state",
|
|
86924
87197
|
"access",
|
|
87198
|
+
"isolation",
|
|
86925
87199
|
"browser",
|
|
86926
87200
|
"workspace",
|
|
86927
87201
|
"startedAt",
|
|
@@ -86942,7 +87216,8 @@ var OPERATIONS = [
|
|
|
86942
87216
|
"running",
|
|
86943
87217
|
"stopped",
|
|
86944
87218
|
"failed"
|
|
86945
|
-
]
|
|
87219
|
+
],
|
|
87220
|
+
"description": "failed: it was to be running and isn't, because its browser or container ended by itself, or because it couldn't be brought back when the service started. Its logs say why. Stop it and start it again."
|
|
86946
87221
|
},
|
|
86947
87222
|
"access": {
|
|
86948
87223
|
"type": "string",
|
|
@@ -86954,6 +87229,14 @@ var OPERATIONS = [
|
|
|
86954
87229
|
],
|
|
86955
87230
|
"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."
|
|
86956
87231
|
},
|
|
87232
|
+
"isolation": {
|
|
87233
|
+
"type": "string",
|
|
87234
|
+
"enum": [
|
|
87235
|
+
"none",
|
|
87236
|
+
"container"
|
|
87237
|
+
],
|
|
87238
|
+
"description": "The isolation it was made in. It is never run in another."
|
|
87239
|
+
},
|
|
86957
87240
|
"browser": {
|
|
86958
87241
|
"type": "boolean"
|
|
86959
87242
|
},
|
|
@@ -87053,6 +87336,14 @@ var OPERATIONS = [
|
|
|
87053
87336
|
"maxLength": 4096,
|
|
87054
87337
|
"description": "The full path of a folder that exists, on the service's machine. With none, the service makes a folder, which goes when the computer is killed. A folder named here never does."
|
|
87055
87338
|
},
|
|
87339
|
+
"isolation": {
|
|
87340
|
+
"type": "string",
|
|
87341
|
+
"enum": [
|
|
87342
|
+
"none",
|
|
87343
|
+
"container"
|
|
87344
|
+
],
|
|
87345
|
+
"description": "How it is isolated, which it keeps for as long as it exists. Left out, it is the service's own, the `isolation` that `getInfo` answers."
|
|
87346
|
+
},
|
|
87056
87347
|
"timeout": {
|
|
87057
87348
|
"type": "integer",
|
|
87058
87349
|
"minimum": 1,
|
|
@@ -87081,6 +87372,7 @@ var OPERATIONS = [
|
|
|
87081
87372
|
"name",
|
|
87082
87373
|
"state",
|
|
87083
87374
|
"access",
|
|
87375
|
+
"isolation",
|
|
87084
87376
|
"browser",
|
|
87085
87377
|
"workspace",
|
|
87086
87378
|
"startedAt",
|
|
@@ -87101,7 +87393,8 @@ var OPERATIONS = [
|
|
|
87101
87393
|
"running",
|
|
87102
87394
|
"stopped",
|
|
87103
87395
|
"failed"
|
|
87104
|
-
]
|
|
87396
|
+
],
|
|
87397
|
+
"description": "failed: it was to be running and isn't, because its browser or container ended by itself, or because it couldn't be brought back when the service started. Its logs say why. Stop it and start it again."
|
|
87105
87398
|
},
|
|
87106
87399
|
"access": {
|
|
87107
87400
|
"type": "string",
|
|
@@ -87113,6 +87406,14 @@ var OPERATIONS = [
|
|
|
87113
87406
|
],
|
|
87114
87407
|
"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."
|
|
87115
87408
|
},
|
|
87409
|
+
"isolation": {
|
|
87410
|
+
"type": "string",
|
|
87411
|
+
"enum": [
|
|
87412
|
+
"none",
|
|
87413
|
+
"container"
|
|
87414
|
+
],
|
|
87415
|
+
"description": "The isolation it was made in. It is never run in another."
|
|
87416
|
+
},
|
|
87116
87417
|
"browser": {
|
|
87117
87418
|
"type": "boolean"
|
|
87118
87419
|
},
|
|
@@ -87221,6 +87522,7 @@ var OPERATIONS = [
|
|
|
87221
87522
|
"name",
|
|
87222
87523
|
"state",
|
|
87223
87524
|
"access",
|
|
87525
|
+
"isolation",
|
|
87224
87526
|
"browser",
|
|
87225
87527
|
"workspace",
|
|
87226
87528
|
"startedAt",
|
|
@@ -87241,7 +87543,8 @@ var OPERATIONS = [
|
|
|
87241
87543
|
"running",
|
|
87242
87544
|
"stopped",
|
|
87243
87545
|
"failed"
|
|
87244
|
-
]
|
|
87546
|
+
],
|
|
87547
|
+
"description": "failed: it was to be running and isn't, because its browser or container ended by itself, or because it couldn't be brought back when the service started. Its logs say why. Stop it and start it again."
|
|
87245
87548
|
},
|
|
87246
87549
|
"access": {
|
|
87247
87550
|
"type": "string",
|
|
@@ -87253,6 +87556,14 @@ var OPERATIONS = [
|
|
|
87253
87556
|
],
|
|
87254
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."
|
|
87255
87558
|
},
|
|
87559
|
+
"isolation": {
|
|
87560
|
+
"type": "string",
|
|
87561
|
+
"enum": [
|
|
87562
|
+
"none",
|
|
87563
|
+
"container"
|
|
87564
|
+
],
|
|
87565
|
+
"description": "The isolation it was made in. It is never run in another."
|
|
87566
|
+
},
|
|
87256
87567
|
"browser": {
|
|
87257
87568
|
"type": "boolean"
|
|
87258
87569
|
},
|
|
@@ -87452,6 +87763,7 @@ var OPERATIONS = [
|
|
|
87452
87763
|
"name",
|
|
87453
87764
|
"state",
|
|
87454
87765
|
"access",
|
|
87766
|
+
"isolation",
|
|
87455
87767
|
"browser",
|
|
87456
87768
|
"workspace",
|
|
87457
87769
|
"startedAt",
|
|
@@ -87472,7 +87784,8 @@ var OPERATIONS = [
|
|
|
87472
87784
|
"running",
|
|
87473
87785
|
"stopped",
|
|
87474
87786
|
"failed"
|
|
87475
|
-
]
|
|
87787
|
+
],
|
|
87788
|
+
"description": "failed: it was to be running and isn't, because its browser or container ended by itself, or because it couldn't be brought back when the service started. Its logs say why. Stop it and start it again."
|
|
87476
87789
|
},
|
|
87477
87790
|
"access": {
|
|
87478
87791
|
"type": "string",
|
|
@@ -87484,6 +87797,14 @@ var OPERATIONS = [
|
|
|
87484
87797
|
],
|
|
87485
87798
|
"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."
|
|
87486
87799
|
},
|
|
87800
|
+
"isolation": {
|
|
87801
|
+
"type": "string",
|
|
87802
|
+
"enum": [
|
|
87803
|
+
"none",
|
|
87804
|
+
"container"
|
|
87805
|
+
],
|
|
87806
|
+
"description": "The isolation it was made in. It is never run in another."
|
|
87807
|
+
},
|
|
87487
87808
|
"browser": {
|
|
87488
87809
|
"type": "boolean"
|
|
87489
87810
|
},
|
|
@@ -87576,6 +87897,7 @@ var OPERATIONS = [
|
|
|
87576
87897
|
"name",
|
|
87577
87898
|
"state",
|
|
87578
87899
|
"access",
|
|
87900
|
+
"isolation",
|
|
87579
87901
|
"browser",
|
|
87580
87902
|
"workspace",
|
|
87581
87903
|
"startedAt",
|
|
@@ -87596,7 +87918,8 @@ var OPERATIONS = [
|
|
|
87596
87918
|
"running",
|
|
87597
87919
|
"stopped",
|
|
87598
87920
|
"failed"
|
|
87599
|
-
]
|
|
87921
|
+
],
|
|
87922
|
+
"description": "failed: it was to be running and isn't, because its browser or container ended by itself, or because it couldn't be brought back when the service started. Its logs say why. Stop it and start it again."
|
|
87600
87923
|
},
|
|
87601
87924
|
"access": {
|
|
87602
87925
|
"type": "string",
|
|
@@ -87608,6 +87931,14 @@ var OPERATIONS = [
|
|
|
87608
87931
|
],
|
|
87609
87932
|
"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."
|
|
87610
87933
|
},
|
|
87934
|
+
"isolation": {
|
|
87935
|
+
"type": "string",
|
|
87936
|
+
"enum": [
|
|
87937
|
+
"none",
|
|
87938
|
+
"container"
|
|
87939
|
+
],
|
|
87940
|
+
"description": "The isolation it was made in. It is never run in another."
|
|
87941
|
+
},
|
|
87611
87942
|
"browser": {
|
|
87612
87943
|
"type": "boolean"
|
|
87613
87944
|
},
|
|
@@ -87705,6 +88036,7 @@ var OPERATIONS = [
|
|
|
87705
88036
|
"name",
|
|
87706
88037
|
"state",
|
|
87707
88038
|
"access",
|
|
88039
|
+
"isolation",
|
|
87708
88040
|
"browser",
|
|
87709
88041
|
"workspace",
|
|
87710
88042
|
"startedAt",
|
|
@@ -87725,7 +88057,8 @@ var OPERATIONS = [
|
|
|
87725
88057
|
"running",
|
|
87726
88058
|
"stopped",
|
|
87727
88059
|
"failed"
|
|
87728
|
-
]
|
|
88060
|
+
],
|
|
88061
|
+
"description": "failed: it was to be running and isn't, because its browser or container ended by itself, or because it couldn't be brought back when the service started. Its logs say why. Stop it and start it again."
|
|
87729
88062
|
},
|
|
87730
88063
|
"access": {
|
|
87731
88064
|
"type": "string",
|
|
@@ -87737,6 +88070,14 @@ var OPERATIONS = [
|
|
|
87737
88070
|
],
|
|
87738
88071
|
"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."
|
|
87739
88072
|
},
|
|
88073
|
+
"isolation": {
|
|
88074
|
+
"type": "string",
|
|
88075
|
+
"enum": [
|
|
88076
|
+
"none",
|
|
88077
|
+
"container"
|
|
88078
|
+
],
|
|
88079
|
+
"description": "The isolation it was made in. It is never run in another."
|
|
88080
|
+
},
|
|
87740
88081
|
"browser": {
|
|
87741
88082
|
"type": "boolean"
|
|
87742
88083
|
},
|
|
@@ -88024,9 +88365,12 @@ async function serve(options) {
|
|
|
88024
88365
|
checkExposure(host, options);
|
|
88025
88366
|
const data = privateFolder(options.data);
|
|
88026
88367
|
const control = digestOf(controlToken(data));
|
|
88368
|
+
const live = await running(data);
|
|
88369
|
+
if (live)
|
|
88370
|
+
throw new Error(`A service is already running with its data in ${data}, at ${live.url}.`);
|
|
88027
88371
|
let base = "";
|
|
88028
|
-
await options.
|
|
88029
|
-
const computers = new Computers(data, () => base, options.
|
|
88372
|
+
await options.runtimes[options.isolation].prepare();
|
|
88373
|
+
const computers = new Computers(data, () => base, options.runtimes, options.isolation);
|
|
88030
88374
|
const handlers = createHandlers(computers);
|
|
88031
88375
|
const routes = OPERATIONS.map((operation) => ({
|
|
88032
88376
|
operation,
|
|
@@ -88114,7 +88458,7 @@ async function serve(options) {
|
|
|
88114
88458
|
});
|
|
88115
88459
|
};
|
|
88116
88460
|
const server = options.tls ? createTlsServer(
|
|
88117
|
-
{ cert:
|
|
88461
|
+
{ cert: readFileSync6(options.tls.cert), key: readFileSync6(options.tls.key) },
|
|
88118
88462
|
listener
|
|
88119
88463
|
) : createServer5(listener);
|
|
88120
88464
|
const allowed = new Set(options.allowOrigins ?? []);
|
|
@@ -88146,11 +88490,12 @@ ${JSON.stringify({ code: status, message })}
|
|
|
88146
88490
|
const bound = server.address();
|
|
88147
88491
|
const scheme = options.tls ? "https" : "http";
|
|
88148
88492
|
base = options.publicUrl?.replace(/\/+$/, "") ?? `${scheme}://${bound.family === "IPv6" ? `[${bound.address}]` : bound.address}:${bound.port}`;
|
|
88493
|
+
const record3 = recordOf(data);
|
|
88494
|
+
rmSync5(record3, { force: true });
|
|
88149
88495
|
await computers.resume();
|
|
88150
|
-
const record3 = join10(data, "server.json");
|
|
88151
88496
|
writePrivate(
|
|
88152
88497
|
record3,
|
|
88153
|
-
JSON.stringify({ url: base, pid: process.pid, isolation: options.
|
|
88498
|
+
JSON.stringify({ url: base, pid: process.pid, isolation: options.isolation })
|
|
88154
88499
|
);
|
|
88155
88500
|
process.stdout.write(`AgentComputer on ${base}, data in ${data}
|
|
88156
88501
|
`);
|
|
@@ -88162,7 +88507,7 @@ ${JSON.stringify({ code: status, message })}
|
|
|
88162
88507
|
process.stderr.write(`${String(error62)}
|
|
88163
88508
|
`);
|
|
88164
88509
|
});
|
|
88165
|
-
|
|
88510
|
+
rmSync5(record3, { force: true });
|
|
88166
88511
|
process.exit(0);
|
|
88167
88512
|
};
|
|
88168
88513
|
for (const signal of ["SIGINT", "SIGTERM"]) process.once(signal, () => void stop());
|
|
@@ -88176,9 +88521,14 @@ var USAGE = `Usage: agentcomputer <command> [--data DIR]
|
|
|
88176
88521
|
[--browser-name NAME] [--browser-path PATH]
|
|
88177
88522
|
run the service; 127.0.0.1:7878 by default, and
|
|
88178
88523
|
anywhere else only with TLS
|
|
88179
|
-
|
|
88524
|
+
up [the options of serve] run the service in the background and return once it
|
|
88525
|
+
answers, printing where; one running is left as it is
|
|
88526
|
+
down stop the service, and its computers with it
|
|
88527
|
+
status where the service is running, if it is
|
|
88528
|
+
info the service's version, the isolation it gives a computer
|
|
88529
|
+
made without saying, and the ones it can make now
|
|
88180
88530
|
create --access none|read|files|terminal [--browser] [--workspace DIR] [--name NAME]
|
|
88181
|
-
[--timeout SECONDS] [--metadata KEY=VALUE]...
|
|
88531
|
+
[--isolation none|container] [--timeout SECONDS] [--metadata KEY=VALUE]...
|
|
88182
88532
|
make a computer; prints its endpoints and token, once
|
|
88183
88533
|
list [--state running|stopped|failed]
|
|
88184
88534
|
the computers
|
|
@@ -88191,6 +88541,8 @@ var USAGE = `Usage: agentcomputer <command> [--data DIR]
|
|
|
88191
88541
|
a one-time ticket to open one live, as a WebSocket
|
|
88192
88542
|
kill <id> stop it and remove what the service made
|
|
88193
88543
|
|
|
88544
|
+
--version prints this program's version, with no service.
|
|
88545
|
+
|
|
88194
88546
|
The data folder is --data, else $AGENTCOMPUTER_DATA, else $XDG_DATA_HOME/agentcomputer, else
|
|
88195
88547
|
~/.local/share/agentcomputer. Commands find the running service there, with its control token.
|
|
88196
88548
|
`;
|
|
@@ -88203,20 +88555,18 @@ function dataFolder(given) {
|
|
|
88203
88555
|
given ?? process.env.AGENTCOMPUTER_DATA ?? (xdg && isAbsolute2(xdg) ? join11(xdg, "agentcomputer") : join11(homedir2(), ".local", "share", "agentcomputer"))
|
|
88204
88556
|
);
|
|
88205
88557
|
}
|
|
88558
|
+
var none = (data) => `No service is running with its data in ${data}. Start one: agentcomputer up`;
|
|
88206
88559
|
function connectionTo(data) {
|
|
88207
|
-
|
|
88208
|
-
|
|
88209
|
-
|
|
88210
|
-
} catch {
|
|
88211
|
-
throw new Error(
|
|
88212
|
-
`No service is running with its data in ${data}. Start one: agentcomputer serve`
|
|
88213
|
-
);
|
|
88214
|
-
}
|
|
88560
|
+
const said = recorded(data);
|
|
88561
|
+
if (!said) throw new Error(none(data));
|
|
88562
|
+
return { apiUrl: said.url, apiKey: said.token };
|
|
88215
88563
|
}
|
|
88216
88564
|
async function main() {
|
|
88217
|
-
const { values, positionals } = parseArgs({
|
|
88565
|
+
const { values, positionals, tokens } = parseArgs({
|
|
88218
88566
|
allowPositionals: true,
|
|
88567
|
+
tokens: true,
|
|
88219
88568
|
options: {
|
|
88569
|
+
version: { type: "boolean" },
|
|
88220
88570
|
data: { type: "string" },
|
|
88221
88571
|
listen: { type: "string" },
|
|
88222
88572
|
isolation: { type: "string" },
|
|
@@ -88247,19 +88597,38 @@ async function main() {
|
|
|
88247
88597
|
}
|
|
88248
88598
|
});
|
|
88249
88599
|
const [command, id, extra] = positionals;
|
|
88600
|
+
if (values.version) return void process.stdout.write(`${VERSION2}
|
|
88601
|
+
`);
|
|
88250
88602
|
if (values.help || !command) return void process.stdout.write(USAGE);
|
|
88251
88603
|
const data = dataFolder(values.data);
|
|
88252
88604
|
const out = (value) => process.stdout.write(`${JSON.stringify(value, null, 2)}
|
|
88253
88605
|
`);
|
|
88606
|
+
if (command === "status") {
|
|
88607
|
+
const found = await running(data);
|
|
88608
|
+
if (!found) throw new Error(none(data));
|
|
88609
|
+
return out(found);
|
|
88610
|
+
}
|
|
88611
|
+
if (command === "up") {
|
|
88612
|
+
const given = process.argv.slice(2);
|
|
88613
|
+
given.splice(tokens.find((token) => token.kind === "positional")?.index, 1);
|
|
88614
|
+
return out(await up(data, process.argv[1], given));
|
|
88615
|
+
}
|
|
88616
|
+
if (command === "down") return down(data);
|
|
88617
|
+
if ((command === "start" || command === "stop") && !id) {
|
|
88618
|
+
const service = command === "start" ? "up" : "down";
|
|
88619
|
+
throw new UsageError(
|
|
88620
|
+
`${command} takes a computer's id. For the service itself: agentcomputer ${service}`
|
|
88621
|
+
);
|
|
88622
|
+
}
|
|
88254
88623
|
const idOf = () => {
|
|
88255
88624
|
if (!id) throw new UsageError(`${command} takes a computer's id.`);
|
|
88256
88625
|
return id;
|
|
88257
88626
|
};
|
|
88258
88627
|
const milliseconds = (seconds2) => Number(seconds2) * 1e3;
|
|
88628
|
+
const { isolation } = values;
|
|
88629
|
+
if (isolation !== void 0 && isolation !== "none" && isolation !== "container")
|
|
88630
|
+
throw new UsageError("--isolation is none or container.");
|
|
88259
88631
|
if (command === "serve") {
|
|
88260
|
-
const isolation = values.isolation ?? "none";
|
|
88261
|
-
if (isolation !== "none" && isolation !== "container")
|
|
88262
|
-
throw new UsageError("--isolation is none or container.");
|
|
88263
88632
|
const { "tls-cert": cert, "tls-key": key } = values;
|
|
88264
88633
|
if (Boolean(cert) !== Boolean(key))
|
|
88265
88634
|
throw new UsageError("--tls-cert and --tls-key go together.");
|
|
@@ -88270,10 +88639,11 @@ async function main() {
|
|
|
88270
88639
|
tlsProxy: values["tls-proxy"],
|
|
88271
88640
|
publicUrl: values["public-url"],
|
|
88272
88641
|
allowOrigins: values["allow-origin"],
|
|
88273
|
-
|
|
88274
|
-
|
|
88275
|
-
path: values["browser-path"]
|
|
88276
|
-
|
|
88642
|
+
isolation: isolation ?? "none",
|
|
88643
|
+
runtimes: {
|
|
88644
|
+
none: new ProcessRuntime({ browser: values["browser-name"], path: values["browser-path"] }),
|
|
88645
|
+
container: new ContainerRuntime(values.image ?? `${IMAGE}:${VERSION2}`)
|
|
88646
|
+
}
|
|
88277
88647
|
});
|
|
88278
88648
|
}
|
|
88279
88649
|
if (command === "browser-host")
|
|
@@ -88303,6 +88673,7 @@ async function main() {
|
|
|
88303
88673
|
...connection,
|
|
88304
88674
|
access: values.access,
|
|
88305
88675
|
...values.browser ? { browser: true } : {},
|
|
88676
|
+
...isolation ? { isolation } : {},
|
|
88306
88677
|
...values.workspace ? { workspace: resolve5(values.workspace) } : {},
|
|
88307
88678
|
...values.name ? { name: values.name } : {},
|
|
88308
88679
|
...values.timeout ? { timeoutMs: milliseconds(values.timeout) } : {},
|