Skip to content

Commit 29b159c

Browse files
committed
wip
1 parent 34d4f36 commit 29b159c

28 files changed

+2133
-390
lines changed

examples/daytona/daytona.test.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { describe, it, expect } from "vitest";
2+
import { buildHeaders } from "../shared/sandbox-agent-client.ts";
3+
import { setupDaytonaSandboxAgent } from "./daytona.ts";
4+
5+
const shouldRun = Boolean(process.env.DAYTONA_API_KEY);
6+
const timeoutMs = Number.parseInt(process.env.SANDBOX_TEST_TIMEOUT_MS || "", 10) || 300_000;
7+
8+
const testFn = shouldRun ? it : it.skip;
9+
10+
describe("daytona example", () => {
11+
testFn(
12+
"starts sandbox-agent and responds to /v1/health",
13+
async () => {
14+
const { baseUrl, token, extraHeaders, cleanup } = await setupDaytonaSandboxAgent();
15+
try {
16+
const response = await fetch(`${baseUrl}/v1/health`, {
17+
headers: buildHeaders({ token, extraHeaders }),
18+
});
19+
expect(response.ok).toBe(true);
20+
const data = await response.json();
21+
expect(data.status).toBe("ok");
22+
} finally {
23+
await cleanup();
24+
}
25+
},
26+
timeoutMs
27+
);
28+
});

examples/daytona/daytona.ts

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
import { Daytona } from "@daytonaio/sdk";
2+
import { pathToFileURL } from "node:url";
3+
import {
4+
ensureUrl,
5+
runPrompt,
6+
waitForHealth,
7+
} from "../shared/sandbox-agent-client.ts";
8+
9+
const INSTALL_SCRIPT = "curl -fsSL https://releases.rivet.dev/sandbox-agent/latest/install.sh | sh";
10+
const DEFAULT_PORT = 3000;
11+
12+
export async function setupDaytonaSandboxAgent(): Promise<{
13+
baseUrl: string;
14+
token: string;
15+
extraHeaders: Record<string, string>;
16+
cleanup: () => Promise<void>;
17+
}> {
18+
const token = process.env.SANDBOX_TOKEN || "";
19+
const port = Number.parseInt(process.env.SANDBOX_PORT || "", 10) || DEFAULT_PORT;
20+
const language = process.env.DAYTONA_LANGUAGE || "typescript";
21+
22+
const daytona = new Daytona();
23+
const sandbox = await daytona.create({
24+
language,
25+
});
26+
27+
await sandbox.process.executeCommand(`bash -lc "${INSTALL_SCRIPT}"`);
28+
29+
const tokenFlag = token ? "--token $SANDBOX_TOKEN" : "--no-token";
30+
const serverCommand = `nohup sandbox-agent server ${tokenFlag} --host 0.0.0.0 --port ${port} >/tmp/sandbox-agent.log 2>&1 &`;
31+
await sandbox.process.executeCommand(`bash -lc "${serverCommand}"`);
32+
33+
const preview = await sandbox.getPreviewLink(port);
34+
const extraHeaders: Record<string, string> = {};
35+
if (preview.token) {
36+
extraHeaders["x-daytona-preview-token"] = preview.token;
37+
}
38+
extraHeaders["x-daytona-skip-preview-warning"] = "true";
39+
40+
const baseUrl = ensureUrl(preview.url);
41+
await waitForHealth({ baseUrl, token, extraHeaders });
42+
43+
const cleanup = async () => {
44+
try {
45+
await sandbox.delete(60);
46+
} catch {
47+
// ignore cleanup errors
48+
}
49+
};
50+
51+
return {
52+
baseUrl,
53+
token,
54+
extraHeaders,
55+
cleanup,
56+
};
57+
}
58+
59+
async function main(): Promise<void> {
60+
const { baseUrl, token, extraHeaders, cleanup } = await setupDaytonaSandboxAgent();
61+
62+
const exitHandler = async () => {
63+
await cleanup();
64+
process.exit(0);
65+
};
66+
67+
process.on("SIGINT", () => {
68+
void exitHandler();
69+
});
70+
process.on("SIGTERM", () => {
71+
void exitHandler();
72+
});
73+
74+
await runPrompt({ baseUrl, token, extraHeaders });
75+
}
76+
77+
if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
78+
main().catch((error) => {
79+
console.error(error);
80+
process.exit(1);
81+
});
82+
}

