export type PluginPermission = | "server.create" | "server.read" | "server.lifecycle" | "server.files.read" | "server.files.write" | "server.logs.read" | "server.artifacts.read" | "server.artifacts.write" | "server.remote.access" | "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 = | "process.install" | "process.start" | "process.stop" | "process.restart" | "process.status" | "deployment.plan.v1" | "config.write" | "files.list" | "files.read" | "files.write" | "files.patch" | "logs.read" | "run.self-update" | "distribution.build" | "dependencies.check" | "dependencies.install" | "logs.backfill" | "remote.ftp.read" | "remote.ftp.write" | "remote.rsync.read" | "remote.rsync.write" | "remote.run.files.read" | "remote.run.files.write" | "remote.run.process.start" | "remote.run.process.stop" | "remote.run.db.mysql.query" | "remote.run.db.mysql.execute" | "remote.run.db.sqlite.query" | "remote.run.db.sqlite.execute" | "remote.run.logs.transfer" | "remote.run.rcon.command" | "remote.run.program.command" | "client-manager.deploy" | "client-manager.control" | "client-manager.update" | "client-manager.rollback" | "client-manager.uninstall" | "artifacts.read" | "artifacts.write" | "ai.invoke"; export type AIPurpose = "config.read" | "config.generate" | "config.suggest" | "logs.diagnose" | "files.suggest"; export type PluginBridgeAction = | "server.instances.read" | "jobs.dispatch" | "logs.query" | "artifacts.open" | "files.request" | "remote.access.request" | "run.distribution.request" | "dependencies.request" | "logs.backfill.request" | "client-manager.request" | "plugin-lifecycle.request" | "ai.invoke"; export type PluginBridgeRequestPayload = Record; export type PluginBridgeRequest = { id: string; pluginId: string; routeKey: string; serverInstanceId?: string; action: PluginBridgeAction; payload: TPayload; }; export type PluginBridgeResponse = | { id: string; ok: true; result: TResult; } | { id: string; ok: false; error: PluginBridgeError; }; export interface PluginBridgeError { code: "unsupported_action" | "missing_permission" | "invalid_payload" | "denied" | "permission_denied" | "unsafe_payload" | "platform_error" | "deferred"; message: string; details?: string[]; } export interface PluginBridgeExecutionRequest = Record> { requestId: string; pluginId: string; routeKey: string; serverInstanceId?: string; action: PluginBridgeAction; aiPurpose?: AIPurpose; payload?: TPayload; } export interface PluginAIInvocationRequest { requestId: string; pluginId: string; routeKey: string; serverInstanceId?: string; purpose: AIPurpose; prompt: string; currentConfig?: string; contextRefs?: Record; } export interface PluginAIInvocationResponse { requestId: string; purpose: AIPurpose; status: "ok" | "denied" | "error" | string; recommendation?: string; configRecommendation?: { diffId: string; key: string; suggestedConfig: string; diffSummary: string; expiresAt: string }; usage?: { model?: string; mocked?: boolean; inputTokens?: number; outputTokens?: number }; error?: PluginBridgeError; } export type PluginArtifactOpenPayload = Record & { artifactId: string; }; export type PluginLifecycleDispatchPayload = Record & { lifecycleAction: "start" | "stop"; capability: "process.start" | "process.stop"; expectedConfigVersion: string; idempotencyKey: string; }; export type ProductionPluginLifecycleOperation = "install" | "enable" | "disable" | "upgrade" | "rollback" | "retire" | "dependency-check"; export type PluginProductionLifecyclePayload = Record & { operation: ProductionPluginLifecycleOperation; targetVersion: string; idempotencyKey: string; }; export type PluginRemoteAccessPayload = Record & { capability: Extract; targetKey?: string; inputRef?: string; idempotencyKey: string; }; export type PluginRunDistributionPayload = Record & { operation: "generate" | "download" | "reset-key" | "update"; targetOS?: RuntimePlatform; targetArch?: RuntimeArch; artifactId?: string; idempotencyKey: string; }; export type PluginDependencyActionPayload = Record & { operation: "check" | "install"; probeKey?: string; planKey?: string; planDigest?: string; idempotencyKey: string; }; export type PluginLogBackfillPayload = Record & { sourceKey: string; cursor?: string; limit?: string; idempotencyKey: string; }; export type PluginClientManagerPayload = Record & { operation: | "generate" | "download" | "reset-key" | "status" | "deploy" | "start" | "stop" | "restart" | "update" | "rollback" | "revoke-session" | "retry" | "uninstall"; profileKey: string; targetOS?: RuntimePlatform; targetArch?: RuntimeArch; artifactId?: string; installationId?: string; expectedDeploymentGeneration?: string; idempotencyKey: string; }; export type RemoteAccessMethod = "ftp" | "rsync" | "run"; export type RemoteDatabaseEngine = "mysql" | "sqlite"; export interface GamePluginRemoteAccess { methods: RemoteAccessMethod[]; runCapabilities?: Array>; databaseEngines?: RemoteDatabaseEngine[]; rcon?: boolean; logTransfer?: boolean; } export type GameClientBridgeCommandState = "pending" | "claimed" | "succeeded" | "failed" | "unknown" | "cancelled" | "expired"; export interface GameClientBridgeCommandDeclaration { type: string; title: string; permission: PluginPermission; 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; sqlRef?: string; maxRows: number; timeoutSeconds: number; pollIntervalSeconds?: number; } export interface GameClientBridgeLogProjectionStepDeclaration { pattern: string; } export interface GameClientBridgeLogProjectionTargetDeclaration { collection: string; upsertKeys: string[]; captureMappings: Record; fixedValues?: Record; observedAtField?: string; } export interface GameClientBridgeLogProjectionPresenceDeclaration { timestampField: string; activeWindowSeconds: number; activityTarget?: GameClientBridgeLogProjectionTargetDeclaration; } export interface GameClientBridgeLogProjectionDeclaration { key: string; streamKeys: string[]; steps: GameClientBridgeLogProjectionStepDeclaration[]; correlationFields: string[]; maxInterveningLines: number; target: GameClientBridgeLogProjectionTargetDeclaration; presence?: GameClientBridgeLogProjectionPresenceDeclaration; } export interface GameClientBridgeDataPackDeclaration { key: string; databaseUserVersion: number; logParserRefs: string[]; configMapRefs: string[]; } export interface GameClientBridgePageContract { pageKey: string; commandTypes?: string[]; snapshotTypes?: string[]; queryTemplateKeys?: string[]; featureKeys?: string[]; } export interface GameClientBridgeFeatureDeclaration { key: string; title: string; permission: PluginPermission; requiredHandlers?: string[]; requiredEventProducers?: 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[]; logProjections?: GameClientBridgeLogProjectionDeclaration[]; dataPacks?: GameClientBridgeDataPackDeclaration[]; commandRetentionSeconds: number; maxCommands: number; pages?: GameClientBridgePageContract[]; features?: GameClientBridgeFeatureDeclaration[]; companion?: GameClientBridgeCompanionDeclaration; } export interface GameClientBridgeProfileStatus { pluginId: string; profileKey: string; available: boolean; reason?: string; commandTypes: string[]; snapshotTypes: string[]; queryTemplateKeys: string[]; handlerTypes?: string[]; eventProducerTypes?: string[]; } export interface GameClientBridgeFeatureAvailability { key: string; available: boolean; reason?: string; } export interface GameClientBridgeStatus { serverInstanceId: string; pluginId: string; available: boolean; reason?: string; profiles: GameClientBridgeProfileStatus[]; features?: GameClientBridgeFeatureAvailability[]; } export interface GameClientBridgeCommandResult { status: "succeeded" | "failed" | "unknown" | "cancelled"; summary?: string; payload?: Record; completedAt: string; } export interface GameClientBridgeCommand { id: string; serverInstanceId: string; pluginId: string; profileKey: string; commandType: string; priority: number; state: GameClientBridgeCommandState; result?: GameClientBridgeCommandResult; expiresAt: string; createdAt: string; updatedAt: string; completedAt?: string; } export interface GameClientBridgeSnapshot = Record> { id: string; serverInstanceId: string; pluginId: string; profileKey: string; type: string; schemaVersion: string; streamKey: string; sequence: number; observedAt: string; payload: TPayload; createdAt: string; expiresAt: string; } export interface GameClientBridgeQueueRequest = Record> { 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; listCommands(serverInstanceId: string): Promise; queueCommand>(serverInstanceId: string, request: GameClientBridgeQueueRequest): Promise; cancelCommand(serverInstanceId: string, commandId: string, reason?: string): Promise; querySnapshots = Record>(serverInstanceId: string, query?: GameClientBridgeSnapshotQuery): Promise>>; } export function createGameClientBridgeQueueRequest>(input: GameClientBridgeQueueRequest): GameClientBridgeQueueRequest { 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 }; export interface RuntimeDiscoveryProbe { key: string; kind: "file.exists" | "command.version" | "service.status" | "port.open" | "steam.app" | "docker.container"; targetKey: string; required?: boolean; expected?: string; platforms?: RuntimePlatform[]; } export interface RuntimeLifecycleProfile { key: string; mode: "local-process" | "hosted-ftp-rcon" | "ftp-only" | "custom-client"; capabilities: RunCapability[]; actionRefs?: Partial>; transportKeys?: string[]; clientManagerRef?: string; dllExtensionRefs?: string[]; platforms?: RuntimePlatform[]; } export interface RuntimeDependencyProbe { key: string; kind: "command.version" | "service.exists" | "port.available" | "steam.app" | "java.version" | "docker.available" | "package.installed" | "file.exists"; targetKey: string; required?: boolean; minimumVersion?: string; platforms?: RuntimePlatform[]; } export interface RuntimeInstallStep { type: "package" | "verified-download" | "steamcmd-app" | "manual"; targetKey: string; packageManager?: "winget" | "choco" | "scoop" | "apt" | "yum" | "dnf" | "pacman" | "zypper" | "brew" | "steamcmd" | "manual"; packageName?: string; version?: string; downloadRef?: string; checksum?: `sha256:${string}`; } export interface RuntimeInstallPlan { key: string; title: string; platforms?: RuntimePlatform[]; steps: RuntimeInstallStep[]; } export interface RuntimeLogSource { key: string; kind: "process.stdout" | "process.stderr" | "file.tail" | "ftp.poll" | "sql.query" | "client-manager"; targetKey?: string; streamKey: string; cursorKind?: "sequence" | "offset" | "fingerprint" | "ftp-listing" | "sql-cursor"; 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"; targetKey?: string; capabilities: RunCapability[]; } export interface RuntimeClientManagerProfile { key: string; displayName?: string; version?: string; repository: { url: string; revisionPolicy: "pinned" | "branch" | "tag"; branch?: string; tag?: string; revision?: string; }; supportedTargets: RuntimeTarget[]; build: { system: "go" | "npm" | "cargo" | "make"; workspaceRef?: string; entryRef?: string; }; configTemplates?: Array<{ key: string; templateRef: string; outputRef: string }>; outputArtifacts: string[]; deployment?: { mode: "run-supervised"; executableRef: string; arguments?: string[]; autoStart?: boolean; requiredRunCapabilities: Array<"client-manager.deploy" | "client-manager.control" | "client-manager.update" | "client-manager.rollback" | "client-manager.uninstall">; }; lifecycle?: { actions: Array<"start" | "stop" | "restart" | "status" | "update" | "rollback" | "uninstall">; startupTimeoutSeconds: number; stopTimeoutSeconds: number; }; health?: { mode: "component-heartbeat" | "process"; intervalSeconds: number; degradedAfterSeconds: number; offlineAfterSeconds: number; requiredCapabilities: Array<"component.register" | "component.heartbeat" | "component.health" | "component.control" | "game-client.bridge" | "logs.stream">; }; compatibility?: { minimumVersion?: string; maximumVersion?: string; allowDowngrade: boolean; }; updatePolicy?: { strategy: "manual-staged"; requireApproval: true; healthConfirmationSeconds: number; retainPrevious: true; }; } export interface RuntimeDLLExtensionProfile { key: string; displayName: string; kind: "ue4ss-dll"; activation: "server-start"; version: string; releaseState: "unpublished" | "ready"; releaseUrl?: string; checksum?: `sha256:${string}`; sizeBytes?: number; targetKey: string; modKey: string; dllRef: string; scumExecutableChecksum?: `sha256:${string}`; ue4ssAbi?: string; supportedTargets: [{ os: "windows"; arch: "amd64" }]; updateOnStart: true; rconPort: number; } export interface GamePluginRuntimeProfiles { discovery?: RuntimeDiscoveryProbe[]; lifecycleProfiles?: RuntimeLifecycleProfile[]; dependencyProbes?: RuntimeDependencyProbe[]; installPlans?: RuntimeInstallPlan[]; logSources?: RuntimeLogSource[]; logEvents?: RuntimeLogEventDeclaration[]; transportProfiles?: RuntimeTransportProfile[]; clientManagers?: RuntimeClientManagerProfile[]; dllExtensions?: RuntimeDLLExtensionProfile[]; } export interface PluginArtifactReference { artifactId: string; filename: string; contentType: string; sizeBytes: number; checksum: string; downloadUrl: string; expiresAt: string; rangeSupported: boolean; chunkSizeBytes: number; storageBehavior?: string; } export interface PluginClientManagerLifecycleStatus { installationId: string; profileKey: string; status: string; phase?: string; targetOS?: RuntimePlatform; targetArch?: RuntimeArch; version?: string; previousVersion?: string; artifactId?: string; currentJobId?: string; deploymentGeneration?: number; health?: string; healthReason?: string; lastSeenAt?: string; actions: string[]; } export type PluginBridgeExecutionResponse = Record> = { requestId: string; pluginId: string; routeKey: string; serverInstanceId?: string; action: PluginBridgeAction; status: "ok" | "queued" | "denied" | "unsupported" | "cancelled" | "error" | string; result?: TResult; error?: PluginBridgeError; }; export type PluginBridgeActionPolicy = { permissions: PluginPermission[]; aiPurposeRequired?: boolean; }; export const pluginBridgeActionPolicies: Record = { "server.instances.read": { permissions: ["server.read"] }, "jobs.dispatch": { permissions: ["server.lifecycle"] }, "logs.query": { permissions: ["server.logs.read"] }, "artifacts.open": { permissions: ["server.artifacts.read"] }, "files.request": { permissions: ["server.files.read"] }, "remote.access.request": { permissions: ["server.remote.access"] }, "run.distribution.request": { permissions: ["server.run.distribution"] }, "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 } }; export type PluginLifecycleAction = "install" | "start" | "stop" | "restart" | "status"; export interface PluginLifecycleActionDeclaration { version: 1; action: PluginLifecycleAction; mode: "oneshot" | "supervised" | "control"; executableKey?: string; arguments?: string[]; environment?: Record<`GAME_${string}` | `SERVER_${string}` | `RUN_${string}`, string>; outputMode?: "pipes" | "console"; timeoutMs?: number; stopTimeoutMs?: number; } export type GamePluginActions = Partial> & { install: string; start: string; stop: string; }; export interface GamePluginPage { key: string; title: string; path: string; bundleKey: string; bundleVersion: string; bundleIntegritySha256: `sha256:${string}`; permissions?: PluginPermission[]; bridgeActions?: PluginBridgeAction[]; } export interface GamePluginBridge { actions: PluginBridgeAction[]; } export interface PluginAssetFile { path: string; mode?: 384 | 448; } export interface GamePluginManifest { id: `game.${string}`; name: string; description?: string; version: string; kind: "game-plugin"; tags?: string[]; server: { type: string; displayName: string; supportedOS?: Array<"windows" | "linux" | "darwin">; createFormSchema: string; }; bridge?: GamePluginBridge; capabilities: RunCapability[]; permissions: PluginPermission[]; remoteAccess?: GamePluginRemoteAccess; runtimeProfiles?: GamePluginRuntimeProfiles; gameClientBridge?: GameClientBridgeManifest; actions?: GamePluginActions; assetFiles?: PluginAssetFile[]; productionLifecycle: { operations: ProductionPluginLifecycleOperation[]; dependencyPolicy: "required" | "optional"; }; pages?: GamePluginPage[]; ai?: { purposes?: AIPurpose[]; mediation: "platform"; configWritePolicy: "review-required"; }; } export interface PluginBridgeContext { pluginId: string; routeKey: string; serverInstanceId?: string; permissions: PluginPermission[]; aiPurposes?: AIPurpose[]; } export function hasPluginPermission(context: PluginBridgeContext, permission: PluginPermission): boolean { return context.permissions.includes(permission); } export function canRequestBridgeAction( context: PluginBridgeContext, action: PluginBridgeAction, options: { aiPurpose?: AIPurpose } = {} ): boolean { const policy = pluginBridgeActionPolicies[action]; if (!policy.permissions.every((permission) => hasPluginPermission(context, permission))) { return false; } if (policy.aiPurposeRequired) { return typeof options.aiPurpose === "string" && (context.aiPurposes ?? []).includes(options.aiPurpose); } return true; } export function createBridgeRequest(input: { id: string; context: PluginBridgeContext; action: PluginBridgeAction; payload: TPayload; }): PluginBridgeRequest { return { id: input.id, pluginId: input.context.pluginId, routeKey: input.context.routeKey, serverInstanceId: input.context.serverInstanceId, action: input.action, payload: input.payload }; } export function createBridgeExecutionRequest>(input: { requestId: string; context: PluginBridgeContext; action: PluginBridgeAction; aiPurpose?: AIPurpose; payload?: TPayload; }): PluginBridgeExecutionRequest { return { requestId: input.requestId, pluginId: input.context.pluginId, routeKey: input.context.routeKey, serverInstanceId: input.context.serverInstanceId, action: input.action, aiPurpose: input.aiPurpose, payload: input.payload }; } export function createArtifactOpenRequest(input: { requestId: string; context: PluginBridgeContext; artifactId: string; }): PluginBridgeExecutionRequest { return createBridgeExecutionRequest({ requestId: input.requestId, context: input.context, action: "artifacts.open", payload: { artifactId: input.artifactId } }); } export function createLifecycleDispatchRequest(input: { requestId: string; context: PluginBridgeContext; action: "start" | "stop"; expectedConfigVersion: number; idempotencyKey: string; }): PluginBridgeExecutionRequest { return createBridgeExecutionRequest({ requestId: input.requestId, context: input.context, action: "jobs.dispatch", payload: { lifecycleAction: input.action, capability: input.action === "start" ? "process.start" : "process.stop", expectedConfigVersion: String(input.expectedConfigVersion), idempotencyKey: input.idempotencyKey } }); } export function createProductionPluginLifecycleRequest(input: { requestId: string; context: PluginBridgeContext; operation: ProductionPluginLifecycleOperation; targetVersion?: string; idempotencyKey: string; }): PluginBridgeExecutionRequest { if (!input.context.serverInstanceId) { throw new Error("serverInstanceId is required for plugin lifecycle requests"); } return createBridgeExecutionRequest({ requestId: input.requestId, context: input.context, action: "plugin-lifecycle.request", payload: { operation: input.operation, targetVersion: input.targetVersion ?? "", idempotencyKey: input.idempotencyKey } }); } export function createRemoteAccessRequest(input: { requestId: string; context: PluginBridgeContext; capability: PluginRemoteAccessPayload["capability"]; targetKey?: string; inputRef?: string; inputs?: Record; idempotencyKey: string; }): PluginBridgeExecutionRequest { 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 }); } export function createRunDistributionRequest(input: { requestId: string; context: PluginBridgeContext; operation: PluginRunDistributionPayload["operation"]; targetOS?: RuntimePlatform; targetArch?: RuntimeArch; artifactId?: string; idempotencyKey: string; }): PluginBridgeExecutionRequest { const payload: PluginRunDistributionPayload = { operation: input.operation, artifactId: input.artifactId ?? "", idempotencyKey: input.idempotencyKey }; if (input.targetOS) { payload.targetOS = input.targetOS; } if (input.targetArch) { payload.targetArch = input.targetArch; } return createBridgeExecutionRequest({ requestId: input.requestId, context: input.context, action: "run.distribution.request", payload }); } export function createDependencyActionRequest(input: { requestId: string; context: PluginBridgeContext; operation: PluginDependencyActionPayload["operation"]; probeKey?: string; planKey?: string; planDigest?: string; idempotencyKey: string; }): PluginBridgeExecutionRequest { if (input.operation === "install" && !/^sha256:[a-fA-F0-9]{64}$/.test(input.planDigest ?? "")) { throw new Error("dependency install requires the reviewed plan SHA-256 digest"); } return createBridgeExecutionRequest({ requestId: input.requestId, context: input.context, action: "dependencies.request", payload: { operation: input.operation, probeKey: input.probeKey ?? "", planKey: input.planKey ?? "", planDigest: input.planDigest ?? "", idempotencyKey: input.idempotencyKey } }); } export function createLogBackfillRequest(input: { requestId: string; context: PluginBridgeContext; sourceKey: string; cursor?: string; limit?: number; idempotencyKey: string; }): PluginBridgeExecutionRequest { return createBridgeExecutionRequest({ requestId: input.requestId, context: input.context, action: "logs.backfill.request", payload: { sourceKey: input.sourceKey, cursor: input.cursor ?? "", limit: typeof input.limit === "number" ? String(input.limit) : "", idempotencyKey: input.idempotencyKey } }); } export function createClientManagerRequest(input: { requestId: string; context: PluginBridgeContext; operation: PluginClientManagerPayload["operation"]; profileKey: string; targetOS?: RuntimePlatform; targetArch?: RuntimeArch; artifactId?: string; installationId?: string; expectedDeploymentGeneration?: number; idempotencyKey: string; }): PluginBridgeExecutionRequest { const payload: PluginClientManagerPayload = { operation: input.operation, profileKey: input.profileKey, artifactId: input.artifactId ?? "", installationId: input.installationId ?? "", expectedDeploymentGeneration: typeof input.expectedDeploymentGeneration === "number" ? String(input.expectedDeploymentGeneration) : "", idempotencyKey: input.idempotencyKey }; if (input.targetOS) { payload.targetOS = input.targetOS; } if (input.targetArch) { payload.targetArch = input.targetArch; } return createBridgeExecutionRequest({ requestId: input.requestId, context: input.context, action: "client-manager.request", payload }); } export function parseArtifactReference(result: Record | undefined): PluginArtifactReference | undefined { if (!result) { return undefined; } const sizeBytes = Number(result.sizeBytes); const chunkSizeBytes = Number(result.chunkSizeBytes); const reference: PluginArtifactReference = { artifactId: result.artifactId ?? "", filename: result.filename ?? "artifact.bin", contentType: result.contentType ?? "application/octet-stream", sizeBytes, checksum: result.checksum ?? "", downloadUrl: result.downloadUrl ?? "", expiresAt: result.expiresAt ?? "", rangeSupported: result.rangeSupported === "true", chunkSizeBytes, storageBehavior: result.storageBehavior }; if (!reference.artifactId || !Number.isFinite(sizeBytes) || sizeBytes <= 0 || !Number.isFinite(chunkSizeBytes) || chunkSizeBytes <= 0) { return undefined; } if (!reference.downloadUrl.startsWith(`/api/v1/artifacts/${encodeURIComponent(reference.artifactId)}/content`)) { return undefined; } for (const value of Object.values(reference)) { if (typeof value === "string" && containsUnsafeReferenceContent(value)) { return undefined; } } return reference; } export function parseClientManagerLifecycleStatus(result: Record | undefined): PluginClientManagerLifecycleStatus | undefined { if (!result) { return undefined; } const deploymentGeneration = Number(result.deploymentGeneration ?? "0"); const values = [ result.installationId, result.profileKey, result.status, result.phase, result.targetOS, result.targetArch, result.version, result.previousVersion, result.artifactId, result.currentJobId, result.health, result.healthReason, result.lastSeenAt, result.actions ]; if (!result.installationId || !result.profileKey || !result.status || !Number.isSafeInteger(deploymentGeneration) || deploymentGeneration < 0) { return undefined; } if (values.some((value) => typeof value === "string" && containsUnsafeReferenceContent(value))) { return undefined; } return { installationId: result.installationId, profileKey: result.profileKey, status: result.status, phase: result.phase, targetOS: result.targetOS as RuntimePlatform | undefined, targetArch: result.targetArch as RuntimeArch | undefined, version: result.version, previousVersion: result.previousVersion, artifactId: result.artifactId, currentJobId: result.currentJobId, deploymentGeneration, health: result.health, healthReason: result.healthReason, lastSeenAt: result.lastSeenAt, actions: (result.actions ?? "").split(",").filter(Boolean) }; } export function parseBridgeExecutionResponse>(response: PluginBridgeExecutionResponse): PluginBridgeExecutionResponse { const safeError = response.error ? bridgeError(response.error.code, response.error.message, response.error.details ?? []) : undefined; return { requestId: response.requestId, pluginId: response.pluginId, routeKey: response.routeKey, serverInstanceId: response.serverInstanceId, action: response.action, status: response.status, result: response.result ? { ...response.result } : undefined, error: safeError }; } export function createAIInvocationRequest(input: { requestId: string; context: PluginBridgeContext; purpose: AIPurpose; prompt: string; currentConfig?: string; contextRefs?: Record; }): PluginAIInvocationRequest { return { requestId: input.requestId, pluginId: input.context.pluginId, routeKey: input.context.routeKey, serverInstanceId: input.context.serverInstanceId, purpose: input.purpose, prompt: input.prompt, currentConfig: input.currentConfig, contextRefs: input.contextRefs }; } export function parseAIInvocationResponse(response: PluginAIInvocationResponse): PluginAIInvocationResponse { return { requestId: response.requestId, purpose: response.purpose, status: response.status, recommendation: response.recommendation, 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 }; } export function bridgeError( code: PluginBridgeError["code"], message: string, details: string[] = [] ): PluginBridgeError { return { code, message, details }; } function containsUnsafeReferenceContent(value: string): boolean { const lowered = value.trim().toLowerCase(); return ( lowered.includes("/users/") || lowered.includes("/private/") || lowered.includes("unix://") || lowered.includes("tcp://") || lowered.includes("bearer ") || lowered.includes("password=") || lowered.includes("api_key=") || lowered.includes("apikey=") || lowered.includes("storage://") || lowered.includes("file://") || lowered.includes("sessiontoken") || lowered.includes("secret://") || lowered.includes("hostpath") || lowered.includes("processid") || lowered.startsWith("sk-") ); }