avr-cli 0.5.0__tar.gz → 0.6.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.
Files changed (127) hide show
  1. {avr_cli-0.5.0 → avr_cli-0.6.0}/PKG-INFO +78 -12
  2. {avr_cli-0.5.0 → avr_cli-0.6.0}/README.md +73 -10
  3. {avr_cli-0.5.0 → avr_cli-0.6.0}/avr_cli.egg-info/PKG-INFO +78 -12
  4. {avr_cli-0.5.0 → avr_cli-0.6.0}/avr_cli.egg-info/SOURCES.txt +18 -0
  5. {avr_cli-0.5.0 → avr_cli-0.6.0}/avr_cli.egg-info/requires.txt +3 -1
  6. {avr_cli-0.5.0 → avr_cli-0.6.0}/avrea_cli/_build_info.py +1 -1
  7. {avr_cli-0.5.0 → avr_cli-0.6.0}/avrea_cli/api_client.py +23 -4
  8. {avr_cli-0.5.0 → avr_cli-0.6.0}/avrea_cli/auth.py +2 -0
  9. {avr_cli-0.5.0 → avr_cli-0.6.0}/avrea_cli/billing.py +2 -2
  10. {avr_cli-0.5.0 → avr_cli-0.6.0}/avrea_cli/commands/auth_cmd.py +35 -3
  11. {avr_cli-0.5.0 → avr_cli-0.6.0}/avrea_cli/commands/job.py +140 -98
  12. {avr_cli-0.5.0 → avr_cli-0.6.0}/avrea_cli/commands/org.py +597 -24
  13. avr_cli-0.6.0/avrea_cli/commands/org_install_runner.py +404 -0
  14. {avr_cli-0.5.0 → avr_cli-0.6.0}/avrea_cli/commands/repo.py +80 -71
  15. {avr_cli-0.5.0 → avr_cli-0.6.0}/avrea_cli/commands/run.py +211 -119
  16. avr_cli-0.6.0/avrea_cli/commands/tests.py +352 -0
  17. {avr_cli-0.5.0 → avr_cli-0.6.0}/avrea_cli/commands/token.py +2 -2
  18. {avr_cli-0.5.0 → avr_cli-0.6.0}/avrea_cli/commands/workflow.py +86 -17
  19. avr_cli-0.6.0/avrea_cli/device_auth.py +247 -0
  20. {avr_cli-0.5.0 → avr_cli-0.6.0}/avrea_cli/display.py +2 -1
  21. {avr_cli-0.5.0 → avr_cli-0.6.0}/avrea_cli/git_mirror.py +36 -7
  22. {avr_cli-0.5.0 → avr_cli-0.6.0}/avrea_cli/helpers.py +50 -8
  23. {avr_cli-0.5.0 → avr_cli-0.6.0}/avrea_cli/json_output.py +35 -5
  24. {avr_cli-0.5.0 → avr_cli-0.6.0}/avrea_cli/log_display.py +21 -6
  25. {avr_cli-0.5.0 → avr_cli-0.6.0}/avrea_cli/main.py +11 -4
  26. {avr_cli-0.5.0 → avr_cli-0.6.0}/avrea_cli/metrics_display.py +88 -3
  27. avr_cli-0.6.0/avrea_cli/metrics_stream.py +121 -0
  28. avr_cli-0.6.0/avrea_cli/native_dispatch.py +276 -0
  29. {avr_cli-0.5.0 → avr_cli-0.6.0}/avrea_cli/output.py +3 -3
  30. {avr_cli-0.5.0 → avr_cli-0.6.0}/avrea_cli/repo_context.py +4 -14
  31. {avr_cli-0.5.0 → avr_cli-0.6.0}/avrea_cli/run_diagnostics.py +1 -1
  32. {avr_cli-0.5.0 → avr_cli-0.6.0}/avrea_cli/run_refs.py +136 -21
  33. {avr_cli-0.5.0 → avr_cli-0.6.0}/avrea_cli/vm.py +445 -205
  34. avr_cli-0.6.0/avrea_cli/vm_state_stream.py +80 -0
  35. {avr_cli-0.5.0 → avr_cli-0.6.0}/pyproject.toml +5 -5
  36. {avr_cli-0.5.0 → avr_cli-0.6.0}/tests/test_api_client.py +21 -0
  37. {avr_cli-0.5.0 → avr_cli-0.6.0}/tests/test_auth_sso.py +6 -0
  38. avr_cli-0.6.0/tests/test_billing.py +62 -0
  39. avr_cli-0.6.0/tests/test_compatible_lazy_imports.py +47 -0
  40. avr_cli-0.6.0/tests/test_device_auth.py +690 -0
  41. {avr_cli-0.5.0 → avr_cli-0.6.0}/tests/test_display.py +2 -3
  42. {avr_cli-0.5.0 → avr_cli-0.6.0}/tests/test_helpers.py +2 -1
  43. avr_cli-0.6.0/tests/test_job_gitlab.py +37 -0
  44. {avr_cli-0.5.0 → avr_cli-0.6.0}/tests/test_job_metrics_cmd.py +133 -76
  45. {avr_cli-0.5.0 → avr_cli-0.6.0}/tests/test_log_display.py +23 -0
  46. {avr_cli-0.5.0 → avr_cli-0.6.0}/tests/test_metrics_display.py +22 -0
  47. avr_cli-0.6.0/tests/test_metrics_stream.py +265 -0
  48. avr_cli-0.6.0/tests/test_native_ci.py +263 -0
  49. avr_cli-0.6.0/tests/test_native_workflow_dispatch.py +417 -0
  50. avr_cli-0.6.0/tests/test_org_install_runners.py +307 -0
  51. avr_cli-0.6.0/tests/test_org_installations.py +720 -0
  52. {avr_cli-0.5.0 → avr_cli-0.6.0}/tests/test_piped_output.py +5 -0
  53. {avr_cli-0.5.0 → avr_cli-0.6.0}/tests/test_repo_clone.py +251 -33
  54. {avr_cli-0.5.0 → avr_cli-0.6.0}/tests/test_repo_context.py +1 -0
  55. {avr_cli-0.5.0 → avr_cli-0.6.0}/tests/test_repo_mirror.py +20 -9
  56. avr_cli-0.6.0/tests/test_run_attempts.py +666 -0
  57. avr_cli-0.6.0/tests/test_run_command_refs.py +249 -0
  58. {avr_cli-0.5.0 → avr_cli-0.6.0}/tests/test_run_diagnose.py +20 -18
  59. {avr_cli-0.5.0 → avr_cli-0.6.0}/tests/test_run_logs.py +11 -11
  60. avr_cli-0.6.0/tests/test_run_refs.py +350 -0
  61. {avr_cli-0.5.0 → avr_cli-0.6.0}/tests/test_run_view.py +123 -29
  62. {avr_cli-0.5.0 → avr_cli-0.6.0}/tests/test_settings.py +0 -7
  63. avr_cli-0.6.0/tests/test_tests.py +576 -0
  64. {avr_cli-0.5.0 → avr_cli-0.6.0}/tests/test_transport.py +5 -3
  65. {avr_cli-0.5.0 → avr_cli-0.6.0}/tests/test_vm.py +46 -107
  66. avr_cli-0.6.0/tests/test_vm_alignment.py +358 -0
  67. {avr_cli-0.5.0 → avr_cli-0.6.0}/tests/test_vm_spawn_script.py +5 -5
  68. avr_cli-0.6.0/tests/test_vm_state_stream.py +66 -0
  69. {avr_cli-0.5.0 → avr_cli-0.6.0}/tests/test_web_flag.py +10 -8
  70. {avr_cli-0.5.0 → avr_cli-0.6.0}/tests/test_workflow_actions.py +34 -10
  71. {avr_cli-0.5.0 → avr_cli-0.6.0}/tests/test_workflow_run.py +37 -5
  72. avr_cli-0.5.0/tests/test_run_attempts.py +0 -357
  73. avr_cli-0.5.0/tests/test_run_command_refs.py +0 -122
  74. avr_cli-0.5.0/tests/test_run_refs.py +0 -174
  75. {avr_cli-0.5.0 → avr_cli-0.6.0}/LICENSE +0 -0
  76. {avr_cli-0.5.0 → avr_cli-0.6.0}/avr_cli.egg-info/dependency_links.txt +0 -0
  77. {avr_cli-0.5.0 → avr_cli-0.6.0}/avr_cli.egg-info/entry_points.txt +0 -0
  78. {avr_cli-0.5.0 → avr_cli-0.6.0}/avr_cli.egg-info/top_level.txt +0 -0
  79. {avr_cli-0.5.0 → avr_cli-0.6.0}/avrea_cli/__init__.py +0 -0
  80. {avr_cli-0.5.0 → avr_cli-0.6.0}/avrea_cli/assets/logo_mark.svg +0 -0
  81. {avr_cli-0.5.0 → avr_cli-0.6.0}/avrea_cli/audit.py +0 -0
  82. {avr_cli-0.5.0 → avr_cli-0.6.0}/avrea_cli/click_ext.py +0 -0
  83. {avr_cli-0.5.0 → avr_cli-0.6.0}/avrea_cli/commands/__init__.py +0 -0
  84. {avr_cli-0.5.0 → avr_cli-0.6.0}/avrea_cli/commands/artifact.py +0 -0
  85. {avr_cli-0.5.0 → avr_cli-0.6.0}/avrea_cli/commands/cache.py +0 -0
  86. {avr_cli-0.5.0 → avr_cli-0.6.0}/avrea_cli/commands/config_cmd.py +0 -0
  87. {avr_cli-0.5.0 → avr_cli-0.6.0}/avrea_cli/commands/health.py +0 -0
  88. {avr_cli-0.5.0 → avr_cli-0.6.0}/avrea_cli/commands/internal.py +0 -0
  89. {avr_cli-0.5.0 → avr_cli-0.6.0}/avrea_cli/commands/log.py +0 -0
  90. {avr_cli-0.5.0 → avr_cli-0.6.0}/avrea_cli/commands/pr.py +0 -0
  91. {avr_cli-0.5.0 → avr_cli-0.6.0}/avrea_cli/commands/sbom.py +0 -0
  92. {avr_cli-0.5.0 → avr_cli-0.6.0}/avrea_cli/commands/settings.py +0 -0
  93. {avr_cli-0.5.0 → avr_cli-0.6.0}/avrea_cli/commands/status.py +0 -0
  94. {avr_cli-0.5.0 → avr_cli-0.6.0}/avrea_cli/config.py +0 -0
  95. {avr_cli-0.5.0 → avr_cli-0.6.0}/avrea_cli/docs_gen.py +0 -0
  96. {avr_cli-0.5.0 → avr_cli-0.6.0}/avrea_cli/firewall.py +0 -0
  97. {avr_cli-0.5.0 → avr_cli-0.6.0}/avrea_cli/paths.py +0 -0
  98. {avr_cli-0.5.0 → avr_cli-0.6.0}/avrea_cli/run_diagnostics_display.py +0 -0
  99. {avr_cli-0.5.0 → avr_cli-0.6.0}/avrea_cli/spawn_script.py +0 -0
  100. {avr_cli-0.5.0 → avr_cli-0.6.0}/avrea_cli/transport.py +0 -0
  101. {avr_cli-0.5.0 → avr_cli-0.6.0}/avrea_cli/version.py +0 -0
  102. {avr_cli-0.5.0 → avr_cli-0.6.0}/setup.cfg +0 -0
  103. {avr_cli-0.5.0 → avr_cli-0.6.0}/tests/test_artifact.py +0 -0
  104. {avr_cli-0.5.0 → avr_cli-0.6.0}/tests/test_auth.py +0 -0
  105. {avr_cli-0.5.0 → avr_cli-0.6.0}/tests/test_auth_login.py +0 -0
  106. {avr_cli-0.5.0 → avr_cli-0.6.0}/tests/test_cache.py +0 -0
  107. {avr_cli-0.5.0 → avr_cli-0.6.0}/tests/test_config.py +0 -0
  108. {avr_cli-0.5.0 → avr_cli-0.6.0}/tests/test_config_cmd.py +0 -0
  109. {avr_cli-0.5.0 → avr_cli-0.6.0}/tests/test_docs_gen.py +0 -0
  110. {avr_cli-0.5.0 → avr_cli-0.6.0}/tests/test_env_conventions.py +0 -0
  111. {avr_cli-0.5.0 → avr_cli-0.6.0}/tests/test_firewall.py +0 -0
  112. {avr_cli-0.5.0 → avr_cli-0.6.0}/tests/test_global_flags.py +0 -0
  113. {avr_cli-0.5.0 → avr_cli-0.6.0}/tests/test_job_list.py +0 -0
  114. {avr_cli-0.5.0 → avr_cli-0.6.0}/tests/test_job_view_logs.py +0 -0
  115. {avr_cli-0.5.0 → avr_cli-0.6.0}/tests/test_json_output.py +0 -0
  116. {avr_cli-0.5.0 → avr_cli-0.6.0}/tests/test_lazy_registry.py +0 -0
  117. {avr_cli-0.5.0 → avr_cli-0.6.0}/tests/test_org_email_domain.py +0 -0
  118. {avr_cli-0.5.0 → avr_cli-0.6.0}/tests/test_org_saml.py +0 -0
  119. {avr_cli-0.5.0 → avr_cli-0.6.0}/tests/test_output.py +0 -0
  120. {avr_cli-0.5.0 → avr_cli-0.6.0}/tests/test_pr_list.py +0 -0
  121. {avr_cli-0.5.0 → avr_cli-0.6.0}/tests/test_public_mirrors.py +0 -0
  122. {avr_cli-0.5.0 → avr_cli-0.6.0}/tests/test_run_list_flags.py +0 -0
  123. {avr_cli-0.5.0 → avr_cli-0.6.0}/tests/test_sbom.py +0 -0
  124. {avr_cli-0.5.0 → avr_cli-0.6.0}/tests/test_scoped_token.py +0 -0
  125. {avr_cli-0.5.0 → avr_cli-0.6.0}/tests/test_token.py +0 -0
  126. {avr_cli-0.5.0 → avr_cli-0.6.0}/tests/test_token_output.py +0 -0
  127. {avr_cli-0.5.0 → avr_cli-0.6.0}/tests/test_workflow_view.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: avr-cli
