Compare commits

...
2 Commits
Author SHA1 Message Date
ZyyandCodex 64c1ec2afd chore(release): v3.30.1
Co-authored-by: Codex <noreply@openai.com>
2026-10-23 19:05:30 +08:00
ZyyandCodex a3f0c1b028 fix: correct localized UI and update hyper-util
Restore quota labels, custom window durations, account usage panel width,
and consistent USD symbols. Return stable provider identity attribute keys
for frontend localization and refine login controls, copy, and alignment.

Pin hyper-util to the 0.1.21 registry release and remove the Git patch.

Tests were not rerun for this combined commit, as requested.

Co-authored-by: Codex <noreply@openai.com>
2026-10-23 19:02:48 +08:00
34 changed files with 132 additions and 138 deletions
+4 -2
View File
@@ -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",
-2
View File
@@ -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,
}
+1 -1
View File
@@ -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"] }
+2 -2
View File
@@ -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"
+4 -9
View File
@@ -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 {
+6 -6
View File
@@ -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
View File
@@ -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` 表示不限制。
+1 -1
View File
@@ -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}`
+6 -3
View File
@@ -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}",
+7 -7
View File
@@ -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",
+2 -2
View File
@@ -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": "暫無請求身份",
+1 -1
View File
@@ -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">
+1 -1
View File
@@ -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)
+3 -4
View File
@@ -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>
+2 -11
View File
@@ -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
View File
@@ -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
View File
@@ -1 +1 @@
version: "3.30.0"
version: "3.30.1"