@pocketenv/cli 0.2.5 → 0.3.1
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/dist/index.js +486 -61
- package/package.json +1 -1
- package/src/cmd/create.ts +9 -6
- package/src/cmd/env.ts +8 -8
- package/src/cmd/exec.ts +53 -0
- package/src/cmd/expose.ts +38 -0
- package/src/cmd/file.ts +139 -0
- package/src/cmd/list.ts +7 -7
- package/src/cmd/ports.ts +63 -0
- package/src/cmd/secret.ts +34 -19
- package/src/cmd/start.ts +16 -7
- package/src/cmd/tailscale.ts +6 -6
- package/src/cmd/unexpose.ts +31 -0
- package/src/cmd/volume.ts +123 -0
- package/src/cmd/vscode.ts +32 -0
- package/src/index.ts +116 -10
- package/src/theme.ts +12 -0
- package/src/types/port.ts +5 -0
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import chalk from "chalk";
|
|
2
|
+
import consola from "consola";
|
|
3
|
+
import getAccessToken from "../lib/getAccessToken";
|
|
4
|
+
import { client } from "../client";
|
|
5
|
+
import { env } from "../lib/env";
|
|
6
|
+
import type { Volume } from "../types/volume";
|
|
7
|
+
import CliTable3 from "cli-table3";
|
|
8
|
+
import { c } from "../theme";
|
|
9
|
+
import dayjs from "dayjs";
|
|
10
|
+
import relativeTime from "dayjs/plugin/relativeTime";
|
|
11
|
+
|
|
12
|
+
dayjs.extend(relativeTime);
|
|
13
|
+
|
|
14
|
+
export async function listVolumes(sandboxId: string) {
|
|
15
|
+
const token = await getAccessToken();
|
|
16
|
+
|
|
17
|
+
const response = await client.get<{ volumes: Volume[] }>(
|
|
18
|
+
"/xrpc/io.pocketenv.volume.getVolumes",
|
|
19
|
+
{
|
|
20
|
+
params: {
|
|
21
|
+
sandboxId,
|
|
22
|
+
},
|
|
23
|
+
headers: {
|
|
24
|
+
Authorization: `Bearer ${env.POCKETENV_TOKEN || token}`,
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
const table = new CliTable3({
|
|
30
|
+
head: [
|
|
31
|
+
c.primary("ID"),
|
|
32
|
+
c.primary("NAME"),
|
|
33
|
+
c.primary("PATH"),
|
|
34
|
+
c.primary("CREATED AT"),
|
|
35
|
+
],
|
|
36
|
+
chars: {
|
|
37
|
+
top: "",
|
|
38
|
+
"top-mid": "",
|
|
39
|
+
"top-left": "",
|
|
40
|
+
"top-right": "",
|
|
41
|
+
bottom: "",
|
|
42
|
+
"bottom-mid": "",
|
|
43
|
+
"bottom-left": "",
|
|
44
|
+
"bottom-right": "",
|
|
45
|
+
left: "",
|
|
46
|
+
"left-mid": "",
|
|
47
|
+
mid: "",
|
|
48
|
+
"mid-mid": "",
|
|
49
|
+
right: "",
|
|
50
|
+
"right-mid": "",
|
|
51
|
+
middle: " ",
|
|
52
|
+
},
|
|
53
|
+
style: {
|
|
54
|
+
border: [],
|
|
55
|
+
head: [],
|
|
56
|
+
},
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
for (const volume of response.data.volumes) {
|
|
60
|
+
table.push([
|
|
61
|
+
c.secondary(volume.id),
|
|
62
|
+
volume.name,
|
|
63
|
+
volume.path,
|
|
64
|
+
dayjs(volume.createdAt).fromNow(),
|
|
65
|
+
]);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
consola.log(table.toString());
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export async function createVolume(
|
|
72
|
+
sandbox: string,
|
|
73
|
+
name: string,
|
|
74
|
+
path: string,
|
|
75
|
+
) {
|
|
76
|
+
const token = await getAccessToken();
|
|
77
|
+
|
|
78
|
+
try {
|
|
79
|
+
await client.post(
|
|
80
|
+
"/xrpc/io.pocketenv.volume.addVolume",
|
|
81
|
+
{
|
|
82
|
+
volume: {
|
|
83
|
+
sandboxId: sandbox,
|
|
84
|
+
name,
|
|
85
|
+
path,
|
|
86
|
+
},
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
headers: {
|
|
90
|
+
Authorization: `Bearer ${env.POCKETENV_TOKEN || token}`,
|
|
91
|
+
},
|
|
92
|
+
},
|
|
93
|
+
);
|
|
94
|
+
|
|
95
|
+
consola.success(
|
|
96
|
+
`Volume ${chalk.rgb(0, 232, 198)(name)} successfully mounted in sandbox ${chalk.rgb(0, 232, 198)(sandbox)} at path ${chalk.rgb(0, 232, 198)(path)}`,
|
|
97
|
+
);
|
|
98
|
+
} catch (error) {
|
|
99
|
+
consola.error("Failed to create volume:", error);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export async function deleteVolume(id: string) {
|
|
104
|
+
const token = await getAccessToken();
|
|
105
|
+
|
|
106
|
+
try {
|
|
107
|
+
await client.post(`/xrpc/io.pocketenv.volume.deleteVolume`, undefined, {
|
|
108
|
+
params: {
|
|
109
|
+
id,
|
|
110
|
+
},
|
|
111
|
+
headers: {
|
|
112
|
+
Authorization: `Bearer ${env.POCKETENV_TOKEN || token}`,
|
|
113
|
+
},
|
|
114
|
+
});
|
|
115
|
+
} catch (error) {
|
|
116
|
+
consola.error(`Failed to delete volume: ${error}`);
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
consola.success(
|
|
121
|
+
`Volume ${chalk.rgb(0, 232, 198)(id)} successfully deleted from sandbox`,
|
|
122
|
+
);
|
|
123
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import consola from "consola";
|
|
2
|
+
import getAccessToken from "../lib/getAccessToken";
|
|
3
|
+
import { client } from "../client";
|
|
4
|
+
import { env } from "../lib/env";
|
|
5
|
+
import { c } from "../theme";
|
|
6
|
+
|
|
7
|
+
export async function exposeVscode(sandbox: string) {
|
|
8
|
+
const token = await getAccessToken();
|
|
9
|
+
try {
|
|
10
|
+
const response = await client.post<{ previewUrl?: string }>(
|
|
11
|
+
`/xrpc/io.pocketenv.sandbox.exposeVscode`,
|
|
12
|
+
undefined,
|
|
13
|
+
{
|
|
14
|
+
params: {
|
|
15
|
+
id: sandbox,
|
|
16
|
+
},
|
|
17
|
+
headers: {
|
|
18
|
+
Authorization: `Bearer ${env.POCKETENV_TOKEN || token}`,
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
consola.success(`VS Code Server exposed for sandbox ${c.primary(sandbox)}`);
|
|
24
|
+
|
|
25
|
+
if (response.data.previewUrl) {
|
|
26
|
+
consola.success(`Preview URL: ${c.secondary(response.data.previewUrl)}`);
|
|
27
|
+
}
|
|
28
|
+
} catch (error) {
|
|
29
|
+
consola.error("Failed to expose VS Code:", error);
|
|
30
|
+
process.exit(1);
|
|
31
|
+
}
|
|
32
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -14,16 +14,15 @@ import { deleteSecret, listSecrets, putSecret } from "./cmd/secret";
|
|
|
14
14
|
import { deleteEnv, listEnvs, putEnv } from "./cmd/env";
|
|
15
15
|
import { getSshKey, putKeys } from "./cmd/sshkeys";
|
|
16
16
|
import { getTailscaleAuthKey, putAuthKey } from "./cmd/tailscale";
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
};
|
|
17
|
+
import { exposePort } from "./cmd/expose";
|
|
18
|
+
import { unexposePort } from "./cmd/unexpose";
|
|
19
|
+
import { createVolume, deleteVolume, listVolumes } from "./cmd/volume";
|
|
20
|
+
import { deleteFile, listFiles, putFile } from "./cmd/file";
|
|
21
|
+
import consola from "consola";
|
|
22
|
+
import { listPorts } from "./cmd/ports";
|
|
23
|
+
import { c } from "./theme";
|
|
24
|
+
import { exposeVscode } from "./cmd/vscode";
|
|
25
|
+
import { exec } from "./cmd/exec";
|
|
27
26
|
|
|
28
27
|
const program = new Command();
|
|
29
28
|
|
|
@@ -78,6 +77,10 @@ program
|
|
|
78
77
|
.command("start")
|
|
79
78
|
.argument("<sandbox>", "the sandbox to start")
|
|
80
79
|
.option("--ssh, -s", "connect to the Sandbox and automatically open a shell")
|
|
80
|
+
.option(
|
|
81
|
+
"--repo, -r <repo>",
|
|
82
|
+
"the repository to clone into the sandbox (e.g., github:user/repo, tangled:user/repo, or a Git URL)",
|
|
83
|
+
)
|
|
81
84
|
.description("start the given sandbox")
|
|
82
85
|
.action(start);
|
|
83
86
|
|
|
@@ -96,6 +99,10 @@ program
|
|
|
96
99
|
"the base sandbox to use for the sandbox, e.g. openclaw, claude-code, codex, copilot ...",
|
|
97
100
|
)
|
|
98
101
|
.option("--ssh, -s", "connect to the Sandbox and automatically open a shell")
|
|
102
|
+
.option(
|
|
103
|
+
"--repo, -r <repo>",
|
|
104
|
+
"the repository to clone into the sandbox (e.g., github:user/repo, tangled:user/repo, or a Git URL)",
|
|
105
|
+
)
|
|
99
106
|
.argument("[name]", "the name of the sandbox to create")
|
|
100
107
|
.description("create a new sandbox")
|
|
101
108
|
.action(createSandbox);
|
|
@@ -112,6 +119,105 @@ program
|
|
|
112
119
|
.description("delete the given sandbox")
|
|
113
120
|
.action(deleteSandbox);
|
|
114
121
|
|
|
122
|
+
program
|
|
123
|
+
.command("vscode")
|
|
124
|
+
.aliases(["code", "code-server"])
|
|
125
|
+
.argument("<sandbox>", "the sandbox to expose VS Code for")
|
|
126
|
+
.description("expose a visual code server to the internet")
|
|
127
|
+
.action(exposeVscode);
|
|
128
|
+
|
|
129
|
+
program
|
|
130
|
+
.enablePositionalOptions()
|
|
131
|
+
.command("exec")
|
|
132
|
+
.argument("<sandbox>", "the sandbox to execute the command in")
|
|
133
|
+
.argument("<command...>", "the command to execute")
|
|
134
|
+
.description("execute a command in the given sandbox")
|
|
135
|
+
.passThroughOptions()
|
|
136
|
+
.action(exec);
|
|
137
|
+
|
|
138
|
+
program
|
|
139
|
+
.command("expose")
|
|
140
|
+
.argument("<sandbox>", "the sandbox to expose a port for")
|
|
141
|
+
.argument("<port>", "the port to expose", (val) => {
|
|
142
|
+
const port = parseInt(val, 10);
|
|
143
|
+
if (isNaN(port)) {
|
|
144
|
+
consola.error(`port must be a number, got: ${val}`);
|
|
145
|
+
process.exit(1);
|
|
146
|
+
}
|
|
147
|
+
return port;
|
|
148
|
+
})
|
|
149
|
+
.argument("[description]", "an optional description for the exposed port")
|
|
150
|
+
.description("expose a port from the given sandbox to the internet")
|
|
151
|
+
.action(exposePort);
|
|
152
|
+
|
|
153
|
+
program
|
|
154
|
+
.command("unexpose")
|
|
155
|
+
.argument("<sandbox>", "the sandbox to unexpose a port for")
|
|
156
|
+
.argument("<port>", "the port to unexpose", (val) => {
|
|
157
|
+
const port = parseInt(val, 10);
|
|
158
|
+
if (isNaN(port)) {
|
|
159
|
+
consola.error(`port must be a number, got: ${val}`);
|
|
160
|
+
process.exit(1);
|
|
161
|
+
}
|
|
162
|
+
return port;
|
|
163
|
+
})
|
|
164
|
+
.description("unexpose a port from the given sandbox")
|
|
165
|
+
.action(unexposePort);
|
|
166
|
+
|
|
167
|
+
const volume = program.command("volume").description("manage volumes");
|
|
168
|
+
|
|
169
|
+
volume
|
|
170
|
+
.command("put")
|
|
171
|
+
.argument("<sandbox>", "the sandbox to put the volume in")
|
|
172
|
+
.argument("<name>", "the name of the volume")
|
|
173
|
+
.argument("<path>", "the path to mount the volume at")
|
|
174
|
+
.description("put a volume in the given sandbox")
|
|
175
|
+
.action(createVolume);
|
|
176
|
+
|
|
177
|
+
volume
|
|
178
|
+
.command("list")
|
|
179
|
+
.aliases(["ls"])
|
|
180
|
+
.argument("<sandbox>", "the sandbox to list volumes for")
|
|
181
|
+
.description("list volumes in the given sandbox")
|
|
182
|
+
.action(listVolumes);
|
|
183
|
+
|
|
184
|
+
volume
|
|
185
|
+
.command("delete")
|
|
186
|
+
.aliases(["rm", "remove"])
|
|
187
|
+
.argument("<id>", "the ID of the volume to delete")
|
|
188
|
+
.description("delete a volume")
|
|
189
|
+
.action(deleteVolume);
|
|
190
|
+
|
|
191
|
+
const file = program.command("file").description("manage files");
|
|
192
|
+
|
|
193
|
+
file
|
|
194
|
+
.command("put")
|
|
195
|
+
.argument("<sandbox>", "the sandbox to put the file in")
|
|
196
|
+
.argument("<path>", "the remote path to upload the file to")
|
|
197
|
+
.argument("[localPath]", "the local path of the file to upload")
|
|
198
|
+
.description("upload a file to the given sandbox")
|
|
199
|
+
.action(putFile);
|
|
200
|
+
|
|
201
|
+
file
|
|
202
|
+
.command("list")
|
|
203
|
+
.aliases(["ls"])
|
|
204
|
+
.argument("<sandbox>", "the sandbox to list files for")
|
|
205
|
+
.description("list files in the given sandbox")
|
|
206
|
+
.action(listFiles);
|
|
207
|
+
|
|
208
|
+
file
|
|
209
|
+
.command("delete")
|
|
210
|
+
.aliases(["rm", "remove"])
|
|
211
|
+
.argument("<id>", "the ID of the file to delete")
|
|
212
|
+
.description("delete a file")
|
|
213
|
+
.action(deleteFile);
|
|
214
|
+
|
|
215
|
+
program
|
|
216
|
+
.command("ports")
|
|
217
|
+
.argument("<sandbox>", "the sandbox to list exposed ports for")
|
|
218
|
+
.description("list exposed ports for a sandbox")
|
|
219
|
+
.action(listPorts);
|
|
220
|
+
|
|
115
221
|
const secret = program.command("secret").description("manage secrets");
|
|
116
222
|
|
|
117
223
|
secret
|
package/src/theme.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import chalk from "chalk";
|
|
2
|
+
|
|
3
|
+
export const c = {
|
|
4
|
+
primary: (s: string | number) => chalk.rgb(0, 232, 198)(s),
|
|
5
|
+
secondary: (s: string | number) => chalk.rgb(0, 198, 232)(s),
|
|
6
|
+
accent: (s: string | number) => chalk.rgb(130, 100, 255)(s),
|
|
7
|
+
highlight: (s: string | number) => chalk.rgb(100, 232, 130)(s),
|
|
8
|
+
muted: (s: string | number) => chalk.rgb(200, 210, 220)(s),
|
|
9
|
+
link: (s: string | number) => chalk.rgb(255, 160, 100)(s),
|
|
10
|
+
sky: (s: string | number) => chalk.rgb(0, 210, 255)(s),
|
|
11
|
+
error: (s: string | number) => chalk.rgb(255, 100, 100)(s),
|
|
12
|
+
};
|