first commit

This commit is contained in:
npc0-hue
2026-07-11 14:56:10 +08:00
commit 7e05d0a4e7
660 changed files with 78119 additions and 0 deletions
+15
View File
@@ -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.
+108
View File
@@ -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,
}
}
+88
View File
@@ -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}
}
+201
View File
@@ -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,
}
}
+98
View File
@@ -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,
}
}
+8
View File
@@ -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"`
}
+317
View File
@@ -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,
}
}
+147
View File
@@ -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
+112
View File
@@ -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)
}
}
+53
View File
@@ -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),
}
}