layerbrain 0.2.1__tar.gz → 0.3.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.
- {layerbrain-0.2.1 → layerbrain-0.3.0}/.github/workflows/ci.yml +1 -1
- {layerbrain-0.2.1 → layerbrain-0.3.0}/PKG-INFO +70 -73
- {layerbrain-0.2.1 → layerbrain-0.3.0}/README.md +64 -66
- {layerbrain-0.2.1 → layerbrain-0.3.0}/layerbrain/__init__.py +2 -1
- {layerbrain-0.2.1 → layerbrain-0.3.0}/layerbrain/__main__.py +1 -1
- layerbrain-0.3.0/layerbrain/_version.py +1 -0
- layerbrain-0.3.0/layerbrain/cli/__init__.py +1 -0
- layerbrain-0.3.0/layerbrain/cli/__main__.py +3 -0
- layerbrain-0.3.0/layerbrain/cli/_auth_client.py +84 -0
- layerbrain-0.3.0/layerbrain/cli/_input.py +32 -0
- {layerbrain-0.2.1 → layerbrain-0.3.0}/layerbrain/cli/_output.py +3 -3
- {layerbrain-0.2.1 → layerbrain-0.3.0}/layerbrain/cli/commands/accounts.py +5 -78
- {layerbrain-0.2.1 → layerbrain-0.3.0}/layerbrain/cli/commands/api_keys.py +14 -9
- {layerbrain-0.2.1 → layerbrain-0.3.0}/layerbrain/cli/commands/audio.py +8 -11
- {layerbrain-0.2.1 → layerbrain-0.3.0}/layerbrain/cli/commands/auth.py +12 -9
- layerbrain-0.3.0/layerbrain/cli/commands/brains.py +77 -0
- {layerbrain-0.2.1 → layerbrain-0.3.0}/layerbrain/cli/commands/chat.py +2 -4
- {layerbrain-0.2.1 → layerbrain-0.3.0}/layerbrain/cli/commands/compute.py +1 -4
- {layerbrain-0.2.1 → layerbrain-0.3.0}/layerbrain/cli/commands/embeddings.py +8 -9
- {layerbrain-0.2.1 → layerbrain-0.3.0}/layerbrain/cli/commands/images.py +8 -11
- {layerbrain-0.2.1 → layerbrain-0.3.0}/layerbrain/cli/commands/internal.py +5 -7
- layerbrain-0.3.0/layerbrain/cli/commands/listen.py +288 -0
- {layerbrain-0.2.1 → layerbrain-0.3.0}/layerbrain/cli/commands/machines.py +42 -19
- {layerbrain-0.2.1 → layerbrain-0.3.0}/layerbrain/cli/commands/memberships.py +8 -36
- layerbrain-0.3.0/layerbrain/cli/commands/models.py +55 -0
- layerbrain-0.3.0/layerbrain/cli/commands/network_flows.py +54 -0
- layerbrain-0.3.0/layerbrain/cli/commands/network_rules.py +104 -0
- layerbrain-0.3.0/layerbrain/cli/commands/networks.py +71 -0
- {layerbrain-0.2.1 → layerbrain-0.3.0}/layerbrain/cli/commands/organizations.py +11 -7
- {layerbrain-0.2.1 → layerbrain-0.3.0}/layerbrain/cli/commands/secrets.py +11 -22
- layerbrain-0.3.0/layerbrain/cli/commands/snapshots.py +103 -0
- {layerbrain-0.2.1 → layerbrain-0.3.0}/layerbrain/cli/commands/statements.py +1 -4
- layerbrain-0.3.0/layerbrain/cli/commands/storage.py +267 -0
- layerbrain-0.2.1/layerbrain/cli/commands/environments.py → layerbrain-0.3.0/layerbrain/cli/commands/subscriptions.py +19 -17
- layerbrain-0.3.0/layerbrain/cli/commands/threed.py +43 -0
- layerbrain-0.3.0/layerbrain/cli/commands/tools.py +35 -0
- {layerbrain-0.2.1 → layerbrain-0.3.0}/layerbrain/cli/commands/upgrade.py +7 -9
- {layerbrain-0.2.1 → layerbrain-0.3.0}/layerbrain/cli/commands/videos.py +8 -12
- layerbrain-0.3.0/layerbrain/cli/commands/webhooks.py +119 -0
- layerbrain-0.2.1/layerbrain/cli/app.py → layerbrain-0.3.0/layerbrain/cli/main.py +15 -5
- {layerbrain-0.2.1 → layerbrain-0.3.0}/layerbrain/sdk/__init__.py +14 -9
- {layerbrain-0.2.1 → layerbrain-0.3.0}/layerbrain/sdk/_client.py +1 -1
- {layerbrain-0.2.1 → layerbrain-0.3.0}/layerbrain/sdk/_config.py +1 -1
- {layerbrain-0.2.1 → layerbrain-0.3.0}/layerbrain/sdk/_connection.py +3 -3
- {layerbrain-0.2.1 → layerbrain-0.3.0}/layerbrain/sdk/_transport.py +3 -3
- {layerbrain-0.2.1 → layerbrain-0.3.0}/layerbrain/sdk/_types.py +76 -117
- {layerbrain-0.2.1 → layerbrain-0.3.0}/layerbrain/sdk/openapi/openapi.json +3717 -2208
- {layerbrain-0.2.1 → layerbrain-0.3.0}/layerbrain/sdk/openapi/pull.py +19 -10
- {layerbrain-0.2.1 → layerbrain-0.3.0}/layerbrain/sdk/resources/__init__.py +12 -6
- {layerbrain-0.2.1 → layerbrain-0.3.0}/layerbrain/sdk/resources/accounts.py +4 -29
- {layerbrain-0.2.1 → layerbrain-0.3.0}/layerbrain/sdk/resources/api_keys.py +18 -23
- {layerbrain-0.2.1 → layerbrain-0.3.0}/layerbrain/sdk/resources/audio.py +4 -4
- layerbrain-0.3.0/layerbrain/sdk/resources/brains.py +25 -0
- {layerbrain-0.2.1 → layerbrain-0.3.0}/layerbrain/sdk/resources/compute.py +6 -11
- {layerbrain-0.2.1 → layerbrain-0.3.0}/layerbrain/sdk/resources/embeddings.py +1 -1
- {layerbrain-0.2.1 → layerbrain-0.3.0}/layerbrain/sdk/resources/images.py +4 -4
- {layerbrain-0.2.1 → layerbrain-0.3.0}/layerbrain/sdk/resources/machines.py +26 -27
- {layerbrain-0.2.1 → layerbrain-0.3.0}/layerbrain/sdk/resources/memberships.py +4 -17
- {layerbrain-0.2.1 → layerbrain-0.3.0}/layerbrain/sdk/resources/models.py +3 -3
- layerbrain-0.3.0/layerbrain/sdk/resources/network_flows.py +37 -0
- layerbrain-0.3.0/layerbrain/sdk/resources/network_rules.py +49 -0
- layerbrain-0.3.0/layerbrain/sdk/resources/networks.py +41 -0
- {layerbrain-0.2.1 → layerbrain-0.3.0}/layerbrain/sdk/resources/organizations.py +4 -9
- {layerbrain-0.2.1 → layerbrain-0.3.0}/layerbrain/sdk/resources/secrets.py +11 -9
- layerbrain-0.3.0/layerbrain/sdk/resources/snapshots.py +49 -0
- {layerbrain-0.2.1 → layerbrain-0.3.0}/layerbrain/sdk/resources/statements.py +10 -4
- layerbrain-0.3.0/layerbrain/sdk/resources/storage.py +128 -0
- layerbrain-0.3.0/layerbrain/sdk/resources/subscriptions.py +41 -0
- layerbrain-0.3.0/layerbrain/sdk/resources/threed.py +17 -0
- layerbrain-0.3.0/layerbrain/sdk/resources/tools.py +13 -0
- layerbrain-0.3.0/layerbrain/sdk/resources/videos.py +17 -0
- layerbrain-0.3.0/layerbrain/sdk/resources/webhooks.py +77 -0
- layerbrain-0.3.0/openapi/openapi.json +9071 -0
- {layerbrain-0.2.1 → layerbrain-0.3.0}/pyproject.toml +6 -10
- {layerbrain-0.2.1 → layerbrain-0.3.0}/scripts/generate.py +175 -23
- {layerbrain-0.2.1 → layerbrain-0.3.0}/setup.cfg +0 -1
- {layerbrain-0.2.1 → layerbrain-0.3.0}/tests/test_cli/test_auth_commands.py +3 -4
- layerbrain-0.3.0/tests/test_cli/test_listen_command.py +104 -0
- {layerbrain-0.2.1 → layerbrain-0.3.0}/tests/test_cli/test_machines_commands.py +17 -4
- layerbrain-0.3.0/tests/test_cli/test_main_help.py +43 -0
- {layerbrain-0.2.1 → layerbrain-0.3.0}/tests/test_client.py +1 -1
- {layerbrain-0.2.1 → layerbrain-0.3.0}/tests/test_resources/test_machine_connection.py +2 -2
- {layerbrain-0.2.1 → layerbrain-0.3.0}/tests/test_resources/test_machines.py +29 -16
- {layerbrain-0.2.1 → layerbrain-0.3.0}/tests/test_resources/test_models.py +1 -1
- layerbrain-0.3.0/tests/test_resources/test_webhooks.py +71 -0
- layerbrain-0.2.1/layerbrain/_version.py +0 -1
- layerbrain-0.2.1/layerbrain/cli/commands/brains.py +0 -38
- layerbrain-0.2.1/layerbrain/cli/commands/engrams.py +0 -154
- layerbrain-0.2.1/layerbrain/cli/commands/models.py +0 -41
- layerbrain-0.2.1/layerbrain/cli/commands/subscriptions.py +0 -153
- layerbrain-0.2.1/layerbrain/cli/commands/threed.py +0 -39
- layerbrain-0.2.1/layerbrain/cli/commands/tools.py +0 -54
- layerbrain-0.2.1/layerbrain/sdk/resources/auth.py +0 -68
- layerbrain-0.2.1/layerbrain/sdk/resources/brains.py +0 -13
- layerbrain-0.2.1/layerbrain/sdk/resources/engrams.py +0 -66
- layerbrain-0.2.1/layerbrain/sdk/resources/environments.py +0 -46
- layerbrain-0.2.1/layerbrain/sdk/resources/subscriptions.py +0 -55
- layerbrain-0.2.1/layerbrain/sdk/resources/threed.py +0 -13
- layerbrain-0.2.1/layerbrain/sdk/resources/tools.py +0 -17
- layerbrain-0.2.1/layerbrain/sdk/resources/videos.py +0 -17
- layerbrain-0.2.1/tests/test_resources/__init__.py +0 -0
- layerbrain-0.2.1/tests/test_resources/test_auth.py +0 -141
- {layerbrain-0.2.1 → layerbrain-0.3.0}/.github/workflows/publish.yml +0 -0
- {layerbrain-0.2.1 → layerbrain-0.3.0}/.gitignore +0 -0
- {layerbrain-0.2.1 → layerbrain-0.3.0}/LICENSE +0 -0
- {layerbrain-0.2.1/layerbrain/cli → layerbrain-0.3.0/layerbrain/cli/commands}/__init__.py +0 -0
- {layerbrain-0.2.1 → layerbrain-0.3.0}/layerbrain/cli/commands/config.py +1 -1
- {layerbrain-0.2.1 → layerbrain-0.3.0}/layerbrain/exceptions.py +0 -0
- {layerbrain-0.2.1 → layerbrain-0.3.0}/layerbrain/py.typed +0 -0
- {layerbrain-0.2.1 → layerbrain-0.3.0}/layerbrain/sdk/_exceptions.py +0 -0
- {layerbrain-0.2.1 → layerbrain-0.3.0}/layerbrain/sdk/_pagination.py +0 -0
- {layerbrain-0.2.1 → layerbrain-0.3.0}/layerbrain/sdk/_resource.py +0 -0
- {layerbrain-0.2.1 → layerbrain-0.3.0}/layerbrain/sdk/openapi/__init__.py +0 -0
- {layerbrain-0.2.1 → layerbrain-0.3.0}/layerbrain/sdk/resources/chat/__init__.py +0 -0
- {layerbrain-0.2.1 → layerbrain-0.3.0}/layerbrain/sdk/resources/chat/completions.py +0 -0
- {layerbrain-0.2.1/layerbrain/cli/commands → layerbrain-0.3.0/tests}/__init__.py +0 -0
- {layerbrain-0.2.1/tests → layerbrain-0.3.0/tests/test_cli}/__init__.py +0 -0
- {layerbrain-0.2.1 → layerbrain-0.3.0}/tests/test_config.py +0 -0
- {layerbrain-0.2.1 → layerbrain-0.3.0}/tests/test_pagination.py +0 -0
- {layerbrain-0.2.1/tests/test_cli → layerbrain-0.3.0/tests/test_resources}/__init__.py +0 -0
- {layerbrain-0.2.1 → layerbrain-0.3.0}/tests/test_resources/test_chat.py +0 -0
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: layerbrain
|
|
3
|
-
Version: 0.
|
|
4
|
-
Summary: The official Python SDK for the Layerbrain API
|
|
3
|
+
Version: 0.3.0
|
|
4
|
+
Summary: The official Python SDK and CLI for the Layerbrain API
|
|
5
5
|
Project-URL: Homepage, https://layerbrain.com
|
|
6
6
|
Project-URL: Documentation, https://docs.layerbrain.com
|
|
7
|
-
Project-URL: Repository, https://github.com/layerbrain/layerbrain
|
|
8
|
-
Project-URL: Changelog, https://github.com/layerbrain/layerbrain
|
|
7
|
+
Project-URL: Repository, https://github.com/layerbrain/layerbrain
|
|
8
|
+
Project-URL: Changelog, https://github.com/layerbrain/layerbrain/releases
|
|
9
9
|
Author-email: Layerbrain <engineering@layerbrain.com>
|
|
10
10
|
License-Expression: MIT
|
|
11
11
|
License-File: LICENSE
|
|
@@ -21,32 +21,50 @@ Classifier: Typing :: Typed
|
|
|
21
21
|
Requires-Python: >=3.10
|
|
22
22
|
Requires-Dist: httpx>=0.27
|
|
23
23
|
Requires-Dist: pydantic>=2.0
|
|
24
|
+
Requires-Dist: rich>=13.0
|
|
24
25
|
Requires-Dist: tomli-w>=1.0
|
|
25
26
|
Requires-Dist: tomli>=2.0; python_version < '3.11'
|
|
27
|
+
Requires-Dist: typer>=0.15
|
|
26
28
|
Requires-Dist: websockets>=12.0
|
|
27
|
-
Provides-Extra: cli
|
|
28
|
-
Requires-Dist: rich>=13.0; extra == 'cli'
|
|
29
|
-
Requires-Dist: typer>=0.15; extra == 'cli'
|
|
30
29
|
Description-Content-Type: text/markdown
|
|
31
30
|
|
|
32
|
-
# Layerbrain
|
|
31
|
+
# Layerbrain CLI
|
|
33
32
|
|
|
34
33
|
[](https://pypi.org/project/layerbrain/)
|
|
35
34
|
[](https://www.python.org/downloads/)
|
|
36
|
-
[](https://github.com/layerbrain/layerbrain
|
|
35
|
+
[](https://github.com/layerbrain/layerbrain/blob/main/LICENSE)
|
|
37
36
|
|
|
38
37
|
The official Python SDK and CLI for the [Layerbrain](https://layerbrain.com) API.
|
|
39
38
|
|
|
40
39
|
## Installation
|
|
41
40
|
|
|
42
41
|
```sh
|
|
43
|
-
# SDK only
|
|
44
42
|
pip install layerbrain
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
For a global CLI install with `uv`:
|
|
46
|
+
|
|
47
|
+
```sh
|
|
48
|
+
uv tool install layerbrain
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
For a `uv`-managed project dependency:
|
|
52
|
+
|
|
53
|
+
```sh
|
|
54
|
+
uv add layerbrain
|
|
55
|
+
```
|
|
45
56
|
|
|
46
|
-
|
|
47
|
-
|
|
57
|
+
For a one-off run without installing the tool globally:
|
|
58
|
+
|
|
59
|
+
```sh
|
|
60
|
+
uvx layerbrain --help
|
|
48
61
|
```
|
|
49
62
|
|
|
63
|
+
All of these give you the same package. `layerbrain` includes both:
|
|
64
|
+
|
|
65
|
+
- the Python SDK via `from layerbrain import Layerbrain`
|
|
66
|
+
- the `layerbrain` command-line interface
|
|
67
|
+
|
|
50
68
|
## Quick Start
|
|
51
69
|
|
|
52
70
|
```python
|
|
@@ -55,6 +73,42 @@ from layerbrain import Layerbrain
|
|
|
55
73
|
client = Layerbrain()
|
|
56
74
|
```
|
|
57
75
|
|
|
76
|
+
## CLI
|
|
77
|
+
|
|
78
|
+
```sh
|
|
79
|
+
layerbrain login
|
|
80
|
+
layerbrain whoami
|
|
81
|
+
layerbrain models list
|
|
82
|
+
layerbrain machines list
|
|
83
|
+
layerbrain listen --events machine.created
|
|
84
|
+
layerbrain webhooks list
|
|
85
|
+
layerbrain networks list
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
You can also run the CLI via:
|
|
89
|
+
|
|
90
|
+
```sh
|
|
91
|
+
python -m layerbrain
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
To update a standalone install:
|
|
95
|
+
|
|
96
|
+
```sh
|
|
97
|
+
layerbrain upgrade
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
Or, if you installed it with `uv tool`:
|
|
101
|
+
|
|
102
|
+
```sh
|
|
103
|
+
uv tool upgrade layerbrain
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
If `layerbrain` is a dependency in a `uv` project:
|
|
107
|
+
|
|
108
|
+
```sh
|
|
109
|
+
uv add --upgrade-package layerbrain layerbrain
|
|
110
|
+
```
|
|
111
|
+
|
|
58
112
|
The client reads your API key from the `LAYERBRAIN_API_KEY` environment variable by default. You can also pass it explicitly:
|
|
59
113
|
|
|
60
114
|
```python
|
|
@@ -103,9 +157,9 @@ machine = client.machines.retrieve("mach_abc123")
|
|
|
103
157
|
client.machines.delete("mach_abc123")
|
|
104
158
|
```
|
|
105
159
|
|
|
106
|
-
### Machine
|
|
160
|
+
### Connect to a Machine (WebSocket)
|
|
107
161
|
|
|
108
|
-
|
|
162
|
+
Open a live machine session over WebSocket for shell and filesystem access:
|
|
109
163
|
|
|
110
164
|
```python
|
|
111
165
|
import asyncio
|
|
@@ -114,7 +168,7 @@ from layerbrain import Layerbrain
|
|
|
114
168
|
async def main():
|
|
115
169
|
client = Layerbrain()
|
|
116
170
|
|
|
117
|
-
#
|
|
171
|
+
# Open a live machine session over WebSocket
|
|
118
172
|
async with await client.machines.connect("mach_abc123") as conn:
|
|
119
173
|
# Shell - execute commands
|
|
120
174
|
result = await conn.shell.execute("ls -la ~/brain")
|
|
@@ -180,13 +234,9 @@ model = client.models.retrieve("meta-llama/llama-3.1-8b")
|
|
|
180
234
|
|
|
181
235
|
```python
|
|
182
236
|
# Web search
|
|
183
|
-
results = client.tools.
|
|
237
|
+
results = client.tools.web_search(query="python httpx tutorial", count=5)
|
|
184
238
|
for r in results["results"]:
|
|
185
239
|
print(r["title"], r["url"])
|
|
186
|
-
|
|
187
|
-
# Fetch page content
|
|
188
|
-
page = client.tools.fetch(url="https://example.com")
|
|
189
|
-
print(page["content"])
|
|
190
240
|
```
|
|
191
241
|
|
|
192
242
|
### Secrets
|
|
@@ -258,59 +308,6 @@ except APIError as e:
|
|
|
258
308
|
| N/A | `ConnectionError` |
|
|
259
309
|
| N/A | `TimeoutError` |
|
|
260
310
|
|
|
261
|
-
## CLI
|
|
262
|
-
|
|
263
|
-
Requires `pip install layerbrain[cli]`.
|
|
264
|
-
|
|
265
|
-
```sh
|
|
266
|
-
# Account
|
|
267
|
-
layerbrain login
|
|
268
|
-
layerbrain whoami
|
|
269
|
-
layerbrain logout
|
|
270
|
-
layerbrain upgrade
|
|
271
|
-
|
|
272
|
-
# AI
|
|
273
|
-
layerbrain chat completions create --model meta-llama/llama-3.1-8b --message "Hello"
|
|
274
|
-
layerbrain models list
|
|
275
|
-
layerbrain models get meta-llama/llama-3.1-8b
|
|
276
|
-
layerbrain embeddings create --model BAAI/bge-large-en-v1.5 --input "Hello world"
|
|
277
|
-
layerbrain images generate --model black-forest-labs/flux-schnell --prompt "A cat"
|
|
278
|
-
layerbrain audio speech --model hexgrad/kokoro --input "Hello world"
|
|
279
|
-
|
|
280
|
-
# Tools
|
|
281
|
-
layerbrain tools search --query "python httpx"
|
|
282
|
-
layerbrain tools fetch --url "https://example.com"
|
|
283
|
-
|
|
284
|
-
# Brain
|
|
285
|
-
layerbrain brains create
|
|
286
|
-
layerbrain engrams list
|
|
287
|
-
layerbrain engrams create --name "my-session"
|
|
288
|
-
|
|
289
|
-
# Compute & Machines
|
|
290
|
-
layerbrain compute list
|
|
291
|
-
layerbrain machines list
|
|
292
|
-
layerbrain machines create --compute na-us-ca-sfo_s.small --duration 60
|
|
293
|
-
layerbrain machines get mach_abc123
|
|
294
|
-
layerbrain machines delete mach_abc123
|
|
295
|
-
|
|
296
|
-
# SSH into a machine (interactive session)
|
|
297
|
-
layerbrain machines ssh --id mach_abc123
|
|
298
|
-
layerbrain machines ssh --id mach_abc123 --user root
|
|
299
|
-
|
|
300
|
-
# Infrastructure
|
|
301
|
-
layerbrain environments list
|
|
302
|
-
layerbrain secrets list
|
|
303
|
-
layerbrain secrets create --name HF_TOKEN --value "hf_..."
|
|
304
|
-
layerbrain organizations list
|
|
305
|
-
layerbrain api-keys list
|
|
306
|
-
layerbrain api-keys create --name "production"
|
|
307
|
-
|
|
308
|
-
# Config
|
|
309
|
-
layerbrain config set default_output json
|
|
310
|
-
```
|
|
311
|
-
|
|
312
|
-
All list commands support `--output json` for JSON output.
|
|
313
|
-
|
|
314
311
|
## Configuration
|
|
315
312
|
|
|
316
313
|
The client reads configuration from (in priority order):
|
|
@@ -1,21 +1,40 @@
|
|
|
1
|
-
# Layerbrain
|
|
1
|
+
# Layerbrain CLI
|
|
2
2
|
|
|
3
3
|
[](https://pypi.org/project/layerbrain/)
|
|
4
4
|
[](https://www.python.org/downloads/)
|
|
5
|
-
[](https://github.com/layerbrain/layerbrain
|
|
5
|
+
[](https://github.com/layerbrain/layerbrain/blob/main/LICENSE)
|
|
6
6
|
|
|
7
7
|
The official Python SDK and CLI for the [Layerbrain](https://layerbrain.com) API.
|
|
8
8
|
|
|
9
9
|
## Installation
|
|
10
10
|
|
|
11
11
|
```sh
|
|
12
|
-
# SDK only
|
|
13
12
|
pip install layerbrain
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
For a global CLI install with `uv`:
|
|
16
|
+
|
|
17
|
+
```sh
|
|
18
|
+
uv tool install layerbrain
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
For a `uv`-managed project dependency:
|
|
22
|
+
|
|
23
|
+
```sh
|
|
24
|
+
uv add layerbrain
|
|
25
|
+
```
|
|
14
26
|
|
|
15
|
-
|
|
16
|
-
|
|
27
|
+
For a one-off run without installing the tool globally:
|
|
28
|
+
|
|
29
|
+
```sh
|
|
30
|
+
uvx layerbrain --help
|
|
17
31
|
```
|
|
18
32
|
|
|
33
|
+
All of these give you the same package. `layerbrain` includes both:
|
|
34
|
+
|
|
35
|
+
- the Python SDK via `from layerbrain import Layerbrain`
|
|
36
|
+
- the `layerbrain` command-line interface
|
|
37
|
+
|
|
19
38
|
## Quick Start
|
|
20
39
|
|
|
21
40
|
```python
|
|
@@ -24,6 +43,42 @@ from layerbrain import Layerbrain
|
|
|
24
43
|
client = Layerbrain()
|
|
25
44
|
```
|
|
26
45
|
|
|
46
|
+
## CLI
|
|
47
|
+
|
|
48
|
+
```sh
|
|
49
|
+
layerbrain login
|
|
50
|
+
layerbrain whoami
|
|
51
|
+
layerbrain models list
|
|
52
|
+
layerbrain machines list
|
|
53
|
+
layerbrain listen --events machine.created
|
|
54
|
+
layerbrain webhooks list
|
|
55
|
+
layerbrain networks list
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
You can also run the CLI via:
|
|
59
|
+
|
|
60
|
+
```sh
|
|
61
|
+
python -m layerbrain
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
To update a standalone install:
|
|
65
|
+
|
|
66
|
+
```sh
|
|
67
|
+
layerbrain upgrade
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Or, if you installed it with `uv tool`:
|
|
71
|
+
|
|
72
|
+
```sh
|
|
73
|
+
uv tool upgrade layerbrain
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
If `layerbrain` is a dependency in a `uv` project:
|
|
77
|
+
|
|
78
|
+
```sh
|
|
79
|
+
uv add --upgrade-package layerbrain layerbrain
|
|
80
|
+
```
|
|
81
|
+
|
|
27
82
|
The client reads your API key from the `LAYERBRAIN_API_KEY` environment variable by default. You can also pass it explicitly:
|
|
28
83
|
|
|
29
84
|
```python
|
|
@@ -72,9 +127,9 @@ machine = client.machines.retrieve("mach_abc123")
|
|
|
72
127
|
client.machines.delete("mach_abc123")
|
|
73
128
|
```
|
|
74
129
|
|
|
75
|
-
### Machine
|
|
130
|
+
### Connect to a Machine (WebSocket)
|
|
76
131
|
|
|
77
|
-
|
|
132
|
+
Open a live machine session over WebSocket for shell and filesystem access:
|
|
78
133
|
|
|
79
134
|
```python
|
|
80
135
|
import asyncio
|
|
@@ -83,7 +138,7 @@ from layerbrain import Layerbrain
|
|
|
83
138
|
async def main():
|
|
84
139
|
client = Layerbrain()
|
|
85
140
|
|
|
86
|
-
#
|
|
141
|
+
# Open a live machine session over WebSocket
|
|
87
142
|
async with await client.machines.connect("mach_abc123") as conn:
|
|
88
143
|
# Shell - execute commands
|
|
89
144
|
result = await conn.shell.execute("ls -la ~/brain")
|
|
@@ -149,13 +204,9 @@ model = client.models.retrieve("meta-llama/llama-3.1-8b")
|
|
|
149
204
|
|
|
150
205
|
```python
|
|
151
206
|
# Web search
|
|
152
|
-
results = client.tools.
|
|
207
|
+
results = client.tools.web_search(query="python httpx tutorial", count=5)
|
|
153
208
|
for r in results["results"]:
|
|
154
209
|
print(r["title"], r["url"])
|
|
155
|
-
|
|
156
|
-
# Fetch page content
|
|
157
|
-
page = client.tools.fetch(url="https://example.com")
|
|
158
|
-
print(page["content"])
|
|
159
210
|
```
|
|
160
211
|
|
|
161
212
|
### Secrets
|
|
@@ -227,59 +278,6 @@ except APIError as e:
|
|
|
227
278
|
| N/A | `ConnectionError` |
|
|
228
279
|
| N/A | `TimeoutError` |
|
|
229
280
|
|
|
230
|
-
## CLI
|
|
231
|
-
|
|
232
|
-
Requires `pip install layerbrain[cli]`.
|
|
233
|
-
|
|
234
|
-
```sh
|
|
235
|
-
# Account
|
|
236
|
-
layerbrain login
|
|
237
|
-
layerbrain whoami
|
|
238
|
-
layerbrain logout
|
|
239
|
-
layerbrain upgrade
|
|
240
|
-
|
|
241
|
-
# AI
|
|
242
|
-
layerbrain chat completions create --model meta-llama/llama-3.1-8b --message "Hello"
|
|
243
|
-
layerbrain models list
|
|
244
|
-
layerbrain models get meta-llama/llama-3.1-8b
|
|
245
|
-
layerbrain embeddings create --model BAAI/bge-large-en-v1.5 --input "Hello world"
|
|
246
|
-
layerbrain images generate --model black-forest-labs/flux-schnell --prompt "A cat"
|
|
247
|
-
layerbrain audio speech --model hexgrad/kokoro --input "Hello world"
|
|
248
|
-
|
|
249
|
-
# Tools
|
|
250
|
-
layerbrain tools search --query "python httpx"
|
|
251
|
-
layerbrain tools fetch --url "https://example.com"
|
|
252
|
-
|
|
253
|
-
# Brain
|
|
254
|
-
layerbrain brains create
|
|
255
|
-
layerbrain engrams list
|
|
256
|
-
layerbrain engrams create --name "my-session"
|
|
257
|
-
|
|
258
|
-
# Compute & Machines
|
|
259
|
-
layerbrain compute list
|
|
260
|
-
layerbrain machines list
|
|
261
|
-
layerbrain machines create --compute na-us-ca-sfo_s.small --duration 60
|
|
262
|
-
layerbrain machines get mach_abc123
|
|
263
|
-
layerbrain machines delete mach_abc123
|
|
264
|
-
|
|
265
|
-
# SSH into a machine (interactive session)
|
|
266
|
-
layerbrain machines ssh --id mach_abc123
|
|
267
|
-
layerbrain machines ssh --id mach_abc123 --user root
|
|
268
|
-
|
|
269
|
-
# Infrastructure
|
|
270
|
-
layerbrain environments list
|
|
271
|
-
layerbrain secrets list
|
|
272
|
-
layerbrain secrets create --name HF_TOKEN --value "hf_..."
|
|
273
|
-
layerbrain organizations list
|
|
274
|
-
layerbrain api-keys list
|
|
275
|
-
layerbrain api-keys create --name "production"
|
|
276
|
-
|
|
277
|
-
# Config
|
|
278
|
-
layerbrain config set default_output json
|
|
279
|
-
```
|
|
280
|
-
|
|
281
|
-
All list commands support `--output json` for JSON output.
|
|
282
|
-
|
|
283
281
|
## Configuration
|
|
284
282
|
|
|
285
283
|
The client reads configuration from (in priority order):
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.3.0"
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Layerbrain CLI package."""
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
"""Private CLI-only auth client.
|
|
2
|
+
|
|
3
|
+
This keeps hidden auth endpoints out of the public SDK surface while still
|
|
4
|
+
letting the CLI perform device login and logout flows.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from typing import Any
|
|
10
|
+
|
|
11
|
+
import httpx
|
|
12
|
+
|
|
13
|
+
from layerbrain.sdk._client import _default_user_agent
|
|
14
|
+
from layerbrain.sdk._config import Config
|
|
15
|
+
from layerbrain.sdk._exceptions import raise_for_status
|
|
16
|
+
from layerbrain.sdk._types import AuthToken, DeviceAuthorization
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class CLIAuthClient:
|
|
20
|
+
"""Private HTTP client for CLI authentication flows."""
|
|
21
|
+
|
|
22
|
+
def __init__(
|
|
23
|
+
self,
|
|
24
|
+
api_key: str | None = None,
|
|
25
|
+
*,
|
|
26
|
+
base_url: str | None = None,
|
|
27
|
+
timeout: float = 30.0,
|
|
28
|
+
) -> None:
|
|
29
|
+
config = Config()
|
|
30
|
+
self._api_key = api_key or config.api_key
|
|
31
|
+
raw_url = (base_url or config.base_url).rstrip("/")
|
|
32
|
+
if raw_url.endswith("/v1"):
|
|
33
|
+
raw_url = raw_url[:-3]
|
|
34
|
+
self._base_url = raw_url
|
|
35
|
+
self._http = httpx.Client(
|
|
36
|
+
headers=self._build_headers(),
|
|
37
|
+
follow_redirects=True,
|
|
38
|
+
timeout=httpx.Timeout(timeout, connect=10.0),
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
def _build_headers(self) -> dict[str, str]:
|
|
42
|
+
headers = {
|
|
43
|
+
"Content-Type": "application/json",
|
|
44
|
+
"User-Agent": _default_user_agent(),
|
|
45
|
+
}
|
|
46
|
+
if self._api_key:
|
|
47
|
+
headers["Authorization"] = f"Bearer {self._api_key}"
|
|
48
|
+
return headers
|
|
49
|
+
|
|
50
|
+
def _full_url(self, path: str) -> str:
|
|
51
|
+
if not path.startswith("/"):
|
|
52
|
+
path = f"/{path}"
|
|
53
|
+
return f"{self._base_url}/v1/auth{path}"
|
|
54
|
+
|
|
55
|
+
def _post(self, path: str, *, json: dict[str, Any] | None = None) -> dict[str, Any]:
|
|
56
|
+
response = self._http.post(self._full_url(path), json=json)
|
|
57
|
+
body = response.json()
|
|
58
|
+
raise_for_status(response.status_code, body)
|
|
59
|
+
return body
|
|
60
|
+
|
|
61
|
+
def device(self, *, client_id: str) -> DeviceAuthorization:
|
|
62
|
+
"""Start the CLI device authorization flow."""
|
|
63
|
+
data = self._post("/device", json={"client_id": client_id})
|
|
64
|
+
return DeviceAuthorization(**data)
|
|
65
|
+
|
|
66
|
+
def token_exchange(self, **kwargs: Any) -> AuthToken | dict[str, Any]:
|
|
67
|
+
"""Exchange a device code grant for an auth token."""
|
|
68
|
+
data = self._post("/token", json=kwargs)
|
|
69
|
+
if data.get("object") == "token":
|
|
70
|
+
return AuthToken(**data)
|
|
71
|
+
return data
|
|
72
|
+
|
|
73
|
+
def logout(self) -> dict[str, Any]:
|
|
74
|
+
"""Invalidate the current session if possible."""
|
|
75
|
+
return self._post("/logout", json={})
|
|
76
|
+
|
|
77
|
+
def close(self) -> None:
|
|
78
|
+
self._http.close()
|
|
79
|
+
|
|
80
|
+
def __enter__(self) -> CLIAuthClient:
|
|
81
|
+
return self
|
|
82
|
+
|
|
83
|
+
def __exit__(self, *args: Any) -> None:
|
|
84
|
+
self.close()
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"""Command input helpers for JSON request bodies."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from typing import Any
|
|
8
|
+
|
|
9
|
+
import typer
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def load_json_input(data: str | None, data_file: str | None) -> dict[str, Any]:
|
|
13
|
+
"""Load a JSON object from inline data or a file path."""
|
|
14
|
+
if data and data_file:
|
|
15
|
+
raise typer.BadParameter("Use either --data or --data-file, not both.")
|
|
16
|
+
|
|
17
|
+
if data_file:
|
|
18
|
+
raw = Path(data_file).read_text()
|
|
19
|
+
elif data:
|
|
20
|
+
raw = data
|
|
21
|
+
else:
|
|
22
|
+
return {}
|
|
23
|
+
|
|
24
|
+
try:
|
|
25
|
+
payload = json.loads(raw)
|
|
26
|
+
except json.JSONDecodeError as exc:
|
|
27
|
+
raise typer.BadParameter(f"Invalid JSON: {exc}") from exc
|
|
28
|
+
|
|
29
|
+
if not isinstance(payload, dict):
|
|
30
|
+
raise typer.BadParameter("JSON input must be an object.")
|
|
31
|
+
|
|
32
|
+
return payload
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
import json
|
|
6
|
-
from typing import Any
|
|
6
|
+
from typing import Any
|
|
7
7
|
|
|
8
8
|
from rich.console import Console
|
|
9
9
|
from rich.table import Table
|
|
@@ -41,7 +41,7 @@ def print_success(message: str) -> None:
|
|
|
41
41
|
|
|
42
42
|
def build_table(
|
|
43
43
|
title: str,
|
|
44
|
-
columns:
|
|
44
|
+
columns: list[tuple[str, str]],
|
|
45
45
|
*,
|
|
46
46
|
show_lines: bool = True,
|
|
47
47
|
) -> Table:
|
|
@@ -86,7 +86,7 @@ API_KEY_STATUS_COLORS = {
|
|
|
86
86
|
}
|
|
87
87
|
|
|
88
88
|
|
|
89
|
-
def status_text(status: str, color_map:
|
|
89
|
+
def status_text(status: str, color_map: dict[str, str]) -> Text:
|
|
90
90
|
"""Create a colored Text object for a status string."""
|
|
91
91
|
color = color_map.get(status, "white")
|
|
92
92
|
return Text(status, style=color)
|
|
@@ -6,12 +6,10 @@ on next regeneration.
|
|
|
6
6
|
|
|
7
7
|
from __future__ import annotations
|
|
8
8
|
|
|
9
|
-
from typing import Optional
|
|
10
|
-
|
|
11
9
|
import typer
|
|
12
10
|
|
|
13
11
|
from layerbrain import Layerbrain
|
|
14
|
-
from layerbrain.
|
|
12
|
+
from layerbrain.cli._input import load_json_input
|
|
15
13
|
from layerbrain.cli._output import (
|
|
16
14
|
build_table,
|
|
17
15
|
console,
|
|
@@ -20,6 +18,7 @@ from layerbrain.cli._output import (
|
|
|
20
18
|
print_success,
|
|
21
19
|
validate_output_format,
|
|
22
20
|
)
|
|
21
|
+
from layerbrain.exceptions import LayerbrainError
|
|
23
22
|
|
|
24
23
|
app = typer.Typer(help="Accounts", no_args_is_help=True)
|
|
25
24
|
|
|
@@ -47,20 +46,6 @@ def list_accounts(
|
|
|
47
46
|
console.print(table)
|
|
48
47
|
|
|
49
48
|
|
|
50
|
-
@app.command()
|
|
51
|
-
def create() -> None:
|
|
52
|
-
"""Create endpoint is not allowed for accounts."""
|
|
53
|
-
client = Layerbrain()
|
|
54
|
-
try:
|
|
55
|
-
result = client.accounts.create()
|
|
56
|
-
except LayerbrainError as e:
|
|
57
|
-
print_error(str(e))
|
|
58
|
-
raise typer.Exit(1) from e
|
|
59
|
-
|
|
60
|
-
print_success(f"Accounts created.")
|
|
61
|
-
print_json(result)
|
|
62
|
-
|
|
63
|
-
|
|
64
49
|
@app.command()
|
|
65
50
|
def delete(
|
|
66
51
|
id: str = typer.Argument(..., help="Accounts ID"),
|
|
@@ -97,71 +82,13 @@ def get_accounts(
|
|
|
97
82
|
@app.command("update")
|
|
98
83
|
def update(
|
|
99
84
|
id: str = typer.Argument(..., help="Accounts ID"),
|
|
85
|
+
data: str | None = typer.Option(None, "--data", help="Inline JSON request body."),
|
|
86
|
+
data_file: str | None = typer.Option(None, "--data-file", help="Path to a JSON request body."),
|
|
100
87
|
) -> None:
|
|
101
88
|
"""Handles PATCH requests to update account info."""
|
|
102
89
|
client = Layerbrain()
|
|
103
90
|
try:
|
|
104
|
-
result = client.accounts.update(id)
|
|
105
|
-
except LayerbrainError as e:
|
|
106
|
-
print_error(str(e))
|
|
107
|
-
raise typer.Exit(1) from e
|
|
108
|
-
|
|
109
|
-
print_json(result)
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
@app.command("clear-data")
|
|
113
|
-
def clear_data(
|
|
114
|
-
id: str = typer.Argument(..., help="Accounts ID"),
|
|
115
|
-
) -> None:
|
|
116
|
-
"""Clear all cloud storage data for the user's organization."""
|
|
117
|
-
client = Layerbrain()
|
|
118
|
-
try:
|
|
119
|
-
result = client.accounts.clear_data(id)
|
|
120
|
-
except LayerbrainError as e:
|
|
121
|
-
print_error(str(e))
|
|
122
|
-
raise typer.Exit(1) from e
|
|
123
|
-
|
|
124
|
-
print_json(result)
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
@app.command("export")
|
|
128
|
-
def export(
|
|
129
|
-
id: str = typer.Argument(..., help="Accounts ID"),
|
|
130
|
-
) -> None:
|
|
131
|
-
"""Generate presigned download URL for latest snapshot."""
|
|
132
|
-
client = Layerbrain()
|
|
133
|
-
try:
|
|
134
|
-
result = client.accounts.export(id)
|
|
135
|
-
except LayerbrainError as e:
|
|
136
|
-
print_error(str(e))
|
|
137
|
-
raise typer.Exit(1) from e
|
|
138
|
-
|
|
139
|
-
print_json(result)
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
@app.command("onboard")
|
|
143
|
-
def onboard(
|
|
144
|
-
id: str = typer.Argument(..., help="Accounts ID"),
|
|
145
|
-
) -> None:
|
|
146
|
-
"""Handle onboarding of a new account with activation code."""
|
|
147
|
-
client = Layerbrain()
|
|
148
|
-
try:
|
|
149
|
-
result = client.accounts.onboard(id)
|
|
150
|
-
except LayerbrainError as e:
|
|
151
|
-
print_error(str(e))
|
|
152
|
-
raise typer.Exit(1) from e
|
|
153
|
-
|
|
154
|
-
print_json(result)
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
@app.command("switch")
|
|
158
|
-
def switch(
|
|
159
|
-
id: str = typer.Argument(..., help="Accounts ID"),
|
|
160
|
-
) -> None:
|
|
161
|
-
"""Switch organization/membership endpoint - returns new token with specified or latest membership."""
|
|
162
|
-
client = Layerbrain()
|
|
163
|
-
try:
|
|
164
|
-
result = client.accounts.switch(id)
|
|
91
|
+
result = client.accounts.update(id, **load_json_input(data, data_file))
|
|
165
92
|
except LayerbrainError as e:
|
|
166
93
|
print_error(str(e))
|
|
167
94
|
raise typer.Exit(1) from e
|