Clojure SDK for Pocketenv
at main 242 lines 6.0 kB view raw view rendered
1# pocketenv-clojure 2[![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) 3[![Clojars Project](https://img.shields.io/clojars/v/io.pocketenv/pocketenv.svg)](https://clojars.org/io.pocketenv/pocketenv) 4 5Clojure SDK for the [Pocketenv](https://pocketenv.io) sandbox platform. 6 7## Installation 8 9Add to your `deps.edn`: 10 11```clojure 12{:deps io.pocketenv/pocketenv {:mvn/version "0.1.1-SNAPSHOT"}} 13``` 14 15## Configuration 16 17Set one of the following before making any calls: 18 19| Method | Details | 20|-----------------|------------------------------------------------| 21| Env var | `POCKETENV_TOKEN=<your-token>` | 22| Token file | `~/.pocketenv/token.json``{"token": "..."}` | 23| Per-call option | Pass `:token "..."` in the opts map | 24 25Optional env vars: 26 27``` 28POCKETENV_API_URL — API base URL (default: https://api.pocketenv.io) 29POCKETENV_PUBLIC_KEY — server public key for client-side encryption (hex-encoded) 30``` 31 32## Quick start 33 34```clojure 35(require '[pocketenv-io.pocketenv :as pocketenv] 36 '[pocketenv-io.sandbox :as sandbox]) 37 38;; Create, start, run a command, then clean up 39(-> (pocketenv/create-sandbox "my-box") 40 (sandbox/start) 41 (sandbox/wait-until-running) 42 (sandbox/exec "echo" ["hello from pocketenv"]) 43 ;; {:ok #ExecResult{:stdout "hello from pocketenv\n" :stderr "" :exit-code 0}} 44 ) 45``` 46 47All 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. 48 49--- 50 51## Sandbox lifecycle 52 53### Create 54 55```clojure 56;; Minimal — uses the default openclaw base image on Cloudflare 57(pocketenv/create-sandbox "my-box") 58;; => {:ok #Sandbox{:id "..." :name "my-box" :status :stopped ...}} 59 60;; With options 61(pocketenv/create-sandbox "my-box" 62 {:provider "daytona" 63 :repo "https://github.com/acme/my-app" 64 :keep-alive true}) 65``` 66 67### Start / Stop / Delete 68 69```clojure 70(def sb (pocketenv/create-sandbox "my-box")) 71 72(sandbox/start sb) 73(sandbox/stop sb) 74(sandbox/delete sb) 75``` 76 77### Wait until running 78 79```clojure 80(-> (pocketenv/create-sandbox "ci-runner") 81 (sandbox/start) 82 (sandbox/wait-until-running {:timeout-ms 120000 :interval-ms 3000})) 83``` 84 85--- 86 87## Running commands 88 89```clojure 90(let [result (-> (pocketenv/get-sandbox "my-box") 91 (sandbox/exec "ls" ["-la" "/"]))] 92 (println (get-in result [:ok :stdout]))) 93 94;; Run a Clojure test suite 95(-> (pocketenv/get-sandbox "my-box") 96 (sandbox/start) 97 (sandbox/wait-until-running) 98 (sandbox/exec "clojure" ["-M:test"])) 99;; => {:ok #ExecResult{:stdout "..." :stderr "" :exit-code 0}} 100``` 101 102`ExecResult` fields: `:stdout`, `:stderr`, `:exit-code`. 103 104--- 105 106## Listing sandboxes 107 108```clojure 109;; Public sandbox catalog 110(pocketenv/list-sandboxes) 111;; => {:ok {:sandboxes [...] :total 42}} 112 113;; With pagination 114(pocketenv/list-sandboxes {:limit 10 :offset 20}) 115 116;; Sandboxes belonging to a specific actor (DID or handle) 117(pocketenv/list-sandboxes-by-actor "alice.pocketenv.io") 118(pocketenv/list-sandboxes-by-actor "did:plc:abc123") 119``` 120 121--- 122 123## Exposing ports 124 125```clojure 126;; Expose port 3000 and get a public preview URL 127(-> (pocketenv/get-sandbox "my-box") 128 (sandbox/expose 3000 {:description "Web server"})) 129;; => {:ok "https://preview.pocketenv.io/..."} 130 131;; List all exposed ports 132(-> (pocketenv/get-sandbox "my-box") 133 (sandbox/list-ports)) 134;; => {:ok [#Port{:port 3000 :description "Web server" :preview-url "..."}]} 135 136;; Remove a port 137(-> (pocketenv/get-sandbox "my-box") 138 (sandbox/unexpose 3000)) 139``` 140 141--- 142 143## VS Code Server 144 145```clojure 146(-> (pocketenv/get-sandbox "my-box") 147 (sandbox/vscode)) 148;; => {:ok "https://preview.pocketenv.io/.../vscode/..."} 149``` 150 151--- 152 153## Secrets 154 155Secrets are encrypted client-side before transmission. 156 157```clojure 158;; Add a secret 159(pocketenv/add-secret "sandbox-id" "DATABASE_URL" "postgres://...") 160 161;; Or via the sandbox pipe 162(-> (pocketenv/get-sandbox "my-box") 163 (sandbox/set-secret "API_KEY" "sk-...")) 164 165;; List secrets (names only — values are never returned) 166(pocketenv/list-secrets "sandbox-id") 167;; => {:ok [#Secret{:id "..." :name "DATABASE_URL" :created-at "..."}]} 168 169;; Delete a secret by id 170(pocketenv/delete-secret "secret-id") 171``` 172 173--- 174 175## SSH keys 176 177Private keys are encrypted client-side before transmission. 178 179```clojure 180;; Store a key pair 181(pocketenv/put-ssh-keys "sandbox-id" private-key-str public-key-str) 182 183;; Or via the sandbox pipe 184(-> (pocketenv/get-sandbox "my-box") 185 (sandbox/set-ssh-keys private-key-str public-key-str)) 186 187;; Retrieve 188(pocketenv/get-ssh-keys "sandbox-id") 189;; => {:ok #SshKey{:id "..." :public-key "..." :private-key "..." :created-at "..."}} 190``` 191 192--- 193 194## Tailscale 195 196```clojure 197;; Store a Tailscale auth key (must start with "tskey-auth-") 198(pocketenv/put-tailscale-auth-key "sandbox-id" "tskey-auth-...") 199 200;; Or via the sandbox pipe 201(-> (pocketenv/get-sandbox "my-box") 202 (sandbox/set-tailscale-auth-key "tskey-auth-...")) 203 204;; Retrieve 205(pocketenv/get-tailscale-auth-key "sandbox-id") 206;; => {:ok #TailscaleAuthKey{:id "..." :auth-key "..." :created-at "..."}} 207``` 208 209--- 210 211## Actor / profile 212 213```clojure 214;; Your own profile (requires authentication) 215(pocketenv/me) 216;; => {:ok #Profile{:did "did:plc:..." :handle "alice" :display-name "Alice" ...}} 217 218;; Any actor's profile 219(pocketenv/get-profile "alice.pocketenv.io") 220(pocketenv/get-profile "did:plc:abc123") 221``` 222 223--- 224 225## Error handling 226 227Every function returns `{:ok value}` or `{:error reason}`. Use `if-let` or pattern matching: 228 229```clojure 230(let [result (pocketenv/get-sandbox "does-not-exist")] 231 (if-let [sb (:ok result)] 232 (println "Found:" (:name sb)) 233 (println "Error:" (:error result)))) 234``` 235 236When chaining with `sandbox/*`, passing `{:error ...}` into any operation throws an `ExceptionInfo`, keeping failures explicit. 237 238--- 239 240## License 241 242MIT