Compare commits
26
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8a104ad8d0 | ||
|
|
59c911dff4 | ||
|
|
127fc82b2a | ||
|
|
9bde5b74a0 | ||
|
|
bdede36b5f | ||
|
|
d2cb75a8dc | ||
|
|
01188b3009 | ||
|
|
54b89b8e55 | ||
|
|
24dbf013e7 | ||
|
|
5a799899ef | ||
|
|
f318713ba5 | ||
|
|
b9d2c8a5f3 | ||
|
|
8883000098 | ||
|
|
a8acf734ed | ||
|
|
c432355be1 | ||
|
|
9d3a06bf7a | ||
|
|
6cc4c1dc2f | ||
|
|
e2fff371ee | ||
|
|
872cdbc684 | ||
|
|
0f78931599 | ||
|
|
a84b2645fc | ||
|
|
6152371b16 | ||
|
|
60acaf3eea | ||
|
|
e064e14425 | ||
|
|
33e35243c4 | ||
|
|
e16bfa59d0 |
@@ -30,15 +30,14 @@ jobs:
|
|||||||
dotnet publish windows/ClaudeCheckerWindows.csproj `
|
dotnet publish windows/ClaudeCheckerWindows.csproj `
|
||||||
-c Release -r win-x64 `
|
-c Release -r win-x64 `
|
||||||
--self-contained true `
|
--self-contained true `
|
||||||
-p:PublishSingleFile=true `
|
|
||||||
-p:Version=${{ steps.version.outputs.version }} `
|
-p:Version=${{ steps.version.outputs.version }} `
|
||||||
-o publish
|
-o publish
|
||||||
|
|
||||||
- name: Zip
|
- name: Build installer
|
||||||
shell: pwsh
|
shell: pwsh
|
||||||
run: |
|
run: |
|
||||||
Copy-Item -Recurse windows/Assets publish/Assets
|
Copy-Item -Recurse windows/Assets publish/Assets
|
||||||
Compress-Archive -Path publish/* -DestinationPath ClaudeChecker-Windows.zip
|
& "C:\Program Files (x86)\Inno Setup 6\iscc.exe" /DAppVersion="${{ steps.version.outputs.version }}" windows\installer.iss
|
||||||
|
|
||||||
- name: Create GitHub pre-release
|
- name: Create GitHub pre-release
|
||||||
env:
|
env:
|
||||||
@@ -46,7 +45,7 @@ jobs:
|
|||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
notes=$(cat windows/RELEASE_NOTES.md 2>/dev/null || echo "Windows beta ${{ steps.version.outputs.version }}")
|
notes=$(cat windows/RELEASE_NOTES.md 2>/dev/null || echo "Windows beta ${{ steps.version.outputs.version }}")
|
||||||
gh release create "$GITHUB_REF_NAME" ClaudeChecker-Windows.zip \
|
gh release create "$GITHUB_REF_NAME" ClaudeChecker-Installer.exe \
|
||||||
--title "$GITHUB_REF_NAME" \
|
--title "$GITHUB_REF_NAME" \
|
||||||
--prerelease \
|
--prerelease \
|
||||||
--notes "$notes"
|
--notes "$notes"
|
||||||
@@ -59,7 +58,7 @@ jobs:
|
|||||||
notes=$(cat windows/RELEASE_NOTES.md 2>/dev/null || echo "Windows beta ${{ steps.version.outputs.version }}")
|
notes=$(cat windows/RELEASE_NOTES.md 2>/dev/null || echo "Windows beta ${{ steps.version.outputs.version }}")
|
||||||
content=$(jq -n \
|
content=$(jq -n \
|
||||||
--arg version "${{ steps.version.outputs.version }}" \
|
--arg version "${{ steps.version.outputs.version }}" \
|
||||||
--arg url "https://github.com/${{ github.repository }}/releases/download/${{ github.ref_name }}/ClaudeChecker-Windows.zip" \
|
--arg url "https://github.com/${{ github.repository }}/releases/download/${{ github.ref_name }}/ClaudeChecker-Installer.exe" \
|
||||||
--arg notes "$notes" \
|
--arg notes "$notes" \
|
||||||
'{version: $version, url: $url, notes: $notes}' | base64 -w0)
|
'{version: $version, url: $url, notes: $notes}' | base64 -w0)
|
||||||
sha=$(gh api "repos/${{ github.repository }}/contents/version-windows-beta.json?ref=main" --jq .sha 2>/dev/null || echo "")
|
sha=$(gh api "repos/${{ github.repository }}/contents/version-windows-beta.json?ref=main" --jq .sha 2>/dev/null || echo "")
|
||||||
@@ -75,3 +74,28 @@ jobs:
|
|||||||
--field "content=$content" \
|
--field "content=$content" \
|
||||||
--field "branch=main"
|
--field "branch=main"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
- name: Update Windows beta badge in README on main
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.RELEASE_TOKEN }}
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
version="${{ steps.version.outputs.version }}"
|
||||||
|
tag="${{ github.ref_name }}"
|
||||||
|
shields_version=$(echo "$version" | sed 's/-/--/g')
|
||||||
|
export WIN_BADGE="[](https://github.com/${{ github.repository }}/releases/tag/${tag}) <!-- WIN_BETA_BADGE -->"
|
||||||
|
sha=$(gh api "repos/${{ github.repository }}/contents/README.md?ref=main" --jq .sha)
|
||||||
|
content=$(gh api "repos/${{ github.repository }}/contents/README.md?ref=main" --jq .content | python3 -c "
|
||||||
|
import sys, base64, os
|
||||||
|
raw = sys.stdin.read().strip()
|
||||||
|
text = base64.b64decode(raw).decode()
|
||||||
|
badge = os.environ['WIN_BADGE']
|
||||||
|
lines = text.splitlines(keepends=True)
|
||||||
|
out = [badge + chr(10) if '<!-- WIN_BETA_BADGE -->' in line else line for line in lines]
|
||||||
|
print(base64.b64encode(''.join(out).encode()).decode(), end='')
|
||||||
|
")
|
||||||
|
gh api --method PUT "repos/${{ github.repository }}/contents/README.md" \
|
||||||
|
--field "message=Windows beta badge $tag" \
|
||||||
|
--field "content=$content" \
|
||||||
|
--field "branch=main" \
|
||||||
|
--field "sha=$sha"
|
||||||
|
|||||||
@@ -31,15 +31,14 @@ jobs:
|
|||||||
dotnet publish windows/ClaudeCheckerWindows.csproj `
|
dotnet publish windows/ClaudeCheckerWindows.csproj `
|
||||||
-c Release -r win-x64 `
|
-c Release -r win-x64 `
|
||||||
--self-contained true `
|
--self-contained true `
|
||||||
-p:PublishSingleFile=true `
|
|
||||||
-p:Version=${{ steps.version.outputs.version }} `
|
-p:Version=${{ steps.version.outputs.version }} `
|
||||||
-o publish
|
-o publish
|
||||||
|
|
||||||
- name: Zip
|
- name: Build installer
|
||||||
shell: pwsh
|
shell: pwsh
|
||||||
run: |
|
run: |
|
||||||
Copy-Item -Recurse windows/Assets publish/Assets
|
Copy-Item -Recurse windows/Assets publish/Assets
|
||||||
Compress-Archive -Path publish/* -DestinationPath ClaudeChecker-Windows.zip
|
& "C:\Program Files (x86)\Inno Setup 6\iscc.exe" /DAppVersion="${{ steps.version.outputs.version }}" windows\installer.iss
|
||||||
|
|
||||||
- name: Create GitHub release
|
- name: Create GitHub release
|
||||||
env:
|
env:
|
||||||
@@ -47,7 +46,7 @@ jobs:
|
|||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
notes=$(cat windows/RELEASE_NOTES.md 2>/dev/null || echo "Windows release ${{ steps.version.outputs.version }}")
|
notes=$(cat windows/RELEASE_NOTES.md 2>/dev/null || echo "Windows release ${{ steps.version.outputs.version }}")
|
||||||
gh release create "$GITHUB_REF_NAME" ClaudeChecker-Windows.zip \
|
gh release create "$GITHUB_REF_NAME" ClaudeChecker-Installer.exe \
|
||||||
--title "$GITHUB_REF_NAME" \
|
--title "$GITHUB_REF_NAME" \
|
||||||
--notes "$notes"
|
--notes "$notes"
|
||||||
|
|
||||||
@@ -59,7 +58,7 @@ jobs:
|
|||||||
notes=$(cat windows/RELEASE_NOTES.md 2>/dev/null || echo "Windows release ${{ steps.version.outputs.version }}")
|
notes=$(cat windows/RELEASE_NOTES.md 2>/dev/null || echo "Windows release ${{ steps.version.outputs.version }}")
|
||||||
content=$(jq -n \
|
content=$(jq -n \
|
||||||
--arg version "${{ steps.version.outputs.version }}" \
|
--arg version "${{ steps.version.outputs.version }}" \
|
||||||
--arg url "https://github.com/${{ github.repository }}/releases/download/${{ github.ref_name }}/ClaudeChecker-Windows.zip" \
|
--arg url "https://github.com/${{ github.repository }}/releases/download/${{ github.ref_name }}/ClaudeChecker-Installer.exe" \
|
||||||
--arg notes "$notes" \
|
--arg notes "$notes" \
|
||||||
'{version: $version, url: $url, notes: $notes}' | base64 -w0)
|
'{version: $version, url: $url, notes: $notes}' | base64 -w0)
|
||||||
sha=$(gh api "repos/${{ github.repository }}/contents/version-windows.json?ref=main" --jq .sha 2>/dev/null || echo "")
|
sha=$(gh api "repos/${{ github.repository }}/contents/version-windows.json?ref=main" --jq .sha 2>/dev/null || echo "")
|
||||||
@@ -75,3 +74,40 @@ jobs:
|
|||||||
--field "content=$content" \
|
--field "content=$content" \
|
||||||
--field "branch=main"
|
--field "branch=main"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
- name: Update Windows stable badge in README on main
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.RELEASE_TOKEN }}
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
version="${{ steps.version.outputs.version }}"
|
||||||
|
tag="${{ github.ref_name }}"
|
||||||
|
shields_version=$(echo "$version" | sed 's/-/--/g')
|
||||||
|
export WIN_STABLE_BADGE="[](https://github.com/${{ github.repository }}/releases/tag/${tag}) <!-- WIN_STABLE_BADGE -->"
|
||||||
|
sha=$(gh api "repos/${{ github.repository }}/contents/README.md?ref=main" --jq .sha)
|
||||||
|
content=$(gh api "repos/${{ github.repository }}/contents/README.md?ref=main" --jq .content | python3 -c "
|
||||||
|
import sys, base64, os
|
||||||
|
raw = sys.stdin.read().strip()
|
||||||
|
text = base64.b64decode(raw).decode()
|
||||||
|
stable_badge = os.environ['WIN_STABLE_BADGE']
|
||||||
|
lines = text.splitlines(keepends=True)
|
||||||
|
out = []
|
||||||
|
for line in lines:
|
||||||
|
if '<!-- WIN_STABLE_BADGE -->' in line:
|
||||||
|
out.append(stable_badge + chr(10))
|
||||||
|
rest = line.replace(' <!-- WIN_STABLE_BADGE -->', '').replace('<!-- WIN_STABLE_BADGE -->', '')
|
||||||
|
if rest.strip() and '<!-- WIN_BETA_BADGE -->' in rest:
|
||||||
|
out.append('<!-- WIN_BETA_BADGE -->' + chr(10))
|
||||||
|
elif rest.strip():
|
||||||
|
out.append(rest)
|
||||||
|
elif '<!-- WIN_BETA_BADGE -->' in line:
|
||||||
|
out.append('<!-- WIN_BETA_BADGE -->' + chr(10))
|
||||||
|
else:
|
||||||
|
out.append(line)
|
||||||
|
print(base64.b64encode(''.join(out).encode()).decode(), end='')
|
||||||
|
")
|
||||||
|
gh api --method PUT "repos/${{ github.repository }}/contents/README.md" \
|
||||||
|
--field "message=Windows stable badge $tag" \
|
||||||
|
--field "content=$content" \
|
||||||
|
--field "branch=main" \
|
||||||
|
--field "sha=$sha"
|
||||||
|
|||||||
@@ -35,7 +35,7 @@
|
|||||||
VerticalAlignment="Center" Text="Waiting for sign-in…"/>
|
VerticalAlignment="Center" Text="Waiting for sign-in…"/>
|
||||||
<Button Grid.Column="1" Content="Done — I'm signed in"
|
<Button Grid.Column="1" Content="Done — I'm signed in"
|
||||||
Style="{StaticResource PrimaryButton}" Click="Done_Click"
|
Style="{StaticResource PrimaryButton}" Click="Done_Click"
|
||||||
x:Name="DoneButton" IsEnabled="False"/>
|
x:Name="DoneButton"/>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Border>
|
</Border>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|||||||
@@ -33,13 +33,11 @@ public partial class LoginWindow : Window
|
|||||||
|
|
||||||
var cookies = await Browser.CoreWebView2.CookieManager.GetCookiesAsync("https://claude.ai");
|
var cookies = await Browser.CoreWebView2.CookieManager.GetCookiesAsync("https://claude.ai");
|
||||||
var signedIn = System.Linq.Enumerable.Any(cookies, c =>
|
var signedIn = System.Linq.Enumerable.Any(cookies, c =>
|
||||||
c.Name.Equals("sessionKey", System.StringComparison.OrdinalIgnoreCase) ||
|
c.Domain.Contains("claude.ai") || c.Domain.Contains("anthropic.com"));
|
||||||
c.Name.StartsWith("sk-ant", System.StringComparison.OrdinalIgnoreCase));
|
|
||||||
|
|
||||||
await Dispatcher.InvokeAsync(() =>
|
await Dispatcher.InvokeAsync(() =>
|
||||||
{
|
{
|
||||||
DoneButton.IsEnabled = !uri.Contains("/login") && !uri.Contains("/signin");
|
StatusText.Text = signedIn && !uri.Contains("/login") && !uri.Contains("/signin")
|
||||||
StatusText.Text = signedIn
|
|
||||||
? "Signed in — click Done to continue."
|
? "Signed in — click Done to continue."
|
||||||
: "Complete sign-in, then click Done.";
|
: "Complete sign-in, then click Done.";
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -282,6 +282,9 @@
|
|||||||
Style="{StaticResource SmallButton}" Click="CopyDiag_Click"/>
|
Style="{StaticResource SmallButton}" Click="CopyDiag_Click"/>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Border>
|
</Border>
|
||||||
|
<TextBlock DockPanel.Dock="Top" Text="Org IDs are redacted when copied."
|
||||||
|
FontSize="10" Foreground="{DynamicResource SecondaryBrush}"
|
||||||
|
Margin="14,6,14,6"/>
|
||||||
<ScrollViewer VerticalScrollBarVisibility="Auto">
|
<ScrollViewer VerticalScrollBarVisibility="Auto">
|
||||||
<StackPanel x:Name="DiagContentPanel" Margin="16,4,16,16"/>
|
<StackPanel x:Name="DiagContentPanel" Margin="16,4,16,16"/>
|
||||||
</ScrollViewer>
|
</ScrollViewer>
|
||||||
|
|||||||
+24
-19
@@ -4,6 +4,7 @@ using System.Collections.Generic;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using System.Windows;
|
using System.Windows;
|
||||||
using System.Windows.Controls;
|
using System.Windows.Controls;
|
||||||
@@ -494,8 +495,21 @@ public partial class PopupWindow : Window
|
|||||||
|
|
||||||
private void CopyDiag_Click(object s, RoutedEventArgs e)
|
private void CopyDiag_Click(object s, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
Clipboard.SetText(BuildDiagText());
|
try
|
||||||
CopyDiagButton.Content = "Copied!";
|
{
|
||||||
|
// SetDataObject(data, false) skips Flush() — avoids CLIPBRD_E_CANT_OPEN crash
|
||||||
|
var diagText = BuildDiagText();
|
||||||
|
for (int i = 0; i < 5; i++)
|
||||||
|
{
|
||||||
|
try { Clipboard.SetDataObject(diagText, false); break; }
|
||||||
|
catch (Exception) { System.Threading.Thread.Sleep(100); }
|
||||||
|
}
|
||||||
|
CopyDiagButton.Content = "Copied! (IDs redacted)";
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
CopyDiagButton.Content = "Copy failed";
|
||||||
|
}
|
||||||
var t = new DispatcherTimer { Interval = TimeSpan.FromSeconds(1.5) };
|
var t = new DispatcherTimer { Interval = TimeSpan.FromSeconds(1.5) };
|
||||||
t.Tick += (_, _) => { CopyDiagButton.Content = "Copy All"; t.Stop(); };
|
t.Tick += (_, _) => { CopyDiagButton.Content = "Copy All"; t.Stop(); };
|
||||||
t.Start();
|
t.Start();
|
||||||
@@ -553,7 +567,6 @@ public partial class PopupWindow : Window
|
|||||||
[
|
[
|
||||||
MakeRow("Version", Updater.CurrentVersion),
|
MakeRow("Version", Updater.CurrentVersion),
|
||||||
MakeRow("Signed in", VM.IsSignedIn ? "Yes" : "No"),
|
MakeRow("Signed in", VM.IsSignedIn ? "Yes" : "No"),
|
||||||
MakeRow("Email", string.IsNullOrEmpty(VM.UserEmail) ? "(none)" : VM.UserEmail),
|
|
||||||
MakeRow("Org ID", string.IsNullOrEmpty(VM.DiagOrgId) ? "(none)" : VM.DiagOrgId),
|
MakeRow("Org ID", string.IsNullOrEmpty(VM.DiagOrgId) ? "(none)" : VM.DiagOrgId),
|
||||||
MakeRow("lastActiveOrg", string.IsNullOrEmpty(VM.DiagLastActiveOrg) ? "(not read)" : VM.DiagLastActiveOrg),
|
MakeRow("lastActiveOrg", string.IsNullOrEmpty(VM.DiagLastActiveOrg) ? "(not read)" : VM.DiagLastActiveOrg),
|
||||||
MakeRow("Error", VM.ErrorMessage ?? "(none)"),
|
MakeRow("Error", VM.ErrorMessage ?? "(none)"),
|
||||||
@@ -570,15 +583,6 @@ public partial class PopupWindow : Window
|
|||||||
reqRows.Add(MakeRow("Time", VM.DiagLastFetch.Value.ToString("HH:mm:ss")));
|
reqRows.Add(MakeRow("Time", VM.DiagLastFetch.Value.ToString("HH:mm:ss")));
|
||||||
AddSection("LAST REQUEST", reqRows);
|
AddSection("LAST REQUEST", reqRows);
|
||||||
|
|
||||||
// COOKIE STORE
|
|
||||||
AddSection("COOKIE STORE",
|
|
||||||
[
|
|
||||||
MakeRow("Total cookies", VM.DiagCookieCount.ToString()),
|
|
||||||
MakeRow("Claude cookies", VM.DiagClaudeCookieCount.ToString()),
|
|
||||||
MakeRow("All domains", VM.DiagCookieDomains.Count == 0
|
|
||||||
? "(none)" : string.Join(", ", VM.DiagCookieDomains)),
|
|
||||||
]);
|
|
||||||
|
|
||||||
// STORED COOKIE NAMES
|
// STORED COOKIE NAMES
|
||||||
var storedNames = GetStoredCookieNames();
|
var storedNames = GetStoredCookieNames();
|
||||||
var nameRows = new List<FrameworkElement>();
|
var nameRows = new List<FrameworkElement>();
|
||||||
@@ -627,27 +631,26 @@ public partial class PopupWindow : Window
|
|||||||
sb.AppendLine("=== ClaudeChecker Diagnostics (Windows) ===");
|
sb.AppendLine("=== ClaudeChecker Diagnostics (Windows) ===");
|
||||||
sb.AppendLine($"Version: {Updater.CurrentVersion}");
|
sb.AppendLine($"Version: {Updater.CurrentVersion}");
|
||||||
sb.AppendLine($"Signed in: {(VM.IsSignedIn ? "Yes" : "No")}");
|
sb.AppendLine($"Signed in: {(VM.IsSignedIn ? "Yes" : "No")}");
|
||||||
sb.AppendLine($"Email: {(string.IsNullOrEmpty(VM.UserEmail) ? "(none)" : VM.UserEmail)}");
|
|
||||||
sb.AppendLine($"Org ID: {(string.IsNullOrEmpty(VM.DiagOrgId) ? "(none)" : VM.DiagOrgId)}");
|
sb.AppendLine($"Org ID: {(string.IsNullOrEmpty(VM.DiagOrgId) ? "(none)" : VM.DiagOrgId)}");
|
||||||
sb.AppendLine($"lastActiveOrg: {(string.IsNullOrEmpty(VM.DiagLastActiveOrg) ? "(not read)" : VM.DiagLastActiveOrg)}");
|
sb.AppendLine($"lastActiveOrg: {(string.IsNullOrEmpty(VM.DiagLastActiveOrg) ? "(not read)" : VM.DiagLastActiveOrg)}");
|
||||||
sb.AppendLine($"Error: {VM.ErrorMessage ?? "(none)"}");
|
sb.AppendLine($"Error: {VM.ErrorMessage ?? "(none)"}");
|
||||||
sb.AppendLine();
|
sb.AppendLine();
|
||||||
sb.AppendLine($"Last path: {VM.DiagLastPath}");
|
sb.AppendLine($"Last path: {VM.DiagLastPath}");
|
||||||
sb.AppendLine($"Last status: {VM.DiagLastStatus}");
|
sb.AppendLine($"Last status: {VM.DiagLastStatus}");
|
||||||
sb.AppendLine($"Last error: {VM.DiagLastError}");
|
sb.AppendLine($"Last error: {(string.IsNullOrEmpty(VM.DiagLastError) ? "(none)" : VM.DiagLastError)}");
|
||||||
if (VM.DiagLastFetch.HasValue)
|
if (VM.DiagLastFetch.HasValue)
|
||||||
sb.AppendLine($"Last fetch: {VM.DiagLastFetch.Value:HH:mm:ss}");
|
sb.AppendLine($"Last fetch: {VM.DiagLastFetch.Value:HH:mm:ss}");
|
||||||
sb.AppendLine();
|
sb.AppendLine();
|
||||||
sb.AppendLine($"Total cookies: {VM.DiagCookieCount}");
|
|
||||||
sb.AppendLine($"Claude cookies: {VM.DiagClaudeCookieCount}");
|
|
||||||
sb.AppendLine($"Domains: {string.Join(", ", VM.DiagCookieDomains)}");
|
|
||||||
sb.AppendLine();
|
sb.AppendLine();
|
||||||
sb.AppendLine("Stored cookies:");
|
sb.AppendLine("Stored cookies:");
|
||||||
foreach (var c in storedNames)
|
foreach (var c in storedNames)
|
||||||
sb.AppendLine($" {c.Domain} {c.Name}");
|
sb.AppendLine($" {c.Domain} {c.Name}");
|
||||||
return sb.ToString();
|
return RedactUUIDs(sb.ToString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static string RedactUUIDs(string text) =>
|
||||||
|
Regex.Replace(text, @"[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}", "****");
|
||||||
|
|
||||||
// ── Event handlers ───────────────────────────────────────────────
|
// ── Event handlers ───────────────────────────────────────────────
|
||||||
|
|
||||||
private void Settings_Click(object s, RoutedEventArgs e)
|
private void Settings_Click(object s, RoutedEventArgs e)
|
||||||
@@ -705,7 +708,9 @@ public partial class PopupWindow : Window
|
|||||||
|
|
||||||
_ = Task.Run(async () =>
|
_ = Task.Run(async () =>
|
||||||
{
|
{
|
||||||
await Task.Delay(3000);
|
// Re-navigate background browser with fresh session, then refresh immediately
|
||||||
|
await Application.Current.Dispatcher.InvokeAsync(
|
||||||
|
() => App.BackgroundBrowser.ReloadAsync()).Task.Unwrap();
|
||||||
await VM.RefreshAsync();
|
await VM.RefreshAsync();
|
||||||
await Application.Current.Dispatcher.InvokeAsync(InitSettings);
|
await Application.Current.Dispatcher.InvokeAsync(InitSettings);
|
||||||
});
|
});
|
||||||
|
|||||||
+14
-19
@@ -1,23 +1,18 @@
|
|||||||
## What's new in beta.49
|
## What's new in beta.62
|
||||||
|
|
||||||
### Architecture
|
### Installer
|
||||||
- All data (usage, plan, overage, prepaid credits) now fetched via the persistent background WebView2 — the same always-live session as the macOS app, so Cloudflare cookies never expire between refreshes
|
- App now ships as a proper Windows installer (no more zip extract)
|
||||||
- HttpClient path kept only as a brief startup fallback before the browser is ready
|
- Installs to user AppData — no admin rights required
|
||||||
- Settings file simplified: only stores session signal, burn history, and user preferences — no more cached API responses
|
- Auto-update now downloads and silently runs the new installer, then relaunches
|
||||||
|
- Desktop shortcut creation is optional during install
|
||||||
|
|
||||||
### Bug fixes
|
### Bug fixes
|
||||||
- Plan label (e.g. "Pro") now updates on every refresh, not just at login
|
- Fixed app crash on launch (startup DllNotFoundException caused by single-file publish; all DLLs now installed alongside the exe)
|
||||||
- Extra Usage Credits values now correct (were 100× too large)
|
- Fixed clicking Copy in Diagnostics crashing the app (CLIPBRD_E_CANT_OPEN)
|
||||||
- Limit and balance now update live on every refresh
|
- Fixed Sign In — Done button now always clickable; session detection no longer depends on specific cookie names that may have changed
|
||||||
- Refresh interval dropdown now shows "1 min", "2 min" etc. correctly
|
|
||||||
- ComboBox no longer flashes bright blue when clicked
|
|
||||||
- Buttons now show a visible pressed state
|
|
||||||
- Footer "Updated X ago" counter now ticks every second; shows "X min, Y sec ago" past 60 s
|
|
||||||
- Removed stray sparkline bars from the 5 h and 7 d limit cards
|
|
||||||
- App window now shown on launch
|
|
||||||
|
|
||||||
### UI improvements
|
### Diagnostics
|
||||||
- Gauge percentage larger, "used" label removed
|
- Org IDs are now redacted when copying diagnostics text
|
||||||
- Each limit card now shows an "after reset" or "today HH:MM" badge
|
- Removed stale Cookie Store section (always showed 0)
|
||||||
- Session Diary card redesigned to match macOS layout (stats row + sparkline, no Claude header)
|
- Fixed missing fields in copied diagnostics output
|
||||||
- Reset date moved inline with time remaining
|
- Privacy note added below diagnostics header
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ using System;
|
|||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.IO.Compression;
|
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
@@ -136,9 +135,7 @@ public class UpdateManager : INotifyPropertyChanged
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var tmpDir = Path.Combine(Path.GetTempPath(), $"CCUpdate_{Guid.NewGuid():N}");
|
var installerPath = Path.Combine(Path.GetTempPath(), $"ClaudeCheckerInstaller_{Guid.NewGuid():N}.exe");
|
||||||
Directory.CreateDirectory(tmpDir);
|
|
||||||
var zipPath = Path.Combine(tmpDir, "ClaudeChecker.zip");
|
|
||||||
|
|
||||||
using var http = new HttpClient();
|
using var http = new HttpClient();
|
||||||
var response = await http.GetAsync(DownloadUrl, HttpCompletionOption.ResponseHeadersRead);
|
var response = await http.GetAsync(DownloadUrl, HttpCompletionOption.ResponseHeadersRead);
|
||||||
@@ -148,7 +145,7 @@ public class UpdateManager : INotifyPropertyChanged
|
|||||||
var received = 0L;
|
var received = 0L;
|
||||||
|
|
||||||
await using (var stream = await response.Content.ReadAsStreamAsync())
|
await using (var stream = await response.Content.ReadAsStreamAsync())
|
||||||
await using (var file = File.Create(zipPath))
|
await using (var file = File.Create(installerPath))
|
||||||
{
|
{
|
||||||
var buffer = new byte[81920];
|
var buffer = new byte[81920];
|
||||||
int read;
|
int read;
|
||||||
@@ -157,48 +154,21 @@ public class UpdateManager : INotifyPropertyChanged
|
|||||||
await file.WriteAsync(buffer.AsMemory(0, read));
|
await file.WriteAsync(buffer.AsMemory(0, read));
|
||||||
received += read;
|
received += read;
|
||||||
if (total > 0)
|
if (total > 0)
|
||||||
DownloadProgress = (double)received / total * 0.8;
|
DownloadProgress = (double)received / total;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
StatusMessage = "Unpacking…";
|
|
||||||
DownloadProgress = 0.85;
|
|
||||||
|
|
||||||
var extractDir = Path.Combine(tmpDir, "extracted");
|
|
||||||
ZipFile.ExtractToDirectory(zipPath, extractDir, overwriteFiles: true);
|
|
||||||
|
|
||||||
// Find the installer exe
|
|
||||||
var newExe = FindExe(extractDir);
|
|
||||||
if (newExe == null) throw new Exception("ClaudeChecker.exe not found in update package.");
|
|
||||||
|
|
||||||
DownloadProgress = 0.95;
|
|
||||||
StatusMessage = "Installing…";
|
|
||||||
|
|
||||||
// Write a batch script to replace the exe after we exit
|
|
||||||
var currentExe = Process.GetCurrentProcess().MainModule!.FileName;
|
|
||||||
var script = $"""
|
|
||||||
@echo off
|
|
||||||
timeout /t 2 /nobreak > nul
|
|
||||||
copy /Y "{newExe}" "{currentExe}"
|
|
||||||
start "" "{currentExe}"
|
|
||||||
rmdir /S /Q "{tmpDir}"
|
|
||||||
""";
|
|
||||||
|
|
||||||
var scriptPath = Path.Combine(Path.GetTempPath(), "claudechecker_update.bat");
|
|
||||||
await File.WriteAllTextAsync(scriptPath, script);
|
|
||||||
|
|
||||||
DownloadProgress = 1.0;
|
DownloadProgress = 1.0;
|
||||||
StatusMessage = "Installed! Relaunching…";
|
StatusMessage = "Installing…";
|
||||||
UpdateComplete = true;
|
UpdateComplete = true;
|
||||||
|
|
||||||
await Task.Delay(800);
|
await Task.Delay(500);
|
||||||
|
|
||||||
Process.Start(new ProcessStartInfo
|
Process.Start(new ProcessStartInfo
|
||||||
{
|
{
|
||||||
FileName = "cmd.exe",
|
FileName = installerPath,
|
||||||
Arguments = $"/C \"{scriptPath}\"",
|
Arguments = "/SILENT /CLOSEAPPLICATIONS",
|
||||||
CreateNoWindow = true,
|
UseShellExecute = true,
|
||||||
UseShellExecute = false,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
Application.Current.Dispatcher.Invoke(() => Application.Current.Shutdown());
|
Application.Current.Dispatcher.Invoke(() => Application.Current.Shutdown());
|
||||||
@@ -211,13 +181,6 @@ public class UpdateManager : INotifyPropertyChanged
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string? FindExe(string dir)
|
|
||||||
{
|
|
||||||
foreach (var f in Directory.EnumerateFiles(dir, "ClaudeChecker.exe", SearchOption.AllDirectories))
|
|
||||||
return f;
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool IsNewer(string version, string current)
|
public static bool IsNewer(string version, string current)
|
||||||
{
|
{
|
||||||
static (int[] Base, int[]? Pre) Parse(string v)
|
static (int[] Base, int[]? Pre) Parse(string v)
|
||||||
|
|||||||
@@ -129,6 +129,20 @@ public sealed class WebViewFetchWindow : Window
|
|||||||
return await tcs.Task;
|
return await tcs.Task;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Re-navigate to claude.ai with the current session (call after sign-in).
|
||||||
|
public async Task ReloadAsync()
|
||||||
|
{
|
||||||
|
if (_wv.CoreWebView2 == null) { await InitAsync(); return; }
|
||||||
|
_readyForScript = false;
|
||||||
|
var navDone = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);
|
||||||
|
EventHandler<CoreWebView2NavigationCompletedEventArgs>? h = null;
|
||||||
|
h = (_, e) => { _wv.CoreWebView2.NavigationCompleted -= h; navDone.TrySetResult(e.IsSuccess); };
|
||||||
|
_wv.CoreWebView2.NavigationCompleted += h;
|
||||||
|
_wv.CoreWebView2.Navigate("https://claude.ai");
|
||||||
|
await Task.WhenAny(navDone.Task, Task.Delay(15000));
|
||||||
|
_readyForScript = navDone.Task.IsCompletedSuccessfully && navDone.Task.Result;
|
||||||
|
}
|
||||||
|
|
||||||
// ── Shared init ───────────────────────────────────────────────────────────
|
// ── Shared init ───────────────────────────────────────────────────────────
|
||||||
|
|
||||||
private int _initGuard;
|
private int _initGuard;
|
||||||
|
|||||||
@@ -0,0 +1,41 @@
|
|||||||
|
#ifndef AppVersion
|
||||||
|
#define AppVersion "0.0.1"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
[Setup]
|
||||||
|
AppName=ClaudeChecker
|
||||||
|
AppVersion={#AppVersion}
|
||||||
|
AppPublisher=superdooper86
|
||||||
|
AppPublisherURL=https://github.com/superdooper86/claudechecker
|
||||||
|
AppSupportURL=https://github.com/superdooper86/claudechecker/issues
|
||||||
|
AppUpdatesURL=https://github.com/superdooper86/claudechecker/releases
|
||||||
|
DefaultDirName={localappdata}\ClaudeChecker
|
||||||
|
DefaultGroupName=ClaudeChecker
|
||||||
|
DisableProgramGroupPage=yes
|
||||||
|
PrivilegesRequired=lowest
|
||||||
|
OutputDir=.
|
||||||
|
OutputBaseFilename=ClaudeChecker-Installer
|
||||||
|
Compression=lzma
|
||||||
|
SolidCompression=yes
|
||||||
|
WizardStyle=modern
|
||||||
|
UninstallDisplayIcon={app}\ClaudeChecker.exe
|
||||||
|
SourceDir=..
|
||||||
|
|
||||||
|
[Languages]
|
||||||
|
Name: "english"; MessagesFile: "compiler:Default.isl"
|
||||||
|
|
||||||
|
[Tasks]
|
||||||
|
Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked
|
||||||
|
|
||||||
|
[Files]
|
||||||
|
Source: "publish\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs
|
||||||
|
|
||||||
|
[Icons]
|
||||||
|
Name: "{group}\ClaudeChecker"; Filename: "{app}\ClaudeChecker.exe"
|
||||||
|
Name: "{userdesktop}\ClaudeChecker"; Filename: "{app}\ClaudeChecker.exe"; Tasks: desktopicon
|
||||||
|
|
||||||
|
[Run]
|
||||||
|
; Silent install (auto-update): launch automatically
|
||||||
|
Filename: "{app}\ClaudeChecker.exe"; Flags: nowait; Check: WizardSilent
|
||||||
|
; Manual install: show launch checkbox on final page
|
||||||
|
Filename: "{app}\ClaudeChecker.exe"; Description: "{cm:LaunchProgram,ClaudeChecker}"; Flags: nowait postinstall skipifsilent
|
||||||
Reference in New Issue
Block a user