An open API service indexing awesome lists of open source software.

https://github.com/pocketenv-io/pocketenv-clojure

Clojure SDK for Pocketenv
https://github.com/pocketenv-io/pocketenv-clojure

agent clojure microvm sandbox

Last synced: about 2 months ago
JSON representation

Clojure SDK for Pocketenv

Awesome Lists containing this project

README

          

# pocketenv-clojure
[![ci](https://github.com/pocketenv-io/pocketenv-clojure/actions/workflows/ci.yml/badge.svg)](https://github.com/pocketenv-io/pocketenv-clojure/actions/workflows/ci.yml)
[![Clojars Project](https://img.shields.io/clojars/v/io.pocketenv/pocketenv.svg)](https://clojars.org/io.pocketenv/pocketenv)

Clojure SDK for the [Pocketenv](https://pocketenv.io) sandbox platform.

## Installation

Add to your `deps.edn`:

```clojure
{:deps io.pocketenv/pocketenv {:mvn/version "0.1.3-SNAPSHOT"}}
```

## Configuration

Set one of the following before making any calls:

| Method | Details |
|-----------------|------------------------------------------------|
| Env var | `POCKETENV_TOKEN=` |
| Token file | `~/.pocketenv/token.json` → `{"token": "..."}` |
| Per-call option | Pass `:token "..."` in the opts map |

Optional env vars:

```
POCKETENV_API_URL — API base URL (default: https://api.pocketenv.io)
POCKETENV_PUBLIC_KEY — server public key for client-side encryption (hex-encoded)
POCKETENV_STORAGE_URL — storage base URL for file transfers (default: https://sandbox.pocketenv.io)
```

## Quick start

```clojure
(require '[pocketenv-io.pocketenv :as pocketenv]
'[pocketenv-io.sandbox :as sandbox])

;; Create, start, run a command, then clean up
(-> (pocketenv/create-sandbox "my-box")
(sandbox/start)
(sandbox/wait-until-running)
(sandbox/exec "echo" ["hello from pocketenv"])
;; {:ok #ExecResult{:stdout "hello from pocketenv\n" :stderr "" :exit-code 0}}
)
```

All functions return `{:ok value}` on success or `{:error reason}` on failure. The `sandbox/*` operations also accept an `{:ok sandbox}` result directly so they can be chained with `->` without unwrapping.

---

## Sandbox lifecycle

### Create

```clojure
;; Minimal — uses the default openclaw base image on Cloudflare
(pocketenv/create-sandbox "my-box")
;; => {:ok #Sandbox{:id "..." :name "my-box" :status :stopped ...}}

;; With options
(pocketenv/create-sandbox "my-box"
{:provider "daytona"
:repo "https://github.com/acme/my-app"
:keep-alive true})
```

### Start / Stop / Delete

```clojure
(def sb (pocketenv/create-sandbox "my-box"))

(sandbox/start sb)
(sandbox/stop sb)
(sandbox/delete sb)
```

### Wait until running

```clojure
(-> (pocketenv/create-sandbox "ci-runner")
(sandbox/start)
(sandbox/wait-until-running {:timeout-ms 120000 :interval-ms 3000}))
```

---

## Running commands

```clojure
(let [result (-> (pocketenv/get-sandbox "my-box")
(sandbox/exec "ls" ["-la" "/"]))]
(println (get-in result [:ok :stdout])))

;; Run a Clojure test suite
(-> (pocketenv/get-sandbox "my-box")
(sandbox/start)
(sandbox/wait-until-running)
(sandbox/exec "clojure" ["-M:test"]))
;; => {:ok #ExecResult{:stdout "..." :stderr "" :exit-code 0}}
```

`ExecResult` fields: `:stdout`, `:stderr`, `:exit-code`.

---

## Listing sandboxes

```clojure
;; Public sandbox catalog
(pocketenv/list-sandboxes)
;; => {:ok {:sandboxes [...] :total 42}}

;; With pagination
(pocketenv/list-sandboxes {:limit 10 :offset 20})

;; Sandboxes belonging to a specific actor (DID or handle)
(pocketenv/list-sandboxes-by-actor "alice.pocketenv.io")
(pocketenv/list-sandboxes-by-actor "did:plc:abc123")
```

---

## Exposing ports

```clojure
;; Expose port 3000 and get a public preview URL
(-> (pocketenv/get-sandbox "my-box")
(sandbox/expose 3000 {:description "Web server"}))
;; => {:ok "https://preview.pocketenv.io/..."}

;; List all exposed ports
(-> (pocketenv/get-sandbox "my-box")
(sandbox/list-ports))
;; => {:ok [#Port{:port 3000 :description "Web server" :preview-url "..."}]}

;; Remove a port
(-> (pocketenv/get-sandbox "my-box")
(sandbox/unexpose 3000))
```

---

## VS Code Server

```clojure
(-> (pocketenv/get-sandbox "my-box")
(sandbox/vscode))
;; => {:ok "https://preview.pocketenv.io/.../vscode/..."}
```

---

## Secrets

Secrets are encrypted client-side before transmission.

```clojure
;; Add a secret
(pocketenv/add-secret "sandbox-id" "DATABASE_URL" "postgres://...")

;; Or via the sandbox pipe
(-> (pocketenv/get-sandbox "my-box")
(sandbox/set-secret "API_KEY" "sk-..."))

;; List secrets (names only — values are never returned)
(pocketenv/list-secrets "sandbox-id")
;; => {:ok [#Secret{:id "..." :name "DATABASE_URL" :created-at "..."}]}

;; Delete a secret by id
(pocketenv/delete-secret "secret-id")
```

---

## SSH keys

Private keys are encrypted client-side before transmission.

```clojure
;; Store a key pair
(pocketenv/put-ssh-keys "sandbox-id" private-key-str public-key-str)

;; Or via the sandbox pipe
(-> (pocketenv/get-sandbox "my-box")
(sandbox/set-ssh-keys private-key-str public-key-str))

;; Retrieve
(pocketenv/get-ssh-keys "sandbox-id")
;; => {:ok #SshKey{:id "..." :public-key "..." :private-key "..." :created-at "..."}}
```

---

## Tailscale

```clojure
;; Store a Tailscale auth key (must start with "tskey-auth-")
(pocketenv/put-tailscale-auth-key "sandbox-id" "tskey-auth-...")

;; Or via the sandbox pipe
(-> (pocketenv/get-sandbox "my-box")
(sandbox/set-tailscale-auth-key "tskey-auth-..."))

;; Retrieve
(pocketenv/get-tailscale-auth-key "sandbox-id")
;; => {:ok #TailscaleAuthKey{:id "..." :auth-key "..." :created-at "..."}}
```

---

## Copying files

Upload a local file or directory into a sandbox, download a sandbox path to local disk, or copy directly between two sandboxes — all through storage, with no manual archiving required.

```clojure
;; Upload a local directory to the sandbox
(-> (pocketenv/get-sandbox "my-box")
(sandbox/upload "./my-project" "/workspace"))

;; Upload a single file
(-> (pocketenv/get-sandbox "my-box")
(sandbox/upload "./config.json" "/app/config.json"))

;; Download a sandbox path to a local directory
(-> (pocketenv/get-sandbox "my-box")
(sandbox/download "/workspace/output" "./output"))

;; Copy a path from one sandbox to another (no local I/O)
(-> (pocketenv/get-sandbox "source-box")
(sandbox/copy-to "dest-sandbox-id" "/workspace" "/workspace"))
```

All three functions return `:ok` on success or `{:error reason}` on failure and are pipe-safe — they accept a bare `Sandbox` or an `{:ok sandbox}` result from a previous step.

---

## Actor / profile

```clojure
;; Your own profile (requires authentication)
(pocketenv/me)
;; => {:ok #Profile{:did "did:plc:..." :handle "alice" :display-name "Alice" ...}}

;; Any actor's profile
(pocketenv/get-profile "alice.pocketenv.io")
(pocketenv/get-profile "did:plc:abc123")
```

---

## Error handling

Every function returns `{:ok value}` or `{:error reason}`. Use `if-let` or pattern matching:

```clojure
(let [result (pocketenv/get-sandbox "does-not-exist")]
(if-let [sb (:ok result)]
(println "Found:" (:name sb))
(println "Error:" (:error result))))
```

When chaining with `sandbox/*`, passing `{:error ...}` into any operation throws an `ExceptionInfo`, keeping failures explicit.

---

## License

MIT