agentcheck-sdk 0.1.0__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.
@@ -0,0 +1,80 @@
1
+ Metadata-Version: 2.4
2
+ Name: agentcheck-sdk
3
+ Version: 0.1.0
4
+ Summary: Record what your AI agent is allowed to do
5
+ Author-email: AgentCheck <dev@agentcheck.io>
6
+ License: MIT
7
+ Project-URL: Homepage, https://agentcheck.io
8
+ Project-URL: Repository, https://github.com/agentcheck/agentcheck-python
9
+ Keywords: ai,agent,delegation,trust,authorization
10
+ Classifier: Development Status :: 3 - Alpha
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: License :: OSI Approved :: MIT License
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.9
15
+ Classifier: Programming Language :: Python :: 3.10
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Programming Language :: Python :: 3.13
19
+ Classifier: Topic :: Security
20
+ Requires-Python: >=3.9
21
+ Description-Content-Type: text/markdown
22
+ Requires-Dist: httpx>=0.25
23
+ Requires-Dist: pydantic>=2.0
24
+
25
+ # agentcheck
26
+
27
+ Record what your AI agent is allowed to do.
28
+
29
+ ## Install
30
+
31
+ ```bash
32
+ pip install agentcheck
33
+ ```
34
+
35
+ ## Quickstart
36
+
37
+ ```python
38
+ import agentcheck
39
+
40
+ agentcheck.init(api_key="ak_live_...", base_url="https://agentcheck.fly.dev")
41
+
42
+ proof = agentcheck.record(
43
+ agent="factory-bot",
44
+ scope="order parts under $10K, monitor equipment 24/7",
45
+ authorized_by="kim@factory.com"
46
+ )
47
+
48
+ # Kim gets an email, clicks "Approve"
49
+ # Anyone can verify: /api/v1/verify/{proof.id}
50
+
51
+ record = agentcheck.get(proof.id)
52
+ print(record.status) # "approved"
53
+ ```
54
+
55
+ ## API
56
+
57
+ | Function | Description |
58
+ |----------|-------------|
59
+ | `agentcheck.init(api_key, base_url)` | Initialize with your API key |
60
+ | `agentcheck.record(agent, scope, authorized_by)` | Create agreement |
61
+ | `agentcheck.get(id)` | Get agreement details |
62
+ | `agentcheck.list(status, agent)` | List agreements |
63
+ | `agentcheck.amend(id, new_scope)` | Amend agreement scope |
64
+
65
+ ## Sign Up
66
+
67
+ ```python
68
+ from agentcheck import Client
69
+
70
+ result = Client.signup(
71
+ email="dev@company.com",
72
+ company_name="My Company",
73
+ base_url="https://agentcheck.fly.dev"
74
+ )
75
+ print(result["api_key"]) # Save this - shown once only
76
+ ```
77
+
78
+ ## License
79
+
80
+ MIT
@@ -0,0 +1,56 @@
1
+ # agentcheck
2
+
3
+ Record what your AI agent is allowed to do.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ pip install agentcheck
9
+ ```
10
+
11
+ ## Quickstart
12
+
13
+ ```python
14
+ import agentcheck
15
+
16
+ agentcheck.init(api_key="ak_live_...", base_url="https://agentcheck.fly.dev")
17
+
18
+ proof = agentcheck.record(
19
+ agent="factory-bot",
20
+ scope="order parts under $10K, monitor equipment 24/7",
21
+ authorized_by="kim@factory.com"
22
+ )
23
+
24
+ # Kim gets an email, clicks "Approve"
25
+ # Anyone can verify: /api/v1/verify/{proof.id}
26
+
27
+ record = agentcheck.get(proof.id)
28
+ print(record.status) # "approved"
29
+ ```
30
+
31
+ ## API
32
+
33
+ | Function | Description |
34
+ |----------|-------------|
35
+ | `agentcheck.init(api_key, base_url)` | Initialize with your API key |
36
+ | `agentcheck.record(agent, scope, authorized_by)` | Create agreement |
37
+ | `agentcheck.get(id)` | Get agreement details |
38
+ | `agentcheck.list(status, agent)` | List agreements |
39
+ | `agentcheck.amend(id, new_scope)` | Amend agreement scope |
40
+
41
+ ## Sign Up
42
+
43
+ ```python
44
+ from agentcheck import Client
45
+
46
+ result = Client.signup(
47
+ email="dev@company.com",
48
+ company_name="My Company",
49
+ base_url="https://agentcheck.fly.dev"
50
+ )
51
+ print(result["api_key"]) # Save this - shown once only
52
+ ```
53
+
54
+ ## License
55
+
56
+ MIT
@@ -0,0 +1,95 @@
1
+ """AgentCheck - Record what your AI agent is allowed to do."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from .client import Client
6
+ from .exceptions import (
7
+ AgentCheckError,
8
+ AuthenticationError,
9
+ NotFoundError,
10
+ ServerError,
11
+ ValidationError,
12
+ )
13
+ from .models import Agreement, AgreementList
14
+
15
+ __version__ = "0.1.0"
16
+ __all__ = [
17
+ "Client",
18
+ "Agreement",
19
+ "AgreementList",
20
+ "AgentCheckError",
21
+ "AuthenticationError",
22
+ "NotFoundError",
23
+ "ValidationError",
24
+ "ServerError",
25
+ "init",
26
+ "record",
27
+ "get",
28
+ "list",
29
+ "amend",
30
+ ]
31
+
32
+ # -- Module-level convenience API --
33
+ # Enables: agentcheck.record(agent, scope, authorized_by)
34
+
35
+ _client: Client | None = None
36
+
37
+
38
+ def init(api_key: str, base_url: str = "https://api.agentcheck.io"):
39
+ """Initialize the global client."""
40
+ global _client
41
+ _client = Client(api_key=api_key, base_url=base_url)
42
+
43
+
44
+ def _get_client() -> Client:
45
+ if _client is None:
46
+ raise AgentCheckError(
47
+ "Call agentcheck.init(api_key='...') first",
48
+ code="not_initialized",
49
+ )
50
+ return _client
51
+
52
+
53
+ def record(
54
+ agent: str,
55
+ scope: str,
56
+ authorized_by: str,
57
+ **kwargs,
58
+ ) -> Agreement:
59
+ """Create a new agreement. One line is all you need."""
60
+ return _get_client().record(
61
+ agent=agent,
62
+ scope=scope,
63
+ authorized_by=authorized_by,
64
+ **kwargs,
65
+ )
66
+
67
+
68
+ def get(agreement_id: str) -> Agreement:
69
+ """Get agreement details."""
70
+ return _get_client().get(agreement_id)
71
+
72
+
73
+ def list(
74
+ status: str | None = None,
75
+ agent: str | None = None,
76
+ limit: int = 50,
77
+ offset: int = 0,
78
+ ) -> AgreementList:
79
+ """List agreements."""
80
+ return _get_client().list(status=status, agent=agent, limit=limit, offset=offset)
81
+
82
+
83
+ def amend(
84
+ agreement_id: str,
85
+ new_scope: str,
86
+ reason: str | None = None,
87
+ require_reauth: bool = True,
88
+ ) -> Agreement:
89
+ """Amend an agreement's scope."""
90
+ return _get_client().amend(
91
+ agreement_id=agreement_id,
92
+ new_scope=new_scope,
93
+ reason=reason,
94
+ require_reauth=require_reauth,
95
+ )
@@ -0,0 +1,157 @@
1
+ from __future__ import annotations
2
+
3
+ import httpx
4
+
5
+ from .exceptions import (
6
+ AgentCheckError,
7
+ AuthenticationError,
8
+ NotFoundError,
9
+ ServerError,
10
+ ValidationError,
11
+ )
12
+ from .models import Agreement, AgreementList
13
+
14
+ _DEFAULT_BASE_URL = "https://api.agentcheck.io"
15
+
16
+
17
+ class Client:
18
+ """AgentCheck API client."""
19
+
20
+ def __init__(
21
+ self,
22
+ api_key: str,
23
+ base_url: str = _DEFAULT_BASE_URL,
24
+ timeout: float = 30.0,
25
+ ):
26
+ self._api_key = api_key
27
+ self._base_url = base_url.rstrip("/")
28
+ self._http = httpx.Client(
29
+ base_url=self._base_url,
30
+ headers={"X-API-Key": api_key},
31
+ timeout=timeout,
32
+ )
33
+
34
+ def _handle_response(self, resp: httpx.Response) -> dict:
35
+ if resp.status_code == 401:
36
+ raise AuthenticationError("Invalid API key", code="unauthorized")
37
+ if resp.status_code == 404:
38
+ raise NotFoundError("Resource not found", code="not_found")
39
+ if resp.status_code == 400:
40
+ data = resp.json()
41
+ msg = data.get("error", {}).get("message", "Bad request")
42
+ raise ValidationError(msg, code="bad_request")
43
+ if resp.status_code >= 500:
44
+ raise ServerError("Server error", code="server_error")
45
+ if not resp.is_success:
46
+ raise AgentCheckError(f"HTTP {resp.status_code}")
47
+ return resp.json()
48
+
49
+ def record(
50
+ self,
51
+ agent: str,
52
+ scope: str,
53
+ authorized_by: str,
54
+ agent_provider: str | None = None,
55
+ expires_in_days: int | None = None,
56
+ metadata: dict | None = None,
57
+ ) -> Agreement:
58
+ """Create a new agreement and send approval email."""
59
+ body: dict = {
60
+ "agent": agent,
61
+ "scope": scope,
62
+ "authorized_by": authorized_by,
63
+ }
64
+ if agent_provider is not None:
65
+ body["agent_provider"] = agent_provider
66
+ if expires_in_days is not None:
67
+ body["expires_in_days"] = expires_in_days
68
+ if metadata is not None:
69
+ body["metadata"] = metadata
70
+
71
+ resp = self._http.post("/api/v1/record", json=body)
72
+ data = self._handle_response(resp)
73
+ return Agreement.model_validate(data["data"])
74
+
75
+ def get(self, agreement_id: str) -> Agreement:
76
+ """Get agreement details by ID."""
77
+ resp = self._http.get(f"/api/v1/record/{agreement_id}")
78
+ data = self._handle_response(resp)
79
+ return Agreement.model_validate(data["data"])
80
+
81
+ def list(
82
+ self,
83
+ status: str | None = None,
84
+ agent: str | None = None,
85
+ limit: int = 50,
86
+ offset: int = 0,
87
+ ) -> AgreementList:
88
+ """List agreements with optional filters."""
89
+ params: dict = {"limit": limit, "offset": offset}
90
+ if status is not None:
91
+ params["status"] = status
92
+ if agent is not None:
93
+ params["agent"] = agent
94
+
95
+ resp = self._http.get("/api/v1/records", params=params)
96
+ data = self._handle_response(resp)
97
+ return AgreementList.model_validate(data["data"])
98
+
99
+ def amend(
100
+ self,
101
+ agreement_id: str,
102
+ new_scope: str,
103
+ reason: str | None = None,
104
+ require_reauth: bool = True,
105
+ ) -> Agreement:
106
+ """Amend an agreement's scope. Creates a new version."""
107
+ body: dict = {
108
+ "new_scope": new_scope,
109
+ "require_reauth": require_reauth,
110
+ }
111
+ if reason is not None:
112
+ body["reason"] = reason
113
+
114
+ resp = self._http.post(f"/api/v1/record/{agreement_id}/amend", json=body)
115
+ data = self._handle_response(resp)
116
+ return Agreement.model_validate(data["data"])
117
+
118
+ def register_webhook(
119
+ self,
120
+ url: str,
121
+ events: list[str],
122
+ ) -> dict:
123
+ """Register a webhook endpoint."""
124
+ resp = self._http.post(
125
+ "/api/v1/webhooks",
126
+ json={"url": url, "events": events},
127
+ )
128
+ data = self._handle_response(resp)
129
+ return data["data"]
130
+
131
+ @staticmethod
132
+ def signup(
133
+ email: str,
134
+ company_name: str | None = None,
135
+ base_url: str = _DEFAULT_BASE_URL,
136
+ ) -> dict:
137
+ """Self-service signup. Returns API key (shown once)."""
138
+ body: dict = {"email": email}
139
+ if company_name is not None:
140
+ body["company_name"] = company_name
141
+ resp = httpx.post(f"{base_url}/api/v1/signup", json=body)
142
+ if resp.status_code == 429:
143
+ raise AgentCheckError("Rate limited", code="rate_limited")
144
+ if not resp.is_success:
145
+ data = resp.json()
146
+ msg = data.get("error", {}).get("message", "Signup failed")
147
+ raise AgentCheckError(msg)
148
+ return resp.json()["data"]
149
+
150
+ def close(self):
151
+ self._http.close()
152
+
153
+ def __enter__(self):
154
+ return self
155
+
156
+ def __exit__(self, *args):
157
+ self.close()
@@ -0,0 +1,23 @@
1
+ class AgentCheckError(Exception):
2
+ """Base exception for AgentCheck SDK."""
3
+
4
+ def __init__(self, message: str, code: str | None = None):
5
+ self.message = message
6
+ self.code = code
7
+ super().__init__(message)
8
+
9
+
10
+ class AuthenticationError(AgentCheckError):
11
+ """Invalid or missing API key."""
12
+
13
+
14
+ class NotFoundError(AgentCheckError):
15
+ """Resource not found."""
16
+
17
+
18
+ class ValidationError(AgentCheckError):
19
+ """Invalid request parameters."""
20
+
21
+
22
+ class ServerError(AgentCheckError):
23
+ """Server-side error."""
@@ -0,0 +1,26 @@
1
+ from __future__ import annotations
2
+
3
+ from datetime import datetime
4
+
5
+ from pydantic import BaseModel
6
+
7
+
8
+ class Agreement(BaseModel):
9
+ id: str
10
+ status: str
11
+ agent: str
12
+ agent_provider: str | None = None
13
+ scope: str
14
+ authorized_by: str
15
+ created_at: datetime
16
+ approved_at: datetime | None = None
17
+ expires_at: datetime | None = None
18
+ verify_url: str
19
+ signature: str | None = None
20
+ sig_algorithm: str = "HMAC-SHA256"
21
+
22
+
23
+ class AgreementList(BaseModel):
24
+ records: list[Agreement]
25
+ limit: int
26
+ offset: int
@@ -0,0 +1,3 @@
1
+ from .handler import WebhookHandler, WebhookEvent
2
+
3
+ __all__ = ["WebhookHandler", "WebhookEvent"]
@@ -0,0 +1,59 @@
1
+ from __future__ import annotations
2
+
3
+ import hashlib
4
+ import hmac
5
+ import json
6
+ from dataclasses import dataclass
7
+ from datetime import datetime
8
+
9
+ from ..exceptions import AgentCheckError
10
+
11
+
12
+ @dataclass
13
+ class WebhookEvent:
14
+ id: str
15
+ type: str
16
+ created_at: str
17
+ data: dict
18
+
19
+
20
+ class WebhookHandler:
21
+ """Verify and parse incoming webhook events."""
22
+
23
+ def __init__(self, secret: str):
24
+ self._secret = secret
25
+
26
+ def verify_and_parse(self, body: str | bytes, signature: str) -> WebhookEvent:
27
+ """Verify HMAC signature and parse webhook payload.
28
+
29
+ Args:
30
+ body: Raw request body (str or bytes).
31
+ signature: Value from X-AgentCheck-Signature header.
32
+
33
+ Returns:
34
+ Parsed WebhookEvent.
35
+
36
+ Raises:
37
+ AgentCheckError: If signature is invalid.
38
+ """
39
+ if isinstance(body, str):
40
+ body_bytes = body.encode("utf-8")
41
+ else:
42
+ body_bytes = body
43
+
44
+ expected = hmac.new(
45
+ self._secret.encode("utf-8"),
46
+ body_bytes,
47
+ hashlib.sha256,
48
+ ).hexdigest()
49
+
50
+ if not hmac.compare_digest(expected, signature):
51
+ raise AgentCheckError("Invalid webhook signature", code="invalid_signature")
52
+
53
+ payload = json.loads(body_bytes)
54
+ return WebhookEvent(
55
+ id=payload["id"],
56
+ type=payload["type"],
57
+ created_at=payload["created_at"],
58
+ data=payload.get("data", {}),
59
+ )
@@ -0,0 +1,80 @@
1
+ Metadata-Version: 2.4
2
+ Name: agentcheck-sdk
3
+ Version: 0.1.0
4
+ Summary: Record what your AI agent is allowed to do
5
+ Author-email: AgentCheck <dev@agentcheck.io>
6
+ License: MIT
7
+ Project-URL: Homepage, https://agentcheck.io
8
+ Project-URL: Repository, https://github.com/agentcheck/agentcheck-python
9
+ Keywords: ai,agent,delegation,trust,authorization
10
+ Classifier: Development Status :: 3 - Alpha
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: License :: OSI Approved :: MIT License
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.9
15
+ Classifier: Programming Language :: Python :: 3.10
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Programming Language :: Python :: 3.13
19
+ Classifier: Topic :: Security
20
+ Requires-Python: >=3.9
21
+ Description-Content-Type: text/markdown
22
+ Requires-Dist: httpx>=0.25
23
+ Requires-Dist: pydantic>=2.0
24
+
25
+ # agentcheck
26
+
27
+ Record what your AI agent is allowed to do.
28
+
29
+ ## Install
30
+
31
+ ```bash
32
+ pip install agentcheck
33
+ ```
34
+
35
+ ## Quickstart
36
+
37
+ ```python
38
+ import agentcheck
39
+
40
+ agentcheck.init(api_key="ak_live_...", base_url="https://agentcheck.fly.dev")
41
+
42
+ proof = agentcheck.record(
43
+ agent="factory-bot",
44
+ scope="order parts under $10K, monitor equipment 24/7",
45
+ authorized_by="kim@factory.com"
46
+ )
47
+
48
+ # Kim gets an email, clicks "Approve"
49
+ # Anyone can verify: /api/v1/verify/{proof.id}
50
+
51
+ record = agentcheck.get(proof.id)
52
+ print(record.status) # "approved"
53
+ ```
54
+
55
+ ## API
56
+
57
+ | Function | Description |
58
+ |----------|-------------|
59
+ | `agentcheck.init(api_key, base_url)` | Initialize with your API key |
60
+ | `agentcheck.record(agent, scope, authorized_by)` | Create agreement |
61
+ | `agentcheck.get(id)` | Get agreement details |
62
+ | `agentcheck.list(status, agent)` | List agreements |
63
+ | `agentcheck.amend(id, new_scope)` | Amend agreement scope |
64
+
65
+ ## Sign Up
66
+
67
+ ```python
68
+ from agentcheck import Client
69
+
70
+ result = Client.signup(
71
+ email="dev@company.com",
72
+ company_name="My Company",
73
+ base_url="https://agentcheck.fly.dev"
74
+ )
75
+ print(result["api_key"]) # Save this - shown once only
76
+ ```
77
+
78
+ ## License
79
+
80
+ MIT
@@ -0,0 +1,13 @@
1
+ README.md
2
+ pyproject.toml
3
+ agentcheck/__init__.py
4
+ agentcheck/client.py
5
+ agentcheck/exceptions.py
6
+ agentcheck/models.py
7
+ agentcheck/webhook/__init__.py
8
+ agentcheck/webhook/handler.py
9
+ agentcheck_sdk.egg-info/PKG-INFO
10
+ agentcheck_sdk.egg-info/SOURCES.txt
11
+ agentcheck_sdk.egg-info/dependency_links.txt
12
+ agentcheck_sdk.egg-info/requires.txt
13
+ agentcheck_sdk.egg-info/top_level.txt
@@ -0,0 +1,2 @@
1
+ httpx>=0.25
2
+ pydantic>=2.0
@@ -0,0 +1 @@
1
+ agentcheck
@@ -0,0 +1,36 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68.0", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "agentcheck-sdk"
7
+ version = "0.1.0"
8
+ description = "Record what your AI agent is allowed to do"
9
+ readme = "README.md"
10
+ license = {text = "MIT"}
11
+ requires-python = ">=3.9"
12
+ authors = [{name = "AgentCheck", email = "dev@agentcheck.io"}]
13
+ keywords = ["ai", "agent", "delegation", "trust", "authorization"]
14
+ classifiers = [
15
+ "Development Status :: 3 - Alpha",
16
+ "Intended Audience :: Developers",
17
+ "License :: OSI Approved :: MIT License",
18
+ "Programming Language :: Python :: 3",
19
+ "Programming Language :: Python :: 3.9",
20
+ "Programming Language :: Python :: 3.10",
21
+ "Programming Language :: Python :: 3.11",
22
+ "Programming Language :: Python :: 3.12",
23
+ "Programming Language :: Python :: 3.13",
24
+ "Topic :: Security",
25
+ ]
26
+ dependencies = [
27
+ "httpx>=0.25",
28
+ "pydantic>=2.0",
29
+ ]
30
+
31
+ [project.urls]
32
+ Homepage = "https://agentcheck.io"
33
+ Repository = "https://github.com/agentcheck/agentcheck-python"
34
+
35
+ [tool.setuptools.packages.find]
36
+ include = ["agentcheck*"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+