botmux 2.28.1-canary.0 → 2.29.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/README.en.md +6 -3
- package/README.md +6 -3
- package/dist/bot-registry.d.ts +2 -1
- package/dist/bot-registry.d.ts.map +1 -1
- package/dist/bot-registry.js +1 -1
- package/dist/bot-registry.js.map +1 -1
- package/dist/cli.js +212 -22
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts +0 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +0 -1
- package/dist/config.js.map +1 -1
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +4 -10
- package/dist/core/session-manager.js.map +1 -1
- package/dist/i18n/en.js +1 -1
- package/dist/i18n/en.js.map +1 -1
- package/dist/i18n/zh.js +1 -1
- package/dist/i18n/zh.js.map +1 -1
- package/dist/im/lark/client.d.ts +3 -2
- package/dist/im/lark/client.d.ts.map +1 -1
- package/dist/im/lark/client.js +0 -6
- package/dist/im/lark/client.js.map +1 -1
- package/dist/im/lark/message-parser.d.ts.map +1 -1
- package/dist/im/lark/message-parser.js +11 -3
- package/dist/im/lark/message-parser.js.map +1 -1
- package/dist/setup/bot-config-editor.d.ts +44 -0
- package/dist/setup/bot-config-editor.d.ts.map +1 -0
- package/dist/setup/bot-config-editor.js +170 -0
- package/dist/setup/bot-config-editor.js.map +1 -0
- package/package.json +1 -1
package/README.en.md
CHANGED
|
@@ -350,6 +350,8 @@ Configure bots via `~/.botmux/bots.json`. Run `botmux setup` to create it intera
|
|
|
350
350
|
botmux setup
|
|
351
351
|
```
|
|
352
352
|
|
|
353
|
+
When `~/.botmux/bots.json` already exists, `botmux setup` can add a bot, reconfigure from scratch, edit an existing bot, or delete a bot config. The edit/delete flow accepts the process name shown by `botmux status` (e.g. `botmux-1` or a custom `botmux-claude-main`) or the `larkAppId`; empty input keeps the current value, and `-` clears optional fields such as `name`, `backendType`, `workingDir`, and `allowedUsers`. Changing `larkAppId` asks for confirmation because historical session/chat state under the old app ID is not migrated automatically. Deleting a bot only removes one local `bots.json` entry; it does not delete the Lark app, historical messages, or local session data. Run `botmux restart` for changes to take effect.
|
|
354
|
+
|
|
353
355
|
**bots.json format:**
|
|
354
356
|
|
|
355
357
|
```json
|
|
@@ -357,6 +359,7 @@ botmux setup
|
|
|
357
359
|
{
|
|
358
360
|
"larkAppId": "cli_xxx_bot1",
|
|
359
361
|
"larkAppSecret": "secret_1",
|
|
362
|
+
"name": "claude-main",
|
|
360
363
|
"cliId": "claude-code",
|
|
361
364
|
"workingDir": "~/projects",
|
|
362
365
|
"allowedUsers": ["alice@company.com"]
|
|
@@ -374,12 +377,12 @@ botmux setup
|
|
|
374
377
|
|-------|----------|-------------|
|
|
375
378
|
| `larkAppId` | Yes | Lark app ID |
|
|
376
379
|
| `larkAppSecret` | Yes | Lark app secret |
|
|
380
|
+
| `name` | No | Process name suffix shown by `botmux status`; e.g. `claude-main` appears as `botmux-claude-main`, defaults to `botmux-<index>` |
|
|
377
381
|
| `cliId` | No | CLI adapter, defaults to `claude-code` (options: `aiden`, `coco`, `codex`, `cursor`, `gemini`, `opencode`) |
|
|
378
|
-
| `cliPathOverride` | No | CLI
|
|
382
|
+
| `cliPathOverride` | No | Absolute path to the CLI entry, for wrappers / routers; typical use: `ccr`, `claude-w`, `aiden-x-claude`, etc. |
|
|
379
383
|
| `backendType` | No | Session backend: `pty` or `tmux` (auto-detected by default) |
|
|
380
384
|
| `workingDir` | No | Default working directory, supports comma-separated |
|
|
381
385
|
| `allowedUsers` | No | Allowed users (email prefixes or open_ids) |
|
|
382
|
-
| `projectScanDir` | No | Directory to scan for git repos |
|
|
383
386
|
| `oncallChats` | No | Oncall bindings (written by `/oncall bind`), e.g. `[{ "chatId": "oc_xxx", "workingDir": "~/projects/foo" }]`; any group member can @ the bot |
|
|
384
387
|
|
|
385
388
|
**Config priority:** `BOTS_CONFIG` env var > `~/.botmux/bots.json`
|
|
@@ -408,7 +411,7 @@ botmux setup
|
|
|
408
411
|
|
|
409
412
|
| Command | Description |
|
|
410
413
|
|---------|-------------|
|
|
411
|
-
| `botmux setup` | Interactive setup (first-time
|
|
414
|
+
| `botmux setup` | Interactive setup (first-time / add / edit / delete bots) |
|
|
412
415
|
| `botmux start` | Start daemon (PM2 managed) |
|
|
413
416
|
| `botmux stop` | Stop daemon |
|
|
414
417
|
| `botmux restart` | Restart daemon (auto-restores active sessions) |
|
package/README.md
CHANGED
|
@@ -339,7 +339,7 @@ botmux autostart enable
|
|
|
339
339
|
|
|
340
340
|
| 命令 | 说明 |
|
|
341
341
|
|------|------|
|
|
342
|
-
| `botmux setup` | 交互式配置(首次使用 /
|
|
342
|
+
| `botmux setup` | 交互式配置(首次使用 / 添加 / 编辑 / 删除机器人) |
|
|
343
343
|
| `botmux start` | 启动 daemon(PM2 管理) |
|
|
344
344
|
| `botmux stop` | 停止 daemon |
|
|
345
345
|
| `botmux restart` | 重启 daemon(自动恢复活跃会话) |
|
|
@@ -421,6 +421,8 @@ dashboard 走单独 pm2 进程 `botmux-dashboard`,跟着 `pnpm daemon:restart`
|
|
|
421
421
|
botmux setup
|
|
422
422
|
```
|
|
423
423
|
|
|
424
|
+
已有 `~/.botmux/bots.json` 时,`botmux setup` 支持添加新机器人、重新配置、编辑现有机器人,以及删除机器人配置。编辑或删除时用 `botmux status` 里的进程名(如 `botmux-1` 或自定义的 `botmux-claude-main`)或 `larkAppId` 选择目标;字段留空表示保留当前值,`name`、`cliPathOverride`、`backendType`、`workingDir`、`allowedUsers` 等可选字段输入 `-` 表示清空。修改 `larkAppId` 会提示确认,因为旧 appId 下的历史会话和群聊状态数据不会自动迁移。删除机器人只移除本机 `bots.json` 中的一项,不删除飞书开放平台应用、历史消息或本地会话数据;修改完成后运行 `botmux restart` 生效。
|
|
425
|
+
|
|
424
426
|
**bots.json 格式:**
|
|
425
427
|
|
|
426
428
|
```json
|
|
@@ -428,6 +430,7 @@ botmux setup
|
|
|
428
430
|
{
|
|
429
431
|
"larkAppId": "cli_xxx_bot1",
|
|
430
432
|
"larkAppSecret": "secret_1",
|
|
433
|
+
"name": "claude-main",
|
|
431
434
|
"cliId": "claude-code",
|
|
432
435
|
"workingDir": "~/projects",
|
|
433
436
|
"allowedUsers": ["alice@company.com"]
|
|
@@ -445,12 +448,12 @@ botmux setup
|
|
|
445
448
|
|------|------|------|
|
|
446
449
|
| `larkAppId` | 是 | 飞书应用 App ID |
|
|
447
450
|
| `larkAppSecret` | 是 | 飞书应用 App Secret |
|
|
451
|
+
| `name` | 否 | `botmux status` 中的进程名后缀;例如 `claude-main` 会显示为 `botmux-claude-main`,留空默认 `botmux-<序号>` |
|
|
448
452
|
| `cliId` | 否 | CLI 适配器,默认 `claude-code`(可选:`aiden`、`coco`、`codex`、`cursor`、`gemini`、`opencode`) |
|
|
449
|
-
| `cliPathOverride` | 否 | CLI
|
|
453
|
+
| `cliPathOverride` | 否 | CLI 入口的绝对路径,用于套 wrapper / router;典型场景:ccr、claude-w、aiden-x-claude 等自定义入口 |
|
|
450
454
|
| `backendType` | 否 | 会话后端:`pty` 或 `tmux`(默认自动检测) |
|
|
451
455
|
| `workingDir` | 否 | 默认工作目录,支持逗号分隔多个目录 |
|
|
452
456
|
| `allowedUsers` | 否 | 允许的用户列表(邮箱前缀或 open_id) |
|
|
453
|
-
| `projectScanDir` | 否 | 扫描 Git 仓库的目录 |
|
|
454
457
|
| `oncallChats` | 否 | oncall 绑定(`/oncall bind` 写入),形如 `[{ "chatId": "oc_xxx", "workingDir": "~/projects/foo" }]`,群内任何成员可 @ 提问 |
|
|
455
458
|
|
|
456
459
|
**配置优先级:** `BOTS_CONFIG` 环境变量 → `~/.botmux/bots.json`
|
package/dist/bot-registry.d.ts
CHANGED
|
@@ -26,13 +26,14 @@ export interface BotDefaultOncall {
|
|
|
26
26
|
export interface BotConfig {
|
|
27
27
|
larkAppId: string;
|
|
28
28
|
larkAppSecret: string;
|
|
29
|
+
/** Optional process-name suffix; the daemon's process name is rendered as `botmux-<name>` (defaults to `botmux-<index>`). */
|
|
30
|
+
name?: string;
|
|
29
31
|
cliId: CliId;
|
|
30
32
|
cliPathOverride?: string;
|
|
31
33
|
backendType?: 'pty' | 'tmux';
|
|
32
34
|
workingDir?: string;
|
|
33
35
|
workingDirs?: string[];
|
|
34
36
|
allowedUsers?: string[];
|
|
35
|
-
projectScanDir?: string;
|
|
36
37
|
/** Oncall bindings: chat_id → default workingDir. Any group member can talk; allowedUsers still gates card buttons / daemon commands. */
|
|
37
38
|
oncallChats?: OncallChat[];
|
|
38
39
|
/** UI language for this bot: 'zh' or 'en'. Falls back to BOTMUX_LANG / LANG env when unset. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bot-registry.d.ts","sourceRoot":"","sources":["../src/bot-registry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,IAAI,MAAM,yBAAyB,CAAC;AAIhD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,yBAAyB,CAAC;AAErD,OAAO,EAA0B,KAAK,MAAM,EAAE,MAAM,iBAAiB,CAAC;AAEtE,MAAM,WAAW,UAAU;IACzB,qDAAqD;IACrD,MAAM,EAAE,MAAM,CAAC;IACf,+EAA+E;IAC/E,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;;;;;GAUG;AACH,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,OAAO,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,SAAS;IACxB,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,MAAM,CAAC;IACtB,KAAK,EAAE,KAAK,CAAC;IACb,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,WAAW,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC;IAC7B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,
|
|
1
|
+
{"version":3,"file":"bot-registry.d.ts","sourceRoot":"","sources":["../src/bot-registry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,IAAI,MAAM,yBAAyB,CAAC;AAIhD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,yBAAyB,CAAC;AAErD,OAAO,EAA0B,KAAK,MAAM,EAAE,MAAM,iBAAiB,CAAC;AAEtE,MAAM,WAAW,UAAU;IACzB,qDAAqD;IACrD,MAAM,EAAE,MAAM,CAAC;IACf,+EAA+E;IAC/E,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;;;;;GAUG;AACH,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,OAAO,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,SAAS;IACxB,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,MAAM,CAAC;IACtB,6HAA6H;IAC7H,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,KAAK,CAAC;IACb,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,WAAW,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC;IAC7B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,yIAAyI;IACzI,WAAW,CAAC,EAAE,UAAU,EAAE,CAAC;IAC3B,+FAA+F;IAC/F,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,oFAAoF;IACpF,aAAa,CAAC,EAAE,gBAAgB,CAAC;IACjC;;;;;;OAMG;IACH,2BAA2B,CAAC,EAAE,MAAM,EAAE,CAAC;CACxC;AAED,MAAM,WAAW,QAAQ;IACvB,MAAM,EAAE,SAAS,CAAC;IAClB,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,oBAAoB,EAAE,MAAM,EAAE,CAAC;CAChC;AAUD,wBAAgB,mBAAmB,IAAI,MAAM,GAAG,SAAS,CAExD;AAwBD,wBAAgB,WAAW,CAAC,GAAG,EAAE,SAAS,GAAG,QAAQ,CAapD;AAED,wBAAgB,MAAM,CAAC,SAAS,EAAE,MAAM,GAAG,QAAQ,CAMlD;AAED,wBAAgB,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC,MAAM,CAE3D;AAED,wBAAgB,UAAU,IAAI,QAAQ,EAAE,CAEvC;AAED,8DAA8D;AAC9D,wBAAgB,cAAc,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,UAAU,GAAG,SAAS,CAGxF;AAeD,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,MAAM,GAAG,UAAU,GAAG,SAAS,CA+B9E;AAED,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAElE;AAED;;;;GAIG;AACH,wBAAgB,cAAc,IAAI,SAAS,EAAE,CAsB5C"}
|
package/dist/bot-registry.js
CHANGED
|
@@ -204,13 +204,13 @@ function parseBotConfigFile(filePath) {
|
|
|
204
204
|
configs.push({
|
|
205
205
|
larkAppId: entry.larkAppId,
|
|
206
206
|
larkAppSecret: entry.larkAppSecret,
|
|
207
|
+
name: typeof entry.name === 'string' && entry.name.trim() ? entry.name.trim() : undefined,
|
|
207
208
|
cliId: entry.cliId ?? 'claude-code',
|
|
208
209
|
cliPathOverride: entry.cliPathOverride,
|
|
209
210
|
backendType: entry.backendType,
|
|
210
211
|
workingDir: workingDirs?.[0] ?? entry.workingDir,
|
|
211
212
|
workingDirs,
|
|
212
213
|
allowedUsers: entry.allowedUsers,
|
|
213
|
-
projectScanDir: entry.projectScanDir,
|
|
214
214
|
oncallChats,
|
|
215
215
|
defaultOncall,
|
|
216
216
|
defaultOncallAutoboundChats,
|
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,QAAQ,EAAE,MAAM,SAAS,CAAC;AAC7D,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAElC,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAC3C,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAe,MAAM,iBAAiB,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
|
@@ -27,6 +27,7 @@ import { createHmac, randomBytes } from 'node:crypto';
|
|
|
27
27
|
import { enableAutostart, disableAutostart, autostartStatus, refreshAutostart } from './autostart.js';
|
|
28
28
|
import { tmuxEnv } from './setup/ensure-tmux.js';
|
|
29
29
|
import { writeBotsJsonAtomic as writeBotsAtomic } from './setup/bots-store.js';
|
|
30
|
+
import { applyBotConfigEdits, assertUniqueBotProcessNames, botProcessName, normalizeBotConfig, parseBotConfigsJson, parseBotSelection, removeBotConfig, resolveCliId, } from './setup/bot-config-editor.js';
|
|
30
31
|
import { logger } from './utils/logger.js';
|
|
31
32
|
import { firstPositional } from './cli/arg-utils.js';
|
|
32
33
|
import { isLocale, setDefaultLocale, SUPPORTED_LOCALES } from './i18n/index.js';
|
|
@@ -102,17 +103,29 @@ function runPm2(args, inherit = true, home = PM2_HOME) {
|
|
|
102
103
|
function loadBotsJson() {
|
|
103
104
|
if (existsSync(BOTS_JSON_FILE)) {
|
|
104
105
|
try {
|
|
105
|
-
return
|
|
106
|
+
return parseBotConfigsJson(readFileSync(BOTS_JSON_FILE, 'utf-8'), BOTS_JSON_FILE);
|
|
106
107
|
}
|
|
107
|
-
catch {
|
|
108
|
-
|
|
108
|
+
catch (err) {
|
|
109
|
+
console.error(`❌ ${err?.message ?? String(err)}`);
|
|
110
|
+
process.exit(1);
|
|
109
111
|
}
|
|
110
112
|
}
|
|
111
113
|
return [];
|
|
112
114
|
}
|
|
115
|
+
function ensureUniqueBotProcessNames(bots) {
|
|
116
|
+
try {
|
|
117
|
+
assertUniqueBotProcessNames(bots, PM2_NAME);
|
|
118
|
+
}
|
|
119
|
+
catch (err) {
|
|
120
|
+
console.error(`❌ ${err?.message ?? String(err)}`);
|
|
121
|
+
console.error(' 请修改 bots.json 中的 name,确保进程名唯一。');
|
|
122
|
+
process.exit(1);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
113
125
|
function ecosystemConfig() {
|
|
114
126
|
const daemonScript = join(PKG_ROOT, 'dist', 'index-daemon.js');
|
|
115
127
|
const bots = loadBotsJson();
|
|
128
|
+
ensureUniqueBotProcessNames(bots);
|
|
116
129
|
const baseApp = {
|
|
117
130
|
script: daemonScript,
|
|
118
131
|
cwd: CONFIG_DIR,
|
|
@@ -124,7 +137,7 @@ function ecosystemConfig() {
|
|
|
124
137
|
};
|
|
125
138
|
const apps = bots.map((_bot, i) => ({
|
|
126
139
|
...baseApp,
|
|
127
|
-
name:
|
|
140
|
+
name: botProcessName(_bot, i, PM2_NAME),
|
|
128
141
|
error_file: join(LOG_DIR, `daemon-${i}-error.log`),
|
|
129
142
|
out_file: join(LOG_DIR, `daemon-${i}-out.log`),
|
|
130
143
|
env: { SESSION_DATA_DIR: DATA_DIR, BOTMUX_BOT_INDEX: String(i) },
|
|
@@ -156,10 +169,18 @@ function ask(rl, question) {
|
|
|
156
169
|
return new Promise(resolve => rl.question(question, resolve));
|
|
157
170
|
}
|
|
158
171
|
// ─── Setup helpers ──────────────────────────────────────────────────────────
|
|
172
|
+
function printInputHelp(title, lines) {
|
|
173
|
+
console.log(`\n${title}`);
|
|
174
|
+
for (const line of lines) {
|
|
175
|
+
console.log(` ${line}`);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
159
178
|
// Thin wrapper around setup/bots-store.writeBotsJsonAtomic so call-sites keep
|
|
160
179
|
// the same name without passing BOTS_JSON_FILE explicitly each time.
|
|
161
180
|
function writeBotsJsonAtomic(bots) {
|
|
162
|
-
|
|
181
|
+
const normalized = bots.map(bot => normalizeBotConfig(bot));
|
|
182
|
+
ensureUniqueBotProcessNames(normalized);
|
|
183
|
+
writeBotsAtomic(BOTS_JSON_FILE, normalized);
|
|
163
184
|
}
|
|
164
185
|
/**
|
|
165
186
|
* 从 bot 配置里取 brand. 旧的 bots.json (1.0 之前) 没这个字段, default 到 feishu
|
|
@@ -341,8 +362,15 @@ async function promptBotConfig(rl) {
|
|
|
341
362
|
console.log('✅ 凭证有效(tenant_access_token 已成功获取)\n');
|
|
342
363
|
console.log('支持的 CLI: 1) claude-code 2) aiden 3) coco 4) codex 5) cursor 6) gemini 7) opencode');
|
|
343
364
|
const cliChoice = await ask(rl, 'CLI 适配器 [1]: ');
|
|
344
|
-
|
|
345
|
-
|
|
365
|
+
let cliId;
|
|
366
|
+
try {
|
|
367
|
+
cliId = resolveCliId(cliChoice) ?? 'claude-code';
|
|
368
|
+
}
|
|
369
|
+
catch (err) {
|
|
370
|
+
console.log(`\n❌ ${err?.message ?? String(err)}`);
|
|
371
|
+
console.log(' 不写 bots.json。请重新运行 botmux setup。');
|
|
372
|
+
return null;
|
|
373
|
+
}
|
|
346
374
|
const workingDir = await ask(rl, '默认工作目录 [~]: ');
|
|
347
375
|
// 不再持久化 brand 字段: setup 阶段 brand=lark 直接被 obtainCredentials 中止,
|
|
348
376
|
// 落盘的永远是 'feishu', 写进配置是死字段. 等 lark 完整接入再加回来, 那时
|
|
@@ -361,7 +389,90 @@ async function promptBotConfig(rl) {
|
|
|
361
389
|
// 字段即可. 手动 fallback 场景没 open_id, 字段直接不写 (== 不限制).
|
|
362
390
|
if (creds.userOpenId)
|
|
363
391
|
bot.allowedUsers = [creds.userOpenId];
|
|
364
|
-
return bot;
|
|
392
|
+
return normalizeBotConfig(bot);
|
|
393
|
+
}
|
|
394
|
+
function formatOptionalValue(v) {
|
|
395
|
+
if (Array.isArray(v))
|
|
396
|
+
return v.join(',');
|
|
397
|
+
if (typeof v === 'string' && v)
|
|
398
|
+
return v;
|
|
399
|
+
return '未设置';
|
|
400
|
+
}
|
|
401
|
+
/**
|
|
402
|
+
* 把 bots.json 渲染成对齐的小表格. 不带行号——进程名 (botmux-N) 已经
|
|
403
|
+
* 是唯一可寻址的标识, 行号 + 进程名后缀 1-based / 0-based 并列容易引
|
|
404
|
+
* 起 off-by-one 误解 (用户曾踩过 "1. botmux-0" 这种排版).
|
|
405
|
+
*
|
|
406
|
+
* 选择机器人时直接输完整进程名 (botmux-N / botmux-custom) 或 AppID,
|
|
407
|
+
* parseBotSelection 不再接受裸数字, 避免又冒出 "序号到底是几" 的歧义.
|
|
408
|
+
*/
|
|
409
|
+
function formatBotConfigTable(bots) {
|
|
410
|
+
if (bots.length === 0)
|
|
411
|
+
return '';
|
|
412
|
+
const headers = ['进程名', 'App ID', 'CLI'];
|
|
413
|
+
const rows = bots.map((b, i) => [
|
|
414
|
+
botProcessName(b, i, PM2_NAME),
|
|
415
|
+
String(b?.larkAppId ?? ''),
|
|
416
|
+
String(b?.cliId ?? 'claude-code'),
|
|
417
|
+
]);
|
|
418
|
+
const widths = headers.map((h, c) => Math.max(displayWidth(h), ...rows.map(r => displayWidth(r[c]))));
|
|
419
|
+
const render = (cells) => ' ' + cells.map((cell, i) => padEndDisplay(cell, widths[i])).join(' ');
|
|
420
|
+
return [render(headers), ...rows.map(render)].join('\n');
|
|
421
|
+
}
|
|
422
|
+
async function promptEditBotConfig(rl, bot) {
|
|
423
|
+
console.log('\n字段留空表示保留当前值;可选字段输入 - 表示清空。\n');
|
|
424
|
+
const input = {};
|
|
425
|
+
printInputHelp('botmux status 显示名称', [
|
|
426
|
+
'可选。用于本机进程名,方便在 botmux status / logs 中识别机器人。',
|
|
427
|
+
'留空保留当前值;输入 - 清空自定义名称并恢复 botmux-<序号>。',
|
|
428
|
+
]);
|
|
429
|
+
input.name = await ask(rl, `botmux status 显示名称 [${formatOptionalValue(bot.name)}]: `);
|
|
430
|
+
printInputHelp('LARK_APP_ID', [
|
|
431
|
+
'飞书开放平台应用的 App ID。修改后,这个配置项会切到另一个飞书应用。',
|
|
432
|
+
'留空保留当前值;修改会二次确认,因为历史会话和群聊状态不会自动迁移。',
|
|
433
|
+
]);
|
|
434
|
+
input.larkAppId = await ask(rl, `LARK_APP_ID [${bot.larkAppId}]: `);
|
|
435
|
+
printInputHelp('LARK_APP_SECRET', [
|
|
436
|
+
'当前 App ID 对应的 App Secret。只更新密钥时填写这一项即可。',
|
|
437
|
+
'留空保留当前值。',
|
|
438
|
+
]);
|
|
439
|
+
input.larkAppSecret = await ask(rl, `LARK_APP_SECRET [保留当前值]: `);
|
|
440
|
+
console.log('\n支持的 CLI: 1) claude-code 2) aiden 3) coco 4) codex 5) cursor 6) gemini 7) opencode');
|
|
441
|
+
printInputHelp('CLI 适配器', [
|
|
442
|
+
'选择 botmux 需要套用哪一种 CLI 参数协议和会话恢复方式。',
|
|
443
|
+
'留空保留当前值;可以输入序号,也可以直接输入适配器 ID。',
|
|
444
|
+
]);
|
|
445
|
+
input.cliChoice = await ask(rl, `CLI 适配器 [${bot.cliId ?? 'claude-code'}]: `);
|
|
446
|
+
printInputHelp('CLI 可执行文件路径覆盖', [
|
|
447
|
+
'可选。CLI 入口的绝对路径,用于在原 CLI 外面套一层 wrapper / router。',
|
|
448
|
+
'典型场景:ccr (Claude Code Router) / claude-w / aiden-x-claude 等自定义入口。',
|
|
449
|
+
'留空保留当前值;输入 - 清空覆盖,回到 PATH 查 cliId 对应的默认二进制。',
|
|
450
|
+
]);
|
|
451
|
+
input.cliPathOverride = await ask(rl, `CLI 可执行文件路径覆盖 [${formatOptionalValue(bot.cliPathOverride)}]: `);
|
|
452
|
+
printInputHelp('会话后端 backendType', [
|
|
453
|
+
'可选。pty 更轻量;tmux 支持 adopt 和 Web Terminal 附着。',
|
|
454
|
+
'留空保留当前值;输入 - 回到自动检测;只接受 pty 或 tmux。',
|
|
455
|
+
]);
|
|
456
|
+
input.backendType = await ask(rl, `会话后端 backendType [${formatOptionalValue(bot.backendType)}]: `);
|
|
457
|
+
printInputHelp('默认工作目录', [
|
|
458
|
+
'可选。新会话默认进入的目录,支持逗号分隔多个候选目录。',
|
|
459
|
+
'留空保留当前值;输入 - 清空并回到默认 ~。',
|
|
460
|
+
]);
|
|
461
|
+
input.workingDir = await ask(rl, `默认工作目录 [${formatOptionalValue(bot.workingDir)}]: `);
|
|
462
|
+
printInputHelp('允许的用户', [
|
|
463
|
+
'可选。限制哪些飞书用户可以操作机器人,支持邮箱前缀或 open_id,多个值用逗号分隔。',
|
|
464
|
+
'留空保留当前值;输入 - 清空限制。',
|
|
465
|
+
]);
|
|
466
|
+
input.allowedUsers = await ask(rl, `允许的用户 [${formatOptionalValue(bot.allowedUsers)}]: `);
|
|
467
|
+
const edited = applyBotConfigEdits(bot, input);
|
|
468
|
+
if (edited.larkAppId !== bot.larkAppId) {
|
|
469
|
+
console.log('\n⚠️ LARK_APP_ID 变更后,旧 appId 下的历史会话/群聊状态数据不会自动迁移。');
|
|
470
|
+
const confirm = (await ask(rl, `确认将 LARK_APP_ID 从 ${bot.larkAppId} 改为 ${edited.larkAppId}? (y/N): `)).trim().toLowerCase();
|
|
471
|
+
if (confirm !== 'y' && confirm !== 'yes') {
|
|
472
|
+
edited.larkAppId = bot.larkAppId;
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
return edited;
|
|
365
476
|
}
|
|
366
477
|
/** Parse .env file to extract bot config for migration to bots.json */
|
|
367
478
|
function parseDotEnvToBotConfig() {
|
|
@@ -382,16 +493,14 @@ function parseDotEnvToBotConfig() {
|
|
|
382
493
|
};
|
|
383
494
|
if (vars.CLI_ID)
|
|
384
495
|
bot.cliId = vars.CLI_ID;
|
|
385
|
-
if (vars.CLI_PATH)
|
|
386
|
-
bot.cliPathOverride = vars.CLI_PATH;
|
|
496
|
+
if (vars.CLI_PATH?.trim())
|
|
497
|
+
bot.cliPathOverride = vars.CLI_PATH.trim();
|
|
387
498
|
if (vars.BACKEND_TYPE)
|
|
388
499
|
bot.backendType = vars.BACKEND_TYPE;
|
|
389
500
|
if (vars.WORKING_DIR)
|
|
390
501
|
bot.workingDir = vars.WORKING_DIR;
|
|
391
502
|
if (vars.ALLOWED_USERS)
|
|
392
503
|
bot.allowedUsers = vars.ALLOWED_USERS.split(',').map((s) => s.trim()).filter(Boolean);
|
|
393
|
-
if (vars.PROJECT_SCAN_DIR)
|
|
394
|
-
bot.projectScanDir = vars.PROJECT_SCAN_DIR;
|
|
395
504
|
return bot;
|
|
396
505
|
}
|
|
397
506
|
/**
|
|
@@ -424,14 +533,12 @@ async function cmdSetup() {
|
|
|
424
533
|
console.log(`数据目录: ${DATA_DIR}\n`);
|
|
425
534
|
if (hasBots) {
|
|
426
535
|
// --- Multi-bot mode (bots.json exists) ---
|
|
427
|
-
const bots =
|
|
428
|
-
console.log(`已配置 ${bots.length}
|
|
429
|
-
|
|
430
|
-
console.log(` ${i + 1}. ${bots[i].larkAppId} (${bots[i].cliId ?? 'claude-code'})`);
|
|
431
|
-
}
|
|
536
|
+
const bots = loadBotsJson();
|
|
537
|
+
console.log(`已配置 ${bots.length} 个机器人:\n`);
|
|
538
|
+
console.log(formatBotConfigTable(bots));
|
|
432
539
|
console.log('');
|
|
433
540
|
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
434
|
-
const action = await ask(rl, '操作: 1) 添加新机器人 2) 重新配置 (1/2) [1]: ');
|
|
541
|
+
const action = await ask(rl, '操作: 1) 添加新机器人 2) 重新配置 3) 编辑现有机器人 4) 删除机器人 (1/2/3/4) [1]: ');
|
|
435
542
|
if (action === '2') {
|
|
436
543
|
console.log('\n── 重新配置 ──\n');
|
|
437
544
|
const newBot = await promptBotConfig(rl);
|
|
@@ -451,6 +558,79 @@ async function cmdSetup() {
|
|
|
451
558
|
console.log(`下一步: botmux restart\n`);
|
|
452
559
|
return;
|
|
453
560
|
}
|
|
561
|
+
if (action === '3') {
|
|
562
|
+
console.log('\n── 编辑现有机器人 ──\n');
|
|
563
|
+
const selected = await ask(rl, '选择机器人(进程名 或 AppID): ');
|
|
564
|
+
const index = parseBotSelection(selected, bots);
|
|
565
|
+
if (index === undefined) {
|
|
566
|
+
rl.close();
|
|
567
|
+
console.log('\n❌ 找不到指定机器人,配置未修改。');
|
|
568
|
+
return;
|
|
569
|
+
}
|
|
570
|
+
const original = bots[index];
|
|
571
|
+
let edited;
|
|
572
|
+
try {
|
|
573
|
+
edited = await promptEditBotConfig(rl, original);
|
|
574
|
+
}
|
|
575
|
+
catch (err) {
|
|
576
|
+
rl.close();
|
|
577
|
+
console.log(`\n❌ 编辑失败: ${err?.message ?? String(err)}`);
|
|
578
|
+
return;
|
|
579
|
+
}
|
|
580
|
+
// 凭证字段有变化时, 像 promptBotConfig 一样跑一次 tenant_access_token
|
|
581
|
+
// 校验. 失败不写盘——避免编辑后 typo 一个字符, daemon 重启时才发现.
|
|
582
|
+
// (cmdRestart 不校验凭证, 只 cmdStart 校验, 所以编辑路径必须自己兜.)
|
|
583
|
+
const appIdChanged = edited.larkAppId !== original.larkAppId;
|
|
584
|
+
const appSecretChanged = edited.larkAppSecret !== original.larkAppSecret;
|
|
585
|
+
if (appIdChanged || appSecretChanged) {
|
|
586
|
+
console.log('\n校验新凭证(取 tenant_access_token)…');
|
|
587
|
+
const { validateCredentials } = await import('./setup/verify-permissions.js');
|
|
588
|
+
const v = await validateCredentials(edited.larkAppId, edited.larkAppSecret, botBrand(edited));
|
|
589
|
+
if (!v.ok) {
|
|
590
|
+
rl.close();
|
|
591
|
+
console.log(`\n❌ 凭证校验失败 (${v.error}): ${v.message}`);
|
|
592
|
+
console.log(' 配置未修改。请重新运行 botmux setup → 编辑现有机器人。');
|
|
593
|
+
return;
|
|
594
|
+
}
|
|
595
|
+
console.log('✅ 凭证有效\n');
|
|
596
|
+
}
|
|
597
|
+
rl.close();
|
|
598
|
+
const nextBots = bots.slice();
|
|
599
|
+
nextBots[index] = edited;
|
|
600
|
+
copyFileSync(BOTS_JSON_FILE, BOTS_JSON_FILE + '.bak');
|
|
601
|
+
console.log(`旧配置已备份: ${BOTS_JSON_FILE}.bak`);
|
|
602
|
+
writeBotsJsonAtomic(nextBots);
|
|
603
|
+
console.log(`✅ 已更新机器人 ${botProcessName(edited, index, PM2_NAME)} (${edited.larkAppId})`);
|
|
604
|
+
// appId 切换 = 换了一个飞书应用, 新 appId 大概率需要重新申请权限 + 配重定向 URL.
|
|
605
|
+
// 把 printRemainingSteps 的深链端给用户, 比 README 警告里那句"历史数据不迁移"更可操作.
|
|
606
|
+
if (appIdChanged) {
|
|
607
|
+
printRemainingSteps(edited.larkAppId, botBrand(edited));
|
|
608
|
+
}
|
|
609
|
+
console.log(`下一步: botmux restart\n`);
|
|
610
|
+
return;
|
|
611
|
+
}
|
|
612
|
+
if (action === '4') {
|
|
613
|
+
console.log('\n── 删除机器人 ──\n');
|
|
614
|
+
const selected = await ask(rl, '选择机器人(进程名 或 AppID): ');
|
|
615
|
+
const result = removeBotConfig(bots, selected);
|
|
616
|
+
if (!result) {
|
|
617
|
+
rl.close();
|
|
618
|
+
console.log('\n❌ 找不到指定机器人,配置未修改。');
|
|
619
|
+
return;
|
|
620
|
+
}
|
|
621
|
+
const confirm = (await ask(rl, `确认删除 ${botProcessName(result.removed, result.index, PM2_NAME)} (${result.removed.larkAppId})? (y/N): `)).trim().toLowerCase();
|
|
622
|
+
rl.close();
|
|
623
|
+
if (confirm !== 'y' && confirm !== 'yes') {
|
|
624
|
+
console.log('\n已取消,配置未修改。');
|
|
625
|
+
return;
|
|
626
|
+
}
|
|
627
|
+
copyFileSync(BOTS_JSON_FILE, BOTS_JSON_FILE + '.bak');
|
|
628
|
+
console.log(`旧配置已备份: ${BOTS_JSON_FILE}.bak`);
|
|
629
|
+
writeBotsJsonAtomic(result.bots);
|
|
630
|
+
console.log(`✅ 已删除机器人 ${botProcessName(result.removed, result.index, PM2_NAME)} (${result.removed.larkAppId})`);
|
|
631
|
+
console.log(`下一步: botmux restart\n`);
|
|
632
|
+
return;
|
|
633
|
+
}
|
|
454
634
|
console.log('\n── 添加新机器人 ──\n');
|
|
455
635
|
const newBot = await promptBotConfig(rl);
|
|
456
636
|
rl.close();
|
|
@@ -757,12 +937,12 @@ async function cmdRestart() {
|
|
|
757
937
|
ensureConfigDir();
|
|
758
938
|
preflightNodeSanity();
|
|
759
939
|
await ensureSystemDependencies();
|
|
940
|
+
const cfg = ecosystemConfig();
|
|
760
941
|
cleanupLegacyPm2();
|
|
761
942
|
// Delete all botmux processes (handles both old single-process and new multi-process)
|
|
762
943
|
deleteAllBotmuxProcesses();
|
|
763
944
|
// Wipe abandoned dashboard-daemon descriptors left behind by killed daemons.
|
|
764
945
|
cleanupStaleDaemonDescriptors();
|
|
765
|
-
const cfg = ecosystemConfig();
|
|
766
946
|
runPm2(['start', cfg]);
|
|
767
947
|
if (refreshAutostart({ pkgRoot: PKG_ROOT, configDir: CONFIG_DIR, logDir: LOG_DIR })) {
|
|
768
948
|
console.log(`autostart unit 已同步到当前 Node/cli.js 路径`);
|
|
@@ -830,13 +1010,23 @@ function cmdLogs() {
|
|
|
830
1010
|
? process.argv[process.argv.indexOf('--lines') + 1] || '50'
|
|
831
1011
|
: '50';
|
|
832
1012
|
const bots = loadBotsJson();
|
|
833
|
-
// Support --bot <index> to filter specific bot logs
|
|
1013
|
+
// Support --bot <0-based-index|pm2-name|appId> to filter specific bot logs.
|
|
834
1014
|
const botIdx = process.argv.includes('--bot')
|
|
835
1015
|
? process.argv[process.argv.indexOf('--bot') + 1]
|
|
836
1016
|
: undefined;
|
|
837
1017
|
let target;
|
|
838
1018
|
if (botIdx !== undefined) {
|
|
839
|
-
|
|
1019
|
+
const numericIdx = /^\d+$/.test(botIdx) ? Number(botIdx) : undefined;
|
|
1020
|
+
const selectedIdx = numericIdx === undefined
|
|
1021
|
+
? parseBotSelection(botIdx, bots)
|
|
1022
|
+
: numericIdx >= 0 && numericIdx < bots.length
|
|
1023
|
+
? numericIdx
|
|
1024
|
+
: undefined;
|
|
1025
|
+
target = selectedIdx !== undefined
|
|
1026
|
+
? botProcessName(bots[selectedIdx], selectedIdx, PM2_NAME)
|
|
1027
|
+
: numericIdx !== undefined
|
|
1028
|
+
? `${PM2_NAME}-${botIdx}`
|
|
1029
|
+
: botIdx;
|
|
840
1030
|
}
|
|
841
1031
|
else {
|
|
842
1032
|
// Show all botmux logs via pm2 regex match
|
|
@@ -1644,7 +1834,7 @@ botmux v${getVersion()} — IM ↔ AI 编程 CLI 桥接
|
|
|
1644
1834
|
start 启动 daemon
|
|
1645
1835
|
stop 停止 daemon
|
|
1646
1836
|
restart 重启 daemon(自动恢复活跃会话)
|
|
1647
|
-
logs 查看 daemon 日志(--lines N, --bot <index>)
|
|
1837
|
+
logs 查看 daemon 日志(--lines N, --bot <0-based-index|pm2-name|appId>)
|
|
1648
1838
|
status 查看 daemon 状态
|
|
1649
1839
|
upgrade 升级到最新版本
|
|
1650
1840
|
dashboard 打印新的 Web Dashboard 一次性登录 URL(旧 token 同时失效)
|