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.
Files changed (56) hide show
  1. {trap_cli-0.0.2 → trap_cli-0.0.4}/PKG-INFO +1 -1
  2. {trap_cli-0.0.2 → trap_cli-0.0.4}/src/trap/_version.py +2 -2
  3. {trap_cli-0.0.2 → trap_cli-0.0.4}/src/trap/auth/client.py +2 -1
  4. {trap_cli-0.0.2 → trap_cli-0.0.4}/src/trap/cli/__init__.py +19 -10
  5. {trap_cli-0.0.2 → trap_cli-0.0.4}/src/trap/cli/_auth.py +2 -4
  6. trap_cli-0.0.4/src/trap/cli/_console.py +22 -0
  7. {trap_cli-0.0.2 → trap_cli-0.0.4}/src/trap/git_ops/local.py +16 -3
  8. {trap_cli-0.0.2 → trap_cli-0.0.4}/src/trap/models/provenance.py +7 -3
  9. {trap_cli-0.0.2 → trap_cli-0.0.4}/src/trap/models/report.py +4 -0
  10. {trap_cli-0.0.2 → trap_cli-0.0.4}/.gitignore +0 -0
  11. {trap_cli-0.0.2 → trap_cli-0.0.4}/LICENSE +0 -0
  12. {trap_cli-0.0.2 → trap_cli-0.0.4}/README.md +0 -0
  13. {trap_cli-0.0.2 → trap_cli-0.0.4}/examples/echo/task/README.md +0 -0
  14. {trap_cli-0.0.2 → trap_cli-0.0.4}/pyproject.toml +0 -0
  15. {trap_cli-0.0.2 → trap_cli-0.0.4}/src/trap/__init__.py +0 -0
  16. {trap_cli-0.0.2 → trap_cli-0.0.4}/src/trap/auth/__init__.py +0 -0
  17. {trap_cli-0.0.2 → trap_cli-0.0.4}/src/trap/auth/login.py +0 -0
  18. {trap_cli-0.0.2 → trap_cli-0.0.4}/src/trap/auth/oauth.py +0 -0
  19. {trap_cli-0.0.2 → trap_cli-0.0.4}/src/trap/auth/store.py +0 -0
  20. {trap_cli-0.0.2 → trap_cli-0.0.4}/src/trap/cost/__init__.py +0 -0
  21. {trap_cli-0.0.2 → trap_cli-0.0.4}/src/trap/cost/calculator.py +0 -0
  22. {trap_cli-0.0.2 → trap_cli-0.0.4}/src/trap/cost/providers.py +0 -0
  23. {trap_cli-0.0.2 → trap_cli-0.0.4}/src/trap/cost/proxy.py +0 -0
  24. {trap_cli-0.0.2 → trap_cli-0.0.4}/src/trap/display/__init__.py +0 -0
  25. {trap_cli-0.0.2 → trap_cli-0.0.4}/src/trap/display/progress.py +0 -0
  26. {trap_cli-0.0.2 → trap_cli-0.0.4}/src/trap/display/submit.py +0 -0
  27. {trap_cli-0.0.2 → trap_cli-0.0.4}/src/trap/environment/__init__.py +0 -0
  28. {trap_cli-0.0.2 → trap_cli-0.0.4}/src/trap/environment/detector.py +0 -0
  29. {trap_cli-0.0.2 → trap_cli-0.0.4}/src/trap/git_ops/__init__.py +0 -0
  30. {trap_cli-0.0.2 → trap_cli-0.0.4}/src/trap/git_ops/base.py +0 -0
  31. {trap_cli-0.0.2 → trap_cli-0.0.4}/src/trap/git_ops/remote.py +0 -0
  32. {trap_cli-0.0.2 → trap_cli-0.0.4}/src/trap/git_ops/rev.py +0 -0
  33. {trap_cli-0.0.2 → trap_cli-0.0.4}/src/trap/git_ops/url.py +0 -0
  34. {trap_cli-0.0.2 → trap_cli-0.0.4}/src/trap/loader/__init__.py +0 -0
  35. {trap_cli-0.0.2 → trap_cli-0.0.4}/src/trap/loader/errors.py +0 -0
  36. {trap_cli-0.0.2 → trap_cli-0.0.4}/src/trap/loader/trap_yaml.py +0 -0
  37. {trap_cli-0.0.2 → trap_cli-0.0.4}/src/trap/loader/traptask_yaml.py +0 -0
  38. {trap_cli-0.0.2 → trap_cli-0.0.4}/src/trap/models/__init__.py +0 -0
  39. {trap_cli-0.0.2 → trap_cli-0.0.4}/src/trap/models/cost.py +0 -0
  40. {trap_cli-0.0.2 → trap_cli-0.0.4}/src/trap/models/environment.py +0 -0
  41. {trap_cli-0.0.2 → trap_cli-0.0.4}/src/trap/models/results.py +0 -0
  42. {trap_cli-0.0.2 → trap_cli-0.0.4}/src/trap/models/trap_yaml.py +0 -0
  43. {trap_cli-0.0.2 → trap_cli-0.0.4}/src/trap/models/traptask_yaml.py +0 -0
  44. {trap_cli-0.0.2 → trap_cli-0.0.4}/src/trap/report/__init__.py +0 -0
  45. {trap_cli-0.0.2 → trap_cli-0.0.4}/src/trap/report/base.py +0 -0
  46. {trap_cli-0.0.2 → trap_cli-0.0.4}/src/trap/report/handle.py +0 -0
  47. {trap_cli-0.0.2 → trap_cli-0.0.4}/src/trap/report/json.py +0 -0
  48. {trap_cli-0.0.2 → trap_cli-0.0.4}/src/trap/report/rich.py +0 -0
  49. {trap_cli-0.0.2 → trap_cli-0.0.4}/src/trap/runner/__init__.py +0 -0
  50. {trap_cli-0.0.2 → trap_cli-0.0.4}/src/trap/runner/capture.py +0 -0
  51. {trap_cli-0.0.2 → trap_cli-0.0.4}/src/trap/runner/grader.py +0 -0
  52. {trap_cli-0.0.2 → trap_cli-0.0.4}/src/trap/runner/judge.py +0 -0
  53. {trap_cli-0.0.2 → trap_cli-0.0.4}/src/trap/runner/layout.py +0 -0
  54. {trap_cli-0.0.2 → trap_cli-0.0.4}/src/trap/runner/proc.py +0 -0
  55. {trap_cli-0.0.2 → trap_cli-0.0.4}/src/trap/runner/solution.py +0 -0
  56. {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.2
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.2'
22
- __version_tuple__ = version_tuple = (0, 0, 2)
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 path segment.
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 _die(msg: object) -> typer.Exit:
31
- """Print an error and return an Exit(2) to raise."""
32
- console.print(f"[red]error[/red]: {msg}")
33
- return typer.Exit(code=2)
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
- def _env_truthy(name: str) -> bool:
37
- return os.environ.get(name, "").strip().lower() in ("1", "true", "yes", "on")
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
- console.print(f"[red]error[/red]: {e}")
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 with `issue`
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(repo=self.origin_normalised_url, commit=self.repo.head.commit.hexsha)
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}. Both None when the tree isn't a
8
- clean, remote-backed git repo — `issue` then names why (see
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