3
- Version: 0.5.0
3
+ Version: 0.6.0
4
4
  Summary: Avrea command-line client
5
5
  Author-email: Avrea <support@avrea.com>
6
6
  License-Expression: Apache-2.0
@@ -11,6 +11,7 @@ Classifier: Development Status :: 3 - Alpha
11
11
  Classifier: Intended Audience :: Developers
12
12
  Classifier: Topic :: Software Development :: Libraries
13
13
  Classifier: Programming Language :: Python :: 3.14
14
+ Classifier: Programming Language :: Python :: 3.15
14
15
  Requires-Python: >=3.14
15
16
  Description-Content-Type: text/markdown
16
17
  License-File: LICENSE
@@ -20,14 +21,16 @@ Requires-Dist: click>=8.3.1
20
21
  Requires-Dist: httpx>=0.28.1
21
22
  Requires-Dist: platformdirs>=4.0
22
23
  Requires-Dist: prettytable>=3.17.0
24
+ Requires-Dist: segno>=1.6.6
23
25
  Requires-Dist: pydantic>=2.12.4
24
26
  Requires-Dist: typing-extensions>=4.15.0
25
27
  Requires-Dist: idna>=3.15
28
+ Requires-Dist: websockets>=15
26
29
  Provides-Extra: dev
