@wu529778790/open-im 1.8.1-beta.6 → 1.8.1-beta.8

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.
@@ -5,6 +5,7 @@ const PLATFORM_LABELS = {
5
5
  wechat: "微信",
6
6
  wework: "企业微信",
7
7
  dingtalk: "钉钉",
8
+ workbuddy: "WorkBuddy",
8
9
  };
9
10
  export const CHANNEL_CAPABILITIES = {
10
11
  telegram: {
@@ -31,6 +32,10 @@ export const CHANNEL_CAPABILITIES = {
31
32
  inbound: { text: "native", image: "fallback", file: "fallback", voice: "fallback", video: "fallback" },
32
33
  outbound: { streamEdit: "native", streamPush: "fallback", image: "fallback", card: "native", typing: "native" },
33
34
  },
35
+ workbuddy: {
36
+ inbound: { text: "native", image: "none", file: "none", voice: "none", video: "none" },
37
+ outbound: { streamEdit: "none", streamPush: "none", image: "none", card: "none", typing: "none" },
38
+ },
34
39
  };
35
40
  function listPreferredPlatforms(kind) {
36
41
  return Object.entries(CHANNEL_CAPABILITIES)
@@ -18,7 +18,7 @@ export type ClaudeRequestHandler = (userId: string, chatId: string, prompt: stri
18
18
  export declare class CommandHandler {
19
19
  private deps;
20
20
  constructor(deps: CommandHandlerDeps);
21
- dispatch(text: string, chatId: string, userId: string, platform: 'dingtalk' | 'feishu' | 'qq' | 'telegram' | 'wechat' | 'wework', handleClaudeRequest: ClaudeRequestHandler): Promise<boolean>;
21
+ dispatch(text: string, chatId: string, userId: string, platform: 'dingtalk' | 'feishu' | 'qq' | 'telegram' | 'wechat' | 'wework' | 'workbuddy', handleClaudeRequest: ClaudeRequestHandler): Promise<boolean>;
22
22
  private getClearHistoryHint;
23
23
  private handleHelp;
24
24
  private handleNew;
@@ -40,7 +40,9 @@ export class CommandHandler {
40
40
  ? '💡 提示:如需清除本对话的历史消息,请清除聊天记录'
41
41
  : platform === 'dingtalk'
42
42
  ? '💡 提示:如需清除本对话的历史消息,请在钉钉中清空聊天记录'
43
- : '💡 提示:如需清除本对话的历史消息,请点击 Telegram 聊天右上角 ⋮ → 清除历史';
43
+ : platform === 'workbuddy'
44
+ ? '💡 提示:如需清除本对话的历史消息,请清除聊天记录'
45
+ : '💡 提示:如需清除本对话的历史消息,请点击 Telegram 聊天右上角 ⋮ → 清除历史';
44
46
  }
45
47
  async handleHelp(chatId, platform) {
46
48
  const help = [
@@ -50,6 +50,7 @@ export declare const PAGE_TEXTS: {
50
50
  readonly qqSummary: "App ID and secret for bot access.";
51
51
  readonly weworkSummary: "Corp ID and secret for enterprise delivery.";
52
52
  readonly dingtalkSummary: "Client credentials plus optional card template.";
53
+ readonly workbuddySummary: "CodeBuddy OAuth for WeChat customer service.";
53
54
  readonly platformCredentialsTitle: "Credentials";
54
55
  readonly platformAccessTitle: "Routing and access";
55
56
  readonly platformTestNote: "Checks required credentials against the platform.";
@@ -70,8 +71,13 @@ export declare const PAGE_TEXTS: {
70
71
  readonly clientId: "Client ID / AppKey";
71
72
  readonly clientSecret: "Client Secret / AppSecret";
72
73
  readonly dingtalkHelp: "Get credentials: Create an enterprise internal app on <a href=\"https://open-dev.dingtalk.com/\" target=\"_blank\">DingTalk Open Platform</a>, enable Stream Mode, and get Client ID / Client Secret";
74
+ readonly workbuddyHelp: "Get credentials: Login via CodeBuddy OAuth to get access/refresh tokens. WorkBuddy connects WeChat customer service through Centrifuge WebSocket.";
73
75
  readonly secret: "Secret";
74
76
  readonly cardTemplateId: "Card template ID";
77
+ readonly workbuddyAccessToken: "Access Token";
78
+ readonly workbuddyRefreshToken: "Refresh Token";
79
+ readonly workbuddyUserId: "User ID";
80
+ readonly workbuddyBaseUrl: "Base URL";
75
81
  readonly optional: "Optional";
76
82
  readonly commaSeparatedIds: "Comma-separated IDs";
77
83
  readonly aiTitle: "AI Tooling";
@@ -178,6 +184,7 @@ export declare const PAGE_TEXTS: {
178
184
  readonly qqSummary: "QQ 机器人 App ID 与 Secret。";
179
185
  readonly weworkSummary: "企业微信 Corp ID 与 Secret。";
180
186
  readonly dingtalkSummary: "钉钉 Client 凭证,可选配置卡片模板 ID。";
187
+ readonly workbuddySummary: "CodeBuddy OAuth 连接微信客服。";
181
188
  readonly platformCredentialsTitle: "凭证";
182
189
  readonly platformAccessTitle: "路由与访问";
183
190
  readonly platformTestNote: "只会检查该平台的必填凭证是否可用。";
@@ -198,7 +205,12 @@ export declare const PAGE_TEXTS: {
198
205
  readonly clientId: "Client ID / AppKey";
199
206
  readonly clientSecret: "Client Secret / AppSecret";
200
207
  readonly dingtalkHelp: "获取凭证:在 <a href=\"https://open-dev.dingtalk.com/\" target=\"_blank\">钉钉开放平台</a> 创建企业内部应用,启用 Stream Mode,并拿到 Client ID / Client Secret";
208
+ readonly workbuddyHelp: "获取凭证:通过 CodeBuddy OAuth 登录获取 access/refresh token。WorkBuddy 通过 Centrifuge WebSocket 连接微信客服。";
201
209
  readonly cardTemplateId: "卡片模板 ID";
210
+ readonly workbuddyAccessToken: "Access Token";
211
+ readonly workbuddyRefreshToken: "Refresh Token";
212
+ readonly workbuddyUserId: "User ID";
213
+ readonly workbuddyBaseUrl: "基础 URL";
202
214
  readonly optional: "可选";
203
215
  readonly commaSeparatedIds: "多个 ID 用逗号分隔";
204
216
  readonly aiTitle: "AI 工具配置";
@@ -50,6 +50,7 @@ export const PAGE_TEXTS = {
50
50
  qqSummary: "App ID and secret for bot access.",
51
51
  weworkSummary: "Corp ID and secret for enterprise delivery.",
52
52
  dingtalkSummary: "Client credentials plus optional card template.",
53
+ workbuddySummary: "CodeBuddy OAuth for WeChat customer service.",
53
54
  platformCredentialsTitle: "Credentials",
54
55
  platformAccessTitle: "Routing and access",
55
56
  platformTestNote: "Checks required credentials against the platform.",
@@ -70,8 +71,13 @@ export const PAGE_TEXTS = {
70
71
  clientId: "Client ID / AppKey",
71
72
  clientSecret: "Client Secret / AppSecret",
72
73
  dingtalkHelp: 'Get credentials: Create an enterprise internal app on <a href="https://open-dev.dingtalk.com/" target="_blank">DingTalk Open Platform</a>, enable Stream Mode, and get Client ID / Client Secret',
74
+ workbuddyHelp: 'Get credentials: Login via CodeBuddy OAuth to get access/refresh tokens. WorkBuddy connects WeChat customer service through Centrifuge WebSocket.',
73
75
  secret: "Secret",
74
76
  cardTemplateId: "Card template ID",
77
+ workbuddyAccessToken: "Access Token",
78
+ workbuddyRefreshToken: "Refresh Token",
79
+ workbuddyUserId: "User ID",
80
+ workbuddyBaseUrl: "Base URL",
75
81
  optional: "Optional",
76
82
  commaSeparatedIds: "Comma-separated IDs",
77
83
  aiTitle: "AI Tooling",
@@ -178,6 +184,7 @@ export const PAGE_TEXTS = {
178
184
  qqSummary: "QQ \u673a\u5668\u4eba App ID \u4e0e Secret\u3002",
179
185
  weworkSummary: "\u4f01\u4e1a\u5fae\u4fe1 Corp ID \u4e0e Secret\u3002",
180
186
  dingtalkSummary: "\u9489\u9489 Client \u51ed\u8bc1\uff0c\u53ef\u9009\u914d\u7f6e\u5361\u7247\u6a21\u677f ID\u3002",
187
+ workbuddySummary: "CodeBuddy OAuth \u8fde\u63a5\u5fae\u4fe1\u5ba2\u670d\u3002",
181
188
  platformCredentialsTitle: "\u51ed\u8bc1",
182
189
  platformAccessTitle: "\u8def\u7531\u4e0e\u8bbf\u95ee",
183
190
  platformTestNote: "\u53ea\u4f1a\u68c0\u67e5\u8be5\u5e73\u53f0\u7684\u5fc5\u586b\u51ed\u8bc1\u662f\u5426\u53ef\u7528\u3002",
@@ -198,7 +205,12 @@ export const PAGE_TEXTS = {
198
205
  clientId: "Client ID / AppKey",
199
206
  clientSecret: "Client Secret / AppSecret",
200
207
  dingtalkHelp: '\u83b7\u53d6\u51ed\u8bc1\uff1a\u5728 <a href="https://open-dev.dingtalk.com/" target="_blank">\u9489\u9489\u5f00\u653e\u5e73\u53f0</a> \u521b\u5efa\u4f01\u4e1a\u5185\u90e8\u5e94\u7528\uff0c\u542f\u7528 Stream Mode\uff0c\u5e76\u62ff\u5230 Client ID / Client Secret',
208
+ workbuddyHelp: '\u83b7\u53d6\u51ed\u8bc1\uff1a\u901a\u8fc7 CodeBuddy OAuth \u767b\u5f55\u83b7\u53d6 access/refresh token\u3002WorkBuddy \u901a\u8fc7 Centrifuge WebSocket \u8fde\u63a5\u5fae\u4fe1\u5ba2\u670d\u3002',
201
209
  cardTemplateId: "\u5361\u7247\u6a21\u677f ID",
210
+ workbuddyAccessToken: "Access Token",
211
+ workbuddyRefreshToken: "Refresh Token",
212
+ workbuddyUserId: "User ID",
213
+ workbuddyBaseUrl: "\u57fa\u7840 URL",
202
214
  optional: "\u53ef\u9009",
203
215
  commaSeparatedIds: "\u591a\u4e2a ID \u7528\u9017\u53f7\u5206\u9694",
204
216
  aiTitle: "AI \u5de5\u5177\u914d\u7f6e",
@@ -4,6 +4,7 @@ export const PAGE_SCRIPT = String.raw ` const platformDefinitions = [
4
4
  { key: "qq", label: "QQ", fields: ["aiCommand", "appId", "secret", "allowedUserIds"], testFields: ["appId", "secret"], requiredFields: ["appId", "secret"] },
5
5
  { key: "wework", label: "WeWork", fields: ["aiCommand", "corpId", "secret", "allowedUserIds"], testFields: ["corpId", "secret"], requiredFields: ["corpId", "secret"] },
6
6
  { key: "dingtalk", label: "DingTalk", fields: ["aiCommand", "clientId", "clientSecret", "cardTemplateId", "allowedUserIds"], testFields: ["clientId", "clientSecret"], requiredFields: ["clientId", "clientSecret"] },
7
+ { key: "workbuddy", label: "WorkBuddy", fields: ["aiCommand", "accessToken", "refreshToken", "userId", "baseUrl", "allowedUserIds"], testFields: ["accessToken", "refreshToken", "userId"], requiredFields: ["accessToken", "refreshToken", "userId"] },
7
8
  ];
8
9
  const platformKeys = platformDefinitions.map((platform) => platform.key);
9
10
  const aiTools = ["claude", "codex", "codebuddy"];
@@ -828,7 +828,7 @@ export const PAGE_HTML_PREFIX = String.raw `<!doctype html>
828
828
  <div class="stats-grid">
829
829
  <div class="stat-card">
830
830
  <div class="stat-label" id="statConfiguredLabel">Configured</div>
831
- <div class="stat-value" id="statConfiguredValue">0/5</div>
831
+ <div class="stat-value" id="statConfiguredValue">0/6</div>
832
832
  </div>
833
833
  <div class="stat-card">
834
834
  <div class="stat-label" id="statEnabledLabel">Enabled</div>
@@ -1058,6 +1058,53 @@ export const PAGE_HTML_PREFIX = String.raw `<!doctype html>
1058
1058
  <div id="test-dingtalk-result" class="mt-4"></div>
1059
1059
  </div>
1060
1060
  </div>
1061
+
1062
+ <!-- WorkBuddy -->
1063
+ <div class="platform-card">
1064
+ <div class="platform-header">
1065
+ <h3 class="platform-title">WorkBuddy</h3>
1066
+ <label class="toggle">
1067
+ <input type="checkbox" id="workbuddy-enabled" class="toggle-input">
1068
+ <span class="toggle-switch"></span>
1069
+ <span class="toggle-label" id="workbuddy-label">Enabled</span>
1070
+ </label>
1071
+ </div>
1072
+ <div class="platform-body">
1073
+ <div class="form-group">
1074
+ <label class="form-label" id="workbuddy-aiCommand-label">AI Tool</label>
1075
+ <select id="workbuddy-aiCommand" class="form-select">
1076
+ <option value="claude">claude</option>
1077
+ <option value="codex">codex</option>
1078
+ <option value="codebuddy">codebuddy</option>
1079
+ </select>
1080
+ </div>
1081
+ <div class="form-group">
1082
+ <label class="form-label" id="workbuddy-accessToken-label">Access Token</label>
1083
+ <input id="workbuddy-accessToken" class="form-input mono" type="password" autocomplete="off" />
1084
+ </div>
1085
+ <div class="form-group">
1086
+ <label class="form-label" id="workbuddy-refreshToken-label">Refresh Token</label>
1087
+ <input id="workbuddy-refreshToken" class="form-input mono" type="password" autocomplete="off" />
1088
+ </div>
1089
+ <div class="form-group">
1090
+ <label class="form-label" id="workbuddy-userId-label">User ID</label>
1091
+ <input id="workbuddy-userId" class="form-input mono" type="text" />
1092
+ </div>
1093
+ <div class="form-group">
1094
+ <label class="form-label" id="workbuddy-baseUrl-label">Base URL (optional)</label>
1095
+ <input id="workbuddy-baseUrl" class="form-input mono" type="text" placeholder="https://copilot.tencent.com" />
1096
+ </div>
1097
+ <div class="form-group">
1098
+ <label class="form-label" id="workbuddy-allowedUserIds-label">Allowed User IDs</label>
1099
+ <textarea id="workbuddy-allowedUserIds" class="form-textarea mono"></textarea>
1100
+ </div>
1101
+ <div class="form-help" id="workbuddy-help">WorkBuddy uses CodeBuddy OAuth to connect WeChat customer service. Get credentials from CodeBuddy login.</div>
1102
+ <div style="display: flex; gap: 12px; flex-wrap: wrap;">
1103
+ <button id="test-workbuddy" class="btn btn-secondary btn-sm" type="button">Test Configuration</button>
1104
+ </div>
1105
+ <div id="test-workbuddy-result" class="mt-4"></div>
1106
+ </div>
1107
+ </div>
1061
1108
  </div>
1062
1109
  </section>
1063
1110
 
@@ -359,6 +359,7 @@ function createProbeConfig(values) {
359
359
  wechatAllowedUserIds: [],
360
360
  weworkAllowedUserIds: [],
361
361
  dingtalkAllowedUserIds: [],
362
+ workbuddyAllowedUserIds: [],
362
363
  aiCommand: "claude",
363
364
  codexCliPath: "codex",
364
365
  claudeWorkDir: process.cwd(),
package/dist/config.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { type LogLevel } from './logger.js';
2
- export type Platform = 'dingtalk' | 'feishu' | 'qq' | 'telegram' | 'wechat' | 'wework';
2
+ export type Platform = 'dingtalk' | 'feishu' | 'qq' | 'telegram' | 'wechat' | 'wework' | 'workbuddy';
3
3
  export type AiCommand = 'claude' | 'codex' | 'codebuddy';
4
4
  export interface Config {
5
5
  enabledPlatforms: Platform[];
@@ -29,6 +29,7 @@ export interface Config {
29
29
  wechatAllowedUserIds: string[];
30
30
  weworkAllowedUserIds: string[];
31
31
  dingtalkAllowedUserIds: string[];
32
+ workbuddyAllowedUserIds: string[];
32
33
  aiCommand: AiCommand;
33
34
  codexCliPath: string;
34
35
  codebuddyCliPath: string;
@@ -82,6 +83,17 @@ export interface Config {
82
83
  allowedUserIds: string[];
83
84
  cardTemplateId?: string;
84
85
  };
86
+ workbuddy?: {
87
+ enabled: boolean;
88
+ aiCommand?: AiCommand;
89
+ allowedUserIds: string[];
90
+ accessToken?: string;
91
+ refreshToken?: string;
92
+ userId?: string;
93
+ baseUrl?: string;
94
+ guid?: string;
95
+ workspacePath?: string;
96
+ };
85
97
  };
86
98
  }
87
99
  export interface FilePlatformTelegram {
@@ -134,6 +146,17 @@ export interface FilePlatformDingtalk {
134
146
  allowedUserIds?: string[];
135
147
  cardTemplateId?: string;
136
148
  }
149
+ interface FilePlatformWorkBuddy {
150
+ enabled?: boolean;
151
+ aiCommand?: AiCommand;
152
+ allowedUserIds?: string[];
153
+ accessToken?: string;
154
+ refreshToken?: string;
155
+ userId?: string;
156
+ baseUrl?: string;
157
+ guid?: string;
158
+ workspacePath?: string;
159
+ }
137
160
  export interface FileToolClaude {
138
161
  cliPath?: string;
139
162
  workDir?: string;
@@ -167,6 +190,7 @@ export interface FileConfig {
167
190
  wechat?: FilePlatformWechat;
168
191
  wework?: FilePlatformWework;
169
192
  dingtalk?: FilePlatformDingtalk;
193
+ workbuddy?: FilePlatformWorkBuddy;
170
194
  };
171
195
  env?: Record<string, string>;
172
196
  aiCommand?: string;
package/dist/config.js CHANGED
@@ -233,6 +233,7 @@ export function loadConfig() {
233
233
  const fileWechat = file.platforms?.wechat;
234
234
  const fileWework = file.platforms?.wework;
235
235
  const fileDingtalk = file.platforms?.dingtalk;
236
+ const fileWorkBuddy = file.platforms?.workbuddy;
236
237
  // 1. 加载各平台凭证(env 优先,其次新结构,最后旧字段)
237
238
  const telegramBotToken = process.env.TELEGRAM_BOT_TOKEN ??
238
239
  fileTelegram?.botToken ??
@@ -276,6 +277,19 @@ export function loadConfig() {
276
277
  fileDingtalk?.clientSecret;
277
278
  const dingtalkCardTemplateId = process.env.DINGTALK_CARD_TEMPLATE_ID ??
278
279
  fileDingtalk?.cardTemplateId;
280
+ // WorkBuddy credentials
281
+ const workbuddyAccessToken = process.env.WORKBUDDY_ACCESS_TOKEN ??
282
+ fileWorkBuddy?.accessToken;
283
+ const workbuddyRefreshToken = process.env.WORKBUDDY_REFRESH_TOKEN ??
284
+ fileWorkBuddy?.refreshToken;
285
+ const workbuddyUserId = process.env.WORKBUDDY_USER_ID ??
286
+ fileWorkBuddy?.userId;
287
+ const workbuddyBaseUrl = process.env.WORKBUDDY_BASE_URL ??
288
+ fileWorkBuddy?.baseUrl;
289
+ const workbuddyGuid = process.env.WORKBUDDY_GUID ??
290
+ fileWorkBuddy?.guid;
291
+ const workbuddyWorkspacePath = process.env.WORKBUDDY_WORKSPACE_PATH ??
292
+ fileWorkBuddy?.workspacePath;
279
293
  // 2. 计算启用平台
280
294
  const enabledPlatforms = [];
281
295
  const telegramEnabledFlag = fileTelegram?.enabled;
@@ -284,6 +298,7 @@ export function loadConfig() {
284
298
  const wechatEnabledFlag = fileWechat?.enabled;
285
299
  const weworkEnabledFlag = fileWework?.enabled;
286
300
  const dingtalkEnabledFlag = fileDingtalk?.enabled;
301
+ const workbuddyEnabledFlag = fileWorkBuddy?.enabled;
287
302
  const telegramEnabled = !!telegramBotToken && (telegramEnabledFlag !== false);
288
303
  const feishuEnabled = !!(feishuAppId && feishuAppSecret) && (feishuEnabledFlag !== false);
289
304
  const qqEnabled = !!(qqAppId && qqSecret) && (qqEnabledFlag !== false);
@@ -294,6 +309,8 @@ export function loadConfig() {
294
309
  // 企业微信只需要 corpId (botId) 和 secret
295
310
  const weworkEnabled = !!(weworkCorpId && weworkSecret) && (weworkEnabledFlag !== false);
296
311
  const dingtalkEnabled = !!(dingtalkClientId && dingtalkClientSecret) && (dingtalkEnabledFlag !== false);
312
+ // WorkBuddy 需要 OAuth 凭证
313
+ const workbuddyEnabled = !!(workbuddyAccessToken && workbuddyRefreshToken && workbuddyUserId) && (workbuddyEnabledFlag !== false);
297
314
  if (telegramEnabled)
298
315
  enabledPlatforms.push('telegram');
299
316
  if (feishuEnabled)
@@ -306,6 +323,8 @@ export function loadConfig() {
306
323
  enabledPlatforms.push('wework');
307
324
  if (dingtalkEnabled)
308
325
  enabledPlatforms.push('dingtalk');
326
+ if (workbuddyEnabled)
327
+ enabledPlatforms.push('workbuddy');
309
328
  if (enabledPlatforms.length === 0) {
310
329
  throw new Error('至少需要配置 Telegram、Feishu、WeChat、WeWork 或 DingTalk 其中一个平台(可以通过环境变量或 config.json)');
311
330
  }
@@ -332,6 +351,9 @@ export function loadConfig() {
332
351
  const dingtalkAllowedUserIds = process.env.DINGTALK_ALLOWED_USER_IDS !== undefined
333
352
  ? parseCommaSeparated(process.env.DINGTALK_ALLOWED_USER_IDS)
334
353
  : fileDingtalk?.allowedUserIds ?? allowedUserIds;
354
+ const workbuddyAllowedUserIds = process.env.WORKBUDDY_ALLOWED_USER_IDS !== undefined
355
+ ? parseCommaSeparated(process.env.WORKBUDDY_ALLOWED_USER_IDS)
356
+ : fileWorkBuddy?.allowedUserIds ?? allowedUserIds;
335
357
  // 5. AI / 工作目录 / 安全配置(从 tools 读取)
336
358
  const aiCommand = normalizeAiCommand(process.env.AI_COMMAND ?? file.aiCommand, 'claude');
337
359
  const tc = file.tools?.claude ?? {};
@@ -579,6 +601,29 @@ export function loadConfig() {
579
601
  allowedUserIds: dingtalkAllowedUserIds,
580
602
  cardTemplateId: dingtalkCardTemplateId,
581
603
  },
604
+ workbuddy: workbuddyEnabled
605
+ ? {
606
+ enabled: true,
607
+ aiCommand: normalizeAiCommand(file.platforms?.workbuddy?.aiCommand, aiCommand),
608
+ allowedUserIds: workbuddyAllowedUserIds,
609
+ accessToken: workbuddyAccessToken,
610
+ refreshToken: workbuddyRefreshToken,
611
+ userId: workbuddyUserId,
612
+ baseUrl: workbuddyBaseUrl,
613
+ guid: workbuddyGuid,
614
+ workspacePath: workbuddyWorkspacePath,
615
+ }
616
+ : {
617
+ enabled: false,
618
+ aiCommand: normalizeAiCommand(file.platforms?.workbuddy?.aiCommand, aiCommand),
619
+ allowedUserIds: workbuddyAllowedUserIds,
620
+ accessToken: workbuddyAccessToken,
621
+ refreshToken: workbuddyRefreshToken,
622
+ userId: workbuddyUserId,
623
+ baseUrl: workbuddyBaseUrl,
624
+ guid: workbuddyGuid,
625
+ workspacePath: workbuddyWorkspacePath,
626
+ },
582
627
  };
583
628
  return {
584
629
  enabledPlatforms,
@@ -608,6 +653,7 @@ export function loadConfig() {
608
653
  wechatAllowedUserIds,
609
654
  weworkAllowedUserIds,
610
655
  dingtalkAllowedUserIds,
656
+ workbuddyAllowedUserIds,
611
657
  aiCommand,
612
658
  codexCliPath,
613
659
  codebuddyCliPath,
@@ -11,6 +11,8 @@ export declare const CARDKIT_THROTTLE_MS = 80;
11
11
  export declare const TELEGRAM_THROTTLE_MS = 200;
12
12
  /** WeChat 流式更新节流:1000ms(AGP 协议建议值) */
13
13
  export declare const WECHAT_THROTTLE_MS = 1000;
14
+ /** WorkBuddy 流式更新节流:1000ms(Centrifuge 协议建议值) */
15
+ export declare const WORKBUDDY_THROTTLE_MS = 1000;
14
16
  export declare const WEWORK_THROTTLE_MS = 500;
15
17
  export declare const MAX_TELEGRAM_MESSAGE_LENGTH = 4000;
16
18
  export declare const MAX_FEISHU_MESSAGE_LENGTH = 4000;
package/dist/constants.js CHANGED
@@ -32,6 +32,8 @@ export const CARDKIT_THROTTLE_MS = 80;
32
32
  export const TELEGRAM_THROTTLE_MS = 200;
33
33
  /** WeChat 流式更新节流:1000ms(AGP 协议建议值) */
34
34
  export const WECHAT_THROTTLE_MS = 1000;
35
+ /** WorkBuddy 流式更新节流:1000ms(Centrifuge 协议建议值) */
36
+ export const WORKBUDDY_THROTTLE_MS = 1000;
35
37
  export const WEWORK_THROTTLE_MS = 500;
36
38
  export const MAX_TELEGRAM_MESSAGE_LENGTH = 4000;
37
39
  export const MAX_FEISHU_MESSAGE_LENGTH = 4000;
package/dist/index.js CHANGED
@@ -23,6 +23,8 @@ import { setupWeWorkHandlers } from "./wework/event-handler.js";
23
23
  import { sendProactiveTextReply as sendWeWorkTextReply } from "./wework/message-sender.js";
24
24
  import { initDingTalk, stopDingTalk, formatDingTalkInitError } from "./dingtalk/client.js";
25
25
  import { setupDingTalkHandlers } from "./dingtalk/event-handler.js";
26
+ import { initWorkBuddy, stopWorkBuddy } from "./workbuddy/client.js";
27
+ import { setupWorkBuddyHandlers } from "./workbuddy/event-handler.js";
26
28
  import { initAdapters, cleanupAdapters } from "./adapters/registry.js";
27
29
  import { SessionManager } from "./session/session-manager.js";
28
30
  import { loadActiveChats, getActiveChatId, flushActiveChats, } from "./shared/active-chats.js";
@@ -34,8 +36,8 @@ const require = createRequire(import.meta.url);
34
36
  const { version: APP_VERSION } = require("../package.json");
35
37
  const log = createLogger("Main");
36
38
  async function sendLifecycleNotification(platform, message) {
37
- // DingTalk 不支持主动发消息(OpenAPI 需 robotCode 等,易报 robot 不存在),跳过启动/关闭通知
38
- if (platform === "dingtalk")
39
+ // DingTalk 和 WorkBuddy 不支持主动发消息(OpenAPI 需 robotCode 等,易报 robot 不存在),跳过启动/关闭通知
40
+ if (platform === "dingtalk" || platform === "workbuddy")
39
41
  return;
40
42
  const telegramChatId = getActiveChatId("telegram");
41
43
  const feishuChatId = getActiveChatId("feishu");
@@ -174,6 +176,7 @@ export async function main() {
174
176
  let wechatHandle = null;
175
177
  let weworkHandle = null;
176
178
  let dingtalkHandle = null;
179
+ let workbuddyHandle = null;
177
180
  // Track successfully initialized platforms
178
181
  const successfulPlatforms = [];
179
182
  if (config.enabledPlatforms.includes("telegram")) {
@@ -237,6 +240,16 @@ export async function main() {
237
240
  log.error("Failed to initialize DingTalk:", formatDingTalkInitError(err));
238
241
  }
239
242
  }
243
+ if (config.enabledPlatforms.includes("workbuddy")) {
244
+ try {
245
+ workbuddyHandle = setupWorkBuddyHandlers(config, sessionManager);
246
+ await initWorkBuddy(config, workbuddyHandle.handleEvent);
247
+ successfulPlatforms.push("workbuddy");
248
+ }
249
+ catch (err) {
250
+ log.error("Failed to initialize WorkBuddy:", err);
251
+ }
252
+ }
240
253
  // Require at least one platform to start successfully
241
254
  if (successfulPlatforms.length === 0) {
242
255
  throw new Error("No platforms initialized successfully. Service cannot start.");
@@ -283,6 +296,8 @@ export async function main() {
283
296
  stopWeWork();
284
297
  dingtalkHandle?.stop();
285
298
  stopDingTalk();
299
+ workbuddyHandle?.stop();
300
+ stopWorkBuddy();
286
301
  sessionManager.destroy();
287
302
  cleanupAdapters();
288
303
  flushActiveChats();
package/dist/setup.js CHANGED
@@ -940,8 +940,9 @@ const PLATFORM_LABELS = {
940
940
  wework: "企业微信",
941
941
  dingtalk: "钉钉",
942
942
  wechat: "微信(测试中)",
943
+ workbuddy: "WorkBuddy",
943
944
  };
944
- const ALL_PLATFORMS = ["telegram", "feishu", "qq", "wework", "dingtalk", "wechat"];
945
+ const ALL_PLATFORMS = ["telegram", "feishu", "qq", "wework", "dingtalk", "wechat", "workbuddy"];
945
946
  /**
946
947
  * 启动时让用户选择要启用的平台(无论单通道还是多通道)
947
948
  * 显示全部 4 个平台,已配置的预选;若用户选择未配置的,引导运行 init
@@ -6,8 +6,8 @@ export interface DingTalkActiveTarget {
6
6
  updatedAt: number;
7
7
  }
8
8
  export declare function loadActiveChats(): void;
9
- export declare function getActiveChatId(platform: 'dingtalk' | 'feishu' | 'qq' | 'telegram' | 'wechat' | 'wework'): string | undefined;
10
- export declare function setActiveChatId(platform: 'dingtalk' | 'feishu' | 'qq' | 'telegram' | 'wechat' | 'wework', chatId: string): void;
9
+ export declare function getActiveChatId(platform: 'dingtalk' | 'feishu' | 'qq' | 'telegram' | 'wechat' | 'wework' | 'workbuddy'): string | undefined;
10
+ export declare function setActiveChatId(platform: 'dingtalk' | 'feishu' | 'qq' | 'telegram' | 'wechat' | 'wework' | 'workbuddy', chatId: string): void;
11
11
  export declare function getDingTalkActiveTarget(): DingTalkActiveTarget | undefined;
12
12
  export declare function setDingTalkActiveTarget(target: Omit<DingTalkActiveTarget, 'updatedAt'>): void;
13
13
  export declare function flushActiveChats(): void;
@@ -0,0 +1,74 @@
1
+ /**
2
+ * WorkBuddy Centrifuge Client - WebSocket connection for WeChat KF messages
3
+ */
4
+ import type { WorkBuddyState, PromptResponsePayload } from './types.js';
5
+ /** Centrifuge client configuration */
6
+ export interface CentrifugeClientConfig {
7
+ url: string;
8
+ connectionToken: string;
9
+ subscriptionToken: string;
10
+ channel: string;
11
+ guid: string;
12
+ userId: string;
13
+ httpBaseUrl?: string;
14
+ httpAccessToken?: string;
15
+ workspaceSessionId?: string;
16
+ }
17
+ /** Client callbacks */
18
+ export interface CentrifugeCallbacks {
19
+ onConnected?: () => void;
20
+ onDisconnected?: (reason?: string) => void;
21
+ onError?: (error: Error) => void;
22
+ onMessage?: (chatId: string, msgId: string, content: string) => void;
23
+ }
24
+ export declare class WorkBuddyCentrifugeClient {
25
+ private config;
26
+ private callbacks;
27
+ private client;
28
+ private sub;
29
+ private extraSubs;
30
+ private state;
31
+ private processedMsgIds;
32
+ private static readonly MAX_MSG_ID_CACHE;
33
+ constructor(config: CentrifugeClientConfig, callbacks?: CentrifugeCallbacks);
34
+ get logPrefix(): string;
35
+ getState(): WorkBuddyState;
36
+ start(): void;
37
+ stop(): void;
38
+ setCallbacks(callbacks: Partial<CentrifugeCallbacks>): void;
39
+ /**
40
+ * Subscribe to additional channel
41
+ */
42
+ subscribeChannel(channel: string, subscriptionToken: string): void;
43
+ /**
44
+ * Send message chunk through Centrifuge
45
+ */
46
+ sendMessageChunk(sessionId: string, promptId: string, content: {
47
+ type: string;
48
+ text?: string;
49
+ }, guid?: string, userId?: string): void;
50
+ /**
51
+ * Send tool call through Centrifuge
52
+ */
53
+ sendToolCall(sessionId: string, promptId: string, toolCall: {
54
+ id: string;
55
+ name: string;
56
+ input?: Record<string, unknown>;
57
+ }, guid?: string, userId?: string): void;
58
+ /**
59
+ * Send prompt response (for WeChat KF, use HTTP instead)
60
+ */
61
+ sendPromptResponse(payload: PromptResponsePayload, _guid?: string, _userId?: string): void;
62
+ /**
63
+ * Handle incoming publication from Centrifuge
64
+ */
65
+ private handlePublication;
66
+ /**
67
+ * Send AGP envelope through Centrifuge
68
+ */
69
+ private sendEnvelope;
70
+ /**
71
+ * Clean up old message IDs from cache
72
+ */
73
+ private cleanMsgIdCache;
74
+ }