@wu529778790/open-im 1.10.8 → 1.10.9-beta.1

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.
@@ -59,6 +59,8 @@ let sessionIdleTtlMs = 30 * 60 * 1000; // 默认 30 分钟未使用则清理
59
59
  let sessionIdleCleanupDisabled = false;
60
60
  const CLEANUP_INTERVAL_MS = 5 * 60 * 1000; // 每 5 分钟检查一次
61
61
  const MAX_ACTIVE_SESSIONS = 100;
62
+ const MAX_CAPTURED_STDERR_CHARS = 4000;
63
+ const MAX_EXPOSED_STDERR_CHARS = 500;
62
64
  let sessionSeq = 0;
63
65
  /**
64
66
  * 由 initAdapters 根据配置调用。ttlMinutes≤0 时关闭空闲回收(仍受 MAX_ACTIVE_SESSIONS 限制)。
@@ -137,6 +139,35 @@ function isResult(msg) {
137
139
  function isSessionCorruptionError(msg) {
138
140
  return /session\s*(not found|expired|corrupt)|no\s*conversation\s*found/i.test(msg);
139
141
  }
142
+ function appendStderrSnippet(previous, chunk) {
143
+ const next = previous + chunk;
144
+ if (next.length <= MAX_CAPTURED_STDERR_CHARS)
145
+ return next;
146
+ return next.slice(-MAX_CAPTURED_STDERR_CHARS);
147
+ }
148
+ function getUsefulStderrSnippet(stderr) {
149
+ const lines = stderr
150
+ .split(/\r?\n/)
151
+ .map((line) => line.trim())
152
+ .filter(Boolean);
153
+ if (lines.length === 0)
154
+ return undefined;
155
+ const summary = lines.slice(-3).join(' | ');
156
+ return summary.length <= MAX_EXPOSED_STDERR_CHARS
157
+ ? summary
158
+ : summary.slice(0, MAX_EXPOSED_STDERR_CHARS - 3) + '...';
159
+ }
160
+ function enrichClaudeErrorMessage(message, stderr) {
161
+ const snippet = getUsefulStderrSnippet(stderr);
162
+ if (!snippet)
163
+ return message;
164
+ if (message.includes(snippet))
165
+ return message;
166
+ if (/process exited with code \d+/i.test(message) || /无输出/.test(message) || /未知错误/.test(message)) {
167
+ return `${message} stderr: ${snippet}`;
168
+ }
169
+ return message;
170
+ }
140
171
  /**
141
172
  * 获取或创建 SDKSession
142
173
  * @param sessionId 已有的 sessionId,如果为 undefined 则创建新会话
@@ -145,7 +176,7 @@ function isSessionCorruptionError(msg) {
145
176
  * @param permissionMode 权限模式
146
177
  * @returns SDKSession 对象和实际的 sessionId
147
178
  */
