mirror of
https://github.com/Buriburizaem0n/nezha_domains.git
synced 2026-09-19 17:50:12 +00:00
115 lines
4.4 KiB
Go
115 lines
4.4 KiB
Go
//go:build linux
|
|
|
|
package dashboard
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
)
|
|
|
|
func (dashboard *Dashboard) WaitForMCPReceiptSet(ctx context.Context, cursor MCPReceiptCursor, expectations []MCPReceiptExpectation) ([]MCPReceiptPair, error) {
|
|
if err := validateMCPReceiptExpectations(expectations); err != nil {
|
|
return nil, err
|
|
}
|
|
for {
|
|
dashboard.eventMu.RLock()
|
|
notify, closed := dashboard.eventNotify, dashboard.eventClosed
|
|
events := append([]MCPReceiptEvent(nil), dashboard.mcpReceiptEvents...)
|
|
dashboard.eventMu.RUnlock()
|
|
pairs, complete, err := matchMCPReceiptSet(events, cursor, expectations)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if complete {
|
|
return pairs, nil
|
|
}
|
|
if closed {
|
|
return nil, ErrReceiptGateClosed
|
|
}
|
|
select {
|
|
case <-notify:
|
|
case <-ctx.Done():
|
|
return nil, ctx.Err()
|
|
}
|
|
}
|
|
}
|
|
|
|
type mcpReceiptIdentity struct {
|
|
dashboardGeneration uint64
|
|
gateGeneration uint64
|
|
serverID uint64
|
|
taskID uint64
|
|
taskType uint64
|
|
}
|
|
|
|
func validateMCPReceiptExpectations(expectations []MCPReceiptExpectation) error {
|
|
if len(expectations) == 0 {
|
|
return errors.New("MCP receipt expectations are empty")
|
|
}
|
|
seen := make(map[mcpReceiptIdentity]struct{}, len(expectations))
|
|
for _, expectation := range expectations {
|
|
identity := mcpReceiptIdentity{dashboardGeneration: expectation.DashboardGeneration, gateGeneration: expectation.GateGeneration, serverID: expectation.ServerID, taskID: expectation.TaskID, taskType: expectation.TaskType}
|
|
// Stress evidence requires exact generation-aware identity; zero is never a wildcard.
|
|
if expectation.DashboardGeneration == 0 || expectation.GateGeneration == 0 || expectation.ServerID == 0 || expectation.TaskID == 0 || expectation.TaskType == 0 {
|
|
return fmt.Errorf("invalid MCP receipt expectation: %+v", expectation)
|
|
}
|
|
if _, duplicate := seen[identity]; duplicate {
|
|
return fmt.Errorf("duplicate MCP receipt expectation for server %d task type %d", expectation.ServerID, expectation.TaskType)
|
|
}
|
|
seen[identity] = struct{}{}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func matchMCPReceiptSet(events []MCPReceiptEvent, cursor MCPReceiptCursor, expectations []MCPReceiptExpectation) ([]MCPReceiptPair, bool, error) {
|
|
if err := validateMCPReceiptExpectations(expectations); err != nil {
|
|
return nil, false, err
|
|
}
|
|
indices := make(map[mcpReceiptIdentity]int, len(expectations))
|
|
for index, expectation := range expectations {
|
|
indices[mcpReceiptIdentity{dashboardGeneration: expectation.DashboardGeneration, gateGeneration: expectation.GateGeneration, serverID: expectation.ServerID, taskID: expectation.TaskID, taskType: expectation.TaskType}] = index
|
|
}
|
|
pairs := make([]MCPReceiptPair, len(expectations))
|
|
taskIndices := make(map[uint64]int, len(expectations))
|
|
for _, event := range events {
|
|
if event.Sequence <= cursor.Sequence {
|
|
continue
|
|
}
|
|
identity := mcpReceiptIdentity{dashboardGeneration: event.DashboardGeneration, gateGeneration: event.GateGeneration, serverID: event.ServerID, taskID: event.TaskID, taskType: event.TaskType}
|
|
index, expected := indices[identity]
|
|
if !expected {
|
|
return nil, false, fmt.Errorf("unexpected MCP receipt event after cursor: %+v", event)
|
|
}
|
|
switch event.Kind {
|
|
case MCPReceiptTask:
|
|
if _, duplicate := taskIndices[event.TaskID]; duplicate || pairs[index].Task.TaskID != 0 {
|
|
return nil, false, fmt.Errorf("duplicate MCP task receipt for server %d task type %d: %+v", event.ServerID, event.TaskType, event)
|
|
}
|
|
taskIndices[event.TaskID] = index
|
|
pairs[index].Task = event
|
|
case MCPReceiptResult:
|
|
taskIndex, exists := taskIndices[event.TaskID]
|
|
if !exists {
|
|
return nil, false, fmt.Errorf("MCP result receipt has no matching task: %+v", event)
|
|
}
|
|
if taskIndex != index || pairs[index].Result.TaskID != 0 {
|
|
return nil, false, fmt.Errorf("duplicate or mismatched MCP result receipt: %+v", event)
|
|
}
|
|
task := pairs[index].Task
|
|
if event.ServerID != task.ServerID || event.TaskType != task.TaskType || event.GateGeneration != task.GateGeneration || event.DashboardGeneration != task.DashboardGeneration || event.TaskID != task.TaskID {
|
|
return nil, false, fmt.Errorf("MCP result receipt does not match task: task=%+v result=%+v", task, event)
|
|
}
|
|
pairs[index].Result = event
|
|
default:
|
|
return nil, false, fmt.Errorf("unexpected MCP receipt kind %q: %+v", event.Kind, event)
|
|
}
|
|
}
|
|
for _, pair := range pairs {
|
|
if pair.Task.TaskID == 0 || pair.Result.TaskID == 0 {
|
|
return nil, false, nil
|
|
}
|
|
}
|
|
return pairs, true, nil
|
|
}
|