Remove SCUM server-management client path

This commit is contained in:
npc0-hue
2026-09-02 17:56:49 +08:00
parent 6e614d3fa3
commit a82f1ff01a
52 changed files with 245 additions and 5131 deletions
@@ -72,10 +72,10 @@ const plugin: GamePluginResponse = {
productionLifecycle: { operations: ["install", "enable", "disable", "upgrade", "rollback", "retire", "dependency-check"], dependencyPolicy: "required" },
gameClientBridge: {
commands: [{ type: "diagnostic.ping", title: "Diagnostic ping", permission: "server.game-client.command", payloadSchemaRef: "schemas/bridge/diagnostic-ping.json", timeoutSeconds: 30, maxPayloadBytes: 4096 }],
snapshots: [{ type: "companion.health", schemaVersion: "1", schemaRef: "schemas/bridge/health.json", keepForSeconds: 3600, maxRecords: 24 }],
snapshots: [{ type: "bridge.health", schemaVersion: "1", schemaRef: "schemas/bridge/health.json", keepForSeconds: 3600, maxRecords: 24 }],
commandRetentionSeconds: 86400,
maxCommands: 1000,
pages: [{ pageKey: "players", commandTypes: ["diagnostic.ping"], snapshotTypes: ["companion.health"] }]
pages: [{ pageKey: "players", commandTypes: ["diagnostic.ping"], snapshotTypes: ["bridge.health"] }]
},
status: "installed"
};
@@ -193,14 +193,14 @@ describe("PluginPageHostPage", () => {
{ operation: "delete" as const, key: "user-2" }
];
const queueRequest = {
profileKey: "scum-client",
profileKey: "example-client",
commandType: "reward.deliver",
payload: { steamId: "redacted", giftKey: "starter" },
payload: { steamId: "steam-1", giftKey: "starter" },
idempotencyKey: "gift:user-1:starter",
expiresAt: "2026-08-15T12:00:00Z"
};
const commandFilter = { profileKey: "scum-client", state: "pending" as const, commandType: "reward.deliver" };
const snapshotQuery = { profileKey: "scum-client", type: "scum.positions", streamKey: "current", limit: 50 };
const commandFilter = { profileKey: "example-client", state: "pending" as const, commandType: "reward.deliver" };
const snapshotQuery = { profileKey: "example-client", type: "example.positions", streamKey: "current", limit: 50 };
const dispatchEnvelope = { requestId: "request-1", action: "server.instances.read" as const, payload: { view: "summary" } };
await actions?.pluginData?.list("scum_users", "user-1");
+3 -3
View File
@@ -29,7 +29,7 @@ export function PluginPageHostPage({ params, onNavigate, initialPlugin, embedded
const [state, setState] = useState<PluginPageState>(() => initialPlugin ? { status: "ready", plugin: initialPlugin } : { status: "loading" });
const [bundle, setBundle] = useState<ComponentType<{ context: ReturnType<typeof createPluginBridgeHostContext>; workspaceActions?: PluginPageWorkspaceActions; availability: PluginPageAvailability }> | null>(null);
const [bundleError, setBundleError] = useState("");
const [availability, setAvailability] = useState<PluginPageAvailability>({ available: false, reason: "正在验证 Companion 可用性。" });
const [availability, setAvailability] = useState<PluginPageAvailability>({ available: false, reason: "正在验证插件桥接可用性。" });
const readyPluginRef = useRef<GamePluginResponse | undefined>(undefined);
const hostContextRef = useRef<ReturnType<typeof createPluginBridgeHostContext> | undefined>(undefined);
@@ -99,7 +99,7 @@ export function PluginPageHostPage({ params, onNavigate, initialPlugin, embedded
if (!serverId) { setAvailability({ available: false, reason: "插件页面没有绑定服务器。" }); return () => { active = false; }; }
void platformApiClient.getGameClientBridgeStatus(serverId).then((status) => {
if (active) setAvailability({ available: status.available, reason: status.reason, features: status.features });
}).catch((error) => { if (active) setAvailability({ available: false, reason: error instanceof Error ? error.message : "无法验证 Companion 可用性。" }); });
}).catch((error) => { if (active) setAvailability({ available: false, reason: error instanceof Error ? error.message : "无法验证插件桥接可用性。" }); });
return () => { active = false; };
}, [bundleLoadKey, loadableBundlePage, serverId]);
@@ -148,7 +148,7 @@ export function PluginPageHostPage({ params, onNavigate, initialPlugin, embedded
</button>
</div>
<div className="action-list" aria-label="plugin page declarations"><span><strong>Bundle</strong> {page.bundleKey ? `${page.bundleKey}@${page.bundleVersion}` : "未声明"}</span><span><strong></strong> {page.bundleIntegritySha256 ? `${page.bundleIntegritySha256.slice(0, 18)}` : "未声明"}</span><span><strong>Companion</strong> {availability.available ? "可用" : "不可用"}</span></div>
<div className="action-list" aria-label="plugin page declarations"><span><strong>Bundle</strong> {page.bundleKey ? `${page.bundleKey}@${page.bundleVersion}` : "未声明"}</span><span><strong></strong> {page.bundleIntegritySha256 ? `${page.bundleIntegritySha256.slice(0, 18)}` : "未声明"}</span><span><strong></strong> {availability.available ? "可用" : "不可用"}</span></div>
</section>
{bundleError && <ErrorState title="插件页面不可用" reason={bundleError} />}
{!bundle && !bundleError && <LoadingState label="正在校验并加载插件页面 bundle…" />}
+3 -70
View File
@@ -7,7 +7,6 @@ import type { GamePluginResponse, JobResponse, RunEndpointResponse, ServerInstan
import {
RuntimeTaskProgressDialog,
type RuntimeTaskDialogAction,
runtimeBuildStages,
runtimeDependencyStages,
runtimeDownloadStages,
runtimeKeyResetStages,
@@ -29,7 +28,6 @@ import {
import { summarizeServerOperations } from "../contracts/operationsConsole";
import { filterServerCards, serverIsOnline, type ServerCardView, type ServerStatusFilter } from "../contracts/workspace";
import {
clientManagerBuildRequest,
dependencyJobRequest,
runDistributionGenerateRequest,
runUpdateRequest,
@@ -298,35 +296,10 @@ export function ServersPage({ session, operations, onNavigate }: PageComponentPr
const intent = quickRuntimeActionLabel(action);
const operationId = operations.begin({ intent, targetKind: "server", targetId: `${instance.id}:${action}`, requester: session.displayName });
setRuntimeTaskActions([]);
let generatedClientProfile: string | null = null;
try {
await requireQuickRuntimeActionAvailable(instance.id, action);
let message: string;
if (action === "generate-client-manager") {
const distribution = await runtimeTask.runTrackedTask({
title: intent,
description: quickRuntimeTaskDescription(instance, action),
stages: runtimeBuildStages,
start: async () => {
const distribution = await platformApiClient.generateClientManager(
instance.id,
clientManagerBuildRequest({
serverInstanceId: instance.id,
profileKey: defaults.clientProfileKey,
targetOs: defaults.clientOs,
targetArch: "amd64",
repositoryUrl: defaults.repositoryUrl,
sourceRevision: "main"
})
);
return { value: distribution, jobId: distribution.buildJobId };
},
poll: (jobId) => platformApiClient.getJob(jobId)
});
generatedClientProfile = defaults.clientProfileKey;
message = `客户端二进制已构建并上传,artifact ${distribution.artifactId}`;
} else {
message = await runtimeTask.runTask({
message = await runtimeTask.runTask({
title: intent,
description: quickRuntimeTaskDescription(instance, action),
stages: quickRuntimeStages(action),
@@ -361,19 +334,8 @@ export function ServersPage({ session, operations, onNavigate }: PageComponentPr
throw new Error("该运行操作已下线");
}
});
}
operations.succeed(operationId, message);
runtimeTask.succeedTask(message);
if (generatedClientProfile) {
const clientProfile = generatedClientProfile;
setRuntimeTaskActions([
{
label: "下载客户端",
kind: "primary",
onClick: () => void downloadGeneratedClient(instance, clientProfile)
}
]);
}
await refresh();
} catch (error) {
const message = error instanceof Error ? error.message : "运行操作失败";
@@ -426,26 +388,6 @@ export function ServersPage({ session, operations, onNavigate }: PageComponentPr
}
}
async function downloadGeneratedClient(instance: ServerInstanceResponse, profileKey: string) {
setRuntimeTaskActions([]);
try {
const message = await runtimeTask.runTask({
title: "下载客户端",
description: `${instance.name} 的客户端管理器已生成,正在创建下载引用。`,
stages: runtimeDownloadStages,
executeStageIndex: 1,
execute: async () => {
const reference = await platformApiClient.downloadLatestClientManager(instance.id, { profileKey });
await downloadArtifactReference(reference, (artifactId, offset, limit) => platformApiClient.readArtifactContent(artifactId, offset, limit));
return `客户端下载已开始,文件 ${safeArtifactFilename(reference.filename)}`;
}
});
runtimeTask.succeedTask(message);
} catch (error) {
runtimeTask.failTask(error instanceof Error ? error.message : "客户端下载失败");
}
}
const latestCreate = operations.operations.find((operation) => operation.intent === "创建服务器");
const runTargetPlugin = runTargetSelection ? plugins.find((plugin) => plugin.id === runTargetSelection.instance.pluginId) : undefined;
const runTargetOsOptions = runPlatformOptions(runTargetPlugin, runTargetSelection?.targetOs ?? "linux");
@@ -665,7 +607,6 @@ type ServerQuickRuntimeAction =
| "download-run"
| "push-run-update"
| "reset-run-key"
| "generate-client-manager"
| "dependencies-check"
| "dependencies-install";
@@ -902,7 +843,7 @@ function ServerCard({ card, metricsPending, metricsUnavailable, canManage, delet
const serverQuickActionGroups: Array<{ label: string; actions: ServerQuickRuntimeAction[] }> = [
{
label: "运行分发",
actions: ["generate-run", "download-run", "push-run-update", "generate-client-manager"]
actions: ["generate-run", "download-run", "push-run-update"]
},
{
label: "诊断维护",
@@ -920,8 +861,6 @@ function quickRuntimeActionLabel(action: ServerQuickRuntimeAction): string {
return "更新 run";
case "reset-run-key":
return "重置 run 密钥";
case "generate-client-manager":
return "生成客户端";
case "dependencies-check":
return "依赖检查";
case "dependencies-install":
@@ -933,9 +872,6 @@ function quickRuntimeStages(action: ServerQuickRuntimeAction) {
if (action === "generate-run") {
return runtimeRunBuildStages;
}
if (action === "generate-client-manager") {
return runtimeBuildStages;
}
if (action === "download-run") {
return runtimeDownloadStages;
}
@@ -952,7 +888,7 @@ function quickRuntimeStages(action: ServerQuickRuntimeAction) {
}
function quickRuntimeExecuteStageIndex(action: ServerQuickRuntimeAction): number {
if (action === "generate-run" || action === "generate-client-manager") {
if (action === "generate-run") {
return 3;
}
if (action === "push-run-update") {
@@ -973,9 +909,6 @@ function quickRuntimeDefaultsForPlugin(pluginId: string) {
const isScum = pluginId.toLowerCase().includes("scum");
return {
runOs: isScum ? "windows" : "linux",
clientOs: "windows",
clientProfileKey: isScum ? "scum-client-manager" : "client-manager",
repositoryUrl: isScum ? "https://github.com/F88888/scum_client.git" : "https://github.com/example/client-manager.git",
probeKey: isScum ? "steamcmd" : "java-21",
installPlanKey: isScum ? "install-steamcmd-linux" : "install-java-linux"
};