Skip to content

Commit 215ab49

Browse files
Update example to use env & cwd (#44)
* Update example to use env & cwd * Create changeset for example update
1 parent bb72193 commit 215ab49

File tree

4 files changed

+148
-13
lines changed

4 files changed

+148
-13
lines changed

.changeset/strong-walls-drop.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@cloudflare/sandbox": patch
3+
---
4+
5+
Update example to use env & cwd

examples/basic/app/index.tsx

Lines changed: 93 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2278,9 +2278,14 @@ function SandboxTester() {
22782278
>("disconnected");
22792279
const [sessionId, setSessionId] = useState<string | null>(null);
22802280
const [commandInput, setCommandInput] = useState("");
2281+
const [commandOptions, setCommandOptions] = useState({
2282+
cwd: "",
2283+
env: "",
2284+
});
22812285
const [results, setResults] = useState<CommandResult[]>([]);
22822286
const [isExecuting, setIsExecuting] = useState(false);
22832287
const resultsEndRef = useRef<HTMLDivElement>(null);
2288+
const commandInputRef = useRef<HTMLInputElement>(null);
22842289

22852290
// Auto-scroll to bottom when new results are added
22862291
useEffect(() => {
@@ -2416,11 +2421,27 @@ function SandboxTester() {
24162421
};
24172422
setResults((prev) => [...prev, newResult]);
24182423

2419-
// Execute the command
2420-
console.log("Executing command:", trimmedCommand);
2421-
const result = await client.execute(trimmedCommand, [], {
2424+
// Parse command options
2425+
const options: { sessionId?: string; cwd?: string; env?: Record<string, string> } = {
24222426
sessionId: sessionId || undefined,
2423-
});
2427+
};
2428+
2429+
if (commandOptions.cwd.trim()) {
2430+
options.cwd = commandOptions.cwd.trim();
2431+
}
2432+
2433+
if (commandOptions.env.trim()) {
2434+
const env: Record<string, string> = {};
2435+
commandOptions.env.split(",").forEach((pair) => {
2436+
const [key, value] = pair.split("=");
2437+
if (key && value) env[key.trim()] = value.trim();
2438+
});
2439+
options.env = env;
2440+
}
2441+
2442+
// Execute the command
2443+
console.log("Executing command:", trimmedCommand, "with options:", options);
2444+
const result = await client.execute(trimmedCommand, [], options);
24242445
console.log("Result:", result);
24252446

24262447
// Update the result with the response
@@ -2437,6 +2458,11 @@ function SandboxTester() {
24372458
});
24382459

24392460
setCommandInput("");
2461+
2462+
// Refocus the input for better UX
2463+
setTimeout(() => {
2464+
commandInputRef.current?.focus();
2465+
}, 0);
24402466
} catch (error: any) {
24412467
console.error("Failed to execute command:", error);
24422468
setResults((prev) => {
@@ -2448,6 +2474,11 @@ function SandboxTester() {
24482474
}
24492475
return updated;
24502476
});
2477+
2478+
// Refocus the input even on error
2479+
setTimeout(() => {
2480+
commandInputRef.current?.focus();
2481+
}, 100);
24512482
} finally {
24522483
setIsExecuting(false);
24532484
}
@@ -2479,18 +2510,32 @@ function SandboxTester() {
24792510
};
24802511
setResults((prev) => [...prev, newResult]);
24812512

2482-
// Execute the command with streaming
2483-
console.log("Executing streaming command:", trimmedCommand);
2484-
await client.executeStream(trimmedCommand, [], {
2513+
// Parse command options (same as regular execute)
2514+
const options: { sessionId?: string; cwd?: string; env?: Record<string, string> } = {
24852515
sessionId: sessionId || undefined,
2486-
});
2516+
};
2517+
2518+
if (commandOptions.cwd.trim()) {
2519+
options.cwd = commandOptions.cwd.trim();
2520+
}
2521+
2522+
if (commandOptions.env.trim()) {
2523+
const env: Record<string, string> = {};
2524+
commandOptions.env.split(",").forEach((pair) => {
2525+
const [key, value] = pair.split("=");
2526+
if (key && value) env[key.trim()] = value.trim();
2527+
});
2528+
options.env = env;
2529+
}
2530+
2531+
// Execute the command with streaming
2532+
console.log("Executing streaming command:", trimmedCommand, "with options:", options);
2533+
await client.executeStream(trimmedCommand, [], options);
24872534
const commandParts = trimmedCommand.split(" ");
24882535
const cmd = commandParts[0];
24892536
const args = commandParts.slice(1);
24902537
// Get the async generator
2491-
const streamGenerator = client.execStream(cmd, args, {
2492-
sessionId: sessionId || undefined,
2493-
});
2538+
const streamGenerator = client.execStream(cmd, args, options);
24942539
// Iterate through the stream events
24952540
for await (const event of streamGenerator) {
24962541
console.log("Stream event:", event);
@@ -2517,6 +2562,11 @@ function SandboxTester() {
25172562
console.log("Streaming command completed");
25182563

25192564
setCommandInput("");
2565+
2566+
// Refocus the input for better UX
2567+
setTimeout(() => {
2568+
commandInputRef.current?.focus();
2569+
}, 0);
25202570
} catch (error: any) {
25212571
console.error("Failed to execute streaming command:", error);
25222572
setResults((prev) => {
@@ -2528,6 +2578,11 @@ function SandboxTester() {
25282578
}
25292579
return updated;
25302580
});
2581+
2582+
// Refocus the input even on error
2583+
setTimeout(() => {
2584+
commandInputRef.current?.focus();
2585+
}, 100);
25312586
} finally {
25322587
setIsExecuting(false);
25332588
}
@@ -2622,6 +2677,7 @@ function SandboxTester() {
26222677
<div className="command-bar">
26232678
<span className="command-prompt">$</span>
26242679
<input
2680+
ref={commandInputRef}
26252681
type="text"
26262682
className="command-input"
26272683
value={commandInput}
@@ -2658,6 +2714,32 @@ function SandboxTester() {
26582714
</div>
26592715
</div>
26602716

2717+
{/* Command Options */}
2718+
<div className="command-options">
2719+
<div className="option-group">
2720+
<input
2721+
type="text"
2722+
placeholder="Working Directory (optional)"
2723+
value={commandOptions.cwd}
2724+
onChange={(e) =>
2725+
setCommandOptions((prev) => ({ ...prev, cwd: e.target.value }))
2726+
}
2727+
className="option-input"
2728+
disabled={isExecuting}
2729+
/>
2730+
<input
2731+
type="text"
2732+
placeholder="Environment (KEY1=val1,KEY2=val2)"
2733+
value={commandOptions.env}
2734+
onChange={(e) =>
2735+
setCommandOptions((prev) => ({ ...prev, env: e.target.value }))
2736+
}
2737+
className="option-input"
2738+
disabled={isExecuting}
2739+
/>
2740+
</div>
2741+
</div>
2742+
26612743
<div className="results-container" ref={resultsEndRef}>
26622744
{results.length === 0 ? (
26632745
<div

examples/basic/app/style.css

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1160,6 +1160,54 @@ body {
11601160
box-shadow: 0 0 0 3px rgba(88, 166, 255, 0.2);
11611161
}
11621162

1163+
/* Command Options */
1164+
.command-options {
1165+
margin-bottom: 1.5rem;
1166+
}
1167+
1168+
.option-group {
1169+
display: flex;
1170+
gap: 0.75rem;
1171+
flex-wrap: wrap;
1172+
}
1173+
1174+
.option-input {
1175+
flex: 1;
1176+
min-width: 200px;
1177+
background: #0d1117;
1178+
border: 1px solid #30363d;
1179+
border-radius: 6px;
1180+
padding: 0.5rem 0.75rem;
1181+
color: #c9d1d9;
1182+
font-size: 0.875rem;
1183+
font-family: "Inter", sans-serif;
1184+
transition: border-color 0.2s, box-shadow 0.2s;
1185+
}
1186+
1187+
.option-input:focus {
1188+
outline: none;
1189+
border-color: #58a6ff;
1190+
box-shadow: 0 0 0 3px rgba(88, 166, 255, 0.1);
1191+
}
1192+
1193+
.option-input::placeholder {
1194+
color: #6e7681;
1195+
}
1196+
1197+
.option-input:disabled {
1198+
opacity: 0.6;
1199+
cursor: not-allowed;
1200+
}
1201+
1202+
@media (max-width: 768px) {
1203+
.option-group {
1204+
flex-direction: column;
1205+
}
1206+
.option-input {
1207+
min-width: auto;
1208+
}
1209+
}
1210+
11631211
.command-prompt {
11641212
color: #3fb950;
11651213
font-family: "Fira Code", monospace;

examples/basic/src/endpoints/execute.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,13 @@ import { parseJsonBody, errorResponse, jsonResponse } from "../http";
33

44
export async function executeCommand(sandbox: Sandbox<unknown>, request: Request) {
55
const body = await parseJsonBody(request);
6-
const { command, sessionId } = body;
6+
const { command, sessionId, cwd, env } = body;
77
if (!command) {
88
return errorResponse("Command is required");
99
}
1010

1111
// Use the current SDK API signature: exec(command, options)
12-
const result = await sandbox.exec(command, { sessionId });
12+
const result = await sandbox.exec(command, { sessionId, cwd, env });
1313
return jsonResponse({
1414
success: result.exitCode === 0,
1515
exitCode: result.exitCode,

0 commit comments

Comments
 (0)