mirror of
https://github.com/zyycn/codex-proxy-rs.git
synced 2026-11-10 16:05:51 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
64c1ec2afd | ||
|
|
a3f0c1b028 |
Generated
+4
-2
@@ -2253,8 +2253,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "hyper-util"
|
||||
version = "0.1.20"
|
||||
source = "git+https://github.com/hyperium/hyper-util?rev=191b1acf0a6b18e807e4abf5a6d4a7c47979ecd6#191b1acf0a6b18e807e4abf5a6d4a7c47979ecd6"
|
||||
version = "0.1.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ddc03d96684f9226b8a787cdb71488417b53ab5ea8fdb1dac946cb9431cc8bff"
|
||||
dependencies = [
|
||||
"base64 0.23.1",
|
||||
"bytes",
|
||||
@@ -2262,6 +2263,7 @@ dependencies = [
|
||||
"futures-util",
|
||||
"http",
|
||||
"http-body",
|
||||
"httparse",
|
||||
"hyper",
|
||||
"ipnet",
|
||||
"libc",
|
||||
|
||||
@@ -57,7 +57,5 @@ codegen-units = 1
|
||||
panic = "abort"
|
||||
|
||||
[patch.crates-io]
|
||||
# Upstream SOCKS IPv6 fix; remove once a release includes hyperium/hyper-util#302.
|
||||
hyper-util = { git = "https://github.com/hyperium/hyper-util", rev = "191b1acf0a6b18e807e4abf5a6d4a7c47979ecd6" }
|
||||
# 固定上游 BEGIN 取消回滚修复,避免未结束事务复用;见 transact-rs/sqlx#4394
|
||||
sqlx = { git = "https://github.com/transact-rs/sqlx", rev = "f1e94ec34dd5f11ad6488a3a8ac7988439a3f757" }
|
||||
|
||||
@@ -1190,10 +1190,10 @@ pub struct DashboardWireProfile {
|
||||
pub release: Option<DashboardDesktopRelease>,
|
||||
}
|
||||
|
||||
/// Provider 画像的差异字段;展示层只显示标签和值,不解释 Provider 语义
|
||||
/// Provider 画像的差异字段,以稳定标识传递语义,名称由前端本地化
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct DashboardWireAttribute {
|
||||
pub label: String,
|
||||
pub key: String,
|
||||
pub value: String,
|
||||
}
|
||||
|
||||
|
||||
@@ -1044,7 +1044,7 @@ pub(super) fn dashboard_profile_provider() -> Arc<dyn ProviderAdmin> {
|
||||
},
|
||||
user_agent: "gateway-admin-test".to_owned(),
|
||||
attributes: vec![DashboardWireAttribute {
|
||||
label: "Core".to_owned(),
|
||||
key: "core".to_owned(),
|
||||
value: "test".to_owned(),
|
||||
}],
|
||||
verified_at: Some(chrono::Utc::now()),
|
||||
|
||||
@@ -875,7 +875,7 @@ pub(crate) fn wire_profile_view(
|
||||
.attributes
|
||||
.into_iter()
|
||||
.map(|attribute| DashboardWireAttributeView {
|
||||
label: attribute.label,
|
||||
key: attribute.key,
|
||||
value: attribute.value,
|
||||
})
|
||||
.collect(),
|
||||
|
||||
@@ -530,7 +530,7 @@ pub struct DashboardWireProfileView {
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct DashboardWireAttributeView {
|
||||
pub label: String,
|
||||
pub key: String,
|
||||
pub value: String,
|
||||
}
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ http = "1.4.0"
|
||||
http-body-util = "0.1.3"
|
||||
hyper = { version = "=1.11.1", default-features = false }
|
||||
hyper-rustls = { version = "=0.27.7", default-features = false, features = ["http1", "http2", "tls12"] }
|
||||
hyper-util = { version = "0.1.20", features = ["client-legacy", "client-proxy", "http1", "http2", "tokio"] }
|
||||
hyper-util = { version = "=0.1.21", features = ["client-legacy", "client-proxy", "http1", "http2", "tokio"] }
|
||||
ipnet = "2.11.0"
|
||||
percent-encoding = "2.3.2"
|
||||
reqwest = { version = "=0.12.28", default-features = false, features = ["json", "stream", "rustls-tls-native-roots", "socks"] }
|
||||
|
||||
@@ -25,13 +25,13 @@ getrandom = "0.4.3"
|
||||
hex = "0.4.3"
|
||||
# TLS 依赖以官方 Core 锁文件为基线,安全补丁经握手特征核验后精确固定
|
||||
# 包含用于约束传递依赖的直接声明
|
||||
# hyper 受 workspace 的 SOCKS IPv6 修复补丁约束,保留补丁要求的兼容版本。
|
||||
# hyper-util 固定到包含 SOCKS IPv6 修复的正式版
|
||||
# 升级前须重复采样 ClientHello,核对算法、扩展集合及排序约束、key_share 与 ALPN。
|
||||
aws-lc-rs = "=1.18.1"
|
||||
h2 = "=0.4.20"
|
||||
hyper = { version = "=1.11.1", default-features = false }
|
||||
hyper-rustls = "=0.27.7"
|
||||
hyper-util = { version = "0.1.20", features = ["client-legacy", "http1", "tokio"] }
|
||||
hyper-util = { version = "=0.1.21", features = ["client-legacy", "http1", "tokio"] }
|
||||
# 与官方 Codex Desktop 一致:HTTP 默认走 native-tls,rustls 保留给自定义 CA 回退;
|
||||
# vendored OpenSSL 固定 Linux 发布版的 TLS 画像,且不启用压缩特性。
|
||||
native-tls = "=0.2.14"
|
||||
|
||||
@@ -298,7 +298,7 @@ impl ProviderAdmin for OpenAiAdminProvider {
|
||||
},
|
||||
user_agent,
|
||||
attributes: vec![DashboardWireAttribute {
|
||||
label: "客户端标识".to_owned(),
|
||||
key: "client_identifier".to_owned(),
|
||||
value: client_identity,
|
||||
}],
|
||||
verified_at: Some(profile.verified_at),
|
||||
@@ -366,7 +366,7 @@ impl ProviderAdmin for OpenAiAdminProvider {
|
||||
},
|
||||
attributes: vec![
|
||||
DashboardWireAttribute {
|
||||
label: "客户端标识".to_owned(),
|
||||
key: "client_identifier".to_owned(),
|
||||
value: if profile.client_kind == ClientKind::Desktop {
|
||||
format!("{}; {}", profile.originator, profile.desktop_version)
|
||||
} else {
|
||||
@@ -374,13 +374,8 @@ impl ProviderAdmin for OpenAiAdminProvider {
|
||||
},
|
||||
},
|
||||
DashboardWireAttribute {
|
||||
label: "版本策略".to_owned(),
|
||||
value: if custom {
|
||||
"固定身份"
|
||||
} else {
|
||||
"自动最新"
|
||||
}
|
||||
.to_owned(),
|
||||
key: "version_policy".to_owned(),
|
||||
value: if custom { "fixed" } else { "latest" }.to_owned(),
|
||||
},
|
||||
],
|
||||
verified_at: (!custom && profile.verified_at != chrono::DateTime::UNIX_EPOCH)
|
||||
|
||||
@@ -472,10 +472,18 @@ async fn openai_admin_provider_exposes_live_wire_profile_and_validated_billing()
|
||||
profile
|
||||
.attributes
|
||||
.iter()
|
||||
.find(|attribute| attribute.label == "客户端标识")
|
||||
.find(|attribute| attribute.key == "client_identifier")
|
||||
.map(|attribute| attribute.value.as_str()),
|
||||
Some("Codex Desktop; 1.2026.190")
|
||||
);
|
||||
assert_eq!(
|
||||
profile
|
||||
.attributes
|
||||
.iter()
|
||||
.find(|attribute| attribute.key == "version_policy")
|
||||
.map(|attribute| attribute.value.as_str()),
|
||||
Some("fixed")
|
||||
);
|
||||
assert!(profile.release.is_none());
|
||||
let billing = admin
|
||||
.calculated_billing(&ProviderBillingInput {
|
||||
|
||||
@@ -369,15 +369,15 @@ impl ProviderAdmin for XaiAdminProvider {
|
||||
view.target.terminal = profile.client_mode.clone();
|
||||
view.attributes = vec![
|
||||
DashboardWireAttribute {
|
||||
label: "客户端标识".to_owned(),
|
||||
key: "client_identifier".to_owned(),
|
||||
value: profile.client_identifier,
|
||||
},
|
||||
DashboardWireAttribute {
|
||||
label: "运行模式".to_owned(),
|
||||
key: "client_mode".to_owned(),
|
||||
value: profile.client_mode,
|
||||
},
|
||||
DashboardWireAttribute {
|
||||
label: "Token 认证".to_owned(),
|
||||
key: "token_auth".to_owned(),
|
||||
value: "xai-grok-cli".to_owned(),
|
||||
},
|
||||
];
|
||||
@@ -429,15 +429,15 @@ impl ProviderAdmin for XaiAdminProvider {
|
||||
user_agent: profile.user_agent(),
|
||||
attributes: vec![
|
||||
DashboardWireAttribute {
|
||||
label: "客户端标识".to_owned(),
|
||||
key: "client_identifier".to_owned(),
|
||||
value: profile.client_identifier,
|
||||
},
|
||||
DashboardWireAttribute {
|
||||
label: "运行模式".to_owned(),
|
||||
key: "client_mode".to_owned(),
|
||||
value: profile.client_mode,
|
||||
},
|
||||
DashboardWireAttribute {
|
||||
label: "Token 认证".to_owned(),
|
||||
key: "token_auth".to_owned(),
|
||||
value: "xai-grok-cli".to_owned(),
|
||||
},
|
||||
],
|
||||
|
||||
+11
@@ -2009,6 +2009,17 @@ errorCode, errorMessage, startedAt, completedAt, expiresAt, createdAt, updatedAt
|
||||
|
||||
图表颜色、系列身份和选中状态使用 `metric`,不根据译文或数组位置推断指标
|
||||
|
||||
### Dashboard 客户端身份
|
||||
|
||||
`wireProfiles[].attributes[]` 返回稳定的 `key` 和 `value`,字段名称由前端本地化
|
||||
|
||||
- `client_identifier`:上游客户端标识
|
||||
- `client_mode`:客户端运行模式
|
||||
- `token_auth`:认证协议
|
||||
- `version_policy`:版本选择策略,值为 `fixed` 或 `latest`
|
||||
|
||||
页面按 `key` 取值;状态判断使用原始值,不依赖界面译文
|
||||
|
||||
### Dashboard 容量与账号用量
|
||||
|
||||
Dashboard 的 `capacityInfo` 统计普通容量,不含独立审批池;`maxConcurrentPerAccount` 为默认账号并发上限,`0` 表示不限制。
|
||||
|
||||
@@ -129,7 +129,7 @@ export interface DashboardWireProfile {
|
||||
terminal: string
|
||||
}
|
||||
userAgent: string
|
||||
attributes: Array<{ label: string, value: string }>
|
||||
attributes: Array<{ key: string, value: string }>
|
||||
verifiedAt?: string
|
||||
verifiedAtDisplay?: string
|
||||
release?: {
|
||||
|
||||
@@ -257,18 +257,29 @@ export function quotaWindowLabel(window: AccountQuotaWindow, includeLimitName =
|
||||
seconds >= period.seconds * 0.95 && seconds <= period.seconds * 1.05,
|
||||
)
|
||||
let label: string
|
||||
if (period)
|
||||
if (period) {
|
||||
label = period.label()
|
||||
else if (seconds !== null && seconds > 0)
|
||||
label = t('accounts.quota.windows.duration', { duration: rollingWindowDurationDisplay(seconds) })
|
||||
else if (window.role === 'monthly')
|
||||
}
|
||||
else if (seconds !== null && seconds > 0) {
|
||||
if (seconds % 86_400 === 0)
|
||||
label = t('accounts.quota.windows.days', { duration: formatInteger(seconds / 86_400) })
|
||||
else if (seconds % 3_600 === 0)
|
||||
label = t('accounts.quota.windows.hours', { duration: formatInteger(seconds / 3_600) })
|
||||
else
|
||||
label = t('accounts.quota.windows.minutes', { duration: formatInteger(Math.ceil(seconds / 60)) })
|
||||
}
|
||||
else if (window.role === 'monthly') {
|
||||
label = t('accounts.quota.windows.monthly')
|
||||
else if (window.role === 'primary')
|
||||
label = t('accounts.quota.windows.usage')
|
||||
else if (window.role === 'secondary')
|
||||
}
|
||||
else if (window.role === 'primary') {
|
||||
label = t('accounts.quota.windows.primary')
|
||||
}
|
||||
else if (window.role === 'secondary') {
|
||||
label = t('accounts.quota.windows.secondary')
|
||||
else
|
||||
}
|
||||
else {
|
||||
label = window.windowLabelDisplay
|
||||
}
|
||||
|
||||
return includeLimitName && window.limitName && window.limitId !== 'codex'
|
||||
? `${window.limitName} · ${label}`
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"accounts": {
|
||||
"quota": {
|
||||
"general": "General quota",
|
||||
"usageInThisWindow": "Usage in this window: {localUsageDisplay}",
|
||||
"quotaUsed": "Quota used: {rate}",
|
||||
"resets": "Resets: {resetAtDisplay}",
|
||||
@@ -48,9 +49,11 @@
|
||||
"weekly": "Weekly limit",
|
||||
"monthly": "Monthly limit",
|
||||
"annual": "Annual limit",
|
||||
"usage": "Usage limit",
|
||||
"secondary": "Secondary usage limit",
|
||||
"duration": "{duration} limit"
|
||||
"primary": "Primary limit",
|
||||
"secondary": "Secondary limit",
|
||||
"days": "{duration}-day limit",
|
||||
"hours": "{duration}-hour limit",
|
||||
"minutes": "{duration}-minute limit"
|
||||
}
|
||||
},
|
||||
"account": "Account",
|
||||
|
||||
@@ -114,8 +114,6 @@
|
||||
"demoKey": "Demo Key",
|
||||
"requestHealthTimeline": "Request a health timeline",
|
||||
"validRequestAvailability": "Valid request availability",
|
||||
"clientId": "Client ID",
|
||||
"tokenAuthentication": "Token authentication",
|
||||
"todaySToken": "Today's tokens",
|
||||
"hours": "5 hours",
|
||||
"requests": "08-23 {value}:00–{value2}:00 · Requests: {count}",
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
{
|
||||
"auth": {
|
||||
"signOut": "Sign out",
|
||||
"adminSignIn": "Admin sign-in",
|
||||
"apiKeySignIn": "API key sign-in",
|
||||
"signingIn": "Signing in…",
|
||||
"signIn": "Sign in",
|
||||
"method": "Sign-in method",
|
||||
"title": "Sign in",
|
||||
"adminSignIn": "Admin login",
|
||||
"apiKeySignIn": "API key login",
|
||||
"signingIn": "Logging in…",
|
||||
"signIn": "Log in",
|
||||
"method": "Login method",
|
||||
"title": "Console login",
|
||||
"welcome": "Welcome back",
|
||||
"username": "Admin account",
|
||||
"usernamePlaceholder": "Enter your admin account",
|
||||
"passwordPlaceholder": "Enter your password",
|
||||
"apiKeyPlaceholder": "Enter your API key",
|
||||
"pageLabel": "Sign in to Codex Proxy RS",
|
||||
"pageLabel": "Log in to Codex Proxy RS",
|
||||
"password": "Password",
|
||||
"apiKey": "API key",
|
||||
"adminRealm": "Admin",
|
||||
|
||||
@@ -78,8 +78,9 @@
|
||||
"empty": "No successful requests"
|
||||
},
|
||||
"clientIdentity": {
|
||||
"notChecked": "Not checked yet",
|
||||
"clientMode": "Client mode",
|
||||
"versionPolicy": "Version policy",
|
||||
"notChecked": "Not checked yet",
|
||||
"customVersion": "Custom version",
|
||||
"active": "Active",
|
||||
"activeDescription": "New requests using the global settings use this identity",
|
||||
@@ -94,7 +95,6 @@
|
||||
"verifiedAt": "Profile verified: {verifiedAt}",
|
||||
"checkedAt": "Release checked: {checkedAt}",
|
||||
"clientId": "Client ID",
|
||||
"tokenAuth": "Token authentication",
|
||||
"title": "Default upstream identity",
|
||||
"provider": "Upstream platform",
|
||||
"empty": "No request identity available",
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"accounts": {
|
||||
"quota": {
|
||||
"general": "通用额度",
|
||||
"usageInThisWindow": "窗口消耗:{localUsageDisplay}",
|
||||
"quotaUsed": "额度已使用:{rate}",
|
||||
"resets": "重置时间: {resetAtDisplay}",
|
||||
@@ -48,9 +49,11 @@
|
||||
"weekly": "周额度",
|
||||
"monthly": "月额度",
|
||||
"annual": "年额度",
|
||||
"usage": "用量上限",
|
||||
"secondary": "次级用量上限",
|
||||
"duration": "{duration}额度"
|
||||
"primary": "主额度",
|
||||
"secondary": "次级额度",
|
||||
"days": "{duration}日额度",
|
||||
"hours": "{duration}小时额度",
|
||||
"minutes": "{duration}分钟额度"
|
||||
}
|
||||
},
|
||||
"account": "账号",
|
||||
|
||||
@@ -114,8 +114,6 @@
|
||||
"demoKey": "演示 Key",
|
||||
"requestHealthTimeline": "请求健康时间线",
|
||||
"validRequestAvailability": "有效请求可用性",
|
||||
"clientId": "客户端 ID",
|
||||
"tokenAuthentication": "Token 认证",
|
||||
"todaySToken": "今日 Token",
|
||||
"hours": "5 小时",
|
||||
"requests": "08-23 {value}:00–{value2}:00 · {count} 次请求",
|
||||
|
||||
@@ -78,8 +78,9 @@
|
||||
"empty": "暂无成功记录"
|
||||
},
|
||||
"clientIdentity": {
|
||||
"notChecked": "尚未检查",
|
||||
"clientMode": "运行模式",
|
||||
"versionPolicy": "版本策略",
|
||||
"notChecked": "尚未检查",
|
||||
"customVersion": "固定自定义",
|
||||
"active": "当前生效",
|
||||
"activeDescription": "跟随通用设置的新请求使用此身份",
|
||||
@@ -94,7 +95,6 @@
|
||||
"verifiedAt": "画像核验 {verifiedAt}",
|
||||
"checkedAt": "发布检查 {checkedAt}",
|
||||
"clientId": "客户端 ID",
|
||||
"tokenAuth": "Token 认证",
|
||||
"title": "通用上游身份",
|
||||
"provider": "上游平台",
|
||||
"empty": "暂无请求身份",
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"accounts": {
|
||||
"quota": {
|
||||
"general": "通用額度",
|
||||
"usageInThisWindow": "窗口消耗:{localUsageDisplay}",
|
||||
"quotaUsed": "額度已使用:{rate}",
|
||||
"resets": "重置時間: {resetAtDisplay}",
|
||||
@@ -48,9 +49,11 @@
|
||||
"weekly": "周額度",
|
||||
"monthly": "月額度",
|
||||
"annual": "年額度",
|
||||
"usage": "用量上限",
|
||||
"secondary": "次級用量上限",
|
||||
"duration": "{duration}額度"
|
||||
"primary": "主額度",
|
||||
"secondary": "次級額度",
|
||||
"days": "{duration}日額度",
|
||||
"hours": "{duration}小時額度",
|
||||
"minutes": "{duration}分鐘額度"
|
||||
}
|
||||
},
|
||||
"account": "賬號",
|
||||
|
||||
@@ -114,8 +114,6 @@
|
||||
"demoKey": "演示 Key",
|
||||
"requestHealthTimeline": "請求健康時間線",
|
||||
"validRequestAvailability": "有效請求可用性",
|
||||
"clientId": "客戶端 ID",
|
||||
"tokenAuthentication": "Token 認證",
|
||||
"todaySToken": "今日 Token",
|
||||
"hours": "5 小時",
|
||||
"requests": "08-23 {value}:00–{value2}:00 · {count} 次請求",
|
||||
|
||||
@@ -78,8 +78,9 @@
|
||||
"empty": "暫無成功記錄"
|
||||
},
|
||||
"clientIdentity": {
|
||||
"notChecked": "尚未檢查",
|
||||
"clientMode": "運行模式",
|
||||
"versionPolicy": "版本策略",
|
||||
"notChecked": "尚未檢查",
|
||||
"customVersion": "固定自定義",
|
||||
"active": "當前生效",
|
||||
"activeDescription": "跟隨通用設置的新請求使用此身份",
|
||||
@@ -94,7 +95,6 @@
|
||||
"verifiedAt": "畫像覈驗 {verifiedAt}",
|
||||
"checkedAt": "發佈檢查 {checkedAt}",
|
||||
"clientId": "客戶端 ID",
|
||||
"tokenAuth": "Token 認證",
|
||||
"title": "通用上游身份",
|
||||
"provider": "上游平臺",
|
||||
"empty": "暫無請求身份",
|
||||
|
||||
@@ -70,7 +70,7 @@ export function formatUsd(value?: string | number | null, precise = false) {
|
||||
if (parsed == null)
|
||||
return '—'
|
||||
const fractionDigits = precise || (Math.abs(parsed) > 0 && Math.abs(parsed) < 0.01) ? 4 : 2
|
||||
return new Intl.NumberFormat(numberLocale(), {
|
||||
return new Intl.NumberFormat('en-US', {
|
||||
style: 'currency',
|
||||
currency: 'USD',
|
||||
minimumFractionDigits: fractionDigits,
|
||||
|
||||
@@ -35,7 +35,7 @@ const modelUsageColumns = computed(() => (defineTableColumns<AccountModelUsage>(
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<section class="grid gap-4 rounded-lg bg-cp-bg-container p-4 shadow-cp-tertiary xl:min-h-0 xl:grid-cols-[minmax(10rem,1fr)_minmax(0,4fr)]">
|
||||
<section class="grid gap-4 rounded-lg bg-cp-bg-container p-4 shadow-cp-tertiary xl:min-h-0 xl:grid-cols-[0.52fr_1.48fr]">
|
||||
<div class="xl:flex xl:min-h-0 xl:flex-col">
|
||||
<div class="mb-3 flex shrink-0 items-center justify-between gap-3">
|
||||
<h3 class="m-0 text-cp-lg font-heavy text-cp-text">
|
||||
|
||||
@@ -194,7 +194,7 @@ function windowRoleOrder(role: AccountQuotaWindow['role']) {
|
||||
|
||||
function quotaLimitLabel(window: AccountQuotaWindow) {
|
||||
if (window.limitId === 'codex')
|
||||
return 'Codex'
|
||||
return t('accounts.quota.general')
|
||||
return window.limitName
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import type { DashboardWireProfile } from '@/api/modules/dashboard'
|
||||
import { ZCard, ZEmpty, ZSegmented } from '@codex-proxy/ui'
|
||||
|
||||
import { Box, CheckCircle2, Monitor, RefreshCw, ShieldCheck, Terminal, TriangleAlert } from '@lucide/vue'
|
||||
@@ -8,36 +9,11 @@ import { useI18n } from 'vue-i18n'
|
||||
import { formatProviderLabel, isSupportedProvider, providerIcon } from '@/utils/providers'
|
||||
|
||||
const props = defineProps<{
|
||||
profiles: WireProfile[]
|
||||
profiles: DashboardWireProfile[]
|
||||
}>()
|
||||
|
||||
const { t } = useI18n({ useScope: 'global' })
|
||||
|
||||
interface WireProfile {
|
||||
provider: string
|
||||
product: string
|
||||
version: string
|
||||
build?: string | null
|
||||
target: {
|
||||
osType: string
|
||||
osVersion: string
|
||||
arch: string
|
||||
terminal: string
|
||||
}
|
||||
userAgent: string
|
||||
attributes: Array<{ label: string, value: string }>
|
||||
verifiedAt?: string | null
|
||||
verifiedAtDisplay?: string | null
|
||||
release?: {
|
||||
status?: 'unchecked' | 'aligned' | 'review_required' | 'check_failed'
|
||||
checkedAt?: string | null
|
||||
checkedAtDisplay?: string | null
|
||||
latestVersion?: string | null
|
||||
latestBuild?: string | null
|
||||
error?: string | null
|
||||
} | null
|
||||
}
|
||||
|
||||
const activeProvider = shallowRef('')
|
||||
|
||||
const providerOptions = computed(() =>
|
||||
@@ -75,7 +51,7 @@ const releaseStatus = computed(() => {
|
||||
const current = profile.value
|
||||
if (!current?.release) {
|
||||
return {
|
||||
label: current?.attributes.some(item => item.label === t('dashboard.clientIdentity.versionPolicy') && item.value === t('dashboard.clientIdentity.customVersion')) ? t('dashboard.clientIdentity.customVersion') : t('dashboard.clientIdentity.active'),
|
||||
label: current?.attributes.some(item => item.key === 'version_policy' && item.value === 'fixed') ? t('dashboard.clientIdentity.customVersion') : t('dashboard.clientIdentity.active'),
|
||||
title: t('dashboard.clientIdentity.activeDescription'),
|
||||
tone: 'bg-cp-info-container text-cp-info-on-container',
|
||||
icon: ShieldCheck,
|
||||
@@ -137,21 +113,33 @@ const clientIdentity = computed(() => {
|
||||
const current = profile.value
|
||||
if (!current)
|
||||
return '—'
|
||||
const value = current.attributes.find(attribute => attribute.label === t('dashboard.clientIdentity.clientId'))?.value ?? '—'
|
||||
const value = current.attributes.find(attribute => attribute.key === 'client_identifier')?.value
|
||||
if (!value)
|
||||
return '—'
|
||||
return current.provider === 'xai' ? toPascalCase(value) : value
|
||||
})
|
||||
|
||||
const attributeLabels = computed<Record<string, string>>(() => ({
|
||||
client_identifier: t('dashboard.clientIdentity.clientId'),
|
||||
token_auth: t('dashboard.clientIdentity.authProtocol'),
|
||||
client_mode: t('dashboard.clientIdentity.clientMode'),
|
||||
version_policy: t('dashboard.clientIdentity.versionPolicy'),
|
||||
}))
|
||||
|
||||
const identityAttributes = computed(() => {
|
||||
const current = profile.value
|
||||
if (!current)
|
||||
return []
|
||||
return isSupportedProvider(current.provider)
|
||||
? [{ label: t('dashboard.clientIdentity.clientId'), value: clientIdentity.value }]
|
||||
: current.attributes
|
||||
: current.attributes.map(attribute => ({
|
||||
label: attributeLabels.value[attribute.key] ?? attribute.key,
|
||||
value: attribute.value,
|
||||
}))
|
||||
})
|
||||
|
||||
const authProtocol = computed(() => {
|
||||
const value = profile.value?.attributes.find(attribute => attribute.label === t('dashboard.clientIdentity.tokenAuth'))?.value
|
||||
const value = profile.value?.attributes.find(attribute => attribute.key === 'token_auth')?.value
|
||||
if (!value)
|
||||
return '—'
|
||||
return toPascalCase(value)
|
||||
|
||||
@@ -1,15 +1,14 @@
|
||||
import { t } from '@/i18n'
|
||||
import { numberLocale } from '@/utils/format'
|
||||
|
||||
export function money(value: string | number | null, currency = 'USD') {
|
||||
export function money(value: string | number | null) {
|
||||
if (value === null)
|
||||
return t('usage.metrics.notPriced')
|
||||
const amount = Number(value)
|
||||
if (!Number.isFinite(amount))
|
||||
return '—'
|
||||
return new Intl.NumberFormat(numberLocale(), {
|
||||
return new Intl.NumberFormat('en-US', {
|
||||
style: 'currency',
|
||||
currency,
|
||||
currency: 'USD',
|
||||
minimumFractionDigits: 2,
|
||||
maximumFractionDigits: 6,
|
||||
}).format(amount)
|
||||
|
||||
@@ -83,7 +83,7 @@ const submitLabel = computed(() => {
|
||||
>
|
||||
{{ t('auth.title') }}
|
||||
</h1>
|
||||
<p class="m-0 -ml-2 text-sm leading-[1.45] font-normal text-(--cp-login-description-color)">
|
||||
<p class="m-0 text-sm leading-[1.45] font-normal text-(--cp-login-description-color) [&:lang(zh)]:-ml-2">
|
||||
{{ t('auth.welcome') }}
|
||||
</p>
|
||||
</section>
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
<script setup lang="ts">
|
||||
import { ZSelect } from '@codex-proxy/ui'
|
||||
|
||||
import { computed, shallowRef } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
|
||||
import { useLocale } from '@/composables/useLocale'
|
||||
import AppLanguageMenu from '@/components/AppLanguageMenu.vue'
|
||||
import { useAuthStore } from '@/stores/modules/auth'
|
||||
|
||||
import LoginBackground from './components/LoginBackground.vue'
|
||||
@@ -19,7 +17,6 @@ type LoginRealm = 'admin' | 'key'
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
const authStore = useAuthStore()
|
||||
const { locale, localeOptions, setLocale } = useLocale()
|
||||
|
||||
const realm = shallowRef<LoginRealm>(normalizeRealm(window.history.state?.loginMode))
|
||||
const username = shallowRef('')
|
||||
@@ -81,13 +78,7 @@ function resolveDestination(): string {
|
||||
<LoginBackground />
|
||||
|
||||
<div class="absolute top-5 right-5 z-20 flex items-center gap-2 max-[560px]:top-4 max-[560px]:right-4">
|
||||
<ZSelect
|
||||
:model-value="locale"
|
||||
:options="localeOptions"
|
||||
:aria-label="t('common.language')"
|
||||
class="w-36"
|
||||
@update:model-value="setLocale"
|
||||
/>
|
||||
<AppLanguageMenu size="default" class="[&>button:hover]:bg-cp-bg-text-hover" />
|
||||
<LoginThemeToggle />
|
||||
</div>
|
||||
|
||||
|
||||
@@ -286,8 +286,8 @@ export const themeDashboardSummary = computed<DashboardSummaryResponse>(() => ({
|
||||
},
|
||||
userAgent: 'codex_cli_rs/0.163.0-alpha.10.2 (Mac OS 27.0.1; arm64) Codex Desktop; 26.1013.40934',
|
||||
attributes: [
|
||||
{ label: t('appearance.preview.dashboard.clientId'), value: 'Codex Desktop; 26.1013.40934' },
|
||||
{ label: t('appearance.preview.dashboard.tokenAuthentication'), value: 'bearer' },
|
||||
{ key: 'client_identifier', value: 'Codex Desktop; 26.1013.40934' },
|
||||
{ key: 'token_auth', value: 'bearer' },
|
||||
],
|
||||
verifiedAt: '2026-10-17T20:15:28+08:00',
|
||||
verifiedAtDisplay: '2026-10-17 20:15:28',
|
||||
@@ -310,8 +310,8 @@ export const themeDashboardSummary = computed<DashboardSummaryResponse>(() => ({
|
||||
},
|
||||
userAgent: 'grok_cli/0.7.3',
|
||||
attributes: [
|
||||
{ label: t('appearance.preview.dashboard.clientId'), value: 'grok_cli' },
|
||||
{ label: t('appearance.preview.dashboard.tokenAuthentication'), value: 'bearer' },
|
||||
{ key: 'client_identifier', value: 'grok_cli' },
|
||||
{ key: 'token_auth', value: 'bearer' },
|
||||
],
|
||||
verifiedAt: '2026-08-23T10:25:00+08:00',
|
||||
verifiedAtDisplay: '2026-08-23 10:25:00',
|
||||
|
||||
+7
-19
@@ -1,25 +1,13 @@
|
||||
# v3.30.0
|
||||
# v3.30.1
|
||||
|
||||
## 升级说明
|
||||
|
||||
- 前后端需同步更新。管理 API 的账号额度窗口由 `windowLabelDisplay` 改为 `windowKind`,趋势摘要由 `label` 改为 `metric`,展示文案由前端按语言生成;自行对接这些字段的客户端需同步适配
|
||||
- 用量请求明细改用 `clientApiKeyName`、`accountName` 和 `model` 按字面包含筛选,移除旧的通用 `search` 及其他未保留的筛选参数;汇总与洞察仅按时间和 Provider 筛选。自定义管理 API 调用方请按本版 API 文档调整参数
|
||||
- 诊断导出接口改为返回 ZIP,包内包含 `diagnostics.json`、`summary.txt` 和 `README.txt`;直接消费原 JSON 下载响应的工具需改为解包读取
|
||||
|
||||
## 新功能
|
||||
|
||||
- 管理端与 Key 用量页支持简体中文、繁體中文和 English,切换后同步更新表格、图表、通知、共享组件与主题预览,并保存语言选择
|
||||
- 诊断包补充请求输入与最终准备发送正文的指纹、上游终态用量和网关归集值,为关键事件保留采集预算,并明确标记缺失或截断的证据,便于排查缓存、用量与重试问题
|
||||
- API 密钥创建、配置和列表操作统一使用客户端导入弹窗,提供 CC Switch 导入入口及项目链接
|
||||
- 前后端需同步更新。Dashboard 客户端身份属性 `wireProfiles[].attributes[]` 以 `key` 代替 `label`,版本策略值统一为 `fixed` / `latest`,展示文案由前端按语言生成;自行对接这些字段的客户端需同步适配
|
||||
|
||||
## 修复与调整
|
||||
|
||||
- OpenAI 出站会话、窗口和缓存关联标识按选中账号稳定隔离,剥离客户端 trace;轮次 ID、父子轮次引用和续接引用保留客户端原值
|
||||
- Live 语音引导、sideband 与挂断转发业务扩展头,保留 `X-Session-Id`、`Voice-Session-Id` 及通话归属;追踪标识按账号隔离,HTTP 业务头保留重复值和原始字节
|
||||
- 修复位置覆盖在跨日、续接或重放时刷新历史上下文日期的问题,保留原日期,仅调整时区和搜索位置
|
||||
- 调整新建的工作区隐私预设,仅改写附加元数据中的用户名和 Git 远端命名空间,保留项目名与工具执行路径;已有自定义规则保持原配置
|
||||
- 修复 SSE / WebSocket 流内 404 请求错误被误判为路径封禁、进而重试换号或将账号标为凭据无效的问题
|
||||
- 完善取消期间的请求证据与插件中间件处理边界;备份创建、删除请求与审计记录原子写入,账号分组变更保留已提交结果,重启受理后阻止并发更新、回滚和重复重启
|
||||
- 统一管理端流式请求和插件页面的会话恢复,身份切换后取消迟到响应,及时清理失效的 API 密钥明文
|
||||
- 优化用量汇总、请求计数、账号额度和冷却状态查询,减少重复数据库读取;统一管理端与 Key 用量页的字段筛选,并调整表格间距、菜单、统计卡片和窄屏布局
|
||||
- 更新 DOMPurify、`ctutils`、`chacha20` 和 `wnaf` 等依赖,修复已知安全问题并移出已撤回的依赖版本
|
||||
- 恢复“通用额度”及自定义“主额度/次级额度”文案,自定义周期按天、小时或分钟展示,修复“150 分钟额度”变为“9,000 秒额度”的问题
|
||||
- 恢复账号详情中 Token 用量面板的原有宽度,美元金额统一使用 `$`,避免切换语言后显示为 `US$`
|
||||
- 修复客户端 ID、xAI 认证协议及固定版本状态依赖中文字段匹配的问题,改用后端稳定属性键和值,由前端处理多语言显示
|
||||
- 登录页语言切换使用统一图标菜单,悬停背景与主题开关保持一致;修复英文欢迎语对齐,统一英文登录文案
|
||||
- 将 `hyper-util` 固定到正式版 `0.1.21`,移除临时 Git 补丁
|
||||
|
||||
@@ -1 +1 @@
|
||||
version: "3.30.0"
|
||||
version: "3.30.1"
|
||||
|
||||
Reference in New Issue
Block a user