Add M0 server skeleton with SQLite store, SSE events, worktrees, and tailscale service
This commit is contained in:
@@ -2,6 +2,7 @@ package claude
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"encoding/hex"
|
||||
@@ -103,6 +104,7 @@ type Session struct {
|
||||
|
||||
writeMu sync.Mutex
|
||||
transcript io.Writer
|
||||
scrub []byte
|
||||
|
||||
pendingMu sync.Mutex
|
||||
pending map[string]chan controlResult
|
||||
@@ -360,6 +362,10 @@ func (s *Session) record(dir string, line []byte) {
|
||||
return
|
||||
}
|
||||
|
||||
if len(s.scrub) > 0 {
|
||||
line = bytes.ReplaceAll(line, s.scrub, []byte("[redacted]"))
|
||||
}
|
||||
|
||||
b, err := json.Marshal(map[string]any{"dir": dir, "msg": json.RawMessage(line)})
|
||||
if err != nil {
|
||||
slog.Error("record transcript", "err", err)
|
||||
@@ -389,10 +395,6 @@ func (s *Session) readLoop(stdout io.Reader) {
|
||||
}
|
||||
|
||||
func (s *Session) handleLine(line []byte) {
|
||||
s.writeMu.Lock()
|
||||
s.record("recv", line)
|
||||
s.writeMu.Unlock()
|
||||
|
||||
env := envelope{}
|
||||
err := json.Unmarshal(line, &env)
|
||||
if err != nil {
|
||||
@@ -400,6 +402,13 @@ func (s *Session) handleLine(line []byte) {
|
||||
return
|
||||
}
|
||||
|
||||
s.writeMu.Lock()
|
||||
if env.Type == "control_response" {
|
||||
s.learnScrub(env.Response)
|
||||
}
|
||||
s.record("recv", line)
|
||||
s.writeMu.Unlock()
|
||||
|
||||
switch env.Type {
|
||||
case "control_response":
|
||||
s.handleControlResponse(env.Response)
|
||||
@@ -422,6 +431,21 @@ func (s *Session) handleLine(line []byte) {
|
||||
s.events <- Event{Type: env.Type, Subtype: env.Subtype, Raw: append(json.RawMessage{}, line...)}
|
||||
}
|
||||
|
||||
func (s *Session) learnScrub(raw json.RawMessage) {
|
||||
resp := struct {
|
||||
Response struct {
|
||||
Account struct {
|
||||
Email string `json:"email"`
|
||||
} `json:"account"`
|
||||
} `json:"response"`
|
||||
}{}
|
||||
err := json.Unmarshal(raw, &resp)
|
||||
if err != nil || resp.Response.Account.Email == "" {
|
||||
return
|
||||
}
|
||||
s.scrub = []byte(resp.Response.Account.Email)
|
||||
}
|
||||
|
||||
func (s *Session) handleControlResponse(raw json.RawMessage) {
|
||||
resp := struct {
|
||||
Subtype string `json:"subtype"`
|
||||
|
||||
Reference in New Issue
Block a user