Add M0 server skeleton with SQLite store, SSE events, worktrees, and tailscale service

This commit is contained in:
Ian Gulliver
2026-10-13 22:19:20 -07:00
parent 555a55efc8
commit 2328904131
26 changed files with 3030 additions and 194 deletions
+28 -4
View File
@@ -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"`