Gleam SDK for Pocketenv
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat: add pocketenv/backup module

Implements sandbox backup operations:
- create/4 — snapshot a directory with optional description and TTL
- list/1 — list all backups for a sandbox
- restore/2 — restore a backup by ID

Mirrors the backup.ts implementation in pocketenv-js.
Updates CHANGELOG (v1.3.0) and README with usage example.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

+162 -1
+9
CHANGELOG.md
··· 5 5 The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), 6 6 and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 7 7 8 + ## [1.3.0] - 2026-04-07 9 + 10 + ### Added 11 + 12 + - `pocketenv/backup` module with three operations: 13 + - `create/4` — snapshot a sandbox directory with optional description and TTL 14 + - `list/1` — list all backups for a sandbox 15 + - `restore/2` — restore a sandbox from a backup by ID 16 + 8 17 ## [1.2.0] - 2026-04-05 9 18 10 19 ### Added
+30 -1
README.md
··· 9 9 ports, networking, and file transfers. 10 10 11 11 ```sh 12 - gleam add pocketenv@1.2 12 + gleam add pocketenv@1.3 13 13 ``` 14 14 15 15 ## Usage ··· 267 267 268 268 // Copy a path from this sandbox to another sandbox (no local I/O) 269 269 let assert Ok(Nil) = sb |> copy.copy_to("other-sandbox-id", "/app/data", "/app/data") 270 + } 271 + ``` 272 + 273 + ### Backups 274 + 275 + ```gleam 276 + import pocketenv 277 + import pocketenv/sandbox 278 + import pocketenv/backup 279 + import gleam/option.{None, Some} 280 + 281 + pub fn main() { 282 + let client = pocketenv.new_client("your-token") 283 + let assert Ok(Some(sandbox_data)) = sandbox.get(client, "sandbox-abc123") 284 + let sb = sandbox_data |> sandbox.connect(client) 285 + 286 + // Create a backup of /app with an optional description and TTL (seconds) 287 + let assert Ok(Nil) = sb |> backup.create("/app", Some("pre-deploy"), None) 288 + 289 + // List all backups 290 + let assert Ok(backups) = sb |> backup.list() 291 + 292 + // Restore a backup by ID 293 + case backups { 294 + [first, ..] -> { 295 + let assert Ok(Nil) = sb |> backup.restore(first.id) 296 + } 297 + [] -> Nil 298 + } 270 299 } 271 300 ``` 272 301
+123
src/pocketenv/backup.gleam
··· 1 + //// Create, list, and restore backups of a sandbox directory. 2 + //// 3 + //// Backups let you snapshot a directory inside a running sandbox and restore 4 + //// it later. All three operations are scoped to a `ConnectedSandbox`. 5 + 6 + import gleam/dynamic/decode 7 + import gleam/json 8 + import gleam/list 9 + import gleam/option.{type Option, None, Some} 10 + import gleam/result 11 + import pocketenv.{type PocketenvError, JsonDecodeError, do_get, do_post} 12 + import pocketenv/sandbox.{type ConnectedSandbox} 13 + 14 + /// A point-in-time snapshot of a sandbox directory. 15 + pub type Backup { 16 + Backup( 17 + id: String, 18 + directory: String, 19 + description: Option(String), 20 + expires_at: Option(String), 21 + created_at: String, 22 + ) 23 + } 24 + 25 + /// Creates a backup of `directory` inside the sandbox. 26 + /// Optionally set a human-readable `description` and a time-to-live `ttl` 27 + /// (in seconds) after which the backup is automatically deleted. 28 + /// 29 + /// ## Example 30 + /// 31 + /// ```gleam 32 + /// let assert Ok(Nil) = sb |> backup.create("/app", Some("pre-deploy"), None) 33 + /// ``` 34 + pub fn create( 35 + sb: ConnectedSandbox, 36 + directory: String, 37 + description: Option(String), 38 + ttl: Option(Int), 39 + ) -> Result(Nil, PocketenvError) { 40 + let fields = [#("directory", json.string(directory))] 41 + let fields = case description { 42 + Some(d) -> list.append(fields, [#("description", json.string(d))]) 43 + None -> fields 44 + } 45 + let fields = case ttl { 46 + Some(t) -> list.append(fields, [#("ttl", json.int(t))]) 47 + None -> fields 48 + } 49 + let body = json.to_string(json.object(fields)) 50 + use _ <- result.try(do_post( 51 + sb.client, 52 + "/xrpc/io.pocketenv.sandbox.createBackup", 53 + [#("id", sb.data.id)], 54 + body, 55 + )) 56 + Ok(Nil) 57 + } 58 + 59 + /// Lists all backups associated with the sandbox. 60 + /// 61 + /// ## Example 62 + /// 63 + /// ```gleam 64 + /// let assert Ok(backups) = sb |> backup.list() 65 + /// ``` 66 + pub fn list(sb: ConnectedSandbox) -> Result(List(Backup), PocketenvError) { 67 + use body <- result.try( 68 + do_get(sb.client, "/xrpc/io.pocketenv.sandbox.getBackups", [ 69 + #("id", sb.data.id), 70 + ]), 71 + ) 72 + json.parse(body, { 73 + use backups <- decode.field("backups", decode.list(backup_decoder())) 74 + decode.success(backups) 75 + }) 76 + |> result.map_error(JsonDecodeError) 77 + } 78 + 79 + /// Restores the backup identified by `backup_id` into the sandbox. 80 + /// 81 + /// ## Example 82 + /// 83 + /// ```gleam 84 + /// let assert Ok(Nil) = sb |> backup.restore("backup-abc123") 85 + /// ``` 86 + pub fn restore( 87 + sb: ConnectedSandbox, 88 + backup_id: String, 89 + ) -> Result(Nil, PocketenvError) { 90 + let body = 91 + json.to_string(json.object([#("backupId", json.string(backup_id))])) 92 + use _ <- result.try(do_post( 93 + sb.client, 94 + "/xrpc/io.pocketenv.sandbox.restoreBackup", 95 + [], 96 + body, 97 + )) 98 + Ok(Nil) 99 + } 100 + 101 + /// JSON decoder for `Backup`. 102 + pub fn backup_decoder() -> decode.Decoder(Backup) { 103 + use id <- decode.field("id", decode.string) 104 + use directory <- decode.field("directory", decode.string) 105 + use description <- decode.optional_field( 106 + "description", 107 + None, 108 + decode.optional(decode.string), 109 + ) 110 + use expires_at <- decode.optional_field( 111 + "expiresAt", 112 + None, 113 + decode.optional(decode.string), 114 + ) 115 + use created_at <- decode.field("createdAt", decode.string) 116 + decode.success(Backup( 117 + id: id, 118 + directory: directory, 119 + description: description, 120 + expires_at: expires_at, 121 + created_at: created_at, 122 + )) 123 + }