148
- async function getOrCreateSession(sessionId, workDir, model, permissionMode) {
179
+ async function getOrCreateSession(sessionId, workDir, model, permissionMode, onStderr) {
149
180
  // 刷新 Claude 环境变量(支持 cc switch 后无需重启即可生效)
150
181
  refreshClaudeEnvToProcess();
151
182
  const resolvedModel = model?.trim() || process.env.ANTHROPIC_MODEL?.trim() || 'claude-opus-4-5';
@@ -155,6 +186,7 @@ async function getOrCreateSession(sessionId, workDir, model, permissionMode) {
155
186
  const sessionOptions = {
156
187
  model: resolvedModel,
157
188
  permissionMode,
189
+ stderr: onStderr,
158
190
  };
159
191
  const baseUrl = process.env.ANTHROPIC_BASE_URL ?? '(default)';
160
192
  log.info(`[V2] getOrCreateSession model param=${String(model ?? '')} resolved=${resolvedModel} baseUrl=${baseUrl} workDir=${workDir}`);
@@ -260,6 +292,7 @@ export class ClaudeSDKAdapter {
260
292
  let pendingTempId; // 记录临时 ID,用于 abort 时清理
261
293
  let runSettled = false;
262
294
  let currentStream; // 用于 abort 时立即中断 stream
295
+ let recentStderr = '';
263
296
  const permissionMode = options?.skipPermissions
264
297
  ? 'bypassPermissions'
265
298
  : options?.permissionMode === 'acceptEdits'
@@ -279,7 +312,9 @@ export class ClaudeSDKAdapter {
279
312
  log.info(`[V2] Session: ${sessionId ?? 'new'}, prompt="${prompt.slice(0, 50)}..."`);
280
313
  log.info(`[V2] model param=${String(options?.model ?? '')} baseUrl=${process.env.ANTHROPIC_BASE_URL ?? '(default)'}`);
281
314
  // 获取或创建会话
282
- const { session, sessionId: returnedId } = await getOrCreateSession(sessionId, workDir, options?.model, permissionMode);
315
+ const { session, sessionId: returnedId } = await getOrCreateSession(sessionId, workDir, options?.model, permissionMode, (data) => {
316
+ recentStderr = appendStderrSnippet(recentStderr, data);
317
+ });
283
318
  if (returnedId.startsWith('pending-')) {
284
319
  pendingTempId = returnedId;
285
320
  }
@@ -380,7 +415,7 @@ export class ClaudeSDKAdapter {
380
415
  }
381
416
  callbacks.onSessionInvalid?.();
382
417
  }
383
- const errMsg = errs[0] || '未知错误';
418
+ const errMsg = enrichClaudeErrorMessage(errs[0] || '未知错误', recentStderr);
384
419
  callbacks.onError(errMsg);
385
420
  return;
386
421
  }
@@ -425,7 +460,7 @@ export class ClaudeSDKAdapter {
425
460
  // 流结束但无 result 也无 accumulated:必须触发回调,否则 Promise 永远挂起
426
461
  log.warn('Stream ended with no result and no accumulated text, calling onError to prevent stuck state');
427
462
  runSettled = true;
428
- callbacks.onError('AI 响应异常结束(无输出),请重试');
463
+ callbacks.onError(enrichClaudeErrorMessage('AI 响应异常结束(无输出),请重试', recentStderr));
429
464
  }
430
465
  }
431
466
  }
@@ -447,7 +482,7 @@ export class ClaudeSDKAdapter {
447
482
  }
448
483
  runSettled = true;
449
484
  const errorObj = err;
450
- const msg = errorObj.message || String(err);
485
+ const msg = enrichClaudeErrorMessage(errorObj.message || String(err), recentStderr);
451
486
  log.error(`Claude SDK V2 error: ${msg}`);
452
487
  if (errorObj.stack) {
453
488
  log.error(`Error stack: ${errorObj.stack}`);
@@ -25,14 +25,12 @@ export class CodeBuddyAdapter {
25
25
  },
26
26
  onError: (err) => {
27
27
  const msg = typeof err === 'string' ? err : String(err);
28
- const friendly = msg.includes('Authentication') || msg.includes('/login') || msg.includes('CODEBUDDY_API_KEY')
29
- ? 'CodeBuddy 需要先登录。请在终端运行 codebuddy login。'
30
- : msg.includes('No conversation found') ||
31
- msg.includes('Session not found') ||
32
- msg.includes('Invalid session') ||
33
- msg.includes('Unable to resume')
34
- ? 'CodeBuddy 会话已失效,旧 session 已清理。请直接重试当前请求。'
35
- : msg;
28
+ const friendly = msg.includes('No conversation found') ||
29
+ msg.includes('Session not found') ||
30
+ msg.includes('Invalid session') ||
31
+ msg.includes('Unable to resume')
32
+ ? 'CodeBuddy 会话已失效,旧 session 已清理。请直接重试当前请求。'
33
+ : msg;
36
34
  callbacks.onError(friendly);
37
35
  },
38
36
  onSessionId: callbacks.onSessionId,
@@ -36,19 +36,11 @@ export class CodexAdapter {
36
36
  },
37
37
  onError: (err) => {
38
38
  const msg = typeof err === "string" ? err : String(err);
39
- const friendly = msg.includes("Authentication") || msg.includes("login")
40
- ? "Codex 需要先登录。请在终端运行 codex login,或在 shell 中 export OPENAI_API_KEY。"
41
- : msg.includes("stream disconnected") ||
42
- msg.includes("error sending request") ||
43
- msg.includes("Connection refused") ||
44
- msg.includes("ENOTFOUND") ||
45
- msg.includes("ETIMEDOUT")
46
- ? "Codex 网络请求失败。如无法访问 chatgpt.com,请在 tools.codex.proxy 或 CODEX_PROXY 中配置代理。"
47
- : msg.includes("No session found") ||
48
- msg.includes("No conversation found") ||
49
- msg.includes("Unable to find session")
50
- ? "Codex 会话已失效,旧 session 已清理。请直接重试当前请求。"
51
- : msg;
39
+ const friendly = msg.includes("No session found") ||
40
+ msg.includes("No conversation found") ||
41
+ msg.includes("Unable to find session")
42
+ ? "Codex 会话已失效,旧 session 已清理。请直接重试当前请求。"
43
+ : msg;
52
44
  callbacks.onError(friendly);
53
45
  },
54
46
  onSessionId: callbacks.onSessionId,
@@ -14,6 +14,8 @@ export declare function loadClaudeSettingsEnv(): Record<string, string>;
14
14
  export declare function saveClaudeSettingsEnv(env: Record<string, string>): void;
15
15
  export declare function normalizeAiCommand(value: unknown, fallback: AiCommand): AiCommand;
16
16
  export declare function hasCodexAuth(): boolean;
17
+ export declare function hasCodeBuddyAuthIndicators(): boolean;
18
+ export declare function getClaudeSdkRuntimeIssue(): string | null;
17
19
  export declare function parseCommaSeparated(value: string): string[];
18
20
  /**
19
21
  * 将最新的 Claude 认证环境变量按优先级合并到 process.env。
@@ -1,6 +1,8 @@
1
- import { readFileSync, writeFileSync, existsSync, mkdirSync, statSync, chmodSync } from 'node:fs';
1
+ import { readFileSync, writeFileSync, existsSync, mkdirSync, statSync, chmodSync, readdirSync } from 'node:fs';
2
+ import { execFileSync } from 'node:child_process';
2
3
  import { join, dirname } from 'node:path';
3
4
  import { homedir } from 'node:os';
5
+ import { createRequire } from 'node:module';
4
6
  import { createLogger } from '../logger.js';
5
7
  import { APP_HOME } from '../constants.js';
6
8
  const log = createLogger('config');
@@ -10,12 +12,17 @@ export const CODEX_AUTH_PATHS = [
10
12
  join(homedir(), '.config', 'codex', 'auth.json'),
11
13
  join(homedir(), 'AppData', 'Roaming', 'codex', 'auth.json'),
12
14
  ];
15
+ const CODEBUDDY_HOME_PATHS = [
16
+ join(homedir(), '.codebuddy'),
17
+ join(homedir(), '.codebuddycn'),
18
+ ];
13
19
  const OLD_ROOT_KEYS = [
14
20
  'claudeWorkDir',
15
21
  'claudeTimeoutMs',
16
22
  'claudeModel',
17
23
  ];
18
24
  const AI_COMMANDS = ['claude', 'codex', 'codebuddy'];
25
+ const require = createRequire(import.meta.url);
19
26
  /** Claude 认证相关的环境变量 key 列表 */
20
27
  export const CLAUDE_AUTH_ENV_KEYS = [
21
28
  'ANTHROPIC_API_KEY',
@@ -231,6 +238,113 @@ export function hasCodexAuth() {
231
238
  }
232
239
  });
233
240
  }
