mslxdff 0.1.158 → 0.1.159

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/package.json +3 -3
  2. package/docs/ARCHITECTURE.md +0 -426
  3. package/docs/FEATURE_TREE.md +0 -164
  4. package/docs/MOBILE.md +0 -82
  5. package/docs/adr/0001-reasoning-content-injection.md +0 -14
  6. package/docs/adr/0002-models-free-filter.md +0 -12
  7. package/docs/adr/0003-zero-state-no-auth.md +0 -10
  8. package/docs/adr/0004-bearer-token.md +0 -18
  9. package/docs/adr/0005-peer-mesh.md +0 -53
  10. package/docs/adr/0006-broadband-member.md +0 -103
  11. package/docs/adr/0007-multi-provider-prefix.md +0 -25
  12. package/docs/adr/0008-share-keys-to-peers.md +0 -52
  13. package/docs/adr/0009-chat-repl.md +0 -37
  14. package/docs/adr/0010-allowlist.md +0 -36
  15. package/docs/adr/0011-broadband-stream.md +0 -30
  16. package/docs/adr/0012-responses-endpoint-codex-sync.md +0 -48
  17. package/docs/adr/0013-node16-compat.md +0 -41
  18. package/docs/adr/0014-deepseek-provider.md +0 -52
  19. package/docs/adr/0015-upstream-probe-routing.md +0 -50
  20. package/docs/adr/0016-model-capabilities.md +0 -27
  21. package/docs/adr/0017-ai-sdk-upstream-engine.md +0 -59
  22. package/docs/adr/0018-zen-client-identity.md +0 -52
  23. package/docs/adr/0019-share-keys-always-lend.md +0 -59
  24. package/docs/adr/0020-zen-free-lane-agent-shape.md +0 -52
  25. package/docs/adr/0021-usage-report-jsonl.md +0 -47
  26. package/docs/adr/0022-models-capability-merge.md +0 -61
  27. package/docs/adr/0023-key-provider-default-direct.md +0 -58
  28. package/docs/adr/0024-node18-baseline.md +0 -63
  29. package/docs/adr/0025-workbuddy-authdir-follows-state.md +0 -71
  30. package/docs/adr/0026-cline-provider-id-unify.md +0 -67
  31. package/docs/adr/0027-codearts-provider.md +0 -48
  32. package/docs/adr/0028-traework-provider.md +0 -34
  33. package/docs/adr/0029-qoder-native-provider.md +0 -60
  34. package/docs/adr/0030-models-list-scoped-by-picks.md +0 -53
  35. package/docs/adr/0031-qoder-true-streaming.md +0 -50
  36. package/docs/adr/0032-generic-responses-channel.md +0 -72
  37. package/docs/adr/0033-cline-allowlist-auto-sync.md +0 -75
  38. package/docs/adr/0034-request-level-human-readable-observability.md +0 -60
  39. package/docs/adr/0035-sdk-channel-headers-timeout.md +0 -49
  40. package/docs/adr/0036-qoder-per-request-sticky-account.md +0 -82
  41. package/docs/adr/0037-qwenwork-independent-provider.md +0 -82
  42. package/docs/adr/0038-zcode-provider.md +0 -140
  43. package/docs/agents/domain.md +0 -51
  44. package/docs/agents/issue-tracker.md +0 -30
  45. package/docs/agents/triage-labels.md +0 -15
  46. package/docs/cli_help.md +0 -1391
  47. package/docs/plans/bench-via-latency-2026-09-01.md +0 -215
  48. package/docs/plugins.md +0 -187
