@opengeni/runtime 4.0.1 → 4.0.3-canary.36199476632001
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/dist/{agent-RJQLKFIW.js → agent-ME6HT4DH.js} +2 -2
- package/dist/assets/codemode-client.json +1 -1
- package/dist/browser-action-json-schema.d.ts +4 -0
- package/dist/{chunk-J5AR5XAJ.js → chunk-FAFZCSN7.js} +695 -210
- package/dist/chunk-FAFZCSN7.js.map +1 -0
- package/dist/{chunk-ZG2YH25H.js → chunk-JAYHA6BF.js} +2 -2
- package/dist/{chunk-UNICCRW5.js → chunk-LZIXCZUV.js} +6 -3
- package/dist/{chunk-UNICCRW5.js.map → chunk-LZIXCZUV.js.map} +1 -1
- package/dist/{chunk-J6J3VYFW.js → chunk-P4V7JGQ2.js} +1037 -105
- package/dist/chunk-P4V7JGQ2.js.map +1 -0
- package/dist/{chunk-SPT3PLMX.js → chunk-PRHMSM6C.js} +2 -2
- package/dist/codex-tool-search.d.ts +12 -0
- package/dist/context-compaction.d.ts +4 -2
- package/dist/index.d.ts +27 -8
- package/dist/index.js +23 -7
- package/dist/interaction-tools.d.ts +1 -1
- package/dist/{lazy-manifest-OEYEHNVY.js → lazy-manifest-GCGUFIWP.js} +3 -3
- package/dist/{localSources-TWHTEKGO.js → localSources-FDH65AFI.js} +4 -4
- package/dist/metrics.d.ts +17 -0
- package/dist/model-provider-routing.d.ts +14 -0
- package/dist/operational-instructions.d.ts +1 -1
- package/dist/provider-quota.d.ts +76 -0
- package/dist/sandbox/browser-control-client.d.ts +28 -1
- package/dist/sandbox/channel-a.d.ts +43 -0
- package/dist/sandbox/index.d.ts +3 -3
- package/dist/sandbox/index.js +10 -6
- package/dist/sandbox/providers/modal-command-router-wire.d.ts +8 -0
- package/dist/sandbox/providers/modal.d.ts +5 -6
- package/dist/workspace-tool-gateway.js +5 -5
- package/package.json +11 -11
- package/src/browser-action-json-schema.ts +10 -0
- package/src/bundled_artifact_skills/opengeni-documents/SKILL.md +4 -1
- package/src/bundled_artifact_skills/opengeni-documents/references/api.md +20 -0
- package/src/bundled_default_skills/opengeni-client/SKILL.md +4 -1
- package/src/bundled_default_skills/opengeni-client/references/discovery-and-autonomy.md +31 -0
- package/src/bundled_default_skills/opengeni-client/references/runtime-profile-and-verification.md +32 -0
- package/src/codex-tool-search.ts +54 -6
- package/src/context-compaction.ts +19 -3
- package/src/index.ts +285 -56
- package/src/interaction-tools.ts +656 -48
- package/src/lazy-tool-transport.ts +4 -0
- package/src/metrics.ts +26 -0
- package/src/model-context-inspector.ts +1 -0
- package/src/model-provider-client.ts +37 -16
- package/src/model-provider-routing.ts +32 -6
- package/src/operational-instructions.ts +1 -1
- package/src/provider-quota.ts +412 -0
- package/src/sandbox/browser-control-client.ts +241 -26
- package/src/sandbox/channel-a.ts +370 -0
- package/src/sandbox/index.ts +6 -1
- package/src/sandbox/providers/modal-command-control.ts +7 -1
- package/src/sandbox/providers/modal-command-router-wire.ts +47 -6
- package/src/sandbox/providers/modal.ts +16 -24
- package/src/sandbox/routing/routing-session.ts +29 -6
- package/dist/chunk-J5AR5XAJ.js.map +0 -1
- package/dist/chunk-J6J3VYFW.js.map +0 -1
- /package/dist/{agent-RJQLKFIW.js.map → agent-ME6HT4DH.js.map} +0 -0
- /package/dist/{chunk-ZG2YH25H.js.map → chunk-JAYHA6BF.js.map} +0 -0
- /package/dist/{chunk-SPT3PLMX.js.map → chunk-PRHMSM6C.js.map} +0 -0
- /package/dist/{lazy-manifest-OEYEHNVY.js.map → lazy-manifest-GCGUFIWP.js.map} +0 -0
- /package/dist/{localSources-TWHTEKGO.js.map → localSources-FDH65AFI.js.map} +0 -0
|
@@ -26,14 +26,14 @@ import {
|
|
|
26
26
|
truncateOutput,
|
|
27
27
|
validateCredentialPair,
|
|
28
28
|
validateRemoteSandboxPathForManifest
|
|
29
|
-
} from "./chunk-
|
|
29
|
+
} from "./chunk-PRHMSM6C.js";
|
|
30
30
|
import {
|
|
31
31
|
NoopSnapshotSpec,
|
|
32
32
|
isNoopSnapshotSpec,
|
|
33
33
|
normalizeSandboxClientCreateArgs,
|
|
34
34
|
resolveSandboxArchiveLimits,
|
|
35
35
|
validateSandboxArchiveLimits
|
|
36
|
-
} from "./chunk-
|
|
36
|
+
} from "./chunk-JAYHA6BF.js";
|
|
37
37
|
import {
|
|
38
38
|
Environment,
|
|
39
39
|
FileMode,
|
|
@@ -96,7 +96,7 @@ import {
|
|
|
96
96
|
shellQuote,
|
|
97
97
|
stableJsonStringify,
|
|
98
98
|
withSandboxSpan
|
|
99
|
-
} from "./chunk-
|
|
99
|
+
} from "./chunk-LZIXCZUV.js";
|
|
100
100
|
|
|
101
101
|
// src/sandbox/index.ts
|
|
102
102
|
import { collectSandboxEnvironment, parseExposedPorts } from "@opengeni/config";
|
|
@@ -6567,7 +6567,7 @@ var CloudflareSandboxSession = class {
|
|
|
6567
6567
|
async materializeEntry(args) {
|
|
6568
6568
|
assertSandboxEntryMetadataSupported("CloudflareSandboxClient", args.path, args.entry, MOUNT_MANIFEST_METADATA_SUPPORT);
|
|
6569
6569
|
if (entryContainsLocalSource(args.entry)) {
|
|
6570
|
-
const { applyLocalSourceManifestEntryToState: applyLocalSourceManifestEntryToState2 } = await import("./localSources-
|
|
6570
|
+
const { applyLocalSourceManifestEntryToState: applyLocalSourceManifestEntryToState2 } = await import("./localSources-FDH65AFI.js");
|
|
6571
6571
|
await applyLocalSourceManifestEntryToState2(this.state, args.path, args.entry, "cloudflare", this.writer(), this.remotePathResolver, this.manifestMaterializationOptions(args.runAs));
|
|
6572
6572
|
return;
|
|
6573
6573
|
}
|
|
@@ -6578,7 +6578,7 @@ var CloudflareSandboxSession = class {
|
|
|
6578
6578
|
assertSandboxManifestMetadataSupported("CloudflareSandboxClient", manifest, MOUNT_MANIFEST_METADATA_SUPPORT);
|
|
6579
6579
|
assertCloudflareManifestMountsSupported(manifest);
|
|
6580
6580
|
if (manifestContainsLocalSource(manifest)) {
|
|
6581
|
-
const { applyLocalSourceManifestToState: applyLocalSourceManifestToState2 } = await import("./localSources-
|
|
6581
|
+
const { applyLocalSourceManifestToState: applyLocalSourceManifestToState2 } = await import("./localSources-FDH65AFI.js");
|
|
6582
6582
|
await applyLocalSourceManifestToState2(this.state, manifest, "cloudflare", this.writer(), this.remotePathResolver, this.manifestMaterializationOptions(runAs));
|
|
6583
6583
|
return;
|
|
6584
6584
|
}
|
|
@@ -15606,6 +15606,9 @@ import {
|
|
|
15606
15606
|
canonicalModalCheckpointProviderBinding
|
|
15607
15607
|
} from "@opengeni/contracts";
|
|
15608
15608
|
|
|
15609
|
+
// src/sandbox/channel-a.ts
|
|
15610
|
+
import { constants as zlibConstants, createGunzip } from "zlib";
|
|
15611
|
+
|
|
15609
15612
|
// src/sandbox/selfhosted/workspace-path.ts
|
|
15610
15613
|
var MAX_MACHINE_PATH_LENGTH = 4096;
|
|
15611
15614
|
var CONTROL_CHARACTER = /[\u0000-\u001f\u007f]/u;
|
|
@@ -15849,7 +15852,11 @@ function notifyDurableOpOwnershipTransferred(opId) {
|
|
|
15849
15852
|
}
|
|
15850
15853
|
|
|
15851
15854
|
// src/sandbox/routing/routing-session.ts
|
|
15852
|
-
import {
|
|
15855
|
+
import { SandboxFilesystemNotFoundError } from "modal";
|
|
15856
|
+
|
|
15857
|
+
// src/sandbox/providers/modal-command-router-wire.ts
|
|
15858
|
+
import { Client, Metadata, credentials, status } from "@grpc/grpc-js";
|
|
15859
|
+
import protobuf from "protobufjs";
|
|
15853
15860
|
|
|
15854
15861
|
// src/sandbox/provider-command-session.ts
|
|
15855
15862
|
import { AsyncLocalStorage as AsyncLocalStorage2 } from "async_hooks";
|
|
@@ -15895,6 +15902,219 @@ function admittedProviderCommandHandle() {
|
|
|
15895
15902
|
return admission.getStore();
|
|
15896
15903
|
}
|
|
15897
15904
|
|
|
15905
|
+
// src/sandbox/providers/modal-command-router-wire.ts
|
|
15906
|
+
var modalRouterWire = protobuf.parse(`syntax = "proto3";
|
|
15907
|
+
message Identity { string task_id = 1; string exec_id = 2; }
|
|
15908
|
+
message Pty {
|
|
15909
|
+
bool enabled = 1; uint32 winsz_rows = 2; uint32 winsz_cols = 3;
|
|
15910
|
+
string env_term = 4; string env_colorterm = 5; string env_term_program = 6;
|
|
15911
|
+
int32 pty_type = 7; bool no_terminate_on_idle_stdin = 8;
|
|
15912
|
+
}
|
|
15913
|
+
message Start {
|
|
15914
|
+
string task_id = 1; string exec_id = 2; repeated string command_args = 3;
|
|
15915
|
+
int32 stdout_config = 4; int32 stderr_config = 5;
|
|
15916
|
+
optional uint32 timeout_secs = 6; optional string workdir = 7;
|
|
15917
|
+
repeated string secret_ids = 8; optional Pty pty_info = 9;
|
|
15918
|
+
bool runtime_debug = 10; string container_id = 11; map<string,string> env = 12;
|
|
15919
|
+
}
|
|
15920
|
+
message Read {
|
|
15921
|
+
string task_id = 1; string exec_id = 2; uint64 offset = 3; int32 file_descriptor = 4;
|
|
15922
|
+
}
|
|
15923
|
+
message Data { bytes data = 1; }
|
|
15924
|
+
message Poll { optional int32 code = 1; optional int32 signal = 2; }
|
|
15925
|
+
message Write {
|
|
15926
|
+
string task_id = 1; string exec_id = 2; uint64 offset = 3; bytes data = 4; bool eof = 5;
|
|
15927
|
+
}
|
|
15928
|
+
message Empty {}
|
|
15929
|
+
`).root;
|
|
15930
|
+
var prefix = "/modal.task_command_router.TaskCommandRouter/";
|
|
15931
|
+
var maxPageBytes = 64 * 1024;
|
|
15932
|
+
var maxWireBytes = 4 * 1024 * 1024;
|
|
15933
|
+
var ModalCommandStartRejectedError = class extends ProviderCommandStartRejectedError {
|
|
15934
|
+
constructor(code, cause) {
|
|
15935
|
+
super(cause);
|
|
15936
|
+
this.code = code;
|
|
15937
|
+
this.name = "ModalCommandStartRejectedError";
|
|
15938
|
+
}
|
|
15939
|
+
};
|
|
15940
|
+
var ModalCommandStartPreDispatchUnavailableError = class _ModalCommandStartPreDispatchUnavailableError extends Error {
|
|
15941
|
+
constructor(cause) {
|
|
15942
|
+
super("Modal command router was not ready before Start dispatch", { cause });
|
|
15943
|
+
this.name = "ModalCommandStartPreDispatchUnavailableError";
|
|
15944
|
+
}
|
|
15945
|
+
static async ensureReady(client, signal) {
|
|
15946
|
+
signal?.throwIfAborted();
|
|
15947
|
+
await new Promise((resolve7, reject) => {
|
|
15948
|
+
let settled = false;
|
|
15949
|
+
const finish = (error) => {
|
|
15950
|
+
if (settled) return;
|
|
15951
|
+
settled = true;
|
|
15952
|
+
signal?.removeEventListener("abort", abort);
|
|
15953
|
+
if (signal?.aborted) reject(signal.reason);
|
|
15954
|
+
else if (error) reject(new _ModalCommandStartPreDispatchUnavailableError(error));
|
|
15955
|
+
else resolve7();
|
|
15956
|
+
};
|
|
15957
|
+
const abort = () => finish();
|
|
15958
|
+
signal?.addEventListener("abort", abort, { once: true });
|
|
15959
|
+
client.waitForReady(Date.now() + 5e3, finish);
|
|
15960
|
+
if (signal?.aborted) abort();
|
|
15961
|
+
});
|
|
15962
|
+
}
|
|
15963
|
+
};
|
|
15964
|
+
function encode(type, value) {
|
|
15965
|
+
const codec = modalRouterWire.lookupType(type);
|
|
15966
|
+
return Buffer.from(codec.encode(codec.fromObject(value)).finish());
|
|
15967
|
+
}
|
|
15968
|
+
var ModalCommandRouterWire = class {
|
|
15969
|
+
client;
|
|
15970
|
+
metadata;
|
|
15971
|
+
closed = false;
|
|
15972
|
+
constructor(access, trustedRoots) {
|
|
15973
|
+
const url = new URL(access.url);
|
|
15974
|
+
if (url.protocol !== "https:" || url.username || url.password || !access.jwt)
|
|
15975
|
+
throw new Error("Modal command router requires authenticated TLS");
|
|
15976
|
+
this.client = new Client(
|
|
15977
|
+
`${url.hostname}:${url.port || "443"}`,
|
|
15978
|
+
credentials.createSsl(trustedRoots),
|
|
15979
|
+
{
|
|
15980
|
+
"grpc.max_receive_message_length": maxWireBytes,
|
|
15981
|
+
"grpc.max_send_message_length": maxWireBytes,
|
|
15982
|
+
"grpc.enable_retries": 0
|
|
15983
|
+
}
|
|
15984
|
+
);
|
|
15985
|
+
this.metadata = new Metadata();
|
|
15986
|
+
this.metadata.set("authorization", `Bearer ${access.jwt}`);
|
|
15987
|
+
}
|
|
15988
|
+
close() {
|
|
15989
|
+
this.closed = true;
|
|
15990
|
+
this.client.close();
|
|
15991
|
+
}
|
|
15992
|
+
async unary(method, requestType, responseType, request, signal) {
|
|
15993
|
+
signal?.throwIfAborted();
|
|
15994
|
+
if (this.closed) throw new Error("Modal command router is closed");
|
|
15995
|
+
return await new Promise((resolve7, reject) => {
|
|
15996
|
+
const call = this.client.makeUnaryRequest(
|
|
15997
|
+
prefix + method,
|
|
15998
|
+
(value) => encode(requestType, value),
|
|
15999
|
+
(bytes) => modalRouterWire.lookupType(responseType).decode(bytes),
|
|
16000
|
+
request,
|
|
16001
|
+
this.metadata,
|
|
16002
|
+
{ deadline: Date.now() + 3e4 },
|
|
16003
|
+
(error, result) => {
|
|
16004
|
+
signal?.removeEventListener("abort", abort);
|
|
16005
|
+
if (signal?.aborted) reject(signal.reason);
|
|
16006
|
+
else if (error) reject(error);
|
|
16007
|
+
else if (result) resolve7(result);
|
|
16008
|
+
else reject(new Error("Modal command router returned no response"));
|
|
16009
|
+
}
|
|
16010
|
+
);
|
|
16011
|
+
const abort = () => call.cancel();
|
|
16012
|
+
signal?.addEventListener("abort", abort, { once: true });
|
|
16013
|
+
if (signal?.aborted) abort();
|
|
16014
|
+
});
|
|
16015
|
+
}
|
|
16016
|
+
async start(request, signal) {
|
|
16017
|
+
if (this.closed) throw new Error("Modal command router is closed");
|
|
16018
|
+
try {
|
|
16019
|
+
await ModalCommandStartPreDispatchUnavailableError.ensureReady(this.client, signal);
|
|
16020
|
+
} catch (error) {
|
|
16021
|
+
if (this.closed) throw new Error("Modal command router is closed", { cause: error });
|
|
16022
|
+
throw error;
|
|
16023
|
+
}
|
|
16024
|
+
try {
|
|
16025
|
+
await this.unary(
|
|
16026
|
+
"TaskExecStart",
|
|
16027
|
+
"Start",
|
|
16028
|
+
"Empty",
|
|
16029
|
+
{
|
|
16030
|
+
...request,
|
|
16031
|
+
stdoutConfig: 1,
|
|
16032
|
+
stderrConfig: 1
|
|
16033
|
+
},
|
|
16034
|
+
signal
|
|
16035
|
+
);
|
|
16036
|
+
} catch (error) {
|
|
16037
|
+
const code = error?.code;
|
|
16038
|
+
if (typeof code === "number" && [
|
|
16039
|
+
status.INVALID_ARGUMENT,
|
|
16040
|
+
status.NOT_FOUND,
|
|
16041
|
+
status.PERMISSION_DENIED,
|
|
16042
|
+
status.UNAUTHENTICATED,
|
|
16043
|
+
status.UNIMPLEMENTED
|
|
16044
|
+
].includes(code))
|
|
16045
|
+
throw new ModalCommandStartRejectedError(code, error);
|
|
16046
|
+
throw error;
|
|
16047
|
+
}
|
|
16048
|
+
}
|
|
16049
|
+
async write(identity, offset, data, signal) {
|
|
16050
|
+
if (!Number.isSafeInteger(offset) || offset < 0)
|
|
16051
|
+
throw new Error("Invalid Modal stdin byte offset");
|
|
16052
|
+
await this.unary(
|
|
16053
|
+
"TaskExecStdinWrite",
|
|
16054
|
+
"Write",
|
|
16055
|
+
"Empty",
|
|
16056
|
+
{ ...identity, offset, data, eof: false },
|
|
16057
|
+
signal
|
|
16058
|
+
);
|
|
16059
|
+
}
|
|
16060
|
+
async poll(identity, signal) {
|
|
16061
|
+
const value = await this.unary("TaskExecPoll", "Identity", "Poll", identity, signal);
|
|
16062
|
+
const decoded = modalRouterWire.lookupType("Poll").toObject(value);
|
|
16063
|
+
if (decoded.code !== void 0) return decoded.code;
|
|
16064
|
+
if (decoded.signal !== void 0) return 128 + decoded.signal;
|
|
16065
|
+
return null;
|
|
16066
|
+
}
|
|
16067
|
+
async read(identity, stream, offset, waitMs, signal) {
|
|
16068
|
+
signal?.throwIfAborted();
|
|
16069
|
+
if (this.closed) throw new Error("Modal command router is closed");
|
|
16070
|
+
if (!Number.isSafeInteger(offset) || offset < 0 || !Number.isFinite(waitMs) || waitMs < 0 || waitMs > 5e4)
|
|
16071
|
+
throw new Error("Invalid Modal output read bounds");
|
|
16072
|
+
const chunks = [];
|
|
16073
|
+
let length = 0;
|
|
16074
|
+
let limited = false;
|
|
16075
|
+
return await new Promise((resolve7, reject) => {
|
|
16076
|
+
const call = this.client.makeServerStreamRequest(
|
|
16077
|
+
prefix + "TaskExecStdioRead",
|
|
16078
|
+
(value) => encode("Read", value),
|
|
16079
|
+
(bytes) => modalRouterWire.lookupType("Data").decode(bytes),
|
|
16080
|
+
{ ...identity, offset, fileDescriptor: stream === "stdout" ? 0 : 1 },
|
|
16081
|
+
this.metadata,
|
|
16082
|
+
{ deadline: Date.now() + Math.max(1, waitMs) }
|
|
16083
|
+
);
|
|
16084
|
+
const abort = () => call.cancel();
|
|
16085
|
+
const finish = (eof, error) => {
|
|
16086
|
+
signal?.removeEventListener("abort", abort);
|
|
16087
|
+
if (signal?.aborted) reject(signal.reason);
|
|
16088
|
+
else if (error) reject(error);
|
|
16089
|
+
else resolve7({ bytes: Buffer.concat(chunks, length), eof });
|
|
16090
|
+
};
|
|
16091
|
+
call.on("data", (value) => {
|
|
16092
|
+
if (limited) return;
|
|
16093
|
+
const part = Buffer.from(value.data).subarray(0, maxPageBytes - length);
|
|
16094
|
+
if (part.length) {
|
|
16095
|
+
chunks.push(part);
|
|
16096
|
+
length += part.length;
|
|
16097
|
+
}
|
|
16098
|
+
if (length === maxPageBytes) {
|
|
16099
|
+
limited = true;
|
|
16100
|
+
call.cancel();
|
|
16101
|
+
}
|
|
16102
|
+
});
|
|
16103
|
+
call.once("error", (error) => {
|
|
16104
|
+
if (limited && error.code === status.CANCELLED || error.code === status.DEADLINE_EXCEEDED)
|
|
16105
|
+
finish(false);
|
|
16106
|
+
else finish(false, error);
|
|
16107
|
+
});
|
|
16108
|
+
call.once("end", () => finish(!limited));
|
|
16109
|
+
signal?.addEventListener("abort", abort, { once: true });
|
|
16110
|
+
if (signal?.aborted) abort();
|
|
16111
|
+
});
|
|
16112
|
+
}
|
|
16113
|
+
};
|
|
16114
|
+
|
|
16115
|
+
// src/sandbox/routing/routing-session.ts
|
|
16116
|
+
import { isDeepStrictEqual } from "util";
|
|
16117
|
+
|
|
15898
16118
|
// src/sandbox/materialization-verification-error.ts
|
|
15899
16119
|
var EXPLANATIONS = {
|
|
15900
16120
|
path_not_visible: "the provider could not see the destination path",
|
|
@@ -16365,6 +16585,10 @@ function eligibleForSupervision(args) {
|
|
|
16365
16585
|
args && typeof args === "object" && !args.tty && !args.runAs
|
|
16366
16586
|
);
|
|
16367
16587
|
}
|
|
16588
|
+
var READ_ONLY_PATH_PROBE_OPERATIONS = /* @__PURE__ */ new Set(["readFile", "listDir", "pathExists", "viewImage"]);
|
|
16589
|
+
function isReadOnlyPathProbeMiss(op, error) {
|
|
16590
|
+
return READ_ONLY_PATH_PROBE_OPERATIONS.has(op) && (isDefinitePathNotFoundError(error) || error instanceof SandboxWorkspaceReadNotFoundError || error instanceof SandboxFilesystemNotFoundError);
|
|
16591
|
+
}
|
|
16368
16592
|
function recordFirstOperationPhase(timing, phase, durationMs, outcome) {
|
|
16369
16593
|
if (!timing) return;
|
|
16370
16594
|
const prior = timing.phases[phase];
|
|
@@ -17172,6 +17396,9 @@ var RoutingSandboxSession = class {
|
|
|
17172
17396
|
);
|
|
17173
17397
|
outcome = "completed";
|
|
17174
17398
|
return result;
|
|
17399
|
+
} catch (error) {
|
|
17400
|
+
if (isReadOnlyPathProbeMiss(op, error)) outcome = "completed";
|
|
17401
|
+
throw error;
|
|
17175
17402
|
} finally {
|
|
17176
17403
|
try {
|
|
17177
17404
|
firstOperationObserver({
|
|
@@ -17331,6 +17558,9 @@ var RoutingSandboxSession = class {
|
|
|
17331
17558
|
}
|
|
17332
17559
|
);
|
|
17333
17560
|
providerOutcome = "completed";
|
|
17561
|
+
} catch (error) {
|
|
17562
|
+
if (isReadOnlyPathProbeMiss(op, error)) providerOutcome = "completed";
|
|
17563
|
+
throw error;
|
|
17334
17564
|
} finally {
|
|
17335
17565
|
recordFirstOperationPhase(
|
|
17336
17566
|
firstOperationTiming,
|
|
@@ -17341,7 +17571,7 @@ var RoutingSandboxSession = class {
|
|
|
17341
17571
|
}
|
|
17342
17572
|
} catch (error) {
|
|
17343
17573
|
if (reservedProcess) {
|
|
17344
|
-
if (error instanceof ProviderCommandStartRejectedError && reservedProcess.providerCommand?.kind === "modal-router-v1") {
|
|
17574
|
+
if ((error instanceof ProviderCommandStartRejectedError || error instanceof ModalCommandStartPreDispatchUnavailableError) && reservedProcess.providerCommand?.kind === "modal-router-v1") {
|
|
17345
17575
|
const persistence = this.deps.providerCommandPersistence?.(reservedProcess);
|
|
17346
17576
|
if (persistence?.rejectSupervisedLaunch) {
|
|
17347
17577
|
try {
|
|
@@ -17349,7 +17579,7 @@ var RoutingSandboxSession = class {
|
|
|
17349
17579
|
} catch (cause) {
|
|
17350
17580
|
throw new RoutingMutationOutcomeUnknownError(
|
|
17351
17581
|
op,
|
|
17352
|
-
"Never-started provider
|
|
17582
|
+
"Never-started provider call could not settle its exact reservation",
|
|
17353
17583
|
{ cause, retainedProcess: reservedProcess }
|
|
17354
17584
|
);
|
|
17355
17585
|
}
|
|
@@ -17541,7 +17771,7 @@ var RoutingSandboxSession = class {
|
|
|
17541
17771
|
return result;
|
|
17542
17772
|
} catch (error) {
|
|
17543
17773
|
materializationFailureReason = materializationVerificationDiagnostic(error)?.reason;
|
|
17544
|
-
if ((op
|
|
17774
|
+
if (isReadOnlyPathProbeMiss(op, error)) {
|
|
17545
17775
|
outcome = "not_found";
|
|
17546
17776
|
}
|
|
17547
17777
|
throw error;
|
|
@@ -19667,6 +19897,97 @@ var ChannelAPartialMutationError = class extends Error {
|
|
|
19667
19897
|
};
|
|
19668
19898
|
var REPOSITORY_DISCOVERY_LIMIT = 256;
|
|
19669
19899
|
var REPOSITORY_DISCOVERY_TRUNCATED_SENTINEL = "__OPENGENI_REPOSITORY_DISCOVERY_TRUNCATED__";
|
|
19900
|
+
var CODE_SEARCH_RG_MAX_BYTES = 64 * 1024 * 1024;
|
|
19901
|
+
var CODE_SEARCH_RG_CHUNK_BYTES = 512 * 1024;
|
|
19902
|
+
var CODE_SEARCH_RG_FRAME_CHARS = 768 * 1024;
|
|
19903
|
+
var CODE_SEARCH_RG_MAX_TRANSFER_BYTES = 8 * 1024 * 1024;
|
|
19904
|
+
var CODE_SEARCH_RG_TOKEN = /^opengeni-code-search\.[A-Za-z0-9]+$/;
|
|
19905
|
+
var CODE_SEARCH_RG_BEGIN = "__OPENGENI_CODE_SEARCH_RG_BEGIN__";
|
|
19906
|
+
var CODE_SEARCH_RG_END = "__OPENGENI_CODE_SEARCH_RG_END__";
|
|
19907
|
+
var CODE_SEARCH_RG_TRAILER = new RegExp(
|
|
19908
|
+
`${CODE_SEARCH_RG_END}(\\d+):([01])(?::(\\d+):([A-Za-z0-9._-]+)(?::(\\d+):([01]))?)?__`
|
|
19909
|
+
);
|
|
19910
|
+
var CODE_SEARCH_RG_STORE_FAILED = "code search could not store its output on this machine; its temporary directory may be full";
|
|
19911
|
+
var CODE_SEARCH_KINDS_BEGIN = "__OPENGENI_CODE_SEARCH_KINDS_BEGIN__";
|
|
19912
|
+
var CODE_SEARCH_KINDS_END = "__OPENGENI_CODE_SEARCH_KINDS_END__";
|
|
19913
|
+
var CODE_SEARCH_RG_FLAGS = /* @__PURE__ */ new Set([
|
|
19914
|
+
"--files",
|
|
19915
|
+
"--null",
|
|
19916
|
+
"--line-number",
|
|
19917
|
+
"--with-filename",
|
|
19918
|
+
"--no-heading",
|
|
19919
|
+
"-i",
|
|
19920
|
+
"-w",
|
|
19921
|
+
"--no-require-git",
|
|
19922
|
+
"--hidden"
|
|
19923
|
+
]);
|
|
19924
|
+
var CODE_SEARCH_RG_VALUE_FLAGS = /* @__PURE__ */ new Map([
|
|
19925
|
+
["--color", (value) => value === "never"],
|
|
19926
|
+
["-m", (value) => /^[1-9]\d{0,5}$/.test(value)],
|
|
19927
|
+
["--max-columns", (value) => /^[1-9]\d{0,6}$/.test(value)],
|
|
19928
|
+
["--max-filesize", (value) => /^[1-9]\d{0,9}$/.test(value)],
|
|
19929
|
+
["-g", (value) => value.length > 0 && value.length <= 512],
|
|
19930
|
+
["-e", (value) => value.length > 0 && value.length <= 16384]
|
|
19931
|
+
]);
|
|
19932
|
+
function validateCodeSearchRipgrepArgs(args, workspaceRoot = "") {
|
|
19933
|
+
const out = [];
|
|
19934
|
+
let index = 0;
|
|
19935
|
+
for (; index < args.length; index++) {
|
|
19936
|
+
const arg = args[index];
|
|
19937
|
+
if (arg.includes(NUL)) throw new ChannelAValidationError("ripgrep argument contains NUL");
|
|
19938
|
+
if (arg === "--") break;
|
|
19939
|
+
if (CODE_SEARCH_RG_FLAGS.has(arg)) {
|
|
19940
|
+
out.push(arg);
|
|
19941
|
+
continue;
|
|
19942
|
+
}
|
|
19943
|
+
const valid = CODE_SEARCH_RG_VALUE_FLAGS.get(arg);
|
|
19944
|
+
const value = args[index + 1];
|
|
19945
|
+
if (!valid || value === void 0 || value.includes(NUL) || !valid(value)) {
|
|
19946
|
+
throw new ChannelAValidationError(`ripgrep argument is not allowed: ${arg}`);
|
|
19947
|
+
}
|
|
19948
|
+
out.push(arg, value);
|
|
19949
|
+
index++;
|
|
19950
|
+
}
|
|
19951
|
+
const paths = args.slice(index + 1);
|
|
19952
|
+
if (index >= args.length || paths.length === 0) {
|
|
19953
|
+
throw new ChannelAValidationError("ripgrep arguments must end with -- and paths");
|
|
19954
|
+
}
|
|
19955
|
+
out.push("--");
|
|
19956
|
+
for (const path of paths) {
|
|
19957
|
+
if (path.includes(NUL) || path.startsWith("-") || path.startsWith("/")) {
|
|
19958
|
+
throw new ChannelAValidationError(`ripgrep path is not allowed: ${path}`);
|
|
19959
|
+
}
|
|
19960
|
+
out.push(assertSafeRelPathOrRoot(path, workspaceRoot) || ".");
|
|
19961
|
+
}
|
|
19962
|
+
return ["--no-config", ...out];
|
|
19963
|
+
}
|
|
19964
|
+
function decodeCodeSearchChunk(stdout, trailerIndex) {
|
|
19965
|
+
const begin = stdout.indexOf(CODE_SEARCH_RG_BEGIN);
|
|
19966
|
+
if (begin < 0 || begin > trailerIndex) {
|
|
19967
|
+
throw new ChannelAUnavailableError("code search output frame is missing");
|
|
19968
|
+
}
|
|
19969
|
+
const encoded = stdout.slice(begin + CODE_SEARCH_RG_BEGIN.length, trailerIndex).replace(/\s+/g, "");
|
|
19970
|
+
if (encoded.length > Math.ceil(CODE_SEARCH_RG_CHUNK_BYTES / 3) * 4) {
|
|
19971
|
+
throw new ChannelAUnavailableError("code search output frame is malformed");
|
|
19972
|
+
}
|
|
19973
|
+
return Buffer.from(encoded, "base64");
|
|
19974
|
+
}
|
|
19975
|
+
async function gunzipCodeSearchPrefix(input, limit) {
|
|
19976
|
+
const gunzip = createGunzip({ finishFlush: zlibConstants.Z_SYNC_FLUSH, chunkSize: 64 * 1024 });
|
|
19977
|
+
gunzip.end(input);
|
|
19978
|
+
const chunks = [];
|
|
19979
|
+
let size = 0;
|
|
19980
|
+
try {
|
|
19981
|
+
for await (const chunk of gunzip) {
|
|
19982
|
+
chunks.push(chunk);
|
|
19983
|
+
size += chunk.length;
|
|
19984
|
+
if (size >= limit) break;
|
|
19985
|
+
}
|
|
19986
|
+
} catch {
|
|
19987
|
+
throw new ChannelAUnavailableError("code search output could not be decoded");
|
|
19988
|
+
}
|
|
19989
|
+
return Buffer.concat(chunks, size).subarray(0, limit);
|
|
19990
|
+
}
|
|
19670
19991
|
var REPOSITORY_DISCOVERY_STATUS_PREFIX = "__OPENGENI_REPOSITORY_DISCOVERY_STATUS__:";
|
|
19671
19992
|
var NUL = String.fromCharCode(0);
|
|
19672
19993
|
var FS_LIST_TRUNCATED_MARKER = "__OPENGENI_FS_LIST_TRUNCATED__";
|
|
@@ -21257,6 +21578,215 @@ var SandboxChannelAService = class {
|
|
|
21257
21578
|
async detectRepos() {
|
|
21258
21579
|
return (await this.detectReposDetailed()).repos;
|
|
21259
21580
|
}
|
|
21581
|
+
// ═════════════════════════ Code search (worker-only) ═══════════════════════
|
|
21582
|
+
/**
|
|
21583
|
+
* Run ripgrep read-only in the workspace root for the Jev `code_search` tool.
|
|
21584
|
+
* Arguments are checked against a closed allowlist (no `--pre`, `-z` or other
|
|
21585
|
+
* flags that run programs), so the command is provably read-only and skips
|
|
21586
|
+
* durable mutation admission. stdout is capped at `maxBytes`, gzip+base64
|
|
21587
|
+
* framed so providers that drop newlines or retain limited output cannot
|
|
21588
|
+
* corrupt it, and bounded by a wall-clock watchdog (not GNU `timeout`, which
|
|
21589
|
+
* stock macOS lacks). Resolves `available: false` when `rg` is not installed,
|
|
21590
|
+
* and throws when the box could not store the output whole (a full
|
|
21591
|
+
* temporary directory) rather than returning a prefix as complete.
|
|
21592
|
+
*/
|
|
21593
|
+
async codeSearchRipgrep(args, options) {
|
|
21594
|
+
const argv = validateCodeSearchRipgrepArgs(args, this.workspaceRoot);
|
|
21595
|
+
const maxBytes = Math.max(1, Math.min(CODE_SEARCH_RG_MAX_BYTES, Math.floor(options.maxBytes)));
|
|
21596
|
+
const maxTransferBytes = Math.max(
|
|
21597
|
+
CODE_SEARCH_RG_CHUNK_BYTES,
|
|
21598
|
+
Math.min(
|
|
21599
|
+
CODE_SEARCH_RG_MAX_TRANSFER_BYTES,
|
|
21600
|
+
Math.floor(options.maxTransferBytes ?? CODE_SEARCH_RG_MAX_TRANSFER_BYTES)
|
|
21601
|
+
)
|
|
21602
|
+
);
|
|
21603
|
+
const seconds = Math.max(1, Math.min(120, Math.ceil(options.timeoutMs / 1e3)));
|
|
21604
|
+
const script = [
|
|
21605
|
+
// Remove output a previous call could not fetch (worker gone mid-transfer).
|
|
21606
|
+
// The trailing slash makes find enter a symlinked /tmp, as on macOS.
|
|
21607
|
+
`find "\${TMPDIR:-/tmp}/" -maxdepth 1 -name 'opengeni-code-search.*' -mmin +15 -exec rm -f {} + 2>/dev/null`,
|
|
21608
|
+
'gz_file=$(mktemp "${TMPDIR:-/tmp}/opengeni-code-search.XXXXXX") || exit 70',
|
|
21609
|
+
'status_file="${gz_file}.status"',
|
|
21610
|
+
'timeout_file="${gz_file}.timed-out"',
|
|
21611
|
+
"keep_gz=",
|
|
21612
|
+
"search_pid=",
|
|
21613
|
+
"watchdog_pid=",
|
|
21614
|
+
'cleanup() { if [ -n "$search_pid" ]; then kill -TERM -- "-$search_pid" 2>/dev/null || kill "$search_pid" 2>/dev/null || true; fi; if [ -n "$watchdog_pid" ]; then kill "$watchdog_pid" 2>/dev/null || true; fi; rm -f "$status_file" "$timeout_file"; [ -n "$keep_gz" ] || rm -f "$gz_file"; }',
|
|
21615
|
+
"abort() { trap - EXIT; keep_gz=; cleanup; exit 143; }",
|
|
21616
|
+
"trap cleanup EXIT",
|
|
21617
|
+
"trap abort HUP INT TERM",
|
|
21618
|
+
`if ! command -v rg >/dev/null 2>&1; then printf '${CODE_SEARCH_RG_END}127:0__'; exit 0; fi`,
|
|
21619
|
+
// Job control gives the pipeline its own process group, so the watchdog
|
|
21620
|
+
// and cleanup stop ripgrep itself rather than only the subshell. Output is
|
|
21621
|
+
// compressed once, straight into the kept file: providers retain only
|
|
21622
|
+
// about 1 MiB per output stream, and later chunks are fetched by name.
|
|
21623
|
+
// That TERM stops only ripgrep and head. The subshell and gzip ignore it
|
|
21624
|
+
// (bash 3.2 runs a trap before the pipeline ends), so a stopped search
|
|
21625
|
+
// still stores a whole member and records every stage's status.
|
|
21626
|
+
"set -m",
|
|
21627
|
+
`( trap '' TERM; ( trap - TERM; exec rg ${argv.map(shellQuote3).join(" ")} ) 2>/dev/null | ( trap - TERM; exec head -c ${maxBytes + 1} ) | gzip -c > "$gz_file"; printf '%s %s %s' "\${PIPESTATUS[0]}" "\${PIPESTATUS[1]}" "\${PIPESTATUS[2]}" > "$status_file" ) </dev/null >/dev/null 2>&1 &`,
|
|
21628
|
+
"search_pid=$!",
|
|
21629
|
+
`(sleeper_pid=; stop_watchdog() { if [ -n "$sleeper_pid" ]; then kill "$sleeper_pid" 2>/dev/null || true; wait "$sleeper_pid" 2>/dev/null || true; fi; exit 0; }; trap stop_watchdog HUP INT TERM; sleep ${seconds} & sleeper_pid=$!; wait "$sleeper_pid"; sleeper_pid=; if kill -TERM -- "-$search_pid" 2>/dev/null; then : > "$timeout_file"; fi) </dev/null >/dev/null 2>&1 &`,
|
|
21630
|
+
"watchdog_pid=$!",
|
|
21631
|
+
'wait "$search_pid" 2>/dev/null',
|
|
21632
|
+
"search_pid=",
|
|
21633
|
+
'kill "$watchdog_pid" 2>/dev/null || true',
|
|
21634
|
+
'wait "$watchdog_pid" 2>/dev/null || true',
|
|
21635
|
+
"watchdog_pid=",
|
|
21636
|
+
"set +m",
|
|
21637
|
+
"rg_status= head_status= gzip_status=",
|
|
21638
|
+
'if [ -f "$status_file" ]; then read -r rg_status head_status gzip_status < "$status_file"; fi',
|
|
21639
|
+
'[ -n "$rg_status" ] || rg_status=125',
|
|
21640
|
+
"timed_out=0",
|
|
21641
|
+
'if [ -f "$timeout_file" ]; then timed_out=1; fi',
|
|
21642
|
+
// A full temporary directory cuts the member without a word, so decode it
|
|
21643
|
+
// end to end: that yields the raw size and fails on a cut or corrupt member.
|
|
21644
|
+
"stored=0",
|
|
21645
|
+
`if raw_size=$(set -o pipefail; gzip -dc < "$gz_file" 2>/dev/null | wc -c | tr -d ' ') && [ "$head_status" = 0 ] && [ "$gzip_status" = 0 ]; then stored=1; fi`,
|
|
21646
|
+
'[ -n "$raw_size" ] || raw_size=0',
|
|
21647
|
+
`gz_size=$(wc -c < "$gz_file" | tr -d ' ')`,
|
|
21648
|
+
'[ -n "$gz_size" ] || gz_size=0',
|
|
21649
|
+
"token=-",
|
|
21650
|
+
// The worker fails an unstored search that did not time out, so only
|
|
21651
|
+
// keep output it will fetch.
|
|
21652
|
+
`if [ "$gz_size" -gt ${CODE_SEARCH_RG_CHUNK_BYTES} ] && { [ "$stored" = 1 ] || [ "$timed_out" = 1 ]; }; then keep_gz=1; token=$(basename "$gz_file"); fi`,
|
|
21653
|
+
`printf '${CODE_SEARCH_RG_BEGIN}'`,
|
|
21654
|
+
`head -c ${CODE_SEARCH_RG_CHUNK_BYTES} "$gz_file" | base64 | tr -d '\\r\\n'`,
|
|
21655
|
+
`printf '${CODE_SEARCH_RG_END}%s:%s:%s:%s:%s:%s__' "$rg_status" "$timed_out" "$gz_size" "$token" "$raw_size" "$stored"`
|
|
21656
|
+
].join("\n");
|
|
21657
|
+
const { stdout } = await this.runReadOnly({
|
|
21658
|
+
cmd: internalBashCommand(script),
|
|
21659
|
+
workdir: this.providerWorkspaceRoot(),
|
|
21660
|
+
yieldTimeMs: (seconds + 15) * 1e3,
|
|
21661
|
+
maxOutputTokens: CODE_SEARCH_RG_FRAME_CHARS
|
|
21662
|
+
});
|
|
21663
|
+
const trailer = CODE_SEARCH_RG_TRAILER.exec(stdout);
|
|
21664
|
+
if (!trailer) {
|
|
21665
|
+
throw new ChannelAUnavailableError("code search did not complete in this workspace");
|
|
21666
|
+
}
|
|
21667
|
+
const exitCode = Number.parseInt(trailer[1], 10);
|
|
21668
|
+
if (exitCode === 127) {
|
|
21669
|
+
return { available: false, stdout: "", exitCode: null, truncated: false, timedOut: false };
|
|
21670
|
+
}
|
|
21671
|
+
if (trailer[3] === void 0 || trailer[5] === void 0) {
|
|
21672
|
+
throw new ChannelAUnavailableError("code search output trailer is malformed");
|
|
21673
|
+
}
|
|
21674
|
+
const timedOut = trailer[2] === "1";
|
|
21675
|
+
if (!timedOut && exitCode === 125) {
|
|
21676
|
+
throw new ChannelAUnavailableError(
|
|
21677
|
+
"code search did not record its status on this machine; its temporary directory may be full"
|
|
21678
|
+
);
|
|
21679
|
+
}
|
|
21680
|
+
if (!timedOut && trailer[6] !== "1") {
|
|
21681
|
+
throw new ChannelAUnavailableError(CODE_SEARCH_RG_STORE_FAILED);
|
|
21682
|
+
}
|
|
21683
|
+
const first = decodeCodeSearchChunk(stdout, trailer.index);
|
|
21684
|
+
const reportedSize = Number.parseInt(trailer[3], 10);
|
|
21685
|
+
const rawSize = Number.parseInt(trailer[5], 10);
|
|
21686
|
+
const token = trailer[4] === "-" ? null : trailer[4];
|
|
21687
|
+
const parts = [first];
|
|
21688
|
+
let fetched = first.length;
|
|
21689
|
+
if (token !== null && reportedSize > fetched) {
|
|
21690
|
+
if (!CODE_SEARCH_RG_TOKEN.test(token)) {
|
|
21691
|
+
throw new ChannelAUnavailableError("code search output frame is malformed");
|
|
21692
|
+
}
|
|
21693
|
+
const target = Math.min(reportedSize, maxTransferBytes);
|
|
21694
|
+
const maxCalls = Math.ceil(maxTransferBytes / CODE_SEARCH_RG_CHUNK_BYTES);
|
|
21695
|
+
for (let call = 0; call < maxCalls && fetched < target; call++) {
|
|
21696
|
+
const length = Math.min(CODE_SEARCH_RG_CHUNK_BYTES, target - fetched);
|
|
21697
|
+
const chunk = await this.codeSearchRipgrepChunk(
|
|
21698
|
+
token,
|
|
21699
|
+
fetched,
|
|
21700
|
+
length,
|
|
21701
|
+
fetched + length >= target
|
|
21702
|
+
);
|
|
21703
|
+
if (chunk === null) break;
|
|
21704
|
+
parts.push(chunk);
|
|
21705
|
+
fetched += chunk.length;
|
|
21706
|
+
if (chunk.length < length) break;
|
|
21707
|
+
}
|
|
21708
|
+
}
|
|
21709
|
+
const transferCut = fetched < reportedSize;
|
|
21710
|
+
const compressed = Buffer.concat(parts);
|
|
21711
|
+
const raw = compressed.length ? await gunzipCodeSearchPrefix(compressed, maxBytes + 1) : Buffer.alloc(0);
|
|
21712
|
+
const rawCut = raw.length > maxBytes;
|
|
21713
|
+
const sizeMismatch = !rawCut && !transferCut && !timedOut && raw.length !== rawSize;
|
|
21714
|
+
const truncated = rawCut || transferCut || sizeMismatch;
|
|
21715
|
+
let text = (rawCut ? raw.subarray(0, maxBytes) : raw).toString("utf8");
|
|
21716
|
+
if (truncated || timedOut) {
|
|
21717
|
+
const lastNewline = text.lastIndexOf("\n");
|
|
21718
|
+
text = lastNewline >= 0 ? text.slice(0, lastNewline + 1) : "";
|
|
21719
|
+
}
|
|
21720
|
+
return {
|
|
21721
|
+
available: true,
|
|
21722
|
+
stdout: text,
|
|
21723
|
+
exitCode: timedOut ? null : exitCode,
|
|
21724
|
+
truncated,
|
|
21725
|
+
timedOut
|
|
21726
|
+
};
|
|
21727
|
+
}
|
|
21728
|
+
/**
|
|
21729
|
+
* Fetch one chunk of compressed ripgrep output kept on the box by a previous
|
|
21730
|
+
* codeSearchRipgrep call. The last fetch deletes the file. Null when the
|
|
21731
|
+
* file is gone.
|
|
21732
|
+
*/
|
|
21733
|
+
async codeSearchRipgrepChunk(token, offset, length, last) {
|
|
21734
|
+
const script = [
|
|
21735
|
+
'f="${TMPDIR:-/tmp}/$1"',
|
|
21736
|
+
`if [ ! -f "$f" ]; then printf '${CODE_SEARCH_RG_END}0:0:0:missing__'; exit 0; fi`,
|
|
21737
|
+
`printf '${CODE_SEARCH_RG_BEGIN}'`,
|
|
21738
|
+
`tail -c +"$2" "$f" | head -c "$3" | base64 | tr -d '\\r\\n'`,
|
|
21739
|
+
`printf '${CODE_SEARCH_RG_END}0:0:0:-__'`,
|
|
21740
|
+
'if [ "$4" = 1 ]; then rm -f "$f"; fi'
|
|
21741
|
+
].join("\n");
|
|
21742
|
+
const { stdout } = await this.runReadOnly({
|
|
21743
|
+
cmd: `${internalBashCommand(script)} opengeni-code-search ${shellQuote3(token)} ${offset + 1} ${length} ${last ? 1 : 0}`,
|
|
21744
|
+
workdir: this.providerWorkspaceRoot(),
|
|
21745
|
+
yieldTimeMs: 3e4,
|
|
21746
|
+
maxOutputTokens: CODE_SEARCH_RG_FRAME_CHARS
|
|
21747
|
+
});
|
|
21748
|
+
const trailer = CODE_SEARCH_RG_TRAILER.exec(stdout);
|
|
21749
|
+
if (!trailer) {
|
|
21750
|
+
throw new ChannelAUnavailableError("code search did not complete in this workspace");
|
|
21751
|
+
}
|
|
21752
|
+
if (trailer[4] === "missing") return null;
|
|
21753
|
+
const chunk = decodeCodeSearchChunk(stdout, trailer.index);
|
|
21754
|
+
if (chunk.length > length) {
|
|
21755
|
+
throw new ChannelAUnavailableError("code search output frame is malformed");
|
|
21756
|
+
}
|
|
21757
|
+
return chunk;
|
|
21758
|
+
}
|
|
21759
|
+
/** Classify workspace-relative paths for `code_search` path filters. */
|
|
21760
|
+
async codeSearchPathKinds(paths) {
|
|
21761
|
+
const checked = paths.map((path) => {
|
|
21762
|
+
if (path.startsWith("-")) throw new ChannelAValidationError(`invalid path: ${path}`);
|
|
21763
|
+
return assertSafeRelPathOrRoot(path, this.workspaceRoot) || ".";
|
|
21764
|
+
});
|
|
21765
|
+
if (checked.length === 0) return {};
|
|
21766
|
+
const script = [
|
|
21767
|
+
`printf '${CODE_SEARCH_KINDS_BEGIN}'`,
|
|
21768
|
+
'for p in "$@"; do if [ -d "$p" ]; then printf d; elif [ -e "$p" ]; then printf f; else printf m; fi; done',
|
|
21769
|
+
`printf '${CODE_SEARCH_KINDS_END}'`
|
|
21770
|
+
].join("\n");
|
|
21771
|
+
const { stdout } = await this.runReadOnly({
|
|
21772
|
+
cmd: `${internalBashCommand(script)} opengeni-code-search ${checked.map(shellQuote3).join(" ")}`,
|
|
21773
|
+
workdir: this.providerWorkspaceRoot(),
|
|
21774
|
+
yieldTimeMs: 2e4,
|
|
21775
|
+
maxOutputTokens: 4096
|
|
21776
|
+
});
|
|
21777
|
+
const match = new RegExp(`${CODE_SEARCH_KINDS_BEGIN}([dfm]*)${CODE_SEARCH_KINDS_END}`).exec(
|
|
21778
|
+
stdout
|
|
21779
|
+
);
|
|
21780
|
+
if (!match || match[1].length !== checked.length) {
|
|
21781
|
+
throw new ChannelAUnavailableError("code search path check did not complete");
|
|
21782
|
+
}
|
|
21783
|
+
const kinds = {};
|
|
21784
|
+
paths.forEach((path, index) => {
|
|
21785
|
+
const code = match[1][index];
|
|
21786
|
+
kinds[path] = code === "d" ? "directory" : code === "f" ? "file" : "missing";
|
|
21787
|
+
});
|
|
21788
|
+
return kinds;
|
|
21789
|
+
}
|
|
21260
21790
|
// ════════════════════════ Terminal exec + PTY (A2) ════════════════════════
|
|
21261
21791
|
/** Run a bounded command to physical completion and return buffered output.
|
|
21262
21792
|
* A deadline cancels the exact provider operation, proves its process group
|
|
@@ -22716,183 +23246,6 @@ var ModalCommandControl = class _ModalCommandControl {
|
|
|
22716
23246
|
}
|
|
22717
23247
|
};
|
|
22718
23248
|
|
|
22719
|
-
// src/sandbox/providers/modal-command-router-wire.ts
|
|
22720
|
-
import { Client, Metadata, credentials, status } from "@grpc/grpc-js";
|
|
22721
|
-
import protobuf from "protobufjs";
|
|
22722
|
-
var modalRouterWire = protobuf.parse(`syntax = "proto3";
|
|
22723
|
-
message Identity { string task_id = 1; string exec_id = 2; }
|
|
22724
|
-
message Pty {
|
|
22725
|
-
bool enabled = 1; uint32 winsz_rows = 2; uint32 winsz_cols = 3;
|
|
22726
|
-
string env_term = 4; string env_colorterm = 5; string env_term_program = 6;
|
|
22727
|
-
int32 pty_type = 7; bool no_terminate_on_idle_stdin = 8;
|
|
22728
|
-
}
|
|
22729
|
-
message Start {
|
|
22730
|
-
string task_id = 1; string exec_id = 2; repeated string command_args = 3;
|
|
22731
|
-
int32 stdout_config = 4; int32 stderr_config = 5;
|
|
22732
|
-
optional uint32 timeout_secs = 6; optional string workdir = 7;
|
|
22733
|
-
repeated string secret_ids = 8; optional Pty pty_info = 9;
|
|
22734
|
-
bool runtime_debug = 10; string container_id = 11; map<string,string> env = 12;
|
|
22735
|
-
}
|
|
22736
|
-
message Read {
|
|
22737
|
-
string task_id = 1; string exec_id = 2; uint64 offset = 3; int32 file_descriptor = 4;
|
|
22738
|
-
}
|
|
22739
|
-
message Data { bytes data = 1; }
|
|
22740
|
-
message Poll { optional int32 code = 1; optional int32 signal = 2; }
|
|
22741
|
-
message Write {
|
|
22742
|
-
string task_id = 1; string exec_id = 2; uint64 offset = 3; bytes data = 4; bool eof = 5;
|
|
22743
|
-
}
|
|
22744
|
-
message Empty {}
|
|
22745
|
-
`).root;
|
|
22746
|
-
var prefix = "/modal.task_command_router.TaskCommandRouter/";
|
|
22747
|
-
var maxPageBytes = 64 * 1024;
|
|
22748
|
-
var maxWireBytes = 4 * 1024 * 1024;
|
|
22749
|
-
var ModalCommandStartRejectedError = class extends ProviderCommandStartRejectedError {
|
|
22750
|
-
constructor(code, cause) {
|
|
22751
|
-
super(cause);
|
|
22752
|
-
this.code = code;
|
|
22753
|
-
this.name = "ModalCommandStartRejectedError";
|
|
22754
|
-
}
|
|
22755
|
-
};
|
|
22756
|
-
function encode(type, value) {
|
|
22757
|
-
const codec = modalRouterWire.lookupType(type);
|
|
22758
|
-
return Buffer.from(codec.encode(codec.fromObject(value)).finish());
|
|
22759
|
-
}
|
|
22760
|
-
var ModalCommandRouterWire = class {
|
|
22761
|
-
client;
|
|
22762
|
-
metadata;
|
|
22763
|
-
closed = false;
|
|
22764
|
-
constructor(access, trustedRoots) {
|
|
22765
|
-
const url = new URL(access.url);
|
|
22766
|
-
if (url.protocol !== "https:" || url.username || url.password || !access.jwt)
|
|
22767
|
-
throw new Error("Modal command router requires authenticated TLS");
|
|
22768
|
-
this.client = new Client(url.host, credentials.createSsl(trustedRoots), {
|
|
22769
|
-
"grpc.max_receive_message_length": maxWireBytes,
|
|
22770
|
-
"grpc.max_send_message_length": maxWireBytes,
|
|
22771
|
-
"grpc.enable_retries": 0
|
|
22772
|
-
});
|
|
22773
|
-
this.metadata = new Metadata();
|
|
22774
|
-
this.metadata.set("authorization", `Bearer ${access.jwt}`);
|
|
22775
|
-
}
|
|
22776
|
-
close() {
|
|
22777
|
-
this.closed = true;
|
|
22778
|
-
this.client.close();
|
|
22779
|
-
}
|
|
22780
|
-
async unary(method, requestType, responseType, request, signal) {
|
|
22781
|
-
signal?.throwIfAborted();
|
|
22782
|
-
if (this.closed) throw new Error("Modal command router is closed");
|
|
22783
|
-
return await new Promise((resolve7, reject) => {
|
|
22784
|
-
const call = this.client.makeUnaryRequest(
|
|
22785
|
-
prefix + method,
|
|
22786
|
-
(value) => encode(requestType, value),
|
|
22787
|
-
(bytes) => modalRouterWire.lookupType(responseType).decode(bytes),
|
|
22788
|
-
request,
|
|
22789
|
-
this.metadata,
|
|
22790
|
-
{ deadline: Date.now() + 3e4 },
|
|
22791
|
-
(error, result) => {
|
|
22792
|
-
signal?.removeEventListener("abort", abort);
|
|
22793
|
-
if (signal?.aborted) reject(signal.reason);
|
|
22794
|
-
else if (error) reject(error);
|
|
22795
|
-
else if (result) resolve7(result);
|
|
22796
|
-
else reject(new Error("Modal command router returned no response"));
|
|
22797
|
-
}
|
|
22798
|
-
);
|
|
22799
|
-
const abort = () => call.cancel();
|
|
22800
|
-
signal?.addEventListener("abort", abort, { once: true });
|
|
22801
|
-
if (signal?.aborted) abort();
|
|
22802
|
-
});
|
|
22803
|
-
}
|
|
22804
|
-
async start(request, signal) {
|
|
22805
|
-
try {
|
|
22806
|
-
await this.unary(
|
|
22807
|
-
"TaskExecStart",
|
|
22808
|
-
"Start",
|
|
22809
|
-
"Empty",
|
|
22810
|
-
{
|
|
22811
|
-
...request,
|
|
22812
|
-
stdoutConfig: 1,
|
|
22813
|
-
stderrConfig: 1
|
|
22814
|
-
},
|
|
22815
|
-
signal
|
|
22816
|
-
);
|
|
22817
|
-
} catch (error) {
|
|
22818
|
-
const code = error?.code;
|
|
22819
|
-
if (typeof code === "number" && [
|
|
22820
|
-
status.INVALID_ARGUMENT,
|
|
22821
|
-
status.NOT_FOUND,
|
|
22822
|
-
status.PERMISSION_DENIED,
|
|
22823
|
-
status.UNAUTHENTICATED,
|
|
22824
|
-
status.UNIMPLEMENTED
|
|
22825
|
-
].includes(code))
|
|
22826
|
-
throw new ModalCommandStartRejectedError(code, error);
|
|
22827
|
-
throw error;
|
|
22828
|
-
}
|
|
22829
|
-
}
|
|
22830
|
-
async write(identity, offset, data, signal) {
|
|
22831
|
-
if (!Number.isSafeInteger(offset) || offset < 0)
|
|
22832
|
-
throw new Error("Invalid Modal stdin byte offset");
|
|
22833
|
-
await this.unary(
|
|
22834
|
-
"TaskExecStdinWrite",
|
|
22835
|
-
"Write",
|
|
22836
|
-
"Empty",
|
|
22837
|
-
{ ...identity, offset, data, eof: false },
|
|
22838
|
-
signal
|
|
22839
|
-
);
|
|
22840
|
-
}
|
|
22841
|
-
async poll(identity, signal) {
|
|
22842
|
-
const value = await this.unary("TaskExecPoll", "Identity", "Poll", identity, signal);
|
|
22843
|
-
const decoded = modalRouterWire.lookupType("Poll").toObject(value);
|
|
22844
|
-
if (decoded.code !== void 0) return decoded.code;
|
|
22845
|
-
if (decoded.signal !== void 0) return 128 + decoded.signal;
|
|
22846
|
-
return null;
|
|
22847
|
-
}
|
|
22848
|
-
async read(identity, stream, offset, waitMs, signal) {
|
|
22849
|
-
signal?.throwIfAborted();
|
|
22850
|
-
if (this.closed) throw new Error("Modal command router is closed");
|
|
22851
|
-
if (!Number.isSafeInteger(offset) || offset < 0 || !Number.isFinite(waitMs) || waitMs < 0 || waitMs > 5e4)
|
|
22852
|
-
throw new Error("Invalid Modal output read bounds");
|
|
22853
|
-
const chunks = [];
|
|
22854
|
-
let length = 0;
|
|
22855
|
-
let limited = false;
|
|
22856
|
-
return await new Promise((resolve7, reject) => {
|
|
22857
|
-
const call = this.client.makeServerStreamRequest(
|
|
22858
|
-
prefix + "TaskExecStdioRead",
|
|
22859
|
-
(value) => encode("Read", value),
|
|
22860
|
-
(bytes) => modalRouterWire.lookupType("Data").decode(bytes),
|
|
22861
|
-
{ ...identity, offset, fileDescriptor: stream === "stdout" ? 0 : 1 },
|
|
22862
|
-
this.metadata,
|
|
22863
|
-
{ deadline: Date.now() + Math.max(1, waitMs) }
|
|
22864
|
-
);
|
|
22865
|
-
const abort = () => call.cancel();
|
|
22866
|
-
const finish = (eof, error) => {
|
|
22867
|
-
signal?.removeEventListener("abort", abort);
|
|
22868
|
-
if (signal?.aborted) reject(signal.reason);
|
|
22869
|
-
else if (error) reject(error);
|
|
22870
|
-
else resolve7({ bytes: Buffer.concat(chunks, length), eof });
|
|
22871
|
-
};
|
|
22872
|
-
call.on("data", (value) => {
|
|
22873
|
-
if (limited) return;
|
|
22874
|
-
const part = Buffer.from(value.data).subarray(0, maxPageBytes - length);
|
|
22875
|
-
if (part.length) {
|
|
22876
|
-
chunks.push(part);
|
|
22877
|
-
length += part.length;
|
|
22878
|
-
}
|
|
22879
|
-
if (length === maxPageBytes) {
|
|
22880
|
-
limited = true;
|
|
22881
|
-
call.cancel();
|
|
22882
|
-
}
|
|
22883
|
-
});
|
|
22884
|
-
call.once("error", (error) => {
|
|
22885
|
-
if (limited && error.code === status.CANCELLED || error.code === status.DEADLINE_EXCEEDED)
|
|
22886
|
-
finish(false);
|
|
22887
|
-
else finish(false, error);
|
|
22888
|
-
});
|
|
22889
|
-
call.once("end", () => finish(!limited));
|
|
22890
|
-
signal?.addEventListener("abort", abort, { once: true });
|
|
22891
|
-
if (signal?.aborted) abort();
|
|
22892
|
-
});
|
|
22893
|
-
}
|
|
22894
|
-
};
|
|
22895
|
-
|
|
22896
23249
|
// src/sandbox/providers/modal-command-control.ts
|
|
22897
23250
|
var ModalCommandControl2 = class _ModalCommandControl {
|
|
22898
23251
|
constructor(client, sandboxIdentity, root, environment) {
|
|
@@ -23063,7 +23416,8 @@ var ModalCommandControl2 = class _ModalCommandControl {
|
|
|
23063
23416
|
)
|
|
23064
23417
|
);
|
|
23065
23418
|
} catch (error) {
|
|
23066
|
-
if (!supervision || error instanceof ModalCommandStartRejectedError
|
|
23419
|
+
if (!supervision || error instanceof ModalCommandStartRejectedError || error instanceof ModalCommandStartPreDispatchUnavailableError)
|
|
23420
|
+
throw error;
|
|
23067
23421
|
}
|
|
23068
23422
|
return command;
|
|
23069
23423
|
}
|
|
@@ -23329,8 +23683,6 @@ function installModalListDirCompatibility(session) {
|
|
|
23329
23683
|
}
|
|
23330
23684
|
var MODAL_EXEC_STDIN_WRITE_PATH = "/modal.task_command_router.TaskCommandRouter/TaskExecStdinWrite";
|
|
23331
23685
|
var MODAL_EXEC_ALREADY_COMPLETED_DETAILS = /^Exec has already completed; stdin is no longer accepting writes(?: \(Error code: [A-Z0-9]+\))?$/;
|
|
23332
|
-
var MODAL_TASK_EXEC_START_PATH = "/modal.task_command_router.TaskCommandRouter/TaskExecStart";
|
|
23333
|
-
var MODAL_TASK_EXEC_START_DNS_RESOLUTION_DETAILS = /^Name resolution failed for target dns:task-[a-z0-9]+\.w\.modal\.host:443$/;
|
|
23334
23686
|
var MODAL_TASK_EXEC_START_ERROR_MAX_DEPTH = 8;
|
|
23335
23687
|
var MODAL_TASK_EXEC_START_ERROR_MAX_NODES = 64;
|
|
23336
23688
|
var MODAL_TASK_EXEC_START_ERROR_MAX_AGGREGATE_ERRORS = 32;
|
|
@@ -23352,10 +23704,7 @@ function hasContradictoryModalHttpStatus(record) {
|
|
|
23352
23704
|
}
|
|
23353
23705
|
return values.some((value) => modalHttpStatus(value) !== null);
|
|
23354
23706
|
}
|
|
23355
|
-
function
|
|
23356
|
-
return record.name === "ClientError" && record.path === MODAL_TASK_EXEC_START_PATH && (record.code === 14 || record.code === "UNAVAILABLE") && typeof record.details === "string" && MODAL_TASK_EXEC_START_DNS_RESOLUTION_DETAILS.test(record.details);
|
|
23357
|
-
}
|
|
23358
|
-
function isModalTaskExecStartDnsResolutionError(error) {
|
|
23707
|
+
function isModalTaskExecStartPreDispatchUnavailableError(error) {
|
|
23359
23708
|
const pending = [{ depth: 0, value: error }];
|
|
23360
23709
|
const seen = /* @__PURE__ */ new WeakSet();
|
|
23361
23710
|
let inspected = 0;
|
|
@@ -23372,7 +23721,12 @@ function isModalTaskExecStartDnsResolutionError(error) {
|
|
|
23372
23721
|
let nested;
|
|
23373
23722
|
try {
|
|
23374
23723
|
const record = current.value;
|
|
23724
|
+
if (isRoutingMutationOutcomeUnknownError(current.value)) return false;
|
|
23375
23725
|
if (hasContradictoryModalHttpStatus(record)) return false;
|
|
23726
|
+
if (current.value instanceof ModalCommandStartPreDispatchUnavailableError) {
|
|
23727
|
+
matchingLeaves += 1;
|
|
23728
|
+
continue;
|
|
23729
|
+
}
|
|
23376
23730
|
nested = [];
|
|
23377
23731
|
for (const key of ["cause", "error"]) {
|
|
23378
23732
|
const value = record[key];
|
|
@@ -23385,11 +23739,7 @@ function isModalTaskExecStartDnsResolutionError(error) {
|
|
|
23385
23739
|
}
|
|
23386
23740
|
nested.push(...errors);
|
|
23387
23741
|
}
|
|
23388
|
-
if (nested.length === 0)
|
|
23389
|
-
if (!isModalTaskExecStartDnsResolutionLeaf(record)) return false;
|
|
23390
|
-
matchingLeaves += 1;
|
|
23391
|
-
continue;
|
|
23392
|
-
}
|
|
23742
|
+
if (nested.length === 0) return false;
|
|
23393
23743
|
} catch {
|
|
23394
23744
|
return false;
|
|
23395
23745
|
}
|
|
@@ -33049,6 +33399,7 @@ function shellQuote5(value) {
|
|
|
33049
33399
|
// src/sandbox/browser-control-client.ts
|
|
33050
33400
|
import { createHash as createHash7, randomUUID as randomUUID8 } from "crypto";
|
|
33051
33401
|
import { posix as posixPath2 } from "path";
|
|
33402
|
+
import { parseBrowserFrameMetadata } from "@opengeni/sdk";
|
|
33052
33403
|
import {
|
|
33053
33404
|
BROWSER_CONTROL_MAX_JSON_BYTES,
|
|
33054
33405
|
BROWSER_CONTROL_PORT as BROWSER_CONTROL_PORT2,
|
|
@@ -33063,6 +33414,8 @@ import {
|
|
|
33063
33414
|
BrowserDownloadExportReceipt,
|
|
33064
33415
|
BrowserDownloadExportRequest,
|
|
33065
33416
|
BrowserDownloadListResponse,
|
|
33417
|
+
BrowserDomReadRequest,
|
|
33418
|
+
BrowserDomReadResponse,
|
|
33066
33419
|
BrowserExternalAuthCommand,
|
|
33067
33420
|
BrowserExternalAuthResult,
|
|
33068
33421
|
BrowserObservation,
|
|
@@ -33070,6 +33423,7 @@ import {
|
|
|
33070
33423
|
BrowserProtectedAuthFillReceipt,
|
|
33071
33424
|
BrowserRevisionMaterialization,
|
|
33072
33425
|
BrowserTarget,
|
|
33426
|
+
BrowserTargetState,
|
|
33073
33427
|
BrowserWorkspaceFileStageRequest,
|
|
33074
33428
|
BrowserWorkspaceFileStageResponse,
|
|
33075
33429
|
ComputerActionCommand,
|
|
@@ -33536,7 +33890,10 @@ var BrowserControlClient = class {
|
|
|
33536
33890
|
return await this.requestJson(input);
|
|
33537
33891
|
}
|
|
33538
33892
|
async requestBytesForSession(input) {
|
|
33539
|
-
return await this.requestBytes(input);
|
|
33893
|
+
return await this.requestBytes({ ...input, surface: "computer" });
|
|
33894
|
+
}
|
|
33895
|
+
async requestBrowserBytesForSession(input) {
|
|
33896
|
+
return await this.requestBytes({ ...input, surface: "browser" });
|
|
33540
33897
|
}
|
|
33541
33898
|
async requestJson(input, retryNativeEndpoint = true) {
|
|
33542
33899
|
const requireHostFetch = this.session.requireHostFetchController === true;
|
|
@@ -33772,10 +34129,10 @@ var BrowserControlClient = class {
|
|
|
33772
34129
|
const data = await readBytes(
|
|
33773
34130
|
this.session,
|
|
33774
34131
|
responsePath,
|
|
33775
|
-
COMPUTER_SCREENSHOT_MAX_BYTES,
|
|
34132
|
+
input.surface === "browser" ? 24 * 1024 * 1024 : COMPUTER_SCREENSHOT_MAX_BYTES,
|
|
33776
34133
|
timeoutMs
|
|
33777
34134
|
);
|
|
33778
|
-
return
|
|
34135
|
+
return controllerImageFrame(data, headers, input);
|
|
33779
34136
|
} catch (error) {
|
|
33780
34137
|
if (error instanceof BrowserControlRequestError || error instanceof BrowserControlProtocolError || error instanceof RangeError) {
|
|
33781
34138
|
throw error;
|
|
@@ -33923,6 +34280,75 @@ var BrowserControlSessionClient = class {
|
|
|
33923
34280
|
})
|
|
33924
34281
|
);
|
|
33925
34282
|
}
|
|
34283
|
+
async targetState(targetId) {
|
|
34284
|
+
let state;
|
|
34285
|
+
try {
|
|
34286
|
+
state = BrowserTargetState.parse(
|
|
34287
|
+
await this.parent.requestForSession({
|
|
34288
|
+
method: "GET",
|
|
34289
|
+
path: this.targetPath(targetId, "state"),
|
|
34290
|
+
token: this.viewToken
|
|
34291
|
+
})
|
|
34292
|
+
);
|
|
34293
|
+
} catch (error) {
|
|
34294
|
+
if (!(error instanceof BrowserControlRequestError) || error.status !== 404 || error.error.code !== "resource_not_found" || error.error.message !== "route not found") {
|
|
34295
|
+
throw error;
|
|
34296
|
+
}
|
|
34297
|
+
const observation = await this.observe(targetId);
|
|
34298
|
+
state = BrowserTargetState.parse({
|
|
34299
|
+
browserSessionId: observation.browserSessionId,
|
|
34300
|
+
controllerGeneration: observation.target.controllerGeneration,
|
|
34301
|
+
targetId: observation.target.id,
|
|
34302
|
+
targetGeneration: observation.target.targetGeneration,
|
|
34303
|
+
documentGeneration: observation.target.documentGeneration,
|
|
34304
|
+
frameId: observation.frameId
|
|
34305
|
+
});
|
|
34306
|
+
}
|
|
34307
|
+
if (state.browserSessionId !== this.reference.browserSessionId || state.controllerGeneration !== this.reference.controllerGeneration || state.targetId !== targetId) {
|
|
34308
|
+
throw new BrowserControlProtocolError("browser target state belongs to another binding");
|
|
34309
|
+
}
|
|
34310
|
+
return state;
|
|
34311
|
+
}
|
|
34312
|
+
async readDom(targetId, requestInput) {
|
|
34313
|
+
const request = BrowserDomReadRequest.parse(requestInput);
|
|
34314
|
+
const result = BrowserDomReadResponse.parse(
|
|
34315
|
+
await this.parent.requestForSession({
|
|
34316
|
+
method: "POST",
|
|
34317
|
+
path: this.targetPath(targetId, "dom-read"),
|
|
34318
|
+
token: this.viewToken,
|
|
34319
|
+
body: request
|
|
34320
|
+
})
|
|
34321
|
+
);
|
|
34322
|
+
if (result.browserSessionId !== this.reference.browserSessionId || result.controllerGeneration !== this.reference.controllerGeneration || result.targetId !== targetId || result.targetGeneration !== request.expectedTargetGeneration || result.documentGeneration !== request.expectedDocumentGeneration || result.frameId !== request.expectedFrameId || result.kind !== request.kind) {
|
|
34323
|
+
throw new BrowserControlProtocolError("browser DOM read belongs to another binding");
|
|
34324
|
+
}
|
|
34325
|
+
return result;
|
|
34326
|
+
}
|
|
34327
|
+
async capture(targetId, options = {}) {
|
|
34328
|
+
const query = new URLSearchParams({
|
|
34329
|
+
format: options.format ?? "jpeg",
|
|
34330
|
+
quality: String(boundedInteger(options.quality ?? 55, 1, 100, "browser screenshot quality"))
|
|
34331
|
+
});
|
|
34332
|
+
if (options.format !== void 0 && options.format !== "jpeg" && options.format !== "png") {
|
|
34333
|
+
throw new RangeError("browser screenshot format is invalid");
|
|
34334
|
+
}
|
|
34335
|
+
if (options.fullPage !== void 0) {
|
|
34336
|
+
if (typeof options.fullPage !== "boolean") {
|
|
34337
|
+
throw new RangeError("browser screenshot fullPage is invalid");
|
|
34338
|
+
}
|
|
34339
|
+
query.set("fullPage", String(options.fullPage));
|
|
34340
|
+
}
|
|
34341
|
+
return await this.parent.requestBrowserBytesForSession({
|
|
34342
|
+
method: "GET",
|
|
34343
|
+
path: this.targetPath(targetId, `screenshot?${query}`),
|
|
34344
|
+
token: this.viewToken,
|
|
34345
|
+
expectedFrameEvidence: {
|
|
34346
|
+
browserSessionId: this.reference.browserSessionId,
|
|
34347
|
+
controllerGeneration: this.reference.controllerGeneration,
|
|
34348
|
+
targetId: requireOpaqueId(targetId, "browser target id")
|
|
34349
|
+
}
|
|
34350
|
+
});
|
|
34351
|
+
}
|
|
33926
34352
|
async readClipboard() {
|
|
33927
34353
|
return BrowserClipboard.parse(
|
|
33928
34354
|
await this.parent.requestForSession({
|
|
@@ -34256,12 +34682,69 @@ async function requestExposedControllerBytes(endpoint, input, defaultTimeoutMs)
|
|
|
34256
34682
|
throw new BrowserControlProtocolError("browser controller image response is invalid");
|
|
34257
34683
|
}
|
|
34258
34684
|
const declaredLength = Number(response.headers.get("content-length"));
|
|
34259
|
-
|
|
34260
|
-
|
|
34261
|
-
|
|
34685
|
+
const maxBytes = input.surface === "browser" ? 24 * 1024 * 1024 : COMPUTER_SCREENSHOT_MAX_BYTES;
|
|
34686
|
+
if (Number.isFinite(declaredLength) && declaredLength > maxBytes) {
|
|
34687
|
+
await response.body?.cancel("frame exceeds its byte bound").catch(() => void 0);
|
|
34688
|
+
throw new RangeError("frame exceeds its byte bound");
|
|
34262
34689
|
}
|
|
34263
|
-
const data =
|
|
34264
|
-
return
|
|
34690
|
+
const data = await readBoundedFrameResponse(response, maxBytes);
|
|
34691
|
+
return controllerImageFrame(data, response.headers, input);
|
|
34692
|
+
}
|
|
34693
|
+
async function readBoundedFrameResponse(response, maxBytes) {
|
|
34694
|
+
if (!response.body) throw new BrowserControlProtocolError("browser controller image is empty");
|
|
34695
|
+
const reader = response.body.getReader();
|
|
34696
|
+
const chunks = [];
|
|
34697
|
+
let size = 0;
|
|
34698
|
+
try {
|
|
34699
|
+
for (; ; ) {
|
|
34700
|
+
const { done, value } = await reader.read();
|
|
34701
|
+
if (done) break;
|
|
34702
|
+
size += value.byteLength;
|
|
34703
|
+
if (size > maxBytes) {
|
|
34704
|
+
await reader.cancel("frame exceeds its byte bound").catch(() => void 0);
|
|
34705
|
+
throw new RangeError("frame exceeds its byte bound");
|
|
34706
|
+
}
|
|
34707
|
+
chunks.push(value);
|
|
34708
|
+
}
|
|
34709
|
+
} finally {
|
|
34710
|
+
reader.releaseLock();
|
|
34711
|
+
}
|
|
34712
|
+
const data = new Uint8Array(size);
|
|
34713
|
+
let offset = 0;
|
|
34714
|
+
for (const chunk of chunks) {
|
|
34715
|
+
data.set(chunk, offset);
|
|
34716
|
+
offset += chunk.byteLength;
|
|
34717
|
+
}
|
|
34718
|
+
return data;
|
|
34719
|
+
}
|
|
34720
|
+
function controllerImageFrame(data, headers, input) {
|
|
34721
|
+
return input.surface === "browser" ? browserControlFrame(data, headers, input.expectedFrameEvidence) : computerControlFrame(data, headers, input.expectedFrameEvidence);
|
|
34722
|
+
}
|
|
34723
|
+
function browserControlFrame(data, headers, expected) {
|
|
34724
|
+
if (data.byteLength < 1 || data.byteLength > 24 * 1024 * 1024) {
|
|
34725
|
+
throw new RangeError("browser frame exceeds its byte bound");
|
|
34726
|
+
}
|
|
34727
|
+
const get = (name) => headers.get(name.toLowerCase()) ?? null;
|
|
34728
|
+
const mediaType = get("content-type")?.split(";", 1)[0]?.trim().toLowerCase();
|
|
34729
|
+
if (mediaType !== "image/jpeg" && mediaType !== "image/png") {
|
|
34730
|
+
throw new BrowserControlProtocolError("browser frame media type is invalid");
|
|
34731
|
+
}
|
|
34732
|
+
const metadataHeader = get("x-opengeni-browser-frame");
|
|
34733
|
+
if (!metadataHeader || metadataHeader.length > 64 * 1024 || !/^[A-Za-z0-9_-]+$/u.test(metadataHeader)) {
|
|
34734
|
+
throw new BrowserControlProtocolError("browser frame metadata is invalid");
|
|
34735
|
+
}
|
|
34736
|
+
let metadata;
|
|
34737
|
+
try {
|
|
34738
|
+
metadata = parseBrowserFrameMetadata(
|
|
34739
|
+
JSON.parse(Buffer.from(metadataHeader, "base64url").toString("utf8"))
|
|
34740
|
+
);
|
|
34741
|
+
} catch {
|
|
34742
|
+
throw new BrowserControlProtocolError("browser frame metadata is invalid");
|
|
34743
|
+
}
|
|
34744
|
+
if (metadata.browserSessionId !== expected.browserSessionId || metadata.controllerGeneration !== expected.controllerGeneration || metadata.targetId !== expected.targetId || metadata.mediaType !== mediaType) {
|
|
34745
|
+
throw new BrowserControlProtocolError("browser frame evidence does not match its request");
|
|
34746
|
+
}
|
|
34747
|
+
return { data, mediaType, metadataHeader, metadata };
|
|
34265
34748
|
}
|
|
34266
34749
|
function computerControlFrame(data, headers, expected) {
|
|
34267
34750
|
if (data.byteLength < 1 || data.byteLength > COMPUTER_SCREENSHOT_MAX_BYTES) {
|
|
@@ -34936,7 +35419,7 @@ async function readText(session, path, maxBytes, timeoutMs) {
|
|
|
34936
35419
|
}
|
|
34937
35420
|
async function readBytes(session, path, maxBytes, timeoutMs) {
|
|
34938
35421
|
const privatePath = absolutePrivatePath(path, "browser private binary response path");
|
|
34939
|
-
if (!Number.isSafeInteger(maxBytes) || maxBytes < 1 || maxBytes > COMPUTER_SCREENSHOT_MAX_BYTES) {
|
|
35422
|
+
if (!Number.isSafeInteger(maxBytes) || maxBytes < 1 || maxBytes > Math.max(24 * 1024 * 1024, COMPUTER_SCREENSHOT_MAX_BYTES)) {
|
|
34940
35423
|
throw new RangeError("browser private binary response limit is invalid");
|
|
34941
35424
|
}
|
|
34942
35425
|
const sizeMarker = `OPENGENI_BROWSER_PRIVATE_SIZE_${randomUUID8()}`;
|
|
@@ -34955,7 +35438,7 @@ async function readBytes(session, path, maxBytes, timeoutMs) {
|
|
|
34955
35438
|
"browser private binary response size"
|
|
34956
35439
|
);
|
|
34957
35440
|
if (size < 1 || size > maxBytes) {
|
|
34958
|
-
throw new RangeError("
|
|
35441
|
+
throw new RangeError("frame exceeds its byte bound");
|
|
34959
35442
|
}
|
|
34960
35443
|
const chunks = [];
|
|
34961
35444
|
for (let offset = 0; offset < size; offset += PRIVATE_READ_CHUNK_BYTES) {
|
|
@@ -38353,6 +38836,8 @@ export {
|
|
|
38353
38836
|
ChannelAUnsupportedError,
|
|
38354
38837
|
ChannelAPartialMutationError,
|
|
38355
38838
|
REPOSITORY_DISCOVERY_LIMIT,
|
|
38839
|
+
CODE_SEARCH_RG_MAX_BYTES,
|
|
38840
|
+
validateCodeSearchRipgrepArgs,
|
|
38356
38841
|
SandboxChannelAService,
|
|
38357
38842
|
stripExecBanner,
|
|
38358
38843
|
isDefinitePathNotFoundError,
|
|
@@ -38388,7 +38873,7 @@ export {
|
|
|
38388
38873
|
assertDescriptorRegistryInvariants,
|
|
38389
38874
|
modalSandboxAttributionEnvironment,
|
|
38390
38875
|
modalSandboxAttributionTags,
|
|
38391
|
-
|
|
38876
|
+
isModalTaskExecStartPreDispatchUnavailableError,
|
|
38392
38877
|
ensureModalRegistryImage,
|
|
38393
38878
|
resolveModalCheckpointProviderBindingForSession,
|
|
38394
38879
|
modalSessionMatchesCheckpointProviderBinding,
|
|
@@ -38571,4 +39056,4 @@ export {
|
|
|
38571
39056
|
collectSandboxEnvironment2 as collectSandboxEnvironment,
|
|
38572
39057
|
parseExposedPorts2 as parseExposedPorts
|
|
38573
39058
|
};
|
|
38574
|
-
//# sourceMappingURL=chunk-
|
|
39059
|
+
//# sourceMappingURL=chunk-FAFZCSN7.js.map
|