241
+ export function hasCodeBuddyAuthIndicators() {
242
+ if (process.env.CODEBUDDY_API_KEY || process.env.CODEBUDDY_AUTH_TOKEN)
243
+ return true;
244
+ return CODEBUDDY_HOME_PATHS.some((base) => {
245
+ try {
246
+ if (!existsSync(base))
247
+ return false;
248
+ const settingsPath = join(base, 'settings.json');
249
+ if (existsSync(settingsPath)) {
250
+ try {
251
+ const settings = JSON.parse(readFileSync(settingsPath, 'utf-8'));
252
+ if (settings.apiKeyHelper)
253
+ return true;
254
+ const env = settings.env;
255
+ if (env && typeof env === 'object' && !Array.isArray(env)) {
256
+ const envRecord = env;
257
+ if (envRecord.CODEBUDDY_API_KEY || envRecord.CODEBUDDY_AUTH_TOKEN)
258
+ return true;
259
+ }
260
+ }
261
+ catch {
262
+ // Ignore malformed settings and keep checking other indicators.
263
+ }
264
+ }
265
+ const localStorageDir = join(base, 'local_storage');
266
+ if (!existsSync(localStorageDir))
267
+ return false;
268
+ return readdirSync(localStorageDir)
269
+ .filter((name) => name.endsWith('.info'))
270
+ .some((name) => {
271
+ try {
272
+ const content = readFileSync(join(localStorageDir, name), 'utf-8');
273
+ return /"userId"\s*:|"nickname"\s*:|"enterpriseName"\s*:|"accessToken"\s*:/.test(content);
274
+ }
275
+ catch {
276
+ return false;
277
+ }
278
+ });
279
+ }
280
+ catch {
281
+ return false;
282
+ }
283
+ });
284
+ }
285
+ export function getClaudeSdkRuntimeIssue() {
286
+ let executablePath;
287
+ let executableArgs;
288
+ try {
289
+ const sdkEntry = require.resolve('@anthropic-ai/claude-agent-sdk');
290
+ const sdkDir = dirname(sdkEntry);
291
+ const legacyCliPath = join(sdkDir, 'cli.js');
292
+ if (existsSync(legacyCliPath)) {
293
+ executablePath = process.execPath;
294
+ executableArgs = [legacyCliPath, '--version'];
295
+ }
296
+ else {
297
+ const binaryExt = process.platform === 'win32' ? '.exe' : '';
298
+ const packageNames = process.platform === 'linux'
299
+ ? [
300
+ `@anthropic-ai/claude-agent-sdk-linux-${process.arch}-musl`,
301
+ `@anthropic-ai/claude-agent-sdk-linux-${process.arch}`,
302
+ ]
303
+ : [`@anthropic-ai/claude-agent-sdk-${process.platform}-${process.arch}`];
304
+ let nativeBinaryPath = null;
305
+ for (const packageName of packageNames) {
306
+ try {
307
+ const candidate = require.resolve(`${packageName}/claude${binaryExt}`);
308
+ if (existsSync(candidate)) {
309
+ nativeBinaryPath = candidate;
310
+ break;
311
+ }
312
+ }
313
+ catch {
314
+ // Try the next package name.
315
+ }
316
+ }
317
+ if (!nativeBinaryPath) {
318
+ return `Claude SDK 安装不完整:未找到 ${legacyCliPath},也未找到适用于 ${process.platform}-${process.arch} 的原生 Claude CLI。请重新安装依赖后再启动。`;
319
+ }
320
+ executablePath = nativeBinaryPath;
321
+ executableArgs = ['--version'];
322
+ }
323
+ }
324
+ catch (error) {
325
+ return `未找到 @anthropic-ai/claude-agent-sdk:${error instanceof Error ? error.message : String(error)}`;
326
+ }
327
+ try {
328
+ execFileSync(executablePath, executableArgs, {
329
+ stdio: 'pipe',
330
+ env: {
331
+ ...process.env,
332
+ CLAUDE_CODE_ENTRYPOINT: process.env.CLAUDE_CODE_ENTRYPOINT || 'sdk-ts',
333
+ },
334
+ timeout: 5000,
335
+ windowsHide: process.platform === 'win32',
336
+ });
337
+ }
338
+ catch (error) {
339
+ const execError = error;
340
+ const stderr = Buffer.isBuffer(execError.stderr)
341
+ ? execError.stderr.toString('utf-8')
342
+ : execError.stderr;
343
+ const details = stderr?.trim() || execError.message || String(error);
344
+ return `Claude SDK 运行时不可用:${details}`;
345
+ }
346
+ return null;
347
+ }
234
348
  export function parseCommaSeparated(value) {
235
349
  return value.split(',').map((s) => s.trim()).filter(Boolean);
236
350
  }
