@evelandhq/sandbox-bwrap 0.4.1 → 0.5.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/README.md CHANGED
@@ -120,8 +120,11 @@ directory — it is durable state. Cleanup closes that compute generation to new
120
120
  the next backend `create()` opens a fresh generation over the same workspace. Repeated
121
121
  `create()` calls through one backend instance share the live generation, so one handle
122
122
  cannot race a new spawn past another handle's cleanup barrier. The provider's
123
- `onSessionStop()` and `onRuntimeShutdown()` do the same, and its `resume()` shares the live
124
- generation the same way.
123
+ `onSandboxStop()` and `onRuntimeShutdown()` do the same, and its `resume()` shares the live
124
+ generation the same way. eve 0.75 renamed the handle hooks from `onSessionStop()` and
125
+ `onSessionDelete()` to `onSandboxStop()` and `onSandboxDelete()` and calls only the new names,
126
+ while eve 0.64 through 0.74 call only the old ones; the handle carries both pairs, each pair
127
+ the same function, so one installed copy serves every eve in the peer range.
125
128
 
126
129
  ### Options
127
130
 
@@ -163,8 +166,12 @@ The provider (eve 0.64 and later):
163
166
  - **resume** (every later access, and after restarts): reopens the recorded workspace and
164
167
  applies the recorded network policy to a new compute generation. It fails instead of
165
168
  recreating a workspace that is gone.
166
- - **onSessionDelete**: kills the session's processes and removes its workspace and
167
- metadata; the template survives.
169
+ - **onSandboxDelete** (`onSessionDelete` before eve 0.75): kills the session's processes
170
+ and removes its workspace and metadata; the template survives.
171
+ - **onSessionEnd** (eve 0.75 and later, once a durable session has completed, expired or
172
+ failed): releases the session's workspace and metadata from the persisted state alone,
173
+ stopping the live generation if this process holds one, without resuming the sandbox. It
174
+ is idempotent, so eve may retry it, and a workspace that is already gone stays gone.
168
175
 
169
176
  The backend (eve 0.62 and 0.63):
170
177
 
package/dist/index.d.ts CHANGED
@@ -3,7 +3,7 @@ import type { BwrapSandboxCreateOptions } from "./options.js";
3
3
  export { BWRAP_BACKEND_NAME, createBwrapSandboxBackend, type CreateBwrapSandboxBackendInput, } from "./backend.js";
4
4
  export type { BwrapBackendBootstrapContext, BwrapBackendCreateInput, BwrapBackendHandle, BwrapBackendPrewarmInput, BwrapBackendRuntimeContext, BwrapBackendSessionState, BwrapSandboxBackend, BwrapSeedFile, } from "./backend-contract.js";
5
5
  export { BwrapTemplateNotProvisionedError } from "./errors.js";
6
- export { BWRAP_PROVIDER_STATE_PROTOCOL_VERSION, createBwrapSandboxProviderDefinition, type BwrapPreparedArtifact, type BwrapSandboxEnvironmentOptions, type BwrapSandboxOpenOptions, type BwrapSandboxProviderDefinition, type BwrapSessionState, type CreateBwrapSandboxProviderDefinitionInput, type MutableNetworkSandboxSession, } from "./provider-definition.js";
6
+ export { BWRAP_PROVIDER_STATE_PROTOCOL_VERSION, createBwrapSandboxProviderDefinition, type BwrapPreparedArtifact, type BwrapSandboxEnvironmentOptions, type BwrapSandboxOpenOptions, type BwrapSandboxProviderDefinition, type BwrapSessionState, type CreateBwrapSandboxProviderDefinitionInput, type BwrapProviderHandle, type BwrapSandboxDeleteOptions, type MutableNetworkSandboxSession, } from "./provider-definition.js";
7
7
  export type { BwrapSession } from "./session.js";
8
8
  export type { BwrapNetworkPolicy, BwrapSandboxCreateOptions, BwrapSandboxUseOptions, } from "./options.js";
9
9
  export type { BwrapCommandFinishReason, BwrapSandboxEvent, BwrapSandboxEventSink, } from "./events.js";
@@ -1,5 +1,5 @@
1
1
  import type { SandboxNetworkPolicy, SandboxSession } from "eve/sandbox";
