buildai-cli 0.3.78__tar.gz → 0.3.80__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 (37) hide show
  1. {buildai_cli-0.3.78 → buildai_cli-0.3.80}/.gitignore +2 -0
  2. {buildai_cli-0.3.78 → buildai_cli-0.3.80}/PKG-INFO +1 -1
  3. {buildai_cli-0.3.78 → buildai_cli-0.3.80}/cli/commands/egoexo.py +61 -2
  4. {buildai_cli-0.3.78 → buildai_cli-0.3.80}/cli/commands/gigcamera.py +158 -1
  5. {buildai_cli-0.3.78 → buildai_cli-0.3.80}/pyproject.toml +1 -1
  6. {buildai_cli-0.3.78 → buildai_cli-0.3.80}/AGENTS.md +0 -0
  7. {buildai_cli-0.3.78 → buildai_cli-0.3.80}/CLAUDE.md +0 -0
  8. {buildai_cli-0.3.78 → buildai_cli-0.3.80}/buildai_bootstrap.py +0 -0
  9. {buildai_cli-0.3.78 → buildai_cli-0.3.80}/cli/__init__.py +0 -0
  10. {buildai_cli-0.3.78 → buildai_cli-0.3.80}/cli/_has_core.py +0 -0
  11. {buildai_cli-0.3.78 → buildai_cli-0.3.80}/cli/auth_local.py +0 -0
  12. {buildai_cli-0.3.78 → buildai_cli-0.3.80}/cli/commands/__init__.py +0 -0
  13. {buildai_cli-0.3.78 → buildai_cli-0.3.80}/cli/commands/api_proxy.py +0 -0
  14. {buildai_cli-0.3.78 → buildai_cli-0.3.80}/cli/commands/auth.py +0 -0
  15. {buildai_cli-0.3.78 → buildai_cli-0.3.80}/cli/commands/db/__init__.py +0 -0
  16. {buildai_cli-0.3.78 → buildai_cli-0.3.80}/cli/commands/db/broker.py +0 -0
  17. {buildai_cli-0.3.78 → buildai_cli-0.3.80}/cli/commands/db/common.py +0 -0
  18. {buildai_cli-0.3.78 → buildai_cli-0.3.80}/cli/commands/db/migrate.py +0 -0
  19. {buildai_cli-0.3.78 → buildai_cli-0.3.80}/cli/commands/db/query.py +0 -0
  20. {buildai_cli-0.3.78 → buildai_cli-0.3.80}/cli/commands/db/schema.py +0 -0
  21. {buildai_cli-0.3.78 → buildai_cli-0.3.80}/cli/commands/db/status.py +0 -0
  22. {buildai_cli-0.3.78 → buildai_cli-0.3.80}/cli/commands/db/tunnel.py +0 -0
  23. {buildai_cli-0.3.78 → buildai_cli-0.3.80}/cli/commands/dev.py +0 -0
  24. {buildai_cli-0.3.78 → buildai_cli-0.3.80}/cli/commands/doctor.py +0 -0
  25. {buildai_cli-0.3.78 → buildai_cli-0.3.80}/cli/commands/processing.py +0 -0
  26. {buildai_cli-0.3.78 → buildai_cli-0.3.80}/cli/config.py +0 -0
  27. {buildai_cli-0.3.78 → buildai_cli-0.3.80}/cli/console.py +0 -0
  28. {buildai_cli-0.3.78 → buildai_cli-0.3.80}/cli/context.py +0 -0
  29. {buildai_cli-0.3.78 → buildai_cli-0.3.80}/cli/db_broker.py +0 -0
  30. {buildai_cli-0.3.78 → buildai_cli-0.3.80}/cli/guard.py +0 -0
  31. {buildai_cli-0.3.78 → buildai_cli-0.3.80}/cli/internal_api.py +0 -0
  32. {buildai_cli-0.3.78 → buildai_cli-0.3.80}/cli/main.py +0 -0
  33. {buildai_cli-0.3.78 → buildai_cli-0.3.80}/cli/nl_query/__init__.py +0 -0
  34. {buildai_cli-0.3.78 → buildai_cli-0.3.80}/cli/nl_query/dataset_tools.py +0 -0
  35. {buildai_cli-0.3.78 → buildai_cli-0.3.80}/cli/ops_init.py +0 -0
  36. {buildai_cli-0.3.78 → buildai_cli-0.3.80}/cli/output.py +0 -0
  37. {buildai_cli-0.3.78 → buildai_cli-0.3.80}/cli/pagination.py +0 -0
