@openai/agents-extensions 0.8.5 → 0.9.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.
Files changed (213) hide show
  1. package/dist/metadata.js +2 -2
  2. package/dist/metadata.mjs +2 -2
  3. package/dist/sandbox/blaxel/index.d.ts +2 -0
  4. package/dist/sandbox/blaxel/index.js +19 -0
  5. package/dist/sandbox/blaxel/index.js.map +1 -0
  6. package/dist/sandbox/blaxel/index.mjs +3 -0
  7. package/dist/sandbox/blaxel/index.mjs.map +1 -0
  8. package/dist/sandbox/blaxel/mounts.d.ts +101 -0
  9. package/dist/sandbox/blaxel/mounts.js +542 -0
  10. package/dist/sandbox/blaxel/mounts.js.map +1 -0
  11. package/dist/sandbox/blaxel/mounts.mjs +529 -0
  12. package/dist/sandbox/blaxel/mounts.mjs.map +1 -0
  13. package/dist/sandbox/blaxel/sandbox.d.ts +155 -0
  14. package/dist/sandbox/blaxel/sandbox.js +1110 -0
  15. package/dist/sandbox/blaxel/sandbox.js.map +1 -0
  16. package/dist/sandbox/blaxel/sandbox.mjs +1072 -0
  17. package/dist/sandbox/blaxel/sandbox.mjs.map +1 -0
  18. package/dist/sandbox/cloudflare/index.d.ts +2 -0
  19. package/dist/sandbox/cloudflare/index.js +19 -0
  20. package/dist/sandbox/cloudflare/index.js.map +1 -0
  21. package/dist/sandbox/cloudflare/index.mjs +3 -0
  22. package/dist/sandbox/cloudflare/index.mjs.map +1 -0
  23. package/dist/sandbox/cloudflare/mounts.d.ts +28 -0
  24. package/dist/sandbox/cloudflare/mounts.js +150 -0
  25. package/dist/sandbox/cloudflare/mounts.js.map +1 -0
  26. package/dist/sandbox/cloudflare/mounts.mjs +143 -0
  27. package/dist/sandbox/cloudflare/mounts.mjs.map +1 -0
  28. package/dist/sandbox/cloudflare/sandbox.d.ts +91 -0
  29. package/dist/sandbox/cloudflare/sandbox.js +1163 -0
  30. package/dist/sandbox/cloudflare/sandbox.js.map +1 -0
  31. package/dist/sandbox/cloudflare/sandbox.mjs +1125 -0
  32. package/dist/sandbox/cloudflare/sandbox.mjs.map +1 -0
  33. package/dist/sandbox/daytona/index.d.ts +2 -0
  34. package/dist/sandbox/daytona/index.js +19 -0
  35. package/dist/sandbox/daytona/index.js.map +1 -0
  36. package/dist/sandbox/daytona/index.mjs +3 -0
  37. package/dist/sandbox/daytona/index.mjs.map +1 -0
  38. package/dist/sandbox/daytona/mounts.d.ts +11 -0
  39. package/dist/sandbox/daytona/mounts.js +17 -0
  40. package/dist/sandbox/daytona/mounts.js.map +1 -0
  41. package/dist/sandbox/daytona/mounts.mjs +12 -0
  42. package/dist/sandbox/daytona/mounts.mjs.map +1 -0
  43. package/dist/sandbox/daytona/sandbox.d.ts +160 -0
  44. package/dist/sandbox/daytona/sandbox.js +938 -0
  45. package/dist/sandbox/daytona/sandbox.js.map +1 -0
  46. package/dist/sandbox/daytona/sandbox.mjs +900 -0
  47. package/dist/sandbox/daytona/sandbox.mjs.map +1 -0
  48. package/dist/sandbox/e2b/index.d.ts +2 -0
  49. package/dist/sandbox/e2b/index.js +19 -0
  50. package/dist/sandbox/e2b/index.js.map +1 -0
  51. package/dist/sandbox/e2b/index.mjs +3 -0
  52. package/dist/sandbox/e2b/index.mjs.map +1 -0
  53. package/dist/sandbox/e2b/mounts.d.ts +11 -0
  54. package/dist/sandbox/e2b/mounts.js +17 -0
  55. package/dist/sandbox/e2b/mounts.js.map +1 -0
  56. package/dist/sandbox/e2b/mounts.mjs +12 -0
  57. package/dist/sandbox/e2b/mounts.mjs.map +1 -0
  58. package/dist/sandbox/e2b/sandbox.d.ts +169 -0
  59. package/dist/sandbox/e2b/sandbox.js +829 -0
  60. package/dist/sandbox/e2b/sandbox.js.map +1 -0
  61. package/dist/sandbox/e2b/sandbox.mjs +791 -0
  62. package/dist/sandbox/e2b/sandbox.mjs.map +1 -0
  63. package/dist/sandbox/modal/index.d.ts +2 -0
  64. package/dist/sandbox/modal/index.js +19 -0
  65. package/dist/sandbox/modal/index.js.map +1 -0
  66. package/dist/sandbox/modal/index.mjs +3 -0
  67. package/dist/sandbox/modal/index.mjs.map +1 -0
  68. package/dist/sandbox/modal/mounts.d.ts +24 -0
  69. package/dist/sandbox/modal/mounts.js +194 -0
  70. package/dist/sandbox/modal/mounts.js.map +1 -0
  71. package/dist/sandbox/modal/mounts.mjs +188 -0
  72. package/dist/sandbox/modal/mounts.mjs.map +1 -0
  73. package/dist/sandbox/modal/sandbox.d.ts +220 -0
  74. package/dist/sandbox/modal/sandbox.js +1621 -0
  75. package/dist/sandbox/modal/sandbox.js.map +1 -0
  76. package/dist/sandbox/modal/sandbox.mjs +1581 -0
  77. package/dist/sandbox/modal/sandbox.mjs.map +1 -0
  78. package/dist/sandbox/runloop/index.d.ts +2 -0
  79. package/dist/sandbox/runloop/index.js +19 -0
  80. package/dist/sandbox/runloop/index.js.map +1 -0
  81. package/dist/sandbox/runloop/index.mjs +3 -0
  82. package/dist/sandbox/runloop/index.mjs.map +1 -0
  83. package/dist/sandbox/runloop/mounts.d.ts +11 -0
  84. package/dist/sandbox/runloop/mounts.js +17 -0
  85. package/dist/sandbox/runloop/mounts.js.map +1 -0
  86. package/dist/sandbox/runloop/mounts.mjs +12 -0
  87. package/dist/sandbox/runloop/mounts.mjs.map +1 -0
  88. package/dist/sandbox/runloop/sandbox.d.ts +266 -0
  89. package/dist/sandbox/runloop/sandbox.js +1390 -0
  90. package/dist/sandbox/runloop/sandbox.js.map +1 -0
  91. package/dist/sandbox/runloop/sandbox.mjs +1346 -0
  92. package/dist/sandbox/runloop/sandbox.mjs.map +1 -0
  93. package/dist/sandbox/shared/archive.d.ts +34 -0
  94. package/dist/sandbox/shared/archive.js +397 -0
  95. package/dist/sandbox/shared/archive.js.map +1 -0
  96. package/dist/sandbox/shared/archive.mjs +389 -0
  97. package/dist/sandbox/shared/archive.mjs.map +1 -0
  98. package/dist/sandbox/shared/editor.d.ts +17 -0
  99. package/dist/sandbox/shared/editor.js +65 -0
  100. package/dist/sandbox/shared/editor.js.map +1 -0
  101. package/dist/sandbox/shared/editor.mjs +61 -0
  102. package/dist/sandbox/shared/editor.mjs.map +1 -0
  103. package/dist/sandbox/shared/environment.d.ts +1 -0
  104. package/dist/sandbox/shared/environment.js +13 -0
  105. package/dist/sandbox/shared/environment.js.map +1 -0
  106. package/dist/sandbox/shared/environment.mjs +2 -0
  107. package/dist/sandbox/shared/environment.mjs.map +1 -0
  108. package/dist/sandbox/shared/git.d.ts +4 -0
  109. package/dist/sandbox/shared/git.js +20 -0
  110. package/dist/sandbox/shared/git.js.map +1 -0
  111. package/dist/sandbox/shared/git.mjs +17 -0
  112. package/dist/sandbox/shared/git.mjs.map +1 -0
  113. package/dist/sandbox/shared/inContainerMounts.d.ts +40 -0
  114. package/dist/sandbox/shared/inContainerMounts.js +670 -0
  115. package/dist/sandbox/shared/inContainerMounts.js.map +1 -0
  116. package/dist/sandbox/shared/inContainerMounts.mjs +662 -0
  117. package/dist/sandbox/shared/inContainerMounts.mjs.map +1 -0
  118. package/dist/sandbox/shared/index.d.ts +23 -0
  119. package/dist/sandbox/shared/index.js +112 -0
  120. package/dist/sandbox/shared/index.js.map +1 -0
  121. package/dist/sandbox/shared/index.mjs +18 -0
  122. package/dist/sandbox/shared/index.mjs.map +1 -0
  123. package/dist/sandbox/shared/localSources.d.ts +8 -0
  124. package/dist/sandbox/shared/localSources.js +378 -0
  125. package/dist/sandbox/shared/localSources.js.map +1 -0
  126. package/dist/sandbox/shared/localSources.mjs +372 -0
  127. package/dist/sandbox/shared/localSources.mjs.map +1 -0
  128. package/dist/sandbox/shared/manifest.d.ts +38 -0
  129. package/dist/sandbox/shared/manifest.js +254 -0
  130. package/dist/sandbox/shared/manifest.js.map +1 -0
  131. package/dist/sandbox/shared/manifest.mjs +233 -0
  132. package/dist/sandbox/shared/manifest.mjs.map +1 -0
  133. package/dist/sandbox/shared/media.d.ts +1 -0
  134. package/dist/sandbox/shared/media.js +10 -0
  135. package/dist/sandbox/shared/media.js.map +1 -0
  136. package/dist/sandbox/shared/media.mjs +2 -0
  137. package/dist/sandbox/shared/media.mjs.map +1 -0
  138. package/dist/sandbox/shared/metadata.d.ts +7 -0
  139. package/dist/sandbox/shared/metadata.js +87 -0
  140. package/dist/sandbox/shared/metadata.js.map +1 -0
  141. package/dist/sandbox/shared/metadata.mjs +81 -0
  142. package/dist/sandbox/shared/metadata.mjs.map +1 -0
  143. package/dist/sandbox/shared/output.d.ts +1 -0
  144. package/dist/sandbox/shared/output.js +8 -0
  145. package/dist/sandbox/shared/output.js.map +1 -0
  146. package/dist/sandbox/shared/output.mjs +2 -0
  147. package/dist/sandbox/shared/output.mjs.map +1 -0
  148. package/dist/sandbox/shared/paths.d.ts +27 -0
  149. package/dist/sandbox/shared/paths.js +245 -0
  150. package/dist/sandbox/shared/paths.js.map +1 -0
  151. package/dist/sandbox/shared/paths.mjs +236 -0
  152. package/dist/sandbox/shared/paths.mjs.map +1 -0
  153. package/dist/sandbox/shared/ports.d.ts +14 -0
  154. package/dist/sandbox/shared/ports.js +75 -0
  155. package/dist/sandbox/shared/ports.js.map +1 -0
  156. package/dist/sandbox/shared/ports.mjs +69 -0
  157. package/dist/sandbox/shared/ports.mjs.map +1 -0
  158. package/dist/sandbox/shared/process.d.ts +16 -0
  159. package/dist/sandbox/shared/process.js +90 -0
  160. package/dist/sandbox/shared/process.js.map +1 -0
  161. package/dist/sandbox/shared/process.mjs +86 -0
  162. package/dist/sandbox/shared/process.mjs.map +1 -0
  163. package/dist/sandbox/shared/pty.d.ts +81 -0
  164. package/dist/sandbox/shared/pty.js +382 -0
  165. package/dist/sandbox/shared/pty.js.map +1 -0
  166. package/dist/sandbox/shared/pty.mjs +335 -0
  167. package/dist/sandbox/shared/pty.mjs.map +1 -0
  168. package/dist/sandbox/shared/runAs.d.ts +46 -0
  169. package/dist/sandbox/shared/runAs.js +149 -0
  170. package/dist/sandbox/shared/runAs.js.map +1 -0
  171. package/dist/sandbox/shared/runAs.mjs +141 -0
  172. package/dist/sandbox/shared/runAs.mjs.map +1 -0
  173. package/dist/sandbox/shared/session.d.ts +16 -0
  174. package/dist/sandbox/shared/session.js +138 -0
  175. package/dist/sandbox/shared/session.js.map +1 -0
  176. package/dist/sandbox/shared/session.mjs +128 -0
  177. package/dist/sandbox/shared/session.mjs.map +1 -0
  178. package/dist/sandbox/shared/sessionBase.d.ts +90 -0
  179. package/dist/sandbox/shared/sessionBase.js +446 -0
  180. package/dist/sandbox/shared/sessionBase.js.map +1 -0
  181. package/dist/sandbox/shared/sessionBase.mjs +442 -0
  182. package/dist/sandbox/shared/sessionBase.mjs.map +1 -0
  183. package/dist/sandbox/shared/sessionState.d.ts +7 -0
  184. package/dist/sandbox/shared/sessionState.js +35 -0
  185. package/dist/sandbox/shared/sessionState.js.map +1 -0
  186. package/dist/sandbox/shared/sessionState.mjs +31 -0
  187. package/dist/sandbox/shared/sessionState.mjs.map +1 -0
  188. package/dist/sandbox/shared/snapshots.d.ts +9 -0
  189. package/dist/sandbox/shared/snapshots.js +62 -0
  190. package/dist/sandbox/shared/snapshots.js.map +1 -0
  191. package/dist/sandbox/shared/snapshots.mjs +57 -0
  192. package/dist/sandbox/shared/snapshots.mjs.map +1 -0
  193. package/dist/sandbox/shared/typeGuards.d.ts +10 -0
  194. package/dist/sandbox/shared/typeGuards.js +50 -0
  195. package/dist/sandbox/shared/typeGuards.js.map +1 -0
  196. package/dist/sandbox/shared/typeGuards.mjs +38 -0
  197. package/dist/sandbox/shared/typeGuards.mjs.map +1 -0
  198. package/dist/sandbox/shared/types.d.ts +24 -0
  199. package/dist/sandbox/shared/types.js +3 -0
  200. package/dist/sandbox/shared/types.js.map +1 -0
  201. package/dist/sandbox/shared/types.mjs +2 -0
  202. package/dist/sandbox/shared/types.mjs.map +1 -0
  203. package/dist/sandbox/vercel/index.d.ts +1 -0
  204. package/dist/sandbox/vercel/index.js +18 -0
  205. package/dist/sandbox/vercel/index.js.map +1 -0
  206. package/dist/sandbox/vercel/index.mjs +2 -0
  207. package/dist/sandbox/vercel/index.mjs.map +1 -0
  208. package/dist/sandbox/vercel/sandbox.d.ts +135 -0
  209. package/dist/sandbox/vercel/sandbox.js +1001 -0
  210. package/dist/sandbox/vercel/sandbox.js.map +1 -0
  211. package/dist/sandbox/vercel/sandbox.mjs +963 -0
  212. package/dist/sandbox/vercel/sandbox.mjs.map +1 -0
  213. package/package.json +108 -4
