diff --git a/ClaudeChecker/DiagnosticsView.swift b/ClaudeChecker/DiagnosticsView.swift
index 7248715..c9bc9f1 100644
--- a/ClaudeChecker/DiagnosticsView.swift
+++ b/ClaudeChecker/DiagnosticsView.swift
@@ -48,6 +48,7 @@ struct DiagnosticsView: View {
DiagRow("Signed in", vm.isSignedIn ? "Yes" : "No")
DiagRow("Email", vm.userEmail.isEmpty ? "(none)" : vm.userEmail)
DiagRow("Org ID", UserDefaults.standard.string(forKey: "claude_org_id") ?? "(none)")
+ DiagRow("lastActiveOrg", vm.diagLastActiveOrg.isEmpty ? "(not read)" : vm.diagLastActiveOrg)
DiagRow("Error", vm.errorMessage ?? "(none)")
}
@@ -65,9 +66,24 @@ struct DiagnosticsView: View {
Divider()
+ // Bootstrap body
+ if !vm.diagBootstrapBody.isEmpty {
+ DiagSection(title: "Bootstrap Response (first 500 chars)") {
+ Text(vm.diagBootstrapBody)
+ .font(.system(size: 10, design: .monospaced))
+ .foregroundColor(.secondary)
+ .textSelection(.enabled)
+ .frame(maxWidth: .infinity, alignment: .leading)
+ .padding(8)
+ .background(Color.primary.opacity(0.04))
+ .cornerRadius(5)
+ }
+ Divider()
+ }
+
// Response body
if !vm.diagLastBody.isEmpty {
- DiagSection(title: "Response Body (first 500 chars)") {
+ DiagSection(title: "Last API Response (first 500 chars)") {
Text(vm.diagLastBody)
.font(.system(size: 10, design: .monospaced))
.foregroundColor(.secondary)
@@ -164,6 +180,7 @@ struct DiagnosticsView: View {
lines.append("Signed in: \(vm.isSignedIn ? "Yes" : "No")")
lines.append("Email: \(vm.userEmail.isEmpty ? "(none)" : vm.userEmail)")
lines.append("Org ID: \(UserDefaults.standard.string(forKey: "claude_org_id") ?? "(none)")")
+ lines.append("lastActiveOrg: \(vm.diagLastActiveOrg.isEmpty ? "(not read)" : vm.diagLastActiveOrg)")
lines.append("Error: \(vm.errorMessage ?? "(none)")")
lines.append("")
lines.append("Last path: \(vm.diagLastPath)")
@@ -173,7 +190,10 @@ struct DiagnosticsView: View {
lines.append("Claude cookies: \(vm.diagClaudeCookieCount)")
lines.append("Domains: \(vm.diagCookieDomains.joined(separator: ", "))")
lines.append("")
- lines.append("Response body:")
+ lines.append("Bootstrap body:")
+ lines.append(vm.diagBootstrapBody)
+ lines.append("")
+ lines.append("Last API response:")
lines.append(vm.diagLastBody)
lines.append("")
lines.append("Live cookies:")
diff --git a/ClaudeChecker/Info.plist b/ClaudeChecker/Info.plist
index 77e5466..a030427 100644
--- a/ClaudeChecker/Info.plist
+++ b/ClaudeChecker/Info.plist
@@ -15,9 +15,9 @@
CFBundlePackageType
APPL
CFBundleShortVersionString
- 1.2.1-beta.23
+ 1.2.1-beta.24
CFBundleVersion
- 71
+ 72
LSMinimumSystemVersion
13.0
LSUIElement
diff --git a/ClaudeChecker/UsageViewModel.swift b/ClaudeChecker/UsageViewModel.swift
index 9e88b2f..c60e918 100644
--- a/ClaudeChecker/UsageViewModel.swift
+++ b/ClaudeChecker/UsageViewModel.swift
@@ -34,7 +34,7 @@ class UsageViewModel: ObservableObject {
private var apiWebView: WKWebView?
private var navWaiter: WKNavWaiter?
- // Diagnostics — populated on every urlFetch call
+ // Diagnostics
@Published var diagCookieCount: Int = 0
@Published var diagClaudeCookieCount: Int = 0
@Published var diagCookieDomains: [String] = []
@@ -42,6 +42,8 @@ class UsageViewModel: ObservableObject {
@Published var diagLastStatus: Int = 0
@Published var diagLastBody: String = ""
@Published var diagLastError: String = ""
+ @Published var diagLastActiveOrg: String = "" // lastActiveOrg cookie value from JS
+ @Published var diagBootstrapBody: String = "" // raw bootstrap response (first 500 chars)
@Published var diagLastFetch: Date? = nil
init() {
@@ -225,10 +227,31 @@ class UsageViewModel: ObservableObject {
defer { isLoading = false }
do {
+ // Read lastActiveOrg cookie from JS — most reliable org source since it's
+ // set by the claude.ai frontend to whichever org is currently active.
+ var cookieOrgId: String? = nil
+ if let wv = apiWebView, wv.url?.host?.hasSuffix("claude.ai") == true {
+ let js = """
+ return document.cookie.split(';')
+ .map(c => c.trim().split('='))
+ .filter(p => p[0] === 'lastActiveOrg')
+ .map(p => p.slice(1).join('='))[0] || null;
+ """
+ let raw: Any? = try? await withCheckedThrowingContinuation { cont in
+ wv.callAsyncJavaScript(js, arguments: [:], in: nil, in: .defaultClient) { r in
+ cont.resume(returning: (try? r.get()) ?? nil)
+ }
+ }
+ if let v = raw as? String, !v.isEmpty {
+ cookieOrgId = v
+ diagLastActiveOrg = v
+ }
+ }
+
let (fetchedOrgId, fetchedEmail, fetchedPlan) = try await fetchBootstrap()
let orgId: String
- if let id = fetchedOrgId {
+ if let id = cookieOrgId ?? fetchedOrgId {
UserDefaults.standard.set(id, forKey: "claude_org_id")
orgId = id
} else if let cached = UserDefaults.standard.string(forKey: "claude_org_id") {
@@ -292,6 +315,7 @@ class UsageViewModel: ObservableObject {
guard body.trimmingCharacters(in: .whitespacesAndNewlines).hasPrefix("{") else {
throw AppError.detail("Non-JSON response (HTML?): \(body.prefix(80))")
}
+ diagBootstrapBody = String(body.prefix(500))
guard let data = body.data(using: .utf8),
let json = try? JSONSerialization.jsonObject(with: data) as? [String: Any] else {
return (nil, nil, nil)