账号、OAuth 与云端工具
MiniMax 账号的 OAuth 设备码登录、按命名空间落盘的凭据与跨进程协调、Token Plan 额度查询、工具子进程的短期令牌,以及云端工具的调用凭据。
用 Token Plan 跑 MiniMax 自家的模型,先要登录 MiniMax 账号。这条链路横跨好几个包:packages/oauth-core 管登录状态、凭据与刷新;packages/tui/src/auth 与 packages/tui/src/account 把它接到命令行和 TUI;packages/oauth-lease-protocol 与 packages/mcode-tools-host 把短期令牌借给工具子进程;packages/agent-tools/src/cloud 是一组要带账号凭据才能调用的云端工具。官方文档的概括(docs/architecture.md:20):
MiniMax OAuth Core manages profile credentials, refresh, and logout. mcode-tools-host provides short-lived access tokens to tool subprocesses through a local lease broker.
怎么用
| 入口 | 作用 | 出处 |
|---|---|---|
mcode login | 登录中国区账号,打印授权地址与用户码,并尝试打开浏览器 | packages/tui/src/cli/program.ts:128 |
mcode login --region global | 登录国际区,内部记作 en | program.ts:378 |
mcode login --no-browser | 只打印地址,不开浏览器 | program.ts:131 |
mcode logout(可带 --region) | 撤销 refresh token、删本地凭据,再给出网页端登出地址 | program.ts:140 |
mcode acp login | 同一套登录,给 ACP 客户端用 | program.ts:116 |
/login、/logout | TUI 里的登录(先选区域)与登出;按是否已登录只显示其一 | packages/tui/src/tui/commands/catalog.ts:306、:313 |
/status、/usage | 账号、模型、工作区与 Token Plan 额度 | 见后文 |
/checkin | 领取每日账号奖励 | catalog.ts:354 |
mcode login 与 mcode logout 不带 --region 时固定作用于中国区,不参考区域偏好(packages/tui/src/auth/factory.ts:31、packages/tui/src/auth/environment.ts:54),国际区账号登出要写 mcode logout --region global。登录时终端打印 Open:、Code: 两行和一句等待提示(packages/tui/src/cli/auth-command.ts:36),授权地址上会追加 client_surface=tui 等来源参数(packages/tui/src/auth/authorization-url.ts:7)。在 TUI 里选了与当前运行区域不同的区域,登录成功后 TUI 会以新区域重启(packages/tui/src/auth/application.ts:107)。
相关环境变量:MAVIS_REGION(cn 或 en)优先于区域偏好;MCODE_OAUTH_DEVICE_AUTHORIZATION_ENDPOINT、MCODE_OAUTH_TOKEN_ENDPOINT、MCODE_OAUTH_REVOCATION_ENDPOINT 可整体替换 OAuth 端点,但必须三个一起给、都是 https(packages/oauth-core/src/endpoint-config.ts:65、:86)。账号相关文件都在数据目录 ~/.minimax-code 下:
| 路径 | 内容 |
|---|---|
auth/prod/cn/mcode-public/auth.json | 凭据:access token、refresh token、过期时间、代次,权限 0600 |
auth/prod/cn/mcode-public/auth-state.json | 状态机,不许出现任何令牌字段 |
auth/prod/cn/mcode-public/auth.lock | 跨进程锁 |
preferences/mcode-region.json | 各构建环境上次登录的区域 |
cli-auth/prod/cn/account-identity.json | 已核实的账号身份缓存,按令牌指纹对应 |
路径里的 prod/cn 随构建环境与区域变化(packages/oauth-core/src/namespace.ts:22、packages/tui/src/auth/region-preference.ts:6、packages/tui/src/auth/identity-storage.ts:37)。
设备码登录
OAuth 端点按区域取生产地址:中国区 https://account.minimax.cn,国际区 https://account.minimax.io,路径分别是 /oauth2/device/code、/oauth2/token、/oauth2/revoke(endpoint-config.ts:24、:51)。客户端身份写死为 client_id 取 mcode-public、scope 取 agent.default、audience 取 agent-backend(packages/oauth-core/src/contracts.ts:1)。
流程是 OAuth 2.0 设备授权,外加 PKCE(packages/oauth-core/src/oauth-client.ts:79):
async startDeviceAuthorization(options: OAuthRequestOptions = {}): Promise<DeviceAuthorization> {
const codeVerifier = randomBytes(32).toString('base64url');
const codeChallenge = createHash('sha256').update(codeVerifier, 'ascii').digest('base64url');
const body = await this.postForm(
this.options.deviceAuthorizationEndpoint,
{
client_id: MCODE_OAUTH_CLIENT_ID,
scope: MCODE_OAUTH_SCOPES.join(' '),
audience: MCODE_OAUTH_AUDIENCE,
code_challenge: codeChallenge,
code_challenge_method: 'S256',
},
{
headers: this.options.deviceAuthorizationHeaders,
signal: options.signal,
},
);紧接着的几行兼容 MiniMax 账号服务的另一种回包:没有 device_code,只给 user_code 和过期字段 expired_in(可能是秒数,也可能是毫秒时间戳),这时改用 user_code 轮询,间隔按毫秒换算(oauth-client.ts:96)。轮询同时认 JSON 里的 status 字段和标准的 OAuth 错误码,遇到 slow_down 就把间隔加 5 秒(oauth-client.ts:159)。拿到的令牌必须是 Bearer 且含 agent.default,sub 与 account_id 从 JWT 负载里解码出来,只解码、不验签(oauth-client.ts:290)。
凭据、锁与状态机
MCodeOAuthCore(packages/oauth-core/src/auth-core.ts)是唯一的凭据主人。几条关键设计:
- 命名空间:凭据按“构建环境/区域/客户端”分目录,中国区与国际区各有一份(
namespace.ts:22)。 - 文件存储:凭据库只有一种实现,明文 JSON 的
auth.json,目录 0700、文件 0600;在 POSIX 上只要组或其他用户有任何权限位就拒绝读取,Windows 跳过这项检查(packages/oauth-core/src/credential-store/file-store.ts:105、:108)。写入走“独占创建临时文件、fsync、rename”(packages/oauth-core/src/fs/atomic-write.ts:12)。系统钥匙串os-keyring只作为旧状态里的取值被识别(packages/oauth-core/src/credential-store/types.ts:4)。 - 状态文件不藏秘密:写
auth-state.json前递归检查键名,出现 access token、refresh token、device code、code verifier 之类就抛错(packages/oauth-core/src/state-store.ts:41)。状态共九种,从anonymous、authorizing、authenticated到logout_pending、error(state-store.ts:10)。 - 跨进程锁:
proper-lockfile,30 秒判为陈旧,最多重试 120 次(packages/oauth-core/src/cross-process-lock.ts:21)。 - 登录租约:开始登录的进程在锁内写入
authorizing和一个 10 分钟的租约(auth-core.ts:27);别的进程此时再登录,只会等待并显示同一个用户码(auth-core.ts:536)。提交时代次加一,并生成新的loginEpoch:刷新不变,每次交互登录都换(auth-core.ts:493)。
取令牌先走无锁快路径,不够新再进锁刷新(auth-core.ts:249):
async getAccessToken(options: AccessTokenOptions): Promise<AccessTokenLease> {
await this.initialize();
validateRequestedScopes(options.requiredScopes);
try {
const initial = await this.readUsableCredential(options.requiredScopes);
if (this.satisfiesMinValidity(initial, options.minValidityMs)) {
return toAccessTokenLease(initial.credential);
}
} catch (error) {
if (!(error instanceof AuthRequiredError)) throw error;
// A concurrent writer may have committed credentials before their state.
// Recheck under the domain lock before reporting authentication as missing.
}
return this.lock.withLock(async () => {
const current = await this.readUsableCredential(options.requiredScopes);
if (this.satisfiesMinValidity(current, options.minValidityMs)) {
return toAccessTokenLease(current.credential);
}
return this.refreshCredential(current);
});
}刷新时服务端回 HTTP 400 的 invalid_grant,就先把状态写成 anonymous 再删凭据,要求重新登录;其他错误只要旧令牌还没过期,会话照常保留(auth-core.ts:682、:727)。调用方遇到 401 时交回 handleUnauthorized(auth-core.ts:285):loginEpoch 变了说明中途换过登录,只拒绝这一个过期请求;否则比代次,更新过就直接重试,落后说明已被登出,相同则强制刷新一次再重试。每个进程都用 Node 的 watchFile 每 250 毫秒检查一次状态文件,感知别处的登录与登出(auth-core.ts:364)。
登出先撤销 refresh token;撤销失败时状态记为 logout_pending 并提前返回,凭据文件暂不删除,但这个状态已经取不出令牌(auth-core.ts:331)。TUI 随后给出 /auth/logout 网页地址,用来清掉浏览器里的登录态(packages/tui/src/auth/logout-url.ts:23)。
旧版把状态放在 auth/ 根目录。迁移只做一件事:把与当前环境、区域匹配的根目录状态标成 error,若新命名空间还没有当前版本的状态,就写一份代次加一的 anonymous 状态(packages/oauth-core/src/namespace-migration.ts:9)。令牌本身不搬,从代码看,老用户升级后要重新登录一次。
登录态进入运行时的方式是“投影”:TUI 启动时向 OAuth Core 要一个至少还剩 60 秒的令牌(packages/tui/src/runtime/lifecycle.ts:218),再查一次账号信息补上 realUserID、邮箱,放进运行时的认证上下文;此后跟着状态文件的变化更新。
区域与环境:公开版只连生产
启动时区域按这个顺序定:显式参数、MAVIS_REGION、区域偏好文件、唯一一个有可用登录的命名空间,最后兜底 cn(packages/tui/src/cli/environment.ts:51)。构建环境则不给用户选:仓库的构建脚本把 __TUI_BUILD_ENV__ 定义成 "prod"(scripts/build.mjs:107),启动时据此覆盖进程里的 MAVIS_BUILD_ENV(cli/environment.ts:63)。切到测试或预发的 --env 只在内部包 @minimax/code 里可用,其他包名一律报错(cli/environment.ts:108,包名见 packages/tui/src/update/install-source.ts:13),而构建产物的 package.json 写的正是公开包名 @minimax-ai/code(scripts/build.mjs:146);指定后端泳道的 --lane 只允许 test、staging 构建(cli/environment.ts:120)。各处地址表里的非生产项都是 .example.invalid 占位(例如 endpoint-config.ts:24),check:source 还会拦截内部域名(scripts/source-inventory.mjs:46)。这与 docs/architecture.md:24 的说法一致:
The public distribution configures production regional services only. Non-production environments retained in shared types use reserved
.example.invalidplaceholders. The public CLI rejects internal environment switches and provides no default connection to internal services.
不过“拒绝切换”管的是构建环境;OAuth 端点与下文的 Matrix 网关地址仍能用环境变量整体替换。另一个小出入:账号信息接口的中国区生产域名是 https://agent.minimaxi.com(packages/tui/src/account/matrix-account-client.ts:9),登出页与云端工具网关用的却是 https://agent.minimax.cn,前者在网关代码里被列为旧托管地址(packages/local-runtime/src/matrix/matrix-env.ts:23)。
Token Plan 额度
/status 先渲染本地能拿到的模型、工作区、权限、规则文件,再异步补账号部分(packages/tui/src/tui/controller/product/feature-flow.ts:1109)。额度由 TuiMatrixAccountClient 查询,最多四步,每步都带 Authorization: Bearer 令牌(matrix-account-client.ts:168):
| 步骤 | 接口 | 取什么 |
|---|---|---|
| 1 | GET /v1/api/user/info | realUserID、邮箱、用户名 |
| 2 | POST /matrix/api/v1/user/get_user_extra_info | 个人工作区(workspace_type 为 0)及其 op_group_id |
| 3 | POST /matrix/api/v1/commerce/get_membership_info | 是否订阅 Token Plan、档位、到期时间、余额 |
| 4 | GET /v1/api/openplatform/coding_plan/remains | 额度,请求头带 X-Group-Id |
前三步发往 Agent 站点(中国区 https://agent.minimaxi.com,国际区 https://agent.minimax.io),第四步发往开放平台(中国区 https://www.minimaxi.com,国际区 https://platform.minimax.io),见 matrix-account-client.ts:22、:42。单次请求超时 3 秒,会员信息缓存 30 秒,额度缓存 5 秒(matrix-account-client.ts:27)。发往 Agent 站点的请求还带 yy、x-timestamp、x-signature 三个头,由两段写死的字面量算出;注释说明它们只标记“第一方客户端”,不是凭据,鉴权靠的仍是 Bearer(matrix-account-client.ts:32)。
额度取 model_remains 的第一项:current_interval_* 是 5 小时窗口,current_weekly_* 是周窗口,状态值 3 表示不限量,剩余百分比优先取服务端给的值,否则按用量与总量换算(matrix-account-client.ts:672)。/status 的 Quota 一节显示 5-hour 与 Weekly 两行,视频额度只在 /usage 里出现(packages/tui/src/tui/features/inspection/product-inspection.ts:196、:585);没订阅就显示一行警告。任一窗口剩余不到 30% 时,状态栏右侧会出现提醒,不到 10% 变红(packages/tui/src/tui/shell/chrome.ts:526、:559)。
给工具子进程发短期令牌
多模态能力走的是 bash 里的 mcode-tools 命令(怎样放上 PATH 见工具体系)。子进程要以用户身份调后端,但 refresh token 不能离开宿主,README 写得很直接(packages/mcode-tools-host/README.md:7):
The host remains the only credential-store, Refresh Token, refresh and logout owner.
办法是在本机起一个租约代理:
- 端点:TUI 在
/tmp下建一个mcode-tools-tui-<pid>-开头的 0700 私有目录(Windows 用系统临时目录),因为 macOS 的 Unix socket 路径很短,数据目录一长就放不下(packages/tui/src/runtime/mcode-tools-integration.ts:149)。socket 是其中的run/mcode-auth-lease-v1.sock,权限 0600;Windows 上是\\.\pipe\mcode-auth-lease-加 16 位哈希的命名管道(packages/oauth-lease-protocol/src/endpoints.ts:6)。 - 能力口令:代理启动时生成 32 字节随机数,base64url 编码后正好 43 个字符,原子写进同目录的
mcode-auth-lease-v1.cap,权限 0600(packages/mcode-tools-host/src/lease-broker.ts:46、:145)。每个请求都要带上它,服务端用timingSafeEqual比对(packages/oauth-lease-protocol/src/node-server.ts:126)。 - 线协议:4 字节大端长度加一段 JSON,单帧不超过 64 KiB;只有
status、lease、unauthorized三个方法,lease要求的最短剩余有效期不超过 5 分钟(packages/oauth-lease-protocol/src/contracts.ts:6、:14)。解析一律严格:多一个键就拒绝,错误消息只能是固定文本。服务端单连接空闲 30 秒断开,最多 32 个连接、每连接 256 个请求(node-server.ts:16)。
代理的处理函数只做三件事(lease-broker.ts:126):
async function handleRequest(request: AuthLeaseRequest): Promise<AuthLeaseSuccessResult> {
if (disposed) throw new AuthLeaseProtocolError('BROKER_UNAVAILABLE');
if (request.method === 'status') return await getCredentialFreeStatus(options.session);
if (request.method === 'lease') {
const lease = await acquireLease(request.minValidityMs);
return {
method: 'lease',
accessToken: lease.accessToken,
expiresAtMs: lease.expiresAtMs,
generation: lease.generation,
audience: AUTH_LEASE_AUDIENCE,
scopes: AUTH_LEASE_SCOPES,
};
}
invalidate();
const action = await options.session.handleUnauthorized(request.generation);
return { method: 'unauthorized', action };
}lease 背后就是 OAuth Core 的 getAccessToken(lifecycle.ts:654),代理自己缓存一份并合并并发请求。它也订阅登录状态:一旦不再是 authenticated 或 refreshing 就关闭发放、清空缓存,并记住见过的最高代次,不再发更旧的令牌(lease-broker.ts:152)。子进程这一侧,入口会先删掉继承来的 MCODE_API_BASE_URL、MCODE_AUTH_BASE_URL、MCODE_CLIENT_ID、MCODE_SCOPE 等变量,再写入 MCODE_AUTH_PROVIDER=shared-broker 与代理坐标(packages/tui/src/cli/mcode-tools-environment.ts:92、:124),继承来的环境变量改不了它连哪个后端、用哪种认证。宿主校验内嵌产物时,也要求清单声明 shared-broker 模式、协议版本一致,生产包名必须是 @minimax/mcode-tools(packages/mcode-tools-host/src/resource.ts:338、:365)。
mcode-tools-host 里另有一套往数据目录 bin/ 下安装分区域启动器的流程(packages/mcode-tools-host/src/integration.ts:45),公开仓库里没有调用方,TUI 用的是自己的 internal-bin 方案。
云端工具
packages/agent-tools/src/cloud/matrix-tools 定义了 18 个工具:web_search 加上 MATRIX_TOOL_NAMES 里的 17 个(packages/agent-tools/src/cloud/matrix-tools/index.ts:137)。
| 类别 | 工具 |
|---|---|
| 搜索 | web_search、images_search_and_download、image_reverse_search |
| 理解 | images_understand、videos_understand、audios_understand、transcribe_audio |
| 图像生成 | image_synthesize |
| 视频生成 | submit_video_generation、query_video_generation、gen_videos、batch_text_to_video、batch_image_to_video |
| 语音与音乐 | get_voice_list、synthesize_speech、batch_synthesize_speech、batch_text_to_audio、batch_text_to_music |
每个工具对应网关上的 /matrix/api/v1/mcp/<工具名>,只有 transcribe_audio 映射到 listen_audio(packages/agent-tools/src/cloud/matrix-tools/tool-defs.ts:571、:589);生成类超时 10 分钟,其余 2 分钟(tool-defs.ts:604)。输入输出都用工作区里的本地路径:工具先把输入文件传到对象存储、换成一小时有效的签名 URL,再把结果下载回指定路径(tool-defs.ts:7)。失败时只返回固定文案,不透传服务端消息(packages/agent-tools/src/cloud/matrix-tools/client.ts:63)。当前模型能原生看图、看视频时,images_understand、videos_understand 会被撤掉(index.ts:312)。
凭据怎么带:请求头统一是 Authorization: Bearer <令牌>。发往托管网关 https://agent.minimax.cn 或 https://agent.minimax.io 时,令牌取当前登录的 access token;用 MATRIX_BASE_URL 指向非托管地址时,必须另设 MATRIX_TOKEN(packages/agent-tools/src/desktop/matrix-client.ts:194,地址表在 matrix-env.ts:13)。路径还会从 /matrix/api/v1/mcp/ 改写成 /mavis/api/v1/mcp/(matrix-client.ts:226)。
在命令行版里真正在用的只有 web_search:它对 electron、tui、cli 三种宿主是原生工具(packages/local-runtime/src/api/host-turn-tools.ts:82),直接用上面的客户端调网关(packages/local-runtime/src/web-search/local-web-search-client.ts:26)。其余 17 个原本经内置的 Matrix MCP 子进程暴露,令牌通过环境变量 MAVIS_MATRIX_ACCESS_TOKEN 传给子进程(packages/local-runtime-v2/src/service/mcp/runtime/builtin-matrix.ts:124),但公开运行时固定传 builtinMatrix: false(packages/local-runtime-v2/src/compat/v1/runtime.ts:580),这条路没有打开,图像、视频、语音改由 mcode-tools 的 Connector 提供。mcode-tools 的源码不在仓库里,它背后接了哪些服务无从核实;从内置技能看,至少包括云盘资产的下载地址与本地文件的临时上传(packages/local-runtime/assets/skills/mcode-tools-master/SKILL.md:36)。
下一篇:TUI 引擎——产品界面底下那份 pi-tui 分叉怎样重绘、布局与解析输入。