@wu529778790/open-im 1.11.13-beta.0 → 1.11.13-beta.2

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.
@@ -345,7 +345,17 @@ export function runCodeBuddy(cliPath, prompt, sessionId, workDir, callbacks, opt
345
345
  if (completed)
346
346
  return;
347
347
  completed = true;
348
- callbacks.onError(`Failed to start CodeBuddy CLI: ${err.message}`);
348
+ const errorCode = err.code;
349
+ const cliPath = child.spawnfile ?? 'codebuddy';
350
+ const cliHint = errorCode === 'ENOENT'
351
+ ? `CodeBuddy CLI 路径不存在:${cliPath}。请检查 config.tools.codebuddy.cliPath 或重新 open-im init。`
352
+ : errorCode === 'EFTYPE'
353
+ ? `CodeBuddy CLI 不是有效的可执行文件:${cliPath}(macOS 可执行性/Shebang 检查失败)。请确认 cliPath 指向二进制或带 shebang 的脚本。`
354
+ : errorCode === 'EACCES'
355
+ ? `CodeBuddy CLI 没有执行权限:${cliPath}。请 chmod +x 或检查文件所有者。`
356
+ : null;
357
+ log.error(`CodeBuddy CLI spawn error: ${err.message}, code=${errorCode}, path=${cliPath}${cliHint ? ` — ${cliHint}` : ''}`);
358
+ callbacks.onError(cliHint ?? `无法启动 CodeBuddy:${err.message}`);
349
359
  });
350
360
  // 墙钟超时:防止 CLI 挂死永久占用用户队列槽
351
361
  const cliTimeoutMs = Number(process.env.OPEN_IM_CLI_TIMEOUT_MS) || 30 * 60 * 1000;
@@ -389,10 +389,21 @@ export function runCodex(cliPath, prompt, sessionId, workDir, callbacks, options
389
389
  });
