botmux 2.21.4 → 2.21.6

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.
Files changed (70) hide show
  1. package/dist/adapters/backend/pty-backend.d.ts.map +1 -1
  2. package/dist/adapters/backend/pty-backend.js +4 -0
  3. package/dist/adapters/backend/pty-backend.js.map +1 -1
  4. package/dist/adapters/backend/tmux-backend.d.ts +19 -1
  5. package/dist/adapters/backend/tmux-backend.d.ts.map +1 -1
  6. package/dist/adapters/backend/tmux-backend.js +104 -58
  7. package/dist/adapters/backend/tmux-backend.js.map +1 -1
  8. package/dist/adapters/backend/tmux-pipe-backend.d.ts.map +1 -1
  9. package/dist/adapters/backend/tmux-pipe-backend.js +23 -5
  10. package/dist/adapters/backend/tmux-pipe-backend.js.map +1 -1
  11. package/dist/bot-registry.d.ts +2 -0
  12. package/dist/bot-registry.d.ts.map +1 -1
  13. package/dist/bot-registry.js +70 -10
  14. package/dist/bot-registry.js.map +1 -1
  15. package/dist/cli.js +31 -13
  16. package/dist/cli.js.map +1 -1
  17. package/dist/config.d.ts.map +1 -1
  18. package/dist/config.js +10 -8
  19. package/dist/config.js.map +1 -1
  20. package/dist/core/dashboard-events.d.ts.map +1 -1
  21. package/dist/core/dashboard-events.js +2 -2
  22. package/dist/core/dashboard-events.js.map +1 -1
  23. package/dist/core/dashboard-ipc-server.d.ts.map +1 -1
  24. package/dist/core/dashboard-ipc-server.js +6 -1
  25. package/dist/core/dashboard-ipc-server.js.map +1 -1
  26. package/dist/core/session-discovery.d.ts.map +1 -1
  27. package/dist/core/session-discovery.js +4 -3
  28. package/dist/core/session-discovery.js.map +1 -1
  29. package/dist/core/session-manager.d.ts.map +1 -1
  30. package/dist/core/session-manager.js +7 -2
  31. package/dist/core/session-manager.js.map +1 -1
  32. package/dist/core/worker-pool.d.ts.map +1 -1
  33. package/dist/core/worker-pool.js +10 -4
  34. package/dist/core/worker-pool.js.map +1 -1
  35. package/dist/daemon.d.ts.map +1 -1
  36. package/dist/daemon.js +18 -10
  37. package/dist/daemon.js.map +1 -1
  38. package/dist/dashboard.js +24 -4
  39. package/dist/dashboard.js.map +1 -1
  40. package/dist/im/lark/card-handler.d.ts.map +1 -1
  41. package/dist/im/lark/card-handler.js +4 -0
  42. package/dist/im/lark/card-handler.js.map +1 -1
  43. package/dist/im/lark/event-dispatcher.d.ts.map +1 -1
  44. package/dist/im/lark/event-dispatcher.js +20 -6
  45. package/dist/im/lark/event-dispatcher.js.map +1 -1
  46. package/dist/im/lark/message-parser.d.ts.map +1 -1
  47. package/dist/im/lark/message-parser.js +7 -3
  48. package/dist/im/lark/message-parser.js.map +1 -1
  49. package/dist/services/chat-first-seen-store.d.ts +12 -0
  50. package/dist/services/chat-first-seen-store.d.ts.map +1 -0
  51. package/dist/services/chat-first-seen-store.js +108 -0
  52. package/dist/services/chat-first-seen-store.js.map +1 -0
  53. package/dist/setup/ensure-tmux.d.ts +49 -0
  54. package/dist/setup/ensure-tmux.d.ts.map +1 -1
  55. package/dist/setup/ensure-tmux.js +116 -31
  56. package/dist/setup/ensure-tmux.js.map +1 -1
  57. package/dist/setup/index.d.ts.map +1 -1
  58. package/dist/setup/index.js +14 -3
  59. package/dist/setup/index.js.map +1 -1
  60. package/dist/utils/bot-routing.d.ts +6 -0
  61. package/dist/utils/bot-routing.d.ts.map +1 -0
  62. package/dist/utils/bot-routing.js +50 -0
  63. package/dist/utils/bot-routing.js.map +1 -0
  64. package/dist/utils/logger.d.ts +10 -0
  65. package/dist/utils/logger.d.ts.map +1 -1
  66. package/dist/utils/logger.js +60 -7
  67. package/dist/utils/logger.js.map +1 -1
  68. package/dist/worker.js +12 -1
  69. package/dist/worker.js.map +1 -1
  70. package/package.json +1 -1
@@ -1,15 +1,23 @@
1
1
  import * as Lark from '@larksuiteoapi/node-sdk';
