Add Unity Pipeline package support
This commit is contained in:
@@ -0,0 +1,42 @@
|
||||
using System;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Unity.Pipeline.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Canonical identity of an object an authoring command created or acted on, returned as the
|
||||
/// command result so an agent can reference it in a follow-up call (via <see cref="ObjectRef"/>).
|
||||
/// Asset objects carry assetPath/guid(/fileId); loaded/scene objects carry instanceId/hierarchyPath.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public class AuthoringResult
|
||||
{
|
||||
/// <summary>Canonical GlobalObjectId string for the object.</summary>
|
||||
[JsonProperty("globalId")]
|
||||
public string GlobalId { get; set; }
|
||||
|
||||
/// <summary>Project-relative asset path, if the object is an asset.</summary>
|
||||
[JsonProperty("assetPath")]
|
||||
public string AssetPath { get; set; }
|
||||
|
||||
/// <summary>Asset GUID, if the object is an asset.</summary>
|
||||
[JsonProperty("guid")]
|
||||
public string Guid { get; set; }
|
||||
|
||||
/// <summary>Local file id within the asset, for sub-assets.</summary>
|
||||
[JsonProperty("fileId")]
|
||||
public long? FileId { get; set; }
|
||||
|
||||
/// <summary>Instance id, if the object is loaded/in a scene.</summary>
|
||||
[JsonProperty("instanceId")]
|
||||
public ObjectId? InstanceId { get; set; }
|
||||
|
||||
/// <summary>Scene hierarchy path, for scene objects.</summary>
|
||||
[JsonProperty("hierarchyPath")]
|
||||
public string HierarchyPath { get; set; }
|
||||
|
||||
/// <summary>Object type name (e.g. "GameObject", "Material", "DefaultAsset").</summary>
|
||||
[JsonProperty("type")]
|
||||
public string Type { get; set; }
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6b92c58d435dcee4e8312f67b90af25c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,49 @@
|
||||
using System;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Unity.Pipeline.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Base class for all responses. Serve also as the base class for all errors.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public class BaseResponse
|
||||
{
|
||||
/// <summary>
|
||||
/// Response message if no error
|
||||
/// </summary>
|
||||
[JsonProperty("message")]
|
||||
public string Message { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Error message if the command failed.
|
||||
/// </summary>
|
||||
[JsonProperty("error")]
|
||||
public string Error { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Additional error details for debugging.
|
||||
/// </summary>
|
||||
[JsonProperty("errorDetails")]
|
||||
public string ErrorDetails { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// When was the response created.
|
||||
/// </summary>
|
||||
[JsonProperty("executedAt")]
|
||||
public DateTime ExecutedAt { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Create a failed execution response.
|
||||
/// </summary>
|
||||
public static BaseResponse Failure(string error, string errorDetails)
|
||||
{
|
||||
return new BaseResponse
|
||||
{
|
||||
ExecutedAt = DateTime.UtcNow,
|
||||
Error = error,
|
||||
ErrorDetails = errorDetails
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 00ec05951f34dac4494abb6eb6c90026
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+93
@@ -0,0 +1,93 @@
|
||||
using System;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace Unity.Pipeline.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Request model for /api/exec endpoint.
|
||||
/// Contains command name and parameters for remote command execution.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public class CommandExecutionRequest
|
||||
{
|
||||
/// <summary>
|
||||
/// Name of the command to execute.
|
||||
/// Must match a registered [CliCommand] name.
|
||||
/// </summary>
|
||||
[JsonProperty("command", Required = Required.Always)]
|
||||
public string Command { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Parameters for the command execution.
|
||||
/// Contains key-value pairs matching command parameter names.
|
||||
/// </summary>
|
||||
[JsonProperty("parameters")]
|
||||
public JObject Parameters { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Optional timeout for command execution in milliseconds.
|
||||
/// Default: 60000 (60 seconds)
|
||||
/// </summary>
|
||||
[JsonProperty("timeout")]
|
||||
public int? Timeout { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Create a new command execution request.
|
||||
/// </summary>
|
||||
public CommandExecutionRequest()
|
||||
{
|
||||
Parameters = new JObject();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a command execution request with specified command and parameters.
|
||||
/// </summary>
|
||||
public CommandExecutionRequest(string command, JObject parameters = null)
|
||||
{
|
||||
Command = command ?? throw new ArgumentNullException(nameof(command));
|
||||
Parameters = parameters ?? new JObject();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a parameter value as the specified type.
|
||||
/// Returns the default value if the parameter is not found or cannot be converted.
|
||||
/// </summary>
|
||||
public T GetParameter<T>(string name, T defaultValue = default(T))
|
||||
{
|
||||
if (Parameters == null || !Parameters.ContainsKey(name))
|
||||
return defaultValue;
|
||||
|
||||
try
|
||||
{
|
||||
return Parameters[name].ToObject<T>();
|
||||
}
|
||||
catch
|
||||
{
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check if a parameter exists in the request.
|
||||
/// </summary>
|
||||
public bool HasParameter(string name)
|
||||
{
|
||||
return Parameters != null && Parameters.ContainsKey(name);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Validate the request structure.
|
||||
/// </summary>
|
||||
public string Validate()
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(Command))
|
||||
return "Command name is required";
|
||||
|
||||
if (Timeout.HasValue && Timeout.Value <= 0)
|
||||
return "Timeout must be a positive number";
|
||||
|
||||
return null; // No validation errors
|
||||
}
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: cb08d2e5232819b43bbec0549c02a689
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
using System;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Unity.Pipeline.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Response model for /api/exec endpoint.
|
||||
/// Contains execution result and metadata for remote command execution.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public class CommandExecutionResponse : BaseResponse
|
||||
{
|
||||
/// <summary>
|
||||
/// Whether the command executed successfully.
|
||||
/// </summary>
|
||||
[JsonProperty("success")]
|
||||
public bool Success { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Name of the command that was executed.
|
||||
/// </summary>
|
||||
[JsonProperty("command")]
|
||||
public string Command { get; set; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Result returned by the command (if any).
|
||||
/// </summary>
|
||||
[JsonProperty("result")]
|
||||
public object Result { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Execution duration in milliseconds.
|
||||
/// </summary>
|
||||
[JsonProperty("executionTimeMs")]
|
||||
public long? ExecutionTimeMs { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Create a successful execution response.
|
||||
/// </summary>
|
||||
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
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a failed execution response.
|
||||
/// </summary>
|
||||
public static CommandExecutionResponse CmdFailure(string cmd, string error, string errorDetails)
|
||||
{
|
||||
return new CommandExecutionResponse
|
||||
{
|
||||
Command = cmd,
|
||||
Success = false,
|
||||
ExecutedAt = DateTime.UtcNow,
|
||||
Error = error,
|
||||
ErrorDetails = errorDetails
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1911e492692621e4998635d7ddce0489
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,46 @@
|
||||
using System;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Unity.Pipeline.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// A single captured Unity console entry, as returned by the <c>console</c> command.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public class ConsoleLogEntry
|
||||
{
|
||||
/// <summary>
|
||||
/// Monotonic sequence number assigned when the entry was captured. Acts as the cursor for
|
||||
/// incremental ("--follow") retrieval: pass the highest seq seen back as the <c>since</c>
|
||||
/// parameter to get only newer entries. Never reused, and increasing for the life of the
|
||||
/// buffer (including across domain reloads, since the buffer is persisted).
|
||||
/// </summary>
|
||||
[JsonProperty("seq")]
|
||||
public long Seq { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// When the entry was captured (UTC).
|
||||
/// </summary>
|
||||
[JsonProperty("timestampUtc")]
|
||||
public DateTime TimestampUtc { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Normalized severity: "log", "warn", or "error". Unity's Error/Exception/Assert log types
|
||||
/// all map to "error"; Warning maps to "warn"; Log maps to "log".
|
||||
/// </summary>
|
||||
[JsonProperty("level")]
|
||||
public string Level { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The log message text.
|
||||
/// </summary>
|
||||
[JsonProperty("message")]
|
||||
public string Message { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The stack trace associated with the entry, if any. Empty for most plain logs.
|
||||
/// </summary>
|
||||
[JsonProperty("stackTrace")]
|
||||
public string StackTrace { get; set; }
|
||||
}
|
||||
}
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: bd1bd665815e523ee0da6504d78923df
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
using System;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Unity.Pipeline.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Response model for the <c>console</c> command: a page of captured console entries plus
|
||||
/// the cursor needed to fetch the next page.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public class ConsoleLogResponse
|
||||
{
|
||||
/// <summary>
|
||||
/// The matching entries, oldest first, after applying the level filter, <c>since</c> cursor,
|
||||
/// and <c>tail</c> limit.
|
||||
/// </summary>
|
||||
[JsonProperty("entries")]
|
||||
public ConsoleLogEntry[] Entries { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The highest <see cref="ConsoleLogEntry.Seq"/> currently held in the buffer (regardless of
|
||||
/// the level filter). A "--follow" client passes this back as <c>since</c> on the next poll
|
||||
/// so it resumes exactly where it left off, even if the only new entries were filtered out.
|
||||
/// </summary>
|
||||
[JsonProperty("cursor")]
|
||||
public long Cursor { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Number of entries returned in <see cref="Entries"/>.
|
||||
/// </summary>
|
||||
[JsonProperty("returned")]
|
||||
public int Returned { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// True when the requested <c>since</c> cursor pointed at entries that have already been
|
||||
/// evicted from the bounded buffer, meaning the consumer missed some entries. Lets a
|
||||
/// "--follow" client warn about a gap instead of silently skipping output.
|
||||
/// </summary>
|
||||
[JsonProperty("dropped")]
|
||||
public bool Dropped { get; set; }
|
||||
}
|
||||
}
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3382b91cda66d80db6ab090f9fbeac16
|
||||
@@ -0,0 +1,92 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Unity.Pipeline.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Response from code evaluation commands containing results, output, and diagnostics.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public class EvalResponse :CommandExecutionResponse
|
||||
{
|
||||
/// <summary>
|
||||
/// Console output captured during execution.
|
||||
/// </summary>
|
||||
[JsonProperty("output")]
|
||||
public string Output { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Compilation diagnostics (errors, warnings) from Roslyn.
|
||||
/// </summary>
|
||||
[JsonProperty("diagnostics")]
|
||||
public List<DiagnosticInfo> Diagnostics { get; set; } = new List<DiagnosticInfo>();
|
||||
|
||||
/// <summary>
|
||||
/// Create a successful evaluation response.
|
||||
/// </summary>
|
||||
public static EvalResponse EvalSuccess(object result, string output = null, long executionTimeMs = 0, List<DiagnosticInfo> diagnostics = null)
|
||||
{
|
||||
return new EvalResponse
|
||||
{
|
||||
Success = true,
|
||||
Result = result,
|
||||
Output = output,
|
||||
ExecutionTimeMs = executionTimeMs,
|
||||
Diagnostics = diagnostics ?? new List<DiagnosticInfo>()
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a failed evaluation response.
|
||||
/// </summary>
|
||||
public static EvalResponse EvalFailure(string error, string errorDetails = null, long executionTimeMs = 0, List<DiagnosticInfo> diagnostics = null)
|
||||
{
|
||||
return new EvalResponse
|
||||
{
|
||||
Success = false,
|
||||
Error = error,
|
||||
ErrorDetails = errorDetails,
|
||||
ExecutionTimeMs = executionTimeMs,
|
||||
Diagnostics = diagnostics ?? new List<DiagnosticInfo>()
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compilation diagnostic information from Roslyn.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public class DiagnosticInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// Severity level: "error", "warning", "info".
|
||||
/// </summary>
|
||||
[JsonProperty("severity")]
|
||||
public string Severity { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Diagnostic message text.
|
||||
/// </summary>
|
||||
[JsonProperty("message")]
|
||||
public string Message { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Line number (0-based).
|
||||
/// </summary>
|
||||
[JsonProperty("line")]
|
||||
public int Line { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Column number (0-based).
|
||||
/// </summary>
|
||||
[JsonProperty("column")]
|
||||
public int Column { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Diagnostic identifier (e.g., "CS1002").
|
||||
/// </summary>
|
||||
[JsonProperty("id")]
|
||||
public string Id { get; set; }
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7f93e07b346878148b61c36fd3ece752
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+203
@@ -0,0 +1,203 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Diagnostics;
|
||||
using Newtonsoft.Json;
|
||||
using UnityEngine;
|
||||
using Unity.Pipeline.Security;
|
||||
|
||||
namespace Unity.Pipeline.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Manages instance descriptor files for CLI discovery.
|
||||
/// Written to Library/Pipeline/.unity-pipeline-port (under the project's git-ignored Library
|
||||
/// folder) for CLI tools to find running Editor instances.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public class InstanceDescriptor
|
||||
{
|
||||
private const string DescriptorFileName = ".unity-pipeline-port";
|
||||
|
||||
/// <summary>
|
||||
/// Process ID of the Unity Editor
|
||||
/// </summary>
|
||||
[JsonProperty("pid")]
|
||||
public int Pid { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// HTTP port the pipeline server is listening on
|
||||
/// </summary>
|
||||
[JsonProperty("port")]
|
||||
public int Port { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Full path to the Unity project
|
||||
/// </summary>
|
||||
[JsonProperty("projectPath")]
|
||||
public string ProjectPath { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Name of the Unity project
|
||||
/// </summary>
|
||||
[JsonProperty("projectName")]
|
||||
public string ProjectName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Unity Editor version string
|
||||
/// </summary>
|
||||
[JsonProperty("unityVersion")]
|
||||
public string UnityVersion { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Editor mode: "editor", "batchmode"
|
||||
/// </summary>
|
||||
[JsonProperty("mode")]
|
||||
public string Mode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// When the Editor instance was started
|
||||
/// </summary>
|
||||
[JsonProperty("startedAt")]
|
||||
public DateTime StartedAt { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Last heartbeat timestamp
|
||||
/// </summary>
|
||||
[JsonProperty("lastHeartbeat")]
|
||||
public DateTime LastHeartbeat { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Security token for code evaluation commands
|
||||
/// </summary>
|
||||
[JsonProperty("evalToken")]
|
||||
public string EvalToken { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Create instance descriptor for current Editor session
|
||||
/// </summary>
|
||||
public static InstanceDescriptor CreateCurrent(int port)
|
||||
{
|
||||
try
|
||||
{
|
||||
var projectPath = Path.GetDirectoryName(Application.dataPath);
|
||||
var projectName = Path.GetFileName(projectPath);
|
||||
var pid = Process.GetCurrentProcess().Id;
|
||||
var unityVersion = Application.unityVersion;
|
||||
var isBatchMode = Application.isBatchMode;
|
||||
|
||||
// Generate eval token at server startup for CLI auto-discovery
|
||||
var evalToken = SecurityTokenManager.GetOrCreateToken();
|
||||
|
||||
return new InstanceDescriptor
|
||||
{
|
||||
Pid = pid,
|
||||
Port = port,
|
||||
ProjectPath = projectPath,
|
||||
ProjectName = projectName,
|
||||
UnityVersion = unityVersion,
|
||||
Mode = isBatchMode ? "batchmode" : "editor",
|
||||
StartedAt = DateTime.UtcNow,
|
||||
LastHeartbeat = DateTime.UtcNow,
|
||||
EvalToken = evalToken
|
||||
};
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
UnityEngine.Debug.LogError($"CreateCurrent failed: {ex.Message}");
|
||||
UnityEngine.Debug.LogError($"Stack trace: {ex.StackTrace}");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write instance descriptor to project root
|
||||
/// </summary>
|
||||
public static void WriteToProjectRoot(InstanceDescriptor descriptor)
|
||||
{
|
||||
try
|
||||
{
|
||||
var filePath = GetDescriptorFilePath(descriptor.ProjectPath);
|
||||
var isNewFile = !File.Exists(filePath);
|
||||
// The descriptor lives under Library/Pipeline, which may not exist yet.
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(filePath));
|
||||
var json = JsonConvert.SerializeObject(descriptor, Formatting.Indented);
|
||||
File.WriteAllText(filePath, json);
|
||||
|
||||
// The descriptor carries the auth token; keep it readable only by the current user.
|
||||
// Applied once on creation (heartbeat rewrites preserve the existing permissions).
|
||||
if (isNewFile)
|
||||
FilePermissions.RestrictToCurrentUser(filePath);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
UnityEngine.Debug.LogError($"WriteToProjectRoot failed: {ex.Message}");
|
||||
UnityEngine.Debug.LogError($"Descriptor: {descriptor?.ProjectPath}, PID: {descriptor?.Pid}");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read instance descriptor from project root
|
||||
/// </summary>
|
||||
public static InstanceDescriptor ReadFromProjectRoot(string projectPath)
|
||||
{
|
||||
var filePath = GetDescriptorFilePath(projectPath);
|
||||
|
||||
if (!File.Exists(filePath))
|
||||
return null;
|
||||
|
||||
try
|
||||
{
|
||||
var json = File.ReadAllText(filePath);
|
||||
return JsonConvert.DeserializeObject<InstanceDescriptor>(json);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Invalid or corrupted file
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove instance descriptor from project root
|
||||
/// </summary>
|
||||
public static void RemoveFromProjectRoot(string projectPath)
|
||||
{
|
||||
var filePath = GetDescriptorFilePath(projectPath);
|
||||
|
||||
if (File.Exists(filePath))
|
||||
{
|
||||
try
|
||||
{
|
||||
File.Delete(filePath);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Ignore cleanup errors
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update heartbeat timestamp in existing descriptor file
|
||||
/// </summary>
|
||||
public static void UpdateHeartbeat(string projectPath)
|
||||
{
|
||||
var descriptor = ReadFromProjectRoot(projectPath);
|
||||
if (descriptor != null)
|
||||
{
|
||||
descriptor.LastHeartbeat = DateTime.UtcNow;
|
||||
WriteToProjectRoot(descriptor);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Absolute path to the editor descriptor file for a project: the git-ignored
|
||||
/// Library/Pipeline folder under the project root. Public so discovery code and tests
|
||||
/// derive the location from one place.
|
||||
/// </summary>
|
||||
public static string GetDescriptorFilePath(string projectPath)
|
||||
{
|
||||
return Path.Combine(projectPath, "Library", "Pipeline", DescriptorFileName);
|
||||
}
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c19a2cde67d600341b17d8056b8393e2
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,57 @@
|
||||
using System;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Unity.Pipeline.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Agent-supplied handle that references an existing Unity object — an asset or a
|
||||
/// loaded/scene object. Supply ONE form; <see cref="Unity.Pipeline.Editor.Authoring.ObjectResolver"/>
|
||||
/// tries them in order: globalId, path, guid (+ optional fileId), instanceId, hierarchyPath.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
[JsonConverter(typeof(ObjectRefConverter))]
|
||||
public class ObjectRef
|
||||
{
|
||||
/// <summary>Canonical GlobalObjectId string (addresses assets and scene objects uniformly).</summary>
|
||||
[JsonProperty("globalId")]
|
||||
public string GlobalId { get; set; }
|
||||
|
||||
/// <summary>Project-relative asset path, e.g. "Assets/Foo/Bar.prefab".</summary>
|
||||
[JsonProperty("path")]
|
||||
public string Path { get; set; }
|
||||
|
||||
/// <summary>Asset GUID.</summary>
|
||||
[JsonProperty("guid")]
|
||||
public string Guid { get; set; }
|
||||
|
||||
/// <summary>Local file id, used with <see cref="Guid"/> to address a sub-asset.</summary>
|
||||
[JsonProperty("fileId")]
|
||||
public long? FileId { get; set; }
|
||||
|
||||
/// <summary>Instance id of a loaded/scene object.</summary>
|
||||
[JsonProperty("instanceId")]
|
||||
public ObjectId? InstanceId { get; set; }
|
||||
|
||||
/// <summary>Scene hierarchy path, e.g. "/Root/Child/Leaf".</summary>
|
||||
[JsonProperty("hierarchyPath")]
|
||||
public string HierarchyPath { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
public bool IsEmpty =>
|
||||
string.IsNullOrEmpty(GlobalId) &&
|
||||
string.IsNullOrEmpty(Path) &&
|
||||
string.IsNullOrEmpty(Guid) &&
|
||||
InstanceId == null &&
|
||||
string.IsNullOrEmpty(HierarchyPath);
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
if (!string.IsNullOrEmpty(GlobalId)) return GlobalId;
|
||||
if (!string.IsNullOrEmpty(Path)) return Path;
|
||||
if (!string.IsNullOrEmpty(Guid)) return FileId.HasValue ? $"guid:{Guid}:{FileId}" : $"guid:{Guid}";
|
||||
if (InstanceId.HasValue) return $"instanceId:{InstanceId}";
|
||||
if (!string.IsNullOrEmpty(HierarchyPath)) return HierarchyPath;
|
||||
return "<empty>";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e970b2bf3a890d743bea6785f8af3765
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+188
@@ -0,0 +1,188 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text.RegularExpressions;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace Unity.Pipeline.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// JSON converter for <see cref="ObjectRef"/> that accepts BOTH a structured object
|
||||
/// (all six fields via their JSON property names) AND a single canonical string handle.
|
||||
///
|
||||
/// The MCP tool schemas intentionally expose object-reference parameters as plain strings, so an
|
||||
/// agent typically passes e.g. <c>"target": "/Player"</c> or <c>"target": "GlobalObjectId_V1-..."</c>.
|
||||
/// Without this converter, Newtonsoft's <c>ToObject<ObjectRef>()</c> (used by
|
||||
/// <c>BasePipelineServer.ExtractCommandParameters</c>) gets a JValue(string), returns null, and the
|
||||
/// command fails Required-parameter validation. This converter coerces the string into the matching
|
||||
/// field, mirroring the priority order of <c>ObjectResolver.TryResolve</c> and <c>ObjectRef.ToString()</c>.
|
||||
///
|
||||
/// Registered on the class itself via <c>[JsonConverter(typeof(ObjectRefConverter))]</c> so it applies
|
||||
/// everywhere an <see cref="ObjectRef"/> is (de)serialized — no server or command changes required.
|
||||
/// </summary>
|
||||
public class ObjectRefConverter : JsonConverter<ObjectRef>
|
||||
{
|
||||
private static readonly Regex s_GuidWithFileId = new Regex(@"^guid:([0-9a-fA-F]+):([0-9]+)$", RegexOptions.Compiled);
|
||||
private static readonly Regex s_GuidOnly = new Regex(@"^guid:([0-9a-fA-F]+)$", RegexOptions.Compiled);
|
||||
private static readonly Regex s_InstanceId = new Regex(@"^instanceId:(-?[0-9]+)$", RegexOptions.Compiled);
|
||||
private static readonly Regex s_PlainInt = new Regex(@"^-?[0-9]+$", RegexOptions.Compiled);
|
||||
private static readonly Regex s_Hex32 = new Regex(@"^[0-9a-fA-F]{32}$", RegexOptions.Compiled);
|
||||
|
||||
public override ObjectRef ReadJson(JsonReader reader, Type objectType, ObjectRef existingValue,
|
||||
bool hasExistingValue, JsonSerializer serializer)
|
||||
{
|
||||
var token = JToken.Load(reader);
|
||||
switch (token.Type)
|
||||
{
|
||||
case JTokenType.Null:
|
||||
return null;
|
||||
case JTokenType.String:
|
||||
return FromString((string)token);
|
||||
case JTokenType.Integer:
|
||||
// A bare JSON number is treated as an instance id (e.g. "target": 48184).
|
||||
return new ObjectRef { InstanceId = token.ToObject<ObjectId>() };
|
||||
case JTokenType.Object:
|
||||
return FromObject((JObject)token);
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public override void WriteJson(JsonWriter writer, ObjectRef value, JsonSerializer serializer)
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
writer.WriteNull();
|
||||
return;
|
||||
}
|
||||
|
||||
// Write a standard JSON object (matching the [JsonProperty] names on ObjectRef). Done
|
||||
// manually rather than via serializer.Serialize to avoid re-entering this converter.
|
||||
writer.WriteStartObject();
|
||||
writer.WritePropertyName("globalId");
|
||||
writer.WriteValue(value.GlobalId);
|
||||
writer.WritePropertyName("path");
|
||||
writer.WriteValue(value.Path);
|
||||
writer.WritePropertyName("guid");
|
||||
writer.WriteValue(value.Guid);
|
||||
writer.WritePropertyName("fileId");
|
||||
if (value.FileId.HasValue) writer.WriteValue(value.FileId.Value); else writer.WriteNull();
|
||||
writer.WritePropertyName("instanceId");
|
||||
// Route the id through ObjectIdConverter so it serializes as its raw numeric form
|
||||
// (the 64-bit EntityId on 6000.4+, the int below). Not this converter — no recursion.
|
||||
if (value.InstanceId.HasValue) serializer.Serialize(writer, value.InstanceId.Value); else writer.WriteNull();
|
||||
writer.WritePropertyName("hierarchyPath");
|
||||
writer.WriteValue(value.HierarchyPath);
|
||||
writer.WriteEndObject();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Map a structured JSON object to an ObjectRef by reading each field directly (rather than
|
||||
/// <c>ToObject<ObjectRef>()</c>, which would recurse back into this converter).
|
||||
///
|
||||
/// Keys are matched case-INSENSITIVELY: agents commonly vary the casing (e.g. "instanceID" for
|
||||
/// instanceId, "fileID" for fileId), and because this custom converter bypasses Newtonsoft's
|
||||
/// default case-insensitive property binding, an exact-case lookup would otherwise drop the
|
||||
/// field and yield an empty (silently unresolved) handle.
|
||||
/// </summary>
|
||||
private static ObjectRef FromObject(JObject jo)
|
||||
{
|
||||
return new ObjectRef
|
||||
{
|
||||
GlobalId = Str(jo, "globalId"),
|
||||
Path = Str(jo, "path"),
|
||||
Guid = Str(jo, "guid"),
|
||||
FileId = Long(jo, "fileId"),
|
||||
InstanceId = ObjId(jo, "instanceId"),
|
||||
HierarchyPath = Str(jo, "hierarchyPath"),
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>Find a property value by name, ignoring case (Newtonsoft's JObject lookup is case-sensitive).</summary>
|
||||
private static JToken Get(JObject jo, string key)
|
||||
{
|
||||
foreach (var p in jo.Properties())
|
||||
{
|
||||
if (string.Equals(p.Name, key, StringComparison.OrdinalIgnoreCase))
|
||||
return p.Value;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static string Str(JObject jo, string key)
|
||||
{
|
||||
var t = Get(jo, key);
|
||||
return t == null || t.Type == JTokenType.Null ? null : t.Value<string>();
|
||||
}
|
||||
|
||||
private static ObjectId? ObjId(JObject jo, string key)
|
||||
{
|
||||
var t = Get(jo, key);
|
||||
return t == null || t.Type == JTokenType.Null ? (ObjectId?)null : t.ToObject<ObjectId>();
|
||||
}
|
||||
|
||||
private static long? Long(JObject jo, string key)
|
||||
{
|
||||
var t = Get(jo, key);
|
||||
return t == null || t.Type == JTokenType.Null ? (long?)null : t.Value<long?>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a single canonical string handle into the matching field. Priority order matches
|
||||
/// <c>ObjectResolver.TryResolve</c> / <c>ObjectRef.ToString()</c>; a bare name falls back to
|
||||
/// the hierarchy path (the most common agent input).
|
||||
/// </summary>
|
||||
internal static ObjectRef FromString(string s)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(s))
|
||||
return null;
|
||||
|
||||
s = s.Trim();
|
||||
|
||||
if (s.StartsWith("GlobalObjectId_V1", StringComparison.Ordinal))
|
||||
return new ObjectRef { GlobalId = s };
|
||||
|
||||
// "Assets/..." and "Packages/..." (and the bare roots) are asset paths that
|
||||
// AssetDatabase.LoadMainAssetAtPath can resolve; everything else falls through to the
|
||||
// hierarchy-path fallback at the end.
|
||||
if (s.StartsWith("Assets/", StringComparison.Ordinal)
|
||||
|| s.StartsWith("Packages/", StringComparison.Ordinal)
|
||||
|| s == "Assets" || s == "Packages")
|
||||
return new ObjectRef { Path = s };
|
||||
|
||||
var guidFile = s_GuidWithFileId.Match(s);
|
||||
if (guidFile.Success && long.TryParse(guidFile.Groups[2].Value, out var fileId))
|
||||
return new ObjectRef { Guid = guidFile.Groups[1].Value, FileId = fileId };
|
||||
|
||||
var guidOnly = s_GuidOnly.Match(s);
|
||||
if (guidOnly.Success)
|
||||
return new ObjectRef { Guid = guidOnly.Groups[1].Value };
|
||||
|
||||
var inst = s_InstanceId.Match(s);
|
||||
if (inst.Success && ObjectId.TryParse(inst.Groups[1].Value, out var prefixedId))
|
||||
return new ObjectRef { InstanceId = prefixedId };
|
||||
|
||||
// A plain integer is an instance id. On 6000.0-6000.3 negative ids are valid (unsaved scene
|
||||
// objects); on 6000.4+ the id is the unsigned 64-bit EntityId, so a leading '-' won't parse
|
||||
// and falls through to the hierarchy-path handling below.
|
||||
if (s_PlainInt.IsMatch(s) && ObjectId.TryParse(s, out var plainId))
|
||||
return new ObjectRef { InstanceId = plainId };
|
||||
|
||||
// Exactly 32 hex chars with no other prefix is a bare asset GUID.
|
||||
if (s_Hex32.IsMatch(s))
|
||||
return new ObjectRef { Guid = s };
|
||||
|
||||
// A relative string with a file extension (e.g. "Materials/Floor.mat", "Enemy.prefab") is
|
||||
// most likely an authoring-root-relative asset path — route it to Path so the resolver can
|
||||
// normalize it under the authoring root. A leading "/" always means a scene hierarchy path,
|
||||
// and an extension-less string ("Player", "Root/Child") stays one. Dotted GameObject names
|
||||
// ("Cube.001") land here too, but the resolver falls back to a hierarchy lookup for those.
|
||||
if (!s.StartsWith("/", StringComparison.Ordinal) && Path.HasExtension(s))
|
||||
return new ObjectRef { Path = s };
|
||||
|
||||
// Leading-"/" canonical paths and bare names resolve as a scene hierarchy path.
|
||||
return new ObjectRef { HierarchyPath = s };
|
||||
}
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e31382a242c2a084e97ba76716798aa3
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+203
@@ -0,0 +1,203 @@
|
||||
using System;
|
||||
using Newtonsoft.Json;
|
||||
using UnityEngine;
|
||||
using Unity.Pipeline.Config;
|
||||
using System.IO;
|
||||
using Unity.Pipeline.Security;
|
||||
|
||||
namespace Unity.Pipeline.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Instance descriptor for runtime Unity Player builds with Pipeline server.
|
||||
/// Different from Editor InstanceDescriptor to reflect runtime context and security.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public class RuntimeInstanceDescriptor
|
||||
{
|
||||
private const string RuntimeDescriptorFileName = ".unity-pipeline-runtime-port";
|
||||
|
||||
/// <summary>
|
||||
/// Process ID of the Unity Player application
|
||||
/// </summary>
|
||||
[JsonProperty("pid")]
|
||||
public int Pid { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// HTTP port the pipeline server is listening on (7900-7999 range)
|
||||
/// </summary>
|
||||
[JsonProperty("port")]
|
||||
public int Port { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Unity runtime platform (Windows, macOS, Linux, etc.)
|
||||
/// </summary>
|
||||
[JsonProperty("platform")]
|
||||
public string Platform { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Unity Player version string
|
||||
/// </summary>
|
||||
[JsonProperty("unityVersion")]
|
||||
public string UnityVersion { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Unique build identifier
|
||||
/// </summary>
|
||||
[JsonProperty("buildGuid")]
|
||||
public string BuildGuid { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// When the runtime instance was started
|
||||
/// </summary>
|
||||
[JsonProperty("startedAt")]
|
||||
public DateTime StartedAt { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Last heartbeat timestamp
|
||||
/// </summary>
|
||||
[JsonProperty("lastHeartbeat")]
|
||||
public DateTime LastHeartbeat { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Working directory where the application is running
|
||||
/// </summary>
|
||||
[JsonProperty("workingDirectory")]
|
||||
public string WorkingDirectory { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Security token used to authorize requests to this runtime server.
|
||||
/// </summary>
|
||||
[JsonProperty("evalToken")]
|
||||
public string EvalToken { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Create runtime instance descriptor for current Player application.
|
||||
/// </summary>
|
||||
public static RuntimeInstanceDescriptor CreateCurrent(int port, RuntimePipelineConfig config)
|
||||
{
|
||||
try
|
||||
{
|
||||
var pid = System.Diagnostics.Process.GetCurrentProcess().Id;
|
||||
var unityVersion = Application.unityVersion;
|
||||
var platform = Application.platform.ToString();
|
||||
var buildGuid = Application.buildGUID;
|
||||
var workingDir = Directory.GetCurrentDirectory();
|
||||
|
||||
var token = SecurityTokenManager.GetOrCreateToken();
|
||||
|
||||
return new RuntimeInstanceDescriptor
|
||||
{
|
||||
Pid = pid,
|
||||
Port = port,
|
||||
Platform = platform,
|
||||
UnityVersion = unityVersion,
|
||||
BuildGuid = buildGuid,
|
||||
StartedAt = DateTime.UtcNow,
|
||||
LastHeartbeat = DateTime.UtcNow,
|
||||
WorkingDirectory = workingDir,
|
||||
EvalToken = token
|
||||
};
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
UnityEngine.Debug.LogError($"Pipeline: CreateCurrent failed: {ex.Message}");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write runtime instance descriptor to working directory.
|
||||
/// </summary>
|
||||
public static void WriteToWorkingDirectory(RuntimeInstanceDescriptor descriptor)
|
||||
{
|
||||
try
|
||||
{
|
||||
var filePath = GetDescriptorFilePath();
|
||||
var isNewFile = !File.Exists(filePath);
|
||||
var json = JsonConvert.SerializeObject(descriptor, Formatting.Indented);
|
||||
|
||||
File.WriteAllText(filePath, json);
|
||||
|
||||
// The descriptor carries the auth token; keep it readable only by the current user.
|
||||
// Applied once on creation (heartbeat rewrites preserve the existing permissions).
|
||||
if (isNewFile)
|
||||
FilePermissions.RestrictToCurrentUser(filePath);
|
||||
|
||||
System.Console.WriteLine($"Pipeline: Runtime descriptor written to {filePath}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
UnityEngine.Debug.LogError($"Pipeline: Failed to write runtime descriptor: {ex.Message}");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read runtime instance descriptor from working directory.
|
||||
/// </summary>
|
||||
public static RuntimeInstanceDescriptor ReadFromWorkingDirectory()
|
||||
{
|
||||
var filePath = GetDescriptorFilePath();
|
||||
|
||||
if (!File.Exists(filePath))
|
||||
return null;
|
||||
|
||||
try
|
||||
{
|
||||
var json = File.ReadAllText(filePath);
|
||||
return JsonConvert.DeserializeObject<RuntimeInstanceDescriptor>(json);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
UnityEngine.Debug.LogWarning($"Pipeline: Failed to read runtime descriptor: {ex.Message}");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove runtime instance descriptor from working directory.
|
||||
/// </summary>
|
||||
public static void RemoveFromWorkingDirectory()
|
||||
{
|
||||
var filePath = GetDescriptorFilePath();
|
||||
|
||||
if (File.Exists(filePath))
|
||||
{
|
||||
try
|
||||
{
|
||||
File.Delete(filePath);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
UnityEngine.Debug.LogWarning($"Pipeline: Failed to remove runtime descriptor: {ex.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update heartbeat timestamp in existing descriptor file.
|
||||
/// </summary>
|
||||
public static void UpdateHeartbeat()
|
||||
{
|
||||
try
|
||||
{
|
||||
var descriptor = ReadFromWorkingDirectory();
|
||||
if (descriptor != null)
|
||||
{
|
||||
descriptor.LastHeartbeat = DateTime.UtcNow;
|
||||
WriteToWorkingDirectory(descriptor);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
UnityEngine.Debug.LogWarning($"Pipeline: Failed to update runtime heartbeat: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private static string GetDescriptorFilePath()
|
||||
{
|
||||
var workingDir = new FileInfo($"{Application.dataPath}/..").FullName;
|
||||
return Path.Combine(workingDir, RuntimeDescriptorFileName);
|
||||
}
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a7787403e02096545b7798cd3d5d076d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,55 @@
|
||||
using System;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Unity.Pipeline.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Response model for /api/status endpoint.
|
||||
/// Provides Editor state information for CLI tools and automation scripts.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public class StatusResponse
|
||||
{
|
||||
/// <summary>
|
||||
/// Current Editor status: "ready", "compiling", "busy"
|
||||
/// </summary>
|
||||
[JsonProperty("status")]
|
||||
public string Status { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether Unity is currently compiling scripts
|
||||
/// </summary>
|
||||
[JsonProperty("compiling")]
|
||||
public bool Compiling { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether a domain reload is in progress
|
||||
/// </summary>
|
||||
[JsonProperty("domainReloadInProgress")]
|
||||
public bool DomainReloadInProgress { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Current play mode state: "stopped", "playing", "paused"
|
||||
/// </summary>
|
||||
[JsonProperty("playMode")]
|
||||
public string PlayMode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Timestamp of last heartbeat update
|
||||
/// </summary>
|
||||
[JsonProperty("lastHeartbeat")]
|
||||
public DateTime LastHeartbeat { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Full path to the Unity project
|
||||
/// </summary>
|
||||
[JsonProperty("projectPath")]
|
||||
public string ProjectPath { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Unity Editor version string
|
||||
/// </summary>
|
||||
[JsonProperty("unityVersion")]
|
||||
public string UnityVersion { get; set; }
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 86f7bba2bb7101544a486545cb8e2f70
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Unity.Pipeline.Models;
|
||||
|
||||
namespace Unity.Pipeline
|
||||
{
|
||||
/// <summary>
|
||||
/// Response for test execution commands, containing summary and detailed results
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public class TestExecutionResponse : CommandExecutionResponse
|
||||
{
|
||||
public TestSummary Summary { get; set; }
|
||||
public List<TestResult> Results { get; set; }
|
||||
public double Duration { get; set; }
|
||||
public string StatusPath { get; set; } // For async mode
|
||||
public string Mode { get; set; } // EditMode, PlayMode, or All
|
||||
public string FilterApplied { get; set; } // What filter was used, if any
|
||||
|
||||
public TestExecutionResponse()
|
||||
{
|
||||
Results = new List<TestResult>();
|
||||
Summary = new TestSummary();
|
||||
}
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 27c96238399817b4c8ffb66925bcbc6f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,17 @@
|
||||
using System;
|
||||
|
||||
namespace Unity.Pipeline
|
||||
{
|
||||
/// <summary>
|
||||
/// Individual test result information
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public class TestResult
|
||||
{
|
||||
public string FullName { get; set; }
|
||||
public string Status { get; set; } // Passed, Failed, Skipped, Inconclusive
|
||||
public double Duration { get; set; }
|
||||
public string Message { get; set; }
|
||||
public string StackTrace { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7ccc266e8f58db84dab523cd4dabc489
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,26 @@
|
||||
using System;
|
||||
|
||||
namespace Unity.Pipeline
|
||||
{
|
||||
/// <summary>
|
||||
/// Summary statistics for test execution
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public class TestSummary
|
||||
{
|
||||
public int Total { get; set; }
|
||||
public int Passed { get; set; }
|
||||
public int Failed { get; set; }
|
||||
public int Skipped { get; set; }
|
||||
public int Inconclusive { get; set; }
|
||||
|
||||
public TestSummary()
|
||||
{
|
||||
Total = 0;
|
||||
Passed = 0;
|
||||
Failed = 0;
|
||||
Skipped = 0;
|
||||
Inconclusive = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: df11040e7d969ff4fa8aaa417c44bd44
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user