@loopstack/github-oauth-example 0.2.0 → 0.2.1
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 +116 -20
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
> An example module for the [Loopstack AI](https://loopstack.ai) automation framework.
|
|
4
4
|
|
|
5
|
-
This module demonstrates how to build workflows that interact with the GitHub API using OAuth authentication. It includes two workflows: a structured overview that fetches repository data, and an interactive chat agent powered by
|
|
5
|
+
This module demonstrates how to build workflows that interact with the GitHub API using OAuth authentication. It includes two workflows: a structured overview that fetches repository data, and an interactive chat agent powered by Claude that can use all 25 GitHub tools.
|
|
6
6
|
|
|
7
7
|
## Workflows
|
|
8
8
|
|
|
@@ -15,16 +15,70 @@ A multi-step workflow that fetches and displays a comprehensive overview of a Gi
|
|
|
15
15
|
**Flow:**
|
|
16
16
|
|
|
17
17
|
```
|
|
18
|
-
start ->
|
|
19
|
-
->
|
|
18
|
+
start -> user_fetched -> orgs_fetched -> repo_fetched -> issues_prs_fetched
|
|
19
|
+
-> content_actions_fetched -> search_done -> end
|
|
20
20
|
```
|
|
21
21
|
|
|
22
22
|
With OAuth branching:
|
|
23
23
|
|
|
24
24
|
```
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
25
|
+
user_fetched -> (unauthorized via @Guard) -> awaiting_auth
|
|
26
|
+
|
|
|
27
|
+
auth_completed -> start (retry)
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
**Key patterns:**
|
|
31
|
+
|
|
32
|
+
The workflow uses `@Guard` to check if authentication is needed and routes to the OAuth flow:
|
|
33
|
+
|
|
34
|
+
```typescript
|
|
35
|
+
@Transition({ from: 'user_fetched', to: 'awaiting_auth', priority: 10 })
|
|
36
|
+
@Guard('needsAuth')
|
|
37
|
+
async authRequired() {
|
|
38
|
+
const result = await this.oAuth.run(
|
|
39
|
+
{ provider: 'github', scopes: ['repo', 'read:org', 'workflow'] },
|
|
40
|
+
{ alias: 'oAuth', callback: { transition: 'authCompleted' } },
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
await this.repository.save(
|
|
44
|
+
LinkDocument,
|
|
45
|
+
{
|
|
46
|
+
label: 'GitHub authentication required',
|
|
47
|
+
workflowId: result.workflowId,
|
|
48
|
+
embed: true,
|
|
49
|
+
expanded: true,
|
|
50
|
+
},
|
|
51
|
+
{ id: `link_${result.workflowId}` },
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
needsAuth(): boolean {
|
|
56
|
+
return !!this.requiresAuthentication;
|
|
57
|
+
}
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
The auth callback uses `wait: true` with `CallbackSchema` to receive the OAuth completion signal:
|
|
61
|
+
|
|
62
|
+
```typescript
|
|
63
|
+
@Transition({
|
|
64
|
+
from: 'awaiting_auth',
|
|
65
|
+
to: 'start',
|
|
66
|
+
wait: true,
|
|
67
|
+
schema: CallbackSchema,
|
|
68
|
+
})
|
|
69
|
+
async authCompleted(payload: { workflowId: string }) {
|
|
70
|
+
await this.repository.save(
|
|
71
|
+
LinkDocument,
|
|
72
|
+
{
|
|
73
|
+
status: 'success',
|
|
74
|
+
label: 'GitHub authentication completed',
|
|
75
|
+
workflowId: payload.workflowId,
|
|
76
|
+
embed: true,
|
|
77
|
+
expanded: false,
|
|
78
|
+
},
|
|
79
|
+
{ id: `link_${payload.workflowId}` },
|
|
80
|
+
);
|
|
81
|
+
}
|
|
28
82
|
```
|
|
29
83
|
|
|
30
84
|
**Tools exercised in the workflow:**
|
|
@@ -61,18 +115,60 @@ All 25 tools are injected and available; 9 are called directly by the workflow t
|
|
|
61
115
|
|
|
62
116
|
### GitHub Agent (`gitHubAgent`)
|
|
63
117
|
|
|
64
|
-
An interactive chat agent that gives
|
|
118
|
+
An interactive chat agent that gives Claude access to all 25 GitHub tools. The agent can manage repositories, issues, pull requests, browse code, check CI/CD status, search across GitHub, and handle OAuth automatically via the `AuthenticateGitHubTask` custom tool.
|
|
65
119
|
|
|
66
120
|
**How it works:**
|
|
67
121
|
|
|
68
|
-
1. Sets up a system
|
|
69
|
-
2. Waits for user input via a
|
|
70
|
-
3. Sends the conversation to
|
|
71
|
-
4.
|
|
72
|
-
5. If a tool returns an auth error,
|
|
122
|
+
1. Sets up a hidden system message describing available GitHub capabilities
|
|
123
|
+
2. Waits for user input via a `wait: true` transition
|
|
124
|
+
3. Sends the conversation to Claude with all 25 GitHub tools plus `authenticateGitHub`
|
|
125
|
+
4. If `stop_reason === 'tool_use'`, delegates tool calls via `DelegateToolCalls` and collects results with `UpdateToolResult`
|
|
126
|
+
5. If a tool returns an auth error, the LLM calls `authenticateGitHub` which launches OAuth
|
|
73
127
|
6. Loops back to wait for the next user message
|
|
74
128
|
|
|
75
|
-
|
|
129
|
+
**Agent loop pattern:**
|
|
130
|
+
|
|
131
|
+
```typescript
|
|
132
|
+
@Transition({ from: 'ready', to: 'prompt_executed' })
|
|
133
|
+
async llmTurn() {
|
|
134
|
+
const result: ToolResult<ClaudeGenerateTextResult> = await this.claudeGenerateText.call({
|
|
135
|
+
system: `You are a helpful GitHub assistant with access to repository, issue, PR, code, actions,
|
|
136
|
+
and search tools. When a tool returns an unauthorized error, use authenticateGitHub
|
|
137
|
+
to let the user sign in, then retry. Be concise and format results using markdown.`,
|
|
138
|
+
claude: { model: 'claude-sonnet-4-6' },
|
|
139
|
+
messagesSearchTag: 'message',
|
|
140
|
+
tools: [
|
|
141
|
+
'gitHubListRepos', 'gitHubGetRepo', 'gitHubCreateRepo', 'gitHubListBranches',
|
|
142
|
+
'gitHubListIssues', 'gitHubGetIssue', 'gitHubCreateIssue', 'gitHubCreateIssueComment',
|
|
143
|
+
'gitHubListPullRequests', 'gitHubGetPullRequest', 'gitHubCreatePullRequest',
|
|
144
|
+
'gitHubMergePullRequest', 'gitHubListPrReviews',
|
|
145
|
+
'gitHubGetFileContent', 'gitHubCreateOrUpdateFile', 'gitHubListDirectory', 'gitHubGetCommit',
|
|
146
|
+
'gitHubListWorkflowRuns', 'gitHubTriggerWorkflow', 'gitHubGetWorkflowRun',
|
|
147
|
+
'gitHubSearchCode', 'gitHubSearchRepos', 'gitHubSearchIssues',
|
|
148
|
+
'gitHubGetAuthenticatedUser', 'gitHubListUserOrgs',
|
|
149
|
+
'authenticateGitHub',
|
|
150
|
+
],
|
|
151
|
+
});
|
|
152
|
+
this.llmResult = result.data;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
@Transition({ from: 'prompt_executed', to: 'awaiting_tools', priority: 10 })
|
|
156
|
+
@Guard('hasToolCalls')
|
|
157
|
+
async executeToolCalls() {
|
|
158
|
+
const result: ToolResult<DelegateToolCallsResult> = await this.delegateToolCalls.call({
|
|
159
|
+
message: this.llmResult!,
|
|
160
|
+
document: ClaudeMessageDocument,
|
|
161
|
+
callback: { transition: 'toolResultReceived' },
|
|
162
|
+
});
|
|
163
|
+
this.delegateResult = result.data;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
hasToolCalls(): boolean {
|
|
167
|
+
return this.llmResult?.stop_reason === 'tool_use';
|
|
168
|
+
}
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
This is the easiest way to interactively test every GitHub tool -- just ask the agent to perform any GitHub operation.
|
|
76
172
|
|
|
77
173
|
## Setup
|
|
78
174
|
|
|
@@ -89,17 +185,17 @@ GITHUB_OAUTH_REDIRECT_URI=http://localhost:5173/oauth/callback
|
|
|
89
185
|
For the GitHub Agent workflow, you also need an LLM API key:
|
|
90
186
|
|
|
91
187
|
```bash
|
|
92
|
-
|
|
188
|
+
ANTHROPIC_API_KEY=your-anthropic-api-key
|
|
93
189
|
```
|
|
94
190
|
|
|
95
191
|
## Dependencies
|
|
96
192
|
|
|
97
|
-
- `@loopstack/
|
|
98
|
-
- `@loopstack/
|
|
99
|
-
- `@loopstack/
|
|
100
|
-
- `@loopstack/
|
|
101
|
-
- `@loopstack/
|
|
102
|
-
- `@loopstack/create-chat-message-tool`
|
|
193
|
+
- `@loopstack/common` - Core framework decorators (`BaseWorkflow`, `@Workflow`, `@Initial`, `@Transition`, `@Final`, `@Guard`, `@InjectTool`, `@InjectWorkflow`, `CallbackSchema`, `ToolResult`)
|
|
194
|
+
- `@loopstack/core` - Provides `LinkDocument` and `MarkdownDocument`
|
|
195
|
+
- `@loopstack/claude-module` - Claude integration (`ClaudeGenerateText`, `ClaudeMessageDocument`, `DelegateToolCalls`, `UpdateToolResult`)
|
|
196
|
+
- `@loopstack/oauth-module` - OAuth infrastructure (`OAuthWorkflow`)
|
|
197
|
+
- `@loopstack/github-module` - All 25 GitHub tools
|
|
198
|
+
- `@loopstack/create-chat-message-tool` - `CreateChatMessage` (used in agent workflow)
|
|
103
199
|
|
|
104
200
|
## About
|
|
105
201
|
|
package/package.json
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
"loopstack",
|
|
9
9
|
"oauth"
|
|
10
10
|
],
|
|
11
|
-
"version": "0.2.
|
|
11
|
+
"version": "0.2.1",
|
|
12
12
|
"license": "Apache-2.0",
|
|
13
13
|
"author": {
|
|
14
14
|
"name": "Jakob Klippel",
|
|
@@ -32,9 +32,9 @@
|
|
|
32
32
|
"@loopstack/claude-module": "^0.22.0",
|
|
33
33
|
"@loopstack/common": "^0.25.0",
|
|
34
34
|
"@loopstack/core": "^0.25.0",
|
|
35
|
-
"@loopstack/create-chat-message-tool": "^0.21.
|
|
36
|
-
"@loopstack/github-module": "^0.2.
|
|
37
|
-
"@loopstack/oauth-module": "^0.2.
|
|
35
|
+
"@loopstack/create-chat-message-tool": "^0.21.1",
|
|
36
|
+
"@loopstack/github-module": "^0.2.1",
|
|
37
|
+
"@loopstack/oauth-module": "^0.2.1",
|
|
38
38
|
"@nestjs/common": "^11.1.14",
|
|
39
39
|
"zod": "^4.3.6"
|
|
40
40
|
},
|