botmux 2.102.0 → 2.103.0
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/cli.d.ts +9 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +141 -6
- package/dist/cli.js.map +1 -1
- package/dist/dashboard/bot-onboarding.d.ts +28 -0
- package/dist/dashboard/bot-onboarding.d.ts.map +1 -1
- package/dist/dashboard/bot-onboarding.js +20 -0
- package/dist/dashboard/bot-onboarding.js.map +1 -1
- package/dist/dashboard/feishu-login.d.ts +64 -0
- package/dist/dashboard/feishu-login.d.ts.map +1 -0
- package/dist/dashboard/feishu-login.js +99 -0
- package/dist/dashboard/feishu-login.js.map +1 -0
- package/dist/dashboard/web/bot-defaults.d.ts.map +1 -1
- package/dist/dashboard/web/bot-defaults.js +108 -0
- package/dist/dashboard/web/bot-defaults.js.map +1 -1
- package/dist/dashboard/web/bot-onboarding.d.ts.map +1 -1
- package/dist/dashboard/web/bot-onboarding.js +4 -1
- package/dist/dashboard/web/bot-onboarding.js.map +1 -1
- package/dist/dashboard/web/i18n.d.ts.map +1 -1
- package/dist/dashboard/web/i18n.js +22 -2
- package/dist/dashboard/web/i18n.js.map +1 -1
- package/dist/dashboard-web/app.js +6 -6
- package/dist/dashboard-web/chunks/bot-defaults-page-ZVBKVYIG.js +380 -0
- package/dist/dashboard-web/chunks/{chunk-DWFMPIAQ.js → chunk-HC7ANVX7.js} +5 -5
- package/dist/dashboard-web/chunks/{chunk-NC77G2OB.js → chunk-NNXPEJ7S.js} +1 -1
- package/dist/dashboard-web/chunks/{connectors-BW6WO7P5.js → connectors-POZADN75.js} +1 -1
- package/dist/dashboard-web/chunks/{groups-page-UTXUVV2M.js → groups-page-UQFQ6DRE.js} +1 -1
- package/dist/dashboard-web/chunks/{insights-page-IW3KNMG4.js → insights-page-ATW2W5KP.js} +1 -1
- package/dist/dashboard-web/chunks/{overview-page-RO6R26AZ.js → overview-page-Z7OV54XU.js} +1 -1
- package/dist/dashboard-web/chunks/{roles-page-5DYKR2I5.js → roles-page-4BJVXK2E.js} +1 -1
- package/dist/dashboard-web/chunks/{schedules-WUZP3UPF.js → schedules-XLETZ2EP.js} +1 -1
- package/dist/dashboard-web/chunks/{sessions-page-GWZ4GKSG.js → sessions-page-RMMQUGXJ.js} +1 -1
- package/dist/dashboard-web/chunks/{settings-page-HJH6HQ7D.js → settings-page-PWKI4TWF.js} +1 -1
- package/dist/dashboard-web/chunks/{skills-page-ATVGKR5I.js → skills-page-SH7KUD45.js} +1 -1
- package/dist/dashboard-web/chunks/{team-federation-page-JVC73TTF.js → team-federation-page-JAJXFLXA.js} +1 -1
- package/dist/dashboard-web/chunks/{workflows-YCZTOJUS.js → workflows-FRJ2DJAO.js} +1 -1
- package/dist/dashboard-web/style.css +57 -0
- package/dist/dashboard.js +78 -2
- package/dist/dashboard.js.map +1 -1
- package/dist/utils/bot-routing.d.ts +26 -3
- package/dist/utils/bot-routing.d.ts.map +1 -1
- package/dist/utils/bot-routing.js +38 -4
- package/dist/utils/bot-routing.js.map +1 -1
- package/package.json +1 -1
- package/dist/dashboard-web/chunks/bot-defaults-page-YAHNXFBC.js +0 -370
package/dist/cli.d.ts
CHANGED
|
@@ -1,4 +1,13 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
export type StartBotLiveResult = {
|
|
3
|
+
ok: true;
|
|
4
|
+
state: 'started' | 'already-online';
|
|
5
|
+
processName: string;
|
|
6
|
+
} | {
|
|
7
|
+
ok: false;
|
|
8
|
+
reason: 'not_found' | 'fleet_down' | 'pm2_error';
|
|
9
|
+
message: string;
|
|
10
|
+
};
|
|
2
11
|
/**
|
|
3
12
|
* runHook: hook 命令的纯业务逻辑,接受已解析的 payload/env/postAskFn,
|
|
4
13
|
* 返回应写到 stdout 的字符串。通过依赖注入使单元测试无需真实 daemon/env。
|
package/dist/cli.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAg7DA,MAAM,MAAM,kBAAkB,GAC1B;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,KAAK,EAAE,SAAS,GAAG,gBAAgB,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,GACtE;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,WAAW,GAAG,YAAY,GAAG,WAAW,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;AAgnHrF;;;;;;;;;;;GAWG;AACH,wBAAsB,OAAO,CAC3B,OAAO,EAAE,OAAO,EAChB,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,EACvC,SAAS,EAAE,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,OAAO,CAAC,OAAO,qBAAqB,EAAE,SAAS,CAAC,EAC9F,KAAK,EAAE,MAAM,EACb,mBAAmB,CAAC,EAAE,MAAM,OAAO,CAAC,OAAO,2BAA2B,EAAE,UAAU,GAAG,IAAI,CAAC,GACzF,OAAO,CAAC;IAAE,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CA8F7B"}
|
package/dist/cli.js
CHANGED
|
@@ -48,7 +48,7 @@ import { callDashboard } from './cli/dashboard-endpoint.js';
|
|
|
48
48
|
import { loadDashboardSecret } from './dashboard/auth.js';
|
|
49
49
|
import { rejectLikelyWindowsStdinMojibake, decodeStdinBytes } from './cli/stdin-encoding.js';
|
|
50
50
|
import { formatBotInfoEntriesForCli, formatChatBotsForCli, } from './cli/bots-list-output.js';
|
|
51
|
-
import { buildFooterAddressing, hasKnownBotMention, knownBotOpenIdsFromCrossRef, orderedFooterRecipients, } from './utils/bot-routing.js';
|
|
51
|
+
import { buildFooterAddressing, hasKnownBotMention, knownBotOpenIdsFromCrossRef, orderedFooterRecipients, stripCodeSpans, } from './utils/bot-routing.js';
|
|
52
52
|
import { isLocale, localeForBot, setDefaultLocale, SUPPORTED_LOCALES, t } from './i18n/index.js';
|
|
53
53
|
import { chatAppLink, larkHosts, normalizeBrand, sdkDomain } from './im/lark/lark-hosts.js';
|
|
54
54
|
import { mergeGlobalConfig, readGlobalConfig, setGlobalLocale, globalConfigPath } from './global-config.js';
|
|
@@ -1182,6 +1182,9 @@ async function cmdSetupScripted(argv) {
|
|
|
1182
1182
|
await finishOpenPlatformSetup(bot.larkAppId, botBrand(bot));
|
|
1183
1183
|
}
|
|
1184
1184
|
const index = existing.length;
|
|
1185
|
+
// daemon 在跑就直接把新 bot 那一个进程拉起来,免整组 botmux restart。
|
|
1186
|
+
const live = ensureBotDaemonStarted(bot.larkAppId, { quiet: cmd.json });
|
|
1187
|
+
const next = live.ok ? 'live' : (live.reason === 'fleet_down' ? 'botmux start' : 'botmux restart');
|
|
1185
1188
|
if (cmd.json) {
|
|
1186
1189
|
console.log(JSON.stringify({
|
|
1187
1190
|
ok: true,
|
|
@@ -1190,7 +1193,8 @@ async function cmdSetupScripted(argv) {
|
|
|
1190
1193
|
botsFile: BOTS_JSON_FILE,
|
|
1191
1194
|
envMigrated: migratedEnv || undefined,
|
|
1192
1195
|
openPlatform: cmd.openPlatformAuto ? 'attempted' : 'skipped',
|
|
1193
|
-
|
|
1196
|
+
live,
|
|
1197
|
+
next,
|
|
1194
1198
|
}, null, 2));
|
|
1195
1199
|
}
|
|
1196
1200
|
else {
|
|
@@ -1201,7 +1205,15 @@ async function cmdSetupScripted(argv) {
|
|
|
1201
1205
|
if (!cmd.openPlatformAuto) {
|
|
1202
1206
|
console.log(' 已跳过开放平台自动配置(权限导入/发版)。需要时加 --open-platform-auto(要扫码),或运行交互式 botmux setup。');
|
|
1203
1207
|
}
|
|
1204
|
-
|
|
1208
|
+
if (live.ok) {
|
|
1209
|
+
console.log(`✅ 已自动上线(${live.processName}),无需重启其它机器人。`);
|
|
1210
|
+
}
|
|
1211
|
+
else if (live.reason === 'fleet_down') {
|
|
1212
|
+
console.log('下一步: botmux start(daemon 尚未运行)');
|
|
1213
|
+
}
|
|
1214
|
+
else {
|
|
1215
|
+
console.log(`⚠️ 自动上线失败(${live.message})。下一步: botmux restart`);
|
|
1216
|
+
}
|
|
1205
1217
|
}
|
|
1206
1218
|
return;
|
|
1207
1219
|
}
|
|
@@ -1459,7 +1471,7 @@ async function cmdSetup() {
|
|
|
1459
1471
|
console.log(`\n✅ 已添加机器人 ${newBot.larkAppId},共 ${bots.length + 1} 个`);
|
|
1460
1472
|
console.log(` 配置文件: ${BOTS_JSON_FILE}`);
|
|
1461
1473
|
await finishOpenPlatformSetup(newBot.larkAppId, botBrand(newBot));
|
|
1462
|
-
|
|
1474
|
+
printAddBotLiveHint(newBot.larkAppId);
|
|
1463
1475
|
return;
|
|
1464
1476
|
}
|
|
1465
1477
|
}
|
|
@@ -1513,7 +1525,7 @@ async function cmdSetup() {
|
|
|
1513
1525
|
console.log(` 配置文件: ${BOTS_JSON_FILE}`);
|
|
1514
1526
|
console.log(` 旧配置已备份: ${ENV_FILE}.bak`);
|
|
1515
1527
|
await finishOpenPlatformSetup(newBot.larkAppId, botBrand(newBot));
|
|
1516
|
-
|
|
1528
|
+
printAddBotLiveHint(newBot.larkAppId);
|
|
1517
1529
|
}
|
|
1518
1530
|
else {
|
|
1519
1531
|
// --- Fresh install ---
|
|
@@ -1874,6 +1886,120 @@ async function cmdRestart() {
|
|
|
1874
1886
|
}
|
|
1875
1887
|
await printDashboardHintWithRetry();
|
|
1876
1888
|
}
|
|
1889
|
+
/**
|
|
1890
|
+
* pm2 process list filtered to botmux entries (bot daemons + dashboard). Returns
|
|
1891
|
+
* `[]` when pm2 isn't running or has no botmux apps at all.
|
|
1892
|
+
*/
|
|
1893
|
+
function listBotmuxPm2Apps() {
|
|
1894
|
+
try {
|
|
1895
|
+
const apps = JSON.parse(pm2Capture(['jlist']));
|
|
1896
|
+
return (Array.isArray(apps) ? apps : [])
|
|
1897
|
+
.filter(a => a && (a.name === PM2_NAME || String(a.name).startsWith(`${PM2_NAME}-`)))
|
|
1898
|
+
.map(a => ({ name: String(a.name), online: a?.pm2_env?.status === 'online' }));
|
|
1899
|
+
}
|
|
1900
|
+
catch {
|
|
1901
|
+
return [];
|
|
1902
|
+
}
|
|
1903
|
+
}
|
|
1904
|
+
/**
|
|
1905
|
+
* Bring a SINGLE bot's daemon online without touching any other bot's process.
|
|
1906
|
+
* The key to "add a bot without `botmux restart`": a new bot is always APPENDED
|
|
1907
|
+
* to bots.json (stable index), so the existing daemons (indices 0..N-1) keep
|
|
1908
|
+
* running unchanged — we only need to spawn the new bot's own process.
|
|
1909
|
+
*
|
|
1910
|
+
* We regenerate ecosystem.config.json (which now includes the new app at index
|
|
1911
|
+
* N) and run `pm2 start --only <processName>`, which starts exactly that one app
|
|
1912
|
+
* and leaves every already-online daemon untouched (unlike `botmux restart`,
|
|
1913
|
+
* which tears down the whole fleet). The new daemon runs its slice of
|
|
1914
|
+
* startDaemon() — registerBot + WSClient long-connection + descriptor publish —
|
|
1915
|
+
* so it starts receiving Feishu messages and the dashboard auto-discovers it via
|
|
1916
|
+
* its freshly-written descriptor.
|
|
1917
|
+
*
|
|
1918
|
+
* Idempotent: a no-op when the target is already online. When the whole fleet is
|
|
1919
|
+
* down (no botmux pm2 apps — the dashboard itself isn't running either), we do
|
|
1920
|
+
* NOT start a lone bot; that case belongs to `botmux start`, which brings up the
|
|
1921
|
+
* entire ecosystem (all bots + dashboard).
|
|
1922
|
+
*/
|
|
1923
|
+
function ensureBotDaemonStarted(appId, opts = {}) {
|
|
1924
|
+
const bots = loadBotsJson();
|
|
1925
|
+
const index = bots.findIndex(b => b?.larkAppId === appId);
|
|
1926
|
+
if (index < 0) {
|
|
1927
|
+
return { ok: false, reason: 'not_found', message: `appId ${appId} 不在 bots.json 中` };
|
|
1928
|
+
}
|
|
1929
|
+
const processName = botProcessName(bots[index], index, PM2_NAME);
|
|
1930
|
+
const running = listBotmuxPm2Apps();
|
|
1931
|
+
if (running.length === 0) {
|
|
1932
|
+
return { ok: false, reason: 'fleet_down', message: 'daemon 未在运行,请先 botmux start' };
|
|
1933
|
+
}
|
|
1934
|
+
if (running.some(a => a.name === processName && a.online)) {
|
|
1935
|
+
return { ok: true, state: 'already-online', processName };
|
|
1936
|
+
}
|
|
1937
|
+
const cfg = ecosystemConfig();
|
|
1938
|
+
try {
|
|
1939
|
+
// `--only <name>` filters the ecosystem to just this app, so pm2 starts only
|
|
1940
|
+
// the new bot's daemon and never restarts the already-online ones.
|
|
1941
|
+
runPm2(['start', cfg, '--only', processName], !opts.quiet);
|
|
1942
|
+
}
|
|
1943
|
+
catch (e) {
|
|
1944
|
+
return { ok: false, reason: 'pm2_error', message: e instanceof Error ? e.message : String(e) };
|
|
1945
|
+
}
|
|
1946
|
+
return { ok: true, state: 'started', processName };
|
|
1947
|
+
}
|
|
1948
|
+
/**
|
|
1949
|
+
* `botmux start-bot <larkAppId>` — bring one freshly-added bot online without a
|
|
1950
|
+
* fleet-wide restart. Invoked by `botmux setup add` (inline) and by the
|
|
1951
|
+
* dashboard onboarding flow (spawned as a subprocess). `--json` for scripted
|
|
1952
|
+
* callers.
|
|
1953
|
+
*/
|
|
1954
|
+
async function cmdStartBot(argv) {
|
|
1955
|
+
const wantsJson = argv.includes('--json');
|
|
1956
|
+
const appId = argv.find(a => !a.startsWith('-'));
|
|
1957
|
+
if (!appId) {
|
|
1958
|
+
const msg = '用法: botmux start-bot <larkAppId> —— 拉起单个新机器人的 daemon(不重启其它 bot)';
|
|
1959
|
+
if (wantsJson)
|
|
1960
|
+
console.log(JSON.stringify({ ok: false, reason: 'missing_app_id', message: msg }));
|
|
1961
|
+
else
|
|
1962
|
+
console.error(`❌ ${msg}`);
|
|
1963
|
+
process.exit(1);
|
|
1964
|
+
}
|
|
1965
|
+
ensureConfigDir();
|
|
1966
|
+
const r = ensureBotDaemonStarted(appId, { quiet: wantsJson });
|
|
1967
|
+
if (wantsJson) {
|
|
1968
|
+
console.log(JSON.stringify(r, null, 2));
|
|
1969
|
+
if (!r.ok)
|
|
1970
|
+
process.exitCode = 1;
|
|
1971
|
+
return;
|
|
1972
|
+
}
|
|
1973
|
+
if (r.ok) {
|
|
1974
|
+
if (r.state === 'already-online')
|
|
1975
|
+
console.log(`✅ ${r.processName} 已在运行,无需操作`);
|
|
1976
|
+
else
|
|
1977
|
+
console.log(`✅ 已拉起 ${r.processName}(未重启其它机器人)`);
|
|
1978
|
+
return;
|
|
1979
|
+
}
|
|
1980
|
+
if (r.reason === 'fleet_down') {
|
|
1981
|
+
console.error('ℹ️ daemon 未在运行。请用 `botmux start` 启动整个进程组。');
|
|
1982
|
+
}
|
|
1983
|
+
else {
|
|
1984
|
+
console.error(`❌ 拉起失败 (${r.reason}): ${r.message}`);
|
|
1985
|
+
}
|
|
1986
|
+
process.exit(1);
|
|
1987
|
+
}
|
|
1988
|
+
/** Print the post-add "next step" line for interactive setup: auto-start the new
|
|
1989
|
+
* bot's own daemon when the fleet is up (no fleet-wide restart), else fall back
|
|
1990
|
+
* to the botmux start / restart hint. */
|
|
1991
|
+
function printAddBotLiveHint(appId) {
|
|
1992
|
+
const live = ensureBotDaemonStarted(appId);
|
|
1993
|
+
if (live.ok) {
|
|
1994
|
+
console.log(`✅ 已自动上线(${live.processName}),无需重启其它机器人。\n`);
|
|
1995
|
+
}
|
|
1996
|
+
else if (live.reason === 'fleet_down') {
|
|
1997
|
+
console.log('下一步: botmux start(daemon 尚未运行)\n');
|
|
1998
|
+
}
|
|
1999
|
+
else {
|
|
2000
|
+
console.log(`⚠️ 自动上线失败(${live.message})。下一步: botmux restart\n`);
|
|
2001
|
+
}
|
|
2002
|
+
}
|
|
1877
2003
|
/** Wraps `ensureDependencies()`. Fonts are nice-to-have (warn only). tmux is
|
|
1878
2004
|
* required since PTY 退役: if it's GENUINELY ABSENT and a bot wants the tmux
|
|
1879
2005
|
* backend (and the operator hasn't opted into BACKEND_TYPE=pty), we hard-fail
|
|
@@ -4501,6 +4627,12 @@ async function cmdSend(rest) {
|
|
|
4501
4627
|
// bot(正是要避免的循环 @)。botEntries/crossRef 仍需加载供 footer 寻址用。
|
|
4502
4628
|
if (!noMention) {
|
|
4503
4629
|
const alreadyMentioned = new Set(mentions.map(m => m.open_id));
|
|
4630
|
+
// Scan a code-span-stripped copy so a bot name quoted inside backticks or a
|
|
4631
|
+
// fenced block (e.g. an example `botmux send --mention @Bot …` or an
|
|
4632
|
+
// explanatory `@Bot`) is not auto-injected as a real handoff — that spurious
|
|
4633
|
+
// <at> would wake a bot the model never meant to @. Explicit --mention still
|
|
4634
|
+
// works (it doesn't go through this prose scan).
|
|
4635
|
+
const textForBotScan = stripCodeSpans(text);
|
|
4504
4636
|
// Sort by name length desc so longer names ("Claude分身") win over their
|
|
4505
4637
|
// prefix ("Claude") when both could match — break-on-first-hit otherwise
|
|
4506
4638
|
// routes "@Claude分身" to Claude.
|
|
@@ -4543,7 +4675,7 @@ async function cmdSend(rest) {
|
|
|
4543
4675
|
// `@Claude2` doesn't match name "Claude" and `@Claude分身好的` doesn't
|
|
4544
4676
|
// either-half-match.
|
|
4545
4677
|
const re = new RegExp(`(?<![A-Za-z0-9_])@${escName}(?![\\p{L}\\p{N}_])`, 'iu');
|
|
4546
|
-
if (!re.test(
|
|
4678
|
+
if (!re.test(textForBotScan))
|
|
4547
4679
|
continue;
|
|
4548
4680
|
// Lark open_id is per-app scoped. Use sender-scoped id from cross-ref
|
|
4549
4681
|
// only — falling back to entry.botOpenId would feed Lark a wrong-scope
|
|
@@ -6102,6 +6234,9 @@ switch (command) {
|
|
|
6102
6234
|
case 'start':
|
|
6103
6235
|
await cmdStart();
|
|
6104
6236
|
break;
|
|
6237
|
+
case 'start-bot':
|
|
6238
|
+
await cmdStartBot(process.argv.slice(3));
|
|
6239
|
+
break;
|
|
6105
6240
|
case 'stop':
|
|
6106
6241
|
cmdStop();
|
|
6107
6242
|
break;
|