27
30
  Requires-Dist: pytest>=9.0.3; extra == "dev"
28
31
  Requires-Dist: pytest-asyncio>=1.3.0; extra == "dev"
29
32
  Requires-Dist: pytest-timeout>=2.4.0; extra == "dev"
30
- Requires-Dist: ruff==0.15.13; extra == "dev"
33
+ Requires-Dist: ruff==0.17.3; extra == "dev"
31
34
  Requires-Dist: ty==0.0.15; extra == "dev"
32
35
  Dynamic: license-file
33
36
 
@@ -89,6 +92,19 @@ Browser-based login (recommended):
89
92
  avr auth login
90
93
  ```
91
94
 
95
+ On a remote machine, VM, or terminal without a browser:
96
+
97
+ ```sh
98
+ avr auth device-login
99
+ ```
100
+
101
+ Open the displayed link on your laptop or phone, or scan the terminal QR code.
102
+ Confirm the code and approve the request. The CLI waits for approval and saves
103
+ the credential on the machine where you ran the command. Use `--no-qr` to hide
104
+ the QR code; the sign-in link is always displayed. QR output is omitted when
105
+ piped or when the terminal cannot display it. Device login requires support
106
+ on your Avrea server; `avr auth login` keeps its existing browser flow.
107
+
92
108
  Show who you are and where you're pointing:
93
109
 
94
110
  ```sh
@@ -126,11 +142,12 @@ avr repo clone foo/faa my-checkout --depth 1 --branch main
126
142
  avr repo clone foo/faa --placement https://git.fi.a.avrea.io
127
143
  ```
128
144
 
129
- Avrea discovers the repository's placements and picks the first mirror that
130
- responds successfully from your machine. `--placement` accepts an advertised
131
- mirror URL or hostname to select one explicitly. Give the `owner/repository` name
132
- or an Avrea repository ID (`rep-...`).
133
- The repository must already have Git mirroring enabled and a usable placement.
145
+ Avrea uses a single available placement directly. With multiple placements,
146
+ it picks the first mirror that responds successfully from your machine.
147
+ `--placement` accepts an advertised mirror URL or hostname to select one
148
+ explicitly. Give the `owner/repository` name or an Avrea repository ID (`rep-...`).
149
+ Requires Git 2.31 or newer. The repository must already have Git mirroring
150
+ enabled and a usable placement.
134
151
 
135
152
  `origin` stays on the selected read-only mirror. The clone installs a
136
153
  repository-local credential helper so ordinary `git fetch` and `git pull`
@@ -189,6 +206,48 @@ avr run logs <run-id> # full log dump, paginated
189
206
  avr job logs <job-id> --follow # tail one job in real time
190
207
  ```
191
208
 
209
+ **Read recorded tests from a run or job**
210
+
211
+ ```sh
212
+ avr tests <run-id> --job unit # select one job by name
213
+ avr tests <github-run-id> # a single-job run needs no selector
214
+ avr tests <job-id> # names, outcomes, timings and failures
215
+ avr tests <job-id> --status failed
216
+ avr tests <job-id> --test test_login
217
+ avr tests <job-id> --suite Authentication --class TestLogin
218
+ avr tests <job-id> --json '*' # reporting state and pagination
219
+ avr tests <job-id> --limit 500 # maximum matching occurrences to return
220
+ avr tests <job-id> --cursor '<cursor>' # resume with the same filters
221
+ ```
222
+
223
+ Accepts Avrea run IDs (`run-` or `awr-`), GitHub numeric run IDs, GitHub
224
+ Actions run URLs, Avrea console run URLs, and Avrea job IDs (`job-` or
225
+ `awj-`). `avr tests list <run-or-job>` is equivalent. A run selects one job,
226
+ not all jobs together. `--job NAME` matches a case-insensitive substring and
227
+ must identify one job; ambiguous names show the available jobs and IDs.
228
+ A run containing one job selects it automatically. The job's repository is
229
+ resolved automatically, or use `--repo owner/name`.
230
+
231
+ The listing reads the job's latest confirmed execution, without substituting
232
+ an older report. GitHub run URLs with `/attempts/N` select that run attempt;
233
+ there is no separate test-execution selector. Test names match exactly and
234
+ case-sensitively; test filters combine with AND. Outcomes are `passed`,
235
+ `failed`, `skipped` and `errored`. Repeated matches stay separate, with their
236
+ recorded failure messages shown directly. `--json FIELDS` and `--jq` also work.
237
+
238
+ Each invocation returns at most 1,000 occurrences and reads at most 20 result
239
+ pages. Filtering can produce empty pages with a continuation. If the search
240
+ stops before the report ends, the command says so; resume with the same job,
241
+ filters and `--cursor`. The retained count covers all tests, before filtering.
242
+ Missing or expired reports are distinct from zero matches or a retained
243
+ report containing zero tests. Complete describes collected reports, not
244
+ whether every expected test ran.
245
+
246
+ These commands require an API deployment supporting the corresponding reads.
247
+ Filtering fails explicitly if the server does not acknowledge the selectors.
248
+ File filters and test splitting are not supported. The commands do not rerun
249
+ tests or change collection.
250
+
192
251
  **Trigger a workflow_dispatch**
193
252
 
194
253
  ```sh
@@ -210,7 +269,7 @@ avr job ssh <job-id> --print-command # just print the ssh string
210
269
  **Live VM metrics while a job runs**
211
270
 
212
271
  ```sh
213
- avr job metrics <job-id> --watch # CPU/memory/IO gauges, refreshed every few seconds
272
+ avr job metrics <job-id> --watch # CPU/memory/IO gauges, streamed live
214
273
  avr job metrics <job-id> # static post-mortem after the job ended
215
274
  ```
216
275
 
@@ -220,8 +279,8 @@ Durable, org-scoped VMs reachable over SSH (plus RDP/VNC for a desktop), separat
220
279
 
221
280
  ```sh
222
281
  avr vm create --name dev --os linux --size 2-vcpu \
223
- --ssh-key @~/.ssh/id_ed25519.pub --ephemeral --wait # boot, wait for SSH, print connect
224
- avr vm create --name ci --os linux --size 8-vcpu --ephemeral \
282
+ --ssh-key @~/.ssh/id_ed25519.pub --wait # boot, wait for SSH, print connect
283
+ avr vm create --name ci --os linux --size 8-vcpu \
225
284
  --repo org/project --disable-cache gha,packages # attach a repo, narrow which caches it uses