examples/docker/docker.test.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { describe, it, expect } from "vitest";
2+
import { buildHeaders } from "../shared/sandbox-agent-client.ts";
3+
import { setupDockerSandboxAgent } from "./docker.ts";
4+
5+
const shouldRun = process.env.RUN_DOCKER_EXAMPLES === "1";
6+
const timeoutMs = Number.parseInt(process.env.SANDBOX_TEST_TIMEOUT_MS || "", 10) || 300_000;
7+
8+
const testFn = shouldRun ? it : it.skip;
9+
10+
describe("docker example", () => {
11+
testFn(
12+
"starts sandbox-agent and responds to /v1/health",
13+
async () => {
14+
const { baseUrl, token, cleanup } = await setupDockerSandboxAgent();
15+
try {
16+
const response = await fetch(`${baseUrl}/v1/health`, {
17+
headers: buildHeaders({ token }),
18+
});
19+
expect(response.ok).toBe(true);
20+
const data = await response.json();
21+
expect(data.status).toBe("ok");
22+
} finally {
23+
await cleanup();
24+
}
25+
},
26+
timeoutMs
27+
);
28+
});

examples/docker/docker.ts

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
import Docker from "dockerode";
2+
import { pathToFileURL } from "node:url";
3+
import {
4+
ensureUrl,
5+
runPrompt,
6+
waitForHealth,
7+
} from "../shared/sandbox-agent-client.ts";
8+
9+
const INSTALL_SCRIPT = "curl -fsSL https://releases.rivet.dev/sandbox-agent/latest/install.sh | sh";
10+
const DEFAULT_IMAGE = "debian:bookworm-slim";
11+
const DEFAULT_PORT = 2468;
12+
13+
async function pullImage(docker: Docker, image: string): Promise<void> {
14+
await new Promise<void>((resolve, reject) => {
15+
docker.pull(image, (error, stream) => {
16+
if (error) {
17+
reject(error);
18+
return;
19+
}
20+
docker.modem.followProgress(stream, (progressError) => {
21+
if (progressError) {
22+
reject(progressError);
23+
} else {
24+
resolve();
25+
}
26+
});
27+
});
28+
});
29+
}
30+
31+
async function ensureImage(docker: Docker, image: string): Promise<void> {
32+
try {
33+
await docker.getImage(image).inspect();
34+
} catch {
35+
await pullImage(docker, image);
36+
}
37+
}
38+
39+
export async function setupDockerSandboxAgent(): Promise<{
40+
baseUrl: string;
41+
token: string;
42+
cleanup: () => Promise<void>;
43+
}> {
44+
const token = process.env.SANDBOX_TOKEN || "";
45+
const port = Number.parseInt(process.env.SANDBOX_PORT || "", 10) || DEFAULT_PORT;
46+
const hostPort = Number.parseInt(process.env.SANDBOX_HOST_PORT || "", 10) || port;
47+
const image = process.env.DOCKER_IMAGE || DEFAULT_IMAGE;
48+
const containerName = process.env.DOCKER_CONTAINER_NAME;
49+
const socketPath = process.env.DOCKER_SOCKET || "/var/run/docker.sock";
50+
51+
const docker = new Docker({ socketPath });
52+
await ensureImage(docker, image);
53+
54+
const tokenFlag = token ? "--token $SANDBOX_TOKEN" : "--no-token";
55+
const command = [
56+
"bash",
57+
"-lc",
58+
[
59+
"apt-get update",
60+
"apt-get install -y curl ca-certificates",
61+
INSTALL_SCRIPT,
62+
`sandbox-agent server ${tokenFlag} --host 0.0.0.0 --port ${port}`,
63+
].join(" && "),
64+
];
65+
66+
const container = await docker.createContainer({
67+
Image: image,
68+
Cmd: command,
69+
Env: token ? [`SANDBOX_TOKEN=${token}`] : [],
70+
ExposedPorts: {
71+
[`${port}/tcp`]: {},
72+
},
73+
HostConfig: {
74+
AutoRemove: true,
75+
PortBindings: {
76+
[`${port}/tcp`]: [{ HostPort: `${hostPort}` }],
77+
},
78+
},
79+
...(containerName ? { name: containerName } : {}),
80+
});
81+
82+
await container.start();
83+
84+
const baseUrl = ensureUrl(`http://127.0.0.1:${hostPort}`);
85+
await waitForHealth({ baseUrl, token });
86+
87+
const cleanup = async () => {
88+
try {
89+
await container.stop({ t: 5 });
90+
} catch {
91+
// ignore stop errors
92+
}
93+
try {
94+
await container.remove({ force: true });
95+
} catch {
96+
// ignore remove errors
97+
}
98+
};
99+
100+
return {
101+
baseUrl,
102+
token,
103+
cleanup,
104+
};
105+
}
106+
107+
async function main(): Promise<void> {
108+
const { baseUrl, token, cleanup } = await setupDockerSandboxAgent();
109+
110+
const exitHandler = async () => {
111+
await cleanup();
112+
process.exit(0);
113+
};
114+
115+
process.on("SIGINT", () => {
116+
void exitHandler();
117+
});
118+
process.on("SIGTERM", () => {
119+
void exitHandler();
120+
});
121+
122+
await runPrompt({ baseUrl, token });
123+
}
124+
125+
if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
126+
main().catch((error) => {
127+
console.error(error);
128+
process.exit(1);
129+
});
130+
}

