arga-cli 0.1.2__tar.gz → 0.1.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.
- {arga_cli-0.1.2 → arga_cli-0.1.4}/PKG-INFO +2 -1
- {arga_cli-0.1.2 → arga_cli-0.1.4}/README.md +1 -0
- {arga_cli-0.1.2 → arga_cli-0.1.4}/arga_cli/main.py +54 -0
- {arga_cli-0.1.2 → arga_cli-0.1.4}/arga_cli.egg-info/PKG-INFO +2 -1
- {arga_cli-0.1.2 → arga_cli-0.1.4}/pyproject.toml +1 -1
- {arga_cli-0.1.2 → arga_cli-0.1.4}/tests/test_cli_runs.py +111 -0
- {arga_cli-0.1.2 → arga_cli-0.1.4}/tests/test_cli_validate_config.py +14 -0
- {arga_cli-0.1.2 → arga_cli-0.1.4}/arga_cli/__init__.py +0 -0
- {arga_cli-0.1.2 → arga_cli-0.1.4}/arga_cli/mcp.py +0 -0
- {arga_cli-0.1.2 → arga_cli-0.1.4}/arga_cli.egg-info/SOURCES.txt +0 -0
- {arga_cli-0.1.2 → arga_cli-0.1.4}/arga_cli.egg-info/dependency_links.txt +0 -0
- {arga_cli-0.1.2 → arga_cli-0.1.4}/arga_cli.egg-info/entry_points.txt +0 -0
- {arga_cli-0.1.2 → arga_cli-0.1.4}/arga_cli.egg-info/requires.txt +0 -0
- {arga_cli-0.1.2 → arga_cli-0.1.4}/arga_cli.egg-info/top_level.txt +0 -0
- {arga_cli-0.1.2 → arga_cli-0.1.4}/setup.cfg +0 -0
- {arga_cli-0.1.2 → arga_cli-0.1.4}/tests/test_cli_git.py +0 -0
- {arga_cli-0.1.2 → arga_cli-0.1.4}/tests/test_cli_mcp.py +0 -0
- {arga_cli-0.1.2 → arga_cli-0.1.4}/tests/test_cli_scan.py +0 -0
- {arga_cli-0.1.2 → arga_cli-0.1.4}/tests/test_cli_test_url.py +0 -0
- {arga_cli-0.1.2 → arga_cli-0.1.4}/tests/test_cli_validate_pr.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: arga-cli
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.4
|
|
4
4
|
Summary: Command-line interface for Arga authentication, MCP installation, and browser validation
|
|
5
5
|
Author: Arga Labs
|
|
6
6
|
Project-URL: Homepage, https://github.com/ArgaLabs/arga-cli
|
|
@@ -198,6 +198,7 @@ arga runs cancel <run_id>
|
|
|
198
198
|
- `arga runs logs <run_id>` prints worker logs plus recent runtime logs for a run you own.
|
|
199
199
|
- When you omit `<run_id>`, `arga runs logs` falls back to `./.arga-session.json` when present, which makes wizard-created twin sessions easy to inspect from the same directory.
|
|
200
200
|
- Add `--json` to `arga runs logs` for a machine-readable response.
|
|
201
|
+
- Add `--errors-only` to keep only failed worker logs plus warning/error runtime entries.
|
|
201
202
|
- `arga runs cancel <run_id>` cancels the run through the validation API.
|
|
202
203
|
|
|
203
204
|
### Git Wrappers
|
|
@@ -178,6 +178,7 @@ arga runs cancel <run_id>
|
|
|
178
178
|
- `arga runs logs <run_id>` prints worker logs plus recent runtime logs for a run you own.
|
|
179
179
|
- When you omit `<run_id>`, `arga runs logs` falls back to `./.arga-session.json` when present, which makes wizard-created twin sessions easy to inspect from the same directory.
|
|
180
180
|
- Add `--json` to `arga runs logs` for a machine-readable response.
|
|
181
|
+
- Add `--errors-only` to keep only failed worker logs plus warning/error runtime entries.
|
|
181
182
|
- `arga runs cancel <run_id>` cancels the run through the validation API.
|
|
182
183
|
|
|
183
184
|
### Git Wrappers
|
|
@@ -10,6 +10,7 @@ import tempfile
|
|
|
10
10
|
import time
|
|
11
11
|
import webbrowser
|
|
12
12
|
from datetime import datetime
|
|
13
|
+
from importlib.metadata import PackageNotFoundError, version
|
|
13
14
|
from pathlib import Path
|
|
14
15
|
from typing import Any
|
|
15
16
|
from urllib.parse import urlencode
|
|
@@ -27,6 +28,13 @@ POLL_TIMEOUT_SECONDS = 600.0
|
|
|
27
28
|
SKIP_TRAILER = "[skip arga]"
|
|
28
29
|
|
|
29
30
|
|
|
31
|
+
def _cli_version() -> str:
|
|
32
|
+
try:
|
|
33
|
+
return version("arga-cli")
|
|
34
|
+
except PackageNotFoundError:
|
|
35
|
+
return "unknown"
|
|
36
|
+
|
|
37
|
+
|
|
30
38
|
class CliError(Exception):
|
|
31
39
|
"""Base CLI error."""
|
|
32
40
|
|
|
@@ -905,6 +913,40 @@ def _print_runtime_logs(runtime_logs: list[dict[str, Any]]) -> None:
|
|
|
905
913
|
print()
|
|
906
914
|
|
|
907
915
|
|
|
916
|
+
def _is_error_runtime_log(runtime_log: dict[str, Any]) -> bool:
|
|
917
|
+
severity = str(runtime_log.get("severity") or "").strip().upper()
|
|
918
|
+
return severity in {"WARNING", "ERROR", "CRITICAL", "ALERT", "EMERGENCY"}
|
|
919
|
+
|
|
920
|
+
|
|
921
|
+
def _is_error_worker_log(worker_log: dict[str, Any]) -> bool:
|
|
922
|
+
status = str(worker_log.get("status") or "").strip().lower()
|
|
923
|
+
error = str(worker_log.get("error") or "").strip()
|
|
924
|
+
return status in {"failed", "error", "cancelled"} or bool(error)
|
|
925
|
+
|
|
926
|
+
|
|
927
|
+
def _filter_run_logs_payload(payload: dict[str, Any], *, errors_only: bool) -> dict[str, Any]:
|
|
928
|
+
if not errors_only:
|
|
929
|
+
return payload
|
|
930
|
+
|
|
931
|
+
filtered_payload = dict(payload)
|
|
932
|
+
worker_logs = payload.get("worker_logs")
|
|
933
|
+
runtime_logs = payload.get("runtime_logs")
|
|
934
|
+
warnings = payload.get("warnings")
|
|
935
|
+
|
|
936
|
+
filtered_payload["worker_logs"] = (
|
|
937
|
+
[item for item in worker_logs if isinstance(item, dict) and _is_error_worker_log(item)]
|
|
938
|
+
if isinstance(worker_logs, list)
|
|
939
|
+
else []
|
|
940
|
+
)
|
|
941
|
+
filtered_payload["runtime_logs"] = (
|
|
942
|
+
[item for item in runtime_logs if isinstance(item, dict) and _is_error_runtime_log(item)]
|
|
943
|
+
if isinstance(runtime_logs, list)
|
|
944
|
+
else []
|
|
945
|
+
)
|
|
946
|
+
filtered_payload["warnings"] = warnings if isinstance(warnings, list) else []
|
|
947
|
+
return filtered_payload
|
|
948
|
+
|
|
949
|
+
|
|
908
950
|
def _print_run_logs(payload: dict[str, Any], fallback_run_id: str) -> None:
|
|
909
951
|
run = payload.get("run")
|
|
910
952
|
run_data = run if isinstance(run, dict) else {}
|
|
@@ -996,6 +1038,8 @@ def run_runs_logs(args: argparse.Namespace) -> int:
|
|
|
996
1038
|
finally:
|
|
997
1039
|
client.close()
|
|
998
1040
|
|
|
1041
|
+
payload = _filter_run_logs_payload(payload, errors_only=args.errors_only)
|
|
1042
|
+
|
|
999
1043
|
if args.json:
|
|
1000
1044
|
print(json.dumps(payload, indent=2))
|
|
1001
1045
|
return 0
|
|
@@ -1204,6 +1248,11 @@ def run_wizard(args: argparse.Namespace) -> int:
|
|
|
1204
1248
|
|
|
1205
1249
|
def build_parser() -> argparse.ArgumentParser:
|
|
1206
1250
|
parser = argparse.ArgumentParser(prog="arga")
|
|
1251
|
+
parser.add_argument(
|
|
1252
|
+
"--version",
|
|
1253
|
+
action="version",
|
|
1254
|
+
version=f"%(prog)s {_cli_version()}",
|
|
1255
|
+
)
|
|
1207
1256
|
subparsers = parser.add_subparsers(dest="command", required=True)
|
|
1208
1257
|
|
|
1209
1258
|
login_parser = subparsers.add_parser("login", help="Authenticate the CLI")
|
|
@@ -1291,6 +1340,11 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
1291
1340
|
help=f"Validation run ID. Defaults to {WIZARD_SESSION_FILE} in the current directory when available.",
|
|
1292
1341
|
)
|
|
1293
1342
|
runs_logs_parser.add_argument("--json", action="store_true", help="Print the raw JSON response")
|
|
1343
|
+
runs_logs_parser.add_argument(
|
|
1344
|
+
"--errors-only",
|
|
1345
|
+
action="store_true",
|
|
1346
|
+
help="Show only failed worker logs and warning/error runtime logs",
|
|
1347
|
+
)
|
|
1294
1348
|
runs_logs_parser.set_defaults(func=run_runs_logs)
|
|
1295
1349
|
|
|
1296
1350
|
runs_cancel_parser = runs_subparsers.add_parser("cancel", help="Cancel a validation run")
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: arga-cli
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.4
|
|
4
4
|
Summary: Command-line interface for Arga authentication, MCP installation, and browser validation
|
|
5
5
|
Author: Arga Labs
|
|
6
6
|
Project-URL: Homepage, https://github.com/ArgaLabs/arga-cli
|
|
@@ -198,6 +198,7 @@ arga runs cancel <run_id>
|
|
|
198
198
|
- `arga runs logs <run_id>` prints worker logs plus recent runtime logs for a run you own.
|
|
199
199
|
- When you omit `<run_id>`, `arga runs logs` falls back to `./.arga-session.json` when present, which makes wizard-created twin sessions easy to inspect from the same directory.
|
|
200
200
|
- Add `--json` to `arga runs logs` for a machine-readable response.
|
|
201
|
+
- Add `--errors-only` to keep only failed worker logs plus warning/error runtime entries.
|
|
201
202
|
- `arga runs cancel <run_id>` cancels the run through the validation API.
|
|
202
203
|
|
|
203
204
|
### Git Wrappers
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "arga-cli"
|
|
7
|
-
version = "0.1.
|
|
7
|
+
version = "0.1.4"
|
|
8
8
|
description = "Command-line interface for Arga authentication, MCP installation, and browser validation"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.12"
|
|
@@ -293,6 +293,117 @@ def test_runs_logs_prints_json(monkeypatch, capsys) -> None:
|
|
|
293
293
|
assert json.loads(output) == payload
|
|
294
294
|
|
|
295
295
|
|
|
296
|
+
def test_runs_logs_errors_only_filters_plain_output(monkeypatch, capsys) -> None:
|
|
297
|
+
monkeypatch.setattr(main, "load_api_key", lambda: "arga_api_key")
|
|
298
|
+
monkeypatch.setattr(main.ApiClient, "close", lambda self: None)
|
|
299
|
+
|
|
300
|
+
def fake_get_logs(self, run_id: str):
|
|
301
|
+
assert run_id == "run_123"
|
|
302
|
+
return {
|
|
303
|
+
"run": {
|
|
304
|
+
"id": run_id,
|
|
305
|
+
"status": "ready",
|
|
306
|
+
"run_type": "twin_quickstart",
|
|
307
|
+
"mode": "staging",
|
|
308
|
+
"repo_full_name": None,
|
|
309
|
+
"commit_sha": None,
|
|
310
|
+
"created_at": "2026-03-25T12:30:00Z",
|
|
311
|
+
"environment_url": "https://preview.example.com",
|
|
312
|
+
"event_log_json": [],
|
|
313
|
+
},
|
|
314
|
+
"worker_logs": [
|
|
315
|
+
{
|
|
316
|
+
"job_id": "job_ok",
|
|
317
|
+
"job_type": "build",
|
|
318
|
+
"target_role": "builder",
|
|
319
|
+
"status": "succeeded",
|
|
320
|
+
"content": "all good",
|
|
321
|
+
"truncated": False,
|
|
322
|
+
"error": None,
|
|
323
|
+
},
|
|
324
|
+
{
|
|
325
|
+
"job_id": "job_bad",
|
|
326
|
+
"job_type": "deploy",
|
|
327
|
+
"target_role": "warm-vm",
|
|
328
|
+
"status": "failed",
|
|
329
|
+
"content": "deploy failed output",
|
|
330
|
+
"truncated": False,
|
|
331
|
+
"error": None,
|
|
332
|
+
},
|
|
333
|
+
],
|
|
334
|
+
"runtime_logs": [
|
|
335
|
+
{
|
|
336
|
+
"timestamp": "2026-03-25T12:31:00Z",
|
|
337
|
+
"service_name": "arga-api",
|
|
338
|
+
"severity": "INFO",
|
|
339
|
+
"event": "environment_ready",
|
|
340
|
+
"code": None,
|
|
341
|
+
"request_id": "req_ok",
|
|
342
|
+
"job_id": None,
|
|
343
|
+
"surface_name": None,
|
|
344
|
+
"message": "Environment ready.",
|
|
345
|
+
},
|
|
346
|
+
{
|
|
347
|
+
"timestamp": "2026-03-25T12:32:00Z",
|
|
348
|
+
"service_name": "preview-proxy",
|
|
349
|
+
"severity": "WARNING",
|
|
350
|
+
"event": "preview.request.finish",
|
|
351
|
+
"code": None,
|
|
352
|
+
"request_id": "req_warn",
|
|
353
|
+
"job_id": None,
|
|
354
|
+
"surface_name": "slack",
|
|
355
|
+
"message": "Preview request completed.",
|
|
356
|
+
},
|
|
357
|
+
],
|
|
358
|
+
"warnings": ["Cloud Logging query was partially truncated."],
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
monkeypatch.setattr(main.ApiClient, "get_run_logs", fake_get_logs)
|
|
362
|
+
|
|
363
|
+
args = main.build_parser().parse_args(["runs", "logs", "run_123", "--errors-only"])
|
|
364
|
+
exit_code = args.func(args)
|
|
365
|
+
output = capsys.readouterr().out
|
|
366
|
+
|
|
367
|
+
assert exit_code == 0
|
|
368
|
+
assert "job_bad" in output
|
|
369
|
+
assert "deploy failed output" in output
|
|
370
|
+
assert "job_ok" not in output
|
|
371
|
+
assert "all good" not in output
|
|
372
|
+
assert "Preview request completed." in output
|
|
373
|
+
assert "Environment ready." not in output
|
|
374
|
+
assert "Warnings:" in output
|
|
375
|
+
|
|
376
|
+
|
|
377
|
+
def test_runs_logs_errors_only_filters_json(monkeypatch, capsys) -> None:
|
|
378
|
+
monkeypatch.setattr(main, "load_api_key", lambda: "arga_api_key")
|
|
379
|
+
monkeypatch.setattr(main.ApiClient, "close", lambda self: None)
|
|
380
|
+
|
|
381
|
+
payload = {
|
|
382
|
+
"run": {"id": "run_123", "status": "ready"},
|
|
383
|
+
"worker_logs": [
|
|
384
|
+
{"job_id": "job_ok", "status": "succeeded", "error": None},
|
|
385
|
+
{"job_id": "job_bad", "status": "failed", "error": None},
|
|
386
|
+
],
|
|
387
|
+
"runtime_logs": [
|
|
388
|
+
{"severity": "INFO", "message": "Environment ready."},
|
|
389
|
+
{"severity": "WARNING", "message": "Preview request completed."},
|
|
390
|
+
],
|
|
391
|
+
"warnings": ["Cloud Logging query was partially truncated."],
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
monkeypatch.setattr(main.ApiClient, "get_run_logs", lambda self, run_id: payload)
|
|
395
|
+
|
|
396
|
+
args = main.build_parser().parse_args(["runs", "logs", "run_123", "--json", "--errors-only"])
|
|
397
|
+
exit_code = args.func(args)
|
|
398
|
+
output = capsys.readouterr().out
|
|
399
|
+
|
|
400
|
+
assert exit_code == 0
|
|
401
|
+
parsed = json.loads(output)
|
|
402
|
+
assert parsed["worker_logs"] == [{"job_id": "job_bad", "status": "failed", "error": None}]
|
|
403
|
+
assert parsed["runtime_logs"] == [{"severity": "WARNING", "message": "Preview request completed."}]
|
|
404
|
+
assert parsed["warnings"] == ["Cloud Logging query was partially truncated."]
|
|
405
|
+
|
|
406
|
+
|
|
296
407
|
def test_runs_logs_uses_wizard_session_file_when_run_id_missing(monkeypatch, capsys, tmp_path) -> None:
|
|
297
408
|
monkeypatch.chdir(tmp_path)
|
|
298
409
|
(tmp_path / main.WIZARD_SESSION_FILE).write_text(json.dumps({"run_id": "run_from_session"}) + "\n")
|
|
@@ -127,3 +127,17 @@ def test_main_dispatches_validate_wrapper_before_argparse(monkeypatch) -> None:
|
|
|
127
127
|
raise AssertionError("Expected main() to exit")
|
|
128
128
|
|
|
129
129
|
assert captured["argv"] == ["config", "arga-labs/validation-server"]
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def test_main_supports_global_version_flag(monkeypatch, capsys) -> None:
|
|
133
|
+
monkeypatch.setattr(main, "_cli_version", lambda: "0.1.3")
|
|
134
|
+
monkeypatch.setattr(sys, "argv", ["arga", "--version"])
|
|
135
|
+
|
|
136
|
+
try:
|
|
137
|
+
main.main()
|
|
138
|
+
except SystemExit as exc:
|
|
139
|
+
assert exc.code == 0
|
|
140
|
+
else:
|
|
141
|
+
raise AssertionError("Expected main() to exit")
|
|
142
|
+
|
|
143
|
+
assert capsys.readouterr().out.strip() == "arga 0.1.3"
|
|
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
|