sbox-sdk 0.0.2

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.
Files changed (132) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +137 -0
  3. package/dist/adapter/index.d.ts +22 -0
  4. package/dist/adapter/index.d.ts.map +1 -0
  5. package/dist/adapter/index.js +16 -0
  6. package/dist/agent-tools/index.d.ts +13 -0
  7. package/dist/agent-tools/index.d.ts.map +1 -0
  8. package/dist/agent-tools/index.js +9 -0
  9. package/dist/agent-tools/policy.d.ts +48 -0
  10. package/dist/agent-tools/policy.d.ts.map +1 -0
  11. package/dist/agent-tools/policy.js +51 -0
  12. package/dist/agent-tools/registry.d.ts +9 -0
  13. package/dist/agent-tools/registry.d.ts.map +1 -0
  14. package/dist/agent-tools/registry.js +412 -0
  15. package/dist/agent-tools/result.d.ts +32 -0
  16. package/dist/agent-tools/result.d.ts.map +1 -0
  17. package/dist/agent-tools/result.js +14 -0
  18. package/dist/agent-tools/types.d.ts +76 -0
  19. package/dist/agent-tools/types.d.ts.map +1 -0
  20. package/dist/agent-tools/types.js +1 -0
  21. package/dist/ai/index.d.ts +36 -0
  22. package/dist/ai/index.d.ts.map +1 -0
  23. package/dist/ai/index.js +40 -0
  24. package/dist/ai-sdk/index.d.ts +31 -0
  25. package/dist/ai-sdk/index.d.ts.map +1 -0
  26. package/dist/ai-sdk/index.js +80 -0
  27. package/dist/anthropic/index.d.ts +42 -0
  28. package/dist/anthropic/index.d.ts.map +1 -0
  29. package/dist/anthropic/index.js +64 -0
  30. package/dist/aws-lambda/index.d.ts +87 -0
  31. package/dist/aws-lambda/index.d.ts.map +1 -0
  32. package/dist/aws-lambda/index.js +290 -0
  33. package/dist/beam/index.d.ts +92 -0
  34. package/dist/beam/index.d.ts.map +1 -0
  35. package/dist/beam/index.js +222 -0
  36. package/dist/blaxel/index.d.ts +125 -0
  37. package/dist/blaxel/index.d.ts.map +1 -0
  38. package/dist/blaxel/index.js +220 -0
  39. package/dist/cli.d.ts +3 -0
  40. package/dist/cli.d.ts.map +1 -0
  41. package/dist/cli.js +249 -0
  42. package/dist/cloudflare/index.d.ts +64 -0
  43. package/dist/cloudflare/index.d.ts.map +1 -0
  44. package/dist/cloudflare/index.js +259 -0
  45. package/dist/codesandbox/index.d.ts +100 -0
  46. package/dist/codesandbox/index.d.ts.map +1 -0
  47. package/dist/codesandbox/index.js +227 -0
  48. package/dist/conformance/index.d.ts +20 -0
  49. package/dist/conformance/index.d.ts.map +1 -0
  50. package/dist/conformance/index.js +189 -0
  51. package/dist/daytona/index.d.ts +64 -0
  52. package/dist/daytona/index.d.ts.map +1 -0
  53. package/dist/daytona/index.js +258 -0
  54. package/dist/e2b/index.d.ts +63 -0
  55. package/dist/e2b/index.d.ts.map +1 -0
  56. package/dist/e2b/index.js +411 -0
  57. package/dist/fly/index.d.ts +75 -0
  58. package/dist/fly/index.d.ts.map +1 -0
  59. package/dist/fly/index.js +222 -0
  60. package/dist/index.d.ts +21 -0
  61. package/dist/index.d.ts.map +1 -0
  62. package/dist/index.js +16 -0
  63. package/dist/internal/capabilities.d.ts +57 -0
  64. package/dist/internal/capabilities.d.ts.map +1 -0
  65. package/dist/internal/capabilities.js +68 -0
  66. package/dist/internal/client.d.ts +9 -0
  67. package/dist/internal/client.d.ts.map +1 -0
  68. package/dist/internal/client.js +126 -0
  69. package/dist/internal/encoding.d.ts +8 -0
  70. package/dist/internal/encoding.d.ts.map +1 -0
  71. package/dist/internal/encoding.js +20 -0
  72. package/dist/internal/errors.d.ts +45 -0
  73. package/dist/internal/errors.d.ts.map +1 -0
  74. package/dist/internal/errors.js +79 -0
  75. package/dist/internal/exec.d.ts +19 -0
  76. package/dist/internal/exec.d.ts.map +1 -0
  77. package/dist/internal/exec.js +208 -0
  78. package/dist/internal/plugin.d.ts +38 -0
  79. package/dist/internal/plugin.d.ts.map +1 -0
  80. package/dist/internal/plugin.js +1 -0
  81. package/dist/internal/runtime.d.ts +8 -0
  82. package/dist/internal/runtime.d.ts.map +1 -0
  83. package/dist/internal/runtime.js +21 -0
  84. package/dist/internal/sandbox.d.ts +12 -0
  85. package/dist/internal/sandbox.d.ts.map +1 -0
  86. package/dist/internal/sandbox.js +438 -0
  87. package/dist/internal/shell.d.ts +36 -0
  88. package/dist/internal/shell.d.ts.map +1 -0
  89. package/dist/internal/shell.js +88 -0
  90. package/dist/internal/stream.d.ts +15 -0
  91. package/dist/internal/stream.d.ts.map +1 -0
  92. package/dist/internal/stream.js +58 -0
  93. package/dist/internal/types.d.ts +381 -0
  94. package/dist/internal/types.d.ts.map +1 -0
  95. package/dist/internal/types.js +1 -0
  96. package/dist/langchain/index.d.ts +25 -0
  97. package/dist/langchain/index.d.ts.map +1 -0
  98. package/dist/langchain/index.js +61 -0
  99. package/dist/mastra/index.d.ts +43 -0
  100. package/dist/mastra/index.d.ts.map +1 -0
  101. package/dist/mastra/index.js +69 -0
  102. package/dist/memory/index.d.ts +57 -0
  103. package/dist/memory/index.d.ts.map +1 -0
  104. package/dist/memory/index.js +573 -0
  105. package/dist/modal/index.d.ts +67 -0
  106. package/dist/modal/index.d.ts.map +1 -0
  107. package/dist/modal/index.js +223 -0
  108. package/dist/morph/index.d.ts +91 -0
  109. package/dist/morph/index.d.ts.map +1 -0
  110. package/dist/morph/index.js +221 -0
  111. package/dist/northflank/index.d.ts +74 -0
  112. package/dist/northflank/index.d.ts.map +1 -0
  113. package/dist/northflank/index.js +265 -0
  114. package/dist/openai/index.d.ts +25 -0
  115. package/dist/openai/index.d.ts.map +1 -0
  116. package/dist/openai/index.js +71 -0
  117. package/dist/railway/index.d.ts +109 -0
  118. package/dist/railway/index.d.ts.map +1 -0
  119. package/dist/railway/index.js +219 -0
  120. package/dist/runloop/index.d.ts +69 -0
  121. package/dist/runloop/index.d.ts.map +1 -0
  122. package/dist/runloop/index.js +226 -0
  123. package/dist/testing/index.d.ts +44 -0
  124. package/dist/testing/index.d.ts.map +1 -0
  125. package/dist/testing/index.js +61 -0
  126. package/dist/vercel/index.d.ts +63 -0
  127. package/dist/vercel/index.d.ts.map +1 -0
  128. package/dist/vercel/index.js +241 -0
  129. package/package.json +252 -0
  130. package/src/aws-lambda/runner/Dockerfile +15 -0
  131. package/src/aws-lambda/runner/README.md +59 -0
  132. package/src/aws-lambda/runner/server.mjs +91 -0
