feat(scum): add file config workbench

This commit is contained in:
npc0-hue
2026-07-28 14:43:24 +08:00
parent e739dd9c79
commit e4ef4024a8
22 changed files with 397 additions and 109 deletions
@@ -281,7 +281,7 @@
"maxCommands": 1000,
"pages": [
{
"pageKey": "operations",
"pageKey": "files-config",
"commandTypes": [
"announcement.send",
"companion.diagnostics",
@@ -347,89 +347,13 @@
"dependencyPolicy": "required",
"approvalRequired": ["disable", "rollback", "retire"]
},
"pages": [
{
"key": "overview",
"title": "SCUM 概览",
"path": "/overview",
"permissions": [
"server.read",
"server.lifecycle"
],
"bridgeActions": [
"server.instances.read",
"jobs.dispatch"
]
},
{
"key": "operations",
"title": "SCUM 运维",
"path": "/operations",
"permissions": [
"server.read",
"server.game-client.read",
"server.game-client.command",
"server.game-client.maintenance",
"server.client-manager.manage",
"server.logs.read",
"server.remote.access"
],
"bridgeActions": [
"server.instances.read",
"jobs.dispatch",
"client-manager.request",
"logs.query",
"remote.access.request"
]
},
{
"key": "config",
"title": "SCUM 配置",
"path": "/config",
"permissions": [
"server.files.read",
"server.files.write",
"ai.invoke"
],
"bridgeActions": [
"files.request",
"ai.invoke"
]
},
{
"key": "logs",
"title": "SCUM 日志",
"path": "/logs",
"permissions": [
"server.logs.read",
"server.artifacts.read",
"server.remote.access",
"ai.invoke"
],
"bridgeActions": [
"logs.query",
"artifacts.open",
"remote.access.request",
"ai.invoke"
]
},
{
"key": "remote",
"title": "SCUM 远程",
"path": "/remote",
"permissions": [
"server.remote.access",
"server.files.read",
"server.files.write",
"server.lifecycle"
],
"bridgeActions": [
"remote.access.request",
"files.request",
"jobs.dispatch"
]
}
],
"pages": [{ "key": "files-config", "title": "文件与配置", "path": "/files-config", "permissions": ["server.read", "server.files.read", "server.files.write", "server.logs.read", "server.game-client.read", "server.remote.access", "ai.invoke"], "bridgeActions": ["server.instances.read", "files.request", "logs.query", "remote.access.request", "ai.invoke"] }],
"fileWorkspace": {
"defaultDirectoryKey": "scum-config",
"directories": [{ "key": "scum-config", "label": "服务器配置", "scope": "config" }, { "key": "scum-logs", "label": "日志文件", "scope": "logs" }],
"files": [{ "key": "scum-server-settings", "directoryKey": "scum-config", "label": "ServerSettings.ini", "kind": "config", "editable": true }, { "key": "scum-server-log", "directoryKey": "scum-logs", "label": "SCUM Server.log", "kind": "log", "streamKey": "scum.server" }, { "key": "scum-chat-log", "directoryKey": "scum-logs", "label": "SCUM Chat.log", "kind": "log", "streamKey": "scum.chat" }],
"configFields": [{ "key": "server-name", "fileKey": "scum-server-settings", "configKey": "ServerName", "label": "服务器名称", "description": "显示在服务器浏览器与玩家连接界面。", "control": "text", "defaultValue": "SCUM Server", "restartImpact": "restart-required" }, { "key": "game-port", "fileKey": "scum-server-settings", "configKey": "GamePort", "label": "游戏端口", "description": "玩家连接所使用的游戏端口。", "control": "port", "minimum": 1, "maximum": 65535, "defaultValue": "7777", "restartImpact": "restart-required" }, { "key": "query-port", "fileKey": "scum-server-settings", "configKey": "QueryPort", "label": "查询和状态发现端口。", "description": "服务器查询和状态发现所使用的端口。", "control": "port", "minimum": 1, "maximum": 65535, "defaultValue": "27015", "restartImpact": "restart-required" }, { "key": "max-players", "fileKey": "scum-server-settings", "configKey": "MaxPlayers", "label": "最大玩家数", "description": "允许同时进入服务器的玩家上限。", "control": "number", "minimum": 1, "maximum": 128, "defaultValue": "64", "restartImpact": "restart-required" }]
},
"ai": {
"purposes": [
"config.suggest",
@@ -190,6 +190,7 @@
}
}
},
"fileWorkspace": { "type": "object", "required": ["defaultDirectoryKey", "directories", "files", "configFields"], "additionalProperties": false, "properties": { "defaultDirectoryKey": { "$ref": "#/$defs/logicalKey" }, "directories": { "type": "array", "items": { "$ref": "#/$defs/pluginLogicalDirectory" } }, "files": { "type": "array", "items": { "$ref": "#/$defs/pluginLogicalFile" } }, "configFields": { "type": "array", "items": { "$ref": "#/$defs/pluginConfigField" } } } },
"ai": {
"type": "object",
"required": ["mediation", "configWritePolicy"],
@@ -202,6 +203,9 @@
}
},
"$defs": {
"pluginLogicalDirectory": { "type": "object", "required": ["key", "label", "scope"], "additionalProperties": false, "properties": { "key": { "$ref": "#/$defs/logicalKey" }, "label": { "type": "string", "minLength": 1, "maxLength": 60 }, "scope": { "enum": ["config", "logs"] } } },
"pluginLogicalFile": { "type": "object", "required": ["key", "directoryKey", "label", "kind"], "additionalProperties": false, "properties": { "key": { "$ref": "#/$defs/logicalKey" }, "directoryKey": { "$ref": "#/$defs/logicalKey" }, "label": { "type": "string", "minLength": 1, "maxLength": 80 }, "kind": { "enum": ["config", "log"] }, "streamKey": { "$ref": "#/$defs/logicalKey" }, "editable": { "type": "boolean" } } },
"pluginConfigField": { "type": "object", "required": ["key", "fileKey", "configKey", "label", "description", "control", "restartImpact"], "additionalProperties": false, "properties": { "key": { "$ref": "#/$defs/logicalKey" }, "fileKey": { "$ref": "#/$defs/logicalKey" }, "configKey": { "type": "string", "pattern": "^[A-Za-z][A-Za-z0-9_.-]*$", "maxLength": 120 }, "label": { "type": "string", "minLength": 1, "maxLength": 80 }, "description": { "type": "string", "minLength": 1, "maxLength": 240 }, "control": { "enum": ["text", "number", "boolean", "port"] }, "minimum": { "type": "integer", "minimum": 0, "maximum": 65535 }, "maximum": { "type": "integer", "minimum": 0, "maximum": 65535 }, "defaultValue": { "type": "string", "maxLength": 120 }, "restartImpact": { "enum": ["none", "restart-required"] } } },
"pluginCreateField": {
"type": "object",
"required": ["key", "label", "type"],
+6 -6
View File
@@ -470,8 +470,8 @@ describe("plugin manifest validation", () => {
"maintenance.prepare"
]));
expect(manifest.gameClientBridge.snapshots.map((snapshot) => snapshot.type)).toEqual(expect.arrayContaining(["companion.health", "online.sessions", "players", "squads", "vehicles", "flags"]));
expect(manifest.gameClientBridge.pages.map((page) => page.pageKey)).toContain("operations");
expect(manifest.gameClientBridge.pages.find((page) => page.pageKey === "operations")?.commandTypes).toEqual(expect.arrayContaining([
expect(manifest.gameClientBridge.pages.map((page) => page.pageKey)).toContain("files-config");
expect(manifest.gameClientBridge.pages.find((page) => page.pageKey === "files-config")?.commandTypes).toEqual(expect.arrayContaining([
"announcement.send",
"companion.diagnostics",
"player.lookup",
@@ -480,7 +480,7 @@ describe("plugin manifest validation", () => {
"restart.prepare",
"maintenance.prepare"
]));
expect(manifest.pages.map((page) => page.key)).toContain("operations");
expect(manifest.pages.map((page) => page.key)).toContain("files-config");
expect(manifest.runtimeProfiles?.lifecycleProfiles?.find((profile) => profile.key === "scum-client")?.capabilities).not.toContain("remote.run.rcon.command");
expect(manifest.runtimeProfiles?.logSources?.map((source) => source.key)).toEqual(expect.arrayContaining(["scum-chat-events", "scum-server-events", "scum-client-events"]));
});
@@ -587,7 +587,7 @@ describe("plugin manifest validation", () => {
}
}
const operationsPage = manifest.gameClientBridge.pages.find((page) => page.pageKey === "operations");
const operationsPage = manifest.gameClientBridge.pages.find((page) => page.pageKey === "files-config");
expect(operationsPage?.snapshotTypes).toEqual(expect.arrayContaining(expectedTypes));
});
@@ -663,8 +663,8 @@ describe("plugin manifest validation", () => {
}
}
const operationsPage = manifest.gameClientBridge.pages.find((page) => page.pageKey === "operations");
const operationsPluginPage = manifest.pages.find((page) => page.key === "operations");
const operationsPage = manifest.gameClientBridge.pages.find((page) => page.pageKey === "files-config");
const operationsPluginPage = manifest.pages.find((page) => page.key === "files-config");
expect(operationsPage?.queryTemplateKeys).toEqual(expect.arrayContaining(expectedKeys));
expect(operationsPluginPage?.permissions).toEqual(expect.arrayContaining(["server.game-client.read", "server.remote.access"]));
expect(operationsPluginPage?.bridgeActions).toContain("remote.access.request");