226
285
  avr vm bootstrap <vm-id> --setup-github --install claude,codex \
227
286
  --repo https://github.com/org/project --env AWS_REGION=eu-north-1
@@ -229,10 +288,17 @@ avr vm ssh <vm-id> # SSH session, host
229
288
  avr vm ssh <vm-id> --login -- claude -p 'summarize the repo' # one-off command in a login shell (sees forwarded env)
230
289
  avr vm ssh <vm-id> --session dev # persistent tmux session, survives a dropped connection
231
290
  avr vm ssh-config <vm-id> --append # add `ssh <alias>` (scp/rsync, VS Code Remote-SSH) to ~/.ssh/config
232
- avr vm list ; avr vm stop <vm-id> # lifecycle
291
+ avr vm list ; avr vm pause <vm-id> # lifecycle
292
+ avr vm resume <vm-id> --wait
293
+ avr vm fork <paused-vm-id> --name experiment --wait
294
+ avr vm capabilities
295
+ avr vm create --os linux --arch arm64 --size 2-vcpu --https-port 3000 --https-port 8080
296
+ avr vm metrics <vm-id> --watch
233
297
  ```
234
298
 
235
- `bootstrap` runs each step over SSH and streams it live, feeding secrets (GitHub token, env values, agent credentials) on stdin rather than argv. `--forward-agent-creds` carries your local `ANTHROPIC_API_KEY` / `OPENAI_API_KEY`, or a Claude subscription token — if none is set it offers to run `claude setup-token` and forwards the pasted `CLAUDE_CODE_OAUTH_TOKEN`. Disks are ephemeral, so re-run it after every `avr vm start`. `avr vm ssh` pins the VM's host key when its endpoint publishes one; if it doesn't, `avr` warns and falls back to trust-on-first-use — pass `--session <name>` to attach to (or create) a persistent tmux session that survives a dropped connection. A one-off `avr vm ssh -- <cmd>` runs in a non-login shell that sources no startup files, so pass `--login` when the command needs bootstrap-forwarded env (e.g. `claude`'s subscription token). `avr vm ssh-config` emits a host-key-pinned SSH config block so plain `ssh`, scp/rsync, and VS Code / Cursor Remote-SSH reach the VM without wrapping each tool — `--append` writes it into `~/.ssh/config` for you (idempotent: re-run after a restart to refresh the endpoint in place). For a desktop, `avr vm rdp` / `avr vm vnc` tunnel over the same SSH endpoint, and `avr vm port-forward` is the generic primitive (repeat `--port` to forward several ports over one connection). A VM created with `--repo` inherits that repository's build/CI caches; `--disable-cache <name>` narrows them per VM (it can turn an inherited cache off, never on).
299
+ `bootstrap` runs each step over SSH and streams it live, feeding secrets (GitHub token, env values, agent credentials) on stdin rather than argv. `--forward-agent-creds` carries your local `ANTHROPIC_API_KEY` / `OPENAI_API_KEY`, or a Claude subscription token — if none is set it offers to run `claude setup-token` and forwards the pasted `CLAUDE_CODE_OAUTH_TOKEN`. Bootstrap changes survive pause and resume. `avr vm ssh` pins the VM's host key when its endpoint publishes one; if it doesn't, `avr` warns and falls back to trust-on-first-use — pass `--session <name>` to attach to (or create) a persistent tmux session that survives a dropped connection. A one-off `avr vm ssh -- <cmd>` runs in a non-login shell that sources no startup files, so pass `--login` when the command needs bootstrap-forwarded env (e.g. `claude`'s subscription token). `avr vm ssh-config` emits a host-key-pinned SSH config block so plain `ssh`, scp/rsync, and VS Code / Cursor Remote-SSH reach the VM without wrapping each tool — `--append` writes it into `~/.ssh/config` for you (idempotent: re-run after a resume to refresh the endpoint in place). For a desktop, `avr vm rdp` / `avr vm vnc` tunnel over the same SSH endpoint, and `avr vm port-forward` is the generic primitive (repeat `--port` to forward several ports over one connection). A VM created with `--repo` inherits that repository's build/CI caches; `--disable-cache <name>` narrows them per VM (it can turn an inherited cache off, never on).
300
+
301
+ VM references accept IDs or display names. Names are generated when omitted. TTL expiry pauses the VM, or deletes it when pausing is unavailable. Agent sandboxes boot once and cannot be paused or resumed. HTTPS URLs are public to anyone who has them; the first configured port is the default. Port updates apply live on a running VM, otherwise at the next start or resume; `avr vm rotate-https-label` replaces leaked URLs. Metrics watch uses a WebSocket stream for both VM and job metrics; explicit `--start`/`--end` windows require single-shot mode.
236
302
 
237
303
  **Cancel or rerun a run**
238
304
 
@@ -56,6 +56,19 @@ Browser-based login (recommended):
56
56
  avr auth login
57
57
  ```
58
58
 
59
+ On a remote machine, VM, or terminal without a browser:
60
+
61
+ ```sh
62
+ avr auth device-login
63
+ ```
64
+
65
+ Open the displayed link on your laptop or phone, or scan the terminal QR code.
66
+ Confirm the code and approve the request. The CLI waits for approval and saves
67
+ the credential on the machine where you ran the command. Use `--no-qr` to hide
68
+ the QR code; the sign-in link is always displayed. QR output is omitted when
69
+ piped or when the terminal cannot display it. Device login requires support
70
+ on your Avrea server; `avr auth login` keeps its existing browser flow.
71
+
59
72
  Show who you are and where you're pointing:
60
73
 
61
74
  ```sh
@@ -93,11 +106,12 @@ avr repo clone foo/faa my-checkout --depth 1 --branch main
93
106
  avr repo clone foo/faa --placement https://git.fi.a.avrea.io
94
107
  ```
95
108
 
96
- Avrea discovers the repository's placements and picks the first mirror that
97
- responds successfully from your machine. `--placement` accepts an advertised
98
- mirror URL or hostname to select one explicitly. Give the `owner/repository` name
99
- or an Avrea repository ID (`rep-...`).
100
- The repository must already have Git mirroring enabled and a usable placement.
109
+ Avrea uses a single available placement directly. With multiple placements,
110
+ it picks the first mirror that responds successfully from your machine.
111
+ `--placement` accepts an advertised mirror URL or hostname to select one
112
+ explicitly. Give the `owner/repository` name or an Avrea repository ID (`rep-...`).
113
+ Requires Git 2.31 or newer. The repository must already have Git mirroring
114
+ enabled and a usable placement.
101
115
 
102
116
  `origin` stays on the selected read-only mirror. The clone installs a
103
117
  repository-local credential helper so ordinary `git fetch` and `git pull`
@@ -156,6 +170,48 @@ avr run logs <run-id> # full log dump, paginated
156
170
  avr job logs <job-id> --follow # tail one job in real time
