package shell import ( "io" "time" ) // EventCallback is called with a copy of data whenever the channel is read or written. // direction is 0 for input (client→server) and 1 for output (server→client). type EventCallback func(ts time.Time, direction int, data []byte) // RecordingChannel wraps an io.ReadWriteCloser and optionally invokes callbacks // on every Read (input) and Write (output). type RecordingChannel struct { inner io.ReadWriteCloser callbacks []EventCallback } // NewRecordingChannel returns a RecordingChannel wrapping rw. func NewRecordingChannel(rw io.ReadWriteCloser) *RecordingChannel { return &RecordingChannel{inner: rw} } // WithCallback clears existing callbacks, sets the given one, and returns the // RecordingChannel for chaining. Kept for backward compatibility. func (r *RecordingChannel) WithCallback(cb EventCallback) *RecordingChannel { r.callbacks = []EventCallback{cb} return r } // AddCallback appends an additional event callback. func (r *RecordingChannel) AddCallback(cb EventCallback) { r.callbacks = append(r.callbacks, cb) } func (r *RecordingChannel) Read(p []byte) (int, error) { n, err := r.inner.Read(p) if n > 0 && len(r.callbacks) > 0 { ts := time.Now() cp := make([]byte, n) copy(cp, p[:n]) for _, cb := range r.callbacks { cb(ts, 0, cp) } } return n, err } func (r *RecordingChannel) Write(p []byte) (int, error) { n, err := r.inner.Write(p) if n > 0 && len(r.callbacks) > 0 { ts := time.Now() cp := make([]byte, n) copy(cp, p[:n]) for _, cb := range r.callbacks { cb(ts, 1, cp) } } return n, err } func (r *RecordingChannel) Close() error { return r.inner.Close() }