Files
browser/platform_web/contracts/scumOperations.ts
T

147 lines
4.9 KiB
TypeScript

import type {
GameClientBridgeCommandDeclarationResponse,
GameClientBridgeQueryTemplateDeclarationResponse,
GameClientBridgeSnapshotDeclarationResponse,
GamePluginResponse,
PluginProductionLifecycleDeclaration,
RuntimeLogEventResponse,
RuntimeLogSourceResponse
} from "../api/types";
import {
isPluginBridgeAction,
isPluginPermission,
type PluginBridgeAction,
type PluginPermission
} from "./pluginBridge";
export const scumOperationsPluginId = "game.scum";
export const scumOperationsRouteKey = "files-config";
export interface ScumOperationsPageContract {
pluginId: typeof scumOperationsPluginId;
routeKey: typeof scumOperationsRouteKey;
serverInstanceId: string;
title: string;
permissions: PluginPermission[];
bridgeActions: PluginBridgeAction[];
commands: GameClientBridgeCommandDeclarationResponse[];
snapshots: GameClientBridgeSnapshotDeclarationResponse[];
queryTemplates: GameClientBridgeQueryTemplateDeclarationResponse[];
logSources: RuntimeLogSourceResponse[];
logEvents: RuntimeLogEventResponse[];
productionLifecycle: PluginProductionLifecycleDeclaration;
}
export interface ScumCompanionHealthView {
status: "online" | "degraded" | "offline" | "unknown";
version?: string;
observedAt?: string;
latencyMs?: number;
capabilities: string[];
}
export interface ScumSessionView {
sessionId: string;
playerName: string;
startedAt?: string;
}
export interface ScumPlayerView {
playerId: string;
playerName: string;
status: string;
squadId?: string;
pingMs?: number;
lastSeenAt?: string;
}
export interface ScumSquadView {
squadId: string;
name: string;
memberCount: number;
leaderPlayerId?: string;
lastActiveAt?: string;
}
export interface ScumVehicleView {
vehicleId: string;
vehicleType: string;
status: string;
ownerPlayerId?: string;
squadId?: string;
fuelPercent?: number;
healthPercent?: number;
lastSeenAt?: string;
}
export interface ScumFlagView {
flagId: string;
status: string;
ownerPlayerId?: string;
squadId?: string;
radiusMeters?: number;
lastUpdatedAt?: string;
}
export interface ScumSnapshotCollection<T> {
observedAt?: string;
total: number;
items: T[];
}
export interface ScumOperationsSnapshotView {
health?: ScumCompanionHealthView;
sessions: ScumSnapshotCollection<ScumSessionView>;
players: ScumSnapshotCollection<ScumPlayerView>;
squads: ScumSnapshotCollection<ScumSquadView>;
vehicles: ScumSnapshotCollection<ScumVehicleView>;
flags: ScumSnapshotCollection<ScumFlagView>;
}
export type ScumOperationsPageResolution =
| { available: true; contract: ScumOperationsPageContract }
| { available: false; reason: string };
type ScumPluginProjection = Pick<GamePluginResponse, "id" | "pages" | "gameClientBridge" | "runtimeProfiles" | "productionLifecycle" | "fileWorkspace">;
export function normalizeScumRouteKey(pluginId: string, routeKey: string): string { return pluginId === scumOperationsPluginId && ["overview", "operations", "config", "logs"].includes(routeKey) ? scumOperationsRouteKey : routeKey; }
export function resolveScumOperationsPageContract(plugin: ScumPluginProjection, serverInstanceId: string): ScumOperationsPageResolution {
if (plugin.id !== scumOperationsPluginId) {
return { available: false, reason: "该路由仅承载 game.scum 插件声明的运维页。" };
}
if (!serverInstanceId.trim()) {
return { available: false, reason: "缺少服务器实例上下文。" };
}
const page = plugin.pages.find((candidate) => candidate.key === scumOperationsRouteKey);
if (!page) {
return { available: false, reason: "SCUM 插件未声明 operations 页面。" };
}
const manifest = plugin.gameClientBridge;
const bridgePage = manifest?.pages?.find((candidate) => candidate.pageKey === scumOperationsRouteKey);
if (!manifest || !bridgePage) {
return { available: false, reason: "SCUM 插件未声明 operations 的 Game Client Bridge 契约。" };
}
const commandTypes = new Set(bridgePage.commandTypes ?? []);
const snapshotTypes = new Set(bridgePage.snapshotTypes ?? []);
const queryTemplateKeys = new Set(bridgePage.queryTemplateKeys ?? []);
return {
available: true,
contract: {
pluginId: scumOperationsPluginId,
routeKey: scumOperationsRouteKey,
serverInstanceId,
title: page.title,
permissions: page.permissions.filter(isPluginPermission),
bridgeActions: (page.bridgeActions ?? []).filter(isPluginBridgeAction),
commands: manifest.commands.filter((command) => commandTypes.has(command.type)),
snapshots: manifest.snapshots.filter((snapshot) => snapshotTypes.has(snapshot.type)),
queryTemplates: (manifest.queryTemplates ?? []).filter((template) => queryTemplateKeys.has(template.key)),
logSources: [...(plugin.runtimeProfiles?.logSources ?? [])],
logEvents: [...(plugin.runtimeProfiles?.logEvents ?? [])],
productionLifecycle: plugin.productionLifecycle
}
};
}