@agentorg/computer 0.0.14 → 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 +36 -17
- package/dist/THIRD_PARTY_LICENSES.txt +1 -1
- package/dist/agentcomputer.js +459 -164
- 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,7 +27957,10 @@ 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;
|
|
27964
27966
|
var ONE_REQUEST = "A body is one JSON-RPC request, as a JSON object: send each request in its own POST.";
|
|
@@ -27981,15 +27983,27 @@ function sendAnswer(upstream, response) {
|
|
|
27981
27983
|
});
|
|
27982
27984
|
}
|
|
27983
27985
|
var self = fileURLToPath(import.meta.url);
|
|
27984
|
-
function
|
|
27985
|
-
const
|
|
27986
|
+
function changed(text3, change) {
|
|
27987
|
+
const one = (json4) => {
|
|
27986
27988
|
const message = JSON.parse(json4);
|
|
27987
|
-
|
|
27988
|
-
message.result.tools = message.result.tools.filter((tool) => !hidden.has(tool.name));
|
|
27989
|
+
change(message);
|
|
27989
27990
|
return JSON.stringify(message);
|
|
27990
27991
|
};
|
|
27991
|
-
if (text3.trimStart().startsWith("{")) return
|
|
27992
|
-
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 }] };
|
|
27993
28007
|
}
|
|
27994
28008
|
async function end(child) {
|
|
27995
28009
|
if (child.exitCode !== null || child.signalCode !== null) return;
|
|
@@ -28152,11 +28166,12 @@ var Browser = class {
|
|
|
28152
28166
|
headers,
|
|
28153
28167
|
...message ? { body: JSON.stringify(message) } : {}
|
|
28154
28168
|
});
|
|
28155
|
-
|
|
28156
|
-
|
|
28157
|
-
|
|
28158
|
-
|
|
28159
|
-
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);
|
|
28160
28175
|
}
|
|
28161
28176
|
async stop() {
|
|
28162
28177
|
this.#stopping = true;
|
|
@@ -30622,10 +30637,10 @@ function $constructor2(name, initializer32, proto, params) {
|
|
|
30622
30637
|
if (initialized) {
|
|
30623
30638
|
const own22 = Object.getPrototypeOf(inst);
|
|
30624
30639
|
const ctorProto = inst._zod.constr.prototype;
|
|
30625
|
-
let
|
|
30626
|
-
while (
|
|
30627
|
-
|
|
30628
|
-
const target =
|
|
30640
|
+
let up2 = own22;
|
|
30641
|
+
while (up2 && up2 !== ctorProto)
|
|
30642
|
+
up2 = Object.getPrototypeOf(up2);
|
|
30643
|
+
const target = up2 ?? own22;
|
|
30629
30644
|
if (!initialized.has(target)) {
|
|
30630
30645
|
initialized.add(target);
|
|
30631
30646
|
members2(target, protoMembers);
|
|
@@ -48482,21 +48497,21 @@ function visit(schema, fnOrHandlers) {
|
|
|
48482
48497
|
case "object": {
|
|
48483
48498
|
const oldShape = def.shape;
|
|
48484
48499
|
const keys = Object.keys(oldShape);
|
|
48485
|
-
let
|
|
48500
|
+
let changed22 = false;
|
|
48486
48501
|
const newShape = {};
|
|
48487
48502
|
for (const k of keys) {
|
|
48488
48503
|
const mapped = run3(oldShape[k]);
|
|
48489
48504
|
if (mapped !== oldShape[k])
|
|
48490
|
-
|
|
48505
|
+
changed22 = true;
|
|
48491
48506
|
newShape[k] = mapped;
|
|
48492
48507
|
}
|
|
48493
48508
|
let newCatchall = def.catchall;
|
|
48494
48509
|
if (def.catchall) {
|
|
48495
48510
|
newCatchall = run3(def.catchall);
|
|
48496
48511
|
if (newCatchall !== def.catchall)
|
|
48497
|
-
|
|
48512
|
+
changed22 = true;
|
|
48498
48513
|
}
|
|
48499
|
-
return
|
|
48514
|
+
return changed22 ? clone2(s, { ...def, shape: newShape, catchall: newCatchall }) : s;
|
|
48500
48515
|
}
|
|
48501
48516
|
case "array": {
|
|
48502
48517
|
const mapped = run3(def.element);
|
|
@@ -48504,21 +48519,21 @@ function visit(schema, fnOrHandlers) {
|
|
|
48504
48519
|
}
|
|
48505
48520
|
case "tuple": {
|
|
48506
48521
|
const oldItems = def.items;
|
|
48507
|
-
let
|
|
48522
|
+
let changed22 = false;
|
|
48508
48523
|
const newItems = [];
|
|
48509
48524
|
for (const item of oldItems) {
|
|
48510
48525
|
const mapped = run3(item);
|
|
48511
48526
|
if (mapped !== item)
|
|
48512
|
-
|
|
48527
|
+
changed22 = true;
|
|
48513
48528
|
newItems.push(mapped);
|
|
48514
48529
|
}
|
|
48515
48530
|
let newRest = def.rest;
|
|
48516
48531
|
if (def.rest) {
|
|
48517
48532
|
newRest = run3(def.rest);
|
|
48518
48533
|
if (newRest !== def.rest)
|
|
48519
|
-
|
|
48534
|
+
changed22 = true;
|
|
48520
48535
|
}
|
|
48521
|
-
return
|
|
48536
|
+
return changed22 ? clone2(s, { ...def, items: newItems, rest: newRest }) : s;
|
|
48522
48537
|
}
|
|
48523
48538
|
case "record":
|
|
48524
48539
|
case "map": {
|
|
@@ -48532,15 +48547,15 @@ function visit(schema, fnOrHandlers) {
|
|
|
48532
48547
|
}
|
|
48533
48548
|
case "union": {
|
|
48534
48549
|
const oldOptions = def.options;
|
|
48535
|
-
let
|
|
48550
|
+
let changed22 = false;
|
|
48536
48551
|
const newOptions = [];
|
|
48537
48552
|
for (const opt of oldOptions) {
|
|
48538
48553
|
const mapped = run3(opt);
|
|
48539
48554
|
if (mapped !== opt)
|
|
48540
|
-
|
|
48555
|
+
changed22 = true;
|
|
48541
48556
|
newOptions.push(mapped);
|
|
48542
48557
|
}
|
|
48543
|
-
return
|
|
48558
|
+
return changed22 ? clone2(s, { ...def, options: newOptions }) : s;
|
|
48544
48559
|
}
|
|
48545
48560
|
case "intersection": {
|
|
48546
48561
|
const newLeft = run3(def.left);
|
|
@@ -56621,14 +56636,14 @@ var require_validate2 = /* @__PURE__ */ __commonJSMin2(((exports) => {
|
|
|
56621
56636
|
} else {
|
|
56622
56637
|
const matches = RELATIVE_JSON_POINTER.exec($data);
|
|
56623
56638
|
if (!matches) throw new Error(`Invalid JSON-pointer: ${$data}`);
|
|
56624
|
-
const
|
|
56639
|
+
const up2 = +matches[1];
|
|
56625
56640
|
jsonPointer = matches[2];
|
|
56626
56641
|
if (jsonPointer === "#") {
|
|
56627
|
-
if (
|
|
56628
|
-
return dataPathArr[dataLevel -
|
|
56642
|
+
if (up2 >= dataLevel) throw new Error(errorMsg("property/index", up2));
|
|
56643
|
+
return dataPathArr[dataLevel - up2];
|
|
56629
56644
|
}
|
|
56630
|
-
if (
|
|
56631
|
-
data = dataNames[dataLevel -
|
|
56645
|
+
if (up2 > dataLevel) throw new Error(errorMsg("data", up2));
|
|
56646
|
+
data = dataNames[dataLevel - up2];
|
|
56632
56647
|
if (!jsonPointer) return data;
|
|
56633
56648
|
}
|
|
56634
56649
|
let expr = data;
|
|
@@ -56638,8 +56653,8 @@ var require_validate2 = /* @__PURE__ */ __commonJSMin2(((exports) => {
|
|
|
56638
56653
|
expr = (0, codegen_1._)`${expr} && ${data}`;
|
|
56639
56654
|
}
|
|
56640
56655
|
return expr;
|
|
56641
|
-
function errorMsg(pointerType,
|
|
56642
|
-
return `Cannot access ${pointerType} ${
|
|
56656
|
+
function errorMsg(pointerType, up2) {
|
|
56657
|
+
return `Cannot access ${pointerType} ${up2} levels up, current level is ${dataLevel}`;
|
|
56643
56658
|
}
|
|
56644
56659
|
}
|
|
56645
56660
|
exports.getData = getData;
|
|
@@ -65904,6 +65919,7 @@ var Sessions = class {
|
|
|
65904
65919
|
this.#room();
|
|
65905
65920
|
const started = Date.now();
|
|
65906
65921
|
const session = this.#start(input2.cmd, input2.workdir ?? this.#root);
|
|
65922
|
+
if (input2.group) session.group = input2.group;
|
|
65907
65923
|
const wait = clamp(input2.yield_time_ms ?? DEFAULT_YIELD_MS, MIN_YIELD_MS, MAX_YIELD_MS);
|
|
65908
65924
|
return this.#after(session, wait, started, input2.max_output_tokens);
|
|
65909
65925
|
}
|
|
@@ -65988,26 +66004,37 @@ var Sessions = class {
|
|
|
65988
66004
|
if (!session) throw new ExecRefused(`No terminal ${id.slice(0, 64)} is here.`);
|
|
65989
66005
|
await this.#end([session], "SIGINT");
|
|
65990
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
|
+
}
|
|
65991
66018
|
/** Stop every terminal: a polite signal, then a certain one. */
|
|
65992
66019
|
async stopAll() {
|
|
65993
66020
|
await this.#end([...this.#sessions.values()], "SIGTERM");
|
|
65994
66021
|
}
|
|
65995
66022
|
async #end(sessions, polite) {
|
|
65996
|
-
const
|
|
65997
|
-
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);
|
|
65998
66025
|
let timer;
|
|
65999
66026
|
await Promise.race([
|
|
66000
|
-
Promise.all(
|
|
66027
|
+
Promise.all(running2.map((s) => s.ended)),
|
|
66001
66028
|
new Promise((done) => {
|
|
66002
66029
|
timer = setTimeout(done, KILL_AFTER_MS);
|
|
66003
66030
|
})
|
|
66004
66031
|
]);
|
|
66005
66032
|
clearTimeout(timer);
|
|
66006
|
-
for (const session of
|
|
66033
|
+
for (const session of running2) if (session.exitCode === void 0) session.signal("SIGKILL");
|
|
66007
66034
|
}
|
|
66008
66035
|
#room() {
|
|
66009
|
-
const
|
|
66010
|
-
if (
|
|
66036
|
+
const running2 = [...this.#sessions.values()].filter((s) => s.exitCode === void 0);
|
|
66037
|
+
if (running2.length >= MAX_RUNNING)
|
|
66011
66038
|
throw new ExecRefused(`${MAX_RUNNING} commands are running. Wait for one, or stop one.`);
|
|
66012
66039
|
}
|
|
66013
66040
|
#running(id) {
|
|
@@ -66379,7 +66406,7 @@ function searched(job) {
|
|
|
66379
66406
|
const more = out.length > limit;
|
|
66380
66407
|
return (more ? [...out.slice(0, limit), `\u2026 more than ${limit} matches`] : out).join("\n");
|
|
66381
66408
|
}
|
|
66382
|
-
function
|
|
66409
|
+
function changed2(job) {
|
|
66383
66410
|
const changes = [];
|
|
66384
66411
|
let more = false;
|
|
66385
66412
|
const newest = () => {
|
|
@@ -66432,7 +66459,7 @@ function changedSince(root, since) {
|
|
|
66432
66459
|
}
|
|
66433
66460
|
if (!isMainThread && workerData?.agentexecSearch) {
|
|
66434
66461
|
const job = workerData;
|
|
66435
|
-
parentPort?.postMessage("since" in job ?
|
|
66462
|
+
parentPort?.postMessage("since" in job ? changed2(job) : searched(job));
|
|
66436
66463
|
}
|
|
66437
66464
|
var MAX_READ_BYTES = 1024 * 1024;
|
|
66438
66465
|
function createHostFiles(root, level) {
|
|
@@ -66585,6 +66612,14 @@ var Answered = {
|
|
|
66585
66612
|
session_id: external_exports.string().nullable().describe("For write_stdin, while it is still running."),
|
|
66586
66613
|
text: external_exports.string().describe("What it wrote since the last call, cut in the middle if long.")
|
|
66587
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
|
+
};
|
|
66588
66623
|
var YieldTime = external_exports.number().int().positive().optional();
|
|
66589
66624
|
var MaxTokens = external_exports.number().int().positive().optional();
|
|
66590
66625
|
var READ_ONLY = { readOnlyHint: true };
|
|
@@ -66613,6 +66648,11 @@ function createServer2(root, sessions, version22, level, extraTools = []) {
|
|
|
66613
66648
|
}
|
|
66614
66649
|
);
|
|
66615
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
|
+
);
|
|
66616
66656
|
server.registerTool(
|
|
66617
66657
|
"exec_command",
|
|
66618
66658
|
{
|
|
@@ -66626,7 +66666,7 @@ function createServer2(root, sessions, version22, level, extraTools = []) {
|
|
|
66626
66666
|
outputSchema: Answered,
|
|
66627
66667
|
annotations: { destructiveHint: true }
|
|
66628
66668
|
},
|
|
66629
|
-
(input2) => answering(() => sessions.exec(input2))
|
|
66669
|
+
(input2, ctx) => answering(async () => sessions.exec({ ...input2, group: groupOf(ctx.mcpReq._meta) }))
|
|
66630
66670
|
);
|
|
66631
66671
|
server.registerTool(
|
|
66632
66672
|
"write_stdin",
|
|
@@ -66729,7 +66769,7 @@ function createServer2(root, sessions, version22, level, extraTools = []) {
|
|
|
66729
66769
|
);
|
|
66730
66770
|
return server;
|
|
66731
66771
|
}
|
|
66732
|
-
var VERSION = "0.0.
|
|
66772
|
+
var VERSION = "0.0.8";
|
|
66733
66773
|
function createAgentExec(options) {
|
|
66734
66774
|
const root = realpathSync(resolve3(options.workspace));
|
|
66735
66775
|
const level = options.level ?? "read";
|
|
@@ -67034,8 +67074,8 @@ var BrowserLive = class {
|
|
|
67034
67074
|
}
|
|
67035
67075
|
/** The cast of a page, started if no socket was shown it yet. */
|
|
67036
67076
|
#cast(page) {
|
|
67037
|
-
const
|
|
67038
|
-
if (
|
|
67077
|
+
const running2 = this.#casts.get(page);
|
|
67078
|
+
if (running2) return running2;
|
|
67039
67079
|
const onFrame = (frame) => {
|
|
67040
67080
|
const watching = this.#shownTo(page);
|
|
67041
67081
|
if (frame.width !== cast.size.width || frame.height !== cast.size.height) {
|
|
@@ -67748,7 +67788,6 @@ var loadPty = () => {
|
|
|
67748
67788
|
return pty;
|
|
67749
67789
|
};
|
|
67750
67790
|
var ProcessRuntime = class {
|
|
67751
|
-
isolation = "none";
|
|
67752
67791
|
#config;
|
|
67753
67792
|
#contained;
|
|
67754
67793
|
/** `contained` is for inside a container, where the computer and its load are its own. */
|
|
@@ -67756,6 +67795,9 @@ var ProcessRuntime = class {
|
|
|
67756
67795
|
this.#config = config3;
|
|
67757
67796
|
this.#contained = contained;
|
|
67758
67797
|
}
|
|
67798
|
+
async available() {
|
|
67799
|
+
return true;
|
|
67800
|
+
}
|
|
67759
67801
|
async prepare() {
|
|
67760
67802
|
}
|
|
67761
67803
|
async stopLeftover() {
|
|
@@ -67850,7 +67892,7 @@ async function serveInside(options) {
|
|
|
67850
67892
|
if (!token) throw new Error(`${INNER_TOKEN_VARIABLE} is not set.`);
|
|
67851
67893
|
delete process.env[INNER_TOKEN_VARIABLE];
|
|
67852
67894
|
const digest = digestOf(token);
|
|
67853
|
-
const
|
|
67895
|
+
const running2 = new ProcessRuntime({ path: BROWSER, sandbox: false }, true).start(
|
|
67854
67896
|
{
|
|
67855
67897
|
id: "inside",
|
|
67856
67898
|
...options,
|
|
@@ -67877,13 +67919,13 @@ async function serveInside(options) {
|
|
|
67877
67919
|
if (!given || !sameToken(given, digest)) return void response.writeHead(401).end();
|
|
67878
67920
|
const { pathname } = new URL(request.url ?? "/", "http://localhost");
|
|
67879
67921
|
if (pathname === "/ready") {
|
|
67880
|
-
const { browserTools, surfaces } = await
|
|
67922
|
+
const { browserTools, surfaces } = await running2;
|
|
67881
67923
|
response.writeHead(200, { "Content-Type": "application/json" });
|
|
67882
67924
|
return void response.end(JSON.stringify({ browserTools, surfaces }));
|
|
67883
67925
|
}
|
|
67884
67926
|
const endpoint = /^\/mcp\/(computer|browser)$/.exec(pathname)?.[1];
|
|
67885
67927
|
if (!endpoint) return void response.writeHead(404).end();
|
|
67886
|
-
return (await
|
|
67928
|
+
return (await running2).handle(endpoint, request, response);
|
|
67887
67929
|
};
|
|
67888
67930
|
const server = createServer3((request, response) => {
|
|
67889
67931
|
route(request, response).catch((error62) => {
|
|
@@ -67899,14 +67941,14 @@ async function serveInside(options) {
|
|
|
67899
67941
|
const mode = url3.searchParams.get("mode");
|
|
67900
67942
|
if (!given || !sameToken(given, digest) || !surface || mode !== "watch" && mode !== "operate")
|
|
67901
67943
|
return void socket.end("HTTP/1.1 401 Unauthorized\r\nConnection: close\r\n\r\n");
|
|
67902
|
-
|
|
67944
|
+
running2.then((computer) => computer.openLive(surface, mode, request, socket, head)).catch(() => socket.destroy());
|
|
67903
67945
|
});
|
|
67904
67946
|
server.listen(INNER_PORT, "0.0.0.0");
|
|
67905
67947
|
for (const signal of ["SIGINT", "SIGTERM"])
|
|
67906
67948
|
process.once(signal, () => {
|
|
67907
|
-
void
|
|
67949
|
+
void running2.then((computer) => computer.stop()).finally(() => process.exit(0));
|
|
67908
67950
|
});
|
|
67909
|
-
await
|
|
67951
|
+
await running2;
|
|
67910
67952
|
}
|
|
67911
67953
|
|
|
67912
67954
|
// src/container.ts
|
|
@@ -67941,17 +67983,30 @@ var UPGRADE_HEADERS = [
|
|
|
67941
67983
|
var nameOf = (id) => `agentcomputer-${id}`;
|
|
67942
67984
|
var sleep5 = (ms) => new Promise((done) => setTimeout(done, ms));
|
|
67943
67985
|
var ContainerRuntime = class {
|
|
67944
|
-
isolation = "container";
|
|
67945
67986
|
#image;
|
|
67946
67987
|
constructor(image) {
|
|
67947
67988
|
this.#image = image;
|
|
67948
67989
|
}
|
|
67949
|
-
|
|
67990
|
+
/** Whether Docker answers, asked for no longer than this. */
|
|
67991
|
+
async #answers(ms) {
|
|
67950
67992
|
try {
|
|
67951
|
-
await docker
|
|
67952
|
-
|
|
67953
|
-
|
|
67993
|
+
await run2("docker", ["version", "--format", "{{.Server.Version}}"], { timeout: ms });
|
|
67994
|
+
return true;
|
|
67995
|
+
} catch {
|
|
67996
|
+
return false;
|
|
67954
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();
|
|
67955
68010
|
try {
|
|
67956
68011
|
await docker(["image", "inspect", "--format", "{{.Id}}", this.#image]);
|
|
67957
68012
|
} catch {
|
|
@@ -68092,11 +68147,14 @@ var ContainerRuntime = class {
|
|
|
68092
68147
|
}
|
|
68093
68148
|
throw new Error("Its container didn't start. Its logs say why.");
|
|
68094
68149
|
}
|
|
68150
|
+
// Neither is said to be done where Docker doesn't answer: the container would be left as it is.
|
|
68095
68151
|
async stopLeftover(id) {
|
|
68152
|
+
await this.#docker();
|
|
68096
68153
|
await docker(["stop", "--time", STOP_SECONDS, nameOf(id)]).catch(() => {
|
|
68097
68154
|
});
|
|
68098
68155
|
}
|
|
68099
68156
|
async remove(id) {
|
|
68157
|
+
await this.#docker();
|
|
68100
68158
|
await docker(["rm", "--force", nameOf(id)]).catch(() => {
|
|
68101
68159
|
});
|
|
68102
68160
|
}
|
|
@@ -68145,10 +68203,14 @@ async function forward(url3, authorization, request, response) {
|
|
|
68145
68203
|
}
|
|
68146
68204
|
|
|
68147
68205
|
// src/handlers.ts
|
|
68148
|
-
var VERSION2 = "0.0.
|
|
68206
|
+
var VERSION2 = "0.0.15";
|
|
68149
68207
|
function createHandlers(computers) {
|
|
68150
68208
|
return {
|
|
68151
|
-
getInfo: async () => ({
|
|
68209
|
+
getInfo: async () => ({
|
|
68210
|
+
version: VERSION2,
|
|
68211
|
+
isolation: computers.isolation,
|
|
68212
|
+
isolationsAvailable: await computers.available()
|
|
68213
|
+
}),
|
|
68152
68214
|
listComputers: async ({ state }) => computers.list(state),
|
|
68153
68215
|
createComputer: (input2) => computers.create(input2),
|
|
68154
68216
|
getComputer: async ({ computerID }) => computers.get(computerID),
|
|
@@ -68167,6 +68229,81 @@ function createHandlers(computers) {
|
|
|
68167
68229
|
};
|
|
68168
68230
|
}
|
|
68169
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
|
+
|
|
68170
68307
|
// node_modules/openapi-fetch/dist/index.mjs
|
|
68171
68308
|
var PATH_PARAM_RE = /\{[^{}]+\}/g;
|
|
68172
68309
|
var supportsRequestInitExt = () => {
|
|
@@ -68852,13 +68989,14 @@ var Computer = class _Computer {
|
|
|
68852
68989
|
this.files = new Files(this.#computer);
|
|
68853
68990
|
}
|
|
68854
68991
|
static async create(opts) {
|
|
68855
|
-
const { access, browser, workspace, name, timeoutMs, metadata, apiKey, apiUrl } = opts;
|
|
68992
|
+
const { access, browser, isolation, workspace, name, timeoutMs, metadata, apiKey, apiUrl } = opts;
|
|
68856
68993
|
const connection = { ...apiKey ? { apiKey } : {}, ...apiUrl ? { apiUrl } : {} };
|
|
68857
68994
|
const created = answered(
|
|
68858
68995
|
await apiOf(connection).POST("/v1/computers", {
|
|
68859
68996
|
body: {
|
|
68860
68997
|
access,
|
|
68861
68998
|
browser: browser ?? false,
|
|
68999
|
+
...isolation === void 0 ? {} : { isolation },
|
|
68862
69000
|
...workspace === void 0 ? {} : { workspace },
|
|
68863
69001
|
...name === void 0 ? {} : { name },
|
|
68864
69002
|
...timeoutMs === void 0 ? {} : { timeout: seconds(timeoutMs) },
|
|
@@ -68963,13 +69101,12 @@ var Computer = class _Computer {
|
|
|
68963
69101
|
};
|
|
68964
69102
|
|
|
68965
69103
|
// src/service.ts
|
|
68966
|
-
import { readFileSync as
|
|
69104
|
+
import { readFileSync as readFileSync6, rmSync as rmSync3 } from "node:fs";
|
|
68967
69105
|
import {
|
|
68968
69106
|
createServer as createServer5,
|
|
68969
69107
|
STATUS_CODES as STATUS_CODES3
|
|
68970
69108
|
} from "node:http";
|
|
68971
69109
|
import { createServer as createTlsServer } from "node:https";
|
|
68972
|
-
import { join as join10 } from "node:path";
|
|
68973
69110
|
|
|
68974
69111
|
// node_modules/@modelcontextprotocol/server/dist/chunk-vYduTEsO.mjs
|
|
68975
69112
|
var __create4 = Object.create;
|
|
@@ -72430,7 +72567,7 @@ var REQUEST_STATE_ONLY_LEG_PACING_MS3 = 250;
|
|
|
72430
72567
|
function inputRequiredRoundsExceededMessage3(method, maxRounds) {
|
|
72431
72568
|
return `Multi-round-trip request '${method}' still required input after ${maxRounds} rounds (inputRequired.maxRounds)`;
|
|
72432
72569
|
}
|
|
72433
|
-
function
|
|
72570
|
+
function sleep7(ms, signal) {
|
|
72434
72571
|
return new Promise((resolve6, reject) => {
|
|
72435
72572
|
if (signal?.aborted) {
|
|
72436
72573
|
reject(signal.reason instanceof SdkError3 ? signal.reason : new SdkError3(SdkErrorCode3.RequestTimeout, String(signal.reason)));
|
|
@@ -76061,14 +76198,14 @@ var require_validate3 = /* @__PURE__ */ __commonJSMin3(((exports) => {
|
|
|
76061
76198
|
} else {
|
|
76062
76199
|
const matches = RELATIVE_JSON_POINTER.exec($data);
|
|
76063
76200
|
if (!matches) throw new Error(`Invalid JSON-pointer: ${$data}`);
|
|
76064
|
-
const
|
|
76201
|
+
const up2 = +matches[1];
|
|
76065
76202
|
jsonPointer = matches[2];
|
|
76066
76203
|
if (jsonPointer === "#") {
|
|
76067
|
-
if (
|
|
76068
|
-
return dataPathArr[dataLevel -
|
|
76204
|
+
if (up2 >= dataLevel) throw new Error(errorMsg("property/index", up2));
|
|
76205
|
+
return dataPathArr[dataLevel - up2];
|
|
76069
76206
|
}
|
|
76070
|
-
if (
|
|
76071
|
-
data = dataNames[dataLevel -
|
|
76207
|
+
if (up2 > dataLevel) throw new Error(errorMsg("data", up2));
|
|
76208
|
+
data = dataNames[dataLevel - up2];
|
|
76072
76209
|
if (!jsonPointer) return data;
|
|
76073
76210
|
}
|
|
76074
76211
|
let expr = data;
|
|
@@ -76078,8 +76215,8 @@ var require_validate3 = /* @__PURE__ */ __commonJSMin3(((exports) => {
|
|
|
76078
76215
|
expr = (0, codegen_1._)`${expr} && ${data}`;
|
|
76079
76216
|
}
|
|
76080
76217
|
return expr;
|
|
76081
|
-
function errorMsg(pointerType,
|
|
76082
|
-
return `Cannot access ${pointerType} ${
|
|
76218
|
+
function errorMsg(pointerType, up2) {
|
|
76219
|
+
return `Cannot access ${pointerType} ${up2} levels up, current level is ${dataLevel}`;
|
|
76083
76220
|
}
|
|
76084
76221
|
}
|
|
76085
76222
|
exports.getData = getData;
|
|
@@ -81427,7 +81564,7 @@ var LegacyInputRequiredShim2 = class {
|
|
|
81427
81564
|
} finally {
|
|
81428
81565
|
roundAbort.dispose();
|
|
81429
81566
|
}
|
|
81430
|
-
} else await
|
|
81567
|
+
} else await sleep7(REQUEST_STATE_ONLY_LEG_PACING_MS3, outerSignal);
|
|
81431
81568
|
let ctxNext = {
|
|
81432
81569
|
...ctx,
|
|
81433
81570
|
mcpReq: {
|
|
@@ -84784,8 +84921,8 @@ var NodeStreamableHTTPServerTransport2 = class {
|
|
|
84784
84921
|
|
|
84785
84922
|
// src/computers.ts
|
|
84786
84923
|
import { randomBytes as randomBytes5 } from "node:crypto";
|
|
84787
|
-
import { appendFileSync, existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync as
|
|
84788
|
-
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";
|
|
84789
84926
|
var READ = ["about", "read", "list", "search"];
|
|
84790
84927
|
var FILES = [...READ, "edit"];
|
|
84791
84928
|
var TOOLS2 = {
|
|
@@ -84793,29 +84930,37 @@ var TOOLS2 = {
|
|
|
84793
84930
|
files: FILES,
|
|
84794
84931
|
terminal: [...FILES, "exec_command", "write_stdin", "list_sessions"]
|
|
84795
84932
|
};
|
|
84796
|
-
var
|
|
84933
|
+
var MAX_LOG2 = 256 * 1024;
|
|
84797
84934
|
var MAX_TIMER_S = 2e6;
|
|
84798
84935
|
var TICKET_MS = 6e4;
|
|
84799
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));
|
|
84800
84939
|
var Computers = class {
|
|
84801
84940
|
#data;
|
|
84802
84941
|
#base;
|
|
84803
|
-
#
|
|
84942
|
+
#runtimes;
|
|
84943
|
+
/** The isolation of a computer made without saying which. */
|
|
84944
|
+
isolation;
|
|
84804
84945
|
#stored = /* @__PURE__ */ new Map();
|
|
84805
84946
|
#live = /* @__PURE__ */ new Map();
|
|
84806
84947
|
#tickets = /* @__PURE__ */ new Map();
|
|
84807
84948
|
#file;
|
|
84808
|
-
constructor(data, base,
|
|
84949
|
+
constructor(data, base, runtimes, isolation) {
|
|
84809
84950
|
this.#data = data;
|
|
84810
84951
|
this.#base = base;
|
|
84811
|
-
this.#
|
|
84812
|
-
this
|
|
84952
|
+
this.#runtimes = runtimes;
|
|
84953
|
+
this.isolation = isolation;
|
|
84954
|
+
this.#file = join10(data, "computers.json");
|
|
84813
84955
|
if (existsSync2(this.#file))
|
|
84814
|
-
for (const entry of JSON.parse(
|
|
84815
|
-
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 });
|
|
84816
84958
|
}
|
|
84817
|
-
|
|
84818
|
-
|
|
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]);
|
|
84819
84964
|
}
|
|
84820
84965
|
/** Bring back what was running when the service last stopped. */
|
|
84821
84966
|
async resume() {
|
|
@@ -84823,12 +84968,17 @@ var Computers = class {
|
|
|
84823
84968
|
if (stored.state !== "running") continue;
|
|
84824
84969
|
if (stored.endAt && Date.parse(stored.endAt) <= Date.now()) {
|
|
84825
84970
|
stored.state = "stopped";
|
|
84826
|
-
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
|
+
);
|
|
84827
84974
|
continue;
|
|
84828
84975
|
}
|
|
84829
|
-
await this.#start(stored).catch(
|
|
84830
|
-
|
|
84831
|
-
|
|
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
|
+
});
|
|
84832
84982
|
}
|
|
84833
84983
|
this.#save();
|
|
84834
84984
|
}
|
|
@@ -84841,7 +84991,7 @@ var Computers = class {
|
|
|
84841
84991
|
if (input2.workspace !== void 0) {
|
|
84842
84992
|
let folder = false;
|
|
84843
84993
|
try {
|
|
84844
|
-
folder = isAbsolute(input2.workspace) &&
|
|
84994
|
+
folder = isAbsolute(input2.workspace) && statSync4(input2.workspace).isDirectory();
|
|
84845
84995
|
} catch {
|
|
84846
84996
|
}
|
|
84847
84997
|
if (!folder) throw new ApiError(400, `There's no folder at ${input2.workspace}.`);
|
|
@@ -84854,6 +85004,7 @@ var Computers = class {
|
|
|
84854
85004
|
id,
|
|
84855
85005
|
name: name ?? id,
|
|
84856
85006
|
access,
|
|
85007
|
+
isolation: input2.isolation ?? this.isolation,
|
|
84857
85008
|
browser,
|
|
84858
85009
|
...workspace ? { workspace } : {},
|
|
84859
85010
|
metadata: metadata ?? {},
|
|
@@ -84864,7 +85015,7 @@ var Computers = class {
|
|
|
84864
85015
|
...timeout === void 0 ? {} : { endAt: expiry(timeout) }
|
|
84865
85016
|
};
|
|
84866
85017
|
if (!workspace && access !== "none") {
|
|
84867
|
-
stored.workspace =
|
|
85018
|
+
stored.workspace = join10(this.#folder(id), "workspace");
|
|
84868
85019
|
mkdirSync2(stored.workspace, { recursive: true, mode: 448 });
|
|
84869
85020
|
}
|
|
84870
85021
|
this.#stored.set(id, stored);
|
|
@@ -84873,9 +85024,10 @@ var Computers = class {
|
|
|
84873
85024
|
} catch (error62) {
|
|
84874
85025
|
await this.#halt(id);
|
|
84875
85026
|
this.#stored.delete(id);
|
|
84876
|
-
await this.#
|
|
85027
|
+
await this.#runtimes[stored.isolation].remove(id).catch(() => {
|
|
85028
|
+
});
|
|
84877
85029
|
rmSync2(this.#folder(id), { recursive: true, force: true });
|
|
84878
|
-
throw
|
|
85030
|
+
throw refusalOf(error62);
|
|
84879
85031
|
}
|
|
84880
85032
|
this.#save();
|
|
84881
85033
|
return { ...this.#describe(stored), accessToken: token };
|
|
@@ -84930,16 +85082,22 @@ var Computers = class {
|
|
|
84930
85082
|
await this.#halt(id);
|
|
84931
85083
|
stored.state = "stopped";
|
|
84932
85084
|
this.#save();
|
|
84933
|
-
throw
|
|
85085
|
+
throw refusalOf(error62);
|
|
84934
85086
|
}
|
|
84935
85087
|
this.#save();
|
|
84936
85088
|
return this.#describe(stored);
|
|
84937
85089
|
}
|
|
84938
85090
|
/** Stop everything and remove what the service made. A folder someone named stays. */
|
|
84939
85091
|
async kill(id) {
|
|
84940
|
-
this.#found(id);
|
|
85092
|
+
const stored = this.#found(id);
|
|
84941
85093
|
await this.#halt(id);
|
|
84942
|
-
|
|
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
|
+
}
|
|
84943
85101
|
this.#stored.delete(id);
|
|
84944
85102
|
this.#save();
|
|
84945
85103
|
rmSync2(this.#folder(id), { recursive: true, force: true });
|
|
@@ -84948,7 +85106,7 @@ var Computers = class {
|
|
|
84948
85106
|
this.#found(id);
|
|
84949
85107
|
let text3 = "";
|
|
84950
85108
|
try {
|
|
84951
|
-
text3 =
|
|
85109
|
+
text3 = readFileSync5(this.#logFile(id), "utf8");
|
|
84952
85110
|
} catch {
|
|
84953
85111
|
}
|
|
84954
85112
|
return text3.split("\n").filter(Boolean).map((entry) => ({
|
|
@@ -84965,17 +85123,17 @@ var Computers = class {
|
|
|
84965
85123
|
throw new ApiError(404, "This computer has no files or terminal.");
|
|
84966
85124
|
if (endpoint === "browser" && !stored.browser)
|
|
84967
85125
|
throw new ApiError(404, "This computer has no browser.");
|
|
84968
|
-
const
|
|
84969
|
-
if (!
|
|
85126
|
+
const running2 = this.#live.get(id)?.running;
|
|
85127
|
+
if (!running2) throw new ApiError(409, "It has stopped.");
|
|
84970
85128
|
this.#log(id, `${request.method} mcp/${endpoint}`);
|
|
84971
|
-
return
|
|
85129
|
+
return running2.handle(endpoint, request, response);
|
|
84972
85130
|
}
|
|
84973
85131
|
/** A one-time ticket to open one of its surfaces live. */
|
|
84974
85132
|
liveSession(id, surface, mode) {
|
|
84975
85133
|
this.#found(id);
|
|
84976
|
-
const
|
|
84977
|
-
if (!
|
|
84978
|
-
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);
|
|
84979
85137
|
if (!info) throw new ApiError(409, `This computer has no ${surface} to open.`);
|
|
84980
85138
|
if (info.unavailable) throw new ApiError(409, info.unavailable);
|
|
84981
85139
|
if (mode === "operate" && !info.operate)
|
|
@@ -85000,10 +85158,10 @@ var Computers = class {
|
|
|
85000
85158
|
this.#tickets.delete(ticket);
|
|
85001
85159
|
if (!found || found.expiresAt <= Date.now())
|
|
85002
85160
|
throw new ApiError(404, "There's no such live session: it was used, or it expired.");
|
|
85003
|
-
const
|
|
85004
|
-
if (!
|
|
85161
|
+
const running2 = this.#live.get(found.computerID)?.running;
|
|
85162
|
+
if (!running2) throw new ApiError(409, "It has stopped.");
|
|
85005
85163
|
this.#log(found.computerID, `live ${found.surface} ${found.mode}`);
|
|
85006
|
-
await
|
|
85164
|
+
await running2.openLive(found.surface, found.mode, request, socket, head);
|
|
85007
85165
|
}
|
|
85008
85166
|
/** Stop every computer, leaving their records to resume at the next start. */
|
|
85009
85167
|
async close() {
|
|
@@ -85015,17 +85173,17 @@ var Computers = class {
|
|
|
85015
85173
|
return stored;
|
|
85016
85174
|
}
|
|
85017
85175
|
#folder(id) {
|
|
85018
|
-
return privateFolder(
|
|
85176
|
+
return privateFolder(join10(this.#data, "computers", id));
|
|
85019
85177
|
}
|
|
85020
85178
|
#logFile(id) {
|
|
85021
|
-
return
|
|
85179
|
+
return join10(this.#folder(id), "log");
|
|
85022
85180
|
}
|
|
85023
85181
|
#log(id, line) {
|
|
85024
85182
|
if (!this.#stored.has(id)) return;
|
|
85025
85183
|
try {
|
|
85026
85184
|
const file2 = this.#logFile(id);
|
|
85027
|
-
if (existsSync2(file2) &&
|
|
85028
|
-
writePrivate(file2,
|
|
85185
|
+
if (existsSync2(file2) && statSync4(file2).size > MAX_LOG2)
|
|
85186
|
+
writePrivate(file2, readFileSync5(file2, "utf8").slice(-MAX_LOG2 / 2));
|
|
85029
85187
|
appendFileSync(file2, `${(/* @__PURE__ */ new Date()).toISOString()} ${line}
|
|
85030
85188
|
`, { mode: 384 });
|
|
85031
85189
|
} catch {
|
|
@@ -85036,12 +85194,13 @@ var Computers = class {
|
|
|
85036
85194
|
}
|
|
85037
85195
|
#describe(stored) {
|
|
85038
85196
|
const path = `${this.#base()}/v1/computers/${stored.id}/mcp`;
|
|
85039
|
-
const
|
|
85197
|
+
const down2 = this.#live.get(stored.id)?.failed === true;
|
|
85040
85198
|
return {
|
|
85041
85199
|
computerID: stored.id,
|
|
85042
85200
|
name: stored.name,
|
|
85043
|
-
state: stored.state === "running" &&
|
|
85201
|
+
state: stored.state === "running" && down2 ? "failed" : stored.state,
|
|
85044
85202
|
access: stored.access,
|
|
85203
|
+
isolation: stored.isolation,
|
|
85045
85204
|
browser: stored.browser,
|
|
85046
85205
|
workspace: stored.workspace ?? null,
|
|
85047
85206
|
startedAt: stored.startedAt,
|
|
@@ -85057,7 +85216,9 @@ var Computers = class {
|
|
|
85057
85216
|
const live = {};
|
|
85058
85217
|
this.#live.set(stored.id, live);
|
|
85059
85218
|
stored.startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
85060
|
-
|
|
85219
|
+
const runtime = this.#runtimes[stored.isolation];
|
|
85220
|
+
await runtime.prepare();
|
|
85221
|
+
live.running = await runtime.start(
|
|
85061
85222
|
{
|
|
85062
85223
|
id: stored.id,
|
|
85063
85224
|
access: stored.access,
|
|
@@ -85110,7 +85271,7 @@ var OPENAPI = {
|
|
|
85110
85271
|
"openapi": "3.1.0",
|
|
85111
85272
|
"info": {
|
|
85112
85273
|
"title": "AgentComputer",
|
|
85113
|
-
"version": "0.0.
|
|
85274
|
+
"version": "0.0.15",
|
|
85114
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.",
|
|
85115
85276
|
"license": {
|
|
85116
85277
|
"name": "Apache-2.0",
|
|
@@ -85570,7 +85731,8 @@ var OPENAPI = {
|
|
|
85570
85731
|
"running",
|
|
85571
85732
|
"stopped",
|
|
85572
85733
|
"failed"
|
|
85573
|
-
]
|
|
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."
|
|
85574
85736
|
},
|
|
85575
85737
|
"Timeout": {
|
|
85576
85738
|
"type": "integer",
|
|
@@ -85611,6 +85773,10 @@ var OPENAPI = {
|
|
|
85611
85773
|
"maxLength": 4096,
|
|
85612
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."
|
|
85613
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
|
+
},
|
|
85614
85780
|
"timeout": {
|
|
85615
85781
|
"$ref": "#/components/schemas/Timeout"
|
|
85616
85782
|
},
|
|
@@ -85619,6 +85785,14 @@ var OPENAPI = {
|
|
|
85619
85785
|
}
|
|
85620
85786
|
}
|
|
85621
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
|
+
},
|
|
85622
85796
|
"Endpoints": {
|
|
85623
85797
|
"type": "object",
|
|
85624
85798
|
"required": [
|
|
@@ -85650,6 +85824,7 @@ var OPENAPI = {
|
|
|
85650
85824
|
"name",
|
|
85651
85825
|
"state",
|
|
85652
85826
|
"access",
|
|
85827
|
+
"isolation",
|
|
85653
85828
|
"browser",
|
|
85654
85829
|
"workspace",
|
|
85655
85830
|
"startedAt",
|
|
@@ -85670,6 +85845,10 @@ var OPENAPI = {
|
|
|
85670
85845
|
"access": {
|
|
85671
85846
|
"$ref": "#/components/schemas/Access"
|
|
85672
85847
|
},
|
|
85848
|
+
"isolation": {
|
|
85849
|
+
"$ref": "#/components/schemas/Isolation",
|
|
85850
|
+
"description": "The isolation it was made in. It is never run in another."
|
|
85851
|
+
},
|
|
85673
85852
|
"browser": {
|
|
85674
85853
|
"type": "boolean"
|
|
85675
85854
|
},
|
|
@@ -86828,19 +87007,23 @@ var OPENAPI = {
|
|
|
86828
87007
|
"type": "object",
|
|
86829
87008
|
"required": [
|
|
86830
87009
|
"version",
|
|
86831
|
-
"isolation"
|
|
87010
|
+
"isolation",
|
|
87011
|
+
"isolationsAvailable"
|
|
86832
87012
|
],
|
|
86833
87013
|
"properties": {
|
|
86834
87014
|
"version": {
|
|
86835
87015
|
"type": "string"
|
|
86836
87016
|
},
|
|
86837
87017
|
"isolation": {
|
|
86838
|
-
"
|
|
86839
|
-
"
|
|
86840
|
-
|
|
86841
|
-
|
|
86842
|
-
|
|
86843
|
-
"
|
|
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."
|
|
86844
87027
|
}
|
|
86845
87028
|
}
|
|
86846
87029
|
}
|
|
@@ -86868,7 +87051,8 @@ var OPERATIONS = [
|
|
|
86868
87051
|
"type": "object",
|
|
86869
87052
|
"required": [
|
|
86870
87053
|
"version",
|
|
86871
|
-
"isolation"
|
|
87054
|
+
"isolation",
|
|
87055
|
+
"isolationsAvailable"
|
|
86872
87056
|
],
|
|
86873
87057
|
"properties": {
|
|
86874
87058
|
"version": {
|
|
@@ -86880,7 +87064,19 @@ var OPERATIONS = [
|
|
|
86880
87064
|
"none",
|
|
86881
87065
|
"container"
|
|
86882
87066
|
],
|
|
86883
|
-
"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."
|
|
86884
87080
|
}
|
|
86885
87081
|
}
|
|
86886
87082
|
},
|
|
@@ -86908,7 +87104,8 @@ var OPERATIONS = [
|
|
|
86908
87104
|
"running",
|
|
86909
87105
|
"stopped",
|
|
86910
87106
|
"failed"
|
|
86911
|
-
]
|
|
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."
|
|
86912
87109
|
}
|
|
86913
87110
|
},
|
|
86914
87111
|
"required": []
|
|
@@ -86922,6 +87119,7 @@ var OPERATIONS = [
|
|
|
86922
87119
|
"name",
|
|
86923
87120
|
"state",
|
|
86924
87121
|
"access",
|
|
87122
|
+
"isolation",
|
|
86925
87123
|
"browser",
|
|
86926
87124
|
"workspace",
|
|
86927
87125
|
"startedAt",
|
|
@@ -86942,7 +87140,8 @@ var OPERATIONS = [
|
|
|
86942
87140
|
"running",
|
|
86943
87141
|
"stopped",
|
|
86944
87142
|
"failed"
|
|
86945
|
-
]
|
|
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."
|
|
86946
87145
|
},
|
|
86947
87146
|
"access": {
|
|
86948
87147
|
"type": "string",
|
|
@@ -86954,6 +87153,14 @@ var OPERATIONS = [
|
|
|
86954
87153
|
],
|
|
86955
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."
|
|
86956
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
|
+
},
|
|
86957
87164
|
"browser": {
|
|
86958
87165
|
"type": "boolean"
|
|
86959
87166
|
},
|
|
@@ -87053,6 +87260,14 @@ var OPERATIONS = [
|
|
|
87053
87260
|
"maxLength": 4096,
|
|
87054
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."
|
|
87055
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
|
+
},
|
|
87056
87271
|
"timeout": {
|
|
87057
87272
|
"type": "integer",
|
|
87058
87273
|
"minimum": 1,
|
|
@@ -87081,6 +87296,7 @@ var OPERATIONS = [
|
|
|
87081
87296
|
"name",
|
|
87082
87297
|
"state",
|
|
87083
87298
|
"access",
|
|
87299
|
+
"isolation",
|
|
87084
87300
|
"browser",
|
|
87085
87301
|
"workspace",
|
|
87086
87302
|
"startedAt",
|
|
@@ -87101,7 +87317,8 @@ var OPERATIONS = [
|
|
|
87101
87317
|
"running",
|
|
87102
87318
|
"stopped",
|
|
87103
87319
|
"failed"
|
|
87104
|
-
]
|
|
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."
|
|
87105
87322
|
},
|
|
87106
87323
|
"access": {
|
|
87107
87324
|
"type": "string",
|
|
@@ -87113,6 +87330,14 @@ var OPERATIONS = [
|
|
|
87113
87330
|
],
|
|
87114
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."
|
|
87115
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
|
+
},
|
|
87116
87341
|
"browser": {
|
|
87117
87342
|
"type": "boolean"
|
|
87118
87343
|
},
|
|
@@ -87221,6 +87446,7 @@ var OPERATIONS = [
|
|
|
87221
87446
|
"name",
|
|
87222
87447
|
"state",
|
|
87223
87448
|
"access",
|
|
87449
|
+
"isolation",
|
|
87224
87450
|
"browser",
|
|
87225
87451
|
"workspace",
|
|
87226
87452
|
"startedAt",
|
|
@@ -87241,7 +87467,8 @@ var OPERATIONS = [
|
|
|
87241
87467
|
"running",
|
|
87242
87468
|
"stopped",
|
|
87243
87469
|
"failed"
|
|
87244
|
-
]
|
|
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."
|
|
87245
87472
|
},
|
|
87246
87473
|
"access": {
|
|
87247
87474
|
"type": "string",
|
|
@@ -87253,6 +87480,14 @@ var OPERATIONS = [
|
|
|
87253
87480
|
],
|
|
87254
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."
|
|
87255
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
|
+
},
|
|
87256
87491
|
"browser": {
|
|
87257
87492
|
"type": "boolean"
|
|
87258
87493
|
},
|
|
@@ -87452,6 +87687,7 @@ var OPERATIONS = [
|
|
|
87452
87687
|
"name",
|
|
87453
87688
|
"state",
|
|
87454
87689
|
"access",
|
|
87690
|
+
"isolation",
|
|
87455
87691
|
"browser",
|
|
87456
87692
|
"workspace",
|
|
87457
87693
|
"startedAt",
|
|
@@ -87472,7 +87708,8 @@ var OPERATIONS = [
|
|
|
87472
87708
|
"running",
|
|
87473
87709
|
"stopped",
|
|
87474
87710
|
"failed"
|
|
87475
|
-
]
|
|
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."
|
|
87476
87713
|
},
|
|
87477
87714
|
"access": {
|
|
87478
87715
|
"type": "string",
|
|
@@ -87484,6 +87721,14 @@ var OPERATIONS = [
|
|
|
87484
87721
|
],
|
|
87485
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."
|
|
87486
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
|
+
},
|
|
87487
87732
|
"browser": {
|
|
87488
87733
|
"type": "boolean"
|
|
87489
87734
|
},
|
|
@@ -87576,6 +87821,7 @@ var OPERATIONS = [
|
|
|
87576
87821
|
"name",
|
|
87577
87822
|
"state",
|
|
87578
87823
|
"access",
|
|
87824
|
+
"isolation",
|
|
87579
87825
|
"browser",
|
|
87580
87826
|
"workspace",
|
|
87581
87827
|
"startedAt",
|
|
@@ -87596,7 +87842,8 @@ var OPERATIONS = [
|
|
|
87596
87842
|
"running",
|
|
87597
87843
|
"stopped",
|
|
87598
87844
|
"failed"
|
|
87599
|
-
]
|
|
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."
|
|
87600
87847
|
},
|
|
87601
87848
|
"access": {
|
|
87602
87849
|
"type": "string",
|
|
@@ -87608,6 +87855,14 @@ var OPERATIONS = [
|
|
|
87608
87855
|
],
|
|
87609
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."
|
|
87610
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
|
+
},
|
|
87611
87866
|
"browser": {
|
|
87612
87867
|
"type": "boolean"
|
|
87613
87868
|
},
|
|
@@ -87705,6 +87960,7 @@ var OPERATIONS = [
|
|
|
87705
87960
|
"name",
|
|
87706
87961
|
"state",
|
|
87707
87962
|
"access",
|
|
87963
|
+
"isolation",
|
|
87708
87964
|
"browser",
|
|
87709
87965
|
"workspace",
|
|
87710
87966
|
"startedAt",
|
|
@@ -87725,7 +87981,8 @@ var OPERATIONS = [
|
|
|
87725
87981
|
"running",
|
|
87726
87982
|
"stopped",
|
|
87727
87983
|
"failed"
|
|
87728
|
-
]
|
|
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."
|
|
87729
87986
|
},
|
|
87730
87987
|
"access": {
|
|
87731
87988
|
"type": "string",
|
|
@@ -87737,6 +87994,14 @@ var OPERATIONS = [
|
|
|
87737
87994
|
],
|
|
87738
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."
|
|
87739
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
|
+
},
|
|
87740
88005
|
"browser": {
|
|
87741
88006
|
"type": "boolean"
|
|
87742
88007
|
},
|
|
@@ -88024,9 +88289,12 @@ async function serve(options) {
|
|
|
88024
88289
|
checkExposure(host, options);
|
|
88025
88290
|
const data = privateFolder(options.data);
|
|
88026
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}.`);
|
|
88027
88295
|
let base = "";
|
|
88028
|
-
await options.
|
|
88029
|
-
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);
|
|
88030
88298
|
const handlers = createHandlers(computers);
|
|
88031
88299
|
const routes = OPERATIONS.map((operation) => ({
|
|
88032
88300
|
operation,
|
|
@@ -88114,7 +88382,7 @@ async function serve(options) {
|
|
|
88114
88382
|
});
|
|
88115
88383
|
};
|
|
88116
88384
|
const server = options.tls ? createTlsServer(
|
|
88117
|
-
{ cert:
|
|
88385
|
+
{ cert: readFileSync6(options.tls.cert), key: readFileSync6(options.tls.key) },
|
|
88118
88386
|
listener
|
|
88119
88387
|
) : createServer5(listener);
|
|
88120
88388
|
const allowed = new Set(options.allowOrigins ?? []);
|
|
@@ -88146,11 +88414,12 @@ ${JSON.stringify({ code: status, message })}
|
|
|
88146
88414
|
const bound = server.address();
|
|
88147
88415
|
const scheme = options.tls ? "https" : "http";
|
|
88148
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 });
|
|
88149
88419
|
await computers.resume();
|
|
88150
|
-
const record3 = join10(data, "server.json");
|
|
88151
88420
|
writePrivate(
|
|
88152
88421
|
record3,
|
|
88153
|
-
JSON.stringify({ url: base, pid: process.pid, isolation: options.
|
|
88422
|
+
JSON.stringify({ url: base, pid: process.pid, isolation: options.isolation })
|
|
88154
88423
|
);
|
|
88155
88424
|
process.stdout.write(`AgentComputer on ${base}, data in ${data}
|
|
88156
88425
|
`);
|
|
@@ -88176,9 +88445,14 @@ var USAGE = `Usage: agentcomputer <command> [--data DIR]
|
|
|
88176
88445
|
[--browser-name NAME] [--browser-path PATH]
|
|
88177
88446
|
run the service; 127.0.0.1:7878 by default, and
|
|
88178
88447
|
anywhere else only with TLS
|
|
88179
|
-
|
|
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
|
|
88180
88454
|
create --access none|read|files|terminal [--browser] [--workspace DIR] [--name NAME]
|
|
88181
|
-
[--timeout SECONDS] [--metadata KEY=VALUE]...
|
|
88455
|
+
[--isolation none|container] [--timeout SECONDS] [--metadata KEY=VALUE]...
|
|
88182
88456
|
make a computer; prints its endpoints and token, once
|
|
88183
88457
|
list [--state running|stopped|failed]
|
|
88184
88458
|
the computers
|
|
@@ -88191,6 +88465,8 @@ var USAGE = `Usage: agentcomputer <command> [--data DIR]
|
|
|
88191
88465
|
a one-time ticket to open one live, as a WebSocket
|
|
88192
88466
|
kill <id> stop it and remove what the service made
|
|
88193
88467
|
|
|
88468
|
+
--version prints this program's version, with no service.
|
|
88469
|
+
|
|
88194
88470
|
The data folder is --data, else $AGENTCOMPUTER_DATA, else $XDG_DATA_HOME/agentcomputer, else
|
|
88195
88471
|
~/.local/share/agentcomputer. Commands find the running service there, with its control token.
|
|
88196
88472
|
`;
|
|
@@ -88203,20 +88479,18 @@ function dataFolder(given) {
|
|
|
88203
88479
|
given ?? process.env.AGENTCOMPUTER_DATA ?? (xdg && isAbsolute2(xdg) ? join11(xdg, "agentcomputer") : join11(homedir2(), ".local", "share", "agentcomputer"))
|
|
88204
88480
|
);
|
|
88205
88481
|
}
|
|
88482
|
+
var none = (data) => `No service is running with its data in ${data}. Start one: agentcomputer up`;
|
|
88206
88483
|
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
|
-
}
|
|
88484
|
+
const said = recorded(data);
|
|
88485
|
+
if (!said) throw new Error(none(data));
|
|
88486
|
+
return { apiUrl: said.url, apiKey: said.token };
|
|
88215
88487
|
}
|
|
88216
88488
|
async function main() {
|
|
88217
|
-
const { values, positionals } = parseArgs({
|
|
88489
|
+
const { values, positionals, tokens } = parseArgs({
|
|
88218
88490
|
allowPositionals: true,
|
|
88491
|
+
tokens: true,
|
|
88219
88492
|
options: {
|
|
88493
|
+
version: { type: "boolean" },
|
|
88220
88494
|
data: { type: "string" },
|
|
88221
88495
|
listen: { type: "string" },
|
|
88222
88496
|
isolation: { type: "string" },
|
|
@@ -88247,19 +88521,38 @@ async function main() {
|
|
|
88247
88521
|
}
|
|
88248
88522
|
});
|
|
88249
88523
|
const [command, id, extra] = positionals;
|
|
88524
|
+
if (values.version) return void process.stdout.write(`${VERSION2}
|
|
88525
|
+
`);
|
|
88250
88526
|
if (values.help || !command) return void process.stdout.write(USAGE);
|
|
88251
88527
|
const data = dataFolder(values.data);
|
|
88252
88528
|
const out = (value) => process.stdout.write(`${JSON.stringify(value, null, 2)}
|
|
88253
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
|
+
}
|
|
88254
88547
|
const idOf = () => {
|
|
88255
88548
|
if (!id) throw new UsageError(`${command} takes a computer's id.`);
|
|
88256
88549
|
return id;
|
|
88257
88550
|
};
|
|
88258
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.");
|
|
88259
88555
|
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
88556
|
const { "tls-cert": cert, "tls-key": key } = values;
|
|
88264
88557
|
if (Boolean(cert) !== Boolean(key))
|
|
88265
88558
|
throw new UsageError("--tls-cert and --tls-key go together.");
|
|
@@ -88270,10 +88563,11 @@ async function main() {
|
|
|
88270
88563
|
tlsProxy: values["tls-proxy"],
|
|
88271
88564
|
publicUrl: values["public-url"],
|
|
88272
88565
|
allowOrigins: values["allow-origin"],
|
|
88273
|
-
|
|
88274
|
-
|
|
88275
|
-
path: values["browser-path"]
|
|
88276
|
-
|
|
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
|
+
}
|
|
88277
88571
|
});
|
|
88278
88572
|
}
|
|
88279
88573
|
if (command === "browser-host")
|
|
@@ -88303,6 +88597,7 @@ async function main() {
|
|
|
88303
88597
|
...connection,
|
|
88304
88598
|
access: values.access,
|
|
88305
88599
|
...values.browser ? { browser: true } : {},
|
|
88600
|
+
...isolation ? { isolation } : {},
|
|
88306
88601
|
...values.workspace ? { workspace: resolve5(values.workspace) } : {},
|
|
88307
88602
|
...values.name ? { name: values.name } : {},
|
|
88308
88603
|
...values.timeout ? { timeoutMs: milliseconds(values.timeout) } : {},
|