using System; using System.Collections.Generic; using System.IO; using System.IO.Compression; using System.Linq; using System.Runtime.InteropServices; using System.Text; using System.Text.Json; using System.Threading; using System.Threading.Tasks; namespace AxCopilot.Services.Agent; public class ZipTool : IAgentTool { private const long MaxExtractSize = 524288000L; public string Name => "zip_tool"; public string Description => "Compress or extract zip archives. Actions: 'compress' — create a zip file from files/folders; 'extract' — extract a zip file to a directory; 'list' — list contents of a zip file without extracting."; public ToolParameterSchema Parameters { get { ToolParameterSchema toolParameterSchema = new ToolParameterSchema(); Dictionary dictionary = new Dictionary(); ToolProperty obj = new ToolProperty { Type = "string", Description = "Action to perform" }; int num = 3; List list = new List(num); CollectionsMarshal.SetCount(list, num); Span span = CollectionsMarshal.AsSpan(list); span[0] = "compress"; span[1] = "extract"; span[2] = "list"; obj.Enum = list; dictionary["action"] = obj; dictionary["zip_path"] = new ToolProperty { Type = "string", Description = "Path to the zip file (to create or extract)" }; dictionary["source_path"] = new ToolProperty { Type = "string", Description = "Source file or directory path (for compress action)" }; dictionary["dest_path"] = new ToolProperty { Type = "string", Description = "Destination directory (for extract action)" }; toolParameterSchema.Properties = dictionary; num = 2; List list2 = new List(num); CollectionsMarshal.SetCount(list2, num); Span span2 = CollectionsMarshal.AsSpan(list2); span2[0] = "action"; span2[1] = "zip_path"; toolParameterSchema.Required = list2; return toolParameterSchema; } } public Task ExecuteAsync(JsonElement args, AgentContext context, CancellationToken ct = default(CancellationToken)) { string text = args.GetProperty("action").GetString() ?? ""; string text2 = args.GetProperty("zip_path").GetString() ?? ""; if (!Path.IsPathRooted(text2)) { text2 = Path.Combine(context.WorkFolder, text2); } try { if (1 == 0) { } ToolResult result = text switch { "compress" => Compress(args, text2, context), "extract" => Extract(args, text2, context), "list" => ListContents(text2), _ => ToolResult.Fail("Unknown action: " + text), }; if (1 == 0) { } return Task.FromResult(result); } catch (Exception ex) { return Task.FromResult(ToolResult.Fail("Zip 오류: " + ex.Message)); } } private static ToolResult Compress(JsonElement args, string zipPath, AgentContext context) { if (!args.TryGetProperty("source_path", out var value)) { return ToolResult.Fail("'source_path' is required for compress action"); } string text = value.GetString() ?? ""; if (!Path.IsPathRooted(text)) { text = Path.Combine(context.WorkFolder, text); } if (File.Exists(zipPath)) { return ToolResult.Fail("Zip file already exists: " + zipPath); } if (Directory.Exists(text)) { ZipFile.CreateFromDirectory(text, zipPath, CompressionLevel.Optimal, includeBaseDirectory: false); FileInfo fileInfo = new FileInfo(zipPath); return ToolResult.Ok($"✓ Created {zipPath} ({fileInfo.Length / 1024}KB)", zipPath); } if (File.Exists(text)) { using (ZipArchive destination = ZipFile.Open(zipPath, ZipArchiveMode.Create)) { destination.CreateEntryFromFile(text, Path.GetFileName(text), CompressionLevel.Optimal); FileInfo fileInfo2 = new FileInfo(zipPath); return ToolResult.Ok($"✓ Created {zipPath} ({fileInfo2.Length / 1024}KB)", zipPath); } } return ToolResult.Fail("Source not found: " + text); } private static ToolResult Extract(JsonElement args, string zipPath, AgentContext context) { if (!File.Exists(zipPath)) { return ToolResult.Fail("Zip file not found: " + zipPath); } JsonElement value; string text = (args.TryGetProperty("dest_path", out value) ? (value.GetString() ?? "") : ""); if (string.IsNullOrEmpty(text)) { text = Path.Combine(Path.GetDirectoryName(zipPath) ?? context.WorkFolder, Path.GetFileNameWithoutExtension(zipPath)); } if (!Path.IsPathRooted(text)) { text = Path.Combine(context.WorkFolder, text); } using (ZipArchive zipArchive = ZipFile.OpenRead(zipPath)) { long num = zipArchive.Entries.Sum((ZipArchiveEntry e) => e.Length); if (num > 524288000) { return ToolResult.Fail($"Uncompressed size ({num / 1024 / 1024}MB) exceeds 500MB limit"); } foreach (ZipArchiveEntry entry in zipArchive.Entries) { string fullPath = Path.GetFullPath(Path.Combine(text, entry.FullName)); if (!fullPath.StartsWith(Path.GetFullPath(text), StringComparison.OrdinalIgnoreCase)) { return ToolResult.Fail("Security: entry '" + entry.FullName + "' escapes destination directory"); } } } Directory.CreateDirectory(text); ZipFile.ExtractToDirectory(zipPath, text, overwriteFiles: true); int value2 = Directory.GetFiles(text, "*", SearchOption.AllDirectories).Length; return ToolResult.Ok($"✓ Extracted {value2} files to {text}"); } private static ToolResult ListContents(string zipPath) { if (!File.Exists(zipPath)) { return ToolResult.Fail("Zip file not found: " + zipPath); } using ZipArchive zipArchive = ZipFile.OpenRead(zipPath); StringBuilder stringBuilder = new StringBuilder(); StringBuilder stringBuilder2 = stringBuilder; StringBuilder stringBuilder3 = stringBuilder2; StringBuilder.AppendInterpolatedStringHandler handler = new StringBuilder.AppendInterpolatedStringHandler(14, 2, stringBuilder2); handler.AppendLiteral("Archive: "); handler.AppendFormatted(Path.GetFileName(zipPath)); handler.AppendLiteral(" ("); handler.AppendFormatted(new FileInfo(zipPath).Length / 1024); handler.AppendLiteral("KB)"); stringBuilder3.AppendLine(ref handler); stringBuilder2 = stringBuilder; StringBuilder stringBuilder4 = stringBuilder2; handler = new StringBuilder.AppendInterpolatedStringHandler(9, 1, stringBuilder2); handler.AppendLiteral("Entries: "); handler.AppendFormatted(zipArchive.Entries.Count); stringBuilder4.AppendLine(ref handler); stringBuilder.AppendLine(); stringBuilder2 = stringBuilder; StringBuilder stringBuilder5 = stringBuilder2; handler = new StringBuilder.AppendInterpolatedStringHandler(4, 3, stringBuilder2); handler.AppendFormatted("Size", 10); handler.AppendLiteral(" "); handler.AppendFormatted("Compressed", 10); handler.AppendLiteral(" "); handler.AppendFormatted("Name"); stringBuilder5.AppendLine(ref handler); stringBuilder.AppendLine(new string('-', 60)); int num = Math.Min(zipArchive.Entries.Count, 200); foreach (ZipArchiveEntry item in zipArchive.Entries.Take(num)) { stringBuilder2 = stringBuilder; StringBuilder stringBuilder6 = stringBuilder2; handler = new StringBuilder.AppendInterpolatedStringHandler(4, 3, stringBuilder2); handler.AppendFormatted(item.Length, 10); handler.AppendLiteral(" "); handler.AppendFormatted(item.CompressedLength, 10); handler.AppendLiteral(" "); handler.AppendFormatted(item.FullName); stringBuilder6.AppendLine(ref handler); } if (zipArchive.Entries.Count > num) { stringBuilder2 = stringBuilder; StringBuilder stringBuilder7 = stringBuilder2; handler = new StringBuilder.AppendInterpolatedStringHandler(22, 1, stringBuilder2); handler.AppendLiteral("\n... and "); handler.AppendFormatted(zipArchive.Entries.Count - num); handler.AppendLiteral(" more entries"); stringBuilder7.AppendLine(ref handler); } long num2 = zipArchive.Entries.Sum((ZipArchiveEntry e) => e.Length); stringBuilder2 = stringBuilder; StringBuilder stringBuilder8 = stringBuilder2; handler = new StringBuilder.AppendInterpolatedStringHandler(23, 1, stringBuilder2); handler.AppendLiteral("\nTotal uncompressed: "); handler.AppendFormatted(num2 / 1024); handler.AppendLiteral("KB"); stringBuilder8.AppendLine(ref handler); return ToolResult.Ok(stringBuilder.ToString()); } }