@chat-adapter/github 0.0.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/LICENSE +9 -0
- package/README.md +255 -0
- package/dist/index.d.ts +377 -0
- package/dist/index.js +885 -0
- package/dist/index.js.map +1 -0
- package/package.json +60 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Vercel, Inc.
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
6
|
+
|
|
7
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
8
|
+
|
|
9
|
+
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
# @chat-adapter/github
|
|
2
|
+
|
|
3
|
+
GitHub adapter for the [chat](https://github.com/vercel-labs/chat) SDK. Enables bots to respond to @mentions in GitHub PR comment threads.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install chat @chat-adapter/github
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
```typescript
|
|
14
|
+
import { Chat } from "chat";
|
|
15
|
+
import { createGitHubAdapter } from "@chat-adapter/github";
|
|
16
|
+
import { MemoryState } from "@chat-adapter/state-memory";
|
|
17
|
+
|
|
18
|
+
const chat = new Chat({
|
|
19
|
+
userName: "my-bot",
|
|
20
|
+
adapters: {
|
|
21
|
+
github: createGitHubAdapter({
|
|
22
|
+
token: process.env.GITHUB_TOKEN!,
|
|
23
|
+
webhookSecret: process.env.GITHUB_WEBHOOK_SECRET!,
|
|
24
|
+
userName: "my-bot",
|
|
25
|
+
logger: console,
|
|
26
|
+
}),
|
|
27
|
+
},
|
|
28
|
+
state: new MemoryState(),
|
|
29
|
+
logger: "info",
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
// Handle @mentions in PR comments
|
|
33
|
+
chat.onNewMention(async (thread, message) => {
|
|
34
|
+
await thread.post("Hello from GitHub!");
|
|
35
|
+
});
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Configuration
|
|
39
|
+
|
|
40
|
+
| Option | Required | Description |
|
|
41
|
+
| ---------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
42
|
+
| `token` | Yes\* | [Personal Access Token](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens) with `repo` scope |
|
|
43
|
+
| `appId` | Yes\* | [GitHub App](https://docs.github.com/en/apps/creating-github-apps) ID |
|
|
44
|
+
| `privateKey` | For Apps | GitHub App private key (PEM format) |
|
|
45
|
+
| `installationId` | No | Installation ID (omit for multi-tenant) |
|
|
46
|
+
| `webhookSecret` | Yes | [Webhook secret](https://docs.github.com/en/webhooks/using-webhooks/validating-webhook-deliveries) for signature verification |
|
|
47
|
+
| `userName` | Yes | Bot username for @mention detection |
|
|
48
|
+
| `botUserId` | No | Bot's numeric user ID (auto-detected if not provided) |
|
|
49
|
+
|
|
50
|
+
\*Either `token` or `appId`/`privateKey` is required.
|
|
51
|
+
|
|
52
|
+
## Environment Variables
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
# Personal Access Token auth
|
|
56
|
+
GITHUB_TOKEN=ghp_xxxxxxxxxxxx
|
|
57
|
+
|
|
58
|
+
# OR GitHub App auth
|
|
59
|
+
GITHUB_APP_ID=123456
|
|
60
|
+
GITHUB_PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY-----..."
|
|
61
|
+
GITHUB_INSTALLATION_ID=12345678 # Optional for multi-tenant apps
|
|
62
|
+
|
|
63
|
+
# Webhook secret (required)
|
|
64
|
+
GITHUB_WEBHOOK_SECRET=your-webhook-secret
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## GitHub Setup
|
|
68
|
+
|
|
69
|
+
### Option A: Personal Access Token (PAT)
|
|
70
|
+
|
|
71
|
+
Best for personal projects, testing, or simple single-repo bots.
|
|
72
|
+
|
|
73
|
+
1. Go to [Settings → Developer settings → Personal access tokens](https://github.com/settings/tokens)
|
|
74
|
+
2. Create a new token with `repo` scope
|
|
75
|
+
3. Set `GITHUB_TOKEN` environment variable
|
|
76
|
+
|
|
77
|
+
### Option B: GitHub App (Recommended)
|
|
78
|
+
|
|
79
|
+
Better rate limits, security, and supports multiple installations.
|
|
80
|
+
|
|
81
|
+
#### 1. Create the App
|
|
82
|
+
|
|
83
|
+
1. Go to [Settings → Developer settings → GitHub Apps → New GitHub App](https://github.com/settings/apps/new)
|
|
84
|
+
2. Fill in:
|
|
85
|
+
- **Name**: Your bot's name
|
|
86
|
+
- **Homepage URL**: Your app's website
|
|
87
|
+
- **Webhook URL**: `https://your-domain.com/api/webhooks/github`
|
|
88
|
+
- **Webhook secret**: Generate a secure secret
|
|
89
|
+
3. Set **Permissions**:
|
|
90
|
+
- Repository -> Issues: Read & write
|
|
91
|
+
- Repository -> Pull requests: Read & write
|
|
92
|
+
- Repository -> Metadata: Read-only
|
|
93
|
+
4. Subscribe to **events**:
|
|
94
|
+
- Issue comment
|
|
95
|
+
- Pull request review comment
|
|
96
|
+
5. Under "Where can this GitHub App be installed?":
|
|
97
|
+
- **Only on this account** - For private/testing apps
|
|
98
|
+
- **Any account** - For public apps others can install
|
|
99
|
+
6. Click **"Create GitHub App"**
|
|
100
|
+
7. Note your **App ID** from the app settings page (shown at the top)
|
|
101
|
+
8. Scroll down and click **"Generate a private key"** - save the downloaded `.pem` file
|
|
102
|
+
|
|
103
|
+
#### 2. Install the App
|
|
104
|
+
|
|
105
|
+
1. After creating the app, go to your app's settings page
|
|
106
|
+
2. Click **"Install App"** in the left sidebar
|
|
107
|
+
3. Click **"Install"** next to your organization or account
|
|
108
|
+
4. Choose which repositories to grant access:
|
|
109
|
+
- **All repositories** - App can access all current and future repos
|
|
110
|
+
- **Only select repositories** - Pick specific repos (recommended for testing)
|
|
111
|
+
5. Click **"Install"**
|
|
112
|
+
6. Note the **Installation ID** from the URL after installation:
|
|
113
|
+
```
|
|
114
|
+
https://github.com/settings/installations/12345678
|
|
115
|
+
^^^^^^^^
|
|
116
|
+
This is your Installation ID
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
#### 3. Configure the Adapter
|
|
120
|
+
|
|
121
|
+
**Single-tenant (fixed installation):**
|
|
122
|
+
|
|
123
|
+
```typescript
|
|
124
|
+
createGitHubAdapter({
|
|
125
|
+
appId: process.env.GITHUB_APP_ID!,
|
|
126
|
+
privateKey: process.env.GITHUB_PRIVATE_KEY!,
|
|
127
|
+
installationId: parseInt(process.env.GITHUB_INSTALLATION_ID!),
|
|
128
|
+
webhookSecret: process.env.GITHUB_WEBHOOK_SECRET!,
|
|
129
|
+
userName: "my-bot[bot]",
|
|
130
|
+
logger: console,
|
|
131
|
+
});
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
**Multi-tenant (public app anyone can install):**
|
|
135
|
+
|
|
136
|
+
Simply omit `installationId`. The adapter automatically extracts it from webhooks and caches API clients per-installation:
|
|
137
|
+
|
|
138
|
+
```typescript
|
|
139
|
+
import { Chat } from "chat";
|
|
140
|
+
import { createGitHubAdapter } from "@chat-adapter/github";
|
|
141
|
+
import { RedisState } from "@chat-adapter/state-redis";
|
|
142
|
+
|
|
143
|
+
const chat = new Chat({
|
|
144
|
+
userName: "my-bot[bot]",
|
|
145
|
+
adapters: {
|
|
146
|
+
github: createGitHubAdapter({
|
|
147
|
+
appId: process.env.GITHUB_APP_ID!,
|
|
148
|
+
privateKey: process.env.GITHUB_PRIVATE_KEY!,
|
|
149
|
+
// No installationId - handled automatically!
|
|
150
|
+
webhookSecret: process.env.GITHUB_WEBHOOK_SECRET!,
|
|
151
|
+
userName: "my-bot[bot]",
|
|
152
|
+
logger: console,
|
|
153
|
+
}),
|
|
154
|
+
},
|
|
155
|
+
// Use Redis to persist installation mappings
|
|
156
|
+
state: new RedisState({ url: process.env.REDIS_URL! }),
|
|
157
|
+
logger: "info",
|
|
158
|
+
});
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
### Webhook Setup
|
|
162
|
+
|
|
163
|
+
See the [GitHub Webhooks documentation](https://docs.github.com/en/webhooks/using-webhooks/creating-webhooks) for detailed instructions.
|
|
164
|
+
|
|
165
|
+
**For repository/org webhooks:**
|
|
166
|
+
|
|
167
|
+
1. Go to repository/org **Settings → Webhooks → Add webhook**
|
|
168
|
+
2. Set **Payload URL** to `https://your-domain.com/api/webhooks/github`
|
|
169
|
+
3. Set **Content type** to `application/json` (**required** - the default `application/x-www-form-urlencoded` will not work)
|
|
170
|
+
4. Set **Secret** to match your `webhookSecret`
|
|
171
|
+
5. Select events:
|
|
172
|
+
- [Issue comments](https://docs.github.com/en/webhooks/webhook-events-and-payloads#issue_comment) (PR-level)
|
|
173
|
+
- [Pull request review comments](https://docs.github.com/en/webhooks/webhook-events-and-payloads#pull_request_review_comment) (line-specific)
|
|
174
|
+
|
|
175
|
+
**For GitHub Apps:** Webhooks are configured during app creation. Make sure to select `application/json` as the content type.
|
|
176
|
+
|
|
177
|
+
## Features
|
|
178
|
+
|
|
179
|
+
- Message posting and editing
|
|
180
|
+
- Message deletion
|
|
181
|
+
- [Reaction handling](https://docs.github.com/en/rest/reactions) (add/remove)
|
|
182
|
+
- PR-level comments (Conversation tab)
|
|
183
|
+
- Review comment threads (Files Changed tab - line-specific)
|
|
184
|
+
- Cards (rendered as [GitHub Flavored Markdown](https://github.github.com/gfm/))
|
|
185
|
+
- Multi-tenant support (automatic installation ID handling)
|
|
186
|
+
|
|
187
|
+
## Thread Model
|
|
188
|
+
|
|
189
|
+
GitHub has two types of comment threads:
|
|
190
|
+
|
|
191
|
+
| Type | Tab | API | Thread ID Format |
|
|
192
|
+
| --------------- | ------------- | -------------------------------------------------------------------- | ------------------------------------------------- |
|
|
193
|
+
| PR-level | Conversation | [Issue Comments](https://docs.github.com/en/rest/issues/comments) | `github:{owner}/{repo}:{prNumber}` |
|
|
194
|
+
| Review comments | Files Changed | [PR Review Comments](https://docs.github.com/en/rest/pulls/comments) | `github:{owner}/{repo}:{prNumber}:rc:{commentId}` |
|
|
195
|
+
|
|
196
|
+
Example thread IDs:
|
|
197
|
+
|
|
198
|
+
- `github:acme/app:123` (PR-level)
|
|
199
|
+
- `github:acme/app:123:rc:456789` (line-specific review comment)
|
|
200
|
+
|
|
201
|
+
## Reactions
|
|
202
|
+
|
|
203
|
+
Supports [GitHub's reaction emoji](https://docs.github.com/en/rest/reactions/reactions#about-reactions):
|
|
204
|
+
|
|
205
|
+
| SDK Emoji | GitHub Reaction |
|
|
206
|
+
| ------------- | --------------- |
|
|
207
|
+
| `thumbs_up` | 👍 (+1) |
|
|
208
|
+
| `thumbs_down` | 👎 (-1) |
|
|
209
|
+
| `laugh` | 😄 |
|
|
210
|
+
| `confused` | 😕 |
|
|
211
|
+
| `heart` | ❤️ |
|
|
212
|
+
| `hooray` | 🎉 |
|
|
213
|
+
| `rocket` | 🚀 |
|
|
214
|
+
| `eyes` | 👀 |
|
|
215
|
+
|
|
216
|
+
## Limitations
|
|
217
|
+
|
|
218
|
+
- **No typing indicators** - GitHub doesn't support typing indicators
|
|
219
|
+
- **No streaming** - Messages posted in full (editing supported for updates)
|
|
220
|
+
- **No DMs** - GitHub doesn't have direct messages
|
|
221
|
+
- **No modals** - GitHub doesn't support interactive modals
|
|
222
|
+
- **Action buttons** - Rendered as text; use link buttons for clickable actions
|
|
223
|
+
|
|
224
|
+
## Troubleshooting
|
|
225
|
+
|
|
226
|
+
### "Invalid signature" error
|
|
227
|
+
|
|
228
|
+
- Verify `GITHUB_WEBHOOK_SECRET` matches your webhook configuration
|
|
229
|
+
- Ensure the request body isn't being modified before verification
|
|
230
|
+
|
|
231
|
+
### "Invalid JSON" error
|
|
232
|
+
|
|
233
|
+
- Change webhook **Content type** to `application/json` (GitHub defaults to `application/x-www-form-urlencoded` which doesn't work)
|
|
234
|
+
|
|
235
|
+
### Bot not responding to mentions
|
|
236
|
+
|
|
237
|
+
- Verify webhook events are configured (issue_comment, pull_request_review_comment)
|
|
238
|
+
- Check that the webhook URL is correct and accessible
|
|
239
|
+
- Ensure the bot has been installed on the repository
|
|
240
|
+
- Verify the `userName` config matches your bot's GitHub username
|
|
241
|
+
|
|
242
|
+
### "Installation ID required" error
|
|
243
|
+
|
|
244
|
+
- This occurs when making API calls outside webhook context in multi-tenant mode
|
|
245
|
+
- Ensure you're using a persistent state adapter (Redis) to store installation mappings
|
|
246
|
+
- The first interaction must come from a webhook to establish the mapping
|
|
247
|
+
|
|
248
|
+
### Rate limiting
|
|
249
|
+
|
|
250
|
+
- [PATs have lower rate limits](https://docs.github.com/en/rest/overview/rate-limits-for-the-rest-api) than GitHub Apps
|
|
251
|
+
- Consider switching to a GitHub App for production use
|
|
252
|
+
|
|
253
|
+
## License
|
|
254
|
+
|
|
255
|
+
MIT
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,377 @@
|
|
|
1
|
+
import { Logger, Adapter, ChatInstance, WebhookOptions, AdapterPostableMessage, RawMessage, EmojiValue, FetchOptions, FetchResult, ThreadInfo, Message, FormattedContent } from 'chat';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Type definitions for the GitHub adapter.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Base configuration options shared by all auth methods.
|
|
9
|
+
*/
|
|
10
|
+
interface GitHubAdapterBaseConfig {
|
|
11
|
+
/** Logger instance for error reporting */
|
|
12
|
+
logger: Logger;
|
|
13
|
+
/**
|
|
14
|
+
* Webhook secret for HMAC-SHA256 verification.
|
|
15
|
+
* Set this in your GitHub webhook settings.
|
|
16
|
+
*/
|
|
17
|
+
webhookSecret: string;
|
|
18
|
+
/**
|
|
19
|
+
* Bot username (e.g., "my-bot" or "my-bot[bot]" for GitHub Apps).
|
|
20
|
+
* Used for @-mention detection.
|
|
21
|
+
*/
|
|
22
|
+
userName: string;
|
|
23
|
+
/**
|
|
24
|
+
* Bot's GitHub user ID (numeric).
|
|
25
|
+
* Used for self-message detection. If not provided, will be fetched on first API call.
|
|
26
|
+
*/
|
|
27
|
+
botUserId?: number;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Configuration using a Personal Access Token (PAT).
|
|
31
|
+
* Simpler setup, suitable for personal bots or testing.
|
|
32
|
+
*/
|
|
33
|
+
interface GitHubAdapterPATConfig extends GitHubAdapterBaseConfig {
|
|
34
|
+
/** Personal Access Token with appropriate scopes (repo, write:discussion) */
|
|
35
|
+
token: string;
|
|
36
|
+
appId?: never;
|
|
37
|
+
privateKey?: never;
|
|
38
|
+
installationId?: never;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Configuration using a GitHub App with a fixed installation.
|
|
42
|
+
* Use this when your bot is only installed on a single org/repo.
|
|
43
|
+
*/
|
|
44
|
+
interface GitHubAdapterAppConfig extends GitHubAdapterBaseConfig {
|
|
45
|
+
/** GitHub App ID */
|
|
46
|
+
appId: string;
|
|
47
|
+
/** GitHub App private key (PEM format) */
|
|
48
|
+
privateKey: string;
|
|
49
|
+
/** Installation ID for the app (for single-tenant apps) */
|
|
50
|
+
installationId: number;
|
|
51
|
+
token?: never;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Configuration using a GitHub App for multi-tenant (public) apps.
|
|
55
|
+
* The installation ID is automatically extracted from each webhook payload.
|
|
56
|
+
* Use this when your bot can be installed by anyone.
|
|
57
|
+
*/
|
|
58
|
+
interface GitHubAdapterMultiTenantAppConfig extends GitHubAdapterBaseConfig {
|
|
59
|
+
/** GitHub App ID */
|
|
60
|
+
appId: string;
|
|
61
|
+
/** GitHub App private key (PEM format) */
|
|
62
|
+
privateKey: string;
|
|
63
|
+
/** Omit installationId to enable multi-tenant mode */
|
|
64
|
+
installationId?: never;
|
|
65
|
+
token?: never;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* GitHub adapter configuration - PAT, single-tenant App, or multi-tenant App.
|
|
69
|
+
*/
|
|
70
|
+
type GitHubAdapterConfig = GitHubAdapterPATConfig | GitHubAdapterAppConfig | GitHubAdapterMultiTenantAppConfig;
|
|
71
|
+
/**
|
|
72
|
+
* Decoded thread ID for GitHub.
|
|
73
|
+
*
|
|
74
|
+
* Thread types:
|
|
75
|
+
* - PR-level: Comments in the "Conversation" tab (issue_comment API)
|
|
76
|
+
* - Review comment: Line-specific comments in "Files changed" tab (pull request review comment API)
|
|
77
|
+
*/
|
|
78
|
+
interface GitHubThreadId {
|
|
79
|
+
/** Repository owner (user or organization) */
|
|
80
|
+
owner: string;
|
|
81
|
+
/** Repository name */
|
|
82
|
+
repo: string;
|
|
83
|
+
/** Pull request number */
|
|
84
|
+
prNumber: number;
|
|
85
|
+
/**
|
|
86
|
+
* Root review comment ID for line-specific threads.
|
|
87
|
+
* If present, this is a review comment thread.
|
|
88
|
+
* If absent, this is a PR-level (issue comment) thread.
|
|
89
|
+
*/
|
|
90
|
+
reviewCommentId?: number;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* GitHub user object (simplified).
|
|
94
|
+
*/
|
|
95
|
+
interface GitHubUser {
|
|
96
|
+
id: number;
|
|
97
|
+
login: string;
|
|
98
|
+
avatar_url?: string;
|
|
99
|
+
type: "User" | "Bot" | "Organization";
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* GitHub repository object (simplified).
|
|
103
|
+
*/
|
|
104
|
+
interface GitHubRepository {
|
|
105
|
+
id: number;
|
|
106
|
+
name: string;
|
|
107
|
+
full_name: string;
|
|
108
|
+
owner: GitHubUser;
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* GitHub issue comment (PR-level comment in Conversation tab).
|
|
112
|
+
*/
|
|
113
|
+
interface GitHubIssueComment {
|
|
114
|
+
id: number;
|
|
115
|
+
body: string;
|
|
116
|
+
user: GitHubUser;
|
|
117
|
+
created_at: string;
|
|
118
|
+
updated_at: string;
|
|
119
|
+
html_url: string;
|
|
120
|
+
/** Reactions summary */
|
|
121
|
+
reactions?: {
|
|
122
|
+
url: string;
|
|
123
|
+
total_count: number;
|
|
124
|
+
"+1": number;
|
|
125
|
+
"-1": number;
|
|
126
|
+
laugh: number;
|
|
127
|
+
hooray: number;
|
|
128
|
+
confused: number;
|
|
129
|
+
heart: number;
|
|
130
|
+
rocket: number;
|
|
131
|
+
eyes: number;
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* GitHub pull request review comment (line-specific comment in Files Changed tab).
|
|
136
|
+
*/
|
|
137
|
+
interface GitHubReviewComment {
|
|
138
|
+
id: number;
|
|
139
|
+
body: string;
|
|
140
|
+
user: GitHubUser;
|
|
141
|
+
created_at: string;
|
|
142
|
+
updated_at: string;
|
|
143
|
+
html_url: string;
|
|
144
|
+
/** The commit SHA the comment is associated with */
|
|
145
|
+
commit_id: string;
|
|
146
|
+
/** The original commit SHA (for outdated comments) */
|
|
147
|
+
original_commit_id: string;
|
|
148
|
+
/** The diff hunk the comment applies to */
|
|
149
|
+
diff_hunk: string;
|
|
150
|
+
/** Path to the file being commented on */
|
|
151
|
+
path: string;
|
|
152
|
+
/** Line number in the diff */
|
|
153
|
+
line?: number;
|
|
154
|
+
/** Original line number */
|
|
155
|
+
original_line?: number;
|
|
156
|
+
/** Side of the diff (LEFT or RIGHT) */
|
|
157
|
+
side?: "LEFT" | "RIGHT";
|
|
158
|
+
/** Start line for multi-line comments */
|
|
159
|
+
start_line?: number | null;
|
|
160
|
+
/** Start side for multi-line comments */
|
|
161
|
+
start_side?: "LEFT" | "RIGHT" | null;
|
|
162
|
+
/**
|
|
163
|
+
* The ID of the comment this is a reply to.
|
|
164
|
+
* If present, this is a reply in an existing thread.
|
|
165
|
+
* If absent, this is the root of a new thread.
|
|
166
|
+
*/
|
|
167
|
+
in_reply_to_id?: number;
|
|
168
|
+
/** Reactions summary */
|
|
169
|
+
reactions?: GitHubIssueComment["reactions"];
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Platform-specific raw message type for GitHub.
|
|
173
|
+
* Can be either an issue comment or a review comment.
|
|
174
|
+
*/
|
|
175
|
+
type GitHubRawMessage = {
|
|
176
|
+
type: "issue_comment";
|
|
177
|
+
comment: GitHubIssueComment;
|
|
178
|
+
repository: GitHubRepository;
|
|
179
|
+
prNumber: number;
|
|
180
|
+
} | {
|
|
181
|
+
type: "review_comment";
|
|
182
|
+
comment: GitHubReviewComment;
|
|
183
|
+
repository: GitHubRepository;
|
|
184
|
+
prNumber: number;
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* GitHub adapter for chat SDK.
|
|
189
|
+
*
|
|
190
|
+
* Supports both PR-level comments (Conversation tab) and review comment threads
|
|
191
|
+
* (Files Changed tab - line-specific).
|
|
192
|
+
*
|
|
193
|
+
* @example Single-tenant (your own org)
|
|
194
|
+
* ```typescript
|
|
195
|
+
* import { Chat } from "chat";
|
|
196
|
+
* import { GitHubAdapter } from "@chat-adapter/github";
|
|
197
|
+
* import { MemoryState } from "@chat-adapter/state-memory";
|
|
198
|
+
*
|
|
199
|
+
* const chat = new Chat({
|
|
200
|
+
* userName: "my-bot",
|
|
201
|
+
* adapters: {
|
|
202
|
+
* github: new GitHubAdapter({
|
|
203
|
+
* token: process.env.GITHUB_TOKEN!,
|
|
204
|
+
* webhookSecret: process.env.GITHUB_WEBHOOK_SECRET!,
|
|
205
|
+
* userName: "my-bot",
|
|
206
|
+
* logger: console,
|
|
207
|
+
* }),
|
|
208
|
+
* },
|
|
209
|
+
* state: new MemoryState(),
|
|
210
|
+
* logger: "info",
|
|
211
|
+
* });
|
|
212
|
+
* ```
|
|
213
|
+
*
|
|
214
|
+
* @example Multi-tenant (public app anyone can install)
|
|
215
|
+
* ```typescript
|
|
216
|
+
* const chat = new Chat({
|
|
217
|
+
* userName: "my-bot[bot]",
|
|
218
|
+
* adapters: {
|
|
219
|
+
* github: new GitHubAdapter({
|
|
220
|
+
* appId: process.env.GITHUB_APP_ID!,
|
|
221
|
+
* privateKey: process.env.GITHUB_PRIVATE_KEY!,
|
|
222
|
+
* // No installationId - automatically extracted from webhooks
|
|
223
|
+
* webhookSecret: process.env.GITHUB_WEBHOOK_SECRET!,
|
|
224
|
+
* userName: "my-bot[bot]",
|
|
225
|
+
* logger: console,
|
|
226
|
+
* }),
|
|
227
|
+
* },
|
|
228
|
+
* state: new MemoryState(),
|
|
229
|
+
* logger: "info",
|
|
230
|
+
* });
|
|
231
|
+
* ```
|
|
232
|
+
*/
|
|
233
|
+
declare class GitHubAdapter implements Adapter<GitHubThreadId, GitHubRawMessage> {
|
|
234
|
+
readonly name = "github";
|
|
235
|
+
readonly userName: string;
|
|
236
|
+
private octokit;
|
|
237
|
+
private appCredentials;
|
|
238
|
+
private installationClients;
|
|
239
|
+
private webhookSecret;
|
|
240
|
+
private chat;
|
|
241
|
+
private logger;
|
|
242
|
+
private _botUserId;
|
|
243
|
+
private formatConverter;
|
|
244
|
+
/** Bot user ID (numeric) used for self-message detection */
|
|
245
|
+
get botUserId(): string | undefined;
|
|
246
|
+
/** Whether this adapter is in multi-tenant mode (no fixed installation ID) */
|
|
247
|
+
get isMultiTenant(): boolean;
|
|
248
|
+
constructor(config: GitHubAdapterConfig);
|
|
249
|
+
/**
|
|
250
|
+
* Get or create an Octokit instance for a specific installation.
|
|
251
|
+
* For single-tenant mode, returns the single instance.
|
|
252
|
+
* For multi-tenant mode, creates/caches instances per installation.
|
|
253
|
+
*/
|
|
254
|
+
private getOctokit;
|
|
255
|
+
initialize(chat: ChatInstance): Promise<void>;
|
|
256
|
+
/**
|
|
257
|
+
* Get the state key for storing installation ID for a repository.
|
|
258
|
+
*/
|
|
259
|
+
private getInstallationKey;
|
|
260
|
+
/**
|
|
261
|
+
* Store the installation ID for a repository (for multi-tenant mode).
|
|
262
|
+
*/
|
|
263
|
+
private storeInstallationId;
|
|
264
|
+
/**
|
|
265
|
+
* Get the installation ID for a repository (for multi-tenant mode).
|
|
266
|
+
*/
|
|
267
|
+
private getInstallationId;
|
|
268
|
+
/**
|
|
269
|
+
* Handle incoming webhook from GitHub.
|
|
270
|
+
*/
|
|
271
|
+
handleWebhook(request: Request, options?: WebhookOptions): Promise<Response>;
|
|
272
|
+
/**
|
|
273
|
+
* Verify GitHub webhook signature using HMAC-SHA256.
|
|
274
|
+
*/
|
|
275
|
+
private verifySignature;
|
|
276
|
+
/**
|
|
277
|
+
* Handle issue_comment webhook (PR-level comments in Conversation tab).
|
|
278
|
+
*/
|
|
279
|
+
private handleIssueComment;
|
|
280
|
+
/**
|
|
281
|
+
* Handle pull_request_review_comment webhook (line-specific comments).
|
|
282
|
+
*/
|
|
283
|
+
private handleReviewComment;
|
|
284
|
+
/**
|
|
285
|
+
* Parse an issue comment into a normalized Message.
|
|
286
|
+
*/
|
|
287
|
+
private parseIssueComment;
|
|
288
|
+
/**
|
|
289
|
+
* Parse a review comment into a normalized Message.
|
|
290
|
+
*/
|
|
291
|
+
private parseReviewComment;
|
|
292
|
+
/**
|
|
293
|
+
* Parse a GitHub user into an Author.
|
|
294
|
+
*/
|
|
295
|
+
private parseAuthor;
|
|
296
|
+
/**
|
|
297
|
+
* Get the Octokit client for a specific thread.
|
|
298
|
+
* In multi-tenant mode, looks up the installation ID from state.
|
|
299
|
+
*/
|
|
300
|
+
private getOctokitForThread;
|
|
301
|
+
/**
|
|
302
|
+
* Post a message to a thread.
|
|
303
|
+
*/
|
|
304
|
+
postMessage(threadId: string, message: AdapterPostableMessage): Promise<RawMessage<GitHubRawMessage>>;
|
|
305
|
+
/**
|
|
306
|
+
* Edit an existing message.
|
|
307
|
+
*/
|
|
308
|
+
editMessage(threadId: string, messageId: string, message: AdapterPostableMessage): Promise<RawMessage<GitHubRawMessage>>;
|
|
309
|
+
/**
|
|
310
|
+
* Delete a message.
|
|
311
|
+
*/
|
|
312
|
+
deleteMessage(threadId: string, messageId: string): Promise<void>;
|
|
313
|
+
/**
|
|
314
|
+
* Add a reaction to a message.
|
|
315
|
+
*/
|
|
316
|
+
addReaction(threadId: string, messageId: string, emoji: EmojiValue | string): Promise<void>;
|
|
317
|
+
/**
|
|
318
|
+
* Remove a reaction from a message.
|
|
319
|
+
*/
|
|
320
|
+
removeReaction(threadId: string, messageId: string, emoji: EmojiValue | string): Promise<void>;
|
|
321
|
+
/**
|
|
322
|
+
* Convert SDK emoji to GitHub reaction content.
|
|
323
|
+
*/
|
|
324
|
+
private emojiToGitHubReaction;
|
|
325
|
+
/**
|
|
326
|
+
* Show typing indicator (no-op for GitHub).
|
|
327
|
+
*/
|
|
328
|
+
startTyping(_threadId: string): Promise<void>;
|
|
329
|
+
/**
|
|
330
|
+
* Fetch messages from a thread.
|
|
331
|
+
*/
|
|
332
|
+
fetchMessages(threadId: string, options?: FetchOptions): Promise<FetchResult<GitHubRawMessage>>;
|
|
333
|
+
/**
|
|
334
|
+
* Fetch thread metadata.
|
|
335
|
+
*/
|
|
336
|
+
fetchThread(threadId: string): Promise<ThreadInfo>;
|
|
337
|
+
/**
|
|
338
|
+
* Encode platform data into a thread ID string.
|
|
339
|
+
*
|
|
340
|
+
* Thread ID formats:
|
|
341
|
+
* - PR-level: `github:{owner}/{repo}:{prNumber}`
|
|
342
|
+
* - Review comment: `github:{owner}/{repo}:{prNumber}:rc:{reviewCommentId}`
|
|
343
|
+
*/
|
|
344
|
+
encodeThreadId(platformData: GitHubThreadId): string;
|
|
345
|
+
/**
|
|
346
|
+
* Decode thread ID string back to platform data.
|
|
347
|
+
*/
|
|
348
|
+
decodeThreadId(threadId: string): GitHubThreadId;
|
|
349
|
+
/**
|
|
350
|
+
* Parse a raw message into normalized format.
|
|
351
|
+
*/
|
|
352
|
+
parseMessage(raw: GitHubRawMessage): Message<GitHubRawMessage>;
|
|
353
|
+
/**
|
|
354
|
+
* Render formatted content to GitHub markdown.
|
|
355
|
+
*/
|
|
356
|
+
renderFormatted(content: FormattedContent): string;
|
|
357
|
+
}
|
|
358
|
+
/**
|
|
359
|
+
* Create a new GitHub adapter instance.
|
|
360
|
+
*
|
|
361
|
+
* @example
|
|
362
|
+
* ```typescript
|
|
363
|
+
* const chat = new Chat({
|
|
364
|
+
* adapters: {
|
|
365
|
+
* github: createGitHubAdapter({
|
|
366
|
+
* token: process.env.GITHUB_TOKEN!,
|
|
367
|
+
* webhookSecret: process.env.GITHUB_WEBHOOK_SECRET!,
|
|
368
|
+
* userName: "my-bot",
|
|
369
|
+
* logger: console,
|
|
370
|
+
* }),
|
|
371
|
+
* },
|
|
372
|
+
* });
|
|
373
|
+
* ```
|
|
374
|
+
*/
|
|
375
|
+
declare function createGitHubAdapter(config: GitHubAdapterConfig): GitHubAdapter;
|
|
376
|
+
|
|
377
|
+
export { GitHubAdapter, type GitHubAdapterAppConfig, type GitHubAdapterConfig, type GitHubAdapterMultiTenantAppConfig, type GitHubAdapterPATConfig, type GitHubRawMessage, type GitHubThreadId, createGitHubAdapter };
|