using System.Net.Sockets; using System.Text; using System.Text.Json; using Hush.Daemon; namespace Hush.Cli; public class SocketClient : IAsyncDisposable { private readonly UnixDomainSocketEndPoint _endPoint; private readonly Socket _socket; public SocketClient() { var runtimeDir = Environment.GetEnvironmentVariable("XDG_RUNTIME_DIR"); var baseDir = string.IsNullOrEmpty(runtimeDir) ? Path.GetTempPath() : runtimeDir; var socketPath = Path.Combine(baseDir, "hush.sock"); _endPoint = new UnixDomainSocketEndPoint(socketPath); _socket = new Socket(AddressFamily.Unix, SocketType.Stream, ProtocolType.Unspecified); } public async Task ConnectAsync(TimeSpan timeout) { var cts = new CancellationTokenSource(timeout); await _socket.ConnectAsync(_endPoint, cts.Token); } public async Task SendCommandAsync(byte command) { await _socket.SendAsync(new[] { command }, SocketFlags.None); } public async Task ReceiveJsonAsync(TimeSpan timeout) { var cts = new CancellationTokenSource(timeout); var buffer = new byte[4096]; var bytesRead = await _socket.ReceiveAsync(buffer, SocketFlags.None, cts.Token); if (bytesRead == 0) return default; var json = Encoding.UTF8.GetString(buffer, 0, bytesRead); return (T?)JsonSerializer.Deserialize(json, typeof(T), DaemonJsonContext.Default); } public async Task ReceiveRawJsonAsync(TimeSpan timeout) { var cts = new CancellationTokenSource(timeout); var buffer = new byte[4096]; var bytesRead = await _socket.ReceiveAsync(buffer, SocketFlags.None, cts.Token); if (bytesRead == 0) return null; return Encoding.UTF8.GetString(buffer, 0, bytesRead); } public async ValueTask DisposeAsync() { _socket.Dispose(); await ValueTask.CompletedTask; } }