Skip to content

Commit d838093

Browse files
committed
Add how-to guides
1 parent 10c3f8d commit d838093

File tree

8 files changed

+3907
-0
lines changed

8 files changed

+3907
-0
lines changed

src/content/docs/sandbox/guides/background-processes.mdx

Lines changed: 582 additions & 0 deletions
Large diffs are not rendered by default.

src/content/docs/sandbox/guides/code-execution.mdx

Lines changed: 623 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 363 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,363 @@
1+
---
2+
title: Execute commands
3+
pcx_content_type: how-to
4+
sidebar:
5+
order: 1
6+
---
7+
8+
import { TypeScriptExample } from "~/components";
9+
10+
This guide shows you how to execute commands in the sandbox, handle output, and manage errors effectively.
11+
12+
## Prerequisites
13+
14+
- Existing sandbox instance (see [Get Started](/sandbox/get-started/))
15+
- Familiar with async/await patterns
16+
17+
## Choose the right method
18+
19+
The SDK provides two methods for command execution:
20+
21+
- **`exec()`** - Run a command and wait for complete result. Best for most use cases.
22+
- **`execStream()`** - Stream output in real-time. Best for long-running commands where you need immediate feedback.
23+
24+
## Execute basic commands
25+
26+
Use `exec()` for simple commands that complete quickly:
27+
28+
<TypeScriptExample>
29+
```
30+
import { getSandbox } from '@cloudflare/sandbox';
31+
32+
const sandbox = getSandbox(env.Sandbox, 'my-sandbox');
33+
34+
// Execute a single command
35+
const result = await sandbox.exec('python --version');
36+
37+
console.log(result.stdout); // "Python 3.11.0"
38+
console.log(result.exitCode); // 0
39+
console.log(result.success); // true
40+
```
41+
</TypeScriptExample>
42+
43+
## Pass arguments safely
44+
45+
When passing user input or dynamic values, avoid string interpolation to prevent injection attacks:
46+
47+
<TypeScriptExample>
48+
```
49+
// ❌ Unsafe - vulnerable to injection
50+
const filename = userInput;
51+
await sandbox.exec(`cat ${filename}`);
52+
53+
// ✅ Safe - use proper escaping or validation
54+
const safeFilename = filename.replace(/[^a-zA-Z0-9_.-]/g, '');
55+
await sandbox.exec(`cat ${safeFilename}`);
56+
57+
// ✅ Better - write to file and execute
58+
await sandbox.writeFile('/tmp/input.txt', userInput);
59+
await sandbox.exec('python process.py /tmp/input.txt');
60+
```
61+
</TypeScriptExample>
62+
63+
## Handle errors
64+
65+
Commands can fail in two ways:
66+
67+
1. **Non-zero exit code** - Command ran but failed (result.success === false)
68+
2. **Execution error** - Command couldn't start (throws exception)
69+
70+
<TypeScriptExample>
71+
```
72+
try {
73+
const result = await sandbox.exec('python analyze.py');
74+
75+
if (!result.success) {
76+
// Command failed (non-zero exit code)
77+
console.error('Analysis failed:', result.stderr);
78+
console.log('Exit code:', result.exitCode);
79+
80+
// Handle specific exit codes
81+
if (result.exitCode === 1) {
82+
throw new Error('Invalid input data');
83+
} else if (result.exitCode === 2) {
84+
throw new Error('Missing dependencies');
85+
}
86+
}
87+
88+
// Success - process output
89+
return JSON.parse(result.stdout);
90+
91+
} catch (error) {
92+
// Execution error (couldn't start command)
93+
console.error('Execution failed:', error.message);
94+
throw error;
95+
}
96+
```
97+
</TypeScriptExample>
98+
99+
## Stream output in real-time
100+
101+
For long-running commands like builds or installations, use streaming to provide immediate feedback:
102+
103+
<TypeScriptExample>
104+
```
105+
// Option 1: Using exec() with callbacks (simpler)
106+
const result = await sandbox.exec('npm install && npm run build', {
107+
stream: true,
108+
onOutput: (stream, data) => {
109+
if (stream === 'stdout') {
110+
console.log(data);
111+
} else {
112+
console.error(data);
113+
}
114+
}
115+
});
116+
117+
console.log('Build complete, exit code:', result.exitCode);
118+
```
119+
</TypeScriptExample>
120+
121+
<TypeScriptExample>
122+
```
123+
// Option 2: Using execStream() (more control)
124+
import { parseSSEStream, type ExecEvent } from '@cloudflare/sandbox';
125+
126+
const stream = await sandbox.execStream('npm test');
127+
128+
for await (const event of parseSSEStream<ExecEvent>(stream)) {
129+
switch (event.type) {
130+
case 'start':
131+
console.log('Tests started');
132+
break;
133+
134+
case 'stdout':
135+
// Parse and process output in real-time
136+
if (event.data.includes('PASS')) {
137+
console.log('✓ Test passed');
138+
}
139+
break;
140+
141+
case 'stderr':
142+
console.error('Error:', event.data);
143+
break;
144+
145+
case 'complete':
146+
console.log('Tests finished, exit code:', event.exitCode);
147+
break;
148+
149+
case 'error':
150+
console.error('Execution failed:', event.error);
151+
break;
152+
}
153+
}
154+
```
155+
</TypeScriptExample>
156+
157+
## Execute shell commands
158+
159+
The sandbox supports shell features like pipes, redirects, and variable expansion:
160+
161+
<TypeScriptExample>
162+
```
163+
// Pipes and filters
164+
const result = await sandbox.exec('ls -la | grep ".py" | wc -l');
165+
console.log('Python files:', result.stdout.trim());
166+
167+
// Output redirection
168+
await sandbox.exec('python generate.py > output.txt 2> errors.txt');
169+
170+
// Multiple commands
171+
await sandbox.exec('cd /workspace && npm install && npm test');
172+
173+
// Environment variables
174+
await sandbox.exec('export API_KEY=secret && python app.py');
175+
```
176+
</TypeScriptExample>
177+
178+
## Run commands with timeouts
179+
180+
Prevent commands from running indefinitely:
181+
182+
<TypeScriptExample>
183+
```
184+
try {
185+
const result = await sandbox.exec('python slow-script.py', {
186+
timeout: 30000 // 30 second timeout
187+
});
188+
189+
console.log('Completed in time:', result.stdout);
190+
191+
} catch (error) {
192+
if (error.message.includes('timeout')) {
193+
console.error('Command timed out after 30 seconds');
194+
// Handle timeout - maybe kill related processes
195+
await sandbox.killAllProcesses();
196+
} else {
197+
throw error;
198+
}
199+
}
200+
```
201+
</TypeScriptExample>
202+
203+
## Execute Python scripts
204+
205+
Common patterns for running Python code:
206+
207+
<TypeScriptExample>
208+
```
209+
// Run inline Python
210+
const result = await sandbox.exec('python -c "print(sum([1, 2, 3, 4, 5]))"');
211+
console.log('Sum:', result.stdout.trim()); // "15"
212+
213+
// Run a script file
214+
await sandbox.writeFile('/workspace/analyze.py', `
215+
import sys
216+
import json
217+
218+
data = json.loads(sys.argv[1])
219+
result = sum(data)
220+
print(json.dumps({"sum": result}))
221+
`);
222+
223+
const output = await sandbox.exec(
224+
`python /workspace/analyze.py '${JSON.stringify([1, 2, 3])}'`
225+
);
226+
227+
const parsed = JSON.parse(output.stdout);
228+
console.log('Result:', parsed.sum); // 6
229+
```
230+
</TypeScriptExample>
231+
232+
## Execute Node.js scripts
233+
234+
<TypeScriptExample>
235+
```
236+
// Install dependencies first
237+
await sandbox.exec('npm install lodash');
238+
239+
// Run Node.js script
240+
await sandbox.writeFile('/workspace/process.js', `
241+
const _ = require('lodash');
242+
const data = [1, 2, 3, 4, 5];
243+
console.log(JSON.stringify({
244+
sum: _.sum(data),
245+
mean: _.mean(data)
246+
}));
247+
`);
248+
249+
const result = await sandbox.exec('node /workspace/process.js');
250+
const stats = JSON.parse(result.stdout);
251+
252+
console.log('Sum:', stats.sum); // 15
253+
console.log('Mean:', stats.mean); // 3
254+
```
255+
</TypeScriptExample>
256+
257+
## Best practices
258+
259+
### Command design
260+
261+
- **Keep commands simple** - Break complex operations into multiple commands
262+
- **Make commands idempotent** - Commands should be safe to retry
263+
- **Validate inputs** - Never trust user input in commands
264+
- **Use absolute paths** - Avoid ambiguity with working directories
265+
266+
### Error handling
267+
268+
- **Always check exit codes** - Non-zero means failure even if no exception
269+
- **Capture stderr** - Error messages help debug failures
270+
- **Provide context** - Log the command that failed for debugging
271+
- **Handle timeouts** - Don't let commands hang indefinitely
272+
273+
### Performance
274+
275+
- **Use streaming for long operations** - Provide immediate feedback to users
276+
- **Batch related commands** - Use `&&` to chain dependent operations
277+
- **Cache installations** - Don't reinstall packages every time
278+
- **Set appropriate timeouts** - Balance between too short and too long
279+
280+
### Security
281+
282+
- **Escape user input** - Prevent command injection attacks
283+
- **Limit command scope** - Use working directories to restrict access
284+
- **Validate command output** - Don't trust command results blindly
285+
- **Use environment variables** - Safer than inline secrets
286+
287+
## Common issues
288+
289+
### Command not found
290+
291+
**Problem**: Command fails with "command not found" error.
292+
293+
**Solution**: Verify the command exists or install it first:
294+
295+
<TypeScriptExample>
296+
```
297+
// Check if command exists
298+
const check = await sandbox.exec('which python3');
299+
if (!check.success) {
300+
// Install if needed
301+
await sandbox.exec('apt-get update && apt-get install -y python3');
302+
}
303+
304+
// Now safe to use
305+
await sandbox.exec('python3 script.py');
306+
```
307+
</TypeScriptExample>
308+
309+
### Working directory issues
310+
311+
**Problem**: Command can't find files because it's running in wrong directory.
312+
313+
**Solution**: Use absolute paths or change directory in command:
314+
315+
<TypeScriptExample>
316+
```
317+
// Option 1: Use absolute paths
318+
await sandbox.exec('python /workspace/my-app/script.py');
319+
320+
// Option 2: Change directory in command
321+
await sandbox.exec('cd /workspace/my-app && python script.py');
322+
323+
// Option 3: Use startProcess() with cwd option (for background processes)
324+
await sandbox.startProcess('python script.py', {
325+
cwd: '/workspace/my-app'
326+
});
327+
```
328+
</TypeScriptExample>
329+
330+
### Output too large
331+
332+
**Problem**: Command produces huge output that causes performance issues.
333+
334+
**Solution**: Stream output and process incrementally, or redirect to file:
335+
336+
<TypeScriptExample>
337+
```
338+
// Option 1: Redirect to file
339+
await sandbox.exec('python generate-large-data.py > /tmp/output.txt');
340+
const result = await sandbox.readFile('/tmp/output.txt');
341+
342+
// Option 2: Stream and process incrementally
343+
const stream = await sandbox.execStream('python generate-data.py');
344+
let processedCount = 0;
345+
346+
for await (const event of parseSSEStream<ExecEvent>(stream)) {
347+
if (event.type === 'stdout') {
348+
// Process each line immediately
349+
processLine(event.data);
350+
processedCount++;
351+
}
352+
}
353+
354+
console.log(`Processed ${processedCount} lines`);
355+
```
356+
</TypeScriptExample>
357+
358+
## Related resources
359+
360+
- [Commands API reference](/sandbox/api/commands/) - Complete method documentation
361+
- [Background processes guide](/sandbox/guides/background-processes/) - Managing long-running processes
362+
- [Streaming output guide](/sandbox/guides/streaming-output/) - Advanced streaming patterns
363+
- [Code Interpreter guide](/sandbox/guides/code-execution/) - Higher-level code execution

0 commit comments

Comments
 (0)