Deadlock u promeni šifre i TERM za PTY
All checks were successful
Tests / unit-tests (push) Successful in 45s
All checks were successful
Tests / unit-tests (push) Successful in 45s
- Ispravljen deadlock: SetPassword zaključa mu pa pozove save() koji opet zaključa mu. Razdvojeno u saveLocked() (bez lock-a) i save() - Vraćeno normalno ponašanje kursora (Claude Code sam upravlja) - PTY okruženje: TERM=xterm-256color, COLORTERM=truecolor Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
ba801b28ea
commit
3bb8d289af
61
config.go
61
config.go
@ -4,6 +4,10 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
)
|
||||
|
||||
type APIConfig struct {
|
||||
@ -19,6 +23,9 @@ type Config struct {
|
||||
Password string `json:"password"`
|
||||
SessionSecret string `json:"session_secret"`
|
||||
API APIConfig `json:"api"`
|
||||
|
||||
path string // putanja do config fajla (ne serijalizuje se)
|
||||
mu sync.Mutex // zaštita za upis
|
||||
}
|
||||
|
||||
func LoadConfig(path string) (*Config, error) {
|
||||
@ -51,5 +58,59 @@ func LoadConfig(path string) (*Config, error) {
|
||||
return nil, fmt.Errorf("session_secret is required")
|
||||
}
|
||||
|
||||
cfg.path = path
|
||||
|
||||
// Ako šifra nije hešovana, heširaj je i sačuvaj
|
||||
if !isHashed(cfg.Password) {
|
||||
hashed, err := bcrypt.GenerateFromPassword([]byte(cfg.Password), bcrypt.DefaultCost)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("hashing password: %w", err)
|
||||
}
|
||||
cfg.Password = string(hashed)
|
||||
if err := cfg.save(); err != nil {
|
||||
return nil, fmt.Errorf("saving hashed password: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
return &cfg, nil
|
||||
}
|
||||
|
||||
// CheckPassword proverava da li se data šifra poklapa sa hešom.
|
||||
func (c *Config) CheckPassword(password string) bool {
|
||||
return bcrypt.CompareHashAndPassword([]byte(c.Password), []byte(password)) == nil
|
||||
}
|
||||
|
||||
// SetPassword postavlja novu šifru (hešuje i čuva u config).
|
||||
func (c *Config) SetPassword(newPassword string) error {
|
||||
hashed, err := bcrypt.GenerateFromPassword([]byte(newPassword), bcrypt.DefaultCost)
|
||||
if err != nil {
|
||||
return fmt.Errorf("hashing password: %w", err)
|
||||
}
|
||||
c.mu.Lock()
|
||||
c.Password = string(hashed)
|
||||
err = c.saveLocked()
|
||||
c.mu.Unlock()
|
||||
return err
|
||||
}
|
||||
|
||||
// save upisuje trenutni config nazad u fajl (pozivalac mora držati c.mu).
|
||||
func (c *Config) saveLocked() error {
|
||||
data, err := json.MarshalIndent(c, "", " ")
|
||||
if err != nil {
|
||||
return fmt.Errorf("marshaling config: %w", err)
|
||||
}
|
||||
data = append(data, '\n')
|
||||
return os.WriteFile(c.path, data, 0600)
|
||||
}
|
||||
|
||||
// save upisuje trenutni config nazad u fajl.
|
||||
func (c *Config) save() error {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
return c.saveLocked()
|
||||
}
|
||||
|
||||
// isHashed proverava da li je string bcrypt heš.
|
||||
func isHashed(s string) bool {
|
||||
return strings.HasPrefix(s, "$2a$") || strings.HasPrefix(s, "$2b$")
|
||||
}
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
@ -12,9 +11,6 @@ import (
|
||||
"github.com/creack/pty"
|
||||
)
|
||||
|
||||
// ANSI escape sequence to hide cursor — Claude Code's Ink TUI sends this.
|
||||
// We strip it so xterm.js cursor stays visible.
|
||||
var cursorHideSeq = []byte("\x1b[?25l")
|
||||
|
||||
const (
|
||||
outputBufferSize = 128 * 1024 // 128KB ring buffer for replay
|
||||
@ -82,10 +78,15 @@ func SpawnPTY(projectDir string) (*PTYSession, error) {
|
||||
cmd.Dir = projectDir
|
||||
|
||||
// Filter env vars to prevent nested session detection
|
||||
cmd.Env = filterEnvMulti(os.Environ(), []string{
|
||||
env := filterEnvMulti(os.Environ(), []string{
|
||||
"CLAUDECODE",
|
||||
"CLAUDE_CODE_ENTRYPOINT",
|
||||
"TERM",
|
||||
"COLORTERM",
|
||||
})
|
||||
// Set proper terminal type for full color and cursor support
|
||||
env = append(env, "TERM=xterm-256color", "COLORTERM=truecolor")
|
||||
cmd.Env = env
|
||||
|
||||
ptmx, err := pty.StartWithSize(cmd, &pty.Winsize{Rows: 24, Cols: 80})
|
||||
if err != nil {
|
||||
@ -123,12 +124,6 @@ func (s *PTYSession) readLoop() {
|
||||
data := make([]byte, n)
|
||||
copy(data, buf[:n])
|
||||
|
||||
// Strip cursor hide sequence so xterm.js cursor stays visible
|
||||
data = bytes.ReplaceAll(data, cursorHideSeq, []byte{})
|
||||
if len(data) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
s.buffer.Write(data)
|
||||
|
||||
s.mu.Lock()
|
||||
|
||||
Loading…
Reference in New Issue
Block a user