flockbay 0.10.19 → 0.10.21
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/dist/codex/flockbayMcpStdioBridge.cjs +8 -0
- package/dist/codex/flockbayMcpStdioBridge.mjs +8 -0
- package/dist/{index-BiUf5vLX.cjs → index-Bhkn02hu.cjs} +279 -38
- package/dist/{index-5jfGXWTy.mjs → index-By332wvJ.mjs} +279 -38
- package/dist/index.cjs +2 -2
- package/dist/index.mjs +2 -2
- package/dist/lib.cjs +1 -1
- package/dist/lib.mjs +1 -1
- package/dist/{runCodex-Bh3-ebwT.cjs → runCodex-CH4lz1QX.cjs} +9 -5
- package/dist/{runCodex-DwsaTF4s.mjs → runCodex-d2KQX2mn.mjs} +9 -5
- package/dist/{runGemini-qA5dD13X.mjs → runGemini-Cn0C7MS1.mjs} +101 -31
- package/dist/{runGemini-hXryGqFd.cjs → runGemini-DNSymY04.cjs} +101 -31
- package/dist/{types-CL_3YyS9.cjs → types-DeH24uWs.cjs} +2 -2
- package/dist/{types-BQvaA3sv.mjs → types-mXJc7o0P.mjs} +1 -1
- package/package.json +1 -1
- package/scripts/claude_version_utils.cjs +66 -12
- package/tools/unreal-mcp/upstream/MCPGameProject/Plugins/UnrealMCP/Source/UnrealMCP/Private/Commands/UnrealMCPCommandSchema.cpp +10 -0
- package/tools/unreal-mcp/upstream/MCPGameProject/Plugins/UnrealMCP/Source/UnrealMCP/Private/Commands/UnrealMCPEditorCommands.cpp +205 -13
- package/tools/unreal-mcp/upstream/MCPGameProject/Plugins/UnrealMCP/Source/UnrealMCP/UnrealMCP.Build.cs +1 -0
|
@@ -614,6 +614,14 @@ async function main() {
|
|
|
614
614
|
"Unreal Editor: Launch Project",
|
|
615
615
|
"Launch Unreal Editor for a given .uproject (no auto-restart). If the editor later crashes or becomes unreachable, Flockbay will abort the current agent run and report it in the chat."
|
|
616
616
|
);
|
|
617
|
+
forwardTool(
|
|
618
|
+
"unreal_editor_relaunch_last",
|
|
619
|
+
{
|
|
620
|
+
extraArgs: z.z.array(z.z.string()).optional().describe("Optional replacement UnrealEditor command-line args (advanced).")
|
|
621
|
+
},
|
|
622
|
+
"Unreal Editor: Relaunch Last Project",
|
|
623
|
+
"Relaunch the last Unreal project previously launched via unreal_editor_launch in this session (no auto-restart). Use this after a crash once you\u2019ve fixed files."
|
|
624
|
+
);
|
|
617
625
|
forwardTool(
|
|
618
626
|
"unreal_headless_screenshot",
|
|
619
627
|
{
|
|
@@ -612,6 +612,14 @@ async function main() {
|
|
|
612
612
|
"Unreal Editor: Launch Project",
|
|
613
613
|
"Launch Unreal Editor for a given .uproject (no auto-restart). If the editor later crashes or becomes unreachable, Flockbay will abort the current agent run and report it in the chat."
|
|
614
614
|
);
|
|
615
|
+
forwardTool(
|
|
616
|
+
"unreal_editor_relaunch_last",
|
|
617
|
+
{
|
|
618
|
+
extraArgs: z.array(z.string()).optional().describe("Optional replacement UnrealEditor command-line args (advanced).")
|
|
619
|
+
},
|
|
620
|
+
"Unreal Editor: Relaunch Last Project",
|
|
621
|
+
"Relaunch the last Unreal project previously launched via unreal_editor_launch in this session (no auto-restart). Use this after a crash once you\u2019ve fixed files."
|
|
622
|
+
);
|
|
615
623
|
forwardTool(
|
|
616
624
|
"unreal_headless_screenshot",
|
|
617
625
|
{
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
var chalk = require('chalk');
|
|
4
4
|
var os = require('node:os');
|
|
5
5
|
var node_crypto = require('node:crypto');
|
|
6
|
-
var types = require('./types-
|
|
6
|
+
var types = require('./types-DeH24uWs.cjs');
|
|
7
7
|
var node_child_process = require('node:child_process');
|
|
8
8
|
var path = require('node:path');
|
|
9
9
|
var node_readline = require('node:readline');
|
|
@@ -1258,7 +1258,8 @@ function buildDaemonSafeEnv(baseEnv, binPath) {
|
|
|
1258
1258
|
if (!p) return;
|
|
1259
1259
|
if (!prepend.includes(p) && !existingParts.includes(p)) prepend.push(p);
|
|
1260
1260
|
};
|
|
1261
|
-
|
|
1261
|
+
const isPathLike = typeof binPath === "string" && binPath.length > 0 && (binPath.includes("/") || binPath.includes("\\")) && !binPath.startsWith("\\\\");
|
|
1262
|
+
if (isPathLike) {
|
|
1262
1263
|
add(path.dirname(binPath));
|
|
1263
1264
|
}
|
|
1264
1265
|
add(path.dirname(process$1.execPath));
|
|
@@ -1272,12 +1273,12 @@ function buildDaemonSafeEnv(baseEnv, binPath) {
|
|
|
1272
1273
|
env[pathKey] = [...prepend, ...existingParts].join(pathSep);
|
|
1273
1274
|
return env;
|
|
1274
1275
|
}
|
|
1275
|
-
const __filename$1 = node_url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index-
|
|
1276
|
+
const __filename$1 = node_url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index-Bhkn02hu.cjs', document.baseURI).href)));
|
|
1276
1277
|
const __dirname$1 = path.join(__filename$1, "..");
|
|
1277
1278
|
function getGlobalClaudeVersion(claudeExecutable) {
|
|
1278
1279
|
try {
|
|
1279
1280
|
const cleanEnv = buildDaemonSafeEnv(getCleanEnv(), claudeExecutable);
|
|
1280
|
-
const output = claudeExecutable.includes("/") && !claudeExecutable.startsWith("\\\\") ? node_child_process.execFileSync(claudeExecutable, ["--version"], {
|
|
1281
|
+
const output = (claudeExecutable.includes("/") || claudeExecutable.includes("\\")) && !claudeExecutable.startsWith("\\\\") ? node_child_process.execFileSync(claudeExecutable, ["--version"], {
|
|
1281
1282
|
encoding: "utf8",
|
|
1282
1283
|
stdio: ["pipe", "pipe", "pipe"],
|
|
1283
1284
|
cwd: os.homedir(),
|
|
@@ -1295,6 +1296,35 @@ function getGlobalClaudeVersion(claudeExecutable) {
|
|
|
1295
1296
|
return null;
|
|
1296
1297
|
}
|
|
1297
1298
|
}
|
|
1299
|
+
function tryReadBundledClaudeVersion(nodeModulesCliPath) {
|
|
1300
|
+
try {
|
|
1301
|
+
const pkgPath = path.join(path.dirname(path.dirname(nodeModulesCliPath)), "package.json");
|
|
1302
|
+
if (!fs.existsSync(pkgPath)) return null;
|
|
1303
|
+
const raw = fs.readFileSync(pkgPath, "utf8");
|
|
1304
|
+
const parsed = JSON.parse(raw);
|
|
1305
|
+
const v = typeof parsed?.version === "string" ? parsed.version.trim() : "";
|
|
1306
|
+
return v || null;
|
|
1307
|
+
} catch {
|
|
1308
|
+
return null;
|
|
1309
|
+
}
|
|
1310
|
+
}
|
|
1311
|
+
function parseSemver3(v) {
|
|
1312
|
+
const m = String(v || "").trim().match(/^(\d+)\.(\d+)\.(\d+)/);
|
|
1313
|
+
if (!m) return null;
|
|
1314
|
+
const a = Number(m[1]);
|
|
1315
|
+
const b = Number(m[2]);
|
|
1316
|
+
const c = Number(m[3]);
|
|
1317
|
+
if (!Number.isFinite(a) || !Number.isFinite(b) || !Number.isFinite(c)) return null;
|
|
1318
|
+
return [a, b, c];
|
|
1319
|
+
}
|
|
1320
|
+
function compareSemver(a, b) {
|
|
1321
|
+
const pa = parseSemver3(a);
|
|
1322
|
+
const pb = parseSemver3(b);
|
|
1323
|
+
if (!pa || !pb) return null;
|
|
1324
|
+
if (pa[0] !== pb[0]) return pa[0] - pb[0];
|
|
1325
|
+
if (pa[1] !== pb[1]) return pa[1] - pb[1];
|
|
1326
|
+
return pa[2] - pb[2];
|
|
1327
|
+
}
|
|
1298
1328
|
function getCleanEnv() {
|
|
1299
1329
|
const env = { ...process$1.env };
|
|
1300
1330
|
const cwd = process$1.cwd();
|
|
@@ -1414,11 +1444,22 @@ function getDefaultClaudeCodePath() {
|
|
|
1414
1444
|
return nodeModulesPath;
|
|
1415
1445
|
}
|
|
1416
1446
|
const globalVersion = getGlobalClaudeVersion(globalPath);
|
|
1447
|
+
const bundledVersion = tryReadBundledClaudeVersion(nodeModulesPath);
|
|
1417
1448
|
types.logger.debug(`[Claude SDK] Global version: ${globalVersion || "unknown"}`);
|
|
1418
|
-
|
|
1449
|
+
types.logger.debug(`[Claude SDK] Bundled version: ${bundledVersion || "unknown"}`);
|
|
1450
|
+
if (!globalVersion || !bundledVersion) {
|
|
1419
1451
|
types.logger.debug(`[Claude SDK] Cannot compare versions, using global: ${globalPath}`);
|
|
1420
1452
|
return globalPath;
|
|
1421
1453
|
}
|
|
1454
|
+
const cmp = compareSemver(bundledVersion, globalVersion);
|
|
1455
|
+
if (cmp === null) {
|
|
1456
|
+
types.logger.debug(`[Claude SDK] Cannot parse versions, using global: ${globalPath}`);
|
|
1457
|
+
return globalPath;
|
|
1458
|
+
}
|
|
1459
|
+
if (cmp > 0) {
|
|
1460
|
+
types.logger.debug(`[Claude SDK] Bundled Claude is newer (${bundledVersion} > ${globalVersion}), using bundled: ${nodeModulesPath}`);
|
|
1461
|
+
return nodeModulesPath;
|
|
1462
|
+
}
|
|
1422
1463
|
return globalPath;
|
|
1423
1464
|
}
|
|
1424
1465
|
function logDebug(message) {
|
|
@@ -1693,8 +1734,9 @@ function query(config) {
|
|
|
1693
1734
|
stdio: ["pipe", "pipe", "pipe"],
|
|
1694
1735
|
signal: config.options?.abort,
|
|
1695
1736
|
env: spawnEnv,
|
|
1696
|
-
//
|
|
1697
|
-
|
|
1737
|
+
// Only use a shell on Windows when spawning a bare command (e.g. "claude").
|
|
1738
|
+
// Passing large `--allowedTools` lists through cmd.exe can hit the ~8k command line limit.
|
|
1739
|
+
shell: isCommandOnly && process$1.platform === "win32"
|
|
1698
1740
|
});
|
|
1699
1741
|
let childStdin = null;
|
|
1700
1742
|
if (typeof prompt === "string") {
|
|
@@ -2470,6 +2512,21 @@ async function consumeToolQuota(args) {
|
|
|
2470
2512
|
return { ok: true, allowed: true, unlimited: true };
|
|
2471
2513
|
}
|
|
2472
2514
|
|
|
2515
|
+
function canonicalizeToolNameForMatching(name) {
|
|
2516
|
+
if (name === "ExitPlanMode") return "exit_plan_mode";
|
|
2517
|
+
return name;
|
|
2518
|
+
}
|
|
2519
|
+
function stripUndefinedDeep(value) {
|
|
2520
|
+
if (value === null || value === void 0) return value;
|
|
2521
|
+
if (Array.isArray(value)) return value.map(stripUndefinedDeep);
|
|
2522
|
+
if (typeof value !== "object") return value;
|
|
2523
|
+
const out = {};
|
|
2524
|
+
for (const [key, child] of Object.entries(value)) {
|
|
2525
|
+
if (child === void 0) continue;
|
|
2526
|
+
out[key] = stripUndefinedDeep(child);
|
|
2527
|
+
}
|
|
2528
|
+
return out;
|
|
2529
|
+
}
|
|
2473
2530
|
class PermissionHandler {
|
|
2474
2531
|
toolCalls = [];
|
|
2475
2532
|
responses = /* @__PURE__ */ new Map();
|
|
@@ -2753,8 +2810,13 @@ ${next}`
|
|
|
2753
2810
|
}
|
|
2754
2811
|
let toolCallId = this.resolveToolCallId(toolName, input);
|
|
2755
2812
|
if (!toolCallId) {
|
|
2756
|
-
|
|
2757
|
-
|
|
2813
|
+
const isPlanMode = toolName === "exit_plan_mode" || toolName === "ExitPlanMode";
|
|
2814
|
+
const timeoutMs = isPlanMode ? 3e3 : 1e3;
|
|
2815
|
+
const deadline = Date.now() + timeoutMs;
|
|
2816
|
+
while (!toolCallId && Date.now() < deadline) {
|
|
2817
|
+
await types.delay(100);
|
|
2818
|
+
toolCallId = this.resolveToolCallId(toolName, input);
|
|
2819
|
+
}
|
|
2758
2820
|
if (!toolCallId) {
|
|
2759
2821
|
throw new Error(`Could not resolve tool call ID for ${toolName}`);
|
|
2760
2822
|
}
|
|
@@ -2835,9 +2897,12 @@ ${next}`
|
|
|
2835
2897
|
* Resolves tool call ID based on tool name and input
|
|
2836
2898
|
*/
|
|
2837
2899
|
resolveToolCallId(name, args) {
|
|
2900
|
+
const normalizedName = canonicalizeToolNameForMatching(name);
|
|
2901
|
+
const normalizedArgs = stripUndefinedDeep(args);
|
|
2838
2902
|
for (let i = this.toolCalls.length - 1; i >= 0; i--) {
|
|
2839
2903
|
const call = this.toolCalls[i];
|
|
2840
|
-
|
|
2904
|
+
const callName = canonicalizeToolNameForMatching(call.name);
|
|
2905
|
+
if (callName === normalizedName && deepEqual(stripUndefinedDeep(call.input), normalizedArgs)) {
|
|
2841
2906
|
if (call.used) {
|
|
2842
2907
|
return null;
|
|
2843
2908
|
}
|
|
@@ -2845,6 +2910,18 @@ ${next}`
|
|
|
2845
2910
|
return call.id;
|
|
2846
2911
|
}
|
|
2847
2912
|
}
|
|
2913
|
+
const candidates = [];
|
|
2914
|
+
for (let i = this.toolCalls.length - 1; i >= 0; i--) {
|
|
2915
|
+
const call = this.toolCalls[i];
|
|
2916
|
+
if (call.used) continue;
|
|
2917
|
+
const callName = canonicalizeToolNameForMatching(call.name);
|
|
2918
|
+
if (callName === normalizedName) candidates.push(call);
|
|
2919
|
+
if (candidates.length > 1) break;
|
|
2920
|
+
}
|
|
2921
|
+
if (candidates.length === 1) {
|
|
2922
|
+
candidates[0].used = true;
|
|
2923
|
+
return candidates[0].id;
|
|
2924
|
+
}
|
|
2848
2925
|
return null;
|
|
2849
2926
|
}
|
|
2850
2927
|
/**
|
|
@@ -7117,7 +7194,11 @@ async function uploadScreenshotViewsForSession(args) {
|
|
|
7117
7194
|
for (const v of args.views) {
|
|
7118
7195
|
const buf = await fs$2.readFile(v.path);
|
|
7119
7196
|
const filename = path.basename(v.path);
|
|
7120
|
-
const contentType =
|
|
7197
|
+
const contentType = (() => {
|
|
7198
|
+
const mime = detectImageMimeTypeFromBuffer(buf);
|
|
7199
|
+
if (mime) return mime;
|
|
7200
|
+
throw new Error(`Unsupported screenshot format (expected PNG/JPEG): ${v.path}`);
|
|
7201
|
+
})();
|
|
7121
7202
|
const blob = new Blob([buf], { type: contentType });
|
|
7122
7203
|
form.append(`file:${v.id}`, blob, filename);
|
|
7123
7204
|
}
|
|
@@ -7158,6 +7239,22 @@ async function uploadScreenshotViewsForSession(args) {
|
|
|
7158
7239
|
}
|
|
7159
7240
|
return out;
|
|
7160
7241
|
}
|
|
7242
|
+
function detectImageMimeTypeFromBuffer(buf) {
|
|
7243
|
+
if (!buf || buf.length < 12) return null;
|
|
7244
|
+
if (buf[0] === 137 && buf[1] === 80 && buf[2] === 78 && buf[3] === 71 && buf[4] === 13 && buf[5] === 10 && buf[6] === 26 && buf[7] === 10) {
|
|
7245
|
+
return "image/png";
|
|
7246
|
+
}
|
|
7247
|
+
if (buf[0] === 255 && buf[1] === 216 && buf[2] === 255) {
|
|
7248
|
+
return "image/jpeg";
|
|
7249
|
+
}
|
|
7250
|
+
if (buf[0] === 71 && buf[1] === 73 && buf[2] === 70 && buf[3] === 56 && (buf[4] === 55 || buf[4] === 57) && buf[5] === 97) {
|
|
7251
|
+
return "image/gif";
|
|
7252
|
+
}
|
|
7253
|
+
if (buf[0] === 82 && buf[1] === 73 && buf[2] === 70 && buf[3] === 70 && buf[8] === 87 && buf[9] === 69 && buf[10] === 66 && buf[11] === 80) {
|
|
7254
|
+
return "image/webp";
|
|
7255
|
+
}
|
|
7256
|
+
return null;
|
|
7257
|
+
}
|
|
7161
7258
|
async function startFlockbayServer(client, options) {
|
|
7162
7259
|
const handler = async (title) => {
|
|
7163
7260
|
types.logger.debug("[flockbayMCP] Changing title to:", title);
|
|
@@ -7230,7 +7327,8 @@ async function startFlockbayServer(client, options) {
|
|
|
7230
7327
|
lastReachableAtMs: 0,
|
|
7231
7328
|
lastIssueAtMs: 0,
|
|
7232
7329
|
lastIssueKey: "",
|
|
7233
|
-
launched: null
|
|
7330
|
+
launched: null,
|
|
7331
|
+
lastLaunch: null
|
|
7234
7332
|
};
|
|
7235
7333
|
const emitIssue = (event) => {
|
|
7236
7334
|
const key = `${event.kind}:${event.severity}:${event.message}`;
|
|
@@ -7255,6 +7353,93 @@ async function startFlockbayServer(client, options) {
|
|
|
7255
7353
|
}
|
|
7256
7354
|
}
|
|
7257
7355
|
};
|
|
7356
|
+
const tailText = (text, maxChars) => {
|
|
7357
|
+
const t = String(text || "").trim();
|
|
7358
|
+
if (!t) return "";
|
|
7359
|
+
if (t.length <= maxChars) return t;
|
|
7360
|
+
return t.slice(t.length - maxChars);
|
|
7361
|
+
};
|
|
7362
|
+
const findLatestFile = async (dir, filter) => {
|
|
7363
|
+
try {
|
|
7364
|
+
const entries = await fs$2.readdir(dir);
|
|
7365
|
+
let best = null;
|
|
7366
|
+
for (const name of entries) {
|
|
7367
|
+
if (!filter(name)) continue;
|
|
7368
|
+
const full = path.join(dir, name);
|
|
7369
|
+
let st;
|
|
7370
|
+
try {
|
|
7371
|
+
st = await fs$2.stat(full);
|
|
7372
|
+
} catch {
|
|
7373
|
+
continue;
|
|
7374
|
+
}
|
|
7375
|
+
if (!st?.isFile?.()) continue;
|
|
7376
|
+
const mtimeMs = Number(st.mtimeMs || 0);
|
|
7377
|
+
if (!best || mtimeMs > best.mtimeMs) best = { path: full, mtimeMs };
|
|
7378
|
+
}
|
|
7379
|
+
return best?.path ?? null;
|
|
7380
|
+
} catch {
|
|
7381
|
+
return null;
|
|
7382
|
+
}
|
|
7383
|
+
};
|
|
7384
|
+
const findLatestCrashDir = async (projectRoot) => {
|
|
7385
|
+
const crashesDir = path.join(projectRoot, "Saved", "Crashes");
|
|
7386
|
+
try {
|
|
7387
|
+
const entries = await fs$2.readdir(crashesDir);
|
|
7388
|
+
let best = null;
|
|
7389
|
+
for (const name of entries) {
|
|
7390
|
+
const full = path.join(crashesDir, name);
|
|
7391
|
+
let st;
|
|
7392
|
+
try {
|
|
7393
|
+
st = await fs$2.stat(full);
|
|
7394
|
+
} catch {
|
|
7395
|
+
continue;
|
|
7396
|
+
}
|
|
7397
|
+
if (!st?.isDirectory?.()) continue;
|
|
7398
|
+
const mtimeMs = Number(st.mtimeMs || 0);
|
|
7399
|
+
if (!best || mtimeMs > best.mtimeMs) best = { path: full, mtimeMs };
|
|
7400
|
+
}
|
|
7401
|
+
return best?.path ?? null;
|
|
7402
|
+
} catch {
|
|
7403
|
+
return null;
|
|
7404
|
+
}
|
|
7405
|
+
};
|
|
7406
|
+
const readFileTail = async (filePath, maxBytes) => {
|
|
7407
|
+
try {
|
|
7408
|
+
const buf = await fs$2.readFile(filePath);
|
|
7409
|
+
const slice = buf.length > maxBytes ? buf.subarray(buf.length - maxBytes) : buf;
|
|
7410
|
+
return slice.toString("utf8");
|
|
7411
|
+
} catch {
|
|
7412
|
+
return null;
|
|
7413
|
+
}
|
|
7414
|
+
};
|
|
7415
|
+
const gatherCrashDiagnosticsBestEffort = async (uprojectPath) => {
|
|
7416
|
+
const projectRoot = path.dirname(uprojectPath);
|
|
7417
|
+
const summary = [];
|
|
7418
|
+
const detail = { uprojectPath, projectRoot };
|
|
7419
|
+
const latestLog = await findLatestFile(path.join(projectRoot, "Saved", "Logs"), (n) => n.toLowerCase().endsWith(".log"));
|
|
7420
|
+
if (latestLog) {
|
|
7421
|
+
detail.projectLogPath = latestLog;
|
|
7422
|
+
const tail = await readFileTail(latestLog, 24e3);
|
|
7423
|
+
if (tail) {
|
|
7424
|
+
detail.projectLogTail = tailText(tail, 12e3);
|
|
7425
|
+
summary.push(`Latest log: ${latestLog}`);
|
|
7426
|
+
}
|
|
7427
|
+
}
|
|
7428
|
+
const latestCrashDir = await findLatestCrashDir(projectRoot);
|
|
7429
|
+
if (latestCrashDir) {
|
|
7430
|
+
detail.latestCrashDir = latestCrashDir;
|
|
7431
|
+
const crashContext = await findLatestFile(latestCrashDir, (n) => n.toLowerCase().includes("crashcontext") && n.toLowerCase().endsWith(".xml"));
|
|
7432
|
+
if (crashContext) {
|
|
7433
|
+
detail.crashContextPath = crashContext;
|
|
7434
|
+
const tail = await readFileTail(crashContext, 24e3);
|
|
7435
|
+
if (tail) {
|
|
7436
|
+
detail.crashContextTail = tailText(tail, 12e3);
|
|
7437
|
+
summary.push(`CrashContext: ${crashContext}`);
|
|
7438
|
+
}
|
|
7439
|
+
}
|
|
7440
|
+
}
|
|
7441
|
+
return { detail, summary };
|
|
7442
|
+
};
|
|
7258
7443
|
const getUnrealEditorExe = (engineRoot) => {
|
|
7259
7444
|
const root = engineRoot.trim().replace(/[\\/]+$/, "");
|
|
7260
7445
|
if (process.platform === "darwin") {
|
|
@@ -7310,7 +7495,7 @@ ${res.stderr}`;
|
|
|
7310
7495
|
kind: "unreachable",
|
|
7311
7496
|
severity: "warning",
|
|
7312
7497
|
detectedAtMs: now,
|
|
7313
|
-
message: "Unreal Editor is no longer reachable (it was reachable earlier). It may have crashed or been closed.",
|
|
7498
|
+
message: "Unreal Editor is no longer reachable (it was reachable earlier). It may have crashed or been closed.\nNext: fix the issue, then relaunch via unreal_editor_relaunch_last (if you launched from this session) or unreal_editor_launch.",
|
|
7314
7499
|
detail: {
|
|
7315
7500
|
lastReachableAtMs: state.lastReachableAtMs
|
|
7316
7501
|
}
|
|
@@ -7342,20 +7527,35 @@ ${res.stderr}`;
|
|
|
7342
7527
|
engineRoot,
|
|
7343
7528
|
startedAtMs: Date.now()
|
|
7344
7529
|
};
|
|
7530
|
+
state.lastLaunch = {
|
|
7531
|
+
uprojectPath,
|
|
7532
|
+
engineRoot,
|
|
7533
|
+
extraArgs,
|
|
7534
|
+
startedAtMs: Date.now()
|
|
7535
|
+
};
|
|
7345
7536
|
child.on("exit", (code, signal) => {
|
|
7346
|
-
|
|
7347
|
-
|
|
7348
|
-
|
|
7349
|
-
|
|
7350
|
-
|
|
7351
|
-
|
|
7352
|
-
|
|
7353
|
-
|
|
7354
|
-
|
|
7355
|
-
|
|
7356
|
-
|
|
7357
|
-
|
|
7358
|
-
|
|
7537
|
+
void (async () => {
|
|
7538
|
+
const now = Date.now();
|
|
7539
|
+
const exitCode = typeof code === "number" ? code : null;
|
|
7540
|
+
const sig = typeof signal === "string" ? signal : null;
|
|
7541
|
+
const isCrash = sig !== null || exitCode !== null && exitCode !== 0;
|
|
7542
|
+
state.launched = null;
|
|
7543
|
+
if (!isCrash) return;
|
|
7544
|
+
const diag = await gatherCrashDiagnosticsBestEffort(uprojectPath).catch(() => ({ detail: {}, summary: [] }));
|
|
7545
|
+
const msgParts = [
|
|
7546
|
+
`Unreal Editor process exited unexpectedly (code=${exitCode ?? "null"} signal=${sig ?? "null"}).`,
|
|
7547
|
+
`Project: ${uprojectPath}`,
|
|
7548
|
+
...diag.summary,
|
|
7549
|
+
`Next: fix the issue, then relaunch via unreal_editor_relaunch_last (or unreal_editor_launch).`
|
|
7550
|
+
];
|
|
7551
|
+
emitIssue({
|
|
7552
|
+
kind: "process_exit",
|
|
7553
|
+
severity: "crash",
|
|
7554
|
+
detectedAtMs: now,
|
|
7555
|
+
message: msgParts.filter(Boolean).join("\n"),
|
|
7556
|
+
detail: { exitCode, signal: sig, pid, uprojectPath, ...diag.detail }
|
|
7557
|
+
});
|
|
7558
|
+
})();
|
|
7359
7559
|
});
|
|
7360
7560
|
child.on("error", (err) => {
|
|
7361
7561
|
const now = Date.now();
|
|
@@ -7364,7 +7564,9 @@ ${res.stderr}`;
|
|
|
7364
7564
|
kind: "process_exit",
|
|
7365
7565
|
severity: "crash",
|
|
7366
7566
|
detectedAtMs: now,
|
|
7367
|
-
message: `Failed to launch Unreal Editor: ${err instanceof Error ? err.message : String(err)}
|
|
7567
|
+
message: `Failed to launch Unreal Editor: ${err instanceof Error ? err.message : String(err)}
|
|
7568
|
+
Project: ${uprojectPath}
|
|
7569
|
+
Next: fix the issue, then relaunch via unreal_editor_relaunch_last (or unreal_editor_launch).`,
|
|
7368
7570
|
detail: { pid, uprojectPath }
|
|
7369
7571
|
});
|
|
7370
7572
|
});
|
|
@@ -7374,6 +7576,12 @@ ${res.stderr}`;
|
|
|
7374
7576
|
noteUnrealActivity,
|
|
7375
7577
|
noteUnrealReachable,
|
|
7376
7578
|
launchEditor,
|
|
7579
|
+
relaunchLast: async (extraArgs) => {
|
|
7580
|
+
const last = state.lastLaunch;
|
|
7581
|
+
if (!last) throw new Error("No known prior Unreal launch in this session. Use unreal_editor_launch with an explicit uprojectPath.");
|
|
7582
|
+
const mergedArgs = Array.isArray(extraArgs) && extraArgs.length > 0 ? extraArgs.filter((a) => typeof a === "string" && a.trim()) : last.extraArgs;
|
|
7583
|
+
return launchEditor({ uprojectPath: last.uprojectPath, engineRoot: last.engineRoot, extraArgs: mergedArgs });
|
|
7584
|
+
},
|
|
7377
7585
|
stop: () => {
|
|
7378
7586
|
try {
|
|
7379
7587
|
interval.unref();
|
|
@@ -8576,13 +8784,13 @@ ${String(st.stdout || "").trim()}`
|
|
|
8576
8784
|
"read_images",
|
|
8577
8785
|
{
|
|
8578
8786
|
title: "Read Images",
|
|
8579
|
-
description: "Read one or more local
|
|
8787
|
+
description: "Read one or more local images by path (PNG/JPEG) and return a `{ views: [...] }` payload so the app can render them as a gallery.",
|
|
8580
8788
|
inputSchema: {
|
|
8581
|
-
paths: z.z.array(z.z.string()).describe("Image paths (absolute or relative to the session directory). PNG only."),
|
|
8789
|
+
paths: z.z.array(z.z.string()).describe("Image paths (absolute or relative to the session directory). PNG/JPG only."),
|
|
8582
8790
|
limit: z.z.number().int().positive().optional().describe("Max number of images to include (default 10)."),
|
|
8583
8791
|
upload: z.z.boolean().optional().describe("Upload images to the session screenshots store and return HTTPS URLs (default true)."),
|
|
8584
8792
|
includeBase64: z.z.boolean().optional().describe("Include base64 data in the payload (default false)."),
|
|
8585
|
-
includeToolImages: z.z.boolean().optional().describe("Include MCP image blocks so vision models can see the
|
|
8793
|
+
includeToolImages: z.z.boolean().optional().describe("Include MCP image blocks so vision models can see the images (default false)."),
|
|
8586
8794
|
maxBytesPerImage: z.z.number().int().positive().optional().describe("Max bytes per image when includeBase64=true (default 2500000).")
|
|
8587
8795
|
}
|
|
8588
8796
|
},
|
|
@@ -8614,14 +8822,16 @@ ${String(st.stdout || "").trim()}`
|
|
|
8614
8822
|
const home = process.env.HOME || process.env.USERPROFILE || "";
|
|
8615
8823
|
if (home) return path.join(home, p.slice(2));
|
|
8616
8824
|
}
|
|
8617
|
-
|
|
8825
|
+
const baseDir = readSessionWorkingDirectory();
|
|
8826
|
+
return path.isAbsolute(p) ? p : path.resolve(baseDir, p);
|
|
8618
8827
|
};
|
|
8619
8828
|
const idsSeen = /* @__PURE__ */ new Set();
|
|
8620
8829
|
const viewsLocal = [];
|
|
8621
8830
|
for (const inputPath of paths) {
|
|
8622
8831
|
const abs = resolvePath(inputPath);
|
|
8623
|
-
|
|
8624
|
-
|
|
8832
|
+
const lower = abs.toLowerCase();
|
|
8833
|
+
if (!lower.endsWith(".png") && !lower.endsWith(".jpg") && !lower.endsWith(".jpeg")) {
|
|
8834
|
+
throw new Error(`Only PNG/JPG images are supported: ${abs}`);
|
|
8625
8835
|
}
|
|
8626
8836
|
if (!fs.existsSync(abs)) {
|
|
8627
8837
|
throw new Error(`Image not found: ${abs}`);
|
|
@@ -8641,7 +8851,11 @@ ${String(st.stdout || "").trim()}`
|
|
|
8641
8851
|
throw new Error(`Image too large (${st.size} bytes) to embed: ${abs}`);
|
|
8642
8852
|
}
|
|
8643
8853
|
const buf = await fs$2.readFile(abs);
|
|
8644
|
-
|
|
8854
|
+
const mime = detectImageMimeTypeFromBuffer(buf);
|
|
8855
|
+
if (!mime) {
|
|
8856
|
+
throw new Error(`Unsupported image format (expected PNG/JPEG): ${abs}`);
|
|
8857
|
+
}
|
|
8858
|
+
viewsLocal.push({ id: unique, path: abs, base64: buf.toString("base64"), mimeType: mime });
|
|
8645
8859
|
} else {
|
|
8646
8860
|
viewsLocal.push({ id: unique, path: abs });
|
|
8647
8861
|
}
|
|
@@ -8666,7 +8880,7 @@ ${String(st.stdout || "").trim()}`
|
|
|
8666
8880
|
viewsLocal.forEach((v, idx) => {
|
|
8667
8881
|
if (!v.base64) return;
|
|
8668
8882
|
content.push({ type: "text", text: `Image ${idx + 1}: ${v.id}` });
|
|
8669
|
-
content.push({ type: "image", data: v.base64, mimeType: "image/png" });
|
|
8883
|
+
content.push({ type: "image", data: v.base64, mimeType: v.mimeType || "image/png" });
|
|
8670
8884
|
});
|
|
8671
8885
|
}
|
|
8672
8886
|
return {
|
|
@@ -8678,7 +8892,7 @@ ${String(st.stdout || "").trim()}`
|
|
|
8678
8892
|
return {
|
|
8679
8893
|
content: [
|
|
8680
8894
|
{ type: "text", text: `Failed to read images: ${error instanceof Error ? error.message : String(error)}` },
|
|
8681
|
-
{ type: "text", text: `Tip: pass absolute PNG paths, or relative paths from the session folder.` }
|
|
8895
|
+
{ type: "text", text: `Tip: pass absolute PNG/JPG paths, or relative paths from the session folder.` }
|
|
8682
8896
|
],
|
|
8683
8897
|
isError: true
|
|
8684
8898
|
};
|
|
@@ -8880,6 +9094,32 @@ ${String(st.stdout || "").trim()}`
|
|
|
8880
9094
|
isError: false
|
|
8881
9095
|
};
|
|
8882
9096
|
}));
|
|
9097
|
+
mcp.registerTool("unreal_editor_relaunch_last", {
|
|
9098
|
+
title: "Unreal Editor: Relaunch Last Project",
|
|
9099
|
+
description: "Relaunch the last Unreal project previously launched via unreal_editor_launch in this session (no auto-restart). Use this after a crash once you\u2019ve fixed files. If it crashes again, Flockbay will abort and report again.",
|
|
9100
|
+
inputSchema: {
|
|
9101
|
+
extraArgs: z.z.array(z.z.string()).optional().describe("Optional replacement UnrealEditor command-line args (advanced).")
|
|
9102
|
+
}
|
|
9103
|
+
}, async (args) => runWithMcpToolCard("unreal_editor_relaunch_last", args, async () => {
|
|
9104
|
+
const extraArgs = Array.isArray(args?.extraArgs) ? args.extraArgs : void 0;
|
|
9105
|
+
unrealEditorSupervisor.noteUnrealActivity();
|
|
9106
|
+
try {
|
|
9107
|
+
const launched = await unrealEditorSupervisor.relaunchLast(extraArgs);
|
|
9108
|
+
return {
|
|
9109
|
+
content: [
|
|
9110
|
+
{ type: "text", text: `Relaunched Unreal Editor (last project).` },
|
|
9111
|
+
{ type: "text", text: JSON.stringify({ pid: launched.pid, exePath: launched.exePath }, null, 2) },
|
|
9112
|
+
{ type: "text", text: "Next: wait for the editor to finish loading, then re-run UnrealMCP tools." }
|
|
9113
|
+
],
|
|
9114
|
+
isError: false
|
|
9115
|
+
};
|
|
9116
|
+
} catch (err) {
|
|
9117
|
+
return {
|
|
9118
|
+
content: [{ type: "text", text: err instanceof Error ? err.message : String(err) }],
|
|
9119
|
+
isError: true
|
|
9120
|
+
};
|
|
9121
|
+
}
|
|
9122
|
+
}));
|
|
8883
9123
|
mcp.registerTool("unreal_build_project", {
|
|
8884
9124
|
title: "Unreal Build Project (UBT)",
|
|
8885
9125
|
description: "Build the project via Unreal Build Tool (via Engine/Build/BatchFiles/Build.*). Returns structured errors (file/line) and a log path for deep debugging.",
|
|
@@ -10305,6 +10545,7 @@ Fix: ${res.hint}` : "";
|
|
|
10305
10545
|
"unreal_headless_screenshot",
|
|
10306
10546
|
"unreal_latest_screenshots",
|
|
10307
10547
|
"unreal_editor_launch",
|
|
10548
|
+
"unreal_editor_relaunch_last",
|
|
10308
10549
|
"unreal_build_project",
|
|
10309
10550
|
"unreal_mcp_command",
|
|
10310
10551
|
"unreal_mcp_list_capabilities",
|
|
@@ -12086,7 +12327,7 @@ ${engineRoot}`, {
|
|
|
12086
12327
|
} else if (subcommand === "codex") {
|
|
12087
12328
|
try {
|
|
12088
12329
|
await chdirToNearestUprojectRootIfPresent();
|
|
12089
|
-
const { runCodex } = await Promise.resolve().then(function () { return require('./runCodex-
|
|
12330
|
+
const { runCodex } = await Promise.resolve().then(function () { return require('./runCodex-CH4lz1QX.cjs'); });
|
|
12090
12331
|
let startedBy = void 0;
|
|
12091
12332
|
let sessionId = void 0;
|
|
12092
12333
|
for (let i = 1; i < args.length; i++) {
|
|
@@ -12181,7 +12422,7 @@ ${engineRoot}`, {
|
|
|
12181
12422
|
}
|
|
12182
12423
|
try {
|
|
12183
12424
|
await chdirToNearestUprojectRootIfPresent();
|
|
12184
|
-
const { runGemini } = await Promise.resolve().then(function () { return require('./runGemini-
|
|
12425
|
+
const { runGemini } = await Promise.resolve().then(function () { return require('./runGemini-DNSymY04.cjs'); });
|
|
12185
12426
|
let startedBy = void 0;
|
|
12186
12427
|
let sessionId = void 0;
|
|
12187
12428
|
for (let i = 1; i < args.length; i++) {
|