import CodexBarCoreimport Foundationimport Testing@testable import CodexBar @Suitestruct StatusProbeTests { @Test func parseCodexStatus() throws { let sample = """ Model: gpt Credits: 980 credits 5h limit: [#####] 75% left Weekly limit: [##] 25% left """ let snap = try CodexStatusProbe.parse(text: sample) #expect(snap.credits == 980) #expect(snap.fiveHourPercentLeft == 75) #expect(snap.weeklyPercentLeft == 25) } @Test func parseCodexStatusWithAnsiAndResets() throws { let sample = """ \u{001B}[38;5;245mCredits:\u{001B}[0m 557 credits 5h limit: [█████ ] 50% left (resets 09:01) Weekly limit: [███████ ] 85% left (resets 04:01 on 27 Nov) """ let snap = try CodexStatusProbe.parse(text: sample) #expect(snap.credits == 557) #expect(snap.fiveHourPercentLeft == 50) #expect(snap.weeklyPercentLeft == 85) } @Test func parseClaudeStatus() throws { let sample = """ Settings: Status Config Usage (tab to cycle) Current session 1% used (Resets 5am (Europe/Vienna)) Current week (all models) 1% used (Resets Dec 2 at 12am (Europe/Vienna)) Current week (Sonnet only) 1% used (Resets Dec 2 at 12am (Europe/Vienna)) Nov 24, 2025 update: We've increased your limits and removed the Opus cap, so you can use Opus 4.5 up to your overall limit. Sonnet now has its own limit—it's set to match your previous overall limit, so you can use just as much as before. Account: user@example.com Org: Example Org """ let snap = try ClaudeStatusProbe.parse(text: sample) #expect(snap.sessionPercentLeft == 99) #expect(snap.weeklyPercentLeft == 99) #expect(snap.opusPercentLeft == 99) #expect(snap.accountEmail == "user@example.com") #expect(snap.accountOrganization == "Example Org") #expect(snap.primaryResetDescription == "Resets 5am (Europe/Vienna)") #expect(snap.secondaryResetDescription == "Resets Dec 2 at 12am (Europe/Vienna)") #expect(snap.opusResetDescription == "Resets Dec 2 at 12am (Europe/Vienna)") } @Test func parseClaudeStatusWithANSI() throws { let sample = """ \u{001B}[35mCurrent session\u{001B}[0m 40% used (Resets 11am) Current week (all models) 10% used (Resets Nov 27) Current week (Sonnet only) 0% used (Resets Nov 27) Account: user@example.com Org: ACME \u{001B}[0m """ let snap = try ClaudeStatusProbe.parse(text: sample) #expect(snap.sessionPercentLeft == 60) #expect(snap.weeklyPercentLeft == 90) #expect(snap.opusPercentLeft == 100) #expect(snap.primaryResetDescription == "Resets 11am") #expect(snap.secondaryResetDescription == "Resets Nov 27") #expect(snap.opusResetDescription == "Resets Nov 27") } @Test func parseClaudeStatusLegacyOpusLabel() throws { let sample = """ Current session 12% used (Resets 11am) Current week (all models) 55% used (Resets Nov 21) Current week (Opus) 5% used (Resets Nov 21) Account: user@example.com Org: Example Org """ let snap = try ClaudeStatusProbe.parse(text: sample) #expect(snap.sessionPercentLeft == 88) #expect(snap.weeklyPercentLeft == 45) #expect(snap.opusPercentLeft == 95) #expect(snap.primaryResetDescription == "Resets 11am") #expect(snap.secondaryResetDescription == "Resets Nov 21") #expect(snap.opusResetDescription == "Resets Nov 21") } @Test func parseClaudeStatusEnterpriseSessionOnly() throws { let sample = """ Current session █ 2% used Resets 3pm (Europe/Vienna) """ let snap = try ClaudeStatusProbe.parse(text: sample) #expect(snap.sessionPercentLeft == 98) #expect(snap.weeklyPercentLeft == nil) #expect(snap.primaryResetDescription == "Resets 3pm (Europe/Vienna)") #expect(snap.secondaryResetDescription == nil) } @Test func parseClaudeStatusResetMappings_withCRLineEndings() throws { let sample = "Current session\r" + "██████████████████████████████████████████████████ 17% used\r" + "Resets 12:59pm (Europe/Paris)\r" + "Current week (all models)\r" + "██████████████████████████████████████████████████ 4% used\r" + "Resets Dec 24 at 3:59pm (Europe/Paris)\r" + "Current week (Sonnet only)\r" + "██████████████████████████████████████████████████ 3% used\r" + "Resets Dec 23 at 3:59am (Europe/Paris)\r" let snap = try ClaudeStatusProbe.parse(text: sample) #expect(snap.sessionPercentLeft == 83) #expect(snap.weeklyPercentLeft == 96) #expect(snap.opusPercentLeft == 97) #expect(snap.primaryResetDescription == "Resets 12:59pm (Europe/Paris)") #expect(snap.secondaryResetDescription == "Resets Dec 24 at 3:59pm (Europe/Paris)") #expect(snap.opusResetDescription == "Resets Dec 23 at 3:59am (Europe/Paris)") } @Test func parseClaudeStatusResetMappings_doesNotPromoteWeeklyResetToSession() throws { let sample = """ Current session ██████████████████████████████████████████████████ 17% used Current week (all models) ██████████████████████████████████████████████████ 4% used Resets Dec 24 at 3:59pm (Europe/Paris) """ let snap = try ClaudeStatusProbe.parse(text: sample) #expect(snap.sessionPercentLeft == 83) #expect(snap.weeklyPercentLeft == 96) #expect(snap.primaryResetDescription == nil) #expect(snap.secondaryResetDescription == "Resets Dec 24 at 3:59pm (Europe/Paris)") } @Test func parseClaudeStatusWithPlanAndAnsiNoise() throws { let sample = """ Settings: Status Config Usage Login method: \u{001B}[22mClaude Max Account\u{001B}[0m Account: user@example.com Org: ACME """ // Only care about login/identity; include minimal usage lines to satisfy parser. let text = """ Current session 10% used Current week (all models) 20% used Current week (Opus) 30% used \(sample) """ let snap = try ClaudeStatusProbe.parse(text: text) #expect(snap.loginMethod == "Max") #expect(snap.accountEmail == "user@example.com") #expect(snap.accountOrganization == "ACME") } @Test func parseClaudeStatusWithBracketPlanNoiseNoEsc() throws { let sample = """ Login method: [22m Claude Max Account Account: user@example.com """ let text = """ Current session 10% used Current week (all models) 20% used Current week (Opus) 30% used \(sample) """ let snap = try ClaudeStatusProbe.parse(text: text) #expect(snap.loginMethod == "Max") } @Test func surfacesClaudeTokenExpired() { let sample = """ Settings: Status Config Usage Error: Failed to load usage data: {"type":"error","error":{"type":"authentication_error", "message":"OAuth token has expired. Please obtain a new token or refresh your existing token.", "details":{"error_visibility":"user_facing","error_code":"token_expired"}},\ "request_id":"req_123"} """ do { _ = try ClaudeStatusProbe.parse(text: sample) #expect(Bool(false), "Parsing should fail for auth error") } catch let ClaudeStatusProbeError.parseFailed(message) { let lower = message.lowercased() #expect(lower.contains("token")) #expect(lower.contains("login")) } catch { #expect(Bool(false), "Unexpected error: \(error)") } } @Test func surfacesClaudeFolderTrustPrompt() { let sample = """ Do you trust the files in this folder? /Users/example/project """ do { _ = try ClaudeStatusProbe.parse(text: sample) #expect(Bool(false), "Parsing should fail for folder trust prompt") } catch let ClaudeStatusProbeError.parseFailed(message) { #expect(message.lowercased().contains("trust")) #expect(message.contains("/Users/example/project")) #expect(message.contains("cd \"/Users/example/project\" && claude")) } catch { #expect(Bool(false), "Unexpected error: \(error)") } } @Test func surfacesClaudeFolderTrustPrompt_withCRLFAndSpaces() { let sample = "Do you trust the files in this folder?\r\n\r\n/Users/example/My Project\r\n" do { _ = try ClaudeStatusProbe.parse(text: sample) #expect(Bool(false), "Parsing should fail for folder trust prompt") } catch let ClaudeStatusProbeError.parseFailed(message) { #expect(message.contains("/Users/example/My Project")) #expect(message.contains("cd \"/Users/example/My Project\" && claude")) } catch { #expect(Bool(false), "Unexpected error: \(error)") } } @Test func surfacesClaudeFolderTrustPrompt_withoutFolderPath() { let sample = """ Do you trust the files in this folder? """ do { _ = try ClaudeStatusProbe.parse(text: sample) #expect(Bool(false), "Parsing should fail for folder trust prompt") } catch let ClaudeStatusProbeError.parseFailed(message) { let lower = message.lowercased() #expect(lower.contains("trust")) #expect(lower.contains("auto-accept")) } catch { #expect(Bool(false), "Unexpected error: \(error)") } } @Test func parsesClaudeResetTimeOnly() { let now = Date(timeIntervalSince1970: 1_733_690_000) let parsed = ClaudeStatusProbe.parseResetDate(from: "Resets 12:59pm (Europe/Helsinki)", now: now) let tz = TimeZone(identifier: "Europe/Helsinki")! var calendar = Calendar(identifier: .gregorian) calendar.timeZone = tz var expected = calendar.date(bySettingHour: 12, minute: 59, second: 0, of: now)! if expected < now { expected = calendar.date(byAdding: .day, value: 1, to: expected)! } #expect(parsed == expected) } @Test func parsesClaudeResetDateAndTime() { let now = Date(timeIntervalSince1970: 1_733_690_000) let parsed = ClaudeStatusProbe.parseResetDate(from: "Resets Dec 9, 8:59am (Europe/Helsinki)", now: now) var calendar = Calendar(identifier: .gregorian) calendar.timeZone = TimeZone(identifier: "Europe/Helsinki")! let expected = calendar.date(from: DateComponents( year: calendar.component(.year, from: now), month: 12, day: 9, hour: 8, minute: 59, second: 0)) #expect(parsed == expected) } @Test func parsesClaudeResetWithDotSeparatedTime() { let now = Date(timeIntervalSince1970: 1_733_690_000) let parsed = ClaudeStatusProbe.parseResetDate(from: "Resets Dec 9 at 5.27am (UTC)", now: now) var calendar = Calendar(identifier: .gregorian) calendar.timeZone = TimeZone(identifier: "UTC")! let expected = calendar.date(from: DateComponents(year: 2024, month: 12, day: 9, hour: 5, minute: 27)) #expect(parsed == expected) } @Test func parsesClaudeResetWithCompactTimes() { let now = Date(timeIntervalSince1970: 1_733_690_000) let parsedTimeOnly = ClaudeStatusProbe.parseResetDate(from: "Resets 1pm (UTC)", now: now) var calendar = Calendar(identifier: .gregorian) calendar.timeZone = TimeZone(identifier: "UTC")! var expected = calendar.date(bySettingHour: 13, minute: 0, second: 0, of: now)! if expected < now { expected = calendar.date(byAdding: .day, value: 1, to: expected)! } #expect(parsedTimeOnly == expected) let parsedDateTime = ClaudeStatusProbe.parseResetDate(from: "Resets Dec 9, 9am", now: now) calendar.timeZone = TimeZone.current let dateExpected = calendar.date(from: DateComponents( year: calendar.component(.year, from: now), month: 12, day: 9, hour: 9, minute: 0, second: 0)) #expect(parsedDateTime == dateExpected) } @Test func liveCodexStatus() async throws { guard ProcessInfo.processInfo.environment["LIVE_CODEX_STATUS"] == "1" else { return } let probe = CodexStatusProbe() do { let snap = try await probe.fetch() let summary = """ Live Codex status: \(snap.rawText) values: 5h \(snap.fiveHourPercentLeft ?? -1)% left, weekly \(snap.weeklyPercentLeft ?? -1)% left, credits \(snap.credits ?? -1) """ print(summary) } catch { // Dump raw PTY text to help debug. let runner = TTYCommandRunner() let res = try runner.run( binary: "codex", send: "/status\n", options: .init(rows: 60, cols: 200, timeout: 12)) print("RAW CODEX PTY OUTPUT BEGIN\n\(res.text)\nRAW CODEX PTY OUTPUT END") let clean = TextParsing.stripANSICodes(res.text) print("CLEAN CODEX OUTPUT BEGIN\n\(clean)\nCLEAN CODEX OUTPUT END") let five = TextParsing.firstInt(pattern: #"5h limit[^\n]*?([0-9]{1,3})%\s+left"#, text: clean) ?? -1 let week = TextParsing.firstInt(pattern: #"Weekly limit[^\n]*?([0-9]{1,3})%\s+left"#, text: clean) ?? -1 let credits = TextParsing.firstNumber(pattern: #"Credits:\s*([0-9][0-9.,]*)"#, text: clean) ?? -1 print("Parsed probes => 5h \(five)% weekly \(week)% credits \(credits)") throw error } }}
import CodexBarCore
import Foundation
import Testing
@testable import CodexBar
@Suite
struct StatusProbeTests {
@Test
func parseCodexStatus() throws {
let sample = """
Model: gpt
Credits: 980 credits
5h limit: [#####] 75% left
Weekly limit: [##] 25% left
"""
let snap = try CodexStatusProbe.parse(text: sample)
#expect(snap.credits == 980)
#expect(snap.fiveHourPercentLeft == 75)
#expect(snap.weeklyPercentLeft == 25)
}
@Test
func parseCodexStatusWithAnsiAndResets() throws {
let sample = """
\u{001B}[38;5;245mCredits:\u{001B}[0m 557 credits
5h limit: [█████ ] 50% left (resets 09:01)
Weekly limit: [███████ ] 85% left (resets 04:01 on 27 Nov)
"""
let snap = try CodexStatusProbe.parse(text: sample)
#expect(snap.credits == 557)
#expect(snap.fiveHourPercentLeft == 50)
#expect(snap.weeklyPercentLeft == 85)
}
@Test
func parseClaudeStatus() throws {
let sample = """
Settings: Status Config Usage (tab to cycle)
Current session
1% used (Resets 5am (Europe/Vienna))
Current week (all models)
1% used (Resets Dec 2 at 12am (Europe/Vienna))
Current week (Sonnet only)
1% used (Resets Dec 2 at 12am (Europe/Vienna))
Nov 24, 2025 update:
We've increased your limits and removed the Opus cap,
so you can use Opus 4.5 up to your overall limit.
Sonnet now has its own limit—it's set to match your previous overall limit,
so you can use just as much as before.
Account: user@example.com
Org: Example Org
"""
let snap = try ClaudeStatusProbe.parse(text: sample)
#expect(snap.sessionPercentLeft == 99)
#expect(snap.weeklyPercentLeft == 99)
#expect(snap.opusPercentLeft == 99)
#expect(snap.accountEmail == "user@example.com")
#expect(snap.accountOrganization == "Example Org")
#expect(snap.primaryResetDescription == "Resets 5am (Europe/Vienna)")
#expect(snap.secondaryResetDescription == "Resets Dec 2 at 12am (Europe/Vienna)")
#expect(snap.opusResetDescription == "Resets Dec 2 at 12am (Europe/Vienna)")
}
@Test
func parseClaudeStatusWithANSI() throws {
let sample = """
\u{001B}[35mCurrent session\u{001B}[0m
40% used (Resets 11am)
Current week (all models)
10% used (Resets Nov 27)
Current week (Sonnet only)
0% used (Resets Nov 27)
Account: user@example.com
Org: ACME
\u{001B}[0m
"""
let snap = try ClaudeStatusProbe.parse(text: sample)
#expect(snap.sessionPercentLeft == 60)
#expect(snap.weeklyPercentLeft == 90)
#expect(snap.opusPercentLeft == 100)
#expect(snap.primaryResetDescription == "Resets 11am")
#expect(snap.secondaryResetDescription == "Resets Nov 27")
#expect(snap.opusResetDescription == "Resets Nov 27")
}
@Test
func parseClaudeStatusLegacyOpusLabel() throws {
let sample = """
Current session
12% used (Resets 11am)
Current week (all models)
55% used (Resets Nov 21)
Current week (Opus)
5% used (Resets Nov 21)
Account: user@example.com
Org: Example Org
"""
let snap = try ClaudeStatusProbe.parse(text: sample)
#expect(snap.sessionPercentLeft == 88)
#expect(snap.weeklyPercentLeft == 45)
#expect(snap.opusPercentLeft == 95)
#expect(snap.primaryResetDescription == "Resets 11am")
#expect(snap.secondaryResetDescription == "Resets Nov 21")
#expect(snap.opusResetDescription == "Resets Nov 21")
}
@Test
func parseClaudeStatusEnterpriseSessionOnly() throws {
let sample = """
Current session
█ 2% used
Resets 3pm (Europe/Vienna)
"""
let snap = try ClaudeStatusProbe.parse(text: sample)
#expect(snap.sessionPercentLeft == 98)
#expect(snap.weeklyPercentLeft == nil)
#expect(snap.primaryResetDescription == "Resets 3pm (Europe/Vienna)")
#expect(snap.secondaryResetDescription == nil)
}
@Test
func parseClaudeStatusResetMappings_withCRLineEndings() throws {
let sample =
"Current session\r" +
"██████████████████████████████████████████████████ 17% used\r" +
"Resets 12:59pm (Europe/Paris)\r" +
"Current week (all models)\r" +
"██████████████████████████████████████████████████ 4% used\r" +
"Resets Dec 24 at 3:59pm (Europe/Paris)\r" +
"Current week (Sonnet only)\r" +
"██████████████████████████████████████████████████ 3% used\r" +
"Resets Dec 23 at 3:59am (Europe/Paris)\r"
let snap = try ClaudeStatusProbe.parse(text: sample)
#expect(snap.sessionPercentLeft == 83)
#expect(snap.weeklyPercentLeft == 96)
#expect(snap.opusPercentLeft == 97)
#expect(snap.primaryResetDescription == "Resets 12:59pm (Europe/Paris)")
#expect(snap.secondaryResetDescription == "Resets Dec 24 at 3:59pm (Europe/Paris)")
#expect(snap.opusResetDescription == "Resets Dec 23 at 3:59am (Europe/Paris)")
}
@Test
func parseClaudeStatusResetMappings_doesNotPromoteWeeklyResetToSession() throws {
let sample = """
Current session
██████████████████████████████████████████████████ 17% used
Current week (all models)
██████████████████████████████████████████████████ 4% used
Resets Dec 24 at 3:59pm (Europe/Paris)
"""
let snap = try ClaudeStatusProbe.parse(text: sample)
#expect(snap.sessionPercentLeft == 83)
#expect(snap.weeklyPercentLeft == 96)
#expect(snap.primaryResetDescription == nil)
#expect(snap.secondaryResetDescription == "Resets Dec 24 at 3:59pm (Europe/Paris)")
}
@Test
func parseClaudeStatusWithPlanAndAnsiNoise() throws {
let sample = """
Settings: Status Config Usage
Login method: \u{001B}[22mClaude Max Account\u{001B}[0m
Account: user@example.com
Org: ACME
"""
// Only care about login/identity; include minimal usage lines to satisfy parser.
let text = """
Current session
10% used
Current week (all models)
20% used
Current week (Opus)
30% used
\(sample)
"""
let snap = try ClaudeStatusProbe.parse(text: text)
#expect(snap.loginMethod == "Max")
#expect(snap.accountEmail == "user@example.com")
#expect(snap.accountOrganization == "ACME")
}
@Test
func parseClaudeStatusWithBracketPlanNoiseNoEsc() throws {
let sample = """
Login method: [22m Claude Max Account
Account: user@example.com
"""
let text = """
Current session
10% used
Current week (all models)
20% used
Current week (Opus)
30% used
\(sample)
"""
let snap = try ClaudeStatusProbe.parse(text: text)
#expect(snap.loginMethod == "Max")
}
@Test
func surfacesClaudeTokenExpired() {
let sample = """
Settings: Status Config Usage
Error: Failed to load usage data: {"type":"error","error":{"type":"authentication_error",
"message":"OAuth token has expired. Please obtain a new token or refresh your existing token.",
"details":{"error_visibility":"user_facing","error_code":"token_expired"}},\
"request_id":"req_123"}
"""
do {
_ = try ClaudeStatusProbe.parse(text: sample)
#expect(Bool(false), "Parsing should fail for auth error")
} catch let ClaudeStatusProbeError.parseFailed(message) {
let lower = message.lowercased()
#expect(lower.contains("token"))
#expect(lower.contains("login"))
} catch {
#expect(Bool(false), "Unexpected error: \(error)")
}
}
@Test
func surfacesClaudeFolderTrustPrompt() {
let sample = """
Do you trust the files in this folder?
/Users/example/project
"""
do {
_ = try ClaudeStatusProbe.parse(text: sample)
#expect(Bool(false), "Parsing should fail for folder trust prompt")
} catch let ClaudeStatusProbeError.parseFailed(message) {
#expect(message.lowercased().contains("trust"))
#expect(message.contains("/Users/example/project"))
#expect(message.contains("cd \"/Users/example/project\" && claude"))
} catch {
#expect(Bool(false), "Unexpected error: \(error)")
}
}
@Test
func surfacesClaudeFolderTrustPrompt_withCRLFAndSpaces() {
let sample = "Do you trust the files in this folder?\r\n\r\n/Users/example/My Project\r\n"
do {
_ = try ClaudeStatusProbe.parse(text: sample)
#expect(Bool(false), "Parsing should fail for folder trust prompt")
} catch let ClaudeStatusProbeError.parseFailed(message) {
#expect(message.contains("/Users/example/My Project"))
#expect(message.contains("cd \"/Users/example/My Project\" && claude"))
} catch {
#expect(Bool(false), "Unexpected error: \(error)")
}
}
@Test
func surfacesClaudeFolderTrustPrompt_withoutFolderPath() {
let sample = """
Do you trust the files in this folder?
"""
do {
_ = try ClaudeStatusProbe.parse(text: sample)
#expect(Bool(false), "Parsing should fail for folder trust prompt")
} catch let ClaudeStatusProbeError.parseFailed(message) {
let lower = message.lowercased()
#expect(lower.contains("trust"))
#expect(lower.contains("auto-accept"))
} catch {
#expect(Bool(false), "Unexpected error: \(error)")
}
}
@Test
func parsesClaudeResetTimeOnly() {
let now = Date(timeIntervalSince1970: 1_733_690_000)
let parsed = ClaudeStatusProbe.parseResetDate(from: "Resets 12:59pm (Europe/Helsinki)", now: now)
let tz = TimeZone(identifier: "Europe/Helsinki")!
var calendar = Calendar(identifier: .gregorian)
calendar.timeZone = tz
var expected = calendar.date(bySettingHour: 12, minute: 59, second: 0, of: now)!
if expected < now {
expected = calendar.date(byAdding: .day, value: 1, to: expected)!
}
#expect(parsed == expected)
}
@Test
func parsesClaudeResetDateAndTime() {
let now = Date(timeIntervalSince1970: 1_733_690_000)
let parsed = ClaudeStatusProbe.parseResetDate(from: "Resets Dec 9, 8:59am (Europe/Helsinki)", now: now)
var calendar = Calendar(identifier: .gregorian)
calendar.timeZone = TimeZone(identifier: "Europe/Helsinki")!
let expected = calendar.date(from: DateComponents(
year: calendar.component(.year, from: now),
month: 12,
day: 9,
hour: 8,
minute: 59,
second: 0))
#expect(parsed == expected)
}
@Test
func parsesClaudeResetWithDotSeparatedTime() {
let now = Date(timeIntervalSince1970: 1_733_690_000)
let parsed = ClaudeStatusProbe.parseResetDate(from: "Resets Dec 9 at 5.27am (UTC)", now: now)
var calendar = Calendar(identifier: .gregorian)
calendar.timeZone = TimeZone(identifier: "UTC")!
let expected = calendar.date(from: DateComponents(year: 2024, month: 12, day: 9, hour: 5, minute: 27))
#expect(parsed == expected)
}
@Test
func parsesClaudeResetWithCompactTimes() {
let now = Date(timeIntervalSince1970: 1_733_690_000)
let parsedTimeOnly = ClaudeStatusProbe.parseResetDate(from: "Resets 1pm (UTC)", now: now)
var calendar = Calendar(identifier: .gregorian)
calendar.timeZone = TimeZone(identifier: "UTC")!
var expected = calendar.date(bySettingHour: 13, minute: 0, second: 0, of: now)!
if expected < now {
expected = calendar.date(byAdding: .day, value: 1, to: expected)!
}
#expect(parsedTimeOnly == expected)
let parsedDateTime = ClaudeStatusProbe.parseResetDate(from: "Resets Dec 9, 9am", now: now)
calendar.timeZone = TimeZone.current
let dateExpected = calendar.date(from: DateComponents(
year: calendar.component(.year, from: now),
month: 12,
day: 9,
hour: 9,
minute: 0,
second: 0))
#expect(parsedDateTime == dateExpected)
}
@Test
func liveCodexStatus() async throws {
guard ProcessInfo.processInfo.environment["LIVE_CODEX_STATUS"] == "1" else { return }
let probe = CodexStatusProbe()
do {
let snap = try await probe.fetch()
let summary = """
Live Codex status:
\(snap.rawText)
values: 5h \(snap.fiveHourPercentLeft ?? -1)% left,
weekly \(snap.weeklyPercentLeft ?? -1)% left,
credits \(snap.credits ?? -1)
"""
print(summary)
} catch {
// Dump raw PTY text to help debug.
let runner = TTYCommandRunner()
let res = try runner.run(
binary: "codex",
send: "/status\n",
options: .init(rows: 60, cols: 200, timeout: 12))
print("RAW CODEX PTY OUTPUT BEGIN\n\(res.text)\nRAW CODEX PTY OUTPUT END")
let clean = TextParsing.stripANSICodes(res.text)
print("CLEAN CODEX OUTPUT BEGIN\n\(clean)\nCLEAN CODEX OUTPUT END")
let five = TextParsing.firstInt(pattern: #"5h limit[^\n]*?([0-9]{1,3})%\s+left"#, text: clean) ?? -1
let week = TextParsing.firstInt(pattern: #"Weekly limit[^\n]*?([0-9]{1,3})%\s+left"#, text: clean) ?? -1
let credits = TextParsing.firstNumber(pattern: #"Credits:\s*([0-9][0-9.,]*)"#, text: clean) ?? -1
print("Parsed probes => 5h \(five)% weekly \(week)% credits \(credits)")
throw error
}
}
}