@agentorg/computer 0.0.8 → 0.0.9
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 +18 -12
- package/dist/THIRD_PARTY_LICENSES.txt +1 -1
- package/dist/agentcomputer.js +712 -335
- package/dist/sdk.js +34 -34
- package/dist/types/generated/schema.d.ts +66 -4
- package/dist/types/sdk.d.ts +23 -30
- package/package.json +2 -2
package/dist/agentcomputer.js
CHANGED
|
@@ -3753,7 +3753,7 @@ var require_websocket_server = __commonJS({
|
|
|
3753
3753
|
// src/main.ts
|
|
3754
3754
|
import { readFileSync as readFileSync6 } from "node:fs";
|
|
3755
3755
|
import { homedir as homedir2 } from "node:os";
|
|
3756
|
-
import { isAbsolute as isAbsolute2, join as
|
|
3756
|
+
import { isAbsolute as isAbsolute2, join as join11, resolve as resolve5 } from "node:path";
|
|
3757
3757
|
import { parseArgs } from "node:util";
|
|
3758
3758
|
import { isMainThread as isMainThread2 } from "node:worker_threads";
|
|
3759
3759
|
|
|
@@ -3849,7 +3849,7 @@ async function serveBrowserMcp(options) {
|
|
|
3849
3849
|
import { execFile, spawn as spawn3 } from "node:child_process";
|
|
3850
3850
|
import { readFileSync as readFileSync3 } from "node:fs";
|
|
3851
3851
|
import { connect } from "node:net";
|
|
3852
|
-
import { join as
|
|
3852
|
+
import { join as join8 } from "node:path";
|
|
3853
3853
|
import { createInterface as createInterface2 } from "node:readline";
|
|
3854
3854
|
import { Readable as Readable3 } from "node:stream";
|
|
3855
3855
|
import { promisify } from "node:util";
|
|
@@ -6616,10 +6616,10 @@ var $ZodUnion = /* @__PURE__ */ $constructor("$ZodUnion", (inst, def) => {
|
|
|
6616
6616
|
}
|
|
6617
6617
|
return void 0;
|
|
6618
6618
|
});
|
|
6619
|
-
const
|
|
6619
|
+
const first2 = def.options.length === 1 ? def.options[0]._zod.run : null;
|
|
6620
6620
|
inst._zod.parse = (payload, ctx) => {
|
|
6621
|
-
if (
|
|
6622
|
-
return
|
|
6621
|
+
if (first2) {
|
|
6622
|
+
return first2(payload, ctx);
|
|
6623
6623
|
}
|
|
6624
6624
|
let async = false;
|
|
6625
6625
|
const results = [];
|
|
@@ -9017,9 +9017,9 @@ var numberProcessor = (schema, ctx, _json, params) => {
|
|
|
9017
9017
|
else
|
|
9018
9018
|
handleUnrepresentable(schema, ctx, json4, params, `A multipleOf divisor of ${divisor} cannot be represented in JSON Schema`);
|
|
9019
9019
|
}
|
|
9020
|
-
const [
|
|
9021
|
-
if (
|
|
9022
|
-
json4.multipleOf =
|
|
9020
|
+
const [first2, ...rest] = divisors;
|
|
9021
|
+
if (first2 !== void 0)
|
|
9022
|
+
json4.multipleOf = first2;
|
|
9023
9023
|
if (rest.length)
|
|
9024
9024
|
json4.allOf = [...json4.allOf ?? [], ...rest.map((m) => ({ multipleOf: m }))];
|
|
9025
9025
|
}
|
|
@@ -19229,8 +19229,8 @@ var require_utils = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
|
19229
19229
|
}
|
|
19230
19230
|
if (bestLength < 2) return hextets.join(":");
|
|
19231
19231
|
const head = hextets.slice(0, bestStart).join(":");
|
|
19232
|
-
const
|
|
19233
|
-
return head + "::" +
|
|
19232
|
+
const tail2 = hextets.slice(bestStart + bestLength).join(":");
|
|
19233
|
+
return head + "::" + tail2;
|
|
19234
19234
|
}
|
|
19235
19235
|
function normalizeIPv6Address(input2) {
|
|
19236
19236
|
const compression = input2.indexOf("::");
|
|
@@ -28153,10 +28153,12 @@ var Browser = class {
|
|
|
28153
28153
|
// src/inside.ts
|
|
28154
28154
|
import { createServer as createServer3 } from "node:http";
|
|
28155
28155
|
import { homedir } from "node:os";
|
|
28156
|
-
import { join as
|
|
28156
|
+
import { join as join7 } from "node:path";
|
|
28157
28157
|
|
|
28158
28158
|
// src/runtime.ts
|
|
28159
|
-
import {
|
|
28159
|
+
import { chmodSync as chmodSync2, existsSync, statSync as statSync2 } from "node:fs";
|
|
28160
|
+
import { createRequire as createRequire3 } from "node:module";
|
|
28161
|
+
import { dirname as dirname3, join as join6 } from "node:path";
|
|
28160
28162
|
|
|
28161
28163
|
// node_modules/@agentorg/exec/dist/lib.js
|
|
28162
28164
|
import { realpathSync } from "node:fs";
|
|
@@ -28959,11 +28961,11 @@ var require_ignore = __commonJS2({
|
|
|
28959
28961
|
return true;
|
|
28960
28962
|
};
|
|
28961
28963
|
var isNotRelative = (path) => {
|
|
28962
|
-
const
|
|
28963
|
-
if (
|
|
28964
|
+
const first2 = path.charCodeAt(0);
|
|
28965
|
+
if (first2 === SLASH_CODE) {
|
|
28964
28966
|
return true;
|
|
28965
28967
|
}
|
|
28966
|
-
if (
|
|
28968
|
+
if (first2 !== DOT_CODE) {
|
|
28967
28969
|
return false;
|
|
28968
28970
|
}
|
|
28969
28971
|
if (path.length === 1) {
|
|
@@ -32872,10 +32874,10 @@ var $ZodUnion2 = /* @__PURE__ */ $constructor2("$ZodUnion", (inst, def) => {
|
|
|
32872
32874
|
}
|
|
32873
32875
|
return void 0;
|
|
32874
32876
|
});
|
|
32875
|
-
const
|
|
32877
|
+
const first2 = def.options.length === 1 ? def.options[0]._zod.run : null;
|
|
32876
32878
|
inst._zod.parse = (payload, ctx) => {
|
|
32877
|
-
if (
|
|
32878
|
-
return
|
|
32879
|
+
if (first2) {
|
|
32880
|
+
return first2(payload, ctx);
|
|
32879
32881
|
}
|
|
32880
32882
|
let async = false;
|
|
32881
32883
|
const results = [];
|
|
@@ -32932,10 +32934,10 @@ function handleExclusiveUnionResults(results, final, inst, ctx) {
|
|
|
32932
32934
|
var $ZodXor2 = /* @__PURE__ */ $constructor2("$ZodXor", (inst, def) => {
|
|
32933
32935
|
$ZodUnion2.init(inst, def);
|
|
32934
32936
|
def.inclusive = false;
|
|
32935
|
-
const
|
|
32937
|
+
const first2 = def.options.length === 1 ? def.options[0]._zod.run : null;
|
|
32936
32938
|
inst._zod.parse = (payload, ctx) => {
|
|
32937
|
-
if (
|
|
32938
|
-
return
|
|
32939
|
+
if (first2) {
|
|
32940
|
+
return first2(payload, ctx);
|
|
32939
32941
|
}
|
|
32940
32942
|
let async = false;
|
|
32941
32943
|
const results = [];
|
|
@@ -45270,9 +45272,9 @@ var numberProcessor2 = (schema, ctx, _json, params) => {
|
|
|
45270
45272
|
else
|
|
45271
45273
|
handleUnrepresentable2(schema, ctx, json32, params, `A multipleOf divisor of ${divisor} cannot be represented in JSON Schema`);
|
|
45272
45274
|
}
|
|
45273
|
-
const [
|
|
45274
|
-
if (
|
|
45275
|
-
json32.multipleOf =
|
|
45275
|
+
const [first2, ...rest] = divisors;
|
|
45276
|
+
if (first2 !== void 0)
|
|
45277
|
+
json32.multipleOf = first2;
|
|
45276
45278
|
if (rest.length)
|
|
45277
45279
|
json32.allOf = [...json32.allOf ?? [], ...rest.map((m) => ({ multipleOf: m }))];
|
|
45278
45280
|
}
|
|
@@ -47956,14 +47958,14 @@ function checkArrayGuards(arraySchema, guards) {
|
|
|
47956
47958
|
const key = canonicalKey(items[i], /* @__PURE__ */ new Set());
|
|
47957
47959
|
if (key === null)
|
|
47958
47960
|
continue;
|
|
47959
|
-
const
|
|
47960
|
-
if (
|
|
47961
|
+
const first2 = firstSeen.get(key);
|
|
47962
|
+
if (first2 === void 0) {
|
|
47961
47963
|
firstSeen.set(key, i);
|
|
47962
47964
|
continue;
|
|
47963
47965
|
}
|
|
47964
47966
|
payload.issues.push({
|
|
47965
47967
|
code: "custom",
|
|
47966
|
-
message: `Array items must be unique: element at index ${i} duplicates the one at index ${
|
|
47968
|
+
message: `Array items must be unique: element at index ${i} duplicates the one at index ${first2}`,
|
|
47967
47969
|
input: items,
|
|
47968
47970
|
path: [i],
|
|
47969
47971
|
continue: true
|
|
@@ -56945,8 +56947,8 @@ var require_utils2 = /* @__PURE__ */ __commonJSMin2(((exports, module) => {
|
|
|
56945
56947
|
}
|
|
56946
56948
|
if (bestLength < 2) return hextets.join(":");
|
|
56947
56949
|
const head = hextets.slice(0, bestStart).join(":");
|
|
56948
|
-
const
|
|
56949
|
-
return head + "::" +
|
|
56950
|
+
const tail2 = hextets.slice(bestStart + bestLength).join(":");
|
|
56951
|
+
return head + "::" + tail2;
|
|
56950
56952
|
}
|
|
56951
56953
|
function normalizeIPv6Address(input2) {
|
|
56952
56954
|
const compression = input2.indexOf("::");
|
|
@@ -63419,9 +63421,9 @@ var WebStandardStreamableHTTPServerTransport = class {
|
|
|
63419
63421
|
}
|
|
63420
63422
|
});
|
|
63421
63423
|
}
|
|
63422
|
-
async _checkScopeChallenge(
|
|
63424
|
+
async _checkScopeChallenge(messages2, authInfo) {
|
|
63423
63425
|
if (!this._scopeChallengeResolver) return;
|
|
63424
|
-
const challenge = await findScopeChallenge(
|
|
63426
|
+
const challenge = await findScopeChallenge(messages2.filter((message) => isJSONRPCRequest2(message)), authInfo, this._scopeChallengeResolver);
|
|
63425
63427
|
return challenge === void 0 ? void 0 : createScopeChallengeResponse(challenge, scopeChallengeResourceMetadataUrl(authInfo));
|
|
63426
63428
|
}
|
|
63427
63429
|
/**
|
|
@@ -63719,21 +63721,21 @@ data:
|
|
|
63719
63721
|
this.onerror?.(/* @__PURE__ */ new Error(`Invalid Request: Batch must not exceed ${MAX_BATCH_SIZE} messages`));
|
|
63720
63722
|
return this.createJsonErrorResponse(400, -32600, `Invalid Request: Batch must not exceed ${MAX_BATCH_SIZE} messages`);
|
|
63721
63723
|
}
|
|
63722
|
-
let
|
|
63724
|
+
let messages2;
|
|
63723
63725
|
try {
|
|
63724
|
-
|
|
63726
|
+
messages2 = Array.isArray(rawMessage) ? rawMessage.map((msg) => JSONRPCMessageSchema2.parse(msg)) : [JSONRPCMessageSchema2.parse(rawMessage)];
|
|
63725
63727
|
} catch (error62) {
|
|
63726
63728
|
this.onerror?.(error62);
|
|
63727
63729
|
return this.createJsonErrorResponse(400, -32700, "Parse error: Invalid JSON-RPC message");
|
|
63728
63730
|
}
|
|
63729
63731
|
if (this._closed) return this.createJsonErrorResponse(404, -32001, "Session not found");
|
|
63730
|
-
const isInitializationRequest =
|
|
63732
|
+
const isInitializationRequest = messages2.some((element) => isInitializeRequest2(element));
|
|
63731
63733
|
if (isInitializationRequest) {
|
|
63732
63734
|
if (this._initialized && this.sessionId !== void 0) {
|
|
63733
63735
|
this.onerror?.(/* @__PURE__ */ new Error("Invalid Request: Server already initialized"));
|
|
63734
63736
|
return this.createJsonErrorResponse(400, -32600, "Invalid Request: Server already initialized");
|
|
63735
63737
|
}
|
|
63736
|
-
if (
|
|
63738
|
+
if (messages2.length > 1) {
|
|
63737
63739
|
this.onerror?.(/* @__PURE__ */ new Error("Invalid Request: Only one initialization request is allowed"));
|
|
63738
63740
|
return this.createJsonErrorResponse(400, -32600, "Invalid Request: Only one initialization request is allowed");
|
|
63739
63741
|
}
|
|
@@ -63750,21 +63752,21 @@ data:
|
|
|
63750
63752
|
if (this._closed) return this.createJsonErrorResponse(404, -32001, "Session not found");
|
|
63751
63753
|
let scopeChallengeResponse;
|
|
63752
63754
|
try {
|
|
63753
|
-
scopeChallengeResponse = await this._checkScopeChallenge(
|
|
63755
|
+
scopeChallengeResponse = await this._checkScopeChallenge(messages2, options?.authInfo);
|
|
63754
63756
|
} catch (error62) {
|
|
63755
63757
|
this.onerror?.(error62);
|
|
63756
63758
|
return this.createJsonErrorResponse(500, -32603, "Internal server error");
|
|
63757
63759
|
}
|
|
63758
63760
|
if (scopeChallengeResponse) return scopeChallengeResponse;
|
|
63759
|
-
if (!
|
|
63760
|
-
for (const message of
|
|
63761
|
+
if (!messages2.some((element) => isJSONRPCRequest2(element))) {
|
|
63762
|
+
for (const message of messages2) this.onmessage?.(message, {
|
|
63761
63763
|
authInfo: options?.authInfo,
|
|
63762
63764
|
request
|
|
63763
63765
|
});
|
|
63764
63766
|
return new Response(null, { status: 202 });
|
|
63765
63767
|
}
|
|
63766
63768
|
const streamId = crypto.randomUUID();
|
|
63767
|
-
const initRequest =
|
|
63769
|
+
const initRequest = messages2.find((m) => isInitializeRequest2(m));
|
|
63768
63770
|
const clientProtocolVersion = initRequest ? initRequest.params.protocolVersion : req.headers.get("mcp-protocol-version") ?? DEFAULT_NEGOTIATED_PROTOCOL_VERSION2;
|
|
63769
63771
|
if (this._enableJsonResponse) return new Promise((resolve42) => {
|
|
63770
63772
|
this._streamMapping.set(streamId, {
|
|
@@ -63773,8 +63775,8 @@ data:
|
|
|
63773
63775
|
this._streamMapping.delete(streamId);
|
|
63774
63776
|
}
|
|
63775
63777
|
});
|
|
63776
|
-
for (const message of
|
|
63777
|
-
for (const message of
|
|
63778
|
+
for (const message of messages2) if (isJSONRPCRequest2(message)) this._requestToStreamMapping.set(message.id, streamId);
|
|
63779
|
+
for (const message of messages2) this.onmessage?.(message, {
|
|
63778
63780
|
authInfo: options?.authInfo,
|
|
63779
63781
|
request
|
|
63780
63782
|
});
|
|
@@ -63798,7 +63800,7 @@ data:
|
|
|
63798
63800
|
"X-Accel-Buffering": "no"
|
|
63799
63801
|
};
|
|
63800
63802
|
if (this.sessionId !== void 0) headers["mcp-session-id"] = this.sessionId;
|
|
63801
|
-
for (const message of
|
|
63803
|
+
for (const message of messages2) if (isJSONRPCRequest2(message)) {
|
|
63802
63804
|
this._streamMapping.set(streamId, {
|
|
63803
63805
|
controller: streamController,
|
|
63804
63806
|
encoder,
|
|
@@ -63814,7 +63816,7 @@ data:
|
|
|
63814
63816
|
this._requestToStreamMapping.set(message.id, streamId);
|
|
63815
63817
|
}
|
|
63816
63818
|
await this.writePrimingEvent(streamController, encoder, streamId, clientProtocolVersion);
|
|
63817
|
-
for (const message of
|
|
63819
|
+
for (const message of messages2) {
|
|
63818
63820
|
let closeSSEStream;
|
|
63819
63821
|
let closeStandaloneSSEStream;
|
|
63820
63822
|
if (isJSONRPCRequest2(message) && this._eventStore && this.supportsEmptySSEData(clientProtocolVersion)) {
|
|
@@ -65811,10 +65813,10 @@ function cut(text22, max) {
|
|
|
65811
65813
|
if (text22.length <= max) return text22;
|
|
65812
65814
|
const half = Math.max(1, Math.floor(max / 2));
|
|
65813
65815
|
const head = text22.slice(0, half).replace(/[\uD800-\uDBFF]$/, "");
|
|
65814
|
-
const
|
|
65816
|
+
const tail2 = text22.slice(-half).replace(/^[\uDC00-\uDFFF]/, "");
|
|
65815
65817
|
return `${head}
|
|
65816
|
-
\u2026 ${text22.length - head.length -
|
|
65817
|
-
${
|
|
65818
|
+
\u2026 ${text22.length - head.length - tail2.length} characters cut \u2026
|
|
65819
|
+
${tail2}`;
|
|
65818
65820
|
}
|
|
65819
65821
|
var Pending = class {
|
|
65820
65822
|
#head = "";
|
|
@@ -65850,6 +65852,10 @@ var DEFAULT_YIELD_MS = 1e4;
|
|
|
65850
65852
|
var MIN_POLL_MS = 5e3;
|
|
65851
65853
|
var WRITE_YIELD_MS = 250;
|
|
65852
65854
|
var MAX_RUNNING = 64;
|
|
65855
|
+
var MAX_ENDED = 64;
|
|
65856
|
+
var KEPT = 64 * 1024;
|
|
65857
|
+
var CMD_SHOWN = 200;
|
|
65858
|
+
var MAX_SIZE = 1e3;
|
|
65853
65859
|
var DEFAULT_MAX_TOKENS = 7e3;
|
|
65854
65860
|
var DRAIN_MS = 200;
|
|
65855
65861
|
var KILL_AFTER_MS = 1e3;
|
|
@@ -65863,48 +65869,183 @@ var SIGNALS = {
|
|
|
65863
65869
|
SIGTERM: 15
|
|
65864
65870
|
};
|
|
65865
65871
|
var clamp = (value, low, high) => Math.min(Math.max(value, low), high);
|
|
65872
|
+
var tail = (text22) => text22.slice(-KEPT).replace(/^[\uDC00-\uDFFF]/, "");
|
|
65866
65873
|
var Sessions = class {
|
|
65867
65874
|
#sessions = /* @__PURE__ */ new Map();
|
|
65875
|
+
/** Those that have ended and are still listed, in the order they ended. */
|
|
65876
|
+
#ended = [];
|
|
65868
65877
|
#root;
|
|
65869
65878
|
#emit;
|
|
65879
|
+
#pty;
|
|
65870
65880
|
constructor(root, emit = () => {
|
|
65871
|
-
}) {
|
|
65881
|
+
}, pty2) {
|
|
65872
65882
|
this.#root = root;
|
|
65873
65883
|
this.#emit = emit;
|
|
65884
|
+
this.#pty = pty2;
|
|
65874
65885
|
}
|
|
65875
65886
|
/** Run `cmd` in a login shell, in `workdir` or the workspace. */
|
|
65876
65887
|
async exec(input2) {
|
|
65877
|
-
|
|
65878
|
-
if (running.length >= MAX_RUNNING)
|
|
65879
|
-
throw new ExecRefused(`${MAX_RUNNING} commands are running. Wait for one, or stop one.`);
|
|
65888
|
+
this.#room();
|
|
65880
65889
|
const started = Date.now();
|
|
65881
65890
|
const session = this.#start(input2.cmd, input2.workdir ?? this.#root);
|
|
65882
65891
|
const wait = clamp(input2.yield_time_ms ?? DEFAULT_YIELD_MS, MIN_YIELD_MS, MAX_YIELD_MS);
|
|
65883
65892
|
return this.#after(session, wait, started, input2.max_output_tokens);
|
|
65884
65893
|
}
|
|
65885
65894
|
/**
|
|
65886
|
-
* Type into a running
|
|
65887
|
-
* (`\u0003`) interrupts it and Ctrl-D (`\u0004`) ends its input
|
|
65895
|
+
* Type into a running terminal, or with no `chars` wait and see what it has written. In a
|
|
65896
|
+
* command, Ctrl-C (`\u0003`) interrupts it and Ctrl-D (`\u0004`) ends its input; a shell on a
|
|
65897
|
+
* terminal device is typed into as it is.
|
|
65888
65898
|
*/
|
|
65889
65899
|
async write(input2) {
|
|
65890
65900
|
const session = this.#sessions.get(input2.session_id);
|
|
65891
|
-
if (!session
|
|
65901
|
+
if (!session || session.reported)
|
|
65902
|
+
throw new ExecRefused(`No session ${input2.session_id.slice(0, 64)} is running.`);
|
|
65892
65903
|
const started = Date.now();
|
|
65893
65904
|
const chars = input2.chars ?? "";
|
|
65894
|
-
if (session.exitCode === void 0)
|
|
65905
|
+
if (session.exitCode === void 0) session.type(chars);
|
|
65895
65906
|
const wait = chars ? clamp(input2.yield_time_ms ?? WRITE_YIELD_MS, MIN_YIELD_MS, MAX_YIELD_MS) : clamp(input2.yield_time_ms ?? MIN_POLL_MS, MIN_POLL_MS, MAX_YIELD_MS);
|
|
65896
65907
|
return this.#after(session, wait, started, input2.max_output_tokens);
|
|
65897
65908
|
}
|
|
65898
|
-
/**
|
|
65909
|
+
/** Every terminal, the oldest first: those running, and the last that ended. */
|
|
65910
|
+
list() {
|
|
65911
|
+
return [...this.#sessions.values()].map((session) => ({
|
|
65912
|
+
id: session.id,
|
|
65913
|
+
by: session.by,
|
|
65914
|
+
cmd: session.cmd,
|
|
65915
|
+
workdir: session.workdir,
|
|
65916
|
+
tty: session.tty,
|
|
65917
|
+
at: session.at,
|
|
65918
|
+
output: tail(session.last),
|
|
65919
|
+
...session.exitCode === void 0 ? {} : { exitCode: session.exitCode, endedAt: session.endedAt }
|
|
65920
|
+
}));
|
|
65921
|
+
}
|
|
65922
|
+
/** Start a person's login shell in the workspace, on a terminal device of this size. */
|
|
65923
|
+
open(columns, rows) {
|
|
65924
|
+
if (!this.#pty)
|
|
65925
|
+
throw new ExecRefused("A shell needs a terminal device, and this host has none to give.");
|
|
65926
|
+
this.#room();
|
|
65927
|
+
const size = sized(columns, rows);
|
|
65928
|
+
const file2 = process.env.SHELL || "/bin/bash";
|
|
65929
|
+
let shell;
|
|
65930
|
+
try {
|
|
65931
|
+
shell = this.#pty(file2, ["-l"], {
|
|
65932
|
+
name: "xterm-256color",
|
|
65933
|
+
...size,
|
|
65934
|
+
cwd: this.#root,
|
|
65935
|
+
env: process.env
|
|
65936
|
+
});
|
|
65937
|
+
} catch (error62) {
|
|
65938
|
+
const why = error62 instanceof Error ? error62.message : String(error62);
|
|
65939
|
+
throw new ExecRefused(`The shell didn't start: ${why}`);
|
|
65940
|
+
}
|
|
65941
|
+
const { session, wrote, finish } = this.#begin("person", file2, this.#root, true, {
|
|
65942
|
+
type: (chars) => shell.write(chars),
|
|
65943
|
+
signal(signal) {
|
|
65944
|
+
try {
|
|
65945
|
+
shell.kill(signal);
|
|
65946
|
+
} catch {
|
|
65947
|
+
}
|
|
65948
|
+
},
|
|
65949
|
+
resize: (cols, lines) => shell.resize(cols, lines)
|
|
65950
|
+
});
|
|
65951
|
+
shell.onData((text22) => wrote(text22));
|
|
65952
|
+
shell.onExit(({ exitCode, signal }) => finish(signal ? 128 + signal : exitCode));
|
|
65953
|
+
return session.id;
|
|
65954
|
+
}
|
|
65955
|
+
/** Type into a running terminal, as `write` does and with no answer. */
|
|
65956
|
+
type(id, chars) {
|
|
65957
|
+
this.#running(id).type(chars);
|
|
65958
|
+
}
|
|
65959
|
+
/** Give a shell's terminal device a new size. */
|
|
65960
|
+
resize(id, columns, rows) {
|
|
65961
|
+
const session = this.#running(id);
|
|
65962
|
+
if (!session.resize) throw new ExecRefused("Only a shell on a terminal device has a size.");
|
|
65963
|
+
const size = sized(columns, rows);
|
|
65964
|
+
session.resize(size.cols, size.rows);
|
|
65965
|
+
}
|
|
65966
|
+
/**
|
|
65967
|
+
* End one terminal: an interrupt, then a certain signal. It resolves once the terminal has
|
|
65968
|
+
* ended or that signal is sent. One that has ended is left alone.
|
|
65969
|
+
*/
|
|
65970
|
+
async stop(id) {
|
|
65971
|
+
const session = this.#sessions.get(id);
|
|
65972
|
+
if (!session) throw new ExecRefused(`No terminal ${id.slice(0, 64)} is here.`);
|
|
65973
|
+
await this.#end([session], "SIGINT");
|
|
65974
|
+
}
|
|
65975
|
+
/** Stop every terminal: a polite signal, then a certain one. */
|
|
65899
65976
|
async stopAll() {
|
|
65900
|
-
|
|
65901
|
-
|
|
65977
|
+
await this.#end([...this.#sessions.values()], "SIGTERM");
|
|
65978
|
+
}
|
|
65979
|
+
async #end(sessions, polite) {
|
|
65980
|
+
const running = sessions.filter((s) => s.exitCode === void 0);
|
|
65981
|
+
for (const session of running) session.signal(session.tty ? "SIGHUP" : polite);
|
|
65982
|
+
let timer;
|
|
65902
65983
|
await Promise.race([
|
|
65903
65984
|
Promise.all(running.map((s) => s.ended)),
|
|
65904
|
-
new Promise((done) =>
|
|
65985
|
+
new Promise((done) => {
|
|
65986
|
+
timer = setTimeout(done, KILL_AFTER_MS);
|
|
65987
|
+
})
|
|
65905
65988
|
]);
|
|
65906
|
-
|
|
65907
|
-
|
|
65989
|
+
clearTimeout(timer);
|
|
65990
|
+
for (const session of running) if (session.exitCode === void 0) session.signal("SIGKILL");
|
|
65991
|
+
}
|
|
65992
|
+
#room() {
|
|
65993
|
+
const running = [...this.#sessions.values()].filter((s) => s.exitCode === void 0);
|
|
65994
|
+
if (running.length >= MAX_RUNNING)
|
|
65995
|
+
throw new ExecRefused(`${MAX_RUNNING} commands are running. Wait for one, or stop one.`);
|
|
65996
|
+
}
|
|
65997
|
+
#running(id) {
|
|
65998
|
+
const session = this.#sessions.get(id);
|
|
65999
|
+
if (!session) throw new ExecRefused(`No terminal ${id.slice(0, 64)} is here.`);
|
|
66000
|
+
if (session.exitCode !== void 0) throw new ExecRefused("That terminal has ended.");
|
|
66001
|
+
return session;
|
|
66002
|
+
}
|
|
66003
|
+
/**
|
|
66004
|
+
* List a terminal that has just started, with the ways to act on its process, and give the two
|
|
66005
|
+
* ways that process reports back.
|
|
66006
|
+
*/
|
|
66007
|
+
#begin(by, cmd, workdir, tty, acts) {
|
|
66008
|
+
const id = `s_${randomBytes(9).toString("hex")}`;
|
|
66009
|
+
const at = (/* @__PURE__ */ new Date()).toISOString();
|
|
66010
|
+
let done = () => {
|
|
66011
|
+
};
|
|
66012
|
+
const session = {
|
|
66013
|
+
id,
|
|
66014
|
+
by,
|
|
66015
|
+
cmd,
|
|
66016
|
+
workdir,
|
|
66017
|
+
tty,
|
|
66018
|
+
at,
|
|
66019
|
+
pending: new Pending(),
|
|
66020
|
+
last: "",
|
|
66021
|
+
ended: new Promise((resolve42) => {
|
|
66022
|
+
done = resolve42;
|
|
66023
|
+
}),
|
|
66024
|
+
...acts
|
|
66025
|
+
};
|
|
66026
|
+
this.#sessions.set(id, session);
|
|
66027
|
+
this.#emit({ type: "start", id, by, cmd, workdir, tty, at });
|
|
66028
|
+
const wrote = (text22, stream) => {
|
|
66029
|
+
if (!text22) return;
|
|
66030
|
+
session.pending.add(text22);
|
|
66031
|
+
session.last += text22;
|
|
66032
|
+
if (session.last.length > 2 * KEPT) session.last = tail(session.last);
|
|
66033
|
+
this.#emit({ type: "output", id, ...stream ? { stream } : {}, text: text22 });
|
|
66034
|
+
};
|
|
66035
|
+
const finish = (exitCode) => {
|
|
66036
|
+
if (session.exitCode !== void 0) return;
|
|
66037
|
+
session.exitCode = exitCode;
|
|
66038
|
+
session.endedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
66039
|
+
this.#emit({ type: "exit", id, exitCode, at: session.endedAt });
|
|
66040
|
+
this.#ended.push(session);
|
|
66041
|
+
if (this.#ended.length > MAX_ENDED) {
|
|
66042
|
+
const read = this.#ended.findIndex((one) => one.reported);
|
|
66043
|
+
const [old] = this.#ended.splice(Math.max(read, 0), 1);
|
|
66044
|
+
this.#sessions.delete(old.id);
|
|
66045
|
+
}
|
|
66046
|
+
done();
|
|
66047
|
+
};
|
|
66048
|
+
return { session, wrote, finish };
|
|
65908
66049
|
}
|
|
65909
66050
|
#start(cmd, cwd) {
|
|
65910
66051
|
const child = spawn2(process.env.SHELL || "/bin/bash", ["-lc", cmd], {
|
|
@@ -65913,62 +66054,44 @@ var Sessions = class {
|
|
|
65913
66054
|
// Its own process group, so a signal reaches what it started.
|
|
65914
66055
|
detached: true
|
|
65915
66056
|
});
|
|
65916
|
-
const
|
|
65917
|
-
|
|
65918
|
-
|
|
65919
|
-
|
|
66057
|
+
const signal = (name) => {
|
|
66058
|
+
try {
|
|
66059
|
+
if (child.pid) process.kill(-child.pid, name);
|
|
66060
|
+
} catch {
|
|
66061
|
+
}
|
|
66062
|
+
};
|
|
66063
|
+
const type = (chars) => {
|
|
66064
|
+
for (const part of chars.split(new RegExp("(\\p{Cc})", "u"))) {
|
|
66065
|
+
if (part === "") signal("SIGINT");
|
|
66066
|
+
else if (part === "") child.stdin.end();
|
|
66067
|
+
else if (part && child.stdin.writable) child.stdin.write(part);
|
|
66068
|
+
}
|
|
66069
|
+
};
|
|
66070
|
+
const { session, wrote, finish } = this.#begin("agent", cmd, cwd, false, { type, signal });
|
|
65920
66071
|
for (const [name, stream] of [
|
|
65921
66072
|
["stdout", child.stdout],
|
|
65922
66073
|
["stderr", child.stderr]
|
|
65923
66074
|
]) {
|
|
65924
66075
|
const decoder = new StringDecoder("utf8");
|
|
65925
|
-
stream.on("data", (chunk) =>
|
|
65926
|
-
const text22 = decoder.write(chunk);
|
|
65927
|
-
pending.add(text22);
|
|
65928
|
-
if (text22) this.#emit({ type: "output", id, stream: name, text: text22 });
|
|
65929
|
-
});
|
|
66076
|
+
stream.on("data", (chunk) => wrote(decoder.write(chunk), name));
|
|
65930
66077
|
}
|
|
65931
66078
|
child.stdin.on("error", () => {
|
|
65932
66079
|
});
|
|
65933
|
-
|
|
65934
|
-
|
|
65935
|
-
|
|
65936
|
-
|
|
65937
|
-
|
|
65938
|
-
|
|
65939
|
-
|
|
65940
|
-
|
|
65941
|
-
|
|
65942
|
-
|
|
65943
|
-
|
|
65944
|
-
` });
|
|
65945
|
-
finish(127);
|
|
65946
|
-
});
|
|
65947
|
-
child.once("exit", (code, signal) => {
|
|
65948
|
-
const exitCode = code ?? 128 + (signal ? SIGNALS[signal] ?? 1 : 1);
|
|
65949
|
-
const late = setTimeout(() => finish(exitCode), DRAIN_MS);
|
|
65950
|
-
child.once("close", () => {
|
|
65951
|
-
clearTimeout(late);
|
|
65952
|
-
finish(exitCode);
|
|
65953
|
-
});
|
|
66080
|
+
child.once("error", (error62) => {
|
|
66081
|
+
wrote(`${error62.message}
|
|
66082
|
+
`, "stderr");
|
|
66083
|
+
finish(127);
|
|
66084
|
+
});
|
|
66085
|
+
child.once("exit", (code, signal2) => {
|
|
66086
|
+
const exitCode = code ?? 128 + (signal2 ? SIGNALS[signal2] ?? 1 : 1);
|
|
66087
|
+
const late = setTimeout(() => finish(exitCode), DRAIN_MS);
|
|
66088
|
+
child.once("close", () => {
|
|
66089
|
+
clearTimeout(late);
|
|
66090
|
+
finish(exitCode);
|
|
65954
66091
|
});
|
|
65955
66092
|
});
|
|
65956
|
-
this.#sessions.set(session.id, session);
|
|
65957
66093
|
return session;
|
|
65958
66094
|
}
|
|
65959
|
-
#type(session, chars) {
|
|
65960
|
-
for (const part of chars.split(new RegExp("(\\p{Cc})", "u"))) {
|
|
65961
|
-
if (part === "") this.#signal(session, "SIGINT");
|
|
65962
|
-
else if (part === "") session.child.stdin.end();
|
|
65963
|
-
else if (part && session.child.stdin.writable) session.child.stdin.write(part);
|
|
65964
|
-
}
|
|
65965
|
-
}
|
|
65966
|
-
#signal(session, signal) {
|
|
65967
|
-
try {
|
|
65968
|
-
if (session.child.pid) process.kill(-session.child.pid, signal);
|
|
65969
|
-
} catch {
|
|
65970
|
-
}
|
|
65971
|
-
}
|
|
65972
66095
|
async #after(session, waitMs, started, maxTokens) {
|
|
65973
66096
|
let timer;
|
|
65974
66097
|
await Promise.race([
|
|
@@ -65979,7 +66102,7 @@ var Sessions = class {
|
|
|
65979
66102
|
]);
|
|
65980
66103
|
clearTimeout(timer);
|
|
65981
66104
|
const exited = session.exitCode !== void 0;
|
|
65982
|
-
if (exited)
|
|
66105
|
+
if (exited) session.reported = true;
|
|
65983
66106
|
return {
|
|
65984
66107
|
exit_code: exited ? session.exitCode : null,
|
|
65985
66108
|
seconds: Math.round(Date.now() - started) / 1e3,
|
|
@@ -65988,6 +66111,12 @@ var Sessions = class {
|
|
|
65988
66111
|
};
|
|
65989
66112
|
}
|
|
65990
66113
|
};
|
|
66114
|
+
function sized(columns, rows) {
|
|
66115
|
+
for (const value of [columns, rows])
|
|
66116
|
+
if (!Number.isInteger(value) || value < 1 || value > MAX_SIZE)
|
|
66117
|
+
throw new ExecRefused(`A terminal has 1 to ${MAX_SIZE} columns and rows.`);
|
|
66118
|
+
return { cols: columns, rows };
|
|
66119
|
+
}
|
|
65991
66120
|
function formatAnswer(answer) {
|
|
65992
66121
|
return [
|
|
65993
66122
|
`exit_code: ${answer.exit_code ?? "running"}`,
|
|
@@ -65997,6 +66126,31 @@ function formatAnswer(answer) {
|
|
|
65997
66126
|
answer.text
|
|
65998
66127
|
].join("\n");
|
|
65999
66128
|
}
|
|
66129
|
+
function listing(terminals) {
|
|
66130
|
+
return terminals.map((one) => {
|
|
66131
|
+
const line = one.cmd.split("\n", 1)[0].slice(0, CMD_SHOWN);
|
|
66132
|
+
const shown = line.replace(/[\uD800-\uDBFF]$/, "");
|
|
66133
|
+
return {
|
|
66134
|
+
session_id: one.id,
|
|
66135
|
+
started_by: one.by,
|
|
66136
|
+
since: one.at,
|
|
66137
|
+
exit_code: one.exitCode ?? null,
|
|
66138
|
+
cmd: shown === one.cmd ? shown : `${shown}\u2026`
|
|
66139
|
+
};
|
|
66140
|
+
});
|
|
66141
|
+
}
|
|
66142
|
+
function formatSessions(listed) {
|
|
66143
|
+
if (listed.length === 0) return "No sessions.";
|
|
66144
|
+
return listed.map(
|
|
66145
|
+
(one) => [
|
|
66146
|
+
`session_id: ${one.session_id}`,
|
|
66147
|
+
`started_by: ${one.started_by}`,
|
|
66148
|
+
`since: ${one.since}`,
|
|
66149
|
+
`exit_code: ${one.exit_code ?? "running"}`,
|
|
66150
|
+
`cmd: ${one.cmd}`
|
|
66151
|
+
].join("\n")
|
|
66152
|
+
).join("\n\n");
|
|
66153
|
+
}
|
|
66000
66154
|
var DEFAULT_LINES = 2e3;
|
|
66001
66155
|
var MAX_READ_CHARS = 28e3;
|
|
66002
66156
|
var MAX_LINE_CHARS = 2e3;
|
|
@@ -66060,10 +66214,10 @@ function createFiles(root) {
|
|
|
66060
66214
|
/** Numbered lines of a file. */
|
|
66061
66215
|
read: (input2) => refusing(async () => {
|
|
66062
66216
|
const lines = (await text22(await at(input2.path), input2.path)).split("\n");
|
|
66063
|
-
const
|
|
66064
|
-
const shown = lines.slice(
|
|
66065
|
-
const last =
|
|
66066
|
-
return `lines ${
|
|
66217
|
+
const first2 = Math.max(input2.offset ?? 1, 1);
|
|
66218
|
+
const shown = lines.slice(first2 - 1, first2 - 1 + (input2.limit ?? DEFAULT_LINES)).map((line, i) => `${first2 + i} ${cut(line, MAX_LINE_CHARS)}`);
|
|
66219
|
+
const last = first2 + shown.length - 1;
|
|
66220
|
+
return `lines ${first2}-${last} of ${lines.length}
|
|
66067
66221
|
${cut(shown.join("\n"), MAX_READ_CHARS)}`;
|
|
66068
66222
|
}),
|
|
66069
66223
|
/** A folder's entries, folders first, each folder ending in `/`. */
|
|
@@ -66409,9 +66563,9 @@ function createServer2(root, sessions, version22, level) {
|
|
|
66409
66563
|
server.registerTool(
|
|
66410
66564
|
"write_stdin",
|
|
66411
66565
|
{
|
|
66412
|
-
description: "Types into a running command and returns what it wrote since. With no chars, waits and returns what it has written. A Ctrl-C character (\\u0003) interrupts it; Ctrl-D (\\u0004) ends its input.",
|
|
66566
|
+
description: "Types into a running command and returns what it wrote since. With no chars, waits and returns what it has written. A Ctrl-C character (\\u0003) interrupts it; Ctrl-D (\\u0004) ends its input. It also reaches a shell a person opened, by its id from list_sessions: there these are keys, what you type joins anything already typed, and Ctrl-D at its prompt closes it. Once an answer has given a session's exit code, that session is refused.",
|
|
66413
66567
|
inputSchema: {
|
|
66414
|
-
session_id: external_exports.string().min(1).describe("From exec_command's answer."),
|
|
66568
|
+
session_id: external_exports.string().min(1).describe("From exec_command's answer, or list_sessions."),
|
|
66415
66569
|
chars: external_exports.string().optional().describe("What to type; include \\n to press Enter."),
|
|
66416
66570
|
yield_time_ms: YieldTime.describe(
|
|
66417
66571
|
"How long to wait: with chars 250 to 30000, by default 250; with none 5000 to 30000."
|
|
@@ -66423,6 +66577,28 @@ function createServer2(root, sessions, version22, level) {
|
|
|
66423
66577
|
},
|
|
66424
66578
|
(input2) => answering(() => sessions.write(input2))
|
|
66425
66579
|
);
|
|
66580
|
+
server.registerTool(
|
|
66581
|
+
"list_sessions",
|
|
66582
|
+
{
|
|
66583
|
+
description: "Lists the sessions here, running or lately ended: each command exec_command started, and each shell a person opened. Gives each one's session_id for write_stdin, who started it, since when, its exit code and the start of its command.",
|
|
66584
|
+
outputSchema: {
|
|
66585
|
+
sessions: external_exports.array(
|
|
66586
|
+
external_exports.object({
|
|
66587
|
+
session_id: external_exports.string(),
|
|
66588
|
+
started_by: external_exports.enum(["agent", "person"]).describe("A person's is their own shell."),
|
|
66589
|
+
since: external_exports.string().describe("When it started."),
|
|
66590
|
+
exit_code: external_exports.number().nullable().describe("Null while it is still running."),
|
|
66591
|
+
cmd: external_exports.string().describe("Its first line, or the start of it.")
|
|
66592
|
+
})
|
|
66593
|
+
)
|
|
66594
|
+
},
|
|
66595
|
+
annotations: READ_ONLY
|
|
66596
|
+
},
|
|
66597
|
+
() => {
|
|
66598
|
+
const listed = listing(sessions.list());
|
|
66599
|
+
return { ...text(formatSessions(listed)), structuredContent: { sessions: listed } };
|
|
66600
|
+
}
|
|
66601
|
+
);
|
|
66426
66602
|
}
|
|
66427
66603
|
server.registerTool(
|
|
66428
66604
|
"read",
|
|
@@ -66478,18 +66654,22 @@ function createServer2(root, sessions, version22, level) {
|
|
|
66478
66654
|
);
|
|
66479
66655
|
return server;
|
|
66480
66656
|
}
|
|
66481
|
-
var VERSION = "0.0.
|
|
66657
|
+
var VERSION = "0.0.5";
|
|
66482
66658
|
function createAgentExec(options) {
|
|
66483
66659
|
const root = realpathSync(resolve3(options.workspace));
|
|
66484
66660
|
const level = options.level ?? "read";
|
|
66485
66661
|
const listeners = /* @__PURE__ */ new Set();
|
|
66486
|
-
const sessions = new Sessions(
|
|
66487
|
-
|
|
66488
|
-
|
|
66489
|
-
|
|
66490
|
-
|
|
66491
|
-
|
|
66492
|
-
|
|
66662
|
+
const sessions = new Sessions(
|
|
66663
|
+
root,
|
|
66664
|
+
(event) => {
|
|
66665
|
+
for (const listener of listeners)
|
|
66666
|
+
try {
|
|
66667
|
+
listener(event);
|
|
66668
|
+
} catch {
|
|
66669
|
+
}
|
|
66670
|
+
},
|
|
66671
|
+
options.pty
|
|
66672
|
+
);
|
|
66493
66673
|
const host = createHostFiles(root, level);
|
|
66494
66674
|
return {
|
|
66495
66675
|
async handle(request, response, body) {
|
|
@@ -66507,6 +66687,16 @@ function createAgentExec(options) {
|
|
|
66507
66687
|
return () => void listeners.delete(listener);
|
|
66508
66688
|
},
|
|
66509
66689
|
files: host.files,
|
|
66690
|
+
terminals: {
|
|
66691
|
+
list: () => sessions.list(),
|
|
66692
|
+
open({ columns, rows }) {
|
|
66693
|
+
if (level !== "terminal") throw new Error("A shell needs the terminal level.");
|
|
66694
|
+
return sessions.open(columns, rows);
|
|
66695
|
+
},
|
|
66696
|
+
write: (id, text22) => sessions.type(id, text22),
|
|
66697
|
+
resize: (id, columns, rows) => sessions.resize(id, columns, rows),
|
|
66698
|
+
stop: (id) => sessions.stop(id)
|
|
66699
|
+
},
|
|
66510
66700
|
async close() {
|
|
66511
66701
|
await Promise.all([sessions.stopAll(), host.close()]);
|
|
66512
66702
|
}
|
|
@@ -66927,11 +67117,6 @@ var BrowserLive = class {
|
|
|
66927
67117
|
}
|
|
66928
67118
|
};
|
|
66929
67119
|
|
|
66930
|
-
// src/live.ts
|
|
66931
|
-
import { chmodSync, existsSync, statSync as statSync2 } from "node:fs";
|
|
66932
|
-
import { createRequire as createRequire3 } from "node:module";
|
|
66933
|
-
import { dirname as dirname3, join as join5 } from "node:path";
|
|
66934
|
-
|
|
66935
67120
|
// node_modules/ws/wrapper.mjs
|
|
66936
67121
|
var import_stream2 = __toESM(require_stream(), 1);
|
|
66937
67122
|
var import_extension = __toESM(require_extension(), 1);
|
|
@@ -67109,78 +67294,70 @@ var FilesLive = class {
|
|
|
67109
67294
|
}
|
|
67110
67295
|
};
|
|
67111
67296
|
|
|
67297
|
+
// src/text.ts
|
|
67298
|
+
var MAX_TEXT = 1e4;
|
|
67299
|
+
function* pieces(text3, most = MAX_TEXT) {
|
|
67300
|
+
for (let from = 0; from < text3.length; ) {
|
|
67301
|
+
let to = Math.min(from + most, text3.length);
|
|
67302
|
+
const last = text3.charCodeAt(to - 1);
|
|
67303
|
+
if (to < text3.length && last >= 55296 && last <= 56319) to -= 1;
|
|
67304
|
+
yield text3.slice(from, to);
|
|
67305
|
+
from = to;
|
|
67306
|
+
}
|
|
67307
|
+
}
|
|
67308
|
+
|
|
67112
67309
|
// src/live.ts
|
|
67113
67310
|
var PROTOCOLS = {
|
|
67114
|
-
|
|
67115
|
-
terminal: "pty@1",
|
|
67311
|
+
terminal: "terminal@1",
|
|
67116
67312
|
files: "files@1",
|
|
67117
67313
|
browser: "screencast@1"
|
|
67118
67314
|
};
|
|
67119
|
-
var
|
|
67120
|
-
var
|
|
67121
|
-
var
|
|
67122
|
-
|
|
67123
|
-
|
|
67124
|
-
|
|
67125
|
-
var
|
|
67126
|
-
var
|
|
67127
|
-
var
|
|
67128
|
-
|
|
67129
|
-
|
|
67130
|
-
|
|
67131
|
-
|
|
67132
|
-
|
|
67133
|
-
|
|
67134
|
-
|
|
67135
|
-
|
|
67136
|
-
const root = dirname3(require3.resolve("node-pty/package.json"));
|
|
67137
|
-
for (const helper of [
|
|
67138
|
-
join5(root, "prebuilds", `${process.platform}-${process.arch}`, "spawn-helper"),
|
|
67139
|
-
join5(root, "build", "Release", "spawn-helper")
|
|
67140
|
-
])
|
|
67141
|
-
try {
|
|
67142
|
-
if (existsSync(helper) && (statSync2(helper).mode & 73) === 0) chmodSync(helper, 493);
|
|
67143
|
-
} catch {
|
|
67144
|
-
return `The terminal needs ${helper} to be executable, and it isn't.`;
|
|
67145
|
-
}
|
|
67146
|
-
return loaded2;
|
|
67147
|
-
})();
|
|
67148
|
-
return pty;
|
|
67315
|
+
var MAX_MESSAGE = 64 * 1024;
|
|
67316
|
+
var MAX_BUFFERED3 = 4 * 1024 * 1024;
|
|
67317
|
+
var deliver = (ws, message) => {
|
|
67318
|
+
if (ws.bufferedAmount > MAX_BUFFERED3) ws.terminate();
|
|
67319
|
+
else ws.send(JSON.stringify(message));
|
|
67320
|
+
};
|
|
67321
|
+
var MAX_CMD = 8e3;
|
|
67322
|
+
var MAX_WORKDIR = 2e3;
|
|
67323
|
+
var SERVERS = {
|
|
67324
|
+
terminal: new import_websocket_server.default({ noServer: true, maxPayload: MAX_MESSAGE }),
|
|
67325
|
+
files: new import_websocket_server.default({ noServer: true, maxPayload: MAX_PART }),
|
|
67326
|
+
browser: new import_websocket_server.default({ noServer: true })
|
|
67327
|
+
};
|
|
67328
|
+
var MALFORMED = "That isn't a terminal@1 message.";
|
|
67329
|
+
var first = (text3, most) => {
|
|
67330
|
+
const [head = ""] = pieces(text3, most);
|
|
67331
|
+
return head;
|
|
67149
67332
|
};
|
|
67333
|
+
function* messages(event) {
|
|
67334
|
+
if (event.type === "start")
|
|
67335
|
+
yield { ...event, cmd: first(event.cmd, MAX_CMD), workdir: first(event.workdir, MAX_WORKDIR) };
|
|
67336
|
+
else if (event.type === "exit") yield event;
|
|
67337
|
+
else for (const text3 of pieces(event.text)) yield { ...event, text: text3 };
|
|
67338
|
+
}
|
|
67150
67339
|
var Live = class {
|
|
67151
|
-
#
|
|
67152
|
-
#
|
|
67153
|
-
#events = [];
|
|
67154
|
-
#watchers = /* @__PURE__ */ new Set();
|
|
67155
|
-
#shells = /* @__PURE__ */ new Map();
|
|
67156
|
-
#shell;
|
|
67157
|
-
#scrollback = "";
|
|
67340
|
+
#terminals;
|
|
67341
|
+
#viewers = /* @__PURE__ */ new Set();
|
|
67158
67342
|
#browser;
|
|
67159
67343
|
#files;
|
|
67160
67344
|
/** Whether a person may write files here: not where the agent can only read them. */
|
|
67161
67345
|
#writes;
|
|
67162
|
-
constructor(exec,
|
|
67163
|
-
|
|
67164
|
-
|
|
67346
|
+
constructor(exec, access, browser) {
|
|
67347
|
+
if (exec && access === "terminal") {
|
|
67348
|
+
this.#terminals = exec.terminals;
|
|
67349
|
+
exec.onCommand((event) => {
|
|
67350
|
+
for (const message of messages(event)) for (const ws of this.#viewers) deliver(ws, message);
|
|
67351
|
+
});
|
|
67352
|
+
}
|
|
67165
67353
|
this.#files = exec && new FilesLive(exec.files);
|
|
67166
67354
|
this.#writes = access !== "read";
|
|
67167
67355
|
this.#browser = browser;
|
|
67168
|
-
exec?.onCommand((event) => this.#heard(event));
|
|
67169
67356
|
}
|
|
67170
|
-
|
|
67357
|
+
surfaces() {
|
|
67171
67358
|
const surfaces = [];
|
|
67172
|
-
if (this.#
|
|
67173
|
-
|
|
67174
|
-
surfaces.push(
|
|
67175
|
-
{ surface: "commands", protocol: PROTOCOLS.commands, operate: false },
|
|
67176
|
-
{
|
|
67177
|
-
surface: "terminal",
|
|
67178
|
-
protocol: PROTOCOLS.terminal,
|
|
67179
|
-
operate: true,
|
|
67180
|
-
...typeof loaded2 === "string" ? { unavailable: loaded2 } : {}
|
|
67181
|
-
}
|
|
67182
|
-
);
|
|
67183
|
-
}
|
|
67359
|
+
if (this.#terminals)
|
|
67360
|
+
surfaces.push({ surface: "terminal", protocol: PROTOCOLS.terminal, operate: true });
|
|
67184
67361
|
if (this.#files)
|
|
67185
67362
|
surfaces.push({ surface: "files", protocol: PROTOCOLS.files, operate: this.#writes });
|
|
67186
67363
|
if (this.#browser)
|
|
@@ -67190,102 +67367,75 @@ var Live = class {
|
|
|
67190
67367
|
/** Take an upgraded request as a socket on one surface. */
|
|
67191
67368
|
open(surface, mode, request, socket, head) {
|
|
67192
67369
|
socket.on("error", () => socket.destroy());
|
|
67193
|
-
|
|
67194
|
-
server.handleUpgrade(request, socket, head, (ws) => {
|
|
67370
|
+
SERVERS[surface].handleUpgrade(request, socket, head, (ws) => {
|
|
67195
67371
|
ws.on("error", () => ws.terminate());
|
|
67196
|
-
if (surface === "
|
|
67372
|
+
if (surface === "terminal" && this.#terminals) this.#view(ws, mode, this.#terminals);
|
|
67197
67373
|
else if (surface === "files" && this.#files) this.#files.open(ws, mode);
|
|
67198
67374
|
else if (surface === "browser" && this.#browser) this.#browser.open(ws, mode);
|
|
67199
|
-
else if (surface === "terminal") this.#attach(ws, mode).catch(() => ws.terminate());
|
|
67200
67375
|
else ws.close(1011, `There's no ${surface} here.`);
|
|
67201
67376
|
});
|
|
67202
67377
|
}
|
|
67378
|
+
/** The terminals themselves end with the AgentExec that runs them. */
|
|
67203
67379
|
close() {
|
|
67204
67380
|
void this.#browser?.close();
|
|
67205
67381
|
this.#files?.close();
|
|
67206
|
-
for (const ws of
|
|
67207
|
-
|
|
67208
|
-
|
|
67209
|
-
|
|
67210
|
-
|
|
67211
|
-
|
|
67212
|
-
|
|
67213
|
-
|
|
67214
|
-
for (const
|
|
67215
|
-
|
|
67216
|
-
|
|
67217
|
-
|
|
67218
|
-
|
|
67219
|
-
|
|
67220
|
-
|
|
67221
|
-
|
|
67222
|
-
|
|
67223
|
-
|
|
67224
|
-
|
|
67225
|
-
|
|
67226
|
-
|
|
67227
|
-
this.#scrollback = "";
|
|
67228
|
-
let shell;
|
|
67382
|
+
for (const ws of this.#viewers) ws.close(1001, "It stopped.");
|
|
67383
|
+
}
|
|
67384
|
+
/**
|
|
67385
|
+
* terminal@1: every terminal on one socket. Those already here come first, each with the end of
|
|
67386
|
+
* what it wrote, and then each event as it happens. Listing and joining are one step, so nothing
|
|
67387
|
+
* falls between them.
|
|
67388
|
+
*/
|
|
67389
|
+
#view(ws, mode, terminals) {
|
|
67390
|
+
for (const { id, by, cmd, workdir, tty, at, output: output2, exitCode, endedAt } of terminals.list()) {
|
|
67391
|
+
const events = [{ type: "start", id, by, cmd, workdir, tty, at }];
|
|
67392
|
+
if (output2) events.push({ type: "output", id, text: output2 });
|
|
67393
|
+
if (exitCode !== void 0 && endedAt)
|
|
67394
|
+
events.push({ type: "exit", id, exitCode, at: endedAt });
|
|
67395
|
+
for (const event of events)
|
|
67396
|
+
for (const message of messages(event)) ws.send(JSON.stringify(message));
|
|
67397
|
+
}
|
|
67398
|
+
this.#viewers.add(ws);
|
|
67399
|
+
ws.on("close", () => this.#viewers.delete(ws));
|
|
67400
|
+
if (mode !== "operate") return;
|
|
67401
|
+
const refuse = (why) => deliver(ws, { type: "refused", message: reason2(why) });
|
|
67402
|
+
ws.on("message", (data) => {
|
|
67229
67403
|
try {
|
|
67230
|
-
|
|
67231
|
-
|
|
67232
|
-
|
|
67233
|
-
|
|
67234
|
-
|
|
67235
|
-
|
|
67236
|
-
|
|
67404
|
+
const sent = JSON.parse(data.toString()) ?? {};
|
|
67405
|
+
const { type, id, text: text3 } = sent;
|
|
67406
|
+
const [columns, rows] = [sent.columns, sent.rows];
|
|
67407
|
+
if (type === "open") terminals.open({ columns, rows });
|
|
67408
|
+
else if (typeof id !== "string") throw new Error(MALFORMED);
|
|
67409
|
+
else if (type === "input" && typeof text3 === "string") terminals.write(id, text3);
|
|
67410
|
+
else if (type === "resize") terminals.resize(id, columns, rows);
|
|
67411
|
+
else if (type === "stop") terminals.stop(id).catch(refuse);
|
|
67412
|
+
else throw new Error(MALFORMED);
|
|
67237
67413
|
} catch (error62) {
|
|
67238
|
-
|
|
67414
|
+
refuse(error62 instanceof SyntaxError ? MALFORMED : error62);
|
|
67239
67415
|
}
|
|
67240
|
-
shell.onData((data) => {
|
|
67241
|
-
this.#scrollback = (this.#scrollback + data).slice(-MAX_SCROLLBACK);
|
|
67242
|
-
const frame = Buffer.concat([Buffer.from([OUTPUT]), Buffer.from(data)]);
|
|
67243
|
-
for (const client of this.#shells.keys()) client.send(frame);
|
|
67244
|
-
});
|
|
67245
|
-
shell.onExit(() => {
|
|
67246
|
-
if (this.#shell !== shell) return;
|
|
67247
|
-
this.#shell = void 0;
|
|
67248
|
-
for (const client of this.#shells.keys()) client.close(1e3, "The shell exited.");
|
|
67249
|
-
});
|
|
67250
|
-
this.#shell = shell;
|
|
67251
|
-
}
|
|
67252
|
-
if (this.#scrollback)
|
|
67253
|
-
ws.send(Buffer.concat([Buffer.from([OUTPUT]), Buffer.from(this.#scrollback)]));
|
|
67254
|
-
this.#shells.set(ws, mode);
|
|
67255
|
-
ws.on("close", () => this.#shells.delete(ws));
|
|
67256
|
-
ws.on("message", (data, binary) => {
|
|
67257
|
-
const frame = binary ? data : Buffer.from(data);
|
|
67258
|
-
if (mode !== "operate" || frame.length === 0 || !this.#shell) return;
|
|
67259
|
-
if (frame[0] === INPUT) this.#shell.write(frame.subarray(1).toString());
|
|
67260
|
-
else if (frame[0] === RESIZE)
|
|
67261
|
-
try {
|
|
67262
|
-
const { columns, rows } = JSON.parse(frame.subarray(1).toString());
|
|
67263
|
-
if (columns > 0 && rows > 0) this.#shell.resize(Math.floor(columns), Math.floor(rows));
|
|
67264
|
-
} catch {
|
|
67265
|
-
}
|
|
67266
67416
|
});
|
|
67267
67417
|
}
|
|
67268
67418
|
};
|
|
67269
67419
|
|
|
67270
67420
|
// src/store.ts
|
|
67271
67421
|
import { createHash, randomBytes as randomBytes4, timingSafeEqual } from "node:crypto";
|
|
67272
|
-
import { chmodSync
|
|
67273
|
-
import { join as
|
|
67422
|
+
import { chmodSync, mkdirSync, readFileSync as readFileSync2, renameSync, writeFileSync } from "node:fs";
|
|
67423
|
+
import { join as join5 } from "node:path";
|
|
67274
67424
|
var newToken = () => randomBytes4(32).toString("base64url");
|
|
67275
67425
|
var digestOf = (token) => createHash("sha256").update(token).digest("hex");
|
|
67276
67426
|
var sameToken = (token, digest) => timingSafeEqual(Buffer.from(digestOf(token), "hex"), Buffer.from(digest, "hex"));
|
|
67277
67427
|
function writePrivate(file2, text3) {
|
|
67278
67428
|
writeFileSync(`${file2}.tmp`, text3, { mode: 384, flush: true });
|
|
67279
67429
|
renameSync(`${file2}.tmp`, file2);
|
|
67280
|
-
|
|
67430
|
+
chmodSync(file2, 384);
|
|
67281
67431
|
}
|
|
67282
67432
|
var privateFolder = (dir) => {
|
|
67283
67433
|
mkdirSync(dir, { recursive: true, mode: 448 });
|
|
67284
|
-
|
|
67434
|
+
chmodSync(dir, 448);
|
|
67285
67435
|
return dir;
|
|
67286
67436
|
};
|
|
67287
67437
|
function controlToken(data) {
|
|
67288
|
-
const file2 =
|
|
67438
|
+
const file2 = join5(data, "control-token");
|
|
67289
67439
|
try {
|
|
67290
67440
|
return readFileSync2(file2, "utf8").trim();
|
|
67291
67441
|
} catch (error62) {
|
|
@@ -67305,6 +67455,33 @@ var ApiError = class extends Error {
|
|
|
67305
67455
|
this.status = status;
|
|
67306
67456
|
}
|
|
67307
67457
|
};
|
|
67458
|
+
var pty;
|
|
67459
|
+
var loadPty = () => {
|
|
67460
|
+
pty ??= (async () => {
|
|
67461
|
+
const fails = (why) => () => {
|
|
67462
|
+
throw new Error(why);
|
|
67463
|
+
};
|
|
67464
|
+
const require3 = createRequire3(import.meta.url);
|
|
67465
|
+
let loaded2;
|
|
67466
|
+
try {
|
|
67467
|
+
loaded2 = require3("node-pty");
|
|
67468
|
+
} catch {
|
|
67469
|
+
return fails("node-pty isn't installed with this service.");
|
|
67470
|
+
}
|
|
67471
|
+
const root = dirname3(require3.resolve("node-pty/package.json"));
|
|
67472
|
+
for (const helper of [
|
|
67473
|
+
join6(root, "prebuilds", `${process.platform}-${process.arch}`, "spawn-helper"),
|
|
67474
|
+
join6(root, "build", "Release", "spawn-helper")
|
|
67475
|
+
])
|
|
67476
|
+
try {
|
|
67477
|
+
if (existsSync(helper) && (statSync2(helper).mode & 73) === 0) chmodSync2(helper, 493);
|
|
67478
|
+
} catch {
|
|
67479
|
+
return fails(`${helper} has to be executable, and it isn't.`);
|
|
67480
|
+
}
|
|
67481
|
+
return loaded2.spawn;
|
|
67482
|
+
})();
|
|
67483
|
+
return pty;
|
|
67484
|
+
};
|
|
67308
67485
|
var ProcessRuntime = class {
|
|
67309
67486
|
isolation = "none";
|
|
67310
67487
|
#config;
|
|
@@ -67320,11 +67497,15 @@ var ProcessRuntime = class {
|
|
|
67320
67497
|
async start(spec, folder, events) {
|
|
67321
67498
|
let exec;
|
|
67322
67499
|
if (spec.access !== "none")
|
|
67323
|
-
exec = createAgentExec({
|
|
67500
|
+
exec = createAgentExec({
|
|
67501
|
+
workspace: spec.workspace,
|
|
67502
|
+
level: spec.access,
|
|
67503
|
+
pty: await loadPty()
|
|
67504
|
+
});
|
|
67324
67505
|
let browser;
|
|
67325
67506
|
let browserDown = false;
|
|
67326
67507
|
if (spec.browser) {
|
|
67327
|
-
const own3 = privateFolder(
|
|
67508
|
+
const own3 = privateFolder(join6(folder, "browser"));
|
|
67328
67509
|
browser = new Browser(
|
|
67329
67510
|
this.#config,
|
|
67330
67511
|
own3,
|
|
@@ -67348,8 +67529,8 @@ var ProcessRuntime = class {
|
|
|
67348
67529
|
browserLive = new BrowserLive(browser.endpoint);
|
|
67349
67530
|
browser.gate = browserLive;
|
|
67350
67531
|
}
|
|
67351
|
-
const live = new Live(exec, spec.
|
|
67352
|
-
const surfaces =
|
|
67532
|
+
const live = new Live(exec, spec.access, browserLive);
|
|
67533
|
+
const surfaces = live.surfaces();
|
|
67353
67534
|
return {
|
|
67354
67535
|
browserTools: browser?.tools ?? [],
|
|
67355
67536
|
/** As they are now: a browser that has stopped can't be opened, and says so. */
|
|
@@ -67390,7 +67571,7 @@ async function serveInside(options) {
|
|
|
67390
67571
|
...options,
|
|
67391
67572
|
...options.access === "none" ? {} : { workspace: "/workspace" }
|
|
67392
67573
|
},
|
|
67393
|
-
privateFolder(
|
|
67574
|
+
privateFolder(join7(homedir(), ".agentcomputer")),
|
|
67394
67575
|
{
|
|
67395
67576
|
log: (line) => process.stdout.write(`${line}
|
|
67396
67577
|
`),
|
|
@@ -67425,7 +67606,7 @@ async function serveInside(options) {
|
|
|
67425
67606
|
socket.on("error", () => socket.destroy());
|
|
67426
67607
|
const given = /^Bearer (\S+)$/.exec(request.headers.authorization ?? "")?.[1];
|
|
67427
67608
|
const url3 = new URL(request.url ?? "/", "http://localhost");
|
|
67428
|
-
const surface = /^\/live\/(
|
|
67609
|
+
const surface = /^\/live\/(terminal|files|browser)$/.exec(url3.pathname)?.[1];
|
|
67429
67610
|
const mode = url3.searchParams.get("mode");
|
|
67430
67611
|
if (!given || !sameToken(given, digest) || !surface || mode !== "watch" && mode !== "operate")
|
|
67431
67612
|
return void socket.end("HTTP/1.1 401 Unauthorized\r\nConnection: close\r\n\r\n");
|
|
@@ -67533,7 +67714,7 @@ var ContainerRuntime = class {
|
|
|
67533
67714
|
}
|
|
67534
67715
|
/** The token between the service and this container, which a restart has to find again. */
|
|
67535
67716
|
#tokenIn(folder) {
|
|
67536
|
-
const file2 =
|
|
67717
|
+
const file2 = join8(folder, "inner-token");
|
|
67537
67718
|
try {
|
|
67538
67719
|
return readFileSync3(file2, "utf8");
|
|
67539
67720
|
} catch {
|
|
@@ -67672,7 +67853,7 @@ async function forward(url3, authorization, request, response) {
|
|
|
67672
67853
|
}
|
|
67673
67854
|
|
|
67674
67855
|
// src/handlers.ts
|
|
67675
|
-
var VERSION2 = "0.0.
|
|
67856
|
+
var VERSION2 = "0.0.9";
|
|
67676
67857
|
function createHandlers(computers) {
|
|
67677
67858
|
return {
|
|
67678
67859
|
getInfo: async () => ({ version: VERSION2, isolation: computers.isolation }),
|
|
@@ -68254,32 +68435,30 @@ async function openLive(session, onMessage) {
|
|
|
68254
68435
|
ws.addEventListener("message", (event) => onMessage(event.data));
|
|
68255
68436
|
return { ws, closed };
|
|
68256
68437
|
}
|
|
68257
|
-
var
|
|
68258
|
-
var RESIZE2 = 49;
|
|
68259
|
-
var Pty = class {
|
|
68438
|
+
var Terminals = class {
|
|
68260
68439
|
#open;
|
|
68261
68440
|
constructor(open4) {
|
|
68262
68441
|
this.#open = open4;
|
|
68263
68442
|
}
|
|
68264
|
-
/**
|
|
68443
|
+
/**
|
|
68444
|
+
* Each terminal as it starts, writes and exits: those already there first, each with the end of
|
|
68445
|
+
* what it wrote. Operating, the default, can also open a shell and act on any of them.
|
|
68446
|
+
*/
|
|
68265
68447
|
async connect(opts) {
|
|
68266
|
-
const
|
|
68267
|
-
|
|
68268
|
-
|
|
68269
|
-
|
|
68270
|
-
|
|
68271
|
-
|
|
68272
|
-
|
|
68273
|
-
const send2 = (type, text3) => {
|
|
68274
|
-
const body = new TextEncoder().encode(text3);
|
|
68275
|
-
const frame = new Uint8Array(body.length + 1);
|
|
68276
|
-
frame[0] = type;
|
|
68277
|
-
frame.set(body, 1);
|
|
68278
|
-
ws.send(frame);
|
|
68279
|
-
};
|
|
68448
|
+
const { ws, closed } = await openLive(
|
|
68449
|
+
await this.#open("terminal", opts.mode ?? "operate"),
|
|
68450
|
+
(data) => {
|
|
68451
|
+
if (typeof data === "string") opts.onEvent(JSON.parse(data));
|
|
68452
|
+
}
|
|
68453
|
+
);
|
|
68454
|
+
const send2 = (input2) => ws.send(JSON.stringify(input2));
|
|
68280
68455
|
return {
|
|
68281
|
-
|
|
68282
|
-
|
|
68456
|
+
openShell: (columns, rows) => send2({ type: "open", columns, rows }),
|
|
68457
|
+
sendInput: (id, text3) => {
|
|
68458
|
+
for (const piece of pieces(text3)) send2({ type: "input", id, text: piece });
|
|
68459
|
+
},
|
|
68460
|
+
resize: (id, columns, rows) => send2({ type: "resize", id, columns, rows }),
|
|
68461
|
+
stop: (id) => send2({ type: "stop", id }),
|
|
68283
68462
|
close: () => ws.close(),
|
|
68284
68463
|
closed
|
|
68285
68464
|
};
|
|
@@ -68287,17 +68466,8 @@ var Pty = class {
|
|
|
68287
68466
|
};
|
|
68288
68467
|
var Commands = class {
|
|
68289
68468
|
#endpoint;
|
|
68290
|
-
|
|
68291
|
-
constructor(endpoint, open4) {
|
|
68469
|
+
constructor(endpoint) {
|
|
68292
68470
|
this.#endpoint = endpoint;
|
|
68293
|
-
this.#open = open4;
|
|
68294
|
-
}
|
|
68295
|
-
/** Each of the agent's commands as it runs: the ones already heard first, then as they come. */
|
|
68296
|
-
async watch(onEvent) {
|
|
68297
|
-
const { ws, closed } = await openLive(await this.#open("commands", "watch"), (data) => {
|
|
68298
|
-
if (typeof data === "string") onEvent(JSON.parse(data));
|
|
68299
|
-
});
|
|
68300
|
-
return { close: () => ws.close(), closed };
|
|
68301
68471
|
}
|
|
68302
68472
|
async run(cmd, opts = {}) {
|
|
68303
68473
|
let answer = await this.#endpoint.command("exec_command", {
|
|
@@ -68374,7 +68544,7 @@ var Computer = class _Computer {
|
|
|
68374
68544
|
computerID;
|
|
68375
68545
|
commands;
|
|
68376
68546
|
files;
|
|
68377
|
-
|
|
68547
|
+
terminals;
|
|
68378
68548
|
#connection;
|
|
68379
68549
|
#token;
|
|
68380
68550
|
#computer;
|
|
@@ -68385,9 +68555,8 @@ var Computer = class _Computer {
|
|
|
68385
68555
|
this.#token = token;
|
|
68386
68556
|
this.#urls = info.endpoints;
|
|
68387
68557
|
this.#computer = new Endpoint(info.endpoints.computer, token);
|
|
68388
|
-
|
|
68389
|
-
this.
|
|
68390
|
-
this.pty = new Pty(open4);
|
|
68558
|
+
this.commands = new Commands(this.#computer);
|
|
68559
|
+
this.terminals = new Terminals((surface, mode) => this.liveSession(surface, mode));
|
|
68391
68560
|
this.files = new Files(this.#computer);
|
|
68392
68561
|
}
|
|
68393
68562
|
static async create(opts) {
|
|
@@ -68508,7 +68677,7 @@ import {
|
|
|
68508
68677
|
STATUS_CODES as STATUS_CODES3
|
|
68509
68678
|
} from "node:http";
|
|
68510
68679
|
import { createServer as createTlsServer } from "node:https";
|
|
68511
|
-
import { join as
|
|
68680
|
+
import { join as join10 } from "node:path";
|
|
68512
68681
|
|
|
68513
68682
|
// node_modules/@modelcontextprotocol/server/dist/chunk-vYduTEsO.mjs
|
|
68514
68683
|
var __create4 = Object.create;
|
|
@@ -75942,8 +76111,8 @@ var require_utils3 = /* @__PURE__ */ __commonJSMin3(((exports, module) => {
|
|
|
75942
76111
|
}
|
|
75943
76112
|
if (bestLength < 2) return hextets.join(":");
|
|
75944
76113
|
const head = hextets.slice(0, bestStart).join(":");
|
|
75945
|
-
const
|
|
75946
|
-
return head + "::" +
|
|
76114
|
+
const tail2 = hextets.slice(bestStart + bestLength).join(":");
|
|
76115
|
+
return head + "::" + tail2;
|
|
75947
76116
|
}
|
|
75948
76117
|
function normalizeIPv6Address(input2) {
|
|
75949
76118
|
const compression = input2.indexOf("::");
|
|
@@ -82420,9 +82589,9 @@ var WebStandardStreamableHTTPServerTransport2 = class {
|
|
|
82420
82589
|
}
|
|
82421
82590
|
});
|
|
82422
82591
|
}
|
|
82423
|
-
async _checkScopeChallenge(
|
|
82592
|
+
async _checkScopeChallenge(messages2, authInfo) {
|
|
82424
82593
|
if (!this._scopeChallengeResolver) return;
|
|
82425
|
-
const challenge = await findScopeChallenge2(
|
|
82594
|
+
const challenge = await findScopeChallenge2(messages2.filter((message) => isJSONRPCRequest3(message)), authInfo, this._scopeChallengeResolver);
|
|
82426
82595
|
return challenge === void 0 ? void 0 : createScopeChallengeResponse2(challenge, scopeChallengeResourceMetadataUrl2(authInfo));
|
|
82427
82596
|
}
|
|
82428
82597
|
/**
|
|
@@ -82720,21 +82889,21 @@ data:
|
|
|
82720
82889
|
this.onerror?.(/* @__PURE__ */ new Error(`Invalid Request: Batch must not exceed ${MAX_BATCH_SIZE2} messages`));
|
|
82721
82890
|
return this.createJsonErrorResponse(400, -32600, `Invalid Request: Batch must not exceed ${MAX_BATCH_SIZE2} messages`);
|
|
82722
82891
|
}
|
|
82723
|
-
let
|
|
82892
|
+
let messages2;
|
|
82724
82893
|
try {
|
|
82725
|
-
|
|
82894
|
+
messages2 = Array.isArray(rawMessage) ? rawMessage.map((msg) => JSONRPCMessageSchema.parse(msg)) : [JSONRPCMessageSchema.parse(rawMessage)];
|
|
82726
82895
|
} catch (error62) {
|
|
82727
82896
|
this.onerror?.(error62);
|
|
82728
82897
|
return this.createJsonErrorResponse(400, -32700, "Parse error: Invalid JSON-RPC message");
|
|
82729
82898
|
}
|
|
82730
82899
|
if (this._closed) return this.createJsonErrorResponse(404, -32001, "Session not found");
|
|
82731
|
-
const isInitializationRequest =
|
|
82900
|
+
const isInitializationRequest = messages2.some((element) => isInitializeRequest3(element));
|
|
82732
82901
|
if (isInitializationRequest) {
|
|
82733
82902
|
if (this._initialized && this.sessionId !== void 0) {
|
|
82734
82903
|
this.onerror?.(/* @__PURE__ */ new Error("Invalid Request: Server already initialized"));
|
|
82735
82904
|
return this.createJsonErrorResponse(400, -32600, "Invalid Request: Server already initialized");
|
|
82736
82905
|
}
|
|
82737
|
-
if (
|
|
82906
|
+
if (messages2.length > 1) {
|
|
82738
82907
|
this.onerror?.(/* @__PURE__ */ new Error("Invalid Request: Only one initialization request is allowed"));
|
|
82739
82908
|
return this.createJsonErrorResponse(400, -32600, "Invalid Request: Only one initialization request is allowed");
|
|
82740
82909
|
}
|
|
@@ -82751,21 +82920,21 @@ data:
|
|
|
82751
82920
|
if (this._closed) return this.createJsonErrorResponse(404, -32001, "Session not found");
|
|
82752
82921
|
let scopeChallengeResponse;
|
|
82753
82922
|
try {
|
|
82754
|
-
scopeChallengeResponse = await this._checkScopeChallenge(
|
|
82923
|
+
scopeChallengeResponse = await this._checkScopeChallenge(messages2, options?.authInfo);
|
|
82755
82924
|
} catch (error62) {
|
|
82756
82925
|
this.onerror?.(error62);
|
|
82757
82926
|
return this.createJsonErrorResponse(500, -32603, "Internal server error");
|
|
82758
82927
|
}
|
|
82759
82928
|
if (scopeChallengeResponse) return scopeChallengeResponse;
|
|
82760
|
-
if (!
|
|
82761
|
-
for (const message of
|
|
82929
|
+
if (!messages2.some((element) => isJSONRPCRequest3(element))) {
|
|
82930
|
+
for (const message of messages2) this.onmessage?.(message, {
|
|
82762
82931
|
authInfo: options?.authInfo,
|
|
82763
82932
|
request
|
|
82764
82933
|
});
|
|
82765
82934
|
return new Response(null, { status: 202 });
|
|
82766
82935
|
}
|
|
82767
82936
|
const streamId = crypto.randomUUID();
|
|
82768
|
-
const initRequest =
|
|
82937
|
+
const initRequest = messages2.find((m) => isInitializeRequest3(m));
|
|
82769
82938
|
const clientProtocolVersion = initRequest ? initRequest.params.protocolVersion : req.headers.get("mcp-protocol-version") ?? DEFAULT_NEGOTIATED_PROTOCOL_VERSION;
|
|
82770
82939
|
if (this._enableJsonResponse) return new Promise((resolve6) => {
|
|
82771
82940
|
this._streamMapping.set(streamId, {
|
|
@@ -82774,8 +82943,8 @@ data:
|
|
|
82774
82943
|
this._streamMapping.delete(streamId);
|
|
82775
82944
|
}
|
|
82776
82945
|
});
|
|
82777
|
-
for (const message of
|
|
82778
|
-
for (const message of
|
|
82946
|
+
for (const message of messages2) if (isJSONRPCRequest3(message)) this._requestToStreamMapping.set(message.id, streamId);
|
|
82947
|
+
for (const message of messages2) this.onmessage?.(message, {
|
|
82779
82948
|
authInfo: options?.authInfo,
|
|
82780
82949
|
request
|
|
82781
82950
|
});
|
|
@@ -82799,7 +82968,7 @@ data:
|
|
|
82799
82968
|
"X-Accel-Buffering": "no"
|
|
82800
82969
|
};
|
|
82801
82970
|
if (this.sessionId !== void 0) headers["mcp-session-id"] = this.sessionId;
|
|
82802
|
-
for (const message of
|
|
82971
|
+
for (const message of messages2) if (isJSONRPCRequest3(message)) {
|
|
82803
82972
|
this._streamMapping.set(streamId, {
|
|
82804
82973
|
controller: streamController,
|
|
82805
82974
|
encoder,
|
|
@@ -82815,7 +82984,7 @@ data:
|
|
|
82815
82984
|
this._requestToStreamMapping.set(message.id, streamId);
|
|
82816
82985
|
}
|
|
82817
82986
|
await this.writePrimingEvent(streamController, encoder, streamId, clientProtocolVersion);
|
|
82818
|
-
for (const message of
|
|
82987
|
+
for (const message of messages2) {
|
|
82819
82988
|
let closeSSEStream;
|
|
82820
82989
|
let closeStandaloneSSEStream;
|
|
82821
82990
|
if (isJSONRPCRequest3(message) && this._eventStore && this.supportsEmptySSEData(clientProtocolVersion)) {
|
|
@@ -84324,11 +84493,11 @@ var NodeStreamableHTTPServerTransport2 = class {
|
|
|
84324
84493
|
// src/computers.ts
|
|
84325
84494
|
import { randomBytes as randomBytes5 } from "node:crypto";
|
|
84326
84495
|
import { appendFileSync, existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync as readFileSync4, rmSync as rmSync2, statSync as statSync3 } from "node:fs";
|
|
84327
|
-
import { isAbsolute, join as
|
|
84496
|
+
import { isAbsolute, join as join9, resolve as resolve4 } from "node:path";
|
|
84328
84497
|
var TOOLS = {
|
|
84329
84498
|
read: ["read", "list", "search"],
|
|
84330
84499
|
files: ["read", "list", "search", "edit"],
|
|
84331
|
-
terminal: ["read", "list", "search", "edit", "exec_command", "write_stdin"]
|
|
84500
|
+
terminal: ["read", "list", "search", "edit", "exec_command", "write_stdin", "list_sessions"]
|
|
84332
84501
|
};
|
|
84333
84502
|
var MAX_LOG = 256 * 1024;
|
|
84334
84503
|
var MAX_TIMER_S = 2e6;
|
|
@@ -84346,7 +84515,7 @@ var Computers = class {
|
|
|
84346
84515
|
this.#data = data;
|
|
84347
84516
|
this.#base = base;
|
|
84348
84517
|
this.#runtime = runtime;
|
|
84349
|
-
this.#file =
|
|
84518
|
+
this.#file = join9(data, "computers.json");
|
|
84350
84519
|
if (existsSync2(this.#file))
|
|
84351
84520
|
for (const entry of JSON.parse(readFileSync4(this.#file, "utf8")))
|
|
84352
84521
|
this.#stored.set(entry.id, entry);
|
|
@@ -84401,7 +84570,7 @@ var Computers = class {
|
|
|
84401
84570
|
...timeout === void 0 ? {} : { endAt: expiry(timeout) }
|
|
84402
84571
|
};
|
|
84403
84572
|
if (!workspace && access !== "none") {
|
|
84404
|
-
stored.workspace =
|
|
84573
|
+
stored.workspace = join9(this.#folder(id), "workspace");
|
|
84405
84574
|
mkdirSync2(stored.workspace, { recursive: true, mode: 448 });
|
|
84406
84575
|
}
|
|
84407
84576
|
this.#stored.set(id, stored);
|
|
@@ -84552,10 +84721,10 @@ var Computers = class {
|
|
|
84552
84721
|
return stored;
|
|
84553
84722
|
}
|
|
84554
84723
|
#folder(id) {
|
|
84555
|
-
return privateFolder(
|
|
84724
|
+
return privateFolder(join9(this.#data, "computers", id));
|
|
84556
84725
|
}
|
|
84557
84726
|
#logFile(id) {
|
|
84558
|
-
return
|
|
84727
|
+
return join9(this.#folder(id), "log");
|
|
84559
84728
|
}
|
|
84560
84729
|
#log(id, line) {
|
|
84561
84730
|
if (!this.#stored.has(id)) return;
|
|
@@ -84647,7 +84816,7 @@ var OPENAPI = {
|
|
|
84647
84816
|
"openapi": "3.1.0",
|
|
84648
84817
|
"info": {
|
|
84649
84818
|
"title": "AgentComputer",
|
|
84650
|
-
"version": "0.0.
|
|
84819
|
+
"version": "0.0.9",
|
|
84651
84820
|
"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.",
|
|
84652
84821
|
"license": {
|
|
84653
84822
|
"name": "Apache-2.0",
|
|
@@ -84968,7 +85137,7 @@ var OPENAPI = {
|
|
|
84968
85137
|
"post": {
|
|
84969
85138
|
"operationId": "createLiveSession",
|
|
84970
85139
|
"x-mcp-tool": "computer_live",
|
|
84971
|
-
"summary": "A one-time ticket to watch or operate one of its surfaces live:
|
|
85140
|
+
"summary": "A one-time ticket to watch or operate one of its surfaces live: its terminals, its files, or its browser. Open its url as a WebSocket within 60 seconds.",
|
|
84972
85141
|
"requestBody": {
|
|
84973
85142
|
"required": true,
|
|
84974
85143
|
"content": {
|
|
@@ -85099,7 +85268,7 @@ var OPENAPI = {
|
|
|
85099
85268
|
"files",
|
|
85100
85269
|
"terminal"
|
|
85101
85270
|
],
|
|
85102
|
-
"description": "How far it reaches into files and commands, each level containing the one before: `read` has read, list and search; `files` adds edit; `terminal` adds exec_command and
|
|
85271
|
+
"description": "How far it reaches into files and commands, each level containing the one before: `read` has 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."
|
|
85103
85272
|
},
|
|
85104
85273
|
"ComputerState": {
|
|
85105
85274
|
"type": "string",
|
|
@@ -85307,12 +85476,11 @@ var OPENAPI = {
|
|
|
85307
85476
|
"Surface": {
|
|
85308
85477
|
"type": "string",
|
|
85309
85478
|
"enum": [
|
|
85310
|
-
"commands",
|
|
85311
85479
|
"terminal",
|
|
85312
85480
|
"files",
|
|
85313
85481
|
"browser"
|
|
85314
85482
|
],
|
|
85315
|
-
"description": "
|
|
85483
|
+
"description": "terminal: every terminal on it, the agent's commands and a person's shells alike, on one socket in terminal@1: text messages are TerminalEvent one way and TerminalInput the other. Watching reads them all; operating also opens a shell, types into any of them, the agent's included, sizes and stops them. files: its workspace's files, in files@1: text messages are FilesRequest one way and FilesAnswer the other, and a file's bytes travel as binary messages of at most 1,048,576 bytes. Watching lists and reads; operating also writes, which a computer with read access doesn't offer. browser: its browser, in screencast@1: binary messages are JPEG frames of the page shown, text messages are ScreencastEvent one way and ScreencastInput the other. Nobody waits for anybody: the agent's tools and a person's input act on the same browser at once. The page shown is each socket's own. A socket is shown each new tab as it opens, until it sends a tab input to show or open one; from then its page changes only when that page closes."
|
|
85316
85484
|
},
|
|
85317
85485
|
"LiveMode": {
|
|
85318
85486
|
"type": "string",
|
|
@@ -85755,6 +85923,218 @@ var OPENAPI = {
|
|
|
85755
85923
|
}
|
|
85756
85924
|
]
|
|
85757
85925
|
},
|
|
85926
|
+
"TerminalEvent": {
|
|
85927
|
+
"description": "What the service sends on a terminal@1 socket, as JSON text, no message over 65,536 bytes. A terminal is a command the agent started or a shell a person opened; its id is the session id the agent's tools use. A new socket is first sent, for each terminal already there, the oldest first, its start, the end of what it wrote (its last 65,536 characters), and its exit if it has ended; then each event as it happens, with nothing to mark where the first ends. The last 64 that ended are among those already there, and one that leaves that list is not told. A character here is a UTF-16 unit. A socket with more than 4 MiB still to be sent to it is closed, with no close frame (1006): one that has stopped reading, or one that is reading while a terminal writes tens of megabytes at once. It isn't an error. Opened again, it is sent every terminal afresh, with the last 65,536 characters each wrote, in place of whatever it held.",
|
|
85928
|
+
"oneOf": [
|
|
85929
|
+
{
|
|
85930
|
+
"type": "object",
|
|
85931
|
+
"description": "A terminal began.",
|
|
85932
|
+
"required": [
|
|
85933
|
+
"type",
|
|
85934
|
+
"id",
|
|
85935
|
+
"by",
|
|
85936
|
+
"cmd",
|
|
85937
|
+
"workdir",
|
|
85938
|
+
"tty",
|
|
85939
|
+
"at"
|
|
85940
|
+
],
|
|
85941
|
+
"properties": {
|
|
85942
|
+
"type": {
|
|
85943
|
+
"const": "start"
|
|
85944
|
+
},
|
|
85945
|
+
"id": {
|
|
85946
|
+
"type": "string"
|
|
85947
|
+
},
|
|
85948
|
+
"by": {
|
|
85949
|
+
"type": "string",
|
|
85950
|
+
"enum": [
|
|
85951
|
+
"agent",
|
|
85952
|
+
"person"
|
|
85953
|
+
],
|
|
85954
|
+
"description": "The way in it came: agent through a tool, person through this surface."
|
|
85955
|
+
},
|
|
85956
|
+
"cmd": {
|
|
85957
|
+
"type": "string",
|
|
85958
|
+
"description": "Its command, the first 8,000 characters of it."
|
|
85959
|
+
},
|
|
85960
|
+
"workdir": {
|
|
85961
|
+
"type": "string",
|
|
85962
|
+
"description": "Its folder, the first 2,000 characters of it."
|
|
85963
|
+
},
|
|
85964
|
+
"tty": {
|
|
85965
|
+
"type": "boolean",
|
|
85966
|
+
"description": "Whether it is on a terminal device, as a person's shell is. One that isn't, as the agent's commands aren't, echoes nothing typed into it and wants a line feed for Enter. Nothing tells the size of one that is."
|
|
85967
|
+
},
|
|
85968
|
+
"at": {
|
|
85969
|
+
"type": "string",
|
|
85970
|
+
"format": "date-time"
|
|
85971
|
+
}
|
|
85972
|
+
}
|
|
85973
|
+
},
|
|
85974
|
+
{
|
|
85975
|
+
"type": "object",
|
|
85976
|
+
"description": "What it wrote, as it writes: 10,000 characters a message at most, a longer piece as several. A command that left something running can still write after its exit.",
|
|
85977
|
+
"required": [
|
|
85978
|
+
"type",
|
|
85979
|
+
"id",
|
|
85980
|
+
"text"
|
|
85981
|
+
],
|
|
85982
|
+
"properties": {
|
|
85983
|
+
"type": {
|
|
85984
|
+
"const": "output"
|
|
85985
|
+
},
|
|
85986
|
+
"id": {
|
|
85987
|
+
"type": "string"
|
|
85988
|
+
},
|
|
85989
|
+
"stream": {
|
|
85990
|
+
"type": "string",
|
|
85991
|
+
"enum": [
|
|
85992
|
+
"stdout",
|
|
85993
|
+
"stderr"
|
|
85994
|
+
],
|
|
85995
|
+
"description": "Left out for one on a terminal device, which has one stream, and in what a new socket is first sent, where the two are one text."
|
|
85996
|
+
},
|
|
85997
|
+
"text": {
|
|
85998
|
+
"type": "string"
|
|
85999
|
+
}
|
|
86000
|
+
}
|
|
86001
|
+
},
|
|
86002
|
+
{
|
|
86003
|
+
"type": "object",
|
|
86004
|
+
"description": "It ended.",
|
|
86005
|
+
"required": [
|
|
86006
|
+
"type",
|
|
86007
|
+
"id",
|
|
86008
|
+
"exitCode",
|
|
86009
|
+
"at"
|
|
86010
|
+
],
|
|
86011
|
+
"properties": {
|
|
86012
|
+
"type": {
|
|
86013
|
+
"const": "exit"
|
|
86014
|
+
},
|
|
86015
|
+
"id": {
|
|
86016
|
+
"type": "string"
|
|
86017
|
+
},
|
|
86018
|
+
"exitCode": {
|
|
86019
|
+
"type": "integer"
|
|
86020
|
+
},
|
|
86021
|
+
"at": {
|
|
86022
|
+
"type": "string",
|
|
86023
|
+
"format": "date-time"
|
|
86024
|
+
}
|
|
86025
|
+
}
|
|
86026
|
+
},
|
|
86027
|
+
{
|
|
86028
|
+
"type": "object",
|
|
86029
|
+
"description": "What this socket asked wasn't done, and why, in words for a person. Sent only to the operate socket that asked. What was done is answered by its event, or by nothing.",
|
|
86030
|
+
"required": [
|
|
86031
|
+
"type",
|
|
86032
|
+
"message"
|
|
86033
|
+
],
|
|
86034
|
+
"properties": {
|
|
86035
|
+
"type": {
|
|
86036
|
+
"const": "refused"
|
|
86037
|
+
},
|
|
86038
|
+
"message": {
|
|
86039
|
+
"type": "string"
|
|
86040
|
+
}
|
|
86041
|
+
}
|
|
86042
|
+
}
|
|
86043
|
+
]
|
|
86044
|
+
},
|
|
86045
|
+
"TerminalInput": {
|
|
86046
|
+
"description": "What an operate socket sends on terminal@1, as JSON text. One that can't be done, or that is none of these, is answered with refused. What a watch socket sends is dropped. A message over 65,536 bytes closes the socket, so a long text goes as several inputs.",
|
|
86047
|
+
"oneOf": [
|
|
86048
|
+
{
|
|
86049
|
+
"type": "object",
|
|
86050
|
+
"description": "Start a shell of your own in the workspace, as its user, on a terminal device of this size. It arrives as a start like any other. Refused past the 64 terminals that may run at once, and where the service has no node-pty.",
|
|
86051
|
+
"required": [
|
|
86052
|
+
"type",
|
|
86053
|
+
"columns",
|
|
86054
|
+
"rows"
|
|
86055
|
+
],
|
|
86056
|
+
"properties": {
|
|
86057
|
+
"type": {
|
|
86058
|
+
"const": "open"
|
|
86059
|
+
},
|
|
86060
|
+
"columns": {
|
|
86061
|
+
"type": "integer",
|
|
86062
|
+
"minimum": 1,
|
|
86063
|
+
"maximum": 1e3
|
|
86064
|
+
},
|
|
86065
|
+
"rows": {
|
|
86066
|
+
"type": "integer",
|
|
86067
|
+
"minimum": 1,
|
|
86068
|
+
"maximum": 1e3
|
|
86069
|
+
}
|
|
86070
|
+
}
|
|
86071
|
+
},
|
|
86072
|
+
{
|
|
86073
|
+
"type": "object",
|
|
86074
|
+
"description": "Type into a running terminal, the agent's included. In a command, Ctrl-C (U+0003) interrupts it and Ctrl-D (U+0004) ends its input. Refused for one that has ended or isn't there.",
|
|
86075
|
+
"required": [
|
|
86076
|
+
"type",
|
|
86077
|
+
"id",
|
|
86078
|
+
"text"
|
|
86079
|
+
],
|
|
86080
|
+
"properties": {
|
|
86081
|
+
"type": {
|
|
86082
|
+
"const": "input"
|
|
86083
|
+
},
|
|
86084
|
+
"id": {
|
|
86085
|
+
"type": "string"
|
|
86086
|
+
},
|
|
86087
|
+
"text": {
|
|
86088
|
+
"type": "string"
|
|
86089
|
+
}
|
|
86090
|
+
}
|
|
86091
|
+
},
|
|
86092
|
+
{
|
|
86093
|
+
"type": "object",
|
|
86094
|
+
"description": "Give a terminal on a terminal device a new size, which is the size for everyone looking at it. Refused for a command, which has none, and for one that has ended or isn't there.",
|
|
86095
|
+
"required": [
|
|
86096
|
+
"type",
|
|
86097
|
+
"id",
|
|
86098
|
+
"columns",
|
|
86099
|
+
"rows"
|
|
86100
|
+
],
|
|
86101
|
+
"properties": {
|
|
86102
|
+
"type": {
|
|
86103
|
+
"const": "resize"
|
|
86104
|
+
},
|
|
86105
|
+
"id": {
|
|
86106
|
+
"type": "string"
|
|
86107
|
+
},
|
|
86108
|
+
"columns": {
|
|
86109
|
+
"type": "integer",
|
|
86110
|
+
"minimum": 1,
|
|
86111
|
+
"maximum": 1e3
|
|
86112
|
+
},
|
|
86113
|
+
"rows": {
|
|
86114
|
+
"type": "integer",
|
|
86115
|
+
"minimum": 1,
|
|
86116
|
+
"maximum": 1e3
|
|
86117
|
+
}
|
|
86118
|
+
}
|
|
86119
|
+
},
|
|
86120
|
+
{
|
|
86121
|
+
"type": "object",
|
|
86122
|
+
"description": "End a terminal: an interrupt for a command, a hangup for a shell, then a kill a second later. Its exit is the answer. One that has ended is left as it is, and one that isn't there is refused.",
|
|
86123
|
+
"required": [
|
|
86124
|
+
"type",
|
|
86125
|
+
"id"
|
|
86126
|
+
],
|
|
86127
|
+
"properties": {
|
|
86128
|
+
"type": {
|
|
86129
|
+
"const": "stop"
|
|
86130
|
+
},
|
|
86131
|
+
"id": {
|
|
86132
|
+
"type": "string"
|
|
86133
|
+
}
|
|
86134
|
+
}
|
|
86135
|
+
}
|
|
86136
|
+
]
|
|
86137
|
+
},
|
|
85758
86138
|
"FilesRequest": {
|
|
85759
86139
|
"description": "What a socket sends on files@1, as JSON text. Each has an id of its own choosing, which the answer repeats; answers come as they are ready, in any order, and ids aren't checked for repeats. A request still unanswered when the socket closes gets no answer. A path is from the workspace, with / between its parts, or absolute inside it, and nothing outside the workspace is reached. A binary message that follows no write closes the socket, and so does any message over 1,048,576 bytes.",
|
|
85760
86140
|
"oneOf": [
|
|
@@ -86224,7 +86604,7 @@ var OPERATIONS = [
|
|
|
86224
86604
|
"files",
|
|
86225
86605
|
"terminal"
|
|
86226
86606
|
],
|
|
86227
|
-
"description": "How far it reaches into files and commands, each level containing the one before: `read` has read, list and search; `files` adds edit; `terminal` adds exec_command and
|
|
86607
|
+
"description": "How far it reaches into files and commands, each level containing the one before: `read` has 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."
|
|
86228
86608
|
},
|
|
86229
86609
|
"browser": {
|
|
86230
86610
|
"type": "boolean"
|
|
@@ -86313,7 +86693,7 @@ var OPERATIONS = [
|
|
|
86313
86693
|
"files",
|
|
86314
86694
|
"terminal"
|
|
86315
86695
|
],
|
|
86316
|
-
"description": "How far it reaches into files and commands, each level containing the one before: `read` has read, list and search; `files` adds edit; `terminal` adds exec_command and
|
|
86696
|
+
"description": "How far it reaches into files and commands, each level containing the one before: `read` has 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."
|
|
86317
86697
|
},
|
|
86318
86698
|
"browser": {
|
|
86319
86699
|
"type": "boolean",
|
|
@@ -86383,7 +86763,7 @@ var OPERATIONS = [
|
|
|
86383
86763
|
"files",
|
|
86384
86764
|
"terminal"
|
|
86385
86765
|
],
|
|
86386
|
-
"description": "How far it reaches into files and commands, each level containing the one before: `read` has read, list and search; `files` adds edit; `terminal` adds exec_command and
|
|
86766
|
+
"description": "How far it reaches into files and commands, each level containing the one before: `read` has 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."
|
|
86387
86767
|
},
|
|
86388
86768
|
"browser": {
|
|
86389
86769
|
"type": "boolean"
|
|
@@ -86523,7 +86903,7 @@ var OPERATIONS = [
|
|
|
86523
86903
|
"files",
|
|
86524
86904
|
"terminal"
|
|
86525
86905
|
],
|
|
86526
|
-
"description": "How far it reaches into files and commands, each level containing the one before: `read` has read, list and search; `files` adds edit; `terminal` adds exec_command and
|
|
86906
|
+
"description": "How far it reaches into files and commands, each level containing the one before: `read` has 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."
|
|
86527
86907
|
},
|
|
86528
86908
|
"browser": {
|
|
86529
86909
|
"type": "boolean"
|
|
@@ -86607,12 +86987,11 @@ var OPERATIONS = [
|
|
|
86607
86987
|
"surface": {
|
|
86608
86988
|
"type": "string",
|
|
86609
86989
|
"enum": [
|
|
86610
|
-
"commands",
|
|
86611
86990
|
"terminal",
|
|
86612
86991
|
"files",
|
|
86613
86992
|
"browser"
|
|
86614
86993
|
],
|
|
86615
|
-
"description": "
|
|
86994
|
+
"description": "terminal: every terminal on it, the agent's commands and a person's shells alike, on one socket in terminal@1: text messages are TerminalEvent one way and TerminalInput the other. Watching reads them all; operating also opens a shell, types into any of them, the agent's included, sizes and stops them. files: its workspace's files, in files@1: text messages are FilesRequest one way and FilesAnswer the other, and a file's bytes travel as binary messages of at most 1,048,576 bytes. Watching lists and reads; operating also writes, which a computer with read access doesn't offer. browser: its browser, in screencast@1: binary messages are JPEG frames of the page shown, text messages are ScreencastEvent one way and ScreencastInput the other. Nobody waits for anybody: the agent's tools and a person's input act on the same browser at once. The page shown is each socket's own. A socket is shown each new tab as it opens, until it sends a tab input to show or open one; from then its page changes only when that page closes."
|
|
86616
86995
|
},
|
|
86617
86996
|
"protocol": {
|
|
86618
86997
|
"type": "string"
|
|
@@ -86754,7 +87133,7 @@ var OPERATIONS = [
|
|
|
86754
87133
|
"files",
|
|
86755
87134
|
"terminal"
|
|
86756
87135
|
],
|
|
86757
|
-
"description": "How far it reaches into files and commands, each level containing the one before: `read` has read, list and search; `files` adds edit; `terminal` adds exec_command and
|
|
87136
|
+
"description": "How far it reaches into files and commands, each level containing the one before: `read` has 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."
|
|
86758
87137
|
},
|
|
86759
87138
|
"browser": {
|
|
86760
87139
|
"type": "boolean"
|
|
@@ -86878,7 +87257,7 @@ var OPERATIONS = [
|
|
|
86878
87257
|
"files",
|
|
86879
87258
|
"terminal"
|
|
86880
87259
|
],
|
|
86881
|
-
"description": "How far it reaches into files and commands, each level containing the one before: `read` has read, list and search; `files` adds edit; `terminal` adds exec_command and
|
|
87260
|
+
"description": "How far it reaches into files and commands, each level containing the one before: `read` has 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."
|
|
86882
87261
|
},
|
|
86883
87262
|
"browser": {
|
|
86884
87263
|
"type": "boolean"
|
|
@@ -87007,7 +87386,7 @@ var OPERATIONS = [
|
|
|
87007
87386
|
"files",
|
|
87008
87387
|
"terminal"
|
|
87009
87388
|
],
|
|
87010
|
-
"description": "How far it reaches into files and commands, each level containing the one before: `read` has read, list and search; `files` adds edit; `terminal` adds exec_command and
|
|
87389
|
+
"description": "How far it reaches into files and commands, each level containing the one before: `read` has 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."
|
|
87011
87390
|
},
|
|
87012
87391
|
"browser": {
|
|
87013
87392
|
"type": "boolean"
|
|
@@ -87128,7 +87507,7 @@ var OPERATIONS = [
|
|
|
87128
87507
|
"method": "POST",
|
|
87129
87508
|
"path": "/v1/computers/{computerID}/live",
|
|
87130
87509
|
"tool": "computer_live",
|
|
87131
|
-
"summary": "A one-time ticket to watch or operate one of its surfaces live:
|
|
87510
|
+
"summary": "A one-time ticket to watch or operate one of its surfaces live: its terminals, its files, or its browser. Open its url as a WebSocket within 60 seconds.",
|
|
87132
87511
|
"status": 201,
|
|
87133
87512
|
"hasBody": true,
|
|
87134
87513
|
"pathParameters": [
|
|
@@ -87147,12 +87526,11 @@ var OPERATIONS = [
|
|
|
87147
87526
|
"surface": {
|
|
87148
87527
|
"type": "string",
|
|
87149
87528
|
"enum": [
|
|
87150
|
-
"commands",
|
|
87151
87529
|
"terminal",
|
|
87152
87530
|
"files",
|
|
87153
87531
|
"browser"
|
|
87154
87532
|
],
|
|
87155
|
-
"description": "
|
|
87533
|
+
"description": "terminal: every terminal on it, the agent's commands and a person's shells alike, on one socket in terminal@1: text messages are TerminalEvent one way and TerminalInput the other. Watching reads them all; operating also opens a shell, types into any of them, the agent's included, sizes and stops them. files: its workspace's files, in files@1: text messages are FilesRequest one way and FilesAnswer the other, and a file's bytes travel as binary messages of at most 1,048,576 bytes. Watching lists and reads; operating also writes, which a computer with read access doesn't offer. browser: its browser, in screencast@1: binary messages are JPEG frames of the page shown, text messages are ScreencastEvent one way and ScreencastInput the other. Nobody waits for anybody: the agent's tools and a person's input act on the same browser at once. The page shown is each socket's own. A socket is shown each new tab as it opens, until it sends a tab input to show or open one; from then its page changes only when that page closes."
|
|
87156
87534
|
},
|
|
87157
87535
|
"mode": {
|
|
87158
87536
|
"allOf": [
|
|
@@ -87194,12 +87572,11 @@ var OPERATIONS = [
|
|
|
87194
87572
|
"surface": {
|
|
87195
87573
|
"type": "string",
|
|
87196
87574
|
"enum": [
|
|
87197
|
-
"commands",
|
|
87198
87575
|
"terminal",
|
|
87199
87576
|
"files",
|
|
87200
87577
|
"browser"
|
|
87201
87578
|
],
|
|
87202
|
-
"description": "
|
|
87579
|
+
"description": "terminal: every terminal on it, the agent's commands and a person's shells alike, on one socket in terminal@1: text messages are TerminalEvent one way and TerminalInput the other. Watching reads them all; operating also opens a shell, types into any of them, the agent's included, sizes and stops them. files: its workspace's files, in files@1: text messages are FilesRequest one way and FilesAnswer the other, and a file's bytes travel as binary messages of at most 1,048,576 bytes. Watching lists and reads; operating also writes, which a computer with read access doesn't offer. browser: its browser, in screencast@1: binary messages are JPEG frames of the page shown, text messages are ScreencastEvent one way and ScreencastInput the other. Nobody waits for anybody: the agent's tools and a person's input act on the same browser at once. The page shown is each socket's own. A socket is shown each new tab as it opens, until it sends a tab input to show or open one; from then its page changes only when that page closes."
|
|
87203
87580
|
},
|
|
87204
87581
|
"mode": {
|
|
87205
87582
|
"type": "string",
|
|
@@ -87419,7 +87796,7 @@ ${JSON.stringify({ code: status, message })}
|
|
|
87419
87796
|
const scheme = options.tls ? "https" : "http";
|
|
87420
87797
|
base = options.publicUrl?.replace(/\/+$/, "") ?? `${scheme}://${bound.family === "IPv6" ? `[${bound.address}]` : bound.address}:${bound.port}`;
|
|
87421
87798
|
await computers.resume();
|
|
87422
|
-
const record3 =
|
|
87799
|
+
const record3 = join10(data, "server.json");
|
|
87423
87800
|
writePrivate(
|
|
87424
87801
|
record3,
|
|
87425
87802
|
JSON.stringify({ url: base, pid: process.pid, isolation: options.runtime.isolation })
|
|
@@ -87459,7 +87836,7 @@ var USAGE = `Usage: agentcomputer <command> [--data DIR]
|
|
|
87459
87836
|
stop <id> stop it, keeping its folder and token
|
|
87460
87837
|
start <id> [--timeout SECONDS] start a stopped computer again
|
|
87461
87838
|
logs <id> what its servers wrote
|
|
87462
|
-
live <id> --surface
|
|
87839
|
+
live <id> --surface terminal|files|browser [--mode watch|operate]
|
|
87463
87840
|
a one-time ticket to open one live, as a WebSocket
|
|
87464
87841
|
kill <id> stop it and remove what the service made
|
|
87465
87842
|
|
|
@@ -87472,13 +87849,13 @@ var UsageError = class extends Error {
|
|
|
87472
87849
|
function dataFolder(given) {
|
|
87473
87850
|
const xdg = process.env.XDG_DATA_HOME;
|
|
87474
87851
|
return resolve5(
|
|
87475
|
-
given ?? process.env.AGENTCOMPUTER_DATA ?? (xdg && isAbsolute2(xdg) ?
|
|
87852
|
+
given ?? process.env.AGENTCOMPUTER_DATA ?? (xdg && isAbsolute2(xdg) ? join11(xdg, "agentcomputer") : join11(homedir2(), ".local", "share", "agentcomputer"))
|
|
87476
87853
|
);
|
|
87477
87854
|
}
|
|
87478
87855
|
function connectionTo(data) {
|
|
87479
87856
|
try {
|
|
87480
|
-
const { url: url3 } = JSON.parse(readFileSync6(
|
|
87481
|
-
return { apiUrl: url3, apiKey: readFileSync6(
|
|
87857
|
+
const { url: url3 } = JSON.parse(readFileSync6(join11(data, "server.json"), "utf8"));
|
|
87858
|
+
return { apiUrl: url3, apiKey: readFileSync6(join11(data, "control-token"), "utf8").trim() };
|
|
87482
87859
|
} catch {
|
|
87483
87860
|
throw new Error(
|
|
87484
87861
|
`No service is running with its data in ${data}. Start one: agentcomputer serve`
|