mirror of
https://github.com/fawney19/Aether.git
synced 2026-10-05 00:47:48 +08:00
fix(claude-code): 额度查询携带 x-app/claude-cli UA 以获取 cedar_ember,并在无重置机会时显式清空
This commit is contained in:
@@ -456,12 +456,15 @@ pub fn parse_claude_code_oauth_usage_response(
|
||||
bucket.insert(format!("{prefix}_reset_at"), serde_json::json!(reset_at));
|
||||
}
|
||||
}
|
||||
if let Some(reset_credits) = parse_claude_code_reset_credits(root, updated_at_unix_secs) {
|
||||
bucket.insert("reset_credits".to_string(), reset_credits);
|
||||
}
|
||||
if bucket.is_empty() {
|
||||
return None;
|
||||
}
|
||||
// Explicit null so a refresh overwrites (rather than keeps) credits that were used up.
|
||||
bucket.insert(
|
||||
"reset_credits".to_string(),
|
||||
parse_claude_code_reset_credits(root, updated_at_unix_secs)
|
||||
.unwrap_or(serde_json::Value::Null),
|
||||
);
|
||||
bucket.insert(
|
||||
"updated_at".to_string(),
|
||||
serde_json::json!(updated_at_unix_secs),
|
||||
@@ -7934,6 +7937,45 @@ mod claude_code_quota_tests {
|
||||
assert!(!credits.to_string().contains("launch"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn real_cedar_ember_response_survives_metadata_redaction() {
|
||||
// Shape captured from a live Claude Pro account (unrelated fields trimmed).
|
||||
let parsed = parse_claude_code_oauth_usage_response(
|
||||
&json!({
|
||||
"five_hour": {"utilization": 32.0, "resets_at": "2026-09-29T19:19:59.933008+00:00"},
|
||||
"cedar_ember": {
|
||||
"eligible": true,
|
||||
"at_limit": false,
|
||||
"grants": [{
|
||||
"id": "opus55-launch-promax-20260921",
|
||||
"label": "Claude Opus 5.5 launch",
|
||||
"resets_total": 1,
|
||||
"resets_left": 1,
|
||||
"starts_at": "2026-09-22T16:00:00+00:00",
|
||||
"ends_at": "2026-10-22T16:00:00+00:00",
|
||||
"clears": ["five_hour", "seven_day"],
|
||||
"paused": false,
|
||||
"usable_now": true,
|
||||
"use_requires_limit": false
|
||||
}],
|
||||
"next_grant_id": "opus55-launch-promax-20260921"
|
||||
}
|
||||
}),
|
||||
1_790_699_000,
|
||||
)
|
||||
.expect("usage should parse");
|
||||
assert_eq!(parsed["reset_credits"]["available_count"], json!(1));
|
||||
|
||||
let safe = crate::provider::redaction::admin_provider_upstream_metadata_safe_json(Some(
|
||||
&json!({ "claude_code": parsed }),
|
||||
));
|
||||
assert_eq!(
|
||||
safe["claude_code"]["reset_credits"]["available_count"],
|
||||
json!(1),
|
||||
"redaction dropped reset_credits: {safe}"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cedar_ember_null_or_empty_omits_reset_credits() {
|
||||
let parsed = parse_claude_code_oauth_usage_response(
|
||||
@@ -7941,7 +7983,7 @@ mod claude_code_quota_tests {
|
||||
1,
|
||||
)
|
||||
.expect("usage should parse");
|
||||
assert!(parsed.get("reset_credits").is_none());
|
||||
assert!(parsed["reset_credits"].is_null());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -11,7 +11,7 @@ use crate::quota_refresh::ProviderPoolQuotaRequestSpec;
|
||||
pub const CLAUDE_CODE_OAUTH_USAGE_URL: &str =
|
||||
"https://api.anthropic.com/api/oauth/usage?cedar_ember=1&skip_spend=1";
|
||||
pub const CLAUDE_CODE_OAUTH_BETA: &str = "oauth-2025-04-20";
|
||||
pub const CLAUDE_CODE_USAGE_USER_AGENT: &str = "claude-code/2.1.284";
|
||||
pub const CLAUDE_CODE_USAGE_USER_AGENT: &str = "claude-cli/2.1.284 (external, cli)";
|
||||
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct ClaudeCodeProviderPoolAdapter;
|
||||
@@ -58,6 +58,8 @@ pub fn build_claude_code_pool_quota_request(
|
||||
"anthropic-beta".to_string(),
|
||||
CLAUDE_CODE_OAUTH_BETA.to_string(),
|
||||
),
|
||||
// The reset-credit (`cedar_ember`) block is only returned to first-party CLI callers.
|
||||
("x-app".to_string(), "cli".to_string()),
|
||||
(
|
||||
"user-agent".to_string(),
|
||||
CLAUDE_CODE_USAGE_USER_AGENT.to_string(),
|
||||
|
||||
Reference in New Issue
Block a user