examples/e2b/e2b.test.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { describe, it, expect } from "vitest";
2+
import { buildHeaders } from "../shared/sandbox-agent-client.ts";
3+
import { setupE2BSandboxAgent } from "./e2b.ts";
4+
5+
const shouldRun = Boolean(process.env.E2B_API_KEY);
6+
const timeoutMs = Number.parseInt(process.env.SANDBOX_TEST_TIMEOUT_MS || "", 10) || 300_000;
7+
8+
const testFn = shouldRun ? it : it.skip;
9+
10+
describe("e2b example", () => {
11+
testFn(
12+
"starts sandbox-agent and responds to /v1/health",
13+
async () => {
14+
const { baseUrl, token, cleanup } = await setupE2BSandboxAgent();
15+
try {
16+
const response = await fetch(`${baseUrl}/v1/health`, {
17+
headers: buildHeaders({ token }),
18+
});
19+
expect(response.ok).toBe(true);
20+
const data = await response.json();
21+
expect(data.status).toBe("ok");
22+
} finally {
23+
await cleanup();
24+
}
25+
},
26+
timeoutMs
27+
);
28+
});

examples/e2b/e2b.ts

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
import { Sandbox } from "@e2b/code-interpreter";
2+
import { pathToFileURL } from "node:url";
3+
import {
4+
ensureUrl,
5+
runPrompt,
6+
waitForHealth,
7+
} from "../shared/sandbox-agent-client.ts";
8+
9+
const INSTALL_SCRIPT = "curl -fsSL https://releases.rivet.dev/sandbox-agent/latest/install.sh | sh";
10+
const DEFAULT_PORT = 2468;
11+
12+
type CommandRunner = (command: string, options?: Record<string, unknown>) => Promise<unknown>;
13+
14+
function resolveCommandRunner(sandbox: Sandbox): CommandRunner {
15+
if (sandbox.commands?.run) {
16+
return sandbox.commands.run.bind(sandbox.commands);
17+
}
18+
if (sandbox.commands?.exec) {
19+
return sandbox.commands.exec.bind(sandbox.commands);
20+
}
21+
throw new Error("E2B SDK does not expose commands.run or commands.exec");
22+
}
23+
24+
export async function setupE2BSandboxAgent(): Promise<{
25+
baseUrl: string;
26+
token: string;
27+
cleanup: () => Promise<void>;
28+
}> {
29+
const token = process.env.SANDBOX_TOKEN || "";
30+
const port = Number.parseInt(process.env.SANDBOX_PORT || "", 10) || DEFAULT_PORT;
31+
32+
const sandbox = await Sandbox.create({
33+
allowInternetAccess: true,
34+
envs: token ? { SANDBOX_TOKEN: token } : undefined,
35+
});
36+
37+
const runCommand = resolveCommandRunner(sandbox);
38+
39+
await runCommand(`bash -lc "${INSTALL_SCRIPT}"`);
40+
const tokenFlag = token ? "--token $SANDBOX_TOKEN" : "--no-token";
41+
await runCommand(`bash -lc "sandbox-agent server ${tokenFlag} --host 0.0.0.0 --port ${port}"`, {
42+
background: true,
43+
envs: token ? { SANDBOX_TOKEN: token } : undefined,
44+
});
45+
46+
const baseUrl = ensureUrl(sandbox.getHost(port));
47+
await waitForHealth({ baseUrl, token });
48+
49+
const cleanup = async () => {
50+
try {
51+
await sandbox.kill();
52+
} catch {
53+
// ignore cleanup errors
54+
}
55+
};
56+
57+
return {
58+
baseUrl,
59+
token,
60+
cleanup,
61+
};
62+
}
63+
64+
async function main(): Promise<void> {
65+
const { baseUrl, token, cleanup } = await setupE2BSandboxAgent();
66+
67+
const exitHandler = async () => {
68+
await cleanup();
69+
process.exit(0);
70+
};
71+
72+
process.on("SIGINT", () => {
73+
void exitHandler();
74+
});
75+
process.on("SIGTERM", () => {
76+
void exitHandler();
77+
});
78+
79+
await runPrompt({ baseUrl, token });
80+
}
81+
82+
if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
83+
main().catch((error) => {
84+
console.error(error);
85+
process.exit(1);
86+
});
87+
}

0 commit comments

Comments
 (0)