abrade-cli 0.3.0__tar.gz → 0.3.1__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 (43) hide show
  1. {abrade_cli-0.3.0 → abrade_cli-0.3.1}/PKG-INFO +2 -2
  2. {abrade_cli-0.3.0 → abrade_cli-0.3.1}/pyproject.toml +2 -2
  3. {abrade_cli-0.3.0 → abrade_cli-0.3.1}/pyproject.toml.orig +6 -2
  4. {abrade_cli-0.3.0 → abrade_cli-0.3.1}/src/abrade_cli/cli.py +3 -1
  5. {abrade_cli-0.3.0 → abrade_cli-0.3.1}/src/abrade_cli/upgrade.py +33 -9
  6. {abrade_cli-0.3.0 → abrade_cli-0.3.1}/README.md +0 -0
  7. {abrade_cli-0.3.0 → abrade_cli-0.3.1}/src/abrade_cli/__init__.py +0 -0
  8. {abrade_cli-0.3.0 → abrade_cli-0.3.1}/src/abrade_cli/action_ledger.py +0 -0
  9. {abrade_cli-0.3.0 → abrade_cli-0.3.1}/src/abrade_cli/agent_http.py +0 -0
  10. {abrade_cli-0.3.0 → abrade_cli-0.3.1}/src/abrade_cli/assertions.py +0 -0
  11. {abrade_cli-0.3.0 → abrade_cli-0.3.1}/src/abrade_cli/auth.py +0 -0
  12. {abrade_cli-0.3.0 → abrade_cli-0.3.1}/src/abrade_cli/baseline.py +0 -0
  13. {abrade_cli-0.3.0 → abrade_cli-0.3.1}/src/abrade_cli/bounded_http.py +0 -0
  14. {abrade_cli-0.3.0 → abrade_cli-0.3.1}/src/abrade_cli/canary.py +0 -0
  15. {abrade_cli-0.3.0 → abrade_cli-0.3.1}/src/abrade_cli/ci_trust.py +0 -0
  16. {abrade_cli-0.3.0 → abrade_cli-0.3.1}/src/abrade_cli/config.py +0 -0
  17. {abrade_cli-0.3.0 → abrade_cli-0.3.1}/src/abrade_cli/containment.py +0 -0
  18. {abrade_cli-0.3.0 → abrade_cli-0.3.1}/src/abrade_cli/corpus.py +0 -0
  19. {abrade_cli-0.3.0 → abrade_cli-0.3.1}/src/abrade_cli/data/__init__.py +0 -0
  20. {abrade_cli-0.3.0 → abrade_cli-0.3.1}/src/abrade_cli/data/sample_corpus.yaml +0 -0
  21. {abrade_cli-0.3.0 → abrade_cli-0.3.1}/src/abrade_cli/evaluator.py +0 -0
  22. {abrade_cli-0.3.0 → abrade_cli-0.3.1}/src/abrade_cli/execution.py +0 -0
  23. {abrade_cli-0.3.0 → abrade_cli-0.3.1}/src/abrade_cli/fonts/Archivo-Bold.ttf +0 -0
  24. {abrade_cli-0.3.0 → abrade_cli-0.3.1}/src/abrade_cli/fonts/IBMPlexMono-Regular.ttf +0 -0
  25. {abrade_cli-0.3.0 → abrade_cli-0.3.1}/src/abrade_cli/fonts/IBMPlexMono-SemiBold.ttf +0 -0
  26. {abrade_cli-0.3.0 → abrade_cli-0.3.1}/src/abrade_cli/fonts/Inter-Regular.ttf +0 -0
  27. {abrade_cli-0.3.0 → abrade_cli-0.3.1}/src/abrade_cli/fonts/Inter-SemiBold.ttf +0 -0
  28. {abrade_cli-0.3.0 → abrade_cli-0.3.1}/src/abrade_cli/fonts/LICENSE-fonts.txt +0 -0
  29. {abrade_cli-0.3.0 → abrade_cli-0.3.1}/src/abrade_cli/fonts/README.md +0 -0
  30. {abrade_cli-0.3.0 → abrade_cli-0.3.1}/src/abrade_cli/model_names.py +0 -0
  31. {abrade_cli-0.3.0 → abrade_cli-0.3.1}/src/abrade_cli/models.py +0 -0
  32. {abrade_cli-0.3.0 → abrade_cli-0.3.1}/src/abrade_cli/release_guard.py +0 -0
  33. {abrade_cli-0.3.0 → abrade_cli-0.3.1}/src/abrade_cli/report.py +0 -0
  34. {abrade_cli-0.3.0 → abrade_cli-0.3.1}/src/abrade_cli/report_theme.py +0 -0
  35. {abrade_cli-0.3.0 → abrade_cli-0.3.1}/src/abrade_cli/response_limits.py +0 -0
  36. {abrade_cli-0.3.0 → abrade_cli-0.3.1}/src/abrade_cli/run_contract.py +0 -0
  37. {abrade_cli-0.3.0 → abrade_cli-0.3.1}/src/abrade_cli/runner.py +0 -0
  38. {abrade_cli-0.3.0 → abrade_cli-0.3.1}/src/abrade_cli/safeio.py +0 -0
  39. {abrade_cli-0.3.0 → abrade_cli-0.3.1}/src/abrade_cli/sarif.py +0 -0
  40. {abrade_cli-0.3.0 → abrade_cli-0.3.1}/src/abrade_cli/storage.py +0 -0
  41. {abrade_cli-0.3.0 → abrade_cli-0.3.1}/src/abrade_cli/tos.py +0 -0
  42. {abrade_cli-0.3.0 → abrade_cli-0.3.1}/src/abrade_cli/upstream_errors.py +0 -0
  43. {abrade_cli-0.3.0 → abrade_cli-0.3.1}/src/abrade_cli/verify.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: abrade-cli
