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 consola from "consola";
2import dayjs from "dayjs";
3import relativeTime from "dayjs/plugin/relativeTime";
4import { Sandbox } from "@pocketenv/sdk";
5import CliTable3 from "cli-table3";
6import { c } from "../theme";
7import { editor } from "@inquirer/prompts";
8import fs from "fs/promises";
9import path from "path";
10import { configureSdk } from "../lib/sdk";
11import { client } from "../client";
12import getAccessToken from "../lib/getAccessToken";
13import { env } from "../lib/env";
14
15dayjs.extend(relativeTime);
16
17export async function putFile(
18 sandboxName: string,
19 remotePath: string,
20 localPath?: string,
21) {
22 let content: string;
23 if (!process.stdin.isTTY) {
24 const chunks: Buffer[] = [];
25 for await (const chunk of process.stdin) chunks.push(chunk);
26 content = Buffer.concat(chunks).toString().trim();
27 } else if (localPath) {
28 const resolvedPath = path.resolve(localPath);
29 try {
30 await fs.access(resolvedPath);
31 } catch (err) {
32 consola.error(`No such file: ${c.error(localPath)}`);
33 process.exit(1);
34 }
35 content = await fs.readFile(resolvedPath, "utf-8");
36 } else {
37 content = (
38 await editor({
39 message: "File content (opens in $EDITOR):",
40 waitForUserInput: false,
41 })
42 ).trim();
43 }
44
45 await configureSdk();
46
47 try {
48 const sandbox = await Sandbox.get(sandboxName);
49 await sandbox.file.write(remotePath, content);
50 consola.success(
51 `File ${c.primary(remotePath)} successfully created in sandbox ${c.primary(sandboxName)}`,
52 );
53 } catch (error) {
54 consola.error(`Failed to create file: ${error}`);
55 }
56}
57
58export async function listFiles(sandboxName: string) {
59 await configureSdk();
60
61 const sandbox = await Sandbox.get(sandboxName);
62 const { files } = await sandbox.file.list();
63
64 const table = new CliTable3({
65 head: [c.primary("ID"), c.primary("PATH"), c.primary("CREATED AT")],
66 chars: {
67 top: "",
68 "top-mid": "",
69 "top-left": "",
70 "top-right": "",
71 bottom: "",
72 "bottom-mid": "",
73 "bottom-left": "",
74 "bottom-right": "",
75 left: "",
76 "left-mid": "",
77 mid: "",
78 "mid-mid": "",
79 right: "",
80 "right-mid": "",
81 middle: " ",
82 },
83 style: {
84 border: [],
85 head: [],
86 },
87 });
88
89 for (const file of files) {
90 table.push([
91 c.secondary(file.id),
92 file.path,
93 dayjs(file.createdAt).fromNow(),
94 ]);
95 }
96
97 consola.log(table.toString());
98}
99
100export async function deleteFile(id: string) {
101 const token = await getAccessToken();
102
103 try {
104 await client.post(`/xrpc/io.pocketenv.file.deleteFile`, undefined, {
105 params: { id },
106 headers: {
107 Authorization: `Bearer ${env.POCKETENV_TOKEN || token}`,
108 },
109 });
110 consola.success(`File ${c.primary(id)} successfully deleted from sandbox`);
111 } catch (error) {
112 consola.error(`Failed to delete file: ${error}`);
113 }
114}