2
- import type { SandboxProviderDefinition } from "eve/sandbox/provider";
2
+ import type { SandboxProviderDefinition, SandboxProviderHandle } from "eve/sandbox/provider";
3
3
  import { type BwrapDirectoryCopier } from "./cache.js";
4
4
  import type { BwrapNetworkPolicy, BwrapSandboxCreateOptions } from "./options.js";
5
5
  import type { ProcessRunner } from "./process.js";
@@ -18,6 +18,19 @@ export declare const BWRAP_PROVIDER_STATE_PROTOCOL_VERSION = 1;
18
18
  export type MutableNetworkSandboxSession = SandboxSession & {
19
19
  setNetworkPolicy(policy: SandboxNetworkPolicy): Promise<void>;
20
20
  };
21
+ /**
22
+ * The handle this provider hands eve. eve 0.75 renamed the handle's lifecycle
23
+ * hooks from `onSessionStop`/`onSessionDelete` to `onSandboxStop`/`onSandboxDelete`
24
+ * and calls only the new names, while 0.64 through 0.74 call only the old ones.
25
+ * One installed copy of this package serves every eve in the peer range, so the
26
+ * handle answers to both names, each pair being the same function.
27
+ */
28
+ export type BwrapProviderHandle = SandboxProviderHandle<MutableNetworkSandboxSession> & {
29
+ onSessionStop(): Promise<void>;
30
+ onSessionDelete(options?: BwrapSandboxDeleteOptions): Promise<void>;
31
+ };
32
+ /** What eve passes when it deletes a sandbox: at most an abort signal. */
33
+ export type BwrapSandboxDeleteOptions = Parameters<SandboxProviderHandle<MutableNetworkSandboxSession>["onSandboxDelete"]>[0];
21
34
  /** Options for `BwrapSandbox.environment(...)`. */