2
- import { readFileSync, existsSync } from 'node:fs';
2
+ import { readFileSync, existsSync, statSync } from 'node:fs';
3
3
  import { resolve } from 'node:path';
4
4
  import { homedir } from 'node:os';
5
+ import { logger } from './utils/logger.js';
5
6
  const bots = new Map();
6
7
  /** Path of the bot config file we loaded (so `/oncall` can persist bindings back). */
7
8
  let loadedConfigPath;
8
9
  export function getLoadedConfigPath() {
9
10
  return loadedConfigPath;
10
11
  }
11
- // Provide a custom logger that writes to stderr the default Lark SDK
12
- // logger uses console.log, which would mix into stdout consumers.
12
+ // Route Lark SDK output through our logger so it inherits the same sink
13
+ // rules (info/debug → daemon.log in daemon mode, stderr in CLI mode,
14
+ // dropped when CLI is silent). The default SDK logger calls console.log,
15
+ // which would corrupt CLI stdout consumers.
16
+ //
17
+ // Volume control: the SDK is chatty at info/debug ("client ready", request
18
+ // traces, etc.); without DEBUG=1 those become no-ops in the CLI path and
19
+ // stay in daemon.log on the daemon path — pm2's error.log no longer sees
20
+ // "[lark:info] client ready" floods.
13
21
  function safeStringify(v) {
14
22
  if (typeof v === 'string')
15
23
  return v;
@@ -20,18 +28,19 @@ function safeStringify(v) {
20
28
  return String(v);
21
29
  }
22
30
  }
