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,222 @@
1
+ /**
2
+ * `sbox-sdk/fly` — adapter for the Fly Machines REST API (no SDK; pure fetch, so
3
+ * it runs anywhere the core does). A "sandbox" is a Fly Machine. exec is the
4
+ * buffered `/exec` endpoint (no per-command cwd/env — the core folds those into
5
+ * a `cd … && KEY=v …` wrapper via `perCommandEnvCwd: false`); filesystem is done
6
+ * via exec + base64. stop() releases compute but the rootfs resets unless a
7
+ * volume is attached (`preservesDiskOnStop: false` — callers are warned).
8
+ */
9
+ import { AsyncQueue, base64ToBytes, bytesToBase64, defineProvider, SandboxError, shellQuote as sq, } from "../adapter/index.js";
10
+ export const FLY_CAPS = {
11
+ background: "unsupported",
12
+ codeInterpreter: "unsupported",
13
+ egressControl: "unsupported",
14
+ exposePort: "native",
15
+ filesUpload: "native",
16
+ filesWatch: "unsupported",
17
+ fork: "unsupported",
18
+ gpu: "unsupported",
19
+ killProcess: "unsupported",
20
+ list: "native",
21
+ metrics: "unsupported",
22
+ pause: "native",
23
+ privatePreview: "unsupported",
24
+ proxiedFetch: "unsupported",
25
+ pty: "unsupported",
26
+ region: "native",
27
+ secretsVault: "unsupported",
28
+ setTimeout: "unsupported",
29
+ snapshot: "unsupported",
30
+ ssh: "unsupported",
31
+ statefulKernel: "unsupported",
32
+ stdin: "unsupported",
33
+ stop: "native",
34
+ streaming: "emulated",
35
+ volumes: "unsupported",
36
+ };
37
+ const FLY_FLAGS = {
38
+ exitCodeNative: true,
39
+ perCommandEnvCwd: false, // /exec has no per-call cwd/env — core wraps `cd && KEY=v`
40
+ preservesDiskOnStop: false, // rootfs resets on stop unless a volume is attached
41
+ preservesMemoryOnPause: true, // suspend preserves memory
42
+ previewModel: "subdomain",
43
+ };
44
+ function mapStatus(s) {
45
+ if (s === 404) {
46
+ return "NotFound";
47
+ }
48
+ if (s === 401 || s === 403) {
49
+ return "Unauthorized";
50
+ }
51
+ if (s === 429) {
52
+ return "QuotaExceeded";
53
+ }
54
+ if (s === 408) {
55
+ return "Timeout";
56
+ }
57
+ return "Provider";
58
+ }
59
+ export const fly = defineProvider((opts) => {
60
+ const base = opts.apiBaseUrl ?? "https://api.machines.dev/v1";
61
+ const fetchImpl = opts.fetch ?? globalThis.fetch;
62
+ const app = opts.appName;
63
+ async function api(method, path, body) {
64
+ const res = await fetchImpl(`${base}${path}`, {
65
+ method,
66
+ headers: {
67
+ authorization: `Bearer ${opts.apiToken}`,
68
+ "content-type": "application/json",
69
+ },
70
+ body: body === undefined ? undefined : JSON.stringify(body),
71
+ });
72
+ if (!res.ok) {
73
+ const text = await res.text().catch(() => "");
74
+ throw new SandboxError(mapStatus(res.status), `fly ${method} ${path} -> ${res.status} ${text}`, {
75
+ provider: "fly",
76
+ status: res.status,
77
+ retryable: res.status === 429 || res.status >= 500,
78
+ });
79
+ }
80
+ const ct = res.headers.get("content-type") ?? "";
81
+ return (ct.includes("json") ? await res.json() : await res.text());
82
+ }
83
+ const makeHandle = (machine) => {
84
+ const { id } = machine;
85
+ const exec1 = (cmd, timeoutMs) => api("POST", `/apps/${app}/machines/${id}/exec`, {
86
+ command: ["sh", "-c", cmd],
87
+ timeout: timeoutMs ? Math.ceil(timeoutMs / 1000) : undefined,
88
+ });
89
+ return {
90
+ id,
91
+ raw: machine,
92
+ getInfo() {
93
+ return {
94
+ id,
95
+ state: mapState(machine.state),
96
+ provider: "fly",
97
+ metadata: {},
98
+ raw: machine,
99
+ };
100
+ },
101
+ async destroy() {
102
+ await api("DELETE", `/apps/${app}/machines/${id}?force=true`);
103
+ },
104
+ async stop() {
105
+ await api("POST", `/apps/${app}/machines/${id}/stop`);
106
+ },
107
+ async pause() {
108
+ await api("POST", `/apps/${app}/machines/${id}/suspend`);
109
+ },
110
+ async resume() {
111
+ await api("POST", `/apps/${app}/machines/${id}/start`);
112
+ },
113
+ exec(cmd, options) {
114
+ const queue = new AsyncQueue();
115
+ void exec1(cmd, options.timeoutMs)
116
+ .then((r) => {
117
+ if (r.stdout) {
118
+ queue.push({ type: "stdout", data: r.stdout });
119
+ }
120
+ if (r.stderr) {
121
+ queue.push({ type: "stderr", data: r.stderr });
122
+ }
123
+ queue.push({ type: "exit", exitCode: r.exit_code ?? 0 });
124
+ queue.close();
125
+ })
126
+ .catch((error) => queue.fail(error instanceof SandboxError
127
+ ? error
128
+ : SandboxError.wrap(error, "fly")));
129
+ return {
130
+ pid: Promise.resolve(""),
131
+ async kill() {
132
+ /* /exec is buffered; no kill handle */
133
+ },
134
+ [Symbol.asyncIterator]: () => queue.iterator(),
135
+ };
136
+ },
137
+ async readFile(path) {
138
+ const r = await exec1(`base64 ${sq(path)}`);
139
+ if ((r.exit_code ?? 1) !== 0) {
140
+ throw new SandboxError("NotFound", `no such file: '${path}'`, {
141
+ provider: "fly",
142
+ });
143
+ }
144
+ return base64ToBytes(r.stdout ?? "");
145
+ },
146
+ async writeFile(path, data) {
147
+ const b64 = bytesToBase64(data);
148
+ const r = await exec1(`mkdir -p "$(dirname ${sq(path)})" && printf %s ${sq(b64)} | base64 -d > ${sq(path)}`);
149
+ if ((r.exit_code ?? 1) !== 0) {
150
+ throw new SandboxError("Provider", `write failed: '${path}'`, {
151
+ provider: "fly",
152
+ });
153
+ }
154
+ },
155
+ exposePort(port) {
156
+ return { url: opts.appDomain ?? `https://${app}.fly.dev`, port };
157
+ },
158
+ };
159
+ };
160
+ const provider = {
161
+ name: "fly",
162
+ capabilities: FLY_CAPS,
163
+ flags: FLY_FLAGS,
164
+ async create(spec) {
165
+ const machine = await api("POST", `/apps/${app}/machines`, {
166
+ region: spec.region ?? opts.region,
167
+ config: {
168
+ image: spec.template ?? opts.image ?? "ubuntu:22.04",
169
+ env: spec.env,
170
+ auto_destroy: false,
171
+ guest: {
172
+ cpus: spec.resources?.vcpus ?? 1,
173
+ memory_mb: spec.resources?.memoryMB ?? 256,
174
+ cpu_kind: "shared",
175
+ },
176
+ },
177
+ });
178
+ // Best-effort wait for the machine to reach 'started'.
179
+ await api("GET", `/apps/${app}/machines/${machine.id}/wait?state=started&timeout=60`).catch(() => { });
180
+ return makeHandle(machine);
181
+ },
182
+ async connect(id) {
183
+ return makeHandle(await api("GET", `/apps/${app}/machines/${id}`));
184
+ },
185
+ async *list() {
186
+ const machines = await api("GET", `/apps/${app}/machines`);
187
+ for (const m of machines) {
188
+ yield {
189
+ id: m.id,
190
+ state: mapState(m.state),
191
+ provider: "fly",
192
+ metadata: {},
193
+ raw: m,
194
+ };
195
+ }
196
+ },
197
+ };
198
+ return provider;
199
+ });
200
+ function mapState(state) {
201
+ switch (state) {
202
+ case "started": {
203
+ return "running";
204
+ }
205
+ case "stopped": {
206
+ return "stopped";
207
+ }
208
+ case "suspended": {
209
+ return "paused";
210
+ }
211
+ case "created":
212
+ case "starting": {
213
+ return "creating";
214
+ }
215
+ case "destroyed": {
216
+ return "destroyed";
217
+ }
218
+ default: {
219
+ return "unknown";
220
+ }
221
+ }
222
+ }
@@ -0,0 +1,21 @@
1
+ /**
2
+ * sbox-sdk — one unified SDK for agent sandbox providers.
3
+ *
4
+ * Pick a provider by importing its adapter from a subpath and passing it in:
5
+ *
6
+ * ```ts
7
+ * import { createSandboxClient } from "sbox-sdk";
8
+ * import { e2b } from "sbox-sdk/e2b";
9
+ *
10
+ * const client = createSandboxClient({ provider: e2b({ apiKey }) });
11
+ * const sandbox = await client.create({ template: "python-3.12" });
12
+ * const { stdout } = await sandbox.commands.run("echo hi");
13
+ * ```
14
+ */
15
+ export { createSandboxClient } from "./internal/client.js";
16
+ export { SandboxError, NotSupportedError, ProviderNotFoundError, AllProvidersFailedError, isRetryableError, isRetryableStatus, } from "./internal/errors.js";
17
+ export type { SandboxErrorCode, SandboxErrorInit, ProviderAttempt, } from "./internal/errors.js";
18
+ export type { CapabilityLevel, CapabilityMap, CapabilityName, CapabilityFlags, Capabilities, Gated, PreviewModel, } from "./internal/capabilities.js";
19
+ export type * from "./internal/types.js";
20
+ export type { SandboxPlugin, PluginSetupContext, MergePlugins, } from "./internal/plugin.js";
21
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAE3D,OAAO,EACL,YAAY,EACZ,iBAAiB,EACjB,qBAAqB,EACrB,uBAAuB,EACvB,gBAAgB,EAChB,iBAAiB,GAClB,MAAM,sBAAsB,CAAC;AAC9B,YAAY,EACV,gBAAgB,EAChB,gBAAgB,EAChB,eAAe,GAChB,MAAM,sBAAsB,CAAC;AAE9B,YAAY,EACV,eAAe,EACf,aAAa,EACb,cAAc,EACd,eAAe,EACf,YAAY,EACZ,KAAK,EACL,YAAY,GACb,MAAM,4BAA4B,CAAC;AAEpC,mBAAmB,qBAAqB,CAAC;AAEzC,YAAY,EACV,aAAa,EACb,kBAAkB,EAClB,YAAY,GACb,MAAM,sBAAsB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,16 @@
1
+ /**
2
+ * sbox-sdk — one unified SDK for agent sandbox providers.
3
+ *
4
+ * Pick a provider by importing its adapter from a subpath and passing it in:
5
+ *
6
+ * ```ts
7
+ * import { createSandboxClient } from "sbox-sdk";
8
+ * import { e2b } from "sbox-sdk/e2b";
9
+ *
10
+ * const client = createSandboxClient({ provider: e2b({ apiKey }) });
11
+ * const sandbox = await client.create({ template: "python-3.12" });
12
+ * const { stdout } = await sandbox.commands.run("echo hi");
13
+ * ```
14
+ */
15
+ export { createSandboxClient } from "./internal/client.js";
16
+ export { SandboxError, NotSupportedError, ProviderNotFoundError, AllProvidersFailedError, isRetryableError, isRetryableStatus, } from "./internal/errors.js";
@@ -0,0 +1,57 @@
1
+ export type CapabilityLevel = "native" | "emulated" | "unsupported";
2
+ /** Static, declarative table per provider. Drives type-level + runtime gating. */
3
+ export interface CapabilityMap {
4
+ list: CapabilityLevel;
5
+ stop: CapabilityLevel;
6
+ pause: CapabilityLevel;
7
+ setTimeout: CapabilityLevel;
8
+ background: CapabilityLevel;
9
+ streaming: CapabilityLevel;
10
+ killProcess: CapabilityLevel;
11
+ pty: CapabilityLevel;
12
+ stdin: CapabilityLevel;
13
+ filesWatch: CapabilityLevel;
14
+ filesUpload: CapabilityLevel;
15
+ codeInterpreter: CapabilityLevel;
16
+ statefulKernel: CapabilityLevel;
17
+ exposePort: CapabilityLevel;
18
+ privatePreview: CapabilityLevel;
19
+ egressControl: CapabilityLevel;
20
+ ssh: CapabilityLevel;
21
+ proxiedFetch: CapabilityLevel;
22
+ snapshot: CapabilityLevel;
23
+ fork: CapabilityLevel;
24
+ volumes: CapabilityLevel;
25
+ gpu: CapabilityLevel;
26
+ region: CapabilityLevel;
27
+ secretsVault: CapabilityLevel;
28
+ metrics: CapabilityLevel;
29
+ }
30
+ export type CapabilityName = keyof CapabilityMap;
31
+ export type PreviewModel = "subdomain" | "tunnel" | "declaredPorts" | "wildcardDNS" | "ip" | "none";
32
+ /** Behavioral flags that aren't present/absent — they change semantics. */
33
+ export interface CapabilityFlags {
34
+ preservesMemoryOnPause: boolean;
35
+ preservesDiskOnStop: boolean;
36
+ /** false => core wraps exec in `sh -c 'cd <cwd> && KEY=v <cmd>'`. */
37
+ perCommandEnvCwd: boolean;
38
+ /** false => core synthesizes exit code via `; echo __sbox_rc=$?`. */
39
+ exitCodeNative: boolean;
40
+ previewModel: PreviewModel;
41
+ }
42
+ /** Runtime read-model surfaced on every sandbox + client. */
43
+ export interface Capabilities {
44
+ readonly map: Readonly<CapabilityMap>;
45
+ readonly flags: Readonly<CapabilityFlags>;
46
+ }
47
+ /** Gated sub-API: the real interface when supported/emulated, else `undefined`. */
48
+ export type Gated<L extends CapabilityLevel, API> = L extends "unsupported" ? undefined : API;
49
+ /** A baseline map of all-`unsupported`, for providers to spread overrides onto. */
50
+ export declare function baseCapabilities(): CapabilityMap;
51
+ export declare function defaultFlags(overrides?: Partial<CapabilityFlags>): CapabilityFlags;
52
+ /** Build a frozen runtime read-model from a provider's static declaration. */
53
+ export declare function freezeCapabilities(map: CapabilityMap, flags: CapabilityFlags): Capabilities;
54
+ export declare function isCapable(caps: Capabilities, name: CapabilityName): boolean;
55
+ /** Throw NotSupportedError up front if a capability is unsupported. */
56
+ export declare function assertCapability(provider: string, caps: Capabilities, name: CapabilityName, feature?: string): void;
57
+ //# sourceMappingURL=capabilities.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"capabilities.d.ts","sourceRoot":"","sources":["../../src/internal/capabilities.ts"],"names":[],"mappings":"AAQA,MAAM,MAAM,eAAe,GAAG,QAAQ,GAAG,UAAU,GAAG,aAAa,CAAC;AAEpE,kFAAkF;AAClF,MAAM,WAAW,aAAa;IAE5B,IAAI,EAAE,eAAe,CAAC;IACtB,IAAI,EAAE,eAAe,CAAC;IACtB,KAAK,EAAE,eAAe,CAAC;IACvB,UAAU,EAAE,eAAe,CAAC;IAE5B,UAAU,EAAE,eAAe,CAAC;IAC5B,SAAS,EAAE,eAAe,CAAC;IAC3B,WAAW,EAAE,eAAe,CAAC;IAC7B,GAAG,EAAE,eAAe,CAAC;IACrB,KAAK,EAAE,eAAe,CAAC;IAEvB,UAAU,EAAE,eAAe,CAAC;IAC5B,WAAW,EAAE,eAAe,CAAC;IAE7B,eAAe,EAAE,eAAe,CAAC;IACjC,cAAc,EAAE,eAAe,CAAC;IAEhC,UAAU,EAAE,eAAe,CAAC;IAC5B,cAAc,EAAE,eAAe,CAAC;IAChC,aAAa,EAAE,eAAe,CAAC;IAC/B,GAAG,EAAE,eAAe,CAAC;IACrB,YAAY,EAAE,eAAe,CAAC;IAE9B,QAAQ,EAAE,eAAe,CAAC;IAC1B,IAAI,EAAE,eAAe,CAAC;IACtB,OAAO,EAAE,eAAe,CAAC;IAEzB,GAAG,EAAE,eAAe,CAAC;IACrB,MAAM,EAAE,eAAe,CAAC;IACxB,YAAY,EAAE,eAAe,CAAC;IAC9B,OAAO,EAAE,eAAe,CAAC;CAC1B;AAED,MAAM,MAAM,cAAc,GAAG,MAAM,aAAa,CAAC;AAEjD,MAAM,MAAM,YAAY,GACpB,WAAW,GACX,QAAQ,GACR,eAAe,GACf,aAAa,GACb,IAAI,GACJ,MAAM,CAAC;AAEX,2EAA2E;AAC3E,MAAM,WAAW,eAAe;IAC9B,sBAAsB,EAAE,OAAO,CAAC;IAChC,mBAAmB,EAAE,OAAO,CAAC;IAC7B,qEAAqE;IACrE,gBAAgB,EAAE,OAAO,CAAC;IAC1B,qEAAqE;IACrE,cAAc,EAAE,OAAO,CAAC;IACxB,YAAY,EAAE,YAAY,CAAC;CAC5B;AAED,6DAA6D;AAC7D,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,GAAG,EAAE,QAAQ,CAAC,aAAa,CAAC,CAAC;IACtC,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAC,eAAe,CAAC,CAAC;CAC3C;AAED,mFAAmF;AACnF,MAAM,MAAM,KAAK,CAAC,CAAC,SAAS,eAAe,EAAE,GAAG,IAAI,CAAC,SAAS,aAAa,GACvE,SAAS,GACT,GAAG,CAAC;AAsCR,mFAAmF;AACnF,wBAAgB,gBAAgB,IAAI,aAAa,CAMhD;AAED,wBAAgB,YAAY,CAC1B,SAAS,GAAE,OAAO,CAAC,eAAe,CAAM,GACvC,eAAe,CAEjB;AAED,8EAA8E;AAC9E,wBAAgB,kBAAkB,CAChC,GAAG,EAAE,aAAa,EAClB,KAAK,EAAE,eAAe,GACrB,YAAY,CAKd;AAED,wBAAgB,SAAS,CAAC,IAAI,EAAE,YAAY,EAAE,IAAI,EAAE,cAAc,GAAG,OAAO,CAE3E;AAED,uEAAuE;AACvE,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,YAAY,EAClB,IAAI,EAAE,cAAc,EACpB,OAAO,GAAE,MAAa,GACrB,IAAI,CAIN"}
@@ -0,0 +1,68 @@
1
+ /**
2
+ * The single source of truth for what a provider can do. Each provider declares
3
+ * a static `CapabilityMap` literal; from it we derive (A) type-level gating via
4
+ * `Gated<>`, (B) the runtime read-model on `sandbox.capabilities`, and (C)
5
+ * fail-fast enforcement via `assertCapability`.
6
+ */
7
+ import { NotSupportedError } from "./errors.js";
8
+ const ALL_CAPABILITY_NAMES = [
9
+ "list",
10
+ "stop",
11
+ "pause",
12
+ "setTimeout",
13
+ "background",
14
+ "streaming",
15
+ "killProcess",
16
+ "pty",
17
+ "stdin",
18
+ "filesWatch",
19
+ "filesUpload",
20
+ "codeInterpreter",
21
+ "statefulKernel",
22
+ "exposePort",
23
+ "privatePreview",
24
+ "egressControl",
25
+ "ssh",
26
+ "proxiedFetch",
27
+ "snapshot",
28
+ "fork",
29
+ "volumes",
30
+ "gpu",
31
+ "region",
32
+ "secretsVault",
33
+ "metrics",
34
+ ];
35
+ const DEFAULT_FLAGS = {
36
+ exitCodeNative: true,
37
+ perCommandEnvCwd: true,
38
+ preservesDiskOnStop: false,
39
+ preservesMemoryOnPause: false,
40
+ previewModel: "none",
41
+ };
42
+ /** A baseline map of all-`unsupported`, for providers to spread overrides onto. */
43
+ export function baseCapabilities() {
44
+ const map = {};
45
+ for (const name of ALL_CAPABILITY_NAMES) {
46
+ map[name] = "unsupported";
47
+ }
48
+ return map;
49
+ }
50
+ export function defaultFlags(overrides = {}) {
51
+ return { ...DEFAULT_FLAGS, ...overrides };
52
+ }
53
+ /** Build a frozen runtime read-model from a provider's static declaration. */
54
+ export function freezeCapabilities(map, flags) {
55
+ return {
56
+ flags: Object.freeze({ ...flags }),
57
+ map: Object.freeze({ ...map }),
58
+ };
59
+ }
60
+ export function isCapable(caps, name) {
61
+ return caps.map[name] !== "unsupported";
62
+ }
63
+ /** Throw NotSupportedError up front if a capability is unsupported. */
64
+ export function assertCapability(provider, caps, name, feature = name) {
65
+ if (caps.map[name] === "unsupported") {
66
+ throw new NotSupportedError(provider, feature);
67
+ }
68
+ }
@@ -0,0 +1,9 @@
1
+ import type { CapabilityMap } from "./capabilities.js";
2
+ import type { MergePlugins, SandboxPlugin } from "./plugin.js";
3
+ import type { ClientOptions, SandboxClient, SandboxProvider } from "./types.js";
4
+ export declare function createSandboxClient(): SandboxClient;
5
+ export declare function createSandboxClient<Caps extends CapabilityMap, Raw, const Ps extends readonly SandboxPlugin[] = []>(options: ClientOptions<Caps, Raw> & {
6
+ provider: SandboxProvider<Caps, Raw>;
7
+ plugins?: Ps;
8
+ }): SandboxClient<Caps, Raw, MergePlugins<Ps>>;
9
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/internal/client.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAOvD,OAAO,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAE/D,OAAO,KAAK,EAEV,aAAa,EAIb,aAAa,EAEb,eAAe,EAEhB,MAAM,YAAY,CAAC;AAepB,wBAAgB,mBAAmB,IAAI,aAAa,CAAC;AACrD,wBAAgB,mBAAmB,CACjC,IAAI,SAAS,aAAa,EAC1B,GAAG,EACH,KAAK,CAAC,EAAE,SAAS,SAAS,aAAa,EAAE,GAAG,EAAE,EAE9C,OAAO,EAAE,aAAa,CAAC,IAAI,EAAE,GAAG,CAAC,GAAG;IAClC,QAAQ,EAAE,eAAe,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;IACrC,OAAO,CAAC,EAAE,EAAE,CAAC;CACd,GACA,aAAa,CAAC,IAAI,EAAE,GAAG,EAAE,YAAY,CAAC,EAAE,CAAC,CAAC,CAAC"}
@@ -0,0 +1,126 @@
1
+ import { memory } from "../memory/index.js";
2
+ /**
3
+ * `createSandboxClient` — the single entry point. Owns provider selection
4
+ * (instance, or the in-memory default), idempotency-aware retry + fallback,
5
+ * lifecycle hooks, and disposal. The router is the only caller of providers.
6
+ */
7
+ import { freezeCapabilities } from "./capabilities.js";
8
+ import { AllProvidersFailedError, NotSupportedError, SandboxError, } from "./errors.js";
9
+ import { buildSandbox } from "./sandbox.js";
10
+ function defaultShouldRetry(err) {
11
+ return (typeof err === "object" &&
12
+ err !== null &&
13
+ "retryable" in err &&
14
+ err.retryable === true);
15
+ }
16
+ function sleep(ms) {
17
+ return new Promise((resolve) => setTimeout(resolve, ms));
18
+ }
19
+ export function createSandboxClient(options) {
20
+ const provider = options?.provider ?? memory();
21
+ const fetchImpl = options?.fetch ?? globalThis.fetch;
22
+ const retry = { retries: 2, ...options?.retry };
23
+ const fallback = options?.fallback ?? [];
24
+ const hooks = options?.hooks;
25
+ const plugins = options?.plugins ?? [];
26
+ const aiProviders = plugins.filter((p) => p.kind === "ai-provider");
27
+ if (aiProviders.length > 1) {
28
+ throw new SandboxError("Validation", `a client supports one AI-provider plugin, got ${aiProviders.length} (${aiProviders
29
+ .map((p) => p.name)
30
+ .join(", ")}). Use the standalone toXTools(sandbox) helpers for multiple frameworks.`);
31
+ }
32
+ const caps = freezeCapabilities(provider.capabilities, provider.flags);
33
+ const base = {
34
+ defaultMetadata: options?.defaultMetadata,
35
+ emulate: options?.emulate,
36
+ fetch: fetchImpl,
37
+ plugins,
38
+ };
39
+ const mkCtx = (attempt, signal, idempotencyKey) => ({
40
+ attempt,
41
+ fetch: fetchImpl,
42
+ idempotencyKey,
43
+ metadata: options?.defaultMetadata,
44
+ signal,
45
+ });
46
+ const backoff = (n) => Math.min(100 * 2 ** (n - 1), 2000);
47
+ const withRetry = async (fn) => {
48
+ const max = retry.retries ?? 2;
49
+ let lastErr;
50
+ for (let attempt = 1; attempt <= max + 1; attempt++) {
51
+ try {
52
+ return await fn(attempt);
53
+ }
54
+ catch (error) {
55
+ lastErr = error;
56
+ await hooks?.onError?.(error, attempt);
57
+ const should = (retry.shouldRetry ?? defaultShouldRetry)(error, attempt);
58
+ if (!should || attempt > max) {
59
+ throw error;
60
+ }
61
+ await sleep((retry.delayMs ?? backoff)(attempt, error));
62
+ }
63
+ }
64
+ throw lastErr instanceof Error
65
+ ? lastErr
66
+ : new SandboxError("Provider", String(lastErr));
67
+ };
68
+ const client = {
69
+ get capabilities() {
70
+ return caps;
71
+ },
72
+ async connect(id) {
73
+ const handle = await withRetry((attempt) => Promise.resolve(provider.connect(id, mkCtx(attempt))));
74
+ const sandbox = buildSandbox(provider, handle, base);
75
+ for (const pl of plugins) {
76
+ await pl.onCreate?.(sandbox, {});
77
+ }
78
+ return sandbox;
79
+ },
80
+ async create(spec = {}, createOptions) {
81
+ const idem = spec.idempotencyKey ?? globalThis.crypto.randomUUID();
82
+ await hooks?.beforeCreate?.(spec);
83
+ // Only fall back across providers when the caller supplied an idempotency
84
+ // key — otherwise a retried create could orphan a second VM.
85
+ const canFallback = !!spec.idempotencyKey && fallback.length > 0;
86
+ const chain = canFallback
87
+ ? [provider, ...fallback]
88
+ : [provider];
89
+ const attempts = [];
90
+ for (const p of chain) {
91
+ try {
92
+ const handle = await withRetry((attempt) => Promise.resolve(p.create({
93
+ ...spec,
94
+ metadata: { ...options?.defaultMetadata, ...spec.metadata },
95
+ }, mkCtx(attempt, spec.signal, idem))));
96
+ await hooks?.afterCreate?.({ id: handle.id, provider: p.name });
97
+ const sandbox = buildSandbox(p, handle, base, { createOptions });
98
+ for (const pl of plugins) {
99
+ await pl.onCreate?.(sandbox, { createOptions });
100
+ }
101
+ return sandbox;
102
+ }
103
+ catch (error) {
104
+ attempts.push({ error, provider: p.name });
105
+ }
106
+ }
107
+ if (attempts.length === 1) {
108
+ throw attempts[0].error;
109
+ }
110
+ throw new AllProvidersFailedError(attempts);
111
+ },
112
+ async dispose() {
113
+ await provider.dispose?.();
114
+ },
115
+ async *list(filter) {
116
+ if (!provider.list) {
117
+ throw new NotSupportedError(provider.name, "list");
118
+ }
119
+ yield* provider.list(filter, mkCtx(1));
120
+ },
121
+ get provider() {
122
+ return provider.name;
123
+ },
124
+ };
125
+ return client;
126
+ }
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Encoding helpers shared by adapters that move bytes over text channels
3
+ * (exec + base64). Web-standard only (atob/btoa) so they work on Node, Bun,
4
+ * Deno, and Workers. (For shell quoting, use `shellQuote` from the adapter kit.)
5
+ */
6
+ export declare function bytesToBase64(b: Uint8Array): string;
7
+ export declare function base64ToBytes(b64: string): Uint8Array;
8
+ //# sourceMappingURL=encoding.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"encoding.d.ts","sourceRoot":"","sources":["../../src/internal/encoding.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,wBAAgB,aAAa,CAAC,CAAC,EAAE,UAAU,GAAG,MAAM,CAMnD;AAED,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,UAAU,CAOrD"}
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Encoding helpers shared by adapters that move bytes over text channels
3
+ * (exec + base64). Web-standard only (atob/btoa) so they work on Node, Bun,
4
+ * Deno, and Workers. (For shell quoting, use `shellQuote` from the adapter kit.)
5
+ */
6
+ export function bytesToBase64(b) {
7
+ let s = "";
8
+ for (const x of b) {
9
+ s += String.fromCodePoint(x);
10
+ }
11
+ return btoa(s);
12
+ }
13
+ export function base64ToBytes(b64) {
14
+ const bin = atob(b64.replace(/\s/g, ""));
15
+ const out = new Uint8Array(bin.length);
16
+ for (let i = 0; i < bin.length; i++) {
17
+ out[i] = bin.codePointAt(i) ?? 0;
18
+ }
19
+ return out;
20
+ }
@@ -0,0 +1,45 @@
1
+ /**
2
+ * One normalized error taxonomy for every provider. Providers must never leak
3
+ * their native SDK errors past the adapter boundary — they route through
4
+ * `mapError` or `SandboxError.wrap()`.
5
+ */
6
+ export type SandboxErrorCode = "NotFound" | "Unauthorized" | "Timeout" | "QuotaExceeded" | "NotSupported" | "Conflict" | "ProviderNotFound" | "Validation" | "AllProvidersFailed" | "Provider";
7
+ export interface SandboxErrorInit {
8
+ provider?: string;
9
+ status?: number;
10
+ cause?: unknown;
11
+ retryable?: boolean;
12
+ timedOut?: boolean;
13
+ aborted?: boolean;
14
+ }
15
+ export declare class SandboxError extends Error {
16
+ readonly code: SandboxErrorCode;
17
+ readonly provider?: string;
18
+ readonly status?: number;
19
+ readonly retryable: boolean;
20
+ readonly timedOut: boolean;
21
+ readonly aborted: boolean;
22
+ constructor(code: SandboxErrorCode, message: string, init?: SandboxErrorInit);
23
+ /** Coerce any thrown value into a SandboxError (pass-through if already one). */
24
+ static wrap(err: unknown, provider?: string, fallback?: SandboxErrorCode): SandboxError;
25
+ }
26
+ /** Thrown synchronously by a namespace facade before any network call. */
27
+ export declare class NotSupportedError extends SandboxError {
28
+ readonly feature: string;
29
+ constructor(provider: string, feature: string);
30
+ }
31
+ export declare class ProviderNotFoundError extends SandboxError {
32
+ constructor(name: string, known?: string[]);
33
+ }
34
+ export interface ProviderAttempt {
35
+ provider: string;
36
+ error: unknown;
37
+ }
38
+ export declare class AllProvidersFailedError extends SandboxError {
39
+ readonly attempts: ProviderAttempt[];
40
+ constructor(attempts: ProviderAttempt[]);
41
+ }
42
+ /** Centralized HTTP-status retryability classifier. */
43
+ export declare function isRetryableStatus(status?: number): boolean;
44
+ export declare function isRetryableError(err: unknown): err is SandboxError;
45
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/internal/errors.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,MAAM,MAAM,gBAAgB,GACxB,UAAU,GACV,cAAc,GACd,SAAS,GACT,eAAe,GACf,cAAc,GACd,UAAU,GACV,kBAAkB,GAClB,YAAY,GACZ,oBAAoB,GACpB,UAAU,CAAC;AAQf,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,qBAAa,YAAa,SAAQ,KAAK;IACrC,QAAQ,CAAC,IAAI,EAAE,gBAAgB,CAAC;IAChC,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;IAC5B,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;gBAGxB,IAAI,EAAE,gBAAgB,EACtB,OAAO,EAAE,MAAM,EACf,IAAI,GAAE,gBAAqB;IAe7B,iFAAiF;IACjF,MAAM,CAAC,IAAI,CACT,GAAG,EAAE,OAAO,EACZ,QAAQ,CAAC,EAAE,MAAM,EACjB,QAAQ,GAAE,gBAA6B,GACtC,YAAY;CAiBhB;AAED,0EAA0E;AAC1E,qBAAa,iBAAkB,SAAQ,YAAY;IACjD,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;gBACb,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM;CAS9C;AAED,qBAAa,qBAAsB,SAAQ,YAAY;gBACzC,IAAI,EAAE,MAAM,EAAE,KAAK,GAAE,MAAM,EAAO;CAO/C;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,OAAO,CAAC;CAChB;AAED,qBAAa,uBAAwB,SAAQ,YAAY;IACvD,QAAQ,CAAC,QAAQ,EAAE,eAAe,EAAE,CAAC;gBACzB,QAAQ,EAAE,eAAe,EAAE;CASxC;AAED,uDAAuD;AACvD,wBAAgB,iBAAiB,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAQ1D;AAED,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,OAAO,GAAG,GAAG,IAAI,YAAY,CAElE"}