trap-cli 0.0.2__tar.gz → 0.0.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.
- {trap_cli-0.0.2 → trap_cli-0.0.4}/PKG-INFO +1 -1
- {trap_cli-0.0.2 → trap_cli-0.0.4}/src/trap/_version.py +2 -2
- {trap_cli-0.0.2 → trap_cli-0.0.4}/src/trap/auth/client.py +2 -1
- {trap_cli-0.0.2 → trap_cli-0.0.4}/src/trap/cli/__init__.py +19 -10
- {trap_cli-0.0.2 → trap_cli-0.0.4}/src/trap/cli/_auth.py +2 -4
- trap_cli-0.0.4/src/trap/cli/_console.py +22 -0
- {trap_cli-0.0.2 → trap_cli-0.0.4}/src/trap/git_ops/local.py +16 -3
- {trap_cli-0.0.2 → trap_cli-0.0.4}/src/trap/models/provenance.py +7 -3
- {trap_cli-0.0.2 → trap_cli-0.0.4}/src/trap/models/report.py +4 -0
- {trap_cli-0.0.2 → trap_cli-0.0.4}/.gitignore +0 -0
- {trap_cli-0.0.2 → trap_cli-0.0.4}/LICENSE +0 -0
- {trap_cli-0.0.2 → trap_cli-0.0.4}/README.md +0 -0
- {trap_cli-0.0.2 → trap_cli-0.0.4}/examples/echo/task/README.md +0 -0
- {trap_cli-0.0.2 → trap_cli-0.0.4}/pyproject.toml +0 -0
- {trap_cli-0.0.2 → trap_cli-0.0.4}/src/trap/__init__.py +0 -0
- {trap_cli-0.0.2 → trap_cli-0.0.4}/src/trap/auth/__init__.py +0 -0
- {trap_cli-0.0.2 → trap_cli-0.0.4}/src/trap/auth/login.py +0 -0
- {trap_cli-0.0.2 → trap_cli-0.0.4}/src/trap/auth/oauth.py +0 -0
- {trap_cli-0.0.2 → trap_cli-0.0.4}/src/trap/auth/store.py +0 -0
- {trap_cli-0.0.2 → trap_cli-0.0.4}/src/trap/cost/__init__.py +0 -0
- {trap_cli-0.0.2 → trap_cli-0.0.4}/src/trap/cost/calculator.py +0 -0
- {trap_cli-0.0.2 → trap_cli-0.0.4}/src/trap/cost/providers.py +0 -0
- {trap_cli-0.0.2 → trap_cli-0.0.4}/src/trap/cost/proxy.py +0 -0
- {trap_cli-0.0.2 → trap_cli-0.0.4}/src/trap/display/__init__.py +0 -0
- {trap_cli-0.0.2 → trap_cli-0.0.4}/src/trap/display/progress.py +0 -0
- {trap_cli-0.0.2 → trap_cli-0.0.4}/src/trap/display/submit.py +0 -0
- {trap_cli-0.0.2 → trap_cli-0.0.4}/src/trap/environment/__init__.py +0 -0
- {trap_cli-0.0.2 → trap_cli-0.0.4}/src/trap/environment/detector.py +0 -0
- {trap_cli-0.0.2 → trap_cli-0.0.4}/src/trap/git_ops/__init__.py +0 -0
- {trap_cli-0.0.2 → trap_cli-0.0.4}/src/trap/git_ops/base.py +0 -0
- {trap_cli-0.0.2 → trap_cli-0.0.4}/src/trap/git_ops/remote.py +0 -0
- {trap_cli-0.0.2 → trap_cli-0.0.4}/src/trap/git_ops/rev.py +0 -0
- {trap_cli-0.0.2 → trap_cli-0.0.4}/src/trap/git_ops/url.py +0 -0
- {trap_cli-0.0.2 → trap_cli-0.0.4}/src/trap/loader/__init__.py +0 -0
- {trap_cli-0.0.2 → trap_cli-0.0.4}/src/trap/loader/errors.py +0 -0
- {trap_cli-0.0.2 → trap_cli-0.0.4}/src/trap/loader/trap_yaml.py +0 -0
- {trap_cli-0.0.2 → trap_cli-0.0.4}/src/trap/loader/traptask_yaml.py +0 -0
- {trap_cli-0.0.2 → trap_cli-0.0.4}/src/trap/models/__init__.py +0 -0
- {trap_cli-0.0.2 → trap_cli-0.0.4}/src/trap/models/cost.py +0 -0
- {trap_cli-0.0.2 → trap_cli-0.0.4}/src/trap/models/environment.py +0 -0
- {trap_cli-0.0.2 → trap_cli-0.0.4}/src/trap/models/results.py +0 -0
- {trap_cli-0.0.2 → trap_cli-0.0.4}/src/trap/models/trap_yaml.py +0 -0
- {trap_cli-0.0.2 → trap_cli-0.0.4}/src/trap/models/traptask_yaml.py +0 -0
- {trap_cli-0.0.2 → trap_cli-0.0.4}/src/trap/report/__init__.py +0 -0
- {trap_cli-0.0.2 → trap_cli-0.0.4}/src/trap/report/base.py +0 -0
- {trap_cli-0.0.2 → trap_cli-0.0.4}/src/trap/report/handle.py +0 -0
- {trap_cli-0.0.2 → trap_cli-0.0.4}/src/trap/report/json.py +0 -0
- {trap_cli-0.0.2 → trap_cli-0.0.4}/src/trap/report/rich.py +0 -0
- {trap_cli-0.0.2 → trap_cli-0.0.4}/src/trap/runner/__init__.py +0 -0
- {trap_cli-0.0.2 → trap_cli-0.0.4}/src/trap/runner/capture.py +0 -0
- {trap_cli-0.0.2 → trap_cli-0.0.4}/src/trap/runner/grader.py +0 -0
- {trap_cli-0.0.2 → trap_cli-0.0.4}/src/trap/runner/judge.py +0 -0
- {trap_cli-0.0.2 → trap_cli-0.0.4}/src/trap/runner/layout.py +0 -0
- {trap_cli-0.0.2 → trap_cli-0.0.4}/src/trap/runner/proc.py +0 -0
- {trap_cli-0.0.2 → trap_cli-0.0.4}/src/trap/runner/solution.py +0 -0
- {trap_cli-0.0.2 → trap_cli-0.0.4}/src/trap/runner/task.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: trap-cli
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.4
|
|
4
4
|
Summary: tp — non-invasive CLI testing framework for AI workflows. Submits results to trapstreet.run.
|
|
5
5
|
Project-URL: Homepage, https://trapstreet.run
|
|
6
6
|
Project-URL: Repository, https://github.com/trapstreet/trap
|
|
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
|
|
|
18
18
|
commit_id: str | None
|
|
19
19
|
__commit_id__: str | None
|
|
20
20
|
|
|
21
|
-
__version__ = version = '0.0.
|
|
22
|
-
__version_tuple__ = version_tuple = (0, 0,
|
|
21
|
+
__version__ = version = '0.0.4'
|
|
22
|
+
__version_tuple__ = version_tuple = (0, 0, 4)
|
|
23
23
|
|
|
24
24
|
__commit_id__ = commit_id = None
|
|
@@ -45,7 +45,8 @@ class ApiClient:
|
|
|
45
45
|
|
|
46
46
|
def submit(self, report_path: Path) -> dict[str, Any]:
|
|
47
47
|
# Content-addressed ingest: the task identity travels inside the report
|
|
48
|
-
# (provenance.task.{repo,commit}), not the URL — so no task_id
|
|
48
|
+
# (provenance.task.{repo,commit,subdirectory}), not the URL — so no task_id
|
|
49
|
+
# path segment.
|
|
49
50
|
try:
|
|
50
51
|
resp = self._client.post("/api/submit", content=report_path.read_bytes())
|
|
51
52
|
resp.raise_for_status()
|
|
@@ -8,10 +8,11 @@ from pathlib import Path
|
|
|
8
8
|
from typing import Annotated
|
|
9
9
|
|
|
10
10
|
import typer
|
|
11
|
-
from rich.console import Console
|
|
12
11
|
|
|
12
|
+
from trap import __version__
|
|
13
13
|
from trap.auth import DEFAULT_SERVER, ApiClient, ApiError, AuthStore
|
|
14
14
|
from trap.cli._auth import auth_app
|
|
15
|
+
from trap.cli._console import _die, _env_truthy, console, err_console
|
|
15
16
|
from trap.display import CaseProgress, render_submit_result
|
|
16
17
|
from trap.environment import EnvironmentDetector
|
|
17
18
|
from trap.git_ops import GitOpsError, LocalRepo, ParsedGitUrl
|
|
@@ -22,19 +23,27 @@ from trap.runner import TaskRunner
|
|
|
22
23
|
|
|
23
24
|
app = typer.Typer(help="AI prompt / agent / workflow / testing framework.")
|
|
24
25
|
app.add_typer(auth_app, name="auth")
|
|
25
|
-
console = Console()
|
|
26
|
-
# Warnings go to stderr so `-o json` stdout stays machine-parseable.
|
|
27
|
-
err_console = Console(stderr=True)
|
|
28
26
|
|
|
29
27
|
|
|
30
|
-
def
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
28
|
+
def _version_callback(value: bool) -> None:
|
|
29
|
+
if value:
|
|
30
|
+
console.print(f"tp {__version__}")
|
|
31
|
+
raise typer.Exit()
|
|
34
32
|
|
|
35
33
|
|
|
36
|
-
|
|
37
|
-
|
|
34
|
+
@app.callback()
|
|
35
|
+
def _main(
|
|
36
|
+
version: Annotated[
|
|
37
|
+
bool,
|
|
38
|
+
typer.Option(
|
|
39
|
+
"--version",
|
|
40
|
+
help="Show the trap version and exit.",
|
|
41
|
+
callback=_version_callback,
|
|
42
|
+
is_eager=True,
|
|
43
|
+
),
|
|
44
|
+
] = False,
|
|
45
|
+
) -> None:
|
|
46
|
+
"""AI prompt / agent / workflow / testing framework."""
|
|
38
47
|
|
|
39
48
|
|
|
40
49
|
def _confirm_remote(url: str, *, trust: bool) -> None:
|
|
@@ -4,12 +4,11 @@ import sys
|
|
|
4
4
|
from typing import Annotated
|
|
5
5
|
|
|
6
6
|
import typer
|
|
7
|
-
from rich.console import Console
|
|
8
7
|
|
|
9
8
|
from trap.auth import DEFAULT_SERVER, ApiClient, ApiError, AuthStore, BrowserProvider, TokenProvider
|
|
9
|
+
from trap.cli._console import _die, console
|
|
10
10
|
|
|
11
11
|
auth_app = typer.Typer(help="Manage authentication.")
|
|
12
|
-
console = Console()
|
|
13
12
|
|
|
14
13
|
|
|
15
14
|
@auth_app.command("login")
|
|
@@ -50,8 +49,7 @@ def auth_login(
|
|
|
50
49
|
console.print(provider.pre_message)
|
|
51
50
|
auth_data = provider.acquire()
|
|
52
51
|
except (ValueError, TimeoutError) as e:
|
|
53
|
-
|
|
54
|
-
raise typer.Exit(code=2) from None
|
|
52
|
+
raise _die(e) from None
|
|
55
53
|
|
|
56
54
|
path = AuthStore().save(auth_data)
|
|
57
55
|
console.print(
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"""Shared CLI console objects and small helpers used across command modules."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import os
|
|
6
|
+
|
|
7
|
+
import typer
|
|
8
|
+
from rich.console import Console
|
|
9
|
+
|
|
10
|
+
console = Console()
|
|
11
|
+
# Warnings go to stderr so `-o json` stdout stays machine-parseable.
|
|
12
|
+
err_console = Console(stderr=True)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def _die(msg: object) -> typer.Exit:
|
|
16
|
+
"""Print an error and return an Exit(2) to raise."""
|
|
17
|
+
console.print(f"[red]error[/red]: {msg}")
|
|
18
|
+
return typer.Exit(code=2)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def _env_truthy(name: str) -> bool:
|
|
22
|
+
return os.environ.get(name, "").strip().lower() in ("1", "true", "yes", "on")
|
|
@@ -58,13 +58,26 @@ class LocalRepo:
|
|
|
58
58
|
except Exception:
|
|
59
59
|
return "git probe failed"
|
|
60
60
|
|
|
61
|
+
@property
|
|
62
|
+
def subdirectory(self) -> str | None:
|
|
63
|
+
"""Opened path relative to the repo root, None at the root (or in a bare repo)."""
|
|
64
|
+
root = self.repo.working_tree_dir
|
|
65
|
+
if root is None:
|
|
66
|
+
return None
|
|
67
|
+
rel = self.path.resolve().relative_to(Path(root).resolve())
|
|
68
|
+
return rel.as_posix() if rel != Path(".") else None
|
|
69
|
+
|
|
61
70
|
def provenance(self) -> GitProvenance:
|
|
62
|
-
"""{repo, commit} for a clean checkout with an origin, else empty
|
|
63
|
-
naming why — we claim nothing about a run that isn't reproducible."""
|
|
71
|
+
"""{repo, commit, subdirectory} for a clean checkout with an origin, else empty
|
|
72
|
+
with `issue` naming why — we claim nothing about a run that isn't reproducible."""
|
|
64
73
|
issue = self.provenance_issue()
|
|
65
74
|
if issue is not None:
|
|
66
75
|
return GitProvenance(issue=issue)
|
|
67
|
-
return GitProvenance(
|
|
76
|
+
return GitProvenance(
|
|
77
|
+
repo=self.origin_normalised_url,
|
|
78
|
+
commit=self.repo.head.commit.hexsha,
|
|
79
|
+
subdirectory=self.subdirectory,
|
|
80
|
+
)
|
|
68
81
|
|
|
69
82
|
@classmethod
|
|
70
83
|
def provenance_of(cls, path: Path) -> GitProvenance:
|
|
@@ -4,12 +4,16 @@ from pydantic import BaseModel, Field
|
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
class GitProvenance(BaseModel):
|
|
7
|
-
"""Git origin of one checkout: {repo, commit}.
|
|
8
|
-
clean, remote-backed git repo — `issue` then names why
|
|
9
|
-
LocalRepo.provenance_issue). Anchored checkouts carry no issue.
|
|
7
|
+
"""Git origin of one checkout: {repo, commit, subdirectory}. repo and commit are
|
|
8
|
+
None when the tree isn't a clean, remote-backed git repo — `issue` then names why
|
|
9
|
+
(see LocalRepo.provenance_issue). Anchored checkouts carry no issue.
|
|
10
|
+
|
|
11
|
+
`subdirectory` locates the checkout within the repo (None at the root), so two
|
|
12
|
+
solutions or tasks living in different subdirectories of one repo stay distinct."""
|
|
10
13
|
|
|
11
14
|
repo: str | None = None
|
|
12
15
|
commit: str | None = None
|
|
16
|
+
subdirectory: str | None = None
|
|
13
17
|
issue: str | None = None
|
|
14
18
|
|
|
15
19
|
|
|
@@ -28,6 +28,9 @@ class ReportData(BaseModel):
|
|
|
28
28
|
started_at_utc: str
|
|
29
29
|
finished_at_utc: str
|
|
30
30
|
|
|
31
|
+
# Leaderboard identity the solution author chose in trap.yaml (`name`); None →
|
|
32
|
+
# the server auto-assigns one.
|
|
33
|
+
solution_name: str | None = None
|
|
31
34
|
# Engine identity (model/framework). Self-reported from trap.yaml today, but the
|
|
32
35
|
# website consumes it from the report — never from trap.yaml directly — precisely
|
|
33
36
|
# because the source may change: a future version is expected to derive this by
|
|
@@ -56,6 +59,7 @@ class ReportData(BaseModel):
|
|
|
56
59
|
grader_metrics=grader_metrics,
|
|
57
60
|
started_at_utc=started_at_utc.isoformat(timespec="seconds"),
|
|
58
61
|
finished_at_utc=finished_at_utc.isoformat(timespec="seconds"),
|
|
62
|
+
solution_name=trap_config.name,
|
|
59
63
|
profile=trap_config.profile,
|
|
60
64
|
environment=environment,
|
|
61
65
|
)
|
|
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
|
|
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
|