@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/endpoint.js
ADDED
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GithubEndpoint — lifecycle, outbound send, inbound admit.
|
|
3
|
+
*/
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import { formatCompact, getLogger } from '@zhin.js/logger';
|
|
6
|
+
import { GhClient } from './gh-client.js';
|
|
7
|
+
import { registerGithubAgentEndpoint } from './github-agent-deps.js';
|
|
8
|
+
import { lookupGithubOauthAccessToken } from './oauth-users.js';
|
|
9
|
+
import { enrichInboundContent, formatInboundContent, formatOutboundBody, parseChannelId, } from './protocol.js';
|
|
10
|
+
import { registerGithubWebhookRoutes } from './webhook.js';
|
|
11
|
+
import { WorkspaceManager } from './workspace-manager.js';
|
|
12
|
+
const logger = getLogger('github');
|
|
13
|
+
export class GithubEndpoint {
|
|
14
|
+
#options;
|
|
15
|
+
gh;
|
|
16
|
+
config;
|
|
17
|
+
name;
|
|
18
|
+
#workspaceManager = null;
|
|
19
|
+
#routeReleases = [];
|
|
20
|
+
#open = false;
|
|
21
|
+
#started = false;
|
|
22
|
+
#unregisterAgent;
|
|
23
|
+
constructor(options) {
|
|
24
|
+
this.#options = options;
|
|
25
|
+
this.config = options.config;
|
|
26
|
+
this.name = options.config.name;
|
|
27
|
+
this.gh = options.createClient?.(options.config) ?? defaultCreateClient(options.config);
|
|
28
|
+
}
|
|
29
|
+
getAPI() {
|
|
30
|
+
return this.gh;
|
|
31
|
+
}
|
|
32
|
+
async getUserOrDefaultAPI(platform, platformUid) {
|
|
33
|
+
if (platform && platformUid) {
|
|
34
|
+
const token = await lookupGithubOauthAccessToken(this.#options.database, platform, platformUid);
|
|
35
|
+
if (token)
|
|
36
|
+
return this.gh.withToken(token);
|
|
37
|
+
}
|
|
38
|
+
return this.gh;
|
|
39
|
+
}
|
|
40
|
+
getClientId() {
|
|
41
|
+
return this.gh.clientId || null;
|
|
42
|
+
}
|
|
43
|
+
getHost() {
|
|
44
|
+
return this.config.host;
|
|
45
|
+
}
|
|
46
|
+
getAppSlug() {
|
|
47
|
+
return this.gh.appSlug || null;
|
|
48
|
+
}
|
|
49
|
+
getInstallations() {
|
|
50
|
+
return this.gh.installations || [];
|
|
51
|
+
}
|
|
52
|
+
getWorkspaceManager() {
|
|
53
|
+
if (this.#workspaceManager)
|
|
54
|
+
return this.#workspaceManager;
|
|
55
|
+
const workspaceRoot = this.config.workspaceRoot
|
|
56
|
+
?? path.join(process.cwd(), 'data', 'github-workspaces');
|
|
57
|
+
this.#workspaceManager = new WorkspaceManager(this.gh, workspaceRoot);
|
|
58
|
+
return this.#workspaceManager;
|
|
59
|
+
}
|
|
60
|
+
getDatabase() {
|
|
61
|
+
return this.#options.database;
|
|
62
|
+
}
|
|
63
|
+
async start() {
|
|
64
|
+
if (this.#started)
|
|
65
|
+
return;
|
|
66
|
+
this.#started = true;
|
|
67
|
+
try {
|
|
68
|
+
const result = await this.gh.verifyAuth();
|
|
69
|
+
if (!result.ok)
|
|
70
|
+
throw new Error(`GitHub 认证失败: ${result.message}`);
|
|
71
|
+
this.#unregisterAgent = registerGithubAgentEndpoint(this.name, this);
|
|
72
|
+
if (this.config.webhookSecret) {
|
|
73
|
+
if (!this.#options.http) {
|
|
74
|
+
throw new TypeError('GitHub webhook_secret requires httpHostToken');
|
|
75
|
+
}
|
|
76
|
+
this.#routeReleases.push(...registerGithubWebhookRoutes(this.#options.http, this));
|
|
77
|
+
logger.debug(formatCompact({
|
|
78
|
+
endpoint: this.name,
|
|
79
|
+
op: 'webhook',
|
|
80
|
+
path: this.config.webhookPath,
|
|
81
|
+
}));
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
logger.debug(formatCompact({
|
|
85
|
+
endpoint: this.name,
|
|
86
|
+
op: 'connect',
|
|
87
|
+
mode: 'api-only',
|
|
88
|
+
bot: this.gh.authenticatedUser,
|
|
89
|
+
}));
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
catch (error) {
|
|
93
|
+
await this.stop();
|
|
94
|
+
logger.error('Failed to connect GitHub endpoint:', error);
|
|
95
|
+
throw error;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
open() {
|
|
99
|
+
this.#open = true;
|
|
100
|
+
}
|
|
101
|
+
close() {
|
|
102
|
+
this.#open = false;
|
|
103
|
+
}
|
|
104
|
+
async stop() {
|
|
105
|
+
this.#open = false;
|
|
106
|
+
for (const release of this.#routeReleases.splice(0))
|
|
107
|
+
release();
|
|
108
|
+
this.#unregisterAgent?.();
|
|
109
|
+
this.#unregisterAgent = undefined;
|
|
110
|
+
this.#started = false;
|
|
111
|
+
logger.debug(formatCompact({ op: 'disconnect', endpoint: this.name }));
|
|
112
|
+
}
|
|
113
|
+
async send({ target, payload }) {
|
|
114
|
+
const parsed = parseChannelId(target);
|
|
115
|
+
if (!parsed)
|
|
116
|
+
throw new Error(`无效的 GitHub channel ID: ${target}`);
|
|
117
|
+
const text = formatOutboundBody(payload);
|
|
118
|
+
const r = parsed.type === 'issue'
|
|
119
|
+
? await this.gh.createIssueComment(parsed.repo, parsed.number, text)
|
|
120
|
+
: await this.gh.createPRComment(parsed.repo, parsed.number, text);
|
|
121
|
+
if (!r.ok)
|
|
122
|
+
throw new Error(`发送失败: ${JSON.stringify(r.data)}`);
|
|
123
|
+
logger.debug(formatCompact({
|
|
124
|
+
op: 'github_send',
|
|
125
|
+
endpoint: this.name,
|
|
126
|
+
target,
|
|
127
|
+
messageId: r.data.id,
|
|
128
|
+
}));
|
|
129
|
+
return String(r.data.id);
|
|
130
|
+
}
|
|
131
|
+
/** Test / internal: admit a parsed comment when open. */
|
|
132
|
+
admit(comment) {
|
|
133
|
+
if (!this.#open)
|
|
134
|
+
return;
|
|
135
|
+
const botUser = this.config.botLogin || this.gh.getBotLogin() || this.gh.authenticatedUser;
|
|
136
|
+
if (botUser && comment.sender === botUser)
|
|
137
|
+
return;
|
|
138
|
+
const content = enrichInboundContent(formatInboundContent(comment.content), this.config, botUser ?? undefined, comment.repo);
|
|
139
|
+
void this.#options.gateway.receive({
|
|
140
|
+
adapter: this.#options.id,
|
|
141
|
+
target: comment.channelId,
|
|
142
|
+
content,
|
|
143
|
+
sender: comment.sender,
|
|
144
|
+
id: comment.id,
|
|
145
|
+
metadata: Object.freeze({
|
|
146
|
+
endpoint: this.name,
|
|
147
|
+
repo: comment.repo,
|
|
148
|
+
kind: comment.kind,
|
|
149
|
+
createdAt: comment.createdAt,
|
|
150
|
+
}),
|
|
151
|
+
}).catch((err) => {
|
|
152
|
+
logger.warn(formatCompact({
|
|
153
|
+
op: 'github_gateway_receive_failed',
|
|
154
|
+
target: comment.channelId,
|
|
155
|
+
error: err instanceof Error ? err.message : String(err),
|
|
156
|
+
}));
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
export function defaultCreateClient(config) {
|
|
161
|
+
const appAuth = config.appId && config.privateKey
|
|
162
|
+
? { appId: config.appId, privateKey: config.privateKey }
|
|
163
|
+
: undefined;
|
|
164
|
+
return new GhClient({ host: config.host, appAuth });
|
|
165
|
+
}
|
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GitHub CLI (`gh`) 客户端封装
|
|
3
|
+
*
|
|
4
|
+
* 支持三种认证方式:
|
|
5
|
+
* 1. gh CLI 默认凭据(gh auth login)
|
|
6
|
+
* 2. GitHub App — JWT → Installation Token(自动续期)
|
|
7
|
+
* 3. 个人 OAuth Token(用户绑定 / Device Flow)
|
|
8
|
+
*
|
|
9
|
+
* 零外部依赖,使用 Node.js 原生模块。
|
|
10
|
+
*/
|
|
11
|
+
export interface AppAuth {
|
|
12
|
+
appId: string | number;
|
|
13
|
+
/** PEM 格式私钥内容,或私钥文件路径 */
|
|
14
|
+
privateKey: string;
|
|
15
|
+
}
|
|
16
|
+
export interface GitHubBotIdentity {
|
|
17
|
+
login: string;
|
|
18
|
+
email: string;
|
|
19
|
+
slug: string;
|
|
20
|
+
userId: number;
|
|
21
|
+
}
|
|
22
|
+
export interface GitHubCommitAuthor {
|
|
23
|
+
name: string;
|
|
24
|
+
email: string;
|
|
25
|
+
date?: string;
|
|
26
|
+
}
|
|
27
|
+
export interface GhClientOptions {
|
|
28
|
+
/** GitHub Enterprise 主机名(默认 github.com) */
|
|
29
|
+
host?: string;
|
|
30
|
+
/** 覆盖 gh CLI 默认认证的 Personal Access Token / OAuth Token */
|
|
31
|
+
token?: string;
|
|
32
|
+
/** GitHub App 认证 — 自动管理 Installation Token */
|
|
33
|
+
appAuth?: AppAuth;
|
|
34
|
+
}
|
|
35
|
+
export declare class GhClient {
|
|
36
|
+
private host?;
|
|
37
|
+
private token?;
|
|
38
|
+
private _appAuth?;
|
|
39
|
+
private _resolvedKey?;
|
|
40
|
+
private _user;
|
|
41
|
+
/** Installation Token 缓存: installationId → { token, expiresAt } */
|
|
42
|
+
private _installationTokens;
|
|
43
|
+
/** repo(小写) → installationId */
|
|
44
|
+
private _repoToInstallation;
|
|
45
|
+
/** 所有 Installation 信息 */
|
|
46
|
+
private _allInstallations;
|
|
47
|
+
/** 上次 syncInstallations 时间 (ms) */
|
|
48
|
+
private _lastSyncTime;
|
|
49
|
+
constructor(options?: GhClientOptions);
|
|
50
|
+
/** 创建使用指定 token 的副本(共享 host 配置,不继承 App 认证) */
|
|
51
|
+
withToken(token: string): GhClient;
|
|
52
|
+
get authenticatedUser(): string | null;
|
|
53
|
+
private exec;
|
|
54
|
+
/**
|
|
55
|
+
* 确保 App 认证的 Installation Token 有效(自动按 repo 查找对应安装)
|
|
56
|
+
* @param repo 仓库全名(owner/repo),用于选择正确的 Installation
|
|
57
|
+
*/
|
|
58
|
+
private ensureTokenForRepo;
|
|
59
|
+
request<T = any>(method: string, path: string, body?: any, headers?: Record<string, string>): Promise<{
|
|
60
|
+
ok: boolean;
|
|
61
|
+
status: number;
|
|
62
|
+
data: T;
|
|
63
|
+
}>;
|
|
64
|
+
private get;
|
|
65
|
+
private post;
|
|
66
|
+
private patch;
|
|
67
|
+
private put;
|
|
68
|
+
private del;
|
|
69
|
+
verifyAuth(): Promise<{
|
|
70
|
+
ok: boolean;
|
|
71
|
+
user: string;
|
|
72
|
+
message: string;
|
|
73
|
+
}>;
|
|
74
|
+
getRepo(repo: string): Promise<{
|
|
75
|
+
ok: boolean;
|
|
76
|
+
status: number;
|
|
77
|
+
data: any;
|
|
78
|
+
}>;
|
|
79
|
+
listIssues(repo: string, state?: 'open' | 'closed' | 'all'): Promise<{
|
|
80
|
+
ok: boolean;
|
|
81
|
+
status: number;
|
|
82
|
+
data: any[];
|
|
83
|
+
}>;
|
|
84
|
+
getIssue(repo: string, number: number): Promise<{
|
|
85
|
+
ok: boolean;
|
|
86
|
+
status: number;
|
|
87
|
+
data: any;
|
|
88
|
+
}>;
|
|
89
|
+
listPRs(repo: string, state?: 'open' | 'closed' | 'all'): Promise<{
|
|
90
|
+
ok: boolean;
|
|
91
|
+
status: number;
|
|
92
|
+
data: any[];
|
|
93
|
+
}>;
|
|
94
|
+
getPR(repo: string, number: number): Promise<{
|
|
95
|
+
ok: boolean;
|
|
96
|
+
status: number;
|
|
97
|
+
data: any;
|
|
98
|
+
}>;
|
|
99
|
+
searchRepos(query: string, perPage?: number): Promise<{
|
|
100
|
+
ok: boolean;
|
|
101
|
+
status: number;
|
|
102
|
+
data: {
|
|
103
|
+
total_count: number;
|
|
104
|
+
items: any[];
|
|
105
|
+
};
|
|
106
|
+
}>;
|
|
107
|
+
listCommits(repo: string, sha?: string, _path?: string, perPage?: number): Promise<{
|
|
108
|
+
ok: boolean;
|
|
109
|
+
status: number;
|
|
110
|
+
data: any[];
|
|
111
|
+
}>;
|
|
112
|
+
listBranches(repo: string, perPage?: number): Promise<{
|
|
113
|
+
ok: boolean;
|
|
114
|
+
status: number;
|
|
115
|
+
data: any[];
|
|
116
|
+
}>;
|
|
117
|
+
listReleases(repo: string, perPage?: number): Promise<{
|
|
118
|
+
ok: boolean;
|
|
119
|
+
status: number;
|
|
120
|
+
data: any[];
|
|
121
|
+
}>;
|
|
122
|
+
listWorkflowRuns(repo: string, perPage?: number): Promise<{
|
|
123
|
+
ok: boolean;
|
|
124
|
+
status: number;
|
|
125
|
+
data: {
|
|
126
|
+
workflow_runs: any[];
|
|
127
|
+
};
|
|
128
|
+
}>;
|
|
129
|
+
closeIssue(repo: string, number: number): Promise<{
|
|
130
|
+
ok: boolean;
|
|
131
|
+
status: number;
|
|
132
|
+
data: any;
|
|
133
|
+
}>;
|
|
134
|
+
closePR(repo: string, number: number): Promise<{
|
|
135
|
+
ok: boolean;
|
|
136
|
+
status: number;
|
|
137
|
+
data: any;
|
|
138
|
+
}>;
|
|
139
|
+
createIssueComment(repo: string, issueNumber: number, body: string): Promise<{
|
|
140
|
+
ok: boolean;
|
|
141
|
+
status: number;
|
|
142
|
+
data: {
|
|
143
|
+
id: number;
|
|
144
|
+
html_url: string;
|
|
145
|
+
message?: string;
|
|
146
|
+
};
|
|
147
|
+
}>;
|
|
148
|
+
deleteIssueComment(repo: string, commentId: number): Promise<{
|
|
149
|
+
ok: boolean;
|
|
150
|
+
status: number;
|
|
151
|
+
data: any;
|
|
152
|
+
}>;
|
|
153
|
+
createPRComment(repo: string, prNumber: number, body: string): Promise<{
|
|
154
|
+
ok: boolean;
|
|
155
|
+
status: number;
|
|
156
|
+
data: {
|
|
157
|
+
id: number;
|
|
158
|
+
html_url: string;
|
|
159
|
+
message?: string;
|
|
160
|
+
};
|
|
161
|
+
}>;
|
|
162
|
+
deletePRReviewComment(repo: string, commentId: number): Promise<{
|
|
163
|
+
ok: boolean;
|
|
164
|
+
status: number;
|
|
165
|
+
data: any;
|
|
166
|
+
}>;
|
|
167
|
+
getFileContent(repo: string, path: string, ref?: string): Promise<{
|
|
168
|
+
ok: boolean;
|
|
169
|
+
status: number;
|
|
170
|
+
data: {
|
|
171
|
+
content: string;
|
|
172
|
+
sha: string;
|
|
173
|
+
encoding: string;
|
|
174
|
+
};
|
|
175
|
+
}>;
|
|
176
|
+
createOrUpdateFile(repo: string, path: string, content: string, message: string, options?: {
|
|
177
|
+
branch?: string;
|
|
178
|
+
sha?: string;
|
|
179
|
+
}): Promise<{
|
|
180
|
+
ok: boolean;
|
|
181
|
+
status: number;
|
|
182
|
+
data: {
|
|
183
|
+
commit: {
|
|
184
|
+
sha: string;
|
|
185
|
+
html_url?: string;
|
|
186
|
+
};
|
|
187
|
+
content: {
|
|
188
|
+
html_url?: string;
|
|
189
|
+
};
|
|
190
|
+
};
|
|
191
|
+
}>;
|
|
192
|
+
getRef(repo: string, ref: string): Promise<{
|
|
193
|
+
ok: boolean;
|
|
194
|
+
status: number;
|
|
195
|
+
data: {
|
|
196
|
+
ref: string;
|
|
197
|
+
object: {
|
|
198
|
+
sha: string;
|
|
199
|
+
type: string;
|
|
200
|
+
};
|
|
201
|
+
};
|
|
202
|
+
}>;
|
|
203
|
+
createRef(repo: string, ref: string, sha: string): Promise<{
|
|
204
|
+
ok: boolean;
|
|
205
|
+
status: number;
|
|
206
|
+
data: any;
|
|
207
|
+
}>;
|
|
208
|
+
createPullRequest(repo: string, title: string, head: string, base: string, body?: string): Promise<{
|
|
209
|
+
ok: boolean;
|
|
210
|
+
status: number;
|
|
211
|
+
data: {
|
|
212
|
+
number: number;
|
|
213
|
+
html_url: string;
|
|
214
|
+
head: {
|
|
215
|
+
ref: string;
|
|
216
|
+
};
|
|
217
|
+
};
|
|
218
|
+
}>;
|
|
219
|
+
getBotLogin(): string | null;
|
|
220
|
+
getCommitAuthor(): GitHubCommitAuthor | null;
|
|
221
|
+
getBotIdentitySync(): GitHubBotIdentity | null;
|
|
222
|
+
getBotIdentity(): Promise<GitHubBotIdentity | null>;
|
|
223
|
+
buildCloneUrl(repo: string, token: string): string;
|
|
224
|
+
resolveBotUserId(): Promise<number | null>;
|
|
225
|
+
starRepo(repo: string): Promise<{
|
|
226
|
+
ok: boolean;
|
|
227
|
+
status: number;
|
|
228
|
+
data: any;
|
|
229
|
+
}>;
|
|
230
|
+
unstarRepo(repo: string): Promise<{
|
|
231
|
+
ok: boolean;
|
|
232
|
+
status: number;
|
|
233
|
+
data: any;
|
|
234
|
+
}>;
|
|
235
|
+
isStarred(repo: string): Promise<boolean>;
|
|
236
|
+
forkRepo(repo: string): Promise<{
|
|
237
|
+
ok: boolean;
|
|
238
|
+
status: number;
|
|
239
|
+
data: any;
|
|
240
|
+
}>;
|
|
241
|
+
/** 解析私钥:PEM字符串 或 文件路径 */
|
|
242
|
+
private resolvePrivateKey;
|
|
243
|
+
/** 生成 GitHub App JWT(有效期 10 分钟) */
|
|
244
|
+
static createJWT(appId: string | number, privateKey: string): string;
|
|
245
|
+
/** 使用 JWT 获取指定 Installation 的 Access Token(有效期 1 小时) */
|
|
246
|
+
refreshInstallationToken(installationId: number): Promise<void>;
|
|
247
|
+
/** 发现所有 App 安装及其可访问的仓库 */
|
|
248
|
+
syncInstallations(): Promise<void>;
|
|
249
|
+
/** 当前是否使用 App 认证 */
|
|
250
|
+
get isAppAuth(): boolean;
|
|
251
|
+
/** 所有已发现的安装 */
|
|
252
|
+
get installations(): {
|
|
253
|
+
id: number;
|
|
254
|
+
account: {
|
|
255
|
+
login: string;
|
|
256
|
+
type: string;
|
|
257
|
+
};
|
|
258
|
+
target_type: string;
|
|
259
|
+
}[];
|
|
260
|
+
/** App 的 slug(verifyAuth 后可用) */
|
|
261
|
+
private _appSlug;
|
|
262
|
+
get appSlug(): string | null;
|
|
263
|
+
/** App 的 client_id(verifyAuth 后从 /app 获取,用于 Device Flow) */
|
|
264
|
+
private _clientId;
|
|
265
|
+
/** Bot 机器用户 numeric id(/users/{slug}[bot]) */
|
|
266
|
+
private _botUserId;
|
|
267
|
+
/** 确保 App 认证的 Installation Token 对指定 repo 有效,并返回 token */
|
|
268
|
+
ensureInstallationTokenForRepo(repo: string): Promise<string | undefined>;
|
|
269
|
+
get clientId(): string | null;
|
|
270
|
+
/**
|
|
271
|
+
* 获取仓库事件(支持 ETag 条件请求)
|
|
272
|
+
* @returns events 数组 + 新 etag;若 304 未修改则 events 为空
|
|
273
|
+
*/
|
|
274
|
+
listRepoEvents(repo: string, etag?: string): Promise<{
|
|
275
|
+
events: any[];
|
|
276
|
+
etag: string | null;
|
|
277
|
+
}>;
|
|
278
|
+
/**
|
|
279
|
+
* 第一步:请求设备码
|
|
280
|
+
* GitHub App 不需要 scope 参数(权限由 App 设置决定),OAuth App 可传 scope。
|
|
281
|
+
* 注意:Device Flow 端点在 github.com(而非 api.github.com),需确保网络可达。
|
|
282
|
+
* @returns device_code, user_code, verification_uri, expires_in, interval
|
|
283
|
+
*/
|
|
284
|
+
static deviceFlowRequestCode(clientId: string, host?: string): Promise<{
|
|
285
|
+
device_code: string;
|
|
286
|
+
user_code: string;
|
|
287
|
+
verification_uri: string;
|
|
288
|
+
expires_in: number;
|
|
289
|
+
interval: number;
|
|
290
|
+
}>;
|
|
291
|
+
/**
|
|
292
|
+
* 第二步:轮询等待用户授权
|
|
293
|
+
* @returns access_token 或 null(超时/拒绝)
|
|
294
|
+
*/
|
|
295
|
+
static deviceFlowPollToken(clientId: string, deviceCode: string, interval?: number, expiresIn?: number, host?: string): Promise<{
|
|
296
|
+
access_token: string;
|
|
297
|
+
token_type: string;
|
|
298
|
+
scope: string;
|
|
299
|
+
} | null>;
|
|
300
|
+
}
|
|
@@ -328,7 +328,7 @@ export class GhClient {
|
|
|
328
328
|
}
|
|
329
329
|
buildCloneUrl(repo, token) {
|
|
330
330
|
const host = this.host || 'github.com';
|
|
331
|
-
return `https://x-access-token:${token}@${host}/${repo}.git`;
|
|
331
|
+
return `https://x-access-token:${encodeURIComponent(token)}@${host}/${repo}.git`;
|
|
332
332
|
}
|
|
333
333
|
async resolveBotUserId() {
|
|
334
334
|
if (this._botUserId != null)
|
|
@@ -594,4 +594,3 @@ export class GhClient {
|
|
|
594
594
|
return null;
|
|
595
595
|
}
|
|
596
596
|
}
|
|
597
|
-
//# sourceMappingURL=gh-client.js.map
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent tool deps for github.
|
|
3
|
+
* Endpoints register themselves on start; tools look up by config name / endpoint id.
|
|
4
|
+
*/
|
|
5
|
+
import type { GhClient } from './gh-client.js';
|
|
6
|
+
import type { ResolvedGithubConfig } from './protocol.js';
|
|
7
|
+
import type { WorkspaceManager } from './workspace-manager.js';
|
|
8
|
+
export interface GithubAgentEndpoint {
|
|
9
|
+
readonly name: string;
|
|
10
|
+
readonly gh: GhClient;
|
|
11
|
+
readonly config: ResolvedGithubConfig;
|
|
12
|
+
getAPI(): GhClient;
|
|
13
|
+
getUserOrDefaultAPI(platform?: string, platformUid?: string): Promise<GhClient | null>;
|
|
14
|
+
getClientId(): string | null;
|
|
15
|
+
getHost(): string | undefined;
|
|
16
|
+
getAppSlug(): string | null;
|
|
17
|
+
getInstallations(): Array<{
|
|
18
|
+
id: number;
|
|
19
|
+
account: {
|
|
20
|
+
login: string;
|
|
21
|
+
type: string;
|
|
22
|
+
};
|
|
23
|
+
target_type: string;
|
|
24
|
+
}>;
|
|
25
|
+
getWorkspaceManager(): WorkspaceManager;
|
|
26
|
+
/** DatabaseHost wired at endpoint creation (optional). */
|
|
27
|
+
getDatabase?(): unknown;
|
|
28
|
+
}
|
|
29
|
+
export interface GithubAgentDeps {
|
|
30
|
+
getEndpoint: (endpointId?: string) => GithubAgentEndpoint;
|
|
31
|
+
/** Alias kept for existing agent handlers that call getAdapter(). */
|
|
32
|
+
getAdapter: () => GithubAgentEndpoint;
|
|
33
|
+
getWorkspaceManager: () => WorkspaceManager;
|
|
34
|
+
getDatabase?: () => {
|
|
35
|
+
models?: Map<string, unknown>;
|
|
36
|
+
} | null | undefined;
|
|
37
|
+
logger?: {
|
|
38
|
+
debug: (...args: unknown[]) => void;
|
|
39
|
+
warn: (...args: unknown[]) => void;
|
|
40
|
+
error: (...args: unknown[]) => void;
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
export declare function registerGithubAgentEndpoint(endpointId: string, endpoint: GithubAgentEndpoint): () => void;
|
|
44
|
+
/** Optional override used by tests / transitional callers. Pass `null` to clear. */
|
|
45
|
+
export declare function setGithubAgentDeps(deps: GithubAgentDeps | null): void;
|
|
46
|
+
export declare function getGithubAgentDeps(): GithubAgentDeps;
|
|
47
|
+
export declare function getAdapter(): GithubAgentEndpoint;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent tool deps for github.
|
|
3
|
+
* Endpoints register themselves on start; tools look up by config name / endpoint id.
|
|
4
|
+
*/
|
|
5
|
+
const endpoints = new Map();
|
|
6
|
+
let override = null;
|
|
7
|
+
export function registerGithubAgentEndpoint(endpointId, endpoint) {
|
|
8
|
+
endpoints.set(endpointId, endpoint);
|
|
9
|
+
return () => {
|
|
10
|
+
if (endpoints.get(endpointId) === endpoint) {
|
|
11
|
+
endpoints.delete(endpointId);
|
|
12
|
+
}
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
/** Optional override used by tests / transitional callers. Pass `null` to clear. */
|
|
16
|
+
export function setGithubAgentDeps(deps) {
|
|
17
|
+
override = deps;
|
|
18
|
+
}
|
|
19
|
+
function lookup(endpointId) {
|
|
20
|
+
if (endpointId) {
|
|
21
|
+
const registered = endpoints.get(endpointId);
|
|
22
|
+
if (!registered)
|
|
23
|
+
throw new Error(`Endpoint ${endpointId} 不存在`);
|
|
24
|
+
return registered;
|
|
25
|
+
}
|
|
26
|
+
const first = endpoints.values().next().value;
|
|
27
|
+
if (!first)
|
|
28
|
+
throw new Error('github agent deps not initialized');
|
|
29
|
+
return first;
|
|
30
|
+
}
|
|
31
|
+
export function getGithubAgentDeps() {
|
|
32
|
+
if (override)
|
|
33
|
+
return override;
|
|
34
|
+
return {
|
|
35
|
+
getEndpoint: lookup,
|
|
36
|
+
getAdapter: () => lookup(),
|
|
37
|
+
getWorkspaceManager: () => lookup().getWorkspaceManager(),
|
|
38
|
+
getDatabase: () => lookup().getDatabase?.(),
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
export function getAdapter() {
|
|
42
|
+
return getGithubAgentDeps().getAdapter();
|
|
43
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GitHub App Bot workflow handlers (Installation Token identity).
|
|
3
|
+
*/
|
|
4
|
+
import type { Message } from 'zhin.js';
|
|
5
|
+
export declare function executeGithubPrepareWorkspace(args: {
|
|
6
|
+
repo?: string;
|
|
7
|
+
}, commMessage?: Message): Promise<string>;
|
|
8
|
+
export declare function executeGithubPatchFile(args: {
|
|
9
|
+
repo?: string;
|
|
10
|
+
path: string;
|
|
11
|
+
content: string;
|
|
12
|
+
message: string;
|
|
13
|
+
branch?: string;
|
|
14
|
+
}, commMessage?: Message): Promise<string>;
|
|
15
|
+
export declare function executeGithubPushBranch(args: {
|
|
16
|
+
repo?: string;
|
|
17
|
+
branch?: string;
|
|
18
|
+
message: string;
|
|
19
|
+
}, commMessage?: Message): Promise<string>;
|
|
20
|
+
export declare function executeGithubCreatePr(args: {
|
|
21
|
+
repo?: string;
|
|
22
|
+
title: string;
|
|
23
|
+
body?: string;
|
|
24
|
+
head?: string;
|
|
25
|
+
base?: string;
|
|
26
|
+
}, commMessage?: Message): Promise<string>;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parse GitHub Issue/PR channel context from IM messages.
|
|
3
|
+
*/
|
|
4
|
+
import type { Message } from 'zhin.js';
|
|
5
|
+
import { type ParsedChannel } from './types.js';
|
|
6
|
+
import type { GhClient } from './gh-client.js';
|
|
7
|
+
export interface GitHubChannelContext extends ParsedChannel {
|
|
8
|
+
channelId: string;
|
|
9
|
+
}
|
|
10
|
+
export declare function parseMessageChannel(message: Message): GitHubChannelContext | null;
|
|
11
|
+
export declare function issueBranchName(number: number): string;
|
|
12
|
+
export declare function resolveWorkspaceBranch(gh: GhClient, ctx: GitHubChannelContext): Promise<{
|
|
13
|
+
branch: string;
|
|
14
|
+
base: string;
|
|
15
|
+
}>;
|
|
16
|
+
export declare function formatChannelContext(ctx: GitHubChannelContext): string;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { Message } from 'zhin.js';
|
|
2
|
+
export declare function executeGithubStar(args: {
|
|
3
|
+
action: 'star' | 'unstar' | 'check';
|
|
4
|
+
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>;
|
|
10
|
+
export declare function executeGithubSubscribe(args: {
|
|
11
|
+
repo: string;
|
|
12
|
+
events?: string;
|
|
13
|
+
}, commMessage?: Message): Promise<string>;
|
|
14
|
+
export declare function executeGithubUnsubscribe(args: {
|
|
15
|
+
repo: string;
|
|
16
|
+
}, commMessage?: Message): Promise<string>;
|
|
17
|
+
export declare function executeGithubSubscriptions(_args: Record<string, never>, commMessage?: Message): Promise<string>;
|