@zhin.js/adapter-github 3.0.2 → 4.0.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/CHANGELOG.md +646 -0
- package/README.md +55 -203
- package/adapters/github.ts +51 -0
- package/agent/skills/github.md +2 -2
- package/agent/tools/bind.ts +2 -2
- package/agent/tools/create_pr.ts +2 -2
- package/agent/tools/install.ts +2 -2
- package/agent/tools/patch_file.ts +2 -2
- package/agent/tools/prepare_workspace.ts +2 -2
- package/agent/tools/push_branch.ts +2 -2
- package/agent/tools/star.ts +2 -2
- package/agent/tools/subscribe.ts +2 -2
- package/agent/tools/subscriptions.ts +2 -2
- package/agent/tools/unbind.ts +2 -2
- package/agent/tools/unsubscribe.ts +2 -2
- package/agent/tools/whoami.ts +2 -2
- package/lib/agent-prompt.d.ts +2 -0
- package/lib/{src/agent-prompt.js → agent-prompt.js} +0 -1
- package/lib/endpoint.d.ts +48 -0
- package/lib/endpoint.js +165 -0
- package/lib/gh-client.d.ts +300 -0
- package/lib/{src/gh-client.js → gh-client.js} +1 -2
- package/lib/github-agent-deps.d.ts +47 -0
- package/lib/github-agent-deps.js +43 -0
- package/lib/github-bot-handlers.d.ts +26 -0
- package/lib/{src/github-bot-handlers.js → github-bot-handlers.js} +0 -1
- package/lib/github-channel-context.d.ts +16 -0
- package/lib/{src/github-channel-context.js → github-channel-context.js} +0 -1
- package/lib/github-tool-handlers.d.ts +17 -0
- package/lib/{src/github-tool-handlers.js → github-tool-handlers.js} +14 -8
- package/lib/index.d.ts +9 -0
- package/lib/index.js +9 -0
- package/lib/oauth-users.d.ts +33 -0
- package/lib/oauth-users.js +38 -0
- package/lib/protocol.d.ts +87 -0
- package/lib/protocol.js +241 -0
- package/lib/types.d.ts +149 -0
- package/lib/{src/types.js → types.js} +0 -1
- package/lib/webhook.d.ts +13 -0
- package/lib/webhook.js +87 -0
- package/lib/workspace-manager.d.ts +21 -0
- package/lib/{src/workspace-manager.js → workspace-manager.js} +46 -1
- package/package.json +53 -26
- package/plugin.ts +52 -0
- package/schema.json +94 -0
- package/src/endpoint.ts +166 -175
- package/src/gh-client.ts +1 -1
- package/src/github-agent-deps.ts +66 -11
- package/src/github-tool-handlers.ts +20 -7
- package/src/index.ts +55 -137
- package/src/oauth-users.ts +47 -0
- package/src/protocol.ts +367 -0
- package/src/webhook.ts +125 -0
- package/src/workspace-manager.ts +54 -0
- package/lib/agent/tools/bind.js +0 -12
- package/lib/agent/tools/bind.js.map +0 -1
- package/lib/agent/tools/create_pr.js +0 -19
- package/lib/agent/tools/create_pr.js.map +0 -1
- package/lib/agent/tools/install.js +0 -12
- package/lib/agent/tools/install.js.map +0 -1
- package/lib/agent/tools/patch_file.js +0 -18
- package/lib/agent/tools/patch_file.js.map +0 -1
- package/lib/agent/tools/prepare_workspace.js +0 -14
- package/lib/agent/tools/prepare_workspace.js.map +0 -1
- package/lib/agent/tools/push_branch.js +0 -17
- package/lib/agent/tools/push_branch.js.map +0 -1
- package/lib/agent/tools/star.js +0 -15
- package/lib/agent/tools/star.js.map +0 -1
- package/lib/agent/tools/subscribe.js +0 -16
- package/lib/agent/tools/subscribe.js.map +0 -1
- package/lib/agent/tools/subscriptions.js +0 -13
- package/lib/agent/tools/subscriptions.js.map +0 -1
- package/lib/agent/tools/unbind.js +0 -12
- package/lib/agent/tools/unbind.js.map +0 -1
- package/lib/agent/tools/unsubscribe.js +0 -15
- package/lib/agent/tools/unsubscribe.js.map +0 -1
- package/lib/agent/tools/whoami.js +0 -12
- package/lib/agent/tools/whoami.js.map +0 -1
- package/lib/src/adapter.js +0 -423
- package/lib/src/adapter.js.map +0 -1
- package/lib/src/agent-prompt.js.map +0 -1
- package/lib/src/endpoint.js +0 -189
- package/lib/src/endpoint.js.map +0 -1
- package/lib/src/gh-client.js.map +0 -1
- package/lib/src/github-agent-deps.js +0 -13
- package/lib/src/github-agent-deps.js.map +0 -1
- package/lib/src/github-bot-handlers.js.map +0 -1
- package/lib/src/github-channel-context.js.map +0 -1
- package/lib/src/github-tool-handlers.js.map +0 -1
- package/lib/src/index.js +0 -104
- package/lib/src/index.js.map +0 -1
- package/lib/src/register-github-mcp.js +0 -41
- package/lib/src/register-github-mcp.js.map +0 -1
- package/lib/src/segment-mapper.js +0 -2
- package/lib/src/segment-mapper.js.map +0 -1
- package/lib/src/types.js.map +0 -1
- package/lib/src/workspace-manager.js.map +0 -1
- package/plugin.yml +0 -3
- package/src/adapter.ts +0 -454
- package/src/register-github-mcp.ts +0 -67
- package/src/segment-mapper.ts +0 -1
package/lib/webhook.js
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { formatCompact, getLogger } from '@zhin.js/logger';
|
|
2
|
+
import { headerValue, parseIssueCommentInbound, parsePrReviewCommentInbound, parsePrReviewInbound, readTextBody, verifyWebhookSignature, } from './protocol.js';
|
|
3
|
+
const logger = getLogger('github');
|
|
4
|
+
export function registerGithubWebhookRoutes(http, handler) {
|
|
5
|
+
const routePath = handler.config.webhookPath;
|
|
6
|
+
return [
|
|
7
|
+
http.route('POST', routePath, async (request, response) => {
|
|
8
|
+
await handleGithubWebhookRequest(request, response, handler);
|
|
9
|
+
}, { summary: 'GitHub webhook', tags: ['github'] }),
|
|
10
|
+
];
|
|
11
|
+
}
|
|
12
|
+
export async function handleGithubWebhookRequest(request, response, handler) {
|
|
13
|
+
const secret = handler.config.webhookSecret;
|
|
14
|
+
if (!secret) {
|
|
15
|
+
response.writeHead(503, { 'Content-Type': 'application/json' });
|
|
16
|
+
response.end(JSON.stringify({ error: 'Webhook not configured' }));
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
const signature = headerValue(request.headers, 'x-hub-signature-256');
|
|
20
|
+
const event = headerValue(request.headers, 'x-github-event');
|
|
21
|
+
const deliveryId = headerValue(request.headers, 'x-github-delivery');
|
|
22
|
+
if (!signature || !event) {
|
|
23
|
+
response.writeHead(400, { 'Content-Type': 'application/json' });
|
|
24
|
+
response.end(JSON.stringify({ error: 'Missing signature or event header' }));
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
try {
|
|
28
|
+
const rawBody = await readTextBody(request);
|
|
29
|
+
if (!verifyWebhookSignature(secret, rawBody, signature)) {
|
|
30
|
+
logger.warn(formatCompact({
|
|
31
|
+
op: 'webhook',
|
|
32
|
+
ok: false,
|
|
33
|
+
error: 'invalid signature',
|
|
34
|
+
delivery: deliveryId,
|
|
35
|
+
}));
|
|
36
|
+
response.writeHead(401, { 'Content-Type': 'application/json' });
|
|
37
|
+
response.end(JSON.stringify({ error: 'Invalid signature' }));
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
let payload;
|
|
41
|
+
try {
|
|
42
|
+
payload = rawBody ? JSON.parse(rawBody) : {};
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
response.writeHead(400, { 'Content-Type': 'application/json' });
|
|
46
|
+
response.end(JSON.stringify({ error: 'Invalid JSON' }));
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
response.writeHead(200, { 'Content-Type': 'application/json' });
|
|
50
|
+
response.end(JSON.stringify({ ok: true }));
|
|
51
|
+
void dispatchGithubWebhookPayload(event, payload, handler).catch((e) => {
|
|
52
|
+
logger.error(`Webhook 事件处理失败 (${event}):`, e);
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
catch (error) {
|
|
56
|
+
logger.error('Webhook error:', error);
|
|
57
|
+
if (!response.headersSent) {
|
|
58
|
+
response.writeHead(500, { 'Content-Type': 'application/json' });
|
|
59
|
+
response.end(JSON.stringify({ error: 'Internal Server Error' }));
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
export async function dispatchGithubWebhookPayload(event, payload, handler) {
|
|
64
|
+
if (!payload || typeof payload !== 'object')
|
|
65
|
+
return;
|
|
66
|
+
const body = payload;
|
|
67
|
+
const repo = body.repository?.full_name;
|
|
68
|
+
logger.debug(`Webhook: ${event}${body.action ? `.${body.action}` : ''} ${repo || ''}`);
|
|
69
|
+
if (event === 'issue_comment') {
|
|
70
|
+
const inbound = parseIssueCommentInbound(payload);
|
|
71
|
+
if (inbound)
|
|
72
|
+
handler.admit(inbound);
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
if (event === 'pull_request_review_comment') {
|
|
76
|
+
const inbound = parsePrReviewCommentInbound(payload);
|
|
77
|
+
if (inbound)
|
|
78
|
+
handler.admit(inbound);
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
if (event === 'pull_request_review') {
|
|
82
|
+
const inbound = parsePrReviewInbound(payload);
|
|
83
|
+
if (inbound)
|
|
84
|
+
handler.admit(inbound);
|
|
85
|
+
}
|
|
86
|
+
// Cross-adapter subscription fan-out deferred (needs multi-adapter send in Runtime).
|
|
87
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { GhClient, GitHubBotIdentity } from './gh-client.js';
|
|
2
|
+
/**
|
|
3
|
+
* 校验 GitHub "owner/name" 全名,防止路径穿越与 git 选项注入。
|
|
4
|
+
* 返回通过校验的原值,便于调用方直接使用「已消毒」的变量。
|
|
5
|
+
*/
|
|
6
|
+
export declare function assertRepoFullName(repo: string): string;
|
|
7
|
+
/**
|
|
8
|
+
* 校验 git ref 名称,拒绝选项注入(`-` 前缀)与 git 非法字符。
|
|
9
|
+
*/
|
|
10
|
+
export declare function assertGitRefName(ref: string): string;
|
|
11
|
+
export declare class WorkspaceManager {
|
|
12
|
+
private readonly gh;
|
|
13
|
+
private readonly rootDir;
|
|
14
|
+
constructor(gh: GhClient, rootDir: string);
|
|
15
|
+
getRepoPath(repo: string): string;
|
|
16
|
+
private runGit;
|
|
17
|
+
configureBotGit(repoPath: string, identity: GitHubBotIdentity): Promise<void>;
|
|
18
|
+
ensureRepo(repo: string): Promise<string>;
|
|
19
|
+
checkoutBranch(repo: string, branch: string, baseRef: string): Promise<string>;
|
|
20
|
+
commitAndPush(repo: string, branch: string, message: string): Promise<string>;
|
|
21
|
+
}
|
|
@@ -4,6 +4,45 @@
|
|
|
4
4
|
import { spawn } from 'node:child_process';
|
|
5
5
|
import fs from 'node:fs';
|
|
6
6
|
import path from 'node:path';
|
|
7
|
+
const REPO_FULL_NAME_RE = /^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/;
|
|
8
|
+
const GIT_REF_ILLEGAL_CHARS = new Set('~^:?*[\\`;');
|
|
9
|
+
function containsIllegalGitRefCharacter(ref) {
|
|
10
|
+
return [...ref].some((character) => {
|
|
11
|
+
const code = character.charCodeAt(0);
|
|
12
|
+
return code <= 31
|
|
13
|
+
|| code === 127
|
|
14
|
+
|| /\s/u.test(character)
|
|
15
|
+
|| GIT_REF_ILLEGAL_CHARS.has(character);
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* 校验 GitHub "owner/name" 全名,防止路径穿越与 git 选项注入。
|
|
20
|
+
* 返回通过校验的原值,便于调用方直接使用「已消毒」的变量。
|
|
21
|
+
*/
|
|
22
|
+
export function assertRepoFullName(repo) {
|
|
23
|
+
if (typeof repo !== 'string' ||
|
|
24
|
+
!REPO_FULL_NAME_RE.test(repo) ||
|
|
25
|
+
repo.split('/').some((seg) => seg === '' || seg.startsWith('-') || /^\.+$/.test(seg))) {
|
|
26
|
+
throw new TypeError(`非法的仓库全名: ${JSON.stringify(repo)}`);
|
|
27
|
+
}
|
|
28
|
+
return repo;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* 校验 git ref 名称,拒绝选项注入(`-` 前缀)与 git 非法字符。
|
|
32
|
+
*/
|
|
33
|
+
export function assertGitRefName(ref) {
|
|
34
|
+
if (typeof ref !== 'string' || ref.length === 0) {
|
|
35
|
+
throw new TypeError(`非法的 git ref: ${JSON.stringify(ref)}`);
|
|
36
|
+
}
|
|
37
|
+
if (ref.startsWith('-') ||
|
|
38
|
+
ref.includes('..') ||
|
|
39
|
+
containsIllegalGitRefCharacter(ref) ||
|
|
40
|
+
ref.endsWith('/') ||
|
|
41
|
+
ref.endsWith('.')) {
|
|
42
|
+
throw new TypeError(`非法的 git ref: ${JSON.stringify(ref)}`);
|
|
43
|
+
}
|
|
44
|
+
return ref;
|
|
45
|
+
}
|
|
7
46
|
export class WorkspaceManager {
|
|
8
47
|
gh;
|
|
9
48
|
rootDir;
|
|
@@ -12,6 +51,7 @@ export class WorkspaceManager {
|
|
|
12
51
|
this.rootDir = rootDir;
|
|
13
52
|
}
|
|
14
53
|
getRepoPath(repo) {
|
|
54
|
+
repo = assertRepoFullName(repo);
|
|
15
55
|
const [owner, name] = repo.split('/');
|
|
16
56
|
return path.join(this.rootDir, owner, name);
|
|
17
57
|
}
|
|
@@ -47,6 +87,7 @@ export class WorkspaceManager {
|
|
|
47
87
|
]).then(() => undefined);
|
|
48
88
|
}
|
|
49
89
|
async ensureRepo(repo) {
|
|
90
|
+
repo = assertRepoFullName(repo);
|
|
50
91
|
if (!this.gh.isAppAuth) {
|
|
51
92
|
throw new Error('GitHub App 认证未配置,无法托管工作区');
|
|
52
93
|
}
|
|
@@ -70,6 +111,9 @@ export class WorkspaceManager {
|
|
|
70
111
|
return repoPath;
|
|
71
112
|
}
|
|
72
113
|
async checkoutBranch(repo, branch, baseRef) {
|
|
114
|
+
repo = assertRepoFullName(repo);
|
|
115
|
+
branch = assertGitRefName(branch);
|
|
116
|
+
baseRef = assertGitRefName(baseRef);
|
|
73
117
|
const repoPath = await this.ensureRepo(repo);
|
|
74
118
|
const localBranches = await this.runGit(repoPath, ['branch', '--list', branch]);
|
|
75
119
|
if (localBranches.trim()) {
|
|
@@ -89,6 +133,8 @@ export class WorkspaceManager {
|
|
|
89
133
|
return repoPath;
|
|
90
134
|
}
|
|
91
135
|
async commitAndPush(repo, branch, message) {
|
|
136
|
+
repo = assertRepoFullName(repo);
|
|
137
|
+
branch = assertGitRefName(branch);
|
|
92
138
|
const repoPath = this.getRepoPath(repo);
|
|
93
139
|
if (!fs.existsSync(repoPath)) {
|
|
94
140
|
throw new Error(`工作区不存在: ${repoPath},请先 github_prepare_workspace`);
|
|
@@ -106,4 +152,3 @@ export class WorkspaceManager {
|
|
|
106
152
|
return `已 push 到 origin/${branch}`;
|
|
107
153
|
}
|
|
108
154
|
}
|
|
109
|
-
//# sourceMappingURL=workspace-manager.js.map
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zhin.js/adapter-github",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "Zhin.js adapter for
|
|
3
|
+
"version": "4.0.0",
|
|
4
|
+
"description": "Zhin.js GitHub adapter for Plugin Runtime (App auth + webhook)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./lib/index.js",
|
|
7
7
|
"types": "./lib/index.d.ts",
|
|
@@ -18,15 +18,6 @@
|
|
|
18
18
|
},
|
|
19
19
|
"./package.json": "./package.json"
|
|
20
20
|
},
|
|
21
|
-
"files": [
|
|
22
|
-
"src",
|
|
23
|
-
"lib",
|
|
24
|
-
"agent",
|
|
25
|
-
"client",
|
|
26
|
-
"dist",
|
|
27
|
-
"plugin.yml",
|
|
28
|
-
"README.md"
|
|
29
|
-
],
|
|
30
21
|
"keywords": [
|
|
31
22
|
"zhin",
|
|
32
23
|
"zhin.js",
|
|
@@ -44,21 +35,29 @@
|
|
|
44
35
|
"url": "https://github.com/lc-cn"
|
|
45
36
|
},
|
|
46
37
|
"license": "MIT",
|
|
47
|
-
"
|
|
48
|
-
"
|
|
49
|
-
"
|
|
50
|
-
"
|
|
51
|
-
|
|
52
|
-
|
|
38
|
+
"repository": {
|
|
39
|
+
"type": "git",
|
|
40
|
+
"url": "git+https://github.com/zhinjs/zhin.git",
|
|
41
|
+
"directory": "plugins/adapters/github"
|
|
42
|
+
},
|
|
43
|
+
"dependencies": {
|
|
44
|
+
"@zhin.js/adapter": "1.1.0",
|
|
45
|
+
"@zhin.js/core": "1.4.0",
|
|
46
|
+
"@zhin.js/host-http": "1.0.2",
|
|
47
|
+
"@zhin.js/logger": "1.0.75",
|
|
48
|
+
"@zhin.js/plugin-runtime": "1.1.0"
|
|
53
49
|
},
|
|
54
50
|
"peerDependencies": {
|
|
55
51
|
"zod": "^4.0.0",
|
|
56
|
-
"@zhin.js/
|
|
57
|
-
"zhin.js": "
|
|
58
|
-
"@zhin.js/
|
|
52
|
+
"@zhin.js/adapter": "1.1.0",
|
|
53
|
+
"@zhin.js/agent": "1.0.5",
|
|
54
|
+
"@zhin.js/core": "1.4.0",
|
|
55
|
+
"@zhin.js/host-http": "1.0.2",
|
|
56
|
+
"@zhin.js/plugin-runtime": "1.1.0",
|
|
57
|
+
"zhin.js": "5.0.0"
|
|
59
58
|
},
|
|
60
59
|
"peerDependenciesMeta": {
|
|
61
|
-
"
|
|
60
|
+
"zhin.js": {
|
|
62
61
|
"optional": true
|
|
63
62
|
},
|
|
64
63
|
"@zhin.js/agent": {
|
|
@@ -68,11 +67,24 @@
|
|
|
68
67
|
"optional": true
|
|
69
68
|
}
|
|
70
69
|
},
|
|
71
|
-
"
|
|
72
|
-
"
|
|
73
|
-
"
|
|
74
|
-
"
|
|
70
|
+
"devDependencies": {
|
|
71
|
+
"@types/node": "^26.1.0",
|
|
72
|
+
"typescript": "^6.0.3",
|
|
73
|
+
"vitest": "^4.1.10",
|
|
74
|
+
"zod": "^4.4.3",
|
|
75
|
+
"@zhin.js/agent": "1.0.5",
|
|
76
|
+
"zhin.js": "5.0.0"
|
|
75
77
|
},
|
|
78
|
+
"files": [
|
|
79
|
+
"adapters",
|
|
80
|
+
"plugin.ts",
|
|
81
|
+
"schema.json",
|
|
82
|
+
"src",
|
|
83
|
+
"lib",
|
|
84
|
+
"agent",
|
|
85
|
+
"README.md",
|
|
86
|
+
"CHANGELOG.md"
|
|
87
|
+
],
|
|
76
88
|
"publishConfig": {
|
|
77
89
|
"access": "public",
|
|
78
90
|
"registry": "https://registry.npmjs.org"
|
|
@@ -80,8 +92,23 @@
|
|
|
80
92
|
"engines": {
|
|
81
93
|
"node": "^20.19.0 || >=22.12.0"
|
|
82
94
|
},
|
|
95
|
+
"zhin": {
|
|
96
|
+
"protocol": 1,
|
|
97
|
+
"type": "plugin",
|
|
98
|
+
"entry": "./plugin.ts",
|
|
99
|
+
"engine": "^1.0.0",
|
|
100
|
+
"runtime": "trusted",
|
|
101
|
+
"features": [
|
|
102
|
+
{
|
|
103
|
+
"package": "@zhin.js/adapter",
|
|
104
|
+
"api": "^1.0.0"
|
|
105
|
+
}
|
|
106
|
+
],
|
|
107
|
+
"plugins": []
|
|
108
|
+
},
|
|
83
109
|
"scripts": {
|
|
84
110
|
"build": "tsc",
|
|
85
|
-
"clean": "rimraf lib"
|
|
111
|
+
"clean": "rimraf lib",
|
|
112
|
+
"test": "NODE_OPTIONS=--experimental-strip-types vitest run --root ../../.. plugins/adapters/github/tests"
|
|
86
113
|
}
|
|
87
114
|
}
|
package/plugin.ts
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { definePlugin, databaseHostToken } from '@zhin.js/plugin-runtime';
|
|
2
|
+
import { defineGithubOauthUsersTable } from './src/oauth-users.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* github_subscriptions — repo event subscriptions per chat channel
|
|
6
|
+
* (used by github_subscriptions agent tool; schema matches legacy defineModel).
|
|
7
|
+
*/
|
|
8
|
+
const GITHUB_SUBSCRIPTIONS_SCHEMA = {
|
|
9
|
+
id: { type: 'integer', primary: true },
|
|
10
|
+
repo: { type: 'text', nullable: false },
|
|
11
|
+
events: { type: 'json', default: [] },
|
|
12
|
+
target_id: { type: 'text', nullable: false },
|
|
13
|
+
target_type: { type: 'text', nullable: false },
|
|
14
|
+
adapter: { type: 'text', nullable: false },
|
|
15
|
+
endpoint: { type: 'text', nullable: false },
|
|
16
|
+
} as const;
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Plugin Runtime GitHub adapter.
|
|
20
|
+
* - Endpoint: `adapters/github.ts`
|
|
21
|
+
* - OAuth user tokens: define `github_oauth_users` when DatabaseHost is present
|
|
22
|
+
*/
|
|
23
|
+
export default definePlugin({
|
|
24
|
+
name: 'github',
|
|
25
|
+
metadata: {
|
|
26
|
+
displayName: 'GitHub Adapter',
|
|
27
|
+
},
|
|
28
|
+
setup(context) {
|
|
29
|
+
if (context.resources.has(databaseHostToken)) {
|
|
30
|
+
const host = context.resources.use(databaseHostToken);
|
|
31
|
+
defineGithubOauthUsersTable(host);
|
|
32
|
+
host.define('github_subscriptions', { ...GITHUB_SUBSCRIPTIONS_SCHEMA });
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Agent prompt contributor (orchestrator/deferred-worker GitHub guidance).
|
|
36
|
+
// `zhin.js/agent` is an optional peer — skip silently on IM-only installs.
|
|
37
|
+
let cancelled = false;
|
|
38
|
+
let unregister: (() => void) | undefined;
|
|
39
|
+
void Promise.all([
|
|
40
|
+
import('zhin.js/agent'),
|
|
41
|
+
import('./src/agent-prompt.js'),
|
|
42
|
+
]).then(([agent, prompt]) => {
|
|
43
|
+
if (cancelled) return;
|
|
44
|
+
agent.registerAgentPromptContributor(prompt.createGithubAgentPromptContributor());
|
|
45
|
+
unregister = () => agent.unregisterAgentPromptContributor('github');
|
|
46
|
+
}).catch(() => { /* optional peer not installed */ });
|
|
47
|
+
return () => {
|
|
48
|
+
cancelled = true;
|
|
49
|
+
unregister?.();
|
|
50
|
+
};
|
|
51
|
+
},
|
|
52
|
+
});
|
package/schema.json
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"type": "object",
|
|
4
|
+
"additionalProperties": false,
|
|
5
|
+
"properties": {
|
|
6
|
+
"host": {
|
|
7
|
+
"type": "string",
|
|
8
|
+
"description": "GitHub Enterprise hostname (default github.com)"
|
|
9
|
+
},
|
|
10
|
+
"webhook_path": {
|
|
11
|
+
"type": "string",
|
|
12
|
+
"default": "/github/webhook"
|
|
13
|
+
},
|
|
14
|
+
"webhookPath": {
|
|
15
|
+
"type": "string",
|
|
16
|
+
"default": "/github/webhook"
|
|
17
|
+
},
|
|
18
|
+
"poll_interval": {
|
|
19
|
+
"type": "number",
|
|
20
|
+
"default": 60,
|
|
21
|
+
"description": "Deferred: polling fallback was removed in the Plugin Runtime migration; currently parsed but unused"
|
|
22
|
+
},
|
|
23
|
+
"auto_reply_repos": {
|
|
24
|
+
"type": "array",
|
|
25
|
+
"items": {
|
|
26
|
+
"type": "string"
|
|
27
|
+
},
|
|
28
|
+
"description": "Repos whose Issue/PR comments auto-trigger AI without @bot"
|
|
29
|
+
},
|
|
30
|
+
"bot_login": {
|
|
31
|
+
"type": "string",
|
|
32
|
+
"description": "Override App bot login (default {slug}[bot])"
|
|
33
|
+
},
|
|
34
|
+
"workspace_root": {
|
|
35
|
+
"type": "string",
|
|
36
|
+
"description": "Managed git workspace root"
|
|
37
|
+
},
|
|
38
|
+
"endpoints": {
|
|
39
|
+
"type": "array",
|
|
40
|
+
"description": "多账号:一个插件实例挂多个 endpoint",
|
|
41
|
+
"items": {
|
|
42
|
+
"type": "object",
|
|
43
|
+
"additionalProperties": true,
|
|
44
|
+
"properties": {
|
|
45
|
+
"name": {
|
|
46
|
+
"type": "string",
|
|
47
|
+
"description": "GitHub App bot name"
|
|
48
|
+
},
|
|
49
|
+
"app_id": {
|
|
50
|
+
"type": [
|
|
51
|
+
"string",
|
|
52
|
+
"number"
|
|
53
|
+
],
|
|
54
|
+
"description": "GitHub App ID"
|
|
55
|
+
},
|
|
56
|
+
"appId": {
|
|
57
|
+
"type": [
|
|
58
|
+
"string",
|
|
59
|
+
"number"
|
|
60
|
+
],
|
|
61
|
+
"description": "GitHub App ID (camelCase alias)"
|
|
62
|
+
},
|
|
63
|
+
"private_key": {
|
|
64
|
+
"type": "string",
|
|
65
|
+
"description": "GitHub App private key (PEM content or file path)"
|
|
66
|
+
},
|
|
67
|
+
"privateKey": {
|
|
68
|
+
"type": "string",
|
|
69
|
+
"description": "GitHub App private key (camelCase alias)"
|
|
70
|
+
},
|
|
71
|
+
"webhook_secret": {
|
|
72
|
+
"type": "string",
|
|
73
|
+
"description": "Webhook HMAC secret; enables httpHostToken POST route"
|
|
74
|
+
},
|
|
75
|
+
"webhookSecret": {
|
|
76
|
+
"type": "string",
|
|
77
|
+
"description": "Webhook HMAC secret (camelCase alias)"
|
|
78
|
+
}
|
|
79
|
+
},
|
|
80
|
+
"required": [
|
|
81
|
+
"name"
|
|
82
|
+
]
|
|
83
|
+
}
|
|
84
|
+
},
|
|
85
|
+
"commandPrefix": {
|
|
86
|
+
"type": "string",
|
|
87
|
+
"default": "",
|
|
88
|
+
"description": "命令前缀(默认 '' 无前缀,任意文本按命令匹配;如 '/' 要求 / 开头)。endpoints[i] 可逐项覆盖"
|
|
89
|
+
}
|
|
90
|
+
},
|
|
91
|
+
"required": [
|
|
92
|
+
"endpoints"
|
|
93
|
+
]
|
|
94
|
+
}
|