using System; using System.Collections.Generic; using System.IO; using System.Linq; namespace Chessistics.Scripts.Automation; /// /// Tiny helpers for the file-based IPC. All writes are atomic: write .tmp then rename. /// internal static class IpcFiles { public const string InboxDir = "inbox"; public const string OutboxDir = "outbox"; public const string ScreensDir = "screens"; public const string ReadyFile = "ready.json"; public const string LogFile = "harness.log"; public static void EnsureDirs(string root) { Directory.CreateDirectory(root); Directory.CreateDirectory(Path.Combine(root, InboxDir)); Directory.CreateDirectory(Path.Combine(root, OutboxDir)); Directory.CreateDirectory(Path.Combine(root, ScreensDir)); } public static void AtomicWrite(string path, string contents) { var tmp = path + ".tmp"; File.WriteAllText(tmp, contents); if (File.Exists(path)) File.Delete(path); File.Move(tmp, path); } /// /// Return the oldest inbox file whose id hasn't been processed yet, or null. /// Sorted by filename — agents should name files with a monotonic prefix for ordering. /// public static string? NextInbox(string root, HashSet processed) { var inbox = Path.Combine(root, InboxDir); if (!Directory.Exists(inbox)) return null; var files = Directory.GetFiles(inbox, "*.json") .Where(f => !f.EndsWith(".tmp.json", StringComparison.Ordinal)) .OrderBy(f => Path.GetFileName(f), StringComparer.Ordinal) .ToList(); foreach (var f in files) { if (!processed.Contains(f)) return f; } return null; } public static string OutboxPath(string root, string id) => Path.Combine(root, OutboxDir, id + ".json"); public static string ScreenshotPath(string root, string name) { var safe = name.Replace('/', '_').Replace('\\', '_').Replace(':', '_'); if (!safe.EndsWith(".png", StringComparison.OrdinalIgnoreCase)) safe += ".png"; return Path.Combine(root, ScreensDir, safe); } }