157
171
  ```
158
172
 
173
+ **Read recorded tests from a run or job**
174
+
175
+ ```sh
176
+ avr tests <run-id> --job unit # select one job by name
177
+ avr tests <github-run-id> # a single-job run needs no selector
178
+ avr tests <job-id> # names, outcomes, timings and failures
179
+ avr tests <job-id> --status failed
180
+ avr tests <job-id> --test test_login
181
+ avr tests <job-id> --suite Authentication --class TestLogin
182
+ avr tests <job-id> --json '*' # reporting state and pagination
183
+ avr tests <job-id> --limit 500 # maximum matching occurrences to return
184
+ avr tests <job-id> --cursor '<cursor>' # resume with the same filters
185
+ ```
186
+
187
+ Accepts Avrea run IDs (`run-` or `awr-`), GitHub numeric run IDs, GitHub
188
+ Actions run URLs, Avrea console run URLs, and Avrea job IDs (`job-` or
189
+ `awj-`). `avr tests list <run-or-job>` is equivalent. A run selects one job,
190
+ not all jobs together. `--job NAME` matches a case-insensitive substring and
191
+ must identify one job; ambiguous names show the available jobs and IDs.
192
+ A run containing one job selects it automatically. The job's repository is
193
+ resolved automatically, or use `--repo owner/name`.
194
+
195
+ The listing reads the job's latest confirmed execution, without substituting
196
+ an older report. GitHub run URLs with `/attempts/N` select that run attempt;
197
+ there is no separate test-execution selector. Test names match exactly and
198
+ case-sensitively; test filters combine with AND. Outcomes are `passed`,
199
+ `failed`, `skipped` and `errored`. Repeated matches stay separate, with their
200
+ recorded failure messages shown directly. `--json FIELDS` and `--jq` also work.
201
+
202
+ Each invocation returns at most 1,000 occurrences and reads at most 20 result
203
+ pages. Filtering can produce empty pages with a continuation. If the search
204
+ stops before the report ends, the command says so; resume with the same job,
205
+ filters and `--cursor`. The retained count covers all tests, before filtering.
206
+ Missing or expired reports are distinct from zero matches or a retained
207
+ report containing zero tests. Complete describes collected reports, not
208
+ whether every expected test ran.
209
+
210
+ These commands require an API deployment supporting the corresponding reads.
211
+ Filtering fails explicitly if the server does not acknowledge the selectors.
212
+ File filters and test splitting are not supported. The commands do not rerun
213
+ tests or change collection.
214
+
159
215
  **Trigger a workflow_dispatch**
160
216
 
161
217
  ```sh
@@ -177,7 +233,7 @@ avr job ssh <job-id> --print-command # just print the ssh string
177
233
  **Live VM metrics while a job runs**
178
234
 
179
235
  ```sh
180
- avr job metrics <job-id> --watch # CPU/memory/IO gauges, refreshed every few seconds
236
+ avr job metrics <job-id> --watch # CPU/memory/IO gauges, streamed live
181
237
  avr job metrics <job-id> # static post-mortem after the job ended
182
238
  ```
183
239
 
@@ -187,8 +243,8 @@ Durable, org-scoped VMs reachable over SSH (plus RDP/VNC for a desktop), separat
187
243
 
188
244
  ```sh
189
245
  avr vm create --name dev --os linux --size 2-vcpu \
190
- --ssh-key @~/.ssh/id_ed25519.pub --ephemeral --wait # boot, wait for SSH, print connect
191
- avr vm create --name ci --os linux --size 8-vcpu --ephemeral \
246
+ --ssh-key @~/.ssh/id_ed25519.pub --wait # boot, wait for SSH, print connect
247
+ avr vm create --name ci --os linux --size 8-vcpu \
192
248
  --repo org/project --disable-cache gha,packages # attach a repo, narrow which caches it uses
193
249
  avr vm bootstrap <vm-id> --setup-github --install claude,codex \
194
250
  --repo https://github.com/org/project --env AWS_REGION=eu-north-1
@@ -196,10 +252,17 @@ avr vm ssh <vm-id> # SSH session, host
196
252
  avr vm ssh <vm-id> --login -- claude -p 'summarize the repo' # one-off command in a login shell (sees forwarded env)
197
253
  avr vm ssh <vm-id> --session dev # persistent tmux session, survives a dropped connection
198
254
  avr vm ssh-config <vm-id> --append # add `ssh <alias>` (scp/rsync, VS Code Remote-SSH) to ~/.ssh/config
199
- avr vm list ; avr vm stop <vm-id> # lifecycle
255
+ avr vm list ; avr vm pause <vm-id> # lifecycle
256
+ avr vm resume <vm-id> --wait
257
+ avr vm fork <paused-vm-id> --name experiment --wait
258
+ avr vm capabilities
259
+ avr vm create --os linux --arch arm64 --size 2-vcpu --https-port 3000 --https-port 8080
260
+ avr vm metrics <vm-id> --watch
200
261
  ```
201
262
 
