@pocketenv/cli 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json ADDED
@@ -0,0 +1,57 @@
1
+ {
2
+ "name": "@pocketenv/cli",
3
+ "main": "dist/index.js",
4
+ "bin": {
5
+ "pocketenv": "dist/index.js"
6
+ },
7
+ "version": "0.2.0",
8
+ "type": "module",
9
+ "keywords": [
10
+ "sandbox",
11
+ "atproto",
12
+ "bluesky",
13
+ "cli",
14
+ "agent",
15
+ "openclaw",
16
+ "microvm"
17
+ ],
18
+ "author": "Tsiry Sandratraina <tsiry.sndr@pocketenv.io>",
19
+ "license": "MPL-2.0",
20
+ "repository": {
21
+ "type": "git",
22
+ "url": "https://github.com/pocketenv-io/pocketenv"
23
+ },
24
+ "scripts": {
25
+ "dev": "tsx ./src/index.ts",
26
+ "build": "pkgroll && chmod +x ./dist/index.js"
27
+ },
28
+ "peerDependencies": {
29
+ "typescript": "^5"
30
+ },
31
+ "devDependencies": {
32
+ "@types/cors": "^2.8.19",
33
+ "@types/eventsource": "^1.1.15",
34
+ "@types/express": "^5.0.6",
35
+ "@types/ws": "^8.18.1",
36
+ "pkgroll": "^2.27.0",
37
+ "tsx": "^4.21.0"
38
+ },
39
+ "dependencies": {
40
+ "@inquirer/prompts": "^8.3.0",
41
+ "axios": "^1.13.6",
42
+ "chalk": "^5.6.2",
43
+ "cli-table3": "^0.6.5",
44
+ "commander": "^14.0.3",
45
+ "consola": "^3.4.2",
46
+ "cors": "^2.8.6",
47
+ "dayjs": "^1.11.20",
48
+ "effect": "^3.19.19",
49
+ "envalid": "^8.1.1",
50
+ "eventsource": "^4.1.0",
51
+ "express": "^5.2.1",
52
+ "libsodium-wrappers": "^0.8.2",
53
+ "open": "^11.0.0",
54
+ "ws": "^8.19.0",
55
+ "zod": "^4.3.6"
56
+ }
57
+ }
package/src/client.ts ADDED
@@ -0,0 +1,6 @@
1
+ import axios from "axios";
2
+ import { env } from "./lib/env";
3
+
4
+ export const client = axios.create({
5
+ baseURL: env.POCKETENV_API_URL,
6
+ });
@@ -0,0 +1,46 @@
1
+ import consola from "consola";
2
+ import { client } from "../client";
3
+ import getAccessToken from "../lib/getAccessToken";
4
+ import type { Sandbox } from "../types/sandbox";
5
+ import chalk from "chalk";
6
+ import connectToSandbox from "./ssh";
7
+
8
+ async function createSandbox(
9
+ name: string,
10
+ { provider, ssh }: { provider: string | undefined; ssh: boolean | undefined },
11
+ ) {
12
+ const token = await getAccessToken();
13
+
14
+ if (["deno", "vercel", "daytona"].includes(provider || "")) {
15
+ consola.error(
16
+ `This Sandbox Runtime is temporarily disabled. ${chalk.greenBright(provider ?? "")}`,
17
+ );
18
+ process.exit(1);
19
+ }
20
+ try {
21
+ const sandbox = await client.post<Sandbox>(
22
+ "/xrpc/io.pocketenv.sandbox.createSandbox",
23
+ {
24
+ name,
25
+ base: "at://did:plc:aturpi2ls3yvsmhc6wybomun/io.pocketenv.sandbox/openclaw",
26
+ provider: provider ?? "cloudflare",
27
+ },
28
+ {
29
+ headers: {
30
+ Authorization: `Bearer ${token}`,
31
+ },
32
+ },
33
+ );
34
+ if (!ssh) {
35
+ consola.success(
36
+ `Sandbox created successfully: ${chalk.greenBright(sandbox.data.name)}`,
37
+ );
38
+ return;
39
+ }
40
+ await connectToSandbox(sandbox.data.name);
41
+ } catch (error) {
42
+ consola.error(`Failed to create sandbox: ${error}`);
43
+ }
44
+ }
45
+
46
+ export default createSandbox;
package/src/cmd/env.ts ADDED
@@ -0,0 +1,126 @@
1
+ import { client } from "../client";
2
+ import getAccessToken from "../lib/getAccessToken";
3
+ import type { Sandbox } from "../types/sandbox";
4
+ import type { Variable } from "../types/variable";
5
+ import chalk from "chalk";
6
+ import dayjs from "dayjs";
7
+ import consola from "consola";
8
+ import Table from "cli-table3";
9
+ import { env } from "../lib/env";
10
+
11
+ export async function listEnvs(sandbox: string) {
12
+ const token = await getAccessToken();
13
+ const { data } = await client.get<{ sandbox: Sandbox }>(
14
+ "/xrpc/io.pocketenv.sandbox.getSandbox",
15
+ {
16
+ params: {
17
+ id: sandbox,
18
+ },
19
+ headers: {
20
+ Authorization: `Bearer ${token}`,
21
+ },
22
+ },
23
+ );
24
+
25
+ if (!data.sandbox) {
26
+ consola.error(`Sandbox not found: ${chalk.greenBright(sandbox)}`);
27
+ process.exit(1);
28
+ }
29
+
30
+ const response = await client.get<{ variables: Variable[] }>(
31
+ "/xrpc/io.pocketenv.variable.getVariables",
32
+ {
33
+ params: {
34
+ sandboxId: data.sandbox.id,
35
+ offset: 0,
36
+ limit: 100,
37
+ },
38
+ headers: {
39
+ Authorization: `Bearer ${env.POCKETENV_TOKEN || token}`,
40
+ },
41
+ },
42
+ );
43
+
44
+ const table = new Table({
45
+ head: [
46
+ chalk.cyan("ID"),
47
+ chalk.cyan("NAME"),
48
+ chalk.cyan("VALUE"),
49
+ chalk.cyan("CREATED AT"),
50
+ ],
51
+ chars: {
52
+ top: "",
53
+ "top-mid": "",
54
+ "top-left": "",
55
+ "top-right": "",
56
+ bottom: "",
57
+ "bottom-mid": "",
58
+ "bottom-left": "",
59
+ "bottom-right": "",
60
+ left: "",
61
+ "left-mid": "",
62
+ mid: "",
63
+ "mid-mid": "",
64
+ right: "",
65
+ "right-mid": "",
66
+ middle: " ",
67
+ },
68
+ style: {
69
+ border: [],
70
+ head: [],
71
+ },
72
+ });
73
+
74
+ for (const variable of response.data.variables) {
75
+ table.push([
76
+ chalk.greenBright(variable.id),
77
+ chalk.greenBright(variable.name),
78
+ variable.value,
79
+ dayjs(variable.createdAt).fromNow(),
80
+ ]);
81
+ }
82
+
83
+ consola.log(table.toString());
84
+ }
85
+
86
+ export async function putEnv(sandbox: string, key: string, value: string) {
87
+ const token = await getAccessToken();
88
+ const { data } = await client.get<{ sandbox: Sandbox }>(
89
+ "/xrpc/io.pocketenv.sandbox.getSandbox",
90
+ {
91
+ params: {
92
+ id: sandbox,
93
+ },
94
+ headers: {
95
+ Authorization: `Bearer ${env.POCKETENV_TOKEN || token}`,
96
+ },
97
+ },
98
+ );
99
+
100
+ await client.post(
101
+ "/xrpc/io.pocketenv.variable.addVariable",
102
+ {
103
+ variable: { sandboxId: data.sandbox.id, name: key, value: value },
104
+ },
105
+ {
106
+ headers: {
107
+ Authorization: `Bearer ${env.POCKETENV_TOKEN || token}`,
108
+ },
109
+ },
110
+ );
111
+
112
+ consola.success("Variable updated successfully");
113
+ }
114
+
115
+ export async function deleteEnv(id: string) {
116
+ const token = await getAccessToken();
117
+
118
+ await client.post("/xrpc/io.pocketenv.variable.deleteVariable", undefined, {
119
+ params: { id },
120
+ headers: {
121
+ Authorization: `Bearer ${env.POCKETENV_TOKEN || token}`,
122
+ },
123
+ });
124
+
125
+ consola.success("Variable deleted successfully");
126
+ }
@@ -0,0 +1,80 @@
1
+ import { client } from "../client";
2
+ import chalk from "chalk";
3
+ import consola from "consola";
4
+ import { env } from "../lib/env";
5
+ import getAccessToken from "../lib/getAccessToken";
6
+ import type { Sandbox } from "../types/sandbox";
7
+ import Table from "cli-table3";
8
+ import dayjs from "dayjs";
9
+ import relativeTime from "dayjs/plugin/relativeTime";
10
+ import type { Profile } from "../types/profile";
11
+ dayjs.extend(relativeTime);
12
+
13
+ async function listSandboxes() {
14
+ const token = await getAccessToken();
15
+
16
+ const profile = await client.get<Profile>(
17
+ "/xrpc/io.pocketenv.actor.getProfile",
18
+ {
19
+ headers: {
20
+ Authorization: `Bearer ${env.POCKETENV_TOKEN || token}`,
21
+ },
22
+ },
23
+ );
24
+
25
+ const response = await client.get<{ sandboxes: Sandbox[] }>(
26
+ "/xrpc/io.pocketenv.actor.getActorSandboxes",
27
+ {
28
+ params: {
29
+ did: profile.data.did,
30
+ offset: 0,
31
+ limit: 100,
32
+ },
33
+ },
34
+ );
35
+
36
+ const table = new Table({
37
+ head: [
38
+ chalk.cyan("NAME"),
39
+ chalk.cyan("BASE"),
40
+ chalk.cyan("STATUS"),
41
+ chalk.cyan("CREATED AT"),
42
+ ],
43
+ chars: {
44
+ top: "",
45
+ "top-mid": "",
46
+ "top-left": "",
47
+ "top-right": "",
48
+ bottom: "",
49
+ "bottom-mid": "",
50
+ "bottom-left": "",
51
+ "bottom-right": "",
52
+ left: "",
53
+ "left-mid": "",
54
+ mid: "",
55
+ "mid-mid": "",
56
+ right: "",
57
+ "right-mid": "",
58
+ middle: " ",
59
+ },
60
+ style: {
61
+ border: [],
62
+ head: [],
63
+ },
64
+ });
65
+
66
+ for (const sandbox of response.data.sandboxes) {
67
+ table.push([
68
+ chalk.greenBright(sandbox.name),
69
+ sandbox.baseSandbox,
70
+ sandbox.status === "RUNNING"
71
+ ? chalk.greenBright(sandbox.status)
72
+ : sandbox.status,
73
+ dayjs(sandbox.createdAt).fromNow(),
74
+ ]);
75
+ }
76
+
77
+ consola.log(table.toString());
78
+ }
79
+
80
+ export default listSandboxes;
@@ -0,0 +1,49 @@
1
+ import { client } from "../client";
2
+ import open from "open";
3
+ import express, { type Request, type Response } from "express";
4
+ import cors from "cors";
5
+ import fs from "node:fs/promises";
6
+ import os from "node:os";
7
+ import path from "node:path";
8
+ import chalk from "chalk";
9
+
10
+ async function login(handle: string) {
11
+ const app = express();
12
+ app.use(cors());
13
+ app.use(express.json());
14
+
15
+ const server = app.listen(6997);
16
+
17
+ app.post("/token", async (req: Request, res: Response) => {
18
+ console.log(chalk.bold(chalk.greenBright("Login successful!\n")));
19
+ const tokenPath = path.join(os.homedir(), ".pocketenv", "token.json");
20
+ await fs.mkdir(path.dirname(tokenPath), { recursive: true });
21
+ await fs.writeFile(
22
+ tokenPath,
23
+ JSON.stringify({ token: req.body.token }, null, 2),
24
+ );
25
+
26
+ res.json({
27
+ ok: 1,
28
+ });
29
+
30
+ server.close();
31
+ });
32
+
33
+ const response = await client.post(`/login`, { handle, cli: true });
34
+
35
+ const redirectUrl = response.data;
36
+
37
+ if (!redirectUrl.includes("authorize")) {
38
+ console.error("Failed to login, please check your handle and try again.");
39
+ server.close();
40
+ return;
41
+ }
42
+
43
+ console.log("Please visit this URL to authorize the app:");
44
+ console.log(chalk.cyan(redirectUrl));
45
+
46
+ await open(response.data);
47
+ }
48
+
49
+ export default login;
@@ -0,0 +1,20 @@
1
+ import os from "node:os";
2
+ import path from "node:path";
3
+ import fs from "node:fs/promises";
4
+ import consola from "consola";
5
+
6
+ async function logout() {
7
+ const tokenPath = path.join(os.homedir(), ".pocketenv", "token.json");
8
+
9
+ try {
10
+ await fs.access(tokenPath);
11
+ } catch {
12
+ consola.log("Logged out successfully");
13
+ return;
14
+ }
15
+
16
+ await fs.unlink(tokenPath);
17
+ consola.log("Logged out successfully");
18
+ }
19
+
20
+ export default logout;
package/src/cmd/rm.ts ADDED
@@ -0,0 +1,24 @@
1
+ import consola from "consola";
2
+ import { client } from "../client";
3
+ import { env } from "../lib/env";
4
+ import getAccessToken from "../lib/getAccessToken";
5
+
6
+ async function deleteSandbox(id: string) {
7
+ const token = await getAccessToken();
8
+ try {
9
+ await client.post("/xrpc/io.pocketenv.sandbox.deleteSandbox", undefined, {
10
+ params: {
11
+ id,
12
+ },
13
+ headers: {
14
+ Authorization: `Bearer ${env.POCKETENV_TOKEN || token}`,
15
+ },
16
+ });
17
+
18
+ consola.success("Sandbox deleted successfully");
19
+ } catch {
20
+ consola.error("Failed to delete sandbox");
21
+ }
22
+ }
23
+
24
+ export default deleteSandbox;
@@ -0,0 +1,127 @@
1
+ import { client } from "../client";
2
+ import getAccessToken from "../lib/getAccessToken";
3
+ import { password } from "@inquirer/prompts";
4
+ import type { Sandbox } from "../types/sandbox";
5
+ import type { Secret } from "../types/secret";
6
+ import chalk from "chalk";
7
+ import consola from "consola";
8
+ import Table from "cli-table3";
9
+ import dayjs from "dayjs";
10
+ import relativeTime from "dayjs/plugin/relativeTime";
11
+ import { env } from "../lib/env";
12
+ import encrypt from "../lib/sodium";
13
+
14
+ dayjs.extend(relativeTime);
15
+
16
+ export async function listSecrets(sandbox: string) {
17
+ const token = await getAccessToken();
18
+ const { data } = await client.get<{ sandbox: Sandbox }>(
19
+ "/xrpc/io.pocketenv.sandbox.getSandbox",
20
+ {
21
+ params: {
22
+ id: sandbox,
23
+ },
24
+ headers: {
25
+ Authorization: `Bearer ${env.POCKETENV_TOKEN || token}`,
26
+ },
27
+ },
28
+ );
29
+ const response = await client.get<{ secrets: Secret[] }>(
30
+ "/xrpc/io.pocketenv.secret.getSecrets",
31
+ {
32
+ params: {
33
+ sandboxId: data.sandbox.id,
34
+ offset: 0,
35
+ limit: 100,
36
+ },
37
+ headers: {
38
+ Authorization: `Bearer ${env.POCKETENV_TOKEN || token}`,
39
+ },
40
+ },
41
+ );
42
+
43
+ const table = new Table({
44
+ head: [chalk.cyan("ID"), chalk.cyan("NAME"), chalk.cyan("CREATED AT")],
45
+ chars: {
46
+ top: "",
47
+ "top-mid": "",
48
+ "top-left": "",
49
+ "top-right": "",
50
+ bottom: "",
51
+ "bottom-mid": "",
52
+ "bottom-left": "",
53
+ "bottom-right": "",
54
+ left: "",
55
+ "left-mid": "",
56
+ mid: "",
57
+ "mid-mid": "",
58
+ right: "",
59
+ "right-mid": "",
60
+ middle: " ",
61
+ },
62
+ style: {
63
+ border: [],
64
+ head: [],
65
+ },
66
+ });
67
+
68
+ for (const secret of response.data.secrets) {
69
+ table.push([
70
+ chalk.greenBright(secret.id),
71
+ chalk.greenBright(secret.name),
72
+ dayjs(secret.createdAt).fromNow(),
73
+ ]);
74
+ }
75
+
76
+ consola.log(table.toString());
77
+ }
78
+
79
+ export async function putSecret(sandbox: string, key: string) {
80
+ const token = await getAccessToken();
81
+ const value = await password({ message: "Enter secret value" });
82
+
83
+ const { data } = await client.get("/xrpc/io.pocketenv.sandbox.getSandbox", {
84
+ params: {
85
+ id: sandbox,
86
+ },
87
+ headers: {
88
+ Authorization: `Bearer ${env.POCKETENV_TOKEN || token}`,
89
+ },
90
+ });
91
+
92
+ if (!data.sandbox) {
93
+ consola.error(`Sandbox not found: ${chalk.greenBright(sandbox)}`);
94
+ process.exit(1);
95
+ }
96
+
97
+ await client.post(
98
+ "/xrpc/io.pocketenv.secret.addSecret",
99
+ {
100
+ secret: {
101
+ sandboxId: data.sandbox.id,
102
+ name: key,
103
+ value: await encrypt(value),
104
+ },
105
+ },
106
+ {
107
+ headers: {
108
+ Authorization: `Bearer ${env.POCKETENV_TOKEN || token}`,
109
+ },
110
+ },
111
+ );
112
+ }
113
+
114
+ export async function deleteSecret(id: string) {
115
+ const token = await getAccessToken();
116
+
117
+ await client.post("/xrpc/io.pocketenv.secret.deleteSecret", undefined, {
118
+ params: {
119
+ id,
120
+ },
121
+ headers: {
122
+ Authorization: `Bearer ${env.POCKETENV_TOKEN || token}`,
123
+ },
124
+ });
125
+
126
+ consola.success("Secret deleted successfully");
127
+ }