@sema-agent/server 7.148.0 → 7.150.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 +551 -9
- package/README.md +17 -2
- package/README.zh-CN.md +5 -1
- package/USAGE.md +126 -68
- package/dist/adoption/wire.d.ts +3 -3
- package/dist/approval-card.d.ts +33 -33
- package/dist/approval-gate-currency.d.ts +1 -1
- package/dist/approval-leg-origin.d.ts +33 -0
- package/dist/approval-leg-origin.js +4 -0
- package/dist/audit.d.ts +1 -1
- package/dist/audit.js +2 -2
- package/dist/auth-bridge.d.ts +9 -1
- package/dist/auth-bridge.js +4 -4
- package/dist/auth-keys.d.ts +10 -1
- package/dist/auth-keys.js +8 -0
- package/dist/background-session-reap.d.ts +1 -1
- package/dist/boot/deferred-sandbox-path-env.d.ts +5 -3
- package/dist/boot/deferred-sandbox-path-env.js +1 -2
- package/dist/boot/execution-env.js +10 -11
- package/dist/boot/execution-lane.d.ts +6 -4
- package/dist/boot/execution-lane.js +3 -1
- package/dist/boot/org-delivery.d.ts +3 -2
- package/dist/boot/parked-revive-gate.d.ts +5 -5
- package/dist/boot/parked-revive-gate.js +2 -3
- package/dist/boot/resolve-spec.d.ts +4 -5
- package/dist/boot/resolve-spec.js +66 -28
- package/dist/boot/retention-lane.d.ts +1 -1
- package/dist/boot/runner-deps.js +3 -1
- package/dist/boot/session-faces.js +21 -21
- package/dist/boot/stage-01-config.d.ts +1 -0
- package/dist/boot/stage-01-config.js +4 -3
- package/dist/boot/stage-02-web-admin-pull.d.ts +1 -0
- package/dist/boot/stage-02-web-admin-pull.js +10 -2
- package/dist/boot/stage-03-store-backend.d.ts +1 -0
- package/dist/boot/stage-03-store-backend.js +4 -4
- package/dist/boot/stage-04-stores.d.ts +1 -0
- package/dist/boot/stage-05-execution-env.d.ts +1 -0
- package/dist/boot/stage-06-runners.d.ts +2 -1
- package/dist/boot/stage-06-runners.js +1 -2
- package/dist/boot/stage-07-capability-layer.d.ts +2 -1
- package/dist/boot/stage-07-capability-layer.js +1 -1
- package/dist/boot/stage-08-reapers.d.ts +2 -1
- package/dist/boot/stage-09-leader.d.ts +2 -1
- package/dist/boot/stage-10-http-server.d.ts +35 -2
- package/dist/boot/stage-10-http-server.js +9 -4
- package/dist/boot/stores.d.ts +3 -0
- package/dist/boot/stores.js +22 -19
- package/dist/boot/web-admin.d.ts +1 -1
- package/dist/capabilities/tool-defer.d.ts +1 -1
- package/dist/capability-facts.d.ts +70 -60
- package/dist/capability-facts.js +45 -49
- package/dist/card-settle-stored-scope.d.ts +56 -0
- package/dist/card-settle-stored-scope.js +27 -0
- package/dist/card-void.d.ts +7 -12
- package/dist/card-void.js +2 -7
- package/dist/config-catalog.d.ts +12 -0
- package/dist/config-catalog.js +37 -33
- package/dist/config-invariants.d.ts +5 -4
- package/dist/config-invariants.js +4 -4
- package/dist/config-types.d.ts +49 -16
- package/dist/config.d.ts +6 -5
- package/dist/config.js +35 -24
- package/dist/context-window-notice.d.ts +31 -0
- package/dist/context-window-notice.js +28 -0
- package/dist/deployment-governance.d.ts +7 -28
- package/dist/deployment-governance.js +1 -14
- package/dist/deployment-shape.d.ts +20 -11
- package/dist/deployment-shape.js +1 -0
- package/dist/device-ws-hub.d.ts +1 -0
- package/dist/device-ws-hub.js +64 -28
- package/dist/device-ws-protocol.d.ts +20 -6
- package/dist/device-ws-protocol.js +9 -2
- package/dist/engine-identity.d.ts +7 -1
- package/dist/engine-identity.js +5 -0
- package/dist/env-name-allowlist-knobs.d.ts +6 -5
- package/dist/env-name-allowlist-knobs.js +5 -0
- package/dist/execution-lane-caps.d.ts +20 -5
- package/dist/execution-lane-caps.js +18 -4
- package/dist/field-conflict.d.ts +2 -2
- package/dist/fleet/engine-signal.d.ts +14 -1
- package/dist/fleet/engine-signal.js +5 -1
- package/dist/fleet/fleet-reconciler.d.ts +1 -1
- package/dist/fleet/serving-lease.d.ts +18 -3
- package/dist/fleet/serving-lease.js +81 -35
- package/dist/fleet-client.d.ts +13 -11
- package/dist/fleet-client.js +53 -33
- package/dist/history-anchor.js +1 -1
- package/dist/hitl-delivery.d.ts +1 -3
- package/dist/hitl-delivery.js +1 -1
- package/dist/hooks/command-spawn.d.ts +7 -2
- package/dist/hooks/command-spawn.js +13 -6
- package/dist/hooks/hook-runner.d.ts +5 -0
- package/dist/hooks/hook-runner.js +11 -7
- package/dist/http/active-run-conflict.d.ts +3 -7
- package/dist/http/active-run-conflict.js +2 -2
- package/dist/http/admission.js +5 -5
- package/dist/http/approval-sweeps.js +5 -3
- package/dist/http/caller-trust.d.ts +2 -2
- package/dist/http/caller-trust.js +3 -3
- package/dist/http/checkpoint-ctx.d.ts +18 -63
- package/dist/http/checkpoint-ctx.js +1 -24
- package/dist/http/claim-heartbeat.d.ts +6 -6
- package/dist/http/claim-heartbeat.js +6 -6
- package/dist/http/claimed-run.d.ts +16 -9
- package/dist/http/claimed-run.js +8 -0
- package/dist/http/dispatch.js +7 -5
- package/dist/http/durable-run-leg.d.ts +8 -1
- package/dist/http/durable-run-leg.js +34 -15
- package/dist/http/execution-context.d.ts +115 -14
- package/dist/http/execution-context.js +130 -40
- package/dist/http/owner-access.d.ts +246 -21
- package/dist/http/owner-access.js +186 -8
- package/dist/http/principal-gate.d.ts +18 -11
- package/dist/http/principal-gate.js +3 -1
- package/dist/http/readiness.d.ts +4 -2
- package/dist/http/resume-ctx-read.d.ts +14 -3
- package/dist/http/resume-ctx-read.js +24 -3
- package/dist/http/resume-legs.d.ts +35 -3
- package/dist/http/resume-legs.js +262 -106
- package/dist/http/route-ctx.d.ts +29 -11
- package/dist/http/routes/a2a-serve.js +17 -14
- package/dist/http/routes/approvals-assistant.d.ts +3 -2
- package/dist/http/routes/approvals-assistant.js +11 -11
- package/dist/http/routes/capabilities.js +5 -4
- package/dist/http/routes/config-catalog.d.ts +1 -1
- package/dist/http/routes/diagnostics.d.ts +3 -1
- package/dist/http/routes/diagnostics.js +30 -5
- package/dist/http/routes/mcp-reconnect.js +2 -2
- package/dist/http/routes/memory-origin.d.ts +1 -1
- package/dist/http/routes/notify-wake.js +2 -3
- package/dist/http/routes/principal-inbox.d.ts +3 -1
- package/dist/http/routes/principal-inbox.js +3 -2
- package/dist/http/routes/projects.d.ts +1 -0
- package/dist/http/routes/projects.js +5 -3
- package/dist/http/routes/retention-ops.d.ts +1 -1
- package/dist/http/routes/run-permission-mode.d.ts +1 -1
- package/dist/http/routes/run-permission-mode.js +2 -2
- package/dist/http/routes/runs.d.ts +1 -1
- package/dist/http/routes/runs.js +33 -37
- package/dist/http/routes/session-memory-sync.js +4 -5
- package/dist/http/routes/session-sync.d.ts +24 -5
- package/dist/http/routes/session-sync.js +45 -37
- package/dist/http/routes/sessions-list.d.ts +1 -1
- package/dist/http/routes/sessions.d.ts +1 -1
- package/dist/http/routes/sessions.js +18 -44
- package/dist/http/routes/tasks.js +136 -101
- package/dist/http/routes/trace-usage.js +14 -14
- package/dist/http/routes/workflows.d.ts +1 -0
- package/dist/http/routes/workflows.js +20 -38
- package/dist/http/server.d.ts +12 -9
- package/dist/http/server.js +35 -6
- package/dist/http/stored-body-replay.d.ts +3 -24
- package/dist/http/stored-body-replay.js +2 -12
- package/dist/http/submit-ctx.d.ts +2 -2
- package/dist/http/submit-ctx.js +5 -5
- package/dist/http/submit-replay.d.ts +15 -2
- package/dist/http/submit-replay.js +10 -4
- package/dist/http/wire-types.d.ts +6 -3
- package/dist/index.d.ts +3 -1
- package/dist/index.js +1 -0
- package/dist/leader/endpoint.js +9 -8
- package/dist/leg-facts.d.ts +13 -2
- package/dist/leg-facts.js +2 -0
- package/dist/local-store-declaration.d.ts +54 -0
- package/dist/local-store-declaration.js +59 -0
- package/dist/mcp-assertion-header.d.ts +1 -1
- package/dist/mcp-assertion-header.js +1 -1
- package/dist/mcp-authorization-face.d.ts +18 -14
- package/dist/mcp-authorization-face.js +3 -2
- package/dist/memory-project-layer.js +1 -1
- package/dist/memory-scope.d.ts +4 -4
- package/dist/memory-scope.js +4 -4
- package/dist/memory-store-refusal.d.ts +1 -1
- package/dist/memory-sync-client.js +2 -2
- package/dist/model-context-window.d.ts +50 -11
- package/dist/model-context-window.js +20 -1
- package/dist/model-entry-refusal.d.ts +5 -4
- package/dist/model-entry-refusal.js +17 -3
- package/dist/model-reach-scope.d.ts +2 -1
- package/dist/observability/corrupt-read-seat.d.ts +1 -1
- package/dist/observability/fail-open.d.ts +19 -7
- package/dist/observability/fail-open.js +19 -7
- package/dist/observability/logger.d.ts +7 -0
- package/dist/observability/logger.js +3 -1
- package/dist/observability/metrics.js +1 -1
- package/dist/observability/run-terminal.d.ts +1 -1
- package/dist/observability/run-terminal.js +1 -0
- package/dist/observability/secret-env-scrub.d.ts +1 -1
- package/dist/observability/secret-env-scrub.js +1 -1
- package/dist/org-delivery-gate.d.ts +31 -6
- package/dist/org-delivery-gate.js +7 -4
- package/dist/owner-faces.d.ts +30 -5
- package/dist/owner-faces.js +2 -0
- package/dist/park-row-commit.d.ts +67 -0
- package/dist/park-row-commit.js +76 -0
- package/dist/parked-decide.d.ts +9 -7
- package/dist/parked-decide.js +25 -6
- package/dist/peer-directory.d.ts +1 -1
- package/dist/peer-seat.d.ts +1 -1
- package/dist/peer-seat.js +52 -59
- package/dist/plugins/adoption-log-sql.d.ts +3 -3
- package/dist/plugins/adoption-log-sql.js +19 -19
- package/dist/plugins/approval-ask-store-sql.d.ts +1 -1
- package/dist/plugins/approval-ask-store-sql.js +3 -5
- package/dist/plugins/approval-exemption-skill-cutoff.d.ts +64 -0
- package/dist/plugins/approval-exemption-skill-cutoff.js +122 -0
- package/dist/plugins/approval-exemption-skill-m46.d.ts +0 -6
- package/dist/plugins/approval-exemption-skill-m46.js +6 -10
- package/dist/plugins/approval-exemption-store.d.ts +26 -1
- package/dist/plugins/approval-exemption-store.js +28 -19
- package/dist/plugins/ensure-schema-lock-policy.d.ts +36 -3
- package/dist/plugins/ensure-schema-lock-policy.js +75 -9
- package/dist/plugins/exec-path-law.d.ts +34 -0
- package/dist/plugins/exec-path-law.js +13 -0
- package/dist/plugins/file-run-store.d.ts +22 -9
- package/dist/plugins/file-run-store.js +51 -23
- package/dist/plugins/local-checkpoint-store.d.ts +8 -1
- package/dist/plugins/local-checkpoint-store.js +5 -1
- package/dist/plugins/local-data-root-admission.d.ts +52 -0
- package/dist/plugins/local-data-root-admission.js +100 -0
- package/dist/plugins/local-engine-high-water.d.ts +28 -7
- package/dist/plugins/local-engine-high-water.js +27 -22
- package/dist/plugins/local-session-store.d.ts +134 -49
- package/dist/plugins/local-session-store.js +149 -108
- package/dist/plugins/memory-run-store.d.ts +14 -9
- package/dist/plugins/memory-run-store.js +48 -22
- package/dist/plugins/one-time-migration-marker.d.ts +19 -3
- package/dist/plugins/one-time-migration-marker.js +7 -1
- package/dist/plugins/one-time-migrations.d.ts +110 -59
- package/dist/plugins/one-time-migrations.js +224 -136
- package/dist/plugins/org-delivery-quota-store.d.ts +34 -11
- package/dist/plugins/org-delivery-quota-store.js +29 -14
- package/dist/plugins/permission-rule-store-sql.d.ts +1 -1
- package/dist/plugins/permission-rule-store-sql.js +5 -9
- package/dist/plugins/pg-pool.d.ts +1 -1
- package/dist/plugins/pg-pool.js +14 -12
- package/dist/plugins/posix-shell-fs.js +4 -2
- package/dist/plugins/remote-env-adb.js +3 -2
- package/dist/plugins/remote-env-device.d.ts +3 -2
- package/dist/plugins/remote-env-device.js +8 -6
- package/dist/plugins/remote-env-e2b.d.ts +4 -8
- package/dist/plugins/remote-env-e2b.js +21 -15
- package/dist/plugins/remote-env-host.d.ts +51 -10
- package/dist/plugins/remote-env-host.js +97 -31
- package/dist/plugins/remote-env-k8s.js +7 -4
- package/dist/plugins/remote-env-local-docker.d.ts +6 -3
- package/dist/plugins/remote-env-local-docker.js +11 -25
- package/dist/plugins/remote-env-ssh.js +4 -3
- package/dist/plugins/remote-shell.d.ts +3 -1
- package/dist/plugins/remote-shell.js +3 -3
- package/dist/plugins/replica-registration.d.ts +35 -0
- package/dist/plugins/replica-registration.js +48 -0
- package/dist/plugins/retention-lane-store-sql.d.ts +1 -1
- package/dist/plugins/run-store-sql.d.ts +27 -8
- package/dist/plugins/run-store-sql.js +46 -18
- package/dist/plugins/scenario-prefix-switch-m42.d.ts +8 -12
- package/dist/plugins/scenario-prefix-switch-m42.js +9 -6
- package/dist/plugins/schema-consistency.d.ts +91 -5
- package/dist/plugins/schema-consistency.js +189 -7
- package/dist/plugins/schema-drift-sentence.d.ts +6 -5
- package/dist/plugins/schema-drift-sentence.js +13 -5
- package/dist/plugins/serving-requirement.d.ts +49 -0
- package/dist/plugins/serving-requirement.js +53 -0
- package/dist/plugins/session-placement.d.ts +5 -4
- package/dist/plugins/shell-sentinel-owner-m48.d.ts +22 -0
- package/dist/plugins/shell-sentinel-owner-m48.js +6 -0
- package/dist/plugins/stop-then-start-sentence.d.ts +55 -0
- package/dist/plugins/stop-then-start-sentence.js +51 -0
- package/dist/plugins/store-backend.d.ts +23 -9
- package/dist/plugins/store-backend.js +58 -24
- package/dist/plugins/store-contracts.d.ts +45 -5
- package/dist/plugins/store-contracts.js +19 -0
- package/dist/plugins/tidb-pool.d.ts +11 -6
- package/dist/plugins/tidb-pool.js +22 -12
- package/dist/plugins/workspace-custody.d.ts +44 -0
- package/dist/plugins/workspace-custody.js +47 -0
- package/dist/process-instance.d.ts +3 -0
- package/dist/process-instance.js +7 -0
- package/dist/project-context.d.ts +66 -0
- package/dist/project-context.js +70 -0
- package/dist/project-identity.d.ts +8 -7
- package/dist/project-identity.js +40 -17
- package/dist/project-layer-key.d.ts +13 -2
- package/dist/project-layer-key.js +9 -1
- package/dist/question.d.ts +9 -21
- package/dist/question.js +28 -19
- package/dist/read-deny-floor-notice.d.ts +36 -0
- package/dist/read-deny-floor-notice.js +40 -0
- package/dist/{plain-string-record.d.ts → request-declaration-disposition.d.ts} +9 -23
- package/dist/request-declaration-disposition.js +39 -0
- package/dist/run-cancel-card.d.ts +28 -0
- package/dist/run-cancel-card.js +20 -0
- package/dist/run-cancel-context.d.ts +10 -0
- package/dist/run-cancel-context.js +1 -0
- package/dist/run-local.js +28 -13
- package/dist/runs.d.ts +31 -7
- package/dist/runs.js +34 -16
- package/dist/security.d.ts +3 -3
- package/dist/server-secret-env.d.ts +34 -20
- package/dist/server-secret-env.js +61 -18
- package/dist/session-agent.d.ts +4 -12
- package/dist/session-agent.js +0 -6
- package/dist/session-children-retire.d.ts +6 -3
- package/dist/session-children-retire.js +2 -5
- package/dist/session-incarnation.d.ts +18 -4
- package/dist/session-incarnation.js +4 -1
- package/dist/session-notify-door.d.ts +4 -4
- package/dist/session-notify-door.js +5 -6
- package/dist/session-rules-seat.d.ts +4 -3
- package/dist/session-rules-seat.js +3 -11
- package/dist/session-sync.js +3 -3
- package/dist/session-workspace.d.ts +3 -3
- package/dist/store-admission.d.ts +33 -0
- package/dist/store-admission.js +49 -0
- package/dist/store-posture.d.ts +5 -0
- package/dist/store-posture.js +18 -1
- package/dist/stores-sql/checkpoint-store-sql.d.ts +22 -3
- package/dist/stores-sql/checkpoint-store-sql.js +109 -23
- package/dist/stores-sql/file-history-store-sql.d.ts +1 -1
- package/dist/stores-sql/file-history-store-sql.js +3 -5
- package/dist/stores-sql/retention-store-sql.d.ts +1 -1
- package/dist/stores-sql/tool-result-store-sql.d.ts +1 -1
- package/dist/stores-sql/tool-result-store-sql.js +4 -6
- package/dist/task-a2a.d.ts +3 -8
- package/dist/task-a2a.js +25 -27
- package/dist/task-cwd.d.ts +27 -10
- package/dist/task-cwd.js +3 -0
- package/dist/task-mcp.d.ts +3 -3
- package/dist/task-mcp.js +1 -1
- package/dist/task-settings.d.ts +13 -0
- package/dist/task-settings.js +14 -7
- package/dist/terminal.d.ts +2 -0
- package/dist/terminal.js +21 -6
- package/dist/tool-approval.d.ts +50 -41
- package/dist/tool-approval.js +96 -73
- package/dist/trace/engine-notice-wire.d.ts +38 -21
- package/dist/trace/engine-notice-wire.js +25 -4
- package/dist/trace/ledger-sink.d.ts +3 -1
- package/dist/trace/project.d.ts +8 -5
- package/dist/trace/project.js +11 -9
- package/dist/trace/projection-table.d.ts +33 -2
- package/dist/trace/redact.d.ts +12 -0
- package/dist/trace/redact.js +5 -1
- package/dist/trace/self-minted-wire.d.ts +268 -56
- package/dist/trace/self-minted-wire.js +110 -40
- package/dist/trace/tool-card.d.ts +3 -0
- package/dist/trace/tool-card.js +24 -0
- package/dist/trace/tool-output-media.d.ts +2 -1
- package/dist/unattended-permission-mode.d.ts +1 -1
- package/dist/web-admin-client/apply-effective.d.ts +1 -1
- package/dist/web-admin-client/apply-effective.js +22 -9
- package/dist/web-admin-client/effective-keys.d.ts +1 -1
- package/dist/web-admin-client/skills-mcp.d.ts +1 -1
- package/docs/DEVICE-EXECUTOR-PROTOCOL.md +57 -29
- package/docs/error-text-lexicon.json +317 -0
- package/package.json +4 -2
- package/scripts/embed-minimal.mjs +183 -0
- package/wire-changes/server@7.149.0.json +372 -0
- package/wire-changes/server@7.150.0.json +335 -0
- package/dist/plain-string-record.js +0 -70
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { FileError, type InheritEnvPolicy, ExecutionError, RemoteExecutionError, SchedulerError, type SchedulerCapability, type ScheduledIntent, type SchedulerContext, type ScheduledTaskId, type ScheduledTaskSummary, type ExecutionEnv, type ExecResult, 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";
|
|
1
|
+
import { FileError, type InheritEnvPolicy, ExecutionError, RemoteExecutionError, SchedulerError, type SchedulerCapability, type ScheduledIntent, type SchedulerContext, type ScheduledTaskId, type ScheduledTaskSummary, type ExecutionEnv, type ExecResult, 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, type ExecutionEnvFactoryContext, BackgroundShellError, type BackgroundShellCapability, type BackgroundShellId, type BackgroundHardKillOutcome, type BackgroundPoll, type BackgroundSpawnOptions, type SecretEnvFinding } from "@sema-agent/core";
|
|
2
2
|
import { type LaneHomeConfig } from "../lane-home-probe.js";
|
|
3
|
+
import { type CustodyMember } from "./workspace-custody.js";
|
|
3
4
|
/** Options accepted by `ExecutionEnv.exec` (core does not re-export the type; derive it from the seam). */
|
|
4
5
|
type ExecOpts = Parameters<ExecutionEnv["exec"]>[1];
|
|
5
6
|
/** SIGTERM → grace → SIGKILL (no host isolation). 🔴 宽限的**唯一属主**:本 driver 的 `kill` 梯子用它排升级定时器;会话级收敛
|
|
@@ -10,7 +11,7 @@ export declare const HOST_BG_KILL_GRACE_MS: number;
|
|
|
10
11
|
export interface HostEnvConfig extends LaneHomeConfig {
|
|
11
12
|
/**
|
|
12
13
|
* Base directory under which this adapter creates its per-task working directory. Default {@link os.tmpdir}.
|
|
13
|
-
* The actual workspace is `<workspaceBase>/sema-host-<id
|
|
14
|
+
* The actual workspace is `<workspaceBase>/sema-host-<id>-<rand>`; its last holder `rm -rf`s it on {@link destroy} (see {@link custody}).
|
|
14
15
|
*/
|
|
15
16
|
workspaceBase?: string;
|
|
16
17
|
/**
|
|
@@ -44,14 +45,21 @@ export interface HostEnvConfig extends LaneHomeConfig {
|
|
|
44
45
|
* user's OWN directory (e.g. the cwd `run-local` was invoked in), not an adapter-created ephemeral sandbox,
|
|
45
46
|
* so the agent's file edits PERSIST and are user-visible (like a local coding agent working in your repo).
|
|
46
47
|
* Takes precedence over `workspaceBase`/`id` (those are ignored). Default unset = ephemeral random + cleanup.
|
|
48
|
+
* 唯一的例外由托管表说:它落在一只本服务铸的临时根里({@link custody} 的 `sharedRoot`)⇒ 本环境共持那只根,不是调用方自有目录。
|
|
47
49
|
*/
|
|
48
50
|
workspaceDir?: string;
|
|
51
|
+
/**
|
|
52
|
+
* 工厂的工作区托管表交给它铸的每一只环境的那一面({@link hostExecutionEnvFactory} 装配;嵌入方直接构造的环境不带 ⇒ 不在任何表里,没有别的持有者)。
|
|
53
|
+
*/
|
|
54
|
+
custody?: HostWorkspaceCustodySeat;
|
|
49
55
|
/** R7 self-wake: the injected `SchedulerCapability` backend (the TOC self-wake scheduler write side).
|
|
50
56
|
* When set, `hasScheduler(env)` is true and core mounts CronCreate/CronDelete/CronList(旧名 CronCancel;Sleep 已 [ref] 撤除;挂载在 prepare-task 非 createHandsToolkit——L8 订正); when absent the env
|
|
51
57
|
* reports `supported:false` → INERT (no scheduler tools). Injected at boot only on the single-user host lane. */
|
|
52
58
|
scheduler?: SchedulerCapability;
|
|
53
|
-
/** [ref] background shell:
|
|
54
|
-
*
|
|
59
|
+
/** [ref] background shell: the assembly hands the deployment-shape row's answer here
|
|
60
|
+
* (`DEPLOYMENT_SHAPE_PREDICATES.backgroundShell.holds(config)`); `false` → INERT (multi-tenant host lane gate).
|
|
61
|
+
* Default (undefined) = the SAME row's answer for an undeclared principal (the single-user TOC host lane), never a literal.
|
|
62
|
+
* `HOST_BG_ENABLED=false` is an additional global kill switch(🪦 旧名
|
|
55
63
|
* `HOST_BG_DISABLED` 自 server 3.0.0 起是 fail-loud 墓碑:设了即拒启并指路新名,不再被读取)。 */
|
|
56
64
|
backgroundShell?: boolean;
|
|
57
65
|
/**
|
|
@@ -61,7 +69,14 @@ export interface HostEnvConfig extends LaneHomeConfig {
|
|
|
61
69
|
*/
|
|
62
70
|
gitBase?: true;
|
|
63
71
|
}
|
|
64
|
-
|
|
72
|
+
/** 工作区托管表交给它铸的环境的那一面(`workspace-custody.ts`;host 车道的读法)。 */
|
|
73
|
+
export interface HostWorkspaceCustodySeat {
|
|
74
|
+
/** `workspaceDir` 落在这只本服务铸的临时根里(委派子代按父的工作目录落根)⇒ 本环境与它的铸者共持这只根:建成后登记、收场时放手、最后放手的持有者删它。 */
|
|
75
|
+
readonly sharedRoot?: string;
|
|
76
|
+
/** 表里除 `self` 之外还有没有成员持有 `root` —— 收场放手时问;有 ⇒ 删交给最后放手的那一只。 */
|
|
77
|
+
heldElsewhere(self: RemoteHostExecutionEnv, root: string): boolean;
|
|
78
|
+
}
|
|
79
|
+
export declare class RemoteHostExecutionEnv implements RemoteExecutionEnv, SchedulerCapability, BackgroundShellCapability, CustodyMember<never> {
|
|
65
80
|
/** 能力块的唯一作者 = `capabilitiesFor`(`execution-lane-caps.ts`;逐键表态与理由在那里,本类零能力字面量)。 */
|
|
66
81
|
readonly capabilities: import("@sema-agent/core").ExecutionEnvCapabilities;
|
|
67
82
|
/** R7 自唤醒: structural delegation to the injected scheduler backend. `hasScheduler(env)` checks BOTH the method
|
|
@@ -97,7 +112,7 @@ export declare class RemoteHostExecutionEnv implements RemoteExecutionEnv, Sched
|
|
|
97
112
|
/**
|
|
98
113
|
* core 7.56.0 [ref]:shell 子进程继承的环境里**收不到**的条目(名 + 规则,从不带值)—— 与 {@link cdPath} 同一只组合的读面
|
|
99
114
|
* (`server-secret-env.ts` 的 `withheldFromHostSubprocess`:继承组合扣下的发现,减去 `cfg.env` 这条显式授予重供的键)。逐命令的 `options.env`
|
|
100
|
-
* 是引擎自己交的显式授予,它知道(core
|
|
115
|
+
* 是引擎自己交的显式授予,它知道(core [ref] 同一残余)。只读:不披露、不计数、不碰 shell。
|
|
101
116
|
*/
|
|
102
117
|
get withheldEnv(): readonly SecretEnvFinding[];
|
|
103
118
|
private readonly workspaceDir;
|
|
@@ -106,6 +121,9 @@ export declare class RemoteHostExecutionEnv implements RemoteExecutionEnv, Sched
|
|
|
106
121
|
private prepared;
|
|
107
122
|
/** Terminal once {@link destroy} ran — guards fs/exec from running against a removed workspace. */
|
|
108
123
|
private destroyed;
|
|
124
|
+
/** {@link destroy} 的那一次收场(第二次调用等同一只);托管表读它({@link releasing}):收场在飞时同一身份不铸新的。 */
|
|
125
|
+
private teardown?;
|
|
126
|
+
private teardownSettled;
|
|
109
127
|
/** [ref] 五轮复审:fg/bg spool 根=tmpdir 私有目录(core bgSpoolDir 同形)。workspace 是命令可写区,
|
|
110
128
|
* `git clean -fdx` 类清理会 unlink 活跃 spool(子进程经继承 fd 对 unlinked inode 继续写,而父进程
|
|
111
129
|
* 按路径 statSync/read 静默断=输出丢失+轮转帽失效);顺带不再弄脏 user-owned workspace(TOC/CC
|
|
@@ -172,12 +190,22 @@ export declare class RemoteHostExecutionEnv implements RemoteExecutionEnv, Sched
|
|
|
172
190
|
*/
|
|
173
191
|
private drainInflightFs;
|
|
174
192
|
private ensureSpoolRoot;
|
|
175
|
-
/**
|
|
176
|
-
private
|
|
193
|
+
/** 本环境对它工作区的租期({@link HostWorkspaceTenure}):借用的永不删;持有的收场放手、最后放手的持有者删。唯一写处 = 构造与 {@link removeOwnedTrees}。 */
|
|
194
|
+
private tenure;
|
|
177
195
|
/** Memoized lazy `mkdir -p` so concurrent first-use prepares the workspace exactly once. */
|
|
178
196
|
private preparing?;
|
|
179
197
|
private readonly handle;
|
|
180
198
|
constructor(config?: HostEnvConfig);
|
|
199
|
+
/** 收场的进度(托管表读):活着 ⇒ 缺席;{@link destroy} 开始 ⇒ 那一次收场。 */
|
|
200
|
+
get releasing(): Promise<void> | undefined;
|
|
201
|
+
/** 收场已落定。 */
|
|
202
|
+
get released(): boolean;
|
|
203
|
+
/** 本车道收场不欠同一身份下一只环境任何账(删不掉的根留在退出兜底簿上,不交接)。 */
|
|
204
|
+
readonly leftover: undefined;
|
|
205
|
+
/** 收场落定且已放手 ⇒ 出托管表(超时臂里放手挂在在飞写之后:落定了还持有 ⇒ 仍是持有者)。 */
|
|
206
|
+
get settled(): boolean;
|
|
207
|
+
/** 本环境此刻持有的、本服务铸的临时根(托管表的持有读法读它);借用 / 已放手 ⇒ 缺席。 */
|
|
208
|
+
get heldWorkspace(): string | undefined;
|
|
181
209
|
workspaceHandle(): WorkspaceHandle;
|
|
182
210
|
connect(config?: RemoteConnectConfig): Promise<{
|
|
183
211
|
ok: true;
|
|
@@ -223,6 +251,7 @@ export declare class RemoteHostExecutionEnv implements RemoteExecutionEnv, Sched
|
|
|
223
251
|
* core 留活过的驻留 watcher 又杀了一遍(run 尾 dispose→destroy 顺序,destroy 是第二刀)。隔离 env(容器/VM)
|
|
224
252
|
* 整体拆机自然带走进程,与本契约不冲突(k8s/e2b 各自 destroy 不走这条路径)。 */
|
|
225
253
|
destroy(): Promise<void>;
|
|
254
|
+
private tearDown;
|
|
226
255
|
/** `destroy()` 真正动手删的那一段(本 env **自有**的两棵树:spool 根 + ephemeral workspace)。
|
|
227
256
|
* 抽成一只是因为它有两个调用时机 —— 在飞写按时清空时由 `destroy()` 直接 await,超时时挂在那批写后面异步跑
|
|
228
257
|
* (见 {@link drainInflightFs} 头注③)。永不抛(teardown 契约)。 */
|
|
@@ -373,7 +402,7 @@ export declare class RemoteHostExecutionEnv implements RemoteExecutionEnv, Sched
|
|
|
373
402
|
* 查不到根因;更糟的是每一个任务都复发一次同一条。⇒ 前判把它换成一句能直接照做的拒绝:哪个目录、
|
|
374
403
|
* 哪个用户、改哪个旋钮。
|
|
375
404
|
*
|
|
376
|
-
*
|
|
405
|
+
* 只对**自铸**的那一只做(没给 `workspaceDir`;给了的是用户自己的目录或父那只已判过的根,`mkdir -p` 一个已存在的只读 checkout 本来
|
|
377
406
|
* 就该成功 —— 在那儿加可写判据会把"只读仓库上跑只读任务"这个正当用法拒掉)。
|
|
378
407
|
* root 与非 POSIX 平台跳过:root 的可写判据恒真(root-squash 的网络盘上反而会假拒),
|
|
379
408
|
* 而 `process.getuid` 不存在时这条 POSIX 权限判据无从谈起。
|
|
@@ -453,6 +482,18 @@ export declare class RemoteHostExecutionEnv implements RemoteExecutionEnv, Sched
|
|
|
453
482
|
* Mirrors `e2bExecutionEnvFactory`: returns the env **unconnected** (lazy) and folds the per-task
|
|
454
483
|
* `ctx.sessionId` into the workspace dir id so two concurrent tasks never share a working directory.
|
|
455
484
|
*/
|
|
456
|
-
|
|
485
|
+
/** 一条腿的环境配置里随腿变的那几格({@link hostExecutionEnvFactory} 第二参:工厂上下文 → 值)。 */
|
|
486
|
+
export interface HostLegConfig extends Pick<HostEnvConfig, "workspaceDir" | "env" | "inheritEnv"> {
|
|
487
|
+
/** 铸成那一拍对这只环境做的一次装饰(交回已有环境时不再做)。 */
|
|
488
|
+
readonly minted?: (env: RemoteHostExecutionEnv) => void;
|
|
489
|
+
}
|
|
490
|
+
/**
|
|
491
|
+
* host 车道的工厂。**一只身份一只工作区**:经工作区托管表(`workspace-custody.ts`,与 local-docker 车道同一只)铸环境 ——
|
|
492
|
+
* 同一身份(`ctx.taskId ?? ctx.sessionId`)还有一只没收场的环境 ⇒ 交回它(停驻腿的环境 = 续跑腿的环境:引擎 park-only 契约要的「重连到同一工作区」);
|
|
493
|
+
* 正在收场 ⇒ 等它落定再铸。铸的时候问同一张表「腿的目录落在哪只本服务铸的临时根里」:落在里面(委派子代按父的工作目录落根)⇒ 与铸者共持那只根,
|
|
494
|
+
* 父收场时子代还持有它就不删,最后放手的持有者删。`legOf` 先于交回 / 铸判(它可以拒)。
|
|
495
|
+
* 起服铸**一只**工厂(每次调用另铸一只工厂 = 没有表)。
|
|
496
|
+
*/
|
|
497
|
+
export declare function hostExecutionEnvFactory(config?: HostEnvConfig, legOf?: (ctx: ExecutionEnvFactoryContext) => HostLegConfig): ExecutionEnvFactory;
|
|
457
498
|
export { localDockerExecutionEnvFactory, type LocalDockerEnvConfig, type LocalDockerEnvDeps, RemoteLocalDockerExecutionEnv } from "./remote-env-local-docker.js";
|
|
458
499
|
//# sourceMappingURL=remote-env-host.d.ts.map
|
|
@@ -2,6 +2,7 @@ import { writeLogText } from "../observability/logger.js";
|
|
|
2
2
|
import { spawn } from "node:child_process";
|
|
3
3
|
import os from "node:os";
|
|
4
4
|
import path from "node:path";
|
|
5
|
+
import { anchoredPath, anchorUnder, joinedPath } from "./exec-path-law.js";
|
|
5
6
|
import fs from "node:fs/promises";
|
|
6
7
|
import { randomBytes } from "node:crypto";
|
|
7
8
|
import { openSync, closeSync, readSync, statSync, truncateSync, unlinkSync, mkdirSync, rmdirSync, mkdtempSync, existsSync, realpathSync, accessSync, constants as FS_CONST } from "node:fs";
|
|
@@ -12,12 +13,14 @@ import { buildCapturedOutput, createCutError } from "../exec-cut.js";
|
|
|
12
13
|
import { hostBackgroundShellEnabled, hostExecSpoolEnabled } from "../config.js";
|
|
13
14
|
import { IS_WIN32, resolveHostShell, hostShell, pinHostShell, spawnGroupOptions, killTreeHard, killTreeSoft, hardKillGroupNow, collapseWin32EnvKeys, adoptHostForegroundChild } from "./host-platform.js";
|
|
14
15
|
import { BackgroundShellManager, seedMemStream, feedMemStream, drainMemStream } from "./background-shell-support.js";
|
|
15
|
-
import { FileError, NodeExecutionEnv, inheritEnvPolicyProblem, ExecutionError, RemoteExecutionError, RollingTailBuffer, walkSymlinkChain, markTruncated, SchedulerError, BackgroundShellError, } from "@sema-agent/core";
|
|
16
|
+
import { FileError, NodeExecutionEnv, inheritEnvPolicyProblem, ExecutionError, RemoteExecutionError, RollingTailBuffer, walkSymlinkChain, markTruncated, SchedulerError, BackgroundShellError, physicalPathOf as kernelRealPathOf, } from "@sema-agent/core";
|
|
16
17
|
import { capabilitiesFor } from "../execution-lane-caps.js";
|
|
18
|
+
import { DEPLOYMENT_SHAPE_PREDICATES } from "../deployment-shape.js";
|
|
17
19
|
import { LaneHomeSeat, laneHomeRefusal } from "../lane-home-probe.js";
|
|
18
20
|
import { composeHostSubprocessEnv, hostSubprocessEnv, withheldFromHostSubprocess } from "../server-secret-env.js";
|
|
19
21
|
import { nameTooLongCode } from "./remote-env-file-error.js";
|
|
20
22
|
import { trackEphemeralHostWorkspace, untrackEphemeralHostWorkspace } from "./host-workspace-registry.js";
|
|
23
|
+
import { createWorkspaceCustody } from "./workspace-custody.js";
|
|
21
24
|
import { OWNER_ONLY_FILE_MODE } from "./owner-only-file-mode.js";
|
|
22
25
|
const PROVIDER = "host";
|
|
23
26
|
const DEFAULT_COMMAND_TIMEOUT_MS = 30 * 60_000;
|
|
@@ -34,6 +37,8 @@ const unsupported = (op) => ({
|
|
|
34
37
|
ok: false,
|
|
35
38
|
error: new RemoteExecutionError("unsupported", `${op} is not supported on a host target (the operator's own machine — no container boundary, not snapshotable; capabilities.suspendable=false)`),
|
|
36
39
|
});
|
|
40
|
+
const BORROWED = Object.freeze({ kind: "borrowed" });
|
|
41
|
+
const RELEASED = Object.freeze({ kind: "released" });
|
|
37
42
|
const UNSUPPORTED_SCHEDULER_CAPS = {
|
|
38
43
|
supported: false,
|
|
39
44
|
maxScheduledPerScope: 0,
|
|
@@ -80,6 +85,8 @@ export class RemoteHostExecutionEnv {
|
|
|
80
85
|
cfg;
|
|
81
86
|
prepared = false;
|
|
82
87
|
destroyed = false;
|
|
88
|
+
teardown;
|
|
89
|
+
teardownSettled = false;
|
|
83
90
|
spoolRootDir;
|
|
84
91
|
liveFgSpools = new Set();
|
|
85
92
|
inflightFs = new Set();
|
|
@@ -122,20 +129,21 @@ export class RemoteHostExecutionEnv {
|
|
|
122
129
|
this.spoolRootDir = mkdtempSync(path.join(os.tmpdir(), "sema-host-spool-"));
|
|
123
130
|
return this.spoolRootDir;
|
|
124
131
|
}
|
|
125
|
-
|
|
132
|
+
tenure;
|
|
126
133
|
preparing;
|
|
127
134
|
handle;
|
|
128
135
|
constructor(config = {}) {
|
|
129
136
|
if (config.workspaceDir) {
|
|
130
137
|
this.workspaceDir = path.resolve(config.workspaceDir);
|
|
131
|
-
|
|
138
|
+
const shared = config.custody?.sharedRoot;
|
|
139
|
+
this.tenure = shared !== undefined ? { kind: "held", root: path.resolve(shared) } : BORROWED;
|
|
132
140
|
}
|
|
133
141
|
else {
|
|
134
142
|
const base = config.workspaceBase ?? os.tmpdir();
|
|
135
143
|
const id = config.id ?? randomBytes(6).toString("hex");
|
|
136
144
|
const suffix = randomBytes(4).toString("hex");
|
|
137
145
|
this.workspaceDir = path.resolve(base, `sema-host-${sanitizeId(id)}-${suffix}`);
|
|
138
|
-
this.
|
|
146
|
+
this.tenure = { kind: "held", root: this.workspaceDir };
|
|
139
147
|
}
|
|
140
148
|
const inheritEnvProblem = inheritEnvPolicyProblem(config.inheritEnv);
|
|
141
149
|
if (inheritEnvProblem !== undefined)
|
|
@@ -156,6 +164,19 @@ export class RemoteHostExecutionEnv {
|
|
|
156
164
|
if (homeVerdict !== undefined)
|
|
157
165
|
throw laneHomeRefusal(homeVerdict);
|
|
158
166
|
}
|
|
167
|
+
get releasing() {
|
|
168
|
+
return this.teardown;
|
|
169
|
+
}
|
|
170
|
+
get released() {
|
|
171
|
+
return this.teardownSettled;
|
|
172
|
+
}
|
|
173
|
+
leftover = undefined;
|
|
174
|
+
get settled() {
|
|
175
|
+
return this.teardownSettled && this.tenure.kind !== "held";
|
|
176
|
+
}
|
|
177
|
+
get heldWorkspace() {
|
|
178
|
+
return this.tenure.kind === "held" ? this.tenure.root : undefined;
|
|
179
|
+
}
|
|
159
180
|
workspaceHandle() {
|
|
160
181
|
return this.handle;
|
|
161
182
|
}
|
|
@@ -190,9 +211,13 @@ export class RemoteHostExecutionEnv {
|
|
|
190
211
|
return { ok: false, error: new RemoteExecutionError("connect_failed", "execution env already destroyed") };
|
|
191
212
|
return ok(this.handle);
|
|
192
213
|
}
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
214
|
+
destroy() {
|
|
215
|
+
this.teardown ??= this.tearDown().finally(() => {
|
|
216
|
+
this.teardownSettled = true;
|
|
217
|
+
});
|
|
218
|
+
return this.teardown;
|
|
219
|
+
}
|
|
220
|
+
async tearDown() {
|
|
196
221
|
this.destroyed = true;
|
|
197
222
|
const p = this.preparing;
|
|
198
223
|
this.preparing = undefined;
|
|
@@ -215,11 +240,15 @@ export class RemoteHostExecutionEnv {
|
|
|
215
240
|
catch { }
|
|
216
241
|
this.spoolRootDir = undefined;
|
|
217
242
|
}
|
|
218
|
-
|
|
243
|
+
const tenure = this.tenure;
|
|
244
|
+
if (tenure.kind !== "held")
|
|
245
|
+
return;
|
|
246
|
+
this.tenure = RELEASED;
|
|
247
|
+
if (this.cfg.custody?.heldElsewhere(this, tenure.root) === true)
|
|
219
248
|
return;
|
|
220
249
|
try {
|
|
221
|
-
await fs.rm(
|
|
222
|
-
untrackEphemeralHostWorkspace(
|
|
250
|
+
await fs.rm(tenure.root, { recursive: true, force: true });
|
|
251
|
+
untrackEphemeralHostWorkspace(tenure.root);
|
|
223
252
|
}
|
|
224
253
|
catch {
|
|
225
254
|
}
|
|
@@ -228,12 +257,13 @@ export class RemoteHostExecutionEnv {
|
|
|
228
257
|
return this.destroy();
|
|
229
258
|
}
|
|
230
259
|
get backgroundCapabilities() {
|
|
260
|
+
const backgroundShellOn = (this.cfg.backgroundShell ?? DEPLOYMENT_SHAPE_PREDICATES.backgroundShell.holds({ remoteExec: { provider: PROVIDER } })) && hostBackgroundShellEnabled();
|
|
231
261
|
return {
|
|
232
|
-
supported:
|
|
262
|
+
supported: backgroundShellOn,
|
|
233
263
|
maxConcurrent: HOST_BG_MAX_CONCURRENT,
|
|
234
264
|
defaultBgTimeoutSec: HOST_BG_DEFAULT_TIMEOUT_SEC,
|
|
235
265
|
maxBgTimeoutSec: HOST_BG_MAX_TIMEOUT_SEC,
|
|
236
|
-
supportsDetach:
|
|
266
|
+
supportsDetach: backgroundShellOn,
|
|
237
267
|
};
|
|
238
268
|
}
|
|
239
269
|
_bgManager;
|
|
@@ -975,7 +1005,7 @@ export class RemoteHostExecutionEnv {
|
|
|
975
1005
|
return ok(this.resolve(p));
|
|
976
1006
|
}
|
|
977
1007
|
async joinPath(parts) {
|
|
978
|
-
return ok(path
|
|
1008
|
+
return ok(joinedPath(path, parts));
|
|
979
1009
|
}
|
|
980
1010
|
async readTextFile(p, abortSignal) {
|
|
981
1011
|
const ready = await this.fsReady(p);
|
|
@@ -1077,7 +1107,7 @@ export class RemoteHostExecutionEnv {
|
|
|
1077
1107
|
const entries = await fs.readdir(abs, { withFileTypes: true });
|
|
1078
1108
|
const out = [];
|
|
1079
1109
|
for (const e of entries) {
|
|
1080
|
-
const child = path
|
|
1110
|
+
const child = anchorUnder(path, abs, e.name);
|
|
1081
1111
|
let size = 0;
|
|
1082
1112
|
let mtimeMs = 0;
|
|
1083
1113
|
try {
|
|
@@ -1218,7 +1248,7 @@ export class RemoteHostExecutionEnv {
|
|
|
1218
1248
|
});
|
|
1219
1249
|
}
|
|
1220
1250
|
resolve(p) {
|
|
1221
|
-
return
|
|
1251
|
+
return anchoredPath(path, this.cwd, p);
|
|
1222
1252
|
}
|
|
1223
1253
|
assertWorkspaceParentWritable() {
|
|
1224
1254
|
const uid = process.getuid?.();
|
|
@@ -1251,7 +1281,7 @@ export class RemoteHostExecutionEnv {
|
|
|
1251
1281
|
ensurePrepared() {
|
|
1252
1282
|
if (this.prepared)
|
|
1253
1283
|
return Promise.resolve();
|
|
1254
|
-
if (
|
|
1284
|
+
if (this.cfg.workspaceDir === undefined) {
|
|
1255
1285
|
try {
|
|
1256
1286
|
this.assertWorkspaceParentWritable();
|
|
1257
1287
|
}
|
|
@@ -1264,8 +1294,8 @@ export class RemoteHostExecutionEnv {
|
|
|
1264
1294
|
.mkdir(this.workspaceDir, { recursive: true })
|
|
1265
1295
|
.then(() => {
|
|
1266
1296
|
this.prepared = true;
|
|
1267
|
-
if (
|
|
1268
|
-
trackEphemeralHostWorkspace(this.
|
|
1297
|
+
if (this.tenure.kind === "held")
|
|
1298
|
+
trackEphemeralHostWorkspace(this.tenure.root);
|
|
1269
1299
|
})
|
|
1270
1300
|
.catch((e) => {
|
|
1271
1301
|
this.preparing = undefined;
|
|
@@ -1295,17 +1325,25 @@ export class RemoteHostExecutionEnv {
|
|
|
1295
1325
|
const abs = this.resolve(p);
|
|
1296
1326
|
if (!existsSync(abs))
|
|
1297
1327
|
return false;
|
|
1298
|
-
const real = (x) => {
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1328
|
+
const real = (x) => {
|
|
1329
|
+
try {
|
|
1330
|
+
return kernelRealPathOf(x);
|
|
1331
|
+
}
|
|
1332
|
+
catch {
|
|
1333
|
+
return undefined;
|
|
1334
|
+
}
|
|
1335
|
+
};
|
|
1336
|
+
const target = real(abs);
|
|
1337
|
+
if (target === undefined)
|
|
1338
|
+
return false;
|
|
1304
1339
|
const inside = (root) => {
|
|
1305
|
-
const
|
|
1340
|
+
const r = real(root);
|
|
1341
|
+
if (r === undefined)
|
|
1342
|
+
return false;
|
|
1343
|
+
const rel = path.relative(r, target);
|
|
1306
1344
|
return rel !== "" && !rel.startsWith("..") && !path.isAbsolute(rel);
|
|
1307
1345
|
};
|
|
1308
|
-
return (this.spoolRootDir !== undefined && inside(this.spoolRootDir)) || (this.
|
|
1346
|
+
return (this.spoolRootDir !== undefined && inside(this.spoolRootDir)) || (this.tenure.kind === "borrowed" && inside(this.workspaceDir));
|
|
1309
1347
|
}
|
|
1310
1348
|
async fsReady(p, opts) {
|
|
1311
1349
|
if (this.destroyed) {
|
|
@@ -1389,11 +1427,39 @@ function asError(e) {
|
|
|
1389
1427
|
function signalNumber(signal) {
|
|
1390
1428
|
return os.constants.signals[signal];
|
|
1391
1429
|
}
|
|
1392
|
-
export function hostExecutionEnvFactory(config = {}) {
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1430
|
+
export function hostExecutionEnvFactory(config = {}, legOf = () => ({})) {
|
|
1431
|
+
const custody = createWorkspaceCustody();
|
|
1432
|
+
const heldElsewhere = (self, root) => custody.holder((m) => m !== self && m.heldWorkspace === root) !== undefined;
|
|
1433
|
+
return (ctx) => {
|
|
1434
|
+
const { minted, ...leg } = legOf(ctx);
|
|
1435
|
+
const identity = ctx.taskId ?? ctx.sessionId;
|
|
1436
|
+
return custody.envOf(identity, () => {
|
|
1437
|
+
const dir = leg.workspaceDir ?? config.workspaceDir;
|
|
1438
|
+
const sharedRoot = dir === undefined ? undefined : custody.holder((m) => m.heldWorkspace !== undefined && dirWithin(dir, m.heldWorkspace))?.heldWorkspace;
|
|
1439
|
+
const env = new RemoteHostExecutionEnv({ ...config, ...leg, id: config.id ?? identity, custody: { heldElsewhere, ...(sharedRoot !== undefined ? { sharedRoot } : {}) } });
|
|
1440
|
+
minted?.(env);
|
|
1441
|
+
return env;
|
|
1442
|
+
});
|
|
1443
|
+
};
|
|
1444
|
+
}
|
|
1445
|
+
function dirWithin(dir, root) {
|
|
1446
|
+
const inside = (d, r) => {
|
|
1447
|
+
const rel = path.relative(r, d);
|
|
1448
|
+
return rel === "" || (rel.split(path.sep)[0] !== ".." && !path.isAbsolute(rel));
|
|
1449
|
+
};
|
|
1450
|
+
return inside(path.resolve(dir), path.resolve(root)) || inside(physicalPathOf(dir), physicalPathOf(root));
|
|
1451
|
+
}
|
|
1452
|
+
function physicalPathOf(p) {
|
|
1453
|
+
let head = path.resolve(p);
|
|
1454
|
+
const tail = [];
|
|
1455
|
+
while (!existsSync(head)) {
|
|
1456
|
+
const up = path.dirname(head);
|
|
1457
|
+
if (up === head)
|
|
1458
|
+
return path.resolve(p);
|
|
1459
|
+
tail.unshift(path.basename(head));
|
|
1460
|
+
head = up;
|
|
1461
|
+
}
|
|
1462
|
+
return path.join(realpathSync(head), ...tail);
|
|
1397
1463
|
}
|
|
1398
1464
|
export { localDockerExecutionEnvFactory, RemoteLocalDockerExecutionEnv } from "./remote-env-local-docker.js";
|
|
1399
1465
|
//# sourceMappingURL=remote-env-host.js.map
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
|
+
import { anchoredPath, joinedPath } from "./exec-path-law.js";
|
|
2
3
|
import { readFileSync } from "node:fs";
|
|
3
4
|
import https from "node:https";
|
|
4
5
|
import http from "node:http";
|
|
@@ -19,6 +20,7 @@ import { buildCapturedOutput, createCutError } from "../exec-cut.js";
|
|
|
19
20
|
import { SANDBOX_IPV6_DEFAULT, sandboxEgressGaps, sandboxPodSysctls } from "../sandbox-egress-policy.js";
|
|
20
21
|
import { FileError, ExecutionError, RemoteExecutionError, withRetry, RollingTailBuffer, markTruncated, } from "@sema-agent/core";
|
|
21
22
|
import { capabilitiesFor } from "../execution-lane-caps.js";
|
|
23
|
+
import { DEPLOYMENT_SHAPE_PREDICATES } from "../deployment-shape.js";
|
|
22
24
|
import { ADAPTER_START, BOOTSTRAP_START, buildFloorPodSeat, createSandboxReadFloor, FLOOR_K8S_CONFIGMAP_KEY, engineStart, floorArgv, } from "./sandbox-read-floor.js";
|
|
23
25
|
import { fileErrorFromExec, classifyFsStderr } from "./remote-env-file-error.js";
|
|
24
26
|
import { createPosixShellFs } from "./posix-shell-fs.js";
|
|
@@ -283,12 +285,13 @@ export class RemoteK8sExecutionEnv {
|
|
|
283
285
|
this.floor = this.deps.readFloor ?? createSandboxReadFloor({ lane: PROVIDER, knob: config.sandboxReadFloor, ...(config.logger !== undefined ? { logger: config.logger } : {}) });
|
|
284
286
|
this.capabilities = capabilitiesFor({ remoteExec: { provider: PROVIDER, s3Snapshot: this.cfg.s3Snapshot }, sandboxReadFloor: config.sandboxReadFloor });
|
|
285
287
|
const bgMax = Math.max(1, Math.min(K8S_BG_MAX_TIMEOUT_SEC, Math.floor(this.cfg.timeoutMs / 1000)));
|
|
288
|
+
const backgroundShellOn = DEPLOYMENT_SHAPE_PREDICATES.backgroundShell.holds({ remoteExec: { provider: PROVIDER } });
|
|
286
289
|
this.backgroundCapabilities = {
|
|
287
|
-
supported:
|
|
290
|
+
supported: backgroundShellOn,
|
|
288
291
|
maxConcurrent: K8S_BG_MAX_CONCURRENT,
|
|
289
292
|
defaultBgTimeoutSec: Math.min(K8S_BG_DEFAULT_TIMEOUT_SEC, bgMax),
|
|
290
293
|
maxBgTimeoutSec: bgMax,
|
|
291
|
-
supportsDetach:
|
|
294
|
+
supportsDetach: backgroundShellOn,
|
|
292
295
|
};
|
|
293
296
|
}
|
|
294
297
|
get sandboxLeaseRef() {
|
|
@@ -298,7 +301,7 @@ export class RemoteK8sExecutionEnv {
|
|
|
298
301
|
return this.cfg.timeoutMs;
|
|
299
302
|
}
|
|
300
303
|
resolve(p) {
|
|
301
|
-
return
|
|
304
|
+
return anchoredPath(path.posix, this.cwd, p);
|
|
302
305
|
}
|
|
303
306
|
workspaceHandle() {
|
|
304
307
|
return { sandboxId: this.podName ?? "(unconnected)", provider: PROVIDER, mountPath: this.cwd, ...(this.handleSnapshotId ? { snapshotId: this.handleSnapshotId } : {}) };
|
|
@@ -2194,7 +2197,7 @@ export class RemoteK8sExecutionEnv {
|
|
|
2194
2197
|
return ok(this.resolve(p));
|
|
2195
2198
|
}
|
|
2196
2199
|
async joinPath(parts) {
|
|
2197
|
-
return ok(path.posix
|
|
2200
|
+
return ok(joinedPath(path.posix, parts));
|
|
2198
2201
|
}
|
|
2199
2202
|
async readBinaryFile(p, abortSignal) {
|
|
2200
2203
|
const abs = this.resolve(p);
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { spawn } from "node:child_process";
|
|
2
2
|
import { type LaneHomeConfig } from "../lane-home-probe.js";
|
|
3
3
|
import { type LaneLogger } from "./remote-shell.js";
|
|
4
|
+
import { type CustodyMember } from "./workspace-custody.js";
|
|
4
5
|
import { FileError, ExecutionError, RemoteExecutionError, type ExecutionEnv, type RemoteExecutionEnv, type WorkspaceHandle, type FileInfo, type Result, type SymlinkChain, type OutputChunk, type ExecStreamOptions, type RemoteConnectConfig, type SnapshotId, type SessionToken, type VmLifecycleOptions, type ExecutionEnvFactory, type ExecutionEnvCapabilities } from "@sema-agent/core";
|
|
5
6
|
import { type SandboxReadFloorKnob } from "../execution-lane-caps.js";
|
|
6
7
|
import { type SandboxReadFloor } from "./sandbox-read-floor.js";
|
|
@@ -105,7 +106,7 @@ export interface LocalDockerEnvDeps {
|
|
|
105
106
|
/** 这只环境手里的读地板模块(缺席 ⇒ `createSandboxReadFloor` 按旋钮选;钉注入守地板的模块)。能力块的 `keepsReadFloor` 读它的状态。 */
|
|
106
107
|
readFloor?: SandboxReadFloor;
|
|
107
108
|
}
|
|
108
|
-
export declare class RemoteLocalDockerExecutionEnv implements RemoteExecutionEnv {
|
|
109
|
+
export declare class RemoteLocalDockerExecutionEnv implements CustodyMember<LocalDockerLeftover>, RemoteExecutionEnv {
|
|
109
110
|
/** 能力块的唯一作者 = `capabilitiesFor`(`execution-lane-caps.ts`;逐键表态与理由在那里,本类零能力字面量)。读地板旋钮是它的输入,{@link floor} 由同一只判据、同一个旋钮值选出。 */
|
|
110
111
|
readonly capabilities: ExecutionEnvCapabilities;
|
|
111
112
|
/** 读拒表字节地板模块:容器里每一次 `docker exec` 起动都先经它的 `wrap`({@link startArgv},唯一接缝)。 */
|
|
@@ -155,8 +156,10 @@ export declare class RemoteLocalDockerExecutionEnv implements RemoteExecutionEnv
|
|
|
155
156
|
get leftover(): LocalDockerLeftover | undefined;
|
|
156
157
|
/** 收场的进度:活着 ⇒ 缺席;{@link destroy} 开始 ⇒ 那一次收场(容器删除证实或放弃之后落定)。工厂读它:活着就交回、收场中就等它落定。 */
|
|
157
158
|
get releasing(): Promise<void> | undefined;
|
|
158
|
-
/**
|
|
159
|
+
/** 收场已落定。 */
|
|
159
160
|
get released(): boolean;
|
|
161
|
+
/** 收场落定、不欠删除 ⇒ 出托管表(欠着的由同一身份的下一只拿走)。容器名由身份派生,一只身份同一时刻只有一只没收场的环境 ⇒ 它收场即删,不存在第二个持有者。 */
|
|
162
|
+
get settled(): boolean;
|
|
160
163
|
private resolve;
|
|
161
164
|
/** 收据句柄。还没连上(懒环境:续跑腿的引擎在恢复相**之前**先取它比车道)⇒ 未连接形 `{ sandboxId: "(unconnected)", provider, mountPath }`(与 k8s / e2b 车道同形),
|
|
162
165
|
* 不抛 —— 抛了,续跑腿在恢复相就失败。 */
|
|
@@ -393,7 +396,7 @@ export declare function localDockerIdentityOfName(workerLabel: string, name: str
|
|
|
393
396
|
* owns the lifetime and calls `destroy()` (= `docker rm -f`) on task end.
|
|
394
397
|
*
|
|
395
398
|
* 身份 = `ctx.taskId ?? ctx.sessionId`(与引擎交给工具的宿主任务号同一读法:顶层 run = run 号,委派子代 = 它自己的会话号 —— 子代从不带 `taskId`)。
|
|
396
|
-
*
|
|
399
|
+
* 一只身份一只工作区(工作区托管表 `workspace-custody.ts`,与 host 车道同一只):本工厂对同一身份交回**那一只**还没销毁的环境(停驻腿的环境引擎还拿着;续跑腿拿到的就是它 —— 引擎 park-only 契约要的「重连到同一工作区」,
|
|
397
400
|
* 在同一进程里就是同一只环境,读地板的中继也只有一只);上一只正在收场 ⇒ 等它的删除落定再铸;没有 ⇒ 铸一只(它连接时在跑的同名容器就附着 —— 上一只进程起的)。
|
|
398
401
|
* 收场落定的从表里清掉。
|
|
399
402
|
* 无主容器:停驻中取消 / 停驻过期(本服务在店层结清,不经引擎的仲裁器,停驻腿的环境没有销毁者)与重起之后才结清的停驻,容器一直在跑 ——
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
|
+
import { anchoredPath, joinedPath } from "./exec-path-law.js";
|
|
2
3
|
import { spawn } from "node:child_process";
|
|
3
4
|
import { hostSubprocessEnv } from "../server-secret-env.js";
|
|
4
5
|
import { LANE_HOME_PROBE_COMMAND, LaneHomeSeat } from "../lane-home-probe.js";
|
|
5
6
|
import { createHash, randomBytes } from "node:crypto";
|
|
6
7
|
import { shellQuote, armPipeDestroyGrace, appendFileStaged, readyUpload, writeFileStaged } from "./remote-shell.js";
|
|
8
|
+
import { createWorkspaceCustody } from "./workspace-custody.js";
|
|
7
9
|
import { signalNumber } from "./host-platform.js";
|
|
8
10
|
import { buildLivenessCut, commandBoundsOf } from "../exec-bounds.js";
|
|
9
11
|
import { buildCapturedOutput, createCutError } from "../exec-cut.js";
|
|
@@ -84,8 +86,11 @@ export class RemoteLocalDockerExecutionEnv {
|
|
|
84
86
|
get released() {
|
|
85
87
|
return this.teardownSettled;
|
|
86
88
|
}
|
|
89
|
+
get settled() {
|
|
90
|
+
return this.teardownSettled && this.leftover === undefined;
|
|
91
|
+
}
|
|
87
92
|
resolve(p) {
|
|
88
|
-
return
|
|
93
|
+
return anchoredPath(path.posix, this.cwd, p);
|
|
89
94
|
}
|
|
90
95
|
workspaceHandle() {
|
|
91
96
|
return this.handle ?? { sandboxId: "(unconnected)", provider: PROVIDER, mountPath: this.cfg.mountPath };
|
|
@@ -614,7 +619,7 @@ export class RemoteLocalDockerExecutionEnv {
|
|
|
614
619
|
return ok(this.resolve(p));
|
|
615
620
|
}
|
|
616
621
|
async joinPath(parts) {
|
|
617
|
-
return ok(path.posix
|
|
622
|
+
return ok(joinedPath(path.posix, parts));
|
|
618
623
|
}
|
|
619
624
|
async readBinaryFile(p, abortSignal) {
|
|
620
625
|
const ready = await this.fsReady(p, abortSignal);
|
|
@@ -974,30 +979,11 @@ function errMsg(e) {
|
|
|
974
979
|
}
|
|
975
980
|
export function localDockerExecutionEnvFactory(config, deps = {}) {
|
|
976
981
|
const { workspaceFillOf, ...base } = config;
|
|
977
|
-
const
|
|
978
|
-
const envOf =
|
|
979
|
-
sweep();
|
|
980
|
-
let leftover;
|
|
981
|
-
for (let held = live.get(identity); held !== undefined; held = live.get(identity)) {
|
|
982
|
-
const releasing = held.releasing;
|
|
983
|
-
if (releasing === undefined)
|
|
984
|
-
return held;
|
|
985
|
-
await releasing;
|
|
986
|
-
if (live.get(identity) === held) {
|
|
987
|
-
leftover = held.leftover;
|
|
988
|
-
live.delete(identity);
|
|
989
|
-
}
|
|
990
|
-
}
|
|
982
|
+
const custody = createWorkspaceCustody();
|
|
983
|
+
const envOf = (identity, ctx) => custody.envOf(identity, (leftover) => {
|
|
991
984
|
const workspaceFill = ctx !== undefined ? workspaceFillOf?.(ctx) : undefined;
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
return env;
|
|
995
|
-
};
|
|
996
|
-
const sweep = () => {
|
|
997
|
-
for (const [identity, env] of live)
|
|
998
|
-
if (env.released && env.leftover === undefined)
|
|
999
|
-
live.delete(identity);
|
|
1000
|
-
};
|
|
985
|
+
return new RemoteLocalDockerExecutionEnv({ ...base, id: identity, ...(workspaceFill !== undefined ? { workspaceFill } : {}), ...(leftover !== undefined ? { leftover } : {}) }, deps);
|
|
986
|
+
});
|
|
1001
987
|
let lister;
|
|
1002
988
|
const reapPort = {
|
|
1003
989
|
label: base.workerLabel,
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
|
+
import { anchoredPath, anchorUnder, joinedPath } from "./exec-path-law.js";
|
|
2
3
|
import { Client } from "ssh2";
|
|
3
4
|
import { createSshHostKeyVerifier } from "../ssh-host-key.js";
|
|
4
5
|
import { appendFileStaged, buildCwdEnvCommand, createCwdGuardedCall, kindFromMode, writeFileStaged } from "./remote-shell.js";
|
|
@@ -57,7 +58,7 @@ export class RemoteSshExecutionEnv {
|
|
|
57
58
|
this.homeSeat = new LaneHomeSeat(PROVIDER, config);
|
|
58
59
|
}
|
|
59
60
|
resolve(p) {
|
|
60
|
-
return
|
|
61
|
+
return anchoredPath(path.posix, this.cwd, p);
|
|
61
62
|
}
|
|
62
63
|
workspaceHandle() {
|
|
63
64
|
return { sandboxId: `${this.cfg.host}:${this.cfg.port}`, provider: PROVIDER, mountPath: this.cwd };
|
|
@@ -595,7 +596,7 @@ export class RemoteSshExecutionEnv {
|
|
|
595
596
|
return ok(this.resolve(p));
|
|
596
597
|
}
|
|
597
598
|
async joinPath(parts) {
|
|
598
|
-
return ok(path.posix
|
|
599
|
+
return ok(joinedPath(path.posix, parts));
|
|
599
600
|
}
|
|
600
601
|
async readTextFile(p, abortSignal) {
|
|
601
602
|
const r = await this.readBinaryFile(p, abortSignal);
|
|
@@ -674,7 +675,7 @@ export class RemoteSshExecutionEnv {
|
|
|
674
675
|
const entries = await new Promise((resolve, reject) => sftp.readdir(abs, (err, list) => (err ? reject(err) : resolve(list))));
|
|
675
676
|
return ok(entries.map((e) => {
|
|
676
677
|
const a = e.attrs;
|
|
677
|
-
return { name: e.filename, path: path.posix
|
|
678
|
+
return { name: e.filename, path: anchorUnder(path.posix, abs, e.filename), kind: kindFromMode(a.mode), size: a.size ?? 0, mtimeMs: (a.mtime ?? 0) * 1000 };
|
|
678
679
|
}));
|
|
679
680
|
}
|
|
680
681
|
catch (e) {
|
|
@@ -60,7 +60,9 @@ export type CwdGuardFace = "settled" | "raw";
|
|
|
60
60
|
* 🔴 [ref](7.103.1)—— 三条远端 shell 腿(ssh / k8s / adb)交给远端 `sh -c` 的那一串**唯一**的组装:进工作目录 →(settled 面)打 started 行 →
|
|
61
61
|
* 导出任务 env → 跑命令。各腿只决定 `dir`(自己的缺省 cwd 规则 + 路径绝对化),串的形状只在这里。
|
|
62
62
|
*
|
|
63
|
-
* 形:`cd '<dir>' || { echo '<marker>: <dir> (cd failed; command not run)' >&2; exit 125; }; echo '<started>' >&2; export K='v'; … <command>`
|
|
63
|
+
* 形:`cd -P '<dir>' || { echo '<marker>: <dir> (cd failed; command not run)' >&2; exit 125; }; echo '<started>' >&2; export K='v'; … <command>`
|
|
64
|
+
* · `-P`(物理):工作目录按内核解 —— 执行缝路径律下 `<dir>` 是未折的拼法(`<base>/bridge/../next`),文件面由内核解 `..`;缺省的逻辑 `cd` 会先词法折成 `<base>/next`,
|
|
65
|
+
* 命令与文件动作就各在一只目录。`-P` 是 POSIX `cd` 的选项(dash / bash / busybox ash / mksh 都认)。
|
|
64
66
|
* (raw 面没有 `echo '<started>' >&2;` 那一段)
|
|
65
67
|
* · cd 失败(目录不存在 / 无权限 / 不是目录)⇒ 命令**不跑**,整条退 {@link CWD_UNAVAILABLE_EXIT};stderr 先是 shell 自己的 cd 报错,再是守卫行。
|
|
66
68
|
* 修前形 `cd '<dir>' && export K='v'; <command>`:`&&` 只绑到第一只 `export`,`;` 之后的命令照样在**登录目录**里跑,还少了那只 env ——
|
|
@@ -30,7 +30,7 @@ export function buildCwdEnvCommand(command, dir, env, face) {
|
|
|
30
30
|
const nonce = randomBytes(8).toString("hex");
|
|
31
31
|
cwdGuard = { dir, marker: `${CWD_UNAVAILABLE_LINE} [${nonce}]`, started: startedLineFor(face, nonce) };
|
|
32
32
|
const line = `${cwdGuard.marker}: ${dir} (cd failed; command not run)`;
|
|
33
|
-
parts.push(`cd ${shellQuote(dir)} || { echo ${shellQuote(line)} >&2; exit ${CWD_UNAVAILABLE_EXIT}; };`);
|
|
33
|
+
parts.push(`cd -P ${shellQuote(dir)} || { echo ${shellQuote(line)} >&2; exit ${CWD_UNAVAILABLE_EXIT}; };`);
|
|
34
34
|
if (cwdGuard.started !== undefined)
|
|
35
35
|
parts.push(`echo ${shellQuote(cwdGuard.started)} >&2;`);
|
|
36
36
|
}
|
|
@@ -184,11 +184,11 @@ function hardenPrivate(dir) {
|
|
|
184
184
|
return `( chmod -- 700 ${dir} 2>/dev/null && ${readModeOwner(dir)} && case "$1" in drw[xs]--[-S]--[-T]*) [ "$3" = "$(id -u)" ];; *) false;; esac && ${aclCleared(dir)} )`;
|
|
185
185
|
}
|
|
186
186
|
function aclCleared(dir) {
|
|
187
|
-
const marker = `ls_out=$(cd ${dir} && ls -ld . 2>&1) && [ -z "$(printf '%s\\n' "$ls_out" | sed -n 2p)" ] && mode_word=\${ls_out%% *} && ` +
|
|
187
|
+
const marker = `ls_out=$(cd -P ${dir} && ls -ld . 2>&1) && [ -z "$(printf '%s\\n' "$ls_out" | sed -n 2p)" ] && mode_word=\${ls_out%% *} && ` +
|
|
188
188
|
`case "$mode_word" in d?????????*+*) echo +;; d?????????*) ;; *) false;; esac`;
|
|
189
189
|
return (`plat=$(uname -s 2>/dev/null) && case "$plat" in ` +
|
|
190
190
|
`Linux) acl_read() { :; }; acl_strip() { false; };; ` +
|
|
191
|
-
`Darwin) acl_read() { ls_out=$(cd ${dir} && ls -led . 2>&1) && case "$ls_out" in d?????????*) ;; *) false;; esac && printf '%s\\n' "$ls_out" | sed 1d; }; acl_strip() { chmod -N ${dir}; };; ` +
|
|
191
|
+
`Darwin) acl_read() { ls_out=$(cd -P ${dir} && ls -led . 2>&1) && case "$ls_out" in d?????????*) ;; *) false;; esac && printf '%s\\n' "$ls_out" | sed 1d; }; acl_strip() { chmod -N ${dir}; };; ` +
|
|
192
192
|
`FreeBSD) acl_read() { ${marker}; }; acl_strip() { setfacl -b ${dir}; };; ` +
|
|
193
193
|
`SunOS) acl_read() { ${marker}; }; acl_strip() { chmod A- ${dir}; };; ` +
|
|
194
194
|
`*) false;; esac && acl=$(acl_read 2>/dev/null) && { [ -z "$acl" ] || { acl_strip 2>/dev/null && acl=$(acl_read 2>/dev/null) && [ -z "$acl" ]; }; }`);
|