@@ -125,6 +125,8 @@ scripts/atlas/
125
125
  scripts/dead_assets.json
126
126
  scripts/dead_assets.ids.txt
127
127
  scripts/figure_h264_1000h/
128
+ /apps/gigcamera/public/prototypes/
129
+ /apps/gigcamera/.local-prototypes/
128
130
 
129
131
  # Terraform/Terragrunt local artifacts
130
132
  **/.terragrunt-cache/
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: buildai-cli
3
- Version: 0.3.78
3
+ Version: 0.3.80
4
4
  Summary: Build AI CLI (Typer)
5
5
  Requires-Python: >=3.11
6
6
  Requires-Dist: httpx>=0.27.0
@@ -52,9 +52,13 @@ def _request(path: str, *, query_params: list[str] | None = None) -> None:
52
52
  @app.command("recording-disposition")
53
53
  def recording_disposition(
54
54
  recording_id: str = typer.Argument(..., help="media.recordings id."),
55
+ limit: int = typer.Option(50, "--limit", min=1, max=100, help="Maximum rows."),
55
56
  ) -> None:
56
57
  """Show why a recording did or did not land in an EgoExo episode."""
57
- _request(f"/v1/dashboard/egoexo/recordings/{recording_id}/disposition")
58
+ _request(
59
+ f"/v1/dashboard/egoexo/recordings/{recording_id}/disposition",
60
+ query_params=[f"limit={limit}"],
61
+ )
58
62
 
59
63
 
60
64
  @app.command("overview")
@@ -66,9 +70,64 @@ def overview() -> None:
66
70
  @app.command("recording-history")
67
71
  def recording_history(
68
72
  recording_id: str = typer.Argument(..., help="media.recordings id."),
73
+ limit: int = typer.Option(100, "--limit", min=1, max=200, help="Maximum outcome rows."),
69
74
  ) -> None:
70
75
  """Show disposition plus recording-grain verdict outcomes for one recording."""
71
- _request(f"/v1/dashboard/egoexo/recordings/{recording_id}/history")
76
+ _request(
77
+ f"/v1/dashboard/egoexo/recordings/{recording_id}/history",
78
+ query_params=[f"limit={limit}"],
79
+ )
80
+
81
+
82
+ @app.command("batch-history")
83
+ def batch_history(
84
+ batch_id: str = typer.Argument(..., help="ingest.recording_batches id."),
85
+ limit: int = typer.Option(100, "--limit", min=1, max=200, help="Maximum outcome rows."),
86
+ ) -> None:
87
+ """Show disposition and batch-grain verdict outcomes for one batch."""
88
+ _request(
89
+ f"/v1/dashboard/egoexo/batches/{batch_id}/history",
90
+ query_params=[f"limit={limit}"],
91
+ )
92
+
93
+
94
+ @app.command("episode-history")
95
+ def episode_history(
96
+ episode_id: str = typer.Argument(..., help="media.episodes id."),
97
+ limit: int = typer.Option(100, "--limit", min=1, max=200, help="Maximum outcome rows."),
98
+ ) -> None:
99
+ """Show episode-grain verdict outcomes plus final dispositions."""
100
+ _request(
101
+ f"/v1/dashboard/egoexo/episodes/{episode_id}/verdict-history",
102
+ query_params=[f"limit={limit}"],
103
+ )
104
+
105
+
106
+ @app.command("events")
107
+ def events(
108
+ limit: int = typer.Option(50, "--limit", min=1, max=200, help="Maximum outcome rows."),
109
+ table_name: str | None = typer.Option(None, "--table", help="Outcome table filter."),
110
+ stage: str | None = typer.Option(None, "--stage", help="Stage filter."),
111
+ status: str | None = typer.Option(None, "--status", help="Status filter."),
112
+ reason: str | None = typer.Option(None, "--reason", help="Reason or failure-class filter."),
113
+ batch_id: str | None = typer.Option(None, "--batch-id", help="Batch id filter."),
114
+ episode_id: str | None = typer.Option(None, "--episode-id", help="Episode id filter."),
115
+ recording_id: str | None = typer.Option(None, "--recording-id", help="Recording id filter."),
116
+ ) -> None:
117
+ """Show a bounded verdict-plane event log for investigation."""
118
+ query_params = [f"limit={limit}"]
119
+ for key, value in [
120
+ ("table_name", table_name),
121
+ ("stage", stage),
122
+ ("status", status),
123
+ ("reason", reason),
124
+ ("batch_id", batch_id),
125
+ ("episode_id", episode_id),
126
+ ("recording_id", recording_id),
127
+ ]:
128
+ if value:
129
+ query_params.append(f"{key}={value}")
130
+ _request("/v1/dashboard/egoexo/verdict/events", query_params=query_params)
72
131
 
