Skip to content

fixes / formats #35

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/ten-aliens-marry.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@cloudflare/sandbox": patch
---

update instructions
5 changes: 3 additions & 2 deletions .github/changeset-version.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@ execSync("npm install", {

// Update Dockerfile and README version references after changeset updates package.json
try {
const packageJson = JSON.parse(fs.readFileSync("./packages/sandbox/package.json", "utf-8"));
const packageJson = JSON.parse(
fs.readFileSync("./packages/sandbox/package.json", "utf-8")
);
const newVersion = packageJson.version;

const dockerfilePath = "./examples/basic/Dockerfile";
Expand Down Expand Up @@ -48,7 +50,6 @@ try {

fs.writeFileSync(readmePath, readmeContent);
console.log(`✅ Updated README.md version to ${newVersion}`);

} catch (error) {
console.error("❌ Failed to update file versions:", error);
// Don't fail the whole release for this
Expand Down
7 changes: 3 additions & 4 deletions examples/basic/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,7 @@ FROM cloudflare/sandbox-test:0.1.3
# arm64 build of the image.
# FROM --platform=linux/arm64 cloudflare/sandbox-test:0.1.3

EXPOSE 8080
EXPOSE 3001

# Run the same command as the original image
CMD ["bun", "index.ts"]
# expose any ports you might want to use (necessary for local dev)
# EXPOSE 8080
# EXPOSE 3001
4 changes: 4 additions & 0 deletions examples/basic/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,18 +49,21 @@ This example demonstrates the proper 3-layer architecture for Sandbox SDK applic
### Layer Responsibilities

**Frontend (`app/index.tsx`)**

- React-based UI with tabbed interface
- HTTP requests to Worker API endpoints
- Server-Sent Events for real-time streaming
- State management for commands, processes, and ports

**Worker (`src/index.ts`)**

- HTTP API gateway with endpoint routing
- Direct calls to Sandbox SDK methods
- SSE streaming for real-time updates
- CORS handling and error responses

**Sandbox Durable Object**

- Implements ISandbox interface methods
- Process lifecycle management
- AsyncIterable streaming capabilities
Expand All @@ -77,6 +80,7 @@ npm run deploy
## Development

### Project Structure

```
examples/basic/
├── src/
Expand Down
72 changes: 52 additions & 20 deletions examples/basic/app/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -499,12 +499,24 @@ body {
}

/* Status color classes */
.text-yellow-500 { color: #ffc107; }
.text-blue-500 { color: #58a6ff; }
.text-green-500 { color: #3fb950; }
.text-red-500 { color: #f85149; }
.text-orange-500 { color: #ff8c00; }
.text-gray-500 { color: #8b949e; }
.text-yellow-500 {
color: #ffc107;
}
.text-blue-500 {
color: #58a6ff;
}
.text-green-500 {
color: #3fb950;
}
.text-red-500 {
color: #f85149;
}
.text-orange-500 {
color: #ff8c00;
}
.text-gray-500 {
color: #8b949e;
}

/* Port Management Tab */
.port-management-tab {
Expand Down Expand Up @@ -784,15 +796,17 @@ body {
}

/* Command and Log Streaming Sections */
.command-streaming, .log-streaming {
.command-streaming,
.log-streaming {
background-color: #161b22;
border: 1px solid #30363d;
border-radius: 8px;
padding: 1.5rem;
margin-bottom: 2rem;
}

.command-streaming h3, .log-streaming h3 {
.command-streaming h3,
.log-streaming h3 {
color: #58a6ff;
font-size: 1.2rem;
margin-bottom: 0.5rem;
Expand Down Expand Up @@ -974,7 +988,8 @@ body {
font-size: 0.85rem;
}

.stream-time, .event-count {
.stream-time,
.event-count {
color: #8b949e;
font-size: 0.8rem;
font-family: "Fira Code", monospace;
Expand Down Expand Up @@ -1946,13 +1961,18 @@ button:focus {
}

.quick-setup-button:before {
content: '';
content: "";
position: absolute;
top: 0;
left: -100%;
width: 100%;
height: 100%;
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.1), transparent);
background: linear-gradient(
90deg,
transparent,
rgba(255, 255, 255, 0.1),
transparent
);
transition: left 0.5s;
}

Expand Down Expand Up @@ -1982,7 +2002,11 @@ button:focus {

.quick-setup-button.react {
border-color: #61dafb;
background: linear-gradient(135deg, rgba(97, 218, 251, 0.1) 0%, rgba(97, 218, 251, 0.05) 100%);
background: linear-gradient(
135deg,
rgba(97, 218, 251, 0.1) 0%,
rgba(97, 218, 251, 0.05) 100%
);
}

.quick-setup-button.react:hover:not(:disabled) {
Expand All @@ -1992,7 +2016,11 @@ button:focus {

.quick-setup-button.vue {
border-color: #4fc08d;
background: linear-gradient(135deg, rgba(79, 192, 141, 0.1) 0%, rgba(79, 192, 141, 0.05) 100%);
background: linear-gradient(
135deg,
rgba(79, 192, 141, 0.1) 0%,
rgba(79, 192, 141, 0.05) 100%
);
}

.quick-setup-button.vue:hover:not(:disabled) {
Expand All @@ -2002,7 +2030,11 @@ button:focus {

.quick-setup-button.static {
border-color: #f39c12;
background: linear-gradient(135deg, rgba(243, 156, 18, 0.1) 0%, rgba(243, 156, 18, 0.05) 100%);
background: linear-gradient(
135deg,
rgba(243, 156, 18, 0.1) 0%,
rgba(243, 156, 18, 0.05) 100%
);
}

.quick-setup-button.static:hover:not(:disabled) {
Expand Down Expand Up @@ -2040,27 +2072,27 @@ button:focus {
.input-group {
flex-direction: column;
}

.file-input {
min-width: unset;
}

.template-buttons {
flex-direction: column;
}

.template-button {
width: 100%;
}

.quick-setup-buttons {
grid-template-columns: 1fr;
}

.quick-setup-button {
padding: 1rem;
}

.setup-icon {
font-size: 2rem;
min-width: 50px;
Expand Down
35 changes: 19 additions & 16 deletions examples/basic/src/endpoints/execute.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,24 @@
import type { Sandbox } from "@cloudflare/sandbox";
import { parseJsonBody, errorResponse, jsonResponse } from "../http";

export async function executeCommand(sandbox: Sandbox<unknown>, request: Request) {
const body = await parseJsonBody(request);
const { command, sessionId } = body;
if (!command) {
return errorResponse("Command is required");
}
export async function executeCommand(
sandbox: Sandbox<unknown>,
request: Request
) {
const body = await parseJsonBody(request);
const { command, sessionId } = body;
if (!command) {
return errorResponse("Command is required");
}

// Use the current SDK API signature: exec(command, options)
const result = await sandbox.exec(command, { sessionId });
return jsonResponse({
success: result.exitCode === 0,
exitCode: result.exitCode,
stdout: result.stdout,
stderr: result.stderr,
command: result.command,
duration: result.duration
});
// Use the current SDK API signature: exec(command, options)
const result = await sandbox.exec(command, { sessionId });
return jsonResponse({
success: result.exitCode === 0,
exitCode: result.exitCode,
stdout: result.stdout,
stderr: result.stderr,
command: result.command,
duration: result.duration,
});
}
87 changes: 47 additions & 40 deletions examples/basic/src/endpoints/executeStream.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,49 +2,56 @@ import type { Sandbox } from "@cloudflare/sandbox";
import { parseSSEStream, type ExecEvent } from "@cloudflare/sandbox";
import { corsHeaders, errorResponse, parseJsonBody } from "../http";

export async function executeCommandStream(sandbox: Sandbox<unknown>, request: Request) {
const body = await parseJsonBody(request);
const { command, sessionId } = body;
export async function executeCommandStream(
sandbox: Sandbox<unknown>,
request: Request
) {
const body = await parseJsonBody(request);
const { command, sessionId } = body;

if (!command) {
return errorResponse("Command is required");
}
if (!command) {
return errorResponse("Command is required");
}

// Create readable stream for SSE
const { readable, writable } = new TransformStream();
const writer = writable.getWriter();

// Create readable stream for SSE
const { readable, writable } = new TransformStream();
const writer = writable.getWriter();
// Start streaming in the background
(async () => {
try {
const encoder = new TextEncoder();

// Start streaming in the background
(async () => {
try {
const encoder = new TextEncoder();
// Get the ReadableStream from sandbox
const stream = await sandbox.execStream(command, { sessionId });

// Get the ReadableStream from sandbox
const stream = await sandbox.execStream(command, { sessionId });

// Convert to AsyncIterable using parseSSEStream
for await (const event of parseSSEStream<ExecEvent>(stream)) {
// Forward each typed event as SSE
await writer.write(encoder.encode(`data: ${JSON.stringify(event)}\n\n`));
}
} catch (error: any) {
const errorEvent = {
type: 'error',
timestamp: new Date().toISOString(),
error: error.message
};
await writer.write(new TextEncoder().encode(`data: ${JSON.stringify(errorEvent)}\n\n`));
} finally {
await writer.close();
}
})();
// Convert to AsyncIterable using parseSSEStream
for await (const event of parseSSEStream<ExecEvent>(stream)) {
// Forward each typed event as SSE
await writer.write(
encoder.encode(`data: ${JSON.stringify(event)}\n\n`)
);
}
} catch (error: any) {
const errorEvent = {
type: "error",
timestamp: new Date().toISOString(),
error: error.message,
};
await writer.write(
new TextEncoder().encode(`data: ${JSON.stringify(errorEvent)}\n\n`)
);
} finally {
await writer.close();
}
})();

return new Response(readable, {
headers: {
"Content-Type": "text/event-stream",
"Cache-Control": "no-cache",
"Connection": "keep-alive",
...corsHeaders(),
},
});
return new Response(readable, {
headers: {
"Content-Type": "text/event-stream",
"Cache-Control": "no-cache",
Connection: "keep-alive",
...corsHeaders(),
},
});
}
6 changes: 3 additions & 3 deletions examples/basic/src/endpoints/fileDelete.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,14 @@ export async function deleteFile(sandbox: Sandbox<unknown>, request: Request) {
}

await sandbox.deleteFile(path);
return jsonResponse({
return jsonResponse({
success: true,
message: "File deleted",
path,
timestamp: new Date().toISOString()
timestamp: new Date().toISOString(),
});
} catch (error: any) {
console.error("Error deleting file:", error);
return errorResponse(`Failed to delete file: ${error.message}`);
}
}
}
6 changes: 3 additions & 3 deletions examples/basic/src/endpoints/fileMove.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,15 @@ export async function moveFile(sandbox: Sandbox<unknown>, request: Request) {
}

await sandbox.moveFile(sourcePath, destinationPath);
return jsonResponse({
return jsonResponse({
success: true,
message: "File moved",
sourcePath,
destinationPath,
timestamp: new Date().toISOString()
timestamp: new Date().toISOString(),
});
} catch (error: any) {
console.error("Error moving file:", error);
return errorResponse(`Failed to move file: ${error.message}`);
}
}
}
Loading