openclaw-channel-github 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.
Files changed (46) hide show
  1. package/README.md +578 -0
  2. package/config.example.json +33 -0
  3. package/dist/index.d.ts +14 -0
  4. package/dist/index.d.ts.map +1 -0
  5. package/dist/index.js +44 -0
  6. package/dist/index.js.map +1 -0
  7. package/dist/src/auth/auth.d.ts +22 -0
  8. package/dist/src/auth/auth.d.ts.map +1 -0
  9. package/dist/src/auth/auth.js +112 -0
  10. package/dist/src/auth/auth.js.map +1 -0
  11. package/dist/src/config/config.d.ts +60 -0
  12. package/dist/src/config/config.d.ts.map +1 -0
  13. package/dist/src/config/config.js +184 -0
  14. package/dist/src/config/config.js.map +1 -0
  15. package/dist/src/events/events.d.ts +234 -0
  16. package/dist/src/events/events.d.ts.map +1 -0
  17. package/dist/src/events/events.js +53 -0
  18. package/dist/src/events/events.js.map +1 -0
  19. package/dist/src/main.d.ts +2 -0
  20. package/dist/src/main.d.ts.map +1 -0
  21. package/dist/src/main.js +115 -0
  22. package/dist/src/main.js.map +1 -0
  23. package/dist/src/normalizer/normalizer.d.ts +94 -0
  24. package/dist/src/normalizer/normalizer.d.ts.map +1 -0
  25. package/dist/src/normalizer/normalizer.js +486 -0
  26. package/dist/src/normalizer/normalizer.js.map +1 -0
  27. package/dist/src/outbound/outbound.d.ts +24 -0
  28. package/dist/src/outbound/outbound.d.ts.map +1 -0
  29. package/dist/src/outbound/outbound.js +125 -0
  30. package/dist/src/outbound/outbound.js.map +1 -0
  31. package/dist/src/routing/routing.d.ts +22 -0
  32. package/dist/src/routing/routing.d.ts.map +1 -0
  33. package/dist/src/routing/routing.js +97 -0
  34. package/dist/src/routing/routing.js.map +1 -0
  35. package/dist/src/server/server.d.ts +20 -0
  36. package/dist/src/server/server.d.ts.map +1 -0
  37. package/dist/src/server/server.js +283 -0
  38. package/dist/src/server/server.js.map +1 -0
  39. package/dist/src/state/state.d.ts +17 -0
  40. package/dist/src/state/state.d.ts.map +1 -0
  41. package/dist/src/state/state.js +49 -0
  42. package/dist/src/state/state.js.map +1 -0
  43. package/docs/api.md +450 -0
  44. package/docs/usage.md +393 -0
  45. package/openclaw.plugin.json +104 -0
  46. package/package.json +81 -0
