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 { return this.request("/healthz", { absolute: true }); } async listGamePlugins(): Promise { return this.request("/game-plugins"); } async listMarketplacePlugins(filter: MarketplacePluginFilterRequest = {}): Promise { return this.request(`/plugin-marketplace/plugins${marketplaceQuery(filter)}`); } async getMarketplacePlugin(id: string): Promise { return this.request(`/plugin-marketplace/plugins/${encodeURIComponent(id)}`); } async setMarketplacePluginState(id: string, request: MarketplacePluginStateRequest): Promise { return this.request(`/plugin-marketplace/plugins/${encodeURIComponent(id)}/state`, { method: "POST", body: request }); } async listServerInstances(): Promise { return this.request("/server-instances"); } async createServerWorkflow(request: ServerLifecycleCreateRequest): Promise { return this.request("/server-instances/workflows/create", { method: "POST", body: request }); } async getServerDeployment(id: string): Promise { return this.request(`/server-instances/${encodeURIComponent(id)}/deployment`); } async revealServerDeployment(id: string): Promise { return this.request(`/server-instances/${encodeURIComponent(id)}/deployment/reveal`); } async updateServerDeployment(id: string, request: ServerDeploymentRequest): Promise { return this.request(`/server-instances/${encodeURIComponent(id)}/deployment`, { method: "PUT", body: request }); } async deployServerInstance(id: string, request: ServerLifecycleCommandRequest): Promise { return this.request(`/server-instances/${encodeURIComponent(id)}/deploy`, { method: "POST", body: request }); } async getServerRuntimeBinding(id: string): Promise { return this.request(`/server-instances/${encodeURIComponent(id)}/runtime-binding`); } async updateServerRuntimeBinding(id: string, request: RuntimeBindingUpdateRequest): Promise { return this.request(`/server-instances/${encodeURIComponent(id)}/runtime-binding`, { method: "PUT", body: request }); } async startServerInstance(id: string, request: ServerLifecycleCommandRequest): Promise { return this.request(`/server-instances/${encodeURIComponent(id)}/start`, { method: "POST", body: request }); } async stopServerInstance(id: string, request: ServerLifecycleCommandRequest): Promise { return this.request(`/server-instances/${encodeURIComponent(id)}/stop`, { method: "POST", body: request }); } async queryServerProcessStatus(id: string, request: ServerLifecycleCommandRequest): Promise { return this.request(`/server-instances/${encodeURIComponent(id)}/process/status`, { method: "POST", body: request }); } async listServerAdministratorCandidates(id: string): Promise { return this.request(`/server-instances/${encodeURIComponent(id)}/administrators/candidates`); } async addServerAdministrator(id: string, request: ServerMemberRequest): Promise { return this.request(`/server-instances/${encodeURIComponent(id)}/administrators`, { method: "POST", body: request }); } async removeServerAdministrator(id: string, userId: string): Promise { return this.request(`/server-instances/${encodeURIComponent(id)}/administrators/${encodeURIComponent(userId)}`, { method: "DELETE" }); } async listRunEndpoints(): Promise { return this.request("/run/endpoints"); } async listJobs(serverInstanceId?: string): Promise { const params = serverInstanceId ? `?serverInstanceId=${encodeURIComponent(serverInstanceId)}` : ""; return this.request(`/jobs${params}`); } async listArtifacts(filter: ArtifactFilterRequest = {}): Promise { return this.request(`/artifacts${artifactQuery(filter)}`); } async openArtifactDownload(id: string): Promise { return this.request(`/artifacts/${encodeURIComponent(id)}/download`, { method: "POST", body: {} }); } async readArtifactContent(id: string, offset = 0, limit?: number): Promise { 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 { return this.request(`/jobs/${encodeURIComponent(id)}`); } async getServerRuntimeActions(id: string): Promise { return this.request(`/server-instances/${encodeURIComponent(id)}/runtime/actions`); } async generateRunDistribution(id: string, request: RunDistributionGenerateRequest): Promise { return this.request(`/server-instances/${encodeURIComponent(id)}/run/generate`, { method: "POST", body: request }); } async downloadLatestRunDistribution(id: string): Promise { return this.request(`/server-instances/${encodeURIComponent(id)}/run/download`, { method: "POST", body: {} }); } async resetRunKey(id: string): Promise { return this.request(`/server-instances/${encodeURIComponent(id)}/run/key/reset`, { method: "POST", body: {} }); } async pushRunUpdate(id: string, request: RunUpdateRequest): Promise { const response = await this.request(`/server-instances/${encodeURIComponent(id)}/run/update`, { method: "POST", body: request }); return parseSafeRunUpdate(response); } async listRunUpdates(id: string): Promise { return parseSafeRunUpdateList(await this.request(`/server-instances/${encodeURIComponent(id)}/run/update`)); } async generateClientManager(id: string, request: ClientManagerBuildRequest): Promise { return this.request(`/server-instances/${encodeURIComponent(id)}/client-managers/generate`, { method: "POST", body: request }); } async downloadLatestClientManager(id: string, request: ClientManagerDownloadRequest = {}): Promise { return this.request(`/server-instances/${encodeURIComponent(id)}/client-managers/download`, { method: "POST", body: request }); } async resetClientManagerKey(id: string, request: ComponentKeyResetRequest): Promise { return this.request(`/server-instances/${encodeURIComponent(id)}/client-managers/key/reset`, { method: "POST", body: request }); } async listClientManagerLifecycles(id: string): Promise { return parseSafeClientManagerLifecycleList(await this.request(`/server-instances/${encodeURIComponent(id)}/client-managers`)); } async getClientManagerLifecycle(id: string, profileKey: string): Promise { return parseSafeClientManagerLifecycle(await this.request(`/server-instances/${encodeURIComponent(id)}/client-managers/${encodeURIComponent(profileKey)}`)); } async deployClientManager(id: string, request: ClientManagerDeployRequest): Promise { return parseSafeClientManagerLifecycle(await this.request(`/server-instances/${encodeURIComponent(id)}/client-managers/deploy`, { method: "POST", body: request })); } async controlClientManager(id: string, request: ClientManagerControlRequest): Promise { return parseSafeClientManagerLifecycle(await this.request(`/server-instances/${encodeURIComponent(id)}/client-managers/control`, { method: "POST", body: request })); } async updateClientManager(id: string, request: ClientManagerUpdateRequest): Promise { return parseSafeClientManagerLifecycle(await this.request(`/server-instances/${encodeURIComponent(id)}/client-managers/update`, { method: "POST", body: request })); } async retryClientManagerLifecycle(id: string, request: ClientManagerRetryRequest): Promise { return parseSafeClientManagerLifecycle(await this.request(`/server-instances/${encodeURIComponent(id)}/client-managers/retry`, { method: "POST", body: request })); } async revokeClientManagerSession(id: string, request: ClientManagerRevokeSessionRequest): Promise { return parseSafeClientManagerLifecycle(await this.request(`/server-instances/${encodeURIComponent(id)}/client-managers/revoke-session`, { method: "POST", body: request })); } async uninstallClientManager(id: string, request: ClientManagerUninstallRequest): Promise { return parseSafeClientManagerLifecycle(await this.request(`/server-instances/${encodeURIComponent(id)}/client-managers/uninstall`, { method: "POST", body: request })); } async getGameClientBridgeStatus(id: string): Promise { return parseSafeGameClientBridgeStatus(await this.request(`/server-instances/${encodeURIComponent(id)}/game-client-bridge`)); } async listGameClientBridgeCommands(id: string, filter: GameClientBridgeCommandFilterRequest = {}): Promise { 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(`/server-instances/${encodeURIComponent(id)}/game-client-bridge/commands${query ? `?${query}` : ""}`)); } async queueGameClientBridgeCommand(id: string, request: GameClientBridgeQueueRequest): Promise { return parseSafeGameClientBridgeCommand(await this.request(`/server-instances/${encodeURIComponent(id)}/game-client-bridge/commands`, { method: "POST", body: request })); } async getGameClientBridgeCommand(id: string, commandId: string): Promise { return parseSafeGameClientBridgeCommand(await this.request(`/server-instances/${encodeURIComponent(id)}/game-client-bridge/commands/${encodeURIComponent(commandId)}`)); } async cancelGameClientBridgeCommand(id: string, commandId: string, request: GameClientBridgeCancelRequest = {}): Promise { return parseSafeGameClientBridgeCancellation(await this.request(`/server-instances/${encodeURIComponent(id)}/game-client-bridge/commands/${encodeURIComponent(commandId)}/cancel`, { method: "POST", body: request })); } async listGameClientBridgeSnapshots(id: string, query: GameClientBridgeSnapshotQuery = {}): Promise { 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(`/server-instances/${encodeURIComponent(id)}/game-client-bridge/snapshots${search ? `?${search}` : ""}`)); } async checkDependencies(id: string, request: DependencyJobRequest): Promise { return this.request(`/server-instances/${encodeURIComponent(id)}/dependencies/check`, { method: "POST", body: request }); } async getDependencyCatalog(id: string): Promise { return parseSafeDependencyCatalog(await this.request(`/server-instances/${encodeURIComponent(id)}/dependencies`)); } async installDependencies(id: string, request: DependencyJobRequest): Promise { return this.request(`/server-instances/${encodeURIComponent(id)}/dependencies/install`, { method: "POST", body: request }); } async listServerLiveLogs(id: string): Promise { return this.request(`/server-instances/${encodeURIComponent(id)}/logs/live`); } async requestLogBackfill(id: string, request: LogBackfillRequest): Promise { return this.request(`/server-instances/${encodeURIComponent(id)}/logs/backfill`, { method: "POST", body: request }); } async createJob(request: JobCreateRequest): Promise { return this.request("/jobs", { method: "POST", body: request }); } async register(request: RegisterRequest): Promise { return this.request("/auth/register", { method: "POST", body: request }); } async login(request: LoginRequest): Promise { return this.request("/auth/login", { method: "POST", body: request }); } async logout(): Promise { await this.request("/auth/logout", { method: "POST", parseJson: false }); } async getCurrentUser(): Promise { return this.request("/users/current"); } async listUsers(): Promise { return this.request("/users"); } async createUser(request: UserCreateRequest): Promise { return this.request("/users", { method: "POST", body: request }); } async updateUser(id: string, request: UserUpdateRequest): Promise { return this.request(`/users/${encodeURIComponent(id)}`, { method: "PUT", body: request }); } async updateCurrentUserProfile(request: UserProfileUpdateRequest): Promise { return this.request("/users/current/profile", { method: "PUT", body: request }); } async updateCurrentUserTheme(request: UserThemePreferenceRequest): Promise { return this.request("/users/current/theme", { method: "PUT", body: request }); } async getServerInstance(id: string): Promise { return this.request(`/server-instances/${encodeURIComponent(id)}`); } async updateServerInstance(id: string, request: ServerInstanceUpdateRequest): Promise { return this.request(`/server-instances/${encodeURIComponent(id)}`, { method: "PUT", body: request }); } async deleteServerInstance(id: string, request: ServerDeletionRequest): Promise { return this.request(`/server-instances/${encodeURIComponent(id)}`, { method: "DELETE", body: request, parseJson: false }); } async getPlatformResourceUsage(): Promise { return this.request("/metrics/platform"); } async getProductionCapacity(): Promise { return this.request("/production/capacity"); } async checkCapacityAdmission(request: { serverInstanceId?: string; runEndpointId?: string; capability: string; targetKey?: string; idempotencyKey?: string }): Promise { return this.request("/production/capacity/admission", { method: "POST", body: request }); } async listAlerts(filter: { state?: string; sourceKind?: string; sourceId?: string; severity?: string } = {}): Promise { const params = new URLSearchParams(); Object.entries(filter).forEach(([key, value]) => { if (value) params.set(key, value); }); const query = params.toString(); return this.request(`/alerts${query ? `?${query}` : ""}`); } async acknowledgeAlert(id: string, note = ""): Promise { return this.request(`/alerts/${encodeURIComponent(id)}/acknowledge`, { method: "POST", body: { note } }); } async resolveAlert(id: string, note = ""): Promise { return this.request(`/alerts/${encodeURIComponent(id)}/resolve`, { method: "POST", body: { note } }); } async retryAlert(id: string, idempotencyKey: string): Promise { return this.request(`/alerts/${encodeURIComponent(id)}/retry`, { method: "POST", body: { idempotencyKey } }); } async listPluginLifecycles(filter: { pluginId?: string; serverInstanceId?: string; currentState?: string } = {}): Promise { const params = new URLSearchParams(); Object.entries(filter).forEach(([key, value]) => { if (value) params.set(key, value); }); const query = params.toString(); return this.request(`/plugin-lifecycles${query ? `?${query}` : ""}`); } async runPluginLifecycle(pluginId: string, request: PluginLifecycleActionRequest): Promise { return this.request(`/plugin-lifecycles/${encodeURIComponent(pluginId)}/actions`, { method: "POST", body: request }); } async listAIConfigDiffs(filter: { serverInstanceId?: string; pluginId?: string; state?: string } = {}): Promise { const params = new URLSearchParams(); Object.entries(filter).forEach(([key, value]) => { if (value) params.set(key, value); }); const query = params.toString(); return this.request(`/ai/config-diffs${query ? `?${query}` : ""}`); } async approveAIConfigDiff(id: string, idempotencyKey: string): Promise { return this.request(`/ai/config-diffs/${encodeURIComponent(id)}/approve`, { method: "POST", body: { idempotencyKey } }); } async listServerMetrics(): Promise { return this.request("/metrics/server-instances"); } async listMetricHistory(serverInstanceId: string, limit = 100): Promise { const params = new URLSearchParams({ serverInstanceId, limit: String(limit) }); return this.request(`/metrics/server-instances/history?${params.toString()}`); } async listBackups(serverInstanceId: string): Promise { return this.request(`/backups?serverInstanceId=${encodeURIComponent(serverInstanceId)}`); } async listGamePlayers(serverInstanceId: string, search = ""): Promise { const query = search ? `?search=${encodeURIComponent(search)}` : ""; return this.request(`/server-instances/${encodeURIComponent(serverInstanceId)}/game-players${query}`); } async getGamePlayerProfile(serverInstanceId: string, playerId: string): Promise { return this.request(`/server-instances/${encodeURIComponent(serverInstanceId)}/game-players/${encodeURIComponent(playerId)}`); } async getBackup(id: string): Promise { return this.request(`/backups/${encodeURIComponent(id)}`); } async listRemoteAdapters(serverInstanceId: string): Promise { return this.request(`/server-instances/${encodeURIComponent(serverInstanceId)}/remote-adapters`); } async requestRemoteAdapter(serverInstanceId: string, request: RemoteAdapterRequest): Promise { return this.request(`/server-instances/${encodeURIComponent(serverInstanceId)}/remote-adapters`, { method: "POST", body: request }); } async sendSourceRCONCommand(serverInstanceId: string, request: SourceRCONCommandRequest): Promise { return this.request(`/server-instances/${encodeURIComponent(serverInstanceId)}/rcon/commands`, { method: "POST", body: request }); } async getServerConfig(id: string): Promise { return this.request(`/server-instances/${encodeURIComponent(id)}/config`); } async previewServerConfigDiff(id: string, request: ServerConfigDiffPreviewRequest): Promise { return this.request(`/server-instances/${encodeURIComponent(id)}/config/diff`, { method: "POST", body: request }); } async approveServerConfigWrite(id: string, request: ServerConfigWriteApprovalRequest): Promise { return this.request(`/server-instances/${encodeURIComponent(id)}/config/approve`, { method: "POST", body: request }); } async dispatchFileOperation(request: FileOperationDispatchRequest): Promise { return this.request("/file-operations/dispatch", { method: "POST", body: request }); } async listLogStreams(): Promise { return this.request("/log-streams"); } async queryLogStream(request: LogStreamCursorRequest): Promise { return this.request("/log-streams/query", { method: "POST", body: request }); } async listAuditEvents(): Promise { return this.request("/audit-events"); } async suggestServerConfig(request: LlmConfigSuggestionRequest): Promise { return this.request("/ai/config-suggestions", { method: "POST", body: request }); } async invokeAI(request: AIInvocationRequest): Promise { return this.request("/ai/invocations", { method: "POST", body: request }); } async authorizePluginBridge(request: PluginBridgeAuthorizeRequest): Promise { return this.request("/plugin-bridge/authorize", { method: "POST", body: request }); } async executePluginBridge(request: PluginBridgeExecuteRequest): Promise { return this.request("/plugin-bridge/execute", { method: "POST", body: request }); } async listAiProviders(): Promise { return this.request("/ai-providers"); } async createAiProvider(request: AiProviderRequest): Promise { return this.request("/ai-providers", { method: "POST", body: request }); } async updateAiProvider(id: string, request: AiProviderUpdateRequest): Promise { return this.request(`/ai-providers/${encodeURIComponent(id)}`, { method: "PUT", body: request }); } async setAiProviderStatus(id: string, request: AiProviderStatusRequest): Promise { return this.request(`/ai-providers/${encodeURIComponent(id)}/status`, { method: "POST", body: request }); } async testAiProvider(id: string): Promise { return this.request(`/ai-providers/${encodeURIComponent(id)}/test`, { method: "POST" }); } async listAiProviderModels(id: string): Promise { return this.request(`/ai-providers/${encodeURIComponent(id)}/models`); } private async request(path: string, options: ApiRequestOptions = {}): Promise { 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; } } interface ApiRequestOptions { absolute?: boolean; method?: string; body?: unknown; init?: RequestInit; parseJson?: boolean; } async function safeReadError(response: Response): Promise { try { return (await response.json()) as ApiErrorResponse; } catch { return null; } } async function responseError(response: Response): Promise { 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);