Skip to content

Latest commit

 

History

History
201 lines (145 loc) · 8.32 KB

File metadata and controls

201 lines (145 loc) · 8.32 KB

xshellz Ruby SDK — API reference

All classes live under the Xshellz module. Every constructor-style class method accepts the same connection options:

  • api_key: — personal access token; falls back to XSHELLZ_API_KEY.
  • api_url: — control-plane base URL; falls back to XSHELLZ_API_URL, then https://api.xshellz.com/v1.
  • http: — HTTP adapter override (anything responding to call(method, url, headers, body) and returning an object with status and body); used by the test suite to fake the network.

A "CommandResult" below is Xshellz::CommandResult — a Struct with stdout (String), stderr (String), exit_code (Integer), and ok? (true when exit_code == 0). A non-zero exit code never raises — it is data, exactly like a local subprocess.


Sandbox — constructors (control plane)

Sandbox.create(name: nil, api_key: nil, api_url: nil, http: nil) { |sbx| ... }

Spawn a new sandbox and return it once it is running. Generates an in-memory ed25519 keypair; the private key (sbx.private_key_openssh) never leaves your process. With a block, the sandbox is yielded and destroyed when the block exits (unless detach was called); the block's value is returned.

Raises AuthError (bad key/scopes/account gate), QuotaError (plan sandbox limit), ApiError (throttle 429, host capacity 503, ...).

Sandbox.get_or_create(name, api_key: nil, api_url: nil, http: nil, private_key: nil, keystore: :default)

Find the account's sandbox named exactly name, or create it. The permanent workflow:

  • Not found → creates the box with that name and saves the generated private key into the keystore.
  • Found → attaches using private_key: if given, else the keystore's saved key. A stopped box is started first. No key anywhere → MissingKeyError (its message says which file was expected).

keystore: accepts :default (~/.xshellz/keys/), a directory String, a Keystore instance, or false/nil to disable persistence (then only create-or-error is possible unless private_key: is passed).

Security note: keystore files are plaintext OpenSSH private keys, 0600, in a 0700 directory. Delete the file and kill the box to revoke.

Sandbox.connect(uuid, private_key:, api_key: nil, api_url: nil, http: nil)

Attach to an existing sandbox by UUID with an explicit OpenSSH private key (the private_key_openssh of the original sandbox). Raises SandboxNotRunningError when the UUID is not among the account's sandboxes.

Sandbox.list(api_key: nil, api_url: nil, http: nil)Array<SandboxInfo>

All of the account's sandboxes. SandboxInfo is a Struct mirroring the wire: uuid, name, status, ssh_command, ssh_host, ssh_port, web_terminal_ready, always_on, trial_hours_remaining, spawned_at, created_at, isolation, gvisor.

Sandbox.get_boxfile(...)String | nil / Sandbox.set_boxfile(manifest, ...)String | nil

Account-level provisioning template (GET/PUT /v1/shells/agent/boxfile). The manifest is seeded into ~/xshellz.box on every newly created box, so destroy+recreate reproduces your package environment. set_boxfile(nil) (or a blank string) clears it; max size 16 KB. Existing boxes are not modified.


Sandbox — properties

Method Returns
uuid sandbox UUID (String)
name sandbox name (String)
status "running", "stopped", ...
ssh_host / ssh_port SSH endpoint
ssh_command copy-paste ssh -p ... root@... line
info the last-known SandboxInfo
private_key_openssh this box's OpenSSH private key (persist to reconnect)
detached? whether detach was called

Sandbox — data plane (SSH/SFTP)

run(command, cwd: nil, env: nil, timeout: nil) { |stream, chunk| ... }CommandResult

Run a shell command and wait. env names are validated; values are exported in the remote shell. The optional block streams (:stdout | :stderr, chunk) as output arrives. Raises SandboxNotRunningError when the box is not running, CommandTimeoutError when timeout seconds elapse.

spawn(command, name: nil)JobHandle

Start command as a detached background process (nohup, stdin from /dev/null) and return immediately. Combined stdout+stderr streams to ~/.xshellz/jobs/<id>.log inside the box; a .pid file is kept alongside so jobs can rebuild handles later. name prefixes the generated job id. Raises Error when the process fails to start.

jobs()Array<JobHandle>

Handles for every job whose pid file still exists in ~/.xshellz/jobs/ (spawned by any process, any time). Probe each with running?/logs.

run_code(language, code, cwd: nil, env: nil, timeout: nil) { ... }CommandResult

Write code to a temp file in the box, execute the matching interpreter, and always delete the temp file (even on timeout/error). Languages: python (runs python3), node, bash, ruby, php — anything else raises UnsupportedLanguageError. Otherwise identical semantics to run.

Files

Method Effect
write_file(path, data) write a String into the box (SFTP)
read_file(path) → String read a file from the box
upload(local_path, remote_path) copy a local file in
download(remote_path, local_path) copy a file out

Sandbox — lifecycle & introspection (control plane)

start()self

Resume an idle-stopped box (POST /{uuid}/start); same /home, same key. Raises SandboxNotRunningError when there is no stopped box to start.

restart()self

Reboot a running box (POST /{uuid}/restart): re-runs the entrypoint, /home preserved. The SSH connection is re-established on the next data-plane call.

stats()SandboxStats

Live resource usage (GET /{uuid}/stats). Struct fields (all mirroring the wire): mem_used_mb, mem_limit_mb, mem_allowed_mb, cpu_percent, cpu_allowed_vcpus, cpu_throttled_periods, pids_current, pids_allowed, disk_used_mb, disk_allowed_mb, net_rx_mb, net_tx_mb, blk_read_mb, blk_write_mb. The *_allowed_* values are your plan's ceilings.

procs()SandboxProcs

Top processes + session info (GET /{uuid}/procs). Struct fields: procs (Array<ProcessInfo>pid, comm, cpu, mem), sessions (active SSH session count), agents (detected agent process names), disk_used_mb, disk_allowed_mb.

terminal_url()String

Mint a fresh signed web-terminal URL (GET /{uuid}/terminal) — a root shell in the browser, no SSH client needed. The embedded token expires after about 1 hour; call again for a fresh URL instead of storing it.

kill() / detach() / refresh() / close()

  • kill — destroy the box (DELETE /{uuid}); idempotent (404 swallowed).
  • detach — keep the box alive when the create block exits.
  • refresh — re-fetch info from the control plane.
  • close — close the SSH connection only; the box keeps running.

JobHandle

Returned by spawn and jobs. Attributes: id, pid, log_path.

Method Effect
running? live probe (kill -0 in the box)
logs(tail_lines: 100) → String last N lines of the job's combined output
stop(grace: 5) SIGTERM, then SIGKILL after grace seconds; returns true when SIGTERM sufficed

Keystore

Local private-key store backing get_or_create. Default directory ~/.xshellz/keys/, one <sanitized-name>.key file per sandbox, 0600.

Method Effect
Keystore.new(dir: Keystore.default_dir) store rooted at dir
path_for(name) → String where name's key lives
save(name, key) → path persist a key (0600)
load(name) → String | nil read a stored key
delete(name) → Boolean remove a stored key

Errors

All inherit Xshellz::Error < StandardError.

Error Raised when
AuthError 401/403 — missing/invalid token, scopes, account gates
QuotaError plan sandbox limit reached or no sandbox entitlement
MissingKeyError get_or_create found the box but no private key
SandboxNotRunningError the box is not running (or not found)
CommandTimeoutError run/run_code timeout: exceeded
UnsupportedLanguageError run_code given an unknown language
ApiError any other 4xx/5xx; has .status and .body