bonito-cli 0.6.0__tar.gz → 0.7.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 (47) hide show
  1. {bonito_cli-0.6.0 → bonito_cli-0.7.0}/PKG-INFO +1 -1
  2. {bonito_cli-0.6.0 → bonito_cli-0.7.0}/bonito_cli/commands/deploy.py +5 -8
  3. {bonito_cli-0.6.0 → bonito_cli-0.7.0}/bonito_cli/commands/init.py +123 -1
  4. {bonito_cli-0.6.0 → bonito_cli-0.7.0}/bonito_cli/commands/providers.py +73 -1
  5. bonito_cli-0.7.0/bonito_cli/examples/enterprise.yaml +67 -0
  6. bonito_cli-0.7.0/bonito_cli/examples/multi-provider-failover.yaml +35 -0
  7. bonito_cli-0.7.0/bonito_cli/examples/quickstart.yaml +7 -0
  8. bonito_cli-0.7.0/bonito_cli/examples/support-agent.yaml +22 -0
  9. bonito_cli-0.7.0/bonito_cli/utils/__init__.py +0 -0
  10. {bonito_cli-0.6.0 → bonito_cli-0.7.0}/pyproject.toml +2 -1
  11. {bonito_cli-0.6.0 → bonito_cli-0.7.0}/.gitignore +0 -0
  12. {bonito_cli-0.6.0 → bonito_cli-0.7.0}/CHANGELOG.md +0 -0
  13. {bonito_cli-0.6.0 → bonito_cli-0.7.0}/CLI_SPEC.md +0 -0
  14. {bonito_cli-0.6.0 → bonito_cli-0.7.0}/README.md +0 -0
  15. {bonito_cli-0.6.0 → bonito_cli-0.7.0}/bonito_cli/__init__.py +0 -0
  16. {bonito_cli-0.6.0 → bonito_cli-0.7.0}/bonito_cli/__main__.py +0 -0
  17. {bonito_cli-0.6.0 → bonito_cli-0.7.0}/bonito_cli/api.py +0 -0
  18. {bonito_cli-0.6.0 → bonito_cli-0.7.0}/bonito_cli/app.py +0 -0
  19. {bonito_cli-0.6.0 → bonito_cli-0.7.0}/bonito_cli/commands/__init__.py +0 -0
  20. {bonito_cli-0.6.0 → bonito_cli-0.7.0}/bonito_cli/commands/admin.py +0 -0
  21. {bonito_cli-0.6.0 → bonito_cli-0.7.0}/bonito_cli/commands/agents.py +0 -0
  22. {bonito_cli-0.6.0 → bonito_cli-0.7.0}/bonito_cli/commands/analytics.py +0 -0
  23. {bonito_cli-0.6.0 → bonito_cli-0.7.0}/bonito_cli/commands/approval.py +0 -0
  24. {bonito_cli-0.6.0 → bonito_cli-0.7.0}/bonito_cli/commands/audit.py +0 -0
  25. {bonito_cli-0.6.0 → bonito_cli-0.7.0}/bonito_cli/commands/auth.py +0 -0
  26. {bonito_cli-0.6.0 → bonito_cli-0.7.0}/bonito_cli/commands/chat.py +0 -0
  27. {bonito_cli-0.6.0 → bonito_cli-0.7.0}/bonito_cli/commands/compliance.py +0 -0
  28. {bonito_cli-0.6.0 → bonito_cli-0.7.0}/bonito_cli/commands/deployments.py +0 -0
  29. {bonito_cli-0.6.0 → bonito_cli-0.7.0}/bonito_cli/commands/gateway.py +0 -0
  30. {bonito_cli-0.6.0 → bonito_cli-0.7.0}/bonito_cli/commands/groups.py +0 -0
  31. {bonito_cli-0.6.0 → bonito_cli-0.7.0}/bonito_cli/commands/kb.py +0 -0
  32. {bonito_cli-0.6.0 → bonito_cli-0.7.0}/bonito_cli/commands/mcp.py +0 -0
  33. {bonito_cli-0.6.0 → bonito_cli-0.7.0}/bonito_cli/commands/memory.py +0 -0
  34. {bonito_cli-0.6.0 → bonito_cli-0.7.0}/bonito_cli/commands/models.py +0 -0
  35. {bonito_cli-0.6.0 → bonito_cli-0.7.0}/bonito_cli/commands/plan.py +0 -0
  36. {bonito_cli-0.6.0 → bonito_cli-0.7.0}/bonito_cli/commands/policies.py +0 -0
  37. {bonito_cli-0.6.0 → bonito_cli-0.7.0}/bonito_cli/commands/projects.py +0 -0
  38. {bonito_cli-0.6.0 → bonito_cli-0.7.0}/bonito_cli/commands/rbac.py +0 -0
  39. {bonito_cli-0.6.0 → bonito_cli-0.7.0}/bonito_cli/commands/routing.py +0 -0
  40. {bonito_cli-0.6.0 → bonito_cli-0.7.0}/bonito_cli/commands/scheduler.py +0 -0
  41. {bonito_cli-0.6.0 → bonito_cli-0.7.0}/bonito_cli/commands/secrets.py +0 -0
  42. {bonito_cli-0.6.0 → bonito_cli-0.7.0}/bonito_cli/commands/sso.py +0 -0
  43. {bonito_cli-0.6.0 → bonito_cli-0.7.0}/bonito_cli/config.py +0 -0
  44. {bonito_cli-0.6.0/bonito_cli/utils → bonito_cli-0.7.0/bonito_cli/examples}/__init__.py +0 -0
  45. {bonito_cli-0.6.0 → bonito_cli-0.7.0}/bonito_cli/utils/auth.py +0 -0
  46. {bonito_cli-0.6.0 → bonito_cli-0.7.0}/bonito_cli/utils/display.py +0 -0
  47. {bonito_cli-0.6.0 → bonito_cli-0.7.0}/bonito_cli/utils/feature_gate.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: bonito-cli
