feat: 完整游戏运维功能

This commit is contained in:
npc0-hue
2026-07-18 09:04:01 +08:00
parent f3b14b7945
commit 48b8ad8d6c
187 changed files with 16607 additions and 1140 deletions
+247 -42
View File
@@ -31,6 +31,44 @@ type User struct {
func (User) TableName() string { return "users" }
type AuthSession struct {
// ID is a non-secret stable session record identifier.
ID string `json:"id" db:"id"`
// UserID owns the authenticated session.
UserID string `json:"userId" db:"user_id"`
// TokenHash is a one-way verifier; the bearer token is never persisted.
TokenHash string `json:"tokenHash" db:"token_hash"`
// Status tracks active or revoked lifecycle state.
Status domain.AuthSessionStatus `json:"status" db:"status"`
// Generation increments when a user rotates a session.
Generation int `json:"generation" db:"generation"`
IssuedAt time.Time `json:"issuedAt" db:"issued_at"`
ExpiresAt time.Time `json:"expiresAt" db:"expires_at"`
LastSeenAt time.Time `json:"lastSeenAt" db:"last_seen_at"`
RevokedAt time.Time `json:"revokedAt,omitempty" db:"revoked_at"`
}
func (AuthSession) TableName() string { return "auth_sessions" }
type RunControlSession struct {
// RunEndpointID is both the endpoint owner and stable session record ID.
RunEndpointID string `json:"runEndpointId" db:"run_endpoint_id"`
// SessionTokenHash is a one-way verifier; raw Run tokens are never persisted.
SessionTokenHash string `json:"sessionTokenHash" db:"session_token_hash"`
Status domain.AuthSessionStatus `json:"status" db:"status"`
Generation int `json:"generation" db:"generation"`
CapabilityFingerprint string `json:"capabilityFingerprint" db:"capability_fingerprint"`
HeartbeatIntervalSeconds int `json:"heartbeatIntervalSeconds" db:"heartbeat_interval_seconds"`
CreatedAt time.Time `json:"createdAt" db:"created_at"`
UpdatedAt time.Time `json:"updatedAt" db:"updated_at"`
ExpiresAt time.Time `json:"expiresAt" db:"expires_at"`
RevokedAt time.Time `json:"revokedAt,omitempty" db:"revoked_at"`
RequireSignedRequests bool `json:"requireSignedRequests" db:"require_signed_requests"`
UsedNonces []string `json:"usedNonces,omitempty" db:"used_nonces"`
}
func (RunControlSession) TableName() string { return "run_control_sessions" }
type AIProvider struct {
// ID is the stable AI provider identifier.
ID string `json:"id" db:"id"`
@@ -145,6 +183,8 @@ type GamePlugin struct {
AIPurposes []string `json:"aiPurposes" db:"ai_purposes"`
// RemoteAccess stores plugin-declared remote access metadata.
RemoteAccess GamePluginRemoteAccess `json:"remoteAccess" db:"remote_access"`
// RuntimeProfiles stores validated manifest-declared runtime contracts.
RuntimeProfiles domain.GamePluginRuntimeProfiles `json:"runtimeProfiles" db:"runtime_profiles"`
// ValidationViolations stores safe validation findings for invalid plugins.
ValidationViolations []string `json:"validationViolations" db:"validation_violations"`
// Status is the plugin lifecycle status.
@@ -172,6 +212,14 @@ type ServerInstance struct {
State domain.ServerInstanceState `json:"state" db:"state"`
// ConfigVersion is the platform-managed optimistic concurrency version.
ConfigVersion int `json:"configVersion" db:"config_version"`
// ConfigKey is the logical configuration target, never a host path.
ConfigKey string `json:"configKey,omitempty" db:"config_key"`
// ConfigContent is the last platform-approved bounded configuration body.
ConfigContent string `json:"configContent,omitempty" db:"config_content"`
// ConfigChecksum is the SHA-256 checksum of ConfigContent.
ConfigChecksum string `json:"configChecksum,omitempty" db:"config_checksum"`
// ConfigUpdatedAt records the last accepted config execution result.
ConfigUpdatedAt time.Time `json:"configUpdatedAt,omitempty" db:"config_updated_at"`
// CreatedAt is the record creation timestamp.
CreatedAt time.Time `json:"createdAt" db:"created_at"`
// UpdatedAt is the last update timestamp.
@@ -198,6 +246,10 @@ type RunEndpoint struct {
DisplayName string `json:"displayName" db:"display_name"`
// Version is the run binary version.
Version string `json:"version" db:"version"`
// Platform is the endpoint operating system.
Platform string `json:"platform" db:"platform"`
// Architecture is the endpoint CPU architecture.
Architecture string `json:"architecture" db:"architecture"`
// Status is the current endpoint status.
Status domain.RunEndpointStatus `json:"status" db:"status"`
// Capabilities lists advertised run capability keys.
@@ -217,6 +269,38 @@ type JobProgress struct {
Message string `json:"message,omitempty" db:"message"`
}
type JobRetryPolicy struct {
// MaxAttempts bounds total claims, including the first attempt.
MaxAttempts int `json:"maxAttempts" db:"max_attempts"`
// InitialBackoffSeconds is the first retry delay.
InitialBackoffSeconds int `json:"initialBackoffSeconds" db:"initial_backoff_seconds"`
// MaxBackoffSeconds caps exponential retry delay.
MaxBackoffSeconds int `json:"maxBackoffSeconds" db:"max_backoff_seconds"`
}
type JobExecutionInput struct {
WorkspaceScope string `json:"workspaceScope,omitempty" db:"workspace_scope"`
Content string `json:"content,omitempty" db:"content"`
ExpectedVersion int `json:"expectedVersion,omitempty" db:"expected_version"`
ExpectedChecksum string `json:"expectedChecksum,omitempty" db:"expected_checksum"`
MaxReadBytes int `json:"maxReadBytes,omitempty" db:"max_read_bytes"`
RemoteAdapterKey string `json:"remoteAdapterKey,omitempty" db:"remote_adapter_key"`
RemoteAdapterKind string `json:"remoteAdapterKind,omitempty" db:"remote_adapter_kind"`
TimeoutSeconds int `json:"timeoutSeconds,omitempty" db:"timeout_seconds"`
}
type JobExecutionResult struct {
Kind string `json:"kind,omitempty" db:"kind"`
ProcessState string `json:"processState,omitempty" db:"process_state"`
ExitClassification string `json:"exitClassification,omitempty" db:"exit_classification"`
ExitCode int `json:"exitCode,omitempty" db:"exit_code"`
Version int `json:"version,omitempty" db:"version"`
Checksum string `json:"checksum,omitempty" db:"checksum"`
SizeBytes int64 `json:"sizeBytes,omitempty" db:"size_bytes"`
AuditSummary string `json:"auditSummary,omitempty" db:"audit_summary"`
Content string `json:"content,omitempty" db:"content"`
}
type Job struct {
// ID is the stable job identifier.
ID string `json:"id" db:"id"`
@@ -238,6 +322,39 @@ type Job struct {
Progress JobProgress `json:"progress" db:"progress"`
// ResultRef references the terminal result artifact or summary.
ResultRef string `json:"resultRef,omitempty" db:"result_ref"`
// ExecutionInput is private approved input delivered only to fenced Run assignments.
ExecutionInput JobExecutionInput `json:"executionInput,omitempty" db:"execution_input"`
// ExecutionResult stores typed execution evidence; private content is not user-projected.
ExecutionResult JobExecutionResult `json:"executionResult,omitempty" db:"execution_result"`
// RetryPolicy stores bounded durable retry settings.
RetryPolicy JobRetryPolicy `json:"retryPolicy" db:"retry_policy"`
// Attempt is the current monotonic per-job attempt.
Attempt int `json:"attempt" db:"attempt"`
// QueueEligibleAt is the first time queued work may be claimed.
QueueEligibleAt time.Time `json:"queueEligibleAt,omitempty" db:"queue_eligible_at"`
// NextAttemptAt is the durable retry eligibility time.
NextAttemptAt time.Time `json:"nextAttemptAt,omitempty" db:"next_attempt_at"`
// LeaseTokenHash stores a one-way verifier, never the raw lease token.
LeaseTokenHash string `json:"leaseTokenHash,omitempty" db:"lease_token_hash"`
// LeaseSessionGen fences the attempt to an authenticated Run session generation.
LeaseSessionGen int `json:"leaseSessionGeneration,omitempty" db:"lease_session_generation"`
// AckDeadlineAt bounds how long Run has to acknowledge a claim.
AckDeadlineAt time.Time `json:"ackDeadlineAt,omitempty" db:"ack_deadline_at"`
// LeaseExpiresAt bounds execution without progress or reconciliation.
LeaseExpiresAt time.Time `json:"leaseExpiresAt,omitempty" db:"lease_expires_at"`
// LastProgressSeq rejects reordered progress updates.
LastProgressSeq uint64 `json:"lastProgressSequence,omitempty" db:"last_progress_sequence"`
// CancelReason and timestamps persist cancellation intent and result.
CancelReason string `json:"cancelReason,omitempty" db:"cancel_reason"`
CancelRequestedAt time.Time `json:"cancelRequestedAt,omitempty" db:"cancel_requested_at"`
CancelCompletedAt time.Time `json:"cancelCompletedAt,omitempty" db:"cancel_completed_at"`
// TerminalAt and TerminalFingerprint make terminal replay durable and idempotent.
TerminalAt time.Time `json:"terminalAt,omitempty" db:"terminal_at"`
TerminalFingerprint string `json:"terminalFingerprint,omitempty" db:"terminal_fingerprint"`
// Reconciliation fields provide restart recovery evidence.
LastReconciledAt time.Time `json:"lastReconciledAt,omitempty" db:"last_reconciled_at"`
ReconcileCount int `json:"reconcileCount,omitempty" db:"reconcile_count"`
ReconcileOutcome string `json:"reconcileOutcome,omitempty" db:"reconcile_outcome"`
// CreatedAt is the record creation timestamp.
CreatedAt time.Time `json:"createdAt" db:"created_at"`
// UpdatedAt is the last update timestamp.
@@ -395,6 +512,7 @@ func GamePluginFromDomain(plugin domain.GamePlugin) GamePlugin {
Tags: plugin.Tags,
AIPurposes: plugin.AIPurposes,
RemoteAccess: remoteAccessFromDomain(plugin.RemoteAccess),
RuntimeProfiles: domain.CopyGamePluginRuntimeProfiles(plugin.RuntimeProfiles),
ValidationViolations: plugin.ValidationViolations,
Status: plugin.Status,
}
@@ -419,6 +537,7 @@ func (plugin GamePlugin) ToDomain() domain.GamePlugin {
Tags: domain.CopyStringSlice(plugin.Tags),
AIPurposes: domain.CopyStringSlice(plugin.AIPurposes),
RemoteAccess: plugin.RemoteAccess.ToDomain(),
RuntimeProfiles: domain.CopyGamePluginRuntimeProfiles(plugin.RuntimeProfiles),
ValidationViolations: domain.CopyStringSlice(plugin.ValidationViolations),
Status: plugin.Status,
}
@@ -521,29 +640,41 @@ func remoteAccessFromDomain(remote domain.GamePluginRemoteAccess) GamePluginRemo
func ServerInstanceFromDomain(instance domain.ServerInstance) ServerInstance {
return ServerInstance{
ID: instance.ID,
PluginID: instance.PluginID,
PluginVersion: instance.PluginVersion,
RunEndpointID: instance.RunEndpointID,
Name: instance.Name,
State: instance.State,
ConfigVersion: instance.ConfigVersion,
CreatedAt: instance.CreatedAt,
UpdatedAt: instance.UpdatedAt,
ID: instance.ID,
PluginID: instance.PluginID,
PluginVersion: instance.PluginVersion,
RunEndpointID: instance.RunEndpointID,
Name: instance.Name,
OwnerUserID: instance.OwnerUserID,
AdminUserIDs: domain.CopyStringSlice(instance.AdminUserIDs),
State: instance.State,
ConfigVersion: instance.ConfigVersion,
ConfigKey: instance.ConfigKey,
ConfigContent: instance.ConfigContent,
ConfigChecksum: instance.ConfigChecksum,
ConfigUpdatedAt: instance.ConfigUpdatedAt,
CreatedAt: instance.CreatedAt,
UpdatedAt: instance.UpdatedAt,
}
}
func (instance ServerInstance) ToDomain() domain.ServerInstance {
return domain.ServerInstance{
ID: instance.ID,
PluginID: instance.PluginID,
PluginVersion: instance.PluginVersion,
RunEndpointID: instance.RunEndpointID,
Name: instance.Name,
State: instance.State,
ConfigVersion: instance.ConfigVersion,
CreatedAt: instance.CreatedAt,
UpdatedAt: instance.UpdatedAt,
ID: instance.ID,
PluginID: instance.PluginID,
PluginVersion: instance.PluginVersion,
RunEndpointID: instance.RunEndpointID,
Name: instance.Name,
OwnerUserID: instance.OwnerUserID,
AdminUserIDs: domain.CopyStringSlice(instance.AdminUserIDs),
State: instance.State,
ConfigVersion: instance.ConfigVersion,
ConfigKey: instance.ConfigKey,
ConfigContent: instance.ConfigContent,
ConfigChecksum: instance.ConfigChecksum,
ConfigUpdatedAt: instance.ConfigUpdatedAt,
CreatedAt: instance.CreatedAt,
UpdatedAt: instance.UpdatedAt,
}
}
@@ -553,6 +684,8 @@ func RunEndpointFromDomain(endpoint domain.RunEndpoint) RunEndpoint {
ID: endpoint.ID,
DisplayName: endpoint.DisplayName,
Version: endpoint.Version,
Platform: endpoint.Platform,
Architecture: endpoint.Architecture,
Status: endpoint.Status,
Capabilities: endpoint.Capabilities,
Capacity: capacityFromDomain(endpoint.Capacity),
@@ -565,6 +698,8 @@ func (endpoint RunEndpoint) ToDomain() domain.RunEndpoint {
ID: endpoint.ID,
DisplayName: endpoint.DisplayName,
Version: endpoint.Version,
Platform: endpoint.Platform,
Architecture: endpoint.Architecture,
Status: endpoint.Status,
Capabilities: domain.CopyStringSlice(endpoint.Capabilities),
Capacity: endpoint.Capacity.ToDomain(),
@@ -592,35 +727,105 @@ func capacityFromDomain(capacity domain.RunCapacity) RunCapacity {
func JobFromDomain(job domain.Job) Job {
return Job{
ID: job.ID,
ServerInstanceID: job.ServerInstanceID,
RunEndpointID: job.RunEndpointID,
Capability: job.Capability,
TargetKey: job.TargetKey,
InputRef: job.InputRef,
IdempotencyKey: job.IdempotencyKey,
State: job.State,
Progress: progressFromDomain(job.Progress),
ResultRef: job.ResultRef,
CreatedAt: job.CreatedAt,
UpdatedAt: job.UpdatedAt,
ID: job.ID,
ServerInstanceID: job.ServerInstanceID,
RunEndpointID: job.RunEndpointID,
Capability: job.Capability,
TargetKey: job.TargetKey,
InputRef: job.InputRef,
IdempotencyKey: job.IdempotencyKey,
State: job.State,
Progress: progressFromDomain(job.Progress),
ResultRef: job.ResultRef,
ExecutionInput: executionInputFromDomain(job.ExecutionInput),
ExecutionResult: executionResultFromDomain(job.ExecutionResult),
RetryPolicy: retryPolicyFromDomain(job.RetryPolicy),
Attempt: job.Attempt,
QueueEligibleAt: job.QueueEligibleAt,
NextAttemptAt: job.NextAttemptAt,
LeaseTokenHash: job.LeaseTokenHash,
LeaseSessionGen: job.LeaseSessionGen,
AckDeadlineAt: job.AckDeadlineAt,
LeaseExpiresAt: job.LeaseExpiresAt,
LastProgressSeq: job.LastProgressSeq,
CancelReason: job.CancelReason,
CancelRequestedAt: job.CancelRequestedAt,
CancelCompletedAt: job.CancelCompletedAt,
TerminalAt: job.TerminalAt,
TerminalFingerprint: job.TerminalFingerprint,
LastReconciledAt: job.LastReconciledAt,
ReconcileCount: job.ReconcileCount,
ReconcileOutcome: job.ReconcileOutcome,
CreatedAt: job.CreatedAt,
UpdatedAt: job.UpdatedAt,
}
}
func (job Job) ToDomain() domain.Job {
return domain.Job{
ID: job.ID,
ServerInstanceID: job.ServerInstanceID,
RunEndpointID: job.RunEndpointID,
Capability: job.Capability,
TargetKey: job.TargetKey,
InputRef: job.InputRef,
IdempotencyKey: job.IdempotencyKey,
State: job.State,
Progress: job.Progress.ToDomain(),
ResultRef: job.ResultRef,
CreatedAt: job.CreatedAt,
UpdatedAt: job.UpdatedAt,
ID: job.ID,
ServerInstanceID: job.ServerInstanceID,
RunEndpointID: job.RunEndpointID,
Capability: job.Capability,
TargetKey: job.TargetKey,
InputRef: job.InputRef,
IdempotencyKey: job.IdempotencyKey,
State: job.State,
Progress: job.Progress.ToDomain(),
ResultRef: job.ResultRef,
ExecutionInput: job.ExecutionInput.ToDomain(),
ExecutionResult: job.ExecutionResult.ToDomain(),
RetryPolicy: job.RetryPolicy.ToDomain(),
Attempt: job.Attempt,
QueueEligibleAt: job.QueueEligibleAt,
NextAttemptAt: job.NextAttemptAt,
LeaseTokenHash: job.LeaseTokenHash,
LeaseSessionGen: job.LeaseSessionGen,
AckDeadlineAt: job.AckDeadlineAt,
LeaseExpiresAt: job.LeaseExpiresAt,
LastProgressSeq: job.LastProgressSeq,
CancelReason: job.CancelReason,
CancelRequestedAt: job.CancelRequestedAt,
CancelCompletedAt: job.CancelCompletedAt,
TerminalAt: job.TerminalAt,
TerminalFingerprint: job.TerminalFingerprint,
LastReconciledAt: job.LastReconciledAt,
ReconcileCount: job.ReconcileCount,
ReconcileOutcome: job.ReconcileOutcome,
CreatedAt: job.CreatedAt,
UpdatedAt: job.UpdatedAt,
}
}
func executionInputFromDomain(input domain.JobExecutionInput) JobExecutionInput {
return JobExecutionInput{WorkspaceScope: input.WorkspaceScope, Content: input.Content, ExpectedVersion: input.ExpectedVersion, ExpectedChecksum: input.ExpectedChecksum, MaxReadBytes: input.MaxReadBytes, RemoteAdapterKey: input.RemoteAdapterKey, RemoteAdapterKind: input.RemoteAdapterKind, TimeoutSeconds: input.TimeoutSeconds}
}
func (input JobExecutionInput) ToDomain() domain.JobExecutionInput {
return domain.JobExecutionInput{WorkspaceScope: input.WorkspaceScope, Content: input.Content, ExpectedVersion: input.ExpectedVersion, ExpectedChecksum: input.ExpectedChecksum, MaxReadBytes: input.MaxReadBytes, RemoteAdapterKey: input.RemoteAdapterKey, RemoteAdapterKind: input.RemoteAdapterKind, TimeoutSeconds: input.TimeoutSeconds}
}
func executionResultFromDomain(result domain.JobExecutionResult) JobExecutionResult {
return JobExecutionResult{Kind: result.Kind, ProcessState: result.ProcessState, ExitClassification: result.ExitClassification, ExitCode: result.ExitCode, Version: result.Version, Checksum: result.Checksum, SizeBytes: result.SizeBytes, AuditSummary: result.AuditSummary, Content: result.Content}
}
func (result JobExecutionResult) ToDomain() domain.JobExecutionResult {
return domain.JobExecutionResult{Kind: result.Kind, ProcessState: result.ProcessState, ExitClassification: result.ExitClassification, ExitCode: result.ExitCode, Version: result.Version, Checksum: result.Checksum, SizeBytes: result.SizeBytes, AuditSummary: result.AuditSummary, Content: result.Content}
}
func (policy JobRetryPolicy) ToDomain() domain.JobRetryPolicy {
return domain.JobRetryPolicy{
MaxAttempts: policy.MaxAttempts,
InitialBackoffSeconds: policy.InitialBackoffSeconds,
MaxBackoffSeconds: policy.MaxBackoffSeconds,
}
}
func retryPolicyFromDomain(policy domain.JobRetryPolicy) JobRetryPolicy {
return JobRetryPolicy{
MaxAttempts: policy.MaxAttempts,
InitialBackoffSeconds: policy.InitialBackoffSeconds,
MaxBackoffSeconds: policy.MaxBackoffSeconds,
}
}