@zhin.js/adapter-github 5.0.14 → 6.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/CHANGELOG.md +39 -0
- package/README.md +18 -2
- package/adapters/github.js +0 -7
- package/adapters/github.ts +0 -7
- package/agent/prompt-sections/platform.ts +16 -0
- package/agent/tools/bind.ts +4 -3
- package/agent/tools/create_pr.ts +3 -2
- package/agent/tools/install.ts +4 -3
- package/agent/tools/patch_file.ts +3 -2
- package/agent/tools/prepare_workspace.ts +3 -2
- package/agent/tools/push_branch.ts +3 -2
- package/agent/tools/star.ts +3 -2
- package/agent/tools/subscribe.ts +3 -3
- package/agent/tools/subscriptions.ts +4 -4
- package/agent/tools/unbind.ts +4 -3
- package/agent/tools/unsubscribe.ts +3 -3
- package/agent/tools/whoami.ts +4 -3
- package/lib/client.d.ts +36 -0
- package/lib/client.js +47 -0
- package/lib/endpoint.d.ts +10 -24
- package/lib/endpoint.js +35 -65
- package/lib/github-bot-handlers.d.ts +5 -4
- package/lib/github-bot-handlers.js +12 -13
- package/lib/github-endpoint-commands.d.ts +1 -1
- package/lib/github-tool-handlers.d.ts +9 -8
- package/lib/github-tool-handlers.js +26 -33
- package/lib/index.d.ts +1 -1
- package/lib/index.js +1 -1
- package/lib/protocol.d.ts +1 -1
- package/lib/protocol.js +1 -1
- package/lib/webhook.d.ts +1 -0
- package/lib/webhook.js +1 -0
- package/package.json +19 -13
- package/plugin.js +0 -17
- package/src/client.ts +65 -0
- package/src/endpoint.ts +36 -75
- package/src/github-bot-handlers.ts +13 -9
- package/src/github-tool-handlers.ts +27 -32
- package/src/index.ts +1 -9
- package/src/protocol.ts +1 -1
- package/src/webhook.ts +5 -0
- package/lib/agent-prompt.d.ts +0 -2
- package/lib/agent-prompt.js +0 -84
- package/lib/github-agent-deps.d.ts +0 -47
- package/lib/github-agent-deps.js +0 -43
- package/src/agent-prompt.ts +0 -101
- package/src/github-agent-deps.ts +0 -82
package/lib/endpoint.js
CHANGED
|
@@ -1,96 +1,55 @@
|
|
|
1
|
-
|
|
2
|
-
* GithubEndpoint — lifecycle, outbound send, inbound admit.
|
|
3
|
-
*/
|
|
4
|
-
import path from 'node:path';
|
|
1
|
+
import { Endpoint } from 'zhin.js/adapter';
|
|
5
2
|
import { formatCompact, getAdapterLogger } from '@zhin.js/logger';
|
|
6
3
|
import { GhClient } from './gh-client.js';
|
|
7
|
-
import { registerGithubAgentEndpoint } from './github-agent-deps.js';
|
|
8
|
-
import { lookupGithubOauthAccessToken } from './oauth-users.js';
|
|
9
4
|
import { enrichInboundContent, formatInboundContent, formatOutboundBody, githubInboundConversation, parseChannelId, } from './protocol.js';
|
|
10
5
|
import { registerGithubWebhookRoutes } from './webhook.js';
|
|
11
|
-
import {
|
|
6
|
+
import { GithubClient } from './client.js';
|
|
12
7
|
/**
|
|
13
8
|
* GitHub 是代码协作面(issue/PR),无好友/群/频道等 IM 社交概念,
|
|
14
9
|
* 不适用 EndpointManagement 语义端口;本 endpoint 不暴露该端口。
|
|
15
10
|
*/
|
|
16
|
-
export class GithubEndpoint {
|
|
11
|
+
export class GithubEndpoint extends Endpoint {
|
|
17
12
|
#logger;
|
|
18
13
|
#options;
|
|
19
|
-
|
|
20
|
-
config;
|
|
21
|
-
name;
|
|
22
|
-
#workspaceManager = null;
|
|
14
|
+
client;
|
|
23
15
|
#routeReleases = [];
|
|
24
16
|
#open = false;
|
|
25
17
|
#started = false;
|
|
26
|
-
#unregisterAgent;
|
|
27
18
|
constructor(options) {
|
|
19
|
+
super();
|
|
28
20
|
this.#logger = getAdapterLogger('github', options.config.id);
|
|
29
21
|
this.#options = options;
|
|
30
|
-
|
|
31
|
-
this.
|
|
32
|
-
this.gh = options.createClient?.(options.config) ?? defaultCreateClient(options.config);
|
|
33
|
-
}
|
|
34
|
-
getAPI() {
|
|
35
|
-
return this.gh;
|
|
36
|
-
}
|
|
37
|
-
async getUserOrDefaultAPI(platform, platformUid) {
|
|
38
|
-
if (platform && platformUid) {
|
|
39
|
-
const token = await lookupGithubOauthAccessToken(this.#options.database, platform, platformUid);
|
|
40
|
-
if (token)
|
|
41
|
-
return this.gh.withToken(token);
|
|
42
|
-
}
|
|
43
|
-
return this.gh;
|
|
44
|
-
}
|
|
45
|
-
getClientId() {
|
|
46
|
-
return this.gh.clientId || null;
|
|
47
|
-
}
|
|
48
|
-
getHost() {
|
|
49
|
-
return this.config.host;
|
|
50
|
-
}
|
|
51
|
-
getAppSlug() {
|
|
52
|
-
return this.gh.appSlug || null;
|
|
22
|
+
const api = options.createClient?.(options.config) ?? defaultCreateClient(options.config);
|
|
23
|
+
this.client = new GithubClient(options.config.id, api, options.config, options.database);
|
|
53
24
|
}
|
|
54
|
-
|
|
55
|
-
return this.
|
|
56
|
-
}
|
|
57
|
-
getWorkspaceManager() {
|
|
58
|
-
if (this.#workspaceManager)
|
|
59
|
-
return this.#workspaceManager;
|
|
60
|
-
const workspaceRoot = this.config.workspaceRoot
|
|
61
|
-
?? path.join(process.cwd(), 'data', 'github-workspaces');
|
|
62
|
-
this.#workspaceManager = new WorkspaceManager(this.gh, workspaceRoot);
|
|
63
|
-
return this.#workspaceManager;
|
|
64
|
-
}
|
|
65
|
-
getDatabase() {
|
|
66
|
-
return this.#options.database;
|
|
25
|
+
get config() {
|
|
26
|
+
return this.client.config;
|
|
67
27
|
}
|
|
68
28
|
async start() {
|
|
69
29
|
if (this.#started)
|
|
70
30
|
return;
|
|
71
31
|
this.#started = true;
|
|
72
32
|
try {
|
|
73
|
-
const result = await this.
|
|
33
|
+
const result = await this.client.api.verifyAuth();
|
|
74
34
|
if (!result.ok)
|
|
75
35
|
throw new Error(`GitHub 认证失败: ${result.message}`);
|
|
76
|
-
|
|
77
|
-
if (this.config.webhookSecret) {
|
|
36
|
+
if (this.client.config.webhookSecret) {
|
|
78
37
|
if (!this.#options.http) {
|
|
79
38
|
throw new TypeError('GitHub webhook_secret requires httpHostToken');
|
|
80
39
|
}
|
|
81
40
|
this.#routeReleases.push(...registerGithubWebhookRoutes(this.#options.http, this));
|
|
82
41
|
this.#logger.debug(formatCompact({
|
|
83
|
-
endpoint: this.name,
|
|
42
|
+
endpoint: this.client.name,
|
|
84
43
|
op: 'webhook',
|
|
85
|
-
path: this.config.webhookPath,
|
|
44
|
+
path: this.client.config.webhookPath,
|
|
86
45
|
}));
|
|
87
46
|
}
|
|
88
47
|
else {
|
|
89
48
|
this.#logger.debug(formatCompact({
|
|
90
|
-
endpoint: this.name,
|
|
49
|
+
endpoint: this.client.name,
|
|
91
50
|
op: 'connect',
|
|
92
51
|
mode: 'api-only',
|
|
93
|
-
bot: this.
|
|
52
|
+
bot: this.client.api.authenticatedUser,
|
|
94
53
|
}));
|
|
95
54
|
}
|
|
96
55
|
}
|
|
@@ -110,8 +69,6 @@ export class GithubEndpoint {
|
|
|
110
69
|
this.#open = false;
|
|
111
70
|
for (const release of this.#routeReleases.splice(0))
|
|
112
71
|
release();
|
|
113
|
-
this.#unregisterAgent?.();
|
|
114
|
-
this.#unregisterAgent = undefined;
|
|
115
72
|
this.#started = false;
|
|
116
73
|
this.#logger.debug(formatCompact({ op: 'disconnect' }));
|
|
117
74
|
}
|
|
@@ -121,13 +78,13 @@ export class GithubEndpoint {
|
|
|
121
78
|
throw new Error(`无效的 GitHub conversation ID: ${conversation.id}`);
|
|
122
79
|
const text = formatOutboundBody(payload);
|
|
123
80
|
const r = parsed.type === 'issue'
|
|
124
|
-
? await this.
|
|
125
|
-
: await this.
|
|
81
|
+
? await this.client.api.createIssueComment(parsed.repo, parsed.number, text)
|
|
82
|
+
: await this.client.api.createPRComment(parsed.repo, parsed.number, text);
|
|
126
83
|
if (!r.ok)
|
|
127
84
|
throw new Error(`发送失败: ${JSON.stringify(r.data)}`);
|
|
128
85
|
this.#logger.debug(formatCompact({
|
|
129
86
|
op: 'github_send',
|
|
130
|
-
endpoint: this.name,
|
|
87
|
+
endpoint: this.client.name,
|
|
131
88
|
target: `${conversation.kind}:${conversation.id}`,
|
|
132
89
|
messageId: r.data.id,
|
|
133
90
|
}));
|
|
@@ -137,17 +94,19 @@ export class GithubEndpoint {
|
|
|
137
94
|
admit(comment) {
|
|
138
95
|
if (!this.#open)
|
|
139
96
|
return;
|
|
140
|
-
const botUser = this.config.botLogin
|
|
97
|
+
const botUser = this.client.config.botLogin
|
|
98
|
+
|| this.client.api.getBotLogin()
|
|
99
|
+
|| this.client.api.authenticatedUser;
|
|
141
100
|
if (botUser && comment.sender === botUser)
|
|
142
101
|
return;
|
|
143
102
|
const conversation = githubInboundConversation(String(this.#options.id), comment);
|
|
144
|
-
const content = enrichInboundContent(formatInboundContent(comment.content), this.config, botUser ?? undefined, comment.repo);
|
|
145
|
-
void this
|
|
103
|
+
const content = enrichInboundContent(formatInboundContent(comment.content), this.client.config, botUser ?? undefined, comment.repo);
|
|
104
|
+
void this.emit('message.receive', {
|
|
146
105
|
conversation,
|
|
147
106
|
message: { conversation, id: comment.id },
|
|
148
107
|
content,
|
|
149
108
|
sender: { id: comment.sender, name: comment.sender },
|
|
150
|
-
endpointId: this.name,
|
|
109
|
+
endpointId: this.client.name,
|
|
151
110
|
metadata: Object.freeze({
|
|
152
111
|
repo: comment.repo,
|
|
153
112
|
kind: comment.kind,
|
|
@@ -161,6 +120,17 @@ export class GithubEndpoint {
|
|
|
161
120
|
}));
|
|
162
121
|
});
|
|
163
122
|
}
|
|
123
|
+
admitPlatform(name, event) {
|
|
124
|
+
if (!this.#open)
|
|
125
|
+
return;
|
|
126
|
+
void this.emitPlatform(name, event).catch((error) => {
|
|
127
|
+
this.#logger.warn(formatCompact({
|
|
128
|
+
op: 'github_platform_event_failed',
|
|
129
|
+
event: name,
|
|
130
|
+
error: error instanceof Error ? error.message : String(error),
|
|
131
|
+
}));
|
|
132
|
+
});
|
|
133
|
+
}
|
|
164
134
|
}
|
|
165
135
|
export function defaultCreateClient(config) {
|
|
166
136
|
const appAuth = config.appId && config.privateKey
|
|
@@ -2,25 +2,26 @@
|
|
|
2
2
|
* GitHub App Bot workflow handlers (Installation Token identity).
|
|
3
3
|
*/
|
|
4
4
|
import type { Message } from 'zhin.js';
|
|
5
|
+
import type { GithubClient } from './client.js';
|
|
5
6
|
export declare function executeGithubPrepareWorkspace(args: {
|
|
6
7
|
repo?: string;
|
|
7
|
-
}, commMessage?: Message): Promise<string>;
|
|
8
|
+
}, client: GithubClient, commMessage?: Message): Promise<string>;
|
|
8
9
|
export declare function executeGithubPatchFile(args: {
|
|
9
10
|
repo?: string;
|
|
10
11
|
path: string;
|
|
11
12
|
content: string;
|
|
12
13
|
message: string;
|
|
13
14
|
branch?: string;
|
|
14
|
-
}, commMessage?: Message): Promise<string>;
|
|
15
|
+
}, client: GithubClient, commMessage?: Message): Promise<string>;
|
|
15
16
|
export declare function executeGithubPushBranch(args: {
|
|
16
17
|
repo?: string;
|
|
17
18
|
branch?: string;
|
|
18
19
|
message: string;
|
|
19
|
-
}, commMessage?: Message): Promise<string>;
|
|
20
|
+
}, client: GithubClient, commMessage?: Message): Promise<string>;
|
|
20
21
|
export declare function executeGithubCreatePr(args: {
|
|
21
22
|
repo?: string;
|
|
22
23
|
title: string;
|
|
23
24
|
body?: string;
|
|
24
25
|
head?: string;
|
|
25
26
|
base?: string;
|
|
26
|
-
}, commMessage?: Message): Promise<string>;
|
|
27
|
+
}, client: GithubClient, commMessage?: Message): Promise<string>;
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { getCurrentCommMessage } from '@zhin.js/agent/security';
|
|
2
|
-
import { getAdapter, getGithubAgentDeps } from './github-agent-deps.js';
|
|
3
2
|
import { parseMessageChannel, resolveWorkspaceBranch, formatChannelContext, } from './github-channel-context.js';
|
|
4
|
-
function requireBotGh() {
|
|
5
|
-
const gh =
|
|
3
|
+
function requireBotGh(client) {
|
|
4
|
+
const gh = client.api;
|
|
6
5
|
if (!gh?.isAppAuth) {
|
|
7
6
|
throw new Error('需要 GitHub App 认证(app_id + private_key),Bot 写操作不可用');
|
|
8
7
|
}
|
|
@@ -23,11 +22,11 @@ function resolveChannel(msg, repo) {
|
|
|
23
22
|
}
|
|
24
23
|
throw new Error('无法解析 GitHub 频道(需要 Issue/PR 评论上下文)');
|
|
25
24
|
}
|
|
26
|
-
export async function executeGithubPrepareWorkspace(args, commMessage) {
|
|
25
|
+
export async function executeGithubPrepareWorkspace(args, client, commMessage) {
|
|
27
26
|
const msg = resolveCommMessage(commMessage);
|
|
28
27
|
const ctx = resolveChannel(msg, args.repo);
|
|
29
|
-
const gh = requireBotGh();
|
|
30
|
-
const wm =
|
|
28
|
+
const gh = requireBotGh(client);
|
|
29
|
+
const wm = client.workspaceManager;
|
|
31
30
|
const { branch, base } = await resolveWorkspaceBranch(gh, ctx);
|
|
32
31
|
const repoPath = await wm.checkoutBranch(ctx.repo, branch, base);
|
|
33
32
|
return [
|
|
@@ -37,10 +36,10 @@ export async function executeGithubPrepareWorkspace(args, commMessage) {
|
|
|
37
36
|
`📍 ${formatChannelContext(ctx)}`,
|
|
38
37
|
].join('\n');
|
|
39
38
|
}
|
|
40
|
-
export async function executeGithubPatchFile(args, commMessage) {
|
|
39
|
+
export async function executeGithubPatchFile(args, client, commMessage) {
|
|
41
40
|
const msg = resolveCommMessage(commMessage);
|
|
42
41
|
const ctx = resolveChannel(msg, args.repo);
|
|
43
|
-
const gh = requireBotGh();
|
|
42
|
+
const gh = requireBotGh(client);
|
|
44
43
|
const { branch } = args.branch
|
|
45
44
|
? { branch: args.branch }
|
|
46
45
|
: await resolveWorkspaceBranch(gh, ctx);
|
|
@@ -53,19 +52,19 @@ export async function executeGithubPatchFile(args, commMessage) {
|
|
|
53
52
|
const url = r.data.content?.html_url ?? r.data.commit?.html_url ?? '';
|
|
54
53
|
return `✅ 已更新 \`${args.path}\` @ \`${branch}\`${url ? `\n🔗 ${url}` : ''}`;
|
|
55
54
|
}
|
|
56
|
-
export async function executeGithubPushBranch(args, commMessage) {
|
|
55
|
+
export async function executeGithubPushBranch(args, client, commMessage) {
|
|
57
56
|
const msg = resolveCommMessage(commMessage);
|
|
58
57
|
const ctx = resolveChannel(msg, args.repo);
|
|
59
|
-
const gh = requireBotGh();
|
|
60
|
-
const wm =
|
|
58
|
+
const gh = requireBotGh(client);
|
|
59
|
+
const wm = client.workspaceManager;
|
|
61
60
|
const branch = args.branch ?? (await resolveWorkspaceBranch(gh, ctx)).branch;
|
|
62
61
|
const result = await wm.commitAndPush(ctx.repo, branch, args.message);
|
|
63
62
|
return `✅ ${result}\n📍 ${ctx.repo} @ \`${branch}\``;
|
|
64
63
|
}
|
|
65
|
-
export async function executeGithubCreatePr(args, commMessage) {
|
|
64
|
+
export async function executeGithubCreatePr(args, client, commMessage) {
|
|
66
65
|
const msg = resolveCommMessage(commMessage);
|
|
67
66
|
const ctx = resolveChannel(msg, args.repo);
|
|
68
|
-
const gh = requireBotGh();
|
|
67
|
+
const gh = requireBotGh(client);
|
|
69
68
|
const resolved = await resolveWorkspaceBranch(gh, ctx);
|
|
70
69
|
const head = args.head ?? resolved.branch;
|
|
71
70
|
const base = args.base ?? resolved.base;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const githubEndpointCommands: import("@zhin.js/adapter").EndpointCommands<Readonly<import("@zhin.js/command").CommandDefinition<unknown, unknown, import("@zhin.js/command").CommandMessage>>>;
|
|
1
|
+
export declare const githubEndpointCommands: import("@zhin.js/adapter").EndpointCommands<Readonly<import("@zhin.js/command").CommandDefinition<unknown, unknown, import("@zhin.js/command").CommandMessage, string | undefined>>>;
|
|
@@ -1,17 +1,18 @@
|
|
|
1
1
|
import type { Message } from 'zhin.js';
|
|
2
|
+
import type { GithubClient } from './client.js';
|
|
2
3
|
export declare function executeGithubStar(args: {
|
|
3
4
|
action: 'star' | 'unstar' | 'check';
|
|
4
5
|
repo: string;
|
|
5
|
-
}, commMessage?: Message): Promise<string>;
|
|
6
|
-
export declare function executeGithubBind(_args: Record<string, never>, commMessage?: Message): Promise<string>;
|
|
7
|
-
export declare function executeGithubUnbind(_args: Record<string, never>, commMessage?: Message): Promise<string>;
|
|
8
|
-
export declare function executeGithubWhoami(_args: Record<string, never>, commMessage?: Message): Promise<string>;
|
|
9
|
-
export declare function executeGithubInstall(): Promise<string>;
|
|
6
|
+
}, client: GithubClient, commMessage?: Message): Promise<string>;
|
|
7
|
+
export declare function executeGithubBind(_args: Record<string, never>, client: GithubClient, commMessage?: Message): Promise<string>;
|
|
8
|
+
export declare function executeGithubUnbind(_args: Record<string, never>, client: GithubClient, commMessage?: Message): Promise<string>;
|
|
9
|
+
export declare function executeGithubWhoami(_args: Record<string, never>, client: GithubClient, commMessage?: Message): Promise<string>;
|
|
10
|
+
export declare function executeGithubInstall(client: GithubClient): Promise<string>;
|
|
10
11
|
export declare function executeGithubSubscribe(args: {
|
|
11
12
|
repo: string;
|
|
12
13
|
events?: string;
|
|
13
|
-
}, commMessage?: Message): Promise<string>;
|
|
14
|
+
}, client: GithubClient, commMessage?: Message): Promise<string>;
|
|
14
15
|
export declare function executeGithubUnsubscribe(args: {
|
|
15
16
|
repo: string;
|
|
16
|
-
}, commMessage?: Message): Promise<string>;
|
|
17
|
-
export declare function executeGithubSubscriptions(_args: Record<string, never>, commMessage?: Message): Promise<string>;
|
|
17
|
+
}, client: GithubClient, commMessage?: Message): Promise<string>;
|
|
18
|
+
export declare function executeGithubSubscriptions(_args: Record<string, never>, client: GithubClient, commMessage?: Message): Promise<string>;
|
|
@@ -1,28 +1,24 @@
|
|
|
1
1
|
import { formatCompact } from '@zhin.js/logger';
|
|
2
2
|
import { getCurrentCommMessage } from '@zhin.js/agent/security';
|
|
3
3
|
import { GhClient } from './gh-client.js';
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
const db = getGithubAgentDeps().getDatabase?.();
|
|
4
|
+
function oauthModel(client) {
|
|
5
|
+
const db = client.database;
|
|
7
6
|
return db?.models?.get('github_oauth_users');
|
|
8
7
|
}
|
|
9
|
-
function subscriptionsModel() {
|
|
10
|
-
const db =
|
|
8
|
+
function subscriptionsModel(client) {
|
|
9
|
+
const db = client.database;
|
|
11
10
|
return db?.models?.get('github_subscriptions');
|
|
12
11
|
}
|
|
13
12
|
function depsLogger() {
|
|
14
|
-
return
|
|
13
|
+
return {
|
|
15
14
|
debug: (...args) => console.debug(...args),
|
|
16
15
|
warn: (...args) => console.warn(...args),
|
|
17
16
|
error: (...args) => console.error(...args),
|
|
18
17
|
};
|
|
19
18
|
}
|
|
20
|
-
export async function executeGithubStar(args, commMessage) {
|
|
21
|
-
const adapter = getAdapter();
|
|
19
|
+
export async function executeGithubStar(args, client, commMessage) {
|
|
22
20
|
const msg = commMessage ?? getCurrentCommMessage();
|
|
23
|
-
const gh = await
|
|
24
|
-
if (!gh)
|
|
25
|
-
return '❌ 没有可用的 GitHub bot';
|
|
21
|
+
const gh = await client.getUserOrDefaultApi(msg?.$adapter, msg?.$sender.id);
|
|
26
22
|
switch (args.action) {
|
|
27
23
|
case 'star': {
|
|
28
24
|
const r = await gh.starRepo(args.repo);
|
|
@@ -40,16 +36,15 @@ export async function executeGithubStar(args, commMessage) {
|
|
|
40
36
|
return `❌ 未知操作: ${args.action}`;
|
|
41
37
|
}
|
|
42
38
|
}
|
|
43
|
-
export async function executeGithubBind(_args, commMessage) {
|
|
44
|
-
const adapter = getAdapter();
|
|
39
|
+
export async function executeGithubBind(_args, client, commMessage) {
|
|
45
40
|
const log = depsLogger();
|
|
46
41
|
const msg = commMessage ?? getCurrentCommMessage();
|
|
47
42
|
if (!msg?.$adapter || !msg?.$sender?.id)
|
|
48
43
|
return '❌ 无法获取当前用户信息';
|
|
49
|
-
const clientId =
|
|
44
|
+
const clientId = client.clientId;
|
|
50
45
|
if (!clientId)
|
|
51
46
|
return '❌ Endpoint 未配置 GitHub App 或 App 无 client_id,无法进行账号绑定';
|
|
52
|
-
const model = oauthModel();
|
|
47
|
+
const model = oauthModel(client);
|
|
53
48
|
if (!model)
|
|
54
49
|
return '❌ 数据库未就绪';
|
|
55
50
|
const [existing] = await model.select().where({ platform: msg.$adapter, platform_uid: msg.$sender.id });
|
|
@@ -57,7 +52,7 @@ export async function executeGithubBind(_args, commMessage) {
|
|
|
57
52
|
return `⚠️ 你已绑定 GitHub 账号: ${existing.github_login}\n如需重新绑定,请先执行 github_unbind`;
|
|
58
53
|
}
|
|
59
54
|
try {
|
|
60
|
-
const host =
|
|
55
|
+
const host = client.host;
|
|
61
56
|
const codeResp = await GhClient.deviceFlowRequestCode(clientId, host);
|
|
62
57
|
const tokenPromise = GhClient.deviceFlowPollToken(clientId, codeResp.device_code, codeResp.interval, codeResp.expires_in, host);
|
|
63
58
|
const replyMsg = [
|
|
@@ -97,11 +92,11 @@ export async function executeGithubBind(_args, commMessage) {
|
|
|
97
92
|
return `❌ Device Flow 启动失败: ${e instanceof Error ? e.message : String(e)}`;
|
|
98
93
|
}
|
|
99
94
|
}
|
|
100
|
-
export async function executeGithubUnbind(_args, commMessage) {
|
|
95
|
+
export async function executeGithubUnbind(_args, client, commMessage) {
|
|
101
96
|
const msg = commMessage ?? getCurrentCommMessage();
|
|
102
97
|
if (!msg?.$adapter || !msg?.$sender?.id)
|
|
103
98
|
return '❌ 无法获取当前用户信息';
|
|
104
|
-
const model = oauthModel();
|
|
99
|
+
const model = oauthModel(client);
|
|
105
100
|
if (!model)
|
|
106
101
|
return '❌ 数据库未就绪';
|
|
107
102
|
const [existing] = await model.select().where({ platform: msg.$adapter, platform_uid: msg.$sender.id });
|
|
@@ -110,31 +105,29 @@ export async function executeGithubUnbind(_args, commMessage) {
|
|
|
110
105
|
await model.delete().where({ id: existing.id });
|
|
111
106
|
return `✅ 已解除 GitHub 账号绑定: ${existing.github_login}`;
|
|
112
107
|
}
|
|
113
|
-
export async function executeGithubWhoami(_args, commMessage) {
|
|
114
|
-
const adapter = getAdapter();
|
|
108
|
+
export async function executeGithubWhoami(_args, client, commMessage) {
|
|
115
109
|
const msg = commMessage ?? getCurrentCommMessage();
|
|
116
110
|
if (!msg?.$adapter || !msg?.$sender?.id)
|
|
117
111
|
return '❌ 无法获取当前用户信息';
|
|
118
|
-
const model = oauthModel();
|
|
112
|
+
const model = oauthModel(client);
|
|
119
113
|
if (!model)
|
|
120
114
|
return '❌ 数据库未就绪';
|
|
121
115
|
const [existing] = await model.select().where({ platform: msg.$adapter, platform_uid: msg.$sender.id });
|
|
122
116
|
if (!existing)
|
|
123
117
|
return '📭 你尚未绑定 GitHub 账号\n🔗 使用 github_bind 绑定你的账号';
|
|
124
|
-
const userGh = new GhClient({ host:
|
|
118
|
+
const userGh = new GhClient({ host: client.host, token: existing.access_token });
|
|
125
119
|
const auth = await userGh.verifyAuth();
|
|
126
120
|
if (auth.ok) {
|
|
127
121
|
return `👤 已绑定 GitHub 账号: ${auth.user}\n📅 绑定时间: ${new Date(existing.created_at).toLocaleString('zh-CN')}`;
|
|
128
122
|
}
|
|
129
123
|
return `⚠️ 已绑定账号 ${existing.github_login},但 Token 已失效\n🔗 请执行 github_unbind 后重新 github_bind`;
|
|
130
124
|
}
|
|
131
|
-
export async function executeGithubInstall() {
|
|
132
|
-
const
|
|
133
|
-
const slug = adapter.getAppSlug();
|
|
125
|
+
export async function executeGithubInstall(client) {
|
|
126
|
+
const slug = client.appSlug;
|
|
134
127
|
if (!slug)
|
|
135
128
|
return '❌ Endpoint 未配置 GitHub App';
|
|
136
|
-
const host =
|
|
137
|
-
const installations =
|
|
129
|
+
const host = client.host || 'github.com';
|
|
130
|
+
const installations = client.installations;
|
|
138
131
|
let msg = `🔗 请点击以下链接安装 GitHub App 到你的仓库:\n https://${host}/apps/${slug}/installations/new`;
|
|
139
132
|
if (installations.length) {
|
|
140
133
|
msg += `\n\n📋 当前已安装 (${installations.length}):`;
|
|
@@ -144,12 +137,12 @@ export async function executeGithubInstall() {
|
|
|
144
137
|
}
|
|
145
138
|
return msg;
|
|
146
139
|
}
|
|
147
|
-
export async function executeGithubSubscribe(args, commMessage) {
|
|
140
|
+
export async function executeGithubSubscribe(args, client, commMessage) {
|
|
148
141
|
const msg = commMessage ?? getCurrentCommMessage();
|
|
149
142
|
if (!msg?.$adapter || !msg?.$sender.id || !msg?.$channel?.id || !msg?.$endpoint) {
|
|
150
143
|
return '❌ 无法获取当前聊天通道信息';
|
|
151
144
|
}
|
|
152
|
-
const model = subscriptionsModel();
|
|
145
|
+
const model = subscriptionsModel(client);
|
|
153
146
|
if (!model)
|
|
154
147
|
return '❌ 数据库未就绪';
|
|
155
148
|
const validEvents = ['push', 'issue', 'star', 'fork', 'unstar', 'pull_request'];
|
|
@@ -179,12 +172,12 @@ export async function executeGithubSubscribe(args, commMessage) {
|
|
|
179
172
|
});
|
|
180
173
|
return `✅ 已订阅 ${args.repo}\n📡 事件: ${events.join(', ')}\n📌 通知将推送到当前通道`;
|
|
181
174
|
}
|
|
182
|
-
export async function executeGithubUnsubscribe(args, commMessage) {
|
|
175
|
+
export async function executeGithubUnsubscribe(args, client, commMessage) {
|
|
183
176
|
const msg = commMessage ?? getCurrentCommMessage();
|
|
184
177
|
if (!msg?.$adapter || !msg?.$channel?.id || !msg?.$endpoint) {
|
|
185
178
|
return '❌ 无法获取当前聊天通道信息';
|
|
186
179
|
}
|
|
187
|
-
const model = subscriptionsModel();
|
|
180
|
+
const model = subscriptionsModel(client);
|
|
188
181
|
if (!model)
|
|
189
182
|
return '❌ 数据库未就绪';
|
|
190
183
|
const [existing] = await model.select().where({
|
|
@@ -198,12 +191,12 @@ export async function executeGithubUnsubscribe(args, commMessage) {
|
|
|
198
191
|
await model.delete().where({ id: existing.id });
|
|
199
192
|
return `✅ 已取消订阅 ${args.repo}`;
|
|
200
193
|
}
|
|
201
|
-
export async function executeGithubSubscriptions(_args, commMessage) {
|
|
194
|
+
export async function executeGithubSubscriptions(_args, client, commMessage) {
|
|
202
195
|
const msg = commMessage ?? getCurrentCommMessage();
|
|
203
196
|
if (!msg?.$adapter || !msg?.$channel?.id || !msg?.$endpoint) {
|
|
204
197
|
return '❌ 无法获取当前聊天通道信息';
|
|
205
198
|
}
|
|
206
|
-
const model = subscriptionsModel();
|
|
199
|
+
const model = subscriptionsModel(client);
|
|
207
200
|
if (!model)
|
|
208
201
|
return '❌ 数据库未就绪';
|
|
209
202
|
const subs = await model.select().where({
|
package/lib/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export { buildChannelId, enrichInboundContent, formatInboundContent, formatNotification, formatOutboundBody, normalizeWebhookPath, parseChannelId, parseIssueCommentInbound, parsePrReviewCommentInbound, parsePrReviewInbound, resolveGithubConfig, shouldAutoReplyRepo, verifyWebhookSignature, type GithubAdapterConfig, type GithubInboundComment, type GithubWireSegment, type ResolvedGithubConfig, } from './protocol.js';
|
|
2
2
|
export * from './types.js';
|
|
3
|
-
export { getAdapter, getGithubAgentDeps, registerGithubAgentEndpoint, setGithubAgentDeps, type GithubAgentDeps, type GithubAgentEndpoint, } from './github-agent-deps.js';
|
|
4
3
|
export { GhClient } from './gh-client.js';
|
|
4
|
+
export { GithubClient, githubClient, type GithubClientEventMap } from './client.js';
|
|
5
5
|
export { WorkspaceManager } from './workspace-manager.js';
|
|
6
6
|
export { parseMessageChannel, issueBranchName, resolveWorkspaceBranch, formatChannelContext, } from './github-channel-context.js';
|
|
7
7
|
export { GithubEndpoint, defaultCreateClient, type GithubEndpointOptions, } from './endpoint.js';
|
package/lib/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export { buildChannelId, enrichInboundContent, formatInboundContent, formatNotification, formatOutboundBody, normalizeWebhookPath, parseChannelId, parseIssueCommentInbound, parsePrReviewCommentInbound, parsePrReviewInbound, resolveGithubConfig, shouldAutoReplyRepo, verifyWebhookSignature, } from './protocol.js';
|
|
2
2
|
export * from './types.js';
|
|
3
|
-
export { getAdapter, getGithubAgentDeps, registerGithubAgentEndpoint, setGithubAgentDeps, } from './github-agent-deps.js';
|
|
4
3
|
export { GhClient } from './gh-client.js';
|
|
4
|
+
export { GithubClient, githubClient } from './client.js';
|
|
5
5
|
export { WorkspaceManager } from './workspace-manager.js';
|
|
6
6
|
export { parseMessageChannel, issueBranchName, resolveWorkspaceBranch, formatChannelContext, } from './github-channel-context.js';
|
|
7
7
|
export { GithubEndpoint, defaultCreateClient, } from './endpoint.js';
|
package/lib/protocol.d.ts
CHANGED
|
@@ -77,7 +77,7 @@ export declare function normalizeWebhookPath(path: string): string;
|
|
|
77
77
|
export declare function shouldAutoReplyRepo(config: Pick<ResolvedGithubConfig, 'autoReplyRepos'>, repo: string): boolean;
|
|
78
78
|
/** Prepend synthetic @bot for auto_reply repos (gateway text path). */
|
|
79
79
|
export declare function enrichInboundContent(content: string, config: Pick<ResolvedGithubConfig, 'autoReplyRepos' | 'botLogin'>, botLogin: string | undefined, repo: string): string;
|
|
80
|
-
/** Build inbound text for
|
|
80
|
+
/** Build inbound text for OutboundMessageService.receive from markdown/comment body. */
|
|
81
81
|
export declare function formatInboundContent(body: string): string;
|
|
82
82
|
/** Build markdown body for Issue/PR comment send. */
|
|
83
83
|
export declare function formatOutboundBody(payload: unknown): string;
|
package/lib/protocol.js
CHANGED
|
@@ -83,7 +83,7 @@ export function enrichInboundContent(content, config, botLogin, repo) {
|
|
|
83
83
|
return content;
|
|
84
84
|
return `@${login} ${content}`;
|
|
85
85
|
}
|
|
86
|
-
/** Build inbound text for
|
|
86
|
+
/** Build inbound text for OutboundMessageService.receive from markdown/comment body. */
|
|
87
87
|
export function formatInboundContent(body) {
|
|
88
88
|
return body;
|
|
89
89
|
}
|
package/lib/webhook.d.ts
CHANGED
|
@@ -6,6 +6,7 @@ import type { HttpHost, HttpRouteRegistration } from '@zhin.js/host-http';
|
|
|
6
6
|
import { type GithubInboundComment, type ResolvedGithubConfig } from './protocol.js';
|
|
7
7
|
export interface GithubWebhookHandler {
|
|
8
8
|
readonly config: ResolvedGithubConfig;
|
|
9
|
+
admitPlatform(name: string, event: unknown): void;
|
|
9
10
|
admit(comment: GithubInboundComment): void;
|
|
10
11
|
}
|
|
11
12
|
export declare function registerGithubWebhookRoutes(http: HttpHost, handler: GithubWebhookHandler): HttpRouteRegistration[];
|
package/lib/webhook.js
CHANGED
|
@@ -66,6 +66,7 @@ export async function dispatchGithubWebhookPayload(event, payload, handler) {
|
|
|
66
66
|
const body = payload;
|
|
67
67
|
const repo = body.repository?.full_name;
|
|
68
68
|
logger.debug(`Webhook: ${event}${body.action ? `.${body.action}` : ''} ${repo || ''}`);
|
|
69
|
+
handler.admitPlatform(typeof body.action === 'string' && body.action ? `${event}.${body.action}` : event, payload);
|
|
69
70
|
if (event === 'issue_comment') {
|
|
70
71
|
const inbound = parseIssueCommentInbound(payload);
|
|
71
72
|
if (inbound)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zhin.js/adapter-github",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "6.0.1",
|
|
4
4
|
"description": "Zhin.js GitHub adapter for Plugin Runtime (App auth + webhook)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./lib/index.js",
|
|
@@ -41,20 +41,22 @@
|
|
|
41
41
|
"directory": "plugins/adapters/github"
|
|
42
42
|
},
|
|
43
43
|
"dependencies": {
|
|
44
|
-
"@zhin.js/adapter": "1.1
|
|
45
|
-
"@zhin.js/core": "1.5.
|
|
46
|
-
"@zhin.js/
|
|
44
|
+
"@zhin.js/adapter": "1.2.1",
|
|
45
|
+
"@zhin.js/core": "1.5.14",
|
|
46
|
+
"@zhin.js/feature-kit": "1.0.13",
|
|
47
|
+
"@zhin.js/host-http": "1.0.13",
|
|
47
48
|
"@zhin.js/im-contract": "1.0.4",
|
|
48
|
-
"@zhin.js/logger": "1.0.
|
|
49
|
+
"@zhin.js/logger": "1.0.77"
|
|
49
50
|
},
|
|
50
51
|
"peerDependencies": {
|
|
51
52
|
"zod": "^4.0.0",
|
|
52
|
-
"@zhin.js/adapter": "1.1
|
|
53
|
-
"@zhin.js/agent": "1.1.
|
|
54
|
-
"@zhin.js/command": "1.0.
|
|
55
|
-
"@zhin.js/core": "1.5.
|
|
56
|
-
"@zhin.js/host-http": "1.0.
|
|
57
|
-
"zhin.js": "
|
|
53
|
+
"@zhin.js/adapter": "1.2.1",
|
|
54
|
+
"@zhin.js/agent": "1.1.16",
|
|
55
|
+
"@zhin.js/command": "1.0.16",
|
|
56
|
+
"@zhin.js/core": "1.5.14",
|
|
57
|
+
"@zhin.js/host-http": "1.0.13",
|
|
58
|
+
"@zhin.js/prompt-section": "0.0.1",
|
|
59
|
+
"zhin.js": "6.0.14"
|
|
58
60
|
},
|
|
59
61
|
"peerDependenciesMeta": {
|
|
60
62
|
"@zhin.js/agent": {
|
|
@@ -63,6 +65,9 @@
|
|
|
63
65
|
"@zhin.js/command": {
|
|
64
66
|
"optional": true
|
|
65
67
|
},
|
|
68
|
+
"@zhin.js/prompt-section": {
|
|
69
|
+
"optional": true
|
|
70
|
+
},
|
|
66
71
|
"zhin.js": {
|
|
67
72
|
"optional": true
|
|
68
73
|
},
|
|
@@ -75,8 +80,9 @@
|
|
|
75
80
|
"typescript": "^6.0.3",
|
|
76
81
|
"vitest": "^4.1.10",
|
|
77
82
|
"zod": "^4.4.3",
|
|
78
|
-
"@zhin.js/agent": "1.1.
|
|
79
|
-
"zhin.js": "
|
|
83
|
+
"@zhin.js/agent": "1.1.16",
|
|
84
|
+
"@zhin.js/prompt-section": "0.0.1",
|
|
85
|
+
"zhin.js": "6.0.14"
|
|
80
86
|
},
|
|
81
87
|
"files": [
|
|
82
88
|
"adapters",
|
package/plugin.js
CHANGED
|
@@ -34,22 +34,5 @@ export default definePlugin({
|
|
|
34
34
|
defineGithubOauthUsersTable(host);
|
|
35
35
|
host.define('github_subscriptions', { ...GITHUB_SUBSCRIPTIONS_SCHEMA });
|
|
36
36
|
}
|
|
37
|
-
// Agent prompt contributor (orchestrator/deferred-worker GitHub guidance).
|
|
38
|
-
// `zhin.js/agent` is an optional peer — skip silently on IM-only installs.
|
|
39
|
-
let cancelled = false;
|
|
40
|
-
let unregister;
|
|
41
|
-
void Promise.all([
|
|
42
|
-
import('zhin.js/agent'),
|
|
43
|
-
import("./lib/agent-prompt.js"),
|
|
44
|
-
]).then(([agent, prompt]) => {
|
|
45
|
-
if (cancelled)
|
|
46
|
-
return;
|
|
47
|
-
agent.registerAgentPromptContributor(prompt.createGithubAgentPromptContributor());
|
|
48
|
-
unregister = () => agent.unregisterAgentPromptContributor('github');
|
|
49
|
-
}).catch(() => { });
|
|
50
|
-
return () => {
|
|
51
|
-
cancelled = true;
|
|
52
|
-
unregister?.();
|
|
53
|
-
};
|
|
54
37
|
},
|
|
55
38
|
});
|