唯一用途:构建 APK(或任何 Gradle 构建)时,让前端在会话流的折叠工具条标题处原地刷新看到构建进度条,
形如 <=====> 73% EXECUTING [1m 32s]。除此之外什么都不做。
- 🎯 只认
gradle/gradlew命令 - 📉 只抽进度那一行(剥掉 ANSI 后取最后一次匹配),变化才推
- 🔌 自建 WebSocket
/api/build-progress(官方registerUpgrade) - 🧠 自动注入一条运行时要求,让助手用能看到进度条的方式跑构建(见下)
- 🚫 不落日志、不留历史、不推 stdout/stderr 正文、不推命令原文
- 🪶 单文件、零依赖(宿主侧复用 DSH 自带的
ws)
配套的 Android 前端(dsh-aui 2.0.3)把它渲染到会话流里那条构建工具行的标题位置,原地刷新;
构建结束后不撤下,而是换成收尾行(✅ BUILD SUCCESSFUL in 2m 23s / ❌ BUILD FAILED in 1m 3s)留在原处。
# 1) 放插件文件
mkdir -p ~/.dsh/profiles/web/plugins/build-progress
curl -fsSL https://raw.githubusercontent.com/akvi0921/dsh-build-progress/main/index.js \
-o ~/.dsh/profiles/web/plugins/build-progress/index.js
# 国内网络可用镜像:https://ghfast.top/https://raw.githubusercontent.com/...
# 2) 追加组合行(保存即热生效;~/.dsh/profiles/web/cordis.patch.yml)
cat >> ~/.dsh/profiles/web/cordis.patch.yml <<'YAML'
- insert:
- id: build-progress
name: './plugins/build-progress/index.js'
config: { pollMs: 150 }
YAML自查(应能连上并收到 hello):
node -e "
const WS=require(require('path').join(process.env.HOME,'.dsh/profiles/node_modules/ws'));
const ws=new WS('ws://127.0.0.1:3080/api/build-progress',{headers:{Origin:'http://127.0.0.1:3080'}});
ws.on('message',m=>{console.log(String(m).slice(0,120));process.exit(0)});
ws.on('error',e=>{console.log('✗',e.message);process.exit(1)});"--console=plain 下 Gradle 根本不画进度条;而一旦输出被 > log.txt、| tail 接走,插件也就采不到。
这两件事都发生在"助手决定怎么跑命令"的那一刻,所以插件在挂载时通过官方 systemPrompt.context()
注册一条每轮都会并入上下文的片段(GUI 的「上下文注入」块里能看到):
- 构建必须这样跑:
TERM=xterm-256color script -q -f -c "gradle assembleDebug --console=rich" /dev/null - 不要重定向/接管道;要看尾部结果就等构建结束再单独
tail/cat
也就是说:装了这个插件,助手自己就会用能看到进度条的方式跑构建,不需要你每次叮嘱。
| 帧 | 字段 | 说明 |
|---|---|---|
hello |
builds:[{callId,sessionId,text,percent,phase,elapsed}] |
连上即发,只反映此刻在跑的构建(不是历史) |
build-progress |
callId,sessionId,text,percent,phase,elapsed |
进度行变化时推;text 如 <=====> 73% EXECUTING [1m 32s] |
build-done |
callId,sessionId,exitCode,ok,text,percent,elapsed |
构建结束;text 是收尾行(优先取 Gradle 自己的 BUILD SUCCESSFUL/FAILED in …),前端把它留在该行 |
callId 是官方 tool/call 事件的调用 id,前端靠它把进度画到对应的那一行工具上。
- 热插入的条目 Cordis 不调
apply(父 include 事务不 settle),所以插件在 import 时自举挂载: 钩住Context.prototype.extend/isolate/intercept取 root ctx,轮询到webServer+subprocess就位再挂。 - 声明式
inject = ['webServer','subprocess']是启动路径的正确姿势(否则 apply 早于服务注册)。 - 进度行要剥 ANSI 后取最后一次匹配:rich 控制台把
<===>拆成多段并夹带光标移动/擦除序列, 只处理\r会把重绘串成一行垃圾。 - 改插件内容想在本进程内立即生效:组合行的
name加个查询串换 URL(如index.js?v=3)—— ESM 按 URL 缓存。
- 只对 Gradle 类构建生效(其它工具的花式 spinner 不推)。
- 后端基址必须是本机回环(
127.0.0.1/localhost):通道只信任回环请求。 - 进度只在构建进行中显示,结束后换成一行收尾结论留在原处(前端内存里,应用重启/会话重载即清),不落盘、不进会话日志。
MIT