@@ -0,0 +1,438 @@
1
+ /**
2
+ * Wires a provider's `DriverHandle` into the public namespaced `Sandbox`:
3
+ * builds the sub-API facades, enforces capability gating (fail-fast
4
+ * NotSupportedError + type-level `undefined`), and silently polyfills universal
5
+ * filesystem ops via `exec` where the adapter doesn't implement them natively.
6
+ */
7
+ import { assertCapability, freezeCapabilities, isCapable, } from "./capabilities.js";
8
+ import { NotSupportedError, SandboxError } from "./errors.js";
9
+ import { createExecHandle } from "./exec.js";
10
+ import { buildExecCommand, joinCmd, parseLsOutput, parseStatOutput, shellQuote, } from "./shell.js";
11
+ export function buildSandbox(provider, handle, base, setup = {}) {
12
+ const caps = freezeCapabilities(provider.capabilities, provider.flags);
13
+ const { name } = provider;
14
+ const mkCtx = (signal) => ({
15
+ attempt: 1,
16
+ fetch: base.fetch,
17
+ metadata: base.defaultMetadata,
18
+ signal,
19
+ });
20
+ const wrapErr = (e) => provider.mapError?.(e) ?? SandboxError.wrap(e, name);
21
+ const guard = async (fn) => {
22
+ try {
23
+ return await fn();
24
+ }
25
+ catch (error) {
26
+ throw wrapErr(error);
27
+ }
28
+ };
29
+ const runExec = (command, opts = {}) => {
30
+ const built = buildExecCommand(command, opts, provider.flags);
31
+ const source = handle.exec(built.command, built.execOptions, mkCtx(opts.signal));
32
+ return createExecHandle(source, {
33
+ mapError: wrapErr,
34
+ parseExitMarker: built.parseExitMarker,
35
+ });
36
+ };
37
+ const commands = {
38
+ async connect(processId) {
39
+ if (!handle.connectProcess) {
40
+ throw new NotSupportedError(name, "commands.connect");
41
+ }
42
+ const proc = await guard(() => handle.connectProcess(processId, mkCtx()));
43
+ return makeProcess(proc, wrapErr);
44
+ },
45
+ async kill(processId, signal) {
46
+ if (!handle.killProcess) {
47
+ throw new NotSupportedError(name, "commands.kill");
48
+ }
49
+ await guard(() => handle.killProcess(processId, signal, mkCtx()));
50
+ },
51
+ async list() {
52
+ if (!handle.listProcesses) {
53
+ throw new NotSupportedError(name, "commands.list");
54
+ }
55
+ return guard(() => handle.listProcesses(mkCtx()));
56
+ },
57
+ run(cmd, opts = {}) {
58
+ const built = buildExecCommand(joinCmd(cmd), opts, provider.flags);
59
+ const source = handle.exec(built.command, built.execOptions, mkCtx(opts.signal));
60
+ return createExecHandle(source, {
61
+ mapError: wrapErr,
62
+ onStderr: opts.onStderr,
63
+ onStdout: opts.onStdout,
64
+ parseExitMarker: built.parseExitMarker,
65
+ });
66
+ },
67
+ async spawn(cmd, opts = {}) {
68
+ if (handle.spawn) {
69
+ const proc = await guard(() => handle.spawn(joinCmd(cmd), opts, mkCtx(opts.signal)));
70
+ return makeProcess(proc, wrapErr);
71
+ }
72
+ assertCapability(name, caps, "background", "commands.spawn");
73
+ throw new NotSupportedError(name, "commands.spawn");
74
+ },
75
+ };
76
+ const files = {
77
+ async download(path) {
78
+ if (handle.download) {
79
+ const stream = await guard(() => handle.download(path, mkCtx()));
80
+ return storedFileFromBytes(path, await drain(stream));
81
+ }
82
+ return files.read(path);
83
+ },
84
+ async exists(path) {
85
+ try {
86
+ await files.stat(path);
87
+ return true;
88
+ }
89
+ catch (error) {
90
+ if (error instanceof SandboxError && error.code === "NotFound") {
91
+ return false;
92
+ }
93
+ throw error;
94
+ }
95
+ },
96
+ async list(path) {
97
+ if (handle.listDir) {
98
+ return guard(() => handle.listDir(path, mkCtx()));
99
+ }
100
+ const res = await runExec(`ls -1Ap ${shellQuote(path)}`);
101
+ if (res.exitCode !== 0) {
102
+ throw new SandboxError("NotFound", `cannot list '${path}'`, {
103
+ provider: name,
104
+ });
105
+ }
106
+ return parseLsOutput(res.stdout, path);
107
+ },
108
+ async mkdir(path, opts) {
109
+ if (handle.mkdir) {
110
+ await guard(() => handle.mkdir(path, opts?.recursive ?? false, mkCtx()));
111
+ return;
112
+ }
113
+ const res = await runExec(`mkdir ${opts?.recursive ? "-p " : ""}${shellQuote(path)}`);
114
+ if (res.exitCode !== 0) {
115
+ throw new SandboxError("Provider", `mkdir failed: ${res.stderr}`, {
116
+ provider: name,
117
+ });
118
+ }
119
+ },
120
+ async read(path) {
121
+ const bytes = await guard(() => handle.readFile(path, mkCtx()));
122
+ return storedFileFromBytes(path, bytes);
123
+ },
124
+ async remove(path, opts) {
125
+ if (handle.remove) {
126
+ await guard(() => handle.remove(path, opts?.recursive ?? false, mkCtx()));
127
+ return;
128
+ }
129
+ const res = await runExec(`rm ${opts?.recursive ? "-rf " : "-f "}${shellQuote(path)}`);
130
+ if (res.exitCode !== 0) {
131
+ throw new SandboxError("Provider", `rm failed: ${res.stderr}`, {
132
+ provider: name,
133
+ });
134
+ }
135
+ },
136
+ async rename(from, to) {
137
+ if (handle.rename) {
138
+ await guard(() => handle.rename(from, to, mkCtx()));
139
+ return;
140
+ }
141
+ const res = await runExec(`mv ${shellQuote(from)} ${shellQuote(to)}`);
142
+ if (res.exitCode !== 0) {
143
+ throw new SandboxError("Provider", `mv failed: ${res.stderr}`, {
144
+ provider: name,
145
+ });
146
+ }
147
+ },
148
+ async stat(path) {
149
+ if (handle.stat) {
150
+ return guard(() => handle.stat(path, mkCtx()));
151
+ }
152
+ const res = await runExec(`stat -c '%F|%s|%Y' ${shellQuote(path)}`);
153
+ if (res.exitCode !== 0) {
154
+ throw new SandboxError("NotFound", `not found: '${path}'`, {
155
+ provider: name,
156
+ });
157
+ }
158
+ const parsed = parseStatOutput(res.stdout);
159
+ if (!parsed) {
160
+ throw new SandboxError("Provider", `cannot stat '${path}'`, {
161
+ provider: name,
162
+ });
163
+ }
164
+ return {
165
+ mtime: parsed.mtime,
166
+ path,
167
+ size: parsed.size,
168
+ type: parsed.type,
169
+ };
170
+ },
171
+ async upload(path, body) {
172
+ if (handle.upload) {
173
+ await guard(() => handle.upload(path, body, mkCtx()));
174
+ return;
175
+ }
176
+ await files.write(path, body);
177
+ },
178
+ async watch(path, cb, opts) {
179
+ assertCapability(name, caps, "filesWatch", "files.watch");
180
+ if (!handle.watch) {
181
+ throw new NotSupportedError(name, "files.watch");
182
+ }
183
+ const stop = await guard(() => handle.watch(path, cb, opts?.recursive ?? false, mkCtx()));
184
+ return {
185
+ close: async () => {
186
+ await stop();
187
+ },
188
+ };
189
+ },
190
+ async write(path, data) {
191
+ const bytes = await toBytes(data);
192
+ await guard(() => handle.writeFile(path, bytes, mkCtx()));
193
+ },
194
+ };
195
+ const code = {
196
+ async createContext(opts) {
197
+ assertCapability(name, caps, "codeInterpreter", "code.createContext");
198
+ if (!handle.createContext) {
199
+ throw new NotSupportedError(name, "code.createContext");
200
+ }
201
+ return guard(() => handle.createContext(opts ?? {}, mkCtx()));
202
+ },
203
+ async runCode(codeStr, opts) {
204
+ assertCapability(name, caps, "codeInterpreter", "code.runCode");
205
+ if (!handle.runCode) {
206
+ throw new NotSupportedError(name, "code.runCode");
207
+ }
208
+ return guard(() => handle.runCode(codeStr, opts ?? {}, mkCtx()));
209
+ },
210
+ };
211
+ const ports = {
212
+ async expose(port, opts) {
213
+ assertCapability(name, caps, "exposePort", "ports.expose");
214
+ if (!handle.exposePort) {
215
+ throw new NotSupportedError(name, "ports.expose");
216
+ }
217
+ return guard(() => handle.exposePort(port, { private: opts?.private }, mkCtx()));
218
+ },
219
+ async fetch(port, path, init) {
220
+ if (handle.proxyFetch) {
221
+ return guard(() => handle.proxyFetch(port, path, init, mkCtx()));
222
+ }
223
+ const preview = await ports.expose(port);
224
+ return base.fetch(new URL(path ?? "/", preview.url).toString(), init);
225
+ },
226
+ async list() {
227
+ if (!handle.listPorts) {
228
+ throw new NotSupportedError(name, "ports.list");
229
+ }
230
+ return guard(() => handle.listPorts(mkCtx()));
231
+ },
232
+ async unexpose(port) {
233
+ if (!handle.unexposePort) {
234
+ throw new NotSupportedError(name, "ports.unexpose");
235
+ }
236
+ await guard(() => handle.unexposePort(port, mkCtx()));
237
+ },
238
+ };
239
+ const snapshots = {
240
+ async create(opts) {
241
+ assertCapability(name, caps, "snapshot", "snapshots.create");
242
+ if (!handle.snapshot) {
243
+ throw new NotSupportedError(name, "snapshots.create");
244
+ }
245
+ return guard(() => handle.snapshot({ name: opts?.name }, mkCtx()));
246
+ },
247
+ async delete(ref) {
248
+ if (!handle.deleteSnapshot) {
249
+ throw new NotSupportedError(name, "snapshots.delete");
250
+ }
251
+ await guard(() => handle.deleteSnapshot(refId(ref), mkCtx()));
252
+ },
253
+ async fork(count) {
254
+ if (!handle.fork) {
255
+ throw new NotSupportedError(name, "snapshots.fork");
256
+ }
257
+ const handles = await guard(() => handle.fork(count ?? 1, mkCtx()));
258
+ return handles.map((h) => buildSandbox(provider, h, base));
259
+ },
260
+ async list() {
261
+ if (!handle.listSnapshots) {
262
+ throw new NotSupportedError(name, "snapshots.list");
263
+ }
264
+ return guard(() => handle.listSnapshots(mkCtx()));
265
+ },
266
+ async restore(ref) {
267
+ if (!handle.restoreSnapshot) {
268
+ throw new NotSupportedError(name, "snapshots.restore");
269
+ }
270
+ await guard(() => handle.restoreSnapshot(refId(ref), mkCtx()));
271
+ },
272
+ };
273
+ const network = {
274
+ async createSsh() {
275
+ assertCapability(name, caps, "ssh", "network.createSsh");
276
+ if (!handle.createSsh) {
277
+ throw new NotSupportedError(name, "network.createSsh");
278
+ }
279
+ return guard(() => handle.createSsh(mkCtx()));
280
+ },
281
+ async setEgressPolicy(policy) {
282
+ assertCapability(name, caps, "egressControl", "network.setEgressPolicy");
283
+ if (!handle.setEgressPolicy) {
284
+ throw new NotSupportedError(name, "network.setEgressPolicy");
285
+ }
286
+ await guard(() => handle.setEgressPolicy(policy, mkCtx()));
287
+ },
288
+ };
289
+ const sandbox = {
290
+ can: ((cap) => isCapable(caps, cap)),
291
+ capabilities: caps,
292
+ code: (isCapable(caps, "codeInterpreter") ? code : undefined),
293
+ commands,
294
+ destroy: () => guard(() => handle.destroy(mkCtx())),
295
+ files,
296
+ getInfo: () => guard(() => handle.getInfo(mkCtx())),
297
+ id: handle.id,
298
+ name: handle.name,
299
+ network: (isCapable(caps, "egressControl")
300
+ ? network
301
+ : undefined),
302
+ async pause() {
303
+ assertCapability(name, caps, "pause", "pause");
304
+ if (!handle.pause) {
305
+ throw new NotSupportedError(name, "pause");
306
+ }
307
+ await guard(() => handle.pause(mkCtx()));
308
+ },
309
+ ports: (isCapable(caps, "exposePort") ? ports : undefined),
310
+ provider: name,
311
+ raw: () => handle.raw,
312
+ async resume() {
313
+ if (!handle.resume) {
314
+ throw new NotSupportedError(name, "resume");
315
+ }
316
+ await guard(() => handle.resume(mkCtx()));
317
+ },
318
+ async setTimeout(ttlMs) {
319
+ assertCapability(name, caps, "setTimeout", "setTimeout");
320
+ if (!handle.setTimeout) {
321
+ throw new NotSupportedError(name, "setTimeout");
322
+ }
323
+ await guard(() => handle.setTimeout(ttlMs, mkCtx()));
324
+ },
325
+ snapshots: (isCapable(caps, "snapshot")
326
+ ? snapshots
327
+ : undefined),
328
+ async stop() {
329
+ assertCapability(name, caps, "stop", "stop");
330
+ if (!handle.stop) {
331
+ throw new NotSupportedError(name, "stop");
332
+ }
333
+ await guard(() => handle.stop(mkCtx()));
334
+ },
335
+ };
336
+ // Plugins: graft contributions onto the sandbox (e.g. `sandbox.tools`), and
337
+ // wrap destroy() to run onDestroy hooks first.
338
+ const { plugins } = base;
339
+ if (plugins && plugins.length > 0) {
340
+ const asBase = sandbox;
341
+ for (const p of plugins) {
342
+ if (p.extend) {
343
+ Object.assign(sandbox, p.extend(asBase, setup));
344
+ }
345
+ }
346
+ const teardown = plugins.filter((p) => p.onDestroy);
347
+ if (teardown.length > 0) {
348
+ const inner = sandbox.destroy.bind(sandbox);
349
+ sandbox.destroy = async () => {
350
+ for (const p of teardown) {
351
+ await p.onDestroy?.(asBase);
352
+ }
353
+ await inner();
354
+ };
355
+ }
356
+ }
357
+ return sandbox;
358
+ }
359
+ // --------------------------------------------------------------------------
360
+ // helpers
361
+ // --------------------------------------------------------------------------
362
+ function refId(ref) {
363
+ return typeof ref === "string" ? ref : ref.id;
364
+ }
365
+ async function toBytes(body) {
366
+ if (typeof body === "string") {
367
+ return new TextEncoder().encode(body);
368
+ }
369
+ if (body instanceof Uint8Array) {
370
+ return body;
371
+ }
372
+ return drain(body);
373
+ }
374
+ async function drain(stream) {
375
+ const reader = stream.getReader();
376
+ const chunks = [];
377
+ let total = 0;
378
+ for (;;) {
379
+ const { done, value } = await reader.read();
380
+ if (done) {
381
+ break;
382
+ }
383
+ if (value) {
384
+ chunks.push(value);
385
+ total += value.length;
386
+ }
387
+ }
388
+ const out = new Uint8Array(total);
389
+ let off = 0;
390
+ for (const c of chunks) {
391
+ out.set(c, off);
392
+ off += c.length;
393
+ }
394
+ return out;
395
+ }
396
+ function storedFileFromBytes(path, bytes) {
397
+ return {
398
+ async bytes() {
399
+ return bytes;
400
+ },
401
+ path,
402
+ stream() {
403
+ return new ReadableStream({
404
+ start(controller) {
405
+ controller.enqueue(bytes);
406
+ controller.close();
407
+ },
408
+ });
409
+ },
410
+ async text() {
411
+ return new TextDecoder().decode(bytes);
412
+ },
413
+ };
414
+ }
415
+ function makeProcess(proc, mapError) {
416
+ const handle = createExecHandle(proc, { mapError });
417
+ return {
418
+ id: proc.id,
419
+ kill: (signal) => proc.kill(signal),
420
+ stderr: filterText(handle, "stderr"),
421
+ stdout: filterText(handle, "stdout"),
422
+ wait: () => handle,
423
+ async writeStdin(data) {
424
+ await handle.writeStdin(data);
425
+ },
426
+ };
427
+ }
428
+ function filterText(handle, kind) {
429
+ return {
430
+ async *[Symbol.asyncIterator]() {
431
+ for await (const ev of handle) {
432
+ if (ev.type === kind) {
433
+ yield ev.data;
434
+ }
435
+ }
436
+ },
437
+ };
438
+ }
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Shell emulation primitives. These build command STRINGS that run INSIDE the
3
+ * remote sandbox via the adapter's `exec` — there is no local `child_process`
4
+ * here. All interpolated values are single-quoted with `shellQuote` so user
5
+ * input cannot break out of the intended command.
6
+ */
7
+ import type { CapabilityFlags } from "./capabilities.js";
8
+ import type { DirEntry, ExecOptions } from "./types.js";
9
+ export declare const EXIT_MARKER = "__sbox_rc";
10
+ /** POSIX-safe single-quote escaping: wrap in '...', and encode embedded quotes. */
11
+ export declare function shellQuote(value: string): string;
12
+ export declare function joinCmd(cmd: string | string[]): string;
13
+ export interface BuiltExec {
14
+ /** The command string to hand to the adapter's exec. */
15
+ command: string;
16
+ /** The ExecOptions to pass to the adapter (cwd/env stripped if baked into the command). */
17
+ execOptions: ExecOptions;
18
+ /** Whether the core should parse an exit-code marker out of stdout. */
19
+ parseExitMarker: boolean;
20
+ }
21
+ /**
22
+ * Decide how a command is executed given a provider's behavioral flags:
23
+ * - native cwd/env -> pass cwd/env straight through as ExecOptions
24
+ * - emulated cwd/env -> bake `cd ... && export ...; cmd` into a `sh -c` string
25
+ * - non-native exit code -> append `; echo __sbox_rc=$?` for the core to parse
26
+ */
27
+ export declare function buildExecCommand(rawCmd: string, opts: ExecOptions, flags: CapabilityFlags): BuiltExec;
28
+ /** Parse `ls -1Ap <dir>` output (names; dirs have a trailing slash) into DirEntry[]. */
29
+ export declare function parseLsOutput(output: string, dir: string): DirEntry[];
30
+ /** Parse `stat -c '%F|%s|%Y' <path>` (GNU coreutils, Linux sandboxes). */
31
+ export declare function parseStatOutput(output: string): {
32
+ type: "file" | "dir" | "symlink";
33
+ size: number;
34
+ mtime?: Date;
35
+ } | null;
36
+ //# sourceMappingURL=shell.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shell.d.ts","sourceRoot":"","sources":["../../src/internal/shell.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACzD,OAAO,KAAK,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAExD,eAAO,MAAM,WAAW,cAAc,CAAC;AAEvC,mFAAmF;AACnF,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEhD;AAED,wBAAgB,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,MAAM,CAEtD;AAED,MAAM,WAAW,SAAS;IACxB,wDAAwD;IACxD,OAAO,EAAE,MAAM,CAAC;IAChB,2FAA2F;IAC3F,WAAW,EAAE,WAAW,CAAC;IACzB,uEAAuE;IACvE,eAAe,EAAE,OAAO,CAAC;CAC1B;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,WAAW,EACjB,KAAK,EAAE,eAAe,GACrB,SAAS,CAsCX;AAED,wFAAwF;AACxF,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,QAAQ,EAAE,CAiBrE;AAED,0EAA0E;AAC1E,wBAAgB,eAAe,CAC7B,MAAM,EAAE,MAAM,GACb;IAAE,IAAI,EAAE,MAAM,GAAG,KAAK,GAAG,SAAS,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,IAAI,CAAA;CAAE,GAAG,IAAI,CAmBzE"}
@@ -0,0 +1,88 @@
1
+ export const EXIT_MARKER = "__sbox_rc";
2
+ /** POSIX-safe single-quote escaping: wrap in '...', and encode embedded quotes. */
3
+ export function shellQuote(value) {
4
+ return `'${value.replaceAll("'", `'\\''`)}'`;
5
+ }
6
+ export function joinCmd(cmd) {
7
+ return Array.isArray(cmd) ? cmd.map(shellQuote).join(" ") : cmd;
8
+ }
9
+ /**
10
+ * Decide how a command is executed given a provider's behavioral flags:
11
+ * - native cwd/env -> pass cwd/env straight through as ExecOptions
12
+ * - emulated cwd/env -> bake `cd ... && export ...; cmd` into a `sh -c` string
13
+ * - non-native exit code -> append `; echo __sbox_rc=$?` for the core to parse
14
+ */
15
+ export function buildExecCommand(rawCmd, opts, flags) {
16
+ const needsExitMarker = !flags.exitCodeNative;
17
+ const hasCwd = !!opts.cwd;
18
+ const hasEnv = !!opts.env && Object.keys(opts.env).length > 0;
19
+ const emulateCwdEnv = !flags.perCommandEnvCwd && (hasCwd || hasEnv);
20
+ if (!emulateCwdEnv && !needsExitMarker) {
21
+ return { command: rawCmd, execOptions: opts, parseExitMarker: false };
22
+ }
23
+ let inner = rawCmd;
24
+ if (needsExitMarker) {
25
+ inner = `${inner}; echo ${EXIT_MARKER}=$?`;
26
+ }
27
+ if (emulateCwdEnv) {
28
+ if (hasEnv) {
29
+ const exports = Object.entries(opts.env)
30
+ .map(([k, v]) => `export ${k}=${shellQuote(v)};`)
31
+ .join(" ");
32
+ inner = `${exports} ${inner}`;
33
+ }
34
+ if (hasCwd) {
35
+ inner = `cd ${shellQuote(opts.cwd)} && ${inner}`;
36
+ }
37
+ }
38
+ // Strip the now-baked cwd/env from the options handed to the adapter.
39
+ const execOptions = { ...opts };
40
+ if (emulateCwdEnv) {
41
+ delete execOptions.cwd;
42
+ delete execOptions.env;
43
+ }
44
+ return {
45
+ command: `sh -c ${shellQuote(inner)}`,
46
+ execOptions,
47
+ parseExitMarker: needsExitMarker,
48
+ };
49
+ }
50
+ /** Parse `ls -1Ap <dir>` output (names; dirs have a trailing slash) into DirEntry[]. */
51
+ export function parseLsOutput(output, dir) {
52
+ const base = dir.endsWith("/") ? dir.slice(0, -1) : dir;
53
+ const entries = [];
54
+ for (const line of output.split("\n")) {
55
+ const name = line.trimEnd();
56
+ if (!name) {
57
+ continue;
58
+ }
59
+ const isDir = name.endsWith("/");
60
+ const clean = isDir ? name.slice(0, -1) : name;
61
+ entries.push({
62
+ name: clean,
63
+ path: `${base}/${clean}`,
64
+ type: isDir ? "dir" : "file",
65
+ });
66
+ }
67
+ return entries;
68
+ }
69
+ /** Parse `stat -c '%F|%s|%Y' <path>` (GNU coreutils, Linux sandboxes). */
70
+ export function parseStatOutput(output) {
71
+ const parts = output.trim().split("|");
72
+ if (parts.length < 2) {
73
+ return null;
74
+ }
75
+ const kind = parts[0] ?? "";
76
+ const size = Number(parts[1] ?? "0");
77
+ const mtimeEpoch = parts[2] ? Number(parts[2]) : undefined;
78
+ const type = kind === "directory"
79
+ ? "dir"
80
+ : kind.includes("symbolic")
81
+ ? "symlink"
82
+ : "file";
83
+ return {
84
+ mtime: mtimeEpoch ? new Date(mtimeEpoch * 1000) : undefined,
85
+ size: Number.isFinite(size) ? size : 0,
86
+ type,
87
+ };
88
+ }
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Shared push-to-pull bridge for adapters: provider SDKs deliver output via
3
+ * push callbacks or their own async iterables, while the core consumes a single
4
+ * `AsyncIterable<OutputEvent>`. `AsyncQueue` adapts the former to the latter.
5
+ */
6
+ export declare class AsyncQueue<T> {
7
+ #private;
8
+ push(item: T): void;
9
+ close(): void;
10
+ fail(err: unknown): void;
11
+ iterator(): AsyncGenerator<T>;
12
+ }
13
+ /** Best-effort exit code from a thrown provider error that carries one. */
14
+ export declare function numExit(e: unknown): number;
15
+ //# sourceMappingURL=stream.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stream.d.ts","sourceRoot":"","sources":["../../src/internal/stream.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,qBAAa,UAAU,CAAC,CAAC;;IAMvB,IAAI,CAAC,IAAI,EAAE,CAAC,GAAG,IAAI;IAInB,KAAK,IAAI,IAAI;IAIb,IAAI,CAAC,GAAG,EAAE,OAAO,GAAG,IAAI;IAYjB,QAAQ,IAAI,cAAc,CAAC,CAAC,CAAC;CAiBrC;AAED,2EAA2E;AAC3E,wBAAgB,OAAO,CAAC,CAAC,EAAE,OAAO,GAAG,MAAM,CAQ1C"}
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Shared push-to-pull bridge for adapters: provider SDKs deliver output via
3
+ * push callbacks or their own async iterables, while the core consumes a single
4
+ * `AsyncIterable<OutputEvent>`. `AsyncQueue` adapts the former to the latter.
5
+ */
6
+ export class AsyncQueue {
7
+ #items = [];
8
+ #wakers = [];
9
+ #closed = false;
10
+ #err = null;
11
+ push(item) {
12
+ this.#items.push(item);
13
+ this.#wake();
14
+ }
15
+ close() {
16
+ this.#closed = true;
17
+ this.#wake();
18
+ }
19
+ fail(err) {
20
+ this.#err = err;
21
+ this.#closed = true;
22
+ this.#wake();
23
+ }
24
+ #wake() {
25
+ const w = this.#wakers;
26
+ this.#wakers = [];
27
+ for (const f of w) {
28
+ f();
29
+ }
30
+ }
31
+ async *iterator() {
32
+ let i = 0;
33
+ for (;;) {
34
+ while (i < this.#items.length) {
35
+ const item = this.#items[i];
36
+ i++;
37
+ yield item;
38
+ }
39
+ if (this.#closed) {
40
+ if (this.#err) {
41
+ throw this.#err;
42
+ }
43
+ return;
44
+ }
45
+ await new Promise((r) => this.#wakers.push(r));
46
+ }
47
+ }
48
+ }
49
+ /** Best-effort exit code from a thrown provider error that carries one. */
50
+ export function numExit(e) {
51
+ if (e && typeof e === "object" && "exitCode" in e) {
52
+ const code = e.exitCode;
53
+ if (typeof code === "number" && Number.isFinite(code)) {
54
+ return code;
55
+ }
56
+ }
57
+ return 1;
58
+ }