Something went wrong. Try again.
[READ-ONLY] Mirror of https://github.com/just-cameron/note. External storage tool for Letta agents to manage persistent notes
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125package sync
import ( "encoding/json" "fmt" "os" "path/filepath" "time")
const StateFileName = ".note-sync.json"
// FileState tracks the sync state of a single filetype FileState struct { LocalHash string `json:"local_hash"` RemoteHash string `json:"remote_hash"` SyncedAt time.Time `json:"synced_at"` BlockID string `json:"block_id,omitempty"`}
// State represents the overall sync statetype State struct { AgentID string `json:"agent_id"` LettaBaseURL string `json:"letta_base_url"` Files map[string]FileState `json:"files"`}
// LoadState loads the sync state from the current directoryfunc LoadState(dir string) (*State, error) { statePath := filepath.Join(dir, StateFileName) data, err := os.ReadFile(statePath) if err != nil { if os.IsNotExist(err) { return nil, fmt.Errorf("not a note-sync directory (no %s found). Run 'note-sync init' first", StateFileName) } return nil, fmt.Errorf("failed to read state file: %w", err) }
var state State if err := json.Unmarshal(data, &state); err != nil { return nil, fmt.Errorf("failed to parse state file: %w", err) }
if state.Files == nil { state.Files = make(map[string]FileState) }
return &state, nil}
// SaveState saves the sync state to diskfunc (s *State) SaveState(dir string) error { statePath := filepath.Join(dir, StateFileName)
data, err := json.MarshalIndent(s, "", " ") if err != nil { return fmt.Errorf("failed to marshal state: %w", err) }
if err := os.WriteFile(statePath, data, 0644); err != nil { return fmt.Errorf("failed to write state file: %w", err) }
return nil}
// InitState creates a new state filefunc InitState(dir, agentID, baseURL string) (*State, error) { if baseURL == "" { baseURL = "https://api.letta.com" }
// Create directory if it doesn't exist if err := os.MkdirAll(dir, 0755); err != nil { return nil, fmt.Errorf("failed to create directory: %w", err) }
state := &State{ AgentID: agentID, LettaBaseURL: baseURL, Files: make(map[string]FileState), }
if err := state.SaveState(dir); err != nil { return nil, err }
return state, nil}
// PathToFile converts a Letta note path to a local file path// /projects/webapp -> projects/webapp.mdfunc PathToFile(notePath string) string { // Remove leading slash if len(notePath) > 0 && notePath[0] == '/' { notePath = notePath[1:] } return notePath + ".md"}
// FileToPath converts a local file path to a Letta note path// projects/webapp.md -> /projects/webappfunc FileToPath(filePath string) string { // Remove .md extension if len(filePath) > 3 && filePath[len(filePath)-3:] == ".md" { filePath = filePath[:len(filePath)-3] } return "/" + filePath}
// IsConflictFile checks if a file is a conflict filefunc IsConflictFile(filename string) bool { return len(filename) > 12 && filename[len(filename)-12:] == ".conflict.md"}
// ConflictFileName returns the conflict file name for a pathfunc ConflictFileName(notePath string) string { // /projects/webapp -> projects/webapp.conflict.md if len(notePath) > 0 && notePath[0] == '/' { notePath = notePath[1:] } return notePath + ".conflict.md"}