239 lines
8.2 KiB
C#
239 lines
8.2 KiB
C#
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<string, ToolProperty> dictionary = new Dictionary<string, ToolProperty>();
|
|
ToolProperty obj = new ToolProperty
|
|
{
|
|
Type = "string",
|
|
Description = "Action to perform"
|
|
};
|
|
int num = 3;
|
|
List<string> list = new List<string>(num);
|
|
CollectionsMarshal.SetCount(list, num);
|
|
Span<string> 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<string> list2 = new List<string>(num);
|
|
CollectionsMarshal.SetCount(list2, num);
|
|
Span<string> span2 = CollectionsMarshal.AsSpan(list2);
|
|
span2[0] = "action";
|
|
span2[1] = "zip_path";
|
|
toolParameterSchema.Required = list2;
|
|
return toolParameterSchema;
|
|
}
|
|
}
|
|
|
|
public Task<ToolResult> 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<string>("Size", 10);
|
|
handler.AppendLiteral(" ");
|
|
handler.AppendFormatted<string>("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());
|
|
}
|
|
}
|