@opengeni/runtime 0.11.0 → 0.13.0
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/{chunk-YUAFQEFY.js → chunk-65UDUFUO.js} +729 -118
- package/dist/chunk-65UDUFUO.js.map +1 -0
- package/dist/chunk-HAN5D27J.js +292 -0
- package/dist/chunk-HAN5D27J.js.map +1 -0
- package/dist/{index-D6yi-JcU.d.ts → index-DGUrY_yR.d.ts} +37 -7
- package/dist/index.d.ts +81 -10
- package/dist/index.js +696 -93
- package/dist/index.js.map +1 -1
- package/dist/mcp-network.d.ts +78 -0
- package/dist/mcp-network.js +62 -0
- package/dist/mcp-network.js.map +1 -0
- package/dist/sandbox/index.d.ts +1 -1
- package/dist/sandbox/index.js +1 -1
- package/package.json +9 -4
- package/src/codex-tool-search.ts +20 -1
- package/src/index.ts +487 -83
- package/src/mcp-network.ts +330 -0
- package/src/sandbox/channel-a.ts +3 -28
- package/src/sandbox/display-stack.ts +860 -23
- package/src/sandbox/exec-banner.ts +67 -0
- package/src/sandbox/index.ts +7 -0
- package/src/sandbox/providers/modal.ts +29 -2
- package/src/sandbox-computer.ts +3 -1
- package/src/usage-telemetry.ts +495 -51
- package/dist/chunk-YUAFQEFY.js.map +0 -1
|
@@ -13274,8 +13274,8 @@ async function sweepModalOrphanSandboxes(settings, liveLeases, options = {}) {
|
|
|
13274
13274
|
if (liveByInstance) {
|
|
13275
13275
|
if (leaseId !== liveByInstance.leaseId || workspaceId !== liveByInstance.workspaceId || sandboxGroupId !== liveByInstance.sandboxGroupId) {
|
|
13276
13276
|
try {
|
|
13277
|
-
const
|
|
13278
|
-
await
|
|
13277
|
+
const sandbox2 = await modal.sandboxes.fromId(info.id);
|
|
13278
|
+
await sandbox2.setTags(
|
|
13279
13279
|
modalSandboxAttributionTags({
|
|
13280
13280
|
leaseId: liveByInstance.leaseId,
|
|
13281
13281
|
workspaceId: liveByInstance.workspaceId,
|
|
@@ -13306,10 +13306,28 @@ async function sweepModalOrphanSandboxes(settings, liveLeases, options = {}) {
|
|
|
13306
13306
|
skipped += 1;
|
|
13307
13307
|
continue;
|
|
13308
13308
|
}
|
|
13309
|
+
const candidate = { sandboxId: info.id, reason, tags };
|
|
13310
|
+
let sandbox;
|
|
13311
|
+
try {
|
|
13312
|
+
sandbox = await modal.sandboxes.fromId(info.id);
|
|
13313
|
+
} catch {
|
|
13314
|
+
skipped += 1;
|
|
13315
|
+
continue;
|
|
13316
|
+
}
|
|
13317
|
+
if (options.revalidateTermination) {
|
|
13318
|
+
try {
|
|
13319
|
+
if (!await options.revalidateTermination(candidate)) {
|
|
13320
|
+
skipped += 1;
|
|
13321
|
+
continue;
|
|
13322
|
+
}
|
|
13323
|
+
} catch {
|
|
13324
|
+
skipped += 1;
|
|
13325
|
+
continue;
|
|
13326
|
+
}
|
|
13327
|
+
}
|
|
13309
13328
|
try {
|
|
13310
|
-
const sandbox = await modal.sandboxes.fromId(info.id);
|
|
13311
13329
|
await sandbox.terminate();
|
|
13312
|
-
terminated.push(
|
|
13330
|
+
terminated.push(candidate);
|
|
13313
13331
|
} catch {
|
|
13314
13332
|
skipped += 1;
|
|
13315
13333
|
}
|
|
@@ -14823,11 +14841,11 @@ var NatsControlRpc = class {
|
|
|
14823
14841
|
}
|
|
14824
14842
|
}
|
|
14825
14843
|
};
|
|
14826
|
-
function offlineControlResponse(
|
|
14827
|
-
return { requestId, error: offlineAgentError(void 0, neverSent), result: void 0 };
|
|
14844
|
+
function offlineControlResponse(requestId2, neverSent = false) {
|
|
14845
|
+
return { requestId: requestId2, error: offlineAgentError(void 0, neverSent), result: void 0 };
|
|
14828
14846
|
}
|
|
14829
|
-
function timeoutControlResponse(
|
|
14830
|
-
return { requestId, error: timeoutAgentError(), result: void 0 };
|
|
14847
|
+
function timeoutControlResponse(requestId2) {
|
|
14848
|
+
return { requestId: requestId2, error: timeoutAgentError(), result: void 0 };
|
|
14831
14849
|
}
|
|
14832
14850
|
|
|
14833
14851
|
// src/sandbox/selfhosted/session.ts
|
|
@@ -15407,10 +15425,10 @@ var OpConsumer = class {
|
|
|
15407
15425
|
* `SelfhostedControlError` on any AgentError. `requestId` is overridable
|
|
15408
15426
|
* because OpStart's REQUEST ID IS THE OP ID (its idempotency key) and must
|
|
15409
15427
|
* be STABLE across retries — every other op uses a fresh UUID per attempt. */
|
|
15410
|
-
async controlOp(op,
|
|
15428
|
+
async controlOp(op, requestId2 = crypto.randomUUID()) {
|
|
15411
15429
|
const res = await this.deps.controlRpc.request(
|
|
15412
15430
|
this.deps.rpcSubject,
|
|
15413
|
-
{ requestId, epoch: this.deps.epoch, op },
|
|
15431
|
+
{ requestId: requestId2, epoch: this.deps.epoch, op },
|
|
15414
15432
|
{ timeoutMs: this.deps.controlTimeoutMs }
|
|
15415
15433
|
);
|
|
15416
15434
|
if (res.error || !res.result) {
|
|
@@ -17732,8 +17750,61 @@ async function verifyStreamToken(secret, token, nowSeconds = Math.floor(Date.now
|
|
|
17732
17750
|
|
|
17733
17751
|
// src/sandbox/display-stack.ts
|
|
17734
17752
|
import { DESKTOP_STREAM_PORT as DESKTOP_STREAM_PORT4 } from "@opengeni/contracts";
|
|
17753
|
+
|
|
17754
|
+
// src/sandbox/exec-banner.ts
|
|
17755
|
+
var EXEC_BANNER_HEADER_MAX_CHARS = 16 * 1024;
|
|
17756
|
+
var OUTPUT_DELIMITER = /\r?\nOutput:\r?\n/u;
|
|
17757
|
+
var SDK_METADATA_LINE = /^(?:Chunk ID:|Wall time:|Original token count:|Output:|Process (?:running with session ID|exited with code))/mu;
|
|
17758
|
+
var RUNNING_LINE = /^Process running with session ID (\d+)$/u;
|
|
17759
|
+
var EXITED_LINE = /^Process exited with code (-?\d+)$/u;
|
|
17760
|
+
var PROCESS_STATUS_PREFIX = /^Process (?:running with session ID|exited with code)/u;
|
|
17761
|
+
function parseExecResponseBanner(raw) {
|
|
17762
|
+
const delimiter = OUTPUT_DELIMITER.exec(raw);
|
|
17763
|
+
if (!delimiter) {
|
|
17764
|
+
return SDK_METADATA_LINE.test(raw) ? { kind: "invalid" } : { kind: "absent" };
|
|
17765
|
+
}
|
|
17766
|
+
if (delimiter.index > EXEC_BANNER_HEADER_MAX_CHARS) return { kind: "invalid" };
|
|
17767
|
+
const header = raw.slice(0, delimiter.index);
|
|
17768
|
+
const status = [];
|
|
17769
|
+
for (const line of header.split(/\r?\n/u)) {
|
|
17770
|
+
const running = RUNNING_LINE.exec(line);
|
|
17771
|
+
if (running) {
|
|
17772
|
+
status.push({ kind: "running", value: running[1] });
|
|
17773
|
+
continue;
|
|
17774
|
+
}
|
|
17775
|
+
const exited = EXITED_LINE.exec(line);
|
|
17776
|
+
if (exited) {
|
|
17777
|
+
status.push({ kind: "exited", value: exited[1] });
|
|
17778
|
+
continue;
|
|
17779
|
+
}
|
|
17780
|
+
if (PROCESS_STATUS_PREFIX.test(line)) return { kind: "invalid" };
|
|
17781
|
+
}
|
|
17782
|
+
if (status.length !== 1) return { kind: "invalid" };
|
|
17783
|
+
const terminal = status[0];
|
|
17784
|
+
const value = Number(terminal.value);
|
|
17785
|
+
if (!Number.isSafeInteger(value)) return { kind: "invalid" };
|
|
17786
|
+
if (terminal.kind === "running") {
|
|
17787
|
+
return value >= 0 ? { kind: "running", sessionId: value } : { kind: "invalid" };
|
|
17788
|
+
}
|
|
17789
|
+
return { kind: "exited", exitCode: value };
|
|
17790
|
+
}
|
|
17791
|
+
function parseExecBannerSessionId(raw) {
|
|
17792
|
+
const banner = parseExecResponseBanner(raw);
|
|
17793
|
+
return banner.kind === "running" ? banner.sessionId : null;
|
|
17794
|
+
}
|
|
17795
|
+
function parseExecBannerExitCode(raw) {
|
|
17796
|
+
const banner = parseExecResponseBanner(raw);
|
|
17797
|
+
return banner.kind === "exited" ? banner.exitCode : null;
|
|
17798
|
+
}
|
|
17799
|
+
|
|
17800
|
+
// src/sandbox/display-stack.ts
|
|
17735
17801
|
var STREAM_PORT = DESKTOP_STREAM_PORT4;
|
|
17736
17802
|
var DISPLAY_STACK_TIMEOUT_MS = 9e4;
|
|
17803
|
+
var DISPLAY_STACK_PROVIDER_YIELD_MS = 15e3;
|
|
17804
|
+
var DISPLAY_STACK_PROVIDER_POLL_MS = 5e3;
|
|
17805
|
+
var DISPLAY_STACK_TERMINATION_GRACE_MS = 5e3;
|
|
17806
|
+
var DISPLAY_STACK_CLEANUP_CALL_MS = 250;
|
|
17807
|
+
var DISPLAY_STACK_DRAIN_GRACE_MS = 100;
|
|
17737
17808
|
var PAINT_PROBE_ATTEMPTS = 150;
|
|
17738
17809
|
var PAINT_PROBE_INTERVAL_S = 0.2;
|
|
17739
17810
|
var PAINT_MIN_BYTES = 6e4;
|
|
@@ -17743,7 +17814,7 @@ var DisplayStackError = class extends Error {
|
|
|
17743
17814
|
exitCode;
|
|
17744
17815
|
stage;
|
|
17745
17816
|
constructor(exitCode, output) {
|
|
17746
|
-
const stage = exitCode === 11 ? "xvfb" : exitCode === 12 ? "x11vnc" : exitCode === 13 ? "websockify" : exitCode === 14 ? "paint" : "unknown";
|
|
17817
|
+
const stage = exitCode === 11 ? "xvfb" : exitCode === 12 ? "x11vnc" : exitCode === 13 ? "websockify" : exitCode === 14 ? "paint" : exitCode === 15 ? "lock" : exitCode === 124 || /OPENGENI_DISPLAY_TIMEOUT/u.test(output) ? "timeout" : "unknown";
|
|
17747
17818
|
super(
|
|
17748
17819
|
`desktop display stack failed at stage "${stage}" (exit ${exitCode})${output ? `:
|
|
17749
17820
|
${output}` : ""}`
|
|
@@ -17763,9 +17834,11 @@ function buildDisplayStackScript(options = {}) {
|
|
|
17763
17834
|
const geometry = options.geometry ?? DEFAULT_DESKTOP_GEOMETRY;
|
|
17764
17835
|
const port = options.port ?? DESKTOP_STREAM_PORT4;
|
|
17765
17836
|
const env = `DESKTOP_W=${geometry.width} DESKTOP_H=${geometry.height} DESKTOP_DPI=${geometry.dpi} STREAM_PORT=${port}`;
|
|
17766
|
-
const
|
|
17767
|
-
const
|
|
17768
|
-
|
|
17837
|
+
const lockedLauncher = `ds_started=$1; fw=$2; ds_class=cold; ds_stage() { ds_now=$(date +%s%3N); echo "OPENGENI_DISPLAY_STAGE stage=$1 elapsed_ms=$((ds_now-ds_started)) classification=$ds_class"; }; fa=$(date +%s%3N); fd=$((fa-fw)); [ "$fd" -ge 50 ] && ds_class=contention; ds_stage outer_flock_acquired; ds_stage launcher_issued; env ${env} opengeni-desktop-up; urc=$?; ds_stage launcher_complete; exit "$urc"`;
|
|
17838
|
+
const bringUp = `if nc -z 127.0.0.1 ${port} >/dev/null 2>&1 && nc -z 127.0.0.1 5900 >/dev/null 2>&1; then ds_class=already_ready; ds_stage precheck_ready; echo "OPENGENI_DESKTOP_UP port=${port} geometry=${geometry.width}x${geometry.height} dpi=${geometry.dpi} (precheck)"; else ds_class=cold; ds_stage precheck_miss; mkdir -p /tmp/opengeni-desktop; ds_stage outer_flock_wait; fw=$(date +%s%3N); flock --close --conflict-exit-code 75 --wait 45 /tmp/opengeni-desktop/up.outer.lock bash -c ${shellQuote3(lockedLauncher)} _ "$ds_started" "$fw"; frc=$?; if [ "$frc" -eq 75 ]; then ds_stage outer_flock_timeout; echo "OPENGENI_DESKTOP_LOCK_TIMEOUT"; exit 15; fi; if [ "$frc" -ne 0 ]; then exit "$frc"; fi; fi`;
|
|
17839
|
+
const paintProbe = `ds_stage paint_probe_started; p=/tmp/opengeni-desktop/paint-probe.png; prev=0; for i in $(seq 1 ${PAINT_PROBE_ATTEMPTS}); do if DISPLAY=:0 scrot -o "$p" >/dev/null 2>&1; then sz=$(wc -c < "$p" 2>/dev/null || echo 0); else sz=0; fi; rm -f "$p"; if [ "$sz" -ge ${PAINT_MIN_BYTES} ] && [ "$prev" -ge ${PAINT_MIN_BYTES} ]; then d=$((sz-prev)); [ "$d" -lt 0 ] && d=$((0-d)); if [ "$d" -le ${PAINT_SETTLE_DELTA_BYTES} ]; then ds_stage paint_ready; break; fi; fi; prev=$sz; if [ "$i" = "${PAINT_PROBE_ATTEMPTS}" ]; then ds_stage paint_failed; echo "OPENGENI_DESKTOP_NOT_PAINTING scrot below ${PAINT_MIN_BYTES}B or unsettled after warmup (last=$sz)"; exit 14; fi; sleep ${PAINT_PROBE_INTERVAL_S}; done`;
|
|
17840
|
+
const trace = `ds_started=$(date +%s%3N); ds_class=unknown; ds_stage() { ds_now=$(date +%s%3N); echo "OPENGENI_DISPLAY_STAGE stage=$1 elapsed_ms=$((ds_now-ds_started)) classification=$ds_class"; }; ds_stage script_entry`;
|
|
17841
|
+
return `${trace}; mkdir -p /tmp/opengeni-desktop; { ${bringUp} ; } && { ${paintProbe} ; }`;
|
|
17769
17842
|
}
|
|
17770
17843
|
function execResultOutput(result) {
|
|
17771
17844
|
if (typeof result === "string") {
|
|
@@ -17775,11 +17848,417 @@ function execResultOutput(result) {
|
|
|
17775
17848
|
}
|
|
17776
17849
|
function execResultExitCode(result) {
|
|
17777
17850
|
if (typeof result === "string") {
|
|
17778
|
-
|
|
17851
|
+
const banner = parseExecResponseBanner(result);
|
|
17852
|
+
if (banner.kind === "exited") return banner.exitCode;
|
|
17853
|
+
return banner.kind === "invalid" ? -1 : null;
|
|
17779
17854
|
}
|
|
17780
17855
|
return typeof result.exitCode === "number" ? result.exitCode : null;
|
|
17781
17856
|
}
|
|
17857
|
+
function execResultSessionId(result) {
|
|
17858
|
+
if (typeof result === "string") {
|
|
17859
|
+
const banner = parseExecResponseBanner(result);
|
|
17860
|
+
return banner.kind === "running" ? banner.sessionId : null;
|
|
17861
|
+
}
|
|
17862
|
+
const value = result.sessionId ?? result.session_id;
|
|
17863
|
+
return typeof value === "number" ? value : null;
|
|
17864
|
+
}
|
|
17865
|
+
function shellQuote3(value) {
|
|
17866
|
+
return `'${value.replaceAll("'", `'"'"'`)}'`;
|
|
17867
|
+
}
|
|
17868
|
+
function boundedDisplayStackCommand(command, timeoutMs, markerPath, markerToken) {
|
|
17869
|
+
const graceMs = Math.min(
|
|
17870
|
+
DISPLAY_STACK_TERMINATION_GRACE_MS,
|
|
17871
|
+
Math.max(20, Math.floor(timeoutMs / 4))
|
|
17872
|
+
);
|
|
17873
|
+
const commandTimeoutMs = Math.max(1, timeoutMs - graceMs);
|
|
17874
|
+
const killAfterMs = Math.max(1, Math.floor(graceMs / 2));
|
|
17875
|
+
const commandTimeoutSeconds = (commandTimeoutMs / 1e3).toFixed(3);
|
|
17876
|
+
const killAfterSeconds = (killAfterMs / 1e3).toFixed(3);
|
|
17877
|
+
const managedCommand = `marker=${shellQuote3(markerPath)}; token=${shellQuote3(markerToken)}; cancel="$marker.cancelled"; if [ -e "$cancel" ]; then echo "OPENGENI_DISPLAY_CANCELLED"; exit 124; fi; pid=$$; pgid=$(ps -o pgid= -p "$pid" 2>/dev/null | tr -d ' '); start=$(awk '{print $22}' "/proc/$pid/stat" 2>/dev/null || true); printf '%s %s %s %s\\n' "$pid" "$pgid" "$start" "$token" >"$marker"; if [ -e "$cancel" ]; then rm -f "$marker"; echo "OPENGENI_DISPLAY_CANCELLED"; exit 124; fi; bash -c ${shellQuote3(command)}; rc=$?; rm -f "$marker"; exit "$rc"`;
|
|
17878
|
+
return (
|
|
17879
|
+
// Do not use timeout --foreground here. The display command contains flock
|
|
17880
|
+
// supervisors and launcher children; default process-group mode is required
|
|
17881
|
+
// so a deadline cannot leave a waiter alive to acquire the lock and launch
|
|
17882
|
+
// after the caller has already failed.
|
|
17883
|
+
`timeout --signal=TERM --kill-after=${killAfterSeconds}s ${commandTimeoutSeconds}s bash -c ${shellQuote3(managedCommand)}; rc=$?; if [ "$rc" -eq 124 ]; then echo "OPENGENI_DISPLAY_TIMEOUT elapsed_ms=${commandTimeoutMs}"; fi; exit "$rc"`
|
|
17884
|
+
);
|
|
17885
|
+
}
|
|
17886
|
+
var DisplayStackWallTimeoutError = class extends Error {
|
|
17887
|
+
};
|
|
17888
|
+
var DisplayStackOperationFencedError = class extends Error {
|
|
17889
|
+
};
|
|
17890
|
+
var stableDisplayStackOperationStates = /* @__PURE__ */ new Map();
|
|
17891
|
+
var stableSessionStateOperationStates = /* @__PURE__ */ new WeakMap();
|
|
17892
|
+
var displayStackOperationStates = /* @__PURE__ */ new WeakMap();
|
|
17893
|
+
function asRecord(value) {
|
|
17894
|
+
return value !== null && typeof value === "object" ? value : null;
|
|
17895
|
+
}
|
|
17896
|
+
function nonEmptyString(value) {
|
|
17897
|
+
return typeof value === "string" && value.length > 0 ? value : null;
|
|
17898
|
+
}
|
|
17899
|
+
function readSessionState(session) {
|
|
17900
|
+
try {
|
|
17901
|
+
return asRecord(session)?.state;
|
|
17902
|
+
} catch {
|
|
17903
|
+
return void 0;
|
|
17904
|
+
}
|
|
17905
|
+
}
|
|
17906
|
+
function stableSandboxIdentity(session, options) {
|
|
17907
|
+
const explicit = nonEmptyString(options.telemetryContext?.sandboxId);
|
|
17908
|
+
if (explicit) return `sandbox:${explicit}`;
|
|
17909
|
+
const sessionRecord = asRecord(session);
|
|
17910
|
+
const state = asRecord(readSessionState(session));
|
|
17911
|
+
const providerState = state ? asRecord(state.providerState) : null;
|
|
17912
|
+
const candidates = [sessionRecord, state, providerState];
|
|
17913
|
+
const fields = [
|
|
17914
|
+
"sandboxId",
|
|
17915
|
+
"instanceId",
|
|
17916
|
+
"agentId",
|
|
17917
|
+
"hostId",
|
|
17918
|
+
"containerId",
|
|
17919
|
+
"id",
|
|
17920
|
+
"workspaceRootPath"
|
|
17921
|
+
];
|
|
17922
|
+
for (const candidate of candidates) {
|
|
17923
|
+
if (!candidate) continue;
|
|
17924
|
+
for (const field of fields) {
|
|
17925
|
+
const value = nonEmptyString(candidate[field]);
|
|
17926
|
+
if (value) return `sandbox:${value}`;
|
|
17927
|
+
}
|
|
17928
|
+
}
|
|
17929
|
+
return null;
|
|
17930
|
+
}
|
|
17931
|
+
function newOperationState() {
|
|
17932
|
+
return {
|
|
17933
|
+
epoch: 0,
|
|
17934
|
+
owner: null,
|
|
17935
|
+
waiters: [],
|
|
17936
|
+
inFlight: /* @__PURE__ */ new Set(),
|
|
17937
|
+
drain: null,
|
|
17938
|
+
pendingCleanup: /* @__PURE__ */ new Map(),
|
|
17939
|
+
physicallyQuiescent: true
|
|
17940
|
+
};
|
|
17941
|
+
}
|
|
17942
|
+
function operationState(session, options) {
|
|
17943
|
+
if ((typeof session !== "object" || session === null) && typeof session !== "function") {
|
|
17944
|
+
return null;
|
|
17945
|
+
}
|
|
17946
|
+
const stableKey = stableSandboxIdentity(session, options);
|
|
17947
|
+
if (stableKey) {
|
|
17948
|
+
let state2 = stableDisplayStackOperationStates.get(stableKey);
|
|
17949
|
+
if (!state2) {
|
|
17950
|
+
state2 = newOperationState();
|
|
17951
|
+
stableDisplayStackOperationStates.set(stableKey, state2);
|
|
17952
|
+
}
|
|
17953
|
+
return state2;
|
|
17954
|
+
}
|
|
17955
|
+
const providerState = readSessionState(session);
|
|
17956
|
+
if (providerState && (typeof providerState === "object" || typeof providerState === "function")) {
|
|
17957
|
+
const stateKey = providerState;
|
|
17958
|
+
let state2 = stableSessionStateOperationStates.get(stateKey);
|
|
17959
|
+
if (!state2) {
|
|
17960
|
+
state2 = newOperationState();
|
|
17961
|
+
stableSessionStateOperationStates.set(stateKey, state2);
|
|
17962
|
+
}
|
|
17963
|
+
return state2;
|
|
17964
|
+
}
|
|
17965
|
+
const key = session;
|
|
17966
|
+
let state = displayStackOperationStates.get(key);
|
|
17967
|
+
if (!state) {
|
|
17968
|
+
state = newOperationState();
|
|
17969
|
+
displayStackOperationStates.set(key, state);
|
|
17970
|
+
}
|
|
17971
|
+
return state;
|
|
17972
|
+
}
|
|
17973
|
+
function settleOperationWaiter(waiter, error) {
|
|
17974
|
+
if (waiter.settled) return;
|
|
17975
|
+
waiter.settled = true;
|
|
17976
|
+
if (waiter.timer) clearTimeout(waiter.timer);
|
|
17977
|
+
if (error === void 0) waiter.resolve();
|
|
17978
|
+
else waiter.reject(error);
|
|
17979
|
+
}
|
|
17980
|
+
function pumpOperationWaiters(state) {
|
|
17981
|
+
if (state.owner) return;
|
|
17982
|
+
while (state.waiters.length > 0) {
|
|
17983
|
+
const waiter = state.waiters.shift();
|
|
17984
|
+
if (waiter.settled) continue;
|
|
17985
|
+
if (waiter.epoch !== state.epoch) {
|
|
17986
|
+
settleOperationWaiter(waiter, new DisplayStackOperationFencedError());
|
|
17987
|
+
continue;
|
|
17988
|
+
}
|
|
17989
|
+
state.owner = waiter;
|
|
17990
|
+
settleOperationWaiter(waiter);
|
|
17991
|
+
return;
|
|
17992
|
+
}
|
|
17993
|
+
}
|
|
17994
|
+
function acquireOperation(state, deadline) {
|
|
17995
|
+
return new Promise((resolve5, reject) => {
|
|
17996
|
+
const waiter = {
|
|
17997
|
+
epoch: state.epoch,
|
|
17998
|
+
settled: false,
|
|
17999
|
+
resolve: () => {
|
|
18000
|
+
resolve5(() => {
|
|
18001
|
+
if (state.owner !== waiter) return;
|
|
18002
|
+
state.owner = null;
|
|
18003
|
+
pumpOperationWaiters(state);
|
|
18004
|
+
});
|
|
18005
|
+
},
|
|
18006
|
+
reject
|
|
18007
|
+
};
|
|
18008
|
+
state.waiters.push(waiter);
|
|
18009
|
+
const remainingMs = deadline - monotonicNow();
|
|
18010
|
+
if (remainingMs <= 0) {
|
|
18011
|
+
waiter.settled = true;
|
|
18012
|
+
state.waiters = state.waiters.filter((candidate) => candidate !== waiter);
|
|
18013
|
+
reject(new DisplayStackWallTimeoutError());
|
|
18014
|
+
return;
|
|
18015
|
+
}
|
|
18016
|
+
waiter.timer = setTimeout(() => {
|
|
18017
|
+
if (waiter.settled) return;
|
|
18018
|
+
waiter.settled = true;
|
|
18019
|
+
state.waiters = state.waiters.filter((candidate) => candidate !== waiter);
|
|
18020
|
+
reject(new DisplayStackWallTimeoutError());
|
|
18021
|
+
}, remainingMs);
|
|
18022
|
+
waiter.timer.unref?.();
|
|
18023
|
+
pumpOperationWaiters(state);
|
|
18024
|
+
});
|
|
18025
|
+
}
|
|
18026
|
+
function invalidateQueuedOperations(state, epoch) {
|
|
18027
|
+
const retained = [];
|
|
18028
|
+
for (const waiter of state.waiters) {
|
|
18029
|
+
if (waiter.epoch === epoch)
|
|
18030
|
+
settleOperationWaiter(waiter, new DisplayStackOperationFencedError());
|
|
18031
|
+
else retained.push(waiter);
|
|
18032
|
+
}
|
|
18033
|
+
state.waiters = retained;
|
|
18034
|
+
}
|
|
18035
|
+
function monotonicNow() {
|
|
18036
|
+
return performance.now();
|
|
18037
|
+
}
|
|
18038
|
+
async function waitForFencedOperation(state, deadline) {
|
|
18039
|
+
const drain = state.drain;
|
|
18040
|
+
if (!drain) return true;
|
|
18041
|
+
try {
|
|
18042
|
+
await beforeDeadline(drain, deadline);
|
|
18043
|
+
} catch {
|
|
18044
|
+
return false;
|
|
18045
|
+
}
|
|
18046
|
+
if (state.drain === drain) state.drain = null;
|
|
18047
|
+
return true;
|
|
18048
|
+
}
|
|
18049
|
+
function trackProviderCall(state, promise) {
|
|
18050
|
+
if (!state) return promise;
|
|
18051
|
+
state.inFlight.add(promise);
|
|
18052
|
+
void promise.then(
|
|
18053
|
+
() => state.inFlight.delete(promise),
|
|
18054
|
+
() => state.inFlight.delete(promise)
|
|
18055
|
+
);
|
|
18056
|
+
return promise;
|
|
18057
|
+
}
|
|
18058
|
+
function boundedProviderDrain(pending) {
|
|
18059
|
+
if (pending.length === 0) return Promise.resolve();
|
|
18060
|
+
return new Promise((resolve5) => {
|
|
18061
|
+
let remaining = pending.length;
|
|
18062
|
+
let timer;
|
|
18063
|
+
const finish = () => {
|
|
18064
|
+
if (timer) clearTimeout(timer);
|
|
18065
|
+
resolve5();
|
|
18066
|
+
};
|
|
18067
|
+
timer = setTimeout(finish, DISPLAY_STACK_DRAIN_GRACE_MS);
|
|
18068
|
+
timer.unref?.();
|
|
18069
|
+
for (const promise of pending) {
|
|
18070
|
+
void promise.then(
|
|
18071
|
+
() => {
|
|
18072
|
+
remaining -= 1;
|
|
18073
|
+
if (remaining === 0) finish();
|
|
18074
|
+
},
|
|
18075
|
+
() => {
|
|
18076
|
+
remaining -= 1;
|
|
18077
|
+
if (remaining === 0) finish();
|
|
18078
|
+
}
|
|
18079
|
+
);
|
|
18080
|
+
}
|
|
18081
|
+
});
|
|
18082
|
+
}
|
|
18083
|
+
function fenceOperation(state, epoch, abortController, cleanup) {
|
|
18084
|
+
abortController.abort(new DisplayStackWallTimeoutError());
|
|
18085
|
+
if (!state) return;
|
|
18086
|
+
if (state.epoch !== epoch) return;
|
|
18087
|
+
state.pendingCleanup.set(cleanup.markerToken, cleanup);
|
|
18088
|
+
state.physicallyQuiescent = false;
|
|
18089
|
+
invalidateQueuedOperations(state, epoch);
|
|
18090
|
+
state.epoch += 1;
|
|
18091
|
+
const pending = [...state.inFlight];
|
|
18092
|
+
const drain = boundedProviderDrain(pending);
|
|
18093
|
+
state.drain = state.drain ? Promise.all([state.drain, drain]).then(() => void 0) : drain;
|
|
18094
|
+
void drain.then(() => {
|
|
18095
|
+
for (const promise of pending) state.inFlight.delete(promise);
|
|
18096
|
+
});
|
|
18097
|
+
}
|
|
18098
|
+
function assertCurrentOperation(state, epoch) {
|
|
18099
|
+
if (state && state.epoch !== epoch) throw new DisplayStackOperationFencedError();
|
|
18100
|
+
}
|
|
18101
|
+
async function beforeDeadline(promise, deadline, now = monotonicNow) {
|
|
18102
|
+
const remainingMs = deadline - now();
|
|
18103
|
+
if (remainingMs <= 0) throw new DisplayStackWallTimeoutError();
|
|
18104
|
+
let timer;
|
|
18105
|
+
try {
|
|
18106
|
+
return await Promise.race([
|
|
18107
|
+
promise,
|
|
18108
|
+
new Promise((_resolve, reject) => {
|
|
18109
|
+
timer = setTimeout(() => reject(new DisplayStackWallTimeoutError()), remainingMs);
|
|
18110
|
+
timer.unref?.();
|
|
18111
|
+
})
|
|
18112
|
+
]);
|
|
18113
|
+
} finally {
|
|
18114
|
+
if (timer) clearTimeout(timer);
|
|
18115
|
+
}
|
|
18116
|
+
}
|
|
18117
|
+
function displayStackCleanupCommand(markerPath, markerToken) {
|
|
18118
|
+
const marker = shellQuote3(markerPath);
|
|
18119
|
+
const token = shellQuote3(markerToken);
|
|
18120
|
+
const script = `marker=${marker}; token=${token}; cancel="$marker.cancelled"; : >"$cancel" || { echo "OPENGENI_DISPLAY_CLEANUP status=invalid"; exit 75; }; if [ ! -r "$marker" ]; then echo "OPENGENI_DISPLAY_CLEANUP status=stopped"; exit 0; fi; read -r pid pgid start actual <"$marker" || { echo "OPENGENI_DISPLAY_CLEANUP status=invalid"; exit 75; }; [ "$actual" = "$token" ] || { echo "OPENGENI_DISPLAY_CLEANUP status=invalid"; exit 75; }; case "$pid" in ''|*[!0-9]*) echo "OPENGENI_DISPLAY_CLEANUP status=invalid"; exit 75;; esac; case "$pgid" in ''|*[!0-9]*) echo "OPENGENI_DISPLAY_CLEANUP status=invalid"; exit 75;; esac; case "$start" in ''|*[!0-9]*) echo "OPENGENI_DISPLAY_CLEANUP status=invalid"; exit 75;; esac; current=$(awk '{print $22}' "/proc/$pid/stat" 2>/dev/null || true); if [ -z "$current" ] || [ "$current" != "$start" ]; then rm -f "$marker"; echo "OPENGENI_DISPLAY_CLEANUP status=stopped"; exit 0; fi; kill -TERM -- "-$pgid" 2>/dev/null || true; for i in $(seq 1 10); do if ! kill -0 -- "-$pgid" 2>/dev/null; then rm -f "$marker"; echo "OPENGENI_DISPLAY_CLEANUP status=stopped"; exit 0; fi; sleep 0.05; done; kill -KILL -- "-$pgid" 2>/dev/null || true; for i in $(seq 1 20); do if ! kill -0 -- "-$pgid" 2>/dev/null; then rm -f "$marker"; echo "OPENGENI_DISPLAY_CLEANUP status=stopped"; exit 0; fi; sleep 0.05; done; echo "OPENGENI_DISPLAY_CLEANUP status=alive"; exit 75`;
|
|
18121
|
+
return `timeout --signal=TERM --kill-after=0.2s 1.5s bash -c ${shellQuote3(script)}`;
|
|
18122
|
+
}
|
|
18123
|
+
async function runProviderCommandToDeadline(session, command, deadline) {
|
|
18124
|
+
const controller = new AbortController();
|
|
18125
|
+
let result = await beforeDeadline(
|
|
18126
|
+
Promise.resolve(
|
|
18127
|
+
typeof session.exec === "function" ? session.exec({
|
|
18128
|
+
cmd: command,
|
|
18129
|
+
yieldTimeMs: Math.min(500, DISPLAY_STACK_CLEANUP_CALL_MS),
|
|
18130
|
+
maxOutputTokens: 4e3,
|
|
18131
|
+
signal: controller.signal
|
|
18132
|
+
}) : session.execCommand({
|
|
18133
|
+
cmd: command,
|
|
18134
|
+
yieldTimeMs: Math.min(500, DISPLAY_STACK_CLEANUP_CALL_MS),
|
|
18135
|
+
maxOutputTokens: 4e3,
|
|
18136
|
+
signal: controller.signal
|
|
18137
|
+
})
|
|
18138
|
+
),
|
|
18139
|
+
deadline
|
|
18140
|
+
);
|
|
18141
|
+
let providerSessionId = execResultSessionId(result);
|
|
18142
|
+
while (providerSessionId !== null) {
|
|
18143
|
+
if (typeof session.writeStdin !== "function") {
|
|
18144
|
+
throw new Error("display cleanup provider yielded without writeStdin");
|
|
18145
|
+
}
|
|
18146
|
+
const remainingMs = deadline - monotonicNow();
|
|
18147
|
+
if (remainingMs <= 1) throw new DisplayStackWallTimeoutError();
|
|
18148
|
+
result = await beforeDeadline(
|
|
18149
|
+
session.writeStdin({
|
|
18150
|
+
sessionId: providerSessionId,
|
|
18151
|
+
chars: "",
|
|
18152
|
+
yieldTimeMs: Math.max(1, Math.min(250, remainingMs - 1)),
|
|
18153
|
+
maxOutputTokens: 4e3,
|
|
18154
|
+
signal: controller.signal
|
|
18155
|
+
}),
|
|
18156
|
+
deadline
|
|
18157
|
+
);
|
|
18158
|
+
providerSessionId = execResultSessionId(result);
|
|
18159
|
+
}
|
|
18160
|
+
return result;
|
|
18161
|
+
}
|
|
18162
|
+
async function interruptProviderProcess(session, providerSessionId, deadline) {
|
|
18163
|
+
if (providerSessionId === null || typeof session.writeStdin !== "function") return;
|
|
18164
|
+
try {
|
|
18165
|
+
await beforeDeadline(
|
|
18166
|
+
session.writeStdin({
|
|
18167
|
+
sessionId: providerSessionId,
|
|
18168
|
+
chars: "",
|
|
18169
|
+
yieldTimeMs: 100,
|
|
18170
|
+
maxOutputTokens: 256
|
|
18171
|
+
}),
|
|
18172
|
+
deadline
|
|
18173
|
+
);
|
|
18174
|
+
} catch {
|
|
18175
|
+
}
|
|
18176
|
+
}
|
|
18177
|
+
async function cleanupDisplayStackProcess(session, pending, deadline) {
|
|
18178
|
+
try {
|
|
18179
|
+
const result = await runProviderCommandToDeadline(
|
|
18180
|
+
session,
|
|
18181
|
+
displayStackCleanupCommand(pending.markerPath, pending.markerToken),
|
|
18182
|
+
deadline
|
|
18183
|
+
);
|
|
18184
|
+
const output = execResultOutput(result);
|
|
18185
|
+
const status = /OPENGENI_DISPLAY_CLEANUP status=(stopped|absent|alive|invalid)/u.exec(
|
|
18186
|
+
output
|
|
18187
|
+
)?.[1];
|
|
18188
|
+
return status === "stopped";
|
|
18189
|
+
} catch {
|
|
18190
|
+
return false;
|
|
18191
|
+
}
|
|
18192
|
+
}
|
|
18193
|
+
async function reconcilePendingCleanup(session, state, deadline) {
|
|
18194
|
+
const entries = [...state.pendingCleanup.values()];
|
|
18195
|
+
if (entries.length === 0) {
|
|
18196
|
+
state.physicallyQuiescent = true;
|
|
18197
|
+
return true;
|
|
18198
|
+
}
|
|
18199
|
+
const results = await Promise.all(
|
|
18200
|
+
entries.map(async (pending) => ({
|
|
18201
|
+
pending,
|
|
18202
|
+
stopped: await cleanupDisplayStackProcess(session, pending, deadline)
|
|
18203
|
+
}))
|
|
18204
|
+
);
|
|
18205
|
+
for (const { pending, stopped } of results) {
|
|
18206
|
+
if (stopped) state.pendingCleanup.delete(pending.markerToken);
|
|
18207
|
+
}
|
|
18208
|
+
state.physicallyQuiescent = state.pendingCleanup.size === 0;
|
|
18209
|
+
return state.physicallyQuiescent;
|
|
18210
|
+
}
|
|
18211
|
+
function requestId() {
|
|
18212
|
+
return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`;
|
|
18213
|
+
}
|
|
18214
|
+
function telemetryReporter(options, started, now = monotonicNow) {
|
|
18215
|
+
const id = requestId();
|
|
18216
|
+
const context = options.telemetryContext;
|
|
18217
|
+
const sink = options.onTelemetry ?? (context ? (event) => console.info(`[display-stack] ${JSON.stringify(event)}`) : void 0);
|
|
18218
|
+
const emittedSandboxStages = /* @__PURE__ */ new Set();
|
|
18219
|
+
const emit = (stage, status, fields = {}) => {
|
|
18220
|
+
if (!sink) return;
|
|
18221
|
+
const event = {
|
|
18222
|
+
type: "display_stack.stage",
|
|
18223
|
+
requestId: id,
|
|
18224
|
+
stage,
|
|
18225
|
+
status,
|
|
18226
|
+
elapsedMs: fields.elapsedMs ?? now() - started,
|
|
18227
|
+
source: fields.source ?? "host",
|
|
18228
|
+
classification: fields.classification ?? "unknown",
|
|
18229
|
+
callerKind: context?.callerKind ?? "unknown",
|
|
18230
|
+
...context?.sandboxId ? { sandboxId: context.sandboxId } : {},
|
|
18231
|
+
...context?.leaseEpoch !== void 0 ? { leaseEpoch: context.leaseEpoch } : {},
|
|
18232
|
+
...fields.providerSessionId !== void 0 ? { providerSessionId: fields.providerSessionId } : {}
|
|
18233
|
+
};
|
|
18234
|
+
try {
|
|
18235
|
+
void Promise.resolve(sink(event)).catch(() => void 0);
|
|
18236
|
+
} catch {
|
|
18237
|
+
}
|
|
18238
|
+
};
|
|
18239
|
+
const emitSandboxStages = (output) => {
|
|
18240
|
+
for (const match of output.matchAll(
|
|
18241
|
+
/OPENGENI_(?:DISPLAY|DESKTOP)_STAGE stage=([a-z0-9_]+) elapsed_ms=(\d+)(?: classification=(cold|already_ready|contention|unknown))?/gu
|
|
18242
|
+
)) {
|
|
18243
|
+
const [line, stage, elapsed, classification] = match;
|
|
18244
|
+
if (!stage || !elapsed || emittedSandboxStages.has(line)) continue;
|
|
18245
|
+
emittedSandboxStages.add(line);
|
|
18246
|
+
emit(stage, "completed", {
|
|
18247
|
+
source: "sandbox",
|
|
18248
|
+
elapsedMs: Number(elapsed),
|
|
18249
|
+
classification: classification ?? "unknown"
|
|
18250
|
+
});
|
|
18251
|
+
}
|
|
18252
|
+
};
|
|
18253
|
+
return { emit, emitSandboxStages };
|
|
18254
|
+
}
|
|
17782
18255
|
function inferExitFromOutput(output) {
|
|
18256
|
+
if (/OPENGENI_DISPLAY_TIMEOUT/.test(output)) {
|
|
18257
|
+
return 124;
|
|
18258
|
+
}
|
|
18259
|
+
if (/OPENGENI_DESKTOP_LOCK_TIMEOUT/.test(output)) {
|
|
18260
|
+
return 15;
|
|
18261
|
+
}
|
|
17783
18262
|
if (/OPENGENI_DESKTOP_NOT_PAINTING/.test(output)) {
|
|
17784
18263
|
return 14;
|
|
17785
18264
|
}
|
|
@@ -17807,15 +18286,163 @@ async function ensureDisplayStack(session, options = {}) {
|
|
|
17807
18286
|
const geometry = options.geometry ?? DEFAULT_DESKTOP_GEOMETRY;
|
|
17808
18287
|
const port = options.port ?? DESKTOP_STREAM_PORT4;
|
|
17809
18288
|
const timeoutMs = options.timeoutMs ?? DISPLAY_STACK_TIMEOUT_MS;
|
|
17810
|
-
|
|
17811
|
-
|
|
17812
|
-
|
|
17813
|
-
const
|
|
17814
|
-
|
|
17815
|
-
|
|
18289
|
+
if (!Number.isFinite(timeoutMs) || timeoutMs <= 0) {
|
|
18290
|
+
throw new RangeError("ensureDisplayStack timeoutMs must be a positive finite number");
|
|
18291
|
+
}
|
|
18292
|
+
const started = monotonicNow();
|
|
18293
|
+
const deadline = started + timeoutMs;
|
|
18294
|
+
const telemetry = telemetryReporter(options, started, monotonicNow);
|
|
18295
|
+
const operationToken = requestId();
|
|
18296
|
+
const markerPath = `/tmp/opengeni-desktop/display-stack-${operationToken}.pid`;
|
|
18297
|
+
const cmd = boundedDisplayStackCommand(
|
|
18298
|
+
buildDisplayStackScript({ geometry, port }),
|
|
18299
|
+
timeoutMs,
|
|
18300
|
+
markerPath,
|
|
18301
|
+
operationToken
|
|
18302
|
+
);
|
|
18303
|
+
const outputParts = [];
|
|
18304
|
+
const state = operationState(session, options);
|
|
18305
|
+
let release;
|
|
18306
|
+
if (state) {
|
|
18307
|
+
try {
|
|
18308
|
+
release = await acquireOperation(state, deadline);
|
|
18309
|
+
} catch (error) {
|
|
18310
|
+
if (error instanceof DisplayStackWallTimeoutError || error instanceof DisplayStackOperationFencedError) {
|
|
18311
|
+
throw new DisplayStackError(
|
|
18312
|
+
124,
|
|
18313
|
+
"OPENGENI_DISPLAY_TIMEOUT display operation admission was fenced"
|
|
18314
|
+
);
|
|
18315
|
+
}
|
|
18316
|
+
throw error;
|
|
18317
|
+
}
|
|
18318
|
+
}
|
|
18319
|
+
try {
|
|
18320
|
+
if (state && !state.physicallyQuiescent) {
|
|
18321
|
+
const cleanupDeadline = monotonicNow() + DISPLAY_STACK_CLEANUP_CALL_MS;
|
|
18322
|
+
const quiesced = await reconcilePendingCleanup(s, state, cleanupDeadline);
|
|
18323
|
+
if (!quiesced) {
|
|
18324
|
+
throw new DisplayStackError(
|
|
18325
|
+
124,
|
|
18326
|
+
"OPENGENI_DISPLAY_TIMEOUT physical cleanup is still pending; retry is fenced"
|
|
18327
|
+
);
|
|
18328
|
+
}
|
|
18329
|
+
}
|
|
18330
|
+
const epoch = state?.epoch ?? 0;
|
|
18331
|
+
const abortController = new AbortController();
|
|
18332
|
+
let activeProviderSessionId = null;
|
|
18333
|
+
telemetry.emit("request_entry", "started");
|
|
18334
|
+
telemetry.emit("exec_issued", "started");
|
|
18335
|
+
try {
|
|
18336
|
+
const initialYieldMs = Math.max(1, Math.min(DISPLAY_STACK_PROVIDER_YIELD_MS, timeoutMs - 1));
|
|
18337
|
+
const initialExec = trackProviderCall(
|
|
18338
|
+
state,
|
|
18339
|
+
Promise.resolve(
|
|
18340
|
+
typeof s.exec === "function" ? s.exec({
|
|
18341
|
+
cmd,
|
|
18342
|
+
yieldTimeMs: initialYieldMs,
|
|
18343
|
+
maxOutputTokens: 2e4,
|
|
18344
|
+
signal: abortController.signal
|
|
18345
|
+
}) : s.execCommand({
|
|
18346
|
+
cmd,
|
|
18347
|
+
yieldTimeMs: initialYieldMs,
|
|
18348
|
+
maxOutputTokens: 2e4,
|
|
18349
|
+
signal: abortController.signal
|
|
18350
|
+
})
|
|
18351
|
+
)
|
|
18352
|
+
);
|
|
18353
|
+
let result = await beforeDeadline(
|
|
18354
|
+
initialExec,
|
|
18355
|
+
deadline,
|
|
18356
|
+
monotonicNow
|
|
18357
|
+
);
|
|
18358
|
+
assertCurrentOperation(state, epoch);
|
|
18359
|
+
let chunk = execResultOutput(result);
|
|
18360
|
+
outputParts.push(chunk);
|
|
18361
|
+
telemetry.emitSandboxStages(chunk);
|
|
18362
|
+
telemetry.emit("provider_first_result", "completed");
|
|
18363
|
+
let providerSessionId = execResultSessionId(result);
|
|
18364
|
+
activeProviderSessionId = providerSessionId;
|
|
18365
|
+
while (providerSessionId !== null) {
|
|
18366
|
+
telemetry.emit("provider_yield", "waiting", { providerSessionId });
|
|
18367
|
+
if (typeof s.writeStdin !== "function") {
|
|
18368
|
+
throw new DisplayStackError(
|
|
18369
|
+
-1,
|
|
18370
|
+
`${outputParts.join("\n")}
|
|
18371
|
+
provider yielded process ${providerSessionId} but exposes no writeStdin poll surface`
|
|
18372
|
+
);
|
|
18373
|
+
}
|
|
18374
|
+
assertCurrentOperation(state, epoch);
|
|
18375
|
+
const remainingMs = deadline - monotonicNow();
|
|
18376
|
+
if (remainingMs <= 1) throw new DisplayStackWallTimeoutError();
|
|
18377
|
+
const pollYieldMs = Math.max(1, Math.min(DISPLAY_STACK_PROVIDER_POLL_MS, remainingMs - 1));
|
|
18378
|
+
result = await beforeDeadline(
|
|
18379
|
+
trackProviderCall(
|
|
18380
|
+
state,
|
|
18381
|
+
s.writeStdin({
|
|
18382
|
+
sessionId: providerSessionId,
|
|
18383
|
+
chars: "",
|
|
18384
|
+
yieldTimeMs: pollYieldMs,
|
|
18385
|
+
maxOutputTokens: 2e4,
|
|
18386
|
+
signal: abortController.signal
|
|
18387
|
+
})
|
|
18388
|
+
),
|
|
18389
|
+
deadline,
|
|
18390
|
+
monotonicNow
|
|
18391
|
+
);
|
|
18392
|
+
assertCurrentOperation(state, epoch);
|
|
18393
|
+
chunk = execResultOutput(result);
|
|
18394
|
+
outputParts.push(chunk);
|
|
18395
|
+
telemetry.emitSandboxStages(chunk);
|
|
18396
|
+
providerSessionId = execResultSessionId(result);
|
|
18397
|
+
activeProviderSessionId = providerSessionId;
|
|
18398
|
+
}
|
|
18399
|
+
const output = outputParts.join("\n");
|
|
18400
|
+
const exitCode = execResultExitCode(result) ?? inferExitFromOutput(output);
|
|
18401
|
+
telemetry.emit("process_complete", exitCode === 0 ? "completed" : "failed");
|
|
18402
|
+
if (exitCode !== 0) {
|
|
18403
|
+
throw new DisplayStackError(exitCode, output);
|
|
18404
|
+
}
|
|
18405
|
+
const marker = (output.match(/OPENGENI_DESKTOP_UP[^\n]*/) ?? [""])[0];
|
|
18406
|
+
telemetry.emit("readiness_complete", "completed");
|
|
18407
|
+
return { port, geometry, marker };
|
|
18408
|
+
} catch (error) {
|
|
18409
|
+
if (error instanceof DisplayStackWallTimeoutError || error instanceof DisplayStackOperationFencedError) {
|
|
18410
|
+
const pendingCleanup = {
|
|
18411
|
+
markerPath,
|
|
18412
|
+
markerToken: operationToken
|
|
18413
|
+
};
|
|
18414
|
+
fenceOperation(state, epoch, abortController, pendingCleanup);
|
|
18415
|
+
const cleanupDeadline = monotonicNow() + DISPLAY_STACK_CLEANUP_CALL_MS;
|
|
18416
|
+
telemetry.emit("process_cleanup", "started");
|
|
18417
|
+
await interruptProviderProcess(
|
|
18418
|
+
s,
|
|
18419
|
+
activeProviderSessionId,
|
|
18420
|
+
Math.min(cleanupDeadline, monotonicNow() + 50)
|
|
18421
|
+
);
|
|
18422
|
+
let quiesced = false;
|
|
18423
|
+
if (state) {
|
|
18424
|
+
quiesced = await reconcilePendingCleanup(s, state, cleanupDeadline);
|
|
18425
|
+
if (!quiesced) {
|
|
18426
|
+
await waitForFencedOperation(
|
|
18427
|
+
state,
|
|
18428
|
+
Math.min(cleanupDeadline, monotonicNow() + DISPLAY_STACK_DRAIN_GRACE_MS)
|
|
18429
|
+
);
|
|
18430
|
+
}
|
|
18431
|
+
} else {
|
|
18432
|
+
quiesced = await cleanupDisplayStackProcess(s, pendingCleanup, cleanupDeadline);
|
|
18433
|
+
}
|
|
18434
|
+
telemetry.emit("process_cleanup", quiesced ? "completed" : "failed");
|
|
18435
|
+
const output = `${outputParts.join("\n")}
|
|
18436
|
+
OPENGENI_DISPLAY_TIMEOUT wall_ms=${timeoutMs}`;
|
|
18437
|
+
telemetry.emit("wall_deadline", "failed");
|
|
18438
|
+
throw new DisplayStackError(124, output);
|
|
18439
|
+
}
|
|
18440
|
+
telemetry.emit("request_failed", "failed");
|
|
18441
|
+
throw error;
|
|
18442
|
+
}
|
|
18443
|
+
} finally {
|
|
18444
|
+
release?.();
|
|
17816
18445
|
}
|
|
17817
|
-
const marker = (output.match(/OPENGENI_DESKTOP_UP[^\n]*/) ?? [""])[0];
|
|
17818
|
-
return { port, geometry, marker };
|
|
17819
18446
|
}
|
|
17820
18447
|
async function tearDownDisplayStack(session) {
|
|
17821
18448
|
const s = session;
|
|
@@ -18235,7 +18862,7 @@ function runCredentialRoot(sessionId) {
|
|
|
18235
18862
|
function runCredentialPointerFile(sessionId) {
|
|
18236
18863
|
return `${runCredentialRoot(sessionId)}/current`;
|
|
18237
18864
|
}
|
|
18238
|
-
function
|
|
18865
|
+
function shellQuote4(value) {
|
|
18239
18866
|
return `'${value.replace(/'/g, "'\\''")}'`;
|
|
18240
18867
|
}
|
|
18241
18868
|
function base64(value) {
|
|
@@ -18253,8 +18880,8 @@ function portableBase64DecodeCommand() {
|
|
|
18253
18880
|
].join("\n");
|
|
18254
18881
|
}
|
|
18255
18882
|
function pointerLockAcquireCommands(root) {
|
|
18256
|
-
const lockFile =
|
|
18257
|
-
const lockDirectory =
|
|
18883
|
+
const lockFile = shellQuote4(`${root}/.pointer.lock`);
|
|
18884
|
+
const lockDirectory = shellQuote4(`${root}/.pointer.lockdir`);
|
|
18258
18885
|
return [
|
|
18259
18886
|
`if command -v flock >/dev/null 2>&1; then`,
|
|
18260
18887
|
` exec 9>${lockFile}`,
|
|
@@ -18298,7 +18925,7 @@ async function runCredentialCommand(session, cmd, commandRunner) {
|
|
|
18298
18925
|
${cmd}
|
|
18299
18926
|
printf '\\n${COMMAND_OK_MARKER}\\n'`;
|
|
18300
18927
|
const args = {
|
|
18301
|
-
cmd: `env -u BASH_ENV bash --noprofile --norc -c ${
|
|
18928
|
+
cmd: `env -u BASH_ENV bash --noprofile --norc -c ${shellQuote4(marked)}`,
|
|
18302
18929
|
yieldTimeMs: 12e4,
|
|
18303
18930
|
maxOutputTokens: 4e3
|
|
18304
18931
|
};
|
|
@@ -18308,19 +18935,19 @@ printf '\\n${COMMAND_OK_MARKER}\\n'`;
|
|
|
18308
18935
|
}
|
|
18309
18936
|
}
|
|
18310
18937
|
async function writeCredentialFile(session, path, content, commandRunner) {
|
|
18311
|
-
await runCredentialCommand(session, `: > ${
|
|
18938
|
+
await runCredentialCommand(session, `: > ${shellQuote4(path)}`, commandRunner);
|
|
18312
18939
|
const encoded = base64(content);
|
|
18313
18940
|
for (let offset = 0; offset < encoded.length; offset += WRITE_CHUNK_BYTES) {
|
|
18314
18941
|
const chunk = encoded.slice(offset, offset + WRITE_CHUNK_BYTES);
|
|
18315
18942
|
await runCredentialCommand(
|
|
18316
18943
|
session,
|
|
18317
|
-
`printf %s ${
|
|
18944
|
+
`printf %s ${shellQuote4(chunk)} | ${portableBase64DecodeCommand()} >> ${shellQuote4(path)}`,
|
|
18318
18945
|
commandRunner
|
|
18319
18946
|
);
|
|
18320
18947
|
}
|
|
18321
18948
|
await runCredentialCommand(
|
|
18322
18949
|
session,
|
|
18323
|
-
`[ "$(wc -c < ${
|
|
18950
|
+
`[ "$(wc -c < ${shellQuote4(path)} | tr -d '[:space:]')" = ${shellQuote4(String(byteLength(content)))} ]`,
|
|
18324
18951
|
commandRunner
|
|
18325
18952
|
);
|
|
18326
18953
|
}
|
|
@@ -18345,19 +18972,19 @@ async function materializeRunCredentials(session, material, options) {
|
|
|
18345
18972
|
session,
|
|
18346
18973
|
[
|
|
18347
18974
|
"umask 077",
|
|
18348
|
-
`mkdir -p -- ${
|
|
18349
|
-
`chmod 0700 -- ${
|
|
18350
|
-
`rm -rf -- ${
|
|
18351
|
-
`mkdir -p -- ${
|
|
18975
|
+
`mkdir -p -- ${shellQuote4(`${root}/versions`)}`,
|
|
18976
|
+
`chmod 0700 -- ${shellQuote4(root)} ${shellQuote4(`${root}/versions`)}`,
|
|
18977
|
+
`rm -rf -- ${shellQuote4(stage)}`,
|
|
18978
|
+
`mkdir -p -- ${shellQuote4(`${stage}/files`)}`
|
|
18352
18979
|
].join("\n"),
|
|
18353
18980
|
options.commandRunner
|
|
18354
18981
|
);
|
|
18355
18982
|
const environmentLines = [
|
|
18356
18983
|
...Object.entries(material.environment).map(
|
|
18357
|
-
([name, value]) => `export ${name}=${
|
|
18984
|
+
([name, value]) => `export ${name}=${shellQuote4(value)}`
|
|
18358
18985
|
),
|
|
18359
18986
|
...Object.entries(material.fileEnvironment).map(
|
|
18360
|
-
([name, path]) => `export ${name}=${
|
|
18987
|
+
([name, path]) => `export ${name}=${shellQuote4(`${version}/files/${path}`)}`
|
|
18361
18988
|
)
|
|
18362
18989
|
];
|
|
18363
18990
|
await writeCredentialFile(
|
|
@@ -18369,17 +18996,17 @@ async function materializeRunCredentials(session, material, options) {
|
|
|
18369
18996
|
);
|
|
18370
18997
|
await runCredentialCommand(
|
|
18371
18998
|
session,
|
|
18372
|
-
`chmod 0600 -- ${
|
|
18999
|
+
`chmod 0600 -- ${shellQuote4(`${stage}/env`)}`,
|
|
18373
19000
|
options.commandRunner
|
|
18374
19001
|
);
|
|
18375
19002
|
for (const file of material.files) {
|
|
18376
19003
|
const target = `${stage}/files/${file.path}`;
|
|
18377
19004
|
const parent = target.slice(0, target.lastIndexOf("/"));
|
|
18378
|
-
await runCredentialCommand(session, `mkdir -p -- ${
|
|
19005
|
+
await runCredentialCommand(session, `mkdir -p -- ${shellQuote4(parent)}`, options.commandRunner);
|
|
18379
19006
|
await writeCredentialFile(session, target, file.content, options.commandRunner);
|
|
18380
19007
|
await runCredentialCommand(
|
|
18381
19008
|
session,
|
|
18382
|
-
`chmod ${
|
|
19009
|
+
`chmod ${shellQuote4(file.mode ?? "0600")} -- ${shellQuote4(target)}`,
|
|
18383
19010
|
options.commandRunner
|
|
18384
19011
|
);
|
|
18385
19012
|
}
|
|
@@ -18387,19 +19014,19 @@ async function materializeRunCredentials(session, material, options) {
|
|
|
18387
19014
|
session,
|
|
18388
19015
|
[
|
|
18389
19016
|
...pointerLockAcquireCommands(root),
|
|
18390
|
-
`previous=$(cat -- ${
|
|
18391
|
-
`mv -- ${
|
|
18392
|
-
`printf '%s\\n' ${
|
|
18393
|
-
`mv -f -- ${
|
|
19017
|
+
`previous=$(cat -- ${shellQuote4(`${root}/current`)} 2>/dev/null || :)`,
|
|
19018
|
+
`mv -- ${shellQuote4(stage)} ${shellQuote4(version)}`,
|
|
19019
|
+
`printf '%s\\n' ${shellQuote4(versionName)} > ${shellQuote4(`${root}/.next`)}`,
|
|
19020
|
+
`mv -f -- ${shellQuote4(`${root}/.next`)} ${shellQuote4(`${root}/current`)}`,
|
|
18394
19021
|
...options.prunePreviousGenerations ? [
|
|
18395
|
-
`find ${
|
|
19022
|
+
`find ${shellQuote4(`${root}/versions`)} -mindepth 1 -maxdepth 1 ! -name ${shellQuote4(versionName)} -exec rm -rf -- {} +`
|
|
18396
19023
|
] : options.pruneOtherAttempts ? [
|
|
18397
|
-
`find ${
|
|
19024
|
+
`find ${shellQuote4(`${root}/versions`)} -mindepth 1 -maxdepth 1 ! -name ${shellQuote4(`${attemptId}-${options.executionGeneration}-*`)} -exec rm -rf -- {} +`
|
|
18398
19025
|
] : options.pruneSupersededGenerations ? [
|
|
18399
|
-
`for candidate in ${
|
|
19026
|
+
`for candidate in ${shellQuote4(`${root}/versions`)}/${prefix}*; do`,
|
|
18400
19027
|
` [ -e "$candidate" ] || continue`,
|
|
18401
19028
|
` name=\${candidate##*/}`,
|
|
18402
|
-
` [ "$name" = ${
|
|
19029
|
+
` [ "$name" = ${shellQuote4(versionName)} ] || [ "$name" = "$previous" ] || rm -rf -- "$candidate"`,
|
|
18403
19030
|
"done"
|
|
18404
19031
|
] : [],
|
|
18405
19032
|
...pointerLockReleaseCommands()
|
|
@@ -18412,10 +19039,10 @@ async function clearRunCredentials(session, sessionId, commandRunner) {
|
|
|
18412
19039
|
await runCredentialCommand(
|
|
18413
19040
|
session,
|
|
18414
19041
|
[
|
|
18415
|
-
`if [ -d ${
|
|
19042
|
+
`if [ -d ${shellQuote4(root)} ]; then`,
|
|
18416
19043
|
...pointerLockAcquireCommands(root).map((line) => ` ${line}`),
|
|
18417
|
-
` rm -f -- ${
|
|
18418
|
-
` rm -rf -- ${
|
|
19044
|
+
` rm -f -- ${shellQuote4(`${root}/current`)} ${shellQuote4(`${root}/.next`)}`,
|
|
19045
|
+
` rm -rf -- ${shellQuote4(`${root}/versions`)}`,
|
|
18419
19046
|
...pointerLockReleaseCommands().map((line) => ` ${line}`),
|
|
18420
19047
|
"fi"
|
|
18421
19048
|
].join("\n"),
|
|
@@ -18432,15 +19059,15 @@ async function clearRunCredentialsForAttempt(session, options, commandRunner) {
|
|
|
18432
19059
|
await runCredentialCommand(
|
|
18433
19060
|
session,
|
|
18434
19061
|
[
|
|
18435
|
-
`if [ -d ${
|
|
19062
|
+
`if [ -d ${shellQuote4(root)} ]; then`,
|
|
18436
19063
|
...pointerLockAcquireCommands(root).map((line) => ` ${line}`),
|
|
18437
|
-
` if [ -r ${
|
|
18438
|
-
` current=$(cat -- ${
|
|
18439
|
-
` case "$current" in ${
|
|
19064
|
+
` if [ -r ${shellQuote4(`${root}/current`)} ]; then`,
|
|
19065
|
+
` current=$(cat -- ${shellQuote4(`${root}/current`)} 2>/dev/null || :)`,
|
|
19066
|
+
` case "$current" in ${shellQuote4(prefix)}*) rm -f -- ${shellQuote4(`${root}/current`)} ;; esac`,
|
|
18440
19067
|
" fi",
|
|
18441
|
-
` if [ -d ${
|
|
18442
|
-
` find ${
|
|
18443
|
-
` find ${
|
|
19068
|
+
` if [ -d ${shellQuote4(`${root}/versions`)} ]; then`,
|
|
19069
|
+
` find ${shellQuote4(`${root}/versions`)} -mindepth 1 -maxdepth 1 -name ${shellQuote4(`${prefix}*`)} -exec rm -rf -- {} +`,
|
|
19070
|
+
` find ${shellQuote4(`${root}/versions`)} -mindepth 1 -maxdepth 1 -name ${shellQuote4(`.stage-${prefix}*`)} -exec rm -rf -- {} +`,
|
|
18444
19071
|
" fi",
|
|
18445
19072
|
...pointerLockReleaseCommands().map((line) => ` ${line}`),
|
|
18446
19073
|
"fi"
|
|
@@ -18452,12 +19079,12 @@ function withRunCredentialEnvironment(cmd, sessionId) {
|
|
|
18452
19079
|
const root = runCredentialRoot(sessionId);
|
|
18453
19080
|
const pointerFile = runCredentialPointerFile(sessionId);
|
|
18454
19081
|
return [
|
|
18455
|
-
`if [ -r ${
|
|
18456
|
-
` _opengeni_credential_version=$(cat -- ${
|
|
19082
|
+
`if [ -r ${shellQuote4(pointerFile)} ]; then`,
|
|
19083
|
+
` _opengeni_credential_version=$(cat -- ${shellQuote4(pointerFile)} 2>/dev/null || :)`,
|
|
18457
19084
|
' case "$_opengeni_credential_version" in',
|
|
18458
19085
|
" ''|*[!A-Za-z0-9_-]*) ;;",
|
|
18459
19086
|
" *)",
|
|
18460
|
-
` _opengeni_credential_env=${
|
|
19087
|
+
` _opengeni_credential_env=${shellQuote4(`${root}/versions`)}/"$_opengeni_credential_version/env"`,
|
|
18461
19088
|
' if [ -r "$_opengeni_credential_env" ]; then',
|
|
18462
19089
|
" set -a",
|
|
18463
19090
|
' . "$_opengeni_credential_env"',
|
|
@@ -18534,7 +19161,7 @@ var ToolspaceTokenPathError = class extends Error {
|
|
|
18534
19161
|
this.name = "ToolspaceTokenPathError";
|
|
18535
19162
|
}
|
|
18536
19163
|
};
|
|
18537
|
-
function
|
|
19164
|
+
function shellQuote5(value) {
|
|
18538
19165
|
return `'${value.replace(/'/g, "'\\''")}'`;
|
|
18539
19166
|
}
|
|
18540
19167
|
function assertToolspaceSessionId(sessionId) {
|
|
@@ -18559,7 +19186,7 @@ function withToolspaceTokenEnvironment(cmd, tokenFile) {
|
|
|
18559
19186
|
if (!tokenFile || tokenFile.includes("\0") || !posixPath.isAbsolute(tokenFile)) {
|
|
18560
19187
|
throw new ToolspaceTokenPathError("Toolspace token file must be absolute");
|
|
18561
19188
|
}
|
|
18562
|
-
return [`export OPENGENI_TOOLSPACE_TOKEN_FILE=${
|
|
19189
|
+
return [`export OPENGENI_TOOLSPACE_TOKEN_FILE=${shellQuote5(tokenFile)}`, cmd].join("\n");
|
|
18563
19190
|
}
|
|
18564
19191
|
function withToolspaceTokenSession(session, tokenFile) {
|
|
18565
19192
|
return new Proxy(session, {
|
|
@@ -18953,11 +19580,11 @@ var SandboxChannelAService = class {
|
|
|
18953
19580
|
const depthArg = Math.max(1, req.depth);
|
|
18954
19581
|
const maxCommandEntries = req.maxEntries + 1;
|
|
18955
19582
|
const gnuPrint = `-printf '%y\\t%s\\t%T@\\t%m\\t%p\\0'`;
|
|
18956
|
-
const gnuSelector = pruneNames.length ? `\\( -type d \\( ${pruneNames.map((name) => `-name ${
|
|
19583
|
+
const gnuSelector = pruneNames.length ? `\\( -type d \\( ${pruneNames.map((name) => `-name ${shellQuote6(name)}`).join(" -o ")} \\) ${gnuPrint} -prune \\) -o ${gnuPrint}` : gnuPrint;
|
|
18957
19584
|
const gnuVisibleSelector = req.includeHidden ? gnuSelector : `\\( -path '*/.*' -prune \\) -o \\( ${gnuSelector} \\)`;
|
|
18958
19585
|
const gnuFind = `find ${findRoot} -mindepth 1 -maxdepth ${depthArg} \\( ${gnuVisibleSelector} \\) 2>/dev/null`;
|
|
18959
19586
|
const portableHiddenGuard = req.includeHidden ? "" : `if [[ "$base" == .* ]]; then continue; fi;`;
|
|
18960
|
-
const portablePruneCase = pruneNames.length ? `case "$base" in ${pruneNames.map(
|
|
19587
|
+
const portablePruneCase = pruneNames.length ? `case "$base" in ${pruneNames.map(shellQuote6).join("|")}) pruned=1 ;; *) pruned=0 ;; esac;` : "pruned=0;";
|
|
18961
19588
|
const portableWalk = [
|
|
18962
19589
|
"shopt -s nullglob dotglob; count=0; stop=0;",
|
|
18963
19590
|
'walk() { local dir="$1" level="$2" p base t size mtime mode pruned;',
|
|
@@ -18986,12 +19613,12 @@ var SandboxChannelAService = class {
|
|
|
18986
19613
|
`if [ "$records" -ge ${req.maxEntries} ] || [ $((bytes + record_bytes + 1)) -gt ${FS_LIST_MAX_OUTPUT_BYTES} ]; then truncated=1; break; fi;`,
|
|
18987
19614
|
`printf '%s\\0' "$record"; bytes=$((bytes + record_bytes + 1)); records=$((records + 1));`,
|
|
18988
19615
|
"done;",
|
|
18989
|
-
`if [ "$truncated" -eq 1 ]; then printf '%s\\0' ${
|
|
19616
|
+
`if [ "$truncated" -eq 1 ]; then printf '%s\\0' ${shellQuote6(FS_LIST_TRUNCATED_MARKER)}; fi`
|
|
18990
19617
|
].join(" ");
|
|
18991
19618
|
const findCommand = [
|
|
18992
19619
|
`{ ${rawFindCommand}; } | { ${boundedOutput}; };`,
|
|
18993
19620
|
"producer_status=${PIPESTATUS[0]};",
|
|
18994
|
-
`if [ "$producer_status" -ne 0 ] && [ "$producer_status" -ne 141 ]; then printf '%s\\0' ${
|
|
19621
|
+
`if [ "$producer_status" -ne 0 ] && [ "$producer_status" -ne 141 ]; then printf '%s\\0' ${shellQuote6(FS_LIST_TRUNCATED_MARKER)}; fi`
|
|
18995
19622
|
].join(" ");
|
|
18996
19623
|
const { stdout, exitCode } = await this.runInConfinedDirectory(root, {
|
|
18997
19624
|
cmd: internalBashCommand(findCommand),
|
|
@@ -19084,11 +19711,11 @@ var SandboxChannelAService = class {
|
|
|
19084
19711
|
async assertConfinedExistingDirectory(path) {
|
|
19085
19712
|
const root = this.workspaceRoot || ".";
|
|
19086
19713
|
const abs = this.joinRoot(path);
|
|
19087
|
-
const rejectLink = path ? `test ! -L ${
|
|
19714
|
+
const rejectLink = path ? `test ! -L ${shellQuote6(abs)} || { printf '__OPENGENI_FS_SYMLINK__'; exit 68; }` : ":";
|
|
19088
19715
|
const script = [
|
|
19089
|
-
`root=$(realpath -e -- ${
|
|
19716
|
+
`root=$(realpath -e -- ${shellQuote6(root)}) || { printf '__OPENGENI_FS_NOT_FOUND__'; exit 66; }`,
|
|
19090
19717
|
rejectLink,
|
|
19091
|
-
`target=$(realpath -e -- ${
|
|
19718
|
+
`target=$(realpath -e -- ${shellQuote6(abs)}) || { printf '__OPENGENI_FS_NOT_FOUND__'; exit 66; }`,
|
|
19092
19719
|
`case "$target" in "$root"|"$root"/*) ;; *) printf '__OPENGENI_FS_ESCAPE__'; exit 67 ;; esac`,
|
|
19093
19720
|
`test -d "$target" || { printf '__OPENGENI_FS_NOT_FOUND__'; exit 66; }`,
|
|
19094
19721
|
`printf '__OPENGENI_FS_CONFINED_OK__'`
|
|
@@ -19099,13 +19726,13 @@ var SandboxChannelAService = class {
|
|
|
19099
19726
|
async assertConfinedMutationParent(path, options) {
|
|
19100
19727
|
const root = this.workspaceRoot || ".";
|
|
19101
19728
|
const abs = this.joinRoot(path);
|
|
19102
|
-
const rejectLink = options.rejectFinalSymlink ? `test ! -L ${
|
|
19729
|
+
const rejectLink = options.rejectFinalSymlink ? `test ! -L ${shellQuote6(abs)} || { printf '__OPENGENI_FS_SYMLINK__'; exit 68; }` : ":";
|
|
19103
19730
|
const locateParent = options.allowMissingParents ? 'probe="$parent"; while [ ! -e "$probe" ] && [ ! -L "$probe" ]; do next=$(dirname -- "$probe"); [ "$next" != "$probe" ] || break; probe="$next"; done' : 'probe="$parent"';
|
|
19104
19731
|
const requireParent = options.allowMissingParents ? ":" : 'test -d "$parent" || { printf "__OPENGENI_FS_NOT_FOUND__"; exit 66; }';
|
|
19105
19732
|
const script = [
|
|
19106
|
-
`root=$(realpath -e -- ${
|
|
19733
|
+
`root=$(realpath -e -- ${shellQuote6(root)}) || { printf '__OPENGENI_FS_NOT_FOUND__'; exit 66; }`,
|
|
19107
19734
|
rejectLink,
|
|
19108
|
-
`parent=$(dirname -- ${
|
|
19735
|
+
`parent=$(dirname -- ${shellQuote6(abs)})`,
|
|
19109
19736
|
locateParent,
|
|
19110
19737
|
`target=$(realpath -e -- "$probe") || { printf '__OPENGENI_FS_NOT_FOUND__'; exit 66; }`,
|
|
19111
19738
|
`case "$target" in "$root"|"$root"/*) ;; *) printf '__OPENGENI_FS_ESCAPE__'; exit 67 ;; esac`,
|
|
@@ -19142,8 +19769,8 @@ var SandboxChannelAService = class {
|
|
|
19142
19769
|
const root = this.workspaceRoot || ".";
|
|
19143
19770
|
const abs = this.joinRoot(path);
|
|
19144
19771
|
const script = [
|
|
19145
|
-
`root=$(realpath -e -- ${
|
|
19146
|
-
`exec 3<${
|
|
19772
|
+
`root=$(realpath -e -- ${shellQuote6(root)}) || { printf '__OPENGENI_FS_NOT_FOUND__'; exit 66; }`,
|
|
19773
|
+
`exec 3<${shellQuote6(abs)} || { printf '__OPENGENI_FS_NOT_FOUND__'; exit 66; }`,
|
|
19147
19774
|
`target=$(readlink -f -- "/proc/$$/fd/3") || { printf '__OPENGENI_FS_NOT_FOUND__'; exit 66; }`,
|
|
19148
19775
|
`case "$target" in "$root"|"$root"/*) ;; *) printf '__OPENGENI_FS_ESCAPE__'; exit 67 ;; esac`,
|
|
19149
19776
|
`test -f "$target" || { printf '__OPENGENI_FS_NOT_FOUND__'; exit 66; }`,
|
|
@@ -19212,7 +19839,7 @@ var SandboxChannelAService = class {
|
|
|
19212
19839
|
});
|
|
19213
19840
|
if (!req.overwrite) {
|
|
19214
19841
|
const { exitCode: exitCode2 } = await this.run({
|
|
19215
|
-
cmd: `test -e ${
|
|
19842
|
+
cmd: `test -e ${shellQuote6(abs)} || test -L ${shellQuote6(abs)}`
|
|
19216
19843
|
});
|
|
19217
19844
|
if (exitCode2 === 0) {
|
|
19218
19845
|
throw new ChannelAConflictError(`path exists and overwrite is false: ${path}`);
|
|
@@ -19220,7 +19847,7 @@ var SandboxChannelAService = class {
|
|
|
19220
19847
|
}
|
|
19221
19848
|
if (req.createParents) {
|
|
19222
19849
|
const dir = dirnameAbs(abs);
|
|
19223
|
-
if (dir) await this.run({ cmd: `mkdir -p ${
|
|
19850
|
+
if (dir) await this.run({ cmd: `mkdir -p ${shellQuote6(dir)}` });
|
|
19224
19851
|
await this.assertConfinedMutationParent(path, {
|
|
19225
19852
|
allowMissingParents: false,
|
|
19226
19853
|
rejectFinalSymlink: true
|
|
@@ -19228,7 +19855,7 @@ var SandboxChannelAService = class {
|
|
|
19228
19855
|
}
|
|
19229
19856
|
const b64 = bytes.toString("base64");
|
|
19230
19857
|
const { exitCode, stderr } = await this.run({
|
|
19231
|
-
cmd: `printf %s ${
|
|
19858
|
+
cmd: `printf %s ${shellQuote6(b64)} | base64 -d > ${shellQuote6(abs)}`
|
|
19232
19859
|
});
|
|
19233
19860
|
if (exitCode !== null && exitCode !== 0) {
|
|
19234
19861
|
if (req.encoding !== "base64" && this.session.createEditor) {
|
|
@@ -19269,7 +19896,7 @@ var SandboxChannelAService = class {
|
|
|
19269
19896
|
rejectFinalSymlink: false
|
|
19270
19897
|
});
|
|
19271
19898
|
const flag = req.recursive ? "-rf" : "-f";
|
|
19272
|
-
const { exitCode, stderr } = await this.run({ cmd: `rm ${flag} ${
|
|
19899
|
+
const { exitCode, stderr } = await this.run({ cmd: `rm ${flag} ${shellQuote6(abs)}` });
|
|
19273
19900
|
if (exitCode !== null && exitCode !== 0) {
|
|
19274
19901
|
throw new ChannelAValidationError(
|
|
19275
19902
|
`failed to delete ${path}: ${stderr || `exit ${exitCode}`}`
|
|
@@ -19294,7 +19921,7 @@ var SandboxChannelAService = class {
|
|
|
19294
19921
|
});
|
|
19295
19922
|
if (!req.overwrite) {
|
|
19296
19923
|
const { exitCode: exitCode2 } = await this.run({
|
|
19297
|
-
cmd: `test -e ${
|
|
19924
|
+
cmd: `test -e ${shellQuote6(newAbs)} || test -L ${shellQuote6(newAbs)}`
|
|
19298
19925
|
});
|
|
19299
19926
|
if (exitCode2 === 0) {
|
|
19300
19927
|
throw new ChannelAConflictError(`destination exists and overwrite is false: ${newPath}`);
|
|
@@ -19302,7 +19929,7 @@ var SandboxChannelAService = class {
|
|
|
19302
19929
|
}
|
|
19303
19930
|
if (req.createParents) {
|
|
19304
19931
|
const dir = dirnameAbs(newAbs);
|
|
19305
|
-
if (dir) await this.run({ cmd: `mkdir -p ${
|
|
19932
|
+
if (dir) await this.run({ cmd: `mkdir -p ${shellQuote6(dir)}` });
|
|
19306
19933
|
await this.assertConfinedMutationParent(newPath, {
|
|
19307
19934
|
allowMissingParents: false,
|
|
19308
19935
|
rejectFinalSymlink: true
|
|
@@ -19310,7 +19937,7 @@ var SandboxChannelAService = class {
|
|
|
19310
19937
|
}
|
|
19311
19938
|
const flag = req.overwrite ? "-f " : "";
|
|
19312
19939
|
const { exitCode, stderr } = await this.run({
|
|
19313
|
-
cmd: `mv ${flag}${
|
|
19940
|
+
cmd: `mv ${flag}${shellQuote6(abs)} ${shellQuote6(newAbs)}`
|
|
19314
19941
|
});
|
|
19315
19942
|
if (exitCode !== null && exitCode !== 0) {
|
|
19316
19943
|
throw new ChannelAValidationError(
|
|
@@ -19335,7 +19962,7 @@ var SandboxChannelAService = class {
|
|
|
19335
19962
|
rejectFinalSymlink: true
|
|
19336
19963
|
});
|
|
19337
19964
|
const flag = req.recursive ? "-p " : "";
|
|
19338
|
-
const { exitCode, stderr } = await this.run({ cmd: `mkdir ${flag}${
|
|
19965
|
+
const { exitCode, stderr } = await this.run({ cmd: `mkdir ${flag}${shellQuote6(abs)}` });
|
|
19339
19966
|
if (exitCode !== null && exitCode !== 0) {
|
|
19340
19967
|
throw new ChannelAValidationError(`failed to mkdir ${path}: ${stderr || `exit ${exitCode}`}`);
|
|
19341
19968
|
}
|
|
@@ -19371,10 +19998,10 @@ var SandboxChannelAService = class {
|
|
|
19371
19998
|
const repo = assertSafeRelPathOrRoot(req.path);
|
|
19372
19999
|
const ctx = req.contextLines;
|
|
19373
20000
|
let range = "";
|
|
19374
|
-
if (req.fromRef && req.toRef) range = `${
|
|
19375
|
-
else if (req.fromRef) range = `${
|
|
20001
|
+
if (req.fromRef && req.toRef) range = `${shellQuote6(req.fromRef)} ${shellQuote6(req.toRef)}`;
|
|
20002
|
+
else if (req.fromRef) range = `${shellQuote6(req.fromRef)}`;
|
|
19376
20003
|
else if (req.staged) range = "--cached";
|
|
19377
|
-
const pathspec = req.pathspec.length ? ` -- ${req.pathspec.map(
|
|
20004
|
+
const pathspec = req.pathspec.length ? ` -- ${req.pathspec.map(shellQuote6).join(" ")}` : "";
|
|
19378
20005
|
const numstat = await this.runInConfinedDirectory(repo, {
|
|
19379
20006
|
cmd: `git -c core.quotePath=false diff --no-color -z --numstat ${range}${pathspec}`.trim()
|
|
19380
20007
|
});
|
|
@@ -19398,7 +20025,7 @@ var SandboxChannelAService = class {
|
|
|
19398
20025
|
continue;
|
|
19399
20026
|
}
|
|
19400
20027
|
const patch = await this.runInConfinedDirectory(repo, {
|
|
19401
|
-
cmd: `git -c core.quotePath=false diff --no-color -U${ctx} ${range} -- ${
|
|
20028
|
+
cmd: `git -c core.quotePath=false diff --no-color -U${ctx} ${range} -- ${shellQuote6(target)}`.trim()
|
|
19402
20029
|
});
|
|
19403
20030
|
const oversized = Buffer.byteLength(patch.stdout, "utf8") > req.maxBytesPerFile;
|
|
19404
20031
|
const parsed = oversized ? { hunks: [], status: "modified" } : parseUnifiedPatch(patch.stdout);
|
|
@@ -19422,7 +20049,7 @@ var SandboxChannelAService = class {
|
|
|
19422
20049
|
const fileArg = target.startsWith("./") ? target : `./${target}`;
|
|
19423
20050
|
const read = await this.runInConfinedDirectory(repo, {
|
|
19424
20051
|
cmd: [
|
|
19425
|
-
`file=${
|
|
20052
|
+
`file=${shellQuote6(fileArg)}`,
|
|
19426
20053
|
'if [ -L "$file" ]; then printf "L\\n"; readlink -z -- "$file" | base64 | tr -d "\\n"; exit; fi',
|
|
19427
20054
|
'size=$(wc -c < "$file") || exit 66',
|
|
19428
20055
|
'line_count=$(wc -l < "$file") || exit 66',
|
|
@@ -19486,9 +20113,9 @@ var SandboxChannelAService = class {
|
|
|
19486
20113
|
async gitLog(req) {
|
|
19487
20114
|
const repo = assertSafeRelPathOrRoot(req.path);
|
|
19488
20115
|
const fmt = `%H${US}%h${US}%P${US}%an${US}%ae${US}%at${US}%cn${US}%ce${US}%ct${US}%s${US}%b${RS}`;
|
|
19489
|
-
const pathspec = req.pathspec.length ? ` -- ${req.pathspec.map(
|
|
20116
|
+
const pathspec = req.pathspec.length ? ` -- ${req.pathspec.map(shellQuote6).join(" ")}` : "";
|
|
19490
20117
|
const { stdout, exitCode } = await this.runInConfinedDirectory(repo, {
|
|
19491
|
-
cmd: `git log --format=${
|
|
20118
|
+
cmd: `git log --format=${shellQuote6(fmt)} -n${req.maxCount + 1} --skip=${req.skip} ${shellQuote6(req.ref)}${pathspec}`
|
|
19492
20119
|
});
|
|
19493
20120
|
if (exitCode !== null && exitCode !== 0) {
|
|
19494
20121
|
return { commits: [], hasMore: false };
|
|
@@ -19515,7 +20142,7 @@ var SandboxChannelAService = class {
|
|
|
19515
20142
|
const repo = assertSafeRelPathOrRoot(req.path);
|
|
19516
20143
|
if (req.filePath) {
|
|
19517
20144
|
const { stdout, exitCode } = await this.runInConfinedDirectory(repo, {
|
|
19518
|
-
cmd: `git cat-file blob ${
|
|
20145
|
+
cmd: `git cat-file blob ${shellQuote6(`${req.ref}:${req.filePath}`)} 2>/dev/null | base64`
|
|
19519
20146
|
});
|
|
19520
20147
|
if (exitCode !== null && exitCode !== 0 && stdout.trim() === "") {
|
|
19521
20148
|
throw new ChannelANotFoundError(`blob not found: ${req.ref}:${req.filePath}`);
|
|
@@ -19756,11 +20383,11 @@ var SandboxChannelAService = class {
|
|
|
19756
20383
|
const safe = assertSafeRelPathOrRoot(rel);
|
|
19757
20384
|
const root = this.workspaceRoot || ".";
|
|
19758
20385
|
const abs = this.joinRoot(safe);
|
|
19759
|
-
const rejectLink = safe ? `test ! -L ${
|
|
20386
|
+
const rejectLink = safe ? `test ! -L ${shellQuote6(abs)} || { printf '__OPENGENI_FS_SYMLINK__'; exit 68; }` : ":";
|
|
19760
20387
|
const script = [
|
|
19761
|
-
`root=$(realpath -e -- ${
|
|
20388
|
+
`root=$(realpath -e -- ${shellQuote6(root)}) || { printf '__OPENGENI_FS_NOT_FOUND__'; exit 66; }`,
|
|
19762
20389
|
rejectLink,
|
|
19763
|
-
`target=$(realpath -e -- ${
|
|
20390
|
+
`target=$(realpath -e -- ${shellQuote6(abs)}) || { printf '__OPENGENI_FS_NOT_FOUND__'; exit 66; }`,
|
|
19764
20391
|
`case "$target" in "$root"|"$root"/*) ;; *) printf '__OPENGENI_FS_ESCAPE__'; exit 67 ;; esac`,
|
|
19765
20392
|
`test -d "$target" || { printf '__OPENGENI_FS_NOT_FOUND__'; exit 66; }`,
|
|
19766
20393
|
`cd -P -- "$target"`,
|
|
@@ -19876,22 +20503,6 @@ function isExecSessionLostBanner(out, execSessionId) {
|
|
|
19876
20503
|
if (!match || match[0] !== out) return false;
|
|
19877
20504
|
return match[1] === String(execSessionId);
|
|
19878
20505
|
}
|
|
19879
|
-
function parseExecBannerSessionId(raw) {
|
|
19880
|
-
const outputIdx = raw.indexOf("\nOutput:\n");
|
|
19881
|
-
const banner = outputIdx >= 0 ? raw.slice(0, outputIdx) : raw.startsWith("Output:\n") ? "" : raw;
|
|
19882
|
-
const match = banner.match(/Process running with session ID (\d+)/);
|
|
19883
|
-
if (!match) return null;
|
|
19884
|
-
const n = Number.parseInt(match[1], 10);
|
|
19885
|
-
return Number.isFinite(n) ? n : null;
|
|
19886
|
-
}
|
|
19887
|
-
function parseExecBannerExitCode(raw) {
|
|
19888
|
-
const outputIdx = raw.indexOf("\nOutput:\n");
|
|
19889
|
-
const banner = outputIdx >= 0 ? raw.slice(0, outputIdx) : raw.startsWith("Output:\n") ? "" : raw;
|
|
19890
|
-
const match = banner.match(/Process exited with code (-?\d+)/);
|
|
19891
|
-
if (!match) return null;
|
|
19892
|
-
const exitCode = Number.parseInt(match[1], 10);
|
|
19893
|
-
return Number.isSafeInteger(exitCode) ? exitCode : null;
|
|
19894
|
-
}
|
|
19895
20506
|
function sniffBinary(bytes) {
|
|
19896
20507
|
const n = Math.min(bytes.byteLength, 8192);
|
|
19897
20508
|
for (let i = 0; i < n; i++) if (bytes[i] === 0) return true;
|
|
@@ -19925,11 +20536,11 @@ function assertSafeRelPath(p) {
|
|
|
19925
20536
|
if (norm === "") throw new ChannelAValidationError("path is required");
|
|
19926
20537
|
return norm;
|
|
19927
20538
|
}
|
|
19928
|
-
function
|
|
20539
|
+
function shellQuote6(s) {
|
|
19929
20540
|
return `'${s.replace(/'/g, `'\\''`)}'`;
|
|
19930
20541
|
}
|
|
19931
20542
|
function internalBashCommand(script) {
|
|
19932
|
-
return `env -u BASH_ENV bash --noprofile --norc -c ${
|
|
20543
|
+
return `env -u BASH_ENV bash --noprofile --norc -c ${shellQuote6(script)}`;
|
|
19933
20544
|
}
|
|
19934
20545
|
function assertSafePruneDirectoryName(name) {
|
|
19935
20546
|
if (name.length > 255 || !/^[A-Za-z0-9._@+-]+$/.test(name) || name === "." || name === "..") {
|
|
@@ -20463,12 +21074,12 @@ function defaultEcho(req, hostname) {
|
|
|
20463
21074
|
durationMs: "1"
|
|
20464
21075
|
};
|
|
20465
21076
|
}
|
|
20466
|
-
function ok(
|
|
20467
|
-
return { requestId, error: void 0, result };
|
|
21077
|
+
function ok(requestId2, result) {
|
|
21078
|
+
return { requestId: requestId2, error: void 0, result };
|
|
20468
21079
|
}
|
|
20469
|
-
function errorResponse(
|
|
21080
|
+
function errorResponse(requestId2, code, message, retryable) {
|
|
20470
21081
|
const error = { code, message, retryable, detail: {} };
|
|
20471
|
-
return { requestId, error, result: void 0 };
|
|
21082
|
+
return { requestId: requestId2, error, result: void 0 };
|
|
20472
21083
|
}
|
|
20473
21084
|
function normalize(path) {
|
|
20474
21085
|
const trimmed = path.replace(/\/+$/, "");
|
|
@@ -21437,6 +22048,8 @@ export {
|
|
|
21437
22048
|
mintStreamToken,
|
|
21438
22049
|
verifyStreamToken,
|
|
21439
22050
|
StreamTokenPayload2 as StreamTokenPayload,
|
|
22051
|
+
parseExecBannerSessionId,
|
|
22052
|
+
parseExecBannerExitCode,
|
|
21440
22053
|
STREAM_PORT,
|
|
21441
22054
|
DISPLAY_STACK_TIMEOUT_MS,
|
|
21442
22055
|
DEFAULT_DESKTOP_GEOMETRY,
|
|
@@ -21490,8 +22103,6 @@ export {
|
|
|
21490
22103
|
SandboxChannelAService,
|
|
21491
22104
|
stripExecBanner2 as stripExecBanner,
|
|
21492
22105
|
isExecSessionLostBanner,
|
|
21493
|
-
parseExecBannerSessionId,
|
|
21494
|
-
parseExecBannerExitCode,
|
|
21495
22106
|
assertSafeRelPath,
|
|
21496
22107
|
parsePorcelainV2,
|
|
21497
22108
|
parseNumstatZ,
|
|
@@ -21519,4 +22130,4 @@ export {
|
|
|
21519
22130
|
collectSandboxEnvironment2 as collectSandboxEnvironment,
|
|
21520
22131
|
parseExposedPorts2 as parseExposedPorts
|
|
21521
22132
|
};
|
|
21522
|
-
//# sourceMappingURL=chunk-
|
|
22133
|
+
//# sourceMappingURL=chunk-65UDUFUO.js.map
|