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 cors from "cors";
2import express from "express";
3import morgan from "morgan";
4import { consola } from "consola";
5import bsky from "bsky";
6import { contextMiddleware, ctx } from "context";
7import { createServer } from "lexicon";
8import chalk from "chalk";
9import API from "./xrpc";
10import ssh, { attachWebSocket as attachSshWebSocket } from "./ssh";
11import tty, { attachWebSocket as attachTtyWebSocket } from "./tty";
12import pty, { attachWebSocket as attachPtyWebSocket } from "./pty";
13import { createRateLimiter } from "./ratelimiter";
14import { createServer as createHttpServer } from "node:http";
15import type { IncomingMessage } from "node:http";
16import type { Duplex } from "node:stream";
17
18let xrpcServer = createServer({
19 validateResponse: false,
20 payload: {
21 jsonLimit: 100 * 1024, // 100kb
22 textLimit: 100 * 1024, // 100kb
23 blobLimit: 5 * 1024 * 1024, // 5mb
24 },
25});
26
27xrpcServer = API(xrpcServer, ctx);
28
29const app = express();
30
31app.use(contextMiddleware);
32app.use(cors());
33app.use(morgan("dev"));
34app.use(createRateLimiter({ windowMs: 30_000, max: 500 }));
35
36const banner = `
37 ___ __ __
38 / _ \\___ ____/ /_____ / /____ ___ _ __
39 / ___/ _ \\/ __/ '_/ -_) __/ -_) _ \\ |/ /
40 /_/ \\___/\\__/_/\\_\\__/\\__/\\__/_/ /_/___/
41
42 `;
43
44app.get("/", (req, res) => {
45 const accept = req.headers.accept || "";
46 const wantsHTML = accept.includes("text/html");
47
48 if (wantsHTML) {
49 res.contentType("text/html");
50 res.send(`<pre>${banner}</pre>`);
51 return;
52 }
53 res.contentType("text/plain");
54 res.send(banner);
55});
56
57app.use(bsky);
58app.use(xrpcServer.xrpc.router);
59app.use("/ssh", ssh);
60app.use("/tty", tty);
61app.use("/pty", pty);
62
63const httpServer = createHttpServer(app);
64
65const wsHandlers = [
66 attachPtyWebSocket("/pty"),
67 attachTtyWebSocket("/tty"),
68 attachSshWebSocket("/ssh"),
69];
70
71httpServer.on(
72 "upgrade",
73 (req: IncomingMessage, socket: Duplex, head: Buffer) => {
74 const pathname = new URL(req.url ?? "", "http://localhost").pathname;
75 for (const { wss, pathRegex } of wsHandlers) {
76 const match = pathname.match(pathRegex);
77 if (match) {
78 wss.handleUpgrade(req, socket, head, (ws) => {
79 wss.emit("connection", ws, req, match[1]!);
80 });
81 return;
82 }
83 }
84 // No handler matched — reject cleanly
85 socket.write("HTTP/1.1 404 Not Found\r\nConnection: close\r\n\r\n");
86 socket.destroy();
87 },
88);
89
90httpServer.listen(process.env.POCKETENV_XPRC_PORT || 8789, () => {
91 consola.log(chalk.greenBright(banner));
92 consola.info(
93 `Pocketenv XRPC API is running on port ${process.env.POCKETENV_XPRC_PORT || 8789}`,
94 );
95});