basedagents 0.1.0__tar.gz → 0.1.2__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.
- {basedagents-0.1.0 → basedagents-0.1.2}/PKG-INFO +1 -1
- {basedagents-0.1.0 → basedagents-0.1.2}/basedagents/__init__.py +1 -1
- {basedagents-0.1.0 → basedagents-0.1.2}/basedagents/auth.py +1 -1
- {basedagents-0.1.0 → basedagents-0.1.2}/basedagents/cli.py +4 -1
- {basedagents-0.1.0 → basedagents-0.1.2}/basedagents/client.py +20 -8
- {basedagents-0.1.0 → basedagents-0.1.2}/basedagents/keypair.py +16 -4
- {basedagents-0.1.0 → basedagents-0.1.2}/basedagents.egg-info/PKG-INFO +1 -1
- {basedagents-0.1.0 → basedagents-0.1.2}/pyproject.toml +1 -1
- {basedagents-0.1.0 → basedagents-0.1.2}/README.md +0 -0
- {basedagents-0.1.0 → basedagents-0.1.2}/basedagents/pow.py +0 -0
- {basedagents-0.1.0 → basedagents-0.1.2}/basedagents.egg-info/SOURCES.txt +0 -0
- {basedagents-0.1.0 → basedagents-0.1.2}/basedagents.egg-info/dependency_links.txt +0 -0
- {basedagents-0.1.0 → basedagents-0.1.2}/basedagents.egg-info/entry_points.txt +0 -0
- {basedagents-0.1.0 → basedagents-0.1.2}/basedagents.egg-info/requires.txt +0 -0
- {basedagents-0.1.0 → basedagents-0.1.2}/basedagents.egg-info/top_level.txt +0 -0
- {basedagents-0.1.0 → basedagents-0.1.2}/setup.cfg +0 -0
|
@@ -20,7 +20,7 @@ from .keypair import AgentKeypair, generate as generate_keypair, from_private_ke
|
|
|
20
20
|
from .client import RegistryClient, BasedAgentsError
|
|
21
21
|
from .auth import build_headers as build_auth_headers
|
|
22
22
|
|
|
23
|
-
__version__ = "0.1.
|
|
23
|
+
__version__ = "0.1.2"
|
|
24
24
|
__all__ = [
|
|
25
25
|
"AgentKeypair",
|
|
26
26
|
"RegistryClient",
|
|
@@ -14,7 +14,7 @@ import sys
|
|
|
14
14
|
from pathlib import Path
|
|
15
15
|
|
|
16
16
|
API_URL = "https://api.basedagents.ai"
|
|
17
|
-
VERSION = "0.1.
|
|
17
|
+
VERSION = "0.1.2"
|
|
18
18
|
|
|
19
19
|
|
|
20
20
|
def _print_err(msg: str) -> None:
|
|
@@ -72,6 +72,9 @@ def cmd_register(args: list[str]) -> None:
|
|
|
72
72
|
if "--api" in args:
|
|
73
73
|
idx = args.index("--api")
|
|
74
74
|
api_url = args[idx + 1]
|
|
75
|
+
if not api_url.startswith("https://") and not api_url.startswith("http://localhost") and not api_url.startswith("http://127.0.0.1"):
|
|
76
|
+
_print_err(f"--api must use https:// (got {api_url!r}). AgentSig credentials must not be sent over plaintext.\nLocal development exception: http://localhost and http://127.0.0.1 are allowed.")
|
|
77
|
+
sys.exit(1)
|
|
75
78
|
|
|
76
79
|
dry_run = "--dry-run" in args
|
|
77
80
|
|
|
@@ -5,6 +5,7 @@ from __future__ import annotations
|
|
|
5
5
|
|
|
6
6
|
import base64
|
|
7
7
|
import json
|
|
8
|
+
import os
|
|
8
9
|
import uuid
|
|
9
10
|
from typing import Any, Callable
|
|
10
11
|
|
|
@@ -14,7 +15,10 @@ from .auth import build_headers
|
|
|
14
15
|
from .keypair import AgentKeypair
|
|
15
16
|
from .pow import solve
|
|
16
17
|
|
|
17
|
-
|
|
18
|
+
_DEFAULT_BASE = "https://api.basedagents.ai"
|
|
19
|
+
# Allow override via env var — use staging URL during tests/development,
|
|
20
|
+
# never point tests at production.
|
|
21
|
+
DEFAULT_API_URL = os.environ.get("BASEDAGENTS_API", _DEFAULT_BASE)
|
|
18
22
|
|
|
19
23
|
|
|
20
24
|
class BasedAgentsError(Exception):
|
|
@@ -109,6 +113,10 @@ class RegistryClient:
|
|
|
109
113
|
challenge_id: str = init["challenge_id"]
|
|
110
114
|
|
|
111
115
|
# Step 2: Solve PoW (difficulty from server — never hardcoded)
|
|
116
|
+
# Cap difficulty to prevent a malicious/MitM server from exhausting the nonce space
|
|
117
|
+
MAX_DIFFICULTY = 28
|
|
118
|
+
if difficulty > MAX_DIFFICULTY:
|
|
119
|
+
raise BasedAgentsError(0, f"Server requested PoW difficulty {difficulty} which exceeds client cap ({MAX_DIFFICULTY}). Aborting.")
|
|
112
120
|
nonce = solve(keypair.public_key_bytes, difficulty, on_progress=on_progress)
|
|
113
121
|
|
|
114
122
|
# Step 3: Sign challenge
|
|
@@ -171,16 +179,17 @@ class RegistryClient:
|
|
|
171
179
|
return self._get(f"/v1/agents/search?{urlencode(params)}")
|
|
172
180
|
|
|
173
181
|
def whois(self, name: str) -> dict[str, Any] | None:
|
|
174
|
-
"""Look up an agent by name. Returns None if not found.
|
|
175
|
-
|
|
182
|
+
"""Look up an agent by exact name (case-insensitive). Returns None if not found.
|
|
183
|
+
|
|
184
|
+
Does NOT return partial/fuzzy matches — a squatter with a similar name
|
|
185
|
+
will not be returned instead of None.
|
|
186
|
+
"""
|
|
187
|
+
result = self.search(q=name, limit=20)
|
|
176
188
|
agents = result.get("agents", [])
|
|
177
|
-
if not agents:
|
|
178
|
-
return None
|
|
179
|
-
# Exact name match (case-insensitive)
|
|
180
189
|
for agent in agents:
|
|
181
190
|
if agent.get("name", "").lower() == name.lower():
|
|
182
191
|
return agent
|
|
183
|
-
return
|
|
192
|
+
return None
|
|
184
193
|
|
|
185
194
|
# ── Verification ──
|
|
186
195
|
|
|
@@ -214,7 +223,10 @@ class RegistryClient:
|
|
|
214
223
|
|
|
215
224
|
nonce = str(uuid.uuid4())
|
|
216
225
|
|
|
217
|
-
# Build the signed payload (subset — no structured_report)
|
|
226
|
+
# Build the signed payload (subset — no structured_report).
|
|
227
|
+
# Note: structured_report is intentionally excluded from the inner Ed25519 signature.
|
|
228
|
+
# This mirrors the server's verification protocol (verify.ts line ~139).
|
|
229
|
+
# structured_report is protected by the outer AgentSig header on the HTTP request.
|
|
218
230
|
signed_fields: dict[str, Any] = {
|
|
219
231
|
"assignment_id": assignment_id,
|
|
220
232
|
"target_id": target_id,
|
|
@@ -69,10 +69,22 @@ class AgentKeypair:
|
|
|
69
69
|
}
|
|
70
70
|
|
|
71
71
|
def save(self, path: Path) -> None:
|
|
72
|
-
"""Save keypair to a JSON file
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
72
|
+
"""Save keypair to a JSON file.
|
|
73
|
+
|
|
74
|
+
The keys directory is created 0o700 (owner-only).
|
|
75
|
+
The file is opened with O_CREAT|O_TRUNC and mode 0o600 atomically —
|
|
76
|
+
no window where the plaintext key is world-readable.
|
|
77
|
+
"""
|
|
78
|
+
path.parent.mkdir(parents=True, exist_ok=True, mode=0o700)
|
|
79
|
+
# Ensure the dir itself is 0o700 even if it pre-existed
|
|
80
|
+
os.chmod(path.parent, 0o700)
|
|
81
|
+
data = json.dumps(self.to_dict(), indent=2).encode()
|
|
82
|
+
# Atomic create with correct mode — no chmod-after-write race
|
|
83
|
+
fd = os.open(path, os.O_WRONLY | os.O_CREAT | os.O_TRUNC, 0o600)
|
|
84
|
+
try:
|
|
85
|
+
os.write(fd, data)
|
|
86
|
+
finally:
|
|
87
|
+
os.close(fd)
|
|
76
88
|
|
|
77
89
|
@classmethod
|
|
78
90
|
def load(cls, path: Path) -> "AgentKeypair":
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "basedagents"
|
|
7
|
-
version = "0.1.
|
|
7
|
+
version = "0.1.2"
|
|
8
8
|
description = "Python SDK for basedagents.ai — cryptographic identity and reputation registry for AI agents"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = { text = "MIT" }
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|