@opengeni/runtime 4.0.3 → 4.1.0-canary.36239117573001
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/assets/codemode-client.json +1 -1
- package/dist/{chunk-GBVN7WSK.js → chunk-ORTF5RRC.js} +371 -28
- package/dist/chunk-ORTF5RRC.js.map +1 -0
- package/dist/{chunk-LL2YQO5H.js → chunk-YAA7WDNO.js} +638 -71
- package/dist/chunk-YAA7WDNO.js.map +1 -0
- 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 +18 -2
- package/dist/knowledge-model-projection.d.ts +28 -0
- 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/channel-a.d.ts +43 -0
- package/dist/sandbox/index.d.ts +1 -1
- package/dist/sandbox/index.js +5 -1
- package/dist/tool-result-spill.d.ts +11 -3
- package/dist/workspace-tool-gateway.js +2 -2
- package/package.json +11 -11
- package/src/codex-tool-search.ts +54 -6
- package/src/context-compaction.ts +19 -3
- package/src/index.ts +277 -56
- package/src/knowledge-model-projection.ts +169 -0
- 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 +70 -25
- package/src/sandbox/channel-a.ts +370 -0
- package/src/sandbox/index.ts +3 -0
- package/src/sandbox/routing/routing-session.ts +25 -4
- package/src/tool-result-spill.ts +17 -5
- package/dist/chunk-GBVN7WSK.js.map +0 -1
- package/dist/chunk-LL2YQO5H.js.map +0 -1
|
@@ -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;
|
|
@@ -15848,6 +15851,9 @@ function notifyDurableOpOwnershipTransferred(opId) {
|
|
|
15848
15851
|
storage.getStore()?.onDurableOpOwnershipTransferred?.(opId);
|
|
15849
15852
|
}
|
|
15850
15853
|
|
|
15854
|
+
// src/sandbox/routing/routing-session.ts
|
|
15855
|
+
import { SandboxFilesystemNotFoundError } from "modal";
|
|
15856
|
+
|
|
15851
15857
|
// src/sandbox/providers/modal-command-router-wire.ts
|
|
15852
15858
|
import { Client, Metadata, credentials, status } from "@grpc/grpc-js";
|
|
15853
15859
|
import protobuf from "protobufjs";
|
|
@@ -16579,6 +16585,10 @@ function eligibleForSupervision(args) {
|
|
|
16579
16585
|
args && typeof args === "object" && !args.tty && !args.runAs
|
|
16580
16586
|
);
|
|
16581
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
|
+
}
|
|
16582
16592
|
function recordFirstOperationPhase(timing, phase, durationMs, outcome) {
|
|
16583
16593
|
if (!timing) return;
|
|
16584
16594
|
const prior = timing.phases[phase];
|
|
@@ -17386,6 +17396,9 @@ var RoutingSandboxSession = class {
|
|
|
17386
17396
|
);
|
|
17387
17397
|
outcome = "completed";
|
|
17388
17398
|
return result;
|
|
17399
|
+
} catch (error) {
|
|
17400
|
+
if (isReadOnlyPathProbeMiss(op, error)) outcome = "completed";
|
|
17401
|
+
throw error;
|
|
17389
17402
|
} finally {
|
|
17390
17403
|
try {
|
|
17391
17404
|
firstOperationObserver({
|
|
@@ -17545,6 +17558,9 @@ var RoutingSandboxSession = class {
|
|
|
17545
17558
|
}
|
|
17546
17559
|
);
|
|
17547
17560
|
providerOutcome = "completed";
|
|
17561
|
+
} catch (error) {
|
|
17562
|
+
if (isReadOnlyPathProbeMiss(op, error)) providerOutcome = "completed";
|
|
17563
|
+
throw error;
|
|
17548
17564
|
} finally {
|
|
17549
17565
|
recordFirstOperationPhase(
|
|
17550
17566
|
firstOperationTiming,
|
|
@@ -17755,7 +17771,7 @@ var RoutingSandboxSession = class {
|
|
|
17755
17771
|
return result;
|
|
17756
17772
|
} catch (error) {
|
|
17757
17773
|
materializationFailureReason = materializationVerificationDiagnostic(error)?.reason;
|
|
17758
|
-
if ((op
|
|
17774
|
+
if (isReadOnlyPathProbeMiss(op, error)) {
|
|
17759
17775
|
outcome = "not_found";
|
|
17760
17776
|
}
|
|
17761
17777
|
throw error;
|
|
@@ -19881,6 +19897,97 @@ var ChannelAPartialMutationError = class extends Error {
|
|
|
19881
19897
|
};
|
|
19882
19898
|
var REPOSITORY_DISCOVERY_LIMIT = 256;
|
|
19883
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
|
+
}
|
|
19884
19991
|
var REPOSITORY_DISCOVERY_STATUS_PREFIX = "__OPENGENI_REPOSITORY_DISCOVERY_STATUS__:";
|
|
19885
19992
|
var NUL = String.fromCharCode(0);
|
|
19886
19993
|
var FS_LIST_TRUNCATED_MARKER = "__OPENGENI_FS_LIST_TRUNCATED__";
|
|
@@ -21471,6 +21578,215 @@ var SandboxChannelAService = class {
|
|
|
21471
21578
|
async detectRepos() {
|
|
21472
21579
|
return (await this.detectReposDetailed()).repos;
|
|
21473
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
|
+
}
|
|
21474
21790
|
// ════════════════════════ Terminal exec + PTY (A2) ════════════════════════
|
|
21475
21791
|
/** Run a bounded command to physical completion and return buffered output.
|
|
21476
21792
|
* A deadline cancels the exact provider operation, proves its process group
|
|
@@ -33600,7 +33916,7 @@ var BrowserControlClient = class {
|
|
|
33600
33916
|
this.session.runtimeMetrics
|
|
33601
33917
|
);
|
|
33602
33918
|
} catch (error) {
|
|
33603
|
-
if (retryNativeEndpoint && !requireHostFetch && error instanceof BrowserControlTransportError) {
|
|
33919
|
+
if (retryNativeEndpoint && !requireHostFetch && input.method === "GET" && error instanceof BrowserControlTransportError) {
|
|
33604
33920
|
return await this.requestJson(input, false);
|
|
33605
33921
|
}
|
|
33606
33922
|
throw error;
|
|
@@ -33695,7 +34011,7 @@ var BrowserControlClient = class {
|
|
|
33695
34011
|
throw error;
|
|
33696
34012
|
}
|
|
33697
34013
|
if (error instanceof BrowserControlTransportError) {
|
|
33698
|
-
if (retryNativeEndpoint && this.nativeAuthority && this.session.ensureBrowserControl) {
|
|
34014
|
+
if (retryNativeEndpoint && input.method === "GET" && this.nativeAuthority && this.session.ensureBrowserControl) {
|
|
33699
34015
|
nativeControllerPorts.delete(nativeControllerKey(this.nativeAuthority));
|
|
33700
34016
|
await this.controllerPort();
|
|
33701
34017
|
return await this.requestJson(input, false);
|
|
@@ -34312,21 +34628,24 @@ async function requestExposedController(endpoint, input, defaultTimeoutMs, metri
|
|
|
34312
34628
|
if (body !== void 0 && Buffer.byteLength(body) > BROWSER_CONTROL_MAX_JSON_BYTES) {
|
|
34313
34629
|
throw new RangeError("browser controller request body is too large");
|
|
34314
34630
|
}
|
|
34315
|
-
const response = await
|
|
34316
|
-
|
|
34317
|
-
|
|
34318
|
-
|
|
34319
|
-
|
|
34320
|
-
|
|
34321
|
-
|
|
34322
|
-
|
|
34323
|
-
|
|
34324
|
-
|
|
34325
|
-
|
|
34326
|
-
|
|
34631
|
+
const response = await controllerTransport(
|
|
34632
|
+
async () => await fetch(streamUrl, {
|
|
34633
|
+
method: input.method,
|
|
34634
|
+
headers: {
|
|
34635
|
+
...exposedPortHeaders(endpoint),
|
|
34636
|
+
authorization: `Bearer ${token}`,
|
|
34637
|
+
...body === void 0 ? {} : { "content-type": "application/json" }
|
|
34638
|
+
},
|
|
34639
|
+
...body === void 0 ? {} : { body },
|
|
34640
|
+
redirect: "manual",
|
|
34641
|
+
signal: AbortSignal.timeout(timeoutMs)
|
|
34642
|
+
})
|
|
34643
|
+
);
|
|
34644
|
+
const responseText = await controllerTransport(async () => await response.text());
|
|
34327
34645
|
if (Buffer.byteLength(responseText) > BROWSER_CONTROL_MAX_JSON_BYTES) {
|
|
34328
34646
|
throw new BrowserControlProtocolError("browser controller response is too large");
|
|
34329
34647
|
}
|
|
34648
|
+
rejectProviderGatewayResponse(responseText, response.status);
|
|
34330
34649
|
if ((response.status === 401 || response.status === 403) && !looksLikeBrowserControlEnvelope(responseText)) {
|
|
34331
34650
|
const signed = parseOpenSandboxSignedUriPath(endpoint.path ?? "");
|
|
34332
34651
|
if (signed) {
|
|
@@ -34347,21 +34666,24 @@ async function requestExposedControllerBytes(endpoint, input, defaultTimeoutMs)
|
|
|
34347
34666
|
const timeoutMs = boundedTimeout2(input.timeoutMs ?? defaultTimeoutMs);
|
|
34348
34667
|
const streamUrl = exposedControllerUrl(endpoint, input.path);
|
|
34349
34668
|
streamUrl.protocol = streamUrl.protocol === "wss:" ? "https:" : "http:";
|
|
34350
|
-
const response = await
|
|
34351
|
-
|
|
34352
|
-
|
|
34353
|
-
|
|
34354
|
-
|
|
34355
|
-
|
|
34356
|
-
|
|
34357
|
-
|
|
34358
|
-
|
|
34359
|
-
|
|
34669
|
+
const response = await controllerTransport(
|
|
34670
|
+
async () => await fetch(streamUrl, {
|
|
34671
|
+
method: input.method,
|
|
34672
|
+
headers: {
|
|
34673
|
+
...exposedPortHeaders(endpoint),
|
|
34674
|
+
authorization: `Bearer ${token}`,
|
|
34675
|
+
accept: "image/jpeg, image/png"
|
|
34676
|
+
},
|
|
34677
|
+
redirect: "manual",
|
|
34678
|
+
signal: AbortSignal.timeout(timeoutMs)
|
|
34679
|
+
})
|
|
34680
|
+
);
|
|
34360
34681
|
if (!response.ok) {
|
|
34361
|
-
const responseText = await response.text();
|
|
34682
|
+
const responseText = await controllerTransport(async () => await response.text());
|
|
34362
34683
|
if (Buffer.byteLength(responseText) > BROWSER_CONTROL_MAX_JSON_BYTES) {
|
|
34363
34684
|
throw new BrowserControlProtocolError("browser controller response is too large");
|
|
34364
34685
|
}
|
|
34686
|
+
rejectProviderGatewayResponse(responseText, response.status);
|
|
34365
34687
|
parseEnvelope(responseText, response.status);
|
|
34366
34688
|
throw new BrowserControlProtocolError("browser controller image response is invalid");
|
|
34367
34689
|
}
|
|
@@ -34371,9 +34693,23 @@ async function requestExposedControllerBytes(endpoint, input, defaultTimeoutMs)
|
|
|
34371
34693
|
await response.body?.cancel("frame exceeds its byte bound").catch(() => void 0);
|
|
34372
34694
|
throw new RangeError("frame exceeds its byte bound");
|
|
34373
34695
|
}
|
|
34374
|
-
const data = await
|
|
34696
|
+
const data = await controllerTransport(
|
|
34697
|
+
async () => await readBoundedFrameResponse(response, maxBytes)
|
|
34698
|
+
);
|
|
34375
34699
|
return controllerImageFrame(data, response.headers, input);
|
|
34376
34700
|
}
|
|
34701
|
+
async function controllerTransport(readOrDispatch) {
|
|
34702
|
+
try {
|
|
34703
|
+
return await readOrDispatch();
|
|
34704
|
+
} catch (error) {
|
|
34705
|
+
if (error instanceof BrowserControlProtocolError || error instanceof BrowserControlTransportError || error instanceof RangeError) {
|
|
34706
|
+
throw error;
|
|
34707
|
+
}
|
|
34708
|
+
throw new BrowserControlTransportError("browser controller host-fetch failed", {
|
|
34709
|
+
cause: error
|
|
34710
|
+
});
|
|
34711
|
+
}
|
|
34712
|
+
}
|
|
34377
34713
|
async function readBoundedFrameResponse(response, maxBytes) {
|
|
34378
34714
|
if (!response.body) throw new BrowserControlProtocolError("browser controller image is empty");
|
|
34379
34715
|
const reader = response.body.getReader();
|
|
@@ -34572,6 +34908,11 @@ function looksLikeBrowserControlEnvelope(body) {
|
|
|
34572
34908
|
return false;
|
|
34573
34909
|
}
|
|
34574
34910
|
}
|
|
34911
|
+
function rejectProviderGatewayResponse(body, status2) {
|
|
34912
|
+
if ([502, 503, 504].includes(status2) && !looksLikeBrowserControlEnvelope(body)) {
|
|
34913
|
+
throw new BrowserControlTransportError(`browser controller returned HTTP ${status2}`);
|
|
34914
|
+
}
|
|
34915
|
+
}
|
|
34575
34916
|
function parseEnvelope(body, status2) {
|
|
34576
34917
|
let value;
|
|
34577
34918
|
try {
|
|
@@ -38520,6 +38861,8 @@ export {
|
|
|
38520
38861
|
ChannelAUnsupportedError,
|
|
38521
38862
|
ChannelAPartialMutationError,
|
|
38522
38863
|
REPOSITORY_DISCOVERY_LIMIT,
|
|
38864
|
+
CODE_SEARCH_RG_MAX_BYTES,
|
|
38865
|
+
validateCodeSearchRipgrepArgs,
|
|
38523
38866
|
SandboxChannelAService,
|
|
38524
38867
|
stripExecBanner,
|
|
38525
38868
|
isDefinitePathNotFoundError,
|
|
@@ -38738,4 +39081,4 @@ export {
|
|
|
38738
39081
|
collectSandboxEnvironment2 as collectSandboxEnvironment,
|
|
38739
39082
|
parseExposedPorts2 as parseExposedPorts
|
|
38740
39083
|
};
|
|
38741
|
-
//# sourceMappingURL=chunk-
|
|
39084
|
+
//# sourceMappingURL=chunk-ORTF5RRC.js.map
|