22
35
  export type BwrapSandboxEnvironmentOptions = Omit<BwrapSandboxCreateOptions, "templateRevision"> & {
23
36
  /**
@@ -52,31 +52,38 @@ export function createBwrapSandboxProviderDefinition(input = {}) {
52
52
  leaseRoot: roots.sessionRoot,
53
53
  });
54
54
  await applyNetworkPolicy(session, created);
55
+ // The processes are the compute and the workspace directory is the
56
+ // durable session: stopping kills the processes and keeps the files.
57
+ const stop = async () => {
58
+ await session.killAll();
59
+ };
60
+ // The session's own workspace is disposable; the template it was
61
+ // cloned from is shared by every other session and must survive.
62
+ const remove = async (deleteOptions) => {
63
+ deleteOptions?.abortSignal?.throwIfAborted();
64
+ await session.killAll();
65
+ runtime.forgetRuntimeSession(sessionPath);
66
+ await removeSessionWorkspace(roots.sessionRoot, sessionPath);
67
+ };
55
68
  return {
56
69
  sandbox: session,
57
- // The processes are the compute and the workspace directory is the
58
- // durable session: stopping kills the processes and keeps the files.
59
- async onSessionStop() {
60
- await session.killAll();
61
- },
70
+ onSandboxStop: stop,
71
+ onSessionStop: stop,
62
72
  async onRuntimeShutdown() {
63
73
  await session.killAll();
64
74
  },
65
- // The session's own workspace is disposable; the template it was
66
- // cloned from is shared by every other session and must survive.
67
- async onSessionDelete(deleteOptions) {
68
- deleteOptions?.abortSignal?.throwIfAborted();
69
- await session.killAll();
70
- runtime.forgetRuntimeSession(sessionPath);
71
- await rm(sessionPath, { force: true, recursive: true });
72
- await removeCacheMetadata({
73
- cacheRoot: roots.sessionRoot,
74
- kind: "session",
75
- id: basename(sessionPath),
76
- });
77
- },
75
+ onSandboxDelete: remove,
76
+ onSessionDelete: remove,
78
77
  };
79
78
  }
79
+ async function removeSessionWorkspace(sessionRoot, sessionPath) {
80
+ await rm(sessionPath, { force: true, recursive: true });
81
+ await removeCacheMetadata({
82
+ cacheRoot: sessionRoot,
83
+ kind: "session",
84
+ id: basename(sessionPath),
85
+ });
86
+ }
80
87
  return {
81
88
  async prepare(ctx) {
82
89
  const templateKey = JSON.stringify({
@@ -176,6 +183,18 @@ export function createBwrapSandboxProviderDefinition(input = {}) {
176
183
  await session.setNetworkPolicy(state.networkPolicy);
177
184
  });
178
185
  },
186
+ // eve 0.75 releases a durable session's sandbox once the session has
187
+ // completed, expired or failed, from persisted state alone. Only the
188
+ // workspace is touched, never the template, and nothing is resumed to
189
+ // do it: a live generation in this process is stopped, a workspace that
190
+ // is already gone is left gone, so the step may retry.
191
+ async onSessionEnd(ctx, _preparedArtifact, sessionState) {
192
+ const state = requireSessionState(sessionState);
193
+ const roots = cacheRoots(ctx.storagePath);
194
+ const sessionPath = sessionPathIn(roots.sessionRoot, state.sessionKey);
195
+ await runtime.closeRuntimeSession(sessionPath);
196
+ await removeSessionWorkspace(roots.sessionRoot, sessionPath);
197
+ },
179
198
  };
180
199
  },
181
200
  };
@@ -1,4 +1,4 @@
1
- export { BWRAP_PROVIDER_STATE_PROTOCOL_VERSION, createBwrapSandboxProviderDefinition, type BwrapPreparedArtifact, type BwrapSandboxEnvironmentOptions, type BwrapSandboxOpenOptions, type BwrapSandboxProviderDefinition, type BwrapSessionState, type CreateBwrapSandboxProviderDefinitionInput, type MutableNetworkSandboxSession, } from "./provider-definition.js";
1
+ export { BWRAP_PROVIDER_STATE_PROTOCOL_VERSION, createBwrapSandboxProviderDefinition, type BwrapPreparedArtifact, type BwrapSandboxEnvironmentOptions, type BwrapSandboxOpenOptions, type BwrapSandboxProviderDefinition, type BwrapSessionState, type CreateBwrapSandboxProviderDefinitionInput, type BwrapProviderHandle, type BwrapSandboxDeleteOptions, type MutableNetworkSandboxSession, } from "./provider-definition.js";
2
2
  export { isBwrapAvailable } from "./process.js";
3
3
  /**
4
4
  * The bubblewrap sandbox provider for eve 0.64 and later.
package/dist/runtime.d.ts CHANGED
@@ -35,6 +35,12 @@ export interface BwrapRuntime {
35
35
  }>;
36
36
  /** Drops a deleted workspace's generation so the next open starts fresh. */
37
37
  forgetRuntimeSession(workspaceDir: string): void;
38
+ /**
39
+ * Stops the live generation over this workspace, if there is one, and drops
40
+ * it. Nothing is started for a workspace that has no generation, so this is
41
+ * safe to call for a session whose sandbox was never resumed in this process.
42
+ */
43
+ closeRuntimeSession(workspaceDir: string): Promise<void>;
38
44
  }
39
45
  export declare function createBwrapRuntime(input: {
40
46
  readonly options: ResolvedBwrapSandboxOptions;
package/dist/runtime.js CHANGED
@@ -68,6 +68,11 @@ export function createBwrapRuntime(input) {
68
68
  forgetRuntimeSession(workspaceDir) {
69
69
  generations.delete(workspaceDir);
70
70
  },
71
+ async closeRuntimeSession(workspaceDir) {
72
+ const current = generations.get(workspaceDir);
73
+ generations.delete(workspaceDir);
74
+ await current?.killAll();
75
+ },
71
76
  };
72
77
  }
73
78
  function resolveSeedPath(seedPath) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@evelandhq/sandbox-bwrap",
3
- "version": "0.4.1",
3
+ "version": "0.5.0",
4
4
  "description": "bubblewrap sandbox for eve agents — real exec sandboxing without Docker or KVM",
5
5
  "keywords": [
6
6
  "agent",
@@ -45,7 +45,7 @@
45
45
  "devDependencies": {
46
46
  "@types/node": "^26.0.1",
47
47
  "ai": "^7.0.44",
48
- "eve": "0.72.1",
48
+ "eve": "0.75.1",
49
49
  "eve-floor": "npm:eve@0.62.0",
50
50
  "oxfmt": "0.58.0",
51
51
  "oxlint": "1.73.0",