urun-cli 0.4.1__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.
@@ -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,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
+ [![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
+ ## 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.
@@ -0,0 +1,299 @@
1
+ # urun CLI
2
+
3
+ Deploy Python apps to urun from your terminal.
4
+
5
+ [![PyPI](https://img.shields.io/pypi/v/urun-cli.svg)](https://pypi.org/project/urun-cli/)
6
+ [![Python](https://img.shields.io/pypi/pyversions/urun-cli.svg)](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.
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "urun-cli"
7
- version = "0.4.1"
7
+ version = "0.5.0"
8
8
  description = "End-user CLI for deploying apps to urun"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.11"