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.
- {buildai_cli-0.3.78 → buildai_cli-0.3.80}/.gitignore +2 -0
- {buildai_cli-0.3.78 → buildai_cli-0.3.80}/PKG-INFO +1 -1
- {buildai_cli-0.3.78 → buildai_cli-0.3.80}/cli/commands/egoexo.py +61 -2
- {buildai_cli-0.3.78 → buildai_cli-0.3.80}/cli/commands/gigcamera.py +158 -1
- {buildai_cli-0.3.78 → buildai_cli-0.3.80}/pyproject.toml +1 -1
- {buildai_cli-0.3.78 → buildai_cli-0.3.80}/AGENTS.md +0 -0
- {buildai_cli-0.3.78 → buildai_cli-0.3.80}/CLAUDE.md +0 -0
- {buildai_cli-0.3.78 → buildai_cli-0.3.80}/buildai_bootstrap.py +0 -0
- {buildai_cli-0.3.78 → buildai_cli-0.3.80}/cli/__init__.py +0 -0
- {buildai_cli-0.3.78 → buildai_cli-0.3.80}/cli/_has_core.py +0 -0
- {buildai_cli-0.3.78 → buildai_cli-0.3.80}/cli/auth_local.py +0 -0
- {buildai_cli-0.3.78 → buildai_cli-0.3.80}/cli/commands/__init__.py +0 -0
- {buildai_cli-0.3.78 → buildai_cli-0.3.80}/cli/commands/api_proxy.py +0 -0
- {buildai_cli-0.3.78 → buildai_cli-0.3.80}/cli/commands/auth.py +0 -0
- {buildai_cli-0.3.78 → buildai_cli-0.3.80}/cli/commands/db/__init__.py +0 -0
- {buildai_cli-0.3.78 → buildai_cli-0.3.80}/cli/commands/db/broker.py +0 -0
- {buildai_cli-0.3.78 → buildai_cli-0.3.80}/cli/commands/db/common.py +0 -0
- {buildai_cli-0.3.78 → buildai_cli-0.3.80}/cli/commands/db/migrate.py +0 -0
- {buildai_cli-0.3.78 → buildai_cli-0.3.80}/cli/commands/db/query.py +0 -0
- {buildai_cli-0.3.78 → buildai_cli-0.3.80}/cli/commands/db/schema.py +0 -0
- {buildai_cli-0.3.78 → buildai_cli-0.3.80}/cli/commands/db/status.py +0 -0
- {buildai_cli-0.3.78 → buildai_cli-0.3.80}/cli/commands/db/tunnel.py +0 -0
- {buildai_cli-0.3.78 → buildai_cli-0.3.80}/cli/commands/dev.py +0 -0
- {buildai_cli-0.3.78 → buildai_cli-0.3.80}/cli/commands/doctor.py +0 -0
- {buildai_cli-0.3.78 → buildai_cli-0.3.80}/cli/commands/processing.py +0 -0
- {buildai_cli-0.3.78 → buildai_cli-0.3.80}/cli/config.py +0 -0
- {buildai_cli-0.3.78 → buildai_cli-0.3.80}/cli/console.py +0 -0
- {buildai_cli-0.3.78 → buildai_cli-0.3.80}/cli/context.py +0 -0
- {buildai_cli-0.3.78 → buildai_cli-0.3.80}/cli/db_broker.py +0 -0
- {buildai_cli-0.3.78 → buildai_cli-0.3.80}/cli/guard.py +0 -0
- {buildai_cli-0.3.78 → buildai_cli-0.3.80}/cli/internal_api.py +0 -0
- {buildai_cli-0.3.78 → buildai_cli-0.3.80}/cli/main.py +0 -0
- {buildai_cli-0.3.78 → buildai_cli-0.3.80}/cli/nl_query/__init__.py +0 -0
- {buildai_cli-0.3.78 → buildai_cli-0.3.80}/cli/nl_query/dataset_tools.py +0 -0
- {buildai_cli-0.3.78 → buildai_cli-0.3.80}/cli/ops_init.py +0 -0
- {buildai_cli-0.3.78 → buildai_cli-0.3.80}/cli/output.py +0 -0
- {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/
|
|
@@ -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(
|
|
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(
|
|
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,
|
|
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
|