botmux 3.29.0 → 3.31.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 CHANGED
@@ -50,7 +50,7 @@ botmux start # start the daemon (botmux autostart enable for aut
50
50
  >
51
51
  > Stable macOS CLI releases use a consistent Apple Developer ID signature. Replacing the binary during an upgrade therefore preserves the code identity used by macOS file and App Data permissions instead of appearing as a new program for every version. Canary, beta, and RC builds remain ad-hoc signed.
52
52
  >
53
- > To upgrade: `botmux upgrade` (replaces the binary in place), or just **re-run the curl command** — also an in-place upgrade, and it won't append a second PATH line.
53
+ > To upgrade: **always re-run the curl command above** (this also upgrades npm/pnpm global installs — in-place replacement, no second PATH line), then open a new terminal and run `botmux restart`; on ≥3.18 binary installs `botmux upgrade` is equivalent. To install a pinned version (rollbacks included): `curl -fsSL https://raw.githubusercontent.com/deepcoldy/botmux/master/install.sh | BOTMUX_VERSION=v3.18.8 sh` (the variable must precede the `sh` on the right side of the pipe). ⚠️ **Do not npm-upgrade from releases older than v3.18.0** — crossing the Node-sources → binary form boundary leaves the daemon unable to restart.
54
54
 
55
55
  <details>
56
56
  <summary>Already living in the Node ecosystem? npm works too (same binary)</summary>
@@ -59,9 +59,9 @@ botmux start # start the daemon (botmux autostart enable for aut
59
59
  npm install -g botmux # requires Node >= 22 to run the install itself
60
60
  ```
61
61
 
62
- The npm package carries **the same self-contained binary** (only the one matching your os/arch is installed); its postinstall points `~/.botmux/bin/botmux` at it and writes PATH the same way. So you end up with exactly **one** botmux version — no more "two Node versions each carrying their own global botmux, fighting each other / no idea which one I just updated".
62
+ The npm package carries **the same self-contained binary** (only the one matching your os/arch is installed). The package's `bin` points at a shipped sh launcher that the package manager links onto PATH itself, so **npm, pnpm and bun all work without any lifecycle script** (pnpm 10/11 and bun skip dependency postinstalls by default, which used to leave those installs with no `botmux` command at all). The postinstall still points `~/.botmux/bin/botmux` at the same binary and writes PATH. So you still end up with exactly **one** botmux **version** — both entry points exec the same binary — and no more "two Node versions each carrying their own global botmux, fighting each other / no idea which one I just updated".
63
63
 
64
- The only difference is **who installs it and who upgrades it later**: the npm path needs Node ≥ 22 to run the install itself and hands upgrades back to `npm i -g botmux@latest`; the curl path never touches Node. Once running, the two are identical — same binary, same commands.
64
+ The only difference is **who installs it**: the npm path needs Node ≥ 22 to run the install itself, the curl path never touches Node; **upgrades always re-run the curl command, no matter how you installed** (npm-upgrading from a pre-v3.18.0 install across that form boundary can leave the daemon unable to come back). Once running, the two are identical — same binary, same commands.
65
65
 
66
66
  </details>
67
67
 
package/README.md CHANGED
@@ -50,7 +50,7 @@ botmux start # 启动 daemon(botmux autostart enable 设开机
50
50
  >
51
51
  > 正式版 macOS CLI 使用稳定的 Apple Developer ID 签名。升级替换二进制后,macOS 的文件与 App 数据访问授权仍绑定同一代码身份,不会因为版本哈希变化而把 botmux 当成一个新程序;canary / beta / rc 等预览版仍使用 ad-hoc 签名。
52
52
  >
53
- > 升级:`botmux upgrade`(原地换二进制),或**重跑一遍上面那条 curl 命令**——同样原地升级,不会重复往启动文件里追加 PATH。
53
+ > 升级:**一律重跑上面那条 curl 命令**(npm / pnpm 全局安装也用它,原地替换、不会重复往启动文件里追加 PATH),装完开个新终端跑 `botmux restart`;≥3.18 的二进制安装上 `botmux upgrade` 与其等价。装指定版本(含回滚):`curl -fsSL https://raw.githubusercontent.com/deepcoldy/botmux/master/install.sh | BOTMUX_VERSION=v3.18.8 sh`(变量必须在管道右侧的 `sh` 前面)。⚠️ **v3.18.0 之前的老版本不要用 npm 升级**——跨「Node 源码 → 二进制」形态边界会让 daemon 重启失败。
54
54
 
