using System; using System.Collections.Generic; using Newtonsoft.Json; using Newtonsoft.Json.Linq; namespace Unity.Pipeline.Models { /// /// Response model for /api/exec endpoint. /// Contains execution result and metadata for remote command execution. /// [Serializable] public class CommandExecutionResponse : BaseResponse { /// /// Whether the command executed successfully. /// [JsonProperty("success")] public bool Success { get; set; } /// /// Name of the command that was executed. /// [JsonProperty("command")] public string Command { get; set; } /// /// Result returned by the command (if any). /// [JsonProperty("result")] public object Result { get; set; } /// /// Execution duration in milliseconds. /// /// /// Like command and executedAt, emitted on the /api/exec wire only in verbose /// mode — the gating lives in ExecResponseSerializer, not on this model. (AUTHAPI-21) /// [JsonProperty("executionTimeMs")] public long? ExecutionTimeMs { get; set; } /// /// Coarse machine-readable server state marker. Set to "busy" when the command was not /// executed because the server's host cannot service it yet (e.g. the Editor is still /// settling after a cold start). Omitted from the JSON on ordinary success/failure /// responses, so existing envelopes are unchanged. /// [JsonProperty("status", NullValueHandling = NullValueHandling.Ignore)] public string Status { get; set; } /// /// True when the failure is transient and the same request is expected to succeed after a /// short wait (poll /api/status until it reports "ready"). Omitted from the JSON on /// ordinary success/failure responses. /// [JsonProperty("retryable", NullValueHandling = NullValueHandling.Ignore)] public bool? Retryable { get; set; } /// /// Stable, machine-readable failure discriminator (e.g. INVALID_COMMAND_ARGS). /// /// HTTP status cannot serve as the discriminator here: every /api/exec failure is 400, so /// status alone cannot separate "your arguments are wrong" from "the command threw". /// Spelled to match the unity CLI's own InvalidArgsCode literal, so neither side /// needs a translation table. /// [JsonProperty("errorCode", NullValueHandling = NullValueHandling.Ignore)] public string ErrorCode { get; set; } /// /// Every argument-binding defect found, one entry each — the binder accumulates rather /// than bailing, so a user fixing a command line sees all of its problems at once. /// Machine-readable rather than prose, because the unity CLI renders and localizes /// these itself; carries the English fallback for /// clients that do not. /// [JsonProperty("argProblems", NullValueHandling = NullValueHandling.Ignore)] public List ArgProblems { get; set; } /// /// The resolved command's catalog entry, shaped like a /api/commands commands[] /// element. Sent with an argument error so a client can render usage without a separate /// schema request. Free to produce - the command is already resolved at that point. /// [JsonProperty("commandSchema", NullValueHandling = NullValueHandling.Ignore)] public JObject CommandSchema { get; set; } /// /// What the server bound from a raw command line. Set ONLY for argv/commandLine requests, /// leaving the structured path's envelope unchanged. A client that does not bind locally /// has no other way to report what the command actually received. /// [JsonProperty("parameters", NullValueHandling = NullValueHandling.Ignore)] public JObject BoundParameters { get; set; } /// /// Create the argument-binding failure response (sent with HTTP 400): the command was /// resolved but its arguments could not be bound, so nothing executed. /// internal static CommandExecutionResponse CmdInvalidArgs(string cmd, string details, List problems, JObject commandSchema) { return new CommandExecutionResponse { Command = cmd, Success = false, ExecutedAt = DateTime.UtcNow, Error = "Invalid Command Arguments", ErrorDetails = details, ErrorCode = "INVALID_COMMAND_ARGS", ArgProblems = problems, CommandSchema = commandSchema }; } /// /// The specific cause of a "busy" response, matching /api/status's own "status" values for /// the same conditions ("settling", "blocked_by_dialog") — a caller can branch on this /// directly instead of inferring the cause from whether "dialogs" happens to be populated. /// Omitted from the JSON on ordinary success/failure responses. /// [JsonProperty("busyReason", NullValueHandling = NullValueHandling.Ignore)] public string BusyReason { get; set; } /// /// Dialogs (see EditorDialogEvents) that opened at or after this call started. Omitted /// when none occurred, so existing envelopes are unchanged. Lets a caller learn "this /// call took 40s because a dialog was shown" even without polling /api/dialog /// concurrently. /// [JsonProperty("dialogsDuringExecution", NullValueHandling = NullValueHandling.Ignore)] public List DialogsDuringExecution { get; set; } /// /// Currently-open dialog(s) (see EditorDialogEvents), attached to a busy response when a /// MainThreadRequired command was rejected because a modal dialog is blocking the main /// thread. Omitted otherwise. /// [JsonProperty("dialogs", NullValueHandling = NullValueHandling.Ignore)] public List Dialogs { get; set; } /// /// Create a successful execution response. /// /// Name of the executed command. /// The command's result payload. /// How long the command took to execute, in milliseconds. /// A successful response wrapping . public static CommandExecutionResponse CmdSuccess(string command, object result = null, long? executionTimeMs = null) { return new CommandExecutionResponse { Success = true, Command = command, ExecutedAt = DateTime.UtcNow, Result = result, ExecutionTimeMs = executionTimeMs }; } /// /// Create a failed execution response. /// /// Name of the executed command. /// Error message. /// Additional error details for debugging. /// A failed response with / set. public static CommandExecutionResponse CmdFailure(string cmd, string error, string errorDetails) { return new CommandExecutionResponse { Command = cmd, Success = false, ExecutedAt = DateTime.UtcNow, Error = error, ErrorDetails = errorDetails }; } /// /// Create the retryable "server busy" response (sent with HTTP 503): the command was not /// executed because the host cannot service it yet — distinguishable from a genuine /// command failure via status/retryable so callers know to retry shortly instead of /// guessing (AUTHAPI-35). is the specific cause ("settling", /// "blocked_by_dialog", ...); omit it only for a generic/legacy busy state. /// /// Name of the command that could not be serviced yet. /// Human-readable reason the host is busy. /// The specific cause of the busy state, or null for a generic/legacy busy state. /// A retryable "server busy" response. public static CommandExecutionResponse CmdBusy(string cmd, string details, string busyReason = null) { return new CommandExecutionResponse { Command = cmd, Success = false, ExecutedAt = DateTime.UtcNow, Error = "Server Busy", ErrorDetails = details, Status = "busy", Retryable = true, BusyReason = busyReason }; } } }