a2al 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.
a2al-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,64 @@
1
+ Metadata-Version: 2.4
2
+ Name: a2al
3
+ Version: 0.1.0
4
+ Summary: Python client for a2al: spawn a2ald and call its REST API
5
+ Author: A2AL Authors
6
+ License: MPL-2.0
7
+ Project-URL: Homepage, https://github.com/a2al/a2al
8
+ Project-URL: Repository, https://github.com/a2al/a2al
9
+ Project-URL: Bug Tracker, https://github.com/a2al/a2al/issues
10
+ Keywords: a2al,a2a,agent,ai,sidecar
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: License :: OSI Approved :: Mozilla Public License 2.0 (MPL 2.0)
14
+ Classifier: Programming Language :: Python :: 3
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: Operating System :: OS Independent
20
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
21
+ Requires-Python: >=3.10
22
+ Description-Content-Type: text/markdown
23
+
24
+ # a2al (Python)
25
+
26
+ Python client for [a2al](https://github.com/a2al/a2al): spawns a local `a2ald` daemon and exposes a typed REST client for its API.
27
+
28
+ ## Installation
29
+
30
+ ```sh
31
+ pip install a2al
32
+ ```
33
+
34
+ Pre-built `a2ald` binaries are bundled inside platform wheels for:
35
+
36
+ | Platform | Architecture |
37
+ |----------|-------------|
38
+ | Linux | x86_64, arm64 |
39
+ | macOS | x86_64, arm64 |
40
+ | Windows | x86_64 |
41
+
42
+ On unsupported platforms, install `a2ald` manually and ensure it is on `PATH`, or set `A2ALD_PATH` to the executable path.
43
+
44
+ ## Usage
45
+
46
+ ```python
47
+ from a2al import Daemon, Client
48
+
49
+ with Daemon() as d:
50
+ c = Client(d.api_base, token=d.api_token)
51
+ print(c.health())
52
+ ```
53
+
54
+ ## Environment Variables
55
+
56
+ | Variable | Description |
57
+ |----------|-------------|
58
+ | `A2ALD_PATH` | Override path to the `a2ald` executable |
59
+ | `A2AL_API_TOKEN` | Bearer token when the daemon enforces auth |
60
+
61
+ ## Requirements
62
+
63
+ - Python 3.10+
64
+ - No third-party dependencies (standard library only)
a2al-0.1.0/README.md ADDED
@@ -0,0 +1,41 @@
1
+ # a2al (Python)
2
+
3
+ Python client for [a2al](https://github.com/a2al/a2al): spawns a local `a2ald` daemon and exposes a typed REST client for its API.
4
+
5
+ ## Installation
6
+
7
+ ```sh
8
+ pip install a2al
9
+ ```
10
+
11
+ Pre-built `a2ald` binaries are bundled inside platform wheels for:
12
+
13
+ | Platform | Architecture |
14
+ |----------|-------------|
15
+ | Linux | x86_64, arm64 |
16
+ | macOS | x86_64, arm64 |
17
+ | Windows | x86_64 |
18
+
19
+ On unsupported platforms, install `a2ald` manually and ensure it is on `PATH`, or set `A2ALD_PATH` to the executable path.
20
+
21
+ ## Usage
22
+
23
+ ```python
24
+ from a2al import Daemon, Client
25
+
26
+ with Daemon() as d:
27
+ c = Client(d.api_base, token=d.api_token)
28
+ print(c.health())
29
+ ```
30
+
31
+ ## Environment Variables
32
+
33
+ | Variable | Description |
34
+ |----------|-------------|
35
+ | `A2ALD_PATH` | Override path to the `a2ald` executable |
36
+ | `A2AL_API_TOKEN` | Bearer token when the daemon enforces auth |
37
+
38
+ ## Requirements
39
+
40
+ - Python 3.10+
41
+ - No third-party dependencies (standard library only)
@@ -0,0 +1,36 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "a2al"
7
+ version = "0.1.0"
8
+ description = "Python client for a2al: spawn a2ald and call its REST API"
9
+ readme = "README.md"
10
+ requires-python = ">=3.10"
11
+ license = { text = "MPL-2.0" }
12
+ authors = [{ name = "A2AL Authors" }]
13
+ keywords = ["a2al", "a2a", "agent", "ai", "sidecar"]
14
+ classifiers = [
15
+ "Development Status :: 4 - Beta",
16
+ "Intended Audience :: Developers",
17
+ "License :: OSI Approved :: Mozilla Public License 2.0 (MPL 2.0)",
18
+ "Programming Language :: Python :: 3",
19
+ "Programming Language :: Python :: 3.10",
20
+ "Programming Language :: Python :: 3.11",
21
+ "Programming Language :: Python :: 3.12",
22
+ "Programming Language :: Python :: 3.13",
23
+ "Operating System :: OS Independent",
24
+ "Topic :: Software Development :: Libraries :: Python Modules",
25
+ ]
26
+
27
+ [project.urls]
28
+ Homepage = "https://github.com/a2al/a2al"
29
+ Repository = "https://github.com/a2al/a2al"
30
+ "Bug Tracker" = "https://github.com/a2al/a2al/issues"
31
+
32
+ [tool.setuptools.packages.find]
33
+ where = ["src"]
34
+
35
+ [tool.setuptools.package-data]
36
+ a2al = ["bin/*"]
a2al-0.1.0/setup.cfg ADDED
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,5 @@
1
+ """A2AL Python sidecar for the a2ald daemon."""
2
+
3
+ from ._sidecar import Client, Daemon
4
+
5
+ __all__ = ["Client", "Daemon"]
@@ -0,0 +1,187 @@
1
+ from __future__ import annotations
2
+
3
+ import atexit
4
+ import json
5
+ import os
6
+ import shutil
7
+ import socket
8
+ import subprocess
9
+ import sys
10
+ import tempfile
11
+ import time
12
+ import urllib.error
13
+ import urllib.request
14
+ from pathlib import Path
15
+ from typing import Any, Mapping, Optional
16
+
17
+
18
+ def _find_exe() -> str:
19
+ exe = "a2ald.exe" if sys.platform == "win32" else "a2ald"
20
+ embedded = Path(__file__).parent / "bin" / exe
21
+ if embedded.is_file() and os.access(str(embedded), os.X_OK):
22
+ return str(embedded)
23
+ return exe
24
+
25
+
26
+ def _free_port() -> int:
27
+ s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
28
+ s.bind(("127.0.0.1", 0))
29
+ _host, port = s.getsockname()
30
+ s.close()
31
+ return int(port)
32
+
33
+
34
+ class Daemon:
35
+ """Runs a2ald with a temp data dir and dynamic API port.
36
+
37
+ Usage (recommended — ensures cleanup on exit)::
38
+
39
+ with Daemon() as d:
40
+ c = Client(d.api_base, token=d.api_token)
41
+ ...
42
+
43
+ Alternatively call ``start()`` / ``close()`` explicitly, or rely on the
44
+ ``atexit`` handler registered by ``start()`` as a fallback.
45
+
46
+ Note: ``_free_port`` binds and releases a port before passing it to a2ald,
47
+ so a brief race window exists. In practice this is benign for local loopback.
48
+ """
49
+
50
+ def __init__(
51
+ self,
52
+ a2ald_exe: Optional[str] = None,
53
+ api_token: Optional[str] = None,
54
+ extra_args: Optional[list[str]] = None,
55
+ ) -> None:
56
+ self._exe = a2ald_exe or os.environ.get("A2ALD_PATH") or _find_exe()
57
+ self._api_token = api_token if api_token is not None else os.environ.get("A2AL_API_TOKEN")
58
+ self._extra = extra_args or []
59
+ self._proc: Optional[subprocess.Popen[bytes]] = None
60
+ self._dd: Optional[str] = None
61
+ self.api_base: Optional[str] = None
62
+
63
+ @property
64
+ def api_token(self) -> Optional[str]:
65
+ return self._api_token
66
+
67
+ def start(self, timeout: float = 45.0) -> None:
68
+ if self._proc is not None:
69
+ return
70
+ self._dd = tempfile.mkdtemp(prefix="a2al-")
71
+ port = _free_port()
72
+ self.api_base = f"http://127.0.0.1:{port}"
73
+ args = [
74
+ self._exe,
75
+ "--data-dir",
76
+ self._dd,
77
+ "--api-addr",
78
+ f"127.0.0.1:{port}",
79
+ *self._extra,
80
+ ]
81
+ env = os.environ.copy()
82
+ if self._api_token:
83
+ env["A2AL_API_TOKEN"] = self._api_token
84
+ self._proc = subprocess.Popen(
85
+ args,
86
+ stdout=subprocess.DEVNULL,
87
+ stderr=subprocess.DEVNULL,
88
+ stdin=subprocess.DEVNULL,
89
+ env=env,
90
+ )
91
+ atexit.register(self.close)
92
+ deadline = time.time() + timeout
93
+ while time.time() < deadline:
94
+ if self._proc.poll() is not None:
95
+ raise RuntimeError("a2ald exited during startup")
96
+ try:
97
+ urllib.request.urlopen(self.api_base + "/health", timeout=1.0)
98
+ return
99
+ except (urllib.error.URLError, OSError):
100
+ time.sleep(0.15)
101
+ raise TimeoutError("a2ald /health not ready")
102
+
103
+ def close(self) -> None:
104
+ if self._proc is not None:
105
+ self._proc.terminate()
106
+ try:
107
+ self._proc.wait(timeout=8)
108
+ except subprocess.TimeoutExpired:
109
+ self._proc.kill()
110
+ self._proc = None
111
+ if self._dd:
112
+ shutil.rmtree(self._dd, ignore_errors=True)
113
+ self._dd = None
114
+ self.api_base = None
115
+
116
+ def __enter__(self) -> Daemon:
117
+ self.start()
118
+ return self
119
+
120
+ def __exit__(self, *exc: object) -> None:
121
+ self.close()
122
+
123
+
124
+ class Client:
125
+ """JSON REST client for a2ald (localhost)."""
126
+
127
+ def __init__(self, base_url: str, token: Optional[str] = None) -> None:
128
+ self.base = base_url.rstrip("/")
129
+ self.token = token
130
+
131
+ def _request(
132
+ self,
133
+ method: str,
134
+ path: str,
135
+ body: Optional[Mapping[str, Any]] = None,
136
+ timeout: float = 120.0,
137
+ ) -> Any:
138
+ data = None
139
+ headers: dict[str, str] = {"Content-Type": "application/json"}
140
+ if self.token:
141
+ headers["Authorization"] = f"Bearer {self.token}"
142
+ if body is not None:
143
+ data = json.dumps(body).encode("utf-8")
144
+ req = urllib.request.Request(
145
+ self.base + path, data=data, headers=headers, method=method
146
+ )
147
+ try:
148
+ with urllib.request.urlopen(req, timeout=timeout) as resp:
149
+ raw = resp.read().decode("utf-8")
150
+ if not raw:
151
+ return None
152
+ return json.loads(raw)
153
+ except urllib.error.HTTPError as exc:
154
+ body_bytes = exc.read()
155
+ try:
156
+ err_body = json.loads(body_bytes)
157
+ msg = err_body.get("error") or str(err_body)
158
+ except Exception:
159
+ msg = body_bytes.decode("utf-8", errors="replace")[:300]
160
+ raise RuntimeError(f"HTTP {exc.code}: {msg}") from exc
161
+
162
+ def health(self) -> Any:
163
+ return self._request("GET", "/health", None)
164
+
165
+ def config_get(self) -> Any:
166
+ return self._request("GET", "/config", None)
167
+
168
+ def agents_list(self) -> Any:
169
+ return self._request("GET", "/agents", None)
170
+
171
+ def identity_generate(self) -> Any:
172
+ return self._request("POST", "/identity/generate", {})
173
+
174
+ def agent_register(self, payload: Mapping[str, Any]) -> Any:
175
+ return self._request("POST", "/agents", dict(payload))
176
+
177
+ def agent_publish(self, aid: str) -> Any:
178
+ return self._request("POST", f"/agents/{aid}/publish", {})
179
+
180
+ def resolve(self, aid: str) -> Any:
181
+ return self._request("POST", f"/resolve/{aid}", {})
182
+
183
+ def connect(self, aid: str, local_aid: str = "") -> Any:
184
+ body: dict[str, str] = {}
185
+ if local_aid:
186
+ body["local_aid"] = local_aid
187
+ return self._request("POST", f"/connect/{aid}", body)
@@ -0,0 +1,64 @@
1
+ Metadata-Version: 2.4
2
+ Name: a2al
3
+ Version: 0.1.0
4
+ Summary: Python client for a2al: spawn a2ald and call its REST API
5
+ Author: A2AL Authors
6
+ License: MPL-2.0
7
+ Project-URL: Homepage, https://github.com/a2al/a2al
8
+ Project-URL: Repository, https://github.com/a2al/a2al
9
+ Project-URL: Bug Tracker, https://github.com/a2al/a2al/issues
10
+ Keywords: a2al,a2a,agent,ai,sidecar
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: License :: OSI Approved :: Mozilla Public License 2.0 (MPL 2.0)
14
+ Classifier: Programming Language :: Python :: 3
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: Operating System :: OS Independent
20
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
21
+ Requires-Python: >=3.10
22
+ Description-Content-Type: text/markdown
23
+
24
+ # a2al (Python)
25
+
26
+ Python client for [a2al](https://github.com/a2al/a2al): spawns a local `a2ald` daemon and exposes a typed REST client for its API.
27
+
28
+ ## Installation
29
+
30
+ ```sh
31
+ pip install a2al
32
+ ```
33
+
34
+ Pre-built `a2ald` binaries are bundled inside platform wheels for:
35
+
36
+ | Platform | Architecture |
37
+ |----------|-------------|
38
+ | Linux | x86_64, arm64 |
39
+ | macOS | x86_64, arm64 |
40
+ | Windows | x86_64 |
41
+
42
+ On unsupported platforms, install `a2ald` manually and ensure it is on `PATH`, or set `A2ALD_PATH` to the executable path.
43
+
44
+ ## Usage
45
+
46
+ ```python
47
+ from a2al import Daemon, Client
48
+
49
+ with Daemon() as d:
50
+ c = Client(d.api_base, token=d.api_token)
51
+ print(c.health())
52
+ ```
53
+
54
+ ## Environment Variables
55
+
56
+ | Variable | Description |
57
+ |----------|-------------|
58
+ | `A2ALD_PATH` | Override path to the `a2ald` executable |
59
+ | `A2AL_API_TOKEN` | Bearer token when the daemon enforces auth |
60
+
61
+ ## Requirements
62
+
63
+ - Python 3.10+
64
+ - No third-party dependencies (standard library only)
@@ -0,0 +1,8 @@
1
+ README.md
2
+ pyproject.toml
3
+ src/a2al/__init__.py
4
+ src/a2al/_sidecar.py
5
+ src/a2al.egg-info/PKG-INFO
6
+ src/a2al.egg-info/SOURCES.txt
7
+ src/a2al.egg-info/dependency_links.txt
8
+ src/a2al.egg-info/top_level.txt
@@ -0,0 +1 @@
1
+ a2al