Run commands and files.
Run a command on your box and get its exit code back, leave one running and check on it later, and copy files in either direction, from the CLI or from any script that holds a CLI token. Nothing to install on the box.
Run a command
prized exec -- make testThe command runs on the box under sh -c in your home directory; stdout, stderr, and the exit code come straight back, and your local stdin is connected. A suspended box wakes first. Use a box name before the -- to pick a box other than the configured one.
| Command | What it does |
|---|---|
prized exec -- npm test | Run in your home on the default box; the exit code is npm test's. |
prized exec box-a7f3 --cwd app -- npm test | Run on a named box, in ~/app (--cwd takes a path relative to your home, or an absolute one). |
prized exec --timeout 10m -- ./build.sh | Kill the command after 10 minutes (TERM, then KILL 5 s later): exit 124, and stderr ends with a prized: command timed out after 600s (SIGTERM) line. No limit by default. |
prized exec --env CI=1 --env NODE_ENV=test -- npm test | Add environment variables (repeatable, up to 32). |
prized exec --json -- make | Capture instead of stream: one object with exitCode, signal, timedOut, stdout, stderr. |
- Quote as you would for ssh: the words after
--are joined with spaces and handed tosh -c, soprized exec -- 'cd app && npm test'runs both halves on the box. - The shell is a plain
sh, not your login shell: tools that only your.bashrcputs on PATH needprized exec -- bash -lc 'npm test'. - For
prized exec,--timeoutis the command's time limit, not the network timeout the other commands use the flag for. - Exit code: the remote command's, or ssh's 255 when the box was unreachable. With
--jsonthe object'soksays whether the command ran andexitCodewhat it returned, and the process still exits with that code.timedOutis true only when the time limit ended the command (that trailer line is removed fromstderr); a command that exits 124 on its own isexitCode: 124, timedOut: false.
Detached processes
prized exec --detach -- ./train.sh
# started p_3f9c0a1b2d4e (pid 41213) in /home/ubuntu--detach starts the command and returns at once with a process id. The process outlives your terminal and the ssh session; its output goes to log files on the box.
| Command | What it does |
|---|---|
prized exec --detach -- CMD | Start CMD detached (--cwd and --env apply) and print its id. |
prized exec --status p_3f9c0a1b2d4e | Running or exited, the exit code or signal, start and finish times, and the tail of each log. |
prized exec --status p_… --tail 1m | How much of each log to show: bytes, or 4k, 1m (the default is 16k, the cap 1m). |
prized exec --kill p_3f9c0a1b2d4e | Stop it: TERM to its process group, then KILL if it is still there after 5 s. |
prized exec --ps | Every detached process on the box, running or finished. |
--status, --kill and --ps never wake a suspended box (they exit 5 and say so); running a command does.
Everything about a detached process lives in one directory on the box, so ls, tail -f, and your own scripts see the same truth the CLI does:
File in ~/.prized/processes/<id>/ | Contents |
|---|---|
cmd | The command line as given. |
cwd | The absolute working directory it ran in. |
pid | The process id (also its process group id). |
boot_id | The box's boot id at start. After a reboot (a paused box resumes from one) the pid is not trusted: the process reads as lost and is never signalled. |
started_at | Start time, RFC 3339 UTC. |
stdout.log, stderr.log | The output, appended as it happens. |
exit | Written when the command ends: the exit code, or 128 + signal. |
Log directories are never deleted for you.
rm -r ~/.prized/processes/p_…on the box when you are done with one.
Copy files
prized cp ./notes.md :notes.mdprized cp works like scp: exactly one side is on the box, written BOX:PATH, and a bare :PATH means the configured box. Paths on the box are relative to your home there unless absolute. Files are written whole (a temp file, then a rename), so an interrupted copy never leaves a half-written target.
| Command | What it does |
|---|---|
prized cp ./file.txt : | Copy a file into your home on the box, keeping its name. |
prized cp -r ./site box-a7f3:www | Copy a directory tree (-r is required for directories). |
prized cp box-a7f3:logs/app.log ./ | Copy a file from the box into the current directory. |
prized cp -r :proj/dist ./dist | Copy a directory tree from the box. |
Modes are preserved; symlinks are skipped and counted. A progress line shows on stderr when it is a terminal. For a live view of the whole box in Finder rather than copies, see Mount.
The edge API
Everything above is also an HTTP API, for scripts and agents that hold a CLI token and have no prized installed. The token comes from Dashboard → Workspace → CLI tokens (or prized login --token); the base URL comes from GET /api/v1/me. The bearer must be a CLI token (dcp_…) or a dashboard terminal ticket (dtt_…): monitor tickets and mobile SSH passwords are refused, whatever else they can open.
export PRIZED_TOKEN=dcp_…
EDGE=$(curl -s -H "Authorization: Bearer $PRIZED_TOKEN" https://api.prized.dev/api/v1/me | jq -r .edge.url)Every route is under $EDGE/v1/box/{box}/, where {box} is the box's name or id, with the token as the bearer. Paths on the box are read the way prized cp reads them: relative paths and ~/… are under your home, absolute paths are taken as is. POST /exec wakes a suspended box; every other route never does (they answer box_not_running).
| Route | Body or query | Answer |
|---|---|---|
POST /exec | {command, cwd?, timeoutSeconds? (1 to 600, default 30), env?, detached?} | exec.result: exitCode, signal, stdout, stderr, stdoutTruncated, stderrTruncated, timedOut, startedAt, finishedAt, cwd |
POST /exec with "detached": true | same | exec.started: processId, pid, cwd, startedAt, logPath |
GET /exec | exec.list: processes[] with processId, pid, running, lost, exitCode, signal, startedAt, command | |
GET /exec/{processId}?tail= | tail in bytes (default 16384, max 1048576) | exec.status: the list fields plus finishedAt, cwd, stdout, stderr, stdoutTruncated, stderrTruncated |
DELETE /exec/{processId} | exec.killed: wasRunning, signal (TERM, or KILL when TERM was not enough) | |
GET /files?path= | file.read: path, encoding (utf8 or base64), size, mode, content; files over 1 MiB answer 413 and point at /download | |
PUT /files | {path, content, encoding? (utf8 or base64), mode? ("0644")} | file.written: path, size |
GET /download?path= | The file as application/octet-stream, or a directory as application/x-tar (X-Prized-Content: directory) | |
PUT /upload?path=&mode= | The raw file as the body | file.uploaded: path, size |
Every JSON answer carries ok: true and a type. timedOut is set only when the box's time limit ended the command (signal then says TERM, or KILL when TERM was not enough); a command that exits 124 by itself is exitCode: 124, timedOut: false. Paths are resolved on the box (symlinks followed) and echoed back as path, so you always see what was actually touched. A synchronous run that outgrows 1 MiB per stream keeps the last 1 MiB and sets the Truncated flag; use a detached run and /download on its log for the whole thing.
# run a command and read its exit code
curl -s -X POST "$EDGE/v1/box/box-a7f3/exec" -H "Authorization: Bearer $PRIZED_TOKEN" -H "Content-Type: application/json" -d '{"command":"npm test","cwd":"app","timeoutSeconds":300}' | jq '{exitCode, timedOut, stdout}'
# start something long, then poll it
curl -s -X POST "$EDGE/v1/box/box-a7f3/exec" -H "Authorization: Bearer $PRIZED_TOKEN" -H "Content-Type: application/json" -d '{"command":"./train.sh","detached":true}'
curl -s "$EDGE/v1/box/box-a7f3/exec/p_3f9c0a1b2d4e?tail=4096" -H "Authorization: Bearer $PRIZED_TOKEN"
curl -s -X DELETE "$EDGE/v1/box/box-a7f3/exec/p_3f9c0a1b2d4e" -H "Authorization: Bearer $PRIZED_TOKEN"
# files
curl -s "$EDGE/v1/box/box-a7f3/files?path=app/package.json" -H "Authorization: Bearer $PRIZED_TOKEN" | jq -r .content
curl -s -X PUT "$EDGE/v1/box/box-a7f3/files" -H "Authorization: Bearer $PRIZED_TOKEN" -H "Content-Type: application/json" -d '{"path":"app/.env.test","content":"CI=1\n","mode":"0600"}'
curl -s -X PUT "$EDGE/v1/box/box-a7f3/upload?path=data/train.bin" -H "Authorization: Bearer $PRIZED_TOKEN" --data-binary @train.bin
curl -s "$EDGE/v1/box/box-a7f3/download?path=app/dist" -H "Authorization: Bearer $PRIZED_TOKEN" -o dist.tarErrors
A failed request answers {"ok": false, "error": {"code", "message"}} with one of these codes:
| HTTP | error.code | Meaning |
|---|---|---|
| 401 | unauthorized | No bearer, a token the control plane rejected, or a credential of another class (a monitor ticket, a mobile SSH password). |
| 404 | not_found | No such box on this account, no such process, no such path (or parent directory), or no such route. |
| 405 | method_not_allowed | A known route with the wrong method; Allow lists the right ones. |
| 409 | box_not_running | Any route but POST /exec on a suspended box; the message names the route and how to wake the box. |
| 400 | invalid_request | The body is not the documented shape (bad JSON, empty command, a bad env name or mode). |
| 400 | invalid_timeout | timeoutSeconds outside 1 to 600. |
| 400 | invalid_path | A path with a NUL or newline byte, a directory where a file was expected, or no path. |
| 403 | permission_denied | The box's account cannot read or write that path. |
| 413 | payload_too_large | Over the inline (1 MiB) or transfer (1 GiB) cap. |
| 429 | rate_limited | Too many requests for this token or box; Retry-After says when. |
| 502 | box_unreachable | The box could not be reached, or did not wake in time. |
| 502 | exec_failed | The ssh session or the on-box script failed; the message carries the box's own words. |
| 5xx | control_plane_unavailable | The control plane could not be reached to check the token. |
Limits
| What | Limit |
|---|---|
| Synchronous run | 600 s (the CLI has no limit unless --timeout) |
| Output kept per stream | 1 MiB (the last 1 MiB; flagged as truncated) |
| Inline file read or write | 1 MiB |
| Upload or download | 1 GiB |
| Environment variables per run | 32 |
| Concurrent requests per token, per box | 8 |
| Sustained request rate per token | 20 per second |
A detached process has no time limit. A directory download is bounded by the 1 GiB cap: when it is reached, tar is stopped on the box and the connection is cut, so a truncated archive never looks complete.
Something unclear or out of date?
Sessions
Your shell runs on the box. A network drop or a closed lid ends the connection but not the session; reconnect and you are where you left off, running processes included.
Agents
Start a coding agent, close the laptop, and let it work. An agent is an ordinary process on a machine you already pay for, so nothing it does is metered.