什么是 moss
moss 是一个 Windows 桌面软件,但它的功能不是出厂就定死的 —— 每个功能都是一个可插拔的插件,而 AI 就住在软件里面。你告诉它缺什么,它当场写好插件插进去。
一句话说清
你不装功能,你告诉它缺什么,它长出来。
宿主(moss.exe)本身几乎不提供业务功能,只干四件事:解析命令行、找解释器、把插件列表交给界面、等界面退出。其余全部以「包」的形式挂在 exe 外面。
pkg.json + 入口文件,任何语言。插上就能用,拔掉不影响别的。它和普通 AI 有什么区别
聊天型 AI 给你一段答案或一段代码,剩下的(建项目、装依赖、开终端、记在哪)都得你自己来。moss 是软件形态的:成果直接变成软件里一个能点、能拖、能敲的功能,下次打开还在。
| 普通 AI 助手 | moss | |
|---|---|---|
| 交付物 | 一段文字 / 一段代码 | 一个装上就能用的插件 |
| 在哪里 | 浏览器标签页,聊完就关 | 桌面窗口,天天打开 |
| 能力边界 | 模型能做什么就做什么 | 缺什么就给自己长一只手(技能) |
| 重复劳动 | 每次都要重新描述一遍 | 长出来的插件是资产,留在软件里 |
所以它既能像 agent 一样帮你干整理文档、批量改名这类普通工作,也能给程序员长真正用得上的工具 —— 而且这两种用法是同一套东西。
为什么这么拆
- 加功能 = 丢一个目录,不用重新编译 exe。
- 改界面不用重新打包:界面是
system/ui/里的网页,不属于任何插件,也删不掉。 - 你的地盘和出厂件分开:
user_data/打包时整体排除,升级只覆盖程序本体。 - 一本契约:宿主、出厂插件、你自己写的包、AI 写出来的包,全部遵守同一份 插件契约。
装好就能跑出厂插件;想让它「长功能」,先在 设置 → AI 填一次接口 —— 地址、密钥、模型三段,填完就不管了。见 AI 服务配置。
软件本身免费,无订阅、无内购。AI 功能需要你自己填一个模型接口(DeepSeek、OpenAI、Kimi、通义、智谱、本地 Ollama 等),费用直接给你选的服务商,不经过我们。
接下来读什么
安装与第一次启动
moss 是绿色免安装的:解压、双击,没有安装程序,不写注册表,不写系统目录。整个过程不需要命令行。
装好之后第一件事:填 AI 配置
只装不配也能用 —— 出厂插件直接跑。但「说一句话让它长新功能」这件事要靠 AI,所以要填一次接口(地址 / 密钥 / 模型)。
位置:设置 → AI。怎么填、填哪家的,见 AI 服务配置。
三步
- 解压到一个固定的文件夹 —— 建议
D:\soft\moss或D:\moss这种你自己的目录。- 别放
C:\Program Files:它要往自己目录里写数据,那里没权限。 - 别放桌面 / 下载目录:升级是整包覆盖,这两处容易被清理工具扫掉。
- 别放
- 双击
moss.exe:直接打开界面。第一次运行会自己建user_data\(你的插件、技能、配置、快照都在这儿)。 - 打开 设置 → AI,填一次接口:填完就能让它长功能了。
装完长这样

左边是插件列表,右边是选中插件的详情 / 命令行 / 文件 / AI。它们的作用见 界面导览。
压缩包里只有两样东西
| 内容 | 说明 |
|---|---|
moss.exe | 宿主内核:解析命令行、找解释器、装插件、开界面 |
system\ | 发行目录:界面、出厂插件、插件契约、技能手册、自带 Python 运行时 |
user_data\ 不用手动建 —— 第一次运行自动生成。使用说明和开发文档也没放进压缩包(为了目录清爽),完整内容就在这个文档站上。为什么这样不影响 AI 干活,见 目录结构。
不确定环境对不对?
进阶:用自检命令查一遍
习惯命令行的,可以在解压目录开一个终端跑 moss.exe --selfcheck:逐项检查安装根、runner 表、插件能不能读到、自带运行时在不在、cd 增强有没有装。
不习惯命令行也没关系:双击打不开、或者界面白屏,绝大多数情况是目录放错了(比如放进了 Program Files,写不了 user_data)。
把它接进命令行(可选)
不用命令行的可以跳过这一节。想让任何终端、任何目录都能直接敲 moss:打开界面 → 设置 → 命令接入 → 勾选「把 moss 加进系统 PATH」,然后新开一个终端(旧窗口不会自动刷新环境变量)。
moss # 用法 + 插件列表
moss ui # 打开界面
moss :md5 你好 # 跑一个插件
几件要知道的事
自带独立的 Python 运行时(system/runtimes/python),插件都用它跑。你机器上装没装 Python、装的哪个版本,都不影响。
目前没有买代码签名。首次运行点「更多信息」→「仍要运行」即可。它不联网(AI 除外,那部分只连你自己配的模型接口)、不写注册表、不写系统目录。
升级只覆盖 moss.exe 和 system\,user_data\ 一个字都不动。想搬家 / 备份:整个文件夹拷走就行 —— 路径不写进注册表。
界面导览
主界面就一个窗口:左边插件列表,右边是选中插件的详情、命令、文件、AI。moss ui 或双击 moss.exe 打开。
不填它,软件只能用出厂插件;填了它,才能「说一句话让它长新功能」。位置在右下角齿轮里的第一组,填法见 AI 服务配置。
一图看懂

