@zhin.js/adapter-github 5.0.2 → 5.0.5
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 +90 -0
- package/README.md +2 -2
- package/adapters/github.js +2 -2
- package/adapters/github.ts +4 -4
- package/lib/endpoint.d.ts +5 -8
- package/lib/endpoint.js +21 -20
- package/lib/github-agent-deps.d.ts +2 -2
- package/lib/github-agent-deps.js +8 -8
- package/lib/oauth-users.d.ts +2 -2
- package/lib/protocol.d.ts +9 -2
- package/lib/protocol.js +16 -3
- package/package.json +16 -15
- package/schema.json +40 -4
- package/src/endpoint.ts +26 -24
- package/src/github-agent-deps.ts +9 -9
- package/src/oauth-users.ts +2 -2
- package/src/protocol.ts +23 -5
- /package/commands/endpoint/add/{[name:string].js → [id].js} +0 -0
- /package/commands/endpoint/add/{[name:string].ts → [id].ts} +0 -0
- /package/commands/endpoint/remove/{[name:string].js → [id].js} +0 -0
- /package/commands/endpoint/remove/{[name:string].ts → [id].ts} +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,95 @@
|
|
|
1
1
|
# @zhin.js/adapter-github
|
|
2
2
|
|
|
3
|
+
## 5.0.5
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 36c7400: Replace `AgentPromptBuildContext.commMessage` with an authenticated platform projection and make Agent prompt assembly consume canonical Turn identity. Platform contributors and prompt hooks no longer receive a classic IM `Message`.
|
|
8
|
+
|
|
9
|
+
Remove the unused Message field and active-context escape hatch from PromptController; turn scheduling is keyed only by canonical session identity.
|
|
10
|
+
|
|
11
|
+
- Updated dependencies [6f9c366]
|
|
12
|
+
- Updated dependencies [373a56b]
|
|
13
|
+
- Updated dependencies [0de46a8]
|
|
14
|
+
- Updated dependencies [c106ecc]
|
|
15
|
+
- Updated dependencies [ca92e03]
|
|
16
|
+
- Updated dependencies [b0f37ae]
|
|
17
|
+
- Updated dependencies [ba08a2f]
|
|
18
|
+
- Updated dependencies [b08f7fe]
|
|
19
|
+
- Updated dependencies [daffd4c]
|
|
20
|
+
- Updated dependencies [36c7400]
|
|
21
|
+
- Updated dependencies [a9fa72e]
|
|
22
|
+
- Updated dependencies [c8de3ef]
|
|
23
|
+
- Updated dependencies [60f0fc8]
|
|
24
|
+
- Updated dependencies [574c990]
|
|
25
|
+
- Updated dependencies [d096f16]
|
|
26
|
+
- Updated dependencies [3f29623]
|
|
27
|
+
- Updated dependencies [2916852]
|
|
28
|
+
- Updated dependencies [d047869]
|
|
29
|
+
- Updated dependencies [162fa34]
|
|
30
|
+
- Updated dependencies [e62561e]
|
|
31
|
+
- Updated dependencies [3eeeb46]
|
|
32
|
+
- Updated dependencies [85d0f82]
|
|
33
|
+
- Updated dependencies [61bfc1c]
|
|
34
|
+
- Updated dependencies [04bad47]
|
|
35
|
+
- Updated dependencies [e40b048]
|
|
36
|
+
- Updated dependencies [5a5b1bb]
|
|
37
|
+
- Updated dependencies [f1708c3]
|
|
38
|
+
- Updated dependencies [d254a81]
|
|
39
|
+
- Updated dependencies [7123c47]
|
|
40
|
+
- Updated dependencies [05befc1]
|
|
41
|
+
- Updated dependencies [0663b6a]
|
|
42
|
+
- Updated dependencies [f28f9b3]
|
|
43
|
+
- Updated dependencies [9f340f7]
|
|
44
|
+
- Updated dependencies [e53444f]
|
|
45
|
+
- Updated dependencies [5eedd26]
|
|
46
|
+
- Updated dependencies [92b0dd7]
|
|
47
|
+
- Updated dependencies [f919b6f]
|
|
48
|
+
- Updated dependencies [098e411]
|
|
49
|
+
- Updated dependencies [e1b7c01]
|
|
50
|
+
- Updated dependencies [9b94f87]
|
|
51
|
+
- Updated dependencies [a7df753]
|
|
52
|
+
- @zhin.js/agent@1.1.5
|
|
53
|
+
- @zhin.js/host-http@1.0.8
|
|
54
|
+
- @zhin.js/im-contract@1.0.3
|
|
55
|
+
- @zhin.js/adapter@1.1.7
|
|
56
|
+
- @zhin.js/plugin-runtime@1.1.5
|
|
57
|
+
- @zhin.js/command@1.0.9
|
|
58
|
+
- @zhin.js/core@1.5.4
|
|
59
|
+
- zhin.js@6.0.4
|
|
60
|
+
|
|
61
|
+
## 5.0.4
|
|
62
|
+
|
|
63
|
+
### Patch Changes
|
|
64
|
+
|
|
65
|
+
- f8c7a54: fix: im
|
|
66
|
+
- Updated dependencies [f8c7a54]
|
|
67
|
+
- @zhin.js/logger@1.0.76
|
|
68
|
+
- @zhin.js/host-http@1.0.7
|
|
69
|
+
- @zhin.js/adapter@1.1.6
|
|
70
|
+
- @zhin.js/agent@1.1.4
|
|
71
|
+
- @zhin.js/command@1.0.8
|
|
72
|
+
- @zhin.js/core@1.5.3
|
|
73
|
+
- @zhin.js/im-contract@1.0.2
|
|
74
|
+
- @zhin.js/plugin-runtime@1.1.4
|
|
75
|
+
- zhin.js@6.0.3
|
|
76
|
+
|
|
77
|
+
## 5.0.3
|
|
78
|
+
|
|
79
|
+
### Patch Changes
|
|
80
|
+
|
|
81
|
+
- Updated dependencies [afc0e66]
|
|
82
|
+
- Updated dependencies [2e41ad5]
|
|
83
|
+
- Updated dependencies [9f57124]
|
|
84
|
+
- @zhin.js/core@1.5.2
|
|
85
|
+
- @zhin.js/adapter@1.1.5
|
|
86
|
+
- @zhin.js/im-contract@1.0.1
|
|
87
|
+
- @zhin.js/plugin-runtime@1.1.3
|
|
88
|
+
- @zhin.js/command@1.0.7
|
|
89
|
+
- @zhin.js/host-http@1.0.6
|
|
90
|
+
- @zhin.js/agent@1.1.3
|
|
91
|
+
- zhin.js@6.0.2
|
|
92
|
+
|
|
3
93
|
## 5.0.2
|
|
4
94
|
|
|
5
95
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -6,7 +6,7 @@ GitHub Plugin Runtime 适配器 — Issue/PR 评论区即聊天通道,GitHub A
|
|
|
6
6
|
|
|
7
7
|
- **聊天通道**:Issue/PR 评论区映射为群聊,支持收发消息
|
|
8
8
|
- **Webhook 入站**:HMAC-SHA256 验签 → `messageGatewayToken`
|
|
9
|
-
- **出站**:`send({
|
|
9
|
+
- **出站**:`send({ conversation, payload })` → Issue/PR comment(`conversation.id` 为 channel ID)
|
|
10
10
|
- **GitHub App 认证**:JWT → Installation Token
|
|
11
11
|
- **Agent 工具**:`agent/` 下 star/bind/subscribe/workspace 等保留
|
|
12
12
|
|
|
@@ -85,7 +85,7 @@ plugins:
|
|
|
85
85
|
| `src/gh-client.ts` | GitHub API 客户端 |
|
|
86
86
|
|
|
87
87
|
- 入站:`httpHostToken` POST → `messageGatewayToken.receive`
|
|
88
|
-
- 出站:`send({
|
|
88
|
+
- 出站:`send({ conversation, payload })`
|
|
89
89
|
|
|
90
90
|
## License
|
|
91
91
|
|
package/adapters/github.js
CHANGED
|
@@ -30,8 +30,8 @@ export default defineAdapter({
|
|
|
30
30
|
const config = resolveGithubConfig(context.config);
|
|
31
31
|
const database = optionalDatabase(context);
|
|
32
32
|
// 注册到插件运行时状态(github.endpoint list 的"运行中"数据源)
|
|
33
|
-
context.use(githubRuntimeStateToken).endpoints.set(config.
|
|
34
|
-
|
|
33
|
+
context.use(githubRuntimeStateToken).endpoints.set(config.id, {
|
|
34
|
+
id: config.id,
|
|
35
35
|
mode: config.webhookSecret ? 'webhook' : 'api',
|
|
36
36
|
});
|
|
37
37
|
if (config.webhookSecret) {
|
package/adapters/github.ts
CHANGED
|
@@ -7,7 +7,7 @@ import { messageGatewayToken } from '@zhin.js/core/runtime';
|
|
|
7
7
|
import { httpHostToken } from '@zhin.js/host-http';
|
|
8
8
|
import {
|
|
9
9
|
databaseHostToken,
|
|
10
|
-
type
|
|
10
|
+
type PluginDatabaseHost,
|
|
11
11
|
} from '@zhin.js/plugin-runtime';
|
|
12
12
|
import { GithubEndpoint } from '../src/endpoint.js';
|
|
13
13
|
import { githubRuntimeStateToken } from '../src/github-runtime-state.js';
|
|
@@ -19,7 +19,7 @@ import {
|
|
|
19
19
|
export { GithubEndpoint } from '../src/endpoint.js';
|
|
20
20
|
export type { GithubEndpointOptions } from '../src/endpoint.js';
|
|
21
21
|
|
|
22
|
-
function optionalDatabase(context: AdapterContext):
|
|
22
|
+
function optionalDatabase(context: AdapterContext): PluginDatabaseHost | undefined {
|
|
23
23
|
try {
|
|
24
24
|
return context.use(databaseHostToken);
|
|
25
25
|
} catch {
|
|
@@ -38,8 +38,8 @@ export default defineAdapter<GithubAdapterConfig>({
|
|
|
38
38
|
const config = resolveGithubConfig(context.config);
|
|
39
39
|
const database = optionalDatabase(context);
|
|
40
40
|
// 注册到插件运行时状态(github.endpoint list 的"运行中"数据源)
|
|
41
|
-
context.use(githubRuntimeStateToken).endpoints.set(config.
|
|
42
|
-
|
|
41
|
+
context.use(githubRuntimeStateToken).endpoints.set(config.id, {
|
|
42
|
+
id: config.id,
|
|
43
43
|
mode: config.webhookSecret ? 'webhook' : 'api',
|
|
44
44
|
});
|
|
45
45
|
if (config.webhookSecret) {
|
package/lib/endpoint.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import type { EndpointInstance } from '@zhin.js/adapter';
|
|
1
|
+
import type { EndpointInstance, EndpointSendRequest } from '@zhin.js/adapter';
|
|
2
2
|
import type { MessageGateway } from '@zhin.js/core/runtime';
|
|
3
3
|
import type { HttpHost } from '@zhin.js/host-http';
|
|
4
|
-
import type { CapabilityId,
|
|
4
|
+
import type { CapabilityId, PluginDatabaseHost } from '@zhin.js/plugin-runtime';
|
|
5
5
|
import { GhClient } from './gh-client.js';
|
|
6
6
|
import { type GithubInboundComment, type ResolvedGithubConfig } from './protocol.js';
|
|
7
7
|
import { WorkspaceManager } from './workspace-manager.js';
|
|
@@ -9,7 +9,7 @@ export interface GithubEndpointOptions {
|
|
|
9
9
|
readonly id: CapabilityId;
|
|
10
10
|
readonly gateway: MessageGateway;
|
|
11
11
|
readonly http?: HttpHost;
|
|
12
|
-
readonly database?:
|
|
12
|
+
readonly database?: PluginDatabaseHost;
|
|
13
13
|
readonly config: ResolvedGithubConfig;
|
|
14
14
|
readonly createClient?: (config: ResolvedGithubConfig) => GhClient;
|
|
15
15
|
}
|
|
@@ -37,15 +37,12 @@ export declare class GithubEndpoint implements EndpointInstance {
|
|
|
37
37
|
target_type: string;
|
|
38
38
|
}[];
|
|
39
39
|
getWorkspaceManager(): WorkspaceManager;
|
|
40
|
-
getDatabase():
|
|
40
|
+
getDatabase(): PluginDatabaseHost | undefined;
|
|
41
41
|
start(): Promise<void>;
|
|
42
42
|
open(): void;
|
|
43
43
|
close(): void;
|
|
44
44
|
stop(): Promise<void>;
|
|
45
|
-
send({
|
|
46
|
-
readonly target: string;
|
|
47
|
-
readonly payload: unknown;
|
|
48
|
-
}): Promise<string>;
|
|
45
|
+
send({ conversation, payload }: EndpointSendRequest): Promise<string>;
|
|
49
46
|
/** Test / internal: admit a parsed comment when open. */
|
|
50
47
|
admit(comment: GithubInboundComment): void;
|
|
51
48
|
}
|
package/lib/endpoint.js
CHANGED
|
@@ -2,19 +2,19 @@
|
|
|
2
2
|
* GithubEndpoint — lifecycle, outbound send, inbound admit.
|
|
3
3
|
*/
|
|
4
4
|
import path from 'node:path';
|
|
5
|
-
import { formatCompact,
|
|
5
|
+
import { formatCompact, getAdapterLogger } from '@zhin.js/logger';
|
|
6
6
|
import { GhClient } from './gh-client.js';
|
|
7
7
|
import { registerGithubAgentEndpoint } from './github-agent-deps.js';
|
|
8
8
|
import { lookupGithubOauthAccessToken } from './oauth-users.js';
|
|
9
|
-
import { enrichInboundContent, formatInboundContent, formatOutboundBody, parseChannelId, } from './protocol.js';
|
|
9
|
+
import { enrichInboundContent, formatInboundContent, formatOutboundBody, githubInboundConversation, parseChannelId, } from './protocol.js';
|
|
10
10
|
import { registerGithubWebhookRoutes } from './webhook.js';
|
|
11
11
|
import { WorkspaceManager } from './workspace-manager.js';
|
|
12
|
-
const logger = getLogger('github');
|
|
13
12
|
/**
|
|
14
13
|
* GitHub 是代码协作面(issue/PR),无好友/群/频道等 IM 社交概念,
|
|
15
14
|
* 不适用 EndpointManagement 语义端口;本 endpoint 不暴露该端口。
|
|
16
15
|
*/
|
|
17
16
|
export class GithubEndpoint {
|
|
17
|
+
#logger;
|
|
18
18
|
#options;
|
|
19
19
|
gh;
|
|
20
20
|
config;
|
|
@@ -25,9 +25,10 @@ export class GithubEndpoint {
|
|
|
25
25
|
#started = false;
|
|
26
26
|
#unregisterAgent;
|
|
27
27
|
constructor(options) {
|
|
28
|
+
this.#logger = getAdapterLogger('github', options.config.id);
|
|
28
29
|
this.#options = options;
|
|
29
30
|
this.config = options.config;
|
|
30
|
-
this.name = options.config.
|
|
31
|
+
this.name = options.config.id;
|
|
31
32
|
this.gh = options.createClient?.(options.config) ?? defaultCreateClient(options.config);
|
|
32
33
|
}
|
|
33
34
|
getAPI() {
|
|
@@ -78,14 +79,14 @@ export class GithubEndpoint {
|
|
|
78
79
|
throw new TypeError('GitHub webhook_secret requires httpHostToken');
|
|
79
80
|
}
|
|
80
81
|
this.#routeReleases.push(...registerGithubWebhookRoutes(this.#options.http, this));
|
|
81
|
-
logger.debug(formatCompact({
|
|
82
|
+
this.#logger.debug(formatCompact({
|
|
82
83
|
endpoint: this.name,
|
|
83
84
|
op: 'webhook',
|
|
84
85
|
path: this.config.webhookPath,
|
|
85
86
|
}));
|
|
86
87
|
}
|
|
87
88
|
else {
|
|
88
|
-
logger.debug(formatCompact({
|
|
89
|
+
this.#logger.debug(formatCompact({
|
|
89
90
|
endpoint: this.name,
|
|
90
91
|
op: 'connect',
|
|
91
92
|
mode: 'api-only',
|
|
@@ -95,7 +96,7 @@ export class GithubEndpoint {
|
|
|
95
96
|
}
|
|
96
97
|
catch (error) {
|
|
97
98
|
await this.stop();
|
|
98
|
-
logger.error('Failed to connect GitHub endpoint:', error);
|
|
99
|
+
this.#logger.error('Failed to connect GitHub endpoint:', error);
|
|
99
100
|
throw error;
|
|
100
101
|
}
|
|
101
102
|
}
|
|
@@ -112,22 +113,22 @@ export class GithubEndpoint {
|
|
|
112
113
|
this.#unregisterAgent?.();
|
|
113
114
|
this.#unregisterAgent = undefined;
|
|
114
115
|
this.#started = false;
|
|
115
|
-
logger.debug(formatCompact({ op: 'disconnect'
|
|
116
|
+
this.#logger.debug(formatCompact({ op: 'disconnect' }));
|
|
116
117
|
}
|
|
117
|
-
async send({
|
|
118
|
-
const parsed = parseChannelId(
|
|
118
|
+
async send({ conversation, payload }) {
|
|
119
|
+
const parsed = parseChannelId(conversation.id);
|
|
119
120
|
if (!parsed)
|
|
120
|
-
throw new Error(`无效的 GitHub
|
|
121
|
+
throw new Error(`无效的 GitHub conversation ID: ${conversation.id}`);
|
|
121
122
|
const text = formatOutboundBody(payload);
|
|
122
123
|
const r = parsed.type === 'issue'
|
|
123
124
|
? await this.gh.createIssueComment(parsed.repo, parsed.number, text)
|
|
124
125
|
: await this.gh.createPRComment(parsed.repo, parsed.number, text);
|
|
125
126
|
if (!r.ok)
|
|
126
127
|
throw new Error(`发送失败: ${JSON.stringify(r.data)}`);
|
|
127
|
-
logger.debug(formatCompact({
|
|
128
|
+
this.#logger.debug(formatCompact({
|
|
128
129
|
op: 'github_send',
|
|
129
130
|
endpoint: this.name,
|
|
130
|
-
target
|
|
131
|
+
target: `${conversation.kind}:${conversation.id}`,
|
|
131
132
|
messageId: r.data.id,
|
|
132
133
|
}));
|
|
133
134
|
return String(r.data.id);
|
|
@@ -139,23 +140,23 @@ export class GithubEndpoint {
|
|
|
139
140
|
const botUser = this.config.botLogin || this.gh.getBotLogin() || this.gh.authenticatedUser;
|
|
140
141
|
if (botUser && comment.sender === botUser)
|
|
141
142
|
return;
|
|
143
|
+
const conversation = githubInboundConversation(String(this.#options.id), comment);
|
|
142
144
|
const content = enrichInboundContent(formatInboundContent(comment.content), this.config, botUser ?? undefined, comment.repo);
|
|
143
145
|
void this.#options.gateway.receive({
|
|
144
|
-
|
|
145
|
-
|
|
146
|
+
conversation,
|
|
147
|
+
message: { conversation, id: comment.id },
|
|
146
148
|
content,
|
|
147
|
-
sender: comment.sender,
|
|
148
|
-
|
|
149
|
+
sender: { id: comment.sender, name: comment.sender },
|
|
150
|
+
endpointId: this.name,
|
|
149
151
|
metadata: Object.freeze({
|
|
150
|
-
endpoint: this.name,
|
|
151
152
|
repo: comment.repo,
|
|
152
153
|
kind: comment.kind,
|
|
153
154
|
createdAt: comment.createdAt,
|
|
154
155
|
}),
|
|
155
156
|
}).catch((err) => {
|
|
156
|
-
logger.warn(formatCompact({
|
|
157
|
+
this.#logger.warn(formatCompact({
|
|
157
158
|
op: 'github_gateway_receive_failed',
|
|
158
|
-
target:
|
|
159
|
+
target: `${conversation.kind}:${conversation.id}`,
|
|
159
160
|
error: err instanceof Error ? err.message : String(err),
|
|
160
161
|
}));
|
|
161
162
|
});
|
|
@@ -27,7 +27,7 @@ export interface GithubAgentEndpoint {
|
|
|
27
27
|
getDatabase?(): unknown;
|
|
28
28
|
}
|
|
29
29
|
export interface GithubAgentDeps {
|
|
30
|
-
getEndpoint: (
|
|
30
|
+
getEndpoint: (endpointKey?: string) => GithubAgentEndpoint;
|
|
31
31
|
/** Alias kept for existing agent handlers that call getAdapter(). */
|
|
32
32
|
getAdapter: () => GithubAgentEndpoint;
|
|
33
33
|
getWorkspaceManager: () => WorkspaceManager;
|
|
@@ -40,7 +40,7 @@ export interface GithubAgentDeps {
|
|
|
40
40
|
error: (...args: unknown[]) => void;
|
|
41
41
|
};
|
|
42
42
|
}
|
|
43
|
-
export declare function registerGithubAgentEndpoint(
|
|
43
|
+
export declare function registerGithubAgentEndpoint(endpointKey: string, endpoint: GithubAgentEndpoint): () => void;
|
|
44
44
|
/** Optional override used by tests / transitional callers. Pass `null` to clear. */
|
|
45
45
|
export declare function setGithubAgentDeps(deps: GithubAgentDeps | null): void;
|
|
46
46
|
export declare function getGithubAgentDeps(): GithubAgentDeps;
|
package/lib/github-agent-deps.js
CHANGED
|
@@ -4,11 +4,11 @@
|
|
|
4
4
|
*/
|
|
5
5
|
const endpoints = new Map();
|
|
6
6
|
let override = null;
|
|
7
|
-
export function registerGithubAgentEndpoint(
|
|
8
|
-
endpoints.set(
|
|
7
|
+
export function registerGithubAgentEndpoint(endpointKey, endpoint) {
|
|
8
|
+
endpoints.set(endpointKey, endpoint);
|
|
9
9
|
return () => {
|
|
10
|
-
if (endpoints.get(
|
|
11
|
-
endpoints.delete(
|
|
10
|
+
if (endpoints.get(endpointKey) === endpoint) {
|
|
11
|
+
endpoints.delete(endpointKey);
|
|
12
12
|
}
|
|
13
13
|
};
|
|
14
14
|
}
|
|
@@ -16,11 +16,11 @@ export function registerGithubAgentEndpoint(endpointId, endpoint) {
|
|
|
16
16
|
export function setGithubAgentDeps(deps) {
|
|
17
17
|
override = deps;
|
|
18
18
|
}
|
|
19
|
-
function lookup(
|
|
20
|
-
if (
|
|
21
|
-
const registered = endpoints.get(
|
|
19
|
+
function lookup(endpointKey) {
|
|
20
|
+
if (endpointKey) {
|
|
21
|
+
const registered = endpoints.get(endpointKey);
|
|
22
22
|
if (!registered)
|
|
23
|
-
throw new Error(`Endpoint ${
|
|
23
|
+
throw new Error(`Endpoint ${endpointKey} 不存在`);
|
|
24
24
|
return registered;
|
|
25
25
|
}
|
|
26
26
|
const first = endpoints.values().next().value;
|
package/lib/oauth-users.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { DatabaseHost } from '@zhin.js/plugin-runtime';
|
|
1
|
+
import type { DatabaseHost, PluginDatabaseHost } from '@zhin.js/plugin-runtime';
|
|
2
2
|
export declare const GITHUB_OAUTH_USERS_TABLE = "github_oauth_users";
|
|
3
3
|
export declare const GITHUB_OAUTH_USERS_SCHEMA: {
|
|
4
4
|
readonly id: {
|
|
@@ -30,4 +30,4 @@ export declare function defineGithubOauthUsersTable(host: {
|
|
|
30
30
|
define(name: string, definition: Record<string, unknown>): void;
|
|
31
31
|
}): void;
|
|
32
32
|
/** Resolve a stored PAT/device-flow token; null when DB missing or no row. */
|
|
33
|
-
export declare function lookupGithubOauthAccessToken(database: DatabaseHost | undefined, platform: string, platformUid: string): Promise<string | null>;
|
|
33
|
+
export declare function lookupGithubOauthAccessToken(database: DatabaseHost | PluginDatabaseHost | undefined, platform: string, platformUid: string): Promise<string | null>;
|
package/lib/protocol.d.ts
CHANGED
|
@@ -3,12 +3,13 @@
|
|
|
3
3
|
* Canonicalization is owned by gateway/core before endpoint.send.
|
|
4
4
|
*/
|
|
5
5
|
import type { IncomingMessage } from 'node:http';
|
|
6
|
+
import type { ConversationRef } from '@zhin.js/im-contract';
|
|
6
7
|
import { type GenericWebhookPayload, type IssueCommentPayload, type PRReviewCommentPayload, type PRReviewPayload } from './types.js';
|
|
7
8
|
export type { EventType, GenericWebhookPayload, GitHubComment, GitHubEndpointConfig, GitHubIssue, GitHubOAuthUser, GitHubPR, GitHubRepo, GitHubUser, IssueCommentPayload, ParsedChannel, PRReviewCommentPayload, PRReviewPayload, Subscription, } from './types.js';
|
|
8
9
|
export { buildChannelId, parseChannelId } from './types.js';
|
|
9
10
|
/** Plugin Runtime owner config (`plugins.<instanceKey>` / schema.json). */
|
|
10
11
|
export interface GithubAdapterConfig {
|
|
11
|
-
readonly
|
|
12
|
+
readonly id?: string;
|
|
12
13
|
readonly host?: string;
|
|
13
14
|
readonly app_id?: string | number;
|
|
14
15
|
readonly appId?: string | number;
|
|
@@ -41,7 +42,7 @@ export interface GithubAdapterConfig {
|
|
|
41
42
|
}
|
|
42
43
|
export interface ResolvedGithubConfig {
|
|
43
44
|
readonly context: 'github';
|
|
44
|
-
readonly
|
|
45
|
+
readonly id: string;
|
|
45
46
|
readonly host?: string;
|
|
46
47
|
readonly appId?: string | number;
|
|
47
48
|
readonly privateKey?: string;
|
|
@@ -65,6 +66,12 @@ export interface GithubInboundComment {
|
|
|
65
66
|
readonly kind: 'issue_comment' | 'pr_review_comment' | 'pr_review';
|
|
66
67
|
readonly createdAt: number;
|
|
67
68
|
}
|
|
69
|
+
/**
|
|
70
|
+
* 入站归一化 → ConversationRef:Issue/PR 评论区即仓库容器内的 channel 会话,
|
|
71
|
+
* 仓库(owner/repo)进 `parent`。出站侧用 `parseChannelId(conversation.id)`
|
|
72
|
+
* 在平台边界还原 repo/type/number。
|
|
73
|
+
*/
|
|
74
|
+
export declare function githubInboundConversation(endpointKey: string, comment: GithubInboundComment): ConversationRef;
|
|
68
75
|
export declare function resolveGithubConfig(config?: GithubAdapterConfig): ResolvedGithubConfig;
|
|
69
76
|
export declare function normalizeWebhookPath(path: string): string;
|
|
70
77
|
export declare function shouldAutoReplyRepo(config: Pick<ResolvedGithubConfig, 'autoReplyRepos'>, repo: string): boolean;
|
package/lib/protocol.js
CHANGED
|
@@ -9,6 +9,19 @@ import { formatCompact, getLogger } from '@zhin.js/logger';
|
|
|
9
9
|
const logger = getLogger('github');
|
|
10
10
|
import { buildChannelId, } from './types.js';
|
|
11
11
|
export { buildChannelId, parseChannelId } from './types.js';
|
|
12
|
+
/**
|
|
13
|
+
* 入站归一化 → ConversationRef:Issue/PR 评论区即仓库容器内的 channel 会话,
|
|
14
|
+
* 仓库(owner/repo)进 `parent`。出站侧用 `parseChannelId(conversation.id)`
|
|
15
|
+
* 在平台边界还原 repo/type/number。
|
|
16
|
+
*/
|
|
17
|
+
export function githubInboundConversation(endpointKey, comment) {
|
|
18
|
+
return {
|
|
19
|
+
endpoint: { id: endpointKey, adapter: endpointKey.split('\0')[0] ?? endpointKey },
|
|
20
|
+
kind: 'channel',
|
|
21
|
+
id: comment.channelId,
|
|
22
|
+
parent: { kind: 'channel', id: comment.repo },
|
|
23
|
+
};
|
|
24
|
+
}
|
|
12
25
|
export function resolveGithubConfig(config = {}) {
|
|
13
26
|
const entry = config.endpoints?.find((item) => item.context === 'github');
|
|
14
27
|
const appIdRaw = pickCredential(config.appId != null ? String(config.appId) : undefined, config.app_id != null ? String(config.app_id) : undefined, entry?.appId != null ? String(entry.appId) : undefined, entry?.app_id != null ? String(entry.app_id) : undefined, process.env.GITHUB_APP_ID);
|
|
@@ -17,8 +30,8 @@ export function resolveGithubConfig(config = {}) {
|
|
|
17
30
|
if (!appId || !privateKey) {
|
|
18
31
|
throw new TypeError('GitHub adapter requires app_id + private_key (plugins.<key> or GITHUB_APP_ID)');
|
|
19
32
|
}
|
|
20
|
-
const
|
|
21
|
-
|| (typeof entry?.
|
|
33
|
+
const id = (typeof config.id === 'string' && config.id)
|
|
34
|
+
|| (typeof entry?.id === 'string' && entry.id)
|
|
22
35
|
|| process.env.GITHUB_BOT_NAME
|
|
23
36
|
|| 'github-bot';
|
|
24
37
|
const webhookSecret = config.webhookSecret ?? config.webhook_secret
|
|
@@ -38,7 +51,7 @@ export function resolveGithubConfig(config = {}) {
|
|
|
38
51
|
?? entry?.workspaceRoot ?? entry?.workspace_root;
|
|
39
52
|
return {
|
|
40
53
|
context: 'github',
|
|
41
|
-
|
|
54
|
+
id,
|
|
42
55
|
...(host ? { host } : {}),
|
|
43
56
|
...(appId != null ? { appId } : {}),
|
|
44
57
|
...(privateKey ? { privateKey } : {}),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zhin.js/adapter-github",
|
|
3
|
-
"version": "5.0.
|
|
3
|
+
"version": "5.0.5",
|
|
4
4
|
"description": "Zhin.js GitHub adapter for Plugin Runtime (App auth + webhook)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./lib/index.js",
|
|
@@ -41,21 +41,22 @@
|
|
|
41
41
|
"directory": "plugins/adapters/github"
|
|
42
42
|
},
|
|
43
43
|
"dependencies": {
|
|
44
|
-
"@zhin.js/adapter": "1.1.
|
|
45
|
-
"@zhin.js/command": "1.0.
|
|
46
|
-
"@zhin.js/core": "1.5.
|
|
47
|
-
"@zhin.js/host-http": "1.0.
|
|
48
|
-
"@zhin.js/
|
|
49
|
-
"@zhin.js/
|
|
44
|
+
"@zhin.js/adapter": "1.1.7",
|
|
45
|
+
"@zhin.js/command": "1.0.9",
|
|
46
|
+
"@zhin.js/core": "1.5.4",
|
|
47
|
+
"@zhin.js/host-http": "1.0.8",
|
|
48
|
+
"@zhin.js/im-contract": "1.0.3",
|
|
49
|
+
"@zhin.js/logger": "1.0.76",
|
|
50
|
+
"@zhin.js/plugin-runtime": "1.1.5"
|
|
50
51
|
},
|
|
51
52
|
"peerDependencies": {
|
|
52
53
|
"zod": "^4.0.0",
|
|
53
|
-
"@zhin.js/adapter": "1.1.
|
|
54
|
-
"@zhin.js/agent": "1.1.
|
|
55
|
-
"@zhin.js/core": "1.5.
|
|
56
|
-
"@zhin.js/host-http": "1.0.
|
|
57
|
-
"@zhin.js/plugin-runtime": "1.1.
|
|
58
|
-
"zhin.js": "6.0.
|
|
54
|
+
"@zhin.js/adapter": "1.1.7",
|
|
55
|
+
"@zhin.js/agent": "1.1.5",
|
|
56
|
+
"@zhin.js/core": "1.5.4",
|
|
57
|
+
"@zhin.js/host-http": "1.0.8",
|
|
58
|
+
"@zhin.js/plugin-runtime": "1.1.5",
|
|
59
|
+
"zhin.js": "6.0.4"
|
|
59
60
|
},
|
|
60
61
|
"peerDependenciesMeta": {
|
|
61
62
|
"zhin.js": {
|
|
@@ -73,8 +74,8 @@
|
|
|
73
74
|
"typescript": "^6.0.3",
|
|
74
75
|
"vitest": "^4.1.10",
|
|
75
76
|
"zod": "^4.4.3",
|
|
76
|
-
"@zhin.js/agent": "1.1.
|
|
77
|
-
"zhin.js": "6.0.
|
|
77
|
+
"@zhin.js/agent": "1.1.5",
|
|
78
|
+
"zhin.js": "6.0.4"
|
|
78
79
|
},
|
|
79
80
|
"files": [
|
|
80
81
|
"adapters",
|
package/schema.json
CHANGED
|
@@ -35,6 +35,24 @@
|
|
|
35
35
|
"type": "string",
|
|
36
36
|
"description": "Managed git workspace root"
|
|
37
37
|
},
|
|
38
|
+
"master": {
|
|
39
|
+
"type": [
|
|
40
|
+
"string",
|
|
41
|
+
"number"
|
|
42
|
+
],
|
|
43
|
+
"description": "框架 master(GitHub user login or id;AI/工具权限、endpoint 管理)。endpoints[i].master 可逐项覆盖"
|
|
44
|
+
},
|
|
45
|
+
"trusted": {
|
|
46
|
+
"type": "array",
|
|
47
|
+
"items": {
|
|
48
|
+
"type": [
|
|
49
|
+
"string",
|
|
50
|
+
"number"
|
|
51
|
+
],
|
|
52
|
+
"description": "Trusted GitHub user login or id"
|
|
53
|
+
},
|
|
54
|
+
"description": "框架 trusted 用户列表(弱于 master)。endpoints[i].trusted 可逐项追加"
|
|
55
|
+
},
|
|
38
56
|
"endpoints": {
|
|
39
57
|
"type": "array",
|
|
40
58
|
"description": "多账号:一个插件实例挂多个 endpoint",
|
|
@@ -42,9 +60,23 @@
|
|
|
42
60
|
"type": "object",
|
|
43
61
|
"additionalProperties": true,
|
|
44
62
|
"properties": {
|
|
45
|
-
"
|
|
46
|
-
"type":
|
|
47
|
-
|
|
63
|
+
"master": {
|
|
64
|
+
"type": [
|
|
65
|
+
"string",
|
|
66
|
+
"number"
|
|
67
|
+
],
|
|
68
|
+
"description": "本 endpoint 的框架 master(GitHub user login or id);覆盖顶层 master"
|
|
69
|
+
},
|
|
70
|
+
"trusted": {
|
|
71
|
+
"type": "array",
|
|
72
|
+
"items": {
|
|
73
|
+
"type": [
|
|
74
|
+
"string",
|
|
75
|
+
"number"
|
|
76
|
+
],
|
|
77
|
+
"description": "Trusted GitHub user login or id"
|
|
78
|
+
},
|
|
79
|
+
"description": "本 endpoint 的 trusted 列表"
|
|
48
80
|
},
|
|
49
81
|
"app_id": {
|
|
50
82
|
"type": [
|
|
@@ -75,10 +107,14 @@
|
|
|
75
107
|
"webhookSecret": {
|
|
76
108
|
"type": "string",
|
|
77
109
|
"description": "Webhook HMAC secret (camelCase alias)"
|
|
110
|
+
},
|
|
111
|
+
"id": {
|
|
112
|
+
"type": "string",
|
|
113
|
+
"description": "GitHub App bot name"
|
|
78
114
|
}
|
|
79
115
|
},
|
|
80
116
|
"required": [
|
|
81
|
-
"
|
|
117
|
+
"id"
|
|
82
118
|
]
|
|
83
119
|
}
|
|
84
120
|
},
|
package/src/endpoint.ts
CHANGED
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
* GithubEndpoint — lifecycle, outbound send, inbound admit.
|
|
3
3
|
*/
|
|
4
4
|
import path from 'node:path';
|
|
5
|
-
import type { EndpointInstance } from '@zhin.js/adapter';
|
|
5
|
+
import type { EndpointInstance, EndpointSendRequest } from '@zhin.js/adapter';
|
|
6
6
|
import type { MessageGateway } from '@zhin.js/core/runtime';
|
|
7
7
|
import type { HttpHost, HttpRouteRegistration } from '@zhin.js/host-http';
|
|
8
|
-
import { formatCompact,
|
|
9
|
-
import type { CapabilityId,
|
|
8
|
+
import { formatCompact, getAdapterLogger } from '@zhin.js/logger';
|
|
9
|
+
import type { CapabilityId, PluginDatabaseHost } from '@zhin.js/plugin-runtime';
|
|
10
10
|
import { GhClient } from './gh-client.js';
|
|
11
11
|
import { registerGithubAgentEndpoint } from './github-agent-deps.js';
|
|
12
12
|
import { lookupGithubOauthAccessToken } from './oauth-users.js';
|
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
enrichInboundContent,
|
|
15
15
|
formatInboundContent,
|
|
16
16
|
formatOutboundBody,
|
|
17
|
+
githubInboundConversation,
|
|
17
18
|
parseChannelId,
|
|
18
19
|
type GithubInboundComment,
|
|
19
20
|
type ResolvedGithubConfig,
|
|
@@ -21,13 +22,11 @@ import {
|
|
|
21
22
|
import { registerGithubWebhookRoutes } from './webhook.js';
|
|
22
23
|
import { WorkspaceManager } from './workspace-manager.js';
|
|
23
24
|
|
|
24
|
-
const logger = getLogger('github');
|
|
25
|
-
|
|
26
25
|
export interface GithubEndpointOptions {
|
|
27
26
|
readonly id: CapabilityId;
|
|
28
27
|
readonly gateway: MessageGateway;
|
|
29
28
|
readonly http?: HttpHost;
|
|
30
|
-
readonly database?:
|
|
29
|
+
readonly database?: PluginDatabaseHost;
|
|
31
30
|
readonly config: ResolvedGithubConfig;
|
|
32
31
|
readonly createClient?: (config: ResolvedGithubConfig) => GhClient;
|
|
33
32
|
}
|
|
@@ -37,6 +36,8 @@ export interface GithubEndpointOptions {
|
|
|
37
36
|
* 不适用 EndpointManagement 语义端口;本 endpoint 不暴露该端口。
|
|
38
37
|
*/
|
|
39
38
|
export class GithubEndpoint implements EndpointInstance {
|
|
39
|
+
readonly #logger!: ReturnType<typeof getAdapterLogger>;
|
|
40
|
+
|
|
40
41
|
readonly #options: GithubEndpointOptions;
|
|
41
42
|
readonly gh: GhClient;
|
|
42
43
|
readonly config: ResolvedGithubConfig;
|
|
@@ -48,9 +49,10 @@ export class GithubEndpoint implements EndpointInstance {
|
|
|
48
49
|
#unregisterAgent?: () => void;
|
|
49
50
|
|
|
50
51
|
constructor(options: GithubEndpointOptions) {
|
|
52
|
+
this.#logger = getAdapterLogger('github', options.config.id);
|
|
51
53
|
this.#options = options;
|
|
52
54
|
this.config = options.config;
|
|
53
|
-
this.name = options.config.
|
|
55
|
+
this.name = options.config.id;
|
|
54
56
|
this.gh = options.createClient?.(options.config) ?? defaultCreateClient(options.config);
|
|
55
57
|
}
|
|
56
58
|
|
|
@@ -94,7 +96,7 @@ export class GithubEndpoint implements EndpointInstance {
|
|
|
94
96
|
return this.#workspaceManager;
|
|
95
97
|
}
|
|
96
98
|
|
|
97
|
-
getDatabase():
|
|
99
|
+
getDatabase(): PluginDatabaseHost | undefined {
|
|
98
100
|
return this.#options.database;
|
|
99
101
|
}
|
|
100
102
|
|
|
@@ -110,13 +112,13 @@ export class GithubEndpoint implements EndpointInstance {
|
|
|
110
112
|
throw new TypeError('GitHub webhook_secret requires httpHostToken');
|
|
111
113
|
}
|
|
112
114
|
this.#routeReleases.push(...registerGithubWebhookRoutes(this.#options.http, this));
|
|
113
|
-
logger.debug(formatCompact({
|
|
115
|
+
this.#logger.debug(formatCompact({
|
|
114
116
|
endpoint: this.name,
|
|
115
117
|
op: 'webhook',
|
|
116
118
|
path: this.config.webhookPath,
|
|
117
119
|
}));
|
|
118
120
|
} else {
|
|
119
|
-
logger.debug(formatCompact({
|
|
121
|
+
this.#logger.debug(formatCompact({
|
|
120
122
|
endpoint: this.name,
|
|
121
123
|
op: 'connect',
|
|
122
124
|
mode: 'api-only',
|
|
@@ -125,7 +127,7 @@ export class GithubEndpoint implements EndpointInstance {
|
|
|
125
127
|
}
|
|
126
128
|
} catch (error) {
|
|
127
129
|
await this.stop();
|
|
128
|
-
logger.error('Failed to connect GitHub endpoint:', error);
|
|
130
|
+
this.#logger.error('Failed to connect GitHub endpoint:', error);
|
|
129
131
|
throw error;
|
|
130
132
|
}
|
|
131
133
|
}
|
|
@@ -144,21 +146,21 @@ export class GithubEndpoint implements EndpointInstance {
|
|
|
144
146
|
this.#unregisterAgent?.();
|
|
145
147
|
this.#unregisterAgent = undefined;
|
|
146
148
|
this.#started = false;
|
|
147
|
-
logger.debug(formatCompact({ op: 'disconnect'
|
|
149
|
+
this.#logger.debug(formatCompact({ op: 'disconnect' }));
|
|
148
150
|
}
|
|
149
151
|
|
|
150
|
-
async send({
|
|
151
|
-
const parsed = parseChannelId(
|
|
152
|
-
if (!parsed) throw new Error(`无效的 GitHub
|
|
152
|
+
async send({ conversation, payload }: EndpointSendRequest): Promise<string> {
|
|
153
|
+
const parsed = parseChannelId(conversation.id);
|
|
154
|
+
if (!parsed) throw new Error(`无效的 GitHub conversation ID: ${conversation.id}`);
|
|
153
155
|
const text = formatOutboundBody(payload);
|
|
154
156
|
const r = parsed.type === 'issue'
|
|
155
157
|
? await this.gh.createIssueComment(parsed.repo, parsed.number, text)
|
|
156
158
|
: await this.gh.createPRComment(parsed.repo, parsed.number, text);
|
|
157
159
|
if (!r.ok) throw new Error(`发送失败: ${JSON.stringify(r.data)}`);
|
|
158
|
-
logger.debug(formatCompact({
|
|
160
|
+
this.#logger.debug(formatCompact({
|
|
159
161
|
op: 'github_send',
|
|
160
162
|
endpoint: this.name,
|
|
161
|
-
target
|
|
163
|
+
target: `${conversation.kind}:${conversation.id}`,
|
|
162
164
|
messageId: r.data.id,
|
|
163
165
|
}));
|
|
164
166
|
return String(r.data.id);
|
|
@@ -169,6 +171,7 @@ export class GithubEndpoint implements EndpointInstance {
|
|
|
169
171
|
if (!this.#open) return;
|
|
170
172
|
const botUser = this.config.botLogin || this.gh.getBotLogin() || this.gh.authenticatedUser;
|
|
171
173
|
if (botUser && comment.sender === botUser) return;
|
|
174
|
+
const conversation = githubInboundConversation(String(this.#options.id), comment);
|
|
172
175
|
const content = enrichInboundContent(
|
|
173
176
|
formatInboundContent(comment.content),
|
|
174
177
|
this.config,
|
|
@@ -176,21 +179,20 @@ export class GithubEndpoint implements EndpointInstance {
|
|
|
176
179
|
comment.repo,
|
|
177
180
|
);
|
|
178
181
|
void this.#options.gateway.receive({
|
|
179
|
-
|
|
180
|
-
|
|
182
|
+
conversation,
|
|
183
|
+
message: { conversation, id: comment.id },
|
|
181
184
|
content,
|
|
182
|
-
sender: comment.sender,
|
|
183
|
-
|
|
185
|
+
sender: { id: comment.sender, name: comment.sender },
|
|
186
|
+
endpointId: this.name,
|
|
184
187
|
metadata: Object.freeze({
|
|
185
|
-
endpoint: this.name,
|
|
186
188
|
repo: comment.repo,
|
|
187
189
|
kind: comment.kind,
|
|
188
190
|
createdAt: comment.createdAt,
|
|
189
191
|
}),
|
|
190
192
|
}).catch((err) => {
|
|
191
|
-
logger.warn(formatCompact({
|
|
193
|
+
this.#logger.warn(formatCompact({
|
|
192
194
|
op: 'github_gateway_receive_failed',
|
|
193
|
-
target:
|
|
195
|
+
target: `${conversation.kind}:${conversation.id}`,
|
|
194
196
|
error: err instanceof Error ? err.message : String(err),
|
|
195
197
|
}));
|
|
196
198
|
});
|
package/src/github-agent-deps.ts
CHANGED
|
@@ -23,7 +23,7 @@ export interface GithubAgentEndpoint {
|
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
export interface GithubAgentDeps {
|
|
26
|
-
getEndpoint: (
|
|
26
|
+
getEndpoint: (endpointKey?: string) => GithubAgentEndpoint;
|
|
27
27
|
/** Alias kept for existing agent handlers that call getAdapter(). */
|
|
28
28
|
getAdapter: () => GithubAgentEndpoint;
|
|
29
29
|
getWorkspaceManager: () => WorkspaceManager;
|
|
@@ -39,13 +39,13 @@ const endpoints = new Map<string, GithubAgentEndpoint>();
|
|
|
39
39
|
let override: GithubAgentDeps | null = null;
|
|
40
40
|
|
|
41
41
|
export function registerGithubAgentEndpoint(
|
|
42
|
-
|
|
42
|
+
endpointKey: string,
|
|
43
43
|
endpoint: GithubAgentEndpoint,
|
|
44
44
|
): () => void {
|
|
45
|
-
endpoints.set(
|
|
45
|
+
endpoints.set(endpointKey, endpoint);
|
|
46
46
|
return () => {
|
|
47
|
-
if (endpoints.get(
|
|
48
|
-
endpoints.delete(
|
|
47
|
+
if (endpoints.get(endpointKey) === endpoint) {
|
|
48
|
+
endpoints.delete(endpointKey);
|
|
49
49
|
}
|
|
50
50
|
};
|
|
51
51
|
}
|
|
@@ -55,10 +55,10 @@ export function setGithubAgentDeps(deps: GithubAgentDeps | null): void {
|
|
|
55
55
|
override = deps;
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
-
function lookup(
|
|
59
|
-
if (
|
|
60
|
-
const registered = endpoints.get(
|
|
61
|
-
if (!registered) throw new Error(`Endpoint ${
|
|
58
|
+
function lookup(endpointKey?: string): GithubAgentEndpoint {
|
|
59
|
+
if (endpointKey) {
|
|
60
|
+
const registered = endpoints.get(endpointKey);
|
|
61
|
+
if (!registered) throw new Error(`Endpoint ${endpointKey} 不存在`);
|
|
62
62
|
return registered;
|
|
63
63
|
}
|
|
64
64
|
const first = endpoints.values().next().value;
|
package/src/oauth-users.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* github_oauth_users — SSOT for Runtime user-token binding (gh bind / Endpoint lookup).
|
|
3
3
|
*/
|
|
4
4
|
import { formatCompact, getLogger } from '@zhin.js/logger';
|
|
5
|
-
import type { DatabaseHost } from '@zhin.js/plugin-runtime';
|
|
5
|
+
import type { DatabaseHost, PluginDatabaseHost } from '@zhin.js/plugin-runtime';
|
|
6
6
|
|
|
7
7
|
const logger = getLogger('github');
|
|
8
8
|
|
|
@@ -25,7 +25,7 @@ export function defineGithubOauthUsersTable(
|
|
|
25
25
|
|
|
26
26
|
/** Resolve a stored PAT/device-flow token; null when DB missing or no row. */
|
|
27
27
|
export async function lookupGithubOauthAccessToken(
|
|
28
|
-
database: DatabaseHost | undefined,
|
|
28
|
+
database: DatabaseHost | PluginDatabaseHost | undefined,
|
|
29
29
|
platform: string,
|
|
30
30
|
platformUid: string,
|
|
31
31
|
): Promise<string | null> {
|
package/src/protocol.ts
CHANGED
|
@@ -7,6 +7,7 @@ import { createHmac, timingSafeEqual } from 'node:crypto';
|
|
|
7
7
|
import type { IncomingMessage } from 'node:http';
|
|
8
8
|
import { pickCredential } from '@zhin.js/adapter';
|
|
9
9
|
import { isMediaRef } from '@zhin.js/core';
|
|
10
|
+
import type { ConversationRef } from '@zhin.js/im-contract';
|
|
10
11
|
import { formatCompact, getLogger } from '@zhin.js/logger';
|
|
11
12
|
|
|
12
13
|
const logger = getLogger('github');
|
|
@@ -39,7 +40,7 @@ export { buildChannelId, parseChannelId } from './types.js';
|
|
|
39
40
|
|
|
40
41
|
/** Plugin Runtime owner config (`plugins.<instanceKey>` / schema.json). */
|
|
41
42
|
export interface GithubAdapterConfig {
|
|
42
|
-
readonly
|
|
43
|
+
readonly id?: string;
|
|
43
44
|
readonly host?: string;
|
|
44
45
|
readonly app_id?: string | number;
|
|
45
46
|
readonly appId?: string | number;
|
|
@@ -73,7 +74,7 @@ export interface GithubAdapterConfig {
|
|
|
73
74
|
|
|
74
75
|
export interface ResolvedGithubConfig {
|
|
75
76
|
readonly context: 'github';
|
|
76
|
-
readonly
|
|
77
|
+
readonly id: string;
|
|
77
78
|
readonly host?: string;
|
|
78
79
|
readonly appId?: string | number;
|
|
79
80
|
readonly privateKey?: string;
|
|
@@ -100,6 +101,23 @@ export interface GithubInboundComment {
|
|
|
100
101
|
readonly createdAt: number;
|
|
101
102
|
}
|
|
102
103
|
|
|
104
|
+
/**
|
|
105
|
+
* 入站归一化 → ConversationRef:Issue/PR 评论区即仓库容器内的 channel 会话,
|
|
106
|
+
* 仓库(owner/repo)进 `parent`。出站侧用 `parseChannelId(conversation.id)`
|
|
107
|
+
* 在平台边界还原 repo/type/number。
|
|
108
|
+
*/
|
|
109
|
+
export function githubInboundConversation(
|
|
110
|
+
endpointKey: string,
|
|
111
|
+
comment: GithubInboundComment,
|
|
112
|
+
): ConversationRef {
|
|
113
|
+
return {
|
|
114
|
+
endpoint: { id: endpointKey, adapter: endpointKey.split('\0')[0] ?? endpointKey },
|
|
115
|
+
kind: 'channel',
|
|
116
|
+
id: comment.channelId,
|
|
117
|
+
parent: { kind: 'channel', id: comment.repo },
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
|
|
103
121
|
export function resolveGithubConfig(config: GithubAdapterConfig = {}): ResolvedGithubConfig {
|
|
104
122
|
const entry = config.endpoints?.find((item) => item.context === 'github');
|
|
105
123
|
const appIdRaw = pickCredential(
|
|
@@ -121,8 +139,8 @@ export function resolveGithubConfig(config: GithubAdapterConfig = {}): ResolvedG
|
|
|
121
139
|
'GitHub adapter requires app_id + private_key (plugins.<key> or GITHUB_APP_ID)',
|
|
122
140
|
);
|
|
123
141
|
}
|
|
124
|
-
const
|
|
125
|
-
|| (typeof entry?.
|
|
142
|
+
const id = (typeof config.id === 'string' && config.id)
|
|
143
|
+
|| (typeof entry?.id === 'string' && entry.id)
|
|
126
144
|
|| process.env.GITHUB_BOT_NAME
|
|
127
145
|
|| 'github-bot';
|
|
128
146
|
const webhookSecret = config.webhookSecret ?? config.webhook_secret
|
|
@@ -147,7 +165,7 @@ export function resolveGithubConfig(config: GithubAdapterConfig = {}): ResolvedG
|
|
|
147
165
|
|
|
148
166
|
return {
|
|
149
167
|
context: 'github',
|
|
150
|
-
|
|
168
|
+
id,
|
|
151
169
|
...(host ? { host } : {}),
|
|
152
170
|
...(appId != null ? { appId } : {}),
|
|
153
171
|
...(privateKey ? { privateKey } : {}),
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|