1
0
forked from wrenn/wrenn

Add terminal tab to capsule detail page and fix envd process lookup bugs

- Add multi-session Terminal tab with xterm.js (session tabs, close, reconnect)
- Keep terminal mounted across tab switches to preserve sessions
- Persist active tab in URL (?tab=terminal) so refresh stays on terminal
- Buffer keystrokes (50ms) to reduce per-character RPC overhead
- Add WebSocket auth via ?token= query param for browser WS connections
- Enable ws:true in Vite dev proxy for WebSocket support

envd fixes (pre-existing bugs exposed by multi-session terminals):
- Fix getProcess tag Range: inverted return values caused early stop when
  multiple tagged processes existed, making SendInput fail with "not found"
- Fix multiplexer deadlock: blocking send to cancelled fork's unbuffered
  channel prevented process cleanup. Now uses buffered channels (cap 64)
  with non-blocking fallback
This commit is contained in:
2026-04-11 04:27:16 +06:00
parent ab3fc4a807
commit 4b2ff279f7
8 changed files with 635 additions and 14 deletions

View File

@ -25,7 +25,11 @@ func NewMultiplexedChannel[T any](buffer int) *MultiplexedChannel[T] {
c.mu.RLock()
for _, cons := range c.channels {
cons <- v
select {
case cons <- v:
default:
// Consumer not reading — skip to prevent deadlock
}
}
c.mu.RUnlock()
@ -52,7 +56,7 @@ func (m *MultiplexedChannel[T]) Fork() (chan T, func()) {
m.mu.Lock()
defer m.mu.Unlock()
consumer := make(chan T)
consumer := make(chan T, 64)
m.channels = append(m.channels, consumer)

View File

@ -62,16 +62,15 @@ func (s *Service) getProcess(selector *rpc.ProcessSelector) (*handler.Handler, e
s.processes.Range(func(_ uint32, value *handler.Handler) bool {
if value.Tag == nil {
return true
return true // no tag, keep looking
}
if *value.Tag == tag {
proc = value
return true
return false // found, stop iterating
}
return false
return true // different tag, keep looking
})
if proc == nil {