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"`
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user