@flue/sdk 0.3.11 → 0.4.1

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.
@@ -1,9 +1,29 @@
1
- import { A as ShellResult, E as SessionEnv, d as FileStat, i as BashFactory, u as CommandDef, w as SandboxFactory } from "./types-DGpyKMFm.mjs";
1
+ import { E as SandboxFactory, M as ShellResult, O as SessionEnv, h as FlueFs, i as BashFactory, u as FileStat } from "./types-BAmV4f3Q.mjs";
2
2
 
3
3
  //#region src/sandbox.d.ts
4
+ /** Adapt a SessionEnv to the public FlueFs surface. */
5
+ declare function createFlueFs(env: SessionEnv): FlueFs;
4
6
  declare function createCwdSessionEnv(parentEnv: SessionEnv, cwd: string): SessionEnv;
5
7
  declare function bashFactoryToSessionEnv(factory: BashFactory): Promise<SessionEnv>;
6
- /** Interface that remote sandbox providers must implement. */
8
+ /**
9
+ * Interface that remote sandbox providers must implement.
10
+ *
11
+ * `exec()` cancellation is expressed two ways. Connectors should honor at
12
+ * least one — preferably `timeout`, since most provider SDKs expose a
13
+ * native timeout option but few support mid-flight cancellation:
14
+ *
15
+ * - `timeout?: number` (seconds): the **primary** cancellation contract.
16
+ * Forward to the provider's native timeout option (E2B `timeoutMs`,
17
+ * Daytona `timeout`, Modal `timeout`, etc.). Required for parity with
18
+ * the LLM bash tool, which always passes a `timeout` hint when the
19
+ * model requests one.
20
+ * - `signal?: AbortSignal` (optional): for connectors whose SDK supports
21
+ * mid-flight cancellation (Mirage's executor, in-process bash). Lets
22
+ * SDK callers do ad-hoc `abort()`. Connectors that can't honor it
23
+ * should ignore it; the deadline is still enforced via `timeout`.
24
+ *
25
+ * Connectors that support both should observe whichever fires first.
26
+ */
7
27
  interface SandboxApi {
8
28
  readFile(path: string): Promise<string>;
9
29
  readFileBuffer(path: string): Promise<Uint8Array>;
@@ -22,9 +42,10 @@ interface SandboxApi {
22
42
  cwd?: string;
23
43
  env?: Record<string, string>;
24
44
  timeout?: number;
45
+ signal?: AbortSignal;
25
46
  }): Promise<ShellResult>;
26
47
  }
27
48
  /** Wrap a SandboxApi into SessionEnv. No just-bash, no intermediate filesystem layer. */
28
- declare function createSandboxSessionEnv(api: SandboxApi, cwd: string, cleanup?: () => Promise<void>): SessionEnv;
49
+ declare function createSandboxSessionEnv(api: SandboxApi, cwd: string): SessionEnv;
29
50
  //#endregion
30
- export { type CommandDef, type FileStat, SandboxApi, type SandboxFactory, type SessionEnv, bashFactoryToSessionEnv, createCwdSessionEnv, createSandboxSessionEnv };
51
+ export { type FileStat, SandboxApi, type SandboxFactory, type SessionEnv, bashFactoryToSessionEnv, createCwdSessionEnv, createFlueFs, createSandboxSessionEnv };
package/dist/sandbox.mjs CHANGED
@@ -1,7 +1,22 @@
1
- import "./agent-Cahthgu3.mjs";
2
- import { i as normalizePath, o as createScopedEnv } from "./session-DlwIt7wq.mjs";
1
+ import "./result-K1IRhWKM.mjs";
2
+ import "./providers-DeFRIwp0.mjs";
3
+ import { t as abortErrorFor } from "./abort-Bg3qsAkU.mjs";
4
+ import { i as normalizePath } from "./session-CFOByKnM.mjs";
3
5
 
4
6
  //#region src/sandbox.ts
