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.
Files changed (121) hide show
  1. {layerbrain-0.2.1 → layerbrain-0.3.0}/.github/workflows/ci.yml +1 -1
  2. {layerbrain-0.2.1 → layerbrain-0.3.0}/PKG-INFO +70 -73
  3. {layerbrain-0.2.1 → layerbrain-0.3.0}/README.md +64 -66
  4. {layerbrain-0.2.1 → layerbrain-0.3.0}/layerbrain/__init__.py +2 -1
  5. {layerbrain-0.2.1 → layerbrain-0.3.0}/layerbrain/__main__.py +1 -1
  6. layerbrain-0.3.0/layerbrain/_version.py +1 -0
  7. layerbrain-0.3.0/layerbrain/cli/__init__.py +1 -0
  8. layerbrain-0.3.0/layerbrain/cli/__main__.py +3 -0
  9. layerbrain-0.3.0/layerbrain/cli/_auth_client.py +84 -0
  10. layerbrain-0.3.0/layerbrain/cli/_input.py +32 -0
  11. {layerbrain-0.2.1 → layerbrain-0.3.0}/layerbrain/cli/_output.py +3 -3
  12. {layerbrain-0.2.1 → layerbrain-0.3.0}/layerbrain/cli/commands/accounts.py +5 -78
  13. {layerbrain-0.2.1 → layerbrain-0.3.0}/layerbrain/cli/commands/api_keys.py +14 -9
  14. {layerbrain-0.2.1 → layerbrain-0.3.0}/layerbrain/cli/commands/audio.py +8 -11
  15. {layerbrain-0.2.1 → layerbrain-0.3.0}/layerbrain/cli/commands/auth.py +12 -9
  16. layerbrain-0.3.0/layerbrain/cli/commands/brains.py +77 -0
  17. {layerbrain-0.2.1 → layerbrain-0.3.0}/layerbrain/cli/commands/chat.py +2 -4
  18. {layerbrain-0.2.1 → layerbrain-0.3.0}/layerbrain/cli/commands/compute.py +1 -4
  19. {layerbrain-0.2.1 → layerbrain-0.3.0}/layerbrain/cli/commands/embeddings.py +8 -9
  20. {layerbrain-0.2.1 → layerbrain-0.3.0}/layerbrain/cli/commands/images.py +8 -11
  21. {layerbrain-0.2.1 → layerbrain-0.3.0}/layerbrain/cli/commands/internal.py +5 -7
  22. layerbrain-0.3.0/layerbrain/cli/commands/listen.py +288 -0
  23. {layerbrain-0.2.1 → layerbrain-0.3.0}/layerbrain/cli/commands/machines.py +42 -19
  24. {layerbrain-0.2.1 → layerbrain-0.3.0}/layerbrain/cli/commands/memberships.py +8 -36
  25. layerbrain-0.3.0/layerbrain/cli/commands/models.py +55 -0
  26. layerbrain-0.3.0/layerbrain/cli/commands/network_flows.py +54 -0
  27. layerbrain-0.3.0/layerbrain/cli/commands/network_rules.py +104 -0
  28. layerbrain-0.3.0/layerbrain/cli/commands/networks.py +71 -0
  29. {layerbrain-0.2.1 → layerbrain-0.3.0}/layerbrain/cli/commands/organizations.py +11 -7
  30. {layerbrain-0.2.1 → layerbrain-0.3.0}/layerbrain/cli/commands/secrets.py +11 -22
  31. layerbrain-0.3.0/layerbrain/cli/commands/snapshots.py +103 -0
  32. {layerbrain-0.2.1 → layerbrain-0.3.0}/layerbrain/cli/commands/statements.py +1 -4
  33. layerbrain-0.3.0/layerbrain/cli/commands/storage.py +267 -0
  34. layerbrain-0.2.1/layerbrain/cli/commands/environments.py → layerbrain-0.3.0/layerbrain/cli/commands/subscriptions.py +19 -17
  35. layerbrain-0.3.0/layerbrain/cli/commands/threed.py +43 -0
  36. layerbrain-0.3.0/layerbrain/cli/commands/tools.py +35 -0
  37. {layerbrain-0.2.1 → layerbrain-0.3.0}/layerbrain/cli/commands/upgrade.py +7 -9
  38. {layerbrain-0.2.1 → layerbrain-0.3.0}/layerbrain/cli/commands/videos.py +8 -12
  39. layerbrain-0.3.0/layerbrain/cli/commands/webhooks.py +119 -0
  40. layerbrain-0.2.1/layerbrain/cli/app.py → layerbrain-0.3.0/layerbrain/cli/main.py +15 -5
  41. {layerbrain-0.2.1 → layerbrain-0.3.0}/layerbrain/sdk/__init__.py +14 -9
  42. {layerbrain-0.2.1 → layerbrain-0.3.0}/layerbrain/sdk/_client.py +1 -1
  43. {layerbrain-0.2.1 → layerbrain-0.3.0}/layerbrain/sdk/_config.py +1 -1
  44. {layerbrain-0.2.1 → layerbrain-0.3.0}/layerbrain/sdk/_connection.py +3 -3
  45. {layerbrain-0.2.1 → layerbrain-0.3.0}/layerbrain/sdk/_transport.py +3 -3
  46. {layerbrain-0.2.1 → layerbrain-0.3.0}/layerbrain/sdk/_types.py +76 -117
  47. {layerbrain-0.2.1 → layerbrain-0.3.0}/layerbrain/sdk/openapi/openapi.json +3717 -2208
  48. {layerbrain-0.2.1 → layerbrain-0.3.0}/layerbrain/sdk/openapi/pull.py +19 -10
  49. {layerbrain-0.2.1 → layerbrain-0.3.0}/layerbrain/sdk/resources/__init__.py +12 -6
  50. {layerbrain-0.2.1 → layerbrain-0.3.0}/layerbrain/sdk/resources/accounts.py +4 -29
  51. {layerbrain-0.2.1 → layerbrain-0.3.0}/layerbrain/sdk/resources/api_keys.py +18 -23
  52. {layerbrain-0.2.1 → layerbrain-0.3.0}/layerbrain/sdk/resources/audio.py +4 -4
  53. layerbrain-0.3.0/layerbrain/sdk/resources/brains.py +25 -0
  54. {layerbrain-0.2.1 → layerbrain-0.3.0}/layerbrain/sdk/resources/compute.py +6 -11
  55. {layerbrain-0.2.1 → layerbrain-0.3.0}/layerbrain/sdk/resources/embeddings.py +1 -1
  56. {layerbrain-0.2.1 → layerbrain-0.3.0}/layerbrain/sdk/resources/images.py +4 -4
  57. {layerbrain-0.2.1 → layerbrain-0.3.0}/layerbrain/sdk/resources/machines.py +26 -27
  58. {layerbrain-0.2.1 → layerbrain-0.3.0}/layerbrain/sdk/resources/memberships.py +4 -17
  59. {layerbrain-0.2.1 → layerbrain-0.3.0}/layerbrain/sdk/resources/models.py +3 -3
  60. layerbrain-0.3.0/layerbrain/sdk/resources/network_flows.py +37 -0
  61. layerbrain-0.3.0/layerbrain/sdk/resources/network_rules.py +49 -0
  62. layerbrain-0.3.0/layerbrain/sdk/resources/networks.py +41 -0
  63. {layerbrain-0.2.1 → layerbrain-0.3.0}/layerbrain/sdk/resources/organizations.py +4 -9
  64. {layerbrain-0.2.1 → layerbrain-0.3.0}/layerbrain/sdk/resources/secrets.py +11 -9
  65. layerbrain-0.3.0/layerbrain/sdk/resources/snapshots.py +49 -0
  66. {layerbrain-0.2.1 → layerbrain-0.3.0}/layerbrain/sdk/resources/statements.py +10 -4
  67. layerbrain-0.3.0/layerbrain/sdk/resources/storage.py +128 -0
  68. layerbrain-0.3.0/layerbrain/sdk/resources/subscriptions.py +41 -0
  69. layerbrain-0.3.0/layerbrain/sdk/resources/threed.py +17 -0
  70. layerbrain-0.3.0/layerbrain/sdk/resources/tools.py +13 -0
  71. layerbrain-0.3.0/layerbrain/sdk/resources/videos.py +17 -0
  72. layerbrain-0.3.0/layerbrain/sdk/resources/webhooks.py +77 -0
  73. layerbrain-0.3.0/openapi/openapi.json +9071 -0
  74. {layerbrain-0.2.1 → layerbrain-0.3.0}/pyproject.toml +6 -10
  75. {layerbrain-0.2.1 → layerbrain-0.3.0}/scripts/generate.py +175 -23
  76. {layerbrain-0.2.1 → layerbrain-0.3.0}/setup.cfg +0 -1
  77. {layerbrain-0.2.1 → layerbrain-0.3.0}/tests/test_cli/test_auth_commands.py +3 -4
  78. layerbrain-0.3.0/tests/test_cli/test_listen_command.py +104 -0
  79. {layerbrain-0.2.1 → layerbrain-0.3.0}/tests/test_cli/test_machines_commands.py +17 -4
  80. layerbrain-0.3.0/tests/test_cli/test_main_help.py +43 -0
  81. {layerbrain-0.2.1 → layerbrain-0.3.0}/tests/test_client.py +1 -1
  82. {layerbrain-0.2.1 → layerbrain-0.3.0}/tests/test_resources/test_machine_connection.py +2 -2
  83. {layerbrain-0.2.1 → layerbrain-0.3.0}/tests/test_resources/test_machines.py +29 -16
  84. {layerbrain-0.2.1 → layerbrain-0.3.0}/tests/test_resources/test_models.py +1 -1
  85. layerbrain-0.3.0/tests/test_resources/test_webhooks.py +71 -0
  86. layerbrain-0.2.1/layerbrain/_version.py +0 -1
  87. layerbrain-0.2.1/layerbrain/cli/commands/brains.py +0 -38
  88. layerbrain-0.2.1/layerbrain/cli/commands/engrams.py +0 -154
  89. layerbrain-0.2.1/layerbrain/cli/commands/models.py +0 -41
  90. layerbrain-0.2.1/layerbrain/cli/commands/subscriptions.py +0 -153
  91. layerbrain-0.2.1/layerbrain/cli/commands/threed.py +0 -39
  92. layerbrain-0.2.1/layerbrain/cli/commands/tools.py +0 -54
  93. layerbrain-0.2.1/layerbrain/sdk/resources/auth.py +0 -68
  94. layerbrain-0.2.1/layerbrain/sdk/resources/brains.py +0 -13
  95. layerbrain-0.2.1/layerbrain/sdk/resources/engrams.py +0 -66
  96. layerbrain-0.2.1/layerbrain/sdk/resources/environments.py +0 -46
  97. layerbrain-0.2.1/layerbrain/sdk/resources/subscriptions.py +0 -55
  98. layerbrain-0.2.1/layerbrain/sdk/resources/threed.py +0 -13
  99. layerbrain-0.2.1/layerbrain/sdk/resources/tools.py +0 -17
  100. layerbrain-0.2.1/layerbrain/sdk/resources/videos.py +0 -17
  101. layerbrain-0.2.1/tests/test_resources/__init__.py +0 -0
  102. layerbrain-0.2.1/tests/test_resources/test_auth.py +0 -141
  103. {layerbrain-0.2.1 → layerbrain-0.3.0}/.github/workflows/publish.yml +0 -0
  104. {layerbrain-0.2.1 → layerbrain-0.3.0}/.gitignore +0 -0
  105. {layerbrain-0.2.1 → layerbrain-0.3.0}/LICENSE +0 -0
  106. {layerbrain-0.2.1/layerbrain/cli → layerbrain-0.3.0/layerbrain/cli/commands}/__init__.py +0 -0
  107. {layerbrain-0.2.1 → layerbrain-0.3.0}/layerbrain/cli/commands/config.py +1 -1
  108. {layerbrain-0.2.1 → layerbrain-0.3.0}/layerbrain/exceptions.py +0 -0
  109. {layerbrain-0.2.1 → layerbrain-0.3.0}/layerbrain/py.typed +0 -0
  110. {layerbrain-0.2.1 → layerbrain-0.3.0}/layerbrain/sdk/_exceptions.py +0 -0
  111. {layerbrain-0.2.1 → layerbrain-0.3.0}/layerbrain/sdk/_pagination.py +0 -0
  112. {layerbrain-0.2.1 → layerbrain-0.3.0}/layerbrain/sdk/_resource.py +0 -0
  113. {layerbrain-0.2.1 → layerbrain-0.3.0}/layerbrain/sdk/openapi/__init__.py +0 -0
  114. {layerbrain-0.2.1 → layerbrain-0.3.0}/layerbrain/sdk/resources/chat/__init__.py +0 -0
  115. {layerbrain-0.2.1 → layerbrain-0.3.0}/layerbrain/sdk/resources/chat/completions.py +0 -0
  116. {layerbrain-0.2.1/layerbrain/cli/commands → layerbrain-0.3.0/tests}/__init__.py +0 -0
  117. {layerbrain-0.2.1/tests → layerbrain-0.3.0/tests/test_cli}/__init__.py +0 -0
  118. {layerbrain-0.2.1 → layerbrain-0.3.0}/tests/test_config.py +0 -0
  119. {layerbrain-0.2.1 → layerbrain-0.3.0}/tests/test_pagination.py +0 -0
  120. {layerbrain-0.2.1/tests/test_cli → layerbrain-0.3.0/tests/test_resources}/__init__.py +0 -0
  121. {layerbrain-0.2.1 → layerbrain-0.3.0}/tests/test_resources/test_chat.py +0 -0
