first commit
This commit is contained in:
@@ -0,0 +1,15 @@
|
||||
# platform/dto
|
||||
|
||||
Request and response DTOs live here. Do not define API request or response structs inside handlers.
|
||||
|
||||
Initial DTO groups:
|
||||
|
||||
- `auth`: login/session payloads.
|
||||
- `users`: user and role management payloads.
|
||||
- `game_plugins`: game management plugin marketplace and installation payloads.
|
||||
- `server_instances`: create server, update config, lifecycle, and detail payloads.
|
||||
- `ai_providers`: AI provider create/update/test/invoke payloads.
|
||||
- `run`: run registration, capability, and status payloads.
|
||||
- `jobs`: job claim, ack, progress, result, cancel, and reconcile payloads.
|
||||
- `artifacts`: chunk upload/download and metadata payloads.
|
||||
- `logs`: ingest, query, tail, and analysis-window payloads.
|
||||
@@ -0,0 +1,108 @@
|
||||
package dto
|
||||
|
||||
import "browser.local/platform/domain"
|
||||
|
||||
type AIInvocationRequest struct {
|
||||
RequestID string `json:"requestId"`
|
||||
PluginID string `json:"pluginId,omitempty"`
|
||||
RouteKey string `json:"routeKey,omitempty"`
|
||||
ServerInstanceID string `json:"serverInstanceId,omitempty"`
|
||||
Purpose string `json:"purpose"`
|
||||
ProviderID string `json:"providerId,omitempty"`
|
||||
Model string `json:"model,omitempty"`
|
||||
Prompt string `json:"prompt"`
|
||||
CurrentConfig string `json:"currentConfig,omitempty"`
|
||||
ContextRefs map[string]string `json:"contextRefs,omitempty"`
|
||||
}
|
||||
|
||||
type LlmConfigSuggestionRequest struct {
|
||||
ServerInstanceID string `json:"serverInstanceId"`
|
||||
Prompt string `json:"prompt"`
|
||||
CurrentConfig string `json:"currentConfig"`
|
||||
}
|
||||
|
||||
type LlmConfigSuggestionResponse struct {
|
||||
ServerInstanceID string `json:"serverInstanceId"`
|
||||
Recommendation string `json:"recommendation"`
|
||||
SuggestedConfig string `json:"suggestedConfig,omitempty"`
|
||||
}
|
||||
|
||||
type AIInvocationUsageResponse struct {
|
||||
ProviderID string `json:"providerId"`
|
||||
Model string `json:"model"`
|
||||
InputTokens int `json:"inputTokens"`
|
||||
OutputTokens int `json:"outputTokens"`
|
||||
Mocked bool `json:"mocked"`
|
||||
}
|
||||
|
||||
type AIConfigRecommendationResponse struct {
|
||||
Key string `json:"key"`
|
||||
SuggestedConfig string `json:"suggestedConfig,omitempty"`
|
||||
DiffSummary string `json:"diffSummary"`
|
||||
}
|
||||
|
||||
type AIInvocationSafeErrorResponse struct {
|
||||
Code string `json:"code"`
|
||||
Message string `json:"message"`
|
||||
Details []string `json:"details,omitempty"`
|
||||
}
|
||||
|
||||
type AIInvocationResponse struct {
|
||||
RequestID string `json:"requestId"`
|
||||
Purpose string `json:"purpose"`
|
||||
ProviderID string `json:"providerId,omitempty"`
|
||||
Model string `json:"model,omitempty"`
|
||||
Status string `json:"status"`
|
||||
Recommendation string `json:"recommendation,omitempty"`
|
||||
ConfigRecommendation *AIConfigRecommendationResponse `json:"configRecommendation,omitempty"`
|
||||
Usage AIInvocationUsageResponse `json:"usage"`
|
||||
Error *AIInvocationSafeErrorResponse `json:"error,omitempty"`
|
||||
}
|
||||
|
||||
func (request AIInvocationRequest) ToDomain() domain.AIInvocationRequest {
|
||||
return domain.AIInvocationRequest{
|
||||
RequestID: request.RequestID,
|
||||
PluginID: request.PluginID,
|
||||
RouteKey: request.RouteKey,
|
||||
ServerInstanceID: request.ServerInstanceID,
|
||||
Purpose: request.Purpose,
|
||||
ProviderID: request.ProviderID,
|
||||
Model: request.Model,
|
||||
Prompt: request.Prompt,
|
||||
CurrentConfig: request.CurrentConfig,
|
||||
ContextRefs: domain.CopyStringMap(request.ContextRefs),
|
||||
}
|
||||
}
|
||||
|
||||
func AIInvocationFromDomain(response domain.AIInvocationResponse) AIInvocationResponse {
|
||||
response = domain.CopyAIInvocationResponse(response)
|
||||
var config *AIConfigRecommendationResponse
|
||||
if response.ConfigRecommendation != nil {
|
||||
config = &AIConfigRecommendationResponse{
|
||||
Key: response.ConfigRecommendation.Key,
|
||||
SuggestedConfig: response.ConfigRecommendation.SuggestedConfig,
|
||||
DiffSummary: response.ConfigRecommendation.DiffSummary,
|
||||
}
|
||||
}
|
||||
var safeError *AIInvocationSafeErrorResponse
|
||||
if response.Error != nil {
|
||||
safeError = &AIInvocationSafeErrorResponse{Code: response.Error.Code, Message: response.Error.Message, Details: response.Error.Details}
|
||||
}
|
||||
return AIInvocationResponse{
|
||||
RequestID: response.RequestID,
|
||||
Purpose: response.Purpose,
|
||||
ProviderID: response.ProviderID,
|
||||
Model: response.Model,
|
||||
Status: response.Status,
|
||||
Recommendation: response.Recommendation,
|
||||
ConfigRecommendation: config,
|
||||
Usage: AIInvocationUsageResponse{
|
||||
ProviderID: response.Usage.ProviderID,
|
||||
Model: response.Usage.Model,
|
||||
InputTokens: response.Usage.InputTokens,
|
||||
OutputTokens: response.Usage.OutputTokens,
|
||||
Mocked: response.Usage.Mocked,
|
||||
},
|
||||
Error: safeError,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
package dto
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"browser.local/platform/domain"
|
||||
)
|
||||
|
||||
type ArtifactDownloadReferenceRequest struct {
|
||||
ArtifactID string `json:"artifactId"`
|
||||
}
|
||||
|
||||
type ArtifactDownloadReferenceResponse struct {
|
||||
ArtifactID string `json:"artifactId"`
|
||||
OwnerKind domain.ArtifactOwnerKind `json:"ownerKind"`
|
||||
OwnerID string `json:"ownerId"`
|
||||
Filename string `json:"filename"`
|
||||
ContentType string `json:"contentType"`
|
||||
SizeBytes int64 `json:"sizeBytes"`
|
||||
Checksum string `json:"checksum"`
|
||||
State domain.ArtifactState `json:"state"`
|
||||
DownloadURL string `json:"downloadUrl"`
|
||||
ExpiresAt time.Time `json:"expiresAt"`
|
||||
RangeSupported bool `json:"rangeSupported"`
|
||||
ChunkSizeBytes int `json:"chunkSizeBytes"`
|
||||
StorageBehavior string `json:"storageBehavior"`
|
||||
}
|
||||
|
||||
type ArtifactContentRequest struct {
|
||||
ArtifactID string `json:"artifactId"`
|
||||
Offset int64 `json:"offset"`
|
||||
Limit int `json:"limit"`
|
||||
}
|
||||
|
||||
type ArtifactTransferProgressResponse struct {
|
||||
ArtifactID string `json:"artifactId"`
|
||||
BytesRead int64 `json:"bytesRead"`
|
||||
TotalSizeBytes int64 `json:"totalSizeBytes"`
|
||||
Complete bool `json:"complete"`
|
||||
}
|
||||
|
||||
type ArtifactDownloadSafeErrorResponse struct {
|
||||
Code string `json:"code"`
|
||||
Message string `json:"message"`
|
||||
Details []string `json:"details,omitempty"`
|
||||
}
|
||||
|
||||
func (request ArtifactDownloadReferenceRequest) ToDomain() domain.ArtifactDownloadReferenceRequest {
|
||||
return domain.ArtifactDownloadReferenceRequest{ArtifactID: request.ArtifactID}
|
||||
}
|
||||
|
||||
func (request ArtifactContentRequest) ToDomain() domain.ArtifactContentRequest {
|
||||
return domain.ArtifactContentRequest{ArtifactID: request.ArtifactID, Offset: request.Offset, Limit: request.Limit}
|
||||
}
|
||||
|
||||
func ArtifactDownloadReferenceFromDomain(reference domain.ArtifactDownloadReference) ArtifactDownloadReferenceResponse {
|
||||
reference = domain.CopyArtifactDownloadReference(reference)
|
||||
return ArtifactDownloadReferenceResponse{
|
||||
ArtifactID: reference.ArtifactID,
|
||||
OwnerKind: reference.OwnerKind,
|
||||
OwnerID: reference.OwnerID,
|
||||
Filename: reference.Filename,
|
||||
ContentType: reference.ContentType,
|
||||
SizeBytes: reference.SizeBytes,
|
||||
Checksum: reference.Checksum,
|
||||
State: reference.State,
|
||||
DownloadURL: reference.DownloadURL,
|
||||
ExpiresAt: reference.ExpiresAt,
|
||||
RangeSupported: reference.RangeSupported,
|
||||
ChunkSizeBytes: reference.ChunkSizeBytes,
|
||||
StorageBehavior: reference.StorageBehavior,
|
||||
}
|
||||
}
|
||||
|
||||
func ArtifactTransferProgressFromDomain(progress domain.ArtifactTransferProgress) ArtifactTransferProgressResponse {
|
||||
progress = domain.CopyArtifactTransferProgress(progress)
|
||||
return ArtifactTransferProgressResponse{
|
||||
ArtifactID: progress.ArtifactID,
|
||||
BytesRead: progress.BytesRead,
|
||||
TotalSizeBytes: progress.TotalSizeBytes,
|
||||
Complete: progress.Complete,
|
||||
}
|
||||
}
|
||||
|
||||
func ArtifactDownloadSafeErrorFromDomain(safeError domain.ArtifactDownloadSafeError) ArtifactDownloadSafeErrorResponse {
|
||||
safeError = domain.CopyArtifactDownloadSafeError(safeError)
|
||||
return ArtifactDownloadSafeErrorResponse{Code: safeError.Code, Message: safeError.Message, Details: safeError.Details}
|
||||
}
|
||||
@@ -0,0 +1,201 @@
|
||||
package dto
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"browser.local/platform/domain"
|
||||
)
|
||||
|
||||
type ArtifactTransferOpenRequest struct {
|
||||
RunEndpointID string `json:"runEndpointId"`
|
||||
SessionToken string `json:"sessionToken"`
|
||||
ArtifactID string `json:"artifactId"`
|
||||
Direction domain.ArtifactTransferDirection `json:"direction"`
|
||||
OwnerKind domain.ArtifactOwnerKind `json:"ownerKind"`
|
||||
OwnerID string `json:"ownerId"`
|
||||
SizeBytes int64 `json:"sizeBytes"`
|
||||
ChunkSizeBytes int `json:"chunkSizeBytes"`
|
||||
Checksum string `json:"checksum"`
|
||||
IdempotencyKey string `json:"idempotencyKey"`
|
||||
}
|
||||
|
||||
type ArtifactTransferOpenResponse struct {
|
||||
Accepted bool `json:"accepted"`
|
||||
TransferID string `json:"transferId"`
|
||||
Direction domain.ArtifactTransferDirection `json:"direction"`
|
||||
Artifact ArtifactResponse `json:"artifact"`
|
||||
TotalChunks int `json:"totalChunks"`
|
||||
ChunkSizeBytes int `json:"chunkSizeBytes"`
|
||||
ReceivedChunkIndexes []int `json:"receivedChunkIndexes"`
|
||||
NextMissingChunkIndex int `json:"nextMissingChunkIndex"`
|
||||
Completed bool `json:"completed"`
|
||||
Duplicate bool `json:"duplicate"`
|
||||
ServerTime time.Time `json:"serverTime"`
|
||||
}
|
||||
|
||||
type ArtifactChunkUploadRequest struct {
|
||||
RunEndpointID string `json:"runEndpointId"`
|
||||
SessionToken string `json:"sessionToken"`
|
||||
TransferID string `json:"transferId"`
|
||||
ArtifactID string `json:"artifactId"`
|
||||
ChunkIndex int `json:"chunkIndex"`
|
||||
Offset int64 `json:"offset"`
|
||||
SizeBytes int `json:"sizeBytes"`
|
||||
Checksum string `json:"checksum"`
|
||||
Payload []byte `json:"payload"`
|
||||
}
|
||||
|
||||
type ArtifactChunkUploadResponse struct {
|
||||
Accepted bool `json:"accepted"`
|
||||
TransferID string `json:"transferId"`
|
||||
ArtifactID string `json:"artifactId"`
|
||||
ChunkIndex int `json:"chunkIndex"`
|
||||
ReceivedChunkIndexes []int `json:"receivedChunkIndexes"`
|
||||
NextMissingChunkIndex int `json:"nextMissingChunkIndex"`
|
||||
Duplicate bool `json:"duplicate"`
|
||||
ServerTime time.Time `json:"serverTime"`
|
||||
}
|
||||
|
||||
type ArtifactTransferStatusRequest struct {
|
||||
RunEndpointID string `json:"runEndpointId"`
|
||||
SessionToken string `json:"sessionToken"`
|
||||
TransferID string `json:"transferId"`
|
||||
ArtifactID string `json:"artifactId"`
|
||||
}
|
||||
|
||||
type ArtifactTransferStatusResponse struct {
|
||||
Accepted bool `json:"accepted"`
|
||||
TransferID string `json:"transferId"`
|
||||
ArtifactID string `json:"artifactId"`
|
||||
Direction domain.ArtifactTransferDirection `json:"direction"`
|
||||
TotalChunks int `json:"totalChunks"`
|
||||
ChunkSizeBytes int `json:"chunkSizeBytes"`
|
||||
ReceivedChunkIndexes []int `json:"receivedChunkIndexes"`
|
||||
NextMissingChunkIndex int `json:"nextMissingChunkIndex"`
|
||||
Completed bool `json:"completed"`
|
||||
ServerTime time.Time `json:"serverTime"`
|
||||
}
|
||||
|
||||
type ArtifactTransferCompleteRequest struct {
|
||||
RunEndpointID string `json:"runEndpointId"`
|
||||
SessionToken string `json:"sessionToken"`
|
||||
TransferID string `json:"transferId"`
|
||||
ArtifactID string `json:"artifactId"`
|
||||
Checksum string `json:"checksum"`
|
||||
SizeBytes int64 `json:"sizeBytes"`
|
||||
}
|
||||
|
||||
type ArtifactTransferCompleteResponse struct {
|
||||
Accepted bool `json:"accepted"`
|
||||
TransferID string `json:"transferId"`
|
||||
Artifact ArtifactResponse `json:"artifact"`
|
||||
Completed bool `json:"completed"`
|
||||
ServerTime time.Time `json:"serverTime"`
|
||||
}
|
||||
|
||||
func (request ArtifactTransferOpenRequest) ToDomain() domain.ArtifactTransferOpen {
|
||||
return domain.ArtifactTransferOpen{
|
||||
RunEndpointID: request.RunEndpointID,
|
||||
SessionToken: request.SessionToken,
|
||||
ArtifactID: request.ArtifactID,
|
||||
Direction: request.Direction,
|
||||
OwnerKind: request.OwnerKind,
|
||||
OwnerID: request.OwnerID,
|
||||
SizeBytes: request.SizeBytes,
|
||||
ChunkSizeBytes: request.ChunkSizeBytes,
|
||||
Checksum: request.Checksum,
|
||||
IdempotencyKey: request.IdempotencyKey,
|
||||
}
|
||||
}
|
||||
|
||||
func (request ArtifactChunkUploadRequest) ToDomain() domain.ArtifactChunkUpload {
|
||||
return domain.ArtifactChunkUpload{
|
||||
RunEndpointID: request.RunEndpointID,
|
||||
SessionToken: request.SessionToken,
|
||||
TransferID: request.TransferID,
|
||||
ArtifactID: request.ArtifactID,
|
||||
ChunkIndex: request.ChunkIndex,
|
||||
Offset: request.Offset,
|
||||
SizeBytes: request.SizeBytes,
|
||||
Checksum: request.Checksum,
|
||||
Payload: domain.CopyBytes(request.Payload),
|
||||
}
|
||||
}
|
||||
|
||||
func (request ArtifactTransferStatusRequest) ToDomain() domain.ArtifactTransferStatusQuery {
|
||||
return domain.ArtifactTransferStatusQuery{
|
||||
RunEndpointID: request.RunEndpointID,
|
||||
SessionToken: request.SessionToken,
|
||||
TransferID: request.TransferID,
|
||||
ArtifactID: request.ArtifactID,
|
||||
}
|
||||
}
|
||||
|
||||
func (request ArtifactTransferCompleteRequest) ToDomain() domain.ArtifactTransferComplete {
|
||||
return domain.ArtifactTransferComplete{
|
||||
RunEndpointID: request.RunEndpointID,
|
||||
SessionToken: request.SessionToken,
|
||||
TransferID: request.TransferID,
|
||||
ArtifactID: request.ArtifactID,
|
||||
Checksum: request.Checksum,
|
||||
SizeBytes: request.SizeBytes,
|
||||
}
|
||||
}
|
||||
|
||||
func ArtifactTransferOpenFromDomain(result domain.ArtifactTransferOpenResult) ArtifactTransferOpenResponse {
|
||||
result = domain.CopyArtifactTransferOpenResult(result)
|
||||
return ArtifactTransferOpenResponse{
|
||||
Accepted: result.Accepted,
|
||||
TransferID: result.TransferID,
|
||||
Direction: result.Direction,
|
||||
Artifact: ArtifactFromDomain(result.Artifact),
|
||||
TotalChunks: result.TotalChunks,
|
||||
ChunkSizeBytes: result.ChunkSizeBytes,
|
||||
ReceivedChunkIndexes: result.ReceivedChunkIndexes,
|
||||
NextMissingChunkIndex: result.NextMissingChunkIndex,
|
||||
Completed: result.Completed,
|
||||
Duplicate: result.Duplicate,
|
||||
ServerTime: result.ServerTime,
|
||||
}
|
||||
}
|
||||
|
||||
func ArtifactChunkUploadFromDomain(result domain.ArtifactChunkUploadResult) ArtifactChunkUploadResponse {
|
||||
result = domain.CopyArtifactChunkUploadResult(result)
|
||||
return ArtifactChunkUploadResponse{
|
||||
Accepted: result.Accepted,
|
||||
TransferID: result.TransferID,
|
||||
ArtifactID: result.ArtifactID,
|
||||
ChunkIndex: result.ChunkIndex,
|
||||
ReceivedChunkIndexes: result.ReceivedChunkIndexes,
|
||||
NextMissingChunkIndex: result.NextMissingChunkIndex,
|
||||
Duplicate: result.Duplicate,
|
||||
ServerTime: result.ServerTime,
|
||||
}
|
||||
}
|
||||
|
||||
func ArtifactTransferStatusFromDomain(result domain.ArtifactTransferStatusResult) ArtifactTransferStatusResponse {
|
||||
result = domain.CopyArtifactTransferStatusResult(result)
|
||||
return ArtifactTransferStatusResponse{
|
||||
Accepted: result.Accepted,
|
||||
TransferID: result.TransferID,
|
||||
ArtifactID: result.ArtifactID,
|
||||
Direction: result.Direction,
|
||||
TotalChunks: result.TotalChunks,
|
||||
ChunkSizeBytes: result.ChunkSizeBytes,
|
||||
ReceivedChunkIndexes: result.ReceivedChunkIndexes,
|
||||
NextMissingChunkIndex: result.NextMissingChunkIndex,
|
||||
Completed: result.Completed,
|
||||
ServerTime: result.ServerTime,
|
||||
}
|
||||
}
|
||||
|
||||
func ArtifactTransferCompleteFromDomain(result domain.ArtifactTransferCompleteResult) ArtifactTransferCompleteResponse {
|
||||
result = domain.CopyArtifactTransferCompleteResult(result)
|
||||
return ArtifactTransferCompleteResponse{
|
||||
Accepted: result.Accepted,
|
||||
TransferID: result.TransferID,
|
||||
Artifact: ArtifactFromDomain(result.Artifact),
|
||||
Completed: result.Completed,
|
||||
ServerTime: result.ServerTime,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
package dto
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"browser.local/platform/domain"
|
||||
)
|
||||
|
||||
type RunCapabilityReport struct {
|
||||
Capabilities []string `json:"capabilities"`
|
||||
Fingerprint string `json:"fingerprint"`
|
||||
}
|
||||
|
||||
type RunControlHelloRequest struct {
|
||||
RegistrationToken string `json:"registrationToken"`
|
||||
RunEndpointID string `json:"runEndpointId"`
|
||||
DisplayName string `json:"displayName"`
|
||||
Version string `json:"version"`
|
||||
Status domain.RunEndpointStatus `json:"status"`
|
||||
Platform string `json:"platform,omitempty"`
|
||||
CapabilityReport RunCapabilityReport `json:"capabilityReport"`
|
||||
Capacity RunCapacityResponse `json:"capacity"`
|
||||
}
|
||||
|
||||
type RunControlHelloResponse struct {
|
||||
Accepted bool `json:"accepted"`
|
||||
RunEndpointID string `json:"runEndpointId"`
|
||||
SessionToken string `json:"sessionToken"`
|
||||
ServerTime time.Time `json:"serverTime"`
|
||||
HeartbeatIntervalSeconds int `json:"heartbeatIntervalSeconds"`
|
||||
FeatureFlags []string `json:"featureFlags,omitempty"`
|
||||
}
|
||||
|
||||
type RunControlHeartbeatRequest struct {
|
||||
RunEndpointID string `json:"runEndpointId"`
|
||||
SessionToken string `json:"sessionToken"`
|
||||
Version string `json:"version"`
|
||||
Status domain.RunEndpointStatus `json:"status"`
|
||||
CapabilityFingerprint string `json:"capabilityFingerprint"`
|
||||
Capacity RunCapacityResponse `json:"capacity"`
|
||||
}
|
||||
|
||||
type RunControlHeartbeatResponse struct {
|
||||
Accepted bool `json:"accepted"`
|
||||
RunEndpointID string `json:"runEndpointId"`
|
||||
NextHeartbeatSeconds int `json:"nextHeartbeatSeconds"`
|
||||
RefreshCapabilities bool `json:"refreshCapabilities"`
|
||||
ServerTime time.Time `json:"serverTime"`
|
||||
}
|
||||
|
||||
func (request RunControlHelloRequest) ToDomain() domain.RunControlHello {
|
||||
return domain.RunControlHello{
|
||||
RegistrationToken: request.RegistrationToken,
|
||||
RunEndpointID: request.RunEndpointID,
|
||||
DisplayName: request.DisplayName,
|
||||
Version: request.Version,
|
||||
Status: request.Status,
|
||||
Platform: request.Platform,
|
||||
CapabilityReport: domain.RunCapabilityReport{
|
||||
Capabilities: domain.CopyStringSlice(request.CapabilityReport.Capabilities),
|
||||
Fingerprint: request.CapabilityReport.Fingerprint,
|
||||
},
|
||||
Capacity: capacityToDomain(request.Capacity),
|
||||
}
|
||||
}
|
||||
|
||||
func (request RunControlHeartbeatRequest) ToDomain() domain.RunControlHeartbeat {
|
||||
return domain.RunControlHeartbeat{
|
||||
RunEndpointID: request.RunEndpointID,
|
||||
SessionToken: request.SessionToken,
|
||||
Version: request.Version,
|
||||
Status: request.Status,
|
||||
CapabilityFingerprint: request.CapabilityFingerprint,
|
||||
Capacity: capacityToDomain(request.Capacity),
|
||||
}
|
||||
}
|
||||
|
||||
func RunControlHelloFromDomain(result domain.RunControlHelloResult) RunControlHelloResponse {
|
||||
result = domain.CopyRunControlHelloResult(result)
|
||||
return RunControlHelloResponse{
|
||||
Accepted: result.Accepted,
|
||||
RunEndpointID: result.RunEndpointID,
|
||||
SessionToken: result.SessionToken,
|
||||
ServerTime: result.ServerTime,
|
||||
HeartbeatIntervalSeconds: result.HeartbeatIntervalSeconds,
|
||||
FeatureFlags: result.FeatureFlags,
|
||||
}
|
||||
}
|
||||
|
||||
func RunControlHeartbeatFromDomain(result domain.RunControlHeartbeatResult) RunControlHeartbeatResponse {
|
||||
return RunControlHeartbeatResponse{
|
||||
Accepted: result.Accepted,
|
||||
RunEndpointID: result.RunEndpointID,
|
||||
NextHeartbeatSeconds: result.NextHeartbeatSeconds,
|
||||
RefreshCapabilities: result.RefreshCapabilities,
|
||||
ServerTime: result.ServerTime,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package dto
|
||||
|
||||
type HealthResponse struct {
|
||||
Service string `json:"service"`
|
||||
Status string `json:"status"`
|
||||
Version string `json:"version"`
|
||||
Time string `json:"time"`
|
||||
}
|
||||
@@ -0,0 +1,317 @@
|
||||
package dto
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"browser.local/platform/domain"
|
||||
)
|
||||
|
||||
type RunJobAssignmentResponse struct {
|
||||
JobID string `json:"jobId"`
|
||||
ServerInstanceID string `json:"serverInstanceId,omitempty"`
|
||||
RunEndpointID string `json:"runEndpointId"`
|
||||
Capability string `json:"capability"`
|
||||
TargetKey string `json:"targetKey,omitempty"`
|
||||
InputRef string `json:"inputRef,omitempty"`
|
||||
IdempotencyKey string `json:"idempotencyKey"`
|
||||
State domain.JobState `json:"state"`
|
||||
Progress JobProgressBody `json:"progress"`
|
||||
ResultRef string `json:"resultRef,omitempty"`
|
||||
LeaseToken string `json:"leaseToken"`
|
||||
Attempt int `json:"attempt"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
UpdatedAt time.Time `json:"updatedAt"`
|
||||
}
|
||||
|
||||
type RunJobClaimRequest struct {
|
||||
RunEndpointID string `json:"runEndpointId"`
|
||||
SessionToken string `json:"sessionToken"`
|
||||
Capabilities []string `json:"capabilities"`
|
||||
Capacity RunCapacityResponse `json:"capacity"`
|
||||
}
|
||||
|
||||
type RunJobClaimResponse struct {
|
||||
Accepted bool `json:"accepted"`
|
||||
RunEndpointID string `json:"runEndpointId"`
|
||||
HasJob bool `json:"hasJob"`
|
||||
Job *RunJobAssignmentResponse `json:"job,omitempty"`
|
||||
NextPollSeconds int `json:"nextPollSeconds"`
|
||||
ServerTime time.Time `json:"serverTime"`
|
||||
}
|
||||
|
||||
type RunJobAckRequest struct {
|
||||
RunEndpointID string `json:"runEndpointId"`
|
||||
SessionToken string `json:"sessionToken"`
|
||||
JobID string `json:"jobId"`
|
||||
LeaseToken string `json:"leaseToken"`
|
||||
Attempt int `json:"attempt"`
|
||||
Message string `json:"message,omitempty"`
|
||||
}
|
||||
|
||||
type RunJobAckResponse struct {
|
||||
Accepted bool `json:"accepted"`
|
||||
Job RunJobAssignmentResponse `json:"job"`
|
||||
ServerTime time.Time `json:"serverTime"`
|
||||
}
|
||||
|
||||
type RunJobProgressRequest struct {
|
||||
RunEndpointID string `json:"runEndpointId"`
|
||||
SessionToken string `json:"sessionToken"`
|
||||
JobID string `json:"jobId"`
|
||||
LeaseToken string `json:"leaseToken"`
|
||||
Attempt int `json:"attempt"`
|
||||
Progress JobProgressBody `json:"progress"`
|
||||
Sequence uint64 `json:"sequence,omitempty"`
|
||||
}
|
||||
|
||||
type RunJobProgressResponse struct {
|
||||
Accepted bool `json:"accepted"`
|
||||
Job RunJobAssignmentResponse `json:"job"`
|
||||
ServerTime time.Time `json:"serverTime"`
|
||||
}
|
||||
|
||||
type RunJobResultRequest struct {
|
||||
RunEndpointID string `json:"runEndpointId"`
|
||||
SessionToken string `json:"sessionToken"`
|
||||
JobID string `json:"jobId"`
|
||||
LeaseToken string `json:"leaseToken"`
|
||||
Attempt int `json:"attempt"`
|
||||
State domain.JobState `json:"state"`
|
||||
Progress JobProgressBody `json:"progress"`
|
||||
ResultRef string `json:"resultRef,omitempty"`
|
||||
Message string `json:"message,omitempty"`
|
||||
ErrorCode string `json:"errorCode,omitempty"`
|
||||
}
|
||||
|
||||
type RunJobResultResponse struct {
|
||||
Accepted bool `json:"accepted"`
|
||||
Job RunJobAssignmentResponse `json:"job"`
|
||||
ServerTime time.Time `json:"serverTime"`
|
||||
}
|
||||
|
||||
type RunJobCancelRequestBody struct {
|
||||
JobID string `json:"jobId"`
|
||||
Reason string `json:"reason"`
|
||||
}
|
||||
|
||||
type RunJobCancelRequestResponse struct {
|
||||
Accepted bool `json:"accepted"`
|
||||
JobID string `json:"jobId"`
|
||||
Reason string `json:"reason"`
|
||||
RequestedAt time.Time `json:"requestedAt"`
|
||||
}
|
||||
|
||||
type RunJobCancelPollRequest struct {
|
||||
RunEndpointID string `json:"runEndpointId"`
|
||||
SessionToken string `json:"sessionToken"`
|
||||
JobID string `json:"jobId,omitempty"`
|
||||
LeaseToken string `json:"leaseToken,omitempty"`
|
||||
}
|
||||
|
||||
type RunJobCancelPollResponse struct {
|
||||
Accepted bool `json:"accepted"`
|
||||
RunEndpointID string `json:"runEndpointId"`
|
||||
HasCancel bool `json:"hasCancel"`
|
||||
JobID string `json:"jobId,omitempty"`
|
||||
Reason string `json:"reason,omitempty"`
|
||||
RequestedAt time.Time `json:"requestedAt,omitempty"`
|
||||
ServerTime time.Time `json:"serverTime"`
|
||||
}
|
||||
|
||||
type RunJobReconcileRequest struct {
|
||||
RunEndpointID string `json:"runEndpointId"`
|
||||
SessionToken string `json:"sessionToken"`
|
||||
ActiveJobIDs []string `json:"activeJobIds"`
|
||||
}
|
||||
|
||||
type RunJobReconcileResponse struct {
|
||||
Accepted bool `json:"accepted"`
|
||||
RunEndpointID string `json:"runEndpointId"`
|
||||
ActiveJobs []RunJobAssignmentResponse `json:"activeJobs"`
|
||||
UnknownJobIDs []string `json:"unknownJobIds"`
|
||||
ServerTime time.Time `json:"serverTime"`
|
||||
}
|
||||
|
||||
func (request RunJobClaimRequest) ToDomain() domain.RunJobClaim {
|
||||
return domain.RunJobClaim{
|
||||
RunEndpointID: request.RunEndpointID,
|
||||
SessionToken: request.SessionToken,
|
||||
Capabilities: domain.CopyStringSlice(request.Capabilities),
|
||||
Capacity: capacityToDomain(request.Capacity),
|
||||
}
|
||||
}
|
||||
|
||||
func (request RunJobAckRequest) ToDomain() domain.RunJobAck {
|
||||
return domain.RunJobAck{
|
||||
RunEndpointID: request.RunEndpointID,
|
||||
SessionToken: request.SessionToken,
|
||||
JobID: request.JobID,
|
||||
LeaseToken: request.LeaseToken,
|
||||
Attempt: request.Attempt,
|
||||
Message: request.Message,
|
||||
}
|
||||
}
|
||||
|
||||
func (request RunJobProgressRequest) ToDomain() domain.RunJobProgress {
|
||||
return domain.RunJobProgress{
|
||||
RunEndpointID: request.RunEndpointID,
|
||||
SessionToken: request.SessionToken,
|
||||
JobID: request.JobID,
|
||||
LeaseToken: request.LeaseToken,
|
||||
Attempt: request.Attempt,
|
||||
Progress: progressReportToDomain(request.Progress),
|
||||
Sequence: request.Sequence,
|
||||
}
|
||||
}
|
||||
|
||||
func (request RunJobResultRequest) ToDomain() domain.RunJobResult {
|
||||
return domain.RunJobResult{
|
||||
RunEndpointID: request.RunEndpointID,
|
||||
SessionToken: request.SessionToken,
|
||||
JobID: request.JobID,
|
||||
LeaseToken: request.LeaseToken,
|
||||
Attempt: request.Attempt,
|
||||
State: request.State,
|
||||
Progress: progressReportToDomain(request.Progress),
|
||||
ResultRef: request.ResultRef,
|
||||
Message: request.Message,
|
||||
ErrorCode: request.ErrorCode,
|
||||
}
|
||||
}
|
||||
|
||||
func (request RunJobCancelRequestBody) ToDomain() domain.RunJobCancelRequest {
|
||||
return domain.RunJobCancelRequest{
|
||||
JobID: request.JobID,
|
||||
Reason: request.Reason,
|
||||
}
|
||||
}
|
||||
|
||||
func (request RunJobCancelPollRequest) ToDomain() domain.RunJobCancelPoll {
|
||||
return domain.RunJobCancelPoll{
|
||||
RunEndpointID: request.RunEndpointID,
|
||||
SessionToken: request.SessionToken,
|
||||
JobID: request.JobID,
|
||||
LeaseToken: request.LeaseToken,
|
||||
}
|
||||
}
|
||||
|
||||
func (request RunJobReconcileRequest) ToDomain() domain.RunJobReconcile {
|
||||
return domain.RunJobReconcile{
|
||||
RunEndpointID: request.RunEndpointID,
|
||||
SessionToken: request.SessionToken,
|
||||
ActiveJobIDs: domain.CopyStringSlice(request.ActiveJobIDs),
|
||||
}
|
||||
}
|
||||
|
||||
func RunJobClaimFromDomain(result domain.RunJobClaimResult) RunJobClaimResponse {
|
||||
result = domain.CopyRunJobClaimResult(result)
|
||||
return RunJobClaimResponse{
|
||||
Accepted: result.Accepted,
|
||||
RunEndpointID: result.RunEndpointID,
|
||||
HasJob: result.HasJob,
|
||||
Job: RunJobAssignmentPtrFromDomain(result.Job),
|
||||
NextPollSeconds: result.NextPollSeconds,
|
||||
ServerTime: result.ServerTime,
|
||||
}
|
||||
}
|
||||
|
||||
func RunJobAckFromDomain(result domain.RunJobAckResult) RunJobAckResponse {
|
||||
return RunJobAckResponse{
|
||||
Accepted: result.Accepted,
|
||||
Job: RunJobAssignmentFromDomain(result.Job),
|
||||
ServerTime: result.ServerTime,
|
||||
}
|
||||
}
|
||||
|
||||
func RunJobProgressFromDomain(result domain.RunJobProgressResult) RunJobProgressResponse {
|
||||
return RunJobProgressResponse{
|
||||
Accepted: result.Accepted,
|
||||
Job: RunJobAssignmentFromDomain(result.Job),
|
||||
ServerTime: result.ServerTime,
|
||||
}
|
||||
}
|
||||
|
||||
func RunJobResultFromDomain(result domain.RunJobResultResult) RunJobResultResponse {
|
||||
return RunJobResultResponse{
|
||||
Accepted: result.Accepted,
|
||||
Job: RunJobAssignmentFromDomain(result.Job),
|
||||
ServerTime: result.ServerTime,
|
||||
}
|
||||
}
|
||||
|
||||
func RunJobCancelRequestFromDomain(result domain.RunJobCancelRequestResult) RunJobCancelRequestResponse {
|
||||
return RunJobCancelRequestResponse{
|
||||
Accepted: result.Accepted,
|
||||
JobID: result.JobID,
|
||||
Reason: result.Reason,
|
||||
RequestedAt: result.RequestedAt,
|
||||
}
|
||||
}
|
||||
|
||||
func RunJobCancelPollFromDomain(result domain.RunJobCancelPollResult) RunJobCancelPollResponse {
|
||||
return RunJobCancelPollResponse{
|
||||
Accepted: result.Accepted,
|
||||
RunEndpointID: result.RunEndpointID,
|
||||
HasCancel: result.HasCancel,
|
||||
JobID: result.JobID,
|
||||
Reason: result.Reason,
|
||||
RequestedAt: result.RequestedAt,
|
||||
ServerTime: result.ServerTime,
|
||||
}
|
||||
}
|
||||
|
||||
func RunJobReconcileFromDomain(result domain.RunJobReconcileResult) RunJobReconcileResponse {
|
||||
result = domain.CopyRunJobReconcileResult(result)
|
||||
items := make([]RunJobAssignmentResponse, len(result.ActiveJobs))
|
||||
for i, assignment := range result.ActiveJobs {
|
||||
items[i] = RunJobAssignmentFromDomain(assignment)
|
||||
}
|
||||
return RunJobReconcileResponse{
|
||||
Accepted: result.Accepted,
|
||||
RunEndpointID: result.RunEndpointID,
|
||||
ActiveJobs: items,
|
||||
UnknownJobIDs: result.UnknownJobIDs,
|
||||
ServerTime: result.ServerTime,
|
||||
}
|
||||
}
|
||||
|
||||
func RunJobAssignmentPtrFromDomain(assignment *domain.RunJobAssignment) *RunJobAssignmentResponse {
|
||||
if assignment == nil {
|
||||
return nil
|
||||
}
|
||||
response := RunJobAssignmentFromDomain(*assignment)
|
||||
return &response
|
||||
}
|
||||
|
||||
func RunJobAssignmentFromDomain(assignment domain.RunJobAssignment) RunJobAssignmentResponse {
|
||||
return RunJobAssignmentResponse{
|
||||
JobID: assignment.JobID,
|
||||
ServerInstanceID: assignment.ServerInstanceID,
|
||||
RunEndpointID: assignment.RunEndpointID,
|
||||
Capability: assignment.Capability,
|
||||
TargetKey: assignment.TargetKey,
|
||||
InputRef: assignment.InputRef,
|
||||
IdempotencyKey: assignment.IdempotencyKey,
|
||||
State: assignment.State,
|
||||
Progress: progressReportFromDomain(assignment.Progress),
|
||||
ResultRef: assignment.ResultRef,
|
||||
LeaseToken: assignment.LeaseToken,
|
||||
Attempt: assignment.Attempt,
|
||||
CreatedAt: assignment.CreatedAt,
|
||||
UpdatedAt: assignment.UpdatedAt,
|
||||
}
|
||||
}
|
||||
|
||||
func progressReportToDomain(progress JobProgressBody) domain.RunJobProgressReport {
|
||||
return domain.RunJobProgressReport{
|
||||
Percent: progress.Percent,
|
||||
Message: progress.Message,
|
||||
}
|
||||
}
|
||||
|
||||
func progressReportFromDomain(progress domain.RunJobProgressReport) JobProgressBody {
|
||||
return JobProgressBody{
|
||||
Percent: progress.Percent,
|
||||
Message: progress.Message,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
package dto
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"browser.local/platform/domain"
|
||||
)
|
||||
|
||||
type LogEntryBody struct {
|
||||
Seq uint64 `json:"seq"`
|
||||
Timestamp time.Time `json:"timestamp"`
|
||||
Level string `json:"level,omitempty"`
|
||||
Line string `json:"line"`
|
||||
Fields map[string]string `json:"fields,omitempty"`
|
||||
Redacted bool `json:"redacted"`
|
||||
}
|
||||
|
||||
type LogBatchIngestRequest struct {
|
||||
RunEndpointID string `json:"runEndpointId"`
|
||||
SessionToken string `json:"sessionToken"`
|
||||
LogStreamID string `json:"logStreamId"`
|
||||
ServerInstanceID string `json:"serverInstanceId"`
|
||||
StreamKey string `json:"streamKey"`
|
||||
Source domain.LogStreamSource `json:"source"`
|
||||
FirstSeq uint64 `json:"firstSeq"`
|
||||
LastSeq uint64 `json:"lastSeq"`
|
||||
Compression string `json:"compression"`
|
||||
Checksum string `json:"checksum"`
|
||||
Entries []LogEntryBody `json:"entries"`
|
||||
}
|
||||
|
||||
type LogBatchIngestResponse struct {
|
||||
Accepted bool `json:"accepted"`
|
||||
LogStreamID string `json:"logStreamId"`
|
||||
AcceptedFrom uint64 `json:"acceptedFrom"`
|
||||
AcceptedTo uint64 `json:"acceptedTo"`
|
||||
LatestSeq uint64 `json:"latestSeq"`
|
||||
Duplicate bool `json:"duplicate"`
|
||||
RetryAfterSec int `json:"retryAfterSec,omitempty"`
|
||||
ServerTime time.Time `json:"serverTime"`
|
||||
}
|
||||
|
||||
type LogStreamCursorRequest struct {
|
||||
LogStreamID string `json:"logStreamId"`
|
||||
AfterSeq uint64 `json:"afterSeq"`
|
||||
Limit int `json:"limit"`
|
||||
}
|
||||
|
||||
type LogStreamCursorResponse struct {
|
||||
LogStreamID string `json:"logStreamId"`
|
||||
Entries []LogEntryBody `json:"entries"`
|
||||
NextSeq uint64 `json:"nextSeq"`
|
||||
LatestSeq uint64 `json:"latestSeq"`
|
||||
}
|
||||
|
||||
func (request LogBatchIngestRequest) ToDomain() domain.LogBatchIngest {
|
||||
return domain.LogBatchIngest{
|
||||
RunEndpointID: request.RunEndpointID,
|
||||
SessionToken: request.SessionToken,
|
||||
LogStreamID: request.LogStreamID,
|
||||
ServerInstanceID: request.ServerInstanceID,
|
||||
StreamKey: request.StreamKey,
|
||||
Source: request.Source,
|
||||
FirstSeq: request.FirstSeq,
|
||||
LastSeq: request.LastSeq,
|
||||
Compression: request.Compression,
|
||||
Checksum: request.Checksum,
|
||||
Entries: logEntriesToDomain(request.Entries),
|
||||
}
|
||||
}
|
||||
|
||||
func (request LogStreamCursorRequest) ToDomain() domain.LogStreamCursorQuery {
|
||||
return domain.LogStreamCursorQuery{
|
||||
LogStreamID: request.LogStreamID,
|
||||
AfterSeq: request.AfterSeq,
|
||||
Limit: request.Limit,
|
||||
}
|
||||
}
|
||||
|
||||
func LogBatchIngestFromDomain(result domain.LogBatchIngestResult) LogBatchIngestResponse {
|
||||
return LogBatchIngestResponse{
|
||||
Accepted: result.Accepted,
|
||||
LogStreamID: result.LogStreamID,
|
||||
AcceptedFrom: result.AcceptedFrom,
|
||||
AcceptedTo: result.AcceptedTo,
|
||||
LatestSeq: result.LatestSeq,
|
||||
Duplicate: result.Duplicate,
|
||||
ServerTime: result.ServerTime,
|
||||
}
|
||||
}
|
||||
|
||||
func LogStreamCursorFromDomain(result domain.LogStreamCursorResult) LogStreamCursorResponse {
|
||||
result = domain.CopyLogStreamCursorResult(result)
|
||||
return LogStreamCursorResponse{
|
||||
LogStreamID: result.LogStreamID,
|
||||
Entries: logEntriesFromDomain(result.Entries),
|
||||
NextSeq: result.NextSeq,
|
||||
LatestSeq: result.LatestSeq,
|
||||
}
|
||||
}
|
||||
|
||||
func logEntriesToDomain(entries []LogEntryBody) []domain.LogEntry {
|
||||
if entries == nil {
|
||||
return nil
|
||||
}
|
||||
out := make([]domain.LogEntry, len(entries))
|
||||
for i, entry := range entries {
|
||||
out[i] = domain.LogEntry{
|
||||
Seq: entry.Seq,
|
||||
Timestamp: entry.Timestamp,
|
||||
Level: entry.Level,
|
||||
Line: entry.Line,
|
||||
Fields: copyStringMap(entry.Fields),
|
||||
Redacted: entry.Redacted,
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func logEntriesFromDomain(entries []domain.LogEntry) []LogEntryBody {
|
||||
if entries == nil {
|
||||
return nil
|
||||
}
|
||||
out := make([]LogEntryBody, len(entries))
|
||||
for i, entry := range entries {
|
||||
out[i] = LogEntryBody{
|
||||
Seq: entry.Seq,
|
||||
Timestamp: entry.Timestamp,
|
||||
Level: entry.Level,
|
||||
Line: entry.Line,
|
||||
Fields: copyStringMap(entry.Fields),
|
||||
Redacted: entry.Redacted,
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func copyStringMap(values map[string]string) map[string]string {
|
||||
if values == nil {
|
||||
return nil
|
||||
}
|
||||
out := make(map[string]string, len(values))
|
||||
for key, value := range values {
|
||||
out[key] = value
|
||||
}
|
||||
return out
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,112 @@
|
||||
package dto
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"browser.local/platform/domain"
|
||||
)
|
||||
|
||||
func TestAIProviderResponseExposesOnlyKeyReference(t *testing.T) {
|
||||
responseType := reflect.TypeOf(AIProviderResponse{})
|
||||
if _, ok := responseType.FieldByName("APIKey"); ok {
|
||||
t.Fatal("AI provider response must not expose raw API key")
|
||||
}
|
||||
if _, ok := responseType.FieldByName("RawAPIKey"); ok {
|
||||
t.Fatal("AI provider response must not expose raw API key")
|
||||
}
|
||||
if _, ok := responseType.FieldByName("APIKeyRef"); !ok {
|
||||
t.Fatal("AI provider response must expose API key reference")
|
||||
}
|
||||
}
|
||||
|
||||
func TestAIProviderFromDomainCopiesModels(t *testing.T) {
|
||||
provider := domain.AIProvider{
|
||||
ID: "ai.openai",
|
||||
Name: "OpenAI",
|
||||
Kind: domain.AIProviderKindOpenAI,
|
||||
BaseURL: "https://api.openai.com/v1",
|
||||
APIKeyRef: "secret://providers/openai",
|
||||
Models: []string{"gpt-4.1"},
|
||||
DefaultModel: "gpt-4.1",
|
||||
RelayMode: domain.AIRelayModeDirect,
|
||||
TimeoutMS: 30000,
|
||||
Status: domain.AIProviderStatusActive,
|
||||
RedactionPolicy: "default",
|
||||
}
|
||||
|
||||
response := AIProviderFromDomain(provider)
|
||||
response.Models[0] = "mutated"
|
||||
|
||||
if provider.Models[0] != "gpt-4.1" {
|
||||
t.Fatalf("expected response models to be copied, got source models %+v", provider.Models)
|
||||
}
|
||||
if response.APIKeyRef != provider.APIKeyRef {
|
||||
t.Fatalf("expected API key reference to be preserved, got %q", response.APIKeyRef)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGamePluginManifestRegistrationToDomainCopiesSlices(t *testing.T) {
|
||||
request := GamePluginManifestRegistrationRequest{
|
||||
ManifestRef: "artifact://manifests/game.example/0.1.0",
|
||||
Manifest: GamePluginManifestBody{
|
||||
ID: "game.example",
|
||||
Name: "Example Server",
|
||||
Version: "0.1.0",
|
||||
Kind: "game-plugin",
|
||||
Tags: []string{"example"},
|
||||
Capabilities: []string{"process.start"},
|
||||
Permissions: []string{"server.lifecycle"},
|
||||
Server: GamePluginManifestServerBody{
|
||||
Type: "example",
|
||||
DisplayName: "Example Server",
|
||||
SupportedOS: []string{"linux"},
|
||||
CreateFormSchema: "schemas/create-form.schema.json",
|
||||
},
|
||||
Actions: PluginLifecycleActionsBody{Install: "actions/install.json", Start: "actions/start.json", Stop: "actions/stop.json"},
|
||||
Pages: []GamePluginPageBody{
|
||||
{Key: "logs", Title: "Logs", Path: "/logs", Permissions: []string{"server.logs.read"}},
|
||||
},
|
||||
AI: GamePluginManifestAIBody{Purposes: []string{"logs.diagnose"}},
|
||||
},
|
||||
}
|
||||
|
||||
domainRegistration := request.ToDomain()
|
||||
domainRegistration.Manifest.Tags[0] = "mutated"
|
||||
domainRegistration.Manifest.Server.SupportedOS[0] = "darwin"
|
||||
domainRegistration.Manifest.Pages[0].Permissions[0] = "ai.invoke"
|
||||
domainRegistration.Manifest.AI.Purposes[0] = "config.suggest"
|
||||
|
||||
if request.Manifest.Tags[0] != "example" || request.Manifest.Server.SupportedOS[0] != "linux" || request.Manifest.Pages[0].Permissions[0] != "server.logs.read" || request.Manifest.AI.Purposes[0] != "logs.diagnose" {
|
||||
t.Fatalf("expected manifest request slices to be copied, got %+v", request)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGamePluginFromDomainCopiesRegistryMetadata(t *testing.T) {
|
||||
plugin := domain.GamePlugin{
|
||||
ID: "game.example",
|
||||
Name: "Example Server",
|
||||
Version: "0.1.0",
|
||||
ServerType: "example",
|
||||
RequiredRunCapabilities: []string{"process.start"},
|
||||
DeclaredPermissions: []string{"server.lifecycle"},
|
||||
SupportedOS: []string{"linux"},
|
||||
Pages: []domain.GamePluginPage{
|
||||
{Key: "logs", Title: "Logs", Path: "/logs", Permissions: []string{"server.logs.read"}},
|
||||
},
|
||||
Tags: []string{"example"},
|
||||
AIPurposes: []string{"logs.diagnose"},
|
||||
}
|
||||
|
||||
response := GamePluginFromDomain(plugin)
|
||||
response.RequiredRunCapabilities[0] = "files.read"
|
||||
response.DeclaredPermissions[0] = "ai.invoke"
|
||||
response.SupportedOS[0] = "darwin"
|
||||
response.Pages[0].Permissions[0] = "ai.invoke"
|
||||
response.Tags[0] = "mutated"
|
||||
response.AIPurposes[0] = "config.suggest"
|
||||
|
||||
if plugin.RequiredRunCapabilities[0] != "process.start" || plugin.DeclaredPermissions[0] != "server.lifecycle" || plugin.SupportedOS[0] != "linux" || plugin.Pages[0].Permissions[0] != "server.logs.read" || plugin.Tags[0] != "example" || plugin.AIPurposes[0] != "logs.diagnose" {
|
||||
t.Fatalf("expected plugin response registry metadata to be copied, got %+v", plugin)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
package dto
|
||||
|
||||
import "browser.local/platform/domain"
|
||||
|
||||
type ServerLifecycleCreateRequest struct {
|
||||
ID string `json:"id"`
|
||||
PluginID string `json:"pluginId"`
|
||||
RunEndpointID string `json:"runEndpointId"`
|
||||
Name string `json:"name"`
|
||||
OwnerUserID string `json:"ownerUserId,omitempty"`
|
||||
IdempotencyKey string `json:"idempotencyKey"`
|
||||
}
|
||||
|
||||
type ServerLifecycleCommandRequest struct {
|
||||
ExpectedConfigVersion int `json:"expectedConfigVersion"`
|
||||
IdempotencyKey string `json:"idempotencyKey"`
|
||||
}
|
||||
|
||||
type ServerLifecycleResponse struct {
|
||||
Accepted bool `json:"accepted"`
|
||||
Action domain.ServerLifecycleAction `json:"action"`
|
||||
Instance ServerInstanceResponse `json:"instance"`
|
||||
Job JobResponse `json:"job"`
|
||||
}
|
||||
|
||||
func (request ServerLifecycleCreateRequest) ToDomain() domain.ServerLifecycleCreate {
|
||||
return domain.ServerLifecycleCreate{
|
||||
ID: request.ID,
|
||||
PluginID: request.PluginID,
|
||||
RunEndpointID: request.RunEndpointID,
|
||||
Name: request.Name,
|
||||
OwnerUserID: request.OwnerUserID,
|
||||
IdempotencyKey: request.IdempotencyKey,
|
||||
}
|
||||
}
|
||||
|
||||
func (request ServerLifecycleCommandRequest) ToDomain(serverInstanceID string) domain.ServerLifecycleCommand {
|
||||
return domain.ServerLifecycleCommand{
|
||||
ServerInstanceID: serverInstanceID,
|
||||
ExpectedConfigVersion: request.ExpectedConfigVersion,
|
||||
IdempotencyKey: request.IdempotencyKey,
|
||||
}
|
||||
}
|
||||
|
||||
func ServerLifecycleFromDomain(result domain.ServerLifecycleResult) ServerLifecycleResponse {
|
||||
result = domain.CopyServerLifecycleResult(result)
|
||||
return ServerLifecycleResponse{
|
||||
Accepted: result.Accepted,
|
||||
Action: result.Action,
|
||||
Instance: ServerInstanceFromDomain(result.Instance),
|
||||
Job: JobFromDomain(result.Job),
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user