@agentorg/computer 0.0.7 → 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 +40 -16
- package/dist/THIRD_PARTY_LICENSES.txt +1 -1
- package/dist/agentcomputer.js +1905 -781
- package/dist/sdk.js +34 -34
- package/dist/types/generated/schema.d.ts +149 -14
- package/dist/types/sdk.d.ts +23 -30
- package/package.json +3 -3
package/dist/agentcomputer.js
CHANGED
|
@@ -2295,7 +2295,7 @@ var require_websocket = __commonJS({
|
|
|
2295
2295
|
var net = __require("net");
|
|
2296
2296
|
var querystring = __require("querystring");
|
|
2297
2297
|
var tls = __require("tls");
|
|
2298
|
-
var { randomBytes:
|
|
2298
|
+
var { randomBytes: randomBytes6, createHash: createHash2 } = __require("crypto");
|
|
2299
2299
|
var { Duplex, Readable: Readable5 } = __require("stream");
|
|
2300
2300
|
var { URL: URL2 } = __require("url");
|
|
2301
2301
|
var PerMessageDeflate2 = require_permessage_deflate();
|
|
@@ -2845,7 +2845,7 @@ var require_websocket = __commonJS({
|
|
|
2845
2845
|
}
|
|
2846
2846
|
}
|
|
2847
2847
|
const defaultPort = isSecure ? 443 : 80;
|
|
2848
|
-
const key =
|
|
2848
|
+
const key = randomBytes6(16).toString("base64");
|
|
2849
2849
|
const request = isSecure ? https.request : http.request;
|
|
2850
2850
|
const protocolSet = /* @__PURE__ */ new Set();
|
|
2851
2851
|
let perMessageDeflate;
|
|
@@ -3095,21 +3095,21 @@ var require_websocket = __commonJS({
|
|
|
3095
3095
|
process.nextTick(cb, err);
|
|
3096
3096
|
}
|
|
3097
3097
|
}
|
|
3098
|
-
function receiverOnConclude(code,
|
|
3098
|
+
function receiverOnConclude(code, reason3) {
|
|
3099
3099
|
const websocket = this[kWebSocket];
|
|
3100
3100
|
websocket._closeFrameReceived = true;
|
|
3101
3101
|
websocket._closeCode = code;
|
|
3102
|
-
if (
|
|
3102
|
+
if (reason3.length === 0) {
|
|
3103
3103
|
websocket._closeMessage = EMPTY_BUFFER;
|
|
3104
3104
|
} else {
|
|
3105
|
-
websocket._closeMessage = Buffer.allocUnsafeSlow(
|
|
3106
|
-
websocket._closeMessage.set(
|
|
3105
|
+
websocket._closeMessage = Buffer.allocUnsafeSlow(reason3.length);
|
|
3106
|
+
websocket._closeMessage.set(reason3);
|
|
3107
3107
|
}
|
|
3108
3108
|
if (websocket._socket[kWebSocket] === void 0) return;
|
|
3109
3109
|
websocket._socket.removeListener("data", socketOnData);
|
|
3110
3110
|
process.nextTick(resume, websocket._socket);
|
|
3111
3111
|
if (code === 1005) websocket.close();
|
|
3112
|
-
else websocket.close(code,
|
|
3112
|
+
else websocket.close(code, reason3);
|
|
3113
3113
|
}
|
|
3114
3114
|
function receiverOnDrain() {
|
|
3115
3115
|
const websocket = this[kWebSocket];
|
|
@@ -3268,7 +3268,7 @@ var require_stream = __commonJS({
|
|
|
3268
3268
|
};
|
|
3269
3269
|
duplex._final = function(callback) {
|
|
3270
3270
|
if (ws.readyState === ws.CONNECTING) {
|
|
3271
|
-
ws.once("open", function
|
|
3271
|
+
ws.once("open", function open4() {
|
|
3272
3272
|
duplex._final(callback);
|
|
3273
3273
|
});
|
|
3274
3274
|
return;
|
|
@@ -3289,7 +3289,7 @@ var require_stream = __commonJS({
|
|
|
3289
3289
|
};
|
|
3290
3290
|
duplex._write = function(chunk, encoding, callback) {
|
|
3291
3291
|
if (ws.readyState === ws.CONNECTING) {
|
|
3292
|
-
ws.once("open", function
|
|
3292
|
+
ws.once("open", function open4() {
|
|
3293
3293
|
duplex._write(chunk, encoding, callback);
|
|
3294
3294
|
});
|
|
3295
3295
|
return;
|
|
@@ -3772,9 +3772,9 @@ var playwright = () => {
|
|
|
3772
3772
|
// src/browser-host.ts
|
|
3773
3773
|
var sandboxFor = (options) => options.sandbox ?? (process.platform !== "linux" || !options.executable && !["chromium", "chrome-for-testing"].includes(options.channel ?? "chrome"));
|
|
3774
3774
|
async function hostBrowser(options) {
|
|
3775
|
-
const
|
|
3776
|
-
process.env.XDG_RUNTIME_DIR =
|
|
3777
|
-
process.env.TMPDIR =
|
|
3775
|
+
const run2 = mkdtempSync(join("/tmp", "agentcomputer-"));
|
|
3776
|
+
process.env.XDG_RUNTIME_DIR = run2;
|
|
3777
|
+
process.env.TMPDIR = run2;
|
|
3778
3778
|
const context = await playwright().chromium.launchPersistentContext(options.profile, {
|
|
3779
3779
|
headless: true,
|
|
3780
3780
|
chromiumSandbox: sandboxFor(options),
|
|
@@ -3783,7 +3783,7 @@ async function hostBrowser(options) {
|
|
|
3783
3783
|
const browser = context.browser();
|
|
3784
3784
|
if (!browser) throw new Error("The browser can't be shared.");
|
|
3785
3785
|
const { endpoint } = await browser.bind("agentcomputer");
|
|
3786
|
-
if (!endpoint.startsWith(
|
|
3786
|
+
if (!endpoint.startsWith(run2 + sep)) {
|
|
3787
3787
|
await context.close();
|
|
3788
3788
|
throw new Error(`The browser's socket isn't in its own folder: ${endpoint}`);
|
|
3789
3789
|
}
|
|
@@ -3797,13 +3797,54 @@ async function hostBrowser(options) {
|
|
|
3797
3797
|
});
|
|
3798
3798
|
await context.close().catch(() => {
|
|
3799
3799
|
});
|
|
3800
|
-
rmSync(
|
|
3800
|
+
rmSync(run2, { recursive: true, force: true });
|
|
3801
3801
|
process.exit(0);
|
|
3802
3802
|
};
|
|
3803
3803
|
for (const signal of ["SIGINT", "SIGTERM"]) process.once(signal, () => void stop());
|
|
3804
3804
|
context.on("close", () => void stop());
|
|
3805
3805
|
}
|
|
3806
3806
|
|
|
3807
|
+
// src/browser-mcp.ts
|
|
3808
|
+
import { createRequire as createRequire2 } from "node:module";
|
|
3809
|
+
var require2 = createRequire2(import.meta.url);
|
|
3810
|
+
var mcpParts = () => require2("playwright-core/lib/coreBundle").tools;
|
|
3811
|
+
async function serveBrowserMcp(options) {
|
|
3812
|
+
const parts = mcpParts();
|
|
3813
|
+
parts.setupExitWatchdog();
|
|
3814
|
+
const config3 = await parts.resolveCLIConfigForMCP({
|
|
3815
|
+
endpoint: options.endpoint,
|
|
3816
|
+
outputDir: options.outputDir,
|
|
3817
|
+
host: "127.0.0.1",
|
|
3818
|
+
port: 0,
|
|
3819
|
+
// The tool that closes the browser refuses, as it does for any browser its clients share.
|
|
3820
|
+
sharedBrowserContext: true
|
|
3821
|
+
});
|
|
3822
|
+
const tools = parts.filteredTools(config3);
|
|
3823
|
+
await parts.start(
|
|
3824
|
+
{
|
|
3825
|
+
name: "Playwright",
|
|
3826
|
+
nameInConfig: "playwright",
|
|
3827
|
+
// playwright-core's, as Playwright MCP's program gives it.
|
|
3828
|
+
version: require2("playwright-core/package.json").version,
|
|
3829
|
+
toolSchemas: tools.map((tool) => tool.schema),
|
|
3830
|
+
ignoreRoots: config3.ignoreRoots,
|
|
3831
|
+
/** One for each client's session: a connection of its own to the one browser. */
|
|
3832
|
+
create: async () => {
|
|
3833
|
+
const browser = await playwright().chromium.connect(options.endpoint);
|
|
3834
|
+
const context = browser.contexts()[0];
|
|
3835
|
+
if (!context) {
|
|
3836
|
+
await browser.close();
|
|
3837
|
+
throw new Error("The browser has no context to work in.");
|
|
3838
|
+
}
|
|
3839
|
+
return new parts.BrowserBackend(config3, context, tools, {
|
|
3840
|
+
dispose: () => browser.close()
|
|
3841
|
+
});
|
|
3842
|
+
}
|
|
3843
|
+
},
|
|
3844
|
+
config3.server
|
|
3845
|
+
);
|
|
3846
|
+
}
|
|
3847
|
+
|
|
3807
3848
|
// src/container.ts
|
|
3808
3849
|
import { execFile, spawn as spawn3 } from "node:child_process";
|
|
3809
3850
|
import { readFileSync as readFileSync3 } from "node:fs";
|
|
@@ -3815,8 +3856,7 @@ import { promisify } from "node:util";
|
|
|
3815
3856
|
|
|
3816
3857
|
// src/browser.ts
|
|
3817
3858
|
import { spawn } from "node:child_process";
|
|
3818
|
-
import {
|
|
3819
|
-
import { dirname, join as join2 } from "node:path";
|
|
3859
|
+
import { join as join2 } from "node:path";
|
|
3820
3860
|
import { createInterface } from "node:readline";
|
|
3821
3861
|
import { pipeline, Readable } from "node:stream";
|
|
3822
3862
|
import { fileURLToPath } from "node:url";
|
|
@@ -6576,10 +6616,10 @@ var $ZodUnion = /* @__PURE__ */ $constructor("$ZodUnion", (inst, def) => {
|
|
|
6576
6616
|
}
|
|
6577
6617
|
return void 0;
|
|
6578
6618
|
});
|
|
6579
|
-
const
|
|
6619
|
+
const first2 = def.options.length === 1 ? def.options[0]._zod.run : null;
|
|
6580
6620
|
inst._zod.parse = (payload, ctx) => {
|
|
6581
|
-
if (
|
|
6582
|
-
return
|
|
6621
|
+
if (first2) {
|
|
6622
|
+
return first2(payload, ctx);
|
|
6583
6623
|
}
|
|
6584
6624
|
let async = false;
|
|
6585
6625
|
const results = [];
|
|
@@ -8977,9 +9017,9 @@ var numberProcessor = (schema, ctx, _json, params) => {
|
|
|
8977
9017
|
else
|
|
8978
9018
|
handleUnrepresentable(schema, ctx, json4, params, `A multipleOf divisor of ${divisor} cannot be represented in JSON Schema`);
|
|
8979
9019
|
}
|
|
8980
|
-
const [
|
|
8981
|
-
if (
|
|
8982
|
-
json4.multipleOf =
|
|
9020
|
+
const [first2, ...rest] = divisors;
|
|
9021
|
+
if (first2 !== void 0)
|
|
9022
|
+
json4.multipleOf = first2;
|
|
8983
9023
|
if (rest.length)
|
|
8984
9024
|
json4.allOf = [...json4.allOf ?? [], ...rest.map((m) => ({ multipleOf: m }))];
|
|
8985
9025
|
}
|
|
@@ -15120,7 +15160,7 @@ function linkedRoundAbort(outer) {
|
|
|
15120
15160
|
if (outer?.aborted) controller.abort(outer.reason);
|
|
15121
15161
|
return {
|
|
15122
15162
|
signal: controller.signal,
|
|
15123
|
-
abort: (
|
|
15163
|
+
abort: (reason3) => controller.abort(reason3),
|
|
15124
15164
|
dispose: () => outer?.removeEventListener("abort", onOuterAbort)
|
|
15125
15165
|
};
|
|
15126
15166
|
}
|
|
@@ -15963,8 +16003,8 @@ var Protocol = class {
|
|
|
15963
16003
|
return;
|
|
15964
16004
|
}
|
|
15965
16005
|
if (options?.signal?.aborted) {
|
|
15966
|
-
const
|
|
15967
|
-
throw
|
|
16006
|
+
const reason3 = options.signal.reason;
|
|
16007
|
+
throw reason3 instanceof SdkError ? reason3 : new SdkError(SdkErrorCode.RequestTimeout, String(reason3));
|
|
15968
16008
|
}
|
|
15969
16009
|
const requestAbort = codec2.era === MODERN_WIRE_REVISION && this._transport.hasPerRequestStream === true ? new AbortController() : void 0;
|
|
15970
16010
|
const messageId = this._requestMessageId++;
|
|
@@ -15986,7 +16026,7 @@ var Protocol = class {
|
|
|
15986
16026
|
}
|
|
15987
16027
|
const outbound = this._envelopeOutbound(jsonrpcRequest);
|
|
15988
16028
|
let responseReceived = false;
|
|
15989
|
-
const cancel = (
|
|
16029
|
+
const cancel = (reason3) => {
|
|
15990
16030
|
if (responseReceived) return;
|
|
15991
16031
|
this._progressHandlers.delete(messageId);
|
|
15992
16032
|
if (requestAbort === void 0) {
|
|
@@ -15995,7 +16035,7 @@ var Protocol = class {
|
|
|
15995
16035
|
method: "notifications/cancelled",
|
|
15996
16036
|
params: {
|
|
15997
16037
|
requestId: messageId,
|
|
15998
|
-
reason: String(
|
|
16038
|
+
reason: String(reason3)
|
|
15999
16039
|
}
|
|
16000
16040
|
}), {
|
|
16001
16041
|
relatedRequestId,
|
|
@@ -16003,7 +16043,7 @@ var Protocol = class {
|
|
|
16003
16043
|
onresumptiontoken
|
|
16004
16044
|
}).catch((error62) => this._onerror(/* @__PURE__ */ new Error(`Failed to send cancellation: ${error62}`)));
|
|
16005
16045
|
} else requestAbort.abort();
|
|
16006
|
-
reject(
|
|
16046
|
+
reject(reason3 instanceof SdkError ? reason3 : new SdkError(SdkErrorCode.RequestTimeout, String(reason3)));
|
|
16007
16047
|
};
|
|
16008
16048
|
this._responseHandlers.set(messageId, (response) => {
|
|
16009
16049
|
if (options?.signal?.aborted) return;
|
|
@@ -19189,8 +19229,8 @@ var require_utils = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
|
19189
19229
|
}
|
|
19190
19230
|
if (bestLength < 2) return hextets.join(":");
|
|
19191
19231
|
const head = hextets.slice(0, bestStart).join(":");
|
|
19192
|
-
const
|
|
19193
|
-
return head + "::" +
|
|
19232
|
+
const tail2 = hextets.slice(bestStart + bestLength).join(":");
|
|
19233
|
+
return head + "::" + tail2;
|
|
19194
19234
|
}
|
|
19195
19235
|
function normalizeIPv6Address(input2) {
|
|
19196
19236
|
const compression = input2.indexOf("::");
|
|
@@ -20413,7 +20453,7 @@ var require_core$3 = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
|
20413
20453
|
}
|
|
20414
20454
|
errorsText(errors = this.errors, { separator = ", ", dataVar = "data" } = {}) {
|
|
20415
20455
|
if (!errors || errors.length === 0) return "No errors";
|
|
20416
|
-
return errors.map((e) => `${dataVar}${e.instancePath} ${e.message}`).reduce((
|
|
20456
|
+
return errors.map((e) => `${dataVar}${e.instancePath} ${e.message}`).reduce((text3, msg) => text3 + separator + msg);
|
|
20417
20457
|
}
|
|
20418
20458
|
$dataMetaSchema(metaSchema, keywordsJsonPointers) {
|
|
20419
20459
|
const rules = this.RULES.all;
|
|
@@ -23818,8 +23858,8 @@ async function random(size) {
|
|
|
23818
23858
|
const evenDistCutoff = Math.pow(2, 8) - Math.pow(2, 8) % mask.length;
|
|
23819
23859
|
let result = "";
|
|
23820
23860
|
while (result.length < size) {
|
|
23821
|
-
const
|
|
23822
|
-
for (const randomByte of
|
|
23861
|
+
const randomBytes6 = await getRandomValues(size - result.length);
|
|
23862
|
+
for (const randomByte of randomBytes6) {
|
|
23823
23863
|
if (randomByte < evenDistCutoff) {
|
|
23824
23864
|
result += mask[randomByte % mask.length];
|
|
23825
23865
|
}
|
|
@@ -25485,11 +25525,11 @@ function normalizeReply(reply, timeoutMs) {
|
|
|
25485
25525
|
error: error62
|
|
25486
25526
|
};
|
|
25487
25527
|
if (error62 instanceof SdkHttpError) {
|
|
25488
|
-
const
|
|
25528
|
+
const text3 = error62.data?.text;
|
|
25489
25529
|
return {
|
|
25490
25530
|
kind: "http-error",
|
|
25491
25531
|
status: error62.data.status,
|
|
25492
|
-
body: typeof
|
|
25532
|
+
body: typeof text3 === "string" ? text3 : void 0,
|
|
25493
25533
|
statusText: error62.data.statusText
|
|
25494
25534
|
};
|
|
25495
25535
|
}
|
|
@@ -25731,10 +25771,10 @@ var Client = class extends Protocol {
|
|
|
25731
25771
|
this._discoverResult = void 0;
|
|
25732
25772
|
this._autoOpenedSubscription = void 0;
|
|
25733
25773
|
if (this._listenState.size > 0) {
|
|
25734
|
-
const
|
|
25774
|
+
const reason3 = new SdkError(SdkErrorCode.ConnectionClosed, "subscriptions/listen: client reconnected or closed; subscription state from the previous connection was reset");
|
|
25735
25775
|
for (const entry of this._listenState.values()) entry.settle({
|
|
25736
25776
|
cause: "remote",
|
|
25737
|
-
error:
|
|
25777
|
+
error: reason3
|
|
25738
25778
|
});
|
|
25739
25779
|
}
|
|
25740
25780
|
this._listenState.clear();
|
|
@@ -26478,8 +26518,8 @@ var Client = class extends Protocol {
|
|
|
26478
26518
|
const hit = await this._cache.read(method, params).catch((error62) => void this._reportStoreError(error62));
|
|
26479
26519
|
if (hit !== void 0) {
|
|
26480
26520
|
if (options?.signal?.aborted) {
|
|
26481
|
-
const
|
|
26482
|
-
throw
|
|
26521
|
+
const reason3 = options.signal.reason;
|
|
26522
|
+
throw reason3 instanceof SdkError ? reason3 : new SdkError(SdkErrorCode.RequestTimeout, String(reason3));
|
|
26483
26523
|
}
|
|
26484
26524
|
return hit.value;
|
|
26485
26525
|
}
|
|
@@ -26620,8 +26660,8 @@ var Client = class extends Protocol {
|
|
|
26620
26660
|
protocolVersion: negotiated
|
|
26621
26661
|
});
|
|
26622
26662
|
if (options?.signal?.aborted) {
|
|
26623
|
-
const
|
|
26624
|
-
throw
|
|
26663
|
+
const reason3 = options.signal.reason;
|
|
26664
|
+
throw reason3 instanceof SdkError ? reason3 : new SdkError(SdkErrorCode.RequestTimeout, String(reason3));
|
|
26625
26665
|
}
|
|
26626
26666
|
const requestAbort = new AbortController();
|
|
26627
26667
|
const listenId = `listen:${this._nextListenId++}`;
|
|
@@ -26684,10 +26724,10 @@ var Client = class extends Protocol {
|
|
|
26684
26724
|
const callerSignal = options.signal;
|
|
26685
26725
|
onCallerAbort = () => {
|
|
26686
26726
|
if (state === "closed") return;
|
|
26687
|
-
const
|
|
26727
|
+
const reason3 = callerSignal.reason;
|
|
26688
26728
|
settle3({
|
|
26689
26729
|
cause: "local",
|
|
26690
|
-
error:
|
|
26730
|
+
error: reason3 instanceof Error ? reason3 : new Error(String(reason3 ?? "Aborted"))
|
|
26691
26731
|
});
|
|
26692
26732
|
wireTeardown().catch(() => {
|
|
26693
26733
|
});
|
|
@@ -26814,10 +26854,10 @@ var Client = class extends Protocol {
|
|
|
26814
26854
|
*/
|
|
26815
26855
|
_onclose() {
|
|
26816
26856
|
if (this._listenState.size > 0) {
|
|
26817
|
-
const
|
|
26857
|
+
const reason3 = new SdkError(SdkErrorCode.ConnectionClosed, "Connection closed");
|
|
26818
26858
|
for (const entry of this._listenState.values()) entry.settle({
|
|
26819
26859
|
cause: "remote",
|
|
26820
|
-
error:
|
|
26860
|
+
error: reason3
|
|
26821
26861
|
});
|
|
26822
26862
|
this._listenState.clear();
|
|
26823
26863
|
}
|
|
@@ -27467,8 +27507,8 @@ var StreamableHTTPClientTransport = class {
|
|
|
27467
27507
|
}
|
|
27468
27508
|
/** Error text for a redirect `response` that was not followed, or `undefined` for any other response. */
|
|
27469
27509
|
_unfollowedRedirect(url3, response) {
|
|
27470
|
-
const
|
|
27471
|
-
return
|
|
27510
|
+
const text3 = this._redirectPolicy === "follow" ? void 0 : unfollowedRedirect(url3, response);
|
|
27511
|
+
return text3 && `${text3} (redirectPolicy: 'same-origin')`;
|
|
27472
27512
|
}
|
|
27473
27513
|
async _commonHeaders() {
|
|
27474
27514
|
const headers = new Headers(this._requestInit?.headers || void 0);
|
|
@@ -27559,13 +27599,13 @@ var StreamableHTTPClientTransport = class {
|
|
|
27559
27599
|
if (response.status === 403) {
|
|
27560
27600
|
const { resourceMetadataUrl, scope, error: error62, errorDescription } = extractWWWAuthenticateParams(response);
|
|
27561
27601
|
if (error62 === "insufficient_scope") {
|
|
27562
|
-
const
|
|
27602
|
+
const text3 = await response.text?.().catch(() => null);
|
|
27563
27603
|
if (await this._stepUpAuthorize({
|
|
27564
27604
|
scope,
|
|
27565
27605
|
resourceMetadataUrl,
|
|
27566
27606
|
errorDescription,
|
|
27567
27607
|
statusText: response.statusText,
|
|
27568
|
-
text:
|
|
27608
|
+
text: text3
|
|
27569
27609
|
}, stepUpRetries) !== "AUTHORIZED") throw markAuthSeamEscape(new UnauthorizedError());
|
|
27570
27610
|
return this._startOrAuthSse(options, isAuthRetry, stepUpRetries + 1);
|
|
27571
27611
|
}
|
|
@@ -27576,8 +27616,8 @@ var StreamableHTTPClientTransport = class {
|
|
|
27576
27616
|
options.onRequestStreamEnd?.();
|
|
27577
27617
|
return;
|
|
27578
27618
|
}
|
|
27579
|
-
const
|
|
27580
|
-
throw new SdkHttpError(SdkErrorCode.ClientHttpFailedToOpenStream, `Failed to open SSE stream: ${
|
|
27619
|
+
const reason3 = this._unfollowedRedirect(this._url, response) ?? response.statusText;
|
|
27620
|
+
throw new SdkHttpError(SdkErrorCode.ClientHttpFailedToOpenStream, `Failed to open SSE stream: ${reason3}`, {
|
|
27581
27621
|
status: response.status,
|
|
27582
27622
|
statusText: response.statusText
|
|
27583
27623
|
});
|
|
@@ -27797,7 +27837,7 @@ var StreamableHTTPClientTransport = class {
|
|
|
27797
27837
|
}));
|
|
27798
27838
|
throw markAuthSeamEscape(new UnauthorizedError());
|
|
27799
27839
|
}
|
|
27800
|
-
const
|
|
27840
|
+
const text3 = await response.text?.().catch(() => null);
|
|
27801
27841
|
if (response.status === 403) {
|
|
27802
27842
|
const { resourceMetadataUrl, scope, error: error62, errorDescription } = extractWWWAuthenticateParams(response);
|
|
27803
27843
|
if (error62 === "insufficient_scope") {
|
|
@@ -27806,13 +27846,13 @@ var StreamableHTTPClientTransport = class {
|
|
|
27806
27846
|
resourceMetadataUrl,
|
|
27807
27847
|
errorDescription,
|
|
27808
27848
|
statusText: response.statusText,
|
|
27809
|
-
text:
|
|
27849
|
+
text: text3
|
|
27810
27850
|
}, stepUpRetries) !== "AUTHORIZED") throw markAuthSeamEscape(new UnauthorizedError());
|
|
27811
27851
|
return this._send(message, options, isAuthRetry, stepUpRetries + 1);
|
|
27812
27852
|
}
|
|
27813
27853
|
}
|
|
27814
|
-
if (response.status === 400 && typeof
|
|
27815
|
-
const parsed = JSONRPCMessageSchema.parse(JSON.parse(
|
|
27854
|
+
if (response.status === 400 && typeof text3 === "string" && this._isModernEnvelopedRequest(message)) try {
|
|
27855
|
+
const parsed = JSONRPCMessageSchema.parse(JSON.parse(text3));
|
|
27816
27856
|
const requests = (Array.isArray(message) ? message : [message]).filter((m) => isJSONRPCRequest(m));
|
|
27817
27857
|
if (isJSONRPCErrorResponse(parsed) && requests.some((r) => r.id === parsed.id)) {
|
|
27818
27858
|
this.onmessage?.(parsed);
|
|
@@ -27820,11 +27860,11 @@ var StreamableHTTPClientTransport = class {
|
|
|
27820
27860
|
}
|
|
27821
27861
|
} catch {
|
|
27822
27862
|
}
|
|
27823
|
-
const
|
|
27824
|
-
throw new SdkHttpError(SdkErrorCode.ClientHttpNotImplemented, `Error POSTing to endpoint: ${
|
|
27863
|
+
const reason3 = this._unfollowedRedirect(this._url, response) ?? text3;
|
|
27864
|
+
throw new SdkHttpError(SdkErrorCode.ClientHttpNotImplemented, `Error POSTing to endpoint: ${reason3}`, {
|
|
27825
27865
|
status: response.status,
|
|
27826
27866
|
statusText: response.statusText,
|
|
27827
|
-
text:
|
|
27867
|
+
text: text3
|
|
27828
27868
|
});
|
|
27829
27869
|
}
|
|
27830
27870
|
if (response.status === 202) {
|
|
@@ -27938,18 +27978,16 @@ function sendAnswer(upstream, response) {
|
|
|
27938
27978
|
pipeline(Readable.fromWeb(upstream.body), response, () => {
|
|
27939
27979
|
});
|
|
27940
27980
|
}
|
|
27941
|
-
var mcpCli = () => join2(dirname(createRequire2(import.meta.url).resolve("@playwright/mcp/package.json")), "cli.js");
|
|
27942
27981
|
var self = fileURLToPath(import.meta.url);
|
|
27943
|
-
|
|
27944
|
-
function withoutHidden(text2, hidden) {
|
|
27982
|
+
function withoutHidden(text3, hidden) {
|
|
27945
27983
|
const clean = (json4) => {
|
|
27946
27984
|
const message = JSON.parse(json4);
|
|
27947
27985
|
if (message.result?.tools)
|
|
27948
27986
|
message.result.tools = message.result.tools.filter((tool) => !hidden.has(tool.name));
|
|
27949
27987
|
return JSON.stringify(message);
|
|
27950
27988
|
};
|
|
27951
|
-
if (
|
|
27952
|
-
return
|
|
27989
|
+
if (text3.trimStart().startsWith("{")) return clean(text3);
|
|
27990
|
+
return text3.replace(/^data: (\{.*\})$/gm, (_, json4) => `data: ${clean(json4)}`);
|
|
27953
27991
|
}
|
|
27954
27992
|
async function end(child) {
|
|
27955
27993
|
if (child.exitCode !== null || child.signalCode !== null) return;
|
|
@@ -28012,17 +28050,7 @@ var Browser = class {
|
|
|
28012
28050
|
if (this.#stopping) return failed(new Error("It stopped."));
|
|
28013
28051
|
const mcp = spawn(
|
|
28014
28052
|
process.execPath,
|
|
28015
|
-
[
|
|
28016
|
-
mcpCli(),
|
|
28017
|
-
"--host",
|
|
28018
|
-
"127.0.0.1",
|
|
28019
|
-
"--port",
|
|
28020
|
-
"0",
|
|
28021
|
-
"--endpoint",
|
|
28022
|
-
endpoint,
|
|
28023
|
-
"--output-dir",
|
|
28024
|
-
join2(folder, "output")
|
|
28025
|
-
],
|
|
28053
|
+
[self, "browser-mcp", "--endpoint", endpoint, "--output", join2(folder, "output")],
|
|
28026
28054
|
// Its own limit on which files it touches is the folder it starts in.
|
|
28027
28055
|
{ cwd, stdio: ["ignore", "pipe", "pipe"] }
|
|
28028
28056
|
);
|
|
@@ -28075,27 +28103,24 @@ var Browser = class {
|
|
|
28075
28103
|
} catch {
|
|
28076
28104
|
}
|
|
28077
28105
|
const tool = message.method === "tools/call" ? String(message.params?.name) : void 0;
|
|
28078
|
-
|
|
28106
|
+
if (tool && this.#hidden.has(tool)) {
|
|
28079
28107
|
response.writeHead(200, { "Content-Type": "application/json" });
|
|
28080
|
-
response.end(
|
|
28108
|
+
return void response.end(
|
|
28081
28109
|
JSON.stringify({
|
|
28082
28110
|
jsonrpc: "2.0",
|
|
28083
28111
|
id: message.id ?? null,
|
|
28084
|
-
|
|
28112
|
+
error: { code: -32602, message: `Unknown tool: ${tool}` }
|
|
28085
28113
|
})
|
|
28086
28114
|
);
|
|
28087
|
-
};
|
|
28088
|
-
if (tool && this.#hidden.has(tool)) return refuse(`Unknown tool: ${tool}`, -32602);
|
|
28089
|
-
if (tool && this.gate) {
|
|
28090
|
-
const answer = await this.gate.wait();
|
|
28091
|
-
if (answer) return refuse(answer);
|
|
28092
28115
|
}
|
|
28093
28116
|
const headers = {};
|
|
28094
28117
|
for (const name of FORWARDED) {
|
|
28095
28118
|
const value = request.headers[name];
|
|
28096
28119
|
if (typeof value === "string") headers[name] = value;
|
|
28097
28120
|
}
|
|
28098
|
-
|
|
28121
|
+
const session = headers["mcp-session-id"] ?? "";
|
|
28122
|
+
if (request.method === "DELETE") this.gate?.sessionEnded(session);
|
|
28123
|
+
if (tool && this.gate?.takeAnsweredChooser(session) && tool !== "browser_file_upload")
|
|
28099
28124
|
await fetch(`${origin}/mcp`, {
|
|
28100
28125
|
method: "POST",
|
|
28101
28126
|
headers,
|
|
@@ -28107,15 +28132,15 @@ var Browser = class {
|
|
|
28107
28132
|
})
|
|
28108
28133
|
}).then((answer) => answer.text()).catch(() => {
|
|
28109
28134
|
});
|
|
28110
|
-
if (tool
|
|
28135
|
+
if (tool === "browser_file_upload") this.gate?.agentAnsweredChooser();
|
|
28111
28136
|
const upstream = await fetch(`${origin}/mcp`, {
|
|
28112
28137
|
method: request.method,
|
|
28113
28138
|
headers,
|
|
28114
28139
|
...body.length > 0 ? { body } : {}
|
|
28115
28140
|
});
|
|
28116
28141
|
if (message.method === "tools/list" && this.#hidden.size > 0) {
|
|
28117
|
-
const
|
|
28118
|
-
return sendAnswer(new Response(
|
|
28142
|
+
const text3 = withoutHidden(await upstream.text(), this.#hidden);
|
|
28143
|
+
return sendAnswer(new Response(text3, upstream), response);
|
|
28119
28144
|
}
|
|
28120
28145
|
sendAnswer(upstream, response);
|
|
28121
28146
|
}
|
|
@@ -28131,7 +28156,9 @@ import { homedir } from "node:os";
|
|
|
28131
28156
|
import { join as join7 } from "node:path";
|
|
28132
28157
|
|
|
28133
28158
|
// src/runtime.ts
|
|
28134
|
-
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";
|
|
28135
28162
|
|
|
28136
28163
|
// node_modules/@agentorg/exec/dist/lib.js
|
|
28137
28164
|
import { realpathSync } from "node:fs";
|
|
@@ -28139,9 +28166,22 @@ import { resolve as resolve3 } from "node:path";
|
|
|
28139
28166
|
import { STATUS_CODES, ServerResponse, createServer } from "node:http";
|
|
28140
28167
|
import { Http2ServerRequest, constants } from "node:http2";
|
|
28141
28168
|
import { Readable as Readable2 } from "node:stream";
|
|
28169
|
+
import { randomBytes as randomBytes3 } from "node:crypto";
|
|
28170
|
+
import { lstat as lstat2, open as open2, readdir as readdir2, rename as rename2, rm as rm2, stat as stat2 } from "node:fs/promises";
|
|
28171
|
+
import { dirname as dirname2, join as join3 } from "node:path";
|
|
28142
28172
|
import { randomBytes as randomBytes2 } from "node:crypto";
|
|
28143
|
-
import {
|
|
28144
|
-
|
|
28173
|
+
import {
|
|
28174
|
+
chmod,
|
|
28175
|
+
lstat,
|
|
28176
|
+
readdir,
|
|
28177
|
+
readFile,
|
|
28178
|
+
realpath,
|
|
28179
|
+
rename,
|
|
28180
|
+
rm,
|
|
28181
|
+
stat,
|
|
28182
|
+
writeFile
|
|
28183
|
+
} from "node:fs/promises";
|
|
28184
|
+
import { basename, dirname, join as join4, resolve, sep as sep2 } from "node:path";
|
|
28145
28185
|
import { spawn as spawn2 } from "node:child_process";
|
|
28146
28186
|
import { randomBytes } from "node:crypto";
|
|
28147
28187
|
import { StringDecoder } from "node:string_decoder";
|
|
@@ -28373,7 +28413,7 @@ var require_ignore = __commonJS2({
|
|
|
28373
28413
|
// ever starts again inside it -- `(\\+?) ` did, which made a long run
|
|
28374
28414
|
// quadratic.
|
|
28375
28415
|
/(\\+)( ?)/g,
|
|
28376
|
-
(_,
|
|
28416
|
+
(_, run2, space) => space ? run2.slice(0, run2.length - run2.length % 2) + SPACE2 : run2,
|
|
28377
28417
|
ESCAPE + SPACE2
|
|
28378
28418
|
],
|
|
28379
28419
|
// git's wildmatch() reads a run of two or more asterisks as one globstar
|
|
@@ -28559,10 +28599,10 @@ var require_ignore = __commonJS2({
|
|
|
28559
28599
|
var REGEX_REPLACE_TRAILING_DOUBLESTAR = /\uE001$/;
|
|
28560
28600
|
var replaceTrailingDoublestar = () => ".+(?=$|\\/$)";
|
|
28561
28601
|
var WILDCARD2 = "[^\\/]*";
|
|
28562
|
-
var separatorAfter = (
|
|
28602
|
+
var separatorAfter = (run2, at) => {
|
|
28563
28603
|
let separator = EMPTY;
|
|
28564
|
-
for (let index = at + 1; index <
|
|
28565
|
-
separator +=
|
|
28604
|
+
for (let index = at + 1; index < run2.length && !run2[index].wildcard; index++) {
|
|
28605
|
+
separator += run2[index].single;
|
|
28566
28606
|
}
|
|
28567
28607
|
return separator;
|
|
28568
28608
|
};
|
|
@@ -28622,26 +28662,26 @@ var require_ignore = __commonJS2({
|
|
|
28622
28662
|
}
|
|
28623
28663
|
}
|
|
28624
28664
|
let out = EMPTY;
|
|
28625
|
-
let
|
|
28665
|
+
let run2 = [];
|
|
28626
28666
|
const flush = () => {
|
|
28627
28667
|
let lastWildcard;
|
|
28628
|
-
|
|
28668
|
+
run2.forEach((token, at) => {
|
|
28629
28669
|
if (token.wildcard) {
|
|
28630
28670
|
lastWildcard = at;
|
|
28631
28671
|
}
|
|
28632
28672
|
});
|
|
28633
|
-
|
|
28673
|
+
run2.forEach((token, at) => {
|
|
28634
28674
|
if (!token.wildcard) {
|
|
28635
28675
|
out += token.single;
|
|
28636
28676
|
return;
|
|
28637
28677
|
}
|
|
28638
|
-
out += at === lastWildcard ? WILDCARD2 : `(?:(?!${separatorAfter(
|
|
28678
|
+
out += at === lastWildcard ? WILDCARD2 : `(?:(?!${separatorAfter(run2, at)})[^\\/])*`;
|
|
28639
28679
|
});
|
|
28640
|
-
|
|
28680
|
+
run2 = [];
|
|
28641
28681
|
};
|
|
28642
28682
|
tokens.forEach((token) => {
|
|
28643
28683
|
if (token.boundary === void 0) {
|
|
28644
|
-
|
|
28684
|
+
run2.push(token);
|
|
28645
28685
|
return;
|
|
28646
28686
|
}
|
|
28647
28687
|
flush();
|
|
@@ -28921,11 +28961,11 @@ var require_ignore = __commonJS2({
|
|
|
28921
28961
|
return true;
|
|
28922
28962
|
};
|
|
28923
28963
|
var isNotRelative = (path) => {
|
|
28924
|
-
const
|
|
28925
|
-
if (
|
|
28964
|
+
const first2 = path.charCodeAt(0);
|
|
28965
|
+
if (first2 === SLASH_CODE) {
|
|
28926
28966
|
return true;
|
|
28927
28967
|
}
|
|
28928
|
-
if (
|
|
28968
|
+
if (first2 !== DOT_CODE) {
|
|
28929
28969
|
return false;
|
|
28930
28970
|
}
|
|
28931
28971
|
if (path.length === 1) {
|
|
@@ -32834,10 +32874,10 @@ var $ZodUnion2 = /* @__PURE__ */ $constructor2("$ZodUnion", (inst, def) => {
|
|
|
32834
32874
|
}
|
|
32835
32875
|
return void 0;
|
|
32836
32876
|
});
|
|
32837
|
-
const
|
|
32877
|
+
const first2 = def.options.length === 1 ? def.options[0]._zod.run : null;
|
|
32838
32878
|
inst._zod.parse = (payload, ctx) => {
|
|
32839
|
-
if (
|
|
32840
|
-
return
|
|
32879
|
+
if (first2) {
|
|
32880
|
+
return first2(payload, ctx);
|
|
32841
32881
|
}
|
|
32842
32882
|
let async = false;
|
|
32843
32883
|
const results = [];
|
|
@@ -32894,10 +32934,10 @@ function handleExclusiveUnionResults(results, final, inst, ctx) {
|
|
|
32894
32934
|
var $ZodXor2 = /* @__PURE__ */ $constructor2("$ZodXor", (inst, def) => {
|
|
32895
32935
|
$ZodUnion2.init(inst, def);
|
|
32896
32936
|
def.inclusive = false;
|
|
32897
|
-
const
|
|
32937
|
+
const first2 = def.options.length === 1 ? def.options[0]._zod.run : null;
|
|
32898
32938
|
inst._zod.parse = (payload, ctx) => {
|
|
32899
|
-
if (
|
|
32900
|
-
return
|
|
32939
|
+
if (first2) {
|
|
32940
|
+
return first2(payload, ctx);
|
|
32901
32941
|
}
|
|
32902
32942
|
let async = false;
|
|
32903
32943
|
const results = [];
|
|
@@ -34276,7 +34316,7 @@ function bucketFor2(state, inst) {
|
|
|
34276
34316
|
return bucket;
|
|
34277
34317
|
}
|
|
34278
34318
|
var handoff2;
|
|
34279
|
-
var
|
|
34319
|
+
var open3 = [];
|
|
34280
34320
|
var memo3 = {
|
|
34281
34321
|
alloc(_inst, payload, empty) {
|
|
34282
34322
|
const bucket = handoff2;
|
|
@@ -34285,7 +34325,7 @@ var memo3 = {
|
|
|
34285
34325
|
handoff2 = void 0;
|
|
34286
34326
|
const entry = { value: empty, issues: null };
|
|
34287
34327
|
bucket.set(payload.value, entry);
|
|
34288
|
-
|
|
34328
|
+
open3.push(entry);
|
|
34289
34329
|
return empty;
|
|
34290
34330
|
},
|
|
34291
34331
|
guard(inst) {
|
|
@@ -34356,10 +34396,10 @@ var memo3 = {
|
|
|
34356
34396
|
return payload;
|
|
34357
34397
|
}
|
|
34358
34398
|
handoff2 = bucket;
|
|
34359
|
-
const depth =
|
|
34399
|
+
const depth = open3.length;
|
|
34360
34400
|
const result = base(payload, ctx);
|
|
34361
34401
|
handoff2 = void 0;
|
|
34362
|
-
const entry =
|
|
34402
|
+
const entry = open3.length > depth ? open3.pop() : void 0;
|
|
34363
34403
|
if (result instanceof Promise) {
|
|
34364
34404
|
return result.then((r) => {
|
|
34365
34405
|
if (entry)
|
|
@@ -38340,8 +38380,8 @@ function ko_default() {
|
|
|
38340
38380
|
localeError: error32()
|
|
38341
38381
|
};
|
|
38342
38382
|
}
|
|
38343
|
-
var capitalizeFirstCharacter = (
|
|
38344
|
-
return
|
|
38383
|
+
var capitalizeFirstCharacter = (text22) => {
|
|
38384
|
+
return text22.charAt(0).toUpperCase() + text22.slice(1);
|
|
38345
38385
|
};
|
|
38346
38386
|
function getUnitTypeFromNumber(number42) {
|
|
38347
38387
|
const abs = Math.abs(number42);
|
|
@@ -45232,9 +45272,9 @@ var numberProcessor2 = (schema, ctx, _json, params) => {
|
|
|
45232
45272
|
else
|
|
45233
45273
|
handleUnrepresentable2(schema, ctx, json32, params, `A multipleOf divisor of ${divisor} cannot be represented in JSON Schema`);
|
|
45234
45274
|
}
|
|
45235
|
-
const [
|
|
45236
|
-
if (
|
|
45237
|
-
json32.multipleOf =
|
|
45275
|
+
const [first2, ...rest] = divisors;
|
|
45276
|
+
if (first2 !== void 0)
|
|
45277
|
+
json32.multipleOf = first2;
|
|
45238
45278
|
if (rest.length)
|
|
45239
45279
|
json32.allOf = [...json32.allOf ?? [], ...rest.map((m) => ({ multipleOf: m }))];
|
|
45240
45280
|
}
|
|
@@ -47918,14 +47958,14 @@ function checkArrayGuards(arraySchema, guards) {
|
|
|
47918
47958
|
const key = canonicalKey(items[i], /* @__PURE__ */ new Set());
|
|
47919
47959
|
if (key === null)
|
|
47920
47960
|
continue;
|
|
47921
|
-
const
|
|
47922
|
-
if (
|
|
47961
|
+
const first2 = firstSeen.get(key);
|
|
47962
|
+
if (first2 === void 0) {
|
|
47923
47963
|
firstSeen.set(key, i);
|
|
47924
47964
|
continue;
|
|
47925
47965
|
}
|
|
47926
47966
|
payload.issues.push({
|
|
47927
47967
|
code: "custom",
|
|
47928
|
-
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}`,
|
|
47929
47969
|
input: items,
|
|
47930
47970
|
path: [i],
|
|
47931
47971
|
continue: true
|
|
@@ -48403,7 +48443,7 @@ function visit(schema, fnOrHandlers) {
|
|
|
48403
48443
|
return h ? h(node22, rewritten) : node22;
|
|
48404
48444
|
};
|
|
48405
48445
|
const cache = /* @__PURE__ */ new Map();
|
|
48406
|
-
function
|
|
48446
|
+
function run2(s) {
|
|
48407
48447
|
const cached22 = cache.get(s);
|
|
48408
48448
|
if (cached22 === RESOLVING) {
|
|
48409
48449
|
return new $ZodLazy2({
|
|
@@ -48426,69 +48466,69 @@ function visit(schema, fnOrHandlers) {
|
|
|
48426
48466
|
case "object": {
|
|
48427
48467
|
const oldShape = def.shape;
|
|
48428
48468
|
const keys = Object.keys(oldShape);
|
|
48429
|
-
let
|
|
48469
|
+
let changed2 = false;
|
|
48430
48470
|
const newShape = {};
|
|
48431
48471
|
for (const k of keys) {
|
|
48432
|
-
const mapped =
|
|
48472
|
+
const mapped = run2(oldShape[k]);
|
|
48433
48473
|
if (mapped !== oldShape[k])
|
|
48434
|
-
|
|
48474
|
+
changed2 = true;
|
|
48435
48475
|
newShape[k] = mapped;
|
|
48436
48476
|
}
|
|
48437
48477
|
let newCatchall = def.catchall;
|
|
48438
48478
|
if (def.catchall) {
|
|
48439
|
-
newCatchall =
|
|
48479
|
+
newCatchall = run2(def.catchall);
|
|
48440
48480
|
if (newCatchall !== def.catchall)
|
|
48441
|
-
|
|
48481
|
+
changed2 = true;
|
|
48442
48482
|
}
|
|
48443
|
-
return
|
|
48483
|
+
return changed2 ? clone2(s, { ...def, shape: newShape, catchall: newCatchall }) : s;
|
|
48444
48484
|
}
|
|
48445
48485
|
case "array": {
|
|
48446
|
-
const mapped =
|
|
48486
|
+
const mapped = run2(def.element);
|
|
48447
48487
|
return mapped === def.element ? s : clone2(s, { ...def, element: mapped });
|
|
48448
48488
|
}
|
|
48449
48489
|
case "tuple": {
|
|
48450
48490
|
const oldItems = def.items;
|
|
48451
|
-
let
|
|
48491
|
+
let changed2 = false;
|
|
48452
48492
|
const newItems = [];
|
|
48453
48493
|
for (const item of oldItems) {
|
|
48454
|
-
const mapped =
|
|
48494
|
+
const mapped = run2(item);
|
|
48455
48495
|
if (mapped !== item)
|
|
48456
|
-
|
|
48496
|
+
changed2 = true;
|
|
48457
48497
|
newItems.push(mapped);
|
|
48458
48498
|
}
|
|
48459
48499
|
let newRest = def.rest;
|
|
48460
48500
|
if (def.rest) {
|
|
48461
|
-
newRest =
|
|
48501
|
+
newRest = run2(def.rest);
|
|
48462
48502
|
if (newRest !== def.rest)
|
|
48463
|
-
|
|
48503
|
+
changed2 = true;
|
|
48464
48504
|
}
|
|
48465
|
-
return
|
|
48505
|
+
return changed2 ? clone2(s, { ...def, items: newItems, rest: newRest }) : s;
|
|
48466
48506
|
}
|
|
48467
48507
|
case "record":
|
|
48468
48508
|
case "map": {
|
|
48469
|
-
const newKey =
|
|
48470
|
-
const newVal =
|
|
48509
|
+
const newKey = run2(def.keyType);
|
|
48510
|
+
const newVal = run2(def.valueType);
|
|
48471
48511
|
return newKey === def.keyType && newVal === def.valueType ? s : clone2(s, { ...def, keyType: newKey, valueType: newVal });
|
|
48472
48512
|
}
|
|
48473
48513
|
case "set": {
|
|
48474
|
-
const newVal =
|
|
48514
|
+
const newVal = run2(def.valueType);
|
|
48475
48515
|
return newVal === def.valueType ? s : clone2(s, { ...def, valueType: newVal });
|
|
48476
48516
|
}
|
|
48477
48517
|
case "union": {
|
|
48478
48518
|
const oldOptions = def.options;
|
|
48479
|
-
let
|
|
48519
|
+
let changed2 = false;
|
|
48480
48520
|
const newOptions = [];
|
|
48481
48521
|
for (const opt of oldOptions) {
|
|
48482
|
-
const mapped =
|
|
48522
|
+
const mapped = run2(opt);
|
|
48483
48523
|
if (mapped !== opt)
|
|
48484
|
-
|
|
48524
|
+
changed2 = true;
|
|
48485
48525
|
newOptions.push(mapped);
|
|
48486
48526
|
}
|
|
48487
|
-
return
|
|
48527
|
+
return changed2 ? clone2(s, { ...def, options: newOptions }) : s;
|
|
48488
48528
|
}
|
|
48489
48529
|
case "intersection": {
|
|
48490
|
-
const newLeft =
|
|
48491
|
-
const newRight =
|
|
48530
|
+
const newLeft = run2(def.left);
|
|
48531
|
+
const newRight = run2(def.right);
|
|
48492
48532
|
return newLeft === def.left && newRight === def.right ? s : clone2(s, { ...def, left: newLeft, right: newRight });
|
|
48493
48533
|
}
|
|
48494
48534
|
case "optional":
|
|
@@ -48500,23 +48540,23 @@ function visit(schema, fnOrHandlers) {
|
|
|
48500
48540
|
case "nonoptional":
|
|
48501
48541
|
case "promise":
|
|
48502
48542
|
case "success": {
|
|
48503
|
-
const newInner =
|
|
48543
|
+
const newInner = run2(def.innerType);
|
|
48504
48544
|
return newInner === def.innerType ? s : clone2(s, { ...def, innerType: newInner });
|
|
48505
48545
|
}
|
|
48506
48546
|
case "pipe": {
|
|
48507
|
-
const newIn =
|
|
48508
|
-
const newOut =
|
|
48547
|
+
const newIn = run2(def.in);
|
|
48548
|
+
const newOut = run2(def.out);
|
|
48509
48549
|
return newIn === def.in && newOut === def.out ? s : clone2(s, { ...def, in: newIn, out: newOut });
|
|
48510
48550
|
}
|
|
48511
48551
|
case "function": {
|
|
48512
|
-
const newInput =
|
|
48513
|
-
const newOutput =
|
|
48552
|
+
const newInput = run2(def.input);
|
|
48553
|
+
const newOutput = run2(def.output);
|
|
48514
48554
|
return newInput === def.input && newOutput === def.output ? s : clone2(s, { ...def, input: newInput, output: newOutput });
|
|
48515
48555
|
}
|
|
48516
48556
|
case "lazy": {
|
|
48517
48557
|
const original = def.getter;
|
|
48518
48558
|
const { _cachedInner, ...rest } = def;
|
|
48519
|
-
return clone2(s, { ...rest, getter: () =>
|
|
48559
|
+
return clone2(s, { ...rest, getter: () => run2(original()) });
|
|
48520
48560
|
}
|
|
48521
48561
|
// A leaf by choice: `parts` are regex fragments, not data positions.
|
|
48522
48562
|
case "template_literal":
|
|
@@ -48547,7 +48587,7 @@ function visit(schema, fnOrHandlers) {
|
|
|
48547
48587
|
}
|
|
48548
48588
|
}
|
|
48549
48589
|
}
|
|
48550
|
-
return
|
|
48590
|
+
return run2(schema);
|
|
48551
48591
|
}
|
|
48552
48592
|
function deepPartial(schema) {
|
|
48553
48593
|
return visit(schema, {
|
|
@@ -52975,7 +53015,7 @@ function linkedRoundAbort2(outer) {
|
|
|
52975
53015
|
if (outer?.aborted) controller.abort(outer.reason);
|
|
52976
53016
|
return {
|
|
52977
53017
|
signal: controller.signal,
|
|
52978
|
-
abort: (
|
|
53018
|
+
abort: (reason22) => controller.abort(reason22),
|
|
52979
53019
|
dispose: () => outer?.removeEventListener("abort", onOuterAbort)
|
|
52980
53020
|
};
|
|
52981
53021
|
}
|
|
@@ -53770,8 +53810,8 @@ var Protocol2 = class {
|
|
|
53770
53810
|
return;
|
|
53771
53811
|
}
|
|
53772
53812
|
if (options?.signal?.aborted) {
|
|
53773
|
-
const
|
|
53774
|
-
throw
|
|
53813
|
+
const reason22 = options.signal.reason;
|
|
53814
|
+
throw reason22 instanceof SdkError2 ? reason22 : new SdkError2(SdkErrorCode2.RequestTimeout, String(reason22));
|
|
53775
53815
|
}
|
|
53776
53816
|
const requestAbort = codec2.era === MODERN_WIRE_REVISION2 && this._transport.hasPerRequestStream === true ? new AbortController() : void 0;
|
|
53777
53817
|
const messageId = this._requestMessageId++;
|
|
@@ -53793,7 +53833,7 @@ var Protocol2 = class {
|
|
|
53793
53833
|
}
|
|
53794
53834
|
const outbound = this._envelopeOutbound(jsonrpcRequest);
|
|
53795
53835
|
let responseReceived = false;
|
|
53796
|
-
const cancel = (
|
|
53836
|
+
const cancel = (reason22) => {
|
|
53797
53837
|
if (responseReceived) return;
|
|
53798
53838
|
this._progressHandlers.delete(messageId);
|
|
53799
53839
|
if (requestAbort === void 0) {
|
|
@@ -53802,7 +53842,7 @@ var Protocol2 = class {
|
|
|
53802
53842
|
method: "notifications/cancelled",
|
|
53803
53843
|
params: {
|
|
53804
53844
|
requestId: messageId,
|
|
53805
|
-
reason: String(
|
|
53845
|
+
reason: String(reason22)
|
|
53806
53846
|
}
|
|
53807
53847
|
}), {
|
|
53808
53848
|
relatedRequestId,
|
|
@@ -53810,7 +53850,7 @@ var Protocol2 = class {
|
|
|
53810
53850
|
onresumptiontoken
|
|
53811
53851
|
}).catch((error62) => this._onerror(/* @__PURE__ */ new Error(`Failed to send cancellation: ${error62}`)));
|
|
53812
53852
|
} else requestAbort.abort();
|
|
53813
|
-
reject(
|
|
53853
|
+
reject(reason22 instanceof SdkError2 ? reason22 : new SdkError2(SdkErrorCode2.RequestTimeout, String(reason22)));
|
|
53814
53854
|
};
|
|
53815
53855
|
this._responseHandlers.set(messageId, (response) => {
|
|
53816
53856
|
if (options?.signal?.aborted) return;
|
|
@@ -56907,8 +56947,8 @@ var require_utils2 = /* @__PURE__ */ __commonJSMin2(((exports, module) => {
|
|
|
56907
56947
|
}
|
|
56908
56948
|
if (bestLength < 2) return hextets.join(":");
|
|
56909
56949
|
const head = hextets.slice(0, bestStart).join(":");
|
|
56910
|
-
const
|
|
56911
|
-
return head + "::" +
|
|
56950
|
+
const tail2 = hextets.slice(bestStart + bestLength).join(":");
|
|
56951
|
+
return head + "::" + tail2;
|
|
56912
56952
|
}
|
|
56913
56953
|
function normalizeIPv6Address(input2) {
|
|
56914
56954
|
const compression = input2.indexOf("::");
|
|
@@ -58131,7 +58171,7 @@ var require_core$32 = /* @__PURE__ */ __commonJSMin2(((exports) => {
|
|
|
58131
58171
|
}
|
|
58132
58172
|
errorsText(errors = this.errors, { separator = ", ", dataVar = "data" } = {}) {
|
|
58133
58173
|
if (!errors || errors.length === 0) return "No errors";
|
|
58134
|
-
return errors.map((e) => `${dataVar}${e.instancePath} ${e.message}`).reduce((
|
|
58174
|
+
return errors.map((e) => `${dataVar}${e.instancePath} ${e.message}`).reduce((text22, msg) => text22 + separator + msg);
|
|
58135
58175
|
}
|
|
58136
58176
|
$dataMetaSchema(metaSchema, keywordsJsonPointers) {
|
|
58137
58177
|
const rules = this.RULES.all;
|
|
@@ -63273,21 +63313,21 @@ async function readRequestBody(request, maxBytes = DEFAULT_MAX_REQUEST_BODY_SIZE
|
|
|
63273
63313
|
const reader = request.body.getReader();
|
|
63274
63314
|
const decoder = new TextDecoder();
|
|
63275
63315
|
let received = 0;
|
|
63276
|
-
let
|
|
63316
|
+
let text22 = "";
|
|
63277
63317
|
try {
|
|
63278
63318
|
for (; ; ) {
|
|
63279
63319
|
const { done, value } = await reader.read();
|
|
63280
63320
|
if (done) break;
|
|
63281
63321
|
received += value.byteLength;
|
|
63282
63322
|
if (received > maxBytes) return { tooLarge: true };
|
|
63283
|
-
|
|
63323
|
+
text22 += decoder.decode(value, { stream: true });
|
|
63284
63324
|
}
|
|
63285
63325
|
} finally {
|
|
63286
63326
|
reader.releaseLock();
|
|
63287
63327
|
}
|
|
63288
63328
|
return {
|
|
63289
63329
|
tooLarge: false,
|
|
63290
|
-
text:
|
|
63330
|
+
text: text22 + decoder.decode()
|
|
63291
63331
|
};
|
|
63292
63332
|
}
|
|
63293
63333
|
var WebStandardStreamableHTTPServerTransport = class {
|
|
@@ -63381,9 +63421,9 @@ var WebStandardStreamableHTTPServerTransport = class {
|
|
|
63381
63421
|
}
|
|
63382
63422
|
});
|
|
63383
63423
|
}
|
|
63384
|
-
async _checkScopeChallenge(
|
|
63424
|
+
async _checkScopeChallenge(messages2, authInfo) {
|
|
63385
63425
|
if (!this._scopeChallengeResolver) return;
|
|
63386
|
-
const challenge = await findScopeChallenge(
|
|
63426
|
+
const challenge = await findScopeChallenge(messages2.filter((message) => isJSONRPCRequest2(message)), authInfo, this._scopeChallengeResolver);
|
|
63387
63427
|
return challenge === void 0 ? void 0 : createScopeChallengeResponse(challenge, scopeChallengeResourceMetadataUrl(authInfo));
|
|
63388
63428
|
}
|
|
63389
63429
|
/**
|
|
@@ -63681,21 +63721,21 @@ data:
|
|
|
63681
63721
|
this.onerror?.(/* @__PURE__ */ new Error(`Invalid Request: Batch must not exceed ${MAX_BATCH_SIZE} messages`));
|
|
63682
63722
|
return this.createJsonErrorResponse(400, -32600, `Invalid Request: Batch must not exceed ${MAX_BATCH_SIZE} messages`);
|
|
63683
63723
|
}
|
|
63684
|
-
let
|
|
63724
|
+
let messages2;
|
|
63685
63725
|
try {
|
|
63686
|
-
|
|
63726
|
+
messages2 = Array.isArray(rawMessage) ? rawMessage.map((msg) => JSONRPCMessageSchema2.parse(msg)) : [JSONRPCMessageSchema2.parse(rawMessage)];
|
|
63687
63727
|
} catch (error62) {
|
|
63688
63728
|
this.onerror?.(error62);
|
|
63689
63729
|
return this.createJsonErrorResponse(400, -32700, "Parse error: Invalid JSON-RPC message");
|
|
63690
63730
|
}
|
|
63691
63731
|
if (this._closed) return this.createJsonErrorResponse(404, -32001, "Session not found");
|
|
63692
|
-
const isInitializationRequest =
|
|
63732
|
+
const isInitializationRequest = messages2.some((element) => isInitializeRequest2(element));
|
|
63693
63733
|
if (isInitializationRequest) {
|
|
63694
63734
|
if (this._initialized && this.sessionId !== void 0) {
|
|
63695
63735
|
this.onerror?.(/* @__PURE__ */ new Error("Invalid Request: Server already initialized"));
|
|
63696
63736
|
return this.createJsonErrorResponse(400, -32600, "Invalid Request: Server already initialized");
|
|
63697
63737
|
}
|
|
63698
|
-
if (
|
|
63738
|
+
if (messages2.length > 1) {
|
|
63699
63739
|
this.onerror?.(/* @__PURE__ */ new Error("Invalid Request: Only one initialization request is allowed"));
|
|
63700
63740
|
return this.createJsonErrorResponse(400, -32600, "Invalid Request: Only one initialization request is allowed");
|
|
63701
63741
|
}
|
|
@@ -63712,21 +63752,21 @@ data:
|
|
|
63712
63752
|
if (this._closed) return this.createJsonErrorResponse(404, -32001, "Session not found");
|
|
63713
63753
|
let scopeChallengeResponse;
|
|
63714
63754
|
try {
|
|
63715
|
-
scopeChallengeResponse = await this._checkScopeChallenge(
|
|
63755
|
+
scopeChallengeResponse = await this._checkScopeChallenge(messages2, options?.authInfo);
|
|
63716
63756
|
} catch (error62) {
|
|
63717
63757
|
this.onerror?.(error62);
|
|
63718
63758
|
return this.createJsonErrorResponse(500, -32603, "Internal server error");
|
|
63719
63759
|
}
|
|
63720
63760
|
if (scopeChallengeResponse) return scopeChallengeResponse;
|
|
63721
|
-
if (!
|
|
63722
|
-
for (const message of
|
|
63761
|
+
if (!messages2.some((element) => isJSONRPCRequest2(element))) {
|
|
63762
|
+
for (const message of messages2) this.onmessage?.(message, {
|
|
63723
63763
|
authInfo: options?.authInfo,
|
|
63724
63764
|
request
|
|
63725
63765
|
});
|
|
63726
63766
|
return new Response(null, { status: 202 });
|
|
63727
63767
|
}
|
|
63728
63768
|
const streamId = crypto.randomUUID();
|
|
63729
|
-
const initRequest =
|
|
63769
|
+
const initRequest = messages2.find((m) => isInitializeRequest2(m));
|
|
63730
63770
|
const clientProtocolVersion = initRequest ? initRequest.params.protocolVersion : req.headers.get("mcp-protocol-version") ?? DEFAULT_NEGOTIATED_PROTOCOL_VERSION2;
|
|
63731
63771
|
if (this._enableJsonResponse) return new Promise((resolve42) => {
|
|
63732
63772
|
this._streamMapping.set(streamId, {
|
|
@@ -63735,8 +63775,8 @@ data:
|
|
|
63735
63775
|
this._streamMapping.delete(streamId);
|
|
63736
63776
|
}
|
|
63737
63777
|
});
|
|
63738
|
-
for (const message of
|
|
63739
|
-
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, {
|
|
63740
63780
|
authInfo: options?.authInfo,
|
|
63741
63781
|
request
|
|
63742
63782
|
});
|
|
@@ -63760,7 +63800,7 @@ data:
|
|
|
63760
63800
|
"X-Accel-Buffering": "no"
|
|
63761
63801
|
};
|
|
63762
63802
|
if (this.sessionId !== void 0) headers["mcp-session-id"] = this.sessionId;
|
|
63763
|
-
for (const message of
|
|
63803
|
+
for (const message of messages2) if (isJSONRPCRequest2(message)) {
|
|
63764
63804
|
this._streamMapping.set(streamId, {
|
|
63765
63805
|
controller: streamController,
|
|
63766
63806
|
encoder,
|
|
@@ -63776,7 +63816,7 @@ data:
|
|
|
63776
63816
|
this._requestToStreamMapping.set(message.id, streamId);
|
|
63777
63817
|
}
|
|
63778
63818
|
await this.writePrimingEvent(streamController, encoder, streamId, clientProtocolVersion);
|
|
63779
|
-
for (const message of
|
|
63819
|
+
for (const message of messages2) {
|
|
63780
63820
|
let closeSSEStream;
|
|
63781
63821
|
let closeStandaloneSSEStream;
|
|
63782
63822
|
if (isJSONRPCRequest2(message) && this._eventStore && this.supportsEmptySSEData(clientProtocolVersion)) {
|
|
@@ -64356,8 +64396,8 @@ var readRawBodyIfAvailable = (request) => {
|
|
|
64356
64396
|
};
|
|
64357
64397
|
var normalizeAbortError = (request, incoming) => {
|
|
64358
64398
|
if (incoming.errored) return incoming.errored;
|
|
64359
|
-
const
|
|
64360
|
-
if (
|
|
64399
|
+
const reason22 = request[abortReasonKey];
|
|
64400
|
+
if (reason22 !== void 0) return reason22 instanceof Error ? reason22 : new Error(String(reason22));
|
|
64361
64401
|
return /* @__PURE__ */ new Error("Client connection prematurely closed.");
|
|
64362
64402
|
};
|
|
64363
64403
|
var readBodyDirect = (request) => {
|
|
@@ -64451,10 +64491,10 @@ var requestPrototype = {
|
|
|
64451
64491
|
get headers() {
|
|
64452
64492
|
return this[headersKey] ||= newHeadersFromIncoming(this[incomingKey]);
|
|
64453
64493
|
},
|
|
64454
|
-
[abortRequest](
|
|
64455
|
-
if (this[abortReasonKey] === void 0) this[abortReasonKey] =
|
|
64494
|
+
[abortRequest](reason22) {
|
|
64495
|
+
if (this[abortReasonKey] === void 0) this[abortReasonKey] = reason22;
|
|
64456
64496
|
const abortController = this[abortControllerKey];
|
|
64457
|
-
if (abortController && !abortController.signal.aborted) abortController.abort(
|
|
64497
|
+
if (abortController && !abortController.signal.aborted) abortController.abort(reason22);
|
|
64458
64498
|
},
|
|
64459
64499
|
[getAbortController]() {
|
|
64460
64500
|
this[abortControllerKey] ||= new AbortController();
|
|
@@ -65098,8 +65138,8 @@ var upgradeWebSocket = defineWebSocketHelper(async (c, events, options) => {
|
|
|
65098
65138
|
ws.on("message", bufferMessage);
|
|
65099
65139
|
const ctx = {
|
|
65100
65140
|
binaryType: "arraybuffer",
|
|
65101
|
-
close(code,
|
|
65102
|
-
ws.close(code,
|
|
65141
|
+
close(code, reason22) {
|
|
65142
|
+
ws.close(code, reason22);
|
|
65103
65143
|
},
|
|
65104
65144
|
protocol: ws.protocol,
|
|
65105
65145
|
raw: ws,
|
|
@@ -65129,11 +65169,11 @@ var upgradeWebSocket = defineWebSocketHelper(async (c, events, options) => {
|
|
|
65129
65169
|
ws.on("message", (data, isBinary) => {
|
|
65130
65170
|
handleMessage(data, isBinary);
|
|
65131
65171
|
});
|
|
65132
|
-
ws.on("close", (code,
|
|
65172
|
+
ws.on("close", (code, reason22) => {
|
|
65133
65173
|
try {
|
|
65134
65174
|
events?.onClose?.(new CloseEvent("close", {
|
|
65135
65175
|
code,
|
|
65136
|
-
reason:
|
|
65176
|
+
reason: reason22.toString()
|
|
65137
65177
|
}), ctx);
|
|
65138
65178
|
} catch (e) {
|
|
65139
65179
|
(options?.onError ?? console.error)(e);
|
|
@@ -65757,9 +65797,9 @@ function createTwoFilesPatch(oldFileName, newFileName, oldStr, newStr, oldHeader
|
|
|
65757
65797
|
} }));
|
|
65758
65798
|
}
|
|
65759
65799
|
}
|
|
65760
|
-
function splitLines(
|
|
65761
|
-
const hasTrailingNl =
|
|
65762
|
-
const result =
|
|
65800
|
+
function splitLines(text22) {
|
|
65801
|
+
const hasTrailingNl = text22.endsWith("\n");
|
|
65802
|
+
const result = text22.split("\n").map((line) => line + "\n");
|
|
65763
65803
|
if (hasTrailingNl) {
|
|
65764
65804
|
result.pop();
|
|
65765
65805
|
} else {
|
|
@@ -65769,26 +65809,26 @@ function splitLines(text2) {
|
|
|
65769
65809
|
}
|
|
65770
65810
|
var CHARS_PER_TOKEN = 4;
|
|
65771
65811
|
var HELD = 1024 * 1024;
|
|
65772
|
-
function cut(
|
|
65773
|
-
if (
|
|
65812
|
+
function cut(text22, max) {
|
|
65813
|
+
if (text22.length <= max) return text22;
|
|
65774
65814
|
const half = Math.max(1, Math.floor(max / 2));
|
|
65775
|
-
const head =
|
|
65776
|
-
const
|
|
65815
|
+
const head = text22.slice(0, half).replace(/[\uD800-\uDBFF]$/, "");
|
|
65816
|
+
const tail2 = text22.slice(-half).replace(/^[\uDC00-\uDFFF]/, "");
|
|
65777
65817
|
return `${head}
|
|
65778
|
-
\u2026 ${
|
|
65779
|
-
${
|
|
65818
|
+
\u2026 ${text22.length - head.length - tail2.length} characters cut \u2026
|
|
65819
|
+
${tail2}`;
|
|
65780
65820
|
}
|
|
65781
65821
|
var Pending = class {
|
|
65782
65822
|
#head = "";
|
|
65783
65823
|
#tail = "";
|
|
65784
65824
|
#dropped = 0;
|
|
65785
|
-
add(
|
|
65825
|
+
add(text22) {
|
|
65786
65826
|
const room = HELD / 2 - this.#head.length;
|
|
65787
65827
|
if (room > 0) {
|
|
65788
|
-
this.#head +=
|
|
65789
|
-
|
|
65828
|
+
this.#head += text22.slice(0, room);
|
|
65829
|
+
text22 = text22.slice(room);
|
|
65790
65830
|
}
|
|
65791
|
-
this.#tail +=
|
|
65831
|
+
this.#tail += text22;
|
|
65792
65832
|
const over = this.#tail.length - HELD / 2;
|
|
65793
65833
|
if (over > 0) {
|
|
65794
65834
|
this.#tail = this.#tail.slice(over);
|
|
@@ -65797,13 +65837,13 @@ var Pending = class {
|
|
|
65797
65837
|
}
|
|
65798
65838
|
/** What is pending, leaving nothing. */
|
|
65799
65839
|
take() {
|
|
65800
|
-
const
|
|
65840
|
+
const text22 = this.#dropped ? `${this.#head}
|
|
65801
65841
|
\u2026 ${this.#dropped} characters cut \u2026
|
|
65802
65842
|
${this.#tail}` : this.#head + this.#tail;
|
|
65803
65843
|
this.#head = "";
|
|
65804
65844
|
this.#tail = "";
|
|
65805
65845
|
this.#dropped = 0;
|
|
65806
|
-
return
|
|
65846
|
+
return text22;
|
|
65807
65847
|
}
|
|
65808
65848
|
};
|
|
65809
65849
|
var MIN_YIELD_MS = 250;
|
|
@@ -65812,6 +65852,10 @@ var DEFAULT_YIELD_MS = 1e4;
|
|
|
65812
65852
|
var MIN_POLL_MS = 5e3;
|
|
65813
65853
|
var WRITE_YIELD_MS = 250;
|
|
65814
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;
|
|
65815
65859
|
var DEFAULT_MAX_TOKENS = 7e3;
|
|
65816
65860
|
var DRAIN_MS = 200;
|
|
65817
65861
|
var KILL_AFTER_MS = 1e3;
|
|
@@ -65825,48 +65869,183 @@ var SIGNALS = {
|
|
|
65825
65869
|
SIGTERM: 15
|
|
65826
65870
|
};
|
|
65827
65871
|
var clamp = (value, low, high) => Math.min(Math.max(value, low), high);
|
|
65872
|
+
var tail = (text22) => text22.slice(-KEPT).replace(/^[\uDC00-\uDFFF]/, "");
|
|
65828
65873
|
var Sessions = class {
|
|
65829
65874
|
#sessions = /* @__PURE__ */ new Map();
|
|
65875
|
+
/** Those that have ended and are still listed, in the order they ended. */
|
|
65876
|
+
#ended = [];
|
|
65830
65877
|
#root;
|
|
65831
65878
|
#emit;
|
|
65879
|
+
#pty;
|
|
65832
65880
|
constructor(root, emit = () => {
|
|
65833
|
-
}) {
|
|
65881
|
+
}, pty2) {
|
|
65834
65882
|
this.#root = root;
|
|
65835
65883
|
this.#emit = emit;
|
|
65884
|
+
this.#pty = pty2;
|
|
65836
65885
|
}
|
|
65837
65886
|
/** Run `cmd` in a login shell, in `workdir` or the workspace. */
|
|
65838
65887
|
async exec(input2) {
|
|
65839
|
-
|
|
65840
|
-
if (running.length >= MAX_RUNNING)
|
|
65841
|
-
throw new ExecRefused(`${MAX_RUNNING} commands are running. Wait for one, or stop one.`);
|
|
65888
|
+
this.#room();
|
|
65842
65889
|
const started = Date.now();
|
|
65843
65890
|
const session = this.#start(input2.cmd, input2.workdir ?? this.#root);
|
|
65844
65891
|
const wait = clamp(input2.yield_time_ms ?? DEFAULT_YIELD_MS, MIN_YIELD_MS, MAX_YIELD_MS);
|
|
65845
65892
|
return this.#after(session, wait, started, input2.max_output_tokens);
|
|
65846
65893
|
}
|
|
65847
65894
|
/**
|
|
65848
|
-
* Type into a running
|
|
65849
|
-
* (`\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.
|
|
65850
65898
|
*/
|
|
65851
65899
|
async write(input2) {
|
|
65852
65900
|
const session = this.#sessions.get(input2.session_id);
|
|
65853
|
-
if (!session
|
|
65901
|
+
if (!session || session.reported)
|
|
65902
|
+
throw new ExecRefused(`No session ${input2.session_id.slice(0, 64)} is running.`);
|
|
65854
65903
|
const started = Date.now();
|
|
65855
65904
|
const chars = input2.chars ?? "";
|
|
65856
|
-
if (session.exitCode === void 0)
|
|
65905
|
+
if (session.exitCode === void 0) session.type(chars);
|
|
65857
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);
|
|
65858
65907
|
return this.#after(session, wait, started, input2.max_output_tokens);
|
|
65859
65908
|
}
|
|
65860
|
-
/**
|
|
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. */
|
|
65861
65976
|
async stopAll() {
|
|
65862
|
-
|
|
65863
|
-
|
|
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;
|
|
65864
65983
|
await Promise.race([
|
|
65865
65984
|
Promise.all(running.map((s) => s.ended)),
|
|
65866
|
-
new Promise((done) =>
|
|
65985
|
+
new Promise((done) => {
|
|
65986
|
+
timer = setTimeout(done, KILL_AFTER_MS);
|
|
65987
|
+
})
|
|
65867
65988
|
]);
|
|
65868
|
-
|
|
65869
|
-
|
|
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 };
|
|
65870
66049
|
}
|
|
65871
66050
|
#start(cmd, cwd) {
|
|
65872
66051
|
const child = spawn2(process.env.SHELL || "/bin/bash", ["-lc", cmd], {
|
|
@@ -65875,62 +66054,44 @@ var Sessions = class {
|
|
|
65875
66054
|
// Its own process group, so a signal reaches what it started.
|
|
65876
66055
|
detached: true
|
|
65877
66056
|
});
|
|
65878
|
-
const
|
|
65879
|
-
|
|
65880
|
-
|
|
65881
|
-
|
|
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 });
|
|
65882
66071
|
for (const [name, stream] of [
|
|
65883
66072
|
["stdout", child.stdout],
|
|
65884
66073
|
["stderr", child.stderr]
|
|
65885
66074
|
]) {
|
|
65886
66075
|
const decoder = new StringDecoder("utf8");
|
|
65887
|
-
stream.on("data", (chunk) =>
|
|
65888
|
-
const text2 = decoder.write(chunk);
|
|
65889
|
-
pending.add(text2);
|
|
65890
|
-
if (text2) this.#emit({ type: "output", id, stream: name, text: text2 });
|
|
65891
|
-
});
|
|
66076
|
+
stream.on("data", (chunk) => wrote(decoder.write(chunk), name));
|
|
65892
66077
|
}
|
|
65893
66078
|
child.stdin.on("error", () => {
|
|
65894
66079
|
});
|
|
65895
|
-
|
|
65896
|
-
|
|
65897
|
-
|
|
65898
|
-
|
|
65899
|
-
|
|
65900
|
-
|
|
65901
|
-
|
|
65902
|
-
|
|
65903
|
-
|
|
65904
|
-
|
|
65905
|
-
|
|
65906
|
-
` });
|
|
65907
|
-
finish(127);
|
|
65908
|
-
});
|
|
65909
|
-
child.once("exit", (code, signal) => {
|
|
65910
|
-
const exitCode = code ?? 128 + (signal ? SIGNALS[signal] ?? 1 : 1);
|
|
65911
|
-
const late = setTimeout(() => finish(exitCode), DRAIN_MS);
|
|
65912
|
-
child.once("close", () => {
|
|
65913
|
-
clearTimeout(late);
|
|
65914
|
-
finish(exitCode);
|
|
65915
|
-
});
|
|
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);
|
|
65916
66091
|
});
|
|
65917
66092
|
});
|
|
65918
|
-
this.#sessions.set(session.id, session);
|
|
65919
66093
|
return session;
|
|
65920
66094
|
}
|
|
65921
|
-
#type(session, chars) {
|
|
65922
|
-
for (const part of chars.split(new RegExp("(\\p{Cc})", "u"))) {
|
|
65923
|
-
if (part === "") this.#signal(session, "SIGINT");
|
|
65924
|
-
else if (part === "") session.child.stdin.end();
|
|
65925
|
-
else if (part && session.child.stdin.writable) session.child.stdin.write(part);
|
|
65926
|
-
}
|
|
65927
|
-
}
|
|
65928
|
-
#signal(session, signal) {
|
|
65929
|
-
try {
|
|
65930
|
-
if (session.child.pid) process.kill(-session.child.pid, signal);
|
|
65931
|
-
} catch {
|
|
65932
|
-
}
|
|
65933
|
-
}
|
|
65934
66095
|
async #after(session, waitMs, started, maxTokens) {
|
|
65935
66096
|
let timer;
|
|
65936
66097
|
await Promise.race([
|
|
@@ -65941,7 +66102,7 @@ var Sessions = class {
|
|
|
65941
66102
|
]);
|
|
65942
66103
|
clearTimeout(timer);
|
|
65943
66104
|
const exited = session.exitCode !== void 0;
|
|
65944
|
-
if (exited)
|
|
66105
|
+
if (exited) session.reported = true;
|
|
65945
66106
|
return {
|
|
65946
66107
|
exit_code: exited ? session.exitCode : null,
|
|
65947
66108
|
seconds: Math.round(Date.now() - started) / 1e3,
|
|
@@ -65950,6 +66111,12 @@ var Sessions = class {
|
|
|
65950
66111
|
};
|
|
65951
66112
|
}
|
|
65952
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
|
+
}
|
|
65953
66120
|
function formatAnswer(answer) {
|
|
65954
66121
|
return [
|
|
65955
66122
|
`exit_code: ${answer.exit_code ?? "running"}`,
|
|
@@ -65959,6 +66126,31 @@ function formatAnswer(answer) {
|
|
|
65959
66126
|
answer.text
|
|
65960
66127
|
].join("\n");
|
|
65961
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
|
+
}
|
|
65962
66154
|
var DEFAULT_LINES = 2e3;
|
|
65963
66155
|
var MAX_READ_CHARS = 28e3;
|
|
65964
66156
|
var MAX_LINE_CHARS = 2e3;
|
|
@@ -65980,6 +66172,7 @@ var refusing = async (work) => {
|
|
|
65980
66172
|
throw new ExecRefused(reason(error62));
|
|
65981
66173
|
}
|
|
65982
66174
|
};
|
|
66175
|
+
var foldersFirst = (a, b) => Number(b.isDirectory()) - Number(a.isDirectory()) || a.name.localeCompare(b.name);
|
|
65983
66176
|
async function inside(root, path) {
|
|
65984
66177
|
const refused2 = () => new ExecRefused(`${path} is outside the workspace.`);
|
|
65985
66178
|
const wanted = resolve(root, path);
|
|
@@ -65994,43 +66187,48 @@ async function inside(root, path) {
|
|
|
65994
66187
|
() => false
|
|
65995
66188
|
))
|
|
65996
66189
|
throw refused2();
|
|
65997
|
-
const above =
|
|
66190
|
+
const above = dirname(at);
|
|
65998
66191
|
if (above === at) throw refused2();
|
|
65999
|
-
return
|
|
66192
|
+
return join4(await real(above), basename(at));
|
|
66000
66193
|
}
|
|
66001
66194
|
};
|
|
66002
66195
|
const found = await real(wanted);
|
|
66003
66196
|
if (found !== root && !found.startsWith(root + sep2)) throw refused2();
|
|
66004
66197
|
return found;
|
|
66005
66198
|
}
|
|
66199
|
+
async function aFile(path, given) {
|
|
66200
|
+
const found = await stat(path);
|
|
66201
|
+
if (found.isDirectory()) throw new ExecRefused(`${given} is a folder.`);
|
|
66202
|
+
if (!found.isFile()) throw new ExecRefused(`${given} isn't a file that can be read.`);
|
|
66203
|
+
return found;
|
|
66204
|
+
}
|
|
66006
66205
|
function createFiles(root) {
|
|
66007
66206
|
const at = (path) => inside(root, path);
|
|
66008
|
-
const
|
|
66207
|
+
const text22 = async (path, given) => {
|
|
66208
|
+
await aFile(path, given);
|
|
66009
66209
|
const bytes = await readFile(path);
|
|
66010
|
-
if (bytes.includes(0)) throw new ExecRefused(`${
|
|
66210
|
+
if (bytes.includes(0)) throw new ExecRefused(`${given} is a binary file.`);
|
|
66011
66211
|
return bytes.toString("utf8");
|
|
66012
66212
|
};
|
|
66013
66213
|
return {
|
|
66014
66214
|
/** Numbered lines of a file. */
|
|
66015
66215
|
read: (input2) => refusing(async () => {
|
|
66016
|
-
const lines = (await
|
|
66017
|
-
const
|
|
66018
|
-
const shown = lines.slice(
|
|
66019
|
-
const last =
|
|
66020
|
-
return `lines ${
|
|
66216
|
+
const lines = (await text22(await at(input2.path), input2.path)).split("\n");
|
|
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}
|
|
66021
66221
|
${cut(shown.join("\n"), MAX_READ_CHARS)}`;
|
|
66022
66222
|
}),
|
|
66023
66223
|
/** A folder's entries, folders first, each folder ending in `/`. */
|
|
66024
66224
|
list: (input2) => refusing(async () => {
|
|
66025
66225
|
const folder = await at(input2.path ?? ".");
|
|
66026
66226
|
const entries = await readdir(folder, { withFileTypes: true });
|
|
66027
|
-
entries.sort(
|
|
66028
|
-
(a, b) => Number(b.isDirectory()) - Number(a.isDirectory()) || a.name.localeCompare(b.name)
|
|
66029
|
-
);
|
|
66227
|
+
entries.sort(foldersFirst);
|
|
66030
66228
|
const lines = await Promise.all(
|
|
66031
66229
|
entries.slice(0, MAX_ENTRIES).map(async (entry) => {
|
|
66032
66230
|
if (entry.isDirectory()) return `${entry.name}/`;
|
|
66033
|
-
const { size } = await lstat(
|
|
66231
|
+
const { size } = await lstat(join4(folder, entry.name));
|
|
66034
66232
|
return `${entry.name} ${size}`;
|
|
66035
66233
|
})
|
|
66036
66234
|
);
|
|
@@ -66050,7 +66248,7 @@ ${cut(shown.join("\n"), MAX_READ_CHARS)}`;
|
|
|
66050
66248
|
});
|
|
66051
66249
|
if (found && found.size > MAX_EDIT_BYTES)
|
|
66052
66250
|
throw new ExecRefused(`${input2.path} is too large to edit here.`);
|
|
66053
|
-
const before = found ? await
|
|
66251
|
+
const before = found ? await text22(path, input2.path) : "";
|
|
66054
66252
|
let after;
|
|
66055
66253
|
if (input2.old_text === "") {
|
|
66056
66254
|
if (before !== "")
|
|
@@ -66067,9 +66265,10 @@ ${cut(shown.join("\n"), MAX_READ_CHARS)}`;
|
|
|
66067
66265
|
after = before.split(input2.old_text).join(input2.new_text);
|
|
66068
66266
|
}
|
|
66069
66267
|
if (after === before) throw new ExecRefused("That edit changes nothing.");
|
|
66070
|
-
const temporary =
|
|
66268
|
+
const temporary = join4(dirname(path), `.agentexec-${randomBytes2(6).toString("hex")}`);
|
|
66071
66269
|
try {
|
|
66072
66270
|
await writeFile(temporary, after, found ? { mode: found.mode } : {});
|
|
66271
|
+
if (found) await chmod(temporary, found.mode & 511);
|
|
66073
66272
|
await rename(temporary, path);
|
|
66074
66273
|
} catch (error62) {
|
|
66075
66274
|
await rm(temporary, { force: true });
|
|
@@ -66084,32 +66283,18 @@ var DEADLINE_MS = 2e4;
|
|
|
66084
66283
|
var MAX_FILE_BYTES = 2 * 1024 * 1024;
|
|
66085
66284
|
var MAX_LINE_CHARS2 = 500;
|
|
66086
66285
|
var DEFAULT_MATCHES = 200;
|
|
66286
|
+
var MAX_CHANGES = 1e3;
|
|
66087
66287
|
var SKIPPED = /* @__PURE__ */ new Set([".git", "node_modules"]);
|
|
66088
|
-
function
|
|
66089
|
-
const start = resolve2(job.root, job.path ?? ".");
|
|
66090
|
-
const pattern = job.regex ? job.pattern : job.pattern.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
66091
|
-
const matcher = new RegExp(pattern, job.ignore_case ? "i" : "");
|
|
66092
|
-
const limit = job.limit ?? DEFAULT_MATCHES;
|
|
66093
|
-
const out = [];
|
|
66094
|
-
const name = (path) => relative(job.root, path) || path;
|
|
66095
|
-
const scan = (file2) => {
|
|
66096
|
-
if (statSync(file2).size > MAX_FILE_BYTES) return;
|
|
66097
|
-
const bytes = readFileSync(file2);
|
|
66098
|
-
if (bytes.includes(0)) return;
|
|
66099
|
-
bytes.toString("utf8").split("\n").forEach((line, i) => {
|
|
66100
|
-
if (out.length <= limit && matcher.test(line))
|
|
66101
|
-
out.push(`${name(file2)}:${i + 1}:${line.slice(0, MAX_LINE_CHARS2)}`);
|
|
66102
|
-
});
|
|
66103
|
-
};
|
|
66288
|
+
function eachFile(start, visit2) {
|
|
66104
66289
|
const walk = (folder, rules) => {
|
|
66105
66290
|
let here = rules;
|
|
66106
66291
|
try {
|
|
66107
|
-
const own22 =
|
|
66108
|
-
|
|
66292
|
+
const own22 = join22(folder, ".gitignore");
|
|
66293
|
+
if (statSync(own22).isFile())
|
|
66294
|
+
here = [...rules, { base: folder, ignore: (0, import_ignore.default)().add(readFileSync(own22, "utf8")) }];
|
|
66109
66295
|
} catch {
|
|
66110
66296
|
}
|
|
66111
66297
|
for (const entry of readdirSync(folder, { withFileTypes: true })) {
|
|
66112
|
-
if (out.length > limit) return;
|
|
66113
66298
|
if (SKIPPED.has(entry.name) || entry.isSymbolicLink()) continue;
|
|
66114
66299
|
const path = join22(folder, entry.name);
|
|
66115
66300
|
const suffix = entry.isDirectory() ? "/" : "";
|
|
@@ -66118,30 +66303,78 @@ function run(job) {
|
|
|
66118
66303
|
);
|
|
66119
66304
|
if (ignored) continue;
|
|
66120
66305
|
try {
|
|
66121
|
-
|
|
66122
|
-
|
|
66306
|
+
const carryOn = entry.isDirectory() ? walk(path, here) : !entry.isFile() || visit2(path);
|
|
66307
|
+
if (!carryOn) return false;
|
|
66123
66308
|
} catch {
|
|
66124
66309
|
}
|
|
66125
66310
|
}
|
|
66311
|
+
return true;
|
|
66126
66312
|
};
|
|
66127
|
-
|
|
66313
|
+
walk(start, []);
|
|
66314
|
+
}
|
|
66315
|
+
function searched(job) {
|
|
66316
|
+
const start = resolve2(job.root, job.path ?? ".");
|
|
66317
|
+
const pattern = job.regex ? job.pattern : job.pattern.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
66318
|
+
const matcher = new RegExp(pattern, job.ignore_case ? "i" : "");
|
|
66319
|
+
const limit = job.limit ?? DEFAULT_MATCHES;
|
|
66320
|
+
const out = [];
|
|
66321
|
+
const name = (path) => relative(job.root, path) || path;
|
|
66322
|
+
const scan = (file2) => {
|
|
66323
|
+
const found = statSync(file2);
|
|
66324
|
+
if (!found.isFile() || found.size > MAX_FILE_BYTES) return;
|
|
66325
|
+
const bytes = readFileSync(file2);
|
|
66326
|
+
if (bytes.includes(0)) return;
|
|
66327
|
+
bytes.toString("utf8").split("\n").forEach((line, i) => {
|
|
66328
|
+
if (out.length <= limit && matcher.test(line))
|
|
66329
|
+
out.push(`${name(file2)}:${i + 1}:${line.slice(0, MAX_LINE_CHARS2)}`);
|
|
66330
|
+
});
|
|
66331
|
+
};
|
|
66332
|
+
if (statSync(start).isDirectory())
|
|
66333
|
+
eachFile(start, (file2) => {
|
|
66334
|
+
scan(file2);
|
|
66335
|
+
return out.length <= limit;
|
|
66336
|
+
});
|
|
66128
66337
|
else scan(start);
|
|
66129
66338
|
const more = out.length > limit;
|
|
66130
66339
|
return (more ? [...out.slice(0, limit), `\u2026 more than ${limit} matches`] : out).join("\n");
|
|
66131
66340
|
}
|
|
66132
|
-
|
|
66133
|
-
const
|
|
66341
|
+
function changed(job) {
|
|
66342
|
+
const changes = [];
|
|
66343
|
+
let more = false;
|
|
66344
|
+
const newest = () => {
|
|
66345
|
+
changes.sort(
|
|
66346
|
+
(a, b) => b.modifiedAt.localeCompare(a.modifiedAt) || a.path.localeCompare(b.path)
|
|
66347
|
+
);
|
|
66348
|
+
if (changes.length <= MAX_CHANGES) return;
|
|
66349
|
+
more = true;
|
|
66350
|
+
changes.length = MAX_CHANGES;
|
|
66351
|
+
};
|
|
66352
|
+
eachFile(job.root, (file2) => {
|
|
66353
|
+
const { mtime, size } = statSync(file2);
|
|
66354
|
+
if (mtime.getTime() > job.since) {
|
|
66355
|
+
changes.push({
|
|
66356
|
+
path: relative(job.root, file2).split(sep22).join("/"),
|
|
66357
|
+
size,
|
|
66358
|
+
modifiedAt: mtime.toISOString()
|
|
66359
|
+
});
|
|
66360
|
+
if (changes.length >= 2 * MAX_CHANGES) newest();
|
|
66361
|
+
}
|
|
66362
|
+
return true;
|
|
66363
|
+
});
|
|
66364
|
+
newest();
|
|
66365
|
+
return { changes, more };
|
|
66366
|
+
}
|
|
66367
|
+
function onThread(job, what) {
|
|
66134
66368
|
return new Promise((done, failed) => {
|
|
66135
|
-
const
|
|
66136
|
-
const worker = new Worker(fileURLToPath2(import.meta.url), { workerData: workerData2 });
|
|
66369
|
+
const worker = new Worker(fileURLToPath2(import.meta.url), { workerData: job });
|
|
66137
66370
|
const timer = setTimeout(() => {
|
|
66138
66371
|
void worker.terminate();
|
|
66139
|
-
failed(new ExecRefused(
|
|
66372
|
+
failed(new ExecRefused(`${what} took over ${DEADLINE_MS / 1e3} s, so it was stopped.`));
|
|
66140
66373
|
}, DEADLINE_MS);
|
|
66141
|
-
worker.once("message", (
|
|
66374
|
+
worker.once("message", (answer) => {
|
|
66142
66375
|
clearTimeout(timer);
|
|
66143
66376
|
void worker.terminate();
|
|
66144
|
-
done(
|
|
66377
|
+
done(answer);
|
|
66145
66378
|
});
|
|
66146
66379
|
worker.once("error", (error62) => {
|
|
66147
66380
|
clearTimeout(timer);
|
|
@@ -66149,8 +66382,136 @@ async function search(root, input2) {
|
|
|
66149
66382
|
});
|
|
66150
66383
|
});
|
|
66151
66384
|
}
|
|
66152
|
-
|
|
66153
|
-
|
|
66385
|
+
async function search(root, input2) {
|
|
66386
|
+
const path = await inside(root, input2.path ?? ".");
|
|
66387
|
+
return onThread({ ...input2, path, root, agentexecSearch: true }, "That search");
|
|
66388
|
+
}
|
|
66389
|
+
function changedSince(root, since) {
|
|
66390
|
+
return onThread({ root, since, agentexecSearch: true }, "Looking for what changed");
|
|
66391
|
+
}
|
|
66392
|
+
if (!isMainThread && workerData?.agentexecSearch) {
|
|
66393
|
+
const job = workerData;
|
|
66394
|
+
parentPort?.postMessage("since" in job ? changed(job) : searched(job));
|
|
66395
|
+
}
|
|
66396
|
+
var MAX_READ_BYTES = 1024 * 1024;
|
|
66397
|
+
function createHostFiles(root, level) {
|
|
66398
|
+
const uploads = /* @__PURE__ */ new Set();
|
|
66399
|
+
let closes = 0;
|
|
66400
|
+
const files = {
|
|
66401
|
+
list: (path = ".") => refusing(async () => {
|
|
66402
|
+
const folder = await inside(root, path);
|
|
66403
|
+
const found = await readdir2(folder, { withFileTypes: true });
|
|
66404
|
+
found.sort(foldersFirst);
|
|
66405
|
+
const entries = await Promise.all(
|
|
66406
|
+
found.slice(0, MAX_ENTRIES).map(async (entry) => {
|
|
66407
|
+
const own22 = await lstat2(join3(folder, entry.name)).catch(() => void 0);
|
|
66408
|
+
return own22 && {
|
|
66409
|
+
name: entry.name,
|
|
66410
|
+
kind: entry.isDirectory() ? "folder" : entry.isSymbolicLink() ? "link" : "file",
|
|
66411
|
+
size: own22.size,
|
|
66412
|
+
modifiedAt: own22.mtime.toISOString()
|
|
66413
|
+
};
|
|
66414
|
+
})
|
|
66415
|
+
);
|
|
66416
|
+
return {
|
|
66417
|
+
entries: entries.filter((entry) => entry !== void 0),
|
|
66418
|
+
more: found.length > MAX_ENTRIES
|
|
66419
|
+
};
|
|
66420
|
+
}),
|
|
66421
|
+
read: (path, offset, length) => refusing(async () => {
|
|
66422
|
+
if (!Number.isSafeInteger(offset) || offset < 0 || !Number.isSafeInteger(length) || length < 1 || length > MAX_READ_BYTES)
|
|
66423
|
+
throw new ExecRefused("A read starts at 0 or later and takes 1 to 1,048,576 bytes.");
|
|
66424
|
+
const real = await inside(root, path);
|
|
66425
|
+
const found = await aFile(real, path);
|
|
66426
|
+
const file2 = await open2(real, "r");
|
|
66427
|
+
try {
|
|
66428
|
+
const bytes = Buffer.alloc(Math.max(0, Math.min(length, found.size - offset)));
|
|
66429
|
+
const { bytesRead } = await file2.read(bytes, 0, bytes.length, offset);
|
|
66430
|
+
return {
|
|
66431
|
+
bytes: bytes.subarray(0, bytesRead),
|
|
66432
|
+
size: found.size,
|
|
66433
|
+
modifiedAt: found.mtime.toISOString()
|
|
66434
|
+
};
|
|
66435
|
+
} finally {
|
|
66436
|
+
await file2.close();
|
|
66437
|
+
}
|
|
66438
|
+
}),
|
|
66439
|
+
changed: (since) => refusing(async () => {
|
|
66440
|
+
const from = since.getTime();
|
|
66441
|
+
if (Number.isNaN(from)) throw new ExecRefused("That isn't a time.");
|
|
66442
|
+
return changedSince(root, from);
|
|
66443
|
+
}),
|
|
66444
|
+
upload: (path) => refusing(async () => {
|
|
66445
|
+
if (level === "read") throw new ExecRefused("Files here can be read, not written.");
|
|
66446
|
+
const opening = closes;
|
|
66447
|
+
const target = await inside(root, path);
|
|
66448
|
+
const before = await stat2(target).catch((error62) => {
|
|
66449
|
+
if (error62.code === "ENOENT") return void 0;
|
|
66450
|
+
throw error62;
|
|
66451
|
+
});
|
|
66452
|
+
if (before?.isDirectory()) throw new ExecRefused(`${path} is a folder.`);
|
|
66453
|
+
await stat2(dirname2(target));
|
|
66454
|
+
const temporary = join3(dirname2(target), `.agentexec-${randomBytes3(6).toString("hex")}`);
|
|
66455
|
+
const file2 = await open2(temporary, "wx");
|
|
66456
|
+
const discard = async () => {
|
|
66457
|
+
await file2.close().catch(() => {
|
|
66458
|
+
});
|
|
66459
|
+
await rm2(temporary, { force: true });
|
|
66460
|
+
};
|
|
66461
|
+
let ended = false;
|
|
66462
|
+
let written = Promise.resolve();
|
|
66463
|
+
const end2 = () => {
|
|
66464
|
+
if (ended) throw new ExecRefused("That upload has ended.");
|
|
66465
|
+
ended = true;
|
|
66466
|
+
uploads.delete(upload);
|
|
66467
|
+
};
|
|
66468
|
+
const upload = {
|
|
66469
|
+
write: (bytes) => refusing(async () => {
|
|
66470
|
+
if (ended) throw new ExecRefused("That upload has ended.");
|
|
66471
|
+
written = written.then(() => file2.appendFile(bytes));
|
|
66472
|
+
await written;
|
|
66473
|
+
}),
|
|
66474
|
+
finish: () => refusing(async () => {
|
|
66475
|
+
end2();
|
|
66476
|
+
try {
|
|
66477
|
+
await written;
|
|
66478
|
+
await file2.close();
|
|
66479
|
+
await rename2(temporary, target);
|
|
66480
|
+
} catch (error62) {
|
|
66481
|
+
await discard();
|
|
66482
|
+
throw error62;
|
|
66483
|
+
}
|
|
66484
|
+
const after = await stat2(target);
|
|
66485
|
+
return { size: after.size, modifiedAt: after.mtime.toISOString() };
|
|
66486
|
+
}),
|
|
66487
|
+
abort: () => refusing(async () => {
|
|
66488
|
+
if (ended) return;
|
|
66489
|
+
end2();
|
|
66490
|
+
await written.catch(() => {
|
|
66491
|
+
});
|
|
66492
|
+
await discard();
|
|
66493
|
+
})
|
|
66494
|
+
};
|
|
66495
|
+
try {
|
|
66496
|
+
if (before) await file2.chmod(before.mode & 511);
|
|
66497
|
+
if (closes !== opening) throw new ExecRefused("It closed before that upload began.");
|
|
66498
|
+
} catch (error62) {
|
|
66499
|
+
await discard();
|
|
66500
|
+
throw error62;
|
|
66501
|
+
}
|
|
66502
|
+
uploads.add(upload);
|
|
66503
|
+
return upload;
|
|
66504
|
+
})
|
|
66505
|
+
};
|
|
66506
|
+
return {
|
|
66507
|
+
files,
|
|
66508
|
+
/** Give up every upload still open, and any that is opening. */
|
|
66509
|
+
close: async () => {
|
|
66510
|
+
closes++;
|
|
66511
|
+
await Promise.all([...uploads].map((upload) => upload.abort()));
|
|
66512
|
+
}
|
|
66513
|
+
};
|
|
66514
|
+
}
|
|
66154
66515
|
var text = (value) => ({ content: [{ type: "text", text: value }] });
|
|
66155
66516
|
var refused = (error62) => {
|
|
66156
66517
|
if (!(error62 instanceof ExecRefused)) throw error62;
|
|
@@ -66202,9 +66563,9 @@ function createServer2(root, sessions, version22, level) {
|
|
|
66202
66563
|
server.registerTool(
|
|
66203
66564
|
"write_stdin",
|
|
66204
66565
|
{
|
|
66205
|
-
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.",
|
|
66206
66567
|
inputSchema: {
|
|
66207
|
-
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."),
|
|
66208
66569
|
chars: external_exports.string().optional().describe("What to type; include \\n to press Enter."),
|
|
66209
66570
|
yield_time_ms: YieldTime.describe(
|
|
66210
66571
|
"How long to wait: with chars 250 to 30000, by default 250; with none 5000 to 30000."
|
|
@@ -66216,6 +66577,28 @@ function createServer2(root, sessions, version22, level) {
|
|
|
66216
66577
|
},
|
|
66217
66578
|
(input2) => answering(() => sessions.write(input2))
|
|
66218
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
|
+
);
|
|
66219
66602
|
}
|
|
66220
66603
|
server.registerTool(
|
|
66221
66604
|
"read",
|
|
@@ -66271,18 +66654,23 @@ function createServer2(root, sessions, version22, level) {
|
|
|
66271
66654
|
);
|
|
66272
66655
|
return server;
|
|
66273
66656
|
}
|
|
66274
|
-
var VERSION = "0.0.
|
|
66657
|
+
var VERSION = "0.0.5";
|
|
66275
66658
|
function createAgentExec(options) {
|
|
66276
66659
|
const root = realpathSync(resolve3(options.workspace));
|
|
66277
66660
|
const level = options.level ?? "read";
|
|
66278
66661
|
const listeners = /* @__PURE__ */ new Set();
|
|
66279
|
-
const sessions = new Sessions(
|
|
66280
|
-
|
|
66281
|
-
|
|
66282
|
-
|
|
66283
|
-
|
|
66284
|
-
|
|
66285
|
-
|
|
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
|
+
);
|
|
66673
|
+
const host = createHostFiles(root, level);
|
|
66286
66674
|
return {
|
|
66287
66675
|
async handle(request, response, body) {
|
|
66288
66676
|
const server = createServer2(root, sessions, VERSION, level);
|
|
@@ -66298,23 +66686,56 @@ function createAgentExec(options) {
|
|
|
66298
66686
|
listeners.add(listener);
|
|
66299
66687
|
return () => void listeners.delete(listener);
|
|
66300
66688
|
},
|
|
66301
|
-
|
|
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
|
+
},
|
|
66700
|
+
async close() {
|
|
66701
|
+
await Promise.all([sessions.stopAll(), host.close()]);
|
|
66702
|
+
}
|
|
66302
66703
|
};
|
|
66303
66704
|
}
|
|
66304
66705
|
|
|
66305
66706
|
// src/screencast.ts
|
|
66707
|
+
var FIRST_FRAME_MS = 500;
|
|
66708
|
+
var RESTARTS = 3;
|
|
66306
66709
|
async function startScreencast(page, onFrame) {
|
|
66307
|
-
|
|
66710
|
+
let seen = false;
|
|
66711
|
+
let stopped = false;
|
|
66712
|
+
const start = () => page.screencast.start({
|
|
66308
66713
|
quality: 70,
|
|
66309
|
-
onFrame: ({ data, viewportWidth, viewportHeight }) =>
|
|
66310
|
-
|
|
66311
|
-
|
|
66714
|
+
onFrame: ({ data, viewportWidth, viewportHeight }) => {
|
|
66715
|
+
seen = true;
|
|
66716
|
+
onFrame({ data, width: viewportWidth, height: viewportHeight });
|
|
66717
|
+
}
|
|
66312
66718
|
});
|
|
66719
|
+
await start();
|
|
66720
|
+
void (async () => {
|
|
66721
|
+
for (let left = RESTARTS; left > 0; left--) {
|
|
66722
|
+
await new Promise((done) => setTimeout(done, FIRST_FRAME_MS).unref());
|
|
66723
|
+
if (seen || stopped) return;
|
|
66724
|
+
await page.screencast.stop().catch(() => {
|
|
66725
|
+
});
|
|
66726
|
+
if (stopped) return;
|
|
66727
|
+
if (!await start().then(() => true).catch(() => false))
|
|
66728
|
+
return;
|
|
66729
|
+
}
|
|
66730
|
+
})();
|
|
66731
|
+
return async () => {
|
|
66732
|
+
stopped = true;
|
|
66733
|
+
await page.screencast.stop().catch(() => {
|
|
66734
|
+
});
|
|
66735
|
+
};
|
|
66313
66736
|
}
|
|
66314
66737
|
|
|
66315
66738
|
// src/browser-live.ts
|
|
66316
|
-
var WAIT_MS = 25e3;
|
|
66317
|
-
var IDLE_MS = 6e4;
|
|
66318
66739
|
var TITLE_CHECK_MS = 1e3;
|
|
66319
66740
|
var STEP_MS = 2e3;
|
|
66320
66741
|
var MAX_BUFFERED = 4 * 1024 * 1024;
|
|
@@ -66322,269 +66743,292 @@ var sleep3 = (ms) => new Promise((done) => setTimeout(done, ms));
|
|
|
66322
66743
|
var within = (work, ms) => Promise.race([work, sleep3(ms).then(() => void 0)]);
|
|
66323
66744
|
var BrowserLive = class {
|
|
66324
66745
|
#endpoint;
|
|
66325
|
-
#
|
|
66326
|
-
#
|
|
66746
|
+
#viewers = /* @__PURE__ */ new Map();
|
|
66747
|
+
#client;
|
|
66327
66748
|
#context;
|
|
66328
66749
|
#ids = /* @__PURE__ */ new Map();
|
|
66329
66750
|
#nextId = 1;
|
|
66330
|
-
#
|
|
66331
|
-
|
|
66332
|
-
#
|
|
66333
|
-
#
|
|
66334
|
-
#
|
|
66335
|
-
|
|
66336
|
-
#
|
|
66337
|
-
|
|
66338
|
-
#
|
|
66339
|
-
#agentWaiting = 0;
|
|
66340
|
-
#answeredChooser = false;
|
|
66751
|
+
#casts = /* @__PURE__ */ new Map();
|
|
66752
|
+
/** A page's cast that is still stopping, which its next one waits for. */
|
|
66753
|
+
#stopping = /* @__PURE__ */ new Map();
|
|
66754
|
+
#dialogs = /* @__PURE__ */ new Map();
|
|
66755
|
+
#choosers = /* @__PURE__ */ new Map();
|
|
66756
|
+
/** The agent's MCP sessions seen so far. */
|
|
66757
|
+
#sessions = /* @__PURE__ */ new Set();
|
|
66758
|
+
/** Those still to be told that a person answered a file chooser. */
|
|
66759
|
+
#owed = /* @__PURE__ */ new Set();
|
|
66341
66760
|
/** The tabs last sent, to send them again only when something about them changed. */
|
|
66342
66761
|
#lastTabs = "";
|
|
66343
66762
|
#titles;
|
|
66344
66763
|
#checking = false;
|
|
66345
|
-
|
|
66764
|
+
/** Counts each time everyone has left, so a connection begun before that knows it is stale. */
|
|
66765
|
+
#ended = 0;
|
|
66346
66766
|
constructor(endpoint) {
|
|
66347
66767
|
this.#endpoint = endpoint;
|
|
66348
66768
|
}
|
|
66349
66769
|
// ---- The agent's side
|
|
66350
|
-
|
|
66351
|
-
|
|
66352
|
-
this.#
|
|
66353
|
-
this.#announceDriver();
|
|
66354
|
-
try {
|
|
66355
|
-
const deadline = Date.now() + WAIT_MS;
|
|
66356
|
-
while (this.#held() && Date.now() < deadline) await sleep3(200);
|
|
66357
|
-
return this.#held() ? "A person has the browser now, so this didn't run. Try again later." : "A person used the browser, so this didn't run and the page may have changed. Take a new snapshot before you act.";
|
|
66358
|
-
} finally {
|
|
66359
|
-
this.#agentWaiting--;
|
|
66360
|
-
this.#announceDriver();
|
|
66361
|
-
}
|
|
66770
|
+
takeAnsweredChooser(session) {
|
|
66771
|
+
this.#sessions.add(session);
|
|
66772
|
+
return this.#owed.delete(session);
|
|
66362
66773
|
}
|
|
66363
|
-
|
|
66364
|
-
|
|
66365
|
-
this.#
|
|
66366
|
-
return answered2;
|
|
66774
|
+
sessionEnded(session) {
|
|
66775
|
+
this.#sessions.delete(session);
|
|
66776
|
+
this.#owed.delete(session);
|
|
66367
66777
|
}
|
|
66368
|
-
|
|
66369
|
-
this.#
|
|
66370
|
-
this.#chooser
|
|
66371
|
-
this.#broadcast({ type: "dialog", open: false });
|
|
66372
|
-
this.#broadcast({ type: "file-chooser", open: false });
|
|
66778
|
+
agentAnsweredChooser() {
|
|
66779
|
+
this.#choosers.clear();
|
|
66780
|
+
for (const ws of this.#viewers.keys()) this.#send(ws, { type: "file-chooser", open: false });
|
|
66373
66781
|
}
|
|
66374
66782
|
// ---- The person's side
|
|
66375
66783
|
open(ws, mode) {
|
|
66376
|
-
|
|
66377
|
-
|
|
66378
|
-
this.#driver = ws;
|
|
66379
|
-
this.#lastInput = Date.now();
|
|
66380
|
-
}
|
|
66784
|
+
const viewer = { mode, chose: false, swallowUp: false, steps: Promise.resolve() };
|
|
66785
|
+
this.#viewers.set(ws, viewer);
|
|
66381
66786
|
ws.on("close", () => this.#left(ws));
|
|
66382
66787
|
ws.on("message", (data, binary) => {
|
|
66383
|
-
if (binary ||
|
|
66788
|
+
if (binary || viewer.mode !== "operate") return;
|
|
66384
66789
|
let message;
|
|
66385
66790
|
try {
|
|
66386
66791
|
message = JSON.parse(data.toString());
|
|
66387
66792
|
} catch {
|
|
66388
66793
|
return;
|
|
66389
66794
|
}
|
|
66390
|
-
this.#input(ws, message);
|
|
66795
|
+
this.#input(ws, viewer, message);
|
|
66391
66796
|
});
|
|
66392
66797
|
this.#begin().then(
|
|
66393
|
-
|
|
66798
|
+
// Unless it chose a tab while this was starting, it is shown the newest.
|
|
66799
|
+
() => viewer.chose ? void 0 : this.#show(ws, this.#newest()),
|
|
66394
66800
|
() => ws.close(1011, "The browser can't be reached.")
|
|
66395
66801
|
);
|
|
66396
66802
|
}
|
|
66397
66803
|
async close() {
|
|
66398
|
-
for (const ws of this.#
|
|
66804
|
+
for (const ws of this.#viewers.keys()) ws.close(1001, "It stopped.");
|
|
66399
66805
|
await this.#end();
|
|
66400
66806
|
}
|
|
66401
|
-
#held() {
|
|
66402
|
-
return this.#driver !== void 0 && Date.now() - this.#lastInput < IDLE_MS;
|
|
66403
|
-
}
|
|
66404
66807
|
#send(ws, message) {
|
|
66405
66808
|
if (ws.readyState === ws.OPEN) ws.send(JSON.stringify(message));
|
|
66406
66809
|
}
|
|
66407
|
-
|
|
66408
|
-
|
|
66810
|
+
/** The sockets shown a page. */
|
|
66811
|
+
#shownTo(page) {
|
|
66812
|
+
return [...this.#viewers].filter(([, viewer]) => viewer.shown === page).map(([ws]) => ws);
|
|
66409
66813
|
}
|
|
66410
|
-
#
|
|
66411
|
-
|
|
66412
|
-
this.#send(ws, {
|
|
66413
|
-
type: "driver",
|
|
66414
|
-
you: ws === this.#driver && this.#held(),
|
|
66415
|
-
held: this.#held(),
|
|
66416
|
-
agentWaiting: this.#agentWaiting > 0
|
|
66417
|
-
});
|
|
66814
|
+
#newest() {
|
|
66815
|
+
return this.#context?.pages().at(-1);
|
|
66418
66816
|
}
|
|
66419
66817
|
async #begin() {
|
|
66420
|
-
this.#
|
|
66421
|
-
const
|
|
66422
|
-
const
|
|
66818
|
+
this.#client ??= (async () => {
|
|
66819
|
+
const ended = this.#ended;
|
|
66820
|
+
const client = await playwright().chromium.connect(await this.#endpoint);
|
|
66821
|
+
const context = client.contexts()[0] ?? await client.newContext();
|
|
66822
|
+
if (ended !== this.#ended) return client;
|
|
66423
66823
|
this.#context = context;
|
|
66424
66824
|
for (const page of context.pages()) this.#track(page);
|
|
66425
66825
|
context.on("page", (page) => {
|
|
66426
66826
|
this.#track(page);
|
|
66427
|
-
|
|
66827
|
+
for (const [ws, viewer] of this.#viewers)
|
|
66828
|
+
if (!viewer.chose || !viewer.shown) void this.#show(ws, page);
|
|
66829
|
+
void this.#tabs();
|
|
66428
66830
|
});
|
|
66429
|
-
|
|
66430
|
-
|
|
66431
|
-
|
|
66831
|
+
if (context.pages().length === 0) await context.newPage();
|
|
66832
|
+
if (ended === this.#ended)
|
|
66833
|
+
this.#titles = setInterval(() => void this.#checkTabs(), TITLE_CHECK_MS);
|
|
66834
|
+
return client;
|
|
66432
66835
|
})();
|
|
66433
|
-
await this.#
|
|
66836
|
+
await this.#client;
|
|
66434
66837
|
}
|
|
66838
|
+
/** Everyone has left. All of it is forgotten at once, so a socket that opens next starts anew. */
|
|
66435
66839
|
async #end() {
|
|
66436
|
-
const
|
|
66437
|
-
|
|
66840
|
+
const client = this.#client;
|
|
66841
|
+
const casts = [...this.#casts.values()];
|
|
66842
|
+
this.#ended++;
|
|
66843
|
+
this.#client = void 0;
|
|
66844
|
+
this.#context = void 0;
|
|
66438
66845
|
clearInterval(this.#titles);
|
|
66439
66846
|
this.#titles = void 0;
|
|
66440
66847
|
this.#lastTabs = "";
|
|
66441
|
-
|
|
66442
|
-
this.#
|
|
66443
|
-
this.#shown = void 0;
|
|
66848
|
+
this.#casts.clear();
|
|
66849
|
+
this.#stopping.clear();
|
|
66444
66850
|
this.#ids.clear();
|
|
66445
|
-
|
|
66851
|
+
this.#dialogs.clear();
|
|
66852
|
+
this.#choosers.clear();
|
|
66853
|
+
await Promise.all(casts.map((cast) => cast.stop.then((stop) => stop?.()).catch(() => {
|
|
66854
|
+
})));
|
|
66855
|
+
await (await client?.catch(() => void 0))?.close().catch(() => {
|
|
66446
66856
|
});
|
|
66447
66857
|
}
|
|
66448
66858
|
#left(ws) {
|
|
66449
|
-
this.#
|
|
66450
|
-
|
|
66451
|
-
|
|
66452
|
-
|
|
66453
|
-
}
|
|
66454
|
-
if (this.#sockets.size === 0) void this.#end();
|
|
66455
|
-
}
|
|
66456
|
-
async #greet(ws) {
|
|
66457
|
-
await this.#tabs(ws);
|
|
66458
|
-
if (this.#size.width) this.#send(ws, { type: "viewport", ...this.#size });
|
|
66459
|
-
if (this.#dialog)
|
|
66460
|
-
this.#send(ws, {
|
|
66461
|
-
type: "dialog",
|
|
66462
|
-
open: true,
|
|
66463
|
-
kind: this.#dialog.type(),
|
|
66464
|
-
message: this.#dialog.message()
|
|
66465
|
-
});
|
|
66466
|
-
if (this.#chooser)
|
|
66467
|
-
this.#send(ws, { type: "file-chooser", open: true, multiple: this.#chooser.isMultiple() });
|
|
66468
|
-
this.#announceDriver();
|
|
66859
|
+
const shown = this.#viewers.get(ws)?.shown;
|
|
66860
|
+
this.#viewers.delete(ws);
|
|
66861
|
+
if (this.#viewers.size === 0) return void this.#end();
|
|
66862
|
+
if (shown) void this.#uncast(shown);
|
|
66469
66863
|
}
|
|
66470
66864
|
#track(page) {
|
|
66471
66865
|
if (this.#ids.has(page)) return;
|
|
66472
66866
|
this.#ids.set(page, this.#nextId++);
|
|
66473
66867
|
page.on("dialog", (dialog) => {
|
|
66474
|
-
this.#
|
|
66475
|
-
this.#
|
|
66476
|
-
|
|
66477
|
-
|
|
66478
|
-
|
|
66479
|
-
|
|
66480
|
-
...dialog.defaultValue() ? { defaultValue: dialog.defaultValue() } : {}
|
|
66481
|
-
});
|
|
66868
|
+
this.#dialogs.set(page, dialog);
|
|
66869
|
+
for (const ws of this.#shownTo(page)) this.#tellDialog(ws, page);
|
|
66870
|
+
});
|
|
66871
|
+
page.on("dialogclosed", () => {
|
|
66872
|
+
this.#dialogs.delete(page);
|
|
66873
|
+
for (const ws of this.#shownTo(page)) this.#tellDialog(ws, page);
|
|
66482
66874
|
});
|
|
66483
66875
|
page.on("filechooser", (chooser) => {
|
|
66484
|
-
this.#
|
|
66485
|
-
|
|
66876
|
+
this.#choosers.set(page, chooser);
|
|
66877
|
+
for (const ws of this.#shownTo(page)) this.#tellChooser(ws, page);
|
|
66486
66878
|
});
|
|
66487
66879
|
page.on("framenavigated", (frame) => {
|
|
66488
66880
|
if (frame === page.mainFrame()) void this.#tabs();
|
|
66489
66881
|
});
|
|
66490
66882
|
page.on("close", () => {
|
|
66491
66883
|
this.#ids.delete(page);
|
|
66492
|
-
|
|
66493
|
-
|
|
66884
|
+
this.#dialogs.delete(page);
|
|
66885
|
+
this.#choosers.delete(page);
|
|
66886
|
+
this.#casts.delete(page);
|
|
66887
|
+
this.#stopping.delete(page);
|
|
66888
|
+
for (const ws of this.#shownTo(page)) void this.#show(ws, this.#newest());
|
|
66889
|
+
void this.#tabs();
|
|
66494
66890
|
});
|
|
66495
66891
|
}
|
|
66496
|
-
|
|
66497
|
-
|
|
66498
|
-
|
|
66499
|
-
|
|
66500
|
-
|
|
66501
|
-
|
|
66502
|
-
|
|
66892
|
+
#tellDialog(ws, page) {
|
|
66893
|
+
const dialog = this.#dialogs.get(page);
|
|
66894
|
+
this.#send(
|
|
66895
|
+
ws,
|
|
66896
|
+
dialog ? {
|
|
66897
|
+
type: "dialog",
|
|
66898
|
+
open: true,
|
|
66899
|
+
kind: dialog.type(),
|
|
66900
|
+
message: dialog.message(),
|
|
66901
|
+
...dialog.defaultValue() ? { defaultValue: dialog.defaultValue() } : {}
|
|
66902
|
+
} : { type: "dialog", open: false }
|
|
66903
|
+
);
|
|
66904
|
+
}
|
|
66905
|
+
#tellChooser(ws, page) {
|
|
66906
|
+
const chooser = this.#choosers.get(page);
|
|
66907
|
+
this.#send(
|
|
66908
|
+
ws,
|
|
66909
|
+
chooser ? { type: "file-chooser", open: true, multiple: chooser.isMultiple() } : { type: "file-chooser", open: false }
|
|
66910
|
+
);
|
|
66911
|
+
}
|
|
66912
|
+
/** The cast of a page, started if no socket was shown it yet. */
|
|
66913
|
+
#cast(page) {
|
|
66914
|
+
const running = this.#casts.get(page);
|
|
66915
|
+
if (running) return running;
|
|
66916
|
+
const onFrame = (frame) => {
|
|
66917
|
+
const watching = this.#shownTo(page);
|
|
66918
|
+
if (frame.width !== cast.size.width || frame.height !== cast.size.height) {
|
|
66919
|
+
cast.size = { width: frame.width, height: frame.height };
|
|
66920
|
+
for (const ws of watching) this.#send(ws, { type: "viewport", ...cast.size });
|
|
66921
|
+
}
|
|
66922
|
+
cast.last = frame.data;
|
|
66923
|
+
for (const ws of watching)
|
|
66924
|
+
if (ws.readyState === ws.OPEN && ws.bufferedAmount < MAX_BUFFERED) ws.send(frame.data);
|
|
66925
|
+
};
|
|
66926
|
+
const cast = {
|
|
66927
|
+
size: { width: 0, height: 0 },
|
|
66928
|
+
// A page has one screencast at a time, so this one starts once the last has stopped.
|
|
66929
|
+
stop: (this.#stopping.get(page) ?? Promise.resolve()).then(() => startScreencast(page, onFrame)).catch(() => void 0)
|
|
66930
|
+
};
|
|
66931
|
+
this.#casts.set(page, cast);
|
|
66932
|
+
return cast;
|
|
66933
|
+
}
|
|
66934
|
+
/** Stop casting a page no socket is shown any more. */
|
|
66935
|
+
async #uncast(page) {
|
|
66936
|
+
const cast = this.#casts.get(page);
|
|
66937
|
+
if (!cast || this.#shownTo(page).length > 0) return;
|
|
66938
|
+
this.#casts.delete(page);
|
|
66939
|
+
const stopped = cast.stop.then((stop) => stop?.()).catch(() => {
|
|
66940
|
+
});
|
|
66941
|
+
this.#stopping.set(page, stopped);
|
|
66942
|
+
await stopped;
|
|
66943
|
+
if (this.#stopping.get(page) === stopped) this.#stopping.delete(page);
|
|
66944
|
+
}
|
|
66945
|
+
/** Change the page a socket is shown, and tell it what that page is waiting on. */
|
|
66946
|
+
async #show(ws, page) {
|
|
66947
|
+
const viewer = this.#viewers.get(ws);
|
|
66948
|
+
if (!viewer || page && viewer.shown === page) return;
|
|
66949
|
+
const before = viewer.shown;
|
|
66950
|
+
viewer.shown = page;
|
|
66951
|
+
viewer.choice = void 0;
|
|
66952
|
+
viewer.swallowUp = false;
|
|
66953
|
+
if (before) await this.#uncast(before);
|
|
66954
|
+
if (this.#viewers.get(ws) !== viewer || viewer.shown !== page) return;
|
|
66503
66955
|
if (page) {
|
|
66504
|
-
|
|
66505
|
-
|
|
66506
|
-
|
|
66507
|
-
|
|
66508
|
-
|
|
66509
|
-
for (const ws of this.#sockets.keys())
|
|
66510
|
-
if (ws.readyState === ws.OPEN && ws.bufferedAmount < MAX_BUFFERED) ws.send(frame.data);
|
|
66511
|
-
}).catch(() => void 0);
|
|
66956
|
+
const cast = this.#cast(page);
|
|
66957
|
+
if (cast.size.width) this.#send(ws, { type: "viewport", ...cast.size });
|
|
66958
|
+
if (cast.last && ws.readyState === ws.OPEN) ws.send(cast.last);
|
|
66959
|
+
this.#tellDialog(ws, page);
|
|
66960
|
+
this.#tellChooser(ws, page);
|
|
66512
66961
|
}
|
|
66513
|
-
await this.#tabs();
|
|
66962
|
+
await this.#tabs(ws);
|
|
66514
66963
|
}
|
|
66515
66964
|
async #tabList() {
|
|
66516
|
-
|
|
66965
|
+
return Promise.all(
|
|
66517
66966
|
[...this.#ids].map(async ([page, id]) => ({
|
|
66518
66967
|
id,
|
|
66519
66968
|
url: page.url(),
|
|
66520
66969
|
title: await within(page.title(), STEP_MS).catch(() => void 0) ?? ""
|
|
66521
66970
|
}))
|
|
66522
66971
|
);
|
|
66523
|
-
return { type: "tabs", tabs, shown: (this.#shown && this.#ids.get(this.#shown)) ?? null };
|
|
66524
66972
|
}
|
|
66525
|
-
|
|
66526
|
-
|
|
66527
|
-
|
|
66528
|
-
if (only) this.#
|
|
66529
|
-
|
|
66973
|
+
/** The tabs, to one socket or to all, each told which one it is shown. */
|
|
66974
|
+
async #tabs(only, listed) {
|
|
66975
|
+
const tabs = listed ?? await this.#tabList();
|
|
66976
|
+
if (!only) this.#lastTabs = JSON.stringify(tabs);
|
|
66977
|
+
for (const [ws, viewer] of this.#viewers)
|
|
66978
|
+
if (!only || ws === only)
|
|
66979
|
+
this.#send(ws, {
|
|
66980
|
+
type: "tabs",
|
|
66981
|
+
tabs,
|
|
66982
|
+
shown: (viewer.shown && this.#ids.get(viewer.shown)) ?? null
|
|
66983
|
+
});
|
|
66530
66984
|
}
|
|
66531
66985
|
/** Send the tabs again if anything about them changed, such as a title. */
|
|
66532
66986
|
async #checkTabs() {
|
|
66533
66987
|
if (this.#checking) return;
|
|
66534
66988
|
this.#checking = true;
|
|
66535
66989
|
try {
|
|
66536
|
-
const
|
|
66537
|
-
|
|
66538
|
-
if (text2 === this.#lastTabs) return;
|
|
66539
|
-
this.#lastTabs = text2;
|
|
66540
|
-
this.#broadcast(message);
|
|
66990
|
+
const tabs = await this.#tabList();
|
|
66991
|
+
if (JSON.stringify(tabs) !== this.#lastTabs) await this.#tabs(void 0, tabs);
|
|
66541
66992
|
} finally {
|
|
66542
66993
|
this.#checking = false;
|
|
66543
66994
|
}
|
|
66544
66995
|
}
|
|
66545
66996
|
/** Each step in order, none waiting on another for long: a dialog blocks the page's input. */
|
|
66546
|
-
#step(work) {
|
|
66547
|
-
|
|
66997
|
+
#step(viewer, work) {
|
|
66998
|
+
viewer.steps = viewer.steps.then(() => within(work(), STEP_MS)).catch(() => {
|
|
66548
66999
|
});
|
|
66549
67000
|
}
|
|
66550
|
-
#input(ws, message) {
|
|
66551
|
-
|
|
66552
|
-
if (this.#driver === ws) this.#driver = void 0;
|
|
66553
|
-
return this.#announceDriver();
|
|
66554
|
-
}
|
|
66555
|
-
const changed = this.#driver !== ws || !this.#held();
|
|
66556
|
-
this.#driver = ws;
|
|
66557
|
-
this.#lastInput = Date.now();
|
|
66558
|
-
if (changed) this.#announceDriver();
|
|
66559
|
-
const page = this.#shown;
|
|
67001
|
+
#input(ws, viewer, message) {
|
|
67002
|
+
const page = viewer.shown;
|
|
66560
67003
|
switch (message.type) {
|
|
66561
67004
|
case "mouse":
|
|
66562
67005
|
if (!page) return;
|
|
66563
|
-
return this.#step(() => this.#mouse(page, message));
|
|
67006
|
+
return this.#step(viewer, () => this.#mouse(ws, viewer, page, message));
|
|
66564
67007
|
case "key":
|
|
66565
67008
|
if (!page) return;
|
|
66566
67009
|
return this.#step(
|
|
67010
|
+
viewer,
|
|
66567
67011
|
() => message.action === "down" ? page.keyboard.down(message.key) : page.keyboard.up(message.key)
|
|
66568
67012
|
);
|
|
66569
67013
|
case "text":
|
|
66570
67014
|
if (!page) return;
|
|
66571
|
-
return this.#step(() => page.keyboard.insertText(message.text));
|
|
67015
|
+
return this.#step(viewer, () => page.keyboard.insertText(message.text));
|
|
66572
67016
|
case "dialog": {
|
|
66573
|
-
const dialog = this.#
|
|
66574
|
-
this.#dialog = void 0;
|
|
66575
|
-
this.#broadcast({ type: "dialog", open: false });
|
|
67017
|
+
const dialog = page && this.#dialogs.get(page);
|
|
66576
67018
|
if (!dialog) return;
|
|
66577
67019
|
return this.#step(
|
|
67020
|
+
viewer,
|
|
66578
67021
|
() => message.accept ? dialog.accept(message.text ?? void 0) : dialog.dismiss()
|
|
66579
67022
|
);
|
|
66580
67023
|
}
|
|
66581
67024
|
case "files": {
|
|
66582
|
-
const chooser = this.#
|
|
66583
|
-
|
|
66584
|
-
this.#
|
|
66585
|
-
|
|
66586
|
-
this.#
|
|
67025
|
+
const chooser = page && this.#choosers.get(page);
|
|
67026
|
+
if (!page || !chooser) return;
|
|
67027
|
+
this.#choosers.delete(page);
|
|
67028
|
+
for (const socket of this.#shownTo(page)) this.#tellChooser(socket, page);
|
|
67029
|
+
for (const session of this.#sessions) this.#owed.add(session);
|
|
66587
67030
|
return this.#step(
|
|
67031
|
+
viewer,
|
|
66588
67032
|
() => chooser.setFiles(
|
|
66589
67033
|
message.files.map((file2) => ({
|
|
66590
67034
|
name: file2.name,
|
|
@@ -66595,16 +67039,16 @@ var BrowserLive = class {
|
|
|
66595
67039
|
);
|
|
66596
67040
|
}
|
|
66597
67041
|
case "choice": {
|
|
66598
|
-
const choice =
|
|
66599
|
-
|
|
67042
|
+
const choice = viewer.choice;
|
|
67043
|
+
viewer.choice = void 0;
|
|
66600
67044
|
if (!choice) return;
|
|
66601
|
-
return this.#step(() => choice.selectOption(message.values));
|
|
67045
|
+
return this.#step(viewer, () => choice.selectOption(message.values));
|
|
66602
67046
|
}
|
|
66603
67047
|
case "tab":
|
|
66604
|
-
return this.#step(() => this.#tab(message));
|
|
67048
|
+
return this.#step(viewer, () => this.#tab(ws, viewer, message));
|
|
66605
67049
|
case "navigate":
|
|
66606
67050
|
if (!page) return;
|
|
66607
|
-
return this.#step(async () => {
|
|
67051
|
+
return this.#step(viewer, async () => {
|
|
66608
67052
|
if (message.to === "back") await page.goBack();
|
|
66609
67053
|
else if (message.to === "forward") await page.goForward();
|
|
66610
67054
|
else if (message.to === "reload") await page.reload();
|
|
@@ -66612,15 +67056,15 @@ var BrowserLive = class {
|
|
|
66612
67056
|
});
|
|
66613
67057
|
}
|
|
66614
67058
|
}
|
|
66615
|
-
async #mouse(page, message) {
|
|
67059
|
+
async #mouse(ws, viewer, page, message) {
|
|
66616
67060
|
const { x, y } = message;
|
|
66617
67061
|
if (message.action === "move") return page.mouse.move(x, y);
|
|
66618
67062
|
if (message.action === "wheel")
|
|
66619
67063
|
return page.mouse.wheel(message.deltaX ?? 0, message.deltaY ?? 0);
|
|
66620
67064
|
const button = message.button ?? "left";
|
|
66621
67065
|
if (message.action === "up") {
|
|
66622
|
-
if (
|
|
66623
|
-
|
|
67066
|
+
if (viewer.swallowUp) {
|
|
67067
|
+
viewer.swallowUp = false;
|
|
66624
67068
|
return;
|
|
66625
67069
|
}
|
|
66626
67070
|
return page.mouse.up({ button });
|
|
@@ -66646,29 +67090,33 @@ var BrowserLive = class {
|
|
|
66646
67090
|
([px, py]) => document.elementFromPoint(px, py),
|
|
66647
67091
|
[x, y]
|
|
66648
67092
|
);
|
|
66649
|
-
|
|
66650
|
-
|
|
66651
|
-
return this.#
|
|
67093
|
+
viewer.choice = handle.asElement() ?? void 0;
|
|
67094
|
+
viewer.swallowUp = true;
|
|
67095
|
+
return this.#send(ws, { type: "choice", ...choice });
|
|
66652
67096
|
}
|
|
66653
67097
|
}
|
|
66654
67098
|
await page.mouse.move(x, y);
|
|
66655
67099
|
return page.mouse.down({ button });
|
|
66656
67100
|
}
|
|
66657
|
-
|
|
67101
|
+
/** Showing or opening a tab makes the socket's page its own from then on. */
|
|
67102
|
+
async #tab(ws, viewer, message) {
|
|
66658
67103
|
const byId = (id) => [...this.#ids].find(([, known]) => known === id)?.[0];
|
|
66659
|
-
if (message.action === "show") return this.#show(byId(message.id));
|
|
66660
67104
|
if (message.action === "close") return byId(message.id)?.close();
|
|
67105
|
+
if (message.action === "show") {
|
|
67106
|
+
const page2 = byId(message.id);
|
|
67107
|
+
if (!page2) return;
|
|
67108
|
+
viewer.chose = true;
|
|
67109
|
+
return this.#show(ws, page2);
|
|
67110
|
+
}
|
|
66661
67111
|
const page = await this.#context?.newPage();
|
|
66662
|
-
if (page
|
|
67112
|
+
if (!page) return;
|
|
67113
|
+
viewer.chose = true;
|
|
67114
|
+
await this.#show(ws, page);
|
|
67115
|
+
if (message.url) await page.goto(message.url).catch(() => {
|
|
66663
67116
|
});
|
|
66664
67117
|
}
|
|
66665
67118
|
};
|
|
66666
67119
|
|
|
66667
|
-
// src/live.ts
|
|
66668
|
-
import { chmodSync, existsSync, statSync as statSync2 } from "node:fs";
|
|
66669
|
-
import { createRequire as createRequire3 } from "node:module";
|
|
66670
|
-
import { dirname as dirname3, join as join4 } from "node:path";
|
|
66671
|
-
|
|
66672
67120
|
// node_modules/ws/wrapper.mjs
|
|
66673
67121
|
var import_stream2 = __toESM(require_stream(), 1);
|
|
66674
67122
|
var import_extension = __toESM(require_extension(), 1);
|
|
@@ -66679,71 +67127,239 @@ var import_subprotocol = __toESM(require_subprotocol(), 1);
|
|
|
66679
67127
|
var import_websocket = __toESM(require_websocket(), 1);
|
|
66680
67128
|
var import_websocket_server = __toESM(require_websocket_server(), 1);
|
|
66681
67129
|
|
|
67130
|
+
// src/files-live.ts
|
|
67131
|
+
var MAX_PART = 1024 * 1024;
|
|
67132
|
+
var MAX_BUFFERED2 = 4 * 1024 * 1024;
|
|
67133
|
+
var MAX_UPLOADS = 64;
|
|
67134
|
+
var reason2 = (error62) => error62 instanceof Error ? error62.message : String(error62);
|
|
67135
|
+
var text2 = (value) => typeof value === "string";
|
|
67136
|
+
var whole = (value) => Number.isSafeInteger(value);
|
|
67137
|
+
var sleep4 = (ms) => new Promise((done) => setTimeout(done, ms));
|
|
67138
|
+
var FilesLive = class {
|
|
67139
|
+
#files;
|
|
67140
|
+
#sockets = /* @__PURE__ */ new Set();
|
|
67141
|
+
/** One walk of the workspace at a time, however many ask. */
|
|
67142
|
+
#walking = Promise.resolve();
|
|
67143
|
+
/** How many files are part written, over every socket. */
|
|
67144
|
+
#uploads = 0;
|
|
67145
|
+
constructor(files) {
|
|
67146
|
+
this.#files = files;
|
|
67147
|
+
}
|
|
67148
|
+
close() {
|
|
67149
|
+
for (const ws of this.#sockets) ws.close(1001, "It stopped.");
|
|
67150
|
+
}
|
|
67151
|
+
open(ws, mode) {
|
|
67152
|
+
this.#sockets.add(ws);
|
|
67153
|
+
const uploads = /* @__PURE__ */ new Map();
|
|
67154
|
+
let awaited;
|
|
67155
|
+
let writing = Promise.resolve();
|
|
67156
|
+
let waiting = 0;
|
|
67157
|
+
let reading = Promise.resolve();
|
|
67158
|
+
let closed = false;
|
|
67159
|
+
const send2 = (answer) => {
|
|
67160
|
+
if (ws.readyState === ws.OPEN) ws.send(JSON.stringify(answer));
|
|
67161
|
+
};
|
|
67162
|
+
const refuse = (id, message) => send2({ type: "refused", id, message });
|
|
67163
|
+
const giveUp = async (path) => {
|
|
67164
|
+
const had = uploads.get(path);
|
|
67165
|
+
if (!had) return;
|
|
67166
|
+
uploads.delete(path);
|
|
67167
|
+
this.#uploads--;
|
|
67168
|
+
await had.upload.abort().catch(() => {
|
|
67169
|
+
});
|
|
67170
|
+
};
|
|
67171
|
+
const drained = async () => {
|
|
67172
|
+
while (!closed && ws.bufferedAmount > MAX_BUFFERED2) await sleep4(50);
|
|
67173
|
+
};
|
|
67174
|
+
const writePart = async (write, bytes) => {
|
|
67175
|
+
const { id, path, offset, last } = write;
|
|
67176
|
+
if (bytes.length !== write.length)
|
|
67177
|
+
throw new Error(`It said ${write.length} bytes, and ${bytes.length} came.`);
|
|
67178
|
+
if (offset === 0) {
|
|
67179
|
+
await giveUp(path);
|
|
67180
|
+
if (this.#uploads >= MAX_UPLOADS)
|
|
67181
|
+
throw new Error(`${MAX_UPLOADS} files are part written here. Finish one first.`);
|
|
67182
|
+
this.#uploads++;
|
|
67183
|
+
try {
|
|
67184
|
+
uploads.set(path, { upload: await this.#files.upload(path), size: 0 });
|
|
67185
|
+
} catch (error62) {
|
|
67186
|
+
this.#uploads--;
|
|
67187
|
+
throw error62;
|
|
67188
|
+
}
|
|
67189
|
+
}
|
|
67190
|
+
const file2 = uploads.get(path);
|
|
67191
|
+
if (!file2 || file2.size !== offset)
|
|
67192
|
+
throw new Error(`The next part of ${path} starts at ${file2?.size ?? 0}.`);
|
|
67193
|
+
await file2.upload.write(bytes);
|
|
67194
|
+
file2.size += bytes.length;
|
|
67195
|
+
if (!last) return void 0;
|
|
67196
|
+
uploads.delete(path);
|
|
67197
|
+
this.#uploads--;
|
|
67198
|
+
return { type: "written", id, path, size: (await file2.upload.finish()).size };
|
|
67199
|
+
};
|
|
67200
|
+
const inTurn = (work) => {
|
|
67201
|
+
const done = reading.then(async () => {
|
|
67202
|
+
await drained();
|
|
67203
|
+
if (!closed) await work();
|
|
67204
|
+
});
|
|
67205
|
+
reading = done.catch(() => {
|
|
67206
|
+
});
|
|
67207
|
+
return done;
|
|
67208
|
+
};
|
|
67209
|
+
const handle = async (request) => {
|
|
67210
|
+
const { id } = request;
|
|
67211
|
+
switch (request.type) {
|
|
67212
|
+
case "list": {
|
|
67213
|
+
const path = request.path ?? ".";
|
|
67214
|
+
if (!text2(path)) throw new Error("A path is text.");
|
|
67215
|
+
return inTurn(
|
|
67216
|
+
async () => send2({ type: "entries", id, path, ...await this.#files.list(path) })
|
|
67217
|
+
);
|
|
67218
|
+
}
|
|
67219
|
+
case "read": {
|
|
67220
|
+
const { path, offset, length } = request;
|
|
67221
|
+
if (!text2(path)) throw new Error("A path is text.");
|
|
67222
|
+
return inTurn(async () => {
|
|
67223
|
+
const { bytes, size, modifiedAt } = await this.#files.read(path, offset, length);
|
|
67224
|
+
if (ws.readyState !== ws.OPEN) return;
|
|
67225
|
+
send2({ type: "data", id, path, offset, length: bytes.length, size, modifiedAt });
|
|
67226
|
+
ws.send(bytes);
|
|
67227
|
+
});
|
|
67228
|
+
}
|
|
67229
|
+
case "changed": {
|
|
67230
|
+
const { since } = request;
|
|
67231
|
+
if (!text2(since)) throw new Error("That isn't a time.");
|
|
67232
|
+
const walked = this.#walking.then(
|
|
67233
|
+
() => ws.readyState === ws.OPEN ? this.#files.changed(new Date(since)) : void 0
|
|
67234
|
+
);
|
|
67235
|
+
this.#walking = walked.catch(() => {
|
|
67236
|
+
});
|
|
67237
|
+
const found = await walked;
|
|
67238
|
+
await drained();
|
|
67239
|
+
if (found) send2({ type: "changes", id, since, ...found });
|
|
67240
|
+
return;
|
|
67241
|
+
}
|
|
67242
|
+
case "write": {
|
|
67243
|
+
awaited = "refused";
|
|
67244
|
+
if (mode !== "operate") throw new Error("This socket watches: it can't write.");
|
|
67245
|
+
const { path, offset, length, last } = request;
|
|
67246
|
+
if (!text2(path) || !whole(offset) || offset < 0 || !whole(length) || length < 0 || length > MAX_PART || typeof last !== "boolean")
|
|
67247
|
+
throw new Error(
|
|
67248
|
+
"A write names a path, where it starts, 0 to 1,048,576 bytes, and last."
|
|
67249
|
+
);
|
|
67250
|
+
awaited = request;
|
|
67251
|
+
return;
|
|
67252
|
+
}
|
|
67253
|
+
default:
|
|
67254
|
+
throw new Error("That isn't something files@1 does.");
|
|
67255
|
+
}
|
|
67256
|
+
};
|
|
67257
|
+
ws.on("close", () => {
|
|
67258
|
+
closed = true;
|
|
67259
|
+
this.#sockets.delete(ws);
|
|
67260
|
+
void writing.finally(() => Promise.all([...uploads.keys()].map(giveUp)));
|
|
67261
|
+
});
|
|
67262
|
+
ws.on("message", (data, binary) => {
|
|
67263
|
+
const write = awaited;
|
|
67264
|
+
awaited = void 0;
|
|
67265
|
+
if (binary) {
|
|
67266
|
+
if (write === "refused") return;
|
|
67267
|
+
if (!write) return ws.close(1008, "Those bytes belong to no write.");
|
|
67268
|
+
waiting++;
|
|
67269
|
+
ws.pause();
|
|
67270
|
+
writing = writing.then(() => closed ? void 0 : writePart(write, data)).then(
|
|
67271
|
+
(done) => done && send2(done),
|
|
67272
|
+
async (error62) => {
|
|
67273
|
+
await giveUp(write.path);
|
|
67274
|
+
refuse(write.id, reason2(error62));
|
|
67275
|
+
}
|
|
67276
|
+
).finally(() => {
|
|
67277
|
+
if (--waiting === 0) ws.resume();
|
|
67278
|
+
});
|
|
67279
|
+
return;
|
|
67280
|
+
}
|
|
67281
|
+
if (write && write !== "refused") refuse(write.id, "Its bytes didn't follow it.");
|
|
67282
|
+
let request;
|
|
67283
|
+
try {
|
|
67284
|
+
request = JSON.parse(data.toString());
|
|
67285
|
+
} catch {
|
|
67286
|
+
return refuse("", "That isn't JSON.");
|
|
67287
|
+
}
|
|
67288
|
+
const { id, type } = request ?? {};
|
|
67289
|
+
if (type === "write") awaited = "refused";
|
|
67290
|
+
if (!text2(id) || id.length < 1 || id.length > 64)
|
|
67291
|
+
return refuse("", "Every request has an id of 1 to 64 characters.");
|
|
67292
|
+
handle(request).catch((error62) => refuse(id, reason2(error62)));
|
|
67293
|
+
});
|
|
67294
|
+
}
|
|
67295
|
+
};
|
|
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
|
+
|
|
66682
67309
|
// src/live.ts
|
|
66683
67310
|
var PROTOCOLS = {
|
|
66684
|
-
|
|
66685
|
-
|
|
67311
|
+
terminal: "terminal@1",
|
|
67312
|
+
files: "files@1",
|
|
66686
67313
|
browser: "screencast@1"
|
|
66687
67314
|
};
|
|
66688
|
-
var
|
|
66689
|
-
var
|
|
66690
|
-
var
|
|
66691
|
-
|
|
66692
|
-
|
|
66693
|
-
|
|
66694
|
-
var
|
|
66695
|
-
var
|
|
66696
|
-
|
|
66697
|
-
|
|
66698
|
-
|
|
66699
|
-
|
|
66700
|
-
loaded2 = require2("node-pty");
|
|
66701
|
-
} catch {
|
|
66702
|
-
return "The terminal needs node-pty, which isn't installed with this service.";
|
|
66703
|
-
}
|
|
66704
|
-
const root = dirname3(require2.resolve("node-pty/package.json"));
|
|
66705
|
-
for (const helper of [
|
|
66706
|
-
join4(root, "prebuilds", `${process.platform}-${process.arch}`, "spawn-helper"),
|
|
66707
|
-
join4(root, "build", "Release", "spawn-helper")
|
|
66708
|
-
])
|
|
66709
|
-
try {
|
|
66710
|
-
if (existsSync(helper) && (statSync2(helper).mode & 73) === 0) chmodSync(helper, 493);
|
|
66711
|
-
} catch {
|
|
66712
|
-
return `The terminal needs ${helper} to be executable, and it isn't.`;
|
|
66713
|
-
}
|
|
66714
|
-
return loaded2;
|
|
66715
|
-
})();
|
|
66716
|
-
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 })
|
|
66717
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;
|
|
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
|
+
}
|
|
66718
67339
|
var Live = class {
|
|
66719
|
-
#
|
|
66720
|
-
#
|
|
66721
|
-
#events = [];
|
|
66722
|
-
#watchers = /* @__PURE__ */ new Set();
|
|
66723
|
-
#shells = /* @__PURE__ */ new Map();
|
|
66724
|
-
#shell;
|
|
66725
|
-
#scrollback = "";
|
|
67340
|
+
#terminals;
|
|
67341
|
+
#viewers = /* @__PURE__ */ new Set();
|
|
66726
67342
|
#browser;
|
|
66727
|
-
|
|
66728
|
-
|
|
66729
|
-
|
|
67343
|
+
#files;
|
|
67344
|
+
/** Whether a person may write files here: not where the agent can only read them. */
|
|
67345
|
+
#writes;
|
|
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
|
+
}
|
|
67353
|
+
this.#files = exec && new FilesLive(exec.files);
|
|
67354
|
+
this.#writes = access !== "read";
|
|
66730
67355
|
this.#browser = browser;
|
|
66731
|
-
exec?.onCommand((event) => this.#heard(event));
|
|
66732
67356
|
}
|
|
66733
|
-
|
|
67357
|
+
surfaces() {
|
|
66734
67358
|
const surfaces = [];
|
|
66735
|
-
if (this.#
|
|
66736
|
-
|
|
66737
|
-
|
|
66738
|
-
|
|
66739
|
-
{
|
|
66740
|
-
surface: "terminal",
|
|
66741
|
-
protocol: PROTOCOLS.terminal,
|
|
66742
|
-
operate: true,
|
|
66743
|
-
...typeof loaded2 === "string" ? { unavailable: loaded2 } : {}
|
|
66744
|
-
}
|
|
66745
|
-
);
|
|
66746
|
-
}
|
|
67359
|
+
if (this.#terminals)
|
|
67360
|
+
surfaces.push({ surface: "terminal", protocol: PROTOCOLS.terminal, operate: true });
|
|
67361
|
+
if (this.#files)
|
|
67362
|
+
surfaces.push({ surface: "files", protocol: PROTOCOLS.files, operate: this.#writes });
|
|
66747
67363
|
if (this.#browser)
|
|
66748
67364
|
surfaces.push({ surface: "browser", protocol: PROTOCOLS.browser, operate: true });
|
|
66749
67365
|
return surfaces;
|
|
@@ -66751,95 +67367,71 @@ var Live = class {
|
|
|
66751
67367
|
/** Take an upgraded request as a socket on one surface. */
|
|
66752
67368
|
open(surface, mode, request, socket, head) {
|
|
66753
67369
|
socket.on("error", () => socket.destroy());
|
|
66754
|
-
|
|
67370
|
+
SERVERS[surface].handleUpgrade(request, socket, head, (ws) => {
|
|
66755
67371
|
ws.on("error", () => ws.terminate());
|
|
66756
|
-
if (surface === "
|
|
67372
|
+
if (surface === "terminal" && this.#terminals) this.#view(ws, mode, this.#terminals);
|
|
67373
|
+
else if (surface === "files" && this.#files) this.#files.open(ws, mode);
|
|
66757
67374
|
else if (surface === "browser" && this.#browser) this.#browser.open(ws, mode);
|
|
66758
|
-
else if (surface === "terminal") this.#attach(ws, mode).catch(() => ws.terminate());
|
|
66759
67375
|
else ws.close(1011, `There's no ${surface} here.`);
|
|
66760
67376
|
});
|
|
66761
67377
|
}
|
|
67378
|
+
/** The terminals themselves end with the AgentExec that runs them. */
|
|
66762
67379
|
close() {
|
|
66763
67380
|
void this.#browser?.close();
|
|
66764
|
-
|
|
66765
|
-
this.#
|
|
66766
|
-
|
|
66767
|
-
|
|
66768
|
-
|
|
66769
|
-
|
|
66770
|
-
|
|
66771
|
-
|
|
66772
|
-
|
|
66773
|
-
|
|
66774
|
-
|
|
66775
|
-
|
|
66776
|
-
|
|
66777
|
-
|
|
66778
|
-
|
|
66779
|
-
|
|
66780
|
-
|
|
66781
|
-
|
|
66782
|
-
|
|
66783
|
-
if (
|
|
66784
|
-
|
|
66785
|
-
|
|
66786
|
-
let shell;
|
|
67381
|
+
this.#files?.close();
|
|
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) => {
|
|
66787
67403
|
try {
|
|
66788
|
-
|
|
66789
|
-
|
|
66790
|
-
|
|
66791
|
-
|
|
66792
|
-
|
|
66793
|
-
|
|
66794
|
-
|
|
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);
|
|
66795
67413
|
} catch (error62) {
|
|
66796
|
-
|
|
67414
|
+
refuse(error62 instanceof SyntaxError ? MALFORMED : error62);
|
|
66797
67415
|
}
|
|
66798
|
-
shell.onData((data) => {
|
|
66799
|
-
this.#scrollback = (this.#scrollback + data).slice(-MAX_SCROLLBACK);
|
|
66800
|
-
const frame = Buffer.concat([Buffer.from([OUTPUT]), Buffer.from(data)]);
|
|
66801
|
-
for (const client of this.#shells.keys()) client.send(frame);
|
|
66802
|
-
});
|
|
66803
|
-
shell.onExit(() => {
|
|
66804
|
-
if (this.#shell !== shell) return;
|
|
66805
|
-
this.#shell = void 0;
|
|
66806
|
-
for (const client of this.#shells.keys()) client.close(1e3, "The shell exited.");
|
|
66807
|
-
});
|
|
66808
|
-
this.#shell = shell;
|
|
66809
|
-
}
|
|
66810
|
-
if (this.#scrollback)
|
|
66811
|
-
ws.send(Buffer.concat([Buffer.from([OUTPUT]), Buffer.from(this.#scrollback)]));
|
|
66812
|
-
this.#shells.set(ws, mode);
|
|
66813
|
-
ws.on("close", () => this.#shells.delete(ws));
|
|
66814
|
-
ws.on("message", (data, binary) => {
|
|
66815
|
-
const frame = binary ? data : Buffer.from(data);
|
|
66816
|
-
if (mode !== "operate" || frame.length === 0 || !this.#shell) return;
|
|
66817
|
-
if (frame[0] === INPUT) this.#shell.write(frame.subarray(1).toString());
|
|
66818
|
-
else if (frame[0] === RESIZE)
|
|
66819
|
-
try {
|
|
66820
|
-
const { columns, rows } = JSON.parse(frame.subarray(1).toString());
|
|
66821
|
-
if (columns > 0 && rows > 0) this.#shell.resize(Math.floor(columns), Math.floor(rows));
|
|
66822
|
-
} catch {
|
|
66823
|
-
}
|
|
66824
67416
|
});
|
|
66825
67417
|
}
|
|
66826
67418
|
};
|
|
66827
67419
|
|
|
66828
67420
|
// src/store.ts
|
|
66829
|
-
import { createHash, randomBytes as
|
|
66830
|
-
import { chmodSync
|
|
67421
|
+
import { createHash, randomBytes as randomBytes4, timingSafeEqual } from "node:crypto";
|
|
67422
|
+
import { chmodSync, mkdirSync, readFileSync as readFileSync2, renameSync, writeFileSync } from "node:fs";
|
|
66831
67423
|
import { join as join5 } from "node:path";
|
|
66832
|
-
var newToken = () =>
|
|
67424
|
+
var newToken = () => randomBytes4(32).toString("base64url");
|
|
66833
67425
|
var digestOf = (token) => createHash("sha256").update(token).digest("hex");
|
|
66834
67426
|
var sameToken = (token, digest) => timingSafeEqual(Buffer.from(digestOf(token), "hex"), Buffer.from(digest, "hex"));
|
|
66835
|
-
function writePrivate(file2,
|
|
66836
|
-
writeFileSync(`${file2}.tmp`,
|
|
67427
|
+
function writePrivate(file2, text3) {
|
|
67428
|
+
writeFileSync(`${file2}.tmp`, text3, { mode: 384, flush: true });
|
|
66837
67429
|
renameSync(`${file2}.tmp`, file2);
|
|
66838
|
-
|
|
67430
|
+
chmodSync(file2, 384);
|
|
66839
67431
|
}
|
|
66840
67432
|
var privateFolder = (dir) => {
|
|
66841
67433
|
mkdirSync(dir, { recursive: true, mode: 448 });
|
|
66842
|
-
|
|
67434
|
+
chmodSync(dir, 448);
|
|
66843
67435
|
return dir;
|
|
66844
67436
|
};
|
|
66845
67437
|
function controlToken(data) {
|
|
@@ -66863,6 +67455,33 @@ var ApiError = class extends Error {
|
|
|
66863
67455
|
this.status = status;
|
|
66864
67456
|
}
|
|
66865
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
|
+
};
|
|
66866
67485
|
var ProcessRuntime = class {
|
|
66867
67486
|
isolation = "none";
|
|
66868
67487
|
#config;
|
|
@@ -66878,7 +67497,11 @@ var ProcessRuntime = class {
|
|
|
66878
67497
|
async start(spec, folder, events) {
|
|
66879
67498
|
let exec;
|
|
66880
67499
|
if (spec.access !== "none")
|
|
66881
|
-
exec = createAgentExec({
|
|
67500
|
+
exec = createAgentExec({
|
|
67501
|
+
workspace: spec.workspace,
|
|
67502
|
+
level: spec.access,
|
|
67503
|
+
pty: await loadPty()
|
|
67504
|
+
});
|
|
66882
67505
|
let browser;
|
|
66883
67506
|
let browserDown = false;
|
|
66884
67507
|
if (spec.browser) {
|
|
@@ -66906,10 +67529,19 @@ var ProcessRuntime = class {
|
|
|
66906
67529
|
browserLive = new BrowserLive(browser.endpoint);
|
|
66907
67530
|
browser.gate = browserLive;
|
|
66908
67531
|
}
|
|
66909
|
-
const live = new Live(exec, spec.
|
|
67532
|
+
const live = new Live(exec, spec.access, browserLive);
|
|
67533
|
+
const surfaces = live.surfaces();
|
|
66910
67534
|
return {
|
|
66911
67535
|
browserTools: browser?.tools ?? [],
|
|
66912
|
-
|
|
67536
|
+
/** As they are now: a browser that has stopped can't be opened, and says so. */
|
|
67537
|
+
get surfaces() {
|
|
67538
|
+
return surfaces.map(
|
|
67539
|
+
(one) => one.surface === "browser" && browserDown ? {
|
|
67540
|
+
...one,
|
|
67541
|
+
unavailable: "Its browser has stopped. Stop the computer and start it again."
|
|
67542
|
+
} : one
|
|
67543
|
+
);
|
|
67544
|
+
},
|
|
66913
67545
|
openLive: async (surface, mode, request, socket, head) => live.open(surface, mode, request, socket, head),
|
|
66914
67546
|
handle: async (endpoint, request, response) => {
|
|
66915
67547
|
if (endpoint === "computer") return exec.handle(request, response);
|
|
@@ -66944,8 +67576,8 @@ async function serveInside(options) {
|
|
|
66944
67576
|
log: (line) => process.stdout.write(`${line}
|
|
66945
67577
|
`),
|
|
66946
67578
|
// The service sees the container end, and shows the computer as failed.
|
|
66947
|
-
failed: (
|
|
66948
|
-
process.stderr.write(`${
|
|
67579
|
+
failed: (reason3) => {
|
|
67580
|
+
process.stderr.write(`${reason3}
|
|
66949
67581
|
`);
|
|
66950
67582
|
process.exit(1);
|
|
66951
67583
|
}
|
|
@@ -66974,7 +67606,7 @@ async function serveInside(options) {
|
|
|
66974
67606
|
socket.on("error", () => socket.destroy());
|
|
66975
67607
|
const given = /^Bearer (\S+)$/.exec(request.headers.authorization ?? "")?.[1];
|
|
66976
67608
|
const url3 = new URL(request.url ?? "/", "http://localhost");
|
|
66977
|
-
const surface = /^\/live\/(
|
|
67609
|
+
const surface = /^\/live\/(terminal|files|browser)$/.exec(url3.pathname)?.[1];
|
|
66978
67610
|
const mode = url3.searchParams.get("mode");
|
|
66979
67611
|
if (!given || !sameToken(given, digest) || !surface || mode !== "watch" && mode !== "operate")
|
|
66980
67612
|
return void socket.end("HTTP/1.1 401 Unauthorized\r\nConnection: close\r\n\r\n");
|
|
@@ -66989,7 +67621,7 @@ async function serveInside(options) {
|
|
|
66989
67621
|
}
|
|
66990
67622
|
|
|
66991
67623
|
// src/container.ts
|
|
66992
|
-
var
|
|
67624
|
+
var run = promisify(execFile);
|
|
66993
67625
|
var START_MS2 = 6e4;
|
|
66994
67626
|
var STOP_SECONDS = "5";
|
|
66995
67627
|
var NOT_FORWARDED = /* @__PURE__ */ new Set([
|
|
@@ -67003,7 +67635,7 @@ var NOT_FORWARDED = /* @__PURE__ */ new Set([
|
|
|
67003
67635
|
]);
|
|
67004
67636
|
async function docker(args, env = process.env) {
|
|
67005
67637
|
try {
|
|
67006
|
-
return (await
|
|
67638
|
+
return (await run("docker", args, { env, maxBuffer: 16 * 1024 * 1024 })).stdout.trim();
|
|
67007
67639
|
} catch (error62) {
|
|
67008
67640
|
const { stderr, message } = error62;
|
|
67009
67641
|
throw new Error(stderr?.trim() || message);
|
|
@@ -67018,7 +67650,7 @@ var UPGRADE_HEADERS = [
|
|
|
67018
67650
|
"sec-websocket-protocol"
|
|
67019
67651
|
];
|
|
67020
67652
|
var nameOf = (id) => `agentcomputer-${id}`;
|
|
67021
|
-
var
|
|
67653
|
+
var sleep5 = (ms) => new Promise((done) => setTimeout(done, ms));
|
|
67022
67654
|
var ContainerRuntime = class {
|
|
67023
67655
|
isolation = "container";
|
|
67024
67656
|
#image;
|
|
@@ -67130,7 +67762,16 @@ var ContainerRuntime = class {
|
|
|
67130
67762
|
throw error62;
|
|
67131
67763
|
}
|
|
67132
67764
|
return {
|
|
67133
|
-
|
|
67765
|
+
browserTools: ready.browserTools,
|
|
67766
|
+
/** As they are now: nothing of a container that has stopped can be opened. */
|
|
67767
|
+
get surfaces() {
|
|
67768
|
+
return ready.surfaces.map(
|
|
67769
|
+
(one) => ended ? {
|
|
67770
|
+
...one,
|
|
67771
|
+
unavailable: "Its container has stopped. Stop the computer and start it again."
|
|
67772
|
+
} : one
|
|
67773
|
+
);
|
|
67774
|
+
},
|
|
67134
67775
|
stop,
|
|
67135
67776
|
openLive: async (surface, mode, request, socket, head) => pipeUpgrade(
|
|
67136
67777
|
Number(port),
|
|
@@ -67155,7 +67796,7 @@ var ContainerRuntime = class {
|
|
|
67155
67796
|
if (answer.ok) return await answer.json();
|
|
67156
67797
|
} catch {
|
|
67157
67798
|
}
|
|
67158
|
-
await
|
|
67799
|
+
await sleep5(100);
|
|
67159
67800
|
}
|
|
67160
67801
|
throw new Error("Its container didn't start. Its logs say why.");
|
|
67161
67802
|
}
|
|
@@ -67212,7 +67853,7 @@ async function forward(url3, authorization, request, response) {
|
|
|
67212
67853
|
}
|
|
67213
67854
|
|
|
67214
67855
|
// src/handlers.ts
|
|
67215
|
-
var VERSION2 = "0.0.
|
|
67856
|
+
var VERSION2 = "0.0.9";
|
|
67216
67857
|
function createHandlers(computers) {
|
|
67217
67858
|
return {
|
|
67218
67859
|
getInfo: async () => ({ version: VERSION2, isolation: computers.isolation }),
|
|
@@ -67761,9 +68402,9 @@ var Endpoint = class {
|
|
|
67761
68402
|
async #call(name, args) {
|
|
67762
68403
|
const client = await this.#connected();
|
|
67763
68404
|
const result = await client.callTool({ name, arguments: args });
|
|
67764
|
-
const
|
|
67765
|
-
if (result.isError) throw new ComputerError(400,
|
|
67766
|
-
return { text:
|
|
68405
|
+
const text3 = result.content.map((block) => block.text ?? "").join("");
|
|
68406
|
+
if (result.isError) throw new ComputerError(400, text3);
|
|
68407
|
+
return { text: text3, structured: result.structuredContent };
|
|
67767
68408
|
}
|
|
67768
68409
|
async text(name, args) {
|
|
67769
68410
|
return (await this.#call(name, args)).text;
|
|
@@ -67794,32 +68435,30 @@ async function openLive(session, onMessage) {
|
|
|
67794
68435
|
ws.addEventListener("message", (event) => onMessage(event.data));
|
|
67795
68436
|
return { ws, closed };
|
|
67796
68437
|
}
|
|
67797
|
-
var
|
|
67798
|
-
var RESIZE2 = 49;
|
|
67799
|
-
var Pty = class {
|
|
68438
|
+
var Terminals = class {
|
|
67800
68439
|
#open;
|
|
67801
|
-
constructor(
|
|
67802
|
-
this.#open =
|
|
68440
|
+
constructor(open4) {
|
|
68441
|
+
this.#open = open4;
|
|
67803
68442
|
}
|
|
67804
|
-
/**
|
|
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
|
+
*/
|
|
67805
68447
|
async connect(opts) {
|
|
67806
|
-
const
|
|
67807
|
-
|
|
67808
|
-
|
|
67809
|
-
|
|
67810
|
-
|
|
67811
|
-
|
|
67812
|
-
|
|
67813
|
-
const send2 = (type, text2) => {
|
|
67814
|
-
const body = new TextEncoder().encode(text2);
|
|
67815
|
-
const frame = new Uint8Array(body.length + 1);
|
|
67816
|
-
frame[0] = type;
|
|
67817
|
-
frame.set(body, 1);
|
|
67818
|
-
ws.send(frame);
|
|
67819
|
-
};
|
|
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));
|
|
67820
68455
|
return {
|
|
67821
|
-
|
|
67822
|
-
|
|
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 }),
|
|
67823
68462
|
close: () => ws.close(),
|
|
67824
68463
|
closed
|
|
67825
68464
|
};
|
|
@@ -67827,17 +68466,8 @@ var Pty = class {
|
|
|
67827
68466
|
};
|
|
67828
68467
|
var Commands = class {
|
|
67829
68468
|
#endpoint;
|
|
67830
|
-
|
|
67831
|
-
constructor(endpoint, open3) {
|
|
68469
|
+
constructor(endpoint) {
|
|
67832
68470
|
this.#endpoint = endpoint;
|
|
67833
|
-
this.#open = open3;
|
|
67834
|
-
}
|
|
67835
|
-
/** Each of the agent's commands as it runs: the ones already heard first, then as they come. */
|
|
67836
|
-
async watch(onEvent) {
|
|
67837
|
-
const { ws, closed } = await openLive(await this.#open("commands", "watch"), (data) => {
|
|
67838
|
-
if (typeof data === "string") onEvent(JSON.parse(data));
|
|
67839
|
-
});
|
|
67840
|
-
return { close: () => ws.close(), closed };
|
|
67841
68471
|
}
|
|
67842
68472
|
async run(cmd, opts = {}) {
|
|
67843
68473
|
let answer = await this.#endpoint.command("exec_command", {
|
|
@@ -67897,16 +68527,16 @@ var Files = class {
|
|
|
67897
68527
|
}
|
|
67898
68528
|
/** A folder's entries, the first thousand. */
|
|
67899
68529
|
async list(path = ".") {
|
|
67900
|
-
const
|
|
67901
|
-
return
|
|
68530
|
+
const text3 = await this.#endpoint.text("list", { path });
|
|
68531
|
+
return text3.split("\n").filter((line) => line.endsWith("/") || line.includes(" ")).map((line) => {
|
|
67902
68532
|
const [name = "", size] = line.split(" ");
|
|
67903
68533
|
return name.endsWith("/") ? { name: name.slice(0, -1), type: "dir" } : { name, type: "file", size: Number(size) };
|
|
67904
68534
|
});
|
|
67905
68535
|
}
|
|
67906
68536
|
/** Lines matching text under a folder, as `path:line:text`. */
|
|
67907
68537
|
async search(pattern, opts = {}) {
|
|
67908
|
-
const
|
|
67909
|
-
return
|
|
68538
|
+
const text3 = await this.#endpoint.text("search", { pattern, ...opts });
|
|
68539
|
+
return text3 ? text3.split("\n") : [];
|
|
67910
68540
|
}
|
|
67911
68541
|
};
|
|
67912
68542
|
var pathOf = (computerID) => ({ params: { path: { computerID } } });
|
|
@@ -67914,7 +68544,7 @@ var Computer = class _Computer {
|
|
|
67914
68544
|
computerID;
|
|
67915
68545
|
commands;
|
|
67916
68546
|
files;
|
|
67917
|
-
|
|
68547
|
+
terminals;
|
|
67918
68548
|
#connection;
|
|
67919
68549
|
#token;
|
|
67920
68550
|
#computer;
|
|
@@ -67925,9 +68555,8 @@ var Computer = class _Computer {
|
|
|
67925
68555
|
this.#token = token;
|
|
67926
68556
|
this.#urls = info.endpoints;
|
|
67927
68557
|
this.#computer = new Endpoint(info.endpoints.computer, token);
|
|
67928
|
-
|
|
67929
|
-
this.
|
|
67930
|
-
this.pty = new Pty(open3);
|
|
68558
|
+
this.commands = new Commands(this.#computer);
|
|
68559
|
+
this.terminals = new Terminals((surface, mode) => this.liveSession(surface, mode));
|
|
67931
68560
|
this.files = new Files(this.#computer);
|
|
67932
68561
|
}
|
|
67933
68562
|
static async create(opts) {
|
|
@@ -71509,7 +72138,7 @@ var REQUEST_STATE_ONLY_LEG_PACING_MS3 = 250;
|
|
|
71509
72138
|
function inputRequiredRoundsExceededMessage3(method, maxRounds) {
|
|
71510
72139
|
return `Multi-round-trip request '${method}' still required input after ${maxRounds} rounds (inputRequired.maxRounds)`;
|
|
71511
72140
|
}
|
|
71512
|
-
function
|
|
72141
|
+
function sleep6(ms, signal) {
|
|
71513
72142
|
return new Promise((resolve6, reject) => {
|
|
71514
72143
|
if (signal?.aborted) {
|
|
71515
72144
|
reject(signal.reason instanceof SdkError3 ? signal.reason : new SdkError3(SdkErrorCode3.RequestTimeout, String(signal.reason)));
|
|
@@ -71533,7 +72162,7 @@ function linkedRoundAbort3(outer) {
|
|
|
71533
72162
|
if (outer?.aborted) controller.abort(outer.reason);
|
|
71534
72163
|
return {
|
|
71535
72164
|
signal: controller.signal,
|
|
71536
|
-
abort: (
|
|
72165
|
+
abort: (reason3) => controller.abort(reason3),
|
|
71537
72166
|
dispose: () => outer?.removeEventListener("abort", onOuterAbort)
|
|
71538
72167
|
};
|
|
71539
72168
|
}
|
|
@@ -72328,8 +72957,8 @@ var Protocol3 = class {
|
|
|
72328
72957
|
return;
|
|
72329
72958
|
}
|
|
72330
72959
|
if (options?.signal?.aborted) {
|
|
72331
|
-
const
|
|
72332
|
-
throw
|
|
72960
|
+
const reason3 = options.signal.reason;
|
|
72961
|
+
throw reason3 instanceof SdkError3 ? reason3 : new SdkError3(SdkErrorCode3.RequestTimeout, String(reason3));
|
|
72333
72962
|
}
|
|
72334
72963
|
const requestAbort = codec2.era === MODERN_WIRE_REVISION3 && this._transport.hasPerRequestStream === true ? new AbortController() : void 0;
|
|
72335
72964
|
const messageId = this._requestMessageId++;
|
|
@@ -72351,7 +72980,7 @@ var Protocol3 = class {
|
|
|
72351
72980
|
}
|
|
72352
72981
|
const outbound = this._envelopeOutbound(jsonrpcRequest);
|
|
72353
72982
|
let responseReceived = false;
|
|
72354
|
-
const cancel = (
|
|
72983
|
+
const cancel = (reason3) => {
|
|
72355
72984
|
if (responseReceived) return;
|
|
72356
72985
|
this._progressHandlers.delete(messageId);
|
|
72357
72986
|
if (requestAbort === void 0) {
|
|
@@ -72360,7 +72989,7 @@ var Protocol3 = class {
|
|
|
72360
72989
|
method: "notifications/cancelled",
|
|
72361
72990
|
params: {
|
|
72362
72991
|
requestId: messageId,
|
|
72363
|
-
reason: String(
|
|
72992
|
+
reason: String(reason3)
|
|
72364
72993
|
}
|
|
72365
72994
|
}), {
|
|
72366
72995
|
relatedRequestId,
|
|
@@ -72368,7 +72997,7 @@ var Protocol3 = class {
|
|
|
72368
72997
|
onresumptiontoken
|
|
72369
72998
|
}).catch((error62) => this._onerror(/* @__PURE__ */ new Error(`Failed to send cancellation: ${error62}`)));
|
|
72370
72999
|
} else requestAbort.abort();
|
|
72371
|
-
reject(
|
|
73000
|
+
reject(reason3 instanceof SdkError3 ? reason3 : new SdkError3(SdkErrorCode3.RequestTimeout, String(reason3)));
|
|
72372
73001
|
};
|
|
72373
73002
|
this._responseHandlers.set(messageId, (response) => {
|
|
72374
73003
|
if (options?.signal?.aborted) return;
|
|
@@ -75482,8 +76111,8 @@ var require_utils3 = /* @__PURE__ */ __commonJSMin3(((exports, module) => {
|
|
|
75482
76111
|
}
|
|
75483
76112
|
if (bestLength < 2) return hextets.join(":");
|
|
75484
76113
|
const head = hextets.slice(0, bestStart).join(":");
|
|
75485
|
-
const
|
|
75486
|
-
return head + "::" +
|
|
76114
|
+
const tail2 = hextets.slice(bestStart + bestLength).join(":");
|
|
76115
|
+
return head + "::" + tail2;
|
|
75487
76116
|
}
|
|
75488
76117
|
function normalizeIPv6Address(input2) {
|
|
75489
76118
|
const compression = input2.indexOf("::");
|
|
@@ -76706,7 +77335,7 @@ var require_core$33 = /* @__PURE__ */ __commonJSMin3(((exports) => {
|
|
|
76706
77335
|
}
|
|
76707
77336
|
errorsText(errors = this.errors, { separator = ", ", dataVar = "data" } = {}) {
|
|
76708
77337
|
if (!errors || errors.length === 0) return "No errors";
|
|
76709
|
-
return errors.map((e) => `${dataVar}${e.instancePath} ${e.message}`).reduce((
|
|
77338
|
+
return errors.map((e) => `${dataVar}${e.instancePath} ${e.message}`).reduce((text3, msg) => text3 + separator + msg);
|
|
76710
77339
|
}
|
|
76711
77340
|
$dataMetaSchema(metaSchema, keywordsJsonPointers) {
|
|
76712
77341
|
const rules = this.RULES.all;
|
|
@@ -80506,7 +81135,7 @@ var LegacyInputRequiredShim2 = class {
|
|
|
80506
81135
|
} finally {
|
|
80507
81136
|
roundAbort.dispose();
|
|
80508
81137
|
}
|
|
80509
|
-
} else await
|
|
81138
|
+
} else await sleep6(REQUEST_STATE_ONLY_LEG_PACING_MS3, outerSignal);
|
|
80510
81139
|
let ctxNext = {
|
|
80511
81140
|
...ctx,
|
|
80512
81141
|
mcpReq: {
|
|
@@ -81852,21 +82481,21 @@ async function readRequestBody2(request, maxBytes = DEFAULT_MAX_REQUEST_BODY_SIZ
|
|
|
81852
82481
|
const reader = request.body.getReader();
|
|
81853
82482
|
const decoder = new TextDecoder();
|
|
81854
82483
|
let received = 0;
|
|
81855
|
-
let
|
|
82484
|
+
let text3 = "";
|
|
81856
82485
|
try {
|
|
81857
82486
|
for (; ; ) {
|
|
81858
82487
|
const { done, value } = await reader.read();
|
|
81859
82488
|
if (done) break;
|
|
81860
82489
|
received += value.byteLength;
|
|
81861
82490
|
if (received > maxBytes) return { tooLarge: true };
|
|
81862
|
-
|
|
82491
|
+
text3 += decoder.decode(value, { stream: true });
|
|
81863
82492
|
}
|
|
81864
82493
|
} finally {
|
|
81865
82494
|
reader.releaseLock();
|
|
81866
82495
|
}
|
|
81867
82496
|
return {
|
|
81868
82497
|
tooLarge: false,
|
|
81869
|
-
text:
|
|
82498
|
+
text: text3 + decoder.decode()
|
|
81870
82499
|
};
|
|
81871
82500
|
}
|
|
81872
82501
|
var WebStandardStreamableHTTPServerTransport2 = class {
|
|
@@ -81960,9 +82589,9 @@ var WebStandardStreamableHTTPServerTransport2 = class {
|
|
|
81960
82589
|
}
|
|
81961
82590
|
});
|
|
81962
82591
|
}
|
|
81963
|
-
async _checkScopeChallenge(
|
|
82592
|
+
async _checkScopeChallenge(messages2, authInfo) {
|
|
81964
82593
|
if (!this._scopeChallengeResolver) return;
|
|
81965
|
-
const challenge = await findScopeChallenge2(
|
|
82594
|
+
const challenge = await findScopeChallenge2(messages2.filter((message) => isJSONRPCRequest3(message)), authInfo, this._scopeChallengeResolver);
|
|
81966
82595
|
return challenge === void 0 ? void 0 : createScopeChallengeResponse2(challenge, scopeChallengeResourceMetadataUrl2(authInfo));
|
|
81967
82596
|
}
|
|
81968
82597
|
/**
|
|
@@ -82260,21 +82889,21 @@ data:
|
|
|
82260
82889
|
this.onerror?.(/* @__PURE__ */ new Error(`Invalid Request: Batch must not exceed ${MAX_BATCH_SIZE2} messages`));
|
|
82261
82890
|
return this.createJsonErrorResponse(400, -32600, `Invalid Request: Batch must not exceed ${MAX_BATCH_SIZE2} messages`);
|
|
82262
82891
|
}
|
|
82263
|
-
let
|
|
82892
|
+
let messages2;
|
|
82264
82893
|
try {
|
|
82265
|
-
|
|
82894
|
+
messages2 = Array.isArray(rawMessage) ? rawMessage.map((msg) => JSONRPCMessageSchema.parse(msg)) : [JSONRPCMessageSchema.parse(rawMessage)];
|
|
82266
82895
|
} catch (error62) {
|
|
82267
82896
|
this.onerror?.(error62);
|
|
82268
82897
|
return this.createJsonErrorResponse(400, -32700, "Parse error: Invalid JSON-RPC message");
|
|
82269
82898
|
}
|
|
82270
82899
|
if (this._closed) return this.createJsonErrorResponse(404, -32001, "Session not found");
|
|
82271
|
-
const isInitializationRequest =
|
|
82900
|
+
const isInitializationRequest = messages2.some((element) => isInitializeRequest3(element));
|
|
82272
82901
|
if (isInitializationRequest) {
|
|
82273
82902
|
if (this._initialized && this.sessionId !== void 0) {
|
|
82274
82903
|
this.onerror?.(/* @__PURE__ */ new Error("Invalid Request: Server already initialized"));
|
|
82275
82904
|
return this.createJsonErrorResponse(400, -32600, "Invalid Request: Server already initialized");
|
|
82276
82905
|
}
|
|
82277
|
-
if (
|
|
82906
|
+
if (messages2.length > 1) {
|
|
82278
82907
|
this.onerror?.(/* @__PURE__ */ new Error("Invalid Request: Only one initialization request is allowed"));
|
|
82279
82908
|
return this.createJsonErrorResponse(400, -32600, "Invalid Request: Only one initialization request is allowed");
|
|
82280
82909
|
}
|
|
@@ -82291,21 +82920,21 @@ data:
|
|
|
82291
82920
|
if (this._closed) return this.createJsonErrorResponse(404, -32001, "Session not found");
|
|
82292
82921
|
let scopeChallengeResponse;
|
|
82293
82922
|
try {
|
|
82294
|
-
scopeChallengeResponse = await this._checkScopeChallenge(
|
|
82923
|
+
scopeChallengeResponse = await this._checkScopeChallenge(messages2, options?.authInfo);
|
|
82295
82924
|
} catch (error62) {
|
|
82296
82925
|
this.onerror?.(error62);
|
|
82297
82926
|
return this.createJsonErrorResponse(500, -32603, "Internal server error");
|
|
82298
82927
|
}
|
|
82299
82928
|
if (scopeChallengeResponse) return scopeChallengeResponse;
|
|
82300
|
-
if (!
|
|
82301
|
-
for (const message of
|
|
82929
|
+
if (!messages2.some((element) => isJSONRPCRequest3(element))) {
|
|
82930
|
+
for (const message of messages2) this.onmessage?.(message, {
|
|
82302
82931
|
authInfo: options?.authInfo,
|
|
82303
82932
|
request
|
|
82304
82933
|
});
|
|
82305
82934
|
return new Response(null, { status: 202 });
|
|
82306
82935
|
}
|
|
82307
82936
|
const streamId = crypto.randomUUID();
|
|
82308
|
-
const initRequest =
|
|
82937
|
+
const initRequest = messages2.find((m) => isInitializeRequest3(m));
|
|
82309
82938
|
const clientProtocolVersion = initRequest ? initRequest.params.protocolVersion : req.headers.get("mcp-protocol-version") ?? DEFAULT_NEGOTIATED_PROTOCOL_VERSION;
|
|
82310
82939
|
if (this._enableJsonResponse) return new Promise((resolve6) => {
|
|
82311
82940
|
this._streamMapping.set(streamId, {
|
|
@@ -82314,8 +82943,8 @@ data:
|
|
|
82314
82943
|
this._streamMapping.delete(streamId);
|
|
82315
82944
|
}
|
|
82316
82945
|
});
|
|
82317
|
-
for (const message of
|
|
82318
|
-
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, {
|
|
82319
82948
|
authInfo: options?.authInfo,
|
|
82320
82949
|
request
|
|
82321
82950
|
});
|
|
@@ -82339,7 +82968,7 @@ data:
|
|
|
82339
82968
|
"X-Accel-Buffering": "no"
|
|
82340
82969
|
};
|
|
82341
82970
|
if (this.sessionId !== void 0) headers["mcp-session-id"] = this.sessionId;
|
|
82342
|
-
for (const message of
|
|
82971
|
+
for (const message of messages2) if (isJSONRPCRequest3(message)) {
|
|
82343
82972
|
this._streamMapping.set(streamId, {
|
|
82344
82973
|
controller: streamController,
|
|
82345
82974
|
encoder,
|
|
@@ -82355,7 +82984,7 @@ data:
|
|
|
82355
82984
|
this._requestToStreamMapping.set(message.id, streamId);
|
|
82356
82985
|
}
|
|
82357
82986
|
await this.writePrimingEvent(streamController, encoder, streamId, clientProtocolVersion);
|
|
82358
|
-
for (const message of
|
|
82987
|
+
for (const message of messages2) {
|
|
82359
82988
|
let closeSSEStream;
|
|
82360
82989
|
let closeStandaloneSSEStream;
|
|
82361
82990
|
if (isJSONRPCRequest3(message) && this._eventStore && this.supportsEmptySSEData(clientProtocolVersion)) {
|
|
@@ -82950,8 +83579,8 @@ var readRawBodyIfAvailable2 = (request) => {
|
|
|
82950
83579
|
};
|
|
82951
83580
|
var normalizeAbortError2 = (request, incoming) => {
|
|
82952
83581
|
if (incoming.errored) return incoming.errored;
|
|
82953
|
-
const
|
|
82954
|
-
if (
|
|
83582
|
+
const reason3 = request[abortReasonKey2];
|
|
83583
|
+
if (reason3 !== void 0) return reason3 instanceof Error ? reason3 : new Error(String(reason3));
|
|
82955
83584
|
return /* @__PURE__ */ new Error("Client connection prematurely closed.");
|
|
82956
83585
|
};
|
|
82957
83586
|
var readBodyDirect2 = (request) => {
|
|
@@ -83045,10 +83674,10 @@ var requestPrototype2 = {
|
|
|
83045
83674
|
get headers() {
|
|
83046
83675
|
return this[headersKey2] ||= newHeadersFromIncoming2(this[incomingKey2]);
|
|
83047
83676
|
},
|
|
83048
|
-
[abortRequest2](
|
|
83049
|
-
if (this[abortReasonKey2] === void 0) this[abortReasonKey2] =
|
|
83677
|
+
[abortRequest2](reason3) {
|
|
83678
|
+
if (this[abortReasonKey2] === void 0) this[abortReasonKey2] = reason3;
|
|
83050
83679
|
const abortController = this[abortControllerKey2];
|
|
83051
|
-
if (abortController && !abortController.signal.aborted) abortController.abort(
|
|
83680
|
+
if (abortController && !abortController.signal.aborted) abortController.abort(reason3);
|
|
83052
83681
|
},
|
|
83053
83682
|
[getAbortController2]() {
|
|
83054
83683
|
this[abortControllerKey2] ||= new AbortController();
|
|
@@ -83692,8 +84321,8 @@ var upgradeWebSocket2 = defineWebSocketHelper2(async (c, events, options) => {
|
|
|
83692
84321
|
ws.on("message", bufferMessage);
|
|
83693
84322
|
const ctx = {
|
|
83694
84323
|
binaryType: "arraybuffer",
|
|
83695
|
-
close(code,
|
|
83696
|
-
ws.close(code,
|
|
84324
|
+
close(code, reason3) {
|
|
84325
|
+
ws.close(code, reason3);
|
|
83697
84326
|
},
|
|
83698
84327
|
protocol: ws.protocol,
|
|
83699
84328
|
raw: ws,
|
|
@@ -83723,11 +84352,11 @@ var upgradeWebSocket2 = defineWebSocketHelper2(async (c, events, options) => {
|
|
|
83723
84352
|
ws.on("message", (data, isBinary) => {
|
|
83724
84353
|
handleMessage(data, isBinary);
|
|
83725
84354
|
});
|
|
83726
|
-
ws.on("close", (code,
|
|
84355
|
+
ws.on("close", (code, reason3) => {
|
|
83727
84356
|
try {
|
|
83728
84357
|
events?.onClose?.(new CloseEvent2("close", {
|
|
83729
84358
|
code,
|
|
83730
|
-
reason:
|
|
84359
|
+
reason: reason3.toString()
|
|
83731
84360
|
}), ctx);
|
|
83732
84361
|
} catch (e) {
|
|
83733
84362
|
(options?.onError ?? console.error)(e);
|
|
@@ -83862,13 +84491,13 @@ var NodeStreamableHTTPServerTransport2 = class {
|
|
|
83862
84491
|
};
|
|
83863
84492
|
|
|
83864
84493
|
// src/computers.ts
|
|
83865
|
-
import { randomBytes as
|
|
84494
|
+
import { randomBytes as randomBytes5 } from "node:crypto";
|
|
83866
84495
|
import { appendFileSync, existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync as readFileSync4, rmSync as rmSync2, statSync as statSync3 } from "node:fs";
|
|
83867
84496
|
import { isAbsolute, join as join9, resolve as resolve4 } from "node:path";
|
|
83868
84497
|
var TOOLS = {
|
|
83869
84498
|
read: ["read", "list", "search"],
|
|
83870
84499
|
files: ["read", "list", "search", "edit"],
|
|
83871
|
-
terminal: ["read", "list", "search", "edit", "exec_command", "write_stdin"]
|
|
84500
|
+
terminal: ["read", "list", "search", "edit", "exec_command", "write_stdin", "list_sessions"]
|
|
83872
84501
|
};
|
|
83873
84502
|
var MAX_LOG = 256 * 1024;
|
|
83874
84503
|
var MAX_TIMER_S = 2e6;
|
|
@@ -83924,7 +84553,7 @@ var Computers = class {
|
|
|
83924
84553
|
if (!folder) throw new ApiError(400, `There's no folder at ${input2.workspace}.`);
|
|
83925
84554
|
workspace = resolve4(input2.workspace);
|
|
83926
84555
|
}
|
|
83927
|
-
const id = `c_${
|
|
84556
|
+
const id = `c_${randomBytes5(9).toString("hex")}`;
|
|
83928
84557
|
const token = newToken();
|
|
83929
84558
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
83930
84559
|
const stored = {
|
|
@@ -84023,12 +84652,12 @@ var Computers = class {
|
|
|
84023
84652
|
}
|
|
84024
84653
|
logs(id) {
|
|
84025
84654
|
this.#found(id);
|
|
84026
|
-
let
|
|
84655
|
+
let text3 = "";
|
|
84027
84656
|
try {
|
|
84028
|
-
|
|
84657
|
+
text3 = readFileSync4(this.#logFile(id), "utf8");
|
|
84029
84658
|
} catch {
|
|
84030
84659
|
}
|
|
84031
|
-
return
|
|
84660
|
+
return text3.split("\n").filter(Boolean).map((entry) => ({
|
|
84032
84661
|
timestamp: entry.slice(0, entry.indexOf(" ")),
|
|
84033
84662
|
line: entry.slice(entry.indexOf(" ") + 1)
|
|
84034
84663
|
}));
|
|
@@ -84060,7 +84689,7 @@ var Computers = class {
|
|
|
84060
84689
|
const now = Date.now();
|
|
84061
84690
|
for (const [key, ticket2] of this.#tickets)
|
|
84062
84691
|
if (ticket2.expiresAt <= now) this.#tickets.delete(key);
|
|
84063
|
-
const ticket = `l_${
|
|
84692
|
+
const ticket = `l_${randomBytes5(24).toString("base64url")}`;
|
|
84064
84693
|
const expiresAt = now + TICKET_MS;
|
|
84065
84694
|
this.#tickets.set(ticket, { computerID: id, surface, mode, expiresAt });
|
|
84066
84695
|
return {
|
|
@@ -84144,9 +84773,9 @@ var Computers = class {
|
|
|
84144
84773
|
this.#folder(stored.id),
|
|
84145
84774
|
{
|
|
84146
84775
|
log: (line) => this.#log(stored.id, line),
|
|
84147
|
-
failed: (
|
|
84776
|
+
failed: (reason3) => {
|
|
84148
84777
|
live.failed = true;
|
|
84149
|
-
this.#log(stored.id,
|
|
84778
|
+
this.#log(stored.id, reason3);
|
|
84150
84779
|
}
|
|
84151
84780
|
}
|
|
84152
84781
|
);
|
|
@@ -84187,7 +84816,7 @@ var OPENAPI = {
|
|
|
84187
84816
|
"openapi": "3.1.0",
|
|
84188
84817
|
"info": {
|
|
84189
84818
|
"title": "AgentComputer",
|
|
84190
|
-
"version": "0.0.
|
|
84819
|
+
"version": "0.0.9",
|
|
84191
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.",
|
|
84192
84821
|
"license": {
|
|
84193
84822
|
"name": "Apache-2.0",
|
|
@@ -84508,7 +85137,7 @@ var OPENAPI = {
|
|
|
84508
85137
|
"post": {
|
|
84509
85138
|
"operationId": "createLiveSession",
|
|
84510
85139
|
"x-mcp-tool": "computer_live",
|
|
84511
|
-
"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.",
|
|
84512
85141
|
"requestBody": {
|
|
84513
85142
|
"required": true,
|
|
84514
85143
|
"content": {
|
|
@@ -84639,7 +85268,7 @@ var OPENAPI = {
|
|
|
84639
85268
|
"files",
|
|
84640
85269
|
"terminal"
|
|
84641
85270
|
],
|
|
84642
|
-
"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."
|
|
84643
85272
|
},
|
|
84644
85273
|
"ComputerState": {
|
|
84645
85274
|
"type": "string",
|
|
@@ -84847,11 +85476,11 @@ var OPENAPI = {
|
|
|
84847
85476
|
"Surface": {
|
|
84848
85477
|
"type": "string",
|
|
84849
85478
|
"enum": [
|
|
84850
|
-
"commands",
|
|
84851
85479
|
"terminal",
|
|
85480
|
+
"files",
|
|
84852
85481
|
"browser"
|
|
84853
85482
|
],
|
|
84854
|
-
"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."
|
|
84855
85484
|
},
|
|
84856
85485
|
"LiveMode": {
|
|
84857
85486
|
"type": "string",
|
|
@@ -84859,7 +85488,7 @@ var OPENAPI = {
|
|
|
84859
85488
|
"watch",
|
|
84860
85489
|
"operate"
|
|
84861
85490
|
],
|
|
84862
|
-
"description": "In watch
|
|
85491
|
+
"description": "In watch you see and don't act: what you send is dropped, except that a files socket may still ask to list, to read and for what changed."
|
|
84863
85492
|
},
|
|
84864
85493
|
"SurfaceInfo": {
|
|
84865
85494
|
"type": "object",
|
|
@@ -84882,8 +85511,7 @@ var OPENAPI = {
|
|
|
84882
85511
|
},
|
|
84883
85512
|
"unavailable": {
|
|
84884
85513
|
"type": "string",
|
|
84885
|
-
"description": "Why it can't be opened
|
|
84886
|
-
"if it can't.": null
|
|
85514
|
+
"description": "Why it can't be opened now, if it can't."
|
|
84887
85515
|
}
|
|
84888
85516
|
}
|
|
84889
85517
|
},
|
|
@@ -84942,7 +85570,7 @@ var OPENAPI = {
|
|
|
84942
85570
|
"oneOf": [
|
|
84943
85571
|
{
|
|
84944
85572
|
"type": "object",
|
|
84945
|
-
"description": "The open tabs, and which one
|
|
85573
|
+
"description": "The open tabs, and which one this socket's frames are of, sent again whenever a tab opens, closes, navigates or changes its title, or the socket is shown another.",
|
|
84946
85574
|
"required": [
|
|
84947
85575
|
"type",
|
|
84948
85576
|
"tabs",
|
|
@@ -85090,30 +85718,6 @@ var OPENAPI = {
|
|
|
85090
85718
|
}
|
|
85091
85719
|
}
|
|
85092
85720
|
}
|
|
85093
|
-
},
|
|
85094
|
-
{
|
|
85095
|
-
"type": "object",
|
|
85096
|
-
"description": "Who holds the browser, and whether the agent is waiting for it.",
|
|
85097
|
-
"required": [
|
|
85098
|
-
"type",
|
|
85099
|
-
"you",
|
|
85100
|
-
"held",
|
|
85101
|
-
"agentWaiting"
|
|
85102
|
-
],
|
|
85103
|
-
"properties": {
|
|
85104
|
-
"type": {
|
|
85105
|
-
"const": "driver"
|
|
85106
|
-
},
|
|
85107
|
-
"you": {
|
|
85108
|
-
"type": "boolean"
|
|
85109
|
-
},
|
|
85110
|
-
"held": {
|
|
85111
|
-
"type": "boolean"
|
|
85112
|
-
},
|
|
85113
|
-
"agentWaiting": {
|
|
85114
|
-
"type": "boolean"
|
|
85115
|
-
}
|
|
85116
|
-
}
|
|
85117
85721
|
}
|
|
85118
85722
|
]
|
|
85119
85723
|
},
|
|
@@ -85276,6 +85880,7 @@ var OPENAPI = {
|
|
|
85276
85880
|
},
|
|
85277
85881
|
{
|
|
85278
85882
|
"type": "object",
|
|
85883
|
+
"description": "Show this socket another tab, open one and show it, or close one. After show or open, the socket's page is its own and no longer follows new tabs.",
|
|
85279
85884
|
"required": [
|
|
85280
85885
|
"type",
|
|
85281
85886
|
"action"
|
|
@@ -85312,20 +85917,533 @@ var OPENAPI = {
|
|
|
85312
85917
|
},
|
|
85313
85918
|
"to": {
|
|
85314
85919
|
"type": "string",
|
|
85315
|
-
"description": "A URL"
|
|
85316
|
-
|
|
85317
|
-
|
|
85920
|
+
"description": "A URL, or back, forward or reload."
|
|
85921
|
+
}
|
|
85922
|
+
}
|
|
85923
|
+
}
|
|
85924
|
+
]
|
|
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
|
+
},
|
|
86138
|
+
"FilesRequest": {
|
|
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.",
|
|
86140
|
+
"oneOf": [
|
|
86141
|
+
{
|
|
86142
|
+
"type": "object",
|
|
86143
|
+
"description": "A folder's entries, folders first, the first thousand. There is no asking for the rest.",
|
|
86144
|
+
"required": [
|
|
86145
|
+
"type",
|
|
86146
|
+
"id"
|
|
86147
|
+
],
|
|
86148
|
+
"properties": {
|
|
86149
|
+
"type": {
|
|
86150
|
+
"const": "list"
|
|
86151
|
+
},
|
|
86152
|
+
"id": {
|
|
86153
|
+
"type": "string",
|
|
86154
|
+
"minLength": 1,
|
|
86155
|
+
"maxLength": 64
|
|
86156
|
+
},
|
|
86157
|
+
"path": {
|
|
86158
|
+
"type": "string",
|
|
86159
|
+
"description": "The workspace itself, if left out."
|
|
86160
|
+
}
|
|
86161
|
+
}
|
|
86162
|
+
},
|
|
86163
|
+
{
|
|
86164
|
+
"type": "object",
|
|
86165
|
+
"description": "Bytes of a file from an offset. The answer is a data message and then one binary message, the bytes: fewer than asked for at the file's end, and none past it.",
|
|
86166
|
+
"required": [
|
|
86167
|
+
"type",
|
|
86168
|
+
"id",
|
|
86169
|
+
"path",
|
|
86170
|
+
"offset",
|
|
86171
|
+
"length"
|
|
86172
|
+
],
|
|
86173
|
+
"properties": {
|
|
86174
|
+
"type": {
|
|
86175
|
+
"const": "read"
|
|
86176
|
+
},
|
|
86177
|
+
"id": {
|
|
86178
|
+
"type": "string",
|
|
86179
|
+
"minLength": 1,
|
|
86180
|
+
"maxLength": 64
|
|
86181
|
+
},
|
|
86182
|
+
"path": {
|
|
86183
|
+
"type": "string"
|
|
86184
|
+
},
|
|
86185
|
+
"offset": {
|
|
86186
|
+
"type": "integer",
|
|
86187
|
+
"minimum": 0
|
|
86188
|
+
},
|
|
86189
|
+
"length": {
|
|
86190
|
+
"type": "integer",
|
|
86191
|
+
"minimum": 1,
|
|
86192
|
+
"maximum": 1048576
|
|
86193
|
+
}
|
|
86194
|
+
}
|
|
86195
|
+
},
|
|
86196
|
+
{
|
|
86197
|
+
"type": "object",
|
|
86198
|
+
"description": "The files modified after a time, in whole milliseconds: the newest thousand, the newest first. It skips what .gitignore files ignore, .git, node_modules and links, and a file that was removed isn't one of them. Each one asked walks the workspace once, and a computer walks for one at a time. Nothing is walked that wasn't asked for.",
|
|
86199
|
+
"required": [
|
|
86200
|
+
"type",
|
|
86201
|
+
"id",
|
|
86202
|
+
"since"
|
|
86203
|
+
],
|
|
86204
|
+
"properties": {
|
|
86205
|
+
"type": {
|
|
86206
|
+
"const": "changed"
|
|
86207
|
+
},
|
|
86208
|
+
"id": {
|
|
86209
|
+
"type": "string",
|
|
86210
|
+
"minLength": 1,
|
|
86211
|
+
"maxLength": 64
|
|
86212
|
+
},
|
|
86213
|
+
"since": {
|
|
86214
|
+
"type": "string",
|
|
86215
|
+
"format": "date-time"
|
|
86216
|
+
}
|
|
86217
|
+
}
|
|
86218
|
+
},
|
|
86219
|
+
{
|
|
86220
|
+
"type": "object",
|
|
86221
|
+
"description": "The next part of a file, on an operate socket, followed by one binary message of exactly length bytes, with no message between them. The part at offset 0 starts the file, or starts it again, and each one after starts where the last ended. Nothing is at the path until the part marked last, which puts the file there whole, over any that was there, and is answered with written. Only that part, and a part that is refused, is answered. It makes no folders. A file not finished when the socket closes is given up.",
|
|
86222
|
+
"required": [
|
|
86223
|
+
"type",
|
|
86224
|
+
"id",
|
|
86225
|
+
"path",
|
|
86226
|
+
"offset",
|
|
86227
|
+
"length",
|
|
86228
|
+
"last"
|
|
86229
|
+
],
|
|
86230
|
+
"properties": {
|
|
86231
|
+
"type": {
|
|
86232
|
+
"const": "write"
|
|
86233
|
+
},
|
|
86234
|
+
"id": {
|
|
86235
|
+
"type": "string",
|
|
86236
|
+
"minLength": 1,
|
|
86237
|
+
"maxLength": 64
|
|
86238
|
+
},
|
|
86239
|
+
"path": {
|
|
86240
|
+
"type": "string"
|
|
86241
|
+
},
|
|
86242
|
+
"offset": {
|
|
86243
|
+
"type": "integer",
|
|
86244
|
+
"minimum": 0
|
|
86245
|
+
},
|
|
86246
|
+
"length": {
|
|
86247
|
+
"type": "integer",
|
|
86248
|
+
"minimum": 0,
|
|
86249
|
+
"maximum": 1048576
|
|
86250
|
+
},
|
|
86251
|
+
"last": {
|
|
86252
|
+
"type": "boolean"
|
|
86253
|
+
}
|
|
86254
|
+
}
|
|
86255
|
+
}
|
|
86256
|
+
]
|
|
86257
|
+
},
|
|
86258
|
+
"FilesAnswer": {
|
|
86259
|
+
"description": "What the service sends on files@1, as JSON text, each with the id it answers. Nothing comes between a data message and its bytes.",
|
|
86260
|
+
"oneOf": [
|
|
86261
|
+
{
|
|
86262
|
+
"type": "object",
|
|
86263
|
+
"required": [
|
|
86264
|
+
"type",
|
|
86265
|
+
"id",
|
|
86266
|
+
"path",
|
|
86267
|
+
"entries",
|
|
86268
|
+
"more"
|
|
86269
|
+
],
|
|
86270
|
+
"properties": {
|
|
86271
|
+
"type": {
|
|
86272
|
+
"const": "entries"
|
|
86273
|
+
},
|
|
86274
|
+
"id": {
|
|
86275
|
+
"type": "string"
|
|
86276
|
+
},
|
|
86277
|
+
"path": {
|
|
86278
|
+
"type": "string"
|
|
86279
|
+
},
|
|
86280
|
+
"entries": {
|
|
86281
|
+
"type": "array",
|
|
86282
|
+
"items": {
|
|
86283
|
+
"type": "object",
|
|
86284
|
+
"description": "A link is not followed, and its size and date are its own.",
|
|
86285
|
+
"required": [
|
|
86286
|
+
"name",
|
|
86287
|
+
"kind",
|
|
86288
|
+
"size",
|
|
86289
|
+
"modifiedAt"
|
|
86290
|
+
],
|
|
86291
|
+
"properties": {
|
|
86292
|
+
"name": {
|
|
86293
|
+
"type": "string"
|
|
86294
|
+
},
|
|
86295
|
+
"kind": {
|
|
86296
|
+
"type": "string",
|
|
86297
|
+
"enum": [
|
|
86298
|
+
"file",
|
|
86299
|
+
"folder",
|
|
86300
|
+
"link"
|
|
86301
|
+
]
|
|
86302
|
+
},
|
|
86303
|
+
"size": {
|
|
86304
|
+
"type": "integer"
|
|
86305
|
+
},
|
|
86306
|
+
"modifiedAt": {
|
|
86307
|
+
"type": "string",
|
|
86308
|
+
"format": "date-time"
|
|
86309
|
+
}
|
|
86310
|
+
}
|
|
86311
|
+
}
|
|
86312
|
+
},
|
|
86313
|
+
"more": {
|
|
86314
|
+
"type": "boolean",
|
|
86315
|
+
"description": "Whether the folder has more than these."
|
|
86316
|
+
}
|
|
86317
|
+
}
|
|
86318
|
+
},
|
|
86319
|
+
{
|
|
86320
|
+
"type": "object",
|
|
86321
|
+
"description": "What the binary message after it is, and the file's whole size and date.",
|
|
86322
|
+
"required": [
|
|
86323
|
+
"type",
|
|
86324
|
+
"id",
|
|
86325
|
+
"path",
|
|
86326
|
+
"offset",
|
|
86327
|
+
"length",
|
|
86328
|
+
"size",
|
|
86329
|
+
"modifiedAt"
|
|
86330
|
+
],
|
|
86331
|
+
"properties": {
|
|
86332
|
+
"type": {
|
|
86333
|
+
"const": "data"
|
|
86334
|
+
},
|
|
86335
|
+
"id": {
|
|
86336
|
+
"type": "string"
|
|
86337
|
+
},
|
|
86338
|
+
"path": {
|
|
86339
|
+
"type": "string"
|
|
86340
|
+
},
|
|
86341
|
+
"offset": {
|
|
86342
|
+
"type": "integer"
|
|
86343
|
+
},
|
|
86344
|
+
"length": {
|
|
86345
|
+
"type": "integer",
|
|
86346
|
+
"description": "How many bytes follow."
|
|
86347
|
+
},
|
|
86348
|
+
"size": {
|
|
86349
|
+
"type": "integer"
|
|
86350
|
+
},
|
|
86351
|
+
"modifiedAt": {
|
|
86352
|
+
"type": "string",
|
|
86353
|
+
"format": "date-time"
|
|
85318
86354
|
}
|
|
85319
86355
|
}
|
|
85320
86356
|
},
|
|
85321
86357
|
{
|
|
85322
86358
|
"type": "object",
|
|
85323
86359
|
"required": [
|
|
85324
|
-
"type"
|
|
86360
|
+
"type",
|
|
86361
|
+
"id",
|
|
86362
|
+
"since",
|
|
86363
|
+
"changes",
|
|
86364
|
+
"more"
|
|
85325
86365
|
],
|
|
85326
86366
|
"properties": {
|
|
85327
86367
|
"type": {
|
|
85328
|
-
"const": "
|
|
86368
|
+
"const": "changes"
|
|
86369
|
+
},
|
|
86370
|
+
"id": {
|
|
86371
|
+
"type": "string"
|
|
86372
|
+
},
|
|
86373
|
+
"since": {
|
|
86374
|
+
"type": "string",
|
|
86375
|
+
"format": "date-time"
|
|
86376
|
+
},
|
|
86377
|
+
"changes": {
|
|
86378
|
+
"type": "array",
|
|
86379
|
+
"items": {
|
|
86380
|
+
"type": "object",
|
|
86381
|
+
"required": [
|
|
86382
|
+
"path",
|
|
86383
|
+
"size",
|
|
86384
|
+
"modifiedAt"
|
|
86385
|
+
],
|
|
86386
|
+
"properties": {
|
|
86387
|
+
"path": {
|
|
86388
|
+
"type": "string"
|
|
86389
|
+
},
|
|
86390
|
+
"size": {
|
|
86391
|
+
"type": "integer"
|
|
86392
|
+
},
|
|
86393
|
+
"modifiedAt": {
|
|
86394
|
+
"type": "string",
|
|
86395
|
+
"format": "date-time"
|
|
86396
|
+
}
|
|
86397
|
+
}
|
|
86398
|
+
}
|
|
86399
|
+
},
|
|
86400
|
+
"more": {
|
|
86401
|
+
"type": "boolean",
|
|
86402
|
+
"description": "Whether more than these changed."
|
|
86403
|
+
}
|
|
86404
|
+
}
|
|
86405
|
+
},
|
|
86406
|
+
{
|
|
86407
|
+
"type": "object",
|
|
86408
|
+
"description": "The file is in place.",
|
|
86409
|
+
"required": [
|
|
86410
|
+
"type",
|
|
86411
|
+
"id",
|
|
86412
|
+
"path",
|
|
86413
|
+
"size"
|
|
86414
|
+
],
|
|
86415
|
+
"properties": {
|
|
86416
|
+
"type": {
|
|
86417
|
+
"const": "written"
|
|
86418
|
+
},
|
|
86419
|
+
"id": {
|
|
86420
|
+
"type": "string"
|
|
86421
|
+
},
|
|
86422
|
+
"path": {
|
|
86423
|
+
"type": "string"
|
|
86424
|
+
},
|
|
86425
|
+
"size": {
|
|
86426
|
+
"type": "integer"
|
|
86427
|
+
}
|
|
86428
|
+
}
|
|
86429
|
+
},
|
|
86430
|
+
{
|
|
86431
|
+
"type": "object",
|
|
86432
|
+
"description": "It wasn't done, and why. The id is empty when what was sent had none to repeat.",
|
|
86433
|
+
"required": [
|
|
86434
|
+
"type",
|
|
86435
|
+
"id",
|
|
86436
|
+
"message"
|
|
86437
|
+
],
|
|
86438
|
+
"properties": {
|
|
86439
|
+
"type": {
|
|
86440
|
+
"const": "refused"
|
|
86441
|
+
},
|
|
86442
|
+
"id": {
|
|
86443
|
+
"type": "string"
|
|
86444
|
+
},
|
|
86445
|
+
"message": {
|
|
86446
|
+
"type": "string"
|
|
85329
86447
|
}
|
|
85330
86448
|
}
|
|
85331
86449
|
}
|
|
@@ -85486,7 +86604,7 @@ var OPERATIONS = [
|
|
|
85486
86604
|
"files",
|
|
85487
86605
|
"terminal"
|
|
85488
86606
|
],
|
|
85489
|
-
"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."
|
|
85490
86608
|
},
|
|
85491
86609
|
"browser": {
|
|
85492
86610
|
"type": "boolean"
|
|
@@ -85575,7 +86693,7 @@ var OPERATIONS = [
|
|
|
85575
86693
|
"files",
|
|
85576
86694
|
"terminal"
|
|
85577
86695
|
],
|
|
85578
|
-
"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."
|
|
85579
86697
|
},
|
|
85580
86698
|
"browser": {
|
|
85581
86699
|
"type": "boolean",
|
|
@@ -85645,7 +86763,7 @@ var OPERATIONS = [
|
|
|
85645
86763
|
"files",
|
|
85646
86764
|
"terminal"
|
|
85647
86765
|
],
|
|
85648
|
-
"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."
|
|
85649
86767
|
},
|
|
85650
86768
|
"browser": {
|
|
85651
86769
|
"type": "boolean"
|
|
@@ -85785,7 +86903,7 @@ var OPERATIONS = [
|
|
|
85785
86903
|
"files",
|
|
85786
86904
|
"terminal"
|
|
85787
86905
|
],
|
|
85788
|
-
"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."
|
|
85789
86907
|
},
|
|
85790
86908
|
"browser": {
|
|
85791
86909
|
"type": "boolean"
|
|
@@ -85869,11 +86987,11 @@ var OPERATIONS = [
|
|
|
85869
86987
|
"surface": {
|
|
85870
86988
|
"type": "string",
|
|
85871
86989
|
"enum": [
|
|
85872
|
-
"commands",
|
|
85873
86990
|
"terminal",
|
|
86991
|
+
"files",
|
|
85874
86992
|
"browser"
|
|
85875
86993
|
],
|
|
85876
|
-
"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."
|
|
85877
86995
|
},
|
|
85878
86996
|
"protocol": {
|
|
85879
86997
|
"type": "string"
|
|
@@ -85885,8 +87003,7 @@ var OPERATIONS = [
|
|
|
85885
87003
|
},
|
|
85886
87004
|
"unavailable": {
|
|
85887
87005
|
"type": "string",
|
|
85888
|
-
"description": "Why it can't be opened
|
|
85889
|
-
"if it can't.": null
|
|
87006
|
+
"description": "Why it can't be opened now, if it can't."
|
|
85890
87007
|
}
|
|
85891
87008
|
}
|
|
85892
87009
|
}
|
|
@@ -86016,7 +87133,7 @@ var OPERATIONS = [
|
|
|
86016
87133
|
"files",
|
|
86017
87134
|
"terminal"
|
|
86018
87135
|
],
|
|
86019
|
-
"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."
|
|
86020
87137
|
},
|
|
86021
87138
|
"browser": {
|
|
86022
87139
|
"type": "boolean"
|
|
@@ -86140,7 +87257,7 @@ var OPERATIONS = [
|
|
|
86140
87257
|
"files",
|
|
86141
87258
|
"terminal"
|
|
86142
87259
|
],
|
|
86143
|
-
"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."
|
|
86144
87261
|
},
|
|
86145
87262
|
"browser": {
|
|
86146
87263
|
"type": "boolean"
|
|
@@ -86269,7 +87386,7 @@ var OPERATIONS = [
|
|
|
86269
87386
|
"files",
|
|
86270
87387
|
"terminal"
|
|
86271
87388
|
],
|
|
86272
|
-
"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."
|
|
86273
87390
|
},
|
|
86274
87391
|
"browser": {
|
|
86275
87392
|
"type": "boolean"
|
|
@@ -86390,7 +87507,7 @@ var OPERATIONS = [
|
|
|
86390
87507
|
"method": "POST",
|
|
86391
87508
|
"path": "/v1/computers/{computerID}/live",
|
|
86392
87509
|
"tool": "computer_live",
|
|
86393
|
-
"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.",
|
|
86394
87511
|
"status": 201,
|
|
86395
87512
|
"hasBody": true,
|
|
86396
87513
|
"pathParameters": [
|
|
@@ -86409,11 +87526,11 @@ var OPERATIONS = [
|
|
|
86409
87526
|
"surface": {
|
|
86410
87527
|
"type": "string",
|
|
86411
87528
|
"enum": [
|
|
86412
|
-
"commands",
|
|
86413
87529
|
"terminal",
|
|
87530
|
+
"files",
|
|
86414
87531
|
"browser"
|
|
86415
87532
|
],
|
|
86416
|
-
"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."
|
|
86417
87534
|
},
|
|
86418
87535
|
"mode": {
|
|
86419
87536
|
"allOf": [
|
|
@@ -86423,7 +87540,7 @@ var OPERATIONS = [
|
|
|
86423
87540
|
"watch",
|
|
86424
87541
|
"operate"
|
|
86425
87542
|
],
|
|
86426
|
-
"description": "In watch
|
|
87543
|
+
"description": "In watch you see and don't act: what you send is dropped, except that a files socket may still ask to list, to read and for what changed."
|
|
86427
87544
|
}
|
|
86428
87545
|
],
|
|
86429
87546
|
"default": "watch"
|
|
@@ -86455,11 +87572,11 @@ var OPERATIONS = [
|
|
|
86455
87572
|
"surface": {
|
|
86456
87573
|
"type": "string",
|
|
86457
87574
|
"enum": [
|
|
86458
|
-
"commands",
|
|
86459
87575
|
"terminal",
|
|
87576
|
+
"files",
|
|
86460
87577
|
"browser"
|
|
86461
87578
|
],
|
|
86462
|
-
"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."
|
|
86463
87580
|
},
|
|
86464
87581
|
"mode": {
|
|
86465
87582
|
"type": "string",
|
|
@@ -86467,7 +87584,7 @@ var OPERATIONS = [
|
|
|
86467
87584
|
"watch",
|
|
86468
87585
|
"operate"
|
|
86469
87586
|
],
|
|
86470
|
-
"description": "In watch
|
|
87587
|
+
"description": "In watch you see and don't act: what you send is dropped, except that a files socket may still ask to list, to read and for what changed."
|
|
86471
87588
|
},
|
|
86472
87589
|
"expiresAt": {
|
|
86473
87590
|
"type": "string",
|
|
@@ -86531,13 +87648,13 @@ function parseListen(listen) {
|
|
|
86531
87648
|
}
|
|
86532
87649
|
var bearerOf = (request) => /^Bearer (\S+)$/.exec(request.headers.authorization ?? "")?.[1];
|
|
86533
87650
|
async function jsonBody(request) {
|
|
86534
|
-
let
|
|
87651
|
+
let text3 = "";
|
|
86535
87652
|
for await (const chunk of request) {
|
|
86536
|
-
|
|
86537
|
-
if (
|
|
87653
|
+
text3 += String(chunk);
|
|
87654
|
+
if (text3.length > MAX_BODY2) throw new ApiError(413, "That request is too large.");
|
|
86538
87655
|
}
|
|
86539
87656
|
try {
|
|
86540
|
-
const parsed = JSON.parse(
|
|
87657
|
+
const parsed = JSON.parse(text3 || "{}");
|
|
86541
87658
|
if (typeof parsed === "object" && parsed !== null && !Array.isArray(parsed))
|
|
86542
87659
|
return parsed;
|
|
86543
87660
|
} catch {
|
|
@@ -86565,7 +87682,7 @@ async function serve(options) {
|
|
|
86565
87682
|
pattern: new RegExp(`^${operation.path.replace(/\{[^}]+\}/g, "([^/]+)")}$`),
|
|
86566
87683
|
check: fromJsonSchema3(operation.input)
|
|
86567
87684
|
}));
|
|
86568
|
-
const
|
|
87685
|
+
const run2 = async (route2, input2) => {
|
|
86569
87686
|
const checked = await route2.check["~standard"].validate(input2);
|
|
86570
87687
|
if (checked.issues)
|
|
86571
87688
|
throw new ApiError(400, checked.issues.map((issue3) => issue3.message).join("; "));
|
|
@@ -86580,7 +87697,7 @@ async function serve(options) {
|
|
|
86580
87697
|
{ description: route2.operation.summary, inputSchema: route2.check },
|
|
86581
87698
|
async (input2) => {
|
|
86582
87699
|
try {
|
|
86583
|
-
const output2 = await
|
|
87700
|
+
const output2 = await run2(route2, input2) ?? {};
|
|
86584
87701
|
return { content: [{ type: "text", text: JSON.stringify(output2) }] };
|
|
86585
87702
|
} catch (error62) {
|
|
86586
87703
|
if (!(error62 instanceof ApiError)) throw error62;
|
|
@@ -86632,7 +87749,7 @@ async function serve(options) {
|
|
|
86632
87749
|
const value = url3.searchParams.get(name);
|
|
86633
87750
|
if (value !== null) input2[name] = value;
|
|
86634
87751
|
}
|
|
86635
|
-
return send(response, operation.status, await
|
|
87752
|
+
return send(response, operation.status, await run2(candidate, input2));
|
|
86636
87753
|
}
|
|
86637
87754
|
throw pathMatched ? new ApiError(405, "Not allowed.") : new ApiError(404, "Not found.");
|
|
86638
87755
|
};
|
|
@@ -86719,7 +87836,7 @@ var USAGE = `Usage: agentcomputer <command> [--data DIR]
|
|
|
86719
87836
|
stop <id> stop it, keeping its folder and token
|
|
86720
87837
|
start <id> [--timeout SECONDS] start a stopped computer again
|
|
86721
87838
|
logs <id> what its servers wrote
|
|
86722
|
-
live <id> --surface
|
|
87839
|
+
live <id> --surface terminal|files|browser [--mode watch|operate]
|
|
86723
87840
|
a one-time ticket to open one live, as a WebSocket
|
|
86724
87841
|
kill <id> stop it and remove what the service made
|
|
86725
87842
|
|
|
@@ -86767,6 +87884,8 @@ async function main() {
|
|
|
86767
87884
|
channel: { type: "string" },
|
|
86768
87885
|
executable: { type: "string" },
|
|
86769
87886
|
"no-sandbox": { type: "boolean" },
|
|
87887
|
+
endpoint: { type: "string" },
|
|
87888
|
+
output: { type: "string" },
|
|
86770
87889
|
access: { type: "string" },
|
|
86771
87890
|
workspace: { type: "string" },
|
|
86772
87891
|
name: { type: "string" },
|
|
@@ -86813,6 +87932,11 @@ async function main() {
|
|
|
86813
87932
|
executable: values.executable,
|
|
86814
87933
|
sandbox: values["no-sandbox"] ? false : void 0
|
|
86815
87934
|
});
|
|
87935
|
+
if (command === "browser-mcp")
|
|
87936
|
+
return serveBrowserMcp({
|
|
87937
|
+
endpoint: values.endpoint ?? "",
|
|
87938
|
+
outputDir: values.output ?? ""
|
|
87939
|
+
});
|
|
86816
87940
|
if (command === "inside")
|
|
86817
87941
|
return serveInside({
|
|
86818
87942
|
access: values.access,
|