@@ -24,7 +24,7 @@ jobs:
24
24
  - name: Install dependencies
25
25
  run: |
26
26
  python -m pip install --upgrade pip
27
- pip install -e ".[cli]"
27
+ pip install -e .
28
28
  pip install flake8
29
29
 
30
30
  - name: Lint
@@ -1,11 +1,11 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: layerbrain
3
- Version: 0.2.1
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-python
8
- Project-URL: Changelog, https://github.com/layerbrain/layerbrain-python/releases
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 Python SDK
31
+ # Layerbrain CLI
33
32
 
34
33
  [![PyPI version](https://img.shields.io/pypi/v/layerbrain.svg)](https://pypi.org/project/layerbrain/)
35
34
  [![Python 3.10+](https://img.shields.io/badge/python-3.10+-blue.svg)](https://www.python.org/downloads/)
36
- [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://github.com/layerbrain/layerbrain-python/blob/main/LICENSE)
35
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](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
- # SDK + CLI
47
- pip install layerbrain[cli]
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 Connect (WebSocket)
160
+ ### Connect to a Machine (WebSocket)
107
161
 
108
- Connect to a running machine via WebSocket for shell and filesystem access:
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
- # Connect to a running machine via WebSocket
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.search(query="python httpx tutorial", count=5)
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 Python SDK
1
+ # Layerbrain CLI
2
2
 
3
3
  [![PyPI version](https://img.shields.io/pypi/v/layerbrain.svg)](https://pypi.org/project/layerbrain/)
4
4
  [![Python 3.10+](https://img.shields.io/badge/python-3.10+-blue.svg)](https://www.python.org/downloads/)
5
- [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://github.com/layerbrain/layerbrain-python/blob/main/LICENSE)
5
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](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
- # SDK + CLI
16
- pip install layerbrain[cli]
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 Connect (WebSocket)
130
+ ### Connect to a Machine (WebSocket)
76
131
 
77
- Connect to a running machine via WebSocket for shell and filesystem access:
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
- # Connect to a running machine via WebSocket
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.search(query="python httpx tutorial", count=5)
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):
@@ -1,4 +1,4 @@
1
- """Layerbrain Python SDK + CLI.
1
+ """Layerbrain Python SDK.
2
2
 
3
3
  Usage::
4
4
 
@@ -10,6 +10,7 @@ Usage::
10
10
  """
11
11
 
12
12
  from layerbrain.sdk import Layerbrain
13
+
13
14
  from ._version import __version__
14
15
 
15
16
  __all__ = [
@@ -1,5 +1,5 @@
1
1
  """Allow running the CLI via `python -m layerbrain`."""
2
2
 
3
- from layerbrain.cli.app import main
3
+ from layerbrain.cli.main import main
4
4
 
5
5
  main()
@@ -0,0 +1 @@
1
+ __version__ = "0.3.0"
@@ -0,0 +1 @@
1
+ """Layerbrain CLI package."""
@@ -0,0 +1,3 @@
1
+ from layerbrain.cli.main import main
2
+
3
+ main()
@@ -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, Dict, List, Tuple
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: List[Tuple[str, str]],
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: Dict[str, str]) -> Text:
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.exceptions import LayerbrainError
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