@wu529778790/open-im 1.9.4-beta.10 → 1.9.4-beta.11

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.
@@ -36,7 +36,8 @@ function loadUserPluginSettings() {
36
36
  return null;
37
37
  }
38
38
  }
39
- const userPluginSettings = loadUserPluginSettings();
39
+ // Pre-load user plugin settings to cache Claude Code user preferences
40
+ loadUserPluginSettings();
40
41
  // 存储所有活跃的 SDKSession 对象,key 为 sessionId
41
42
  // 使用 Map 而不是 Set,因为我们需要通过 sessionId 获取 session
42
43
  const activeSessions = new Map();
@@ -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: Platform, handleClaudeRequest: ClaudeRequestHandler): Promise<boolean>;
21
+ dispatch(text: string, chatId: string, userId: string, platform: Platform, _handleClaudeRequest: ClaudeRequestHandler): Promise<boolean>;
22
22
  private handleHelp;
23
23
  private handleNew;
24
24
  private handlePwd;
@@ -9,7 +9,7 @@ export class CommandHandler {
9
9
  constructor(deps) {
10
10
  this.deps = deps;
11
11
  }
12
- async dispatch(text, chatId, userId, platform, handleClaudeRequest) {
12
+ async dispatch(text, chatId, userId, platform, _handleClaudeRequest) {
13
13
  const t = text.trim();
14
14
  if (platform === 'telegram' && t === '/start') {
15
15
  await this.deps.sender.sendTextReply(chatId, '欢迎使用 open-im AI CLI 桥接!\n\n发送消息与 AI 交互,输入 /help 查看帮助。');
@@ -75,7 +75,7 @@ export class CommandHandler {
75
75
  await this.deps.sender.sendTextReply(chatId, lines.join('\n'));
76
76
  return true;
77
77
  }
78
- async handleCd(chatId, userId, dir, platform) {
78
+ async handleCd(chatId, userId, dir, _platform) {
79
79
  // 如果 dir 为空,显示目录选择界面
80
80
  if (!dir) {
81
81
  const currentDir = this.deps.sessionManager.getWorkDir(userId);
package/dist/config.js CHANGED
@@ -13,15 +13,6 @@ const log = createLogger('config');
13
13
  // Re-export file I/O and credential helpers from sub-modules
14
14
  export { CONFIG_PATH, loadFileConfig, saveFileConfig, getClaudeConfigHome, loadClaudeSettingsEnv, saveClaudeSettingsEnv, normalizeAiCommand, hasCodexAuth, parseCommaSeparated, } from './config/file-io.js';
15
15
  import { loadFileConfig, normalizeAiCommand, hasCodexAuth, parseCommaSeparated, loadClaudeSettingsEnv, } from './config/file-io.js';
16
- const AI_COMMANDS = ['claude', 'codex', 'codebuddy'];
17
- /** 检查是否已配置 Claude API 凭证 */
18
- function hasClaudeCredentials() {
19
- return !!(process.env.ANTHROPIC_API_KEY ||
20
- process.env.ANTHROPIC_AUTH_TOKEN ||
21
- process.env.CLAUDE_CODE_OAUTH_TOKEN ||
22
- process.env.ANTHROPIC_BASE_URL // 使用自定义 API(如第三方模型)时可能不需要标准凭证
23
- );
24
- }
25
16
  /** 检测是否需要交互式配置(无 token 且无环境变量) */
26
17
  export function needsSetup() {
27
18
  // 环境变量已提供任一平台的凭证,则认为已配置
@@ -1,5 +1,3 @@
1
- import { createLogger } from '../logger.js';
2
- const log = createLogger('DingTalk');
3
1
  export const DINGTALK_OPENAPI_BASE = 'https://api.dingtalk.com';
4
2
  const DINGTALK_OAPI_BASE = 'https://oapi.dingtalk.com';
5
3
  const TEXT_MSG_KEY = 'sampleText';
@@ -27,8 +27,7 @@ const streamStates = new Map();
27
27
  // Periodic cleanup of orphaned stream states (max 30 minutes)
28
28
  const STREAM_MAX_AGE_MS = 30 * 60 * 1000;
29
29
  setInterval(() => {
30
- const now = Date.now();
31
- for (const [id, state] of streamStates) {
30
+ for (const [id] of streamStates) {
32
31
  // streamStates in DingTalk don't have createdAt, clean up by size
33
32
  if (streamStates.size > 50) {
34
33
  streamStates.delete(id);
@@ -212,49 +212,6 @@ export function setupFeishuHandlers(config, sessionManager) {
212
212
  },
213
213
  }),
214
214
  });
215
- /**
216
- * 解析 action value(兼容对象、JSON 字符串)
217
- */
218
- function parseActionValue(raw) {
219
- if (!raw)
220
- return null;
221
- let obj = null;
222
- if (typeof raw === 'string') {
223
- try {
224
- obj = JSON.parse(raw);
225
- }
226
- catch (err) {
227
- log.debug('Failed to parse action value as JSON:', err);
228
- return null;
229
- }
230
- }
231
- else if (typeof raw === 'object' && raw !== null) {
232
- obj = raw;
233
- }
234
- return obj?.action && obj?.value ? obj : null;
235
- }
236
- /**
237
- * 从卡片回调事件中提取延时更新 token(格式 c-xxxx)
238
- * 飞书文档:从卡片交互返回内容获取,用于延时更新接口
239
- */
240
- function extractCardToken(data) {
241
- const raw = data;
242
- const event = (raw?.event ?? raw);
243
- const action = event?.action;
244
- const context = event?.context;
245
- const candidates = [
246
- event?.token,
247
- event?.open_api_token,
248
- raw?.token,
249
- action?.token,
250
- context?.token,
251
- ].filter((t) => typeof t === 'string' && t.startsWith('c-'));
252
- const token = candidates[0] ?? null;
253
- if (!token) {
254
- log.debug('[extractCardToken] No token found, event keys:', Object.keys(event ?? {}));
255
- }
256
- return token;
257
- }
258
215
  /**
259
216
  * Handle card button click (card.action.trigger) - 需在 3 秒内返回响应
260
217
  * 同步只返回 toast,避免 200672;用延时更新 API 异步替换为只读卡片,防止二次点击
package/dist/index.js CHANGED
@@ -93,13 +93,20 @@ const PLATFORM_MODULES = {
93
93
  };
94
94
  async function sendLifecycleNotification(platform, message) {
95
95
  const mod = PLATFORM_MODULES[platform];
96
- if (!mod?.sendNotification)
96
+ if (!mod?.sendNotification) {
97
+ log.debug(`[${platform}] No sendNotification, skipping lifecycle notification`);
97
98
  return;
99
+ }
98
100
  const chatId = getActiveChatId(platform);
99
- if (!chatId)
101
+ if (!chatId) {
102
+ log.info(`[${platform}] No active chatId, skipping lifecycle notification`);
100
103
  return;
101
- await mod.sendNotification(chatId, message).catch((err) => {
102
- log.debug(`Failed to send ${platform} notification:`, err);
104
+ }
105
+ log.info(`[${platform}] Sending lifecycle notification to chatId=${chatId}`);
106
+ await mod.sendNotification(chatId, message).then(() => {
107
+ log.info(`[${platform}] Lifecycle notification sent successfully`);
108
+ }).catch((err) => {
109
+ log.warn(`Failed to send ${platform} notification:`, err);
103
110
  });
104
111
  }
105
112
  function buildStartupMessage(platform, appVersion, aiCommand, defaultWorkDir, sessionManager) {
@@ -5,10 +5,12 @@ import { createPlatformEventContext } from './create-event-context.js';
5
5
  import { AccessControl } from '../access/access-control.js';
6
6
  import { CommandHandler } from '../commands/handler.js';
7
7
  // Setup mocks for AccessControl
8
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
8
9
  vi.mocked(AccessControl).mockImplementation(function (ids) {
9
10
  this.isAllowed = (userId) => ids.length === 0 || ids.includes(userId);
10
11
  });
11
12
  // Setup mocks for CommandHandler
13
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
12
14
  vi.mocked(CommandHandler).mockImplementation(function (deps) {
13
15
  this.dispatch = vi.fn();
14
16
  this.deps = deps;
@@ -94,7 +94,7 @@ export function createPlatformAIRequestHandler(deps) {
94
94
  },
95
95
  };
96
96
  // Merge in platform callbacks (if provided)
97
- let mergedCallbacks = { ...defaultCallbacks };
97
+ const mergedCallbacks = { ...defaultCallbacks };
98
98
  // Use taskCallbacksFactory if provided (has full context access)
99
99
  if (taskCallbacksFactory) {
100
100
  const factoryCallbacks = taskCallbacksFactory({
@@ -17,6 +17,9 @@
17
17
  import type { Platform } from '../config.js';
18
18
  import type { PlatformEventContext } from './create-event-context.js';
19
19
  import type { EnqueueResult } from '../queue/request-queue.js';
20
+ import type { HandleAIRequestParams } from './handle-ai-request.js';
21
+ /** AI request handler function type (object params, as returned by createPlatformAIRequestHandler). */
22
+ export type HandleAIRequestFn = (params: HandleAIRequestParams) => Promise<void>;
20
23
  /**
21
24
  * Callback to send a text reply to the user.
22
25
  * Used for access-denied messages and queue status notifications.
@@ -37,7 +40,8 @@ export interface HandleTextFlowParams {
37
40
  /** The platform event context (accessControl, commandHandler, requestQueue, etc.). */
38
41
  ctx: PlatformEventContext;
39
42
  /** The platform-specific AI request handler (from createPlatformAIRequestHandler). */
40
- handleAIRequest: any;
43
+ /** The platform-specific AI request handler (from createPlatformAIRequestHandler). */
44
+ handleAIRequest: HandleAIRequestFn;
41
45
  /** Function to send a text reply back to the user. */
42
46
  sendTextReply: SendTextReplyFn;
43
47
  /** Optional: additional workDir override. If not provided, resolved from sessionManager. */
@@ -53,8 +53,8 @@ export async function handleTextFlow(params) {
53
53
  setChatUser(chatId, userId, platform);
54
54
  // 4. Command dispatch
55
55
  try {
56
- // Create a wrapper that matches ClaudeRequestHandler signature
57
- const handleAIRequestWrapper = (userId, chatId, prompt, workDir, convId, _threadCtx, replyToMessageId) => {
56
+ // Wrapper: dispatch expects positional args, handleAIRequest expects an object
57
+ const dispatchHandler = (userId, chatId, prompt, workDir, convId, _threadCtx, replyToMessageId) => {
58
58
  return handleAIRequest({
59
59
  userId,
60
60
  chatId,
@@ -64,7 +64,7 @@ export async function handleTextFlow(params) {
64
64
  replyToMessageId,
65
65
  });
66
66
  };
67
- const handled = await ctx.commandHandler.dispatch(text, chatId, userId, platform, handleAIRequestWrapper);
67
+ const handled = await ctx.commandHandler.dispatch(text, chatId, userId, platform, dispatchHandler);
68
68
  if (handled) {
69
69
  return true;
70
70
  }
package/dist/qq/client.js CHANGED
@@ -98,6 +98,7 @@ async function getGatewayUrl(config) {
98
98
  }
99
99
  function normalizeInboundEvent(payload) {
100
100
  const type = payload.t;
101
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
101
102
  const data = (payload.d ?? {});
102
103
  const attachments = Array.isArray(data.attachments)
103
104
  ? data.attachments.map((attachment) => ({
@@ -118,7 +118,7 @@ export function setupQQHandlers(config, sessionManager) {
118
118
  sessionManager,
119
119
  sender: { sendTextReply, sendDirectorySelection },
120
120
  });
121
- const { accessControl, requestQueue, runningTasks, commandHandler } = platformContext;
121
+ const { accessControl, requestQueue, runningTasks } = platformContext;
122
122
  const recentEventIds = new Map();
123
123
  const recentEventFingerprints = new Map();
124
124
  // QQ-specific: Store taskKey -> { { chatId, msgId } } mapping
@@ -37,9 +37,6 @@ function getToolTitle(toolId, status) {
37
37
  },
38
38
  });
39
39
  }
40
- function generateReqId() {
41
- return `${Date.now()}-${randomBytes(8).toString('hex')}`;
42
- }
43
40
  function generateStreamId() {
44
41
  return `${Date.now()}-${randomBytes(8).toString('hex')}`;
45
42
  }
@@ -30,16 +30,16 @@ export function setupWorkBuddyHandlers(config, sessionManager) {
30
30
  const stopTaskCleanup = startTaskCleanup(ctx.runningTasks);
31
31
  // WorkBuddy-specific sender callbacks (no thinking message needed)
32
32
  const platformSender = {
33
- sendThinkingMessage: async (chatId, _replyToMessageId, toolId) => {
33
+ sendThinkingMessage: async (_chatId, _replyToMessageId, _toolId) => {
34
34
  // WorkBuddy uses incoming msgId as thinking message ID
35
35
  // This is a no-op since we'll use the incoming msgId
36
36
  return 'workbuddy_no_thinking';
37
37
  },
38
- sendTextReply: async (chatId, text) => {
38
+ sendTextReply: async (_chatId, text) => {
39
39
  // WorkBuddy-specific reply (needs msgId captured per event)
40
- await sendTextReply(null, chatId, text, '');
40
+ await sendTextReply(null, _chatId, text, '');
41
41
  },
42
- startTyping: (chatId) => {
42
+ startTyping: (_chatId) => {
43
43
  // WorkBuddy doesn't support typing indicators
44
44
  return () => { };
45
45
  },
@@ -50,10 +50,10 @@ export function setupWorkBuddyHandlers(config, sessionManager) {
50
50
  // WorkBuddy doesn't support streaming updates via Centrifuge
51
51
  log.debug(`Stream update (not sent): ${content.substring(0, 50)}...`);
52
52
  },
53
- sendComplete: async (content) => {
53
+ sendComplete: async (_content) => {
54
54
  // Will be handled per-event with correct msgId
55
55
  },
56
- sendError: async (error) => {
56
+ sendError: async (_error) => {
57
57
  // Will be handled per-event with correct msgId
58
58
  },
59
59
  extraCleanup: () => {
@@ -72,7 +72,7 @@ export function setupWorkBuddyHandlers(config, sessionManager) {
72
72
  // Create platform-specific AI request handler
73
73
  // Note: WorkBuddy uses a different handleAIRequest signature with msgId
74
74
  // We'll need to wrap the standard handler
75
- const standardHandleAIRequest = createPlatformAIRequestHandler({
75
+ createPlatformAIRequestHandler({
76
76
  platform: 'workbuddy',
77
77
  config,
78
78
  sessionManager,
@@ -2,8 +2,6 @@
2
2
  * WorkBuddy OAuth - CodeBuddy authentication for WeChat KF integration
3
3
  */
4
4
  import { hostname } from 'node:os';
5
- import { createLogger } from '../logger.js';
6
- const log = createLogger('WorkBuddyOAuth');
7
5
  const DEFAULT_BASE_URL = 'https://copilot.tencent.com';
8
6
  const PLATFORM = 'ide';
9
7
  export class WorkBuddyOAuth {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wu529778790/open-im",
3
- "version": "1.9.4-beta.10",
3
+ "version": "1.9.4-beta.11",
4
4
  "description": "Multi-platform IM bridge for AI CLI tools (Claude, Codex, CodeBuddy)",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",