agentskills-http 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,119 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: agentskills-http
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: HTTP-based skill providers for the Agent Skills format (https://agentskills.io)
|
|
5
|
+
License: MIT
|
|
6
|
+
Author: Pratik Panda
|
|
7
|
+
Requires-Python: >=3.12,<4.0
|
|
8
|
+
Classifier: Development Status :: 3 - Alpha
|
|
9
|
+
Classifier: Intended Audience :: Developers
|
|
10
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
15
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
16
|
+
Requires-Dist: agentskills-core (>=0.1.0)
|
|
17
|
+
Requires-Dist: httpx (>=0.27)
|
|
18
|
+
Requires-Dist: pyyaml (>=6.0)
|
|
19
|
+
Project-URL: Homepage, https://agentskills.io
|
|
20
|
+
Project-URL: Repository, https://github.com/pratikxpanda/agentskills-sdk
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
|
|
23
|
+
# agentskills-http
|
|
24
|
+
|
|
25
|
+
> HTTP static-file skill provider for the [Agent Skills SDK](../../README.md).
|
|
26
|
+
|
|
27
|
+
Serves [Agent Skills](https://agentskills.io) from any static HTTP file host — S3, Azure Blob, CDN, GitHub Pages, Nginx, etc. Expects the same directory-tree layout as the filesystem provider, served over HTTP.
|
|
28
|
+
|
|
29
|
+
## Installation
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
pip install agentskills-http
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Requires Python 3.12+. Installs `agentskills-core`, `httpx`, and `pyyaml` as dependencies.
|
|
36
|
+
|
|
37
|
+
## Expected URL Layout
|
|
38
|
+
|
|
39
|
+
```text
|
|
40
|
+
https://cdn.example.com/skills/
|
|
41
|
+
├── incident-response/
|
|
42
|
+
│ ├── SKILL.md
|
|
43
|
+
│ ├── references/severity-levels.md
|
|
44
|
+
│ ├── scripts/page-oncall.sh
|
|
45
|
+
│ └── assets/flowchart.mermaid
|
|
46
|
+
└── another-skill/
|
|
47
|
+
└── SKILL.md
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Usage
|
|
51
|
+
|
|
52
|
+
```python
|
|
53
|
+
from agentskills_core import SkillRegistry
|
|
54
|
+
from agentskills_http import HTTPStaticFileSkillProvider
|
|
55
|
+
|
|
56
|
+
async with HTTPStaticFileSkillProvider("https://cdn.example.com/skills") as provider:
|
|
57
|
+
registry = SkillRegistry()
|
|
58
|
+
await registry.register("incident-response", provider)
|
|
59
|
+
|
|
60
|
+
skill = registry.get_skill("incident-response")
|
|
61
|
+
meta = await skill.get_metadata()
|
|
62
|
+
body = await skill.get_body()
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### Custom Headers
|
|
66
|
+
|
|
67
|
+
Pass authentication or other headers:
|
|
68
|
+
|
|
69
|
+
```python
|
|
70
|
+
provider = HTTPStaticFileSkillProvider(
|
|
71
|
+
"https://cdn.example.com/skills",
|
|
72
|
+
headers={"Authorization": "Bearer <token>"},
|
|
73
|
+
)
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### Bring Your Own Client
|
|
77
|
+
|
|
78
|
+
Supply a pre-configured `httpx.AsyncClient` for full control over timeouts, proxies, etc.:
|
|
79
|
+
|
|
80
|
+
```python
|
|
81
|
+
import httpx
|
|
82
|
+
|
|
83
|
+
client = httpx.AsyncClient(timeout=30, headers={"Authorization": "Bearer <token>"})
|
|
84
|
+
provider = HTTPStaticFileSkillProvider("https://cdn.example.com/skills", client=client)
|
|
85
|
+
# caller is responsible for closing the client
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
> **Note:** `client` and `headers` are mutually exclusive. Configure headers on the client directly when providing your own.
|
|
89
|
+
|
|
90
|
+
## API
|
|
91
|
+
|
|
92
|
+
### `HTTPStaticFileSkillProvider(base_url, *, client=None, headers=None)`
|
|
93
|
+
|
|
94
|
+
| Method | Returns | Description |
|
|
95
|
+
| --- | --- | --- |
|
|
96
|
+
| `get_metadata(skill_id)` | `dict[str, Any]` | Parsed YAML frontmatter from `SKILL.md` |
|
|
97
|
+
| `get_body(skill_id)` | `str` | Markdown body after the frontmatter |
|
|
98
|
+
| `get_script(skill_id, name)` | `bytes` | Raw script content |
|
|
99
|
+
| `get_asset(skill_id, name)` | `bytes` | Raw asset content |
|
|
100
|
+
| `get_reference(skill_id, name)` | `bytes` | Raw reference content |
|
|
101
|
+
| `aclose()` | `None` | Close the HTTP client (if owned by the provider) |
|
|
102
|
+
|
|
103
|
+
Supports `async with` for automatic cleanup.
|
|
104
|
+
|
|
105
|
+
## Error Handling
|
|
106
|
+
|
|
107
|
+
| Scenario | Exception |
|
|
108
|
+
| --- | --- |
|
|
109
|
+
| 404 on `SKILL.md` | `SkillNotFoundError` |
|
|
110
|
+
| 404 on a resource | `ResourceNotFoundError` |
|
|
111
|
+
| Other HTTP errors (500, 403, ...) | `AgentSkillsError` |
|
|
112
|
+
| Connection failures | `AgentSkillsError` |
|
|
113
|
+
|
|
114
|
+
All exceptions inherit from `AgentSkillsError`.
|
|
115
|
+
|
|
116
|
+
## License
|
|
117
|
+
|
|
118
|
+
MIT
|
|
119
|
+
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
# agentskills-http
|
|
2
|
+
|
|
3
|
+
> HTTP static-file skill provider for the [Agent Skills SDK](../../README.md).
|
|
4
|
+
|
|
5
|
+
Serves [Agent Skills](https://agentskills.io) from any static HTTP file host — S3, Azure Blob, CDN, GitHub Pages, Nginx, etc. Expects the same directory-tree layout as the filesystem provider, served over HTTP.
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
pip install agentskills-http
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Requires Python 3.12+. Installs `agentskills-core`, `httpx`, and `pyyaml` as dependencies.
|
|
14
|
+
|
|
15
|
+
## Expected URL Layout
|
|
16
|
+
|
|
17
|
+
```text
|
|
18
|
+
https://cdn.example.com/skills/
|
|
19
|
+
├── incident-response/
|
|
20
|
+
│ ├── SKILL.md
|
|
21
|
+
│ ├── references/severity-levels.md
|
|
22
|
+
│ ├── scripts/page-oncall.sh
|
|
23
|
+
│ └── assets/flowchart.mermaid
|
|
24
|
+
└── another-skill/
|
|
25
|
+
└── SKILL.md
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Usage
|
|
29
|
+
|
|
30
|
+
```python
|
|
31
|
+
from agentskills_core import SkillRegistry
|
|
32
|
+
from agentskills_http import HTTPStaticFileSkillProvider
|
|
33
|
+
|
|
34
|
+
async with HTTPStaticFileSkillProvider("https://cdn.example.com/skills") as provider:
|
|
35
|
+
registry = SkillRegistry()
|
|
36
|
+
await registry.register("incident-response", provider)
|
|
37
|
+
|
|
38
|
+
skill = registry.get_skill("incident-response")
|
|
39
|
+
meta = await skill.get_metadata()
|
|
40
|
+
body = await skill.get_body()
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### Custom Headers
|
|
44
|
+
|
|
45
|
+
Pass authentication or other headers:
|
|
46
|
+
|
|
47
|
+
```python
|
|
48
|
+
provider = HTTPStaticFileSkillProvider(
|
|
49
|
+
"https://cdn.example.com/skills",
|
|
50
|
+
headers={"Authorization": "Bearer <token>"},
|
|
51
|
+
)
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### Bring Your Own Client
|
|
55
|
+
|
|
56
|
+
Supply a pre-configured `httpx.AsyncClient` for full control over timeouts, proxies, etc.:
|
|
57
|
+
|
|
58
|
+
```python
|
|
59
|
+
import httpx
|
|
60
|
+
|
|
61
|
+
client = httpx.AsyncClient(timeout=30, headers={"Authorization": "Bearer <token>"})
|
|
62
|
+
provider = HTTPStaticFileSkillProvider("https://cdn.example.com/skills", client=client)
|
|
63
|
+
# caller is responsible for closing the client
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
> **Note:** `client` and `headers` are mutually exclusive. Configure headers on the client directly when providing your own.
|
|
67
|
+
|
|
68
|
+
## API
|
|
69
|
+
|
|
70
|
+
### `HTTPStaticFileSkillProvider(base_url, *, client=None, headers=None)`
|
|
71
|
+
|
|
72
|
+
| Method | Returns | Description |
|
|
73
|
+
| --- | --- | --- |
|
|
74
|
+
| `get_metadata(skill_id)` | `dict[str, Any]` | Parsed YAML frontmatter from `SKILL.md` |
|
|
75
|
+
| `get_body(skill_id)` | `str` | Markdown body after the frontmatter |
|
|
76
|
+
| `get_script(skill_id, name)` | `bytes` | Raw script content |
|
|
77
|
+
| `get_asset(skill_id, name)` | `bytes` | Raw asset content |
|
|
78
|
+
| `get_reference(skill_id, name)` | `bytes` | Raw reference content |
|
|
79
|
+
| `aclose()` | `None` | Close the HTTP client (if owned by the provider) |
|
|
80
|
+
|
|
81
|
+
Supports `async with` for automatic cleanup.
|
|
82
|
+
|
|
83
|
+
## Error Handling
|
|
84
|
+
|
|
85
|
+
| Scenario | Exception |
|
|
86
|
+
| --- | --- |
|
|
87
|
+
| 404 on `SKILL.md` | `SkillNotFoundError` |
|
|
88
|
+
| 404 on a resource | `ResourceNotFoundError` |
|
|
89
|
+
| Other HTTP errors (500, 403, ...) | `AgentSkillsError` |
|
|
90
|
+
| Connection failures | `AgentSkillsError` |
|
|
91
|
+
|
|
92
|
+
All exceptions inherit from `AgentSkillsError`.
|
|
93
|
+
|
|
94
|
+
## License
|
|
95
|
+
|
|
96
|
+
MIT
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"""HTTP-based skill providers for the Agent Skills format.
|
|
2
|
+
|
|
3
|
+
This package provides :class:`HTTPStaticFileSkillProvider`, a concrete
|
|
4
|
+
implementation of :class:`~agentskills_core.SkillProvider` that fetches
|
|
5
|
+
`Agent Skills <https://agentskills.io>`_ from a static HTTP file host
|
|
6
|
+
(S3, Azure Blob Storage, CDN, GitHub Pages, or any web server serving
|
|
7
|
+
raw files).
|
|
8
|
+
|
|
9
|
+
Install::
|
|
10
|
+
|
|
11
|
+
pip install agentskills-http
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from agentskills_http.static import HTTPStaticFileSkillProvider
|
|
15
|
+
|
|
16
|
+
__all__ = ["HTTPStaticFileSkillProvider"]
|
|
File without changes
|
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
"""HTTP static-file skill provider.
|
|
2
|
+
|
|
3
|
+
This module implements :class:`HTTPStaticFileSkillProvider`, which fetches
|
|
4
|
+
`Agent Skills <https://agentskills.io>`_ from any static HTTP file host.
|
|
5
|
+
It expects the same directory-tree layout used by
|
|
6
|
+
:class:`~agentskills_fs.LocalFileSystemSkillProvider`, served over HTTP.
|
|
7
|
+
|
|
8
|
+
Expected URL layout::
|
|
9
|
+
|
|
10
|
+
{base_url}/
|
|
11
|
+
├── incident-response/
|
|
12
|
+
│ ├── SKILL.md # YAML frontmatter + markdown body
|
|
13
|
+
│ ├── references/severity-levels.md
|
|
14
|
+
│ ├── scripts/page-oncall.sh
|
|
15
|
+
│ └── assets/flowchart.mermaid
|
|
16
|
+
└── another-skill/
|
|
17
|
+
└── SKILL.md
|
|
18
|
+
|
|
19
|
+
The provider is a pure content accessor — it does not enumerate or
|
|
20
|
+
discover skills. Registration is handled explicitly by the application
|
|
21
|
+
via :meth:`SkillRegistry.register <agentskills_core.SkillRegistry.register>`.
|
|
22
|
+
Resource names (scripts, assets, references) are discovered by the agent
|
|
23
|
+
from the skill body rather than from a manifest.
|
|
24
|
+
|
|
25
|
+
All methods are ``async`` and use `httpx <https://www.python-httpx.org/>`_
|
|
26
|
+
for non-blocking HTTP requests.
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
from __future__ import annotations
|
|
30
|
+
|
|
31
|
+
from typing import Any
|
|
32
|
+
from urllib.parse import quote
|
|
33
|
+
|
|
34
|
+
import httpx
|
|
35
|
+
|
|
36
|
+
from agentskills_core import (
|
|
37
|
+
AgentSkillsError,
|
|
38
|
+
ResourceNotFoundError,
|
|
39
|
+
SkillNotFoundError,
|
|
40
|
+
SkillProvider,
|
|
41
|
+
split_frontmatter,
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class HTTPStaticFileSkillProvider(SkillProvider):
|
|
46
|
+
"""Skill provider backed by a static HTTP file host.
|
|
47
|
+
|
|
48
|
+
The provider expects an HTTP server (S3, Azure Blob, CDN, Nginx,
|
|
49
|
+
GitHub Pages, etc.) that hosts skill files at predictable URL paths.
|
|
50
|
+
Resource names (scripts, assets, references) are discovered by the
|
|
51
|
+
agent from the skill body rather than from a separate manifest.
|
|
52
|
+
|
|
53
|
+
The provider owns an :class:`httpx.AsyncClient` for connection
|
|
54
|
+
pooling. If you supply your own client the provider will use it
|
|
55
|
+
without closing it. Otherwise call :meth:`aclose` or use
|
|
56
|
+
``async with`` when you are finished.
|
|
57
|
+
|
|
58
|
+
Args:
|
|
59
|
+
base_url: Root URL where the skill tree is hosted. A trailing
|
|
60
|
+
slash is stripped automatically.
|
|
61
|
+
client: Optional pre-configured :class:`httpx.AsyncClient`.
|
|
62
|
+
When provided, the caller is responsible for closing it.
|
|
63
|
+
headers: Optional extra headers sent with every request (e.g.
|
|
64
|
+
``Authorization``).
|
|
65
|
+
|
|
66
|
+
Example::
|
|
67
|
+
|
|
68
|
+
async with HTTPStaticFileSkillProvider("https://cdn.example.com/skills") as provider:
|
|
69
|
+
registry = SkillRegistry()
|
|
70
|
+
await registry.register("incident-response", provider)
|
|
71
|
+
|
|
72
|
+
skill = registry.get_skill("incident-response")
|
|
73
|
+
meta = await skill.get_metadata()
|
|
74
|
+
print(f"{meta['name']}: {meta['description']}")
|
|
75
|
+
"""
|
|
76
|
+
|
|
77
|
+
def __init__(
|
|
78
|
+
self,
|
|
79
|
+
base_url: str,
|
|
80
|
+
*,
|
|
81
|
+
client: httpx.AsyncClient | None = None,
|
|
82
|
+
headers: dict[str, str] | None = None,
|
|
83
|
+
) -> None:
|
|
84
|
+
if client is not None and headers is not None:
|
|
85
|
+
raise ValueError(
|
|
86
|
+
"Cannot specify both 'client' and 'headers'. "
|
|
87
|
+
"Configure headers on the client directly."
|
|
88
|
+
)
|
|
89
|
+
self._base_url = base_url.rstrip("/")
|
|
90
|
+
self._owns_client = client is None
|
|
91
|
+
self._client = client or httpx.AsyncClient(headers=headers)
|
|
92
|
+
|
|
93
|
+
async def aclose(self) -> None:
|
|
94
|
+
"""Close the underlying HTTP client if it is owned by this provider."""
|
|
95
|
+
if self._owns_client:
|
|
96
|
+
await self._client.aclose()
|
|
97
|
+
|
|
98
|
+
async def __aenter__(self) -> HTTPStaticFileSkillProvider:
|
|
99
|
+
return self
|
|
100
|
+
|
|
101
|
+
async def __aexit__(self, *exc: object) -> None:
|
|
102
|
+
await self.aclose()
|
|
103
|
+
|
|
104
|
+
# ------------------------------------------------------------------
|
|
105
|
+
# Metadata & body
|
|
106
|
+
# ------------------------------------------------------------------
|
|
107
|
+
|
|
108
|
+
async def get_metadata(self, skill_id: str) -> dict[str, Any]:
|
|
109
|
+
"""Fetch ``SKILL.md`` and return the parsed YAML frontmatter.
|
|
110
|
+
|
|
111
|
+
Args:
|
|
112
|
+
skill_id: Skill name to look up.
|
|
113
|
+
|
|
114
|
+
Returns:
|
|
115
|
+
Dictionary of frontmatter key-value pairs.
|
|
116
|
+
|
|
117
|
+
Raises:
|
|
118
|
+
SkillNotFoundError: If the skill's ``SKILL.md`` cannot be
|
|
119
|
+
fetched.
|
|
120
|
+
"""
|
|
121
|
+
raw = await self._get_skill_md(skill_id)
|
|
122
|
+
frontmatter, _ = split_frontmatter(raw)
|
|
123
|
+
return frontmatter
|
|
124
|
+
|
|
125
|
+
async def get_body(self, skill_id: str) -> str:
|
|
126
|
+
"""Fetch ``SKILL.md`` and return the markdown body.
|
|
127
|
+
|
|
128
|
+
Args:
|
|
129
|
+
skill_id: Skill name to look up.
|
|
130
|
+
|
|
131
|
+
Returns:
|
|
132
|
+
Markdown instruction text.
|
|
133
|
+
|
|
134
|
+
Raises:
|
|
135
|
+
SkillNotFoundError: If the skill's ``SKILL.md`` cannot be
|
|
136
|
+
fetched.
|
|
137
|
+
"""
|
|
138
|
+
raw = await self._get_skill_md(skill_id)
|
|
139
|
+
_, body = split_frontmatter(raw)
|
|
140
|
+
return body
|
|
141
|
+
|
|
142
|
+
# ------------------------------------------------------------------
|
|
143
|
+
# Scripts
|
|
144
|
+
# ------------------------------------------------------------------
|
|
145
|
+
|
|
146
|
+
async def get_script(self, skill_id: str, name: str) -> bytes:
|
|
147
|
+
"""Fetch a single script file.
|
|
148
|
+
|
|
149
|
+
Args:
|
|
150
|
+
skill_id: Skill name.
|
|
151
|
+
name: Script filename.
|
|
152
|
+
|
|
153
|
+
Returns:
|
|
154
|
+
Raw content as bytes.
|
|
155
|
+
|
|
156
|
+
Raises:
|
|
157
|
+
ResourceNotFoundError: If the script does not exist.
|
|
158
|
+
"""
|
|
159
|
+
return await self._get_resource(skill_id, "scripts", name)
|
|
160
|
+
|
|
161
|
+
# ------------------------------------------------------------------
|
|
162
|
+
# Assets
|
|
163
|
+
# ------------------------------------------------------------------
|
|
164
|
+
|
|
165
|
+
async def get_asset(self, skill_id: str, name: str) -> bytes:
|
|
166
|
+
"""Fetch a single asset file.
|
|
167
|
+
|
|
168
|
+
Args:
|
|
169
|
+
skill_id: Skill name.
|
|
170
|
+
name: Asset filename.
|
|
171
|
+
|
|
172
|
+
Returns:
|
|
173
|
+
Raw content as bytes.
|
|
174
|
+
|
|
175
|
+
Raises:
|
|
176
|
+
ResourceNotFoundError: If the asset does not exist.
|
|
177
|
+
"""
|
|
178
|
+
return await self._get_resource(skill_id, "assets", name)
|
|
179
|
+
|
|
180
|
+
# ------------------------------------------------------------------
|
|
181
|
+
# References
|
|
182
|
+
# ------------------------------------------------------------------
|
|
183
|
+
|
|
184
|
+
async def get_reference(self, skill_id: str, name: str) -> bytes:
|
|
185
|
+
"""Fetch a single reference file.
|
|
186
|
+
|
|
187
|
+
Args:
|
|
188
|
+
skill_id: Skill name.
|
|
189
|
+
name: Reference filename.
|
|
190
|
+
|
|
191
|
+
Returns:
|
|
192
|
+
Raw content as bytes.
|
|
193
|
+
|
|
194
|
+
Raises:
|
|
195
|
+
ResourceNotFoundError: If the reference does not exist.
|
|
196
|
+
"""
|
|
197
|
+
return await self._get_resource(skill_id, "references", name)
|
|
198
|
+
|
|
199
|
+
# ------------------------------------------------------------------
|
|
200
|
+
# Internal helpers
|
|
201
|
+
# ------------------------------------------------------------------
|
|
202
|
+
|
|
203
|
+
async def _get_text(self, url: str) -> str:
|
|
204
|
+
"""GET a URL and return the response text.
|
|
205
|
+
|
|
206
|
+
Raises:
|
|
207
|
+
SkillNotFoundError: On 404.
|
|
208
|
+
AgentSkillsError: On other HTTP or connection errors.
|
|
209
|
+
"""
|
|
210
|
+
try:
|
|
211
|
+
resp = await self._client.get(url)
|
|
212
|
+
except httpx.HTTPError as exc:
|
|
213
|
+
raise AgentSkillsError(f"HTTP request failed: {url}") from exc
|
|
214
|
+
if resp.status_code == 404:
|
|
215
|
+
raise SkillNotFoundError(f"Not found: {url}")
|
|
216
|
+
try:
|
|
217
|
+
resp.raise_for_status()
|
|
218
|
+
except httpx.HTTPStatusError as exc:
|
|
219
|
+
raise AgentSkillsError(f"HTTP {resp.status_code} error for {url}") from exc
|
|
220
|
+
return resp.text
|
|
221
|
+
|
|
222
|
+
async def _get_bytes(self, url: str) -> bytes:
|
|
223
|
+
"""GET a URL and return the response bytes.
|
|
224
|
+
|
|
225
|
+
Raises:
|
|
226
|
+
ResourceNotFoundError: On 404.
|
|
227
|
+
AgentSkillsError: On other HTTP or connection errors.
|
|
228
|
+
"""
|
|
229
|
+
try:
|
|
230
|
+
resp = await self._client.get(url)
|
|
231
|
+
except httpx.HTTPError as exc:
|
|
232
|
+
raise AgentSkillsError(f"HTTP request failed: {url}") from exc
|
|
233
|
+
if resp.status_code == 404:
|
|
234
|
+
raise ResourceNotFoundError(f"Not found: {url}")
|
|
235
|
+
try:
|
|
236
|
+
resp.raise_for_status()
|
|
237
|
+
except httpx.HTTPStatusError as exc:
|
|
238
|
+
raise AgentSkillsError(f"HTTP {resp.status_code} error for {url}") from exc
|
|
239
|
+
return resp.content
|
|
240
|
+
|
|
241
|
+
async def _get_skill_md(self, skill_id: str) -> str:
|
|
242
|
+
"""Fetch the full text of a skill's ``SKILL.md``."""
|
|
243
|
+
url = f"{self._base_url}/{quote(skill_id)}/SKILL.md"
|
|
244
|
+
return await self._get_text(url)
|
|
245
|
+
|
|
246
|
+
async def _get_resource(self, skill_id: str, subdir: str, name: str) -> bytes:
|
|
247
|
+
"""Fetch a single resource file from a skill subdirectory."""
|
|
248
|
+
url = f"{self._base_url}/{quote(skill_id)}/{subdir}/{quote(name)}"
|
|
249
|
+
return await self._get_bytes(url)
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
[tool.poetry]
|
|
2
|
+
name = "agentskills-http"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "HTTP-based skill providers for the Agent Skills format (https://agentskills.io)"
|
|
5
|
+
license = "MIT"
|
|
6
|
+
authors = ["Pratik Panda"]
|
|
7
|
+
readme = "README.md"
|
|
8
|
+
homepage = "https://agentskills.io"
|
|
9
|
+
repository = "https://github.com/pratikxpanda/agentskills-sdk"
|
|
10
|
+
packages = [{include = "agentskills_http"}]
|
|
11
|
+
classifiers = [
|
|
12
|
+
"Development Status :: 3 - Alpha",
|
|
13
|
+
"Intended Audience :: Developers",
|
|
14
|
+
"Topic :: Software Development :: Libraries",
|
|
15
|
+
"Programming Language :: Python :: 3.12",
|
|
16
|
+
"Programming Language :: Python :: 3.13",
|
|
17
|
+
]
|
|
18
|
+
|
|
19
|
+
[tool.poetry.dependencies]
|
|
20
|
+
python = ">=3.12,<4.0"
|
|
21
|
+
agentskills-core = ">=0.1.0"
|
|
22
|
+
httpx = ">=0.27"
|
|
23
|
+
pyyaml = ">=6.0"
|
|
24
|
+
|
|
25
|
+
[build-system]
|
|
26
|
+
requires = ["poetry-core"]
|
|
27
|
+
build-backend = "poetry.core.masonry.api"
|