808 lines
34 KiB
TypeScript
808 lines
34 KiB
TypeScript
import type {
|
|
AiProviderListResponse,
|
|
AiProviderModelsResponse,
|
|
AiProviderRequest,
|
|
AiProviderResponse,
|
|
AiProviderStatusRequest,
|
|
AiProviderTestResponse,
|
|
AiProviderUpdateRequest,
|
|
AIInvocationRequest,
|
|
AIInvocationResponse,
|
|
AIConfigDiffApprovalResponse,
|
|
AIConfigDiffListResponse,
|
|
AlertListResponse,
|
|
AlertResponse,
|
|
AlertRetryResponse,
|
|
ApiErrorResponse,
|
|
ArtifactContentChunk,
|
|
ArtifactDownloadReferenceResponse,
|
|
ArtifactFilterRequest,
|
|
ArtifactListResponse,
|
|
AuthSessionResponse,
|
|
AuditEventListResponse,
|
|
ClientManagerBuildRequest,
|
|
ClientManagerControlRequest,
|
|
ClientManagerDeployRequest,
|
|
ClientManagerDistributionResponse,
|
|
ClientManagerDownloadRequest,
|
|
ClientManagerInstallationListResponse,
|
|
ClientManagerInstallationResponse,
|
|
ClientManagerRetryRequest,
|
|
ClientManagerRevokeSessionRequest,
|
|
ClientManagerUninstallRequest,
|
|
ClientManagerUpdateRequest,
|
|
ComponentKeyResponse,
|
|
ComponentKeyResetRequest,
|
|
CurrentUserResponse,
|
|
DependencyCatalogResponse,
|
|
DependencyJobRequest,
|
|
FileOperationDispatchRequest,
|
|
FileOperationDispatchResponse,
|
|
GameClientBridgeCancelRequest,
|
|
GameClientBridgeCancelResponse,
|
|
GameClientBridgeCommandFilterRequest,
|
|
GameClientBridgeCommandListResponse,
|
|
GameClientBridgeCommandResponse,
|
|
GameClientBridgeQueueRequest,
|
|
GameClientBridgeSnapshotListResponse,
|
|
GameClientBridgeSnapshotQuery,
|
|
GameClientBridgeStatusResponse,
|
|
GamePluginListResponse,
|
|
GamePlayerListResponse,
|
|
GamePlayerProfileResponse,
|
|
HealthResponse,
|
|
JobCreateRequest,
|
|
JobListResponse,
|
|
JobResponse,
|
|
LlmConfigSuggestionRequest,
|
|
LlmConfigSuggestionResponse,
|
|
LogBackfillRequest,
|
|
LogStreamCursorRequest,
|
|
LogStreamCursorResponse,
|
|
LogStreamListResponse,
|
|
LoginRequest,
|
|
MarketplacePluginFilterRequest,
|
|
MarketplacePluginListResponse,
|
|
MarketplacePluginResponse,
|
|
MarketplacePluginStateRequest,
|
|
PlatformResourceUsageResponse,
|
|
ProductionCapacitySummaryResponse,
|
|
CapacityAdmissionDecisionResponse,
|
|
PluginLifecycleActionRequest,
|
|
PluginLifecycleActionResponse,
|
|
PluginLifecycleListResponse,
|
|
PluginBridgeAuthorizeRequest,
|
|
PluginBridgeAuthorizeResponse,
|
|
PluginBridgeExecuteRequest,
|
|
PluginBridgeExecuteResponse,
|
|
RegisterRequest,
|
|
RunDistributionGenerateRequest,
|
|
RunDistributionResponse,
|
|
RunEndpointListResponse,
|
|
RunUpdateJobResponse,
|
|
RunUpdateJobListResponse,
|
|
RunUpdateRequest,
|
|
RuntimeBindingResponse,
|
|
RuntimeBindingUpdateRequest,
|
|
ServerConfigResponse,
|
|
ServerConfigDiffPreviewRequest,
|
|
ServerConfigDiffPreviewResponse,
|
|
ServerLifecycleCommandRequest,
|
|
ServerLifecycleCreateRequest,
|
|
ServerLifecycleResponse,
|
|
ServerDeploymentRequest,
|
|
ServerDeploymentRevealResponse,
|
|
ServerDeploymentResponse,
|
|
ServerConfigWriteApprovalRequest,
|
|
ServerConfigWriteDispatchResponse,
|
|
SourceRCONCommandRequest,
|
|
SourceRCONCommandResponse,
|
|
ServerInstanceListResponse,
|
|
ServerDeletionRequest,
|
|
ServerInstanceUpdateRequest,
|
|
ServerInstanceResponse,
|
|
ServerMemberListResponse,
|
|
ServerMemberRequest,
|
|
ServerMetricsListResponse,
|
|
MetricSampleListResponse,
|
|
BackupListResponse,
|
|
BackupResponse,
|
|
RemoteAdapterDeclarationListResponse,
|
|
RemoteAdapterRequest,
|
|
RemoteAdapterResponse,
|
|
ServerRuntimeActionsResponse,
|
|
UserCreateRequest,
|
|
UserListResponse,
|
|
UserProfileUpdateRequest,
|
|
UserResponse,
|
|
UserThemePreferenceRequest,
|
|
UserThemePreferenceResponse,
|
|
UserUpdateRequest
|
|
} from "./types";
|
|
import { readWebRuntimeEnv } from "../schemas/env";
|
|
import { parseSafeDependencyCatalog, parseSafeRunUpdate, parseSafeRunUpdateList } from "../schemas/runtimeUpdates";
|
|
import { parseSafeClientManagerLifecycle, parseSafeClientManagerLifecycleList } from "../schemas/clientManagerLifecycle";
|
|
import {
|
|
parseSafeGameClientBridgeCancellation,
|
|
parseSafeGameClientBridgeCommand,
|
|
parseSafeGameClientBridgeCommandList,
|
|
parseSafeGameClientBridgeSnapshotList,
|
|
parseSafeGameClientBridgeStatus
|
|
} from "../schemas/gameClientBridge";
|
|
import { safeDiagnosticText } from "../utils/safeDiagnosticText";
|
|
|
|
let platformApiSessionToken: string | null = null;
|
|
let platformApiAuthFailureHandler: ((error: PlatformApiError) => void) | null = null;
|
|
|
|
export function setPlatformApiSessionToken(token: string | null) {
|
|
platformApiSessionToken = token;
|
|
}
|
|
|
|
export function setPlatformApiAuthFailureHandler(handler: ((error: PlatformApiError) => void) | null) {
|
|
platformApiAuthFailureHandler = handler;
|
|
}
|
|
|
|
export class PlatformApiError extends Error {
|
|
constructor(
|
|
readonly status: number,
|
|
readonly code: string,
|
|
message: string
|
|
) {
|
|
super(message);
|
|
this.name = "PlatformApiError";
|
|
}
|
|
}
|
|
|
|
export class PlatformApiClient {
|
|
constructor(private readonly baseUrl = "/api/v1", private readonly sessionTokenProvider: () => string | null = () => platformApiSessionToken) {}
|
|
|
|
async health(): Promise<HealthResponse> {
|
|
return this.request<HealthResponse>("/healthz", { absolute: true });
|
|
}
|
|
|
|
async listGamePlugins(): Promise<GamePluginListResponse> {
|
|
return this.request<GamePluginListResponse>("/game-plugins");
|
|
}
|
|
|
|
async listMarketplacePlugins(filter: MarketplacePluginFilterRequest = {}): Promise<MarketplacePluginListResponse> {
|
|
return this.request<MarketplacePluginListResponse>(`/plugin-marketplace/plugins${marketplaceQuery(filter)}`);
|
|
}
|
|
|
|
async getMarketplacePlugin(id: string): Promise<MarketplacePluginResponse> {
|
|
return this.request<MarketplacePluginResponse>(`/plugin-marketplace/plugins/${encodeURIComponent(id)}`);
|
|
}
|
|
|
|
async setMarketplacePluginState(id: string, request: MarketplacePluginStateRequest): Promise<MarketplacePluginResponse> {
|
|
return this.request<MarketplacePluginResponse>(`/plugin-marketplace/plugins/${encodeURIComponent(id)}/state`, {
|
|
method: "POST",
|
|
body: request
|
|
});
|
|
}
|
|
|
|
async listServerInstances(): Promise<ServerInstanceListResponse> {
|
|
return this.request<ServerInstanceListResponse>("/server-instances");
|
|
}
|
|
|
|
async createServerWorkflow(request: ServerLifecycleCreateRequest): Promise<ServerLifecycleResponse> {
|
|
return this.request<ServerLifecycleResponse>("/server-instances/workflows/create", {
|
|
method: "POST",
|
|
body: request
|
|
});
|
|
}
|
|
|
|
async getServerDeployment(id: string): Promise<ServerDeploymentResponse> {
|
|
return this.request<ServerDeploymentResponse>(`/server-instances/${encodeURIComponent(id)}/deployment`);
|
|
}
|
|
|
|
async revealServerDeployment(id: string): Promise<ServerDeploymentRevealResponse> {
|
|
return this.request<ServerDeploymentRevealResponse>(`/server-instances/${encodeURIComponent(id)}/deployment/reveal`);
|
|
}
|
|
|
|
async updateServerDeployment(id: string, request: ServerDeploymentRequest): Promise<ServerDeploymentResponse> {
|
|
return this.request<ServerDeploymentResponse>(`/server-instances/${encodeURIComponent(id)}/deployment`, { method: "PUT", body: request });
|
|
}
|
|
|
|
async deployServerInstance(id: string, request: ServerLifecycleCommandRequest): Promise<ServerLifecycleResponse> {
|
|
return this.request<ServerLifecycleResponse>(`/server-instances/${encodeURIComponent(id)}/deploy`, { method: "POST", body: request });
|
|
}
|
|
|
|
async getServerRuntimeBinding(id: string): Promise<RuntimeBindingResponse> {
|
|
return this.request<RuntimeBindingResponse>(`/server-instances/${encodeURIComponent(id)}/runtime-binding`);
|
|
}
|
|
|
|
async updateServerRuntimeBinding(id: string, request: RuntimeBindingUpdateRequest): Promise<RuntimeBindingResponse> {
|
|
return this.request<RuntimeBindingResponse>(`/server-instances/${encodeURIComponent(id)}/runtime-binding`, { method: "PUT", body: request });
|
|
}
|
|
|
|
async startServerInstance(id: string, request: ServerLifecycleCommandRequest): Promise<ServerLifecycleResponse> {
|
|
return this.request<ServerLifecycleResponse>(`/server-instances/${encodeURIComponent(id)}/start`, {
|
|
method: "POST",
|
|
body: request
|
|
});
|
|
}
|
|
|
|
async stopServerInstance(id: string, request: ServerLifecycleCommandRequest): Promise<ServerLifecycleResponse> {
|
|
return this.request<ServerLifecycleResponse>(`/server-instances/${encodeURIComponent(id)}/stop`, {
|
|
method: "POST",
|
|
body: request
|
|
});
|
|
}
|
|
|
|
async queryServerProcessStatus(id: string, request: ServerLifecycleCommandRequest): Promise<ServerLifecycleResponse> {
|
|
return this.request<ServerLifecycleResponse>(`/server-instances/${encodeURIComponent(id)}/process/status`, {
|
|
method: "POST",
|
|
body: request
|
|
});
|
|
}
|
|
|
|
async listServerAdministratorCandidates(id: string): Promise<ServerMemberListResponse> {
|
|
return this.request<ServerMemberListResponse>(`/server-instances/${encodeURIComponent(id)}/administrators/candidates`);
|
|
}
|
|
|
|
async addServerAdministrator(id: string, request: ServerMemberRequest): Promise<ServerInstanceResponse> {
|
|
return this.request<ServerInstanceResponse>(`/server-instances/${encodeURIComponent(id)}/administrators`, {
|
|
method: "POST",
|
|
body: request
|
|
});
|
|
}
|
|
|
|
async removeServerAdministrator(id: string, userId: string): Promise<ServerInstanceResponse> {
|
|
return this.request<ServerInstanceResponse>(`/server-instances/${encodeURIComponent(id)}/administrators/${encodeURIComponent(userId)}`, {
|
|
method: "DELETE"
|
|
});
|
|
}
|
|
|
|
async listRunEndpoints(): Promise<RunEndpointListResponse> {
|
|
return this.request<RunEndpointListResponse>("/run/endpoints");
|
|
}
|
|
|
|
async listJobs(serverInstanceId?: string): Promise<JobListResponse> {
|
|
const params = serverInstanceId ? `?serverInstanceId=${encodeURIComponent(serverInstanceId)}` : "";
|
|
return this.request<JobListResponse>(`/jobs${params}`);
|
|
}
|
|
|
|
async listArtifacts(filter: ArtifactFilterRequest = {}): Promise<ArtifactListResponse> {
|
|
return this.request<ArtifactListResponse>(`/artifacts${artifactQuery(filter)}`);
|
|
}
|
|
|
|
async openArtifactDownload(id: string): Promise<ArtifactDownloadReferenceResponse> {
|
|
return this.request<ArtifactDownloadReferenceResponse>(`/artifacts/${encodeURIComponent(id)}/download`, { method: "POST", body: {} });
|
|
}
|
|
|
|
async readArtifactContent(id: string, offset = 0, limit?: number): Promise<ArtifactContentChunk> {
|
|
const params = new URLSearchParams({ offset: String(offset) });
|
|
if (limit !== undefined) {
|
|
params.set("limit", String(limit));
|
|
}
|
|
const headers = new Headers();
|
|
const sessionToken = this.sessionTokenProvider();
|
|
if (sessionToken) {
|
|
headers.set("Authorization", `Bearer ${sessionToken}`);
|
|
}
|
|
const response = await fetch(`${this.baseUrl}/artifacts/${encodeURIComponent(id)}/content?${params.toString()}`, { headers, credentials: "same-origin" });
|
|
if (!response.ok) {
|
|
throw await responseError(response);
|
|
}
|
|
const payload = await response.arrayBuffer();
|
|
return {
|
|
artifactId: response.headers.get("X-Artifact-Id") ?? undefined,
|
|
payload,
|
|
contentType: response.headers.get("Content-Type") ?? "application/octet-stream",
|
|
contentLength: Number(response.headers.get("Content-Length") ?? payload.byteLength),
|
|
contentRange: response.headers.get("Content-Range") ?? undefined,
|
|
checksum: response.headers.get("X-Artifact-Checksum") ?? undefined,
|
|
contentChecksum: response.headers.get("X-Artifact-Content-Checksum") ?? undefined,
|
|
storageBehavior: response.headers.get("X-Artifact-Storage") ?? undefined
|
|
};
|
|
}
|
|
|
|
async getJob(id: string): Promise<JobResponse> {
|
|
return this.request<JobResponse>(`/jobs/${encodeURIComponent(id)}`);
|
|
}
|
|
|
|
async getServerRuntimeActions(id: string): Promise<ServerRuntimeActionsResponse> {
|
|
return this.request<ServerRuntimeActionsResponse>(`/server-instances/${encodeURIComponent(id)}/runtime/actions`);
|
|
}
|
|
|
|
async generateRunDistribution(id: string, request: RunDistributionGenerateRequest): Promise<RunDistributionResponse> {
|
|
return this.request<RunDistributionResponse>(`/server-instances/${encodeURIComponent(id)}/run/generate`, {
|
|
method: "POST",
|
|
body: request
|
|
});
|
|
}
|
|
|
|
async downloadLatestRunDistribution(id: string): Promise<ArtifactDownloadReferenceResponse> {
|
|
return this.request<ArtifactDownloadReferenceResponse>(`/server-instances/${encodeURIComponent(id)}/run/download`, {
|
|
method: "POST",
|
|
body: {}
|
|
});
|
|
}
|
|
|
|
async resetRunKey(id: string): Promise<ComponentKeyResponse> {
|
|
return this.request<ComponentKeyResponse>(`/server-instances/${encodeURIComponent(id)}/run/key/reset`, {
|
|
method: "POST",
|
|
body: {}
|
|
});
|
|
}
|
|
|
|
async pushRunUpdate(id: string, request: RunUpdateRequest): Promise<RunUpdateJobResponse> {
|
|
const response = await this.request<unknown>(`/server-instances/${encodeURIComponent(id)}/run/update`, {
|
|
method: "POST",
|
|
body: request
|
|
});
|
|
return parseSafeRunUpdate(response);
|
|
}
|
|
|
|
async listRunUpdates(id: string): Promise<RunUpdateJobListResponse> {
|
|
return parseSafeRunUpdateList(await this.request<unknown>(`/server-instances/${encodeURIComponent(id)}/run/update`));
|
|
}
|
|
|
|
async generateClientManager(id: string, request: ClientManagerBuildRequest): Promise<ClientManagerDistributionResponse> {
|
|
return this.request<ClientManagerDistributionResponse>(`/server-instances/${encodeURIComponent(id)}/client-managers/generate`, {
|
|
method: "POST",
|
|
body: request
|
|
});
|
|
}
|
|
|
|
async downloadLatestClientManager(id: string, request: ClientManagerDownloadRequest = {}): Promise<ArtifactDownloadReferenceResponse> {
|
|
return this.request<ArtifactDownloadReferenceResponse>(`/server-instances/${encodeURIComponent(id)}/client-managers/download`, {
|
|
method: "POST",
|
|
body: request
|
|
});
|
|
}
|
|
|
|
async resetClientManagerKey(id: string, request: ComponentKeyResetRequest): Promise<ComponentKeyResponse> {
|
|
return this.request<ComponentKeyResponse>(`/server-instances/${encodeURIComponent(id)}/client-managers/key/reset`, {
|
|
method: "POST",
|
|
body: request
|
|
});
|
|
}
|
|
|
|
async listClientManagerLifecycles(id: string): Promise<ClientManagerInstallationListResponse> {
|
|
return parseSafeClientManagerLifecycleList(await this.request<unknown>(`/server-instances/${encodeURIComponent(id)}/client-managers`));
|
|
}
|
|
|
|
async getClientManagerLifecycle(id: string, profileKey: string): Promise<ClientManagerInstallationResponse> {
|
|
return parseSafeClientManagerLifecycle(await this.request<unknown>(`/server-instances/${encodeURIComponent(id)}/client-managers/${encodeURIComponent(profileKey)}`));
|
|
}
|
|
|
|
async deployClientManager(id: string, request: ClientManagerDeployRequest): Promise<ClientManagerInstallationResponse> {
|
|
return parseSafeClientManagerLifecycle(await this.request<unknown>(`/server-instances/${encodeURIComponent(id)}/client-managers/deploy`, { method: "POST", body: request }));
|
|
}
|
|
|
|
async controlClientManager(id: string, request: ClientManagerControlRequest): Promise<ClientManagerInstallationResponse> {
|
|
return parseSafeClientManagerLifecycle(await this.request<unknown>(`/server-instances/${encodeURIComponent(id)}/client-managers/control`, { method: "POST", body: request }));
|
|
}
|
|
|
|
async updateClientManager(id: string, request: ClientManagerUpdateRequest): Promise<ClientManagerInstallationResponse> {
|
|
return parseSafeClientManagerLifecycle(await this.request<unknown>(`/server-instances/${encodeURIComponent(id)}/client-managers/update`, { method: "POST", body: request }));
|
|
}
|
|
|
|
async retryClientManagerLifecycle(id: string, request: ClientManagerRetryRequest): Promise<ClientManagerInstallationResponse> {
|
|
return parseSafeClientManagerLifecycle(await this.request<unknown>(`/server-instances/${encodeURIComponent(id)}/client-managers/retry`, { method: "POST", body: request }));
|
|
}
|
|
|
|
async revokeClientManagerSession(id: string, request: ClientManagerRevokeSessionRequest): Promise<ClientManagerInstallationResponse> {
|
|
return parseSafeClientManagerLifecycle(await this.request<unknown>(`/server-instances/${encodeURIComponent(id)}/client-managers/revoke-session`, { method: "POST", body: request }));
|
|
}
|
|
|
|
async uninstallClientManager(id: string, request: ClientManagerUninstallRequest): Promise<ClientManagerInstallationResponse> {
|
|
return parseSafeClientManagerLifecycle(await this.request<unknown>(`/server-instances/${encodeURIComponent(id)}/client-managers/uninstall`, { method: "POST", body: request }));
|
|
}
|
|
|
|
async getGameClientBridgeStatus(id: string): Promise<GameClientBridgeStatusResponse> {
|
|
return parseSafeGameClientBridgeStatus(await this.request<unknown>(`/server-instances/${encodeURIComponent(id)}/game-client-bridge`));
|
|
}
|
|
|
|
async listGameClientBridgeCommands(id: string, filter: GameClientBridgeCommandFilterRequest = {}): Promise<GameClientBridgeCommandListResponse> {
|
|
const params = new URLSearchParams();
|
|
if (filter.profileKey) params.set("profileKey", filter.profileKey);
|
|
if (filter.state) params.set("state", filter.state);
|
|
if (filter.commandType) params.set("commandType", filter.commandType);
|
|
const query = params.toString();
|
|
return parseSafeGameClientBridgeCommandList(await this.request<unknown>(`/server-instances/${encodeURIComponent(id)}/game-client-bridge/commands${query ? `?${query}` : ""}`));
|
|
}
|
|
|
|
async queueGameClientBridgeCommand(id: string, request: GameClientBridgeQueueRequest): Promise<GameClientBridgeCommandResponse> {
|
|
return parseSafeGameClientBridgeCommand(await this.request<unknown>(`/server-instances/${encodeURIComponent(id)}/game-client-bridge/commands`, { method: "POST", body: request }));
|
|
}
|
|
|
|
async getGameClientBridgeCommand(id: string, commandId: string): Promise<GameClientBridgeCommandResponse> {
|
|
return parseSafeGameClientBridgeCommand(await this.request<unknown>(`/server-instances/${encodeURIComponent(id)}/game-client-bridge/commands/${encodeURIComponent(commandId)}`));
|
|
}
|
|
|
|
async cancelGameClientBridgeCommand(id: string, commandId: string, request: GameClientBridgeCancelRequest = {}): Promise<GameClientBridgeCancelResponse> {
|
|
return parseSafeGameClientBridgeCancellation(await this.request<unknown>(`/server-instances/${encodeURIComponent(id)}/game-client-bridge/commands/${encodeURIComponent(commandId)}/cancel`, { method: "POST", body: request }));
|
|
}
|
|
|
|
async listGameClientBridgeSnapshots(id: string, query: GameClientBridgeSnapshotQuery = {}): Promise<GameClientBridgeSnapshotListResponse> {
|
|
const params = new URLSearchParams();
|
|
if (query.profileKey) params.set("profileKey", query.profileKey);
|
|
if (query.type) params.set("type", query.type);
|
|
if (query.streamKey) params.set("streamKey", query.streamKey);
|
|
if (query.observedAfter) params.set("observedAfter", query.observedAfter);
|
|
if (query.limit !== undefined) params.set("limit", String(query.limit));
|
|
const search = params.toString();
|
|
return parseSafeGameClientBridgeSnapshotList(await this.request<unknown>(`/server-instances/${encodeURIComponent(id)}/game-client-bridge/snapshots${search ? `?${search}` : ""}`));
|
|
}
|
|
|
|
async checkDependencies(id: string, request: DependencyJobRequest): Promise<JobResponse> {
|
|
return this.request<JobResponse>(`/server-instances/${encodeURIComponent(id)}/dependencies/check`, {
|
|
method: "POST",
|
|
body: request
|
|
});
|
|
}
|
|
|
|
async getDependencyCatalog(id: string): Promise<DependencyCatalogResponse> {
|
|
return parseSafeDependencyCatalog(await this.request<unknown>(`/server-instances/${encodeURIComponent(id)}/dependencies`));
|
|
}
|
|
|
|
async installDependencies(id: string, request: DependencyJobRequest): Promise<JobResponse> {
|
|
return this.request<JobResponse>(`/server-instances/${encodeURIComponent(id)}/dependencies/install`, {
|
|
method: "POST",
|
|
body: request
|
|
});
|
|
}
|
|
|
|
async listServerLiveLogs(id: string): Promise<LogStreamListResponse> {
|
|
return this.request<LogStreamListResponse>(`/server-instances/${encodeURIComponent(id)}/logs/live`);
|
|
}
|
|
|
|
async requestLogBackfill(id: string, request: LogBackfillRequest): Promise<JobResponse> {
|
|
return this.request<JobResponse>(`/server-instances/${encodeURIComponent(id)}/logs/backfill`, {
|
|
method: "POST",
|
|
body: request
|
|
});
|
|
}
|
|
|
|
async createJob(request: JobCreateRequest): Promise<JobResponse> {
|
|
return this.request<JobResponse>("/jobs", { method: "POST", body: request });
|
|
}
|
|
|
|
async register(request: RegisterRequest): Promise<AuthSessionResponse> {
|
|
return this.request<AuthSessionResponse>("/auth/register", { method: "POST", body: request });
|
|
}
|
|
|
|
async login(request: LoginRequest): Promise<AuthSessionResponse> {
|
|
return this.request<AuthSessionResponse>("/auth/login", { method: "POST", body: request });
|
|
}
|
|
|
|
async logout(): Promise<void> {
|
|
await this.request<void>("/auth/logout", { method: "POST", parseJson: false });
|
|
}
|
|
|
|
async getCurrentUser(): Promise<CurrentUserResponse> {
|
|
return this.request<CurrentUserResponse>("/users/current");
|
|
}
|
|
|
|
async listUsers(): Promise<UserListResponse> {
|
|
return this.request<UserListResponse>("/users");
|
|
}
|
|
|
|
async createUser(request: UserCreateRequest): Promise<UserResponse> {
|
|
return this.request<UserResponse>("/users", { method: "POST", body: request });
|
|
}
|
|
|
|
async updateUser(id: string, request: UserUpdateRequest): Promise<UserResponse> {
|
|
return this.request<UserResponse>(`/users/${encodeURIComponent(id)}`, { method: "PUT", body: request });
|
|
}
|
|
|
|
async updateCurrentUserProfile(request: UserProfileUpdateRequest): Promise<CurrentUserResponse> {
|
|
return this.request<CurrentUserResponse>("/users/current/profile", { method: "PUT", body: request });
|
|
}
|
|
|
|
async updateCurrentUserTheme(request: UserThemePreferenceRequest): Promise<UserThemePreferenceResponse> {
|
|
return this.request<UserThemePreferenceResponse>("/users/current/theme", { method: "PUT", body: request });
|
|
}
|
|
|
|
async getServerInstance(id: string): Promise<ServerInstanceResponse> {
|
|
return this.request<ServerInstanceResponse>(`/server-instances/${encodeURIComponent(id)}`);
|
|
}
|
|
|
|
async updateServerInstance(id: string, request: ServerInstanceUpdateRequest): Promise<ServerInstanceResponse> {
|
|
return this.request<ServerInstanceResponse>(`/server-instances/${encodeURIComponent(id)}`, {
|
|
method: "PUT",
|
|
body: request
|
|
});
|
|
}
|
|
|
|
async deleteServerInstance(id: string, request: ServerDeletionRequest): Promise<void> {
|
|
return this.request<void>(`/server-instances/${encodeURIComponent(id)}`, { method: "DELETE", body: request, parseJson: false });
|
|
}
|
|
|
|
async getPlatformResourceUsage(): Promise<PlatformResourceUsageResponse> {
|
|
return this.request<PlatformResourceUsageResponse>("/metrics/platform");
|
|
}
|
|
|
|
async getProductionCapacity(): Promise<ProductionCapacitySummaryResponse> {
|
|
return this.request<ProductionCapacitySummaryResponse>("/production/capacity");
|
|
}
|
|
|
|
async checkCapacityAdmission(request: { serverInstanceId?: string; runEndpointId?: string; capability: string; targetKey?: string; idempotencyKey?: string }): Promise<CapacityAdmissionDecisionResponse> {
|
|
return this.request<CapacityAdmissionDecisionResponse>("/production/capacity/admission", { method: "POST", body: request });
|
|
}
|
|
|
|
async listAlerts(filter: { state?: string; sourceKind?: string; sourceId?: string; severity?: string } = {}): Promise<AlertListResponse> {
|
|
const params = new URLSearchParams();
|
|
Object.entries(filter).forEach(([key, value]) => { if (value) params.set(key, value); });
|
|
const query = params.toString();
|
|
return this.request<AlertListResponse>(`/alerts${query ? `?${query}` : ""}`);
|
|
}
|
|
|
|
async acknowledgeAlert(id: string, note = ""): Promise<AlertResponse> {
|
|
return this.request<AlertResponse>(`/alerts/${encodeURIComponent(id)}/acknowledge`, { method: "POST", body: { note } });
|
|
}
|
|
|
|
async resolveAlert(id: string, note = ""): Promise<AlertResponse> {
|
|
return this.request<AlertResponse>(`/alerts/${encodeURIComponent(id)}/resolve`, { method: "POST", body: { note } });
|
|
}
|
|
|
|
async retryAlert(id: string, idempotencyKey: string): Promise<AlertRetryResponse> {
|
|
return this.request<AlertRetryResponse>(`/alerts/${encodeURIComponent(id)}/retry`, { method: "POST", body: { idempotencyKey } });
|
|
}
|
|
|
|
async listPluginLifecycles(filter: { pluginId?: string; serverInstanceId?: string; currentState?: string } = {}): Promise<PluginLifecycleListResponse> {
|
|
const params = new URLSearchParams();
|
|
Object.entries(filter).forEach(([key, value]) => { if (value) params.set(key, value); });
|
|
const query = params.toString();
|
|
return this.request<PluginLifecycleListResponse>(`/plugin-lifecycles${query ? `?${query}` : ""}`);
|
|
}
|
|
|
|
async runPluginLifecycle(pluginId: string, request: PluginLifecycleActionRequest): Promise<PluginLifecycleActionResponse> {
|
|
return this.request<PluginLifecycleActionResponse>(`/plugin-lifecycles/${encodeURIComponent(pluginId)}/actions`, { method: "POST", body: request });
|
|
}
|
|
|
|
async listAIConfigDiffs(filter: { serverInstanceId?: string; pluginId?: string; state?: string } = {}): Promise<AIConfigDiffListResponse> {
|
|
const params = new URLSearchParams();
|
|
Object.entries(filter).forEach(([key, value]) => { if (value) params.set(key, value); });
|
|
const query = params.toString();
|
|
return this.request<AIConfigDiffListResponse>(`/ai/config-diffs${query ? `?${query}` : ""}`);
|
|
}
|
|
|
|
async approveAIConfigDiff(id: string, idempotencyKey: string): Promise<AIConfigDiffApprovalResponse> {
|
|
return this.request<AIConfigDiffApprovalResponse>(`/ai/config-diffs/${encodeURIComponent(id)}/approve`, { method: "POST", body: { idempotencyKey } });
|
|
}
|
|
|
|
async listServerMetrics(): Promise<ServerMetricsListResponse> {
|
|
return this.request<ServerMetricsListResponse>("/metrics/server-instances");
|
|
}
|
|
|
|
async listMetricHistory(serverInstanceId: string, limit = 100): Promise<MetricSampleListResponse> {
|
|
const params = new URLSearchParams({ serverInstanceId, limit: String(limit) });
|
|
return this.request<MetricSampleListResponse>(`/metrics/server-instances/history?${params.toString()}`);
|
|
}
|
|
|
|
async listBackups(serverInstanceId: string): Promise<BackupListResponse> {
|
|
return this.request<BackupListResponse>(`/backups?serverInstanceId=${encodeURIComponent(serverInstanceId)}`);
|
|
}
|
|
|
|
async listGamePlayers(serverInstanceId: string, search = ""): Promise<GamePlayerListResponse> {
|
|
const query = search ? `?search=${encodeURIComponent(search)}` : "";
|
|
return this.request<GamePlayerListResponse>(`/server-instances/${encodeURIComponent(serverInstanceId)}/game-players${query}`);
|
|
}
|
|
|
|
async getGamePlayerProfile(serverInstanceId: string, playerId: string): Promise<GamePlayerProfileResponse> {
|
|
return this.request<GamePlayerProfileResponse>(`/server-instances/${encodeURIComponent(serverInstanceId)}/game-players/${encodeURIComponent(playerId)}`);
|
|
}
|
|
|
|
async getBackup(id: string): Promise<BackupResponse> {
|
|
return this.request<BackupResponse>(`/backups/${encodeURIComponent(id)}`);
|
|
}
|
|
|
|
async listRemoteAdapters(serverInstanceId: string): Promise<RemoteAdapterDeclarationListResponse> {
|
|
return this.request<RemoteAdapterDeclarationListResponse>(`/server-instances/${encodeURIComponent(serverInstanceId)}/remote-adapters`);
|
|
}
|
|
|
|
async requestRemoteAdapter(serverInstanceId: string, request: RemoteAdapterRequest): Promise<RemoteAdapterResponse> {
|
|
return this.request<RemoteAdapterResponse>(`/server-instances/${encodeURIComponent(serverInstanceId)}/remote-adapters`, { method: "POST", body: request });
|
|
}
|
|
|
|
async sendSourceRCONCommand(serverInstanceId: string, request: SourceRCONCommandRequest): Promise<SourceRCONCommandResponse> {
|
|
return this.request<SourceRCONCommandResponse>(`/server-instances/${encodeURIComponent(serverInstanceId)}/rcon/commands`, { method: "POST", body: request });
|
|
}
|
|
|
|
async getServerConfig(id: string): Promise<ServerConfigResponse> {
|
|
return this.request<ServerConfigResponse>(`/server-instances/${encodeURIComponent(id)}/config`);
|
|
}
|
|
|
|
async previewServerConfigDiff(id: string, request: ServerConfigDiffPreviewRequest): Promise<ServerConfigDiffPreviewResponse> {
|
|
return this.request<ServerConfigDiffPreviewResponse>(`/server-instances/${encodeURIComponent(id)}/config/diff`, {
|
|
method: "POST",
|
|
body: request
|
|
});
|
|
}
|
|
|
|
async approveServerConfigWrite(id: string, request: ServerConfigWriteApprovalRequest): Promise<ServerConfigWriteDispatchResponse> {
|
|
return this.request<ServerConfigWriteDispatchResponse>(`/server-instances/${encodeURIComponent(id)}/config/approve`, {
|
|
method: "POST",
|
|
body: request
|
|
});
|
|
}
|
|
|
|
async dispatchFileOperation(request: FileOperationDispatchRequest): Promise<FileOperationDispatchResponse> {
|
|
return this.request<FileOperationDispatchResponse>("/file-operations/dispatch", {
|
|
method: "POST",
|
|
body: request
|
|
});
|
|
}
|
|
|
|
async listLogStreams(): Promise<LogStreamListResponse> {
|
|
return this.request<LogStreamListResponse>("/log-streams");
|
|
}
|
|
|
|
async queryLogStream(request: LogStreamCursorRequest): Promise<LogStreamCursorResponse> {
|
|
return this.request<LogStreamCursorResponse>("/log-streams/query", { method: "POST", body: request });
|
|
}
|
|
|
|
async listAuditEvents(): Promise<AuditEventListResponse> {
|
|
return this.request<AuditEventListResponse>("/audit-events");
|
|
}
|
|
|
|
async suggestServerConfig(request: LlmConfigSuggestionRequest): Promise<LlmConfigSuggestionResponse> {
|
|
return this.request<LlmConfigSuggestionResponse>("/ai/config-suggestions", { method: "POST", body: request });
|
|
}
|
|
|
|
async invokeAI(request: AIInvocationRequest): Promise<AIInvocationResponse> {
|
|
return this.request<AIInvocationResponse>("/ai/invocations", { method: "POST", body: request });
|
|
}
|
|
|
|
async authorizePluginBridge(request: PluginBridgeAuthorizeRequest): Promise<PluginBridgeAuthorizeResponse> {
|
|
return this.request<PluginBridgeAuthorizeResponse>("/plugin-bridge/authorize", {
|
|
method: "POST",
|
|
body: request
|
|
});
|
|
}
|
|
|
|
async executePluginBridge(request: PluginBridgeExecuteRequest): Promise<PluginBridgeExecuteResponse> {
|
|
return this.request<PluginBridgeExecuteResponse>("/plugin-bridge/execute", {
|
|
method: "POST",
|
|
body: request
|
|
});
|
|
}
|
|
|
|
async listAiProviders(): Promise<AiProviderListResponse> {
|
|
return this.request<AiProviderListResponse>("/ai-providers");
|
|
}
|
|
|
|
async createAiProvider(request: AiProviderRequest): Promise<AiProviderResponse> {
|
|
return this.request<AiProviderResponse>("/ai-providers", {
|
|
method: "POST",
|
|
body: request
|
|
});
|
|
}
|
|
|
|
async updateAiProvider(id: string, request: AiProviderUpdateRequest): Promise<AiProviderResponse> {
|
|
return this.request<AiProviderResponse>(`/ai-providers/${encodeURIComponent(id)}`, {
|
|
method: "PUT",
|
|
body: request
|
|
});
|
|
}
|
|
|
|
async setAiProviderStatus(id: string, request: AiProviderStatusRequest): Promise<AiProviderResponse> {
|
|
return this.request<AiProviderResponse>(`/ai-providers/${encodeURIComponent(id)}/status`, {
|
|
method: "POST",
|
|
body: request
|
|
});
|
|
}
|
|
|
|
async testAiProvider(id: string): Promise<AiProviderTestResponse> {
|
|
return this.request<AiProviderTestResponse>(`/ai-providers/${encodeURIComponent(id)}/test`, {
|
|
method: "POST"
|
|
});
|
|
}
|
|
|
|
async listAiProviderModels(id: string): Promise<AiProviderModelsResponse> {
|
|
return this.request<AiProviderModelsResponse>(`/ai-providers/${encodeURIComponent(id)}/models`);
|
|
}
|
|
|
|
private async request<T>(path: string, options: ApiRequestOptions = {}): Promise<T> {
|
|
const headers = new Headers(options.init?.headers);
|
|
if (options.body !== undefined) {
|
|
headers.set("Content-Type", "application/json");
|
|
}
|
|
const sessionToken = this.sessionTokenProvider();
|
|
if (sessionToken && !headers.has("Authorization")) {
|
|
headers.set("Authorization", `Bearer ${sessionToken}`);
|
|
}
|
|
|
|
const response = await fetch(options.absolute ? path : `${this.baseUrl}${path}`, {
|
|
...options.init,
|
|
credentials: options.init?.credentials ?? "same-origin",
|
|
method: options.method ?? options.init?.method ?? "GET",
|
|
headers,
|
|
body: options.body === undefined ? options.init?.body : JSON.stringify(options.body)
|
|
});
|
|
|
|
if (!response.ok) {
|
|
throw await responseError(response);
|
|
}
|
|
|
|
if (options.parseJson === false || response.status === 204) {
|
|
return undefined as T;
|
|
}
|
|
|
|
return response.json() as Promise<T>;
|
|
}
|
|
}
|
|
|
|
interface ApiRequestOptions {
|
|
absolute?: boolean;
|
|
method?: string;
|
|
body?: unknown;
|
|
init?: RequestInit;
|
|
parseJson?: boolean;
|
|
}
|
|
|
|
async function safeReadError(response: Response): Promise<ApiErrorResponse | null> {
|
|
try {
|
|
return (await response.json()) as ApiErrorResponse;
|
|
} catch {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
async function responseError(response: Response): Promise<PlatformApiError> {
|
|
const apiError = await safeReadError(response);
|
|
const message = response.status === 401
|
|
? "会话已失效,请重新登录。"
|
|
: response.status === 403
|
|
? safeForbiddenMessage(apiError?.message)
|
|
: apiError?.message ?? `request failed: ${response.status}`;
|
|
const error = new PlatformApiError(response.status, apiError?.code ?? "request_failed", message);
|
|
if (response.status === 401) {
|
|
platformApiSessionToken = null;
|
|
platformApiAuthFailureHandler?.(error);
|
|
}
|
|
return error;
|
|
}
|
|
|
|
function safeForbiddenMessage(apiMessage?: string): string {
|
|
const sanitized = safeDiagnosticText(apiMessage, "")?.trim();
|
|
if (!sanitized || sanitized === "account is not allowed to access this resource") {
|
|
return "没有权限访问该资源。";
|
|
}
|
|
const missingPermission = sanitized.match(/^plugin does not declare required permission:\s*([a-z0-9._-]+)$/i);
|
|
if (missingPermission) {
|
|
return `插件未声明所需权限:${missingPermission[1]}`;
|
|
}
|
|
if (sanitized === "plugin is not installed") {
|
|
return "插件未安装,不能执行该操作。";
|
|
}
|
|
return "没有权限访问该资源。";
|
|
}
|
|
|
|
function marketplaceQuery(filter: MarketplacePluginFilterRequest): string {
|
|
const params = new URLSearchParams();
|
|
if (filter.status && filter.status !== "all") {
|
|
params.set("status", filter.status);
|
|
}
|
|
if (filter.serverType) {
|
|
params.set("serverType", filter.serverType);
|
|
}
|
|
if (filter.capability) {
|
|
params.set("capability", filter.capability);
|
|
}
|
|
if (filter.keyword) {
|
|
params.set("keyword", filter.keyword);
|
|
}
|
|
const query = params.toString();
|
|
return query ? `?${query}` : "";
|
|
}
|
|
|
|
function artifactQuery(filter: ArtifactFilterRequest): string {
|
|
const params = new URLSearchParams();
|
|
if (filter.ownerKind) {
|
|
params.set("ownerKind", filter.ownerKind);
|
|
}
|
|
if (filter.ownerId) {
|
|
params.set("ownerId", filter.ownerId);
|
|
}
|
|
if (filter.state) {
|
|
params.set("state", filter.state);
|
|
}
|
|
const query = params.toString();
|
|
return query ? `?${query}` : "";
|
|
}
|
|
|
|
export const platformApiClient = new PlatformApiClient(readWebRuntimeEnv().platformApiBaseUrl);
|