Remove legacy client-manager platform path

This commit is contained in:
npc0-hue
2026-09-03 16:40:05 +08:00
parent ec2462a312
commit 80cddbf19d
51 changed files with 382 additions and 4271 deletions
+1 -1
View File
@@ -241,7 +241,7 @@ export interface RuntimeDiscoveryProbeResponse {
export interface RuntimeLifecycleProfileResponse {
key: string;
mode: "local-process" | "hosted-ftp-rcon" | "ftp-only" | "custom-client";
mode: "local-process" | "hosted-ftp-rcon" | "ftp-only";
capabilities: string[];
actionRefs?: Record<string, string>;
transportKeys?: string[];
+5 -1
View File
@@ -1,5 +1,5 @@
import type { PluginBridgeExecuteEnvelope, PluginBridgeExecutionResult } from "./pluginBridge";
import type { GameClientBridgeCommandFilterRequest, GameClientBridgeQueueRequest, GameClientBridgeSnapshotQuery } from "../api/types";
import type { GameClientBridgeCommandFilterRequest, GameClientBridgeQueueRequest, GameClientBridgeSnapshotQuery, LogStreamCursorRequest, LogStreamCursorResponse, LogStreamListResponse } from "../api/types";
export interface PluginDataMutation {
operation: "put" | "delete";
@@ -20,5 +20,9 @@ export interface PluginPageWorkspaceActions {
list: (filter?: GameClientBridgeCommandFilterRequest) => Promise<unknown>;
snapshots: (query?: GameClientBridgeSnapshotQuery) => Promise<unknown>;
};
logs?: {
listStreams: () => Promise<LogStreamListResponse>;
query: (request: LogStreamCursorRequest) => Promise<LogStreamCursorResponse>;
};
dispatch?: (envelope: PluginBridgeExecuteEnvelope, signal?: AbortSignal) => Promise<PluginBridgeExecutionResult>;
}
+11 -2
View File
@@ -21,9 +21,11 @@ const apiMocks = vi.hoisted(() => ({
listGameClientBridgeCommands: vi.fn(),
listGameClientBridgeSnapshots: vi.fn(),
listGamePlugins: vi.fn(),
listLogStreams: vi.fn(),
listPluginData: vi.fn(),
putPluginData: vi.fn(),
queueGameClientBridgeCommand: vi.fn(),
queryLogStream: vi.fn(),
transactPluginData: vi.fn()
}));
@@ -53,7 +55,7 @@ const plugin: GamePluginResponse = {
manifestRef: "artifact://manifests/game.scum/1.0.0",
createFormSchemaRef: "schemas/create-form.schema.json",
requiredRunCapabilities: [],
declaredPermissions: ["server.read", "server.game-client.read", "server.game-client.command"],
declaredPermissions: ["server.read", "server.logs.read", "server.game-client.read", "server.game-client.command"],
permissions: { ai: true, logs: false, files: false, jobs: true, artifacts: false, remoteAccess: false },
lifecycleActions: {},
bridgeActions: ["server.instances.read"],
@@ -64,7 +66,7 @@ const plugin: GamePluginResponse = {
bundleKey: "scum-server-plugin",
bundleVersion: "1.0.3",
bundleIntegritySha256: "sha256:3488b316d909e597024f8f31c7bc96ab8019f643d74a528dc442d3df0dc3d54e",
permissions: ["server.read", "server.game-client.read", "server.game-client.command"],
permissions: ["server.read", "server.logs.read", "server.game-client.read", "server.game-client.command"],
bridgeActions: ["server.instances.read"]
}],
tags: ["scum"],
@@ -97,6 +99,8 @@ beforeEach(() => {
apiMocks.getGameClientBridgeCommand.mockResolvedValue({ id: "command-1" });
apiMocks.listGameClientBridgeCommands.mockResolvedValue({ items: [], count: 0 });
apiMocks.listGameClientBridgeSnapshots.mockResolvedValue({ items: [], count: 0 });
apiMocks.listLogStreams.mockResolvedValue({ items: [{ id: "stream-login", serverInstanceId: "server-1", source: "file", streamKey: "scum.login", latestSeq: 2, storageBackend: "local-segments", retentionPolicy: "default", createdAt: "2026-08-15T12:00:00Z", updatedAt: "2026-08-15T12:00:00Z" }], count: 1 });
apiMocks.queryLogStream.mockResolvedValue({ logStreamId: "stream-login", entries: [], nextSeq: 2, latestSeq: 2 });
apiMocks.executePluginBridge.mockResolvedValue({ requestId: "request-1", action: "server.instances.read", status: "ok", result: { state: "ready" } });
bundleMocks.loadPluginPageBundle.mockResolvedValue(({ workspaceActions }: { workspaceActions?: PluginPageWorkspaceActions }) => {
capturedWorkspaceActions = workspaceActions;
@@ -186,6 +190,7 @@ describe("PluginPageHostPage", () => {
const actions = capturedWorkspaceActions;
expect(actions?.pluginData).toBeDefined();
expect(actions?.gameClient).toBeDefined();
expect(actions?.logs).toBeDefined();
expect(actions?.dispatch).toBeDefined();
const mutations = [
@@ -211,6 +216,8 @@ describe("PluginPageHostPage", () => {
await actions?.gameClient?.get("command-1");
await actions?.gameClient?.list(commandFilter);
await actions?.gameClient?.snapshots(snapshotQuery);
await actions?.logs?.listStreams();
await actions?.logs?.query({ logStreamId: "stream-login", afterSeq: 1, limit: 50 });
await expect(actions?.dispatch?.(dispatchEnvelope)).resolves.toMatchObject({ status: "ok", result: { state: "ready" } });
expect(apiMocks.listPluginData).toHaveBeenCalledWith("server-1", "scum_users", "user-1");
@@ -221,6 +228,8 @@ describe("PluginPageHostPage", () => {
expect(apiMocks.getGameClientBridgeCommand).toHaveBeenCalledWith("server-1", "command-1");
expect(apiMocks.listGameClientBridgeCommands).toHaveBeenCalledWith("server-1", commandFilter);
expect(apiMocks.listGameClientBridgeSnapshots).toHaveBeenCalledWith("server-1", snapshotQuery);
expect(apiMocks.listLogStreams).toHaveBeenCalledWith("server-1");
expect(apiMocks.queryLogStream).toHaveBeenCalledWith({ logStreamId: "stream-login", afterSeq: 1, limit: 50 });
expect(apiMocks.executePluginBridge).toHaveBeenCalledWith({
requestId: "request-1",
pluginId: "game.scum",
+12
View File
@@ -81,6 +81,18 @@ export function PluginPageHostPage({ params, onNavigate, initialPlugin, embedded
list: (filter) => platformApiClient.listGameClientBridgeCommands(serverId, filter),
snapshots: (query) => platformApiClient.listGameClientBridgeSnapshots(serverId, query)
},
logs: {
listStreams: () => {
const context = hostContextRef.current;
if (!serverId || !context?.permissions.includes("server.logs.read")) throw new Error("plugin page is missing server.logs.read");
return platformApiClient.listLogStreams(serverId);
},
query: (request) => {
const context = hostContextRef.current;
if (!serverId || !context?.permissions.includes("server.logs.read")) throw new Error("plugin page is missing server.logs.read");
return platformApiClient.queryLogStream(request);
}
},
dispatch: (envelope, signal) => {
const context = hostContextRef.current;
if (!context) return Promise.resolve({ requestId: envelope.requestId, action: envelope.action, status: "error", error: { code: "host_unavailable", message: "plugin bridge host is unavailable" } });