diff --git a/ClaudeChecker/ContentView.swift b/ClaudeChecker/ContentView.swift
index fd75fae..fc5feb6 100644
--- a/ClaudeChecker/ContentView.swift
+++ b/ClaudeChecker/ContentView.swift
@@ -596,13 +596,31 @@ struct SettingsView: View {
.font(.system(size: 12, weight: .medium))
.foregroundColor(.secondary)
- HStack(spacing: 6) {
- Circle()
- .fill(vm.isSignedIn ? Color.green : Color.orange)
- .frame(width: 7, height: 7)
- Text(vm.isSignedIn ? "Signed in to claude.ai" : "Not signed in")
- .font(.system(size: 12, weight: .medium))
- .foregroundColor(vm.isSignedIn ? .primary : .orange)
+ if vm.isSignedIn {
+ VStack(alignment: .leading, spacing: 2) {
+ HStack(spacing: 6) {
+ Circle()
+ .fill(Color.green)
+ .frame(width: 7, height: 7)
+ Text("Signed in to claude.ai")
+ .font(.system(size: 13, weight: .semibold))
+ }
+ if !vm.userEmail.isEmpty {
+ Text(vm.userEmail)
+ .font(.system(size: 11.5))
+ .foregroundColor(.secondary)
+ .padding(.leading, 13)
+ }
+ }
+ } else {
+ HStack(spacing: 6) {
+ Circle()
+ .fill(Color.orange)
+ .frame(width: 7, height: 7)
+ Text("Not signed in")
+ .font(.system(size: 13, weight: .semibold))
+ .foregroundColor(.orange)
+ }
}
Text("Your session is stored locally and persists across restarts.")
diff --git a/ClaudeChecker/Info.plist b/ClaudeChecker/Info.plist
index ba1ad04..292b022 100644
--- a/ClaudeChecker/Info.plist
+++ b/ClaudeChecker/Info.plist
@@ -15,9 +15,9 @@
CFBundlePackageType
APPL
CFBundleShortVersionString
- 2.0.6
+ 2.0.7
CFBundleVersion
- 13
+ 14
LSMinimumSystemVersion
13.0
LSUIElement
diff --git a/ClaudeChecker/UsageViewModel.swift b/ClaudeChecker/UsageViewModel.swift
index 3f1cea4..e20ce9b 100644
--- a/ClaudeChecker/UsageViewModel.swift
+++ b/ClaudeChecker/UsageViewModel.swift
@@ -14,6 +14,7 @@ class UsageViewModel: ObservableObject {
@Published var planLabel: String = "Claude"
@Published var isNotAuthenticated: Bool = false
@Published var isSignedIn: Bool = false
+ @Published var userEmail: String = ""
@Published var triggerLogin: Bool = false
@Published var showInMenuBar: Bool = true {
didSet { UserDefaults.standard.set(showInMenuBar, forKey: "show_in_menubar") }
@@ -46,6 +47,7 @@ class UsageViewModel: ObservableObject {
isSignedIn = false
isNotAuthenticated = true
lastUpdated = nil
+ userEmail = ""
limits = []
extraUsage = nil
prepaidCredits = nil
@@ -63,7 +65,9 @@ class UsageViewModel: ObservableObject {
async let usageFetch = fetchUsage(orgId: orgId)
async let prepaidFetch = fetchPrepaidCredits(orgId: orgId)
async let overageFetch = fetchOverageSpendLimit(orgId: orgId)
- let (usage, prepaid, overage) = try await (usageFetch, prepaidFetch, overageFetch)
+ async let emailFetch = fetchUserEmail()
+ let (usage, prepaid, overage, email) = try await (usageFetch, prepaidFetch, overageFetch, emailFetch)
+ if let email { userEmail = email }
limits = buildLimits(from: usage)
extraUsage = usage.extraUsage
prepaidCredits = prepaid
@@ -121,6 +125,23 @@ class UsageViewModel: ObservableObject {
return try JSONDecoder().decode(UsageResponse.self, from: data)
}
+ private func fetchUserEmail() async throws -> String? {
+ let url = URL(string: "https://claude.ai/api/bootstrap")!
+ var req = URLRequest(url: url)
+ req.setValue("application/json", forHTTPHeaderField: "accept")
+ let cookies = await WKWebsiteDataStore.default().httpCookieStore.allCookies()
+ let claudeCookies = cookies.filter { $0.domain.contains("claude.ai") }
+ if let header = HTTPCookie.requestHeaderFields(with: claudeCookies)["Cookie"] {
+ req.setValue(header, forHTTPHeaderField: "Cookie")
+ }
+ let (data, response) = try await URLSession.shared.data(for: req)
+ guard let http = response as? HTTPURLResponse, http.statusCode == 200 else { return nil }
+ guard let json = try? JSONSerialization.jsonObject(with: data) as? [String: Any],
+ let account = json["account"] as? [String: Any],
+ let email = account["email_address"] as? String else { return nil }
+ return email
+ }
+
private func fetchPrepaidCredits(orgId: String) async throws -> PrepaidCredits? {
let url = URL(string: "https://claude.ai/api/organizations/\(orgId)/prepaid/credits")!
var req = URLRequest(url: url)
diff --git a/README.md b/README.md
index ea6b01a..0e89829 100644
--- a/README.md
+++ b/README.md
@@ -8,7 +8,7 @@
[](https://www.apple.com/macos/)
[](https://swift.org)
-[](https://github.com/superdooper86/claudechecker/releases)
+[](https://github.com/superdooper86/claudechecker/releases)
[](LICENSE)
@@ -138,7 +138,7 @@ ClaudeChecker reads from the following `claude.ai` API endpoints (authenticated
| Endpoint | Used for |
|---|---|
-| `/api/bootstrap` | Organisation UUID |
+| `/api/bootstrap` | Organisation UUID and account email |
| `/api/organizations/{id}/usage` | 5-hour and 7-day quota windows |
| `/api/organizations/{id}/overage_spend_limit` | Extra usage spent and monthly limit |
| `/api/organizations/{id}/prepaid/credits` | Current credit balance |
diff --git a/version.json b/version.json
index 702782c..b35739a 100644
--- a/version.json
+++ b/version.json
@@ -1,5 +1,5 @@
{
- "version": "2.0.6",
- "url": "https://github.com/superdooper86/claudechecker/releases/download/v2.0.6/ClaudeChecker.zip",
- "notes": "Authentication section shows sign-in status with re-authenticate and sign out options."
+ "version": "2.0.7",
+ "url": "https://github.com/superdooper86/claudechecker/releases/download/v2.0.7/ClaudeChecker.zip",
+ "notes": "Show account email in settings auth section."
}