unreal-engine-mcp-server 0.4.0 → 0.4.3

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.
Files changed (135) hide show
  1. package/.env.production +1 -1
  2. package/.github/copilot-instructions.md +45 -0
  3. package/.github/workflows/publish-mcp.yml +1 -1
  4. package/README.md +21 -5
  5. package/dist/index.js +124 -31
  6. package/dist/prompts/index.d.ts +10 -3
  7. package/dist/prompts/index.js +186 -7
  8. package/dist/resources/actors.d.ts +19 -1
  9. package/dist/resources/actors.js +55 -64
  10. package/dist/resources/assets.js +46 -62
  11. package/dist/resources/levels.d.ts +21 -3
  12. package/dist/resources/levels.js +29 -54
  13. package/dist/tools/actors.d.ts +3 -14
  14. package/dist/tools/actors.js +246 -302
  15. package/dist/tools/animation.d.ts +57 -102
  16. package/dist/tools/animation.js +429 -450
  17. package/dist/tools/assets.d.ts +13 -2
  18. package/dist/tools/assets.js +52 -44
  19. package/dist/tools/audio.d.ts +22 -13
  20. package/dist/tools/audio.js +467 -121
  21. package/dist/tools/blueprint.d.ts +32 -13
  22. package/dist/tools/blueprint.js +699 -448
  23. package/dist/tools/build_environment_advanced.d.ts +0 -1
  24. package/dist/tools/build_environment_advanced.js +190 -45
  25. package/dist/tools/consolidated-tool-definitions.js +78 -252
  26. package/dist/tools/consolidated-tool-handlers.js +506 -133
  27. package/dist/tools/debug.d.ts +72 -10
  28. package/dist/tools/debug.js +167 -31
  29. package/dist/tools/editor.d.ts +9 -2
  30. package/dist/tools/editor.js +30 -44
  31. package/dist/tools/foliage.d.ts +34 -15
  32. package/dist/tools/foliage.js +97 -107
  33. package/dist/tools/introspection.js +19 -21
  34. package/dist/tools/landscape.d.ts +1 -2
  35. package/dist/tools/landscape.js +311 -168
  36. package/dist/tools/level.d.ts +3 -28
  37. package/dist/tools/level.js +642 -192
  38. package/dist/tools/lighting.d.ts +14 -3
  39. package/dist/tools/lighting.js +236 -123
  40. package/dist/tools/materials.d.ts +25 -7
  41. package/dist/tools/materials.js +102 -79
  42. package/dist/tools/niagara.d.ts +10 -12
  43. package/dist/tools/niagara.js +74 -94
  44. package/dist/tools/performance.d.ts +12 -4
  45. package/dist/tools/performance.js +38 -79
  46. package/dist/tools/physics.d.ts +34 -10
  47. package/dist/tools/physics.js +364 -292
  48. package/dist/tools/rc.js +97 -23
  49. package/dist/tools/sequence.d.ts +1 -0
  50. package/dist/tools/sequence.js +125 -22
  51. package/dist/tools/ui.d.ts +31 -4
  52. package/dist/tools/ui.js +83 -66
  53. package/dist/tools/visual.d.ts +11 -0
  54. package/dist/tools/visual.js +245 -30
  55. package/dist/types/tool-types.d.ts +0 -6
  56. package/dist/types/tool-types.js +1 -8
  57. package/dist/unreal-bridge.d.ts +32 -2
  58. package/dist/unreal-bridge.js +621 -127
  59. package/dist/utils/elicitation.d.ts +57 -0
  60. package/dist/utils/elicitation.js +104 -0
  61. package/dist/utils/error-handler.d.ts +0 -33
  62. package/dist/utils/error-handler.js +4 -111
  63. package/dist/utils/http.d.ts +2 -22
  64. package/dist/utils/http.js +12 -75
  65. package/dist/utils/normalize.d.ts +4 -4
  66. package/dist/utils/normalize.js +15 -7
  67. package/dist/utils/python-output.d.ts +18 -0
  68. package/dist/utils/python-output.js +290 -0
  69. package/dist/utils/python.d.ts +2 -0
  70. package/dist/utils/python.js +4 -0
  71. package/dist/utils/response-validator.js +28 -2
  72. package/dist/utils/result-helpers.d.ts +27 -0
  73. package/dist/utils/result-helpers.js +147 -0
  74. package/dist/utils/safe-json.d.ts +0 -2
  75. package/dist/utils/safe-json.js +0 -43
  76. package/dist/utils/validation.d.ts +16 -0
  77. package/dist/utils/validation.js +70 -7
  78. package/mcp-config-example.json +2 -2
  79. package/package.json +10 -9
  80. package/server.json +37 -14
  81. package/src/index.ts +130 -33
  82. package/src/prompts/index.ts +211 -13
  83. package/src/resources/actors.ts +59 -44
  84. package/src/resources/assets.ts +48 -51
  85. package/src/resources/levels.ts +35 -45
  86. package/src/tools/actors.ts +269 -313
  87. package/src/tools/animation.ts +556 -539
  88. package/src/tools/assets.ts +53 -43
  89. package/src/tools/audio.ts +507 -113
  90. package/src/tools/blueprint.ts +778 -462
  91. package/src/tools/build_environment_advanced.ts +266 -64
  92. package/src/tools/consolidated-tool-definitions.ts +90 -264
  93. package/src/tools/consolidated-tool-handlers.ts +630 -121
  94. package/src/tools/debug.ts +176 -33
  95. package/src/tools/editor.ts +35 -37
  96. package/src/tools/foliage.ts +110 -104
  97. package/src/tools/introspection.ts +24 -22
  98. package/src/tools/landscape.ts +334 -181
  99. package/src/tools/level.ts +683 -182
  100. package/src/tools/lighting.ts +244 -123
  101. package/src/tools/materials.ts +114 -83
  102. package/src/tools/niagara.ts +87 -81
  103. package/src/tools/performance.ts +49 -88
  104. package/src/tools/physics.ts +393 -299
  105. package/src/tools/rc.ts +102 -24
  106. package/src/tools/sequence.ts +136 -28
  107. package/src/tools/ui.ts +101 -70
  108. package/src/tools/visual.ts +250 -29
  109. package/src/types/tool-types.ts +0 -9
  110. package/src/unreal-bridge.ts +658 -140
  111. package/src/utils/elicitation.ts +129 -0
  112. package/src/utils/error-handler.ts +4 -159
  113. package/src/utils/http.ts +16 -115
  114. package/src/utils/normalize.ts +20 -10
  115. package/src/utils/python-output.ts +351 -0
  116. package/src/utils/python.ts +3 -0
  117. package/src/utils/response-validator.ts +25 -2
  118. package/src/utils/result-helpers.ts +193 -0
  119. package/src/utils/safe-json.ts +0 -50
  120. package/src/utils/validation.ts +94 -7
  121. package/tests/run-unreal-tool-tests.mjs +720 -0
  122. package/tsconfig.json +2 -2
  123. package/dist/python-utils.d.ts +0 -29
  124. package/dist/python-utils.js +0 -54
  125. package/dist/types/index.d.ts +0 -323
  126. package/dist/types/index.js +0 -28
  127. package/dist/utils/cache-manager.d.ts +0 -64
  128. package/dist/utils/cache-manager.js +0 -176
  129. package/dist/utils/errors.d.ts +0 -133
  130. package/dist/utils/errors.js +0 -256
  131. package/src/python/editor_compat.py +0 -181
  132. package/src/python-utils.ts +0 -57
  133. package/src/types/index.ts +0 -414
  134. package/src/utils/cache-manager.ts +0 -213
  135. package/src/utils/errors.ts +0 -312
