flockbay 0.10.20 → 0.10.22
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/{index-CX0Z8pmz.mjs → index-BjZUYSzh.mjs} +471 -31
- package/dist/{index-D_mglYG0.cjs → index-DQTqwzYd.cjs} +471 -31
- 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-Biis9GFw.mjs → runCodex-B7fGICdv.mjs} +11 -13
- package/dist/{runCodex-CXJW0tzo.cjs → runCodex-CaWagdzG.cjs} +11 -13
- package/dist/{runGemini-FOBXtEU6.cjs → runGemini-8w5P093W.cjs} +234 -49
- package/dist/{runGemini-BSH4b0wu.mjs → runGemini-CK43WQk8.mjs} +234 -49
- package/dist/{types-C4QeUggl.mjs → types-CMWcip0F.mjs} +41 -3
- package/dist/{types-BYHCKlu_.cjs → types-Z2OYpI8c.cjs} +41 -2
- 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/UnrealMCPEditorCommands.cpp +32 -11
- package/tools/unreal-mcp/upstream/MCPGameProject/Plugins/UnrealMCP/Source/UnrealMCP/UnrealMCP.Build.cs +1 -0
|
@@ -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-Z2OYpI8c.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');
|
|
@@ -408,7 +408,7 @@ const PLATFORM_SYSTEM_PROMPT = trimIdent(`
|
|
|
408
408
|
|
|
409
409
|
# Policy blocks (not user rejections)
|
|
410
410
|
|
|
411
|
-
If a tool call is **blocked by
|
|
411
|
+
If a tool call is **blocked by Policy** (e.g. a \`FlockbayPolicy\` card, or a denial reason like \u201CBlocked by Policy \u2026\u201D), this is automatic enforcement by the platform \u2014 it is **not** the user rejecting your tool call. Follow the provided next-step instructions (read docs/ledger, claim files, etc) and then retry.
|
|
412
412
|
|
|
413
413
|
# Documentation Library (server-stored docs)
|
|
414
414
|
|
|
@@ -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-DQTqwzYd.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();
|
|
@@ -2488,7 +2545,7 @@ class PermissionHandler {
|
|
|
2488
2545
|
const decision = args.decision;
|
|
2489
2546
|
const reason = args.reason;
|
|
2490
2547
|
const kind = decision === "approved" || decision === "approved_for_session" ? "policy_allow" : decision === "abort" && reason === "permission_prompt_required" ? "policy_prompt" : "policy_block";
|
|
2491
|
-
const summary = kind === "policy_allow" ? "Allowed." : kind === "policy_prompt" ? "Waiting for permission to run this tool." : reason ? `Blocked: ${reason}` : "Blocked by
|
|
2548
|
+
const summary = kind === "policy_allow" ? "Allowed." : kind === "policy_prompt" ? "Waiting for permission to run this tool." : reason ? `Blocked: ${reason}` : "Blocked by Policy.";
|
|
2492
2549
|
const callId = `policy:${args.toolCallId}:${node_crypto.randomUUID().slice(0, 8)}`;
|
|
2493
2550
|
const payload = {
|
|
2494
2551
|
kind,
|
|
@@ -2553,13 +2610,13 @@ class PermissionHandler {
|
|
|
2553
2610
|
if (args.reason === "docs_index_read_required") {
|
|
2554
2611
|
return {
|
|
2555
2612
|
uiReason: "read the game Documentation index before making edits.",
|
|
2556
|
-
modelMessage: "Blocked by
|
|
2613
|
+
modelMessage: "Blocked by Policy: read the game Documentation index before making edits.\nNext: call `mcp__flockbay__docs_index_read`, then retry the edit."
|
|
2557
2614
|
};
|
|
2558
2615
|
}
|
|
2559
2616
|
if (args.reason === "ledger_read_required") {
|
|
2560
2617
|
return {
|
|
2561
2618
|
uiReason: "read the ledger before making file edits.",
|
|
2562
|
-
modelMessage: "Blocked by
|
|
2619
|
+
modelMessage: "Blocked by Policy: read the ledger before making file edits.\nNext: call `mcp__flockbay__ledger_read` (or `mcp__flockbay__coordination_ledger_snapshot`), then retry the edit."
|
|
2563
2620
|
};
|
|
2564
2621
|
}
|
|
2565
2622
|
if (args.reason === "file_claim_required") {
|
|
@@ -2567,13 +2624,13 @@ class PermissionHandler {
|
|
|
2567
2624
|
const next = file ? `Next: claim it via \`mcp__flockbay__ledger_claim\` (files: ["${file}"]) or \`mcp__flockbay__coordination_claim_files\`, then retry the edit.` : "Next: claim the file via `mcp__flockbay__ledger_claim` (or `mcp__flockbay__coordination_claim_files`), then retry the edit.";
|
|
2568
2625
|
return {
|
|
2569
2626
|
uiReason: display,
|
|
2570
|
-
modelMessage: `Blocked by
|
|
2627
|
+
modelMessage: `Blocked by Policy: ${display}
|
|
2571
2628
|
${next}`
|
|
2572
2629
|
};
|
|
2573
2630
|
}
|
|
2574
2631
|
return {
|
|
2575
2632
|
uiReason: "this session is in read-only mode.",
|
|
2576
|
-
modelMessage: "Blocked by
|
|
2633
|
+
modelMessage: "Blocked by Policy: this session is in read-only mode.\nNext: switch permission mode to allow edits, then retry."
|
|
2577
2634
|
};
|
|
2578
2635
|
}
|
|
2579
2636
|
enforceCoordinationGate(toolName, input) {
|
|
@@ -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$1(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,64 @@ async function uploadScreenshotViewsForSession(args) {
|
|
|
7158
7239
|
}
|
|
7159
7240
|
return out;
|
|
7160
7241
|
}
|
|
7242
|
+
function detectImageMimeTypeFromBuffer$1(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
|
+
}
|
|
7258
|
+
async function readFileHeader(filePath, bytes) {
|
|
7259
|
+
const fh = await fs$2.open(filePath, "r");
|
|
7260
|
+
try {
|
|
7261
|
+
const maxBytes = Math.max(1, Math.floor(bytes));
|
|
7262
|
+
const buf = Buffer.allocUnsafe(maxBytes);
|
|
7263
|
+
const { bytesRead } = await fh.read(buf, 0, maxBytes, 0);
|
|
7264
|
+
return buf.subarray(0, bytesRead);
|
|
7265
|
+
} finally {
|
|
7266
|
+
try {
|
|
7267
|
+
await fh.close();
|
|
7268
|
+
} catch {
|
|
7269
|
+
}
|
|
7270
|
+
}
|
|
7271
|
+
}
|
|
7272
|
+
async function normalizeScreenshotPathExtensionToMatchBytes(filePath) {
|
|
7273
|
+
const abs = String(filePath || "").trim();
|
|
7274
|
+
if (!abs) return { path: abs, changed: false };
|
|
7275
|
+
if (!fs.existsSync(abs)) return { path: abs, changed: false };
|
|
7276
|
+
const lower = abs.toLowerCase();
|
|
7277
|
+
const header = await readFileHeader(abs, 16);
|
|
7278
|
+
const mime = detectImageMimeTypeFromBuffer$1(header);
|
|
7279
|
+
if (!mime) {
|
|
7280
|
+
return { path: abs, changed: false, detail: "unknown_image_format" };
|
|
7281
|
+
}
|
|
7282
|
+
if (mime === "image/jpeg" && lower.endsWith(".png")) {
|
|
7283
|
+
const next = abs.replace(/\.png$/i, ".jpg");
|
|
7284
|
+
if (fs.existsSync(next)) {
|
|
7285
|
+
throw new Error(`Screenshot already exists at normalized path: ${next}`);
|
|
7286
|
+
}
|
|
7287
|
+
await fs$2.rename(abs, next);
|
|
7288
|
+
return { path: next, changed: true, detail: "renamed_png_to_jpg" };
|
|
7289
|
+
}
|
|
7290
|
+
if (mime === "image/png" && (lower.endsWith(".jpg") || lower.endsWith(".jpeg"))) {
|
|
7291
|
+
const next = abs.replace(/\.(jpg|jpeg)$/i, ".png");
|
|
7292
|
+
if (fs.existsSync(next)) {
|
|
7293
|
+
throw new Error(`Screenshot already exists at normalized path: ${next}`);
|
|
7294
|
+
}
|
|
7295
|
+
await fs$2.rename(abs, next);
|
|
7296
|
+
return { path: next, changed: true, detail: "renamed_jpg_to_png" };
|
|
7297
|
+
}
|
|
7298
|
+
return { path: abs, changed: false };
|
|
7299
|
+
}
|
|
7161
7300
|
async function startFlockbayServer(client, options) {
|
|
7162
7301
|
const handler = async (title) => {
|
|
7163
7302
|
types.logger.debug("[flockbayMCP] Changing title to:", title);
|
|
@@ -8687,13 +8826,13 @@ ${String(st.stdout || "").trim()}`
|
|
|
8687
8826
|
"read_images",
|
|
8688
8827
|
{
|
|
8689
8828
|
title: "Read Images",
|
|
8690
|
-
description: "Read one or more local
|
|
8829
|
+
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.",
|
|
8691
8830
|
inputSchema: {
|
|
8692
|
-
paths: z.z.array(z.z.string()).describe("Image paths (absolute or relative to the session directory). PNG only."),
|
|
8831
|
+
paths: z.z.array(z.z.string()).describe("Image paths (absolute or relative to the session directory). PNG/JPG only."),
|
|
8693
8832
|
limit: z.z.number().int().positive().optional().describe("Max number of images to include (default 10)."),
|
|
8694
8833
|
upload: z.z.boolean().optional().describe("Upload images to the session screenshots store and return HTTPS URLs (default true)."),
|
|
8695
8834
|
includeBase64: z.z.boolean().optional().describe("Include base64 data in the payload (default false)."),
|
|
8696
|
-
includeToolImages: z.z.boolean().optional().describe("Include MCP image blocks so vision models can see the
|
|
8835
|
+
includeToolImages: z.z.boolean().optional().describe("Include MCP image blocks so vision models can see the images (default false)."),
|
|
8697
8836
|
maxBytesPerImage: z.z.number().int().positive().optional().describe("Max bytes per image when includeBase64=true (default 2500000).")
|
|
8698
8837
|
}
|
|
8699
8838
|
},
|
|
@@ -8725,14 +8864,16 @@ ${String(st.stdout || "").trim()}`
|
|
|
8725
8864
|
const home = process.env.HOME || process.env.USERPROFILE || "";
|
|
8726
8865
|
if (home) return path.join(home, p.slice(2));
|
|
8727
8866
|
}
|
|
8728
|
-
|
|
8867
|
+
const baseDir = readSessionWorkingDirectory();
|
|
8868
|
+
return path.isAbsolute(p) ? p : path.resolve(baseDir, p);
|
|
8729
8869
|
};
|
|
8730
8870
|
const idsSeen = /* @__PURE__ */ new Set();
|
|
8731
8871
|
const viewsLocal = [];
|
|
8732
8872
|
for (const inputPath of paths) {
|
|
8733
8873
|
const abs = resolvePath(inputPath);
|
|
8734
|
-
|
|
8735
|
-
|
|
8874
|
+
const lower = abs.toLowerCase();
|
|
8875
|
+
if (!lower.endsWith(".png") && !lower.endsWith(".jpg") && !lower.endsWith(".jpeg")) {
|
|
8876
|
+
throw new Error(`Only PNG/JPG images are supported: ${abs}`);
|
|
8736
8877
|
}
|
|
8737
8878
|
if (!fs.existsSync(abs)) {
|
|
8738
8879
|
throw new Error(`Image not found: ${abs}`);
|
|
@@ -8752,7 +8893,11 @@ ${String(st.stdout || "").trim()}`
|
|
|
8752
8893
|
throw new Error(`Image too large (${st.size} bytes) to embed: ${abs}`);
|
|
8753
8894
|
}
|
|
8754
8895
|
const buf = await fs$2.readFile(abs);
|
|
8755
|
-
|
|
8896
|
+
const mime = detectImageMimeTypeFromBuffer$1(buf);
|
|
8897
|
+
if (!mime) {
|
|
8898
|
+
throw new Error(`Unsupported image format (expected PNG/JPEG): ${abs}`);
|
|
8899
|
+
}
|
|
8900
|
+
viewsLocal.push({ id: unique, path: abs, base64: buf.toString("base64"), mimeType: mime });
|
|
8756
8901
|
} else {
|
|
8757
8902
|
viewsLocal.push({ id: unique, path: abs });
|
|
8758
8903
|
}
|
|
@@ -8777,7 +8922,7 @@ ${String(st.stdout || "").trim()}`
|
|
|
8777
8922
|
viewsLocal.forEach((v, idx) => {
|
|
8778
8923
|
if (!v.base64) return;
|
|
8779
8924
|
content.push({ type: "text", text: `Image ${idx + 1}: ${v.id}` });
|
|
8780
|
-
content.push({ type: "image", data: v.base64, mimeType: "image/png" });
|
|
8925
|
+
content.push({ type: "image", data: v.base64, mimeType: v.mimeType || "image/png" });
|
|
8781
8926
|
});
|
|
8782
8927
|
}
|
|
8783
8928
|
return {
|
|
@@ -8789,7 +8934,7 @@ ${String(st.stdout || "").trim()}`
|
|
|
8789
8934
|
return {
|
|
8790
8935
|
content: [
|
|
8791
8936
|
{ type: "text", text: `Failed to read images: ${error instanceof Error ? error.message : String(error)}` },
|
|
8792
|
-
{ type: "text", text: `Tip: pass absolute PNG paths, or relative paths from the session folder.` }
|
|
8937
|
+
{ type: "text", text: `Tip: pass absolute PNG/JPG paths, or relative paths from the session folder.` }
|
|
8793
8938
|
],
|
|
8794
8939
|
isError: true
|
|
8795
8940
|
};
|
|
@@ -8798,7 +8943,7 @@ ${String(st.stdout || "").trim()}`
|
|
|
8798
8943
|
);
|
|
8799
8944
|
mcp.registerTool("unreal_latest_screenshots", {
|
|
8800
8945
|
title: "Latest Unreal Screenshots (Validation)",
|
|
8801
|
-
description: "Fetch the latest PNG
|
|
8946
|
+
description: "Fetch the latest screenshots (PNG/JPG) from `Saved/Screenshots/Flockbay/` (for validation) and return a `{ views: [...] }` payload so the app can display them.",
|
|
8802
8947
|
inputSchema: {
|
|
8803
8948
|
uprojectPath: z.z.string().describe("Absolute path to the .uproject file."),
|
|
8804
8949
|
limit: z.z.number().int().positive().optional().describe("Max number of screenshots to return (default 12)."),
|
|
@@ -8835,7 +8980,7 @@ ${String(st.stdout || "").trim()}`
|
|
|
8835
8980
|
};
|
|
8836
8981
|
}
|
|
8837
8982
|
const files = await fs$2.readdir(outDir);
|
|
8838
|
-
const candidates = files.filter((f) =>
|
|
8983
|
+
const candidates = files.filter((f) => /\.(png|jpg|jpeg)$/i.test(f));
|
|
8839
8984
|
if (candidates.length === 0) {
|
|
8840
8985
|
return {
|
|
8841
8986
|
content: [
|
|
@@ -9090,10 +9235,26 @@ ${String(st.stdout || "").trim()}`
|
|
|
9090
9235
|
const pluginInfoWasCached = Boolean(unrealMcpPluginInfoCache);
|
|
9091
9236
|
const pluginInfo = type !== "get_plugin_info" ? await getUnrealMcpPluginInfoBestEffort(timeoutMs) : null;
|
|
9092
9237
|
const response = await types.sendUnrealMcpTcpCommand({ type, params, timeoutMs });
|
|
9238
|
+
let screenshotNormalizationNote = null;
|
|
9239
|
+
if (type === "take_screenshot") {
|
|
9240
|
+
const responseObj = response && typeof response === "object" ? response : null;
|
|
9241
|
+
const candidate = responseObj && typeof responseObj.filepath === "string" ? responseObj : responseObj && responseObj.result && typeof responseObj.result === "object" && typeof responseObj.result.filepath === "string" ? responseObj.result : null;
|
|
9242
|
+
if (candidate && typeof candidate.filepath === "string") {
|
|
9243
|
+
const before = String(candidate.filepath || "").trim();
|
|
9244
|
+
if (before) {
|
|
9245
|
+
const normalized = await normalizeScreenshotPathExtensionToMatchBytes(before);
|
|
9246
|
+
if (normalized.changed) {
|
|
9247
|
+
candidate.filepath = normalized.path;
|
|
9248
|
+
screenshotNormalizationNote = `Normalized screenshot path (${normalized.detail || "extension_fixed"}): ${before} \u2192 ${normalized.path}`;
|
|
9249
|
+
}
|
|
9250
|
+
}
|
|
9251
|
+
}
|
|
9252
|
+
}
|
|
9093
9253
|
unrealEditorSupervisor.noteUnrealReachable();
|
|
9094
9254
|
return {
|
|
9095
9255
|
content: [
|
|
9096
9256
|
{ type: "text", text: `UnrealMCP command ok: ${type}` },
|
|
9257
|
+
...screenshotNormalizationNote ? [{ type: "text", text: screenshotNormalizationNote }] : [],
|
|
9097
9258
|
{ type: "text", text: JSON.stringify(response, null, 2) },
|
|
9098
9259
|
...pluginInfo && !pluginInfoWasCached ? [{ type: "text", text: formatUnrealMcpCapabilities(pluginInfo) }] : []
|
|
9099
9260
|
],
|
|
@@ -11753,6 +11914,269 @@ async function handleConnectVendor(vendor, displayName, flags) {
|
|
|
11753
11914
|
}
|
|
11754
11915
|
}
|
|
11755
11916
|
|
|
11917
|
+
function readArgValue$1(args, key) {
|
|
11918
|
+
const idx = args.indexOf(key);
|
|
11919
|
+
if (idx === -1) return null;
|
|
11920
|
+
const value = args[idx + 1];
|
|
11921
|
+
if (!value || value.startsWith("-")) return null;
|
|
11922
|
+
return value;
|
|
11923
|
+
}
|
|
11924
|
+
function splitList(value) {
|
|
11925
|
+
if (!value) return [];
|
|
11926
|
+
return value.split(/[;,]/g).map((v) => v.trim()).filter(Boolean);
|
|
11927
|
+
}
|
|
11928
|
+
function ensureDir(dir) {
|
|
11929
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
11930
|
+
}
|
|
11931
|
+
function detectImageMimeTypeFromBuffer(buf) {
|
|
11932
|
+
if (!buf || buf.length < 12) return null;
|
|
11933
|
+
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) {
|
|
11934
|
+
return "image/png";
|
|
11935
|
+
}
|
|
11936
|
+
if (buf[0] === 255 && buf[1] === 216 && buf[2] === 255) return "image/jpeg";
|
|
11937
|
+
if (buf[0] === 71 && buf[1] === 73 && buf[2] === 70 && buf[3] === 56) return "image/gif";
|
|
11938
|
+
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) {
|
|
11939
|
+
return "image/webp";
|
|
11940
|
+
}
|
|
11941
|
+
return null;
|
|
11942
|
+
}
|
|
11943
|
+
async function waitForUnreal(options) {
|
|
11944
|
+
const startedAt = Date.now();
|
|
11945
|
+
let lastErr = null;
|
|
11946
|
+
while (Date.now() - startedAt < options.timeoutMs) {
|
|
11947
|
+
try {
|
|
11948
|
+
const res = await types.sendUnrealMcpTcpCommand({ type: "ping", host: options.host, port: options.port, timeoutMs: 2e3 });
|
|
11949
|
+
const msg = typeof res?.message === "string" ? res.message : null;
|
|
11950
|
+
if (msg === "pong") return { ok: true };
|
|
11951
|
+
} catch (err) {
|
|
11952
|
+
lastErr = err instanceof Error ? err.message : String(err);
|
|
11953
|
+
}
|
|
11954
|
+
await new Promise((r) => setTimeout(r, 750));
|
|
11955
|
+
}
|
|
11956
|
+
return {
|
|
11957
|
+
ok: false,
|
|
11958
|
+
error: `Timed out waiting for UnrealMCP ping after ${options.timeoutMs}ms.${lastErr ? ` Last error: ${lastErr}` : ""}`
|
|
11959
|
+
};
|
|
11960
|
+
}
|
|
11961
|
+
function resolveUnrealEditorExe(engineRoot) {
|
|
11962
|
+
const exe = process.platform === "win32" ? path.join(engineRoot, "Engine", "Binaries", "Win64", "UnrealEditor.exe") : path.join(engineRoot, "Engine", "Binaries", process.platform === "darwin" ? "Mac" : "Linux", "UnrealEditor");
|
|
11963
|
+
return exe;
|
|
11964
|
+
}
|
|
11965
|
+
async function runRuntimeSmoke(options) {
|
|
11966
|
+
const editorExe = resolveUnrealEditorExe(options.engineRoot);
|
|
11967
|
+
if (!fs.existsSync(editorExe)) {
|
|
11968
|
+
return { ok: false, error: `Missing UnrealEditor executable: ${editorExe}` };
|
|
11969
|
+
}
|
|
11970
|
+
if (!fs.existsSync(options.projectPath)) {
|
|
11971
|
+
return { ok: false, error: `Missing .uproject: ${options.projectPath}` };
|
|
11972
|
+
}
|
|
11973
|
+
let child = null;
|
|
11974
|
+
const ping = await waitForUnreal({ host: options.host, port: options.port, timeoutMs: 2e3 }).catch((e) => ({ ok: false, error: String(e) }));
|
|
11975
|
+
if (!ping.ok && options.launchIfNeeded) {
|
|
11976
|
+
const args = [
|
|
11977
|
+
options.projectPath,
|
|
11978
|
+
"-NoSplash",
|
|
11979
|
+
"-NoSound",
|
|
11980
|
+
"-nop4"
|
|
11981
|
+
];
|
|
11982
|
+
child = node_child_process.spawn(editorExe, args, {
|
|
11983
|
+
stdio: "ignore",
|
|
11984
|
+
detached: false
|
|
11985
|
+
});
|
|
11986
|
+
}
|
|
11987
|
+
const ready = await waitForUnreal({ host: options.host, port: options.port, timeoutMs: options.connectTimeoutMs });
|
|
11988
|
+
if (!ready.ok) {
|
|
11989
|
+
try {
|
|
11990
|
+
child?.kill();
|
|
11991
|
+
} catch {
|
|
11992
|
+
}
|
|
11993
|
+
return ready;
|
|
11994
|
+
}
|
|
11995
|
+
try {
|
|
11996
|
+
const pluginInfo = await types.sendUnrealMcpTcpCommand({ type: "get_plugin_info", host: options.host, port: options.port, timeoutMs: options.timeoutMs });
|
|
11997
|
+
const createdBy = String(pluginInfo?.createdBy || "").trim();
|
|
11998
|
+
const friendlyName = String(pluginInfo?.friendlyName || "").trim();
|
|
11999
|
+
const baseDir = String(pluginInfo?.baseDir || "").trim();
|
|
12000
|
+
const schemaVersion = Number(pluginInfo?.schemaVersion);
|
|
12001
|
+
const commands = Array.isArray(pluginInfo?.commands) ? pluginInfo.commands.filter((c) => typeof c === "string") : [];
|
|
12002
|
+
if (friendlyName !== "Flockbay MCP" || createdBy !== "Respaced Inc.") {
|
|
12003
|
+
return {
|
|
12004
|
+
ok: false,
|
|
12005
|
+
error: `Unexpected plugin identity loaded by Unreal.
|
|
12006
|
+
friendlyName=${friendlyName || "(missing)"} createdBy=${createdBy || "(missing)"}
|
|
12007
|
+
baseDir=${baseDir || "(missing)"}
|
|
12008
|
+
Expected FriendlyName="Flockbay MCP" CreatedBy="Respaced Inc."`
|
|
12009
|
+
};
|
|
12010
|
+
}
|
|
12011
|
+
if (!Number.isFinite(schemaVersion) || schemaVersion <= 0) {
|
|
12012
|
+
return { ok: false, error: `Invalid schemaVersion from get_plugin_info: ${String(pluginInfo?.schemaVersion)}` };
|
|
12013
|
+
}
|
|
12014
|
+
const requireCommands = [
|
|
12015
|
+
"ping",
|
|
12016
|
+
"get_plugin_info",
|
|
12017
|
+
"list_capabilities",
|
|
12018
|
+
"get_command_schema",
|
|
12019
|
+
"get_play_in_editor_status",
|
|
12020
|
+
"play_in_editor_windowed",
|
|
12021
|
+
"stop_play_in_editor",
|
|
12022
|
+
"take_screenshot",
|
|
12023
|
+
"create_blueprint",
|
|
12024
|
+
"compile_blueprint",
|
|
12025
|
+
"map_check"
|
|
12026
|
+
];
|
|
12027
|
+
const missing = requireCommands.filter((c) => !commands.includes(c));
|
|
12028
|
+
if (missing.length > 0) {
|
|
12029
|
+
return { ok: false, error: `Missing required commands in this UnrealMCP build: ${missing.join(", ")}` };
|
|
12030
|
+
}
|
|
12031
|
+
const playStatus0 = await types.sendUnrealMcpTcpCommand({ type: "get_play_in_editor_status", host: options.host, port: options.port, timeoutMs: options.timeoutMs });
|
|
12032
|
+
const isPlaying = Boolean(playStatus0?.isPlaySessionInProgress);
|
|
12033
|
+
if (isPlaying) {
|
|
12034
|
+
await types.sendUnrealMcpTcpCommand({ type: "stop_play_in_editor", host: options.host, port: options.port, timeoutMs: options.timeoutMs });
|
|
12035
|
+
}
|
|
12036
|
+
await types.sendUnrealMcpTcpCommand({ type: "play_in_editor_windowed", host: options.host, port: options.port, timeoutMs: Math.max(options.timeoutMs, 2e4) });
|
|
12037
|
+
await new Promise((r) => setTimeout(r, 1e3));
|
|
12038
|
+
await types.sendUnrealMcpTcpCommand({ type: "stop_play_in_editor", host: options.host, port: options.port, timeoutMs: Math.max(options.timeoutMs, 2e4) });
|
|
12039
|
+
const projectDir = path.dirname(options.projectPath);
|
|
12040
|
+
const shotsDir = path.join(projectDir, "Saved", "Screenshots", "Flockbay");
|
|
12041
|
+
ensureDir(shotsDir);
|
|
12042
|
+
const shotPath = path.join(shotsDir, `smoke_${Date.now()}.png`);
|
|
12043
|
+
await types.sendUnrealMcpTcpCommand({
|
|
12044
|
+
type: "take_screenshot",
|
|
12045
|
+
params: { filepath: shotPath },
|
|
12046
|
+
host: options.host,
|
|
12047
|
+
port: options.port,
|
|
12048
|
+
timeoutMs: options.timeoutMs
|
|
12049
|
+
});
|
|
12050
|
+
if (!fs.existsSync(shotPath)) return { ok: false, error: `Screenshot did not exist on disk after take_screenshot: ${shotPath}` };
|
|
12051
|
+
const bytes = fs.readFileSync(shotPath);
|
|
12052
|
+
const mime = detectImageMimeTypeFromBuffer(bytes);
|
|
12053
|
+
if (mime !== "image/png") {
|
|
12054
|
+
return { ok: false, error: `Screenshot bytes do not match .png extension (detected ${mime || "unknown"}): ${shotPath}` };
|
|
12055
|
+
}
|
|
12056
|
+
const bpName = `BP_Smoke_${Date.now()}`;
|
|
12057
|
+
await types.sendUnrealMcpTcpCommand({
|
|
12058
|
+
type: "create_blueprint",
|
|
12059
|
+
params: { name: bpName, path: "/Game/FlockbaySmoke/", parent_class: "Actor" },
|
|
12060
|
+
host: options.host,
|
|
12061
|
+
port: options.port,
|
|
12062
|
+
timeoutMs: Math.max(options.timeoutMs, 2e4)
|
|
12063
|
+
});
|
|
12064
|
+
await types.sendUnrealMcpTcpCommand({
|
|
12065
|
+
type: "compile_blueprint",
|
|
12066
|
+
params: { blueprint_name: bpName },
|
|
12067
|
+
host: options.host,
|
|
12068
|
+
port: options.port,
|
|
12069
|
+
timeoutMs: Math.max(options.timeoutMs, 6e4)
|
|
12070
|
+
});
|
|
12071
|
+
await types.sendUnrealMcpTcpCommand({
|
|
12072
|
+
type: "map_check",
|
|
12073
|
+
host: options.host,
|
|
12074
|
+
port: options.port,
|
|
12075
|
+
timeoutMs: Math.max(options.timeoutMs, 3e4)
|
|
12076
|
+
});
|
|
12077
|
+
return { ok: true };
|
|
12078
|
+
} finally {
|
|
12079
|
+
if (options.killAfter && child) {
|
|
12080
|
+
try {
|
|
12081
|
+
if (process.platform === "win32") {
|
|
12082
|
+
node_child_process.spawn("taskkill", ["/PID", String(child.pid), "/T", "/F"], { stdio: "ignore" });
|
|
12083
|
+
} else {
|
|
12084
|
+
child.kill();
|
|
12085
|
+
}
|
|
12086
|
+
} catch {
|
|
12087
|
+
}
|
|
12088
|
+
}
|
|
12089
|
+
}
|
|
12090
|
+
}
|
|
12091
|
+
async function runUnrealMcpMatrixSmoke(args) {
|
|
12092
|
+
const engineRoots = splitList(readArgValue$1(args, "--engine-roots")) || [];
|
|
12093
|
+
const engineRootSingle = readArgValue$1(args, "--engine-root");
|
|
12094
|
+
if (engineRootSingle) engineRoots.push(engineRootSingle.trim());
|
|
12095
|
+
const project = readArgValue$1(args, "--project");
|
|
12096
|
+
const host = (readArgValue$1(args, "--host") || "127.0.0.1").trim() || "127.0.0.1";
|
|
12097
|
+
const port = Number(readArgValue$1(args, "--port") || "55557");
|
|
12098
|
+
const connectTimeoutMs = Number(readArgValue$1(args, "--connect-timeout-ms") || "180000");
|
|
12099
|
+
const timeoutMs = Number(readArgValue$1(args, "--timeout-ms") || "30000");
|
|
12100
|
+
const doBuild = !args.includes("--runtime-only");
|
|
12101
|
+
const doRuntime = !args.includes("--build-only");
|
|
12102
|
+
const launch = args.includes("--launch-editor");
|
|
12103
|
+
const killAfter = args.includes("--kill-editor");
|
|
12104
|
+
if (engineRoots.length === 0) {
|
|
12105
|
+
console.error(chalk.red("Missing --engine-root or --engine-roots."));
|
|
12106
|
+
console.error(chalk.gray('Example: flockbay doctor unreal-mcp-smoke --engine-roots "C:\\\\Epic\\\\UE_5.5;C:\\\\Epic\\\\UE_5.6" --project "C:\\\\Projects\\\\MyProj\\\\MyProj.uproject" --launch-editor --kill-editor'));
|
|
12107
|
+
process.exit(1);
|
|
12108
|
+
}
|
|
12109
|
+
if (doRuntime && !project) {
|
|
12110
|
+
console.error(chalk.red("Missing --project (required for runtime smoke)."));
|
|
12111
|
+
process.exit(1);
|
|
12112
|
+
}
|
|
12113
|
+
console.log(chalk.bold("\nUnrealMCP Matrix Smoke\n"));
|
|
12114
|
+
console.log(chalk.gray(`Platform: ${process.platform}`));
|
|
12115
|
+
console.log(chalk.gray(`Host: ${host}:${port}`));
|
|
12116
|
+
console.log(chalk.gray(`Engines: ${engineRoots.join(", ")}`));
|
|
12117
|
+
if (project) console.log(chalk.gray(`Project: ${project}`));
|
|
12118
|
+
console.log(chalk.gray(`Build: ${doBuild ? "yes" : "no"} Runtime: ${doRuntime ? "yes" : "no"} Launch: ${launch ? "yes" : "no"} Kill: ${killAfter ? "yes" : "no"}`));
|
|
12119
|
+
console.log("");
|
|
12120
|
+
const failures = [];
|
|
12121
|
+
for (const engineRootRaw of engineRoots) {
|
|
12122
|
+
const engineRoot = engineRootRaw.trim();
|
|
12123
|
+
if (!engineRoot) continue;
|
|
12124
|
+
console.log(chalk.bold(`== Engine: ${engineRoot} ==`));
|
|
12125
|
+
if (doBuild) {
|
|
12126
|
+
console.log(chalk.cyan("Build: installing UnrealMCP plugin sources..."));
|
|
12127
|
+
const installed = types.installUnrealMcpPluginToEngine(engineRoot);
|
|
12128
|
+
if (!installed.ok) {
|
|
12129
|
+
failures.push({ engineRoot, phase: "build", error: installed.errorMessage });
|
|
12130
|
+
console.log(chalk.red(`Build: failed (install)
|
|
12131
|
+
${installed.errorMessage}
|
|
12132
|
+
`));
|
|
12133
|
+
if (!doRuntime) continue;
|
|
12134
|
+
} else {
|
|
12135
|
+
console.log(chalk.green(`Build: sources installed to ${installed.destDir}`));
|
|
12136
|
+
console.log(chalk.cyan("Build: compiling plugin via RunUAT BuildPlugin..."));
|
|
12137
|
+
const built = await types.buildAndInstallUnrealMcpPlugin({ engineRoot, flockbayHomeDir: types.configuration.flockbayHomeDir });
|
|
12138
|
+
if (!built.ok) {
|
|
12139
|
+
failures.push({ engineRoot, phase: "build", error: built.errorMessage });
|
|
12140
|
+
console.log(chalk.red(`Build: failed
|
|
12141
|
+
${built.errorMessage}
|
|
12142
|
+
`));
|
|
12143
|
+
} else {
|
|
12144
|
+
console.log(chalk.green(`Build: ok (log: ${built.buildLogPath})`));
|
|
12145
|
+
}
|
|
12146
|
+
}
|
|
12147
|
+
}
|
|
12148
|
+
if (doRuntime) {
|
|
12149
|
+
console.log(chalk.cyan("Runtime: running command smoke..."));
|
|
12150
|
+
const res = await runRuntimeSmoke({
|
|
12151
|
+
engineRoot,
|
|
12152
|
+
projectPath: project,
|
|
12153
|
+
host,
|
|
12154
|
+
port,
|
|
12155
|
+
connectTimeoutMs,
|
|
12156
|
+
timeoutMs,
|
|
12157
|
+
launchIfNeeded: launch,
|
|
12158
|
+
killAfter
|
|
12159
|
+
});
|
|
12160
|
+
if (!res.ok) {
|
|
12161
|
+
failures.push({ engineRoot, phase: "runtime", error: res.error });
|
|
12162
|
+
console.log(chalk.red(`Runtime: failed
|
|
12163
|
+
${res.error}
|
|
12164
|
+
`));
|
|
12165
|
+
} else {
|
|
12166
|
+
console.log(chalk.green("Runtime: ok\n"));
|
|
12167
|
+
}
|
|
12168
|
+
}
|
|
12169
|
+
}
|
|
12170
|
+
if (failures.length > 0) {
|
|
12171
|
+
console.error(chalk.red("\nMatrix smoke failed.\n"));
|
|
12172
|
+
for (const f of failures) {
|
|
12173
|
+
console.error(chalk.red(`- ${f.engineRoot} (${f.phase}): ${f.error}`));
|
|
12174
|
+
}
|
|
12175
|
+
process.exit(1);
|
|
12176
|
+
}
|
|
12177
|
+
console.log(chalk.green("\nMatrix smoke passed.\n"));
|
|
12178
|
+
}
|
|
12179
|
+
|
|
11756
12180
|
function readTailUtf8(filePath, maxBytes) {
|
|
11757
12181
|
try {
|
|
11758
12182
|
const stat = fs__namespace.statSync(filePath);
|
|
@@ -12113,6 +12537,16 @@ async function authAndSetupMachineIfNeeded() {
|
|
|
12113
12537
|
}
|
|
12114
12538
|
if (!args.includes("--version")) ;
|
|
12115
12539
|
if (subcommand === "doctor") {
|
|
12540
|
+
if (args[1] === "unreal-mcp-smoke") {
|
|
12541
|
+
try {
|
|
12542
|
+
await runUnrealMcpMatrixSmoke(args.slice(2));
|
|
12543
|
+
} catch (error) {
|
|
12544
|
+
console.error(chalk.red("UnrealMCP smoke failed:"), error instanceof Error ? error.message : String(error));
|
|
12545
|
+
if (process.env.DEBUG) console.error(error);
|
|
12546
|
+
process.exit(1);
|
|
12547
|
+
}
|
|
12548
|
+
return;
|
|
12549
|
+
}
|
|
12116
12550
|
if (args[1] === "clean") {
|
|
12117
12551
|
const result = await killRunawayFlockbayProcesses();
|
|
12118
12552
|
console.log(`Cleaned up ${result.killed} runaway processes`);
|
|
@@ -12224,7 +12658,7 @@ ${engineRoot}`, {
|
|
|
12224
12658
|
} else if (subcommand === "codex") {
|
|
12225
12659
|
try {
|
|
12226
12660
|
await chdirToNearestUprojectRootIfPresent();
|
|
12227
|
-
const { runCodex } = await Promise.resolve().then(function () { return require('./runCodex-
|
|
12661
|
+
const { runCodex } = await Promise.resolve().then(function () { return require('./runCodex-CaWagdzG.cjs'); });
|
|
12228
12662
|
let startedBy = void 0;
|
|
12229
12663
|
let sessionId = void 0;
|
|
12230
12664
|
for (let i = 1; i < args.length; i++) {
|
|
@@ -12250,7 +12684,13 @@ ${engineRoot}`, {
|
|
|
12250
12684
|
const geminiSubcommand = args[1];
|
|
12251
12685
|
if (geminiSubcommand === "model" && args[2] === "set" && args[3]) {
|
|
12252
12686
|
const modelName = args[3];
|
|
12253
|
-
const validModels = [
|
|
12687
|
+
const validModels = [
|
|
12688
|
+
"gemini-2.5-pro",
|
|
12689
|
+
"gemini-2.5-flash",
|
|
12690
|
+
"gemini-2.5-flash-lite",
|
|
12691
|
+
"gemini-3-pro-preview",
|
|
12692
|
+
"gemini-3-flash-preview"
|
|
12693
|
+
];
|
|
12254
12694
|
if (!validModels.includes(modelName)) {
|
|
12255
12695
|
console.error(`Invalid model: ${modelName}`);
|
|
12256
12696
|
console.error(`Available models: ${validModels.join(", ")}`);
|
|
@@ -12319,7 +12759,7 @@ ${engineRoot}`, {
|
|
|
12319
12759
|
}
|
|
12320
12760
|
try {
|
|
12321
12761
|
await chdirToNearestUprojectRootIfPresent();
|
|
12322
|
-
const { runGemini } = await Promise.resolve().then(function () { return require('./runGemini-
|
|
12762
|
+
const { runGemini } = await Promise.resolve().then(function () { return require('./runGemini-8w5P093W.cjs'); });
|
|
12323
12763
|
let startedBy = void 0;
|
|
12324
12764
|
let sessionId = void 0;
|
|
12325
12765
|
for (let i = 1; i < args.length; i++) {
|