3
- Version: 0.3.0
3
+ Version: 0.3.1
4
4
  Summary: Abrade's self-hosted CLI — run the injection suite on your own machine, results stay local, no SaaS account required.
5
5
  Keywords: ai-agents,security-testing,prompt-injection,red-teaming,llm-security
6
6
  Author: Abrade
@@ -14,7 +14,7 @@ Classifier: Programming Language :: Python :: 3.12
14
14
  Classifier: Programming Language :: Python :: 3.13
15
15
  Classifier: Topic :: Security
16
16
  Classifier: Topic :: Software Development :: Testing
17
- Requires-Dist: anthropic>=0.119.0
17
+ Requires-Dist: anthropic>=0.119.0,<1
18
18
  Requires-Dist: fpdf2>=2.8.7
19
19
  Requires-Dist: httpx>=0.28.1,<1
20
20
  Requires-Dist: pydantic>=2.13.4
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "abrade-cli"
3
- version = "0.3.0"
3
+ version = "0.3.1"
4
4
  description = "Abrade's self-hosted CLI — run the injection suite on your own machine, results stay local, no SaaS account required."
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.12"
@@ -23,7 +23,7 @@ classifiers = [
23
23
  "Topic :: Software Development :: Testing",
24
24
  ]
25
25
  dependencies = [
26
- "anthropic>=0.119.0",
26
+ "anthropic>=0.119.0,<1",
27
27
  "fpdf2>=2.8.7",
28
28
  "httpx>=0.28.1,<1",
29
29
  "pydantic>=2.13.4",
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "abrade-cli"
3
- version = "0.3.0"
3
+ version = "0.3.1"
4
4
  description = "Abrade's self-hosted CLI — run the injection suite on your own machine, results stay local, no SaaS account required."
5
5
  readme = "README.md"
6
6
  # The project, not a personal handle. PyPI renders this as "Author" on
@@ -40,7 +40,11 @@ classifiers = [
40
40
  "Topic :: Software Development :: Testing",
41
41
  ]
42
42
  dependencies = [
43
- "anthropic>=0.119.0",
43
+ # Below 1 because we hand the SDK an httpx client, which is where the response
44
+ # budgets live, and anthropic 2 accepts only httpx2. pip resolves the newest this
45
+ # allows and never reads uv.lock, so without a cap 0.3.0 crashed on the default config
46
+ # for anyone installing it while passing everything run on the lock.
47
+ "anthropic>=0.119.0,<1",
44
48
  "fpdf2>=2.8.7",
45
49
  "httpx>=0.28.1,<1",
46
50
  "pydantic>=2.13.4",
@@ -162,7 +162,9 @@ def upgrade(
162
162
  f"[yellow]Update available:[/yellow] "
163
163
  f"{release.installed} → [bold]{release.latest}[/bold]"
164
164
  )
165
- console.print(f"\n {upgrade_module.install_command()}\n")
165
+ # Written out to be copied: a long path must not wrap, and a bracket is not markup.
166
+ command = escape(upgrade_module.install_command())
167
+ console.print(f"\n {command}\n", soft_wrap=True)
166
168
  if check_only:
167
169
  raise typer.Exit(code=1)
168
170
 
@@ -17,8 +17,11 @@ updated it and the new command still isn't there".
17
17
 
18
18
  from __future__ import annotations
19
19
 
20
+ import importlib.util
20
21
  import json
21
22
  import os
23
+ import shlex
24
+ import subprocess
22
25
  import sys
23
26
  import urllib.error
24
27
  import urllib.request
@@ -72,25 +75,46 @@ def latest_version(*, timeout: float = 10.0) -> str:
72
75
  raise UpgradeCheckError("PyPI returned an unexpected response.") from exc
73
76
 
74
77
 
78
+ def _has_pip() -> bool:
79
+ return importlib.util.find_spec("pip") is not None
80
+
81
+
82
+ def _quoted(path: str) -> str:
83
+ return subprocess.list2cmdline([path]) if os.name == "nt" else shlex.quote(path)
84
+
85
+
75
86
  def install_command() -> str:
76
87
  """The upgrade command for whichever installer put this here.
77
88
 
78
89
  Detected from the path of the running interpreter rather than asked
79
90
  for, because the common failure is not knowing which manager was used
80
91
  — and the answer is sitting in sys.executable.
81
- """
82
- executable = Path(sys.executable).resolve()
83
- parts = {p.lower() for p in executable.parts}
84
92
 
85
- if "uv" in parts or "uv-tool" in parts or os.environ.get("UV_TOOL_DIR"):
93
+ The path as it stands, never resolved. A venv's python is a symlink to the
94
+ interpreter it was made from, so following it leaves the one directory that
95
+ says who installed this.
96
+ """
97
+ executable = Path(sys.executable)
98
+ ordered = [p.lower() for p in executable.parts]
99
+ parts = set(ordered)
100
+ uv_tool_dir = os.environ.get("UV_TOOL_DIR")
101
+
102
+ if (
103
+ ("uv", "tools") in zip(ordered, ordered[1:], strict=False)
104
+ or "uv-tool" in parts
105
+ or (uv_tool_dir and executable.is_relative_to(uv_tool_dir))
106
+ ):
86
107
  return "uv tool upgrade abrade-cli"
87
108
  if "pipx" in parts:
88
109
  return "pipx upgrade abrade-cli"
89
- if any("virtualenvs" in p or ".venv" in p for p in parts):
90
- # Inside a project environment, so upgrading globally would touch a
91
- # different copy than the one being run.
92
- return "uv pip install --upgrade abrade-cli"
93
- return "pip install --upgrade abrade-cli"
110
+ # Aimed at this interpreter either way. A bare `pip` is whichever is first on PATH,
111
+ # and a bare `uv pip` whichever environment it finds from the working directory.
112
+ # Upgrading another copy succeeds and changes nothing about the one that asked.
113
+ python = _quoted(sys.executable)
114
+ if _has_pip():
115
+ return f"{python} -m pip install --upgrade abrade-cli"
116
+ # `uv venv` leaves pip out, so `-m pip` would only fail.
117
+ return f"uv pip install --python {python} --upgrade abrade-cli"
94
118
 
95
119
 
96
120
  def check(installed: str, *, timeout: float = 10.0) -> Release:
File without changes