unreal-engine-mcp-server 0.4.0 → 0.4.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.production +1 -1
- package/.github/copilot-instructions.md +45 -0
- package/.github/workflows/publish-mcp.yml +3 -2
- package/README.md +21 -5
- package/dist/index.js +124 -31
- package/dist/prompts/index.d.ts +10 -3
- package/dist/prompts/index.js +186 -7
- package/dist/resources/actors.d.ts +19 -1
- package/dist/resources/actors.js +55 -64
- package/dist/resources/assets.js +46 -62
- package/dist/resources/levels.d.ts +21 -3
- package/dist/resources/levels.js +29 -54
- package/dist/tools/actors.d.ts +3 -14
- package/dist/tools/actors.js +246 -302
- package/dist/tools/animation.d.ts +57 -102
- package/dist/tools/animation.js +429 -450
- package/dist/tools/assets.d.ts +13 -2
- package/dist/tools/assets.js +52 -44
- package/dist/tools/audio.d.ts +22 -13
- package/dist/tools/audio.js +467 -121
- package/dist/tools/blueprint.d.ts +32 -13
- package/dist/tools/blueprint.js +699 -448
- package/dist/tools/build_environment_advanced.d.ts +0 -1
- package/dist/tools/build_environment_advanced.js +190 -45
- package/dist/tools/consolidated-tool-definitions.js +78 -252
- package/dist/tools/consolidated-tool-handlers.js +506 -133
- package/dist/tools/debug.d.ts +72 -10
- package/dist/tools/debug.js +167 -31
- package/dist/tools/editor.d.ts +9 -2
- package/dist/tools/editor.js +30 -44
- package/dist/tools/foliage.d.ts +34 -15
- package/dist/tools/foliage.js +97 -107
- package/dist/tools/introspection.js +19 -21
- package/dist/tools/landscape.d.ts +1 -2
- package/dist/tools/landscape.js +311 -168
- package/dist/tools/level.d.ts +3 -28
- package/dist/tools/level.js +642 -192
- package/dist/tools/lighting.d.ts +14 -3
- package/dist/tools/lighting.js +236 -123
- package/dist/tools/materials.d.ts +25 -7
- package/dist/tools/materials.js +102 -79
- package/dist/tools/niagara.d.ts +10 -12
- package/dist/tools/niagara.js +74 -94
- package/dist/tools/performance.d.ts +12 -4
- package/dist/tools/performance.js +38 -79
- package/dist/tools/physics.d.ts +34 -10
- package/dist/tools/physics.js +364 -292
- package/dist/tools/rc.js +97 -23
- package/dist/tools/sequence.d.ts +1 -0
- package/dist/tools/sequence.js +125 -22
- package/dist/tools/ui.d.ts +31 -4
- package/dist/tools/ui.js +83 -66
- package/dist/tools/visual.d.ts +11 -0
- package/dist/tools/visual.js +245 -30
- package/dist/types/tool-types.d.ts +0 -6
- package/dist/types/tool-types.js +1 -8
- package/dist/unreal-bridge.d.ts +32 -2
- package/dist/unreal-bridge.js +621 -127
- package/dist/utils/elicitation.d.ts +57 -0
- package/dist/utils/elicitation.js +104 -0
- package/dist/utils/error-handler.d.ts +0 -33
- package/dist/utils/error-handler.js +4 -111
- package/dist/utils/http.d.ts +2 -22
- package/dist/utils/http.js +12 -75
- package/dist/utils/normalize.d.ts +4 -4
- package/dist/utils/normalize.js +15 -7
- package/dist/utils/python-output.d.ts +18 -0
- package/dist/utils/python-output.js +290 -0
- package/dist/utils/python.d.ts +2 -0
- package/dist/utils/python.js +4 -0
- package/dist/utils/response-validator.js +28 -2
- package/dist/utils/result-helpers.d.ts +27 -0
- package/dist/utils/result-helpers.js +147 -0
- package/dist/utils/safe-json.d.ts +0 -2
- package/dist/utils/safe-json.js +0 -43
- package/dist/utils/validation.d.ts +16 -0
- package/dist/utils/validation.js +70 -7
- package/mcp-config-example.json +2 -2
- package/package.json +10 -9
- package/server.json +37 -14
- package/src/index.ts +130 -33
- package/src/prompts/index.ts +211 -13
- package/src/resources/actors.ts +59 -44
- package/src/resources/assets.ts +48 -51
- package/src/resources/levels.ts +35 -45
- package/src/tools/actors.ts +269 -313
- package/src/tools/animation.ts +556 -539
- package/src/tools/assets.ts +53 -43
- package/src/tools/audio.ts +507 -113
- package/src/tools/blueprint.ts +778 -462
- package/src/tools/build_environment_advanced.ts +266 -64
- package/src/tools/consolidated-tool-definitions.ts +90 -264
- package/src/tools/consolidated-tool-handlers.ts +630 -121
- package/src/tools/debug.ts +176 -33
- package/src/tools/editor.ts +35 -37
- package/src/tools/foliage.ts +110 -104
- package/src/tools/introspection.ts +24 -22
- package/src/tools/landscape.ts +334 -181
- package/src/tools/level.ts +683 -182
- package/src/tools/lighting.ts +244 -123
- package/src/tools/materials.ts +114 -83
- package/src/tools/niagara.ts +87 -81
- package/src/tools/performance.ts +49 -88
- package/src/tools/physics.ts +393 -299
- package/src/tools/rc.ts +102 -24
- package/src/tools/sequence.ts +136 -28
- package/src/tools/ui.ts +101 -70
- package/src/tools/visual.ts +250 -29
- package/src/types/tool-types.ts +0 -9
- package/src/unreal-bridge.ts +658 -140
- package/src/utils/elicitation.ts +129 -0
- package/src/utils/error-handler.ts +4 -159
- package/src/utils/http.ts +16 -115
- package/src/utils/normalize.ts +20 -10
- package/src/utils/python-output.ts +351 -0
- package/src/utils/python.ts +3 -0
- package/src/utils/response-validator.ts +25 -2
- package/src/utils/result-helpers.ts +193 -0
- package/src/utils/safe-json.ts +0 -50
- package/src/utils/validation.ts +94 -7
- package/tests/run-unreal-tool-tests.mjs +720 -0
- package/tsconfig.json +2 -2
- package/dist/python-utils.d.ts +0 -29
- package/dist/python-utils.js +0 -54
- package/dist/types/index.d.ts +0 -323
- package/dist/types/index.js +0 -28
- package/dist/utils/cache-manager.d.ts +0 -64
- package/dist/utils/cache-manager.js +0 -176
- package/dist/utils/errors.d.ts +0 -133
- package/dist/utils/errors.js +0 -256
- package/src/python/editor_compat.py +0 -181
- package/src/python-utils.ts +0 -57
- package/src/types/index.ts +0 -414
- package/src/utils/cache-manager.ts +0 -213
- package/src/utils/errors.ts +0 -312
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import type { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
2
|
+
import { Logger } from './logger.js';
|
|
3
|
+
export type PrimitiveSchema = {
|
|
4
|
+
type: 'string';
|
|
5
|
+
title?: string;
|
|
6
|
+
description?: string;
|
|
7
|
+
minLength?: number;
|
|
8
|
+
maxLength?: number;
|
|
9
|
+
pattern?: string;
|
|
10
|
+
format?: 'email' | 'uri' | 'date' | 'date-time';
|
|
11
|
+
default?: string;
|
|
12
|
+
} | {
|
|
13
|
+
type: 'number' | 'integer';
|
|
14
|
+
title?: string;
|
|
15
|
+
description?: string;
|
|
16
|
+
minimum?: number;
|
|
17
|
+
maximum?: number;
|
|
18
|
+
default?: number;
|
|
19
|
+
} | {
|
|
20
|
+
type: 'boolean';
|
|
21
|
+
title?: string;
|
|
22
|
+
description?: string;
|
|
23
|
+
default?: boolean;
|
|
24
|
+
} | {
|
|
25
|
+
type: 'string';
|
|
26
|
+
enum: string[];
|
|
27
|
+
enumNames?: string[];
|
|
28
|
+
title?: string;
|
|
29
|
+
description?: string;
|
|
30
|
+
default?: string;
|
|
31
|
+
};
|
|
32
|
+
export interface ElicitSchema {
|
|
33
|
+
type: 'object';
|
|
34
|
+
properties: Record<string, PrimitiveSchema>;
|
|
35
|
+
required?: string[];
|
|
36
|
+
}
|
|
37
|
+
export interface ElicitOptions {
|
|
38
|
+
timeoutMs?: number;
|
|
39
|
+
fallback?: () => Promise<{
|
|
40
|
+
ok: boolean;
|
|
41
|
+
value?: any;
|
|
42
|
+
error?: string;
|
|
43
|
+
}>;
|
|
44
|
+
}
|
|
45
|
+
export declare function createElicitationHelper(server: Server, log: Logger): {
|
|
46
|
+
supports: () => boolean;
|
|
47
|
+
elicit: (message: string, requestedSchema: ElicitSchema, opts?: ElicitOptions) => Promise<{
|
|
48
|
+
ok: boolean;
|
|
49
|
+
value?: any;
|
|
50
|
+
error?: string;
|
|
51
|
+
} | {
|
|
52
|
+
ok: boolean;
|
|
53
|
+
error: any;
|
|
54
|
+
}>;
|
|
55
|
+
getDefaultTimeoutMs: () => number;
|
|
56
|
+
};
|
|
57
|
+
//# sourceMappingURL=elicitation.d.ts.map
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
export function createElicitationHelper(server, log) {
|
|
2
|
+
// We do not require explicit capability detection: we optimistically try once
|
|
3
|
+
// and disable on a Method-not-found (-32601) error for the session.
|
|
4
|
+
let supported = true; // optimistic; will be set false on first failure
|
|
5
|
+
const MIN_TIMEOUT_MS = 30_000;
|
|
6
|
+
const MAX_TIMEOUT_MS = 10 * 60 * 1000;
|
|
7
|
+
const DEFAULT_TIMEOUT_MS = 3 * 60 * 1000;
|
|
8
|
+
const timeoutEnvRaw = process.env.MCP_ELICITATION_TIMEOUT_MS ?? process.env.ELICITATION_TIMEOUT_MS ?? '';
|
|
9
|
+
const parsedEnvTimeout = Number.parseInt(timeoutEnvRaw, 10);
|
|
10
|
+
const defaultTimeoutMs = Number.isFinite(parsedEnvTimeout) && parsedEnvTimeout > 0
|
|
11
|
+
? Math.min(Math.max(parsedEnvTimeout, MIN_TIMEOUT_MS), MAX_TIMEOUT_MS)
|
|
12
|
+
: DEFAULT_TIMEOUT_MS;
|
|
13
|
+
if (timeoutEnvRaw) {
|
|
14
|
+
log.debug('Configured elicitation timeout override detected', {
|
|
15
|
+
defaultTimeoutMs,
|
|
16
|
+
fromEnv: timeoutEnvRaw
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
function isSafeSchema(schema) {
|
|
20
|
+
if (!schema || schema.type !== 'object' || typeof schema.properties !== 'object')
|
|
21
|
+
return false;
|
|
22
|
+
const propertyEntries = Object.entries(schema.properties ?? {});
|
|
23
|
+
const propertyKeys = propertyEntries.map(([key]) => key);
|
|
24
|
+
if (schema.required) {
|
|
25
|
+
if (!Array.isArray(schema.required))
|
|
26
|
+
return false;
|
|
27
|
+
const invalidRequired = schema.required.some((key) => typeof key !== 'string' || !propertyKeys.includes(key));
|
|
28
|
+
if (invalidRequired)
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
return propertyEntries.every(([, rawSchema]) => {
|
|
32
|
+
if (!rawSchema || typeof rawSchema !== 'object')
|
|
33
|
+
return false;
|
|
34
|
+
const primitive = rawSchema; // narrow for guards
|
|
35
|
+
if ('properties' in primitive || 'items' in primitive)
|
|
36
|
+
return false; // nested schemas unsupported
|
|
37
|
+
if (Array.isArray(primitive.enum)) {
|
|
38
|
+
const enumValues = primitive.enum;
|
|
39
|
+
const allStrings = enumValues.every((value) => typeof value === 'string');
|
|
40
|
+
if (!allStrings)
|
|
41
|
+
return false;
|
|
42
|
+
return !('type' in primitive) || primitive.type === 'string';
|
|
43
|
+
}
|
|
44
|
+
if (primitive.type === 'string')
|
|
45
|
+
return true;
|
|
46
|
+
if (primitive.type === 'number' || primitive.type === 'integer')
|
|
47
|
+
return true;
|
|
48
|
+
if (primitive.type === 'boolean')
|
|
49
|
+
return true;
|
|
50
|
+
return false;
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
async function elicit(message, requestedSchema, opts = {}) {
|
|
54
|
+
if (!supported || !isSafeSchema(requestedSchema)) {
|
|
55
|
+
if (opts.fallback)
|
|
56
|
+
return opts.fallback();
|
|
57
|
+
return { ok: false, error: 'elicitation-unsupported' };
|
|
58
|
+
}
|
|
59
|
+
const params = { message, requestedSchema };
|
|
60
|
+
try {
|
|
61
|
+
const elicitMethod = server?.elicitInput;
|
|
62
|
+
if (typeof elicitMethod !== 'function') {
|
|
63
|
+
supported = false;
|
|
64
|
+
throw new Error('elicitInput-not-available');
|
|
65
|
+
}
|
|
66
|
+
const requestedTimeout = opts.timeoutMs;
|
|
67
|
+
const timeoutMs = Math.max(MIN_TIMEOUT_MS, requestedTimeout ?? defaultTimeoutMs);
|
|
68
|
+
const res = await elicitMethod.call(server, params, { timeout: timeoutMs });
|
|
69
|
+
const action = res?.action;
|
|
70
|
+
const content = res?.content;
|
|
71
|
+
if (action === 'accept')
|
|
72
|
+
return { ok: true, value: content };
|
|
73
|
+
if (action === 'decline' || action === 'cancel') {
|
|
74
|
+
if (opts.fallback)
|
|
75
|
+
return opts.fallback();
|
|
76
|
+
return { ok: false, error: action };
|
|
77
|
+
}
|
|
78
|
+
if (opts.fallback)
|
|
79
|
+
return opts.fallback();
|
|
80
|
+
return { ok: false, error: 'unexpected-response' };
|
|
81
|
+
}
|
|
82
|
+
catch (e) {
|
|
83
|
+
const msg = String(e?.message || e);
|
|
84
|
+
const code = e?.code ?? e?.error?.code;
|
|
85
|
+
// If client doesn't support it, don’t try again this session
|
|
86
|
+
if (msg.includes('Method not found') ||
|
|
87
|
+
msg.includes('elicitInput-not-available') ||
|
|
88
|
+
msg.includes('request-not-available') ||
|
|
89
|
+
String(code) === '-32601') {
|
|
90
|
+
supported = false;
|
|
91
|
+
}
|
|
92
|
+
log.debug('Elicitation failed; falling back', { error: msg, code });
|
|
93
|
+
if (opts.fallback)
|
|
94
|
+
return opts.fallback();
|
|
95
|
+
return { ok: false, error: msg.includes('timeout') ? 'timeout' : 'rpc-failed' };
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return {
|
|
99
|
+
supports: () => supported,
|
|
100
|
+
elicit,
|
|
101
|
+
getDefaultTimeoutMs: () => defaultTimeoutMs
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
//# sourceMappingURL=elicitation.js.map
|
|
@@ -11,15 +11,6 @@ export declare enum ErrorType {
|
|
|
11
11
|
TIMEOUT = "TIMEOUT",
|
|
12
12
|
UNKNOWN = "UNKNOWN"
|
|
13
13
|
}
|
|
14
|
-
/**
|
|
15
|
-
* Custom error class for MCP tools
|
|
16
|
-
*/
|
|
17
|
-
export declare class ToolError extends Error {
|
|
18
|
-
type: ErrorType;
|
|
19
|
-
toolName: string;
|
|
20
|
-
originalError?: any | undefined;
|
|
21
|
-
constructor(type: ErrorType, toolName: string, message: string, originalError?: any | undefined);
|
|
22
|
-
}
|
|
23
14
|
/**
|
|
24
15
|
* Consistent error handling for all tools
|
|
25
16
|
*/
|
|
@@ -28,14 +19,6 @@ export declare class ErrorHandler {
|
|
|
28
19
|
* Create a standardized error response
|
|
29
20
|
*/
|
|
30
21
|
static createErrorResponse(error: any, toolName: string, context?: any): BaseToolResponse;
|
|
31
|
-
/**
|
|
32
|
-
* Create a standardized warning response
|
|
33
|
-
*/
|
|
34
|
-
static createWarningResponse(message: string, result: any, toolName: string): BaseToolResponse;
|
|
35
|
-
/**
|
|
36
|
-
* Create a standardized success response
|
|
37
|
-
*/
|
|
38
|
-
static createSuccessResponse(message: string, data?: any): BaseToolResponse;
|
|
39
22
|
/**
|
|
40
23
|
* Categorize error by type
|
|
41
24
|
*/
|
|
@@ -46,21 +29,5 @@ export declare class ErrorHandler {
|
|
|
46
29
|
private static getUserFriendlyMessage;
|
|
47
30
|
/** Determine if an error is likely retriable */
|
|
48
31
|
private static isRetriable;
|
|
49
|
-
/**
|
|
50
|
-
* Wrap async function with error handling
|
|
51
|
-
*/
|
|
52
|
-
static wrapAsync<T extends BaseToolResponse>(toolName: string, fn: () => Promise<T>, context?: any): Promise<T>;
|
|
53
|
-
/**
|
|
54
|
-
* Validate required parameters
|
|
55
|
-
*/
|
|
56
|
-
static validateParams(params: any, required: string[], toolName: string): void;
|
|
57
|
-
/**
|
|
58
|
-
* Handle Unreal Engine specific errors
|
|
59
|
-
*/
|
|
60
|
-
static handleUnrealError(error: any, operation: string): string;
|
|
61
|
-
/**
|
|
62
|
-
* Create operation result with consistent structure
|
|
63
|
-
*/
|
|
64
|
-
static createResult<T extends BaseToolResponse>(success: boolean, message: string, data?: Partial<T>): T;
|
|
65
32
|
}
|
|
66
33
|
//# sourceMappingURL=error-handler.d.ts.map
|
|
@@ -13,21 +13,6 @@ export var ErrorType;
|
|
|
13
13
|
ErrorType["TIMEOUT"] = "TIMEOUT";
|
|
14
14
|
ErrorType["UNKNOWN"] = "UNKNOWN";
|
|
15
15
|
})(ErrorType || (ErrorType = {}));
|
|
16
|
-
/**
|
|
17
|
-
* Custom error class for MCP tools
|
|
18
|
-
*/
|
|
19
|
-
export class ToolError extends Error {
|
|
20
|
-
type;
|
|
21
|
-
toolName;
|
|
22
|
-
originalError;
|
|
23
|
-
constructor(type, toolName, message, originalError) {
|
|
24
|
-
super(message);
|
|
25
|
-
this.type = type;
|
|
26
|
-
this.toolName = toolName;
|
|
27
|
-
this.originalError = originalError;
|
|
28
|
-
this.name = 'ToolError';
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
16
|
/**
|
|
32
17
|
* Consistent error handling for all tools
|
|
33
18
|
*/
|
|
@@ -66,36 +51,15 @@ export class ErrorHandler {
|
|
|
66
51
|
})
|
|
67
52
|
};
|
|
68
53
|
}
|
|
69
|
-
/**
|
|
70
|
-
* Create a standardized warning response
|
|
71
|
-
*/
|
|
72
|
-
static createWarningResponse(message, result, toolName) {
|
|
73
|
-
log.warn(`Tool ${toolName} warning: ${message}`);
|
|
74
|
-
return {
|
|
75
|
-
success: true,
|
|
76
|
-
warning: message,
|
|
77
|
-
message: `${toolName} completed with warnings`,
|
|
78
|
-
...result
|
|
79
|
-
};
|
|
80
|
-
}
|
|
81
|
-
/**
|
|
82
|
-
* Create a standardized success response
|
|
83
|
-
*/
|
|
84
|
-
static createSuccessResponse(message, data = {}) {
|
|
85
|
-
return {
|
|
86
|
-
success: true,
|
|
87
|
-
message,
|
|
88
|
-
...data
|
|
89
|
-
};
|
|
90
|
-
}
|
|
91
54
|
/**
|
|
92
55
|
* Categorize error by type
|
|
93
56
|
*/
|
|
94
57
|
static categorizeError(error) {
|
|
95
|
-
|
|
96
|
-
|
|
58
|
+
const explicitType = (error?.type || error?.errorType || '').toString().toUpperCase();
|
|
59
|
+
if (explicitType && Object.values(ErrorType).includes(explicitType)) {
|
|
60
|
+
return explicitType;
|
|
97
61
|
}
|
|
98
|
-
const errorMessage = error
|
|
62
|
+
const errorMessage = error?.message?.toLowerCase() || String(error).toLowerCase();
|
|
99
63
|
// Connection errors
|
|
100
64
|
if (errorMessage.includes('econnrefused') ||
|
|
101
65
|
errorMessage.includes('timeout') ||
|
|
@@ -168,76 +132,5 @@ export class ErrorHandler {
|
|
|
168
132
|
catch { }
|
|
169
133
|
return false;
|
|
170
134
|
}
|
|
171
|
-
/**
|
|
172
|
-
* Wrap async function with error handling
|
|
173
|
-
*/
|
|
174
|
-
static async wrapAsync(toolName, fn, context) {
|
|
175
|
-
try {
|
|
176
|
-
const result = await fn();
|
|
177
|
-
// Ensure result has success field
|
|
178
|
-
if (typeof result === 'object' && result !== null) {
|
|
179
|
-
if (!('success' in result)) {
|
|
180
|
-
result.success = true;
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
return result;
|
|
184
|
-
}
|
|
185
|
-
catch (error) {
|
|
186
|
-
return this.createErrorResponse(error, toolName, context);
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
/**
|
|
190
|
-
* Validate required parameters
|
|
191
|
-
*/
|
|
192
|
-
static validateParams(params, required, toolName) {
|
|
193
|
-
if (!params || typeof params !== 'object') {
|
|
194
|
-
throw new ToolError(ErrorType.PARAMETER, toolName, 'Invalid parameters: expected object');
|
|
195
|
-
}
|
|
196
|
-
for (const field of required) {
|
|
197
|
-
if (!(field in params) || params[field] === undefined || params[field] === null) {
|
|
198
|
-
throw new ToolError(ErrorType.PARAMETER, toolName, `Missing required parameter: ${field}`);
|
|
199
|
-
}
|
|
200
|
-
// Additional validation for common types
|
|
201
|
-
if (field.includes('Path') || field.includes('Name')) {
|
|
202
|
-
if (typeof params[field] !== 'string' || params[field].trim() === '') {
|
|
203
|
-
throw new ToolError(ErrorType.PARAMETER, toolName, `Invalid ${field}: must be a non-empty string`);
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
/**
|
|
209
|
-
* Handle Unreal Engine specific errors
|
|
210
|
-
*/
|
|
211
|
-
static handleUnrealError(error, operation) {
|
|
212
|
-
const errorStr = String(error.message || error).toLowerCase();
|
|
213
|
-
// Common Unreal errors
|
|
214
|
-
if (errorStr.includes('worldcontext')) {
|
|
215
|
-
return `${operation} completed (WorldContext warnings are normal)`;
|
|
216
|
-
}
|
|
217
|
-
if (errorStr.includes('does not exist')) {
|
|
218
|
-
return `Asset or object not found for ${operation}`;
|
|
219
|
-
}
|
|
220
|
-
if (errorStr.includes('access denied') || errorStr.includes('read-only')) {
|
|
221
|
-
return `Permission denied for ${operation}. Check file permissions.`;
|
|
222
|
-
}
|
|
223
|
-
if (errorStr.includes('already exists')) {
|
|
224
|
-
return `Object already exists for ${operation}`;
|
|
225
|
-
}
|
|
226
|
-
return `Unreal Engine error during ${operation}: ${error.message || error}`;
|
|
227
|
-
}
|
|
228
|
-
/**
|
|
229
|
-
* Create operation result with consistent structure
|
|
230
|
-
*/
|
|
231
|
-
static createResult(success, message, data) {
|
|
232
|
-
const result = {
|
|
233
|
-
success,
|
|
234
|
-
message,
|
|
235
|
-
...(data || {})
|
|
236
|
-
};
|
|
237
|
-
if (!success && !result.error) {
|
|
238
|
-
result.error = message;
|
|
239
|
-
}
|
|
240
|
-
return result;
|
|
241
|
-
}
|
|
242
135
|
}
|
|
243
136
|
//# sourceMappingURL=error-handler.js.map
|
package/dist/utils/http.d.ts
CHANGED
|
@@ -1,26 +1,6 @@
|
|
|
1
|
-
import { AxiosInstance
|
|
2
|
-
interface RetryConfig {
|
|
3
|
-
maxRetries: number;
|
|
4
|
-
initialDelay: number;
|
|
5
|
-
maxDelay: number;
|
|
6
|
-
backoffMultiplier: number;
|
|
7
|
-
retryableStatuses: number[];
|
|
8
|
-
retryableErrors: string[];
|
|
9
|
-
}
|
|
1
|
+
import { AxiosInstance } from 'axios';
|
|
10
2
|
/**
|
|
11
|
-
* Enhanced HTTP client factory with connection pooling and
|
|
3
|
+
* Enhanced HTTP client factory with connection pooling and request timing
|
|
12
4
|
*/
|
|
13
5
|
export declare function createHttpClient(baseURL: string): AxiosInstance;
|
|
14
|
-
/**
|
|
15
|
-
* Execute request with retry logic for resilience
|
|
16
|
-
*/
|
|
17
|
-
export declare function requestWithRetry<T = any>(client: AxiosInstance, config: AxiosRequestConfig, retryConfig?: Partial<RetryConfig>): Promise<T>;
|
|
18
|
-
/**
|
|
19
|
-
* Batch multiple requests for efficiency
|
|
20
|
-
*/
|
|
21
|
-
export declare function batchRequests<T = any>(client: AxiosInstance, requests: AxiosRequestConfig[], options?: {
|
|
22
|
-
concurrency?: number;
|
|
23
|
-
throwOnError?: boolean;
|
|
24
|
-
}): Promise<(T | Error)[]>;
|
|
25
|
-
export {};
|
|
26
6
|
//# sourceMappingURL=http.d.ts.map
|
package/dist/utils/http.js
CHANGED
|
@@ -2,40 +2,24 @@ import axios from 'axios';
|
|
|
2
2
|
import http from 'http';
|
|
3
3
|
import https from 'https';
|
|
4
4
|
import { Logger } from './logger.js';
|
|
5
|
-
//
|
|
5
|
+
// Enhanced connection pooling configuration to prevent socket failures
|
|
6
6
|
const httpAgent = new http.Agent({
|
|
7
7
|
keepAlive: true,
|
|
8
|
-
keepAliveMsecs:
|
|
9
|
-
maxSockets:
|
|
10
|
-
maxFreeSockets:
|
|
11
|
-
timeout:
|
|
8
|
+
keepAliveMsecs: 60000, // Increased keep-alive time
|
|
9
|
+
maxSockets: 20, // Increased socket pool
|
|
10
|
+
maxFreeSockets: 10, // More free sockets
|
|
11
|
+
timeout: 60000, // Longer timeout
|
|
12
12
|
});
|
|
13
13
|
const httpsAgent = new https.Agent({
|
|
14
14
|
keepAlive: true,
|
|
15
|
-
keepAliveMsecs:
|
|
16
|
-
maxSockets:
|
|
17
|
-
maxFreeSockets:
|
|
18
|
-
timeout:
|
|
15
|
+
keepAliveMsecs: 60000, // Increased keep-alive time
|
|
16
|
+
maxSockets: 20, // Increased socket pool
|
|
17
|
+
maxFreeSockets: 10, // More free sockets
|
|
18
|
+
timeout: 60000, // Longer timeout
|
|
19
19
|
});
|
|
20
20
|
const log = new Logger('HTTP');
|
|
21
|
-
const defaultRetryConfig = {
|
|
22
|
-
maxRetries: 3,
|
|
23
|
-
initialDelay: 1000,
|
|
24
|
-
maxDelay: 10000,
|
|
25
|
-
backoffMultiplier: 2,
|
|
26
|
-
retryableStatuses: [408, 429, 500, 502, 503, 504],
|
|
27
|
-
retryableErrors: ['ECONNABORTED', 'ETIMEDOUT', 'ECONNRESET', 'ENOTFOUND']
|
|
28
|
-
};
|
|
29
21
|
/**
|
|
30
|
-
*
|
|
31
|
-
*/
|
|
32
|
-
function calculateBackoff(attempt, config) {
|
|
33
|
-
const delay = Math.min(config.initialDelay * Math.pow(config.backoffMultiplier, attempt - 1), config.maxDelay);
|
|
34
|
-
// Add jitter to prevent thundering herd
|
|
35
|
-
return delay + Math.random() * 1000;
|
|
36
|
-
}
|
|
37
|
-
/**
|
|
38
|
-
* Enhanced HTTP client factory with connection pooling and retry logic
|
|
22
|
+
* Enhanced HTTP client factory with connection pooling and request timing
|
|
39
23
|
*/
|
|
40
24
|
export function createHttpClient(baseURL) {
|
|
41
25
|
const client = axios.create({
|
|
@@ -85,53 +69,6 @@ export function createHttpClient(baseURL) {
|
|
|
85
69
|
});
|
|
86
70
|
return client;
|
|
87
71
|
}
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
*/
|
|
91
|
-
export async function requestWithRetry(client, config, retryConfig = {}) {
|
|
92
|
-
const retry = { ...defaultRetryConfig, ...retryConfig };
|
|
93
|
-
let lastError = null;
|
|
94
|
-
for (let attempt = 1; attempt <= retry.maxRetries; attempt++) {
|
|
95
|
-
try {
|
|
96
|
-
const response = await client.request(config);
|
|
97
|
-
// Check if we should retry based on status
|
|
98
|
-
if (retry.retryableStatuses.includes(response.status)) {
|
|
99
|
-
throw new Error(`Retryable status: ${response.status}`);
|
|
100
|
-
}
|
|
101
|
-
return response.data;
|
|
102
|
-
}
|
|
103
|
-
catch (error) {
|
|
104
|
-
lastError = error;
|
|
105
|
-
const axiosError = error;
|
|
106
|
-
// Check if error is retryable
|
|
107
|
-
const isRetryable = retry.retryableErrors.includes(axiosError.code || '') ||
|
|
108
|
-
(axiosError.response && retry.retryableStatuses.includes(axiosError.response.status));
|
|
109
|
-
if (!isRetryable || attempt === retry.maxRetries) {
|
|
110
|
-
throw error;
|
|
111
|
-
}
|
|
112
|
-
// Calculate delay and wait
|
|
113
|
-
const delay = calculateBackoff(attempt, retry);
|
|
114
|
-
log.debug(`[HTTP] Retry attempt ${attempt}/${retry.maxRetries} after ${Math.round(delay)}ms`);
|
|
115
|
-
await new Promise(resolve => setTimeout(resolve, delay));
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
throw lastError || new Error('Request failed after retries');
|
|
119
|
-
}
|
|
120
|
-
/**
|
|
121
|
-
* Batch multiple requests for efficiency
|
|
122
|
-
*/
|
|
123
|
-
export async function batchRequests(client, requests, options = {}) {
|
|
124
|
-
const { concurrency = 5, throwOnError = false } = options;
|
|
125
|
-
const results = [];
|
|
126
|
-
// Process requests in batches
|
|
127
|
-
for (let i = 0; i < requests.length; i += concurrency) {
|
|
128
|
-
const batch = requests.slice(i, i + concurrency);
|
|
129
|
-
const batchPromises = batch.map(req => client.request(req)
|
|
130
|
-
.then(res => res.data)
|
|
131
|
-
.catch(err => throwOnError ? Promise.reject(err) : err));
|
|
132
|
-
const batchResults = await Promise.all(batchPromises);
|
|
133
|
-
results.push(...batchResults);
|
|
134
|
-
}
|
|
135
|
-
return results;
|
|
136
|
-
}
|
|
72
|
+
// No retry helpers are exported; consolidated command flows rely on
|
|
73
|
+
// Unreal's own retry/backoff semantics to avoid duplicate side effects.
|
|
137
74
|
//# sourceMappingURL=http.js.map
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
export type Vec3Array = [number, number, number];
|
|
2
|
-
export type Rot3Array = [number, number, number];
|
|
3
1
|
export interface Vec3Obj {
|
|
4
2
|
x: number;
|
|
5
3
|
y: number;
|
|
@@ -10,8 +8,10 @@ export interface Rot3Obj {
|
|
|
10
8
|
yaw: number;
|
|
11
9
|
roll: number;
|
|
12
10
|
}
|
|
11
|
+
export type Vec3Tuple = [number, number, number];
|
|
12
|
+
export type Rot3Tuple = [number, number, number];
|
|
13
13
|
export declare function toVec3Object(input: any): Vec3Obj | null;
|
|
14
|
-
export declare function toVec3Array(input: any): Vec3Array | null;
|
|
15
14
|
export declare function toRotObject(input: any): Rot3Obj | null;
|
|
16
|
-
export declare function
|
|
15
|
+
export declare function toVec3Tuple(input: any): Vec3Tuple | null;
|
|
16
|
+
export declare function toRotTuple(input: any): Rot3Tuple | null;
|
|
17
17
|
//# sourceMappingURL=normalize.d.ts.map
|
package/dist/utils/normalize.js
CHANGED
|
@@ -18,10 +18,6 @@ export function toVec3Object(input) {
|
|
|
18
18
|
catch { }
|
|
19
19
|
return null;
|
|
20
20
|
}
|
|
21
|
-
export function toVec3Array(input) {
|
|
22
|
-
const obj = toVec3Object(input);
|
|
23
|
-
return obj ? [obj.x, obj.y, obj.z] : null;
|
|
24
|
-
}
|
|
25
21
|
export function toRotObject(input) {
|
|
26
22
|
try {
|
|
27
23
|
if (Array.isArray(input) && input.length === 3) {
|
|
@@ -42,8 +38,20 @@ export function toRotObject(input) {
|
|
|
42
38
|
catch { }
|
|
43
39
|
return null;
|
|
44
40
|
}
|
|
45
|
-
export function
|
|
46
|
-
const
|
|
47
|
-
|
|
41
|
+
export function toVec3Tuple(input) {
|
|
42
|
+
const vec = toVec3Object(input);
|
|
43
|
+
if (!vec) {
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
const { x, y, z } = vec;
|
|
47
|
+
return [x, y, z];
|
|
48
|
+
}
|
|
49
|
+
export function toRotTuple(input) {
|
|
50
|
+
const rot = toRotObject(input);
|
|
51
|
+
if (!rot) {
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
const { pitch, yaw, roll } = rot;
|
|
55
|
+
return [pitch, yaw, roll];
|
|
48
56
|
}
|
|
49
57
|
//# sourceMappingURL=normalize.js.map
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export interface PythonOutput {
|
|
2
|
+
raw: unknown;
|
|
3
|
+
text: string;
|
|
4
|
+
}
|
|
5
|
+
export interface TaggedJsonResult<T> extends PythonOutput {
|
|
6
|
+
data: T | null;
|
|
7
|
+
}
|
|
8
|
+
export interface StandardResultPayload {
|
|
9
|
+
success?: boolean;
|
|
10
|
+
message?: string;
|
|
11
|
+
error?: string;
|
|
12
|
+
[key: string]: unknown;
|
|
13
|
+
}
|
|
14
|
+
export declare function toPythonOutput(response: unknown): PythonOutput;
|
|
15
|
+
export declare function parseStandardResult(response: unknown, tag?: string): TaggedJsonResult<StandardResultPayload>;
|
|
16
|
+
export declare function stripTaggedResultLines(text: string, tag?: string): string;
|
|
17
|
+
export declare function extractTaggedLine(output: string | PythonOutput, prefix: string): string | null;
|
|
18
|
+
//# sourceMappingURL=python-output.d.ts.map
|