23
- const stderrLogger = {
24
- error: (...msg) => { process.stderr.write(`[lark:error] ${msg.map(safeStringify).join(' ')}\n`); },
25
- warn: (...msg) => { process.stderr.write(`[lark:warn] ${msg.map(safeStringify).join(' ')}\n`); },
26
- info: (...msg) => { process.stderr.write(`[lark:info] ${msg.map(safeStringify).join(' ')}\n`); },
27
- debug: (...msg) => { process.stderr.write(`[lark:debug] ${msg.map(safeStringify).join(' ')}\n`); },
28
- trace: (...msg) => { process.stderr.write(`[lark:trace] ${msg.map(safeStringify).join(' ')}\n`); },
31
+ const fmtLark = (msg) => msg.map(safeStringify).join(' ');
32
+ const larkLogger = {
33
+ error: (...msg) => logger.error(`[lark] ${fmtLark(msg)}`),
34
+ warn: (...msg) => logger.warn(`[lark] ${fmtLark(msg)}`),
35
+ info: (...msg) => logger.info(`[lark] ${fmtLark(msg)}`),
36
+ debug: (...msg) => logger.debug(`[lark] ${fmtLark(msg)}`),
37
+ trace: (..._msg) => { },
29
38
  };
30
39
  export function registerBot(cfg) {
31
40
  const client = new Lark.Client({
32
41
  appId: cfg.larkAppId,
33
42
  appSecret: cfg.larkAppSecret,
34
- logger: stderrLogger,
43
+ logger: larkLogger,
35
44
  });
36
45
  const state = {
37
46
  config: cfg,
@@ -59,6 +68,57 @@ export function findOncallChat(larkAppId, chatId) {
59
68
  const bot = bots.get(larkAppId);
60
69
  return bot?.config.oncallChats?.find(c => c.chatId === chatId);
61
70
  }
71
+ // Cross-bot oncall chat check — cached by config-file mtime.
72
+ //
73
+ // /oncall bind is per-bot, but oncall is meant to be a chat-level property:
74
+ // once any bot in a multi-bot deployment binds the chat, every sibling bot
75
+ // should treat that chat as an oncall workspace too (otherwise unbound bots
76
+ // fall back to allowedUsers and reply "⚠️ 无操作权限" when @-mentioned).
77
+ //
78
+ // Multi-daemon deployments run one bot per process, so the in-memory `bots`
79
+ // map only sees this daemon's own bot — sibling bots' bindings live only on
80
+ // disk in the shared bots.json. Re-read that file lazily, keyed by mtime,
81
+ // so the hot path is a single stat() once the cache is warm.
82
+ let oncallChatCache = null;
83
+ export function findOncallChatForAnyBot(chatId) {
84
+ // Fast path: this daemon's own bot(s). Covers single-daemon setups and any
85
+ // case where the receiving bot itself is bound.
86
+ for (const bot of bots.values()) {
87
+ const entry = bot.config.oncallChats?.find(c => c.chatId === chatId);
88
+ if (entry)
89
+ return entry;
90
+ }
91
+ // Slow path: scan the shared bots.json for sibling bots' bindings.
92
+ const path = loadedConfigPath;
93
+ if (!path)
94
+ return undefined;
95
+ try {
96
+ const stat = statSync(path);
97
+ if (!oncallChatCache || oncallChatCache.mtimeMs !== stat.mtimeMs) {
98
+ const raw = JSON.parse(readFileSync(path, 'utf-8'));
99
+ const chats = new Map();
100
+ if (Array.isArray(raw)) {
101
+ for (const entry of raw) {
102
+ if (!Array.isArray(entry?.oncallChats))
103
+ continue;
104
+ for (const c of entry.oncallChats) {
105
+ if (c && typeof c.chatId === 'string' && typeof c.workingDir === 'string') {
106
+ chats.set(c.chatId, { chatId: c.chatId, workingDir: c.workingDir });
107
+ }
108
+ }
109
+ }
110
+ }
111
+ oncallChatCache = { mtimeMs: stat.mtimeMs, chats };
112
+ }
113
+ return oncallChatCache.chats.get(chatId);
114
+ }
115
+ catch {
116
+ return undefined;
117
+ }
118
+ }
119
+ export function isChatOncallBoundForAnyBot(chatId) {
120
+ return !!findOncallChatForAnyBot(chatId);
121
+ }
62
122
  /**
63
123
  * Load bot configurations from one of (in priority order):
64
124
  * 1. BOTS_CONFIG env var — path to a JSON file
@@ -1 +1 @@
1
- {"version":3,"file":"bot-registry.js","sourceRoot":"","sources":["../src/bot-registry.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"bot-registry.js","sourceRoot":"","sources":["../src/bot-registry.ts"],"names":[],"mappings":"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"}
package/dist/cli.js CHANGED
@@ -25,6 +25,14 @@ import { createInterface } from 'node:readline';
25
25
  import { createRequire } from 'node:module';
26
26
  import { createHmac, randomBytes } from 'node:crypto';
27
27
  import { enableAutostart, disableAutostart, autostartStatus, refreshAutostart } from './autostart.js';
28
+ import { loadOncallChatsByApp, pickBotEntryByName } from './utils/bot-routing.js';
29
+ import { tmuxEnv } from './setup/ensure-tmux.js';
30
+ import { logger } from './utils/logger.js';
31
+ // CLI subcommands (send/thread/bots/list/etc) print JSON to stdout for
32
+ // callers to parse. Transitive logger.info calls from shared modules would
33
+ // corrupt that stream, so the CLI process runs silent by default. DEBUG=1
34
+ // re-enables logging end-to-end for CLI troubleshooting.
35
+ logger.setSilent(true);
28
36
  const __filename = fileURLToPath(import.meta.url);
29
37
  const __dirname = dirname(__filename);
30
38
  const require = createRequire(import.meta.url);
@@ -512,9 +520,9 @@ async function cmdRestart() {
512
520
  console.log(`autostart unit 已同步到当前 Node/cli.js 路径`);
513
521
  }
514
522
  }
515
- /** Wraps `ensureDependencies()` so the cmdStart/cmdRestart paths get a clean
516
- * exit on tmux failure. Fonts failures are non-fatal and surface as warnings
517
- * printed by ensureDependencies itself. */
523
+ /** Wraps `ensureDependencies()`. Neither tmux nor fonts are load-bearing
524
+ * ensureDependencies surfaces failures as warnings and the daemon continues
525
+ * on PTY backend. Only an unexpected exception (programmer error) propagates. */
518
526
  async function ensureSystemDependencies() {
519
527
  const { ensureDependencies } = await import('./setup/index.js');
520
528
  try {
@@ -522,8 +530,8 @@ async function ensureSystemDependencies() {
522
530
  }
523
531
  catch (err) {
524
532
  console.error('');
525
- console.error(err?.message ?? String(err));
526
- process.exit(1);
533
+ console.error(`依赖检测内部错误: ${err?.message ?? String(err)}`);
534
+ // Don't exit — let daemon start try anyway; worst case PTY backend works.
527
535
  }
528
536
  }
529
537
  /**
@@ -920,7 +928,7 @@ function printSessionTable(active) {
920
928
  /** Check if a tmux session exists. */
921
929
  function tmuxSessionExists(name) {
922
930
  try {
923
- execSync(`tmux has-session -t ${name} 2>/dev/null`, { stdio: 'ignore' });
931
+ execSync(`tmux has-session -t ${name} 2>/dev/null`, { stdio: 'ignore', env: tmuxEnv() });
924
932
  return true;
925
933
  }
926
934
  catch {
@@ -1064,7 +1072,7 @@ function interactiveSessionPicker(active) {
1064
1072
  // Kill tmux session
1065
1073
  if (r.hasTmux) {
1066
1074
  try {
1067
- execSync(`tmux kill-session -t '${r.tmuxName}' 2>/dev/null`, { stdio: 'ignore' });
1075
+ execSync(`tmux kill-session -t '${r.tmuxName}' 2>/dev/null`, { stdio: 'ignore', env: tmuxEnv() });
1068
1076
  }
1069
1077
  catch { /* */ }
1070
1078
  }
@@ -1135,6 +1143,7 @@ function interactiveSessionPicker(active) {
1135
1143
  cleanup();
1136
1144
  spawnSync('tmux', ['attach-session', '-t', selected.tmuxName], {
1137
1145
  stdio: 'inherit',
1146
+ env: tmuxEnv(),
1138
1147
  });
1139
1148
  resolve();
1140
1149
  return;
@@ -1232,7 +1241,7 @@ function cmdDelete() {
1232
1241
  // Kill associated tmux session if it exists
1233
1242
  const tmuxName = `bmx-${s.sessionId.substring(0, 8)}`;
1234
1243
  try {
1235
- execSync(`tmux kill-session -t '${tmuxName}' 2>/dev/null`, { stdio: 'ignore' });
1244
+ execSync(`tmux kill-session -t '${tmuxName}' 2>/dev/null`, { stdio: 'ignore', env: tmuxEnv() });
1236
1245
  console.log(` killed tmux ${tmuxName}`);
1237
1246
  }
1238
1247
  catch { /* no tmux session */ }
@@ -1835,7 +1844,7 @@ async function cmdSend(rest) {
1835
1844
  }
1836
1845
  }
1837
1846
  // Register bots so Lark client works
1838
- const { registerBot, loadBotConfigs, findOncallChat } = await import('./bot-registry.js');
1847
+ const { registerBot, loadBotConfigs, findOncallChatForAnyBot } = await import('./bot-registry.js');
1839
1848
  try {
1840
1849
  for (const cfg of loadBotConfigs())
1841
1850
  registerBot(cfg);
@@ -1849,10 +1858,13 @@ async function cmdSend(rest) {
1849
1858
  // reply_in_thread, otherwise Lark would force every reply into a fresh
1850
1859
  // topic — defeating the whole point of chat-scope routing.
1851
1860
  const isChatScope = s.scope === 'chat';
1852
- // Oncall addressing only meaningful for thread replies inside the session's
1853
- // own chat — skip when publishing top-level or to a different chat.
1861
+ // Oncall addressing only meaningful for replies inside the session's own
1862
+ // chat — skip when publishing top-level or to a different chat. Treat
1863
+ // oncall as chat-level: in multi-daemon setups this session's bot may not
1864
+ // be the one that persisted the binding, but users still expect footer
1865
+ // addressing to go to the last caller in the shared oncall workspace.
1854
1866
  const oncallEntry = !sendTopLevel && !overrideChatId && s.chatId
1855
- ? findOncallChat(appId, s.chatId) : undefined;
1867
+ ? findOncallChatForAnyBot(s.chatId) : undefined;
1856
1868
  // Dispatch helper: top-level / chat-scope send vs reply-in-thread, single decision point
1857
1869
  const dispatch = (content, msgType) => (sendTopLevel || isChatScope)
1858
1870
  ? sendMessage(appId, targetChatId, content, msgType)
@@ -2106,6 +2118,12 @@ async function cmdSend(rest) {
2106
2118
  botEntries = JSON.parse(readFileSync(botInfoPath, 'utf-8'));
2107
2119
  }
2108
2120
  catch { /* */ }
2121
+ // Disambiguate same-named bots by oncall binding to the outbound chat.
2122
+ // Without this, Array.find on botName silently routes to whichever
2123
+ // bots-info.json entry sorts first — typically the wrong one when a
2124
+ // deployment runs two apps under the same display name (e.g. two CoCo
2125
+ // bots, only one bound to this chat).
2126
+ const oncallChatsByApp = loadOncallChatsByApp();
2109
2127
  const openIdToAppId = new Map();
2110
2128
  for (const e of botEntries)
2111
2129
  if (e.botOpenId)
@@ -2117,7 +2135,7 @@ async function cmdSend(rest) {
2117
2135
  try {
2118
2136
  const crossRef = JSON.parse(readFileSync(join(dataDir, file), 'utf-8'));
2119
2137
  for (const [botName, crossOpenId] of Object.entries(crossRef)) {
2120
- const entry = botEntries.find(e => e.botName?.toLowerCase() === botName.toLowerCase());
2138
+ const entry = pickBotEntryByName(botEntries, botName, targetChatId, oncallChatsByApp);
2121
2139
  if (entry)
2122
2140
  openIdToAppId.set(crossOpenId, entry.larkAppId);
2123
2141
  }