@computesdk/blaxel 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +666 -0
- package/dist/index.d.mts +60 -0
- package/dist/index.d.ts +60 -0
- package/dist/index.js +403 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +377 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +60 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 computesdk
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,666 @@
|
|
|
1
|
+
# @computesdk/blaxel
|
|
2
|
+
|
|
3
|
+
Blaxel provider for ComputeSDK - Execute code in secure Blaxel cloud sandboxes.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @computesdk/blaxel
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
### With ComputeSDK
|
|
14
|
+
|
|
15
|
+
```typescript
|
|
16
|
+
import { compute } from 'computesdk';
|
|
17
|
+
import { blaxel } from '@computesdk/blaxel';
|
|
18
|
+
|
|
19
|
+
// Set as default provider
|
|
20
|
+
compute.setConfig({
|
|
21
|
+
provider: blaxel({ apiKey: process.env.BL_API_KEY })
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
// Create sandbox
|
|
25
|
+
const sandbox = await compute.sandbox.create();
|
|
26
|
+
|
|
27
|
+
// Execute code
|
|
28
|
+
const result = await sandbox.runCode('console.log("Hello from Blaxel!")');
|
|
29
|
+
console.log(result.stdout); // "Hello from Blaxel!"
|
|
30
|
+
|
|
31
|
+
// Clean up
|
|
32
|
+
await sandbox.destroy();
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### Direct Usage
|
|
36
|
+
|
|
37
|
+
```typescript
|
|
38
|
+
import { blaxel } from '@computesdk/blaxel';
|
|
39
|
+
|
|
40
|
+
// Create provider with configuration
|
|
41
|
+
const provider = blaxel({
|
|
42
|
+
workspace: 'your-workspace',
|
|
43
|
+
apiKey: 'your-api-key',
|
|
44
|
+
image: 'blaxel/prod-py-app:latest', // Python image
|
|
45
|
+
memory: 8192, // 8GB RAM
|
|
46
|
+
ports: [3000, 8080] // Exposed ports
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
// Use with compute singleton
|
|
50
|
+
const sandbox = await compute.sandbox.create({
|
|
51
|
+
provider,
|
|
52
|
+
options: {
|
|
53
|
+
runtime: 'python', // Runtime specified at creation time
|
|
54
|
+
timeout: 3600000, // 1 hour timeout
|
|
55
|
+
envs: {
|
|
56
|
+
DEBUG: 'true'
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Configuration
|
|
63
|
+
|
|
64
|
+
### Environment Variables
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
export BL_WORKSPACE=your_workspace_id
|
|
68
|
+
export BL_API_KEY=your_api_key_here
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### Configuration Options
|
|
72
|
+
|
|
73
|
+
```typescript
|
|
74
|
+
interface BlaxelConfig {
|
|
75
|
+
/** Blaxel workspace ID - fallback to BL_WORKSPACE env var */
|
|
76
|
+
workspace?: string;
|
|
77
|
+
/** Blaxel API key - fallback to BL_API_KEY env var */
|
|
78
|
+
apiKey?: string;
|
|
79
|
+
/** Default image for sandboxes */
|
|
80
|
+
image?: string;
|
|
81
|
+
/** Default region for sandbox deployment */
|
|
82
|
+
region?: string;
|
|
83
|
+
/** Default memory allocation in MB (default: 4096) */
|
|
84
|
+
memory?: number;
|
|
85
|
+
/** Default ports for sandbox (default: [3000]) */
|
|
86
|
+
ports?: number[];
|
|
87
|
+
}
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
> 💡 **Note:** For persistent storage across sandbox sessions, see [Mounting & using sandbox volumes](https://docs.blaxel.ai/Sandboxes/Volumes)
|
|
91
|
+
|
|
92
|
+
### Default Images
|
|
93
|
+
|
|
94
|
+
The provider automatically selects images based on runtime:
|
|
95
|
+
- **Python:** `blaxel/prod-py-app:latest`
|
|
96
|
+
- **Node.js:** `blaxel/prod-ts-app:latest`
|
|
97
|
+
- **Default:** `blaxel/prod-base:latest`
|
|
98
|
+
|
|
99
|
+
## Features
|
|
100
|
+
|
|
101
|
+
- ✅ **Code Execution** - Python and Node.js runtime support with proper stdout/stderr streaming
|
|
102
|
+
- ✅ **Command Execution** - Run shell commands with background support
|
|
103
|
+
- ✅ **Filesystem Operations** - Full file system access (read, write, mkdir, ls, rm)
|
|
104
|
+
- ✅ **Auto Runtime Detection** - Automatically detects Python vs Node.js from code patterns
|
|
105
|
+
- ✅ **Custom Images** - Support for custom Docker images
|
|
106
|
+
- ✅ **Memory Configuration** - Configurable memory allocation
|
|
107
|
+
- ✅ **Preview URLs** - Public/private preview URLs with TTL, custom domains, and headers
|
|
108
|
+
- ✅ **Environment Variables** - Pass custom environment variables to sandboxes
|
|
109
|
+
- ✅ **Metadata Labels** - Attach custom metadata to sandboxes
|
|
110
|
+
- ✅ **Multi-Port Support** - Configure multiple ports for sandbox access
|
|
111
|
+
- ✅ **Status Detection** - Automatic conversion of Blaxel status to standard format
|
|
112
|
+
|
|
113
|
+
> 📚 For more details, see the [Sandbox technical guide](https://docs.blaxel.ai/Sandboxes/Overview)
|
|
114
|
+
|
|
115
|
+
## API Reference
|
|
116
|
+
|
|
117
|
+
### Code Execution
|
|
118
|
+
|
|
119
|
+
```typescript
|
|
120
|
+
// Execute Python code
|
|
121
|
+
const result = await sandbox.runCode(`
|
|
122
|
+
import json
|
|
123
|
+
data = {"message": "Hello from Python"}
|
|
124
|
+
print(json.dumps(data))
|
|
125
|
+
`, 'python');
|
|
126
|
+
|
|
127
|
+
// Execute Node.js code
|
|
128
|
+
const result = await sandbox.runCode(`
|
|
129
|
+
const data = { message: "Hello from Node.js" };
|
|
130
|
+
console.log(JSON.stringify(data));
|
|
131
|
+
`, 'node');
|
|
132
|
+
|
|
133
|
+
// Auto-detection (based on code patterns)
|
|
134
|
+
const result = await sandbox.runCode('print("Auto-detected as Python")');
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
### Command Execution
|
|
138
|
+
|
|
139
|
+
```typescript
|
|
140
|
+
// List files
|
|
141
|
+
const result = await sandbox.runCommand('ls', ['-la']);
|
|
142
|
+
|
|
143
|
+
// Install packages
|
|
144
|
+
const result = await sandbox.runCommand('pip', ['install', 'requests']);
|
|
145
|
+
|
|
146
|
+
// Run background process
|
|
147
|
+
const bgResult = await sandbox.runCommand('npm', ['start'], { background: true });
|
|
148
|
+
console.log('Process started in background');
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
### Filesystem Operations
|
|
152
|
+
|
|
153
|
+
> 📚 For detailed filesystem API documentation, see the [Sandbox API reference](https://docs.blaxel.ai/api-reference/filesystem/get-file-or-directory-information)
|
|
154
|
+
|
|
155
|
+
```typescript
|
|
156
|
+
// Write file
|
|
157
|
+
await sandbox.filesystem.writeFile('/tmp/hello.py', 'print("Hello World")');
|
|
158
|
+
|
|
159
|
+
// Read file
|
|
160
|
+
const content = await sandbox.filesystem.readFile('/tmp/hello.py');
|
|
161
|
+
|
|
162
|
+
// Create directory
|
|
163
|
+
await sandbox.filesystem.mkdir('/tmp/data');
|
|
164
|
+
|
|
165
|
+
// List directory contents with metadata
|
|
166
|
+
const files = await sandbox.filesystem.readdir('/tmp');
|
|
167
|
+
// Returns FileEntry[] with name, path, isDirectory, size, lastModified
|
|
168
|
+
|
|
169
|
+
// Check if file exists
|
|
170
|
+
const exists = await sandbox.filesystem.exists('/tmp/hello.py');
|
|
171
|
+
|
|
172
|
+
// Remove file or directory
|
|
173
|
+
await sandbox.filesystem.remove('/tmp/hello.py');
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
### Sandbox Management
|
|
177
|
+
|
|
178
|
+
```typescript
|
|
179
|
+
// Get sandbox info
|
|
180
|
+
const info = await sandbox.getInfo();
|
|
181
|
+
console.log(info.id, info.provider, info.status, info.runtime);
|
|
182
|
+
// Status: 'running', 'stopped', or 'error'
|
|
183
|
+
// Runtime: Automatically detected from image name
|
|
184
|
+
|
|
185
|
+
// Create with specific configuration
|
|
186
|
+
const sandbox = await provider.sandbox.create({
|
|
187
|
+
runtime: 'python', // Selects appropriate image
|
|
188
|
+
timeout: 1800000, // 30 minutes in milliseconds (converted to "1800s")
|
|
189
|
+
envs: { // Environment variables
|
|
190
|
+
API_KEY: 'secret-key',
|
|
191
|
+
NODE_ENV: 'production'
|
|
192
|
+
},
|
|
193
|
+
metadata: { // Custom metadata labels
|
|
194
|
+
labels: {
|
|
195
|
+
project: 'my-project',
|
|
196
|
+
environment: 'staging'
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
// Create with custom memory and region
|
|
202
|
+
const customSandbox = await provider.sandbox.create({
|
|
203
|
+
memory: 8192, // 8GB RAM
|
|
204
|
+
region: 'us-pdx-1', // See regions documentation below
|
|
205
|
+
ports: [3000, 8080, 9000] // Multiple ports
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
> 📚 Learn more: [Deployment regions](https://docs.blaxel.ai/Infrastructure/Regions) | [Specifying region when creating sandboxes](https://docs.blaxel.ai/Sandboxes/Overview#create-a-sandbox)
|
|
209
|
+
|
|
210
|
+
// Reconnect to existing sandbox
|
|
211
|
+
const existing = await provider.sandbox.create({
|
|
212
|
+
sandboxId: 'blaxel-1234567890'
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
// Get sandbox by ID
|
|
216
|
+
const sandbox = await provider.sandbox.getById('blaxel-1234567890');
|
|
217
|
+
|
|
218
|
+
// Destroy sandbox
|
|
219
|
+
await provider.sandbox.destroy('blaxel-1234567890');
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
### Preview URLs
|
|
223
|
+
|
|
224
|
+
> 📚 For a complete guide, see [Creating preview URLs & custom domains](https://docs.blaxel.ai/Sandboxes/Preview-url)
|
|
225
|
+
|
|
226
|
+
```typescript
|
|
227
|
+
// getUrl options interface
|
|
228
|
+
interface GetUrlOptions {
|
|
229
|
+
port: number; // Port number to expose
|
|
230
|
+
ttl?: number; // Preview TTL in milliseconds
|
|
231
|
+
prefixUrl?: string; // Custom prefix URL for the preview
|
|
232
|
+
customDomain?: string; // Custom domain for the preview
|
|
233
|
+
headers?: {
|
|
234
|
+
response?: Record<string, string>; // Response headers sent to clients
|
|
235
|
+
request?: Record<string, string>; // Request headers for internal routing
|
|
236
|
+
};
|
|
237
|
+
authentication?: {
|
|
238
|
+
public?: boolean; // Create public preview (default: true)
|
|
239
|
+
tokenExpiryMinutes?: number; // Token expiry for private previews (default: 60)
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// Create a public preview URL (default headers applied)
|
|
244
|
+
const publicUrl = await sandbox.getUrl({ port: 3000 });
|
|
245
|
+
console.log(publicUrl); // https://tkmu0oj2bf6iuoag6mmlt8.preview.bl.run
|
|
246
|
+
|
|
247
|
+
// Create a private preview URL with authentication token
|
|
248
|
+
const privateUrl = await sandbox.getUrl({
|
|
249
|
+
port: 3000,
|
|
250
|
+
authentication: {
|
|
251
|
+
public: false,
|
|
252
|
+
tokenExpiryMinutes: 30 // Token expires in 30 minutes (default: 60)
|
|
253
|
+
}
|
|
254
|
+
});
|
|
255
|
+
console.log(privateUrl);
|
|
256
|
+
// https://tkmu0oj2bf6iuoag6mmlt8.preview.bl.run?bl_preview_token=<token>
|
|
257
|
+
|
|
258
|
+
// Create preview with custom TTL (Time To Live)
|
|
259
|
+
const shortLivedUrl = await sandbox.getUrl({
|
|
260
|
+
port: 3000,
|
|
261
|
+
ttl: 300000 // Preview expires in 5 minutes (300,000 ms)
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
// Create preview with custom CORS headers (replaces all defaults)
|
|
265
|
+
const customUrl = await sandbox.getUrl({
|
|
266
|
+
port: 3000,
|
|
267
|
+
headers: {
|
|
268
|
+
response: {
|
|
269
|
+
"Access-Control-Allow-Origin": "https://mydomain.com",
|
|
270
|
+
"Access-Control-Allow-Methods": "GET, POST",
|
|
271
|
+
"Access-Control-Allow-Credentials": "true",
|
|
272
|
+
"X-Custom-Header": "custom-value"
|
|
273
|
+
},
|
|
274
|
+
request: {
|
|
275
|
+
"X-Internal-Auth": "internal-token"
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
// Create preview with custom domain
|
|
281
|
+
const customDomainUrl = await sandbox.getUrl({
|
|
282
|
+
port: 3000,
|
|
283
|
+
customDomain: "app.example.com"
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
// Create preview with custom prefix URL
|
|
287
|
+
const prefixedUrl = await sandbox.getUrl({
|
|
288
|
+
port: 3000,
|
|
289
|
+
prefixUrl: "my-preview"
|
|
290
|
+
});
|
|
291
|
+
// https://my-preview-my-workspace.preview.bl.run
|
|
292
|
+
|
|
293
|
+
// Full example with all options
|
|
294
|
+
const advancedUrl = await sandbox.getUrl({
|
|
295
|
+
port: 3000,
|
|
296
|
+
ttl: 3600000, // 1 hour TTL
|
|
297
|
+
prefixUrl: "my-preview",
|
|
298
|
+
customDomain: "preview.myapp.com",
|
|
299
|
+
headers: {
|
|
300
|
+
response: {
|
|
301
|
+
"Access-Control-Allow-Origin": "https://myapp.com",
|
|
302
|
+
"X-Frame-Options": "SAMEORIGIN"
|
|
303
|
+
},
|
|
304
|
+
request: {
|
|
305
|
+
"X-API-Version": "v2"
|
|
306
|
+
}
|
|
307
|
+
},
|
|
308
|
+
authentication: {
|
|
309
|
+
public: false,
|
|
310
|
+
tokenExpiryMinutes: 120 // 2 hour token
|
|
311
|
+
}
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
// The token is automatically appended to the URL for private previews
|
|
315
|
+
// You can also pass the token as a header: X-Blaxel-Preview-Token
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
### Direct Instance Access
|
|
319
|
+
|
|
320
|
+
> 📚 You can also [connect to sandboxes remotely from a terminal](https://docs.blaxel.ai/Sandboxes/Overview#connect-to-a-sandbox-with-a-terminal) for direct access
|
|
321
|
+
|
|
322
|
+
```typescript
|
|
323
|
+
import { createBlaxelCompute } from '@computesdk/blaxel';
|
|
324
|
+
|
|
325
|
+
const compute = createBlaxelCompute({
|
|
326
|
+
workspace: 'your-workspace',
|
|
327
|
+
apiKey: 'your-key',
|
|
328
|
+
memory: 4096,
|
|
329
|
+
ports: [3000]
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
const sandbox = await compute.sandbox.create();
|
|
333
|
+
const instance = sandbox.getInstance(); // Typed as SandboxInstance
|
|
334
|
+
|
|
335
|
+
// Use Blaxel-specific features directly
|
|
336
|
+
const result = await instance.process.exec({
|
|
337
|
+
command: 'ls -la'
|
|
338
|
+
});
|
|
339
|
+
|
|
340
|
+
// Stream logs from process
|
|
341
|
+
const stream = instance.process.streamLogs(result.name, {
|
|
342
|
+
onStdout(data) { console.log('stdout:', data); },
|
|
343
|
+
onStderr(data) { console.error('stderr:', data); }
|
|
344
|
+
});
|
|
345
|
+
|
|
346
|
+
// Wait for completion
|
|
347
|
+
await instance.process.wait(result.name);
|
|
348
|
+
stream.close();
|
|
349
|
+
|
|
350
|
+
// Access Blaxel filesystem API
|
|
351
|
+
await instance.fs.write('/tmp/test.txt', 'Hello Blaxel');
|
|
352
|
+
const content = await instance.fs.read('/tmp/test.txt');
|
|
353
|
+
|
|
354
|
+
// Create preview with Blaxel API
|
|
355
|
+
const preview = await instance.previews.create({
|
|
356
|
+
spec: {
|
|
357
|
+
port: 3000,
|
|
358
|
+
public: true
|
|
359
|
+
}
|
|
360
|
+
});
|
|
361
|
+
```
|
|
362
|
+
|
|
363
|
+
## Runtime Detection
|
|
364
|
+
|
|
365
|
+
The provider automatically detects the runtime based on code patterns:
|
|
366
|
+
|
|
367
|
+
**Python indicators:**
|
|
368
|
+
- `print(` statements
|
|
369
|
+
- `import` statements
|
|
370
|
+
- `def` function definitions
|
|
371
|
+
- Python-specific syntax (`f"`, `f'`, `__`, `sys.`, `json.`)
|
|
372
|
+
|
|
373
|
+
**Default:** Node.js for all other cases
|
|
374
|
+
|
|
375
|
+
## Error Handling
|
|
376
|
+
|
|
377
|
+
```typescript
|
|
378
|
+
try {
|
|
379
|
+
const result = await sandbox.runCode('invalid code');
|
|
380
|
+
} catch (error) {
|
|
381
|
+
if (error.message.includes('Syntax error')) {
|
|
382
|
+
console.error('Code has syntax errors');
|
|
383
|
+
} else if (error.message.includes('authentication')) {
|
|
384
|
+
console.error('Check your BL_API_KEY');
|
|
385
|
+
} else if (error.message.includes('quota')) {
|
|
386
|
+
console.error('Blaxel usage limits reached');
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
```
|
|
390
|
+
|
|
391
|
+
### Exit Codes
|
|
392
|
+
- `0` - Success
|
|
393
|
+
- `1` - General error or runtime error
|
|
394
|
+
- `127` - Command not found
|
|
395
|
+
|
|
396
|
+
## Web Framework Integration
|
|
397
|
+
|
|
398
|
+
Use with web frameworks via the request handler:
|
|
399
|
+
|
|
400
|
+
```typescript
|
|
401
|
+
import { handleComputeRequest } from 'computesdk';
|
|
402
|
+
import { blaxel } from '@computesdk/blaxel';
|
|
403
|
+
|
|
404
|
+
export async function POST(request: Request) {
|
|
405
|
+
return handleComputeRequest({
|
|
406
|
+
request,
|
|
407
|
+
provider: blaxel({ apiKey: process.env.BL_API_KEY })
|
|
408
|
+
});
|
|
409
|
+
}
|
|
410
|
+
```
|
|
411
|
+
|
|
412
|
+
## Examples
|
|
413
|
+
|
|
414
|
+
### Data Processing
|
|
415
|
+
|
|
416
|
+
```typescript
|
|
417
|
+
const result = await sandbox.runCode(`
|
|
418
|
+
import json
|
|
419
|
+
|
|
420
|
+
# Process data
|
|
421
|
+
data = [1, 2, 3, 4, 5]
|
|
422
|
+
result = {
|
|
423
|
+
"sum": sum(data),
|
|
424
|
+
"average": sum(data) / len(data),
|
|
425
|
+
"max": max(data)
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
print(json.dumps(result))
|
|
429
|
+
`);
|
|
430
|
+
|
|
431
|
+
const output = JSON.parse(result.stdout);
|
|
432
|
+
console.log(output); // { sum: 15, average: 3, max: 5 }
|
|
433
|
+
```
|
|
434
|
+
|
|
435
|
+
### File Processing
|
|
436
|
+
|
|
437
|
+
```typescript
|
|
438
|
+
// Create data file
|
|
439
|
+
await sandbox.filesystem.writeFile('/tmp/data.json',
|
|
440
|
+
JSON.stringify({ users: ['Alice', 'Bob', 'Charlie'] })
|
|
441
|
+
);
|
|
442
|
+
|
|
443
|
+
// Process file
|
|
444
|
+
const result = await sandbox.runCode(`
|
|
445
|
+
import json
|
|
446
|
+
|
|
447
|
+
with open('/tmp/data.json', 'r') as f:
|
|
448
|
+
data = json.load(f)
|
|
449
|
+
|
|
450
|
+
# Process users
|
|
451
|
+
user_count = len(data['users'])
|
|
452
|
+
print(f"Found {user_count} users")
|
|
453
|
+
|
|
454
|
+
# Save result
|
|
455
|
+
result = {"user_count": user_count, "processed": True}
|
|
456
|
+
with open('/tmp/result.json', 'w') as f:
|
|
457
|
+
json.dump(result, f)
|
|
458
|
+
`);
|
|
459
|
+
|
|
460
|
+
// Read result
|
|
461
|
+
const resultData = await sandbox.filesystem.readFile('/tmp/result.json');
|
|
462
|
+
console.log(JSON.parse(resultData));
|
|
463
|
+
```
|
|
464
|
+
|
|
465
|
+
### Web Scraping Example
|
|
466
|
+
|
|
467
|
+
```typescript
|
|
468
|
+
// Install dependencies
|
|
469
|
+
await sandbox.runCommand('pip', ['install', 'requests', 'beautifulsoup4']);
|
|
470
|
+
|
|
471
|
+
// Scrape website
|
|
472
|
+
const result = await sandbox.runCode(`
|
|
473
|
+
import requests
|
|
474
|
+
from bs4 import BeautifulSoup
|
|
475
|
+
import json
|
|
476
|
+
|
|
477
|
+
# Fetch webpage
|
|
478
|
+
response = requests.get('https://example.com')
|
|
479
|
+
soup = BeautifulSoup(response.text, 'html.parser')
|
|
480
|
+
|
|
481
|
+
# Extract title
|
|
482
|
+
title = soup.find('title').text if soup.find('title') else 'No title'
|
|
483
|
+
|
|
484
|
+
# Output result
|
|
485
|
+
result = {
|
|
486
|
+
"status_code": response.status_code,
|
|
487
|
+
"title": title,
|
|
488
|
+
"content_length": len(response.text)
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
print(json.dumps(result))
|
|
492
|
+
`);
|
|
493
|
+
|
|
494
|
+
console.log(JSON.parse(result.stdout));
|
|
495
|
+
```
|
|
496
|
+
|
|
497
|
+
### API Development
|
|
498
|
+
|
|
499
|
+
```typescript
|
|
500
|
+
// Create a simple API server
|
|
501
|
+
await sandbox.filesystem.writeFile('/tmp/server.js', `
|
|
502
|
+
const http = require('http');
|
|
503
|
+
|
|
504
|
+
const server = http.createServer((req, res) => {
|
|
505
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
506
|
+
res.end(JSON.stringify({
|
|
507
|
+
message: 'Hello from Blaxel!',
|
|
508
|
+
timestamp: new Date().toISOString()
|
|
509
|
+
}));
|
|
510
|
+
});
|
|
511
|
+
|
|
512
|
+
server.listen(3000, () => {
|
|
513
|
+
console.log('Server running on port 3000');
|
|
514
|
+
});
|
|
515
|
+
`);
|
|
516
|
+
|
|
517
|
+
// Start server in background
|
|
518
|
+
await sandbox.runCommand('node', ['/tmp/server.js'], { background: true });
|
|
519
|
+
|
|
520
|
+
// Get the preview URL (public by default)
|
|
521
|
+
const url = await sandbox.getUrl({ port: 3000 });
|
|
522
|
+
console.log(`API available at: ${url}`);
|
|
523
|
+
// Example: https://tkmu0oj2bf6iuoag6mmlt8.preview.bl.run
|
|
524
|
+
|
|
525
|
+
// Or create a private API endpoint with authentication
|
|
526
|
+
const privateUrl = await sandbox.getUrl({
|
|
527
|
+
port: 3000,
|
|
528
|
+
authentication: {
|
|
529
|
+
public: false,
|
|
530
|
+
tokenExpiryMinutes: 120 // 2 hour token
|
|
531
|
+
}
|
|
532
|
+
});
|
|
533
|
+
console.log(`Private API: ${privateUrl}`);
|
|
534
|
+
```
|
|
535
|
+
|
|
536
|
+
## Authentication Methods
|
|
537
|
+
|
|
538
|
+
1. **Automatic (when running on Blaxel)** - No configuration needed
|
|
539
|
+
2. **Via Configuration** - Pass credentials directly
|
|
540
|
+
3. **Via Environment Variables** - Set BL_WORKSPACE and BL_API_KEY
|
|
541
|
+
4. **Via Blaxel CLI** - Run `bl login` for local development
|
|
542
|
+
|
|
543
|
+
## Sandbox URLs
|
|
544
|
+
|
|
545
|
+
Blaxel sandboxes are accessible via dynamically generated preview URLs:
|
|
546
|
+
```
|
|
547
|
+
https://{unique-id}.preview.bl.run or https://{unique-id}.{region}.preview.bl.run
|
|
548
|
+
```
|
|
549
|
+
|
|
550
|
+
Or with custom domain:
|
|
551
|
+
```
|
|
552
|
+
https://{unique-id}.{custom-domain}
|
|
553
|
+
```
|
|
554
|
+
|
|
555
|
+
> 📚 Learn more about [preview URLs and custom domains](https://docs.blaxel.ai/Sandboxes/Preview-url)
|
|
556
|
+
|
|
557
|
+
### Public vs Private Previews
|
|
558
|
+
|
|
559
|
+
**Public Previews** (default):
|
|
560
|
+
- No authentication required
|
|
561
|
+
- Accessible to anyone with the URL
|
|
562
|
+
- Suitable for public demos and testing
|
|
563
|
+
|
|
564
|
+
**Private Previews**:
|
|
565
|
+
- Require authentication token
|
|
566
|
+
- Token can be passed as `bl_preview_token` query parameter
|
|
567
|
+
- Token can also be passed as `X-Blaxel-Preview-Token` header
|
|
568
|
+
- Configurable token expiry time
|
|
569
|
+
|
|
570
|
+
### Preview Configuration Options
|
|
571
|
+
|
|
572
|
+
**TTL (Time To Live)**:
|
|
573
|
+
- Controls how long the preview URL remains active
|
|
574
|
+
- Specified in milliseconds
|
|
575
|
+
- Automatically converted to Blaxel's format (e.g., "300s" for 5 minutes)
|
|
576
|
+
|
|
577
|
+
**Custom Domain**:
|
|
578
|
+
- Use your own domain for preview URLs
|
|
579
|
+
- Must be configured in Blaxel settings
|
|
580
|
+
|
|
581
|
+
**Prefix URL**:
|
|
582
|
+
- Add a path prefix to all preview URLs
|
|
583
|
+
- Useful for API versioning or routing
|
|
584
|
+
|
|
585
|
+
**Headers**:
|
|
586
|
+
- `response`: Headers sent to clients accessing the preview
|
|
587
|
+
- `request`: Headers used for internal routing within Blaxel
|
|
588
|
+
|
|
589
|
+
**Default Response Headers**:
|
|
590
|
+
|
|
591
|
+
The provider applies these CORS headers by default when no custom response headers are provided:
|
|
592
|
+
```javascript
|
|
593
|
+
{
|
|
594
|
+
"Access-Control-Allow-Origin": "*",
|
|
595
|
+
"Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, OPTIONS, PATCH",
|
|
596
|
+
"Access-Control-Allow-Headers": "Content-Type, Authorization, X-Requested-With, X-Blaxel-Preview-Token, X-Blaxel-Authorization",
|
|
597
|
+
"Access-Control-Allow-Credentials": "true",
|
|
598
|
+
"Access-Control-Expose-Headers": "Content-Length, X-Request-Id",
|
|
599
|
+
"Access-Control-Max-Age": "86400",
|
|
600
|
+
"Vary": "Origin"
|
|
601
|
+
}
|
|
602
|
+
```
|
|
603
|
+
|
|
604
|
+
**Note:** If you provide custom response headers, they completely replace the defaults. Make sure to include all necessary CORS headers when providing custom headers.
|
|
605
|
+
|
|
606
|
+
Example:
|
|
607
|
+
```typescript
|
|
608
|
+
// Public preview with default headers and settings
|
|
609
|
+
const url = await sandbox.getUrl({ port: 3000 });
|
|
610
|
+
|
|
611
|
+
// Private preview with 2-hour token and 24-hour TTL
|
|
612
|
+
const secureUrl = await sandbox.getUrl({
|
|
613
|
+
port: 3000,
|
|
614
|
+
ttl: 86400000, // 24 hours in milliseconds
|
|
615
|
+
authentication: {
|
|
616
|
+
public: false,
|
|
617
|
+
tokenExpiryMinutes: 120
|
|
618
|
+
}
|
|
619
|
+
});
|
|
620
|
+
|
|
621
|
+
// Private preview with complete custom headers (replaces all defaults)
|
|
622
|
+
const customSecureUrl = await sandbox.getUrl({
|
|
623
|
+
port: 3000,
|
|
624
|
+
authentication: {
|
|
625
|
+
public: false,
|
|
626
|
+
tokenExpiryMinutes: 60
|
|
627
|
+
},
|
|
628
|
+
headers: {
|
|
629
|
+
response: {
|
|
630
|
+
// Must include all necessary CORS headers when providing custom headers, if you want ot
|
|
631
|
+
"Access-Control-Allow-Origin": "https://app.example.com",
|
|
632
|
+
"Access-Control-Allow-Methods": "GET, POST, PUT, DELETE",
|
|
633
|
+
"Access-Control-Allow-Headers": "Content-Type, Authorization, X-Blaxel-Preview-Token",
|
|
634
|
+
"Access-Control-Allow-Credentials": "true",
|
|
635
|
+
"X-Frame-Options": "SAMEORIGIN"
|
|
636
|
+
},
|
|
637
|
+
request: {
|
|
638
|
+
"X-Service-Name": "preview-service",
|
|
639
|
+
"X-Request-ID": "unique-request-id"
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
});
|
|
643
|
+
```
|
|
644
|
+
|
|
645
|
+
The provider automatically creates appropriate preview URLs with CORS headers configured for broad compatibility.
|
|
646
|
+
|
|
647
|
+
## Further Reading
|
|
648
|
+
|
|
649
|
+
### Core Documentation
|
|
650
|
+
- [Sandbox technical guide](https://docs.blaxel.ai/Sandboxes/Overview) - Comprehensive overview of Blaxel sandboxes
|
|
651
|
+
- [Sandbox API reference](https://docs.blaxel.ai/api-reference/filesystem/get-file-or-directory-information) - Complete API documentation for filesystem operations
|
|
652
|
+
- [ComputeSDK documentation](https://github.com/computesdk/computesdk) - Main SDK documentation
|
|
653
|
+
|
|
654
|
+
### Guides
|
|
655
|
+
- [Creating preview URLs & custom domains](https://docs.blaxel.ai/Sandboxes/Preview-url) - Detailed guide on preview configuration
|
|
656
|
+
- [Connecting to sandboxes remotely](https://docs.blaxel.ai/Sandboxes/Overview#connect-to-a-sandbox-with-a-terminal) - Terminal access to running sandboxes
|
|
657
|
+
- [Deployment regions](https://docs.blaxel.ai/Infrastructure/Regions) - Available regions and selection guide
|
|
658
|
+
- [Specifying regions for sandboxes](https://docs.blaxel.ai/Sandboxes/Overview#create-a-sandbox) - Region configuration during sandbox creation
|
|
659
|
+
- [Mounting & using sandbox volumes](https://docs.blaxel.ai/Sandboxes/Volumes) - Persistent storage with volumes
|
|
660
|
+
|
|
661
|
+
### Support
|
|
662
|
+
Feel free to reach out if you have any questions — we're here to help!
|
|
663
|
+
|
|
664
|
+
## License
|
|
665
|
+
|
|
666
|
+
MIT
|