@zhin.js/adapter-github 1.0.1 → 1.1.2
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/CHANGELOG.md +1212 -0
- package/README.md +73 -195
- package/adapters/github/index.js +51 -0
- package/adapters/github/index.ts +59 -0
- package/agents/github/agent.json +20 -0
- package/agents/github/boundaries.md +3 -0
- package/agents/github/conventions.md +3 -0
- package/agents/github/skills/github-account/SKILL.md +20 -0
- package/agents/github/skills/github-account/handlers.js +116 -0
- package/agents/github/skills/github-account/handlers.ts +136 -0
- package/agents/github/skills/github-account/tools/bind/index.js +13 -0
- package/agents/github/skills/github-account/tools/bind/index.ts +13 -0
- package/agents/github/skills/github-account/tools/install/index.js +13 -0
- package/agents/github/skills/github-account/tools/install/index.ts +13 -0
- package/agents/github/skills/github-account/tools/unbind/index.js +13 -0
- package/agents/github/skills/github-account/tools/unbind/index.ts +13 -0
- package/agents/github/skills/github-account/tools/whoami/index.js +13 -0
- package/agents/github/skills/github-account/tools/whoami/index.ts +13 -0
- package/agents/github/skills/github-cli/SKILL.md +47 -0
- package/agents/github/skills/github-repository/SKILL.md +21 -0
- package/agents/github/skills/github-repository/handlers.js +77 -0
- package/agents/github/skills/github-repository/handlers.ts +113 -0
- package/agents/github/skills/github-repository/tools/create_pr/index.js +20 -0
- package/agents/github/skills/github-repository/tools/create_pr/index.ts +20 -0
- package/agents/github/skills/github-repository/tools/patch_file/index.js +19 -0
- package/agents/github/skills/github-repository/tools/patch_file/index.ts +19 -0
- package/agents/github/skills/github-repository/tools/prepare_workspace/index.js +15 -0
- package/agents/github/skills/github-repository/tools/prepare_workspace/index.ts +15 -0
- package/agents/github/skills/github-repository/tools/push_branch/index.js +18 -0
- package/agents/github/skills/github-repository/tools/push_branch/index.ts +18 -0
- package/agents/github/skills/github-subscriptions/SKILL.md +20 -0
- package/agents/github/skills/github-subscriptions/handlers.js +101 -0
- package/agents/github/skills/github-subscriptions/handlers.ts +119 -0
- package/agents/github/skills/github-subscriptions/tools/star/index.js +16 -0
- package/agents/github/skills/github-subscriptions/tools/star/index.ts +16 -0
- package/agents/github/skills/github-subscriptions/tools/subscribe/index.js +16 -0
- package/agents/github/skills/github-subscriptions/tools/subscribe/index.ts +16 -0
- package/agents/github/skills/github-subscriptions/tools/subscriptions/index.js +13 -0
- package/agents/github/skills/github-subscriptions/tools/subscriptions/index.ts +13 -0
- package/agents/github/skills/github-subscriptions/tools/unsubscribe/index.js +15 -0
- package/agents/github/skills/github-subscriptions/tools/unsubscribe/index.ts +15 -0
- package/agents/github/system.md +3 -0
- package/commands/github/endpoint/add/[id]/index.js +3 -0
- package/commands/github/endpoint/add/[id]/index.ts +3 -0
- package/commands/github/endpoint/definition.js +23 -0
- package/commands/github/endpoint/definition.ts +23 -0
- package/commands/github/endpoint/list/index.js +3 -0
- package/commands/github/endpoint/list/index.ts +3 -0
- package/commands/github/endpoint/remove/[id]/index.js +3 -0
- package/commands/github/endpoint/remove/[id]/index.ts +3 -0
- package/lib/client.d.ts +36 -0
- package/lib/client.js +44 -0
- package/lib/endpoint.d.ts +32 -22
- package/lib/endpoint.js +125 -145
- package/lib/gh-client.d.ts +73 -1
- package/lib/gh-client.js +99 -1
- package/lib/github-channel-context.d.ts +16 -0
- package/lib/github-channel-context.js +31 -0
- package/lib/github-runtime-state.d.ts +1 -0
- package/lib/github-runtime-state.js +6 -0
- package/lib/index.d.ts +7 -32
- package/lib/index.js +7 -385
- package/lib/oauth-users.d.ts +33 -0
- package/lib/oauth-users.js +38 -0
- package/lib/protocol.d.ts +72 -0
- package/lib/protocol.js +281 -0
- package/lib/types.d.ts +0 -18
- package/lib/types.js +2 -4
- package/lib/webhook.d.ts +14 -0
- package/lib/webhook.js +88 -0
- package/lib/workspace-manager.d.ts +21 -0
- package/lib/workspace-manager.js +154 -0
- package/package.json +93 -23
- package/plugin.js +38 -0
- package/prompt-sections/platform/index.js +16 -0
- package/prompt-sections/platform/index.ts +16 -0
- package/schema.json +137 -0
- package/src/client.ts +62 -0
- package/src/endpoint.ts +145 -150
- package/src/gh-client.ts +131 -0
- package/src/github-channel-context.ts +46 -0
- package/src/github-runtime-state.ts +7 -0
- package/src/index.ts +48 -431
- package/src/oauth-users.ts +47 -0
- package/src/protocol.ts +372 -0
- package/src/types.ts +0 -22
- package/src/webhook.ts +130 -0
- package/src/workspace-manager.ts +168 -0
- package/lib/adapter.d.ts +0 -64
- package/lib/adapter.d.ts.map +0 -1
- package/lib/adapter.js +0 -416
- package/lib/adapter.js.map +0 -1
- package/lib/agent-prompt.d.ts +0 -3
- package/lib/agent-prompt.d.ts.map +0 -1
- package/lib/agent-prompt.js +0 -82
- package/lib/agent-prompt.js.map +0 -1
- package/lib/endpoint.d.ts.map +0 -1
- package/lib/endpoint.js.map +0 -1
- package/lib/gh-client.d.ts.map +0 -1
- package/lib/gh-client.js.map +0 -1
- package/lib/index.d.ts.map +0 -1
- package/lib/index.js.map +0 -1
- package/lib/register-github-mcp.d.ts +0 -6
- package/lib/register-github-mcp.d.ts.map +0 -1
- package/lib/register-github-mcp.js +0 -35
- package/lib/register-github-mcp.js.map +0 -1
- package/lib/types.d.ts.map +0 -1
- package/lib/types.js.map +0 -1
- package/plugin.yml +0 -3
- package/skills/github/SKILL.md +0 -266
- package/src/adapter.ts +0 -448
- package/src/agent-prompt.ts +0 -99
- package/src/register-github-mcp.ts +0 -60
package/README.md
CHANGED
|
@@ -1,19 +1,14 @@
|
|
|
1
1
|
# @zhin.js/adapter-github
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
查询 · 管理 · 通知 · OAuth 用户绑定 四合一。
|
|
3
|
+
GitHub Plugin Runtime 适配器 — Issue/PR 评论区即聊天通道,GitHub App 认证,Webhook 入站经 `httpHostToken`。
|
|
6
4
|
|
|
7
5
|
## 功能特性
|
|
8
6
|
|
|
9
|
-
-
|
|
10
|
-
-
|
|
11
|
-
-
|
|
12
|
-
-
|
|
13
|
-
-
|
|
14
|
-
- ✅ **OAuth 用户绑定**:用户可绑定自己的 GitHub 账号,star/fork 以个人身份执行
|
|
15
|
-
- ✅ **AI Skill**:所有工具自动暴露给 AI 调用
|
|
16
|
-
- ✅ **无 router 也能运行**:核心功能不依赖 HTTP 服务,有 router 时自动注册 Webhook 和 OAuth 路由
|
|
7
|
+
- **聊天通道**:Issue/PR 评论区映射为群聊,支持收发消息
|
|
8
|
+
- **Webhook 入站**:HMAC-SHA256 验签 → `Endpoint.emit(...)`
|
|
9
|
+
- **出站**:`send({ conversation, payload })` → Issue/PR comment(`conversation.id` 为 channel ID)
|
|
10
|
+
- **GitHub App 认证**:JWT → Installation Token
|
|
11
|
+
- **Agent 工具**:按账号、订阅和仓库操作拆分在 `agents/github/skills/github-*/tools/`,只激活当前任务所需的一组
|
|
17
12
|
|
|
18
13
|
## 安装
|
|
19
14
|
|
|
@@ -21,66 +16,58 @@
|
|
|
21
16
|
pnpm add @zhin.js/adapter-github
|
|
22
17
|
```
|
|
23
18
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
- **Homepage URL**: 任意
|
|
35
|
-
- **Webhook URL**: `http://your-server:port/pub/github/webhook`(有 HTTP 服务时)
|
|
36
|
-
- **Webhook secret**: 自定义密钥
|
|
37
|
-
4. 权限设置(Permissions):
|
|
38
|
-
- **Repository → Issues**: Read & Write
|
|
39
|
-
- **Repository → Pull requests**: Read & Write
|
|
40
|
-
- **Repository → Contents**: Read
|
|
41
|
-
- **Repository → Metadata**: Read
|
|
42
|
-
- **Repository → Actions**: Read(查看 CI)
|
|
43
|
-
5. 订阅事件(Subscribe to events):
|
|
44
|
-
- Issue comment、Pull request、Pull request review、Pull request review comment
|
|
45
|
-
- Push、Star、Fork(用于通知)
|
|
46
|
-
6. 创建后记录 **App ID**,点击 **Generate a private key** 下载 `.pem` 文件
|
|
47
|
-
7. 安装 App 到目标仓库/组织
|
|
48
|
-
|
|
49
|
-
## 配置
|
|
19
|
+
Webhook 需要 Root 提供 `@zhin.js/host-http`(`zhin runtime start` 默认装配)。
|
|
20
|
+
|
|
21
|
+
## 前置条件
|
|
22
|
+
|
|
23
|
+
1. 创建 GitHub App,记录 App ID、私钥与 Webhook Secret。
|
|
24
|
+
2. 授予目标仓库所需的 Issues、Pull requests 与 Contents 权限。
|
|
25
|
+
3. 将 Webhook 指向公网 HTTPS 的 `/github/webhook`,并订阅 Issue、PR 与评论事件。
|
|
26
|
+
4. 把 App 安装到目标仓库;仓库 Workroom 使用稳定的 `owner/repo` 地址匹配。
|
|
27
|
+
|
|
28
|
+
## 配置(Plugin Runtime)
|
|
50
29
|
|
|
51
30
|
```yaml
|
|
52
31
|
# zhin.config.yml
|
|
53
|
-
endpoints:
|
|
54
|
-
- context: github
|
|
55
|
-
name: my-github-bot
|
|
56
|
-
app_id: 123456
|
|
57
|
-
private_key: ./data/github-app.pem # PEM 文件路径或直接粘贴内容
|
|
58
|
-
webhook_secret: your-secret # 可选;配置后启用 Webhook
|
|
59
|
-
webhook_path: /pub/github/webhook # 可选;默认 /github/webhook,/pub/* 可绕过 Bearer
|
|
60
|
-
|
|
61
32
|
plugins:
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
33
|
+
github:
|
|
34
|
+
webhook_path: /github/webhook
|
|
35
|
+
auto_reply_repos:
|
|
36
|
+
- zhinjs/zhin
|
|
37
|
+
workspace_root: ./data/github-workspaces
|
|
38
|
+
endpoints:
|
|
39
|
+
- id: my-github-bot
|
|
40
|
+
app_id: "${GITHUB_APP_ID}"
|
|
41
|
+
private_key: ./data/github-app.pem
|
|
42
|
+
webhook_secret: "${GITHUB_WEBHOOK_SECRET}"
|
|
65
43
|
```
|
|
66
44
|
|
|
67
|
-
|
|
68
|
-
GITHUB_APP_ID=123456
|
|
69
|
-
GITHUB_WEBHOOK_SECRET=your-secret
|
|
70
|
-
# MCP server-github(适配器自动 addMcp,与 App 认证独立)
|
|
71
|
-
GITHUB_PERSONAL_ACCESS_TOKEN=ghp_xxxxxxxxxxxx
|
|
72
|
-
# 或 ai.githubMcp.token / ai.mcpServers
|
|
73
|
-
```
|
|
45
|
+
`private_key` 支持文件路径或 PEM 内容。未配置 `webhook_secret` 时仅 API 出站 / agent 工具可用(无入站)。
|
|
74
46
|
|
|
75
|
-
|
|
47
|
+
`AdapterIndex` 会先合并插件实例默认值与 endpoint 覆盖值,再把一份完整配置交给
|
|
48
|
+
GitHub adapter。协议层只接受这份展开后的 endpoint 配置,不读取环境变量、不解析嵌套
|
|
49
|
+
`endpoints`,也不接受 camelCase 配置别名。`${...}` 由 composition root 在加载配置时展开。
|
|
76
50
|
|
|
77
|
-
`private_key`
|
|
78
|
-
- 文件路径:`./data/github-app.pem`
|
|
79
|
-
- 直接内容:`"-----BEGIN RSA PRIVATE KEY-----\n..."`
|
|
51
|
+
多 App:一个插件实例挂多个 endpoint(`endpoints` 数组逐项覆盖顶层字段;`id`、`app_id`、`private_key` 必填):
|
|
80
52
|
|
|
81
|
-
|
|
53
|
+
```yaml
|
|
54
|
+
plugins:
|
|
55
|
+
github:
|
|
56
|
+
endpoints:
|
|
57
|
+
- id: app-a
|
|
58
|
+
app_id: 123456
|
|
59
|
+
private_key: ./data/app-a.pem
|
|
60
|
+
- id: app-b
|
|
61
|
+
app_id: 234567
|
|
62
|
+
private_key: ./data/app-b.pem
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## 已移除的能力
|
|
82
66
|
|
|
83
|
-
|
|
67
|
+
- **`ai.githubMcp.enabled` / `ai.githubMcp.token`**:Plugin Runtime 迁移后 `register-github-mcp`(stdio `@modelcontextprotocol/server-github`,PAT 人身份)已移除,该配置不再生效。如需 MCP 工具,请按 `mcps/<name>/index.ts`(`@zhin.js/mcp-feature`)约定自行装配。
|
|
68
|
+
- 轮询降级已删除,入站只通过 Webhook;没有保留无效的轮询配置字段。
|
|
69
|
+
|
|
70
|
+
## Channel ID
|
|
84
71
|
|
|
85
72
|
| 类型 | Channel ID | 示例 |
|
|
86
73
|
|------|-----------|------|
|
|
@@ -89,141 +76,32 @@ GitHub adapter 将 Issue/PR 映射为聊天频道:
|
|
|
89
76
|
|
|
90
77
|
## AI 工具
|
|
91
78
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
| 工具 | 说明 |
|
|
95
|
-
|------|------|
|
|
96
|
-
| `github_star` | Star / Unstar / 检查(per-user OAuth 或 Endpoint 默认) |
|
|
97
|
-
| `github_bind` / `github_unbind` / `github_whoami` | 用户 GitHub 账号绑定 |
|
|
98
|
-
| `github_install` | GitHub App 安装链接 |
|
|
99
|
-
| `github_subscribe` / `github_unsubscribe` / `github_subscriptions` | 频道级 Webhook 订阅 |
|
|
79
|
+
见 `agents/github/skills/github-*/tools/`:账号绑定、订阅管理和仓库写入分别按需披露。
|
|
100
80
|
|
|
101
|
-
|
|
81
|
+
## 架构
|
|
102
82
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
配置好 Webhook 后,支持以下事件的跨平台推送:
|
|
127
|
-
|
|
128
|
-
| 事件 | 参数 | 说明 |
|
|
129
|
-
|------|------|------|
|
|
130
|
-
| Push | `push` | 代码推送 |
|
|
131
|
-
| Issue | `issue` | Issue 创建/更新/关闭 |
|
|
132
|
-
| Star | `star` | 仓库被 star |
|
|
133
|
-
| Unstar | `unstar` | 仓库被 unstar |
|
|
134
|
-
| Fork | `fork` | 仓库被 fork |
|
|
135
|
-
| Pull Request | `pr` / `pull_request` | PR 创建/更新/合并 |
|
|
136
|
-
|
|
137
|
-
### Webhook 端点
|
|
138
|
-
|
|
139
|
-
```
|
|
140
|
-
POST /pub/github/webhook
|
|
141
|
-
```
|
|
142
|
-
|
|
143
|
-
Headers:
|
|
144
|
-
- `X-GitHub-Event`: 事件类型
|
|
145
|
-
- `X-Hub-Signature-256`: 签名(配置了 secret 时)
|
|
146
|
-
|
|
147
|
-
## OAuth 用户绑定(Device Flow)
|
|
148
|
-
|
|
149
|
-
用户通过工具 **`github_bind`** 绑定 GitHub 账号([OAuth Device Flow](https://docs.github.com/en/apps/oauth-apps/building-oauth-apps/authorizing-oauth-apps#device-flow)),**不是**浏览器 OAuth 回调页面。
|
|
150
|
-
|
|
151
|
-
### 认证策略
|
|
152
|
-
|
|
153
|
-
| 操作类型 | 使用的 Token | 原因 |
|
|
154
|
-
|---------|------------|------|
|
|
155
|
-
| 读取 issue/PR/repo 信息 | App JWT | 不涉及用户身份 |
|
|
156
|
-
| 评论(bot 发言) | App JWT | 以机器人身份发言 |
|
|
157
|
-
| star / unstar | User OAuth | 个人行为 |
|
|
158
|
-
| fork | User OAuth | 个人行为 |
|
|
159
|
-
|
|
160
|
-
### 绑定流程
|
|
161
|
-
|
|
162
|
-
1. 用户在聊天中触发 `github_bind`(或说「绑定 GitHub」)
|
|
163
|
-
2. Endpoint 返回 **verification URI** 与 **user code**(GitHub Device Flow)
|
|
164
|
-
3. 用户在浏览器打开链接并输入 code 完成授权
|
|
165
|
-
4. Token 写入 `github_oauth_users` 表;可用 `github_whoami` / `github_unbind` 管理
|
|
166
|
-
|
|
167
|
-
**Client ID**:从 GitHub App 的 `/app` API 自动获取(`GhClient.getClientId()`),**无需**在 Endpoint 配置里写 `client_id` / `client_secret`。
|
|
168
|
-
|
|
169
|
-
### 前置条件
|
|
170
|
-
|
|
171
|
-
- 需要 **`database`** 插件(`github_oauth_users` 模型)
|
|
172
|
-
- Device Flow 在 IM 内完成,**不依赖** `/pub/github/oauth` HTTP 路由
|
|
173
|
-
|
|
174
|
-
## 数据库表
|
|
175
|
-
|
|
176
|
-
### github_subscriptions
|
|
177
|
-
|
|
178
|
-
| 字段 | 类型 | 说明 |
|
|
179
|
-
|------|------|------|
|
|
180
|
-
| id | integer | 主键 |
|
|
181
|
-
| repo | text | 仓库名 (owner/repo) |
|
|
182
|
-
| events | json | 订阅的事件列表 |
|
|
183
|
-
| target_id | text | 目标频道 ID |
|
|
184
|
-
| target_type | text | 类型 (private/group/channel) |
|
|
185
|
-
| adapter | text | 适配器名称 |
|
|
186
|
-
| endpoint | text | 机器人名称 |
|
|
187
|
-
|
|
188
|
-
### github_events
|
|
189
|
-
|
|
190
|
-
| 字段 | 类型 | 说明 |
|
|
191
|
-
|------|------|------|
|
|
192
|
-
| id | integer | 主键 |
|
|
193
|
-
| repo | text | 仓库名 |
|
|
194
|
-
| event_type | text | 事件类型 |
|
|
195
|
-
| payload | json | 事件载荷 |
|
|
196
|
-
|
|
197
|
-
### github_oauth_users
|
|
198
|
-
|
|
199
|
-
| 字段 | 类型 | 说明 |
|
|
200
|
-
|------|------|------|
|
|
201
|
-
| id | integer | 主键 |
|
|
202
|
-
| platform | text | 聊天平台 |
|
|
203
|
-
| platform_uid | text | 平台用户 ID |
|
|
204
|
-
| github_login | text | GitHub 用户名 |
|
|
205
|
-
| access_token | text | Device Flow 获得的 token |
|
|
206
|
-
| created_at | integer | 创建时间戳 |
|
|
207
|
-
|
|
208
|
-
## 架构说明
|
|
209
|
-
|
|
210
|
-
```
|
|
211
|
-
adapter-github/
|
|
212
|
-
├── src/
|
|
213
|
-
│ ├── index.ts # 插件入口:provide、useContext、模型表
|
|
214
|
-
│ ├── adapter.ts # GitHubAdapter
|
|
215
|
-
│ ├── endpoint.ts # GitHubEndpoint(Issue/PR 频道)
|
|
216
|
-
│ ├── gh-client.ts # GitHub REST(App JWT / Installation Token)
|
|
217
|
-
│ ├── register-github-mcp.ts # orchestrator.addMcp(server-github)
|
|
218
|
-
│ ├── agent-prompt.ts # 平台 prompt / deferred 工具筛选
|
|
219
|
-
│ └── types.ts # EndpointConfig、Webhook payload、Channel ID
|
|
220
|
-
├── package.json
|
|
221
|
-
└── README.md
|
|
222
|
-
```
|
|
223
|
-
|
|
224
|
-
- **无 `@zhin.js/host-router` 时**:REST 工具与轮询仍可用,但无法注册 Webhook
|
|
225
|
-
- **有 `@zhin.js/host-router` 时**:注册 Webhook;MCP 通过 `orchestrator.addMcp`(需 PAT 或 `ai.githubMcp.token`)
|
|
226
|
-
|
|
227
|
-
## 许可证
|
|
83
|
+
| 路径 | 职责 |
|
|
84
|
+
|------|------|
|
|
85
|
+
| `plugin.ts` | 插件元数据;有 DatabaseHost 时定义 `github_oauth_users` |
|
|
86
|
+
| `adapters/github/index.ts` | 薄 `defineAdapter` 入口(发现约定) |
|
|
87
|
+
| `src/endpoint.ts` | Endpoint 生命周期、出站、admit |
|
|
88
|
+
| `src/webhook.ts` | HMAC 验签与事件分发 |
|
|
89
|
+
| `src/oauth-users.ts` | OAuth 表 SSOT + token 查找 |
|
|
90
|
+
| `src/protocol.ts` | 协议纯函数(channel / payload) |
|
|
91
|
+
| `src/gh-client.ts` | GitHub API 客户端 |
|
|
92
|
+
|
|
93
|
+
- 入站:`httpHostToken` POST → `Endpoint.emit(...)`
|
|
94
|
+
- 出站:`send({ conversation, payload })`
|
|
95
|
+
|
|
96
|
+
## 故障排查
|
|
97
|
+
|
|
98
|
+
| 现象 | 排查 |
|
|
99
|
+
| --- | --- |
|
|
100
|
+
| Webhook 401 | 检查 Secret、`X-Hub-Signature-256` 与原始请求体 |
|
|
101
|
+
| App 鉴权失败 | 检查 App ID、私钥 PEM/文件路径与服务器时钟 |
|
|
102
|
+
| 评论没有进入 Workroom | 先查 Endpoint 收件箱,再核对 Catalog 的 Endpoint 与 `owner/repo` |
|
|
103
|
+
| 能收事件但不能回复 | 检查 installation 与仓库权限;PAT MCP 不替代 App 出站 |
|
|
104
|
+
|
|
105
|
+
## License
|
|
228
106
|
|
|
229
107
|
MIT
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
// Generated by build-plugin-runtime-entries.mjs. Do not edit.
|
|
2
|
+
/**
|
|
3
|
+
* Convention entry: discover `adapters/github/index.ts` → defineAdapter.
|
|
4
|
+
* Implementation lives under `src/` (endpoint / webhook / oauth / protocol).
|
|
5
|
+
*/
|
|
6
|
+
import { defineAdapter } from 'zhin.js/adapter';
|
|
7
|
+
import { httpHostToken } from '@zhin.js/host-http';
|
|
8
|
+
import { databaseHostToken, } from 'zhin.js';
|
|
9
|
+
import { GithubEndpoint } from "../../lib/endpoint.js";
|
|
10
|
+
import { githubRuntimeStateToken } from "../../lib/github-runtime-state.js";
|
|
11
|
+
import { resolveGithubConfig, } from "../../lib/protocol.js";
|
|
12
|
+
export { GithubEndpoint } from "../../lib/endpoint.js";
|
|
13
|
+
function optionalDatabase(context) {
|
|
14
|
+
try {
|
|
15
|
+
return context.use(databaseHostToken);
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
return undefined;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
export default defineAdapter({
|
|
22
|
+
capabilities: ['inbound', 'outbound'],
|
|
23
|
+
// Issue/PR 评论以 markdown 图片链接消费远程 URL;无交互面,交互段降级纯文本。
|
|
24
|
+
segments: {
|
|
25
|
+
outboundMedia: ['url'],
|
|
26
|
+
interactive: 'text',
|
|
27
|
+
},
|
|
28
|
+
create(context) {
|
|
29
|
+
const config = resolveGithubConfig(context.config);
|
|
30
|
+
const database = optionalDatabase(context);
|
|
31
|
+
// 注册到插件运行时状态(github endpoint list 的"运行中"数据源)
|
|
32
|
+
context.use(githubRuntimeStateToken).endpoints.set(config.id, {
|
|
33
|
+
id: config.id,
|
|
34
|
+
mode: config.webhookSecret ? 'webhook' : 'api',
|
|
35
|
+
});
|
|
36
|
+
if (config.webhookSecret) {
|
|
37
|
+
return new GithubEndpoint({
|
|
38
|
+
id: context.id,
|
|
39
|
+
http: context.use(httpHostToken),
|
|
40
|
+
database,
|
|
41
|
+
config,
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
// API-only: Issue/PR send + agent tools without webhook.
|
|
45
|
+
return new GithubEndpoint({
|
|
46
|
+
id: context.id,
|
|
47
|
+
database,
|
|
48
|
+
config,
|
|
49
|
+
});
|
|
50
|
+
},
|
|
51
|
+
});
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Convention entry: discover `adapters/github/index.ts` → defineAdapter.
|
|
3
|
+
* Implementation lives under `src/` (endpoint / webhook / oauth / protocol).
|
|
4
|
+
*/
|
|
5
|
+
import { defineAdapter, type AdapterContext } from 'zhin.js/adapter';
|
|
6
|
+
import { httpHostToken } from '@zhin.js/host-http';
|
|
7
|
+
import {
|
|
8
|
+
databaseHostToken,
|
|
9
|
+
type PluginDatabaseHost,
|
|
10
|
+
} from 'zhin.js';
|
|
11
|
+
import { GithubEndpoint } from '../../src/endpoint.js';
|
|
12
|
+
import { githubRuntimeStateToken } from '../../src/github-runtime-state.js';
|
|
13
|
+
import {
|
|
14
|
+
resolveGithubConfig,
|
|
15
|
+
type GithubEndpointConfig,
|
|
16
|
+
} from '../../src/protocol.js';
|
|
17
|
+
|
|
18
|
+
export { GithubEndpoint } from '../../src/endpoint.js';
|
|
19
|
+
export type { GithubEndpointOptions } from '../../src/endpoint.js';
|
|
20
|
+
|
|
21
|
+
function optionalDatabase(context: AdapterContext): PluginDatabaseHost | undefined {
|
|
22
|
+
try {
|
|
23
|
+
return context.use(databaseHostToken);
|
|
24
|
+
} catch {
|
|
25
|
+
return undefined;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export default defineAdapter<GithubEndpointConfig>({
|
|
30
|
+
capabilities: ['inbound', 'outbound'],
|
|
31
|
+
// Issue/PR 评论以 markdown 图片链接消费远程 URL;无交互面,交互段降级纯文本。
|
|
32
|
+
segments: {
|
|
33
|
+
outboundMedia: ['url'],
|
|
34
|
+
interactive: 'text',
|
|
35
|
+
},
|
|
36
|
+
create(context) {
|
|
37
|
+
const config = resolveGithubConfig(context.config);
|
|
38
|
+
const database = optionalDatabase(context);
|
|
39
|
+
// 注册到插件运行时状态(github endpoint list 的"运行中"数据源)
|
|
40
|
+
context.use(githubRuntimeStateToken).endpoints.set(config.id, {
|
|
41
|
+
id: config.id,
|
|
42
|
+
mode: config.webhookSecret ? 'webhook' : 'api',
|
|
43
|
+
});
|
|
44
|
+
if (config.webhookSecret) {
|
|
45
|
+
return new GithubEndpoint({
|
|
46
|
+
id: context.id,
|
|
47
|
+
http: context.use(httpHostToken),
|
|
48
|
+
database,
|
|
49
|
+
config,
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
// API-only: Issue/PR send + agent tools without webhook.
|
|
53
|
+
return new GithubEndpoint({
|
|
54
|
+
id: context.id,
|
|
55
|
+
database,
|
|
56
|
+
config,
|
|
57
|
+
});
|
|
58
|
+
},
|
|
59
|
+
});
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "GITHUB Platform Agent",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Handles github platform-specific operations and progressively loads only the required platform Skill.",
|
|
5
|
+
"trigger_rules": {
|
|
6
|
+
"file_patterns": [],
|
|
7
|
+
"keywords": [
|
|
8
|
+
"github",
|
|
9
|
+
"adapter:github"
|
|
10
|
+
]
|
|
11
|
+
},
|
|
12
|
+
"entry_points": [
|
|
13
|
+
"system.md",
|
|
14
|
+
"boundaries.md",
|
|
15
|
+
"conventions.md"
|
|
16
|
+
],
|
|
17
|
+
"platforms": [
|
|
18
|
+
"github"
|
|
19
|
+
]
|
|
20
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: github-account
|
|
3
|
+
platforms:
|
|
4
|
+
- github
|
|
5
|
+
description: GitHub 账号绑定、安装状态和身份查询能力。
|
|
6
|
+
keywords:
|
|
7
|
+
- "github"
|
|
8
|
+
- "账号"
|
|
9
|
+
- "绑定"
|
|
10
|
+
- "安装"
|
|
11
|
+
tools:
|
|
12
|
+
- "bind"
|
|
13
|
+
- "install"
|
|
14
|
+
- "unbind"
|
|
15
|
+
- "whoami"
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
# github-account
|
|
19
|
+
|
|
20
|
+
处理 GitHub 身份与绑定状态时加载。`bind` 使用 Device Flow 绑定用户 OAuth 身份;`install` 返回 GitHub App 安装入口;`whoami` 用于确认当前绑定。用户想用自己的账号执行操作时先绑定。解绑和安装相关操作遵守 Tool 审批,不回显 Token、Device Code 或其他凭据。
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
// Generated by build-plugin-runtime-entries.mjs. Do not edit.
|
|
2
|
+
import { formatCompact } from '@zhin.js/logger';
|
|
3
|
+
import { getCurrentCommMessage } from '@zhin.js/agent/security';
|
|
4
|
+
import { GhClient } from "../../../../lib/gh-client.js";
|
|
5
|
+
function oauthModel(client) {
|
|
6
|
+
const db = client.database;
|
|
7
|
+
return db?.models?.get('github_oauth_users');
|
|
8
|
+
}
|
|
9
|
+
function depsLogger() {
|
|
10
|
+
return {
|
|
11
|
+
debug: (...args) => console.debug(...args),
|
|
12
|
+
warn: (...args) => console.warn(...args),
|
|
13
|
+
error: (...args) => console.error(...args),
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
export async function executeGithubBind(_args, client, commMessage) {
|
|
17
|
+
const log = depsLogger();
|
|
18
|
+
const msg = commMessage ?? getCurrentCommMessage();
|
|
19
|
+
if (!msg?.$adapter || !msg?.$sender?.id)
|
|
20
|
+
return '❌ 无法获取当前用户信息';
|
|
21
|
+
const clientId = client.clientId;
|
|
22
|
+
if (!clientId)
|
|
23
|
+
return '❌ Endpoint 未配置 GitHub App 或 App 无 client_id,无法进行账号绑定';
|
|
24
|
+
const model = oauthModel(client);
|
|
25
|
+
if (!model)
|
|
26
|
+
return '❌ 数据库未就绪';
|
|
27
|
+
const [existing] = await model.select().where({ platform: msg.$adapter, platform_uid: msg.$sender.id });
|
|
28
|
+
if (existing) {
|
|
29
|
+
return `⚠️ 你已绑定 GitHub 账号: ${existing.github_login}\n如需重新绑定,请先执行 github_unbind`;
|
|
30
|
+
}
|
|
31
|
+
try {
|
|
32
|
+
const host = client.host;
|
|
33
|
+
const codeResp = await GhClient.deviceFlowRequestCode(clientId, host);
|
|
34
|
+
const tokenPromise = GhClient.deviceFlowPollToken(clientId, codeResp.device_code, codeResp.interval, codeResp.expires_in, host);
|
|
35
|
+
const replyMsg = [
|
|
36
|
+
'🔗 请在浏览器中打开以下链接进行授权:',
|
|
37
|
+
` ${codeResp.verification_uri}`,
|
|
38
|
+
'',
|
|
39
|
+
`📋 输入验证码: **${codeResp.user_code}**`,
|
|
40
|
+
'',
|
|
41
|
+
`⏳ 等待授权中…(${Math.floor(codeResp.expires_in / 60)} 分钟内有效)`,
|
|
42
|
+
].join('\n');
|
|
43
|
+
tokenPromise.then(async (tokenData) => {
|
|
44
|
+
if (!tokenData) {
|
|
45
|
+
log.warn(formatCompact({ op: 'device_flow', ok: false, platform: msg.$adapter, sender: msg.$sender.id }));
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
const userGh = new GhClient({ host, token: tokenData.access_token });
|
|
49
|
+
const authResult = await userGh.verifyAuth();
|
|
50
|
+
const login = authResult.ok ? authResult.user : 'unknown';
|
|
51
|
+
await model.insert({
|
|
52
|
+
id: Date.now(),
|
|
53
|
+
platform: msg.$adapter,
|
|
54
|
+
platform_uid: msg.$sender.id,
|
|
55
|
+
github_login: login,
|
|
56
|
+
access_token: tokenData.access_token,
|
|
57
|
+
created_at: Date.now(),
|
|
58
|
+
});
|
|
59
|
+
log.debug(formatCompact({ op: 'bind', platform: msg.$adapter, sender: msg.$sender.id, login }));
|
|
60
|
+
if (msg?.$reply) {
|
|
61
|
+
await msg.$reply(`✅ GitHub 账号绑定成功!\n👤 ${login}`);
|
|
62
|
+
}
|
|
63
|
+
}).catch((err) => {
|
|
64
|
+
log.error('GitHub Device Flow 错误:', err);
|
|
65
|
+
});
|
|
66
|
+
return replyMsg;
|
|
67
|
+
}
|
|
68
|
+
catch (e) {
|
|
69
|
+
return `❌ Device Flow 启动失败: ${e instanceof Error ? e.message : String(e)}`;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
export async function executeGithubUnbind(_args, client, commMessage) {
|
|
73
|
+
const msg = commMessage ?? getCurrentCommMessage();
|
|
74
|
+
if (!msg?.$adapter || !msg?.$sender?.id)
|
|
75
|
+
return '❌ 无法获取当前用户信息';
|
|
76
|
+
const model = oauthModel(client);
|
|
77
|
+
if (!model)
|
|
78
|
+
return '❌ 数据库未就绪';
|
|
79
|
+
const [existing] = await model.select().where({ platform: msg.$adapter, platform_uid: msg.$sender.id });
|
|
80
|
+
if (!existing)
|
|
81
|
+
return '📭 你尚未绑定 GitHub 账号';
|
|
82
|
+
await model.delete().where({ id: existing.id });
|
|
83
|
+
return `✅ 已解除 GitHub 账号绑定: ${existing.github_login}`;
|
|
84
|
+
}
|
|
85
|
+
export async function executeGithubWhoami(_args, client, commMessage) {
|
|
86
|
+
const msg = commMessage ?? getCurrentCommMessage();
|
|
87
|
+
if (!msg?.$adapter || !msg?.$sender?.id)
|
|
88
|
+
return '❌ 无法获取当前用户信息';
|
|
89
|
+
const model = oauthModel(client);
|
|
90
|
+
if (!model)
|
|
91
|
+
return '❌ 数据库未就绪';
|
|
92
|
+
const [existing] = await model.select().where({ platform: msg.$adapter, platform_uid: msg.$sender.id });
|
|
93
|
+
if (!existing)
|
|
94
|
+
return '📭 你尚未绑定 GitHub 账号\n🔗 使用 github_bind 绑定你的账号';
|
|
95
|
+
const userGh = new GhClient({ host: client.host, token: existing.access_token });
|
|
96
|
+
const auth = await userGh.verifyAuth();
|
|
97
|
+
if (auth.ok) {
|
|
98
|
+
return `👤 已绑定 GitHub 账号: ${auth.user}\n📅 绑定时间: ${new Date(existing.created_at).toLocaleString('zh-CN')}`;
|
|
99
|
+
}
|
|
100
|
+
return `⚠️ 已绑定账号 ${existing.github_login},但 Token 已失效\n🔗 请执行 github_unbind 后重新 github_bind`;
|
|
101
|
+
}
|
|
102
|
+
export async function executeGithubInstall(client) {
|
|
103
|
+
const slug = client.appSlug;
|
|
104
|
+
if (!slug)
|
|
105
|
+
return '❌ Endpoint 未配置 GitHub App';
|
|
106
|
+
const host = client.host || 'github.com';
|
|
107
|
+
const installations = client.installations;
|
|
108
|
+
let msg = `🔗 请点击以下链接安装 GitHub App 到你的仓库:\n https://${host}/apps/${slug}/installations/new`;
|
|
109
|
+
if (installations.length) {
|
|
110
|
+
msg += `\n\n📋 当前已安装 (${installations.length}):`;
|
|
111
|
+
for (const inst of installations) {
|
|
112
|
+
msg += `\n • ${inst.account.login} (${inst.account.type})`;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
return msg;
|
|
116
|
+
}
|