From b58eeffd2f8e9872fc13d31b99fe7e15af1a60d8 Mon Sep 17 00:00:00 2001 From: slhafzjw Date: Thu, 1 Jan 2026 23:32:21 +0800 Subject: [PATCH] feat(LocalRunnerClient): support overflow event in DynamicActionMcpServer --- .../core/action/runner/LocalRunnerClient.java | 92 ++++++++++++++----- 1 file changed, 68 insertions(+), 24 deletions(-) diff --git a/Partner-Main/src/main/java/work/slhaf/partner/core/action/runner/LocalRunnerClient.java b/Partner-Main/src/main/java/work/slhaf/partner/core/action/runner/LocalRunnerClient.java index 2929dc01..51ed8313 100644 --- a/Partner-Main/src/main/java/work/slhaf/partner/core/action/runner/LocalRunnerClient.java +++ b/Partner-Main/src/main/java/work/slhaf/partner/core/action/runner/LocalRunnerClient.java @@ -38,15 +38,13 @@ import java.io.InputStreamReader; import java.nio.charset.StandardCharsets; import java.nio.file.*; import java.time.Duration; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutorService; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; import java.util.function.BiFunction; +import java.util.stream.Collectors; import java.util.stream.Stream; import static work.slhaf.partner.common.util.PathUtil.buildPathStr; @@ -548,26 +546,23 @@ public class LocalRunnerClient extends RunnerClient { @NotNull protected LocalWatchServiceBuild.InitLoader buildLoad() { // 从该路径列出已存在的目录,每个目录对应不同的行动程序及描述文件,从描述文件加载程序信息 - return this::load; - } - - private void load() { - Path root = ctx.root; - File file = root.toFile(); - if (file.isFile()) { - throw new ActionInitFailedException("未找到目录: " + root); - } - File[] files = file.listFiles(); - if (files == null) { - throw new ActionInitFailedException("未正常读取目录: " + root); - } - for (File dir : files) { - if (!normalPath(dir.toPath())) { - continue; + return () -> { + Path root = ctx.root; + File file = root.toFile(); + if (file.isFile()) { + throw new ActionInitFailedException("未找到目录: " + root); } - - addAction(dir.getName(), dir.toPath()); - } + File[] files = file.listFiles(); + if (files == null) { + throw new ActionInitFailedException("未正常读取目录: " + root); + } + for (File dir : files) { + if (!normalPath(dir.toPath())) { + continue; + } + addAction(dir.getName(), dir.toPath()); + } + }; } private McpStatelessServerFeatures.AsyncToolSpecification buildAsyncToolSpecification(MetaActionInfo info, File program, String actionKey, String name) { @@ -703,7 +698,56 @@ public class LocalRunnerClient extends RunnerClient { @Override @NotNull protected LocalWatchServiceBuild.EventHandler buildOverflow() { - return null; + return (thisDir, context) -> { + // 直接从 existedMetaActions 中拿取现有的 key,上游也从这里发现可用工具 + Set existed = existedMetaActions.keySet().stream().map(actionKey -> actionKey.split("::")[1]).collect(Collectors.toSet()); + Set currentDirs = new HashSet<>(); + // 按照预期 root 目录下有效 path 只包括各个 action 目录 + // 排除非目录 path + try (Stream stream = Files.list(ctx.root).filter(Files::isDirectory)) { + stream.forEach(path -> { + String name = path.getFileName().toString(); + currentDirs.add(name); + + boolean contains = existed.contains(name); + boolean normal = normalPath(path); + + // 如果该目录对应 action 已被记录,且符合 action 目录要求,则无处理 + // 如果已被记录,但不符合,则移除行动 + // 此时必定被监听 + if (contains && !normal) { + removeAction(name); + } + + // 如果 action 没有记录,但符合要求,由于此时必定尚未被监听,则注册监听且添加新 action + // 如果未被记录,且不符合要求,则只注册监听 + if (!contains) { + boolean alreadyWatching = ctx.watchKeys.values().stream() + .anyMatch(p -> p.equals(path)); + if (!alreadyWatching) { + try { + WatchKey watchKey = path.register(ctx.watchService, ctx.kinds.toArray(WatchEvent.Kind[]::new)); + ctx.watchKeys.put(watchKey, path); + } catch (IOException e) { + log.error("监听目录注册失败: {}", path); + } + } + + if (normal) { + addAction(name, path); + } + } + }); + } catch (IOException e) { + log.error("目录无法读取: {}", ctx.root); + return; + } + for (String existedName : existed) { + if (!currentDirs.contains(existedName)) { + removeAction(existedName); + } + } + }; } }