@pocketenv/cli 0.3.0 → 0.3.2

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 CHANGED
@@ -22,7 +22,7 @@ import relativeTime from 'dayjs/plugin/relativeTime.js';
22
22
  import { password, editor, input } from '@inquirer/prompts';
23
23
  import sodium from 'libsodium-wrappers';
24
24
 
25
- var version = "0.3.0";
25
+ var version = "0.3.2";
26
26
 
27
27
  async function getAccessToken() {
28
28
  const tokenPath = path.join(os.homedir(), ".pocketenv", "token.json");
@@ -410,17 +410,32 @@ async function ssh(sandboxName) {
410
410
  }
411
411
  }
412
412
 
413
- async function start(name, { ssh: ssh$1 }) {
413
+ function expandRepo(repo) {
414
+ const githubMatch = repo.match(/^github:([^/]+\/[^/]+)$/);
415
+ if (githubMatch) return `https://github.com/${githubMatch[1]}`;
416
+ const tangledMatch = repo.match(/^tangled:([^/]+\/[^/]+)$/);
417
+ if (tangledMatch) return `https://tangled.org/${tangledMatch[1]}`;
418
+ return repo;
419
+ }
420
+
421
+ async function start(name, { ssh: ssh$1, repo }) {
414
422
  const token = await getAccessToken();
423
+ if (repo) repo = expandRepo(repo);
415
424
  try {
416
- await client.post("/xrpc/io.pocketenv.sandbox.startSandbox", void 0, {
417
- params: {
418
- id: name
425
+ await client.post(
426
+ "/xrpc/io.pocketenv.sandbox.startSandbox",
427
+ {
428
+ repo
419
429
  },
420
- headers: {
421
- Authorization: `Bearer ${env$1.POCKETENV_TOKEN || token}`
430
+ {
431
+ params: {
432
+ id: name
433
+ },
434
+ headers: {
435
+ Authorization: `Bearer ${env$1.POCKETENV_TOKEN || token}`
436
+ }
422
437
  }
423
- });
438
+ );
424
439
  if (ssh$1) {
425
440
  await ssh(name);
426
441
  return;
@@ -572,9 +587,11 @@ async function stop(name) {
572
587
  async function createSandbox(name, {
573
588
  provider,
574
589
  ssh: ssh$1,
575
- base
590
+ base,
591
+ repo
576
592
  }) {
577
593
  const token = await getAccessToken();
594
+ if (repo) repo = expandRepo(repo);
578
595
  if (["deno", "vercel", "daytona"].includes(provider || "")) {
579
596
  consola.error(
580
597
  `This Sandbox Runtime is temporarily disabled. ${c.primary(provider ?? "")}`
@@ -587,7 +604,8 @@ async function createSandbox(name, {
587
604
  {
588
605
  name,
589
606
  base: base ?? "at://did:plc:aturpi2ls3yvsmhc6wybomun/io.pocketenv.sandbox/openclaw",
590
- provider: provider ?? "cloudflare"
607
+ provider: provider ?? "cloudflare",
608
+ repo
591
609
  },
592
610
  {
593
611
  headers: {
@@ -1516,6 +1534,43 @@ async function exposeVscode(sandbox) {
1516
1534
  }
1517
1535
  }
1518
1536
 
1537
+ async function exec(sandbox, command) {
1538
+ const token = await getAccessToken();
1539
+ try {
1540
+ const [cmd, ...args] = command;
1541
+ const response = await client.post(
1542
+ "/xrpc/io.pocketenv.sandbox.exec",
1543
+ {
1544
+ command: `${cmd} ${args.join(" ")}`
1545
+ },
1546
+ {
1547
+ params: {
1548
+ id: sandbox
1549
+ },
1550
+ headers: {
1551
+ Authorization: `Bearer ${env$1.POCKETENV_TOKEN || token}`
1552
+ }
1553
+ }
1554
+ );
1555
+ if (response.data.stdout) {
1556
+ process.stdout.write(
1557
+ response.data.stdout.endsWith("\n") ? response.data.stdout : response.data.stdout + "\n"
1558
+ );
1559
+ }
1560
+ if (response.data.stderr) {
1561
+ process.stderr.write(
1562
+ response.data.stderr.endsWith("\n") ? response.data.stderr : response.data.stderr + "\n"
1563
+ );
1564
+ }
1565
+ if (response.data.exitCode !== 0) {
1566
+ consola.error(`Command exited with code ${response.data.exitCode}`);
1567
+ }
1568
+ process.exit(response.data.exitCode);
1569
+ } catch (error) {
1570
+ consola.error("Failed to execute command:", error);
1571
+ }
1572
+ }
1573
+
1519
1574
  const program = new Command();
1520
1575
  program.name("pocketenv").description(
1521
1576
  `${chalk.bold.rgb(0, 232, 198)(`pocketenv v${version}`)} ${c.muted("\u2500")} ${c.muted("Open, interoperable sandbox platform for agents and humans")}`
@@ -1542,17 +1597,22 @@ program.command("login").argument("<handle>", "your AT Proto handle (e.g., <user
1542
1597
  program.command("whoami").description("get the current logged-in user").action(whoami);
1543
1598
  program.command("console").aliases(["shell", "ssh", "s"]).argument("[sandbox]", "the sandbox to connect to").description("open an interactive shell for the given sandbox").action(ssh);
1544
1599
  program.command("ls").description("list sandboxes").action(listSandboxes);
1545
- program.command("start").argument("<sandbox>", "the sandbox to start").option("--ssh, -s", "connect to the Sandbox and automatically open a shell").description("start the given sandbox").action(start);
1600
+ program.command("start").argument("<sandbox>", "the sandbox to start").option("--ssh, -s", "connect to the Sandbox and automatically open a shell").option(
1601
+ "--repo, -r <repo>",
1602
+ "the repository to clone into the sandbox (e.g., github:user/repo, tangled:user/repo, or a Git URL)"
1603
+ ).description("start the given sandbox").action(start);
1546
1604
  program.command("stop").argument("<sandbox>", "the sandbox to stop").description("stop the given sandbox").action(stop);
1547
1605
  program.command("create").aliases(["new"]).option("--provider, -p <provider>", "the provider to use for the sandbox").option(
1548
1606
  "--base, -b <base>",
1549
1607
  "the base sandbox to use for the sandbox, e.g. openclaw, claude-code, codex, copilot ..."
1550
- ).option("--ssh, -s", "connect to the Sandbox and automatically open a shell").argument("[name]", "the name of the sandbox to create").description("create a new sandbox").action(createSandbox);
1608
+ ).option("--ssh, -s", "connect to the Sandbox and automatically open a shell").option(
1609
+ "--repo, -r <repo>",
1610
+ "the repository to clone into the sandbox (e.g., github:user/repo, tangled:user/repo, or a Git URL)"
1611
+ ).argument("[name]", "the name of the sandbox to create").description("create a new sandbox").action(createSandbox);
1551
1612
  program.command("logout").description("logout (removes session token)").action(logout);
1552
1613
  program.command("rm").aliases(["delete", "remove"]).argument("<sandbox>", "the sandbox to delete").description("delete the given sandbox").action(deleteSandbox);
1553
- program.command("vscode").argument("<sandbox>", "the sandbox to expose VS Code for").description(
1554
- "expose a VS Code Server instance running in the given sandbox to the internet"
1555
- ).action(exposeVscode);
1614
+ program.command("vscode").aliases(["code", "code-server"]).argument("<sandbox>", "the sandbox to expose VS Code for").description("expose a visual code server to the internet").action(exposeVscode);
1615
+ program.enablePositionalOptions().command("exec").argument("<sandbox>", "the sandbox to execute the command in").argument("<command...>", "the command to execute").description("execute a command in the given sandbox").passThroughOptions().action(exec);
1556
1616
  program.command("expose").argument("<sandbox>", "the sandbox to expose a port for").argument("<port>", "the port to expose", (val) => {
1557
1617
  const port = parseInt(val, 10);
1558
1618
  if (isNaN(port)) {
package/package.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "bin": {
5
5
  "pocketenv": "dist/index.js"
6
6
  },
7
- "version": "0.3.0",
7
+ "version": "0.3.2",
8
8
  "type": "module",
9
9
  "keywords": [
10
10
  "sandbox",
package/src/cmd/create.ts CHANGED
@@ -4,6 +4,7 @@ import getAccessToken from "../lib/getAccessToken";
4
4
  import type { Sandbox } from "../types/sandbox";
5
5
  import connectToSandbox from "./ssh";
6
6
  import { c } from "../theme";
7
+ import { expandRepo } from "../lib/expandRepo";
7
8
 
8
9
  async function createSandbox(
9
10
  name: string,
@@ -11,13 +12,16 @@ async function createSandbox(
11
12
  provider,
12
13
  ssh,
13
14
  base,
15
+ repo,
14
16
  }: {
15
- provider: string | undefined;
16
- ssh: boolean | undefined;
17
- base: string | undefined;
17
+ provider?: string;
18
+ ssh?: boolean;
19
+ base?: string;
20
+ repo?: string;
18
21
  },
19
22
  ) {
20
23
  const token = await getAccessToken();
24
+ if (repo) repo = expandRepo(repo);
21
25
 
22
26
  if (["deno", "vercel", "daytona"].includes(provider || "")) {
23
27
  consola.error(
@@ -34,6 +38,7 @@ async function createSandbox(
34
38
  base ??
35
39
  "at://did:plc:aturpi2ls3yvsmhc6wybomun/io.pocketenv.sandbox/openclaw",
36
40
  provider: provider ?? "cloudflare",
41
+ repo,
37
42
  },
38
43
  {
39
44
  headers: {
@@ -0,0 +1,53 @@
1
+ import consola from "consola";
2
+ import getAccessToken from "../lib/getAccessToken";
3
+ import { client } from "../client";
4
+ import { env } from "../lib/env";
5
+
6
+ export async function exec(sandbox: string, command: string[]) {
7
+ const token = await getAccessToken();
8
+
9
+ try {
10
+ const [cmd, ...args] = command;
11
+ const response = await client.post<{
12
+ stderr: string;
13
+ stdout: string;
14
+ exitCode: number;
15
+ }>(
16
+ "/xrpc/io.pocketenv.sandbox.exec",
17
+ {
18
+ command: `${cmd} ${args.join(" ")}`,
19
+ },
20
+ {
21
+ params: {
22
+ id: sandbox,
23
+ },
24
+ headers: {
25
+ Authorization: `Bearer ${env.POCKETENV_TOKEN || token}`,
26
+ },
27
+ },
28
+ );
29
+
30
+ if (response.data.stdout) {
31
+ process.stdout.write(
32
+ response.data.stdout.endsWith("\n")
33
+ ? response.data.stdout
34
+ : response.data.stdout + "\n",
35
+ );
36
+ }
37
+ if (response.data.stderr) {
38
+ process.stderr.write(
39
+ response.data.stderr.endsWith("\n")
40
+ ? response.data.stderr
41
+ : response.data.stderr + "\n",
42
+ );
43
+ }
44
+
45
+ if (response.data.exitCode !== 0) {
46
+ consola.error(`Command exited with code ${response.data.exitCode}`);
47
+ }
48
+
49
+ process.exit(response.data.exitCode);
50
+ } catch (error) {
51
+ consola.error("Failed to execute command:", error);
52
+ }
53
+ }
package/src/cmd/start.ts CHANGED
@@ -4,19 +4,30 @@ import getAccessToken from "../lib/getAccessToken";
4
4
  import { client } from "../client";
5
5
  import { env } from "../lib/env";
6
6
  import connectToSandbox from "./ssh";
7
+ import { expandRepo } from "../lib/expandRepo";
7
8
 
8
- async function start(name: string, { ssh }: { ssh?: boolean }) {
9
+ async function start(
10
+ name: string,
11
+ { ssh, repo }: { ssh?: boolean; repo?: string },
12
+ ) {
9
13
  const token = await getAccessToken();
14
+ if (repo) repo = expandRepo(repo);
10
15
 
11
16
  try {
12
- await client.post("/xrpc/io.pocketenv.sandbox.startSandbox", undefined, {
13
- params: {
14
- id: name,
17
+ await client.post(
18
+ "/xrpc/io.pocketenv.sandbox.startSandbox",
19
+ {
20
+ repo,
15
21
  },
16
- headers: {
17
- Authorization: `Bearer ${env.POCKETENV_TOKEN || token}`,
22
+ {
23
+ params: {
24
+ id: name,
25
+ },
26
+ headers: {
27
+ Authorization: `Bearer ${env.POCKETENV_TOKEN || token}`,
28
+ },
18
29
  },
19
- });
30
+ );
20
31
 
21
32
  if (ssh) {
22
33
  await connectToSandbox(name);
package/src/index.ts CHANGED
@@ -22,6 +22,7 @@ import consola from "consola";
22
22
  import { listPorts } from "./cmd/ports";
23
23
  import { c } from "./theme";
24
24
  import { exposeVscode } from "./cmd/vscode";
25
+ import { exec } from "./cmd/exec";
25
26
 
26
27
  const program = new Command();
27
28
 
@@ -76,6 +77,10 @@ program
76
77
  .command("start")
77
78
  .argument("<sandbox>", "the sandbox to start")
78
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
+ )
79
84
  .description("start the given sandbox")
80
85
  .action(start);
81
86
 
@@ -94,6 +99,10 @@ program
94
99
  "the base sandbox to use for the sandbox, e.g. openclaw, claude-code, codex, copilot ...",
95
100
  )
96
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
+ )
97
106
  .argument("[name]", "the name of the sandbox to create")
98
107
  .description("create a new sandbox")
99
108
  .action(createSandbox);
@@ -112,12 +121,20 @@ program
112
121
 
113
122
  program
114
123
  .command("vscode")
124
+ .aliases(["code", "code-server"])
115
125
  .argument("<sandbox>", "the sandbox to expose VS Code for")
116
- .description(
117
- "expose a VS Code Server instance running in the given sandbox to the internet",
118
- )
126
+ .description("expose a visual code server to the internet")
119
127
  .action(exposeVscode);
120
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
+
121
138
  program
122
139
  .command("expose")
123
140
  .argument("<sandbox>", "the sandbox to expose a port for")
@@ -0,0 +1,9 @@
1
+ export function expandRepo(repo: string): string {
2
+ const githubMatch = repo.match(/^github:([^/]+\/[^/]+)$/);
3
+ if (githubMatch) return `https://github.com/${githubMatch[1]}`;
4
+
5
+ const tangledMatch = repo.match(/^tangled:([^/]+\/[^/]+)$/);
6
+ if (tangledMatch) return `https://tangled.org/${tangledMatch[1]}`;
7
+
8
+ return repo;
9
+ }