v2.4.0: Dependencies, Settings Refactor, Accessibility & OEM Integration#356
Open
v2.4.0: Dependencies, Settings Refactor, Accessibility & OEM Integration#356
Conversation
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Expand MachineService interface: profiles, telemetry, history, settings - Create ProxyAdapter (wraps MeticAI backend, Docker mode) - Create DirectAdapter (uses @meticulous-home/espresso-api, PWA mode) - Add MachineServiceProvider with mode selection (direct/proxy) - Add machineMode utility (build-time + runtime detection) - Install espresso-api, espresso-profile, @google/genai, idb, fzstd - Install vite-plugin-pwa, fake-indexeddb (dev) - Add VITE_MACHINE_MODE/VITE_DEFAULT_MACHINE_URL env type declarations - All 320 tests pass, 0 lint errors Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Create AIService interface (profile gen, shot analysis, image gen, recommendations, dial-in) - Create ProxyAIService wrapping MeticAI backend endpoints - Create BrowserAIService using @google/genai SDK directly - Port prompt_builder.py to TypeScript (image, profile, analysis, recommendation, dial-in prompts) - Create AIServiceProvider with mode selection (direct/proxy) - All 320 tests pass, 0 lint errors Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Create AppDatabase with idb library: settings, annotations, AI cache, pour-over, dial-in, profile images - TTL-based AI cache (7-day expiry) with auto-cleanup - LRU eviction for profile images (50 MB cap) - Storage migration hook for first-run initialization - All 320 tests pass, 0 lint errors Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Create useMachineTelemetry hook supporting both proxy and direct modes - Proxy mode: WebSocket to MeticAI backend /api/ws/live (existing pattern) - Direct mode: Socket.IO via MachineService (espresso-api events) - Field mapping from espresso-api StatusData to MachineState - Exponential backoff reconnection, staleness detection - All 320 tests pass, 0 lint errors Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Create featureFlags module with proxy/direct mode flag sets - Proxy mode: all features enabled (Docker backend) - Direct mode: disable mDNS, scheduled shots, system mgmt, tailscale, MCP, cloud sync - Direct mode: enable PWA install prompt, AI via browser SDK - hasFeature() utility for conditional rendering - All 320 tests pass, 0 lint errors Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Add build:docker and build:machine scripts to package.json - Add test:direct script for PWA-mode testing - Configure vite-plugin-pwa with workbox caching strategies: - Static assets: CacheFirst - Machine API: NetworkFirst (5s timeout) - PWA manifest with standalone display mode - Machine build uses /meticai/ base path for Tornado static handler - Manual chunk splitting: recharts, framer-motion, machine-api, genai - Docker build: 5.8 MB output, Machine build: 5.8 MB output - Both builds verified, all 320 tests pass, 0 lint errors Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- install-meticai.sh: resource checks, download, backup, extract - validate-meticai.sh: verify files, routes, API connectivity - update-meticai.sh: delegates to installer with backup - uninstall-meticai.sh: interactive cleanup with confirmation - All scripts include Tornado route configuration instructions Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Add build-machine-pwa.yml: builds VITE_MACHINE_MODE=direct, creates meticai-web.tar.gz artifact, runs lint and direct-mode tests - Update auto-release.yml: build PWA tarball and attach to GitHub release with machine install instructions - Update tests.yml: add test:direct step, include feature branch in CI - All 320 tests pass in both proxy and direct modes Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- machineMode.test.ts: 13 tests for mode detection, env vars, port detection - featureFlags.test.ts: 13 tests for proxy/direct flags, hasFeature, caching - AppDatabase.test.ts: 27 tests for IndexedDB CRUD, TTL cache, LRU eviction - prompts.test.ts: 42 tests for all 6 prompt builders, tag system, safety Total: 95 new tests (320 → 415), all passing in both modes Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Critical wiring fixes: - Wire AIServiceProvider into main.tsx component tree - Replace useWebSocket with useMachineTelemetry in App.tsx - Call useStorageMigration in App.tsx for IndexedDB init - Gate Tailscale and Updates UI sections behind feature flags Build fixes: - Add maximumFileSizeToCacheInBytes to Workbox config (logo.png > 2MB) - Exclude static manifest.json from Workbox precache glob - Remove PNG from precache glob (large assets) AI service improvements: - Add wrapApiError() for user-friendly Gemini error messages (429/401/404) - Wrap all generateContent/generateImages calls in try/catch - Port full dial-in prompt with coffee params and iteration history Other fixes: - Map brew_head_temperature in direct mode telemetry - Update LRU timestamp on profile image reads (true LRU semantics) - Fix lint errors in test files (unused imports/vars) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- machineMode.ts: add comment clarifying meticulous.local fallback is proxy-mode only; direct mode uses window.location.host (same origin) - install-meticai.sh: replace hardcoded meticulous.local with dynamic hostname detection; add note about randomized hostnames - validate-meticai.sh: add CPU load average to system resource report The Meticulous machine uses randomized hostnames (e.g. meticulous-abc123.local), not a fixed meticulous.local. Since the PWA is served from the machine itself, direct mode correctly uses window.location.host. The install script now shows the actual hostname. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Meticulous machines don't have curl installed. All machine scripts now use wget as primary HTTP tool with curl as fallback. Added fetch() and download() helpers to install script, http_status() helper to validate script. No new dependencies required — wget is standard on the machine. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
… mode The Meticulous machine has neither curl nor wget — only busybox and python3. Updated HTTP helpers to try: busybox wget → python3 urllib → curl → wget. Added --local flag for SCP-based installs where the tarball is pre-copied to the machine. This is the recommended approach for testing since no HTTP tool needs to be installed. Usage: bash install-meticai.sh --local /tmp/meticai-web.tar.gz Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Use TARBALL variable to reference the source directly instead of copying to /tmp/meticai-web.tar.gz. Also preserves the user's original file when using --local (only cleans up downloaded files). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The @meticulous-home/espresso-api package is CJS-only (exports.default). Rolldown's production bundle wraps the default export differently than dev mode, causing 'Object is not a constructor' at runtime. Added interop that handles both cases: direct function or wrapped .default. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The inline env var in 'VAR=x cmd1 && cmd2' only applies to cmd1. Using 'export VAR=x && cmd1 && cmd2' ensures vite build sees the VITE_MACHINE_MODE=direct flag and applies the /meticai/ base path. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Prefix logo, config.json, i18n loadPath with import.meta.env.BASE_URL - Guard MeticAI proxy API calls (/api/settings, /api/history, /api/version) with isDirectMode() checks — these endpoints don't exist on the machine - SettingsView: load/save settings from localStorage in direct mode - Install script: skip backups for --local reinstalls, clean stale backups - Recover ~12MB from accumulated backup directories on machine Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
In direct mode, the Meticulous machine only has /api/v1/ endpoints. MeticAI proxy endpoints (/api/settings, /api/machine/*, /api/history, etc.) don't exist. Rather than guarding 80+ individual fetch calls, install a global fetch interceptor in main.tsx that: - Silently returns 404 for /api/<non-v1> paths (no network request) - Passes through /api/v1/ paths to the Meticulous backend - Passes through espresso-api calls (which use axios, not fetch) Also skip config.json fetch entirely in direct mode (no file exists). Verified on machine: all assets load, no 404 errors, machine API works, storage stable at 2478 MB. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The fetch interceptor now translates key MeticAI proxy endpoints to
their Meticulous-native /api/v1/ equivalents:
- /api/machine/profiles → /api/v1/profile/list (wraps in {profiles})
- /api/machine/profile/:id/json → /api/v1/profile/get/:id
- /api/machine/status → synthetic idle response (real state via Socket.IO)
- /api/last-shot → /api/v1/history/last
- /api/history → /api/v1/history (wraps in {entries, total})
- All other proxy paths → 200 with empty JSON
Also guard profile image-proxy URLs (set via <img src>, bypasses
fetch interceptor) with isDirectMode() in ControlCenter,
ControlCenterExpanded, LiveShotView, and App.tsx.
Machine has 18 profiles that should now be visible through the
translated API layer.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…t mode interceptor - Run profile: home → poll load (2s intervals, 10 retries) → start - Run with overrides: same flow (overrides not supported in direct mode) - Profile import from file: POST to /api/v1/profile/save - Profile import from machine: no-op (already on machine) - Import all: no-op success response - Delete profile: translate to /api/v1/profile/delete/:id - Machine commands: start/stop/load-profile → /api/v1/action/* - jsonResponse helper now supports status codes Verified on live machine: home→load takes ~4s, full run flow works. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Three bugs fixed in direct mode interceptor: 1. Run profile no longer sends 'home' (which triggered a purge). Instead: try load → if busy, stop → retry with 2s backoff. 2. Preheat: POST /api/machine/preheat → GET /api/v1/action/preheat 3. Schedule shot: POST /api/machine/schedule-shot → preheat (if requested) + setTimeout for delayed profile load → start Also added: - /api/machine/profiles/orphaned → empty list (no MeticAI DB) - /api/profiles/sync/status → zero counts - POST /api/profiles/sync → no-op Verified on live machine: no purge, preheat works, stop+load in ~2s. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Direct mode telemetry fixes:
- Convert profile_time from ms to seconds (shot timer was 1000x too high)
- Use data.profile/loaded_profile for active_profile (not data.name
which is the stage name during brewing)
- Fetch target_weight from loaded profile's final_weight via API
- Map data.name to state field (shows stage like 'heating', 'preinfusion')
Profile catalogue:
- Add in_history/has_description fields to profile list response
- Wrap profile JSON in {profile: data} to match expected format
History:
- Translate machine history entries to MeticAI HistoryEntry format
(id, created_at, profile_name, coffee_analysis, etc.)
- Convert epoch timestamp to ISO date string
- Same for /api/last-shot endpoint
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
… shot analysis - Fix profile load handler reading URL string as body instead of init.body - Add retry logic for 409 'machine is busy' responses (stop + retry) - Fix /api/shots/analyze interceptor for shot analysis in direct mode - Expand API translation layer for machine-hosted PWA
- BrowserAIService uses Gemini SDK directly in browser for profile generation - DirectAdapter, MachineService, MeticAIAdapter direct mode support - Add profile validator and prompt for browser-based generation
- Hide Machine IP setting in direct mode, hide MQTT bridge in direct mode - Profile catalogue navigates back to start view in direct mode - Profile catalogue edit mode improvements for direct mode - Telemetry unit conversion fixes (ms→s) - ProfileBreakdown, RunShotView, PourOverView direct mode adjustments - StartView profile catalogue navigation
- Add profileCatalogue.loaded, profileCatalogue.loadFailed keys - Add pwa/direct mode related translation strings
Move padding-top from body to #root so the ambient background gradient renders behind the notch while content stays below it.
The dynamic `import('capacitor-zeroconf')` was hanging indefinitely in
the Capacitor WKWebView — Vite code-splits it into a separate chunk
that never loads. Confirmed by step-by-step diagnostics showing
execution stalls at STEP 2 (import).
Fix: use a static `import { ZeroConf } from 'capacitor-zeroconf'`
instead. The web fallback is safe (returns rejected promises, doesn't
crash), so there's no need for lazy-loading.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Capacitor 8 discovers plugins via NSClassFromString() from the packageClassList in capacitor.config.json. With SPM static linking, the linker strips ObjC classes that aren't directly referenced, causing ALL SPM plugins (not just ZeroConf) to fail at runtime with 'plugin is not implemented on ios'. The -ObjC flag forces the linker to keep all ObjC classes from static libraries, fixing plugin registration for ZeroConf, Browser, Preferences, Haptics, and all other SPM-linked plugins. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This reverts commit 8056b00.
The Capacitor 8 xcframework gates getString(_:), getBool(_:), reject() and other APIs behind #if $NonescapableTypes (Swift 6.1 / Xcode 16.3). Previously only Preferences and ZeroConf were patched; this extends the patch script to automatically discover and fix ALL plugin Swift files under @capacitor/, @capacitor-community/, and @aparajita/. Newly patched: App, Browser, Clipboard, LocalNotifications, Share, BiometricAuth, SecureStorage. Also adds -ObjC to OTHER_LDFLAGS (both Debug and Release) so the linker retains ObjC classes from SPM static libraries — required for NSClassFromString() to find plugins at runtime. The patch script now: - Auto-discovers all plugin Swift files (no per-plugin config needed) - Uses a robust Python parser for reject() calls (handles commas in string literals, nested parens, variable expressions) - Adds -ObjC linker flag to pbxproj if missing - Remains idempotent and safe to run multiple times Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Extended patch-capacitor-spm.sh to handle ALL gated APIs discovered during iterative build testing: - getString/getBool/getInt/getFloat/getDouble with constant args (kKey) - getArray with Type.self parameter - UIColor.capacitor.color(fromHex:) → inline hex parser - Data.capacitor.data(base64EncodedOrDataUrl:) → inline base64 parser - JSTypes.coerceDictionaryToJSObject → as? JSObject cast - bridge?.localURL(fromWebURL:) → NSObject perform selector - bridge?.viewController → NSObject KVC cast Verified: clean reinstall + patch + build succeeds on iPhone 16 Pro simulator. All 13 Capacitor plugins compile correctly. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The _meticulous._tcp mDNS service advertises port 80 (web UI), but the API that we probe during connection testing is always on port 8080. This caused auto-discovery to find the machine but fail the connection test, preventing onboarding auto-configuration. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
ZeroConf.unwatch() and ZeroConf.close() never resolve their promises on iOS, causing discoverMachines() to hang after finding the machine. The discovery result was never returned to the onboarding wizard, so auto-configure never triggered despite successful mDNS discovery. Fix: fire-and-forget cleanup calls instead of awaiting them. Also: use mDNS-reported port (port 80 works via nginx proxy), and add diagnostic logging to auto-fill effect. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Fix Control Center not loading after onboarding: dispatch machine-url-changed event from setMachineUrl() so MachineServiceProvider reconnects Socket.IO to discovered IP - Fix profile images not loading in Capacitor/direct mode: add useProfileImageSrc hook and resolveDisplayImage utility that resolve display.image from profile data (data URIs, CDN URLs, machine-relative paths) instead of relying on image-proxy which only works through fetch() interception, not <img src> - Hide manual config in onboarding when auto-connected: show success state prominently, move discovery/manual sections behind a 'Configure manually' toggle - Soften Buy Me a Coffee wording across all 6 locales: use 'consider' phrasing instead of imperative Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Node.js v22+ ships a built-in localStorage object that shadows happy-dom's mock when --localstorage-file is not set. The methods (setItem, getItem, clear) are undefined, causing all 27 tests to fail. Fix: provide an in-memory localStorage shim via vi.stubGlobal(). Also add test for the new machine-url-changed event dispatch. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Fix Live View TDZ error (move machineState before hook call) - Fix Pour Over 'can't find variable Pause' (add Pause import) - Fix safe area insets (move padding to #root, fill edge-to-edge) - Disable text selection/callout in iOS WKWebView - Fix cancel preheat (use 'reset' instead of 'stop') - Fix sounds toggle (optimistic status callback) - Fix shot counter (handle history response format) - Fix untranslated notifications (add i18n keys to 6 locales) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ction - Use ref for i18n strings in useBrewNotifications to keep callback identity stable across language changes (prevents effect re-fire) - Widen user-select exception to include pre, code, .selectable Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Move safe-area-inset-bottom from #root (scroll container) to content wrapper via --safe-pb CSS variable. This ensures: - No scroll when content fits viewport (collapsed control center) - Proper bottom clearance when content overflows (expanded CC) - App background fills the safe area zone (no black band) Make #root a flex column so content wrapper uses flex-1 instead of min-h-screen, preventing the content from always exceeding viewport. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Set contentInset: 'never' so CSS env() handles all safe area insets instead of native WKWebView adjustment conflicting with viewport-fit=cover - Update backgroundColor to #030202 to match oklch(0.09 0.005 50) dark theme - Disable native scrollView to prevent double-scroll with #root overflow Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Network banner: add 10s polling safety net alongside event listener for iOS where networkStatusChange events can be missed. Add cancelled flag to prevent updates after unmount. Temperature: clamp sensor values to 0-150°C range in both direct and proxy telemetry paths. Transient glitches (e.g. 2000°C during state transitions) are rejected and the previous valid reading is kept. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
… tare - abortShot now clears preheat_countdown via synthetic status callback, preventing the 'preheating' override from keeping UI stuck on 'heating' - Purge action uses 'home' endpoint (the Meticulous API action that triggers a purge) instead of non-existent 'purge' endpoint - Pour over weight card is now tappable to tare scale, matching live view Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Scroll #root to top in handleViewHistoryEntry so each profile detail view opens at the top instead of inheriting previous scroll position - Resolve profile display.image via resolveDisplayImage() for direct/Capacitor mode before passing as cachedImageUrl to detail view Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Remove copy button (native share sheet has built-in copy) - Remove three-dot context menu (duplicated tab bar + share) - Remove inline 'Export as Image' button in Analyze tab - Replace with enhanced share button showing action sheet: Share Summary (text), Share Chart (PNG), Share Analysis (PNG) - Chart and analysis use domToPng → shareImageDataUri pipeline - Add shareImageDataUri() to useNativeShare for image sharing via data URI on native and Web Share API files on web - Fix SPM patch regex to handle dotted constant args (e.g. Constants.MethodParameter.path) - Add i18n keys for all 6 locales Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Owner
Author
Addressing 2nd round of Copilot review comments (Apr 10)All 4 unresolved comments about API response parsing have been fixed in prior commits: MeticAIAdapter.ts:
ProxyCatalogueService.ts:
All endpoints now correctly unwrap the backend's response wrapper objects. |
…rors - Fix useProfileImageCache tests failing in direct mode by mocking machineMode to proxy (tests verify caching logic, not mode-specific image resolution) - Add DirectAdapter unit tests (44 tests): connection lifecycle, socket events, brewing commands, abortShot/purge/tare, sounds, settings, history, profiles, subscribe/unsubscribe patterns - Add useNativeShare tests (12 tests): native/web/fallback share paths, shareImageDataUri, AbortError handling, download fallback - Fix TypeScript: StatusData cast for synthetic status fields, definite assignment for resolveWatch, HistoryListingEntry cast, stale Storybook props Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…hine info - Reorder sections: Profile → Temperatures → Actions → Settings → Machine Info - Fix telemetry seed race condition: store seeded values and replay on first subscriber so total_shots, firmware_version, and sounds_enabled always reach the UI regardless of subscription timing - Seed firmware_version from getDeviceInfo() during connect - Fix profile selection crash: null guard on p.profile?.name - Enrich Machine Info: fetch DeviceInfo on expanded mount, show serial, software version, image version, update channel, model version - Hide individual info rows when null; hide entire section if no data - Larger profile image (10×10) in collapsed view with change icon - Add profile selector to collapsed view via ArrowsClockwise button - Add CaretDown icon to native profile selector button - Filter profiles with missing names from selector - Add i18n keys for new machine info labels in all 6 locales Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…dling
- Fix loadProfile() to handle both flat {name,id} and nested {profile:{name,id}} response shapes from machine API
- Add Cancel button (ActionSheetButtonStyle.Cancel) to native ActionSheet
- Change profile change icon from ArrowsClockwise to CaretUpDown
Closes part of #253
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ix grouphead temp - Remove active profile section from expanded view (now in collapsed view) - Reorder sections: Actions → Temperatures → Settings → Machine Info - Map sensors.g to brew_head_temperature (was incorrectly using sensors.t for both) - Show grouphead temp only when it differs from boiler temp Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
sensors.g is 0 (not undefined) when the grouphead sensor isn't reporting — use || instead of ?? so 0 falls back to sensors.t. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ouphead sensor sensors.g flickers between 0 and real values causing UI jitter. Use sensors.t (boiler) for both temperatures until grouphead stabilises. Compact control centre now shows boiler_temperature directly. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The machine sends brew_head_temperature via the Socket.IO 'sensors' event (Temperatures.t_ext_1), NOT via sensors.g in the 'status' event. sensors.g is unreliable and flickers between 0 and real values. - Add onTemperatures to MachineService interface + all adapters - Subscribe to 'sensors' Socket.IO event in DirectAdapter - Map t_ext_1 to brew_head_temperature in useMachineTelemetry - Update adapter parity tests Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Remove discovery debug log from onboarding machine step - Move IP hint text to the configure manually section - Add safe-area-inset-top to no-internet banner (dynamic island) - Add 5s polling on web for network status (events can be missed) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Add 'Get a free API key from Google AI Studio' button that opens aistudio.google.com/app/apikey (Capacitor Browser on native, window.open on web) - Fix TS7030: explicit return undefined in auto-fill useEffect - Fix TS2367: extract isTesting before JSX narrowing in manual config block - Remove leftover console.error debug logs from auto-fill - Add onboarding.ai.getKey i18n key to all 6 locales Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
v2.4.0 Release — Dependencies + OEM Integration + iOS App
Summary
This PR brings
version/2.4.0intomainwith:Dependency Updates
iOS App (#253)
RuntimePlatform(web/machine-hosted/native) fromMachineMode(direct/proxy)window.Capacitor.isNativePlatform()getServerUrl()returns machine URL in native mode (fixes all 314+ hook references)DirectModeInterceptorprefixes relative/api/...URLs with machine base URL in native modeCapacitorStorageadapter (wraps @capacitor/preferences, falls back to localStorage)MachineServiceContextCAPACITOR_FLAGS(machineDiscovery=true, pwaInstall=false)OEM Integration (#326)
DirectAdapter.executeRawAction()for abort/purge/home via RESTMeticAIAdaptermigrated from raw fetch to apiFetch, fixed response shapes and endpoint pathsBug Fixes
Test Results
Exclusions
Closes #317, #326. Implements scaffolding for #253.