功能修改
This commit is contained in:
+238
-8
@@ -11,6 +11,9 @@ export type PluginPermission =
|
||||
| "server.run.distribution"
|
||||
| "server.dependencies.manage"
|
||||
| "server.client-manager.manage"
|
||||
| "server.game-client.read"
|
||||
| "server.game-client.command"
|
||||
| "server.game-client.maintenance"
|
||||
| "ai.invoke";
|
||||
|
||||
export type RunCapability =
|
||||
@@ -58,6 +61,7 @@ export type PluginBridgeAction =
|
||||
| "dependencies.request"
|
||||
| "logs.backfill.request"
|
||||
| "client-manager.request"
|
||||
| "plugin-lifecycle.request"
|
||||
| "ai.invoke";
|
||||
|
||||
export type PluginBridgeRequestPayload = Record<string, unknown>;
|
||||
@@ -115,7 +119,7 @@ export interface PluginAIInvocationResponse {
|
||||
purpose: AIPurpose;
|
||||
status: "ok" | "denied" | "error" | string;
|
||||
recommendation?: string;
|
||||
suggestedConfig?: string;
|
||||
configRecommendation?: { diffId: string; key: string; suggestedConfig: string; diffSummary: string; expiresAt: string };
|
||||
usage?: { model?: string; mocked?: boolean; inputTokens?: number; outputTokens?: number };
|
||||
error?: PluginBridgeError;
|
||||
}
|
||||
@@ -131,6 +135,15 @@ export type PluginLifecycleDispatchPayload = Record<string, string> & {
|
||||
idempotencyKey: string;
|
||||
};
|
||||
|
||||
export type ProductionPluginLifecycleOperation = "install" | "enable" | "disable" | "upgrade" | "rollback" | "retire" | "dependency-check";
|
||||
|
||||
export type PluginProductionLifecyclePayload = Record<string, string> & {
|
||||
operation: ProductionPluginLifecycleOperation;
|
||||
targetVersion: string;
|
||||
idempotencyKey: string;
|
||||
confirmed: "true" | "false";
|
||||
};
|
||||
|
||||
export type PluginRemoteAccessPayload = Record<string, string> & {
|
||||
capability: Extract<RunCapability, `remote.${string}`>;
|
||||
targetKey?: string;
|
||||
@@ -196,6 +209,165 @@ export interface GamePluginRemoteAccess {
|
||||
logTransfer?: boolean;
|
||||
}
|
||||
|
||||
export type GameClientBridgeApprovalLevel = "none" | "operator" | "platform-admin";
|
||||
export type GameClientBridgeApprovalState = "not_required" | "pending" | "approved" | "rejected";
|
||||
export type GameClientBridgeCommandState = "pending" | "claimed" | "succeeded" | "failed" | "cancelled" | "expired";
|
||||
|
||||
export interface GameClientBridgeCommandDeclaration {
|
||||
type: string;
|
||||
title: string;
|
||||
permission: PluginPermission;
|
||||
approvalLevel: GameClientBridgeApprovalLevel;
|
||||
payloadSchemaRef: string;
|
||||
resultSchemaRef?: string;
|
||||
timeoutSeconds: number;
|
||||
maxPayloadBytes: number;
|
||||
}
|
||||
|
||||
export interface GameClientBridgeSnapshotDeclaration {
|
||||
type: string;
|
||||
schemaVersion: string;
|
||||
schemaRef: string;
|
||||
keepForSeconds: number;
|
||||
maxRecords: number;
|
||||
}
|
||||
|
||||
export interface GameClientBridgeQueryTemplateDeclaration {
|
||||
key: string;
|
||||
title: string;
|
||||
permission: PluginPermission;
|
||||
engine: "sqlite";
|
||||
transportKey: string;
|
||||
targetKey: string;
|
||||
parameterSchemaRef: string;
|
||||
resultSchemaRef: string;
|
||||
maxRows: number;
|
||||
timeoutSeconds: number;
|
||||
}
|
||||
|
||||
export interface GameClientBridgePageContract {
|
||||
pageKey: string;
|
||||
commandTypes?: string[];
|
||||
snapshotTypes?: string[];
|
||||
queryTemplateKeys?: string[];
|
||||
}
|
||||
|
||||
export interface GameClientBridgeCompanionDeclaration {
|
||||
profileKey: string;
|
||||
configTemplateKey: string;
|
||||
configSchemaRef: string;
|
||||
configFormat: "yaml";
|
||||
platformBaseUrlSource: "run-control";
|
||||
registrationProof: "hmac-sha256";
|
||||
proofMaterialSource: "component-package";
|
||||
proofMaterialEnv: string;
|
||||
sessionMode: "component-session";
|
||||
tlsPolicy: "verify-system-roots";
|
||||
heartbeatIntervalSeconds: number;
|
||||
commandPollIntervalSeconds: number;
|
||||
requestTimeoutSeconds: number;
|
||||
}
|
||||
|
||||
export interface GameClientBridgeManifest {
|
||||
commands: GameClientBridgeCommandDeclaration[];
|
||||
snapshots: GameClientBridgeSnapshotDeclaration[];
|
||||
queryTemplates?: GameClientBridgeQueryTemplateDeclaration[];
|
||||
commandRetentionSeconds: number;
|
||||
maxCommands: number;
|
||||
pages?: GameClientBridgePageContract[];
|
||||
companion?: GameClientBridgeCompanionDeclaration;
|
||||
}
|
||||
|
||||
export interface GameClientBridgeProfileStatus {
|
||||
pluginId: string;
|
||||
profileKey: string;
|
||||
available: boolean;
|
||||
reason?: string;
|
||||
commandTypes: string[];
|
||||
snapshotTypes: string[];
|
||||
queryTemplateKeys: string[];
|
||||
}
|
||||
|
||||
export interface GameClientBridgeStatus {
|
||||
serverInstanceId: string;
|
||||
pluginId: string;
|
||||
available: boolean;
|
||||
reason?: string;
|
||||
profiles: GameClientBridgeProfileStatus[];
|
||||
}
|
||||
|
||||
export interface GameClientBridgeCommandResult {
|
||||
status: "succeeded" | "failed" | "cancelled";
|
||||
summary?: string;
|
||||
payload?: Record<string, unknown>;
|
||||
completedAt: string;
|
||||
}
|
||||
|
||||
export interface GameClientBridgeCommand {
|
||||
id: string;
|
||||
serverInstanceId: string;
|
||||
pluginId: string;
|
||||
profileKey: string;
|
||||
commandType: string;
|
||||
priority: number;
|
||||
state: GameClientBridgeCommandState;
|
||||
approvalState: GameClientBridgeApprovalState;
|
||||
result?: GameClientBridgeCommandResult;
|
||||
auditReferences?: string[];
|
||||
expiresAt: string;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
completedAt?: string;
|
||||
}
|
||||
|
||||
export interface GameClientBridgeSnapshot<TPayload extends Record<string, unknown> = Record<string, unknown>> {
|
||||
id: string;
|
||||
serverInstanceId: string;
|
||||
pluginId: string;
|
||||
profileKey: string;
|
||||
type: string;
|
||||
schemaVersion: string;
|
||||
streamKey: string;
|
||||
sequence: number;
|
||||
observedAt: string;
|
||||
payload: TPayload;
|
||||
auditReferences?: string[];
|
||||
createdAt: string;
|
||||
expiresAt: string;
|
||||
}
|
||||
|
||||
export interface GameClientBridgeQueueRequest<TPayload extends Record<string, unknown> = Record<string, unknown>> {
|
||||
profileKey: string;
|
||||
commandType: string;
|
||||
payload: TPayload;
|
||||
idempotencyKey: string;
|
||||
priority?: number;
|
||||
expiresAt: string;
|
||||
}
|
||||
|
||||
export interface GameClientBridgeSnapshotQuery {
|
||||
profileKey?: string;
|
||||
type?: string;
|
||||
streamKey?: string;
|
||||
observedAfter?: string;
|
||||
limit?: number;
|
||||
}
|
||||
|
||||
export interface GameClientBridgePageClient {
|
||||
getStatus(serverInstanceId: string): Promise<GameClientBridgeStatus>;
|
||||
listCommands(serverInstanceId: string): Promise<GameClientBridgeCommand[]>;
|
||||
queueCommand<TPayload extends Record<string, unknown>>(serverInstanceId: string, request: GameClientBridgeQueueRequest<TPayload>): Promise<GameClientBridgeCommand>;
|
||||
cancelCommand(serverInstanceId: string, commandId: string, reason?: string): Promise<GameClientBridgeCommand>;
|
||||
querySnapshots<TPayload extends Record<string, unknown> = Record<string, unknown>>(serverInstanceId: string, query?: GameClientBridgeSnapshotQuery): Promise<Array<GameClientBridgeSnapshot<TPayload>>>;
|
||||
}
|
||||
|
||||
export function createGameClientBridgeQueueRequest<TPayload extends Record<string, unknown>>(input: GameClientBridgeQueueRequest<TPayload>): GameClientBridgeQueueRequest<TPayload> {
|
||||
if (!input.profileKey || !input.commandType || !input.idempotencyKey || !input.expiresAt) {
|
||||
throw new Error("profileKey, commandType, idempotencyKey, and expiresAt are required");
|
||||
}
|
||||
return { profileKey: input.profileKey, commandType: input.commandType, payload: { ...input.payload }, idempotencyKey: input.idempotencyKey, priority: input.priority, expiresAt: input.expiresAt };
|
||||
}
|
||||
|
||||
export type RuntimePlatform = "windows" | "linux" | "darwin";
|
||||
export type RuntimeArch = "amd64" | "arm64";
|
||||
export type RuntimeTarget = { os: RuntimePlatform; arch: RuntimeArch };
|
||||
@@ -254,6 +426,19 @@ export interface RuntimeLogSource {
|
||||
retentionDays?: number;
|
||||
}
|
||||
|
||||
export type RuntimeLogEventSeverity = "info" | "notice" | "warning" | "critical";
|
||||
|
||||
export interface RuntimeLogEventDeclaration {
|
||||
key: string;
|
||||
title: string;
|
||||
sourceKey: string;
|
||||
eventType: string;
|
||||
permission: PluginPermission;
|
||||
schemaRef: string;
|
||||
retentionDays: number;
|
||||
severity: RuntimeLogEventSeverity;
|
||||
}
|
||||
|
||||
export interface RuntimeTransportProfile {
|
||||
key: string;
|
||||
kind: "file" | "ftp" | "rsync" | "mysql" | "sqlite" | "rcon";
|
||||
@@ -318,6 +503,7 @@ export interface GamePluginRuntimeProfiles {
|
||||
dependencyProbes?: RuntimeDependencyProbe[];
|
||||
installPlans?: RuntimeInstallPlan[];
|
||||
logSources?: RuntimeLogSource[];
|
||||
logEvents?: RuntimeLogEventDeclaration[];
|
||||
transportProfiles?: RuntimeTransportProfile[];
|
||||
clientManagers?: RuntimeClientManagerProfile[];
|
||||
}
|
||||
@@ -380,6 +566,7 @@ export const pluginBridgeActionPolicies: Record<PluginBridgeAction, PluginBridge
|
||||
"dependencies.request": { permissions: ["server.dependencies.manage"] },
|
||||
"logs.backfill.request": { permissions: ["server.logs.read"] },
|
||||
"client-manager.request": { permissions: ["server.client-manager.manage"] },
|
||||
"plugin-lifecycle.request": { permissions: ["server.lifecycle"] },
|
||||
"ai.invoke": { permissions: ["ai.invoke"], aiPurposeRequired: true }
|
||||
};
|
||||
|
||||
@@ -432,10 +619,18 @@ export interface GamePluginManifest {
|
||||
permissions: PluginPermission[];
|
||||
remoteAccess?: GamePluginRemoteAccess;
|
||||
runtimeProfiles?: GamePluginRuntimeProfiles;
|
||||
gameClientBridge?: GameClientBridgeManifest;
|
||||
actions?: GamePluginActions;
|
||||
productionLifecycle: {
|
||||
operations: ProductionPluginLifecycleOperation[];
|
||||
dependencyPolicy: "required" | "optional";
|
||||
approvalRequired: Array<"disable" | "rollback" | "retire">;
|
||||
};
|
||||
pages?: GamePluginPage[];
|
||||
ai?: {
|
||||
purposes?: AIPurpose[];
|
||||
mediation: "platform";
|
||||
configWritePolicy: "review-required";
|
||||
};
|
||||
}
|
||||
|
||||
@@ -533,24 +728,59 @@ export function createLifecycleDispatchRequest(input: {
|
||||
});
|
||||
}
|
||||
|
||||
export function createProductionPluginLifecycleRequest(input: {
|
||||
requestId: string;
|
||||
context: PluginBridgeContext;
|
||||
operation: ProductionPluginLifecycleOperation;
|
||||
targetVersion?: string;
|
||||
idempotencyKey: string;
|
||||
confirmed?: boolean;
|
||||
}): PluginBridgeExecutionRequest<PluginProductionLifecyclePayload> {
|
||||
if (!input.context.serverInstanceId) {
|
||||
throw new Error("serverInstanceId is required for plugin lifecycle requests");
|
||||
}
|
||||
if (["disable", "rollback", "retire"].includes(input.operation) && !input.confirmed) {
|
||||
throw new Error("disruptive plugin lifecycle requests require confirmation");
|
||||
}
|
||||
return createBridgeExecutionRequest({
|
||||
requestId: input.requestId,
|
||||
context: input.context,
|
||||
action: "plugin-lifecycle.request",
|
||||
payload: {
|
||||
operation: input.operation,
|
||||
targetVersion: input.targetVersion ?? "",
|
||||
idempotencyKey: input.idempotencyKey,
|
||||
confirmed: input.confirmed ? "true" : "false"
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export function createRemoteAccessRequest(input: {
|
||||
requestId: string;
|
||||
context: PluginBridgeContext;
|
||||
capability: PluginRemoteAccessPayload["capability"];
|
||||
targetKey?: string;
|
||||
inputRef?: string;
|
||||
inputs?: Record<string, string>;
|
||||
idempotencyKey: string;
|
||||
}): PluginBridgeExecutionRequest<PluginRemoteAccessPayload> {
|
||||
const payload: PluginRemoteAccessPayload = {
|
||||
capability: input.capability,
|
||||
targetKey: input.targetKey ?? "",
|
||||
inputRef: input.inputRef ?? "",
|
||||
idempotencyKey: input.idempotencyKey
|
||||
};
|
||||
for (const [key, value] of Object.entries(input.inputs ?? {})) {
|
||||
if (!/^[A-Za-z0-9][A-Za-z0-9._:-]{0,159}$/.test(key) || /(sql|query|shell|script|password|secret|token|credential|dsn|path)/i.test(key)) {
|
||||
throw new Error(`remote adapter input key is unsafe: ${key}`);
|
||||
}
|
||||
payload[`input.${key}`] = value;
|
||||
}
|
||||
return createBridgeExecutionRequest({
|
||||
requestId: input.requestId,
|
||||
context: input.context,
|
||||
action: "remote.access.request",
|
||||
payload: {
|
||||
capability: input.capability,
|
||||
targetKey: input.targetKey ?? "",
|
||||
inputRef: input.inputRef ?? "",
|
||||
idempotencyKey: input.idempotencyKey
|
||||
}
|
||||
payload
|
||||
});
|
||||
}
|
||||
|
||||
@@ -781,7 +1011,7 @@ export function parseAIInvocationResponse(response: PluginAIInvocationResponse):
|
||||
purpose: response.purpose,
|
||||
status: response.status,
|
||||
recommendation: response.recommendation,
|
||||
suggestedConfig: response.suggestedConfig,
|
||||
configRecommendation: response.configRecommendation ? { ...response.configRecommendation } : undefined,
|
||||
usage: response.usage ? { ...response.usage } : undefined,
|
||||
error: response.error ? bridgeError(response.error.code, response.error.message, response.error.details ?? []) : undefined
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user