@agentorg/computer 0.0.7 → 0.0.8
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 +24 -6
- package/dist/THIRD_PARTY_LICENSES.txt +1 -1
- package/dist/agentcomputer.js +1266 -519
- package/dist/types/generated/schema.d.ts +86 -13
- 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;
|
|
@@ -3753,7 +3753,7 @@ var require_websocket_server = __commonJS({
|
|
|
3753
3753
|
// src/main.ts
|
|
3754
3754
|
import { readFileSync as readFileSync6 } from "node:fs";
|
|
3755
3755
|
import { homedir as homedir2 } from "node:os";
|
|
3756
|
-
import { isAbsolute as isAbsolute2, join as
|
|
3756
|
+
import { isAbsolute as isAbsolute2, join as join12, resolve as resolve5 } from "node:path";
|
|
3757
3757
|
import { parseArgs } from "node:util";
|
|
3758
3758
|
import { isMainThread as isMainThread2 } from "node:worker_threads";
|
|
3759
3759
|
|
|
@@ -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,26 +3797,66 @@ 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";
|
|
3810
3851
|
import { connect } from "node:net";
|
|
3811
|
-
import { join as
|
|
3852
|
+
import { join as join9 } from "node:path";
|
|
3812
3853
|
import { createInterface as createInterface2 } from "node:readline";
|
|
3813
3854
|
import { Readable as Readable3 } from "node:stream";
|
|
3814
3855
|
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";
|
|
@@ -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;
|
|
@@ -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
|
}
|
|
@@ -28128,10 +28153,10 @@ var Browser = class {
|
|
|
28128
28153
|
// src/inside.ts
|
|
28129
28154
|
import { createServer as createServer3 } from "node:http";
|
|
28130
28155
|
import { homedir } from "node:os";
|
|
28131
|
-
import { join as
|
|
28156
|
+
import { join as join8 } from "node:path";
|
|
28132
28157
|
|
|
28133
28158
|
// src/runtime.ts
|
|
28134
|
-
import { join as
|
|
28159
|
+
import { join as join7 } from "node:path";
|
|
28135
28160
|
|
|
28136
28161
|
// node_modules/@agentorg/exec/dist/lib.js
|
|
28137
28162
|
import { realpathSync } from "node:fs";
|
|
@@ -28139,9 +28164,22 @@ import { resolve as resolve3 } from "node:path";
|
|
|
28139
28164
|
import { STATUS_CODES, ServerResponse, createServer } from "node:http";
|
|
28140
28165
|
import { Http2ServerRequest, constants } from "node:http2";
|
|
28141
28166
|
import { Readable as Readable2 } from "node:stream";
|
|
28167
|
+
import { randomBytes as randomBytes3 } from "node:crypto";
|
|
28168
|
+
import { lstat as lstat2, open as open2, readdir as readdir2, rename as rename2, rm as rm2, stat as stat2 } from "node:fs/promises";
|
|
28169
|
+
import { dirname as dirname2, join as join3 } from "node:path";
|
|
28142
28170
|
import { randomBytes as randomBytes2 } from "node:crypto";
|
|
28143
|
-
import {
|
|
28144
|
-
|
|
28171
|
+
import {
|
|
28172
|
+
chmod,
|
|
28173
|
+
lstat,
|
|
28174
|
+
readdir,
|
|
28175
|
+
readFile,
|
|
28176
|
+
realpath,
|
|
28177
|
+
rename,
|
|
28178
|
+
rm,
|
|
28179
|
+
stat,
|
|
28180
|
+
writeFile
|
|
28181
|
+
} from "node:fs/promises";
|
|
28182
|
+
import { basename, dirname, join as join4, resolve, sep as sep2 } from "node:path";
|
|
28145
28183
|
import { spawn as spawn2 } from "node:child_process";
|
|
28146
28184
|
import { randomBytes } from "node:crypto";
|
|
28147
28185
|
import { StringDecoder } from "node:string_decoder";
|
|
@@ -28373,7 +28411,7 @@ var require_ignore = __commonJS2({
|
|
|
28373
28411
|
// ever starts again inside it -- `(\\+?) ` did, which made a long run
|
|
28374
28412
|
// quadratic.
|
|
28375
28413
|
/(\\+)( ?)/g,
|
|
28376
|
-
(_,
|
|
28414
|
+
(_, run2, space) => space ? run2.slice(0, run2.length - run2.length % 2) + SPACE2 : run2,
|
|
28377
28415
|
ESCAPE + SPACE2
|
|
28378
28416
|
],
|
|
28379
28417
|
// git's wildmatch() reads a run of two or more asterisks as one globstar
|
|
@@ -28559,10 +28597,10 @@ var require_ignore = __commonJS2({
|
|
|
28559
28597
|
var REGEX_REPLACE_TRAILING_DOUBLESTAR = /\uE001$/;
|
|
28560
28598
|
var replaceTrailingDoublestar = () => ".+(?=$|\\/$)";
|
|
28561
28599
|
var WILDCARD2 = "[^\\/]*";
|
|
28562
|
-
var separatorAfter = (
|
|
28600
|
+
var separatorAfter = (run2, at) => {
|
|
28563
28601
|
let separator = EMPTY;
|
|
28564
|
-
for (let index = at + 1; index <
|
|
28565
|
-
separator +=
|
|
28602
|
+
for (let index = at + 1; index < run2.length && !run2[index].wildcard; index++) {
|
|
28603
|
+
separator += run2[index].single;
|
|
28566
28604
|
}
|
|
28567
28605
|
return separator;
|
|
28568
28606
|
};
|
|
@@ -28622,26 +28660,26 @@ var require_ignore = __commonJS2({
|
|
|
28622
28660
|
}
|
|
28623
28661
|
}
|
|
28624
28662
|
let out = EMPTY;
|
|
28625
|
-
let
|
|
28663
|
+
let run2 = [];
|
|
28626
28664
|
const flush = () => {
|
|
28627
28665
|
let lastWildcard;
|
|
28628
|
-
|
|
28666
|
+
run2.forEach((token, at) => {
|
|
28629
28667
|
if (token.wildcard) {
|
|
28630
28668
|
lastWildcard = at;
|
|
28631
28669
|
}
|
|
28632
28670
|
});
|
|
28633
|
-
|
|
28671
|
+
run2.forEach((token, at) => {
|
|
28634
28672
|
if (!token.wildcard) {
|
|
28635
28673
|
out += token.single;
|
|
28636
28674
|
return;
|
|
28637
28675
|
}
|
|
28638
|
-
out += at === lastWildcard ? WILDCARD2 : `(?:(?!${separatorAfter(
|
|
28676
|
+
out += at === lastWildcard ? WILDCARD2 : `(?:(?!${separatorAfter(run2, at)})[^\\/])*`;
|
|
28639
28677
|
});
|
|
28640
|
-
|
|
28678
|
+
run2 = [];
|
|
28641
28679
|
};
|
|
28642
28680
|
tokens.forEach((token) => {
|
|
28643
28681
|
if (token.boundary === void 0) {
|
|
28644
|
-
|
|
28682
|
+
run2.push(token);
|
|
28645
28683
|
return;
|
|
28646
28684
|
}
|
|
28647
28685
|
flush();
|
|
@@ -34276,7 +34314,7 @@ function bucketFor2(state, inst) {
|
|
|
34276
34314
|
return bucket;
|
|
34277
34315
|
}
|
|
34278
34316
|
var handoff2;
|
|
34279
|
-
var
|
|
34317
|
+
var open3 = [];
|
|
34280
34318
|
var memo3 = {
|
|
34281
34319
|
alloc(_inst, payload, empty) {
|
|
34282
34320
|
const bucket = handoff2;
|
|
@@ -34285,7 +34323,7 @@ var memo3 = {
|
|
|
34285
34323
|
handoff2 = void 0;
|
|
34286
34324
|
const entry = { value: empty, issues: null };
|
|
34287
34325
|
bucket.set(payload.value, entry);
|
|
34288
|
-
|
|
34326
|
+
open3.push(entry);
|
|
34289
34327
|
return empty;
|
|
34290
34328
|
},
|
|
34291
34329
|
guard(inst) {
|
|
@@ -34356,10 +34394,10 @@ var memo3 = {
|
|
|
34356
34394
|
return payload;
|
|
34357
34395
|
}
|
|
34358
34396
|
handoff2 = bucket;
|
|
34359
|
-
const depth =
|
|
34397
|
+
const depth = open3.length;
|
|
34360
34398
|
const result = base(payload, ctx);
|
|
34361
34399
|
handoff2 = void 0;
|
|
34362
|
-
const entry =
|
|
34400
|
+
const entry = open3.length > depth ? open3.pop() : void 0;
|
|
34363
34401
|
if (result instanceof Promise) {
|
|
34364
34402
|
return result.then((r) => {
|
|
34365
34403
|
if (entry)
|
|
@@ -38340,8 +38378,8 @@ function ko_default() {
|
|
|
38340
38378
|
localeError: error32()
|
|
38341
38379
|
};
|
|
38342
38380
|
}
|
|
38343
|
-
var capitalizeFirstCharacter = (
|
|
38344
|
-
return
|
|
38381
|
+
var capitalizeFirstCharacter = (text22) => {
|
|
38382
|
+
return text22.charAt(0).toUpperCase() + text22.slice(1);
|
|
38345
38383
|
};
|
|
38346
38384
|
function getUnitTypeFromNumber(number42) {
|
|
38347
38385
|
const abs = Math.abs(number42);
|
|
@@ -48403,7 +48441,7 @@ function visit(schema, fnOrHandlers) {
|
|
|
48403
48441
|
return h ? h(node22, rewritten) : node22;
|
|
48404
48442
|
};
|
|
48405
48443
|
const cache = /* @__PURE__ */ new Map();
|
|
48406
|
-
function
|
|
48444
|
+
function run2(s) {
|
|
48407
48445
|
const cached22 = cache.get(s);
|
|
48408
48446
|
if (cached22 === RESOLVING) {
|
|
48409
48447
|
return new $ZodLazy2({
|
|
@@ -48426,69 +48464,69 @@ function visit(schema, fnOrHandlers) {
|
|
|
48426
48464
|
case "object": {
|
|
48427
48465
|
const oldShape = def.shape;
|
|
48428
48466
|
const keys = Object.keys(oldShape);
|
|
48429
|
-
let
|
|
48467
|
+
let changed2 = false;
|
|
48430
48468
|
const newShape = {};
|
|
48431
48469
|
for (const k of keys) {
|
|
48432
|
-
const mapped =
|
|
48470
|
+
const mapped = run2(oldShape[k]);
|
|
48433
48471
|
if (mapped !== oldShape[k])
|
|
48434
|
-
|
|
48472
|
+
changed2 = true;
|
|
48435
48473
|
newShape[k] = mapped;
|
|
48436
48474
|
}
|
|
48437
48475
|
let newCatchall = def.catchall;
|
|
48438
48476
|
if (def.catchall) {
|
|
48439
|
-
newCatchall =
|
|
48477
|
+
newCatchall = run2(def.catchall);
|
|
48440
48478
|
if (newCatchall !== def.catchall)
|
|
48441
|
-
|
|
48479
|
+
changed2 = true;
|
|
48442
48480
|
}
|
|
48443
|
-
return
|
|
48481
|
+
return changed2 ? clone2(s, { ...def, shape: newShape, catchall: newCatchall }) : s;
|
|
48444
48482
|
}
|
|
48445
48483
|
case "array": {
|
|
48446
|
-
const mapped =
|
|
48484
|
+
const mapped = run2(def.element);
|
|
48447
48485
|
return mapped === def.element ? s : clone2(s, { ...def, element: mapped });
|
|
48448
48486
|
}
|
|
48449
48487
|
case "tuple": {
|
|
48450
48488
|
const oldItems = def.items;
|
|
48451
|
-
let
|
|
48489
|
+
let changed2 = false;
|
|
48452
48490
|
const newItems = [];
|
|
48453
48491
|
for (const item of oldItems) {
|
|
48454
|
-
const mapped =
|
|
48492
|
+
const mapped = run2(item);
|
|
48455
48493
|
if (mapped !== item)
|
|
48456
|
-
|
|
48494
|
+
changed2 = true;
|
|
48457
48495
|
newItems.push(mapped);
|
|
48458
48496
|
}
|
|
48459
48497
|
let newRest = def.rest;
|
|
48460
48498
|
if (def.rest) {
|
|
48461
|
-
newRest =
|
|
48499
|
+
newRest = run2(def.rest);
|
|
48462
48500
|
if (newRest !== def.rest)
|
|
48463
|
-
|
|
48501
|
+
changed2 = true;
|
|
48464
48502
|
}
|
|
48465
|
-
return
|
|
48503
|
+
return changed2 ? clone2(s, { ...def, items: newItems, rest: newRest }) : s;
|
|
48466
48504
|
}
|
|
48467
48505
|
case "record":
|
|
48468
48506
|
case "map": {
|
|
48469
|
-
const newKey =
|
|
48470
|
-
const newVal =
|
|
48507
|
+
const newKey = run2(def.keyType);
|
|
48508
|
+
const newVal = run2(def.valueType);
|
|
48471
48509
|
return newKey === def.keyType && newVal === def.valueType ? s : clone2(s, { ...def, keyType: newKey, valueType: newVal });
|
|
48472
48510
|
}
|
|
48473
48511
|
case "set": {
|
|
48474
|
-
const newVal =
|
|
48512
|
+
const newVal = run2(def.valueType);
|
|
48475
48513
|
return newVal === def.valueType ? s : clone2(s, { ...def, valueType: newVal });
|
|
48476
48514
|
}
|
|
48477
48515
|
case "union": {
|
|
48478
48516
|
const oldOptions = def.options;
|
|
48479
|
-
let
|
|
48517
|
+
let changed2 = false;
|
|
48480
48518
|
const newOptions = [];
|
|
48481
48519
|
for (const opt of oldOptions) {
|
|
48482
|
-
const mapped =
|
|
48520
|
+
const mapped = run2(opt);
|
|
48483
48521
|
if (mapped !== opt)
|
|
48484
|
-
|
|
48522
|
+
changed2 = true;
|
|
48485
48523
|
newOptions.push(mapped);
|
|
48486
48524
|
}
|
|
48487
|
-
return
|
|
48525
|
+
return changed2 ? clone2(s, { ...def, options: newOptions }) : s;
|
|
48488
48526
|
}
|
|
48489
48527
|
case "intersection": {
|
|
48490
|
-
const newLeft =
|
|
48491
|
-
const newRight =
|
|
48528
|
+
const newLeft = run2(def.left);
|
|
48529
|
+
const newRight = run2(def.right);
|
|
48492
48530
|
return newLeft === def.left && newRight === def.right ? s : clone2(s, { ...def, left: newLeft, right: newRight });
|
|
48493
48531
|
}
|
|
48494
48532
|
case "optional":
|
|
@@ -48500,23 +48538,23 @@ function visit(schema, fnOrHandlers) {
|
|
|
48500
48538
|
case "nonoptional":
|
|
48501
48539
|
case "promise":
|
|
48502
48540
|
case "success": {
|
|
48503
|
-
const newInner =
|
|
48541
|
+
const newInner = run2(def.innerType);
|
|
48504
48542
|
return newInner === def.innerType ? s : clone2(s, { ...def, innerType: newInner });
|
|
48505
48543
|
}
|
|
48506
48544
|
case "pipe": {
|
|
48507
|
-
const newIn =
|
|
48508
|
-
const newOut =
|
|
48545
|
+
const newIn = run2(def.in);
|
|
48546
|
+
const newOut = run2(def.out);
|
|
48509
48547
|
return newIn === def.in && newOut === def.out ? s : clone2(s, { ...def, in: newIn, out: newOut });
|
|
48510
48548
|
}
|
|
48511
48549
|
case "function": {
|
|
48512
|
-
const newInput =
|
|
48513
|
-
const newOutput =
|
|
48550
|
+
const newInput = run2(def.input);
|
|
48551
|
+
const newOutput = run2(def.output);
|
|
48514
48552
|
return newInput === def.input && newOutput === def.output ? s : clone2(s, { ...def, input: newInput, output: newOutput });
|
|
48515
48553
|
}
|
|
48516
48554
|
case "lazy": {
|
|
48517
48555
|
const original = def.getter;
|
|
48518
48556
|
const { _cachedInner, ...rest } = def;
|
|
48519
|
-
return clone2(s, { ...rest, getter: () =>
|
|
48557
|
+
return clone2(s, { ...rest, getter: () => run2(original()) });
|
|
48520
48558
|
}
|
|
48521
48559
|
// A leaf by choice: `parts` are regex fragments, not data positions.
|
|
48522
48560
|
case "template_literal":
|
|
@@ -48547,7 +48585,7 @@ function visit(schema, fnOrHandlers) {
|
|
|
48547
48585
|
}
|
|
48548
48586
|
}
|
|
48549
48587
|
}
|
|
48550
|
-
return
|
|
48588
|
+
return run2(schema);
|
|
48551
48589
|
}
|
|
48552
48590
|
function deepPartial(schema) {
|
|
48553
48591
|
return visit(schema, {
|
|
@@ -52975,7 +53013,7 @@ function linkedRoundAbort2(outer) {
|
|
|
52975
53013
|
if (outer?.aborted) controller.abort(outer.reason);
|
|
52976
53014
|
return {
|
|
52977
53015
|
signal: controller.signal,
|
|
52978
|
-
abort: (
|
|
53016
|
+
abort: (reason22) => controller.abort(reason22),
|
|
52979
53017
|
dispose: () => outer?.removeEventListener("abort", onOuterAbort)
|
|
52980
53018
|
};
|
|
52981
53019
|
}
|
|
@@ -53770,8 +53808,8 @@ var Protocol2 = class {
|
|
|
53770
53808
|
return;
|
|
53771
53809
|
}
|
|
53772
53810
|
if (options?.signal?.aborted) {
|
|
53773
|
-
const
|
|
53774
|
-
throw
|
|
53811
|
+
const reason22 = options.signal.reason;
|
|
53812
|
+
throw reason22 instanceof SdkError2 ? reason22 : new SdkError2(SdkErrorCode2.RequestTimeout, String(reason22));
|
|
53775
53813
|
}
|
|
53776
53814
|
const requestAbort = codec2.era === MODERN_WIRE_REVISION2 && this._transport.hasPerRequestStream === true ? new AbortController() : void 0;
|
|
53777
53815
|
const messageId = this._requestMessageId++;
|
|
@@ -53793,7 +53831,7 @@ var Protocol2 = class {
|
|
|
53793
53831
|
}
|
|
53794
53832
|
const outbound = this._envelopeOutbound(jsonrpcRequest);
|
|
53795
53833
|
let responseReceived = false;
|
|
53796
|
-
const cancel = (
|
|
53834
|
+
const cancel = (reason22) => {
|
|
53797
53835
|
if (responseReceived) return;
|
|
53798
53836
|
this._progressHandlers.delete(messageId);
|
|
53799
53837
|
if (requestAbort === void 0) {
|
|
@@ -53802,7 +53840,7 @@ var Protocol2 = class {
|
|
|
53802
53840
|
method: "notifications/cancelled",
|
|
53803
53841
|
params: {
|
|
53804
53842
|
requestId: messageId,
|
|
53805
|
-
reason: String(
|
|
53843
|
+
reason: String(reason22)
|
|
53806
53844
|
}
|
|
53807
53845
|
}), {
|
|
53808
53846
|
relatedRequestId,
|
|
@@ -53810,7 +53848,7 @@ var Protocol2 = class {
|
|
|
53810
53848
|
onresumptiontoken
|
|
53811
53849
|
}).catch((error62) => this._onerror(/* @__PURE__ */ new Error(`Failed to send cancellation: ${error62}`)));
|
|
53812
53850
|
} else requestAbort.abort();
|
|
53813
|
-
reject(
|
|
53851
|
+
reject(reason22 instanceof SdkError2 ? reason22 : new SdkError2(SdkErrorCode2.RequestTimeout, String(reason22)));
|
|
53814
53852
|
};
|
|
53815
53853
|
this._responseHandlers.set(messageId, (response) => {
|
|
53816
53854
|
if (options?.signal?.aborted) return;
|
|
@@ -58131,7 +58169,7 @@ var require_core$32 = /* @__PURE__ */ __commonJSMin2(((exports) => {
|
|
|
58131
58169
|
}
|
|
58132
58170
|
errorsText(errors = this.errors, { separator = ", ", dataVar = "data" } = {}) {
|
|
58133
58171
|
if (!errors || errors.length === 0) return "No errors";
|
|
58134
|
-
return errors.map((e) => `${dataVar}${e.instancePath} ${e.message}`).reduce((
|
|
58172
|
+
return errors.map((e) => `${dataVar}${e.instancePath} ${e.message}`).reduce((text22, msg) => text22 + separator + msg);
|
|
58135
58173
|
}
|
|
58136
58174
|
$dataMetaSchema(metaSchema, keywordsJsonPointers) {
|
|
58137
58175
|
const rules = this.RULES.all;
|
|
@@ -63273,21 +63311,21 @@ async function readRequestBody(request, maxBytes = DEFAULT_MAX_REQUEST_BODY_SIZE
|
|
|
63273
63311
|
const reader = request.body.getReader();
|
|
63274
63312
|
const decoder = new TextDecoder();
|
|
63275
63313
|
let received = 0;
|
|
63276
|
-
let
|
|
63314
|
+
let text22 = "";
|
|
63277
63315
|
try {
|
|
63278
63316
|
for (; ; ) {
|
|
63279
63317
|
const { done, value } = await reader.read();
|
|
63280
63318
|
if (done) break;
|
|
63281
63319
|
received += value.byteLength;
|
|
63282
63320
|
if (received > maxBytes) return { tooLarge: true };
|
|
63283
|
-
|
|
63321
|
+
text22 += decoder.decode(value, { stream: true });
|
|
63284
63322
|
}
|
|
63285
63323
|
} finally {
|
|
63286
63324
|
reader.releaseLock();
|
|
63287
63325
|
}
|
|
63288
63326
|
return {
|
|
63289
63327
|
tooLarge: false,
|
|
63290
|
-
text:
|
|
63328
|
+
text: text22 + decoder.decode()
|
|
63291
63329
|
};
|
|
63292
63330
|
}
|
|
63293
63331
|
var WebStandardStreamableHTTPServerTransport = class {
|
|
@@ -64356,8 +64394,8 @@ var readRawBodyIfAvailable = (request) => {
|
|
|
64356
64394
|
};
|
|
64357
64395
|
var normalizeAbortError = (request, incoming) => {
|
|
64358
64396
|
if (incoming.errored) return incoming.errored;
|
|
64359
|
-
const
|
|
64360
|
-
if (
|
|
64397
|
+
const reason22 = request[abortReasonKey];
|
|
64398
|
+
if (reason22 !== void 0) return reason22 instanceof Error ? reason22 : new Error(String(reason22));
|
|
64361
64399
|
return /* @__PURE__ */ new Error("Client connection prematurely closed.");
|
|
64362
64400
|
};
|
|
64363
64401
|
var readBodyDirect = (request) => {
|
|
@@ -64451,10 +64489,10 @@ var requestPrototype = {
|
|
|
64451
64489
|
get headers() {
|
|
64452
64490
|
return this[headersKey] ||= newHeadersFromIncoming(this[incomingKey]);
|
|
64453
64491
|
},
|
|
64454
|
-
[abortRequest](
|
|
64455
|
-
if (this[abortReasonKey] === void 0) this[abortReasonKey] =
|
|
64492
|
+
[abortRequest](reason22) {
|
|
64493
|
+
if (this[abortReasonKey] === void 0) this[abortReasonKey] = reason22;
|
|
64456
64494
|
const abortController = this[abortControllerKey];
|
|
64457
|
-
if (abortController && !abortController.signal.aborted) abortController.abort(
|
|
64495
|
+
if (abortController && !abortController.signal.aborted) abortController.abort(reason22);
|
|
64458
64496
|
},
|
|
64459
64497
|
[getAbortController]() {
|
|
64460
64498
|
this[abortControllerKey] ||= new AbortController();
|
|
@@ -65098,8 +65136,8 @@ var upgradeWebSocket = defineWebSocketHelper(async (c, events, options) => {
|
|
|
65098
65136
|
ws.on("message", bufferMessage);
|
|
65099
65137
|
const ctx = {
|
|
65100
65138
|
binaryType: "arraybuffer",
|
|
65101
|
-
close(code,
|
|
65102
|
-
ws.close(code,
|
|
65139
|
+
close(code, reason22) {
|
|
65140
|
+
ws.close(code, reason22);
|
|
65103
65141
|
},
|
|
65104
65142
|
protocol: ws.protocol,
|
|
65105
65143
|
raw: ws,
|
|
@@ -65129,11 +65167,11 @@ var upgradeWebSocket = defineWebSocketHelper(async (c, events, options) => {
|
|
|
65129
65167
|
ws.on("message", (data, isBinary) => {
|
|
65130
65168
|
handleMessage(data, isBinary);
|
|
65131
65169
|
});
|
|
65132
|
-
ws.on("close", (code,
|
|
65170
|
+
ws.on("close", (code, reason22) => {
|
|
65133
65171
|
try {
|
|
65134
65172
|
events?.onClose?.(new CloseEvent("close", {
|
|
65135
65173
|
code,
|
|
65136
|
-
reason:
|
|
65174
|
+
reason: reason22.toString()
|
|
65137
65175
|
}), ctx);
|
|
65138
65176
|
} catch (e) {
|
|
65139
65177
|
(options?.onError ?? console.error)(e);
|
|
@@ -65757,9 +65795,9 @@ function createTwoFilesPatch(oldFileName, newFileName, oldStr, newStr, oldHeader
|
|
|
65757
65795
|
} }));
|
|
65758
65796
|
}
|
|
65759
65797
|
}
|
|
65760
|
-
function splitLines(
|
|
65761
|
-
const hasTrailingNl =
|
|
65762
|
-
const result =
|
|
65798
|
+
function splitLines(text22) {
|
|
65799
|
+
const hasTrailingNl = text22.endsWith("\n");
|
|
65800
|
+
const result = text22.split("\n").map((line) => line + "\n");
|
|
65763
65801
|
if (hasTrailingNl) {
|
|
65764
65802
|
result.pop();
|
|
65765
65803
|
} else {
|
|
@@ -65769,26 +65807,26 @@ function splitLines(text2) {
|
|
|
65769
65807
|
}
|
|
65770
65808
|
var CHARS_PER_TOKEN = 4;
|
|
65771
65809
|
var HELD = 1024 * 1024;
|
|
65772
|
-
function cut(
|
|
65773
|
-
if (
|
|
65810
|
+
function cut(text22, max) {
|
|
65811
|
+
if (text22.length <= max) return text22;
|
|
65774
65812
|
const half = Math.max(1, Math.floor(max / 2));
|
|
65775
|
-
const head =
|
|
65776
|
-
const tail =
|
|
65813
|
+
const head = text22.slice(0, half).replace(/[\uD800-\uDBFF]$/, "");
|
|
65814
|
+
const tail = text22.slice(-half).replace(/^[\uDC00-\uDFFF]/, "");
|
|
65777
65815
|
return `${head}
|
|
65778
|
-
\u2026 ${
|
|
65816
|
+
\u2026 ${text22.length - head.length - tail.length} characters cut \u2026
|
|
65779
65817
|
${tail}`;
|
|
65780
65818
|
}
|
|
65781
65819
|
var Pending = class {
|
|
65782
65820
|
#head = "";
|
|
65783
65821
|
#tail = "";
|
|
65784
65822
|
#dropped = 0;
|
|
65785
|
-
add(
|
|
65823
|
+
add(text22) {
|
|
65786
65824
|
const room = HELD / 2 - this.#head.length;
|
|
65787
65825
|
if (room > 0) {
|
|
65788
|
-
this.#head +=
|
|
65789
|
-
|
|
65826
|
+
this.#head += text22.slice(0, room);
|
|
65827
|
+
text22 = text22.slice(room);
|
|
65790
65828
|
}
|
|
65791
|
-
this.#tail +=
|
|
65829
|
+
this.#tail += text22;
|
|
65792
65830
|
const over = this.#tail.length - HELD / 2;
|
|
65793
65831
|
if (over > 0) {
|
|
65794
65832
|
this.#tail = this.#tail.slice(over);
|
|
@@ -65797,13 +65835,13 @@ var Pending = class {
|
|
|
65797
65835
|
}
|
|
65798
65836
|
/** What is pending, leaving nothing. */
|
|
65799
65837
|
take() {
|
|
65800
|
-
const
|
|
65838
|
+
const text22 = this.#dropped ? `${this.#head}
|
|
65801
65839
|
\u2026 ${this.#dropped} characters cut \u2026
|
|
65802
65840
|
${this.#tail}` : this.#head + this.#tail;
|
|
65803
65841
|
this.#head = "";
|
|
65804
65842
|
this.#tail = "";
|
|
65805
65843
|
this.#dropped = 0;
|
|
65806
|
-
return
|
|
65844
|
+
return text22;
|
|
65807
65845
|
}
|
|
65808
65846
|
};
|
|
65809
65847
|
var MIN_YIELD_MS = 250;
|
|
@@ -65885,9 +65923,9 @@ var Sessions = class {
|
|
|
65885
65923
|
]) {
|
|
65886
65924
|
const decoder = new StringDecoder("utf8");
|
|
65887
65925
|
stream.on("data", (chunk) => {
|
|
65888
|
-
const
|
|
65889
|
-
pending.add(
|
|
65890
|
-
if (
|
|
65926
|
+
const text22 = decoder.write(chunk);
|
|
65927
|
+
pending.add(text22);
|
|
65928
|
+
if (text22) this.#emit({ type: "output", id, stream: name, text: text22 });
|
|
65891
65929
|
});
|
|
65892
65930
|
}
|
|
65893
65931
|
child.stdin.on("error", () => {
|
|
@@ -65980,6 +66018,7 @@ var refusing = async (work) => {
|
|
|
65980
66018
|
throw new ExecRefused(reason(error62));
|
|
65981
66019
|
}
|
|
65982
66020
|
};
|
|
66021
|
+
var foldersFirst = (a, b) => Number(b.isDirectory()) - Number(a.isDirectory()) || a.name.localeCompare(b.name);
|
|
65983
66022
|
async function inside(root, path) {
|
|
65984
66023
|
const refused2 = () => new ExecRefused(`${path} is outside the workspace.`);
|
|
65985
66024
|
const wanted = resolve(root, path);
|
|
@@ -65994,26 +66033,33 @@ async function inside(root, path) {
|
|
|
65994
66033
|
() => false
|
|
65995
66034
|
))
|
|
65996
66035
|
throw refused2();
|
|
65997
|
-
const above =
|
|
66036
|
+
const above = dirname(at);
|
|
65998
66037
|
if (above === at) throw refused2();
|
|
65999
|
-
return
|
|
66038
|
+
return join4(await real(above), basename(at));
|
|
66000
66039
|
}
|
|
66001
66040
|
};
|
|
66002
66041
|
const found = await real(wanted);
|
|
66003
66042
|
if (found !== root && !found.startsWith(root + sep2)) throw refused2();
|
|
66004
66043
|
return found;
|
|
66005
66044
|
}
|
|
66045
|
+
async function aFile(path, given) {
|
|
66046
|
+
const found = await stat(path);
|
|
66047
|
+
if (found.isDirectory()) throw new ExecRefused(`${given} is a folder.`);
|
|
66048
|
+
if (!found.isFile()) throw new ExecRefused(`${given} isn't a file that can be read.`);
|
|
66049
|
+
return found;
|
|
66050
|
+
}
|
|
66006
66051
|
function createFiles(root) {
|
|
66007
66052
|
const at = (path) => inside(root, path);
|
|
66008
|
-
const
|
|
66053
|
+
const text22 = async (path, given) => {
|
|
66054
|
+
await aFile(path, given);
|
|
66009
66055
|
const bytes = await readFile(path);
|
|
66010
|
-
if (bytes.includes(0)) throw new ExecRefused(`${
|
|
66056
|
+
if (bytes.includes(0)) throw new ExecRefused(`${given} is a binary file.`);
|
|
66011
66057
|
return bytes.toString("utf8");
|
|
66012
66058
|
};
|
|
66013
66059
|
return {
|
|
66014
66060
|
/** Numbered lines of a file. */
|
|
66015
66061
|
read: (input2) => refusing(async () => {
|
|
66016
|
-
const lines = (await
|
|
66062
|
+
const lines = (await text22(await at(input2.path), input2.path)).split("\n");
|
|
66017
66063
|
const first = Math.max(input2.offset ?? 1, 1);
|
|
66018
66064
|
const shown = lines.slice(first - 1, first - 1 + (input2.limit ?? DEFAULT_LINES)).map((line, i) => `${first + i} ${cut(line, MAX_LINE_CHARS)}`);
|
|
66019
66065
|
const last = first + shown.length - 1;
|
|
@@ -66024,13 +66070,11 @@ ${cut(shown.join("\n"), MAX_READ_CHARS)}`;
|
|
|
66024
66070
|
list: (input2) => refusing(async () => {
|
|
66025
66071
|
const folder = await at(input2.path ?? ".");
|
|
66026
66072
|
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
|
-
);
|
|
66073
|
+
entries.sort(foldersFirst);
|
|
66030
66074
|
const lines = await Promise.all(
|
|
66031
66075
|
entries.slice(0, MAX_ENTRIES).map(async (entry) => {
|
|
66032
66076
|
if (entry.isDirectory()) return `${entry.name}/`;
|
|
66033
|
-
const { size } = await lstat(
|
|
66077
|
+
const { size } = await lstat(join4(folder, entry.name));
|
|
66034
66078
|
return `${entry.name} ${size}`;
|
|
66035
66079
|
})
|
|
66036
66080
|
);
|
|
@@ -66050,7 +66094,7 @@ ${cut(shown.join("\n"), MAX_READ_CHARS)}`;
|
|
|
66050
66094
|
});
|
|
66051
66095
|
if (found && found.size > MAX_EDIT_BYTES)
|
|
66052
66096
|
throw new ExecRefused(`${input2.path} is too large to edit here.`);
|
|
66053
|
-
const before = found ? await
|
|
66097
|
+
const before = found ? await text22(path, input2.path) : "";
|
|
66054
66098
|
let after;
|
|
66055
66099
|
if (input2.old_text === "") {
|
|
66056
66100
|
if (before !== "")
|
|
@@ -66067,9 +66111,10 @@ ${cut(shown.join("\n"), MAX_READ_CHARS)}`;
|
|
|
66067
66111
|
after = before.split(input2.old_text).join(input2.new_text);
|
|
66068
66112
|
}
|
|
66069
66113
|
if (after === before) throw new ExecRefused("That edit changes nothing.");
|
|
66070
|
-
const temporary =
|
|
66114
|
+
const temporary = join4(dirname(path), `.agentexec-${randomBytes2(6).toString("hex")}`);
|
|
66071
66115
|
try {
|
|
66072
66116
|
await writeFile(temporary, after, found ? { mode: found.mode } : {});
|
|
66117
|
+
if (found) await chmod(temporary, found.mode & 511);
|
|
66073
66118
|
await rename(temporary, path);
|
|
66074
66119
|
} catch (error62) {
|
|
66075
66120
|
await rm(temporary, { force: true });
|
|
@@ -66084,32 +66129,18 @@ var DEADLINE_MS = 2e4;
|
|
|
66084
66129
|
var MAX_FILE_BYTES = 2 * 1024 * 1024;
|
|
66085
66130
|
var MAX_LINE_CHARS2 = 500;
|
|
66086
66131
|
var DEFAULT_MATCHES = 200;
|
|
66132
|
+
var MAX_CHANGES = 1e3;
|
|
66087
66133
|
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
|
-
};
|
|
66134
|
+
function eachFile(start, visit2) {
|
|
66104
66135
|
const walk = (folder, rules) => {
|
|
66105
66136
|
let here = rules;
|
|
66106
66137
|
try {
|
|
66107
|
-
const own22 =
|
|
66108
|
-
|
|
66138
|
+
const own22 = join22(folder, ".gitignore");
|
|
66139
|
+
if (statSync(own22).isFile())
|
|
66140
|
+
here = [...rules, { base: folder, ignore: (0, import_ignore.default)().add(readFileSync(own22, "utf8")) }];
|
|
66109
66141
|
} catch {
|
|
66110
66142
|
}
|
|
66111
66143
|
for (const entry of readdirSync(folder, { withFileTypes: true })) {
|
|
66112
|
-
if (out.length > limit) return;
|
|
66113
66144
|
if (SKIPPED.has(entry.name) || entry.isSymbolicLink()) continue;
|
|
66114
66145
|
const path = join22(folder, entry.name);
|
|
66115
66146
|
const suffix = entry.isDirectory() ? "/" : "";
|
|
@@ -66118,30 +66149,78 @@ function run(job) {
|
|
|
66118
66149
|
);
|
|
66119
66150
|
if (ignored) continue;
|
|
66120
66151
|
try {
|
|
66121
|
-
|
|
66122
|
-
|
|
66152
|
+
const carryOn = entry.isDirectory() ? walk(path, here) : !entry.isFile() || visit2(path);
|
|
66153
|
+
if (!carryOn) return false;
|
|
66123
66154
|
} catch {
|
|
66124
66155
|
}
|
|
66125
66156
|
}
|
|
66157
|
+
return true;
|
|
66126
66158
|
};
|
|
66127
|
-
|
|
66159
|
+
walk(start, []);
|
|
66160
|
+
}
|
|
66161
|
+
function searched(job) {
|
|
66162
|
+
const start = resolve2(job.root, job.path ?? ".");
|
|
66163
|
+
const pattern = job.regex ? job.pattern : job.pattern.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
66164
|
+
const matcher = new RegExp(pattern, job.ignore_case ? "i" : "");
|
|
66165
|
+
const limit = job.limit ?? DEFAULT_MATCHES;
|
|
66166
|
+
const out = [];
|
|
66167
|
+
const name = (path) => relative(job.root, path) || path;
|
|
66168
|
+
const scan = (file2) => {
|
|
66169
|
+
const found = statSync(file2);
|
|
66170
|
+
if (!found.isFile() || found.size > MAX_FILE_BYTES) return;
|
|
66171
|
+
const bytes = readFileSync(file2);
|
|
66172
|
+
if (bytes.includes(0)) return;
|
|
66173
|
+
bytes.toString("utf8").split("\n").forEach((line, i) => {
|
|
66174
|
+
if (out.length <= limit && matcher.test(line))
|
|
66175
|
+
out.push(`${name(file2)}:${i + 1}:${line.slice(0, MAX_LINE_CHARS2)}`);
|
|
66176
|
+
});
|
|
66177
|
+
};
|
|
66178
|
+
if (statSync(start).isDirectory())
|
|
66179
|
+
eachFile(start, (file2) => {
|
|
66180
|
+
scan(file2);
|
|
66181
|
+
return out.length <= limit;
|
|
66182
|
+
});
|
|
66128
66183
|
else scan(start);
|
|
66129
66184
|
const more = out.length > limit;
|
|
66130
66185
|
return (more ? [...out.slice(0, limit), `\u2026 more than ${limit} matches`] : out).join("\n");
|
|
66131
66186
|
}
|
|
66132
|
-
|
|
66133
|
-
const
|
|
66187
|
+
function changed(job) {
|
|
66188
|
+
const changes = [];
|
|
66189
|
+
let more = false;
|
|
66190
|
+
const newest = () => {
|
|
66191
|
+
changes.sort(
|
|
66192
|
+
(a, b) => b.modifiedAt.localeCompare(a.modifiedAt) || a.path.localeCompare(b.path)
|
|
66193
|
+
);
|
|
66194
|
+
if (changes.length <= MAX_CHANGES) return;
|
|
66195
|
+
more = true;
|
|
66196
|
+
changes.length = MAX_CHANGES;
|
|
66197
|
+
};
|
|
66198
|
+
eachFile(job.root, (file2) => {
|
|
66199
|
+
const { mtime, size } = statSync(file2);
|
|
66200
|
+
if (mtime.getTime() > job.since) {
|
|
66201
|
+
changes.push({
|
|
66202
|
+
path: relative(job.root, file2).split(sep22).join("/"),
|
|
66203
|
+
size,
|
|
66204
|
+
modifiedAt: mtime.toISOString()
|
|
66205
|
+
});
|
|
66206
|
+
if (changes.length >= 2 * MAX_CHANGES) newest();
|
|
66207
|
+
}
|
|
66208
|
+
return true;
|
|
66209
|
+
});
|
|
66210
|
+
newest();
|
|
66211
|
+
return { changes, more };
|
|
66212
|
+
}
|
|
66213
|
+
function onThread(job, what) {
|
|
66134
66214
|
return new Promise((done, failed) => {
|
|
66135
|
-
const
|
|
66136
|
-
const worker = new Worker(fileURLToPath2(import.meta.url), { workerData: workerData2 });
|
|
66215
|
+
const worker = new Worker(fileURLToPath2(import.meta.url), { workerData: job });
|
|
66137
66216
|
const timer = setTimeout(() => {
|
|
66138
66217
|
void worker.terminate();
|
|
66139
|
-
failed(new ExecRefused(
|
|
66218
|
+
failed(new ExecRefused(`${what} took over ${DEADLINE_MS / 1e3} s, so it was stopped.`));
|
|
66140
66219
|
}, DEADLINE_MS);
|
|
66141
|
-
worker.once("message", (
|
|
66220
|
+
worker.once("message", (answer) => {
|
|
66142
66221
|
clearTimeout(timer);
|
|
66143
66222
|
void worker.terminate();
|
|
66144
|
-
done(
|
|
66223
|
+
done(answer);
|
|
66145
66224
|
});
|
|
66146
66225
|
worker.once("error", (error62) => {
|
|
66147
66226
|
clearTimeout(timer);
|
|
@@ -66149,8 +66228,136 @@ async function search(root, input2) {
|
|
|
66149
66228
|
});
|
|
66150
66229
|
});
|
|
66151
66230
|
}
|
|
66152
|
-
|
|
66153
|
-
|
|
66231
|
+
async function search(root, input2) {
|
|
66232
|
+
const path = await inside(root, input2.path ?? ".");
|
|
66233
|
+
return onThread({ ...input2, path, root, agentexecSearch: true }, "That search");
|
|
66234
|
+
}
|
|
66235
|
+
function changedSince(root, since) {
|
|
66236
|
+
return onThread({ root, since, agentexecSearch: true }, "Looking for what changed");
|
|
66237
|
+
}
|
|
66238
|
+
if (!isMainThread && workerData?.agentexecSearch) {
|
|
66239
|
+
const job = workerData;
|
|
66240
|
+
parentPort?.postMessage("since" in job ? changed(job) : searched(job));
|
|
66241
|
+
}
|
|
66242
|
+
var MAX_READ_BYTES = 1024 * 1024;
|
|
66243
|
+
function createHostFiles(root, level) {
|
|
66244
|
+
const uploads = /* @__PURE__ */ new Set();
|
|
66245
|
+
let closes = 0;
|
|
66246
|
+
const files = {
|
|
66247
|
+
list: (path = ".") => refusing(async () => {
|
|
66248
|
+
const folder = await inside(root, path);
|
|
66249
|
+
const found = await readdir2(folder, { withFileTypes: true });
|
|
66250
|
+
found.sort(foldersFirst);
|
|
66251
|
+
const entries = await Promise.all(
|
|
66252
|
+
found.slice(0, MAX_ENTRIES).map(async (entry) => {
|
|
66253
|
+
const own22 = await lstat2(join3(folder, entry.name)).catch(() => void 0);
|
|
66254
|
+
return own22 && {
|
|
66255
|
+
name: entry.name,
|
|
66256
|
+
kind: entry.isDirectory() ? "folder" : entry.isSymbolicLink() ? "link" : "file",
|
|
66257
|
+
size: own22.size,
|
|
66258
|
+
modifiedAt: own22.mtime.toISOString()
|
|
66259
|
+
};
|
|
66260
|
+
})
|
|
66261
|
+
);
|
|
66262
|
+
return {
|
|
66263
|
+
entries: entries.filter((entry) => entry !== void 0),
|
|
66264
|
+
more: found.length > MAX_ENTRIES
|
|
66265
|
+
};
|
|
66266
|
+
}),
|
|
66267
|
+
read: (path, offset, length) => refusing(async () => {
|
|
66268
|
+
if (!Number.isSafeInteger(offset) || offset < 0 || !Number.isSafeInteger(length) || length < 1 || length > MAX_READ_BYTES)
|
|
66269
|
+
throw new ExecRefused("A read starts at 0 or later and takes 1 to 1,048,576 bytes.");
|
|
66270
|
+
const real = await inside(root, path);
|
|
66271
|
+
const found = await aFile(real, path);
|
|
66272
|
+
const file2 = await open2(real, "r");
|
|
66273
|
+
try {
|
|
66274
|
+
const bytes = Buffer.alloc(Math.max(0, Math.min(length, found.size - offset)));
|
|
66275
|
+
const { bytesRead } = await file2.read(bytes, 0, bytes.length, offset);
|
|
66276
|
+
return {
|
|
66277
|
+
bytes: bytes.subarray(0, bytesRead),
|
|
66278
|
+
size: found.size,
|
|
66279
|
+
modifiedAt: found.mtime.toISOString()
|
|
66280
|
+
};
|
|
66281
|
+
} finally {
|
|
66282
|
+
await file2.close();
|
|
66283
|
+
}
|
|
66284
|
+
}),
|
|
66285
|
+
changed: (since) => refusing(async () => {
|
|
66286
|
+
const from = since.getTime();
|
|
66287
|
+
if (Number.isNaN(from)) throw new ExecRefused("That isn't a time.");
|
|
66288
|
+
return changedSince(root, from);
|
|
66289
|
+
}),
|
|
66290
|
+
upload: (path) => refusing(async () => {
|
|
66291
|
+
if (level === "read") throw new ExecRefused("Files here can be read, not written.");
|
|
66292
|
+
const opening = closes;
|
|
66293
|
+
const target = await inside(root, path);
|
|
66294
|
+
const before = await stat2(target).catch((error62) => {
|
|
66295
|
+
if (error62.code === "ENOENT") return void 0;
|
|
66296
|
+
throw error62;
|
|
66297
|
+
});
|
|
66298
|
+
if (before?.isDirectory()) throw new ExecRefused(`${path} is a folder.`);
|
|
66299
|
+
await stat2(dirname2(target));
|
|
66300
|
+
const temporary = join3(dirname2(target), `.agentexec-${randomBytes3(6).toString("hex")}`);
|
|
66301
|
+
const file2 = await open2(temporary, "wx");
|
|
66302
|
+
const discard = async () => {
|
|
66303
|
+
await file2.close().catch(() => {
|
|
66304
|
+
});
|
|
66305
|
+
await rm2(temporary, { force: true });
|
|
66306
|
+
};
|
|
66307
|
+
let ended = false;
|
|
66308
|
+
let written = Promise.resolve();
|
|
66309
|
+
const end2 = () => {
|
|
66310
|
+
if (ended) throw new ExecRefused("That upload has ended.");
|
|
66311
|
+
ended = true;
|
|
66312
|
+
uploads.delete(upload);
|
|
66313
|
+
};
|
|
66314
|
+
const upload = {
|
|
66315
|
+
write: (bytes) => refusing(async () => {
|
|
66316
|
+
if (ended) throw new ExecRefused("That upload has ended.");
|
|
66317
|
+
written = written.then(() => file2.appendFile(bytes));
|
|
66318
|
+
await written;
|
|
66319
|
+
}),
|
|
66320
|
+
finish: () => refusing(async () => {
|
|
66321
|
+
end2();
|
|
66322
|
+
try {
|
|
66323
|
+
await written;
|
|
66324
|
+
await file2.close();
|
|
66325
|
+
await rename2(temporary, target);
|
|
66326
|
+
} catch (error62) {
|
|
66327
|
+
await discard();
|
|
66328
|
+
throw error62;
|
|
66329
|
+
}
|
|
66330
|
+
const after = await stat2(target);
|
|
66331
|
+
return { size: after.size, modifiedAt: after.mtime.toISOString() };
|
|
66332
|
+
}),
|
|
66333
|
+
abort: () => refusing(async () => {
|
|
66334
|
+
if (ended) return;
|
|
66335
|
+
end2();
|
|
66336
|
+
await written.catch(() => {
|
|
66337
|
+
});
|
|
66338
|
+
await discard();
|
|
66339
|
+
})
|
|
66340
|
+
};
|
|
66341
|
+
try {
|
|
66342
|
+
if (before) await file2.chmod(before.mode & 511);
|
|
66343
|
+
if (closes !== opening) throw new ExecRefused("It closed before that upload began.");
|
|
66344
|
+
} catch (error62) {
|
|
66345
|
+
await discard();
|
|
66346
|
+
throw error62;
|
|
66347
|
+
}
|
|
66348
|
+
uploads.add(upload);
|
|
66349
|
+
return upload;
|
|
66350
|
+
})
|
|
66351
|
+
};
|
|
66352
|
+
return {
|
|
66353
|
+
files,
|
|
66354
|
+
/** Give up every upload still open, and any that is opening. */
|
|
66355
|
+
close: async () => {
|
|
66356
|
+
closes++;
|
|
66357
|
+
await Promise.all([...uploads].map((upload) => upload.abort()));
|
|
66358
|
+
}
|
|
66359
|
+
};
|
|
66360
|
+
}
|
|
66154
66361
|
var text = (value) => ({ content: [{ type: "text", text: value }] });
|
|
66155
66362
|
var refused = (error62) => {
|
|
66156
66363
|
if (!(error62 instanceof ExecRefused)) throw error62;
|
|
@@ -66271,7 +66478,7 @@ function createServer2(root, sessions, version22, level) {
|
|
|
66271
66478
|
);
|
|
66272
66479
|
return server;
|
|
66273
66480
|
}
|
|
66274
|
-
var VERSION = "0.0.
|
|
66481
|
+
var VERSION = "0.0.4";
|
|
66275
66482
|
function createAgentExec(options) {
|
|
66276
66483
|
const root = realpathSync(resolve3(options.workspace));
|
|
66277
66484
|
const level = options.level ?? "read";
|
|
@@ -66283,6 +66490,7 @@ function createAgentExec(options) {
|
|
|
66283
66490
|
} catch {
|
|
66284
66491
|
}
|
|
66285
66492
|
});
|
|
66493
|
+
const host = createHostFiles(root, level);
|
|
66286
66494
|
return {
|
|
66287
66495
|
async handle(request, response, body) {
|
|
66288
66496
|
const server = createServer2(root, sessions, VERSION, level);
|
|
@@ -66298,23 +66506,46 @@ function createAgentExec(options) {
|
|
|
66298
66506
|
listeners.add(listener);
|
|
66299
66507
|
return () => void listeners.delete(listener);
|
|
66300
66508
|
},
|
|
66301
|
-
|
|
66509
|
+
files: host.files,
|
|
66510
|
+
async close() {
|
|
66511
|
+
await Promise.all([sessions.stopAll(), host.close()]);
|
|
66512
|
+
}
|
|
66302
66513
|
};
|
|
66303
66514
|
}
|
|
66304
66515
|
|
|
66305
66516
|
// src/screencast.ts
|
|
66517
|
+
var FIRST_FRAME_MS = 500;
|
|
66518
|
+
var RESTARTS = 3;
|
|
66306
66519
|
async function startScreencast(page, onFrame) {
|
|
66307
|
-
|
|
66520
|
+
let seen = false;
|
|
66521
|
+
let stopped = false;
|
|
66522
|
+
const start = () => page.screencast.start({
|
|
66308
66523
|
quality: 70,
|
|
66309
|
-
onFrame: ({ data, viewportWidth, viewportHeight }) =>
|
|
66310
|
-
|
|
66311
|
-
|
|
66524
|
+
onFrame: ({ data, viewportWidth, viewportHeight }) => {
|
|
66525
|
+
seen = true;
|
|
66526
|
+
onFrame({ data, width: viewportWidth, height: viewportHeight });
|
|
66527
|
+
}
|
|
66312
66528
|
});
|
|
66529
|
+
await start();
|
|
66530
|
+
void (async () => {
|
|
66531
|
+
for (let left = RESTARTS; left > 0; left--) {
|
|
66532
|
+
await new Promise((done) => setTimeout(done, FIRST_FRAME_MS).unref());
|
|
66533
|
+
if (seen || stopped) return;
|
|
66534
|
+
await page.screencast.stop().catch(() => {
|
|
66535
|
+
});
|
|
66536
|
+
if (stopped) return;
|
|
66537
|
+
if (!await start().then(() => true).catch(() => false))
|
|
66538
|
+
return;
|
|
66539
|
+
}
|
|
66540
|
+
})();
|
|
66541
|
+
return async () => {
|
|
66542
|
+
stopped = true;
|
|
66543
|
+
await page.screencast.stop().catch(() => {
|
|
66544
|
+
});
|
|
66545
|
+
};
|
|
66313
66546
|
}
|
|
66314
66547
|
|
|
66315
66548
|
// src/browser-live.ts
|
|
66316
|
-
var WAIT_MS = 25e3;
|
|
66317
|
-
var IDLE_MS = 6e4;
|
|
66318
66549
|
var TITLE_CHECK_MS = 1e3;
|
|
66319
66550
|
var STEP_MS = 2e3;
|
|
66320
66551
|
var MAX_BUFFERED = 4 * 1024 * 1024;
|
|
@@ -66322,269 +66553,292 @@ var sleep3 = (ms) => new Promise((done) => setTimeout(done, ms));
|
|
|
66322
66553
|
var within = (work, ms) => Promise.race([work, sleep3(ms).then(() => void 0)]);
|
|
66323
66554
|
var BrowserLive = class {
|
|
66324
66555
|
#endpoint;
|
|
66325
|
-
#
|
|
66326
|
-
#
|
|
66556
|
+
#viewers = /* @__PURE__ */ new Map();
|
|
66557
|
+
#client;
|
|
66327
66558
|
#context;
|
|
66328
66559
|
#ids = /* @__PURE__ */ new Map();
|
|
66329
66560
|
#nextId = 1;
|
|
66330
|
-
#
|
|
66331
|
-
|
|
66332
|
-
#
|
|
66333
|
-
#
|
|
66334
|
-
#
|
|
66335
|
-
|
|
66336
|
-
#
|
|
66337
|
-
|
|
66338
|
-
#
|
|
66339
|
-
#agentWaiting = 0;
|
|
66340
|
-
#answeredChooser = false;
|
|
66561
|
+
#casts = /* @__PURE__ */ new Map();
|
|
66562
|
+
/** A page's cast that is still stopping, which its next one waits for. */
|
|
66563
|
+
#stopping = /* @__PURE__ */ new Map();
|
|
66564
|
+
#dialogs = /* @__PURE__ */ new Map();
|
|
66565
|
+
#choosers = /* @__PURE__ */ new Map();
|
|
66566
|
+
/** The agent's MCP sessions seen so far. */
|
|
66567
|
+
#sessions = /* @__PURE__ */ new Set();
|
|
66568
|
+
/** Those still to be told that a person answered a file chooser. */
|
|
66569
|
+
#owed = /* @__PURE__ */ new Set();
|
|
66341
66570
|
/** The tabs last sent, to send them again only when something about them changed. */
|
|
66342
66571
|
#lastTabs = "";
|
|
66343
66572
|
#titles;
|
|
66344
66573
|
#checking = false;
|
|
66345
|
-
|
|
66574
|
+
/** Counts each time everyone has left, so a connection begun before that knows it is stale. */
|
|
66575
|
+
#ended = 0;
|
|
66346
66576
|
constructor(endpoint) {
|
|
66347
66577
|
this.#endpoint = endpoint;
|
|
66348
66578
|
}
|
|
66349
66579
|
// ---- 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
|
-
}
|
|
66580
|
+
takeAnsweredChooser(session) {
|
|
66581
|
+
this.#sessions.add(session);
|
|
66582
|
+
return this.#owed.delete(session);
|
|
66362
66583
|
}
|
|
66363
|
-
|
|
66364
|
-
|
|
66365
|
-
this.#
|
|
66366
|
-
return answered2;
|
|
66584
|
+
sessionEnded(session) {
|
|
66585
|
+
this.#sessions.delete(session);
|
|
66586
|
+
this.#owed.delete(session);
|
|
66367
66587
|
}
|
|
66368
|
-
|
|
66369
|
-
this.#
|
|
66370
|
-
this.#chooser
|
|
66371
|
-
this.#broadcast({ type: "dialog", open: false });
|
|
66372
|
-
this.#broadcast({ type: "file-chooser", open: false });
|
|
66588
|
+
agentAnsweredChooser() {
|
|
66589
|
+
this.#choosers.clear();
|
|
66590
|
+
for (const ws of this.#viewers.keys()) this.#send(ws, { type: "file-chooser", open: false });
|
|
66373
66591
|
}
|
|
66374
66592
|
// ---- The person's side
|
|
66375
66593
|
open(ws, mode) {
|
|
66376
|
-
|
|
66377
|
-
|
|
66378
|
-
this.#driver = ws;
|
|
66379
|
-
this.#lastInput = Date.now();
|
|
66380
|
-
}
|
|
66594
|
+
const viewer = { mode, chose: false, swallowUp: false, steps: Promise.resolve() };
|
|
66595
|
+
this.#viewers.set(ws, viewer);
|
|
66381
66596
|
ws.on("close", () => this.#left(ws));
|
|
66382
66597
|
ws.on("message", (data, binary) => {
|
|
66383
|
-
if (binary ||
|
|
66598
|
+
if (binary || viewer.mode !== "operate") return;
|
|
66384
66599
|
let message;
|
|
66385
66600
|
try {
|
|
66386
66601
|
message = JSON.parse(data.toString());
|
|
66387
66602
|
} catch {
|
|
66388
66603
|
return;
|
|
66389
66604
|
}
|
|
66390
|
-
this.#input(ws, message);
|
|
66605
|
+
this.#input(ws, viewer, message);
|
|
66391
66606
|
});
|
|
66392
66607
|
this.#begin().then(
|
|
66393
|
-
|
|
66608
|
+
// Unless it chose a tab while this was starting, it is shown the newest.
|
|
66609
|
+
() => viewer.chose ? void 0 : this.#show(ws, this.#newest()),
|
|
66394
66610
|
() => ws.close(1011, "The browser can't be reached.")
|
|
66395
66611
|
);
|
|
66396
66612
|
}
|
|
66397
66613
|
async close() {
|
|
66398
|
-
for (const ws of this.#
|
|
66614
|
+
for (const ws of this.#viewers.keys()) ws.close(1001, "It stopped.");
|
|
66399
66615
|
await this.#end();
|
|
66400
66616
|
}
|
|
66401
|
-
#held() {
|
|
66402
|
-
return this.#driver !== void 0 && Date.now() - this.#lastInput < IDLE_MS;
|
|
66403
|
-
}
|
|
66404
66617
|
#send(ws, message) {
|
|
66405
66618
|
if (ws.readyState === ws.OPEN) ws.send(JSON.stringify(message));
|
|
66406
66619
|
}
|
|
66407
|
-
|
|
66408
|
-
|
|
66620
|
+
/** The sockets shown a page. */
|
|
66621
|
+
#shownTo(page) {
|
|
66622
|
+
return [...this.#viewers].filter(([, viewer]) => viewer.shown === page).map(([ws]) => ws);
|
|
66409
66623
|
}
|
|
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
|
-
});
|
|
66624
|
+
#newest() {
|
|
66625
|
+
return this.#context?.pages().at(-1);
|
|
66418
66626
|
}
|
|
66419
66627
|
async #begin() {
|
|
66420
|
-
this.#
|
|
66421
|
-
const
|
|
66422
|
-
const
|
|
66628
|
+
this.#client ??= (async () => {
|
|
66629
|
+
const ended = this.#ended;
|
|
66630
|
+
const client = await playwright().chromium.connect(await this.#endpoint);
|
|
66631
|
+
const context = client.contexts()[0] ?? await client.newContext();
|
|
66632
|
+
if (ended !== this.#ended) return client;
|
|
66423
66633
|
this.#context = context;
|
|
66424
66634
|
for (const page of context.pages()) this.#track(page);
|
|
66425
66635
|
context.on("page", (page) => {
|
|
66426
66636
|
this.#track(page);
|
|
66427
|
-
|
|
66637
|
+
for (const [ws, viewer] of this.#viewers)
|
|
66638
|
+
if (!viewer.chose || !viewer.shown) void this.#show(ws, page);
|
|
66639
|
+
void this.#tabs();
|
|
66428
66640
|
});
|
|
66429
|
-
|
|
66430
|
-
|
|
66431
|
-
|
|
66641
|
+
if (context.pages().length === 0) await context.newPage();
|
|
66642
|
+
if (ended === this.#ended)
|
|
66643
|
+
this.#titles = setInterval(() => void this.#checkTabs(), TITLE_CHECK_MS);
|
|
66644
|
+
return client;
|
|
66432
66645
|
})();
|
|
66433
|
-
await this.#
|
|
66646
|
+
await this.#client;
|
|
66434
66647
|
}
|
|
66648
|
+
/** Everyone has left. All of it is forgotten at once, so a socket that opens next starts anew. */
|
|
66435
66649
|
async #end() {
|
|
66436
|
-
const
|
|
66437
|
-
|
|
66650
|
+
const client = this.#client;
|
|
66651
|
+
const casts = [...this.#casts.values()];
|
|
66652
|
+
this.#ended++;
|
|
66653
|
+
this.#client = void 0;
|
|
66654
|
+
this.#context = void 0;
|
|
66438
66655
|
clearInterval(this.#titles);
|
|
66439
66656
|
this.#titles = void 0;
|
|
66440
66657
|
this.#lastTabs = "";
|
|
66441
|
-
|
|
66442
|
-
this.#
|
|
66443
|
-
this.#shown = void 0;
|
|
66658
|
+
this.#casts.clear();
|
|
66659
|
+
this.#stopping.clear();
|
|
66444
66660
|
this.#ids.clear();
|
|
66445
|
-
|
|
66661
|
+
this.#dialogs.clear();
|
|
66662
|
+
this.#choosers.clear();
|
|
66663
|
+
await Promise.all(casts.map((cast) => cast.stop.then((stop) => stop?.()).catch(() => {
|
|
66664
|
+
})));
|
|
66665
|
+
await (await client?.catch(() => void 0))?.close().catch(() => {
|
|
66446
66666
|
});
|
|
66447
66667
|
}
|
|
66448
66668
|
#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();
|
|
66669
|
+
const shown = this.#viewers.get(ws)?.shown;
|
|
66670
|
+
this.#viewers.delete(ws);
|
|
66671
|
+
if (this.#viewers.size === 0) return void this.#end();
|
|
66672
|
+
if (shown) void this.#uncast(shown);
|
|
66469
66673
|
}
|
|
66470
66674
|
#track(page) {
|
|
66471
66675
|
if (this.#ids.has(page)) return;
|
|
66472
66676
|
this.#ids.set(page, this.#nextId++);
|
|
66473
66677
|
page.on("dialog", (dialog) => {
|
|
66474
|
-
this.#
|
|
66475
|
-
this.#
|
|
66476
|
-
|
|
66477
|
-
|
|
66478
|
-
|
|
66479
|
-
|
|
66480
|
-
...dialog.defaultValue() ? { defaultValue: dialog.defaultValue() } : {}
|
|
66481
|
-
});
|
|
66678
|
+
this.#dialogs.set(page, dialog);
|
|
66679
|
+
for (const ws of this.#shownTo(page)) this.#tellDialog(ws, page);
|
|
66680
|
+
});
|
|
66681
|
+
page.on("dialogclosed", () => {
|
|
66682
|
+
this.#dialogs.delete(page);
|
|
66683
|
+
for (const ws of this.#shownTo(page)) this.#tellDialog(ws, page);
|
|
66482
66684
|
});
|
|
66483
66685
|
page.on("filechooser", (chooser) => {
|
|
66484
|
-
this.#
|
|
66485
|
-
|
|
66686
|
+
this.#choosers.set(page, chooser);
|
|
66687
|
+
for (const ws of this.#shownTo(page)) this.#tellChooser(ws, page);
|
|
66486
66688
|
});
|
|
66487
66689
|
page.on("framenavigated", (frame) => {
|
|
66488
66690
|
if (frame === page.mainFrame()) void this.#tabs();
|
|
66489
66691
|
});
|
|
66490
66692
|
page.on("close", () => {
|
|
66491
66693
|
this.#ids.delete(page);
|
|
66492
|
-
|
|
66493
|
-
|
|
66694
|
+
this.#dialogs.delete(page);
|
|
66695
|
+
this.#choosers.delete(page);
|
|
66696
|
+
this.#casts.delete(page);
|
|
66697
|
+
this.#stopping.delete(page);
|
|
66698
|
+
for (const ws of this.#shownTo(page)) void this.#show(ws, this.#newest());
|
|
66699
|
+
void this.#tabs();
|
|
66494
66700
|
});
|
|
66495
66701
|
}
|
|
66496
|
-
|
|
66497
|
-
|
|
66498
|
-
|
|
66499
|
-
|
|
66500
|
-
|
|
66501
|
-
|
|
66502
|
-
|
|
66702
|
+
#tellDialog(ws, page) {
|
|
66703
|
+
const dialog = this.#dialogs.get(page);
|
|
66704
|
+
this.#send(
|
|
66705
|
+
ws,
|
|
66706
|
+
dialog ? {
|
|
66707
|
+
type: "dialog",
|
|
66708
|
+
open: true,
|
|
66709
|
+
kind: dialog.type(),
|
|
66710
|
+
message: dialog.message(),
|
|
66711
|
+
...dialog.defaultValue() ? { defaultValue: dialog.defaultValue() } : {}
|
|
66712
|
+
} : { type: "dialog", open: false }
|
|
66713
|
+
);
|
|
66714
|
+
}
|
|
66715
|
+
#tellChooser(ws, page) {
|
|
66716
|
+
const chooser = this.#choosers.get(page);
|
|
66717
|
+
this.#send(
|
|
66718
|
+
ws,
|
|
66719
|
+
chooser ? { type: "file-chooser", open: true, multiple: chooser.isMultiple() } : { type: "file-chooser", open: false }
|
|
66720
|
+
);
|
|
66721
|
+
}
|
|
66722
|
+
/** The cast of a page, started if no socket was shown it yet. */
|
|
66723
|
+
#cast(page) {
|
|
66724
|
+
const running = this.#casts.get(page);
|
|
66725
|
+
if (running) return running;
|
|
66726
|
+
const onFrame = (frame) => {
|
|
66727
|
+
const watching = this.#shownTo(page);
|
|
66728
|
+
if (frame.width !== cast.size.width || frame.height !== cast.size.height) {
|
|
66729
|
+
cast.size = { width: frame.width, height: frame.height };
|
|
66730
|
+
for (const ws of watching) this.#send(ws, { type: "viewport", ...cast.size });
|
|
66731
|
+
}
|
|
66732
|
+
cast.last = frame.data;
|
|
66733
|
+
for (const ws of watching)
|
|
66734
|
+
if (ws.readyState === ws.OPEN && ws.bufferedAmount < MAX_BUFFERED) ws.send(frame.data);
|
|
66735
|
+
};
|
|
66736
|
+
const cast = {
|
|
66737
|
+
size: { width: 0, height: 0 },
|
|
66738
|
+
// A page has one screencast at a time, so this one starts once the last has stopped.
|
|
66739
|
+
stop: (this.#stopping.get(page) ?? Promise.resolve()).then(() => startScreencast(page, onFrame)).catch(() => void 0)
|
|
66740
|
+
};
|
|
66741
|
+
this.#casts.set(page, cast);
|
|
66742
|
+
return cast;
|
|
66743
|
+
}
|
|
66744
|
+
/** Stop casting a page no socket is shown any more. */
|
|
66745
|
+
async #uncast(page) {
|
|
66746
|
+
const cast = this.#casts.get(page);
|
|
66747
|
+
if (!cast || this.#shownTo(page).length > 0) return;
|
|
66748
|
+
this.#casts.delete(page);
|
|
66749
|
+
const stopped = cast.stop.then((stop) => stop?.()).catch(() => {
|
|
66750
|
+
});
|
|
66751
|
+
this.#stopping.set(page, stopped);
|
|
66752
|
+
await stopped;
|
|
66753
|
+
if (this.#stopping.get(page) === stopped) this.#stopping.delete(page);
|
|
66754
|
+
}
|
|
66755
|
+
/** Change the page a socket is shown, and tell it what that page is waiting on. */
|
|
66756
|
+
async #show(ws, page) {
|
|
66757
|
+
const viewer = this.#viewers.get(ws);
|
|
66758
|
+
if (!viewer || page && viewer.shown === page) return;
|
|
66759
|
+
const before = viewer.shown;
|
|
66760
|
+
viewer.shown = page;
|
|
66761
|
+
viewer.choice = void 0;
|
|
66762
|
+
viewer.swallowUp = false;
|
|
66763
|
+
if (before) await this.#uncast(before);
|
|
66764
|
+
if (this.#viewers.get(ws) !== viewer || viewer.shown !== page) return;
|
|
66503
66765
|
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);
|
|
66766
|
+
const cast = this.#cast(page);
|
|
66767
|
+
if (cast.size.width) this.#send(ws, { type: "viewport", ...cast.size });
|
|
66768
|
+
if (cast.last && ws.readyState === ws.OPEN) ws.send(cast.last);
|
|
66769
|
+
this.#tellDialog(ws, page);
|
|
66770
|
+
this.#tellChooser(ws, page);
|
|
66512
66771
|
}
|
|
66513
|
-
await this.#tabs();
|
|
66772
|
+
await this.#tabs(ws);
|
|
66514
66773
|
}
|
|
66515
66774
|
async #tabList() {
|
|
66516
|
-
|
|
66775
|
+
return Promise.all(
|
|
66517
66776
|
[...this.#ids].map(async ([page, id]) => ({
|
|
66518
66777
|
id,
|
|
66519
66778
|
url: page.url(),
|
|
66520
66779
|
title: await within(page.title(), STEP_MS).catch(() => void 0) ?? ""
|
|
66521
66780
|
}))
|
|
66522
66781
|
);
|
|
66523
|
-
return { type: "tabs", tabs, shown: (this.#shown && this.#ids.get(this.#shown)) ?? null };
|
|
66524
66782
|
}
|
|
66525
|
-
|
|
66526
|
-
|
|
66527
|
-
|
|
66528
|
-
if (only) this.#
|
|
66529
|
-
|
|
66783
|
+
/** The tabs, to one socket or to all, each told which one it is shown. */
|
|
66784
|
+
async #tabs(only, listed) {
|
|
66785
|
+
const tabs = listed ?? await this.#tabList();
|
|
66786
|
+
if (!only) this.#lastTabs = JSON.stringify(tabs);
|
|
66787
|
+
for (const [ws, viewer] of this.#viewers)
|
|
66788
|
+
if (!only || ws === only)
|
|
66789
|
+
this.#send(ws, {
|
|
66790
|
+
type: "tabs",
|
|
66791
|
+
tabs,
|
|
66792
|
+
shown: (viewer.shown && this.#ids.get(viewer.shown)) ?? null
|
|
66793
|
+
});
|
|
66530
66794
|
}
|
|
66531
66795
|
/** Send the tabs again if anything about them changed, such as a title. */
|
|
66532
66796
|
async #checkTabs() {
|
|
66533
66797
|
if (this.#checking) return;
|
|
66534
66798
|
this.#checking = true;
|
|
66535
66799
|
try {
|
|
66536
|
-
const
|
|
66537
|
-
|
|
66538
|
-
if (text2 === this.#lastTabs) return;
|
|
66539
|
-
this.#lastTabs = text2;
|
|
66540
|
-
this.#broadcast(message);
|
|
66800
|
+
const tabs = await this.#tabList();
|
|
66801
|
+
if (JSON.stringify(tabs) !== this.#lastTabs) await this.#tabs(void 0, tabs);
|
|
66541
66802
|
} finally {
|
|
66542
66803
|
this.#checking = false;
|
|
66543
66804
|
}
|
|
66544
66805
|
}
|
|
66545
66806
|
/** Each step in order, none waiting on another for long: a dialog blocks the page's input. */
|
|
66546
|
-
#step(work) {
|
|
66547
|
-
|
|
66807
|
+
#step(viewer, work) {
|
|
66808
|
+
viewer.steps = viewer.steps.then(() => within(work(), STEP_MS)).catch(() => {
|
|
66548
66809
|
});
|
|
66549
66810
|
}
|
|
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;
|
|
66811
|
+
#input(ws, viewer, message) {
|
|
66812
|
+
const page = viewer.shown;
|
|
66560
66813
|
switch (message.type) {
|
|
66561
66814
|
case "mouse":
|
|
66562
66815
|
if (!page) return;
|
|
66563
|
-
return this.#step(() => this.#mouse(page, message));
|
|
66816
|
+
return this.#step(viewer, () => this.#mouse(ws, viewer, page, message));
|
|
66564
66817
|
case "key":
|
|
66565
66818
|
if (!page) return;
|
|
66566
66819
|
return this.#step(
|
|
66820
|
+
viewer,
|
|
66567
66821
|
() => message.action === "down" ? page.keyboard.down(message.key) : page.keyboard.up(message.key)
|
|
66568
66822
|
);
|
|
66569
66823
|
case "text":
|
|
66570
66824
|
if (!page) return;
|
|
66571
|
-
return this.#step(() => page.keyboard.insertText(message.text));
|
|
66825
|
+
return this.#step(viewer, () => page.keyboard.insertText(message.text));
|
|
66572
66826
|
case "dialog": {
|
|
66573
|
-
const dialog = this.#
|
|
66574
|
-
this.#dialog = void 0;
|
|
66575
|
-
this.#broadcast({ type: "dialog", open: false });
|
|
66827
|
+
const dialog = page && this.#dialogs.get(page);
|
|
66576
66828
|
if (!dialog) return;
|
|
66577
66829
|
return this.#step(
|
|
66830
|
+
viewer,
|
|
66578
66831
|
() => message.accept ? dialog.accept(message.text ?? void 0) : dialog.dismiss()
|
|
66579
66832
|
);
|
|
66580
66833
|
}
|
|
66581
66834
|
case "files": {
|
|
66582
|
-
const chooser = this.#
|
|
66583
|
-
|
|
66584
|
-
this.#
|
|
66585
|
-
|
|
66586
|
-
this.#
|
|
66835
|
+
const chooser = page && this.#choosers.get(page);
|
|
66836
|
+
if (!page || !chooser) return;
|
|
66837
|
+
this.#choosers.delete(page);
|
|
66838
|
+
for (const socket of this.#shownTo(page)) this.#tellChooser(socket, page);
|
|
66839
|
+
for (const session of this.#sessions) this.#owed.add(session);
|
|
66587
66840
|
return this.#step(
|
|
66841
|
+
viewer,
|
|
66588
66842
|
() => chooser.setFiles(
|
|
66589
66843
|
message.files.map((file2) => ({
|
|
66590
66844
|
name: file2.name,
|
|
@@ -66595,16 +66849,16 @@ var BrowserLive = class {
|
|
|
66595
66849
|
);
|
|
66596
66850
|
}
|
|
66597
66851
|
case "choice": {
|
|
66598
|
-
const choice =
|
|
66599
|
-
|
|
66852
|
+
const choice = viewer.choice;
|
|
66853
|
+
viewer.choice = void 0;
|
|
66600
66854
|
if (!choice) return;
|
|
66601
|
-
return this.#step(() => choice.selectOption(message.values));
|
|
66855
|
+
return this.#step(viewer, () => choice.selectOption(message.values));
|
|
66602
66856
|
}
|
|
66603
66857
|
case "tab":
|
|
66604
|
-
return this.#step(() => this.#tab(message));
|
|
66858
|
+
return this.#step(viewer, () => this.#tab(ws, viewer, message));
|
|
66605
66859
|
case "navigate":
|
|
66606
66860
|
if (!page) return;
|
|
66607
|
-
return this.#step(async () => {
|
|
66861
|
+
return this.#step(viewer, async () => {
|
|
66608
66862
|
if (message.to === "back") await page.goBack();
|
|
66609
66863
|
else if (message.to === "forward") await page.goForward();
|
|
66610
66864
|
else if (message.to === "reload") await page.reload();
|
|
@@ -66612,15 +66866,15 @@ var BrowserLive = class {
|
|
|
66612
66866
|
});
|
|
66613
66867
|
}
|
|
66614
66868
|
}
|
|
66615
|
-
async #mouse(page, message) {
|
|
66869
|
+
async #mouse(ws, viewer, page, message) {
|
|
66616
66870
|
const { x, y } = message;
|
|
66617
66871
|
if (message.action === "move") return page.mouse.move(x, y);
|
|
66618
66872
|
if (message.action === "wheel")
|
|
66619
66873
|
return page.mouse.wheel(message.deltaX ?? 0, message.deltaY ?? 0);
|
|
66620
66874
|
const button = message.button ?? "left";
|
|
66621
66875
|
if (message.action === "up") {
|
|
66622
|
-
if (
|
|
66623
|
-
|
|
66876
|
+
if (viewer.swallowUp) {
|
|
66877
|
+
viewer.swallowUp = false;
|
|
66624
66878
|
return;
|
|
66625
66879
|
}
|
|
66626
66880
|
return page.mouse.up({ button });
|
|
@@ -66646,20 +66900,29 @@ var BrowserLive = class {
|
|
|
66646
66900
|
([px, py]) => document.elementFromPoint(px, py),
|
|
66647
66901
|
[x, y]
|
|
66648
66902
|
);
|
|
66649
|
-
|
|
66650
|
-
|
|
66651
|
-
return this.#
|
|
66903
|
+
viewer.choice = handle.asElement() ?? void 0;
|
|
66904
|
+
viewer.swallowUp = true;
|
|
66905
|
+
return this.#send(ws, { type: "choice", ...choice });
|
|
66652
66906
|
}
|
|
66653
66907
|
}
|
|
66654
66908
|
await page.mouse.move(x, y);
|
|
66655
66909
|
return page.mouse.down({ button });
|
|
66656
66910
|
}
|
|
66657
|
-
|
|
66911
|
+
/** Showing or opening a tab makes the socket's page its own from then on. */
|
|
66912
|
+
async #tab(ws, viewer, message) {
|
|
66658
66913
|
const byId = (id) => [...this.#ids].find(([, known]) => known === id)?.[0];
|
|
66659
|
-
if (message.action === "show") return this.#show(byId(message.id));
|
|
66660
66914
|
if (message.action === "close") return byId(message.id)?.close();
|
|
66915
|
+
if (message.action === "show") {
|
|
66916
|
+
const page2 = byId(message.id);
|
|
66917
|
+
if (!page2) return;
|
|
66918
|
+
viewer.chose = true;
|
|
66919
|
+
return this.#show(ws, page2);
|
|
66920
|
+
}
|
|
66661
66921
|
const page = await this.#context?.newPage();
|
|
66662
|
-
if (page
|
|
66922
|
+
if (!page) return;
|
|
66923
|
+
viewer.chose = true;
|
|
66924
|
+
await this.#show(ws, page);
|
|
66925
|
+
if (message.url) await page.goto(message.url).catch(() => {
|
|
66663
66926
|
});
|
|
66664
66927
|
}
|
|
66665
66928
|
};
|
|
@@ -66667,7 +66930,7 @@ var BrowserLive = class {
|
|
|
66667
66930
|
// src/live.ts
|
|
66668
66931
|
import { chmodSync, existsSync, statSync as statSync2 } from "node:fs";
|
|
66669
66932
|
import { createRequire as createRequire3 } from "node:module";
|
|
66670
|
-
import { dirname as dirname3, join as
|
|
66933
|
+
import { dirname as dirname3, join as join5 } from "node:path";
|
|
66671
66934
|
|
|
66672
66935
|
// node_modules/ws/wrapper.mjs
|
|
66673
66936
|
var import_stream2 = __toESM(require_stream(), 1);
|
|
@@ -66679,10 +66942,178 @@ var import_subprotocol = __toESM(require_subprotocol(), 1);
|
|
|
66679
66942
|
var import_websocket = __toESM(require_websocket(), 1);
|
|
66680
66943
|
var import_websocket_server = __toESM(require_websocket_server(), 1);
|
|
66681
66944
|
|
|
66945
|
+
// src/files-live.ts
|
|
66946
|
+
var MAX_PART = 1024 * 1024;
|
|
66947
|
+
var MAX_BUFFERED2 = 4 * 1024 * 1024;
|
|
66948
|
+
var MAX_UPLOADS = 64;
|
|
66949
|
+
var reason2 = (error62) => error62 instanceof Error ? error62.message : String(error62);
|
|
66950
|
+
var text2 = (value) => typeof value === "string";
|
|
66951
|
+
var whole = (value) => Number.isSafeInteger(value);
|
|
66952
|
+
var sleep4 = (ms) => new Promise((done) => setTimeout(done, ms));
|
|
66953
|
+
var FilesLive = class {
|
|
66954
|
+
#files;
|
|
66955
|
+
#sockets = /* @__PURE__ */ new Set();
|
|
66956
|
+
/** One walk of the workspace at a time, however many ask. */
|
|
66957
|
+
#walking = Promise.resolve();
|
|
66958
|
+
/** How many files are part written, over every socket. */
|
|
66959
|
+
#uploads = 0;
|
|
66960
|
+
constructor(files) {
|
|
66961
|
+
this.#files = files;
|
|
66962
|
+
}
|
|
66963
|
+
close() {
|
|
66964
|
+
for (const ws of this.#sockets) ws.close(1001, "It stopped.");
|
|
66965
|
+
}
|
|
66966
|
+
open(ws, mode) {
|
|
66967
|
+
this.#sockets.add(ws);
|
|
66968
|
+
const uploads = /* @__PURE__ */ new Map();
|
|
66969
|
+
let awaited;
|
|
66970
|
+
let writing = Promise.resolve();
|
|
66971
|
+
let waiting = 0;
|
|
66972
|
+
let reading = Promise.resolve();
|
|
66973
|
+
let closed = false;
|
|
66974
|
+
const send2 = (answer) => {
|
|
66975
|
+
if (ws.readyState === ws.OPEN) ws.send(JSON.stringify(answer));
|
|
66976
|
+
};
|
|
66977
|
+
const refuse = (id, message) => send2({ type: "refused", id, message });
|
|
66978
|
+
const giveUp = async (path) => {
|
|
66979
|
+
const had = uploads.get(path);
|
|
66980
|
+
if (!had) return;
|
|
66981
|
+
uploads.delete(path);
|
|
66982
|
+
this.#uploads--;
|
|
66983
|
+
await had.upload.abort().catch(() => {
|
|
66984
|
+
});
|
|
66985
|
+
};
|
|
66986
|
+
const drained = async () => {
|
|
66987
|
+
while (!closed && ws.bufferedAmount > MAX_BUFFERED2) await sleep4(50);
|
|
66988
|
+
};
|
|
66989
|
+
const writePart = async (write, bytes) => {
|
|
66990
|
+
const { id, path, offset, last } = write;
|
|
66991
|
+
if (bytes.length !== write.length)
|
|
66992
|
+
throw new Error(`It said ${write.length} bytes, and ${bytes.length} came.`);
|
|
66993
|
+
if (offset === 0) {
|
|
66994
|
+
await giveUp(path);
|
|
66995
|
+
if (this.#uploads >= MAX_UPLOADS)
|
|
66996
|
+
throw new Error(`${MAX_UPLOADS} files are part written here. Finish one first.`);
|
|
66997
|
+
this.#uploads++;
|
|
66998
|
+
try {
|
|
66999
|
+
uploads.set(path, { upload: await this.#files.upload(path), size: 0 });
|
|
67000
|
+
} catch (error62) {
|
|
67001
|
+
this.#uploads--;
|
|
67002
|
+
throw error62;
|
|
67003
|
+
}
|
|
67004
|
+
}
|
|
67005
|
+
const file2 = uploads.get(path);
|
|
67006
|
+
if (!file2 || file2.size !== offset)
|
|
67007
|
+
throw new Error(`The next part of ${path} starts at ${file2?.size ?? 0}.`);
|
|
67008
|
+
await file2.upload.write(bytes);
|
|
67009
|
+
file2.size += bytes.length;
|
|
67010
|
+
if (!last) return void 0;
|
|
67011
|
+
uploads.delete(path);
|
|
67012
|
+
this.#uploads--;
|
|
67013
|
+
return { type: "written", id, path, size: (await file2.upload.finish()).size };
|
|
67014
|
+
};
|
|
67015
|
+
const inTurn = (work) => {
|
|
67016
|
+
const done = reading.then(async () => {
|
|
67017
|
+
await drained();
|
|
67018
|
+
if (!closed) await work();
|
|
67019
|
+
});
|
|
67020
|
+
reading = done.catch(() => {
|
|
67021
|
+
});
|
|
67022
|
+
return done;
|
|
67023
|
+
};
|
|
67024
|
+
const handle = async (request) => {
|
|
67025
|
+
const { id } = request;
|
|
67026
|
+
switch (request.type) {
|
|
67027
|
+
case "list": {
|
|
67028
|
+
const path = request.path ?? ".";
|
|
67029
|
+
if (!text2(path)) throw new Error("A path is text.");
|
|
67030
|
+
return inTurn(
|
|
67031
|
+
async () => send2({ type: "entries", id, path, ...await this.#files.list(path) })
|
|
67032
|
+
);
|
|
67033
|
+
}
|
|
67034
|
+
case "read": {
|
|
67035
|
+
const { path, offset, length } = request;
|
|
67036
|
+
if (!text2(path)) throw new Error("A path is text.");
|
|
67037
|
+
return inTurn(async () => {
|
|
67038
|
+
const { bytes, size, modifiedAt } = await this.#files.read(path, offset, length);
|
|
67039
|
+
if (ws.readyState !== ws.OPEN) return;
|
|
67040
|
+
send2({ type: "data", id, path, offset, length: bytes.length, size, modifiedAt });
|
|
67041
|
+
ws.send(bytes);
|
|
67042
|
+
});
|
|
67043
|
+
}
|
|
67044
|
+
case "changed": {
|
|
67045
|
+
const { since } = request;
|
|
67046
|
+
if (!text2(since)) throw new Error("That isn't a time.");
|
|
67047
|
+
const walked = this.#walking.then(
|
|
67048
|
+
() => ws.readyState === ws.OPEN ? this.#files.changed(new Date(since)) : void 0
|
|
67049
|
+
);
|
|
67050
|
+
this.#walking = walked.catch(() => {
|
|
67051
|
+
});
|
|
67052
|
+
const found = await walked;
|
|
67053
|
+
await drained();
|
|
67054
|
+
if (found) send2({ type: "changes", id, since, ...found });
|
|
67055
|
+
return;
|
|
67056
|
+
}
|
|
67057
|
+
case "write": {
|
|
67058
|
+
awaited = "refused";
|
|
67059
|
+
if (mode !== "operate") throw new Error("This socket watches: it can't write.");
|
|
67060
|
+
const { path, offset, length, last } = request;
|
|
67061
|
+
if (!text2(path) || !whole(offset) || offset < 0 || !whole(length) || length < 0 || length > MAX_PART || typeof last !== "boolean")
|
|
67062
|
+
throw new Error(
|
|
67063
|
+
"A write names a path, where it starts, 0 to 1,048,576 bytes, and last."
|
|
67064
|
+
);
|
|
67065
|
+
awaited = request;
|
|
67066
|
+
return;
|
|
67067
|
+
}
|
|
67068
|
+
default:
|
|
67069
|
+
throw new Error("That isn't something files@1 does.");
|
|
67070
|
+
}
|
|
67071
|
+
};
|
|
67072
|
+
ws.on("close", () => {
|
|
67073
|
+
closed = true;
|
|
67074
|
+
this.#sockets.delete(ws);
|
|
67075
|
+
void writing.finally(() => Promise.all([...uploads.keys()].map(giveUp)));
|
|
67076
|
+
});
|
|
67077
|
+
ws.on("message", (data, binary) => {
|
|
67078
|
+
const write = awaited;
|
|
67079
|
+
awaited = void 0;
|
|
67080
|
+
if (binary) {
|
|
67081
|
+
if (write === "refused") return;
|
|
67082
|
+
if (!write) return ws.close(1008, "Those bytes belong to no write.");
|
|
67083
|
+
waiting++;
|
|
67084
|
+
ws.pause();
|
|
67085
|
+
writing = writing.then(() => closed ? void 0 : writePart(write, data)).then(
|
|
67086
|
+
(done) => done && send2(done),
|
|
67087
|
+
async (error62) => {
|
|
67088
|
+
await giveUp(write.path);
|
|
67089
|
+
refuse(write.id, reason2(error62));
|
|
67090
|
+
}
|
|
67091
|
+
).finally(() => {
|
|
67092
|
+
if (--waiting === 0) ws.resume();
|
|
67093
|
+
});
|
|
67094
|
+
return;
|
|
67095
|
+
}
|
|
67096
|
+
if (write && write !== "refused") refuse(write.id, "Its bytes didn't follow it.");
|
|
67097
|
+
let request;
|
|
67098
|
+
try {
|
|
67099
|
+
request = JSON.parse(data.toString());
|
|
67100
|
+
} catch {
|
|
67101
|
+
return refuse("", "That isn't JSON.");
|
|
67102
|
+
}
|
|
67103
|
+
const { id, type } = request ?? {};
|
|
67104
|
+
if (type === "write") awaited = "refused";
|
|
67105
|
+
if (!text2(id) || id.length < 1 || id.length > 64)
|
|
67106
|
+
return refuse("", "Every request has an id of 1 to 64 characters.");
|
|
67107
|
+
handle(request).catch((error62) => refuse(id, reason2(error62)));
|
|
67108
|
+
});
|
|
67109
|
+
}
|
|
67110
|
+
};
|
|
67111
|
+
|
|
66682
67112
|
// src/live.ts
|
|
66683
67113
|
var PROTOCOLS = {
|
|
66684
67114
|
commands: "commands@1",
|
|
66685
67115
|
terminal: "pty@1",
|
|
67116
|
+
files: "files@1",
|
|
66686
67117
|
browser: "screencast@1"
|
|
66687
67118
|
};
|
|
66688
67119
|
var MAX_EVENTS = 2e3;
|
|
@@ -66691,20 +67122,21 @@ var OUTPUT = 48;
|
|
|
66691
67122
|
var INPUT = 48;
|
|
66692
67123
|
var RESIZE = 49;
|
|
66693
67124
|
var sockets = new import_websocket_server.default({ noServer: true });
|
|
67125
|
+
var fileSockets = new import_websocket_server.default({ noServer: true, maxPayload: MAX_PART });
|
|
66694
67126
|
var pty;
|
|
66695
67127
|
var loadPty = () => {
|
|
66696
67128
|
pty ??= (async () => {
|
|
66697
|
-
const
|
|
67129
|
+
const require3 = createRequire3(import.meta.url);
|
|
66698
67130
|
let loaded2;
|
|
66699
67131
|
try {
|
|
66700
|
-
loaded2 =
|
|
67132
|
+
loaded2 = require3("node-pty");
|
|
66701
67133
|
} catch {
|
|
66702
67134
|
return "The terminal needs node-pty, which isn't installed with this service.";
|
|
66703
67135
|
}
|
|
66704
|
-
const root = dirname3(
|
|
67136
|
+
const root = dirname3(require3.resolve("node-pty/package.json"));
|
|
66705
67137
|
for (const helper of [
|
|
66706
|
-
|
|
66707
|
-
|
|
67138
|
+
join5(root, "prebuilds", `${process.platform}-${process.arch}`, "spawn-helper"),
|
|
67139
|
+
join5(root, "build", "Release", "spawn-helper")
|
|
66708
67140
|
])
|
|
66709
67141
|
try {
|
|
66710
67142
|
if (existsSync(helper) && (statSync2(helper).mode & 73) === 0) chmodSync(helper, 493);
|
|
@@ -66724,9 +67156,14 @@ var Live = class {
|
|
|
66724
67156
|
#shell;
|
|
66725
67157
|
#scrollback = "";
|
|
66726
67158
|
#browser;
|
|
66727
|
-
|
|
67159
|
+
#files;
|
|
67160
|
+
/** Whether a person may write files here: not where the agent can only read them. */
|
|
67161
|
+
#writes;
|
|
67162
|
+
constructor(exec, workspace, access, browser) {
|
|
66728
67163
|
this.#workspace = workspace;
|
|
66729
|
-
this.#terminal = terminal && exec !== void 0;
|
|
67164
|
+
this.#terminal = access === "terminal" && exec !== void 0;
|
|
67165
|
+
this.#files = exec && new FilesLive(exec.files);
|
|
67166
|
+
this.#writes = access !== "read";
|
|
66730
67167
|
this.#browser = browser;
|
|
66731
67168
|
exec?.onCommand((event) => this.#heard(event));
|
|
66732
67169
|
}
|
|
@@ -66744,6 +67181,8 @@ var Live = class {
|
|
|
66744
67181
|
}
|
|
66745
67182
|
);
|
|
66746
67183
|
}
|
|
67184
|
+
if (this.#files)
|
|
67185
|
+
surfaces.push({ surface: "files", protocol: PROTOCOLS.files, operate: this.#writes });
|
|
66747
67186
|
if (this.#browser)
|
|
66748
67187
|
surfaces.push({ surface: "browser", protocol: PROTOCOLS.browser, operate: true });
|
|
66749
67188
|
return surfaces;
|
|
@@ -66751,9 +67190,11 @@ var Live = class {
|
|
|
66751
67190
|
/** Take an upgraded request as a socket on one surface. */
|
|
66752
67191
|
open(surface, mode, request, socket, head) {
|
|
66753
67192
|
socket.on("error", () => socket.destroy());
|
|
66754
|
-
|
|
67193
|
+
const server = surface === "files" ? fileSockets : sockets;
|
|
67194
|
+
server.handleUpgrade(request, socket, head, (ws) => {
|
|
66755
67195
|
ws.on("error", () => ws.terminate());
|
|
66756
67196
|
if (surface === "commands") this.#watch(ws);
|
|
67197
|
+
else if (surface === "files" && this.#files) this.#files.open(ws, mode);
|
|
66757
67198
|
else if (surface === "browser" && this.#browser) this.#browser.open(ws, mode);
|
|
66758
67199
|
else if (surface === "terminal") this.#attach(ws, mode).catch(() => ws.terminate());
|
|
66759
67200
|
else ws.close(1011, `There's no ${surface} here.`);
|
|
@@ -66761,6 +67202,7 @@ var Live = class {
|
|
|
66761
67202
|
}
|
|
66762
67203
|
close() {
|
|
66763
67204
|
void this.#browser?.close();
|
|
67205
|
+
this.#files?.close();
|
|
66764
67206
|
for (const ws of [...this.#watchers, ...this.#shells.keys()]) ws.close(1001, "It stopped.");
|
|
66765
67207
|
this.#shell?.kill();
|
|
66766
67208
|
this.#shell = void 0;
|
|
@@ -66826,14 +67268,14 @@ var Live = class {
|
|
|
66826
67268
|
};
|
|
66827
67269
|
|
|
66828
67270
|
// src/store.ts
|
|
66829
|
-
import { createHash, randomBytes as
|
|
67271
|
+
import { createHash, randomBytes as randomBytes4, timingSafeEqual } from "node:crypto";
|
|
66830
67272
|
import { chmodSync as chmodSync2, mkdirSync, readFileSync as readFileSync2, renameSync, writeFileSync } from "node:fs";
|
|
66831
|
-
import { join as
|
|
66832
|
-
var newToken = () =>
|
|
67273
|
+
import { join as join6 } from "node:path";
|
|
67274
|
+
var newToken = () => randomBytes4(32).toString("base64url");
|
|
66833
67275
|
var digestOf = (token) => createHash("sha256").update(token).digest("hex");
|
|
66834
67276
|
var sameToken = (token, digest) => timingSafeEqual(Buffer.from(digestOf(token), "hex"), Buffer.from(digest, "hex"));
|
|
66835
|
-
function writePrivate(file2,
|
|
66836
|
-
writeFileSync(`${file2}.tmp`,
|
|
67277
|
+
function writePrivate(file2, text3) {
|
|
67278
|
+
writeFileSync(`${file2}.tmp`, text3, { mode: 384, flush: true });
|
|
66837
67279
|
renameSync(`${file2}.tmp`, file2);
|
|
66838
67280
|
chmodSync2(file2, 384);
|
|
66839
67281
|
}
|
|
@@ -66843,7 +67285,7 @@ var privateFolder = (dir) => {
|
|
|
66843
67285
|
return dir;
|
|
66844
67286
|
};
|
|
66845
67287
|
function controlToken(data) {
|
|
66846
|
-
const file2 =
|
|
67288
|
+
const file2 = join6(data, "control-token");
|
|
66847
67289
|
try {
|
|
66848
67290
|
return readFileSync2(file2, "utf8").trim();
|
|
66849
67291
|
} catch (error62) {
|
|
@@ -66882,7 +67324,7 @@ var ProcessRuntime = class {
|
|
|
66882
67324
|
let browser;
|
|
66883
67325
|
let browserDown = false;
|
|
66884
67326
|
if (spec.browser) {
|
|
66885
|
-
const own3 = privateFolder(
|
|
67327
|
+
const own3 = privateFolder(join7(folder, "browser"));
|
|
66886
67328
|
browser = new Browser(
|
|
66887
67329
|
this.#config,
|
|
66888
67330
|
own3,
|
|
@@ -66906,10 +67348,19 @@ var ProcessRuntime = class {
|
|
|
66906
67348
|
browserLive = new BrowserLive(browser.endpoint);
|
|
66907
67349
|
browser.gate = browserLive;
|
|
66908
67350
|
}
|
|
66909
|
-
const live = new Live(exec, spec.workspace, spec.access
|
|
67351
|
+
const live = new Live(exec, spec.workspace, spec.access, browserLive);
|
|
67352
|
+
const surfaces = await live.surfaces();
|
|
66910
67353
|
return {
|
|
66911
67354
|
browserTools: browser?.tools ?? [],
|
|
66912
|
-
|
|
67355
|
+
/** As they are now: a browser that has stopped can't be opened, and says so. */
|
|
67356
|
+
get surfaces() {
|
|
67357
|
+
return surfaces.map(
|
|
67358
|
+
(one) => one.surface === "browser" && browserDown ? {
|
|
67359
|
+
...one,
|
|
67360
|
+
unavailable: "Its browser has stopped. Stop the computer and start it again."
|
|
67361
|
+
} : one
|
|
67362
|
+
);
|
|
67363
|
+
},
|
|
66913
67364
|
openLive: async (surface, mode, request, socket, head) => live.open(surface, mode, request, socket, head),
|
|
66914
67365
|
handle: async (endpoint, request, response) => {
|
|
66915
67366
|
if (endpoint === "computer") return exec.handle(request, response);
|
|
@@ -66939,13 +67390,13 @@ async function serveInside(options) {
|
|
|
66939
67390
|
...options,
|
|
66940
67391
|
...options.access === "none" ? {} : { workspace: "/workspace" }
|
|
66941
67392
|
},
|
|
66942
|
-
privateFolder(
|
|
67393
|
+
privateFolder(join8(homedir(), ".agentcomputer")),
|
|
66943
67394
|
{
|
|
66944
67395
|
log: (line) => process.stdout.write(`${line}
|
|
66945
67396
|
`),
|
|
66946
67397
|
// The service sees the container end, and shows the computer as failed.
|
|
66947
|
-
failed: (
|
|
66948
|
-
process.stderr.write(`${
|
|
67398
|
+
failed: (reason3) => {
|
|
67399
|
+
process.stderr.write(`${reason3}
|
|
66949
67400
|
`);
|
|
66950
67401
|
process.exit(1);
|
|
66951
67402
|
}
|
|
@@ -66974,7 +67425,7 @@ async function serveInside(options) {
|
|
|
66974
67425
|
socket.on("error", () => socket.destroy());
|
|
66975
67426
|
const given = /^Bearer (\S+)$/.exec(request.headers.authorization ?? "")?.[1];
|
|
66976
67427
|
const url3 = new URL(request.url ?? "/", "http://localhost");
|
|
66977
|
-
const surface = /^\/live\/(commands|terminal|browser)$/.exec(url3.pathname)?.[1];
|
|
67428
|
+
const surface = /^\/live\/(commands|terminal|files|browser)$/.exec(url3.pathname)?.[1];
|
|
66978
67429
|
const mode = url3.searchParams.get("mode");
|
|
66979
67430
|
if (!given || !sameToken(given, digest) || !surface || mode !== "watch" && mode !== "operate")
|
|
66980
67431
|
return void socket.end("HTTP/1.1 401 Unauthorized\r\nConnection: close\r\n\r\n");
|
|
@@ -66989,7 +67440,7 @@ async function serveInside(options) {
|
|
|
66989
67440
|
}
|
|
66990
67441
|
|
|
66991
67442
|
// src/container.ts
|
|
66992
|
-
var
|
|
67443
|
+
var run = promisify(execFile);
|
|
66993
67444
|
var START_MS2 = 6e4;
|
|
66994
67445
|
var STOP_SECONDS = "5";
|
|
66995
67446
|
var NOT_FORWARDED = /* @__PURE__ */ new Set([
|
|
@@ -67003,7 +67454,7 @@ var NOT_FORWARDED = /* @__PURE__ */ new Set([
|
|
|
67003
67454
|
]);
|
|
67004
67455
|
async function docker(args, env = process.env) {
|
|
67005
67456
|
try {
|
|
67006
|
-
return (await
|
|
67457
|
+
return (await run("docker", args, { env, maxBuffer: 16 * 1024 * 1024 })).stdout.trim();
|
|
67007
67458
|
} catch (error62) {
|
|
67008
67459
|
const { stderr, message } = error62;
|
|
67009
67460
|
throw new Error(stderr?.trim() || message);
|
|
@@ -67018,7 +67469,7 @@ var UPGRADE_HEADERS = [
|
|
|
67018
67469
|
"sec-websocket-protocol"
|
|
67019
67470
|
];
|
|
67020
67471
|
var nameOf = (id) => `agentcomputer-${id}`;
|
|
67021
|
-
var
|
|
67472
|
+
var sleep5 = (ms) => new Promise((done) => setTimeout(done, ms));
|
|
67022
67473
|
var ContainerRuntime = class {
|
|
67023
67474
|
isolation = "container";
|
|
67024
67475
|
#image;
|
|
@@ -67082,7 +67533,7 @@ var ContainerRuntime = class {
|
|
|
67082
67533
|
}
|
|
67083
67534
|
/** The token between the service and this container, which a restart has to find again. */
|
|
67084
67535
|
#tokenIn(folder) {
|
|
67085
|
-
const file2 =
|
|
67536
|
+
const file2 = join9(folder, "inner-token");
|
|
67086
67537
|
try {
|
|
67087
67538
|
return readFileSync3(file2, "utf8");
|
|
67088
67539
|
} catch {
|
|
@@ -67130,7 +67581,16 @@ var ContainerRuntime = class {
|
|
|
67130
67581
|
throw error62;
|
|
67131
67582
|
}
|
|
67132
67583
|
return {
|
|
67133
|
-
|
|
67584
|
+
browserTools: ready.browserTools,
|
|
67585
|
+
/** As they are now: nothing of a container that has stopped can be opened. */
|
|
67586
|
+
get surfaces() {
|
|
67587
|
+
return ready.surfaces.map(
|
|
67588
|
+
(one) => ended ? {
|
|
67589
|
+
...one,
|
|
67590
|
+
unavailable: "Its container has stopped. Stop the computer and start it again."
|
|
67591
|
+
} : one
|
|
67592
|
+
);
|
|
67593
|
+
},
|
|
67134
67594
|
stop,
|
|
67135
67595
|
openLive: async (surface, mode, request, socket, head) => pipeUpgrade(
|
|
67136
67596
|
Number(port),
|
|
@@ -67155,7 +67615,7 @@ var ContainerRuntime = class {
|
|
|
67155
67615
|
if (answer.ok) return await answer.json();
|
|
67156
67616
|
} catch {
|
|
67157
67617
|
}
|
|
67158
|
-
await
|
|
67618
|
+
await sleep5(100);
|
|
67159
67619
|
}
|
|
67160
67620
|
throw new Error("Its container didn't start. Its logs say why.");
|
|
67161
67621
|
}
|
|
@@ -67212,7 +67672,7 @@ async function forward(url3, authorization, request, response) {
|
|
|
67212
67672
|
}
|
|
67213
67673
|
|
|
67214
67674
|
// src/handlers.ts
|
|
67215
|
-
var VERSION2 = "0.0.
|
|
67675
|
+
var VERSION2 = "0.0.8";
|
|
67216
67676
|
function createHandlers(computers) {
|
|
67217
67677
|
return {
|
|
67218
67678
|
getInfo: async () => ({ version: VERSION2, isolation: computers.isolation }),
|
|
@@ -67761,9 +68221,9 @@ var Endpoint = class {
|
|
|
67761
68221
|
async #call(name, args) {
|
|
67762
68222
|
const client = await this.#connected();
|
|
67763
68223
|
const result = await client.callTool({ name, arguments: args });
|
|
67764
|
-
const
|
|
67765
|
-
if (result.isError) throw new ComputerError(400,
|
|
67766
|
-
return { text:
|
|
68224
|
+
const text3 = result.content.map((block) => block.text ?? "").join("");
|
|
68225
|
+
if (result.isError) throw new ComputerError(400, text3);
|
|
68226
|
+
return { text: text3, structured: result.structuredContent };
|
|
67767
68227
|
}
|
|
67768
68228
|
async text(name, args) {
|
|
67769
68229
|
return (await this.#call(name, args)).text;
|
|
@@ -67798,8 +68258,8 @@ var INPUT2 = 48;
|
|
|
67798
68258
|
var RESIZE2 = 49;
|
|
67799
68259
|
var Pty = class {
|
|
67800
68260
|
#open;
|
|
67801
|
-
constructor(
|
|
67802
|
-
this.#open =
|
|
68261
|
+
constructor(open4) {
|
|
68262
|
+
this.#open = open4;
|
|
67803
68263
|
}
|
|
67804
68264
|
/** Attach to its shell, starting one if none is running. Every attachment shares it. */
|
|
67805
68265
|
async connect(opts) {
|
|
@@ -67810,8 +68270,8 @@ var Pty = class {
|
|
|
67810
68270
|
const frame = new Uint8Array(data);
|
|
67811
68271
|
if (frame[0] === INPUT2) opts.onData(decoder.decode(frame.subarray(1), { stream: true }));
|
|
67812
68272
|
});
|
|
67813
|
-
const send2 = (type,
|
|
67814
|
-
const body = new TextEncoder().encode(
|
|
68273
|
+
const send2 = (type, text3) => {
|
|
68274
|
+
const body = new TextEncoder().encode(text3);
|
|
67815
68275
|
const frame = new Uint8Array(body.length + 1);
|
|
67816
68276
|
frame[0] = type;
|
|
67817
68277
|
frame.set(body, 1);
|
|
@@ -67828,9 +68288,9 @@ var Pty = class {
|
|
|
67828
68288
|
var Commands = class {
|
|
67829
68289
|
#endpoint;
|
|
67830
68290
|
#open;
|
|
67831
|
-
constructor(endpoint,
|
|
68291
|
+
constructor(endpoint, open4) {
|
|
67832
68292
|
this.#endpoint = endpoint;
|
|
67833
|
-
this.#open =
|
|
68293
|
+
this.#open = open4;
|
|
67834
68294
|
}
|
|
67835
68295
|
/** Each of the agent's commands as it runs: the ones already heard first, then as they come. */
|
|
67836
68296
|
async watch(onEvent) {
|
|
@@ -67897,16 +68357,16 @@ var Files = class {
|
|
|
67897
68357
|
}
|
|
67898
68358
|
/** A folder's entries, the first thousand. */
|
|
67899
68359
|
async list(path = ".") {
|
|
67900
|
-
const
|
|
67901
|
-
return
|
|
68360
|
+
const text3 = await this.#endpoint.text("list", { path });
|
|
68361
|
+
return text3.split("\n").filter((line) => line.endsWith("/") || line.includes(" ")).map((line) => {
|
|
67902
68362
|
const [name = "", size] = line.split(" ");
|
|
67903
68363
|
return name.endsWith("/") ? { name: name.slice(0, -1), type: "dir" } : { name, type: "file", size: Number(size) };
|
|
67904
68364
|
});
|
|
67905
68365
|
}
|
|
67906
68366
|
/** Lines matching text under a folder, as `path:line:text`. */
|
|
67907
68367
|
async search(pattern, opts = {}) {
|
|
67908
|
-
const
|
|
67909
|
-
return
|
|
68368
|
+
const text3 = await this.#endpoint.text("search", { pattern, ...opts });
|
|
68369
|
+
return text3 ? text3.split("\n") : [];
|
|
67910
68370
|
}
|
|
67911
68371
|
};
|
|
67912
68372
|
var pathOf = (computerID) => ({ params: { path: { computerID } } });
|
|
@@ -67925,9 +68385,9 @@ var Computer = class _Computer {
|
|
|
67925
68385
|
this.#token = token;
|
|
67926
68386
|
this.#urls = info.endpoints;
|
|
67927
68387
|
this.#computer = new Endpoint(info.endpoints.computer, token);
|
|
67928
|
-
const
|
|
67929
|
-
this.commands = new Commands(this.#computer,
|
|
67930
|
-
this.pty = new Pty(
|
|
68388
|
+
const open4 = (surface, mode) => this.liveSession(surface, mode);
|
|
68389
|
+
this.commands = new Commands(this.#computer, open4);
|
|
68390
|
+
this.pty = new Pty(open4);
|
|
67931
68391
|
this.files = new Files(this.#computer);
|
|
67932
68392
|
}
|
|
67933
68393
|
static async create(opts) {
|
|
@@ -68048,7 +68508,7 @@ import {
|
|
|
68048
68508
|
STATUS_CODES as STATUS_CODES3
|
|
68049
68509
|
} from "node:http";
|
|
68050
68510
|
import { createServer as createTlsServer } from "node:https";
|
|
68051
|
-
import { join as
|
|
68511
|
+
import { join as join11 } from "node:path";
|
|
68052
68512
|
|
|
68053
68513
|
// node_modules/@modelcontextprotocol/server/dist/chunk-vYduTEsO.mjs
|
|
68054
68514
|
var __create4 = Object.create;
|
|
@@ -71509,7 +71969,7 @@ var REQUEST_STATE_ONLY_LEG_PACING_MS3 = 250;
|
|
|
71509
71969
|
function inputRequiredRoundsExceededMessage3(method, maxRounds) {
|
|
71510
71970
|
return `Multi-round-trip request '${method}' still required input after ${maxRounds} rounds (inputRequired.maxRounds)`;
|
|
71511
71971
|
}
|
|
71512
|
-
function
|
|
71972
|
+
function sleep6(ms, signal) {
|
|
71513
71973
|
return new Promise((resolve6, reject) => {
|
|
71514
71974
|
if (signal?.aborted) {
|
|
71515
71975
|
reject(signal.reason instanceof SdkError3 ? signal.reason : new SdkError3(SdkErrorCode3.RequestTimeout, String(signal.reason)));
|
|
@@ -71533,7 +71993,7 @@ function linkedRoundAbort3(outer) {
|
|
|
71533
71993
|
if (outer?.aborted) controller.abort(outer.reason);
|
|
71534
71994
|
return {
|
|
71535
71995
|
signal: controller.signal,
|
|
71536
|
-
abort: (
|
|
71996
|
+
abort: (reason3) => controller.abort(reason3),
|
|
71537
71997
|
dispose: () => outer?.removeEventListener("abort", onOuterAbort)
|
|
71538
71998
|
};
|
|
71539
71999
|
}
|
|
@@ -72328,8 +72788,8 @@ var Protocol3 = class {
|
|
|
72328
72788
|
return;
|
|
72329
72789
|
}
|
|
72330
72790
|
if (options?.signal?.aborted) {
|
|
72331
|
-
const
|
|
72332
|
-
throw
|
|
72791
|
+
const reason3 = options.signal.reason;
|
|
72792
|
+
throw reason3 instanceof SdkError3 ? reason3 : new SdkError3(SdkErrorCode3.RequestTimeout, String(reason3));
|
|
72333
72793
|
}
|
|
72334
72794
|
const requestAbort = codec2.era === MODERN_WIRE_REVISION3 && this._transport.hasPerRequestStream === true ? new AbortController() : void 0;
|
|
72335
72795
|
const messageId = this._requestMessageId++;
|
|
@@ -72351,7 +72811,7 @@ var Protocol3 = class {
|
|
|
72351
72811
|
}
|
|
72352
72812
|
const outbound = this._envelopeOutbound(jsonrpcRequest);
|
|
72353
72813
|
let responseReceived = false;
|
|
72354
|
-
const cancel = (
|
|
72814
|
+
const cancel = (reason3) => {
|
|
72355
72815
|
if (responseReceived) return;
|
|
72356
72816
|
this._progressHandlers.delete(messageId);
|
|
72357
72817
|
if (requestAbort === void 0) {
|
|
@@ -72360,7 +72820,7 @@ var Protocol3 = class {
|
|
|
72360
72820
|
method: "notifications/cancelled",
|
|
72361
72821
|
params: {
|
|
72362
72822
|
requestId: messageId,
|
|
72363
|
-
reason: String(
|
|
72823
|
+
reason: String(reason3)
|
|
72364
72824
|
}
|
|
72365
72825
|
}), {
|
|
72366
72826
|
relatedRequestId,
|
|
@@ -72368,7 +72828,7 @@ var Protocol3 = class {
|
|
|
72368
72828
|
onresumptiontoken
|
|
72369
72829
|
}).catch((error62) => this._onerror(/* @__PURE__ */ new Error(`Failed to send cancellation: ${error62}`)));
|
|
72370
72830
|
} else requestAbort.abort();
|
|
72371
|
-
reject(
|
|
72831
|
+
reject(reason3 instanceof SdkError3 ? reason3 : new SdkError3(SdkErrorCode3.RequestTimeout, String(reason3)));
|
|
72372
72832
|
};
|
|
72373
72833
|
this._responseHandlers.set(messageId, (response) => {
|
|
72374
72834
|
if (options?.signal?.aborted) return;
|
|
@@ -76706,7 +77166,7 @@ var require_core$33 = /* @__PURE__ */ __commonJSMin3(((exports) => {
|
|
|
76706
77166
|
}
|
|
76707
77167
|
errorsText(errors = this.errors, { separator = ", ", dataVar = "data" } = {}) {
|
|
76708
77168
|
if (!errors || errors.length === 0) return "No errors";
|
|
76709
|
-
return errors.map((e) => `${dataVar}${e.instancePath} ${e.message}`).reduce((
|
|
77169
|
+
return errors.map((e) => `${dataVar}${e.instancePath} ${e.message}`).reduce((text3, msg) => text3 + separator + msg);
|
|
76710
77170
|
}
|
|
76711
77171
|
$dataMetaSchema(metaSchema, keywordsJsonPointers) {
|
|
76712
77172
|
const rules = this.RULES.all;
|
|
@@ -80506,7 +80966,7 @@ var LegacyInputRequiredShim2 = class {
|
|
|
80506
80966
|
} finally {
|
|
80507
80967
|
roundAbort.dispose();
|
|
80508
80968
|
}
|
|
80509
|
-
} else await
|
|
80969
|
+
} else await sleep6(REQUEST_STATE_ONLY_LEG_PACING_MS3, outerSignal);
|
|
80510
80970
|
let ctxNext = {
|
|
80511
80971
|
...ctx,
|
|
80512
80972
|
mcpReq: {
|
|
@@ -81852,21 +82312,21 @@ async function readRequestBody2(request, maxBytes = DEFAULT_MAX_REQUEST_BODY_SIZ
|
|
|
81852
82312
|
const reader = request.body.getReader();
|
|
81853
82313
|
const decoder = new TextDecoder();
|
|
81854
82314
|
let received = 0;
|
|
81855
|
-
let
|
|
82315
|
+
let text3 = "";
|
|
81856
82316
|
try {
|
|
81857
82317
|
for (; ; ) {
|
|
81858
82318
|
const { done, value } = await reader.read();
|
|
81859
82319
|
if (done) break;
|
|
81860
82320
|
received += value.byteLength;
|
|
81861
82321
|
if (received > maxBytes) return { tooLarge: true };
|
|
81862
|
-
|
|
82322
|
+
text3 += decoder.decode(value, { stream: true });
|
|
81863
82323
|
}
|
|
81864
82324
|
} finally {
|
|
81865
82325
|
reader.releaseLock();
|
|
81866
82326
|
}
|
|
81867
82327
|
return {
|
|
81868
82328
|
tooLarge: false,
|
|
81869
|
-
text:
|
|
82329
|
+
text: text3 + decoder.decode()
|
|
81870
82330
|
};
|
|
81871
82331
|
}
|
|
81872
82332
|
var WebStandardStreamableHTTPServerTransport2 = class {
|
|
@@ -82950,8 +83410,8 @@ var readRawBodyIfAvailable2 = (request) => {
|
|
|
82950
83410
|
};
|
|
82951
83411
|
var normalizeAbortError2 = (request, incoming) => {
|
|
82952
83412
|
if (incoming.errored) return incoming.errored;
|
|
82953
|
-
const
|
|
82954
|
-
if (
|
|
83413
|
+
const reason3 = request[abortReasonKey2];
|
|
83414
|
+
if (reason3 !== void 0) return reason3 instanceof Error ? reason3 : new Error(String(reason3));
|
|
82955
83415
|
return /* @__PURE__ */ new Error("Client connection prematurely closed.");
|
|
82956
83416
|
};
|
|
82957
83417
|
var readBodyDirect2 = (request) => {
|
|
@@ -83045,10 +83505,10 @@ var requestPrototype2 = {
|
|
|
83045
83505
|
get headers() {
|
|
83046
83506
|
return this[headersKey2] ||= newHeadersFromIncoming2(this[incomingKey2]);
|
|
83047
83507
|
},
|
|
83048
|
-
[abortRequest2](
|
|
83049
|
-
if (this[abortReasonKey2] === void 0) this[abortReasonKey2] =
|
|
83508
|
+
[abortRequest2](reason3) {
|
|
83509
|
+
if (this[abortReasonKey2] === void 0) this[abortReasonKey2] = reason3;
|
|
83050
83510
|
const abortController = this[abortControllerKey2];
|
|
83051
|
-
if (abortController && !abortController.signal.aborted) abortController.abort(
|
|
83511
|
+
if (abortController && !abortController.signal.aborted) abortController.abort(reason3);
|
|
83052
83512
|
},
|
|
83053
83513
|
[getAbortController2]() {
|
|
83054
83514
|
this[abortControllerKey2] ||= new AbortController();
|
|
@@ -83692,8 +84152,8 @@ var upgradeWebSocket2 = defineWebSocketHelper2(async (c, events, options) => {
|
|
|
83692
84152
|
ws.on("message", bufferMessage);
|
|
83693
84153
|
const ctx = {
|
|
83694
84154
|
binaryType: "arraybuffer",
|
|
83695
|
-
close(code,
|
|
83696
|
-
ws.close(code,
|
|
84155
|
+
close(code, reason3) {
|
|
84156
|
+
ws.close(code, reason3);
|
|
83697
84157
|
},
|
|
83698
84158
|
protocol: ws.protocol,
|
|
83699
84159
|
raw: ws,
|
|
@@ -83723,11 +84183,11 @@ var upgradeWebSocket2 = defineWebSocketHelper2(async (c, events, options) => {
|
|
|
83723
84183
|
ws.on("message", (data, isBinary) => {
|
|
83724
84184
|
handleMessage(data, isBinary);
|
|
83725
84185
|
});
|
|
83726
|
-
ws.on("close", (code,
|
|
84186
|
+
ws.on("close", (code, reason3) => {
|
|
83727
84187
|
try {
|
|
83728
84188
|
events?.onClose?.(new CloseEvent2("close", {
|
|
83729
84189
|
code,
|
|
83730
|
-
reason:
|
|
84190
|
+
reason: reason3.toString()
|
|
83731
84191
|
}), ctx);
|
|
83732
84192
|
} catch (e) {
|
|
83733
84193
|
(options?.onError ?? console.error)(e);
|
|
@@ -83862,9 +84322,9 @@ var NodeStreamableHTTPServerTransport2 = class {
|
|
|
83862
84322
|
};
|
|
83863
84323
|
|
|
83864
84324
|
// src/computers.ts
|
|
83865
|
-
import { randomBytes as
|
|
84325
|
+
import { randomBytes as randomBytes5 } from "node:crypto";
|
|
83866
84326
|
import { appendFileSync, existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync as readFileSync4, rmSync as rmSync2, statSync as statSync3 } from "node:fs";
|
|
83867
|
-
import { isAbsolute, join as
|
|
84327
|
+
import { isAbsolute, join as join10, resolve as resolve4 } from "node:path";
|
|
83868
84328
|
var TOOLS = {
|
|
83869
84329
|
read: ["read", "list", "search"],
|
|
83870
84330
|
files: ["read", "list", "search", "edit"],
|
|
@@ -83886,7 +84346,7 @@ var Computers = class {
|
|
|
83886
84346
|
this.#data = data;
|
|
83887
84347
|
this.#base = base;
|
|
83888
84348
|
this.#runtime = runtime;
|
|
83889
|
-
this.#file =
|
|
84349
|
+
this.#file = join10(data, "computers.json");
|
|
83890
84350
|
if (existsSync2(this.#file))
|
|
83891
84351
|
for (const entry of JSON.parse(readFileSync4(this.#file, "utf8")))
|
|
83892
84352
|
this.#stored.set(entry.id, entry);
|
|
@@ -83924,7 +84384,7 @@ var Computers = class {
|
|
|
83924
84384
|
if (!folder) throw new ApiError(400, `There's no folder at ${input2.workspace}.`);
|
|
83925
84385
|
workspace = resolve4(input2.workspace);
|
|
83926
84386
|
}
|
|
83927
|
-
const id = `c_${
|
|
84387
|
+
const id = `c_${randomBytes5(9).toString("hex")}`;
|
|
83928
84388
|
const token = newToken();
|
|
83929
84389
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
83930
84390
|
const stored = {
|
|
@@ -83941,7 +84401,7 @@ var Computers = class {
|
|
|
83941
84401
|
...timeout === void 0 ? {} : { endAt: expiry(timeout) }
|
|
83942
84402
|
};
|
|
83943
84403
|
if (!workspace && access !== "none") {
|
|
83944
|
-
stored.workspace =
|
|
84404
|
+
stored.workspace = join10(this.#folder(id), "workspace");
|
|
83945
84405
|
mkdirSync2(stored.workspace, { recursive: true, mode: 448 });
|
|
83946
84406
|
}
|
|
83947
84407
|
this.#stored.set(id, stored);
|
|
@@ -84023,12 +84483,12 @@ var Computers = class {
|
|
|
84023
84483
|
}
|
|
84024
84484
|
logs(id) {
|
|
84025
84485
|
this.#found(id);
|
|
84026
|
-
let
|
|
84486
|
+
let text3 = "";
|
|
84027
84487
|
try {
|
|
84028
|
-
|
|
84488
|
+
text3 = readFileSync4(this.#logFile(id), "utf8");
|
|
84029
84489
|
} catch {
|
|
84030
84490
|
}
|
|
84031
|
-
return
|
|
84491
|
+
return text3.split("\n").filter(Boolean).map((entry) => ({
|
|
84032
84492
|
timestamp: entry.slice(0, entry.indexOf(" ")),
|
|
84033
84493
|
line: entry.slice(entry.indexOf(" ") + 1)
|
|
84034
84494
|
}));
|
|
@@ -84060,7 +84520,7 @@ var Computers = class {
|
|
|
84060
84520
|
const now = Date.now();
|
|
84061
84521
|
for (const [key, ticket2] of this.#tickets)
|
|
84062
84522
|
if (ticket2.expiresAt <= now) this.#tickets.delete(key);
|
|
84063
|
-
const ticket = `l_${
|
|
84523
|
+
const ticket = `l_${randomBytes5(24).toString("base64url")}`;
|
|
84064
84524
|
const expiresAt = now + TICKET_MS;
|
|
84065
84525
|
this.#tickets.set(ticket, { computerID: id, surface, mode, expiresAt });
|
|
84066
84526
|
return {
|
|
@@ -84092,10 +84552,10 @@ var Computers = class {
|
|
|
84092
84552
|
return stored;
|
|
84093
84553
|
}
|
|
84094
84554
|
#folder(id) {
|
|
84095
|
-
return privateFolder(
|
|
84555
|
+
return privateFolder(join10(this.#data, "computers", id));
|
|
84096
84556
|
}
|
|
84097
84557
|
#logFile(id) {
|
|
84098
|
-
return
|
|
84558
|
+
return join10(this.#folder(id), "log");
|
|
84099
84559
|
}
|
|
84100
84560
|
#log(id, line) {
|
|
84101
84561
|
if (!this.#stored.has(id)) return;
|
|
@@ -84144,9 +84604,9 @@ var Computers = class {
|
|
|
84144
84604
|
this.#folder(stored.id),
|
|
84145
84605
|
{
|
|
84146
84606
|
log: (line) => this.#log(stored.id, line),
|
|
84147
|
-
failed: (
|
|
84607
|
+
failed: (reason3) => {
|
|
84148
84608
|
live.failed = true;
|
|
84149
|
-
this.#log(stored.id,
|
|
84609
|
+
this.#log(stored.id, reason3);
|
|
84150
84610
|
}
|
|
84151
84611
|
}
|
|
84152
84612
|
);
|
|
@@ -84187,7 +84647,7 @@ var OPENAPI = {
|
|
|
84187
84647
|
"openapi": "3.1.0",
|
|
84188
84648
|
"info": {
|
|
84189
84649
|
"title": "AgentComputer",
|
|
84190
|
-
"version": "0.0.
|
|
84650
|
+
"version": "0.0.8",
|
|
84191
84651
|
"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
84652
|
"license": {
|
|
84193
84653
|
"name": "Apache-2.0",
|
|
@@ -84508,7 +84968,7 @@ var OPENAPI = {
|
|
|
84508
84968
|
"post": {
|
|
84509
84969
|
"operationId": "createLiveSession",
|
|
84510
84970
|
"x-mcp-tool": "computer_live",
|
|
84511
|
-
"summary": "A one-time ticket to watch or operate one of its surfaces live: the agent's commands as they run,
|
|
84971
|
+
"summary": "A one-time ticket to watch or operate one of its surfaces live: the agent's commands as they run, a shell of your own, its files, or its browser. Open its url as a WebSocket within 60 seconds.",
|
|
84512
84972
|
"requestBody": {
|
|
84513
84973
|
"required": true,
|
|
84514
84974
|
"content": {
|
|
@@ -84849,9 +85309,10 @@ var OPENAPI = {
|
|
|
84849
85309
|
"enum": [
|
|
84850
85310
|
"commands",
|
|
84851
85311
|
"terminal",
|
|
85312
|
+
"files",
|
|
84852
85313
|
"browser"
|
|
84853
85314
|
],
|
|
84854
|
-
"description": "commands: the agent's commands as they run, read-only, in commands@1. terminal: a shell of your own in its workspace, as its user, in pty@1. 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.
|
|
85315
|
+
"description": "commands: the agent's commands as they run, read-only, in commands@1. terminal: a shell of your own in its workspace, as its user, in pty@1. 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
85316
|
},
|
|
84856
85317
|
"LiveMode": {
|
|
84857
85318
|
"type": "string",
|
|
@@ -84859,7 +85320,7 @@ var OPENAPI = {
|
|
|
84859
85320
|
"watch",
|
|
84860
85321
|
"operate"
|
|
84861
85322
|
],
|
|
84862
|
-
"description": "In watch
|
|
85323
|
+
"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
85324
|
},
|
|
84864
85325
|
"SurfaceInfo": {
|
|
84865
85326
|
"type": "object",
|
|
@@ -84882,8 +85343,7 @@ var OPENAPI = {
|
|
|
84882
85343
|
},
|
|
84883
85344
|
"unavailable": {
|
|
84884
85345
|
"type": "string",
|
|
84885
|
-
"description": "Why it can't be opened
|
|
84886
|
-
"if it can't.": null
|
|
85346
|
+
"description": "Why it can't be opened now, if it can't."
|
|
84887
85347
|
}
|
|
84888
85348
|
}
|
|
84889
85349
|
},
|
|
@@ -84942,7 +85402,7 @@ var OPENAPI = {
|
|
|
84942
85402
|
"oneOf": [
|
|
84943
85403
|
{
|
|
84944
85404
|
"type": "object",
|
|
84945
|
-
"description": "The open tabs, and which one
|
|
85405
|
+
"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
85406
|
"required": [
|
|
84947
85407
|
"type",
|
|
84948
85408
|
"tabs",
|
|
@@ -85090,30 +85550,6 @@ var OPENAPI = {
|
|
|
85090
85550
|
}
|
|
85091
85551
|
}
|
|
85092
85552
|
}
|
|
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
85553
|
}
|
|
85118
85554
|
]
|
|
85119
85555
|
},
|
|
@@ -85276,6 +85712,7 @@ var OPENAPI = {
|
|
|
85276
85712
|
},
|
|
85277
85713
|
{
|
|
85278
85714
|
"type": "object",
|
|
85715
|
+
"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
85716
|
"required": [
|
|
85280
85717
|
"type",
|
|
85281
85718
|
"action"
|
|
@@ -85312,20 +85749,321 @@ var OPENAPI = {
|
|
|
85312
85749
|
},
|
|
85313
85750
|
"to": {
|
|
85314
85751
|
"type": "string",
|
|
85315
|
-
"description": "A URL"
|
|
85316
|
-
|
|
85317
|
-
|
|
85752
|
+
"description": "A URL, or back, forward or reload."
|
|
85753
|
+
}
|
|
85754
|
+
}
|
|
85755
|
+
}
|
|
85756
|
+
]
|
|
85757
|
+
},
|
|
85758
|
+
"FilesRequest": {
|
|
85759
|
+
"description": "What a socket sends on files@1, as JSON text. Each has an id of its own choosing, which the answer repeats; answers come as they are ready, in any order, and ids aren't checked for repeats. A request still unanswered when the socket closes gets no answer. A path is from the workspace, with / between its parts, or absolute inside it, and nothing outside the workspace is reached. A binary message that follows no write closes the socket, and so does any message over 1,048,576 bytes.",
|
|
85760
|
+
"oneOf": [
|
|
85761
|
+
{
|
|
85762
|
+
"type": "object",
|
|
85763
|
+
"description": "A folder's entries, folders first, the first thousand. There is no asking for the rest.",
|
|
85764
|
+
"required": [
|
|
85765
|
+
"type",
|
|
85766
|
+
"id"
|
|
85767
|
+
],
|
|
85768
|
+
"properties": {
|
|
85769
|
+
"type": {
|
|
85770
|
+
"const": "list"
|
|
85771
|
+
},
|
|
85772
|
+
"id": {
|
|
85773
|
+
"type": "string",
|
|
85774
|
+
"minLength": 1,
|
|
85775
|
+
"maxLength": 64
|
|
85776
|
+
},
|
|
85777
|
+
"path": {
|
|
85778
|
+
"type": "string",
|
|
85779
|
+
"description": "The workspace itself, if left out."
|
|
85318
85780
|
}
|
|
85319
85781
|
}
|
|
85320
85782
|
},
|
|
85321
85783
|
{
|
|
85322
85784
|
"type": "object",
|
|
85785
|
+
"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.",
|
|
85323
85786
|
"required": [
|
|
85324
|
-
"type"
|
|
85787
|
+
"type",
|
|
85788
|
+
"id",
|
|
85789
|
+
"path",
|
|
85790
|
+
"offset",
|
|
85791
|
+
"length"
|
|
85325
85792
|
],
|
|
85326
85793
|
"properties": {
|
|
85327
85794
|
"type": {
|
|
85328
|
-
"const": "
|
|
85795
|
+
"const": "read"
|
|
85796
|
+
},
|
|
85797
|
+
"id": {
|
|
85798
|
+
"type": "string",
|
|
85799
|
+
"minLength": 1,
|
|
85800
|
+
"maxLength": 64
|
|
85801
|
+
},
|
|
85802
|
+
"path": {
|
|
85803
|
+
"type": "string"
|
|
85804
|
+
},
|
|
85805
|
+
"offset": {
|
|
85806
|
+
"type": "integer",
|
|
85807
|
+
"minimum": 0
|
|
85808
|
+
},
|
|
85809
|
+
"length": {
|
|
85810
|
+
"type": "integer",
|
|
85811
|
+
"minimum": 1,
|
|
85812
|
+
"maximum": 1048576
|
|
85813
|
+
}
|
|
85814
|
+
}
|
|
85815
|
+
},
|
|
85816
|
+
{
|
|
85817
|
+
"type": "object",
|
|
85818
|
+
"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.",
|
|
85819
|
+
"required": [
|
|
85820
|
+
"type",
|
|
85821
|
+
"id",
|
|
85822
|
+
"since"
|
|
85823
|
+
],
|
|
85824
|
+
"properties": {
|
|
85825
|
+
"type": {
|
|
85826
|
+
"const": "changed"
|
|
85827
|
+
},
|
|
85828
|
+
"id": {
|
|
85829
|
+
"type": "string",
|
|
85830
|
+
"minLength": 1,
|
|
85831
|
+
"maxLength": 64
|
|
85832
|
+
},
|
|
85833
|
+
"since": {
|
|
85834
|
+
"type": "string",
|
|
85835
|
+
"format": "date-time"
|
|
85836
|
+
}
|
|
85837
|
+
}
|
|
85838
|
+
},
|
|
85839
|
+
{
|
|
85840
|
+
"type": "object",
|
|
85841
|
+
"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.",
|
|
85842
|
+
"required": [
|
|
85843
|
+
"type",
|
|
85844
|
+
"id",
|
|
85845
|
+
"path",
|
|
85846
|
+
"offset",
|
|
85847
|
+
"length",
|
|
85848
|
+
"last"
|
|
85849
|
+
],
|
|
85850
|
+
"properties": {
|
|
85851
|
+
"type": {
|
|
85852
|
+
"const": "write"
|
|
85853
|
+
},
|
|
85854
|
+
"id": {
|
|
85855
|
+
"type": "string",
|
|
85856
|
+
"minLength": 1,
|
|
85857
|
+
"maxLength": 64
|
|
85858
|
+
},
|
|
85859
|
+
"path": {
|
|
85860
|
+
"type": "string"
|
|
85861
|
+
},
|
|
85862
|
+
"offset": {
|
|
85863
|
+
"type": "integer",
|
|
85864
|
+
"minimum": 0
|
|
85865
|
+
},
|
|
85866
|
+
"length": {
|
|
85867
|
+
"type": "integer",
|
|
85868
|
+
"minimum": 0,
|
|
85869
|
+
"maximum": 1048576
|
|
85870
|
+
},
|
|
85871
|
+
"last": {
|
|
85872
|
+
"type": "boolean"
|
|
85873
|
+
}
|
|
85874
|
+
}
|
|
85875
|
+
}
|
|
85876
|
+
]
|
|
85877
|
+
},
|
|
85878
|
+
"FilesAnswer": {
|
|
85879
|
+
"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.",
|
|
85880
|
+
"oneOf": [
|
|
85881
|
+
{
|
|
85882
|
+
"type": "object",
|
|
85883
|
+
"required": [
|
|
85884
|
+
"type",
|
|
85885
|
+
"id",
|
|
85886
|
+
"path",
|
|
85887
|
+
"entries",
|
|
85888
|
+
"more"
|
|
85889
|
+
],
|
|
85890
|
+
"properties": {
|
|
85891
|
+
"type": {
|
|
85892
|
+
"const": "entries"
|
|
85893
|
+
},
|
|
85894
|
+
"id": {
|
|
85895
|
+
"type": "string"
|
|
85896
|
+
},
|
|
85897
|
+
"path": {
|
|
85898
|
+
"type": "string"
|
|
85899
|
+
},
|
|
85900
|
+
"entries": {
|
|
85901
|
+
"type": "array",
|
|
85902
|
+
"items": {
|
|
85903
|
+
"type": "object",
|
|
85904
|
+
"description": "A link is not followed, and its size and date are its own.",
|
|
85905
|
+
"required": [
|
|
85906
|
+
"name",
|
|
85907
|
+
"kind",
|
|
85908
|
+
"size",
|
|
85909
|
+
"modifiedAt"
|
|
85910
|
+
],
|
|
85911
|
+
"properties": {
|
|
85912
|
+
"name": {
|
|
85913
|
+
"type": "string"
|
|
85914
|
+
},
|
|
85915
|
+
"kind": {
|
|
85916
|
+
"type": "string",
|
|
85917
|
+
"enum": [
|
|
85918
|
+
"file",
|
|
85919
|
+
"folder",
|
|
85920
|
+
"link"
|
|
85921
|
+
]
|
|
85922
|
+
},
|
|
85923
|
+
"size": {
|
|
85924
|
+
"type": "integer"
|
|
85925
|
+
},
|
|
85926
|
+
"modifiedAt": {
|
|
85927
|
+
"type": "string",
|
|
85928
|
+
"format": "date-time"
|
|
85929
|
+
}
|
|
85930
|
+
}
|
|
85931
|
+
}
|
|
85932
|
+
},
|
|
85933
|
+
"more": {
|
|
85934
|
+
"type": "boolean",
|
|
85935
|
+
"description": "Whether the folder has more than these."
|
|
85936
|
+
}
|
|
85937
|
+
}
|
|
85938
|
+
},
|
|
85939
|
+
{
|
|
85940
|
+
"type": "object",
|
|
85941
|
+
"description": "What the binary message after it is, and the file's whole size and date.",
|
|
85942
|
+
"required": [
|
|
85943
|
+
"type",
|
|
85944
|
+
"id",
|
|
85945
|
+
"path",
|
|
85946
|
+
"offset",
|
|
85947
|
+
"length",
|
|
85948
|
+
"size",
|
|
85949
|
+
"modifiedAt"
|
|
85950
|
+
],
|
|
85951
|
+
"properties": {
|
|
85952
|
+
"type": {
|
|
85953
|
+
"const": "data"
|
|
85954
|
+
},
|
|
85955
|
+
"id": {
|
|
85956
|
+
"type": "string"
|
|
85957
|
+
},
|
|
85958
|
+
"path": {
|
|
85959
|
+
"type": "string"
|
|
85960
|
+
},
|
|
85961
|
+
"offset": {
|
|
85962
|
+
"type": "integer"
|
|
85963
|
+
},
|
|
85964
|
+
"length": {
|
|
85965
|
+
"type": "integer",
|
|
85966
|
+
"description": "How many bytes follow."
|
|
85967
|
+
},
|
|
85968
|
+
"size": {
|
|
85969
|
+
"type": "integer"
|
|
85970
|
+
},
|
|
85971
|
+
"modifiedAt": {
|
|
85972
|
+
"type": "string",
|
|
85973
|
+
"format": "date-time"
|
|
85974
|
+
}
|
|
85975
|
+
}
|
|
85976
|
+
},
|
|
85977
|
+
{
|
|
85978
|
+
"type": "object",
|
|
85979
|
+
"required": [
|
|
85980
|
+
"type",
|
|
85981
|
+
"id",
|
|
85982
|
+
"since",
|
|
85983
|
+
"changes",
|
|
85984
|
+
"more"
|
|
85985
|
+
],
|
|
85986
|
+
"properties": {
|
|
85987
|
+
"type": {
|
|
85988
|
+
"const": "changes"
|
|
85989
|
+
},
|
|
85990
|
+
"id": {
|
|
85991
|
+
"type": "string"
|
|
85992
|
+
},
|
|
85993
|
+
"since": {
|
|
85994
|
+
"type": "string",
|
|
85995
|
+
"format": "date-time"
|
|
85996
|
+
},
|
|
85997
|
+
"changes": {
|
|
85998
|
+
"type": "array",
|
|
85999
|
+
"items": {
|
|
86000
|
+
"type": "object",
|
|
86001
|
+
"required": [
|
|
86002
|
+
"path",
|
|
86003
|
+
"size",
|
|
86004
|
+
"modifiedAt"
|
|
86005
|
+
],
|
|
86006
|
+
"properties": {
|
|
86007
|
+
"path": {
|
|
86008
|
+
"type": "string"
|
|
86009
|
+
},
|
|
86010
|
+
"size": {
|
|
86011
|
+
"type": "integer"
|
|
86012
|
+
},
|
|
86013
|
+
"modifiedAt": {
|
|
86014
|
+
"type": "string",
|
|
86015
|
+
"format": "date-time"
|
|
86016
|
+
}
|
|
86017
|
+
}
|
|
86018
|
+
}
|
|
86019
|
+
},
|
|
86020
|
+
"more": {
|
|
86021
|
+
"type": "boolean",
|
|
86022
|
+
"description": "Whether more than these changed."
|
|
86023
|
+
}
|
|
86024
|
+
}
|
|
86025
|
+
},
|
|
86026
|
+
{
|
|
86027
|
+
"type": "object",
|
|
86028
|
+
"description": "The file is in place.",
|
|
86029
|
+
"required": [
|
|
86030
|
+
"type",
|
|
86031
|
+
"id",
|
|
86032
|
+
"path",
|
|
86033
|
+
"size"
|
|
86034
|
+
],
|
|
86035
|
+
"properties": {
|
|
86036
|
+
"type": {
|
|
86037
|
+
"const": "written"
|
|
86038
|
+
},
|
|
86039
|
+
"id": {
|
|
86040
|
+
"type": "string"
|
|
86041
|
+
},
|
|
86042
|
+
"path": {
|
|
86043
|
+
"type": "string"
|
|
86044
|
+
},
|
|
86045
|
+
"size": {
|
|
86046
|
+
"type": "integer"
|
|
86047
|
+
}
|
|
86048
|
+
}
|
|
86049
|
+
},
|
|
86050
|
+
{
|
|
86051
|
+
"type": "object",
|
|
86052
|
+
"description": "It wasn't done, and why. The id is empty when what was sent had none to repeat.",
|
|
86053
|
+
"required": [
|
|
86054
|
+
"type",
|
|
86055
|
+
"id",
|
|
86056
|
+
"message"
|
|
86057
|
+
],
|
|
86058
|
+
"properties": {
|
|
86059
|
+
"type": {
|
|
86060
|
+
"const": "refused"
|
|
86061
|
+
},
|
|
86062
|
+
"id": {
|
|
86063
|
+
"type": "string"
|
|
86064
|
+
},
|
|
86065
|
+
"message": {
|
|
86066
|
+
"type": "string"
|
|
85329
86067
|
}
|
|
85330
86068
|
}
|
|
85331
86069
|
}
|
|
@@ -85871,9 +86609,10 @@ var OPERATIONS = [
|
|
|
85871
86609
|
"enum": [
|
|
85872
86610
|
"commands",
|
|
85873
86611
|
"terminal",
|
|
86612
|
+
"files",
|
|
85874
86613
|
"browser"
|
|
85875
86614
|
],
|
|
85876
|
-
"description": "commands: the agent's commands as they run, read-only, in commands@1. terminal: a shell of your own in its workspace, as its user, in pty@1. 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.
|
|
86615
|
+
"description": "commands: the agent's commands as they run, read-only, in commands@1. terminal: a shell of your own in its workspace, as its user, in pty@1. 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
86616
|
},
|
|
85878
86617
|
"protocol": {
|
|
85879
86618
|
"type": "string"
|
|
@@ -85885,8 +86624,7 @@ var OPERATIONS = [
|
|
|
85885
86624
|
},
|
|
85886
86625
|
"unavailable": {
|
|
85887
86626
|
"type": "string",
|
|
85888
|
-
"description": "Why it can't be opened
|
|
85889
|
-
"if it can't.": null
|
|
86627
|
+
"description": "Why it can't be opened now, if it can't."
|
|
85890
86628
|
}
|
|
85891
86629
|
}
|
|
85892
86630
|
}
|
|
@@ -86390,7 +87128,7 @@ var OPERATIONS = [
|
|
|
86390
87128
|
"method": "POST",
|
|
86391
87129
|
"path": "/v1/computers/{computerID}/live",
|
|
86392
87130
|
"tool": "computer_live",
|
|
86393
|
-
"summary": "A one-time ticket to watch or operate one of its surfaces live: the agent's commands as they run,
|
|
87131
|
+
"summary": "A one-time ticket to watch or operate one of its surfaces live: the agent's commands as they run, a shell of your own, its files, or its browser. Open its url as a WebSocket within 60 seconds.",
|
|
86394
87132
|
"status": 201,
|
|
86395
87133
|
"hasBody": true,
|
|
86396
87134
|
"pathParameters": [
|
|
@@ -86411,9 +87149,10 @@ var OPERATIONS = [
|
|
|
86411
87149
|
"enum": [
|
|
86412
87150
|
"commands",
|
|
86413
87151
|
"terminal",
|
|
87152
|
+
"files",
|
|
86414
87153
|
"browser"
|
|
86415
87154
|
],
|
|
86416
|
-
"description": "commands: the agent's commands as they run, read-only, in commands@1. terminal: a shell of your own in its workspace, as its user, in pty@1. 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.
|
|
87155
|
+
"description": "commands: the agent's commands as they run, read-only, in commands@1. terminal: a shell of your own in its workspace, as its user, in pty@1. 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
87156
|
},
|
|
86418
87157
|
"mode": {
|
|
86419
87158
|
"allOf": [
|
|
@@ -86423,7 +87162,7 @@ var OPERATIONS = [
|
|
|
86423
87162
|
"watch",
|
|
86424
87163
|
"operate"
|
|
86425
87164
|
],
|
|
86426
|
-
"description": "In watch
|
|
87165
|
+
"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
87166
|
}
|
|
86428
87167
|
],
|
|
86429
87168
|
"default": "watch"
|
|
@@ -86457,9 +87196,10 @@ var OPERATIONS = [
|
|
|
86457
87196
|
"enum": [
|
|
86458
87197
|
"commands",
|
|
86459
87198
|
"terminal",
|
|
87199
|
+
"files",
|
|
86460
87200
|
"browser"
|
|
86461
87201
|
],
|
|
86462
|
-
"description": "commands: the agent's commands as they run, read-only, in commands@1. terminal: a shell of your own in its workspace, as its user, in pty@1. 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.
|
|
87202
|
+
"description": "commands: the agent's commands as they run, read-only, in commands@1. terminal: a shell of your own in its workspace, as its user, in pty@1. 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
87203
|
},
|
|
86464
87204
|
"mode": {
|
|
86465
87205
|
"type": "string",
|
|
@@ -86467,7 +87207,7 @@ var OPERATIONS = [
|
|
|
86467
87207
|
"watch",
|
|
86468
87208
|
"operate"
|
|
86469
87209
|
],
|
|
86470
|
-
"description": "In watch
|
|
87210
|
+
"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
87211
|
},
|
|
86472
87212
|
"expiresAt": {
|
|
86473
87213
|
"type": "string",
|
|
@@ -86531,13 +87271,13 @@ function parseListen(listen) {
|
|
|
86531
87271
|
}
|
|
86532
87272
|
var bearerOf = (request) => /^Bearer (\S+)$/.exec(request.headers.authorization ?? "")?.[1];
|
|
86533
87273
|
async function jsonBody(request) {
|
|
86534
|
-
let
|
|
87274
|
+
let text3 = "";
|
|
86535
87275
|
for await (const chunk of request) {
|
|
86536
|
-
|
|
86537
|
-
if (
|
|
87276
|
+
text3 += String(chunk);
|
|
87277
|
+
if (text3.length > MAX_BODY2) throw new ApiError(413, "That request is too large.");
|
|
86538
87278
|
}
|
|
86539
87279
|
try {
|
|
86540
|
-
const parsed = JSON.parse(
|
|
87280
|
+
const parsed = JSON.parse(text3 || "{}");
|
|
86541
87281
|
if (typeof parsed === "object" && parsed !== null && !Array.isArray(parsed))
|
|
86542
87282
|
return parsed;
|
|
86543
87283
|
} catch {
|
|
@@ -86565,7 +87305,7 @@ async function serve(options) {
|
|
|
86565
87305
|
pattern: new RegExp(`^${operation.path.replace(/\{[^}]+\}/g, "([^/]+)")}$`),
|
|
86566
87306
|
check: fromJsonSchema3(operation.input)
|
|
86567
87307
|
}));
|
|
86568
|
-
const
|
|
87308
|
+
const run2 = async (route2, input2) => {
|
|
86569
87309
|
const checked = await route2.check["~standard"].validate(input2);
|
|
86570
87310
|
if (checked.issues)
|
|
86571
87311
|
throw new ApiError(400, checked.issues.map((issue3) => issue3.message).join("; "));
|
|
@@ -86580,7 +87320,7 @@ async function serve(options) {
|
|
|
86580
87320
|
{ description: route2.operation.summary, inputSchema: route2.check },
|
|
86581
87321
|
async (input2) => {
|
|
86582
87322
|
try {
|
|
86583
|
-
const output2 = await
|
|
87323
|
+
const output2 = await run2(route2, input2) ?? {};
|
|
86584
87324
|
return { content: [{ type: "text", text: JSON.stringify(output2) }] };
|
|
86585
87325
|
} catch (error62) {
|
|
86586
87326
|
if (!(error62 instanceof ApiError)) throw error62;
|
|
@@ -86632,7 +87372,7 @@ async function serve(options) {
|
|
|
86632
87372
|
const value = url3.searchParams.get(name);
|
|
86633
87373
|
if (value !== null) input2[name] = value;
|
|
86634
87374
|
}
|
|
86635
|
-
return send(response, operation.status, await
|
|
87375
|
+
return send(response, operation.status, await run2(candidate, input2));
|
|
86636
87376
|
}
|
|
86637
87377
|
throw pathMatched ? new ApiError(405, "Not allowed.") : new ApiError(404, "Not found.");
|
|
86638
87378
|
};
|
|
@@ -86679,7 +87419,7 @@ ${JSON.stringify({ code: status, message })}
|
|
|
86679
87419
|
const scheme = options.tls ? "https" : "http";
|
|
86680
87420
|
base = options.publicUrl?.replace(/\/+$/, "") ?? `${scheme}://${bound.family === "IPv6" ? `[${bound.address}]` : bound.address}:${bound.port}`;
|
|
86681
87421
|
await computers.resume();
|
|
86682
|
-
const record3 =
|
|
87422
|
+
const record3 = join11(data, "server.json");
|
|
86683
87423
|
writePrivate(
|
|
86684
87424
|
record3,
|
|
86685
87425
|
JSON.stringify({ url: base, pid: process.pid, isolation: options.runtime.isolation })
|
|
@@ -86719,7 +87459,7 @@ var USAGE = `Usage: agentcomputer <command> [--data DIR]
|
|
|
86719
87459
|
stop <id> stop it, keeping its folder and token
|
|
86720
87460
|
start <id> [--timeout SECONDS] start a stopped computer again
|
|
86721
87461
|
logs <id> what its servers wrote
|
|
86722
|
-
live <id> --surface commands|terminal [--mode watch|operate]
|
|
87462
|
+
live <id> --surface commands|terminal|files|browser [--mode watch|operate]
|
|
86723
87463
|
a one-time ticket to open one live, as a WebSocket
|
|
86724
87464
|
kill <id> stop it and remove what the service made
|
|
86725
87465
|
|
|
@@ -86732,13 +87472,13 @@ var UsageError = class extends Error {
|
|
|
86732
87472
|
function dataFolder(given) {
|
|
86733
87473
|
const xdg = process.env.XDG_DATA_HOME;
|
|
86734
87474
|
return resolve5(
|
|
86735
|
-
given ?? process.env.AGENTCOMPUTER_DATA ?? (xdg && isAbsolute2(xdg) ?
|
|
87475
|
+
given ?? process.env.AGENTCOMPUTER_DATA ?? (xdg && isAbsolute2(xdg) ? join12(xdg, "agentcomputer") : join12(homedir2(), ".local", "share", "agentcomputer"))
|
|
86736
87476
|
);
|
|
86737
87477
|
}
|
|
86738
87478
|
function connectionTo(data) {
|
|
86739
87479
|
try {
|
|
86740
|
-
const { url: url3 } = JSON.parse(readFileSync6(
|
|
86741
|
-
return { apiUrl: url3, apiKey: readFileSync6(
|
|
87480
|
+
const { url: url3 } = JSON.parse(readFileSync6(join12(data, "server.json"), "utf8"));
|
|
87481
|
+
return { apiUrl: url3, apiKey: readFileSync6(join12(data, "control-token"), "utf8").trim() };
|
|
86742
87482
|
} catch {
|
|
86743
87483
|
throw new Error(
|
|
86744
87484
|
`No service is running with its data in ${data}. Start one: agentcomputer serve`
|
|
@@ -86767,6 +87507,8 @@ async function main() {
|
|
|
86767
87507
|
channel: { type: "string" },
|
|
86768
87508
|
executable: { type: "string" },
|
|
86769
87509
|
"no-sandbox": { type: "boolean" },
|
|
87510
|
+
endpoint: { type: "string" },
|
|
87511
|
+
output: { type: "string" },
|
|
86770
87512
|
access: { type: "string" },
|
|
86771
87513
|
workspace: { type: "string" },
|
|
86772
87514
|
name: { type: "string" },
|
|
@@ -86813,6 +87555,11 @@ async function main() {
|
|
|
86813
87555
|
executable: values.executable,
|
|
86814
87556
|
sandbox: values["no-sandbox"] ? false : void 0
|
|
86815
87557
|
});
|
|
87558
|
+
if (command === "browser-mcp")
|
|
87559
|
+
return serveBrowserMcp({
|
|
87560
|
+
endpoint: values.endpoint ?? "",
|
|
87561
|
+
outputDir: values.output ?? ""
|
|
87562
|
+
});
|
|
86816
87563
|
if (command === "inside")
|
|
86817
87564
|
return serveInside({
|
|
86818
87565
|
access: values.access,
|