@agentorg/computer 0.0.13 → 0.0.15
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 -18
- package/dist/THIRD_PARTY_LICENSES.txt +1 -1
- package/dist/agentcomputer.js +477 -169
- 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";
|
|
@@ -4829,10 +4828,10 @@ function $constructor(name, initializer4, proto, params) {
|
|
|
4829
4828
|
if (initialized) {
|
|
4830
4829
|
const own3 = Object.getPrototypeOf(inst);
|
|
4831
4830
|
const ctorProto = inst._zod.constr.prototype;
|
|
4832
|
-
let
|
|
4833
|
-
while (
|
|
4834
|
-
|
|
4835
|
-
const target =
|
|
4831
|
+
let up2 = own3;
|
|
4832
|
+
while (up2 && up2 !== ctorProto)
|
|
4833
|
+
up2 = Object.getPrototypeOf(up2);
|
|
4834
|
+
const target = up2 ?? own3;
|
|
4836
4835
|
if (!initialized.has(target)) {
|
|
4837
4836
|
initialized.add(target);
|
|
4838
4837
|
members(target, protoMembers);
|
|
@@ -18888,14 +18887,14 @@ var require_validate = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
|
18888
18887
|
} else {
|
|
18889
18888
|
const matches = RELATIVE_JSON_POINTER.exec($data);
|
|
18890
18889
|
if (!matches) throw new Error(`Invalid JSON-pointer: ${$data}`);
|
|
18891
|
-
const
|
|
18890
|
+
const up2 = +matches[1];
|
|
18892
18891
|
jsonPointer = matches[2];
|
|
18893
18892
|
if (jsonPointer === "#") {
|
|
18894
|
-
if (
|
|
18895
|
-
return dataPathArr[dataLevel -
|
|
18893
|
+
if (up2 >= dataLevel) throw new Error(errorMsg("property/index", up2));
|
|
18894
|
+
return dataPathArr[dataLevel - up2];
|
|
18896
18895
|
}
|
|
18897
|
-
if (
|
|
18898
|
-
data = dataNames[dataLevel -
|
|
18896
|
+
if (up2 > dataLevel) throw new Error(errorMsg("data", up2));
|
|
18897
|
+
data = dataNames[dataLevel - up2];
|
|
18899
18898
|
if (!jsonPointer) return data;
|
|
18900
18899
|
}
|
|
18901
18900
|
let expr = data;
|
|
@@ -18905,8 +18904,8 @@ var require_validate = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
|
18905
18904
|
expr = (0, codegen_1._)`${expr} && ${data}`;
|
|
18906
18905
|
}
|
|
18907
18906
|
return expr;
|
|
18908
|
-
function errorMsg(pointerType,
|
|
18909
|
-
return `Cannot access ${pointerType} ${
|
|
18907
|
+
function errorMsg(pointerType, up2) {
|
|
18908
|
+
return `Cannot access ${pointerType} ${up2} levels up, current level is ${dataLevel}`;
|
|
18910
18909
|
}
|
|
18911
18910
|
}
|
|
18912
18911
|
exports.getData = getData;
|
|
@@ -27958,9 +27957,13 @@ var StreamableHTTPClientTransport = class {
|
|
|
27958
27957
|
|
|
27959
27958
|
// src/browser.ts
|
|
27960
27959
|
var RUNS_CODE = "browser_run_code_unsafe";
|
|
27961
|
-
var
|
|
27960
|
+
var SCREENSHOT = "browser_take_screenshot";
|
|
27961
|
+
var MIB = 1024 * 1024;
|
|
27962
|
+
var MAX_BODY = 8 * MIB;
|
|
27963
|
+
var MAX_PICTURE = 3 * MIB;
|
|
27962
27964
|
var START_MS = 3e4;
|
|
27963
27965
|
var STOP_MS = 2e3;
|
|
27966
|
+
var ONE_REQUEST = "A body is one JSON-RPC request, as a JSON object: send each request in its own POST.";
|
|
27964
27967
|
var FORWARDED = [
|
|
27965
27968
|
"accept",
|
|
27966
27969
|
"content-type",
|
|
@@ -27980,15 +27983,27 @@ function sendAnswer(upstream, response) {
|
|
|
27980
27983
|
});
|
|
27981
27984
|
}
|
|
27982
27985
|
var self = fileURLToPath(import.meta.url);
|
|
27983
|
-
function
|
|
27984
|
-
const
|
|
27986
|
+
function changed(text3, change) {
|
|
27987
|
+
const one = (json4) => {
|
|
27985
27988
|
const message = JSON.parse(json4);
|
|
27986
|
-
|
|
27987
|
-
message.result.tools = message.result.tools.filter((tool) => !hidden.has(tool.name));
|
|
27989
|
+
change(message);
|
|
27988
27990
|
return JSON.stringify(message);
|
|
27989
27991
|
};
|
|
27990
|
-
if (text3.trimStart().startsWith("{")) return
|
|
27991
|
-
return text3.replace(/^data: (\{
|
|
27992
|
+
if (text3.trimStart().startsWith("{")) return one(text3);
|
|
27993
|
+
return text3.replace(/^data: (\{[^\n\r]*\})$/gm, (_, json4) => `data: ${one(json4)}`);
|
|
27994
|
+
}
|
|
27995
|
+
var withoutHidden = (hidden) => (message) => {
|
|
27996
|
+
if (message.result?.tools)
|
|
27997
|
+
message.result.tools = message.result.tools.filter((tool) => !hidden.has(tool.name));
|
|
27998
|
+
};
|
|
27999
|
+
function withoutLarge(message) {
|
|
28000
|
+
const sizes = (message.result?.content ?? []).map(
|
|
28001
|
+
(block) => block.type === "image" ? Buffer.byteLength(block.data ?? "", "base64") : 0
|
|
28002
|
+
);
|
|
28003
|
+
const size = Math.max(0, ...sizes);
|
|
28004
|
+
if (size <= MAX_PICTURE) return;
|
|
28005
|
+
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.`;
|
|
28006
|
+
message.result = { isError: true, content: [{ type: "text", text: text3 }] };
|
|
27992
28007
|
}
|
|
27993
28008
|
async function end(child) {
|
|
27994
28009
|
if (child.exitCode !== null || child.signalCode !== null) return;
|
|
@@ -28098,18 +28113,29 @@ var Browser = class {
|
|
|
28098
28113
|
chunks.push(chunk);
|
|
28099
28114
|
}
|
|
28100
28115
|
const body = Buffer.concat(chunks);
|
|
28101
|
-
let message
|
|
28116
|
+
let message;
|
|
28102
28117
|
try {
|
|
28103
|
-
|
|
28118
|
+
const read = body.length > 0 ? JSON.parse(body.toString("utf8")) : void 0;
|
|
28119
|
+
if (typeof read === "object" && read !== null && !Array.isArray(read)) message = read;
|
|
28104
28120
|
} catch {
|
|
28105
28121
|
}
|
|
28106
|
-
|
|
28122
|
+
if (body.length > 0 && !message) {
|
|
28123
|
+
response.writeHead(400, { "Content-Type": "application/json" });
|
|
28124
|
+
return void response.end(
|
|
28125
|
+
JSON.stringify({
|
|
28126
|
+
jsonrpc: "2.0",
|
|
28127
|
+
id: null,
|
|
28128
|
+
error: { code: -32600, message: ONE_REQUEST }
|
|
28129
|
+
})
|
|
28130
|
+
);
|
|
28131
|
+
}
|
|
28132
|
+
const tool = message?.method === "tools/call" ? String(message.params?.name) : void 0;
|
|
28107
28133
|
if (tool && this.#hidden.has(tool)) {
|
|
28108
28134
|
response.writeHead(200, { "Content-Type": "application/json" });
|
|
28109
28135
|
return void response.end(
|
|
28110
28136
|
JSON.stringify({
|
|
28111
28137
|
jsonrpc: "2.0",
|
|
28112
|
-
id: message
|
|
28138
|
+
id: message?.id ?? null,
|
|
28113
28139
|
error: { code: -32602, message: `Unknown tool: ${tool}` }
|
|
28114
28140
|
})
|
|
28115
28141
|
);
|
|
@@ -28134,16 +28160,18 @@ var Browser = class {
|
|
|
28134
28160
|
}).then((answer) => answer.text()).catch(() => {
|
|
28135
28161
|
});
|
|
28136
28162
|
if (tool === "browser_file_upload") this.gate?.agentAnsweredChooser();
|
|
28163
|
+
if (message) headers["content-type"] = "application/json";
|
|
28137
28164
|
const upstream = await fetch(`${origin}/mcp`, {
|
|
28138
28165
|
method: request.method,
|
|
28139
28166
|
headers,
|
|
28140
|
-
...
|
|
28167
|
+
...message ? { body: JSON.stringify(message) } : {}
|
|
28141
28168
|
});
|
|
28142
|
-
|
|
28143
|
-
|
|
28144
|
-
|
|
28145
|
-
|
|
28146
|
-
sendAnswer(upstream, response);
|
|
28169
|
+
let change;
|
|
28170
|
+
if (message?.method === "tools/list" && this.#hidden.size > 0)
|
|
28171
|
+
change = withoutHidden(this.#hidden);
|
|
28172
|
+
else if (tool === SCREENSHOT) change = withoutLarge;
|
|
28173
|
+
if (!change) return sendAnswer(upstream, response);
|
|
28174
|
+
sendAnswer(new Response(changed(await upstream.text(), change), upstream), response);
|
|
28147
28175
|
}
|
|
28148
28176
|
async stop() {
|
|
28149
28177
|
this.#stopping = true;
|
|
@@ -30609,10 +30637,10 @@ function $constructor2(name, initializer32, proto, params) {
|
|
|
30609
30637
|
if (initialized) {
|
|
30610
30638
|
const own22 = Object.getPrototypeOf(inst);
|
|
30611
30639
|
const ctorProto = inst._zod.constr.prototype;
|
|
30612
|
-
let
|
|
30613
|
-
while (
|
|
30614
|
-
|
|
30615
|
-
const target =
|
|
30640
|
+
let up2 = own22;
|
|
30641
|
+
while (up2 && up2 !== ctorProto)
|
|
30642
|
+
up2 = Object.getPrototypeOf(up2);
|
|
30643
|
+
const target = up2 ?? own22;
|
|
30616
30644
|
if (!initialized.has(target)) {
|
|
30617
30645
|
initialized.add(target);
|
|
30618
30646
|
members2(target, protoMembers);
|
|
@@ -48469,21 +48497,21 @@ function visit(schema, fnOrHandlers) {
|
|
|
48469
48497
|
case "object": {
|
|
48470
48498
|
const oldShape = def.shape;
|
|
48471
48499
|
const keys = Object.keys(oldShape);
|
|
48472
|
-
let
|
|
48500
|
+
let changed22 = false;
|
|
48473
48501
|
const newShape = {};
|
|
48474
48502
|
for (const k of keys) {
|
|
48475
48503
|
const mapped = run3(oldShape[k]);
|
|
48476
48504
|
if (mapped !== oldShape[k])
|
|
48477
|
-
|
|
48505
|
+
changed22 = true;
|
|
48478
48506
|
newShape[k] = mapped;
|
|
48479
48507
|
}
|
|
48480
48508
|
let newCatchall = def.catchall;
|
|
48481
48509
|
if (def.catchall) {
|
|
48482
48510
|
newCatchall = run3(def.catchall);
|
|
48483
48511
|
if (newCatchall !== def.catchall)
|
|
48484
|
-
|
|
48512
|
+
changed22 = true;
|
|
48485
48513
|
}
|
|
48486
|
-
return
|
|
48514
|
+
return changed22 ? clone2(s, { ...def, shape: newShape, catchall: newCatchall }) : s;
|
|
48487
48515
|
}
|
|
48488
48516
|
case "array": {
|
|
48489
48517
|
const mapped = run3(def.element);
|
|
@@ -48491,21 +48519,21 @@ function visit(schema, fnOrHandlers) {
|
|
|
48491
48519
|
}
|
|
48492
48520
|
case "tuple": {
|
|
48493
48521
|
const oldItems = def.items;
|
|
48494
|
-
let
|
|
48522
|
+
let changed22 = false;
|
|
48495
48523
|
const newItems = [];
|
|
48496
48524
|
for (const item of oldItems) {
|
|
48497
48525
|
const mapped = run3(item);
|
|
48498
48526
|
if (mapped !== item)
|
|
48499
|
-
|
|
48527
|
+
changed22 = true;
|
|
48500
48528
|
newItems.push(mapped);
|
|
48501
48529
|
}
|
|
48502
48530
|
let newRest = def.rest;
|
|
48503
48531
|
if (def.rest) {
|
|
48504
48532
|
newRest = run3(def.rest);
|
|
48505
48533
|
if (newRest !== def.rest)
|
|
48506
|
-
|
|
48534
|
+
changed22 = true;
|
|
48507
48535
|
}
|
|
48508
|
-
return
|
|
48536
|
+
return changed22 ? clone2(s, { ...def, items: newItems, rest: newRest }) : s;
|
|
48509
48537
|
}
|
|
48510
48538
|
case "record":
|
|
48511
48539
|
case "map": {
|
|
@@ -48519,15 +48547,15 @@ function visit(schema, fnOrHandlers) {
|
|
|
48519
48547
|
}
|
|
48520
48548
|
case "union": {
|
|
48521
48549
|
const oldOptions = def.options;
|
|
48522
|
-
let
|
|
48550
|
+
let changed22 = false;
|
|
48523
48551
|
const newOptions = [];
|
|
48524
48552
|
for (const opt of oldOptions) {
|
|
48525
48553
|
const mapped = run3(opt);
|
|
48526
48554
|
if (mapped !== opt)
|
|
48527
|
-
|
|
48555
|
+
changed22 = true;
|
|
48528
48556
|
newOptions.push(mapped);
|
|
48529
48557
|
}
|
|
48530
|
-
return
|
|
48558
|
+
return changed22 ? clone2(s, { ...def, options: newOptions }) : s;
|
|
48531
48559
|
}
|
|
48532
48560
|
case "intersection": {
|
|
48533
48561
|
const newLeft = run3(def.left);
|
|
@@ -56608,14 +56636,14 @@ var require_validate2 = /* @__PURE__ */ __commonJSMin2(((exports) => {
|
|
|
56608
56636
|
} else {
|
|
56609
56637
|
const matches = RELATIVE_JSON_POINTER.exec($data);
|
|
56610
56638
|
if (!matches) throw new Error(`Invalid JSON-pointer: ${$data}`);
|
|
56611
|
-
const
|
|
56639
|
+
const up2 = +matches[1];
|
|
56612
56640
|
jsonPointer = matches[2];
|
|
56613
56641
|
if (jsonPointer === "#") {
|
|
56614
|
-
if (
|
|
56615
|
-
return dataPathArr[dataLevel -
|
|
56642
|
+
if (up2 >= dataLevel) throw new Error(errorMsg("property/index", up2));
|
|
56643
|
+
return dataPathArr[dataLevel - up2];
|
|
56616
56644
|
}
|
|
56617
|
-
if (
|
|
56618
|
-
data = dataNames[dataLevel -
|
|
56645
|
+
if (up2 > dataLevel) throw new Error(errorMsg("data", up2));
|
|
56646
|
+
data = dataNames[dataLevel - up2];
|
|
56619
56647
|
if (!jsonPointer) return data;
|
|
56620
56648
|
}
|
|
56621
56649
|
let expr = data;
|
|
@@ -56625,8 +56653,8 @@ var require_validate2 = /* @__PURE__ */ __commonJSMin2(((exports) => {
|
|
|
56625
56653
|
expr = (0, codegen_1._)`${expr} && ${data}`;
|
|
56626
56654
|
}
|
|
56627
56655
|
return expr;
|
|
56628
|
-
function errorMsg(pointerType,
|
|
56629
|
-
return `Cannot access ${pointerType} ${
|
|
56656
|
+
function errorMsg(pointerType, up2) {
|
|
56657
|
+
return `Cannot access ${pointerType} ${up2} levels up, current level is ${dataLevel}`;
|
|
56630
56658
|
}
|
|
56631
56659
|
}
|
|
56632
56660
|
exports.getData = getData;
|
|
@@ -65891,6 +65919,7 @@ var Sessions = class {
|
|
|
65891
65919
|
this.#room();
|
|
65892
65920
|
const started = Date.now();
|
|
65893
65921
|
const session = this.#start(input2.cmd, input2.workdir ?? this.#root);
|
|
65922
|
+
if (input2.group) session.group = input2.group;
|
|
65894
65923
|
const wait = clamp(input2.yield_time_ms ?? DEFAULT_YIELD_MS, MIN_YIELD_MS, MAX_YIELD_MS);
|
|
65895
65924
|
return this.#after(session, wait, started, input2.max_output_tokens);
|
|
65896
65925
|
}
|
|
@@ -65975,26 +66004,37 @@ var Sessions = class {
|
|
|
65975
66004
|
if (!session) throw new ExecRefused(`No terminal ${id.slice(0, 64)} is here.`);
|
|
65976
66005
|
await this.#end([session], "SIGINT");
|
|
65977
66006
|
}
|
|
66007
|
+
/**
|
|
66008
|
+
* End the terminals still running of one group, as `stop` ends one, and say how many. A shell a
|
|
66009
|
+
* person opened is in no group.
|
|
66010
|
+
*/
|
|
66011
|
+
async stopGroup(group) {
|
|
66012
|
+
const running2 = [...this.#sessions.values()].filter(
|
|
66013
|
+
(session) => session.group === group && session.exitCode === void 0
|
|
66014
|
+
);
|
|
66015
|
+
await this.#end(running2, "SIGINT");
|
|
66016
|
+
return running2.length;
|
|
66017
|
+
}
|
|
65978
66018
|
/** Stop every terminal: a polite signal, then a certain one. */
|
|
65979
66019
|
async stopAll() {
|
|
65980
66020
|
await this.#end([...this.#sessions.values()], "SIGTERM");
|
|
65981
66021
|
}
|
|
65982
66022
|
async #end(sessions, polite) {
|
|
65983
|
-
const
|
|
65984
|
-
for (const session of
|
|
66023
|
+
const running2 = sessions.filter((s) => s.exitCode === void 0);
|
|
66024
|
+
for (const session of running2) session.signal(session.tty ? "SIGHUP" : polite);
|
|
65985
66025
|
let timer;
|
|
65986
66026
|
await Promise.race([
|
|
65987
|
-
Promise.all(
|
|
66027
|
+
Promise.all(running2.map((s) => s.ended)),
|
|
65988
66028
|
new Promise((done) => {
|
|
65989
66029
|
timer = setTimeout(done, KILL_AFTER_MS);
|
|
65990
66030
|
})
|
|
65991
66031
|
]);
|
|
65992
66032
|
clearTimeout(timer);
|
|
65993
|
-
for (const session of
|
|
66033
|
+
for (const session of running2) if (session.exitCode === void 0) session.signal("SIGKILL");
|
|
65994
66034
|
}
|
|
65995
66035
|
#room() {
|
|
65996
|
-
const
|
|
65997
|
-
if (
|
|
66036
|
+
const running2 = [...this.#sessions.values()].filter((s) => s.exitCode === void 0);
|
|
66037
|
+
if (running2.length >= MAX_RUNNING)
|
|
65998
66038
|
throw new ExecRefused(`${MAX_RUNNING} commands are running. Wait for one, or stop one.`);
|
|
65999
66039
|
}
|
|
66000
66040
|
#running(id) {
|
|
@@ -66366,7 +66406,7 @@ function searched(job) {
|
|
|
66366
66406
|
const more = out.length > limit;
|
|
66367
66407
|
return (more ? [...out.slice(0, limit), `\u2026 more than ${limit} matches`] : out).join("\n");
|
|
66368
66408
|
}
|
|
66369
|
-
function
|
|
66409
|
+
function changed2(job) {
|
|
66370
66410
|
const changes = [];
|
|
66371
66411
|
let more = false;
|
|
66372
66412
|
const newest = () => {
|
|
@@ -66419,7 +66459,7 @@ function changedSince(root, since) {
|
|
|
66419
66459
|
}
|
|
66420
66460
|
if (!isMainThread && workerData?.agentexecSearch) {
|
|
66421
66461
|
const job = workerData;
|
|
66422
|
-
parentPort?.postMessage("since" in job ?
|
|
66462
|
+
parentPort?.postMessage("since" in job ? changed2(job) : searched(job));
|
|
66423
66463
|
}
|
|
66424
66464
|
var MAX_READ_BYTES = 1024 * 1024;
|
|
66425
66465
|
function createHostFiles(root, level) {
|
|
@@ -66572,6 +66612,14 @@ var Answered = {
|
|
|
66572
66612
|
session_id: external_exports.string().nullable().describe("For write_stdin, while it is still running."),
|
|
66573
66613
|
text: external_exports.string().describe("What it wrote since the last call, cut in the middle if long.")
|
|
66574
66614
|
};
|
|
66615
|
+
var GROUP = "agentexec/group";
|
|
66616
|
+
var STOP_GROUP = "agentexec/group/stop";
|
|
66617
|
+
var Group = external_exports.string().min(1).max(200);
|
|
66618
|
+
var groupOf = (meta3) => {
|
|
66619
|
+
const word = Group.optional().safeParse(meta3?.[GROUP]);
|
|
66620
|
+
if (!word.success) throw new ExecRefused(`${GROUP} in _meta is a string of 1 to 200 characters.`);
|
|
66621
|
+
return word.data;
|
|
66622
|
+
};
|
|
66575
66623
|
var YieldTime = external_exports.number().int().positive().optional();
|
|
66576
66624
|
var MaxTokens = external_exports.number().int().positive().optional();
|
|
66577
66625
|
var READ_ONLY = { readOnlyHint: true };
|
|
@@ -66600,6 +66648,11 @@ function createServer2(root, sessions, version22, level, extraTools = []) {
|
|
|
66600
66648
|
}
|
|
66601
66649
|
);
|
|
66602
66650
|
if (level === "terminal") {
|
|
66651
|
+
server.server.setRequestHandler(
|
|
66652
|
+
STOP_GROUP,
|
|
66653
|
+
{ params: external_exports.object({ group: Group }), result: external_exports.object({ stopped: external_exports.number() }) },
|
|
66654
|
+
async ({ group }) => ({ stopped: await sessions.stopGroup(group) })
|
|
66655
|
+
);
|
|
66603
66656
|
server.registerTool(
|
|
66604
66657
|
"exec_command",
|
|
66605
66658
|
{
|
|
@@ -66613,7 +66666,7 @@ function createServer2(root, sessions, version22, level, extraTools = []) {
|
|
|
66613
66666
|
outputSchema: Answered,
|
|
66614
66667
|
annotations: { destructiveHint: true }
|
|
66615
66668
|
},
|
|
66616
|
-
(input2) => answering(() => sessions.exec(input2))
|
|
66669
|
+
(input2, ctx) => answering(async () => sessions.exec({ ...input2, group: groupOf(ctx.mcpReq._meta) }))
|
|
66617
66670
|
);
|
|
66618
66671
|
server.registerTool(
|
|
66619
66672
|
"write_stdin",
|
|
@@ -66716,7 +66769,7 @@ function createServer2(root, sessions, version22, level, extraTools = []) {
|
|
|
66716
66769
|
);
|
|
66717
66770
|
return server;
|
|
66718
66771
|
}
|
|
66719
|
-
var VERSION = "0.0.
|
|
66772
|
+
var VERSION = "0.0.8";
|
|
66720
66773
|
function createAgentExec(options) {
|
|
66721
66774
|
const root = realpathSync(resolve3(options.workspace));
|
|
66722
66775
|
const level = options.level ?? "read";
|
|
@@ -67021,8 +67074,8 @@ var BrowserLive = class {
|
|
|
67021
67074
|
}
|
|
67022
67075
|
/** The cast of a page, started if no socket was shown it yet. */
|
|
67023
67076
|
#cast(page) {
|
|
67024
|
-
const
|
|
67025
|
-
if (
|
|
67077
|
+
const running2 = this.#casts.get(page);
|
|
67078
|
+
if (running2) return running2;
|
|
67026
67079
|
const onFrame = (frame) => {
|
|
67027
67080
|
const watching = this.#shownTo(page);
|
|
67028
67081
|
if (frame.width !== cast.size.width || frame.height !== cast.size.height) {
|
|
@@ -67735,7 +67788,6 @@ var loadPty = () => {
|
|
|
67735
67788
|
return pty;
|
|
67736
67789
|
};
|
|
67737
67790
|
var ProcessRuntime = class {
|
|
67738
|
-
isolation = "none";
|
|
67739
67791
|
#config;
|
|
67740
67792
|
#contained;
|
|
67741
67793
|
/** `contained` is for inside a container, where the computer and its load are its own. */
|
|
@@ -67743,6 +67795,9 @@ var ProcessRuntime = class {
|
|
|
67743
67795
|
this.#config = config3;
|
|
67744
67796
|
this.#contained = contained;
|
|
67745
67797
|
}
|
|
67798
|
+
async available() {
|
|
67799
|
+
return true;
|
|
67800
|
+
}
|
|
67746
67801
|
async prepare() {
|
|
67747
67802
|
}
|
|
67748
67803
|
async stopLeftover() {
|
|
@@ -67837,7 +67892,7 @@ async function serveInside(options) {
|
|
|
67837
67892
|
if (!token) throw new Error(`${INNER_TOKEN_VARIABLE} is not set.`);
|
|
67838
67893
|
delete process.env[INNER_TOKEN_VARIABLE];
|
|
67839
67894
|
const digest = digestOf(token);
|
|
67840
|
-
const
|
|
67895
|
+
const running2 = new ProcessRuntime({ path: BROWSER, sandbox: false }, true).start(
|
|
67841
67896
|
{
|
|
67842
67897
|
id: "inside",
|
|
67843
67898
|
...options,
|
|
@@ -67864,13 +67919,13 @@ async function serveInside(options) {
|
|
|
67864
67919
|
if (!given || !sameToken(given, digest)) return void response.writeHead(401).end();
|
|
67865
67920
|
const { pathname } = new URL(request.url ?? "/", "http://localhost");
|
|
67866
67921
|
if (pathname === "/ready") {
|
|
67867
|
-
const { browserTools, surfaces } = await
|
|
67922
|
+
const { browserTools, surfaces } = await running2;
|
|
67868
67923
|
response.writeHead(200, { "Content-Type": "application/json" });
|
|
67869
67924
|
return void response.end(JSON.stringify({ browserTools, surfaces }));
|
|
67870
67925
|
}
|
|
67871
67926
|
const endpoint = /^\/mcp\/(computer|browser)$/.exec(pathname)?.[1];
|
|
67872
67927
|
if (!endpoint) return void response.writeHead(404).end();
|
|
67873
|
-
return (await
|
|
67928
|
+
return (await running2).handle(endpoint, request, response);
|
|
67874
67929
|
};
|
|
67875
67930
|
const server = createServer3((request, response) => {
|
|
67876
67931
|
route(request, response).catch((error62) => {
|
|
@@ -67886,14 +67941,14 @@ async function serveInside(options) {
|
|
|
67886
67941
|
const mode = url3.searchParams.get("mode");
|
|
67887
67942
|
if (!given || !sameToken(given, digest) || !surface || mode !== "watch" && mode !== "operate")
|
|
67888
67943
|
return void socket.end("HTTP/1.1 401 Unauthorized\r\nConnection: close\r\n\r\n");
|
|
67889
|
-
|
|
67944
|
+
running2.then((computer) => computer.openLive(surface, mode, request, socket, head)).catch(() => socket.destroy());
|
|
67890
67945
|
});
|
|
67891
67946
|
server.listen(INNER_PORT, "0.0.0.0");
|
|
67892
67947
|
for (const signal of ["SIGINT", "SIGTERM"])
|
|
67893
67948
|
process.once(signal, () => {
|
|
67894
|
-
void
|
|
67949
|
+
void running2.then((computer) => computer.stop()).finally(() => process.exit(0));
|
|
67895
67950
|
});
|
|
67896
|
-
await
|
|
67951
|
+
await running2;
|
|
67897
67952
|
}
|
|
67898
67953
|
|
|
67899
67954
|
// src/container.ts
|
|
@@ -67928,17 +67983,30 @@ var UPGRADE_HEADERS = [
|
|
|
67928
67983
|
var nameOf = (id) => `agentcomputer-${id}`;
|
|
67929
67984
|
var sleep5 = (ms) => new Promise((done) => setTimeout(done, ms));
|
|
67930
67985
|
var ContainerRuntime = class {
|
|
67931
|
-
isolation = "container";
|
|
67932
67986
|
#image;
|
|
67933
67987
|
constructor(image) {
|
|
67934
67988
|
this.#image = image;
|
|
67935
67989
|
}
|
|
67936
|
-
|
|
67990
|
+
/** Whether Docker answers, asked for no longer than this. */
|
|
67991
|
+
async #answers(ms) {
|
|
67937
67992
|
try {
|
|
67938
|
-
await docker
|
|
67939
|
-
|
|
67940
|
-
|
|
67993
|
+
await run2("docker", ["version", "--format", "{{.Server.Version}}"], { timeout: ms });
|
|
67994
|
+
return true;
|
|
67995
|
+
} catch {
|
|
67996
|
+
return false;
|
|
67941
67997
|
}
|
|
67998
|
+
}
|
|
67999
|
+
// Asked each time the service says what it can make, so it doesn't wait on a Docker that hangs.
|
|
68000
|
+
available() {
|
|
68001
|
+
return this.#answers(1e3);
|
|
68002
|
+
}
|
|
68003
|
+
/** Docker, or the refusal of what can't be done without it. */
|
|
68004
|
+
async #docker() {
|
|
68005
|
+
if (!await this.#answers(1e4))
|
|
68006
|
+
throw new ApiError(409, "A container needs Docker, which isn't answering on this machine.");
|
|
68007
|
+
}
|
|
68008
|
+
async prepare() {
|
|
68009
|
+
await this.#docker();
|
|
67942
68010
|
try {
|
|
67943
68011
|
await docker(["image", "inspect", "--format", "{{.Id}}", this.#image]);
|
|
67944
68012
|
} catch {
|
|
@@ -68079,11 +68147,14 @@ var ContainerRuntime = class {
|
|
|
68079
68147
|
}
|
|
68080
68148
|
throw new Error("Its container didn't start. Its logs say why.");
|
|
68081
68149
|
}
|
|
68150
|
+
// Neither is said to be done where Docker doesn't answer: the container would be left as it is.
|
|
68082
68151
|
async stopLeftover(id) {
|
|
68152
|
+
await this.#docker();
|
|
68083
68153
|
await docker(["stop", "--time", STOP_SECONDS, nameOf(id)]).catch(() => {
|
|
68084
68154
|
});
|
|
68085
68155
|
}
|
|
68086
68156
|
async remove(id) {
|
|
68157
|
+
await this.#docker();
|
|
68087
68158
|
await docker(["rm", "--force", nameOf(id)]).catch(() => {
|
|
68088
68159
|
});
|
|
68089
68160
|
}
|
|
@@ -68132,10 +68203,14 @@ async function forward(url3, authorization, request, response) {
|
|
|
68132
68203
|
}
|
|
68133
68204
|
|
|
68134
68205
|
// src/handlers.ts
|
|
68135
|
-
var VERSION2 = "0.0.
|
|
68206
|
+
var VERSION2 = "0.0.15";
|
|
68136
68207
|
function createHandlers(computers) {
|
|
68137
68208
|
return {
|
|
68138
|
-
getInfo: async () => ({
|
|
68209
|
+
getInfo: async () => ({
|
|
68210
|
+
version: VERSION2,
|
|
68211
|
+
isolation: computers.isolation,
|
|
68212
|
+
isolationsAvailable: await computers.available()
|
|
68213
|
+
}),
|
|
68139
68214
|
listComputers: async ({ state }) => computers.list(state),
|
|
68140
68215
|
createComputer: (input2) => computers.create(input2),
|
|
68141
68216
|
getComputer: async ({ computerID }) => computers.get(computerID),
|
|
@@ -68154,6 +68229,81 @@ function createHandlers(computers) {
|
|
|
68154
68229
|
};
|
|
68155
68230
|
}
|
|
68156
68231
|
|
|
68232
|
+
// src/running.ts
|
|
68233
|
+
import { spawn as spawn4 } from "node:child_process";
|
|
68234
|
+
import { closeSync, openSync, readFileSync as readFileSync4, statSync as statSync3, writeFileSync as writeFileSync2 } from "node:fs";
|
|
68235
|
+
import { join as join9 } from "node:path";
|
|
68236
|
+
var MAX_LOG = 1024 * 1024;
|
|
68237
|
+
var STOP_MS2 = 3e4;
|
|
68238
|
+
var sleep6 = (ms) => new Promise((done) => setTimeout(done, ms));
|
|
68239
|
+
var recordOf = (data) => join9(data, "server.json");
|
|
68240
|
+
function recorded(data) {
|
|
68241
|
+
try {
|
|
68242
|
+
const { url: url3, pid } = JSON.parse(readFileSync4(recordOf(data), "utf8"));
|
|
68243
|
+
return { url: url3, pid, token: readFileSync4(join9(data, "control-token"), "utf8").trim() };
|
|
68244
|
+
} catch {
|
|
68245
|
+
return void 0;
|
|
68246
|
+
}
|
|
68247
|
+
}
|
|
68248
|
+
async function running(data, said = recorded(data)) {
|
|
68249
|
+
if (!said) return void 0;
|
|
68250
|
+
try {
|
|
68251
|
+
const answer = await fetch(`${said.url}/v1/info`, {
|
|
68252
|
+
headers: { Authorization: `Bearer ${said.token}` },
|
|
68253
|
+
signal: AbortSignal.timeout(3e3)
|
|
68254
|
+
});
|
|
68255
|
+
if (!answer.ok) return void 0;
|
|
68256
|
+
const { version: version3, isolation } = await answer.json();
|
|
68257
|
+
return { url: said.url, pid: said.pid, version: version3, isolation };
|
|
68258
|
+
} catch {
|
|
68259
|
+
return void 0;
|
|
68260
|
+
}
|
|
68261
|
+
}
|
|
68262
|
+
async function up(data, program, args) {
|
|
68263
|
+
const already = await running(data);
|
|
68264
|
+
if (already) return already;
|
|
68265
|
+
const log = join9(privateFolder(data), "service.log");
|
|
68266
|
+
try {
|
|
68267
|
+
const before = readFileSync4(log);
|
|
68268
|
+
if (before.length > MAX_LOG) writeFileSync2(log, before.subarray(before.length - MAX_LOG));
|
|
68269
|
+
} catch {
|
|
68270
|
+
}
|
|
68271
|
+
const from = statSync3(log, { throwIfNoEntry: false })?.size ?? 0;
|
|
68272
|
+
const out = openSync(log, "a", 384);
|
|
68273
|
+
const child = spawn4(process.execPath, [program, "serve", ...args], {
|
|
68274
|
+
detached: true,
|
|
68275
|
+
stdio: ["ignore", out, out]
|
|
68276
|
+
});
|
|
68277
|
+
closeSync(out);
|
|
68278
|
+
child.unref();
|
|
68279
|
+
let ended = false;
|
|
68280
|
+
child.once("exit", () => {
|
|
68281
|
+
ended = true;
|
|
68282
|
+
});
|
|
68283
|
+
for (; ; await sleep6(100)) {
|
|
68284
|
+
const said = recorded(data);
|
|
68285
|
+
const up2 = await running(data, said);
|
|
68286
|
+
const own3 = said?.pid === child.pid;
|
|
68287
|
+
if (up2 && (own3 || ended)) return up2;
|
|
68288
|
+
if (!ended && !own3) continue;
|
|
68289
|
+
if (!ended) {
|
|
68290
|
+
child.kill();
|
|
68291
|
+
throw new Error(`The service doesn't answer at ${said?.url} from here, so it was stopped.`);
|
|
68292
|
+
}
|
|
68293
|
+
const wrote = readFileSync4(log).subarray(from).toString().trim();
|
|
68294
|
+
throw new Error(`The service didn't start.${wrote ? ` It said:
|
|
68295
|
+
${wrote}` : ""}`);
|
|
68296
|
+
}
|
|
68297
|
+
}
|
|
68298
|
+
async function down(data) {
|
|
68299
|
+
const found = await running(data);
|
|
68300
|
+
if (!found) return;
|
|
68301
|
+
process.kill(found.pid, "SIGTERM");
|
|
68302
|
+
for (const deadline = Date.now() + STOP_MS2; Date.now() < deadline; await sleep6(100))
|
|
68303
|
+
if (!await running(data)) return;
|
|
68304
|
+
throw new Error(`The service at ${found.url} didn't stop.`);
|
|
68305
|
+
}
|
|
68306
|
+
|
|
68157
68307
|
// node_modules/openapi-fetch/dist/index.mjs
|
|
68158
68308
|
var PATH_PARAM_RE = /\{[^{}]+\}/g;
|
|
68159
68309
|
var supportsRequestInitExt = () => {
|
|
@@ -68839,13 +68989,14 @@ var Computer = class _Computer {
|
|
|
68839
68989
|
this.files = new Files(this.#computer);
|
|
68840
68990
|
}
|
|
68841
68991
|
static async create(opts) {
|
|
68842
|
-
const { access, browser, workspace, name, timeoutMs, metadata, apiKey, apiUrl } = opts;
|
|
68992
|
+
const { access, browser, isolation, workspace, name, timeoutMs, metadata, apiKey, apiUrl } = opts;
|
|
68843
68993
|
const connection = { ...apiKey ? { apiKey } : {}, ...apiUrl ? { apiUrl } : {} };
|
|
68844
68994
|
const created = answered(
|
|
68845
68995
|
await apiOf(connection).POST("/v1/computers", {
|
|
68846
68996
|
body: {
|
|
68847
68997
|
access,
|
|
68848
68998
|
browser: browser ?? false,
|
|
68999
|
+
...isolation === void 0 ? {} : { isolation },
|
|
68849
69000
|
...workspace === void 0 ? {} : { workspace },
|
|
68850
69001
|
...name === void 0 ? {} : { name },
|
|
68851
69002
|
...timeoutMs === void 0 ? {} : { timeout: seconds(timeoutMs) },
|
|
@@ -68950,13 +69101,12 @@ var Computer = class _Computer {
|
|
|
68950
69101
|
};
|
|
68951
69102
|
|
|
68952
69103
|
// src/service.ts
|
|
68953
|
-
import { readFileSync as
|
|
69104
|
+
import { readFileSync as readFileSync6, rmSync as rmSync3 } from "node:fs";
|
|
68954
69105
|
import {
|
|
68955
69106
|
createServer as createServer5,
|
|
68956
69107
|
STATUS_CODES as STATUS_CODES3
|
|
68957
69108
|
} from "node:http";
|
|
68958
69109
|
import { createServer as createTlsServer } from "node:https";
|
|
68959
|
-
import { join as join10 } from "node:path";
|
|
68960
69110
|
|
|
68961
69111
|
// node_modules/@modelcontextprotocol/server/dist/chunk-vYduTEsO.mjs
|
|
68962
69112
|
var __create4 = Object.create;
|
|
@@ -72417,7 +72567,7 @@ var REQUEST_STATE_ONLY_LEG_PACING_MS3 = 250;
|
|
|
72417
72567
|
function inputRequiredRoundsExceededMessage3(method, maxRounds) {
|
|
72418
72568
|
return `Multi-round-trip request '${method}' still required input after ${maxRounds} rounds (inputRequired.maxRounds)`;
|
|
72419
72569
|
}
|
|
72420
|
-
function
|
|
72570
|
+
function sleep7(ms, signal) {
|
|
72421
72571
|
return new Promise((resolve6, reject) => {
|
|
72422
72572
|
if (signal?.aborted) {
|
|
72423
72573
|
reject(signal.reason instanceof SdkError3 ? signal.reason : new SdkError3(SdkErrorCode3.RequestTimeout, String(signal.reason)));
|
|
@@ -76048,14 +76198,14 @@ var require_validate3 = /* @__PURE__ */ __commonJSMin3(((exports) => {
|
|
|
76048
76198
|
} else {
|
|
76049
76199
|
const matches = RELATIVE_JSON_POINTER.exec($data);
|
|
76050
76200
|
if (!matches) throw new Error(`Invalid JSON-pointer: ${$data}`);
|
|
76051
|
-
const
|
|
76201
|
+
const up2 = +matches[1];
|
|
76052
76202
|
jsonPointer = matches[2];
|
|
76053
76203
|
if (jsonPointer === "#") {
|
|
76054
|
-
if (
|
|
76055
|
-
return dataPathArr[dataLevel -
|
|
76204
|
+
if (up2 >= dataLevel) throw new Error(errorMsg("property/index", up2));
|
|
76205
|
+
return dataPathArr[dataLevel - up2];
|
|
76056
76206
|
}
|
|
76057
|
-
if (
|
|
76058
|
-
data = dataNames[dataLevel -
|
|
76207
|
+
if (up2 > dataLevel) throw new Error(errorMsg("data", up2));
|
|
76208
|
+
data = dataNames[dataLevel - up2];
|
|
76059
76209
|
if (!jsonPointer) return data;
|
|
76060
76210
|
}
|
|
76061
76211
|
let expr = data;
|
|
@@ -76065,8 +76215,8 @@ var require_validate3 = /* @__PURE__ */ __commonJSMin3(((exports) => {
|
|
|
76065
76215
|
expr = (0, codegen_1._)`${expr} && ${data}`;
|
|
76066
76216
|
}
|
|
76067
76217
|
return expr;
|
|
76068
|
-
function errorMsg(pointerType,
|
|
76069
|
-
return `Cannot access ${pointerType} ${
|
|
76218
|
+
function errorMsg(pointerType, up2) {
|
|
76219
|
+
return `Cannot access ${pointerType} ${up2} levels up, current level is ${dataLevel}`;
|
|
76070
76220
|
}
|
|
76071
76221
|
}
|
|
76072
76222
|
exports.getData = getData;
|
|
@@ -81414,7 +81564,7 @@ var LegacyInputRequiredShim2 = class {
|
|
|
81414
81564
|
} finally {
|
|
81415
81565
|
roundAbort.dispose();
|
|
81416
81566
|
}
|
|
81417
|
-
} else await
|
|
81567
|
+
} else await sleep7(REQUEST_STATE_ONLY_LEG_PACING_MS3, outerSignal);
|
|
81418
81568
|
let ctxNext = {
|
|
81419
81569
|
...ctx,
|
|
81420
81570
|
mcpReq: {
|
|
@@ -84771,8 +84921,8 @@ var NodeStreamableHTTPServerTransport2 = class {
|
|
|
84771
84921
|
|
|
84772
84922
|
// src/computers.ts
|
|
84773
84923
|
import { randomBytes as randomBytes5 } from "node:crypto";
|
|
84774
|
-
import { appendFileSync, existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync as
|
|
84775
|
-
import { isAbsolute, join as
|
|
84924
|
+
import { appendFileSync, existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync as readFileSync5, rmSync as rmSync2, statSync as statSync4 } from "node:fs";
|
|
84925
|
+
import { isAbsolute, join as join10, resolve as resolve4 } from "node:path";
|
|
84776
84926
|
var READ = ["about", "read", "list", "search"];
|
|
84777
84927
|
var FILES = [...READ, "edit"];
|
|
84778
84928
|
var TOOLS2 = {
|
|
@@ -84780,29 +84930,37 @@ var TOOLS2 = {
|
|
|
84780
84930
|
files: FILES,
|
|
84781
84931
|
terminal: [...FILES, "exec_command", "write_stdin", "list_sessions"]
|
|
84782
84932
|
};
|
|
84783
|
-
var
|
|
84933
|
+
var MAX_LOG2 = 256 * 1024;
|
|
84784
84934
|
var MAX_TIMER_S = 2e6;
|
|
84785
84935
|
var TICKET_MS = 6e4;
|
|
84786
84936
|
var expiry = (seconds2) => new Date(Date.now() + seconds2 * 1e3).toISOString();
|
|
84937
|
+
var messageOf = (error62) => error62 instanceof Error ? error62.message : String(error62);
|
|
84938
|
+
var refusalOf = (error62) => error62 instanceof ApiError ? error62 : new ApiError(500, messageOf(error62));
|
|
84787
84939
|
var Computers = class {
|
|
84788
84940
|
#data;
|
|
84789
84941
|
#base;
|
|
84790
|
-
#
|
|
84942
|
+
#runtimes;
|
|
84943
|
+
/** The isolation of a computer made without saying which. */
|
|
84944
|
+
isolation;
|
|
84791
84945
|
#stored = /* @__PURE__ */ new Map();
|
|
84792
84946
|
#live = /* @__PURE__ */ new Map();
|
|
84793
84947
|
#tickets = /* @__PURE__ */ new Map();
|
|
84794
84948
|
#file;
|
|
84795
|
-
constructor(data, base,
|
|
84949
|
+
constructor(data, base, runtimes, isolation) {
|
|
84796
84950
|
this.#data = data;
|
|
84797
84951
|
this.#base = base;
|
|
84798
|
-
this.#
|
|
84799
|
-
this
|
|
84952
|
+
this.#runtimes = runtimes;
|
|
84953
|
+
this.isolation = isolation;
|
|
84954
|
+
this.#file = join10(data, "computers.json");
|
|
84800
84955
|
if (existsSync2(this.#file))
|
|
84801
|
-
for (const entry of JSON.parse(
|
|
84802
|
-
this.#stored.set(entry.id, entry);
|
|
84956
|
+
for (const entry of JSON.parse(readFileSync5(this.#file, "utf8")))
|
|
84957
|
+
this.#stored.set(entry.id, { ...entry, isolation: entry.isolation ?? isolation });
|
|
84803
84958
|
}
|
|
84804
|
-
|
|
84805
|
-
|
|
84959
|
+
/** The isolations a computer can be made in now. */
|
|
84960
|
+
async available() {
|
|
84961
|
+
const all = Object.keys(this.#runtimes);
|
|
84962
|
+
const can = await Promise.all(all.map((one) => this.#runtimes[one].available()));
|
|
84963
|
+
return all.filter((_, index) => can[index]);
|
|
84806
84964
|
}
|
|
84807
84965
|
/** Bring back what was running when the service last stopped. */
|
|
84808
84966
|
async resume() {
|
|
@@ -84810,12 +84968,17 @@ var Computers = class {
|
|
|
84810
84968
|
if (stored.state !== "running") continue;
|
|
84811
84969
|
if (stored.endAt && Date.parse(stored.endAt) <= Date.now()) {
|
|
84812
84970
|
stored.state = "stopped";
|
|
84813
|
-
await this.#
|
|
84971
|
+
await this.#runtimes[stored.isolation].stopLeftover(stored.id).catch(
|
|
84972
|
+
(error62) => this.#log(stored.id, `What it left running wasn't stopped: ${messageOf(error62)}`)
|
|
84973
|
+
);
|
|
84814
84974
|
continue;
|
|
84815
84975
|
}
|
|
84816
|
-
await this.#start(stored).catch(
|
|
84817
|
-
|
|
84818
|
-
|
|
84976
|
+
await this.#start(stored).catch((error62) => {
|
|
84977
|
+
const live = this.#live.get(stored.id);
|
|
84978
|
+
if (live) live.failed = true;
|
|
84979
|
+
this.#arm(stored);
|
|
84980
|
+
this.#log(stored.id, `It didn't start again: ${messageOf(error62)}`);
|
|
84981
|
+
});
|
|
84819
84982
|
}
|
|
84820
84983
|
this.#save();
|
|
84821
84984
|
}
|
|
@@ -84828,7 +84991,7 @@ var Computers = class {
|
|
|
84828
84991
|
if (input2.workspace !== void 0) {
|
|
84829
84992
|
let folder = false;
|
|
84830
84993
|
try {
|
|
84831
|
-
folder = isAbsolute(input2.workspace) &&
|
|
84994
|
+
folder = isAbsolute(input2.workspace) && statSync4(input2.workspace).isDirectory();
|
|
84832
84995
|
} catch {
|
|
84833
84996
|
}
|
|
84834
84997
|
if (!folder) throw new ApiError(400, `There's no folder at ${input2.workspace}.`);
|
|
@@ -84841,6 +85004,7 @@ var Computers = class {
|
|
|
84841
85004
|
id,
|
|
84842
85005
|
name: name ?? id,
|
|
84843
85006
|
access,
|
|
85007
|
+
isolation: input2.isolation ?? this.isolation,
|
|
84844
85008
|
browser,
|
|
84845
85009
|
...workspace ? { workspace } : {},
|
|
84846
85010
|
metadata: metadata ?? {},
|
|
@@ -84851,7 +85015,7 @@ var Computers = class {
|
|
|
84851
85015
|
...timeout === void 0 ? {} : { endAt: expiry(timeout) }
|
|
84852
85016
|
};
|
|
84853
85017
|
if (!workspace && access !== "none") {
|
|
84854
|
-
stored.workspace =
|
|
85018
|
+
stored.workspace = join10(this.#folder(id), "workspace");
|
|
84855
85019
|
mkdirSync2(stored.workspace, { recursive: true, mode: 448 });
|
|
84856
85020
|
}
|
|
84857
85021
|
this.#stored.set(id, stored);
|
|
@@ -84860,9 +85024,10 @@ var Computers = class {
|
|
|
84860
85024
|
} catch (error62) {
|
|
84861
85025
|
await this.#halt(id);
|
|
84862
85026
|
this.#stored.delete(id);
|
|
84863
|
-
await this.#
|
|
85027
|
+
await this.#runtimes[stored.isolation].remove(id).catch(() => {
|
|
85028
|
+
});
|
|
84864
85029
|
rmSync2(this.#folder(id), { recursive: true, force: true });
|
|
84865
|
-
throw
|
|
85030
|
+
throw refusalOf(error62);
|
|
84866
85031
|
}
|
|
84867
85032
|
this.#save();
|
|
84868
85033
|
return { ...this.#describe(stored), accessToken: token };
|
|
@@ -84917,16 +85082,22 @@ var Computers = class {
|
|
|
84917
85082
|
await this.#halt(id);
|
|
84918
85083
|
stored.state = "stopped";
|
|
84919
85084
|
this.#save();
|
|
84920
|
-
throw
|
|
85085
|
+
throw refusalOf(error62);
|
|
84921
85086
|
}
|
|
84922
85087
|
this.#save();
|
|
84923
85088
|
return this.#describe(stored);
|
|
84924
85089
|
}
|
|
84925
85090
|
/** Stop everything and remove what the service made. A folder someone named stays. */
|
|
84926
85091
|
async kill(id) {
|
|
84927
|
-
this.#found(id);
|
|
85092
|
+
const stored = this.#found(id);
|
|
84928
85093
|
await this.#halt(id);
|
|
84929
|
-
|
|
85094
|
+
try {
|
|
85095
|
+
await this.#runtimes[stored.isolation].remove(id);
|
|
85096
|
+
} catch (error62) {
|
|
85097
|
+
stored.state = "stopped";
|
|
85098
|
+
this.#save();
|
|
85099
|
+
throw refusalOf(error62);
|
|
85100
|
+
}
|
|
84930
85101
|
this.#stored.delete(id);
|
|
84931
85102
|
this.#save();
|
|
84932
85103
|
rmSync2(this.#folder(id), { recursive: true, force: true });
|
|
@@ -84935,7 +85106,7 @@ var Computers = class {
|
|
|
84935
85106
|
this.#found(id);
|
|
84936
85107
|
let text3 = "";
|
|
84937
85108
|
try {
|
|
84938
|
-
text3 =
|
|
85109
|
+
text3 = readFileSync5(this.#logFile(id), "utf8");
|
|
84939
85110
|
} catch {
|
|
84940
85111
|
}
|
|
84941
85112
|
return text3.split("\n").filter(Boolean).map((entry) => ({
|
|
@@ -84952,17 +85123,17 @@ var Computers = class {
|
|
|
84952
85123
|
throw new ApiError(404, "This computer has no files or terminal.");
|
|
84953
85124
|
if (endpoint === "browser" && !stored.browser)
|
|
84954
85125
|
throw new ApiError(404, "This computer has no browser.");
|
|
84955
|
-
const
|
|
84956
|
-
if (!
|
|
85126
|
+
const running2 = this.#live.get(id)?.running;
|
|
85127
|
+
if (!running2) throw new ApiError(409, "It has stopped.");
|
|
84957
85128
|
this.#log(id, `${request.method} mcp/${endpoint}`);
|
|
84958
|
-
return
|
|
85129
|
+
return running2.handle(endpoint, request, response);
|
|
84959
85130
|
}
|
|
84960
85131
|
/** A one-time ticket to open one of its surfaces live. */
|
|
84961
85132
|
liveSession(id, surface, mode) {
|
|
84962
85133
|
this.#found(id);
|
|
84963
|
-
const
|
|
84964
|
-
if (!
|
|
84965
|
-
const info =
|
|
85134
|
+
const running2 = this.#live.get(id)?.running;
|
|
85135
|
+
if (!running2) throw new ApiError(409, "It has stopped.");
|
|
85136
|
+
const info = running2.surfaces.find((one) => one.surface === surface);
|
|
84966
85137
|
if (!info) throw new ApiError(409, `This computer has no ${surface} to open.`);
|
|
84967
85138
|
if (info.unavailable) throw new ApiError(409, info.unavailable);
|
|
84968
85139
|
if (mode === "operate" && !info.operate)
|
|
@@ -84987,10 +85158,10 @@ var Computers = class {
|
|
|
84987
85158
|
this.#tickets.delete(ticket);
|
|
84988
85159
|
if (!found || found.expiresAt <= Date.now())
|
|
84989
85160
|
throw new ApiError(404, "There's no such live session: it was used, or it expired.");
|
|
84990
|
-
const
|
|
84991
|
-
if (!
|
|
85161
|
+
const running2 = this.#live.get(found.computerID)?.running;
|
|
85162
|
+
if (!running2) throw new ApiError(409, "It has stopped.");
|
|
84992
85163
|
this.#log(found.computerID, `live ${found.surface} ${found.mode}`);
|
|
84993
|
-
await
|
|
85164
|
+
await running2.openLive(found.surface, found.mode, request, socket, head);
|
|
84994
85165
|
}
|
|
84995
85166
|
/** Stop every computer, leaving their records to resume at the next start. */
|
|
84996
85167
|
async close() {
|
|
@@ -85002,17 +85173,17 @@ var Computers = class {
|
|
|
85002
85173
|
return stored;
|
|
85003
85174
|
}
|
|
85004
85175
|
#folder(id) {
|
|
85005
|
-
return privateFolder(
|
|
85176
|
+
return privateFolder(join10(this.#data, "computers", id));
|
|
85006
85177
|
}
|
|
85007
85178
|
#logFile(id) {
|
|
85008
|
-
return
|
|
85179
|
+
return join10(this.#folder(id), "log");
|
|
85009
85180
|
}
|
|
85010
85181
|
#log(id, line) {
|
|
85011
85182
|
if (!this.#stored.has(id)) return;
|
|
85012
85183
|
try {
|
|
85013
85184
|
const file2 = this.#logFile(id);
|
|
85014
|
-
if (existsSync2(file2) &&
|
|
85015
|
-
writePrivate(file2,
|
|
85185
|
+
if (existsSync2(file2) && statSync4(file2).size > MAX_LOG2)
|
|
85186
|
+
writePrivate(file2, readFileSync5(file2, "utf8").slice(-MAX_LOG2 / 2));
|
|
85016
85187
|
appendFileSync(file2, `${(/* @__PURE__ */ new Date()).toISOString()} ${line}
|
|
85017
85188
|
`, { mode: 384 });
|
|
85018
85189
|
} catch {
|
|
@@ -85023,12 +85194,13 @@ var Computers = class {
|
|
|
85023
85194
|
}
|
|
85024
85195
|
#describe(stored) {
|
|
85025
85196
|
const path = `${this.#base()}/v1/computers/${stored.id}/mcp`;
|
|
85026
|
-
const
|
|
85197
|
+
const down2 = this.#live.get(stored.id)?.failed === true;
|
|
85027
85198
|
return {
|
|
85028
85199
|
computerID: stored.id,
|
|
85029
85200
|
name: stored.name,
|
|
85030
|
-
state: stored.state === "running" &&
|
|
85201
|
+
state: stored.state === "running" && down2 ? "failed" : stored.state,
|
|
85031
85202
|
access: stored.access,
|
|
85203
|
+
isolation: stored.isolation,
|
|
85032
85204
|
browser: stored.browser,
|
|
85033
85205
|
workspace: stored.workspace ?? null,
|
|
85034
85206
|
startedAt: stored.startedAt,
|
|
@@ -85044,7 +85216,9 @@ var Computers = class {
|
|
|
85044
85216
|
const live = {};
|
|
85045
85217
|
this.#live.set(stored.id, live);
|
|
85046
85218
|
stored.startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
85047
|
-
|
|
85219
|
+
const runtime = this.#runtimes[stored.isolation];
|
|
85220
|
+
await runtime.prepare();
|
|
85221
|
+
live.running = await runtime.start(
|
|
85048
85222
|
{
|
|
85049
85223
|
id: stored.id,
|
|
85050
85224
|
access: stored.access,
|
|
@@ -85097,7 +85271,7 @@ var OPENAPI = {
|
|
|
85097
85271
|
"openapi": "3.1.0",
|
|
85098
85272
|
"info": {
|
|
85099
85273
|
"title": "AgentComputer",
|
|
85100
|
-
"version": "0.0.
|
|
85274
|
+
"version": "0.0.15",
|
|
85101
85275
|
"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.",
|
|
85102
85276
|
"license": {
|
|
85103
85277
|
"name": "Apache-2.0",
|
|
@@ -85557,7 +85731,8 @@ var OPENAPI = {
|
|
|
85557
85731
|
"running",
|
|
85558
85732
|
"stopped",
|
|
85559
85733
|
"failed"
|
|
85560
|
-
]
|
|
85734
|
+
],
|
|
85735
|
+
"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."
|
|
85561
85736
|
},
|
|
85562
85737
|
"Timeout": {
|
|
85563
85738
|
"type": "integer",
|
|
@@ -85598,6 +85773,10 @@ var OPENAPI = {
|
|
|
85598
85773
|
"maxLength": 4096,
|
|
85599
85774
|
"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."
|
|
85600
85775
|
},
|
|
85776
|
+
"isolation": {
|
|
85777
|
+
"$ref": "#/components/schemas/Isolation",
|
|
85778
|
+
"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."
|
|
85779
|
+
},
|
|
85601
85780
|
"timeout": {
|
|
85602
85781
|
"$ref": "#/components/schemas/Timeout"
|
|
85603
85782
|
},
|
|
@@ -85606,6 +85785,14 @@ var OPENAPI = {
|
|
|
85606
85785
|
}
|
|
85607
85786
|
}
|
|
85608
85787
|
},
|
|
85788
|
+
"Isolation": {
|
|
85789
|
+
"type": "string",
|
|
85790
|
+
"enum": [
|
|
85791
|
+
"none",
|
|
85792
|
+
"container"
|
|
85793
|
+
],
|
|
85794
|
+
"description": "none: processes of whoever started the service. container: a container of its own, which needs Docker on the service's machine."
|
|
85795
|
+
},
|
|
85609
85796
|
"Endpoints": {
|
|
85610
85797
|
"type": "object",
|
|
85611
85798
|
"required": [
|
|
@@ -85637,6 +85824,7 @@ var OPENAPI = {
|
|
|
85637
85824
|
"name",
|
|
85638
85825
|
"state",
|
|
85639
85826
|
"access",
|
|
85827
|
+
"isolation",
|
|
85640
85828
|
"browser",
|
|
85641
85829
|
"workspace",
|
|
85642
85830
|
"startedAt",
|
|
@@ -85657,6 +85845,10 @@ var OPENAPI = {
|
|
|
85657
85845
|
"access": {
|
|
85658
85846
|
"$ref": "#/components/schemas/Access"
|
|
85659
85847
|
},
|
|
85848
|
+
"isolation": {
|
|
85849
|
+
"$ref": "#/components/schemas/Isolation",
|
|
85850
|
+
"description": "The isolation it was made in. It is never run in another."
|
|
85851
|
+
},
|
|
85660
85852
|
"browser": {
|
|
85661
85853
|
"type": "boolean"
|
|
85662
85854
|
},
|
|
@@ -86815,19 +87007,23 @@ var OPENAPI = {
|
|
|
86815
87007
|
"type": "object",
|
|
86816
87008
|
"required": [
|
|
86817
87009
|
"version",
|
|
86818
|
-
"isolation"
|
|
87010
|
+
"isolation",
|
|
87011
|
+
"isolationsAvailable"
|
|
86819
87012
|
],
|
|
86820
87013
|
"properties": {
|
|
86821
87014
|
"version": {
|
|
86822
87015
|
"type": "string"
|
|
86823
87016
|
},
|
|
86824
87017
|
"isolation": {
|
|
86825
|
-
"
|
|
86826
|
-
"
|
|
86827
|
-
|
|
86828
|
-
|
|
86829
|
-
|
|
86830
|
-
"
|
|
87018
|
+
"$ref": "#/components/schemas/Isolation",
|
|
87019
|
+
"description": "The isolation of a computer made without saying which, chosen when the service is started."
|
|
87020
|
+
},
|
|
87021
|
+
"isolationsAvailable": {
|
|
87022
|
+
"type": "array",
|
|
87023
|
+
"items": {
|
|
87024
|
+
"$ref": "#/components/schemas/Isolation"
|
|
87025
|
+
},
|
|
87026
|
+
"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."
|
|
86831
87027
|
}
|
|
86832
87028
|
}
|
|
86833
87029
|
}
|
|
@@ -86855,7 +87051,8 @@ var OPERATIONS = [
|
|
|
86855
87051
|
"type": "object",
|
|
86856
87052
|
"required": [
|
|
86857
87053
|
"version",
|
|
86858
|
-
"isolation"
|
|
87054
|
+
"isolation",
|
|
87055
|
+
"isolationsAvailable"
|
|
86859
87056
|
],
|
|
86860
87057
|
"properties": {
|
|
86861
87058
|
"version": {
|
|
@@ -86867,7 +87064,19 @@ var OPERATIONS = [
|
|
|
86867
87064
|
"none",
|
|
86868
87065
|
"container"
|
|
86869
87066
|
],
|
|
86870
|
-
"description": "
|
|
87067
|
+
"description": "The isolation of a computer made without saying which, chosen when the service is started."
|
|
87068
|
+
},
|
|
87069
|
+
"isolationsAvailable": {
|
|
87070
|
+
"type": "array",
|
|
87071
|
+
"items": {
|
|
87072
|
+
"type": "string",
|
|
87073
|
+
"enum": [
|
|
87074
|
+
"none",
|
|
87075
|
+
"container"
|
|
87076
|
+
],
|
|
87077
|
+
"description": "none: processes of whoever started the service. container: a container of its own, which needs Docker on the service's machine."
|
|
87078
|
+
},
|
|
87079
|
+
"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."
|
|
86871
87080
|
}
|
|
86872
87081
|
}
|
|
86873
87082
|
},
|
|
@@ -86895,7 +87104,8 @@ var OPERATIONS = [
|
|
|
86895
87104
|
"running",
|
|
86896
87105
|
"stopped",
|
|
86897
87106
|
"failed"
|
|
86898
|
-
]
|
|
87107
|
+
],
|
|
87108
|
+
"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."
|
|
86899
87109
|
}
|
|
86900
87110
|
},
|
|
86901
87111
|
"required": []
|
|
@@ -86909,6 +87119,7 @@ var OPERATIONS = [
|
|
|
86909
87119
|
"name",
|
|
86910
87120
|
"state",
|
|
86911
87121
|
"access",
|
|
87122
|
+
"isolation",
|
|
86912
87123
|
"browser",
|
|
86913
87124
|
"workspace",
|
|
86914
87125
|
"startedAt",
|
|
@@ -86929,7 +87140,8 @@ var OPERATIONS = [
|
|
|
86929
87140
|
"running",
|
|
86930
87141
|
"stopped",
|
|
86931
87142
|
"failed"
|
|
86932
|
-
]
|
|
87143
|
+
],
|
|
87144
|
+
"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."
|
|
86933
87145
|
},
|
|
86934
87146
|
"access": {
|
|
86935
87147
|
"type": "string",
|
|
@@ -86941,6 +87153,14 @@ var OPERATIONS = [
|
|
|
86941
87153
|
],
|
|
86942
87154
|
"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."
|
|
86943
87155
|
},
|
|
87156
|
+
"isolation": {
|
|
87157
|
+
"type": "string",
|
|
87158
|
+
"enum": [
|
|
87159
|
+
"none",
|
|
87160
|
+
"container"
|
|
87161
|
+
],
|
|
87162
|
+
"description": "The isolation it was made in. It is never run in another."
|
|
87163
|
+
},
|
|
86944
87164
|
"browser": {
|
|
86945
87165
|
"type": "boolean"
|
|
86946
87166
|
},
|
|
@@ -87040,6 +87260,14 @@ var OPERATIONS = [
|
|
|
87040
87260
|
"maxLength": 4096,
|
|
87041
87261
|
"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."
|
|
87042
87262
|
},
|
|
87263
|
+
"isolation": {
|
|
87264
|
+
"type": "string",
|
|
87265
|
+
"enum": [
|
|
87266
|
+
"none",
|
|
87267
|
+
"container"
|
|
87268
|
+
],
|
|
87269
|
+
"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."
|
|
87270
|
+
},
|
|
87043
87271
|
"timeout": {
|
|
87044
87272
|
"type": "integer",
|
|
87045
87273
|
"minimum": 1,
|
|
@@ -87068,6 +87296,7 @@ var OPERATIONS = [
|
|
|
87068
87296
|
"name",
|
|
87069
87297
|
"state",
|
|
87070
87298
|
"access",
|
|
87299
|
+
"isolation",
|
|
87071
87300
|
"browser",
|
|
87072
87301
|
"workspace",
|
|
87073
87302
|
"startedAt",
|
|
@@ -87088,7 +87317,8 @@ var OPERATIONS = [
|
|
|
87088
87317
|
"running",
|
|
87089
87318
|
"stopped",
|
|
87090
87319
|
"failed"
|
|
87091
|
-
]
|
|
87320
|
+
],
|
|
87321
|
+
"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."
|
|
87092
87322
|
},
|
|
87093
87323
|
"access": {
|
|
87094
87324
|
"type": "string",
|
|
@@ -87100,6 +87330,14 @@ var OPERATIONS = [
|
|
|
87100
87330
|
],
|
|
87101
87331
|
"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."
|
|
87102
87332
|
},
|
|
87333
|
+
"isolation": {
|
|
87334
|
+
"type": "string",
|
|
87335
|
+
"enum": [
|
|
87336
|
+
"none",
|
|
87337
|
+
"container"
|
|
87338
|
+
],
|
|
87339
|
+
"description": "The isolation it was made in. It is never run in another."
|
|
87340
|
+
},
|
|
87103
87341
|
"browser": {
|
|
87104
87342
|
"type": "boolean"
|
|
87105
87343
|
},
|
|
@@ -87208,6 +87446,7 @@ var OPERATIONS = [
|
|
|
87208
87446
|
"name",
|
|
87209
87447
|
"state",
|
|
87210
87448
|
"access",
|
|
87449
|
+
"isolation",
|
|
87211
87450
|
"browser",
|
|
87212
87451
|
"workspace",
|
|
87213
87452
|
"startedAt",
|
|
@@ -87228,7 +87467,8 @@ var OPERATIONS = [
|
|
|
87228
87467
|
"running",
|
|
87229
87468
|
"stopped",
|
|
87230
87469
|
"failed"
|
|
87231
|
-
]
|
|
87470
|
+
],
|
|
87471
|
+
"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."
|
|
87232
87472
|
},
|
|
87233
87473
|
"access": {
|
|
87234
87474
|
"type": "string",
|
|
@@ -87240,6 +87480,14 @@ var OPERATIONS = [
|
|
|
87240
87480
|
],
|
|
87241
87481
|
"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."
|
|
87242
87482
|
},
|
|
87483
|
+
"isolation": {
|
|
87484
|
+
"type": "string",
|
|
87485
|
+
"enum": [
|
|
87486
|
+
"none",
|
|
87487
|
+
"container"
|
|
87488
|
+
],
|
|
87489
|
+
"description": "The isolation it was made in. It is never run in another."
|
|
87490
|
+
},
|
|
87243
87491
|
"browser": {
|
|
87244
87492
|
"type": "boolean"
|
|
87245
87493
|
},
|
|
@@ -87439,6 +87687,7 @@ var OPERATIONS = [
|
|
|
87439
87687
|
"name",
|
|
87440
87688
|
"state",
|
|
87441
87689
|
"access",
|
|
87690
|
+
"isolation",
|
|
87442
87691
|
"browser",
|
|
87443
87692
|
"workspace",
|
|
87444
87693
|
"startedAt",
|
|
@@ -87459,7 +87708,8 @@ var OPERATIONS = [
|
|
|
87459
87708
|
"running",
|
|
87460
87709
|
"stopped",
|
|
87461
87710
|
"failed"
|
|
87462
|
-
]
|
|
87711
|
+
],
|
|
87712
|
+
"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."
|
|
87463
87713
|
},
|
|
87464
87714
|
"access": {
|
|
87465
87715
|
"type": "string",
|
|
@@ -87471,6 +87721,14 @@ var OPERATIONS = [
|
|
|
87471
87721
|
],
|
|
87472
87722
|
"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."
|
|
87473
87723
|
},
|
|
87724
|
+
"isolation": {
|
|
87725
|
+
"type": "string",
|
|
87726
|
+
"enum": [
|
|
87727
|
+
"none",
|
|
87728
|
+
"container"
|
|
87729
|
+
],
|
|
87730
|
+
"description": "The isolation it was made in. It is never run in another."
|
|
87731
|
+
},
|
|
87474
87732
|
"browser": {
|
|
87475
87733
|
"type": "boolean"
|
|
87476
87734
|
},
|
|
@@ -87563,6 +87821,7 @@ var OPERATIONS = [
|
|
|
87563
87821
|
"name",
|
|
87564
87822
|
"state",
|
|
87565
87823
|
"access",
|
|
87824
|
+
"isolation",
|
|
87566
87825
|
"browser",
|
|
87567
87826
|
"workspace",
|
|
87568
87827
|
"startedAt",
|
|
@@ -87583,7 +87842,8 @@ var OPERATIONS = [
|
|
|
87583
87842
|
"running",
|
|
87584
87843
|
"stopped",
|
|
87585
87844
|
"failed"
|
|
87586
|
-
]
|
|
87845
|
+
],
|
|
87846
|
+
"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."
|
|
87587
87847
|
},
|
|
87588
87848
|
"access": {
|
|
87589
87849
|
"type": "string",
|
|
@@ -87595,6 +87855,14 @@ var OPERATIONS = [
|
|
|
87595
87855
|
],
|
|
87596
87856
|
"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."
|
|
87597
87857
|
},
|
|
87858
|
+
"isolation": {
|
|
87859
|
+
"type": "string",
|
|
87860
|
+
"enum": [
|
|
87861
|
+
"none",
|
|
87862
|
+
"container"
|
|
87863
|
+
],
|
|
87864
|
+
"description": "The isolation it was made in. It is never run in another."
|
|
87865
|
+
},
|
|
87598
87866
|
"browser": {
|
|
87599
87867
|
"type": "boolean"
|
|
87600
87868
|
},
|
|
@@ -87692,6 +87960,7 @@ var OPERATIONS = [
|
|
|
87692
87960
|
"name",
|
|
87693
87961
|
"state",
|
|
87694
87962
|
"access",
|
|
87963
|
+
"isolation",
|
|
87695
87964
|
"browser",
|
|
87696
87965
|
"workspace",
|
|
87697
87966
|
"startedAt",
|
|
@@ -87712,7 +87981,8 @@ var OPERATIONS = [
|
|
|
87712
87981
|
"running",
|
|
87713
87982
|
"stopped",
|
|
87714
87983
|
"failed"
|
|
87715
|
-
]
|
|
87984
|
+
],
|
|
87985
|
+
"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."
|
|
87716
87986
|
},
|
|
87717
87987
|
"access": {
|
|
87718
87988
|
"type": "string",
|
|
@@ -87724,6 +87994,14 @@ var OPERATIONS = [
|
|
|
87724
87994
|
],
|
|
87725
87995
|
"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."
|
|
87726
87996
|
},
|
|
87997
|
+
"isolation": {
|
|
87998
|
+
"type": "string",
|
|
87999
|
+
"enum": [
|
|
88000
|
+
"none",
|
|
88001
|
+
"container"
|
|
88002
|
+
],
|
|
88003
|
+
"description": "The isolation it was made in. It is never run in another."
|
|
88004
|
+
},
|
|
87727
88005
|
"browser": {
|
|
87728
88006
|
"type": "boolean"
|
|
87729
88007
|
},
|
|
@@ -88011,9 +88289,12 @@ async function serve(options) {
|
|
|
88011
88289
|
checkExposure(host, options);
|
|
88012
88290
|
const data = privateFolder(options.data);
|
|
88013
88291
|
const control = digestOf(controlToken(data));
|
|
88292
|
+
const live = await running(data);
|
|
88293
|
+
if (live)
|
|
88294
|
+
throw new Error(`A service is already running with its data in ${data}, at ${live.url}.`);
|
|
88014
88295
|
let base = "";
|
|
88015
|
-
await options.
|
|
88016
|
-
const computers = new Computers(data, () => base, options.
|
|
88296
|
+
await options.runtimes[options.isolation].prepare();
|
|
88297
|
+
const computers = new Computers(data, () => base, options.runtimes, options.isolation);
|
|
88017
88298
|
const handlers = createHandlers(computers);
|
|
88018
88299
|
const routes = OPERATIONS.map((operation) => ({
|
|
88019
88300
|
operation,
|
|
@@ -88101,7 +88382,7 @@ async function serve(options) {
|
|
|
88101
88382
|
});
|
|
88102
88383
|
};
|
|
88103
88384
|
const server = options.tls ? createTlsServer(
|
|
88104
|
-
{ cert:
|
|
88385
|
+
{ cert: readFileSync6(options.tls.cert), key: readFileSync6(options.tls.key) },
|
|
88105
88386
|
listener
|
|
88106
88387
|
) : createServer5(listener);
|
|
88107
88388
|
const allowed = new Set(options.allowOrigins ?? []);
|
|
@@ -88133,11 +88414,12 @@ ${JSON.stringify({ code: status, message })}
|
|
|
88133
88414
|
const bound = server.address();
|
|
88134
88415
|
const scheme = options.tls ? "https" : "http";
|
|
88135
88416
|
base = options.publicUrl?.replace(/\/+$/, "") ?? `${scheme}://${bound.family === "IPv6" ? `[${bound.address}]` : bound.address}:${bound.port}`;
|
|
88417
|
+
const record3 = recordOf(data);
|
|
88418
|
+
rmSync3(record3, { force: true });
|
|
88136
88419
|
await computers.resume();
|
|
88137
|
-
const record3 = join10(data, "server.json");
|
|
88138
88420
|
writePrivate(
|
|
88139
88421
|
record3,
|
|
88140
|
-
JSON.stringify({ url: base, pid: process.pid, isolation: options.
|
|
88422
|
+
JSON.stringify({ url: base, pid: process.pid, isolation: options.isolation })
|
|
88141
88423
|
);
|
|
88142
88424
|
process.stdout.write(`AgentComputer on ${base}, data in ${data}
|
|
88143
88425
|
`);
|
|
@@ -88163,9 +88445,14 @@ var USAGE = `Usage: agentcomputer <command> [--data DIR]
|
|
|
88163
88445
|
[--browser-name NAME] [--browser-path PATH]
|
|
88164
88446
|
run the service; 127.0.0.1:7878 by default, and
|
|
88165
88447
|
anywhere else only with TLS
|
|
88166
|
-
|
|
88448
|
+
up [the options of serve] run the service in the background and return once it
|
|
88449
|
+
answers, printing where; one running is left as it is
|
|
88450
|
+
down stop the service, and its computers with it
|
|
88451
|
+
status where the service is running, if it is
|
|
88452
|
+
info the service's version, the isolation it gives a computer
|
|
88453
|
+
made without saying, and the ones it can make now
|
|
88167
88454
|
create --access none|read|files|terminal [--browser] [--workspace DIR] [--name NAME]
|
|
88168
|
-
[--timeout SECONDS] [--metadata KEY=VALUE]...
|
|
88455
|
+
[--isolation none|container] [--timeout SECONDS] [--metadata KEY=VALUE]...
|
|
88169
88456
|
make a computer; prints its endpoints and token, once
|
|
88170
88457
|
list [--state running|stopped|failed]
|
|
88171
88458
|
the computers
|
|
@@ -88178,6 +88465,8 @@ var USAGE = `Usage: agentcomputer <command> [--data DIR]
|
|
|
88178
88465
|
a one-time ticket to open one live, as a WebSocket
|
|
88179
88466
|
kill <id> stop it and remove what the service made
|
|
88180
88467
|
|
|
88468
|
+
--version prints this program's version, with no service.
|
|
88469
|
+
|
|
88181
88470
|
The data folder is --data, else $AGENTCOMPUTER_DATA, else $XDG_DATA_HOME/agentcomputer, else
|
|
88182
88471
|
~/.local/share/agentcomputer. Commands find the running service there, with its control token.
|
|
88183
88472
|
`;
|
|
@@ -88190,20 +88479,18 @@ function dataFolder(given) {
|
|
|
88190
88479
|
given ?? process.env.AGENTCOMPUTER_DATA ?? (xdg && isAbsolute2(xdg) ? join11(xdg, "agentcomputer") : join11(homedir2(), ".local", "share", "agentcomputer"))
|
|
88191
88480
|
);
|
|
88192
88481
|
}
|
|
88482
|
+
var none = (data) => `No service is running with its data in ${data}. Start one: agentcomputer up`;
|
|
88193
88483
|
function connectionTo(data) {
|
|
88194
|
-
|
|
88195
|
-
|
|
88196
|
-
|
|
88197
|
-
} catch {
|
|
88198
|
-
throw new Error(
|
|
88199
|
-
`No service is running with its data in ${data}. Start one: agentcomputer serve`
|
|
88200
|
-
);
|
|
88201
|
-
}
|
|
88484
|
+
const said = recorded(data);
|
|
88485
|
+
if (!said) throw new Error(none(data));
|
|
88486
|
+
return { apiUrl: said.url, apiKey: said.token };
|
|
88202
88487
|
}
|
|
88203
88488
|
async function main() {
|
|
88204
|
-
const { values, positionals } = parseArgs({
|
|
88489
|
+
const { values, positionals, tokens } = parseArgs({
|
|
88205
88490
|
allowPositionals: true,
|
|
88491
|
+
tokens: true,
|
|
88206
88492
|
options: {
|
|
88493
|
+
version: { type: "boolean" },
|
|
88207
88494
|
data: { type: "string" },
|
|
88208
88495
|
listen: { type: "string" },
|
|
88209
88496
|
isolation: { type: "string" },
|
|
@@ -88234,19 +88521,38 @@ async function main() {
|
|
|
88234
88521
|
}
|
|
88235
88522
|
});
|
|
88236
88523
|
const [command, id, extra] = positionals;
|
|
88524
|
+
if (values.version) return void process.stdout.write(`${VERSION2}
|
|
88525
|
+
`);
|
|
88237
88526
|
if (values.help || !command) return void process.stdout.write(USAGE);
|
|
88238
88527
|
const data = dataFolder(values.data);
|
|
88239
88528
|
const out = (value) => process.stdout.write(`${JSON.stringify(value, null, 2)}
|
|
88240
88529
|
`);
|
|
88530
|
+
if (command === "status") {
|
|
88531
|
+
const found = await running(data);
|
|
88532
|
+
if (!found) throw new Error(none(data));
|
|
88533
|
+
return out(found);
|
|
88534
|
+
}
|
|
88535
|
+
if (command === "up") {
|
|
88536
|
+
const given = process.argv.slice(2);
|
|
88537
|
+
given.splice(tokens.find((token) => token.kind === "positional")?.index, 1);
|
|
88538
|
+
return out(await up(data, process.argv[1], given));
|
|
88539
|
+
}
|
|
88540
|
+
if (command === "down") return down(data);
|
|
88541
|
+
if ((command === "start" || command === "stop") && !id) {
|
|
88542
|
+
const service = command === "start" ? "up" : "down";
|
|
88543
|
+
throw new UsageError(
|
|
88544
|
+
`${command} takes a computer's id. For the service itself: agentcomputer ${service}`
|
|
88545
|
+
);
|
|
88546
|
+
}
|
|
88241
88547
|
const idOf = () => {
|
|
88242
88548
|
if (!id) throw new UsageError(`${command} takes a computer's id.`);
|
|
88243
88549
|
return id;
|
|
88244
88550
|
};
|
|
88245
88551
|
const milliseconds = (seconds2) => Number(seconds2) * 1e3;
|
|
88552
|
+
const { isolation } = values;
|
|
88553
|
+
if (isolation !== void 0 && isolation !== "none" && isolation !== "container")
|
|
88554
|
+
throw new UsageError("--isolation is none or container.");
|
|
88246
88555
|
if (command === "serve") {
|
|
88247
|
-
const isolation = values.isolation ?? "none";
|
|
88248
|
-
if (isolation !== "none" && isolation !== "container")
|
|
88249
|
-
throw new UsageError("--isolation is none or container.");
|
|
88250
88556
|
const { "tls-cert": cert, "tls-key": key } = values;
|
|
88251
88557
|
if (Boolean(cert) !== Boolean(key))
|
|
88252
88558
|
throw new UsageError("--tls-cert and --tls-key go together.");
|
|
@@ -88257,10 +88563,11 @@ async function main() {
|
|
|
88257
88563
|
tlsProxy: values["tls-proxy"],
|
|
88258
88564
|
publicUrl: values["public-url"],
|
|
88259
88565
|
allowOrigins: values["allow-origin"],
|
|
88260
|
-
|
|
88261
|
-
|
|
88262
|
-
path: values["browser-path"]
|
|
88263
|
-
|
|
88566
|
+
isolation: isolation ?? "none",
|
|
88567
|
+
runtimes: {
|
|
88568
|
+
none: new ProcessRuntime({ browser: values["browser-name"], path: values["browser-path"] }),
|
|
88569
|
+
container: new ContainerRuntime(values.image ?? `${IMAGE}:${VERSION2}`)
|
|
88570
|
+
}
|
|
88264
88571
|
});
|
|
88265
88572
|
}
|
|
88266
88573
|
if (command === "browser-host")
|
|
@@ -88290,6 +88597,7 @@ async function main() {
|
|
|
88290
88597
|
...connection,
|
|
88291
88598
|
access: values.access,
|
|
88292
88599
|
...values.browser ? { browser: true } : {},
|
|
88600
|
+
...isolation ? { isolation } : {},
|
|
88293
88601
|
...values.workspace ? { workspace: resolve5(values.workspace) } : {},
|
|
88294
88602
|
...values.name ? { name: values.name } : {},
|
|
88295
88603
|
...values.timeout ? { timeoutMs: milliseconds(values.timeout) } : {},
|