agentworth 0.1.8__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,15 @@
1
+ node_modules/
2
+ dist/
3
+ *.log
4
+ .env
5
+ .DS_Store
6
+ agentworth.db
7
+ agentworth.db-*
8
+ clients/rust/target/
9
+ clients/rust/Cargo.lock
10
+ clients/c/example
11
+ clients/c/example.exe
12
+ __pycache__/
13
+ *.pyc
14
+ clients/python/dist/
15
+ *.egg-info/
@@ -0,0 +1,33 @@
1
+ Metadata-Version: 2.4
2
+ Name: agentworth
3
+ Version: 0.1.8
4
+ Summary: Python client for the AgentWorth governance gate (REST over the HTTP ingress)
5
+ Author: General Liquidity
6
+ License: MIT
7
+ Keywords: agentic-payments,agentworth,governance
8
+ Requires-Python: >=3.8
9
+ Description-Content-Type: text/markdown
10
+
11
+ # agentworth (Python client)
12
+
13
+ A thin REST client for the [AgentWorth](https://github.com/general-liquidity/agentworth)
14
+ governance gate. Point it at a running `agentworth serve`; every payment it submits
15
+ runs through the same gate (auto-execute inside a mandate, park for operator approval,
16
+ or block). A `blocked` outcome is a normal result, not an error. Standard library
17
+ only - no dependencies.
18
+
19
+ ```python
20
+ from agentworth import AgentWorthClient
21
+
22
+ os = AgentWorthClient("http://127.0.0.1:8787", token="...")
23
+ res = os.pay(payee="tesco", payee_class="groceries", amount=8000, # minor-units
24
+ rationale="the weekly grocery shop")
25
+ print(res["outcome"]) # settled | pending | blocked | failed
26
+ print(os.status(), os.ready())
27
+ ```
28
+
29
+ Idempotency keys are generated per `pay()` (override with `idempotency_key=`). The
30
+ full-feature surface is the TypeScript SDK (`@general-liquidity/agentworth`); this
31
+ is for Python hosts that talk to a running ingress.
32
+
33
+ License: MIT.
@@ -0,0 +1,23 @@
1
+ # agentworth (Python client)
2
+
3
+ A thin REST client for the [AgentWorth](https://github.com/general-liquidity/agentworth)
4
+ governance gate. Point it at a running `agentworth serve`; every payment it submits
5
+ runs through the same gate (auto-execute inside a mandate, park for operator approval,
6
+ or block). A `blocked` outcome is a normal result, not an error. Standard library
7
+ only - no dependencies.
8
+
9
+ ```python
10
+ from agentworth import AgentWorthClient
11
+
12
+ os = AgentWorthClient("http://127.0.0.1:8787", token="...")
13
+ res = os.pay(payee="tesco", payee_class="groceries", amount=8000, # minor-units
14
+ rationale="the weekly grocery shop")
15
+ print(res["outcome"]) # settled | pending | blocked | failed
16
+ print(os.status(), os.ready())
17
+ ```
18
+
19
+ Idempotency keys are generated per `pay()` (override with `idempotency_key=`). The
20
+ full-feature surface is the TypeScript SDK (`@general-liquidity/agentworth`); this
21
+ is for Python hosts that talk to a running ingress.
22
+
23
+ License: MIT.
@@ -0,0 +1,103 @@
1
+ """AgentWorth Python client — a thin REST client over the HTTP ingress.
2
+
3
+ The ingress runs the SAME gate as everything else, so this client adds no
4
+ authority: a payment it submits is auto-executed inside a mandate, parked for
5
+ operator approval, or blocked. Standard-library only (no dependencies).
6
+
7
+ from agentworth import AgentWorthClient
8
+
9
+ os = AgentWorthClient("http://127.0.0.1:8787", token="...")
10
+ result = os.pay(payee="tesco", payee_class="groceries", amount=8000,
11
+ rationale="the weekly grocery shop")
12
+ print(result["outcome"]) # "settled" | "pending" | "blocked" | "failed"
13
+ """
14
+
15
+ from __future__ import annotations
16
+
17
+ import json
18
+ import uuid
19
+ import urllib.error
20
+ import urllib.request
21
+ from typing import Any, Optional
22
+
23
+ __all__ = ["AgentWorthClient", "AgentWorthError"]
24
+
25
+
26
+ class AgentWorthError(Exception):
27
+ """Raised on a transport error (not on a gate 'blocked' — that's a normal result)."""
28
+
29
+
30
+ class AgentWorthClient:
31
+ def __init__(self, base_url: str = "http://127.0.0.1:8787", token: Optional[str] = None):
32
+ self.base_url = base_url.rstrip("/")
33
+ self.token = token
34
+
35
+ def _request(self, method: str, path: str, body: Any = None, headers: Optional[dict] = None):
36
+ data = json.dumps(body).encode() if body is not None else None
37
+ h = {"content-type": "application/json"}
38
+ if self.token:
39
+ h["authorization"] = f"Bearer {self.token}"
40
+ if headers:
41
+ h.update(headers)
42
+ req = urllib.request.Request(self.base_url + path, data=data, method=method, headers=h)
43
+ try:
44
+ with urllib.request.urlopen(req) as resp:
45
+ payload = resp.read()
46
+ return resp.status, (json.loads(payload) if payload else None)
47
+ except urllib.error.HTTPError as e:
48
+ payload = e.read()
49
+ # 403/401/413/429 carry a JSON body too — return it, don't raise.
50
+ return e.code, (json.loads(payload) if payload else None)
51
+ except urllib.error.URLError as e:
52
+ raise AgentWorthError(f"transport error: {e}") from e
53
+
54
+ def pay(
55
+ self,
56
+ payee: str,
57
+ payee_class: str,
58
+ amount: int,
59
+ currency: str = "GBP",
60
+ rail: str = "card",
61
+ rationale: str = "",
62
+ idempotency_key: Optional[str] = None,
63
+ ) -> dict:
64
+ """Submit a payment intent. Returns the gate result
65
+ {intentId, outcome, reasons, receiptId, verified}. `amount` is minor-units."""
66
+ headers = {"idempotency-key": idempotency_key or str(uuid.uuid4())}
67
+ _, body = self._request(
68
+ "POST",
69
+ "/payment-intent",
70
+ {
71
+ "payee": payee,
72
+ "payeeClass": payee_class,
73
+ "amount": amount,
74
+ "currency": currency,
75
+ "rail": rail,
76
+ "rationale": rationale,
77
+ },
78
+ headers,
79
+ )
80
+ return body or {}
81
+
82
+ def status(self) -> dict:
83
+ """Kill-switch / circuit-breaker state."""
84
+ return self._request("GET", "/status")[1] or {}
85
+
86
+ def ready(self) -> dict:
87
+ """Readiness probe."""
88
+ return self._request("GET", "/ready")[1] or {}
89
+
90
+ def openapi(self) -> dict:
91
+ """The served OpenAPI 3.1 document."""
92
+ return self._request("GET", "/openapi.json")[1] or {}
93
+
94
+ def get_disclosure(self) -> dict:
95
+ """Fetch this node's signed Verifiable Agency disclosure (public, no auth)."""
96
+ return self._request("GET", "/.well-known/agent-disclosure")[1] or {}
97
+
98
+ def verify_disclosure(self, disclosure: dict) -> dict:
99
+ """Verifier-as-a-service: submit a signed disclosure, get a verdict
100
+ {decision, tier, checks, reasons, cost}. Lets a heterogeneous counterparty
101
+ verify a peer without implementing ed25519 itself. A "refuse" decision is a
102
+ normal result, not an error."""
103
+ return self._request("POST", "/verify-disclosure", disclosure)[1] or {}
@@ -0,0 +1,17 @@
1
+ [project]
2
+ name = "agentworth"
3
+ version = "0.1.8"
4
+ description = "Python client for the AgentWorth governance gate (REST over the HTTP ingress)"
5
+ readme = "README.md"
6
+ requires-python = ">=3.8"
7
+ license = { text = "MIT" }
8
+ authors = [{ name = "General Liquidity" }]
9
+ keywords = ["agentic-payments", "governance", "agentworth"]
10
+ dependencies = []
11
+
12
+ [build-system]
13
+ requires = ["hatchling"]
14
+ build-backend = "hatchling.build"
15
+
16
+ [tool.hatch.build.targets.wheel]
17
+ only-include = ["agentworth.py"]