buildai-cli 0.3.43__tar.gz → 0.3.44__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 (31) hide show
  1. {buildai_cli-0.3.43 → buildai_cli-0.3.44}/PKG-INFO +1 -1
  2. {buildai_cli-0.3.43 → buildai_cli-0.3.44}/cli/auth_local.py +5 -2
  3. {buildai_cli-0.3.43 → buildai_cli-0.3.44}/cli/commands/auth.py +29 -131
  4. {buildai_cli-0.3.43 → buildai_cli-0.3.44}/cli/main.py +1 -1
  5. {buildai_cli-0.3.43 → buildai_cli-0.3.44}/cli/ops_init.py +17 -0
  6. {buildai_cli-0.3.43 → buildai_cli-0.3.44}/pyproject.toml +1 -1
  7. {buildai_cli-0.3.43 → buildai_cli-0.3.44}/.gitignore +0 -0
  8. {buildai_cli-0.3.43 → buildai_cli-0.3.44}/AGENTS.md +0 -0
  9. {buildai_cli-0.3.43 → buildai_cli-0.3.44}/CLAUDE.md +0 -0
  10. {buildai_cli-0.3.43 → buildai_cli-0.3.44}/buildai_bootstrap.py +0 -0
  11. {buildai_cli-0.3.43 → buildai_cli-0.3.44}/cli/__init__.py +0 -0
  12. {buildai_cli-0.3.43 → buildai_cli-0.3.44}/cli/_has_core.py +0 -0
  13. {buildai_cli-0.3.43 → buildai_cli-0.3.44}/cli/commands/__init__.py +0 -0
  14. {buildai_cli-0.3.43 → buildai_cli-0.3.44}/cli/commands/api_proxy.py +0 -0
  15. {buildai_cli-0.3.43 → buildai_cli-0.3.44}/cli/commands/db/__init__.py +0 -0
  16. {buildai_cli-0.3.43 → buildai_cli-0.3.44}/cli/commands/db/common.py +0 -0
  17. {buildai_cli-0.3.43 → buildai_cli-0.3.44}/cli/commands/db/migrate.py +0 -0
  18. {buildai_cli-0.3.43 → buildai_cli-0.3.44}/cli/commands/db/query.py +0 -0
  19. {buildai_cli-0.3.43 → buildai_cli-0.3.44}/cli/commands/db/schema.py +0 -0
  20. {buildai_cli-0.3.43 → buildai_cli-0.3.44}/cli/commands/db/status.py +0 -0
  21. {buildai_cli-0.3.43 → buildai_cli-0.3.44}/cli/commands/dev.py +0 -0
  22. {buildai_cli-0.3.43 → buildai_cli-0.3.44}/cli/commands/doctor.py +0 -0
  23. {buildai_cli-0.3.43 → buildai_cli-0.3.44}/cli/config.py +0 -0
  24. {buildai_cli-0.3.43 → buildai_cli-0.3.44}/cli/console.py +0 -0
  25. {buildai_cli-0.3.43 → buildai_cli-0.3.44}/cli/context.py +0 -0
  26. {buildai_cli-0.3.43 → buildai_cli-0.3.44}/cli/guard.py +0 -0
  27. {buildai_cli-0.3.43 → buildai_cli-0.3.44}/cli/internal_api.py +0 -0
  28. {buildai_cli-0.3.43 → buildai_cli-0.3.44}/cli/nl_query/__init__.py +0 -0
  29. {buildai_cli-0.3.43 → buildai_cli-0.3.44}/cli/nl_query/dataset_tools.py +0 -0
  30. {buildai_cli-0.3.43 → buildai_cli-0.3.44}/cli/output.py +0 -0
  31. {buildai_cli-0.3.43 → buildai_cli-0.3.44}/cli/pagination.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: buildai-cli
3
- Version: 0.3.43
3
+ Version: 0.3.44
4
4
  Summary: Build AI CLI (Typer)
5
5
  Requires-Python: >=3.11
6
6
  Requires-Dist: python-dotenv>=1.0.0
@@ -200,8 +200,11 @@ def sanctioned_auth_profiles() -> tuple[LocalAuthProfile, ...]:
200
200
  LocalAuthProfile(
201
201
  name="engineers-dev",
202
202
  audience="engineers",
203
- summary="Use the single engineer-facing dev service account for normal local work.",
204
- purpose="Use this for the default combined local shell, local CLI reads, and normal developer workflows.",
203
+ summary="Use the engineer-facing read-only DB and operator lane.",
204
+ purpose=(
205
+ "Use this for direct CLI database reads, schema inspection, and desktop SQL "
206
+ "tooling. Local API runtime parity should use the service-runtime profiles instead."
207
+ ),
205
208
  ),
