Compare commits

...
4 changed files with 66 additions and 30 deletions
+37 -11
View File
@@ -47,7 +47,6 @@ public partial class LoginWindow : Window
: "Complete sign-in, then click Done.";
});
// Auto-close once we land on any claude.ai page that isn't the login flow
if (!uri.Contains("/login") && !uri.Contains("/signin") && signedIn)
await SaveAndClose(cookies);
};
@@ -67,9 +66,19 @@ public partial class LoginWindow : Window
UsageViewModel.SaveCookies(cookies);
// Fetch bootstrap + usage from within WebView2 (already authenticated, no header issues)
try
{
// Use WebMessageReceived so the async script can post back without relying
// on Promise-awaiting support in the WebView2 runtime version.
var tcs = new TaskCompletionSource<string?>(TaskCreationOptions.RunContinuationsAsynchronously);
EventHandler<CoreWebView2WebMessageReceivedEventArgs>? msgHandler = null;
msgHandler = (_, args) =>
{
Browser.CoreWebView2.WebMessageReceived -= msgHandler;
tcs.TrySetResult(args.WebMessageAsJson);
};
Browser.CoreWebView2.WebMessageReceived += msgHandler;
const string script = @"(async()=>{try{
const h={headers:{accept:'application/json'}};
const b=await(await fetch('/api/bootstrap',h)).json();
@@ -84,16 +93,22 @@ public partial class LoginWindow : Window
if(Array.isArray(ol)&&ol.length>0){id=ol[0]?.uuid||null;orgSrc='orgs-list';}}catch(e2){}
}
if(!id){
try{const pu=await(await fetch('/api/usage',h)).json();
if(pu&&!pu.error)return{email:e,orgId:null,usage:pu,debug:'no-org|bkeys:'+bkeys+'|akeys:'+akeys};}catch(e3){}
return{email:e,orgId:null,usage:null,debug:'no-org|bkeys:'+bkeys+'|akeys:'+akeys};
let pu=null;
try{pu=await(await fetch('/api/usage',h)).json();}catch(e3){}
window.chrome.webview.postMessage({email:e,orgId:null,usage:(pu&&!pu.error?pu:null),debug:'no-org|bkeys:'+bkeys+'|akeys:'+akeys});
return;
}
const u=await(await fetch('/api/organizations/'+id+'/usage',h)).json();
return{email:e,orgId:id,usage:u,debug:'orgSrc:'+orgSrc+'|bkeys:'+bkeys+'|akeys:'+akeys};
}catch(ex){return{error:String(ex)};}})()";
window.chrome.webview.postMessage({email:e,orgId:id,usage:u,debug:'orgSrc:'+orgSrc+'|bkeys:'+bkeys+'|akeys:'+akeys});
}catch(ex){window.chrome.webview.postMessage({error:String(ex)});}})()";
var json = await Browser.CoreWebView2.ExecuteScriptAsync(script);
if (json != "null" && !string.IsNullOrEmpty(json))
await Browser.CoreWebView2.ExecuteScriptAsync(script);
// Wait up to 15 s for the script to post its message
var completed = await Task.WhenAny(tcs.Task, Task.Delay(15000));
var json = completed == tcs.Task ? tcs.Task.Result : null;
if (!string.IsNullOrEmpty(json))
{
using var doc = JsonDocument.Parse(json);
var root = doc.RootElement;
@@ -110,12 +125,23 @@ public partial class LoginWindow : Window
if (root.TryGetProperty("debug", out var dbg) && dbg.ValueKind == JsonValueKind.String)
AppSettings.Default.DebugInfo = dbg.GetString() ?? "";
else if (root.TryGetProperty("error", out var err))
AppSettings.Default.DebugInfo = "error:" + err.GetRawText();
AppSettings.Default.DebugInfo = "JS error: " + err.GetRawText();
else
AppSettings.Default.DebugInfo = "timeout or empty message";
AppSettings.Default.Save();
}
else
{
AppSettings.Default.DebugInfo = json == null ? "script timeout (15s)" : "empty message";
AppSettings.Default.Save();
}
}
catch (Exception ex)
{
AppSettings.Default.DebugInfo = "exception: " + ex.Message;
AppSettings.Default.Save();
}
catch { }
await Dispatcher.InvokeAsync(() => DialogResult = true);
}
+2 -2
View File
@@ -1,3 +1,3 @@
## What's new in beta.25
## What's new in beta.27
- Debug: Settings panel now shows bootstrap diagnostic info after sign-in (helps diagnose missing data)
- Fix build error: use WebMessageAsJson (not WebMessageAsString) and post plain JS objects via postMessage
+6 -5
View File
@@ -201,13 +201,14 @@ public class UsageViewModel : INotifyPropertyChanged
if(Array.isArray(ol)&&ol.length>0)id=ol[0]?.uuid||null;}catch(e2){}
}
if(!id){
try{const pu=await(await fetch('/api/usage',h)).json();
if(pu&&!pu.error)return{email:em,orgId:null,usage:pu};}catch(e3){}
return{email:em,orgId:null,usage:null};
let pu=null;
try{pu=await(await fetch('/api/usage',h)).json();}catch(e3){}
window.chrome.webview.postMessage({email:em,orgId:null,usage:(pu&&!pu.error?pu:null)});
return;
}
const u=await(await fetch('/api/organizations/'+id+'/usage',h)).json();
return{email:em,orgId:id,usage:u};
}catch(ex){return null;}})()";
window.chrome.webview.postMessage({email:em,orgId:id,usage:u});
}catch(ex){window.chrome.webview.postMessage(null);}})();";
var resultJson = await Application.Current.Dispatcher.InvokeAsync(async () =>
{
+21 -12
View File
@@ -34,25 +34,34 @@ internal sealed class WebViewFetchWindow : Window
await _wv.EnsureCoreWebView2Async(env);
var tcs = new TaskCompletionSource<string?>();
var tcs = new TaskCompletionSource<string?>(TaskCreationOptions.RunContinuationsAsynchronously);
EventHandler<CoreWebView2NavigationCompletedEventArgs>? handler = null;
handler = async (_, e) =>
// Use WebMessageReceived so the script can post back asynchronously without
// relying on Promise-awaiting support in the WebView2 runtime version.
EventHandler<CoreWebView2WebMessageReceivedEventArgs>? msgHandler = null;
msgHandler = (_, args) =>
{
_wv.CoreWebView2.NavigationCompleted -= handler;
_wv.CoreWebView2.WebMessageReceived -= msgHandler;
tcs.TrySetResult(args.WebMessageAsJson);
};
_wv.CoreWebView2.WebMessageReceived += msgHandler;
EventHandler<CoreWebView2NavigationCompletedEventArgs>? navHandler = null;
navHandler = async (_, e) =>
{
_wv.CoreWebView2.NavigationCompleted -= navHandler;
if (!e.IsSuccess) { tcs.TrySetResult(null); return; }
try
{
var result = await _wv.CoreWebView2.ExecuteScriptAsync(script);
tcs.TrySetResult(result);
}
try { await _wv.CoreWebView2.ExecuteScriptAsync(script); }
catch { tcs.TrySetResult(null); }
};
_wv.CoreWebView2.NavigationCompleted += handler;
_wv.CoreWebView2.NavigationCompleted += navHandler;
_wv.CoreWebView2.Navigate(navigateUrl);
_ = Task.Delay(timeoutMs).ContinueWith(_ => tcs.TrySetResult(null));
_ = Task.Delay(timeoutMs).ContinueWith(_ =>
{
_wv.CoreWebView2.WebMessageReceived -= msgHandler;
tcs.TrySetResult(null);
});
return await tcs.Task;
}