agentnode-sdk 0.1.0__tar.gz → 0.2.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.
- agentnode_sdk-0.2.0/.env.example +8 -0
- {agentnode_sdk-0.1.0 → agentnode_sdk-0.2.0}/.gitignore +22 -1
- agentnode_sdk-0.2.0/PKG-INFO +93 -0
- agentnode_sdk-0.2.0/README.md +70 -0
- agentnode_sdk-0.2.0/agentnode.lock +23 -0
- {agentnode_sdk-0.1.0 → agentnode_sdk-0.2.0}/agentnode_sdk/__init__.py +19 -1
- agentnode_sdk-0.2.0/agentnode_sdk/async_client.py +139 -0
- {agentnode_sdk-0.1.0 → agentnode_sdk-0.2.0}/agentnode_sdk/client.py +297 -0
- {agentnode_sdk-0.1.0 → agentnode_sdk-0.2.0}/agentnode_sdk/exceptions.py +18 -0
- agentnode_sdk-0.2.0/agentnode_sdk/installer.py +446 -0
- {agentnode_sdk-0.1.0 → agentnode_sdk-0.2.0}/agentnode_sdk/models.py +26 -0
- {agentnode_sdk-0.1.0 → agentnode_sdk-0.2.0}/pyproject.toml +1 -1
- agentnode_sdk-0.2.0/tests/test_async_client.py +283 -0
- {agentnode_sdk-0.1.0 → agentnode_sdk-0.2.0}/tests/test_client.py +2 -2
- agentnode_sdk-0.2.0/tests/test_edge_cases.py +442 -0
- agentnode_sdk-0.2.0/tests/test_v02.py +210 -0
- agentnode_sdk-0.1.0/PKG-INFO +0 -26
- agentnode_sdk-0.1.0/README.md +0 -3
- {agentnode_sdk-0.1.0 → agentnode_sdk-0.2.0}/tests/__init__.py +0 -0
|
@@ -15,10 +15,11 @@ cli/dist/
|
|
|
15
15
|
web/.next/
|
|
16
16
|
web/out/
|
|
17
17
|
|
|
18
|
-
# Environment
|
|
18
|
+
# Environment & Secrets
|
|
19
19
|
.env
|
|
20
20
|
.env.local
|
|
21
21
|
.env.production
|
|
22
|
+
.pypirc
|
|
22
23
|
|
|
23
24
|
# IDE
|
|
24
25
|
.idea/
|
|
@@ -42,5 +43,25 @@ htmlcov/
|
|
|
42
43
|
# Spec documents
|
|
43
44
|
*.docx
|
|
44
45
|
|
|
46
|
+
# Screenshots & temp files
|
|
47
|
+
*.png
|
|
48
|
+
screenshots/
|
|
49
|
+
test-screenshots/
|
|
50
|
+
spec_extracted.txt
|
|
51
|
+
calendar.ics
|
|
52
|
+
events.json
|
|
53
|
+
|
|
54
|
+
# Playwright
|
|
55
|
+
.playwright-mcp/
|
|
56
|
+
|
|
45
57
|
# Claude Code
|
|
46
58
|
.claude/
|
|
59
|
+
|
|
60
|
+
# Internal growth / marketing content
|
|
61
|
+
growth/
|
|
62
|
+
backend/.next/
|
|
63
|
+
kwfinder-*.md
|
|
64
|
+
packages_dump.json
|
|
65
|
+
snapshot_nonowner.txt
|
|
66
|
+
seo/
|
|
67
|
+
backend/scripts/enrichment_*.json
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: agentnode-sdk
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Python SDK for AgentNode — the open upgrade and discovery infrastructure for AI agents.
|
|
5
|
+
Project-URL: Homepage, https://agentnode.net
|
|
6
|
+
Project-URL: Repository, https://github.com/agentnode-ai/agentnode
|
|
7
|
+
Project-URL: Documentation, https://agentnode.net/docs
|
|
8
|
+
License-Expression: MIT
|
|
9
|
+
Keywords: agent,agentnode,ai,capabilities,langchain,mcp
|
|
10
|
+
Classifier: Development Status :: 3 - Alpha
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
16
|
+
Requires-Python: >=3.10
|
|
17
|
+
Requires-Dist: httpx>=0.25
|
|
18
|
+
Provides-Extra: dev
|
|
19
|
+
Requires-Dist: pytest; extra == 'dev'
|
|
20
|
+
Requires-Dist: pytest-asyncio; extra == 'dev'
|
|
21
|
+
Requires-Dist: respx; extra == 'dev'
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
|
|
24
|
+
# agentnode-sdk
|
|
25
|
+
|
|
26
|
+
Python SDK for [AgentNode](https://agentnode.net) — the open upgrade and discovery infrastructure for AI agents.
|
|
27
|
+
|
|
28
|
+
## Installation
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
pip install agentnode-sdk
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Quick Start
|
|
35
|
+
|
|
36
|
+
```python
|
|
37
|
+
from agentnode_sdk import AgentNodeClient
|
|
38
|
+
from agentnode_sdk.installer import load_tool
|
|
39
|
+
|
|
40
|
+
client = AgentNodeClient(api_key="ank_...")
|
|
41
|
+
|
|
42
|
+
# Search for packages
|
|
43
|
+
results = client.search("pdf extraction")
|
|
44
|
+
for hit in results.hits:
|
|
45
|
+
print(f"{hit.slug} — {hit.summary}")
|
|
46
|
+
|
|
47
|
+
# Resolve capability gaps
|
|
48
|
+
resolved = client.resolve(
|
|
49
|
+
capabilities=["pdf_extraction"],
|
|
50
|
+
framework="langchain",
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
# v0.2: Load specific tools from multi-tool packs
|
|
54
|
+
describe = load_tool("csv-analyzer-pack", tool_name="describe")
|
|
55
|
+
result = describe({"file_path": "data.csv"})
|
|
56
|
+
|
|
57
|
+
# Single-tool packs — no tool_name needed
|
|
58
|
+
extract = load_tool("pdf-reader-pack")
|
|
59
|
+
pdf = extract({"file_path": "report.pdf"})
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## API Reference
|
|
63
|
+
|
|
64
|
+
### `AgentNodeClient`
|
|
65
|
+
|
|
66
|
+
The main client with typed return models.
|
|
67
|
+
|
|
68
|
+
| Method | Description |
|
|
69
|
+
|--------|-------------|
|
|
70
|
+
| `search(query, ...)` | Search packages by keyword or capability |
|
|
71
|
+
| `resolve(capabilities, ...)` | Resolve capability gaps to ranked packages |
|
|
72
|
+
| `get_package(slug)` | Get package details |
|
|
73
|
+
| `get_install_metadata(slug)` | Get install info (artifact, permissions, deps) |
|
|
74
|
+
| `download(slug)` | Track download and get artifact URL |
|
|
75
|
+
| `load_tool(slug, tool_name=)` | Load a tool function from an installed pack (v0.2) |
|
|
76
|
+
|
|
77
|
+
### `AgentNode`
|
|
78
|
+
|
|
79
|
+
Lightweight client returning raw dicts.
|
|
80
|
+
|
|
81
|
+
| Method | Description |
|
|
82
|
+
|--------|-------------|
|
|
83
|
+
| `search(query, ...)` | Search packages |
|
|
84
|
+
| `resolve_upgrade(missing_capability, ...)` | Resolve a single capability gap |
|
|
85
|
+
| `check_policy(package_slug, ...)` | Evaluate security policy |
|
|
86
|
+
| `get_install_metadata(slug)` | Get install metadata |
|
|
87
|
+
| `install(slug)` | Create installation record |
|
|
88
|
+
| `recommend(missing_capabilities)` | Get recommendations |
|
|
89
|
+
| `validate(manifest)` | Validate a package manifest |
|
|
90
|
+
|
|
91
|
+
## License
|
|
92
|
+
|
|
93
|
+
MIT
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# agentnode-sdk
|
|
2
|
+
|
|
3
|
+
Python SDK for [AgentNode](https://agentnode.net) — the open upgrade and discovery infrastructure for AI agents.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pip install agentnode-sdk
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Quick Start
|
|
12
|
+
|
|
13
|
+
```python
|
|
14
|
+
from agentnode_sdk import AgentNodeClient
|
|
15
|
+
from agentnode_sdk.installer import load_tool
|
|
16
|
+
|
|
17
|
+
client = AgentNodeClient(api_key="ank_...")
|
|
18
|
+
|
|
19
|
+
# Search for packages
|
|
20
|
+
results = client.search("pdf extraction")
|
|
21
|
+
for hit in results.hits:
|
|
22
|
+
print(f"{hit.slug} — {hit.summary}")
|
|
23
|
+
|
|
24
|
+
# Resolve capability gaps
|
|
25
|
+
resolved = client.resolve(
|
|
26
|
+
capabilities=["pdf_extraction"],
|
|
27
|
+
framework="langchain",
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
# v0.2: Load specific tools from multi-tool packs
|
|
31
|
+
describe = load_tool("csv-analyzer-pack", tool_name="describe")
|
|
32
|
+
result = describe({"file_path": "data.csv"})
|
|
33
|
+
|
|
34
|
+
# Single-tool packs — no tool_name needed
|
|
35
|
+
extract = load_tool("pdf-reader-pack")
|
|
36
|
+
pdf = extract({"file_path": "report.pdf"})
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## API Reference
|
|
40
|
+
|
|
41
|
+
### `AgentNodeClient`
|
|
42
|
+
|
|
43
|
+
The main client with typed return models.
|
|
44
|
+
|
|
45
|
+
| Method | Description |
|
|
46
|
+
|--------|-------------|
|
|
47
|
+
| `search(query, ...)` | Search packages by keyword or capability |
|
|
48
|
+
| `resolve(capabilities, ...)` | Resolve capability gaps to ranked packages |
|
|
49
|
+
| `get_package(slug)` | Get package details |
|
|
50
|
+
| `get_install_metadata(slug)` | Get install info (artifact, permissions, deps) |
|
|
51
|
+
| `download(slug)` | Track download and get artifact URL |
|
|
52
|
+
| `load_tool(slug, tool_name=)` | Load a tool function from an installed pack (v0.2) |
|
|
53
|
+
|
|
54
|
+
### `AgentNode`
|
|
55
|
+
|
|
56
|
+
Lightweight client returning raw dicts.
|
|
57
|
+
|
|
58
|
+
| Method | Description |
|
|
59
|
+
|--------|-------------|
|
|
60
|
+
| `search(query, ...)` | Search packages |
|
|
61
|
+
| `resolve_upgrade(missing_capability, ...)` | Resolve a single capability gap |
|
|
62
|
+
| `check_policy(package_slug, ...)` | Evaluate security policy |
|
|
63
|
+
| `get_install_metadata(slug)` | Get install metadata |
|
|
64
|
+
| `install(slug)` | Create installation record |
|
|
65
|
+
| `recommend(missing_capabilities)` | Get recommendations |
|
|
66
|
+
| `validate(manifest)` | Validate a package manifest |
|
|
67
|
+
|
|
68
|
+
## License
|
|
69
|
+
|
|
70
|
+
MIT
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"lockfile_version": "0.1",
|
|
3
|
+
"updated_at": "2026-03-18T02:18:53.827839+00:00",
|
|
4
|
+
"packages": {
|
|
5
|
+
"word-counter-pack": {
|
|
6
|
+
"version": "1.0.0",
|
|
7
|
+
"package_type": "toolpack",
|
|
8
|
+
"entrypoint": "word_counter_pack.tool",
|
|
9
|
+
"capability_ids": [
|
|
10
|
+
"data_cleaning"
|
|
11
|
+
],
|
|
12
|
+
"tools": [
|
|
13
|
+
{
|
|
14
|
+
"name": "count_words",
|
|
15
|
+
"entrypoint": "word_counter_pack.tool:count_words"
|
|
16
|
+
}
|
|
17
|
+
],
|
|
18
|
+
"artifact_hash": "sha256:445388c86aa2b20c3e748f58b2d221548f1f3f71ff5c715a64cdc80966fab03c",
|
|
19
|
+
"installed_at": "2026-03-18T02:18:53.827396+00:00",
|
|
20
|
+
"source": "sdk"
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
@@ -1,14 +1,20 @@
|
|
|
1
1
|
"""AgentNode Python SDK — discover, resolve, and install AI agent capabilities."""
|
|
2
2
|
|
|
3
|
+
from agentnode_sdk.async_client import AsyncAgentNode
|
|
3
4
|
from agentnode_sdk.client import AgentNode, AgentNodeClient
|
|
4
5
|
from agentnode_sdk.exceptions import (
|
|
5
6
|
AgentNodeError,
|
|
7
|
+
AgentNodeToolError,
|
|
6
8
|
AuthError,
|
|
7
9
|
NotFoundError,
|
|
10
|
+
RateLimitError,
|
|
8
11
|
ValidationError,
|
|
9
12
|
)
|
|
13
|
+
from agentnode_sdk.installer import load_tool
|
|
10
14
|
from agentnode_sdk.models import (
|
|
15
|
+
CanInstallResult,
|
|
11
16
|
InstallMetadata,
|
|
17
|
+
InstallResult,
|
|
12
18
|
PackageDetail,
|
|
13
19
|
ResolvedPackage,
|
|
14
20
|
ResolveResult,
|
|
@@ -16,13 +22,23 @@ from agentnode_sdk.models import (
|
|
|
16
22
|
SearchResult,
|
|
17
23
|
)
|
|
18
24
|
|
|
19
|
-
|
|
25
|
+
# Convenience aliases
|
|
26
|
+
Client = AgentNodeClient
|
|
27
|
+
ToolError = AgentNodeToolError
|
|
28
|
+
|
|
29
|
+
__version__ = "0.2.0"
|
|
20
30
|
__all__ = [
|
|
21
31
|
"AgentNode",
|
|
32
|
+
"AsyncAgentNode",
|
|
22
33
|
"AgentNodeClient",
|
|
34
|
+
"Client",
|
|
35
|
+
"load_tool",
|
|
23
36
|
"AgentNodeError",
|
|
37
|
+
"AgentNodeToolError",
|
|
38
|
+
"ToolError",
|
|
24
39
|
"NotFoundError",
|
|
25
40
|
"AuthError",
|
|
41
|
+
"RateLimitError",
|
|
26
42
|
"ValidationError",
|
|
27
43
|
"PackageDetail",
|
|
28
44
|
"SearchResult",
|
|
@@ -30,4 +46,6 @@ __all__ = [
|
|
|
30
46
|
"ResolveResult",
|
|
31
47
|
"ResolvedPackage",
|
|
32
48
|
"InstallMetadata",
|
|
49
|
+
"InstallResult",
|
|
50
|
+
"CanInstallResult",
|
|
33
51
|
]
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
"""Async AgentNode API client. Mirrors AgentNode (sync) using httpx.AsyncClient."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
import httpx
|
|
5
|
+
|
|
6
|
+
from agentnode_sdk.client import DEFAULT_BASE_URL, ERROR_CLASS_MAP
|
|
7
|
+
from agentnode_sdk.exceptions import AgentNodeError
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class AsyncAgentNode:
|
|
11
|
+
"""Async variant of the AgentNode SDK client. Returns plain dicts."""
|
|
12
|
+
|
|
13
|
+
def __init__(self, api_key: str, base_url: str = DEFAULT_BASE_URL):
|
|
14
|
+
self._client = httpx.AsyncClient(
|
|
15
|
+
base_url=base_url.rstrip("/"),
|
|
16
|
+
headers={"X-API-Key": api_key},
|
|
17
|
+
timeout=30,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
async def close(self):
|
|
21
|
+
await self._client.aclose()
|
|
22
|
+
|
|
23
|
+
async def __aenter__(self):
|
|
24
|
+
return self
|
|
25
|
+
|
|
26
|
+
async def __aexit__(self, *args):
|
|
27
|
+
await self.close()
|
|
28
|
+
|
|
29
|
+
# --- Public API ---
|
|
30
|
+
|
|
31
|
+
async def search(
|
|
32
|
+
self,
|
|
33
|
+
query: str = "",
|
|
34
|
+
capability_id: str = "",
|
|
35
|
+
framework: str = "",
|
|
36
|
+
sort_by: str = "relevance",
|
|
37
|
+
page: int = 1,
|
|
38
|
+
) -> dict:
|
|
39
|
+
body = {k: v for k, v in {
|
|
40
|
+
"q": query,
|
|
41
|
+
"capability_id": capability_id,
|
|
42
|
+
"framework": framework,
|
|
43
|
+
"sort_by": sort_by,
|
|
44
|
+
"page": page,
|
|
45
|
+
}.items() if v}
|
|
46
|
+
return self._handle(await self._client.post("/search", json=body))
|
|
47
|
+
|
|
48
|
+
async def resolve_upgrade(
|
|
49
|
+
self,
|
|
50
|
+
missing_capability: str,
|
|
51
|
+
framework: str = "",
|
|
52
|
+
runtime: str = "",
|
|
53
|
+
current_capabilities: list[str] | None = None,
|
|
54
|
+
policy: dict | None = None,
|
|
55
|
+
) -> dict:
|
|
56
|
+
return await self._post(
|
|
57
|
+
"/resolve-upgrade",
|
|
58
|
+
missing_capability=missing_capability,
|
|
59
|
+
current_capabilities=current_capabilities or [],
|
|
60
|
+
framework=framework,
|
|
61
|
+
runtime=runtime,
|
|
62
|
+
policy=policy or {},
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
async def check_policy(
|
|
66
|
+
self,
|
|
67
|
+
package_slug: str,
|
|
68
|
+
framework: str = "",
|
|
69
|
+
policy: dict | None = None,
|
|
70
|
+
) -> dict:
|
|
71
|
+
return await self._post(
|
|
72
|
+
"/check-policy",
|
|
73
|
+
package_slug=package_slug,
|
|
74
|
+
framework=framework,
|
|
75
|
+
policy=policy or {},
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
async def get_install_metadata(self, package_slug: str, version: str = "") -> dict:
|
|
79
|
+
"""Read-only install metadata. Does NOT create installation records."""
|
|
80
|
+
params = {"version": version} if version else {}
|
|
81
|
+
return self._handle(
|
|
82
|
+
await self._client.get(f"/packages/{package_slug}/install-info", params=params)
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
async def get_package(self, slug: str) -> dict:
|
|
86
|
+
return self._handle(await self._client.get(f"/packages/{slug}"))
|
|
87
|
+
|
|
88
|
+
async def validate(self, manifest: dict) -> dict:
|
|
89
|
+
return self._handle(
|
|
90
|
+
await self._client.post("/packages/validate", json={"manifest": manifest})
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
async def install(
|
|
94
|
+
self,
|
|
95
|
+
package_slug: str,
|
|
96
|
+
version: str = "",
|
|
97
|
+
source: str = "sdk",
|
|
98
|
+
event_type: str = "install",
|
|
99
|
+
) -> dict:
|
|
100
|
+
"""Create installation record and get artifact URL."""
|
|
101
|
+
body: dict = {"source": source, "event_type": event_type}
|
|
102
|
+
if version:
|
|
103
|
+
body["version"] = version
|
|
104
|
+
return self._handle(
|
|
105
|
+
await self._client.post(f"/packages/{package_slug}/install", json=body)
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
async def recommend(
|
|
109
|
+
self,
|
|
110
|
+
missing_capabilities: list[str],
|
|
111
|
+
framework: str = "",
|
|
112
|
+
runtime: str = "",
|
|
113
|
+
) -> dict:
|
|
114
|
+
return await self._post(
|
|
115
|
+
"/recommend",
|
|
116
|
+
missing_capabilities=missing_capabilities,
|
|
117
|
+
framework=framework,
|
|
118
|
+
runtime=runtime,
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
# --- Internal ---
|
|
122
|
+
|
|
123
|
+
async def _post(self, path: str, **kwargs) -> dict:
|
|
124
|
+
body = {k: v for k, v in kwargs.items() if v is not None and v != ""}
|
|
125
|
+
return self._handle(await self._client.post(path, json=body))
|
|
126
|
+
|
|
127
|
+
def _handle(self, response: httpx.Response) -> dict:
|
|
128
|
+
"""Parse response. Raise typed AgentNodeError on API errors."""
|
|
129
|
+
if response.status_code >= 400:
|
|
130
|
+
try:
|
|
131
|
+
body = response.json()
|
|
132
|
+
err = body.get("error", {})
|
|
133
|
+
code = err.get("code", "UNKNOWN")
|
|
134
|
+
message = err.get("message", response.text)
|
|
135
|
+
except (ValueError, KeyError):
|
|
136
|
+
code, message = "UNKNOWN", response.text
|
|
137
|
+
exc_class = ERROR_CLASS_MAP.get(response.status_code, AgentNodeError)
|
|
138
|
+
raise exc_class(code, message)
|
|
139
|
+
return response.json()
|