202
- `bootstrap` runs each step over SSH and streams it live, feeding secrets (GitHub token, env values, agent credentials) on stdin rather than argv. `--forward-agent-creds` carries your local `ANTHROPIC_API_KEY` / `OPENAI_API_KEY`, or a Claude subscription token — if none is set it offers to run `claude setup-token` and forwards the pasted `CLAUDE_CODE_OAUTH_TOKEN`. Disks are ephemeral, so re-run it after every `avr vm start`. `avr vm ssh` pins the VM's host key when its endpoint publishes one; if it doesn't, `avr` warns and falls back to trust-on-first-use — pass `--session <name>` to attach to (or create) a persistent tmux session that survives a dropped connection. A one-off `avr vm ssh -- <cmd>` runs in a non-login shell that sources no startup files, so pass `--login` when the command needs bootstrap-forwarded env (e.g. `claude`'s subscription token). `avr vm ssh-config` emits a host-key-pinned SSH config block so plain `ssh`, scp/rsync, and VS Code / Cursor Remote-SSH reach the VM without wrapping each tool — `--append` writes it into `~/.ssh/config` for you (idempotent: re-run after a restart to refresh the endpoint in place). For a desktop, `avr vm rdp` / `avr vm vnc` tunnel over the same SSH endpoint, and `avr vm port-forward` is the generic primitive (repeat `--port` to forward several ports over one connection). A VM created with `--repo` inherits that repository's build/CI caches; `--disable-cache <name>` narrows them per VM (it can turn an inherited cache off, never on).
263
+ `bootstrap` runs each step over SSH and streams it live, feeding secrets (GitHub token, env values, agent credentials) on stdin rather than argv. `--forward-agent-creds` carries your local `ANTHROPIC_API_KEY` / `OPENAI_API_KEY`, or a Claude subscription token — if none is set it offers to run `claude setup-token` and forwards the pasted `CLAUDE_CODE_OAUTH_TOKEN`. Bootstrap changes survive pause and resume. `avr vm ssh` pins the VM's host key when its endpoint publishes one; if it doesn't, `avr` warns and falls back to trust-on-first-use — pass `--session <name>` to attach to (or create) a persistent tmux session that survives a dropped connection. A one-off `avr vm ssh -- <cmd>` runs in a non-login shell that sources no startup files, so pass `--login` when the command needs bootstrap-forwarded env (e.g. `claude`'s subscription token). `avr vm ssh-config` emits a host-key-pinned SSH config block so plain `ssh`, scp/rsync, and VS Code / Cursor Remote-SSH reach the VM without wrapping each tool — `--append` writes it into `~/.ssh/config` for you (idempotent: re-run after a resume to refresh the endpoint in place). For a desktop, `avr vm rdp` / `avr vm vnc` tunnel over the same SSH endpoint, and `avr vm port-forward` is the generic primitive (repeat `--port` to forward several ports over one connection). A VM created with `--repo` inherits that repository's build/CI caches; `--disable-cache <name>` narrows them per VM (it can turn an inherited cache off, never on).
264
+
265
+ VM references accept IDs or display names. Names are generated when omitted. TTL expiry pauses the VM, or deletes it when pausing is unavailable. Agent sandboxes boot once and cannot be paused or resumed. HTTPS URLs are public to anyone who has them; the first configured port is the default. Port updates apply live on a running VM, otherwise at the next start or resume; `avr vm rotate-https-label` replaces leaked URLs. Metrics watch uses a WebSocket stream for both VM and job metrics; explicit `--start`/`--end` windows require single-shot mode.
203
266
 
204
267
  **Cancel or rerun a run**
205
268
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: avr-cli
3
- Version: 0.5.0
3
+ Version: 0.6.0
4
4
  Summary: Avrea command-line client
5
5
  Author-email: Avrea <support@avrea.com>
6
6
  License-Expression: Apache-2.0
@@ -11,6 +11,7 @@ Classifier: Development Status :: 3 - Alpha
11
11
  Classifier: Intended Audience :: Developers
12
12
  Classifier: Topic :: Software Development :: Libraries
13
13
  Classifier: Programming Language :: Python :: 3.14
14
+ Classifier: Programming Language :: Python :: 3.15
14
15
  Requires-Python: >=3.14
15
16
  Description-Content-Type: text/markdown
16
17
  License-File: LICENSE
@@ -20,14 +21,16 @@ Requires-Dist: click>=8.3.1
20
21
  Requires-Dist: httpx>=0.28.1
21
22
  Requires-Dist: platformdirs>=4.0
22
23
  Requires-Dist: prettytable>=3.17.0
24
+ Requires-Dist: segno>=1.6.6
23
25
  Requires-Dist: pydantic>=2.12.4
24
26
  Requires-Dist: typing-extensions>=4.15.0
25
27
  Requires-Dist: idna>=3.15
28
+ Requires-Dist: websockets>=15
26
29
  Provides-Extra: dev
27
30
  Requires-Dist: pytest>=9.0.3; extra == "dev"
28
31
  Requires-Dist: pytest-asyncio>=1.3.0; extra == "dev"
29
32
  Requires-Dist: pytest-timeout>=2.4.0; extra == "dev"
30
- Requires-Dist: ruff==0.15.13; extra == "dev"
33
+ Requires-Dist: ruff==0.17.3; extra == "dev"
31
34
  Requires-Dist: ty==0.0.15; extra == "dev"
32
35
  Dynamic: license-file
33
36
 
@@ -89,6 +92,19 @@ Browser-based login (recommended):
89
92
  avr auth login
90
93
  ```
91
94
 
95
+ On a remote machine, VM, or terminal without a browser:
96
+
97
+ ```sh
98
+ avr auth device-login
99
+ ```
100
+
101
+ Open the displayed link on your laptop or phone, or scan the terminal QR code.
102
+ Confirm the code and approve the request. The CLI waits for approval and saves
103
+ the credential on the machine where you ran the command. Use `--no-qr` to hide
104
+ the QR code; the sign-in link is always displayed. QR output is omitted when
105
+ piped or when the terminal cannot display it. Device login requires support
106
+ on your Avrea server; `avr auth login` keeps its existing browser flow.
107
+
92
108
  Show who you are and where you're pointing:
93
109
 
94
110
  ```sh
@@ -126,11 +142,12 @@ avr repo clone foo/faa my-checkout --depth 1 --branch main
126
142
  avr repo clone foo/faa --placement https://git.fi.a.avrea.io
127
143
  ```
128
144
 
129
- Avrea discovers the repository's placements and picks the first mirror that
130
- responds successfully from your machine. `--placement` accepts an advertised
131
- mirror URL or hostname to select one explicitly. Give the `owner/repository` name
132
- or an Avrea repository ID (`rep-...`).
133
- The repository must already have Git mirroring enabled and a usable placement.
145
+ Avrea uses a single available placement directly. With multiple placements,
146
+ it picks the first mirror that responds successfully from your machine.
147
+ `--placement` accepts an advertised mirror URL or hostname to select one
148
+ explicitly. Give the `owner/repository` name or an Avrea repository ID (`rep-...`).
149
+ Requires Git 2.31 or newer. The repository must already have Git mirroring
150
+ enabled and a usable placement.
134
151
 
135
152
  `origin` stays on the selected read-only mirror. The clone installs a
136
153
  repository-local credential helper so ordinary `git fetch` and `git pull`
@@ -189,6 +206,48 @@ avr run logs <run-id> # full log dump, paginated
189
206
  avr job logs <job-id> --follow # tail one job in real time
190
207
  ```
191
208
 
209
+ **Read recorded tests from a run or job**
210
+
211
+ ```sh
212
+ avr tests <run-id> --job unit # select one job by name
213
+ avr tests <github-run-id> # a single-job run needs no selector
214
+ avr tests <job-id> # names, outcomes, timings and failures
215
+ avr tests <job-id> --status failed
216
+ avr tests <job-id> --test test_login
217
+ avr tests <job-id> --suite Authentication --class TestLogin
218
+ avr tests <job-id> --json '*' # reporting state and pagination
219
+ avr tests <job-id> --limit 500 # maximum matching occurrences to return
220
+ avr tests <job-id> --cursor '<cursor>' # resume with the same filters
221
+ ```
222
+
223
+ Accepts Avrea run IDs (`run-` or `awr-`), GitHub numeric run IDs, GitHub
224
+ Actions run URLs, Avrea console run URLs, and Avrea job IDs (`job-` or
225
+ `awj-`). `avr tests list <run-or-job>` is equivalent. A run selects one job,
226
+ not all jobs together. `--job NAME` matches a case-insensitive substring and
227
+ must identify one job; ambiguous names show the available jobs and IDs.
228
+ A run containing one job selects it automatically. The job's repository is
229
+ resolved automatically, or use `--repo owner/name`.
230
+
231
+ The listing reads the job's latest confirmed execution, without substituting
232
+ an older report. GitHub run URLs with `/attempts/N` select that run attempt;
233
+ there is no separate test-execution selector. Test names match exactly and
234
+ case-sensitively; test filters combine with AND. Outcomes are `passed`,
235
+ `failed`, `skipped` and `errored`. Repeated matches stay separate, with their
236
+ recorded failure messages shown directly. `--json FIELDS` and `--jq` also work.
237
+
238
+ Each invocation returns at most 1,000 occurrences and reads at most 20 result
239
+ pages. Filtering can produce empty pages with a continuation. If the search
240
+ stops before the report ends, the command says so; resume with the same job,
241
+ filters and `--cursor`. The retained count covers all tests, before filtering.
242
+ Missing or expired reports are distinct from zero matches or a retained
243
+ report containing zero tests. Complete describes collected reports, not
244
+ whether every expected test ran.
245
+
246
+ These commands require an API deployment supporting the corresponding reads.
247
+ Filtering fails explicitly if the server does not acknowledge the selectors.
248
+ File filters and test splitting are not supported. The commands do not rerun
249
+ tests or change collection.
250
+
192
251
  **Trigger a workflow_dispatch**
193
252
 
194
253
  ```sh
@@ -210,7 +269,7 @@ avr job ssh <job-id> --print-command # just print the ssh string
210
269
  **Live VM metrics while a job runs**
211
270
 
212
271
  ```sh
213
- avr job metrics <job-id> --watch # CPU/memory/IO gauges, refreshed every few seconds
272
+ avr job metrics <job-id> --watch # CPU/memory/IO gauges, streamed live
214
273
  avr job metrics <job-id> # static post-mortem after the job ended
215
274
  ```
216
275
 
@@ -220,8 +279,8 @@ Durable, org-scoped VMs reachable over SSH (plus RDP/VNC for a desktop), separat
220
279
 
221
280
  ```sh
222
281
  avr vm create --name dev --os linux --size 2-vcpu \
223
- --ssh-key @~/.ssh/id_ed25519.pub --ephemeral --wait # boot, wait for SSH, print connect
224
- avr vm create --name ci --os linux --size 8-vcpu --ephemeral \
282
+ --ssh-key @~/.ssh/id_ed25519.pub --wait # boot, wait for SSH, print connect
283
+ avr vm create --name ci --os linux --size 8-vcpu \
225
284
  --repo org/project --disable-cache gha,packages # attach a repo, narrow which caches it uses
226
285
  avr vm bootstrap <vm-id> --setup-github --install claude,codex \
227
286
  --repo https://github.com/org/project --env AWS_REGION=eu-north-1
@@ -229,10 +288,17 @@ avr vm ssh <vm-id> # SSH session, host
229
288
  avr vm ssh <vm-id> --login -- claude -p 'summarize the repo' # one-off command in a login shell (sees forwarded env)
230
289
  avr vm ssh <vm-id> --session dev # persistent tmux session, survives a dropped connection
231
290
  avr vm ssh-config <vm-id> --append # add `ssh <alias>` (scp/rsync, VS Code Remote-SSH) to ~/.ssh/config
232
- avr vm list ; avr vm stop <vm-id> # lifecycle
291
+ avr vm list ; avr vm pause <vm-id> # lifecycle
292
+ avr vm resume <vm-id> --wait
293
+ avr vm fork <paused-vm-id> --name experiment --wait
294
+ avr vm capabilities
295
+ avr vm create --os linux --arch arm64 --size 2-vcpu --https-port 3000 --https-port 8080
296
+ avr vm metrics <vm-id> --watch
233
297
  ```
234
298
 
235
- `bootstrap` runs each step over SSH and streams it live, feeding secrets (GitHub token, env values, agent credentials) on stdin rather than argv. `--forward-agent-creds` carries your local `ANTHROPIC_API_KEY` / `OPENAI_API_KEY`, or a Claude subscription token — if none is set it offers to run `claude setup-token` and forwards the pasted `CLAUDE_CODE_OAUTH_TOKEN`. Disks are ephemeral, so re-run it after every `avr vm start`. `avr vm ssh` pins the VM's host key when its endpoint publishes one; if it doesn't, `avr` warns and falls back to trust-on-first-use — pass `--session <name>` to attach to (or create) a persistent tmux session that survives a dropped connection. A one-off `avr vm ssh -- <cmd>` runs in a non-login shell that sources no startup files, so pass `--login` when the command needs bootstrap-forwarded env (e.g. `claude`'s subscription token). `avr vm ssh-config` emits a host-key-pinned SSH config block so plain `ssh`, scp/rsync, and VS Code / Cursor Remote-SSH reach the VM without wrapping each tool — `--append` writes it into `~/.ssh/config` for you (idempotent: re-run after a restart to refresh the endpoint in place). For a desktop, `avr vm rdp` / `avr vm vnc` tunnel over the same SSH endpoint, and `avr vm port-forward` is the generic primitive (repeat `--port` to forward several ports over one connection). A VM created with `--repo` inherits that repository's build/CI caches; `--disable-cache <name>` narrows them per VM (it can turn an inherited cache off, never on).
299
+ `bootstrap` runs each step over SSH and streams it live, feeding secrets (GitHub token, env values, agent credentials) on stdin rather than argv. `--forward-agent-creds` carries your local `ANTHROPIC_API_KEY` / `OPENAI_API_KEY`, or a Claude subscription token — if none is set it offers to run `claude setup-token` and forwards the pasted `CLAUDE_CODE_OAUTH_TOKEN`. Bootstrap changes survive pause and resume. `avr vm ssh` pins the VM's host key when its endpoint publishes one; if it doesn't, `avr` warns and falls back to trust-on-first-use — pass `--session <name>` to attach to (or create) a persistent tmux session that survives a dropped connection. A one-off `avr vm ssh -- <cmd>` runs in a non-login shell that sources no startup files, so pass `--login` when the command needs bootstrap-forwarded env (e.g. `claude`'s subscription token). `avr vm ssh-config` emits a host-key-pinned SSH config block so plain `ssh`, scp/rsync, and VS Code / Cursor Remote-SSH reach the VM without wrapping each tool — `--append` writes it into `~/.ssh/config` for you (idempotent: re-run after a resume to refresh the endpoint in place). For a desktop, `avr vm rdp` / `avr vm vnc` tunnel over the same SSH endpoint, and `avr vm port-forward` is the generic primitive (repeat `--port` to forward several ports over one connection). A VM created with `--repo` inherits that repository's build/CI caches; `--disable-cache <name>` narrows them per VM (it can turn an inherited cache off, never on).
300
+
301
+ VM references accept IDs or display names. Names are generated when omitted. TTL expiry pauses the VM, or deletes it when pausing is unavailable. Agent sandboxes boot once and cannot be paused or resumed. HTTPS URLs are public to anyone who has them; the first configured port is the default. Port updates apply live on a running VM, otherwise at the next start or resume; `avr vm rotate-https-label` replaces leaked URLs. Metrics watch uses a WebSocket stream for both VM and job metrics; explicit `--start`/`--end` windows require single-shot mode.
236
302
 
237
303
  **Cancel or rerun a run**
238
304
 
@@ -15,6 +15,7 @@ avrea_cli/auth.py
15
15
  avrea_cli/billing.py
16
16
  avrea_cli/click_ext.py
17
17
  avrea_cli/config.py
18
+ avrea_cli/device_auth.py
18
19
  avrea_cli/display.py
19
20
  avrea_cli/docs_gen.py
20
21
  avrea_cli/firewall.py
@@ -24,6 +25,8 @@ avrea_cli/json_output.py
24
25
  avrea_cli/log_display.py
25
26
  avrea_cli/main.py
26
27
  avrea_cli/metrics_display.py
28
+ avrea_cli/metrics_stream.py
29
+ avrea_cli/native_dispatch.py
27
30
  avrea_cli/output.py
28
31
  avrea_cli/paths.py
29
32
  avrea_cli/repo_context.py
@@ -34,6 +37,7 @@ avrea_cli/spawn_script.py
34
37
  avrea_cli/transport.py
35
38
  avrea_cli/version.py
36
39
  avrea_cli/vm.py
40
+ avrea_cli/vm_state_stream.py
37
41
  avrea_cli/assets/logo_mark.svg
38
42
  avrea_cli/commands/__init__.py
39
43
  avrea_cli/commands/artifact.py
@@ -45,12 +49,14 @@ avrea_cli/commands/internal.py
45
49
  avrea_cli/commands/job.py
46
50
  avrea_cli/commands/log.py
47
51
  avrea_cli/commands/org.py
52
+ avrea_cli/commands/org_install_runner.py
48
53
  avrea_cli/commands/pr.py
49
54
  avrea_cli/commands/repo.py
50
55
  avrea_cli/commands/run.py
51
56
  avrea_cli/commands/sbom.py
52
57
  avrea_cli/commands/settings.py
53
58
  avrea_cli/commands/status.py
59
+ avrea_cli/commands/tests.py
54
60
  avrea_cli/commands/token.py
55
61
  avrea_cli/commands/workflow.py
56
62
  tests/test_api_client.py
@@ -58,15 +64,19 @@ tests/test_artifact.py
58
64
  tests/test_auth.py
59
65
  tests/test_auth_login.py
60
66
  tests/test_auth_sso.py
67
+ tests/test_billing.py
61
68
  tests/test_cache.py
69
+ tests/test_compatible_lazy_imports.py
62
70
  tests/test_config.py
63
71
  tests/test_config_cmd.py
72
+ tests/test_device_auth.py
64
73
  tests/test_display.py
65
74
  tests/test_docs_gen.py
66
75
  tests/test_env_conventions.py
67
76
  tests/test_firewall.py
68
77
  tests/test_global_flags.py
69
78
  tests/test_helpers.py
79
+ tests/test_job_gitlab.py
70
80
  tests/test_job_list.py
71
81
  tests/test_job_metrics_cmd.py
72
82
  tests/test_job_view_logs.py
@@ -74,7 +84,12 @@ tests/test_json_output.py
74
84
  tests/test_lazy_registry.py
75
85
  tests/test_log_display.py
76
86
  tests/test_metrics_display.py
87
+ tests/test_metrics_stream.py
88
+ tests/test_native_ci.py
89
+ tests/test_native_workflow_dispatch.py
77
90
  tests/test_org_email_domain.py
91
+ tests/test_org_install_runners.py
92
+ tests/test_org_installations.py
78
93
  tests/test_org_saml.py
79
94
  tests/test_output.py
80
95
  tests/test_piped_output.py
@@ -93,11 +108,14 @@ tests/test_run_view.py
93
108
  tests/test_sbom.py
94
109
  tests/test_scoped_token.py
95
110
  tests/test_settings.py
111
+ tests/test_tests.py
96
112
  tests/test_token.py
97
113
  tests/test_token_output.py
98
114
  tests/test_transport.py
99
115
  tests/test_vm.py
116
+ tests/test_vm_alignment.py
100
117
  tests/test_vm_spawn_script.py
118
+ tests/test_vm_state_stream.py
101
119
  tests/test_web_flag.py
102
120
  tests/test_workflow_actions.py
103
121
  tests/test_workflow_run.py
@@ -4,13 +4,15 @@ click>=8.3.1
4
4
  httpx>=0.28.1
5
5
  platformdirs>=4.0
6
6
  prettytable>=3.17.0
7
+ segno>=1.6.6
7
8
  pydantic>=2.12.4
8
9
  typing-extensions>=4.15.0
9
10
  idna>=3.15
11
+ websockets>=15
10
12
 
11
13
  [dev]
12
14
  pytest>=9.0.3
13
15
  pytest-asyncio>=1.3.0
14
16
  pytest-timeout>=2.4.0
15
- ruff==0.15.13
17
+ ruff==0.17.3
16
18
  ty==0.0.15
@@ -1,2 +1,2 @@
1
1
  # Auto-generated by .github/workflows/release.yml; do not edit.
2
- GIT_COMMIT = "620a47d50c11e7e1905980829c01fe2d974fe837"
2
+ GIT_COMMIT = "c9e1e4b6a11db522d142ec0dfaeb8119917ec41c"
@@ -112,6 +112,7 @@ class ApiClient:
112
112
  content: str | bytes | None = None,
113
113
  params: dict[str, Any] | None = None,
114
114
  content_type: str | None = None,
115
+ idempotency_key: str | None = None,
115
116
  ) -> dict[str, Any]:
116
117
  """POST request to public API."""
117
118
  if json is not None and content is not None:
@@ -121,6 +122,8 @@ class ApiClient:
121
122
  headers = self.config.get_api_headers()
122
123
  if content_type is not None:
123
124
  headers = {**headers, "Content-Type": content_type}
125
+ if idempotency_key is not None:
126
+ headers = {**headers, "Idempotency-Key": idempotency_key}
124
127
  try:
125
128
  response = self._http.post(
126
129
  url,
@@ -137,11 +140,16 @@ class ApiClient:
137
140
  response.raise_for_status()
138
141
  return response.json()
139
142
 
140
- def public_put(self, path: str, json: dict[str, Any] | None = None) -> dict[str, Any]:
143
+ def public_put(self, path: str, json: dict[str, Any] | None = None, timeout: float | None = None) -> dict[str, Any]:
141
144
  """PUT request to public API."""
142
145
  url = f"{self.config.public_api_url}{path}"
143
146
  try:
144
- response = self._http.put(url, headers=self.config.get_api_headers(), timeout=self.timeout, json=json)
147
+ response = self._http.put(
148
+ url,
149
+ headers=self.config.get_api_headers(),
150
+ timeout=timeout if timeout is not None else self.timeout,
151
+ json=json,
152
+ )
145
153
  except httpx.HTTPError as exc:
146
154
  self._log("PUT", url, type(exc).__name__)
147
155
  raise
@@ -149,7 +157,12 @@ class ApiClient:
149
157
  response.raise_for_status()
150
158
  return response.json()
151
159
 
152
- def public_patch(self, path: str, json: dict[str, Any] | None = None) -> dict[str, Any] | None:
160
+ def public_patch(
161
+ self,
162
+ path: str,
163
+ json: dict[str, Any] | None = None,
164
+ timeout: float | None = None,
165
+ ) -> dict[str, Any] | None:
153
166
  """PATCH request to public API.
154
167
 
155
168
  Mirrors ``public_delete``'s empty-response handling: a 204 No
@@ -159,8 +172,14 @@ class ApiClient:
159
172
  non-JSON crash on the client would mask the actual outcome.
160
173
  """
161
174
  url = f"{self.config.public_api_url}{path}"
175
+ effective_timeout = timeout if timeout is not None else self.timeout
162
176
  try:
163
- response = self._http.patch(url, headers=self.config.get_api_headers(), timeout=self.timeout, json=json)
177
+ response = self._http.patch(
178
+ url,
179
+ headers=self.config.get_api_headers(),
180
+ timeout=effective_timeout,
181
+ json=json,
182
+ )
164
183
  except httpx.HTTPError as exc:
165
184
  self._log("PATCH", url, type(exc).__name__)
166
185
  raise
@@ -404,6 +404,8 @@ def _auth_url(public_api_url: str, callback_uri: str, *, provider: str, saml_log
404
404
  return f"{public_api_url}/oauth/google/login?final_redirect={callback_uri}"
405
405
  if provider == "github":
406
406
  return f"{public_api_url}/oauth/github/login?next={callback_uri}"
407
+ if provider == "gitlab":
408
+ return f"{public_api_url}/oauth/gitlab/login?next={callback_uri}"
407
409
  raise click.ClickException(f"Unsupported auth provider '{provider}'")
408
410
 
409
411
 
@@ -401,8 +401,8 @@ def payment_methods_add(ctx, org_id, number, exp_month, exp_year, cvc):
401
401
  try:
402
402
  body = exc.response.json()
403
403
  detail = body.get("error", {}).get("message", "")
404
- except Exception:
405
- pass
404
+ except ValueError, AttributeError:
405
+ detail = ""
406
406
  click.echo(f"Error: Stripe rejected the card details.{f' ({detail})' if detail else ''}", err=True)
407
407
  raise click.Abort() from None
408
408
  except httpx.RequestError as e: