github-agent 0.2.55__tar.gz → 0.7.1__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.
- {github_agent-0.2.55/github_agent.egg-info → github_agent-0.7.1}/PKG-INFO +72 -4
- github_agent-0.2.55/PKG-INFO → github_agent-0.7.1/README.md +66 -18
- {github_agent-0.2.55 → github_agent-0.7.1}/github_agent/agent_server.py +2 -3
- github_agent-0.2.55/github_agent/api_wrapper.py → github_agent-0.7.1/github_agent/api_client.py +46 -44
- {github_agent-0.2.55 → github_agent-0.7.1}/github_agent/auth.py +10 -10
- {github_agent-0.2.55 → github_agent-0.7.1}/github_agent/github_input_models.py +33 -33
- {github_agent-0.2.55 → github_agent-0.7.1}/github_agent/github_response_models.py +45 -44
- github_agent-0.7.1/github_agent/mcp_config.json +3 -0
- {github_agent-0.2.55 → github_agent-0.7.1}/github_agent/mcp_server.py +35 -27
- github_agent-0.2.55/README.md → github_agent-0.7.1/github_agent.egg-info/PKG-INFO +86 -1
- {github_agent-0.2.55 → github_agent-0.7.1}/github_agent.egg-info/SOURCES.txt +5 -12
- github_agent-0.7.1/github_agent.egg-info/requires.txt +5 -0
- {github_agent-0.2.55 → github_agent-0.7.1}/github_agent.egg-info/top_level.txt +1 -0
- github_agent-0.7.1/pyproject.toml +52 -0
- github_agent-0.7.1/tests/test_github_agent_api_brute_force_coverage.py +107 -0
- github_agent-0.7.1/tests/test_github_agent_brute_force_coverage.py +155 -0
- github_agent-0.2.55/github_agent/agent_data/CRON.md +0 -12
- github_agent-0.2.55/github_agent/agent_data/CRON_LOG.md +0 -4
- github_agent-0.2.55/github_agent/agent_data/HEARTBEAT.md +0 -31
- github_agent-0.2.55/github_agent/agent_data/IDENTITY.md +0 -48
- github_agent-0.2.55/github_agent/agent_data/MEMORY.md +0 -8
- github_agent-0.2.55/github_agent/agent_data/NODE_AGENTS.md +0 -55
- github_agent-0.2.55/github_agent/agent_data/USER.md +0 -7
- github_agent-0.2.55/github_agent/agent_data/chats +0 -3
- github_agent-0.2.55/github_agent/agent_data/icon.png +0 -3
- github_agent-0.2.55/github_agent/agent_data/mcp_config.json +0 -18
- github_agent-0.2.55/github_agent.egg-info/requires.txt +0 -1
- github_agent-0.2.55/pyproject.toml +0 -33
- {github_agent-0.2.55 → github_agent-0.7.1}/LICENSE +0 -0
- {github_agent-0.2.55 → github_agent-0.7.1}/github_agent/__init__.py +0 -0
- {github_agent-0.2.55 → github_agent-0.7.1}/github_agent/__main__.py +0 -0
- {github_agent-0.2.55 → github_agent-0.7.1}/github_agent.egg-info/dependency_links.txt +0 -0
- {github_agent-0.2.55 → github_agent-0.7.1}/github_agent.egg-info/entry_points.txt +0 -0
- {github_agent-0.2.55 → github_agent-0.7.1}/scripts/validate_a2a_agent.py +2 -2
- {github_agent-0.2.55 → github_agent-0.7.1}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: github-agent
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.7.1
|
|
4
4
|
Summary: GitHub Agent for MCP
|
|
5
5
|
Author-email: Audel Rouhi <knucklessg1@gmail.com>
|
|
6
6
|
License: MIT
|
|
@@ -9,10 +9,13 @@ Classifier: License :: Public Domain
|
|
|
9
9
|
Classifier: Environment :: Console
|
|
10
10
|
Classifier: Operating System :: POSIX :: Linux
|
|
11
11
|
Classifier: Programming Language :: Python :: 3
|
|
12
|
-
Requires-Python:
|
|
12
|
+
Requires-Python: <3.14,>=3.11
|
|
13
13
|
Description-Content-Type: text/markdown
|
|
14
14
|
License-File: LICENSE
|
|
15
|
-
Requires-Dist: agent-utilities[agent,logfire]>=0.2
|
|
15
|
+
Requires-Dist: agent-utilities[agent,logfire]>=0.6.2
|
|
16
|
+
Provides-Extra: test
|
|
17
|
+
Requires-Dist: pytest; extra == "test"
|
|
18
|
+
Requires-Dist: pytest-asyncio; extra == "test"
|
|
16
19
|
Dynamic: license-file
|
|
17
20
|
|
|
18
21
|
# GitHub Agent - A2A | AG-UI | MCP
|
|
@@ -38,7 +41,7 @@ Dynamic: license-file
|
|
|
38
41
|

