treeship-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.
- treeship_sdk-0.1.0/.gitignore +40 -0
- treeship_sdk-0.1.0/PKG-INFO +87 -0
- treeship_sdk-0.1.0/README.md +64 -0
- treeship_sdk-0.1.0/pyproject.toml +33 -0
- treeship_sdk-0.1.0/treeship_sdk/__init__.py +5 -0
- treeship_sdk-0.1.0/treeship_sdk/async_client.py +162 -0
- treeship_sdk-0.1.0/treeship_sdk/client.py +203 -0
- treeship_sdk-0.1.0/treeship_sdk/py.typed +0 -0
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
|
2
|
+
|
|
3
|
+
# dependencies
|
|
4
|
+
/node_modules
|
|
5
|
+
/.pnp
|
|
6
|
+
.pnp.js
|
|
7
|
+
|
|
8
|
+
# testing
|
|
9
|
+
/coverage
|
|
10
|
+
|
|
11
|
+
# next.js
|
|
12
|
+
/.next/
|
|
13
|
+
/out/
|
|
14
|
+
|
|
15
|
+
# production
|
|
16
|
+
/build
|
|
17
|
+
/dist
|
|
18
|
+
|
|
19
|
+
# misc
|
|
20
|
+
.DS_Store
|
|
21
|
+
*.pem
|
|
22
|
+
.env
|
|
23
|
+
.env.local
|
|
24
|
+
.env.development.local
|
|
25
|
+
.env.test.local
|
|
26
|
+
.env.production.local
|
|
27
|
+
|
|
28
|
+
# debug
|
|
29
|
+
npm-debug.log*
|
|
30
|
+
yarn-debug.log*
|
|
31
|
+
yarn-error.log*
|
|
32
|
+
|
|
33
|
+
# IDE
|
|
34
|
+
.idea/
|
|
35
|
+
.vscode/
|
|
36
|
+
*.swp
|
|
37
|
+
*.swo
|
|
38
|
+
|
|
39
|
+
# vercel
|
|
40
|
+
.vercel
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: treeship-sdk
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Cryptographic attestations for AI agents
|
|
5
|
+
Project-URL: Homepage, https://treeship.dev
|
|
6
|
+
Project-URL: Documentation, https://docs.treeship.dev
|
|
7
|
+
Project-URL: Repository, https://github.com/treeship-dev/treeship
|
|
8
|
+
Author-email: Treeship <hello@treeship.dev>
|
|
9
|
+
License: MIT
|
|
10
|
+
Keywords: agents,ai,attestation,cryptography,treeship,verification
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
20
|
+
Requires-Python: >=3.9
|
|
21
|
+
Requires-Dist: httpx>=0.27.0
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
|
|
24
|
+
# treeship-sdk
|
|
25
|
+
|
|
26
|
+
Cryptographic attestations for AI agents.
|
|
27
|
+
|
|
28
|
+
## Installation
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
pip install treeship-sdk
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Quick Start
|
|
35
|
+
|
|
36
|
+
```python
|
|
37
|
+
from treeship import Treeship
|
|
38
|
+
|
|
39
|
+
# Initialize (uses TREESHIP_API_KEY and TREESHIP_AGENT env vars)
|
|
40
|
+
ts = Treeship()
|
|
41
|
+
|
|
42
|
+
# Create an attestation
|
|
43
|
+
result = ts.attest(
|
|
44
|
+
action="Approved loan application #12345",
|
|
45
|
+
inputs_hash=ts.hash({"customer_id": "cust_123", "amount": 50000})
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
print(f"Attestation: {result.attestation_id}")
|
|
49
|
+
print(f"Verify at: {result.url}")
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Environment Variables
|
|
53
|
+
|
|
54
|
+
| Variable | Description |
|
|
55
|
+
|----------|-------------|
|
|
56
|
+
| `TREESHIP_API_KEY` | Your API key (required for attest) |
|
|
57
|
+
| `TREESHIP_AGENT` | Default agent slug |
|
|
58
|
+
| `TREESHIP_API_URL` | API URL (default: https://api.treeship.dev) |
|
|
59
|
+
|
|
60
|
+
## Async Support
|
|
61
|
+
|
|
62
|
+
```python
|
|
63
|
+
from treeship import AsyncTreeship
|
|
64
|
+
|
|
65
|
+
async with AsyncTreeship() as ts:
|
|
66
|
+
result = await ts.attest(
|
|
67
|
+
action="Processed request",
|
|
68
|
+
inputs_hash=ts.hash(request_data)
|
|
69
|
+
)
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## Verification
|
|
73
|
+
|
|
74
|
+
```python
|
|
75
|
+
# Verify an attestation
|
|
76
|
+
result = ts.verify("attestation-id-here")
|
|
77
|
+
print(f"Valid: {result['valid']}")
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## Documentation
|
|
81
|
+
|
|
82
|
+
- [Full Documentation](https://docs.treeship.dev)
|
|
83
|
+
- [API Reference](https://docs.treeship.dev/api-reference)
|
|
84
|
+
|
|
85
|
+
## License
|
|
86
|
+
|
|
87
|
+
MIT
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# treeship-sdk
|
|
2
|
+
|
|
3
|
+
Cryptographic attestations for AI agents.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pip install treeship-sdk
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Quick Start
|
|
12
|
+
|
|
13
|
+
```python
|
|
14
|
+
from treeship import Treeship
|
|
15
|
+
|
|
16
|
+
# Initialize (uses TREESHIP_API_KEY and TREESHIP_AGENT env vars)
|
|
17
|
+
ts = Treeship()
|
|
18
|
+
|
|
19
|
+
# Create an attestation
|
|
20
|
+
result = ts.attest(
|
|
21
|
+
action="Approved loan application #12345",
|
|
22
|
+
inputs_hash=ts.hash({"customer_id": "cust_123", "amount": 50000})
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
print(f"Attestation: {result.attestation_id}")
|
|
26
|
+
print(f"Verify at: {result.url}")
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Environment Variables
|
|
30
|
+
|
|
31
|
+
| Variable | Description |
|
|
32
|
+
|----------|-------------|
|
|
33
|
+
| `TREESHIP_API_KEY` | Your API key (required for attest) |
|
|
34
|
+
| `TREESHIP_AGENT` | Default agent slug |
|
|
35
|
+
| `TREESHIP_API_URL` | API URL (default: https://api.treeship.dev) |
|
|
36
|
+
|
|
37
|
+
## Async Support
|
|
38
|
+
|
|
39
|
+
```python
|
|
40
|
+
from treeship import AsyncTreeship
|
|
41
|
+
|
|
42
|
+
async with AsyncTreeship() as ts:
|
|
43
|
+
result = await ts.attest(
|
|
44
|
+
action="Processed request",
|
|
45
|
+
inputs_hash=ts.hash(request_data)
|
|
46
|
+
)
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Verification
|
|
50
|
+
|
|
51
|
+
```python
|
|
52
|
+
# Verify an attestation
|
|
53
|
+
result = ts.verify("attestation-id-here")
|
|
54
|
+
print(f"Valid: {result['valid']}")
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Documentation
|
|
58
|
+
|
|
59
|
+
- [Full Documentation](https://docs.treeship.dev)
|
|
60
|
+
- [API Reference](https://docs.treeship.dev/api-reference)
|
|
61
|
+
|
|
62
|
+
## License
|
|
63
|
+
|
|
64
|
+
MIT
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "treeship-sdk"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Cryptographic attestations for AI agents"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = { text = "MIT" }
|
|
11
|
+
requires-python = ">=3.9"
|
|
12
|
+
dependencies = ["httpx>=0.27.0"]
|
|
13
|
+
keywords = ["treeship", "attestation", "ai", "agents", "cryptography", "verification"]
|
|
14
|
+
authors = [{ name = "Treeship", email = "hello@treeship.dev" }]
|
|
15
|
+
classifiers = [
|
|
16
|
+
"Development Status :: 4 - Beta",
|
|
17
|
+
"Intended Audience :: Developers",
|
|
18
|
+
"License :: OSI Approved :: MIT License",
|
|
19
|
+
"Programming Language :: Python :: 3",
|
|
20
|
+
"Programming Language :: Python :: 3.9",
|
|
21
|
+
"Programming Language :: Python :: 3.10",
|
|
22
|
+
"Programming Language :: Python :: 3.11",
|
|
23
|
+
"Programming Language :: Python :: 3.12",
|
|
24
|
+
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
25
|
+
]
|
|
26
|
+
|
|
27
|
+
[project.urls]
|
|
28
|
+
Homepage = "https://treeship.dev"
|
|
29
|
+
Documentation = "https://docs.treeship.dev"
|
|
30
|
+
Repository = "https://github.com/treeship-dev/treeship"
|
|
31
|
+
|
|
32
|
+
[tool.hatch.build.targets.wheel]
|
|
33
|
+
packages = ["treeship_sdk"]
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import hashlib
|
|
4
|
+
import json
|
|
5
|
+
import os
|
|
6
|
+
from typing import Any, Optional, Dict
|
|
7
|
+
|
|
8
|
+
import httpx
|
|
9
|
+
|
|
10
|
+
from .client import AttestResult, _get_api_url
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class AsyncTreeship:
|
|
14
|
+
"""
|
|
15
|
+
Async Treeship client for creating cryptographic attestations.
|
|
16
|
+
|
|
17
|
+
Example:
|
|
18
|
+
>>> from treeship import AsyncTreeship
|
|
19
|
+
>>> async with AsyncTreeship() as ts:
|
|
20
|
+
... result = await ts.attest(
|
|
21
|
+
... action="Approved loan application #12345",
|
|
22
|
+
... inputs_hash=ts.hash({"customer_id": "cust_123"})
|
|
23
|
+
... )
|
|
24
|
+
... print(result.url)
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
def __init__(
|
|
28
|
+
self,
|
|
29
|
+
api_key: Optional[str] = None,
|
|
30
|
+
agent: Optional[str] = None,
|
|
31
|
+
api_url: Optional[str] = None,
|
|
32
|
+
):
|
|
33
|
+
"""
|
|
34
|
+
Initialize the async Treeship client.
|
|
35
|
+
|
|
36
|
+
Args:
|
|
37
|
+
api_key: API key for authentication. Defaults to TREESHIP_API_KEY env var.
|
|
38
|
+
agent: Default agent slug. Defaults to TREESHIP_AGENT env var.
|
|
39
|
+
api_url: API base URL. Defaults to TREESHIP_API_URL env var or https://api.treeship.dev.
|
|
40
|
+
"""
|
|
41
|
+
self.api_key = api_key or os.environ.get("TREESHIP_API_KEY", "")
|
|
42
|
+
self.agent = agent or os.getenv("TREESHIP_AGENT", "")
|
|
43
|
+
self._api_url = api_url or _get_api_url()
|
|
44
|
+
self._client = httpx.AsyncClient(
|
|
45
|
+
base_url=self._api_url,
|
|
46
|
+
headers={
|
|
47
|
+
"Authorization": f"Bearer {self.api_key}",
|
|
48
|
+
"User-Agent": "treeship-sdk/0.1.0",
|
|
49
|
+
},
|
|
50
|
+
timeout=30.0,
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
@staticmethod
|
|
54
|
+
def hash(data: Any) -> str:
|
|
55
|
+
"""
|
|
56
|
+
Create a SHA256 hash of data for use as inputs_hash.
|
|
57
|
+
|
|
58
|
+
Args:
|
|
59
|
+
data: Any JSON-serializable data, string, or bytes.
|
|
60
|
+
|
|
61
|
+
Returns:
|
|
62
|
+
Hex-encoded SHA256 hash.
|
|
63
|
+
"""
|
|
64
|
+
if isinstance(data, (dict, list)):
|
|
65
|
+
data = json.dumps(data, sort_keys=True, separators=(",", ":"))
|
|
66
|
+
if isinstance(data, str):
|
|
67
|
+
data = data.encode()
|
|
68
|
+
return hashlib.sha256(data).hexdigest()
|
|
69
|
+
|
|
70
|
+
async def attest(
|
|
71
|
+
self,
|
|
72
|
+
action: str,
|
|
73
|
+
inputs_hash: Optional[str] = None,
|
|
74
|
+
agent: Optional[str] = None,
|
|
75
|
+
metadata: Optional[Dict[str, Any]] = None,
|
|
76
|
+
) -> AttestResult:
|
|
77
|
+
"""
|
|
78
|
+
Create a cryptographic attestation.
|
|
79
|
+
|
|
80
|
+
Args:
|
|
81
|
+
action: Human-readable description of the action.
|
|
82
|
+
inputs_hash: SHA256 hash of inputs. If not provided, hashes the action string.
|
|
83
|
+
agent: Agent slug. Uses default agent if not provided.
|
|
84
|
+
metadata: Optional key-value metadata.
|
|
85
|
+
|
|
86
|
+
Returns:
|
|
87
|
+
AttestResult with attestation details and verification URL.
|
|
88
|
+
"""
|
|
89
|
+
slug = agent or self.agent
|
|
90
|
+
if not slug:
|
|
91
|
+
raise ValueError(
|
|
92
|
+
"Agent slug required: pass agent= or set TREESHIP_AGENT environment variable"
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
if not self.api_key:
|
|
96
|
+
raise ValueError(
|
|
97
|
+
"API key required: pass api_key= or set TREESHIP_API_KEY environment variable"
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
response = await self._client.post(
|
|
101
|
+
"/v1/attest",
|
|
102
|
+
json={
|
|
103
|
+
"agent_slug": slug,
|
|
104
|
+
"action": action,
|
|
105
|
+
"inputs_hash": inputs_hash or self.hash(action),
|
|
106
|
+
"metadata": metadata or {},
|
|
107
|
+
},
|
|
108
|
+
)
|
|
109
|
+
response.raise_for_status()
|
|
110
|
+
return AttestResult.from_dict(response.json())
|
|
111
|
+
|
|
112
|
+
async def verify(self, attestation_id: str) -> Dict[str, Any]:
|
|
113
|
+
"""
|
|
114
|
+
Verify an attestation by ID.
|
|
115
|
+
|
|
116
|
+
Args:
|
|
117
|
+
attestation_id: The attestation UUID.
|
|
118
|
+
|
|
119
|
+
Returns:
|
|
120
|
+
Dict with verification result including 'valid' boolean.
|
|
121
|
+
"""
|
|
122
|
+
response = await self._client.get(f"/v1/verify/{attestation_id}")
|
|
123
|
+
response.raise_for_status()
|
|
124
|
+
return response.json()
|
|
125
|
+
|
|
126
|
+
async def get_agent(self, slug: Optional[str] = None) -> Dict[str, Any]:
|
|
127
|
+
"""
|
|
128
|
+
Get agent feed with recent attestations.
|
|
129
|
+
|
|
130
|
+
Args:
|
|
131
|
+
slug: Agent slug. Uses default agent if not provided.
|
|
132
|
+
|
|
133
|
+
Returns:
|
|
134
|
+
Dict with agent info and attestations list.
|
|
135
|
+
"""
|
|
136
|
+
agent_slug = slug or self.agent
|
|
137
|
+
if not agent_slug:
|
|
138
|
+
raise ValueError("Agent slug required")
|
|
139
|
+
response = await self._client.get(f"/v1/agent/{agent_slug}")
|
|
140
|
+
response.raise_for_status()
|
|
141
|
+
return response.json()
|
|
142
|
+
|
|
143
|
+
async def get_pubkey(self) -> Dict[str, Any]:
|
|
144
|
+
"""
|
|
145
|
+
Get the public signing key for independent verification.
|
|
146
|
+
|
|
147
|
+
Returns:
|
|
148
|
+
Dict with key_id, algorithm, and public_key_pem.
|
|
149
|
+
"""
|
|
150
|
+
response = await self._client.get("/v1/pubkey")
|
|
151
|
+
response.raise_for_status()
|
|
152
|
+
return response.json()
|
|
153
|
+
|
|
154
|
+
async def close(self) -> None:
|
|
155
|
+
"""Close the HTTP client."""
|
|
156
|
+
await self._client.aclose()
|
|
157
|
+
|
|
158
|
+
async def __aenter__(self) -> AsyncTreeship:
|
|
159
|
+
return self
|
|
160
|
+
|
|
161
|
+
async def __aexit__(self, *args: Any) -> None:
|
|
162
|
+
await self.close()
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import hashlib
|
|
4
|
+
import json
|
|
5
|
+
import os
|
|
6
|
+
from dataclasses import dataclass
|
|
7
|
+
from typing import Any, Optional, Dict
|
|
8
|
+
|
|
9
|
+
import httpx
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def _get_api_url() -> str:
|
|
13
|
+
return os.getenv("TREESHIP_API_URL", "https://api.treeship.dev")
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@dataclass
|
|
17
|
+
class AttestResult:
|
|
18
|
+
"""Result of creating an attestation."""
|
|
19
|
+
|
|
20
|
+
attestation_id: str
|
|
21
|
+
signature: str
|
|
22
|
+
payload_hash: str
|
|
23
|
+
key_id: str
|
|
24
|
+
timestamp: str
|
|
25
|
+
url: str
|
|
26
|
+
verify_command: str
|
|
27
|
+
agent_slug: str
|
|
28
|
+
action: str
|
|
29
|
+
inputs_hash: str
|
|
30
|
+
|
|
31
|
+
@classmethod
|
|
32
|
+
def from_dict(cls, d: Dict[str, Any]) -> AttestResult:
|
|
33
|
+
return cls(
|
|
34
|
+
attestation_id=d["attestation_id"],
|
|
35
|
+
signature=d["signature"],
|
|
36
|
+
payload_hash=d["payload_hash"],
|
|
37
|
+
key_id=d["key_id"],
|
|
38
|
+
timestamp=d["timestamp"],
|
|
39
|
+
url=d["public_url"],
|
|
40
|
+
verify_command=d["verify_command"],
|
|
41
|
+
agent_slug=d["agent_slug"],
|
|
42
|
+
action=d["action"],
|
|
43
|
+
inputs_hash=d["inputs_hash"],
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class Treeship:
|
|
48
|
+
"""
|
|
49
|
+
Treeship client for creating cryptographic attestations.
|
|
50
|
+
|
|
51
|
+
Example:
|
|
52
|
+
>>> from treeship import Treeship
|
|
53
|
+
>>> ts = Treeship()
|
|
54
|
+
>>> result = ts.attest(
|
|
55
|
+
... action="Approved loan application #12345",
|
|
56
|
+
... inputs_hash=ts.hash({"customer_id": "cust_123"})
|
|
57
|
+
... )
|
|
58
|
+
>>> print(result.url)
|
|
59
|
+
"""
|
|
60
|
+
|
|
61
|
+
def __init__(
|
|
62
|
+
self,
|
|
63
|
+
api_key: Optional[str] = None,
|
|
64
|
+
agent: Optional[str] = None,
|
|
65
|
+
api_url: Optional[str] = None,
|
|
66
|
+
):
|
|
67
|
+
"""
|
|
68
|
+
Initialize the Treeship client.
|
|
69
|
+
|
|
70
|
+
Args:
|
|
71
|
+
api_key: API key for authentication. Defaults to TREESHIP_API_KEY env var.
|
|
72
|
+
agent: Default agent slug. Defaults to TREESHIP_AGENT env var.
|
|
73
|
+
api_url: API base URL. Defaults to TREESHIP_API_URL env var or https://api.treeship.dev.
|
|
74
|
+
"""
|
|
75
|
+
self.api_key = api_key or os.environ.get("TREESHIP_API_KEY", "")
|
|
76
|
+
self.agent = agent or os.getenv("TREESHIP_AGENT", "")
|
|
77
|
+
self._api_url = api_url or _get_api_url()
|
|
78
|
+
self._client = httpx.Client(
|
|
79
|
+
base_url=self._api_url,
|
|
80
|
+
headers={
|
|
81
|
+
"Authorization": f"Bearer {self.api_key}",
|
|
82
|
+
"User-Agent": "treeship-sdk/0.1.0",
|
|
83
|
+
},
|
|
84
|
+
timeout=30.0,
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
@staticmethod
|
|
88
|
+
def hash(data: Any) -> str:
|
|
89
|
+
"""
|
|
90
|
+
Create a SHA256 hash of data for use as inputs_hash.
|
|
91
|
+
|
|
92
|
+
Args:
|
|
93
|
+
data: Any JSON-serializable data, string, or bytes.
|
|
94
|
+
|
|
95
|
+
Returns:
|
|
96
|
+
Hex-encoded SHA256 hash.
|
|
97
|
+
"""
|
|
98
|
+
if isinstance(data, (dict, list)):
|
|
99
|
+
data = json.dumps(data, sort_keys=True, separators=(",", ":"))
|
|
100
|
+
if isinstance(data, str):
|
|
101
|
+
data = data.encode()
|
|
102
|
+
return hashlib.sha256(data).hexdigest()
|
|
103
|
+
|
|
104
|
+
def attest(
|
|
105
|
+
self,
|
|
106
|
+
action: str,
|
|
107
|
+
inputs_hash: Optional[str] = None,
|
|
108
|
+
agent: Optional[str] = None,
|
|
109
|
+
metadata: Optional[Dict[str, Any]] = None,
|
|
110
|
+
) -> AttestResult:
|
|
111
|
+
"""
|
|
112
|
+
Create a cryptographic attestation.
|
|
113
|
+
|
|
114
|
+
Args:
|
|
115
|
+
action: Human-readable description of the action.
|
|
116
|
+
inputs_hash: SHA256 hash of inputs. If not provided, hashes the action string.
|
|
117
|
+
agent: Agent slug. Uses default agent if not provided.
|
|
118
|
+
metadata: Optional key-value metadata.
|
|
119
|
+
|
|
120
|
+
Returns:
|
|
121
|
+
AttestResult with attestation details and verification URL.
|
|
122
|
+
|
|
123
|
+
Raises:
|
|
124
|
+
ValueError: If no agent slug is available.
|
|
125
|
+
httpx.HTTPStatusError: If the API request fails.
|
|
126
|
+
"""
|
|
127
|
+
slug = agent or self.agent
|
|
128
|
+
if not slug:
|
|
129
|
+
raise ValueError(
|
|
130
|
+
"Agent slug required: pass agent= or set TREESHIP_AGENT environment variable"
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
if not self.api_key:
|
|
134
|
+
raise ValueError(
|
|
135
|
+
"API key required: pass api_key= or set TREESHIP_API_KEY environment variable"
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
response = self._client.post(
|
|
139
|
+
"/v1/attest",
|
|
140
|
+
json={
|
|
141
|
+
"agent_slug": slug,
|
|
142
|
+
"action": action,
|
|
143
|
+
"inputs_hash": inputs_hash or self.hash(action),
|
|
144
|
+
"metadata": metadata or {},
|
|
145
|
+
},
|
|
146
|
+
)
|
|
147
|
+
response.raise_for_status()
|
|
148
|
+
return AttestResult.from_dict(response.json())
|
|
149
|
+
|
|
150
|
+
def verify(self, attestation_id: str) -> Dict[str, Any]:
|
|
151
|
+
"""
|
|
152
|
+
Verify an attestation by ID.
|
|
153
|
+
|
|
154
|
+
Args:
|
|
155
|
+
attestation_id: The attestation UUID.
|
|
156
|
+
|
|
157
|
+
Returns:
|
|
158
|
+
Dict with verification result including 'valid' boolean.
|
|
159
|
+
|
|
160
|
+
Raises:
|
|
161
|
+
httpx.HTTPStatusError: If the attestation is not found or request fails.
|
|
162
|
+
"""
|
|
163
|
+
response = self._client.get(f"/v1/verify/{attestation_id}")
|
|
164
|
+
response.raise_for_status()
|
|
165
|
+
return response.json()
|
|
166
|
+
|
|
167
|
+
def get_agent(self, slug: Optional[str] = None) -> Dict[str, Any]:
|
|
168
|
+
"""
|
|
169
|
+
Get agent feed with recent attestations.
|
|
170
|
+
|
|
171
|
+
Args:
|
|
172
|
+
slug: Agent slug. Uses default agent if not provided.
|
|
173
|
+
|
|
174
|
+
Returns:
|
|
175
|
+
Dict with agent info and attestations list.
|
|
176
|
+
"""
|
|
177
|
+
agent_slug = slug or self.agent
|
|
178
|
+
if not agent_slug:
|
|
179
|
+
raise ValueError("Agent slug required")
|
|
180
|
+
response = self._client.get(f"/v1/agent/{agent_slug}")
|
|
181
|
+
response.raise_for_status()
|
|
182
|
+
return response.json()
|
|
183
|
+
|
|
184
|
+
def get_pubkey(self) -> Dict[str, Any]:
|
|
185
|
+
"""
|
|
186
|
+
Get the public signing key for independent verification.
|
|
187
|
+
|
|
188
|
+
Returns:
|
|
189
|
+
Dict with key_id, algorithm, and public_key_pem.
|
|
190
|
+
"""
|
|
191
|
+
response = self._client.get("/v1/pubkey")
|
|
192
|
+
response.raise_for_status()
|
|
193
|
+
return response.json()
|
|
194
|
+
|
|
195
|
+
def close(self) -> None:
|
|
196
|
+
"""Close the HTTP client."""
|
|
197
|
+
self._client.close()
|
|
198
|
+
|
|
199
|
+
def __enter__(self) -> Treeship:
|
|
200
|
+
return self
|
|
201
|
+
|
|
202
|
+
def __exit__(self, *args: Any) -> None:
|
|
203
|
+
self.close()
|
|
File without changes
|