@sema-agent/server 7.140.0 → 7.142.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/MIGRATION.md +505 -0
- package/README.md +1 -1
- package/README.zh-CN.md +1 -1
- package/USAGE.md +142 -25
- package/dist/approval-ask-audit-store.d.ts +54 -38
- package/dist/approval-ask-audit-store.js +58 -39
- package/dist/approval-batch-census.d.ts +27 -0
- package/dist/approval-batch-census.js +27 -0
- package/dist/approval-card.d.ts +39 -24
- package/dist/approval-card.js +6 -3
- package/dist/approval-crash-convergence.d.ts +17 -7
- package/dist/approval-crash-convergence.js +94 -22
- package/dist/approval-deny-reasons.d.ts +34 -30
- package/dist/approval-deny-reasons.js +21 -6
- package/dist/approval-policy-settlement.d.ts +1 -1
- package/dist/approval-reconciler.d.ts +33 -1
- package/dist/approval-reconciler.js +43 -40
- package/dist/approval.d.ts +15 -0
- package/dist/approval.js +10 -0
- package/dist/audit.d.ts +4 -2
- package/dist/audit.js +2 -2
- package/dist/auth-bridge.d.ts +9 -6
- package/dist/auth-bridge.js +9 -3
- package/dist/boot/adoption.js +2 -1
- package/dist/boot/budget-tracing.d.ts +16 -0
- package/dist/boot/budget-tracing.js +10 -6
- package/dist/boot/device-lane.d.ts +11 -0
- package/dist/boot/device-lane.js +16 -0
- package/dist/boot/engine-lease.d.ts +24 -3
- package/dist/boot/engine-lease.js +9 -2
- package/dist/boot/execution-env.d.ts +24 -4
- package/dist/boot/execution-env.js +34 -27
- package/dist/boot/execution-lane.js +2 -1
- package/dist/boot/exit-boundary.js +5 -4
- package/dist/boot/memory-boundary.d.ts +2 -2
- package/dist/boot/memory-consolidation.d.ts +1 -1
- package/dist/boot/memory-consolidation.js +3 -2
- package/dist/boot/parked-revive-gate.js +2 -1
- package/dist/boot/permission-rules-audit.d.ts +0 -28
- package/dist/boot/permission-rules-audit.js +2 -1
- package/dist/boot/read-deny-anchor-audit.js +2 -1
- package/dist/boot/reapers.d.ts +2 -2
- package/dist/boot/reapers.js +4 -3
- package/dist/boot/resolve-spec.js +18 -6
- package/dist/boot/retention-lane.js +5 -3
- package/dist/boot/runner-deps.js +16 -13
- package/dist/boot/runtime-caps.js +3 -2
- package/dist/boot/session-faces.d.ts +1 -1
- package/dist/boot/session-faces.js +8 -6
- package/dist/boot/shutdown.js +8 -2
- package/dist/boot/stage-04-stores.js +3 -2
- package/dist/boot/stage-05-execution-env.d.ts +1 -1
- package/dist/boot/stage-05-execution-env.js +11 -7
- package/dist/boot/stage-06-runners.d.ts +1 -3
- package/dist/boot/stage-06-runners.js +3 -2
- package/dist/boot/stage-07-capability-layer.d.ts +1 -2
- package/dist/boot/stage-07-capability-layer.js +10 -11
- package/dist/boot/stage-08-reapers.d.ts +1 -2
- package/dist/boot/stage-08-reapers.js +3 -2
- package/dist/boot/stage-09-leader.d.ts +1 -2
- package/dist/boot/stage-10-http-server.d.ts +1 -2
- package/dist/boot/stage-10-http-server.js +6 -4
- package/dist/boot/stores.js +13 -6
- package/dist/boot/web-admin.js +18 -17
- package/dist/boot/webfetch-summarize-lane.js +2 -1
- package/dist/boot/workflow-orchestration.js +3 -2
- package/dist/capabilities/builtin-tools.d.ts +2 -1
- package/dist/capabilities/builtin-tools.js +2 -4
- package/dist/capabilities/composite-members.d.ts +2 -1
- package/dist/capabilities/composite-members.js +2 -1
- package/dist/capabilities/scenarios.d.ts +1 -1
- package/dist/capability-facts.d.ts +33 -0
- package/dist/capability-facts.js +32 -0
- package/dist/card-void.d.ts +38 -0
- package/dist/card-void.js +23 -0
- package/dist/config-catalog.js +12 -8
- package/dist/config-provider.d.ts +13 -13
- package/dist/config-provider.js +19 -33
- package/dist/config-types.d.ts +39 -10
- package/dist/config.d.ts +6 -0
- package/dist/config.js +72 -49
- package/dist/delegated-session-owner.d.ts +21 -19
- package/dist/delegated-session-owner.js +1 -1
- package/dist/device-store.d.ts +7 -0
- package/dist/device-store.js +3 -0
- package/dist/execution-config-root.d.ts +22 -0
- package/dist/execution-config-root.js +35 -0
- package/dist/execution-lane-caps.d.ts +50 -2
- package/dist/execution-lane-caps.js +90 -13
- package/dist/execution-placement.d.ts +3 -0
- package/dist/execution-placement.js +7 -0
- package/dist/fleet/drain-entry.d.ts +3 -0
- package/dist/fleet/fleet-bus.d.ts +6 -3
- package/dist/hooks/hook-runner.d.ts +6 -0
- package/dist/hooks/hook-runner.js +30 -1
- package/dist/hosted-posture.d.ts +8 -0
- package/dist/hosted-posture.js +4 -0
- package/dist/http/admission.js +3 -3
- package/dist/http/caller-trust.d.ts +3 -2
- package/dist/http/caller-trust.js +2 -1
- package/dist/http/dispatch.js +8 -2
- package/dist/http/execution-context.d.ts +2 -2
- package/dist/http/idempotency.d.ts +1 -1
- package/dist/http/live-frame.d.ts +24 -0
- package/dist/http/live-frame.js +16 -0
- package/dist/http/principal-gate.d.ts +3 -1
- package/dist/http/principal-gate.js +17 -8
- package/dist/http/resume-legs.js +42 -29
- package/dist/http/route-table.js +4 -0
- package/dist/http/routes/approvals-assistant.js +1 -0
- package/dist/http/routes/attachments.js +18 -0
- package/dist/http/routes/capabilities.js +10 -6
- package/dist/http/routes/diagnostics.d.ts +0 -19
- package/dist/http/routes/diagnostics.js +16 -2
- package/dist/http/routes/memory-bundle.d.ts +2 -2
- package/dist/http/routes/memory-bundle.js +1 -1
- package/dist/http/routes/memory-compliance.js +1 -1
- package/dist/http/routes/memory-policy.js +27 -7
- package/dist/http/routes/run-permission-mode.d.ts +75 -0
- package/dist/http/routes/run-permission-mode.js +170 -0
- package/dist/http/routes/runs.js +3 -3
- package/dist/http/routes/session-memory-sync.d.ts +63 -0
- package/dist/http/routes/session-memory-sync.js +157 -0
- package/dist/http/routes/session-sync.d.ts +3 -6
- package/dist/http/routes/session-sync.js +7 -2
- package/dist/http/routes/sessions-list.d.ts +2 -1
- package/dist/http/routes/sessions.js +5 -4
- package/dist/http/routes/tasks.js +70 -71
- package/dist/http/run-read-face.d.ts +7 -0
- package/dist/http/run-read-face.js +10 -4
- package/dist/http/send.d.ts +6 -1
- package/dist/http/send.js +10 -3
- package/dist/http/server.d.ts +20 -7
- package/dist/http/server.js +2 -4
- package/dist/http/sse-log.d.ts +3 -8
- package/dist/http/stored-body-replay.d.ts +3 -3
- package/dist/http/stored-body-replay.js +3 -1
- package/dist/http/submit-replay.d.ts +4 -3
- package/dist/http/submit-replay.js +2 -2
- package/dist/http/sync-submit-body.d.ts +21 -4
- package/dist/http/sync-submit-body.js +13 -0
- package/dist/identity-source.d.ts +21 -2
- package/dist/identity-source.js +6 -0
- package/dist/instruction-source-notice.d.ts +7 -0
- package/dist/instruction-source-notice.js +6 -6
- package/dist/mcp-assertion-header.d.ts +4 -0
- package/dist/mcp-assertion-header.js +5 -0
- package/dist/memory-incident-sink.d.ts +19 -6
- package/dist/memory-incident-sink.js +8 -4
- package/dist/memory-posture.d.ts +1 -1
- package/dist/memory-scope.d.ts +4 -4
- package/dist/memory-scope.js +1 -1
- package/dist/memory-sync-client.d.ts +35 -23
- package/dist/memory-sync-client.js +122 -52
- package/dist/memory-sync-identity.d.ts +24 -0
- package/dist/memory-sync-identity.js +57 -0
- package/dist/memory-sync.d.ts +7 -1
- package/dist/memory-sync.js +6 -3
- package/dist/observability/fail-open.d.ts +57 -12
- package/dist/observability/fail-open.js +50 -9
- package/dist/observability/metrics.js +0 -1
- package/dist/observability/run-terminal.d.ts +93 -31
- package/dist/observability/run-terminal.js +175 -171
- package/dist/observability/secret-env-scrub.d.ts +20 -1
- package/dist/observability/secret-env-scrub.js +14 -0
- package/dist/orchestration/workflow-completion-inbox.d.ts +5 -2
- package/dist/orchestration/workflow-completion-inbox.js +13 -5
- package/dist/orchestration/workflow-notify-journal.d.ts +5 -3
- package/dist/orchestration/workflow-notify-journal.js +2 -1
- package/dist/permission-mode-admission.d.ts +68 -0
- package/dist/permission-mode-admission.js +23 -0
- package/dist/permission-rule-list.d.ts +55 -6
- package/dist/permission-rule-list.js +52 -5
- package/dist/plugins/approval-ask-store-file.d.ts +14 -4
- package/dist/plugins/approval-ask-store-file.js +48 -7
- package/dist/plugins/approval-ask-store-memory.d.ts +2 -1
- package/dist/plugins/approval-ask-store-memory.js +9 -8
- package/dist/plugins/approval-ask-store-sql.d.ts +42 -10
- package/dist/plugins/approval-ask-store-sql.js +49 -19
- package/dist/plugins/caching-session-store.d.ts +6 -0
- package/dist/plugins/caching-session-store.js +3 -0
- package/dist/plugins/checkpoint-store-sql.d.ts +65 -17
- package/dist/plugins/checkpoint-store-sql.js +74 -3
- package/dist/plugins/device-store-sql.d.ts +5 -2
- package/dist/plugins/device-store-sql.js +4 -1
- package/dist/plugins/execution-placement-sql.d.ts +11 -1
- package/dist/plugins/execution-placement-sql.js +12 -1
- package/dist/plugins/file-run-store.d.ts +7 -2
- package/dist/plugins/file-run-store.js +12 -2
- package/dist/plugins/fork-routing-session-store.d.ts +10 -32
- package/dist/plugins/fork-routing-session-store.js +2 -61
- package/dist/plugins/k8s-exec-protocol.d.ts +4 -1
- package/dist/plugins/k8s-exec-protocol.js +6 -1
- package/dist/plugins/local-checkpoint-store.d.ts +10 -1
- package/dist/plugins/local-checkpoint-store.js +22 -0
- package/dist/plugins/local-session-store.d.ts +260 -39
- package/dist/plugins/local-session-store.js +582 -57
- package/dist/plugins/local-session-window.d.ts +17 -3
- package/dist/plugins/local-session-window.js +7 -13
- package/dist/plugins/local-task-attachment-store.d.ts +12 -2
- package/dist/plugins/local-task-attachment-store.js +67 -43
- package/dist/plugins/memory-key-guards.d.ts +3 -3
- package/dist/plugins/memory-run-store.d.ts +7 -2
- package/dist/plugins/memory-run-store.js +12 -2
- package/dist/plugins/one-time-migrations.d.ts +68 -2
- package/dist/plugins/one-time-migrations.js +120 -4
- package/dist/plugins/pg-pool.js +3 -2
- package/dist/plugins/pg-session-storage.d.ts +2 -0
- package/dist/plugins/pg-session-storage.js +3 -2
- package/dist/plugins/remote-env-e2b.d.ts +47 -7
- package/dist/plugins/remote-env-e2b.js +242 -46
- package/dist/plugins/remote-env-k8s.d.ts +51 -6
- package/dist/plugins/remote-env-k8s.js +234 -44
- package/dist/plugins/remote-env-local-docker.d.ts +40 -14
- package/dist/plugins/remote-env-local-docker.js +127 -33
- package/dist/plugins/run-store-sql.d.ts +11 -3
- package/dist/plugins/run-store-sql.js +13 -4
- package/dist/plugins/sandbox-read-floor.d.ts +273 -0
- package/dist/plugins/sandbox-read-floor.js +608 -0
- package/dist/plugins/session-ownership.d.ts +2 -1
- package/dist/plugins/session-placement.d.ts +17 -4
- package/dist/plugins/session-placement.js +2 -1
- package/dist/plugins/session-store.d.ts +18 -20
- package/dist/plugins/session-store.js +4 -12
- package/dist/plugins/store-backend.d.ts +41 -7
- package/dist/plugins/store-backend.js +22 -4
- package/dist/plugins/store-contracts.d.ts +35 -3
- package/dist/plugins/store-contracts.js +3 -0
- package/dist/plugins/task-attachment-store.d.ts +112 -14
- package/dist/plugins/task-attachment-store.js +221 -28
- package/dist/plugins/tidb-pool.js +2 -1
- package/dist/plugins/tidb-session-store.d.ts +4 -1
- package/dist/plugins/tidb-session-store.js +3 -2
- package/dist/plugins/workspace-fill.d.ts +60 -0
- package/dist/plugins/workspace-fill.js +106 -0
- package/dist/project-bundle.d.ts +38 -0
- package/dist/project-bundle.js +609 -0
- package/dist/run-cancel-context.d.ts +34 -0
- package/dist/run-cancel-context.js +7 -0
- package/dist/run-local.js +2 -8
- package/dist/runs.d.ts +39 -12
- package/dist/runs.js +69 -55
- package/dist/sandbox-egress-policy.d.ts +4 -1
- package/dist/sandbox-egress-policy.js +3 -1
- package/dist/sandbox-git-plane.d.ts +31 -12
- package/dist/sandbox-git-plane.js +24 -4
- package/dist/security.d.ts +11 -2
- package/dist/security.js +8 -0
- package/dist/server-secret-env.d.ts +2 -1
- package/dist/server-secret-env.js +2 -4
- package/dist/session-error-wire.d.ts +46 -4
- package/dist/session-error-wire.js +23 -0
- package/dist/session-incarnation.d.ts +9 -3
- package/dist/session-sync-kernel.d.ts +42 -1
- package/dist/session-sync-kernel.js +17 -0
- package/dist/session-sync.js +16 -4
- package/dist/sql-engine-coordinates.d.ts +49 -0
- package/dist/sql-engine-coordinates.js +27 -0
- package/dist/standing-answer-grant.d.ts +31 -2
- package/dist/standing-answer-grant.js +14 -2
- package/dist/store-posture.d.ts +1 -1
- package/dist/task-a2a.d.ts +1 -1
- package/dist/task-mcp.d.ts +1 -1
- package/dist/task-settings.d.ts +62 -27
- package/dist/task-settings.js +105 -29
- package/dist/terminal.d.ts +97 -20
- package/dist/terminal.js +107 -18
- package/dist/tool-approval.d.ts +4 -3
- package/dist/tool-approval.js +12 -14
- package/dist/trace/core-keyset-guard.d.ts +196 -2
- package/dist/trace/engine-notice-wire.d.ts +8 -2
- package/dist/trace/engine-notice-wire.js +4 -2
- package/dist/trace/exit-seat.d.ts +9 -1
- package/dist/trace/exit-seat.js +1 -0
- package/dist/trace/ledger-sink.d.ts +8 -4
- package/dist/trace/ledger-sink.js +2 -2
- package/dist/trace/model-segment.d.ts +2 -2
- package/dist/trace/project.d.ts +18 -1
- package/dist/trace/project.js +16 -0
- package/dist/trace/redact.js +323 -90
- package/dist/trace/self-minted-wire.d.ts +419 -4
- package/dist/trace/self-minted-wire.js +145 -0
- package/dist/trace/wire-projection-faces.d.ts +1 -1
- package/dist/trace/wire-projection-faces.js +1 -1
- package/dist/verifier-pause-card.d.ts +8 -0
- package/dist/verifier-pause-card.js +19 -0
- package/dist/web-admin-client/apply-effective.d.ts +5 -7
- package/dist/web-admin-client/apply-effective.js +22 -2
- package/dist/web-admin-client/candidate-gate.d.ts +1 -1
- package/dist/web-admin-client/effective-keys.js +2 -0
- package/dist/web-admin-client/read-warnings.d.ts +2 -2
- package/dist/web-admin-client/types.d.ts +4 -0
- package/docs/DEVICE-EXECUTOR-PROTOCOL.md +3 -2
- package/package.json +2 -2
- package/wire-changes/server@7.141.0.json +367 -0
- package/wire-changes/server@7.142.0.json +639 -0
|
@@ -19,6 +19,7 @@ import { buildCapturedOutput, createCutError } from "../exec-cut.js";
|
|
|
19
19
|
import { SANDBOX_IPV6_DEFAULT, sandboxEgressGaps, sandboxPodSysctls } from "../sandbox-egress-policy.js";
|
|
20
20
|
import { FileError, ExecutionError, RemoteExecutionError, withRetry, RollingTailBuffer, markTruncated, } from "@sema-agent/core";
|
|
21
21
|
import { capabilitiesFor } from "../execution-lane-caps.js";
|
|
22
|
+
import { ADAPTER_START, BOOTSTRAP_START, buildFloorPodSeat, createSandboxReadFloor, FLOOR_K8S_CONFIGMAP_KEY, engineStart, floorArgv, } from "./sandbox-read-floor.js";
|
|
22
23
|
import { fileErrorFromExec, classifyFsStderr } from "./remote-env-file-error.js";
|
|
23
24
|
import { createPosixShellFs } from "./posix-shell-fs.js";
|
|
24
25
|
import { randomUUID } from "node:crypto";
|
|
@@ -47,6 +48,9 @@ export { applyPodSpecPatch } from "./k8s-exec-protocol.js";
|
|
|
47
48
|
import { sandboxRunEndHoldMs } from "./k8s-pod-reaper.js";
|
|
48
49
|
import { BROKER_COMMAND, BROKER_CONTAINER_NAME, BROKER_LEASE_DIR, BROKER_LEASE_KEY, BROKER_PROBE_ARG, BROKER_VOLUME_NAME } from "../broker/constants.js";
|
|
49
50
|
import { buildSandboxLeaseFile, placeholderLeaseEntries, sandboxGitRouteEnv } from "../sandbox-git-lease.js";
|
|
51
|
+
function minted(c) {
|
|
52
|
+
return c.ok ? { ...c, origin: "minted" } : c;
|
|
53
|
+
}
|
|
50
54
|
const CACHE_VOLUME = ADAPTER_VOLUME_NAMES.cache;
|
|
51
55
|
function occupantKeyOf(occupant) {
|
|
52
56
|
if (occupant === undefined)
|
|
@@ -202,6 +206,7 @@ function brokerContainer(image) {
|
|
|
202
206
|
}
|
|
203
207
|
export class RemoteK8sExecutionEnv {
|
|
204
208
|
capabilities;
|
|
209
|
+
floor;
|
|
205
210
|
cwd;
|
|
206
211
|
podName;
|
|
207
212
|
podIP;
|
|
@@ -211,6 +216,7 @@ export class RemoteK8sExecutionEnv {
|
|
|
211
216
|
inFlight = new Set();
|
|
212
217
|
idleWaiters = [];
|
|
213
218
|
connectInFlight;
|
|
219
|
+
resumeInFlight;
|
|
214
220
|
connectRefusal;
|
|
215
221
|
get homeDir() {
|
|
216
222
|
return this.homeSeat.home;
|
|
@@ -229,6 +235,7 @@ export class RemoteK8sExecutionEnv {
|
|
|
229
235
|
cacheUnavailableSaid = false;
|
|
230
236
|
logger;
|
|
231
237
|
onWorkspaceSnapshot;
|
|
238
|
+
workspaceFill;
|
|
232
239
|
cacheMisses;
|
|
233
240
|
constructor(config, deps) {
|
|
234
241
|
this.cfg = {
|
|
@@ -268,8 +275,10 @@ export class RemoteK8sExecutionEnv {
|
|
|
268
275
|
this.cacheLane = cacheLaneOf(config.cache, config.spaceOccupant);
|
|
269
276
|
this.logger = config.logger;
|
|
270
277
|
this.onWorkspaceSnapshot = config.onWorkspaceSnapshot;
|
|
278
|
+
this.workspaceFill = config.workspaceFill;
|
|
271
279
|
this.cacheMisses = config.cacheMisses;
|
|
272
|
-
this.
|
|
280
|
+
this.floor = this.deps.readFloor ?? createSandboxReadFloor({ lane: PROVIDER, knob: config.sandboxReadFloor, ...(config.logger !== undefined ? { logger: config.logger } : {}) });
|
|
281
|
+
this.capabilities = capabilitiesFor({ remoteExec: { provider: PROVIDER, s3Snapshot: this.cfg.s3Snapshot }, sandboxReadFloor: config.sandboxReadFloor });
|
|
273
282
|
const bgMax = Math.max(1, Math.min(K8S_BG_MAX_TIMEOUT_SEC, Math.floor(this.cfg.timeoutMs / 1000)));
|
|
274
283
|
this.backgroundCapabilities = {
|
|
275
284
|
supported: true,
|
|
@@ -371,10 +380,14 @@ export class RemoteK8sExecutionEnv {
|
|
|
371
380
|
});
|
|
372
381
|
}
|
|
373
382
|
connect(config) {
|
|
374
|
-
this.connectInFlight
|
|
375
|
-
this.connectInFlight
|
|
383
|
+
if (this.connectInFlight)
|
|
384
|
+
return this.connectInFlight;
|
|
385
|
+
const walk = this.establish(config).finally(() => {
|
|
386
|
+
if (this.connectInFlight === walk)
|
|
387
|
+
this.connectInFlight = undefined;
|
|
376
388
|
});
|
|
377
|
-
|
|
389
|
+
this.connectInFlight = walk;
|
|
390
|
+
return walk;
|
|
378
391
|
}
|
|
379
392
|
async establish(config) {
|
|
380
393
|
if (this.connectRefusal)
|
|
@@ -383,10 +396,10 @@ export class RemoteK8sExecutionEnv {
|
|
|
383
396
|
const lane = this.spaceLane;
|
|
384
397
|
switch (lane.state) {
|
|
385
398
|
case "off":
|
|
386
|
-
return this.provision(signal);
|
|
399
|
+
return this.settle(minted(await this.provision(signal)));
|
|
387
400
|
case "unavailable":
|
|
388
401
|
this.sayUnavailable(lane.reason);
|
|
389
|
-
return this.provision(signal);
|
|
402
|
+
return this.settle(minted(await this.provision(signal)));
|
|
390
403
|
case "unreadable":
|
|
391
404
|
return this.provision(signal);
|
|
392
405
|
case "open": {
|
|
@@ -396,7 +409,7 @@ export class RemoteK8sExecutionEnv {
|
|
|
396
409
|
const latest = await this.readLatest(lane.space, signal);
|
|
397
410
|
if (!latest.ok)
|
|
398
411
|
return latest;
|
|
399
|
-
const c = latest.value === undefined ? await this.provision(signal) : await this.restoreSnapshot({ lane: "space", space: lane.space }, latest.value, signal);
|
|
412
|
+
const c = await this.settle(latest.value === undefined ? minted(await this.provision(signal)) : await this.restoreSnapshot({ lane: "space", space: lane.space }, latest.value, signal));
|
|
400
413
|
if (c.ok)
|
|
401
414
|
this.armSpace();
|
|
402
415
|
return c;
|
|
@@ -407,6 +420,43 @@ export class RemoteK8sExecutionEnv {
|
|
|
407
420
|
}
|
|
408
421
|
}
|
|
409
422
|
}
|
|
423
|
+
async settle(c) {
|
|
424
|
+
if (!c.ok)
|
|
425
|
+
return c;
|
|
426
|
+
const fill = this.workspaceFill;
|
|
427
|
+
if (fill !== undefined) {
|
|
428
|
+
const filled = await fill.fill(this.fillChannel(), this.cwd, c.origin);
|
|
429
|
+
if (!filled.ok) {
|
|
430
|
+
await this.releasePod();
|
|
431
|
+
return filled.standing ? this.holdRefusal(filled.error) : { ok: false, error: filled.error };
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
if (this.destroyed) {
|
|
435
|
+
await this.releasePod();
|
|
436
|
+
return { ok: false, error: new RemoteExecutionError("connect_failed", "env destroyed during connect") };
|
|
437
|
+
}
|
|
438
|
+
return { ok: true, value: c.value };
|
|
439
|
+
}
|
|
440
|
+
fillChannel() {
|
|
441
|
+
const exec = (command) => this.runExec(ADAPTER_START, command, { capMs: this.cfg.dataTimeoutMs });
|
|
442
|
+
return {
|
|
443
|
+
run: async (command) => {
|
|
444
|
+
const r = await exec(command);
|
|
445
|
+
return r.ok ? { ok: true, exitCode: r.value.exitCode, stdout: r.value.stdout, stderr: r.value.stderr } : { ok: false, error: r.error.message };
|
|
446
|
+
},
|
|
447
|
+
write: async (abs, bytes) => {
|
|
448
|
+
const w = await this.writeChunked(abs, bytes, true, undefined, exec);
|
|
449
|
+
return w.ok ? { ok: true } : { ok: false, error: w.error.message };
|
|
450
|
+
},
|
|
451
|
+
};
|
|
452
|
+
}
|
|
453
|
+
async releasePod() {
|
|
454
|
+
const name = this.podName;
|
|
455
|
+
this.podName = undefined;
|
|
456
|
+
this.podIP = undefined;
|
|
457
|
+
if (name)
|
|
458
|
+
await this.deletePod(name).catch(() => { });
|
|
459
|
+
}
|
|
410
460
|
armSpace() {
|
|
411
461
|
if (this.spaceLane.state === "open")
|
|
412
462
|
this.spaceLane.space.fence.arm(this.cfg.dataTimeoutMs);
|
|
@@ -667,6 +717,8 @@ export class RemoteK8sExecutionEnv {
|
|
|
667
717
|
];
|
|
668
718
|
const pkgSourceMarked = Boolean(this.cfg.podEnv?.SEMA_PKG_SOURCE || secretEnv.SEMA_PKG_SOURCE);
|
|
669
719
|
const sysctls = sandboxPodSysctls(this.cfg.ipv6);
|
|
720
|
+
const floorSeat = await this.ensureFloorSeat(signal);
|
|
721
|
+
const mounts = [...(cache ? [{ name: CACHE_VOLUME, mountPath: cache.mount }] : []), ...(floorSeat?.mounts ?? [])];
|
|
670
722
|
const pod = {
|
|
671
723
|
apiVersion: "v1",
|
|
672
724
|
kind: "Pod",
|
|
@@ -691,7 +743,7 @@ export class RemoteK8sExecutionEnv {
|
|
|
691
743
|
? ["sh", "-c", "[ -f /opt/sema/pkg-source.sh ] && sh /opt/sema/pkg-source.sh || true; sleep infinity"]
|
|
692
744
|
: ["sh", "-c", "sleep infinity"],
|
|
693
745
|
...(envEntries.length > 0 ? { env: envEntries } : {}),
|
|
694
|
-
...(
|
|
746
|
+
...(mounts.length > 0 ? { volumeMounts: mounts } : {}),
|
|
695
747
|
resources: { limits: { cpu: this.cfg.cpu, memory: this.cfg.memory } },
|
|
696
748
|
},
|
|
697
749
|
...(broker !== undefined ? [brokerContainer(broker.image)] : []),
|
|
@@ -699,6 +751,7 @@ export class RemoteK8sExecutionEnv {
|
|
|
699
751
|
...((volumes) => (volumes.length > 0 ? { volumes } : {}))([
|
|
700
752
|
...(cache ? [{ name: CACHE_VOLUME, persistentVolumeClaim: { claimName: cache.claim.name } }] : []),
|
|
701
753
|
...(leaseName !== undefined ? [{ name: BROKER_VOLUME_NAME, secret: { secretName: leaseName, items: [{ key: BROKER_LEASE_KEY, path: BROKER_LEASE_KEY }], defaultMode: 0o400 } }] : []),
|
|
754
|
+
...(floorSeat?.volumes ?? []),
|
|
702
755
|
]),
|
|
703
756
|
},
|
|
704
757
|
};
|
|
@@ -806,7 +859,7 @@ export class RemoteK8sExecutionEnv {
|
|
|
806
859
|
this.podIP = podIP;
|
|
807
860
|
this.leaseRef = leaseName !== undefined && broker !== undefined ? { namespace: ns, podName: name, leaseSecretName: leaseName, origins: [...broker.origins] } : undefined;
|
|
808
861
|
const askHome = this.homeSeat.asks;
|
|
809
|
-
const mk = await this.runExec(`mkdir -p ${shellQuote(this.cfg.mountPath)}${askHome ? ` && { ${LANE_HOME_PROBE_COMMAND} || true; }` : ""}`);
|
|
862
|
+
const mk = await this.runExec(ADAPTER_START, `mkdir -p ${shellQuote(this.cfg.mountPath)}${askHome ? ` && { ${LANE_HOME_PROBE_COMMAND} || true; }` : ""}`);
|
|
810
863
|
if (!mk.ok || mk.value.exitCode !== 0) {
|
|
811
864
|
await this.deletePod(name).catch(() => { });
|
|
812
865
|
this.podName = undefined;
|
|
@@ -825,6 +878,7 @@ export class RemoteK8sExecutionEnv {
|
|
|
825
878
|
}
|
|
826
879
|
if (cache?.fresh)
|
|
827
880
|
this.logger?.warn("workspace_cache_fresh", { space: incarnationLogPrefix(cache.spaceKey), claim: cache.claim.name, size: cache.size });
|
|
881
|
+
await this.floor.attach(this.floorTransport(), { origin: "created", workspaceRoot: this.cfg.mountPath });
|
|
828
882
|
return ok(this.workspaceHandle());
|
|
829
883
|
}
|
|
830
884
|
async createPodSecret(secretName, podName, podUid, values, signal) {
|
|
@@ -1184,7 +1238,7 @@ export class RemoteK8sExecutionEnv {
|
|
|
1184
1238
|
const excludes = (this.cfg.snapshotExclude ?? []).map((item) => ` --exclude=${shellQuote(`./${item}`)}`).join("");
|
|
1185
1239
|
const cmd = `tar czf ${tmp}${excludes} -C ${shellQuote(this.cfg.mountPath)} . && curl -fsS -X PUT -T ${tmp} ${shellQuote(url)}; ` +
|
|
1186
1240
|
`rc=$?; rm -f ${tmp}; exit $rc`;
|
|
1187
|
-
const r = await this.runExec(cmd, { timeoutMs: boundMs });
|
|
1241
|
+
const r = await this.runExec(ADAPTER_START, cmd, { timeoutMs: boundMs });
|
|
1188
1242
|
if (!r.ok)
|
|
1189
1243
|
return { ok: false, error: new RemoteExecutionError("connect_failed", `workspace snapshot upload failed: ${this.redactUrl(r.error.message, url)}`, r.error) };
|
|
1190
1244
|
if (r.value.exitCode !== 0)
|
|
@@ -1195,10 +1249,31 @@ export class RemoteK8sExecutionEnv {
|
|
|
1195
1249
|
await this.deletePod(name).catch(() => { });
|
|
1196
1250
|
return ok(snapshotId);
|
|
1197
1251
|
}
|
|
1198
|
-
|
|
1252
|
+
resumeVM(snapshotId, options) {
|
|
1199
1253
|
if (!this.cfg.s3Snapshot)
|
|
1200
|
-
return unsupported("resumeVM");
|
|
1201
|
-
const
|
|
1254
|
+
return Promise.resolve(unsupported("resumeVM"));
|
|
1255
|
+
const same = this.resumeInFlight;
|
|
1256
|
+
if (same?.snapshotId === snapshotId)
|
|
1257
|
+
return same.walk;
|
|
1258
|
+
const prior = this.connectInFlight;
|
|
1259
|
+
const walk = this.resumeWalk(prior, snapshotId, options).finally(() => {
|
|
1260
|
+
if (this.connectInFlight === walk)
|
|
1261
|
+
this.connectInFlight = undefined;
|
|
1262
|
+
if (this.resumeInFlight?.walk === walk)
|
|
1263
|
+
this.resumeInFlight = undefined;
|
|
1264
|
+
});
|
|
1265
|
+
this.connectInFlight = walk;
|
|
1266
|
+
this.resumeInFlight = { snapshotId, walk };
|
|
1267
|
+
return walk;
|
|
1268
|
+
}
|
|
1269
|
+
async resumeWalk(prior, snapshotId, options) {
|
|
1270
|
+
if (prior)
|
|
1271
|
+
await prior;
|
|
1272
|
+
if (this.podName) {
|
|
1273
|
+
this.logger?.warn("sandbox_pod_released_before_restore", { pod: this.podName, ...(this.cfg.sessionId ? { sessionId: this.cfg.sessionId } : {}), effect: "this env held a pod minted before the park snapshot's restore — it is deleted; the restore provisions the leg's one pod" });
|
|
1274
|
+
await this.releasePod();
|
|
1275
|
+
}
|
|
1276
|
+
const r = await this.settle(await this.restoreSnapshot({ lane: "park" }, snapshotId, options?.abortSignal));
|
|
1202
1277
|
if (!r.ok)
|
|
1203
1278
|
return r;
|
|
1204
1279
|
this.handleSnapshotId = snapshotId;
|
|
@@ -1220,22 +1295,21 @@ export class RemoteK8sExecutionEnv {
|
|
|
1220
1295
|
const c = await this.provision(abortSignal);
|
|
1221
1296
|
if (!c.ok)
|
|
1222
1297
|
return c;
|
|
1223
|
-
const r = await this.runExec(cmd, { timeoutMs: this.cfg.dataTimeoutMs });
|
|
1298
|
+
const r = await this.runExec(ADAPTER_START, cmd, { timeoutMs: this.cfg.dataTimeoutMs });
|
|
1224
1299
|
const failure = !r.ok
|
|
1225
1300
|
? { cls: r.error.code === "timeout" ? "timeout" : "transport", detail: `workspace snapshot restore failed: ${this.redactUrl(r.error.message, url)}`, cause: r.error }
|
|
1226
1301
|
: r.value.exitCode !== 0
|
|
1227
1302
|
? { cls: restoreClassOfExit(r.value.exitCode), detail: `workspace snapshot restore exited ${r.value.exitCode}: ${this.redactUrl(r.value.stderr, url).slice(0, 300)}` }
|
|
1228
1303
|
: undefined;
|
|
1229
1304
|
if (!failure) {
|
|
1230
|
-
if (
|
|
1231
|
-
|
|
1232
|
-
|
|
1305
|
+
if (r.ok && r.value.stdout.includes("EMPTY_SNAPSHOT")) {
|
|
1306
|
+
if (target.lane === "space")
|
|
1307
|
+
await this.dropDanglingLatest(target.space, snapshotId);
|
|
1308
|
+
return { ok: true, value: this.workspaceHandle(), origin: "minted" };
|
|
1309
|
+
}
|
|
1310
|
+
return { ok: true, value: this.workspaceHandle(), origin: "restored" };
|
|
1233
1311
|
}
|
|
1234
|
-
|
|
1235
|
-
this.podName = undefined;
|
|
1236
|
-
this.podIP = undefined;
|
|
1237
|
-
if (name)
|
|
1238
|
-
await this.deletePod(name).catch(() => { });
|
|
1312
|
+
await this.releasePod();
|
|
1239
1313
|
if (attempt < RESTORE_MAX_ATTEMPTS[target.lane] && restoreFailureIsTransient(failure.cls)) {
|
|
1240
1314
|
if (await this.restorePause(abortSignal))
|
|
1241
1315
|
continue;
|
|
@@ -1326,6 +1400,7 @@ export class RemoteK8sExecutionEnv {
|
|
|
1326
1400
|
}
|
|
1327
1401
|
async destroy() {
|
|
1328
1402
|
await this.disposeBackgroundShells().catch(() => { });
|
|
1403
|
+
this.floor.dispose();
|
|
1329
1404
|
this.destroyed = true;
|
|
1330
1405
|
const lane = this.spaceLane;
|
|
1331
1406
|
switch (lane.state) {
|
|
@@ -1410,7 +1485,7 @@ export class RemoteK8sExecutionEnv {
|
|
|
1410
1485
|
async killPodProcesses() {
|
|
1411
1486
|
if (!this.podName)
|
|
1412
1487
|
return false;
|
|
1413
|
-
const r = await this.runExec(buildKillAllScript(), { timeoutMs: this.cfg.rpcTimeoutMs + KILL_ALL_RECHECK_MS });
|
|
1488
|
+
const r = await this.runExec(ADAPTER_START, buildKillAllScript(), { timeoutMs: this.cfg.rpcTimeoutMs + KILL_ALL_RECHECK_MS });
|
|
1414
1489
|
return r.ok && r.value.exitCode === 0;
|
|
1415
1490
|
}
|
|
1416
1491
|
async persistWorkspace(space) {
|
|
@@ -1463,23 +1538,124 @@ export class RemoteK8sExecutionEnv {
|
|
|
1463
1538
|
await this.endSpace(space);
|
|
1464
1539
|
}
|
|
1465
1540
|
}
|
|
1466
|
-
|
|
1541
|
+
async ensureFloorSeat(signal) {
|
|
1542
|
+
const seat = buildFloorPodSeat(this.floor);
|
|
1543
|
+
if (seat === undefined)
|
|
1544
|
+
return undefined;
|
|
1545
|
+
if (seat.configMaps.length === 0) {
|
|
1546
|
+
this.floor.refusePlacement("this server carries no pinned supervisor build");
|
|
1547
|
+
return undefined;
|
|
1548
|
+
}
|
|
1549
|
+
for (const cm of seat.configMaps) {
|
|
1550
|
+
const body = { apiVersion: "v1", kind: "ConfigMap", metadata: { name: cm.name }, immutable: true, binaryData: { [FLOOR_K8S_CONFIGMAP_KEY]: cm.binaryDataB64 } };
|
|
1551
|
+
let r;
|
|
1552
|
+
try {
|
|
1553
|
+
r = await this.request("POST", `/api/v1/namespaces/${this.cfg.namespace}/configmaps`, body, { signal });
|
|
1554
|
+
}
|
|
1555
|
+
catch (e) {
|
|
1556
|
+
this.floor.refusePlacement(`creating the supervisor ConfigMap ${cm.name} failed: ${errorMessageOf(e)}`);
|
|
1557
|
+
return undefined;
|
|
1558
|
+
}
|
|
1559
|
+
if (r.status !== 409 && (r.status < 200 || r.status >= 300)) {
|
|
1560
|
+
this.floor.refusePlacement(`creating the supervisor ConfigMap ${cm.name} was answered ${r.status}${r.status === 401 || r.status === 403 ? " (the server's service account needs create on configmaps in this namespace)" : ""}`);
|
|
1561
|
+
return undefined;
|
|
1562
|
+
}
|
|
1563
|
+
}
|
|
1564
|
+
return seat;
|
|
1565
|
+
}
|
|
1566
|
+
floorTransport() {
|
|
1567
|
+
return {
|
|
1568
|
+
bootstrap: async (command) => {
|
|
1569
|
+
const r = await this.runExec(BOOTSTRAP_START, command, { timeoutMs: this.cfg.rpcTimeoutMs });
|
|
1570
|
+
return r.ok ? { ok: true, exitCode: r.value.exitCode, stdout: r.value.stdout, stderr: r.value.stderr } : { ok: false, error: r.error.message };
|
|
1571
|
+
},
|
|
1572
|
+
place: async () => ({ ok: true }),
|
|
1573
|
+
openRelay: (identity, command, events) => {
|
|
1574
|
+
let closed = false;
|
|
1575
|
+
const close = (why) => {
|
|
1576
|
+
if (closed)
|
|
1577
|
+
return;
|
|
1578
|
+
closed = true;
|
|
1579
|
+
events.closed(why);
|
|
1580
|
+
};
|
|
1581
|
+
switch (identity) {
|
|
1582
|
+
case "root":
|
|
1583
|
+
queueMicrotask(() => close("a k8s exec cannot start the relay as root"));
|
|
1584
|
+
return { send: () => { }, close: () => { } };
|
|
1585
|
+
case "exec-user":
|
|
1586
|
+
break;
|
|
1587
|
+
}
|
|
1588
|
+
let sock;
|
|
1589
|
+
try {
|
|
1590
|
+
const { url, headers } = this.execUrl(["sh", "-c", command], true);
|
|
1591
|
+
sock = this.openExec(url, headers);
|
|
1592
|
+
}
|
|
1593
|
+
catch (e) {
|
|
1594
|
+
queueMicrotask(() => close(`the relay exec could not be opened: ${errorMessageOf(e)}`));
|
|
1595
|
+
return { send: () => { }, close: () => { } };
|
|
1596
|
+
}
|
|
1597
|
+
const decoder = new StringDecoder("utf8");
|
|
1598
|
+
let open = false;
|
|
1599
|
+
const pending = [];
|
|
1600
|
+
const write = (frame) => {
|
|
1601
|
+
if (sock.send === undefined)
|
|
1602
|
+
return close("the exec socket cannot write (no stdin channel)");
|
|
1603
|
+
try {
|
|
1604
|
+
sock.send(frame);
|
|
1605
|
+
}
|
|
1606
|
+
catch (e) {
|
|
1607
|
+
close(`writing to the relay failed: ${errorMessageOf(e)}`);
|
|
1608
|
+
}
|
|
1609
|
+
};
|
|
1610
|
+
sock.on("open", () => {
|
|
1611
|
+
open = true;
|
|
1612
|
+
for (const f of pending.splice(0))
|
|
1613
|
+
write(f);
|
|
1614
|
+
});
|
|
1615
|
+
sock.on("message", (...args) => {
|
|
1616
|
+
const data = args[0];
|
|
1617
|
+
const buf = Buffer.isBuffer(data) ? data : Buffer.from(data);
|
|
1618
|
+
if (buf.length > 1 && buf[0] === 1)
|
|
1619
|
+
events.data(decoder.write(buf.subarray(1)));
|
|
1620
|
+
});
|
|
1621
|
+
sock.on("close", () => close("the relay exec closed"));
|
|
1622
|
+
sock.on("error", (e) => close(`the relay exec failed: ${errorMessageOf(e)}`));
|
|
1623
|
+
return {
|
|
1624
|
+
send: (line) => {
|
|
1625
|
+
const frame = Buffer.concat([Buffer.from([0]), Buffer.from(line, "utf8")]);
|
|
1626
|
+
if (open)
|
|
1627
|
+
write(frame);
|
|
1628
|
+
else
|
|
1629
|
+
pending.push(frame);
|
|
1630
|
+
},
|
|
1631
|
+
close: () => (sock.terminate ?? sock.close).call(sock),
|
|
1632
|
+
};
|
|
1633
|
+
},
|
|
1634
|
+
};
|
|
1635
|
+
}
|
|
1636
|
+
execUrl(argv, stdin = false) {
|
|
1467
1637
|
const { apiUrl, token } = this.creds();
|
|
1468
1638
|
const base = apiUrl.replace(/^http/, "ws");
|
|
1469
1639
|
const q = new URLSearchParams();
|
|
1470
1640
|
q.append("container", "sandbox");
|
|
1641
|
+
if (stdin)
|
|
1642
|
+
q.append("stdin", "true");
|
|
1471
1643
|
q.append("stdout", "true");
|
|
1472
1644
|
q.append("stderr", "true");
|
|
1473
|
-
for (const part of
|
|
1645
|
+
for (const part of argv)
|
|
1474
1646
|
q.append("command", part);
|
|
1475
1647
|
return { url: `${base}/api/v1/namespaces/${this.cfg.namespace}/pods/${this.podName}/exec?${q.toString()}`, headers: { authorization: `Bearer ${token}` } };
|
|
1476
1648
|
}
|
|
1477
|
-
runExec(command, opts) {
|
|
1649
|
+
runExec(start, command, opts) {
|
|
1650
|
+
const launch = this.floor.wrap(start);
|
|
1651
|
+
if (!launch.ok)
|
|
1652
|
+
return Promise.resolve(launch);
|
|
1653
|
+
opts?.onLaunch?.(launch.value);
|
|
1478
1654
|
let sock;
|
|
1479
1655
|
let headers;
|
|
1480
1656
|
let url;
|
|
1481
1657
|
try {
|
|
1482
|
-
({ url, headers } = this.execUrl(command));
|
|
1658
|
+
({ url, headers } = this.execUrl(floorArgv(launch.value, ["sh", "-c", command])));
|
|
1483
1659
|
sock = this.openExec(url, headers);
|
|
1484
1660
|
}
|
|
1485
1661
|
catch (e) {
|
|
@@ -1673,12 +1849,12 @@ export class RemoteK8sExecutionEnv {
|
|
|
1673
1849
|
});
|
|
1674
1850
|
}
|
|
1675
1851
|
async exec(command, options) {
|
|
1676
|
-
return this.execCall(createCwdGuardedCall("k8s", command, this.workingDirOf(options?.cwd), options?.env), options);
|
|
1852
|
+
return this.execCall(engineStart(options?.readFloor), createCwdGuardedCall("k8s", command, this.workingDirOf(options?.cwd), options?.env), options);
|
|
1677
1853
|
}
|
|
1678
1854
|
fsExec(command, abortSignal) {
|
|
1679
|
-
return this.execCall(createCwdGuardedCall("k8s", command, undefined, undefined), { abortSignal });
|
|
1855
|
+
return this.execCall(ADAPTER_START, createCwdGuardedCall("k8s", command, undefined, undefined), { abortSignal });
|
|
1680
1856
|
}
|
|
1681
|
-
async execCall(call, options) {
|
|
1857
|
+
async execCall(start, call, options) {
|
|
1682
1858
|
const c = await this.ensureConnected();
|
|
1683
1859
|
if (!c.ok)
|
|
1684
1860
|
return c;
|
|
@@ -1687,7 +1863,7 @@ export class RemoteK8sExecutionEnv {
|
|
|
1687
1863
|
const abortSignal = options?.abortSignal ? AbortSignal.any([options.abortSignal, inflight.signal]) : inflight.signal;
|
|
1688
1864
|
try {
|
|
1689
1865
|
if (!detachSignal) {
|
|
1690
|
-
return call.settle(await this.runExec(call.command, {
|
|
1866
|
+
return call.settle(await this.runExec(start, call.command, {
|
|
1691
1867
|
signal: abortSignal,
|
|
1692
1868
|
timeoutMs: options?.timeout != null ? options.timeout * 1000 : undefined,
|
|
1693
1869
|
boundOutput: true,
|
|
@@ -1699,7 +1875,10 @@ export class RemoteK8sExecutionEnv {
|
|
|
1699
1875
|
const adoptId = randomUUID();
|
|
1700
1876
|
const dir = `${K8S_BG_DIR_ROOT}/exec-${adoptId}`;
|
|
1701
1877
|
const tee = { out: seedMemStream(""), err: seedMemStream("") };
|
|
1702
|
-
|
|
1878
|
+
let startId;
|
|
1879
|
+
let adoptedHere = false;
|
|
1880
|
+
const settled = call.settle(await this.runExec(start.kind === "engine" ? engineStart(start.readFloor, true) : start, buildDetachCapableExec(dir, call.command), {
|
|
1881
|
+
onLaunch: (l) => void (startId = l.mode === "run" ? l.startId : undefined),
|
|
1703
1882
|
signal: abortSignal,
|
|
1704
1883
|
timeoutMs: options?.timeout != null ? options.timeout * 1000 : undefined,
|
|
1705
1884
|
boundOutput: true,
|
|
@@ -1716,12 +1895,17 @@ export class RemoteK8sExecutionEnv {
|
|
|
1716
1895
|
data: (channel, payload) => feedMemStream(channel === 1 ? mem.out : mem.err, payload, K8S_BG_MEM_CAP),
|
|
1717
1896
|
closed: (terminal) => ctx.onTerminal(terminal.failed, terminal.exitCode),
|
|
1718
1897
|
});
|
|
1719
|
-
return { jobId: adoptId, dir, stdoutCursor: 0, stderrCursor: 0, adopt: { mem, sock: io.sock } };
|
|
1898
|
+
return { jobId: adoptId, dir, stdoutCursor: 0, stderrCursor: 0, adopt: { mem, sock: io.sock }, ...(startId !== undefined ? { startId } : {}) };
|
|
1720
1899
|
});
|
|
1900
|
+
if (adopted !== undefined)
|
|
1901
|
+
adoptedHere = true;
|
|
1721
1902
|
return adopted?.shellId;
|
|
1722
1903
|
},
|
|
1723
1904
|
},
|
|
1724
1905
|
}));
|
|
1906
|
+
if (!adoptedHere)
|
|
1907
|
+
this.floor.release(startId);
|
|
1908
|
+
return settled;
|
|
1725
1909
|
}
|
|
1726
1910
|
finally {
|
|
1727
1911
|
this.endExec(inflight);
|
|
@@ -1759,7 +1943,7 @@ export class RemoteK8sExecutionEnv {
|
|
|
1759
1943
|
const cap = options?.maxOutputBytes;
|
|
1760
1944
|
let bytes = 0;
|
|
1761
1945
|
const signal = options?.signal ? AbortSignal.any([options.signal, ac.signal]) : ac.signal;
|
|
1762
|
-
const done = this.runExec(buildCwdEnvCommand(command, this.workingDirOf(options?.cwd), options?.env, "raw").command, {
|
|
1946
|
+
const done = this.runExec(engineStart(undefined), buildCwdEnvCommand(command, this.workingDirOf(options?.cwd), options?.env, "raw").command, {
|
|
1763
1947
|
signal,
|
|
1764
1948
|
timeoutMs: options?.timeout != null ? options.timeout * 1000 : undefined,
|
|
1765
1949
|
readTimeoutMs: options?.readTimeoutMs,
|
|
@@ -1828,7 +2012,8 @@ export class RemoteK8sExecutionEnv {
|
|
|
1828
2012
|
}
|
|
1829
2013
|
makeBgDriver() {
|
|
1830
2014
|
const self = this;
|
|
1831
|
-
const bgExec = (script, boundOutput) => self.runExec(script, { timeoutMs: self.cfg.rpcTimeoutMs, boundOutput });
|
|
2015
|
+
const bgExec = (start, script, boundOutput, onLaunch) => self.runExec(start, script, { timeoutMs: self.cfg.rpcTimeoutMs, boundOutput, ...(onLaunch !== undefined ? { onLaunch } : {}) });
|
|
2016
|
+
const under = (state) => ({ kind: "under", startId: state.startId });
|
|
1832
2017
|
return {
|
|
1833
2018
|
async launch(command, options, ctx) {
|
|
1834
2019
|
const conn = await self.ensureConnected();
|
|
@@ -1839,14 +2024,17 @@ export class RemoteK8sExecutionEnv {
|
|
|
1839
2024
|
const inner = buildCwdEnvCommand(command, self.workingDirOf(options?.cwd), options?.env, "raw").command;
|
|
1840
2025
|
const cmdB64 = Buffer.from(inner, "utf8").toString("base64");
|
|
1841
2026
|
const runnerB64 = Buffer.from(buildBgRunnerScript(dir), "utf8").toString("base64");
|
|
1842
|
-
|
|
2027
|
+
let startId;
|
|
2028
|
+
const r = await bgExec(engineStart(options?.readFloor, true), buildBgLauncherScript(dir, cmdB64, runnerB64), true, (l) => void (startId = l.mode === "run" ? l.startId : undefined));
|
|
2029
|
+
if (!r.ok || r.value.exitCode !== 0 || !r.value.stdout.includes("SEMABG_LAUNCHED"))
|
|
2030
|
+
self.floor.release(startId);
|
|
1843
2031
|
if (!r.ok)
|
|
1844
2032
|
throw new Error(`background launch failed: ${r.error.message}`);
|
|
1845
2033
|
if (r.value.exitCode !== 0 || !r.value.stdout.includes("SEMABG_LAUNCHED")) {
|
|
1846
2034
|
throw new Error(`background launch exited ${r.value.exitCode}: ${r.value.stderr.slice(0, 200)}`);
|
|
1847
2035
|
}
|
|
1848
2036
|
void ctx;
|
|
1849
|
-
return { jobId, dir, stdoutCursor: 0, stderrCursor: 0 };
|
|
2037
|
+
return { jobId, dir, stdoutCursor: 0, stderrCursor: 0, ...(startId !== undefined ? { startId } : {}) };
|
|
1850
2038
|
},
|
|
1851
2039
|
async read(state) {
|
|
1852
2040
|
if (state.adopt) {
|
|
@@ -1859,7 +2047,7 @@ export class RemoteK8sExecutionEnv {
|
|
|
1859
2047
|
bytesDroppedBeforeCursor: o.dropped + e.dropped,
|
|
1860
2048
|
};
|
|
1861
2049
|
}
|
|
1862
|
-
const r = await bgExec(buildBgPollScript(state.dir, state.stdoutCursor, state.stderrCursor, K8S_BG_READ_CAP), false);
|
|
2050
|
+
const r = await bgExec(under(state), buildBgPollScript(state.dir, state.stdoutCursor, state.stderrCursor, K8S_BG_READ_CAP), false);
|
|
1863
2051
|
if (!r.ok)
|
|
1864
2052
|
throw new Error(`background poll failed: ${r.error.message}`);
|
|
1865
2053
|
const p = parseBgPollOutput(r.value.stdout);
|
|
@@ -1874,7 +2062,7 @@ export class RemoteK8sExecutionEnv {
|
|
|
1874
2062
|
state.stderrCursor = p.errTotal;
|
|
1875
2063
|
let terminal = p.exitCode !== undefined ? { failed: false, exitCode: p.exitCode } : !p.alive ? { failed: true } : undefined;
|
|
1876
2064
|
if (!terminal && (p.outTotal > K8S_BG_FILE_CAP || p.errTotal > K8S_BG_FILE_CAP)) {
|
|
1877
|
-
void bgExec(buildBgKillScript(state.dir), true).catch(() => { });
|
|
2065
|
+
void bgExec(under(state), buildBgKillScript(state.dir), true).catch(() => { });
|
|
1878
2066
|
terminal = { failed: true };
|
|
1879
2067
|
}
|
|
1880
2068
|
return {
|
|
@@ -1886,7 +2074,7 @@ export class RemoteK8sExecutionEnv {
|
|
|
1886
2074
|
};
|
|
1887
2075
|
},
|
|
1888
2076
|
async kill(state) {
|
|
1889
|
-
await bgExec(buildBgKillScript(state.dir), true).catch(() => { });
|
|
2077
|
+
await bgExec(under(state), buildBgKillScript(state.dir), true).catch(() => { });
|
|
1890
2078
|
},
|
|
1891
2079
|
async disposeOne(state) {
|
|
1892
2080
|
const closeAdopted = () => {
|
|
@@ -1899,10 +2087,12 @@ export class RemoteK8sExecutionEnv {
|
|
|
1899
2087
|
};
|
|
1900
2088
|
if (self.destroyed) {
|
|
1901
2089
|
closeAdopted();
|
|
2090
|
+
self.floor.release(state.startId);
|
|
1902
2091
|
return;
|
|
1903
2092
|
}
|
|
1904
|
-
await bgExec(buildBgDisposeScript(state.dir), true).catch(() => { });
|
|
2093
|
+
await bgExec(under(state), buildBgDisposeScript(state.dir), true).catch(() => { });
|
|
1905
2094
|
closeAdopted();
|
|
2095
|
+
self.floor.release(state.startId);
|
|
1906
2096
|
},
|
|
1907
2097
|
};
|
|
1908
2098
|
}
|
|
@@ -1958,12 +2148,12 @@ export class RemoteK8sExecutionEnv {
|
|
|
1958
2148
|
const abs = this.resolve(p);
|
|
1959
2149
|
return appendFileStaged(this.stagedLane(content), abs, abortSignal, (ctx) => ctx.inPlace((signal) => this.writeChunked(abs, content, false, signal)));
|
|
1960
2150
|
}
|
|
1961
|
-
async writeChunked(abs, content, truncateFirst, abortSignal) {
|
|
2151
|
+
async writeChunked(abs, content, truncateFirst, abortSignal, exec = (command) => this.execData(command, abortSignal)) {
|
|
1962
2152
|
const bytes = typeof content === "string" ? Buffer.from(content, "utf8") : Buffer.from(content);
|
|
1963
2153
|
for (let off = 0, first = true; off < bytes.length || first; off += WRITE_CHUNK_BYTES, first = false) {
|
|
1964
2154
|
const b64 = bytes.subarray(off, off + WRITE_CHUNK_BYTES).toString("base64");
|
|
1965
2155
|
const redirect = first && truncateFirst ? ">" : ">>";
|
|
1966
|
-
const r = await
|
|
2156
|
+
const r = await exec(`printf '%s' ${shellQuote(b64)} | base64 -d ${redirect} ${shellQuote(abs)}`);
|
|
1967
2157
|
if (!r.ok)
|
|
1968
2158
|
return { ok: false, error: fileErrorFromExec(r.error, abs) };
|
|
1969
2159
|
if (r.value.exitCode !== 0)
|
|
@@ -2013,7 +2203,7 @@ export class RemoteK8sExecutionEnv {
|
|
|
2013
2203
|
const c = await this.ensureConnected();
|
|
2014
2204
|
if (!c.ok)
|
|
2015
2205
|
return c;
|
|
2016
|
-
return this.runExec(command, { signal: abortSignal, capMs: this.cfg.dataTimeoutMs });
|
|
2206
|
+
return this.runExec(ADAPTER_START, command, { signal: abortSignal, capMs: this.cfg.dataTimeoutMs });
|
|
2017
2207
|
}
|
|
2018
2208
|
workingDirOf(cwd) {
|
|
2019
2209
|
const dir = cwd ?? this.cwd;
|