package/dist/config.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  export type { Platform, AiCommand, Config, FilePlatformTelegram, FilePlatformFeishu, FilePlatformQQ, FilePlatformWechat, FilePlatformWework, FilePlatformDingtalk, FilePlatformWorkBuddy, FileToolClaude, FileToolCodex, FileToolCodeBuddy, FileConfig, } from './config/types.js';
2
2
  import type { Platform, AiCommand, Config } from './config/types.js';
3
- export { CONFIG_PATH, loadFileConfig, saveFileConfig, getClaudeConfigHome, loadClaudeSettingsEnv, saveClaudeSettingsEnv, normalizeAiCommand, hasCodexAuth, parseCommaSeparated, CLAUDE_AUTH_ENV_KEYS, refreshClaudeEnvToProcess, processEnvForNonClaudeCliChild, CODEX_AUTH_PATHS, } from './config/file-io.js';
3
+ export { CONFIG_PATH, loadFileConfig, saveFileConfig, getClaudeConfigHome, getClaudeSdkRuntimeIssue, hasCodeBuddyAuthIndicators, loadClaudeSettingsEnv, saveClaudeSettingsEnv, normalizeAiCommand, hasCodexAuth, parseCommaSeparated, CLAUDE_AUTH_ENV_KEYS, refreshClaudeEnvToProcess, processEnvForNonClaudeCliChild, CODEX_AUTH_PATHS, } from './config/file-io.js';
4
4
  /** 检测是否需要交互式配置(无 token 且无环境变量) */
