cli 教程
本教程介绍如何在 MoonBit 之外用 cli 可执行程序计算分数快照:从 shell、像索引构建器那样从 Python,以及从 Node.js。你需要本仓库的检出、MoonBit 工具链以及 Node.js 20.16、22.3 或更高版本。
快速开始
在仓库根目录构建可执行程序:
moon build src/cli --target js
把一个包的信号写入文件:
cat > payload.json <<'JSON'
{"dependents": 20, "recent_dependents": 4, "downloads": 300,
"days_since_release": 40, "historical_dependents": 10,
"historical_recent_dependents": 2, "historical_downloads": 0,
"historical_days_since_release": 10}
JSON
node _build/js/debug/build/cli/cli.js score-snapshot --input payload.json
命令在一行中输出快照:
{"score":301.7852882193072,"score_30d_ago":135.2764584196223,"score_growth_30d":166.5088297996849,"score_growth_ratio_30d":1.2308780976744749,"rank_label":"S","momentum_label":"Rising","activity_multiplier":1.06}
这些数字与 MoonBit 中 @score.compute_score_snapshot(20, 4, 300, 40, 10, 2, 0, 10) 的结果相同。
日常任务
省略未知的信息
每个键都是可选的,默认为 0。一个五天前发布、有三个依赖方、其余信息未知的包:
echo '{"dependents": 3, "days_since_release": 5}' > new.json
node _build/js/debug/build/cli/cli.js score-snapshot --input new.json
{"score":59.00068800926255,"score_30d_ago":0,"score_growth_30d":59.00068800926255,"score_growth_ratio_30d":1,"rank_label":"C","momentum_label":"Stable","activity_multiplier":1.12}
历史信号全为 0,所以 score_30d_ago 为 0,增长率报告为 1。
从 Python 调用
索引构建器为每个包运行一次该命令。其辅助函数的最小版本如下:
import json, os, subprocess, tempfile
CLI = "_build/js/debug/build/cli/cli.js"
def score_snapshot(signals: dict) -> dict:
with tempfile.NamedTemporaryFile("w", suffix=".json", delete=False) as f:
json.dump(signals, f)
path = f.name
try:
done = subprocess.run(
["node", CLI, "score-snapshot", "--input", path],
check=True, capture_output=True, text=True,
)
finally:
os.unlink(path)
return json.loads(done.stdout)
print(score_snapshot({"dependents": 20, "recent_dependents": 4,
"downloads": 300, "days_since_release": 40})["rank_label"])
S
check=True 会把每个非零退出状态变成异常,这同时涵盖了 JSON 错误消息和输入文件缺失的情况。
从 Node.js 调用
import { execFileSync } from "node:child_process";
import { writeFileSync } from "node:fs";
writeFileSync("p.json", JSON.stringify({ dependents: 936, days_since_release: 120 }));
const out = execFileSync("node", [
"_build/js/debug/build/cli/cli.js", "score-snapshot", "--input", "p.json",
]);
console.log(JSON.parse(out).rank_label);
S
当乘数为 时,仅凭 936 个依赖方就足以达到等级 S;score 设计推导了原因。
处理错误
错误会输出一个带 error 键的 JSON 对象,并以状态 1 退出:
echo 'not json' > bad.json
node _build/js/debug/build/cli/cli.js score-snapshot --input bad.json; echo "status $?"
{"error":"Failed to parse JSON input"}
status 1
解析输出之前先检查退出状态。文件缺失时同样以 1 退出,但会向标准错误输出 Node.js 堆栈跟踪,而不是 JSON。
进阶
- 批量处理。 每次调用都会启动一个 Node.js 进程,其开销远大于计算分数本身。在一个 MoonBit 程序中处理大量包时,请像 score 教程那样直接调用
@score.compute_score_snapshot。 - 信号从哪里来。 架构指南解释了索引构建器在调用此命令之前如何统计依赖方、近期依赖方和历史值。
- 严格的输入。 该命令接受不规范的输入:字符串和
null变为0,小数被截断。如果一个悄无声息的0会掩盖 bug,请在调用方验证负载。
常见陷阱
- 数字写成字符串。
{"dependents": "20"}会按零个依赖方评分。请写20,而不是"20"。 - 参数顺序错误。
score-snapshot必须是脚本之后的第一个参数;--input file.json score-snapshot会输出用法错误。 - 构建过期。 索引构建器会复用已有的
cli.js。修改src/score后请重新构建,否则数据库会按旧规则评分。 - 过大的计数。
2147483647或任何更大的数(转换会饱和到最大的Int)会使分数变为NaN。JSON 没有NaN,因此输出中本应是数字的位置会出现字符串"NaN":{"score":"NaN",...}。