Skip to content
Open
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
44 changes: 20 additions & 24 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,43 +2,39 @@

## [2.2.0](https://github.com/codesandbox/codesandbox-sdk/compare/v2.1.0...v2.2.0) (2025-09-02)


### Features

* Add traceparent for all requests to API ([#180](https://github.com/codesandbox/codesandbox-sdk/issues/180)) ([b6f4846](https://github.com/codesandbox/codesandbox-sdk/commit/b6f484665de0bf0533127e098ff0ef1aa641a84b))
* batch writes ([#175](https://github.com/codesandbox/codesandbox-sdk/issues/175)) ([493c5d5](https://github.com/codesandbox/codesandbox-sdk/commit/493c5d52d1eaa527d099b0270a5b0e78a694abbd))

- Add traceparent for all requests to API ([#180](https://github.com/codesandbox/codesandbox-sdk/issues/180)) ([b6f4846](https://github.com/codesandbox/codesandbox-sdk/commit/b6f484665de0bf0533127e098ff0ef1aa641a84b))
- batch writes ([#175](https://github.com/codesandbox/codesandbox-sdk/issues/175)) ([493c5d5](https://github.com/codesandbox/codesandbox-sdk/commit/493c5d52d1eaa527d099b0270a5b0e78a694abbd))

### Bug Fixes

* ensure private preview on private sandbox ([#179](https://github.com/codesandbox/codesandbox-sdk/issues/179)) ([04381a0](https://github.com/codesandbox/codesandbox-sdk/commit/04381a071fb54aa385ad40ed7ff6d489945565ef))
* prevent api config overrides ([#177](https://github.com/codesandbox/codesandbox-sdk/issues/177)) ([a9ec1a7](https://github.com/codesandbox/codesandbox-sdk/commit/a9ec1a78c2c83a53dfd0649dfa1764589b9fa671))
* Queue messages on lost connection and reconnect also on hibernation ([#176](https://github.com/codesandbox/codesandbox-sdk/issues/176)) ([c5a8ffd](https://github.com/codesandbox/codesandbox-sdk/commit/c5a8ffdf4bcba321c4d3c9581f752c5e72a3bc8f))
- ensure private preview on private sandbox ([#179](https://github.com/codesandbox/codesandbox-sdk/issues/179)) ([04381a0](https://github.com/codesandbox/codesandbox-sdk/commit/04381a071fb54aa385ad40ed7ff6d489945565ef))
- prevent api config overrides ([#177](https://github.com/codesandbox/codesandbox-sdk/issues/177)) ([a9ec1a7](https://github.com/codesandbox/codesandbox-sdk/commit/a9ec1a78c2c83a53dfd0649dfa1764589b9fa671))
- Queue messages on lost connection and reconnect also on hibernation ([#176](https://github.com/codesandbox/codesandbox-sdk/issues/176)) ([c5a8ffd](https://github.com/codesandbox/codesandbox-sdk/commit/c5a8ffdf4bcba321c4d3c9581f752c5e72a3bc8f))

## [2.1.0](https://github.com/codesandbox/codesandbox-sdk/compare/v2.0.7...v2.1.0) (2025-08-22)


### Features

* add fetching single sandbox ([#142](https://github.com/codesandbox/codesandbox-sdk/issues/142)) ([2f58d43](https://github.com/codesandbox/codesandbox-sdk/commit/2f58d43ee44c98eeb7d0e917c8b423a80d202585))
* add listRunning method to sandboxes namespace ([#145](https://github.com/codesandbox/codesandbox-sdk/issues/145)) ([6050dbd](https://github.com/codesandbox/codesandbox-sdk/commit/6050dbd289058c782e634a7ce9e25d9cf5921276))
* add open telemetry for sandboxes methods ([#147](https://github.com/codesandbox/codesandbox-sdk/issues/147)) ([b331315](https://github.com/codesandbox/codesandbox-sdk/commit/b3313153b357dfda0d666a6a56ea79f6aa3dbbdf))
* add tracing to Sandbox and SandboxClient, also allow passing to browser and node connectors ([#150](https://github.com/codesandbox/codesandbox-sdk/issues/150)) ([6ef2bf5](https://github.com/codesandbox/codesandbox-sdk/commit/6ef2bf51120068e35ff43607e3353a69d8fbf070))
* Debug Sandboxes through CLI ([#163](https://github.com/codesandbox/codesandbox-sdk/issues/163)) ([9af1cdd](https://github.com/codesandbox/codesandbox-sdk/commit/9af1cdd0657c1a74572dc473ac6e04f6e1a40cd5))
* enhance container setup logging in build command ([836a7a6](https://github.com/codesandbox/codesandbox-sdk/commit/836a7a6ed1dc7c73d737e04475083faf8d6d8fc4))
* enhance container setup logging in build command ([a6f9fe7](https://github.com/codesandbox/codesandbox-sdk/commit/a6f9fe7c93450cfeded21048f62a6a2f0b842091))
* private sandbox, public hosts with public-hosts privacy ([#154](https://github.com/codesandbox/codesandbox-sdk/issues/154)) ([dce7caf](https://github.com/codesandbox/codesandbox-sdk/commit/dce7cafd719e398b1f1421776bd55fa5601eccd0))

- add fetching single sandbox ([#142](https://github.com/codesandbox/codesandbox-sdk/issues/142)) ([2f58d43](https://github.com/codesandbox/codesandbox-sdk/commit/2f58d43ee44c98eeb7d0e917c8b423a80d202585))
- add listRunning method to sandboxes namespace ([#145](https://github.com/codesandbox/codesandbox-sdk/issues/145)) ([6050dbd](https://github.com/codesandbox/codesandbox-sdk/commit/6050dbd289058c782e634a7ce9e25d9cf5921276))
- add open telemetry for sandboxes methods ([#147](https://github.com/codesandbox/codesandbox-sdk/issues/147)) ([b331315](https://github.com/codesandbox/codesandbox-sdk/commit/b3313153b357dfda0d666a6a56ea79f6aa3dbbdf))
- add tracing to Sandbox and SandboxClient, also allow passing to browser and node connectors ([#150](https://github.com/codesandbox/codesandbox-sdk/issues/150)) ([6ef2bf5](https://github.com/codesandbox/codesandbox-sdk/commit/6ef2bf51120068e35ff43607e3353a69d8fbf070))
- Debug Sandboxes through CLI ([#163](https://github.com/codesandbox/codesandbox-sdk/issues/163)) ([9af1cdd](https://github.com/codesandbox/codesandbox-sdk/commit/9af1cdd0657c1a74572dc473ac6e04f6e1a40cd5))
- enhance container setup logging in build command ([836a7a6](https://github.com/codesandbox/codesandbox-sdk/commit/836a7a6ed1dc7c73d737e04475083faf8d6d8fc4))
- enhance container setup logging in build command ([a6f9fe7](https://github.com/codesandbox/codesandbox-sdk/commit/a6f9fe7c93450cfeded21048f62a6a2f0b842091))
- private sandbox, public hosts with public-hosts privacy ([#154](https://github.com/codesandbox/codesandbox-sdk/issues/154)) ([dce7caf](https://github.com/codesandbox/codesandbox-sdk/commit/dce7cafd719e398b1f1421776bd55fa5601eccd0))

### Bug Fixes

* Add custom retry delay support for startVM API calls ([#156](https://github.com/codesandbox/codesandbox-sdk/issues/156)) ([ce3a282](https://github.com/codesandbox/codesandbox-sdk/commit/ce3a2823e66198f453d257a68757226d50e3bf17))
* Decouple pitcher-client ([#148](https://github.com/codesandbox/codesandbox-sdk/issues/148)) ([3a6f9ea](https://github.com/codesandbox/codesandbox-sdk/commit/3a6f9ea213d978dc5a896bfc4d275deae6608abe))
* friendly 503 error for overloaded Sandbox ([#172](https://github.com/codesandbox/codesandbox-sdk/issues/172)) ([f9987b1](https://github.com/codesandbox/codesandbox-sdk/commit/f9987b1a0b625bd580b94b6035c17d09d561cbbc))
* include response handling in retries and dispose clients in build to avoid reconnecst ([#162](https://github.com/codesandbox/codesandbox-sdk/issues/162)) ([f70903a](https://github.com/codesandbox/codesandbox-sdk/commit/f70903a593c51bdfcdbf49d86b7b7bdce7cfe4a4))
* properly dispose and prevent wakeups ([#170](https://github.com/codesandbox/codesandbox-sdk/issues/170)) ([029e3a5](https://github.com/codesandbox/codesandbox-sdk/commit/029e3a554010fe278eaeb6632f9df45264cbdc29))
* Stabilize websocket connection ([#166](https://github.com/codesandbox/codesandbox-sdk/issues/166)) ([cb2f330](https://github.com/codesandbox/codesandbox-sdk/commit/cb2f330897c5e4c26637bc39a85d0e28dc4331d6))
* update log line length to be smaller ([9a3099f](https://github.com/codesandbox/codesandbox-sdk/commit/9a3099fc3984c6ff01fa901ac1616cbc7b883119))
- Add custom retry delay support for startVM API calls ([#156](https://github.com/codesandbox/codesandbox-sdk/issues/156)) ([ce3a282](https://github.com/codesandbox/codesandbox-sdk/commit/ce3a2823e66198f453d257a68757226d50e3bf17))
- Decouple pitcher-client ([#148](https://github.com/codesandbox/codesandbox-sdk/issues/148)) ([3a6f9ea](https://github.com/codesandbox/codesandbox-sdk/commit/3a6f9ea213d978dc5a896bfc4d275deae6608abe))
- friendly 503 error for overloaded Sandbox ([#172](https://github.com/codesandbox/codesandbox-sdk/issues/172)) ([f9987b1](https://github.com/codesandbox/codesandbox-sdk/commit/f9987b1a0b625bd580b94b6035c17d09d561cbbc))
- include response handling in retries and dispose clients in build to avoid reconnecst ([#162](https://github.com/codesandbox/codesandbox-sdk/issues/162)) ([f70903a](https://github.com/codesandbox/codesandbox-sdk/commit/f70903a593c51bdfcdbf49d86b7b7bdce7cfe4a4))
- properly dispose and prevent wakeups ([#170](https://github.com/codesandbox/codesandbox-sdk/issues/170)) ([029e3a5](https://github.com/codesandbox/codesandbox-sdk/commit/029e3a554010fe278eaeb6632f9df45264cbdc29))
- Stabilize websocket connection ([#166](https://github.com/codesandbox/codesandbox-sdk/issues/166)) ([cb2f330](https://github.com/codesandbox/codesandbox-sdk/commit/cb2f330897c5e4c26637bc39a85d0e28dc4331d6))
- update log line length to be smaller ([9a3099f](https://github.com/codesandbox/codesandbox-sdk/commit/9a3099fc3984c6ff01fa901ac1616cbc7b883119))

## [2.0.7](https://github.com/codesandbox/codesandbox-sdk/compare/v2.0.6...v2.0.7) (2025-08-06)

Expand Down
1 change: 0 additions & 1 deletion src/API.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,6 @@ import type {
} from "./api-clients/client";
import { PitcherManagerResponse } from "./types";


export interface APIOptions {
apiKey: string;
config?: Config;
Expand Down
33 changes: 21 additions & 12 deletions src/bin/ui/components/VmTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -138,23 +138,24 @@ export const VmTable = ({
{/* Header */}
<Box>
<Text bold color="blue">
{padString("VM ID", 14)} {padString("Last Active", 14)} {padString("Started At", 14)} {padString("Runtime", 10)} Credits
{padString("VM ID", 14)} {padString("Last Active", 14)}{" "}
{padString("Started At", 14)} {padString("Runtime", 10)} Credits
</Text>
</Box>

{/* Separator */}
<Box>
<Text dimColor>{"─".repeat(Math.min(terminalWidth - 2, 60))}</Text>
</Box>

{/* Data rows */}
{visibleVms.map((vm, visibleIndex) => {
const actualIndex = startIndex + visibleIndex;
const isSelected = selectedIndex === actualIndex;

// Safely get VM ID and handle edge cases
const vmId = (vm?.id && typeof vm.id === 'string') ? vm.id : "N/A";
const vmId = vm?.id && typeof vm.id === "string" ? vm.id : "N/A";

// Skip rendering if VM is completely invalid
if (!vm) {
return (
Expand All @@ -163,26 +164,34 @@ export const VmTable = ({
</Box>
);
}

return (
<Box key={`${vmId}-${actualIndex}`}>
<Text
<Text
backgroundColor={isSelected ? "blue" : undefined}
color={isSelected ? "white" : undefined}
>
{padString(vmId, 14)} {padString(formatDate(vm.last_active_at), 14)} {padString(formatDate(vm.session_started_at), 14)} {padString(calculateRuntime(vm.session_started_at, vm.last_active_at), 10)} {vm.credit_basis || "N/A"} cr/hr
{padString(vmId, 14)}{" "}
{padString(formatDate(vm.last_active_at), 14)}{" "}
{padString(formatDate(vm.session_started_at), 14)}{" "}
{padString(
calculateRuntime(vm.session_started_at, vm.last_active_at),
10
)}{" "}
{vm.credit_basis || "N/A"} cr/hr
</Text>
</Box>
);
})}

{/* VM count and range info */}
<Box marginTop={1}>
<Text dimColor>
{vmsSorted.length <= maxVisibleRows
? `${vmsSorted.length} VMs total`
: `Showing ${startIndex + 1}-${endIndex} of ${vmsSorted.length} VMs`
}
: `Showing ${startIndex + 1}-${endIndex} of ${
vmsSorted.length
} VMs`}
</Text>
</Box>
</Box>
Expand Down
2 changes: 1 addition & 1 deletion src/bin/ui/hooks/useVmInput.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ export const useVmInput = ({ vms, onSubmit }: UseVmInputOptions) => {

setSelectedVmIndex(newIndex);
const vm = vms[newIndex];
const vmId = (vm?.id && typeof vm.id === 'string') ? vm.id : null;
const vmId = vm?.id && typeof vm.id === "string" ? vm.id : null;
setSelectedVm(vmId);

// Set the selected VM ID in the text input
Expand Down
14 changes: 8 additions & 6 deletions src/bin/ui/views/Dashboard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,25 +56,27 @@ export const Dashboard = () => {
if (!a.session_started_at && !b.session_started_at) return 0;
if (!a.session_started_at) return 1;
if (!b.session_started_at) return -1;

const dateA = new Date(a.session_started_at);
const dateB = new Date(b.session_started_at);

// Check for invalid dates
if (isNaN(dateA.getTime()) && isNaN(dateB.getTime())) return 0;
if (isNaN(dateA.getTime())) return 1;
if (isNaN(dateB.getTime())) return -1;

return dateA.getTime() - dateB.getTime();
})
: [];


// Calculate visible rows dynamically based on terminal size
// Account for UI elements: instructions (1), sandbox label (1), input field (1),
// Account for UI elements: instructions (1), sandbox label (1), input field (1),
// table title (1), table header (1), separator (1), bottom margin (2)
const uiElementRows = 8;
const maxVisibleRows = Math.max(1, Math.floor((terminalHeight - uiElementRows) * 0.7) - 3);
const maxVisibleRows = Math.max(
1,
Math.floor((terminalHeight - uiElementRows) * 0.7) - 3
);

const {
sandboxId,
Expand Down
30 changes: 28 additions & 2 deletions src/bin/ui/views/Sandbox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { Box, Text, useInput } from "ink";
import { useView } from "../viewContext";
import { useQuery } from "@tanstack/react-query";
import { useSDK } from "../sdkContext";
import { exec } from "child_process";

export const Sandbox = () => {
const { view, setView } = useView<"sandbox">();
Expand Down Expand Up @@ -61,9 +62,16 @@ export const Sandbox = () => {
const getMenuOptions = () => {
switch (sandboxState) {
case "RUNNING":
return ["Open", "Terminal", "Hibernate", "Shutdown", "Restart"];
return [
"Open",
"Open editor in browser",
"Terminal",
"Hibernate",
"Shutdown",
"Restart",
];
case "IDLE":
return ["Start"];
return ["Start", "Open editor in browser"];
default:
return [];
}
Expand All @@ -80,6 +88,24 @@ export const Sandbox = () => {
case "Open":
setView({ name: "open", params: { id: view.params.id } });
break;
case "Open editor in browser":
const url = `https://codesandbox.io/s/${view.params.id}`;
const platform = process.platform;

// Open browser based on platform
const command =
platform === "darwin"
? `open "${url}"`
: platform === "win32"
? `start "${url}"`
: `xdg-open "${url}"`; // Linux

exec(command, (error) => {
if (error) {
console.error(`Failed to open browser: ${error.message}`);
}
});
break;
case "Hibernate":
case "Shutdown":
setSandboxState("PENDING");
Expand Down
110 changes: 55 additions & 55 deletions tests/sandbox-creation.test.ts
Original file line number Diff line number Diff line change
@@ -1,80 +1,80 @@
import { describe, it, expect, beforeEach, afterEach } from 'vitest'
import nock from 'nock'
import { CodeSandbox } from '../src/index'
import {
mockForkSandboxSuccess,
mockStartVMSuccess,
setupTestEnvironment,
cleanupTestEnvironment
} from './test-utils'

describe('Sandbox Creation', () => {
import { describe, it, expect, beforeEach, afterEach } from "vitest";
import nock from "nock";
import { CodeSandbox } from "../src/index";
import {
mockForkSandboxSuccess,
mockStartVMSuccess,
setupTestEnvironment,
cleanupTestEnvironment,
} from "./test-utils";

describe("Sandbox Creation", () => {
beforeEach(() => {
setupTestEnvironment()
})
setupTestEnvironment();
});

afterEach(() => {
cleanupTestEnvironment()
})
cleanupTestEnvironment();
});

it('should successfully create and start a sandbox', async () => {
it("should successfully create and start a sandbox", async () => {
// Mock the fork sandbox API call (pcz35m is the default template)
const forkScope = mockForkSandboxSuccess('test-sandbox-123', {
title: 'Test Sandbox',
description: 'Integration test sandbox',
const forkScope = mockForkSandboxSuccess("test-sandbox-123", {
title: "Test Sandbox",
description: "Integration test sandbox",
privacy: 1,
tags: ['integration-test', 'sdk']
})
tags: ["integration-test", "sdk"],
});

// Mock the start VM API call - use regex to match any ID
const startScope = mockStartVMSuccess('test-sandbox-123')
const startScope = mockStartVMSuccess("test-sandbox-123");

// Initialize SDK
const sdk = new CodeSandbox()
const sdk = new CodeSandbox();

// Create sandbox
const sandbox = await sdk.sandboxes.create({
title: 'Test Sandbox',
description: 'Integration test sandbox',
privacy: 'unlisted',
tags: ['integration-test']
})
title: "Test Sandbox",
description: "Integration test sandbox",
privacy: "unlisted",
tags: ["integration-test"],
});

// Verify sandbox was created successfully
expect(sandbox).toBeDefined()
expect(sandbox.id).toBe('test-sandbox-123')
expect(sandbox).toBeDefined();
expect(sandbox.id).toBe("test-sandbox-123");

// Verify all API calls were made
expect(forkScope.isDone()).toBe(true)
expect(startScope.isDone()).toBe(true)
}, 10000) // 10 second timeout for integration test
expect(forkScope.isDone()).toBe(true);
expect(startScope.isDone()).toBe(true);
}, 10000); // 10 second timeout for integration test

it('should use default template when no id is provided', async () => {
it("should use default template when no id is provided", async () => {
// Mock default template call - pcz35m is the default template
const forkScope = mockForkSandboxSuccess('default-sandbox-456')
const forkScope = mockForkSandboxSuccess("default-sandbox-456");

const startScope = mockStartVMSuccess('default-sandbox-456')
const startScope = mockStartVMSuccess("default-sandbox-456");

const sdk = new CodeSandbox();

const sdk = new CodeSandbox()

// Create sandbox without specifying template id
const sandbox = await sdk.sandboxes.create()
const sandbox = await sdk.sandboxes.create();

expect(sandbox).toBeDefined()
expect(sandbox.id).toBe('default-sandbox-456')
expect(forkScope.isDone()).toBe(true)
expect(startScope.isDone()).toBe(true)
})
expect(sandbox).toBeDefined();
expect(sandbox.id).toBe("default-sandbox-456");
expect(forkScope.isDone()).toBe(true);
expect(startScope.isDone()).toBe(true);
});

it('should handle API errors gracefully', async () => {
it("should handle API errors gracefully", async () => {
// Mock fork sandbox failure
nock('https://api.codesandbox.io')
.post('/sandbox/pcz35m/fork')
.reply(500, { message: 'Internal server error' })
nock("https://api.codesandbox.io")
.post("/sandbox/pcz35m/fork")
.reply(500, { message: "Internal server error" });

const sdk = new CodeSandbox();

const sdk = new CodeSandbox()

// Expect the creation to throw an error
await expect(sdk.sandboxes.create()).rejects.toThrow()
})
})
await expect(sdk.sandboxes.create()).rejects.toThrow();
});
});
Loading