From 963e3d2e3483d411854126447804ec090728692b Mon Sep 17 00:00:00 2001 From: K9i Date: Sun, 20 Jul 2025 09:54:15 +0900 Subject: [PATCH 1/3] =?UTF-8?q?fix:=20=E3=82=A2=E3=83=83=E3=83=97=E3=83=87?= =?UTF-8?q?=E3=83=BC=E3=83=88=E3=83=81=E3=83=A3=E3=83=B3=E3=83=8D=E3=83=AB?= =?UTF-8?q?=E3=81=AE=E6=9C=80=E6=96=B0=E3=83=90=E3=83=BC=E3=82=B8=E3=83=A7?= =?UTF-8?q?=E3=83=B3=E8=A1=A8=E7=A4=BA=E6=A9=9F=E8=83=BD=E3=82=92=E4=BF=AE?= =?UTF-8?q?=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - バージョン取得時のエラーハンドリングを改善 - デバッグログを追加して問題の特定を容易に - チャンネル変更時に最新バージョンを再取得するように修正 --- .../Views/Tabs/SettingsTabView.swift | 35 ++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/Sources/ClaudeUsageMonitor/Views/Tabs/SettingsTabView.swift b/Sources/ClaudeUsageMonitor/Views/Tabs/SettingsTabView.swift index 5399b9d..41b4e0b 100644 --- a/Sources/ClaudeUsageMonitor/Views/Tabs/SettingsTabView.swift +++ b/Sources/ClaudeUsageMonitor/Views/Tabs/SettingsTabView.swift @@ -584,6 +584,9 @@ struct SettingsTabView: View { if let appDelegate = NSApplication.shared.delegate as? AppDelegate { appDelegate.updateChannelChanged(to: channel) } + + // Re-fetch latest versions when channel changes + fetchLatestVersions() } private func checkLatestVersion() { @@ -628,10 +631,13 @@ struct SettingsTabView: View { } private func fetchLatestVersions() { + print("🔄 Starting to fetch latest versions...") + // Fetch stable version Task { await fetchLatestVersion(for: .stable) { version in self.latestStableVersion = version + print("📦 Stable version set to: \(version ?? "nil")") } } @@ -639,28 +645,55 @@ struct SettingsTabView: View { Task { await fetchLatestVersion(for: .dev) { version in self.latestDevVersion = version + print("📦 Dev version set to: \(version ?? "nil")") } } } private func fetchLatestVersion(for channel: UpdateChannel, completion: @escaping (String?) -> Void) async { guard let url = URL(string: channel.appcastURL) else { + print("❌ Invalid URL for channel \(channel): \(channel.appcastURL)") completion(nil) return } do { - let (data, _) = try await URLSession.shared.data(from: url) + let (data, response) = try await URLSession.shared.data(from: url) + + if let httpResponse = response as? HTTPURLResponse { + print("📡 Fetching \(channel) version from: \(url)") + print("📡 Response status: \(httpResponse.statusCode)") + + guard httpResponse.statusCode == 200 else { + print("❌ HTTP error: \(httpResponse.statusCode)") + await MainActor.run { + completion(nil) + } + return + } + } + let parser = XMLParser(data: data) let delegate = AppcastParserDelegate() parser.delegate = delegate if parser.parse(), let version = delegate.latestVersion { + print("✅ Found \(channel) version: \(version)") await MainActor.run { completion(version) } + } else { + print("❌ Failed to parse XML for \(channel)") + // デバッグ用: XMLの最初の部分を表示 + if let xmlString = String(data: data.prefix(500), encoding: .utf8) { + print("XML preview: \(xmlString)") + } + await MainActor.run { + completion(nil) + } } } catch { + print("❌ Error fetching \(channel) version: \(error)") await MainActor.run { completion(nil) } From 7df4b2ed316efa708c88c67b70d8397f319fbb33 Mon Sep 17 00:00:00 2001 From: K9i Date: Sun, 20 Jul 2025 10:00:23 +0900 Subject: [PATCH 2/3] =?UTF-8?q?fix:=20Sparkle=E6=9D=A1=E4=BB=B6=E4=BB=98?= =?UTF-8?q?=E3=81=8D=E3=82=B3=E3=83=B3=E3=83=91=E3=82=A4=E3=83=AB=E3=81=A7?= =?UTF-8?q?updateChannelChanged=E5=91=BC=E3=81=B3=E5=87=BA=E3=81=97?= =?UTF-8?q?=E3=82=92=E3=83=A9=E3=83=83=E3=83=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Sources/ClaudeUsageMonitor/Views/Tabs/SettingsTabView.swift | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Sources/ClaudeUsageMonitor/Views/Tabs/SettingsTabView.swift b/Sources/ClaudeUsageMonitor/Views/Tabs/SettingsTabView.swift index 41b4e0b..55667c5 100644 --- a/Sources/ClaudeUsageMonitor/Views/Tabs/SettingsTabView.swift +++ b/Sources/ClaudeUsageMonitor/Views/Tabs/SettingsTabView.swift @@ -580,10 +580,12 @@ struct SettingsTabView: View { private func selectUpdateChannel(_ channel: UpdateChannel) { UserDefaults.standard.updateChannel = channel + #if canImport(Sparkle) // Notify AppDelegate to update Sparkle configuration if let appDelegate = NSApplication.shared.delegate as? AppDelegate { appDelegate.updateChannelChanged(to: channel) } + #endif // Re-fetch latest versions when channel changes fetchLatestVersions() From 1924351f9394fd3ce468d7762c52d62c9c30d6a2 Mon Sep 17 00:00:00 2001 From: K9i Date: Sun, 20 Jul 2025 10:16:00 +0900 Subject: [PATCH 3/3] =?UTF-8?q?fix:=20ccusage=E3=83=90=E3=83=BC=E3=82=B8?= =?UTF-8?q?=E3=83=A7=E3=83=B3=E5=9B=BA=E5=AE=9A=E3=81=A8=E3=83=88=E3=83=BC?= =?UTF-8?q?=E3=82=AF=E3=83=B3=E8=A8=88=E7=AE=97=E3=81=AE=E5=95=8F=E9=A1=8C?= =?UTF-8?q?=E3=82=92=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - BundleConfiguration.ccusageVersionにフォールバック値15.3.0を追加 - CommandExecutorでccusageバージョンの検証を強化 - SessionViewModelで残りトークンが負の値にならないよう修正 - デバッグログを追加してバージョンとトークン計算を追跡可能に --- .../ClaudeUsageMonitor/App/BundleConfiguration.swift | 12 +++++++++++- .../Services/CommandExecutor.swift | 10 ++++++++-- .../ViewModels/SessionViewModel.swift | 6 +++++- 3 files changed, 24 insertions(+), 4 deletions(-) diff --git a/Sources/ClaudeUsageMonitor/App/BundleConfiguration.swift b/Sources/ClaudeUsageMonitor/App/BundleConfiguration.swift index bbb5fae..931ce24 100644 --- a/Sources/ClaudeUsageMonitor/App/BundleConfiguration.swift +++ b/Sources/ClaudeUsageMonitor/App/BundleConfiguration.swift @@ -18,6 +18,16 @@ enum BundleConfiguration { } static var ccusageVersion: String { - return Bundle.main.object(forInfoDictionaryKey: "CcusageVersion") as? String ?? "" + let version = Bundle.main.object(forInfoDictionaryKey: "CcusageVersion") as? String ?? "" + let fallbackVersion = "15.3.0" + + // Debug時にInfo.plistが更新されていない場合のフォールバック + if version.isEmpty { + print("⚠️ [BundleConfiguration] CcusageVersion not found in Info.plist, using fallback: \(fallbackVersion)") + return fallbackVersion + } + + print("📦 [BundleConfiguration] Using ccusage version: \(version)") + return version } } diff --git a/Sources/ClaudeUsageMonitor/Services/CommandExecutor.swift b/Sources/ClaudeUsageMonitor/Services/CommandExecutor.swift index a8c745d..5340e66 100644 --- a/Sources/ClaudeUsageMonitor/Services/CommandExecutor.swift +++ b/Sources/ClaudeUsageMonitor/Services/CommandExecutor.swift @@ -74,18 +74,23 @@ class CommandExecutor { func executeCcusageCommand(subcommand: String? = nil, additionalArgs: [String] = []) async throws -> String { var command: String var ccusageArgs: [String] = [] + + let ccusageVersion = BundleConfiguration.ccusageVersion + guard !ccusageVersion.isEmpty else { + throw CommandError.commandNotFound("ccusage version not configured") + } // Try bunx first if let bunxPath = try await findCommand("bunx") { print("[CommandExecutor] Found bunx at: \(bunxPath)") command = bunxPath - ccusageArgs.append("ccusage@\(BundleConfiguration.ccusageVersion)") + ccusageArgs.append("ccusage@\(ccusageVersion)") } // Fall back to npx else if let npxPath = try await findCommand("npx") { print("[CommandExecutor] Found npx at: \(npxPath)") command = npxPath - ccusageArgs.append("ccusage@\(BundleConfiguration.ccusageVersion)") + ccusageArgs.append("ccusage@\(ccusageVersion)") } // Neither found else { @@ -103,6 +108,7 @@ class CommandExecutor { ccusageArgs.append(contentsOf: additionalArgs) print("[CommandExecutor] Executing: \(command) \(ccusageArgs.joined(separator: " "))") + print("[CommandExecutor] Using ccusage version: \(ccusageVersion)") // Execute the command let task = Process() diff --git a/Sources/ClaudeUsageMonitor/ViewModels/SessionViewModel.swift b/Sources/ClaudeUsageMonitor/ViewModels/SessionViewModel.swift index 46b0c49..27a6b20 100644 --- a/Sources/ClaudeUsageMonitor/ViewModels/SessionViewModel.swift +++ b/Sources/ClaudeUsageMonitor/ViewModels/SessionViewModel.swift @@ -26,13 +26,17 @@ class SessionViewModel: ObservableObject { } session = data - remainingTokens = monitor.usageData.sessionTokenLimit - data.totalTokens + remainingTokens = max(0, monitor.usageData.sessionTokenLimit - data.totalTokens) percentage = monitor.usageData.sessionUsagePercentage burnRate = Double(monitor.usageData.sessionBurnRate) ?? 0 remainingTime = monitor.usageData.sessionRemainingTime cost = data.costUSD planDescription = monitor.usageData.planDescription resetTime = Date.formatTime(from: data.endTime) + + // デバッグ用ログ + print("🔍 [SessionViewModel] Total: \(data.totalTokens), Limit: \(monitor.usageData.sessionTokenLimit), Remaining: \(remainingTokens)") + print("🔍 [SessionViewModel] Plan: \(planDescription), Percentage: \(String(format: "%.1f", percentage))%") // 通知機能は初回リリースでは無効化 /*