@loopstack/github-module 0.1.0
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.
- package/README.md +67 -0
- package/dist/github-oauth.provider.d.ts +19 -0
- package/dist/github-oauth.provider.d.ts.map +1 -0
- package/dist/github-oauth.provider.js +91 -0
- package/dist/github-oauth.provider.js.map +1 -0
- package/dist/github.module.d.ts +3 -0
- package/dist/github.module.d.ts.map +1 -0
- package/dist/github.module.js +75 -0
- package/dist/github.module.js.map +1 -0
- package/dist/index.d.ts +28 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +44 -0
- package/dist/index.js.map +1 -0
- package/dist/tools/actions/github-get-workflow-run.tool.d.ts +13 -0
- package/dist/tools/actions/github-get-workflow-run.tool.d.ts.map +1 -0
- package/dist/tools/actions/github-get-workflow-run.tool.js +106 -0
- package/dist/tools/actions/github-get-workflow-run.tool.js.map +1 -0
- package/dist/tools/actions/github-list-workflow-runs.tool.d.ts +16 -0
- package/dist/tools/actions/github-list-workflow-runs.tool.d.ts.map +1 -0
- package/dist/tools/actions/github-list-workflow-runs.tool.js +129 -0
- package/dist/tools/actions/github-list-workflow-runs.tool.js.map +1 -0
- package/dist/tools/actions/github-trigger-workflow.tool.d.ts +15 -0
- package/dist/tools/actions/github-trigger-workflow.tool.d.ts.map +1 -0
- package/dist/tools/actions/github-trigger-workflow.tool.js +101 -0
- package/dist/tools/actions/github-trigger-workflow.tool.js.map +1 -0
- package/dist/tools/content/github-create-or-update-file.tool.d.ts +17 -0
- package/dist/tools/content/github-create-or-update-file.tool.d.ts.map +1 -0
- package/dist/tools/content/github-create-or-update-file.tool.js +115 -0
- package/dist/tools/content/github-create-or-update-file.tool.js.map +1 -0
- package/dist/tools/content/github-get-commit.tool.d.ts +13 -0
- package/dist/tools/content/github-get-commit.tool.d.ts.map +1 -0
- package/dist/tools/content/github-get-commit.tool.js +113 -0
- package/dist/tools/content/github-get-commit.tool.js.map +1 -0
- package/dist/tools/content/github-get-file-content.tool.d.ts +14 -0
- package/dist/tools/content/github-get-file-content.tool.d.ts.map +1 -0
- package/dist/tools/content/github-get-file-content.tool.js +107 -0
- package/dist/tools/content/github-get-file-content.tool.js.map +1 -0
- package/dist/tools/content/github-list-directory.tool.d.ts +14 -0
- package/dist/tools/content/github-list-directory.tool.d.ts.map +1 -0
- package/dist/tools/content/github-list-directory.tool.js +102 -0
- package/dist/tools/content/github-list-directory.tool.js.map +1 -0
- package/dist/tools/issues/github-create-issue-comment.tool.d.ts +14 -0
- package/dist/tools/issues/github-create-issue-comment.tool.d.ts.map +1 -0
- package/dist/tools/issues/github-create-issue-comment.tool.js +100 -0
- package/dist/tools/issues/github-create-issue-comment.tool.js.map +1 -0
- package/dist/tools/issues/github-create-issue.tool.d.ts +16 -0
- package/dist/tools/issues/github-create-issue.tool.d.ts.map +1 -0
- package/dist/tools/issues/github-create-issue.tool.js +112 -0
- package/dist/tools/issues/github-create-issue.tool.js.map +1 -0
- package/dist/tools/issues/github-get-issue.tool.d.ts +13 -0
- package/dist/tools/issues/github-get-issue.tool.d.ts.map +1 -0
- package/dist/tools/issues/github-get-issue.tool.js +106 -0
- package/dist/tools/issues/github-get-issue.tool.js.map +1 -0
- package/dist/tools/issues/github-list-issues.tool.d.ts +17 -0
- package/dist/tools/issues/github-list-issues.tool.d.ts.map +1 -0
- package/dist/tools/issues/github-list-issues.tool.js +115 -0
- package/dist/tools/issues/github-list-issues.tool.js.map +1 -0
- package/dist/tools/pull-requests/github-create-pull-request.tool.d.ts +17 -0
- package/dist/tools/pull-requests/github-create-pull-request.tool.d.ts.map +1 -0
- package/dist/tools/pull-requests/github-create-pull-request.tool.js +113 -0
- package/dist/tools/pull-requests/github-create-pull-request.tool.js.map +1 -0
- package/dist/tools/pull-requests/github-get-pull-request.tool.d.ts +13 -0
- package/dist/tools/pull-requests/github-get-pull-request.tool.d.ts.map +1 -0
- package/dist/tools/pull-requests/github-get-pull-request.tool.js +113 -0
- package/dist/tools/pull-requests/github-get-pull-request.tool.js.map +1 -0
- package/dist/tools/pull-requests/github-list-pr-reviews.tool.d.ts +13 -0
- package/dist/tools/pull-requests/github-list-pr-reviews.tool.d.ts.map +1 -0
- package/dist/tools/pull-requests/github-list-pr-reviews.tool.js +97 -0
- package/dist/tools/pull-requests/github-list-pr-reviews.tool.js.map +1 -0
- package/dist/tools/pull-requests/github-list-pull-requests.tool.d.ts +16 -0
- package/dist/tools/pull-requests/github-list-pull-requests.tool.d.ts.map +1 -0
- package/dist/tools/pull-requests/github-list-pull-requests.tool.js +113 -0
- package/dist/tools/pull-requests/github-list-pull-requests.tool.js.map +1 -0
- package/dist/tools/pull-requests/github-merge-pull-request.tool.d.ts +16 -0
- package/dist/tools/pull-requests/github-merge-pull-request.tool.d.ts.map +1 -0
- package/dist/tools/pull-requests/github-merge-pull-request.tool.js +108 -0
- package/dist/tools/pull-requests/github-merge-pull-request.tool.js.map +1 -0
- package/dist/tools/repos/github-create-repo.tool.d.ts +14 -0
- package/dist/tools/repos/github-create-repo.tool.d.ts.map +1 -0
- package/dist/tools/repos/github-create-repo.tool.js +108 -0
- package/dist/tools/repos/github-create-repo.tool.js.map +1 -0
- package/dist/tools/repos/github-get-repo.tool.d.ts +12 -0
- package/dist/tools/repos/github-get-repo.tool.d.ts.map +1 -0
- package/dist/tools/repos/github-get-repo.tool.js +107 -0
- package/dist/tools/repos/github-get-repo.tool.js.map +1 -0
- package/dist/tools/repos/github-list-branches.tool.d.ts +13 -0
- package/dist/tools/repos/github-list-branches.tool.d.ts.map +1 -0
- package/dist/tools/repos/github-list-branches.tool.js +97 -0
- package/dist/tools/repos/github-list-branches.tool.js.map +1 -0
- package/dist/tools/repos/github-list-repos.tool.d.ts +14 -0
- package/dist/tools/repos/github-list-repos.tool.d.ts.map +1 -0
- package/dist/tools/repos/github-list-repos.tool.js +107 -0
- package/dist/tools/repos/github-list-repos.tool.js.map +1 -0
- package/dist/tools/search/github-search-code.tool.d.ts +13 -0
- package/dist/tools/search/github-search-code.tool.d.ts.map +1 -0
- package/dist/tools/search/github-search-code.tool.js +100 -0
- package/dist/tools/search/github-search-code.tool.js.map +1 -0
- package/dist/tools/search/github-search-issues.tool.d.ts +14 -0
- package/dist/tools/search/github-search-issues.tool.d.ts.map +1 -0
- package/dist/tools/search/github-search-issues.tool.js +109 -0
- package/dist/tools/search/github-search-issues.tool.js.map +1 -0
- package/dist/tools/search/github-search-repos.tool.d.ts +14 -0
- package/dist/tools/search/github-search-repos.tool.d.ts.map +1 -0
- package/dist/tools/search/github-search-repos.tool.js +106 -0
- package/dist/tools/search/github-search-repos.tool.js.map +1 -0
- package/dist/tools/users/github-get-authenticated-user.tool.d.ts +9 -0
- package/dist/tools/users/github-get-authenticated-user.tool.d.ts.map +1 -0
- package/dist/tools/users/github-get-authenticated-user.tool.js +96 -0
- package/dist/tools/users/github-get-authenticated-user.tool.js.map +1 -0
- package/dist/tools/users/github-list-user-orgs.tool.d.ts +11 -0
- package/dist/tools/users/github-list-user-orgs.tool.d.ts.map +1 -0
- package/dist/tools/users/github-list-user-orgs.tool.js +95 -0
- package/dist/tools/users/github-list-user-orgs.tool.js.map +1 -0
- package/package.json +81 -0
- package/src/github-oauth.provider.ts +85 -0
- package/src/github.module.ts +63 -0
- package/src/index.ts +27 -0
- package/src/tools/actions/github-get-workflow-run.tool.ts +115 -0
- package/src/tools/actions/github-list-workflow-runs.tool.ts +141 -0
- package/src/tools/actions/github-trigger-workflow.tool.ts +97 -0
- package/src/tools/content/github-create-or-update-file.tool.ts +124 -0
- package/src/tools/content/github-get-commit.tool.ts +124 -0
- package/src/tools/content/github-get-file-content.tool.ts +112 -0
- package/src/tools/content/github-list-directory.tool.ts +105 -0
- package/src/tools/issues/github-create-issue-comment.tool.ts +100 -0
- package/src/tools/issues/github-create-issue.tool.ts +114 -0
- package/src/tools/issues/github-get-issue.tool.ts +115 -0
- package/src/tools/issues/github-list-issues.tool.ts +126 -0
- package/src/tools/pull-requests/github-create-pull-request.tool.ts +119 -0
- package/src/tools/pull-requests/github-get-pull-request.tool.ts +128 -0
- package/src/tools/pull-requests/github-list-pr-reviews.tool.ts +99 -0
- package/src/tools/pull-requests/github-list-pull-requests.tool.ts +124 -0
- package/src/tools/pull-requests/github-merge-pull-request.tool.ts +108 -0
- package/src/tools/repos/github-create-repo.tool.ts +111 -0
- package/src/tools/repos/github-get-repo.tool.ts +120 -0
- package/src/tools/repos/github-list-branches.tool.ts +97 -0
- package/src/tools/repos/github-list-repos.tool.ts +115 -0
- package/src/tools/search/github-search-code.tool.ts +105 -0
- package/src/tools/search/github-search-issues.tool.ts +120 -0
- package/src/tools/search/github-search-repos.tool.ts +115 -0
- package/src/tools/users/github-get-authenticated-user.tool.ts +98 -0
- package/src/tools/users/github-list-user-orgs.tool.ts +94 -0
package/package.json
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@loopstack/github-module",
|
|
3
|
+
"displayName": "GitHub Module",
|
|
4
|
+
"description": "GitHub integration for the Loopstack automation framework. Provides a GitHub OAuth provider and tools for GitHub services such as repositories, issues, pull requests, actions, and more.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"authentication",
|
|
7
|
+
"github",
|
|
8
|
+
"loopstack",
|
|
9
|
+
"module",
|
|
10
|
+
"oauth"
|
|
11
|
+
],
|
|
12
|
+
"version": "0.1.0",
|
|
13
|
+
"license": "Apache-2.0",
|
|
14
|
+
"author": {
|
|
15
|
+
"name": "Jakob Klippel",
|
|
16
|
+
"url": "https://www.linkedin.com/in/jakob-klippel/"
|
|
17
|
+
},
|
|
18
|
+
"main": "dist/index.js",
|
|
19
|
+
"types": "dist/index.d.ts",
|
|
20
|
+
"exports": {
|
|
21
|
+
".": "./dist/index.js",
|
|
22
|
+
"./src/*": "./src/*"
|
|
23
|
+
},
|
|
24
|
+
"scripts": {
|
|
25
|
+
"build": "nest build",
|
|
26
|
+
"compile": "tsc --noEmit",
|
|
27
|
+
"format": "prettier --write .",
|
|
28
|
+
"lint": "eslint .",
|
|
29
|
+
"test": "jest --passWithNoTests",
|
|
30
|
+
"watch": "nest build --watch"
|
|
31
|
+
},
|
|
32
|
+
"dependencies": {
|
|
33
|
+
"@loopstack/oauth-module": "^0.1.3",
|
|
34
|
+
"zod": "^4.3.6"
|
|
35
|
+
},
|
|
36
|
+
"files": [
|
|
37
|
+
"dist",
|
|
38
|
+
"src"
|
|
39
|
+
],
|
|
40
|
+
"jest": {
|
|
41
|
+
"moduleFileExtensions": [
|
|
42
|
+
"js",
|
|
43
|
+
"json",
|
|
44
|
+
"ts"
|
|
45
|
+
],
|
|
46
|
+
"rootDir": "src",
|
|
47
|
+
"testRegex": ".*\\.spec\\.ts$",
|
|
48
|
+
"transform": {
|
|
49
|
+
"^.+\\.ts$": "ts-jest"
|
|
50
|
+
},
|
|
51
|
+
"collectCoverageFrom": [
|
|
52
|
+
"**/*.(t|j)s"
|
|
53
|
+
],
|
|
54
|
+
"coverageDirectory": "../coverage",
|
|
55
|
+
"testEnvironment": "node",
|
|
56
|
+
"maxWorkers": 1
|
|
57
|
+
},
|
|
58
|
+
"loopstack": {
|
|
59
|
+
"modules": [
|
|
60
|
+
{
|
|
61
|
+
"path": "src/github.module.ts",
|
|
62
|
+
"className": "GitHubModule"
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
"package": "@loopstack/oauth-module",
|
|
66
|
+
"className": "OAuthModule"
|
|
67
|
+
}
|
|
68
|
+
],
|
|
69
|
+
"workflows": [
|
|
70
|
+
{
|
|
71
|
+
"package": "@loopstack/oauth-module",
|
|
72
|
+
"className": "OAuthWorkflow",
|
|
73
|
+
"propertyName": "oAuth"
|
|
74
|
+
}
|
|
75
|
+
],
|
|
76
|
+
"installModes": [
|
|
77
|
+
"add",
|
|
78
|
+
"install"
|
|
79
|
+
]
|
|
80
|
+
}
|
|
81
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { Injectable, Logger, OnModuleInit } from '@nestjs/common';
|
|
2
|
+
import { ConfigService } from '@nestjs/config';
|
|
3
|
+
import { OAuthProviderInterface, OAuthProviderRegistry, OAuthTokenSet } from '@loopstack/oauth-module';
|
|
4
|
+
|
|
5
|
+
@Injectable()
|
|
6
|
+
export class GitHubOAuthProvider implements OAuthProviderInterface, OnModuleInit {
|
|
7
|
+
private readonly logger = new Logger(GitHubOAuthProvider.name);
|
|
8
|
+
|
|
9
|
+
readonly providerId = 'github';
|
|
10
|
+
readonly defaultScopes = ['repo', 'user', 'workflow', 'read:org'];
|
|
11
|
+
|
|
12
|
+
constructor(
|
|
13
|
+
private readonly configService: ConfigService,
|
|
14
|
+
private readonly providerRegistry: OAuthProviderRegistry,
|
|
15
|
+
) {}
|
|
16
|
+
|
|
17
|
+
onModuleInit(): void {
|
|
18
|
+
this.providerRegistry.register(this);
|
|
19
|
+
this.logger.log('GitHub OAuth provider registered');
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
private get clientId(): string {
|
|
23
|
+
const id = this.configService.get<string>('GITHUB_CLIENT_ID');
|
|
24
|
+
if (!id) throw new Error('GITHUB_CLIENT_ID is not configured');
|
|
25
|
+
return id;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
private get clientSecret(): string {
|
|
29
|
+
const secret = this.configService.get<string>('GITHUB_CLIENT_SECRET');
|
|
30
|
+
if (!secret) throw new Error('GITHUB_CLIENT_SECRET is not configured');
|
|
31
|
+
return secret;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
private get redirectUri(): string {
|
|
35
|
+
return this.configService.get<string>('GITHUB_OAUTH_REDIRECT_URI', '/oauth/callback');
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
buildAuthUrl(scopes: string[], state: string): string {
|
|
39
|
+
const params = new URLSearchParams({
|
|
40
|
+
client_id: this.clientId,
|
|
41
|
+
redirect_uri: this.redirectUri,
|
|
42
|
+
scope: scopes.join(' '),
|
|
43
|
+
state,
|
|
44
|
+
});
|
|
45
|
+
return `https://github.com/login/oauth/authorize?${params.toString()}`;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
async exchangeCode(code: string): Promise<OAuthTokenSet> {
|
|
49
|
+
const response = await fetch('https://github.com/login/oauth/access_token', {
|
|
50
|
+
method: 'POST',
|
|
51
|
+
headers: {
|
|
52
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
53
|
+
Accept: 'application/json',
|
|
54
|
+
},
|
|
55
|
+
body: new URLSearchParams({
|
|
56
|
+
code,
|
|
57
|
+
client_id: this.clientId,
|
|
58
|
+
client_secret: this.clientSecret,
|
|
59
|
+
redirect_uri: this.redirectUri,
|
|
60
|
+
}),
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
if (!response.ok) {
|
|
64
|
+
const errorBody = await response.text();
|
|
65
|
+
this.logger.error(`Token exchange failed: ${response.status} ${errorBody}`);
|
|
66
|
+
throw new Error(`GitHub token exchange failed: ${response.statusText}`);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const tokens = (await response.json()) as {
|
|
70
|
+
access_token: string;
|
|
71
|
+
token_type: string;
|
|
72
|
+
scope: string;
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
return {
|
|
76
|
+
accessToken: tokens.access_token,
|
|
77
|
+
expiresIn: 315360000,
|
|
78
|
+
scope: tokens.scope,
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
refreshToken(): Promise<OAuthTokenSet> {
|
|
83
|
+
throw new Error('GitHub tokens do not expire and cannot be refreshed.');
|
|
84
|
+
}
|
|
85
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { Module } from '@nestjs/common';
|
|
2
|
+
import { OAuthModule } from '@loopstack/oauth-module';
|
|
3
|
+
import { GitHubOAuthProvider } from './github-oauth.provider';
|
|
4
|
+
import { GitHubGetWorkflowRunTool } from './tools/actions/github-get-workflow-run.tool';
|
|
5
|
+
import { GitHubListWorkflowRunsTool } from './tools/actions/github-list-workflow-runs.tool';
|
|
6
|
+
import { GitHubTriggerWorkflowTool } from './tools/actions/github-trigger-workflow.tool';
|
|
7
|
+
import { GitHubCreateOrUpdateFileTool } from './tools/content/github-create-or-update-file.tool';
|
|
8
|
+
import { GitHubGetCommitTool } from './tools/content/github-get-commit.tool';
|
|
9
|
+
import { GitHubGetFileContentTool } from './tools/content/github-get-file-content.tool';
|
|
10
|
+
import { GitHubListDirectoryTool } from './tools/content/github-list-directory.tool';
|
|
11
|
+
import { GitHubCreateIssueCommentTool } from './tools/issues/github-create-issue-comment.tool';
|
|
12
|
+
import { GitHubCreateIssueTool } from './tools/issues/github-create-issue.tool';
|
|
13
|
+
import { GitHubGetIssueTool } from './tools/issues/github-get-issue.tool';
|
|
14
|
+
import { GitHubListIssuesTool } from './tools/issues/github-list-issues.tool';
|
|
15
|
+
import { GitHubCreatePullRequestTool } from './tools/pull-requests/github-create-pull-request.tool';
|
|
16
|
+
import { GitHubGetPullRequestTool } from './tools/pull-requests/github-get-pull-request.tool';
|
|
17
|
+
import { GitHubListPrReviewsTool } from './tools/pull-requests/github-list-pr-reviews.tool';
|
|
18
|
+
import { GitHubListPullRequestsTool } from './tools/pull-requests/github-list-pull-requests.tool';
|
|
19
|
+
import { GitHubMergePullRequestTool } from './tools/pull-requests/github-merge-pull-request.tool';
|
|
20
|
+
import { GitHubCreateRepoTool } from './tools/repos/github-create-repo.tool';
|
|
21
|
+
import { GitHubGetRepoTool } from './tools/repos/github-get-repo.tool';
|
|
22
|
+
import { GitHubListBranchesTool } from './tools/repos/github-list-branches.tool';
|
|
23
|
+
import { GitHubListReposTool } from './tools/repos/github-list-repos.tool';
|
|
24
|
+
import { GitHubSearchCodeTool } from './tools/search/github-search-code.tool';
|
|
25
|
+
import { GitHubSearchIssuesTool } from './tools/search/github-search-issues.tool';
|
|
26
|
+
import { GitHubSearchReposTool } from './tools/search/github-search-repos.tool';
|
|
27
|
+
import { GitHubGetAuthenticatedUserTool } from './tools/users/github-get-authenticated-user.tool';
|
|
28
|
+
import { GitHubListUserOrgsTool } from './tools/users/github-list-user-orgs.tool';
|
|
29
|
+
|
|
30
|
+
const tools = [
|
|
31
|
+
GitHubListReposTool,
|
|
32
|
+
GitHubGetRepoTool,
|
|
33
|
+
GitHubCreateRepoTool,
|
|
34
|
+
GitHubListBranchesTool,
|
|
35
|
+
GitHubListIssuesTool,
|
|
36
|
+
GitHubGetIssueTool,
|
|
37
|
+
GitHubCreateIssueTool,
|
|
38
|
+
GitHubCreateIssueCommentTool,
|
|
39
|
+
GitHubListPullRequestsTool,
|
|
40
|
+
GitHubGetPullRequestTool,
|
|
41
|
+
GitHubCreatePullRequestTool,
|
|
42
|
+
GitHubMergePullRequestTool,
|
|
43
|
+
GitHubListPrReviewsTool,
|
|
44
|
+
GitHubGetFileContentTool,
|
|
45
|
+
GitHubCreateOrUpdateFileTool,
|
|
46
|
+
GitHubListDirectoryTool,
|
|
47
|
+
GitHubGetCommitTool,
|
|
48
|
+
GitHubListWorkflowRunsTool,
|
|
49
|
+
GitHubTriggerWorkflowTool,
|
|
50
|
+
GitHubGetWorkflowRunTool,
|
|
51
|
+
GitHubSearchCodeTool,
|
|
52
|
+
GitHubSearchReposTool,
|
|
53
|
+
GitHubSearchIssuesTool,
|
|
54
|
+
GitHubGetAuthenticatedUserTool,
|
|
55
|
+
GitHubListUserOrgsTool,
|
|
56
|
+
];
|
|
57
|
+
|
|
58
|
+
@Module({
|
|
59
|
+
imports: [OAuthModule],
|
|
60
|
+
providers: [GitHubOAuthProvider, ...tools],
|
|
61
|
+
exports: [GitHubOAuthProvider, ...tools],
|
|
62
|
+
})
|
|
63
|
+
export class GitHubModule {}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export * from './github.module';
|
|
2
|
+
export * from './github-oauth.provider';
|
|
3
|
+
export * from './tools/repos/github-list-repos.tool';
|
|
4
|
+
export * from './tools/repos/github-get-repo.tool';
|
|
5
|
+
export * from './tools/repos/github-create-repo.tool';
|
|
6
|
+
export * from './tools/repos/github-list-branches.tool';
|
|
7
|
+
export * from './tools/issues/github-list-issues.tool';
|
|
8
|
+
export * from './tools/issues/github-get-issue.tool';
|
|
9
|
+
export * from './tools/issues/github-create-issue.tool';
|
|
10
|
+
export * from './tools/issues/github-create-issue-comment.tool';
|
|
11
|
+
export * from './tools/pull-requests/github-list-pull-requests.tool';
|
|
12
|
+
export * from './tools/pull-requests/github-get-pull-request.tool';
|
|
13
|
+
export * from './tools/pull-requests/github-create-pull-request.tool';
|
|
14
|
+
export * from './tools/pull-requests/github-merge-pull-request.tool';
|
|
15
|
+
export * from './tools/pull-requests/github-list-pr-reviews.tool';
|
|
16
|
+
export * from './tools/content/github-get-file-content.tool';
|
|
17
|
+
export * from './tools/content/github-create-or-update-file.tool';
|
|
18
|
+
export * from './tools/content/github-list-directory.tool';
|
|
19
|
+
export * from './tools/content/github-get-commit.tool';
|
|
20
|
+
export * from './tools/actions/github-list-workflow-runs.tool';
|
|
21
|
+
export * from './tools/actions/github-trigger-workflow.tool';
|
|
22
|
+
export * from './tools/actions/github-get-workflow-run.tool';
|
|
23
|
+
export * from './tools/search/github-search-code.tool';
|
|
24
|
+
export * from './tools/search/github-search-repos.tool';
|
|
25
|
+
export * from './tools/search/github-search-issues.tool';
|
|
26
|
+
export * from './tools/users/github-get-authenticated-user.tool';
|
|
27
|
+
export * from './tools/users/github-list-user-orgs.tool';
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { Inject, Logger } from '@nestjs/common';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import { Input, RunContext, Tool, ToolInterface, ToolResult } from '@loopstack/common';
|
|
4
|
+
import { OAuthTokenStore } from '@loopstack/oauth-module';
|
|
5
|
+
|
|
6
|
+
export type GitHubGetWorkflowRunArgs = {
|
|
7
|
+
owner: string;
|
|
8
|
+
repo: string;
|
|
9
|
+
runId: number;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
@Tool({
|
|
13
|
+
config: {
|
|
14
|
+
description:
|
|
15
|
+
'Gets detailed information about a specific GitHub Actions workflow run. Returns { error: "unauthorized" } if no valid token is available.',
|
|
16
|
+
},
|
|
17
|
+
})
|
|
18
|
+
export class GitHubGetWorkflowRunTool implements ToolInterface {
|
|
19
|
+
private readonly logger = new Logger(GitHubGetWorkflowRunTool.name);
|
|
20
|
+
|
|
21
|
+
@Inject()
|
|
22
|
+
private tokenStore: OAuthTokenStore;
|
|
23
|
+
|
|
24
|
+
@Input({
|
|
25
|
+
schema: z
|
|
26
|
+
.object({
|
|
27
|
+
owner: z.string(),
|
|
28
|
+
repo: z.string(),
|
|
29
|
+
runId: z.number(),
|
|
30
|
+
})
|
|
31
|
+
.strict(),
|
|
32
|
+
})
|
|
33
|
+
args: GitHubGetWorkflowRunArgs;
|
|
34
|
+
|
|
35
|
+
async execute(args: GitHubGetWorkflowRunArgs, ctx: RunContext): Promise<ToolResult> {
|
|
36
|
+
const accessToken = await this.tokenStore.getValidAccessToken(ctx.userId, 'github');
|
|
37
|
+
|
|
38
|
+
if (!accessToken) {
|
|
39
|
+
return {
|
|
40
|
+
data: {
|
|
41
|
+
error: 'unauthorized',
|
|
42
|
+
message: 'No valid GitHub token found. Please authenticate first.',
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const url = `https://api.github.com/repos/${encodeURIComponent(args.owner)}/${encodeURIComponent(args.repo)}/actions/runs/${args.runId}`;
|
|
48
|
+
const response = await fetch(url, {
|
|
49
|
+
headers: {
|
|
50
|
+
Authorization: `Bearer ${accessToken}`,
|
|
51
|
+
Accept: 'application/vnd.github+json',
|
|
52
|
+
'X-GitHub-Api-Version': '2022-11-28',
|
|
53
|
+
},
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
if (response.status === 401 || response.status === 403) {
|
|
57
|
+
this.logger.warn(`GitHub API returned ${response.status} for user ${ctx.userId}`);
|
|
58
|
+
return {
|
|
59
|
+
data: {
|
|
60
|
+
error: '401',
|
|
61
|
+
message: 'GitHub token was rejected. Please re-authenticate.',
|
|
62
|
+
},
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (!response.ok) {
|
|
67
|
+
const body = await response.text();
|
|
68
|
+
this.logger.error(`GitHub API error: ${response.status} ${body}`);
|
|
69
|
+
return {
|
|
70
|
+
data: {
|
|
71
|
+
error: 'api_error',
|
|
72
|
+
message: `GitHub API error: ${response.statusText}`,
|
|
73
|
+
},
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const run = (await response.json()) as {
|
|
78
|
+
id: number;
|
|
79
|
+
name: string;
|
|
80
|
+
status: string;
|
|
81
|
+
conclusion: string | null;
|
|
82
|
+
head_branch: string;
|
|
83
|
+
head_sha: string;
|
|
84
|
+
event: string;
|
|
85
|
+
workflow_id: number;
|
|
86
|
+
run_number: number;
|
|
87
|
+
run_attempt: number;
|
|
88
|
+
created_at: string;
|
|
89
|
+
updated_at: string;
|
|
90
|
+
run_started_at: string;
|
|
91
|
+
html_url: string;
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
return {
|
|
95
|
+
data: {
|
|
96
|
+
run: {
|
|
97
|
+
id: run.id,
|
|
98
|
+
name: run.name,
|
|
99
|
+
status: run.status,
|
|
100
|
+
conclusion: run.conclusion,
|
|
101
|
+
headBranch: run.head_branch,
|
|
102
|
+
headSha: run.head_sha,
|
|
103
|
+
event: run.event,
|
|
104
|
+
workflowId: run.workflow_id,
|
|
105
|
+
runNumber: run.run_number,
|
|
106
|
+
runAttempt: run.run_attempt,
|
|
107
|
+
createdAt: run.created_at,
|
|
108
|
+
updatedAt: run.updated_at,
|
|
109
|
+
runStartedAt: run.run_started_at,
|
|
110
|
+
htmlUrl: run.html_url,
|
|
111
|
+
},
|
|
112
|
+
},
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import { Inject, Logger } from '@nestjs/common';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import { Input, RunContext, Tool, ToolInterface, ToolResult } from '@loopstack/common';
|
|
4
|
+
import { OAuthTokenStore } from '@loopstack/oauth-module';
|
|
5
|
+
|
|
6
|
+
export type GitHubListWorkflowRunsArgs = {
|
|
7
|
+
owner: string;
|
|
8
|
+
repo: string;
|
|
9
|
+
branch?: string;
|
|
10
|
+
status?: string;
|
|
11
|
+
perPage?: number;
|
|
12
|
+
page?: number;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
@Tool({
|
|
16
|
+
config: {
|
|
17
|
+
description:
|
|
18
|
+
'Lists workflow runs for a GitHub repository. Returns { error: "unauthorized" } if no valid token is available.',
|
|
19
|
+
},
|
|
20
|
+
})
|
|
21
|
+
export class GitHubListWorkflowRunsTool implements ToolInterface {
|
|
22
|
+
private readonly logger = new Logger(GitHubListWorkflowRunsTool.name);
|
|
23
|
+
|
|
24
|
+
@Inject()
|
|
25
|
+
private tokenStore: OAuthTokenStore;
|
|
26
|
+
|
|
27
|
+
@Input({
|
|
28
|
+
schema: z
|
|
29
|
+
.object({
|
|
30
|
+
owner: z.string(),
|
|
31
|
+
repo: z.string(),
|
|
32
|
+
branch: z.string().optional(),
|
|
33
|
+
status: z
|
|
34
|
+
.enum([
|
|
35
|
+
'completed',
|
|
36
|
+
'action_required',
|
|
37
|
+
'cancelled',
|
|
38
|
+
'failure',
|
|
39
|
+
'neutral',
|
|
40
|
+
'skipped',
|
|
41
|
+
'stale',
|
|
42
|
+
'success',
|
|
43
|
+
'timed_out',
|
|
44
|
+
'in_progress',
|
|
45
|
+
'queued',
|
|
46
|
+
'requested',
|
|
47
|
+
'waiting',
|
|
48
|
+
'pending',
|
|
49
|
+
])
|
|
50
|
+
.optional(),
|
|
51
|
+
perPage: z.number().default(30),
|
|
52
|
+
page: z.number().default(1),
|
|
53
|
+
})
|
|
54
|
+
.strict(),
|
|
55
|
+
})
|
|
56
|
+
args: GitHubListWorkflowRunsArgs;
|
|
57
|
+
|
|
58
|
+
async execute(args: GitHubListWorkflowRunsArgs, ctx: RunContext): Promise<ToolResult> {
|
|
59
|
+
const accessToken = await this.tokenStore.getValidAccessToken(ctx.userId, 'github');
|
|
60
|
+
|
|
61
|
+
if (!accessToken) {
|
|
62
|
+
return {
|
|
63
|
+
data: {
|
|
64
|
+
error: 'unauthorized',
|
|
65
|
+
message: 'No valid GitHub token found. Please authenticate first.',
|
|
66
|
+
},
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const params = new URLSearchParams({
|
|
71
|
+
per_page: String(args.perPage ?? 30),
|
|
72
|
+
page: String(args.page ?? 1),
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
if (args.branch) params.set('branch', args.branch);
|
|
76
|
+
if (args.status) params.set('status', args.status);
|
|
77
|
+
|
|
78
|
+
const url = `https://api.github.com/repos/${encodeURIComponent(args.owner)}/${encodeURIComponent(args.repo)}/actions/runs?${params.toString()}`;
|
|
79
|
+
const response = await fetch(url, {
|
|
80
|
+
headers: {
|
|
81
|
+
Authorization: `Bearer ${accessToken}`,
|
|
82
|
+
Accept: 'application/vnd.github+json',
|
|
83
|
+
'X-GitHub-Api-Version': '2022-11-28',
|
|
84
|
+
},
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
if (response.status === 401 || response.status === 403) {
|
|
88
|
+
this.logger.warn(`GitHub API returned ${response.status} for user ${ctx.userId}`);
|
|
89
|
+
return {
|
|
90
|
+
data: {
|
|
91
|
+
error: '401',
|
|
92
|
+
message: 'GitHub token was rejected. Please re-authenticate.',
|
|
93
|
+
},
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
if (!response.ok) {
|
|
98
|
+
const body = await response.text();
|
|
99
|
+
this.logger.error(`GitHub API error: ${response.status} ${body}`);
|
|
100
|
+
return {
|
|
101
|
+
data: {
|
|
102
|
+
error: 'api_error',
|
|
103
|
+
message: `GitHub API error: ${response.statusText}`,
|
|
104
|
+
},
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const data = (await response.json()) as {
|
|
109
|
+
total_count: number;
|
|
110
|
+
workflow_runs: Array<{
|
|
111
|
+
id: number;
|
|
112
|
+
name: string;
|
|
113
|
+
status: string;
|
|
114
|
+
conclusion: string | null;
|
|
115
|
+
head_branch: string;
|
|
116
|
+
head_sha: string;
|
|
117
|
+
event: string;
|
|
118
|
+
created_at: string;
|
|
119
|
+
updated_at: string;
|
|
120
|
+
html_url: string;
|
|
121
|
+
}>;
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
const runs = data.workflow_runs.map((run) => ({
|
|
125
|
+
id: run.id,
|
|
126
|
+
name: run.name,
|
|
127
|
+
status: run.status,
|
|
128
|
+
conclusion: run.conclusion,
|
|
129
|
+
headBranch: run.head_branch,
|
|
130
|
+
headSha: run.head_sha,
|
|
131
|
+
event: run.event,
|
|
132
|
+
createdAt: run.created_at,
|
|
133
|
+
updatedAt: run.updated_at,
|
|
134
|
+
htmlUrl: run.html_url,
|
|
135
|
+
}));
|
|
136
|
+
|
|
137
|
+
return {
|
|
138
|
+
data: { totalCount: data.total_count, runs },
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { Inject, Logger } from '@nestjs/common';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import { Input, RunContext, Tool, ToolInterface, ToolResult } from '@loopstack/common';
|
|
4
|
+
import { OAuthTokenStore } from '@loopstack/oauth-module';
|
|
5
|
+
|
|
6
|
+
export type GitHubTriggerWorkflowArgs = {
|
|
7
|
+
owner: string;
|
|
8
|
+
repo: string;
|
|
9
|
+
workflowId: string;
|
|
10
|
+
ref: string;
|
|
11
|
+
inputs?: Record<string, string>;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
@Tool({
|
|
15
|
+
config: {
|
|
16
|
+
description:
|
|
17
|
+
'Triggers a GitHub Actions workflow dispatch event. Returns 204 No Content on success. Returns { error: "unauthorized" } if no valid token is available.',
|
|
18
|
+
},
|
|
19
|
+
})
|
|
20
|
+
export class GitHubTriggerWorkflowTool implements ToolInterface {
|
|
21
|
+
private readonly logger = new Logger(GitHubTriggerWorkflowTool.name);
|
|
22
|
+
|
|
23
|
+
@Inject()
|
|
24
|
+
private tokenStore: OAuthTokenStore;
|
|
25
|
+
|
|
26
|
+
@Input({
|
|
27
|
+
schema: z
|
|
28
|
+
.object({
|
|
29
|
+
owner: z.string(),
|
|
30
|
+
repo: z.string(),
|
|
31
|
+
workflowId: z.string(),
|
|
32
|
+
ref: z.string(),
|
|
33
|
+
inputs: z.record(z.string(), z.string()).optional(),
|
|
34
|
+
})
|
|
35
|
+
.strict(),
|
|
36
|
+
})
|
|
37
|
+
args: GitHubTriggerWorkflowArgs;
|
|
38
|
+
|
|
39
|
+
async execute(args: GitHubTriggerWorkflowArgs, ctx: RunContext): Promise<ToolResult> {
|
|
40
|
+
const accessToken = await this.tokenStore.getValidAccessToken(ctx.userId, 'github');
|
|
41
|
+
|
|
42
|
+
if (!accessToken) {
|
|
43
|
+
return {
|
|
44
|
+
data: {
|
|
45
|
+
error: 'unauthorized',
|
|
46
|
+
message: 'No valid GitHub token found. Please authenticate first.',
|
|
47
|
+
},
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const requestBody: Record<string, unknown> = {
|
|
52
|
+
ref: args.ref,
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
if (args.inputs) requestBody.inputs = args.inputs;
|
|
56
|
+
|
|
57
|
+
const url = `https://api.github.com/repos/${encodeURIComponent(args.owner)}/${encodeURIComponent(args.repo)}/actions/workflows/${encodeURIComponent(args.workflowId)}/dispatches`;
|
|
58
|
+
const response = await fetch(url, {
|
|
59
|
+
method: 'POST',
|
|
60
|
+
headers: {
|
|
61
|
+
Authorization: `Bearer ${accessToken}`,
|
|
62
|
+
Accept: 'application/vnd.github+json',
|
|
63
|
+
'X-GitHub-Api-Version': '2022-11-28',
|
|
64
|
+
'Content-Type': 'application/json',
|
|
65
|
+
},
|
|
66
|
+
body: JSON.stringify(requestBody),
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
if (response.status === 401 || response.status === 403) {
|
|
70
|
+
this.logger.warn(`GitHub API returned ${response.status} for user ${ctx.userId}`);
|
|
71
|
+
return {
|
|
72
|
+
data: {
|
|
73
|
+
error: '401',
|
|
74
|
+
message: 'GitHub token was rejected. Please re-authenticate.',
|
|
75
|
+
},
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (!response.ok && response.status !== 204) {
|
|
80
|
+
const body = await response.text();
|
|
81
|
+
this.logger.error(`GitHub API error: ${response.status} ${body}`);
|
|
82
|
+
return {
|
|
83
|
+
data: {
|
|
84
|
+
error: 'api_error',
|
|
85
|
+
message: `GitHub API error: ${response.statusText}`,
|
|
86
|
+
},
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return {
|
|
91
|
+
data: {
|
|
92
|
+
triggered: true,
|
|
93
|
+
message: 'Workflow dispatch event triggered successfully.',
|
|
94
|
+
},
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
}
|