1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
|
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE NoImplicitPrelude #-}
-- : out omni-agent-log
module Omni.Agent.Log where
import Alpha
import qualified Data.Text.IO as TIO
import qualified System.Console.ANSI as ANSI
import qualified System.IO as IO
import System.IO.Unsafe (unsafePerformIO)
import Data.IORef (IORef, newIORef, readIORef, writeIORef, modifyIORef')
-- | Status of the agent for the UI
data Status = Status
{ statusWorker :: Text,
statusTask :: Maybe Text,
statusFiles :: Int,
statusCredits :: Double,
statusTime :: Text, -- formatted time string
statusActivity :: Text
}
deriving (Show, Eq)
emptyStatus :: Text -> Status
emptyStatus workerName =
Status
{ statusWorker = workerName,
statusTask = Nothing,
statusFiles = 0,
statusCredits = 0.0,
statusTime = "00:00",
statusActivity = "Idle"
}
-- | Global state for the status bar
{-# NOINLINE currentStatus #-}
currentStatus :: IORef Status
currentStatus = unsafePerformIO (newIORef (emptyStatus "Unknown"))
-- | Initialize the status bar system
init :: Text -> IO ()
init workerName = do
writeIORef currentStatus (emptyStatus workerName)
-- Reserve 2 lines at bottom
IO.hPutStrLn IO.stderr ""
IO.hPutStrLn IO.stderr ""
ANSI.hCursorUp IO.stderr 2
-- | Update the status
update :: (Status -> Status) -> IO ()
update f = do
modifyIORef' currentStatus f
render
-- | Set the activity message
updateActivity :: Text -> IO ()
updateActivity msg = update (\s -> s {statusActivity = msg})
-- | Log a scrolling message (appears above status bars)
log :: Text -> IO ()
log msg = do
-- Clear status bars
ANSI.hClearLine IO.stderr
ANSI.hCursorDown IO.stderr 1
ANSI.hClearLine IO.stderr
ANSI.hCursorUp IO.stderr 1
-- Print message (scrolls screen)
TIO.hPutStrLn IO.stderr msg
-- Re-render status bars at bottom
-- (Since we scrolled, we are now on the line above where the first status line should be)
render
-- | Render the two status lines
render :: IO ()
render = do
Status {..} <- readIORef currentStatus
-- Line 1: Meta
-- [Worker: name] Task: t-123 | Files: 3 | Credits: $0.45 | Time: 05:23
let taskStr = maybe "None" identity statusTask
meta = "[Worker: " <> statusWorker <> "] Task: " <> taskStr
<> " | Files: " <> tshow statusFiles
<> " | Credits: $" <> tshow statusCredits
<> " | Time: " <> statusTime
ANSI.hSetCursorColumn IO.stderr 0
ANSI.hClearLine IO.stderr
TIO.hPutStr IO.stderr meta
-- Line 2: Activity
-- [14:05:22] 🤖 Thinking...
ANSI.hCursorDown IO.stderr 1
ANSI.hSetCursorColumn IO.stderr 0
ANSI.hClearLine IO.stderr
TIO.hPutStr IO.stderr ("🤖 " <> statusActivity)
-- Return cursor to line 1
ANSI.hCursorUp IO.stderr 1
IO.hFlush IO.stderr
|