lcloud-cli 0.1.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.
- lcloud_cli-0.1.0/.github/workflows/ci.yml +61 -0
- lcloud_cli-0.1.0/.gitignore +34 -0
- lcloud_cli-0.1.0/CHANGELOG.md +38 -0
- lcloud_cli-0.1.0/CONTRIBUTING.md +44 -0
- lcloud_cli-0.1.0/LICENSE +21 -0
- lcloud_cli-0.1.0/PKG-INFO +217 -0
- lcloud_cli-0.1.0/README.md +184 -0
- lcloud_cli-0.1.0/pyproject.toml +87 -0
- lcloud_cli-0.1.0/src/lambda_cloud/__init__.py +3 -0
- lcloud_cli-0.1.0/src/lambda_cloud/__main__.py +6 -0
- lcloud_cli-0.1.0/src/lambda_cloud/api/__init__.py +17 -0
- lcloud_cli-0.1.0/src/lambda_cloud/api/client.py +150 -0
- lcloud_cli-0.1.0/src/lambda_cloud/api/service.py +242 -0
- lcloud_cli-0.1.0/src/lambda_cloud/cli/__init__.py +1 -0
- lcloud_cli-0.1.0/src/lambda_cloud/cli/app.py +111 -0
- lcloud_cli-0.1.0/src/lambda_cloud/cli/commands/__init__.py +1 -0
- lcloud_cli-0.1.0/src/lambda_cloud/cli/commands/audit.py +37 -0
- lcloud_cli-0.1.0/src/lambda_cloud/cli/commands/auth.py +69 -0
- lcloud_cli-0.1.0/src/lambda_cloud/cli/commands/completion.py +37 -0
- lcloud_cli-0.1.0/src/lambda_cloud/cli/commands/config_cmd.py +45 -0
- lcloud_cli-0.1.0/src/lambda_cloud/cli/commands/filesystems.py +53 -0
- lcloud_cli-0.1.0/src/lambda_cloud/cli/commands/firewall.py +157 -0
- lcloud_cli-0.1.0/src/lambda_cloud/cli/commands/images.py +23 -0
- lcloud_cli-0.1.0/src/lambda_cloud/cli/commands/instance_types.py +19 -0
- lcloud_cli-0.1.0/src/lambda_cloud/cli/commands/instances.py +215 -0
- lcloud_cli-0.1.0/src/lambda_cloud/cli/commands/regions.py +19 -0
- lcloud_cli-0.1.0/src/lambda_cloud/cli/commands/ssh_keys.py +99 -0
- lcloud_cli-0.1.0/src/lambda_cloud/cli/state.py +66 -0
- lcloud_cli-0.1.0/src/lambda_cloud/cli/ui/__init__.py +13 -0
- lcloud_cli-0.1.0/src/lambda_cloud/cli/ui/console.py +94 -0
- lcloud_cli-0.1.0/src/lambda_cloud/cli/ui/history.py +62 -0
- lcloud_cli-0.1.0/src/lambda_cloud/cli/ui/tables.py +258 -0
- lcloud_cli-0.1.0/src/lambda_cloud/core/__init__.py +1 -0
- lcloud_cli-0.1.0/src/lambda_cloud/core/config.py +125 -0
- lcloud_cli-0.1.0/src/lambda_cloud/core/errors.py +58 -0
- lcloud_cli-0.1.0/src/lambda_cloud/mngr/__init__.py +5 -0
- lcloud_cli-0.1.0/src/lambda_cloud/mngr/models.py +198 -0
- lcloud_cli-0.1.0/tests/commands/test_instances.py +188 -0
- lcloud_cli-0.1.0/tests/commands/test_ssh_keys.py +109 -0
- lcloud_cli-0.1.0/tests/conftest.py +73 -0
- lcloud_cli-0.1.0/tests/test_client.py +80 -0
- lcloud_cli-0.1.0/tests/test_config.py +64 -0
- lcloud_cli-0.1.0/tests/test_service.py +85 -0
- lcloud_cli-0.1.0/uv.lock +618 -0
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
|
|
8
|
+
permissions:
|
|
9
|
+
contents: read
|
|
10
|
+
|
|
11
|
+
concurrency:
|
|
12
|
+
group: ${{ github.workflow }}-${{ github.ref }}
|
|
13
|
+
cancel-in-progress: true
|
|
14
|
+
|
|
15
|
+
jobs:
|
|
16
|
+
lint:
|
|
17
|
+
name: lint (ruff)
|
|
18
|
+
runs-on: ubuntu-latest
|
|
19
|
+
steps:
|
|
20
|
+
- uses: actions/checkout@v4
|
|
21
|
+
- uses: astral-sh/setup-uv@v5
|
|
22
|
+
- name: Run ruff check
|
|
23
|
+
run: uvx ruff@0.15.4 check src tests
|
|
24
|
+
- name: Check formatting
|
|
25
|
+
run: uvx ruff@0.15.4 format --check src tests
|
|
26
|
+
|
|
27
|
+
test:
|
|
28
|
+
name: test (python ${{ matrix.python-version }})
|
|
29
|
+
runs-on: ubuntu-latest
|
|
30
|
+
strategy:
|
|
31
|
+
fail-fast: false
|
|
32
|
+
matrix:
|
|
33
|
+
python-version: ["3.10", "3.11", "3.12", "3.13"]
|
|
34
|
+
steps:
|
|
35
|
+
- uses: actions/checkout@v4
|
|
36
|
+
- uses: astral-sh/setup-uv@v5
|
|
37
|
+
- name: Set up Python ${{ matrix.python-version }}
|
|
38
|
+
uses: actions/setup-python@v5
|
|
39
|
+
with:
|
|
40
|
+
python-version: ${{ matrix.python-version }}
|
|
41
|
+
- name: Install dependencies
|
|
42
|
+
run: uv pip install --system -e '.[dev]'
|
|
43
|
+
- name: Run pytest
|
|
44
|
+
run: pytest --cov --cov-report=term-missing
|
|
45
|
+
|
|
46
|
+
build:
|
|
47
|
+
name: build package
|
|
48
|
+
runs-on: ubuntu-latest
|
|
49
|
+
needs: [lint, test]
|
|
50
|
+
steps:
|
|
51
|
+
- uses: actions/checkout@v4
|
|
52
|
+
- uses: astral-sh/setup-uv@v5
|
|
53
|
+
- name: Build distributions
|
|
54
|
+
run: uv build
|
|
55
|
+
- name: Check distribution metadata
|
|
56
|
+
run: uvx twine check 'dist/*'
|
|
57
|
+
- uses: actions/upload-artifact@v4
|
|
58
|
+
with:
|
|
59
|
+
name: dist
|
|
60
|
+
path: dist/
|
|
61
|
+
if-no-files-found: error
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# Byte-compiled / optimized
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
|
|
6
|
+
# Distribution / packaging
|
|
7
|
+
build/
|
|
8
|
+
dist/
|
|
9
|
+
*.egg-info/
|
|
10
|
+
.eggs/
|
|
11
|
+
|
|
12
|
+
# Virtual environments
|
|
13
|
+
.venv/
|
|
14
|
+
venv/
|
|
15
|
+
|
|
16
|
+
# Test / coverage reports
|
|
17
|
+
.pytest_cache/
|
|
18
|
+
.coverage
|
|
19
|
+
.coverage.*
|
|
20
|
+
htmlcov/
|
|
21
|
+
coverage.xml
|
|
22
|
+
|
|
23
|
+
# Ruff / mypy caches
|
|
24
|
+
.ruff_cache/
|
|
25
|
+
.mypy_cache/
|
|
26
|
+
|
|
27
|
+
# Local config / secrets
|
|
28
|
+
.env
|
|
29
|
+
*.pem
|
|
30
|
+
config.json
|
|
31
|
+
*.key
|
|
32
|
+
|
|
33
|
+
# OS
|
|
34
|
+
.DS_Store
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
## [0.1.0] - 2026-08-20
|
|
11
|
+
|
|
12
|
+
Initial release of the community Lambda Cloud CLI.
|
|
13
|
+
|
|
14
|
+
### Added
|
|
15
|
+
|
|
16
|
+
- Authentication flow: `login`, `logout`, `whoami`, `config show`
|
|
17
|
+
(API key from flag > `LAMBDA_API_KEY` > config file with 0600 permissions).
|
|
18
|
+
- `instances`: list, get, launch (tags, filesystems, image, user-data,
|
|
19
|
+
firewall rulesets), restart, terminate (with confirmation), rename.
|
|
20
|
+
- `types list`: instance types with specs, hourly price and live capacity.
|
|
21
|
+
- `ssh-keys`: list, add (upload existing key or generate a pair — private key
|
|
22
|
+
written with 0600 permissions), delete.
|
|
23
|
+
- `filesystems`: list, create, delete.
|
|
24
|
+
- `images list` with `--region` / `--family` filters.
|
|
25
|
+
- `regions list`.
|
|
26
|
+
- `firewall rulesets` (list/get/create/update/delete) and
|
|
27
|
+
`firewall global` (get/update) with local rule validation.
|
|
28
|
+
- `audit list` with pagination support (`--all`).
|
|
29
|
+
- Global options: `--output table|json`, `--api-key`, `--verbose`,
|
|
30
|
+
`--version`, `--show-completion`.
|
|
31
|
+
- HTTP client honouring the documented API rate limits (1 req/s), retrying
|
|
32
|
+
`429` responses with `Retry-After`, and surfacing the API's structured
|
|
33
|
+
error codes.
|
|
34
|
+
- Test suite (pytest + respx) and CI (ruff, pytest on Python 3.10–3.13,
|
|
35
|
+
package build).
|
|
36
|
+
|
|
37
|
+
[Unreleased]: https://github.com/mrprokl/lambda-cloud-cli/compare/v0.1.0...HEAD
|
|
38
|
+
[0.1.0]: https://github.com/mrprokl/lambda-cloud-cli/releases/tag/v0.1.0
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# Contributing to lambda-cloud-cli
|
|
2
|
+
|
|
3
|
+
Thanks for helping the community! Here is the short version:
|
|
4
|
+
|
|
5
|
+
## Setup
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
git clone https://github.com/mrprokl/lambda-cloud-cli.git
|
|
9
|
+
cd lambda-cloud-cli
|
|
10
|
+
uv venv
|
|
11
|
+
uv pip install -e '.[dev]'
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## Checks to run before opening a PR
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
uv run ruff format src tests
|
|
18
|
+
uv run ruff check --fix src tests
|
|
19
|
+
uv run pytest --cov
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Guidelines
|
|
23
|
+
|
|
24
|
+
- **No secrets in the repo, ever.** The test suite never performs real
|
|
25
|
+
network calls: mock the API with [`respx`](https://lundberg.github.io/respx/).
|
|
26
|
+
- Keep the layered architecture; dependencies flow strictly downward:
|
|
27
|
+
|
|
28
|
+
```
|
|
29
|
+
cli/commands → cli/ui → api (client/service) → core / mngr
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
New endpoint? Add the function to `api/service.py`, the table to
|
|
33
|
+
`cli/ui/tables.py`, the command to `cli/commands/`, and tests under `tests/`.
|
|
34
|
+
- Keep models tolerant to API additions (`extra="ignore"` in
|
|
35
|
+
`mngr/models.py`).
|
|
36
|
+
- Commit messages follow [Conventional Commits](https://www.conventionalcommits.org/):
|
|
37
|
+
`feat:`, `fix:`, `docs:`, `chore:`, `refactor:`, `test:`.
|
|
38
|
+
- Public API and behaviors are documented in the README: keep it in sync.
|
|
39
|
+
|
|
40
|
+
## Reporting issues
|
|
41
|
+
|
|
42
|
+
Include: the command you ran, the output (use `--output json` when useful),
|
|
43
|
+
your Python version (`python --version`) and the CLI version
|
|
44
|
+
(`lambda-cloud --version`). Never paste your API key.
|
lcloud_cli-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Thomas Gomez
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: lcloud-cli
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Unofficial community command-line interface for the Lambda Cloud API
|
|
5
|
+
Project-URL: Homepage, https://github.com/mrprokl/lambda-cloud-cli
|
|
6
|
+
Project-URL: Repository, https://github.com/mrprokl/lambda-cloud-cli
|
|
7
|
+
Project-URL: Issues, https://github.com/mrprokl/lambda-cloud-cli/issues
|
|
8
|
+
Project-URL: Changelog, https://github.com/mrprokl/lambda-cloud-cli/blob/main/CHANGELOG.md
|
|
9
|
+
Project-URL: Documentation, https://docs.lambda.ai/public-cloud/
|
|
10
|
+
Author: Thomas Gomez
|
|
11
|
+
License: MIT
|
|
12
|
+
License-File: LICENSE
|
|
13
|
+
Keywords: cli,cloud,gpu,lambda,lambda-labs
|
|
14
|
+
Classifier: Environment :: Console
|
|
15
|
+
Classifier: Intended Audience :: Developers
|
|
16
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
17
|
+
Classifier: Programming Language :: Python :: 3
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
22
|
+
Classifier: Topic :: System :: Systems Administration
|
|
23
|
+
Requires-Python: >=3.10
|
|
24
|
+
Requires-Dist: httpx>=0.27
|
|
25
|
+
Requires-Dist: pydantic>=2
|
|
26
|
+
Requires-Dist: rich>=13
|
|
27
|
+
Requires-Dist: typer>=0.12
|
|
28
|
+
Provides-Extra: dev
|
|
29
|
+
Requires-Dist: pytest-cov>=5; extra == 'dev'
|
|
30
|
+
Requires-Dist: pytest>=8; extra == 'dev'
|
|
31
|
+
Requires-Dist: respx>=0.21; extra == 'dev'
|
|
32
|
+
Description-Content-Type: text/markdown
|
|
33
|
+
|
|
34
|
+
# lambda-cloud-cli
|
|
35
|
+
|
|
36
|
+
[](https://github.com/mrprokl/lambda-cloud-cli/actions/workflows/ci.yml)
|
|
37
|
+
[](https://pypi.org/project/lcloud-cli/)
|
|
38
|
+
[](LICENSE)
|
|
39
|
+
[](pyproject.toml)
|
|
40
|
+
|
|
41
|
+
Unofficial community command-line interface for the
|
|
42
|
+
[Lambda Cloud](https://cloud.lambda.ai) API.
|
|
43
|
+
|
|
44
|
+
> **Disclaimer**: this project is **not** affiliated with, endorsed by, or
|
|
45
|
+
> supported by Lambda, Inc. It is built against the
|
|
46
|
+
> [public Lambda Cloud API](https://docs.lambda.ai/public-cloud/).
|
|
47
|
+
|
|
48
|
+
## Installation
|
|
49
|
+
|
|
50
|
+
Requires Python 3.10+.
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
# Option 1 — uv (recommended)
|
|
54
|
+
uv tool install lcloud-cli
|
|
55
|
+
|
|
56
|
+
# Option 2 — pipx
|
|
57
|
+
pipx install lcloud-cli
|
|
58
|
+
|
|
59
|
+
# Option 3 — plain pip
|
|
60
|
+
pip install lcloud-cli
|
|
61
|
+
|
|
62
|
+
# Option 4 — straight from GitHub (bleeding edge)
|
|
63
|
+
uv tool install git+https://github.com/mrprokl/lambda-cloud-cli
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
From source, for development:
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
git clone https://github.com/mrprokl/lambda-cloud-cli.git
|
|
70
|
+
cd lambda-cloud-cli
|
|
71
|
+
uv venv
|
|
72
|
+
uv pip install -e '.[dev]'
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## Quickstart
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
# 1. Authenticate (get your key at https://cloud.lambda.ai/api-keys)
|
|
79
|
+
lambda-cloud login
|
|
80
|
+
|
|
81
|
+
# 2. See what's available
|
|
82
|
+
lambda-cloud regions list
|
|
83
|
+
lambda-cloud types list
|
|
84
|
+
lambda-cloud images list
|
|
85
|
+
|
|
86
|
+
# 3. Launch a GPU instance
|
|
87
|
+
lambda-cloud instances launch \
|
|
88
|
+
--type gpu_1x_a10 \
|
|
89
|
+
--region us-west-1 \
|
|
90
|
+
--ssh-key my-key
|
|
91
|
+
|
|
92
|
+
# 4. Watch it boot
|
|
93
|
+
lambda-cloud instances list
|
|
94
|
+
lambda-cloud instances get <instance-id>
|
|
95
|
+
|
|
96
|
+
# 5. Clean up
|
|
97
|
+
lambda-cloud instances terminate <instance-id>
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
Output is human-friendly tables by default; add `--output json` (or `-o json`)
|
|
101
|
+
to any command for scripting:
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
lambda-cloud -o json instances list | jq '.[].ip'
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
## Authentication
|
|
108
|
+
|
|
109
|
+
The API key is resolved in this order:
|
|
110
|
+
|
|
111
|
+
1. `--api-key` flag
|
|
112
|
+
2. `LAMBDA_API_KEY` environment variable
|
|
113
|
+
3. Config file written by `lambda-cloud login`
|
|
114
|
+
(`$XDG_CONFIG_HOME/lambda-cloud/config.json`, default
|
|
115
|
+
`~/.config/lambda-cloud/config.json`, mode `0600`)
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
lambda-cloud login # interactive prompt, key is validated
|
|
119
|
+
lambda-cloud login --api-key X # non-interactive
|
|
120
|
+
lambda-cloud whoami # which key is in use? is it valid?
|
|
121
|
+
lambda-cloud logout # remove stored key
|
|
122
|
+
lambda-cloud config show # inspect configuration & defaults
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
## Commands
|
|
126
|
+
|
|
127
|
+
| Command | Description |
|
|
128
|
+
| --- | --- |
|
|
129
|
+
| `instances list` | List running instances |
|
|
130
|
+
| `instances get <id>` | Instance details |
|
|
131
|
+
| `instances launch` | Launch an on-demand instance |
|
|
132
|
+
| `instances restart <id>...` | Restart instances |
|
|
133
|
+
| `instances terminate <id>...` | Terminate instances (destructive!) |
|
|
134
|
+
| `instances rename <id> --name X` | Rename an instance |
|
|
135
|
+
| `types list` | Instance types, specs, prices, regional capacity |
|
|
136
|
+
| `ssh-keys list` / `add` / `delete` | Manage SSH keys (can generate a pair) |
|
|
137
|
+
| `filesystems list` / `create` / `delete` | Manage shared filesystems |
|
|
138
|
+
| `images list [--region R] [--family F]` | List machine images |
|
|
139
|
+
| `regions list` | List regions |
|
|
140
|
+
| `firewall rulesets ...` | CRUD on regional firewall rulesets |
|
|
141
|
+
| `firewall global get` / `update` | Global firewall ruleset |
|
|
142
|
+
| `audit list [--all]` | Account audit events (paginated) |
|
|
143
|
+
| `config show` | Show CLI configuration |
|
|
144
|
+
| `login` / `logout` / `whoami` | Credential management |
|
|
145
|
+
|
|
146
|
+
### Launching instances
|
|
147
|
+
|
|
148
|
+
```bash
|
|
149
|
+
lambda-cloud instances launch \
|
|
150
|
+
--type gpu_8x_h100_sxm5 \
|
|
151
|
+
--region us-south-1 \
|
|
152
|
+
--ssh-key my-key \
|
|
153
|
+
--name training-run \
|
|
154
|
+
--filesystem shared-data \
|
|
155
|
+
--tag env=prod --tag team=ml \
|
|
156
|
+
--image-family lambda-stack \
|
|
157
|
+
--user-data ./cloud-init.yaml
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
### Firewall rules files
|
|
161
|
+
|
|
162
|
+
`firewall rulesets create --rules-file rules.json` expects a JSON list:
|
|
163
|
+
|
|
164
|
+
```json
|
|
165
|
+
[
|
|
166
|
+
{"protocol": "tcp", "port_range": [22, 22], "source_network": "0.0.0.0/0", "description": "SSH"},
|
|
167
|
+
{"protocol": "tcp", "port_range": [8888, 8888], "source_network": "203.0.113.0/24", "description": "Jupyter"},
|
|
168
|
+
{"protocol": "icmp", "source_network": "0.0.0.0/0", "description": "Ping"}
|
|
169
|
+
]
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
Rules are validated locally before submission: `icmp` must not define
|
|
173
|
+
`port_range`, every other protocol must.
|
|
174
|
+
|
|
175
|
+
## Environment variables
|
|
176
|
+
|
|
177
|
+
| Variable | Purpose |
|
|
178
|
+
| --- | --- |
|
|
179
|
+
| `LAMBDA_API_KEY` | API key (overrides stored config) |
|
|
180
|
+
| `LAMBDA_CLOUD_CONFIG_DIR` | Override the config directory |
|
|
181
|
+
| `LAMBDA_CLOUD_API_URL` | Override the API base URL (useful for testing) |
|
|
182
|
+
| `LAMBDA_CLOUD_MIN_INTERVAL` | Min seconds between API calls (default `1.05`, per documented rate limit) |
|
|
183
|
+
|
|
184
|
+
## Rate limits
|
|
185
|
+
|
|
186
|
+
Per the API documentation: 1 request/second in general, and 1 request per
|
|
187
|
+
12 seconds on `instance-operations/launch`. The client throttles requests
|
|
188
|
+
client-side and retries `429` responses honouring `Retry-After`.
|
|
189
|
+
|
|
190
|
+
## Development
|
|
191
|
+
|
|
192
|
+
```bash
|
|
193
|
+
uv venv
|
|
194
|
+
uv pip install -e '.[dev]'
|
|
195
|
+
uv run ruff check src tests
|
|
196
|
+
uv run pytest
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
Layout follows a strict layered architecture:
|
|
200
|
+
|
|
201
|
+
```
|
|
202
|
+
src/lambda_cloud/
|
|
203
|
+
├── cli/ # interface: commands, state, ui (console/tables/formatters)
|
|
204
|
+
├── api/ # http client + service layer (request shaping, validation)
|
|
205
|
+
├── core/ # foundations: config, errors
|
|
206
|
+
└── mngr/ # pydantic resource models
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
Dependencies flow strictly downward: `cli → api → core/mngr`.
|
|
210
|
+
|
|
211
|
+
## Contributing
|
|
212
|
+
|
|
213
|
+
Issues and pull requests are welcome! See [CONTRIBUTING.md](CONTRIBUTING.md).
|
|
214
|
+
|
|
215
|
+
## License
|
|
216
|
+
|
|
217
|
+
MIT — see [LICENSE](LICENSE).
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
# lambda-cloud-cli
|
|
2
|
+
|
|
3
|
+
[](https://github.com/mrprokl/lambda-cloud-cli/actions/workflows/ci.yml)
|
|
4
|
+
[](https://pypi.org/project/lcloud-cli/)
|
|
5
|
+
[](LICENSE)
|
|
6
|
+
[](pyproject.toml)
|
|
7
|
+
|
|
8
|
+
Unofficial community command-line interface for the
|
|
9
|
+
[Lambda Cloud](https://cloud.lambda.ai) API.
|
|
10
|
+
|
|
11
|
+
> **Disclaimer**: this project is **not** affiliated with, endorsed by, or
|
|
12
|
+
> supported by Lambda, Inc. It is built against the
|
|
13
|
+
> [public Lambda Cloud API](https://docs.lambda.ai/public-cloud/).
|
|
14
|
+
|
|
15
|
+
## Installation
|
|
16
|
+
|
|
17
|
+
Requires Python 3.10+.
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
# Option 1 — uv (recommended)
|
|
21
|
+
uv tool install lcloud-cli
|
|
22
|
+
|
|
23
|
+
# Option 2 — pipx
|
|
24
|
+
pipx install lcloud-cli
|
|
25
|
+
|
|
26
|
+
# Option 3 — plain pip
|
|
27
|
+
pip install lcloud-cli
|
|
28
|
+
|
|
29
|
+
# Option 4 — straight from GitHub (bleeding edge)
|
|
30
|
+
uv tool install git+https://github.com/mrprokl/lambda-cloud-cli
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
From source, for development:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
git clone https://github.com/mrprokl/lambda-cloud-cli.git
|
|
37
|
+
cd lambda-cloud-cli
|
|
38
|
+
uv venv
|
|
39
|
+
uv pip install -e '.[dev]'
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Quickstart
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
# 1. Authenticate (get your key at https://cloud.lambda.ai/api-keys)
|
|
46
|
+
lambda-cloud login
|
|
47
|
+
|
|
48
|
+
# 2. See what's available
|
|
49
|
+
lambda-cloud regions list
|
|
50
|
+
lambda-cloud types list
|
|
51
|
+
lambda-cloud images list
|
|
52
|
+
|
|
53
|
+
# 3. Launch a GPU instance
|
|
54
|
+
lambda-cloud instances launch \
|
|
55
|
+
--type gpu_1x_a10 \
|
|
56
|
+
--region us-west-1 \
|
|
57
|
+
--ssh-key my-key
|
|
58
|
+
|
|
59
|
+
# 4. Watch it boot
|
|
60
|
+
lambda-cloud instances list
|
|
61
|
+
lambda-cloud instances get <instance-id>
|
|
62
|
+
|
|
63
|
+
# 5. Clean up
|
|
64
|
+
lambda-cloud instances terminate <instance-id>
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Output is human-friendly tables by default; add `--output json` (or `-o json`)
|
|
68
|
+
to any command for scripting:
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
lambda-cloud -o json instances list | jq '.[].ip'
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## Authentication
|
|
75
|
+
|
|
76
|
+
The API key is resolved in this order:
|
|
77
|
+
|
|
78
|
+
1. `--api-key` flag
|
|
79
|
+
2. `LAMBDA_API_KEY` environment variable
|
|
80
|
+
3. Config file written by `lambda-cloud login`
|
|
81
|
+
(`$XDG_CONFIG_HOME/lambda-cloud/config.json`, default
|
|
82
|
+
`~/.config/lambda-cloud/config.json`, mode `0600`)
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
lambda-cloud login # interactive prompt, key is validated
|
|
86
|
+
lambda-cloud login --api-key X # non-interactive
|
|
87
|
+
lambda-cloud whoami # which key is in use? is it valid?
|
|
88
|
+
lambda-cloud logout # remove stored key
|
|
89
|
+
lambda-cloud config show # inspect configuration & defaults
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## Commands
|
|
93
|
+
|
|
94
|
+
| Command | Description |
|
|
95
|
+
| --- | --- |
|
|
96
|
+
| `instances list` | List running instances |
|
|
97
|
+
| `instances get <id>` | Instance details |
|
|
98
|
+
| `instances launch` | Launch an on-demand instance |
|
|
99
|
+
| `instances restart <id>...` | Restart instances |
|
|
100
|
+
| `instances terminate <id>...` | Terminate instances (destructive!) |
|
|
101
|
+
| `instances rename <id> --name X` | Rename an instance |
|
|
102
|
+
| `types list` | Instance types, specs, prices, regional capacity |
|
|
103
|
+
| `ssh-keys list` / `add` / `delete` | Manage SSH keys (can generate a pair) |
|
|
104
|
+
| `filesystems list` / `create` / `delete` | Manage shared filesystems |
|
|
105
|
+
| `images list [--region R] [--family F]` | List machine images |
|
|
106
|
+
| `regions list` | List regions |
|
|
107
|
+
| `firewall rulesets ...` | CRUD on regional firewall rulesets |
|
|
108
|
+
| `firewall global get` / `update` | Global firewall ruleset |
|
|
109
|
+
| `audit list [--all]` | Account audit events (paginated) |
|
|
110
|
+
| `config show` | Show CLI configuration |
|
|
111
|
+
| `login` / `logout` / `whoami` | Credential management |
|
|
112
|
+
|
|
113
|
+
### Launching instances
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
lambda-cloud instances launch \
|
|
117
|
+
--type gpu_8x_h100_sxm5 \
|
|
118
|
+
--region us-south-1 \
|
|
119
|
+
--ssh-key my-key \
|
|
120
|
+
--name training-run \
|
|
121
|
+
--filesystem shared-data \
|
|
122
|
+
--tag env=prod --tag team=ml \
|
|
123
|
+
--image-family lambda-stack \
|
|
124
|
+
--user-data ./cloud-init.yaml
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### Firewall rules files
|
|
128
|
+
|
|
129
|
+
`firewall rulesets create --rules-file rules.json` expects a JSON list:
|
|
130
|
+
|
|
131
|
+
```json
|
|
132
|
+
[
|
|
133
|
+
{"protocol": "tcp", "port_range": [22, 22], "source_network": "0.0.0.0/0", "description": "SSH"},
|
|
134
|
+
{"protocol": "tcp", "port_range": [8888, 8888], "source_network": "203.0.113.0/24", "description": "Jupyter"},
|
|
135
|
+
{"protocol": "icmp", "source_network": "0.0.0.0/0", "description": "Ping"}
|
|
136
|
+
]
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
Rules are validated locally before submission: `icmp` must not define
|
|
140
|
+
`port_range`, every other protocol must.
|
|
141
|
+
|
|
142
|
+
## Environment variables
|
|
143
|
+
|
|
144
|
+
| Variable | Purpose |
|
|
145
|
+
| --- | --- |
|
|
146
|
+
| `LAMBDA_API_KEY` | API key (overrides stored config) |
|
|
147
|
+
| `LAMBDA_CLOUD_CONFIG_DIR` | Override the config directory |
|
|
148
|
+
| `LAMBDA_CLOUD_API_URL` | Override the API base URL (useful for testing) |
|
|
149
|
+
| `LAMBDA_CLOUD_MIN_INTERVAL` | Min seconds between API calls (default `1.05`, per documented rate limit) |
|
|
150
|
+
|
|
151
|
+
## Rate limits
|
|
152
|
+
|
|
153
|
+
Per the API documentation: 1 request/second in general, and 1 request per
|
|
154
|
+
12 seconds on `instance-operations/launch`. The client throttles requests
|
|
155
|
+
client-side and retries `429` responses honouring `Retry-After`.
|
|
156
|
+
|
|
157
|
+
## Development
|
|
158
|
+
|
|
159
|
+
```bash
|
|
160
|
+
uv venv
|
|
161
|
+
uv pip install -e '.[dev]'
|
|
162
|
+
uv run ruff check src tests
|
|
163
|
+
uv run pytest
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
Layout follows a strict layered architecture:
|
|
167
|
+
|
|
168
|
+
```
|
|
169
|
+
src/lambda_cloud/
|
|
170
|
+
├── cli/ # interface: commands, state, ui (console/tables/formatters)
|
|
171
|
+
├── api/ # http client + service layer (request shaping, validation)
|
|
172
|
+
├── core/ # foundations: config, errors
|
|
173
|
+
└── mngr/ # pydantic resource models
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
Dependencies flow strictly downward: `cli → api → core/mngr`.
|
|
177
|
+
|
|
178
|
+
## Contributing
|
|
179
|
+
|
|
180
|
+
Issues and pull requests are welcome! See [CONTRIBUTING.md](CONTRIBUTING.md).
|
|
181
|
+
|
|
182
|
+
## License
|
|
183
|
+
|
|
184
|
+
MIT — see [LICENSE](LICENSE).
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "lcloud-cli"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "Unofficial community command-line interface for the Lambda Cloud API"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.10"
|
|
7
|
+
license = { text = "MIT" }
|
|
8
|
+
authors = [{ name = "Thomas Gomez" }]
|
|
9
|
+
keywords = ["lambda", "lambda-labs", "cloud", "gpu", "cli"]
|
|
10
|
+
classifiers = [
|
|
11
|
+
"Environment :: Console",
|
|
12
|
+
"Intended Audience :: Developers",
|
|
13
|
+
"License :: OSI Approved :: MIT License",
|
|
14
|
+
"Programming Language :: Python :: 3",
|
|
15
|
+
"Programming Language :: Python :: 3.10",
|
|
16
|
+
"Programming Language :: Python :: 3.11",
|
|
17
|
+
"Programming Language :: Python :: 3.12",
|
|
18
|
+
"Programming Language :: Python :: 3.13",
|
|
19
|
+
"Topic :: System :: Systems Administration",
|
|
20
|
+
]
|
|
21
|
+
dependencies = [
|
|
22
|
+
"httpx>=0.27",
|
|
23
|
+
"typer>=0.12",
|
|
24
|
+
"rich>=13",
|
|
25
|
+
"pydantic>=2",
|
|
26
|
+
]
|
|
27
|
+
|
|
28
|
+
[project.optional-dependencies]
|
|
29
|
+
dev = [
|
|
30
|
+
"pytest>=8",
|
|
31
|
+
"respx>=0.21",
|
|
32
|
+
"pytest-cov>=5",
|
|
33
|
+
]
|
|
34
|
+
|
|
35
|
+
[project.urls]
|
|
36
|
+
Homepage = "https://github.com/mrprokl/lambda-cloud-cli"
|
|
37
|
+
Repository = "https://github.com/mrprokl/lambda-cloud-cli"
|
|
38
|
+
Issues = "https://github.com/mrprokl/lambda-cloud-cli/issues"
|
|
39
|
+
Changelog = "https://github.com/mrprokl/lambda-cloud-cli/blob/main/CHANGELOG.md"
|
|
40
|
+
Documentation = "https://docs.lambda.ai/public-cloud/"
|
|
41
|
+
|
|
42
|
+
[project.scripts]
|
|
43
|
+
lambda-cloud = "lambda_cloud.cli.app:main"
|
|
44
|
+
|
|
45
|
+
[build-system]
|
|
46
|
+
requires = ["hatchling"]
|
|
47
|
+
build-backend = "hatchling.build"
|
|
48
|
+
|
|
49
|
+
[tool.hatch.build.targets.wheel]
|
|
50
|
+
packages = ["src/lambda_cloud"]
|
|
51
|
+
|
|
52
|
+
[tool.ruff]
|
|
53
|
+
target-version = "py310"
|
|
54
|
+
line-length = 100
|
|
55
|
+
src = ["src", "tests"]
|
|
56
|
+
|
|
57
|
+
[tool.ruff.lint]
|
|
58
|
+
select = [
|
|
59
|
+
"E", # pycodestyle errors
|
|
60
|
+
"W", # pycodestyle warnings
|
|
61
|
+
"F", # pyflakes
|
|
62
|
+
"I", # isort
|
|
63
|
+
"B", # flake8-bugbear
|
|
64
|
+
"UP", # pyupgrade
|
|
65
|
+
"SIM", # flake8-simplify
|
|
66
|
+
"T20", # no print() outside of Typer/rich wrappers
|
|
67
|
+
]
|
|
68
|
+
|
|
69
|
+
[tool.ruff.lint.per-file-ignores]
|
|
70
|
+
# typer.Option()/typer.Argument() in argument defaults is the idiomatic Typer API.
|
|
71
|
+
"src/lambda_cloud/cli/**" = ["B008"]
|
|
72
|
+
"tests/**" = ["B011", "B017"]
|
|
73
|
+
|
|
74
|
+
[tool.ruff.lint.isort]
|
|
75
|
+
known-first-party = ["lambda_cloud"]
|
|
76
|
+
|
|
77
|
+
[tool.pytest.ini_options]
|
|
78
|
+
testpaths = ["tests"]
|
|
79
|
+
addopts = "-q"
|
|
80
|
+
pythonpath = ["."]
|
|
81
|
+
|
|
82
|
+
[tool.coverage.run]
|
|
83
|
+
source = ["src/lambda_cloud"]
|
|
84
|
+
branch = true
|
|
85
|
+
|
|
86
|
+
[tool.coverage.report]
|
|
87
|
+
exclude_also = ["if TYPE_CHECKING:", "raise typer.Exit"]
|