3
- Version: 0.6.0
3
+ Version: 0.7.0
4
4
  Summary: Bonito CLI — Unified multi-cloud AI management from your terminal
5
5
  Author-email: Bonito <hello@getbonito.com>
6
6
  License: MIT
@@ -100,8 +100,8 @@ def _validate(cfg: Dict[str, Any]) -> List[str]:
100
100
  continue
101
101
  if "system_prompt" not in agent_cfg:
102
102
  errors.append(f"agents.{agent_name}: missing 'system_prompt'")
103
- if "model" not in agent_cfg:
104
- errors.append(f"agents.{agent_name}: missing 'model'")
103
+ if "model" not in agent_cfg and "model_id" not in agent_cfg:
104
+ errors.append(f"agents.{agent_name}: missing 'model' (or 'model_id')")
105
105
 
106
106
  return errors
107
107
 
@@ -490,10 +490,10 @@ def _deploy_agents(
490
490
  agent_mcp_refs = cfg.get("mcp_servers", [])
491
491
  agent_delegates = cfg.get("delegates", [])
492
492
  if agent_mcp_refs or agent_delegates:
493
- payload["tool_policy"] = {"mode": "all"}
493
+ payload["tool_policy"] = {"mode": "allowed", "allowed": [], "denied": [], "http_allowlist": []}
494
494
  elif cfg.get("rag"):
495
495
  # RAG agents get search_knowledge_base tool
496
- payload["tool_policy"] = {"mode": "all"}
496
+ payload["tool_policy"] = {"mode": "allowed", "allowed": ["kb_search"], "denied": [], "http_allowlist": []}
497
497
 
498
498
  # Attach knowledge base if configured via RAG section
499
499
  rag = cfg.get("rag", {})
@@ -505,10 +505,7 @@ def _deploy_agents(
505
505
  elif verbose:
506
506
  console.print(f" [dim yellow]! KB '{kb_name}' not found - skipping[/dim yellow]")
507
507
 
508
- # Attach secrets if specified
509
- agent_secrets = cfg.get("secrets", [])
510
- if agent_secrets:
511
- payload["secrets"] = agent_secrets
508
+ # Note: agent secrets are managed via /api/secrets, not in the agent payload
512
509
 
513
510
  # Check if agent already exists — update instead of creating a duplicate
514
511
  existing = existing_agents.get(display_name.lower())
@@ -4,6 +4,7 @@ from __future__ import annotations
4
4
 
5
5
  import json
6
6
  import os
7
+ import shutil
7
8
  import subprocess
8
9
  from pathlib import Path
9
10
  from typing import Dict, List, Optional, Any
@@ -417,4 +418,125 @@ def init_wizard(
417
418
  console.print("• Store your API key: [cyan]export BONITO_API_KEY={api_key}[/cyan]")
418
419
  console.print("• Start chatting: [cyan]bonito chat[/cyan]")
419
420
  console.print("• View usage: [cyan]bonito analytics usage[/cyan]")
420
- console.print("• Manage models: [cyan]bonito models list[/cyan]")
421
+ console.print("• Manage models: [cyan]bonito models list[/cyan]")
422
+
423
+
424
+ # ── Template scaffolding ──────────────────────────────────────
425
+
426
+ _EXAMPLES_DIR = Path(__file__).resolve().parent.parent / "examples"
427
+
428
+ _TEMPLATES = {
429
+ "1": {
430
+ "name": "Quickstart",
431
+ "file": "quickstart.yaml",
432
+ "description": "Single provider (OpenAI) — simplest possible setup",
433
+ },
434
+ "2": {
435
+ "name": "Multi-Provider Failover",
436
+ "file": "multi-provider-failover.yaml",
437
+ "description": "3 providers with failover and cost-optimized routing",
438
+ },
439
+ "3": {
440
+ "name": "Support Agent",
441
+ "file": "support-agent.yaml",
442
+ "description": "Customer support agent with governed tools and KB search",
443
+ },
444
+ "4": {
445
+ "name": "Enterprise",
446
+ "file": "enterprise.yaml",
447
+ "description": "Multi-cloud (AWS/Azure/GCP), failover routing, governed agents",
448
+ },
449
+ }
450
+
451
+
452
+ @app.command("template")
453
+ def init_template(
454
+ output: Optional[Path] = typer.Option(
455
+ None,
456
+ "--output", "-o",
457
+ help="Output file path (default: ./bonito.yaml)",
458
+ ),
459
+ template: Optional[str] = typer.Option(
460
+ None,
461
+ "--template", "-t",
462
+ help="Template number (1-4) for non-interactive use",
463
+ ),
464
+ ):
465
+ """
466
+ Scaffold a bonito.yaml from a starter template.
467
+
468
+ Shows a menu of available templates, copies your pick to the
469
+ current directory, and prints next steps.
470
+
471
+ \b
472
+ Examples:
473
+ bonito init template
474
+ bonito init template -t 1
475
+ bonito init template -t 4 -o infra/bonito.yaml
476
+ """
477
+ dest = output or Path.cwd() / "bonito.yaml"
478
+
479
+ # ── Show menu ─────────────────────────────────────────────
480
+ console.print(Panel(
481
+ "[bold cyan]Bonito Template Scaffolding[/bold cyan]\n\n"
482
+ "Pick a starter template to generate a [bold]bonito.yaml[/bold] file.",
483
+ border_style="cyan",
484
+ ))
485
+
486
+ table = Table(show_header=True, header_style="bold cyan", border_style="dim")
487
+ table.add_column("#", style="bold", width=3)
488
+ table.add_column("Template", style="bold white")
489
+ table.add_column("Description", style="dim")
490
+
491
+ for key, tmpl in _TEMPLATES.items():
492
+ table.add_row(key, tmpl["name"], tmpl["description"])
493
+
494
+ console.print(table)
495
+ console.print()
496
+
497
+ # ── Pick template ─────────────────────────────────────────
498
+ if template is not None:
499
+ choice = template.strip()
500
+ else:
501
+ choice = Prompt.ask(
502
+ "[cyan]Select a template[/cyan]",
503
+ choices=list(_TEMPLATES.keys()),
504
+ default="1",
505
+ )
506
+
507
+ if choice not in _TEMPLATES:
508
+ print_error(f"Invalid template: {choice}. Choose 1-{len(_TEMPLATES)}.")
509
+ raise typer.Exit(1)
510
+
511
+ selected = _TEMPLATES[choice]
512
+ src = _EXAMPLES_DIR / selected["file"]
513
+
514
+ if not src.exists():
515
+ print_error(f"Template file not found: {src}")
516
+ raise typer.Exit(1)
517
+
518
+ # ── Check for existing file ───────────────────────────────
519
+ if dest.exists():
520
+ overwrite = Confirm.ask(
521
+ f"[yellow]{dest} already exists. Overwrite?[/yellow]",
522
+ default=False,
523
+ )
524
+ if not overwrite:
525
+ console.print("[dim]Aborted.[/dim]")
526
+ raise typer.Exit(0)
527
+
528
+ # ── Copy template ─────────────────────────────────────────
529
+ dest.parent.mkdir(parents=True, exist_ok=True)
530
+ shutil.copy2(src, dest)
531
+
532
+ print_success(f"Created {dest} from template [bold]{selected['name']}[/bold]")
533
+
534
+ # ── Next steps ────────────────────────────────────────────
535
+ console.print(Panel(
536
+ f"[bold]Next steps:[/bold]\n\n"
537
+ f" 1. Edit [cyan]{dest}[/cyan] to add credentials or customise\n"
538
+ f" 2. Deploy: [cyan]bonito deploy -f {dest}[/cyan]\n"
539
+ f" 3. Or validate first: [cyan]bonito deploy -f {dest} --dry-run[/cyan]",
540
+ title="[bold green]Ready[/bold green]",
541
+ border_style="green",
542
+ ))
@@ -23,7 +23,11 @@ from ..utils.display import (
23
23
  console = Console()
24
24
  app = typer.Typer(help="☁️ Cloud provider management")
25
25
 
26
- _EMOJI = {"aws": "☁️ ", "azure": "🔷", "gcp": "🔺", "aws_bedrock": "☁️ ", "azure_openai": "🔷", "gcp_vertex": "🔺"}
26
+ _EMOJI = {
27
+ "aws": "☁️ ", "azure": "🔷", "gcp": "🔺",
28
+ "aws_bedrock": "☁️ ", "azure_openai": "🔷", "gcp_vertex": "🔺",
29
+ "openai": "🤖", "anthropic": "🧠", "groq": "⚡", "openrouter": "🔀",
30
+ }
27
31
 
28
32
 
29
33
  # ── list ────────────────────────────────────────────────────────
@@ -215,6 +219,74 @@ def add_gcp(
215
219
  )
216
220
 
217
221
 
222
+ @add_app.command("openai")
223
+ def add_openai(
224
+ api_key: Optional[str] = typer.Option(None, "--api-key", help="OpenAI API key"),
225
+ name: Optional[str] = typer.Option(None, "--name"),
226
+ json_output: bool = typer.Option(False, "--json"),
227
+ ):
228
+ """Connect an OpenAI provider."""
229
+ ensure_authenticated()
230
+ if not api_key:
231
+ api_key = Prompt.ask("OpenAI API Key", password=True)
232
+ _connect_provider(
233
+ {"provider_type": "openai", "name": name or "OpenAI", "credentials": {"api_key": api_key}},
234
+ get_output_format(json_output),
235
+ "OpenAI",
236
+ )
237
+
238
+
239
+ @add_app.command("anthropic")
240
+ def add_anthropic(
241
+ api_key: Optional[str] = typer.Option(None, "--api-key", help="Anthropic API key"),
242
+ name: Optional[str] = typer.Option(None, "--name"),
243
+ json_output: bool = typer.Option(False, "--json"),
244
+ ):
245
+ """Connect an Anthropic provider."""
246
+ ensure_authenticated()
247
+ if not api_key:
248
+ api_key = Prompt.ask("Anthropic API Key", password=True)
249
+ _connect_provider(
250
+ {"provider_type": "anthropic", "name": name or "Anthropic", "credentials": {"api_key": api_key}},
251
+ get_output_format(json_output),
252
+ "Anthropic",
253
+ )
254
+
255
+
256
+ @add_app.command("groq")
257
+ def add_groq(
258
+ api_key: Optional[str] = typer.Option(None, "--api-key", help="Groq API key"),
259
+ name: Optional[str] = typer.Option(None, "--name"),
260
+ json_output: bool = typer.Option(False, "--json"),
261
+ ):
262
+ """Connect a Groq provider."""
263
+ ensure_authenticated()
264
+ if not api_key:
265
+ api_key = Prompt.ask("Groq API Key", password=True)
266
+ _connect_provider(
267
+ {"provider_type": "groq", "name": name or "Groq", "credentials": {"api_key": api_key}},
268
+ get_output_format(json_output),
269
+ "Groq",
270
+ )
271
+
272
+
273
+ @add_app.command("openrouter")
274
+ def add_openrouter(
275
+ api_key: Optional[str] = typer.Option(None, "--api-key", help="OpenRouter API key"),
276
+ name: Optional[str] = typer.Option(None, "--name"),
277
+ json_output: bool = typer.Option(False, "--json"),
278
+ ):
279
+ """Connect an OpenRouter provider."""
280
+ ensure_authenticated()
281
+ if not api_key:
282
+ api_key = Prompt.ask("OpenRouter API Key", password=True)
283
+ _connect_provider(
284
+ {"provider_type": "openrouter", "name": name or "OpenRouter", "credentials": {"api_key": api_key}},
285
+ get_output_format(json_output),
286
+ "OpenRouter",
287
+ )
288
+
289
+
218
290
  app.add_typer(add_app, name="add")
219
291
 
220
292
 
@@ -0,0 +1,67 @@
1
+ version: "1"
2
+ name: enterprise-ai-ops
3
+ description: "Enterprise AI operations — multi-cloud, failover, governed agents"
4
+
5
+ providers:
6
+ - name: aws-bedrock
7
+ type: aws
8
+ - name: azure-openai
9
+ type: azure
10
+ - name: gcp-vertex
11
+ type: gcp
12
+ - name: anthropic-direct
13
+ type: anthropic
14
+
15
+ routing:
16
+ - name: production-failover
17
+ strategy: failover
18
+ targets:
19
+ - provider: aws-bedrock
20
+ model: us.anthropic.claude-sonnet-4-20250514-v1:0
21
+ priority: 1
22
+ - provider: azure-openai
23
+ model: gpt-4o
24
+ priority: 2
25
+ - provider: gcp-vertex
26
+ model: gemini-2.0-flash
27
+ priority: 3
28
+
29
+ - name: internal-cost-optimized
30
+ strategy: cost
31
+ targets:
32
+ - provider: gcp-vertex
33
+ model: gemini-2.0-flash
34
+ priority: 1
35
+ - provider: aws-bedrock
36
+ model: us.amazon.nova-lite-v1:0
37
+ priority: 2
38
+
39
+ agents:
40
+ - name: Tier 1 Support
41
+ model_id: us.anthropic.claude-sonnet-4-20250514-v1:0
42
+ system_prompt: |
43
+ Enterprise support agent. Investigate alerts, follow internal playbooks,
44
+ and remediate issues. Escalate to Tier 2 if unresolved after 2 attempts.
45
+ model_config:
46
+ temperature: 0.2
47
+ max_tokens: 2048
48
+ tools:
49
+ mode: allowlist
50
+ allowed:
51
+ - kb_search
52
+ - http
53
+ http_allowlist:
54
+ - "https://internal-api.company.com/*"
55
+
56
+ - name: Knowledge Assistant
57
+ model_id: gpt-4o
58
+ system_prompt: |
59
+ Internal knowledge assistant. Answer questions about company policies,
60
+ processes, and documentation using the knowledge base.
61
+ model_config:
62
+ temperature: 0.3
63
+ max_tokens: 1024
64
+ tools:
65
+ mode: allowlist
66
+ allowed:
67
+ - kb_search
@@ -0,0 +1,35 @@
1
+ version: "1"
2
+ name: multi-provider
3
+ description: "Multi-provider setup with automatic failover routing"
4
+
5
+ providers:
6
+ - name: openai-primary
7
+ type: openai
8
+ - name: anthropic-backup
9
+ type: anthropic
10
+ - name: groq-fast
11
+ type: groq
12
+
13
+ routing:
14
+ - name: smart-failover
15
+ strategy: failover
16
+ targets:
17
+ - provider: anthropic-backup
18
+ model: anthropic/claude-sonnet-4-20250514
19
+ priority: 1
20
+ - provider: openai-primary
21
+ model: gpt-4o
22
+ priority: 2
23
+ - provider: groq-fast
24
+ model: llama-3.3-70b-versatile
25
+ priority: 3
26
+
27
+ - name: cost-optimized
28
+ strategy: cost
29
+ targets:
30
+ - provider: groq-fast
31
+ model: llama-3.3-70b-versatile
32
+ priority: 1
33
+ - provider: openai-primary
34
+ model: gpt-4o-mini
35
+ priority: 2
@@ -0,0 +1,7 @@
1
+ version: "1"
2
+ name: my-first-bonito
3
+ description: "Quick start — connect OpenAI and start routing"
4
+
5
+ providers:
6
+ - name: openai
7
+ type: openai
@@ -0,0 +1,22 @@
1
+ version: "1"
2
+ name: support-bot
3
+ description: "Customer support agent with governed tools and knowledge base"
4
+
5
+ providers:
6
+ - name: openai
7
+ type: openai
8
+
9
+ agents:
10
+ - name: Customer Support
11
+ model_id: gpt-4o
12
+ system_prompt: |
13
+ You are a helpful customer support agent. Answer questions accurately
14
+ using the knowledge base. If you don't know the answer, escalate to
15
+ a human. Always be polite and concise.
16
+ model_config:
17
+ temperature: 0.3
18
+ max_tokens: 1024
19
+ tools:
20
+ mode: allowlist
21
+ allowed:
22
+ - kb_search
File without changes
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "bonito-cli"
7
- version = "0.6.0"
7
+ version = "0.7.0"
8
8
  description = "Bonito CLI — Unified multi-cloud AI management from your terminal"
9
9
  readme = "README.md"
10
10
  license = {text = "MIT"}
@@ -25,3 +25,4 @@ bonito = "bonito_cli.app:app"
25
25
 
26
26
  [tool.hatch.build.targets.wheel]
27
27
  packages = ["bonito_cli"]
28
+ artifacts = ["bonito_cli/examples/*.yaml"]
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes