banto 5.2.2__tar.gz → 5.2.4__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.
- {banto-5.2.2 → banto-5.2.4}/PKG-INFO +17 -11
- {banto-5.2.2 → banto-5.2.4}/README.md +15 -10
- {banto-5.2.2 → banto-5.2.4}/banto/__init__.py +1 -1
- {banto-5.2.2 → banto-5.2.4}/banto/__main__.py +21 -1
- {banto-5.2.2 → banto-5.2.4}/banto/broker_client.py +13 -2
- {banto-5.2.2 → banto-5.2.4}/banto/broker_setup.py +4 -1
- {banto-5.2.2 → banto-5.2.4}/banto.egg-info/PKG-INFO +17 -11
- {banto-5.2.2 → banto-5.2.4}/banto.egg-info/SOURCES.txt +1 -0
- {banto-5.2.2 → banto-5.2.4}/banto.egg-info/requires.txt +3 -0
- {banto-5.2.2 → banto-5.2.4}/pyproject.toml +2 -1
- {banto-5.2.2 → banto-5.2.4}/tests/test_broker.py +34 -0
- banto-5.2.4/tests/test_cli_version.py +58 -0
- {banto-5.2.2 → banto-5.2.4}/LICENSE +0 -0
- {banto-5.2.2 → banto-5.2.4}/banto/backend.py +0 -0
- {banto-5.2.2 → banto-5.2.4}/banto/broker.py +0 -0
- {banto-5.2.2 → banto-5.2.4}/banto/broker_cli.py +0 -0
- {banto-5.2.2 → banto-5.2.4}/banto/broker_mcp.py +0 -0
- {banto-5.2.2 → banto-5.2.4}/banto/chatgpt.py +0 -0
- {banto-5.2.2 → banto-5.2.4}/banto/config.json +0 -0
- {banto-5.2.2 → banto-5.2.4}/banto/ecdsa_p256.py +0 -0
- {banto-5.2.2 → banto-5.2.4}/banto/guard.py +0 -0
- {banto-5.2.2 → banto-5.2.4}/banto/keychain.py +0 -0
- {banto-5.2.2 → banto-5.2.4}/banto/kimaru_evaluation.py +0 -0
- {banto-5.2.2 → banto-5.2.4}/banto/kimaru_material.py +0 -0
- {banto-5.2.2 → banto-5.2.4}/banto/lease.py +0 -0
- {banto-5.2.2 → banto-5.2.4}/banto/lease_cli.py +0 -0
- {banto-5.2.2 → banto-5.2.4}/banto/mcp_server.py +0 -0
- {banto-5.2.2 → banto-5.2.4}/banto/pricing.json +0 -0
- {banto-5.2.2 → banto-5.2.4}/banto/profiles.py +0 -0
- {banto-5.2.2 → banto-5.2.4}/banto/register_popup.py +0 -0
- {banto-5.2.2 → banto-5.2.4}/banto/rotation_capabilities.json +0 -0
- {banto-5.2.2 → banto-5.2.4}/banto/sealed_cli.py +0 -0
- {banto-5.2.2 → banto-5.2.4}/banto/sealed_signer.py +0 -0
- {banto-5.2.2 → banto-5.2.4}/banto/sync/__init__.py +0 -0
- {banto-5.2.2 → banto-5.2.4}/banto/sync/audit.py +0 -0
- {banto-5.2.2 → banto-5.2.4}/banto/sync/browser_batch.py +0 -0
- {banto-5.2.2 → banto-5.2.4}/banto/sync/browser_issuer.py +0 -0
- {banto-5.2.2 → banto-5.2.4}/banto/sync/browser_recorder.py +0 -0
- {banto-5.2.2 → banto-5.2.4}/banto/sync/capabilities.py +0 -0
- {banto-5.2.2 → banto-5.2.4}/banto/sync/cli.py +0 -0
- {banto-5.2.2 → banto-5.2.4}/banto/sync/cloudflare_token_rotator.py +0 -0
- {banto-5.2.2 → banto-5.2.4}/banto/sync/config.py +0 -0
- {banto-5.2.2 → banto-5.2.4}/banto/sync/drivers/__init__.py +0 -0
- {banto-5.2.2 → banto-5.2.4}/banto/sync/drivers/alibaba.py +0 -0
- {banto-5.2.2 → banto-5.2.4}/banto/sync/drivers/aws_sm.py +0 -0
- {banto-5.2.2 → banto-5.2.4}/banto/sync/drivers/aws_ssm.py +0 -0
- {banto-5.2.2 → banto-5.2.4}/banto/sync/drivers/azure.py +0 -0
- {banto-5.2.2 → banto-5.2.4}/banto/sync/drivers/azure_devops.py +0 -0
- {banto-5.2.2 → banto-5.2.4}/banto/sync/drivers/base.py +0 -0
- {banto-5.2.2 → banto-5.2.4}/banto/sync/drivers/bitbucket.py +0 -0
- {banto-5.2.2 → banto-5.2.4}/banto/sync/drivers/circleci.py +0 -0
- {banto-5.2.2 → banto-5.2.4}/banto/sync/drivers/cloudflare.py +0 -0
- {banto-5.2.2 → banto-5.2.4}/banto/sync/drivers/deno.py +0 -0
- {banto-5.2.2 → banto-5.2.4}/banto/sync/drivers/digitalocean.py +0 -0
- {banto-5.2.2 → banto-5.2.4}/banto/sync/drivers/docker.py +0 -0
- {banto-5.2.2 → banto-5.2.4}/banto/sync/drivers/flyio.py +0 -0
- {banto-5.2.2 → banto-5.2.4}/banto/sync/drivers/gcp.py +0 -0
- {banto-5.2.2 → banto-5.2.4}/banto/sync/drivers/github.py +0 -0
- {banto-5.2.2 → banto-5.2.4}/banto/sync/drivers/gitlab.py +0 -0
- {banto-5.2.2 → banto-5.2.4}/banto/sync/drivers/hasura.py +0 -0
- {banto-5.2.2 → banto-5.2.4}/banto/sync/drivers/heroku.py +0 -0
- {banto-5.2.2 → banto-5.2.4}/banto/sync/drivers/huawei.py +0 -0
- {banto-5.2.2 → banto-5.2.4}/banto/sync/drivers/jdcloud.py +0 -0
- {banto-5.2.2 → banto-5.2.4}/banto/sync/drivers/kubernetes.py +0 -0
- {banto-5.2.2 → banto-5.2.4}/banto/sync/drivers/laravel_forge.py +0 -0
- {banto-5.2.2 → banto-5.2.4}/banto/sync/drivers/local.py +0 -0
- {banto-5.2.2 → banto-5.2.4}/banto/sync/drivers/naver.py +0 -0
- {banto-5.2.2 → banto-5.2.4}/banto/sync/drivers/netlify.py +0 -0
- {banto-5.2.2 → banto-5.2.4}/banto/sync/drivers/nhn.py +0 -0
- {banto-5.2.2 → banto-5.2.4}/banto/sync/drivers/railway.py +0 -0
- {banto-5.2.2 → banto-5.2.4}/banto/sync/drivers/render.py +0 -0
- {banto-5.2.2 → banto-5.2.4}/banto/sync/drivers/sakura.py +0 -0
- {banto-5.2.2 → banto-5.2.4}/banto/sync/drivers/supabase.py +0 -0
- {banto-5.2.2 → banto-5.2.4}/banto/sync/drivers/tencent.py +0 -0
- {banto-5.2.2 → banto-5.2.4}/banto/sync/drivers/terraform.py +0 -0
- {banto-5.2.2 → banto-5.2.4}/banto/sync/drivers/vercel.py +0 -0
- {banto-5.2.2 → banto-5.2.4}/banto/sync/drivers/volcengine.py +0 -0
- {banto-5.2.2 → banto-5.2.4}/banto/sync/google_rotator.py +0 -0
- {banto-5.2.2 → banto-5.2.4}/banto/sync/history.py +0 -0
- {banto-5.2.2 → banto-5.2.4}/banto/sync/incident_report.py +0 -0
- {banto-5.2.2 → banto-5.2.4}/banto/sync/notifiers/__init__.py +0 -0
- {banto-5.2.2 → banto-5.2.4}/banto/sync/notifiers/base.py +0 -0
- {banto-5.2.2 → banto-5.2.4}/banto/sync/notifiers/datadog.py +0 -0
- {banto-5.2.2 → banto-5.2.4}/banto/sync/notifiers/pagerduty.py +0 -0
- {banto-5.2.2 → banto-5.2.4}/banto/sync/notifiers/slack.py +0 -0
- {banto-5.2.2 → banto-5.2.4}/banto/sync/notifiers/teams.py +0 -0
- {banto-5.2.2 → banto-5.2.4}/banto/sync/openai_rotator.py +0 -0
- {banto-5.2.2 → banto-5.2.4}/banto/sync/propagation.py +0 -0
- {banto-5.2.2 → banto-5.2.4}/banto/sync/setup.py +0 -0
- {banto-5.2.2 → banto-5.2.4}/banto/sync/smoke_presets.py +0 -0
- {banto-5.2.2 → banto-5.2.4}/banto/sync/stripe_webhook_rotator.py +0 -0
- {banto-5.2.2 → banto-5.2.4}/banto/sync/sync.py +0 -0
- {banto-5.2.2 → banto-5.2.4}/banto/sync/sync_state.py +0 -0
- {banto-5.2.2 → banto-5.2.4}/banto/sync/validate.py +0 -0
- {banto-5.2.2 → banto-5.2.4}/banto/sync/vercel_inventory.py +0 -0
- {banto-5.2.2 → banto-5.2.4}/banto/sync/web.py +0 -0
- {banto-5.2.2 → banto-5.2.4}/banto/sync/x_oauth_pkce.py +0 -0
- {banto-5.2.2 → banto-5.2.4}/banto/sync/x_oauth_rotator.py +0 -0
- {banto-5.2.2 → banto-5.2.4}/banto/sync/xai_rotator.py +0 -0
- {banto-5.2.2 → banto-5.2.4}/banto/vault.py +0 -0
- {banto-5.2.2 → banto-5.2.4}/banto/widgets.py +0 -0
- {banto-5.2.2 → banto-5.2.4}/banto.egg-info/dependency_links.txt +0 -0
- {banto-5.2.2 → banto-5.2.4}/banto.egg-info/entry_points.txt +0 -0
- {banto-5.2.2 → banto-5.2.4}/banto.egg-info/top_level.txt +0 -0
- {banto-5.2.2 → banto-5.2.4}/setup.cfg +0 -0
- {banto-5.2.2 → banto-5.2.4}/tests/test_broker_setup.py +0 -0
- {banto-5.2.2 → banto-5.2.4}/tests/test_ecdsa_p256.py +0 -0
- {banto-5.2.2 → banto-5.2.4}/tests/test_hold_timeout.py +0 -0
- {banto-5.2.2 → banto-5.2.4}/tests/test_host_validation.py +0 -0
- {banto-5.2.2 → banto-5.2.4}/tests/test_keychain_account.py +0 -0
- {banto-5.2.2 → banto-5.2.4}/tests/test_kimaru_evaluation.py +0 -0
- {banto-5.2.2 → banto-5.2.4}/tests/test_kimaru_material.py +0 -0
- {banto-5.2.2 → banto-5.2.4}/tests/test_lease.py +0 -0
- {banto-5.2.2 → banto-5.2.4}/tests/test_model_profiles.py +0 -0
- {banto-5.2.2 → banto-5.2.4}/tests/test_recommend_profile.py +0 -0
- {banto-5.2.2 → banto-5.2.4}/tests/test_register_popup.py +0 -0
- {banto-5.2.2 → banto-5.2.4}/tests/test_sealed_signer.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: banto
|
|
3
|
-
Version: 5.2.
|
|
3
|
+
Version: 5.2.4
|
|
4
4
|
Summary: Local-first secret management with optional budget gating and dynamic leases
|
|
5
5
|
Author: AllNew LLC
|
|
6
6
|
License-Expression: LicenseRef-Dual
|
|
@@ -20,6 +20,7 @@ Classifier: Topic :: Software Development :: Libraries
|
|
|
20
20
|
Requires-Python: >=3.10
|
|
21
21
|
Description-Content-Type: text/markdown
|
|
22
22
|
License-File: LICENSE
|
|
23
|
+
Requires-Dist: tomli>=1.1; python_version < "3.11"
|
|
23
24
|
Provides-Extra: mcp
|
|
24
25
|
Requires-Dist: mcp<2,>=1.0; extra == "mcp"
|
|
25
26
|
Provides-Extra: browser
|
|
@@ -37,7 +38,7 @@ Dynamic: license-file
|
|
|
37
38
|
</p>
|
|
38
39
|
|
|
39
40
|
<p align="center">
|
|
40
|
-
<img src="https://img.shields.io/badge/version-5.2.
|
|
41
|
+
<img src="https://img.shields.io/badge/version-5.2.4-0D3B66?style=flat-square" alt="version">
|
|
41
42
|
<img src="https://img.shields.io/badge/python-3.10%2B-0D3B66?style=flat-square" alt="python">
|
|
42
43
|
<img src="https://img.shields.io/badge/license-Dual-262626?style=flat-square" alt="license">
|
|
43
44
|
<img src="https://img.shields.io/badge/stdlib%20only-262626?style=flat-square&label=dependencies" alt="dependencies">
|
|
@@ -193,30 +194,34 @@ BANTO SYNC VALIDATE — Testing 4 key(s)
|
|
|
193
194
|
|
|
194
195
|
- macOS (uses Keychain for secret storage)
|
|
195
196
|
- Python 3.10+
|
|
196
|
-
- No external dependencies (MCP server: `pip install "banto[mcp]
|
|
197
|
+
- No external dependencies (MCP server: `pip install "banto[mcp]"`)
|
|
197
198
|
|
|
198
199
|
## ⚡ Quick start
|
|
199
200
|
|
|
200
201
|
### 1. Install
|
|
201
202
|
|
|
202
203
|
```bash
|
|
203
|
-
# banto is not published on PyPI yet. Install directly from the GitHub repository.
|
|
204
|
-
|
|
205
204
|
# uv (recommended)
|
|
206
|
-
uv tool install
|
|
205
|
+
uv tool install banto
|
|
207
206
|
|
|
208
207
|
# pipx
|
|
209
|
-
pipx install
|
|
208
|
+
pipx install banto
|
|
210
209
|
|
|
211
210
|
# pip
|
|
212
|
-
pip install
|
|
211
|
+
pip install banto
|
|
212
|
+
|
|
213
|
+
# With MCP server support
|
|
214
|
+
pip install "banto[mcp]"
|
|
213
215
|
|
|
214
216
|
# From source
|
|
215
217
|
git clone https://github.com/allnew-llc/banto.git
|
|
216
218
|
cd banto
|
|
217
219
|
pip install -e .
|
|
218
220
|
|
|
219
|
-
#
|
|
221
|
+
# Latest development version from GitHub
|
|
222
|
+
uv tool install "git+https://github.com/allnew-llc/banto.git"
|
|
223
|
+
pipx install "git+https://github.com/allnew-llc/banto.git"
|
|
224
|
+
pip install "git+https://github.com/allnew-llc/banto.git"
|
|
220
225
|
pip install "banto[mcp] @ git+https://github.com/allnew-llc/banto.git"
|
|
221
226
|
```
|
|
222
227
|
|
|
@@ -258,7 +263,7 @@ banto budget --provider openai 30 # $30/month for OpenAI
|
|
|
258
263
|
## 💻 CLI reference
|
|
259
264
|
|
|
260
265
|
<details>
|
|
261
|
-
<summary><strong>Core commands (
|
|
266
|
+
<summary><strong>Core commands (10)</strong></summary>
|
|
262
267
|
|
|
263
268
|
| Command | Description |
|
|
264
269
|
|---------|-------------|
|
|
@@ -271,6 +276,7 @@ banto budget --provider openai 30 # $30/month for OpenAI
|
|
|
271
276
|
| `banto list` | List stored keys and budget summary |
|
|
272
277
|
| `banto check <model> ...` | Dry-run budget check (`--tokens`, `--n`, `--seconds`, `--quality`, `--size`) |
|
|
273
278
|
| `banto init` | Copy default config to `~/.config/banto/` |
|
|
279
|
+
| `banto version` | Print the installed version (`banto --version` / `banto -V` also work, including in broker mode) |
|
|
274
280
|
|
|
275
281
|
</details>
|
|
276
282
|
|
|
@@ -358,7 +364,7 @@ Add to your `.mcp.json`:
|
|
|
358
364
|
Requires the optional MCP dependency:
|
|
359
365
|
|
|
360
366
|
```bash
|
|
361
|
-
pip install "banto[mcp]
|
|
367
|
+
pip install "banto[mcp]"
|
|
362
368
|
```
|
|
363
369
|
|
|
364
370
|
### ChatGPT
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
</p>
|
|
10
10
|
|
|
11
11
|
<p align="center">
|
|
12
|
-
<img src="https://img.shields.io/badge/version-5.2.
|
|
12
|
+
<img src="https://img.shields.io/badge/version-5.2.4-0D3B66?style=flat-square" alt="version">
|
|
13
13
|
<img src="https://img.shields.io/badge/python-3.10%2B-0D3B66?style=flat-square" alt="python">
|
|
14
14
|
<img src="https://img.shields.io/badge/license-Dual-262626?style=flat-square" alt="license">
|
|
15
15
|
<img src="https://img.shields.io/badge/stdlib%20only-262626?style=flat-square&label=dependencies" alt="dependencies">
|
|
@@ -165,30 +165,34 @@ BANTO SYNC VALIDATE — Testing 4 key(s)
|
|
|
165
165
|
|
|
166
166
|
- macOS (uses Keychain for secret storage)
|
|
167
167
|
- Python 3.10+
|
|
168
|
-
- No external dependencies (MCP server: `pip install "banto[mcp]
|
|
168
|
+
- No external dependencies (MCP server: `pip install "banto[mcp]"`)
|
|
169
169
|
|
|
170
170
|
## ⚡ Quick start
|
|
171
171
|
|
|
172
172
|
### 1. Install
|
|
173
173
|
|
|
174
174
|
```bash
|
|
175
|
-
# banto is not published on PyPI yet. Install directly from the GitHub repository.
|
|
176
|
-
|
|
177
175
|
# uv (recommended)
|
|
178
|
-
uv tool install
|
|
176
|
+
uv tool install banto
|
|
179
177
|
|
|
180
178
|
# pipx
|
|
181
|
-
pipx install
|
|
179
|
+
pipx install banto
|
|
182
180
|
|
|
183
181
|
# pip
|
|
184
|
-
pip install
|
|
182
|
+
pip install banto
|
|
183
|
+
|
|
184
|
+
# With MCP server support
|
|
185
|
+
pip install "banto[mcp]"
|
|
185
186
|
|
|
186
187
|
# From source
|
|
187
188
|
git clone https://github.com/allnew-llc/banto.git
|
|
188
189
|
cd banto
|
|
189
190
|
pip install -e .
|
|
190
191
|
|
|
191
|
-
#
|
|
192
|
+
# Latest development version from GitHub
|
|
193
|
+
uv tool install "git+https://github.com/allnew-llc/banto.git"
|
|
194
|
+
pipx install "git+https://github.com/allnew-llc/banto.git"
|
|
195
|
+
pip install "git+https://github.com/allnew-llc/banto.git"
|
|
192
196
|
pip install "banto[mcp] @ git+https://github.com/allnew-llc/banto.git"
|
|
193
197
|
```
|
|
194
198
|
|
|
@@ -230,7 +234,7 @@ banto budget --provider openai 30 # $30/month for OpenAI
|
|
|
230
234
|
## 💻 CLI reference
|
|
231
235
|
|
|
232
236
|
<details>
|
|
233
|
-
<summary><strong>Core commands (
|
|
237
|
+
<summary><strong>Core commands (10)</strong></summary>
|
|
234
238
|
|
|
235
239
|
| Command | Description |
|
|
236
240
|
|---------|-------------|
|
|
@@ -243,6 +247,7 @@ banto budget --provider openai 30 # $30/month for OpenAI
|
|
|
243
247
|
| `banto list` | List stored keys and budget summary |
|
|
244
248
|
| `banto check <model> ...` | Dry-run budget check (`--tokens`, `--n`, `--seconds`, `--quality`, `--size`) |
|
|
245
249
|
| `banto init` | Copy default config to `~/.config/banto/` |
|
|
250
|
+
| `banto version` | Print the installed version (`banto --version` / `banto -V` also work, including in broker mode) |
|
|
246
251
|
|
|
247
252
|
</details>
|
|
248
253
|
|
|
@@ -330,7 +335,7 @@ Add to your `.mcp.json`:
|
|
|
330
335
|
Requires the optional MCP dependency:
|
|
331
336
|
|
|
332
337
|
```bash
|
|
333
|
-
pip install "banto[mcp]
|
|
338
|
+
pip install "banto[mcp]"
|
|
334
339
|
```
|
|
335
340
|
|
|
336
341
|
### ChatGPT
|
|
@@ -11,6 +11,7 @@ Commands:
|
|
|
11
11
|
list List stored provider keys
|
|
12
12
|
check <model> ... Dry-run budget check for a model
|
|
13
13
|
init Copy default config to ~/.config/banto/
|
|
14
|
+
version Show the installed version (also --version, -V)
|
|
14
15
|
"""
|
|
15
16
|
|
|
16
17
|
import getpass
|
|
@@ -475,6 +476,15 @@ def cmd_sealed(args: list[str]) -> None:
|
|
|
475
476
|
cmd_sealed_dispatch(args)
|
|
476
477
|
|
|
477
478
|
|
|
479
|
+
def _print_version() -> None:
|
|
480
|
+
from . import __version__
|
|
481
|
+
print(f"banto {__version__}")
|
|
482
|
+
|
|
483
|
+
|
|
484
|
+
def cmd_version(args: list[str]) -> None:
|
|
485
|
+
_print_version()
|
|
486
|
+
|
|
487
|
+
|
|
478
488
|
def cmd_chatgpt(args: list[str]) -> None:
|
|
479
489
|
if not args or args[0] in ("-h", "--help"):
|
|
480
490
|
print("banto chatgpt: Connect banto to ChatGPT\n")
|
|
@@ -503,10 +513,18 @@ COMMANDS = {
|
|
|
503
513
|
"lease": cmd_lease,
|
|
504
514
|
"sealed": cmd_sealed,
|
|
505
515
|
"chatgpt": cmd_chatgpt,
|
|
516
|
+
"version": cmd_version,
|
|
506
517
|
}
|
|
507
518
|
|
|
519
|
+
VERSION_FLAGS = ("--version", "-V", "version")
|
|
520
|
+
|
|
508
521
|
|
|
509
522
|
def main() -> None:
|
|
523
|
+
# Handled before broker routing so it works in broker mode too and never
|
|
524
|
+
# touches Keychain, the broker socket, or config files.
|
|
525
|
+
if len(sys.argv) > 1 and sys.argv[1] in VERSION_FLAGS:
|
|
526
|
+
_print_version()
|
|
527
|
+
return
|
|
510
528
|
if len(sys.argv) > 1 and sys.argv[1] == "broker":
|
|
511
529
|
from .broker import main as broker_main
|
|
512
530
|
sys.argv.pop(1)
|
|
@@ -518,7 +536,8 @@ def main() -> None:
|
|
|
518
536
|
return
|
|
519
537
|
if len(sys.argv) < 2 or sys.argv[1] in ("-h", "--help"):
|
|
520
538
|
print("banto: Budget-gated API key vault for LLM applications\n")
|
|
521
|
-
print("Usage: banto <command> [args]
|
|
539
|
+
print("Usage: banto <command> [args]")
|
|
540
|
+
print(" banto --version | -V\n")
|
|
522
541
|
print("Commands:")
|
|
523
542
|
print(" broker <cmd> Common Keychain process: install, health, enable, call")
|
|
524
543
|
print(" status Show budget status (with per-provider/model breakdown)")
|
|
@@ -534,6 +553,7 @@ def main() -> None:
|
|
|
534
553
|
print(" lease <cmd> Dynamic secrets with TTL and auto-revoke")
|
|
535
554
|
print(" sealed <cmd> Secure Enclave sealed signing (non-extractable keys)")
|
|
536
555
|
print(" chatgpt connect Connect banto to ChatGPT (starts tunnel)")
|
|
556
|
+
print(" version Show the installed version (also --version, -V)")
|
|
537
557
|
print()
|
|
538
558
|
print("Budget examples:")
|
|
539
559
|
print(" banto budget Show all limits")
|
|
@@ -59,9 +59,20 @@ class BrokerClient:
|
|
|
59
59
|
with socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) as connection:
|
|
60
60
|
connection.settimeout(self.timeout)
|
|
61
61
|
connection.connect(str(self.path))
|
|
62
|
-
|
|
62
|
+
send_error = None
|
|
63
|
+
try:
|
|
64
|
+
connection.sendall(encode_message({"operation": operation, "arguments": arguments}))
|
|
65
|
+
except (BrokenPipeError, ConnectionResetError) as error:
|
|
66
|
+
# The broker may reply (e.g. peer_denied) and close before reading
|
|
67
|
+
# the request; its reply is still buffered, so read it.
|
|
68
|
+
send_error = error
|
|
63
69
|
with connection.makefile("rb") as stream:
|
|
64
|
-
|
|
70
|
+
try:
|
|
71
|
+
result = read_message(stream)
|
|
72
|
+
except BrokerError:
|
|
73
|
+
if send_error is not None:
|
|
74
|
+
raise send_error from None
|
|
75
|
+
raise
|
|
65
76
|
except (OSError, ValueError):
|
|
66
77
|
raise BrokerError("broker_unavailable_or_outcome_unknown; do not retry mutations blindly") from None
|
|
67
78
|
if not result.get("ok"):
|
|
@@ -45,7 +45,10 @@ def json_update(text: str, entry: dict) -> str:
|
|
|
45
45
|
|
|
46
46
|
|
|
47
47
|
def toml_update(text: str, command: str) -> str:
|
|
48
|
-
|
|
48
|
+
try:
|
|
49
|
+
import tomllib
|
|
50
|
+
except ModuleNotFoundError: # Python 3.10: tomllib was added in 3.11
|
|
51
|
+
import tomli as tomllib
|
|
49
52
|
parsed = tomllib.loads(text)
|
|
50
53
|
entry = {"command": command, "args": [], "enabled": True}
|
|
51
54
|
existing = parsed.get("mcp_servers", {}).get("banto")
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: banto
|
|
3
|
-
Version: 5.2.
|
|
3
|
+
Version: 5.2.4
|
|
4
4
|
Summary: Local-first secret management with optional budget gating and dynamic leases
|
|
5
5
|
Author: AllNew LLC
|
|
6
6
|
License-Expression: LicenseRef-Dual
|
|
@@ -20,6 +20,7 @@ Classifier: Topic :: Software Development :: Libraries
|
|
|
20
20
|
Requires-Python: >=3.10
|
|
21
21
|
Description-Content-Type: text/markdown
|
|
22
22
|
License-File: LICENSE
|
|
23
|
+
Requires-Dist: tomli>=1.1; python_version < "3.11"
|
|
23
24
|
Provides-Extra: mcp
|
|
24
25
|
Requires-Dist: mcp<2,>=1.0; extra == "mcp"
|
|
25
26
|
Provides-Extra: browser
|
|
@@ -37,7 +38,7 @@ Dynamic: license-file
|
|
|
37
38
|
</p>
|
|
38
39
|
|
|
39
40
|
<p align="center">
|
|
40
|
-
<img src="https://img.shields.io/badge/version-5.2.
|
|
41
|
+
<img src="https://img.shields.io/badge/version-5.2.4-0D3B66?style=flat-square" alt="version">
|
|
41
42
|
<img src="https://img.shields.io/badge/python-3.10%2B-0D3B66?style=flat-square" alt="python">
|
|
42
43
|
<img src="https://img.shields.io/badge/license-Dual-262626?style=flat-square" alt="license">
|
|
43
44
|
<img src="https://img.shields.io/badge/stdlib%20only-262626?style=flat-square&label=dependencies" alt="dependencies">
|
|
@@ -193,30 +194,34 @@ BANTO SYNC VALIDATE — Testing 4 key(s)
|
|
|
193
194
|
|
|
194
195
|
- macOS (uses Keychain for secret storage)
|
|
195
196
|
- Python 3.10+
|
|
196
|
-
- No external dependencies (MCP server: `pip install "banto[mcp]
|
|
197
|
+
- No external dependencies (MCP server: `pip install "banto[mcp]"`)
|
|
197
198
|
|
|
198
199
|
## ⚡ Quick start
|
|
199
200
|
|
|
200
201
|
### 1. Install
|
|
201
202
|
|
|
202
203
|
```bash
|
|
203
|
-
# banto is not published on PyPI yet. Install directly from the GitHub repository.
|
|
204
|
-
|
|
205
204
|
# uv (recommended)
|
|
206
|
-
uv tool install
|
|
205
|
+
uv tool install banto
|
|
207
206
|
|
|
208
207
|
# pipx
|
|
209
|
-
pipx install
|
|
208
|
+
pipx install banto
|
|
210
209
|
|
|
211
210
|
# pip
|
|
212
|
-
pip install
|
|
211
|
+
pip install banto
|
|
212
|
+
|
|
213
|
+
# With MCP server support
|
|
214
|
+
pip install "banto[mcp]"
|
|
213
215
|
|
|
214
216
|
# From source
|
|
215
217
|
git clone https://github.com/allnew-llc/banto.git
|
|
216
218
|
cd banto
|
|
217
219
|
pip install -e .
|
|
218
220
|
|
|
219
|
-
#
|
|
221
|
+
# Latest development version from GitHub
|
|
222
|
+
uv tool install "git+https://github.com/allnew-llc/banto.git"
|
|
223
|
+
pipx install "git+https://github.com/allnew-llc/banto.git"
|
|
224
|
+
pip install "git+https://github.com/allnew-llc/banto.git"
|
|
220
225
|
pip install "banto[mcp] @ git+https://github.com/allnew-llc/banto.git"
|
|
221
226
|
```
|
|
222
227
|
|
|
@@ -258,7 +263,7 @@ banto budget --provider openai 30 # $30/month for OpenAI
|
|
|
258
263
|
## 💻 CLI reference
|
|
259
264
|
|
|
260
265
|
<details>
|
|
261
|
-
<summary><strong>Core commands (
|
|
266
|
+
<summary><strong>Core commands (10)</strong></summary>
|
|
262
267
|
|
|
263
268
|
| Command | Description |
|
|
264
269
|
|---------|-------------|
|
|
@@ -271,6 +276,7 @@ banto budget --provider openai 30 # $30/month for OpenAI
|
|
|
271
276
|
| `banto list` | List stored keys and budget summary |
|
|
272
277
|
| `banto check <model> ...` | Dry-run budget check (`--tokens`, `--n`, `--seconds`, `--quality`, `--size`) |
|
|
273
278
|
| `banto init` | Copy default config to `~/.config/banto/` |
|
|
279
|
+
| `banto version` | Print the installed version (`banto --version` / `banto -V` also work, including in broker mode) |
|
|
274
280
|
|
|
275
281
|
</details>
|
|
276
282
|
|
|
@@ -358,7 +364,7 @@ Add to your `.mcp.json`:
|
|
|
358
364
|
Requires the optional MCP dependency:
|
|
359
365
|
|
|
360
366
|
```bash
|
|
361
|
-
pip install "banto[mcp]
|
|
367
|
+
pip install "banto[mcp]"
|
|
362
368
|
```
|
|
363
369
|
|
|
364
370
|
### ChatGPT
|
|
@@ -4,12 +4,13 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "banto"
|
|
7
|
-
version = "5.2.
|
|
7
|
+
version = "5.2.4"
|
|
8
8
|
description = "Local-first secret management with optional budget gating and dynamic leases"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = "LicenseRef-Dual"
|
|
11
11
|
requires-python = ">=3.10"
|
|
12
12
|
authors = [{ name = "AllNew LLC" }]
|
|
13
|
+
dependencies = ["tomli>=1.1; python_version < '3.11'"]
|
|
13
14
|
keywords = ["llm", "api", "budget", "keychain", "macos", "security", "secrets", "sync", "vault", "lease", "dynamic-secrets"]
|
|
14
15
|
classifiers = [
|
|
15
16
|
"Development Status :: 4 - Beta",
|
|
@@ -51,6 +51,40 @@ def test_other_uid_denied(local_server, monkeypatch):
|
|
|
51
51
|
client.call("health")
|
|
52
52
|
|
|
53
53
|
|
|
54
|
+
def test_reply_read_when_broker_closes_before_request(monkeypatch):
|
|
55
|
+
import socket
|
|
56
|
+
import tempfile
|
|
57
|
+
with tempfile.TemporaryDirectory(prefix="banto-", dir="/tmp") as directory:
|
|
58
|
+
path = Path(directory) / "b.sock"
|
|
59
|
+
listener = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
|
|
60
|
+
listener.bind(str(path))
|
|
61
|
+
os.chmod(path, 0o600)
|
|
62
|
+
listener.listen(1)
|
|
63
|
+
closed = threading.Event()
|
|
64
|
+
encode = broker_client.encode_message
|
|
65
|
+
|
|
66
|
+
def deny_and_close():
|
|
67
|
+
connection, _ = listener.accept()
|
|
68
|
+
connection.sendall(encode({"ok": False, "error": "peer_denied"}))
|
|
69
|
+
connection.close()
|
|
70
|
+
closed.set()
|
|
71
|
+
|
|
72
|
+
thread = threading.Thread(target=deny_and_close, daemon=True)
|
|
73
|
+
thread.start()
|
|
74
|
+
|
|
75
|
+
def encode_after_close(value):
|
|
76
|
+
assert closed.wait(5)
|
|
77
|
+
return encode(value)
|
|
78
|
+
|
|
79
|
+
monkeypatch.setattr(broker_client, "encode_message", encode_after_close)
|
|
80
|
+
try:
|
|
81
|
+
with pytest.raises(BrokerError, match="peer_denied"):
|
|
82
|
+
BrokerClient(path).call("health")
|
|
83
|
+
finally:
|
|
84
|
+
thread.join(5)
|
|
85
|
+
listener.close()
|
|
86
|
+
|
|
87
|
+
|
|
54
88
|
def test_exception_does_not_leak(local_server):
|
|
55
89
|
server, client = local_server
|
|
56
90
|
def fail(*args):
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# Copyright 2025-2026 AllNew LLC
|
|
2
|
+
# Licensed under LicenseRef-Dual (see LICENSE)
|
|
3
|
+
"""Tests for `banto --version` / `-V` / `version`."""
|
|
4
|
+
|
|
5
|
+
import subprocess
|
|
6
|
+
import sys
|
|
7
|
+
from unittest.mock import Mock
|
|
8
|
+
|
|
9
|
+
import pytest
|
|
10
|
+
|
|
11
|
+
import banto
|
|
12
|
+
from banto import __main__ as cli
|
|
13
|
+
from banto import broker_cli
|
|
14
|
+
from banto.broker_client import BrokerClient
|
|
15
|
+
|
|
16
|
+
FLAGS = ["--version", "-V", "version"]
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@pytest.mark.parametrize("flag", FLAGS)
|
|
20
|
+
def test_version_prints_and_exits_zero(flag, monkeypatch, capsys):
|
|
21
|
+
monkeypatch.setattr(sys, "argv", ["banto", flag])
|
|
22
|
+
cli.main() # returns normally (exit status 0)
|
|
23
|
+
assert capsys.readouterr().out == f"banto {banto.__version__}\n"
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@pytest.mark.parametrize("flag", FLAGS)
|
|
27
|
+
def test_version_works_in_broker_mode(flag, tmp_path, monkeypatch, capsys):
|
|
28
|
+
(tmp_path / "required").touch()
|
|
29
|
+
monkeypatch.setattr(broker_cli, "runtime_dir", lambda: tmp_path)
|
|
30
|
+
call = Mock(side_effect=AssertionError("broker must not be called"))
|
|
31
|
+
monkeypatch.setattr(BrokerClient, "call", call)
|
|
32
|
+
monkeypatch.setattr(sys, "argv", ["banto", flag])
|
|
33
|
+
cli.main()
|
|
34
|
+
assert capsys.readouterr().out == f"banto {banto.__version__}\n"
|
|
35
|
+
call.assert_not_called()
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
@pytest.mark.parametrize("flag", ["--version", "-V"])
|
|
39
|
+
def test_version_subprocess_exit_code(flag):
|
|
40
|
+
proc = subprocess.run(
|
|
41
|
+
[sys.executable, "-m", "banto", flag], capture_output=True, text=True, timeout=60
|
|
42
|
+
)
|
|
43
|
+
assert proc.returncode == 0
|
|
44
|
+
assert proc.stdout.strip() == f"banto {banto.__version__}"
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def test_help_lists_version(monkeypatch, capsys):
|
|
48
|
+
monkeypatch.setattr(sys, "argv", ["banto", "--help"])
|
|
49
|
+
with pytest.raises(SystemExit) as exc:
|
|
50
|
+
cli.main()
|
|
51
|
+
assert exc.value.code == 0
|
|
52
|
+
out = capsys.readouterr().out
|
|
53
|
+
assert "--version" in out
|
|
54
|
+
assert "version " in out
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def test_version_is_registered_command():
|
|
58
|
+
assert "version" in cli.COMMANDS
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|