左边:插件列表
- 顶部搜索框按包名 / 说明 / 标签过滤,敲几个字就缩到几个。
- 左上一个开关在「插件 / 历史」之间切换:历史里是最近跑过的路径调用,点一下重跑。旁边那颗按钮能把左栏收起,只留图标(宽度会记住)。
- 列表工具条:筛选(全部 / 系统 / 用户)、排序(名字 / 加入时间 / 最后运行 / 自定义拖动排序)、刷新、新建包。
- 拖动文件经过窗口时,这里会浮出一条提示,只列出收得下它的插件。
右边:五个选项卡
| 选项卡 | 里面是什么 |
|---|---|
| 详情 | 插件说明、参数表单、「使用说明」卡(来自包里的 README.md)、自动启动开关、DROP 提示 |
| 命令行 | 一条命令输入框 + 终端输出。它等价于在终端敲 moss :包名,插件需要问一句话也能在这里答 |
| 文件管理 | 浏览插件目录、看源码、看改动差异、管理版本快照、一键回退 |
| AI | AI 工作台:聊天 + 它这一轮读了什么、改了什么、跑了什么(见 AI 工作台) |
| 界面 | 只有插件声明了内嵌界面时才出现:把插件自己的网页嵌进来当标签页用 |
底部状态栏
左边是最近一条状态提示,右边那颗齿轮就是设置(等于打开 user_data/config.toml 的图形版)。设置面板分七组,详见 设置详解。
拖进来就跑
从资源管理器往窗口里拖文件或文件夹,会浮出一层遮罩,只列出声明了能接这种输入的插件,松手就跑。拖图片、拖 txt、拖文件夹分别会发生什么,见 拖进来就跑。
双击 exe、快捷方式、快速启动栏 —— 这时候没有终端可写,moss 直接开界面;在终端里敲 moss(不带参数)则打印用法和插件列表。带参数时一律按参数干活,不看有没有控制台。
命令行基础
日常使用完全不需要终端:装好 → 双击 → 点插件 → 拖文件。这一页是给「愿意用键盘」的人准备的加分项,先看 界面导览 也一样能上手。
界面能做到的事,命令行都能做到 —— 而且命令行能做的事,界面不一定有。四件最常用的事先记住就够。
四种入口
| 你敲的 | 它干什么 |
|---|---|
moss | 打印用法 + 插件列表 |
moss ui | 打开界面 |
moss :<包名> [参数] | 跑一个装好的插件,例如 moss :md5 你好 |
moss <名字> | 在当前目录(及向上几层)找匹配的脚本并运行,例如 moss m → main.py / main.go / main.html |
找脚本的写法(不带冒号)
moss m # 当前目录下找 main.py / main.go / main.html
moss b/m # 进 b 开头的子目录,再找 m 开头的
moss s/t/main # 逐级往下
moss /m # 同 moss m,但不切换目录
moss ~m # 后台脱离运行,不阻塞当前控制台
moss zeen # 文件系统里没找到?去历史里找上次那个 ZeEn(命中多个取最近的)
跑插件:两种参数写法
位置参数按插件声明的顺序来,key=value 则按名字来 —— 两种都行:
moss :my-first 你好 3
moss :myfirst text=你好 times=3
插件可以把某个参数标成 rest: true,意思是「剩下的全归我,我自己解释」—— img 就是这种:
moss :img webp 60% 1200 *.png -o D:\out
符号可以改
行首那个符号决定命令的种类,默认是:
| 符号 | 含义 | 默认 |
|---|---|---|
| 路径调用 | 找脚本跑 | (裸名) |
| 跑包 | 跑装好的插件 | : |
| 不切目录 | 在当前目录里跑 | / |
| 后台脱离 | 不阻塞控制台 | ~ |
如果你的主要用途是「跑装好的插件」,可以在 设置 → 快捷命令 里把「跑包」留空、把「路径调用」设成 : —— 于是 moss img 直接跑包,moss :img 才是找文件。
不知道敲什么
| 命令 | 用途 |
|---|---|
moss --history | 最近跑过什么,方向键选,回车重跑 |
moss --spec | 打印插件契约(写插件前读它) |
moss --resolve <名字> | 只看解析结果不执行:为什么选中它 / 为什么没找到 |
moss --selfcheck | 自检环境 |
完整清单见 命令大全。
出厂插件清单
发行版自带 17 个插件,开箱就能用。它们和 AI 新长出来的插件、你自己写的插件,用同一套契约、同一套界面 —— 没有任何区别。
界面上点插件再点「运行」,或者命令行 moss :<包名> [参数]。下面每条都给了最常用的写法。
一览
| 包名 | 名称 | 类型 | 一句话 |
|---|---|---|---|
exec | 快捷执行 | 界面 | 命令脚本管理器:把常跑的命令存成一张卡片 |
fanyi | 快捷翻译 | 界面 | 双击 Ctrl 翻译选中文字并自动粘贴 |
paste | 贴边快输 | 界面 | 常用文本 / 剪贴板快速粘贴 |
img | 图片工具 | 界面 + 命令行 | 图片压缩 / 格式转换 / 多分辨率 ico |
path | PATH 管理 | 界面 | 管理用户 PATH,带备份和差异对比 |
pip | Pip 工具 | 界面 | 图形化 Python 包管理器 |
skill | 技能 | 界面 | AI 给自己造的工具都在这儿:允许 / 撤销 / 删除 / 转插件 |
github | GitHub 可视化助手 | 界面 + 命令行 | 看得见的 git:时间线、同步、镜像、恢复 |
sync | 文件同步 | 界面 | 按文件名规则把文件归到该去的地方 |
h | 历史快捷运行 | 命令行 | moss :h 名字 在原来的目录里重跑一次 |
md5 | MD5 计算 | 命令行 | 算字符串 / 文件 / 文件夹的 MD5 |
tree | 目录树 | 命令行 | 按 .gitignore 过滤的目录树 |
mark | 目录书签 | 命令行 | 记下目录,下次一句话跳过去 |
kill | 杀进程 | 命令行 | 按进程名或端口结束进程 |
ip | IP 查询 | 命令行 | 查本机出口 IP 或某个 IP 的归属 |
suoluetu | 图标修复 | 命令行 | 重建 Windows 图标缓存 |
dsh | DSH 桥 | 界面 + 守护 | 在外面访问这台机器上的 DSH(需要账号) |
效率与输入
exec · 快捷执行
多语言的命令脚本管理器:把常跑的命令存成一条一条,下次点一下或敲个别名就跑。界面是 Apple 风格的 WebView,可以内嵌成 moss 的标签页,也能自己开窗。
moss :exec # 打开管理器
moss :exec list # 列出存过的命令
moss :exec 别名 参数 # 不打开界面,直接跑那条
类型 app · 数据在 user_data/state/exec
fanyi · 快捷翻译
选中任何一段文字,双击 Ctrl 就翻译并自动粘回去。多个翻译通道自动切换(Google / 有道 / MyMemory),带本地缓存;界面是热键的可视化台(四步进度、任务卡、日志、历史)。
moss :fanyi # 打开界面,调热键和查看历史
类型 app · 需要联网 · 会读写剪贴板、注册全局热键
paste · 贴边快输
把常用文本(签名、地址、常用回复)分组存好,需要时贴边弹出、一键粘贴。分组之间热键可以重复、也能同时启用多个分组,窗口可拉伸。
moss :paste
类型 app · 会读写剪贴板、注册全局热键
h · 历史快捷运行
moss --history 的短名字版:按名字找回以前跑过的目标,先切到它当时的目录再跑一次,省掉 cd。
moss :h # 列出最近记录
moss :h zeen # 重跑上次那个 ZeEn
moss :h wwwroot/zeen/main # 逐级写法也认,/ 和 \ 都行
类型 task · 别名 hist / history
mark · 目录书签
在某个目录里敲一下就把这个目录记下来,下次给个关键字就直接跳过去。
moss :mark # 记录当前目录
moss :mark list # 列出所有书签
moss :mark 项目 # 按关键字找并切过去
moss :mark --gui # 打开界面编辑
类型 task · 只收文件夹拖入 · 数据在 user_data/state/mark
文件与图片
img · 图片工具
压缩、转格式(jpg / png / webp / bmp / gif / tiff)、生成多分辨率 ico。不带参数打开界面(缩略图选择器,可框选、可拖文件夹);带参数走命令行,语法很短、顺序随便。
moss :img # 打开界面
moss :img ico a.png -o D:\out # 转成 ico 放到 D:\out
moss :img webp 60% 1200 *.png -o D:\out # 转 webp,质量 60、宽 1200
moss :img 1200 -o D:\out # 全部缩到 1200 宽,格式不动
moss :img 60% / # 全部按 60% 重压,写回源目录
命令行里必须给 /(写回源目录)或 -o <目录>,否则它会弹菜单让你选;没有终端可交互时(脚本、管道、AI 调用)直接报错说明该补什么 —— 不会替你猜、更不会覆盖原图。
类型 app · 只收图片扩展名拖入 · -s 16,32 可指定 ico 尺寸
sync · 文件同步
按文件名规则把一堆文件归到该去的地方 —— 下载目录收拾、照片按日期归档这类活。支持把文件夹直接拖进来。
moss :sync # 打开界面配规则
类型 app · 收文件夹拖入
tree · 目录树
生成目录树,按 .gitignore 和内置忽略表过滤(node_modules、venv、__pycache__ 这类不会刷屏)。
moss :tree # 当前目录
moss :tree docs 2 # docs 目录,深度 2
moss :tree docs 2 -a # 连隐藏文件一起
moss :tree -node_modules,dist # 再额外忽略几个名字
类型 task · 收文件夹拖入 · 已对 AI 开放
md5 · MD5 计算
算字符串、文件或整个文件夹的 MD5,结果自动进剪贴板。不填参数就用剪贴板里的内容算。
moss :md5 D:\a.txt
moss :md5 "一段文字" # 带引号强制按字符串算
moss :md5 # 算剪贴板里的内容
类型 task · 收文件 / 文件夹拖入 · 已对 AI 开放
suoluetu · 图标修复
桌面图标变成白纸、显示错乱时用它:关掉 explorer、删掉 IconCache.db、再把 explorer 拉起来。屏幕会闪一下,属于正常。
moss :suoluetu
类型 task · 会重启资源管理器
系统与网络
path · PATH 管理
查看和管理 当前用户的 PATH(不需要管理员)。带备份时间线:改动前先存一份,出问题双击就能回退;还能去重、清理失效目录、拖动调整优先级。
moss :path # 打开界面
moss :path add D:\tools\bin
moss :path remove D:\old\bin
moss :path add # 不加路径 = 加这个插件自己的目录
moss :path help
类型 app · 动的是 HKCU\Environment\Path · 改完广播 WM_SETTINGCHANGE,新开终端生效
pip · Pip 工具
图形化的 Python 包管理器:查 / 装 / 卸 / 升级 moss 自己那个 venv 里的包,带 PyPI 搜索、交互式 Shell、操作日志。
moss :pip # 打开界面(别直接双击它的 exe)
类型 app · 给插件装依赖时用它,不要动系统 Python
kill · 杀进程
按进程名(大小写不敏感的包含匹配)或端口号结束进程。命中多个时会按可执行文件路径分组,上下键选一组,或者一次清理全部 —— 不会糊里糊涂把同名的别的程序一起杀了。
moss :kill node
moss :kill 8080 # 纯数字 = 找正在监听这个端口的进程
类型 task · 风险等级 high(不会给 AI 调)
ip · IP 查询
查本机当前出口 IP,或某个 IP 的归属地 / 运营商(走 ip-api.com,需要联网)。
moss :ip # 本机出口 IP
moss :ip 8.8.8.8 # 查某个 IP
类型 task · 已对 AI 开放
开发与远程
github · GitHub 可视化助手
把 git 状态变成看得见的东西:提交时间线(标出哪些已推送)、一键同步、一键镜像(建远端仓库 / 本地备份 / 加镜像远端)、把某个版本从 GitHub 恢复出来、切分支,本地和远端并排看。拖一个目录进来就换当前仓库。
它的每个按钮都会告诉你等价的 git / gh 命令和原样输出 —— 拿它当 git 的学习器也合适。
moss :github # 打开界面
moss :github status D:\code\my-repo
moss :github log
类型 task + 内嵌界面 · 别名 git · 收文件夹拖入
skill · 技能
AI 给自己造的工具(技能)都在这里:图标墙、详情、文件列表、时间,可以允许 / 撤销 / 删除,也能一键转成正式插件。第一次启用必须由人点 —— AI 自己批不了。
moss :skill # 打开管理器
moss :skill list # 列出技能
moss :skill show <id> # 看详情
moss :skill allow <id> # 允许(AI 下次开工才带上)
moss :skill revoke <id> # 撤销
moss :skill rm <id> # 删除
moss :skill promote <id> [新id] # 转成插件,搬进 user_data/pkgs
类型 app · 用户层在 user_data/state/skill/,预制层在 system/pkgs/skill/builtin/
dsh · DSH 桥
在别的设备上访问这台机器上的 DSH:本机起一个桥守护进程,主动连出做 NAT 穿透,带本机密码票据门,把请求转发到 127.0.0.1:3080;DSH 没在跑时会自动把它拉起来。
moss :dsh # 前台运行(Ctrl+C 停止)
moss :dsh --daemon # 转后台守护
moss :dsh --status # 看配置摘要
moss :dsh --set-token # 改令牌
moss :dsh --set-password # 改本机密码
需要本机有 node + @deepseek-ai/dsh,并且要在服务器上注册一个账号、在设置页登录。不满足就只是一个跑不起来的空壳。
类型 app · 风险等级 high
拖进来就跑
不用先打开插件、不用选参数:把文件或文件夹拖到 moss 窗口上,能处理它的插件自己亮起来,松手就跑。
三个真实例子
拖一张图片:照片.png
拖动过程中,窗口里只亮两个插件:图片工具和 MD5。松手落到哪个上,就跑哪个:
- 落到图片工具 → 打开它的界面并把这个文件带进去,可以直接压缩、转格式(jpg / png / webp / ico)、批量处理。
- 落到 MD5 → 直接算出这个文件的 MD5,结果自动进剪贴板。
为什么只亮这两个:图片工具在声明里写了自己只收 jpg / jpeg / png / bmp / gif / tiff / webp,MD5 没限制扩展名(什么都收)。没声明的插件在这一刻根本不会出现 —— 不会把你的图片塞给处理不了它的插件白跑一趟。
拖一个文本:会议记录.txt
只有一个插件会亮:MD5,因为它没有限制扩展名。落上去就是算这个文件的 MD5。
「我要的是把 txt 整理归类 / 转成 PDF / 提取里面的待办」—— 这些出厂插件里没有,但那正是它的用法:打开 AI 工作台说一句,它长一个能接住 txt 的插件出来;长好之后,你再拖 txt,列表里就会出现它。
为什么不多亮几个:宿主不会为了"看起来丰富"给你一堆接不住的东西。默认列表是空的,缺什么就长什么。
拖一个文件夹:下载
这时会亮起一串(每个都是把整个目录当工作目录):
| 插件 | 拖进去之后 |
|---|---|
| 目录树 | 立刻生成这个文件夹的树,按 .gitignore 过滤,node_modules、venv 这类不会刷屏 |
| 目录书签 | 记下来,下次一句话就跳过去 |
| 文件同步 | 按你的规则把里面的文件归到该去的位置 |
| 图片工具 | 把这个目录里的图片全部拉进界面,批量压缩 / 转格式 |
| GitHub 可视化 | 把这个目录当成当前仓库,打开提交时间线 |
| PATH 管理 | 把这个目录加进用户 PATH |
为什么是这些:声明了「我收文件夹」的插件才收。能用文件夹解决的就不去写「只收单个文件」—— 声明窄一点,拖动时候选列表才干净。
那一瞬间,是 AI 在判断吗?
不是。拖动的筛选是宿主按每个插件的 drop 声明做的:纯本地、瞬间出结果、不联网、不花 token。AI 在更前面一层:
① 谁能接什么 —— 插件自己在 pkg.json 里声明,宿主照单筛选;
② 缺哪一类插件 —— 你跟 AI 说一句,它读契约、写代码、真机跑通,长出一个;
③ 长出来的插件 —— 和出厂插件完全一样,下一次拖动列表里就有它。
所以你会看到两种体验:拖现成的东西 → 秒出候选;缺一类处理 → 说一句话,之后再拖就有了。这两件事都不需要你写配置或改代码。
没有插件接得住的时候
遮罩里是空的,什么都不会跑 —— 不会硬塞给某个插件。这时候直接跟 AI 说你的需求,或者到 出厂插件清单 看看是不是已经有能用的。
是怎么交给插件的
| 声明 | 效果 | 典型例子 |
|---|---|---|
cwd: true | 把它当工作目录;拖文件时取文件所在的目录 | 目录树:在那个目录生成树 |
param: true | 路径追加到参数末尾(带引号,空格路径也安全) | MD5:算这个文件的 MD5 |
base: true | 只给文件名(配合 cwd) | 需要相对路径的场合 |
自己写插件、想让它收拖入:见 收拖入。
历史、cd 与短命令
每天要敲的那几条路,moss 都帮你记着:跑过什么、在哪跑的、怎么少敲几个字。
刚才跑的再跑一次
moss --history # 方向键选,回车直接重跑(去重,最多 20 条)
moss --history 50 # 只打印不交互(管道 / 脚本用这种)
回车进入程序后,程序退出就结束,不会回到列表 —— 不会出现「退不出来」的死循环。
记录存在 user_data/history,可以手动看。
按名字重跑
:h 插件是「历史」的短名字版:按关键字找回那次调用,先切到它当时的目录再跑,省掉 cd。
moss :h # 列出最近记录
moss :h zeen # 重跑上次那个 ZeEn
moss :h wwwroot/zeen/main # 逐级写法,/ 和 \ 都行
文件系统里找不到 moss zeen 时,宿主也会自动去历史里找同名那次(命中多个取最近的)。
跳到某个目录
moss cd 项目 # 智能找目录并切过去
moss cd --install # 把包装函数写进 PowerShell 配置 → 屏幕上不留回显
moss cd --uninstall # 删掉那个包装函数
没有包装函数时,它靠「认出父 shell 并把命令注入控制台」实现,代价是屏幕上会看到它注入的那行命令;--install 之后这一行也没了。
短命令:把 moss :img 缩成 moimg
设置 → 短名设置 里打开,它会在 PATH 上放一个目录,里面是一批小短名。前缀默认 mo:
| 开关 | 效果 |
|---|---|
「前缀」= moss | mo 一条 ⇒ 等于敲 moss |
「前缀+包名」= moss :包名 | moimg ⇒ 等于敲 moss :img |
moss --alias status # 看现在生成了哪些短名
moss --alias sync # 手动对账(装了新插件后跑一下)
前缀可以改(shortcut.prefix),短名目录在 user_data/shortcuts。
排障:为什么没找到 / 为什么选中它
moss --resolve img # 只看解析结果,不执行
它会告诉你这个名字最后落到了哪个插件、哪一层(user_data/pkgs 还是 system/pkgs)、还是根本不是插件。
AI 工作台
界面右边那个 AI 不是聊天框,是一个能动手的循环:读契约、读插件、改代码、跑命令、看退出码,跑通了才算完成。
它一轮在干什么
- 读插件契约(
system/spec/那份,唯一真相) - 读现有插件的代码,或看你这句需求该落在哪个包
- 用 search / replace 发改动(不是重写整个文件)
- 跑
moss :<id> …真机验证,看退出码和输出 - 不过关就自己回头改,直到跑通才交给你
四条硬规矩(这些不是提示词,是代码里的闸)
| 规矩 | 为什么 |
|---|---|
| 改文件只发 search/replace | 同样改 3 行输出几十 token 而不是几千;也不会出现「重写时悄悄丢一段」 |
done 是硬闸 | 最后一次改动之后必须有一次退出码 0 的运行,否则把话打回去继续 —— 不是「模型说完成了」 |
| 每一步记 token | 把「费不费」从感觉变成数字 |
| 写权限是一张清单 | 一个目录一对「读 / 写」勾,没勾的地方读都读不了 |
core/、moss.exe、moss.py、venv/、.git/、备份库在底层写死保护 —— 就算你勾了「任意路径」也写不了。这条是结构性的,不靠自觉。
动手之前先留一份快照
每一轮对话里第一次改文件之前,宿主会强制存一条「动手前」的版本快照,标题就是你当时说的那句话 —— 之后改坏了,在文件管理里点一下就退回。快照机制见 版本快照与回退。
它怎么用「手」
它手上的工具只有几类:跑 moss 命令(run_moss)、读写你授权过的目录、读契约与技能手册、以及给自己造技能(见 技能)。它不是靠猜,而是靠跑。
怎么用得好
- 说清「要什么」和「怎么算成功」,别只说「优化一下」。
- 给一个真实输入的例子:文件名长什么样、在哪个目录、想要什么输出。
- 它说改好了,你看一眼命令行选项卡里的运行结果 —— 那就是它的验收证据。
- 不满意就继续说,它会接着改;改到一半发现方向不对,用快照退回去重来。
AI 需要一组模型接口(地址 / 密钥 / 模型)。第一次用先看 AI 服务配置,在 设置 → AI 里填一次即可,支持多组切换。
技能:AI 给自己造的手
AI 干到一半发现宿主没给它这个能力(截屏、量像素、解析一个怪格式),可以当场给自己造一个工具 —— 但那只是「声明」,你要点一次「允许」,它下次开工才带上。
技能就是插件
语法一字不差:一个目录 + pkg.json + 入口文件。区别只有三处:
| 插件 | 技能 | |
|---|---|---|
| 住哪 | user_data/pkgs/<id>/ | user_data/state/skill/<id>/ |
| 谁用 | 你(也能被 AI 调) | AI 自己,在你的授权范围内 |
| 怎么生效 | 放进去就在列表里 | 必须由你点「允许」 |
声明 ≠ 授权
插件里写 "ai": {"expose": true} 只是它声明「我可以被 AI 调」,默认是 false(装了 100 个插件,不该 100 个都能被 AI 调)。真正能不能用,还要看 user_data/ai/skills_allow.json 里有没有你的授权,而且绑指纹 —— 包改了就作废,要重新允许。
allow / revoke / rm / promote 这四个动作只能人做。AI 拿 run_moss 去调这几个词,宿主直接拒掉。这是设计出来的闸,不是防君子。
管理技能
moss :skill # 打开管理器(图标墙 / 详情 / 文件 / 时间)
moss :skill list # 列出所有技能
moss :skill show <id> # 看它是什么、为什么造、用了什么
moss :skill allow <id> # 允许:下一次开工才进工具表
moss :skill revoke <id> # 撤销
moss :skill rm <id> # 删掉
moss :skill promote <id> [新id] # 转成正式插件(搬进 user_data/pkgs)
技能在插件列表里看不见 —— 它不住 user_data/pkgs/。要找它就去「技能」这个插件里找。
什么时候它会造
只有一种情况:缺一个能力,而且这次任务里要反复用。一次性的活它会直接写脚本进你的包,不动技能库。
觉得某个技能好用,就在管理器里 转成插件 —— 它就变成正常插件了:出现在列表里、能被你手动跑、也能被别的流程调。
版本快照与回退
改坏了不用重来。快照按内容寻址存,同一个文件存一百次也只占一份;恢复的时候只写回真正变了的文件,别的文件一个都不碰。
界面里怎么用
选中插件 → 文件管理 选项卡:左边是文件,右边是版本快照列表。点一条快照能看「那次动了哪几个文件」,再点「回退」就回到那一刻。