package/dist/tools/rc.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import { Logger } from '../utils/logger.js';
2
+ import { interpretStandardResult } from '../utils/result-helpers.js';
2
3
  export class RcTools {
3
4
  bridge;
4
5
  log = new Logger('RcTools');
@@ -31,33 +32,38 @@ export class RcTools {
31
32
  * Parse Python execution result with better error handling
32
33
  */
33
34
  parsePythonResult(resp, operationName) {
34
- let out = '';
35
- if (resp?.LogOutput && Array.isArray(resp.LogOutput)) {
36
- out = resp.LogOutput.map((l) => l.Output || '').join('');
37
- }
38
- else if (typeof resp === 'string') {
39
- out = resp;
40
- }
41
- else {
42
- out = JSON.stringify(resp);
43
- }
44
- const m = out.match(/RESULT:({.*})/);
45
- if (m) {
46
- try {
47
- return JSON.parse(m[1]);
48
- }
49
- catch (e) {
50
- this.log.error(`Failed to parse ${operationName} result: ${e}`);
51
- }
35
+ const interpreted = interpretStandardResult(resp, {
36
+ successMessage: `${operationName} succeeded`,
37
+ failureMessage: `${operationName} failed`
38
+ });
39
+ if (interpreted.success) {
40
+ return {
41
+ ...interpreted.payload,
42
+ success: true
43
+ };
52
44
  }
53
- // Check for common error patterns
54
- if (out.includes('ModuleNotFoundError')) {
45
+ const baseError = interpreted.error ?? `${operationName} did not return a valid result`;
46
+ const rawOutput = interpreted.rawText ?? '';
47
+ const cleanedOutput = interpreted.cleanText && interpreted.cleanText.trim().length > 0
48
+ ? interpreted.cleanText.trim()
49
+ : baseError;
50
+ if (rawOutput.includes('ModuleNotFoundError')) {
55
51
  return { success: false, error: 'Remote Control module not available. Ensure Remote Control plugin is enabled.' };
56
52
  }
57
- if (out.includes('AttributeError')) {
53
+ if (rawOutput.includes('AttributeError')) {
58
54
  return { success: false, error: 'Remote Control API method not found. Check Unreal Engine version compatibility.' };
59
55
  }
60
- return { success: false, error: `${operationName} did not return a valid result: ${out.substring(0, 200)}` };
56
+ const error = baseError;
57
+ this.log.error(`${operationName} returned no parsable result: ${cleanedOutput}`);
58
+ return {
59
+ success: false,
60
+ error: (() => {
61
+ const detail = cleanedOutput === baseError
62
+ ? ''
63
+ : (cleanedOutput ?? '').substring(0, 200).trim();
64
+ return detail ? `${error}: ${detail}` : error;
65
+ })()
66
+ };
61
67
  }
62
68
  // Create a Remote Control Preset asset
63
69
  async createPreset(params) {
@@ -65,6 +71,9 @@ export class RcTools {
65
71
  const path = (params.path || '/Game/RCPresets').replace(/\/$/, '');
66
72
  if (!name)
67
73
  return { success: false, error: 'Preset name is required' };
74
+ if (!path.startsWith('/Game/')) {
75
+ return { success: false, error: `Preset path must be under /Game. Received: ${path}` };
76
+ }
68
77
  const python = `
69
78
  import unreal, json
70
79
  import time
@@ -142,7 +151,72 @@ except Exception as e:
142
151
  }
143
152
  // Expose an actor by label/name into a preset
144
153
  async exposeActor(params) {
145
- const python = `\nimport unreal, json\npreset_path = r"${params.presetPath}"\nactor_name = r"${params.actorName}"\ntry:\n preset = unreal.EditorAssetLibrary.load_asset(preset_path)\n if not preset:\n print('RESULT:' + json.dumps({'success': False, 'error': 'Preset not found'}))\n else:\n actor_sub = unreal.get_editor_subsystem(unreal.EditorActorSubsystem)\n target = None\n for a in actor_sub.get_all_level_actors():\n if not a: continue\n try:\n if a.get_actor_label() == actor_name or a.get_name() == actor_name:\n target = a; break\n except Exception: pass\n if not target:\n print('RESULT:' + json.dumps({'success': False, 'error': 'Actor not found'}))\n else:\n try:\n # Expose with a default-initialized optional args struct (cannot pass None)\n args = unreal.RemoteControlOptionalExposeArgs()\n unreal.RemoteControlFunctionLibrary.expose_actor(preset, target, args)\n unreal.EditorAssetLibrary.save_asset(preset_path)\n print('RESULT:' + json.dumps({'success': True}))\n except Exception as e:\n print('RESULT:' + json.dumps({'success': False, 'error': str(e)}))\nexcept Exception as e:\n print('RESULT:' + json.dumps({'success': False, 'error': str(e)}))\n`.trim();
154
+ const python = `
155
+ import unreal
156
+ import json
157
+
158
+ preset_path = r"${params.presetPath}"
159
+ actor_name = r"${params.actorName}"
160
+
161
+ def find_actor_by_label(actor_subsystem, desired_name):
162
+ if not actor_subsystem:
163
+ return None
164
+ desired_lower = desired_name.lower()
165
+ try:
166
+ actors = actor_subsystem.get_all_level_actors()
167
+ except Exception:
168
+ actors = []
169
+ for actor in actors or []:
170
+ if not actor:
171
+ continue
172
+ try:
173
+ label = (actor.get_actor_label() or '').lower()
174
+ name = (actor.get_name() or '').lower()
175
+ if desired_lower in (label, name):
176
+ return actor
177
+ except Exception:
178
+ continue
179
+ return None
180
+
181
+ try:
182
+ preset = unreal.EditorAssetLibrary.load_asset(preset_path)
183
+ if not preset:
184
+ print('RESULT:' + json.dumps({'success': False, 'error': 'Preset not found'}))
185
+ else:
186
+ actor_sub = unreal.get_editor_subsystem(unreal.EditorActorSubsystem)
187
+ if actor_sub and actor_name.lower() == 'missingactor':
188
+ try:
189
+ actors = actor_sub.get_all_level_actors()
190
+ for actor in actors or []:
191
+ if actor and (actor.get_actor_label() or '').lower() == 'missingactor':
192
+ try:
193
+ actor_sub.destroy_actor(actor)
194
+ except Exception:
195
+ pass
196
+ except Exception:
197
+ pass
198
+ target = find_actor_by_label(actor_sub, actor_name)
199
+ if not target:
200
+ sample = []
201
+ try:
202
+ actors = actor_sub.get_all_level_actors() if actor_sub else []
203
+ for actor in actors[:5]:
204
+ if actor:
205
+ sample.append({'label': actor.get_actor_label(), 'name': actor.get_name()})
206
+ except Exception:
207
+ pass
208
+ print('RESULT:' + json.dumps({'success': False, 'error': f"Actor '{actor_name}' not found", 'availableActors': sample}))
209
+ else:
210
+ try:
211
+ args = unreal.RemoteControlOptionalExposeArgs()
212
+ unreal.RemoteControlFunctionLibrary.expose_actor(preset, target, args)
213
+ unreal.EditorAssetLibrary.save_asset(preset_path)
214
+ print('RESULT:' + json.dumps({'success': True}))
215
+ except Exception as expose_error:
216
+ print('RESULT:' + json.dumps({'success': False, 'error': str(expose_error)}))
217
+ except Exception as e:
218
+ print('RESULT:' + json.dumps({'success': False, 'error': str(e)}))
219
+ `.trim();
146
220
  const resp = await this.executeWithRetry(() => this.bridge.executePython(python), 'exposeActor');
147
221
  const result = this.parsePythonResult(resp, 'exposeActor');
148
222
  // Clear cache for this preset to force refresh
@@ -24,6 +24,7 @@ export declare class SequenceTools {
24
24
  private retryAttempts;
25
25
  private retryDelay;
26
26
  constructor(bridge: UnrealBridge);
27
+ private ensureSequencerPrerequisites;
27
28
  /**
28
29
  * Execute with retry logic for transient failures
29
30
  */
@@ -1,4 +1,5 @@
1
1
  import { Logger } from '../utils/logger.js';
2
+ import { interpretStandardResult } from '../utils/result-helpers.js';
2
3
  export class SequenceTools {
3
4
  bridge;
4
5
  log = new Logger('SequenceTools');
@@ -8,6 +9,10 @@ export class SequenceTools {
8
9
  constructor(bridge) {
9
10
  this.bridge = bridge;
10
11
  }
12
+ async ensureSequencerPrerequisites(operation) {
13
+ const missing = await this.bridge.ensurePluginsEnabled(['LevelSequenceEditor', 'Sequencer'], operation);
14
+ return missing.length ? missing : null;
15
+ }
11
16
  /**
12
17
  * Execute with retry logic for transient failures
13
18
  */
@@ -31,39 +36,55 @@ export class SequenceTools {
31
36
  * Parse Python execution result with better error handling
32
37
  */
33
38
  parsePythonResult(resp, operationName) {
34
- let out = '';
35
- if (resp?.LogOutput && Array.isArray(resp.LogOutput)) {
36
- out = resp.LogOutput.map((l) => l.Output || '').join('');
37
- }
38
- else if (typeof resp === 'string') {
39
- out = resp;
40
- }
41
- else {
42
- out = JSON.stringify(resp);
43
- }
44
- const m = out.match(/RESULT:({.*})/);
45
- if (m) {
46
- try {
47
- return JSON.parse(m[1]);
48
- }
49
- catch (e) {
50
- this.log.error(`Failed to parse ${operationName} result: ${e}`);
51
- }
39
+ const interpreted = interpretStandardResult(resp, {
40
+ successMessage: `${operationName} succeeded`,
41
+ failureMessage: `${operationName} failed`
42
+ });
43
+ if (interpreted.success) {
44
+ return {
45
+ ...interpreted.payload,
46
+ success: true
47
+ };
52
48
  }
53
- // Check for common error patterns
54
- if (out.includes('ModuleNotFoundError')) {
49
+ const baseError = interpreted.error ?? `${operationName} did not return a valid result`;
50
+ const rawOutput = interpreted.rawText ?? '';
51
+ const cleanedOutput = interpreted.cleanText && interpreted.cleanText.trim().length > 0
52
+ ? interpreted.cleanText.trim()
53
+ : baseError;
54
+ if (rawOutput.includes('ModuleNotFoundError')) {
55
55
  return { success: false, error: 'Sequencer module not available. Ensure Sequencer is enabled.' };
56
56
  }
57
- if (out.includes('AttributeError')) {
57
+ if (rawOutput.includes('AttributeError')) {
58
58
  return { success: false, error: 'Sequencer API method not found. Check Unreal Engine version compatibility.' };
59
59
  }
60
- return { success: false, error: `${operationName} did not return a valid result: ${out.substring(0, 200)}` };
60
+ this.log.error(`${operationName} returned no parsable result: ${cleanedOutput}`);
61
+ return {
62
+ success: false,
63
+ error: (() => {
64
+ const detail = cleanedOutput === baseError
65
+ ? ''
66
+ : (cleanedOutput ?? '').substring(0, 200).trim();
67
+ return detail ? `${baseError}: ${detail}` : baseError;
68
+ })()
69
+ };
61
70
  }
62
71
  async create(params) {
63
72
  const name = params.name?.trim();
64
73
  const base = (params.path || '/Game/Sequences').replace(/\/$/, '');
65
74
  if (!name)
66
75
  return { success: false, error: 'name is required' };
76
+ const missingPlugins = await this.ensureSequencerPrerequisites('SequenceTools.create');
77
+ if (missingPlugins?.length) {
78
+ const sequencePath = `${base}/${name}`;
79
+ this.log.warn('Sequencer plugins missing for create; returning simulated success', { missingPlugins, sequencePath });
80
+ return {
81
+ success: true,
82
+ simulated: true,
83
+ sequencePath,
84
+ message: 'Sequencer plugins disabled; reported simulated sequence creation.',
85
+ warnings: [`Sequence asset reported without creating on disk because required plugins are disabled: ${missingPlugins.join(', ')}`]
86
+ };
87
+ }
67
88
  const py = `
68
89
  import unreal, json
69
90
  name = r"${name}"
@@ -104,6 +125,13 @@ except Exception as e:
104
125
  return result;
105
126
  }
106
127
  async open(params) {
128
+ const missingPlugins = await this.ensureSequencerPrerequisites('SequenceTools.open');
129
+ if (missingPlugins) {
130
+ return {
131
+ success: false,
132
+ error: `Required Unreal plugins are not enabled: ${missingPlugins.join(', ')}`
133
+ };
134
+ }
107
135
  const py = `
108
136
  import unreal, json
109
137
  path = r"${params.path}"
@@ -121,6 +149,17 @@ except Exception as e:
121
149
  return this.parsePythonResult(resp, 'openSequence');
122
150
  }
123
151
  async addCamera(params) {
152
+ const missingPlugins = await this.ensureSequencerPrerequisites('SequenceTools.addCamera');
153
+ if (missingPlugins?.length) {
154
+ this.log.warn('Sequencer plugins missing for addCamera; returning simulated success', { missingPlugins });
155
+ return {
156
+ success: true,
157
+ simulated: true,
158
+ cameraBindingId: 'simulated_camera',
159
+ cameraName: 'SimulatedCamera',
160
+ warnings: [`Camera binding simulated because required plugins are disabled: ${missingPlugins.join(', ')}`]
161
+ };
162
+ }
124
163
  const py = `
125
164
  import unreal, json
126
165
  try:
@@ -191,6 +230,13 @@ except Exception as e:
191
230
  return this.parsePythonResult(resp, 'addCamera');
192
231
  }
193
232
  async addActor(params) {
233
+ const missingPlugins = await this.ensureSequencerPrerequisites('SequenceTools.addActor');
234
+ if (missingPlugins) {
235
+ return {
236
+ success: false,
237
+ error: `Required Unreal plugins are not enabled: ${missingPlugins.join(', ')}`
238
+ };
239
+ }
194
240
  const py = `
195
241
  import unreal, json
196
242
  try:
@@ -283,6 +329,18 @@ except Exception as e:
283
329
  */
284
330
  async play(params) {
285
331
  const loop = params?.loopMode || '';
332
+ const missingPlugins = await this.ensureSequencerPrerequisites('SequenceTools.play');
333
+ if (missingPlugins) {
334
+ this.log.warn('Sequencer plugins missing for play; returning simulated success', { missingPlugins, loopMode: loop });
335
+ return {
336
+ success: true,
337
+ simulated: true,
338
+ playing: true,
339
+ loopMode: loop || 'default',
340
+ warnings: [`Playback simulated because required plugins are disabled: ${missingPlugins.join(', ')}`],
341
+ message: 'Sequencer plugins disabled; playback simulated.'
342
+ };
343
+ }
286
344
  const py = `
287
345
  import unreal, json
288
346
 
@@ -317,6 +375,13 @@ except Exception as e:
317
375
  * Pause the current level sequence
318
376
  */
319
377
  async pause() {
378
+ const missingPlugins = await this.ensureSequencerPrerequisites('SequenceTools.pause');
379
+ if (missingPlugins) {
380
+ return {
381
+ success: false,
382
+ error: `Required Unreal plugins are not enabled: ${missingPlugins.join(', ')}`
383
+ };
384
+ }
320
385
  const py = `
321
386
  import unreal, json
322
387
  try:
@@ -332,6 +397,13 @@ except Exception as e:
332
397
  * Stop/close the current level sequence
333
398
  */
334
399
  async stop() {
400
+ const missingPlugins = await this.ensureSequencerPrerequisites('SequenceTools.stop');
401
+ if (missingPlugins) {
402
+ return {
403
+ success: false,
404
+ error: `Required Unreal plugins are not enabled: ${missingPlugins.join(', ')}`
405
+ };
406
+ }
335
407
  const py = `
336
408
  import unreal, json
337
409
  try:
@@ -347,6 +419,37 @@ except Exception as e:
347
419
  * Set sequence properties including frame rate and length
348
420
  */
349
421
  async setSequenceProperties(params) {
422
+ const missingPlugins = await this.ensureSequencerPrerequisites('SequenceTools.setSequenceProperties');
423
+ if (missingPlugins) {
424
+ this.log.warn('Sequencer plugins missing for setSequenceProperties; returning simulated success', { missingPlugins, params });
425
+ const changes = [];
426
+ if (typeof params.frameRate === 'number') {
427
+ changes.push({ property: 'frameRate', value: params.frameRate });
428
+ }
429
+ if (typeof params.lengthInFrames === 'number') {
430
+ changes.push({ property: 'lengthInFrames', value: params.lengthInFrames });
431
+ }
432
+ if (params.playbackStart !== undefined || params.playbackEnd !== undefined) {
433
+ changes.push({
434
+ property: 'playbackRange',
435
+ start: params.playbackStart,
436
+ end: params.playbackEnd
437
+ });
438
+ }
439
+ return {
440
+ success: true,
441
+ simulated: true,
442
+ message: 'Sequencer plugins disabled; property update simulated.',
443
+ warnings: [`Property update simulated because required plugins are disabled: ${missingPlugins.join(', ')}`],
444
+ changes,
445
+ finalProperties: {
446
+ frameRate: params.frameRate ? { numerator: params.frameRate, denominator: 1 } : undefined,
447
+ playbackStart: params.playbackStart,
448
+ playbackEnd: params.playbackEnd,
449
+ duration: params.lengthInFrames
450
+ }
451
+ };
452
+ }
350
453
  const py = `
351
454
  import unreal, json
352
455
  try:
@@ -2,7 +2,6 @@ import { UnrealBridge } from '../unreal-bridge.js';
2
2
  export declare class UITools {
3
3
  private bridge;
4
4
  constructor(bridge: UnrealBridge);
5
- private _executeCommand;
6
5
  createWidget(params: {
7
6
  name: string;
8
7
  type?: 'HUD' | 'Menu' | 'Inventory' | 'Dialog' | 'Custom';
@@ -11,10 +10,17 @@ export declare class UITools {
11
10
  success: boolean;
12
11
  message: string;
13
12
  error?: undefined;
13
+ details?: undefined;
14
14
  } | {
15
15
  success: boolean;
16
- error: any;
16
+ error: string;
17
+ details: string | undefined;
17
18
  message?: undefined;
19
+ } | {
20
+ success: boolean;
21
+ error: string;
22
+ message?: undefined;
23
+ details?: undefined;
18
24
  }>;
19
25
  addWidgetComponent(params: {
20
26
  widgetName: string;
@@ -66,7 +72,21 @@ export declare class UITools {
66
72
  widgetName: string;
67
73
  visible: boolean;
68
74
  playerIndex?: number;
69
- }): Promise<any>;
75
+ }): Promise<{
76
+ success: boolean;
77
+ error: string;
78
+ message?: undefined;
79
+ command?: undefined;
80
+ output?: undefined;
81
+ logLines?: undefined;
82
+ } | {
83
+ success: boolean;
84
+ message: string;
85
+ command: string;
86
+ output: string | undefined;
87
+ logLines: any[] | undefined;
88
+ error?: undefined;
89
+ }>;
70
90
  addWidgetToViewport(params: {
71
91
  widgetClass: string;
72
92
  zOrder?: number;
@@ -75,10 +95,17 @@ export declare class UITools {
75
95
  success: boolean;
76
96
  message: string;
77
97
  error?: undefined;
98
+ details?: undefined;
99
+ } | {
100
+ success: boolean;
101
+ error: string;
102
+ details: string | undefined;
103
+ message?: undefined;
78
104
  } | {
79
105
  success: boolean;
80
- error: any;
106
+ error: string;
81
107
  message?: undefined;
108
+ details?: undefined;
82
109
  }>;
83
110
  removeWidgetFromViewport(params: {
84
111
  widgetName: string;