mirror of
https://wget.la/https://github.com/leookun/cursor-byok
synced 2026-08-17 19:47:10 +08:00
173 lines
4.3 KiB
Go
173 lines
4.3 KiB
Go
package forwarder
|
|
|
|
import (
|
|
"errors"
|
|
"log"
|
|
"os"
|
|
"path/filepath"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
const historyMaintenanceLockStaleAfter = 30 * time.Minute
|
|
const historyMaintenanceTempStaleAfter = 5 * time.Minute
|
|
|
|
func (service *Service) startHistoryMaintenance() {
|
|
if service == nil || service.store == nil {
|
|
return
|
|
}
|
|
go func() {
|
|
if err := service.runHistoryMaintenance(); err != nil {
|
|
log.Printf("forwarder history maintenance failed: %v", err)
|
|
}
|
|
}()
|
|
}
|
|
|
|
func (service *Service) runHistoryMaintenance() error {
|
|
historyRoot := strings.TrimSpace(service.store.HistoryDir())
|
|
if historyRoot == "" {
|
|
return nil
|
|
}
|
|
release, ok, err := acquireHistoryMaintenanceLock(historyRoot)
|
|
if err != nil || !ok {
|
|
return err
|
|
}
|
|
defer release()
|
|
|
|
entries, err := os.ReadDir(historyRoot)
|
|
if err != nil {
|
|
if errors.Is(err, os.ErrNotExist) {
|
|
return nil
|
|
}
|
|
return err
|
|
}
|
|
for _, entry := range entries {
|
|
if !entry.IsDir() {
|
|
cleanupRootLegacyHistoryArtifact(historyRoot, entry.Name())
|
|
continue
|
|
}
|
|
service.cleanupConversationLegacyArtifacts(filepath.Join(historyRoot, entry.Name()))
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func cleanupRootLegacyHistoryArtifact(historyRoot string, name string) {
|
|
trimmedName := strings.TrimSpace(name)
|
|
if trimmedName == "" || trimmedName == ".history-maintenance.lock" {
|
|
return
|
|
}
|
|
if !isRootLegacyHistoryArtifact(trimmedName) {
|
|
return
|
|
}
|
|
path := filepath.Join(historyRoot, trimmedName)
|
|
if strings.Contains(trimmedName, ".tmp-") {
|
|
cleanupStaleHistoryTempArtifact(path)
|
|
return
|
|
}
|
|
if err := os.RemoveAll(path); err != nil && !errors.Is(err, os.ErrNotExist) {
|
|
log.Printf("forwarder root legacy cleanup failed path=%s err=%v", path, err)
|
|
}
|
|
}
|
|
|
|
func isRootLegacyHistoryArtifact(name string) bool {
|
|
if strings.TrimSpace(name) == usageFileName || strings.TrimSpace(name) == usageFileName+".lock" {
|
|
return false
|
|
}
|
|
if strings.HasSuffix(name, ".json") || strings.HasSuffix(name, ".jsonl") {
|
|
return true
|
|
}
|
|
if strings.Contains(name, ".tmp-") {
|
|
return true
|
|
}
|
|
if strings.HasSuffix(name, ".lock") {
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
func (service *Service) cleanupConversationLegacyArtifacts(conversationDir string) {
|
|
for _, name := range []string{
|
|
"turns",
|
|
"active",
|
|
"latest.json",
|
|
"summary.json",
|
|
"replay.json",
|
|
"runtime.json",
|
|
"request.json",
|
|
"recovery.json",
|
|
"conversation.json",
|
|
"entries.jsonl",
|
|
} {
|
|
path := filepath.Join(conversationDir, name)
|
|
if err := os.RemoveAll(path); err != nil && !errors.Is(err, os.ErrNotExist) {
|
|
log.Printf("forwarder legacy cleanup failed path=%s err=%v", path, err)
|
|
}
|
|
}
|
|
entries, err := os.ReadDir(conversationDir)
|
|
if err != nil {
|
|
return
|
|
}
|
|
for _, entry := range entries {
|
|
if !entry.IsDir() {
|
|
if strings.Contains(entry.Name(), ".tmp-") {
|
|
cleanupStaleHistoryTempArtifact(filepath.Join(conversationDir, entry.Name()))
|
|
}
|
|
continue
|
|
}
|
|
if _, err := strconv.Atoi(strings.TrimSpace(entry.Name())); err != nil {
|
|
continue
|
|
}
|
|
path := filepath.Join(conversationDir, entry.Name())
|
|
if err := os.RemoveAll(path); err != nil && !errors.Is(err, os.ErrNotExist) {
|
|
log.Printf("forwarder numeric legacy cleanup failed path=%s err=%v", path, err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func cleanupStaleHistoryTempArtifact(path string) {
|
|
info, err := os.Stat(path)
|
|
if err != nil {
|
|
return
|
|
}
|
|
if time.Since(info.ModTime()) < historyMaintenanceTempStaleAfter {
|
|
return
|
|
}
|
|
if err := os.Remove(path); err != nil && !errors.Is(err, os.ErrNotExist) {
|
|
log.Printf("forwarder temp cleanup failed path=%s err=%v", path, err)
|
|
}
|
|
}
|
|
|
|
func acquireHistoryMaintenanceLock(historyRoot string) (func(), bool, error) {
|
|
if err := os.MkdirAll(historyRoot, 0o755); err != nil {
|
|
return nil, false, err
|
|
}
|
|
lockPath := filepath.Join(historyRoot, ".history-maintenance.lock")
|
|
for attempt := 0; attempt < 2; attempt++ {
|
|
file, err := os.OpenFile(lockPath, os.O_CREATE|os.O_EXCL|os.O_WRONLY, 0o600)
|
|
if err == nil {
|
|
_, _ = file.WriteString(time.Now().UTC().Format(time.RFC3339Nano))
|
|
_ = file.Close()
|
|
return func() {
|
|
_ = os.Remove(lockPath)
|
|
}, true, nil
|
|
}
|
|
if !errors.Is(err, os.ErrExist) {
|
|
return nil, false, err
|
|
}
|
|
info, statErr := os.Stat(lockPath)
|
|
if statErr != nil {
|
|
if errors.Is(statErr, os.ErrNotExist) {
|
|
continue
|
|
}
|
|
return nil, false, statErr
|
|
}
|
|
if time.Since(info.ModTime()) > historyMaintenanceLockStaleAfter {
|
|
_ = os.Remove(lockPath)
|
|
continue
|
|
}
|
|
return nil, false, nil
|
|
}
|
|
return nil, false, nil
|
|
}
|