@sandbar-sdk/cli 0.0.0 → 0.3.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 +124 -2
  2. package/dist/main.js +2335 -0
  3. package/package.json +33 -7
package/README.md CHANGED
@@ -1,5 +1,127 @@
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
+ | `cp <src> <dst>` | Copy one file; one side is `<sandbox>:/path` |
33
+ | `cat <sandbox>:/path` | Print a file |
34
+ | `doctor` | Check config, credentials and the connection; creates nothing |
35
+ | `providers` | Official providers, install state and missing credentials |
36
+ | `schema` | Machine-readable description of every command, flag and exit code |
37
+ | `skill [install [--project]]` | Print the agent skill for this CLI, or install it for your coding agents |
38
+ | `completion <zsh\|bash\|fish>` | Print a shell completion script |
39
+
40
+ A `<sandbox>` is a store name, a native id held in the store, or `@file.json` holding a saved sandbox reference.
41
+
42
+ ## Output and exit codes
43
+
44
+ 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`.
45
+
46
+ | Code | Meaning |
47
+ | --- | --- |
48
+ | 0 | success |
49
+ | 1 | other failure (including a taken `--name` or an existing `cp --no-clobber` target) |
50
+ | 2 | usage error |
51
+ | 3 | not found |
52
+ | 4 | unsupported, adapter or password-manager CLI not installed, or `limits.maxSandboxes` reached |
53
+ | 5 | authentication or permission, including a password-manager secret that cannot be read |
54
+ | 6 | outcome unknown: an effect may have happened, inspect before retrying |
55
+ | 7 | retryable before any effect (rate limit, capacity, unavailable) |
56
+ | 8 | partial: some steps confirmed, see `error.known` |
57
+
58
+ `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.
59
+
60
+ 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.
61
+
62
+ ## Shell completion
63
+
64
+ ```sh
65
+ source <(sandbar completion zsh) # add to ~/.zshrc
66
+ source <(sandbar completion bash) # add to ~/.bashrc
67
+ sandbar completion fish > ~/.config/fish/completions/sandbar.fish
68
+ ```
69
+
70
+ Completion covers commands, flags, `--provider` ids, `--profile` names from `sandbar.json`, and sandbox names for `exec`, `sh`, `inspect`, `rm`, `cp` and `cat`. Sandbox names come from the local store, so pressing tab never calls a provider.
71
+
72
+ ## Agent skill
73
+
74
+ 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.
75
+
76
+ ## Configuration
77
+
78
+ `~/.config/sandbar/sandbar.json` (`$XDG_CONFIG_HOME/sandbar`, or `--config` / `SANDBAR_CONFIG`):
79
+
80
+ ```json
81
+ {
82
+ "default": "e2b",
83
+ "profiles": {
84
+ "e2b": { "provider": "e2b", "credentials": { "apiKey": "env:E2B_API_KEY" }, "config": { "network": "internet" } },
85
+ "local": { "module": "./my-adapter.js", "export": "myAdapter", "config": {}, "credentials": {} }
86
+ },
87
+ "state": { "kind": "fs", "dir": "~/.config/sandbar/state" },
88
+ "limits": { "maxSandboxes": null }
89
+ }
90
+ ```
91
+
92
+ - Profile selection: `--profile`, `SANDBAR_PROFILE`, `--provider`, `SANDBAR_PROVIDER`, then `default`.
93
+ - `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.
94
+ - 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`.
95
+ - `limits.maxSandboxes` (or `SANDBAR_MAX_SANDBOXES`) caps how many sandboxes the store may hold. Unset means uncapped.
96
+
97
+ ## Password managers
98
+
99
+ 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.
100
+
101
+ | Reference | Read with | Sign-in |
102
+ | --- | --- | --- |
103
+ | `op://vault/item/field` | `op read` (1Password CLI) | desktop app integration, `op signin` or `OP_SERVICE_ACCOUNT_TOKEN` |
104
+ | `bws://<secret-id>` | `bws secret get` (Bitwarden Secrets Manager CLI) | `BWS_ACCESS_TOKEN` |
105
+ | `pass://path/to/entry` | `pass show` (first line of the entry) | your GPG agent |
106
+
107
+ ```json
108
+ { "profiles": { "e2b": { "provider": "e2b", "credentials": { "apiKey": "op://Dev/E2B/credential" } } } }
109
+ ```
110
+
111
+ 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.
112
+
113
+ ## State store
114
+
115
+ Sandbox references are kept as one JSON file per sandbox in `~/.config/sandbar/state/sandboxes/<name>.json` (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.
116
+
117
+ ## Not yet
118
+
119
+ Planned for later steps: `--lifetime`/`--idle`/`--port` create flags, recursive `cp`, detached processes and logs, preview URLs, suspend/resume/renew, `gc`, image builds and records, snapshots and volumes, `sandbar adapter add`, an S3-compatible store, KeePassXC and system-keyring references, and MCP mode. `--image` is passed to the adapter as an OCI image; providers that need a build first reject it before creating anything.
120
+
121
+ ## Development
122
+
123
+ Tests run the real CLI as a subprocess against the fake provider; they make no provider calls:
124
+
125
+ ```sh
126
+ bun test packages/cli
127
+ ```