73
132
 
74
133
  @app.command("runtime-errors")
@@ -4,11 +4,12 @@ from __future__ import annotations
4
4
 
5
5
  import asyncio
6
6
  import hashlib
7
+ import json
7
8
  from dataclasses import asdict, is_dataclass
8
9
  from datetime import date
9
10
  from decimal import Decimal
10
11
  from time import monotonic
11
- from typing import Literal, cast
12
+ from typing import Any, Literal, cast
12
13
  from uuid import UUID
13
14
 
14
15
  import typer
@@ -157,6 +158,20 @@ def _renderable_result(value: object) -> object:
157
158
  return value
158
159
 
159
160
 
161
+ def _parse_json_object(value: str, *, option_name: str) -> dict[str, Any]:
162
+ """Parse a JSON object option while returning a clear CLI error."""
163
+
164
+ try:
165
+ parsed = json.loads(value)
166
+ except json.JSONDecodeError as exc:
167
+ error(f"{option_name} must be valid JSON: {exc}")
168
+ raise typer.Exit(1) from exc
169
+ if not isinstance(parsed, dict):
170
+ error(f"{option_name} must decode to a JSON object.")
171
+ raise typer.Exit(1)
172
+ return parsed
173
+
174
+
160
175
  def _parse_ist_day(value: str, *, field_name: str) -> date:
161
176
  """Parse one ISO calendar day supplied as a GigCamera IST boundary."""
162
177
 
@@ -173,6 +188,148 @@ def _idempotency_key(base: str, *, run_suffix: str | None) -> str:
173
188
  return f"{base}:{suffix}" if suffix else base
174
189
 
175
190
 