命令行里怎么用
| 命令 | 干什么 |
|---|---|
moss --snap list [对象] | 列出快照;不给对象就是按组汇总 |
moss --snap save [对象] [--title T] | 存一次快照;不给路径 = 当前目录 |
moss --snap show <id> | 看那一次的清单(有哪些文件) |
moss --snap diff <id> | 和现在比:一致 / 变了 / 快照有我没了 / 我有多 |
moss --snap revert <对象> | 一键「对比 + 回退」:先列变了什么,再按只写回对齐 |
moss --snap restore <id> | 恢复;默认只写回,--sync 则一模一样(多出来的进回收站) |
moss --snap grep <对象> <关键词> | 在快照里搜内容 —— 包改废了就查原来怎么写的 |
moss --snap cat <对象> <路径> | 看快照里某个文件的内容 |
moss --snap check | 按指纹校验内容块,被改过 / 丢了当场看得见 |
moss --snap ignore | 看过滤规则(哪些文件不会被存进去) |
moss --snap gc | 回收没人引用的内容块 |
「对象」可以是插件,也可以是路径
moss --snap save :md5 # 插件:写在它自己的组里
moss --snap save D:\proj\x # 外部目录也行(落在 @abs-xxxxxxxx 组里)
moss --snap group D:\proj\x # 只算不存:这个对象会落到哪个分组
moss --snap list # 不给对象 = 按组汇总
--yes
restore / export / rm / gc 不加 --yes 只打印「会干什么」,不动手。加 --json 得到结构化输出(给脚本 / AI 用)。退出码:0 成功、2 用法错、3 没东西可做、4 出错。
哪些文件不进快照
规则文件是 user_data/snapshot.ignore,写法同 .gitignore 的常用那几条。node_modules、venv 这类默认就被挡在外面,所以快照不会一存几百兆。
和 AI 的关系
AI 每次动手前存的那条叫「动手前」快照(by="ai")。它只能删自己存的 —— 你自己存的那份,它删不掉,这是结构性保证。
设置详解
界面右下角那颗齿轮打开设置面板,七组 —— 第一组 AI 是核心,其余按需要再动。它背后就是 user_data/config.toml —— 那个文件里每一项都带注释,直接改也完全可以。
七组
| 分组 | 管什么 | 常动的东西 |
|---|---|---|
| AI 核心 | 模型接口(可以多组) | 地址 / 密钥 / 模型 —— 不填它就只能用现成插件,不能长新功能。见 AI 服务配置 |
| 命令接入 | 让 moss 在命令行里好用 | 「把 moss 加进系统 PATH」、cd 增强 |
| 快捷命令 | 行首符号决定命令种类 | 路径调用 / 跑包 / 不切目录 / 后台脱离 四个符号 |
| 短名设置 | 把 moss :img 缩成一条短命令 | 前缀、两种短名开关 |
| 执行提示 | 每次运行前 / 后打印的那几行 | 清屏、打印命令、目录、PID、耗时、开始结束时间 |
| 输出格式 | 那几行的文字样式 | 起始 / 结束横幅、行首符号 |
| 高级设置 | 不常动的三项 | 向上查找层数、Ctrl+C 是否强杀整棵树、退出时是否结束插件进程 |
配置文件长什么样
user_data\config.toml # 宿主配置(每项都有注释,直接打开就知道是干什么的)
user_data\config.toml.bak-… # 改配置前自动留的备份
设置面板不认识任何设置项、也不自己实现校验规则 —— 规则只有 core/config.py 那一份。所以你在面板里改和在文件里改,效果完全一样;面板只是把「现在这样 / 为什么冲突」摆出来。
自动启动不在设置里
「开机 / 登录自动运行某个插件」在插件详情页里(详情 → 自动启动),不在设置面板。因为那是每个插件自己的属性,放在详情里才直观。
几个容易找错的开关
- 想让
moss img直接跑插件而不是找文件 → 快捷命令:跑包留空、路径调用填:。 - 嫌每次运行前清屏太跳 → 执行提示:关掉「运行前清屏」。
- Ctrl+C 杀不干净子进程树 → 高级设置:打开「第一次按 Ctrl+C 就强杀整棵树」。
- 升级后短名失效 →
moss --alias sync手动对账一次。
三分钟写第一个插件
一个插件 = 一个目录 + pkg.json + 一个入口文件。不用改宿主、不用重新打包 exe、不用配环境。
1. 生成骨架
moss --new hello
moss --new hello name=打个招呼 desc=把输入重复几遍
它会在 user_data/pkgs/hello/ 生成三个文件:
user_data/pkgs/hello/
├─ pkg.json 声明(零代码)
├─ run.py 入口(真代码)
└─ AGENT.md 给 AI 看的说明:怎么调、有什么副作用、会怎么失败
2. pkg.json
{
"spec": 1,
"id": "hello",
"kind": "task",
"version": "0.1.0",
"name": { "zh-CN": "打个招呼" },
"desc": { "zh-CN": "把输入重复几遍" },
"aliases": ["hello"],
"runner": "python",
"entry": { "file": "run.py" },
"params": [
{ "name": "text", "type": "string", "default": "你好", "desc": "要重复的内容" },
{ "name": "times", "type": "int", "default": 1, "desc": "重复几遍" }
],
"output": { "mode": "json", "schema": { "text": "string", "times": "int" } }
}
3. run.py
import json, os
params = json.loads(os.environ.get("MOSS_PKG_PARAMS") or "{}")
text = str(params.get("text") or "你好")
times = max(1, min(int(params.get("times") or 1), 100))
for _ in range(times):
print(text) # 给人看
data_dir = os.environ["MOSS_PLUGIN_DATA_DIR"]
os.makedirs(data_dir, exist_ok=True) # 宿主只给路径,目录自己建
with open(os.environ["MOSS_RESULT_FILE"], "w", encoding="utf-8") as f:
json.dump({"text": text, "times": times}, f, ensure_ascii=False) # 给机器看
4. 跑它
moss :hello
moss :hello 你好 3 # 位置参数
moss :hello text=你好 times=3 # 或者按名字
界面上也会立刻出现这个插件 —— 不用重启 moss,列表是按目录实时扫的。
5. 改到满意
- 参数不够用?在
params里加一条,界面上自动长出对应的输入框。 - 输出要给人看?
print就进「命令行」选项卡。 - 输出要给别的程序 / AI 用?写
MOSS_RESULT_FILE。 - 要界面?别自己开窗口,把界面写成网页并声明
ui.embed,见 界面:一份代码两种跑法。
自己用 → user_data/pkgs/<id>/(升级不碰)。要随 moss 一起发出去 → system/pkgs/<id>/(升级会整体替换成新版)。同名时用户层压住出厂层,出厂原件还在。
6. 验收
改完跑一遍,退出码 0 才算过:
moss :hello 你好 2
echo $LASTEXITCODE
出问题先自己看三样:命令回显、报错原文、moss --resolve hello 说它落在哪一层。
pkg.json 字段表
唯一的格式规范。完整契约在 system/spec/插件契约.md,随时可以用 moss --spec 打印出来 —— 那份永远和当前版本一致。
顶层字段
「内核」那列是诚实标注:真用 已经在按它做事;读 会读、但不据此做事;占位 只是预留,将来才实现。
| 字段 | 必需 | 说明 | 内核 |
|---|---|---|---|
spec | 建议 | 格式版本,现在固定 1 | 真用 |
id | 是 | 机器身份:目录名 / 台账键 / 数据目录名。发布后不能改(小写字母开头,只含 a-z 0-9 _ -,≤32 字符) | 真用 |
kind | 是 | task 跑完就结束 · app 界面型(会藏控制台窗口) · agent 预留 | 真用 |
version | 是 | 你自己的版本号,跟 moss 版本无关 | 真用 |
name desc | 是 | 展示名与说明,多语言对象 {"zh-CN": "…", "en": "…"} | 真用 |
aliases | 可选 | 你敲的短名,可多个:moss :myfirst | 真用 |
icon | 可选 | 图标文件名(相对包目录)。不写就自动找 icon.svg → .png → .webp → .jpg/.jpeg → .ico → .gif → .bmp | 真用 |
tags | 可选 | 检索用 | 真用 |
runner | 是 | python / go / html / exe / shell / powershell | 真用 |
entry.file | 是 | 入口代码文件(相对包目录) | 真用 |
params | 可选 | 参数声明,见下 | 真用 |
env.cwd | 可选 | 用哪个目录当工作目录;不写=你敲命令时所在的目录 | 真用 |
env.elevated | 可选 | 要不要管理员权限(预留) | 占位 |
output | 可选 | 结果形状(mode + schema),给人 / 给 AI 看 | 读 |
capabilities | 可选 | 能力声明(net / clipboard / fsRead / fsWrite / elevated) | 占位 |
requires | 可选 | 钉死运行时版本,例如 {"python": "3.12"} | 真用 |
ai | 可选 | 能不能被 AI 调,见下 | 真用 |
docs | 是 | 详情页那块「使用说明」指向哪些文件(readme / usage / agent) | 真用 |
ui | 可选 | 内嵌界面声明,见 界面 | 真用 |
drop | 可选 | 拖进来收不收、怎么收,见 收拖入 | 真用 |
accepts | 可选 | 更宽的「能接受什么输入」占位(拖放已由 drop 承担) | 占位 |
未知字段会被原样保留:老宿主读到新包不会把它写残,所以你可以放心加自己的键。
params 每一项
| 键 | 说明 |
|---|---|
name | 参数名(key=value 写法用的就是它) |
type | string(默认) / int / float / bool |
required | 必填?缺了宿主会报错 |
default | 默认值 |
placeholder | 界面输入框的提示文字 |
desc | 说明 |
rest | 设成 true 表示「剩下的参数全归我,宿主原样拼成一串交给我」 |
ai 块
"ai": {
"expose": true, // 声明「我可以被 AI 调」,默认 false
"risk": "low", // 风险等级
"timeout": 60, // 单次最多跑多久(默认 60 秒,最多 300)
"examples": [ // 给 AI 的少样本示例
{ "target": "D:\\a.txt", "note": "算这个文件的 md5" }
]
}
expose: true 只是包自己的声明。真正能不能用还要看 user_data/ai/skills_allow.json 里有没有你的授权,而且绑指纹 —— 包改了就作废。因为 AI 自己也能写文件,把 expose 当授权等于让它自己给自己发通行证。
docs 块
"docs": {
"readme": "README.md", // 详情页渲染成「使用说明」卡,可展开/收起
"usage": "USAGE.md", // 用法说明
"agent": "AGENT.md" // 只给 AI 看,不上界面
}
宿主会把文件读成文本塞进界面数据(每个最多 24000 字符,超了带 truncated)。没有参数、desc 只有一句话的包必须写这个,否则详情页一个字的使用方法都没有。
最小可用清单
只想到「能跑起来」的话,这几个字段就够:spec、id、kind、version、name、desc、runner、entry.file。
参数、输出与结果文件
输入只有一个来源(环境变量),输出有两条通道(stdout 给人、结果文件给机器)。不要自己解析 sys.argv。
参数怎么进来
宿主按 pkg.json 的 params 解析好,以 JSON 字符串放进 MOSS_PKG_PARAMS:
import json, os
params = json.loads(os.environ.get("MOSS_PKG_PARAMS") or "{}")
用户写 moss :hello 你好 3 和写 moss :hello text=你好 times=3,你这边看到的都是同一个 dict。
原始参数字符串另有一个 MOSS_ARGS(空格连接),排障时好打印。
rest:把一长串收进一个参数
像 img 那种「自己有小语法、参数个数不定」的插件,就把一个参数标成 rest: true:宿主把包名后面那一整串原样塞进这个字段,由插件自己解释。
"params": [
{ "name": "args", "type": "string", "rest": true,
"desc": "留空 = 打开界面;填了 = 命令行参数" }
]
没标 rest 时,宿主只取前 N 个位置参数,多余的静默丢掉 —— 用户多打一个词就悄无声息地没了,这种坑比报错难查得多。rest 参数要声明在 params 最后。
两条输出通道
| 通道 | 给谁 | 怎么写 |
|---|---|---|
print() / stdout | 人(命令行选项卡里直接看到) | 随便印,带表情也行 |
MOSS_RESULT_FILE | 机器(界面 / AI / 脚本) | 按 output.schema 写 JSON |
result_file = os.environ.get("MOSS_RESULT_FILE")
if result_file:
with open(result_file, "w", encoding="utf-8") as f:
json.dump({"ok": True, "files": 12}, f, ensure_ascii=False)
output.mode 写 "json" 时,界面和 AI 会按 schema 去读这个文件;写 "text" 就只用 stdout。
要问用户一句话怎么办
别只看 sys.stdin.isatty() —— 界面模式下 stdin 是一根管子,isatty() 永远是假,可界面其实能回答你。正确的判据是「真终端 或者 宿主说有人在听」:
def can_ask():
if sys.stdin.isatty():
return True
return str(os.environ.get("MOSS_INTERACTIVE") or "").strip() not in ("", "0", "false")
def ask(prompt, choices):
if not can_ask():
print("这里没法交互,请明确指定:…(列出非交互的写法)")
return None
print(prompt)
for i, c in enumerate(choices, 1):
print(" %d) %s" % (i, c))
try:
return int(input().strip())
except (EOFError, ValueError):
return None
input()
MOSS_INTERACTIVE 没设就表示「没人在听」(计划任务、后台跑)。这时候 input() 会永远卡住 —— 给个明确的用法提示,像 img 那样列出 -o <目录> / / 两种非交互写法。
中文与编码
宿主把 PYTHONIOENCODING 设成 utf-8(你自己设过就尊重你的)。所以 print("✔ 完成") 不会因为 cp936 崩掉;但如果你的代码里出现了奇怪的中文乱码,先检查你有没有自己又把它改回去。
环境变量与数据目录
一条铁律:只认环境变量,不要猜路径。宿主的内部布局随时可能变(数据目录名就改过一次),环境变量是对外承诺。
宿主注入的变量
| 变量 | 是什么 |
|---|---|
MOSS_HOME | 安装根(moss.exe 所在目录) |
MOSS_USER_DATA_DIR | user_data/ 的完整路径 |
MOSS_PLUGIN_DATA_DIR | 你的数据根(user_data/state/<你的 id>/) |
MOSS_PLUGIN_CACHE_DIR | 可再生的缓存 |
MOSS_PLUGIN_LOG_DIR | 日志 |
MOSS_PLUGIN_TMP_DIR | 临时文件 |
MOSS_PKG_ID MOSS_PKG_VERSION | 你是谁 |
MOSS_PKG_PARAMS | 参数(JSON 字符串) |
MOSS_ARGS | 原始参数字符串(空格连接) |
MOSS_VIA | 谁触发的:cli / history / … |
MOSS_RESULT_FILE | 把结构化结果写进这个文件 |
MOSS_VENV | 宿主识别到虚拟环境时给出 |
MOSS_APPEARANCE | 外观 JSON(深浅色 / 强调色),界面型包用 |
MOSS_PKG_ICON | 你自己图标的绝对路径;没有图标时不注入 |
MOSS_INTERACTIVE | 1 = 有人在听你说话,可以 input() |
PYTHONIOENCODING | 宿主设成 utf-8 |
数据放哪儿
宿主只给路径、不预建目录 —— 你用哪个就自己 makedirs。约定四个子目录:
| 子目录 | 放什么 | 会被备份吗 |
|---|---|---|
data/ | 持久数据(配置、数据库、成品) | 要备份 |
cache/ | 可再生(缩略图、临时索引) | 不用,删了就重建 |
tmp/ | 随便清 | 不用 |
logs/ | 日志 | 不用 |
import os
from pathlib import Path
root = Path(os.environ["MOSS_PLUGIN_DATA_DIR"])
(root / "data").mkdir(parents=True, exist_ok=True)
(root / "cache").mkdir(parents=True, exist_ok=True)
重装 / 升级会把包目录整体换掉,写进去的东西就没了。包目录是只读的。
要密钥怎么办
绝不写进 pkg.json(分享插件就等于泄露)。契约里留了 keyRef 指向宿主托管的口令 —— 但这一块宿主还没实现,现在只能让用户自己在插件配置里填,并存在 data/ 里(自己写清楚这是明文)。
钉运行时版本
"requires": { "python": "3.12" }
不写就用默认版本(现在是 3.12)。自带运行时在 system/runtimes/python/;加别的解释器就再建 system/runtimes/<名字>/。
自带运行时是 Python 嵌入式发行版(隔离模式,不把脚本目录加进 sys.path、也忽略 PYTHONPATH)。所以用自带运行时跑 .py 时,宿主动会走一下 system/bootstrap.py 引导 —— 于是你 import 同目录的模块照样能工作。系统 Python / venv 里则照常直接跑。
界面:一份代码两种跑法
想要界面就把界面写成网页(HTML)。同一份页面既能内嵌进 moss 的「界面」选项卡,也能自己开一个窗口 —— 靠一个垫片自动分流,你的界面代码一个字都不用改。
声明
"ui": { "embed": "ui/index.html", "title": { "zh-CN": "我的插件" } }
路径相对包目录。不写就没有「界面」选项卡 —— 那个选项卡只在当前插件声明了它时才出现。
页面里引三个脚本(顺序别换)
<script src="vendor/vue.global.prod.js"></script> <!-- 你的框架,本地放着 -->
<script src="moss-embed.js"></script> <!-- 垫片:从样板包抄走 -->
<script src="app.js"></script>
然后只认这一套(window.moss):
await moss.call('do_it', {text: 'hello'}); // 调后端的方法(后端就是你的 main.py)
moss.on('time', d => { ... }); // 接后端主动推的事件
moss.on('ready' | 'invoke' | 'drop' | 'theme' | 'visible' | 'close', fn);
moss.on('captured' | 'dragend', fn); // 拖放期间宿主告诉你「怎么回事」
moss.env.embedded; // true = 被嵌在 moss 里;false = 独立窗口
老写法 window.pywebview.api.xxx() 也能用 —— 垫片在内嵌环境下把它垫出来了,所以现成界面搬进来不用改代码。
后端怎么配合
独立窗口(moss :<id>) | 内嵌(界面里点「运行」) | |
|---|---|---|
| 你怎么知道 | 环境里没有 MOSS_UI_EMBED | 宿主机给了 MOSS_UI_EMBED=1 |
| 你该干什么 | 自己 webview.create_window(url=…, js_api=Api()) | 别开窗口,在 stdin/stdout 上说话 |
| 调用怎么回 | js_api 的方法(收到一份 dict) | 一行一个 JSON 对象 |
内嵌模式的协议细节(消息长什么样、方法白名单、错误怎么回)在契约 §4.2,样板是现成的:system/pkgs/skill/ —— pkg.json + embed.py + main.py + 一整套 ui/,能跑、能抄。
什么时候可以不写网页界面
只有这三种:① 必须是个独立的大窗口(比如要铺一片缩略图);② 界面必须用非网页技术(Tk / 原生控件);③ 这个包根本没有界面(命令行 / 托盘 / 后台)。这三种也照样要在 AGENT.md 里写清为什么。
webview.create_window
那是例外,不是默认。默认方案是「网页界面 + ui.embed」—— 你只写一份页面,用户既能在 moss 里当标签页用,也能单独开窗。
内嵌时拖放怎么收
Chromium 不给页面真实路径,路径只能从宿主来(moss.on('drop', …))。这一块有六个坑,单独一页:收拖入。
收拖入
不写 drop = 完全不收拖入。 写了才在拖动时被考虑,而且收文件 / 收文件夹要分开声明 —— 声明窄一点最好。
声明
"drop": {
"label": "算它",
"dir": { "cwd": true },
"file": { "cwd": true, "param": true, "base": true, "ext": ["png", "jpg"] }
}
| 键 | 说明 |
|---|---|
label | 投放框上的字,默认「运行」 |
file / dir | 收文件 / 收文件夹(false = 不收这一种)。用不上就别写 |
cwd | 把它当工作目录;拖的是文件 = 文件所在的目录 |
param | 路径追加到参数串末尾 |
quote | 加双引号,默认 true(不加引号的 C:\a\b.ico 过 shlex 会变成 C:ab.ico) |
base | 只给文件名(配合 cwd) |
ext | 只收这几种扩展名(小写不带点)。不写 = 什么都收;[] 或 "*" 也 = 什么都收。文件夹不看它 |
drop 也可以是一个数组,表示一次拖入跑多条命令。
两条硬要求
- 能收文件夹就够的,别写
file——tree/mark/sync只收文件夹。声明窄一点,拖动时列表才干净,不该收的东西根本不会来烦你。 - 声明必须跟代码一致 ——
img的ext就是它代码里那份支持格式清单,多写一个就是把处理不了的文件塞进来白跑。
内嵌界面里收拖放:三个最容易翻车的点
dataTransfer.files
Chromium 不给页面真实路径。路径只从宿主来:moss.on('drop', d => d.items)。
内嵌时宿主会铺一层罩子接住这一拖,于是 ① 你会收到一次假的 dragleave;② 你再也收不到 drop。
规矩:moss.on('captured') 之后别信 dragleave;moss.on('dragend') 和 moss.on('drop') 里都要把提示收掉。只写 dragleave / drop 的页面,症状就是「松手之后提示一直挂在那儿」。
内嵌模式有方法白名单:页面会调的每一个方法,后端都要在协议里认得,否则页面拿到的是一个错误,而不是数据。
自查顺序
- 先按
moss --spec里的拖放那张表逐条对一遍。 - 真起一个后端进程(
MOSS_UI_EMBED=1),把页面会调的每个方法都发一遍。 - 真渲染页面(无头浏览器),按宿主真实的顺序发
captured→dragend→drop:注意dragend前面不一定有dragleave,断言别依赖它。 - 最后在 moss 里手动拖一次真文件 —— 自动化探针过了不代表真机过。
调试与验收
moss 的验收标准很朴素:跑得通、退出码 0。下面是查问题的顺序。
四把钥匙
| 命令 | 什么时候用 |
|---|---|
moss --selfcheck | 整体不对劲:路径、runner、插件、运行时 |
moss --resolve <名字> | 「为什么跑的是它 / 为什么没找到」 |
moss --spec | 不确定某个字段怎么写 |
moss --history | 忘了上次怎么调通的 |
退出码
插件自己的退出码会原样传上来;宿主自己的错误有自己的码(比如 --snap 那套:2 用法错、3 没东西可做、4 出错)。脚本里判断「成功」就认 0。
moss :hello 你好 2
echo $LASTEXITCODE # PowerShell:0 = 成功
日志放哪
- 插件自己的日志 →
MOSS_PLUGIN_LOG_DIR(就是数据根下的logs/)。 - 界面型插件初始化失败 → 宿主会把「卡在哪一步 + 异常原文」写进
user_data/state/<id>/ui-error.log,页面上也报中文原因。 - 运行记录 → 命令行选项卡里能回看,历史在
user_data/history。
界面型包最常见的三个坑
- JS 里一个小错,整个界面白屏:加
window.onunhandledrejection和try/catch把「卡在哪一步」写出来,别只弹一句英文。 - 先切模式再弹对话框:用户一取消,状态已经变了,界面就回不去了。规矩是挑到东西才改状态。
- 焦点 / hover 事件只绑一次:下拉框在「收起再点开」时常常不再触发
focus,因为 mousedown 被preventDefault了。补一次 click 检查。
验收清单
moss :<id> <典型参数>退出码 0,输出是你要的。- 不带参数时的行为也想过了(开界面?打用法?用剪贴板?)。
- 非法参数不会崩,给的是人话。
- 数据只写在
MOSS_PLUGIN_DATA_DIR里,包目录没被写脏。 - 要界面的:
ui.embed声明了,内嵌和独立窗口两种都打开试过。 - 要拖放的:真的拖一个文件进来试过,不是只跑了单元测试。
AGENT.md写了:怎么调、副作用、失败模式。
目录结构
发行包只有两样东西:moss.exe 和 system\。user_data\ 是软件第一次运行时自己生成的,不用你建,也不随包发。
moss\ ← 你解压出来的文件夹(比如 D:\soft\moss)
├─ moss.exe 宿主内核(解析命令行、找解释器、装插件、开界面)
├─ system\ ★ 跟发行版一起走(升级时整体替换)
│ ├─ ui\ 宿主自己的界面(不属于任何插件,删不掉)
│ ├─ pkgs\ 出厂插件(只读层)
│ ├─ spec\ 插件契约 —— 格式的唯一真相
│ ├─ skills\ 给 AI 看的技能手册
│ ├─ ai\ 技能库(宿主和 :skill 插件共用)
│ ├─ runtimes\ 自带 Python 运行时
│ ├─ option\ 设置面板的规则与清单
│ ├─ icon\ 图标
│ ├─ bootstrap.py 用自带运行时跑插件时的引导
│ └─ runners.json 执行方式(加一门语言只改这个文件)
└─ user_data\ ← ★ 第一次运行自动生成(打包时整体排除)
├─ pkgs\ 你自己装的插件
├─ state\ 各插件的数据、技能、界面错误日志
├─ backups\ 版本快照(内容寻址,改坏了能退)
├─ history\ 运行历史
├─ shortcuts\ 短命令(moimg 这类)
├─ ai\ AI 授权(skills_allow.json)
├─ resolve\ 解析缓存
├─ trash\ 回收站
├─ config.toml 设置(每项都带注释)
└─ snapshot.ignore 快照的过滤规则
就三个东西,各归各的
| 目录 | 谁的地盘 | 升级时 |
|---|---|---|
core/(编进 exe) | 宿主 | 换新 |
system/ | 官方 | 整体替换 |
user_data/ | 你的 | 不碰;同名插件还会压住出厂版 |
同名时高优先级遮蔽低优先级:user_data/pkgs/img 存在时,你跑 moss :img 得到的是你那份,出厂原件仍在 system/pkgs/img 里 —— 可对比、可还原。
放哪儿
| 位置 | 行不行 | 为什么 |
|---|---|---|
D:\soft\moss、D:\moss 这类自己的目录 | 推荐 | 有写权限,升级覆盖方便 |
| U 盘 / 移动硬盘 | 可以 | 绿色软件,路径不写进注册表 |
C:\Program Files | 不行 | 它要往自己目录写 user_data,那里默认没权限 |
| 桌面 / 下载目录 | 不建议 | 容易被清理工具扫掉;升级是整包覆盖,放这儿容易乱 |
路径里尽量不要有中文和空格 —— 绝大多数插件不在乎,但少数会去调外部程序的插件会稳一点。
为什么压缩包里没有说明书和开发文档
为了目录清爽:用户打开文件夹只需要认两个东西 —— moss.exe 和 system\。所以根目录不放 README.md、使用说明.txt、插件开发向导.md 这类人看的文档;完整文档在这个站点上(会更新),软件里每个插件的详情页也有用法。
AI 干活时读的是:system/spec/插件契约.md(也就是 moss --spec 打印的那份)、system/skills/ 里的技能手册、以及每个插件自己的 AGENT.md。根目录那几份人看的文档删掉不影响 AI —— 前提是 system\ 完整。
插件的数据在哪
插件的运行数据按 id 算,不跟着层走:不管这个插件住在 system/pkgs 还是 user_data/pkgs,它的数据都在 user_data/state/<id>/。所以在两层之间搬插件,数据不会丢。
moss --layer img # 看 img 现在在哪一层
moss --layer img user # 搬到 user_data/pkgs(升级不再碰它)
moss --layer img system # 搬回出厂层(升级会替换成官方新版)
搬家与备份
整个文件夹拷走就行 —— 路径不写进注册表。只想备一份「我的东西」,拷 user_data/ 即可。
命令大全
不用记,moss 不带参数就打印用法和插件列表。这里是全量整理,方便扫。
运行
| 命令 | 说明 |
|---|---|
moss <名字> | 在当前目录(及向上几层)找匹配的脚本并运行 |
moss b/m · moss s/t/main | 逐级往下找 |
moss /<名字> | 同上,但不切换目录 |
moss ~<名字> | 后台脱离运行,不阻塞当前控制台 |
moss :<别名> [参数] | 运行一个已安装的插件 |
moss --resolve <名字> | 只看解析结果,不执行 |
界面
| 命令 | 说明 |
|---|---|
moss ui | 打开界面(双击 exe 也是这个) |
moss --ui | 同上,写全的形式 |
目录与历史
| 命令 | 说明 |
|---|---|
moss cd <目标> | 智能找目录并切过去 |
moss cd --install / --uninstall | 装 / 卸 PowerShell 包装函数(屏幕上不留回显) |
moss --history | 方向键选,回车重跑(去重,最多 20 条) |
moss --history <条数> | 只打印不交互 |
插件管理
| 命令 | 说明 |
|---|---|
moss --new <id> [name=… desc=… kind=…] | 生成一个能跑的插件骨架 |
moss --remove <别名> | 卸载插件:本体 + 数据(永久删除,需确认;-y 跳过) |
moss --rename <旧> <新id> | 改名:目录 / 数据目录 / 清单里的 id 一起改 |
moss --layer <包> [user|system] | 在两层之间搬插件目录 |
moss --alias [sync|status] | 短命令对账 / 看现状 |
moss --spec | 打印插件契约 |
AI
| 命令 | 说明 |
|---|---|
moss --ai-config | 看当前那组参数(key 默认打码) |
moss --ai-config-reveal | 同上,但 key 给全文 |
moss --ai-profile-set <组> k=v… | 加 / 改一组(base_url / api_key / model) |
moss --ai-active <组> | 选当前用哪一组 |
moss --ai-profile-del <组> | 删一组 |
moss --ai-rename <旧> <新> | 改名(整块搬,key 不用再读一遍) |
moss --ai-presets | 常见服务商的预制参数 |
moss --ai-test [组] | 测试连接(流式发一个最小请求,报状态码和第一个增量) |
moss --ai-models [组] | 获取可用模型清单 |
moss --ai-balance [组] | 查询余额(支持已知接口的服务商) |
moss --ai-import-legacy | 把旧配置文件导成一组 |
版本快照
完整用法见 版本快照与回退。会动磁盘的动作都要 --yes。
| 子命令 | 说明 |
|---|---|
list / save / show | 列出 / 存一次 / 看清单 |
diff / revert / restore | 对比 / 一键对比+回退 / 恢复 |
grep / cat / ls | 在快照里搜内容 / 看单个文件 / 列文件 |
export / rm / gc | 导出 / 删一次 / 回收无引用内容块 |
tags / group / presets | 看标签 / 这个对象会落到哪个分组 / 看过滤命中 |
ignore / check | 看过滤规则 / 按指纹校验 |
诊断
| 命令 | 说明 |
|---|---|
moss --selfcheck | 自检:安装根 / runner 表 / 插件 / 自带运行时 / cd 注入 |
moss | 用法 + 插件列表 |
位置参数(按 params 顺序)或 key=value 都行。带空格的参数建议用 key=value 或插件自己的写法 —— 有些 rest 通道会把引号吃掉。
AI 服务配置
AI 功能要你自己提供一组模型接口:地址、密钥、模型名。可以存多组来回切 —— 便宜的用来干杂活,强的用来写插件。
在界面里配
设置 → AI,三个视图:
- 列表:已存的所有组,标出当前用的是哪一组。
- 选服务商:九宫格里挑一个预制模板(地址自动填好),或者手动填基地址。
- 编辑:名字 / 接口地址 / 密钥 / 模型。三项都填了才保存得了。
改完点「测试连接」,它会流式发一个最小请求,回状态码和第一个增量 —— 通了就是真通了,不用等真干活才发现 key 写错。
预制服务商
| 服务商 | 说明 |
|---|---|
| DeepSeek | 官方 API |
| Gemini | Google(OpenAI 兼容口) |
| OpenAI | ChatGPT 系列 |
| Kimi | 月之暗面 |
| 通义千问 | 阿里(兼容模式) |
| 智谱 | GLM |
| 硅基流动 | 聚合平台 |
| 本地 Ollama | 本机 127.0.0.1:11434,不用 key |
任何 OpenAI 兼容的接口都能用 —— 选中「自定义」把基地址填成 https://…/v1 就行。
命令行配
moss --ai-presets # 看有哪些预制
moss --ai-profile-set 主 force=1 base_url=https://api.deepseek.com/v1 api_key=sk-… model=deepseek-chat
moss --ai-active 主 # 切到这一组
moss --ai-test 主 # 测试连接
moss --ai-models 主 # 拉可用模型清单
moss --ai-balance 主 # 查余额
moss --ai-config # 看当前配置(key 打码)
model 可以写多个,逗号隔开(界面上是一个下拉可以切)。
关于密钥
现在就是明文写在 user_data/config.toml 里,没有加密。所以:别把这个文件夹发给别人;备份文件(config.toml.bak-…)同样含明文密钥。
请求直连你自己填的那个接口,不经过任何中间服务器。哪次 AI 干活要联网,只有这一条路。
花多少钱
AI 工作台会逐年轮记 token(usage),你可以在界面上看到它这一轮用了多少。想省钱:
- 杂活用便宜模型,写插件再切强的;
- 一句话把需求说清楚,别让它来回猜;
- 它只在「写 / 改插件」时才调模型,平时跑插件不联网、不花钱。
常见问题
先用搜索框找找,没有的话进群问(页面底部有两个群二维码)。
装和用
要装 Python 吗?
不用。自带独立的 Python 3.12(system/runtimes/python),插件都用它跑,不依赖你机器上的 Python,也不会弄乱原有环境。
要装 Node 吗?
不用 —— 只有 dsh 那个远程桥插件需要 node + @deepseek-ai/dsh。别的插件和宿主都不需要。
Windows 提示「未知发布者」怎么办?
没有代码签名。点「更多信息」→「仍要运行」即可。它不联网(AI 除外)、不写注册表、不写系统目录。
升级会不会弄丢我的东西?
不会。升级只覆盖 moss.exe 和 system\,user_data\ 一个字都不动。
能装在 Program Files 吗?
不建议。它要往自己目录写 user_data,那里没权限。放 D:\Tools\moss 这类自己的目录。
手机 / 平板能用吗?
不能。这是 Windows 桌面软件,只支持 Windows 10 / 11(64 位)。
AI
AI 会不会乱改我的东西?
四道闸:① 每次动手改文件之前先存「动手前」快照,改坏了能退回;② 它能读写哪些目录是一张清单,没勾的地方读都读不了;③ core\、moss.exe、venv\、.git\、备份库是硬锁,勾了「任意路径」也写不了;④ 它给自己新造的工具(技能)要你点一次「允许」才生效。
为什么第一次用技能要我先点「允许」?
因为「声明」不等于「授权」。AI 自己也能写文件,如果把插件里的 expose 当授权,就等于让它自己给自己发通行证。这一次点击是设计,不是待修的 bug。
AI 说做完了,但我不满意怎么办?
继续说就行,它会接着改;想推倒重来就在文件管理里回到那条「动手前」快照。它的验收标准是「真机跑通、退出码 0」,所以先去看命令行选项卡里那次运行。
必须联网吗?
只有让 AI 写插件 / 造工具时要联网(调你自己配的模型接口)。跑现有插件、管文件、快照回退这些全程离线可用。
为什么压缩包里没有使用说明 / README?
为了目录清爽 —— 根目录只该有 moss.exe 和 system\,用户一眼知道点哪个。文档在这个站点上(会更新),每个插件的详情页也有它自己的用法。AI 也不需要这几份文件,它读的是 system/spec 和 system/skills(见 目录结构)。
一定要用命令行吗?
不。装好双击就是界面,点插件、拖文件、点运行。命令行是可选的那一半 —— 给愿意用键盘的人。
插件
插件会不会互相影响?
不会。每个插件一个独立目录,数据按 id 分开放,拔掉一个不影响别的。它自己长出来的插件和原生插件用同一份契约,没有区别。
能写 Go / PowerShell 插件吗?
能。runner 支持 python / go / html / exe / shell / powershell。加一门新语言只改 system/runners.json,不用动宿主代码。
怎么把一个插件分享给别人?
把它在 user_data/pkgs/<id> 的整个目录打包发过去,对方解压到自己的 user_data/pkgs/ 即可。注意别把密钥写进 pkg.json。
卸载插件会删我的数据吗?
moss --remove 会连数据一起删(永久,需要确认)。只想暂时不用,就把目录挪走或在界面里关掉。
排障
终端里敲 moss 提示找不到命令?
没加进 PATH。设置 → 命令接入 → 勾上「把 moss 加进系统 PATH」,然后新开一个终端(旧窗口不会自动刷新环境变量)。
界面打不开 / 白屏?
先 moss --selfcheck。界面型插件白屏的话,看 user_data/state/<id>/ui-error.log,里面写着卡在哪一步和异常原文。
双击 exe 弹了一下黑框就没了?
那是控制台窗口在启动阶段被藏起来,正常。要是真的没反应,用终端跑 moss.exe --selfcheck 看报错。
改坏了怎么退回?
文件管理 → 版本快照 → 选「动手前」那条 → 回退。命令行里是 moss --snap revert :<插件>。详见 版本快照与回退。
边界与版本
这些是「现在的样子」,不是承诺。写在这里是为了让你少猜。
已经能用
- Windows 单机,不需要装任何服务、不写注册表(
path插件改 PATH 除外,那是它的本职)。 - 插件:多语言、可插拔、能内嵌界面、能收拖入、能被 AI 调。
- AI:写插件、改插件、造技能、跑验证;带快照、权限清单、硬锁。
- 版本快照:存 / 比 / 回退 / 导出 / 在快照里搜内容。
- 绿色便携:整个文件夹拷走就是搬家。
还没有 / 还不完整
| 项 | 现状 |
|---|---|
插件契约 spec 1 | 没到 1.0,契约还会动。以 moss --spec 为准 |
托管密钥(keyRef) | 宿主还没实现 —— 别指望它替你管凭据 |
capabilities 声明 | 会读,但不据此拦截(真正生效的是 AI 的目录权限清单) |
verify / accepts / env.elevated | 只是占位字段,将来才实现 |
| 插件商店 / 在线分发 | 没有。现在靠目录拷贝和群 |
| 非 Windows 平台 | 没有。Linux / macOS 不在计划内 |
| 手机端 | 没有,也不会有 —— 它是桌面软件 |
会变的地方
你写的插件不用怕这些变化:环境变量是对外承诺,宿主的内部布局(state/ 下怎么分目录、快照怎么存)随时可能改,所以插件里请只认环境变量。
想要新功能
最好的方式不是等发版 —— 打开 moss,直接跟它说。它读契约、写插件、跑一遍验证,然后交给你验收。真要落到出厂里,再让它进 system/pkgs。
遇到问题、想要个新插件,进群说一声更快:QQ 群 / 微信群二维码。