botmux 2.92.0 → 2.93.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.
- package/README.en.md +1 -1
- package/README.md +1 -1
- package/dist/bot-registry.d.ts.map +1 -1
- package/dist/bot-registry.js +5 -4
- package/dist/bot-registry.js.map +1 -1
- package/dist/cli/dashboard-endpoint.d.ts +1 -1
- package/dist/cli/dashboard-endpoint.d.ts.map +1 -1
- package/dist/cli/dashboard-endpoint.js +3 -0
- package/dist/cli/dashboard-endpoint.js.map +1 -1
- package/dist/core/session-manager.js +2 -2
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/trigger-session.d.ts +10 -0
- package/dist/core/trigger-session.d.ts.map +1 -1
- package/dist/core/trigger-session.js +22 -2
- package/dist/core/trigger-session.js.map +1 -1
- package/dist/core/worker-pool.d.ts +6 -0
- package/dist/core/worker-pool.d.ts.map +1 -1
- package/dist/core/worker-pool.js +20 -6
- package/dist/core/worker-pool.js.map +1 -1
- package/dist/daemon.d.ts +2 -1
- package/dist/daemon.d.ts.map +1 -1
- package/dist/daemon.js +20 -5
- package/dist/daemon.js.map +1 -1
- package/dist/dashboard/federation-spoke-api.d.ts +31 -0
- package/dist/dashboard/federation-spoke-api.d.ts.map +1 -1
- package/dist/dashboard/federation-spoke-api.js +36 -14
- package/dist/dashboard/federation-spoke-api.js.map +1 -1
- package/dist/dashboard/web/i18n.js +2 -2
- package/dist/dashboard/web/i18n.js.map +1 -1
- package/dist/dashboard-web/app.js +2 -2
- package/dist/dashboard.js +55 -51
- package/dist/dashboard.js.map +1 -1
- package/dist/i18n/en.d.ts.map +1 -1
- package/dist/i18n/en.js +9 -6
- package/dist/i18n/en.js.map +1 -1
- package/dist/i18n/zh.d.ts.map +1 -1
- package/dist/i18n/zh.js +9 -6
- package/dist/i18n/zh.js.map +1 -1
- package/dist/im/lark/card-handler.d.ts.map +1 -1
- package/dist/im/lark/card-handler.js +24 -2
- package/dist/im/lark/card-handler.js.map +1 -1
- package/dist/im/lark/doc-comment.d.ts +20 -0
- package/dist/im/lark/doc-comment.d.ts.map +1 -1
- package/dist/im/lark/doc-comment.js +23 -0
- package/dist/im/lark/doc-comment.js.map +1 -1
- package/dist/im/lark/event-dispatcher.d.ts.map +1 -1
- package/dist/im/lark/event-dispatcher.js +13 -9
- package/dist/im/lark/event-dispatcher.js.map +1 -1
- package/dist/platform/bind.d.ts.map +1 -1
- package/dist/platform/bind.js +17 -3
- package/dist/platform/bind.js.map +1 -1
- package/dist/services/group-creator.d.ts.map +1 -1
- package/dist/services/group-creator.js +18 -2
- package/dist/services/group-creator.js.map +1 -1
- package/dist/skills/definitions.js +1 -1
- package/dist/worker.js +69 -12
- package/dist/worker.js.map +1 -1
- package/package.json +1 -1
package/dist/dashboard.js
CHANGED
|
@@ -21,7 +21,7 @@ import { handleConnectorApi } from './dashboard/connector-api.js';
|
|
|
21
21
|
import { redactGroupsForPublic, redactSchedulesForPublic } from './dashboard/public-redact.js';
|
|
22
22
|
import { handleWebhookRoute } from './dashboard/webhook-routes.js';
|
|
23
23
|
import { handleFederationApi } from './dashboard/federation-api.js';
|
|
24
|
-
import { handleFederationSpokeApi, syncAllMemberships } from './dashboard/federation-spoke-api.js';
|
|
24
|
+
import { handleFederationSpokeApi, syncAllMemberships, autoBindOwnerIfUnambiguous } from './dashboard/federation-spoke-api.js';
|
|
25
25
|
import { getRunsDir } from './workflows/runs-dir.js';
|
|
26
26
|
import { BotOnboardingManager } from './dashboard/bot-onboarding.js';
|
|
27
27
|
import { CLI_SELECT_OPTIONS, resolveCliSelection, isTtadkWrapper, ttadkAcceptsModel, TTADK_DEFAULT_MODEL, TTADK_MODEL_SUGGESTIONS, } from './setup/cli-selection.js';
|
|
@@ -52,8 +52,7 @@ import { installDashboardSkill, parseDashboardSkillInstallRequest, parseInstallL
|
|
|
52
52
|
import { botDefaultsPayload, botSummaryPayload } from './dashboard/bot-payload.js';
|
|
53
53
|
import { isValidRoleProfileId } from './services/role-profile-store.js';
|
|
54
54
|
import { mergeSafeInsightOverviews } from './services/insight/report.js';
|
|
55
|
-
import {
|
|
56
|
-
import { readPlatformBinding, PLATFORM_BINDING_PATH } from './platform/binding.js';
|
|
55
|
+
import { readPlatformBinding } from './platform/binding.js';
|
|
57
56
|
import { startPlatformTunnelClient } from './platform/tunnel-client.js';
|
|
58
57
|
import { cleanupIdleSessions, parseIdleCleanupHours } from './dashboard/session-cleanup.js';
|
|
59
58
|
const SECRET_PATH = join(homedir(), '.botmux', '.dashboard-secret');
|
|
@@ -898,6 +897,22 @@ const server = createServer(async (req, res) => {
|
|
|
898
897
|
return jsonRes(res, 404, { error: 'no_active_token' });
|
|
899
898
|
return jsonRes(res, 200, { url: dashboardUrlFor(activeToken) });
|
|
900
899
|
}
|
|
900
|
+
// CLI 通知绑定变化(HMAC + loopback)——`botmux bind` 写完绑定后捅一下,立即重连平台,
|
|
901
|
+
// 无需重启 daemon,也不依赖 fs.watch。
|
|
902
|
+
if (req.method === 'POST' && url.pathname === '/__cli/reload-binding') {
|
|
903
|
+
const gate = verifyCliRequest(req, url.pathname);
|
|
904
|
+
if (!gate.ok)
|
|
905
|
+
return jsonRes(res, gate.status, gate.body);
|
|
906
|
+
try {
|
|
907
|
+
platformTunnel?.stop();
|
|
908
|
+
}
|
|
909
|
+
catch {
|
|
910
|
+
/* ignore */
|
|
911
|
+
}
|
|
912
|
+
platformTunnel = null;
|
|
913
|
+
startPlatformTunnelIfBound();
|
|
914
|
+
return jsonRes(res, 200, { ok: true });
|
|
915
|
+
}
|
|
901
916
|
const presentedToken = authedToken(req, url);
|
|
902
917
|
const dashboardSettings = resolveDashboardSettings();
|
|
903
918
|
const decision = decideDashboardAuth({
|
|
@@ -2306,7 +2321,6 @@ listenWithProbe({
|
|
|
2306
2321
|
}
|
|
2307
2322
|
logger.info(`[dashboard] listening on ${config.dashboard.host}:${port}`);
|
|
2308
2323
|
startPlatformTunnelIfBound();
|
|
2309
|
-
watchPlatformBinding(); // bind 后无需重启 daemon,自动连接平台
|
|
2310
2324
|
}).catch((err) => {
|
|
2311
2325
|
logger.error(`[dashboard] could not bind near ${config.dashboard.host}:${config.dashboard.port} after probing — set BOTMUX_DASHBOARD_PORT to a free port. ${err.message}`);
|
|
2312
2326
|
process.exit(1);
|
|
@@ -2321,14 +2335,46 @@ const federationSync = setInterval(() => {
|
|
|
2321
2335
|
.catch(() => { });
|
|
2322
2336
|
}, 2 * 60 * 1000);
|
|
2323
2337
|
federationSync.unref();
|
|
2338
|
+
// 单候选自动绑定:standalone(未入团队)部署不必手动点面板「绑定」——用各机器人自己的
|
|
2339
|
+
// 凭证从 allowedUsers 解析出唯一负责人就自动认领,左上角飞书头像 / 拉群把发起人拉进群 /
|
|
2340
|
+
// 机器人归属随即生效。多候选(部署里配了多个人)仍保留手动选择。幂等:绑定后即刻 no-op。
|
|
2341
|
+
// 启动时按 0/5/15/60s 退避重试几次以覆盖 boot 时网络/凭证尚未就绪,之后交给手动按钮,
|
|
2342
|
+
// 不挂进永久心跳(避免对真·多候选/无 allowedUsers 的部署每 2 分钟空打飞书)。
|
|
2343
|
+
async function tryAutoBindOwner() {
|
|
2344
|
+
try {
|
|
2345
|
+
const r = await autoBindOwnerIfUnambiguous(config.session.dataDir, { fetcher: fetch, live: liveBots() });
|
|
2346
|
+
if (r.status === 'bound') {
|
|
2347
|
+
logger.info(`[identity] 已自动绑定本部署负责人:${r.owner?.name || r.owner?.unionId}(头像/拉群/归属即时生效)`);
|
|
2348
|
+
return 'done';
|
|
2349
|
+
}
|
|
2350
|
+
if (r.status === 'already_bound')
|
|
2351
|
+
return 'done';
|
|
2352
|
+
if (r.status === 'need_choice') {
|
|
2353
|
+
logger.info(`[identity] 检测到 ${r.candidates?.length ?? 0} 个候选负责人,请到面板「团队」手动选择绑定`);
|
|
2354
|
+
return 'done';
|
|
2355
|
+
}
|
|
2356
|
+
return 'retry'; // no_candidates:可能是网络/凭证未就绪的瞬时失败,退避后重试
|
|
2357
|
+
}
|
|
2358
|
+
catch (e) {
|
|
2359
|
+
logger.debug(`[identity] 自动绑定尝试失败(将退避重试):${e.message}`);
|
|
2360
|
+
return 'retry';
|
|
2361
|
+
}
|
|
2362
|
+
}
|
|
2363
|
+
void (async () => {
|
|
2364
|
+
for (const delayMs of [0, 5_000, 15_000, 60_000]) {
|
|
2365
|
+
if (delayMs)
|
|
2366
|
+
await new Promise((r) => setTimeout(r, delayMs));
|
|
2367
|
+
if ((await tryAutoBindOwner()) === 'done')
|
|
2368
|
+
return;
|
|
2369
|
+
}
|
|
2370
|
+
})();
|
|
2324
2371
|
// 中心化平台隧道(已绑定才启动;每台机器一个,跑在 dashboard 进程里)
|
|
2325
2372
|
let platformTunnel = null;
|
|
2326
|
-
// 当前隧道对应的绑定身份(platformUrl|machineId|machineToken);用于判断 bind 变化是否需要重连
|
|
2327
|
-
let platformBindingKey = null;
|
|
2328
2373
|
function readBotmuxVersion() {
|
|
2374
|
+
// 与本地 dashboard「版本与更新」卡同源:源码 checkout 的 package.json 是占位的 0.0.0,
|
|
2375
|
+
// resolveCurrentVersion() 会用 git describe 推出真实版本(如 2.91.1),npm 安装则用 package.json。
|
|
2329
2376
|
try {
|
|
2330
|
-
|
|
2331
|
-
return pkg.version || 'unknown';
|
|
2377
|
+
return resolveCurrentVersion();
|
|
2332
2378
|
}
|
|
2333
2379
|
catch {
|
|
2334
2380
|
return 'unknown';
|
|
@@ -2360,10 +2406,8 @@ function readPlatformBotsInfo() {
|
|
|
2360
2406
|
function startPlatformTunnelIfBound() {
|
|
2361
2407
|
try {
|
|
2362
2408
|
const binding = readPlatformBinding();
|
|
2363
|
-
if (!binding)
|
|
2364
|
-
platformBindingKey = null;
|
|
2409
|
+
if (!binding)
|
|
2365
2410
|
return;
|
|
2366
|
-
}
|
|
2367
2411
|
const version = readBotmuxVersion();
|
|
2368
2412
|
platformTunnel = startPlatformTunnelClient({
|
|
2369
2413
|
binding,
|
|
@@ -2373,52 +2417,12 @@ function startPlatformTunnelIfBound() {
|
|
|
2373
2417
|
getBots: () => readPlatformBotsInfo(),
|
|
2374
2418
|
log: (msg, extra) => logger.info(`[platform-tunnel] ${msg}${extra ? ' ' + JSON.stringify(extra) : ''}`),
|
|
2375
2419
|
});
|
|
2376
|
-
platformBindingKey = `${binding.platformUrl}|${binding.machineId}|${binding.machineToken}`;
|
|
2377
2420
|
logger.info(`[platform-tunnel] 绑定到 ${binding.platformUrl},启动隧道`);
|
|
2378
2421
|
}
|
|
2379
2422
|
catch (e) {
|
|
2380
2423
|
logger.warn(`[platform-tunnel] 启动失败: ${e.message}`);
|
|
2381
2424
|
}
|
|
2382
2425
|
}
|
|
2383
|
-
/**
|
|
2384
|
-
* 监听绑定文件变化:`botmux bind` 写入后无需重启 daemon,自动连接平台。
|
|
2385
|
-
* 只在绑定「身份」(platformUrl/machineId/machineToken) 变化时重连——团队成员变化也会改写该文件
|
|
2386
|
-
* (tunnel-client 自己写的),那种不重连,避免反复重启。
|
|
2387
|
-
*/
|
|
2388
|
-
function watchPlatformBinding() {
|
|
2389
|
-
let timer = null;
|
|
2390
|
-
const onChange = () => {
|
|
2391
|
-
if (timer)
|
|
2392
|
-
clearTimeout(timer);
|
|
2393
|
-
timer = setTimeout(() => {
|
|
2394
|
-
const b = readPlatformBinding();
|
|
2395
|
-
const key = b ? `${b.platformUrl}|${b.machineId}|${b.machineToken}` : null;
|
|
2396
|
-
if (key === platformBindingKey)
|
|
2397
|
-
return; // 没变(或仅团队变化)→ 不重连
|
|
2398
|
-
logger.info('[platform-tunnel] 检测到绑定变化,重连平台');
|
|
2399
|
-
try {
|
|
2400
|
-
platformTunnel?.stop();
|
|
2401
|
-
}
|
|
2402
|
-
catch {
|
|
2403
|
-
/* ignore */
|
|
2404
|
-
}
|
|
2405
|
-
platformTunnel = null;
|
|
2406
|
-
startPlatformTunnelIfBound();
|
|
2407
|
-
}, 800);
|
|
2408
|
-
};
|
|
2409
|
-
try {
|
|
2410
|
-
// 监听所在目录而非文件本身——绑定走原子写(临时文件 + rename),直接 watch 文件会在 rename 后失效
|
|
2411
|
-
const dir = dirname(PLATFORM_BINDING_PATH);
|
|
2412
|
-
const base = PLATFORM_BINDING_PATH.slice(dir.length + 1);
|
|
2413
|
-
fsWatch(dir, (_event, filename) => {
|
|
2414
|
-
if (!filename || filename === base)
|
|
2415
|
-
onChange();
|
|
2416
|
-
});
|
|
2417
|
-
}
|
|
2418
|
-
catch (e) {
|
|
2419
|
-
logger.warn(`[platform-tunnel] 绑定文件监听启动失败: ${e.message}`);
|
|
2420
|
-
}
|
|
2421
|
-
}
|
|
2422
2426
|
// Graceful shutdown
|
|
2423
2427
|
function shutdown() {
|
|
2424
2428
|
for (const off of subs.values())
|