191
+ @app.command("daily-quest-config-show")
192
+ def daily_quest_config_show(
193
+ ctx: typer.Context,
194
+ config_key: str = typer.Option(
195
+ "gigcamera_daily_quest_v1",
196
+ "--config-key",
197
+ help="Config key to inspect.",
198
+ ),
199
+ version: int | None = typer.Option(
200
+ None,
201
+ "--version",
202
+ min=1,
203
+ help="Exact config version. Omit to resolve the active rollout config.",
204
+ ),
205
+ cohort_key: str | None = typer.Option(
206
+ None,
207
+ "--cohort-key",
208
+ help="Optional rollout cohort key used for active resolution.",
209
+ ),
210
+ experiment_key: str | None = typer.Option(
211
+ None,
212
+ "--experiment-key",
213
+ help="Optional experiment key used for active resolution.",
214
+ ),
215
+ variant_key: str | None = typer.Option(
216
+ None,
217
+ "--variant-key",
218
+ help="Optional variant key used for active resolution.",
219
+ ),
220
+ format: Format = format_option(),
221
+ ) -> None:
222
+ """Inspect Daily Quest config through the same DAL read path used by runtime code."""
223
+
224
+ settings = _settings_for_command(ctx, write=False)
225
+
226
+ async def run() -> None:
227
+ from dal.gigcamera import daily_quest_config as daily_quest_config_dal
228
+
229
+ async with get_cli_context(settings, profile=(ctx.obj or {}).get("cli_profile")) as (
230
+ _db,
231
+ dal_ctx,
232
+ ):
233
+ if version is None:
234
+ config = await daily_quest_config_dal.get_active_config(
235
+ dal_ctx,
236
+ config_key=config_key,
237
+ cohort_key=cohort_key,
238
+ experiment_key=experiment_key,
239
+ variant_key=variant_key,
240
+ )
241
+ mode = "active"
242
+ else:
243
+ config = await daily_quest_config_dal.get_config(
244
+ dal_ctx,
245
+ config_key=config_key,
246
+ version=version,
247
+ )
248
+ mode = "version"
249
+
250
+ render({"mode": mode, "config": config}, format=format)
251
+
252
+ asyncio.run(run())
253
+
254
+
255
+ @app.command("daily-quest-config-update")
256
+ def daily_quest_config_update(
257
+ ctx: typer.Context,
258
+ config_key: str = typer.Option(
259
+ "gigcamera_daily_quest_v1",
260
+ "--config-key",
261
+ help="Config key to update.",
262
+ ),
263
+ version: int = typer.Option(1, "--version", min=1, help="Config version to update."),
264
+ patch_json: str = typer.Option(
265
+ ...,
266
+ "--patch-json",
267
+ help="JSON object of config fields to change. Use decimal strings for money values.",
268
+ ),
269
+ reason: str = typer.Option(
270
+ ...,
271
+ "--reason",
272
+ help="Human-readable rollout/audit reason for this config change.",
273
+ ),
274
+ write: bool = typer.Option(False, "--write", help="Persist the update and append audit."),
275
+ format: Format = format_option(),
276
+ ) -> None:
277
+ """Preview or apply a Daily Quest config patch without redeploying services."""
278
+
279
+ settings = _settings_for_command(ctx, write=write)
280
+ patch = _parse_json_object(patch_json, option_name="--patch-json")
281
+ _require_internal_admin_for_write(ctx, write=write)
282
+
283
+ async def run() -> None:
284
+ from dal.gigcamera import daily_quest_config as daily_quest_config_dal
285
+
286
+ async with get_cli_context(settings, profile=(ctx.obj or {}).get("cli_profile")) as (
287
+ _db,
288
+ dal_ctx,
289
+ ):
290
+ before = await daily_quest_config_dal.get_config(
291
+ dal_ctx,
292
+ config_key=config_key,
293
+ version=version,
294
+ )
295
+ if before is None:
296
+ raise LookupError(f"Daily Quest config {config_key} v{version} does not exist")
297
+
298
+ if write:
299
+ async with dal_ctx.transaction():
300
+ after = await daily_quest_config_dal.update_config(
301
+ dal_ctx,
302
+ config_key=config_key,
303
+ version=version,
304
+ patch=patch,
305
+ reason=reason,
306
+ )
307
+ else:
308
+ after = await daily_quest_config_dal.preview_config_update(
309
+ dal_ctx,
310
+ config_key=config_key,
311
+ version=version,
312
+ patch=patch,
313
+ )
314
+
315
+ render(
316
+ {
317
+ "dry_run": not write,
318
+ "config_key": config_key,
319
+ "version": version,
320
+ "reason": reason,
321
+ "before": before,
322
+ "after": after,
323
+ "next_step": None
324
+ if write
325
+ else "rerun with --write --profile internal_admin to persist",
326
+ },
327
+ format=format,
328
+ )
329
+
330
+ asyncio.run(run())
331
+
332
+
176
333
  @app.command("admin-analytics-read-model-refresh")
177
334
  def admin_analytics_read_model_refresh(
178
335
  ctx: typer.Context,
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "buildai-cli"
7
- version = "0.3.78"
7
+ version = "0.3.80"
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