@zhin.js/adapter-github 1.1.17 → 1.1.19
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 +65 -0
- package/README.md +10 -2
- package/adapters/github/index.js +3 -36
- package/adapters/github/index.ts +3 -39
- package/agents/github/skills/github-account/handlers.ts +6 -6
- package/agents/github/skills/github-repository/handlers.js +1 -1
- package/agents/github/skills/github-repository/handlers.ts +7 -7
- package/agents/github/skills/github-subscriptions/handlers.ts +6 -6
- package/commands/github/endpoint/definition.js +0 -2
- package/commands/github/endpoint/definition.ts +0 -2
- package/lib/adapter.d.ts +19 -0
- package/lib/adapter.js +30 -0
- package/lib/client.d.ts +0 -35
- package/lib/client.js +1 -44
- package/lib/console-spec.d.ts +12 -0
- package/lib/console-spec.js +12 -0
- package/lib/console.d.ts +8 -0
- package/lib/console.js +13 -0
- package/lib/endpoint.d.ts +9 -12
- package/lib/endpoint.js +56 -31
- package/lib/gh-client.d.ts +27 -6
- package/lib/gh-client.js +74 -49
- package/lib/index.d.ts +2 -1
- package/lib/index.js +1 -1
- package/lib/webhook.d.ts +1 -1
- package/lib/webhook.js +3 -3
- package/package.json +28 -20
- package/pages/index/index.js +15 -0
- package/pages/index/index.ts +16 -0
- package/plugin.js +3 -4
- package/src/adapter.ts +32 -0
- package/src/client.ts +0 -61
- package/src/console-spec.ts +17 -0
- package/src/console.ts +18 -0
- package/src/endpoint.ts +52 -33
- package/src/gh-client.ts +90 -57
- package/src/index.ts +2 -1
- package/src/webhook.ts +4 -4
- package/lib/github-runtime-state.d.ts +0 -1
- package/lib/github-runtime-state.js +0 -6
- package/src/github-runtime-state.ts +0 -7
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zhin.js/adapter-github",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.19",
|
|
4
4
|
"description": "Zhin.js GitHub adapter for Plugin Runtime (App auth + webhook)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./lib/index.js",
|
|
@@ -39,25 +39,28 @@
|
|
|
39
39
|
"directory": "plugins/adapters/github"
|
|
40
40
|
},
|
|
41
41
|
"dependencies": {
|
|
42
|
-
"@zhin.js/adapter": "1.1.
|
|
43
|
-
"@zhin.js/agent-feature": "1.1.
|
|
44
|
-
"@zhin.js/
|
|
45
|
-
"@zhin.js/
|
|
46
|
-
"@zhin.js/
|
|
47
|
-
"@zhin.js/
|
|
42
|
+
"@zhin.js/adapter": "1.1.26",
|
|
43
|
+
"@zhin.js/agent-feature": "1.1.11",
|
|
44
|
+
"@zhin.js/client": "1.1.22",
|
|
45
|
+
"@zhin.js/console-contract": "1.1.3",
|
|
46
|
+
"@zhin.js/core": "1.1.54",
|
|
47
|
+
"@zhin.js/feature-kit": "1.1.10",
|
|
48
|
+
"@zhin.js/host-http": "1.1.12",
|
|
49
|
+
"@zhin.js/im-contract": "1.1.9",
|
|
48
50
|
"@zhin.js/logger": "1.1.2",
|
|
49
|
-
"@zhin.js/
|
|
50
|
-
"@zhin.js/
|
|
51
|
+
"@zhin.js/page": "1.1.11",
|
|
52
|
+
"@zhin.js/skill": "1.1.11",
|
|
53
|
+
"@zhin.js/tool": "1.1.13"
|
|
51
54
|
},
|
|
52
55
|
"peerDependencies": {
|
|
53
56
|
"zod": "^4.0.0",
|
|
54
|
-
"@zhin.js/adapter": "^1.1.
|
|
55
|
-
"@zhin.js/agent": "^1.1.
|
|
56
|
-
"@zhin.js/command": "^1.1.
|
|
57
|
-
"@zhin.js/core": "^1.1.
|
|
58
|
-
"@zhin.js/host-http": "^1.1.
|
|
59
|
-
"@zhin.js/prompt-section": "^1.1.
|
|
60
|
-
"zhin.js": "^1.1.
|
|
57
|
+
"@zhin.js/adapter": "^1.1.26",
|
|
58
|
+
"@zhin.js/agent": "^1.1.45",
|
|
59
|
+
"@zhin.js/command": "^1.1.12",
|
|
60
|
+
"@zhin.js/core": "^1.1.54",
|
|
61
|
+
"@zhin.js/host-http": "^1.1.12",
|
|
62
|
+
"@zhin.js/prompt-section": "^1.1.10",
|
|
63
|
+
"zhin.js": "^1.1.19"
|
|
61
64
|
},
|
|
62
65
|
"peerDependenciesMeta": {
|
|
63
66
|
"@zhin.js/agent": {
|
|
@@ -81,13 +84,14 @@
|
|
|
81
84
|
"typescript": "^6.0.3",
|
|
82
85
|
"vitest": "^4.1.10",
|
|
83
86
|
"zod": "^4.4.3",
|
|
84
|
-
"@zhin.js/agent": "1.1.
|
|
85
|
-
"@zhin.js/prompt-section": "1.1.
|
|
86
|
-
"zhin.js": "1.1.
|
|
87
|
+
"@zhin.js/agent": "1.1.45",
|
|
88
|
+
"@zhin.js/prompt-section": "1.1.10",
|
|
89
|
+
"zhin.js": "1.1.19"
|
|
87
90
|
},
|
|
88
91
|
"files": [
|
|
89
92
|
"adapters",
|
|
90
93
|
"commands",
|
|
94
|
+
"pages",
|
|
91
95
|
"plugin.js",
|
|
92
96
|
"schema.json",
|
|
93
97
|
"src",
|
|
@@ -113,7 +117,7 @@
|
|
|
113
117
|
"features": [
|
|
114
118
|
{
|
|
115
119
|
"package": "@zhin.js/adapter",
|
|
116
|
-
"api": "^
|
|
120
|
+
"api": "^2.0.0"
|
|
117
121
|
},
|
|
118
122
|
{
|
|
119
123
|
"package": "@zhin.js/command",
|
|
@@ -130,6 +134,10 @@
|
|
|
130
134
|
{
|
|
131
135
|
"package": "@zhin.js/agent-feature",
|
|
132
136
|
"api": "^1.0.0"
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
"package": "@zhin.js/page",
|
|
140
|
+
"api": "^1.0.0"
|
|
133
141
|
}
|
|
134
142
|
],
|
|
135
143
|
"plugins": []
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// Generated by build-plugin-runtime-entries.mjs. Do not edit.
|
|
2
|
+
import { definePage } from '@zhin.js/console-contract';
|
|
3
|
+
import { createAdapterConsolePage } from '@zhin.js/client';
|
|
4
|
+
export const meta = definePage({
|
|
5
|
+
title: 'GitHub',
|
|
6
|
+
titleI18n: { en: 'GitHub' },
|
|
7
|
+
icon: 'Github',
|
|
8
|
+
order: 63,
|
|
9
|
+
requiredPermissions: ['github:read'],
|
|
10
|
+
});
|
|
11
|
+
/**
|
|
12
|
+
* Convention page: `pages/index/index.ts` → `/github`(插件路径)。
|
|
13
|
+
* 连接状态、连接测试、endpoint 增删;登录待办来自 LoginAssist(ICQQ 等)。
|
|
14
|
+
*/
|
|
15
|
+
export default createAdapterConsolePage('github');
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { definePage } from '@zhin.js/console-contract';
|
|
2
|
+
import { createAdapterConsolePage } from '@zhin.js/client';
|
|
3
|
+
|
|
4
|
+
export const meta = definePage({
|
|
5
|
+
title: 'GitHub',
|
|
6
|
+
titleI18n: { en: 'GitHub' },
|
|
7
|
+
icon: 'Github',
|
|
8
|
+
order: 63,
|
|
9
|
+
requiredPermissions: ['github:read'],
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Convention page: `pages/index/index.ts` → `/github`(插件路径)。
|
|
14
|
+
* 连接状态、连接测试、endpoint 增删;登录待办来自 LoginAssist(ICQQ 等)。
|
|
15
|
+
*/
|
|
16
|
+
export default createAdapterConsolePage('github');
|
package/plugin.js
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
// Generated by build-plugin-runtime-entries.mjs. Do not edit.
|
|
2
|
-
import {
|
|
3
|
-
import { definePlugin, databaseHostToken } from 'zhin.js';
|
|
4
|
-
import { githubRuntimeStateToken } from "./lib/github-runtime-state.js";
|
|
2
|
+
import { definePlugin, databaseHostToken, pluginConsoleRpcToken } from 'zhin.js';
|
|
5
3
|
import { defineGithubOauthUsersTable } from "./lib/oauth-users.js";
|
|
4
|
+
import { createGithubConsoleRpc } from "./lib/console.js";
|
|
6
5
|
/**
|
|
7
6
|
* github_subscriptions — repo event subscriptions per chat channel
|
|
8
7
|
* (used by github_subscriptions agent tool; schema is registered through DatabaseHost).
|
|
@@ -28,7 +27,7 @@ export default definePlugin({
|
|
|
28
27
|
},
|
|
29
28
|
setup(context) {
|
|
30
29
|
// 运行中 endpoint 注册表(github endpoint list 的"运行中"数据源)
|
|
31
|
-
context.resources.provide(
|
|
30
|
+
context.resources.provide(pluginConsoleRpcToken, createGithubConsoleRpc(context.resources, () => context.config.get()));
|
|
32
31
|
if (context.resources.has(databaseHostToken)) {
|
|
33
32
|
const host = context.resources.use(databaseHostToken);
|
|
34
33
|
defineGithubOauthUsersTable(host);
|
package/src/adapter.ts
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { GhClient } from './gh-client.js';
|
|
2
|
+
import { Adapter, type AdapterContext, type EndpointCreationContext } from 'zhin.js/adapter';
|
|
3
|
+
import { GithubEndpoint } from './endpoint.js';
|
|
4
|
+
import { resolveGithubConfig, type GithubEndpointConfig } from './protocol.js';
|
|
5
|
+
import type { GithubClientEventMap } from './client.js';
|
|
6
|
+
import { httpHostToken } from '@zhin.js/host-http';
|
|
7
|
+
import { databaseHostToken } from 'zhin.js';
|
|
8
|
+
|
|
9
|
+
export class GithubAdapter extends Adapter<GithubEndpoint, GithubEndpointConfig, GithubClientEventMap> {
|
|
10
|
+
readonly #context: AdapterContext<GithubEndpointConfig>;
|
|
11
|
+
constructor(context: AdapterContext<GithubEndpointConfig>) { super(); this.#context = context; }
|
|
12
|
+
override get configurationIdentityFields(): readonly string[] { return ['id', 'app_id', 'host']; }
|
|
13
|
+
override get configurationUpdateFields(): readonly string[] { return ['private_key', 'webhook_secret', 'webhook_path', 'auto_reply_repos', 'bot_login', 'workspace_root']; }
|
|
14
|
+
override get configurationIdentityProbeFields(): readonly string[] { return ['private_key']; }
|
|
15
|
+
protected override async identifyConfiguration(input: GithubEndpointConfig, signal: AbortSignal, _changed: readonly string[]): Promise<string | undefined> {
|
|
16
|
+
const config = resolveGithubConfig(input), client = new GhClient({ host: config.host, appAuth: { appId: config.appId, privateKey: config.privateKey }, signal });
|
|
17
|
+
const authenticated = await client.verifyAuth(); signal.throwIfAborted();
|
|
18
|
+
if (!authenticated.ok) throw new Error('GitHub application identity verification failed');
|
|
19
|
+
const identity = await client.getBotIdentity(); signal.throwIfAborted();
|
|
20
|
+
if (!identity?.userId) throw new Error('GitHub identity verification returned no bot userId');
|
|
21
|
+
return JSON.stringify(['github', config.host ?? 'github.com', identity.userId]);
|
|
22
|
+
}
|
|
23
|
+
protected override validateConfiguration(config: GithubEndpointConfig): void { resolveGithubConfig(config); }
|
|
24
|
+
protected createEndpoint(input: GithubEndpointConfig, execution: EndpointCreationContext): GithubEndpoint {
|
|
25
|
+
const config = resolveGithubConfig(input);
|
|
26
|
+
return new GithubEndpoint({ id: execution.canonicalId, config, ...(config.webhookSecret ? { http: this.#context.use(httpHostToken) } : {}), ...(this.#context.has?.(databaseHostToken) ? { database: this.#context.use(databaseHostToken) } : {}) });
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
declare module '@zhin.js/feature-kit' {
|
|
31
|
+
interface AdapterRegistry { readonly 'github': GithubAdapter; }
|
|
32
|
+
}
|
package/src/client.ts
CHANGED
|
@@ -1,62 +1 @@
|
|
|
1
|
-
import type { PluginDatabaseHost } from 'zhin.js';
|
|
2
|
-
import { defineEndpointClient } from 'zhin.js/adapter';
|
|
3
|
-
import { GhClient } from './gh-client.js';
|
|
4
|
-
import { lookupGithubOauthAccessToken } from './oauth-users.js';
|
|
5
|
-
import type { ResolvedGithubConfig } from './protocol.js';
|
|
6
|
-
import { WorkspaceManager } from './workspace-manager.js';
|
|
7
|
-
|
|
8
|
-
/** GitHub SDK surface exposed to plugin handlers and Agent tools. */
|
|
9
|
-
export class GithubClient {
|
|
10
|
-
#workspaceManager?: WorkspaceManager;
|
|
11
|
-
|
|
12
|
-
constructor(
|
|
13
|
-
readonly name: string,
|
|
14
|
-
readonly api: GhClient,
|
|
15
|
-
readonly config: ResolvedGithubConfig,
|
|
16
|
-
readonly database?: PluginDatabaseHost,
|
|
17
|
-
) {}
|
|
18
|
-
|
|
19
|
-
async getUserOrDefaultApi(platform?: string, platformUid?: string): Promise<GhClient> {
|
|
20
|
-
if (platform && platformUid) {
|
|
21
|
-
const token = await lookupGithubOauthAccessToken(this.database, platform, platformUid);
|
|
22
|
-
if (token) return this.api.withToken(token);
|
|
23
|
-
}
|
|
24
|
-
return this.api;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
get clientId(): string | null {
|
|
28
|
-
return this.api.clientId || null;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
get host(): string | undefined {
|
|
32
|
-
return this.config.host;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
get appSlug(): string | null {
|
|
36
|
-
return this.api.appSlug || null;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
get installations() {
|
|
40
|
-
return this.api.installations || [];
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
get workspaceManager(): WorkspaceManager {
|
|
44
|
-
if (!this.#workspaceManager) {
|
|
45
|
-
this.#workspaceManager = new WorkspaceManager(this.api, this.config.workspaceRoot);
|
|
46
|
-
}
|
|
47
|
-
return this.#workspaceManager;
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
|
|
51
1
|
export type GithubClientEventMap = Record<string, unknown>;
|
|
52
|
-
|
|
53
|
-
declare module '@zhin.js/feature-kit' {
|
|
54
|
-
interface AdapterClientRegistry {
|
|
55
|
-
readonly github: {
|
|
56
|
-
readonly client: GithubClient;
|
|
57
|
-
readonly events: GithubClientEventMap;
|
|
58
|
-
};
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
export const githubClient = defineEndpointClient<GithubClient, GithubClientEventMap>('github');
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GitHub Console 页面元数据(浏览器与服务端共享;不要在这里 import 服务端代码)。
|
|
3
|
+
*/
|
|
4
|
+
import type { AdapterConsoleField } from 'zhin.js/adapter';
|
|
5
|
+
|
|
6
|
+
export const githubConsoleSpec = {
|
|
7
|
+
adapterKey: 'github',
|
|
8
|
+
adapterDisplayName: 'GitHub',
|
|
9
|
+
fields: [
|
|
10
|
+
{ key: 'app_id', required: true, env: true, description: 'GitHub App ID' },
|
|
11
|
+
{ key: 'private_key', required: true, secret: true, description: 'PEM 文件路径(推荐,如 ./data/xxx.pem)或 PEM 内容', descriptionI18n: { en: 'PEM file path (recommended) or PEM content' } },
|
|
12
|
+
{ key: 'webhook_secret', env: true, description: 'webhook 签名密钥(不配则 API-only)', descriptionI18n: { en: 'Webhook signing secret (API-only when omitted)' } },
|
|
13
|
+
] as readonly AdapterConsoleField[],
|
|
14
|
+
docsUrl: 'https://zhin.js.org/adapters/github',
|
|
15
|
+
readPermission: 'github:read',
|
|
16
|
+
writePermission: 'github:write',
|
|
17
|
+
} as const;
|
package/src/console.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GitHub Console RPC:运行状态 + endpoint 增删(原 `github endpoint` 聊天命令)。
|
|
3
|
+
*
|
|
4
|
+
* 页面绑定 `pages/index`,读取 / 写入分别要求 `<adapter>:read` / `<adapter>:write`。
|
|
5
|
+
*/
|
|
6
|
+
import { createAdapterConsoleRpc, type AdapterConsoleResources } from 'zhin.js/adapter';
|
|
7
|
+
import type { PluginConsoleRpc } from 'zhin.js';
|
|
8
|
+
import { githubConsoleSpec } from './console-spec.js';
|
|
9
|
+
|
|
10
|
+
export function createGithubConsoleRpc(
|
|
11
|
+
_resources: AdapterConsoleResources,
|
|
12
|
+
getConfig: () => unknown,
|
|
13
|
+
): PluginConsoleRpc {
|
|
14
|
+
return createAdapterConsoleRpc({
|
|
15
|
+
...githubConsoleSpec,
|
|
16
|
+
getConfig,
|
|
17
|
+
});
|
|
18
|
+
}
|
package/src/endpoint.ts
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
import { Endpoint } from 'zhin.js/adapter';
|
|
1
|
+
import { createEndpointSpecification, endpointSpec, type Endpoint, type EndpointSendRequest } from 'zhin.js/adapter';
|
|
2
2
|
/**
|
|
3
3
|
* GithubEndpoint — lifecycle, outbound send, inbound admit.
|
|
4
4
|
*/
|
|
5
|
-
import { type EndpointSendRequest } from 'zhin.js/adapter';
|
|
6
5
|
import type { HttpHost, HttpRouteRegistration } from '@zhin.js/host-http';
|
|
7
6
|
import { formatCompact, getAdapterLogger } from '@zhin.js/logger';
|
|
8
7
|
import type { CapabilityId, PluginDatabaseHost } from 'zhin.js';
|
|
@@ -17,98 +16,110 @@ import {
|
|
|
17
16
|
type ResolvedGithubConfig,
|
|
18
17
|
} from './protocol.js';
|
|
19
18
|
import { registerGithubWebhookRoutes } from './webhook.js';
|
|
20
|
-
import {
|
|
19
|
+
import { WorkspaceManager } from './workspace-manager.js';
|
|
20
|
+
import { lookupGithubOauthAccessToken } from './oauth-users.js';
|
|
21
21
|
|
|
22
22
|
export interface GithubEndpointOptions {
|
|
23
23
|
readonly id: CapabilityId;
|
|
24
24
|
readonly http?: HttpHost;
|
|
25
25
|
readonly database?: PluginDatabaseHost;
|
|
26
26
|
readonly config: ResolvedGithubConfig;
|
|
27
|
-
readonly createClient?: (config: ResolvedGithubConfig) => GhClient;
|
|
28
27
|
}
|
|
29
28
|
|
|
30
29
|
/**
|
|
31
30
|
* GitHub 是代码协作面(issue/PR),无好友/群/频道等 IM 社交概念,
|
|
32
31
|
* 不适用 EndpointManagement 语义端口;本 endpoint 不暴露该端口。
|
|
33
32
|
*/
|
|
34
|
-
export class GithubEndpoint extends Endpoint
|
|
33
|
+
export class GithubEndpoint extends GhClient implements Endpoint {
|
|
34
|
+
readonly [endpointSpec] = createEndpointSpecification(this, {
|
|
35
|
+
start: signal => this.#startFramework(signal), open: () => this.#openFramework(), close: () => this.#closeFramework(),
|
|
36
|
+
stop: () => this.#stopFramework(), send: request => this.#sendFramework(request),
|
|
37
|
+
});
|
|
38
|
+
#workspaceManager?: WorkspaceManager;
|
|
39
|
+
get database() { return this.#options.database; }
|
|
40
|
+
get host(): string | undefined { return this.#options.config.host; }
|
|
41
|
+
async getUserOrDefaultApi(platform?: string, platformUid?: string): Promise<GhClient> {
|
|
42
|
+
if (platform && platformUid) { const token = await lookupGithubOauthAccessToken(this.database, platform, platformUid); if (token) return this.withToken(token); }
|
|
43
|
+
return this;
|
|
44
|
+
}
|
|
45
|
+
get workspaceManager(): WorkspaceManager { return this.#workspaceManager ??= new WorkspaceManager(this, this.config.workspaceRoot); }
|
|
35
46
|
readonly #logger!: ReturnType<typeof getAdapterLogger>;
|
|
36
47
|
|
|
37
48
|
readonly #options: GithubEndpointOptions;
|
|
38
|
-
readonly client: GithubClient;
|
|
39
49
|
#routeReleases: HttpRouteRegistration[] = [];
|
|
40
50
|
#open = false;
|
|
41
51
|
#started = false;
|
|
42
52
|
|
|
43
53
|
constructor(options: GithubEndpointOptions) {
|
|
44
|
-
super();
|
|
54
|
+
super({ host: options.config.host, appAuth: { appId: options.config.appId, privateKey: options.config.privateKey } });
|
|
45
55
|
this.#logger = getAdapterLogger('github', options.config.id);
|
|
46
56
|
this.#options = options;
|
|
47
|
-
|
|
48
|
-
this.client = new GithubClient(options.config.id, api, options.config, options.database);
|
|
57
|
+
|
|
49
58
|
}
|
|
50
59
|
|
|
51
60
|
get config(): ResolvedGithubConfig {
|
|
52
|
-
return this.
|
|
61
|
+
return this.#options.config;
|
|
53
62
|
}
|
|
54
63
|
|
|
55
|
-
async
|
|
64
|
+
async #startFramework(signal: AbortSignal): Promise<void> {
|
|
65
|
+
this.bindExecutionSignal(signal); signal.throwIfAborted();
|
|
56
66
|
if (this.#started) return;
|
|
57
67
|
this.#started = true;
|
|
58
68
|
try {
|
|
59
|
-
const result = await this.
|
|
69
|
+
const result = await this.verifyAuth();
|
|
70
|
+
signal.throwIfAborted();
|
|
60
71
|
if (!result.ok) throw new Error(`GitHub 认证失败: ${result.message}`);
|
|
61
|
-
if (this.
|
|
72
|
+
if (this.#options.config.webhookSecret) {
|
|
62
73
|
if (!this.#options.http) {
|
|
63
74
|
throw new TypeError('GitHub webhook_secret requires httpHostToken');
|
|
64
75
|
}
|
|
65
76
|
this.#routeReleases.push(...registerGithubWebhookRoutes(this.#options.http, this));
|
|
66
77
|
this.#logger.debug(formatCompact({
|
|
67
|
-
endpoint: this.
|
|
78
|
+
endpoint: this.#options.config.id,
|
|
68
79
|
op: 'webhook',
|
|
69
|
-
path: this.
|
|
80
|
+
path: this.#options.config.webhookPath,
|
|
70
81
|
}));
|
|
71
82
|
} else {
|
|
72
83
|
this.#logger.debug(formatCompact({
|
|
73
|
-
endpoint: this.
|
|
84
|
+
endpoint: this.#options.config.id,
|
|
74
85
|
op: 'connect',
|
|
75
86
|
mode: 'api-only',
|
|
76
|
-
bot: this.
|
|
87
|
+
bot: this.authenticatedUser,
|
|
77
88
|
}));
|
|
78
89
|
}
|
|
79
90
|
} catch (error) {
|
|
80
|
-
await this
|
|
91
|
+
await this.#stopFramework();
|
|
81
92
|
this.#logger.error('Failed to connect GitHub endpoint:', error);
|
|
82
93
|
throw error;
|
|
83
94
|
}
|
|
84
95
|
}
|
|
85
96
|
|
|
86
|
-
|
|
97
|
+
#openFramework(): void {
|
|
87
98
|
this.#open = true;
|
|
88
99
|
}
|
|
89
100
|
|
|
90
|
-
|
|
101
|
+
#closeFramework(): void {
|
|
91
102
|
this.#open = false;
|
|
92
103
|
}
|
|
93
104
|
|
|
94
|
-
async
|
|
105
|
+
async #stopFramework(): Promise<void> {
|
|
95
106
|
this.#open = false;
|
|
96
107
|
for (const release of this.#routeReleases.splice(0)) release();
|
|
97
108
|
this.#started = false;
|
|
98
109
|
this.#logger.debug(formatCompact({ op: 'disconnect' }));
|
|
99
110
|
}
|
|
100
111
|
|
|
101
|
-
async
|
|
112
|
+
async #sendFramework({ conversation, payload, signal }: EndpointSendRequest): Promise<string> {
|
|
102
113
|
const parsed = parseChannelId(conversation.id);
|
|
103
114
|
if (!parsed) throw new Error(`无效的 GitHub conversation ID: ${conversation.id}`);
|
|
104
115
|
const text = formatOutboundBody(payload);
|
|
105
116
|
const r = parsed.type === 'issue'
|
|
106
|
-
? await this.
|
|
107
|
-
: await this.
|
|
117
|
+
? await this.createIssueComment({ repo: parsed.repo, number: parsed.number, body: text, signal })
|
|
118
|
+
: await this.createPRComment({ repo: parsed.repo, number: parsed.number, body: text, signal });
|
|
108
119
|
if (!r.ok) throw new Error(`发送失败: ${JSON.stringify(r.data)}`);
|
|
109
120
|
this.#logger.debug(formatCompact({
|
|
110
121
|
op: 'github_send',
|
|
111
|
-
endpoint: this.
|
|
122
|
+
endpoint: this.#options.config.id,
|
|
112
123
|
target: `${conversation.kind}:${conversation.id}`,
|
|
113
124
|
messageId: r.data.id,
|
|
114
125
|
}));
|
|
@@ -116,25 +127,33 @@ export class GithubEndpoint extends Endpoint<GithubClient> {
|
|
|
116
127
|
}
|
|
117
128
|
|
|
118
129
|
/** Test / internal: admit a parsed comment when open. */
|
|
119
|
-
admit(comment: GithubInboundComment): void {
|
|
130
|
+
admit(comment: GithubInboundComment, nativeEvent: unknown = comment): void {
|
|
131
|
+
if (!this.#open) return;
|
|
132
|
+
void this[endpointSpec].events.run(() => this.#admitComment(comment, nativeEvent)).catch(error => {
|
|
133
|
+
this.#logger.warn(formatCompact({ op: 'github_gateway_receive_failed', error: error instanceof Error ? error.message : String(error) }));
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
#admitComment(comment: GithubInboundComment, nativeEvent: unknown = comment): void {
|
|
120
138
|
if (!this.#open) return;
|
|
121
|
-
const botUser = this.
|
|
122
|
-
|| this.
|
|
123
|
-
|| this.
|
|
139
|
+
const botUser = this.#options.config.botLogin
|
|
140
|
+
|| this.getBotLogin()
|
|
141
|
+
|| this.authenticatedUser;
|
|
124
142
|
if (botUser && comment.sender === botUser) return;
|
|
125
143
|
const conversation = githubInboundConversation(String(this.#options.id), comment);
|
|
126
144
|
const content = enrichInboundContent(
|
|
127
145
|
formatInboundContent(comment.content),
|
|
128
|
-
this.
|
|
146
|
+
this.#options.config,
|
|
129
147
|
botUser ?? undefined,
|
|
130
148
|
comment.repo,
|
|
131
149
|
);
|
|
132
|
-
void this.emit('message.receive', {
|
|
150
|
+
void this[endpointSpec].events.emit('message.receive', {
|
|
151
|
+
event: nativeEvent,
|
|
133
152
|
conversation,
|
|
134
153
|
message: { conversation, id: comment.id },
|
|
135
154
|
content,
|
|
136
155
|
sender: { id: comment.sender, name: comment.sender },
|
|
137
|
-
endpointId: this.
|
|
156
|
+
endpointId: this.#options.config.id,
|
|
138
157
|
metadata: Object.freeze({
|
|
139
158
|
repo: comment.repo,
|
|
140
159
|
kind: comment.kind,
|
|
@@ -151,7 +170,7 @@ export class GithubEndpoint extends Endpoint<GithubClient> {
|
|
|
151
170
|
|
|
152
171
|
admitPlatform(name: string, event: unknown): void {
|
|
153
172
|
if (!this.#open) return;
|
|
154
|
-
void this.
|
|
173
|
+
void this[endpointSpec].events.run(() => this[endpointSpec].events.platform(name, event)).catch((error) => {
|
|
155
174
|
this.#logger.warn(formatCompact({
|
|
156
175
|
op: 'github_platform_event_failed',
|
|
157
176
|
event: name,
|