Add Log command to Acme interface

Opens streaming log window that displays session chat history.
Window is named /AnviLLM/Log.{session-id-or-alias} and streams
log data continuously, marking window as clean after each write.
This commit is contained in:
Levi Neely 2026-02-15 12:09:14 +01:00
parent ab5f9705cd
commit b47f6ba97d
3 changed files with 68 additions and 2 deletions

1
.gitignore vendored
View File

@ -7,3 +7,4 @@ anvilweb
.claude
.kiro
*.elc
Assist

View File

@ -205,7 +205,7 @@ Type `Assist` in Acme and middle-click to open the `/AnviLLM/` session manager.
### Commands
**Main window** (`/AnviLLM/`) tag: `Get Attach Stop Restart Kill Alias Context Daemon Sandbox`
**Main window** (`/AnviLLM/`) tag: `Get Attach Stop Restart Kill Alias Context Log Daemon Sandbox`
| Command | Description |
|---------|-------------|
@ -217,6 +217,7 @@ Type `Assist` in Acme and middle-click to open the `/AnviLLM/` session manager.
| `Attach <id>` | Open tmux terminal |
| `Alias <id> <name>` | Name a session |
| `Context <id>` | Edit context (prepended to prompts) |
| `Log <id>` | View session log |
| `Daemon` | Manage anvilsrv daemon (start/stop/status) |
| `Sandbox` | Configure sandbox settings |

View File

@ -92,7 +92,7 @@ func main() {
defer w.CloseFiles()
w.Name(windowName)
w.Write("tag", []byte("Get Attach Stop Restart Kill Alias Context Daemon Sandbox "))
w.Write("tag", []byte("Get Attach Stop Restart Kill Alias Context Log Daemon Sandbox "))
refreshList(w)
w.Ctl("clean")
@ -221,6 +221,19 @@ func main() {
if err := openContextWindow(sess); err != nil {
fmt.Fprintf(os.Stderr, "Error opening context window: %v\n", err)
}
case "Log":
if arg == "" {
fmt.Fprintf(os.Stderr, "Usage: Log <session-id>\n")
continue
}
sess, err := getSession(arg)
if err != nil {
fmt.Fprintf(os.Stderr, "Session not found: %s\n", arg)
continue
}
if err := openLogWindow(sess); err != nil {
fmt.Fprintf(os.Stderr, "Error opening log window: %v\n", err)
}
case "Sandbox":
if err := openSandboxWindow(); err != nil {
fmt.Fprintf(os.Stderr, "Error opening sandbox window: %v\n", err)
@ -592,6 +605,57 @@ func openContextWindow(sess *SessionInfo) error {
return nil
}
func openLogWindow(sess *SessionInfo) error {
go func() {
displayName := sess.Alias
if displayName == "" {
displayName = sess.ID
}
w, err := acme.New()
if err != nil {
fmt.Fprintf(os.Stderr, "Error creating log window: %v\n", err)
return
}
defer w.CloseFiles()
w.Name(fmt.Sprintf("/AnviLLM/Log.%s", displayName))
w.Ctl("clean")
// Stream log using external command
logPath := fmt.Sprintf("agent/%s/log", sess.ID)
cmd := exec.Command("9p", "read", logPath)
stdout, err := cmd.StdoutPipe()
if err != nil {
fmt.Fprintf(os.Stderr, "Error creating pipe: %v\n", err)
return
}
if err := cmd.Start(); err != nil {
fmt.Fprintf(os.Stderr, "Error starting 9p read: %v\n", err)
return
}
// Stream output to window
buf := make([]byte, 8192)
for {
n, err := stdout.Read(buf)
if n > 0 {
w.Addr("$")
w.Write("data", buf[:n])
w.Ctl("clean")
}
if err != nil {
break
}
}
cmd.Wait()
}()
return nil
}
func handleContextWindow(w *acme.Win, sess *SessionInfo) {
defer w.CloseFiles()