@sandbar-sdk/cli 0.0.0 → 0.4.0

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.
Files changed (3) hide show
  1. package/README.md +145 -2
  2. package/dist/main.js +2663 -0
  3. package/package.json +38 -7
package/README.md CHANGED
@@ -1,5 +1,148 @@
1
1
  # @sandbar-sdk/cli
2
2
 
3
- This 0.0.0 version reserves the package name so releases can use npm trusted publishing. It contains no code.
3
+ `sandbar` creates and manages sandboxes on any Sandbar provider from the command line. It is built for agents first: it never prompts, every command has a stable `--json` form, and exit codes say what to do next.
4
4
 
5
- Releases are published from https://github.com/pandemicsyn/sandbar/blob/main/packages/cli.
5
+ Install it with `npm install -g @sandbar-sdk/cli` (or `bun add -g @sandbar-sdk/cli`); `sandbar --version` prints the installed version. Adapters are installed separately.
6
+
7
+ ## Quick start
8
+
9
+ ```sh
10
+ export SANDBAR_PROVIDER=e2b E2B_API_KEY=...
11
+ sandbar create --name web
12
+ sandbar exec web -- uname -a
13
+ sandbar cp ./app.py web:/work/app.py
14
+ sandbar rm web
15
+
16
+ sandbar run -- python3 -c 'print(2 + 2)' # create, run, always destroy
17
+ ```
18
+
19
+ The adapter package must be installed where the CLI can find it: in `~/.config/sandbar/adapters` (`cd ~/.config/sandbar/adapters && npm install @sandbar-sdk/e2b`) or next to the CLI itself. Adapters receive your credentials, so the current project's `node_modules` is searched only when you opt in with `"adapters": { "searchProject": true }` or `SANDBAR_PROJECT_ADAPTERS=1`. `sandbar providers` shows what is installed and which credentials are set.
20
+
21
+ ## Commands
22
+
23
+ | Command | What it does |
24
+ | --- | --- |
25
+ | `create [--name] [--image <oci-ref> \| --prepared <id>] [--network] [--label k=v] [--if-not-exists]` | Create a sandbox, print its name |
26
+ | `ls [--offline]` | List store records with live state (`running`, `gone`, `unreachable`, ...) |
27
+ | `inspect <sandbox>` | State, expiry and reference |
28
+ | `rm <sandbox>... \| --all [--force]` | Destroy and forget; only sandboxes this CLI created unless `--force`. `--all` skips the others and reports them |
29
+ | `exec <sandbox> [--cwd] [--env K=V] [--stdin \| --stdin-file] [--timeout] [--max-output] -- cmd...` | Run argv; exit code passes through |
30
+ | `sh <sandbox> '<script>'` (or `-- '<script>'`) | Run a shell script; same flags as exec |
31
+ | `run [create and exec flags] [--keep] -- cmd...` | Create, run one command, destroy |
32
+ | `image build <oci-ref> [--name] [--refresh]` | Build an OCI image on a provider that needs a build first, and record it |
33
+ | `image ls`, `image inspect <name>`, `image forget <name>` | List, show or drop image records; `forget` lists what the provider still holds |
34
+ | `cp <src> <dst>` | Copy one file; one side is `<sandbox>:/path` |
35
+ | `cat <sandbox>:/path` | Print a file |
36
+ | `doctor` | Check config, credentials and the connection; creates nothing |
37
+ | `providers` | Official providers, install state and missing credentials |
38
+ | `schema` | Machine-readable description of every command, flag and exit code |
39
+ | `skill [install [--project]]` | Print the agent skill for this CLI, or install it for your coding agents |
40
+ | `completion <zsh\|bash\|fish>` | Print a shell completion script |
41
+
42
+ A `<sandbox>` is a store name, a native id held in the store, or `@file.json` holding a saved sandbox reference.
43
+
44
+ ## Output and exit codes
45
+
46
+ With `--json` (or `SANDBAR_JSON=1`) stdout holds exactly one document: `{"ok":true,"data":...}` or `{"ok":false,"error":{"code","message","retryable","outcome","hint?","known?","details?"}}`. Diagnostics go to stderr. When `rm` stops partway, `error.details` lists what was `removed`, `skipped` and `failed`.
47
+
48
+ | Code | Meaning |
49
+ | --- | --- |
50
+ | 0 | success |
51
+ | 1 | other failure (including a taken `--name` or an existing `cp --no-clobber` target) |
52
+ | 2 | usage error |
53
+ | 3 | not found |
54
+ | 4 | unsupported, adapter missing (`ADAPTER_NOT_INSTALLED`) or failing to load (`ADAPTER_LOAD_FAILED`), password-manager CLI not installed, or `limits.maxSandboxes` reached |
55
+ | 5 | authentication or permission, including a password-manager secret that cannot be read |
56
+ | 6 | outcome unknown: an effect may have happened, inspect before retrying |
57
+ | 7 | retryable before any effect (rate limit, capacity, unavailable) |
58
+ | 8 | partial: some steps confirmed, see `error.known` |
59
+
60
+ `exec`, `sh` and `run` exit with the remote command's exit code. Their own failures always use 125, so they never look like a command's result; providers report deadlines differently, so there is no separate deadline code. A missing exit status is 125 with `OUTCOME_UNKNOWN` and the captured output in `error.known.exec`, never 0. If `run` cannot destroy its sandbox it exits 125 with `CLEANUP_FAILED`, and the command's result is inside that one error document.
61
+
62
+ SIGINT, SIGTERM and SIGHUP all stop local waiting and let `run` clean up, then exit 130, 143 or 129 (`INTERRUPTED` for `run`); a second signal exits at once.
63
+
64
+ ## Shell completion
65
+
66
+ ```sh
67
+ source <(sandbar completion zsh) # add to ~/.zshrc
68
+ source <(sandbar completion bash) # add to ~/.bashrc
69
+ sandbar completion fish > ~/.config/fish/completions/sandbar.fish
70
+ ```
71
+
72
+ Completion covers commands, flags, `--provider` ids, `--profile` names from `sandbar.json`, sandbox names for `exec`, `sh`, `inspect`, `rm`, `cp` and `cat`, and image record names for `image inspect` and `image forget`. Sandbox names come from the local store, so pressing tab never calls a provider.
73
+
74
+ ## Agent skill
75
+
76
+ The CLI carries an agent skill that matches its own version. `sandbar skill` prints it; `sandbar skill install` writes it to `~/.agents/skills/sandbar-cli/` and, when `~/.claude` exists, to `~/.claude/skills/sandbar-cli/`. Add `--project` to install into the current folder instead. Rerunning updates a copy it wrote; a copy installed another way is left alone.
77
+
78
+ ## Configuration
79
+
80
+ `~/.config/sandbar/sandbar.json` (`$XDG_CONFIG_HOME/sandbar`, or `--config` / `SANDBAR_CONFIG`):
81
+
82
+ ```json
83
+ {
84
+ "default": "e2b",
85
+ "profiles": {
86
+ "e2b": { "provider": "e2b", "credentials": { "apiKey": "env:E2B_API_KEY" }, "config": { "network": "internet" } },
87
+ "local": { "module": "./my-adapter.js", "export": "myAdapter", "config": {}, "credentials": {} }
88
+ },
89
+ "state": { "kind": "fs", "dir": "~/.config/sandbar/state" },
90
+ "limits": { "maxSandboxes": null }
91
+ }
92
+ ```
93
+
94
+ - Profile selection: `--profile`, `SANDBAR_PROFILE`, `--provider`, `SANDBAR_PROVIDER`, then `default`.
95
+ - `provider` names an official adapter (`@sandbar-sdk/<provider>`); `config` and `credentials` are that adapter's own settings. `module` and `export` load any adapter, either a definition or a bound adapter.
96
+ - Credentials can be written in the profile directly, as `env:NAME` references, or as password-manager references (below). Keep the file private (`chmod 600`) if it holds keys. Env-only providers read `DAYTONA_API_KEY` and `DAYTONA_TARGET`; `E2B_API_KEY`; `BOXD_API_KEY`, `BOXD_ORG` and `BOXD_NETWORK=internet` (boxd always allows egress, so its adapter requires choosing it); `VERCEL_TOKEN`, `VERCEL_TEAM_ID` and `VERCEL_PROJECT_ID`; `MODAL_TOKEN_ID`, `MODAL_TOKEN_SECRET`, `MODAL_APP_NAME` and `MODAL_ENVIRONMENT`.
97
+ - `limits.maxSandboxes` (or `SANDBAR_MAX_SANDBOXES`) caps how many sandboxes the store may hold. Unset means uncapped.
98
+
99
+ ## Password managers
100
+
101
+ Any `config` or `credentials` value (including one that comes from a provider's environment variable) can name a secret in a password manager. The CLI reads it with the manager's own command-line tool, using that tool's sign-in, only when the profile connects. It never writes the value anywhere.
102
+
103
+ | Reference | Read with | Sign-in |
104
+ | --- | --- | --- |
105
+ | `op://vault/item/field` | `op read` (1Password CLI) | desktop app integration, `op signin` or `OP_SERVICE_ACCOUNT_TOKEN` |
106
+ | `bws://<secret-id>` | `bws secret get` (Bitwarden Secrets Manager CLI) | `BWS_ACCESS_TOKEN` |
107
+ | `pass://path/to/entry` | `pass show` (first line of the entry) | your GPG agent |
108
+
109
+ ```json
110
+ { "profiles": { "e2b": { "provider": "e2b", "credentials": { "apiKey": "op://Dev/E2B/credential" } } } }
111
+ ```
112
+
113
+ If the tool is not installed the command exits 4 (`SECRET_TOOL_MISSING`); if it cannot read the secret it exits 5 (`SECRET_UNAVAILABLE`) with the tool's own message. Each reference is read at most once per command.
114
+
115
+ ## Images
116
+
117
+ `--image` always takes an OCI reference, such as `python:3.12` or `ghcr.io/org/tool@sha256:...`. A provider-specific template or snapshot goes in the profile's `image` setting or `--prepared <id>`.
118
+
119
+ - boxd and Modal boot the OCI image directly. Nothing is built or kept.
120
+ - E2B and Daytona must build it into a prepared image first. A build is slow, may cost money, and leaves a provider-side template or snapshot behind that destroying a sandbox does not delete. So `create --image` and `run --image` only use a build already recorded for the same reference and profile; otherwise they fail with exit 4 before creating anything, naming the command to run:
121
+
122
+ ```sh
123
+ sandbar image build python:3.12 --provider e2b # once
124
+ sandbar run --image python:3.12 --provider e2b -- python3 --version
125
+ ```
126
+
127
+ Pass `--build` to build a missing image during `create` or `run`, or set `"images": { "autoBuild": true }` in `sandbar.json` to always do so. Each reference is built once per profile either way.
128
+ - Vercel takes neither and rejects `--image`.
129
+
130
+ `sandbar image build` records the reference as typed, the profile, the prepared image (bound to the provider account that built it) and the provider-side resources it left. Running it again for a recorded reference does nothing unless you pass `--refresh`, which builds again, for example after a mutable tag such as `python:3.12` moved; pin a digest for an exact image. `image forget` removes the record and prints the resources the provider still holds; the CLI cannot delete them yet, so remove them with the provider's own tools when you no longer need them. A build whose outcome is unknown (exit 6) keeps its record, and `image inspect` asks the provider again how it ended. Providers do not report a resolved digest, so records do not have one.
131
+
132
+ ## State store
133
+
134
+ Sandbox references are kept as one JSON file per sandbox in `~/.config/sandbar/state/sandboxes/<name>.json`, and image records in `images/<name>.json` next to it (override with `--state-dir` or `SANDBAR_STATE_DIR`, relative to the working directory, or `state.dir`, relative to the config file). Records hold references only, never credentials. A name is claimed before the provider call, so a crash or uncertain create leaves a record that `ls` shows. An uncertain create also keeps the SDK's recovery reference: `rm` and `inspect` use it to ask how the original request ended (never resending it) and `rm` destroys the sandbox if one was made; otherwise `rm --force` drops the record. Records are written whole (a temporary file linked or renamed into place) and updates take a short per-record lock, so concurrent CLI processes on one machine never see half-written files; `ls` skips an unreadable record with a warning. The `maxSandboxes` check is best effort under concurrent creates. The store sits behind a small per-record interface (create-if-absent, compare-and-swap update, paged list) so object storage can back it later.
135
+
136
+ ## Not yet
137
+
138
+ Planned for later steps: `--lifetime`/`--idle`/`--port` create flags, recursive `cp`, detached processes and logs, preview URLs, suspend/resume/renew, `gc`, deleting built images (`image rm`), snapshots and volumes, `sandbar adapter add`, an S3-compatible store, KeePassXC and system-keyring references, and MCP mode.
139
+
140
+ ## Development
141
+
142
+ Tests run the real CLI as a subprocess against the fake provider; they make no provider calls:
143
+
144
+ ```sh
145
+ bun test packages/cli
146
+ ```
147
+
148
+ Inside this repository the official adapters are development dependencies of the CLI, so `bun packages/cli/src/main.ts` finds them from any directory without an adapters install.