大家好,我是前端之虎陈随易,公众号 陈随易,个人网站:https://chensuiyi.me。

最近研究 MoonBit 生态的时候,发现了一个很有意思的东西:skills.mooncakes.io。
这玩意儿不是博客,也不是 App 商店,而是 MoonBit 的 技能市场。
一句话说清楚:你写好一个 MoonBit 包,往 mooncakes.io 上一扔,别人一行命令就能跑起来,不用管你用了啥依赖、啥操作系统、怎么编译。
今天就用几个真实例子,聊聊它到底怎么写、怎么用、有啥用,跟传统包管理比起来到底新在哪。
它是个啥?举个例子
假如我写了个小工具,功能很简单:把 JSON 文件里的字段按字母顺序排个序。
要是在以前,我想把这个工具分享给别人,通常得这么折腾:
- 把源码发过去。
- 告诉他怎么装依赖。
- 教他怎么编译。
- 然后帮他解决各种环境报错。
说实话,挺折腾的。
而 skills.mooncakes.io 做的事情就是:你上传一个支持 Wasm 后端的 MoonBit 模块,平台会自动把它构建成经过 wasm-opt 优化的 Wasm 二进制。别人只需要一行命令:
bash
moon runwasm 你的用户名/json-sort@0.1.0 < input.json
就能直接跑起来。不需要装 Node、Python 或者其他环境,只要有 MoonBit 工具链就行。
说白了,就是把 代码 直接变成 可执行能力。
都能干点啥?看几个真实例子
skills.mooncakes.io 上已经有一些可以直接运行的技能,比如:
Yoorkin/cowsay:在终端生成 cowsay 图案。moonbit-community/embed:把静态文件嵌入成 MoonBit 源文件。moonbit-community/moongrep:结构化的 MoonBit 代码搜索工具。moonbitlang/yacc:LR(1) 解析器生成器。moonbitlang/parser/cmd/moonfmt:MoonBit 代码格式化工具。
这些功能本身都不复杂,但以前要用它们,往往得装各种不同的工具、依赖和环境。
现在,统一的命令就能把它们跑起来:
bash
moon runwasm 作者名/包名@版本号
比如:
bash
moon runwasm Yoorkin/cowsay@0.1.2 -- hello
moon runwasm moonbit-community/embed@0.1.1 ./fixtures -o fixtures_bundle.mbt
moon runwasm moonbit-community/moongrep@0.1.11 -- scan --rules path/to/rules path/to/src
moon runwasm moonbitlang/yacc@0.7.17 -- --help
moon runwasm moonbitlang/parser/cmd/moonfmt@0.3.7
每个技能都是独立的、可执行的、可复用的。
一个技能项目长啥样?
光说不练假把式,咱直接看个最小项目。
假设项目叫 json-sort/,目录结构如下:
bash
json-sort/
├── moon.mod
├── moon.pkg
├── main.mbt
└── SKILL.md
moon.mod 是模块配置文件,用 MoonBit 自己的 DSL 格式:
toml
name = "你的用户名/json-sort"
version = "0.1.0"
preferred_target = "wasm"
supported_targets = "wasm"
import {
"moonbit-community/miniio@0.2.0",
}
注意两个 target 字段的区别:
preferred_target 只是设置 moon build、moon check 等命令的默认后端,代表开发时的偏好。
supported_targets 才是真正限制模块支持哪些后端,省略它时默认支持所有后端。
如果模块和包都声明了,最终支持范围取两者的交集。
只有有效范围包含 wasm,mooncakes.io 才会尝试为这个包构建 Wasm 二进制,moon runwasm 才能运行。
moon.pkg 说明这是一个可执行包,声明只支持 Wasm 后端,并导入需要的依赖:
toml
import {
"moonbit-community/miniio",
}
options(
"is-main": true,
"supported-targets": "wasm",
)
main.mbt 是入口文件,读取标准输入的 JSON,排序字段后输出:
moonbit
fn main {
// 从标准输入读取 JSON
// 按字母顺序排序字段
// 输出结果
}
SKILL.md 是关键文件。它说明这个技能是什么、怎么调用、适合什么场景。平台会根据这个文件识别和展示你的技能。
比如:
markdown
name: 你的用户名/json-sort
description: 读取 JSON 文件,按字母顺序排序字段后输出。
json-sort
用于整理 JSON 文件字段顺序,方便代码审查和版本控制。
用法
bash
moon runwasm 你的用户名/json-sort@0.1.0 < input.json
示例
输入:
json
{ "b": 2, "a": 1 }
输出:
json
{ "a": 1, "b": 2 }
写好之后,用 moon publish 把模块上传到 mooncakes.io。
平台会自动检测到 SKILL.md,把它展示到 skills.mooncakes.io 上。
别人不需要知道你的源码在哪,也不需要手动编译,只需要运行:
bash
moon runwasm 你的用户名/json-sort@0.1.0 < input.json
就能看到结果。
怎么发布?怎么运行?
发布
步骤不复杂:
- 写一个 MoonBit 模块。
- 在项目中加上
SKILL.md。 - 确保可执行包声明
"is-main": true,并且有效支持的后端包含wasm(通过supported_targets显式声明,或者默认支持所有后端且代码确实能在 Wasm 下编译通过)。 - 执行
moon publish,把模块上传到 mooncakes.io。 - 平台会自动为支持 Wasm 后端的包构建 Wasm,并用 wasm-opt 优化,然后展示到
skills.mooncakes.io。
整个过程不用你自己手动编译 Wasm,也不用写复杂的发布脚本,上传之后,平台自动搞定。
运行
moon runwasm 目前是实验性命令(experimental),发布之后,别人只需要一行命令:
bash
moon runwasm 用户名/包名[@版本号] [参数]
版本号可以省略,省略时从 registry 解析最新版本(已经下载过的会优先用本地缓存)。比如:
bash
moon runwasm Yoorkin/cowsay -- hello
moon runwasm Yoorkin/cowsay@0.1.2 -- hello
如果模块里有多个可执行子包,可以通过包路径指定入口。@版本号 可以放在模块名后,也可以放在完整包路径后,两种写法等价:
bash
moon runwasm moonbitlang/parser/cmd/moonfmt@0.3.7
moon runwasm moonbitlang/parser@0.3.7/cmd/moonfmt
给 Wasm 程序传参数时,建议用 -- 把 moon runwasm 的选项和要传给程序的参数分开:
bash
moon runwasm Yoorkin/cowsay -- hello world
这跟安装 Python 包、Node 包完全是两回事,不需要虚拟环境,不需要安装依赖,也不会污染系统。
跟传统方式对比一下
假设你想用某个 JavaScript 工具,传统流程可能是:
bash
npm install -g 某个工具
# 或者 npx 某个工具
# 或者 clone 仓库 -> npm install -> npm run build
这个过程中,你大概率会遇到:版本冲突、全局污染、Node 版本不兼容、依赖安装失败、网络问题等等。
而用 MoonBit Skills,只需要:
bash
moon runwasm 作者/包@版本
就这一行。
它能解决啥问题?
直接看几个场景。
临时工具
你临时想在终端里整点活,比如生成个 cowsay 图案:
bash
moon runwasm Yoorkin/cowsay@0.1.2 -- hello
用完即走,不用安装,不污染系统。
CI/CD 脚本
构建流程里需要某个文本处理工具。传统方式可能要拉 Docker 镜像,或者配特定语言环境。用 MoonBit Skills 就一行命令,省事。
AI Agent 工具调用
未来 AI Agent 可能会调用大量工具。如果每个工具都能打包成一个可解释、可执行、可分发的技能单元,Agent 直接读 SKILL.md 就知道它是什么、怎么调用,然后执行。
跨团队协作
你写了个内部格式化工具,想给同事用。以前得发文档、配环境、教安装。现在直接给同事一个命令:
bash
moon runwasm 你的用户名/格式化工具@0.1.0
他就能直接用。
总结
它解决的问题大致有这些:
- 发布工具麻烦。
- 环境依赖复杂。
- 运行不可控。
- 跨平台兼容性差。
- 可发现性差。
- 可执行性差。
一句话:它让能力变得像 URL 一样容易被分享和调用。
跟传统包管理比,新在哪?
可能有人会问:这不就是 npm、cargo、pip 干的事吗?
差别其实挺大的。我给大家列个表对比一下:
| 特性 | 传统语言包管理 | MoonBit Skills |
|---|---|---|
| 分发对象 | 源码包 + 依赖声明 | 预构建的 Wasm 二进制 |
| 依赖隔离 | 有依赖树,常有版本冲突 | 技能互相独立,没有依赖树 |
| 沙箱隔离 | 脚本可执行任意代码 | Wasm 沙箱隔离 |
| 自我说明 | README 格式不统一 | 标准 SKILL.md,Agent 可直接理解 |
把 包 变成 技能
有人会说:npx 也是一行命令啊。
但 npx 跑的是源码包,背后还要解析依赖树、处理版本冲突、可能跑 postinstall 脚本。MoonBit Skills 跑的是平台已经构建好的 Wasm 二进制,没有 node_modules,没有版本冲突,也没有生命周期脚本。
传统包管理管的是 依赖,你安装了一个包,还得写代码去用它。MoonBit Skills 管的是 能力,找到一个技能,直接就能执行。
说白了,传统包管理像给人发一堆零件,MoonBit Skills 是直接给你一个装好的成品。
Wasm 作为默认执行格式
很多语言生态里,Wasm 只是可选目标,而在 MoonBit Skills 里,Wasm 是默认的分发形态。
选 Wasm 做分发格式,不是随便定的。它有几个特别契合技能场景的优势:
- 跨平台一致:同一份 Wasm 二进制,在 Windows、macOS、Linux 上行为一致,不用为不同系统分别打包。
- 沙箱隔离:Wasm 跑在受限环境里,默认不能随意访问文件系统或网络,比直接跑脚本更安全。
- 无依赖、独立执行 :一个技能就是一个二进制,没有
node_modules那种依赖树,也不会和系统里其他工具冲突。 - 启动快、体积小:经过 wasm-opt 优化后,二进制体积小,冷启动快,很适合 CI/CD 和 Agent 工具调用。
所以 MoonBit Skills 才能做到:写完一个工具,上传后别人直接一行命令跑起来。
SKILL.md 作为能力描述标准
这个设计挺有意思。
SKILL.md 用结构化的 frontmatter(name、description)加 Markdown 正文,统一了技能的描述方式。
未来 AI Agent 要调用工具,不用猜你的 API 怎么用,读 SKILL.md 就知道命令格式和场景。
语言级别的工具链整合
moon runwasm 是 MoonBit 工具链原生支持的命令,不是第三方插件。
从 写代码 到 发布技能 再到 被别人运行,整条链路都是原生打通的。
对开发者意味着啥?
- 你写的工具不再只是自己的脚本,而是可以被全世界直接运行的能力。
- 不用写复杂的安装文档,一行命令就能让别人用起来。
- 不用为不同平台打包,Wasm 帮你解决跨平台问题。
- 你的能力更容易被 AI Agent 和其他工具发现并调用。
对普通用户意味着啥?
- 看到一个工具,不用研究源码,不用配环境,一行命令就能试。
- 不污染系统,不安装全局依赖。
- 用完即走,像调用一个本地命令一样自然。
结语
skills.mooncakes.io 是 MoonBit 生态里一个挺有意思的尝试。
它不只是让包更容易发布,而是让能力变得更容易被分享、被发现、被调用。
核心思路就三句话:
- 写一次工具。
- 上传成一个技能。
- 别人一行命令就能跑。
它不是在发库,而是在发能力
说实话,我个人挺看好这个方向。
尤其是 AI Agent 越来越火之后,这种「写完就能跑、跑完就能丢」的能力单元,价值只会越来越大。
不知道你怎么看呢?欢迎在评论区留言。