7
+ /** Adapt a SessionEnv to the public FlueFs surface. */
8
+ function createFlueFs(env) {
9
+ return {
10
+ readFile: (path) => env.readFile(path),
11
+ readFileBuffer: (path) => env.readFileBuffer(path),
12
+ writeFile: (path, content) => env.writeFile(path, content),
13
+ stat: (path) => env.stat(path),
14
+ readdir: (path) => env.readdir(path),
15
+ exists: (path) => env.exists(path),
16
+ mkdir: (path, options) => env.mkdir(path, options),
17
+ rm: (path, options) => env.rm(path, options)
18
+ };
19
+ }
5
20
  function createCwdSessionEnv(parentEnv, cwd) {
6
21
  const scopedCwd = normalizePath(cwd);
7
22
  const resolvePath = (p) => {
@@ -13,9 +28,9 @@ function createCwdSessionEnv(parentEnv, cwd) {
13
28
  exec: (cmd, opts) => parentEnv.exec(cmd, {
14
29
  cwd: opts?.cwd ?? scopedCwd,
15
30
  env: opts?.env,
16
- timeout: opts?.timeout
31
+ timeout: opts?.timeout,
32
+ signal: opts?.signal
17
33
  }),
18
- scope: async (options) => createCwdSessionEnv(await createScopedEnv(parentEnv, options?.commands ?? []), scopedCwd),
19
34
  readFile: (p) => parentEnv.readFile(resolvePath(p)),
20
35
  readFileBuffer: (p) => parentEnv.readFileBuffer(resolvePath(p)),
21
36
  writeFile: (p, c) => parentEnv.writeFile(resolvePath(p), c),
@@ -25,57 +40,31 @@ function createCwdSessionEnv(parentEnv, cwd) {
25
40
  mkdir: (p, o) => parentEnv.mkdir(resolvePath(p), o),
26
41
  rm: (p, o) => parentEnv.rm(resolvePath(p), o),
27
42
  cwd: scopedCwd,
28
- resolvePath,
29
- cleanup: () => parentEnv.cleanup()
43
+ resolvePath
30
44
  };
31
45
  }
32
46
  async function bashFactoryToSessionEnv(factory) {
33
- const seen = /* @__PURE__ */ new WeakSet();
34
- async function createBash() {
35
- const bash = await factory();
36
- assertBashLike(bash);
37
- if (seen.has(bash)) throw new Error("[flue] BashFactory must return a fresh Bash-like instance for each operation. Share the filesystem object in the factory closure to persist files across calls.");
38
- seen.add(bash);
39
- return bash;
40
- }
41
- async function createBashScopedEnv(commands) {
42
- const scoped = await createBash();
43
- registerCommands(scoped, commands);
44
- return createBashSessionEnv(scoped, createBashScopedEnv);
45
- }
46
- return createBashSessionEnv(await createBash(), createBashScopedEnv);
47
+ const bash = await factory();
48
+ assertBashLike(bash);
49
+ return createBashSessionEnv(bash);
47
50
  }
48
- function createBashSessionEnv(bash, createScope) {
51
+ function createBashSessionEnv(bash) {
49
52
  const fs = bash.fs;
50
53
  const cwd = bash.getCwd();
51
54
  const resolve = (p) => p.startsWith("/") ? p : fs.resolvePath(cwd, p);
52
55
  return {
53
56
  exec: async (cmd, opts) => {
54
57
  const exec = bash.exec;
55
- const timeout = opts?.timeout;
56
- let timeoutSignal;
57
- let timer;
58
- if (typeof timeout === "number") {
59
- const controller = new AbortController();
60
- timeoutSignal = controller.signal;
61
- timer = setTimeout(() => controller.abort(), timeout * 1e3);
62
- }
63
- try {
64
- const result = await exec.call(bash, cmd, opts ? {
65
- cwd: opts.cwd,
66
- env: opts.env,
67
- signal: timeoutSignal
68
- } : void 0);
69
- if (timeoutSignal?.aborted) return {
70
- ...result,
71
- stderr: result.stderr || `[flue] Command timed out after ${timeout} seconds.`
72
- };
73
- return result;
74
- } finally {
75
- if (timer) clearTimeout(timer);
76
- }
58
+ const timeoutSignal = typeof opts?.timeout === "number" ? AbortSignal.timeout(opts.timeout * 1e3) : void 0;
59
+ const mergedSignal = opts?.signal && timeoutSignal ? AbortSignal.any([opts.signal, timeoutSignal]) : opts?.signal ?? timeoutSignal;
60
+ const result = await exec.call(bash, cmd, opts ? {
61
+ cwd: opts.cwd,
62
+ env: opts.env,
63
+ signal: mergedSignal
64
+ } : void 0);
65
+ if (opts?.signal?.aborted) throw abortErrorFor(opts.signal);
66
+ return result;
77
67
  },
78
- scope: (options) => createScope(options?.commands ?? []),
79
68
  readFile: (p) => fs.readFile(resolve(p)),
80
69
  readFileBuffer: (p) => fs.readFileBuffer(resolve(p)),
81
70
  writeFile: async (p, content) => {
@@ -92,23 +81,14 @@ function createBashSessionEnv(bash, createScope) {
92
81
  mkdir: (p, o) => fs.mkdir(resolve(p), o),
93
82
  rm: (p, o) => fs.rm(resolve(p), o),
94
83
  cwd,
95
- resolvePath: resolve,
96
- cleanup: async () => {}
84
+ resolvePath: resolve
97
85
  };
98
86
  }
99
- function registerCommands(bash, commands) {
100
- if (commands.length === 0) return;
101
- if (typeof bash.registerCommand !== "function") throw new Error("[flue] Cannot use commands: this Bash-like sandbox does not support command registration.");
102
- for (const cmd of commands) bash.registerCommand({
103
- name: cmd.name,
104
- execute: cmd.execute
105
- });
106
- }
107
87
  function assertBashLike(value) {
108
88
  if (typeof value !== "object" || value === null || !("exec" in value) || !("getCwd" in value) || !("fs" in value) || typeof value.exec !== "function" || typeof value.getCwd !== "function" || typeof value.fs !== "object") throw new Error("[flue] BashFactory must return a Bash-like object.");
109
89
  }
110
90
  /** Wrap a SandboxApi into SessionEnv. No just-bash, no intermediate filesystem layer. */
111
- function createSandboxSessionEnv(api, cwd, cleanup) {
91
+ function createSandboxSessionEnv(api, cwd) {
112
92
  const resolvePath = (p) => {
113
93
  if (p.startsWith("/")) return normalizePath(p);
114
94
  if (cwd === "/") return normalizePath("/" + p);
@@ -116,11 +96,16 @@ function createSandboxSessionEnv(api, cwd, cleanup) {
116
96
  };
117
97
  return {
118
98
  async exec(command, options) {
119
- return api.exec(command, {
99
+ const signal = options?.signal;
100
+ if (signal?.aborted) throw abortErrorFor(signal);
101
+ const result = await api.exec(command, {
120
102
  cwd: options?.cwd ?? cwd,
121
103
  env: options?.env,
122
- timeout: options?.timeout
104
+ timeout: options?.timeout,
105
+ signal
123
106
  });
107
+ if (signal?.aborted) throw abortErrorFor(signal);
108
+ return result;
124
109
  },
125
110
  async readFile(path) {
126
111
  return api.readFile(resolvePath(path));
@@ -147,12 +132,9 @@ function createSandboxSessionEnv(api, cwd, cleanup) {
147
132
  return api.rm(resolvePath(path), options);
148
133
  },
149
134
  cwd,
150
- resolvePath,
151
- async cleanup() {
152
- if (cleanup) await cleanup();
153
- }
135
+ resolvePath
154
136
  };
155
137
  }
156
138
 
157
139
  //#endregion
158
- export { bashFactoryToSessionEnv, createCwdSessionEnv, createSandboxSessionEnv };
140
+ export { bashFactoryToSessionEnv, createCwdSessionEnv, createFlueFs, createSandboxSessionEnv };