55
55
  <details>
56
56
  <summary>已经在用 Node 生态?也可以走 npm(同一个二进制)</summary>
@@ -59,9 +59,9 @@ botmux start # 启动 daemon(botmux autostart enable 设开机
59
59
  npm install -g botmux # 需要 Node >= 22 装包本身
60
60
  ```
61
61
 
62
- npm 包内带的是**同一个自包含二进制**(按 os/arch 只装匹配的那一个),postinstall 把 `~/.botmux/bin/botmux` 指向它并同样写 PATH。所以装完只有**一个** botmux 版本,不再出现「装了两个 Node 版本、各自带一份全局 botmux 互相打架 / 不知道更新了哪个」。
62
+ npm 包内带的是**同一个自包含二进制**(按 os/arch 只装匹配的那一个)。包的 `bin` 指向随包发布的 sh 启动器,由包管理器自己链到 PATH——**npm / pnpm / bun 三种装法都不依赖生命周期脚本**(pnpm 10/11 与 bun 默认不跑依赖的 postinstall,早期版本因此装完没有任何 `botmux` 命令)。postinstall 仍会把 `~/.botmux/bin/botmux` 指向同一个二进制并写 PATH。所以装完始终只有**一个** botmux **版本**(两个入口都 exec 同一个二进制),不再出现「装了两个 Node 版本、各自带一份全局 botmux 互相打架 / 不知道更新了哪个」。
63
63
 
64
- 区别只在**谁来装、以后谁来升**:npm 路径需要 Node ≥ 22 才能执行安装本身,升级交回 `npm i -g botmux@latest`;curl 路径全程不碰 Node。跑起来之后两者完全一致——同样的二进制、同样的命令。
64
+ 区别只在**谁来装**:npm 路径需要 Node ≥ 22 才能执行安装本身,curl 路径全程不碰 Node;**无论哪种装法,升级都重跑 curl**(v3.18.0 之前的老版本用 npm 跨形态升级会让 daemon 起不回来)。跑起来之后两者完全一致——同样的二进制、同样的命令。
65
65
 
66
66
  </details>
67
67
 
@@ -71,6 +71,7 @@ npm 包内带的是**同一个自包含二进制**(按 os/arch 只装匹配的
71
71
 
72
72
  - **[实时流式卡片](https://deepcoldy.github.io/botmux/cards)** — 每轮对话一张实时刷新的卡片,终端画面原样截图回传;一键显示/隐藏输出、翻屏、重启/关闭/接管会话。
73
73
  - **[多机器人协作](https://deepcoldy.github.io/botmux/multi-bot)** — 同群多 bot @mention 路由,不同 CLI 背后不同模型,天然多样性;方案评审 / 代码 review / 技术选型让它们互相挑刺。
74
+ - **[群内真人独立 lane](docs/principal-lanes.md)** — 可复用 Dashboard 的「跨身份打断隔离(XPI)」开关,让同一群里的真人各用独立 CLI 上下文和 git worktree;消息仍公开可见,引用别人的任务仍可走建议/确认协作。
74
75
  - **[多话题并行编排](https://deepcoldy.github.io/botmux/multi-topic)** — 给编排者一个大任务,它自动在群里种话题、拉各 bot 起独立会话跑流水线,飞书任务面板一眼看完所有子任务进度。
75
76
  - **[可交互 Web 终端](https://deepcoldy.github.io/botmux/web-terminal)** — 不只是看输出,浏览器 / 手机直接操作 CLI,移动端带悬浮快捷键栏(Esc、Ctrl+C、方向键)。
76
77
  - **[会话接入 & 接力](https://deepcoldy.github.io/botmux/adopt)** — 本地 tmux 里跑到一半,手机 `/adopt` 接管;`/relay` 把整个会话(原进程、原记忆)搬进团队群继续。
@@ -79,6 +80,20 @@ npm 包内带的是**同一个自包含二进制**(按 os/arch 只装匹配的
79
80
 
80
81
  更多:[角色与团队](https://deepcoldy.github.io/botmux/roles) · [文件沙盒](https://deepcoldy.github.io/botmux/sandbox) · [Dashboard 管控面](https://deepcoldy.github.io/botmux/dashboard) · [tmux 会话常驻](https://deepcoldy.github.io/botmux/tmux) · [飞书会议智能体(效果展示)](https://bytedance.larkoffice.com/wiki/UBOXwH01CixfxfkqxUpcKgvQnsg)。
81
82
 
83
+ ## 按 Bot 设置零注入
84
+
85
+ 在 Dashboard → 自定义中心 →「按 Bot 设置零注入」勾选一个或多个 bot,批量开启或恢复原配置;支持搜索与全选当前结果,每个 bot 独立保存,失败项会保留勾选供重试。也可在对应 `bots.json` 条目设置 `"promptInjection": "none"`。适合 lead-bot 派活、sub-bot 专注执行的场景。`default` / 删除字段恢复原行为,既有 prompt、角色和技能配置不会被清空。
86
+
87
+ 此模式只传任务正文与附件信息,跳过 botmux 的系统提示、逐轮提醒、身份信封、角色、白板、记忆与技能目录;自动从 CLI 转写回传最终回答。使用新会话验证:已经进入历史的提示无法撤回,CLI 原生系统提示、项目 `AGENTS.md` 及用户自行安装的技能仍由 CLI 管理。
88
+
89
+ 支持范围复用 CLI 的最终回复兜底采集能力:目前包括 Claude Code、Codex、TraeX、CoCo、Hermes、MTR、Pi、Oh My Pi、ebsd、Grok(PTY、tmux 等本地后端,包含 Codex / TraeX RPC 输入)。暂不支持远端后端和 v3 workflow;仅 adopt 能采集 final 的 CLI 不作为整 bot 开关的支持依据。CLI 的共享技能目录若存在全局安装的 `botmux-*` 技能,会拒绝启动以免假称零注入;请使用按会话的技能注入方式或独立 home,不会删除其它 bot 共用的文件。
90
+
91
+ 普通协作可直接在群内 @ 执行 bot,最终回答自动回复当前会话,并默认 @ 本轮任务的发起人(真人或 bot)。收件人取自宿主记录的本轮身份,排队和重试不借用后续轮次的发送者;缺少可用身份时不猜测会话 owner,也不注入额外 prompt。已经 @ 发起 bot 的回复本身就是回报,不再额外通过 HTTP 重复追加一轮任务。
92
+
93
+ 零注入会话也默认回传 Web 终端中的最终回答:`/rewind` 重跑和随后直接输入的多轮对话,都沿用最新飞书发起人的回复位置和 @,以普通回复卡发送,不附加终端来源或复述输入。每个终端回合开始时固定回复对象,新飞书消息只影响之后开始的回合;`/model` 等设置命令、菜单输出和已有历史不回传。该行为复用原生转写监听,不增加开关或 prompt;普通模式和 `/adopt` 保持原行为。
94
+
95
+ 没有通过 @ 发起 bot 回报时,已建立签名回报绑定的 `botmux dispatch --bot-app <稳定 App ID>` 派单仍可自动回报原 lead:sub 的 daemon 根据绑定去掉自动附加的 report/send 指令,每轮最终回答回报一次,不自动判定任务完成。回报使用独立重试和持久化去重回执,失败不影响子会话回复;lead 正常在原会话回复,不进入 HTTP 结果轮询。提交结果不明时拒绝自动重放。`dispatch --into` 向此前没有派单记录的普通消息追加任务不会创建该绑定,仍会保留完成指令,不能据此验证零注入。
96
+
82
97
  ## 支持的 CLI / Agent
83
98
 
84
99
  `bots.json` 里用 `cliId` 一键切换。**20+ 适配器**,覆盖本地 CLI(进程隔离,`tmux attach` 可直连)和 API / 云 Agent(如 Mira、riff——通过 API / 远端接入,非本地进程;mojo 为 API 驱动、默认在宿主机执行工具,可配 cloud: true 走云沙箱)。代表项:
package/package.json CHANGED
@@ -1,9 +1,13 @@
1
1
  {
2
2
  "name": "botmux",
3
- "version": "3.29.0",
3
+ "version": "3.31.0",
4
4
  "description": "Bridge between IM platforms and AI coding CLIs — one topic, one CLI session with live streaming",
5
5
  "type": "module",
6
+ "bin": {
7
+ "botmux": "scripts/botmux-launcher.sh"
8
+ },
6
9
  "files": [
10
+ "scripts/botmux-launcher.sh",
7
11
  "scripts/postinstall-bin.mjs",
8
12
  "scripts/install-path-entry.mjs",
9
13
  "README.md",
@@ -15,7 +19,7 @@
15
19
  "access": "public"
16
20
  },
17
21
  "scripts": {
18
- "build": "bun run audit:domains && node scripts/generate-app-icon-data.mjs && node scripts/generate-pi-initial-prompt-extension.mjs && node scripts/clean-dist.mjs && tsc && bun run typecheck:scripts && bun run typecheck:test-mocks && node scripts/generate-runtime-build-id.mjs && cp src/setup/lark-scopes.json dist/setup/ && bun run dashboard:bundle && chmod +x dist/cli.js && node scripts/audit-dist.mjs && node scripts/audit-embedded-assets.mjs --require-dist",
22
+ "build": "bun run audit:domains && node scripts/generate-app-icon-data.mjs && node scripts/generate-pi-initial-prompt-extension.mjs && node scripts/generate-pi-turn-boundary-extension.mjs && node scripts/clean-dist.mjs && tsc && bun run typecheck:scripts && bun run typecheck:test-mocks && node scripts/generate-runtime-build-id.mjs && cp src/setup/lark-scopes.json dist/setup/ && bun run dashboard:bundle && chmod +x dist/cli.js && node scripts/audit-dist.mjs && node scripts/audit-embedded-assets.mjs --require-dist",
19
23
  "verify:binary": "bun scripts/verify-binary.mjs",
20
24
  "typecheck:scripts": "tsc -p tsconfig.scripts.json",
21
25
  "typecheck:test-mocks": "tsc -p tsconfig.test-mocks.json",
@@ -82,12 +86,12 @@
82
86
  },
83
87
  "optionalDependencies": {
84
88
  "@napi-rs/canvas": "^0.1.65",
85
- "botmux-darwin-arm64": "3.29.0",
86
- "botmux-darwin-x64": "3.29.0",
87
- "botmux-linux-arm64": "3.29.0",
88
- "botmux-linux-arm64-musl": "3.29.0",
89
- "botmux-linux-x64": "3.29.0",
90
- "botmux-linux-x64-musl": "3.29.0"
89
+ "botmux-darwin-arm64": "3.31.0",
90
+ "botmux-darwin-x64": "3.31.0",
91
+ "botmux-linux-arm64": "3.31.0",
92
+ "botmux-linux-arm64-musl": "3.31.0",
93
+ "botmux-linux-x64": "3.31.0",
94
+ "botmux-linux-x64-musl": "3.31.0"
91
95
  },
92
96
  "devDependencies": {
93
97
  "@midscene/web": "^1.7.6",
@@ -0,0 +1,120 @@
1
+ #!/bin/sh
2
+ # botmux launcher — the `bin` entry npm/pnpm/bun link onto PATH.
3
+ #
4
+ # ── WHY THIS FILE EXISTS ───────────────────────────────────────────────────────
5
+ # The main package ships NO binary (0.1MB, 6 files): the compiled single-file
6
+ # executable lives in a platform subpackage (`botmux-linux-x64`, …) pulled in as
7
+ # an optional dependency. `bin` can only point INSIDE the main package, so it
8
+ # cannot name that binary directly — hence this dispatcher, which resolves the
9
+ # subpackage at RUN time and `exec`s it.
10
+ #
11
+ # Until now the only thing that produced a runnable `botmux` was
12
+ # `scripts/postinstall-bin.mjs`, which writes ~/.botmux/bin/botmux. That still
13
+ # runs and is still what gives multi-Node-version boxes ONE unambiguous global
14
+ # botmux. But postinstall is not guaranteed to run:
15
+ #
16
+ # MEASURED on v3.18.13, isolated HOME, each manager's own global install:
17
+ # npm i -g botmux → postinstall runs → launcher written → `botmux` works
18
+ # bun add -g botmux → "Blocked 2 postinstalls" → NO launcher → no command
19
+ # pnpm add -g botmux → script not run at all → NO launcher → no command
20
+ #
21
+ # Both failures exit 0 and print "installed botmux@3.18.13" / "+ botmux 3.18.13",
22
+ # so the user believes it worked and only finds out at `botmux: command not found`.
23
+ # bun can be rescued with `bun pm -g trust botmux`; pnpm could NOT be rescued —
24
+ # `onlyBuiltDependencies: [botmux]` still produced no launcher (measured).
25
+ #
26
+ # With a `bin` entry the package manager links this script itself, so all three
27
+ # get a working command with no lifecycle script at all. postinstall then becomes
28
+ # an OPTIMISATION (the single canonical launcher) rather than a prerequisite.
29
+ #
30
+ # ── WHY sh AND NOT node ────────────────────────────────────────────────────────
31
+ # A Node dispatcher would reintroduce a Node dependency for a package whose whole
32
+ # point is a self-contained binary, and would add interpreter startup to every
33
+ # invocation. `bin` pointing at a shell script works on npm, pnpm and bun alike
34
+ # (measured, all three link it and run it). `exec` replaces this process, so the
35
+ # binary receives the original argv and signals with no shell left in between.
36
+ #
37
+ # Windows is not handled here: there are no win32 platform subpackages at all
38
+ # (only darwin/linux × x64/arm64 ± musl), so there is nothing for a .cmd shim to
39
+ # dispatch to. WSL2 reports as linux and is the supported route.
40
+
41
+ set -e
42
+
43
+ # Resolve this script through symlinks, then take the PACKAGE root.
44
+ #
45
+ # Two things make this fiddly, both MEASURED:
46
+ # · `bin` entries are linked into the manager's bin dir, so $0 is often a
47
+ # symlink — but pnpm instead generates a wrapper that runs `/bin/sh <path>`,
48
+ # where <path> goes through a symlinked package dir. Either way the honest
49
+ # answer comes from resolving the path and then `cd -P`.
50
+ # · This file lives in `<pkg>/scripts/`, so the package root is the PARENT of
51
+ # this script's directory. Using the script's own dir made every sibling
52
+ # lookup miss by one level.
53
+ target=$0
54
+ # Bounded: a symlink cycle would otherwise spin here forever.
55
+ i=0
56
+ while [ -L "$target" ] && [ "$i" -lt 40 ]; do
57
+ link=$(readlink "$target")
58
+ case $link in
59
+ /*) target=$link ;;
60
+ *) target=$(dirname "$target")/$link ;;
61
+ esac
62
+ i=$((i + 1))
63
+ done
64
+ # `cd -P` resolves any symlinked parent directory (pnpm's store links).
65
+ pkg=$(cd "$(dirname "$target")/.." && pwd -P)
66
+
67
+ # musl (Alpine and most slim images): npm/pnpm/bun select the -musl subpackage via
68
+ # each subpackage's `libc` field, so we must look for that same name. Positive
69
+ # evidence only — never claim musl on a glibc box.
70
+ libc=''
71
+ if [ "$(uname -s)" = Linux ]; then
72
+ # ⚠️ ONE `ls` PER DIRECTORY. `ls a/glob b/glob` exits non-zero when EITHER
73
+ # glob misses, i.e. it is an AND — and on real Alpine the loader lives only in
74
+ # /lib, so the combined form NEVER fired and only /etc/alpine-release saved it
75
+ # (MEASURED on alpine:3.20: combined → exit 1, per-dir OR → exit 0). That left
76
+ # a musl box without that file resolving to the glibc subpackage name. Same
77
+ # per-directory shape as install.sh, postinstall-bin.mjs and binary-self-update.ts.
78
+ if ls /lib/ld-musl-* >/dev/null 2>&1 \
79
+ || ls /usr/lib/ld-musl-* >/dev/null 2>&1 \
80
+ || [ -f /etc/alpine-release ]; then
81
+ libc='-musl'
82
+ fi
83
+ fi
84
+
85
+ case $(uname -s) in
86
+ Linux) os=linux ;;
87
+ Darwin) os=darwin ;;
88
+ *) os='' ;;
89
+ esac
90
+ case $(uname -m) in
91
+ x86_64|amd64) arch=x64 ;;
92
+ aarch64|arm64) arch=arm64 ;;
93
+ *) arch='' ;;
94
+ esac
95
+
96
+ if [ -z "$os" ] || [ -z "$arch" ]; then
97
+ echo "botmux: unsupported platform $(uname -s)/$(uname -m)." >&2
98
+ echo "Supported: linux-x64, linux-arm64, darwin-x64, darwin-arm64. On Windows use WSL2." >&2
99
+ exit 1
100
+ fi
101
+
102
+ sub="botmux-$os-$arch$libc"
103
+
104
+ # Two layouts, both MEASURED against real global installs of v3.18.13:
105
+ # npm → <main>/node_modules/botmux-linux-x64/botmux (nested)
106
+ # pnpm, bun → <main>/../botmux-linux-x64/botmux (sibling / hoisted)
107
+ # Try the exact-libc name first, then the other libc as a fallback: a mirrored or
108
+ # repacked registry can drop the `libc` field, leaving the "wrong" one installed.
109
+ for name in "$sub" "botmux-$os-$arch"; do
110
+ for candidate in "$pkg/node_modules/$name/botmux" "$pkg/../$name/botmux"; do
111
+ if [ -x "$candidate" ]; then
112
+ exec "$candidate" "$@"
113
+ fi
114
+ done
115
+ done
116
+
117
+ echo "botmux: no platform binary found for $sub." >&2
118
+ echo "The optional platform package did not install. Reinstall with:" >&2
119
+ echo " npm i -g botmux --force (or: pnpm add -g botmux / bun add -g botmux)" >&2
120
+ exit 1
@@ -2,10 +2,12 @@
2
2
  * Put `~/.botmux/bin` on the user's PATH by writing their shell's startup file.
3
3
  *
4
4
  * WHY THIS EXISTS
5
- * `npm i -g botmux` has no `bin` field (removed with the Node fallback — see
6
- * postinstall-bin.mjs), so the ONLY `botmux` command is the launcher written to
7
- * `~/.botmux/bin/botmux`. If that directory is not on PATH, a successful install
8
- * still leaves the user with `botmux: command not found`.
5
+ * The package's `bin` is a sh launcher the package manager links onto PATH, but
6
+ * that only covers installs done BY a package manager: `install.sh` (the curl
7
+ * route) has no manager involved, and the postinstall launcher at
8
+ * `~/.botmux/bin/botmux` is what gives a multi-Node-version box ONE unambiguous
9
+ * global botmux. If that directory is not on PATH, those routes still leave the
10
+ * user with `botmux: command not found` — which is what this file prevents.
9
11
  *
10
12
  * Both installers used to just PRINT a hint, and the hint was:
11
13
  *
@@ -330,8 +330,9 @@ try {
330
330
  // This used to only PRINT `echo 'export PATH=…' >> ~/.profile`. Two problems:
331
331
  // the user had to act on it, and for zsh users the suggested file is WRONG —
332
332
  // zsh never reads ~/.profile (measured), so following the hint verbatim left
333
- // `botmux` still not found. There is no `bin` field any more, so PATH is the
334
- // only way this launcher becomes a command; we now write the right startup file
333
+ // `botmux` still not found. PATH is the only way THIS launcher becomes a
334
+ // command (the package's own `bin` is linked by the package manager and is a
335
+ // separate entry point); we now write the right startup file
335
336
  // for the user's actual shell (bash/zsh/fish/other) and tell them what we did.
336
337
  //
337
338
  // ⚠️ DO NOT GATE THIS ON `process.env.PATH`. It used to be wrapped in