@sema-agent/server 7.139.0 → 7.141.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 +471 -0
- package/README.md +1 -1
- package/README.zh-CN.md +1 -1
- package/USAGE.md +94 -26
- package/dist/adoption/runner.js +1 -1
- package/dist/adoption/sql.js +1 -1
- package/dist/approval-batch-census.d.ts +27 -0
- package/dist/approval-batch-census.js +27 -0
- package/dist/approval-card.d.ts +37 -22
- package/dist/approval-card.js +6 -3
- 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/bench/s1/live-deps.js +3 -2
- package/dist/bench/s1/row.d.ts +5 -2
- package/dist/bench/s1/row.js +2 -22
- package/dist/boot/adoption.js +1 -1
- 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.js +18 -12
- package/dist/boot/execution-lane.d.ts +8 -0
- package/dist/boot/execution-lane.js +6 -1
- package/dist/boot/memory-boundary.d.ts +16 -53
- package/dist/boot/memory-boundary.js +10 -20
- package/dist/boot/memory-consolidation.d.ts +16 -12
- package/dist/boot/memory-consolidation.js +24 -13
- package/dist/boot/reapers.d.ts +2 -2
- package/dist/boot/resolve-spec.d.ts +3 -5
- package/dist/boot/resolve-spec.js +9 -8
- package/dist/boot/runner-deps.d.ts +3 -3
- package/dist/boot/runner-deps.js +1 -0
- package/dist/boot/session-faces.d.ts +1 -1
- package/dist/boot/session-faces.js +4 -3
- package/dist/boot/shutdown.js +5 -0
- package/dist/boot/stage-03-store-backend.d.ts +2 -6
- package/dist/boot/stage-03-store-backend.js +2 -3
- package/dist/boot/stage-04-stores.d.ts +2 -6
- package/dist/boot/stage-04-stores.js +5 -5
- package/dist/boot/stage-05-execution-env.d.ts +2 -6
- package/dist/boot/stage-05-execution-env.js +3 -1
- package/dist/boot/stage-06-runners.d.ts +2 -8
- package/dist/boot/stage-06-runners.js +3 -3
- package/dist/boot/stage-07-capability-layer.d.ts +2 -7
- package/dist/boot/stage-07-capability-layer.js +8 -10
- package/dist/boot/stage-08-reapers.d.ts +2 -7
- package/dist/boot/stage-09-leader.d.ts +2 -7
- package/dist/boot/stage-10-http-server.d.ts +2 -7
- package/dist/boot/stage-10-http-server.js +6 -4
- package/dist/boot/stores.d.ts +3 -6
- package/dist/boot/stores.js +37 -44
- package/dist/boot/task-list-lane.d.ts +1 -1
- package/dist/boot/task-list-lane.js +1 -1
- package/dist/boot/web-admin.js +36 -9
- package/dist/capabilities/execution-lane.d.ts +2 -2
- package/dist/capabilities/scenarios.d.ts +1 -1
- package/dist/capability-facts.d.ts +9 -0
- package/dist/capability-facts.js +9 -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 +18 -17
- package/dist/config-provider.js +28 -36
- package/dist/config-types.d.ts +36 -9
- package/dist/config.js +56 -46
- package/dist/delegated-session-owner.d.ts +23 -20
- package/dist/delegated-session-owner.js +2 -2
- package/dist/deployment-shape.d.ts +9 -8
- package/dist/deployment-shape.js +4 -4
- 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 +145 -107
- package/dist/execution-lane-caps.js +144 -83
- 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/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 +4 -0
- package/dist/http/live-frame.d.ts +24 -0
- package/dist/http/live-frame.js +16 -0
- package/dist/http/memory-compliance-failure-reply.d.ts +3 -1
- package/dist/http/memory-compliance-failure-reply.js +5 -0
- package/dist/http/memory-layer-reply.d.ts +30 -0
- package/dist/http/memory-layer-reply.js +24 -0
- package/dist/http/resume-legs.js +49 -35
- package/dist/http/retired-service-deps.d.ts +1 -1
- package/dist/http/retired-service-deps.js +1 -1
- package/dist/http/route-table.js +2 -0
- package/dist/http/routes/adoption.js +1 -1
- package/dist/http/routes/approvals-assistant.js +1 -0
- package/dist/http/routes/capabilities.js +5 -4
- 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 +4 -1
- package/dist/http/routes/memory-compliance.js +9 -2
- package/dist/http/routes/memory-consolidation.js +12 -1
- package/dist/http/routes/memory-origin.js +14 -2
- package/dist/http/routes/memory-policy.js +7 -2
- 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/session-sync.d.ts +3 -6
- package/dist/http/routes/session-sync.js +7 -2
- package/dist/http/routes/sessions.js +33 -15
- package/dist/http/routes/tasks.js +67 -68
- package/dist/http/run-read-face.js +3 -3
- package/dist/http/send.d.ts +1 -1
- package/dist/http/send.js +2 -2
- package/dist/http/server.d.ts +14 -27
- package/dist/http/server.js +2 -2
- 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/identity-source.d.ts +1 -1
- package/dist/identity-source.js +1 -1
- package/dist/instruction-source-notice.d.ts +7 -0
- package/dist/instruction-source-notice.js +6 -6
- package/dist/lane-home-probe.d.ts +68 -42
- package/dist/lane-home-probe.js +53 -30
- package/dist/leader/wire.js +5 -4
- package/dist/mcp-assertion-header.d.ts +4 -0
- package/dist/mcp-assertion-header.js +5 -0
- package/dist/memory-bundle-engine.d.ts +16 -9
- package/dist/memory-bundle-engine.js +29 -6
- package/dist/memory-operator-faces.d.ts +85 -30
- package/dist/memory-operator-faces.js +136 -30
- package/dist/memory-posture.d.ts +1 -1
- package/dist/memory-project-layer.d.ts +3 -21
- package/dist/memory-project-layer.js +11 -8
- package/dist/memory-scope-stores.d.ts +73 -0
- package/dist/memory-scope-stores.js +86 -0
- package/dist/memory-scope.d.ts +33 -9
- package/dist/memory-scope.js +17 -5
- package/dist/memory-sync-client.d.ts +34 -9
- package/dist/memory-sync-client.js +108 -28
- package/dist/observability/fail-open.d.ts +30 -2
- package/dist/observability/fail-open.js +30 -2
- package/dist/observability/metrics.js +0 -1
- package/dist/observability/run-terminal.d.ts +6 -2
- package/dist/observability/run-terminal.js +25 -26
- 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/adoption-log-sql.js +4 -4
- package/dist/plugins/approval-ask-store-sql.js +14 -14
- package/dist/plugins/approval-nonce-store.js +4 -4
- package/dist/plugins/blob-backend.d.ts +4 -4
- package/dist/plugins/blob-backend.js +7 -7
- package/dist/plugins/breaker-state-sql.js +2 -2
- package/dist/plugins/caching-session-store.d.ts +6 -0
- package/dist/plugins/caching-session-store.js +3 -0
- package/dist/plugins/checkpoint-index-columns.js +2 -2
- package/dist/plugins/checkpoint-store-sql.d.ts +65 -17
- package/dist/plugins/checkpoint-store-sql.js +90 -19
- package/dist/plugins/device-store-sql.d.ts +5 -2
- package/dist/plugins/device-store-sql.js +23 -20
- package/dist/plugins/ensure-index.d.ts +1 -2
- package/dist/plugins/execution-placement-sql.d.ts +19 -9
- package/dist/plugins/execution-placement-sql.js +20 -9
- package/dist/plugins/file-history-store-sql.d.ts +2 -2
- package/dist/plugins/file-history-store-sql.js +4 -4
- package/dist/plugins/file-run-store.d.ts +2 -2
- package/dist/plugins/file-run-store.js +1 -1
- package/dist/plugins/fork-routing-session-store.d.ts +10 -32
- package/dist/plugins/fork-routing-session-store.js +2 -61
- package/dist/plugins/image-bake-store-sql.js +9 -9
- package/dist/plugins/image-index-sql.js +11 -11
- package/dist/plugins/k8s-exec-protocol.d.ts +4 -1
- package/dist/plugins/k8s-exec-protocol.js +6 -1
- package/dist/plugins/leader-run-store-sql.js +4 -4
- 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 +227 -35
- package/dist/plugins/local-session-store.js +518 -37
- package/dist/plugins/local-session-window.d.ts +17 -3
- package/dist/plugins/local-session-window.js +7 -13
- package/dist/plugins/mailbox-store-sql.d.ts +2 -1
- package/dist/plugins/mailbox-store-sql.js +4 -4
- package/dist/plugins/memory-engine-tidb.js +5 -5
- package/dist/plugins/memory-optout-grant-store-sql.js +1 -1
- package/dist/plugins/memory-run-store.d.ts +2 -2
- package/dist/plugins/memory-run-store.js +1 -1
- package/dist/plugins/memory-sync-store-tidb.js +3 -3
- package/dist/plugins/one-time-migrations.d.ts +52 -11
- package/dist/plugins/one-time-migrations.js +101 -39
- package/dist/plugins/org-delivery-quota-store.js +2 -2
- package/dist/plugins/outcome-ledger-sql.js +2 -2
- package/dist/plugins/permission-rule-store-sql.d.ts +4 -4
- package/dist/plugins/permission-rule-store-sql.js +16 -16
- 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-adb.d.ts +7 -24
- package/dist/plugins/remote-env-adb.js +22 -5
- package/dist/plugins/remote-env-device.d.ts +2 -28
- package/dist/plugins/remote-env-device.js +2 -1
- package/dist/plugins/remote-env-e2b.d.ts +40 -27
- package/dist/plugins/remote-env-e2b.js +180 -20
- package/dist/plugins/remote-env-file-error.d.ts +1 -1
- package/dist/plugins/remote-env-file-error.js +8 -2
- package/dist/plugins/remote-env-host.d.ts +19 -35
- package/dist/plugins/remote-env-host.js +26 -3
- package/dist/plugins/remote-env-k8s.d.ts +22 -14
- package/dist/plugins/remote-env-k8s.js +150 -26
- package/dist/plugins/remote-env-local-docker.d.ts +27 -24
- package/dist/plugins/remote-env-local-docker.js +91 -24
- package/dist/plugins/remote-env-ssh.d.ts +4 -24
- package/dist/plugins/remote-env-ssh.js +4 -3
- package/dist/plugins/resume-anchor-store-sql.js +2 -2
- package/dist/plugins/retention-lane-store-sql.d.ts +1 -1
- package/dist/plugins/retention-lane-store-sql.js +2 -2
- package/dist/plugins/retention-store-sql.d.ts +1 -1
- package/dist/plugins/retention-store-sql.js +6 -6
- package/dist/plugins/run-store-sql.d.ts +3 -3
- package/dist/plugins/run-store-sql.js +19 -18
- package/dist/plugins/sandbox-read-floor.d.ts +273 -0
- package/dist/plugins/sandbox-read-floor.js +608 -0
- package/dist/plugins/schema-consistency.d.ts +6 -6
- package/dist/plugins/schema-consistency.js +1 -1
- package/dist/plugins/schema-ensure-legs.d.ts +2 -3
- package/dist/plugins/schema-ensure-legs.js +2 -2
- package/dist/plugins/schema-live-columns.d.ts +1 -1
- package/dist/plugins/session-capture-record-store-sql.js +1 -1
- 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-rules-column.d.ts +1 -1
- package/dist/plugins/session-rules-column.js +1 -1
- package/dist/plugins/session-store.d.ts +18 -20
- package/dist/plugins/session-store.js +4 -12
- package/dist/plugins/session-workspace-cell.d.ts +1 -1
- package/dist/plugins/session-workspace-cell.js +1 -1
- package/dist/plugins/shared-memory-store-sql.js +2 -2
- package/dist/plugins/sql-driver.d.ts +10 -5
- package/dist/plugins/sql-driver.js +6 -6
- package/dist/plugins/sql-errors.js +1 -1
- package/dist/plugins/store-backend.d.ts +35 -6
- package/dist/plugins/store-backend.js +27 -10
- package/dist/plugins/store-contracts.d.ts +9 -2
- package/dist/plugins/task-list-store-sql.js +11 -11
- package/dist/plugins/tidb-pool.js +7 -6
- package/dist/plugins/tidb-session-storage.js +4 -4
- package/dist/plugins/tidb-session-store.d.ts +4 -1
- package/dist/plugins/tidb-session-store.js +16 -15
- package/dist/plugins/tool-result-store-sql.d.ts +2 -2
- package/dist/plugins/tool-result-store-sql.js +3 -3
- package/dist/plugins/user-input-ledger-sql.js +2 -2
- package/dist/plugins/workflow-journal-store-sql.js +5 -5
- package/dist/plugins/workflow-run-store-sql.js +19 -19
- package/dist/plugins/write-behind-counter.d.ts +2 -3
- package/dist/plugins/write-behind-counter.js +5 -5
- package/dist/resource-suspend.d.ts +14 -12
- package/dist/resource-suspend.js +1 -1
- package/dist/resource-window.d.ts +4 -3
- package/dist/run-cancel-context.d.ts +34 -0
- package/dist/run-cancel-context.js +7 -0
- package/dist/run-local.js +14 -19
- package/dist/runs.d.ts +47 -16
- package/dist/runs.js +112 -113
- package/dist/security.d.ts +2 -2
- package/dist/server-secret-env.d.ts +2 -1
- package/dist/server-secret-env.js +2 -4
- package/dist/session-error-wire.d.ts +22 -4
- package/dist/session-error-wire.js +16 -0
- package/dist/session-incarnation.d.ts +10 -4
- package/dist/session-incarnation.js +1 -1
- package/dist/session-policy-wire.d.ts +1 -1
- 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/sql-engine-posture.d.ts +1 -1
- package/dist/sql-engine-posture.js +1 -1
- 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/{plugins → stores-sql}/background-agent-store-sql.d.ts +2 -2
- package/dist/{plugins → stores-sql}/background-agent-store-sql.js +8 -8
- package/dist/{plugins → stores-sql}/session-policy-store-sql.d.ts +7 -5
- package/dist/{plugins → stores-sql}/session-policy-store-sql.js +12 -12
- package/dist/{plugins → stores-sql}/usage-window-store-sql.d.ts +1 -1
- package/dist/{plugins → stores-sql}/usage-window-store-sql.js +3 -3
- package/dist/task-a2a.d.ts +1 -1
- package/dist/task-a2a.js +5 -6
- package/dist/task-mcp.d.ts +21 -8
- package/dist/task-mcp.js +17 -9
- package/dist/task-settings.d.ts +52 -25
- package/dist/task-settings.js +91 -28
- package/dist/terminal.d.ts +103 -19
- package/dist/terminal.js +72 -20
- package/dist/trace/core-keyset-guard.d.ts +79 -2
- package/dist/trace/exit-seat.d.ts +7 -0
- package/dist/trace/exit-seat.js +1 -0
- package/dist/trace/model-segment.d.ts +2 -2
- package/dist/trace/redact.d.ts +3 -1
- package/dist/trace/redact.js +395 -110
- package/dist/trace/self-minted-wire.d.ts +226 -2
- package/dist/trace/self-minted-wire.js +79 -0
- 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 +2 -2
- package/dist/web-admin-client/candidate-gate.d.ts +11 -7
- package/dist/web-admin-client/candidate-gate.js +1 -1
- package/dist/web-admin-client/http-client.d.ts +5 -4
- package/dist/web-admin-client/http-client.js +2 -2
- package/dist/web-admin-client/read-warnings.d.ts +7 -17
- package/dist/web-admin-client/read-warnings.js +5 -9
- package/docs/DEVICE-EXECUTOR-PROTOCOL.md +3 -2
- package/package.json +2 -2
- package/wire-changes/server@7.140.0.json +184 -0
- package/wire-changes/server@7.141.0.json +367 -0
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
2
|
import { Sandbox, CommandExitError, FileType } from "e2b";
|
|
3
3
|
import { FileError, ExecutionError, RemoteExecutionError, RollingTailBuffer, markTruncated, } from "@sema-agent/core";
|
|
4
|
+
import { capabilitiesFor } from "../execution-lane-caps.js";
|
|
5
|
+
import { ADAPTER_START, BOOTSTRAP_START, createSandboxReadFloor, e2bFloorCommand, engineStart, } from "./sandbox-read-floor.js";
|
|
6
|
+
import { LANE_HOME_PROBE_COMMAND, LaneHomeSeat } from "../lane-home-probe.js";
|
|
4
7
|
import { fileErrorFromExec } from "./remote-env-file-error.js";
|
|
5
8
|
import { appendFileStaged, rewriteAppend, numEnvOr, runSymlinkChain, shellQuote, writeFileStaged } from "./remote-shell.js";
|
|
6
9
|
import { buildLivenessCut, commandBoundsOf, firstBoundOf, laneLivenessOf, LIVENESS_MS_DEFAULT } from "../exec-bounds.js";
|
|
@@ -19,7 +22,9 @@ const E2B_BG_PROVIDER_BACKSTOP_PAD_SEC = 30;
|
|
|
19
22
|
const ok = (value) => ({ ok: true, value });
|
|
20
23
|
export class RemoteContainerExecutionEnv {
|
|
21
24
|
cwd;
|
|
22
|
-
homeDir
|
|
25
|
+
get homeDir() {
|
|
26
|
+
return this.homeSeat.home;
|
|
27
|
+
}
|
|
23
28
|
cdPath = "unknown";
|
|
24
29
|
withheldEnv = [];
|
|
25
30
|
cfg;
|
|
@@ -29,6 +34,8 @@ export class RemoteContainerExecutionEnv {
|
|
|
29
34
|
keepAlive;
|
|
30
35
|
handle;
|
|
31
36
|
destroyed = false;
|
|
37
|
+
homeSeat;
|
|
38
|
+
homeRefusal;
|
|
32
39
|
connecting;
|
|
33
40
|
activeCommands = 0;
|
|
34
41
|
paused = false;
|
|
@@ -44,10 +51,92 @@ export class RemoteContainerExecutionEnv {
|
|
|
44
51
|
};
|
|
45
52
|
this.laneLiveness = laneLivenessOf(this.cfg.livenessMs, "E2B_LIVENESS_MS");
|
|
46
53
|
this.cwd = this.cfg.mountPath;
|
|
47
|
-
this.
|
|
54
|
+
this.homeSeat = new LaneHomeSeat(PROVIDER, config);
|
|
48
55
|
this.sandboxApi = deps.sandboxApi ?? Sandbox;
|
|
56
|
+
this.floor = deps.readFloor ?? createSandboxReadFloor({ lane: PROVIDER, knob: config.sandboxReadFloor, ...(config.logger !== undefined ? { logger: config.logger } : {}) });
|
|
57
|
+
this.capabilities = capabilitiesFor({ remoteExec: { provider: PROVIDER }, sandboxReadFloor: config.sandboxReadFloor });
|
|
58
|
+
}
|
|
59
|
+
capabilities;
|
|
60
|
+
floor;
|
|
61
|
+
startIn(start, command, envs) {
|
|
62
|
+
const launch = this.floor.wrap(start);
|
|
63
|
+
return launch.ok ? ok(e2bFloorCommand(launch.value, command, envs)) : launch;
|
|
64
|
+
}
|
|
65
|
+
runIn(sbx, s, opts = {}) {
|
|
66
|
+
const { background, ...rest } = opts;
|
|
67
|
+
const o = { ...rest, ...(s.user !== undefined ? { user: s.user } : {}), ...(s.envs !== undefined ? { envs: s.envs } : {}) };
|
|
68
|
+
return background ? sbx.commands.run(s.cmd, { ...o, background: true }) : sbx.commands.run(s.cmd, o);
|
|
69
|
+
}
|
|
70
|
+
floorTransport(sbx) {
|
|
71
|
+
const bootstrap = async (command) => {
|
|
72
|
+
const s = this.startIn(BOOTSTRAP_START, command, undefined);
|
|
73
|
+
if (!s.ok)
|
|
74
|
+
return { ok: false, error: s.error.message };
|
|
75
|
+
try {
|
|
76
|
+
const r = await this.rpc("floor.bootstrap", (signal) => this.runIn(sbx, s.value, { signal, timeoutMs: this.cfg.rpcTimeoutMs }));
|
|
77
|
+
return { ok: true, exitCode: r.exitCode, stdout: r.stdout, stderr: r.stderr };
|
|
78
|
+
}
|
|
79
|
+
catch (e) {
|
|
80
|
+
if (e instanceof CommandExitError)
|
|
81
|
+
return { ok: true, exitCode: e.exitCode, stdout: e.stdout, stderr: e.stderr };
|
|
82
|
+
return { ok: false, error: errMsg(e) };
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
return {
|
|
86
|
+
bootstrap,
|
|
87
|
+
place: async (dest, bytes) => {
|
|
88
|
+
const dir = path.posix.dirname(dest);
|
|
89
|
+
const tmp = `${dest}.${Math.random().toString(16).slice(2, 10)}`;
|
|
90
|
+
const mk = await bootstrap(`umask 022 && mkdir -p ${shellQuote(dir)}`);
|
|
91
|
+
if (!mk.ok || mk.exitCode !== 0)
|
|
92
|
+
return { ok: false, error: mk.ok ? mk.stderr.trim() || `exit ${mk.exitCode}` : mk.error };
|
|
93
|
+
try {
|
|
94
|
+
const ab = bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength);
|
|
95
|
+
await this.rpc("files.write", (signal) => sbx.files.write(tmp, ab, { user: "root", signal }), undefined, this.cfg.dataTimeoutMs);
|
|
96
|
+
}
|
|
97
|
+
catch (e) {
|
|
98
|
+
return { ok: false, error: errMsg(e) };
|
|
99
|
+
}
|
|
100
|
+
const mv = await bootstrap(`chmod 0755 ${shellQuote(tmp)} && mv -f ${shellQuote(tmp)} ${shellQuote(dest)}`);
|
|
101
|
+
return mv.ok && mv.exitCode === 0 ? { ok: true } : { ok: false, error: mv.ok ? mv.stderr.trim() || `exit ${mv.exitCode}` : mv.error };
|
|
102
|
+
},
|
|
103
|
+
openRelay: (identity, command, events) => {
|
|
104
|
+
let handle;
|
|
105
|
+
let closed = false;
|
|
106
|
+
const pending = [];
|
|
107
|
+
const user = (() => {
|
|
108
|
+
switch (identity) {
|
|
109
|
+
case "root":
|
|
110
|
+
return { user: "root" };
|
|
111
|
+
case "exec-user":
|
|
112
|
+
return {};
|
|
113
|
+
}
|
|
114
|
+
})();
|
|
115
|
+
sbx.commands
|
|
116
|
+
.run(command, { background: true, stdin: true, timeoutMs: 0, ...user, onStdout: (d) => events.data(d) })
|
|
117
|
+
.then((h) => {
|
|
118
|
+
handle = h;
|
|
119
|
+
if (closed)
|
|
120
|
+
return void h.kill().catch(() => { });
|
|
121
|
+
for (const line of pending.splice(0))
|
|
122
|
+
void h.sendStdin(line).catch(() => { });
|
|
123
|
+
h.wait().then(() => events.closed("relay exited"), (e) => events.closed(`relay exited: ${errMsg(e)}`));
|
|
124
|
+
}, (e) => events.closed(`relay start failed: ${errMsg(e)}`));
|
|
125
|
+
return {
|
|
126
|
+
send: (line) => {
|
|
127
|
+
if (handle !== undefined)
|
|
128
|
+
void handle.sendStdin(line).catch(() => { });
|
|
129
|
+
else
|
|
130
|
+
pending.push(line);
|
|
131
|
+
},
|
|
132
|
+
close: () => {
|
|
133
|
+
closed = true;
|
|
134
|
+
void handle?.kill().catch(() => { });
|
|
135
|
+
},
|
|
136
|
+
};
|
|
137
|
+
},
|
|
138
|
+
};
|
|
49
139
|
}
|
|
50
|
-
capabilities = { isolation: true, suspendable: true, canonicalPathAuthoritative: true, sharesProcessTable: false, hostLocalPaths: false, externalContentTarget: false, io: true, keepsReadFloor: false };
|
|
51
140
|
workspaceHandle() {
|
|
52
141
|
if (!this.handle) {
|
|
53
142
|
throw new RemoteExecutionError("connect_failed", "workspaceHandle() called before connect()");
|
|
@@ -60,8 +149,12 @@ export class RemoteContainerExecutionEnv {
|
|
|
60
149
|
}
|
|
61
150
|
async startBackground(command) {
|
|
62
151
|
const conn = await this.ensureConnected();
|
|
63
|
-
if (conn.ok)
|
|
64
|
-
|
|
152
|
+
if (!conn.ok)
|
|
153
|
+
return;
|
|
154
|
+
const s = this.startIn(engineStart(undefined), command, undefined);
|
|
155
|
+
if (!s.ok)
|
|
156
|
+
return void this.cfg.logger?.warn?.("remote_env_background_refused", { error: s.error.message });
|
|
157
|
+
await this.runIn(conn.value, s.value, { background: true });
|
|
65
158
|
}
|
|
66
159
|
async connect(config) {
|
|
67
160
|
if (config?.snapshotId) {
|
|
@@ -69,6 +162,8 @@ export class RemoteContainerExecutionEnv {
|
|
|
69
162
|
}
|
|
70
163
|
if (this.destroyed)
|
|
71
164
|
return { ok: false, error: new RemoteExecutionError("connect_failed", "execution env already destroyed") };
|
|
165
|
+
if (this.homeRefusal)
|
|
166
|
+
return { ok: false, error: this.homeRefusal };
|
|
72
167
|
if (this.paused)
|
|
73
168
|
return { ok: false, error: new RemoteExecutionError("connect_failed", "execution env is suspended (paused) — resumeVM(snapshotId) before use") };
|
|
74
169
|
if (this.sbx)
|
|
@@ -95,6 +190,17 @@ export class RemoteContainerExecutionEnv {
|
|
|
95
190
|
await sbx.kill().catch(() => { });
|
|
96
191
|
return { ok: false, error: new RemoteExecutionError("connect_failed", "execution env destroyed during connect") };
|
|
97
192
|
}
|
|
193
|
+
const home = await this.homeVerdict(sbx, config?.abortSignal);
|
|
194
|
+
if (home !== undefined || this.destroyed) {
|
|
195
|
+
await sbx.kill().catch(() => { });
|
|
196
|
+
this.cfg.metrics?.inc("remote_env_connect_total", { provider: PROVIDER, result: "error" });
|
|
197
|
+
return { ok: false, error: home ?? new RemoteExecutionError("connect_failed", "execution env destroyed during connect") };
|
|
198
|
+
}
|
|
199
|
+
await this.floor.attach(this.floorTransport(sbx), { origin: "created", workspaceRoot: this.cfg.mountPath });
|
|
200
|
+
if (this.destroyed) {
|
|
201
|
+
await sbx.kill().catch(() => { });
|
|
202
|
+
return { ok: false, error: new RemoteExecutionError("connect_failed", "execution env destroyed during connect") };
|
|
203
|
+
}
|
|
98
204
|
this.sbx = sbx;
|
|
99
205
|
this.paused = false;
|
|
100
206
|
this.startKeepAlive();
|
|
@@ -147,6 +253,8 @@ export class RemoteContainerExecutionEnv {
|
|
|
147
253
|
async resumeVM(snapshotId, options) {
|
|
148
254
|
if (this.destroyed)
|
|
149
255
|
return { ok: false, error: new RemoteExecutionError("connect_failed", "env already destroyed") };
|
|
256
|
+
if (this.homeRefusal)
|
|
257
|
+
return { ok: false, error: this.homeRefusal };
|
|
150
258
|
if (this.sbx && !this.paused && this.sbx.sandboxId === snapshotId)
|
|
151
259
|
return ok(this.workspaceHandle());
|
|
152
260
|
if (this.connecting)
|
|
@@ -164,11 +272,26 @@ export class RemoteContainerExecutionEnv {
|
|
|
164
272
|
await sbx.kill().catch(() => { });
|
|
165
273
|
return { ok: false, error: new RemoteExecutionError("connect_failed", "execution env destroyed during resume") };
|
|
166
274
|
}
|
|
275
|
+
const restoredRoot = options?.priorHandle?.mountPath ?? this.cfg.mountPath;
|
|
276
|
+
await this.floor.attach(this.floorTransport(sbx), { origin: "resumed", workspaceRoot: restoredRoot });
|
|
277
|
+
if (this.destroyed) {
|
|
278
|
+
await sbx.kill().catch(() => { });
|
|
279
|
+
return { ok: false, error: new RemoteExecutionError("connect_failed", "execution env destroyed during resume") };
|
|
280
|
+
}
|
|
281
|
+
const home = await this.homeVerdict(sbx, options?.abortSignal);
|
|
282
|
+
if (this.destroyed) {
|
|
283
|
+
await sbx.kill().catch(() => { });
|
|
284
|
+
return { ok: false, error: new RemoteExecutionError("connect_failed", "execution env destroyed during resume") };
|
|
285
|
+
}
|
|
286
|
+
if (home !== undefined) {
|
|
287
|
+
await this.rpc("sandbox.pause", (signal) => sbx.pause({ signal }), undefined, this.cfg.dataTimeoutMs).catch((e) => this.cfg.logger?.warn?.("remote_env_home_refused_repause_failed", { sandboxId: sbx.sandboxId, error: errMsg(e) }));
|
|
288
|
+
this.cfg.metrics?.inc("remote_env_resume_total", { provider: PROVIDER, result: "error" });
|
|
289
|
+
return { ok: false, error: home };
|
|
290
|
+
}
|
|
167
291
|
this.sbx = sbx;
|
|
168
292
|
this.paused = false;
|
|
169
293
|
this.startKeepAlive();
|
|
170
294
|
this.cfg.logger?.info?.("remote_env_resume", { requested: snapshotId, connected: sbx.sandboxId });
|
|
171
|
-
const restoredRoot = options?.priorHandle?.mountPath ?? this.cfg.mountPath;
|
|
172
295
|
this.cwd = restoredRoot;
|
|
173
296
|
this.handle = { sandboxId: sbx.sandboxId, provider: PROVIDER, mountPath: restoredRoot, snapshotId, sessionToken: sbx.sandboxId };
|
|
174
297
|
this.cfg.metrics?.inc("remote_env_resume_total", { provider: PROVIDER, result: "ok" });
|
|
@@ -196,6 +319,30 @@ export class RemoteContainerExecutionEnv {
|
|
|
196
319
|
return { ok: false, error: this.remoteError(e, "post_resume_failed") };
|
|
197
320
|
}
|
|
198
321
|
}
|
|
322
|
+
async homeVerdict(sbx, signal) {
|
|
323
|
+
if (!this.homeSeat.asks)
|
|
324
|
+
return undefined;
|
|
325
|
+
let probe;
|
|
326
|
+
try {
|
|
327
|
+
const start = this.startIn(ADAPTER_START, LANE_HOME_PROBE_COMMAND, undefined);
|
|
328
|
+
if (!start.ok)
|
|
329
|
+
throw start.error;
|
|
330
|
+
const r = await this.rpc("sandbox.home", (s) => this.runIn(sbx, start.value, { signal: s }), signal);
|
|
331
|
+
probe = r.exitCode === 0 ? { ok: true, stdout: r.stdout } : { ok: false, reason: `exit ${r.exitCode}` };
|
|
332
|
+
}
|
|
333
|
+
catch (e) {
|
|
334
|
+
probe = { ok: false, reason: e instanceof CommandExitError ? `exit ${e.exitCode}` : errMsg(e) };
|
|
335
|
+
}
|
|
336
|
+
if (signal?.aborted)
|
|
337
|
+
return new RemoteExecutionError("aborted", "connect aborted while reading the sandbox's home");
|
|
338
|
+
const verdict = this.homeSeat.settle(probe);
|
|
339
|
+
if (verdict === undefined)
|
|
340
|
+
return undefined;
|
|
341
|
+
const error = new RemoteExecutionError("connect_failed", verdict.refusal);
|
|
342
|
+
if (verdict.answered)
|
|
343
|
+
this.homeRefusal = error;
|
|
344
|
+
return error;
|
|
345
|
+
}
|
|
199
346
|
startKeepAlive() {
|
|
200
347
|
this.stopKeepAlive();
|
|
201
348
|
const everyMs = Math.max(30_000, Math.floor(this.cfg.timeoutMs * 0.8));
|
|
@@ -218,6 +365,7 @@ export class RemoteContainerExecutionEnv {
|
|
|
218
365
|
this.destroyed = true;
|
|
219
366
|
this.connecting = undefined;
|
|
220
367
|
this.stopKeepAlive();
|
|
368
|
+
this.floor.dispose();
|
|
221
369
|
await this.disposeBackgroundShells();
|
|
222
370
|
const sbx = this.sbx;
|
|
223
371
|
this.sbx = undefined;
|
|
@@ -238,12 +386,21 @@ export class RemoteContainerExecutionEnv {
|
|
|
238
386
|
await this.destroy();
|
|
239
387
|
}
|
|
240
388
|
async exec(command, options) {
|
|
389
|
+
return this.execAs(engineStart(options?.readFloor), command, options);
|
|
390
|
+
}
|
|
391
|
+
execInternal(command, options) {
|
|
392
|
+
return this.execAs(ADAPTER_START, command, options);
|
|
393
|
+
}
|
|
394
|
+
async execAs(start, command, options) {
|
|
241
395
|
if (options?.abortSignal?.aborted)
|
|
242
396
|
return { ok: false, error: new ExecutionError("aborted", "exec aborted before start") };
|
|
243
397
|
const conn = await this.ensureConnected();
|
|
244
398
|
if (!conn.ok)
|
|
245
399
|
return { ok: false, error: new ExecutionError("shell_unavailable", `remote connect failed: ${conn.error.message}`, conn.error) };
|
|
246
400
|
const sbx = conn.value;
|
|
401
|
+
const launch = this.startIn(start, command, options?.env);
|
|
402
|
+
if (!launch.ok)
|
|
403
|
+
return launch;
|
|
247
404
|
this.activeCommands++;
|
|
248
405
|
const detachSignal = options?.detachSignal;
|
|
249
406
|
const tee = detachSignal
|
|
@@ -268,10 +425,9 @@ export class RemoteContainerExecutionEnv {
|
|
|
268
425
|
let starting;
|
|
269
426
|
let startCut = false;
|
|
270
427
|
try {
|
|
271
|
-
handle = await this.rpc("commands.run", () => (starting =
|
|
428
|
+
handle = await this.rpc("commands.run", () => (starting = this.runIn(sbx, launch.value, {
|
|
272
429
|
background: true,
|
|
273
430
|
cwd: this.resolve(options?.cwd ?? this.cwd),
|
|
274
|
-
...(options?.env ? { envs: options.env } : {}),
|
|
275
431
|
timeoutMs: tee
|
|
276
432
|
? Math.max(this.commandTimeoutMs(options?.timeout), (E2B_BG_MAX_TIMEOUT_SEC + E2B_BG_PROVIDER_BACKSTOP_PAD_SEC) * 1000)
|
|
277
433
|
: this.commandTimeoutMs(options?.timeout),
|
|
@@ -395,6 +551,9 @@ export class RemoteContainerExecutionEnv {
|
|
|
395
551
|
if (!conn.ok)
|
|
396
552
|
throw conn.error;
|
|
397
553
|
const sbx = conn.value;
|
|
554
|
+
const launch = this.startIn(engineStart(undefined), command, options?.env);
|
|
555
|
+
if (!launch.ok)
|
|
556
|
+
throw new RemoteExecutionError("unsupported", launch.error.message, launch.error);
|
|
398
557
|
const queue = [];
|
|
399
558
|
let finished = false;
|
|
400
559
|
let failure;
|
|
@@ -421,10 +580,9 @@ export class RemoteContainerExecutionEnv {
|
|
|
421
580
|
let handle;
|
|
422
581
|
let starting;
|
|
423
582
|
try {
|
|
424
|
-
handle = await this.rpc("commands.run", () => (starting =
|
|
583
|
+
handle = await this.rpc("commands.run", () => (starting = this.runIn(sbx, launch.value, {
|
|
425
584
|
background: true,
|
|
426
585
|
cwd: this.resolve(options?.cwd ?? this.cwd),
|
|
427
|
-
...(options?.env ? { envs: options.env } : {}),
|
|
428
586
|
timeoutMs: this.commandTimeoutMs(options?.timeout),
|
|
429
587
|
onStdout: (d) => push("stdout", d),
|
|
430
588
|
onStderr: (d) => push("stderr", d),
|
|
@@ -552,12 +710,14 @@ export class RemoteContainerExecutionEnv {
|
|
|
552
710
|
if (!conn.ok)
|
|
553
711
|
throw new Error(`remote connect failed: ${conn.error.message}`);
|
|
554
712
|
const sbx = conn.value;
|
|
713
|
+
const launch = self.startIn(engineStart(options?.readFloor), command, options?.env);
|
|
714
|
+
if (!launch.ok)
|
|
715
|
+
throw launch.error;
|
|
555
716
|
const stdout = { tail: new RollingTailBuffer(), total: 0, cursor: 0 };
|
|
556
717
|
const stderr = { tail: new RollingTailBuffer(), total: 0, cursor: 0 };
|
|
557
|
-
const handle = await self.rpc("commands.run.bg", () =>
|
|
718
|
+
const handle = await self.rpc("commands.run.bg", () => self.runIn(sbx, launch.value, {
|
|
558
719
|
background: true,
|
|
559
720
|
cwd: self.resolve(options?.cwd ?? self.cwd),
|
|
560
|
-
...(options?.env ? { envs: options.env } : {}),
|
|
561
721
|
timeoutMs: (ctx.bgTimeoutSec + E2B_BG_PROVIDER_BACKSTOP_PAD_SEC) * 1000,
|
|
562
722
|
onStdout: (d) => {
|
|
563
723
|
const b = Buffer.from(d, "utf8");
|
|
@@ -675,7 +835,7 @@ export class RemoteContainerExecutionEnv {
|
|
|
675
835
|
return { ok: false, error: this.fileError(e, dest, signal) };
|
|
676
836
|
}
|
|
677
837
|
},
|
|
678
|
-
run: (cmd, signal) => this.
|
|
838
|
+
run: (cmd, signal) => this.execInternal(cmd, { cwd: "/", ...(signal !== undefined ? { abortSignal: signal } : {}) }),
|
|
679
839
|
};
|
|
680
840
|
}
|
|
681
841
|
async writeFile(p, content, abortSignal) {
|
|
@@ -717,7 +877,7 @@ export class RemoteContainerExecutionEnv {
|
|
|
717
877
|
}
|
|
718
878
|
async readLink(p, abortSignal) {
|
|
719
879
|
const abs = this.resolve(p);
|
|
720
|
-
const r = await this.
|
|
880
|
+
const r = await this.execInternal(`readlink -- ${shellQuote(abs)}`, { abortSignal });
|
|
721
881
|
if (!r.ok)
|
|
722
882
|
return { ok: false, error: fileErrorFromExec(r.error, abs) };
|
|
723
883
|
if (r.value.exitCode !== 0)
|
|
@@ -725,11 +885,11 @@ export class RemoteContainerExecutionEnv {
|
|
|
725
885
|
return ok(r.value.stdout.trim());
|
|
726
886
|
}
|
|
727
887
|
async canonicalChain(p, abortSignal) {
|
|
728
|
-
return runSymlinkChain(this.resolve(p), (cmd) => this.
|
|
888
|
+
return runSymlinkChain(this.resolve(p), (cmd) => this.execInternal(cmd, { abortSignal }));
|
|
729
889
|
}
|
|
730
890
|
async canonicalPath(p, abortSignal) {
|
|
731
891
|
const abs = this.resolve(p);
|
|
732
|
-
const r = await this.
|
|
892
|
+
const r = await this.execInternal(`realpath -- ${shellQuote(abs)}`, { abortSignal });
|
|
733
893
|
if (!r.ok)
|
|
734
894
|
return { ok: false, error: fileErrorFromExec(r.error, abs) };
|
|
735
895
|
if (r.value.exitCode !== 0) {
|
|
@@ -776,7 +936,7 @@ export class RemoteContainerExecutionEnv {
|
|
|
776
936
|
const abs = this.resolve(p);
|
|
777
937
|
if (options?.recursive) {
|
|
778
938
|
const flags = options.force ? "-rf" : "-r";
|
|
779
|
-
const r = await this.
|
|
939
|
+
const r = await this.execInternal(`rm ${flags} -- ${shellQuote(abs)}`, { abortSignal: options.abortSignal });
|
|
780
940
|
if (!r.ok)
|
|
781
941
|
return { ok: false, error: fileErrorFromExec(r.error, abs) };
|
|
782
942
|
if (r.value.exitCode !== 0)
|
|
@@ -795,14 +955,14 @@ export class RemoteContainerExecutionEnv {
|
|
|
795
955
|
}
|
|
796
956
|
}
|
|
797
957
|
async createTempDir(prefix = "tmp-", abortSignal) {
|
|
798
|
-
const r = await this.
|
|
958
|
+
const r = await this.execInternal(`mktemp -d -t ${shellQuote(`${prefix}XXXXXX`)}`, { abortSignal });
|
|
799
959
|
return this.tempResult(r, "mktemp -d");
|
|
800
960
|
}
|
|
801
961
|
async createTempFile(options) {
|
|
802
962
|
const prefix = options?.prefix ?? "";
|
|
803
963
|
const suffix = options?.suffix ?? "";
|
|
804
964
|
const suffixArg = suffix ? `--suffix=${shellQuote(suffix)} -t` : "-t";
|
|
805
|
-
const r = await this.
|
|
965
|
+
const r = await this.execInternal(`mktemp ${suffixArg} ${shellQuote(`${prefix}XXXXXX`)}`, { abortSignal: options?.abortSignal });
|
|
806
966
|
return this.tempResult(r, "mktemp");
|
|
807
967
|
}
|
|
808
968
|
resolve(p) {
|
|
@@ -905,7 +1065,7 @@ export class RemoteContainerExecutionEnv {
|
|
|
905
1065
|
async fsReady(p) {
|
|
906
1066
|
const conn = await this.ensureConnected();
|
|
907
1067
|
if (!conn.ok)
|
|
908
|
-
return { ok: false, error:
|
|
1068
|
+
return { ok: false, error: fileErrorFromExec(conn.error, this.resolve(p)) };
|
|
909
1069
|
return ok(conn.value);
|
|
910
1070
|
}
|
|
911
1071
|
tempResult(r, op) {
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
* 那条教训:宣布"类修完成"而三个实现只落了一个)。收成一处之后,判据只有一条 ——
|
|
16
16
|
* **文件面的 exec 包装必须走这里**,而这条能被机器检查(见 `test/env-adapter-file-error-parity.test.ts`)。
|
|
17
17
|
*/
|
|
18
|
-
import { FileError,
|
|
18
|
+
import { FileError, RemoteExecutionError, type ExecutionError } from "@sema-agent/core";
|
|
19
19
|
/** exec 层的错误码 → 文件层的码。只映射**有对应语义**的那些,其余落 `unknown`(诚实降级)。 */
|
|
20
20
|
export declare function fileErrorFromExec(e: ExecutionError | RemoteExecutionError, path?: string): FileError;
|
|
21
21
|
/** POSIX 的一个名字段上限(NAME_MAX,字节;Linux / macOS / 常见文件系统同值)。也是远端写的私有暂存目录名的上限(`remote-shell.ts` `buildStagingPaths`)。 */
|
|
@@ -1,6 +1,12 @@
|
|
|
1
|
-
import { FileError } from "@sema-agent/core";
|
|
1
|
+
import { FileError, RemoteExecutionError } from "@sema-agent/core";
|
|
2
|
+
function neverOpened(e) {
|
|
3
|
+
if (e.code === "connect_failed")
|
|
4
|
+
return true;
|
|
5
|
+
const cause = e.cause;
|
|
6
|
+
return cause instanceof RemoteExecutionError && cause.code === "connect_failed";
|
|
7
|
+
}
|
|
2
8
|
export function fileErrorFromExec(e, path) {
|
|
3
|
-
const code = e.code === "aborted" ? "aborted" : "unknown";
|
|
9
|
+
const code = e.code === "aborted" ? "aborted" : neverOpened(e) ? "target_unavailable" : "unknown";
|
|
4
10
|
return new FileError(code, e.message, path, e);
|
|
5
11
|
}
|
|
6
12
|
export const NAME_MAX_BYTES = 255;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { FileError, type InheritEnvPolicy, ExecutionError, RemoteExecutionError, SchedulerError, type SchedulerCapability, type ScheduledIntent, type SchedulerContext, type ScheduledTaskId, type ScheduledTaskSummary, type ExecutionEnv, type RemoteExecutionEnv, type WorkspaceHandle, type FileInfo, type Result, type SymlinkChain, type BoundFileObject, type OutputChunk, type ExecStreamOptions, type RemoteConnectConfig, type SnapshotId, type SessionToken, type VmLifecycleOptions, type ExecutionEnvFactory, BackgroundShellError, type BackgroundShellCapability, type BackgroundShellId, type BackgroundHardKillOutcome, type BackgroundPoll, type BackgroundSpawnOptions, type SecretEnvFinding } from "@sema-agent/core";
|
|
2
|
+
import { type LaneHomeConfig } from "../lane-home-probe.js";
|
|
2
3
|
/** Options accepted by `ExecutionEnv.exec` (core does not re-export the type; derive it from the seam). */
|
|
3
4
|
type ExecOpts = Parameters<ExecutionEnv["exec"]>[1];
|
|
4
5
|
/** SIGTERM → grace → SIGKILL (no host isolation). 🔴 宽限的**唯一属主**:本 driver 的 `kill` 梯子用它排升级定时器;会话级收敛
|
|
@@ -6,7 +7,7 @@ type ExecOpts = Parameters<ExecutionEnv["exec"]>[1];
|
|
|
6
7
|
* 这把梯子的用户面宽限取消),不另抄数。启动期读一次;`0` 被 `numEnvOr` 读作缺席(回落 1000),要几乎不等写 `1`。 */
|
|
7
8
|
export declare const HOST_BG_KILL_GRACE_MS: number;
|
|
8
9
|
/** Construction config for {@link RemoteHostExecutionEnv}. */
|
|
9
|
-
export interface HostEnvConfig {
|
|
10
|
+
export interface HostEnvConfig extends LaneHomeConfig {
|
|
10
11
|
/**
|
|
11
12
|
* Base directory under which this adapter creates its per-task working directory. Default {@link os.tmpdir}.
|
|
12
13
|
* The actual workspace is `<workspaceBase>/sema-host-<id>` and is `rm -rf`'d on {@link destroy}.
|
|
@@ -24,15 +25,6 @@ export interface HostEnvConfig {
|
|
|
24
25
|
* own `process.env` is the default base (a local coding agent inherits the user's environment).
|
|
25
26
|
*/
|
|
26
27
|
env?: Record<string, string>;
|
|
27
|
-
/**
|
|
28
|
-
* [ref]④ / core [ref] —— 本执行环境的 **home 目录**(`ExecutionEnv.homeDir` 座,绝对路径,本环境自己的
|
|
29
|
-
* 命名空间里)。`~/…` 形权限规则(`Edit(~/.ssh/**)`)对**跑在本 env 里的调用**就以它为基。
|
|
30
|
-
* 缺席 ⇒ 本 env 声明「我没有 home」,core 对 `~/` 规则判 `unreadable`(fail-closed 的 ask),
|
|
31
|
-
* 绝不回落引擎进程自己的 home(那在远端腿上是另一个用户的另一个目录 —— 静默守错目录正是 [ref] 的病)。
|
|
32
|
-
* 值由 `boot/execution-env.ts` 从**唯一属主** `execution-lane-caps.ts` 的 `executionLaneHomeDir` 递进来;
|
|
33
|
-
* adapter 不自算(两处各算一份 = 422 门与引擎对同一条规则给相反答案)。
|
|
34
|
-
*/
|
|
35
|
-
homeDir?: string;
|
|
36
28
|
/** Per-command wall-clock bound (ms) when the caller gives none. Default {@link DEFAULT_COMMAND_TIMEOUT_MS}. */
|
|
37
29
|
commandTimeoutMs?: number;
|
|
38
30
|
/** Shell-child env policy — core's `InheritEnvPolicy`, read by core's `inheritedHostEnv` (the three words are judged
|
|
@@ -64,29 +56,8 @@ export interface HostEnvConfig {
|
|
|
64
56
|
backgroundShell?: boolean;
|
|
65
57
|
}
|
|
66
58
|
export declare class RemoteHostExecutionEnv implements RemoteExecutionEnv, SchedulerCapability, BackgroundShellCapability {
|
|
67
|
-
/**
|
|
68
|
-
|
|
69
|
-
* 🔴 **[ref] / core 7.20.0([ref])`canonicalPathAuthoritative: true`** —— 本适配器的 `canonicalPath`
|
|
70
|
-
* 就是**命令真正跑的那块文件系统**的权威答案(worker 本机的 `fs.realpath`)。声明它 = 告诉引擎:整链答不出来是**解析器出了事**
|
|
71
|
-
* (RPC 断、传输掉),不是「一个它看不见的命名空间」⇒ 读边界的执行期复核**拒**,而不是让词法判决站着。
|
|
72
|
-
* 缺席是今天每个适配器的读法(整链沉默 ⇒ 词法判决成立、命令照跑),所以这一声明是**收紧**方向。
|
|
73
|
-
* 严格读法 `=== true`(core `canonicalPathIsAuthoritative`);`isSuspendable` 一族同样严格,对本仓的
|
|
74
|
-
* 布尔字面量零影响(四键都写死字面 `true`/`false`,不存在「非布尔值被读成缺席」那一形 —— 那一形由
|
|
75
|
-
* core 的 `config.execution_env_capability_invalid` 普查响亮播报,本仓的受众表已登记)。
|
|
76
|
-
* 谁该声明由闭集门看着(`test/execution-lane-caps.test.ts` 的逐 lane 对账:新 lane 不表态 = 编译红)。
|
|
77
|
-
*/
|
|
78
|
-
/** `sharesProcessTable`(core 7.57.0,必填):命令是本进程 spawn 的子进程(同一张进程表),引擎的模式杀守卫对它照 NodeExecutionEnv 一样问 `pgrep`。 */
|
|
79
|
-
/** 能力块(引擎 [ref],八键必填、各键只经引擎读器读):`hostLocalPaths` = 这条车道答出的路径是不是本进程所在机器上的文件;`externalContentTarget` = 产出是否来自部署信任边界之外;`io` = 文件与 shell 两面真到达目标;`keepsReadFloor` = 是否守住读拒表的字节地板(本车道不守 ⇒ 读拒表非空的 run 被告知,部署要求地板时准备期拒)。 */
|
|
80
|
-
readonly capabilities: {
|
|
81
|
-
isolation: false;
|
|
82
|
-
suspendable: false;
|
|
83
|
-
canonicalPathAuthoritative: true;
|
|
84
|
-
sharesProcessTable: true;
|
|
85
|
-
hostLocalPaths: true;
|
|
86
|
-
externalContentTarget: false;
|
|
87
|
-
io: true;
|
|
88
|
-
keepsReadFloor: false;
|
|
89
|
-
};
|
|
59
|
+
/** 能力块的唯一作者 = `capabilitiesFor`(`execution-lane-caps.ts`;逐键表态与理由在那里,本类零能力字面量)。 */
|
|
60
|
+
readonly capabilities: import("@sema-agent/core").ExecutionEnvCapabilities;
|
|
90
61
|
/** R7 自唤醒: structural delegation to the injected scheduler backend. `hasScheduler(env)` checks BOTH the method
|
|
91
62
|
* presence (always, since we implement the interface) AND `schedulerCapabilities.supported===true` — which is only
|
|
92
63
|
* true when a backend was injected, so a host env without one stays INERT (the four scheduler tools don't mount). */
|
|
@@ -98,8 +69,13 @@ export declare class RemoteHostExecutionEnv implements RemoteExecutionEnv, Sched
|
|
|
98
69
|
list(ctx: SchedulerContext): Promise<Result<ScheduledTaskSummary[], SchedulerError>>;
|
|
99
70
|
/** Working directory; relative paths resolve against it (ExecutionEnv contract). Starts at the workspace root. */
|
|
100
71
|
cwd: string;
|
|
101
|
-
/** core `ExecutionEnv.homeDir`([ref])
|
|
102
|
-
|
|
72
|
+
/** core `ExecutionEnv.homeDir`([ref]):本机执行用户的 home —— 主张在场 ⇒ 主张(构造时与子进程拿到的 `HOME` 对证过),否则构造时读到的那个 `HOME`,见 {@link LaneHomeSeat}。 */
|
|
73
|
+
get homeDir(): string | undefined;
|
|
74
|
+
/** 家目录的座(主张在场 ⇒ 构造时对证 · 否则构造时实测)。读数 = {@link childHome}:本类自己组合给子进程的那份 env,不 spawn。 */
|
|
75
|
+
private readonly homeSeat;
|
|
76
|
+
/** 家目录:主张与子进程拿到的 `HOME` 对不上 ⇒ 这只环境的拒句;`shellReady` / `fsReady` / `connect` 三道门原样回它,一条命令、一次文件动作都不做。
|
|
77
|
+
* 读数是本类自己组合的 env(纯函数,同一份输入同一个答案)⇒ 构造时判一次、记一辈子。 */
|
|
78
|
+
private readonly homeRefusal;
|
|
103
79
|
/**
|
|
104
80
|
* core 7.49.0 [ref]:shell 子进程**真拿到的** `CDPATH` 条目(`[]` = 没有)—— 删除守卫按它解裸 `cd <dir>`。本车道的命令就是本机子进程,
|
|
105
81
|
* env 是本类自己组合的(继承姿态 + `cfg.env`),所以说得出:读同一只组合({@link childEnv} 的**读面**,不披露、不钉 shell),切法与 core
|
|
@@ -454,6 +430,14 @@ export declare class RemoteHostExecutionEnv implements RemoteExecutionEnv, Sched
|
|
|
454
430
|
* 引擎对点到自有凭据名的白名单在这里**抛**(spawn 时的拒;构造器已先问过同一只判)—— 子进程不起,拒句带名。
|
|
455
431
|
*/
|
|
456
432
|
private childEnv;
|
|
433
|
+
/**
|
|
434
|
+
* 家目录的读数:本类组合给子进程的那份 env 里的 `HOME`(组合层,不 spawn、不记剥除 —— 与 {@link cdPath} 同一只读面)。env 里没有 `HOME` ⇒ 读
|
|
435
|
+
* 账户的家目录(`os.userInfo().homedir`):那正是子进程的 shell 对缺席 `HOME` 的 `~` 展开(POSIX 取账户 passwd 行;win32 是账户的配置目录)。
|
|
436
|
+
* 不读 `os.homedir()`:它先读**服务进程自己的** `HOME` —— 子进程没继承到的那个值(对抗轮 r1 [high] 实测:服务 `HOME` 指别处 + `inheritEnv: []` 时两者分叉)。
|
|
437
|
+
* 账户查不到(无 passwd 行)⇒ 读不出(有主张 ⇒ 不开;没主张 ⇒ 座缺席 + warn)。
|
|
438
|
+
* 残余(引擎 `NodeExecutionEnv.homeDir` 注同一句):POSIX 上 `HOME` 缺席时 shell 里的 `$HOME` 字面量展开为空,与 `~` 不是一个值 —— 本读数按 `~` 记。
|
|
439
|
+
*/
|
|
440
|
+
private childHome;
|
|
457
441
|
private fileError;
|
|
458
442
|
}
|
|
459
443
|
/**
|
|
@@ -12,6 +12,8 @@ import { hostBackgroundShellEnabled, hostExecSpoolEnabled } from "../config.js";
|
|
|
12
12
|
import { IS_WIN32, resolveHostShell, hostShell, pinHostShell, spawnGroupOptions, killTreeHard, killTreeSoft, hardKillGroupNow, collapseWin32EnvKeys, adoptHostForegroundChild } from "./host-platform.js";
|
|
13
13
|
import { BackgroundShellManager, seedMemStream, feedMemStream, drainMemStream } from "./background-shell-support.js";
|
|
14
14
|
import { FileError, NodeExecutionEnv, inheritEnvPolicyProblem, ExecutionError, RemoteExecutionError, RollingTailBuffer, walkSymlinkChain, markTruncated, SchedulerError, BackgroundShellError, } from "@sema-agent/core";
|
|
15
|
+
import { capabilitiesFor } from "../execution-lane-caps.js";
|
|
16
|
+
import { LaneHomeSeat } from "../lane-home-probe.js";
|
|
15
17
|
import { composeHostSubprocessEnv, hostSubprocessEnv, withheldFromHostSubprocess } from "../server-secret-env.js";
|
|
16
18
|
import { nameTooLongCode } from "./remote-env-file-error.js";
|
|
17
19
|
import { trackEphemeralHostWorkspace, untrackEphemeralHostWorkspace } from "./host-workspace-registry.js";
|
|
@@ -38,7 +40,7 @@ const UNSUPPORTED_SCHEDULER_CAPS = {
|
|
|
38
40
|
maxScheduledHorizonSec: 0,
|
|
39
41
|
};
|
|
40
42
|
export class RemoteHostExecutionEnv {
|
|
41
|
-
capabilities = {
|
|
43
|
+
capabilities = capabilitiesFor({ remoteExec: { provider: "host" } });
|
|
42
44
|
get schedulerCapabilities() {
|
|
43
45
|
return this.cfg.scheduler?.schedulerCapabilities ?? UNSUPPORTED_SCHEDULER_CAPS;
|
|
44
46
|
}
|
|
@@ -58,7 +60,11 @@ export class RemoteHostExecutionEnv {
|
|
|
58
60
|
: Promise.resolve({ ok: false, error: new SchedulerError("unsupported", "scheduler not enabled on this worker") });
|
|
59
61
|
}
|
|
60
62
|
cwd;
|
|
61
|
-
homeDir
|
|
63
|
+
get homeDir() {
|
|
64
|
+
return this.homeSeat.home;
|
|
65
|
+
}
|
|
66
|
+
homeSeat;
|
|
67
|
+
homeRefusal;
|
|
62
68
|
get cdPath() {
|
|
63
69
|
const value = this.childEnv(undefined, undefined).CDPATH;
|
|
64
70
|
return value === undefined || value === "" ? [] : value.split(":");
|
|
@@ -140,8 +146,9 @@ export class RemoteHostExecutionEnv {
|
|
|
140
146
|
if (Array.isArray(config.inheritEnv))
|
|
141
147
|
this.cfg.inheritEnv = [...config.inheritEnv];
|
|
142
148
|
this.cwd = this.workspaceDir;
|
|
143
|
-
this.homeDir = config.homeDir;
|
|
144
149
|
this.handle = { sandboxId: this.workspaceDir, provider: PROVIDER, mountPath: this.workspaceDir };
|
|
150
|
+
this.homeSeat = new LaneHomeSeat(PROVIDER, config, IS_WIN32 ? "win32" : "posix");
|
|
151
|
+
this.homeRefusal = this.homeSeat.settle(this.childHome())?.refusal;
|
|
145
152
|
}
|
|
146
153
|
workspaceHandle() {
|
|
147
154
|
return this.handle;
|
|
@@ -151,6 +158,8 @@ export class RemoteHostExecutionEnv {
|
|
|
151
158
|
return unsupported("connect(snapshotId)");
|
|
152
159
|
if (this.destroyed)
|
|
153
160
|
return { ok: false, error: new RemoteExecutionError("connect_failed", "execution env already destroyed") };
|
|
161
|
+
if (this.homeRefusal !== undefined)
|
|
162
|
+
return { ok: false, error: new RemoteExecutionError("connect_failed", this.homeRefusal) };
|
|
154
163
|
if (config?.abortSignal?.aborted)
|
|
155
164
|
return { ok: false, error: new RemoteExecutionError("aborted", "connect aborted") };
|
|
156
165
|
try {
|
|
@@ -1253,6 +1262,8 @@ export class RemoteHostExecutionEnv {
|
|
|
1253
1262
|
async shellReady() {
|
|
1254
1263
|
if (this.destroyed)
|
|
1255
1264
|
return { ok: false, error: new ExecutionError("shell_unavailable", "execution env already destroyed") };
|
|
1265
|
+
if (this.homeRefusal !== undefined)
|
|
1266
|
+
return { ok: false, error: new ExecutionError("shell_unavailable", this.homeRefusal) };
|
|
1256
1267
|
try {
|
|
1257
1268
|
try {
|
|
1258
1269
|
await resolveHostShell();
|
|
@@ -1284,6 +1295,8 @@ export class RemoteHostExecutionEnv {
|
|
|
1284
1295
|
return (this.spoolRootDir !== undefined && inside(this.spoolRootDir)) || (this.persistent && inside(this.workspaceDir));
|
|
1285
1296
|
}
|
|
1286
1297
|
async fsReady(p, opts) {
|
|
1298
|
+
if (this.homeRefusal !== undefined)
|
|
1299
|
+
return { ok: false, error: new FileError("target_unavailable", this.homeRefusal, this.resolve(p)) };
|
|
1287
1300
|
if (this.destroyed) {
|
|
1288
1301
|
const lingering = opts?.lingeringBgWrite === true && this._bgManager?.hasShells() === true && this.isLingeringMirrorTarget(p);
|
|
1289
1302
|
if (!lingering)
|
|
@@ -1313,6 +1326,16 @@ export class RemoteHostExecutionEnv {
|
|
|
1313
1326
|
pinHostShell(base, spawnShell);
|
|
1314
1327
|
return collapseWin32EnvKeys(base);
|
|
1315
1328
|
}
|
|
1329
|
+
childHome() {
|
|
1330
|
+
try {
|
|
1331
|
+
const env = this.childEnv(undefined, undefined);
|
|
1332
|
+
const home = Object.entries(env).find(([k]) => (IS_WIN32 ? k.toUpperCase() : k) === "HOME")?.[1];
|
|
1333
|
+
return { ok: true, value: home ?? os.userInfo().homedir };
|
|
1334
|
+
}
|
|
1335
|
+
catch (e) {
|
|
1336
|
+
return { ok: false, reason: errMsg(e) };
|
|
1337
|
+
}
|
|
1338
|
+
}
|
|
1316
1339
|
fileError(e, p, abortSignal) {
|
|
1317
1340
|
if (e instanceof FileError)
|
|
1318
1341
|
return e;
|