feat: support to build for windows and add proxy when do http request…#481
feat: support to build for windows and add proxy when do http request…#481peilinok wants to merge 24 commits intoRunanywhereAI:mainfrom
Conversation
… or download modals
|
Too many files changed for review. ( |
There was a problem hiding this comment.
Pull request overview
Adds initial Windows (preview) support for the Flutter SDK + example app, and introduces configurable proxy support for HTTP requests—especially model downloads—along with some Windows-specific native changes in runanywhere-commons.
Changes:
- Add Windows Flutter plugin implementations (C++ + CMake) for
runanywhere,runanywhere_llamacpp, andrunanywhere_onnx, plus Windows example runner scaffolding. - Add a configurable download-only HTTP client factory (enabling proxy usage for model downloads) and wire it from the Flutter example app.
- Update
runanywhere-commonsfor Windows vertical-slice behavior (filesystem traversal, extraction stub, Windows build script/docs).
Reviewed changes
Copilot reviewed 68 out of 104 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| sdk/runanywhere-flutter/packages/runanywhere_onnx/windows/runanywhere_onnx_plugin_c_api.cpp | Windows C-API registrar entrypoint for ONNX plugin |
| sdk/runanywhere-flutter/packages/runanywhere_onnx/windows/runanywhere_onnx_plugin.h | Windows ONNX plugin header |
| sdk/runanywhere-flutter/packages/runanywhere_onnx/windows/runanywhere_onnx_plugin.cpp | Windows ONNX method-channel implementation |
| sdk/runanywhere-flutter/packages/runanywhere_onnx/windows/include/runanywhere_onnx/runanywhere_onnx_plugin_c_api.h | Exported C-API header for ONNX plugin |
| sdk/runanywhere-flutter/packages/runanywhere_onnx/windows/CMakeLists.txt | Build + bundle ONNX backend DLL for Windows plugin |
| sdk/runanywhere-flutter/packages/runanywhere_onnx/pubspec.yaml | Declare Windows plugin for runanywhere_onnx |
| sdk/runanywhere-flutter/packages/runanywhere_onnx/lib/native/onnx_bindings.dart | Load ONNX backend DLL on Windows |
| sdk/runanywhere-flutter/packages/runanywhere_llamacpp/windows/runanywhere_llamacpp_plugin_c_api.cpp | Windows C-API registrar entrypoint for LlamaCpp plugin |
| sdk/runanywhere-flutter/packages/runanywhere_llamacpp/windows/runanywhere_llamacpp_plugin.h | Windows LlamaCpp plugin header |
| sdk/runanywhere-flutter/packages/runanywhere_llamacpp/windows/runanywhere_llamacpp_plugin.cpp | Windows LlamaCpp method-channel implementation |
| sdk/runanywhere-flutter/packages/runanywhere_llamacpp/windows/include/runanywhere_llamacpp/runanywhere_llamacpp_plugin_c_api.h | Exported C-API header for LlamaCpp plugin |
| sdk/runanywhere-flutter/packages/runanywhere_llamacpp/windows/CMakeLists.txt | Build + bundle LlamaCpp backend DLL for Windows plugin |
| sdk/runanywhere-flutter/packages/runanywhere_llamacpp/pubspec.yaml | Declare Windows plugin for runanywhere_llamacpp |
| sdk/runanywhere-flutter/packages/runanywhere_llamacpp/lib/native/llamacpp_bindings.dart | Load LlamaCpp backend DLL on Windows |
| sdk/runanywhere-flutter/packages/runanywhere/windows/runanywhere_plugin_c_api.cpp | Windows C-API registrar entrypoint for core plugin |
| sdk/runanywhere-flutter/packages/runanywhere/windows/runanywhere_plugin.h | Windows core plugin header |
| sdk/runanywhere-flutter/packages/runanywhere/windows/runanywhere_plugin.cpp | Windows core method-channel implementation |
| sdk/runanywhere-flutter/packages/runanywhere/windows/include/runanywhere/runanywhere_plugin_c_api.h | Exported C-API header for core plugin |
| sdk/runanywhere-flutter/packages/runanywhere/windows/CMakeLists.txt | Build + bundle rac_commons.dll for Windows core plugin |
| sdk/runanywhere-flutter/packages/runanywhere/pubspec.yaml | Declare Windows plugin for runanywhere |
| sdk/runanywhere-flutter/packages/runanywhere/lib/public/runanywhere.dart | Add download HTTP client factory configuration API |
| sdk/runanywhere-flutter/packages/runanywhere/lib/native/platform_loader.dart | Improve Windows DLL search path (include executable dir) |
| sdk/runanywhere-flutter/packages/runanywhere/lib/infrastructure/download/download_service.dart | Add injectable HTTP client factory for downloads |
| sdk/runanywhere-flutter/packages/runanywhere/README.md | Document Windows setup + support matrix update |
| sdk/runanywhere-commons/src/infrastructure/extraction/rac_extraction_stub.cpp | Add extraction stub for builds without native extraction |
| sdk/runanywhere-commons/src/infrastructure/download/download_orchestrator.cpp | Windows filesystem-based directory traversal helpers |
| sdk/runanywhere-commons/src/core/rac_core.cpp | Restrict diffusion registry init/cleanup to Apple platforms |
| sdk/runanywhere-commons/src/backends/onnx/CMakeLists.txt | Add Windows build-path status message for ONNX backend |
| sdk/runanywhere-commons/src/backends/llamacpp/CMakeLists.txt | Add Windows backend config and build flags for LlamaCpp |
| sdk/runanywhere-commons/scripts/build-windows.bat | Simplified Windows build script producing DLL artifacts |
| sdk/runanywhere-commons/README.md | Document Windows build steps + current limitations |
| examples/flutter/RunAnywhereAI/windows/runner/win32_window.h | Windows runner window abstraction (Flutter template) |
| examples/flutter/RunAnywhereAI/windows/runner/win32_window.cpp | Windows runner window implementation (Flutter template) |
| examples/flutter/RunAnywhereAI/windows/runner/utils.h | Windows runner utilities (Flutter template) |
| examples/flutter/RunAnywhereAI/windows/runner/utils.cpp | Windows runner utilities implementation (Flutter template) |
| examples/flutter/RunAnywhereAI/windows/runner/runner.exe.manifest | Runner manifest (DPI awareness/compat) |
| examples/flutter/RunAnywhereAI/windows/runner/resource.h | Windows runner resource header |
| examples/flutter/RunAnywhereAI/windows/runner/main.cpp | Windows runner entrypoint |
| examples/flutter/RunAnywhereAI/windows/runner/flutter_window.h | Runner FlutterWindow wrapper |
| examples/flutter/RunAnywhereAI/windows/runner/flutter_window.cpp | Runner FlutterWindow implementation |
| examples/flutter/RunAnywhereAI/windows/runner/Runner.rc | Runner resources/version metadata |
| examples/flutter/RunAnywhereAI/windows/runner/CMakeLists.txt | Runner target build configuration |
| examples/flutter/RunAnywhereAI/windows/flutter/generated_plugins.cmake | Windows generated plugin list for example app |
| examples/flutter/RunAnywhereAI/windows/flutter/generated_plugin_registrant.h | Windows generated plugin registrant header |
| examples/flutter/RunAnywhereAI/windows/flutter/generated_plugin_registrant.cc | Windows generated plugin registrant implementation |
| examples/flutter/RunAnywhereAI/windows/flutter/CMakeLists.txt | Flutter Windows toolchain wrapper (template) |
| examples/flutter/RunAnywhereAI/windows/CMakeLists.txt | Example app Windows top-level CMake |
| examples/flutter/RunAnywhereAI/windows/.gitignore | Windows build artifacts ignores |
| examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/windows/include/flutter_secure_storage_windows/flutter_secure_storage_windows_plugin.h | Vendored plugin Windows C-API header (include path) |
| examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/windows/flutter_secure_storage_windows_plugin.h | Vendored plugin Windows C-API header (local) |
| examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/windows/flutter_secure_storage_windows_plugin.cpp | Vendored plugin Windows implementation |
| examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/windows/CMakeLists.txt | Build config for vendored Windows secure storage plugin |
| examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/pubspec.yaml | Vendored plugin pubspec |
| examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/lib/src/flutter_secure_storage_windows_stub.dart | Non-FFI stub implementation |
| examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/lib/src/flutter_secure_storage_windows_ffi.dart | FFI-backed Windows implementation |
| examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/lib/flutter_secure_storage_windows.dart | Conditional export (stub vs FFI) |
| examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/example/windows/runner/win32_window.h | Vendored example runner file |
| examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/example/windows/runner/win32_window.cpp | Vendored example runner file |
| examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/example/windows/runner/utils.h | Vendored example runner file |
| examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/example/windows/runner/utils.cpp | Vendored example runner file |
| examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/example/windows/runner/runner.exe.manifest | Vendored example runner file |
| examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/example/windows/runner/resource.h | Vendored example runner file |
| examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/example/windows/runner/main.cpp | Vendored example runner file |
| examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/example/windows/runner/flutter_window.h | Vendored example runner file |
| examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/example/windows/runner/flutter_window.cpp | Vendored example runner file |
| examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/example/windows/runner/Runner.rc | Vendored example runner file |
| examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/example/windows/runner/CMakeLists.txt | Vendored example runner build config |
| examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/example/windows/flutter/generated_plugins.cmake | Vendored generated CMake plugin list |
| examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/example/windows/flutter/generated_plugin_registrant.h | Vendored generated plugin registrant |
| examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/example/windows/flutter/generated_plugin_registrant.cc | Vendored generated plugin registrant |
| examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/example/windows/flutter/CMakeLists.txt | Vendored flutter toolchain wrapper |
| examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/example/windows/CMakeLists.txt | Vendored example top-level CMake |
| examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/example/pubspec.yaml | Vendored example pubspec |
| examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/example/lib/main.dart | Vendored example app Dart code |
| examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/example/analysis_options.yaml | Vendored example analyzer config |
| examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/example/README.md | Vendored example README |
| examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/analysis_options.yaml | Vendored plugin analyzer config |
| examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/README.md | Vendored plugin README |
| examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/LICENSE | Vendored plugin license |
| examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/CHANGELOG.md | Vendored plugin changelog |
| examples/flutter/RunAnywhereAI/pubspec.yaml | Pin record version + override secure storage windows |
| examples/flutter/RunAnywhereAI/lib/features/voice/voice_assistant_view.dart | Gate unsupported voice assistant on Windows |
| examples/flutter/RunAnywhereAI/lib/features/voice/text_to_speech_view.dart | Gate unsupported TTS on Windows |
| examples/flutter/RunAnywhereAI/lib/features/voice/speech_to_text_view.dart | Gate unsupported STT on Windows |
| examples/flutter/RunAnywhereAI/lib/features/vision/vision_hub_view.dart | Gate unsupported vision on Windows |
| examples/flutter/RunAnywhereAI/lib/features/settings/tool_settings_view_model.dart | Route sample HTTP calls through proxy-aware service |
| examples/flutter/RunAnywhereAI/lib/features/settings/combined_settings_view.dart | Add UI for general/download proxy configuration |
| examples/flutter/RunAnywhereAI/lib/features/rag/rag_demo_view.dart | Gate unsupported RAG on Windows |
| examples/flutter/RunAnywhereAI/lib/features/chat/chat_interface_view.dart | Prevent navigation to RAG when unsupported |
| examples/flutter/RunAnywhereAI/lib/core/utilities/constants.dart | Add proxy-related key/value constants |
| examples/flutter/RunAnywhereAI/lib/core/services/proxy_settings_service.dart | Persist/validate proxy settings (prefs + keychain) |
| examples/flutter/RunAnywhereAI/lib/core/services/platform_capability_service.dart | Centralize Windows feature gating flags |
| examples/flutter/RunAnywhereAI/lib/core/services/example_http_service.dart | Create proxy-aware HttpClient/IOClient helpers |
| examples/flutter/RunAnywhereAI/lib/core/models/proxy_settings.dart | Proxy settings models/enums |
| examples/flutter/RunAnywhereAI/lib/core/design_system/unsupported_feature_view.dart | Reusable unsupported-feature UI |
| examples/flutter/RunAnywhereAI/lib/core/design_system/app_colors.dart | Adjust grouped background color + add compat extension |
| examples/flutter/RunAnywhereAI/lib/app/runanywhere_ai_app.dart | Wire download proxy factory + update module logs |
| examples/flutter/RunAnywhereAI/ios/Runner/GeneratedPluginRegistrant.m | Update plugin registration headers/names |
| examples/flutter/RunAnywhereAI/android/app/src/main/java/io/flutter/plugins/GeneratedPluginRegistrant.java | Update plugin registration error message |
| examples/flutter/RunAnywhereAI/.gitignore | Stop ignoring windows/ so Windows project is tracked |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| import 'package:runanywhere/native/dart_bridge_rag.dart'; | ||
| import 'package:runanywhere/native/dart_bridge_structured_output.dart'; | ||
| import 'package:runanywhere/native/dart_bridge_vlm.dart'; | ||
| import 'package:runanywhere/native/ffi_types.dart' show RacVlmImageFormat; | ||
| import 'package:runanywhere/native/dart_bridge_structured_output.dart'; |
There was a problem hiding this comment.
There are two identical imports of dart_bridge_structured_output.dart in this import block, which will cause analyzer/linter failures (and may fail compilation depending on settings). Remove the duplicate and keep a single import (and ensure it’s placed consistently with the surrounding import ordering).
| for %%I in ("%~dp0.") do set "SCRIPT_HOME=%%~fI" | ||
| set "ROOT=%SCRIPT_HOME%" | ||
| set "BUILD_DIR=%ROOT%\build-windows-x64" | ||
| set "DIST_DIR=%ROOT%\dist\windows\x64" |
There was a problem hiding this comment.
ROOT is set to the scripts directory (%~dp0), but the subsequent cmake -S "%ROOT%" expects the source root containing CMakeLists.txt (likely sdk/runanywhere-commons). As written, this will configure CMake from sdk/runanywhere-commons/scripts, which doesn’t contain a top-level CMakeLists.txt, so the build will fail. Update ROOT to point at the repo/package root (e.g., %SCRIPT_HOME%\..) and keep BUILD_DIR/DIST_DIR under that root.
| final file = File(destPath); | ||
| await file.create(recursive: true); | ||
| final sink = file.openWrite(); | ||
| var downloaded = 0; |
There was a problem hiding this comment.
In the multi-file download path, the file sink is created with file.openWrite() but isn’t protected by a try/finally. If an exception occurs while streaming (network error, disk full, cancellation, etc.), the sink may remain open and the partial file may be left in a corrupted state. Wrap the sink usage in a try/finally (closing in finally) and consider cleaning up the partially written file on failure.
📝 WalkthroughWalkthroughAdds extensive Windows support across the example app and SDK: Windows build tooling/CMake, native Windows plugins (RunAnywhere, LlamaCPP, ONNX), a Windows flutter_secure_storage implementation with tests, proxy/HTTP client configuration, platform capability gating in the UI, VLM/vision refactors and helpers, and multiple Windows-specific build/download scripts and utilities. Changes
Sequence Diagram(s)sequenceDiagram
participant App as Flutter Example
participant CapSvc as PlatformCapabilityService
participant UI as Feature View
participant Unsupported as UnsupportedFeatureView
App->>CapSvc: query supportsX (e.g., supportsVision/supportsRag)
CapSvc->>CapSvc: evaluate Platform.isWindows
alt Feature unsupported on Windows
CapSvc-->>App: false
App->>UI: build()
UI->>Unsupported: instantiate with title/message
Unsupported-->>UI: rendered scaffold
UI-->>App: return Unsupported Scaffold
else Feature supported
CapSvc-->>App: true
App->>UI: build()
UI-->>App: render full feature UI
end
sequenceDiagram
participant Settings as CombinedSettingsView
participant ProxySvc as ProxySettingsService
participant Keychain as KeychainHelper
participant Prefs as SharedPreferences
participant HTTP as ExampleHttpService
Settings->>ProxySvc: load(scope)
ProxySvc->>Prefs: read keys for scope
ProxySvc->>Keychain: read username/password
Keychain-->>ProxySvc: credentials
ProxySvc-->>Settings: return ProxySettings
Settings->>ProxySvc: save(scope, settings)
ProxySvc->>ProxySvc: validate(settings)
ProxySvc->>Prefs: persist enabled/scheme/host/port/bypass
ProxySvc->>Keychain: store/delete credentials
ProxySvc-->>Settings: validation result
Settings->>HTTP: testProxy(scope, uri)
HTTP->>ProxySvc: getCurrent(scope)
ProxySvc-->>HTTP: returns settings
HTTP->>HTTP: createScopedHttpClient(uri) (findProxy/auth config)
HTTP-->>Settings: return success/failure
Estimated code review effort🎯 5 (Critical) | ⏱️ ~120 minutes Possibly related PRs
Suggested labels
Suggested reviewers
Poem
✨ Finishing Touches🧪 Generate unit tests (beta)
|
There was a problem hiding this comment.
Actionable comments posted: 10
Note
Due to the large number of review comments, Critical severity comments were prioritized as inline comments.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
sdk/runanywhere-commons/src/backends/llamacpp/CMakeLists.txt (1)
285-298:⚠️ Potential issue | 🔴 CriticalDuplicate
elseif(RAC_PLATFORM_WINDOWS)in platform-specific configuration section.Similar to the issue above, lines 295-298 contain another duplicate Windows block that will never execute. The second block only has a comment about threading being built-in, while the first block (lines 285-288) actually links
ws2_32.Remove the duplicate block at lines 295-298.
🐛 Proposed fix: remove duplicate Windows block
elseif(RAC_PLATFORM_WINDOWS) message(STATUS "Configuring LlamaCPP backend for Windows") target_compile_definitions(rac_backend_llamacpp PRIVATE NOMINMAX WIN32_LEAN_AND_MEAN) target_link_libraries(rac_backend_llamacpp PUBLIC ws2_32) elseif(RAC_PLATFORM_LINUX) message(STATUS "Configuring LlamaCPP backend for Linux") # Linux-specific link libraries target_link_libraries(rac_backend_llamacpp PUBLIC pthread dl) - -elseif(RAC_PLATFORM_WINDOWS) - message(STATUS "Configuring LlamaCPP backend for Windows") - # No extra link libraries needed on Windows (threading is built-in) endif()🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@sdk/runanywhere-commons/src/backends/llamacpp/CMakeLists.txt` around lines 285 - 298, The CMake platform configuration contains a duplicate elseif(RAC_PLATFORM_WINDOWS) block that will never execute; remove the redundant second elseif(RAC_PLATFORM_WINDOWS) block (the one with only the comment about threading) so the platform branches become elseif(RAC_PLATFORM_WINDOWS) { message + target_compile_definitions + target_link_libraries(ws2_32) } elseif(RAC_PLATFORM_LINUX) { target_link_libraries(pthread dl) } endif(), ensuring only one RAC_PLATFORM_WINDOWS branch remains for target_link_libraries(rac_backend_llamacpp) and target_compile_definitions.sdk/runanywhere-commons/src/backends/onnx/CMakeLists.txt (1)
325-334:⚠️ Potential issue | 🟡 MinorDuplicate
elseif(RAC_PLATFORM_WINDOWS)creates dead code.There are two
elseif(RAC_PLATFORM_WINDOWS)blocks in the platform configuration chain. The second one at lines 331-334 is unreachable because the first one at lines 325-327 will always match first. Remove the duplicate block.🔧 Proposed fix
elseif(RAC_PLATFORM_WINDOWS) message(STATUS "ONNX Backend: Windows build path available when ONNX dependencies are configured") + # No extra link libraries needed on Windows elseif(RAC_PLATFORM_LINUX) target_link_libraries(rac_backend_onnx PUBLIC pthread dl) - -elseif(RAC_PLATFORM_WINDOWS) - # No extra link libraries needed on Windows - message(STATUS "ONNX Backend: CPU EP for Windows") endif()🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@sdk/runanywhere-commons/src/backends/onnx/CMakeLists.txt` around lines 325 - 334, There is a duplicate elseif(RAC_PLATFORM_WINDOWS) block that is unreachable; remove the second elseif(RAC_PLATFORM_WINDOWS) block (the one printing "ONNX Backend: CPU EP for Windows") so the platform chain is not duplicated, leaving only the first elseif(RAC_PLATFORM_WINDOWS) message and the RAC_PLATFORM_LINUX target_link_libraries(rac_backend_onnx PUBLIC pthread dl) branch intact; if Windows-specific linking or messages are required, consolidate them into the existing first elseif(RAC_PLATFORM_WINDOWS) block instead of adding a second block.
🟠 Major comments (17)
examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/LICENSE-1-29 (1)
1-29:⚠️ Potential issue | 🟠 MajorRemove the vendored
flutter_secure_storage_windowspackage entirely.The main
pubspec.yamlalready declaresflutter_secure_storage: ^9.0.0as a dependency.flutter_secure_storage_windowsis an endorsed implementation that gets automatically included when the parent package is declared—vendoring it is redundant and creates unnecessary maintenance burden. Additionally, the vendored version (3.1.2) is outdated compared to the latest on pub.dev (4.1.0), and the package itself recommends usingflutter_secure_storageinstead.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/LICENSE` around lines 1 - 29, Remove the vendored flutter_secure_storage_windows package and its files (the LICENSE and all vendor folder contents referencing flutter_secure_storage_windows v3.1.2); rely on the existing top-level dependency flutter_secure_storage ^9.0.0 declared in pubspec.yaml so the endorsed Windows implementation is resolved automatically—delete the vendor directory and any references/imports to flutter_secure_storage_windows to avoid redundancy and stale code.examples/flutter/RunAnywhereAI/lib/core/services/platform_capability_service.dart-1-1 (1)
1-1:⚠️ Potential issue | 🟠 MajorRemove
dart:iofrom shared Flutter UI code.The
import 'dart:io'on line 1 breaks web compilation.Platform.isWindows(lines 12, 16) is unavailable for Flutter web builds. UsekIsWebanddefaultTargetPlatformfrompackage:flutter/foundation.dartinstead.Suggested fix
-import 'dart:io'; +import 'package:flutter/foundation.dart'; class PlatformCapabilityService { static const PlatformCapabilityService shared = PlatformCapabilityService._(); const PlatformCapabilityService._(); + bool get _isWindows => + !kIsWeb && defaultTargetPlatform == TargetPlatform.windows; + bool get supportsChat => true; bool get supportsTools => true; bool get supportsStructuredOutput => true; - bool get supportsVision => !Platform.isWindows; + bool get supportsVision => !_isWindows; bool get supportsSpeechToText => true; bool get supportsTextToSpeech => true; bool get supportsVoiceAssistant => true; - bool get supportsRag => !Platform.isWindows; + bool get supportsRag => !_isWindows;🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@examples/flutter/RunAnywhereAI/lib/core/services/platform_capability_service.dart` at line 1, Remove the `import 'dart:io'` and replace any use of `Platform.isWindows` with a web-safe check using `kIsWeb` and `defaultTargetPlatform` from `package:flutter/foundation.dart`; specifically import `package:flutter/foundation.dart` and change checks like `Platform.isWindows` to `!kIsWeb && defaultTargetPlatform == TargetPlatform.windows` (or use `kIsWeb` early to short-circuit), ensuring platform-specific branches in platform_capability_service.dart reference `kIsWeb`, `defaultTargetPlatform`, and `TargetPlatform` instead of `Platform`.sdk/runanywhere-flutter/packages/runanywhere/README.md-86-103 (1)
86-103:⚠️ Potential issue | 🟠 MajorClarify repo-relative vs consumer setup commands.
Line 91 and Line 98 assume a monorepo checkout (
sdk\...,examples\...). For pub package consumers, these paths won’t exist, so setup is not actionable.🛠️ Suggested doc split
## Windows Setup (Preview) -Build native DLLs first: +If you are working inside this monorepo, build native DLLs first: ```bat cd sdk\runanywhere-commons call scripts\build-windows.bat llamacpp --clean-Then build the example app:
+Then build the bundled example app:cd examples\flutter\RunAnywhereAI fvm flutter build windows+If you are integrating
runanywhereinto your own Flutter app from pub.dev,
+build your app directly from your project root:
+bat +flutter build windows +</details> <details> <summary>🤖 Prompt for AI Agents</summary>Verify each finding against the current code and only fix it if needed.
In
@sdk/runanywhere-flutter/packages/runanywhere/README.mdaround lines 86 -
103, The Windows Setup (Preview) docs assume a monorepo checkout and uses
repo-relative commands under the sdk and examples folders, which won’t work for
pub package consumers; update the "Windows Setup (Preview)" README section to
split instructions: 1) keep the existing monorepo-specific steps for building
native DLLs and the bundled example (referencing the existing build-windows.bat
invocation and the fvm flutter build windows step) and 2) add a clear
consumer-focused subsection telling pub.dev integrators to build from their app
root (e.g., "flutter build windows") and to skip the repo-specific build scripts
unless they vendor or copy the native DLLs, and label the example build steps as
"bundled example" so readers know which workflow applies to them.</details> </blockquote></details> <details> <summary>sdk/runanywhere-flutter/packages/runanywhere/lib/public/runanywhere.dart-2280-2287 (1)</summary><blockquote> `2280-2287`: _⚠️ Potential issue_ | _🟠 Major_ **Proxy support is still incomplete for the SDK's own HTTP calls.** This new hook only configures `ModelDownloadService`. Device registration, auth, telemetry, and any other SDK API traffic in this file still go through other services, so a proxy-only environment can still fail before downloads even start. <details> <summary>🤖 Prompt for AI Agents</summary>Verify each finding against the current code and only fix it if needed.
In
@sdk/runanywhere-flutter/packages/runanywhere/lib/public/runanywhere.dart
around lines 2280 - 2287, The current configureDownloadHttpClientFactory only
configures ModelDownloadService but leaves device registration, auth, telemetry
and other SDK API traffic using the default HTTP client; extend the method to
set the same factory for the SDK-wide HTTP layer so all SDK network calls honor
the proxy. Specifically, in configureDownloadHttpClientFactory (which currently
calls ModelDownloadService.shared.configureClientFactory), also invoke the
central/shared HTTP client configurator used by other services (e.g.,
SDKHttpClient.shared.configureClientFactory or
NetworkService.shared.configureClientFactory — whichever is the SDK-wide HTTP
client in this module) so DeviceRegistrationService, AuthService,
TelemetryService, etc. use the provided factory; ensure null is handled the same
way and update any references to the global client so all internal SDK API calls
route through the configured factory.</details> </blockquote></details> <details> <summary>examples/flutter/RunAnywhereAI/lib/features/voice/speech_to_text_view.dart-123-138 (1)</summary><blockquote> `123-138`: _⚠️ Potential issue_ | _🟠 Major_ **Refresh STT model state on first mount too.** This helper only runs after the picker callback now. If an STT model was already loaded before opening this screen, `_hasModelSelected` stays false and the blocking overlay remains until the user reopens the sheet. <details> <summary>Suggested fix</summary> ```diff `@override` void initState() { super.initState(); unawaited(_checkMicrophonePermission()); + if (sdk.RunAnywhere.isSDKInitialized) { + unawaited(_refreshModelState()); + } }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@examples/flutter/RunAnywhereAI/lib/features/voice/speech_to_text_view.dart` around lines 123 - 138, The screen never refreshes the STT model on first mount so _hasModelSelected remains false if a model was loaded earlier; call _refreshModelState() during widget initialization (e.g., in initState or the appropriate lifecycle method) so the current model is fetched as soon as the view mounts, and ensure you set _hasModelSelected based on whether currentModel is non-null inside _refreshModelState (or immediately after awaiting sdk.RunAnywhere.currentSTTModel()) so the blocking overlay is removed when a model already exists.examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/example/pubspec.yaml-5-7 (1)
5-7:⚠️ Potential issue | 🟠 MajorWiden the example's Dart upper bound to Dart 3+.
The vendored example is capped at
<3.0.0, which excludes Dart 3.x. The parent package already allows<4.0.0, and the main RunAnywhereAI project requires Dart 3.0.0+, creating an incompatibility. The example will fail dependency resolution under current toolchains.Suggested fix
environment: - sdk: '>=2.12.0 <3.0.0' + sdk: '>=2.12.0 <4.0.0' flutter: ">=2.0.0"🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/example/pubspec.yaml` around lines 5 - 7, Update the environment SDK constraint in the pubspec.yaml environment block so the example allows Dart 3.x; specifically change the sdk entry in the environment section (the "sdk" constraint) from '>=2.12.0 <3.0.0' to a range that permits Dart 3 (for example '>=2.12.0 <4.0.0') so it matches the parent package and the main project.sdk/runanywhere-flutter/packages/runanywhere/lib/infrastructure/download/download_service.dart-184-207 (1)
184-207:⚠️ Potential issue | 🟠 MajorEnsure file sink is always closed on stream errors/cancellation.
If
response.streamthrows,sink.flush/closeon Lines 205-206 are skipped, leaving the file handle open.✅ Proposed fix
- final file = File(destPath); - await file.create(recursive: true); - final sink = file.openWrite(); - var downloaded = 0; - - await for (final chunk in response.stream) { - sink.add(chunk); - downloaded += chunk.length; + final file = File(destPath); + await file.create(recursive: true); + final sink = file.openWrite(); + var downloaded = 0; + try { + await for (final chunk in response.stream) { + sink.add(chunk); + downloaded += chunk.length; - // Report progress proportionally across all files - final fileProgress = downloaded.toDouble() / (model.downloadSize ?? 1); - final overallProgress = (i + fileProgress) / totalFiles; - yield ModelDownloadProgress( - modelId: modelId, - bytesDownloaded: downloaded, - totalBytes: model.downloadSize ?? 0, - stage: ModelDownloadStage.downloading, - overallProgress: overallProgress * 0.9, - ); - } - - await sink.flush(); - await sink.close(); + // Report progress proportionally across all files + final fileProgress = + downloaded.toDouble() / (model.downloadSize ?? 1); + final overallProgress = (i + fileProgress) / totalFiles; + yield ModelDownloadProgress( + modelId: modelId, + bytesDownloaded: downloaded, + totalBytes: model.downloadSize ?? 0, + stage: ModelDownloadStage.downloading, + overallProgress: overallProgress * 0.9, + ); + } + await sink.flush(); + } finally { + await sink.close(); + } _logger.info('Downloaded: ${descriptor.destinationPath}');🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@sdk/runanywhere-flutter/packages/runanywhere/lib/infrastructure/download/download_service.dart` around lines 184 - 207, The file sink opened for writing (sink = file.openWrite()) can remain unclosed if response.stream throws or iteration is cancelled; wrap the stream consumption and progress-yielding block in a try/finally (or try/catch/finally) so that await sink.flush() and await sink.close() always run in the finally block; update the method that contains the response.stream loop (the download routine that yields ModelDownloadProgress for modelId/descriptor) to move the existing flush/close into finally and optionally log the error before rethrowing to preserve behavior.examples/flutter/RunAnywhereAI/windows/runner/main.cpp-18-32 (1)
18-32:⚠️ Potential issue | 🟠 MajorBalance COM initialization across all exit paths.
::CoInitializeExon line 18 is called without checking its return value, and the early return on line 31 skips::CoUninitialize. This leaves COM initialization unbalanced ifwindow.Createfails, leaking COM state. The same pattern applies to the exit path on lines 41–42.Check the HRESULT from
CoInitializeExand callCoUninitializeconditionally on all exit paths (both the failure return and the success return).🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@examples/flutter/RunAnywhereAI/windows/runner/main.cpp` around lines 18 - 32, Check the HRESULT returned by ::CoInitializeEx and, if it succeeds (S_OK or S_FALSE), ensure ::CoUninitialize is called on every exit path: when FlutterWindow::Create(...) fails and on the normal successful exit; i.e., record the result of ::CoInitializeEx at the top of main, only proceed if initialization succeeded or handle failure appropriately, and call ::CoUninitialize before each early return (including the failure after window.Create) and at the end of main so COM initialization is balanced across all paths.examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/example/windows/runner/main.cpp-18-32 (1)
18-32:⚠️ Potential issue | 🟠 MajorBalance COM lifecycle: validate initialization and ensure cleanup on all paths.
Line 18 initializes COM without validating the return value, and the early return at line 31 (when window creation fails) skips the
CoUninitializeat line 41, creating a resource leak.Recommended fix
- ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); + const HRESULT com_hr = + ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); + const bool com_initialized = SUCCEEDED(com_hr); flutter::DartProject project(L"data"); std::vector<std::string> command_line_arguments = GetCommandLineArguments(); project.set_dart_entrypoint_arguments(std::move(command_line_arguments)); FlutterWindow window(project); Win32Window::Point origin(10, 10); Win32Window::Size size(1280, 720); if (!window.Create(L"flutter_secure_storage_windows_example", origin, size)) { + if (com_initialized) { + ::CoUninitialize(); + } return EXIT_FAILURE; } window.SetQuitOnClose(true); ::MSG msg; while (::GetMessage(&msg, nullptr, 0, 0)) { ::TranslateMessage(&msg); ::DispatchMessage(&msg); } - ::CoUninitialize(); + if (com_initialized) { + ::CoUninitialize(); + } return EXIT_SUCCESS;🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/example/windows/runner/main.cpp` around lines 18 - 32, Check the return value of CoInitializeEx (store HRESULT hr = CoInitializeEx(...)) and only call CoUninitialize if initialization succeeded; ensure all early exits (e.g., when FlutterWindow::Create returns false and you currently return EXIT_FAILURE) call CoUninitialize before returning to avoid leaking COM. Concretely: modify the code around CoInitializeEx/CoUninitialize and window.Create so that you track success (hr) and on the Create failure path call CoUninitialize() if SUCCEEDED(hr) (or use a small RAII/scope guard around CoInitializeEx for automatic cleanup), and skip CoUninitialize if CoInitializeEx failed.sdk/runanywhere-commons/scripts/build-windows.bat-80-95 (1)
80-95:⚠️ Potential issue | 🟠 MajorFail the build when a requested backend DLL is missing.
If
onnxorllamacppis selected and that backend fails to emit its DLL, this script still reachesBuild complete. That turns a build failure into a later packaging/runtime failure.Proposed fix
-if exist "%BUILD_DIR%\src\backends\llamacpp\Release\rac_backend_llamacpp.dll" ( - copy /y "%BUILD_DIR%\src\backends\llamacpp\Release\rac_backend_llamacpp.dll" "%DIST_DIR%\rac_backend_llamacpp.dll" >nul -) +if /I "%BUILD_LLAMA%"=="ON" ( + if not exist "%BUILD_DIR%\src\backends\llamacpp\Release\rac_backend_llamacpp.dll" ( + echo ERROR: rac_backend_llamacpp.dll was not produced. + exit /b 1 + ) + copy /y "%BUILD_DIR%\src\backends\llamacpp\Release\rac_backend_llamacpp.dll" "%DIST_DIR%\rac_backend_llamacpp.dll" >nul + if errorlevel 1 exit /b 1 +) -if exist "%BUILD_DIR%\src\backends\onnx\Release\rac_backend_onnx.dll" ( - copy /y "%BUILD_DIR%\src\backends\onnx\Release\rac_backend_onnx.dll" "%DIST_DIR%\rac_backend_onnx.dll" >nul -) +if /I "%BUILD_ONNX%"=="ON" ( + if not exist "%BUILD_DIR%\src\backends\onnx\Release\rac_backend_onnx.dll" ( + echo ERROR: rac_backend_onnx.dll was not produced. + exit /b 1 + ) + copy /y "%BUILD_DIR%\src\backends\onnx\Release\rac_backend_onnx.dll" "%DIST_DIR%\rac_backend_onnx.dll" >nul + if errorlevel 1 exit /b 1 +)🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@sdk/runanywhere-commons/scripts/build-windows.bat` around lines 80 - 95, The script currently copies rac_backend_llamacpp.dll and rac_backend_onnx.dll but doesn't fail the build when a requested backend is missing; modify the logic so that when a backend is requested (check BUILD_ONNX=="ON" and similarly for the llamacpp selection variable if present) you verify the destination DLL exists after the copy and if it does not echo a clear error (e.g., "ERROR: missing rac_backend_onnx.dll") and exit /b 1 to fail the build; also, when BUILD_ONNX=="ON", after the for loops that copy sherpa-onnx DLLs verify at least one sherpa-onnx DLL was copied (e.g., check if any matching files exist in %DIST_DIR%) and exit with an error if none were found.sdk/runanywhere-commons/src/infrastructure/download/download_orchestrator.cpp-215-239 (1)
215-239:⚠️ Potential issue | 🟠 MajorAlign Windows model file discovery with POSIX ordering.
The Windows implementation immediately recurses or returns when it encounters a directory or model file, but the POSIX implementation first scans all regular files in the current directory before recursing into subdirectories. Since
std::filesystem::directory_iteratorhas no guaranteed order, the Windows branch can select a nested model before examining all files at the archive root, causing inconsistent model selection between platforms.Proposed fix
`#if` defined(_WIN32) std::error_code ec; + std::string found_model; + std::vector<std::string> subdirs; for (const auto& entry : std::filesystem::directory_iterator(directory, ec)) { const auto name = entry.path().filename().string(); if (name == "." || name == "..") continue; if (!name.empty() && name[0] == '.') continue; const auto full_path = entry.path().string(); if (entry.is_regular_file(ec)) { const char* dot = strrchr(name.c_str(), '.'); if (dot && is_model_extension(dot + 1)) { if (is_auxiliary_model_file(name)) { continue; } - snprintf(out_path, path_size, "%s", full_path.c_str()); - return true; + found_model = full_path; + break; } } else if (entry.is_directory(ec)) { - if (find_single_model_file(full_path.c_str(), depth + 1, max_depth, out_path, - path_size)) { - return true; - } + subdirs.push_back(full_path); } } + if (!found_model.empty()) { + snprintf(out_path, path_size, "%s", found_model.c_str()); + return true; + } + for (const auto& subdir : subdirs) { + if (find_single_model_file(subdir.c_str(), depth + 1, max_depth, out_path, path_size)) { + return true; + } + } return false; `#else`🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@sdk/runanywhere-commons/src/infrastructure/download/download_orchestrator.cpp` around lines 215 - 239, The Windows branch of find_single_model_file mirrors POSIX behavior: instead of immediately recursing or returning when encountering an entry, first iterate the directory and process all regular files (use entry.is_regular_file(ec), check name/dot/skips, use is_model_extension and is_auxiliary_model_file, set out_path and return true if found), and only after that do a second pass (or separate loop) over entries that are directories calling find_single_model_file recursively; keep using std::error_code ec and preserve the same filename/dot-skipping logic and checks (is_regular_file, is_directory, is_model_extension, is_auxiliary_model_file) so Windows will examine all files in the current directory before recursing into subdirectories.examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/example/windows/runner/flutter_window.cpp-59-63 (1)
59-63:⚠️ Potential issue | 🟠 MajorPotential null pointer dereference in
WM_FONTCHANGEhandler.
flutter_controller_may be null whenWM_FONTCHANGEis received (e.g., during window destruction or before Flutter initialization completes). The handler at line 61 dereferencesflutter_controller_without a null check, unlike the block at lines 50-57 which properly guards access.🐛 Proposed fix
switch (message) { case WM_FONTCHANGE: - flutter_controller_->engine()->ReloadSystemFonts(); + if (flutter_controller_) { + flutter_controller_->engine()->ReloadSystemFonts(); + } break; }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/example/windows/runner/flutter_window.cpp` around lines 59 - 63, The WM_FONTCHANGE case may dereference a null flutter_controller_, so update the switch handler to guard access: check that flutter_controller_ is non-null and that flutter_controller_->engine() is non-null before calling ReloadSystemFonts (same pattern used in the earlier block that checks flutter_controller_). If either is null, skip calling ReloadSystemFonts (break/return) to avoid a crash.examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/example/integration_test/app_test.dart-20-39 (1)
20-39:⚠️ Potential issue | 🟠 MajorThese tests can touch files outside their sandbox.
cleanUpFilesremoves every.securefile from the real parent directory, and the'/../a'case probes traversal against the real storage root. Running this suite on a workstation can delete or create sibling-app data instead of only test artifacts.Also applies to: 1036-1040
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/example/integration_test/app_test.dart` around lines 20 - 39, The cleanup helper cleanUpFiles is deleting .secure files from the real application support parent directory and tests use '/../a' which can traverse out of the sandbox; change the tests and cleanUpFiles to operate in a dedicated temp/test-specific directory (e.g., create a unique subdirectory under getTemporaryDirectory() or a uniquely-named subfolder inside getApplicationSupportDirectory()) and only delete files inside that directory (never walk directory.parent); update any test cases that currently use '/../a' to construct paths confined to that test directory (or use a mocked filesystem) so the code under test cannot touch sibling or real app data; ensure references to cleanUpFiles, the test that constructs '/../a', and any file deletion logic are updated accordingly.examples/flutter/RunAnywhereAI/lib/core/models/proxy_settings.dart-52-69 (1)
52-69:⚠️ Potential issue | 🟠 MajorAllow
copyWithto clearport.
port ?? this.portmakescopyWith(port: null)a no-op, so a user who clears the port field keeps the previous port and the proxy can stay erroneously configured.Suggested fix
class ProxySettings { + static const Object _unset = Object(); + final bool enabled; final ProxyScheme scheme; final String host; final int? port; @@ ProxySettings copyWith({ bool? enabled, ProxyScheme? scheme, String? host, - int? port, + Object? port = _unset, String? username, String? password, bool? bypassLocal, }) { return ProxySettings( enabled: enabled ?? this.enabled, scheme: scheme ?? this.scheme, host: host ?? this.host, - port: port ?? this.port, + port: identical(port, _unset) ? this.port : port as int?, username: username ?? this.username, password: password ?? this.password, bypassLocal: bypassLocal ?? this.bypassLocal, ); }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@examples/flutter/RunAnywhereAI/lib/core/models/proxy_settings.dart` around lines 52 - 69, The copyWith currently treats port: port ?? this.port so calling copyWith(port: null) cannot clear the port; update ProxySettings.copyWith to accept an explicit clear flag (e.g. add parameter bool clearPort = false) and set the returned port using port: clearPort ? null : (port ?? this.port) so callers can clear the port by passing clearPort: true, while preserving existing behavior for other fields; modify ProxySettings.copyWith signature and the returned ProxySettings initializer accordingly.examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/lib/src/flutter_secure_storage_windows_ffi.dart-148-150 (1)
148-150:⚠️ Potential issue | 🟠 MajorAwait the legacy delete before returning from
write().Right now
write()can complete before the backward-compatibility cleanup does. Callers can then observe stale legacy data, and any delete failure becomes detached from the write operation.Proposed fix
if (options.useBackwardCompatibility) { // Clear old entry. - _backwardCompatible.delete(key: key, options: options); + await _backwardCompatible.delete(key: key, options: options); } }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/lib/src/flutter_secure_storage_windows_ffi.dart` around lines 148 - 150, The write() implementation currently triggers the legacy cleanup via _backwardCompatible.delete(key: key, options: options) but does not await it, allowing write() to return before the legacy entry is removed; update write() to await the future returned by _backwardCompatible.delete (or return that awaited result) so the backward-compatibility deletion completes (and any errors surface) before write() completes—locate the write() method and replace the fire-and-forget call to _backwardCompatible.delete(...) with an awaited call.examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/test/unit_test.dart-866-917 (1)
866-917:⚠️ Potential issue | 🟠 MajorGuard these two tests with
withFfi(...)too.These are the only storage tests here that still execute the FFI-backed implementation without the Windows gate. That leaves Linux/macOS CI running Windows-specific code even though the rest of the suite is explicitly skipped there.
Proposed fix
test( 'write - new', - () async { + () => withFfi(() async { const key = 'KEY'; const value = 'VALUE'; var deleteCalled = 0; final target = createTarget((call) async { @@ final result = await target.read(key: key, options: options); expect(result, value); expect(deleteCalled, 2); - }, + }), ); test( 'write - overwrite', - () async { + () => withFfi(() async { const key = 'KEY'; const value1 = 'VALUE1'; const value2 = 'VALUE2'; @@ final result = await target.read(key: key, options: options); expect(result, value2); expect(deleteCalled, 3); - }, + }), );🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/test/unit_test.dart` around lines 866 - 917, Wrap the two tests titled 'write - new' and 'write - overwrite' with the FFI gate by calling withFfi(() async { ... }) around their test bodies so they only run the FFI-backed implementation on Windows; locate the tests using their names and ensure the existing calls to createTarget, createOptions, target.write, and target.read are moved inside the withFfi closure (preserving async/await), e.g. replace each test body with: withFfi(() async { /* existing body */ });.examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/lib/src/flutter_secure_storage_windows_ffi.dart-360-374 (1)
360-374:⚠️ Potential issue | 🟠 MajorDo not retry every
FileSystemExceptionforever.This loop never exits for permanent failures like access denied, locked files, or an invalid path, so one bad write can hang the caller indefinitely. Restrict retries to the specific missing-file race you want to tolerate, and cap the attempts.
Proposed fix
- // Loop to handle race condition. - while (true) { + for (var attempt = 0; attempt < 3; attempt++) { try { await (await file.create(recursive: true)) .writeAsBytes(encryptedText, flush: true); - // If success, finish loop. - break; + break; } on FileSystemException catch (e) { - // Another process has been deleted a file or parent directory - // since previous File.create() call. - // We will retry writing. - debugPrint( - 'Reading file has been deleted by another process. $e', - ); + final code = e.osError?.errorCode; + final isMissingPathRace = + code == ERROR_FILE_NOT_FOUND || code == ERROR_PATH_NOT_FOUND; + if (!isMissingPathRace || attempt == 2) { + rethrow; + } } }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/lib/src/flutter_secure_storage_windows_ffi.dart` around lines 360 - 374, The infinite retry loop around file.create()/writeAsBytes should be changed to only retry the race-condition where the file or parent directory was removed and to stop after a small maxAttempts; replace the while(true) with a bounded retry (e.g., maxAttempts = 3) and on FileSystemException inspect the exception (e.g., e.osError?.errorCode or e.message) and only retry for filesystem-not-found/missing-parent conditions, otherwise rethrow or return the error after logging; reference the current variables and calls (file.create, writeAsBytes, encryptedText, FileSystemException catch (e), debugPrint) to locate and update the loop and error handling.
🟡 Minor comments (11)
examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/analysis_options.yaml-1-1 (1)
1-1:⚠️ Potential issue | 🟡 MinorUse
package:flutter_lintsfor consistency with the plugin's example app.The plugin's
analysis_options.yamlimportspackage:lint/package.yaml, while its bundled example app importspackage:flutter_lints/flutter.yaml. Althoughlintis properly declared in dev_dependencies,package:flutter_lintsis the recommended choice for Flutter plugins and is already in use within the same package. This inconsistency should be resolved by aligning both to usepackage:flutter_lints.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/analysis_options.yaml` at line 1, Replace the include in analysis_options.yaml that currently references "package:lint/package.yaml" with the Flutter-recommended lint bundle used elsewhere in this package; update the include to use "package:flutter_lints/flutter.yaml" so the plugin and its example app use the same lint configuration.examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/README.md-3-3 (1)
3-3:⚠️ Potential issue | 🟡 MinorCapitalize “Windows” in the package description.
This is a minor docs polish issue in user-facing text.
✏️ Suggested fix
-The windows implementation of [`flutter_secure_storage`][1]. +The Windows implementation of [`flutter_secure_storage`][1].🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/README.md` at line 3, The package description currently reads "The windows implementation of [`flutter_secure_storage`][1]." — update that text to capitalize "Windows" (i.e., "The Windows implementation of [`flutter_secure_storage`][1].") so the OS name is properly capitalized in the README package description.sdk/runanywhere-commons/tests/test_download_orchestrator.cpp-223-240 (1)
223-240:⚠️ Potential issue | 🟡 MinorThis test can pass without proving that
mmprojis actually skipped.Because both files live in the same directory, a broken implementation still passes whenever the primary
.ggufhappens to be enumerated first. Please add a deterministic negative case as well—e.g. onlymmproj-*.ggufpresent and assert it is rejected/falls back—or drive the lower-level selector with a stable candidate list.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@sdk/runanywhere-commons/tests/test_download_orchestrator.cpp` around lines 223 - 240, The test currently can pass spuriously because directory enumeration order may favor the primary .gguf; update the test in test_download_orchestrator.cpp to include a deterministic negative case: create a directory containing only an mmproj-*.gguf (using write_dummy_file) and call rac_find_model_path_after_extraction expecting it to reject/fail or return the fallback behavior, and assert that the returned path does not select the mmproj file; alternatively modify the test to drive the lower-level selector with a stable candidate list so you deterministically verify that rac_find_model_path_after_extraction ignores files whose basename starts with "mmproj-".docs/building.md-146-157 (1)
146-157:⚠️ Potential issue | 🟡 MinorDocument the Windows toolchain prerequisites in this section.
The new workflow assumes
fvm flutter build windowscan run, but the file's prerequisite list is still Android-only. Please add the required Windows setup here (at least Visual Studio Desktop C++, Flutter desktop enabled, and FVM if it's mandatory), otherwise readers will fail before the first command.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/building.md` around lines 146 - 157, Update the "Windows Voice Validation" section to list required Windows toolchain prerequisites so readers can run the shown commands; specifically mention installing Visual Studio with the "Desktop development with C++" workload (including MSVC, Windows 10/11 SDK and CMake), enabling Flutter desktop support (flutter config --enable-windows-desktop), and using FVM if the instructions rely on it (or note it as optional) before running the shown commands (references: the "Windows Voice Validation" heading and the fvm flutter build windows command).examples/flutter/RunAnywhereAI/lib/app/runanywhere_ai_app.dart-339-341 (1)
339-341:⚠️ Potential issue | 🟡 MinorKeep these backend fallback logs platform-aware.
This widget still runs on non-Windows targets, so hardcoding
"Windows optional"/"this Windows build"will mislabel Android/iOS/macOS failures and send debugging in the wrong direction. Please make the message platform-aware or keep it generic.Also applies to: 349-349
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@examples/flutter/RunAnywhereAI/lib/app/runanywhere_ai_app.dart` around lines 339 - 341, The backend fallback logs currently hardcode "Windows" in the debugPrint messages inside the ONNX registration try/catch (the debugPrint call that logs '✅ ONNX backend registered (Windows optional)' and the catch that logs '⚠️ ONNX backend not available on this Windows build: $e'); update these messages to be platform-aware or generic by using the runtime platform identifier (e.g., Platform.operatingSystem or similar) or by removing the OS name so the message applies to all targets, and apply the same change to the other debugPrint at the second occurrence noted in the diff.sdk/runanywhere-flutter/packages/runanywhere/lib/public/runanywhere.dart-26-27 (1)
26-27:⚠️ Potential issue | 🟡 MinorRemove the duplicate
dart_bridge_structured_output.dartimport at line 30.The same library is imported twice—once at line 27 and again at line 30. This triggers
duplicate_importin analyzer-enforced builds and should be removed.Suggested fix
import 'package:runanywhere/native/dart_bridge_rag.dart'; import 'package:runanywhere/native/dart_bridge_structured_output.dart'; import 'package:runanywhere/native/dart_bridge_vlm.dart'; import 'package:runanywhere/native/ffi_types.dart' show RacVlmImageFormat; -import 'package:runanywhere/native/dart_bridge_structured_output.dart'; import 'package:runanywhere/public/configuration/sdk_environment.dart';🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@sdk/runanywhere-flutter/packages/runanywhere/lib/public/runanywhere.dart` around lines 26 - 27, Remove the duplicate import of the same library: keep a single import of 'package:runanywhere/native/dart_bridge_structured_output.dart' and delete the redundant import line that re-imports it (the duplicate alongside 'package:runanywhere/native/dart_bridge_rag.dart'); ensure only one import statement for dart_bridge_structured_output.dart remains to resolve the duplicate_import analyzer error.sdk/runanywhere-flutter/packages/runanywhere/lib/infrastructure/download/download_service.dart-194-201 (1)
194-201:⚠️ Potential issue | 🟡 MinorMulti-file progress calculation is inconsistent.
Current math combines file index with byte ratio, so overall progress can be significantly underreported. Track cumulative downloaded bytes across files and derive progress from total bytes instead.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@sdk/runanywhere-flutter/packages/runanywhere/lib/infrastructure/download/download_service.dart` around lines 194 - 201, The overallProgress math is incorrect: instead of combining the file index with the per-file byte ratio, track cumulative bytes downloaded across files and compute overall progress as cumulativeDownloadedBytes / totalBytes. In the download loop (where fileProgress, overallProgress and ModelDownloadProgress are created) add/maintain a cumulativeDownloadedBytes variable and compute totalBytes by summing model.downloadSize (falling back to 0) for all files; then set overallProgress = (cumulativeDownloadedBytes + downloaded) / totalBytes (and apply the existing *0.9 if you want to keep the stage scaling) when yielding ModelDownloadProgress so progress reflects actual bytes downloaded across all files.examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/lib/src/flutter_secure_storage_windows_stub.dart-7-13 (1)
7-13:⚠️ Potential issue | 🟡 Minor
registerWith()will trigger assertion failure in debug mode.The constructor contains
assert(false, 'Cannot instantiate this class.')(line 8), butregisterWith()(line 12) calls the constructor. In debug mode, this causes an assertion failure during plugin registration on non-Windows platforms where this stub is used.This contradicts the stub's design: all methods are implemented as working no-ops (returning
false, empty futures, etc.), suggesting the class is meant to be usable. The assert prevents this in debug mode.Remove the assert since the stub is designed to function as a no-op fallback:
Suggested fix
FlutterSecureStorageWindows() - : assert(false, 'Cannot instantiate this class.'); + ; // Stub implementation for non-Windows platforms🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/lib/src/flutter_secure_storage_windows_stub.dart` around lines 7 - 13, The FlutterSecureStorageWindows constructor currently contains assert(false, 'Cannot instantiate this class.'), which causes registerWith() to fail in debug mode when it sets FlutterSecureStoragePlatform.instance = FlutterSecureStorageWindows(); remove the assert and make the constructor a normal no-op constructor so the stub can be instantiated during plugin registration (update the FlutterSecureStorageWindows() constructor and leave registerWith() and FlutterSecureStoragePlatform.instance assignment unchanged).examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/example/lib/main.dart-150-196 (1)
150-196:⚠️ Potential issue | 🟡 MinorRemove the first
FutureBuilder; the result is rendered twice.Both builders listen to the same
_future, and the second one already rendersResultplusDetail. Keeping both produces duplicate result fields and duplicate spinners after each action.Proposed fix
- if (_future != null) - FutureBuilder<TestResult>( - builder: (context, snapshot) { - if (!snapshot.hasData && !snapshot.hasError) { - return const CircularProgressIndicator(); - } - - resultSummaryFieldController.text = - (snapshot.data?.success ?? false) ? 'SUCCESS' : 'FAIL'; - - return TextField( - controller: resultSummaryFieldController, - decoration: const InputDecoration(label: Text('Result')), - ); - }, - future: _future, - ), - if (_future != null) FutureBuilder<TestResult>(🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/example/lib/main.dart` around lines 150 - 196, Remove the duplicated FutureBuilder: delete the first FutureBuilder<TestResult> block that reads/writes resultSummaryFieldController and uses future _future, and keep the second FutureBuilder<TestResult> which already renders both the "Result" and "Detail" TextFields and handles the loading/error states; ensure only one FutureBuilder referencing _future remains and that it updates resultSummaryFieldController and resultDetailFieldController as in the existing second block.examples/flutter/RunAnywhereAI/lib/core/services/proxy_settings_service.dart-120-135 (1)
120-135:⚠️ Potential issue | 🟡 MinorPersist normalized credentials, not the raw strings.
validate()treats' 'as empty, butsave()usessettings.username.isNotEmptyand will still store whitespace-only credentials. That can silently enable proxy auth with blank-looking values.Proposed fix
+ final trimmedUsername = settings.username.trim(); + final trimmedPassword = settings.password.trim(); + - if (settings.username.isNotEmpty) { + if (trimmedUsername.isNotEmpty) { await KeychainHelper.saveString( key: _usernameKey(scope), - data: settings.username, + data: trimmedUsername, ); await KeychainHelper.saveString( key: _passwordKey(scope), - data: settings.password, + data: trimmedPassword, ); } else { await KeychainHelper.delete(_usernameKey(scope)); await KeychainHelper.delete(_passwordKey(scope)); }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@examples/flutter/RunAnywhereAI/lib/core/services/proxy_settings_service.dart` around lines 120 - 135, The save() flow currently checks settings.username.isNotEmpty and persists raw strings, so whitespace-only credentials slip through; update save() to use normalized values (e.g., trimmed username/password or whatever validate() uses) before checking emptiness, persist those normalized strings via KeychainHelper.saveString(key: _usernameKey(scope)/_passwordKey(scope)), delete keys when the normalized values are empty, and update _current[scope] to store the normalized ProxySettings so that ProxySettingsValidationResult.valid() and subsequent logic see the cleaned credentials.examples/flutter/RunAnywhereAI/windows/runner/win32_window.cpp-109-111 (1)
109-111:⚠️ Potential issue | 🟡 MinorCheck return value of
UnregisterClassbefore updating state.
UnregisterClassmay fail for various reasons (e.g., if the module handle or class name doesn't match). Currently,class_registered_is set tofalseunconditionally at line 111, even if unregistration fails. This causes the registrar state to diverge from the OS state, breaking subsequent re-registration attempts. The nullptr parameter is correct and equivalent toGetModuleHandle(nullptr)for EXE applications.Proposed fix
void WindowClassRegistrar::UnregisterWindowClass() { - UnregisterClass(kWindowClassName, nullptr); - class_registered_ = false; + if (UnregisterClass(kWindowClassName, nullptr) != 0) { + class_registered_ = false; + } }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@examples/flutter/RunAnywhereAI/windows/runner/win32_window.cpp` around lines 109 - 111, The UnregisterWindowClass implementation should check the BOOL result of UnregisterClass(kWindowClassName, nullptr) and only set class_registered_ = false when UnregisterClass returns nonzero; if it fails, preserve class_registered_ and handle/log the failure (e.g., call GetLastError() and record the error) so the registrar state stays consistent; update the WindowClassRegistrar::UnregisterWindowClass method to perform this check and error handling.
🧹 Nitpick comments (5)
examples/flutter/RunAnywhereAI/lib/features/models/model_selection_sheet.dart (1)
2-2: Consider usingPlatformCapabilityServicefor consistency.Other parts of this PR (e.g.,
chat_interface_view.dart) usePlatformCapabilityService.sharedto gate features by platform. UsingPlatform.isWindowsdirectly here works but creates two different patterns for platform capability checks.For consistency and centralized control over platform-specific feature availability, consider using the
PlatformCapabilityServiceapproach:if (widget.context == ModelSelectionContext.tts && PlatformCapabilityService.shared.supportsTextToSpeech)This would require adding a
supportsSystemTTScapability or checkingsupportsTextToSpeechappropriately.Also applies to: 242-243
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@examples/flutter/RunAnywhereAI/lib/features/models/model_selection_sheet.dart` at line 2, Replace direct Platform.isWindows checks in ModelSelectionSheet (where widget.context == ModelSelectionContext.tts) with the centralized PlatformCapabilityService check used elsewhere (PlatformCapabilityService.shared.supportsTextToSpeech or a newly-added supportsSystemTTS flag); update the conditional(s) in the ModelSelectionSheet widget (and any helper methods inside that file) to use PlatformCapabilityService.shared instead of dart:io Platform, and add a capability accessor to PlatformCapabilityService if needed so the feature gating is consistent with chat_interface_view.dart.examples/flutter/RunAnywhereAI/lib/features/voice/text_to_speech_view.dart (1)
130-157: Consider showing loading state during voice refresh.The
_refreshModelStatemethod awaitssdk.RunAnywhere.currentTTSVoice()without setting any loading indicator. For quick responses this is fine, but if the SDK call takes noticeable time, users may see a brief inconsistent state. This is a minor UX consideration rather than a bug.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@examples/flutter/RunAnywhereAI/lib/features/voice/text_to_speech_view.dart` around lines 130 - 157, Add a loading flag during the voice refresh in _refreshModelState: introduce a boolean state field (e.g. _isRefreshingVoice or reuse an existing _isLoading) and set it to true with setState before awaiting sdk.RunAnywhere.currentTTSVoice(), then set it back to false (inside setState) after the await (or early returns), preserving mounted checks; update the existing setState blocks to include this flag so the UI can show a loading indicator while the SDK call is in progress.sdk/runanywhere-flutter/packages/runanywhere_llamacpp/windows/runanywhere_llamacpp_plugin.cpp (1)
40-50: Incomplete version string for pre-Windows 7 systems.If
IsWindows7OrGreater()returns false (unlikely but possible),version_stream.str()will be just"Windows "with no version identifier. Consider adding anelsefallback for completeness.💡 Proposed fix
} else if (IsWindows7OrGreater()) { version_stream << "7"; + } else { + version_stream << "Unknown"; }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@sdk/runanywhere-flutter/packages/runanywhere_llamacpp/windows/runanywhere_llamacpp_plugin.cpp` around lines 40 - 50, The getPlatformVersion handler builds a version string using IsWindows10OrGreater / IsWindows8OrGreater / IsWindows7OrGreater but lacks a fallback when all are false, producing just "Windows ". Update the logic in the method handling "getPlatformVersion" (the block using version_stream and those IsWindows* functions) to append a sensible fallback like "Unknown" or "pre-7" in an added else branch so result->Success(flutter::EncodableValue(version_stream.str())) always returns a complete string.sdk/runanywhere-flutter/packages/runanywhere/windows/runanywhere_plugin.cpp (1)
40-50: Same edge case: incomplete version string for pre-Windows 7.Same issue as in
runanywhere_llamacpp_plugin.cpp—consider adding anelsefallback for pre-Windows 7 systems.💡 Proposed fix
} else if (IsWindows7OrGreater()) { version_stream << "7"; + } else { + version_stream << "Unknown"; }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@sdk/runanywhere-flutter/packages/runanywhere/windows/runanywhere_plugin.cpp` around lines 40 - 50, The getPlatformVersion handler currently builds version_stream in runanywhere_plugin.cpp but lacks a fallback for pre-Windows 7, causing an incomplete string; update the if/else chain in the method that checks method_call.method_name() == "getPlatformVersion" (the block using IsWindows10OrGreater(), IsWindows8OrGreater(), IsWindows7OrGreater() and result->Success(flutter::EncodableValue(version_stream.str()))) to add a final else that appends an appropriate fallback (e.g., "earlier than 7" or the actual version number) before calling result->Success so older Windows versions return a meaningful string.examples/flutter/RunAnywhereAI/lib/features/settings/combined_settings_view.dart (1)
360-399: Consider dismissing the dialog before showing save-success snackbar.The current flow shows the success snackbar (lines 388-394) before dismissing the dialog (line 397). If
dialogContext.mountedis false butcontext(outer widget) is still mounted, the snackbar will appear while the dialog is still visible, which may cause a brief visual overlap.This is a minor UX consideration and the code is functionally correct.
💡 Optional: Dismiss dialog before showing success snackbar
await _loadProxyConfiguration(scope); + if (dialogContext.mounted) { + Navigator.pop(dialogContext); + } + if (mounted) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text('${scope.displayName} settings saved'), ), ); } - - if (dialogContext.mounted) { - Navigator.pop(dialogContext); - } },🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@examples/flutter/RunAnywhereAI/lib/features/settings/combined_settings_view.dart` around lines 360 - 399, The success snackbar is shown while the dialog is still open; dismiss the dialog first to avoid overlap by calling Navigator.pop(dialogContext) (guarded by dialogContext.mounted) before showing the success SnackBar via ScaffoldMessenger.of(context). Ensure you still await _loadProxyConfiguration(scope) as needed, keep the mounted checks for the outer context, and only show the SnackBar after the dialog has been popped.
| if (freopen_s(&unused, "CONOUT$", "w", stdout)) { | ||
| _dup2(_fileno(stdout), 1); | ||
| } | ||
| if (freopen_s(&unused, "CONOUT$", "w", stderr)) { | ||
| _dup2(_fileno(stdout), 2); | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# First, locate the file
find . -name "utils.cpp" -path "*/flutter_secure_storage_windows/example/windows/runner/*"Repository: RunanywhereAI/runanywhere-sdks
Length of output: 177
🏁 Script executed:
# If found, read the relevant lines
if [ -f "examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/example/windows/runner/utils.cpp" ]; then
cat -n "examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/example/windows/runner/utils.cpp" | head -30
fiRepository: RunanywhereAI/runanywhere-sdks
Length of output: 1034
🌐 Web query:
freopen_s MSVC CRT return value success failure
💡 Result:
In MSVC CRT, freopen_s returns 0 on success. On failure, it returns a non-zero error code (errno_t). If an error occurs, it also writes NULL to the stream pointer (unless the stream pointer is NULL) and sets errno to an appropriate error value such as EINVAL for invalid parameters. Example usage: errno_t err = freopen_s(&stream, "file.txt", "w", stderr); if (err != 0) { // Handle error } This behavior is consistent across Microsoft documentation, with code samples checking for err != 0 to detect failure.
Citations:
- 1: https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/freopen-s-wfreopen-s?view=msvc-170
- 2: http://msdn.microsoft.com/en-us/library/88k7d7a7%28v=vs.110%29
- 3: http://msdn.microsoft.com/en-us/library/88k7d7a7(v=vs.90)
- 4: https://learn.microsoft.com/en-us/previous-versions/88k7d7a7(v=vs.140)
🌐 Web query:
_dup2 function Windows MSVC parameters file descriptor
💡 Result:
The _dup2 function in Windows MSVC (Microsoft Visual C++) duplicates a file descriptor. It reassigns file descriptor fd2 to refer to the same open file as fd1. Syntax: int _dup2(int fd1, int fd2); Parameters: - fd1: File descriptor referring to an open file (source). - fd2: Any file descriptor (target, which will be reassigned; if already open, it is closed first unless fd1 == fd2). Return value: 0 on success, -1 on error (sets errno to EBADF for invalid descriptor or EMFILE if no descriptors available). Include <io.h>. Use _fileno to get file descriptor from FILE*. Note: POSIX dup2 is deprecated; use _dup2. Not supported in UWP apps without redirection.
Citations:
- 1: https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/dup-dup2?view=msvc-170
- 2: http://msdn.microsoft.com/en-us/library/8syseb29(v=vs.110)
- 3: https://github.com/MicrosoftDocs/cpp-docs/blob/main/docs/c-runtime-library/reference/dup-dup2.md
- 4: https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/posix-dup-dup2?view=msvc-170
Fix console redirection logic: inverted condition checks and incorrect stderr duplication.
freopen_s returns 0 on success, but the conditions execute _dup2 only on failure. Additionally, line 17 duplicates stdout into fd 2 instead of stderr. Both issues prevent proper console output redirection.
Proposed fix
- if (freopen_s(&unused, "CONOUT$", "w", stdout)) {
+ if (freopen_s(&unused, "CONOUT$", "w", stdout) == 0) {
_dup2(_fileno(stdout), 1);
}
- if (freopen_s(&unused, "CONOUT$", "w", stderr)) {
- _dup2(_fileno(stdout), 2);
+ if (freopen_s(&unused, "CONOUT$", "w", stderr) == 0) {
+ _dup2(_fileno(stderr), 2);
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| if (freopen_s(&unused, "CONOUT$", "w", stdout)) { | |
| _dup2(_fileno(stdout), 1); | |
| } | |
| if (freopen_s(&unused, "CONOUT$", "w", stderr)) { | |
| _dup2(_fileno(stdout), 2); | |
| } | |
| if (freopen_s(&unused, "CONOUT$", "w", stdout) == 0) { | |
| _dup2(_fileno(stdout), 1); | |
| } | |
| if (freopen_s(&unused, "CONOUT$", "w", stderr) == 0) { | |
| _dup2(_fileno(stderr), 2); | |
| } |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In
`@examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/example/windows/runner/utils.cpp`
around lines 13 - 18, The console redirection logic is inverted and duplicates
the wrong stream: change both freopen_s condition checks to test for success
(compare return value == 0) instead of truthy, and when reopening stderr use
_dup2(_fileno(stderr), 2) (not duplicating stdout into fd 2). Update the blocks
around freopen_s(&unused, "CONOUT$", "w", stdout) and freopen_s(&unused,
"CONOUT$", "w", stderr) accordingly so successful reopen leads to the
corresponding _dup2 call for stdout and stderr.
| std::optional<std::string> GetStringArg( | ||
| const std::string ¶m, | ||
| const flutter::EncodableMap *args); | ||
|
|
||
| // Derive the key for a value given a method argument map. | ||
| std::optional<std::string> FlutterSecureStorageWindowsPlugin::GetValueKey(const flutter::EncodableMap *args); |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
Is a member declaration like std::optionalstd::string ClassName::method(...);valid inside a C++ class definition, or is theClassName:: extra qualification rejected by the language?
💡 Result:
No, a member declaration like std::optionalstd::string ClassName::method(...); is not valid inside a C++ class definition. The ClassName:: extra qualification is rejected by the language.
Citations:
- 1: https://stackoverflow.com/questions/48255775/c-qualified-name-is-not-allowed-in-member-declaration
- 2: https://stackoverflow.com/questions/7111957/is-qualified-name-in-the-member-function-declaration-allowed
- 3: https://learn.microsoft.com/en-us/cpp/error-messages/compiler-errors-2/compiler-error-c2838?view=msvc-170
- 4: https://stackoverflow.com/questions/67580854/qualified-name-is-not-allowed-in-c-already-compiling-in-c-17
Remove the extra qualification from the member declaration.
FlutterSecureStorageWindowsPlugin::GetValueKey(...) is declared inside the class body. C++ does not allow qualified names in member declarations—this is a compilation error. Change the declaration to GetValueKey(...) without the class scope prefix.
Suggested fix
- std::optional<std::string> FlutterSecureStorageWindowsPlugin::GetValueKey(const flutter::EncodableMap *args);
+ std::optional<std::string> GetValueKey(const flutter::EncodableMap *args);📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| std::optional<std::string> GetStringArg( | |
| const std::string ¶m, | |
| const flutter::EncodableMap *args); | |
| // Derive the key for a value given a method argument map. | |
| std::optional<std::string> FlutterSecureStorageWindowsPlugin::GetValueKey(const flutter::EncodableMap *args); | |
| std::optional<std::string> GetStringArg( | |
| const std::string ¶m, | |
| const flutter::EncodableMap *args); | |
| // Derive the key for a value given a method argument map. | |
| std::optional<std::string> GetValueKey(const flutter::EncodableMap *args); |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In
`@examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/windows/flutter_secure_storage_windows_plugin.cpp`
around lines 50 - 55, The member declaration for GetValueKey in the class
incorrectly uses a qualified name; remove the class scope qualifier so the
declaration becomes a plain member declaration. Locate
FlutterSecureStorageWindowsPlugin::GetValueKey(...) in the class body and change
it to GetValueKey(const flutter::EncodableMap *args); also verify the nearby
helper declaration GetStringArg(...) remains unchanged and that the definition
outside the class still uses the qualified name
FlutterSecureStorageWindowsPlugin::GetValueKey(...) to match.
| SHGetKnownFolderPath(FOLDERID_RoamingAppData,KF_FLAG_DEFAULT,NULL,&appdataPath); | ||
|
|
||
| if (nameBuffer == NULL) { | ||
| return ERROR_OUTOFMEMORY; | ||
| } | ||
|
|
||
| resVal = GetModuleFileName(NULL,nameBuffer,MAX_PATH); | ||
| if (resVal == 0) { | ||
| return GetLastError(); | ||
| } | ||
|
|
||
| versionInfoSize = GetFileVersionInfoSize(nameBuffer, NULL); | ||
| if (versionInfoSize != 0) { | ||
| infoBuffer = (char*) calloc(versionInfoSize,sizeof(char)); | ||
| if (infoBuffer == NULL) { | ||
| return ERROR_OUTOFMEMORY; | ||
| } | ||
| if (GetFileVersionInfo(nameBuffer, 0, versionInfoSize, infoBuffer) == 0) { | ||
| free(infoBuffer); | ||
| infoBuffer = NULL; | ||
| } | ||
| else { | ||
|
|
||
| if (VerQueryValue(infoBuffer, TEXT("\\StringFileInfo\\040904e4\\CompanyName"), &queryVal, &queryLen) != 0) { | ||
| companyName = SanitizeDirString(std::wstring((const TCHAR*)queryVal)); | ||
| } | ||
| else if (VerQueryValue(infoBuffer, TEXT("\\StringFileInfo\\040904b0\\CompanyName"), &queryVal, &queryLen) != 0) { | ||
| companyName = SanitizeDirString(std::wstring((const TCHAR*)queryVal)); | ||
| } | ||
| else { | ||
| companyName = L"placeholder_company"; | ||
| } | ||
| if (VerQueryValue(infoBuffer, TEXT("\\StringFileInfo\\040904e4\\ProductName"), &queryVal, &queryLen) != 0) { | ||
| productName = SanitizeDirString(std::wstring((const TCHAR*)queryVal)); | ||
| } | ||
| else if (VerQueryValue(infoBuffer, TEXT("\\StringFileInfo\\040904b0\\ProductName"), &queryVal, &queryLen) != 0) { | ||
| productName = SanitizeDirString(std::wstring((const TCHAR*)queryVal)); | ||
| } | ||
| else { | ||
| productName = L"placeholder_product"; | ||
| } | ||
| } | ||
| stream << appdataPath << "\\" << companyName << "\\" << productName; | ||
| path = stream.str(); | ||
| } | ||
| else { | ||
| return GetLastError(); | ||
| } | ||
| return ERROR_SUCCESS; |
There was a problem hiding this comment.
Don't return success with an empty app-support path.
If GetFileVersionInfo(...) fails, companyName and productName stay empty but the function still returns ERROR_SUCCESS and hands callers a path rooted at roaming AppData. Downstream ReadAll/DeleteAll then enumerate or delete unrelated .secure files outside this app.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In
`@examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/windows/flutter_secure_storage_windows_plugin.cpp`
around lines 216 - 264, The function currently proceeds to build and return an
app-support path even when GetFileVersionInfo fails, leaving
companyName/productName empty and returning ERROR_SUCCESS; change the control
flow in the block that calls GetFileVersionInfo so that if GetFileVersionInfo
returns 0 you free infoBuffer (if allocated) and return a failure code (e.g.,
GetLastError() or ERROR_RESOURCE_DATA_NOT_FOUND) instead of falling through;
ensure companyName and productName are only used after successful VerQueryValue
checks (or set to safe placeholders only after successful version info parsing),
and keep the existing cleanup (free(infoBuffer)) before every early return to
avoid leaks — look for the GetFileVersionInfo call, the infoBuffer allocation,
the VerQueryValue branches, the stream/ path construction, and the final return
ERROR_SUCCESS to implement this fix.
| catch (DWORD e) | ||
| { | ||
| auto str_code = this->GetErrorString(e); | ||
| result->Error("Exception encountered: " + str_code, method); | ||
| } |
There was a problem hiding this comment.
Catch the exceptions this file actually throws.
Write() throws std::runtime_error, but HandleMethodCall only catches DWORD. Any write-side failure will therefore escape the method channel handler and can terminate the runner instead of surfacing a Dart error.
Suggested fix
catch (DWORD e)
{
auto str_code = this->GetErrorString(e);
result->Error("Exception encountered: " + str_code, method);
}
+ catch (const std::exception& e)
+ {
+ result->Error("Exception encountered", e.what());
+ }
+ catch (...)
+ {
+ result->Error("Exception encountered", "unknown native exception");
+ }
}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In
`@examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/windows/flutter_secure_storage_windows_plugin.cpp`
around lines 448 - 452, The catch in HandleMethodCall currently only catches
DWORD but Write() throws std::runtime_error (and other std::exception types), so
change the error handling around the Write() call in HandleMethodCall to also
catch std::exception (e.g., catch (const std::exception& e)) and convert
e.what() into a Dart error via result->Error (similar to how the DWORD path uses
GetErrorString), and optionally keep a separate catch for DWORD to preserve
GetErrorString semantics; ensure both paths call result->Error with the method
name and an informative message so C++ exceptions are surfaced to Dart instead
of escaping.
| if (!PathExists(appSupportPath)) { | ||
| MakePath(appSupportPath); | ||
| } | ||
| fs = std::basic_ofstream<BYTE>(appSupportPath + L"\\" + std::wstring(key.begin(), key.end()) + L".secure", std::ios::binary | std::ios::trunc); |
There was a problem hiding this comment.
Sanitize or encode keys before using them as filenames.
key is concatenated directly into ...\\${key}.secure. Inputs like ../foo or a\\b can escape the app-support directory and turn write, read, delete, and containsKey into arbitrary file operations.
Also applies to: 615-615, 798-798, 880-880
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In
`@examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/windows/flutter_secure_storage_windows_plugin.cpp`
at line 549, The code uses the raw key string to build filenames (e.g., the
ofstream call at the shown diff) which allows path traversal; create and use a
sanitizer/encoder for keys (e.g., sanitizeKeyToFilename or encodeKeyForStorage)
and call it from the write/read/delete/containsKey implementations so all file
ops use the sanitized name. The sanitizer should reject or normalize path
separators and ".." (replace '\' and '/' with '_' or percent-encode them),
restrict to a safe char set (or alternatively use a stable hash like SHA256 hex
of the key as the filename and store a mapping if needed), and then construct
the path using the sanitized/hashed result instead of std::wstring(key.begin(),
key.end()).
| hFile = FindFirstFile((appSupportPath + L"\\*.secure").c_str(), &searchRes); | ||
| if (hFile == INVALID_HANDLE_VALUE) { | ||
| return flutter::EncodableMap(); | ||
| } |
There was a problem hiding this comment.
Don't skip legacy storage when there are no .secure files.
Both methods return immediately when FindFirstFile misses file-backed entries, so a legacy-only install makes readAll() return {} and deleteAll() leave Credential Manager data untouched. That breaks the backward-compat paths this PR is testing.
Also applies to: 831-833
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In
`@examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/windows/flutter_secure_storage_windows_plugin.cpp`
around lines 745 - 748, The code returns early when
FindFirstFile((appSupportPath + L"\\*.secure")...) yields INVALID_HANDLE_VALUE,
which causes readAll() and deleteAll() to skip legacy Credential Manager
handling; instead, don't return from the function on INVALID_HANDLE_VALUE —
treat it as "no file-backed entries" and continue to the existing legacy code
path that reads/deletes from Credential Manager; specifically update the blocks
around FindFirstFile, hFile and searchRes in the functions readAll() and
deleteAll() so that if hFile == INVALID_HANDLE_VALUE you skip the file iteration
logic but do not return (allow the subsequent legacy-processing code to run),
and ensure any opened handles are closed (FindClose) when appropriate.
| switch (message) { | ||
| case WM_FONTCHANGE: | ||
| flutter_controller_->engine()->ReloadSystemFonts(); | ||
| break; | ||
| } |
There was a problem hiding this comment.
Potential null pointer dereference in WM_FONTCHANGE handler.
When flutter_controller_ is null, the check on line 55 fails and execution falls through to the switch statement. However, line 66 accesses flutter_controller_->engine() without a null check, which would cause a crash if WM_FONTCHANGE is received when flutter_controller_ is null.
🐛 Proposed fix to guard against null controller
switch (message) {
case WM_FONTCHANGE:
- flutter_controller_->engine()->ReloadSystemFonts();
+ if (flutter_controller_) {
+ flutter_controller_->engine()->ReloadSystemFonts();
+ }
break;
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| switch (message) { | |
| case WM_FONTCHANGE: | |
| flutter_controller_->engine()->ReloadSystemFonts(); | |
| break; | |
| } | |
| switch (message) { | |
| case WM_FONTCHANGE: | |
| if (flutter_controller_) { | |
| flutter_controller_->engine()->ReloadSystemFonts(); | |
| } | |
| break; | |
| } |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@examples/flutter/RunAnywhereAI/windows/runner/flutter_window.cpp` around
lines 64 - 68, The WM_FONTCHANGE case can dereference a null
flutter_controller_; update the handler so before calling
flutter_controller_->engine()->ReloadSystemFonts() you verify
flutter_controller_ is non-null (and ideally flutter_controller_->engine() is
non-null) and only call ReloadSystemFonts when those checks pass; modify the
switch/WM_FONTCHANGE branch in flutter_window.cpp to guard the call to
ReloadSystemFonts using the flutter_controller_ and engine null checks.
| void CreateAndAttachConsole() { | ||
| if (::AllocConsole()) { | ||
| FILE *unused; | ||
| if (freopen_s(&unused, "CONOUT$", "w", stdout)) { | ||
| _dup2(_fileno(stdout), 1); | ||
| } | ||
| if (freopen_s(&unused, "CONOUT$", "w", stderr)) { | ||
| _dup2(_fileno(stdout), 2); | ||
| } | ||
| std::ios::sync_with_stdio(); | ||
| FlutterDesktopResyncOutputStreams(); | ||
| } | ||
| } |
There was a problem hiding this comment.
Bug: freopen_s return value check is inverted.
freopen_s returns 0 on success and a non-zero error code on failure. The current logic calls _dup2 only when freopen_s fails, which is the opposite of the intended behavior. The _dup2 calls should execute on success (when freopen_s returns 0).
🐛 Proposed fix
void CreateAndAttachConsole() {
if (::AllocConsole()) {
FILE *unused;
- if (freopen_s(&unused, "CONOUT$", "w", stdout)) {
+ if (freopen_s(&unused, "CONOUT$", "w", stdout) == 0) {
_dup2(_fileno(stdout), 1);
}
- if (freopen_s(&unused, "CONOUT$", "w", stderr)) {
+ if (freopen_s(&unused, "CONOUT$", "w", stderr) == 0) {
_dup2(_fileno(stdout), 2);
}
std::ios::sync_with_stdio();
FlutterDesktopResyncOutputStreams();
}
}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@examples/flutter/RunAnywhereAI/windows/runner/utils.cpp` around lines 10 -
22, In CreateAndAttachConsole(), the freopen_s return checks are inverted and
the stderr dup uses stdout; change the logic to call _dup2 only when freopen_s
returns 0 (success) and for the second freopen_s use _fileno(stderr) with target
2; specifically update the two conditions around freopen_s(&unused, "CONOUT$",
"w", stdout) and freopen_s(&unused, "CONOUT$", "w", stderr) to test for == 0 and
then call _dup2(_fileno(stdout), 1) and _dup2(_fileno(stderr), 2) respectively.
| for %%I in ("%~dp0.") do set "SCRIPT_HOME=%%~fI" | ||
| set "ROOT=%SCRIPT_HOME%" | ||
| set "WINDOWS_SCRIPT_DIR=%ROOT%\scripts\windows" | ||
| set "BUILD_DIR=%ROOT%\build-windows-x64" | ||
| set "DIST_DIR=%ROOT%\dist\windows\x64" |
There was a problem hiding this comment.
Build root is pointing at scripts/, not sdk/runanywhere-commons.
Lines 23-25 make ROOT equal to %~dp0, so Line 56 resolves download-sherpa-onnx.bat under scripts\scripts\windows, and Line 60 points CMake at the scripts folder instead of the project root. This entrypoint will fail before the actual Windows build starts.
Proposed fix
-for %%I in ("%~dp0.") do set "SCRIPT_HOME=%%~fI"
-set "ROOT=%SCRIPT_HOME%"
-set "WINDOWS_SCRIPT_DIR=%ROOT%\scripts\windows"
-set "BUILD_DIR=%ROOT%\build-windows-x64"
-set "DIST_DIR=%ROOT%\dist\windows\x64"
+for %%I in ("%~dp0.") do set "SCRIPT_HOME=%%~fI"
+for %%I in ("%SCRIPT_HOME%\..") do set "ROOT=%%~fI"
+set "WINDOWS_SCRIPT_DIR=%SCRIPT_HOME%\windows"
+set "BUILD_DIR=%ROOT%\build-windows-x64"
+set "DIST_DIR=%ROOT%\dist\windows\x64"📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| for %%I in ("%~dp0.") do set "SCRIPT_HOME=%%~fI" | |
| set "ROOT=%SCRIPT_HOME%" | |
| set "WINDOWS_SCRIPT_DIR=%ROOT%\scripts\windows" | |
| set "BUILD_DIR=%ROOT%\build-windows-x64" | |
| set "DIST_DIR=%ROOT%\dist\windows\x64" | |
| for %%I in ("%~dp0.") do set "SCRIPT_HOME=%%~fI" | |
| for %%I in ("%SCRIPT_HOME%\..") do set "ROOT=%%~fI" | |
| set "WINDOWS_SCRIPT_DIR=%SCRIPT_HOME%\windows" | |
| set "BUILD_DIR=%ROOT%\build-windows-x64" | |
| set "DIST_DIR=%ROOT%\dist\windows\x64" |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@sdk/runanywhere-commons/scripts/build-windows.bat` around lines 23 - 27, The
build root is being set to the script folder so ROOT ends up as
sdk/runanywhere-commons/scripts and paths like download-sherpa-onnx.bat and
CMake resolve to scripts\scripts\windows; change the logic that sets
SCRIPT_HOME/ROOT so ROOT points to the project root (one directory up from the
script folder) and then derive WINDOWS_SCRIPT_DIR as %ROOT%\scripts\windows (and
BUILD_DIR/DIST_DIR from %ROOT%); update the variables SCRIPT_HOME and ROOT (and
any use of WINDOWS_SCRIPT_DIR) to compute the parent directory of the current
script location so CMake and download-sherpa-onnx.bat resolve under the
repository root rather than scripts\scripts\windows.
| elseif(RAC_PLATFORM_WINDOWS) | ||
| set(GGML_METAL OFF CACHE BOOL "" FORCE) | ||
| set(GGML_VULKAN OFF CACHE BOOL "" FORCE) | ||
| set(GGML_CUDA OFF CACHE BOOL "" FORCE) | ||
| set(GGML_OPENCL OFF CACHE BOOL "" FORCE) | ||
| set(GGML_HIPBLAS OFF CACHE BOOL "" FORCE) | ||
| set(GGML_SYCL OFF CACHE BOOL "" FORCE) | ||
| set(GGML_KOMPUTE OFF CACHE BOOL "" FORCE) | ||
| set(GGML_RPC OFF CACHE BOOL "" FORCE) | ||
| set(GGML_NATIVE OFF CACHE BOOL "" FORCE) |
There was a problem hiding this comment.
Critical: Duplicate elseif(RAC_PLATFORM_WINDOWS) blocks - second block is unreachable.
Lines 104-117 contain a second elseif(RAC_PLATFORM_WINDOWS) that will never execute because the first block at lines 80-89 matches first. This appears to be a merge error.
The unreachable block sets GGML_OPENMP OFF which is missing from the first block. You likely need to merge these settings.
🐛 Proposed fix: merge Windows platform settings
elseif(RAC_PLATFORM_WINDOWS)
set(GGML_METAL OFF CACHE BOOL "" FORCE)
set(GGML_VULKAN OFF CACHE BOOL "" FORCE)
set(GGML_CUDA OFF CACHE BOOL "" FORCE)
set(GGML_OPENCL OFF CACHE BOOL "" FORCE)
set(GGML_HIPBLAS OFF CACHE BOOL "" FORCE)
set(GGML_SYCL OFF CACHE BOOL "" FORCE)
set(GGML_KOMPUTE OFF CACHE BOOL "" FORCE)
set(GGML_RPC OFF CACHE BOOL "" FORCE)
set(GGML_NATIVE OFF CACHE BOOL "" FORCE)
+ set(GGML_OPENMP OFF CACHE BOOL "" FORCE)
+ message(STATUS "Configuring llama.cpp for Windows (CPU-only)")
elseif(RAC_PLATFORM_LINUX)
# Disable GPU backends not typically available on Linux ARM
...
-elseif(RAC_PLATFORM_WINDOWS)
- # Windows: CPU-only by default, no Metal/CUDA/Vulkan
- set(GGML_METAL OFF CACHE BOOL "" FORCE)
- set(GGML_CUDA OFF CACHE BOOL "" FORCE)
- set(GGML_VULKAN OFF CACHE BOOL "" FORCE)
- set(GGML_OPENCL OFF CACHE BOOL "" FORCE)
- set(GGML_HIPBLAS OFF CACHE BOOL "" FORCE)
- set(GGML_SYCL OFF CACHE BOOL "" FORCE)
- set(GGML_KOMPUTE OFF CACHE BOOL "" FORCE)
- set(GGML_RPC OFF CACHE BOOL "" FORCE)
- set(GGML_OPENMP OFF CACHE BOOL "" FORCE)
- set(GGML_NATIVE OFF CACHE BOOL "" FORCE)
- message(STATUS "Configuring llama.cpp for Windows (CPU-only)")
endif()Also applies to: 104-117
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@sdk/runanywhere-commons/src/backends/llamacpp/CMakeLists.txt` around lines 80
- 89, There is a duplicate elseif(RAC_PLATFORM_WINDOWS) block causing the second
block (which sets GGML_OPENMP OFF) to be unreachable; open the CMakeLists and
merge the Windows settings by consolidating both elseif(RAC_PLATFORM_WINDOWS)
blocks into one so the Windows branch sets GGML_METAL, GGML_VULKAN, GGML_CUDA,
GGML_OPENCL, GGML_HIPBLAS, GGML_SYCL, GGML_KOMPUTE, GGML_RPC, GGML_NATIVE and
also GGML_OPENMP OFF (use CACHE BOOL "" FORCE for each variable to match
existing style), removing the redundant elseif to avoid the unreachable code.
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (11)
sdk/runanywhere-commons/scripts/build-windows.bat (1)
7-16: Unknown arguments are silently ignored.The parser falls through all
ifclauses andshifts without flagging unrecognized input, so typos likebuild-windows.bat onxxsilently default toBACKENDS=llamacppinstead of warning the user. Consider printing the usage and exiting non-zero when no clause matches.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@sdk/runanywhere-commons/scripts/build-windows.bat` around lines 7 - 16, The argument parsing loop at label :parse_args silently ignores unknown flags (it always shifts and continues), which can cause typos like "onxx" to be accepted; update the loop in :parse_args to detect when none of the if /I "%~1"=="..." branches match and in that case call the usage routine (goto usage or print usage) and exit with a non-zero code so the script fails fast; reference the BACKENDS and CLEAN variables and ensure existing branches (all, llamacpp, onnx, --clean, -h, --help) still set those variables before shift/goto args_done.sdk/runanywhere-flutter/packages/runanywhere/lib/public/runanywhere.dart (3)
221-267: Dead code:_authenticateWithBackendis now unreachable.The
// ignore: unused_elementannotation confirms this method has no callers. SinceinitializeWithParamsno longer invokes it (auth appears to be handled elsewhere), consider removing it outright rather than suppressing the warning — suppressed dead code tends to drift out of sync with the real auth flow. If it's intentionally kept as reference for a future code path, add a short comment explaining why.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@sdk/runanywhere-flutter/packages/runanywhere/lib/public/runanywhere.dart` around lines 221 - 267, The private method _authenticateWithBackend is dead/unreferenced (the // ignore: unused_element hints it) because initializeWithParams no longer calls it; remove the entire _authenticateWithBackend function and its ignore annotation to avoid drifting, or if you intend to keep it as a reference, replace the ignore with a concise comment explaining its purpose and future use (mentioning DartBridgeAuth, DartBridgeDevice.instance.getDeviceId, and HTTPService.shared.setToken so reviewers can locate related auth flow). Ensure any removed imports or references are cleaned up.
1817-1827: Unusedmodelparameter in_resolveVLMModelFilePath.After delegating to
resolveVlmMainModelPath, theModelInfo modelargument is no longer used. Either drop it from the signature (and the call site at line 1455) or use it to prefer an exact filename match when available (e.g., ifmodel.name/metadata hints at the main gguf basename), which would be more robust than "first non-mmproj .gguf wins" when a folder contains multiple.gguffiles.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@sdk/runanywhere-flutter/packages/runanywhere/lib/public/runanywhere.dart` around lines 1817 - 1827, The _resolveVLMModelFilePath function currently accepts a ModelInfo model parameter that is never used; either remove the unused parameter from the signature and update all call sites that pass a ModelInfo (e.g., the caller that invokes _resolveVLMModelFilePath) to stop providing that argument, or enhance _resolveVLMModelFilePath to use model (for example use model.name or other metadata to prefer an exact gguf basename match before falling back to resolveVlmMainModelPath) so it disambiguates when multiple .gguf files exist; make the change inside _resolveVLMModelFilePath and adjust callers accordingly.
2240-2247: Document the HTTP client factory ownership contract and null-clearing behavior.The factory-returned
http.Clientwill be unconditionally closed by the SDK after each download completes (in the finally block), regardless of whether it's a fresh client or a caller-provided one. This means:
- Explicitly document that passing
nullclears the factory so callers know how to reset to the default behavior.- Document the ownership transfer: Once the SDK receives a client from the factory, it takes ownership and closes it. If callers return a shared long-lived client, the SDK will close it, which could break the caller's subsequent use of that client.
Consider either:
- Documenting the ownership contract explicitly (SDK closes all clients from the factory), or
- Wrapping factory-returned clients to make
close()a no-op, preserving caller ownership.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@sdk/runanywhere-flutter/packages/runanywhere/lib/public/runanywhere.dart` around lines 2240 - 2247, Update the configureDownloadHttpClientFactory doc comment to explicitly state two behaviors: passing null clears the custom factory (resets to default), and any http.Client returned by the provided factory is owned by the SDK and will be closed by the SDK after each download (so callers must not return shared/long‑lived clients). Reference the public API name configureDownloadHttpClientFactory and the underlying ModelDownloadService.shared.configureClientFactory behavior; alternatively note as an option that callers can return a wrapped http.Client with a no-op close if they need to preserve ownership.sdk/runanywhere-flutter/packages/runanywhere/lib/internal/vlm_file_resolution.dart (2)
12-25: Silentcatch (_)may hide real errors.Both helpers swallow all exceptions and return
null, which makes permission errors, I/O failures, and programming mistakes indistinguishable from "no match". At minimum, log the caught error (the SDK already hasSDKLogger) so operators can diagnose missing-model reports. Returningnullas the external contract is fine — just don't drop the diagnostic signal.- } catch (_) { - return null; + } catch (e, st) { + SDKLogger('VLMFileResolution').debug('resolveVlmMainModelPath failed: $e\n$st'); + return null; }Also applies to: 32-45
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@sdk/runanywhere-flutter/packages/runanywhere/lib/internal/vlm_file_resolution.dart` around lines 12 - 25, The current catch (_) swallows all exceptions; change it to catch (e, st) and log the error and stack using the SDKLogger before returning null so permission/I/O/programming errors are visible; update the try/catch surrounding dir.list() and the analogous block around the other helper (the block that builds ggufPaths/mainModelPaths using vlmPathBasename) to call SDKLogger.error (or the SDK's logging method) with a clear message, the exception e and stack st, then return null as the external result.
3-5: Consider usingpackage:pathfor basename extraction.
vlmPathBasenameworks for typical cases but has edge cases: trailing separators yield an empty string (e.g.,C:\foo\→''), and mixed separators are handled only by simple replacement. Dart'spackage:path(p.basename) is platform-aware and more robust. Sincepackage:pathis already a direct dependency in this project, the refactor is straightforward.Proposed change
+import 'package:path/path.dart' as p; -String vlmPathBasename(String path) { - return path.replaceAll('\\', '/').split('/').last; -} +String vlmPathBasename(String path) => p.basename(path);Note:
p.basenameuses the platform's separator by default. If cross-platform normalization of Windows-style paths on POSIX is required (as indicated by existing tests), usep.Context(style: Style.windows).basename(path)conditionally, or keep a fallback that normalizes\to/first.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@sdk/runanywhere-flutter/packages/runanywhere/lib/internal/vlm_file_resolution.dart` around lines 3 - 5, Replace the hand-rolled basename logic in vlmPathBasename with package:path's basename to handle edge cases (trailing separators, mixed separators, platform differences); update vlmPathBasename to call p.basename(path) or, if Windows-style normalization is required, use p.Context(style: Style.windows).basename(path) (import p from 'package:path/path.dart') so basename extraction is robust and platform-aware.sdk/runanywhere-flutter/packages/runanywhere/test/vlm_file_resolution_test.dart (1)
6-48: LGTM — solid coverage for the new helpers.The temp-dir setup, teardown via
addTearDown, and assertions around the main/mmproj/archive files correctly validate the selection logic (especially themmprojexclusion). One optional addition: a case verifying that passing a file path (not directory) toresolveVlmMainModelPathstill resolves via the parent directory, since that branch is part of the implementation's contract but isn't exercised here.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@sdk/runanywhere-flutter/packages/runanywhere/test/vlm_file_resolution_test.dart` around lines 6 - 48, Add a test that calls resolveVlmMainModelPath with a file path (not a directory) to exercise the branch that treats file inputs by using their parent directory; in the existing group 'VLM file resolution' create a temp file (e.g., mainModel) in the temp dir, write contents, then pass mainModel.path into resolveVlmMainModelPath and assert the returned path equals mainModel.path (also assert findVlmMmprojPath when appropriate) so the code path in resolveVlmMainModelPath that uses parent directory is covered.docs/superpowers/plans/2026-04-17-windows-vision-restoration.md (1)
635-649: Plan drifted from shipped implementation (non-blocking).A few spots in the plan no longer match the landed code, worth a quick sync pass if this doc is intended to stay accurate:
disposeCamerain the plan isFuture<void> async(lines 635–640); the shipped view model usesvoid disposeCamera()with an unawaitedsession?.dispose().- The error panel snippet (lines 874–891) is the older red variant; shipped
vlm_camera_view.dartuses the orange/info-outline container.- The shipped test file grew additional cases (dispose/init races, dispose-during-capture, initialize-error cleanup) beyond what Step 1 enumerates.
Since this is a planning doc, this is informational rather than a defect.
Also applies to: 874-891
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/superpowers/plans/2026-04-17-windows-vision-restoration.md` around lines 635 - 649, Update the plan doc to match the shipped implementation: change the disposeCamera signature and behavior to match the view model's void disposeCamera() with unawaited session?.dispose(), update the error panel snippet to use the shipped orange/info-outline container as in vlm_camera_view.dart, and expand the tests section to list the additional shipped test cases (dispose/init races, dispose-during-capture, initialize-error cleanup) so the plan and test checklist reflect the current codebase.examples/flutter/RunAnywhereAI/lib/features/vision/services/vision_camera_backend.dart (2)
33-44:handle!cast will produce an unhelpful error if the device didn't come from this backend.
device.handle! as CameraDescriptionfails with a generic null-check or type-cast exception if a test fake or a different backend'sVisionCameraDeviceis ever passed. A guarded cast with a descriptiveStateError/ArgumentErrormakes mis-wiring obvious; alternatively, narrowhandletoCameraDescription?on the concrete constructor path.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@examples/flutter/RunAnywhereAI/lib/features/vision/services/vision_camera_backend.dart` around lines 33 - 44, The createSession implementation in CameraPluginVisionCameraBackend currently does a blind cast device.handle! as CameraDescription which produces opaque null-check/type-cast errors if a non-plugin device or a test double is passed; change createSession to validate the handle first (e.g., check device.handle is CameraDescription and not null) and throw a clear ArgumentError or StateError with a descriptive message (referencing the device.identity or id) if the handle is missing or of the wrong type, then pass the safely-casted CameraDescription into _CameraPluginVisionCameraSession/CameraController.
4-4: The switch expression lacks a default arm;VisionCameraLensDirection.unknownis unused and the mapping is not forward-compatible.The
unknownvariant is defined but never produced. The switch currently requiresCameraLensDirectionto have exactly{front, back, external}— if the upstreamcamerapackage adds a new value in a future version, this code will fail to compile. Adding a default arm that maps tounknownboth utilizes the unused variant and future-proofs the mapping.♻️ Proposed change
lensDirection: switch (camera.lensDirection) { CameraLensDirection.front => VisionCameraLensDirection.front, CameraLensDirection.back => VisionCameraLensDirection.back, CameraLensDirection.external => VisionCameraLensDirection.external, + _ => VisionCameraLensDirection.unknown, },🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@examples/flutter/RunAnywhereAI/lib/features/vision/services/vision_camera_backend.dart` at line 4, The mapping from the upstream CameraLensDirection to our enum VisionCameraLensDirection currently omits a default switch arm, leaving VisionCameraLensDirection.unknown unused and making the code brittle; update the switch (where you map CameraLensDirection -> VisionCameraLensDirection) to include a default/catch-all case that returns VisionCameraLensDirection.unknown so the unknown variant is used and the mapping remains forward-compatible if CameraLensDirection adds values later.examples/flutter/RunAnywhereAI/lib/features/vision/vlm_camera_view.dart (1)
350-363: Simplify the snackbarunawaitedwrapping.The
.then((_) => null)tail is a no-op since.closedalready returns aFuture;unawaited(...)accepts it directly. This keeps the call site slimmer.♻️ Proposed simplification
- unawaited(Clipboard.setData( - ClipboardData(text: _viewModel.currentDescription))); - unawaited( - ScaffoldMessenger.of(context) - .showSnackBar( - const SnackBar( - content: Text('Description copied to clipboard'), - duration: Duration(seconds: 2), - ), - ) - .closed - .then((_) => null), - ); + unawaited(Clipboard.setData( + ClipboardData(text: _viewModel.currentDescription), + )); + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: Text('Description copied to clipboard'), + duration: Duration(seconds: 2), + ), + );🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@examples/flutter/RunAnywhereAI/lib/features/vision/vlm_camera_view.dart` around lines 350 - 363, The snackbar call wraps ScaffoldMessenger.of(context).showSnackBar(...).closed with an unnecessary .then((_) => null) before passing to unawaited; remove the .then((_) => null) and pass the Future returned by .closed directly to unawaited so the code becomes unawaited(ScaffoldMessenger.of(context).showSnackBar(...).closed); keep the Clipboard.setData unawaited(Clipboard.setData(ClipboardData(text: _viewModel.currentDescription))) as-is and ensure you reference the same SnackBar construction and _viewModel.currentDescription when editing.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In
`@examples/flutter/RunAnywhereAI/lib/features/vision/services/vision_camera_backend.dart`:
- Line 41: The code forces ImageFormatGroup.bgra8888 for camera frames; add a
runtime check after camera initialization (in the vision_camera_backend code
that sets imageFormatGroup) to detect the actual format provided by the platform
(especially on Windows/camera_windows) and log or adapt behavior if it differs;
specifically, inspect the CameraController or returned
CameraDescription/previewFormat to confirm the format group, emit a warning if
it's not bgra8888, and conditionally adjust downstream pixel handling or fall
back to a supported format to avoid misinterpreting BGRA/RGBA channel order when
Windows frame streaming is enabled.
In `@examples/flutter/RunAnywhereAI/lib/features/vision/vlm_view_model.dart`:
- Around line 283-285: The silent catch in the auto-stream block should surface
errors instead of swallowing them: change the catch (_) in the auto-stream
try/catch to capture the exception (e, stack) and assign a user-visible message
to the existing _error field (or call the ViewModel's error setter), and log the
error via the existing logger or debugPrint so failures (camera revoked, model
unloaded, inference errors) are recorded; also ensure any LIVE state toggles
(the code around the auto-stream start/stop functions) reflect the failure so
the UI indicator does not remain stuck on LIVE.
- Around line 56-108: The initializeCamera method can be run concurrently and
leak sessions; add an in-flight guard or token to serialize/ignore overlapping
calls so only one initialization proceeds and any sessions created by earlier,
superseded calls are disposed. Concretely: introduce a private flag or
initialization token (e.g., _isInitializing or _initializeToken) checked at the
start of initializeCamera and set for the lifetime of the async operation;
before assigning _cameraSession = nextSession only allow the assignment when the
token/flag still matches (otherwise dispose nextSession); also ensure any
previous _cameraSession is awaited/disposed before creating a new session (use
await _cameraSession?.dispose()), and clear the flag/token and notify listeners
in all success/failure/early-return paths; reference initializeCamera,
_cameraSession, _isDisposed, and _safeNotifyListeners when applying these
changes.
---
Nitpick comments:
In `@docs/superpowers/plans/2026-04-17-windows-vision-restoration.md`:
- Around line 635-649: Update the plan doc to match the shipped implementation:
change the disposeCamera signature and behavior to match the view model's void
disposeCamera() with unawaited session?.dispose(), update the error panel
snippet to use the shipped orange/info-outline container as in
vlm_camera_view.dart, and expand the tests section to list the additional
shipped test cases (dispose/init races, dispose-during-capture, initialize-error
cleanup) so the plan and test checklist reflect the current codebase.
In
`@examples/flutter/RunAnywhereAI/lib/features/vision/services/vision_camera_backend.dart`:
- Around line 33-44: The createSession implementation in
CameraPluginVisionCameraBackend currently does a blind cast device.handle! as
CameraDescription which produces opaque null-check/type-cast errors if a
non-plugin device or a test double is passed; change createSession to validate
the handle first (e.g., check device.handle is CameraDescription and not null)
and throw a clear ArgumentError or StateError with a descriptive message
(referencing the device.identity or id) if the handle is missing or of the wrong
type, then pass the safely-casted CameraDescription into
_CameraPluginVisionCameraSession/CameraController.
- Line 4: The mapping from the upstream CameraLensDirection to our enum
VisionCameraLensDirection currently omits a default switch arm, leaving
VisionCameraLensDirection.unknown unused and making the code brittle; update the
switch (where you map CameraLensDirection -> VisionCameraLensDirection) to
include a default/catch-all case that returns VisionCameraLensDirection.unknown
so the unknown variant is used and the mapping remains forward-compatible if
CameraLensDirection adds values later.
In `@examples/flutter/RunAnywhereAI/lib/features/vision/vlm_camera_view.dart`:
- Around line 350-363: The snackbar call wraps
ScaffoldMessenger.of(context).showSnackBar(...).closed with an unnecessary
.then((_) => null) before passing to unawaited; remove the .then((_) => null)
and pass the Future returned by .closed directly to unawaited so the code
becomes unawaited(ScaffoldMessenger.of(context).showSnackBar(...).closed); keep
the Clipboard.setData unawaited(Clipboard.setData(ClipboardData(text:
_viewModel.currentDescription))) as-is and ensure you reference the same
SnackBar construction and _viewModel.currentDescription when editing.
In `@sdk/runanywhere-commons/scripts/build-windows.bat`:
- Around line 7-16: The argument parsing loop at label :parse_args silently
ignores unknown flags (it always shifts and continues), which can cause typos
like "onxx" to be accepted; update the loop in :parse_args to detect when none
of the if /I "%~1"=="..." branches match and in that case call the usage routine
(goto usage or print usage) and exit with a non-zero code so the script fails
fast; reference the BACKENDS and CLEAN variables and ensure existing branches
(all, llamacpp, onnx, --clean, -h, --help) still set those variables before
shift/goto args_done.
In
`@sdk/runanywhere-flutter/packages/runanywhere/lib/internal/vlm_file_resolution.dart`:
- Around line 12-25: The current catch (_) swallows all exceptions; change it to
catch (e, st) and log the error and stack using the SDKLogger before returning
null so permission/I/O/programming errors are visible; update the try/catch
surrounding dir.list() and the analogous block around the other helper (the
block that builds ggufPaths/mainModelPaths using vlmPathBasename) to call
SDKLogger.error (or the SDK's logging method) with a clear message, the
exception e and stack st, then return null as the external result.
- Around line 3-5: Replace the hand-rolled basename logic in vlmPathBasename
with package:path's basename to handle edge cases (trailing separators, mixed
separators, platform differences); update vlmPathBasename to call
p.basename(path) or, if Windows-style normalization is required, use
p.Context(style: Style.windows).basename(path) (import p from
'package:path/path.dart') so basename extraction is robust and platform-aware.
In `@sdk/runanywhere-flutter/packages/runanywhere/lib/public/runanywhere.dart`:
- Around line 221-267: The private method _authenticateWithBackend is
dead/unreferenced (the // ignore: unused_element hints it) because
initializeWithParams no longer calls it; remove the entire
_authenticateWithBackend function and its ignore annotation to avoid drifting,
or if you intend to keep it as a reference, replace the ignore with a concise
comment explaining its purpose and future use (mentioning DartBridgeAuth,
DartBridgeDevice.instance.getDeviceId, and HTTPService.shared.setToken so
reviewers can locate related auth flow). Ensure any removed imports or
references are cleaned up.
- Around line 1817-1827: The _resolveVLMModelFilePath function currently accepts
a ModelInfo model parameter that is never used; either remove the unused
parameter from the signature and update all call sites that pass a ModelInfo
(e.g., the caller that invokes _resolveVLMModelFilePath) to stop providing that
argument, or enhance _resolveVLMModelFilePath to use model (for example use
model.name or other metadata to prefer an exact gguf basename match before
falling back to resolveVlmMainModelPath) so it disambiguates when multiple .gguf
files exist; make the change inside _resolveVLMModelFilePath and adjust callers
accordingly.
- Around line 2240-2247: Update the configureDownloadHttpClientFactory doc
comment to explicitly state two behaviors: passing null clears the custom
factory (resets to default), and any http.Client returned by the provided
factory is owned by the SDK and will be closed by the SDK after each download
(so callers must not return shared/long‑lived clients). Reference the public API
name configureDownloadHttpClientFactory and the underlying
ModelDownloadService.shared.configureClientFactory behavior; alternatively note
as an option that callers can return a wrapped http.Client with a no-op close if
they need to preserve ownership.
In
`@sdk/runanywhere-flutter/packages/runanywhere/test/vlm_file_resolution_test.dart`:
- Around line 6-48: Add a test that calls resolveVlmMainModelPath with a file
path (not a directory) to exercise the branch that treats file inputs by using
their parent directory; in the existing group 'VLM file resolution' create a
temp file (e.g., mainModel) in the temp dir, write contents, then pass
mainModel.path into resolveVlmMainModelPath and assert the returned path equals
mainModel.path (also assert findVlmMmprojPath when appropriate) so the code path
in resolveVlmMainModelPath that uses parent directory is covered.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 8049daf8-a059-4612-8539-0c2b8483b77f
📒 Files selected for processing (21)
docs/building.mddocs/superpowers/plans/2026-04-17-windows-vision-restoration.mddocs/superpowers/specs/2026-04-17-windows-vision-design.mdexamples/flutter/RunAnywhereAI/lib/core/services/platform_capability_service.dartexamples/flutter/RunAnywhereAI/lib/features/vision/services/vision_camera_backend.dartexamples/flutter/RunAnywhereAI/lib/features/vision/services/vision_permission_gateway.dartexamples/flutter/RunAnywhereAI/lib/features/vision/services/vision_vlm_service.dartexamples/flutter/RunAnywhereAI/lib/features/vision/vision_hub_view.dartexamples/flutter/RunAnywhereAI/lib/features/vision/vlm_camera_view.dartexamples/flutter/RunAnywhereAI/lib/features/vision/vlm_view_model.dartexamples/flutter/RunAnywhereAI/pubspec.yamlexamples/flutter/RunAnywhereAI/test/features/vision/vlm_view_model_test.dartexamples/flutter/RunAnywhereAI/test/widget_test.dartexamples/flutter/RunAnywhereAI/windows/flutter/generated_plugin_registrant.ccexamples/flutter/RunAnywhereAI/windows/flutter/generated_plugins.cmakesdk/runanywhere-commons/CMakeLists.txtsdk/runanywhere-commons/scripts/build-windows.batsdk/runanywhere-commons/src/backends/llamacpp/CMakeLists.txtsdk/runanywhere-flutter/packages/runanywhere/lib/internal/vlm_file_resolution.dartsdk/runanywhere-flutter/packages/runanywhere/lib/public/runanywhere.dartsdk/runanywhere-flutter/packages/runanywhere/test/vlm_file_resolution_test.dart
✅ Files skipped from review due to trivial changes (6)
- examples/flutter/RunAnywhereAI/test/widget_test.dart
- examples/flutter/RunAnywhereAI/lib/features/vision/vision_hub_view.dart
- sdk/runanywhere-commons/CMakeLists.txt
- docs/building.md
- examples/flutter/RunAnywhereAI/windows/flutter/generated_plugin_registrant.cc
- docs/superpowers/specs/2026-04-17-windows-vision-design.md
🚧 Files skipped from review as they are similar to previous changes (4)
- examples/flutter/RunAnywhereAI/pubspec.yaml
- examples/flutter/RunAnywhereAI/lib/core/services/platform_capability_service.dart
- examples/flutter/RunAnywhereAI/windows/flutter/generated_plugins.cmake
- sdk/runanywhere-commons/src/backends/llamacpp/CMakeLists.txt
| CameraController( | ||
| description, | ||
| ResolutionPreset.medium, | ||
| imageFormatGroup: ImageFormatGroup.bgra8888, |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
camera_windows supported ImageFormatGroup bgra8888
💡 Result:
No, Windows camera APIs (MediaCapture, CameraCaptureUI, MediaFrameReader) do not support an "ImageFormatGroup bgra8888". ImageFormatGroup is a Flutter camera plugin concept, not native Windows. Windows uses BitmapPixelFormat.Bgra8 (equivalent to BGRA8888) for SoftwareBitmap from camera frames, which is widely supported. CameraCaptureUI outputs JPEG/PNG files convertible to Bgra8. MediaFrameReader can request MediaEncodingSubtypes.Bgra8 for color frames.
Citations:
- 1: https://learn.microsoft.com/en-gb/windows/uwp/audio-video-camera/capture-photos-and-video-with-cameracaptureui
- 2: https://learn.microsoft.com/en-us/uwp/api/windows.media.capture.cameracaptureui?view=winrt-28000
- 3: https://learn.microsoft.com/en-us/windows/apps/develop/camera/cameracaptureui
- 4: https://learn.microsoft.com/en-us/windows/uwp/audio-video-camera/capture-photos-and-video-with-cameracaptureui
- 5: https://learn.microsoft.com/en-us/uwp/api/windows.graphics.imaging.bitmappixelformat?view=winrt-28000
- 6: https://learn.microsoft.com/en-us/windows/apps/develop/camera/process-media-frames-with-mediaframereader
- 7: https://github.com/microsoft/Windows-universal-samples/blob/master/Samples/CameraFrames/cpp/FrameRenderer.cpp
🏁 Script executed:
# Search for camera_windows plugin documentation and implementation
# First, check if there's any documentation or reference in the repo about camera_windows
fd -i "camera" --type f | head -20Repository: RunanywhereAI/runanywhere-sdks
Length of output: 371
🌐 Web query:
Flutter camera_windows plugin ImageFormatGroup bgra8888 support documentation
💡 Result:
The Flutter camera_windows plugin supports the ImageFormatGroup parameter in its initializeCamera method, with a default of ImageFormatGroup.unknown. However, there is no explicit documentation confirming support for ImageFormatGroup.bgra8888 specifically on Windows. ImageFormatGroup.bgra8888 is defined in the core camera library as "32-bit BGRA" (primarily associated with iOS kCVPixelFormatType_32BGRA). Windows camera implementations (using Win32 Media Foundation or DirectShow) typically produce BGRA or similar formats natively, but the plugin docs do not specify which groups are honored or what format CameraImage uses when image streaming is enabled (recently restored via PRs but reverted; ongoing work per changelog and issues). Official pub.dev docs list image streaming as "not yet implemented" (issue #97542), though API methods like onStreamedFrameAvailable exist. No source explicitly states bgra8888 support for Windows; users report BGRA-like planes in prototypes (e.g., flutter/flutter#97542 comments). For streamed frames, check the actual CameraImage.format.group at runtime after setting imageFormatGroup: ImageFormatGroup.bgra8888 in CameraController.
Citations:
- 1: https://pub.dev/documentation/camera_windows/latest/camera_windows/CameraWindows-class.html
- 2: https://pub.dev/documentation/camera/latest/camera/ImageFormatGroup.html
- 3: https://pub.dev/documentation/camera_platform_interface/latest/camera_platform_interface/ImageFormatGroup.html
- 4: https://pub.dev/documentation/camera_windows/latest
- 5: https://pub.dev/packages/camera_windows
- 6: https://pub.dev/packages/camera_windows/changelog
- 7: [camera_windows] Restore image streaming support flutter/packages#8234
- 8: [camera_windows] Support image streams on Windows platform flutter/packages#7067
- 9: https://flutter.googlesource.com/mirrors/packages/+/refs/tags/webview_flutter_wkwebview-v3.10.1/packages/camera/camera_windows/lib/camera_windows.dart
- 10: [camera] Support image streams on Windows platform flutter/flutter#97542
🌐 Web query:
camera_windows plugin supported image formats
💡 Result:
The camera_windows plugin, which is the Windows implementation of Flutter's camera plugin, supports JPEG as the image format for captured photos via takePicture. This is hardcoded in the plugin's C++ source code, where the constant kPictureCaptureExtension = "jpeg" is used, and photos are saved to the Pictures folder with filenames like "PhotoCapture_[timestamp].jpeg". There is a setImageFileFormat method in the API, but it is inherited from the platform interface and not implemented in camera_windows (no evidence of usage or support for other formats like PNG). Image streaming (raw CameraImage frames) is not supported (open issue #97542), but when implemented in forks, frames use BGRA8888 format.
Citations:
- 1: https://pub.dev/documentation/camera_windows/latest
- 2: https://pub.dev/packages/camera_windows
- 3: https://github.com/flutter/plugins/blob/main/packages/camera/camera_windows/windows/camera_plugin.cpp
- 4: https://pub.dev/documentation/camera_windows/latest/camera_windows/CameraWindows-class.html
- 5: https://learn.microsoft.com/hr-hr/windows/apps/develop/camera/cameracaptureui
- 6: https://learn.microsoft.com/en-us/windows/apps/develop/camera/cameracaptureui
- 7: https://docs.unity3d.com/2021.3/Documentation/ScriptReference/Windows.WebCam.PhotoCapture.TakePhotoAsync.html
Monitor ImageFormatGroup.bgra8888 behavior when frame streaming is added to Windows.
Currently harmless—image streaming is not yet implemented in camera_windows (issue #97542). When added, verify that the plugin correctly handles bgra8888 format, as Windows implementations have used BGRA8888 for streamed frames in prototypes. Test at runtime to confirm the actual format group after initialization.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In
`@examples/flutter/RunAnywhereAI/lib/features/vision/services/vision_camera_backend.dart`
at line 41, The code forces ImageFormatGroup.bgra8888 for camera frames; add a
runtime check after camera initialization (in the vision_camera_backend code
that sets imageFormatGroup) to detect the actual format provided by the platform
(especially on Windows/camera_windows) and log or adapt behavior if it differs;
specifically, inspect the CameraController or returned
CameraDescription/previewFormat to confirm the format group, emit a warning if
it's not bgra8888, and conditionally adjust downstream pixel handling or fall
back to a supported format to avoid misinterpreting BGRA/RGBA channel order when
Windows frame streaming is enabled.
| Future<void> initializeCamera() async { | ||
| try { | ||
| final cameras = await availableCameras(); | ||
| if (cameras.isEmpty) { | ||
| debugPrint('❌ No cameras available'); | ||
| final devices = await _cameraBackend.listDevices(); | ||
| if (_isDisposed) { | ||
| return; | ||
| } | ||
| if (devices.isEmpty) { | ||
| _hasCameraDevice = false; | ||
| _isCameraInitialized = false; | ||
| _error = 'No cameras available on this device.'; | ||
| _safeNotifyListeners(); | ||
| return; | ||
| } | ||
|
|
||
| // Select back camera (or first available) | ||
| final camera = cameras.firstWhere( | ||
| (c) => c.lensDirection == CameraLensDirection.back, | ||
| orElse: () => cameras.first, | ||
| ); | ||
|
|
||
| // Create controller with BGRA format request (iOS preferred, Android fallback to YUV) | ||
| _cameraController = CameraController( | ||
| camera, | ||
| ResolutionPreset.medium, | ||
| imageFormatGroup: ImageFormatGroup.bgra8888, | ||
| _hasCameraDevice = true; | ||
| final device = devices.firstWhere( | ||
| (camera) => camera.lensDirection == VisionCameraLensDirection.back, | ||
| orElse: () => devices.first, | ||
| ); | ||
|
|
||
| await _cameraController!.initialize(); | ||
| await _cameraSession?.dispose(); | ||
| if (_isDisposed) { | ||
| return; | ||
| } | ||
| final nextSession = _cameraBackend.createSession(device); | ||
| if (_isDisposed) { | ||
| unawaited(nextSession.dispose()); | ||
| return; | ||
| } | ||
| try { | ||
| await nextSession.initialize(); | ||
| } catch (_) { | ||
| await nextSession.dispose(); | ||
| rethrow; | ||
| } | ||
| if (_isDisposed) { | ||
| unawaited(nextSession.dispose()); | ||
| return; | ||
| } | ||
| _cameraSession = nextSession; | ||
|
|
||
| _isCameraInitialized = true; | ||
| notifyListeners(); | ||
|
|
||
| debugPrint('✅ Camera initialized: ${camera.lensDirection}'); | ||
| _error = null; | ||
| _safeNotifyListeners(); | ||
| } catch (e) { | ||
| debugPrint('❌ Camera initialization failed: $e'); | ||
| if (_isDisposed) { | ||
| return; | ||
| } | ||
| _isCameraInitialized = false; | ||
| _error = 'Failed to initialize camera: $e'; | ||
| notifyListeners(); | ||
| _safeNotifyListeners(); | ||
| } | ||
| } |
There was a problem hiding this comment.
Concurrent initializeCamera calls can leak a camera session.
There is no in-flight guard. If initializeCamera is invoked twice before the first completes (e.g. user grants permission while _initializeAsync is still running, or taps after model selection), both calls can reach _cameraBackend.createSession(...) and await nextSession.initialize(). The later assignment at line 95 overwrites _cameraSession, and the session created by the earlier call is leaked (its preview and capture handles remain open).
🛡️ Suggested in-flight guard
+ Future<void>? _initializeCameraFuture;
+
Future<void> initializeCamera() async {
+ final existing = _initializeCameraFuture;
+ if (existing != null) return existing;
+ final completer = Completer<void>();
+ _initializeCameraFuture = completer.future;
+ try {
+ await _initializeCameraInternal();
+ } finally {
+ _initializeCameraFuture = null;
+ completer.complete();
+ }
+ }
+
+ Future<void> _initializeCameraInternal() async {
try {
final devices = await _cameraBackend.listDevices();
...🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@examples/flutter/RunAnywhereAI/lib/features/vision/vlm_view_model.dart`
around lines 56 - 108, The initializeCamera method can be run concurrently and
leak sessions; add an in-flight guard or token to serialize/ignore overlapping
calls so only one initialization proceeds and any sessions created by earlier,
superseded calls are disposed. Concretely: introduce a private flag or
initialization token (e.g., _isInitializing or _initializeToken) checked at the
start of initializeCamera and set for the lifetime of the async operation;
before assigning _cameraSession = nextSession only allow the assignment when the
token/flag still matches (otherwise dispose nextSession); also ensure any
previous _cameraSession is awaited/disposed before creating a new session (use
await _cameraSession?.dispose()), and clear the flag/token and notify listeners
in all success/failure/early-return paths; reference initializeCamera,
_cameraSession, _isDisposed, and _safeNotifyListeners when applying these
changes.
| } catch (_) { | ||
| // Auto-stream failures stay non-blocking. | ||
| } finally { |
There was a problem hiding this comment.
Silent catch (_) hides live-mode failures.
Auto-stream errors are entirely discarded. If capture or VLM inference starts failing consistently (e.g. camera revoked, model unloaded), the UI keeps the LIVE indicator on with no feedback and no log trail to debug. Consider surfacing via the existing _error/logger or at least a debugPrint so failure modes aren't invisible.
♻️ Proposed tweak
- } catch (_) {
- // Auto-stream failures stay non-blocking.
+ } catch (e, st) {
+ // Auto-stream failures stay non-blocking, but surface them for debugging.
+ debugPrint('Auto-stream capture/describe failed: $e\n$st');
}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@examples/flutter/RunAnywhereAI/lib/features/vision/vlm_view_model.dart`
around lines 283 - 285, The silent catch in the auto-stream block should surface
errors instead of swallowing them: change the catch (_) in the auto-stream
try/catch to capture the exception (e, stack) and assign a user-visible message
to the existing _error field (or call the ViewModel's error setter), and log the
error via the existing logger or debugPrint so failures (camera revoked, model
unloaded, inference errors) are recorded; also ensure any LIVE state toggles
(the code around the auto-stream start/stop functions) reflect the failure so
the UI indicator does not remain stuck on LIVE.




… or download modals
Description
Brief description of the changes made.
Type of Change
Testing
Platform-Specific Testing (check all that apply)
Swift SDK / iOS Sample:
Kotlin SDK / Android Sample:
Flutter SDK / Flutter Sample:
React Native SDK / React Native Sample:
Playground:
Web SDK / Web Sample:
Labels
Please add the appropriate label(s):
SDKs:
Swift SDK- Changes to Swift SDK (sdk/runanywhere-swift)Kotlin SDK- Changes to Kotlin SDK (sdk/runanywhere-kotlin)Flutter SDK- Changes to Flutter SDK (sdk/runanywhere-flutter)React Native SDK- Changes to React Native SDK (sdk/runanywhere-react-native)Web SDK- Changes to Web SDK (sdk/runanywhere-web)Commons- Changes to shared native code (sdk/runanywhere-commons)Sample Apps:
iOS Sample- Changes to iOS example app (examples/ios)Android Sample- Changes to Android example app (examples/android)Flutter Sample- Changes to Flutter example app (examples/flutter)React Native Sample- Changes to React Native example app (examples/react-native)Web Sample- Changes to Web example app (examples/web)Checklist
Screenshots
Attach relevant UI screenshots for changes (if applicable):
Summary by CodeRabbit
New Features
Documentation
Bug Fixes