@loopstack/github-oauth-example 0.3.0 → 0.4.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 +3 -4
- package/dist/tools/authenticate-github-task.tool.d.ts +13 -5
- package/dist/tools/authenticate-github-task.tool.d.ts.map +1 -1
- package/dist/tools/authenticate-github-task.tool.js +17 -17
- package/dist/tools/authenticate-github-task.tool.js.map +1 -1
- package/dist/workflows/github-agent.ui.yaml +6 -16
- package/dist/workflows/github-agent.workflow.d.ts +46 -40
- package/dist/workflows/github-agent.workflow.d.ts.map +1 -1
- package/dist/workflows/github-agent.workflow.js +161 -206
- package/dist/workflows/github-agent.workflow.js.map +1 -1
- package/dist/workflows/github-repos-overview.workflow.d.ts +32 -28
- package/dist/workflows/github-repos-overview.workflow.d.ts.map +1 -1
- package/dist/workflows/github-repos-overview.workflow.js +111 -127
- package/dist/workflows/github-repos-overview.workflow.js.map +1 -1
- package/package.json +6 -7
- package/src/tools/authenticate-github-task.tool.ts +30 -18
- package/src/workflows/__tests__/github-repos-overview-workflow.spec.ts +18 -44
- package/src/workflows/github-agent.ui.yaml +6 -16
- package/src/workflows/github-agent.workflow.ts +137 -128
- package/src/workflows/github-repos-overview.workflow.ts +106 -150
- package/dist/workflows/github-repos-overview.ui.yaml +0 -17
- package/src/workflows/github-repos-overview.ui.yaml +0 -17
|
@@ -1,18 +1,16 @@
|
|
|
1
|
+
import { Inject } from '@nestjs/common';
|
|
1
2
|
import { z } from 'zod';
|
|
2
3
|
import {
|
|
3
4
|
BaseWorkflow,
|
|
4
5
|
CallbackSchema,
|
|
5
|
-
Final,
|
|
6
6
|
Guard,
|
|
7
|
-
Initial,
|
|
8
|
-
InjectTool,
|
|
9
|
-
InjectWorkflow,
|
|
10
7
|
LinkDocument,
|
|
11
8
|
MarkdownDocument,
|
|
12
|
-
|
|
9
|
+
TEMPLATE_RENDERER,
|
|
13
10
|
Transition,
|
|
14
11
|
Workflow,
|
|
15
12
|
} from '@loopstack/common';
|
|
13
|
+
import type { LoopstackContext, TemplateRenderFn } from '@loopstack/common';
|
|
16
14
|
import {
|
|
17
15
|
GitHubGetAuthenticatedUserTool,
|
|
18
16
|
GitHubGetRepoTool,
|
|
@@ -26,17 +24,13 @@ import {
|
|
|
26
24
|
} from '@loopstack/github-module';
|
|
27
25
|
import { OAuthWorkflow } from '@loopstack/oauth-module';
|
|
28
26
|
|
|
29
|
-
interface
|
|
30
|
-
|
|
27
|
+
interface GitHubReposOverviewState {
|
|
28
|
+
owner: string;
|
|
29
|
+
repo: string;
|
|
30
|
+
requiresAuthentication?: boolean;
|
|
31
31
|
user?: { login: string; name: string | null; htmlUrl: string; publicRepos: number };
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
interface GitHubOrgsResult {
|
|
35
|
-
orgs: Array<{ login: string; description: string | null }>;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
interface GitHubRepoResult {
|
|
39
|
-
repo: {
|
|
32
|
+
orgs?: Array<{ login: string; description: string | null }>;
|
|
33
|
+
repoDetails?: {
|
|
40
34
|
fullName: string;
|
|
41
35
|
description: string | null;
|
|
42
36
|
language: string | null;
|
|
@@ -46,18 +40,9 @@ interface GitHubRepoResult {
|
|
|
46
40
|
defaultBranch: string;
|
|
47
41
|
htmlUrl: string;
|
|
48
42
|
};
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
branches: Array<{ name: string; protected: boolean }>;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
interface GitHubIssuesResult {
|
|
56
|
-
issues: Array<{ number: number; title: string; state: string; user: string; htmlUrl: string }>;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
interface GitHubPullRequestsResult {
|
|
60
|
-
pullRequests: Array<{
|
|
43
|
+
branches?: Array<{ name: string; protected: boolean }>;
|
|
44
|
+
issues?: Array<{ number: number; title: string; state: string; user: string; htmlUrl: string }>;
|
|
45
|
+
pullRequests?: Array<{
|
|
61
46
|
number: number;
|
|
62
47
|
title: string;
|
|
63
48
|
state: string;
|
|
@@ -65,30 +50,22 @@ interface GitHubPullRequestsResult {
|
|
|
65
50
|
draft: boolean;
|
|
66
51
|
htmlUrl: string;
|
|
67
52
|
}>;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
interface GitHubDirectoryResult {
|
|
71
|
-
entries: Array<{ name: string; type: string; path: string }>;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
interface GitHubWorkflowRunsResult {
|
|
75
|
-
totalCount: number;
|
|
76
|
-
runs: Array<{
|
|
53
|
+
directoryEntries?: Array<{ name: string; type: string; path: string }>;
|
|
54
|
+
workflowRuns?: Array<{
|
|
77
55
|
id: number;
|
|
78
56
|
name: string;
|
|
79
57
|
status: string;
|
|
80
58
|
conclusion: string | null;
|
|
81
59
|
htmlUrl: string;
|
|
82
60
|
}>;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
interface GitHubSearchCodeResult {
|
|
86
|
-
totalCount: number;
|
|
87
|
-
results: Array<{ name: string; path: string; repository: string }>;
|
|
61
|
+
searchResults?: Array<{ name: string; path: string; repository: string }>;
|
|
88
62
|
}
|
|
89
63
|
|
|
90
64
|
@Workflow({
|
|
91
|
-
|
|
65
|
+
title: 'GitHub Repository Overview',
|
|
66
|
+
description:
|
|
67
|
+
'Comprehensive GitHub example that exercises every GitHub tool.\nFetches user info, repository details, issues, pull requests, branches,\ndirectory contents, workflow runs, and search results for a given repository.\nIf not authenticated, launches the OAuth sub-workflow and retries.',
|
|
68
|
+
name: 'github_repos_overview',
|
|
92
69
|
schema: z
|
|
93
70
|
.object({
|
|
94
71
|
owner: z.string().default('octocat'),
|
|
@@ -96,75 +73,51 @@ interface GitHubSearchCodeResult {
|
|
|
96
73
|
})
|
|
97
74
|
.strict(),
|
|
98
75
|
})
|
|
99
|
-
export class GitHubReposOverviewWorkflow extends BaseWorkflow<
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
76
|
+
export class GitHubReposOverviewWorkflow extends BaseWorkflow<
|
|
77
|
+
{ owner: string; repo: string },
|
|
78
|
+
GitHubReposOverviewState
|
|
79
|
+
> {
|
|
80
|
+
constructor(
|
|
81
|
+
private readonly gitHubGetAuthenticatedUser: GitHubGetAuthenticatedUserTool,
|
|
82
|
+
private readonly gitHubListUserOrgs: GitHubListUserOrgsTool,
|
|
83
|
+
private readonly gitHubGetRepo: GitHubGetRepoTool,
|
|
84
|
+
private readonly gitHubListBranches: GitHubListBranchesTool,
|
|
85
|
+
private readonly gitHubListIssues: GitHubListIssuesTool,
|
|
86
|
+
private readonly gitHubListPullRequests: GitHubListPullRequestsTool,
|
|
87
|
+
private readonly gitHubListDirectory: GitHubListDirectoryTool,
|
|
88
|
+
private readonly gitHubListWorkflowRuns: GitHubListWorkflowRunsTool,
|
|
89
|
+
private readonly gitHubSearchCode: GitHubSearchCodeTool,
|
|
90
|
+
private readonly oAuthWorkflow: OAuthWorkflow,
|
|
91
|
+
@Inject(TEMPLATE_RENDERER) private readonly render: TemplateRenderFn,
|
|
92
|
+
) {
|
|
93
|
+
super();
|
|
94
|
+
}
|
|
112
95
|
|
|
113
|
-
owner!: string;
|
|
114
|
-
repo!: string;
|
|
115
|
-
requiresAuthentication?: boolean;
|
|
116
|
-
user?: { login: string; name: string | null; htmlUrl: string; publicRepos: number };
|
|
117
|
-
orgs?: Array<{ login: string; description: string | null }>;
|
|
118
|
-
repoDetails?: {
|
|
119
|
-
fullName: string;
|
|
120
|
-
description: string | null;
|
|
121
|
-
language: string | null;
|
|
122
|
-
stars: number;
|
|
123
|
-
forks: number;
|
|
124
|
-
openIssues: number;
|
|
125
|
-
defaultBranch: string;
|
|
126
|
-
htmlUrl: string;
|
|
127
|
-
};
|
|
128
|
-
branches?: Array<{ name: string; protected: boolean }>;
|
|
129
|
-
issues?: Array<{ number: number; title: string; state: string; user: string; htmlUrl: string }>;
|
|
130
|
-
pullRequests?: Array<{
|
|
131
|
-
number: number;
|
|
132
|
-
title: string;
|
|
133
|
-
state: string;
|
|
134
|
-
user: string;
|
|
135
|
-
draft: boolean;
|
|
136
|
-
htmlUrl: string;
|
|
137
|
-
}>;
|
|
138
|
-
directoryEntries?: Array<{ name: string; type: string; path: string }>;
|
|
139
|
-
workflowRuns?: Array<{
|
|
140
|
-
id: number;
|
|
141
|
-
name: string;
|
|
142
|
-
status: string;
|
|
143
|
-
conclusion: string | null;
|
|
144
|
-
htmlUrl: string;
|
|
145
|
-
}>;
|
|
146
|
-
searchResults?: Array<{ name: string; path: string; repository: string }>;
|
|
147
96
|
// --- Step 1: Fetch authenticated user ---
|
|
148
97
|
|
|
149
|
-
@
|
|
150
|
-
async fetchUser(
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
98
|
+
@Transition({ to: 'user_fetched' })
|
|
99
|
+
async fetchUser(state: GitHubReposOverviewState, ctx: LoopstackContext): Promise<GitHubReposOverviewState> {
|
|
100
|
+
const args = ctx.args as { owner: string; repo: string };
|
|
101
|
+
const result = await this.gitHubGetAuthenticatedUser.call();
|
|
102
|
+
return {
|
|
103
|
+
...state,
|
|
104
|
+
owner: args.owner,
|
|
105
|
+
repo: args.repo,
|
|
106
|
+
requiresAuthentication: result.data!.error === 'unauthorized',
|
|
107
|
+
user: result.data!.user,
|
|
108
|
+
};
|
|
156
109
|
}
|
|
157
110
|
|
|
158
111
|
// If unauthorized -> launch OAuth
|
|
159
112
|
@Transition({ from: 'user_fetched', to: 'awaiting_auth', priority: 10 })
|
|
160
113
|
@Guard('needsAuth')
|
|
161
|
-
async authRequired() {
|
|
162
|
-
const result = await this.
|
|
114
|
+
async authRequired(state: GitHubReposOverviewState): Promise<GitHubReposOverviewState> {
|
|
115
|
+
const result = await this.oAuthWorkflow.run(
|
|
163
116
|
{ provider: 'github', scopes: ['repo', 'read:org', 'workflow'] },
|
|
164
|
-
{
|
|
117
|
+
{ callback: { transition: 'authCompleted' } },
|
|
165
118
|
);
|
|
166
119
|
|
|
167
|
-
await this.
|
|
120
|
+
await this.documentStore.save(
|
|
168
121
|
LinkDocument,
|
|
169
122
|
{
|
|
170
123
|
label: 'GitHub authentication required',
|
|
@@ -174,10 +127,11 @@ export class GitHubReposOverviewWorkflow extends BaseWorkflow<{ owner: string; r
|
|
|
174
127
|
},
|
|
175
128
|
{ id: `link_${result.workflowId}` },
|
|
176
129
|
);
|
|
130
|
+
return state;
|
|
177
131
|
}
|
|
178
132
|
|
|
179
|
-
needsAuth(): boolean {
|
|
180
|
-
return !!
|
|
133
|
+
needsAuth(state: GitHubReposOverviewState): boolean {
|
|
134
|
+
return !!state.requiresAuthentication;
|
|
181
135
|
}
|
|
182
136
|
|
|
183
137
|
// Auth completed -> retry from start
|
|
@@ -187,8 +141,11 @@ export class GitHubReposOverviewWorkflow extends BaseWorkflow<{ owner: string; r
|
|
|
187
141
|
wait: true,
|
|
188
142
|
schema: CallbackSchema,
|
|
189
143
|
})
|
|
190
|
-
async authCompleted(
|
|
191
|
-
|
|
144
|
+
async authCompleted(
|
|
145
|
+
state: GitHubReposOverviewState,
|
|
146
|
+
payload: { workflowId: string },
|
|
147
|
+
): Promise<GitHubReposOverviewState> {
|
|
148
|
+
await this.documentStore.save(
|
|
192
149
|
LinkDocument,
|
|
193
150
|
{
|
|
194
151
|
status: 'success',
|
|
@@ -199,99 +156,98 @@ export class GitHubReposOverviewWorkflow extends BaseWorkflow<{ owner: string; r
|
|
|
199
156
|
},
|
|
200
157
|
{ id: `link_${payload.workflowId}` },
|
|
201
158
|
);
|
|
159
|
+
return state;
|
|
202
160
|
}
|
|
203
161
|
|
|
204
162
|
// --- Step 2: Fetch user orgs ---
|
|
205
163
|
|
|
206
164
|
@Transition({ from: 'user_fetched', to: 'orgs_fetched' })
|
|
207
|
-
async fetchOrgs() {
|
|
208
|
-
const result
|
|
209
|
-
|
|
165
|
+
async fetchOrgs(state: GitHubReposOverviewState): Promise<GitHubReposOverviewState> {
|
|
166
|
+
const result = await this.gitHubListUserOrgs.call({ perPage: 10 });
|
|
167
|
+
return { ...state, orgs: result.data!.orgs };
|
|
210
168
|
}
|
|
211
169
|
|
|
212
170
|
// --- Step 3: Fetch repo details and branches ---
|
|
213
171
|
|
|
214
172
|
@Transition({ from: 'orgs_fetched', to: 'repo_fetched' })
|
|
215
|
-
async fetchRepoDetails() {
|
|
216
|
-
const repoResult
|
|
217
|
-
owner:
|
|
218
|
-
repo:
|
|
173
|
+
async fetchRepoDetails(state: GitHubReposOverviewState): Promise<GitHubReposOverviewState> {
|
|
174
|
+
const repoResult = await this.gitHubGetRepo.call({
|
|
175
|
+
owner: state.owner,
|
|
176
|
+
repo: state.repo,
|
|
219
177
|
});
|
|
220
|
-
this.repoDetails = repoResult.data!.repo;
|
|
221
178
|
|
|
222
|
-
const branchesResult
|
|
223
|
-
owner:
|
|
224
|
-
repo:
|
|
179
|
+
const branchesResult = await this.gitHubListBranches.call({
|
|
180
|
+
owner: state.owner,
|
|
181
|
+
repo: state.repo,
|
|
225
182
|
});
|
|
226
|
-
|
|
183
|
+
return { ...state, repoDetails: repoResult.data!.repo, branches: branchesResult.data!.branches };
|
|
227
184
|
}
|
|
228
185
|
|
|
229
186
|
// --- Step 4: Fetch issues and PRs ---
|
|
230
187
|
|
|
231
188
|
@Transition({ from: 'repo_fetched', to: 'issues_prs_fetched' })
|
|
232
|
-
async fetchIssuesPrs() {
|
|
233
|
-
const issuesResult
|
|
234
|
-
owner:
|
|
235
|
-
repo:
|
|
189
|
+
async fetchIssuesPrs(state: GitHubReposOverviewState): Promise<GitHubReposOverviewState> {
|
|
190
|
+
const issuesResult = await this.gitHubListIssues.call({
|
|
191
|
+
owner: state.owner,
|
|
192
|
+
repo: state.repo,
|
|
236
193
|
state: 'open',
|
|
237
194
|
perPage: 10,
|
|
238
195
|
});
|
|
239
|
-
this.issues = issuesResult.data!.issues;
|
|
240
196
|
|
|
241
|
-
const prsResult
|
|
242
|
-
owner:
|
|
243
|
-
repo:
|
|
197
|
+
const prsResult = await this.gitHubListPullRequests.call({
|
|
198
|
+
owner: state.owner,
|
|
199
|
+
repo: state.repo,
|
|
244
200
|
state: 'open',
|
|
245
201
|
perPage: 10,
|
|
246
202
|
});
|
|
247
|
-
|
|
203
|
+
return { ...state, issues: issuesResult.data!.issues, pullRequests: prsResult.data!.pullRequests };
|
|
248
204
|
}
|
|
249
205
|
|
|
250
206
|
// --- Step 5: Fetch directory listing and workflow runs ---
|
|
251
207
|
|
|
252
208
|
@Transition({ from: 'issues_prs_fetched', to: 'content_actions_fetched' })
|
|
253
|
-
async fetchContentActions() {
|
|
254
|
-
const dirResult
|
|
255
|
-
owner:
|
|
256
|
-
repo:
|
|
209
|
+
async fetchContentActions(state: GitHubReposOverviewState): Promise<GitHubReposOverviewState> {
|
|
210
|
+
const dirResult = await this.gitHubListDirectory.call({
|
|
211
|
+
owner: state.owner,
|
|
212
|
+
repo: state.repo,
|
|
257
213
|
});
|
|
258
|
-
this.directoryEntries = dirResult.data!.entries;
|
|
259
214
|
|
|
260
|
-
const runsResult
|
|
261
|
-
owner:
|
|
262
|
-
repo:
|
|
215
|
+
const runsResult = await this.gitHubListWorkflowRuns.call({
|
|
216
|
+
owner: state.owner,
|
|
217
|
+
repo: state.repo,
|
|
263
218
|
perPage: 5,
|
|
264
219
|
});
|
|
265
|
-
|
|
220
|
+
return { ...state, directoryEntries: dirResult.data!.entries, workflowRuns: runsResult.data!.runs };
|
|
266
221
|
}
|
|
267
222
|
|
|
268
223
|
// --- Step 6: Search code in the repo ---
|
|
269
224
|
|
|
270
225
|
@Transition({ from: 'content_actions_fetched', to: 'search_done' })
|
|
271
|
-
async fetchSearch() {
|
|
272
|
-
const result
|
|
273
|
-
query: `repo:${
|
|
226
|
+
async fetchSearch(state: GitHubReposOverviewState): Promise<GitHubReposOverviewState> {
|
|
227
|
+
const result = await this.gitHubSearchCode.call({
|
|
228
|
+
query: `repo:${state.owner}/${state.repo}`,
|
|
274
229
|
perPage: 5,
|
|
275
230
|
});
|
|
276
|
-
|
|
231
|
+
return { ...state, searchResults: result.data!.results };
|
|
277
232
|
}
|
|
278
233
|
|
|
279
234
|
// --- Display all results ---
|
|
280
235
|
|
|
281
|
-
@
|
|
282
|
-
async displayResults() {
|
|
283
|
-
await this.
|
|
236
|
+
@Transition({ from: 'search_done', to: 'end' })
|
|
237
|
+
async displayResults(state: GitHubReposOverviewState): Promise<unknown> {
|
|
238
|
+
await this.documentStore.save(MarkdownDocument, {
|
|
284
239
|
markdown: this.render(__dirname + '/templates/repoOverview.md', {
|
|
285
|
-
user:
|
|
286
|
-
orgs:
|
|
287
|
-
repo:
|
|
288
|
-
branches:
|
|
289
|
-
issues:
|
|
290
|
-
pullRequests:
|
|
291
|
-
directoryEntries:
|
|
292
|
-
workflowRuns:
|
|
293
|
-
searchResults:
|
|
240
|
+
user: state.user,
|
|
241
|
+
orgs: state.orgs,
|
|
242
|
+
repo: state.repoDetails,
|
|
243
|
+
branches: state.branches,
|
|
244
|
+
issues: state.issues,
|
|
245
|
+
pullRequests: state.pullRequests,
|
|
246
|
+
directoryEntries: state.directoryEntries,
|
|
247
|
+
workflowRuns: state.workflowRuns,
|
|
248
|
+
searchResults: state.searchResults,
|
|
294
249
|
}),
|
|
295
250
|
});
|
|
251
|
+
return {};
|
|
296
252
|
}
|
|
297
253
|
}
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
title: 'GitHub Repository Overview'
|
|
2
|
-
|
|
3
|
-
description: |
|
|
4
|
-
Comprehensive GitHub example that exercises every GitHub tool.
|
|
5
|
-
Fetches user info, repository details, issues, pull requests, branches,
|
|
6
|
-
directory contents, workflow runs, and search results for a given repository.
|
|
7
|
-
If not authenticated, launches the OAuth sub-workflow and retries.
|
|
8
|
-
|
|
9
|
-
ui:
|
|
10
|
-
form:
|
|
11
|
-
properties:
|
|
12
|
-
owner:
|
|
13
|
-
title: 'Repository Owner'
|
|
14
|
-
placeholder: 'octocat'
|
|
15
|
-
repo:
|
|
16
|
-
title: 'Repository Name'
|
|
17
|
-
placeholder: 'Hello-World'
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
title: 'GitHub Repository Overview'
|
|
2
|
-
|
|
3
|
-
description: |
|
|
4
|
-
Comprehensive GitHub example that exercises every GitHub tool.
|
|
5
|
-
Fetches user info, repository details, issues, pull requests, branches,
|
|
6
|
-
directory contents, workflow runs, and search results for a given repository.
|
|
7
|
-
If not authenticated, launches the OAuth sub-workflow and retries.
|
|
8
|
-
|
|
9
|
-
ui:
|
|
10
|
-
form:
|
|
11
|
-
properties:
|
|
12
|
-
owner:
|
|
13
|
-
title: 'Repository Owner'
|
|
14
|
-
placeholder: 'octocat'
|
|
15
|
-
repo:
|
|
16
|
-
title: 'Repository Name'
|
|
17
|
-
placeholder: 'Hello-World'
|