91b878f347
- Added 6 Quaternius weapon models (Pistol, Revolver, Rifle, Shotgun, P90, SniperRifle) - Created weapon resource files with balanced stats - Wired Pistol + Rifle into player.tscn replacing Kenney blasters - Fixed WASD input (matched input map action names) - Fixed Escape key toggles mouse capture - Added Audio autoload singleton - Fixed broken sound asset paths across all scripts - Fixed all scene ext_resource text paths (nested under assets/kenney-fps/) - Fixed all .import source_file paths - Deleted stale .import files (will be regenerated by editor on open)
248 lines
9.6 KiB
TypeScript
248 lines
9.6 KiB
TypeScript
import type { ChildProcess } from 'child_process';
|
|
/**
|
|
* Thrown when the bridge socket closes (Godot exited, port closed, or peer
|
|
* dropped the connection mid-flight). Lets callers distinguish
|
|
* "session ended" from generic transport errors.
|
|
*/
|
|
export declare class BridgeDisconnectedError extends Error {
|
|
constructor(message: string);
|
|
}
|
|
export declare const BRIDGE_WAIT_SPAWNED_TIMEOUT_MS = 8000;
|
|
/**
|
|
* Normalize a path for cross-platform comparison.
|
|
* Folds Windows backslashes to forward slashes and strips trailing slashes,
|
|
* so Node's `path.normalize` output matches Godot's `globalize_path("res://")`.
|
|
*/
|
|
export declare function normalizeForCompare(p: string): string;
|
|
/**
|
|
* Extract JSON from Godot output by finding the first { or [ and matching to the end.
|
|
* This strips debug logs, version banners, and other noise.
|
|
*/
|
|
export declare function extractJson(output: string): string;
|
|
/**
|
|
* Strip Godot banner and debug lines from output, keeping only meaningful content.
|
|
*/
|
|
export declare function cleanOutput(output: string): string;
|
|
export declare function cleanStdout(stdout: string): string;
|
|
export interface GodotProcess {
|
|
process: ChildProcess;
|
|
output: string[];
|
|
errors: string[];
|
|
totalErrorsWritten: number;
|
|
exitCode: number | null;
|
|
hasExited: boolean;
|
|
sessionToken: string;
|
|
}
|
|
export type RuntimeSessionMode = 'spawned' | 'attached';
|
|
export interface RuntimeStopResult {
|
|
mode: RuntimeSessionMode;
|
|
output: string[];
|
|
errors: string[];
|
|
externalProcessPreserved?: boolean;
|
|
}
|
|
export interface GodotServerConfig {
|
|
godotPath?: string;
|
|
debugMode?: boolean;
|
|
}
|
|
export interface OperationParams {
|
|
[key: string]: unknown;
|
|
}
|
|
export interface OperationResult {
|
|
stdout: string;
|
|
stderr: string;
|
|
}
|
|
export interface ToolAnnotations {
|
|
readOnlyHint?: boolean;
|
|
destructiveHint?: boolean;
|
|
idempotentHint?: boolean;
|
|
openWorldHint?: boolean;
|
|
title?: string;
|
|
}
|
|
export interface ToolDefinition {
|
|
name: string;
|
|
description: string;
|
|
inputSchema: {
|
|
type: string;
|
|
properties: Record<string, unknown>;
|
|
required: string[];
|
|
};
|
|
outputSchema?: {
|
|
type: string;
|
|
properties?: Record<string, unknown>;
|
|
required?: string[];
|
|
};
|
|
annotations?: ToolAnnotations;
|
|
}
|
|
export interface ToolResponse {
|
|
content: Array<{
|
|
type: string;
|
|
text?: string;
|
|
[k: string]: unknown;
|
|
}>;
|
|
isError?: boolean;
|
|
structuredContent?: Record<string, unknown>;
|
|
[k: string]: unknown;
|
|
}
|
|
export type ToolHandler = (runner: GodotRunner, args: OperationParams) => Promise<ToolResponse> | ToolResponse;
|
|
export declare function normalizeParameters(params: OperationParams): OperationParams;
|
|
export declare function convertCamelToSnakeCase(params: OperationParams): OperationParams;
|
|
/**
|
|
* Check whether a display server (X11 / Wayland) is available on the current
|
|
* platform. On macOS and Windows the display subsystem is always present;
|
|
* on Linux we probe the standard environment variables.
|
|
*/
|
|
export declare function checkDisplayAvailable(): boolean;
|
|
export declare function validatePath(path: string): boolean;
|
|
/**
|
|
* Stricter check for paths that must stay inside `projectPath`. Rejects `..`
|
|
* (via `validatePath`) and absolute paths that escape the project root.
|
|
* `path.join('/project', '/etc/passwd')` resolves to `/etc/passwd`, so the
|
|
* basic `..`-substring check alone permits absolute-path traversal.
|
|
*
|
|
* Tolerates a leading `res://` (Godot's project-root URI) by stripping it
|
|
* before resolving — autoload entries and resource paths use this prefix.
|
|
*/
|
|
export declare function validateSubPath(projectPath: string, userPath: string): boolean;
|
|
/**
|
|
* Validate a Godot scene-tree path (NodePath). Scene-tree paths are a
|
|
* separate namespace from filesystem paths — they address nodes inside
|
|
* a scene, not files on disk, so the project-root containment check
|
|
* in `validateSubPath` does not apply.
|
|
*
|
|
* Rejects empty strings and `..` segments. Accepts both relative
|
|
* (`root/Player`) and absolute (`/root/Player`) Godot forms; the
|
|
* codebase convention is the relative form.
|
|
*/
|
|
export declare function validateNodePath(path: string): boolean;
|
|
/**
|
|
* True when `child` resolves to `parent` or a path beneath it. Used by
|
|
* defense-in-depth checks on bridge-returned paths (e.g. screenshot files
|
|
* that must live under `.mcp/screenshots/`).
|
|
*/
|
|
export declare function isUnderDir(parent: string, child: string): boolean;
|
|
/**
|
|
* Return `error.message` when `error` is an `Error`, otherwise `'Unknown error'`.
|
|
* Centralizes the catch-block boilerplate so handlers can build error responses
|
|
* without repeating the `instanceof Error` ternary.
|
|
*/
|
|
export declare function getErrorMessage(error: unknown): string;
|
|
/**
|
|
* Build the absolute path to a project's `project.godot` manifest. Use this
|
|
* instead of `join(dir, 'project.godot')` ad hoc.
|
|
*/
|
|
export declare function projectGodotPath(projectDir: string): string;
|
|
/**
|
|
* Extract the first [ERROR] message from GDScript stderr output.
|
|
* Falls back to a generic message if no [ERROR] line is found.
|
|
*/
|
|
export declare function extractGdError(stderr: string): string;
|
|
export declare function createErrorResponse(message: string, possibleSolutions?: string[]): {
|
|
content: Array<{
|
|
type: 'text';
|
|
text: string;
|
|
}>;
|
|
isError: boolean;
|
|
};
|
|
/**
|
|
* Wrap a structured payload as a ToolResponse that satisfies the MCP
|
|
* outputSchema contract. The same payload is emitted both as a JSON text
|
|
* content block (for lenient clients) and as `structuredContent` (required
|
|
* by strict clients per MCP spec revision 2025-06-18).
|
|
*
|
|
* `extraContent` is prepended for handlers that also return non-text blocks
|
|
* (e.g. `take_screenshot`'s inline image).
|
|
*/
|
|
export declare function createStructuredResponse<T extends Record<string, unknown>>(payload: T, extraContent?: Array<{
|
|
type: string;
|
|
[k: string]: unknown;
|
|
}>): ToolResponse;
|
|
interface ValidatedProjectArgs {
|
|
projectPath: string;
|
|
}
|
|
interface ValidatedSceneArgs {
|
|
projectPath: string;
|
|
scenePath: string;
|
|
}
|
|
type ValidationErrorResult = ReturnType<typeof createErrorResponse>;
|
|
export declare function validateProjectArgs(args: OperationParams): ValidatedProjectArgs | ValidationErrorResult;
|
|
export declare function validateSceneArgs(args: OperationParams, opts?: {
|
|
sceneRequired?: boolean;
|
|
}): ValidatedSceneArgs | ValidationErrorResult;
|
|
export declare class GodotRunner {
|
|
private godotPath;
|
|
private operationsScriptPath;
|
|
private bridge;
|
|
private validatedPaths;
|
|
private cachedVersion;
|
|
activeProcess: GodotProcess | null;
|
|
activeProjectPath: string | null;
|
|
activeSessionMode: RuntimeSessionMode | null;
|
|
activeBridgePort: number | null;
|
|
private socket;
|
|
private rxChunks;
|
|
private rxTotal;
|
|
private inFlight;
|
|
constructor(config?: GodotServerConfig);
|
|
private isValidGodotPathSync;
|
|
private spawnAsync;
|
|
private isValidGodotPath;
|
|
detectGodotPath(): Promise<void>;
|
|
getGodotPath(): string | null;
|
|
/**
|
|
* Read the port currently baked into the project's bridge script. Returns
|
|
* null if the file is missing or malformed. Thin pass-through to
|
|
* BridgeManager — used by bridge-wait-timeout race detection.
|
|
*/
|
|
readBakedBridgePort(projectPath: string): number | null;
|
|
getVersion(): Promise<string>;
|
|
executeOperation(operation: string, params: OperationParams, projectPath: string, timeoutMs?: number): Promise<OperationResult>;
|
|
launchEditor(projectPath: string): ChildProcess;
|
|
runProject(projectPath: string, scene?: string, background?: boolean, bridgePort?: number): Promise<GodotProcess>;
|
|
attachProject(projectPath: string, bridgePort?: number): Promise<void>;
|
|
stopProject(): Promise<RuntimeStopResult | null>;
|
|
hasActiveRuntimeSession(): boolean;
|
|
/**
|
|
* Send a JSON command to the McpBridge over a long-lived TCP connection.
|
|
*
|
|
* MCP serializes tool calls so we hold one in-flight command at a time. The
|
|
* socket is lazy-connected on first call and persists across commands until
|
|
* `closeConnection` (or a peer-side close). A close mid-flight rejects with
|
|
* `BridgeDisconnectedError`; a per-command timeout rejects but does NOT
|
|
* close the socket — a slow command does not invalidate the session.
|
|
*/
|
|
sendCommand(command: string, params?: Record<string, unknown>, timeoutMs?: number): Promise<string>;
|
|
/**
|
|
* Tear down the bridge socket. Idempotent. Any in-flight command is
|
|
* rejected with a session-ended error.
|
|
*/
|
|
closeConnection(): void;
|
|
private resetRxBuffer;
|
|
getErrorCount(): number;
|
|
getErrorsSince(marker: number): string[];
|
|
private static readonly SCRIPT_ERROR_PATTERNS;
|
|
private static readonly RETRYABLE_BRIDGE_COMMANDS;
|
|
extractRuntimeErrors(lines: string[]): string[];
|
|
private sendCommandWithReconnect;
|
|
sendCommandWithErrors(command: string, params?: Record<string, unknown>, timeoutMs?: number): Promise<{
|
|
response: string;
|
|
runtimeErrors: string[];
|
|
}>;
|
|
/**
|
|
* Shared poll loop for `waitForBridge` (spawned) and `waitForBridgeAttached`.
|
|
* Sends `ping` payloads until the bridge replies with a pong that
|
|
* `validatePong` accepts, the deadline passes, or `shouldAbort` reports
|
|
* the spawned process has exited.
|
|
*/
|
|
private pollBridge;
|
|
waitForBridgeAttached(timeoutMs?: number, intervalMs?: number): Promise<{
|
|
ready: boolean;
|
|
error?: string;
|
|
}>;
|
|
waitForBridge(timeoutMs?: number, intervalMs?: number): Promise<{
|
|
ready: boolean;
|
|
error?: string;
|
|
}>;
|
|
getRecentErrors(count?: number): string[];
|
|
}
|
|
export {};
|
|
//# sourceMappingURL=godot-runner.d.ts.map
|