beta.17: adopt login WebView for API calls, add diagnostics
This commit is contained in:
@@ -98,16 +98,17 @@ class UsageViewModel: ObservableObject {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Called after login: reloads the background WebView to pick up the new session.
|
// Called after login: adopts the proven-authenticated login WebView for all API calls.
|
||||||
func reloadAPIWebViewAndRefresh() async {
|
// This avoids the background WebView potentially missing auth tokens that only exist
|
||||||
guard let wv = apiWebView else {
|
// in the login WebView's JS context (localStorage, Service Workers, etc.).
|
||||||
await refresh()
|
func adoptAndRefresh(_ loginWebView: WKWebView) async {
|
||||||
return
|
loginWebView.removeFromSuperview()
|
||||||
}
|
loginWebView.frame = NSRect(x: 0, y: 0, width: 1, height: 1)
|
||||||
apiWebViewLoaded = false
|
apiWindow?.contentView?.addSubview(loginWebView)
|
||||||
wv.load(URLRequest(url: URL(string: "https://claude.ai")!))
|
apiWebView = loginWebView
|
||||||
await waitForAPIWebViewReady()
|
apiWebViewLoaded = true
|
||||||
try? await Task.sleep(nanoseconds: 500_000_000)
|
resumeAPIReadyContinuations()
|
||||||
|
try? await Task.sleep(nanoseconds: 400_000_000)
|
||||||
await refresh()
|
await refresh()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -115,15 +116,22 @@ class UsageViewModel: ObservableObject {
|
|||||||
// This includes all credentials the page has (cookies, localStorage tokens, etc.),
|
// This includes all credentials the page has (cookies, localStorage tokens, etc.),
|
||||||
// which URLSession cannot access — hence using callAsyncJavaScript instead.
|
// which URLSession cannot access — hence using callAsyncJavaScript instead.
|
||||||
private func webViewFetch(_ path: String) async throws -> (statusCode: Int, body: String) {
|
private func webViewFetch(_ path: String) async throws -> (statusCode: Int, body: String) {
|
||||||
guard let wv = apiWebView else { throw AppError.networkError }
|
guard let wv = apiWebView else { throw AppError.detail("no webview") }
|
||||||
await waitForAPIWebViewReady()
|
await waitForAPIWebViewReady()
|
||||||
let js = "const r = await fetch(path, {credentials:'include'}); return {s: r.status, b: await r.text()};"
|
let js = "const r = await fetch(path, {credentials:'include'}); return {s: r.status, b: await r.text()};"
|
||||||
let result = try await wv.callAsyncJavaScript(
|
do {
|
||||||
js, arguments: ["path": path], in: nil, in: .page)
|
let result = try await wv.callAsyncJavaScript(js, arguments: ["path": path], in: nil, in: .page)
|
||||||
guard let d = result as? [String: Any],
|
guard let d = result as? [String: Any],
|
||||||
let s = (d["s"] as? NSNumber)?.intValue,
|
let s = (d["s"] as? NSNumber)?.intValue,
|
||||||
let b = d["b"] as? String else { throw AppError.networkError }
|
let b = d["b"] as? String else {
|
||||||
return (s, b)
|
throw AppError.detail("bad result: \(String(describing: result).prefix(120))")
|
||||||
|
}
|
||||||
|
return (s, b)
|
||||||
|
} catch let e as AppError { throw e }
|
||||||
|
catch {
|
||||||
|
let loc = wv.url?.absoluteString ?? "?"
|
||||||
|
throw AppError.detail("JS err @ \(loc): \(error.localizedDescription.prefix(100))")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func checkInitialSignInState() async {
|
private func checkInitialSignInState() async {
|
||||||
@@ -409,10 +417,12 @@ private class APIWebViewDelegate: NSObject, WKNavigationDelegate {
|
|||||||
enum AppError: LocalizedError {
|
enum AppError: LocalizedError {
|
||||||
case notAuthenticated
|
case notAuthenticated
|
||||||
case networkError
|
case networkError
|
||||||
|
case detail(String)
|
||||||
var errorDescription: String? {
|
var errorDescription: String? {
|
||||||
switch self {
|
switch self {
|
||||||
case .notAuthenticated: return "Not signed into claude.ai — open claude.ai in your browser first."
|
case .notAuthenticated: return "Not signed into claude.ai — open claude.ai in your browser first."
|
||||||
case .networkError: return "Network error fetching usage data."
|
case .networkError: return "Network error fetching usage data."
|
||||||
|
case .detail(let msg): return msg
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user