Remove AI config approval flow
This commit is contained in:
@@ -3,7 +3,6 @@ package service
|
||||
import (
|
||||
"errors"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"browser.local/platform/domain"
|
||||
"browser.local/platform/repo"
|
||||
@@ -24,7 +23,7 @@ func (MockAIProviderClient) Invoke(provider domain.AIProvider, request domain.AI
|
||||
if model == "" && len(provider.Models) > 0 {
|
||||
model = provider.Models[0]
|
||||
}
|
||||
recommendation := "Mock AI recommendation for " + request.Purpose + ": review the proposed change before dispatch."
|
||||
recommendation := "Mock AI recommendation for " + request.Purpose + ": configuration changes are dispatched automatically."
|
||||
result := domain.AIProviderInvocationResult{
|
||||
Recommendation: recommendation,
|
||||
Usage: domain.AIInvocationUsage{
|
||||
@@ -46,10 +45,12 @@ func (svc *CoreService) InvokeAIForSession(sessionID string, request domain.AIIn
|
||||
if err := validator.ValidateAIInvocationRequest(request); err != nil {
|
||||
return domain.AIInvocationResponse{}, err
|
||||
}
|
||||
user, err := svc.GetCurrentUser(sessionID)
|
||||
if err != nil {
|
||||
if _, err := svc.GetCurrentUser(sessionID); err != nil {
|
||||
return domain.AIInvocationResponse{}, err
|
||||
}
|
||||
var currentConfig domain.ServerConfig
|
||||
var hasCurrentConfig bool
|
||||
var err error
|
||||
if request.ServerInstanceID != "" {
|
||||
instance, err := svc.GetServerInstanceForSession(sessionID, request.ServerInstanceID)
|
||||
if err != nil {
|
||||
@@ -63,6 +64,8 @@ func (svc *CoreService) InvokeAIForSession(sessionID string, request domain.AIIn
|
||||
if err != nil {
|
||||
return domain.AIInvocationResponse{}, err
|
||||
}
|
||||
currentConfig = config
|
||||
hasCurrentConfig = true
|
||||
request.CurrentConfig = config.Content
|
||||
}
|
||||
}
|
||||
@@ -111,16 +114,25 @@ func (svc *CoreService) InvokeAIForSession(sessionID string, request domain.AIIn
|
||||
Usage: result.Usage,
|
||||
}
|
||||
if result.SuggestedConfig != "" {
|
||||
if request.ServerInstanceID == "" {
|
||||
if request.ServerInstanceID == "" || !hasCurrentConfig {
|
||||
return domain.AIInvocationResponse{}, validationError("serverInstanceId is required for AI config recommendations")
|
||||
}
|
||||
idempotencyKey := aiConfigWriteIdempotencyKey(request.RequestID, request.ServerInstanceID)
|
||||
svc.pluginOperationsMu.Lock()
|
||||
preview, persistErr := svc.persistAIConfigDiff(user.ID, provider, request, result)
|
||||
dispatch, dispatchErr := svc.ApproveServerConfigWriteForSession(sessionID, domain.ServerConfigWriteApproval{
|
||||
ServerInstanceID: request.ServerInstanceID,
|
||||
ExpectedConfigVersion: currentConfig.ConfigVersion,
|
||||
ExpectedChecksum: currentConfig.Checksum,
|
||||
Key: currentConfig.Key,
|
||||
ProposedContent: result.SuggestedConfig,
|
||||
IdempotencyKey: idempotencyKey,
|
||||
})
|
||||
svc.pluginOperationsMu.Unlock()
|
||||
if persistErr != nil {
|
||||
return domain.AIInvocationResponse{}, persistErr
|
||||
if dispatchErr != nil {
|
||||
return domain.AIInvocationResponse{}, dispatchErr
|
||||
}
|
||||
response.ConfigRecommendation = &domain.AIConfigRecommendation{Key: preview.Key, SuggestedConfig: preview.ProposedConfig, DiffSummary: preview.DiffSummary, DiffID: preview.ID, ExpiresAt: preview.ExpiresAt.Format(time.RFC3339)}
|
||||
response.ConfigRecommendation = &domain.AIConfigRecommendation{Key: currentConfig.Key, SuggestedConfig: result.SuggestedConfig, DiffSummary: "AI 配置建议已直接派发 config.write 任务"}
|
||||
response.ConfigExecution = &domain.AIConfigExecution{Status: dispatch.Status, Job: dispatch.Job}
|
||||
}
|
||||
if err := validator.ValidateAIInvocationResponse(response); err != nil {
|
||||
return domain.AIInvocationResponse{}, err
|
||||
@@ -179,7 +191,7 @@ func buildSuggestedConfig(currentConfig string, prompt string) string {
|
||||
if strings.Contains(strings.ToLower(prompt), "pvp") && !strings.Contains(base, "pvp=") {
|
||||
base += "\npvp=false"
|
||||
}
|
||||
return base + "\n# ai.recommendation=review-required\n"
|
||||
return base + "\n# ai.recommendation=auto-applied\n"
|
||||
}
|
||||
|
||||
func boundedTokenEstimate(value string) int {
|
||||
|
||||
@@ -283,7 +283,7 @@ func boundedProviderPrompt(request domain.AIInvocationRequest) string {
|
||||
builder.WriteString(request.CurrentConfig)
|
||||
}
|
||||
if request.Purpose == "config.suggest" || request.Purpose == "config.generate" {
|
||||
builder.WriteString("\nReturn JSON with recommendation and suggestedConfig. Configuration changes require separate operator approval.")
|
||||
builder.WriteString("\nReturn JSON with recommendation and suggestedConfig. Configuration changes are dispatched automatically as a bounded config.write job.")
|
||||
}
|
||||
return builder.String()
|
||||
}
|
||||
|
||||
@@ -13,10 +13,6 @@ import (
|
||||
"browser.local/platform/validator"
|
||||
)
|
||||
|
||||
const (
|
||||
aiConfigDiffTTL = 30 * time.Minute
|
||||
)
|
||||
|
||||
func (svc *CoreService) ListPluginLifecyclesForSession(sessionID string, filter domain.PluginLifecycleFilter) ([]domain.PluginLifecycleInstallation, error) {
|
||||
user, err := svc.GetCurrentUser(sessionID)
|
||||
if err != nil {
|
||||
@@ -128,84 +124,6 @@ func (svc *CoreService) RunPluginLifecycleForSession(sessionID string, request d
|
||||
return domain.CopyPluginLifecycleResult(domain.PluginLifecycleResult{Installation: installation, Job: job, Status: "queued"}), nil
|
||||
}
|
||||
|
||||
func (svc *CoreService) ListAIConfigDiffsForSession(sessionID string, filter domain.AIConfigDiffFilter) ([]domain.AIConfigDiffPreview, error) {
|
||||
user, err := svc.GetCurrentUser(sessionID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
previews, err := svc.store.AIConfigDiffs().List(filter)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
visible := make([]domain.AIConfigDiffPreview, 0, len(previews))
|
||||
for _, preview := range previews {
|
||||
instance, err := svc.store.ServerInstances().Get(preview.ServerInstanceID)
|
||||
if err == nil && canAccessServer(user, instance) {
|
||||
visible = append(visible, preview)
|
||||
}
|
||||
}
|
||||
sort.Slice(visible, func(i, j int) bool { return visible[i].CreatedAt.After(visible[j].CreatedAt) })
|
||||
return domain.CopyAIConfigDiffPreviews(visible), nil
|
||||
}
|
||||
|
||||
func (svc *CoreService) ApproveAIConfigDiffForSession(sessionID string, request domain.AIConfigDiffApprovalRequest) (domain.AIConfigDiffApprovalResult, error) {
|
||||
if err := validator.ValidateAIConfigDiffApprovalRequest(request); err != nil {
|
||||
return domain.AIConfigDiffApprovalResult{}, err
|
||||
}
|
||||
user, err := svc.GetCurrentUser(sessionID)
|
||||
if err != nil {
|
||||
return domain.AIConfigDiffApprovalResult{}, err
|
||||
}
|
||||
svc.pluginOperationsMu.Lock()
|
||||
defer svc.pluginOperationsMu.Unlock()
|
||||
preview, err := svc.store.AIConfigDiffs().Get(request.DiffID)
|
||||
if err != nil {
|
||||
return domain.AIConfigDiffApprovalResult{}, err
|
||||
}
|
||||
instance, err := svc.store.ServerInstances().Get(preview.ServerInstanceID)
|
||||
if err != nil {
|
||||
return domain.AIConfigDiffApprovalResult{}, err
|
||||
}
|
||||
if !canAccessServer(user, instance) || (!isPlatformAdmin(user) && preview.CreatedBy != user.ID) {
|
||||
return domain.AIConfigDiffApprovalResult{}, ErrForbidden
|
||||
}
|
||||
if preview.State == domain.AIConfigDiffStateApproved {
|
||||
if preview.ApprovalIdempotencyKey != request.IdempotencyKey {
|
||||
return domain.AIConfigDiffApprovalResult{}, validationError("AI config diff is already approved with another idempotency key")
|
||||
}
|
||||
job, err := svc.store.Jobs().Get(preview.JobID)
|
||||
if err != nil {
|
||||
return domain.AIConfigDiffApprovalResult{}, err
|
||||
}
|
||||
dispatch := domain.ServerConfigWriteDispatch{Job: job, Status: "queued"}
|
||||
return domain.CopyAIConfigDiffApprovalResult(domain.AIConfigDiffApprovalResult{Preview: preview, Dispatch: dispatch}), nil
|
||||
}
|
||||
if preview.State != domain.AIConfigDiffStatePending {
|
||||
return domain.AIConfigDiffApprovalResult{}, validationError("AI config diff is not pending approval")
|
||||
}
|
||||
if !preview.ExpiresAt.After(svc.now()) {
|
||||
preview.State = domain.AIConfigDiffStateExpired
|
||||
preview.UpdatedAt = svc.now()
|
||||
_ = svc.store.AIConfigDiffs().Update(preview)
|
||||
return domain.AIConfigDiffApprovalResult{}, validationError("AI config diff has expired")
|
||||
}
|
||||
dispatch, err := svc.ApproveServerConfigWriteForSession(sessionID, domain.ServerConfigWriteApproval{ServerInstanceID: preview.ServerInstanceID, ExpectedConfigVersion: preview.ConfigVersion, ExpectedChecksum: preview.CurrentConfigChecksum, Key: preview.Key, ProposedContent: preview.ProposedConfig, IdempotencyKey: request.IdempotencyKey})
|
||||
if err != nil {
|
||||
return domain.AIConfigDiffApprovalResult{}, err
|
||||
}
|
||||
stamp := svc.now()
|
||||
preview.State = domain.AIConfigDiffStateApproved
|
||||
preview.ApprovedBy = user.ID
|
||||
preview.ApprovedAt = stamp
|
||||
preview.ApprovalIdempotencyKey = request.IdempotencyKey
|
||||
preview.JobID = dispatch.Job.ID
|
||||
preview.UpdatedAt = stamp
|
||||
if err := svc.store.AIConfigDiffs().Update(preview); err != nil {
|
||||
return domain.AIConfigDiffApprovalResult{}, err
|
||||
}
|
||||
return domain.CopyAIConfigDiffApprovalResult(domain.AIConfigDiffApprovalResult{Preview: preview, Dispatch: dispatch}), nil
|
||||
}
|
||||
|
||||
func (svc *CoreService) projectPluginOperationsJobResult(job domain.Job, stamp time.Time) error {
|
||||
if !strings.HasPrefix(job.ID, "job-plugin-lifecycle-") || job.ExecutionInput.LifecycleOperation == "" || job.ExecutionInput.PluginID == "" {
|
||||
return nil
|
||||
@@ -230,63 +148,6 @@ func (svc *CoreService) projectPluginOperationsJobResult(job domain.Job, stamp t
|
||||
return svc.store.PluginLifecycles().Update(installation)
|
||||
}
|
||||
|
||||
func (svc *CoreService) persistAIConfigDiff(actorID string, provider domain.AIProvider, request domain.AIInvocationRequest, result domain.AIProviderInvocationResult) (domain.AIConfigDiffPreview, error) {
|
||||
config, err := svc.getServerConfigForUser(actorID, request.ServerInstanceID)
|
||||
if err != nil {
|
||||
return domain.AIConfigDiffPreview{}, err
|
||||
}
|
||||
stamp := svc.now()
|
||||
preview := domain.AIConfigDiffPreview{ID: aiConfigDiffID(request.RequestID, request.ServerInstanceID), RequestID: request.RequestID, CreatedBy: actorID, ServerInstanceID: request.ServerInstanceID, PluginID: request.PluginID, ProviderID: provider.ID, Model: result.Usage.Model, Key: config.Key, ConfigVersion: config.ConfigVersion, CurrentConfigChecksum: config.Checksum, ProposedConfig: result.SuggestedConfig, DiffSummary: "review required before config write dispatch", State: domain.AIConfigDiffStatePending, ExpiresAt: stamp.Add(aiConfigDiffTTL), CreatedAt: stamp, UpdatedAt: stamp}
|
||||
if err := validator.ValidateAIConfigDiffPreview(preview); err != nil {
|
||||
return domain.AIConfigDiffPreview{}, err
|
||||
}
|
||||
if err := svc.store.AIConfigDiffs().Create(preview); err != nil {
|
||||
if !errors.Is(err, repo.ErrDuplicate) {
|
||||
return domain.AIConfigDiffPreview{}, err
|
||||
}
|
||||
existing, getErr := svc.store.AIConfigDiffs().Get(preview.ID)
|
||||
if getErr != nil {
|
||||
return domain.AIConfigDiffPreview{}, getErr
|
||||
}
|
||||
if existing.CreatedBy != actorID || existing.ServerInstanceID != request.ServerInstanceID || existing.PluginID != request.PluginID || existing.ProposedConfig != result.SuggestedConfig {
|
||||
return domain.AIConfigDiffPreview{}, validationError("requestId is already used for a different AI config recommendation")
|
||||
}
|
||||
return existing, nil
|
||||
}
|
||||
return preview, nil
|
||||
}
|
||||
|
||||
func (svc *CoreService) getServerConfigForUser(userID, serverInstanceID string) (domain.ServerConfig, error) {
|
||||
instance, err := svc.store.ServerInstances().Get(serverInstanceID)
|
||||
if err != nil {
|
||||
return domain.ServerConfig{}, err
|
||||
}
|
||||
user, err := svc.store.Users().Get(userID)
|
||||
if err != nil {
|
||||
return domain.ServerConfig{}, err
|
||||
}
|
||||
if !canAccessServer(user, instance) {
|
||||
return domain.ServerConfig{}, ErrForbidden
|
||||
}
|
||||
config := domain.ServerConfig{ServerInstanceID: instance.ID, ConfigVersion: instance.ConfigVersion, Format: "properties", Key: instance.ConfigKey, Content: instance.ConfigContent, Checksum: instance.ConfigChecksum, Source: "platform-derived", UpdatedAt: instance.ConfigUpdatedAt}
|
||||
if config.ConfigVersion <= 0 {
|
||||
config.ConfigVersion = 1
|
||||
}
|
||||
if config.Key == "" {
|
||||
config.Key = "server.properties"
|
||||
}
|
||||
if config.Content == "" {
|
||||
config.Content = buildLogicalServerConfig(instance)
|
||||
}
|
||||
if config.Checksum == "" {
|
||||
config.Checksum = validator.BytesChecksum([]byte(config.Content))
|
||||
}
|
||||
if config.UpdatedAt.IsZero() {
|
||||
config.UpdatedAt = svc.now()
|
||||
}
|
||||
return config, validator.ValidateServerConfig(config)
|
||||
}
|
||||
|
||||
func (svc *CoreService) pluginLifecycleDenied(actorID string, instance domain.ServerInstance, plugin domain.GamePlugin, request domain.PluginLifecycleRequest, reason string) (domain.PluginLifecycleResult, error) {
|
||||
svc.pluginOperationsMu.Lock()
|
||||
defer svc.pluginOperationsMu.Unlock()
|
||||
@@ -436,7 +297,7 @@ func pluginLifecycleInstallationID(pluginID, serverInstanceID string) string {
|
||||
return "plugin-lifecycle-" + hex.EncodeToString(sum[:12])
|
||||
}
|
||||
|
||||
func aiConfigDiffID(requestID, serverInstanceID string) string {
|
||||
sum := sha256.Sum256([]byte(requestID + "\x00" + serverInstanceID))
|
||||
return "ai-config-diff-" + hex.EncodeToString(sum[:12])
|
||||
func aiConfigWriteIdempotencyKey(requestID, serverInstanceID string) string {
|
||||
sum := sha256.Sum256([]byte("ai-config-write\x00" + requestID + "\x00" + serverInstanceID))
|
||||
return "ai-config-write-" + hex.EncodeToString(sum[:16])
|
||||
}
|
||||
|
||||
@@ -72,7 +72,7 @@ func TestPluginLifecycleBridgeDispatchesBoundedJob(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestAIConfigRecommendationRequiresApprovalAndRejectsStaleRevision(t *testing.T) {
|
||||
func TestAIConfigRecommendationDispatchesConfigWriteImmediately(t *testing.T) {
|
||||
svc, session, instance := newPluginOperationsFixture(t)
|
||||
provider, err := svc.CreateAIProvider(domain.AIProvider{ID: "ai-local", Name: "Local AI", Kind: domain.AIProviderKindOllama, BaseURL: "http://127.0.0.1:11434/v1", Models: []string{"test-model"}, DefaultModel: "test-model", RelayMode: domain.AIRelayModeLocal, TimeoutMS: 1000, Status: domain.AIProviderStatusActive, RedactionPolicy: "strict"})
|
||||
if err != nil {
|
||||
@@ -82,43 +82,19 @@ func TestAIConfigRecommendationRequiresApprovalAndRejectsStaleRevision(t *testin
|
||||
if err != nil {
|
||||
t.Fatalf("invoke AI: %v", err)
|
||||
}
|
||||
if response.ConfigRecommendation == nil || response.ConfigRecommendation.DiffID == "" {
|
||||
t.Fatalf("expected persisted config recommendation, got %+v", response)
|
||||
if response.ConfigRecommendation == nil || response.ConfigExecution == nil || response.ConfigExecution.Job.ID == "" {
|
||||
t.Fatalf("expected direct config write dispatch, got %+v", response)
|
||||
}
|
||||
if response.ConfigExecution.Job.Capability != domain.JobCapabilityConfigWrite || response.ConfigExecution.Status != "queued" {
|
||||
t.Fatalf("unexpected AI config execution: %+v", response.ConfigExecution)
|
||||
}
|
||||
jobs, _ := svc.store.Jobs().List(domain.JobFilter{ServerInstanceID: instance.ID})
|
||||
if len(jobs) != 0 {
|
||||
t.Fatalf("AI recommendation must not dispatch before approval: %+v", jobs)
|
||||
if len(jobs) != 1 || jobs[0].ID != response.ConfigExecution.Job.ID {
|
||||
t.Fatalf("AI recommendation must dispatch exactly one config write job: %+v", jobs)
|
||||
}
|
||||
approved, err := svc.ApproveAIConfigDiffForSession(session, domain.AIConfigDiffApprovalRequest{DiffID: response.ConfigRecommendation.DiffID, IdempotencyKey: "approve-ai-config-1"})
|
||||
if err != nil {
|
||||
t.Fatalf("approve AI diff: %v", err)
|
||||
}
|
||||
if approved.Preview.State != domain.AIConfigDiffStateApproved || approved.Dispatch.Job.ID == "" {
|
||||
t.Fatalf("expected approved diff and queued job, got %+v", approved)
|
||||
}
|
||||
repeated, err := svc.ApproveAIConfigDiffForSession(session, domain.AIConfigDiffApprovalRequest{DiffID: response.ConfigRecommendation.DiffID, IdempotencyKey: "approve-ai-config-1"})
|
||||
if err != nil || repeated.Dispatch.Job.ID != approved.Dispatch.Job.ID {
|
||||
t.Fatalf("repeat approval must return original job: %+v err=%v", repeated, err)
|
||||
}
|
||||
jobs, _ = svc.store.Jobs().List(domain.JobFilter{ServerInstanceID: instance.ID})
|
||||
if len(jobs) != 1 {
|
||||
t.Fatalf("approval must dispatch exactly one job, got %+v", jobs)
|
||||
}
|
||||
|
||||
staleResponse, err := svc.InvokeAIForSession(session, domain.AIInvocationRequest{RequestID: "ai-config-stale", ServerInstanceID: instance.ID, ProviderID: provider.ID, Purpose: "config.suggest", Prompt: "disable pvp"})
|
||||
if err != nil {
|
||||
t.Fatalf("invoke stale AI candidate: %v", err)
|
||||
}
|
||||
stored, err := svc.store.ServerInstances().Get(instance.ID)
|
||||
if err != nil {
|
||||
t.Fatalf("get server: %v", err)
|
||||
}
|
||||
stored.ConfigVersion++
|
||||
if err := svc.store.ServerInstances().Update(stored); err != nil {
|
||||
t.Fatalf("advance config revision: %v", err)
|
||||
}
|
||||
if _, err := svc.ApproveAIConfigDiffForSession(session, domain.AIConfigDiffApprovalRequest{DiffID: staleResponse.ConfigRecommendation.DiffID, IdempotencyKey: "approve-stale"}); err == nil || !strings.Contains(err.Error(), "expectedConfigVersion") {
|
||||
t.Fatalf("expected stale revision rejection, got %v", err)
|
||||
repeated, err := svc.InvokeAIForSession(session, domain.AIInvocationRequest{RequestID: "ai-config-1", ServerInstanceID: instance.ID, ProviderID: provider.ID, Purpose: "config.suggest", Prompt: "disable pvp"})
|
||||
if err != nil || repeated.ConfigExecution == nil || repeated.ConfigExecution.Job.ID != response.ConfigExecution.Job.ID {
|
||||
t.Fatalf("repeat AI request must reuse the idempotent job: %+v err=%v", repeated, err)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -116,8 +116,6 @@ type Core interface {
|
||||
ListServerMetricsForSession(string) ([]domain.ServerMetrics, error)
|
||||
ListPluginLifecyclesForSession(string, domain.PluginLifecycleFilter) ([]domain.PluginLifecycleInstallation, error)
|
||||
RunPluginLifecycleForSession(string, domain.PluginLifecycleRequest) (domain.PluginLifecycleResult, error)
|
||||
ListAIConfigDiffsForSession(string, domain.AIConfigDiffFilter) ([]domain.AIConfigDiffPreview, error)
|
||||
ApproveAIConfigDiffForSession(string, domain.AIConfigDiffApprovalRequest) (domain.AIConfigDiffApprovalResult, error)
|
||||
IngestMetricBatch(domain.MetricBatchIngest) (domain.MetricBatchIngestResult, error)
|
||||
ListMetricSamplesForSession(string, domain.MetricSampleFilter) ([]domain.MetricSample, error)
|
||||
CreateBackupForSession(string, domain.BackupRecord) (domain.BackupRecord, error)
|
||||
@@ -912,17 +910,6 @@ func (svc *CoreService) replaceGamePluginReference(fromPluginID, toPluginID, toP
|
||||
return err
|
||||
}
|
||||
}
|
||||
previews, err := svc.store.AIConfigDiffs().List(domain.AIConfigDiffFilter{PluginID: fromPluginID})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, preview := range previews {
|
||||
preview.PluginID = toPluginID
|
||||
preview.UpdatedAt = stamp
|
||||
if err := svc.store.AIConfigDiffs().Update(preview); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return svc.replacePluginDataReferences(fromPluginID, toPluginID)
|
||||
}
|
||||
|
||||
@@ -1215,9 +1202,14 @@ func (svc *CoreService) executeBridgeAIInvoke(sessionID string, base domain.Plug
|
||||
if response.ConfigRecommendation != nil {
|
||||
base.Result["suggestedConfig"] = response.ConfigRecommendation.SuggestedConfig
|
||||
base.Result["diffSummary"] = response.ConfigRecommendation.DiffSummary
|
||||
base.Result["diffId"] = response.ConfigRecommendation.DiffID
|
||||
base.Result["key"] = response.ConfigRecommendation.Key
|
||||
base.Result["expiresAt"] = response.ConfigRecommendation.ExpiresAt
|
||||
}
|
||||
if response.ConfigExecution != nil {
|
||||
base.Result["executionStatus"] = response.ConfigExecution.Status
|
||||
base.Result["jobId"] = response.ConfigExecution.Job.ID
|
||||
base.Result["capability"] = response.ConfigExecution.Job.Capability
|
||||
base.Result["targetKey"] = response.ConfigExecution.Job.TargetKey
|
||||
base.Result["inputRef"] = response.ConfigExecution.Job.InputRef
|
||||
}
|
||||
if response.Error != nil {
|
||||
base.Error = &domain.PluginBridgeSafeError{Code: response.Error.Code, Message: response.Error.Message, Details: response.Error.Details}
|
||||
|
||||
@@ -2448,7 +2448,7 @@ func validPluginManifestRegistration() domain.GamePluginManifestRegistration {
|
||||
BridgeActions: []string{string(domain.PluginBridgeActionLogsQuery), string(domain.PluginBridgeActionFilesRequest), string(domain.PluginBridgeActionAIInvoke)},
|
||||
},
|
||||
},
|
||||
AI: domain.GamePluginManifestAI{Purposes: []string{"logs.diagnose"}, Mediation: "platform", ConfigWritePolicy: "review-required"},
|
||||
AI: domain.GamePluginManifestAI{Purposes: []string{"logs.diagnose"}, Mediation: "platform"},
|
||||
ProductionLifecycle: domain.GamePluginProductionLifecycle{Operations: []string{"install", "enable", "disable", "upgrade", "rollback", "retire", "dependency-check"}, DependencyPolicy: "optional"},
|
||||
RuntimeProfiles: domain.GamePluginRuntimeProfiles{LifecycleProfiles: []domain.RuntimeLifecycleProfile{{Key: "local", Mode: "local-process", Capabilities: []string{"process.install", "process.start", "process.stop"}}}},
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user