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.
@@ -11,7 +11,7 @@
11
11
  * - Windows CMD: curl -fsSL https://claude.ai/install.cmd | cmd
12
12
  */
13
13
 
14
- const { execSync } = require('child_process');
14
+ const { execSync, execFileSync } = require('child_process');
15
15
  const path = require('path');
16
16
  const fs = require('fs');
17
17
  const os = require('os');
@@ -258,19 +258,53 @@ function findLatestVersionBinary(versionsDir, binaryName = null) {
258
258
  * @returns {{path: string, source: string}|null} Path and source, or null if not found
259
259
  */
260
260
  function findGlobalClaudeCliPath() {
261
- // Check npm global first (highest priority)
261
+ const candidates = [];
262
+
262
263
  const npmPath = findNpmGlobalCliPath();
263
- if (npmPath) return { path: npmPath, source: 'npm' };
264
+ if (npmPath) candidates.push({ path: npmPath, source: 'npm', priority: 3 });
264
265
 
265
- // Check Homebrew installation
266
266
  const homebrewPath = findHomebrewCliPath();
267
- if (homebrewPath) return { path: homebrewPath, source: 'Homebrew' };
267
+ if (homebrewPath) candidates.push({ path: homebrewPath, source: 'Homebrew', priority: 2 });
268
268
 
269
- // Check native installer
270
269
  const nativePath = findNativeInstallerCliPath();
271
- if (nativePath) return { path: nativePath, source: 'native installer' };
270
+ if (nativePath) candidates.push({ path: nativePath, source: 'native installer', priority: 1 });
272
271
 
273
- return null;
272
+ if (candidates.length === 0) return null;
273
+
274
+ // Prefer the newest version across installs when we can determine it.
275
+ // If versions are missing/unparseable, fall back to the legacy priority ordering.
276
+ let best = null;
277
+ for (const c of candidates) {
278
+ const version = getVersion(c.path);
279
+ const parsed = parseSemver3(version);
280
+ const score = parsed ? parsed : null;
281
+
282
+ if (!best) {
283
+ best = { ...c, version, score };
284
+ continue;
285
+ }
286
+
287
+ if (best.score && score) {
288
+ if (compareVersions(version, best.version) > 0) {
289
+ best = { ...c, version, score };
290
+ }
291
+ continue;
292
+ }
293
+
294
+ // If one has a parseable version and the other doesn't, prefer the parseable one.
295
+ if (!best.score && score) {
296
+ best = { ...c, version, score };
297
+ continue;
298
+ }
299
+ if (best.score && !score) continue;
300
+
301
+ // Neither has a version: use priority.
302
+ if ((c.priority || 0) > (best.priority || 0)) {
303
+ best = { ...c, version, score };
304
+ }
305
+ }
306
+
307
+ return best ? { path: best.path, source: best.source } : candidates[0];
274
308
  }
275
309
 
276
310
  /**
@@ -280,15 +314,35 @@ function findGlobalClaudeCliPath() {
280
314
  */
281
315
  function getVersion(cliPath) {
282
316
  try {
283
- const pkgPath = path.join(path.dirname(cliPath), 'package.json');
284
- if (fs.existsSync(pkgPath)) {
285
- const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
286
- return pkg.version;
317
+ const isJsFile = cliPath.endsWith('.js') || cliPath.endsWith('.cjs');
318
+ if (isJsFile) {
319
+ const pkgPath = path.join(path.dirname(cliPath), 'package.json');
320
+ if (fs.existsSync(pkgPath)) {
321
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
322
+ return pkg.version;
323
+ }
324
+ return null;
287
325
  }
326
+
327
+ // Binary install: try to ask it.
328
+ const out = execFileSync(cliPath, ['--version'], { encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe'] }).trim();
329
+ const m = out.match(/(\d+\.\d+\.\d+)/);
330
+ return m ? m[1] : null;
288
331
  } catch (e) {}
289
332
  return null;
290
333
  }
291
334
 
335
+ function parseSemver3(v) {
336
+ if (!v || typeof v !== 'string') return null;
337
+ const m = v.trim().match(/^(\d+)\.(\d+)\.(\d+)/);
338
+ if (!m) return null;
339
+ const a = Number(m[1]);
340
+ const b = Number(m[2]);
341
+ const c = Number(m[3]);
342
+ if (!Number.isFinite(a) || !Number.isFinite(b) || !Number.isFinite(c)) return null;
343
+ return [a, b, c];
344
+ }
345
+
292
346
  /**
293
347
  * Compare semver versions
294
348
  * @param {string} a - First version
@@ -27,6 +27,8 @@
27
27
  #include "Components/StaticMeshComponent.h"
28
28
  #include "Materials/MaterialInterface.h"
29
29
  #include "Sound/SoundWave.h"
30
+ #include "IImageWrapper.h"
31
+ #include "IImageWrapperModule.h"
30
32
  #include "EditorSubsystem.h"
31
33
  #include "Subsystems/EditorActorSubsystem.h"
32
34
  #include "Engine/Blueprint.h"
@@ -54,6 +56,31 @@
54
56
  #include "Components/SceneCaptureComponent2D.h"
55
57
  #include "Engine/TextureRenderTarget2D.h"
56
58
 
59
+ static bool SavePngToFile(const FString& FilePath, int32 Width, int32 Height, const TArray<FColor>& Bitmap)
60
+ {
61
+ IImageWrapperModule& ImageWrapperModule = FModuleManager::LoadModuleChecked<IImageWrapperModule>(FName("ImageWrapper"));
62
+ TSharedPtr<IImageWrapper> Wrapper = ImageWrapperModule.CreateImageWrapper(EImageFormat::PNG);
63
+ if (!Wrapper.IsValid())
64
+ {
65
+ return false;
66
+ }
67
+
68
+ if (!Wrapper->SetRaw(Bitmap.GetData(), Bitmap.Num() * sizeof(FColor), Width, Height, ERGBFormat::BGRA, 8))
69
+ {
70
+ return false;
71
+ }
72
+
73
+ const TArray64<uint8>& Compressed = Wrapper->GetCompressed(100);
74
+ if (Compressed.Num() <= 0 || Compressed.Num() > MAX_int32)
75
+ {
76
+ return false;
77
+ }
78
+
79
+ TArray<uint8> Out;
80
+ Out.Append(Compressed.GetData(), static_cast<int32>(Compressed.Num()));
81
+ return FFileHelper::SaveArrayToFile(Out, *FilePath);
82
+ }
83
+
57
84
  FUnrealMCPEditorCommands::FUnrealMCPEditorCommands()
58
85
  {
59
86
  }
@@ -219,7 +246,7 @@ TSharedPtr<FJsonObject> FUnrealMCPEditorCommands::HandleSaveAll(const TSharedPtr
219
246
  }
220
247
 
221
248
  TArray<UPackage*> DirtyBefore;
222
- FEditorFileUtils::GetDirtyPackages(DirtyBefore, /*bIncludeMapPackages=*/true, /*bIncludeContentPackages=*/true);
249
+ FEditorFileUtils::GetDirtyPackages(DirtyBefore);
223
250
 
224
251
  TSet<FString> DirtyBeforeNames;
225
252
  for (UPackage* Pkg : DirtyBefore)
@@ -239,7 +266,7 @@ TSharedPtr<FJsonObject> FUnrealMCPEditorCommands::HandleSaveAll(const TSharedPtr
239
266
  );
240
267
 
241
268
  TArray<UPackage*> DirtyAfter;
242
- FEditorFileUtils::GetDirtyPackages(DirtyAfter, /*bIncludeMapPackages=*/true, /*bIncludeContentPackages=*/true);
269
+ FEditorFileUtils::GetDirtyPackages(DirtyAfter);
243
270
 
244
271
  TSet<FString> DirtyAfterNames;
245
272
  for (UPackage* Pkg : DirtyAfter)
@@ -293,8 +320,7 @@ TSharedPtr<FJsonObject> FUnrealMCPEditorCommands::HandleSaveAll(const TSharedPtr
293
320
  Details->SetArrayField(TEXT("stillDirtyPackages"), StillDirty);
294
321
 
295
322
  TSharedPtr<FJsonObject> ErrObj = FUnrealMCPCommonUtils::CreateErrorResponse(
296
- TEXT("Some packages are still dirty after save_all. They may require manual Save As, source control checkout, or have save errors. Resolve in-editor and retry save_all."),
297
- );
323
+ TEXT("Some packages are still dirty after save_all. They may require manual Save As, source control checkout, or have save errors. Resolve in-editor and retry save_all."));
298
324
  ErrObj->SetObjectField(TEXT("details"), Details);
299
325
  return ErrObj;
300
326
  }
@@ -2445,9 +2471,7 @@ TSharedPtr<FJsonObject> FUnrealMCPEditorCommands::HandleTakeScreenshot(const TSh
2445
2471
 
2446
2472
  CaptureActor->Destroy();
2447
2473
 
2448
- TArray<uint8> CompressedBitmap;
2449
- FImageUtils::CompressImageArray(Width, Height, Bitmap, CompressedBitmap);
2450
- if (!FFileHelper::SaveArrayToFile(CompressedBitmap, *FilePath))
2474
+ if (!SavePngToFile(FilePath, Width, Height, Bitmap))
2451
2475
  {
2452
2476
  return FUnrealMCPCommonUtils::CreateErrorResponse(TEXT("Failed to save screenshot PNG to disk."));
2453
2477
  }
@@ -2472,10 +2496,7 @@ TSharedPtr<FJsonObject> FUnrealMCPEditorCommands::HandleTakeScreenshot(const TSh
2472
2496
 
2473
2497
  if (Viewport->ReadPixels(Bitmap, FReadSurfaceDataFlags(), ViewportRect))
2474
2498
  {
2475
- TArray<uint8> CompressedBitmap;
2476
- FImageUtils::CompressImageArray(ViewSize.X, ViewSize.Y, Bitmap, CompressedBitmap);
2477
-
2478
- if (FFileHelper::SaveArrayToFile(CompressedBitmap, *FilePath))
2499
+ if (SavePngToFile(FilePath, ViewSize.X, ViewSize.Y, Bitmap))
2479
2500
  {
2480
2501
  TSharedPtr<FJsonObject> ResultObj = MakeShared<FJsonObject>();
2481
2502
  ResultObj->SetStringField(TEXT("filepath"), FilePath);
@@ -45,6 +45,7 @@ public class UnrealMCP : ModuleRules
45
45
  "EditorScriptingUtilities",
46
46
  "EditorSubsystem",
47
47
  "Landscape",
48
+ "ImageWrapper",
48
49
  "Slate",
49
50
  "SlateCore",
50
51
  "MessageLog",