{{ locale === 'zh' ? '请登录以继续' : 'Please sign in to continue' }}
{{ locale === 'zh' ? '你的账号正在等待管理员审批,审批通过后即可使用。' : 'Your account is pending admin approval.' }}
{{ locale === 'zh' ? '你的账号已被管理员拒绝,请联系管理员。' : 'Your account has been rejected. Please contact the admin.' }}
{{ locale === 'zh' ? '你还没有注册任何设备。点击下方按钮添加一台 Mac Studio,开始使用 AI 推理服务。' : 'No devices registered yet. Add a Mac Studio to start using AI inference services.' }}
| {{ locale === 'zh' ? '时间' : 'Time' }} | tenant | {{ locale === 'zh' ? '阶段' : 'Stage' }} | {{ locale === 'zh' ? '退出码' : 'Exit' }} | {{ locale === 'zh' ? '错误' : 'Error' }} | |
|---|---|---|---|---|---|
| {{ formatDate((r._ts || 0) * 1000) }} | {{ r.tenant_id }} |
{{ r.stage }} | {{ r.exit_code }} | ||
| {{ installReportsLoading ? '...' : (locale === 'zh' ? '暂无诊断报告' : 'No reports yet') }} | |||||
{{ diagnoseResult.stdout }}
{{ diagnoseResult.stderr }}
| {{ locale === 'zh' ? '时间' : 'Time' }} | tenant | command | status | rc | elapsed | args |
|---|---|---|---|---|---|---|
| {{ r.ts }} | {{ r.tenant_id }} |
{{ r.command }} |
{{ r.status }} | {{ r.rc ?? '-' }} | {{ r.elapsed_ms }}ms | {{ JSON.stringify(r.args) }} |
| {{ t('col.time') }} | {{ t('col.model') }} | Tokens | {{ t('col.latency') }} | {{ t('col.status') }} |
|---|---|---|---|---|
| {{ t('noRequests') }} | ||||
| {{ formatTime(r.time_str || r.timestamp) }} | {{ r.model }} | {{ r.tokens_in + r.tokens_out }} | {{ fmtMs(r.latency_ms) }} | {{ r.status_code }} |
| {{ t('col.modelName') }} | {{ t('col.source') }} | {{ t('col.backendModel') }} | {{ t('col.apiBase') }} | {{ t('col.actions') }} |
|---|---|---|---|---|
| {{ t('noRoutes') }} | ||||
| {{ r.model_name }} {{ t('autoSync') || 'Auto' }} | {{ getRouteSource(r).label }} | {{ r.litellm_params?.model || '-' }} | {{ r.litellm_params?.api_base || '-' }} | |
| {{ t('col.route') }} | {{ t('col.behavior') }} | {{ t('col.backendModel') }} | {{ t('col.apiBase') }} | {{ t('col.actions') }} |
|---|---|---|---|---|
| {{ t('audioNoRoutes') }} | ||||
|
{{ route.route_name }}
{{ route.display_name }}
{{ locale === 'zh' ? '默认路由' : 'Default route' }}
{{ locale === 'zh' ? '当前激活' : 'Active model' }}
|
{{ route.endpoint }}
model=
{{ route.model_param }}
{{ locale === 'zh' ? '可省略 model,命中当前 active ASR' : 'model is optional and uses the current active ASR' }}
|
{{ route.backend_model }} |
{{ route.api_base }}
{{ route.files_endpoint || '/v1/files' }} / {{ route.endpoint }}
|
|
| {{ t('col.name') }} | {{ t('col.size') }} | {{ t('col.modified') }} | {{ t('col.actions') }} |
|---|---|---|---|
| {{ t('noModels') }} | |||
| {{ m.name }} | {{ formatSize(m.size) }} | {{ formatDate(m.modified_at) }} | |
| {{ t('col.name') }} | {{ t('quantType') }} | {{ t('col.size') }} | {{ t('col.status') }} | {{ t('col.actions') }} |
|---|---|---|---|---|
| {{ t('noGGUF') }} | ||||
| {{ m.name }}({{ m.shard_count }} shards) | {{ m.quant_type }} | {{ formatSize(m.total_size) }} | Loaded {{ t('llamaStatusBadgeStale') }} {{ llamaLoadingProgress >= 0 ? 'Loading ' + Math.round(llamaLoadingProgress * 100) + '%' : 'Loading...' }} Incomplete ({{ m.shard_count }}/{{ m.expected_shards }}) Error Available | |
| {{ t('col.name') }} | {{ t('audioCol.type') }} | {{ t('col.size') }} | {{ t('audioCol.languages') }} | {{ t('audioCol.status') }} | {{ t('col.actions') }} | {{ t('audioCol.note') }} |
|---|---|---|---|---|---|---|
| {{ t('noModels') }} | ||||||
|
{{ m.display_name }}
{{ m.model_id }}
|
ASR TTS | {{ formatSize(m.total_size_bytes) }} | {{ (m.supported_languages || []).join(', ') || '—' }} | {{ t('audioStatus.tts_placeholder') }} {{ t('audioStatus.not_downloaded') }} {{ t('audioStatus.downloading') }} {{ (m.download_progress || 0).toFixed(0) }}% {{ t('audioStatus.downloaded') }} {{ t('audioStatus.loaded') }} {{ t('audioStatus.active') }} {{ t('audioStatus.failed') }} |
⚠️ {{ m.unavailable_reason }}
|
|
| {{ t('col.name') }} | {{ t('col.size') }} | {{ locale === 'zh' ? '状态' : 'Status' }} | {{ locale === 'zh' ? '进度' : 'Progress' }} | {{ t('col.actions') }} | {{ locale === 'zh' ? '错误' : 'Error' }} |
|---|---|---|---|---|---|
| {{ t('noModels') }} | |||||
|
{{ m.display_name || m.model_id }}
{{ t('mano.modelState.active') }}
{{ m.model_id }}
|
{{ m.size_bytes ? formatSize(m.size_bytes) : '—' }} | {{ t('mano.modelState.not_downloaded') }} {{ t('mano.modelState.downloading') }} {{ ((m.progress || 0) * 100).toFixed(0) }}% {{ t('mano.modelState.downloaded') }} {{ t('mano.modelState.loading') }} {{ t('mano.modelState.loaded') }} {{ t('mano.modelState.unloaded') }} {{ t('mano.modelState.failed') }} | ⚠️ {{ locale === 'zh' ? '请联系管理员配置 HF token' : 'Please ask admin to configure HF token' }} {{ m.error }} | ||
| {{ locale === 'zh' ? '地址' : 'URL' }} | {{ locale === 'zh' ? '状态' : 'Status' }} | {{ locale === 'zh' ? '延迟' : 'Latency' }} | {{ locale === 'zh' ? '模型数' : 'Models' }} | {{ locale === 'zh' ? '模型' : 'Serving' }} | {{ locale === 'zh' ? '检查时间' : 'Checked' }} |
|---|---|---|---|---|---|
| {{ p.url }} | {{ p.status }} | {{ p.latency_ms != null ? fmtMs(p.latency_ms) : '—' }} | {{ p.model_count }} | {{ (p.models || []).join(', ') || '—' }} | {{ p.checked_at }} |
{{ omlxText('此设备后台尚未支持新版模型管理。旧列表只能确认模型已上架,不能确认内存加载状态;新增操作已禁用。', 'This device needs the new backend. The legacy list only confirms publication, not memory residency; new actions are disabled.') }}
{{ omlxDetail(omlxAdmin.error) }}
{{ omlxText('尚未验证管理能力,请刷新查看连接状态。', 'Management capability has not been verified. Refresh to check the connection.') }}
| {{ t('col.name') }} | {{ t('col.size') }} | {{ omlxText('状态', 'Status') }} | {{ t('col.actions') }} |
|---|---|---|---|
| {{ omlxError ? omlxText('无法确认模型清单', 'Inventory unavailable') : t('noModels') }} | |||
{{ m.name }} {{ m.runtime_id }} |
{{ formatSize(m.size_bytes) }} | {{ modelStateText(m) }} |
{{ omlxDetail(m.blocked_reason) }}
|
{{ omlxText('上架、加载与下载是独立状态。仅未上架且未被引擎使用的本地副本允许删除;卸载只释放内存。', 'Published, loaded and downloaded are separate states. Only unpublished local copies unused by the engine can be deleted; unloading only frees memory.') }}
{{ omlxText('只保存修改的字段,清空数值可恢复引擎默认。若引擎要求重新加载,会显示提示,不会自动重启。', 'Only changed fields are saved; clear a numeric value to restore the engine default. A required reload is reported and never started automatically.') }}
{{ omlxText('由本设备的 oMLX 下载;完成后仍需验证架构和运行内存。', 'Downloaded by oMLX on this device; architecture support and runtime memory still need verification.') }}
{{ omlxText('使用引擎提供的缓存清理功能,保留模型权重。空间大小与模型统计分开。', 'Uses the engine cache controls and retains model weights. Cache space is separate from model size.') }}
{{ omlxLogs.length ? omlxLogs.join('\n') : (locale === 'zh' ? '(暂无日志)' : '(no logs)') }}
| {{ t('col.name') }} | {{ locale === 'zh' ? '引擎' : 'Engine' }} | {{ t('col.size') }} | {{ locale === 'zh' ? '状态' : 'Status' }} | {{ t('col.actions') }} |
|---|---|---|---|---|
| {{ t('noModels') }} | ||||
|
{{ row.name }}
{{ row.id }}
|
{{ storageEngineLabel(row.engine) }} | {{ formatSize(row.size_bytes) }} {{ omlxText('预估', 'Estimated') }} | {{ modelStateText(row) }} |
{{ omlxDetail(row.blocked_reason) }}
|
{{ omlxText('共享缓存可能包含上方已经统计的模型,以下大小不累加到模型总计。', 'Shared caches may include models counted above; these sizes are not added to the model total.') }}
| {{ t('col.name') }} | {{ t('col.size') }} | {{ omlxText('文件状态', 'File status') }} |
|---|---|---|
{{ entry.name }} {{ entry.relative_path }} | {{ formatSize(entry.size_bytes) }} | {{ entry.status_label }} · {{ omlxText('扫描不完整', 'Incomplete scan') }} |
{{ currentTenantId || '-' }}| {{ locale === 'zh' ? '检查项' : 'Check' }} | {{ locale === 'zh' ? '接口' : 'Endpoint' }} | {{ locale === 'zh' ? '期望' : 'Expected' }} | {{ locale === 'zh' ? '结果' : 'Result' }} | {{ locale === 'zh' ? '耗时' : 'Latency' }} | {{ locale === 'zh' ? '详情' : 'Detail' }} |
|---|---|---|---|---|---|
| {{ item.name }} |
{{ item.method }}
{{ endpointPath(item.url) }}
|
{{ item.expected }} | {{ item.status }} · {{ item.status_code || 0 }} | {{ fmtMs(item.elapsed_ms || 0) }} | {{ item.detail }} |
| {{ t('col.model') }} | {{ t('col.requests') }} | Tokens In | Tokens Out | {{ t('avgLatency') }} |
|---|---|---|---|---|
| {{ t('noData') }} | ||||
| {{ m.model }} | {{ m.requests }} | {{ formatNumber(m.tokens_in) }} | {{ formatNumber(m.tokens_out) }} | {{ fmtMs(m.avg_latency_ms) }} |
{{ locale === 'zh' ? '注册一台设备并在你的 Mac Studio 上运行安装命令,即可开始远程管理。' : 'Register a device and run the install command on your Mac Studio to get started.' }}
| {{ locale === 'zh' ? '名称' : 'Name' }} | {{ locale === 'zh' ? '状态' : 'Status' }} | {{ locale === 'zh' ? '版本' : 'Version' }} | {{ locale === 'zh' ? '租户 ID' : 'Tenant ID' }} | Token | {{ locale === 'zh' ? '最后连接' : 'Last Connected' }} | {{ locale === 'zh' ? '操作' : 'Actions' }} |
|---|---|---|---|---|---|---|
| {{ t.name }} | {{ t.effective_status === 'connected' ? (locale === 'zh' ? '在线' : 'Online') : t.effective_status === 'never_connected' ? (locale === 'zh' ? '从未连接' : 'Never Connected') : (locale === 'zh' ? '离线' : 'Offline') }} | {{ locale === 'zh' ? '需手动升级' : 'manual upgrade' }} {{ t.agent_version.slice(0,16) }} {{ locale === 'zh' ? '可更新' : 'update' }} {{ t.upgrade_status }} {{ locale === 'zh' ? '升级失败' : 'failed' }} — | {{ t.tenant_id }} |
{{ t.agent_token_prefix }}... |
{{ t.last_connected ? formatDate(t.last_connected) : '-' }} |
微信:{{ billingAdminWechat }}
| {{ locale === 'zh' ? '时间' : 'Time' }} | {{ locale === 'zh' ? '模型' : 'Model' }} | {{ locale === 'zh' ? '状态' : 'Status' }} | {{ locale === 'zh' ? '输入 / 输出 Tokens' : 'Input / Output Tokens' }} | {{ locale === 'zh' ? '金额' : 'Amount' }} | {{ locale === 'zh' ? '余额' : 'Balance' }} |
|---|---|---|---|---|---|
| {{ formatDateTime(tx.created_at) }} |
{{ tx.model_id || '-' }}
{{ tx.reason || (locale === 'zh' ? '充值' : 'Top-up') }}
|
{{ tx.status || '-' }} | {{ formatNumber(tx.input_tokens || 0) }} / {{ formatNumber(tx.output_tokens || 0) }} - | {{ formatUsdAmount(tx.amount, 8) }} | {{ formatUsdAmount(tx.balance_after, 2) }} |
| {{ locale === 'zh' ? '模型' : 'Model' }} | {{ locale === 'zh' ? '输入 USD / 100万 Tokens' : 'Input USD / 1M Tokens' }} | {{ locale === 'zh' ? '输出 USD / 100万 Tokens' : 'Output USD / 1M Tokens' }} | {{ locale === 'zh' ? '来源' : 'Source' }} |
|---|---|---|---|
{{ p.id }} |
{{ formatUsdAmount(p.input_price_per_million_tokens, 3) }} | {{ formatUsdAmount(p.output_price_per_million_tokens, 3) }} | {{ p.source || '-' }} |
| {{ t('col.engine') }} | URL | {{ t('col.port') }} | {{ t('col.status') }} |
|---|---|---|---|
| Ollama | http://127.0.0.1:11435 | 11435 | {{ statusText(engineStatus('ollama')) }} |
| llama-server | http://127.0.0.1:8000 | 8000 | {{ statusText(engineStatus('llama-server')) }} |
| LiteLLM | http://127.0.0.1:4000 | 4000 | {{ statusText(engineStatus('litellm')) }} |
| Audio Server | http://127.0.0.1:8200 | 8200 | {{ statusText(engineStatus('audio-server')) }} |
| Mano-P Server | http://127.0.0.1:8300 | 8300 | {{ statusText(engineStatus('mano-server')) }} |
| oMLX | http://127.0.0.1:8002 | 8002 | {{ statusText(engineStatus('omlx')) }} |
| Manager | http://127.0.0.1:8080 | 8080 | {{ statusText(managerLiveStatus) }} |
| {{ t('col.service') }} | URL |
|---|---|
| LiteLLM API | {{ publicBases.inference }} |
| Audio API | {{ publicBases.audio }} |
| {{ t('managerPanel') }} | {{ publicBases.manager }} |
| {{ locale === 'zh' ? '名称' : 'Name' }} | Key | {{ locale === 'zh' ? '创建时间' : 'Created' }} | {{ locale === 'zh' ? '最后使用' : 'Last Used' }} | {{ t('col.actions') }} |
|---|---|---|---|---|
| {{ k.name }} | {{ k.prefix }}... |
{{ formatDate(k.created) }} | {{ k.last_used ? formatDate(k.last_used) : '-' }} |
{{ locale === 'zh' ? 'Mac Studio AI Manager 是一个远程管理面板,通过 WSS (WebSocket Secure) 中继技术,让你在任何地方通过浏览器管理局域网内的 Mac Studio AI 推理服务。无需公网 IP,无需端口转发。' : 'Mac Studio AI Manager is a remote management panel that uses WSS (WebSocket Secure) relay technology to manage your LAN-based Mac Studio AI inference services from anywhere via a browser. No public IP or port forwarding required.' }}
| {{ locale === 'zh' ? '项目' : 'Item' }} | {{ locale === 'zh' ? '要求' : 'Requirement' }} |
|---|---|
| {{ locale === 'zh' ? '操作系统' : 'OS' }} | macOS 13+ |
| Python | 3.10+ |
| {{ locale === 'zh' ? '网络' : 'Network' }} | {{ locale === 'zh' ? '可访问外网(WSS 外连)' : 'Internet access (WSS outbound)' }} |
| {{ locale === 'zh' ? '推理引擎' : 'Inference Engine' }} | Ollama / llama-server / LiteLLM ({{ locale === 'zh' ? '至少一个' : 'at least one' }}) |
{{ locale === 'zh' ? '在「设备」页面点击「添加设备」,输入设备名称后系统会生成专属安装命令(包含 token 和 tenant-id)。' : 'Go to the "Devices" tab, click "Add Device", enter a name. The system generates an install command with your token and tenant-id.' }}
~/.mac-studio-agent/(含 Agent + Manager)~/.mac-studio-agent/ (Agent + Manager)| {{ locale === 'zh' ? '服务' : 'Service' }} | launchd Label | {{ locale === 'zh' ? '端口' : 'Port' }} | {{ locale === 'zh' ? '说明' : 'Description' }} |
|---|---|---|---|
| Manager | com.reai.mac-studio-manager | 8080 | {{ locale === 'zh' ? '管理 API 服务' : 'Management API service' }} |
| Agent | com.reai.mac-studio-agent | - | {{ locale === 'zh' ? 'WSS 隧道客户端' : 'WSS tunnel client' }} |
{{ locale === 'zh' ? '重复执行安装命令会自动升级:停止旧服务 → 更新代码 → 保留配置和数据 → 重启。无需先卸载。' : 'Re-running the install command auto-upgrades: stop old services → update code → keep config and data → restart. No need to uninstall first.' }}
{{ locale === 'zh' ? '停止 Manager + Agent 服务,删除程序文件,保留统计数据和日志。' : 'Stops Manager + Agent services, removes program files, keeps stats data and logs.' }}
{{ locale === 'zh' ? '在标准卸载基础上,额外删除统计数据库、状态文件和日志。' : 'In addition to standard uninstall, also removes stats database, state files, and logs.' }}
传统方案需要公网 IP 或 FRP 端口转发。WSS 中继采用了不同的方式:
Traditional setups require a public IP or FRP port forwarding. WSS relay takes a different approach:
| {{ locale === 'zh' ? '层级' : 'Layer' }} | {{ locale === 'zh' ? '机制' : 'Mechanism' }} |
|---|---|
| {{ locale === 'zh' ? '传输层' : 'Transport' }} | TLS/HTTPS + WSS ({{ locale === 'zh' ? '全链路加密' : 'end-to-end encrypted' }}) |
| {{ locale === 'zh' ? '用户认证' : 'User Auth' }} | OAuth 2.0 PKCE ({{ locale === 'zh' ? '外脑登录' : 'external provider' }}) |
| {{ locale === 'zh' ? '设备认证' : 'Device Auth' }} | Per-tenant Agent Token ({{ locale === 'zh' ? '哈希存储' : 'hashed storage' }}) |
| API {{ locale === 'zh' ? '认证' : 'Auth' }} | API Key ({{ locale === 'zh' ? '绑定租户' : 'tenant-bound' }}) |
| {{ locale === 'zh' ? '本地引擎' : 'Local Engines' }} | {{ locale === 'zh' ? '仅监听 127.0.0.1,不暴露外网' : 'Listen on 127.0.0.1 only, not exposed' }} |
{{ locale === 'zh' ? '需要 Python 3.10+,当前版本 3.9' : 'Requires Python 3.10+, current version 3.9' }}brew install python@3.12curl: (22) The requested URL returned error: 404tail -f ~/Library/Logs/mac-studio-agent.err.log{{ locale === 'zh' ? '推理 API 和 Audio API 共用同一套 Key,支持两种认证方式:' : 'Inference API and Audio API share the same key and support two auth methods:' }}
| {{ locale === 'zh' ? '端点' : 'Endpoint' }} | {{ locale === 'zh' ? '说明' : 'Description' }} |
|---|---|
GET /v1/models | {{ locale === 'zh' ? '列出可用模型' : 'List available models' }} |
POST /v1/chat/completions | {{ locale === 'zh' ? '对话补全(支持流式)' : 'Chat completion (supports streaming)' }} |
POST /v1/messages | {{ locale === 'zh' ? 'Anthropic Messages / Claude Messaging 兼容对话(支持流式)' : 'Anthropic Messages / Claude Messaging-compatible chat (supports streaming)' }} |
{{ locale === 'zh' ? '可用模型以 GET /v1/models(使用你的 Key)实时返回为准:不同 API Key 绑定不同设备/租户,看到的列表不同;模型 id 大小写敏感(如 glm-5.2 为全小写)。下方示例中的模型名仅为演示,请替换为你的列表中的任一 id。' : 'Available models are returned live by GET /v1/models with YOUR key: different keys are bound to different devices/tenants and see different lists; model ids are case-sensitive (e.g. glm-5.2 is all lowercase). Model names in the examples below are illustrative only — replace with any id from your list.' }}
{{ locale === 'zh' ? 'Audio API 公网入口暴露 /v1/files 和 /v1/audio/transcriptions;ASR 模型列表在管理面板的 Audio API 路由中查看。POST /v1/audio/transcriptions 中 model 可选,省略时使用当前 active ASR。' : 'The public Audio API exposes /v1/files and /v1/audio/transcriptions. ASR model routes are listed in the Manager Audio API routes. In POST /v1/audio/transcriptions, model is optional and falls back to the active ASR.' }}
| {{ locale === 'zh' ? '端点' : 'Endpoint' }} | {{ locale === 'zh' ? '说明' : 'Description' }} |
|---|---|
GET /v1/files | {{ locale === 'zh' ? '列出已上传文件' : 'List uploaded files' }} |
POST /v1/files | {{ locale === 'zh' ? '上传音频文件,返回 file_id' : 'Upload an audio file and return file_id' }} |
POST /v1/audio/transcriptions | {{ locale === 'zh' ? '音频转文字;model 可省略,命中当前 active ASR' : 'Audio transcription; model is optional and uses the current active ASR' }} |