package/docs/cli_help.md DELETED
@@ -1,1391 +0,0 @@
1
- # mslxdff CLI 完整参数手册
2
-
3
- > **活文档**:本文件与 `bin/mslxdff.js`、`docs/ARCHITECTURE.md §6` 同为单一事实源。
4
- > **新增或改动任何 CLI 参数,必须同步更新本文件**(否则视为未完成)。检查:`npm run docs:check` 会校验 `ARCHITECTURE.md` 的 CLI 表与实现一致,本文件需人工保持与之同步。
5
- > 适用版本:`>=0.1.79`(含 WorkBuddy 供应商 + 端点可配 `modelsPath`/`chatPath` + `provider <id> models` 直查 + `provider <id> bench` 测速 + `bench --via` 直连 vs 经 peer 延迟对比 + opencode 匿名 hermes 优先 + cline free 单一源与启动自检;**2026-09-24:Cline 白名单 auto-sync(上游列表=真相)**;**2026-09-25:`timeline.log` 人读时间线 + 按模型链路日志 `<provider>-<model>.log` + SDK 通道 headers 超时**)。最后更新:2026-09-25。
6
-
7
- ## 目录
8
-
9
- - [0. 约定与通用规则](#0-约定与通用规则)
10
- - [1. 速览表](#1-速览表)
11
- - [2. 启动与生命周期](#2-启动与生命周期)
12
- - [3. 状态与诊断](#3-状态与诊断)
13
- - [4. 认证与端口](#4-认证与端口)
14
- - [5. 模型管理](#5-模型管理)
15
- - [6. 供应商 Provider](#6-供应商-provider)
16
- - [7. 外部同步(WorkBuddy / opencode)](#7-外部同步workbuddy--opencode)
17
- - [8. 群组网络](#8-群组网络)
18
- - [9. 帮助](#9-帮助)
19
- - [附录 A 环境变量](#附录-a-环境变量)
20
- - [附录 B 状态文件](#附录-b-状态文件)
21
- - [附录 C 退出码与提示](#附录-c-退出码与提示)
22
- - [附录 D 交互式终端说明](#附录-d-交互式终端说明)
23
-
24
- ---
25
-
26
- ## 0. 约定与通用规则
27
-
28
- - **单/双横线等价**:`-status` 与 `--status` 完全等价(源码用 `args.includes("-status") || args.includes("--status")` 判断)。下表只写一遍,别名在“别名”列注明。
29
- - **大小写敏感**:模型 id、group 名、`-provider` 的 id 均大小写敏感。
30
- - **参数顺序**:多数命令不强制顺序,但子命令紧跟主参数(如 `-provider openrouter list` 的 `list` 必须在 id 之后)。
31
- - **TTY 判定**:部分命令在 `process.stdin.isTTY && process.stdout.isTTY` 时进入交互式(` -models`/ `-provider <id>` 空参),非 TTY(管道/脚本/CI)则走非交互分支或报错提示用法。
32
- - **状态持久化**:`token`/`port`/`preferredModel`/`modelPicks`/`providerKeys`/`groupsJoined` 等写入 `MSLXDFF_STATE_FILE`(默认 `~/.config/mslxdff/state.json`,`0600` 权限)。热数据(`modelErrors`/`modelLatencies`/`peerErrors`)500ms 批量刷盘,冷数据立即落盘。
33
- - **裸 `PORT` 被忽略**:只认 `MSLXDFF_PORT` 环境变量或 `-port N` 持久化,不读裸 `PORT`(见 `AGENTS.md`)。
34
- - **帮助优先级最高**:只要参数中出现 `-help/--help/-h`,立即打印帮助并 `exit 0`,不执行其他命令。
35
-
36
- ---
37
-
38
- ## 1. 速览表
39
-
40
- | 命令 | 别名 | 作用一句话 | 是否写 state | 是否需 daemon 运行 |
41
- |---|---|---|---|---|
42
- | `mslxdff` | — | 无参:daemon 已在且版本 ≥ 本地→直接显示 status+help(**只升不降**,低版本不会覆盖高版本);否则以后台 daemon 启动并退出(npx 友好) | 否 | 否 |
43
- | `mslxdff -d` | `--daemon` | 以 detached 后台进程启动 daemon,同裸跑有“只升不降”保护(高版本已在跑时不会被低版本覆盖) | 否 | 否 |
44
- | `mslxdff -status` | `--status`, `-s` | 打印 daemon/health/port/config、upstream providers(启用/key/baseUrl/allowlist/共享)、models(free 缓存/preferred/picks + 体检表 avg首字/tps/啰嗦/p95)、autostart/plugins、群组/failover、recent calls(含 ttfb/tps/tok)、last error | 否 | 否(未运行时也打印,health 显示 fail) |
45
- | `mslxdff -log [N]` | `--log`, `-logs`, `--logs` | 显示最近 N 条事件(默认 10)+ `timeline.log` 人读时间线(直连/组员/重试/最终结果/总耗时),并提示其他日志路径(按模型链路日志 `<provider>-<model>.log`、calls/errors/daemon) | 否 | 否 |
46
- | `mslxdff -debug` | `--debug` | 停掉后台 daemon,前台运行并实时打印事件流;Ctrl+C 恢复后台 | 清空旧日志 | 会停旧 daemon |
47
- | `mslxdff -plugins` | `--plugins` | 列出插件目录与已识别插件及其 hooks,不启动 daemon | 否 | 否 |
48
- | `mslxdff -stop` | `--stop` | 停止 daemon | 否 | 需运行 |
49
- | `mslxdff -restart` | `--restart` | 重启 daemon | 否 | 有则重启,无则拉起 |
50
- | `mslxdff -uninstall` | `--uninstall` | 停 daemon 并删除 state/pid/log/calls/errors/events 文件,提示 `npm uninstall -g` | 删文件 | — |
51
- | `mslxdff -port N` | `--port N` | 持久化监听端口到 state,运行中则重启 daemon | 是(`port`) | 重启 |
52
- | `mslxdff -showtoken` | `--showtoken` | 打印当前 Bearer token | 首次会生成 | 否 |
53
- | `mslxdff -refresh-token` | `--refresh-token` | 轮换 token 并打印新值 | 是(`token`) | 否 |
54
- | `mslxdff -update` | `--update` | 查询 npm `latest`,若有新版则 `npm install -g` 并重启 daemon | 否 | 重启若有更新 |
55
- | `mslxdff -models` | — | 交互式多选(↑↓移动 ←→翻页 Space勾选 Enter保存 q取消,候选池=opencode免费池+各供应商allowlist原名/别名+已勾选):空格勾选常用模型,Enter 保存到 `modelPicks`;非 TTY 则等价 ` -model list` 纯列表(含分隔后 allowlist) | 是(`modelPicks`) | 否 |
56
- | `mslxdff -model list [--provider <id>] [--json]` | `-models` 同 | 列出免费模型(每次 4s 超时尝试刷新,失败回退缓存);默认先列 `opencode` 免费池,`────────────────────────────────────────` 分隔后列其他供应商 `allowlist`(原名 + 别名 `别名: dash`,如 `cline/z-ai/glm-5.3-flash (别名: cline-z-ai-glm-5.3-flash)`);`--provider cline` 只看该供应商 allowlist,`--json` 输出 `{"object":"list","data":[...]}` 供脚本 | 否 | 否 |
57
- | `mslxdff -model set <id>` | — | 设默认模型 `preferredModel`,并自动加入 `modelPicks` | 是 | 热重载 |
58
- | `mslxdff -model status` | — | 显示每模型健康状态 normal/limit/error + 时间 + HTTP 码 | 否 | 否 |
59
- | `mslxdff -model refresh` | — | 强制从上游拉取模型列表并更新缓存 | 是(cacheFile) | 否 |
60
- | `mslxdff -model pick <id>` | — | 勾选一个模型到 `modelPicks`(**非空即成为 `GET /v1/models` 对外目录白名单**,ADR-0030) | 是 | 热更新(目录条数随之变) |
61
- | `mslxdff -model unpick <id>` | — | 从 `modelPicks` 移除(该模型随之从 `/v1/models` 消失,`?all=1` 仍可见) | 是 | 热更新 |
62
- | `mslxdff -model picks` | — | 列出当前勾选集(= 对外目录范围;空集表示目录不裁剪、全量暴露) | 否 | — |
63
- | `mslxdff -model pick clear` | — | 清空勾选集(`auto` 回退全量候选 **且** `/v1/models` 回退全量目录) | 是 | 热更新 |
64
- | `mslxdff -provider add <id> <baseUrl> <key> [allow...] [--models-path <path>] [--chat-path <path>]` | `--provider` | 一键添加通用 OpenAI 兼容供应商(`providerConfigs`,前缀路由 `<id>/model`;`--models-path` 如 `/v1/models`、`--chat-path` 如 `/v1/chat/completions` 可配异形路径,`b.ai=/v1/models`、`workbuddy=/console/enterprises/personal/models`;`cline` 内置 `recommended-models`,无需配置) | 是 | 重启生效 |
65
- | `mslxdff -provider add workbuddy https://copilot.tencent.com <key> [allow...]` | `--provider` | 添加 WorkBuddy 专用供应商(`providerConfigs.workbuddy={baseUrl,keys,auths}`,`workbuddy/hy3` 前缀路由,走 `workbuddy-token-auto.js` 自动落盘 `auths`) | 是 | 重启生效 |
66
- | `mslxdff -provider <id> models [--json]` | `--provider` | 列该供应商可用模型(按 `allowlist` 过滤,`--json` 输出 `{"object":"list","data":[...]}`;`workbuddy` 29 个、`cline` 5 个等,无需 `curl`;`cline` 与聚合目录同源走 `recommended-models` 的 `free`(不再列全量 443 个内部目录);表格含能力列:上下文长度(`1M/200k`)+ `📷`读图 `🧠`推理 `🔧`工具调用,workbuddy 走上游原生字段,无此字段的供应商显示 `—`) | 否 | 否 |
67
- | `mslxdff -provider <id> bench [--json] [--prompt <text>] [--max-tokens N] [--timeout N]` | `--provider` | 评估该供应商已勾选模型的速度(TTFB/总耗时/TPS/字/秒,仅测 allowlist ∩ 全局 picks 交集;空则探活 `GET /v1/models→/models` 并提示先 `allowlist set`,`--json` 供脚本) | 否 | 否 |
68
- | `mslxdff -provider <id> bench --via [--include-opencode] [--json] [--samples N] [--timeout N] [--apply]` / `mslxdff -provider bench --via` | `--provider` | **家宽选路**:对比 `direct` vs 经每个在线 `peer` 到同一上游的 `TTFB`(串行省额度,`max_tokens=5 prompt=hi` 轻探针,`--json` 时 `stdout` 纯 JSON `meta/results/advice`、进度走 `stderr`;默认跳过 `opencode` 供应商,需 `--include-opencode` 且 TTY 二次确认 `y/N`,非 TTY 自动跳过;结果不写 `state.json`;空组/全离线空状态引导 ` -group list`;`--apply` 落盘 `via-routes.json` 供网关择路) | 否 | 否 |
69
- | `mslxdff -provider cline login` | `--provider` | Cline WorkOS 设备授权流:浏览器授权 → 自动拿 `refreshToken` 落盘。此后 `cline` 走 `refresh→workos:token` + Cline 指纹头,`deepseek-v4-flash` 不再 `403`(免费通道强制 stream 聚合) | 是 | 重启生效 |
70
- | `mslxdff -provider cline free [--json]` | `--provider` | **只读**:直查上游免费模型目录(`GET /api/v1/ai/cline/recommended-models` 的 `free` 数组,当前 5 个)并列出与当前 `allowlist` 的差异,不写任何 state;`--json` 输出 JSON 供脚本 | 否 | 否 |
71
- | `mslxdff -provider cline free sync [--yes] [--json] [--keep-extra]` | `--provider` | 把上游免费目录同步为 `cline` 的 `allowlist`(写入裸 id 如 `z-ai/glm-5.3-flash`):**默认 dry-run 预览**,加 `--yes` 才落盘;`--keep-extra` 只增不删。**注意**:本命令是**一次性全量替换**(不带 `--keep-extra` 会清空现有 allowlist,连 auto-sync 并入的 clinePass 一起挤掉);daemon 运行期的 auto-sync 则是只增不减(见下节);**上游不可达(走内置兜底)时拒绝写盘**,防把 `FALLBACK_FREE` 落成白名单——与 auto-sync 同一口径:兜底不是上游真相,确需手填改走 `-provider cline allowlist set <id...>` | 是(`--yes` 时写 `providerConfigs.cline.allowedModels`) | 热更新立即生效 |
72
- | `mslxdff -provider cline quota [--json] [--account <hash>] [--model <substr>]` | `--provider` | **只读**:聚合 `cline-usage.jsonl` 的账号×模型双口径统计并分组打印:free 走本周期 tokens(+已完成周期/上周期),pass 走近 24h;`--json` 供脚本,`--account/--model` 过滤;空账本给引导 | 否 | 否 |
73
- | `mslxdff -provider cline migrate [--dry-run]` | `--provider` | 把旧 `providerConfigs.clinebot` 合并进 `cline`(keys 去重 + 剔除 `sk_` 形态、allowlist 求并、baseUrl 归一到 `https://api.cline.bot`)后删除旧键,幂等;真改动前备份 `state.json.bak-<ISO 时间戳>` | 是 | 重启生效 |
74
- | `mslxdff -provider codearts login` / `codearts models [--json]` | `--provider` | 华为云 CodeArts Agent(盘古助手)PKCE 授权:浏览器登录拿 `refreshToken/codeVerifier/dpopJwk` 组凭证 blob 落盘 `providerConfigs.codearts.keys`(一账号一 blob,多账号 keyring 轮转);`models` 三路发现 + benefit claim(幂等),对外 `codearts/<modelId>` 前缀(ADR-0027) |
75
- | `mslxdff -provider traework login` | `--provider` | TRAE SOLO 通道浏览器授权(复刻 traework2api login.sh):打印 trae.cn 授权链接 → 登录后粘贴 `127.0.0.1` 回调链接 → ExchangeToken → 落盘 `auths/trae-<uid>.json`(0600)+ state 双写,自动签到+查积分;此后 `traework/<modelId>` 前缀路由(恒 `stream:true` SOLO SSE 透传/聚合,模型空/auto→`glm-5.2`,动态表+静态 32 回退;1005 plan 长冷却 12h、401 换号、429 短冷,过期前 24h 预刷新);**恒 local-only** 不借出 key |
76
- | `mslxdff -provider qoder login [--region cn\|global]` | `--provider` | Qoder 设备授权(PKCE + poll):打印 `qoder.com/device/selectAccounts` 兑换链接 → 浏览器登录 → 落盘 `auths/qoder-<uid>.json`(0600)+ state 双写,默认 `allowAnyModels=true`;此后 `qoder/<modelId>` 前缀路由(15 模型,恒上游 `stream:true`,多号 keyring 轮转);`--region cn` 走国内站 `qoder.com.cn`(`*.qoder.com.cn` 端点镜像);**恒 local-only** 不借出(ADR-0029) | 是(写 `providerConfigs.qoder`) | 重启生效 |
77
- | `mslxdff -provider qwenwork login` | `--provider` | 千问办公设备授权(PKCE + poll,`gateway.qwenwork.cn`):打印 `device/selectAccounts` 兑换链接 → 浏览器确认授权 → 落盘 `auths/qwenwork-<uid>.json`(0600)+ state 双写,**默认 `allowAnyModels=false` + 只种 `flash/pro/qwen3.8-max-preview` 3 个实测模型**(与 qoder 惯例故意不同:额度是账号积分池、每日回血未实测,防 auto 烧分);登录尾部显示套餐名 + 积分剩余;此后 `qwenwork/<modelId>` 前缀路由(恒上游 `stream:true`,多号 keyring 轮转,401/403 refresh 回写轮换再重试;无身份签名会被判 101,手动配 key 首轮自动补 userinfo);**恒 local-only** 不借出(ADR-0037) | 是(写 `providerConfigs.qwenwork`) | 重启生效 |
78
- | `mslxdff -provider zcode login [--bigmodel]` | `--provider` | ZCode(智谱官方编程工作台)免费额度授权:打印 chat.z.ai 授权链接 → 浏览器登录 → **CLI 轮询**(`oauth/cli/init`→`oauth/cli/poll`,无需回调服务器、**全程免验证码**)拿 zcode JWT,落盘 `auths/zcode-<uid>.json`(0600)+ state keys,并把内置目录(`GLM-5.3`/`GLM-5.3-Flash`/`GLM-5.2`/`GLM-5-Turbo`)只增不减并入 allowlist(开箱可用);此后 `zcode/<modelId>` 前缀路由(Anthropic 网关 `.../zcode-plan/anthropic/v1/messages`,恒上游 `stream:true`,非流式本地聚合;12 项 ZCode 客户端头 + per-account deviceMid);错误分级 401/1006 短冷+重登指引、1005 长冷 1h+`quota_exhausted`、3002/3008/3009/3010 短冷、3007 不冷却直透;`--bigmodel` 切 bigmodel.cn 入口;**恒 local-only** 不借出(ADR-0038) | 是(写 `providerConfigs.zcode`) | 重启生效 |
79
- | `mslxdff -provider zcode quota [--json]` | `--provider` | 查 ZCode 套餐/余量(`GET /api/v1/zcode-plan/billing/balance?app_version=` 按 plans/balances 分组表格:套餐名/状态/有效期 + 各模型 entitlement 余量);空套餐给领取指引、401 给重登指引;`--json` 供脚本 | 否(只读) | 立即生效 |
80
- | `mslxdff -provider <id> set-models-path <path>` | `--provider` | 改 `models` 路径(如 `myapi` 的 `/v1/models`、`workbuddy` 的 `/console/...`) | 是 | 重启生效 |
81
- | `mslxdff -provider <id> set-chat-path <path>` | `--provider` | 改 `chat` 路径(如 `/v1/chat/completions`、`/v2/chat/completions`) | 是 | 重启生效 |
82
- | `mslxdff -provider <id> ...` | `--provider` | 配置需鉴权供应商的 API keys/地址(多 key 轮转、set-url 改地址)及共享开关 | 是 | 重启生效 |
83
- | `mslxdff -provider <id> allowlist ...` | `--provider` | 管理供应商模型白名单(空=阻塞除非 `allowAny on`,非空仅名单内可用,防昂贵模型) | 是 | 热更新立即生效 |
84
- | `mslxdff -provider <id> allowAny on\|off` | `--provider` | 空 allowlist 时放行或阻塞(默认 `OFF`,`opencode` 例外 `ON`) | 是 | 热更新立即生效 |
85
- | `mslxdff -provider <id> del` | `--provider` | 删除整个供应商(清 `providerConfigs`/`providerKeys`,自动重启生效,`opencode` 不可删) | 是(删) | 自动重启 |
86
- | `mslxdff -providers list` | `--providers`, `-provider list` | 列出所有已部署上游供应商(opencode/openrouter/通用/workbuddy)及启用状态(含 allowlist 摘要) | 否 | 否 |
87
- | `mslxdff -provider workbuddy login` | — | WorkBuddy 设备授权加号(浏览器登录→自动轮询 5 分钟→落盘 `auths/workbuddy-<uid>.json` + state,不走抓包,追加新号首选;旧号重复 login 只更新凭证) | 是 | 热加载(网关建议 `-restart`) |
88
- | `mslxdff -provider workbuddy import [--file=路径]` | `MSLXDFF_WORKBUDDY_DESKTOP_INFO=路径` 显式指定(`MSLXDFF_WORKBUDDY_UA` 覆盖设备 UA 指纹,`CODEBUDDY_BIN=路径` 指定抓包用 CLI) | 从本机桌面端登录态直接导入:自动发现 `workbuddy-desktop.info`(Win 走 `%LOCALAPPDATA%`/`%APPDATA%`,mac 走 `~/Library/Application Support`,Linux 走 `~/.config`,按文件名搜、多命中取最新;找不到时用 `--file`/env 显式指定),桌面已切新号时最快,无需浏览器/抓包 | 是 | 热加载 |
89
- | `mslxdff -workbuddy checkin` | `-wb checkin`, `--workbuddy checkin` | WorkBuddy 每日签到 100 credits(多号并行 3,双域 `POST /v2/billing/meter/daily-checkin` 幂等,`code 10001 已签到` 视为成功;`--json` 聚合 `total/dailyPacks/nextExpire`,`workbuddy-checkin.js` 代理,`node workbuddy-token-auto.js` 已自动触发;daemon 默认每日 09:00 自动全号签到,`MSLXDFF_WORKBUDDY_CHECKIN=0` 关) | 否 | 否 |
90
- | `mslxdff -workbuddy growth [--json] [--codes a,b] [--account <uid>]` | `-wb growth`, `--workbuddy growth` | WorkBuddy 成长任务全自动:拉列表→参与(accept)→触发(免费模型+`extra_vars.growthEvent`)→领奖(claim),串行(任务间 1.2s、账号间 1s),已领取幂等跳过;可自动 = `chat_5`/`automation_1`/`skill_1`/`Model_chat_GLM5.2`,需客户端任务标 MANUAL 不发包(`growth-plans.js` 未登记默认 MANUAL);daemon 默认每日 09:30 自动,`MSLXDFF_WORKBUDDY_GROWTH=0` 关,`_GROWTH_HOUR` 改时间,`_GROWTH_MODEL` 换触发模型(默认 `hy3`);`--codes` 只做指定任务 | 否 | 否 |
91
- | `mslxdff -workbuddy travel [--json]` | `-wb travel` | WorkBuddy 猫猫旅行:无猫自动同意协议 + 领养(+300,门槛未达自动补一次对话解锁);到站领奖 / 空闲派出(location 4)/ 旅行中跳过;`--json` 输出分步 steps 与 `credits` | 否 | 否 |
92
- | `mslxdff -workbuddy balance [--json]` | `-wb balance` | WorkBuddy 多号余额总览(`total/dailyPacks/nextExpire/fetchedAt`,`workbuddy-balance.js` TTL 5min) | 否 | 否 |
93
- | `mslxdff -workbuddy list` | `-wb list` | 列出已接入 WorkBuddy 账号(`uid/domain/enterpriseId`) | 否 | 否 |
94
- | `mslxdff -workbuddy remove <uid> [--keep-file]` | `-wb remove` | 按 `uid`(全等或前缀 6 位)摘除账号(删 `keys/auths` 与 `auths/workbuddy-<uid>.json`,清 `balanceCache`) | 是 | 重启生效 |
95
- | `mslxdff -setto workbuddy [modelId]` | `--setto` | 设默认模型并原子写入 `~/.workbuddy/models.json`(仅 127.0.0.1/v1;`picks` 非空时自动摘除未在 picks 的失效本地条目,非本地条目永不动) | 是 | 热重载 |
96
- | `mslxdff -setto chatgpt [modelId]` | `--setto`(`codex` 等价) | 设默认模型并写入 Codex/ChatGPT 三端共用 `~/.codex/config.toml`(`model_providers.mslxdff` → `http://127.0.0.1:<port>/v1` + Responses API,鉴权走 `mslxdff -showtoken` 命令不落盘) | 是 | Codex 重启/reload 生效 |
97
- | `mslxdff -free` | `--free`, `-free-check`, `--free-check` | V2EX 白嫖雷达(仅 V2EX 单源):拉 `latest.json + hot.json` 按 `白嫖|限免|免费额度|注册送|羊毛` 过滤 | 否 | 否 |
98
- | `mslxdff -enable-autostart` | `--enable-autostart` | 开机自启:注册 Windows 任务计划 / Linux systemd user(重启后自动拉起) | 否 | 否 |
99
- | `mslxdff -disable-autostart` | `--disable-autostart` | 关闭开机自启 | 否 | 否 |
100
- | `mslxdff -autostart status` | `--autostart status` | 查看自启状态(已启用/未启用 + 任务/注册表路径) | 否 | 否 |
101
- | `mslxdff -timezone [set <tz>\|clear\|status]` | `--timezone`, `-tz`, `--tz` | 时区配置:默认 `Asia/Shanghai`,可设 `UTC`/`America/New_York` 等(`MSLXDFF_TZ` 环境变量临时覆盖,`state.json: timezone` 持久化) | 是(`timezone`) | 否 |
102
- | `mslxdff -free-watch` | `--free-watch` | V2EX 白嫖雷达 watch 模式(每 5 分钟轮询,前台常驻) | 否 | 否 |
103
- | `mslxdff -setto opencode [modelId\|--all]` | `--setto` | 把本地网关注册为 opencode 供应商(`provider.mslxdff`,`http://127.0.0.1:<port>/v1`,模型直写裸名如 `deepseek-v4-flash-free`,`/` 自动转 `-` 如 `bai/deepseek`→`bai-deepseek` 到达 8989 自动还原,`--all` 批量同步全部 `modelPicks`;`picks` 非空时自动摘除未在 picks 的失效模型;**自动附模型能力**(models.dev 目录:推理档位/📷读图/tool_call/上下文长度/价格,写入 opencode Model 形状字段,opencode 原生识别;`workbuddy/` 模型不在目录 → 走上游原生字段兜底(上下文/读图/推理默认档),其余未收录模型仅写名称不影响使用;旧格式条目自动升级注入(缺 variants 的也补)) | 是(`opencode.json`) | 热重载 |
104
- | `mslxdff -creategroup <name>` | `--creategroup`, `-group create <name>` | 在本节点创建群组(组名即密码,本节点为 leader) | 是(`groups`+`groupsJoined`) | 否 |
105
- | `mslxdff -addtogroup [<host> [<name>]] [--broadband]` | `--addtogroup` | 以成员身份加入远端 leader 的群组;`--broadband` 为宽带中继模式(不占端口,只出站);**省略参数进手机宽带接入向导**(问组长地址+组名,自动起服务并回报出口 IP) | 是(`groupsJoined`) | 否 |
106
- | `mslxdff -group sync` | `--group sync` | 刷新所有已加入群组的成员列表到本地 failover peers | 否 | 否 |
107
- | `mslxdff -group leave <name>` | — | 成员侧离开单群组(本地移除) | 是 | — |
108
- | `mslxdff -group list` | — | 列出本节点群组与成员(带健康探测与序号,宽带显示 via leader) | 否 | — |
109
- | `mslxdff -group remove <seq>` | — | 仅 leader:按 `list` 序号踢出成员(1-based,排除 leader) | 是(`groups`) | 需 leader |
110
- | `mslxdff -leavegroup` | `--leavegroup`, `-leave-groups` | 成员侧离开所有已加入群组(跳过 leader 组并提示用 `-delgroup`) | 是 | — |
111
- | `mslxdff -delgroup <name>` | `--delgroup` | 仅 leader:解散本节点领导的群组 | 是 | 需 leader |
112
- | `mslxdff -resetban [ip]` | `--resetban` | 清除加群失败封禁(全清或按 ip) | 是(`bans`) | 否 |
113
- | `mslxdff -use-group [on\|off]` | `--use-group` | opencode 失败时是否走组员网络(默认 on;`off` 则所有供应商仅本机,不走 via-route/hedge/peer/broadband 组员中继;带 key 上游默认恒直连(ADR-0023,仅 opencode 走组员;`MSLXDFF_USE_GROUP_KEYS=1` 可开回,cline/workbuddy 仍硬禁);`MSLXDFF_USE_GROUP` 环境变量可覆盖) | 是(`useGroup`) | 热重载(下次请求生效) |
114
- | `mslxdff -chat ["prompt"]` | `--chat` | 对话终端:`mimo-v2.5-free → big-pickle → 本地网关 auto:8989` 三级兜底(前两者直连 `https://opencode.ai/zen/v1/chat/completions`,失败自动切本地 `http://127.0.0.1:8989/v1/chat/completions` 的 `auto` 择优,含多供应商/hedge/peer),模糊匹配由模型完成,历史持久化超长压缩,仅拦 -uninstall,daemon 重启不影响 | 是(`chat-history.json`) | 否(独立进程) |
115
- | `mslxdff -help` | `--help`, `-h` | 打印帮助 | 否 | 否 |
116
-
117
- ---
118
-
119
- ## 2. 启动与生命周期
120
-
121
- ### `mslxdff`(无参)
122
-
123
- - **语法**:`mslxdff`
124
- - **作用**:最常用的“裸跑”入口。**只升不降**:
125
- 1. 若 daemon 已在运行且版本 ≥ 本地(`isPidAlive(pid)` 且 `compareSemver(runningVersion, VERSION) >=0`):直接复用,打印 `printStatus()` + `printHelp()` 后退出,不另起进程(低版本不会覆盖高版本,提示 `keeping vX (not downgrading)`)。
126
- 2. 若 daemon 版本 < 本地或未运行:以后台 detached 进程启动/升级 daemon(`stopDaemonIfOutdated()` 仅当 `VERSION > runningVersion` 才停旧),等待 `/health` 就绪(4s),打印 `vX.Y.Z started as a background daemon`、`endpoint`、`log`、`pid` 后退出。**绝不驻留终端**,npx 友好。
127
- - **示例**:
128
- ```bash
129
- mslxdff
130
- # → mslxdff v0.1.57 listening ... / 已运行时显示 status
131
- ```
132
-
133
- ### `-d` / `--daemon`
134
-
135
- - **语法**:`mslxdff -d` 或 `mslxdff --daemon`
136
- - **作用**:显式以后台 daemon 启动。同样 **只升不降**:先 `stopDaemonIfOutdated()`(仅 `VERSION > runningVersion` 才停旧),若已在跑更高版本则 `keeping` 并复用,不另起降级。`spawn detached` 后等待健康检查。
137
- - **环境**:子进程带 `MSLXDFF_DAEMON=1` 标记;父进程等待后打印 `daemon started (pid XXX)`、`log`、`pid`。
138
- - **与其他参数混用**:`startDaemon` 会过滤掉 `-d/--daemon` 再透传其余参数(如 `-port 8989`)。
139
- - **示例**:
140
- ```bash
141
- mslxdff -d
142
- mslxdff --daemon -port 9090
143
- ```
144
-
145
- ### `-port N` / `--port N`
146
-
147
- - **语法**:`mslxdff -port <N>` / `mslxdff --port <N>`
148
- - **作用**:持久化监听端口到 `state.json` 的 `port` 字段。
149
- - **行为**:
150
- - 校验:必须为整数 `1..65535`,否则 `invalid port` 并 `exit 1`。
151
- - 若 daemon 正在运行:`setPort(port)` → `stopDaemon()` → `startDaemon(["-port", String(port)])` → 等健康 → 打印 `restarted on port N (pid XXX)` 与 `endpoint`。
152
- - 若未运行:仅 `setPort(port)`,打印 `port saved: N (daemon not running; takes effect on next start)`。
153
- - **优先级**:`state.json port` > `MSLXDFF_PORT` env > 默认 `8989`(`src/server.js resolvePort()`)。裸 `PORT` 忽略。
154
- - **示例**:
155
- ```bash
156
- mslxdff -port 8989
157
- mslxdff -port 9090
158
- ```
159
-
160
- ### `-stop` / `--stop`
161
-
162
- - **语法**:`mslxdff -stop`
163
- - **作用**:停止后台 daemon(`stopDaemon()` 读 `daemon.pid` 并 `kill`)。
164
- - **输出**:
165
- - 成功:`mslxdff daemon stopped (pid XXX)`
166
- - 未运行:`mslxdff daemon not running`(若有原因则带 `reason`)。
167
- - **示例**:`mslxdff -stop`
168
-
169
- ### `-restart` / `--restart`
170
-
171
- - **语法**:`mslxdff -restart`
172
- - **作用**:重启 daemon(`stopDaemon()` → `startDaemon([])` → 等 `/health` 4s)。`daemon 已在运行` 时先停再起,`stale pid` 时清旧 pid 后拉起,`未运行` 时直接拉起。结束打 `restarted as a background daemon (pid XXX)` + `endpoint`/`log`/`pid`。
173
- - **示例**:`mslxdff -restart`
174
-
175
- ### `-uninstall` / `--uninstall`
176
-
177
- - **语法**:`mslxdff -uninstall`
178
- - **作用**:先尝试 `stopDaemon()`,再删除以下文件(`rmSync {force:true}`):
179
- - `stateFile`(`MSLXDFF_STATE_FILE`)
180
- - `pidFile()`、`logFile()`(daemon pid 与日志)
181
- - `<daemonDir>/calls.log`、`errors.log`、`events.log`
182
- - **输出**:`removed N file(s): ...` 或 `no state/log files to remove`,最后提示:
183
- ```
184
- package still installed — finish with:
185
- npm uninstall -g mslxdff
186
- ```
187
- - **注意**:不会自动执行 `npm uninstall -g`,需手动完成。
188
-
189
- ### `-update` / `--update`
190
-
191
- - **语法**:`mslxdff -update`
192
- - **作用**:自更新到 npm 已发布的最新版。
193
- - **流程**:
194
- 1. `npm view mslxdff dist-tags.latest --json` 查询 `latest`(Windows 走 `npm.cmd` + `shell:true`)。
195
- 2. 解析版本号,`compareSemver(latest, VERSION) <= 0` 则 `already up to date` 退出。
196
- 3. 否则 `npm install -g mslxdff@latest`(`stdio: inherit`),若 daemon 在运行则 `stopDaemon()` → `startDaemon([])` → 等健康 → `restarted (pid XXX)`。
197
- - **示例**:`mslxdff -update`
198
-
199
- ---
200
-
201
- ## 3. 状态与诊断
202
-
203
- ### `-status` / `--status` / `-s`
204
-
205
- - **语法**:`mslxdff -status` / `mslxdff --status` / `mslxdff -s`
206
- - **作用**:只读聚合展示当前节点全貌(不启 daemon)。依次打印(v0.1.60 起大幅增强,原仅 daemon/模型/调用):
207
- - `mslxdff vX.Y.Z`、`daemon` 是否运行(pid + uptime + version ok)、`endpoint` + `health` 探活(ok/fail + ms)、`config`(port 来源 persisted/env/default + state/log 路径 + bind host)
208
- - `upstream providers`:所有已配置供应商(`opencode` 恒 enabled 无需 key + `openrouter`/`workbuddy`/通用 `providerConfigs.<id>`),每行 `●/○ enabled/disabled keys/acc allow baseUrl 共享` + 备注(测试桩/缺 key 等),0 enabled 时提示加 `mslxdff -provider add` 或 `node workbuddy-token-auto.js`(`providerRows` 聚合 `loadProviderConfigs+Keys+BaseUrl+Allow`,workbuddy `k-new` stub 特殊识别)
209
- - `models`:`models.json` free 数 + 缓存时间/age、`preferred`(含 avg首字/tps/次数)、`picks`(勾选集,空=全量)、`free list`(每模型 `fmtStatus` + avg首字/tps/次数,<10ms 视为测试数据隐藏)、`模型体检 TopN`(`modelStats` 按次数排序的 `avg首字/总耗时/速度/啰嗦/样本/p95`,<10ms 隐藏,仅样本>0)
210
- - `autostart`:`getAutostartStatus()` 的 `detail` + `task/unit`,含 `mslxdff -autostart status` 提示
211
- - `plugins`:`resolvePluginDirs+loadPlugins` 的 `plugins.length` 与每插件 `name@version [hooks]` 或 `none` 提示
212
- - `joined groups`:每个已加入群组的 `name`、`leaderUrl`、成员列表(同前)
213
- - `failover targets`:`peers.all()` 列表(同前),空时提示 `failover: (none — 加组后自动出现)`
214
- - `groups on this node`:本节点作为 leader 创建的组名
215
- - `recent calls:`:`(gateway 持久化,最近5条;token/速度看 mslxdff -stats)` 头 + avg + 每行 `ts/model/status/dur[stream][auto]`(来自 `recentCalls(5)`,只打印 calls.log 真实存在的字段——token/首字/速度不进 calls.log)
216
- - `last error`:`lastError()` 的 `ts/model/status/message`(无则 `none — 暂无错误`)
217
- - 额外:`auth token: use mslxdff -showtoken` + `health: http://127.0.0.1:<port>/health` 或 `not running — start with: mslxdff -d` + `hints: mslxdff -providers list · mslxdff -model status ...`
218
- - 体验:空状态有明确提示(如 `models: not cached yet`、`recent calls: (none yet — 发一次请求后出现)`、`plugins: (none) — 放 *.mjs ...`),`health` 探测失败有 `health fail (…)`,测试桩 `workbuddy k-new` 标 `disabled (测试桩…)`
219
- - **实现**:`createGroupsService` + `loadGroupsJoined`,成员通过 `refreshGroupMembers`(1.5s 超时)拉取。
220
- - **示例**:`mslxdff -status`
221
-
222
- ### `-stats` / `--stats`(模型用量报表)
223
-
224
- - **语法**:`mslxdff -stats [--hours N] [--json] [--model <id>]`
225
- - **作用**:打印近 `N` 小时(默认 24,上限 168)的模型用量。默认分成 `Token 用量` 与 `响应性能` 两张自适应边框表:`Token 用量` 每行 `模型 / 请求 / 输入 / 输出 / 思考 / 合计`,`响应性能` 每行 `模型 / 首字 / 总耗时 / 速度`,两张表均含 `合计`;长模型 id 自动扩列,不截断、不挤乱其他列。
226
- - **口径**:速度 = 输出 tokens ÷ 生成耗时(总耗时 − 首字),**按窗口加权**(`Σ输出 ÷ Σ生成耗时`),不是每请求速度的算术平均(短回答会把算术均值拉飞);只统计成功请求(status 200)。普通表格中的大 token 用 `k/M` 缩写,`--json` 保留精确整数。
227
- - **数据来源**:`<logDir>/usage/YYYY-MM-DD.jsonl` 逐请求 JSONL(写 `src/usage/record.js`,聚合 `src/usage/report.js`)。**与 `state.json` 的 `modelStats` 终生 EMA 是两套数据**——EMA 服务排序与 `-status`/`-model stats`,本报表服务时间窗口。
228
- - **保留期**:默认 2 天(覆盖 24h 窗口 + 跨天边界),按日删旧文件;`MSLXDFF_USAGE_KEEP_DAYS=N` 调整。查询窗口超过保留期时会显示“历史可能不完整”警告。
229
- - **开关**:`MSLXDFF_USAGE_LOG=0` 完全关闭采集(此时 `-stats` 只打印关闭提示)。
230
- - **不含/未展开**:`-chat` 直连 `mimo-v2.5-free`/`big-pickle` 不经 8989 网关,不计入;失败请求(非 200)无 usage 也不计入,所以“请求数”是成功请求数。表格展示当前聚合层的全部字段,但不展开逐请求 `via`、`interrupted` 和单次 `tps` 明细。
231
- - **示例**:`mslxdff -stats` · `mslxdff -stats --hours 1` · `mslxdff -stats --json`(脚本用)· `mslxdff -stats --model opencode/big-pickle`
232
-
233
- ### `-log [N]` / `--log [N]` / `-logs [N]` / `--logs [N]`
234
-
235
- - **语法**:`mslxdff -log [N]`(`N` 可选正整数,默认 `10`)
236
- - **作用**:显示最近 `N` 条事件(读 `eventsFile()`,`recentEvents(count)`),并打印:
237
- - `log dir: ...`、`events: ...`
238
- - `--- last X event(s) ---` + 每行 `fmtEvent(e)`(见 `fmtEvent` 的 `type` 分支)
239
- - `timeline.log` 同步显示最近 N 条人读时间线(每请求一行:直连、组员、重试、最终结果、总耗时)
240
- - 按模型链路日志在同一个日志目录:`<provider>-<model>.log`(如 `ocgo-muse-spark-1.3-contributor.log`),每个请求逐阶段记录 request/route/upstream/peer/relay/result/client-response 与安全摘要;**事件面用黑名单**(默认全部可见,只排除噪声 `peer-health`/`heartbeat` 与敏感面 `client-session`/`upstream-probe*`),决定类事件只渲染登记过的标量字段(`status`/`reason`/`pick`/`cooled` 等),payload 与正文一律不落;上游回显 `upstream=<host>`/`account=<uid|region>`/`pick=new|sticky|switch|forced`/`cooled=<status>`;不落 prompt/响应正文/凭据
241
- - 当 `count <= 10` 时额外提示 `hint: mslxdff -log 100 | calls: ... errors: ... daemon: ...`
242
- - **参数解析**:`args[ idx+1 ]` 转 `Number`,仅当整数且 `>0` 时取用,否则默认 10。
243
- - **示例**:
244
- ```bash
245
- mslxdff -log # 最近 10 条
246
- mslxdff -log 100 # 最近 100 条
247
- mslxdff --logs 50
248
- ```
249
-
250
- ### `-debug` / `--debug`
251
-
252
- - **语法**:`mslxdff -debug`
253
- - **作用**:进入前台调试模式,实时跟随事件流。
254
- - **流程**:
255
- 1. **Linux 且 systemd 自启服务 active** 时先 `systemctl --user stop mslxdff`(unit 配了 `Restart=always`,仅 SIGTERM 停掉的 daemon 会被 systemd 3 秒后拉起、抢端口并反杀 debug 前台)
256
- 2. `stopDaemon()` 停旧 daemon(若有则打印 `[debug] stopped background daemon (pid XXX)`)
257
- 3. 清空旧日志:`eventsFile()`、`callsFile()`、`errorsFile()`、`logFile()` 写空(计数并打印 `已清理旧日志 X 个文件 (dir),本次会话干净输出`)
258
- 4. 打印 `--- live (Ctrl+C: stop debugging and restore background daemon) ---`
259
- 5. 置 `MSLXDFF_DEBUG=1`、`MSLXDFF_DAEMON=1`,**不退出**,落入 daemon 主体(`bus.subscribe(e => console.log(fmtEvent(e)))`)
260
- 6. 监听 `SIGINT/SIGTERM`:先释放端口 → Linux 优先 `systemctl --user start mslxdff`,否则 `startDaemon([])` → `setTimeout(exit,300)`
261
- - **注意**:`-debug` 会清日志,适合排障时“干净输出”。退出调试务必 `Ctrl+C`,不要直接 `kill -9`。调试会话内 **auto-update 自动跳过**(否则启动 30 秒后的版本检查发现新版会把自己升级重启掉)。
262
- - **示例**:`mslxdff -debug`
263
-
264
- ### `-plugins` / `--plugins`
265
-
266
- - **语法**:`mslxdff -plugins`
267
- - **作用**:不启动 daemon,仅列出插件目录与已识别插件。
268
- - **输出**:
269
- ```
270
- plugin dirs:
271
- [official (bundled)] /path/to/pkg/plugins
272
- [user] ~/.config/mslxdff/plugins
273
- hello@1.0.0 [server:start] (user)
274
- 我的第一个 mslxdff 插件
275
- load error: bad.mjs — ...
276
- ```
277
- 无插件时提示:`(no plugins — drop *.mjs files into a dir above, see docs/plugins.md)`。
278
- - **目录解析**:`resolvePluginDirs({ pkgRoot })` 返回 `[pkg/plugins, userPlugins]`;若 `MSLXDFF_PLUGINS_DIR` 设了则只扫该目录。
279
- - **示例**:`mslxdff -plugins`
280
-
281
- ---
282
-
283
- ## 4. 认证与端口
284
-
285
- ### `-showtoken` / `--showtoken`
286
-
287
- - **语法**:`mslxdff -showtoken`
288
- - **作用**:打印当前 `Authorization: Bearer <token>` 的 token 明文(`loadToken()`)。
289
- - **副作用**:若 `state.json` 尚无 token,会生成一个(`randomBytes(32).hex`)并落盘。
290
- - **示例**:`mslxdff -showtoken`
291
-
292
- ### `-refresh-token` / `--refresh-token`
293
-
294
- - **语法**:`mslxdff -refresh-token`
295
- - **作用**:轮换 token(`refreshToken()` 生成新 64 hex 并 `createdAt` 落盘),打印新 token。
296
- - **影响**:所有客户端需更新 `Authorization` 头;daemon 热重载下次请求即生效。
297
- - **示例**:`mslxdff -refresh-token`
298
-
299
- ### `-port N` 参见 [2. 启动与生命周期](#2-启动与生命周期)
300
-
301
- ---
302
-
303
- ## 5. 模型管理
304
-
305
- > 模型 id 说明:裸 id(如 `big-pickle`)恒指默认供应商 `opencode`;带前缀的 id(如 `openrouter/google/gemma-3-27b-it:free`)路由到对应供应商。`auto` 为特殊 id,表示“让 mslxdff 自动选最快可用模型”。
306
-
307
- ### `-models`(交互式多选,TTY 专属)
308
-
309
- - **语法**:`mslxdff -models`(无子命令)
310
- - **作用**:交互式勾选常用模型集合 `modelPicks`。`modelPicks` 为空表示“不筛选,全量 auto”。候选池 = `opencode` 免费池 + 已启用供应商 `allowlist` 原名(`provider/raw`) + 已勾选的遗留 picks(便于取消;provider 不存在或未启用如缺 baseUrl 的不再列出,启用后自动回来,`status --all` 仍可审计),`allowAny ON` 的供应商无 allowlist 时不在候选池(提示用 `allowlist set` 限制或 `provider models` 看 live)。
311
- - **交互**(仅 TTY):
312
- - `↑/↓` 移动光标,`Space` 勾选/取消,`Enter` 保存,`q/Esc` 取消(`picks 不变`)。
313
- - 初始光标在当前首选模型 `getPreferredModel()` 所在行;已勾选项带 `picked` 标记(含 `cline/...` 等 allowlist 原名)。
314
- - 保存:`saveModelPicks([...result])`,打印 `saved N picked model(s): ...` 或 `(none — auto uses full list)`。结果集恒为列表内勾选项(`items ∩ initialPicked` 起步),不在列表中的失效 picks 随保存自动从 state 移除;取消则原样保留。
315
- - 取消:`cancelled — picks unchanged`。
316
- - **非 TTY 行为**:等价于 ` -model list` 的纯列表分支(opencode 在上 + `────────────────────────────────────────` 分隔后 allowlist 原名/别名,带 `*` 标注已勾选),不进入交互。
317
- - **前置**:每次执行都会 4s 超时尝试刷新模型列表(`tryRefreshModels()`),成功则用新列表,失败回退 stale 缓存。
318
- - **示例**:
319
- ```bash
320
- mslxdff -models
321
- # → ↑/↓ 移动 Space 勾选 Enter 保存
322
- ```
323
-
324
- ### `-model list` / `-models`(非交互列表,支持按供应商过滤)
325
-
326
- - **语法**:`mslxdff -model list` 或 `mslxdff -models`(非 TTY);`mslxdff -model list --provider cline` 只看该供应商 allowlist(`cline/z-ai/...` + 别名);`--json` 输出 `{"object":"list","data":[...]}` 供脚本
327
- - **作用**:列出当前代理对外暴露的免费模型(已过滤,仅 free)。每次都尝试刷新,4s 超时失败则回退缓存;无缓存且刷新失败则 `no cached models and refresh failed`。默认输出分两段:`opencode` 免费池在上,`────────────────────────────────────────` 分隔后为其他供应商 `allowlist`(原名 + 别名,如 `cline/z-ai/glm-5.3-flash (别名: cline-z-ai-glm-5.3-flash)`)。`--provider cline` 时直接展示该供应商 allowlist(原名+别名),`workbuddy` 等 `allowAny ON` 时提示“allowlist 空=放行全部,live 列表用 `mslxdff -provider workbuddy models`”。
328
- - **输出**:
329
- ```
330
- 8 free model(s) (cached 2026-08-30 20:35) (9 picked, * = picked):
331
-
332
- ── opencode (8) ──
333
- * big-pickle
334
- deepseek-v4-flash-free
335
- ...
336
-
337
- ────────────────────────────────────────
338
- 其他供应商 (allowlist,原名 + 别名) (2 providers):
339
-
340
- ── workbuddy (allowAny ON ...) baseUrl=https://copilot.tencent.com ──
341
- (未设 allowlist,全部放行) 查看 live: mslxdff -provider workbuddy models
342
-
343
- ── cline (allowlist 3 ...) baseUrl=https://api.cline.bot/api/v1 ──
344
- * cline/z-ai/glm-5.3-flash (别名: cline-z-ai-glm-5.3-flash)
345
- ```
346
- - **示例**:
347
- ```bash
348
- mslxdff -model list | cat # 全量
349
- mslxdff -model list --provider workbuddy # 只看 workbuddy
350
- mslxdff -model list --provider workbuddy --json # 脚本用
351
- mslxdff -provider workbuddy models # 同义,直查该供应商(推荐)
352
- ```
353
-
354
- ### `-model set <id>`
355
-
356
- - **语法**:`mslxdff -model set <id>`
357
- - **作用**:设默认(首选)模型 `preferredModel`,并**自动加入**勾选集(`modelPicks` 去重)。
358
- - **校验**:`<id>` 不能为空;`normalizeModel(raw)` 为空则报错。
359
- - **输出**:
360
- ```
361
- default model set to: <id> (daemon hot-reloads on next request)
362
- picked: <id1>, <id2> (auto will pick within these)
363
- ```
364
- - **示例**:
365
- ```bash
366
- mslxdff -model set big-pickle
367
- mslxdff -model set openrouter/google/gemma-3-27b-it:free
368
- ```
369
-
370
- ### `-model status`
371
-
372
- - **语法**:`mslxdff -model status`
373
- - **作用**:显示每模型的健康状态(读 `loadModelErrors()` + `models.json` 缓存的并集)。
374
- - **状态值**:`normal` / `error` / `limit` 等(`e.status`),兼容旧版纯 `number`(时间戳)存储。
375
- - **输出**:`id status (MM-DD HH:MM:SS) HTTP code`
376
- - **示例**:`mslxdff -model status`
377
-
378
- ### `-model refresh`
379
-
380
- - **语法**:`mslxdff -model refresh`
381
- - **作用**:强制从上游拉取最新免费模型列表并更新缓存(`cacheFile: <logDir>/models.json`,`refreshMs: 0`)。
382
- - **成功**:`refreshed: N free model(s)` + 每行 id。
383
- - **失败**:`could not refresh models: ...` 并 `exit 1`。
384
- - **示例**:`mslxdff -model refresh`
385
-
386
- ### `-model pick <id>` / `-model unpick <id>` / `-model picks` / `-model pick clear`
387
-
388
- - **语法**:
389
- ```bash
390
- mslxdff -model pick <id> # 勾选一个模型(去重)
391
- mslxdff -model unpick <id> # 取消勾选一个模型
392
- mslxdff -model picks # 列出当前勾选集
393
- mslxdff -model pick clear # 清空勾选集,auto 回退全量
394
- ```
395
- - **作用**:非 TTY 场景下管理 `modelPicks`。`saveModelPicks([...new Set([...loadModelPicks(), id])])`。
396
- - **输出**:
397
- ```
398
- picked: a, b (auto will pick within these)
399
- # 或
400
- picked: (none) (auto uses full list)
401
- # 或
402
- picks cleared — auto uses the full model list again
403
- ```
404
- - **候选约束**:`src/auto.js candidates()` 先按勾选集过滤候选池;勾选全失效(全被冷却/下线)则自动回退全量。
405
- - **示例**:
406
- ```bash
407
- mslxdff -model pick big-pickle
408
- mslxdff -model pick openrouter/google/gemma-3-27b-it:free
409
- mslxdff -model picks
410
- mslxdff -model unpick big-pickle
411
- mslxdff -model pick clear
412
- ```
413
-
414
- ### 模型连通性探活(给 `mslxdff -chat` 的 Agent 看,禁止幻觉命令)
415
-
416
- > **唯一正确方式**:`curl` 直连本机网关 `POST /v1/chat/completions`,自动带 `Authorization: Bearer $(mslxdff -showtoken)`。**严禁** `mslxdff "hi" --model X` / `mslxdff --model X "hi"` / `mslxdff -chat --model X` 等不存在的命令(CLI 无此参数,执行只会输出 `mslxdff vX.Y.Z` 状态页)。
417
-
418
- - **语法(curl 工具)**:
419
- ```json
420
- {
421
- "url": "http://localhost:8989/v1/chat/completions",
422
- "method": "POST",
423
- "headers": {"Content-Type":"application/json"},
424
- "body": "{\"model\":\"<provider/模型>\",\"messages\":[{\"role\":\"user\",\"content\":\"hi\"}],\"stream\":false}"
425
- }
426
- ```
427
- 等价 shell:
428
- ```bash
429
- curl -H "Authorization: Bearer $(mslxdff -showtoken)" \
430
- -H "Content-Type: application/json" \
431
- http://127.0.0.1:8989/v1/chat/completions \
432
- -d '{"model":"cline/z-ai/glm-5.3-flash","messages":[{"role":"user","content":"hi"}],"stream":false}'
433
- ```
434
- - **模型 id 精确匹配**:`cline/z-ai/glm-5.3-flash`、`cline/deepseek/deepseek-v4-flash`、`workbuddy/hy3` 等必须与 `mslxdff -providers list` / `GET /v1/models` 的 `id` 完全一致(含前缀)。
435
- - **结果判读**:
436
- - `200 + x-mslxdff-via: local` → 通,`body` 含 `choices[0].message.content` 与 `usage.cost`
437
- - `401 {"error":"Unauthorized"}` + `www-authenticate: Bearer` → 本机 token 陈旧(`state.json` 改动后未重启),提示用户 `mslxdff -stop && mslxdff` 重启 daemon
438
- - `403 {"error":"model not allowed…"} + x-mslxdff-allowlist:1` → `allowlist` 未放行,需 `mslxdff -provider <id> allowlist add <model>` 或 `allowAny on`
439
- - `429/5xx` → 上游限流/故障,走冷却与转发兜底
440
- - **与 `-chat` 的区别**:`-chat` 的 `mimo-v2.5-free → big-pickle → gateway auto:8989` 是**前两者直连上游** `https://opencode.ai/zen/v1/chat/completions`(`createUpstreamClient`,`globalThis.fetch`,`keepAlive:false`),均失败则**自动切本地网关** `http://127.0.0.1:8989/v1/chat/completions` 的 `auto`(含多供应商择优/hedge/peer,含 `workbuddy/cline` 等),`mimo→pickle` 阶段不经网关;探活 `cline/*` / `workbuddy/*` 必须经本地网关 `curl`,不能用 `run_command` 拼 CLI。
441
- - **反例(禁止)**:
442
- ```bash
443
- mslxdff "hi" --model cline/z-ai/glm-5.3-flash # ❌ 输出 status 页
444
- mslxdff --model cline/z-ai/glm-5.3-flash "hi" # ❌ 同上
445
- mslxdff -chat --model cline/z-ai/glm-5.3-flash # ❌ -chat 无 --model 参数
446
- ```
447
-
448
- ---
449
-
450
- ## 6. 供应商 Provider
451
-
452
- > 多供应商架构(ADR-0007 + 通用 0.1.59):`opencode` 为默认供应商(裸 id,向后兼容,恒启用,无 key);其他供应商带 `<provider>/` 前缀(如 `openrouter/google/gemma:free` / `myapi/gpt-4`),按前缀路由并在转发前剥回原始 id。已实现 `openrouter`(匿名可拉 `GET /api/v1/models`,chat 必须有 key)与通用 OpenAI 兼容供应商(`providerConfigs.<id>={baseUrl,keys}`,`mslxdff -provider add <id> <baseUrl> <key>` 一键添加)。
453
-
454
- > **⚠️ 生效说明(有点别扭但很快)**:`providerConfigs` 的结构性改动(`add` / `set-url` / `set-*-path` / `clear` / 新增 key 覆盖)在 daemon 启动时一次性创建连接池与 `KeyRing`,**需 `mslxdff -restart`(<1s)才生效**,否则仍走旧实例,可能命中 `401` 或冷却。`allowlist` / `allowAny` / `model set` / `model pick` 为热更新,无需重启。未来可能做热重建,目前重启最稳妥 —— 虽有点别扭但成本极低,体验像游戏读档。
455
-
456
- ### `-provider add <id> <baseUrl> <key>` / `-provider <id> [key...|add|remove|list|clear|set-url]`
457
-
458
- #### 通用语法
459
-
460
- ```bash
461
- mslxdff -provider add <id> <baseUrl> <key> # 一键添加通用 OpenAI 兼容供应商
462
- mslxdff -provider <id> [key...|add|remove|list|clear|set-url]
463
- # 别名:--provider
464
- # id 归一化:toLowerCase + 非字母数字转 _
465
- # 特殊:opencode / oc 恒提示“无需 key、永不共享”并直接退出
466
- ```
467
-
468
- #### 无子命令(交互式隐藏输入)
469
-
470
- - **触发**:`mslxdff -provider <id>` 且 `isTTY`。
471
- - **行为**:提示 `Enter <id> API keys, one per line (input hidden). Blank line to finish:`,逐行 `rl.question`(隐藏),空行结束。逐一 `addProviderKey(id, k)` 追加到现有 keys,不覆盖。
472
- - **成功**:`added N <id> API key(s) (now M total) — restart daemon to activate`
473
- - **空输入**:`empty input — nothing changed`
474
- - **非 TTY 且无 key 参数**:报错 `provide keys inline (non-TTY): mslxdff -provider openrouter <key1> [key2 ...]` 并 `exit 1`。
475
-
476
- #### `mslxdff -provider <id> <key1> [key2 ...]`(批量设置,覆盖)
477
-
478
- - **作用**:批量设置该供应商的全部 keys(**覆盖**旧值)。`saveProviderKeys(id, keys)`,去重、trim、去空。
479
- - **输出**:`set <id> API keys (N: abcd…wxyz, ...) — restart daemon to activate`(仅首尾 4 字符脱敏)。
480
- - **示例**:
481
- ```bash
482
- mslxdff -provider openrouter sk-or-v1-aaa sk-or-v1-bbb sk-or-v1-ccc
483
- ```
484
-
485
- #### `mslxdff -provider <id> add <key>`(追加单 key)
486
-
487
- - **语法**:`mslxdff -provider openrouter add <key>`
488
- - **作用**:追加一个 key(去重),不覆盖已有。`addProviderKey(id, key)`。
489
- - **输出**:`added <id> API key (now N total) — restart daemon to activate`
490
- - **示例**:`mslxdff -provider openrouter add sk-or-v1-ddd`
491
-
492
- #### `mslxdff -provider <id> remove <seq|key> [seq|key ...]`(按序号或值删除,支持逗号)
493
-
494
- - **语法**:`mslxdff -provider openrouter remove <seq|key> [seq|key ...]`,多个目标可用空格或逗号分隔(如 `remove 1 3` / `remove 1,3` / `remove sk-aaa sk-bbb` 混写)。
495
- - **序号语义**:`seq` 为 `list` 显示的 `1-based` 编号(`[1]` 对应第一行)。内部基于删除前快照 `current` 解析序号为值,再批量 `removeProviderKeys`,`new Set` 去重。
496
- - **越界**:`! no key at sequence N (provider has M) — skipped`,不报错,跳过该序号。
497
- - **输出**:`removed X <id> API key(s) (now Y total) — restart daemon to activate` 或 `nothing to remove`。
498
- - **示例**:
499
- ```bash
500
- mslxdff -provider openrouter list
501
- # → [1] sk-a… [2] sk-b… [3] sk-c…
502
- mslxdff -provider openrouter remove 2
503
- mslxdff -provider openrouter remove 1,3
504
- mslxdff -provider openrouter remove sk-or-v1-bbb
505
- ```
506
-
507
- #### `mslxdff -provider <id> list` / `status`(列表,脱敏)
508
-
509
- - **语法**:`mslxdff -provider <id> list` 或 `mslxdff -provider <id> status`
510
- - **作用**:列出该供应商已配置的 keys(脱敏)与共享开关。
511
- - **输出**:
512
- ```
513
- provider: openrouter (3 keys)
514
- [1] sk-o…aaaa (51 chars)
515
- [2] sk-o…bbbb (51 chars)
516
- [3] sk-o…cccc (51 chars)
517
- remove by: mslxdff -provider openrouter remove <seq> [seq...] | <key-value>
518
- share keys to peers: 借出(默认,key 随转发自动附带,ADR-0019)
519
- NOTE: opencode is the default provider and can never be shared
520
- ```
521
- 无 key 时:`provider: openrouter (no keys configured)`。
522
- - **示例**:`mslxdff -provider openrouter list`
523
-
524
- #### `mslxdff -provider <id> clear`(清空)
525
-
526
- - **语法**:`mslxdff -provider openrouter clear`
527
- - **作用**:清空该供应商全部 keys(`saveProviderKeys(id, [])`),provider 在下次 daemon 启动时自动禁用。
528
- - **输出**:`cleared openrouter API keys (provider disabled on next daemon start)`
529
- - **示例**:`mslxdff -provider openrouter clear`
530
-
531
- #### `mslxdff -provider <id> del`(删除整个供应商,自动重启)
532
-
533
- - **语法**:`mslxdff -provider kenari del` / `mslxdff -provider kenari delete` / `mslxdff -provider kenari rm`(别名 `delete`/`rm`/`remove-provider`/`del-provider`,`opencode` 受保护不可删)
534
- - **作用**:彻底删除该供应商的 `providerConfigs`/`providerKeys`,不留 `○ disabled` 空行。区别于 `clear`(仅清 keys 仍占一行)。
535
- - **行为**:`saveProviderConfig(id, {baseUrl:"",keys:[]…})` 触发删除分支,同步清理 `providerKeys` 残留;若 daemon 运行中则 **自动 `mslxdff -restart`(<1s)**,否则下次启动生效。需重启才生效的“别扭”在此被自动抚平。
536
- - **输出**:`已删除供应商: kenari — 配置已清空` + `检测到 daemon 运行中,自动重启以生效…` / `已自动重启完成`。
537
- - **示例**:
538
- ```bash
539
- mslxdff -provider cline del # 注意:历史迁移注记已反转 —— 0.1.x 起供应商 id 统一为 cline,删/留都以 cline 为准(旧的 clinebot 配置走 -provider cline migrate 合并)
540
- mslxdff -providers list # 确认已从 9 → 8
541
- ```
542
-
543
- #### key 随转发自动借出(ADR-0019,无开关)
544
-
545
- - **语义**:借道 = 用你的 key。转发(peer 接力 / via-route)时,命中本机**有 key** 的供应商即自动把 key 列表放私有头 `x-mslxdff-share-keys: provider=k1,k2` 附带;组员侧 `parseShareKeysHeader` 解析后 `dispatcher.chat(body, {shareKeys})` → `provider.chatWithKeys` 用临时 `keyring` 调上游,**用完即弃,不落盘**。
546
- - **组内互信是前提**:不再有 `share on|off` 开关(`providerShareKeys` state / `MSLXDFF_<ID>_SHARE_KEYS` 均已删除);旧字段变为惰性数据。
547
- - **硬排除**:`opencode`(无 key 恒排除)、`workbuddy`(local-only 本就不走组员)、`cline` 与 `codearts`(key 是 refresh-token 型,借出后对端刷新会轮换,与本机互踢下线;`share-keys.js` NEVER_SHARE_IDS + 凭据形状双层兜底)、`traework`(TRAE SOLO 本机账号绑定型,与 workbuddy 同类恒不借出)。
548
- - **无开关、无白名单**(组内互信是前提):旧 `MSLXDFF_SHARE_PROVIDERS` 白名单已删除。
549
- - **示例**:`mslxdff -provider openrouter list` 输出显示 `share keys to peers: 借出(随转发自动附带,ADR-0019)`;**硬排除供应商(`codearts`/`cline`/`workbuddy` 等 local-only)则显示 `不借出(local-only / 硬排除…)`**(`codearts` 每条 key 还会展示 `user/uid/domain` 账号摘要);`-status` 供应商表 `共享 借出`(opencode 行显示 `无法共享`)。
550
-
551
- #### `mslxdff -provider add <id> <baseUrl> <key>`(通用 OpenAI 兼容供应商一键添加,支持异形路径)
552
-
553
- - **语法**:`mslxdff -provider add <id> <baseUrl> <key> [allow...] [--models-path <path>] [--chat-path <path>]`
554
- - **作用**:一键注册任意 OpenAI 兼容网关为新供应商。`baseUrl` 为 OpenAI 根(如 `https://api.example.com/v1`,去尾 `/`),`key` 为 Bearer token;模型对外形如 `myapi/gpt-4`,转发时剥前缀 `gpt-4` 调 `POST <baseUrl><chatPath>`,`GET <baseUrl><modelsPath>` 拉模型列表(不过滤,全量前缀化)。`--models-path`/`--chat-path` 用于适配异形上游:`opencode=/zen/v1/models`、`b.ai=/v1/models`、`cline=/api/v1/models`、`workbuddy=/console/enterprises/personal/models`,不传则按供应商默认值(`workbuddy` 为定制,其余为 `/models` & `/chat/completions`)。
555
- - **安全默认(0.1.61 起)**:`allowAnyModels=false`,**空 `allowlist` 时上游直接禁用**,`chat` 返回 `403 {"error":"model not allowed… — allowed: (none) (use: mslxdff -provider <id> allowlist add <model>)"}` + 头 `x-mslxdff-allowlist:1`,不打上游、不计费。**必须二选一**:`mslxdff -provider <id> allowlist set <m1> <m2> ...`(推荐,精确 free 模型)或 `mslxdff -provider <id> allowAny on`(放行全部,`opencode` 例外默认 `ON`)。
556
- - **行为**:`saveProviderConfig(id, {baseUrl, keys:[key], modelsPath, chatPath})`,若 `id` 已存在则更新 `baseUrl` 并追加 key(去重);`opencode/openrouter` 保留走原分支,误用 `add openrouter ...` 会提示改用 `add <key>` / `set-url`。
557
- - **校验**:`id` 经 `normalizeProviderId`,`baseUrl` 必须 `http(s)://` 前缀,`modelsPath`/`chatPath` 必须以 `/` 开头。
558
- - **输出**:`added generic provider: myapi / baseUrl: ... / keys: 1 (...) / allow=none(BLOCKED) — set allowlist or allowAny on to enable / use as: myapi/<model-id> — restart daemon to activate`
559
- - **示例**:
560
- ```bash
561
- mslxdff -provider add myapi https://api.example.com/v1 sk-xxx
562
- mslxdff -provider add myapi https://api.example.com/v1 sk-xxx --models-path /v1/models --chat-path /v1/chat/completions # b.ai 等异形
563
- mslxdff -provider myapi list # 看 baseUrl + keys + paths
564
- # 调用
565
- curl -H "Authorization: Bearer $(mslxdff -showtoken)" http://127.0.0.1:8989/v1/chat/completions -d '{"model":"myapi/gpt-4","messages":[{"role":"user","content":"hi"}]}'
566
- ```
567
-
568
- #### `mslxdff -provider <id> models [--json]`(查该供应商支持哪些模型,直观答案)
569
-
570
- - **语法**:`mslxdff -provider workbuddy models` / `mslxdff -provider cline models --json` / `mslxdff -provider myapi models`
571
- - **作用**:**“上游供应商支持哪些模型”的一级答案**,无需 `curl`。直连该供应商 `GET <baseUrl><modelsPath>` 拉取,按 `allowlist` 过滤后按 `workbuddy/` 前缀输出;`opencode` 时读本地 `models.json` 缓存的裸 id。`--json` 输出 `{"object":"list","data":[...]}` 供脚本 `jq`。
572
- - **能力列**:上下文长度(`1M/200k`)+ `📷`读图 `🧠`推理 `🔧`工具调用;`workbuddy` 读上游原生字段(`maxInputTokens/supportsImages/supportsReasoning/supportsToolCall`,first-party 最准,`disabledMultimodal` 会压过 `supportsImages`),其余供应商无此字段显示 `—`;查单个模型完整能力 JSON 用 `curl local/models/capabilities?provider=workbuddy&id=<裸id>`。
573
- - **ctrl+t 档位**:`-setto opencode` 会在条目里写 `variants`(opencode `ctrl+t` 直接切推理档位,即 `model.variants` 键;config 优先级高于 opencode 启发式,绕过其 glm/kimi/deepseek-v3/minimax/qwen/big-pickle 黑名单):models.dev effort 型按目录档位写,workbuddy 推理模型按通用 `low/medium/high` 写(实测 `reasoning_effort` 生效,low 思考量约 high 一半;toggle 型不写);改完配置后 TUI 需重启(其 provider 数据只在启动时拉一次)。
574
- - **示例**:
575
- ```bash
576
- mslxdff -provider workbuddy models # 29 个 workbuddy/hy3 ...(含能力列)
577
- mslxdff -provider cline models --json | jq .data[].id
578
- mslxdff -model list --provider workbuddy # 同义(见 5. 模型管理)
579
- ```
580
-
581
- #### `mslxdff -provider <id> bench [--json] [--prompt <text>] [--max-tokens N] [--timeout N]`(评估已勾选模型速度,防误烧)
582
-
583
- - **语法**:`mslxdff -provider workbuddy bench` / `mslxdff -provider myapi bench --json` / `mslxdff -provider workbuddy bench --prompt hi --max-tokens 32 --timeout 30000`
584
- - **作用**:**只测(`allowlist` ∩ 全局 `picks`)交集**,逐个发最小 `POST <baseUrl><chatPath>` 探针,采集 `TTFB/总耗时/TPS(或字/秒)/tokens/状态` 并表格排序标 `*最快`;空 `allowlist` 时**不发任何 chat**,仅 `GET <baseUrl>/v1/models → GET <baseUrl>/models` 探活并提示 `mslxdff -provider <id> allowlist set <model>`,避免全量 60 个误扣费。`--json` 输出 `[{id, ok, ttfbMs, totalMs, tps, charsPerSec, tokens, error, label}]` 供脚本。
585
- - **行为**:串行(防 429),`30s` 超时(`AbortSignal.timeout`),`401` 鉴权失败/`402` 余额不足/`429` 限流/`超时` 人话且不中断其余模型,`workbuddy` 自动带 `X-User-Id/X-Domain` 等特化头,通用供应商仅 `Bearer`。
586
- - **输出**:
587
- ```
588
- bench workbuddy: 共 2 个已勾选模型,逐个测速(串行,30000ms 超时)...
589
- [1/2] hy3 ... OK TTFB 120ms 总 800ms 42.3 t/s
590
- [2/2] hy4-preview ... FAIL 余额不足 (insufficient balance)
591
-
592
- 模型 状态 TTFB 总耗时 速度 tokens 备注
593
- ────────────────────────────────────────────────────────────────────────────────
594
- * hy3 成功 120ms 800ms 42.3 t/s 10
595
- hy4-preview 余额不足 80ms 200ms — — insufficient balance
596
- ────────────────────────────────────────────────────────────────────────────────
597
- 最快:hy3 TTFB 120ms 总 800ms 42.3 t/s
598
- 完成:2 个,成功 1,失败 1
599
- ```
600
- 空 allowlist 时:
601
- ```
602
- provider workbuddy: 未设置 allowlist(allowAny=OFF),不发起测速,仅探活模型列表...
603
- 尝试:GET https://copilot.tencent.com/console/enterprises/personal/models → ...
604
- 发现 28 个模型:
605
- - hy3
606
- - hy4-preview
607
- 下一步:mslxdff -provider workbuddy allowlist set hy3 hy4-preview
608
- ```
609
- - **示例**:
610
- ```bash
611
- mslxdff -provider workbuddy bench
612
- mslxdff -provider workbuddy bench --json | jq .
613
- mslxdff -provider myapi bench --prompt "hi" --max-tokens 32 --timeout 30000
614
- ```
615
-
616
- #### `mslxdff -provider <id> bench --via` / `mslxdff -provider bench --via`(家宽选路:直连 vs 经 peer 延迟对比 + 动态择路)
617
-
618
- - **语法**:`mslxdff -provider bench --via [--include-opencode] [--json] [--samples N] [--timeout N] [--apply]` / `mslxdff -provider <id> bench --via [--include-opencode] [--json] [--samples N] [--timeout N] [--apply]`
619
- - **作用**:同一上游模型,**现场比** `direct`(本机直连)与经每个在线 `peer` 转发的 `TTFB`。家宽/出口 IP 不同会导致限流与首字延迟差很大,此命令一测就知道走哪条路最快;`--apply` 则把 `best` 写入 `via-routes.json`,网关对**显式锁模型** `provider/model` 按 `best` 单路径直达(`via:host` 单 peer 借 `x-mslxdff-share-keys`,`direct` 本机),不并发,失败秒切直连,`--apply` 手动重跑即动态调整。
620
- - **行为**:
621
- - **不写 state(除 --apply)**:默认仅打印 `stdout`,不改 `preferredModel`/`allowlist`;`--apply` 时落盘 `via-routes.json`(`~/.config/mslxdff/via-routes.json`,随 `MSLXDFF_STATE_FILE` 派生,`MSLXDFF_VIA_ROUTES_FILE` 可覆盖,`MSLXDFF_VIA_ROUTE_TTL_MS=0` 默认不过期)。
622
- - **串行省额度**:同一模型对 `direct + 每个 peer` 串行发 `POST <baseUrl><chatPath>` 轻探针(`max_tokens=5 prompt=hi stream:false`,剥 `provider/` 前缀),每个结果记 `TTFB/总耗时/TPS/ok/error/label`。
623
- - **额度保护**:默认**跳过** `opencode` 供应商(`opencode` 为免费共享池,走 `peer` 对冲会烧组员额度)。如确需包含,必须加 `--include-opencode`,且 **TTY 二次确认 `y/N`**(`[bench-via] 组员额度保护:默认跳过 opencode … --include-opencode y/N > `),`非 TTY`(脚本/CI)直接跳过并提示。
624
- - **空状态**:无已加入组或全离线时直接空状态引导 ` -group list`(不发起任何探针),`direct` 与 `via` 共用同一 `runOne` 测 `TTFB`,统一 `formatViaReport` 打印 `bench-via: direct vs via` 头、`★` 最快、`— offline`。
625
- - **`--json`**:`stdout` 纯 `{"meta":{"provider","model","samples","timeoutMs","includeOpencode"},"results":[…],"advice":"…"}`,进度与告警走 `stderr`(便于 `jq`);`--apply` 时落盘信息亦走 `stderr`。
626
- - **`--apply` 动态择路**:落盘后网关对显式锁模型(如 `cline/z-ai/glm-5.3-flash`)按 `best` 单路径择路(工作 `workbuddy→direct`、`cline→172`、`bai/aihubmix→leader` 已验证),`via:host:port` 失败自动回落 `direct`;走 peer 时 key 随请求自动附带(ADR-0019),`B` 无配置也能借 `A` 的 key。
627
- - **输出(文本)**:
628
- ```
629
- [bench-via] 测试 workbuddy/hy3: direct + 2 peer(s) 串行(max_tokens=5,30000ms 超时)...
630
- [1/3] via direct ... OK TTFB 120ms 总 800ms
631
- [2/3] via p1 (192.168.1.8) ... OK TTFB 80ms 总 600ms
632
- [3/3] via p2 (10.0.0.5) ... offline
633
- bench-via: direct vs via (workbuddy/hy3) samples=1 timeout=30000ms
634
- direct TTFB 120ms 总 800ms ★ ——
635
- via p1 (192.168.1.8) TTFB 80ms 总 600ms ★ 最快
636
- via p2 — offline
637
- 建议:经 p1 最快,可让该 peer 优先承载此模型。
638
- ```
639
- 空组时:`暂无在线 peer(空组)— 先 mslxdff -group list 查看/加组`。
640
- - **示例**:
641
- ```bash
642
- mslxdff -provider bench --via # 对当前 preferredModel
643
- mslxdff -provider workbuddy bench --via # 对 workbuddy 的 preferredModel
644
- mslxdff -provider workbuddy bench --via --json | jq . # 脚本
645
- mslxdff -provider bench --via --include-opencode # 含 opencode(TTY 会二次确认)
646
- mslxdff -provider bench --via --samples 3 --timeout 10000
647
- mslxdff -provider bench --via --apply # 产表并落盘 via-routes.json,供网关显式模型单路径择路(工作→direct,cline→172,bai/aihubmix→leader)
648
- mslxdff -provider workbuddy bench --via --apply # 仅 workbuddy 产表
649
- ```
650
-
651
- #### `mslxdff -provider cline login`(Cline 免 403:WorkOS 设备授权拿 refreshToken)
652
-
653
- - **语法**:`mslxdff -provider cline login`(别名 `auth`/`oauth`;`cline` 同)
654
- - **作用**:`cline` 供应商默认直连 `api.cline.bot` 用 `Bearer sk_xxx` 会遇 `403 only available via Cline product surfaces`(服务端强校验 Cline 客户端指纹)。本命令走 Cline 官方 WorkOS 设备授权流:打印浏览器授权链接 → 你登录一次 → 自动 `POST /api/v1/auth/register` 换 `refreshToken` → **落盘 `providerConfigs.cline.keys`**(供应商 id 统一为 `cline`,不再双写)。此后 `cline` 所有请求自动走:`refreshToken → POST /api/v1/auth/refresh → workos:accessToken` + 完整指纹头(`User-Agent: Cline/3.0.47`、`X-CLIENT-TYPE: cline-sdk`、`X-PLATFORM: terminal`、`X-Task-ID` 等),`deepseek/deepseek-v4-flash` 不再 403;deepseek 家族(含 `cline-free/deepseek-*`)非流式请求内部强制 `stream:true` 并聚合返回(避免 `500 empty response content`),对外仍按请求方 `stream` 标志。
655
- - **多账号**:重复 `login` 追加(同邮箱自动替换不追加:`auths` 存邮箱映射,老账号无映射时用 refresh 反查兜底;`list` 显示邮箱);`429 Daily free limit reached`/空响应自动解析冷却(`Try again in Xh Xm`)并切换下一账号,800ms 串行队列防并发空响应。
656
- - **网络**:直连 `api.workos.com` 被墙会报超时(20s);开代理后重试:`set HTTPS_PROXY=http://127.0.0.1:7890`(`HTTP_PROXY` 同),login 自动经代理。
657
- - **示例**:
658
- ```bash
659
- mslxdff -provider cline login # 浏览器授权 → token 落盘
660
- mslxdff -restart # 重启使新账号生效
661
- mslxdff -provider cline bench --json # 测速 deepseek-v4-flash 是否 200
662
- ```
663
-
664
- #### `mslxdff -provider cline free [--json]` / `-provider cline free sync [--yes] [--json] [--keep-extra]`(上游免费目录:查询与一键落成 allowlist)
665
-
666
- - **语法**:
667
- ```bash
668
- mslxdff -provider cline free [--json] # 只读:列上游 free 目录 + 与当前 allowlist 的差异
669
- mslxdff -provider cline free sync [--yes] [--json] [--keep-extra] # 写:把 free 目录同步为 allowlist(默认 dry-run)
670
- ```
671
- - **数据源**:`GET https://api.cline.bot/api/v1/ai/cline/recommended-models`(公开免鉴权)返回 `free` / `recommended` / `clinePass` / `clineCloud` 四类,**只取 `free`**(当前 5 个:`z-ai/glm-5.3-flash`、`cline-free/deepseek-v4.1-flash`、`cline-free/muse-spark-1.3-contributor`、`cline-free/solar-pro4`、`poolside/laguna-s-2.1:free`)。与聚合目录/daemon 启动自检同源(`logDir/cline-free.json` 快照)。
672
- - **`free`(只读)**:打印 free 目录 + 与当前 `allowlist` 的差异(目录有而名单没有 / 名单有而目录没有),**不写任何 state**;`--json` 输出 JSON 供脚本。
673
- - **`free sync`(写)**:把 `allowlist` 置为上游 free 集合,**默认 dry-run 只预览**(末行提示 `预览模式,未写入。执行:mslxdff -provider cline free sync --yes`),加 `--yes` 才落盘 `providerConfigs.cline.allowedModels`(只重建名单,保留 baseUrl/keys/auths/端点);`--keep-extra` 只增不删(保留名单里不在目录的条目)。写入的是**裸 id**(如 `z-ai/glm-5.3-flash`),对外 id 仍是 `cline/z-ai/glm-5.3-flash`。
674
- - **自动同步 (2026-09-24)**:daemon 运行期每次成功读取 `recommended-models` 后,把 free+clinePass 自动并入 `providerConfigs.cline.allowedModels`(只增不减、幂等零写盘)。新上架免费模型 (gemini-3.8-flash/space-bunny-alpha/mimo-v2.6-flash) 不再被误拦;通道切换 (`cline-pass/mimo`→`cline-free/mimo`) 两通道并存。关闭用 `MSLXDFF_CLINE_AUTOSYNC=0`(退回纯静态白名单)。观测:终端输出 `[cline] allowlist auto-sync: +N → M total (...)`。
675
- - **额度说明**:该接口只回**目录**、不回余额;免费额度用尽只能由上游 `429`(`Try again in Xh Ym Zs`)反推并进冷却。
676
- - **输出(示例)**:
677
- ```
678
- cline free 目录(GET /api/v1/ai/cline/recommended-models → free):5 个
679
- z-ai/glm-5.3-flash
680
- cline-free/deepseek-v4.1-flash
681
- ...
682
- 当前 allowlist:8 个
683
- deepseek/deepseek-v4-flash (不在 free 目录)
684
- meta/muse-spark-1.3-contributor (不在 free 目录)
685
- 结果:allowlist 8 → 5(0 新增 / 3 移除 / 5 保留)
686
- 预览模式,未写入。执行:mslxdff -provider cline free sync --yes
687
- ```
688
- - **示例**:
689
- ```bash
690
- mslxdff -provider cline free # 5 个 free + 与 allowlist 的差异
691
- mslxdff -provider cline free sync # 预览:8 → 5
692
- mslxdff -provider cline free sync --yes # 落盘
693
- mslxdff -provider cline free sync --yes --keep-extra
694
- ```
695
-
696
- #### `mslxdff -provider cline quota [--json] [--account <hash>] [--model <substr>]`(账号×模型用量:free 周期 / pass 24h)
697
-
698
- - **语法**:`mslxdff -provider cline quota [--json] [--account <acct_hash>] [--model <子串>]`
699
- - **只读**:聚合 `cline-usage.jsonl`(`loadAndAggregate`,IO 失败返回空不报错),按账号哈希分组:free 行显示本周期 tokens + 已完成周期数(上周期产出)+ 累计,pass 行显示近 24h + 累计;`--json` 输出 `{provider, entries[]}` 供脚本;`--account/--model` 过滤;空账本打印引导(先跑一轮对话再查)。
700
- - **口径**:free 周期由 429 封存/恢复划分(`recordLimit`),pass 为滚动 24h 窗口;只记 output tokens,不记 prompt/正文/凭据。
701
- - **示例**:
702
- ```bash
703
- mslxdff -provider cline quota # 全量分组
704
- mslxdff -provider cline quota --model gemini # 只看 gemini
705
- mslxdff -provider cline quota --json # 脚本消费
706
- ```
707
-
708
- #### `mslxdff -provider cline migrate [--dry-run]`(旧的 clinebot 配置合并进 cline)
709
-
710
- - **语法**:`mslxdff -provider cline migrate [--dry-run]`
711
- - **作用**:Cline 供应商历史上有两个 id(`cline` / `clinebot`):login 双写 + 每次 refreshToken 轮换回写另一个 id,使 `clinebot` 永不消亡(daemon 内两个实例、`/v1/models` 可能重复暴露 `cline/x` 与 `clinebot/x`)。本命令把 `providerConfigs.clinebot` 合并进 `cline` 后**删除旧键**:keys 去重并剔除 `sk_` 形态(`cline` 只认 refreshToken)、allowlist 求并、baseUrl 归一到 `https://api.cline.bot`;**幂等**(没有 `clinebot` 时 no-op),真会改动前先备份 `state.json.bak-<ISO 时间戳>` 并 append 一条 `cline-unify-migrated` 事件(不含凭据)。CLI 入口同时把 `clinebot` / `cline-bot` 一次性归一为 `cline`(daemon 启动与 login 写盘前也会跑同一迁移)。
712
- - **local-only(硬约束,不可回退)**:`cline` 恒为 local-only —— 不经组员转发(`shouldUseGroupForModel("cline/…") === false`)、不借出 key(`share-keys` 硬排除 refresh-token 型凭据)、只走本地直连,历史别名同样硬排除(ADR-0015 / ADR-0019 / ADR-0026)。
713
- - **示例**:
714
- ```bash
715
- mslxdff -provider cline migrate --dry-run # 只看会改什么
716
- mslxdff -provider cline migrate # 落盘(改前自动备份 state.json)
717
- mslxdff -restart # 重启后只剩一个 cline 实例
718
- ```
719
-
720
- #### `mslxdff -provider codearts login` / `-provider codearts models [--json]`(华为云 CodeArts Agent 盘古助手免费福利模型)
721
-
722
- - **语法**:
723
- ```bash
724
- mslxdff -provider codearts login # 浏览器 PKCE 授权 → 凭证 blob 落盘
725
- mslxdff -provider codearts login --no-allow-any # 接入后保持 allowAny OFF(默认 ON)
726
- mslxdff -provider codearts models [--json] # 三路发现列模型(benefit 自动 claim)
727
- ```
728
- - **作用**:华为云 CodeArts Agent(盘古助手)免费福利模型接入。`login` 走 PKCE 浏览器授权(本地回调 server 收 code + ticket 轮询双通道),成功后把 `refreshToken/codeVerifier/dpopJwk/AK/SK/securityToken` 组成**一账号一 blob JSON** 落盘 `providerConfigs.codearts.keys`(多账号重复 login 追加 = keyring 轮转;默认 `allowAnyModels=true`,`--no-allow-any` 关)。此后对话走 `codearts/<modelId>` 前缀:恒 `stream:true` 直连上游 v2 SSE(`text` 全文快照→流式 delta / 非流式聚合 `chat.completion`);SDK-HMAC-SHA256 签名 + DPoP ES256;STS 临时凭证临期(提前 30min)单飞刷新、refresh_token 单次轮换**原位写回**(`invalid_grant` 终态死号 → 提示重跑 login);HTTP 200 内嵌错误码映射 429/400/502。
729
- - **恒 local-only(硬约束)**:不经组员转发/via-route、不借出 key(refreshToken+DPoP 绑定型凭据,外借 = 对端刷新轮换互踢下线,同 cline;ADR-0015/0019/0027)。
730
- - **示例**:
731
- ```bash
732
- mslxdff -provider codearts login
733
- mslxdff -provider codearts models # 三路发现:builtin 归一 + 代理型 + 福利网关
734
- curl http://127.0.0.1:8989/v1/chat/completions -H "Authorization: Bearer <token>" \
735
- -d '{"model":"codearts/glm-5.3-flash","messages":[{"role":"user","content":"hi"}],"stream":true}'
736
- mslxdff -provider codearts login # 再跑一次 = 追加第二个华为账号
737
- ```
738
-
739
- #### `mslxdff -provider <id> set-url <baseUrl>` / `set-models-path` / `set-chat-path`(改供应商端点)
740
-
741
- - **语法**:
742
- ```bash
743
- mslxdff -provider <id> set-url <baseUrl> # 别名 setUrl/url
744
- mslxdff -provider <id> set-models-path <path> # 如 /v1/models 或 /console/enterprises/personal/models
745
- mslxdff -provider <id> set-chat-path <path> # 如 /v1/chat/completions 或 /v2/chat/completions
746
- ```
747
- - **作用**:仅改已注册通用供应商的对应路径,保留 `keys`/`allowlist`;`openrouter` 的地址仍由 `MSLXDFF_OPENROUTER_BASE_URL` 控制,不建议用此改。`modelsPath`/`chatPath` 持久化到 `state.json providerConfigs.<id>`,`0600` 原子写。
748
- - **输出**:`set <id> modelsPath: /v1/models — restart daemon to activate` / `set <id> baseUrl: https://... — restart daemon to activate`
749
- - **示例**:
750
- ```bash
751
- mslxdff -provider myapi set-url https://api.new.com/v1
752
- mslxdff -provider myapi set-models-path /v1/models
753
- mslxdff -provider myapi set-chat-path /chat/completions
754
- ```
755
-
756
- #### `mslxdff -providers list` / `mslxdff -provider list`(列出所有已部署供应商)
757
-
758
- - **语法**:`mslxdff -providers list` / `mslxdff -providers status` / `mslxdff --providers list` / `mslxdff -provider list`(单数 `list` 为同义别名,无参 `mslxdff -providers` 亦视为 `list`)
759
- - **作用**:聚合展示当前已部署的所有上游供应商及其启用状态,不启动 daemon。
760
- - **判定**:
761
- - `opencode` 恒 `enabled`(`https://opencode.ai`,无 key,`cannot share`)
762
- - `openrouter` 当 `keys.length>0` 为 `enabled`(`https://openrouter.ai/api/v1`)
763
- - 通用:`providerConfigs.<id>={baseUrl,keys}` 中 `baseUrl && keys.length>0` 为 `enabled`,否则 `disabled` 并标注 `missing baseUrl / no keys`
764
- - **输出**:
765
- ```
766
- providers (3):
767
- opencode enabled 0 keys allow=all baseUrl=https://opencode.ai cannot share (built-in, no key, cannot share)
768
- openrouter disabled 0 keys allow=none(BLOCKED) baseUrl=https://openrouter.ai/api/v1 共享 借出 (no keys)
769
- bai enabled 1 key sk-t…93hc allow=2(glm-5.3-flash,minimax-m3) baseUrl=https://api.b.ai/v1 共享 借出
770
- ```
771
- 每行含 `keys` 脱敏(首尾 4 字符)、`baseUrl`、`共享 借出/无法共享`、`allow` 摘要(`allow=none(BLOCKED)` 表示空名单+`allowAny OFF`)与 `note`。
772
- - **示例**:
773
- ```bash
774
- mslxdff -providers list
775
- mslxdff -provider list # 同义
776
- mslxdff -provider bai list # 单供应商详情(非聚合,含 allowlist)
777
- ```
778
-
779
- #### `mslxdff -provider <id> allowlist [list|set|add|remove|clear]`(供应商模型白名单,防昂贵/奇怪模型)
780
-
781
- - **语法**:
782
- ```bash
783
- mslxdff -provider <id> allowlist list # 查看
784
- mslxdff -provider <id> allowlist set <m1> <m2> ... # 覆盖
785
- mslxdff -provider <id> allowlist add <m> [m2 ...] # 追加(去重)
786
- mslxdff -provider <id> allowlist remove <m> [m2 ...] # 移除
787
- mslxdff -provider <id> allowlist clear # 清空(= 阻塞,见 allowAny)
788
- # 别名:allow / allowed / whitelist 均等价于 allowlist
789
- # 接入时直接带白名单:
790
- mslxdff -provider add <id> <baseUrl> <key> [m1 m2 ...]
791
- ```
792
- - **语义(0.1.61 起安全默认)**:
793
- - `allowlist` 为空 + `allowAnyModels=false`(默认,`opencode` 例外为 `true`)→ **阻塞**,`chat` 直接 `403`(`{"error":"model not allowed for provider \"...\" — allowed: (none) (use: mslxdff -provider ... allowlist add <model>)"}` + 头 `x-mslxdff-allowlist:1`),`GET /v1/models` 不暴露任何模型。
794
- - `allowlist` 为空 + `allowAnyModels=true` → **不限**,全部模型可用(需显式 `mslxdff -provider <id> allowAny on`)。
795
- - 非空 → **仅名单内可用**,`chat` 时 `rawModel` 不在名单则立即 `403`,不计入冷却、不触发 fallback;`GET /v1/models` 亦仅返回白名单内的模型(按 `raw` 精确匹配,支持 `bai/glm-...` 或 `glm-...` 两种写法,存储时自动归一为 raw)。
796
- - **勾选集裁剪对外目录(ADR-0030)**:`modelPicks` 非空时 `GET /v1/models` 只返回勾选到的 `id`(精确匹配、含供应商前缀),裁剪点在能力富化(ADR-0022)之后、`models:list` 插件 hook 之前,条目形状不变;**空勾选不裁剪**(否则一次 `-model pick clear` 会让所有下游客户端零模型可用);`GET /v1/models?all=1` 绕过裁剪取全量(`-models` 交互候选取数即走此口,保证取消勾选的模型仍回到候选列表)。与 `allowlist` 分属两层语义:`allowlist` 是供应商准入安全阀(空即 `403`),`modelPicks` 是用户偏好目录,二者互不改写。
797
- - **存储**:`state.json providerConfigs.<id>.{allowedModels: string[], allowAnyModels: boolean}`(`saveProviderAllowedModels` 去重、trim、支持逗号分隔的一串如 `m1,m2`)。`providerConfigs.<id>.baseUrl/keys` 为空但 `allowedModels` 非空时仍保留条目(便于先定白名单后补 key)。
798
- - **生效**:热更新立即生效(`chat` 与 `listModels` 均无需重启;`provider add` 的白名单亦同);`opencode` 亦支持(`mslxdff -provider opencode allowlist set big-pickle mimo-v2.5-free`)。
799
- - **示例**:
800
- ```bash
801
- mslxdff -provider add myapi https://api.example.com/v1 sk-xxx gpt-4 gpt-3.5 # 接入即定白名单
802
- mslxdff -provider myapi allowlist list
803
- # → provider: myapi baseUrl: https://api.example.com/v1
804
- # allowedModels: 2 models (only these can be used)
805
- # [1] gpt-4
806
- # [2] gpt-3.5
807
- mslxdff -provider myapi allowlist add gpt-4o
808
- mslxdff -provider myapi allowlist remove gpt-3.5
809
- mslxdff -provider myapi allowlist clear # 回到阻塞(allowAny OFF)或不限(allowAny ON)
810
- mslxdff -providers list # 聚合视图亦显示 allow=2(...) 或 allow=none(BLOCKED) 摘要
811
- ```
812
-
813
- #### `mslxdff -provider <id> allowAny on|off`(空 allowlist 时放行或阻塞,安全开关)
814
-
815
- - **语法**:`mslxdff -provider <id> allowAny on|off`(别名 `allow-any`/`allow_any`/`allowany`,`list` 为空参时仅查看)
816
- - **作用**:控制 `allowlist` 为空时的行为。默认 `OFF`(`opencode` 例外 `ON`,兼容历史免费池),`OFF` 时空名单=`403 BLOCKED`,`ON` 时空名单=放行全部。
817
- - **存储**:`state.json providerConfigs.<id>.allowAnyModels: boolean`(显式存 `true/false`,`saveProviderAllowAnyModels`)。
818
- - **示例**:
819
- ```bash
820
- mslxdff -provider cline allowlist list # → (none — BLOCK ALL, provider disabled until allowlist set or allowAny ON)
821
- mslxdff -provider cline allowAny on # 空名单时放行全部(不推荐,cline 易欠费)
822
- mslxdff -provider workbuddy allowAny on # 恢复旧不限行为
823
- ```
824
-
825
- #### WorkBuddy 专用供应商(`workbuddy/hy3` 等 16 CLI 模型)
826
-
827
- - **语法**:
828
- ```bash
829
- # 单号接入(禁手填,必须自动化)
830
- mslxdff -provider add workbuddy https://copilot.tencent.com <key> [allow1 allow2 ...] # 仅当用户贴 eyJ 时可用,否则禁手填
831
-
832
- # 多号追加(路径A,推荐,设备授权,无需抓包)
833
- # 1) 项目根执行,浏览器用新账号登录即可(桌面端不用退旧号)
834
- mslxdff -provider workbuddy login
835
- # 多号追加(路径B,抓包兜底)
836
- # 1) WorkBuddy 桌面退出当前账号 → 用新账号重新登录 https://copilot.tencent.com(能对话即成功)
837
- # 2) 项目根执行(--force 跳过旧号 refresh 捷径直抓包;whistle :8899 自动追加新号,不覆旧号)
838
- node workbuddy-token-auto.js --force
839
- mslxdff -workbuddy list # 验证 2 行 uid
840
- mslxdff -workbuddy balance --json # 看 total/dailyPacks/nextExpire
841
-
842
- # 运维
843
- mslxdff -provider workbuddy list # 看 baseUrl/keys/共享/allowlist
844
- mslxdff -provider workbuddy allowlist set hy3 hy4-preview glm-5.3-flash # 仅低耗
845
- mslxdff -workbuddy checkin # 每日签到 100 credits(多号并行 3,幂等,--json 聚合)
846
- mslxdff -workbuddy growth [--json] # 成长任务全自动(参与→触发→领奖,串行,已领跳过)
847
- mslxdff -workbuddy travel [--json] # 猫猫旅行(无猫领养 +300 / 派出 / 到站领奖)
848
- mslxdff -workbuddy balance [--json] # 多号余额总览(total/dailyPacks/nextExpire,TTL 5min)
849
- mslxdff -workbuddy list # 列出账号(uid/domain/enterpriseId)
850
- mslxdff -workbuddy remove <uid> [--keep-file] # 按 uid 摘除(删 keys/auths 与 auths/workbuddy-<uid>.json)
851
- ```
852
- - **存储**:`state.json providerConfigs.workbuddy={ baseUrl:"https://copilot.tencent.com", keys:["k1",...], auths:[{uid,domain,enterpriseId,refreshToken}], allowedModels:["hy3",...] }`,`auths` 与 `keys` 一一对应(多号同索引),由 `node workbuddy-token-auto.js` 自动落盘(`workbuddy-<uid>.json` + `state.json`,`0600`)或 `-provider add workbuddy` 解析 JWT `uid` 自动追加。**凭据目录跟随 state 文件**(`<state 目录>/auths`,默认 `~/.config/mslxdff/auths`;旧 cwd 兜底 `./auths` 仅作只读兜底,ADR-0025)。`baseUrl` 默认 `https://copilot.tencent.com`(`MSLXDFF_WORKBUDDY_BASE_URL` 可覆盖)。
853
- - **上游**:`POST https://copilot.tencent.com/v2/chat/completions`(强制 `stream:true`,头含 `X-User-Id/X-Domain/X-Product:SaaS + Origin/Referer/User-Agent`,多号环形:`402/insufficient` 自动切号 + `balanceCache` TTL 5min,`header x-mslxdff-workbuddy-uid` 或 `model workbuddy/<uid>:<id>` 定号,`x-mslxdff-workbuddy-uid` 回显),`GET https://copilot.tencent.com/console/enterprises/personal/models`(`credits xN.NN` 升序,前缀 `workbuddy/`)+ `POST /v2/billing/meter/get-user-resource` 查余额(`workbuddy-balance.js`),401/403 自动 `POST /v2/plugin/auth/token/refresh` 回写并重放一次。**SDK 通道**:底层缺省改走 `@ai-sdk/openai-compatible`(官方 SDK 栈;局部 `MSLXDFF_WORKBUDDY_SDK` 或未设置时继承的全局 `MSLXDFF_UPSTREAM_ENGINE` 设 `legacy`/关闭词即回退原生 transport,不可用自动回退并告警一次,上层逻辑不变)。
854
- - **白名单**:同通用供应商(空=不限,非空仅名单内可用,`403 + x-mslxdff-allowlist:1` 直通,`/v1/models` 过滤)。
855
- - **共享**:`workbuddy` local-only(ADR-0015)恒不走组员,不参与 key 借出(ADR-0019 硬排除);`opencode` 同理恒排除。
856
- - **签到**:`POST https://www.codebuddy.cn/v2/billing/meter/daily-checkin` + `https://copilot.tencent.com/v2/billing/meter/daily-checkin` 双域,`code 0` 新增 100 credits/30d 裂变包,`code 10001 已签到` 视为成功;并行 3,`--json` 聚合 `results[].balance`;`workbuddy-token-auto.js` 已在 `refresh` 后自动 `spawn workbuddy-checkin.js`;daemon 默认每日 09:00 自动全号签到(`MSLXDFF_WORKBUDDY_CHECKIN_HOUR` 改时间,`=0` 关,启动时过期补签),新追加账号次日自动纳入无需配置,不再需要 `schtasks`。
857
- - **调用**:
858
- ```bash
859
- curl -H "Authorization: Bearer $(mslxdff -showtoken)" http://127.0.0.1:8989/v1/chat/completions -d '{"model":"workbuddy/hy3","messages":[{"role":"user","content":"hi"}]}'
860
- curl -H "Authorization: Bearer $(mslxdff -showtoken)" -H "x-mslxdff-workbuddy-uid: a06ef5f8" http://127.0.0.1:8989/v1/chat/completions -d '{"model":"workbuddy/hy3","messages":[]}' # 钉死 C
861
- curl -H "Authorization: Bearer $(mslxdff -showtoken)" http://127.0.0.1:8989/v1/chat/completions -d '{"model":"workbuddy/a06ef:hy3","messages":[]}' # 前缀亦可
862
- ```
863
-
864
- #### 存储与生效
865
-
866
- - **优先级**:`MSLXDFF_<ID>_KEY` env(单值)> `state.json providerConfigs.<id>.keys` / `providerKeys.<id>`(数组);`MSLXDFF_<ID>_BASE_URL` env 覆盖 `providerConfigs.<id>.baseUrl`。`opencode` 无视 key,恒为 `public`;`workbuddy` 额外支持 `MSLXDFF_WORKBUDDY_*`(见附录 A)。
867
- - **文件**:`~/.config/mslxdff/state.json` 的 `providerConfigs: { myapi: { baseUrl: "https://api.example.com/v1", keys: ["sk-..."] }, workbuddy: { baseUrl:"https://copilot.tencent.com", keys:["k1"], auths:[{uid,refreshToken}], allowedModels:["hy3"] } }`(新)与 `providerKeys: { openrouter: ["sk-..."] }`(兼容旧版单字符串)。
868
- - **生效时机**:修改后需重启 daemon(`stop` + `start` 或 ` -port` 触发的重启);`allowlist` 热更新立即生效。
869
- - **多 key 调度**:`src/providers/keyring.js` round-robin,`401/403/429/5xx` 冷却 30s(`MSLXDFF_GENERIC_COOLDOWN_MS` / `MSLXDFF_OPENROUTER_COOLDOWN_MS` / `MSLXDFF_WORKBUDDY_COOLDOWN_MS`),全冷却则抛 `provider temporarily unavailable`。
870
-
871
- ---
872
-
873
- ### `-timezone` / `--timezone` / `-tz` / `--tz`(时区配置,默认 Asia/Shanghai)
874
-
875
- - **语法**:
876
- ```bash
877
- mslxdff -timezone # 查看当前时区(state + env)
878
- mslxdff -timezone set Asia/Shanghai # 设为上海时间(落盘 state.json)
879
- mslxdff -timezone set UTC # 设为 UTC
880
- mslxdff -timezone Asia/Tokyo # 直接设(set 可省略)
881
- mslxdff -timezone clear # 恢复默认 Asia/Shanghai
882
- MSLXDFF_TZ=UTC mslxdff -status # 临时用 UTC(env 覆盖,不落盘)
883
- ```
884
- - **作用**:统一所有时间展示(`logs`/`rotation.log`/`banned until`/`token createdAt`/`-log`/`-status`/`free` 等)所用时区。`state.json: timezone` 持久化,`MSLXDFF_TZ`(或 `MSLXDFF_TIMEZONE`/`TZ`)环境变量临时覆盖且优先级最高。
885
- - **校验**:`isValidTimezone` 用 `Intl.DateTimeFormat` 校验,非法则 `无效时区` 并 `exit 1`,示例:`Asia/Shanghai, UTC, America/New_York, Europe/London, Asia/Tokyo`。
886
- - **生效**:`saveTimezone` 原子写 `state.json`(`0600`),即时生效(下次 `fmtShanghai*` 调用即用新时区);`clear` 删 `timezone` 字段回退默认。
887
- - **实现**:`src/state/schemas/timezone.js`(`DEFAULT_TZ=Asia/Shanghai`)+ `src/time.js`(`getTimezone()`→`loadTimezone()`,所有 `fmt*` 统一走 `tzParts`),`src/logs.js`/`rotation-log.js`/`groups.js`/`token.js`/`system.js` 均已改用 `fmtShanghaiYMDHMS/HMS`。
888
- - **示例**:
889
- ```bash
890
- mslxdff -timezone
891
- # → timezone: Asia/Shanghai
892
- # state: Asia/Shanghai (默认 Asia/Shanghai)
893
- # env : (未设 MSLXDFF_TZ)
894
- mslxdff -timezone set America/New_York
895
- # → timezone 已设为: America/New_York
896
- ```
897
-
898
- ---
899
-
900
- ## 7. 外部同步(WorkBuddy / opencode)
901
-
902
- ### `-setto opencode [modelId|--all]` / `--setto opencode [modelId|--all]`
903
-
904
- - **语法**:`mslxdff -setto opencode [modelId]`(`--setto` 等价);批量 `mslxdff -setto opencode --all` / `-a` / `all`
905
- - **作用**:把本地网关 `http://127.0.0.1:<port>/v1` 以 `provider.mslxdff` 写入 `~/.config/opencode/opencode.json`,使 opencode 把 mslxdff 当供应商(`mslxdff/<model>`)。模型键直写裸名(如 `deepseek-v4-flash-free`),含 `/` 的自动转 `-`(如 `bai/deepseek-v4-flash`→`bai-deepseek-v4-flash`),到达 8989 后网关通过 `~/.config/mslxdff/model-aliases.json` 自动还原为 `/` 形式,`opencode` 里选 `mslxdff/deepseek-v4-flash-free` 或 `mslxdff/bai-deepseek-v4-flash` 直达本地同名模型。
906
- - **参数**:
907
- - 无 `modelId`:取 `loadPreferredModel() || getPreferredModel()`(`big-pickle` 兜底)单条同步。
908
- - 有 `modelId`:`normalizeModel(raw)` 归一后直写裸名(`/`→`-`),`savePreferredModel`,`modelId` 不能为 `auto` 或空。
909
- - `--all` / `-a` / `all`:批量同步全部 `modelPicks`(`state.json` 的勾选集,空则回退到首选),每条 `inserted/updated` 累积,原子写一次完成。
910
- - **流程**:
911
- 1. 单条时 4s 超时尝试刷新模型列表,若 `id` 及其 `dash` 形态均不在 free 列表则 `warn` 但仍继续;批量时跳过校验直接同步。
912
- 2. `loadToken()` 取 token,`getPort() || MSLXDFF_PORT || 8989` 取端口,`opencodeConfigPath()` 取文件(`OPENCODE_CONFIG` 可覆盖)。
913
- 3. `syncToOpencode({ id, token, port, file, keep })`:`/`→`-` 转存储键,`/` 形态同时注册 `model-aliases.json` 的 `dash→slash` 映射;已存在(同存储键)则 `updated`,否则 `inserted`,保留其他 `provider`,原子写 `tmp→rename`,损坏备份 `.bak`。
914
- 4. 剪枝(`keep = picks 非空 ? picks : null`,`null` 时跳过):删 `provider.mslxdff.models` 里归一化(去 `mslxdff-` 前缀 + `/`→`-`)后不在 `keep ∪ {本次id}` 的键,输出 `pruned N 个失效模型`。
915
- - **输出**:
916
- ```
917
- default model set to: deepseek-v4-flash-free (daemon hot-reloads on next request)
918
- synced to opencode: inserted "deepseek-v4-flash-free" @ C:\Users\you\.config\opencode\opencode.json
919
- url: http://127.0.0.1:8989/v1
920
- opencode 选 mslxdff/deepseek-v4-flash-free 直达本地 deepseek-v4-flash-free
921
- # 批量
922
- synced to opencode: 19 inserted, 0 updated, total 19 @ ...\opencode.json
923
- url: http://127.0.0.1:8989/v1
924
- models: big-pickle, deepseek-v4-flash-free, bai/deepseek-v4-flash, ...
925
- ```
926
- - **入站**:网关对 `POST /v1/chat/completions` 的 `model` 做两段还原:`mslxdff/<model>` 剥前缀 → `model-aliases` 的 `dash→slash`(如 `bai-deepseek-v4-flash`→`bai/deepseek-v4-flash`),`x-mslxdff-alias` 头回显。
927
- - **示例**:
928
- ```bash
929
- mslxdff -setto opencode deepseek-v4-flash-free
930
- mslxdff -setto opencode bai/deepseek-v4-flash # 存为 bai-deepseek-v4-flash,自动映射
931
- mslxdff -setto opencode big-pickle
932
- mslxdff -setto opencode --all # picks 全部进 opencode 菜单
933
- mslxdff -setto opencode # 用当前首选
934
- ```
935
-
936
- ### `-setto workbuddy [modelId]` / `--setto workbuddy [modelId]`
937
-
938
- - **语法**:`mslxdff -setto workbuddy [modelId]`
939
- - **作用**:设默认模型并原子写入 WorkBuddy 的 `~/.workbuddy/models.json`,供 WorkBuddy 以 `http://127.0.0.1:<port>/v1` 为 OpenAI 兼容端点调用 mslxdff。
940
- - **参数**:
941
- - 无 `modelId`:取 `loadPreferredModel() || getPreferredModel()`(当前首选/出厂默认 `big-pickle`)。
942
- - 有 `modelId`:`normalizeModel(raw)` 归一化后 `savePreferredModel(norm)`,`modelId` 不能为 `auto` 或空,否则 `modelId 不能为 auto 或空`。
943
- - **流程**:
944
- 1. (可选)4s 超时尝试刷新模型列表,若 `id` 不在 free 列表则 `warn: "id" not in current free list (...)` 但仍继续同步。
945
- 2. `loadToken()` 取 token,`getPort() || MSLXDFF_PORT || 8989` 取端口,`workbuddyModelsPath()` 取文件路径。
946
- 3. `syncToWorkbuddy({ id, token, port, file, keep })`:若 `models.json` 不存在则插入 `127.0.0.1/v1` 条目,存在则更新其 `token/port/model`。
947
- 4. 剪枝(`keep` 口径同上):只删我们写的本地条目(`127.0.0.1`)中不在 `keep ∪ {本次id}` 的,非本地条目永不动,输出 `pruned N 个失效模型`。
948
- - **输出**:
949
- ```
950
- default model set to: <id> (daemon hot-reloads on next request)
951
- synced to WorkBuddy: updated "big-pickle" @ C:\Users\you\.workbuddy\models.json
952
- url: http://127.0.0.1:8989/v1/chat/completions
953
- ```
954
- - **约束**:WorkBuddy 仅认 `127.0.0.1/v1`,不认 `localhost`。
955
- - **示例**:
956
- ```bash
957
- mslxdff -setto workbuddy big-pickle
958
- mslxdff -setto workbuddy openrouter/google/gemma-3-27b-it:free
959
- mslxdff -setto workbuddy # 用当前首选同步
960
- ```
961
-
962
- ### `-setto chatgpt [modelId]` / `--setto chatgpt [modelId]`
963
-
964
- - **语法**:`mslxdff -setto chatgpt [modelId]`(`codex` 等价,`--setto` 等价)。
965
- - **作用**:设默认模型并写入 Codex CLI / IDE 插件 / ChatGPT 桌面端三端共用的 `~/.codex/config.toml`(`CODEX_HOME` 可覆盖),把本地网关注册为 `model_providers.mslxdff` 自定义 provider。
966
- - **原理**:现行 Codex 自定义 provider 只认 Responses API(`wire_api` 唯一合法值 `responses`),所以网关侧新增 `POST /v1/responses`(`src/routes/responses-route.js`,复用 ChatPipeline 全链路,只做 Responses⇄Chat 形状翻译,见 `src/responses/translate.js`)。
967
- - **鉴权**:`[model_providers.mslxdff.auth]` 配绝对路径 `node + bin/mslxdff.js -showtoken`(Codex 起子进程不继承终端 PATH,裸 `mslxdff` 会 `program not found`;Windows 路径用 TOML 单引号防转义),Codex 定时调命令取 Bearer,token 永不落盘且 rotation 后自动生效。
968
- - **流程**:
969
- 1. 无 `modelId` 取当前首选,有则归一化后 `savePreferredModel`(`auto` 拒绝)。
970
- 2. `syncToCodex({ id, port, file })`:顶层 `model`/`model_provider = "mslxdff"` upsert + `[model_providers.mslxdff]` 整段替换(含 auth 子段),用户其他段(mcp 等)原样保留,原子写。
971
- - **输出**:
972
- ```
973
- synced to codex: updated "big-pickle" @ C:\Users\you\.codex\config.toml
974
- url: http://127.0.0.1:8989/v1/responses (Responses API)
975
- 鉴权走 mslxdff -showtoken 命令(token 不落盘),直接 codex exec "hi" 验证
976
- ```
977
- - **约束**:`previous_response_id` 多轮状态网关不存(stateless,每轮全量 input);thinking 模型 reasoning 暂不透传,先用非 thinking 模型(如 `big-pickle`);`GET /v1/models` 对 Codex 调用者(UA/originator `codex_*` 或 `?client_version=`)额外返回顶层 `models: []`(Codex 解码硬要该字段,填真目录会覆盖其内置 agent prompt,必须空;学 OmniRoute);`response.completed` 的 `usage` 必须转 Responses 口径(`input_tokens/output_tokens`,Codex 硬解码,缺则整轮作废),done 事件带累积全文。
978
- - **排障**:`MSLXDFF_RESPONSES_DEBUG=1 mslxdff -restart` 后 `daemon.log` 看 `[responses]` 四段(`req` 请求摘要 → `chat` 翻译后 → `done-json` 上游结果 → `done-stream/done-resp` 发出统计:块数/字数/tool 调用/finish/usage),Codex 侧看 `~/.codex/logs_2.sqlite` 的 `Request completed ... 127.0.0.1 ... status=` 与 `failed to .../missing field` 行。
979
- - **示例**:
980
- ```bash
981
- mslxdff -setto chatgpt big-pickle
982
- mslxdff -setto chatgpt # 用当前首选同步
983
- codex exec "hi" # 验证走本地网关
984
- ```
985
- - **切换模型(三选一,provider 不用动)**:
986
- 1. `mslxdff -setto chatgpt <modelId>` —— 1 秒重写 `model = ...` 行,最常用。
987
- 2. 单次覆盖:`codex exec -m <modelId> "提示词"`(CLI 参数优先,不改配置文件)。
988
- 3. 直接改 `~/.codex/config.toml` 第一行的 `model = "..."`,存盘即生效(桌面端重进会话)。
989
- - 可填任何网关能服务的 id(free 列表、`-model list` 里的、`workbuddy/...` 等供应商前缀形态);`model_provider = "mslxdff"` 保持不动。
990
-
991
- ---
992
-
993
- ## 8. 群组网络
994
-
995
- > 群组用于分散 `opencode.ai` 的 IP 级限流:A 转发给 B 时,上游看到的是 B 的出口 IP。组内还支持 failover、hedge 对冲、宽带中继等。Leader 持有 `groups.<name>.members`,成员通过 `-addtogroup` 注册到 leader。
996
-
997
- ### `-creategroup <name>` / `--creategroup <name>` / `-group create <name>`
998
-
999
- - **语法**:三者等价,`<name>` 即组密码。
1000
- - **作用**:在本节点创建群组,本节点成为 leader。`groups.create(name)`,`markJoined({ name, leaderUrl:"", myUrl:"", memberName:"leader" })`,`syncAllJoinedGroups` 同步 peers。
1001
- - **输出**:
1002
- ```
1003
- group created: my@mslxd # 或 already exists
1004
- members on this node: 0 (failover: 0)
1005
- others join with: mslxdff -addtogroup <this-node-host> my@mslxd
1006
- ```
1007
- - **示例**:`mslxdff -creategroup my@mslxd`
1008
-
1009
- ### `-addtogroup [<leader-host> [<name>]] [--broadband]` / `--addtogroup`
1010
-
1011
- - **语法**:`mslxdff -addtogroup <leader-host> <name> [--broadband]`
1012
- - `<leader-host>` 可为 `host`、`host:port` 或完整 `http(s)://host:port`(尾斜杠自动去除,缺端口默认 `:8989`;非法输入给人话原因)。
1013
- - `[--broadband]` 可选,位于任意位置(会被过滤),表示宽带动态 IP 成员。
1014
- - **省略参数**(`-addtogroup` 或 `-addtogroup <host>`)→ 进入**手机宽带接入向导**(见下节)。
1015
- - **作用**:以成员身份加入远端 leader 的群组。
1016
- - **流程**:
1017
- 1. 归一化 `leaderUrl`(`normalizeLeaderUrl`),取 `myToken`、`myPort`(`effectivePort()`)。
1018
- 2. 组装 `joinBody`:
1019
- - 普通:`{ name, key:name, leaderUrl, myPort, token, kind:"static" }`,`myUrl` 由 leader 返回的 `data.you.url` 确定。
1020
- - 宽带:`{ name, key:name, leaderUrl, url:"relay://<token8>", token, kind:"broadband" }`,`myUrl`/`memberName` 均为 `relay://...`。
1021
- 3. `POST <leaderUrl>/v1/groups/join`(8s 超时),失败回 `HTTP <status> <text>` 或 `fetch failed`。
1022
- 4. `markJoined({ name, leaderUrl, myUrl, memberName, kind })`,`syncAllJoinedGroups`。
1023
- - **输出**:
1024
- ```
1025
- joined group "my@mslxd" at http://1.2.3.4:8989
1026
- 1 failover target(s) configured
1027
- # 宽带:
1028
- joined group "my@mslxd" at http://1.2.3.4:8989 [broadband]
1029
- 1 failover target(s) configured (broadband via leader, local 127.0.0.1)
1030
- ```
1031
- - **失败**:`join failed: ...` 并 `exit 1`。
1032
- - **示例**:
1033
- ```bash
1034
- mslxdff -addtogroup 1.2.3.4 my@mslxd
1035
- mslxdff -addtogroup http://1.2.3.4:8989 my@mslxd --broadband
1036
- ```
1037
-
1038
- ### 手机宽带接入向导(Termux 一键入组)
1039
-
1040
- - **语法**:`mslxdff -addtogroup`(零参数)或 `mslxdff -addtogroup <leader-host>`(只缺组名)
1041
- - **作用**:面向手机等无公网入站设备:只问「组长地址」「组名」两问,自动以 `--broadband` 加入、自动确保后台服务运行、回报出口 IP,全程人话反馈。
1042
- - **流程**:
1043
- 1. 打印 banner,依次问 `1/2 组长地址(ip:端口)`、`2/2 组名`(非法输入给人话原因并重试,最多 3 次;输入结束则体面取消)。
1044
- 2. `joinGroupCore({ isBroadband: true })` 加组;失败给「地址/端口、组长是否在跑、网络是否可达」三条排障 + 重试命令。
1045
- 3. 调组长 `POST /v1/groups/relay/heartbeat` 取 `ip`(组长 `clientIp()` 视角)作为**出口 IP**;组长暂时不可达时降级显示「待确认」,不阻塞。
1046
- 4. `ensureServiceRunning()`:已有后台 daemon 则复用(daemon 每 10s 自动接上 SSE 长连,无需重启),否则 `startDaemon()` + 等 `/health`。
1047
- 5. 打印保活指引(Termux 环境提示 `termux-wake-lock`)。
1048
- - **输出**:
1049
- ```
1050
- 手机宽带接入 — 让这台设备成为组内出口(不占端口,只出站)
1051
-
1052
- 1/2 组长地址(ip:端口,如 149.13.91.10:8989): 149.13.91.10:8989
1053
- 2/2 组名: my@mslxd
1054
- → 正在连接组长 http://149.13.91.10:8989 ...
1055
- ✓ 已加入组「my@mslxd」(宽带成员 relay://08cb0592)
1056
- → 正在确认出口 IP ...
1057
- ✓ 出口 IP: 203.0.113.7(组长视角,上游分流按这个 IP)
1058
- → 正在启动后台服务 ...
1059
- ✓ 后台服务已启动(pid 1234)
1060
-
1061
- 保持在线:
1062
- • termux-wake-lock 防止系统休眠杀进程(强烈建议)
1063
- • mslxdff -status 查看组与出口状态
1064
- • mslxdff -stop 停止贡献
1065
- ```
1066
- - **Termux 首次使用**:
1067
- ```bash
1068
- pkg install nodejs-lts # 装 Node(Termux 仓库自带,无需 root)
1069
- npm i -g mslxdff # 或 npx -y mslxdff@latest
1070
- mslxdff -addtogroup # 进向导:输入组长 ip:端口 与组名
1071
- termux-wake-lock # 防系统休眠杀进程
1072
- ```
1073
- - **失败**:地址/组名非法重试 3 次后 `exit 1`;加入失败 → 人话原因 + 排障三条 + `exit 1`;输入结束 → `· 已取消(输入结束)`。
1074
- - **完整手机接入指南(Termux 上手 5 步 + 排错表)**:`docs/MOBILE.md`
1075
- - **示例**:
1076
- ```bash
1077
- mslxdff -addtogroup # 交互式(手机推荐)
1078
- mslxdff -addtogroup 149.13.91.10:8989 # 只缺组名,仍交互
1079
- ```
1080
-
1081
- ### `-group sync`
1082
-
1083
- - **语法**:`mslxdff -group sync`
1084
- - **作用**:刷新所有已加入群组的成员列表到本地 failover peers。
1085
- - **行为**:
1086
- - Leader 组:直接读 `groups.list()[name].members` → `syncPeersFromMembers(..., skipIds:["leader"])`。
1087
- - 成员组:`refreshGroupMembers({ leaderUrl, memberName, url:myUrl, token, kind })` 幂等重注册以拿最新成员 → `syncPeersFromMembers`。
1088
- - **输出**:每组一行 `name: N member(s), M failover target(s) configured` 或 `sync failed — error`;未加入任何组则 `not joined to any group (use -addtogroup or -creategroup)`。
1089
- - **示例**:`mslxdff -group sync`
1090
-
1091
- ### `-group leave <name>`
1092
-
1093
- - **语法**:`mslxdff -group leave <name>`
1094
- - **作用**:成员侧本地离开单群组(不通知 leader,仅本地 `groupsJoined` 过滤 + `peers.removeByGroup(name)`)。
1095
- - **输出**:`left group "name" (N member(s) removed)` 或 `not a member of group "name"`。
1096
- - **与 `-leavegroup` 区别**:`leave` 是单组本地移除;`-leavegroup` 是全量离开并尝试通知 leader 注销。
1097
- - **示例**:`mslxdff -group leave my@mslxd`
1098
-
1099
- ### `-group list`
1100
-
1101
- - **语法**:`mslxdff -group list`
1102
- - **作用**:列出本节点所有群组及成员,带健康探测与稳定序号(用于 `remove`)。
1103
- - **输出结构**:
1104
- ```
1105
- my@mslxd (2 members)
1106
- 1. http://1.2.3.4:8989 ok 23ms
1107
- 2. http://5.6.7.8:8989 [broadband] via leader 12s ago ip=5.6.7.8
1108
- leader http://leader:8989 ok 15ms
1109
- joined groups (1):
1110
- my@mslxd leader http://leader:8989
1111
- ```
1112
- - 成员按 state 顺序渲染,序号 `1..N` 稳定(leader 排除在外,单独 `leader` 行)。
1113
- - 每个成员并发 `probeHealth`(`fetch /health`,1.5s 超时);宽带成员不探测,显示 `via leader Xs ago` / `stale` + `ip`。
1114
- - Leader 本机与成员组分别取成员:leader 读本地 `groups.list()`,成员走 `refreshGroupMembers`(失败则 `members unavailable — leader unreachable`)。
1115
- - **示例**:`mslxdff -group list`
1116
-
1117
- ### `-group remove <seq>`
1118
-
1119
- - **语法**:`mslxdff -group remove <seq>`(`seq` 为 `list` 显示的 `1-based` 序号,leader 不计)
1120
- - **作用**:仅 leader 侧:按序号踢出成员 `groups.removeMember(name, {url})`。
1121
- - **校验**:
1122
- - `seq` 非整数或 `<1` → `usage: mslxdff -group remove <seq>` 并 `exit 1`。
1123
- - 本节点非 leader(`!joined.find(g=>!g.leaderUrl)`)→ `group remove requires being the leader — this node leads no group`。
1124
- - 序号越界 → `member #N not found — group "name" has M member(s)`。
1125
- - **输出**:`removed http://... from "name"` 或 `already gone`。
1126
- - **示例**:`mslxdff -group remove 2`
1127
-
1128
- ### `-leavegroup` / `--leavegroup` / `-leave-groups`
1129
-
1130
- - **语法**:`mslxdff -leavegroup`
1131
- - **作用**:成员侧离开**所有**已加入群组。
1132
- - **行为**:
1133
- - 遍历 `loadGroupsJoined()`:
1134
- - 成员组(`g.leaderUrl` 存在):`peers.removeByGroup(g.name)` + `POST <leaderUrl>/v1/groups/leave`(`Authorization Bearer myToken`,`{name}`),成功 `left (deregistered from ...)`,失败 `left locally (leader said: ...)` 或 `leader unreachable: ...`。
1135
- - Leader 组:跳过,记入 `leaders`。
1136
- - 持久化:`saveGroupsJoined( filtered: 仅保留 leader 组 )`,打印 `left N group(s)`。
1137
- - 若有 leader 组被跳过,额外提示:
1138
- ```
1139
- skipped N group(s) where this node is the leader:
1140
- my@mslxd — leaders can't leave; disband it with: mslxdff -delgroup my@mslxd
1141
- ```
1142
- - **与 `-group leave` 区别**:`-leavegroup` 批量且尝试通知 leader;`-group leave` 单组本地移除。
1143
- - **示例**:`mslxdff -leavegroup`
1144
-
1145
- ### `-delgroup <name>` / `--delgroup <name>`
1146
-
1147
- - **语法**:`mslxdff -delgroup <name>`
1148
- - **作用**:仅 leader:解散本节点领导的群组(删 `groups` 定义 + `peers.removeByGroup` + `groupsJoined` 过滤)。
1149
- - **校验**:
1150
- - `groups.list()[name]` 不存在:查 `groupsJoined` 是否为成员组 → 提示 `is led by ... — you are a member, use -leavegroup to leave it`;或 `not found on this node` 并 `exit 1`。
1151
- - **成功**:`group "name" disbanded (N members removed)`。
1152
- - **示例**:`mslxdff -delgroup my@mslxd`
1153
-
1154
- ### `-resetban [ip]` / `--resetban [ip]`
1155
-
1156
- - **语法**:`mslxdff -resetban` 或 `mslxdff -resetban <ip>`
1157
- - **作用**:清除加群失败封禁。`createBansService({ windowMs, threshold })`,`bans.clear(ip)`(无 ip 则全清)。
1158
- - **输出**:`ban cleared for 1.2.3.4` 或 `all bans cleared`。
1159
- - **示例**:
1160
- ```bash
1161
- mslxdff -resetban
1162
- mslxdff -resetban 1.2.3.4
1163
- ```
1164
-
1165
- ### `-use-group [on|off]` / `--use-group [on|off]`
1166
-
1167
- - **语法**:`mslxdff -use-group`(查询)或 `mslxdff -use-group on|off`(设置)/ `--use-group=off`
1168
- - **作用**:控制 opencode 免费池失败时是否走组员网络(via-route/peer/broadband/hedge)。默认 `on`(允许组员中继,分散限流);设 `off` 后,**所有供应商**都不再尝试组员,仅本机直连。**带 key 上游默认恒直连**(ADR-0023:bai/sensenova/aihubmix/ocgo/internapi/tokenrouter 等失败直接返回上游错误,不走 peer 竞速——peer 嵌套 502 反压曾把可直连成功的请求拖成整单 502;`MSLXDFF_USE_GROUP_KEYS=1` 可开回组员)。**硬禁**:`workbuddy`/cline 系(local-only:本机账号绑定,组员无该账号转过去也用不了)——无论开关一律仅本机直连。状态持久化到 `state.json: useGroup`(`true/false`),热重载(下次请求即生效,无需重启)。环境变量 `MSLXDFF_USE_GROUP=0|1` 可临时覆盖(优先级高于 state)。
1169
- - **输出**:
1170
- - 查询:`use-group: on (effective, 仅 opencode 免费池) / stored: on / keys: off / env ...`
1171
- - 设置:`use-group set to off (stored in state.json) / opencode 也不再走组员网络 / 带 key 上游默认恒直连 ...`
1172
- - **示例**:
1173
- ```bash
1174
- mslxdff -use-group # 查询当前
1175
- mslxdff -use-group off # 仅本机,不走组员(特殊排查/限流时)
1176
- mslxdff -use-group on # 恢复默认
1177
- MSLXDFF_USE_GROUP=0 mslxdff -d # 临时关闭(env 覆盖)
1178
- ```
1179
-
1180
- ---
1181
-
1182
- ## 9. 对话终端
1183
-
1184
- ### `-chat ["prompt"]` / `--chat ["prompt"]`
1185
-
1186
- - **语法**:`mslxdff -chat`(进入常驻 REPL)或 `mslxdff -chat "把 hy3 设为默认模型"`(单次执行后退出)
1187
- - **作用**:自然语言转精确 CLI 命令并执行。背后是 `src/chat/*` 独立模块,**三级兜底 `mimo-v2.5-free → big-pickle → 本地网关 auto:8989`**(前两者直连 `https://opencode.ai/zen/v1/chat/completions`,均失败则自动切本地 `http://127.0.0.1:<port>/v1/chat/completions` 的 `auto` 择优——**带 `x-mslxdff-auto-provider: opencode` 头,auto 候选也限定在 opencode 免费池**,不会用到 workbuddy/deepseek 等其他供应商;30s 超时,`gateway no choice` 等会透传),把用户说的简称(如 `hy3`)自行查可用模型列表补全为全称(如 `hy3-free`)再调用工具。失败时 REPL 尾部会显示 `mimo→big-pickle` 或 `gateway auto` 的 `fallback/gateway-fallback` 标记及耗时。**`/model <id>` 锁定后退出三级兜底**:严格只用该模型,失败即报错不换模型(opencode 上游 free 池限定,见下)。**-chat 整体只支持 opencode 上游模型**(直连 opencode.ai 免费池),其他供应商(deepseek/ workbuddy/ cline/ 等)走网关 `curl` 探活。
1188
- - **交互**:
1189
- - `mimo> ` 提示符,支持上下历史、`/help`(看可用说法)、`/model`(查看/锁定模型)、`/clear`(清历史)、`/history`(看条数)、`/exit`/`quit`/`退出`/`Ctrl+D` 退出。
1190
- - `/model <id>`:锁定会话模型,**仅限 opencode 上游免费池**(裸 id 无供应商前缀,`-free` 后缀或 `big-pickle`,共 8 个:`mimo-v2.5-free`、`big-pickle`、`ling-3.0-flash-fin-free`、`deepseek-v4-flash-free`、`nemotron-3.5-lightning-free`、`nemotron-3-ultra-free`、`muse-spark-1.3-contributor-free`、`muse-spark-1.2-contributor-free`);**带供应商前缀(`deepseek/chat-free` 等)一律拒绝**,非 free 模型拒绝并列当前 free 池。锁定后**严格单模型**:该模型失败直接报错(含失败原因与"不自动换模型"提示),绝不降级到 mimo/big-pickle/auto;提示符变为 `<id>>`;`/model auto`(或 `clear`)解除回默认三级链;`/model` 无参显示当前锁定。锁定为会话内存态,退出 REPL 即失效。
1191
- - 单次模式:`mslxdff -chat "查看组列表"` 直接执行一次后退出,适合管道/脚本。
1192
- - **工具**(3 类 + 纯回答):
1193
- - `run_command`:执行 `cli_help.md` 所列任意命令(不含 `mslxdff` 前缀),**仅拦截 `-uninstall`**,`-stop`/`-port` 等可执行;模糊匹配与精确性由大模型负责,进程侧不做二次归一。
1194
- - `read_file`:读取**项目内**文件(`src/` `docs/` `package.json`)或日志目录(`~/.config/mslxdff/*`),用于“看看日志/配置”,超出项目根或超 20KB 截断,目录则列文件名。
1195
- - `curl`:网络/HTTP 探活,检测上游或本机可用性。支持 `url` 简写(`upstream`=`https://opencode.ai/zen/v1/models`、`local/health`=`http://127.0.0.1:<port>/health`、`local/models`),也支持完整 `http(s)` URL;可选 `method`/`headers`/`body`/`timeoutMs`,上游自动补 `x-opencode-client/desktop` + `Authorization: Bearer public` + `User-Agent: opencode/<semver>` + opencode 形状 `x-opencode-session/request`(zen 免费层 2026-09-17 起的客户端身份门禁),本机 `/v1/*` 自动带 state token。返回状态码、耗时、响应头与前 6KB body,便于自检“上游是否活着/本地是否监听”。
1196
- - 纯回答:闲聊或解释时不调工具,直接中文回复。
1197
- - **历史与压缩**:
1198
- - 持久化:`~/.config/mslxdff/chat-history.json`(`MSLXDFF_CHAT_HISTORY` 可覆盖),存最近 120 条,`daemon` 重启不影响(chat 是前台独立进程,与 daemon 无父子关系)。
1199
- - 压缩原理:当总字符 > `400000`(`CHAT_HISTORY_MAX_CHARS`≈128k*95%≈400k 字符)时触发——**保留 system + 最近 40 条**,将其余旧消息打包让**大模型自己做摘要**(`summarizeHistory` 发一次 `mimo`/`big-pickle` 调用,提示“压缩成 800 字内中文摘要,保留关键操作与偏好及时间线”),成功则用 `【历史摘要】…` 替代旧段,失败则直接截断。这样大模型 128k 上下文下接近 95% 才压缩,避免频繁压缩。
1200
- - **独立进程保证**:`mslxdff -chat` 就是用户终端的前台进程,`bin` 中直接 `await startChat()`,不经 `startDaemon`,`daemon` 的 `stopDaemonIfOutdated`/`-port` 重启完全走另一条链路。
1201
- - **示例**:
1202
- ```bash
1203
- mslxdff -chat
1204
- # mimo> 设置hy3为默认模型
1205
- # → 执行: mslxdff -model set hy3-free
1206
- # → OK: default model set to: hy3-free
1207
-
1208
- mslxdff -chat "查看最近20条日志"
1209
- # → 执行: mslxdff -log 20
1210
-
1211
- mslxdff -chat "读一下 src/logs.js"
1212
- # → 读取: src/logs.js OK
1213
- ```
1214
-
1215
- ---
1216
-
1217
- ## 10. 帮助
1218
-
1219
- ### `-help` / `--help` / `-h`
1220
-
1221
- - **语法**:`mslxdff -help`(任一别名出现即触发)
1222
- - **作用**:打印 `printHelp()` 的完整 Usage + Environment 段并 `exit 0`,不执行其他逻辑。
1223
- - **内容**:与本文件一致的精简版(含 `mslxdff vX.Y.Z — OpenCode Free OpenAI-compatible proxy` 头)。
1224
- - **示例**:`mslxdff -help` / `mslxdff --help` / `mslxdff -h`
1225
-
1226
- ---
1227
-
1228
- ## 附录 A 环境变量
1229
-
1230
- | 变量 | 默认 | 说明 |
1231
- |---|---|---|
1232
- | `MSLXDFF_PORT` | `8989` | 监听端口(`state.json port` 优先于它;裸 `PORT` 忽略) |
1233
- | `MSLXDFF_HOST` / `MSLXDFF_BIND_HOST` | `0.0.0.0`(宽带成员自动 `127.0.0.1`) | 绑定 host(`src/server.js effectiveHost()`) |
1234
- | `MSLXDFF_STATE_FILE` | `~/.config/mslxdff/state.json` | state 持久化路径 |
1235
- | `MSLXDFF_TZ` / `MSLXDFF_TIMEZONE` / `TZ` | `Asia/Shanghai` | 时区(`state.json: timezone` 持久化,env 临时覆盖,`Intl` 校验;所有时间展示统一走此时区) |
1236
- | `MSLXDFF_DAEMON_DIR` | 随 state 派生(`dirname(stateFile)`) | daemon pid/log/models 目录 |
1237
- | `MSLXDFF_STATE_FLUSH_MS` | `500` | 热数据批量刷盘间隔,`0` 则同步刷(测试用) |
1238
- | `UPSTREAM_BASE_URL` | `https://opencode.ai` | 默认供应商上游 |
1239
- | `MSLXDFF_UPSTREAM_ENGINE` | `sdk` | 上游 wire 层引擎(ADR-0017):缺省即 AI SDK(opencode 流式 chat 走 `@ai-sdk/openai-compatible`,`muse-spark*` 等 responses 类走 `@ai-sdk/openai` 的 responses 适配器;响应带 `x-mslxdff-upstream-engine: sdk` 标记,复用 legacy keep-alive 连接池;非流式自动委派 legacy;SDK 不可用自动回退并告警一次);显式 `legacy` 或关闭词(`0/off/false/no/disable`)=原实现;通用 OpenAI 兼容族与 cline 的流式 chat 同源(供应商级 `MSLXDFF_<ID>_SDK` 显式设置即局部生效,未设置则继承本变量=全局一键熔断) |
1240
- | `UPSTREAM_AUTH_TOKEN` | `public` | 上游鉴权值(opencode 侧恒 `public`) |
1241
- | `MSLXDFF_OPENROUTER_KEY` | — | OpenRouter 单 key(env 优先于 state;多 key 用 `-provider` 持久化) |
1242
- | `MSLXDFF_OPENROUTER_BASE_URL` | `https://openrouter.ai/api/v1` | OpenRouter 上游地址(可覆盖为代理/测试地址) |
1243
- | `MSLXDFF_OPENROUTER_TIMEOUT_MS` | `30000` | OpenRouter 单次 fetch 超时 |
1244
- | `MSLXDFF_OPENROUTER_COOLDOWN_MS` | `30000` | 多 key 冷却(401/403/429/5xx 后) |
1245
- | `MSLXDFF_OA_KEEPALIVE_TIMEOUT` | `30000` | OpenRouter keepAlive 超时 |
1246
- | `MSLXDFF_OA_KEEPALIVE_MAX_TIMEOUT` | `60000` | keepAlive 最大超时 |
1247
- | `MSLXDFF_OA_KEEPALIVE_CONNECTIONS` | `20` | keepAlive 连接数 |
1248
- | `MSLXDFF_OPENROUTER_REFERER` | `https://github.com/mslxdff` | OpenRouter 必需 `HTTP-Referer` |
1249
- | `MSLXDFF_OPENROUTER_TITLE` | `mslxdff` | OpenRouter 必需 `X-Title` |
1250
- | `MSLXDFF_WORKBUDDY_KEY` | — | WorkBuddy 单 key(env 优先;多 key 用 `-provider workbuddy ...`) |
1251
- | `MSLXDFF_WORKBUDDY_BASE_URL` | `https://copilot.tencent.com` | WorkBuddy 上游(`POST /v2/chat/completions` + `GET /console/.../models`) |
1252
- | `MSLXDFF_WORKBUDDY_TIMEOUT_MS` | `30000` | WorkBuddy 单次 fetch 超时 |
1253
- | `MSLXDFF_WORKBUDDY_COOLDOWN_MS` | `30000` | WorkBuddy 多 key 冷却(401/403/429/5xx) |
1254
- | `MSLXDFF_WORKBUDDY_SHARE_KEYS` | `off` | WorkBuddy 共享开关(`1/on/true` 显式开,默认关) |
1255
- | `MSLXDFF_WORKBUDDY_SDK` | 继承 `MSLXDFF_UPSTREAM_ENGINE`(缺省 sdk) | WorkBuddy SDK 通道:缺省底层走 `@ai-sdk/openai-compatible`(optionalDependencies,需 Node>=18;不可用自动回退原生并告警一次);设 `legacy`/关闭词回退原生 transport;未设置则继承全局 `MSLXDFF_UPSTREAM_ENGINE`(全局 `legacy` 即一键熔断);上层轮换/刷新/reshape 全链复用 |
1256
- | `MSLXDFF_WORKBUDDY_CHECKIN` | `1` | daemon 每日自动签到开关(`0` 关;开则每天本地时 `MSLXDFF_WORKBUDDY_CHECKIN_HOUR` 全号签到+过期 token 续期,code 10001 幂等,落盘 `workbuddyCheckin {date}` 防重复,启动时过期补签) |
1257
- | `MSLXDFF_WORKBUDDY_CHECKIN_HOUR` | `9` | 自动签到小时(0~23 本地时,非法回退 9) |
1258
- | `WORKBUDDY_AUTH_DIR` | `<state 目录>/auths`(默认 `~/.config/mslxdff/auths`) | WorkBuddy token 落盘目录(`workbuddy-*.json`,`0600`)。**跟随 state 文件走,不再用 cwd 兜底**;旧 `./auths` 降级为只读兜底(ADR-0025) |
1259
- | `MSLXDFF_QODER_COOLDOWN_MS` | `30000` | qoder 多号冷却(401/403/429/5xx 与 fetch 异常——流式路径的真实状态码经内部头 `x-mslxdff-qoder-upstream-status` 带出后才判定,400 等业务错不冷却) |
1260
- | `MSLXDFF_QODER_TIMEOUT_MS` | `120000` | qoder 上游连接超时(COSY 会话请求的 `timeoutSignal`) |
1261
- | `MSLXDFF_QODER_STICKY_MS` | `600000` | qoder 同请求粘号 TTL(同一次客户端请求内复用同一个号,仅冷却才换,ADR-0036;`0`=关,退回每次调用 round-robin) |
1262
- | `MSLXDFF_QODER_CHECKIN` | `1` | qoder 每日自动签到开关(`0` 关;开启则每日本地时 `_CHECKIN_HOUR` 全号签到,按每号 region 选域名,落盘 `state.qoderCheckin`) |
1263
- | `MSLXDFF_QODER_CHECKIN_HOUR` | `9` | qoder 自动签到小时(`0~23` 本地时,非法回退 9) |
1264
- | `MSLXDFF_QWENWORK_COOLDOWN_MS` | `30000` | qwenwork 多号冷却(401/403/429/5xx 与 fetch 异常;额度错走 `MSLXDFF_QWENWORK_QUOTA_COOLDOWN_MS` 长冷却,400 等业务错不冷却) |
1265
- | `MSLXDFF_QWENWORK_QUOTA_COOLDOWN_MS` | `3600000` (1h) | qwenwork 额度耗尽号冷却(code 14018/中英额度措辞命中;额度按天恢复,短冷却等于反复撞死号) |
1266
- | `MSLXDFF_QWENWORK_TIMEOUT_MS` | `120000` | qwenwork 上游连接超时(COSY 签名请求的 `timeoutSignal`) |
1267
- | `MSLXDFF_QWENWORK_UA` / `_COSY_VERSION` / `_RELEASE_VERSION` / `_BUILD` | `qoderwork/1.0.5` / `1.1.18` / `1.0.5-26090901` / `26090901` | qwenwork 协议版本快照覆盖(排障用;客户端升级后 403 时改,改后必须现网复测) |
1268
- | `QWENWORK_AUTH_DIR` | `<state 目录>/auths` | qwenwork 账号落盘目录(`qwenwork-<uid>.json`,0600 tmp+rename;测试环境走系统 tmp) |
1269
- | `QWENWORK_DEBUG` | `0` | qwenwork 排障日志(`1` 打 `[qwenwork-chat]`:refresh 失败/身份补齐失败) |
1270
- | `MSLXDFF_<ID>_KEY` | — | 任意供应商的 env key(`<ID>` 大写、非字母数字转 `_`) |
1271
- | `MSLXDFF_<ID>_BASE_URL` | — | 通用供应商 env baseUrl(覆盖 `providerConfigs.<id>.baseUrl`) |
1272
- | `MSLXDFF_<ID>_SHARE_KEYS` | — | 任意供应商的共享开关覆盖(`1/true/on/yes` 视为开) |
1273
- | `MSLXDFF_<ID>_RESPONSES_PATH` | `/responses` | 通用供应商 responses 端点路径覆盖(ADR-0032):responses 类模型(`muse-spark*`,判定=models.dev 模型级 `provider.npm` + 前缀兜底,与 `/v1/models` 的 `capabilities.upstreamApi` 同源)自动改打该路径(缺省 `<baseUrl>/responses`),不再走 `chatPath`(此前固定 `/chat/completions` → 上游 `503 Endpoint is unavailable`);流式优先 `@ai-sdk/openai` responses 适配器(含加密思考往返),非流式/SDK 不可用走原生 `chatToResponsesBody` 正转换 + 反向整形,出参恒 chat 形状;异形上游可覆盖(如 `MSLXDFF_OCGO_RESPONSES_PATH=/zen/go/v1/responses`) |
1274
- | `MSLXDFF_GENERIC_TIMEOUT_MS` | `30000` | 通用供应商单次 fetch 超时 |
1275
- | `MSLXDFF_GENERIC_COOLDOWN_MS` | `30000` | 通用供应商多 key 冷却 |
1276
- | `MSLXDFF_GENERIC_KEEPALIVE_TIMEOUT` | `30000` | 通用 keepAlive 超时 |
1277
- | `MSLXDFF_GENERIC_KEEPALIVE_MAX_TIMEOUT` | `60000` | 通用 keepAlive 最大超时 |
1278
- | `MSLXDFF_GENERIC_KEEPALIVE_CONNECTIONS` | `20` | 通用 keepAlive 连接数 |
1279
- | `MODELS_REFRESH_MS` | `7200000` (2h) | 模型列表后台刷新间隔 |
1280
- | `MSLXDFF_PREFERRED_MODEL` | `big-pickle` | 覆盖出厂首选模型(`src/auto.js`) |
1281
- | `MSLXDFF_MODEL_COOLDOWN_MS` | `60000` | 模型错误冷却 |
1282
- | `MSLXDFF_SLOW_COOLDOWN_MS` | `300000` (5m) | 慢模型冷却 |
1283
- | `MSLXDFF_PEER_COOLDOWN_MS` | `30000` | 组员 failover 冷却(普通失败) |
1284
- | `MSLXDFF_PEER_LIMIT_COOLDOWN_MS` | `300000` (5m) | 组员**连续 429** 后的长冷却(第 2 次起算,期间不再试它) |
1285
- | `MSLXDFF_PEER_HEAT_MS` | `300000` (5m) | 组员成功后 hot 时长 |
1286
- | `MSLXDFF_MAX_HOPS` | `3` | 组员转发最大跳数 |
1287
- | `MSLXDFF_GROUP_SYNC_MS` | `60000` (1m) | 群组成员同步间隔 |
1288
- | `MSLXDFF_BROADBAND_STALE_MS` | `90000` (90s) | 宽带成员心跳过期阈值 |
1289
- | `MSLXDFF_PEER_HEALTH_TTL_MS` | — | 组员健康缓存 TTL(`src/routes/peers.js`) |
1290
- | `MSLXDFF_PEER_RACE_LIMIT` | — | 组员并发竞速限制 |
1291
- | `MSLXDFF_PEER_CONNECT_TIMEOUT_MS` | `3000` | 组员转发连接级超时(DNS+TCP/TLS 握手);黑洞节点快速失败,不再占用 30s 响应超时 |
1292
- | `MSLXDFF_BAN_WINDOW_MS` | `172800000` (48h) | 加群失败封禁窗口 |
1293
- | `MSLXDFF_BAN_THRESHOLD` | `5` | 封禁阈值(窗口内失败次数) |
1294
- | `MSLXDFF_USE_GROUP` | `1` (on) | 组员中继总开关(`0/off` 关闭后所有供应商仅本机;on 时也仅 opencode 走组员,key 供应商默认直连) |
1295
- | `MSLXDFF_USE_GROUP_KEYS` | `0` (off) | key 供应商组员开关(ADR-0023:`1` 则 key 供应商失败也可走组员;cline/workbuddy 仍硬禁;受 `MSLXDFF_USE_GROUP`/off 约束) |
1296
- | `MSLXDFF_HEDGE_DELAY_MS` | `1000` | 首块对冲等待(`0/off` 关闭,显式锁模型按 `via-routes.json` 单路径择路,不经 hedge) |
1297
- | `MSLXDFF_VIA_ROUTES_FILE` | `~/.config/mslxdff/via-routes.json` | via-routes 落盘路径(随 `MSLXDFF_STATE_FILE` 派生) |
1298
- | `MSLXDFF_VIA_ROUTE_TTL_MS` | `0` | via-routes 条目 TTL(`0`=不过期,手动 `bench --via --apply` 重跑即更新) |
1299
- | `MSLXDFF_BENCH_DELAY_MS` | `120` | bench --via 串行间隔 ms |
1300
- | `MSLXDFF_SLOW_TOTAL_MS` | `20000` | 慢模型判定:总耗时阈值 |
1301
- | `MSLXDFF_STREAM_TIMEOUT_MS` | `25000` | 流式首块闸门:非末位候选到点即真掐上游换候选(`0`=关闭;keepalive 注释帧不算首块、不解除闸门) |
1302
- | `MSLXDFF_LAST_CANDIDATE_TIMEOUT_MS` | `120000` | 末位/唯一候选与借道(via-route)的耐心档:首块未到才放弃(`0`=不限,慢上游可调大) |
1303
- | `MSLXDFF_SDK_HEADERS_TIMEOUT_MS` | `120000` (2m) | AI SDK 通道 headers 超时:`doStream` 到点仍未返回响应头即判挂死并抛错(由调用方回退/换路;`0`=关闭);防上游连接半死导致请求永不返回,**错误文案不含 "timed out"**(否则 cline runChat 会按文案重试 3 次放大挂死) |
1304
- | `MSLXDFF_STALL_TIMEOUT_MS` | `0`(关闭) | 相邻 chunk 间隔 stall 阈值(仅作质量分) |
1305
- | `MSLXDFF_EMPTY_TURN_RETRIES` | `2` | 空转 200(模型无输出)同模型自动重试次数(`0`=关闭回旧行为;仅 `EMPTY_MODEL_RESPONSE`,429/403/500 与 fetch 异常不重试) |
1306
- | `MSLXDFF_EMPTY_TURN_RETRY_DELAY_MS` | `1000` | 空转重试前暂停 ms(给上游 1s 喘息再重拉同模型) |
1307
- | `MSLXDFF_MODELS_DEV_URL` | `https://models.opencode.ai/api.json` | 模型能力目录源(ADR-0016,opencode 官方同源;备选 `https://models.dev/api.json`) |
1308
- | `MSLXDFF_MODELS_DEV_TTL_MS` | `86400000` (24h) | 能力目录缓存 TTL(过期重拉;`0`=每请求拉) |
1309
- | `MSLXDFF_MODELS_DEV_CACHE` | `~/.config/mslxdff/models-dev.json` | 能力目录磁盘缓存路径(fetch 失败回退旧缓存) |
1310
- | `MSLXDFF_MAX_STREAM_MS` | `120000` (2m) | 流式总时长上限 |
1311
- | `MSLXDFF_FREE_ANON` | — | 空或非 `0/off/false` 则启用 free 模型 public 429 后的匿名重试 |
1312
- | `MSLXDFF_FREE_ANON_RETRIES` | `3` | 匿名重试次数 |
1313
- | `MSLXDFF_FREE_ANON_DELAY_MS` | `1000` | 匿名重试间隔 |
1314
- | `MSLXDFF_FREE_ANON_LOG` | `<cwd>/free-anon-extra.txt` | 匿名命中日志路径 |
1315
- | `MSLXDFF_OPENCODE_UA` | `opencode/1.18.31` | opencode 上游身份 `User-Agent`(zen 免费层门禁要求 `opencode/<semver>`,缺版本即 403;2026-09-18 起还要求 ≥ `1.18.0`,低版本 `426 UpgradeRequired`;配了就以配置为准,用于跟随官方版本升级) |
1316
- | `MSLXDFF_FREE_LANE` | `1` | 免费层 agent 形状注入(ADR-0020):zen 免费模型自动补 `stream:true` + 核心五工具(bash/edit/glob/grep/read),非流式调用聚合回 JSON;`0`/关闭词=完全不动(上游撤门禁时的逃生阀) |
1317
- | `MSLXDFF_FREE_LANE_DEBUG` | — | `1` 时 `daemon.log` 打 `[free-lane]` 发送/响应摘要(模型/URL/stream/tools 数/UA/状态),排障 403/426 用 |
1318
- | `MSLXDFF_PREHEAT` | `1` | 上游预热开关(`0` 关闭;仅预热 opencode 的连接池与模型缓存,其他供应商按需首次请求自拉);cline free 自检独立于此开关(daemon 启动时对比快照报增删,写 `daemon.log` + 更新 `logDir/cline-free.json`) |
1319
- | `MSLXDFF_UPSTREAM_KEEPALIVE_TIMEOUT` | `30000` | opencode 上游 keepAlive 超时 |
1320
- | `MSLXDFF_UPSTREAM_KEEPALIVE_MAX_TIMEOUT` | `60000` | keepAlive 最大超时 |
1321
- | `MSLXDFF_UPSTREAM_KEEPALIVE_CONNECTIONS` | `20` | keepAlive 连接数 |
1322
- | `MSLXDFF_PLUGINS_DIR` | `<pkg>/plugins` + `~/.config/mslxdff/plugins` | 插件目录覆盖(设了则只扫该目录) |
1323
- | `MSLXDFF_DAEMON` | — | 内部标记:子进程为 daemon 时置 `1` |
1324
- | `MSLXDFF_DEBUG` | — | `1` 时前台打印详细事件与 free-anon 日志 |
1325
- | `MSLXDFF_LOGS_SYNC` | — | `1` 时日志同步写 |
1326
- | `MSLXDFF_AUTO_UPDATE` / `MSLXDFF_AUTO_UPDATE_MS` | 默认每小时 | 自动更新:`0/off/false` 关闭,`1/true/on` 每小时,数值则为毫秒间隔 |
1327
- | `MSLXDFF_CODEARTS_TIMEOUT_MS` / `_COOLDOWN_MS` | `30000 / 30000` | codearts 上游请求超时 / key 冷却(多账号轮转间隔) |
1328
- | `MSLXDFF_CODEARTS_REFRESH_SKEW_MS` | `1800000` | STS 临时凭证提前刷新窗口(默认提前 30min) |
1329
- | `MSLXDFF_CODEARTS_AUTO_CLAIM` | `1` | 福利模型自动领取(`benefit claim`,幂等;`0` 关) |
1330
- | `MSLXDFF_CODEARTS_BASE_URL` | — | codearts 上游地址覆盖(state `providerConfigs.codearts.baseUrl` 优先) |
1331
-
1332
- > 注:`-port`/`-provider` 等 CLI 写入的 state 优先级高于同名 env(如 `MSLXDFF_PORT`),但 `MSLXDFF_<ID>_KEY` 单值 env 优先于 state 的多 key(便于容器/CI 临时覆盖)。
1333
-
1334
- ---
1335
-
1336
- ## 附录 B 状态文件
1337
-
1338
- - **路径**:`MSLXDFF_STATE_FILE` 或 `~/.config/mslxdff/state.json`(`0600`)
1339
- - **daemon 派生**:`pidFile()`、`logFile()`、`calls.log`、`errors.log`、`events.log`、`models.json` 均在 `dirname(stateFile)` 下。
1340
- - **主要字段**:
1341
- ```jsonc
1342
- {
1343
- "token": "64 hex",
1344
- "createdAt": "2026-08-27 08:00:00",
1345
- "timezone": "Asia/Shanghai",
1346
- "port": 8989,
1347
- "useGroup": true,
1348
- "preferredModel": "big-pickle",
1349
- "modelPicks": ["big-pickle", "openrouter/..."],
1350
- "providerKeys": { "openrouter": ["sk-...","sk-..."] },
1351
- "providerConfigs": { "myapi": { "baseUrl": "https://api.example.com/v1", "keys": ["sk-..."], "allowedModels": ["gpt-4", "gpt-3.5"] } },
1352
- "peers": [],
1353
- "groups": { "my@mslxd": { "members": { "leader": {...}, "http://...": {...} } } },
1354
- "groupsJoined": [{ "name": "my@mslxd", "leaderUrl": "http://...", "myUrl": "...", "memberName": "...", "kind": "static|broadband" }],
1355
- "bans": { "1.2.3.4": 1234567890 },
1356
- "modelErrors": { "some-model-free": { "status": "error", "at": 123, "code": 429 } },
1357
- "modelLatencies": { "big-pickle": 123 }
1358
- }
1359
- ```
1360
- `allowedModels` 空数组或缺失 = 不限;非空仅名单内可用(`403` 拦截,`/v1/models` 过滤)。
1361
- - **.gitignore**:`**/*state*.json`、`**/*key*.json` 等已加固,key 类 state 绝不进 repo。
1362
-
1363
- ---
1364
-
1365
- ## 附录 C 退出码与提示
1366
-
1367
- - `0`:成功(`--help`/`--status`/`--log`/`--plugins`/`-model list` 等正常结束也为 0)。
1368
- - `1`:参数错误或业务失败(`invalid port`、`usage: ...`、`could not refresh models`、`join failed`、`remove failed`、`group remove requires being the leader` 等)。
1369
- - 常见 `console.error` 提示:
1370
- - `usage: mslxdff -provider <id> [key...|add|remove|list|clear|set-url]` — `-provider` 缺 id。
1371
- - `usage: mslxdff -provider add <id> <baseUrl> <key>` — 通用供应商缺参。
1372
- - `opencode is the default (bare) provider — it needs no API key and can never be shared` — 对 `opencode` 执行 provider 操作。
1373
- - `group remove requires being the leader — this node leads no group` — 非 leader 尝试踢人。
1374
- - `member #N not found — group "name" has M member(s)` — 序号越界。
1375
- - `"name" is led by http://... — you are a member, use -leavegroup to leave it` — 成员误用 `-delgroup`。
1376
- - `not joined to any group` / `not a member of group` — 未加入或不在该组。
1377
-
1378
- ---
1379
-
1380
- ## 附录 D 交互式终端说明
1381
-
1382
- - **触发条件**:`process.stdin.isTTY && process.stdout.isTTY` 同时为真。
1383
- - **涉及命令**:
1384
- - `mslxdff -models`:多选勾选(`renderChooser`,ANSI 原地重绘,`↑/↓/Space/Enter/q`)。
1385
- - `mslxdff -provider <id>`(无子命令):隐藏输入多行追加(`readline/promises`,空行结束)。
1386
- - **非 TTY 行为**:一律走非交互分支(列表或报错提示用法),不会挂起等待输入,适合脚本/CI。
1387
- - **按键**:`parseKey` 识别 `up/down/enter/space/cancel`(`cancel` 为 `q/Esc/Ctrl+C`)。
1388
-
1389
- ---
1390
-
1391
- > 维护者注意:改 `bin/mslxdff.js` 的参数解析后,务必同步更新本文件与 `docs/ARCHITECTURE.md §6` 的 CLI 表,并跑 `npm run docs:check`。