feat: add Smart Fridge shell and per-credential shell routing
Implement Samsung FridgeOS-themed shell (PLAN.md §3.3) with inventory management, temperature controls, diagnostics, alerts, and other appliance commands. Add per-credential shell routing so static credentials can specify which shell to use via the `shell` config field, passed through ssh.Permissions.Extensions. Also extract shared ReadLine helper from bash to the shell package so both shells can reuse terminal input handling. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -55,7 +55,7 @@ func (b *BashShell) Handle(ctx context.Context, sess *shell.SessionContext, rw i
|
||||
return nil
|
||||
}
|
||||
|
||||
line, err := readLine(ctx, rw)
|
||||
line, err := shell.ReadLine(ctx, rw)
|
||||
if errors.Is(err, io.EOF) {
|
||||
fmt.Fprint(rw, "logout\r\n")
|
||||
return nil
|
||||
@@ -103,58 +103,3 @@ func formatPrompt(state *shellState) string {
|
||||
return fmt.Sprintf("%s@%s:%s# ", state.username, state.hostname, cwd)
|
||||
}
|
||||
|
||||
// readLine reads a line of input byte-by-byte, handling backspace, Ctrl+C, and Ctrl+D.
|
||||
func readLine(ctx context.Context, rw io.ReadWriter) (string, error) {
|
||||
var buf []byte
|
||||
b := make([]byte, 1)
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return "", ctx.Err()
|
||||
default:
|
||||
}
|
||||
|
||||
n, err := rw.Read(b)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if n == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
ch := b[0]
|
||||
switch {
|
||||
case ch == '\r' || ch == '\n':
|
||||
fmt.Fprint(rw, "\r\n")
|
||||
return string(buf), nil
|
||||
|
||||
case ch == 4: // Ctrl+D
|
||||
if len(buf) == 0 {
|
||||
return "", io.EOF
|
||||
}
|
||||
|
||||
case ch == 3: // Ctrl+C
|
||||
fmt.Fprint(rw, "^C\r\n")
|
||||
return "", nil
|
||||
|
||||
case ch == 127 || ch == 8: // DEL or Backspace
|
||||
if len(buf) > 0 {
|
||||
buf = buf[:len(buf)-1]
|
||||
fmt.Fprint(rw, "\b \b")
|
||||
}
|
||||
|
||||
case ch == 27: // ESC - start of escape sequence
|
||||
// Read and discard the rest of the escape sequence.
|
||||
// Most are 3 bytes: ESC [ X (arrow keys, etc.)
|
||||
next := make([]byte, 1)
|
||||
if n, _ := rw.Read(next); n > 0 && next[0] == '[' {
|
||||
rw.Read(next) // read the final byte
|
||||
}
|
||||
|
||||
case ch >= 32 && ch < 127: // printable ASCII
|
||||
buf = append(buf, ch)
|
||||
rw.Write([]byte{ch})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user