@@ -134,87 +134,6 @@ class UsageMonitor: ObservableObject, UsageMonitoring {
134
134
return
135
135
}
136
136
137
- // App Sandbox prevents direct command execution
138
- // The following code only works when App Sandbox is disabled
139
- guard ProcessInfo . processInfo. environment [ " APP_SANDBOX_CONTAINER_ID " ] == nil else {
140
- print ( " [DEBUG] App Sandbox is enabled - cannot execute external commands " )
141
- return
142
- }
143
-
144
- // Fallback to npx command (only when App Sandbox is disabled)
145
- print ( " [DEBUG] App Sandbox is disabled - falling back to npx command " )
146
- do {
147
- let process = Process ( )
148
-
149
- // Try to use the same npx detection as in fetchSessionData
150
- let npxSearchPaths = [
151
- " /Users/ \( NSUserName ( ) ) /.local/share/mise/shims/npx " ,
152
- " /opt/homebrew/bin/npx " ,
153
- " /usr/local/bin/npx "
154
- ]
155
-
156
- var foundNpx = false
157
- for path in npxSearchPaths {
158
- let expandedPath = ( path as NSString ) . expandingTildeInPath
159
- if FileManager . default. fileExists ( atPath: expandedPath) {
160
- process. executableURL = URL ( fileURLWithPath: expandedPath)
161
- process. arguments = [ " ccusage@latest " , " --json " ]
162
- foundNpx = true
163
- print ( " [DEBUG] Using npx at: \( expandedPath) " )
164
- break
165
- }
166
- }
167
-
168
- if !foundNpx {
169
- // Use shell with extended PATH
170
- process. executableURL = URL ( fileURLWithPath: " /bin/zsh " )
171
- process. arguments = [ " -l " , " -c " ,
172
- " export PATH= \" $HOME/.local/share/mise/shims:/opt/homebrew/bin:/usr/local/bin:$PATH \" && npx ccusage@latest --json " ]
173
- print ( " [DEBUG] Using shell with extended PATH for daily data " )
174
- }
175
-
176
- let pipe = Pipe ( )
177
- let errorPipe = Pipe ( )
178
- process. standardOutput = pipe
179
- process. standardError = errorPipe
180
-
181
- try process. run ( )
182
- process. waitUntilExit ( )
183
-
184
- // Check for errors
185
- let errorData = errorPipe. fileHandleForReading. readDataToEndOfFile ( )
186
- if !errorData. isEmpty {
187
- let errorString = String ( data: errorData, encoding: . utf8) ?? " Unknown error "
188
- print ( " ccusage error: \( errorString) " )
189
- }
190
-
191
- let data = pipe. fileHandleForReading. readDataToEndOfFile ( )
192
- if !data. isEmpty {
193
- let response = try JSONDecoder ( ) . decode ( CcusageResponse . self, from: data)
194
- // Get today's date in YYYY-MM-DD format
195
- let formatter = DateFormatter ( )
196
- formatter. dateFormat = " yyyy-MM-dd "
197
- let today = formatter. string ( from: Date ( ) )
198
-
199
- // Find today's usage from the array
200
- if let todayData = response. daily. first ( where: { $0. date == today } ) {
201
- usageData. todayUsage = todayData
202
- print ( " [DEBUG] Today's totalTokens: \( todayData. totalTokens) (billable: \( todayData. inputTokens + todayData. outputTokens) ) " )
203
- } else {
204
- // If no data for today, create empty data
205
- usageData. todayUsage = nil
206
- }
207
-
208
- // Store monthly total
209
- usageData. monthlyTotal = response. totals
210
- print ( " [DEBUG] Monthly totalTokens: \( response. totals. totalTokens) (billable: \( response. totals. inputTokens + response. totals. outputTokens) ) " )
211
- usageData. lastUpdated = Date ( )
212
- }
213
- } catch {
214
- self . error = ClaudeMonitorError . unknownError ( " Failed to fetch usage data: \( error. localizedDescription) " )
215
- print ( " Error details: \( error) " )
216
- }
217
-
218
137
isLoading = false
219
138
}
220
139
@@ -297,132 +216,6 @@ class UsageMonitor: ObservableObject, UsageMonitoring {
297
216
print ( " [DEBUG] Server connection failed: \( error. localizedDescription) " )
298
217
self . error = ClaudeMonitorError . networkError ( L10n . Error. serverNotRunning)
299
218
}
300
-
301
- // App Sandbox prevents direct command execution
302
- guard ProcessInfo . processInfo. environment [ " APP_SANDBOX_CONTAINER_ID " ] == nil else {
303
- print ( " [DEBUG] App Sandbox is enabled - cannot execute external commands " )
304
- return
305
- }
306
-
307
- // Fallback: Try multiple methods to run ccusage (only when App Sandbox is disabled)
308
- print ( " [DEBUG] App Sandbox is disabled - falling back to direct ccusage execution " )
309
-
310
- // Method 1: Try to find npx in common locations
311
- let npxSearchPaths = [
312
- " /Users/ \( NSUserName ( ) ) /.local/share/mise/shims/npx " ,
313
- " /opt/homebrew/bin/npx " ,
314
- " /usr/local/bin/npx " ,
315
- " /Users/ \( NSUserName ( ) ) /.nvm/default/bin/npx " ,
316
- " /Users/ \( NSUserName ( ) ) /.volta/bin/npx "
317
- ]
318
-
319
- var npxPath : String ? = nil
320
- for path in npxSearchPaths {
321
- let expandedPath = ( path as NSString ) . expandingTildeInPath
322
- if FileManager . default. fileExists ( atPath: expandedPath) {
323
- npxPath = expandedPath
324
- print ( " [DEBUG] Found npx at: \( expandedPath) " )
325
- break
326
- }
327
- }
328
-
329
- // Method 2: Use which command to find npx
330
- if npxPath == nil {
331
- let whichProcess = Process ( )
332
- whichProcess. executableURL = URL ( fileURLWithPath: " /usr/bin/which " )
333
- whichProcess. arguments = [ " npx " ]
334
- let whichPipe = Pipe ( )
335
- whichProcess. standardOutput = whichPipe
336
- whichProcess. standardError = Pipe ( )
337
-
338
- do {
339
- try whichProcess. run ( )
340
- whichProcess. waitUntilExit ( )
341
-
342
- let data = whichPipe. fileHandleForReading. readDataToEndOfFile ( )
343
- if let output = String ( data: data, encoding: . utf8) ? . trimmingCharacters ( in: . whitespacesAndNewlines) ,
344
- !output. isEmpty {
345
- npxPath = output
346
- print ( " [DEBUG] Found npx via which: \( output) " )
347
- }
348
- } catch {
349
- print ( " [DEBUG] which command failed: \( error) " )
350
- }
351
- }
352
-
353
- // Method 3: Try with full shell initialization
354
- do {
355
- let process = Process ( )
356
-
357
- if let npxPath = npxPath {
358
- // Use found npx directly
359
- process. executableURL = URL ( fileURLWithPath: npxPath)
360
- process. arguments = [ " ccusage " , " blocks " , " --active " , " --json " ]
361
- print ( " [DEBUG] Using direct npx: \( npxPath) " )
362
- } else {
363
- // Last resort: use shell with full environment
364
- process. executableURL = URL ( fileURLWithPath: " /bin/zsh " )
365
- process. arguments = [ " -l " , " -c " ,
366
- " export PATH= \" $HOME/.local/share/mise/shims:$HOME/.volta/bin:/opt/homebrew/bin:/usr/local/bin:$PATH \" && npx ccusage blocks --active --json " ]
367
- print ( " [DEBUG] Using shell with extended PATH " )
368
- }
369
-
370
- let pipe = Pipe ( )
371
- let errorPipe = Pipe ( )
372
- process. standardOutput = pipe
373
- process. standardError = errorPipe
374
-
375
- try process. run ( )
376
- process. waitUntilExit ( )
377
-
378
- // Check for errors
379
- let errorData = errorPipe. fileHandleForReading. readDataToEndOfFile ( )
380
- if !errorData. isEmpty {
381
- let errorString = String ( data: errorData, encoding: . utf8) ?? " Unknown error "
382
- print ( " [DEBUG] ccusage stderr: \( errorString) " )
383
- }
384
-
385
- let data = pipe. fileHandleForReading. readDataToEndOfFile ( )
386
- if !data. isEmpty {
387
- if let jsonString = String ( data: data, encoding: . utf8) {
388
- print ( " [DEBUG] ccusage output length: \( jsonString. count) characters " )
389
- }
390
-
391
- let blocksResponse = try JSONDecoder ( ) . decode ( BlocksResponse . self, from: data)
392
-
393
- // 過去のセッションから最大トークン使用量を検出
394
- var maxTokens = 0
395
- for block in blocksResponse. blocks {
396
- if !block. isGap && block. totalTokens > maxTokens {
397
- maxTokens = block. totalTokens
398
- }
399
- }
400
- usageData. historicalMaxTokens = maxTokens
401
-
402
- // プランタイプを自動判定して保存
403
- if maxTokens > UsageData . max5SessionTokenLimit {
404
- updateDetectedPlan ( " Max20 " )
405
- } else if maxTokens > UsageData . proSessionTokenLimit {
406
- updateDetectedPlan ( " Max5 " )
407
- } else {
408
- if usageData. detectedPlanType == nil {
409
- updateDetectedPlan ( " Pro " )
410
- }
411
- }
412
-
413
- if let activeBlock = blocksResponse. blocks. first ( where: { $0. isActive } ) {
414
- usageData. activeSession = activeBlock
415
- print ( " [DEBUG] Session data loaded via fallback " )
416
- print ( " [DEBUG] Historical max tokens: \( maxTokens) " )
417
- print ( " [DEBUG] Detected plan: \( usageData. detectedPlan) " )
418
- }
419
- } else {
420
- print ( " [DEBUG] No data from ccusage command " )
421
- }
422
- } catch {
423
- print ( " [DEBUG] All fallback methods failed: \( error) " )
424
- print ( " [DEBUG] Error details: \( error. localizedDescription) " )
425
- }
426
219
}
427
220
428
221
func formatTokens( _ tokens: Int ) -> String {
0 commit comments