119 lines
6.2 KiB
C#
119 lines
6.2 KiB
C#
using WxAgent.Core;
|
|
|
|
namespace WxAgent.Host;
|
|
|
|
internal static class RemoteCliCommands
|
|
{
|
|
public static async Task<object> RunAsync(string[] args, CancellationToken cancellationToken)
|
|
{
|
|
if (args.Length < 3)
|
|
throw Invalid("Remote commands require a group and action.");
|
|
if (IsConfigurationCommand(args))
|
|
throw Invalid("Windows Agent configuration is GUI-only; double-click WxAgent.Tray.exe and use 服务设置... .");
|
|
var configPath = Path.GetFullPath(Required(args, "--config"));
|
|
var group = args[1];
|
|
var action = args[2];
|
|
return (group, action) switch
|
|
{
|
|
("auth", "show") => await AuthShowAsync(configPath, cancellationToken),
|
|
("probe", "run") => await ProbeAsync(configPath, cancellationToken),
|
|
("status", "show") => await StatusShowAsync(args, configPath, cancellationToken),
|
|
("reporting", "show") => await ReportingShowAsync(configPath, cancellationToken),
|
|
_ => throw Invalid("Unknown remote command. Use: auth show, status show, probe run, reporting show.")
|
|
};
|
|
}
|
|
|
|
private static async Task<object> AuthShowAsync(string path, CancellationToken cancellationToken)
|
|
{
|
|
var configuration = await RemoteNodeConfigurationStore.LoadAsync(path, cancellationToken);
|
|
return new
|
|
{
|
|
config = path,
|
|
remote = configuration.Remote.Redacted(),
|
|
configured = configuration.Remote.IsConfigured,
|
|
reportingConfigVersion = configuration.Reporting.ConfigVersion
|
|
};
|
|
}
|
|
|
|
private static async Task<object> ProbeAsync(string path, CancellationToken cancellationToken)
|
|
{
|
|
var configuration = await RemoteNodeConfigurationStore.LoadAsync(path, cancellationToken);
|
|
configuration.Remote.Validate();
|
|
using var client = new RemoteControlClient(configuration.Remote);
|
|
var accounts = configuration.Reporting.Accounts.Select(account => new RemoteAccountSummary(
|
|
account.AccountId,
|
|
string.Equals(account.AccountId, configuration.Remote.ActiveAccountId, StringComparison.Ordinal),
|
|
account.Enabled,
|
|
account.AllowedChats.Count(chat => chat.Type == ReportingChatType.Group && chat.Enabled && chat.IdentityVerified),
|
|
account.AllowedChats.Count(chat => chat.Type == ReportingChatType.Private && chat.Enabled && chat.IdentityVerified))).ToArray();
|
|
var registration = await client.RegisterAsync(new RemoteNodeRegistration(configuration.Remote.NodeId!, "cli", RemoteProtocol.Version,
|
|
["heartbeat", "poll-tasks", "send-text", "report-message"], configuration.Reporting.ConfigVersion, accounts), cancellationToken);
|
|
var heartbeat = await client.HeartbeatAsync(new RemoteHeartbeat(configuration.Remote.NodeId!, "cli", RemoteProtocol.Version,
|
|
RemoteNodeStatus.Online, false, false, false, configuration.Remote.ActiveAccountId, 0,
|
|
configuration.Reporting.ConfigVersion, Guid.NewGuid().ToString("N")), cancellationToken);
|
|
return new { status = "ok", node = registration.NodeId, authState = client.AuthState, heartbeat = heartbeat.Status };
|
|
}
|
|
|
|
private static async Task<object> StatusShowAsync(string[] args, string path, CancellationToken cancellationToken)
|
|
{
|
|
var configuration = await RemoteNodeConfigurationStore.LoadAsync(path, cancellationToken);
|
|
var dataDirectory = Path.GetFullPath(Option(args, "--data-dir") ?? Path.GetDirectoryName(path) ?? AppContext.BaseDirectory);
|
|
var eventQueue = new RemoteEventQueue(Path.Combine(dataDirectory, "remote-event-queue.json"));
|
|
var ledger = new RemoteTaskLedger(Path.Combine(dataDirectory, "remote-task-ledger.json"));
|
|
return new
|
|
{
|
|
config = path,
|
|
remote = configuration.Remote.Redacted(),
|
|
configured = configuration.Remote.IsConfigured,
|
|
reporting = new { configuration.Reporting.Enabled, configuration.Reporting.ConfigVersion, pendingEvents = eventQueue.PendingCount },
|
|
unreportedTaskResults = ledger.UnreportedResults().Count,
|
|
audit = configuration.Audit.TakeLast(20)
|
|
};
|
|
}
|
|
|
|
private static async Task<object> ReportingShowAsync(string path, CancellationToken cancellationToken)
|
|
{
|
|
var configuration = await RemoteNodeConfigurationStore.LoadAsync(path, cancellationToken);
|
|
return new
|
|
{
|
|
config = path,
|
|
enabled = configuration.Reporting.Enabled,
|
|
configVersion = configuration.Reporting.ConfigVersion,
|
|
accounts = configuration.Reporting.Accounts.Select(account => new
|
|
{
|
|
accountId = Mask(account.AccountId),
|
|
account.Enabled,
|
|
allowedGroupCount = account.AllowedChats.Count(chat => chat.Type == ReportingChatType.Group && chat.Enabled && chat.IdentityVerified),
|
|
allowedPrivateCount = account.AllowedChats.Count(chat => chat.Type == ReportingChatType.Private && chat.Enabled && chat.IdentityVerified),
|
|
unverifiedCount = account.AllowedChats.Count(chat => !chat.IdentityVerified)
|
|
}),
|
|
audit = configuration.Audit.TakeLast(20)
|
|
};
|
|
}
|
|
|
|
private static bool IsConfigurationCommand(string[] args) => (args[1], args[2]) switch
|
|
{
|
|
("auth", "set" or "clear") => true,
|
|
("reporting", "enable" or "disable" or "account-add" or "account-enable" or "account-disable" or "allow" or "deny") => true,
|
|
_ => false
|
|
};
|
|
|
|
private static string Required(string[] args, string name) => Option(args, name) switch
|
|
{
|
|
{ Length: > 0 } value => value,
|
|
_ => throw Invalid($"Missing {name}.")
|
|
};
|
|
|
|
private static string? Option(string[] args, string name)
|
|
{
|
|
var index = Array.IndexOf(args, name);
|
|
return index >= 0 && index + 1 < args.Length ? args[index + 1] : null;
|
|
}
|
|
|
|
private static bool Has(string[] args, string name) => args.Contains(name, StringComparer.Ordinal);
|
|
|
|
private static string Mask(string value) => value.Length <= 8 ? "<masked>" : value[..4] + "…" + value[^4..];
|
|
|
|
private static WxAgentException Invalid(string message) => new(WxAgentErrorCode.InvalidArgument, message);
|
|
}
|