206
209
  LocalAuthProfile(
207
210
  name="api-runtime-like",
@@ -3,11 +3,8 @@
3
3
  from __future__ import annotations
4
4
 
5
5
  import sys
6
- import time
7
- import webbrowser
8
6
  from typing import Any
9
7
 
10
- import httpx
11
8
  import typer
12
9
  from rich.table import Table
13
10
 
@@ -21,7 +18,7 @@ from cli.auth_local import (
21
18
  sanctioned_auth_profiles,
22
19
  )
23
20
  from cli.config import clear_credential, resolve_api_url, save_cli_config
24
- from cli.console import console, data_console, error, info, status_console, success, warning
21
+ from cli.console import data_console, error, info, status_console, success
25
22
  from cli.internal_api import get_internal_api_client
26
23
  from cli.output import Format, auto_format, format_option, render
27
24
 
@@ -37,57 +34,6 @@ def _api_base_url() -> str:
37
34
  return resolve_api_url()
38
35
 
39
36
 
40
- def _raise_for_status(resp: httpx.Response) -> None:
41
- """Promote API login errors into user-facing Typer validation errors."""
42
-
43
- try:
44
- payload = resp.json()
45
- except Exception:
46
- payload = None
47
-
48
- message = None
49
- if isinstance(payload, dict):
50
- if isinstance(payload.get("detail"), str):
51
- message = payload["detail"]
52
- elif isinstance(payload.get("error"), dict):
53
- message = payload["error"].get("message")
54
- if not message:
55
- message = resp.text or f"HTTP {resp.status_code}"
56
- raise typer.BadParameter(message)
57
-
58
-
59
- def _start_device_login(client: httpx.Client, client_name: str) -> dict[str, Any]:
60
- """Kick off the device-flow login used by the public API credential plane."""
61
-
62
- resp = client.post(
63
- f"{_api_base_url()}/v1/auth/cli/start",
64
- json={"client_name": client_name},
65
- )
66
- if resp.status_code >= 400:
67
- _raise_for_status(resp)
68
- return resp.json()
69
-
70
-
71
- def _poll_device_login(client: httpx.Client, device_code: str) -> dict[str, Any]:
72
- """Poll the public API until the device-flow login is approved or expires."""
73
-
74
- resp = client.get(
75
- f"{_api_base_url()}/v1/auth/cli/poll",
76
- params={"device_code": device_code},
77
- )
78
- if resp.status_code == 429:
79
- retry_after = resp.headers.get("Retry-After")
80
- if retry_after:
81
- try:
82
- time.sleep(max(float(retry_after), 0.0))
83
- except ValueError:
84
- pass
85
- return {"status": "pending"}
86
- if resp.status_code >= 400:
87
- _raise_for_status(resp)
88
- return resp.json()
89
-
90
-
91
37
  def _store_token(token: str, *, profile: str | None = None) -> None:
92
38
  """Persist a public API token in the normal CLI credentials location."""
93
39
 
@@ -95,69 +41,16 @@ def _store_token(token: str, *, profile: str | None = None) -> None:
95
41
  success("Saved CLI credentials to ~/.buildai/credentials.json")
96
42
 
97
43
 
98
- def _device_login(
99
- *,
100
- client_name: str = "buildai-cli",
101
- open_browser: bool = True,
102
- profile: str | None = None,
103
- ) -> None:
104
- """Run the browser-capable device flow used for Build AI API auth."""
105
-
106
- with httpx.Client(timeout=15.0) as client:
107
- try:
108
- start = _start_device_login(client, client_name)
109
- except typer.BadParameter as exc:
110
- error(str(exc))
111
- raise typer.Exit(1) from exc
112
-
113
- verification_url = start["verification_url"]
114
- user_code = start["user_code"]
115
- expires_in = int(start["expires_in"])
116
- poll_interval = max(int(start["poll_interval"]), 1)
117
-
118
- console.print(f" code : [cyan]{user_code}[/cyan]")
119
- console.print(f" verify : [cyan]{verification_url}[/cyan]")
120
- info("Approve this login in the browser, then the CLI will finish automatically.")
121
-
122
- if open_browser:
123
- try:
124
- if webbrowser.open(verification_url):
125
- info("Opened verification URL in your browser.")
126
- else:
127
- warning("Could not open your browser automatically.")
128
- except Exception:
129
- warning("Could not open your browser automatically.")
130
-
131
- deadline = time.monotonic() + expires_in + poll_interval
132
- while time.monotonic() < deadline:
133
- try:
134
- poll = _poll_device_login(client, start["device_code"])
135
- except typer.BadParameter as exc:
136
- error(str(exc))
137
- raise typer.Exit(1) from exc
138
-
139
- status = poll.get("status")
140
- if status == "approved":
141
- token = poll.get("token")
142
- if not token:
143
- error("CLI login completed without a token.")
144
- raise typer.Exit(1)
145
- _store_token(token, profile=profile)
146
- return
147
- if status == "pending":
148
- time.sleep(poll_interval)
149
- continue
150
- if status == "expired":
151
- error("CLI login expired before approval.")
152
- raise typer.Exit(1)
153
- if status == "consumed":
154
- error("CLI login token was already consumed.")
155
- raise typer.Exit(1)
156
- error(f"Unexpected CLI login status: {status!r}")
157
- raise typer.Exit(1)
44
+ def _prompt_for_token() -> str:
45
+ """Prompt for an existing API key instead of pretending a browser flow exists."""
158
46
 
159
- error("CLI login timed out before approval.")
160
- raise typer.Exit(1)
47
+ info("Build AI CLI login stores an existing API key.")
48
+ status_console.print("[dim]Get one from data.build.ai -> Developer -> Create API key.[/dim]")
49
+ value = typer.prompt("Paste Build AI API key", hide_input=True).strip()
50
+ if not value:
51
+ error("Expected a non-empty API key.")
52
+ raise typer.Exit(1)
53
+ return value
161
54
 
162
55
 
163
56
  def _profile_rows() -> list[dict[str, Any]]:
@@ -290,33 +183,38 @@ def _emit_report(
290
183
 
291
184
  @app.command("login")
292
185
  def login(
186
+ api_key: str | None = typer.Option(
187
+ None,
188
+ "--api-key",
189
+ help="Store this API key directly instead of prompting.",
190
+ hidden=True,
191
+ ),
293
192
  token: bool = typer.Option(
294
193
  False,
295
194
  "--token",
296
- help="Read a token from stdin instead of running browser-based device login.",
297
- ),
298
- client_name: str = typer.Option(
299
- "buildai-cli",
300
- "--client-name",
301
- help="Client name sent to the API for device-login requests.",
302
- ),
303
- open_browser: bool = typer.Option(
304
- True,
305
- "--open-browser/--no-open-browser",
306
- help="Open the verification URL in your browser during device login.",
195
+ help="Read an API key from stdin instead of prompting.",
307
196
  ),
308
197
  profile: str | None = typer.Option(None, "--profile", hidden=True),
309
198
  ) -> None:
310
- """Log in to the public API using device flow or a piped CI token."""
199
+ """Store an existing Build AI API key for API-backed CLI commands."""
200
+
201
+ if api_key and token:
202
+ error("Choose either --api-key or --token, not both.")
203
+ raise typer.Exit(1)
204
+
205
+ if api_key:
206
+ _store_token(api_key.strip(), profile=profile)
207
+ return
311
208
 
312
209
  if token:
313
210
  value = sys.stdin.read().strip()
314
211
  if not value:
315
- error("Expected a token on stdin.")
212
+ error("Expected an API key on stdin.")
316
213
  raise typer.Exit(1)
317
214
  _store_token(value, profile=profile)
318
215
  return
319
- _device_login(client_name=client_name, open_browser=open_browser, profile=profile)
216
+
217
+ _store_token(_prompt_for_token(), profile=profile)
320
218
 
321
219
 
322
220
  @app.command("whoami")
@@ -224,7 +224,7 @@ Build AI CLI — auth, dev, and database utilities.
224
224
 
225
225
  Get started:
226
226
  uv tool install buildai-cli Install the standalone CLI
227
- buildai auth login Authenticate (opens browser)
227
+ buildai auth login Paste or pipe an API key
228
228
  buildai auth whoami Inspect API and local auth state
229
229
  buildai dev db info Print the local DB recipe for CLI and desktop tools
230
230
  buildai doctor auth Diagnose local auth before DB work
@@ -43,6 +43,22 @@ def _auth_env_prefix(settings_app_env: object) -> str:
43
43
  return "PREVIEW"
44
44
 
45
45
 
46
+ def _apply_default_db_target(*, env_prefix: str) -> None:
47
+ """Seed the canonical production DB target when the shell provided none.
48
+
49
+ The engineer/operator CLI should not require folklore env setup before it
50
+ can even attempt the sanctioned impersonation lane. Defaulting the DB
51
+ target here keeps `buildai db ...` aligned with the current single-database
52
+ reality while still letting explicit env overrides win.
53
+ """
54
+
55
+ os.environ.setdefault("ALLOYDB_CLUSTER", "buildai-india")
56
+ os.environ.setdefault("ALLOYDB_INSTANCE", "buildai-india-primary")
57
+ os.environ.setdefault("ALLOYDB_REGION", "asia-south1")
58
+ os.environ.setdefault("DB_NAME", "buildai_production")
59
+ os.environ.setdefault(f"ALLOYDB_IAM_AUTH_{env_prefix}", "true")
60
+
61
+
46
62
  def init_ops_context(ctx: typer.Context):
47
63
  """Heavy DB/auth/observability init — called lazily by ops-plane commands.
48
64
 
@@ -95,6 +111,7 @@ def init_ops_context(ctx: typer.Context):
95
111
  # --- resolve auth -------------------------------------------------------
96
112
  auth_info: list[str] = []
97
113
  env_prefix = _auth_env_prefix(settings.app_env)
114
+ _apply_default_db_target(env_prefix=env_prefix)
98
115
 
99
116
  default_user = settings.effective_db_user
100
117
  default_use_iam = settings.effective_use_iam_auth
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "buildai-cli"
7
- version = "0.3.43"
7
+ version = "0.3.44"
8
8
  description = "Build AI CLI (Typer)"
9
9
  requires-python = ">=3.11"
10
10
  dependencies = [
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes