rcli network driver + example-app brand alignment (#FF6900)#570
rcli network driver + example-app brand alignment (#FF6900)#570sanchitmonga22 wants to merge 3 commits into
Conversation
|
Important Review skippedDraft detected. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
📝 WalkthroughWalkthroughThe PR adds shared design guidance and aligns Android, Flutter, iOS, React Native, and web examples with the ChangesExample app design system alignment
Control-plane CLI workflows
Estimated code review effort: 5 (Critical) | ~120 minutes Sequence Diagram(s)sequenceDiagram
participant CLI
participant Bootstrap
participant ControlPlane
participant SDK
participant Backend
CLI->>Bootstrap: Parse and validate connection options
Bootstrap->>SDK: Initialize SDK state
Bootstrap->>ControlPlane: Register device callbacks
CLI->>ControlPlane: Run auth login or telemetry command
ControlPlane->>Backend: Authenticate and exchange API key
ControlPlane->>SDK: Initialize phase two
SDK->>Backend: Post device or telemetry data
Backend-->>CLI: Return registration and telemetry accounting
Possibly related PRs
Suggested labels: 🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (2 warnings)
✅ Passed checks (3 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
@coderabbitai full review Please do a detailed review of this PR. It combines two changes:
Areas worth close attention: the RN theme migration (legacy barrel deletion + 11 file migration), the Android tonal-ramp regeneration, the Flutter ColorScheme seed/copyWith, and the rcli connection-flag threading + auth handshake. |
|
✅ Action performedFull review finished. |
…lemetry Turns rcli into a control-plane client that can point at any backend for integration testing and manual debugging: - Global --base-url / --api-key / --environment flags (+ RUNANYWHERE_* env vars), threaded into the SDK config with zero regression when absent. - rcli auth login — real handshake (API key -> JWT, device register, model-assignments fetch). - rcli telemetry emit --modality <m> and rcli telemetry blast — model-free telemetry through the real network transport, with a 12-modality result table. Verified end-to-end against a locally-run backend: login registers a device with real hardware facts; blast lands all 12 modalities as verified rows; a bad key exits non-zero. Existing commands unchanged (10/10 CLI tests). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01U4scoVoLn3Z3NxaiK3LC5M
Every example app had drifted from the brand: UI accents were the legacy #FF5500 (Flutter/RN were plain blue) while the logos were already the correct #FF6900. This finishes the migration. - examples/DESIGN_GUIDELINE.md: canonical brand doc (primary = the logo's #FF6900, gradient ->#FB2C36, full palette light+dark, per-platform mapping, the white-on-orange contrast caveat). - iOS (AppColors + AccentColor + Keyboard/Activity extensions), Android (Color.kt tonal ramp on the brand hue; dead template swatches removed), Flutter (mislabeled-blue primary -> brand, seed pinned), Web (CSS tokens + loading SVG + theme-color), React Native (two competing theme systems consolidated onto one anchored to #FF6900; legacy barrel deleted; migrated screens now get dark mode). - Each app's AGENTS.md gains a Design System section pointing at the guideline (CLAUDE.md symlinks intact); parent AGENTS.md references it. - Removed the dead rac_telemetry_manager_parse_response declaration + export. Grep-proven: zero legacy accent literals remain; #FF6900 present in all five. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01U4scoVoLn3Z3NxaiK3LC5M
f5271dd to
c14f46b
Compare
|
Force-pushed a cleanup (squashed to 2 commits: rcli driver + example-app brand alignment). @coderabbitai full review — please review the updated commits. |
|
✅ Action performedFull review finished. |
There was a problem hiding this comment.
Actionable comments posted: 9
🧹 Nitpick comments (4)
examples/web/RunAnywhereAI/src/styles/components.css (2)
474-474: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueUse the existing design-system token.
This
rgbavalue exactly matches the--color-primary-lighttoken defined indesign-system.css. Using the token maintains a single source of truth for brand colors.♻️ Proposed fix
- box-shadow: 0 2px 8px rgba(255,105,0,0.12); + box-shadow: 0 2px 8px var(--color-primary-light);🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@examples/web/RunAnywhereAI/src/styles/components.css` at line 474, Replace the hardcoded rgba box-shadow color at the affected style declaration with the existing --color-primary-light design-system token from design-system.css, preserving the current shadow dimensions and opacity behavior.
2387-2388: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueConsider using
color-mixto reference the primary brand token.Instead of hardcoding the RGB values (
255, 105, 0) of the brand color across component files, consider usingcolor-mixwithvar(--color-primary)to derive the transparent values. This prevents the RGB values from falling out of sync if the primary brand hex changes in the future.For example:
color-mix(in srgb, var(--color-primary) 40%, transparent).🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@examples/web/RunAnywhereAI/src/styles/components.css` around lines 2387 - 2388, Update the box-shadow colors in the animation containing the 0%, 50%, and 100% keyframes to use color-mix with var(--color-primary) instead of hardcoded RGB values, preserving the existing 40% opacity effect and transparent final state.sdk/runanywhere-cli/src/net/control_plane.cpp (1)
230-240: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win
platform_name()duplicatesdesktop_platform()inbootstrap.cpp.Both functions implement the identical
#if defined(__APPLE__)/__linux__/_WIN32chain returning "macos"/"linux"/"windows"/"desktop" (bootstrap.cpp Lines 143-153). Sincecontrol_plane.his now a public header,bootstrap.cppcould reusenet::platform_name()instead of maintaining a second copy that can silently drift.♻️ Proposed consolidation
// bootstrap.cpp -const char *desktop_platform() { -#if defined(__APPLE__) - return "macos"; -#elif defined(__linux__) - return "linux"; -#elif defined(_WIN32) - return "windows"; -#else - return "desktop"; -#endif -} +// use net::platform_name() instead (see net/control_plane.h)- sdk_config.platform = desktop_platform(); + sdk_config.platform = net::platform_name();🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@sdk/runanywhere-cli/src/net/control_plane.cpp` around lines 230 - 240, Remove the duplicate platform detection logic from desktop_platform() in bootstrap.cpp and reuse net::platform_name() from control_plane.h instead. Preserve the existing platform string results and ensure bootstrap.cpp references the shared function rather than maintaining its own preprocessor chain.sdk/runanywhere-cli/src/commands/cmd_auth.cpp (1)
63-78: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick winJSON
token_expires_atloses the raw epoch value.For
--json(machine-readable) output,format_epoch_secondsreturns an ISO-8601 string, so scripted consumers must re-parse a date string instead of reading a Unix timestamp directly. Consider emitting both the raw epoch integer and the formatted string in JSON mode.♻️ Proposed fix
.field("device_uuid", summary.persistent_device_id) - .field("token_expires_at", format_epoch_seconds(summary.token_expires_at)) + .field("token_expires_at", static_cast<int64_t>(summary.token_expires_at)) + .field("token_expires_at_iso", format_epoch_seconds(summary.token_expires_at))🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@sdk/runanywhere-cli/src/commands/cmd_auth.cpp` around lines 63 - 78, Update the JSON output in the options.json branch to preserve the raw Unix timestamp alongside the existing formatted token_expires_at value. Add a distinct field using summary.token_expires_at directly, while keeping token_expires_at as the ISO-8601 string and leaving non-JSON output unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@examples/flutter/RunAnywhereAI/lib/app/runanywhere_ai_app.dart`:
- Around line 219-234: Update the light and dark ThemeData color schemes so the
custom primary override does not retain an incompatible seed-derived onPrimary
value: either remove copyWith(primary: AppColors.brandOrange) or explicitly set
onPrimary to a contrasting color alongside the custom primary. Apply the same
correction wherever AppColors.brandOrange is assigned as primary.
In `@examples/react-native/RunAnywhereAI/src/screens/ChatScreen.tsx`:
- Around line 791-793: Update the active LoRA pill color expression near
loraAdapterCount so its text and icon meet AA contrast in light mode against
colors.primary; use the existing darker ink color rather than colors.onPrimary,
while preserving the inactive colors.primary state.
In `@examples/web/RunAnywhereAI/src/main.ts`:
- Around line 636-637: Replace the hardcoded colors in the SVG gradient stops
near the gradient definition with existing design-system CSS custom-property
tokens, using the appropriate primary and gradient-end variables (such as
--color-primary and --bubble-user-end). If no suitable end-color token exists,
add one in the relevant CSS and reference it from the SVG instead of embedding
hex values in the view.
In `@sdk/runanywhere-cli/src/bootstrap.cpp`:
- Around line 168-182: Update initialize_sdk_metadata so a non-RAC_SUCCESS
result from rac_state_initialize is propagated as failure instead of only
warning and continuing to rac_sdk_init. Ensure bootstrap() handles that returned
failure and does not report successful initialization, while preserving the
existing result description for diagnostics.
In `@sdk/runanywhere-cli/src/commands/cmd_telemetry.cpp`:
- Around line 210-246: Move the telemetry orchestration out of
run_telemetry_session, emit, and blast into a single higher-level commons/net
entry point that owns login, manager setup, tracking, flush, destruction,
endpoint accounting, and callback handling. Update these command functions to
only parse options, invoke that helper, and render the returned result, removing
CLI-side modality, endpoint, UUID, and response-accounting logic. Preserve
existing success and failure behavior.
- Around line 100-116: Update extract_bool_field to skip optional whitespace
between the colon and the boolean value before comparing against “true”.
Preserve the existing false result when the key is absent or the value is not
true, and leave extract_int_field unchanged.
- Around line 356-380: In the telemetry summary loop around kModalities, remove
the hardcoded endpoint reconstruction and locate the matching EndpointStats
entry by its modality-specific endpoint suffix, using the actual endpoint keys
captured by the callback. Preserve the existing row status, counters, and all_ok
aggregation once the matching stats are found, while retaining the "NO POST"
fallback when no endpoint matches.
In `@sdk/runanywhere-cli/src/net/control_plane.cpp`:
- Around line 62-117: Move hostname, device-model, OS-version, and chip-name
retrieval behind the rac_platform_adapter_t interface, adding adapter
capabilities modeled on get_memory_info and implementing them in the desktop
adapter. Update device_get_info and the query helpers to consume
adapter-provided values, removing direct calls to std::getenv, uname, and
sysctlbyname from the network module while preserving existing fallback
behavior.
- Around line 392-399: Update the authentication response handling around
rac_auth_handle_authenticate_response so RAC_ERROR_SECURE_STORAGE_FAILED no
longer proceeds silently: surface a clear warning to the user while preserving
the existing successful flow and distinct rejection handling for other
non-success results.
---
Nitpick comments:
In `@examples/web/RunAnywhereAI/src/styles/components.css`:
- Line 474: Replace the hardcoded rgba box-shadow color at the affected style
declaration with the existing --color-primary-light design-system token from
design-system.css, preserving the current shadow dimensions and opacity
behavior.
- Around line 2387-2388: Update the box-shadow colors in the animation
containing the 0%, 50%, and 100% keyframes to use color-mix with
var(--color-primary) instead of hardcoded RGB values, preserving the existing
40% opacity effect and transparent final state.
In `@sdk/runanywhere-cli/src/commands/cmd_auth.cpp`:
- Around line 63-78: Update the JSON output in the options.json branch to
preserve the raw Unix timestamp alongside the existing formatted
token_expires_at value. Add a distinct field using summary.token_expires_at
directly, while keeping token_expires_at as the ISO-8601 string and leaving
non-JSON output unchanged.
In `@sdk/runanywhere-cli/src/net/control_plane.cpp`:
- Around line 230-240: Remove the duplicate platform detection logic from
desktop_platform() in bootstrap.cpp and reuse net::platform_name() from
control_plane.h instead. Preserve the existing platform string results and
ensure bootstrap.cpp references the shared function rather than maintaining its
own preprocessor chain.
🪄 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: e7bc59ca-823b-4592-a5d3-2dc70fc2ba44
📒 Files selected for processing (53)
AGENTS.mdexamples/DESIGN_GUIDELINE.mdexamples/android/RunAnywhereAI/AGENTS.mdexamples/android/RunAnywhereAI/app/src/main/java/com/runanywhere/runanywhereai/ui/screens/benchmark/BenchmarkShareCard.ktexamples/android/RunAnywhereAI/app/src/main/java/com/runanywhere/runanywhereai/ui/theme/Color.ktexamples/android/RunAnywhereAI/app/src/main/res/values/colors.xmlexamples/flutter/RunAnywhereAI/AGENTS.mdexamples/flutter/RunAnywhereAI/lib/app/content_view.dartexamples/flutter/RunAnywhereAI/lib/app/runanywhere_ai_app.dartexamples/flutter/RunAnywhereAI/lib/core/design_system/app_colors.dartexamples/ios/RunAnywhereAI/AGENTS.mdexamples/ios/RunAnywhereAI/RunAnywhereAI/Assets.xcassets/AccentColor.colorset/Contents.jsonexamples/ios/RunAnywhereAI/RunAnywhereAI/Core/DesignSystem/AppColors.swiftexamples/ios/RunAnywhereAI/RunAnywhereAI/Features/VoiceKeyboard/FlowActivationView.swiftexamples/ios/RunAnywhereAI/RunAnywhereActivityExtension/RunAnywhereActivityExtensionLiveActivity.swiftexamples/ios/RunAnywhereAI/RunAnywhereKeyboard/KeyboardView.swiftexamples/react-native/RunAnywhereAI/AGENTS.mdexamples/react-native/RunAnywhereAI/App.tsxexamples/react-native/RunAnywhereAI/src/components/chat/LoRASheet.tsxexamples/react-native/RunAnywhereAI/src/components/chat/ToolCallIndicator.tsxexamples/react-native/RunAnywhereAI/src/components/chat/TypingIndicator.tsxexamples/react-native/RunAnywhereAI/src/components/common/LoadingOverlay.tsxexamples/react-native/RunAnywhereAI/src/components/common/ModelStatusBanner.tsxexamples/react-native/RunAnywhereAI/src/navigation/BottomTabs.tsxexamples/react-native/RunAnywhereAI/src/screens/ChatAnalyticsScreen.tsxexamples/react-native/RunAnywhereAI/src/screens/ChatScreen.tsxexamples/react-native/RunAnywhereAI/src/screens/StorageScreen.tsxexamples/react-native/RunAnywhereAI/src/screens/VLMScreen.tsxexamples/react-native/RunAnywhereAI/src/theme/colors.tsexamples/react-native/RunAnywhereAI/src/theme/index.tsexamples/react-native/RunAnywhereAI/src/theme/spacing.tsexamples/react-native/RunAnywhereAI/src/theme/system/colors.tsexamples/react-native/RunAnywhereAI/src/theme/system/index.tsexamples/react-native/RunAnywhereAI/src/theme/system/themedStyles.tsexamples/react-native/RunAnywhereAI/src/theme/typography.tsexamples/react-native/RunAnywhereAI/src/utils/modelDisplay.tsexamples/web/RunAnywhereAI/AGENTS.mdexamples/web/RunAnywhereAI/index.htmlexamples/web/RunAnywhereAI/src/main.tsexamples/web/RunAnywhereAI/src/styles/components.cssexamples/web/RunAnywhereAI/src/styles/design-system.csssdk/runanywhere-cli/CMakeLists.txtsdk/runanywhere-cli/README.mdsdk/runanywhere-cli/src/app.cppsdk/runanywhere-cli/src/bootstrap.cppsdk/runanywhere-cli/src/bootstrap.hsdk/runanywhere-cli/src/commands/cmd_auth.cppsdk/runanywhere-cli/src/commands/cmd_telemetry.cppsdk/runanywhere-cli/src/commands/commands.hsdk/runanywhere-cli/src/net/control_plane.cppsdk/runanywhere-cli/src/net/control_plane.hsdk/runanywhere-commons/exports/RACommons.exportssdk/runanywhere-commons/include/rac/infrastructure/telemetry/rac_telemetry_manager.h
💤 Files with no reviewable changes (6)
- examples/react-native/RunAnywhereAI/src/theme/colors.ts
- examples/react-native/RunAnywhereAI/src/theme/spacing.ts
- examples/react-native/RunAnywhereAI/src/theme/index.ts
- sdk/runanywhere-commons/include/rac/infrastructure/telemetry/rac_telemetry_manager.h
- examples/react-native/RunAnywhereAI/src/theme/typography.ts
- sdk/runanywhere-commons/exports/RACommons.exports
| theme: ThemeData( | ||
| colorScheme: ColorScheme.fromSeed( | ||
| seedColor: AppColors.primaryBlue, | ||
| seedColor: AppColors.brandOrange, | ||
| brightness: Brightness.light, | ||
| ), | ||
| ).copyWith(primary: AppColors.brandOrange), | ||
| useMaterial3: true, | ||
| appBarTheme: const AppBarTheme(centerTitle: true, elevation: 0), | ||
| navigationBarTheme: NavigationBarThemeData( | ||
| indicatorColor: AppColors.primaryBlue.withValues(alpha: 0.2), | ||
| indicatorColor: AppColors.primaryAccent.withValues(alpha: 0.2), | ||
| ), | ||
| ), | ||
| darkTheme: ThemeData( | ||
| colorScheme: ColorScheme.fromSeed( | ||
| seedColor: AppColors.primaryBlue, | ||
| seedColor: AppColors.brandOrange, | ||
| brightness: Brightness.dark, | ||
| ), | ||
| ).copyWith(primary: AppColors.brandOrange), |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🌐 Web query:
white text contrast ratio on #FF6900 WCAG AA pass
💡 Result:
White text on the color #FF6900 does not pass WCAG AA requirements [1]. To meet WCAG AA (Success Criterion 1.4.3), text must maintain a contrast ratio of at least 4.5:1 against its background, or 3:1 for large-scale text [2][3][4]. The combination of white (hex #FFFFFF) and the orange shade #FF6900 produces a contrast ratio of approximately 2.89:1 [1]. Because this ratio is below both the 4.5:1 threshold for normal text and the 3:1 threshold for large text, it fails to meet WCAG AA compliance [2][1][4]. For reference, #FF6900 provides better contrast when used with dark text; for example, using black text (#000000) on a #FF6900 background results in a ratio of approximately 7.27:1, which passes both WCAG AA and AAA requirements [1].
Citations:
- 1: https://colorfyi.com/color/FF6900/
- 2: https://www.w3.org/WAI/WCAG22/Understanding/contrast-minimum
- 3: https://www.w3.org/TR/wcag/
- 4: https://web-accessibility-checker.com/en/blog/color-contrast-wcag-guide
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Inspect the relevant theme file around the reported lines.
sed -n '180,280p' examples/flutter/RunAnywhereAI/lib/app/runanywhere_ai_app.dart
# Look for any other overrides that might affect primary/onPrimary in this app.
rg -n "copyWith\\(primary:|onPrimary|ColorScheme.fromSeed|brandOrange|primaryAccent" examples/flutter/RunAnywhereAI/lib -g '*.dart'Repository: RunanywhereAI/runanywhere-sdks
Length of output: 6166
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Read the theme file with line numbers for exact context.
cat -n examples/flutter/RunAnywhereAI/lib/app/runanywhere_ai_app.dart | sed -n '200,260p'
# Check whether the same pattern appears elsewhere.
rg -n "copyWith\\(primary: AppColors\\.brandOrange|ColorScheme\\.fromSeed\\(|onPrimary" examples/flutter/RunAnywhereAI/lib -g '*.dart'Repository: RunanywhereAI/runanywhere-sdks
Length of output: 2974
🏁 Script executed:
#!/bin/bash
set -euo pipefail
cat -n examples/flutter/RunAnywhereAI/lib/app/runanywhere_ai_app.dart | sed -n '200,260p'
rg -n "copyWith\\(primary: AppColors\\.brandOrange|ColorScheme\\.fromSeed\\(|onPrimary" examples/flutter/RunAnywhereAI/lib -g '*.dart'Repository: RunanywhereAI/runanywhere-sdks
Length of output: 2974
Keep onPrimary aligned with the custom primary
ColorScheme.fromSeed(...) seeds a tonal palette, but copyWith(primary: AppColors.brandOrange) leaves onPrimary at the seed-derived value. White text on #FF6900 is only ~2.9:1, so primary-colored surfaces can miss WCAG AA. Either drop the override or set a contrasting onPrimary.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@examples/flutter/RunAnywhereAI/lib/app/runanywhere_ai_app.dart` around lines
219 - 234, Update the light and dark ThemeData color schemes so the custom
primary override does not retain an incompatible seed-derived onPrimary value:
either remove copyWith(primary: AppColors.brandOrange) or explicitly set
onPrimary to a contrasting color alongside the custom primary. Apply the same
correction wherever AppColors.brandOrange is assigned as primary.
| color={ | ||
| loraAdapterCount > 0 | ||
| ? Colors.textWhite | ||
| : Colors.primaryPurple | ||
| loraAdapterCount > 0 ? colors.onPrimary : colors.primary | ||
| } |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
rg -nP -C2 'onPrimary' examples/react-native/RunAnywhereAI/src/theme/system/colors.tsRepository: RunanywhereAI/runanywhere-sdks
Length of output: 967
🏁 Script executed:
#!/bin/bash
set -euo pipefail
sed -n '1,260p' examples/react-native/RunAnywhereAI/src/theme/system/colors.ts
printf '\n--- contrast check ---\n'
python3 - <<'PY'
from math import isclose
# Replace with palette values from the file if needed after inspection.
# This script is ready to compute contrast once the hex values are known.
PYRepository: RunanywhereAI/runanywhere-sdks
Length of output: 7311
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Show the relevant section around the active LoRA pill.
sed -n '760,820p' examples/react-native/RunAnywhereAI/src/screens/ChatScreen.tsx
printf '\n--- contrast ratio ---\n'
python3 - <<'PY'
def hex_to_rgb(h):
h = h.lstrip('#')
return tuple(int(h[i:i+2], 16)/255.0 for i in (0,2,4))
def rel_lum(rgb):
def f(c):
return c/12.92 if c <= 0.03928 else ((c+0.055)/1.055) ** 2.4
r,g,b = (f(c) for c in rgb)
return 0.2126*r + 0.7152*g + 0.0722*b
def contrast(a, b):
la, lb = rel_lum(hex_to_rgb(a)), rel_lum(hex_to_rgb(b))
hi, lo = max(la, lb), min(la, lb)
return (hi + 0.05) / (lo + 0.05)
fg = "`#FFFBFF`" # lightScheme.onPrimary
bg = "`#FF6900`" # primary
print(f"contrast({fg}, {bg}) = {contrast(fg, bg):.2f}:1")
PYRepository: RunanywhereAI/runanywhere-sdks
Length of output: 2329
Active LoRA pill needs a darker active color in light mode
examples/react-native/RunAnywhereAI/src/screens/ChatScreen.tsx:791-793 uses colors.onPrimary on colors.primary in light mode, which is #FFFBFF on #FF6900 (~2.82:1). That misses AA for the pill text/icon; switch the active state to a darker ink color or make the active label/icon larger/bolder.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@examples/react-native/RunAnywhereAI/src/screens/ChatScreen.tsx` around lines
791 - 793, Update the active LoRA pill color expression near loraAdapterCount so
its text and icon meet AA contrast in light mode against colors.primary; use the
existing darker ink color rather than colors.onPrimary, while preserving the
inactive colors.primary state.
| <stop offset="0%" style="stop-color:#FF6900"/> | ||
| <stop offset="100%" style="stop-color:#FB2C36"/> |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win
Do not hardcode brand hexes in views.
The SVG gradient uses hardcoded hex values (#FF6900 and #FB2C36). As per coding guidelines, you must use design-system CSS custom-property tokens and never hardcode brand hexes in views.
Consider using existing tokens like var(--color-primary) and var(--bubble-user-end), or adding a dedicated gradient-end token to the CSS instead of hardcoding the hexes here.
♻️ Proposed fix to use CSS tokens
- <stop offset="0%" style="stop-color:`#FF6900`"/>
- <stop offset="100%" style="stop-color:`#FB2C36`"/>
+ <stop offset="0%" style="stop-color:var(--color-primary)"/>
+ <stop offset="100%" style="stop-color:var(--bubble-user-end)"/>📝 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.
| <stop offset="0%" style="stop-color:#FF6900"/> | |
| <stop offset="100%" style="stop-color:#FB2C36"/> | |
| <stop offset="0%" style="stop-color:var(--color-primary)"/> | |
| <stop offset="100%" style="stop-color:var(--bubble-user-end)"/> |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@examples/web/RunAnywhereAI/src/main.ts` around lines 636 - 637, Replace the
hardcoded colors in the SVG gradient stops near the gradient definition with
existing design-system CSS custom-property tokens, using the appropriate primary
and gradient-end variables (such as --color-primary and --bubble-user-end). If
no suitable end-color token exists, add one in the relevant CSS and reference it
from the SVG instead of embedding hex values in the view.
Source: Coding guidelines
| // Mirror rac_sdk_init_phase1_proto's step order: runtime state first (the | ||
| // auth / device-registration / telemetry paths read env + credentials from | ||
| // rac_state), then the copied SDK configuration + client info. | ||
| const rac_result_t state_rc = rac_state_initialize( | ||
| connection.environment, connection.api_key.c_str(), | ||
| connection.base_url.c_str(), device_id[0] != '\0' ? device_id : ""); | ||
| if (state_rc != RAC_SUCCESS) { | ||
| out::status_line("warning: SDK state init failed: " + | ||
| out::describe_result(state_rc)); | ||
| } | ||
|
|
||
| rac_sdk_config_t sdk_config = {}; | ||
| sdk_config.environment = RAC_ENV_DEVELOPMENT; | ||
| sdk_config.api_key = ""; | ||
| sdk_config.base_url = ""; | ||
| sdk_config.environment = connection.environment; | ||
| sdk_config.api_key = connection.api_key.c_str(); | ||
| sdk_config.base_url = connection.base_url.c_str(); |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
Don't silently continue when rac_state_initialize fails.
initialize_sdk_metadata only warns (Line 174-177) when rac_state_initialize fails, then proceeds to call rac_sdk_init and returns normally; bootstrap() treats this as success. Every downstream control-plane read (rac_state_get_environment, rac_state_get_api_key, rac_state_get_base_url in control_plane.cpp) depends on this call having succeeded. If it fails, rcli auth login/rcli telemetry emit will read stale/default state and fail with a confusing, unrelated error (e.g. "development mode has no control plane") instead of surfacing the real root cause.
🛡️ Proposed fix: propagate the failure
-void initialize_sdk_metadata(const Connection &connection) {
+rac_result_t initialize_sdk_metadata(const Connection &connection) {
char device_id[RAC_DEVICE_ID_BUFFER_MIN_SIZE] = {};
...
const rac_result_t state_rc = rac_state_initialize(
connection.environment, connection.api_key.c_str(),
connection.base_url.c_str(), device_id[0] != '\0' ? device_id : "");
if (state_rc != RAC_SUCCESS) {
- out::status_line("warning: SDK state init failed: " +
- out::describe_result(state_rc));
+ out::error_line("SDK state init failed: " + out::describe_result(state_rc));
+ return state_rc;
}
...
+ return RAC_SUCCESS;
}And in bootstrap():
- initialize_sdk_metadata(connection);
+ rc = initialize_sdk_metadata(connection);
+ if (rc != RAC_SUCCESS) {
+ return rc;
+ }📝 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.
| // Mirror rac_sdk_init_phase1_proto's step order: runtime state first (the | |
| // auth / device-registration / telemetry paths read env + credentials from | |
| // rac_state), then the copied SDK configuration + client info. | |
| const rac_result_t state_rc = rac_state_initialize( | |
| connection.environment, connection.api_key.c_str(), | |
| connection.base_url.c_str(), device_id[0] != '\0' ? device_id : ""); | |
| if (state_rc != RAC_SUCCESS) { | |
| out::status_line("warning: SDK state init failed: " + | |
| out::describe_result(state_rc)); | |
| } | |
| rac_sdk_config_t sdk_config = {}; | |
| sdk_config.environment = RAC_ENV_DEVELOPMENT; | |
| sdk_config.api_key = ""; | |
| sdk_config.base_url = ""; | |
| sdk_config.environment = connection.environment; | |
| sdk_config.api_key = connection.api_key.c_str(); | |
| sdk_config.base_url = connection.base_url.c_str(); | |
| // Mirror rac_sdk_init_phase1_proto's step order: runtime state first (the | |
| // auth / device-registration / telemetry paths read env + credentials from | |
| // rac_state), then the copied SDK configuration + client info. | |
| char device_id[RAC_DEVICE_ID_BUFFER_MIN_SIZE] = {}; | |
| const rac_result_t state_rc = rac_state_initialize( | |
| connection.environment, connection.api_key.c_str(), | |
| connection.base_url.c_str(), device_id[0] != '\0' ? device_id : ""); | |
| if (state_rc != RAC_SUCCESS) { | |
| out::error_line("SDK state init failed: " + out::describe_result(state_rc)); | |
| return state_rc; | |
| } | |
| rac_sdk_config_t sdk_config = {}; | |
| sdk_config.environment = connection.environment; | |
| sdk_config.api_key = connection.api_key.c_str(); | |
| sdk_config.base_url = connection.base_url.c_str(); | |
| return RAC_SUCCESS; |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@sdk/runanywhere-cli/src/bootstrap.cpp` around lines 168 - 182, Update
initialize_sdk_metadata so a non-RAC_SUCCESS result from rac_state_initialize is
propagated as failure instead of only warning and continuing to rac_sdk_init.
Ensure bootstrap() handles that returned failure and does not report successful
initialization, while preserving the existing result description for
diagnostics.
| // Minimal field extraction from the backend's SDKTelemetryBatchResponse JSON | ||
| // ({"success":true,"events_received":N,"events_stored":N,"events_skipped":N, | ||
| // "storage_version":"V2"}). The CLI deliberately carries no JSON parser. | ||
| int extract_int_field(const std::string& json, const std::string& key) { | ||
| const std::string needle = "\"" + key + "\":"; | ||
| const size_t pos = json.find(needle); | ||
| if (pos == std::string::npos) { | ||
| return -1; | ||
| } | ||
| return std::atoi(json.c_str() + pos + needle.size()); | ||
| } | ||
|
|
||
| bool extract_bool_field(const std::string& json, const std::string& key) { | ||
| const std::string needle = "\"" + key + "\":"; | ||
| const size_t pos = json.find(needle); | ||
| return pos != std::string::npos && json.compare(pos + needle.size(), 4, "true") == 0; | ||
| } |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win
Manual JSON extraction is brittle to backend formatting changes.
extract_bool_field does a byte-exact comparison of 4 chars right after "key": (Line 115). This only works for compact JSON with no space after the colon (e.g. "success":true). If the backend ever emits pretty-printed or differently-spaced JSON (e.g. "success": true), every response is misclassified as success=false, causing emit/blast to report failures for successful telemetry deliveries. extract_int_field's atoi call is safe here since atoi skips leading whitespace, but extract_bool_field's raw compare is not.
🐛 Proposed fix — skip optional whitespace before comparing
bool extract_bool_field(const std::string& json, const std::string& key) {
const std::string needle = "\"" + key + "\":";
- const size_t pos = json.find(needle);
- return pos != std::string::npos && json.compare(pos + needle.size(), 4, "true") == 0;
+ size_t pos = json.find(needle);
+ if (pos == std::string::npos) {
+ return false;
+ }
+ pos += needle.size();
+ while (pos < json.size() && std::isspace(static_cast<unsigned char>(json[pos]))) {
+ ++pos;
+ }
+ return json.compare(pos, 4, "true") == 0;
}📝 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.
| // Minimal field extraction from the backend's SDKTelemetryBatchResponse JSON | |
| // ({"success":true,"events_received":N,"events_stored":N,"events_skipped":N, | |
| // "storage_version":"V2"}). The CLI deliberately carries no JSON parser. | |
| int extract_int_field(const std::string& json, const std::string& key) { | |
| const std::string needle = "\"" + key + "\":"; | |
| const size_t pos = json.find(needle); | |
| if (pos == std::string::npos) { | |
| return -1; | |
| } | |
| return std::atoi(json.c_str() + pos + needle.size()); | |
| } | |
| bool extract_bool_field(const std::string& json, const std::string& key) { | |
| const std::string needle = "\"" + key + "\":"; | |
| const size_t pos = json.find(needle); | |
| return pos != std::string::npos && json.compare(pos + needle.size(), 4, "true") == 0; | |
| } | |
| // Minimal field extraction from the backend's SDKTelemetryBatchResponse JSON | |
| // ({"success":true,"events_received":N,"events_stored":N,"events_skipped":N, | |
| // "storage_version":"V2"}). The CLI deliberately carries no JSON parser. | |
| int extract_int_field(const std::string& json, const std::string& key) { | |
| const std::string needle = "\"" + key + "\":"; | |
| const size_t pos = json.find(needle); | |
| if (pos == std::string::npos) { | |
| return -1; | |
| } | |
| return std::atoi(json.c_str() + pos + needle.size()); | |
| } | |
| bool extract_bool_field(const std::string& json, const std::string& key) { | |
| const std::string needle = "\"" + key + "\":"; | |
| size_t pos = json.find(needle); | |
| if (pos == std::string::npos) { | |
| return false; | |
| } | |
| pos += needle.size(); | |
| while (pos < json.size() && std::isspace(static_cast<unsigned char>(json[pos]))) { | |
| +pos; | |
| } | |
| return json.compare(pos, 4, "true") == 0; | |
| } |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@sdk/runanywhere-cli/src/commands/cmd_telemetry.cpp` around lines 100 - 116,
Update extract_bool_field to skip optional whitespace between the colon and the
boolean value before comparing against “true”. Preserve the existing false
result when the key is absent or the value is not true, and leave
extract_int_field unchanged.
| /** | ||
| * Login (JWT), create a manager wired to the real transport, run `track_fn`, | ||
| * flush, and account per-endpoint results. Returns false on login failure. | ||
| */ | ||
| template <typename TrackFn> | ||
| bool run_telemetry_session(const GlobalOptions& options, FlushReport* report, TrackFn&& track_fn) { | ||
| Bootstrapped env; | ||
| if (bootstrap(options, &env) != RAC_SUCCESS) { | ||
| return false; | ||
| } | ||
|
|
||
| // The V2 telemetry endpoints only accept a JWT, so emit implies login — | ||
| // one process performs the handshake and the flush (in-process token). | ||
| std::string error; | ||
| if (net::login(nullptr, &error) != RAC_SUCCESS) { | ||
| out::error_line(error); | ||
| return false; | ||
| } | ||
|
|
||
| const char* device_id = rac_state_get_device_id(); | ||
| rac_telemetry_manager_t* manager = rac_telemetry_manager_create( | ||
| rac_state_get_environment(), device_id != nullptr ? device_id : "", net::platform_name(), | ||
| RCLI_VERSION); | ||
| if (manager == nullptr) { | ||
| out::error_line("telemetry manager creation failed"); | ||
| return false; | ||
| } | ||
| rac_telemetry_manager_set_device_info(manager, net::device_model().c_str(), | ||
| net::os_version_string().c_str()); | ||
| rac_telemetry_manager_set_http_callback(manager, telemetry_http_callback, &report->context); | ||
|
|
||
| report->tracked = track_fn(manager); | ||
| rac_telemetry_manager_flush(manager); | ||
| rac_telemetry_manager_set_http_callback(manager, nullptr, nullptr); | ||
| rac_telemetry_manager_destroy(manager); | ||
| return true; | ||
| } |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟠 Major | 🏗️ Heavy lift
Command file orchestrates many commons entry points, contrary to the cmd_*.cpp guideline.
run_telemetry_session (and the emit/blast functions that use it) chains rac_telemetry_manager_create, _set_device_info, _set_http_callback, repeated _track, _flush, _destroy, plus net::login and net::control_plane_post (via the callback) — well beyond "invoke exactly one commons entry point." Contrast with cmd_auth.cpp, which calls bootstrap() + a single net::login() and stays compliant. The modality table, endpoint-path knowledge, UUID generation, and manual response accounting are also CLI-side reimplementations of what looks like SDK-internal orchestration.
Consider exposing a single higher-level commons/net entry point (e.g. a "run telemetry session" helper) that owns manager lifecycle + flush + accounting, leaving this file to just parse flags and render the result — consistent with cmd_auth.cpp's shape.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@sdk/runanywhere-cli/src/commands/cmd_telemetry.cpp` around lines 210 - 246,
Move the telemetry orchestration out of run_telemetry_session, emit, and blast
into a single higher-level commons/net entry point that owns login, manager
setup, tracking, flush, destruction, endpoint accounting, and callback handling.
Update these command functions to only parse options, invoke that helper, and
render the returned result, removing CLI-side modality, endpoint, UUID, and
response-accounting logic. Preserve existing success and failure behavior.
Source: Coding guidelines
| bool all_ok = true; | ||
| std::vector<std::vector<std::string>> rows; | ||
| for (const ModalitySpec& spec : kModalities) { | ||
| const std::string endpoint = std::string("/api/v2/sdk/telemetry/") + spec.name; | ||
| const auto it = report.context.endpoints.find(endpoint); | ||
| std::string status = "NO POST"; | ||
| int received = 0; | ||
| int stored = 0; | ||
| int skipped = 0; | ||
| bool row_ok = false; | ||
| if (it != report.context.endpoints.end()) { | ||
| const EndpointStats& stats = it->second; | ||
| received = stats.received; | ||
| stored = stats.stored; | ||
| skipped = stats.skipped; | ||
| row_ok = stats.failures == 0 && stats.received == count; | ||
| status = row_ok ? ("HTTP " + std::to_string(stats.last_status)) | ||
| : (stats.last_error.empty() | ||
| ? "HTTP " + std::to_string(stats.last_status) | ||
| : stats.last_error); | ||
| } | ||
| all_ok = all_ok && row_ok; | ||
| rows.push_back({spec.name, row_ok ? "ok" : "FAILED", status, std::to_string(received), | ||
| std::to_string(stored), std::to_string(skipped)}); | ||
| } |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Hardcoded endpoint reconstruction duplicates SDK-internal path knowledge and can silently misreport results.
endpoint is rebuilt here as "/api/v2/sdk/telemetry/" + spec.name purely to look up stats in report.context.endpoints, even though that map is already keyed by the actual endpoint string received from commons via the callback's endpoint parameter (Line 139-143). If the real endpoint format ever diverges from this literal (API version bump, trailing segment, casing), every row silently falls into the "NO POST" / FAILED branch even though the POST succeeded — a false failure that's hard to notice since the command still exits non-zero "correctly" but for the wrong reason.
This also duplicates the endpoint-path pattern that commons already owns (per the file's own header comment: "one POST per modality to /api/v2/sdk/telemetry/{modality}"), which the cmd_*.cpp coding guideline flags: "Do not place ... SDK-internal path/framework knowledge in commands."
🐛 Proposed fix — match by suffix instead of reconstructing the full path
- const std::string endpoint = std::string("/api/v2/sdk/telemetry/") + spec.name;
- const auto it = report.context.endpoints.find(endpoint);
+ const auto it = std::find_if(
+ report.context.endpoints.begin(), report.context.endpoints.end(),
+ [&](const auto& kv) {
+ const std::string& ep = kv.first;
+ return ep.size() >= std::string(spec.name).size() + 1 &&
+ ep.compare(ep.size() - std::string(spec.name).size(), std::string::npos,
+ spec.name) == 0;
+ });📝 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.
| bool all_ok = true; | |
| std::vector<std::vector<std::string>> rows; | |
| for (const ModalitySpec& spec : kModalities) { | |
| const std::string endpoint = std::string("/api/v2/sdk/telemetry/") + spec.name; | |
| const auto it = report.context.endpoints.find(endpoint); | |
| std::string status = "NO POST"; | |
| int received = 0; | |
| int stored = 0; | |
| int skipped = 0; | |
| bool row_ok = false; | |
| if (it != report.context.endpoints.end()) { | |
| const EndpointStats& stats = it->second; | |
| received = stats.received; | |
| stored = stats.stored; | |
| skipped = stats.skipped; | |
| row_ok = stats.failures == 0 && stats.received == count; | |
| status = row_ok ? ("HTTP " + std::to_string(stats.last_status)) | |
| : (stats.last_error.empty() | |
| ? "HTTP " + std::to_string(stats.last_status) | |
| : stats.last_error); | |
| } | |
| all_ok = all_ok && row_ok; | |
| rows.push_back({spec.name, row_ok ? "ok" : "FAILED", status, std::to_string(received), | |
| std::to_string(stored), std::to_string(skipped)}); | |
| } | |
| bool all_ok = true; | |
| std::vector<std::vector<std::string>> rows; | |
| for (const ModalitySpec& spec : kModalities) { | |
| const auto it = std::find_if( | |
| report.context.endpoints.begin(), report.context.endpoints.end(), | |
| [&](const auto& kv) { | |
| const std::string& ep = kv.first; | |
| return ep.size() >= std::string(spec.name).size() + 1 && | |
| ep.compare(ep.size() - std::string(spec.name).size(), std::string::npos, | |
| spec.name) == 0; | |
| }); | |
| std::string status = "NO POST"; | |
| int received = 0; | |
| int stored = 0; | |
| int skipped = 0; | |
| bool row_ok = false; | |
| if (it != report.context.endpoints.end()) { | |
| const EndpointStats& stats = it->second; | |
| received = stats.received; | |
| stored = stats.stored; | |
| skipped = stats.skipped; | |
| row_ok = stats.failures == 0 && stats.received == count; | |
| status = row_ok ? ("HTTP " + std::to_string(stats.last_status)) | |
| : (stats.last_error.empty() | |
| ? "HTTP " + std::to_string(stats.last_status) | |
| : stats.last_error); | |
| } | |
| all_ok = all_ok && row_ok; | |
| rows.push_back({spec.name, row_ok ? "ok" : "FAILED", status, std::to_string(received), | |
| std::to_string(stored), std::to_string(skipped)}); | |
| } |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@sdk/runanywhere-cli/src/commands/cmd_telemetry.cpp` around lines 356 - 380,
In the telemetry summary loop around kModalities, remove the hardcoded endpoint
reconstruction and locate the matching EndpointStats entry by its
modality-specific endpoint suffix, using the actual endpoint keys captured by
the callback. Preserve the existing row status, counters, and all_ok aggregation
once the matching stats are found, while retaining the "NO POST" fallback when
no endpoint matches.
Source: Coding guidelines
| std::string query_hostname() { | ||
| #if defined(_WIN32) | ||
| const char* name = std::getenv("COMPUTERNAME"); | ||
| return name != nullptr ? name : "windows-host"; | ||
| #else | ||
| struct utsname info{}; | ||
| if (uname(&info) == 0 && info.nodename[0] != '\0') { | ||
| return info.nodename; | ||
| } | ||
| return "desktop-host"; | ||
| #endif | ||
| } | ||
|
|
||
| std::string query_device_model() { | ||
| #if defined(__APPLE__) | ||
| char model[128] = {}; | ||
| size_t size = sizeof(model); | ||
| if (sysctlbyname("hw.model", model, &size, nullptr, 0) == 0 && model[0] != '\0') { | ||
| return model; | ||
| } | ||
| return "Mac"; | ||
| #elif defined(_WIN32) | ||
| return "Windows PC"; | ||
| #else | ||
| struct utsname info{}; | ||
| if (uname(&info) == 0 && info.machine[0] != '\0') { | ||
| return std::string(info.sysname[0] != '\0' ? info.sysname : "Linux") + " " + info.machine; | ||
| } | ||
| return "Linux PC"; | ||
| #endif | ||
| } | ||
|
|
||
| std::string query_os_version() { | ||
| #if defined(_WIN32) | ||
| return {}; | ||
| #else | ||
| struct utsname info{}; | ||
| if (uname(&info) == 0 && info.release[0] != '\0') { | ||
| // Backend os_version column caps at 20 chars. | ||
| return std::string(info.release).substr(0, 20); | ||
| } | ||
| return {}; | ||
| #endif | ||
| } | ||
|
|
||
| std::string query_chip_name() { | ||
| #if defined(__APPLE__) | ||
| char brand[256] = {}; | ||
| size_t size = sizeof(brand); | ||
| if (sysctlbyname("machdep.cpu.brand_string", brand, &size, nullptr, 0) == 0 && | ||
| brand[0] != '\0') { | ||
| return brand; | ||
| } | ||
| #endif | ||
| return {}; | ||
| } |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟠 Major | 🏗️ Heavy lift
Direct platform-API calls bypass the rac_platform_adapter_t IoC boundary.
query_hostname, query_device_model, query_os_version, and query_chip_name call uname, sysctlbyname, and std::getenv directly. This same function's caller (device_get_info, Line 165-170) already fetches comparable device metadata (memory) through rac_get_platform_adapter(), showing the adapter is the intended path for this class of data. Querying the OS directly here duplicates responsibility that belongs behind the adapter interface and bypasses the inversion-of-control boundary the SDK otherwise enforces.
Consider exposing hostname/device-model/OS-version/chip info as additional rac_platform_adapter_t capabilities (mirroring get_memory_info) so rcli's desktop adapter implementation supplies this data through the same IoC surface other consumers use, rather than rcli's own network module querying the OS.
As per coding guidelines, "C++ must access platform services only through the rac_platform_adapter_t inversion-of-control interface; it must not call platform APIs directly."
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@sdk/runanywhere-cli/src/net/control_plane.cpp` around lines 62 - 117, Move
hostname, device-model, OS-version, and chip-name retrieval behind the
rac_platform_adapter_t interface, adding adapter capabilities modeled on
get_memory_info and implementing them in the desktop adapter. Update
device_get_info and the query helpers to consume adapter-provided values,
removing direct calls to std::getenv, uname, and sysctlbyname from the network
module while preserving existing fallback behavior.
| const int auth_rc = rac_auth_handle_authenticate_response(response.body.c_str()); | ||
| if (auth_rc != RAC_SUCCESS && auth_rc != RAC_ERROR_SECURE_STORAGE_FAILED) { | ||
| if (error != nullptr) { | ||
| *error = "authentication response rejected: " + single_line_preview(response.body); | ||
| } | ||
| return RAC_ERROR_INVALID_RESPONSE; | ||
| } | ||
| } |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win
Secure-storage failure is swallowed silently.
When rac_auth_handle_authenticate_response returns RAC_ERROR_SECURE_STORAGE_FAILED, the code treats the login as successful and continues with no warning to the user (Line 393). The auth token may not have been persisted securely, but nothing surfaces that degradation.
🛡️ Proposed fix: surface the degradation
const int auth_rc = rac_auth_handle_authenticate_response(response.body.c_str());
- if (auth_rc != RAC_SUCCESS && auth_rc != RAC_ERROR_SECURE_STORAGE_FAILED) {
+ if (auth_rc == RAC_ERROR_SECURE_STORAGE_FAILED) {
+ out::status_line("warning: auth token could not be stored securely");
+ } else if (auth_rc != RAC_SUCCESS) {
if (error != nullptr) {
*error = "authentication response rejected: " + single_line_preview(response.body);
}
return RAC_ERROR_INVALID_RESPONSE;
}📝 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.
| const int auth_rc = rac_auth_handle_authenticate_response(response.body.c_str()); | |
| if (auth_rc != RAC_SUCCESS && auth_rc != RAC_ERROR_SECURE_STORAGE_FAILED) { | |
| if (error != nullptr) { | |
| *error = "authentication response rejected: " + single_line_preview(response.body); | |
| } | |
| return RAC_ERROR_INVALID_RESPONSE; | |
| } | |
| } | |
| const int auth_rc = rac_auth_handle_authenticate_response(response.body.c_str()); | |
| if (auth_rc == RAC_ERROR_SECURE_STORAGE_FAILED) { | |
| out::status_line("warning: auth token could not be stored securely"); | |
| } else if (auth_rc != RAC_SUCCESS) { | |
| if (error != nullptr) { | |
| *error = "authentication response rejected: " + single_line_preview(response.body); | |
| } | |
| return RAC_ERROR_INVALID_RESPONSE; | |
| } | |
| } |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@sdk/runanywhere-cli/src/net/control_plane.cpp` around lines 392 - 399, Update
the authentication response handling around
rac_auth_handle_authenticate_response so RAC_ERROR_SECURE_STORAGE_FAILED no
longer proceeds silently: surface a clear warning to the user while preserving
the existing successful flow and distinct rejection handling for other
non-success results.
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@sdk/runanywhere-cli/src/commands/cmd_telemetry.cpp`:
- Around line 139-141: Update the request body construction in the
control_plane_post call to use a zero length whenever json_body is null, while
preserving json_length when the pointer is valid. Ensure the fallback empty
string can never be constructed with a non-zero length.
In `@sdk/runanywhere-cli/src/net/control_plane.cpp`:
- Around line 404-421: Update login() to call rac_proto_buffer_free(&out_buffer)
after rac_sdk_init_phase2_proto completes and before both the failure return in
the parse/phase2_rc check and the successful return path. Ensure the
caller-owned buffer is released exactly once on every exit while preserving the
existing result and error handling.
🪄 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: f9ec4151-abd7-4128-a1d3-31eb01bb7555
📒 Files selected for processing (53)
AGENTS.mdexamples/DESIGN_GUIDELINE.mdexamples/android/RunAnywhereAI/AGENTS.mdexamples/android/RunAnywhereAI/app/src/main/java/com/runanywhere/runanywhereai/ui/screens/benchmark/BenchmarkShareCard.ktexamples/android/RunAnywhereAI/app/src/main/java/com/runanywhere/runanywhereai/ui/theme/Color.ktexamples/android/RunAnywhereAI/app/src/main/res/values/colors.xmlexamples/flutter/RunAnywhereAI/AGENTS.mdexamples/flutter/RunAnywhereAI/lib/app/content_view.dartexamples/flutter/RunAnywhereAI/lib/app/runanywhere_ai_app.dartexamples/flutter/RunAnywhereAI/lib/core/design_system/app_colors.dartexamples/ios/RunAnywhereAI/AGENTS.mdexamples/ios/RunAnywhereAI/RunAnywhereAI/Assets.xcassets/AccentColor.colorset/Contents.jsonexamples/ios/RunAnywhereAI/RunAnywhereAI/Core/DesignSystem/AppColors.swiftexamples/ios/RunAnywhereAI/RunAnywhereAI/Features/VoiceKeyboard/FlowActivationView.swiftexamples/ios/RunAnywhereAI/RunAnywhereActivityExtension/RunAnywhereActivityExtensionLiveActivity.swiftexamples/ios/RunAnywhereAI/RunAnywhereKeyboard/KeyboardView.swiftexamples/react-native/RunAnywhereAI/AGENTS.mdexamples/react-native/RunAnywhereAI/App.tsxexamples/react-native/RunAnywhereAI/src/components/chat/LoRASheet.tsxexamples/react-native/RunAnywhereAI/src/components/chat/ToolCallIndicator.tsxexamples/react-native/RunAnywhereAI/src/components/chat/TypingIndicator.tsxexamples/react-native/RunAnywhereAI/src/components/common/LoadingOverlay.tsxexamples/react-native/RunAnywhereAI/src/components/common/ModelStatusBanner.tsxexamples/react-native/RunAnywhereAI/src/navigation/BottomTabs.tsxexamples/react-native/RunAnywhereAI/src/screens/ChatAnalyticsScreen.tsxexamples/react-native/RunAnywhereAI/src/screens/ChatScreen.tsxexamples/react-native/RunAnywhereAI/src/screens/StorageScreen.tsxexamples/react-native/RunAnywhereAI/src/screens/VLMScreen.tsxexamples/react-native/RunAnywhereAI/src/theme/colors.tsexamples/react-native/RunAnywhereAI/src/theme/index.tsexamples/react-native/RunAnywhereAI/src/theme/spacing.tsexamples/react-native/RunAnywhereAI/src/theme/system/colors.tsexamples/react-native/RunAnywhereAI/src/theme/system/index.tsexamples/react-native/RunAnywhereAI/src/theme/system/themedStyles.tsexamples/react-native/RunAnywhereAI/src/theme/typography.tsexamples/react-native/RunAnywhereAI/src/utils/modelDisplay.tsexamples/web/RunAnywhereAI/AGENTS.mdexamples/web/RunAnywhereAI/index.htmlexamples/web/RunAnywhereAI/src/main.tsexamples/web/RunAnywhereAI/src/styles/components.cssexamples/web/RunAnywhereAI/src/styles/design-system.csssdk/runanywhere-cli/CMakeLists.txtsdk/runanywhere-cli/README.mdsdk/runanywhere-cli/src/app.cppsdk/runanywhere-cli/src/bootstrap.cppsdk/runanywhere-cli/src/bootstrap.hsdk/runanywhere-cli/src/commands/cmd_auth.cppsdk/runanywhere-cli/src/commands/cmd_telemetry.cppsdk/runanywhere-cli/src/commands/commands.hsdk/runanywhere-cli/src/net/control_plane.cppsdk/runanywhere-cli/src/net/control_plane.hsdk/runanywhere-commons/exports/RACommons.exportssdk/runanywhere-commons/include/rac/infrastructure/telemetry/rac_telemetry_manager.h
💤 Files with no reviewable changes (6)
- examples/react-native/RunAnywhereAI/src/theme/colors.ts
- sdk/runanywhere-commons/exports/RACommons.exports
- examples/react-native/RunAnywhereAI/src/theme/spacing.ts
- examples/react-native/RunAnywhereAI/src/theme/index.ts
- examples/react-native/RunAnywhereAI/src/theme/typography.ts
- sdk/runanywhere-commons/include/rac/infrastructure/telemetry/rac_telemetry_manager.h
| const net::HttpResult result = net::control_plane_post( | ||
| endpoint, std::string(json_body != nullptr ? json_body : "", json_length), | ||
| requires_auth == RAC_TRUE); |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win
Guard the string length when json_body is null.
std::string(json_body != nullptr ? json_body : "", json_length) still passes json_length even when the pointer fell back to "". If commons ever hands a null body with a non-zero length, this reads past the empty literal (OOB). Zero the length in the null branch.
🛡️ Proposed fix
- const net::HttpResult result = net::control_plane_post(
- endpoint, std::string(json_body != nullptr ? json_body : "", json_length),
- requires_auth == RAC_TRUE);
+ const net::HttpResult result = net::control_plane_post(
+ endpoint, json_body != nullptr ? std::string(json_body, json_length) : std::string(),
+ requires_auth == RAC_TRUE);📝 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.
| const net::HttpResult result = net::control_plane_post( | |
| endpoint, std::string(json_body != nullptr ? json_body : "", json_length), | |
| requires_auth == RAC_TRUE); | |
| const net::HttpResult result = net::control_plane_post( | |
| endpoint, json_body != nullptr ? std::string(json_body, json_length) : std::string(), | |
| requires_auth == RAC_TRUE); |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@sdk/runanywhere-cli/src/commands/cmd_telemetry.cpp` around lines 139 - 141,
Update the request body construction in the control_plane_post call to use a
zero length whenever json_body is null, while preserving json_length when the
pointer is valid. Ensure the fallback empty string can never be constructed with
a non-zero length.
| v1::SdkInitPhase2Request request; | ||
| const std::string request_bytes = proto::serialize(request); | ||
| rac_proto_buffer_t out_buffer; | ||
| rac_proto_buffer_init(&out_buffer); | ||
| const rac_result_t phase2_rc = rac_sdk_init_phase2_proto( | ||
| request_bytes.empty() ? nullptr | ||
| : reinterpret_cast<const uint8_t*>(request_bytes.data()), | ||
| request_bytes.size(), &out_buffer); | ||
| v1::SdkInitResult result; | ||
| std::string parse_error; | ||
| if (!proto::parse_proto_buffer(&out_buffer, &result, &parse_error) || | ||
| phase2_rc != RAC_SUCCESS) { | ||
| if (error != nullptr) { | ||
| *error = "services init failed: " + | ||
| (parse_error.empty() ? out::describe_result(phase2_rc) : parse_error); | ||
| } | ||
| return phase2_rc != RAC_SUCCESS ? phase2_rc : RAC_ERROR_INVALID_RESPONSE; | ||
| } |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Confirm the proto buffer ownership/free contract in commons.
rg -nP '\brac_proto_buffer_(init|free|reset|destroy)\b' -g '*.h' -g '*.hpp' -g '*.c' -g '*.cpp'Repository: RunanywhereAI/runanywhere-sdks
Length of output: 168
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Locate the control plane implementation and the proto buffer API definitions/usages.
git ls-files 'sdk/runanywhere-cli/src/net/control_plane.cpp' \
'sdk/runanywhere-commons/**' \
'sdk/**' \
| sed -n '1,200p'
echo '--- rac_sdk_init_phase2_proto usages ---'
rg -n "rac_sdk_init_phase2_proto|rac_proto_buffer_" sdk -g '*.h' -g '*.hpp' -g '*.c' -g '*.cpp'
echo '--- control_plane.cpp around the cited lines ---'
sed -n '360,460p' sdk/runanywhere-cli/src/net/control_plane.cppRepository: RunanywhereAI/runanywhere-sdks
Length of output: 50387
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo '--- proto buffer API declaration ---'
sed -n '1,220p' sdk/runanywhere-swift/Sources/RunAnywhere/CRACommons/include/rac_error_proto.h
echo '--- proto buffer implementation ---'
fd -a 'rac_proto_buffer.cpp' sdk/runanywhere-commons sdk/runanywhere-swift | head -n 20Repository: RunanywhereAI/runanywhere-sdks
Length of output: 3700
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo '--- control_plane.cpp around login() ---'
sed -n '1,260p' sdk/runanywhere-cli/src/net/control_plane.cpp
echo '--- phase2 function implementation ---'
sed -n '560,640p' sdk/runanywhere-commons/src/lifecycle/sdk_init.cppRepository: RunanywhereAI/runanywhere-sdks
Length of output: 12090
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo '--- control_plane.cpp around login() ---'
sed -n '360,450p' sdk/runanywhere-cli/src/net/control_plane.cpp
echo '--- phase2 function implementation ---'
sed -n '580,630p' sdk/runanywhere-commons/src/lifecycle/sdk_init.cppRepository: RunanywhereAI/runanywhere-sdks
Length of output: 6455
Free out_buffer before returning from login()
rac_proto_buffer_init(&out_buffer) creates caller-owned storage, and this path never calls rac_proto_buffer_free(&out_buffer) on either the success or failure exits after rac_sdk_init_phase2_proto, so login() leaks the proto buffer on every call. Free it before each return.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@sdk/runanywhere-cli/src/net/control_plane.cpp` around lines 404 - 421, Update
login() to call rac_proto_buffer_free(&out_buffer) after
rac_sdk_init_phase2_proto completes and before both the failure return in the
parse/phase2_rc check and the successful return path. Ensure the caller-owned
buffer is released exactly once on every exit while preserving the existing
result and error handling.
…iftPM CLI target The RunAnywhereMLXCLI SwiftPM target lists rcli sources explicitly, but the network-driver files were never added — so the callers compiled while the definitions did not, failing swift-spm at link with undefined symbols rcli::net::register_device_callbacks / commands::register_auth / commands::register_telemetry. Add src/net/control_plane.cpp, src/commands/cmd_auth.cpp, src/commands/cmd_telemetry.cpp (src/main.cpp stays excluded — RunAnywhereMLXCLI provides its own Swift entry point). Verified: 'swift build --product RunAnywhereMLXCLI' links clean. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012nxQHeWG9c5SEwzp1tbqdc
rcli control-plane network driver + example-app brand alignment
Combines two SDK-repo changes into one PR.
Part 1 — rcli control-plane network driver (
sdk/runanywhere-cli)Turns
rcliinto a control-plane client that can point at any backend (including localhost) for integration testing and manual debugging.--base-url/--api-key/--environmentflags (+RUNANYWHERE_*env vars), threaded into the SDK config with zero regression when absent.rcli auth login— real handshake (API key → JWT, device register, model-assignments fetch).rcli telemetry emit --modality <m>andrcli telemetry blast— model-free telemetry through the real network transport with a 12-modality result table.Part 2 — align the 5 example apps to the brand + design guideline
Every example app had drifted from the brand: their UI accents were the legacy
#FF5500(Flutter/RN were plain blue) while their logos were already the correct#FF6900. This finishes the migration.examples/DESIGN_GUIDELINE.md— canonical brand doc (primary = the logo's #FF6900, gradient →#FB2C36, full palette light+dark, per-platform mapping, the white-on-orange contrast caveat).AGENTS.mdgains a Design System section pointing at the guideline (CLAUDE.md symlinks intact); parentAGENTS.mdreferences it.rac_telemetry_manager_parse_responsedeclaration + stale export.Grep-proven: zero legacy accent literals remain; #FF6900 present in all five apps.
Summary by CodeRabbit
New Features
Style
Documentation