|
|
39
42
|

|
|
40
43
|
|
|
41
|
-
*Version: 0.
|
|
44
|
+
*Version: 0.7.1*
|
|
42
45
|
|
|
43
46
|
## Overview
|
|
44
47
|
|
|
@@ -222,3 +225,68 @@ docker-compose up -d
|
|
|
222
225
|
|
|
223
226
|

|
|
224
227
|

|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
## MCP Configuration Examples
|
|
231
|
+
|
|
232
|
+
### 1. Standard IO (stdio) Deployment
|
|
233
|
+
|
|
234
|
+
```json
|
|
235
|
+
{
|
|
236
|
+
"mcpServers": {
|
|
237
|
+
"github-agent": {
|
|
238
|
+
"command": "uv",
|
|
239
|
+
"args": [
|
|
240
|
+
"run",
|
|
241
|
+
"github-mcp"
|
|
242
|
+
],
|
|
243
|
+
"env": {
|
|
244
|
+
"AGENT_DESCRIPTION": "<YOUR_AGENT_DESCRIPTION>",
|
|
245
|
+
"AGENT_SYSTEM_PROMPT": "<YOUR_AGENT_SYSTEM_PROMPT>",
|
|
246
|
+
"CONTENTSTOOL": "True",
|
|
247
|
+
"DEFAULT_AGENT_NAME": "<YOUR_DEFAULT_AGENT_NAME>",
|
|
248
|
+
"GITHUB_TOKEN": "<YOUR_GITHUB_TOKEN>",
|
|
249
|
+
"GITHUB_URL": "<YOUR_GITHUB_URL>",
|
|
250
|
+
"GITHUB_VERIFY": "<YOUR_GITHUB_VERIFY>",
|
|
251
|
+
"ISSUETOOL": "True",
|
|
252
|
+
"PULLSTOOL": "True",
|
|
253
|
+
"REPOSTOOL": "True"
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
### 2. Streamable HTTP (SSE) Deployment
|
|
261
|
+
|
|
262
|
+
```json
|
|
263
|
+
{
|
|
264
|
+
"mcpServers": {
|
|
265
|
+
"github-agent": {
|
|
266
|
+
"command": "uv",
|
|
267
|
+
"args": [
|
|
268
|
+
"run",
|
|
269
|
+
"github-mcp",
|
|
270
|
+
"--transport",
|
|
271
|
+
"http",
|
|
272
|
+
"--host",
|
|
273
|
+
"0.0.0.0",
|
|
274
|
+
"--port",
|
|
275
|
+
"8000"
|
|
276
|
+
],
|
|
277
|
+
"env": {
|
|
278
|
+
"AGENT_DESCRIPTION": "<YOUR_AGENT_DESCRIPTION>",
|
|
279
|
+
"AGENT_SYSTEM_PROMPT": "<YOUR_AGENT_SYSTEM_PROMPT>",
|
|
280
|
+
"CONTENTSTOOL": "True",
|
|
281
|
+
"DEFAULT_AGENT_NAME": "<YOUR_DEFAULT_AGENT_NAME>",
|
|
282
|
+
"GITHUB_TOKEN": "<YOUR_GITHUB_TOKEN>",
|
|
283
|
+
"GITHUB_URL": "<YOUR_GITHUB_URL>",
|
|
284
|
+
"GITHUB_VERIFY": "<YOUR_GITHUB_VERIFY>",
|
|
285
|
+
"ISSUETOOL": "True",
|
|
286
|
+
"PULLSTOOL": "True",
|
|
287
|
+
"REPOSTOOL": "True"
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
```
|
|
@@ -1,20 +1,3 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: github-agent
|
|
3
|
-
Version: 0.2.55
|
|
4
|
-
Summary: GitHub Agent for MCP
|
|
5
|
-
Author-email: Audel Rouhi <knucklessg1@gmail.com>
|
|
6
|
-
License: MIT
|
|
7
|
-
Classifier: Development Status :: 5 - Production/Stable
|
|
8
|
-
Classifier: License :: Public Domain
|
|
9
|
-
Classifier: Environment :: Console
|
|
10
|
-
Classifier: Operating System :: POSIX :: Linux
|
|
11
|
-
Classifier: Programming Language :: Python :: 3
|
|
12
|
-
Requires-Python: >=3.10
|
|
13
|
-
Description-Content-Type: text/markdown
|
|
14
|
-
License-File: LICENSE
|
|
15
|
-
Requires-Dist: agent-utilities[agent,logfire]>=0.2.31
|
|
16
|
-
Dynamic: license-file
|
|
17
|
-
|
|
18
1
|
# GitHub Agent - A2A | AG-UI | MCP
|
|
19
2
|
|
|
20
3
|

|
|
@@ -38,7 +21,7 @@ Dynamic: license-file
|
|
|
38
21
|

|
|
39
22
|

|
|
40
23
|
|
|
41
|
-
*Version: 0.
|
|
24
|
+
*Version: 0.7.1*
|
|
42
25
|
|
|
43
26
|
## Overview
|
|
44
27
|
|
|
@@ -222,3 +205,68 @@ docker-compose up -d
|
|
|
222
205
|
|
|
223
206
|

|
|
224
207
|

|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
## MCP Configuration Examples
|
|
211
|
+
|
|
212
|
+
### 1. Standard IO (stdio) Deployment
|
|
213
|
+
|
|
214
|
+
```json
|
|
215
|
+
{
|
|
216
|
+
"mcpServers": {
|
|
217
|
+
"github-agent": {
|
|
218
|
+
"command": "uv",
|
|
219
|
+
"args": [
|
|
220
|
+
"run",
|
|
221
|
+
"github-mcp"
|
|
222
|
+
],
|
|
223
|
+
"env": {
|
|
224
|
+
"AGENT_DESCRIPTION": "<YOUR_AGENT_DESCRIPTION>",
|
|
225
|
+
"AGENT_SYSTEM_PROMPT": "<YOUR_AGENT_SYSTEM_PROMPT>",
|
|
226
|
+
"CONTENTSTOOL": "True",
|
|
227
|
+
"DEFAULT_AGENT_NAME": "<YOUR_DEFAULT_AGENT_NAME>",
|
|
228
|
+
"GITHUB_TOKEN": "<YOUR_GITHUB_TOKEN>",
|
|
229
|
+
"GITHUB_URL": "<YOUR_GITHUB_URL>",
|
|
230
|
+
"GITHUB_VERIFY": "<YOUR_GITHUB_VERIFY>",
|
|
231
|
+
"ISSUETOOL": "True",
|
|
232
|
+
"PULLSTOOL": "True",
|
|
233
|
+
"REPOSTOOL": "True"
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
### 2. Streamable HTTP (SSE) Deployment
|
|
241
|
+
|
|
242
|
+
```json
|
|
243
|
+
{
|
|
244
|
+
"mcpServers": {
|
|
245
|
+
"github-agent": {
|
|
246
|
+
"command": "uv",
|
|
247
|
+
"args": [
|
|
248
|
+
"run",
|
|
249
|
+
"github-mcp",
|
|
250
|
+
"--transport",
|
|
251
|
+
"http",
|
|
252
|
+
"--host",
|
|
253
|
+
"0.0.0.0",
|
|
254
|
+
"--port",
|
|
255
|
+
"8000"
|
|
256
|
+
],
|
|
257
|
+
"env": {
|
|
258
|
+
"AGENT_DESCRIPTION": "<YOUR_AGENT_DESCRIPTION>",
|
|
259
|
+
"AGENT_SYSTEM_PROMPT": "<YOUR_AGENT_SYSTEM_PROMPT>",
|
|
260
|
+
"CONTENTSTOOL": "True",
|
|
261
|
+
"DEFAULT_AGENT_NAME": "<YOUR_DEFAULT_AGENT_NAME>",
|
|
262
|
+
"GITHUB_TOKEN": "<YOUR_GITHUB_TOKEN>",
|
|
263
|
+
"GITHUB_URL": "<YOUR_GITHUB_URL>",
|
|
264
|
+
"GITHUB_VERIFY": "<YOUR_GITHUB_VERIFY>",
|
|
265
|
+
"ISSUETOOL": "True",
|
|
266
|
+
"PULLSTOOL": "True",
|
|
267
|
+
"REPOSTOOL": "True"
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
```
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/python
|
|
2
|
-
|
|
2
|
+
import logging
|
|
3
3
|
import os
|
|
4
4
|
import sys
|
|
5
|
-
import logging
|
|
6
5
|
import warnings
|
|
7
6
|
|
|
8
7
|
from agent_utilities import (
|
|
@@ -13,7 +12,7 @@ from agent_utilities import (
|
|
|
13
12
|
load_identity,
|
|
14
13
|
)
|
|
15
14
|
|
|
16
|
-
__version__ = "0.
|
|
15
|
+
__version__ = "0.7.1"
|
|
17
16
|
|
|
18
17
|
logging.basicConfig(
|
|
19
18
|
level=logging.INFO,
|
github_agent-0.2.55/github_agent/api_wrapper.py → github_agent-0.7.1/github_agent/api_client.py
RENAMED
|
@@ -1,51 +1,52 @@
|
|
|
1
1
|
#!/usr/bin/python
|
|
2
2
|
|
|
3
|
+
import logging
|
|
3
4
|
import re
|
|
5
|
+
from concurrent.futures import ThreadPoolExecutor, as_completed
|
|
6
|
+
from typing import TypeVar
|
|
7
|
+
|
|
4
8
|
import requests
|
|
5
9
|
import urllib3
|
|
6
|
-
import logging
|
|
7
|
-
from typing import List, TypeVar, Tuple
|
|
8
|
-
from pydantic import ValidationError
|
|
9
|
-
from concurrent.futures import ThreadPoolExecutor, as_completed
|
|
10
10
|
from agent_utilities.base_utilities import get_logger
|
|
11
|
+
from pydantic import BaseModel, ValidationError
|
|
11
12
|
|
|
12
13
|
logger = get_logger(__name__)
|
|
13
14
|
|
|
15
|
+
from agent_utilities.core.decorators import require_auth
|
|
16
|
+
from agent_utilities.core.exceptions import (
|
|
17
|
+
AuthError,
|
|
18
|
+
MissingParameterError,
|
|
19
|
+
ParameterError,
|
|
20
|
+
UnauthorizedError,
|
|
21
|
+
)
|
|
22
|
+
|
|
14
23
|
from github_agent.github_input_models import (
|
|
15
|
-
RepoModel,
|
|
16
|
-
IssueModel,
|
|
17
|
-
PullRequestModel,
|
|
18
|
-
ContentModel,
|
|
19
24
|
BranchModel,
|
|
20
25
|
CommitModel,
|
|
26
|
+
ContentModel,
|
|
27
|
+
IssueModel,
|
|
28
|
+
PullRequestModel,
|
|
29
|
+
RepoModel,
|
|
21
30
|
)
|
|
22
31
|
from github_agent.github_response_models import (
|
|
23
|
-
Repository,
|
|
24
|
-
Issue,
|
|
25
|
-
PullRequest,
|
|
26
|
-
Content,
|
|
27
32
|
Branch,
|
|
28
33
|
Commit,
|
|
34
|
+
Content,
|
|
35
|
+
Issue,
|
|
36
|
+
PullRequest,
|
|
37
|
+
Repository,
|
|
29
38
|
Response,
|
|
30
39
|
)
|
|
31
|
-
from agent_utilities.decorators import require_auth
|
|
32
|
-
from agent_utilities.exceptions import (
|
|
33
|
-
AuthError,
|
|
34
|
-
UnauthorizedError,
|
|
35
|
-
ParameterError,
|
|
36
|
-
MissingParameterError,
|
|
37
|
-
)
|
|
38
|
-
|
|
39
|
-
T = TypeVar("T")
|
|
40
40
|
|
|
41
|
+
T = TypeVar("T", bound=BaseModel)
|
|
41
42
|
|
|
42
|
-
class Api(object):
|
|
43
43
|
|
|
44
|
+
class Api:
|
|
44
45
|
def __init__(
|
|
45
46
|
self,
|
|
46
47
|
url: str = "https://api.github.com",
|
|
47
|
-
token: str = None,
|
|
48
|
-
proxies: dict = None,
|
|
48
|
+
token: str | None = None,
|
|
49
|
+
proxies: dict | None = None,
|
|
49
50
|
verify: bool = True,
|
|
50
51
|
debug: bool = False,
|
|
51
52
|
):
|
|
@@ -73,7 +74,6 @@ class Api(object):
|
|
|
73
74
|
if token:
|
|
74
75
|
self.headers["Authorization"] = f"Bearer {token}"
|
|
75
76
|
else:
|
|
76
|
-
|
|
77
77
|
logger.warning("No token provided for GitHub API")
|
|
78
78
|
|
|
79
79
|
try:
|
|
@@ -84,20 +84,20 @@ class Api(object):
|
|
|
84
84
|
proxies=self.proxies,
|
|
85
85
|
)
|
|
86
86
|
if response.status_code in (401, 403):
|
|
87
|
-
logger.error(f"Authentication Error: {response.
|
|
87
|
+
logger.error(f"Authentication Error: {response.text}")
|
|
88
88
|
raise AuthError if response.status_code == 401 else UnauthorizedError
|
|
89
89
|
except requests.exceptions.RequestException as e:
|
|
90
90
|
logger.error(f"Connection Error: {str(e)}")
|
|
91
91
|
|
|
92
92
|
def _fetch_next_page(
|
|
93
93
|
self, endpoint: str, model: T, header: dict, page: int
|
|
94
|
-
) ->
|
|
94
|
+
) -> list[dict]:
|
|
95
95
|
"""Fetch a single page of data from the specified endpoint"""
|
|
96
|
-
model.page = page
|
|
96
|
+
model.page = page # type: ignore[attr-defined]
|
|
97
97
|
model.model_post_init(None)
|
|
98
98
|
response = self._session.get(
|
|
99
99
|
url=f"{self.url}{endpoint}" if endpoint.startswith("/") else endpoint,
|
|
100
|
-
params=model.api_parameters,
|
|
100
|
+
params=model.api_parameters, # type: ignore[attr-defined]
|
|
101
101
|
headers=header,
|
|
102
102
|
verify=self.verify,
|
|
103
103
|
proxies=self.proxies,
|
|
@@ -119,7 +119,7 @@ class Api(object):
|
|
|
119
119
|
|
|
120
120
|
def _fetch_all_pages(
|
|
121
121
|
self, endpoint: str, model: T
|
|
122
|
-
) ->
|
|
122
|
+
) -> tuple[requests.Response, list[dict]]:
|
|
123
123
|
"""Generic method to fetch all pages with parallelization if possible"""
|
|
124
124
|
all_data = []
|
|
125
125
|
|
|
@@ -127,7 +127,7 @@ class Api(object):
|
|
|
127
127
|
|
|
128
128
|
response = self._session.get(
|
|
129
129
|
url=initial_url,
|
|
130
|
-
params=model.api_parameters,
|
|
130
|
+
params=model.api_parameters, # type: ignore[attr-defined]
|
|
131
131
|
headers=self.headers,
|
|
132
132
|
verify=self.verify,
|
|
133
133
|
proxies=self.proxies,
|
|
@@ -142,14 +142,15 @@ class Api(object):
|
|
|
142
142
|
|
|
143
143
|
total_pages = self._get_total_pages(response)
|
|
144
144
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
145
|
+
max_pages = getattr(model, "max_pages", total_pages)
|
|
146
|
+
if not max_pages or max_pages == 0 or max_pages > total_pages:
|
|
147
|
+
max_pages = total_pages
|
|
148
|
+
model.max_pages = total_pages # type: ignore[attr-defined]
|
|
149
149
|
|
|
150
|
+
if max_pages > 1:
|
|
150
151
|
with ThreadPoolExecutor(max_workers=5) as executor:
|
|
151
152
|
futures = []
|
|
152
|
-
for page in range(2,
|
|
153
|
+
for page in range(2, max_pages + 1):
|
|
153
154
|
futures.append(
|
|
154
155
|
executor.submit(
|
|
155
156
|
self._fetch_next_page,
|
|
@@ -177,7 +178,7 @@ class Api(object):
|
|
|
177
178
|
parsed_data = [Repository(**item) for item in data]
|
|
178
179
|
return Response(response=response, data=parsed_data)
|
|
179
180
|
except ValidationError as e:
|
|
180
|
-
raise ParameterError(f"Invalid parameters: {e.errors()}")
|
|
181
|
+
raise ParameterError(f"Invalid parameters: {e.errors()}") from e
|
|
181
182
|
|
|
182
183
|
@require_auth
|
|
183
184
|
def get_repository(self, owner: str, repo: str) -> Response:
|
|
@@ -194,8 +195,8 @@ class Api(object):
|
|
|
194
195
|
return Response(response=response, data=parsed_data)
|
|
195
196
|
except requests.exceptions.HTTPError as e:
|
|
196
197
|
if e.response.status_code == 404:
|
|
197
|
-
raise ParameterError(f"Repository {owner}/{repo} not found")
|
|
198
|
-
raise
|
|
198
|
+
raise ParameterError(f"Repository {owner}/{repo} not found") from e
|
|
199
|
+
raise
|
|
199
200
|
|
|
200
201
|
@require_auth
|
|
201
202
|
def get_issues(self, **kwargs) -> Response:
|
|
@@ -210,7 +211,7 @@ class Api(object):
|
|
|
210
211
|
parsed_data = [Issue(**item) for item in data]
|
|
211
212
|
return Response(response=response, data=parsed_data)
|
|
212
213
|
except ValidationError as e:
|
|
213
|
-
raise ParameterError(f"Invalid parameters: {e.errors()}")
|
|
214
|
+
raise ParameterError(f"Invalid parameters: {e.errors()}") from e
|
|
214
215
|
|
|
215
216
|
@require_auth
|
|
216
217
|
def get_pull_requests(self, **kwargs) -> Response:
|
|
@@ -225,7 +226,7 @@ class Api(object):
|
|
|
225
226
|
parsed_data = [PullRequest(**item) for item in data]
|
|
226
227
|
return Response(response=response, data=parsed_data)
|
|
227
228
|
except ValidationError as e:
|
|
228
|
-
raise ParameterError(f"Invalid parameters: {e.errors()}")
|
|
229
|
+
raise ParameterError(f"Invalid parameters: {e.errors()}") from e
|
|
229
230
|
|
|
230
231
|
@require_auth
|
|
231
232
|
def get_contents(self, **kwargs) -> Response:
|
|
@@ -241,13 +242,14 @@ class Api(object):
|
|
|
241
242
|
)
|
|
242
243
|
response.raise_for_status()
|
|
243
244
|
data = response.json()
|
|
245
|
+
parsed_data: list[Content] | Content
|
|
244
246
|
if isinstance(data, list):
|
|
245
247
|
parsed_data = [Content(**item) for item in data]
|
|
246
248
|
else:
|
|
247
249
|
parsed_data = Content(**data)
|
|
248
250
|
return Response(response=response, data=parsed_data)
|
|
249
251
|
except ValidationError as e:
|
|
250
|
-
raise ParameterError(f"Invalid parameters: {e.errors()}")
|
|
252
|
+
raise ParameterError(f"Invalid parameters: {e.errors()}") from e
|
|
251
253
|
|
|
252
254
|
@require_auth
|
|
253
255
|
def get_branches(self, **kwargs) -> Response:
|
|
@@ -260,7 +262,7 @@ class Api(object):
|
|
|
260
262
|
parsed_data = [Branch(**item) for item in data]
|
|
261
263
|
return Response(response=response, data=parsed_data)
|
|
262
264
|
except ValidationError as e:
|
|
263
|
-
raise ParameterError(f"Invalid parameters: {e.errors()}")
|
|
265
|
+
raise ParameterError(f"Invalid parameters: {e.errors()}") from e
|
|
264
266
|
|
|
265
267
|
@require_auth
|
|
266
268
|
def get_commits(self, **kwargs) -> Response:
|
|
@@ -273,4 +275,4 @@ class Api(object):
|
|
|
273
275
|
parsed_data = [Commit(**item) for item in data]
|
|
274
276
|
return Response(response=response, data=parsed_data)
|
|
275
277
|
except ValidationError as e:
|
|
276
|
-
raise ParameterError(f"Invalid parameters: {e.errors()}")
|
|
278
|
+
raise ParameterError(f"Invalid parameters: {e.errors()}") from e
|
|
@@ -2,12 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
import os
|
|
4
4
|
import threading
|
|
5
|
-
from typing import Optional
|
|
6
5
|
|
|
7
6
|
import requests
|
|
8
|
-
from agent_utilities.base_utilities import
|
|
9
|
-
from
|
|
10
|
-
|
|
7
|
+
from agent_utilities.base_utilities import get_logger, to_boolean
|
|
8
|
+
from agent_utilities.core.exceptions import AuthError, UnauthorizedError
|
|
9
|
+
|
|
10
|
+
from github_agent.api_client import Api
|
|
11
11
|
|
|
12
12
|
local = threading.local()
|
|
13
13
|
logger = get_logger(__name__)
|
|
@@ -15,9 +15,9 @@ logger = get_logger(__name__)
|
|
|
15
15
|
|
|
16
16
|
def get_client(
|
|
17
17
|
instance: str = os.getenv("GITHUB_URL", "https://api.github.com"),
|
|
18
|
-
token:
|
|
18
|
+
token: str | None = os.getenv("GITHUB_TOKEN", None),
|
|
19
19
|
verify: bool = to_boolean(string=os.getenv("GITHUB_VERIFY", "True")),
|
|
20
|
-
config:
|
|
20
|
+
config: dict | None = None,
|
|
21
21
|
) -> Api:
|
|
22
22
|
"""
|
|
23
23
|
Factory function to create the GitHub Api client.
|
|
@@ -45,22 +45,22 @@ def get_client(
|
|
|
45
45
|
exchange_data = {
|
|
46
46
|
"grant_type": "urn:ietf:params:oauth:grant-type:token-exchange",
|
|
47
47
|
"subject_token": user_token,
|
|
48
|
-
"subject_token_type": "urn:ietf:params:oauth:token-type:access_token",
|
|
49
|
-
"requested_token_type": "urn:ietf:params:oauth:token-type:access_token",
|
|
48
|
+
"subject_token_type": "urn:ietf:params:oauth:token-type:access_token", # nosec B105
|
|
49
|
+
"requested_token_type": "urn:ietf:params:oauth:token-type:access_token", # nosec B105
|
|
50
50
|
"audience": config["audience"],
|
|
51
51
|
"scope": config["delegated_scopes"],
|
|
52
52
|
}
|
|
53
53
|
auth = (config["oidc_client_id"], config["oidc_client_secret"])
|
|
54
54
|
try:
|
|
55
55
|
response = requests.post(
|
|
56
|
-
config["token_endpoint"], data=exchange_data, auth=auth
|
|
56
|
+
config["token_endpoint"], data=exchange_data, auth=auth, timeout=30
|
|
57
57
|
)
|
|
58
58
|
response.raise_for_status()
|
|
59
59
|
new_token = response.json()["access_token"]
|
|
60
60
|
logger.info("Token exchange successful")
|
|
61
61
|
except Exception as e:
|
|
62
62
|
logger.error(f"Token exchange failed: {str(e)}")
|
|
63
|
-
raise RuntimeError(f"Token exchange failed: {str(e)}")
|
|
63
|
+
raise RuntimeError(f"Token exchange failed: {str(e)}") from e
|
|
64
64
|
|
|
65
65
|
try:
|
|
66
66
|
return Api(
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
|
|
1
|
+
from typing import Any
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
#!/usr/bin/python
|
|
4
4
|
from pydantic import (
|
|
5
5
|
BaseModel,
|
|
6
6
|
Field,
|
|
@@ -12,12 +12,14 @@ class BaseModelWrapper(BaseModel):
|
|
|
12
12
|
Base Model wrapping common functionalities.
|
|
13
13
|
"""
|
|
14
14
|
|
|
15
|
-
max_pages:
|
|
15
|
+
max_pages: int | None = Field(
|
|
16
16
|
description="Max amount of pages to retrieve", default=None
|
|
17
17
|
)
|
|
18
|
-
page:
|
|
19
|
-
per_page:
|
|
20
|
-
api_parameters:
|
|
18
|
+
page: int | None = Field(description="Pagination page", default=1)
|
|
19
|
+
per_page: int | None = Field(description="Results per page", default=100)
|
|
20
|
+
api_parameters: dict[str, Any] = Field(
|
|
21
|
+
description="API Parameters", default_factory=dict
|
|
22
|
+
)
|
|
21
23
|
|
|
22
24
|
def model_post_init(self, __context):
|
|
23
25
|
self.api_parameters = {}
|
|
@@ -32,15 +34,13 @@ class RepoModel(BaseModelWrapper):
|
|
|
32
34
|
Pydantic model for Repository requests.
|
|
33
35
|
"""
|
|
34
36
|
|
|
35
|
-
owner:
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
repo: Optional[str] = Field(None, description="The name of the repository.")
|
|
39
|
-
visibility: Optional[str] = Field(
|
|
37
|
+
owner: str | None = Field(None, description="The account owner of the repository.")
|
|
38
|
+
repo: str | None = Field(None, description="The name of the repository.")
|
|
39
|
+
visibility: str | None = Field(
|
|
40
40
|
None, description="Can be one of all, public, or private."
|
|
41
41
|
)
|
|
42
|
-
affiliation:
|
|
43
|
-
type:
|
|
42
|
+
affiliation: str | None = Field(None, description="Affiliation filter.")
|
|
43
|
+
type: str | None = Field(
|
|
44
44
|
None, description="Can be one of all, owner, public, private, member."
|
|
45
45
|
)
|
|
46
46
|
|
|
@@ -61,25 +61,25 @@ class IssueModel(BaseModelWrapper):
|
|
|
61
61
|
|
|
62
62
|
owner: str = Field(..., description="The account owner of the repository.")
|
|
63
63
|
repo: str = Field(..., description="The name of the repository.")
|
|
64
|
-
issue_number:
|
|
64
|
+
issue_number: int | None = Field(
|
|
65
65
|
None, description="The number that identifies the issue."
|
|
66
66
|
)
|
|
67
|
-
state:
|
|
67
|
+
state: str | None = Field(
|
|
68
68
|
None,
|
|
69
69
|
description="Indicates the state of the issues to return. Can be either open, closed, or all.",
|
|
70
70
|
)
|
|
71
|
-
labels:
|
|
71
|
+
labels: str | list[str] | None = Field(
|
|
72
72
|
None, description="A list of comma separated label names."
|
|
73
73
|
)
|
|
74
|
-
assignee:
|
|
74
|
+
assignee: str | None = Field(
|
|
75
75
|
None,
|
|
76
76
|
description="Can be the name of a user. Use none for issues with no assigned user, and * for assigned issues to any user.",
|
|
77
77
|
)
|
|
78
|
-
creator:
|
|
79
|
-
mentioned:
|
|
78
|
+
creator: str | None = Field(None, description="The user that created the issue.")
|
|
79
|
+
mentioned: str | None = Field(
|
|
80
80
|
None, description="A user that is mentioned in the issue."
|
|
81
81
|
)
|
|
82
|
-
since:
|
|
82
|
+
since: str | None = Field(
|
|
83
83
|
None, description="Only show notifications updated after the given time."
|
|
84
84
|
)
|
|
85
85
|
|
|
@@ -105,22 +105,22 @@ class PullRequestModel(BaseModelWrapper):
|
|
|
105
105
|
|
|
106
106
|
owner: str = Field(..., description="The account owner of the repository.")
|
|
107
107
|
repo: str = Field(..., description="The name of the repository.")
|
|
108
|
-
pull_number:
|
|
108
|
+
pull_number: int | None = Field(
|
|
109
109
|
None, description="The number that identifies the pull request."
|
|
110
110
|
)
|
|
111
|
-
state:
|
|
111
|
+
state: str | None = Field(
|
|
112
112
|
None, description="State of the PR. (open, closed, or all)"
|
|
113
113
|
)
|
|
114
|
-
head:
|
|
114
|
+
head: str | None = Field(
|
|
115
115
|
None,
|
|
116
116
|
description="Filter pulls by head user or head organization and branch name in the format of user:ref-name or organization:ref-name.",
|
|
117
117
|
)
|
|
118
|
-
base:
|
|
119
|
-
sort:
|
|
118
|
+
base: str | None = Field(None, description="Filter pulls by base branch name.")
|
|
119
|
+
sort: str | None = Field(
|
|
120
120
|
None,
|
|
121
121
|
description="What to sort results by. Can be created, updated, popularity, long-running.",
|
|
122
122
|
)
|
|
123
|
-
direction:
|
|
123
|
+
direction: str | None = Field(
|
|
124
124
|
None, description="The direction of the sort. Can be asc or desc."
|
|
125
125
|
)
|
|
126
126
|
|
|
@@ -146,7 +146,7 @@ class ContentModel(BaseModelWrapper):
|
|
|
146
146
|
owner: str = Field(..., description="The account owner of the repository.")
|
|
147
147
|
repo: str = Field(..., description="The name of the repository.")
|
|
148
148
|
path: str = Field(..., description="The content path.")
|
|
149
|
-
ref:
|
|
149
|
+
ref: str | None = Field(
|
|
150
150
|
None,
|
|
151
151
|
description="The name of the commit/branch/tag. Default: the repository's default branch.",
|
|
152
152
|
)
|
|
@@ -164,7 +164,7 @@ class BranchModel(BaseModelWrapper):
|
|
|
164
164
|
|
|
165
165
|
owner: str = Field(..., description="The account owner of the repository.")
|
|
166
166
|
repo: str = Field(..., description="The name of the repository.")
|
|
167
|
-
branch:
|
|
167
|
+
branch: str | None = Field(None, description="The name of the branch.")
|
|
168
168
|
|
|
169
169
|
def model_post_init(self, __context):
|
|
170
170
|
super().model_post_init(__context)
|
|
@@ -177,19 +177,19 @@ class CommitModel(BaseModelWrapper):
|
|
|
177
177
|
|
|
178
178
|
owner: str = Field(..., description="The account owner of the repository.")
|
|
179
179
|
repo: str = Field(..., description="The name of the repository.")
|
|
180
|
-
sha:
|
|
180
|
+
sha: str | None = Field(
|
|
181
181
|
None, description="SHA or branch to start listing commits from."
|
|
182
182
|
)
|
|
183
|
-
path:
|
|
183
|
+
path: str | None = Field(
|
|
184
184
|
None, description="Only commits containing this file path will be returned."
|
|
185
185
|
)
|
|
186
|
-
author:
|
|
186
|
+
author: str | None = Field(
|
|
187
187
|
None, description="GitHub username or email address to filter by commit author."
|
|
188
188
|
)
|
|
189
|
-
since:
|
|
189
|
+
since: str | None = Field(
|
|
190
190
|
None, description="Only show notifications updated after the given time."
|
|
191
191
|
)
|
|
192
|
-
until:
|
|
192
|
+
until: str | None = Field(
|
|
193
193
|
None, description="Only show notifications updated before the given time."
|
|
194
194
|
)
|
|
195
195
|
|