5
5
  export declare function needsSetup(): boolean;
6
6
  export declare function loadConfig(): Config;
package/dist/config.js CHANGED
@@ -27,8 +27,8 @@ function resolveFilePlatformAi(file, platform) {
27
27
  return normalizeAiCommand(raw ?? process.env.AI_COMMAND ?? file.aiCommand, 'claude');
28
28
  }
29
29
  // Re-export file I/O and credential helpers from sub-modules
30
- export { CONFIG_PATH, loadFileConfig, saveFileConfig, getClaudeConfigHome, loadClaudeSettingsEnv, saveClaudeSettingsEnv, normalizeAiCommand, hasCodexAuth, parseCommaSeparated, CLAUDE_AUTH_ENV_KEYS, refreshClaudeEnvToProcess, processEnvForNonClaudeCliChild, CODEX_AUTH_PATHS, } from './config/file-io.js';
31
- import { loadFileConfig, normalizeAiCommand, hasCodexAuth, parseCommaSeparated, loadClaudeSettingsEnv, } from './config/file-io.js';
30
+ export { CONFIG_PATH, loadFileConfig, saveFileConfig, getClaudeConfigHome, getClaudeSdkRuntimeIssue, hasCodeBuddyAuthIndicators, loadClaudeSettingsEnv, saveClaudeSettingsEnv, normalizeAiCommand, hasCodexAuth, parseCommaSeparated, CLAUDE_AUTH_ENV_KEYS, refreshClaudeEnvToProcess, processEnvForNonClaudeCliChild, CODEX_AUTH_PATHS, } from './config/file-io.js';
31
+ import { getClaudeSdkRuntimeIssue, hasCodeBuddyAuthIndicators, loadFileConfig, normalizeAiCommand, hasCodexAuth, parseCommaSeparated, loadClaudeSettingsEnv, } from './config/file-io.js';
32
32
  /** 检测是否需要交互式配置(无 token 且无环境变量) */
33
33
  export function needsSetup() {
34
34
  // 环境变量已提供任一平台的凭证,则认为已配置
@@ -275,6 +275,10 @@ export function loadConfig() {
275
275
  ].join('\n');
276
276
  throw new Error(errorMsg);
277
277
  }
278
+ const claudeRuntimeIssue = getClaudeSdkRuntimeIssue();
279
+ if (claudeRuntimeIssue) {
280
+ throw new Error(claudeRuntimeIssue);
281
+ }
278
282
  }
279
283
  // 7. 校验 Codex CLI(任一已启用渠道使用 codex 时)
280
284
  if (toolsNeeded.has('codex')) {
@@ -353,6 +357,10 @@ export function loadConfig() {
353
357
  throw new Error(installGuide);
354
358
  }
355
359
  }
360
+ if (!hasCodeBuddyAuthIndicators()) {
361
+ log.warn('CodeBuddy 模式:未检测到明确的登录态或 API Key。首次使用请先运行 codebuddy login;' +
362
+ '若使用自定义 Token,请在 CodeBuddy 设置或环境变量中配置。');
363
+ }
356
364
  }
357
365
  // 7. 日志与平台配置
358
366
  const logDir = process.env.LOG_DIR ?? file.logDir ?? join(APP_HOME, 'logs');
@@ -274,7 +274,9 @@ export function runAITask(deps, ctx, prompt, toolAdapter, platformAdapter) {
274
274
  resolve();
275
275
  },
