@mafdet/cli
v0.4.13
Published
Mafdet AI command line — model calls, account queries, and a permissioned local agent.
Maintainers
Readme
@mafdet/cli
Mafdet AI 官方命令行:模型调用、账户查询,以及一个受权限约束的本地 Agent。
0.4.13。 英文版见 README.md。
Mafdet AI 是什么
Mafdet AI 是一个 OpenAI 兼容的 AI 网关:一把 API Key、一个 base URL,即可调用多家 提供方的模型,按量计费。任何支持 OpenAI API 格式的工具或 SDK,只要把 base URL 和 Key 换成 Mafdet AI 的就能用。
- Base URL:
https://api.mafdet.ai/v1 - 端点:
/chat/completions、/embeddings、/audio/speech - 提供方:Google(Gemini)、Kimi(月之暗面)、Qwen、DeepSeek
- 截至 2026 年 8 月共 30 个模型。目录会变动,以带实时价格的
models.json为准 - 文档:https://docs.mafdet.ai
环境要求
Node.js 20 或更高,以及一把 Mafdet API Key。
安装
npm install -g @mafdet/cli交互式会话
在终端里不带参数直接敲 mafdet 就进入会话:选认证方式、选模型,然后和 Agent 对话。
mafdet # 只读
mafdet --allow-write # 改动先展示 diff 再确认
mafdet --allow-command "pnpm test"它和 mafdet agent 是同一个 Agent,只是画法不同——同样的工具、同样的审批、
同样的计费。参数含义与那边完全一致,两个都不给的会话根本没有加载写工具和命令工具。
管道里、--json 下、CI 中、或带 --no-tui 时,mafdet 的行为与 0.3 完全一样:
用法写 stderr、exit 2。任何读取 CLI 输出的程序都不会突然收到一个界面。
快速开始
export MAFDET_API_KEY="sk-mafdet-live-..." # 只从环境变量读,没有 --api-key 参数
export MAFDET_MODEL="deepseek-v4-flash"
mafdet doctor # 一次查完 Key、端点、模型、余额、工作区、git
mafdet models list # 这把 Key 实际能调用的模型
mafdet chat "解释一下 TypeScript 装饰器"
mafdet account balancePowerShell
$env:MAFDET_API_KEY = "sk-mafdet-live-..."
$env:MAFDET_MODEL = "deepseek-v4-flash"
mafdet doctor所有用户可见输出都是纯 ASCII,因此在非 UTF-8 代码页的控制台(Windows PowerShell 5.1
的默认状态,中文系统为 cp936)也不会变成乱码。管道传入 prompt 也可以,但 PowerShell
默认按 UTF-16 走管道——建议直接把 prompt 作为参数,或用 --input <文件>。
⚠️ macOS 与 Windows 尚未验收。Windows 上的命令超时终止已改用 taskkill /T,
但从未在真 Windows 上执行过——它是否真的能杀到每一个孙子进程,属于未验证,
不是「已修复」。
命令
| 命令 | 作用 |
| ---------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------- |
| mafdet doctor | 一次检查 Node、两个端点、Key、工作区、git、模型与工具调用能力、余额 |
| mafdet models list [--surface chat\|embeddings\|speech] [--agent] | 这把 Key 能调用的模型,并附上目录元数据 |
| mafdet models show <id> | 单个模型详情,含 Agent 是否可以驱动它 |
| mafdet chat [提示词] [--model] [--system <文件>] [--input <文件>] [--no-stream] [--quiet] [--save] | 发一次对话,默认流式 |
| mafdet embed [文本\|<文件>] --model <id> [--output <文件>] | 文本转向量 |
| mafdet speech [文本\|<文件>] --model <id> --output <文件> --voice <音色> | 文本转音频 |
| mafdet agent "<目标>" [--allow-write] [--allow-command "<前缀>"] | 本地 Agent,默认只读 |
| mafdet agent(不带目标,需终端) | 交互会话 |
| mafdet account balance\|usage\|subscription | 钱包、用量、套餐(需 account:read scope) |
| mafdet sessions list\|show\|usage\|delete | 保存在这台电脑上的会话(不会调用模型) |
| mafdet sessions usage <id> --refresh | 在本地估算旁显示账单账本的已结算金额 |
| mafdet config list | 当前生效的配置及其来源 |
查询类命令都支持 --json(输出一份完整 JSON 文档)与 --plain(无表头的 TSV,方便
cut、awk 处理)。
Agent 的权限模型
默认只读:可以列目录、搜索、读文件、看 git status/diff,不能改任何东西。
--allow-write 才会加上 edit_file 与 create_file,并且:
- 每次改动都先展示 diff 再询问,回车即拒绝;
- 非交互环境(无终端)一律不给写权限,即使显式传了
--allow-write——此时写工具 根本不会提供给模型; - 没有任何"全部同意"的开关(无
--yes、无--force、无--allow-secrets)。
--allow-command "<前缀>" 才会加上 run_command,且只对这些命令前缀免提示。
pnpm test 放行 pnpm test --filter web,但不放行 pnpm publish。
没有 shell:命令被切成 argv 直接执行,所以管道、重定向、;、&&、反引号、$( )
一律被拒绝并说明原因,而不是尝试"消毒"。
批准一条命令意味着什么。 意味着允许那个程序在你的机器上、以你的身份运行。
工作区限定的是 Agent 自己的工具能碰哪些文件,它不是沙箱,也约束不了那个程序。
pnpm test 可以读你的家目录、可以联网、可以起一个比这次运行活得更久的进程——
因为那就是"运行一个程序"的含义。我们会拒绝一小份明显破坏性的名字
(sudo、su、doas、chown、shutdown、reboot,以及按路径调用),
但请把它当作防手滑的护栏,而不是隔离。批准前缀时,请按"我自己会不会直接敲这条命令"来判断。
会话内命令:/model、/permissions、/diff、/cost、/compact、/exit。
配置
优先级:--model > MAFDET_MODEL > 项目配置 > 用户配置 > 默认值。
mafdet config list 会告诉你最终是哪一层生效的。
| 环境变量 | 默认值 |
| -------------------- | --------------------------- |
| MAFDET_API_KEY | (必填) |
| MAFDET_MODEL | 无 |
| MAFDET_BASE_URL | https://api.mafdet.ai/v1 |
| MAFDET_BACKEND_URL | https://backend.mafdet.ai |
用户配置位置:Linux ~/.config/mafdet/config.json、macOS
~/Library/Application Support/mafdet/config.json、Windows %APPDATA%\mafdet\config.json。
项目可以放一个 .mafdet.json,但只允许 model 一项。
里面出现凭据或端点会直接报错:克隆一个仓库绝不该改变你的 Key 发往哪里。
退出码
脚本可以据此分支;一旦发布,某个值的含义就不再改变。
| 码 | 含义 |
| --: | -------------------------------------- |
| 0 | 成功 |
| 1 | 一般失败(含 doctor 有检查项未通过) |
| 2 | 命令行或配置错误 |
| 3 | 认证或权限失败 |
| 4 | 模型或 API 不可用 |
| 5 | 本地权限被拒绝 |
| 6 | 用户取消 |
| 7 | 达到费用、时间或轮次上限 |
兼容性承诺
如果你要在脚本里依赖它,这里说明什么会变、什么不会。
同一大版本内:
- 退出码的含义不变,某个码也不会被挪作他用;
--json里的字段保持名称、类型与含义;只会新增字段——所以请按字段名解析, 忽略不认识的键;--plain的列顺序固定,始终是无表头的 TSV。
这是 0.x 版本,按 semver 次版本号仍允许破坏性变更。我们不打算动用这个余地, 任何调整都会写进 CHANGELOG.md;1.0.0 起正式生效。
金额一律是整数微美元,--json 里以字符串给出,避免任何解析器把它变成浮点。
请不要去解析给人看的美元数字。
输出约定
结果与机器可读的 JSON 走 stdout;进度、警告、摘要、审批提示走 stderr。
所以 mafdet chat ... > answer.txt 只会拿到答案本身,而
mafdet models list --json | jq 永远能解析。
凭据
Key 只从 MAFDET_API_KEY 读取。刻意不提供 --api-key 参数——那会把 Key 写进
shell 历史和进程列表。Key 不会被写入任何配置文件,也不会完整出现在任何输出里,
只以 sk-mafdet-live-<redacted> 这样的指纹形式展示。
保存下来的会话
交互式会话会保存在这台电脑上,也只在这台电脑上。它们不会上传,Mafdet 没有副本。
这是关于存档的说法,不是关于模型的。你发送的消息仍然会送到 API 去作答, 仍然出现在你的用量记录里;留在本地的是那份保存下来的副本。
存放位置:
| 平台 | 路径 |
| ----------- | --------------------------------------------------- |
| Linux / WSL | ${XDG_STATE_HOME:-~/.local/state}/mafdet/sessions |
| macOS | ~/Library/Application Support/mafdet/sessions |
| Windows | %LOCALAPPDATA%\mafdet\sessions |
这些文件是明文。一份存档包含你的消息、模型的回复,以及 agent 工作时读过的任何源码——
所以请把会话目录当作它所对应的那个项目本身来对待。API Key 是唯一的例外:
所有内容在写盘前都会过一遍脱敏,你粘进提示词里的 Key 会以 <redacted> 的形式存下来。
Linux 与 macOS 上文件权限 0600、目录 0700;Windows 没有等价的权限位,
继承 %LOCALAPPDATA% 的 ACL。
Agent 读不到这些目录——即使你在家目录里运行 mafdet、存档实际落在工作区内也一样。
否则在 A 项目里工作的 agent 就能读到 B 项目会话的完整明文。
关闭与清理:
mafdet --no-history # 这一次运行完全不记录
mafdet chat "..." --save # 一次性对话只有明确要求时才保存
mafdet sessions list # 已保存的会话;没有时会告诉你路径
mafdet sessions delete <id> --yes一次 mafdet chat 不是有人会去恢复的对话,所以不带 --save 就不保存。
--no-history 仍然压过 --save;被 Ctrl+C 取消的对话完全不保存——
半个回复是比没有更糟的记录。
--no-history 什么都不留——不留空目录,也不留索引条目。
删除会移除本地消息和这个会话的用量映射。它不会移除 API 用量、账单记录或扣费: 那些属于 API Key,不属于存档。而且它只是一次普通的文件删除,不是安全擦除—— 备份、快照和文件系统本身仍可能保有副本。
对账:这次会话到底花了多少
mafdet sessions usage <id> 从本地记录累加一次会话的用量。那些金额是估算,
算法与网关一致,但是在本地算的。
--refresh 会按会话时间窗口和游标查询当前 API Key 的用量事件;request ID 只留在本地,
用于匹配账本返回的记录。它不发送消息、标题、工作区路径或 session ID。由另一把 API Key
支付的请求会被跳过并如实告知,好让你知道该换那把 Key 再跑一次。
账本还没跟上的请求保持 pending,从未拿到 ID 的请求保持 unknown。
两者都绝不显示为 0——0 读起来像「免费」。
费用与取消
chat 会在 stderr 打印 token 数与预估费用,算法与网关计费一致,但以钱包为准。
Ctrl+C 会关闭连接。实测中被取消的流式请求没有产生费用,但这是一次观察结果、不是保证:
如果上游已经生成完毕,仍可能产生扣费。因此取消后的摘要标注为 partial:。
