blueprint-extractor-mcp 2.2.0 → 2.3.0
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/README.md +4 -3
- package/dist/automation-controller.js +3 -10
- package/dist/index.js +656 -101
- package/dist/project-controller.d.ts +28 -0
- package/dist/project-controller.js +62 -6
- package/package.json +1 -1
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process';
|
|
1
2
|
export type BuildPlatform = 'Win64' | 'Mac' | 'Linux';
|
|
2
3
|
export type BuildConfiguration = 'Debug' | 'DebugGame' | 'Development' | 'Shipping' | 'Test';
|
|
3
4
|
export type SyncStrategy = 'live_coding' | 'build_and_restart';
|
|
@@ -11,6 +12,11 @@ export interface CompileProjectCodeRequest {
|
|
|
11
12
|
includeOutput?: boolean;
|
|
12
13
|
clearUhtCache?: boolean;
|
|
13
14
|
}
|
|
15
|
+
export interface LaunchEditorRequest {
|
|
16
|
+
engineRoot?: string;
|
|
17
|
+
projectPath?: string;
|
|
18
|
+
additionalArgs?: string[];
|
|
19
|
+
}
|
|
14
20
|
export interface SyncProjectCodeRequest extends CompileProjectCodeRequest {
|
|
15
21
|
changedPaths: string[];
|
|
16
22
|
forceRebuild?: boolean;
|
|
@@ -58,6 +64,19 @@ export interface RestartReconnectResult {
|
|
|
58
64
|
reconnectTimeoutMs: number;
|
|
59
65
|
diagnostics: string[];
|
|
60
66
|
}
|
|
67
|
+
export interface LaunchEditorResult {
|
|
68
|
+
success: boolean;
|
|
69
|
+
operation: 'launch_editor';
|
|
70
|
+
engineRoot: string;
|
|
71
|
+
projectPath: string;
|
|
72
|
+
projectDir: string;
|
|
73
|
+
command: {
|
|
74
|
+
executable: string;
|
|
75
|
+
args: string[];
|
|
76
|
+
};
|
|
77
|
+
detached: boolean;
|
|
78
|
+
diagnostics: string[];
|
|
79
|
+
}
|
|
61
80
|
export type ProbeConnection = (() => Promise<boolean>) | null;
|
|
62
81
|
export interface CommandInvocation {
|
|
63
82
|
executable: string;
|
|
@@ -78,18 +97,23 @@ export interface ProjectControllerOptions {
|
|
|
78
97
|
env?: NodeJS.ProcessEnv;
|
|
79
98
|
platform?: NodeJS.Platform;
|
|
80
99
|
runCommand?: CommandRunner;
|
|
100
|
+
spawnProcess?: typeof spawn;
|
|
81
101
|
sleep?: (ms: number) => Promise<void>;
|
|
82
102
|
}
|
|
83
103
|
export interface ProjectControllerLike {
|
|
84
104
|
readonly liveCodingSupported: boolean;
|
|
85
105
|
classifyChangedPaths(changedPaths: string[], forceRebuild?: boolean): SyncStrategyPlan;
|
|
86
106
|
compileProjectCode(request: CompileProjectCodeRequest): Promise<CompileProjectCodeResult>;
|
|
107
|
+
launchEditor(request: LaunchEditorRequest): Promise<LaunchEditorResult>;
|
|
87
108
|
waitForEditorRestart(probeConnection: ProbeConnection, options?: {
|
|
88
109
|
disconnectTimeoutMs?: number;
|
|
89
110
|
reconnectTimeoutMs?: number;
|
|
111
|
+
waitForDisconnect?: boolean;
|
|
112
|
+
waitForReconnect?: boolean;
|
|
90
113
|
}): Promise<RestartReconnectResult>;
|
|
91
114
|
}
|
|
92
115
|
export declare function resolveCommandInvocation(executable: string, args: string[], platform: NodeJS.Platform, env?: NodeJS.ProcessEnv): CommandInvocation;
|
|
116
|
+
export declare function resolveEditorExecutable(engineRoot: string, platform: NodeJS.Platform, mode?: 'editor' | 'commandlet'): Promise<string>;
|
|
93
117
|
export declare function classifyChangedPaths(changedPaths: string[], forceRebuild?: boolean): SyncStrategyPlan;
|
|
94
118
|
/** Classify UBT build output into an error category. */
|
|
95
119
|
export declare function classifyBuildError(stdout: string, stderr: string, exitCode: number): {
|
|
@@ -101,13 +125,17 @@ export declare class ProjectController implements ProjectControllerLike {
|
|
|
101
125
|
private readonly env;
|
|
102
126
|
private readonly platform;
|
|
103
127
|
private readonly runCommand;
|
|
128
|
+
private readonly spawnProcess;
|
|
104
129
|
private readonly sleep;
|
|
105
130
|
constructor(options?: ProjectControllerOptions);
|
|
106
131
|
get liveCodingSupported(): boolean;
|
|
107
132
|
classifyChangedPaths(changedPaths: string[], forceRebuild?: boolean): SyncStrategyPlan;
|
|
108
133
|
compileProjectCode(request: CompileProjectCodeRequest): Promise<CompileProjectCodeResult>;
|
|
134
|
+
launchEditor(request: LaunchEditorRequest): Promise<LaunchEditorResult>;
|
|
109
135
|
waitForEditorRestart(probeConnection: ProbeConnection, options?: {
|
|
110
136
|
disconnectTimeoutMs?: number;
|
|
111
137
|
reconnectTimeoutMs?: number;
|
|
138
|
+
waitForDisconnect?: boolean;
|
|
139
|
+
waitForReconnect?: boolean;
|
|
112
140
|
}): Promise<RestartReconnectResult>;
|
|
113
141
|
}
|
|
@@ -38,6 +38,18 @@ export function resolveCommandInvocation(executable, args, platform, env = proce
|
|
|
38
38
|
args: ['/d', '/s', '/c', commandLine],
|
|
39
39
|
};
|
|
40
40
|
}
|
|
41
|
+
export async function resolveEditorExecutable(engineRoot, platform, mode = 'editor') {
|
|
42
|
+
const executableName = mode === 'commandlet'
|
|
43
|
+
? (platform === 'win32' ? 'UnrealEditor-Cmd.exe' : 'UnrealEditor-Cmd')
|
|
44
|
+
: (platform === 'win32' ? 'UnrealEditor.exe' : 'UnrealEditor');
|
|
45
|
+
const executable = platform === 'win32'
|
|
46
|
+
? resolve(engineRoot, 'Engine', 'Binaries', 'Win64', executableName)
|
|
47
|
+
: platform === 'darwin'
|
|
48
|
+
? resolve(engineRoot, 'Engine', 'Binaries', 'Mac', executableName)
|
|
49
|
+
: resolve(engineRoot, 'Engine', 'Binaries', 'Linux', executableName);
|
|
50
|
+
await access(executable, fsConstants.F_OK);
|
|
51
|
+
return executable;
|
|
52
|
+
}
|
|
41
53
|
async function defaultRunCommand(executable, args, options) {
|
|
42
54
|
return await new Promise((resolveRun, rejectRun) => {
|
|
43
55
|
const invocation = resolveCommandInvocation(executable, args, process.platform, options.env ?? process.env);
|
|
@@ -276,11 +288,13 @@ export class ProjectController {
|
|
|
276
288
|
env;
|
|
277
289
|
platform;
|
|
278
290
|
runCommand;
|
|
291
|
+
spawnProcess;
|
|
279
292
|
sleep;
|
|
280
293
|
constructor(options = {}) {
|
|
281
294
|
this.env = options.env ?? process.env;
|
|
282
295
|
this.platform = parsePlatform(options.platform, process.platform);
|
|
283
296
|
this.runCommand = options.runCommand ?? defaultRunCommand;
|
|
297
|
+
this.spawnProcess = options.spawnProcess ?? spawn;
|
|
284
298
|
this.sleep = options.sleep ?? defaultSleep;
|
|
285
299
|
}
|
|
286
300
|
get liveCodingSupported() {
|
|
@@ -369,12 +383,52 @@ export class ProjectController {
|
|
|
369
383
|
}
|
|
370
384
|
return result;
|
|
371
385
|
}
|
|
386
|
+
async launchEditor(request) {
|
|
387
|
+
const engineRoot = request.engineRoot ?? this.env.UE_ENGINE_ROOT;
|
|
388
|
+
const projectPath = request.projectPath ?? this.env.UE_PROJECT_PATH;
|
|
389
|
+
const diagnostics = [];
|
|
390
|
+
if (!engineRoot) {
|
|
391
|
+
throw new Error('launch_editor requires engine_root or UE_ENGINE_ROOT');
|
|
392
|
+
}
|
|
393
|
+
if (!projectPath) {
|
|
394
|
+
throw new Error('launch_editor requires project_path or UE_PROJECT_PATH');
|
|
395
|
+
}
|
|
396
|
+
const executable = await resolveEditorExecutable(engineRoot, this.platform, 'editor');
|
|
397
|
+
const args = [projectPath, ...(request.additionalArgs ?? [])];
|
|
398
|
+
const invocation = resolveCommandInvocation(executable, args, this.platform, this.env);
|
|
399
|
+
const child = this.spawnProcess(invocation.executable, invocation.args, {
|
|
400
|
+
cwd: dirname(projectPath),
|
|
401
|
+
env: this.env,
|
|
402
|
+
shell: false,
|
|
403
|
+
windowsVerbatimArguments: isWindowsBatchScript(executable, this.platform),
|
|
404
|
+
detached: true,
|
|
405
|
+
stdio: 'ignore',
|
|
406
|
+
});
|
|
407
|
+
child.unref();
|
|
408
|
+
return {
|
|
409
|
+
success: true,
|
|
410
|
+
operation: 'launch_editor',
|
|
411
|
+
engineRoot,
|
|
412
|
+
projectPath,
|
|
413
|
+
projectDir: dirname(projectPath),
|
|
414
|
+
command: {
|
|
415
|
+
executable: invocation.executable,
|
|
416
|
+
args: invocation.args,
|
|
417
|
+
},
|
|
418
|
+
detached: true,
|
|
419
|
+
diagnostics,
|
|
420
|
+
};
|
|
421
|
+
}
|
|
372
422
|
async waitForEditorRestart(probeConnection, options = {}) {
|
|
373
423
|
const disconnectTimeoutMs = options.disconnectTimeoutMs ?? DEFAULT_DISCONNECT_TIMEOUT_MS;
|
|
374
424
|
const reconnectTimeoutMs = options.reconnectTimeoutMs ?? DEFAULT_RECONNECT_TIMEOUT_MS;
|
|
425
|
+
const waitForDisconnect = options.waitForDisconnect ?? true;
|
|
426
|
+
const waitForReconnect = options.waitForReconnect ?? true;
|
|
375
427
|
const diagnostics = [];
|
|
376
|
-
const disconnected =
|
|
377
|
-
|
|
428
|
+
const disconnected = waitForDisconnect
|
|
429
|
+
? await waitForState(probeConnection, false, disconnectTimeoutMs, this.sleep)
|
|
430
|
+
: false;
|
|
431
|
+
if (waitForDisconnect && !disconnected) {
|
|
378
432
|
diagnostics.push('Editor never disconnected after restart request');
|
|
379
433
|
return {
|
|
380
434
|
success: false,
|
|
@@ -386,14 +440,16 @@ export class ProjectController {
|
|
|
386
440
|
diagnostics,
|
|
387
441
|
};
|
|
388
442
|
}
|
|
389
|
-
const reconnected =
|
|
390
|
-
|
|
443
|
+
const reconnected = waitForReconnect
|
|
444
|
+
? await waitForState(probeConnection, true, reconnectTimeoutMs, this.sleep)
|
|
445
|
+
: false;
|
|
446
|
+
if (waitForReconnect && !reconnected) {
|
|
391
447
|
diagnostics.push('Editor did not reconnect before the timeout elapsed');
|
|
392
448
|
}
|
|
393
449
|
return {
|
|
394
|
-
success: reconnected,
|
|
450
|
+
success: (waitForDisconnect ? disconnected : true) && (waitForReconnect ? reconnected : true),
|
|
395
451
|
operation: 'restart_editor',
|
|
396
|
-
disconnected
|
|
452
|
+
disconnected,
|
|
397
453
|
reconnected,
|
|
398
454
|
disconnectTimeoutMs,
|
|
399
455
|
reconnectTimeoutMs,
|