390
390
  child.on('error', (err) => {
391
391
  const errorCode = err.code;
392
- log.error(`Codex CLI spawn error: ${err.message}, code=${errorCode}, path=${cliPath}`);
392
+ // EFTYPE / ENOENT / EACCES:cliPath 不可执行、不存在或没有权限。
393
+ // macOS 上缺少 shebang 的脚本会收到 EFTYPE;Windows 上通常是 ENOENT。
394
+ // 给出可操作的修复建议,避免用户只看到冷冰冰的 fork/exec 错误。
395
+ const cliHint = errorCode === 'ENOENT'
396
+ ? `Codex CLI 路径不存在:${cliPath}。请检查 config.tools.codex.cliPath 或重新 open-im init。`
397
+ : errorCode === 'EFTYPE'
398
+ ? `Codex CLI 不是有效的可执行文件:${cliPath}(macOS 可执行性/Shebang 检查失败)。请确认 cliPath 指向二进制或带 shebang 的脚本。`
399
+ : errorCode === 'EACCES'
400
+ ? `Codex CLI 没有执行权限:${cliPath}。请 chmod +x 或检查文件所有者。`
401
+ : null;
402
+ log.error(`Codex CLI spawn error: ${err.message}, code=${errorCode}, path=${cliPath}${cliHint ? ` — ${cliHint}` : ''}`);
393
403
  if (!completed) {
394
404
  completed = true;
395
- callbacks.onError(`Failed to start Codex CLI: ${err.message}`);
405
+ const friendlyMsg = cliHint ?? `无法启动 Codex:${err.message}`;
406
+ callbacks.onError(friendlyMsg);
396
407
  }
397
408
  gate.childClosed = true;
398
409
  finalize();
package/dist/index.js CHANGED
@@ -274,14 +274,30 @@ export async function main() {
274
274
  loadActiveChats();
275
275
  initAdapters(config);
276
276
  startKeepalive();
277
- // 尽早启动 shutdown 并写入 port 文件,使 open-im start 的 8s 就绪超时能通过(平台初始化可能较慢)
277
+ // 尽早启动 shutdown 并写入 port 文件,使 open-im start 的 8s 就绪超时能通过(平台初始化可能较慢)。
278
+ // HTTP handler 可能在 platform 初始化完成前被外部调用(如 open-im stop / 探测脚本),
279
+ // 此时 shutdown 尚未定义。用占位函数兜底:把请求排进队列,shutdown 就绪后执行。
280
+ const shutdownQueue = [];
281
+ let shutdownImpl = null;
282
+ const runShutdown = () => {
283
+ const fn = shutdownImpl;
284
+ if (!fn)
285
+ throw new Error('shutdown not ready');
286
+ return fn().catch(() => process.exit(1));
287
+ };
288
+ const requestShutdown = () => {
289
+ if (shutdownImpl)
290
+ runShutdown();
291
+ else
292
+ shutdownQueue.push(() => runShutdown());
293
+ };
278
294
  let shutdownServer = null;
279
295
  await new Promise((resolve, reject) => {
280
296
  shutdownServer = createServer((req, res) => {
281
297
  if (req.url === "/shutdown" || req.url === "/") {
282
298
  res.writeHead(200, { "Content-Type": "text/plain" });
283
299
  res.end("ok");
284
- shutdown().catch(() => process.exit(1));
300
+ requestShutdown();
285
301
  }
286
302
  else {
287
303
  res.writeHead(404);
@@ -354,6 +370,13 @@ export async function main() {
354
370
  return;
355
371
  shuttingDown = true;
356
372
  log.info("Shutting down...");
373
+ // shutdown 已就绪:放行 platform 初始化期间排队的 stop 请求
374
+ shutdownImpl = shutdown;
375
+ for (const pending of shutdownQueue.splice(0))
376
+ pending();
377
+ // 排队请求已全部触发并 await 完成 → 主动退出,避免进程空转
378
+ if (shuttingDown)
379
+ return;
357
380
  const uptimeSec = Math.floor((Date.now() - startedAt) / 1000);
358
381
  const m = Math.floor(uptimeSec / 60);
359
382
  const shutdownMsg = buildShutdownMessage(m);
package/dist/logger.js CHANGED
@@ -70,6 +70,11 @@ function checkLogSize() {
70
70
  return;
71
71
  try {
72
72
  const currentLogPath = join(logDir, getLogFileName());
73
+ // ENOENT 防御:上次轮转刚 unlink 当前文件、新流尚未建立时,
74
+ // write() 可能追入旧 stream 后立刻触发 checkLogSize()。此时文件不存在,
75
+ // 直接跳过,下一轮 write() 会自动建好新文件。
76
+ if (!existsSync(currentLogPath))
77
+ return;
73
78
  const stats = statSync(currentLogPath);
74
79
  if (stats.size > MAX_LOG_SIZE) {
75
80
  // 关闭当前日志流
@@ -180,7 +180,7 @@ function buildCompletionNote(result, sessionManager, ctx) {
180
180
  parts.push(ctxWarning);
181
181
  return parts.join(' | ');
182
182
  }
183
- function buildRunOptions(config, sessionManager, ctx, aiCommand, toolAdapter) {
183
+ function buildRunOptions(config, sessionManager, ctx, aiCommand) {
184
184
  // 权限 hook 尚未接入 IM,暂时全局跳过
185
185
  const defaultSkipPermissions = true;
186
186
  return {
@@ -474,7 +474,7 @@ export function runAITask(deps, ctx, prompt, toolAdapter, platformAdapter) {
474
474
  cleanup();
475
475
  resolve();
476
476
  },
477
- }, buildRunOptions(config, sessionManager, ctx, aiCommand, toolAdapter));
477
+ }, buildRunOptions(config, sessionManager, ctx, aiCommand));
478
478
  return activeHandle;
479
479
  };
480
480
  taskState = {
@@ -51,7 +51,7 @@ async function ping(target, workDir) {
51
51
  try {
52
52
  handle.abort();
53
53
  }
54
- catch (err) {
54
+ catch {
55
55
  // 中止失败,忽略错误
56
56
  }
57
57
  settle(false, `timeout after ${PING_TIMEOUT_MS}ms`);
@@ -3,15 +3,11 @@ import { createLogger } from "../logger.js";
3
3
  import { isFatalReconnectError, jitteredDelay } from "../shared/reconnect.js";
4
4
  const log = createLogger("Telegram");
5
5
  let bot;
6
- let botUsername;
7
6
  export function getBot() {
8
7
  if (!bot)
9
8
  throw new Error("Telegram bot not initialized");
10
9
  return bot;
11
10
  }
12
- function getBotUsername() {
13
- return botUsername;
14
- }
15
11
  export async function initTelegram(config, setupHandlers) {
16
12
  const token = config.telegramBotToken ?? "";
17
13
  if (!token) {
@@ -19,8 +15,7 @@ export async function initTelegram(config, setupHandlers) {
19
15
  }
20
16
  bot = new Telegraf(token);
21
17
  setupHandlers(bot);
22
- const me = (await bot.telegram.getMe());
23
- botUsername = me.username;
18
+ await bot.telegram.getMe();
24
19
  const launchWithRetry = async (attempt = 1) => {
25
20
  try {
26
21
  await bot.launch();
@@ -40,9 +40,6 @@ const reconnectManager = new ReconnectManager({
40
40
  }
41
41
  },
42
42
  });
43
- function getChannelState() {
44
- return stateManager.current;
45
- }
46
43
  export async function initWorkBuddy(config, eventHandler, onStateChange) {
47
44
  const pc = config.platforms?.workbuddy;
48
45
  if (!pc?.enabled) {
@@ -96,7 +93,7 @@ async function connect() {
96
93
  log.error('Host workspace registration failed:', err);
97
94
  if (isFatalReconnectError(err)) {
98
95
  reconnectManager.setFatal(true);
99
- log.warn('WorkBuddy 致命错误(鉴权失败),转慢探测(token 可能已过期):', err);
96
+ log.warn('WorkBuddy 致命错误(鉴权失败),转慢探测(token 可能已过期)。请运行 open-im init 重新登录 WorkBuddy。:', err);
100
97
  }
101
98
  reconnectManager.schedule();
102
99
  return;
@@ -242,9 +239,6 @@ async function connect() {
242
239
  export function getCentrifugeClient() {
243
240
  return centrifugeClient;
244
241
  }
245
- function getOAuth() {
246
- return oauthClient;
247
- }
248
242
  /**
249
243
  * 单飞刷新 WorkBuddy access token:并发 401 只触发一次刷新,复用同一 Promise。
250
244
  * 刷新成功后 oauthClient.accessToken 原地更新,所有 HTTP 调用(含心跳)自动用新 token。
@@ -41,14 +41,3 @@ export async function sendErrorReply(_client, chatId, error, msgId) {
41
41
  stop_reason: 'error',
42
42
  });
43
43
  }
44
- /**
45
- * Send streaming chunk to WeChat KF
46
- */
47
- function sendStreamingChunk(_client, chatId, text, msgId) {
48
- const client = _client ?? getCentrifugeClient();
49
- if (!client) {
50
- log.warn('WorkBuddy client not available, cannot send chunk');
51
- return;
52
- }
53
- client.sendMessageChunk(chatId, msgId, { type: 'text', text: toReplyPlainText(text) });
54
- }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wu529778790/open-im",
3
- "version": "1.11.13-beta.0",
3
+ "version": "1.11.13-beta.2",
4
4
  "description": "Your AI coding assistant, in every chat app. Multi-platform IM bridge for Claude Code, Codex, and CodeBuddy.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",