| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102 |
- // Package agentfiles 提供 MCP Agent 本地落盘目录(/tmp/fwh-toolbox-agent)的清理能力。
- //
- // 背景:MCP 工具的查询/导出结果会无条件落盘到 results/ 目录,storage_file_read_to_local
- // 下载的文件会落到 downloads/ 目录。这些都是会话内的中间产物,需要定期清理避免无限堆积。
- //
- // 本包被设计为独立、无外部依赖(仅标准库 + zap 日志),以便:
- // - MCP 进程(cmd/mcp)在启动时调用一次(A 方案:进程启动清理);
- // - 后端主服务的 cron scheduler 定时调用(B 方案:定时清理)。
- //
- // MCP 是后端通过 kiro-cli 拉起的本机子进程,与后端共享同一 /tmp 文件系统,
- // 因此后端 cron 能清理到 MCP 写入的这些目录。
- package agentfiles
- import (
- "os"
- "path/filepath"
- "time"
- "go.uber.org/zap"
- )
- // AgentBaseDir 是 MCP Agent 本地落盘的根目录。
- // 注意:需与 cmd/mcp/tools/helper.AgentBaseDir 保持一致。
- const AgentBaseDir = "/tmp/fwh-toolbox-agent"
- // DefaultMaxAge 默认过期阈值:文件 mtime 超过此时长即视为可清理。
- // 取 24h 以覆盖单个会话的生命周期,避免清掉会话内稍后仍需读回的落盘文件。
- const DefaultMaxAge = 24 * time.Hour
- // cleanupSubDirs 需要清理的子目录(相对 AgentBaseDir)。
- // results:查询/导出结果落盘;downloads:read_to_local 下载中转。
- var cleanupSubDirs = []string{"results", "downloads"}
- // CleanupResult 描述一次清理的结果统计。
- type CleanupResult struct {
- Scanned int // 扫描到的文件总数
- Removed int // 成功删除的文件数
- Failed int // 删除失败的文件数
- Freed int64 // 释放的字节数(仅统计成功删除的文件)
- }
- // Cleanup 清理 AgentBaseDir 下各子目录中 mtime 超过 maxAge 的文件。
- // maxAge <= 0 时使用 DefaultMaxAge。
- //
- // 清理为"尽力而为":单个文件删除失败只记 warn 并继续,不返回错误、不中断调用方。
- // 仅删除普通文件,跳过子目录(当前落盘不产生嵌套目录)。
- func Cleanup(maxAge time.Duration) CleanupResult {
- if maxAge <= 0 {
- maxAge = DefaultMaxAge
- }
- cutoff := time.Now().Add(-maxAge)
- var res CleanupResult
- for _, sub := range cleanupSubDirs {
- dir := filepath.Join(AgentBaseDir, sub)
- entries, err := os.ReadDir(dir)
- if err != nil {
- // 目录不存在是正常情况(尚未产生过任何落盘文件),忽略。
- if !os.IsNotExist(err) {
- zap.L().Warn("[agentfiles] 读取清理目录失败",
- zap.String("dir", dir), zap.Error(err))
- }
- continue
- }
- for _, e := range entries {
- if e.IsDir() {
- continue
- }
- info, err := e.Info()
- if err != nil {
- zap.L().Warn("[agentfiles] 获取文件信息失败",
- zap.String("dir", dir), zap.String("name", e.Name()), zap.Error(err))
- continue
- }
- res.Scanned++
- if info.ModTime().After(cutoff) {
- continue // 未过期,保留
- }
- path := filepath.Join(dir, e.Name())
- if err := os.Remove(path); err != nil {
- res.Failed++
- zap.L().Warn("[agentfiles] 删除过期文件失败",
- zap.String("path", path), zap.Error(err))
- continue
- }
- res.Removed++
- res.Freed += info.Size()
- }
- }
- if res.Scanned > 0 {
- zap.L().Info("[agentfiles] 清理完成",
- zap.Int("scanned", res.Scanned),
- zap.Int("removed", res.Removed),
- zap.Int("failed", res.Failed),
- zap.Int64("freedBytes", res.Freed),
- zap.Duration("maxAge", maxAge),
- )
- }
- return res
- }
|