想让 MiniMax Code CLI 在 CI 流水线或定时任务里跑起来,第一件事不是写脚本,而是确认它有没有“一次性执行”入口:给一段任务文本,跑完输出结果并自己退出,中途不弹出需要人按键的界面。判断依据在你的本地帮助文本里能找到,比如是否存在把任务作为参数直接传入、并把结果打到标准输出而不是进入交互界面的选项。如果帮助里翻不到这类入口,当前版本通常只能当手动工具用,硬塞进无人值守脚本会在某个确认提示处停住,直到超时被杀。
先在终端执行本机 CLI 的帮助命令,检索一次性执行、非交互、跳过确认相关的关键词,确认是否存在无需人工输入即可跑到结束的调用方式。若存在,用最小脚本传入任务并分别落盘标准输出、标准错误和退出码,先手动跑一遍观察是否出现等待输入的提示。只有退出码、目标产物变化、日志无错误行三项同时正常,才把它接进定时任务;否则降级为有人值守运行,或改用其他可控入口。
在帮助输出里找有没有一次性执行入口
先跑本机 CLI 的顶层帮助和子命令帮助,不同版本把这类信息放在不同层级,顶层看不出不代表子命令里没有。建议把帮助文本落盘后再检索,避免翻屏漏看:
# 具体可执行名以本机安装结果为准
code-cli `--help` > help.txt 2>&1
code-cli <子命令> `--help` >> help.txt 2>&1
grep -inE 'exec|run|prompt|print|batch|quiet|yes|force|non-interactive|stdin' help.txt
看关键词时关注三类语义:一是“把任务当参数传入”,通常表现为接受一段 prompt 文本;二是“结果走标准输出而不是进入界面”,常见形式是 print、quiet 一类的选项;三是“跳过确认或假定同意”,常见形式是 yes、force、assume-yes 一类。参数名各版本不一定相同,必须以本机 help 输出为准,不要照抄别处的写法。
如果检索结果为空,或者帮助里只有启动交互界面、打开编辑器、登录、配置这几项,就要明确承认:这个版本目前没有可用的非交互入口。此时把它写进 cron 或 CI 只能得到“挂住”的结果,处理方式要么是继续人工运行,要么换成能接受文件输入、能返回状态码的调用路径。
写一个最小脚本跑一次无人值守任务
确认存在一次性执行入口后,先写一个最小骨架验证“没人按键也能跑到结束”。骨架做四件事:切到固定工作目录、从文件读入任务文本、把标准输出和标准错误分开落盘、把退出码单独记下来。下面用占位符表示参数,替换成你本地帮助里真实存在的写法:
#!/usr/bin/env bash
set -uo pipefail
WORKDIR=/srv/agent/work
LOGDIR=/srv/agent/logs
TASK_FILE=/srv/agent/task.txt
CLI=code-cli
mkdir -p "$LOGDIR"
cd "$WORKDIR" || exit 2
STAMP=$(date +%Y%m%d-%H%M%S)
OUT="$LOGDIR/run-$STAMP.out"
ERR="$LOGDIR/run-$STAMP.err"
"$CLI" <一次性执行选项> "$(cat "$TASK_FILE")" \
>"$OUT" 2>"$ERR"
RC=$?
{
echo "time=$STAMP rc=$RC cwd=$WORKDIR"
echo "stdout_lines=$(wc -l < "$OUT")"
echo "stderr_lines=$(wc -l < "$ERR")"
} | tee "$LOGDIR/run-$STAMP.meta"
exit $RC
注意这里没用 set -e,因为你需要拿到真实退出码而不是被脚本自己提前中断;命令失败也要继续把元信息写完。第一次运行建议放在测试目录而不是正式仓库,运行结束后对照 .out 和 .err 看进程是正常结束还是被挂起后杀掉。如果 .out 长期没有新内容、.err 里出现等待输入、选择确认之类的字符,说明这个调用方式并不适合直接无人值守。
处理会中断脚本的确认环节
应对办法不是先猜参数,而是先手动跑一次,把确认提示的形态和触发条件看清楚:是在读写文件前问 y/N,是要在几个选项里选择一个,还是要求回车确认路径。把这些提示出现的时机记下来,再判断能否绕开。
- 优先找官方提供的跳过确认或假定同意的选项,这是最稳的路径,参数名以本机帮助为准。
- 如果没有这类选项,可以试着把标准输入指向空文件,观察它是报错退出、直接取消,还是当成默认值继续。这一步必须看退出码,退出码为 0 不代表任务真的做完了。
- 用 yes 之类的方式喂固定回答,只在提示是固定 y/N 且语义明确时考虑;一旦提示变成选择列表或路径确认,喂进去的字符很容易被当成非法输入。
- 用伪终端工具包裹交互界面属于兜底手段,它依赖提示文本的精确匹配,CLI 升级或文案调整就会失效,需要连同版本号一起固定并定期回归。
这里不给出具体开关名,因为不同版本差异较大,写死一个名字反而容易误导。你能核验的判断标准只有一条:把标准输入关掉之后,进程是否仍能自己走到结束。
用退出码和产物一起判断成功失败
退出码为 0 只说明进程自己认为正常退出了,工具在“什么都没做”的情况下也可能返回 0。判断条件建议三条一起看,缺一条都可能放过空跑:
- 退出码:非 0 一律视为失败并进入告警,不要用 || true 吞掉。
- 目标产物是否变化:记录运行前后目标文件的修改时间或校验和,没有变化就要追查任务是否被静默跳过。
- 日志里的错误行:在 .err 和 .out 里检索 error、failed、denied、timeout 这类词,同时检查输出是否为空,空输出按异常处理。
before=$(md5sum "$WORKDIR/target.md" 2>/dev/null | cut -d' ' -f1)
# 运行 CLI 后
fter=$(md5sum "$WORKDIR/target.md" 2>/dev/null | cut -d' ' -f1)
[ "$RC" -eq 0 ] || exit 1
[ "$before" != "$after" ] || exit 1
grep -qiE 'error|failed|denied' "$OUT" "$ERR" && exit 1
这段判断可以放在脚本末尾统一执行,也可以单独的巡检任务每天比对一次历史日志。之前运行正常不代表现在仍然正常,涉及文件写入的任务每次都要核对产物。
给脚本加上权限与时间限制
无人值守时没有人在旁边叫停,所以要把最坏情况提前框住。三类措施各有代价,按你的场景取舍:
- 执行超时:用 timeout 之类的方式给整个调用设上限,超时后按失败处理并保留日志。代价是进程被强制终止,可能留下只写了一半的产物,需要额外做清理或把输出写到临时目录再原子替换。
- 只读挂载或只给最小目录权限:让工作目录可写、其余路径只读,能减少误改范围。代价是工具若需要在其他位置写缓存或临时文件,会直接报错失败,需要先把这些路径确认清楚。
- 单独凭据隔离:给自动化任务单独申请一份凭据,只授予完成任务所需的最小范围,并与人工使用的凭据分开。代价是多了轮换和权限维护的工作量,凭据泄露时的影响范围也会更清晰。
这三项都落实之后,再把这个脚本放进定时任务或 CI,出现问题至少能从日志、退出码和凭据记录三处还原当时发生了什么。