@zhin.js/adapter-github 0.1.35 → 0.1.37

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/src/index.ts CHANGED
@@ -3,17 +3,18 @@
3
3
  */
4
4
  import { usePlugin, type Plugin, type Context, type ToolFeature, type Tool, type ToolContext } from 'zhin.js';
5
5
  import { GitHubAdapter } from './adapter.js';
6
+ import { GhClient } from './gh-client.js';
6
7
  import type { EventType } from './types.js';
7
8
 
8
9
  declare module 'zhin.js' {
10
+ interface Adapters {
11
+ github: GitHubAdapter;
12
+ }
9
13
  namespace Plugin {
10
14
  interface Contexts {
11
15
  router: import('@zhin.js/http').Router;
12
16
  }
13
17
  }
14
- interface Adapters {
15
- github: GitHubAdapter;
16
- }
17
18
  interface Models {
18
19
  github_subscriptions: {
19
20
  id: number;
@@ -37,6 +38,7 @@ declare module 'zhin.js' {
37
38
  export * from './types.js';
38
39
  export { GitHubBot, parseMarkdown, toMarkdown } from './bot.js';
39
40
  export { GitHubAdapter } from './adapter.js';
41
+ export { GhClient } from './gh-client.js';
40
42
 
41
43
  const plugin = usePlugin();
42
44
  const { provide, defineModel, useContext, logger } = plugin;
@@ -63,16 +65,13 @@ defineModel('github_oauth_users', {
63
65
  platform: { type: 'text', nullable: false },
64
66
  platform_uid: { type: 'text', nullable: false },
65
67
  github_login: { type: 'text', nullable: false },
66
- github_id: { type: 'integer', nullable: false },
67
68
  access_token: { type: 'text', nullable: false },
68
- scope: { type: 'text', default: '' },
69
- created_at: { type: 'date', nullable: false },
70
- updated_at: { type: 'date', nullable: false },
69
+ created_at: { type: 'integer', default: 0 },
71
70
  });
72
71
 
73
72
  provide({
74
73
  name: 'github',
75
- description: 'GitHub Adapter — Issues/PRs as chat channels, full repo management via GitHub App',
74
+ description: 'GitHub Adapter — Issues/PRs as chat channels, full repo management via gh CLI',
76
75
  mounted: async (p: Plugin) => {
77
76
  const adapter = new GitHubAdapter(p);
78
77
  await adapter.start();
@@ -83,9 +82,28 @@ provide({
83
82
  },
84
83
  } as Context<'github'>);
85
84
 
86
- useContext('router', 'github', (router, adapter) => {
87
- adapter.setupWebhook(router);
88
- adapter.setupOAuth(router);
85
+ // 混合模式:有 router + webhook_secret → Webhook 实时;否则 → 轮询降级
86
+ useContext('github', (adapter) => {
87
+ if (adapter.hasWebhookConfig) {
88
+ // 尝试注册 Webhook(需要 router Context)
89
+ const router = plugin.inject('router');
90
+ if (router) {
91
+ adapter.setupWebhook(router);
92
+ logger.info('GitHub 事件源: Webhook (实时)');
93
+ } else {
94
+ // router 还没就绪,等它挂载后再注册
95
+ plugin.useContext('router', (r) => {
96
+ adapter.setupWebhook(r);
97
+ logger.info('GitHub 事件源: Webhook (实时, 延迟注册)');
98
+ });
99
+ }
100
+ }
101
+ // Webhook 未配置或未激活时,总是启动轮询作为兜底
102
+ if (!adapter.webhookActive) {
103
+ adapter.startPolling();
104
+ logger.info('GitHub 事件源: 轮询');
105
+ }
106
+ return () => adapter.stopPolling();
89
107
  });
90
108
 
91
109
  // ── Tool 工具注册 ─────────────────────────────────────────────────────────
@@ -113,8 +131,8 @@ useContext('tool', 'github', (toolService: ToolFeature, adapter: GitHubAdapter)
113
131
  },
114
132
  platforms: ['github'],
115
133
  tags: ['github'],
116
- execute: async (args: Record<string, any>) => {
117
- const api = adapter.getAPI();
134
+ execute: async (args: Record<string, any>, context?: ToolContext) => {
135
+ const api = await adapter.getUserOrDefaultAPI(context?.platform, context?.senderId);
118
136
  if (!api) return '❌ 没有可用的 GitHub bot';
119
137
  const { action, repo, number: num, title, body, head, base, state, approve, method } = args;
120
138
  switch (action) {
@@ -191,8 +209,8 @@ useContext('tool', 'github', (toolService: ToolFeature, adapter: GitHubAdapter)
191
209
  },
192
210
  platforms: ['github'],
193
211
  tags: ['github'],
194
- execute: async (args: Record<string, any>) => {
195
- const api = adapter.getAPI();
212
+ execute: async (args: Record<string, any>, context?: ToolContext) => {
213
+ const api = await adapter.getUserOrDefaultAPI(context?.platform, context?.senderId);
196
214
  if (!api) return '❌ 没有可用的 GitHub bot';
197
215
  const { action, repo, number: num, title, body, labels, state } = args;
198
216
  switch (action) {
@@ -255,8 +273,8 @@ useContext('tool', 'github', (toolService: ToolFeature, adapter: GitHubAdapter)
255
273
  },
256
274
  platforms: ['github'],
257
275
  tags: ['github'],
258
- execute: async (args: Record<string, any>) => {
259
- const api = adapter.getAPI();
276
+ execute: async (args: Record<string, any>, context?: ToolContext) => {
277
+ const api = await adapter.getUserOrDefaultAPI(context?.platform, context?.senderId);
260
278
  if (!api) return '❌ 没有可用的 GitHub bot';
261
279
  const { action, repo, limit: lim } = args;
262
280
  const limit = lim || 10;
@@ -324,8 +342,8 @@ useContext('tool', 'github', (toolService: ToolFeature, adapter: GitHubAdapter)
324
342
  },
325
343
  platforms: ['github'],
326
344
  tags: ['github'],
327
- execute: async (args: Record<string, any>) => {
328
- const api = adapter.getAPI();
345
+ execute: async (args: Record<string, any>, context?: ToolContext) => {
346
+ const api = await adapter.getUserOrDefaultAPI(context?.platform, context?.senderId);
329
347
  if (!api) return '❌ 没有可用的 GitHub bot';
330
348
  const { action, query: q, limit: lim } = args;
331
349
  const limit = lim || 10;
@@ -377,8 +395,8 @@ useContext('tool', 'github', (toolService: ToolFeature, adapter: GitHubAdapter)
377
395
  },
378
396
  platforms: ['github'],
379
397
  tags: ['github'],
380
- execute: async (args: Record<string, any>) => {
381
- const api = adapter.getAPI();
398
+ execute: async (args: Record<string, any>, context?: ToolContext) => {
399
+ const api = await adapter.getUserOrDefaultAPI(context?.platform, context?.senderId);
382
400
  if (!api) return '❌ 没有可用的 GitHub bot';
383
401
  const { action, repo, number: num, labels } = args;
384
402
  switch (action) {
@@ -427,8 +445,8 @@ useContext('tool', 'github', (toolService: ToolFeature, adapter: GitHubAdapter)
427
445
  },
428
446
  platforms: ['github'],
429
447
  tags: ['github'],
430
- execute: async (args: Record<string, any>) => {
431
- const api = adapter.getAPI();
448
+ execute: async (args: Record<string, any>, context?: ToolContext) => {
449
+ const api = await adapter.getUserOrDefaultAPI(context?.platform, context?.senderId);
432
450
  if (!api) return '❌ 没有可用的 GitHub bot';
433
451
  const { action, repo, number: num, assignees } = args;
434
452
  const assigneeArr = assignees.split(',').map((s: string) => s.trim());
@@ -456,8 +474,8 @@ useContext('tool', 'github', (toolService: ToolFeature, adapter: GitHubAdapter)
456
474
  },
457
475
  platforms: ['github'],
458
476
  tags: ['github'],
459
- execute: async (args: Record<string, any>) => {
460
- const api = adapter.getAPI();
477
+ execute: async (args: Record<string, any>, context?: ToolContext) => {
478
+ const api = await adapter.getUserOrDefaultAPI(context?.platform, context?.senderId);
461
479
  if (!api) return '❌ 没有可用的 GitHub bot';
462
480
  const r = await api.getFileContent(args.repo, args.path, args.ref);
463
481
  if (!r.ok) return `❌ ${r.data?.message || JSON.stringify(r.data)}`;
@@ -493,8 +511,8 @@ useContext('tool', 'github', (toolService: ToolFeature, adapter: GitHubAdapter)
493
511
  },
494
512
  platforms: ['github'],
495
513
  tags: ['github'],
496
- execute: async (args: Record<string, any>) => {
497
- const api = adapter.getAPI();
514
+ execute: async (args: Record<string, any>, context?: ToolContext) => {
515
+ const api = await adapter.getUserOrDefaultAPI(context?.platform, context?.senderId);
498
516
  if (!api) return '❌ 没有可用的 GitHub bot';
499
517
  const { action, repo, sha, path, base, head, limit: lim } = args;
500
518
  if (action === 'list') {
@@ -538,8 +556,8 @@ useContext('tool', 'github', (toolService: ToolFeature, adapter: GitHubAdapter)
538
556
  },
539
557
  platforms: ['github'],
540
558
  tags: ['github'],
541
- execute: async (args: Record<string, any>) => {
542
- const api = adapter.getAPI();
559
+ execute: async (args: Record<string, any>, context?: ToolContext) => {
560
+ const api = await adapter.getUserOrDefaultAPI(context?.platform, context?.senderId);
543
561
  if (!api) return '❌ 没有可用的 GitHub bot';
544
562
  const { type: itemType, repo, number: num, title, body, state } = args;
545
563
  const data: any = {};
@@ -557,7 +575,7 @@ useContext('tool', 'github', (toolService: ToolFeature, adapter: GitHubAdapter)
557
575
  // --- Star ---
558
576
  {
559
577
  name: 'github_star',
560
- description: 'Star 或取消 Star 一个 GitHub 仓库(需要先 github_bind 绑定账号)',
578
+ description: 'Star 或取消 Star 一个 GitHub 仓库(使用你绑定的 GitHub 账号,未绑定则用 Bot 默认账号)',
561
579
  parameters: {
562
580
  type: 'object' as const,
563
581
  properties: {
@@ -569,22 +587,21 @@ useContext('tool', 'github', (toolService: ToolFeature, adapter: GitHubAdapter)
569
587
  platforms: ['github'],
570
588
  tags: ['github'],
571
589
  execute: async (args: Record<string, any>, context?: ToolContext) => {
572
- if (!context?.platform || !context?.senderId) return '❌ 无法获取用户信息';
573
- const client = await adapter.getOAuthClient(context.platform, context.senderId);
574
- if (!client) return '❌ 你还没有绑定 GitHub 账号,请先使用 github_bind';
590
+ const gh = await adapter.getUserOrDefaultAPI(context?.platform, context?.senderId);
591
+ if (!gh) return '❌ 没有可用的 GitHub bot';
575
592
  const { action, repo } = args;
576
593
  switch (action) {
577
594
  case 'star': {
578
- const r = await client.starRepo(repo);
579
- return r.ok || r.status === 204 ? `⭐ 已 Star ${repo}` : `❌ ${r.data?.message || JSON.stringify(r.data)}`;
595
+ const r = await gh.starRepo(repo);
596
+ return r.ok ? `⭐ 已 Star ${repo}` : `❌ ${r.data?.message || JSON.stringify(r.data)}`;
580
597
  }
581
598
  case 'unstar': {
582
- const r = await client.unstarRepo(repo);
583
- return r.ok || r.status === 204 ? `💔 已取消 Star ${repo}` : `❌ ${r.data?.message || JSON.stringify(r.data)}`;
599
+ const r = await gh.unstarRepo(repo);
600
+ return r.ok ? `💔 已取消 Star ${repo}` : `❌ ${r.data?.message || JSON.stringify(r.data)}`;
584
601
  }
585
602
  case 'check': {
586
- const starred = await client.isStarred(repo);
587
- return starred ? `⭐ 你已 Star ${repo}` : `☆ 你尚未 Star ${repo}`;
603
+ const starred = await gh.isStarred(repo);
604
+ return starred ? `⭐ Star ${repo}` : `☆ 尚未 Star ${repo}`;
588
605
  }
589
606
  default: return `❌ 未知操作: ${action}`;
590
607
  }
@@ -593,7 +610,7 @@ useContext('tool', 'github', (toolService: ToolFeature, adapter: GitHubAdapter)
593
610
  // --- Fork ---
594
611
  {
595
612
  name: 'github_fork',
596
- description: 'Fork 一个 GitHub 仓库到自己的账号下(需要先 github_bind 绑定账号)',
613
+ description: 'Fork 一个 GitHub 仓库(使用你绑定的 GitHub 账号,未绑定则用 Bot 默认账号)',
597
614
  parameters: {
598
615
  type: 'object' as const,
599
616
  properties: {
@@ -604,14 +621,171 @@ useContext('tool', 'github', (toolService: ToolFeature, adapter: GitHubAdapter)
604
621
  platforms: ['github'],
605
622
  tags: ['github'],
606
623
  execute: async (args: Record<string, any>, context?: ToolContext) => {
607
- if (!context?.platform || !context?.senderId) return '❌ 无法获取用户信息';
608
- const client = await adapter.getOAuthClient(context.platform, context.senderId);
609
- if (!client) return '❌ 你还没有绑定 GitHub 账号,请先使用 github_bind';
610
- const r = await client.forkRepo(args.repo);
624
+ const gh = await adapter.getUserOrDefaultAPI(context?.platform, context?.senderId);
625
+ if (!gh) return '❌ 没有可用的 GitHub bot';
626
+ const r = await gh.forkRepo(args.repo);
611
627
  if (!r.ok) return `❌ ${r.data?.message || JSON.stringify(r.data)}`;
612
628
  return `🍴 已 Fork ${args.repo} → ${r.data.full_name}\n🔗 ${r.data.html_url}`;
613
629
  },
614
630
  },
631
+ // --- Bind (Device Flow) ---
632
+ {
633
+ name: 'github_bind',
634
+ description: '绑定你的 GitHub 账号 — 使用 Device Flow 授权,无需输入密码',
635
+ parameters: {
636
+ type: 'object' as const,
637
+ properties: {},
638
+ },
639
+ tags: ['github'],
640
+ execute: async (_args: Record<string, any>, context?: ToolContext) => {
641
+ if (!context?.platform || !context?.senderId) {
642
+ return '❌ 无法获取当前用户信息';
643
+ }
644
+ const clientId = adapter.getClientId();
645
+ if (!clientId) return '❌ Bot 未配置 GitHub App 或 App 无 client_id,无法进行账号绑定';
646
+
647
+ const db = plugin.root?.inject('database') as any;
648
+ const model = db?.models?.get('github_oauth_users');
649
+ if (!model) return '❌ 数据库未就绪';
650
+
651
+ // 检查是否已绑定
652
+ const [existing] = await model.select().where({ platform: context.platform, platform_uid: context.senderId });
653
+ if (existing) {
654
+ return `⚠️ 你已绑定 GitHub 账号: ${existing.github_login}\n如需重新绑定,请先执行 github_unbind`;
655
+ }
656
+
657
+ try {
658
+ const host = adapter.getHost();
659
+ const codeResp = await GhClient.deviceFlowRequestCode(clientId, host);
660
+ // 异步轮询 token(最多等 codeResp.expires_in 秒)
661
+ const tokenPromise = GhClient.deviceFlowPollToken(
662
+ clientId, codeResp.device_code, codeResp.interval, codeResp.expires_in, host,
663
+ );
664
+
665
+ // 先回复用户授权链接
666
+ const replyMsg = [
667
+ `🔗 请在浏览器中打开以下链接进行授权:`,
668
+ ` ${codeResp.verification_uri}`,
669
+ ``,
670
+ `📋 输入验证码: **${codeResp.user_code}**`,
671
+ ``,
672
+ `⏳ 等待授权中…(${Math.floor(codeResp.expires_in / 60)} 分钟内有效)`,
673
+ ].join('\n');
674
+
675
+ // 在后台等待用户授权完成
676
+ tokenPromise.then(async (tokenData) => {
677
+ if (!tokenData) {
678
+ // 授权超时或被拒绝 — 由于 execute 已经返回了,这里只能通过日志记录
679
+ logger.warn(`GitHub Device Flow 超时/拒绝: ${context.platform}:${context.senderId}`);
680
+ return;
681
+ }
682
+
683
+ // 使用 token 获取 GitHub 用户名
684
+ const userGh = new GhClient({ host, token: tokenData.access_token });
685
+ const authResult = await userGh.verifyAuth();
686
+ const login = authResult.ok ? authResult.user : 'unknown';
687
+
688
+ await model.insert({
689
+ id: Date.now(),
690
+ platform: context.platform,
691
+ platform_uid: context.senderId,
692
+ github_login: login,
693
+ access_token: tokenData.access_token,
694
+ created_at: Date.now(),
695
+ });
696
+ logger.info(`GitHub 账号绑定成功: ${context.platform}:${context.senderId} → ${login}`);
697
+
698
+ // 尝试回复绑定成功消息
699
+ if (context.message?.$reply) {
700
+ await context.message.$reply(`✅ GitHub 账号绑定成功!\n👤 ${login}`);
701
+ }
702
+ }).catch(err => {
703
+ logger.error('GitHub Device Flow 错误:', err);
704
+ });
705
+
706
+ return replyMsg;
707
+ } catch (e: any) {
708
+ return `❌ Device Flow 启动失败: ${e.message}`;
709
+ }
710
+ },
711
+ },
712
+ // --- Unbind ---
713
+ {
714
+ name: 'github_unbind',
715
+ description: '解除你绑定的 GitHub 账号',
716
+ parameters: {
717
+ type: 'object' as const,
718
+ properties: {},
719
+ },
720
+ tags: ['github'],
721
+ execute: async (_args: Record<string, any>, context?: ToolContext) => {
722
+ if (!context?.platform || !context?.senderId) {
723
+ return '❌ 无法获取当前用户信息';
724
+ }
725
+ const db = plugin.root?.inject('database') as any;
726
+ const model = db?.models?.get('github_oauth_users');
727
+ if (!model) return '❌ 数据库未就绪';
728
+
729
+ const [existing] = await model.select().where({ platform: context.platform, platform_uid: context.senderId });
730
+ if (!existing) return '📭 你尚未绑定 GitHub 账号';
731
+
732
+ await model.delete().where({ id: existing.id });
733
+ return `✅ 已解除 GitHub 账号绑定: ${existing.github_login}`;
734
+ },
735
+ },
736
+ // --- Whoami ---
737
+ {
738
+ name: 'github_whoami',
739
+ description: '查看你绑定的 GitHub 账号信息',
740
+ parameters: {
741
+ type: 'object' as const,
742
+ properties: {},
743
+ },
744
+ tags: ['github'],
745
+ execute: async (_args: Record<string, any>, context?: ToolContext) => {
746
+ if (!context?.platform || !context?.senderId) {
747
+ return '❌ 无法获取当前用户信息';
748
+ }
749
+ const db = plugin.root?.inject('database') as any;
750
+ const model = db?.models?.get('github_oauth_users');
751
+ if (!model) return '❌ 数据库未就绪';
752
+
753
+ const [existing] = await model.select().where({ platform: context.platform, platform_uid: context.senderId });
754
+ if (!existing) return '📭 你尚未绑定 GitHub 账号\n🔗 使用 github_bind 绑定你的账号';
755
+
756
+ // 验证 token 是否仍然有效
757
+ const userGh = new GhClient({ host: adapter.getHost(), token: existing.access_token });
758
+ const auth = await userGh.verifyAuth();
759
+ if (auth.ok) {
760
+ return `👤 已绑定 GitHub 账号: ${auth.user}\n📅 绑定时间: ${new Date(existing.created_at).toLocaleString('zh-CN')}`;
761
+ }
762
+ return `⚠️ 已绑定账号 ${existing.github_login},但 Token 已失效\n🔗 请执行 github_unbind 后重新 github_bind`;
763
+ },
764
+ },
765
+ // --- Install App ---
766
+ {
767
+ name: 'github_install',
768
+ description: '获取安装 GitHub App 的链接 — 安装后 Bot 可以访问你的仓库,你也可以使用更多功能',
769
+ parameters: {
770
+ type: 'object' as const,
771
+ properties: {},
772
+ },
773
+ tags: ['github'],
774
+ execute: async () => {
775
+ const slug = adapter.getAppSlug();
776
+ if (!slug) return '❌ Bot 未配置 GitHub App';
777
+ const host = adapter.getHost() || 'github.com';
778
+ const installations = adapter.getInstallations();
779
+ let msg = `🔗 请点击以下链接安装 GitHub App 到你的仓库:\n https://${host}/apps/${slug}/installations/new`;
780
+ if (installations.length) {
781
+ msg += `\n\n📋 当前已安装 (${installations.length}):`;
782
+ for (const inst of installations) {
783
+ msg += `\n • ${inst.account.login} (${inst.account.type})`;
784
+ }
785
+ }
786
+ return msg;
787
+ },
788
+ },
615
789
  // --- Subscribe ---
616
790
  {
617
791
  name: 'github_subscribe',
@@ -728,84 +902,6 @@ useContext('tool', 'github', (toolService: ToolFeature, adapter: GitHubAdapter)
728
902
  }).join('\n\n');
729
903
  },
730
904
  },
731
- // --- Bind ---
732
- {
733
- name: 'github_bind',
734
- description: '绑定 GitHub 账号,生成 OAuth 授权链接',
735
- parameters: {
736
- type: 'object' as const,
737
- properties: {},
738
- },
739
- platforms: ['github'],
740
- tags: ['github'],
741
- execute: async (_args: Record<string, any>, context?: ToolContext) => {
742
- if (!context?.platform || !context?.senderId) return '❌ 无法获取用户信息';
743
-
744
- // 检查是否已绑定
745
- const db = plugin.root?.inject('database') as any;
746
- const model = db?.models?.get('github_oauth_users');
747
- if (model) {
748
- const [existing] = await model.select().where({ platform: context.platform, platform_uid: context.senderId });
749
- if (existing) return `✅ 你已绑定 GitHub 账号: ${existing.github_login}\n如需重新绑定,请先使用 github_unbind 解绑`;
750
- }
751
-
752
- const url = adapter.createOAuthState(context.platform, context.senderId);
753
- if (!url) return '❌ GitHub App 未配置 OAuth (缺少 client_id)';
754
-
755
- return `🔗 请点击以下链接绑定你的 GitHub 账号:\n${url}\n\n链接有效期 5 分钟`;
756
- },
757
- },
758
- // --- Unbind ---
759
- {
760
- name: 'github_unbind',
761
- description: '解绑 GitHub 账号',
762
- parameters: {
763
- type: 'object' as const,
764
- properties: {},
765
- },
766
- platforms: ['github'],
767
- tags: ['github'],
768
- execute: async (_args: Record<string, any>, context?: ToolContext) => {
769
- if (!context?.platform || !context?.senderId) return '❌ 无法获取用户信息';
770
- const db = plugin.root?.inject('database') as any;
771
- const model = db?.models?.get('github_oauth_users');
772
- if (!model) return '❌ 数据库未就绪';
773
-
774
- const [existing] = await model.select().where({ platform: context.platform, platform_uid: context.senderId });
775
- if (!existing) return '📭 你还没有绑定 GitHub 账号';
776
-
777
- await model.delete().where({ id: existing.id });
778
- return `✅ 已解绑 GitHub 账号: ${existing.github_login}`;
779
- },
780
- },
781
- // --- Whoami ---
782
- {
783
- name: 'github_whoami',
784
- description: '查看当前绑定的 GitHub 账号信息',
785
- parameters: {
786
- type: 'object' as const,
787
- properties: {},
788
- },
789
- platforms: ['github'],
790
- tags: ['github'],
791
- execute: async (_args: Record<string, any>, context?: ToolContext) => {
792
- if (!context?.platform || !context?.senderId) return '❌ 无法获取用户信息';
793
- const db = plugin.root?.inject('database') as any;
794
- const model = db?.models?.get('github_oauth_users');
795
- if (!model) return '❌ 数据库未就绪';
796
-
797
- const [row] = await model.select().where({ platform: context.platform, platform_uid: context.senderId });
798
- if (!row) return '📭 你还没有绑定 GitHub 账号,使用 github_bind 进行绑定';
799
-
800
- return [
801
- `🐙 GitHub 账号信息`,
802
- `👤 用户名: ${row.github_login}`,
803
- `🆔 GitHub ID: ${row.github_id}`,
804
- `📅 绑定时间: ${row.created_at instanceof Date ? row.created_at.toLocaleDateString() : String(row.created_at).split('T')[0]}`,
805
- `🔑 权限: ${row.scope || '(默认)'}`,
806
- ].join('\n');
807
- },
808
- },
809
905
  ];
810
906
 
811
907
  const disposers = tools.map(t => toolService.addTool(t, plugin.name));
@@ -814,4 +910,4 @@ useContext('tool', 'github', (toolService: ToolFeature, adapter: GitHubAdapter)
814
910
  return () => disposers.forEach(d => d());
815
911
  });
816
912
 
817
- logger.debug('GitHub 适配器已加载 (GitHub App 认证)');
913
+ logger.debug('GitHub 适配器已加载 (gh CLI 认证)');
package/src/types.ts CHANGED
@@ -1,45 +1,23 @@
1
1
  // ── Bot 配置 ─────────────────────────────────────────────────────────
2
- // GitHub App 认证: app_id + private_key → JWT → Installation Token
3
- // OAuth 用户授权: client_id + client_secret → 用户绑定 GitHub 账号
2
+ // 基于 gh CLI 认证:需要系统已安装并认证 gh CLI
3
+ // 认证方式:gh auth login
4
4
 
5
5
  export interface GitHubBotConfig {
6
6
  context: 'github';
7
7
  /** Bot 标识名称 */
8
8
  name: string;
9
+ /** GitHub Enterprise 主机名(默认 github.com) */
10
+ host?: string;
9
11
  /** GitHub App ID */
10
- app_id: number;
11
- /** GitHub App 私钥 PEM 内容或文件路径 */
12
- private_key: string;
13
- /** Installation ID (不填则自动获取第一个) */
14
- installation_id?: number;
15
- /** Webhook 签名密钥 */
12
+ app_id?: string | number;
13
+ /** GitHub App 私钥(PEM 内容或文件路径) */
14
+ private_key?: string;
15
+ /** Webhook Secret(配置后启用 Webhook 接收事件,不配置则使用轮询) */
16
16
  webhook_secret?: string;
17
- /** GitHub App OAuth: Client ID (在 App 设置页获取) */
18
- client_id?: string;
19
- /** GitHub App OAuth: Client Secret */
20
- client_secret?: string;
21
- /** 服务公开访问地址,用于 OAuth 回调和绑定链接(如 https://bot.example.com) */
22
- public_url?: string;
23
- }
24
-
25
- // ── OAuth 用户绑定 ───────────────────────────────────────────────────
26
-
27
- export interface GitHubOAuthUser {
28
- id: number;
29
- /** 聊天平台名称 (icqq / kook / discord ...) */
30
- platform: string;
31
- /** 聊天平台用户 ID */
32
- platform_uid: string;
33
- /** GitHub 用户名 */
34
- github_login: string;
35
- /** GitHub 用户 ID */
36
- github_id: number;
37
- /** OAuth access_token */
38
- access_token: string;
39
- /** 授权范围 */
40
- scope: string;
41
- created_at: Date;
42
- updated_at: Date;
17
+ /** Webhook 路由路径(默认 /github/webhook) */
18
+ webhook_path?: string;
19
+ /** 事件轮询间隔(秒,默认 60,Webhook 模式下作为降级备选) */
20
+ poll_interval?: number;
43
21
  }
44
22
 
45
23
  // ── Channel ID ───────────────────────────────────────────────────────
@@ -165,3 +143,20 @@ export interface Subscription {
165
143
  export type PrAction = 'list' | 'view' | 'diff' | 'merge' | 'create' | 'review' | 'close';
166
144
  export type IssueAction = 'list' | 'view' | 'create' | 'close' | 'comment';
167
145
  export type RepoAction = 'info' | 'branches' | 'releases' | 'runs' | 'stars';
146
+
147
+ // ── OAuth 用户绑定 ───────────────────────────────────────────────────
148
+ // 存储各平台用户绑定的 GitHub OAuth Token(Device Flow)
149
+
150
+ export interface GitHubOAuthUser {
151
+ id: number;
152
+ /** 来源平台 (icqq, kook, discord …) */
153
+ platform: string;
154
+ /** 该平台上的用户 ID */
155
+ platform_uid: string;
156
+ /** GitHub 用户名 */
157
+ github_login: string;
158
+ /** OAuth Access Token */
159
+ access_token: string;
160
+ /** 绑定时间 (ms) */
161
+ created_at: number;
162
+ }
package/lib/api.d.ts.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"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"}