the universal sandbox runtime for agents and humans.
pocketenv.io
sandbox
openclaw
agent
claude-code
vercel-sandbox
deno-sandbox
cloudflare-sandbox
atproto
sprites
daytona
1import ora from "ora";
2import { c } from "../theme";
3import consola from "consola";
4import { Sandbox } from "@pocketenv/sdk";
5import { configureSdk } from "../lib/sdk";
6
7async function copy(source: string, destination: string) {
8 const spinner = ora(
9 `Copying files from ${c.primary(source)} to ${c.primary(destination)}...`,
10 ).start();
11
12 if (source === destination) {
13 consola.error("Source and destination cannot be the same.");
14 process.exit(1);
15 }
16
17 const controller = new AbortController();
18 const { signal } = controller;
19
20 const onInterrupt = async () => {
21 controller.abort();
22 spinner.stop();
23 process.exit(130);
24 };
25
26 process.once("SIGINT", onInterrupt);
27
28 await configureSdk();
29
30 try {
31 if (!source.includes(":/") && destination.includes(":/")) {
32 await localToSandbox(source, destination, signal);
33 } else if (source.includes(":/") && !destination.includes(":/")) {
34 await sandboxToLocal(source, destination, signal);
35 } else if (source.includes(":/") && destination.includes(":/")) {
36 await sandboxToSandbox(source, destination, signal);
37 } else {
38 consola.error("Both source and destination cannot be local paths.");
39 process.exit(1);
40 }
41
42 spinner.stopAndPersist({
43 text: `Copied files from ${c.primary(source)} to ${c.primary(destination)}`,
44 });
45 } finally {
46 process.off("SIGINT", onInterrupt);
47 }
48}
49
50async function localToSandbox(
51 source: string,
52 destination: string,
53 signal: AbortSignal,
54) {
55 const sandboxId = destination.split(":/")[0]!;
56 const sandbox = await Sandbox.get(sandboxId);
57
58 if (sandbox.data.status !== "RUNNING") {
59 consola.error(`Sandbox ${c.primary(sandboxId)} is not running.`);
60 process.exit(1);
61 }
62
63 await sandbox.copy.upload(source, destination.split(":")[1]!, { signal });
64}
65
66async function sandboxToLocal(
67 source: string,
68 destination: string,
69 signal: AbortSignal,
70) {
71 const sandboxId = source.split(":/")[0]!;
72 const sandbox = await Sandbox.get(sandboxId);
73
74 if (sandbox.data.status !== "RUNNING") {
75 consola.error(`Sandbox ${c.primary(sandboxId)} is not running.`);
76 process.exit(1);
77 }
78
79 await sandbox.copy.download(source.split(":")[1]!, destination, { signal });
80}
81
82async function sandboxToSandbox(
83 source: string,
84 destination: string,
85 signal: AbortSignal,
86) {
87 const sourceSandboxId = source.split(":/")[0]!;
88 const destinationSandboxId = destination.split(":/")[0]!;
89
90 const [sourceSandbox, destinationSandbox] = await Promise.all([
91 Sandbox.get(sourceSandboxId),
92 Sandbox.get(destinationSandboxId),
93 ]);
94
95 if (sourceSandbox.data.status !== "RUNNING") {
96 consola.error(
97 `Source Sandbox ${c.primary(sourceSandboxId)} is not running.`,
98 );
99 process.exit(1);
100 }
101
102 if (destinationSandbox.data.status !== "RUNNING") {
103 consola.error(
104 `Destination Sandbox ${c.primary(destinationSandboxId)} is not running.`,
105 );
106 process.exit(1);
107 }
108
109 await sourceSandbox.copy.to(
110 destinationSandboxId,
111 source.split(":")[1]!,
112 destination.split(":")[1]!,
113 { signal },
114 );
115}
116
117export default copy;