urun-cli 0.4.0__tar.gz → 0.5.0__tar.gz
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.
- urun_cli-0.5.0/CHANGELOG.md +70 -0
- urun_cli-0.5.0/PKG-INFO +321 -0
- urun_cli-0.5.0/README.md +299 -0
- {urun_cli-0.4.0 → urun_cli-0.5.0}/pyproject.toml +1 -1
- urun_cli-0.5.0/src/urun/api.py +446 -0
- urun_cli-0.5.0/src/urun/cli.py +1159 -0
- {urun_cli-0.4.0 → urun_cli-0.5.0}/src/urun/config.py +37 -0
- {urun_cli-0.4.0 → urun_cli-0.5.0}/src/urun/discovery.py +14 -1
- urun_cli-0.5.0/src/urun/retry.py +169 -0
- urun_cli-0.4.0/CHANGELOG.md +0 -24
- urun_cli-0.4.0/PKG-INFO +0 -154
- urun_cli-0.4.0/README.md +0 -132
- urun_cli-0.4.0/src/urun/api.py +0 -234
- urun_cli-0.4.0/src/urun/cli.py +0 -410
- {urun_cli-0.4.0 → urun_cli-0.5.0}/.gitignore +0 -0
- {urun_cli-0.4.0 → urun_cli-0.5.0}/LICENSE +0 -0
- {urun_cli-0.4.0 → urun_cli-0.5.0}/SECURITY.md +0 -0
- {urun_cli-0.4.0 → urun_cli-0.5.0}/src/urun/__init__.py +0 -0
- {urun_cli-0.4.0 → urun_cli-0.5.0}/src/urun/deps.py +0 -0
- {urun_cli-0.4.0 → urun_cli-0.5.0}/src/urun/errors.py +0 -0
- {urun_cli-0.4.0 → urun_cli-0.5.0}/src/urun/manifest.py +0 -0
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## Unreleased
|
|
4
|
+
|
|
5
|
+
- Make `urun deploy` resilient to transient network blips. The manifest/blob
|
|
6
|
+
push and the "Waiting for build to complete" build-status poll now retry on
|
|
7
|
+
transient failures (read/connect timeouts, connection resets, 5xx, 429) with
|
|
8
|
+
exponential backoff + jitter (base 1s, factor 2, cap 30s) instead of fatally
|
|
9
|
+
exiting on the first `network error: The read operation timed out`. A single
|
|
10
|
+
failed status read backs off and re-polls — the build continues server-side —
|
|
11
|
+
while a real build failure or a 4xx auth/validation error still fails fast and
|
|
12
|
+
loud. Every retry is logged (`transient ...; retrying in Ns`) so backoff is
|
|
13
|
+
visible, never silent. New `urun.retry` module centralizes the
|
|
14
|
+
transient-vs-terminal classification and backoff policy.
|
|
15
|
+
|
|
16
|
+
- Add experimental `urun list apps` subcommand. Reads from a new control-plane
|
|
17
|
+
endpoint `GET /apps` (server-side edge function not yet shipped). The STATUS
|
|
18
|
+
column projects the app's current lifecycle phase to one of `provisioning`,
|
|
19
|
+
`ready`, `pending`, `paused`, or `failed`; see the README or
|
|
20
|
+
`urun list apps --help` for the full mapping. Supports `--json` for the raw
|
|
21
|
+
payload.
|
|
22
|
+
- Add experimental `urun list sessions` subcommand. Lists live and historical
|
|
23
|
+
sessions for the org, with per-row STARTED / DURATION / STATE / DETAIL.
|
|
24
|
+
Newest sessions print at the bottom of the table (tail-friendly). Supports
|
|
25
|
+
`--limit` (default 100), `--state` (filter to a single backend
|
|
26
|
+
status), and `--json`. Reads from a new control-plane endpoint
|
|
27
|
+
`GET /sessions` (server-side edge function not yet shipped).
|
|
28
|
+
- Add experimental `urun list compute` subcommand. Lists the compute slices
|
|
29
|
+
the org currently has provisioned, broken down per (app, function,
|
|
30
|
+
compute_shape) with INSTANCES (allocated/ready), GPU UNITS
|
|
31
|
+
(allocated/ready), live SESSIONS count, and snapshot AGE. Slices with no
|
|
32
|
+
provisioned GPU units are omitted server-side so the listing reflects
|
|
33
|
+
what is running right now (`urun list apps` for the full deployment
|
|
34
|
+
catalogue; `urun list sessions` for history). Supports `--limit`
|
|
35
|
+
(default 100) and `--json`. Reads from a new control-plane
|
|
36
|
+
endpoint `GET /compute` (server-side edge function not yet shipped).
|
|
37
|
+
- Add experimental `urun app status|scale|delete|activate` lifecycle commands
|
|
38
|
+
for managing a single deployed app (addressed by slug, org-scoped via the API
|
|
39
|
+
key). `app status` shows the build/deployment/capacity/live-session rollup;
|
|
40
|
+
`app scale --replicas N` sets the deployment's desired replica count (use 0 to
|
|
41
|
+
drain; GPU/shape stay fixed at deploy time, so only `--replicas` is exposed);
|
|
42
|
+
`app delete` (alias `app rm`) retires an app — driving it to `paused`/
|
|
43
|
+
`disabled` so the control-plane materializer stops recreating its runtime —
|
|
44
|
+
with an interactive confirmation (`--yes` to skip); `app activate` reverses a
|
|
45
|
+
retire. All accept `--environment` (default `prod`) and `--json`. Backed by a
|
|
46
|
+
new server-side `app` edge function (`GET /app/<slug>`,
|
|
47
|
+
`POST /app/<slug>/scale`, `DELETE /app/<slug>`, `POST /app/<slug>/activate`).
|
|
48
|
+
|
|
49
|
+
## 0.3.0
|
|
50
|
+
|
|
51
|
+
- Implement the `urun org` / `urun org id` command: print the caller's org id
|
|
52
|
+
(resolved via the control-plane org-config endpoint).
|
|
53
|
+
- Implement `urun auth jwks set`: register the org's trusted JWKS for federated
|
|
54
|
+
identity, via `--jwks-url` or `--jwks-json` (file or stdin), with optional
|
|
55
|
+
`--issuer`/`--audience`. This registers a trust relationship only; JWTs are
|
|
56
|
+
issued out of band and the CLI never mints, fetches, or stores one.
|
|
57
|
+
- Add `ApiClient.register_trusted_jwks`, calling `POST /org-config/trusted-jwks`.
|
|
58
|
+
|
|
59
|
+
## 0.2.0
|
|
60
|
+
|
|
61
|
+
- Bumped past 0.1.1/0.1.2 because both filenames were occupied by previously-deleted PyPI artifacts.
|
|
62
|
+
- Drop the org-id requirement from `urun deploy`; authentication now relies solely on the API key.
|
|
63
|
+
- Add `urun login`, `urun run`, and `urun org`/`urun auth` subcommands.
|
|
64
|
+
|
|
65
|
+
## 0.1.0
|
|
66
|
+
|
|
67
|
+
- Initial public `urun deploy` CLI.
|
|
68
|
+
- Deploy from a Python app file with local Python imports included automatically.
|
|
69
|
+
- Source manifest generation, dependency declaration upload, and API deploy flow.
|
|
70
|
+
- Public docs for the config-free v1 CLI surface.
|
urun_cli-0.5.0/PKG-INFO
ADDED
|
@@ -0,0 +1,321 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: urun-cli
|
|
3
|
+
Version: 0.5.0
|
|
4
|
+
Summary: End-user CLI for deploying apps to urun
|
|
5
|
+
Project-URL: Homepage, https://urun.sh
|
|
6
|
+
Project-URL: Repository, https://github.com/urun-sh/urun-cli
|
|
7
|
+
Project-URL: Issues, https://github.com/urun-sh/urun-cli/issues
|
|
8
|
+
Author: urun
|
|
9
|
+
License-Expression: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: cli,deploy,urun
|
|
12
|
+
Classifier: Development Status :: 3 - Alpha
|
|
13
|
+
Classifier: Environment :: Console
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Topic :: Software Development :: Build Tools
|
|
20
|
+
Requires-Python: >=3.11
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
|
|
23
|
+
# urun CLI
|
|
24
|
+
|
|
25
|
+
Deploy Python apps to urun from your terminal.
|
|
26
|
+
|
|
27
|
+
[](https://pypi.org/project/urun-cli/)
|
|
28
|
+
[](https://pypi.org/project/urun-cli/)
|
|
29
|
+
|
|
30
|
+
## Install
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
uv tool install urun-cli
|
|
34
|
+
# or
|
|
35
|
+
pip install urun-cli
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
The package installs the `urun` command:
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
urun --version
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
For one-off `uvx` usage:
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
uvx --from urun-cli urun --version
|
|
48
|
+
# or the package-matching command alias
|
|
49
|
+
uvx urun-cli --version
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Quick start
|
|
53
|
+
|
|
54
|
+
Today, an operator manually vends an org-scoped deploy API key. Save it locally
|
|
55
|
+
with `urun login`:
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
urun login --api-key urun_<32hex>
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
`urun login` verifies the key with the urun API and stores credentials for later
|
|
62
|
+
commands. The future browser-based login flow is not available in this CLI
|
|
63
|
+
release.
|
|
64
|
+
|
|
65
|
+
For CI or one-off commands, you can still use the environment variable:
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
export URUN_API_KEY=urun_<32hex>
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Create `app.py`:
|
|
72
|
+
|
|
73
|
+
```python
|
|
74
|
+
import urun
|
|
75
|
+
from urun import App
|
|
76
|
+
|
|
77
|
+
app = App("hello-h100")
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
@app.function(gpus="h100:1")
|
|
81
|
+
def hello(ctx: urun.Context):
|
|
82
|
+
print(f"running on {ctx.device}")
|
|
83
|
+
return {"device": str(ctx.device)}
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Run it:
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
urun run app.py
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
In this release, `urun run` uses the same deploy pipeline as `urun deploy`.
|
|
93
|
+
`deploy` remains available as the lower-level command while the full
|
|
94
|
+
deploy/run/monitor workflow is being built.
|
|
95
|
+
|
|
96
|
+
## Inspect apps
|
|
97
|
+
|
|
98
|
+
List every app deployed in your org and its current status:
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
urun list apps
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Sample output:
|
|
105
|
+
|
|
106
|
+
```text
|
|
107
|
+
APP FUNCTION COMPUTE STATUS RELEASE DETAIL
|
|
108
|
+
causal-forcing-stream generate_video h100:1 ready fa61f31b0961 0/1 GPU units in use
|
|
109
|
+
queued-app warmup a10:1 provisioning 000000000000 building
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
The STATUS column is one of `provisioning`, `ready`, `pending`, `paused`, or
|
|
113
|
+
`failed`. It is derived from three backend signals reporting on sequential
|
|
114
|
+
lifecycle phases:
|
|
115
|
+
|
|
116
|
+
| Build (S3 status) | Promotion (`app_deployments`) | Capacity (`function_ready`) | STATUS |
|
|
117
|
+
| ---------------------- | ----------------------------- | --------------------------- | -------------- |
|
|
118
|
+
| `queued` \| `building` | (no row yet) | - | `provisioning` |
|
|
119
|
+
| `failed` | (no row yet) | - | `failed` |
|
|
120
|
+
| `ready` | `active` | `false` | `pending` |
|
|
121
|
+
| `ready` | `active` | `true` | `ready` |
|
|
122
|
+
| `ready` | `paused` | (irrelevant) | `paused` |
|
|
123
|
+
| `ready` | `failed` | (irrelevant) | `failed` |
|
|
124
|
+
|
|
125
|
+
The DETAIL column carries the disambiguating signal (raw build state, error
|
|
126
|
+
message, `ready_reason`, or in-use GPU counts). Pass `--json` for the raw
|
|
127
|
+
payload.
|
|
128
|
+
|
|
129
|
+
This command is **experimental** and requires the server-side `GET /apps`
|
|
130
|
+
endpoint, which is in development.
|
|
131
|
+
|
|
132
|
+
## Inspect sessions
|
|
133
|
+
|
|
134
|
+
List live and historical sessions in your org. Newest sessions appear at the
|
|
135
|
+
**bottom** of the table so the command works well with `tail`:
|
|
136
|
+
|
|
137
|
+
```bash
|
|
138
|
+
urun list sessions
|
|
139
|
+
urun list sessions | tail -20
|
|
140
|
+
urun list sessions --state failed
|
|
141
|
+
urun list sessions --limit 500
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
Sample output:
|
|
145
|
+
|
|
146
|
+
```text
|
|
147
|
+
ID APP FUNCTION SHAPE STARTED DURATION STATE DETAIL
|
|
148
|
+
2cc8a91f4b3d helios world_gen h100:4 2026-06-02 10:55 UTC 44s failed no_capacity
|
|
149
|
+
3f0017daee01 helios world_gen h100:1 2026-06-02 11:08 UTC 18m43s completed client_disconnect
|
|
150
|
+
4a1c886e2d0a causal-forcing-… generate_video h100:1 2026-06-02 14:21 UTC 3m12s live -
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
The STATE column maps the raw backend status to a user-friendly label:
|
|
154
|
+
|
|
155
|
+
| Backend status | STATE |
|
|
156
|
+
| -------------- | ----------- |
|
|
157
|
+
| `allocated` | `starting` |
|
|
158
|
+
| `connected` | `live` |
|
|
159
|
+
| `closed` | `completed` |
|
|
160
|
+
| `failed` | `failed` |
|
|
161
|
+
| `cancelled` | `cancelled` |
|
|
162
|
+
|
|
163
|
+
DURATION is computed from `allocated_at` to `closed_at` for terminal sessions,
|
|
164
|
+
or `allocated_at` to now for live ones. DETAIL carries `close_reason` when
|
|
165
|
+
present. Pass `--json` for the raw payload (full IDs, ISO timestamps, all
|
|
166
|
+
fields).
|
|
167
|
+
|
|
168
|
+
Pass `--limit` to control how many rows are fetched (default 100).
|
|
169
|
+
|
|
170
|
+
This command is **experimental** and requires the server-side `GET /sessions`
|
|
171
|
+
endpoint, which is in development.
|
|
172
|
+
|
|
173
|
+
## Inspect active compute
|
|
174
|
+
|
|
175
|
+
List the compute slices your org currently has provisioned:
|
|
176
|
+
|
|
177
|
+
```bash
|
|
178
|
+
urun list compute
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
Sample output:
|
|
182
|
+
|
|
183
|
+
```text
|
|
184
|
+
APP FUNCTION SHAPE INSTANCES GPU UNITS SESSIONS AGE
|
|
185
|
+
causal-forcing-stream generate_video h100:1 1/2 1/2 1 12s
|
|
186
|
+
helios world_gen h100:4 0/1 0/4 0 3m
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
Each row is one actively provisioned `(app, function, compute_shape)`
|
|
190
|
+
slice. `INSTANCES` and `GPU UNITS` show `<allocated>/<provisioned>` — a
|
|
191
|
+
row with `0/1` is an idle warm runtime with no active sessions on it.
|
|
192
|
+
`SESSIONS` is the live session count. `AGE` is how stale the capacity
|
|
193
|
+
snapshot is; very old ages may indicate the runtime is no longer
|
|
194
|
+
reporting.
|
|
195
|
+
|
|
196
|
+
Slices with no provisioned capacity are omitted, so this command answers
|
|
197
|
+
"what is running right now". For the full deployment catalogue
|
|
198
|
+
(including paused / failed / unprovisioned apps) use `urun list apps`;
|
|
199
|
+
for historical or in-flight sessions use `urun list sessions`.
|
|
200
|
+
|
|
201
|
+
Pass `--limit` to control how many rows are fetched (default 100).
|
|
202
|
+
|
|
203
|
+
This command is **experimental** and requires the server-side `GET /compute`
|
|
204
|
+
endpoint, which is in development.
|
|
205
|
+
|
|
206
|
+
## Manage apps
|
|
207
|
+
|
|
208
|
+
Manage the lifecycle of a single deployed app. The app is addressed by its
|
|
209
|
+
slug (the name shown under `APP` in `urun list apps`); every operation is
|
|
210
|
+
org-scoped via your API key.
|
|
211
|
+
|
|
212
|
+
Show detailed status for one app (the single-app complement to `list apps`):
|
|
213
|
+
|
|
214
|
+
```bash
|
|
215
|
+
urun app status lingbot
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
```text
|
|
219
|
+
App: lingbot
|
|
220
|
+
Name: LingBot
|
|
221
|
+
Environment: prod
|
|
222
|
+
App status: active
|
|
223
|
+
Deployment: active
|
|
224
|
+
Desired replicas: 2
|
|
225
|
+
Function: handle_lingbot_runtime
|
|
226
|
+
Compute: b200:4
|
|
227
|
+
GPU: 4 x b200
|
|
228
|
+
Release: 1c6d6287abcd
|
|
229
|
+
Live sessions: 1
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
Scale an app's runtime replica count (the backend's scaling knob; the
|
|
233
|
+
control plane turns it into the runtime StatefulSet replica count):
|
|
234
|
+
|
|
235
|
+
```bash
|
|
236
|
+
urun app scale lingbot --replicas 3
|
|
237
|
+
urun app scale lingbot --replicas 0 # drain to zero without retiring
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
GPU count and compute shape are fixed at deploy time per release (set via
|
|
241
|
+
`@app.function`), so `scale` intentionally exposes only `--replicas`.
|
|
242
|
+
|
|
243
|
+
Retire an app so the control plane stops running it (drives the deployment
|
|
244
|
+
to `paused` and the app to `disabled`, so the materializer stops recreating
|
|
245
|
+
its runtime). This is the clean, reversible, API-driven alternative to a
|
|
246
|
+
manual database edit:
|
|
247
|
+
|
|
248
|
+
```bash
|
|
249
|
+
urun app delete lingbot-handle # prompts for confirmation
|
|
250
|
+
urun app delete lingbot-handle --yes # or urun app rm lingbot-handle --yes
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
Reverse a retire and bring the app back online:
|
|
254
|
+
|
|
255
|
+
```bash
|
|
256
|
+
urun app activate lingbot-handle
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
All `app` subcommands accept `--environment` (default `prod`) and `--json`.
|
|
260
|
+
These commands are **experimental** and require the server-side `app`
|
|
261
|
+
lifecycle endpoint, which is in development.
|
|
262
|
+
|
|
263
|
+
## What gets deployed
|
|
264
|
+
|
|
265
|
+
`urun deploy` creates a source manifest from your Python entrypoint:
|
|
266
|
+
|
|
267
|
+
| Entrypoint | Included source |
|
|
268
|
+
| --- | --- |
|
|
269
|
+
| `urun deploy app.py` | `app.py` and local Python files it imports |
|
|
270
|
+
|
|
271
|
+
Dependencies are declared in your urun app code. Project-level files such as
|
|
272
|
+
`pyproject.toml` and `requirements.txt` are not uploaded as dependency
|
|
273
|
+
declarations by the CLI.
|
|
274
|
+
|
|
275
|
+
Generated/cache content such as `.git`, dotfiles, `__pycache__`, and `.pyc`
|
|
276
|
+
files is excluded. Add `.urunignore` to exclude additional paths.
|
|
277
|
+
|
|
278
|
+
Non-Python assets such as templates, static files, and data files are not
|
|
279
|
+
auto-included yet.
|
|
280
|
+
|
|
281
|
+
## Common options
|
|
282
|
+
|
|
283
|
+
Shared by `run` and `deploy`:
|
|
284
|
+
|
|
285
|
+
| Option | Description |
|
|
286
|
+
| --- | --- |
|
|
287
|
+
| `--name` | Override the derived app name. |
|
|
288
|
+
| `--api-url` | Override the API URL; defaults to `URUN_API_URL`, saved login credentials, or `https://api.urun.sh/v1`. |
|
|
289
|
+
| `--api-key` | Deploy API key; defaults to `URUN_API_KEY` or saved login credentials. |
|
|
290
|
+
| `--no-wait` | Finalize but do not poll for readiness. |
|
|
291
|
+
| `--poll-interval`, `--timeout` | Control readiness polling. |
|
|
292
|
+
|
|
293
|
+
## Troubleshooting
|
|
294
|
+
|
|
295
|
+
| Error | Fix |
|
|
296
|
+
| --- | --- |
|
|
297
|
+
| `missing API key` | Run `urun login`, set `URUN_API_KEY`, or pass `--api-key`. |
|
|
298
|
+
| `invalid API key format` | Use `urun_<32 lowercase hex chars>`. |
|
|
299
|
+
| `entrypoint not found` | Run from the project root or pass the entrypoint path. |
|
|
300
|
+
| `path is outside the project root` | Move the file under the project before deploying. |
|
|
301
|
+
| Expected files are missing | Import local Python files from `app.py`; non-Python assets are not auto-included yet. |
|
|
302
|
+
|
|
303
|
+
## Development
|
|
304
|
+
|
|
305
|
+
Contributing and test instructions are in [CONTRIBUTING.md](CONTRIBUTING.md).
|
|
306
|
+
|
|
307
|
+
## License
|
|
308
|
+
|
|
309
|
+
MIT.
|
|
310
|
+
|
|
311
|
+
## Development environment
|
|
312
|
+
|
|
313
|
+
This repo has a Nix/direnv/devcontainer baseline:
|
|
314
|
+
|
|
315
|
+
```bash
|
|
316
|
+
direnv allow
|
|
317
|
+
just sync
|
|
318
|
+
just check
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
Use VS Code Dev Containers to open the repository with the same toolchain in a container. Copy `devcontainer.env.example` to `.devcontainer.env` if you need to pass local git identity or other non-secret development settings into the container.
|
urun_cli-0.5.0/README.md
ADDED
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
# urun CLI
|
|
2
|
+
|
|
3
|
+
Deploy Python apps to urun from your terminal.
|
|
4
|
+
|
|
5
|
+
[](https://pypi.org/project/urun-cli/)
|
|
6
|
+
[](https://pypi.org/project/urun-cli/)
|
|
7
|
+
|
|
8
|
+
## Install
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
uv tool install urun-cli
|
|
12
|
+
# or
|
|
13
|
+
pip install urun-cli
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
The package installs the `urun` command:
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
urun --version
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
For one-off `uvx` usage:
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
uvx --from urun-cli urun --version
|
|
26
|
+
# or the package-matching command alias
|
|
27
|
+
uvx urun-cli --version
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Quick start
|
|
31
|
+
|
|
32
|
+
Today, an operator manually vends an org-scoped deploy API key. Save it locally
|
|
33
|
+
with `urun login`:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
urun login --api-key urun_<32hex>
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
`urun login` verifies the key with the urun API and stores credentials for later
|
|
40
|
+
commands. The future browser-based login flow is not available in this CLI
|
|
41
|
+
release.
|
|
42
|
+
|
|
43
|
+
For CI or one-off commands, you can still use the environment variable:
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
export URUN_API_KEY=urun_<32hex>
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Create `app.py`:
|
|
50
|
+
|
|
51
|
+
```python
|
|
52
|
+
import urun
|
|
53
|
+
from urun import App
|
|
54
|
+
|
|
55
|
+
app = App("hello-h100")
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
@app.function(gpus="h100:1")
|
|
59
|
+
def hello(ctx: urun.Context):
|
|
60
|
+
print(f"running on {ctx.device}")
|
|
61
|
+
return {"device": str(ctx.device)}
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Run it:
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
urun run app.py
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
In this release, `urun run` uses the same deploy pipeline as `urun deploy`.
|
|
71
|
+
`deploy` remains available as the lower-level command while the full
|
|
72
|
+
deploy/run/monitor workflow is being built.
|
|
73
|
+
|
|
74
|
+
## Inspect apps
|
|
75
|
+
|
|
76
|
+
List every app deployed in your org and its current status:
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
urun list apps
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Sample output:
|
|
83
|
+
|
|
84
|
+
```text
|
|
85
|
+
APP FUNCTION COMPUTE STATUS RELEASE DETAIL
|
|
86
|
+
causal-forcing-stream generate_video h100:1 ready fa61f31b0961 0/1 GPU units in use
|
|
87
|
+
queued-app warmup a10:1 provisioning 000000000000 building
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
The STATUS column is one of `provisioning`, `ready`, `pending`, `paused`, or
|
|
91
|
+
`failed`. It is derived from three backend signals reporting on sequential
|
|
92
|
+
lifecycle phases:
|
|
93
|
+
|
|
94
|
+
| Build (S3 status) | Promotion (`app_deployments`) | Capacity (`function_ready`) | STATUS |
|
|
95
|
+
| ---------------------- | ----------------------------- | --------------------------- | -------------- |
|
|
96
|
+
| `queued` \| `building` | (no row yet) | - | `provisioning` |
|
|
97
|
+
| `failed` | (no row yet) | - | `failed` |
|
|
98
|
+
| `ready` | `active` | `false` | `pending` |
|
|
99
|
+
| `ready` | `active` | `true` | `ready` |
|
|
100
|
+
| `ready` | `paused` | (irrelevant) | `paused` |
|
|
101
|
+
| `ready` | `failed` | (irrelevant) | `failed` |
|
|
102
|
+
|
|
103
|
+
The DETAIL column carries the disambiguating signal (raw build state, error
|
|
104
|
+
message, `ready_reason`, or in-use GPU counts). Pass `--json` for the raw
|
|
105
|
+
payload.
|
|
106
|
+
|
|
107
|
+
This command is **experimental** and requires the server-side `GET /apps`
|
|
108
|
+
endpoint, which is in development.
|
|
109
|
+
|
|
110
|
+
## Inspect sessions
|
|
111
|
+
|
|
112
|
+
List live and historical sessions in your org. Newest sessions appear at the
|
|
113
|
+
**bottom** of the table so the command works well with `tail`:
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
urun list sessions
|
|
117
|
+
urun list sessions | tail -20
|
|
118
|
+
urun list sessions --state failed
|
|
119
|
+
urun list sessions --limit 500
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
Sample output:
|
|
123
|
+
|
|
124
|
+
```text
|
|
125
|
+
ID APP FUNCTION SHAPE STARTED DURATION STATE DETAIL
|
|
126
|
+
2cc8a91f4b3d helios world_gen h100:4 2026-06-02 10:55 UTC 44s failed no_capacity
|
|
127
|
+
3f0017daee01 helios world_gen h100:1 2026-06-02 11:08 UTC 18m43s completed client_disconnect
|
|
128
|
+
4a1c886e2d0a causal-forcing-… generate_video h100:1 2026-06-02 14:21 UTC 3m12s live -
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
The STATE column maps the raw backend status to a user-friendly label:
|
|
132
|
+
|
|
133
|
+
| Backend status | STATE |
|
|
134
|
+
| -------------- | ----------- |
|
|
135
|
+
| `allocated` | `starting` |
|
|
136
|
+
| `connected` | `live` |
|
|
137
|
+
| `closed` | `completed` |
|
|
138
|
+
| `failed` | `failed` |
|
|
139
|
+
| `cancelled` | `cancelled` |
|
|
140
|
+
|
|
141
|
+
DURATION is computed from `allocated_at` to `closed_at` for terminal sessions,
|
|
142
|
+
or `allocated_at` to now for live ones. DETAIL carries `close_reason` when
|
|
143
|
+
present. Pass `--json` for the raw payload (full IDs, ISO timestamps, all
|
|
144
|
+
fields).
|
|
145
|
+
|
|
146
|
+
Pass `--limit` to control how many rows are fetched (default 100).
|
|
147
|
+
|
|
148
|
+
This command is **experimental** and requires the server-side `GET /sessions`
|
|
149
|
+
endpoint, which is in development.
|
|
150
|
+
|
|
151
|
+
## Inspect active compute
|
|
152
|
+
|
|
153
|
+
List the compute slices your org currently has provisioned:
|
|
154
|
+
|
|
155
|
+
```bash
|
|
156
|
+
urun list compute
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
Sample output:
|
|
160
|
+
|
|
161
|
+
```text
|
|
162
|
+
APP FUNCTION SHAPE INSTANCES GPU UNITS SESSIONS AGE
|
|
163
|
+
causal-forcing-stream generate_video h100:1 1/2 1/2 1 12s
|
|
164
|
+
helios world_gen h100:4 0/1 0/4 0 3m
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
Each row is one actively provisioned `(app, function, compute_shape)`
|
|
168
|
+
slice. `INSTANCES` and `GPU UNITS` show `<allocated>/<provisioned>` — a
|
|
169
|
+
row with `0/1` is an idle warm runtime with no active sessions on it.
|
|
170
|
+
`SESSIONS` is the live session count. `AGE` is how stale the capacity
|
|
171
|
+
snapshot is; very old ages may indicate the runtime is no longer
|
|
172
|
+
reporting.
|
|
173
|
+
|
|
174
|
+
Slices with no provisioned capacity are omitted, so this command answers
|
|
175
|
+
"what is running right now". For the full deployment catalogue
|
|
176
|
+
(including paused / failed / unprovisioned apps) use `urun list apps`;
|
|
177
|
+
for historical or in-flight sessions use `urun list sessions`.
|
|
178
|
+
|
|
179
|
+
Pass `--limit` to control how many rows are fetched (default 100).
|
|
180
|
+
|
|
181
|
+
This command is **experimental** and requires the server-side `GET /compute`
|
|
182
|
+
endpoint, which is in development.
|
|
183
|
+
|
|
184
|
+
## Manage apps
|
|
185
|
+
|
|
186
|
+
Manage the lifecycle of a single deployed app. The app is addressed by its
|
|
187
|
+
slug (the name shown under `APP` in `urun list apps`); every operation is
|
|
188
|
+
org-scoped via your API key.
|
|
189
|
+
|
|
190
|
+
Show detailed status for one app (the single-app complement to `list apps`):
|
|
191
|
+
|
|
192
|
+
```bash
|
|
193
|
+
urun app status lingbot
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
```text
|
|
197
|
+
App: lingbot
|
|
198
|
+
Name: LingBot
|
|
199
|
+
Environment: prod
|
|
200
|
+
App status: active
|
|
201
|
+
Deployment: active
|
|
202
|
+
Desired replicas: 2
|
|
203
|
+
Function: handle_lingbot_runtime
|
|
204
|
+
Compute: b200:4
|
|
205
|
+
GPU: 4 x b200
|
|
206
|
+
Release: 1c6d6287abcd
|
|
207
|
+
Live sessions: 1
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
Scale an app's runtime replica count (the backend's scaling knob; the
|
|
211
|
+
control plane turns it into the runtime StatefulSet replica count):
|
|
212
|
+
|
|
213
|
+
```bash
|
|
214
|
+
urun app scale lingbot --replicas 3
|
|
215
|
+
urun app scale lingbot --replicas 0 # drain to zero without retiring
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
GPU count and compute shape are fixed at deploy time per release (set via
|
|
219
|
+
`@app.function`), so `scale` intentionally exposes only `--replicas`.
|
|
220
|
+
|
|
221
|
+
Retire an app so the control plane stops running it (drives the deployment
|
|
222
|
+
to `paused` and the app to `disabled`, so the materializer stops recreating
|
|
223
|
+
its runtime). This is the clean, reversible, API-driven alternative to a
|
|
224
|
+
manual database edit:
|
|
225
|
+
|
|
226
|
+
```bash
|
|
227
|
+
urun app delete lingbot-handle # prompts for confirmation
|
|
228
|
+
urun app delete lingbot-handle --yes # or urun app rm lingbot-handle --yes
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
Reverse a retire and bring the app back online:
|
|
232
|
+
|
|
233
|
+
```bash
|
|
234
|
+
urun app activate lingbot-handle
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
All `app` subcommands accept `--environment` (default `prod`) and `--json`.
|
|
238
|
+
These commands are **experimental** and require the server-side `app`
|
|
239
|
+
lifecycle endpoint, which is in development.
|
|
240
|
+
|
|
241
|
+
## What gets deployed
|
|
242
|
+
|
|
243
|
+
`urun deploy` creates a source manifest from your Python entrypoint:
|
|
244
|
+
|
|
245
|
+
| Entrypoint | Included source |
|
|
246
|
+
| --- | --- |
|
|
247
|
+
| `urun deploy app.py` | `app.py` and local Python files it imports |
|
|
248
|
+
|
|
249
|
+
Dependencies are declared in your urun app code. Project-level files such as
|
|
250
|
+
`pyproject.toml` and `requirements.txt` are not uploaded as dependency
|
|
251
|
+
declarations by the CLI.
|
|
252
|
+
|
|
253
|
+
Generated/cache content such as `.git`, dotfiles, `__pycache__`, and `.pyc`
|
|
254
|
+
files is excluded. Add `.urunignore` to exclude additional paths.
|
|
255
|
+
|
|
256
|
+
Non-Python assets such as templates, static files, and data files are not
|
|
257
|
+
auto-included yet.
|
|
258
|
+
|
|
259
|
+
## Common options
|
|
260
|
+
|
|
261
|
+
Shared by `run` and `deploy`:
|
|
262
|
+
|
|
263
|
+
| Option | Description |
|
|
264
|
+
| --- | --- |
|
|
265
|
+
| `--name` | Override the derived app name. |
|
|
266
|
+
| `--api-url` | Override the API URL; defaults to `URUN_API_URL`, saved login credentials, or `https://api.urun.sh/v1`. |
|
|
267
|
+
| `--api-key` | Deploy API key; defaults to `URUN_API_KEY` or saved login credentials. |
|
|
268
|
+
| `--no-wait` | Finalize but do not poll for readiness. |
|
|
269
|
+
| `--poll-interval`, `--timeout` | Control readiness polling. |
|
|
270
|
+
|
|
271
|
+
## Troubleshooting
|
|
272
|
+
|
|
273
|
+
| Error | Fix |
|
|
274
|
+
| --- | --- |
|
|
275
|
+
| `missing API key` | Run `urun login`, set `URUN_API_KEY`, or pass `--api-key`. |
|
|
276
|
+
| `invalid API key format` | Use `urun_<32 lowercase hex chars>`. |
|
|
277
|
+
| `entrypoint not found` | Run from the project root or pass the entrypoint path. |
|
|
278
|
+
| `path is outside the project root` | Move the file under the project before deploying. |
|
|
279
|
+
| Expected files are missing | Import local Python files from `app.py`; non-Python assets are not auto-included yet. |
|
|
280
|
+
|
|
281
|
+
## Development
|
|
282
|
+
|
|
283
|
+
Contributing and test instructions are in [CONTRIBUTING.md](CONTRIBUTING.md).
|
|
284
|
+
|
|
285
|
+
## License
|
|
286
|
+
|
|
287
|
+
MIT.
|
|
288
|
+
|
|
289
|
+
## Development environment
|
|
290
|
+
|
|
291
|
+
This repo has a Nix/direnv/devcontainer baseline:
|
|
292
|
+
|
|
293
|
+
```bash
|
|
294
|
+
direnv allow
|
|
295
|
+
just sync
|
|
296
|
+
just check
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
Use VS Code Dev Containers to open the repository with the same toolchain in a container. Copy `devcontainer.env.example` to `.devcontainer.env` if you need to pass local git identity or other non-secret development settings into the container.
|