urun-cli 0.4.1__tar.gz → 0.5.1__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.
@@ -16,3 +16,4 @@ build/
16
16
  # Local development environment
17
17
  .devcontainer.env
18
18
  .cache/
19
+ .claude/
@@ -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.
@@ -0,0 +1,355 @@
1
+ Metadata-Version: 2.4
2
+ Name: urun-cli
3
+ Version: 0.5.1
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
+ [![PyPI](https://img.shields.io/pypi/v/urun-cli.svg)](https://pypi.org/project/urun-cli/)
28
+ [![Python](https://img.shields.io/pypi/pyversions/urun-cli.svg)](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
+ ## Serve a model from the catalog
97
+
98
+ `urun serve` deploys a model straight from the urun model catalog — no app code
99
+ required. It is `urun deploy` with a serve app templated from the resolved
100
+ catalog row (engine, HF repo, GPU, engine args).
101
+
102
+ List the enumerable model matrix (models, variants, the GPU each fits, engine
103
+ shape, and whether the placement is dev-testable or prod-only):
104
+
105
+ ```bash
106
+ urun serve catalog
107
+ urun serve catalog --json
108
+ ```
109
+
110
+ Serve a model. With no variant the model's default (first) variant is used; with
111
+ no `--gpu` the variant's first placement is used:
112
+
113
+ ```bash
114
+ urun serve qwen-coder # default variant + first placement
115
+ urun serve glm-5.2:UD-IQ2_M # explicit <id>:<variant>
116
+ urun serve qwen-coder-480b:fp8 --gpu b200:8
117
+ ```
118
+
119
+ The catalog is anon-readable reference data published by a urun-infra migration.
120
+ Reads go over PostgREST on the same control-plane host as the API URL; supply the
121
+ public Supabase anon key via `--anon-key` or `URUN_CATALOG_ANON_KEY`
122
+ (`URUN_SUPABASE_ANON_KEY` is also honored). Override the PostgREST base with
123
+ `--catalog-url` / `URUN_CATALOG_URL` if needed.
124
+
125
+ By default `urun serve <id>` renders a templated serve app and deploys it. If you
126
+ maintain your own `_serve` app, point `--entrypoint` at it; the CLI deploys that
127
+ app with `URUN_SERVE_CONFIG` and `URUN_SERVE_CATALOG_ROW` set to the resolved
128
+ row instead of rendering one.
129
+
130
+ ## Inspect apps
131
+
132
+ List every app deployed in your org and its current status:
133
+
134
+ ```bash
135
+ urun list apps
136
+ ```
137
+
138
+ Sample output:
139
+
140
+ ```text
141
+ APP FUNCTION COMPUTE STATUS RELEASE DETAIL
142
+ causal-forcing-stream generate_video h100:1 ready fa61f31b0961 0/1 GPU units in use
143
+ queued-app warmup a10:1 provisioning 000000000000 building
144
+ ```
145
+
146
+ The STATUS column is one of `provisioning`, `ready`, `pending`, `paused`, or
147
+ `failed`. It is derived from three backend signals reporting on sequential
148
+ lifecycle phases:
149
+
150
+ | Build (S3 status) | Promotion (`app_deployments`) | Capacity (`function_ready`) | STATUS |
151
+ | ---------------------- | ----------------------------- | --------------------------- | -------------- |
152
+ | `queued` \| `building` | (no row yet) | - | `provisioning` |
153
+ | `failed` | (no row yet) | - | `failed` |
154
+ | `ready` | `active` | `false` | `pending` |
155
+ | `ready` | `active` | `true` | `ready` |
156
+ | `ready` | `paused` | (irrelevant) | `paused` |
157
+ | `ready` | `failed` | (irrelevant) | `failed` |
158
+
159
+ The DETAIL column carries the disambiguating signal (raw build state, error
160
+ message, `ready_reason`, or in-use GPU counts). Pass `--json` for the raw
161
+ payload.
162
+
163
+ This command is **experimental** and requires the server-side `GET /apps`
164
+ endpoint, which is in development.
165
+
166
+ ## Inspect sessions
167
+
168
+ List live and historical sessions in your org. Newest sessions appear at the
169
+ **bottom** of the table so the command works well with `tail`:
170
+
171
+ ```bash
172
+ urun list sessions
173
+ urun list sessions | tail -20
174
+ urun list sessions --state failed
175
+ urun list sessions --limit 500
176
+ ```
177
+
178
+ Sample output:
179
+
180
+ ```text
181
+ ID APP FUNCTION SHAPE STARTED DURATION STATE DETAIL
182
+ 2cc8a91f4b3d helios world_gen h100:4 2026-06-02 10:55 UTC 44s failed no_capacity
183
+ 3f0017daee01 helios world_gen h100:1 2026-06-02 11:08 UTC 18m43s completed client_disconnect
184
+ 4a1c886e2d0a causal-forcing-… generate_video h100:1 2026-06-02 14:21 UTC 3m12s live -
185
+ ```
186
+
187
+ The STATE column maps the raw backend status to a user-friendly label:
188
+
189
+ | Backend status | STATE |
190
+ | -------------- | ----------- |
191
+ | `allocated` | `starting` |
192
+ | `connected` | `live` |
193
+ | `closed` | `completed` |
194
+ | `failed` | `failed` |
195
+ | `cancelled` | `cancelled` |
196
+
197
+ DURATION is computed from `allocated_at` to `closed_at` for terminal sessions,
198
+ or `allocated_at` to now for live ones. DETAIL carries `close_reason` when
199
+ present. Pass `--json` for the raw payload (full IDs, ISO timestamps, all
200
+ fields).
201
+
202
+ Pass `--limit` to control how many rows are fetched (default 100).
203
+
204
+ This command is **experimental** and requires the server-side `GET /sessions`
205
+ endpoint, which is in development.
206
+
207
+ ## Inspect active compute
208
+
209
+ List the compute slices your org currently has provisioned:
210
+
211
+ ```bash
212
+ urun list compute
213
+ ```
214
+
215
+ Sample output:
216
+
217
+ ```text
218
+ APP FUNCTION SHAPE INSTANCES GPU UNITS SESSIONS AGE
219
+ causal-forcing-stream generate_video h100:1 1/2 1/2 1 12s
220
+ helios world_gen h100:4 0/1 0/4 0 3m
221
+ ```
222
+
223
+ Each row is one actively provisioned `(app, function, compute_shape)`
224
+ slice. `INSTANCES` and `GPU UNITS` show `<allocated>/<provisioned>` — a
225
+ row with `0/1` is an idle warm runtime with no active sessions on it.
226
+ `SESSIONS` is the live session count. `AGE` is how stale the capacity
227
+ snapshot is; very old ages may indicate the runtime is no longer
228
+ reporting.
229
+
230
+ Slices with no provisioned capacity are omitted, so this command answers
231
+ "what is running right now". For the full deployment catalogue
232
+ (including paused / failed / unprovisioned apps) use `urun list apps`;
233
+ for historical or in-flight sessions use `urun list sessions`.
234
+
235
+ Pass `--limit` to control how many rows are fetched (default 100).
236
+
237
+ This command is **experimental** and requires the server-side `GET /compute`
238
+ endpoint, which is in development.
239
+
240
+ ## Manage apps
241
+
242
+ Manage the lifecycle of a single deployed app. The app is addressed by its
243
+ slug (the name shown under `APP` in `urun list apps`); every operation is
244
+ org-scoped via your API key.
245
+
246
+ Show detailed status for one app (the single-app complement to `list apps`):
247
+
248
+ ```bash
249
+ urun app status lingbot
250
+ ```
251
+
252
+ ```text
253
+ App: lingbot
254
+ Name: LingBot
255
+ Environment: prod
256
+ App status: active
257
+ Deployment: active
258
+ Desired replicas: 2
259
+ Function: handle_lingbot_runtime
260
+ Compute: b200:4
261
+ GPU: 4 x b200
262
+ Release: 1c6d6287abcd
263
+ Live sessions: 1
264
+ ```
265
+
266
+ Scale an app's runtime replica count (the backend's scaling knob; the
267
+ control plane turns it into the runtime StatefulSet replica count):
268
+
269
+ ```bash
270
+ urun app scale lingbot --replicas 3
271
+ urun app scale lingbot --replicas 0 # drain to zero without retiring
272
+ ```
273
+
274
+ GPU count and compute shape are fixed at deploy time per release (set via
275
+ `@app.function`), so `scale` intentionally exposes only `--replicas`.
276
+
277
+ Retire an app so the control plane stops running it (drives the deployment
278
+ to `paused` and the app to `disabled`, so the materializer stops recreating
279
+ its runtime). This is the clean, reversible, API-driven alternative to a
280
+ manual database edit:
281
+
282
+ ```bash
283
+ urun app delete lingbot-handle # prompts for confirmation
284
+ urun app delete lingbot-handle --yes # or urun app rm lingbot-handle --yes
285
+ ```
286
+
287
+ Reverse a retire and bring the app back online:
288
+
289
+ ```bash
290
+ urun app activate lingbot-handle
291
+ ```
292
+
293
+ All `app` subcommands accept `--environment` (default `prod`) and `--json`.
294
+ These commands are **experimental** and require the server-side `app`
295
+ lifecycle endpoint, which is in development.
296
+
297
+ ## What gets deployed
298
+
299
+ `urun deploy` creates a source manifest from your Python entrypoint:
300
+
301
+ | Entrypoint | Included source |
302
+ | --- | --- |
303
+ | `urun deploy app.py` | `app.py` and local Python files it imports |
304
+
305
+ Dependencies are declared in your urun app code. Project-level files such as
306
+ `pyproject.toml` and `requirements.txt` are not uploaded as dependency
307
+ declarations by the CLI.
308
+
309
+ Generated/cache content such as `.git`, dotfiles, `__pycache__`, and `.pyc`
310
+ files is excluded. Add `.urunignore` to exclude additional paths.
311
+
312
+ Non-Python assets such as templates, static files, and data files are not
313
+ auto-included yet.
314
+
315
+ ## Common options
316
+
317
+ Shared by `run` and `deploy`:
318
+
319
+ | Option | Description |
320
+ | --- | --- |
321
+ | `--name` | Override the derived app name. |
322
+ | `--api-url` | Override the API URL; defaults to `URUN_API_URL`, saved login credentials, or `https://api.urun.sh/v1`. |
323
+ | `--api-key` | Deploy API key; defaults to `URUN_API_KEY` or saved login credentials. |
324
+ | `--no-wait` | Finalize but do not poll for readiness. |
325
+ | `--poll-interval`, `--timeout` | Control readiness polling. |
326
+
327
+ ## Troubleshooting
328
+
329
+ | Error | Fix |
330
+ | --- | --- |
331
+ | `missing API key` | Run `urun login`, set `URUN_API_KEY`, or pass `--api-key`. |
332
+ | `invalid API key format` | Use `urun_<32 lowercase hex chars>`. |
333
+ | `entrypoint not found` | Run from the project root or pass the entrypoint path. |
334
+ | `path is outside the project root` | Move the file under the project before deploying. |
335
+ | Expected files are missing | Import local Python files from `app.py`; non-Python assets are not auto-included yet. |
336
+
337
+ ## Development
338
+
339
+ Contributing and test instructions are in [CONTRIBUTING.md](CONTRIBUTING.md).
340
+
341
+ ## License
342
+
343
+ MIT.
344
+
345
+ ## Development environment
346
+
347
+ This repo has a Nix/direnv/devcontainer baseline:
348
+
349
+ ```bash
350
+ direnv allow
351
+ just sync
352
+ just check
353
+ ```
354
+
355
+ 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.