@@ -0,0 +1,1072 @@
1
+ import { UserError } from '@openai/agents-core';
2
+ import { loadEnv, randomUUID } from '@openai/agents-core/_shims';
3
+ import { SandboxProviderError, SandboxUnsupportedFeatureError, normalizeSandboxClientCreateArgs, } from '@openai/agents-core/sandbox';
4
+ import { addPtyWebSocketListener, appendPtyOutput, assertCoreSnapshotUnsupported, assertResumeRecreateAllowed, createPtyProcessEntry, assertSandboxManifestMetadataSupported, SANDBOX_MANIFEST_METADATA_SUPPORT, assertRunAsUnsupported, closeRemoteSessionOnManifestError, assertShellEnvironmentName, deserializeRemoteSandboxSessionStateValues, formatPtyExecUpdate, materializeEnvironment, openPtyWebSocket, parseExposedPortEndpoint, posixDirname, resolveSandboxWorkdir, serializeRemoteSandboxSessionState, shellQuote, shellCommandForPty, toUint8Array, markPtyDone, writePtyStdin, isRecord, readOptionalBoolean, readOptionalNumber, readOptionalRecordArray, readOptionalString, readOptionalStringRecord, readString, withProviderError, withSandboxSpan, PtyProcessRegistry, RemoteSandboxSessionBase, } from "../shared/index.mjs";
5
+ import { isBlaxelCloudBucketMountEntry, isBlaxelDriveMountEntry, mountBlaxelCloudBucket, mountBlaxelDrive, unmountBlaxelDrive, unmountBlaxelFuseMount, } from "./mounts.mjs";
6
+ export const DEFAULT_BLAXEL_WORKSPACE_ROOT = '/workspace';
7
+ const BLAXEL_AUTOGENERATED_NAME_CREATE_ATTEMPTS = 5;
8
+ const BLAXEL_NON_RESUMABLE_SANDBOX_STATUSES = new Set([
9
+ 'DEACTIVATING',
10
+ 'DEACTIVATED',
11
+ 'DELETING',
12
+ 'FAILED',
13
+ 'TERMINATING',
14
+ 'TERMINATED',
15
+ ]);
16
+ export class BlaxelSandboxSession extends RemoteSandboxSessionBase {
17
+ sandbox;
18
+ apiKey;
19
+ ownsSandbox;
20
+ closed = false;
21
+ ptyProcesses = new PtyProcessRegistry();
22
+ activeFuseMountPaths = new Set();
23
+ activeDriveMountPaths = new Set();
24
+ constructor(args) {
25
+ super({
26
+ state: args.state,
27
+ options: {
28
+ providerName: 'BlaxelSandboxClient',
29
+ providerId: 'blaxel',
30
+ concurrencyLimits: args.concurrencyLimits,
31
+ },
32
+ });
33
+ this.sandbox = args.sandbox;
34
+ this.apiKey = args.apiKey;
35
+ this.ownsSandbox = args.ownsSandbox ?? true;
36
+ this.ensureMountArtifactScope();
37
+ }
38
+ supportsPty() {
39
+ return Boolean(this.state.sandboxUrl && this.apiKey);
40
+ }
41
+ async writeStdin(args) {
42
+ return await writePtyStdin({
43
+ providerName: 'BlaxelSandboxClient',
44
+ registry: this.ptyProcesses,
45
+ sessionId: args.sessionId,
46
+ chars: args.chars,
47
+ yieldTimeMs: args.yieldTimeMs,
48
+ maxOutputTokens: args.maxOutputTokens,
49
+ });
50
+ }
51
+ async execPtyCommand(args) {
52
+ assertRunAsUnsupported('BlaxelSandboxClient', args.runAs);
53
+ if (!this.state.sandboxUrl || !this.apiKey) {
54
+ throw new SandboxUnsupportedFeatureError('BlaxelSandboxClient tty=true requires a sandbox URL and apiKey.', {
55
+ provider: 'blaxel',
56
+ feature: 'tty',
57
+ });
58
+ }
59
+ const start = Date.now();
60
+ const entry = createPtyProcessEntry({ tty: true });
61
+ const socket = await openPtyWebSocket({
62
+ url: buildBlaxelPtyWebSocketUrl({
63
+ sandboxUrl: this.state.sandboxUrl,
64
+ token: this.apiKey,
65
+ sessionId: `pty-${Math.random().toString(16).slice(2, 14)}`,
66
+ cwd: resolveSandboxWorkdir(this.state.manifest.root, args.workdir),
67
+ }),
68
+ providerName: 'BlaxelSandboxClient',
69
+ });
70
+ const removeMessageListener = addPtyWebSocketListener(socket, 'message', (event) => handleBlaxelPtyMessage(entry, event));
71
+ const removeCloseListener = addPtyWebSocketListener(socket, 'close', (event) => {
72
+ markPtyDone(entry, blaxelPtyCloseExitCode(entry, event));
73
+ });
74
+ const removeErrorListener = addPtyWebSocketListener(socket, 'error', () => {
75
+ markPtyDone(entry, 1);
76
+ });
77
+ entry.sendInput = async (chars) => {
78
+ socket.send(JSON.stringify({ type: 'input', data: chars }));
79
+ };
80
+ entry.terminate = async () => {
81
+ removeMessageListener();
82
+ removeCloseListener();
83
+ removeErrorListener();
84
+ socket.close();
85
+ };
86
+ let registered = false;
87
+ try {
88
+ await entry.sendInput(`${shellCommandForPty({
89
+ ...args,
90
+ cmd: buildShellCommand(args.cmd, this.state.environment),
91
+ })}\n`);
92
+ const { sessionId, pruned } = this.ptyProcesses.register(entry);
93
+ registered = true;
94
+ if (pruned) {
95
+ await pruned.terminate?.().catch(() => { });
96
+ }
97
+ return await formatPtyExecUpdate({
98
+ registry: this.ptyProcesses,
99
+ sessionId,
100
+ entry,
101
+ startTime: start,
102
+ yieldTimeMs: args.yieldTimeMs,
103
+ maxOutputTokens: args.maxOutputTokens,
104
+ });
105
+ }
106
+ finally {
107
+ if (!registered) {
108
+ await entry.terminate?.().catch(() => { });
109
+ }
110
+ }
111
+ }
112
+ allowOnDemandExposedPorts() {
113
+ return true;
114
+ }
115
+ useCachedExposedPortEndpoint(_port) {
116
+ return this.state.exposedPortPublic !== false;
117
+ }
118
+ exposedPortSource() {
119
+ return 'preview URL';
120
+ }
121
+ async resolveRemoteExposedPort(requestedPort) {
122
+ if (!this.sandbox.previews?.createIfNotExists) {
123
+ throw new SandboxProviderError('BlaxelSandboxClient exposed port resolution requires the Blaxel previews API.', {
124
+ provider: 'blaxel',
125
+ port: requestedPort,
126
+ });
127
+ }
128
+ let preview;
129
+ try {
130
+ preview = await this.sandbox.previews.createIfNotExists({
131
+ metadata: { name: `port-${requestedPort}` },
132
+ spec: {
133
+ port: requestedPort,
134
+ public: this.state.exposedPortPublic ?? true,
135
+ },
136
+ });
137
+ }
138
+ catch (error) {
139
+ throw new SandboxProviderError(`BlaxelSandboxClient failed to create a preview for exposed port ${requestedPort}.`, {
140
+ provider: 'blaxel',
141
+ port: requestedPort,
142
+ cause: error instanceof Error ? error.message : String(error),
143
+ });
144
+ }
145
+ const url = extractBlaxelPreviewUrl(preview);
146
+ if (!url) {
147
+ throw new SandboxProviderError('BlaxelSandboxClient preview did not include a URL.', {
148
+ provider: 'blaxel',
149
+ port: requestedPort,
150
+ });
151
+ }
152
+ const query = this.state.exposedPortPublic === false
153
+ ? await createBlaxelPreviewTokenQuery(preview, this.state.exposedPortUrlTtlS ?? 3600, requestedPort)
154
+ : undefined;
155
+ return parseExposedPortEndpoint(url, {
156
+ providerName: 'BlaxelSandboxClient',
157
+ source: 'preview URL',
158
+ query,
159
+ });
160
+ }
161
+ async prepareWorkspaceRoot() {
162
+ const result = await this.sandbox.process.exec({
163
+ command: `mkdir -p -- ${shellQuote(this.state.manifest.root)}`,
164
+ workingDir: '/',
165
+ waitForCompletion: true,
166
+ timeout: this.state.timeouts?.fastOperationTimeoutMs,
167
+ });
168
+ if ((result.exitCode ?? 1) !== 0) {
169
+ throw new SandboxProviderError('BlaxelSandboxClient failed to prepare the workspace root.', {
170
+ provider: 'blaxel',
171
+ operation: 'prepare workspace root',
172
+ sandboxName: this.state.sandboxName,
173
+ root: this.state.manifest.root,
174
+ stderr: result.stderr ?? '',
175
+ stdout: result.stdout ?? '',
176
+ });
177
+ }
178
+ }
179
+ async close() {
180
+ await this.ptyProcesses.terminateAll();
181
+ if (this.state.pauseOnExit || this.closed) {
182
+ return;
183
+ }
184
+ await withSandboxSpan('sandbox.shutdown', {
185
+ backend_id: 'blaxel',
186
+ sandbox_name: this.state.sandboxName,
187
+ }, async () => {
188
+ await this.unmountActiveMounts();
189
+ if (this.ownsSandbox) {
190
+ await this.sandbox.delete();
191
+ }
192
+ });
193
+ this.closed = true;
194
+ }
195
+ async shutdown() {
196
+ await this.close();
197
+ }
198
+ async delete() {
199
+ await this.ptyProcesses.terminateAll();
200
+ if (this.closed) {
201
+ return;
202
+ }
203
+ await withSandboxSpan('sandbox.shutdown', {
204
+ backend_id: 'blaxel',
205
+ sandbox_name: this.state.sandboxName,
206
+ }, async () => {
207
+ await this.unmountActiveMounts();
208
+ if (this.ownsSandbox) {
209
+ await this.sandbox.delete();
210
+ }
211
+ });
212
+ this.closed = true;
213
+ }
214
+ async rehydrateActiveMountPathsFromManifest() {
215
+ for (const { logicalPath, entry, } of this.state.manifest.mountTargetsForMaterialization()) {
216
+ const mountPath = await this.resolveRemotePath(entry.mountPath ?? logicalPath, { forWrite: true });
217
+ if (isBlaxelCloudBucketMountEntry(entry)) {
218
+ this.activeFuseMountPaths.add(mountPath);
219
+ }
220
+ else if (isBlaxelDriveMountEntry(entry)) {
221
+ this.activeDriveMountPaths.add(mountPath);
222
+ }
223
+ }
224
+ }
225
+ manifestMetadataSupport() {
226
+ return SANDBOX_MANIFEST_METADATA_SUPPORT;
227
+ }
228
+ manifestMaterializationOptions() {
229
+ return {
230
+ materializeMount: this.materializeMountEntry.bind(this),
231
+ };
232
+ }
233
+ assertExecRunAs(_runAs) {
234
+ void _runAs;
235
+ }
236
+ assertFilesystemRunAs(_runAs) {
237
+ void _runAs;
238
+ }
239
+ async materializeMountEntry(absolutePath, entry) {
240
+ const mountPath = await this.resolveRemotePath(entry.mountPath ?? absolutePath, { forWrite: true });
241
+ if (entry.mountStrategy?.type === 'blaxel_cloud_bucket') {
242
+ await mountBlaxelCloudBucket({
243
+ entry,
244
+ mountPath,
245
+ artifactScope: this.ensureMountArtifactScope(),
246
+ runCommand: this.mountCommandRunner(),
247
+ writeSecretFile: this.writeMountSecretFile.bind(this),
248
+ });
249
+ this.activeFuseMountPaths.add(mountPath);
250
+ return;
251
+ }
252
+ if (entry.mountStrategy?.type === 'blaxel_drive') {
253
+ const config = await mountBlaxelDrive({
254
+ entry,
255
+ mountPath,
256
+ drives: this.sandbox.drives,
257
+ });
258
+ this.activeDriveMountPaths.add(config.mountPath);
259
+ return;
260
+ }
261
+ throw new SandboxUnsupportedFeatureError('BlaxelSandboxClient only supports BlaxelCloudBucketMountStrategy and BlaxelDriveMountStrategy mount entries.', {
262
+ provider: 'blaxel',
263
+ feature: 'entry.mountStrategy',
264
+ path: absolutePath,
265
+ mountType: entry.type,
266
+ strategyType: entry.mountStrategy?.type,
267
+ });
268
+ }
269
+ async unmountActiveMounts() {
270
+ for (const mountPath of [...this.activeDriveMountPaths].reverse()) {
271
+ try {
272
+ await unmountBlaxelDrive({
273
+ mountPath,
274
+ drives: this.sandbox.drives,
275
+ });
276
+ this.activeDriveMountPaths.delete(mountPath);
277
+ }
278
+ catch (error) {
279
+ throw new SandboxProviderError('BlaxelSandboxClient failed to unmount an active drive mount.', {
280
+ provider: 'blaxel',
281
+ mountPath,
282
+ cause: unknownErrorMessage(error),
283
+ });
284
+ }
285
+ }
286
+ for (const mountPath of [...this.activeFuseMountPaths].reverse()) {
287
+ try {
288
+ await unmountBlaxelFuseMount({
289
+ mountPath,
290
+ artifactScope: this.ensureMountArtifactScope(),
291
+ runCommand: this.mountCommandRunner(),
292
+ });
293
+ this.activeFuseMountPaths.delete(mountPath);
294
+ }
295
+ catch (error) {
296
+ throw new SandboxProviderError('BlaxelSandboxClient failed to unmount an active FUSE mount.', {
297
+ provider: 'blaxel',
298
+ mountPath,
299
+ cause: unknownErrorMessage(error),
300
+ });
301
+ }
302
+ }
303
+ }
304
+ mountCommandRunner() {
305
+ return async (command, options = {}) => {
306
+ const result = await this.sandbox.process.exec({
307
+ command,
308
+ workingDir: '/',
309
+ waitForCompletion: true,
310
+ timeout: options.timeoutMs ??
311
+ this.state.timeouts?.fastOperationTimeoutMs ??
312
+ this.state.timeoutMs,
313
+ });
314
+ return {
315
+ status: result.exitCode ?? 1,
316
+ stdout: result.stdout ?? '',
317
+ stderr: result.stderr ?? '',
318
+ };
319
+ };
320
+ }
321
+ async writeMountSecretFile(path, content) {
322
+ const operation = this.sandbox.fs.write(path, content);
323
+ await withBlaxelOperationTimeout(operation, this.state.timeouts?.fileUploadTimeoutMs, 'write mount secret', {
324
+ onTimeout: () => {
325
+ void operation.then(async () => {
326
+ await this.sandbox.fs.rm(path).catch(() => { });
327
+ }, () => { });
328
+ },
329
+ });
330
+ }
331
+ async runRemoteCommand(command, options) {
332
+ const shellCommand = buildShellCommand(command, this.state.environment);
333
+ const commandForUser = options.runAs
334
+ ? `sudo -n -u ${shellQuote(options.runAs)} -- sh -lc ${shellQuote(shellCommand)}`
335
+ : shellCommand;
336
+ const result = await this.sandbox.process.exec({
337
+ command: commandForUser,
338
+ workingDir: options.workdir,
339
+ waitForCompletion: true,
340
+ timeout: this.timeoutForCommand(options),
341
+ });
342
+ return {
343
+ status: result.exitCode ?? 1,
344
+ stdout: result.stdout ?? '',
345
+ stderr: result.stderr ?? '',
346
+ };
347
+ }
348
+ async mkdirRemote(path) {
349
+ await this.sandbox.fs.mkdir(path);
350
+ }
351
+ async readRemoteText(path) {
352
+ return await withBlaxelOperationTimeout(this.sandbox.fs.read(path), this.state.timeouts?.fileDownloadTimeoutMs, 'read file');
353
+ }
354
+ async readRemoteFile(path) {
355
+ return await toUint8Array(await withBlaxelOperationTimeout(this.sandbox.fs.readBinary(path), this.state.timeouts?.fileDownloadTimeoutMs, 'read binary file'));
356
+ }
357
+ async writeRemoteFile(path, content) {
358
+ const tempPath = remoteWriteTempPath(path);
359
+ if (typeof content === 'string') {
360
+ await this.writeRemoteTempFile(tempPath, this.sandbox.fs.write(tempPath, content), 'write file');
361
+ }
362
+ else {
363
+ await this.writeRemoteTempFile(tempPath, this.sandbox.fs.writeBinary(tempPath, content), 'write binary file');
364
+ }
365
+ try {
366
+ const result = await this.sandbox.process.exec({
367
+ command: `mv -f -- ${shellQuote(tempPath)} ${shellQuote(path)}`,
368
+ workingDir: '/',
369
+ waitForCompletion: true,
370
+ timeout: this.state.timeouts?.fileUploadTimeoutMs,
371
+ });
372
+ if ((result.exitCode ?? 1) === 0) {
373
+ return;
374
+ }
375
+ throw new SandboxProviderError('BlaxelSandboxClient failed to commit uploaded file.', {
376
+ provider: 'blaxel',
377
+ operation: 'commit uploaded file',
378
+ path,
379
+ tempPath,
380
+ exitCode: result.exitCode ?? 1,
381
+ stdout: result.stdout ?? '',
382
+ stderr: result.stderr ?? '',
383
+ });
384
+ }
385
+ catch (error) {
386
+ await this.sandbox.fs.rm(tempPath).catch(() => { });
387
+ throw error;
388
+ }
389
+ }
390
+ async writeRemoteTempFile(tempPath, operation, operationName) {
391
+ await withBlaxelOperationTimeout(operation, this.state.timeouts?.fileUploadTimeoutMs, operationName, {
392
+ onTimeout: () => {
393
+ void operation.then(async () => {
394
+ await this.sandbox.fs.rm(tempPath).catch(() => { });
395
+ }, () => { });
396
+ },
397
+ });
398
+ }
399
+ async deleteRemotePath(path) {
400
+ await this.sandbox.fs.rm(path);
401
+ }
402
+ ensureMountArtifactScope() {
403
+ this.state.mountArtifactScope ??= randomUUID();
404
+ return this.state.mountArtifactScope;
405
+ }
406
+ timeoutForCommand(options) {
407
+ if (options.timeoutMs !== undefined) {
408
+ return options.timeoutMs;
409
+ }
410
+ if (options.kind === 'archive') {
411
+ return this.state.timeouts?.workspaceTarTimeoutMs ?? this.state.timeoutMs;
412
+ }
413
+ if (options.kind === 'exec') {
414
+ return this.state.timeouts?.execTimeoutMs ?? this.state.timeoutMs;
415
+ }
416
+ if (options.kind === 'path' || options.kind === 'running') {
417
+ return this.state.timeouts?.fastOperationTimeoutMs;
418
+ }
419
+ return undefined;
420
+ }
421
+ }
422
+ /**
423
+ * @see {@link https://docs.blaxel.ai/Sandboxes/Overview | Sandboxes overview}.
424
+ * @see {@link https://docs.blaxel.ai/Sandboxes/Filesystem | Filesystem operations}.
425
+ * @see {@link https://docs.blaxel.ai/Sandboxes/Sessions | Client-side sessions}.
426
+ */
427
+ export class BlaxelSandboxClient {
428
+ backendId = 'blaxel';
429
+ options;
430
+ constructor(options = {}) {
431
+ this.options = options;
432
+ }
433
+ async create(args, manifestOptions, internalOptions = {}) {
434
+ const createArgs = normalizeSandboxClientCreateArgs(args, manifestOptions);
435
+ assertCoreSnapshotUnsupported('BlaxelSandboxClient', createArgs.snapshot);
436
+ const manifest = createArgs.manifest;
437
+ const resolvedOptions = {
438
+ ...this.options,
439
+ ...createArgs.options,
440
+ };
441
+ const timeouts = resolveBlaxelTimeouts(resolvedOptions);
442
+ assertSandboxManifestMetadataSupported('BlaxelSandboxClient', manifest, SANDBOX_MANIFEST_METADATA_SUPPORT);
443
+ const SandboxInstance = await loadBlaxelSandboxClass();
444
+ return await withSandboxSpan('sandbox.start', {
445
+ backend_id: this.backendId,
446
+ }, async () => {
447
+ const sandboxCreateParams = {
448
+ ...(resolvedOptions.image ? { image: resolvedOptions.image } : {}),
449
+ ...(typeof resolvedOptions.memory === 'number'
450
+ ? { memory: resolvedOptions.memory }
451
+ : {}),
452
+ ...(resolvedOptions.region ? { region: resolvedOptions.region } : {}),
453
+ ...(resolvedOptions.ports ? { ports: resolvedOptions.ports } : {}),
454
+ ...(typeof resolvedOptions.exposedPortPublic === 'boolean'
455
+ ? { exposedPortPublic: resolvedOptions.exposedPortPublic }
456
+ : {}),
457
+ ...(typeof resolvedOptions.exposedPortUrlTtlS === 'number'
458
+ ? { exposedPortUrlTtlS: resolvedOptions.exposedPortUrlTtlS }
459
+ : {}),
460
+ ...(typeof resolvedOptions.timeoutMs === 'number'
461
+ ? { timeoutMs: resolvedOptions.timeoutMs }
462
+ : {}),
463
+ ...(typeof timeouts.createTimeoutMs === 'number'
464
+ ? { createTimeoutMs: timeouts.createTimeoutMs }
465
+ : {}),
466
+ ...(resolvedOptions.labels ? { labels: resolvedOptions.labels } : {}),
467
+ ...(resolvedOptions.ttl ? { ttl: resolvedOptions.ttl } : {}),
468
+ };
469
+ const { sandbox, ownsSandbox, sandboxName } = await withProviderError('BlaxelSandboxClient', 'blaxel', 'create sandbox', async () => await createBlaxelSandboxWithOwnership({
470
+ SandboxInstance,
471
+ sandboxName: resolvedOptions.name,
472
+ allowExistingNamedSandbox: internalOptions.allowExistingNamedSandbox ?? true,
473
+ paramsForName: (sandboxName) => ({
474
+ name: sandboxName,
475
+ ...sandboxCreateParams,
476
+ }),
477
+ }), { sandboxName: resolvedOptions.name });
478
+ let environment;
479
+ try {
480
+ environment = await materializeEnvironment(manifest, resolvedOptions.env);
481
+ }
482
+ catch (error) {
483
+ if (ownsSandbox) {
484
+ try {
485
+ await sandbox.delete();
486
+ }
487
+ catch (deleteError) {
488
+ throw new UserError(`Failed to materialize a Blaxel sandbox environment and delete the sandbox. Environment error: ${unknownErrorMessage(error)} Delete error: ${unknownErrorMessage(deleteError)}`);
489
+ }
490
+ }
491
+ throw error;
492
+ }
493
+ const session = new BlaxelSandboxSession({
494
+ sandbox,
495
+ apiKey: resolvedOptions.apiKey ?? loadEnv().BL_API_KEY,
496
+ ownsSandbox,
497
+ concurrencyLimits: createArgs.concurrencyLimits,
498
+ state: {
499
+ manifest,
500
+ sandboxName,
501
+ image: resolvedOptions.image,
502
+ memory: resolvedOptions.memory,
503
+ region: resolvedOptions.region,
504
+ ports: resolvedOptions.ports,
505
+ exposedPortPublic: resolvedOptions.exposedPortPublic,
506
+ exposedPortUrlTtlS: resolvedOptions.exposedPortUrlTtlS,
507
+ timeoutMs: resolvedOptions.timeoutMs,
508
+ createTimeoutMs: timeouts.createTimeoutMs,
509
+ timeouts,
510
+ sandboxUrl: extractBlaxelSandboxUrl(sandbox),
511
+ sandboxIdentity: extractBlaxelSandboxIdentity(sandbox),
512
+ mountArtifactScope: randomUUID(),
513
+ labels: resolvedOptions.labels,
514
+ ttl: resolvedOptions.ttl,
515
+ pauseOnExit: resolvedOptions.pauseOnExit ?? false,
516
+ ownsSandbox,
517
+ environment,
518
+ },
519
+ });
520
+ try {
521
+ await session.prepareWorkspaceRoot();
522
+ await session.applyManifest(manifest);
523
+ }
524
+ catch (error) {
525
+ session.state.pauseOnExit = false;
526
+ await closeRemoteSessionOnManifestError('Blaxel', session, error);
527
+ }
528
+ return session;
529
+ });
530
+ }
531
+ async serializeSessionState(state) {
532
+ return serializeRemoteSandboxSessionState(state);
533
+ }
534
+ canPersistOwnedSessionState(state) {
535
+ return state.pauseOnExit && state.ownsSandbox && !!state.sandboxIdentity;
536
+ }
537
+ async deserializeSessionState(state) {
538
+ const baseState = deserializeRemoteSandboxSessionStateValues(state, this.options.env);
539
+ return {
540
+ ...state,
541
+ ...baseState,
542
+ sandboxName: readString(state, 'sandboxName'),
543
+ image: readOptionalString(state, 'image'),
544
+ memory: readOptionalNumber(state, 'memory'),
545
+ region: readOptionalString(state, 'region'),
546
+ ports: readOptionalRecordArray(state.ports),
547
+ exposedPortPublic: readOptionalBoolean(state, 'exposedPortPublic'),
548
+ exposedPortUrlTtlS: readOptionalNumber(state, 'exposedPortUrlTtlS'),
549
+ timeoutMs: readOptionalNumber(state, 'timeoutMs'),
550
+ createTimeoutMs: readOptionalNumber(state, 'createTimeoutMs'),
551
+ timeouts: resolveBlaxelTimeouts({
552
+ timeoutMs: readOptionalNumber(state, 'timeoutMs'),
553
+ createTimeoutMs: readOptionalNumber(state, 'createTimeoutMs'),
554
+ timeouts: isRecord(state.timeouts)
555
+ ? state.timeouts
556
+ : undefined,
557
+ }),
558
+ sandboxUrl: readOptionalString(state, 'sandboxUrl'),
559
+ sandboxIdentity: readOptionalString(state, 'sandboxIdentity'),
560
+ mountArtifactScope: readOptionalString(state, 'mountArtifactScope') ?? randomUUID(),
561
+ labels: readOptionalStringRecord(state.labels),
562
+ ttl: readOptionalString(state, 'ttl'),
563
+ pauseOnExit: Boolean(state.pauseOnExit),
564
+ ownsSandbox: readOptionalBoolean(state, 'ownsSandbox') ?? true,
565
+ };
566
+ }
567
+ async resume(state) {
568
+ const SandboxInstance = await loadBlaxelSandboxClass();
569
+ let sandbox;
570
+ try {
571
+ sandbox = await SandboxInstance.get(state.sandboxName);
572
+ }
573
+ catch (error) {
574
+ assertResumeRecreateAllowed(error, {
575
+ providerName: 'BlaxelSandboxClient',
576
+ provider: 'blaxel',
577
+ details: { sandboxName: state.sandboxName },
578
+ });
579
+ return await this.recreateFromState(state);
580
+ }
581
+ if (isBlaxelSandboxNonResumable(sandbox)) {
582
+ return await this.recreateFromState(state);
583
+ }
584
+ if (state.ownsSandbox) {
585
+ assertBlaxelSandboxIdentityMatchesState(sandbox, state);
586
+ }
587
+ const sandboxUrl = extractBlaxelSandboxUrl(sandbox);
588
+ if (sandboxUrl) {
589
+ state.sandboxUrl = sandboxUrl;
590
+ }
591
+ const session = new BlaxelSandboxSession({
592
+ state,
593
+ sandbox,
594
+ apiKey: this.options.apiKey ?? loadEnv().BL_API_KEY,
595
+ ownsSandbox: state.ownsSandbox,
596
+ });
597
+ await session.rehydrateActiveMountPathsFromManifest();
598
+ return session;
599
+ }
600
+ async recreateFromState(state) {
601
+ const session = await this.create(state.manifest, {
602
+ image: state.image,
603
+ memory: state.memory,
604
+ region: state.region,
605
+ ports: state.ports,
606
+ exposedPortPublic: state.exposedPortPublic,
607
+ exposedPortUrlTtlS: state.exposedPortUrlTtlS,
608
+ timeoutMs: state.timeoutMs,
609
+ createTimeoutMs: state.createTimeoutMs,
610
+ timeouts: state.timeouts,
611
+ apiKey: this.options.apiKey,
612
+ labels: state.labels,
613
+ ttl: state.ttl,
614
+ name: state.sandboxName,
615
+ pauseOnExit: state.pauseOnExit,
616
+ env: state.environment,
617
+ }, {
618
+ allowExistingNamedSandbox: !state.ownsSandbox,
619
+ });
620
+ return session;
621
+ }
622
+ }
623
+ async function loadBlaxelSandboxClass() {
624
+ try {
625
+ const { SandboxInstance } = await import('@blaxel/core');
626
+ if (!SandboxInstance) {
627
+ throw new Error('Missing SandboxInstance export from @blaxel/core.');
628
+ }
629
+ return adaptBlaxelSandboxClass(SandboxInstance);
630
+ }
631
+ catch (error) {
632
+ throw new UserError(`Blaxel sandbox support requires the optional \`@blaxel/core\` package. Install it before using Blaxel-backed sandbox examples. ${error.message}`);
633
+ }
634
+ }
635
+ async function createBlaxelSandboxWithOwnership(args) {
636
+ if (!args.sandboxName) {
637
+ return await createAutogeneratedBlaxelSandbox(args);
638
+ }
639
+ return await createNamedBlaxelSandboxWithOwnership({
640
+ SandboxInstance: args.SandboxInstance,
641
+ sandboxName: args.sandboxName,
642
+ allowExistingNamedSandbox: args.allowExistingNamedSandbox,
643
+ params: args.paramsForName(args.sandboxName),
644
+ });
645
+ }
646
+ async function createNamedBlaxelSandboxWithOwnership(args) {
647
+ try {
648
+ return {
649
+ sandbox: await args.SandboxInstance.create(args.params),
650
+ ownsSandbox: true,
651
+ sandboxName: args.sandboxName,
652
+ };
653
+ }
654
+ catch (error) {
655
+ if (!isBlaxelSandboxAlreadyExistsError(error)) {
656
+ throw error;
657
+ }
658
+ if (!args.allowExistingNamedSandbox) {
659
+ throw new UserError(`Blaxel sandbox ${args.sandboxName} cannot be safely recreated because another sandbox already uses that name.`);
660
+ }
661
+ }
662
+ const existing = await args.SandboxInstance.get(args.sandboxName);
663
+ if (!isBlaxelSandboxNonResumable(existing)) {
664
+ return {
665
+ sandbox: existing,
666
+ ownsSandbox: false,
667
+ sandboxName: args.sandboxName,
668
+ };
669
+ }
670
+ try {
671
+ return {
672
+ sandbox: await args.SandboxInstance.create(args.params),
673
+ ownsSandbox: true,
674
+ sandboxName: args.sandboxName,
675
+ };
676
+ }
677
+ catch (error) {
678
+ if (!isBlaxelSandboxAlreadyExistsError(error)) {
679
+ throw error;
680
+ }
681
+ if (!args.allowExistingNamedSandbox) {
682
+ throw new UserError(`Blaxel sandbox ${args.sandboxName} cannot be safely recreated because another sandbox already uses that name.`);
683
+ }
684
+ const fallback = await args.SandboxInstance.get(args.sandboxName);
685
+ assertBlaxelSandboxReusable(fallback, args.sandboxName);
686
+ return {
687
+ sandbox: fallback,
688
+ ownsSandbox: false,
689
+ sandboxName: args.sandboxName,
690
+ };
691
+ }
692
+ }
693
+ async function createAutogeneratedBlaxelSandbox(args) {
694
+ let lastConflict;
695
+ for (let attempt = 0; attempt < BLAXEL_AUTOGENERATED_NAME_CREATE_ATTEMPTS; attempt += 1) {
696
+ const sandboxName = generateBlaxelSandboxName();
697
+ try {
698
+ return {
699
+ sandbox: await args.SandboxInstance.create(args.paramsForName(sandboxName)),
700
+ ownsSandbox: true,
701
+ sandboxName,
702
+ };
703
+ }
704
+ catch (error) {
705
+ if (!isBlaxelSandboxAlreadyExistsError(error)) {
706
+ throw error;
707
+ }
708
+ lastConflict = error;
709
+ }
710
+ }
711
+ throw lastConflict;
712
+ }
713
+ function generateBlaxelSandboxName() {
714
+ return `openai-agents-${Math.random().toString(16).slice(2, 10)}`;
715
+ }
716
+ function isBlaxelSandboxNonResumable(sandbox) {
717
+ const status = sandbox.status?.trim().toUpperCase();
718
+ return status ? BLAXEL_NON_RESUMABLE_SANDBOX_STATUSES.has(status) : false;
719
+ }
720
+ function assertBlaxelSandboxReusable(sandbox, sandboxName) {
721
+ if (!isBlaxelSandboxNonResumable(sandbox)) {
722
+ return;
723
+ }
724
+ throw new UserError(`Blaxel sandbox ${sandboxName} cannot be safely reused because its status is ${sandbox.status ?? 'unknown'}.`);
725
+ }
726
+ function assertBlaxelSandboxIdentityMatchesState(sandbox, state) {
727
+ const identity = extractBlaxelSandboxIdentity(sandbox);
728
+ if (identity && identity === state.sandboxIdentity) {
729
+ return;
730
+ }
731
+ throw new UserError(`Blaxel sandbox ${state.sandboxName} cannot be safely resumed with ownership because its identity could not be verified.`);
732
+ }
733
+ function extractBlaxelSandboxIdentity(sandbox) {
734
+ const metadata = sandbox.metadata;
735
+ if (!isRecord(metadata)) {
736
+ return undefined;
737
+ }
738
+ const name = readMetadataString(metadata, 'name');
739
+ const createdAt = readMetadataString(metadata, 'createdAt');
740
+ if (!name || !createdAt) {
741
+ return undefined;
742
+ }
743
+ return JSON.stringify({
744
+ name,
745
+ createdAt,
746
+ workspace: readMetadataString(metadata, 'workspace') ?? null,
747
+ createdBy: readMetadataString(metadata, 'createdBy') ?? null,
748
+ });
749
+ }
750
+ function readMetadataString(metadata, key) {
751
+ const value = metadata[key];
752
+ return typeof value === 'string' && value.length > 0 ? value : undefined;
753
+ }
754
+ function isBlaxelSandboxAlreadyExistsError(error) {
755
+ if (isRecord(error)) {
756
+ if (isConflictValue(error.code)) {
757
+ return true;
758
+ }
759
+ const response = error.response;
760
+ if (isRecord(response) && isConflictValue(response.status)) {
761
+ return true;
762
+ }
763
+ }
764
+ return /\b(409|already exists|SANDBOX_ALREADY_EXISTS)\b/iu.test(unknownErrorMessage(error));
765
+ }
766
+ function isConflictValue(value) {
767
+ if (value === 409 || value === '409') {
768
+ return true;
769
+ }
770
+ return (typeof value === 'string' &&
771
+ value.trim().toUpperCase() === 'SANDBOX_ALREADY_EXISTS');
772
+ }
773
+ function adaptBlaxelSandboxClass(SandboxInstance) {
774
+ return {
775
+ create: async (params) => adaptBlaxelSandbox(await SandboxInstance.create(params)),
776
+ get: async (name) => adaptBlaxelSandbox(await SandboxInstance.get(name)),
777
+ };
778
+ }
779
+ function adaptBlaxelSandbox(sandbox) {
780
+ const sandboxUrl = extractBlaxelSandboxUrl(sandbox);
781
+ const previews = adaptBlaxelPreviewApi(sandbox.previews);
782
+ return {
783
+ metadata: sandbox.metadata,
784
+ url: sandboxUrl,
785
+ status: typeof sandbox.status === 'string' ? sandbox.status : undefined,
786
+ ...(sandboxUrl ? { sandbox: { metadata: { url: sandboxUrl } } } : {}),
787
+ process: {
788
+ exec: async (params) => await sandbox.process.exec(params),
789
+ },
790
+ fs: {
791
+ mkdir: async (path) => {
792
+ await sandbox.fs.mkdir(path);
793
+ },
794
+ write: async (path, content) => {
795
+ await sandbox.fs.write(path, content);
796
+ },
797
+ writeBinary: async (path, content) => {
798
+ await sandbox.fs.writeBinary(path, content);
799
+ },
800
+ read: async (path) => await sandbox.fs.read(path),
801
+ readBinary: async (path) => await sandbox.fs.readBinary(path),
802
+ rm: async (path, recursive) => {
803
+ await sandbox.fs.rm(path, recursive);
804
+ },
805
+ },
806
+ ...(previews ? { previews } : {}),
807
+ drives: adaptBlaxelDriveApi(sandbox.drives),
808
+ delete: async () => {
809
+ await sandbox.delete();
810
+ },
811
+ };
812
+ }
813
+ function adaptBlaxelPreviewApi(previews) {
814
+ if (!isRecord(previews)) {
815
+ return undefined;
816
+ }
817
+ const createIfNotExists = previews.createIfNotExists;
818
+ if (typeof createIfNotExists !== 'function') {
819
+ return undefined;
820
+ }
821
+ return {
822
+ createIfNotExists: async (params) => await createIfNotExists.call(previews, params),
823
+ };
824
+ }
825
+ function adaptBlaxelDriveApi(drives) {
826
+ if (!isRecord(drives)) {
827
+ return undefined;
828
+ }
829
+ const mount = drives.mount;
830
+ if (typeof mount !== 'function') {
831
+ return undefined;
832
+ }
833
+ const unmount = drives.unmount;
834
+ if (!('list' in drives)) {
835
+ return {
836
+ mount: async (driveName, mountPath, drivePath, readOnly) => await mount.call(drives, driveName, mountPath, drivePath, readOnly),
837
+ unmount: async (mountPath) => typeof unmount === 'function'
838
+ ? await unmount.call(drives, mountPath)
839
+ : undefined,
840
+ };
841
+ }
842
+ const sdkDrives = drives;
843
+ return {
844
+ mount: async (driveName, mountPath, drivePath, readOnly) => await mount.call(sdkDrives, {
845
+ driveName,
846
+ mountPath,
847
+ ...(drivePath ? { drivePath } : {}),
848
+ readOnly,
849
+ }),
850
+ unmount: async (mountPath) => typeof unmount === 'function'
851
+ ? await unmount.call(sdkDrives, mountPath)
852
+ : undefined,
853
+ };
854
+ }
855
+ function unknownErrorMessage(error) {
856
+ return error instanceof Error ? error.message : String(error);
857
+ }
858
+ function buildShellCommand(command, environment) {
859
+ const exports = Object.entries(environment).map(([key, value]) => {
860
+ assertShellEnvironmentName(key);
861
+ return `export ${key}=${shellQuote(value)}`;
862
+ });
863
+ if (exports.length === 0) {
864
+ return command;
865
+ }
866
+ return [...exports, command].join(' && ');
867
+ }
868
+ function remoteWriteTempPath(path) {
869
+ const dir = posixDirname(path);
870
+ const prefix = dir === '/' ? '' : dir;
871
+ return `${prefix}/.openai-agents-write-${randomUUID()}`;
872
+ }
873
+ function resolveBlaxelTimeouts(options) {
874
+ return {
875
+ ...options.timeouts,
876
+ execTimeoutMs: options.timeouts?.execTimeoutMs ?? options.timeoutMs,
877
+ createTimeoutMs: options.timeouts?.createTimeoutMs ?? options.createTimeoutMs,
878
+ workspaceTarTimeoutMs: options.timeouts?.workspaceTarTimeoutMs ?? options.timeoutMs,
879
+ fastOperationTimeoutMs: options.timeouts?.fastOperationTimeoutMs ?? options.timeoutMs,
880
+ };
881
+ }
882
+ async function withBlaxelOperationTimeout(operation, timeoutMs, operationName, options = {}) {
883
+ if (typeof timeoutMs !== 'number') {
884
+ return await operation;
885
+ }
886
+ let timeoutId;
887
+ const timeout = new Promise((_, reject) => {
888
+ timeoutId = setTimeout(() => {
889
+ options.onTimeout?.();
890
+ reject(new SandboxProviderError(`BlaxelSandboxClient ${operationName} timed out after ${timeoutMs}ms.`, {
891
+ provider: 'blaxel',
892
+ operation: operationName,
893
+ timeoutMs,
894
+ }));
895
+ }, timeoutMs);
896
+ });
897
+ try {
898
+ return await Promise.race([operation, timeout]);
899
+ }
900
+ finally {
901
+ if (timeoutId) {
902
+ clearTimeout(timeoutId);
903
+ }
904
+ }
905
+ }
906
+ function extractBlaxelSandboxUrl(sandbox) {
907
+ if (!isRecord(sandbox)) {
908
+ return undefined;
909
+ }
910
+ const directUrl = sandbox.url;
911
+ if (typeof directUrl === 'string') {
912
+ return directUrl;
913
+ }
914
+ const nestedSandbox = sandbox.sandbox;
915
+ if (!isRecord(nestedSandbox)) {
916
+ return undefined;
917
+ }
918
+ const metadata = nestedSandbox.metadata;
919
+ if (!isRecord(metadata) || typeof metadata.url !== 'string') {
920
+ return undefined;
921
+ }
922
+ return metadata.url;
923
+ }
924
+ function buildBlaxelPtyWebSocketUrl(args) {
925
+ const base = args.sandboxUrl.replace(/\/$/u, '');
926
+ const wsBase = base.replace(/^https:/u, 'wss:').replace(/^http:/u, 'ws:');
927
+ const params = new URLSearchParams({
928
+ token: args.token,
929
+ cols: String(args.cols ?? 80),
930
+ rows: String(args.rows ?? 24),
931
+ sessionId: args.sessionId,
932
+ workingDir: args.cwd,
933
+ });
934
+ return `${wsBase}/terminal/ws?${params.toString()}`;
935
+ }
936
+ function handleBlaxelPtyMessage(entry, event) {
937
+ const text = webSocketEventText(event);
938
+ if (!text) {
939
+ return;
940
+ }
941
+ let payload;
942
+ try {
943
+ payload = JSON.parse(text);
944
+ }
945
+ catch {
946
+ return;
947
+ }
948
+ if (!isRecord(payload)) {
949
+ return;
950
+ }
951
+ const type = payload.type ?? payload.Type;
952
+ if (type === 'output') {
953
+ const data = payload.data ?? payload.Data;
954
+ if (typeof data === 'string') {
955
+ appendPtyOutput(entry, data);
956
+ }
957
+ return;
958
+ }
959
+ if (type === 'error') {
960
+ const data = payload.data ?? payload.Data;
961
+ if (typeof data === 'string') {
962
+ appendPtyOutput(entry, data);
963
+ }
964
+ markPtyDone(entry, 1);
965
+ }
966
+ }
967
+ function blaxelPtyCloseExitCode(entry, event) {
968
+ if (entry.exitCode !== null) {
969
+ return entry.exitCode;
970
+ }
971
+ return webSocketCloseCode(event) === 1000 ? 0 : 1;
972
+ }
973
+ function webSocketCloseCode(event) {
974
+ if (typeof event === 'number') {
975
+ return event;
976
+ }
977
+ if (!isRecord(event)) {
978
+ return undefined;
979
+ }
980
+ const code = event.code ?? event.data;
981
+ return typeof code === 'number' ? code : undefined;
982
+ }
983
+ function extractBlaxelPreviewUrl(preview) {
984
+ if (!isRecord(preview)) {
985
+ return undefined;
986
+ }
987
+ for (const nested of ['spec', 'status']) {
988
+ const value = preview[nested];
989
+ if (isRecord(value) && typeof value.url === 'string' && value.url) {
990
+ return value.url;
991
+ }
992
+ }
993
+ for (const key of ['url', 'endpoint']) {
994
+ const value = preview[key];
995
+ if (typeof value === 'string' && value) {
996
+ return value;
997
+ }
998
+ }
999
+ return extractBlaxelPreviewUrl(preview.preview);
1000
+ }
1001
+ async function createBlaxelPreviewTokenQuery(preview, ttlSeconds, port) {
1002
+ const tokens = findBlaxelPreviewTokens(preview);
1003
+ if (!tokens) {
1004
+ throw new SandboxProviderError('BlaxelSandboxClient private previews require a preview token API.', {
1005
+ provider: 'blaxel',
1006
+ port,
1007
+ });
1008
+ }
1009
+ const createToken = tokens.create;
1010
+ if (typeof createToken !== 'function') {
1011
+ throw new SandboxProviderError('BlaxelSandboxClient private previews require tokens.create().', {
1012
+ provider: 'blaxel',
1013
+ port,
1014
+ });
1015
+ }
1016
+ let token;
1017
+ try {
1018
+ token = await createToken.call(tokens, new Date(Date.now() + Math.max(1, ttlSeconds) * 1000));
1019
+ }
1020
+ catch (error) {
1021
+ throw new SandboxProviderError(`BlaxelSandboxClient failed to create a private preview token for port ${port}.`, {
1022
+ provider: 'blaxel',
1023
+ port,
1024
+ cause: error instanceof Error ? error.message : String(error),
1025
+ });
1026
+ }
1027
+ const tokenValue = typeof token === 'string'
1028
+ ? token
1029
+ : isRecord(token) && typeof token.value === 'string'
1030
+ ? token.value
1031
+ : isRecord(token) && typeof token.token === 'string'
1032
+ ? token.token
1033
+ : undefined;
1034
+ if (!tokenValue) {
1035
+ throw new SandboxProviderError('BlaxelSandboxClient private preview token response did not include a token value.', {
1036
+ provider: 'blaxel',
1037
+ port,
1038
+ });
1039
+ }
1040
+ return new URLSearchParams({
1041
+ bl_preview_token: tokenValue,
1042
+ }).toString();
1043
+ }
1044
+ function findBlaxelPreviewTokens(preview, seen = new Set()) {
1045
+ if (!isRecord(preview)) {
1046
+ return undefined;
1047
+ }
1048
+ if (seen.has(preview)) {
1049
+ return undefined;
1050
+ }
1051
+ seen.add(preview);
1052
+ if (isRecord(preview.tokens)) {
1053
+ return preview.tokens;
1054
+ }
1055
+ return findBlaxelPreviewTokens(preview.preview, seen);
1056
+ }
1057
+ function webSocketEventText(event) {
1058
+ const data = event && typeof event === 'object' && 'data' in event
1059
+ ? event.data
1060
+ : event;
1061
+ if (typeof data === 'string') {
1062
+ return data;
1063
+ }
1064
+ if (data instanceof ArrayBuffer) {
1065
+ return new TextDecoder().decode(data);
1066
+ }
1067
+ if (ArrayBuffer.isView(data)) {
1068
+ return new TextDecoder().decode(new Uint8Array(data.buffer, data.byteOffset, data.byteLength));
1069
+ }
1070
+ return undefined;
1071
+ }
1072
+ //# sourceMappingURL=sandbox.mjs.map