Metadata-Version: 2.4
Name: isard
Version: 0.4.7
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Console
Classifier: Intended Audience :: Education
Classifier: Operating System :: MacOS
Classifier: Operating System :: Microsoft :: Windows
Classifier: Operating System :: POSIX :: Linux
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Rust
Classifier: Typing :: Typed
License-File: LICENSE-MIT
License-File: LICENSE-APACHE
Summary: Gencat / IsardVDI client: a Python library and the `isard` CLI, in Rust
Keywords: isardvdi,gencat,vdi,saml
Author: David de Mingo
License-Expression: MIT OR Apache-2.0
Requires-Python: >=3.10
Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
Project-URL: Documentation, https://isard.xtec.dev/docs/
Project-URL: Homepage, https://isard.xtec.dev

# isard

Gencat / IsardVDI from Python — and the `isard` command line — in one wheel.
The client is written in Rust and compiled into the package (the way polars
ships), so there is nothing to build and no dependency to resolve.

```bash
pip install isard        # or: uv tool install isard   (just the CLI)
```

## The command line

```bash
isard login
isard list --hardware
isard up windows                      # start it, arm the SSH key, wait for sshd
isard exec windows -- 'Get-Date'      # stdout is data, the exit code is the guest's
isard stop windows --wait
```

`isard --help` lists every command. The same CLI is downloadable as a single
binary from <https://isard.xtec.dev>, with the documentation at <https://isard.xtec.dev/docs/>.

## The library

```python
import isard

client = isard.connect()        # the CLI's cached session, or a fresh login
for desktop in client.get_desktops(hardware=True):
    print(desktop["name"], desktop["state"], desktop["vcpus"])

desktop = client.get_desktops("windows")[0]
client.start_desktop(desktop["id"])
client.wait_desktop(desktop["id"], "Started")
```

- **Credentials** come from the arguments of `connect()` / `login()`, else
  `GENCAT_USERNAME` / `GENCAT_PASSWORD` (a `.env` file works, and so does
  `~/.config/isard/.env`). The library never prompts.
- **An API key** wins: with `ISARD_API_KEY` set (environment or `.env`) and no
  arguments, `connect()` uses that key — no login, no cache, and a 401 is
  raised rather than retried. Make one with `isard api-key create`.
- **Sessions** are shared with the CLI: `connect()` reuses
  `~/.config/isard/isardvdi.json` and caches a new login there, and a 401 on a
  cached token logs in again once and retries.
- **Records** are plain dicts — `Desktop`, `Template`, `Media`, … are
  `TypedDict`s in the type stubs.
- **Errors** are `isard.IsardError` subclasses: `HttpError` (with
  `status_code`), `NetworkError`, `AuthError`, `BookingRequiredError`,
  `IsardTimeoutError`, ….
- Calls block, and release the GIL while they wait — run them in threads freely.
- **Seeds**: `isard.render_cloud_init("box-1", "isard", "pw")` → `(user_data,
  meta_data)`, `isard.render_kickstart(…)`, and `isard.build_seed_iso("cidata",
  {"user-data": …, "meta-data": …}, "seed.iso")` — pure Rust, no xorriso.
- **On a desktop**: `isard.Remote("windows")` runs commands and moves files over the
  route `isard up windows` cached — `.run("Get-Date")["stdout"]`, `.push("~/proj",
  mirror=True)`, `.get(path)`, `.put(data, path)`, `.job(script, result)`. It keeps
  the GIL and stays on the thread that made it.

Licensed MIT OR Apache-2.0.