276
276
  }, {
277
- model: sessionManager.getModel(ctx.userId, ctx.threadId) ?? config.claudeModel,
277
+ model: aiCommand === 'claude'
278
+ ? (sessionManager.getModel(ctx.userId, ctx.threadId) ?? config.claudeModel)
279
+ : undefined,
278
280
  chatId: ctx.chatId,
279
281
  // 默认跳过权限确认,保持全自动执行(可通过 config 或环境变量关闭)
280
282
  skipPermissions: config.skipPermissions ?? true,
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * WorkBuddy Centrifuge Client - WebSocket connection for WeChat KF messages
3
3
  */
4
- import { Centrifuge } from 'centrifuge';
4
+ import { Centrifuge, SubscriptionState, } from 'centrifuge';
5
5
  import { WebSocket } from 'ws';
6
6
  import { randomUUID } from 'node:crypto';
7
7
  import { createLogger } from '../logger.js';
@@ -17,7 +17,7 @@ export class WorkBuddyCentrifugeClient {
17
17
  callbacks;
18
18
  client = null;
19
19
  sub = null;
20
- extraSubs = [];
20
+ extraSubs = new Map();
21
21
  state = 'disconnected';
22
22
  processedMsgIds = new Set();
23
23
  consecutiveErrors = 0;
@@ -94,10 +94,10 @@ export class WorkBuddyCentrifugeClient {
94
94
  log.info(`${this.logPrefix} Stopping...`);
95
95
  this.state = 'disconnected';
96
96
  this.processedMsgIds.clear();
97
- for (const sub of this.extraSubs) {
97
+ for (const sub of this.extraSubs.values()) {
98
98
  sub.unsubscribe();
99
99
  }
100
- this.extraSubs = [];
100
+ this.extraSubs.clear();
101
101
  if (this.sub) {
102
102
  this.sub.unsubscribe();
103
103
  this.sub = null;
@@ -119,8 +119,28 @@ export class WorkBuddyCentrifugeClient {
119
119
  log.warn(`${this.logPrefix} Cannot subscribe: client not initialized`);
120
120
  return;
121
121
  }
122
+ const existing = this.extraSubs.get(channel);
123
+ if (existing) {
124
+ if (existing.state === SubscriptionState.Unsubscribed) {
125
+ log.info(`${this.logPrefix} Re-subscribing to additional channel: ${channel}`);
126
+ existing.subscribe();
127
+ }
128
+ else {
129
+ log.debug(`${this.logPrefix} Additional channel already tracked: ${channel} (state=${existing.state})`);
130
+ }
131
+ return;
132
+ }
122
133
  log.info(`${this.logPrefix} Subscribing to additional channel: ${channel}`);
123
- const sub = this.client.newSubscription(channel, { token: subscriptionToken });
134
+ let sub;
135
+ try {
136
+ sub = this.client.newSubscription(channel, { token: subscriptionToken });
137
+ }
138
+ catch (error) {
139
+ const msg = error instanceof Error ? error.message : String(error);
140
+ log.error(`${this.logPrefix} Failed to create extra subscription (${channel}): ${msg}`);
141
+ this.callbacks.onError?.(error instanceof Error ? error : new Error(msg));
142
+ return;
143
+ }
124
144
  sub.on('publication', (ctx) => {
125
145
  this.handlePublication(ctx.data);
126
146
  });
@@ -130,7 +150,12 @@ export class WorkBuddyCentrifugeClient {
130
150
  sub.on('subscribed', () => {
131
151
  log.info(`${this.logPrefix} Extra channel subscribed: ${channel}`);
132
152
  });
133
- this.extraSubs.push(sub);
153
+ sub.on('unsubscribed', () => {
154
+ if (this.extraSubs.get(channel) === sub) {
155
+ this.extraSubs.delete(channel);
156
+ }
157
+ });
158
+ this.extraSubs.set(channel, sub);
134
159
  sub.subscribe();
135
160
  }
136
161
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wu529778790/open-im",
3
- "version": "1.10.8",
3
+ "version": "1.10.9-beta.1",
4
4
  "description": "Multi-platform IM bridge for AI CLI tools (Claude, Codex, CodeBuddy)",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -52,6 +52,7 @@
52
52
  },
53
53
  "dependencies": {
54
54
  "@anthropic-ai/claude-agent-sdk": "^0.2.92",
55
+ "@aws-sdk/client-s3": "^3.1035.0",
55
56
  "@larksuiteoapi/node-sdk": "^1.59.0",
56
57
  "centrifuge": "^5.5.3",
57
58
  "dingtalk-stream": "^2.1.4",