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.
- package/dist/adapters/backend/pty-backend.d.ts.map +1 -1
- package/dist/adapters/backend/pty-backend.js +4 -0
- package/dist/adapters/backend/pty-backend.js.map +1 -1
- package/dist/adapters/backend/tmux-backend.d.ts +19 -1
- package/dist/adapters/backend/tmux-backend.d.ts.map +1 -1
- package/dist/adapters/backend/tmux-backend.js +104 -58
- package/dist/adapters/backend/tmux-backend.js.map +1 -1
- package/dist/adapters/backend/tmux-pipe-backend.d.ts.map +1 -1
- package/dist/adapters/backend/tmux-pipe-backend.js +23 -5
- package/dist/adapters/backend/tmux-pipe-backend.js.map +1 -1
- package/dist/bot-registry.d.ts +2 -0
- package/dist/bot-registry.d.ts.map +1 -1
- package/dist/bot-registry.js +70 -10
- package/dist/bot-registry.js.map +1 -1
- package/dist/cli.js +31 -13
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +10 -8
- package/dist/config.js.map +1 -1
- package/dist/core/dashboard-events.d.ts.map +1 -1
- package/dist/core/dashboard-events.js +2 -2
- package/dist/core/dashboard-events.js.map +1 -1
- package/dist/core/dashboard-ipc-server.d.ts.map +1 -1
- package/dist/core/dashboard-ipc-server.js +6 -1
- package/dist/core/dashboard-ipc-server.js.map +1 -1
- package/dist/core/session-discovery.d.ts.map +1 -1
- package/dist/core/session-discovery.js +4 -3
- package/dist/core/session-discovery.js.map +1 -1
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +7 -2
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/worker-pool.d.ts.map +1 -1
- package/dist/core/worker-pool.js +10 -4
- package/dist/core/worker-pool.js.map +1 -1
- package/dist/daemon.d.ts.map +1 -1
- package/dist/daemon.js +18 -10
- package/dist/daemon.js.map +1 -1
- package/dist/dashboard.js +24 -4
- package/dist/dashboard.js.map +1 -1
- package/dist/im/lark/card-handler.d.ts.map +1 -1
- package/dist/im/lark/card-handler.js +4 -0
- package/dist/im/lark/card-handler.js.map +1 -1
- package/dist/im/lark/event-dispatcher.d.ts.map +1 -1
- package/dist/im/lark/event-dispatcher.js +20 -6
- package/dist/im/lark/event-dispatcher.js.map +1 -1
- package/dist/im/lark/message-parser.d.ts.map +1 -1
- package/dist/im/lark/message-parser.js +7 -3
- package/dist/im/lark/message-parser.js.map +1 -1
- package/dist/services/chat-first-seen-store.d.ts +12 -0
- package/dist/services/chat-first-seen-store.d.ts.map +1 -0
- package/dist/services/chat-first-seen-store.js +108 -0
- package/dist/services/chat-first-seen-store.js.map +1 -0
- package/dist/setup/ensure-tmux.d.ts +49 -0
- package/dist/setup/ensure-tmux.d.ts.map +1 -1
- package/dist/setup/ensure-tmux.js +116 -31
- package/dist/setup/ensure-tmux.js.map +1 -1
- package/dist/setup/index.d.ts.map +1 -1
- package/dist/setup/index.js +14 -3
- package/dist/setup/index.js.map +1 -1
- package/dist/utils/bot-routing.d.ts +6 -0
- package/dist/utils/bot-routing.d.ts.map +1 -0
- package/dist/utils/bot-routing.js +50 -0
- package/dist/utils/bot-routing.js.map +1 -0
- package/dist/utils/logger.d.ts +10 -0
- package/dist/utils/logger.d.ts.map +1 -1
- package/dist/utils/logger.js +60 -7
- package/dist/utils/logger.js.map +1 -1
- package/dist/worker.js +12 -1
- package/dist/worker.js.map +1 -1
- package/package.json +1 -1
package/dist/bot-registry.js
CHANGED
|
@@ -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
|
-
//
|
|
12
|
-
//
|
|
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
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
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:
|
|
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
|
package/dist/bot-registry.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bot-registry.js","sourceRoot":"","sources":["../src/bot-registry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,IAAI,MAAM,yBAAyB,CAAC;AAChD,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;
|
|
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()
|
|
516
|
-
*
|
|
517
|
-
*
|
|
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
|
-
|
|
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,
|
|
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
|
|
1853
|
-
//
|
|
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
|
-
?
|
|
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
|
|
2138
|
+
const entry = pickBotEntryByName(botEntries, botName, targetChatId, oncallChatsByApp);
|
|
2121
2139
|
if (entry)
|
|
2122
2140
|
openIdToAppId.set(crossOpenId, entry.larkAppId);
|
|
2123
2141
|
}
|