Move signal handling from internal/tui to pkg/core
WatchSignals and CtrlCExitWindow are generic — any frontend can use them. Removing them from the TUI package keeps tui focused on terminal I/O only. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
c5b16e739e
commit
dffce36dd4
|
|
@ -1,40 +0,0 @@
|
||||||
package tui
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"os"
|
|
||||||
"os/signal"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"ollie/pkg/core"
|
|
||||||
)
|
|
||||||
|
|
||||||
const ctrlCExitWindow = 750 * time.Millisecond
|
|
||||||
|
|
||||||
func startSignalWatcher(appCancel context.CancelCauseFunc, c core.Core, errStream io.Writer) {
|
|
||||||
ch := make(chan os.Signal, 16)
|
|
||||||
signals := []os.Signal{os.Interrupt}
|
|
||||||
if haveSIGTERM {
|
|
||||||
signals = append(signals, SIGTERM)
|
|
||||||
}
|
|
||||||
signal.Notify(ch, signals...)
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
for sig := range ch {
|
|
||||||
switch sig {
|
|
||||||
case SIGTERM:
|
|
||||||
signal.Stop(ch)
|
|
||||||
close(ch)
|
|
||||||
fmt.Fprint(errStream, "\n(terminated)\n")
|
|
||||||
appCancel(context.Canceled)
|
|
||||||
continue
|
|
||||||
case os.Interrupt:
|
|
||||||
if c.Interrupt(core.ErrInterrupted) {
|
|
||||||
fmt.Fprint(errStream, "\n^C\n")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
|
|
@ -80,7 +80,7 @@ func (t *TUI) Run(ctx context.Context) {
|
||||||
t.split = newSplitInput(tt, tt.Output(), t.core.Prompt(), nil)
|
t.split = newSplitInput(tt, tt.Output(), t.core.Prompt(), nil)
|
||||||
|
|
||||||
appCtx, appCancel := context.WithCancelCause(ctx)
|
appCtx, appCancel := context.WithCancelCause(ctx)
|
||||||
startSignalWatcher(appCancel, t.core, os.Stderr)
|
core.WatchSignals(appCancel, t.core, os.Stderr)
|
||||||
|
|
||||||
var lastCtrlC time.Time
|
var lastCtrlC time.Time
|
||||||
firstRead := true
|
firstRead := true
|
||||||
|
|
@ -101,7 +101,7 @@ func (t *TUI) Run(ctx context.Context) {
|
||||||
errs := err.Error()
|
errs := err.Error()
|
||||||
if errs == "interrupted" || errs == "^C" {
|
if errs == "interrupted" || errs == "^C" {
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
if !lastCtrlC.IsZero() && now.Sub(lastCtrlC) <= ctrlCExitWindow {
|
if !lastCtrlC.IsZero() && now.Sub(lastCtrlC) <= core.CtrlCExitWindow {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
lastCtrlC = now
|
lastCtrlC = now
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,41 @@
|
||||||
|
package core
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"os/signal"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const CtrlCExitWindow = 750 * time.Millisecond
|
||||||
|
|
||||||
|
// WatchSignals installs OS signal handlers for the lifetime of the program.
|
||||||
|
// SIGTERM cancels the app context; SIGINT interrupts the current agent turn.
|
||||||
|
// It is safe to call from any frontend (TUI, HTTP, CLI one-shot, etc.).
|
||||||
|
func WatchSignals(appCancel context.CancelCauseFunc, c Core, errStream io.Writer) {
|
||||||
|
ch := make(chan os.Signal, 16)
|
||||||
|
signals := []os.Signal{os.Interrupt}
|
||||||
|
if haveSIGTERM {
|
||||||
|
signals = append(signals, sigTERM)
|
||||||
|
}
|
||||||
|
signal.Notify(ch, signals...)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
for sig := range ch {
|
||||||
|
switch sig {
|
||||||
|
case sigTERM:
|
||||||
|
signal.Stop(ch)
|
||||||
|
close(ch)
|
||||||
|
fmt.Fprint(errStream, "\n(terminated)\n")
|
||||||
|
appCancel(context.Canceled)
|
||||||
|
continue
|
||||||
|
case os.Interrupt:
|
||||||
|
if c.Interrupt(ErrInterrupted) {
|
||||||
|
fmt.Fprint(errStream, "\n^C\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
//go:build !windows
|
//go:build !windows
|
||||||
|
|
||||||
package tui
|
package core
|
||||||
|
|
||||||
import "syscall"
|
import "syscall"
|
||||||
|
|
||||||
const (
|
const (
|
||||||
haveSIGTERM = true
|
haveSIGTERM = true
|
||||||
SIGTERM = syscall.SIGTERM
|
sigTERM = syscall.SIGTERM
|
||||||
)
|
)
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
//go:build windows
|
//go:build windows
|
||||||
|
|
||||||
package tui
|
package core
|
||||||
|
|
||||||
import "os"
|
import "os"
|
||||||
|
|
||||||
const haveSIGTERM = false
|
const haveSIGTERM = false
|
||||||
|
|
||||||
var SIGTERM = os.Signal(nil)
|
var sigTERM = os.Signal(nil)
|
||||||
Reference in New Issue