Compare commits

...
Author SHA1 Message Date
SuperDooper d2cb75a8dc Installer: bundle all DLLs (remove single-file publish) 2026-05-11 22:05:41 +02:00
SuperDooper 01188b3009 Build: remove PublishSingleFile from Windows stable workflow 2026-05-11 22:05:40 +02:00
SuperDooper 54b89b8e55 Build: remove PublishSingleFile from Windows beta workflow 2026-05-11 22:05:39 +02:00
SuperDooper 24dbf013e7 Fix clipboard crash: use SetDataObject(false) to skip Flush() 2026-05-11 21:44:21 +02:00
SuperDooper 5a799899ef Installer: use userdesktop instead of commondesktop (no admin needed) 2026-05-11 21:41:14 +02:00
SuperDooper f318713ba5 Fix installer OutputDir: use . relative to SourceDir 2026-05-11 21:34:23 +02:00
SuperDooper b9d2c8a5f3 CI: build Inno Setup installer instead of zip 2026-05-11 21:27:59 +02:00
SuperDooper 8883000098 CI: build Inno Setup installer instead of zip 2026-05-11 21:27:57 +02:00
SuperDooper a8acf734ed Updater: use Inno Setup installer for auto-update 2026-05-11 21:27:46 +02:00
SuperDooper c432355be1 Add Inno Setup installer script 2026-05-11 21:27:36 +02:00
SuperDooper 9d3a06bf7a Diagnostics: retry clipboard on COMException to fix crash 2026-05-11 21:20:04 +02:00
SuperDooper 6cc4c1dc2f Diagnostics: update privacy note 2026-05-11 21:07:53 +02:00
SuperDooper e2fff371ee Diagnostics: show (none) for empty last error in copy 2026-05-11 21:06:53 +02:00
SuperDooper 872cdbc684 Diagnostics: clean up orphaned AddSection brackets 2026-05-11 20:51:56 +02:00
SuperDooper 0f78931599 Diagnostics: remove stale Cookie Store section 2026-05-11 20:51:36 +02:00
SuperDooper a84b2645fc Diagnostics: add privacy note 2026-05-11 20:48:25 +02:00
SuperDooper 6152371b16 CI: handle inline WIN_STABLE_BADGE placeholder on first stable release 2026-05-11 20:46:58 +02:00
SuperDooper 60acaf3eea CI: update Win Stable badge and clear Win Beta on stable release 2026-05-11 20:45:03 +02:00
SuperDooper e064e14425 CI: update Windows beta badge label to Win_Beta 2026-05-11 20:43:58 +02:00
SuperDooper 33e35243c4 CI: update Windows beta badge in README on release 2026-05-11 20:41:59 +02:00
6 changed files with 138 additions and 70 deletions
+29 -5
View File
@@ -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="[![Win Beta](https://img.shields.io/badge/Win_Beta-${shields_version}-blue?style=flat)](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"
+41 -5
View File
@@ -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="[![Win Stable](https://img.shields.io/badge/Win_Stable-${shields_version}-blue?style=flat)](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"
+3
View File
@@ -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>
+16 -15
View File
@@ -495,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! (IDs redacted)"; {
// 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();
@@ -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>();
@@ -633,13 +637,10 @@ public partial class PopupWindow : Window
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)
+8 -45
View File
@@ -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)
+41
View File
@@ -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