lcloud-cli 0.1.0__py3-none-any.whl

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,198 @@
1
+ """Pydantic models for Lambda Cloud API resources.
2
+
3
+ All models ignore unknown fields so the CLI keeps working when the API adds
4
+ new attributes.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ from datetime import datetime
10
+ from enum import Enum
11
+
12
+ from pydantic import BaseModel, ConfigDict
13
+
14
+
15
+ class APIModel(BaseModel):
16
+ """Base model that tolerates unknown fields from the API."""
17
+
18
+ model_config = ConfigDict(extra="ignore")
19
+
20
+
21
+ class Region(APIModel):
22
+ name: str
23
+ description: str = ""
24
+
25
+
26
+ class InstanceTypeSpecs(APIModel):
27
+ vcpus: int
28
+ memory_gib: int
29
+ storage_gib: int
30
+ gpus: int
31
+
32
+
33
+ class InstanceType(APIModel):
34
+ name: str
35
+ description: str = ""
36
+ gpu_description: str = ""
37
+ price_cents_per_hour: int
38
+ specs: InstanceTypeSpecs
39
+ architecture: str = ""
40
+
41
+ @property
42
+ def price_per_hour(self) -> float:
43
+ return self.price_cents_per_hour / 100
44
+
45
+
46
+ class InstanceTypeOffer(APIModel):
47
+ """An instance type together with the regions where capacity is available."""
48
+
49
+ instance_type: InstanceType
50
+ regions_with_capacity_available: list[Region] = []
51
+
52
+
53
+ class InstanceStatus(str, Enum):
54
+ BOOTING = "booting"
55
+ ACTIVE = "active"
56
+ UNHEALTHY = "unhealthy"
57
+ TERMINATED = "terminated"
58
+ TERMINATING = "terminating"
59
+ PREEMPTED = "preempted"
60
+
61
+
62
+ class FilesystemMountEntry(APIModel):
63
+ mount_point: str
64
+ file_system_id: str
65
+
66
+
67
+ class TagEntry(APIModel):
68
+ key: str
69
+ value: str
70
+
71
+
72
+ class FirewallRulesetEntry(APIModel):
73
+ id: str
74
+
75
+
76
+ class Instance(APIModel):
77
+ id: str
78
+ name: str | None = None
79
+ ip: str | None = None
80
+ private_ip: str | None = None
81
+ status: InstanceStatus
82
+ ssh_key_names: list[str] = []
83
+ file_system_names: list[str] = []
84
+ file_system_mounts: list[FilesystemMountEntry] | None = None
85
+ region: Region | None = None
86
+ instance_type: InstanceType | None = None
87
+ hostname: str | None = None
88
+ jupyter_url: str | None = None
89
+ tags: list[TagEntry] | None = None
90
+ firewall_rulesets: list[FirewallRulesetEntry] | None = None
91
+
92
+
93
+ class InstanceLaunchResponse(APIModel):
94
+ instance_ids: list[str]
95
+
96
+
97
+ class InstanceRestartResponse(APIModel):
98
+ restarted_instances: list[Instance]
99
+
100
+
101
+ class InstanceTerminateResponse(APIModel):
102
+ terminated_instances: list[Instance]
103
+
104
+
105
+ class SSHKey(APIModel):
106
+ id: str
107
+ name: str
108
+ public_key: str
109
+
110
+
111
+ class GeneratedSSHKey(SSHKey):
112
+ private_key: str
113
+
114
+
115
+ class User(APIModel):
116
+ id: str
117
+ email: str
118
+ status: str = ""
119
+
120
+
121
+ class Filesystem(APIModel):
122
+ id: str
123
+ name: str
124
+ mount_point: str
125
+ created: datetime | None = None
126
+ created_by: User | None = None
127
+ is_in_use: bool = False
128
+ region: Region | None = None
129
+ bytes_used: int | None = None
130
+
131
+
132
+ class Image(APIModel):
133
+ id: str
134
+ name: str
135
+ description: str = ""
136
+ family: str = ""
137
+ version: str = ""
138
+ architecture: str = ""
139
+ region: Region | None = None
140
+ created_time: datetime | None = None
141
+ updated_time: datetime | None = None
142
+
143
+
144
+ class NetworkProtocol(str, Enum):
145
+ TCP = "tcp"
146
+ UDP = "udp"
147
+ ICMP = "icmp"
148
+ ALL = "all"
149
+
150
+
151
+ class FirewallRule(APIModel):
152
+ protocol: NetworkProtocol
153
+ port_range: tuple[int, int] | None = None
154
+ source_network: str
155
+ description: str = ""
156
+
157
+ @property
158
+ def ports_display(self) -> str:
159
+ if self.protocol is NetworkProtocol.ICMP or self.port_range is None:
160
+ return "-"
161
+ low, high = self.port_range
162
+ return str(low) if low == high else f"{low}-{high}"
163
+
164
+
165
+ class FirewallRuleset(APIModel):
166
+ id: str
167
+ name: str
168
+ region: Region | None = None
169
+ rules: list[FirewallRule] = []
170
+ created: datetime | None = None
171
+ instance_ids: list[str] = []
172
+
173
+
174
+ class GlobalFirewallRuleset(APIModel):
175
+ id: str
176
+ name: str = ""
177
+ rules: list[FirewallRule] = []
178
+
179
+
180
+ class AuditResult(APIModel):
181
+ status: str
182
+ status_code: int | None = None
183
+
184
+
185
+ class AuditEvent(APIModel):
186
+ event_id: str
187
+ event_time: datetime | str
188
+ action: str
189
+ service_name: str = ""
190
+ resource_name: str = ""
191
+ catalog_version: str = ""
192
+ actor_email: str | None = None
193
+ actor_display_name: str | None = None
194
+ resource_lrns: list[str] = []
195
+ client_ip: str | None = None
196
+ client_user_agent: str | None = None
197
+ surface: str | None = None
198
+ result: AuditResult | None = None
@@ -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
+ [![CI](https://github.com/mrprokl/lambda-cloud-cli/actions/workflows/ci.yml/badge.svg)](https://github.com/mrprokl/lambda-cloud-cli/actions/workflows/ci.yml)
37
+ [![PyPI](https://img.shields.io/pypi/v/lcloud-cli)](https://pypi.org/project/lcloud-cli/)
38
+ [![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](LICENSE)
39
+ [![Python](https://img.shields.io/badge/python-3.10%2B-blue.svg)](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,34 @@
1
+ lambda_cloud/__init__.py,sha256=Vor4v_WsX85G0EngVv5YRj3_mXIwDZUg68OZHsSXn5E,80
2
+ lambda_cloud/__main__.py,sha256=4fC1kF8-teRPt8naDGjGMzFAQjoIQndyA-Q26ueL8cc,104
3
+ lambda_cloud/api/__init__.py,sha256=m2-ps6t6CfGtrYO5KjmtB2s-iliqxTJWsnbXUIWShI4,363
4
+ lambda_cloud/api/client.py,sha256=eb-g2FOsy1WCUEJ8vgr9Qe9GXAvsvos_1JpdNdD0GuA,5089
5
+ lambda_cloud/api/service.py,sha256=oHgmYSG6Z-tZGopqr8j7fkzrYMzO_BDj5zBHD6gPMt4,8427
6
+ lambda_cloud/cli/__init__.py,sha256=dHVLlTp2UXSVSSLvgrwWhik79t7HJD17zGiyt0Ixxps,36
7
+ lambda_cloud/cli/app.py,sha256=_mHGR_s59-hNJpqAh-39mRfapneZ_nLkQohjJYsQb_Q,3110
8
+ lambda_cloud/cli/state.py,sha256=exb7y8jHnI05kEtYIMcJs_VvtUOxMczvXlzpFDnGdGA,2181
9
+ lambda_cloud/cli/commands/__init__.py,sha256=rJF3EBYw66IPPeaGbZsk4ujIsEvEH08ls3TDbpYFSQ8,60
10
+ lambda_cloud/cli/commands/audit.py,sha256=l_VwEbxY-F1ZUuS3YxBNpCWJg3M17xW0D0Wm76HXGnw,1137
11
+ lambda_cloud/cli/commands/auth.py,sha256=8XV2xfchBegiw9XxFduk1ml7xGdDcDZFBb0AckuNy9k,2075
12
+ lambda_cloud/cli/commands/completion.py,sha256=ZQVrH3DE18IYXE254mQMxGikhuY2BGn3TWUIKeHCdAI,1176
13
+ lambda_cloud/cli/commands/config_cmd.py,sha256=qoWeCQ0DvA8G_0nBlYc8fsdLn1TvBAFY9Nwga7oRxVo,1436
14
+ lambda_cloud/cli/commands/filesystems.py,sha256=0CIEFqpeLI3Z5Qs_lp4RGNIpDZKsCNE5kFofZD2npV0,1763
15
+ lambda_cloud/cli/commands/firewall.py,sha256=_GYzFwzZhYgMD_g4oZWxY8QcnPb5iXmeO-pCxr4Uvic,5657
16
+ lambda_cloud/cli/commands/images.py,sha256=HRaXwe_83cIoKPGKE5Q4kU-U0aFvA_ZhHzB3tm32Cu8,776
17
+ lambda_cloud/cli/commands/instance_types.py,sha256=B0rrwwdefcfZPuDkKBm8hWGmExbrBUVkSD2cymDpDfs,599
18
+ lambda_cloud/cli/commands/instances.py,sha256=KBu5ib446ptAcRLCi9GtmqiW2Cd8Pqf2Lo4v8NAJ3a0,7584
19
+ lambda_cloud/cli/commands/regions.py,sha256=Q86KvgfSJOYFd_6dpDh_Luq_Vv3ekX3_CJgzvqoQXT4,501
20
+ lambda_cloud/cli/commands/ssh_keys.py,sha256=FUGlHuIrSYkQY3clV8ae5fWxLFPz9A0d7lbT7EZVE4w,3255
21
+ lambda_cloud/cli/ui/__init__.py,sha256=kPHAqtlN0TYe9QYADKE3C2kHzMycIZAEar3c0ww3Is0,298
22
+ lambda_cloud/cli/ui/console.py,sha256=jglSJ_8SYO9HUWDARNLI1Xxaa42xbMSSX0w5WpGwtUs,2845
23
+ lambda_cloud/cli/ui/history.py,sha256=Fi1e3DJweCZw1EaG_NAsARzHANVXblaPrJTyZA6Az78,1707
24
+ lambda_cloud/cli/ui/tables.py,sha256=1rufRsRNVSBInHHtqX5F4VuOTfDhOSCV3ql-2Mq8szg,8229
25
+ lambda_cloud/core/__init__.py,sha256=_93pFaX96u1AgjToqJp5EYgKIT5rM--LGw_tdGdoccg,69
26
+ lambda_cloud/core/config.py,sha256=MIcjfPm0jssJ9O9cbxJNXxiA7u29FSobgSCSAKBvC5E,3859
27
+ lambda_cloud/core/errors.py,sha256=qVL-ONLe5ZMcqwxQ1CkhpevaHAWp7LKwWBiScG0BXAg,1742
28
+ lambda_cloud/mngr/__init__.py,sha256=duIKAAb8NMHWAqWS_5K2OjHSzgENQzEC0ufPGBEBwB4,100
29
+ lambda_cloud/mngr/models.py,sha256=Me0earhloYsjdrDeswT4cXotzvFVYjSXlhkw4SoUlK0,4294
30
+ lcloud_cli-0.1.0.dist-info/METADATA,sha256=iQZkXWFZhhr5jK4bHgweneGLnTf9DISi9zA4GFAoiMA,6896
31
+ lcloud_cli-0.1.0.dist-info/WHEEL,sha256=zOwg4jB6zX2kU910N-cMawjivD6tO8NEWvE12je1bVk,87
32
+ lcloud_cli-0.1.0.dist-info/entry_points.txt,sha256=g6jPOX-Pw1ZaNhI_eqyak8o_HbKa7Bn--vQ93sasfgA,59
33
+ lcloud_cli-0.1.0.dist-info/licenses/LICENSE,sha256=iN6c6rS4SgEwg2PMC9JjG3BC-OpZLGa6IDPglK572iw,1069
34
+ lcloud_cli-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.32.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ lambda-cloud = lambda_cloud.cli.app:main
@@ -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.