package/docs/api.md ADDED
@@ -0,0 +1,450 @@
1
+ # OpenClaw GitHub Channel - API Reference
2
+
3
+ ## HTTP Endpoints
4
+
5
+ ### POST /webhook
6
+
7
+ Receives GitHub webhook events. This is the main entry point for all GitHub integrations.
8
+
9
+ **Headers:**
10
+
11
+ | Header | Required | Description |
12
+ |--------|----------|-------------|
13
+ | `X-Hub-Signature-256` | Yes | HMAC-SHA256 signature of the payload |
14
+ | `X-GitHub-Event` | Yes | GitHub event type (e.g., `issue_comment`) |
15
+ | `X-GitHub-Delivery` | Yes | Unique delivery ID for deduplication |
16
+ | `Content-Type` | Yes | Must be `application/json` |
17
+
18
+ **Request Body:** Raw GitHub webhook JSON payload (max 10 MB).
19
+
20
+ **Response:**
21
+
22
+ All responses return `200 OK` with a JSON body (except for errors). This prevents GitHub from retrying webhook deliveries for handled events.
23
+
24
+ | Status | Body | Description |
25
+ |--------|------|-------------|
26
+ | `200` | `{"status":"processed","session_key":"..."}` | Event was processed successfully |
27
+ | `200` | `{"status":"duplicate"}` | Duplicate delivery, already processed |
28
+ | `200` | `{"status":"unsupported_event"}` | Event type not supported |
29
+ | `200` | `{"status":"skipped"}` | Event action not normalizable |
30
+ | `200` | `{"status":"repo_not_allowed"}` | Repository not in allowlist |
31
+ | `200` | `{"status":"bot_ignored"}` | Event from bot sender, ignored |
32
+ | `200` | `{"status":"outbound_marker_ignored"}` | Event contains outbound marker |
33
+ | `200` | `{"status":"no_trigger"}` | No trigger condition matched |
34
+ | `401` | `Invalid signature` | Webhook signature verification failed |
35
+ | `405` | `Method not allowed` | Non-POST request |
36
+ | `500` | Error message | Internal processing error |
37
+
38
+ **Example:**
39
+
40
+ ```bash
41
+ curl -X POST https://your-server.com/webhook \
42
+ -H "Content-Type: application/json" \
43
+ -H "X-GitHub-Event: issue_comment" \
44
+ -H "X-GitHub-Delivery: abc123" \
45
+ -H "X-Hub-Signature-256: sha256=..." \
46
+ -d '{"action":"created","issue":{...},"comment":{...}}'
47
+ ```
48
+
49
+ ---
50
+
51
+ ### GET /health
52
+
53
+ Health check endpoint.
54
+
55
+ **Response:**
56
+
57
+ ```json
58
+ {"status":"ok"}
59
+ ```
60
+
61
+ ---
62
+
63
+ ## Normalized Event Format
64
+
65
+ All GitHub events are normalized into a unified `NormalizedEvent` structure:
66
+
67
+ ```json
68
+ {
69
+ "provider": "github",
70
+ "accountId": "default",
71
+ "repository": "owner/repo",
72
+ "thread": {
73
+ "type": "issue|pull_request|discussion",
74
+ "number": 42,
75
+ "title": "Issue/PR/Discussion title",
76
+ "url": "https://github.com/owner/repo/issues/42"
77
+ },
78
+ "message": {
79
+ "type": "comment|issue_body|pr_body|review|review_comment|discussion_body|context_update|ci_status",
80
+ "id": "comment-999",
81
+ "text": "Message content",
82
+ "createdAt": "2026-03-15T00:00:00Z"
83
+ },
84
+ "sender": {
85
+ "id": "github:12345",
86
+ "login": "alice",
87
+ "association": "MEMBER",
88
+ "isBot": false
89
+ },
90
+ "trigger": {
91
+ "kind": "mention|command|label|auto",
92
+ "command": "/openclaw"
93
+ },
94
+ "context": {
95
+ "labels": ["bug", "ai-review"],
96
+ "assignees": ["alice", "bob"],
97
+ "state": "open",
98
+ "eventAction": "opened",
99
+ "reviewState": "approved",
100
+ "filePath": "src/main.go",
101
+ "line": 42,
102
+ "ciStatus": "completed",
103
+ "ciConclusion": "success",
104
+ "merged": false,
105
+ "headRef": "feature-branch",
106
+ "baseRef": "main",
107
+ "reviewThreadId": 12345,
108
+ "discussionCategory": "General"
109
+ }
110
+ }
111
+ ```
112
+
113
+ ### Thread Types
114
+
115
+ | Type | Description |
116
+ |------|-------------|
117
+ | `issue` | GitHub Issue |
118
+ | `pull_request` | GitHub Pull Request |
119
+ | `discussion` | GitHub Discussion |
120
+
121
+ ### Message Types
122
+
123
+ | Type | Source | Description |
124
+ |------|--------|-------------|
125
+ | `comment` | Issue/PR/Discussion comments | User comment text |
126
+ | `issue_body` | Issue opened | Initial issue description |
127
+ | `pr_body` | PR opened | Initial PR description |
128
+ | `review` | PR review submitted | Review summary text |
129
+ | `review_comment` | Inline PR review comment | Inline code comment |
130
+ | `discussion_body` | Discussion created | Discussion description |
131
+ | `context_update` | Edit/close/label/sync events | State change notification |
132
+ | `ci_status` | check_run/workflow_run | CI/CD status update |
133
+
134
+ ### Trigger Kinds
135
+
136
+ | Kind | Description |
137
+ |------|-------------|
138
+ | `mention` | Bot was @mentioned in the message |
139
+ | `command` | Message starts with a configured slash command |
140
+ | `label` | A configured label was found on the issue/PR |
141
+ | `auto` | Event matched an auto-trigger rule |
142
+
143
+ ### Context Fields
144
+
145
+ | Field | Type | When Present |
146
+ |-------|------|-------------|
147
+ | `labels` | []string | Issue/PR events |
148
+ | `assignees` | []string | Issue/PR events |
149
+ | `state` | string | Issue/PR events |
150
+ | `eventAction` | string | All context events |
151
+ | `reviewState` | string | PR review events |
152
+ | `filePath` | string | Inline review comments |
153
+ | `line` | int | Inline review comments |
154
+ | `ciStatus` | string | CI events |
155
+ | `ciConclusion` | string | CI events |
156
+ | `merged` | bool | PR close events |
157
+ | `headRef` | string | PR events |
158
+ | `baseRef` | string | PR events |
159
+ | `reviewThreadId` | int64 | Inline review comments |
160
+ | `discussionCategory` | string | Discussion events |
161
+
162
+ ---
163
+
164
+ ## Session Key Format
165
+
166
+ Session keys uniquely identify a conversation thread:
167
+
168
+ ```
169
+ github:<owner>/<repo>:<threadType>:<number>
170
+ ```
171
+
172
+ **Examples:**
173
+ - `github:openclaw/openclaw:issue:42`
174
+ - `github:openclaw/openclaw:pull_request:100`
175
+ - `github:openclaw/openclaw:discussion:7`
176
+ - `github:openclaw/openclaw:pull_request:100:review-thread:12345`
177
+
178
+ ---
179
+
180
+ ## Outbound API
181
+
182
+ The channel sends responses back to GitHub using the following API calls:
183
+
184
+ ### Send Comment
185
+
186
+ Creates a comment on an issue or PR.
187
+
188
+ ```
189
+ POST /repos/{owner}/{repo}/issues/{number}/comments
190
+ ```
191
+
192
+ **Headers:**
193
+ ```
194
+ Authorization: Bearer <installation-token>
195
+ Accept: application/vnd.github+json
196
+ X-GitHub-Api-Version: 2022-11-28
197
+ ```
198
+
199
+ **Body:**
200
+ ```json
201
+ {
202
+ "body": "AI response text\n<!-- openclaw-outbound -->"
203
+ }
204
+ ```
205
+
206
+ ### Send PR Review
207
+
208
+ Creates a PR review with an optional approval action.
209
+
210
+ ```
211
+ POST /repos/{owner}/{repo}/pulls/{number}/reviews
212
+ ```
213
+
214
+ **Body:**
215
+ ```json
216
+ {
217
+ "body": "Review summary\n<!-- openclaw-outbound -->",
218
+ "event": "COMMENT|APPROVE|REQUEST_CHANGES",
219
+ "comments": [
220
+ {
221
+ "path": "src/main.go",
222
+ "line": 42,
223
+ "body": "Consider refactoring this function."
224
+ }
225
+ ]
226
+ }
227
+ ```
228
+
229
+ ### Add Reaction
230
+
231
+ Adds a reaction emoji to a comment.
232
+
233
+ ```
234
+ POST /repos/{owner}/{repo}/issues/comments/{comment_id}/reactions
235
+ ```
236
+
237
+ **Body:**
238
+ ```json
239
+ {
240
+ "content": "+1|heart|rocket|eyes"
241
+ }
242
+ ```
243
+
244
+ ---
245
+
246
+ ## MessageHandler Interface
247
+
248
+ The webhook handler accepts a `MessageHandler` callback for custom processing:
249
+
250
+ ```go
251
+ type MessageHandler func(sessionKey string, event *normalizer.NormalizedEvent) (string, error)
252
+ ```
253
+
254
+ **Parameters:**
255
+ - `sessionKey` — Stable session identifier (e.g., `github:owner/repo:issue:42`)
256
+ - `event` — Normalized event with all context metadata
257
+
258
+ **Returns:**
259
+ - `string` — Reply text to send back to GitHub (empty string = no reply)
260
+ - `error` — Processing error (returns 500 to webhook caller)
261
+
262
+ **Example:**
263
+
264
+ ```go
265
+ handler.MessageHandler = func(sessionKey string, event *normalizer.NormalizedEvent) (string, error) {
266
+ // Forward to OpenClaw Gateway
267
+ response, err := gateway.Process(sessionKey, event)
268
+ if err != nil {
269
+ return "", err
270
+ }
271
+ return response.Text, nil
272
+ }
273
+ ```
274
+
275
+ ---
276
+
277
+ ## Configuration JSON Schema
278
+
279
+ ```json
280
+ {
281
+ "$schema": "http://json-schema.org/draft-07/schema#",
282
+ "type": "object",
283
+ "properties": {
284
+ "server": {
285
+ "type": "object",
286
+ "properties": {
287
+ "addr": {"type": "string", "default": ":8080"}
288
+ }
289
+ },
290
+ "channel": {
291
+ "type": "object",
292
+ "required": ["enabled"],
293
+ "properties": {
294
+ "enabled": {"type": "boolean"},
295
+ "mode": {"type": "string", "enum": ["app", "token"]},
296
+ "appId": {"type": "integer"},
297
+ "installationId": {"type": "integer"},
298
+ "privateKeyPath": {"type": "string"},
299
+ "webhookSecret": {"type": "string"},
300
+ "repositories": {
301
+ "type": "array",
302
+ "items": {"type": "string", "pattern": "^[^/]+/[^/]+$"}
303
+ },
304
+ "ignoreBots": {"type": "boolean", "default": true},
305
+ "accounts": {
306
+ "type": "object",
307
+ "additionalProperties": {
308
+ "type": "object",
309
+ "properties": {
310
+ "mode": {"type": "string", "enum": ["app", "token"]},
311
+ "appId": {"type": "integer"},
312
+ "installationId": {"type": "integer"},
313
+ "privateKeyPath": {"type": "string"},
314
+ "webhookSecret": {"type": "string"},
315
+ "repositories": {
316
+ "type": "array",
317
+ "items": {"type": "string"}
318
+ }
319
+ }
320
+ }
321
+ },
322
+ "trigger": {
323
+ "type": "object",
324
+ "properties": {
325
+ "requireMention": {"type": "boolean"},
326
+ "botUsername": {"type": "string"},
327
+ "commands": {"type": "array", "items": {"type": "string"}},
328
+ "labels": {"type": "array", "items": {"type": "string"}}
329
+ }
330
+ },
331
+ "autoTrigger": {
332
+ "type": "object",
333
+ "properties": {
334
+ "onPROpened": {"type": "boolean"},
335
+ "onIssueOpened": {"type": "boolean"}
336
+ }
337
+ },
338
+ "outbound": {
339
+ "type": "object",
340
+ "properties": {
341
+ "mode": {"type": "string", "enum": ["comment", "review", "auto"]},
342
+ "outboundMarker": {"type": "string"}
343
+ }
344
+ },
345
+ "rateLimit": {
346
+ "type": "object",
347
+ "properties": {
348
+ "maxEventsPerMinute": {"type": "integer", "minimum": 0}
349
+ }
350
+ }
351
+ }
352
+ }
353
+ }
354
+ }
355
+ ```
356
+
357
+ ---
358
+
359
+ ## Supported GitHub Events
360
+
361
+ ### Full Event Matrix
362
+
363
+ | Event | Action | Phase | Processed As |
364
+ |-------|--------|-------|-------------|
365
+ | `issues` | `opened` | 1 | `issue_body` message |
366
+ | `issues` | `edited` | 3 | `context_update` |
367
+ | `issues` | `closed` | 3 | `context_update` |
368
+ | `issues` | `reopened` | 3 | `context_update` |
369
+ | `issues` | `labeled` | 3 | `context_update` + label trigger |
370
+ | `issue_comment` | `created` | 1 | `comment` message |
371
+ | `issue_comment` | `edited` | 3 | `context_update` |
372
+ | `pull_request` | `opened` | 1 | `pr_body` message |
373
+ | `pull_request` | `edited` | 3 | `context_update` |
374
+ | `pull_request` | `closed` | 3 | `context_update` (closed/merged) |
375
+ | `pull_request` | `synchronize` | 3 | `context_update` (new commits) |
376
+ | `pull_request` | `labeled` | 3 | `context_update` + label trigger |
377
+ | `pull_request_review` | `submitted` | 1 | `review` message |
378
+ | `pull_request_review_comment` | `created` | 1 | `review_comment` message |
379
+ | `discussion` | `created` | 4 | `discussion_body` message |
380
+ | `discussion_comment` | `created` | 4 | `comment` message |
381
+ | `check_run` | `completed` | 4 | `ci_status` update |
382
+ | `workflow_run` | `completed` | 4 | `ci_status` update |
383
+
384
+ ---
385
+
386
+ ## Error Handling
387
+
388
+ ### Webhook Processing Errors
389
+
390
+ | Error | HTTP Status | Behavior |
391
+ |-------|-------------|----------|
392
+ | Invalid signature | 401 | Rejected immediately |
393
+ | Unsupported event | 200 | Acknowledged, not processed |
394
+ | Duplicate delivery | 200 | Acknowledged, not re-processed |
395
+ | Repository not allowed | 200 | Acknowledged, not processed |
396
+ | Internal error | 500 | Error logged, GitHub may retry |
397
+
398
+ ### Outbound Errors
399
+
400
+ When outbound API calls fail:
401
+ - Error is logged with session context
402
+ - HTTP 500 returned to webhook caller
403
+ - GitHub may retry the webhook delivery
404
+ - Idempotency store prevents duplicate processing on retry
405
+
406
+ ### Long Text Handling
407
+
408
+ GitHub comments have a ~65,536 character limit. The channel automatically truncates responses that exceed this limit, appending `"..."` to indicate truncation.
409
+
410
+ ---
411
+
412
+ ## Plugin Manifest
413
+
414
+ The channel includes a `plugin.yaml` manifest for integration with the OpenClaw plugin system.
415
+
416
+ ### Install via CLI
417
+
418
+ ```bash
419
+ openclaw plugins install github.com/Iceber/openclaw-channel-github
420
+ ```
421
+
422
+ ### Manifest Fields
423
+
424
+ | Field | Description |
425
+ |-------|-------------|
426
+ | `metadata.name` | Plugin name: `github` |
427
+ | `metadata.version` | Semantic version |
428
+ | `spec.channelType` | Channel type identifier: `github` |
429
+ | `spec.runtime.language` | `go` |
430
+ | `spec.runtime.entrypoint` | `cmd/openclaw-github-channel` |
431
+ | `spec.build.command` | Build command for the binary |
432
+ | `spec.capabilities` | Channel capability matrix |
433
+ | `spec.events` | Supported GitHub webhook events |
434
+ | `spec.permissions` | Required GitHub App permissions |
435
+ | `spec.defaultConfig` | Default configuration template |
436
+
437
+ ### Capability Matrix
438
+
439
+ | Capability | Supported |
440
+ |-----------|-----------|
441
+ | `textInbound` | ✅ |
442
+ | `textOutbound` | ✅ |
443
+ | `reaction` | ✅ |
444
+ | `editAwareness` | ✅ |
445
+ | `deleteAwareness` | ❌ |
446
+ | `attachmentSend` | ❌ |
447
+ | `threadReply` | ✅ |
448
+ | `richReviewOutput` | ✅ |
449
+ | `realtimeTyping` | ❌ |
450
+ | `realtimePresence` | ❌ |