From fc578f5ea92ac30eb3d2ffa50702258691f202ea Mon Sep 17 00:00:00 2001 From: Jinwoo Hong <73622457+Jinwoo-H@users.noreply.github.com> Date: Mon, 4 May 2026 13:08:27 -0700 Subject: [PATCH] feat(mobile): Expo companion app [beta] (#1245) Co-authored-by: Orca --- .github/workflows/mobile-build.yml | 71 + .github/workflows/mobile.yml | 43 + docs/debug/mobile-fit-debug-status.md | 203 + mobile/.gitignore | 12 + mobile/.oxlintrc.json | 3 + mobile/README.md | 172 + mobile/app.json | 61 + mobile/app/_layout.tsx | 73 + mobile/app/about.tsx | 145 + mobile/app/h/[hostId]/index.tsx | 1405 ++ .../app/h/[hostId]/session/[worktreeId].tsx | 1248 ++ mobile/app/h/_layout.tsx | 16 + mobile/app/index.tsx | 947 ++ mobile/app/notifications.tsx | 109 + mobile/app/pair-scan.tsx | 307 + mobile/app/settings.tsx | 102 + mobile/app/troubleshoot.tsx | 455 + mobile/assets/adaptive-icon.png | Bin 0 -> 40835 bytes mobile/assets/favicon.png | Bin 0 -> 123 bytes mobile/assets/icon.png | Bin 0 -> 719234 bytes mobile/assets/splash-icon.png | Bin 0 -> 7816 bytes mobile/mock-homepage.html | 808 + mobile/package.json | 53 + mobile/pnpm-lock.yaml | 12571 ++++++++++++++++ mobile/scripts/mock-server.ts | 321 + mobile/scripts/repro-terminal-colors.ts | 362 + .../scripts/repro-worktree-startup-stream.ts | 367 + mobile/scripts/test-subscribe.ts | 324 + mobile/src/cache/worktree-cache.ts | 35 + mobile/src/components/ActionSheetModal.tsx | 133 + mobile/src/components/AgentSpinner.tsx | 62 + mobile/src/components/BottomDrawer.tsx | 252 + mobile/src/components/ConfirmModal.tsx | 111 + mobile/src/components/CustomKeyModal.tsx | 338 + mobile/src/components/NewWorktreeModal.tsx | 756 + mobile/src/components/OrcaLogo.tsx | 23 + mobile/src/components/PickerModal.tsx | 108 + mobile/src/components/StatusDot.tsx | 25 + mobile/src/components/TextInputModal.tsx | 151 + .../src/notifications/mobile-notifications.ts | 111 + mobile/src/platform/haptics.ts | 13 + mobile/src/storage/preferences.ts | 88 + mobile/src/terminal/TerminalWebView.tsx | 633 + mobile/src/theme/mobile-theme.ts | 45 + mobile/src/transport/e2ee.ts | 91 + mobile/src/transport/host-store.ts | 66 + mobile/src/transport/index.ts | 19 + mobile/src/transport/pairing.ts | 16 + mobile/src/transport/rpc-client.ts | 371 + mobile/src/transport/types.ts | 62 + mobile/terminal-output-streaming-findings.md | 56 + mobile/tsconfig.json | 10 + package.json | 3 + pnpm-lock.yaml | 156 + skills/mobile-fit-debug/SKILL.md | 182 + src/cli/runtime-client.test.ts | 10 +- src/cli/runtime/metadata.ts | 8 +- src/cli/runtime/status.ts | 2 +- src/cli/runtime/transport.ts | 18 +- .../browser/cdp-bridge-integration.test.ts | 4 +- .../runtime-home-service.test.ts | 1 + src/main/codex-accounts/service.test.ts | 1 + src/main/daemon/headless-emulator.ts | 8 +- src/main/daemon/shell-ready.ts | 6 +- src/main/index.ts | 11 +- src/main/ipc/mobile.ts | 125 + src/main/ipc/notifications.ts | 19 +- src/main/ipc/pty.ts | 122 +- src/main/ipc/register-core-handlers.test.ts | 2 +- src/main/ipc/register-core-handlers.ts | 2 +- src/main/ipc/runtime.ts | 31 + src/main/providers/local-pty-shell-ready.ts | 8 +- src/main/runtime/device-registry.ts | 83 + src/main/runtime/e2ee-keypair.ts | 50 + .../runtime/fit-override-integration.test.ts | 275 + .../mobile-subscribe-integration.test.ts | 471 + src/main/runtime/orca-runtime.test.ts | 34 +- src/main/runtime/orca-runtime.ts | 1414 +- src/main/runtime/rpc/core.ts | 50 +- src/main/runtime/rpc/dispatcher.ts | 120 +- src/main/runtime/rpc/e2ee-channel.test.ts | 266 + src/main/runtime/rpc/e2ee-channel.ts | 178 + src/main/runtime/rpc/e2ee-crypto.test.ts | 79 + src/main/runtime/rpc/e2ee-crypto.ts | 41 + src/main/runtime/rpc/e2ee-integration.test.ts | 179 + src/main/runtime/rpc/methods/index.ts | 10 +- src/main/runtime/rpc/methods/notifications.ts | 44 + src/main/runtime/rpc/methods/repo.ts | 5 + src/main/runtime/rpc/methods/stats.ts | 11 + src/main/runtime/rpc/methods/terminal.ts | 202 +- src/main/runtime/rpc/methods/worktree.ts | 38 +- src/main/runtime/rpc/streaming.test.ts | 266 + src/main/runtime/rpc/transport.ts | 22 + src/main/runtime/rpc/unix-socket-transport.ts | 203 + src/main/runtime/rpc/ws-transport.test.ts | 256 + src/main/runtime/rpc/ws-transport.ts | 193 + src/main/runtime/runtime-metadata.test.ts | 38 +- src/main/runtime/runtime-rpc.test.ts | 103 +- src/main/runtime/runtime-rpc.ts | 421 +- src/main/runtime/tls-certificate.ts | 63 + .../window/attach-main-window-services.ts | 87 +- src/preload/api-types.ts | 44 +- src/preload/index.ts | 71 +- .../components/settings/ExperimentalPane.tsx | 159 +- .../settings/HiddenExperimentalGroup.tsx | 37 + .../src/components/settings/MobilePane.tsx | 244 + .../src/components/settings/Settings.tsx | 1 + .../settings/SupportedAgentsDisclaimer.tsx | 50 + .../settings/experimental-search.ts | 17 + .../components/terminal-pane/TerminalPane.tsx | 121 + .../terminal-pane/expand-collapse.ts | 7 +- .../terminal-pane/pty-buffer-serializer.ts | 36 + .../terminal-pane/pty-connection.test.ts | 4 +- .../terminal-pane/pty-connection.ts | 79 +- .../components/terminal-pane/replay-guard.ts | 23 + .../terminal-pane/terminal-appearance.ts | 11 +- .../terminal-pane/useTerminalFontZoom.ts | 4 +- src/renderer/src/hooks/useIpcEvents.test.ts | 40 + src/renderer/src/hooks/useIpcEvents.ts | 24 +- .../pane-manager/mobile-fit-overrides.test.ts | 409 + .../lib/pane-manager/mobile-fit-overrides.ts | 111 + .../src/lib/pane-manager/pane-drag-reorder.ts | 4 +- .../pane-fit-resize-observer.test.ts | 2 +- .../src/lib/pane-manager/pane-manager.ts | 2 +- .../lib/pane-manager/pane-tree-ops.test.ts | 113 +- .../src/lib/pane-manager/pane-tree-ops.ts | 28 +- .../src/runtime/sync-runtime-graph.ts | 3 +- src/shared/constants.ts | 1 + src/shared/pairing.test.ts | 51 + src/shared/pairing.ts | 35 + src/shared/runtime-bootstrap.ts | 26 +- src/shared/runtime-types.ts | 7 + src/shared/types.ts | 6 + 133 files changed, 30973 insertions(+), 471 deletions(-) create mode 100644 .github/workflows/mobile-build.yml create mode 100644 .github/workflows/mobile.yml create mode 100644 docs/debug/mobile-fit-debug-status.md create mode 100644 mobile/.gitignore create mode 100644 mobile/.oxlintrc.json create mode 100644 mobile/README.md create mode 100644 mobile/app.json create mode 100644 mobile/app/_layout.tsx create mode 100644 mobile/app/about.tsx create mode 100644 mobile/app/h/[hostId]/index.tsx create mode 100644 mobile/app/h/[hostId]/session/[worktreeId].tsx create mode 100644 mobile/app/h/_layout.tsx create mode 100644 mobile/app/index.tsx create mode 100644 mobile/app/notifications.tsx create mode 100644 mobile/app/pair-scan.tsx create mode 100644 mobile/app/settings.tsx create mode 100644 mobile/app/troubleshoot.tsx create mode 100644 mobile/assets/adaptive-icon.png create mode 100644 mobile/assets/favicon.png create mode 100644 mobile/assets/icon.png create mode 100644 mobile/assets/splash-icon.png create mode 100644 mobile/mock-homepage.html create mode 100644 mobile/package.json create mode 100644 mobile/pnpm-lock.yaml create mode 100644 mobile/scripts/mock-server.ts create mode 100644 mobile/scripts/repro-terminal-colors.ts create mode 100644 mobile/scripts/repro-worktree-startup-stream.ts create mode 100644 mobile/scripts/test-subscribe.ts create mode 100644 mobile/src/cache/worktree-cache.ts create mode 100644 mobile/src/components/ActionSheetModal.tsx create mode 100644 mobile/src/components/AgentSpinner.tsx create mode 100644 mobile/src/components/BottomDrawer.tsx create mode 100644 mobile/src/components/ConfirmModal.tsx create mode 100644 mobile/src/components/CustomKeyModal.tsx create mode 100644 mobile/src/components/NewWorktreeModal.tsx create mode 100644 mobile/src/components/OrcaLogo.tsx create mode 100644 mobile/src/components/PickerModal.tsx create mode 100644 mobile/src/components/StatusDot.tsx create mode 100644 mobile/src/components/TextInputModal.tsx create mode 100644 mobile/src/notifications/mobile-notifications.ts create mode 100644 mobile/src/platform/haptics.ts create mode 100644 mobile/src/storage/preferences.ts create mode 100644 mobile/src/terminal/TerminalWebView.tsx create mode 100644 mobile/src/theme/mobile-theme.ts create mode 100644 mobile/src/transport/e2ee.ts create mode 100644 mobile/src/transport/host-store.ts create mode 100644 mobile/src/transport/index.ts create mode 100644 mobile/src/transport/pairing.ts create mode 100644 mobile/src/transport/rpc-client.ts create mode 100644 mobile/src/transport/types.ts create mode 100644 mobile/terminal-output-streaming-findings.md create mode 100644 mobile/tsconfig.json create mode 100644 skills/mobile-fit-debug/SKILL.md create mode 100644 src/main/ipc/mobile.ts create mode 100644 src/main/runtime/device-registry.ts create mode 100644 src/main/runtime/e2ee-keypair.ts create mode 100644 src/main/runtime/fit-override-integration.test.ts create mode 100644 src/main/runtime/mobile-subscribe-integration.test.ts create mode 100644 src/main/runtime/rpc/e2ee-channel.test.ts create mode 100644 src/main/runtime/rpc/e2ee-channel.ts create mode 100644 src/main/runtime/rpc/e2ee-crypto.test.ts create mode 100644 src/main/runtime/rpc/e2ee-crypto.ts create mode 100644 src/main/runtime/rpc/e2ee-integration.test.ts create mode 100644 src/main/runtime/rpc/methods/notifications.ts create mode 100644 src/main/runtime/rpc/methods/stats.ts create mode 100644 src/main/runtime/rpc/streaming.test.ts create mode 100644 src/main/runtime/rpc/transport.ts create mode 100644 src/main/runtime/rpc/unix-socket-transport.ts create mode 100644 src/main/runtime/rpc/ws-transport.test.ts create mode 100644 src/main/runtime/rpc/ws-transport.ts create mode 100644 src/main/runtime/tls-certificate.ts create mode 100644 src/renderer/src/components/settings/HiddenExperimentalGroup.tsx create mode 100644 src/renderer/src/components/settings/MobilePane.tsx create mode 100644 src/renderer/src/components/settings/SupportedAgentsDisclaimer.tsx create mode 100644 src/renderer/src/components/terminal-pane/pty-buffer-serializer.ts create mode 100644 src/renderer/src/lib/pane-manager/mobile-fit-overrides.test.ts create mode 100644 src/renderer/src/lib/pane-manager/mobile-fit-overrides.ts create mode 100644 src/shared/pairing.test.ts create mode 100644 src/shared/pairing.ts diff --git a/.github/workflows/mobile-build.yml b/.github/workflows/mobile-build.yml new file mode 100644 index 00000000000..9e7d88666aa --- /dev/null +++ b/.github/workflows/mobile-build.yml @@ -0,0 +1,71 @@ +name: Mobile Release + +on: + push: + tags: + - 'mobile-v*' + workflow_dispatch: + +jobs: + android-build: + runs-on: ubuntu-latest + + # Why: the "Create GitHub Release" step below uses the default GITHUB_TOKEN + # to call `gh release create`, which requires contents:write. Without this, + # the job builds the APK fine but fails at release time with + # "HTTP 403: Resource not accessible by integration". + permissions: + contents: write + + defaults: + run: + working-directory: mobile + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 24 + + - name: Setup pnpm + uses: pnpm/action-setup@v4 + with: + run_install: false + + - name: Install dependencies + run: pnpm install --frozen-lockfile + + - name: Setup JDK 17 + uses: actions/setup-java@v4 + with: + distribution: temurin + java-version: 17 + + - name: Expo prebuild + run: npx expo prebuild --platform android --no-install + + - name: Build Android release APK + run: cd android && ./gradlew assembleRelease + + - name: Upload APK artifact + uses: actions/upload-artifact@v4 + with: + name: orca-mobile-apk + path: mobile/android/app/build/outputs/apk/release/*.apk + + - name: Create GitHub Release + if: startsWith(github.ref, 'refs/tags/mobile-v') + env: + GH_TOKEN: ${{ github.token }} + run: | + tag="${GITHUB_REF#refs/tags/}" + gh release create "$tag" \ + --repo "$GITHUB_REPOSITORY" \ + --title "Orca Mobile $tag" \ + --prerelease \ + --latest=false \ + --generate-notes \ + android/app/build/outputs/apk/release/*.apk diff --git a/.github/workflows/mobile.yml b/.github/workflows/mobile.yml new file mode 100644 index 00000000000..95fcff00a08 --- /dev/null +++ b/.github/workflows/mobile.yml @@ -0,0 +1,43 @@ +name: Mobile Checks + +on: + pull_request: + types: + - opened + - synchronize + - reopened + - ready_for_review + paths: + - 'mobile/**' + +jobs: + verify: + runs-on: ubuntu-latest + + defaults: + run: + working-directory: mobile + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version-file: package.json + + - name: Setup pnpm + uses: pnpm/action-setup@v4 + with: + run_install: false + + - name: Install dependencies + run: pnpm install --frozen-lockfile + + - name: Typecheck + run: npx tsc --noEmit + + - name: Lint + run: pnpm lint + diff --git a/docs/debug/mobile-fit-debug-status.md b/docs/debug/mobile-fit-debug-status.md new file mode 100644 index 00000000000..8bd9518aa1c --- /dev/null +++ b/docs/debug/mobile-fit-debug-status.md @@ -0,0 +1,203 @@ +# Mobile Phone-Fit Debug Status + +## Architecture Overview + +``` +Mobile Client Server (orca-runtime) Desktop Renderer +───────────── ──────────────────── ──────────────── +subscribeToTerminal(handle) → terminal.subscribe handler → IPC: terminalFitOverrideChanged + sends { client, viewport } calls handleMobileSubscribe() setFitOverride() → banner render + resizes PTY, sets override + serializes scrollback + ← scrollback { cols, rows, + serialized, displayMode } + +switchTab(handle) → terminal.unsubscribe (old) + unsub old, subscribe new handleMobileUnsubscribe() + starts 300ms restore timer + → terminal.subscribe (new) + handleMobileSubscribe() + cancels timer, inline-restores old + resizes new PTY + +toggleDisplayMode(handle) → terminal.setDisplayMode + applyMobileDisplayMode() + ← resized event on stream +``` + +## Key Data Structures (Server) + +- `mobileSubscribers: Map` +- `pendingRestoreTimers: Map` (changed from clientId-keyed to ptyId-keyed) +- `terminalFitOverrides: Map` +- `mobileDisplayModes: Map` + +## Key Data Structures (Mobile Client) + +- `viewportRef: { cols, rows } | null` — measured once from xterm, passed with every subscribe +- `viewportMeasuredRef: boolean` — true after first successful measurement +- `terminalUnsubsRef: Map` — active subscription cleanup closures +- `initializedHandlesRef: Set` — tracks which terminals have been init'd (prevents double-init) +- `subscribeSeqRef: Map` — monotonic counter to ignore stale scrollback + +--- + +## Bug 1: pendingRestoreTimers lost when 2 unsubscribes happen back-to-back + +**Status: FIXED (verification inconclusive — may need more testing)** + +**Root cause**: `pendingRestoreTimers` was keyed by `clientId` (one slot per device). When two terminals were unsubscribed in quick succession, the second timer overwrote the first. + +**Fix applied**: +1. Changed `pendingRestoreTimers` from `Map` to `Map` +2. `handleMobileSubscribe` cancels only the restore timer for the SAME ptyId (re-subscribe case). Other terminals' timers fire normally so their desktop banners clear. +3. `handleMobileSubscribe` skips resize if PTY is already at target phone dims +4. `handleCreateTerminal` now unsubscribes the old active terminal before setting the new one +5. Restore happens via: 300ms timer (tab switch), or `onClientDisconnected` (full disconnect) + +**Bug 8 (banners accumulate on tab switch)**: The original Bug 1 fix was too aggressive — it cancelled ALL pending restore timers for the client, not just the one for the ptyId being re-subscribed. This prevented the 300ms restore timer from firing for the old terminal, so its desktop banner persisted. Fixed by narrowing the cancel scope to only the same ptyId. + +**Files**: `orca-runtime.ts`, `[worktreeId].tsx` + +--- + +## Bug 2: Intermittent blank terminal on tab switch + +**Status: FIX v2 APPLIED — needs testing** + +**Symptom**: After creating a new terminal tab, the original 2 terminals occasionally show blank. Leaving the worktree and re-entering fixes it. + +**Root cause**: The WebView loads xterm.js from CDN, which takes time. Messages sent before `web-ready` queue in `pendingMessagesRef` and flush when ready. The original `handleTerminalWebReady` would unsub+resub ALL initialized terminals (even inactive ones), creating stale server-side subscriptions and disrupting the data stream. + +**Fix v1 (FAILED)**: Gated `subscribeToTerminal` on webReady. This was too aggressive — it prevented subscriptions entirely, so no scrollback arrived, no init was queued, and the terminal stayed blank. + +**Fix v2 (current)**: +1. `subscribeToTerminal` has NO webReady guard — subscriptions start immediately, init messages queue in `pendingMessagesRef`, and flush when `web-ready` fires +2. `handleTerminalWebReady` distinguishes first load vs reload: + - **First load** (`wasAlreadyReady=false`): just marks webReady, triggers viewport measurement for active terminal. Pending messages flush after this callback returns → terminal renders. + - **Reload** (`wasAlreadyReady=true`): unsubscribes and resubscribes to get fresh scrollback (old xterm buffer is gone). Only resubscribes if active. +3. `setTerminalWebViewRef` simplified to just store the ref (no subscription logic) + +**Flow (first load)**: +1. `fetchTerminals` → `subscribeToTerminal(active)` → stream starts +2. Scrollback arrives → `init()` queued in pendingMessages (WebView not ready yet) +3. WebView loads xterm from CDN → `web-ready` fires +4. `handleTerminalWebReady`: marks webReady, triggers viewport measurement (async) +5. `flushPendingMessages()`: sends queued init → terminal renders +6. Viewport measured → resubscribe with dims → server phone-fits → reinit with phone dims + +--- + +## Bug 5: Desktop banner not showing for initial split pane after creating new tab + +**Status: FIXED (verified via CDP + e2e testing)** + +**Root cause**: The bug was caused by the inline restore mechanism in `handleMobileSubscribe`. When mobile switched tabs, the server would restore the previous terminal to desktop dims (sending `desktop-fit` IPC), which cleared the override from `overridesByPtyId`. After the next subscribe, the override was re-set via `mobile-fit` IPC, but the timing was tight and the banner could flicker or fail to appear. + +**Fix**: Removed inline restore from `handleMobileSubscribe` (Bug 1 fix). PTYs now stay at phone dims when the mobile client switches tabs. Overrides persist in `overridesByPtyId` until the mobile client disconnects. This means ALL terminals the mobile client has subscribed to show the banner — which is correct since the mobile client "owns" those terminals. + +**Verification**: E2e testing via CDP (desktop renderer) and agent-device (mobile) confirmed: +- `setFitOverride()` is called correctly via IPC +- `onOverrideChange` fires and triggers re-renders +- `getFitOverrideForPane()` finds overrides when `ptyIdByPaneId` bindings exist +- Banners show correctly for all terminals (1, 2, and 3 tabs) including after creating new tabs +- The earlier diagnostic showed HMR clearing module-level maps was a test artifact, not a production issue + +--- + +## Bug 7: Terminals disappear ("0 terminals") during rapid tab switching + +**Status: FIXED (verified via e2e testing)** + +**Symptom**: During rapid tab switching (3+ toggles in quick succession), all terminals disappear from the mobile UI. The terminal list shows "0 terminals". Leaving the worktree and re-entering fixes it. + +**Root cause**: The periodic `fetchTerminals()` (every 2s) calls with `allowEmptyLoaded: true`. The server can transiently return an empty terminal list during rapid operations. The old code's subscription cleanup loop (`liveHandles` check) ran before the empty guard, unsubscribing all terminals, which then made the empty guard's `terminalUnsubsRef.current.size > 0` check fail. + +**Fix applied**: +1. Added `lastKnownTerminalCountRef` to track the previous non-zero terminal count +2. When the server returns 0 terminals but `lastKnownTerminalCountRef > 0`, skip the FIRST empty response (set ref to 0 and return early) +3. On the NEXT fetch, if still empty, `lastKnownTerminalCountRef` is 0, so the guard doesn't trigger and terminals are cleared normally +4. This gives a ~2s grace period (one polling interval) to filter out transient empty responses +5. The guard runs BEFORE the subscription cleanup loop, preventing premature unsubscription + +**Verification**: Rapid tab switching (8 cycles across 3 terminals in ~1.6s) with 20s wait — terminals survived. Previous code cleared to "0 terminals" within 18s. + +**Files**: `[worktreeId].tsx` + +--- + +## Bug 3: Viewport measurement chicken-and-egg + +**Status: SOLVED (workaround in place)** + +The first subscribe has `viewport=none`. After scrollback init, async measure viewport, resubscribe with dims. Takes 2-3 round-trips. Works reliably. + +--- + +## Bug 4: Desktop terminal focus doesn't follow mobile tab switching + +**Status: FIXED** + +`switchTab` now calls `terminal.focus` RPC. + +--- + +## Bug 6: Claude not launching on new workspace + +**Status: FIXED** + +**Root cause**: Mobile's `NewWorktreeModal` sends `startupCommand` (e.g. `'claude'`) in the `worktree.create` RPC call, but the Zod schema (`WorktreeCreate`) did not include `startupCommand`, so it was silently stripped during validation. The runtime's `createManagedWorktree` never received the startup command, so `args.startup` was always undefined and the activation IPC never included a startup payload. The left pane spawned a plain shell instead of the selected agent. + +**Fix applied**: +1. Added `startupCommand: OptionalString` to the `WorktreeCreate` Zod schema in `src/main/runtime/rpc/methods/worktree.ts` +2. RPC handler maps `params.startupCommand` → `{ command }` and passes to `runtime.createManagedWorktree()` +3. Added `startup?: WorktreeStartupLaunch` to `createManagedWorktree`'s args type in `orca-runtime.ts` + +**Bug 6b: `waitForLeafPtyId` times out due to handle invalidation** + +When a leaf's ptyId changes from null to a real value, `syncWindowGraph` invalidates the old handle (deletes it from `this.handles`). The `waitForLeafPtyId` callback calls `resolveLeafForHandle(handle)` which returns null because the handle no longer exists. The wait never resolves. + +**Fix**: `waitForLeafPtyId` now captures the handle's `tabId` and `leafId` before the handle can be invalidated. The callback falls back to direct `this.leaves.get(getLeafKey(tabId, leafId))` lookup when the handle-based lookup fails. + +**Files**: `src/main/runtime/rpc/methods/worktree.ts`, `src/main/runtime/orca-runtime.ts` + +--- + +## Diagnostic Logging + +All logs prefixed with `[mobile-fit]`. + +### Server-side (main process stdout) +| Location | What it logs | +|----------|-------------| +| `terminal.subscribe` handler | handle, ptyId, client type, viewport | +| `handleMobileSubscribe` | ptyId, mode, viewport, skip reasons, resize details | +| `handleMobileUnsubscribe` | ptyId, subscriber state, wasResized | +| `applyMobileDisplayMode` | ptyId, mode, subscriber state | + +### Mobile client (React Native console) +| Location | What it logs | +|----------|-------------| +| `subscribeToTerminal` | handle, seq, viewport, measured state | +| scrollback handler | cols, rows, displayMode, hasSerialized, alreadyInit | +| resized handler | cols, rows, displayMode, reason | +| `switchTab` | prev/next handle, hasUnsub, hasRef | +| `toggleDisplayMode` | handle, current mode, next mode | +| `setTerminalWebViewRef` | handle, isActive, activeHandle | + +### Desktop renderer (DevTools console) +| Location | What it logs | +|----------|-------------| +| `useIpcEvents.ts` | fitOverrideChanged IPC events received | +| `TerminalPane.tsx` | onOverrideChange callbacks fired | + +--- + +## Priority Order + +1. ~~**Bug 2 (blank terminal)**~~ — **FIXED**. WebView readiness lifecycle corrected. +2. ~~**Duplicate prompt lines**~~ — **FIXED**. Removed inline restore, added alreadyAtTarget skip. +3. ~~**Bug 7 (0 terminals)**~~ — **FIXED**. Consecutive-empty guard with `lastKnownTerminalCountRef`. +4. ~~**Bug 5 (banner missing)**~~ — **FIXED**. Side effect of inline restore removal + verified via CDP. +5. ~~**Bug 1 (timer overwrite)**~~ — **FIXED**. `pendingRestoreTimers` keyed by ptyId, cancel without restore. +6. **Cleanup** — Remove `[mobile-fit]` diagnostic logs, Debug Test button, dead code. diff --git a/mobile/.gitignore b/mobile/.gitignore new file mode 100644 index 00000000000..39a8c8da590 --- /dev/null +++ b/mobile/.gitignore @@ -0,0 +1,12 @@ +node_modules/ +.expo/ +dist/ +android/ +ios/ +*.jks +*.p8 +*.p12 +*.key +*.mobileprovision +*.orig.* +web-build/ diff --git a/mobile/.oxlintrc.json b/mobile/.oxlintrc.json new file mode 100644 index 00000000000..90d8894e282 --- /dev/null +++ b/mobile/.oxlintrc.json @@ -0,0 +1,3 @@ +{ + "rules": {} +} diff --git a/mobile/README.md b/mobile/README.md new file mode 100644 index 00000000000..4d9181b161f --- /dev/null +++ b/mobile/README.md @@ -0,0 +1,172 @@ +# Orca Mobile + +React Native companion app for Orca. Monitor worktrees, view terminal output, and send commands from your phone. + +Local development uses two processes: + +- Orca desktop/Electron from the repo root. This hosts the mobile WebSocket RPC server on port `6768`. +- Expo Metro from `mobile/`. This serves the React Native app on port `8081`. + +## Prerequisites + +- Node.js 24+ +- pnpm +- Xcode and/or Android Studio tooling for simulator or device builds +- Expo Go on your phone, or a development client build when native modules are needed +- Phone and desktop on the same LAN when testing a physical phone + +## Start Desktop Orca + +From the repository root: + +```bash +pnpm install +pnpm dev +``` + +Confirm the mobile RPC server is listening: + +```bash +lsof -nP -iTCP:6768 -sTCP:LISTEN +``` + +Restart `pnpm dev` after changing Electron main-process code. Metro hot reload only applies to the mobile JavaScript bundle. + +## Start The Mobile App + +```bash +cd mobile +pnpm install +pnpm start +``` + +Scan the Expo QR code with your phone's camera on iOS, or Expo Go on Android. + +For a native dev-client build: + +```bash +pnpm exec expo run:android +pnpm exec expo run:ios +pnpm start --dev-client +``` + +## Pair With Desktop Orca + +1. Open Orca desktop. +2. Go to Settings > Mobile. +3. Scan the pairing QR code from the mobile app. +4. Confirm the mobile host endpoint is `ws://:6768`. + +For the Android emulator, use `ws://10.0.2.2:6768`. For a physical phone, use the desktop LAN IP, for example `ws://192.168.0.179:6768`. + +If the phone has a stale host entry, remove it from the app and pair again. + +## Development Paths + +### Android Phone + +1. Install Expo Go from Google Play +2. Run `pnpm start`, scan QR with Expo Go +3. For native modules: `pnpm exec expo run:android` +4. Run with `pnpm start --dev-client` + +### iOS Simulator + +1. Install Xcode from the App Store +2. Run `pnpm start --ios` to open in iOS Simulator + +## Physical Phone Debugging + +The phone can be inspected through the connected device tooling: + +```bash +orca snapshot --json +orca click --element @e3 --json +orca fill --element @e1 --value "ls" --json +orca screenshot --json +``` + +Use `snapshot` first to find the current element refs, then click/fill those refs. After mobile file edits, Metro usually hot reloads automatically, but navigating out of and back into the session screen can be useful because it re-runs `terminal.subscribe`. + +## Terminal Streaming Repro Without A Phone + +Use this when terminal output does not render on device and you need to split server streaming bugs from WebView/UI bugs: + +```bash +cd mobile +ORCA_MOBILE_WS_URL=ws://127.0.0.1:6768 pnpm exec tsx scripts/test-subscribe.ts +``` + +You can pass a worktree selector as the third argument: + +```bash +pnpm exec tsx scripts/test-subscribe.ts "id:" +pnpm exec tsx scripts/test-subscribe.ts "path:/absolute/worktree/path" +pnpm exec tsx scripts/test-subscribe.ts "name:my-worktree" +``` + +The expected result includes: + +```text +streamSawMarker: true +readSawMarker: true +``` + +If this repro fails, debug the desktop runtime/PTY path before the mobile WebView. If it passes but the phone is blank, debug the session screen or `TerminalWebView` readiness/queueing path. + +## Terminal Color Repro Without A Phone + +Use this when terminal colors disappear after switching tabs. Open a Claude Code terminal and at least one other terminal in the target worktree, then run: + +```bash +cd mobile +ORCA_MOBILE_WS_URL=ws://127.0.0.1:6768 pnpm exec tsx scripts/repro-terminal-colors.ts \ + "id:" +``` + +The script captures `terminal.subscribe` snapshots in an A → B → A sequence and writes raw snapshots to `mobile/terminal-color-repro/`. If the two A snapshots have different `sgrColor` counts, the desktop snapshot changed during the switch. If they match, the ANSI color data is still present and the bug is in mobile replay/rendering. + +## Validation + +Run these checks before committing mobile terminal changes: + +```bash +cd mobile +pnpm exec tsc --noEmit +pnpm lint +cd .. +pnpm typecheck:node +``` + +## Mock Server + +Develop the mobile app without a running Orca desktop instance: + +```bash +pnpm mock-server # starts mock WebSocket server on port 6768 +``` + +Connect from the app using endpoint `ws://localhost:6768` and token `mock-device-token`. + +## Connecting to Real Orca + +1. Start Orca desktop with WebSocket transport enabled +2. In Orca, go to Settings > Mobile and scan the QR code with this app +3. The QR encodes the connection endpoint, device token, and TLS fingerprint + +## Project Structure + +``` +mobile/ +├── app/ # Expo Router screens (file-based routing) +│ ├── _layout.tsx # Root layout with navigation stack +│ ├── index.tsx # Home screen — paired hosts list +│ └── pair-scan.tsx # QR code scanning screen +├── src/ +│ ├── terminal/ # Terminal WebView and xterm bridge +│ └── transport/ # WebSocket RPC client +├── scripts/ +│ ├── test-subscribe.ts # Desktop streaming repro without a phone +│ └── mock-server.ts # Standalone mock WebSocket server +└── assets/ # App icons and splash screen +``` diff --git a/mobile/app.json b/mobile/app.json new file mode 100644 index 00000000000..25cc7cf02f6 --- /dev/null +++ b/mobile/app.json @@ -0,0 +1,61 @@ +{ + "expo": { + "name": "Orca", + "slug": "orca-mobile", + "version": "0.0.1", + "orientation": "portrait", + "icon": "./assets/icon.png", + "userInterfaceStyle": "automatic", + "scheme": "orca", + "newArchEnabled": true, + "splash": { + "image": "./assets/splash-icon.png", + "resizeMode": "contain", + "backgroundColor": "#111111" + }, + "ios": { + "supportsTablet": true, + "bundleIdentifier": "com.stably.orca.mobile", + "buildNumber": "2", + "infoPlist": { + "NSLocalNetworkUsageDescription": "Orca connects to the desktop app on your local network.", + "NSAppTransportSecurity": { + "NSAllowsLocalNetworking": true + } + } + }, + "android": { + "adaptiveIcon": { + "foregroundImage": "./assets/adaptive-icon.png", + "backgroundColor": "#111111" + }, + "usesCleartextTraffic": true, + "allowBackup": false, + "package": "com.stably.orca.mobile" + }, + "plugins": [ + "expo-router", + [ + "expo-splash-screen", + { + "image": "./assets/splash-icon.png", + "backgroundColor": "#111111" + } + ], + [ + "expo-camera", + { + "cameraPermission": "Allow Orca to use the camera for QR code scanning." + } + ], + [ + "expo-build-properties", + { + "android": { + "usesCleartextTraffic": true + } + } + ] + ] + } +} diff --git a/mobile/app/_layout.tsx b/mobile/app/_layout.tsx new file mode 100644 index 00000000000..742addac1cb --- /dev/null +++ b/mobile/app/_layout.tsx @@ -0,0 +1,73 @@ +import { useCallback } from 'react' +import { View, StyleSheet } from 'react-native' +import { Stack } from 'expo-router' +import { StatusBar } from 'expo-status-bar' +import * as SplashScreen from 'expo-splash-screen' +import * as Notifications from 'expo-notifications' +import { colors } from '../src/theme/mobile-theme' +import { OrcaLogo } from '../src/components/OrcaLogo' + +// Why: keeps the native splash screen visible until the React tree is mounted +// and ready to render. Without this the user sees a blank white/black frame +// between the native splash and the first React paint. +SplashScreen.preventAutoHideAsync() + +// Why: without this, expo-notifications silently drops notifications when +// the app is in the foreground. Setting all three to true makes iOS/Android +// display the banner, play the sound, and show the badge even while the +// app is active. This runs once at module load time before any notification +// is scheduled. +Notifications.setNotificationHandler({ + handleNotification: async () => ({ + shouldShowBanner: true, + shouldShowList: true, + shouldPlaySound: true, + shouldSetBadge: false + }) +}) + +export default function RootLayout() { + // Why: hide the native splash only once the navigation Stack has been laid + // out — this is the earliest moment the user will see actual app content. + // Previously the splash hid when a placeholder View rendered, leaving a + // grey gap before the real screen appeared. + const onNavigatorLayout = useCallback(async () => { + await SplashScreen.hideAsync() + }, []) + + return ( + + + + + }} + /> + + + + + + + + + ) +} + +const styles = StyleSheet.create({ + root: { + flex: 1, + backgroundColor: colors.bgBase + } +}) diff --git a/mobile/app/about.tsx b/mobile/app/about.tsx new file mode 100644 index 00000000000..088c6da211f --- /dev/null +++ b/mobile/app/about.tsx @@ -0,0 +1,145 @@ +import { View, Text, StyleSheet, Pressable, Linking } from 'react-native' +import { useSafeAreaInsets } from 'react-native-safe-area-context' +import { useRouter } from 'expo-router' +import { ChevronLeft, Globe } from 'lucide-react-native' +import Svg, { Path } from 'react-native-svg' +import { OrcaLogo } from '../src/components/OrcaLogo' +import { colors, spacing, typography } from '../src/theme/mobile-theme' + +function GithubIcon({ size = 16, color = colors.textSecondary }) { + return ( + + + + ) +} + +function XIcon({ size = 16, color = colors.textSecondary }) { + return ( + + + + ) +} + +export default function AboutScreen() { + const router = useRouter() + const insets = useSafeAreaInsets() + + return ( + + + router.back()}> + + + About + + + + + Orca + Open-source agent IDE for 100x builders + + + + [styles.row, pressed && styles.rowPressed]} + onPress={() => void Linking.openURL('https://onOrca.dev')} + > + + onOrca.dev + + + [styles.row, pressed && styles.rowPressed]} + onPress={() => void Linking.openURL('https://github.com/stablyai/orca')} + > + + stablyai/orca + + + [styles.row, pressed && styles.rowPressed]} + onPress={() => void Linking.openURL('https://x.com/orca_build')} + > + + @orca_build + + + + ) +} + +const styles = StyleSheet.create({ + container: { + flex: 1, + backgroundColor: colors.bgBase, + padding: spacing.lg + }, + topRow: { + flexDirection: 'row', + alignItems: 'center', + marginBottom: spacing.xl + }, + backButton: { + width: 36, + height: 36, + borderRadius: 18, + alignItems: 'center', + justifyContent: 'center', + marginRight: spacing.sm + }, + heading: { + fontSize: 20, + fontWeight: '700', + color: colors.textPrimary + }, + brand: { + alignItems: 'center', + paddingVertical: spacing.xl, + marginBottom: spacing.lg + }, + brandName: { + fontSize: 22, + fontWeight: '800', + color: colors.textPrimary, + marginTop: spacing.sm + }, + brandSub: { + fontSize: 13, + color: colors.textMuted, + marginTop: spacing.xs + }, + section: { + backgroundColor: colors.bgPanel, + borderRadius: 12, + overflow: 'hidden' + }, + row: { + flexDirection: 'row', + alignItems: 'center', + gap: spacing.sm + 2, + paddingVertical: spacing.md, + paddingHorizontal: spacing.md + 2 + }, + rowPressed: { + backgroundColor: colors.bgRaised + }, + rowLabel: { + flex: 1, + fontSize: typography.bodySize, + fontWeight: '500', + color: colors.textPrimary + }, + rowValue: { + flex: 1, + textAlign: 'right', + fontSize: typography.bodySize, + color: colors.textSecondary + }, + separator: { + height: StyleSheet.hairlineWidth, + backgroundColor: colors.borderSubtle, + marginHorizontal: spacing.md + } +}) diff --git a/mobile/app/h/[hostId]/index.tsx b/mobile/app/h/[hostId]/index.tsx new file mode 100644 index 00000000000..72373333dd1 --- /dev/null +++ b/mobile/app/h/[hostId]/index.tsx @@ -0,0 +1,1405 @@ +import { useState, useEffect, useCallback, useMemo, useRef } from 'react' +import { + View, + Text, + StyleSheet, + SectionList, + Pressable, + ActivityIndicator, + TextInput +} from 'react-native' +import { SafeAreaView } from 'react-native-safe-area-context' +import { useLocalSearchParams, useRouter } from 'expo-router' +import { + Search, + X, + Pin, + Bell, + GitPullRequest, + SlidersHorizontal, + Layers, + ChevronDown, + ChevronRight, + ChevronLeft, + Plus, + Moon, + Filter, + Check +} from 'lucide-react-native' +import { connect, type RpcClient } from '../../../src/transport/rpc-client' +import { loadHosts, updateLastConnected, removeHost } from '../../../src/transport/host-store' +import type { ConnectionState, RpcSuccess } from '../../../src/transport/types' +import { triggerMediumImpact } from '../../../src/platform/haptics' +import { StatusDot } from '../../../src/components/StatusDot' +import { NewWorktreeModal } from '../../../src/components/NewWorktreeModal' +import { AgentSpinner } from '../../../src/components/AgentSpinner' +import { PickerModal, type PickerOption } from '../../../src/components/PickerModal' +import { ActionSheetContent } from '../../../src/components/ActionSheetModal' +import { ConfirmModal } from '../../../src/components/ConfirmModal' +import { BottomDrawer } from '../../../src/components/BottomDrawer' +import { getCachedWorktrees } from '../../../src/cache/worktree-cache' +import { colors, spacing, typography } from '../../../src/theme/mobile-theme' +import { + loadPinnedIds, + savePinnedIds, + loadPreferences, + savePreferences +} from '../../../src/storage/preferences' + +type Worktree = { + worktreeId: string + repo: string + branch: string + displayName: string + liveTerminalCount: number + hasAttachedPty: boolean + preview: string + unread: boolean + lastOutputAt?: number + isPinned: boolean + linkedPR: { number: number; state: string } | null + status?: 'working' | 'active' | 'permission' | 'done' | 'inactive' +} + +type SortMode = 'smart' | 'name' | 'recent' +type _FilterMode = 'all' | 'active' +type GroupMode = 'none' | 'repo' | 'prStatus' + +type FilterState = { + activeOnly: boolean + selectedRepos: Set +} + +const STATUS_LABELS: Record = { + connecting: 'Connecting…', + handshaking: 'Securing…', + connected: 'Connected', + disconnected: 'Disconnected', + reconnecting: 'Reconnecting…', + 'auth-failed': 'Auth failed' +} + +const SORT_OPTIONS: PickerOption[] = [ + { value: 'smart', label: 'Smart', subtitle: 'Unread and active first' }, + { value: 'name', label: 'Name', subtitle: 'Alphabetical by name' }, + { value: 'recent', label: 'Recent', subtitle: 'Most recent output first' } +] + +const GROUP_OPTIONS: PickerOption[] = [ + { value: 'none', label: 'No Grouping' }, + { value: 'repo', label: 'Repository' }, + { value: 'prStatus', label: 'PR Status' } +] + +function getWorktreeStatus(w: Worktree): 'working' | 'active' | 'permission' | 'done' | 'inactive' { + if (w.status) return w.status + if (w.liveTerminalCount > 0) return 'active' + return 'inactive' +} + +// Why: the previous 10-minute lastOutputAt window was too strict — most +// worktrees with idle terminal prompts had no recent output and were excluded. +// Any worktree with live terminals or unread output counts as "active". +function isWorktreeActive(w: Worktree): boolean { + if (w.unread) return true + if (w.status) return w.status !== 'inactive' + if (w.liveTerminalCount > 0) return true + return false +} + +function sortWorktrees(worktrees: Worktree[], mode: SortMode): Worktree[] { + return [...worktrees].sort((a, b) => { + if (mode === 'name') return (a.displayName || a.repo).localeCompare(b.displayName || b.repo) + if (mode === 'recent') return (b.lastOutputAt ?? 0) - (a.lastOutputAt ?? 0) + // 'smart' — attention-first + if (a.unread !== b.unread) return a.unread ? -1 : 1 + const aStatus = getWorktreeStatus(a) + const bStatus = getWorktreeStatus(b) + const statusOrder = { permission: 0, working: 1, done: 2, active: 3, inactive: 4 } + if (statusOrder[aStatus] !== statusOrder[bStatus]) + return statusOrder[aStatus] - statusOrder[bStatus] + if ((a.lastOutputAt ?? 0) !== (b.lastOutputAt ?? 0)) + return (b.lastOutputAt ?? 0) - (a.lastOutputAt ?? 0) + return (a.displayName || a.repo).localeCompare(b.displayName || b.repo) + }) +} + +function filterWorktrees(worktrees: Worktree[], filters: FilterState, search: string): Worktree[] { + let result = worktrees + if (filters.activeOnly) { + result = result.filter(isWorktreeActive) + } + if (filters.selectedRepos.size > 0) { + result = result.filter((w) => filters.selectedRepos.has(w.repo)) + } + if (search.trim()) { + const q = search.toLowerCase() + result = result.filter( + (w) => + (w.displayName || w.repo).toLowerCase().includes(q) || + w.branch.toLowerCase().includes(q) || + w.repo.toLowerCase().includes(q) + ) + } + return result +} + +type Section = { title: string; icon?: 'pin'; data: Worktree[] } + +// Why: matches desktop's PR_GROUP_META naming from worktree-list-groups.ts. +// no PR/draft/unknown → "In Progress", open → "In Review", merged → "Done", closed → "Closed" +type PRGroupKey = 'done' | 'in-review' | 'in-progress' | 'closed' + +const PR_GROUP_LABELS: Record = { + done: 'Done', + 'in-review': 'In Review', + 'in-progress': 'In Progress', + closed: 'Closed' +} + +const PR_GROUP_ORDER: PRGroupKey[] = ['done', 'in-review', 'in-progress', 'closed'] + +function getPRGroupKey(w: Worktree): PRGroupKey { + if (!w.linkedPR) return 'in-progress' + const s = w.linkedPR.state.toLowerCase() + if (s === 'merged') return 'done' + if (s === 'closed') return 'closed' + if (s === 'draft') return 'in-progress' + return 'in-review' +} + +function isWorktreePinned(w: Worktree, localPins: Set): boolean { + return w.isPinned || localPins.has(w.worktreeId) +} + +function buildSections( + worktrees: Worktree[], + sortMode: SortMode, + filters: FilterState, + search: string, + groupMode: GroupMode, + pinnedIds: Set +): Section[] { + const filtered = filterWorktrees(worktrees, filters, search) + const sorted = sortWorktrees(filtered, sortMode) + + const pinned = sorted.filter((w) => isWorktreePinned(w, pinnedIds)) + const unpinned = sorted.filter((w) => !isWorktreePinned(w, pinnedIds)) + const active = unpinned.filter(isWorktreeActive) + const inactive = unpinned.filter((w) => !isWorktreeActive(w)) + + const sections: Section[] = [] + if (pinned.length > 0) { + sections.push({ title: 'Pinned', icon: 'pin', data: pinned }) + } + + if (groupMode === 'none') { + if (active.length > 0) { + // Why: without explicit grouping, mobile's primary workflow is jumping + // back into running sessions before browsing the full worktree archive. + sections.push({ title: 'Active', data: active }) + } + if (inactive.length > 0) { + sections.push({ title: pinned.length > 0 || active.length > 0 ? 'All' : '', data: inactive }) + } + } else if (groupMode === 'repo') { + const byRepo = new Map() + for (const w of unpinned) { + const key = w.repo || 'Unknown' + const list = byRepo.get(key) + if (list) list.push(w) + else byRepo.set(key, [w]) + } + for (const [repo, items] of byRepo) { + sections.push({ title: repo, data: items }) + } + } else if (groupMode === 'prStatus') { + const byGroup = new Map() + for (const w of unpinned) { + const key = getPRGroupKey(w) + const list = byGroup.get(key) + if (list) list.push(w) + else byGroup.set(key, [w]) + } + for (const groupKey of PR_GROUP_ORDER) { + const items = byGroup.get(groupKey) + if (items && items.length > 0) { + sections.push({ title: PR_GROUP_LABELS[groupKey], data: items }) + } + } + } + + return sections +} + +export default function HostScreen() { + const { hostId, action } = useLocalSearchParams<{ hostId: string; action?: string }>() + const router = useRouter() + const [initialCache] = useState(() => + hostId ? (getCachedWorktrees(hostId) as Worktree[] | null) : null + ) + const [client, setClient] = useState(null) + const clientRef = useRef(null) + const [connState, setConnState] = useState('disconnected') + const [worktrees, setWorktrees] = useState(initialCache ?? []) + const [worktreesLoaded, setWorktreesLoaded] = useState(initialCache != null) + const [hostName, setHostName] = useState('') + const [error, setError] = useState('') + const [lastKnownWorktrees, setLastKnownWorktrees] = useState(initialCache ?? []) + const [search, setSearch] = useState('') + const [showSearch, setShowSearch] = useState(false) + const [sortMode, setSortMode] = useState('smart') + const [filters, setFilters] = useState({ + activeOnly: false, + selectedRepos: new Set() + }) + const [groupMode, setGroupMode] = useState('none') + + // Modals + const [showSortPicker, setShowSortPicker] = useState(false) + const [showGroupPicker, setShowGroupPicker] = useState(false) + const [showFilterModal, setShowFilterModal] = useState(false) + const [actionTarget, setActionTarget] = useState(null) + const [confirmDelete, setConfirmDelete] = useState(null) + const [confirmRemoveHost, setConfirmRemoveHost] = useState(false) + const [showNewWorktree, setShowNewWorktree] = useState(false) + const [sleptIds, setSleptIds] = useState>(new Set()) + + // Persisted pin state + const [pinnedIds, setPinnedIds] = useState>(new Set()) + const [_prefsLoaded, setPrefsLoaded] = useState(false) + const [collapsedGroups, setCollapsedGroups] = useState>(new Set()) + + useEffect(() => { + if (action === 'newWorktree') setShowNewWorktree(true) + }, [action]) + + // Load persisted pins and preferences + useEffect(() => { + if (!hostId) return + let stale = false + void (async () => { + const [pins, prefs] = await Promise.all([loadPinnedIds(hostId), loadPreferences(hostId)]) + if (stale) return + setPinnedIds(pins) + setSortMode(prefs.sortMode as SortMode) + setFilters({ + activeOnly: prefs.filterMode === 'active', + selectedRepos: new Set(prefs.selectedRepos ?? []) + }) + setGroupMode(prefs.groupMode as GroupMode) + setCollapsedGroups(new Set(prefs.collapsedGroups)) + setPrefsLoaded(true) + })() + return () => { + stale = true + } + }, [hostId]) + + useEffect(() => { + let disposed = false + let rpcClient: RpcClient | null = null + clientRef.current = null + setClient(null) + setConnState('connecting') + setHostName('') + setError('') + // Why: re-seed from the current host's cache on every hostId change. + // The useState initializer only runs on first mount, so if Expo Router + // reuses this screen with a different hostId, we must reset here. + const freshCache = hostId ? (getCachedWorktrees(hostId) as Worktree[] | null) : null + if (freshCache) { + setWorktrees(freshCache) + setLastKnownWorktrees(freshCache) + setWorktreesLoaded(true) + } else { + setWorktreesLoaded(false) + setWorktrees([]) + setLastKnownWorktrees([]) + } + + // Why: defer the RPC connection until after the navigation animation + // completes. Without this, connect() and loadHosts() block the JS + // thread during mount, delaying the screen transition by ~200-400ms. + // With cached worktrees the user sees content instantly; the live + // connection starts once the animation settles. + const rafId = requestAnimationFrame(() => { + if (disposed) return + void (async () => { + const hosts = await loadHosts() + const host = hosts.find((h) => h.id === hostId) + if (!host || disposed) { + if (!host && !disposed) setError('Host not found') + return + } + + rpcClient = connect(host.endpoint, host.deviceToken, host.publicKeyB64, (state) => { + if (!disposed) setConnState(state) + }) + if (disposed) { + rpcClient.close() + rpcClient = null + return + } + setHostName(host.name) + clientRef.current = rpcClient + setClient(rpcClient) + + await updateLastConnected(host.id) + })() + }) + + return () => { + disposed = true + cancelAnimationFrame(rafId) + rpcClient?.close() + if (clientRef.current === rpcClient) { + clientRef.current = null + } + } + }, [hostId]) + + const fetchWorktrees = useCallback(async () => { + if (!client || connState !== 'connected') return + const requestClient = client + const requestHostId = hostId + + try { + const response = await requestClient.sendRequest('worktree.ps') + if (clientRef.current !== requestClient || hostId !== requestHostId) return + if (response.ok) { + const result = (response as RpcSuccess).result as { worktrees: Worktree[] } + setWorktrees(result.worktrees) + setLastKnownWorktrees(result.worktrees) + setWorktreesLoaded(true) + + // Clear optimistic sleep overrides once the server confirms the + // worktree is actually inactive (liveTerminalCount dropped to 0). + setSleptIds((prev) => { + if (prev.size === 0) { + return prev + } + const still = new Set() + for (const id of prev) { + const wt = result.worktrees.find((w) => w.worktreeId === id) + if (wt && wt.liveTerminalCount > 0) { + still.add(id) + } + } + return still.size === prev.size ? prev : still + }) + + // Sync local pin state from server so desktop-initiated pins/unpins + // are reflected without relying on stale AsyncStorage. + const serverPinned = new Set( + result.worktrees.filter((w) => w.isPinned).map((w) => w.worktreeId) + ) + setPinnedIds((prev) => { + if (serverPinned.size === prev.size && [...serverPinned].every((id) => prev.has(id))) { + return prev + } + if (hostId) void savePinnedIds(hostId, serverPinned) + return serverPinned + }) + } + } catch { + // Will retry on reconnect + } + }, [client, connState, hostId]) + + useEffect(() => { + if (connState === 'connected') { + void fetchWorktrees() + } + }, [connState, fetchWorktrees]) + + useEffect(() => { + if (connState !== 'connected') return + const interval = setInterval(() => { + void fetchWorktrees() + }, 3000) + return () => clearInterval(interval) + }, [connState, fetchWorktrees]) + + const updateLocalPins = useCallback( + (worktreeId: string, pinned: boolean) => { + setPinnedIds((prev) => { + const next = new Set(prev) + if (pinned) next.add(worktreeId) + else next.delete(worktreeId) + if (hostId) void savePinnedIds(hostId, next) + return next + }) + }, + [hostId] + ) + + const togglePin = useCallback( + (worktreeId: string) => { + const worktree = worktrees.find((w) => w.worktreeId === worktreeId) + const currentlyPinned = worktree + ? isWorktreePinned(worktree, pinnedIds) + : pinnedIds.has(worktreeId) + const newPinned = !currentlyPinned + + setWorktrees((prev) => + prev.map((w) => (w.worktreeId === worktreeId ? { ...w, isPinned: newPinned } : w)) + ) + setLastKnownWorktrees((prev) => + prev.map((w) => (w.worktreeId === worktreeId ? { ...w, isPinned: newPinned } : w)) + ) + + updateLocalPins(worktreeId, newPinned) + + if (client) { + client + .sendRequest('worktree.set', { + worktree: `id:${worktreeId}`, + isPinned: newPinned + }) + .catch(() => {}) + } + }, + [client, worktrees, pinnedIds, updateLocalPins] + ) + + const handleDeleteWorktree = useCallback( + async (item: Worktree) => { + if (!client) return + + const removeFromList = (list: Worktree[]) => + list.filter((w) => w.worktreeId !== item.worktreeId) + setWorktrees(removeFromList) + setLastKnownWorktrees(removeFromList) + + try { + const response = await client.sendRequest('worktree.rm', { + worktree: `id:${item.worktreeId}`, + force: true + }) + if (!response.ok) { + setWorktrees((prev) => [...prev, item]) + setLastKnownWorktrees((prev) => [...prev, item]) + } + void fetchWorktrees() + } catch { + setWorktrees((prev) => [...prev, item]) + setLastKnownWorktrees((prev) => [...prev, item]) + } + }, + [client, fetchWorktrees] + ) + + const handleRemoveHost = useCallback(async () => { + if (!hostId) return + await removeHost(hostId) + router.back() + }, [hostId, router]) + + const openWorktreeSession = useCallback( + (item: Worktree) => { + if (client && connState === 'connected') { + void client + .sendRequest('worktree.activate', { + worktree: `id:${item.worktreeId}` + }) + .catch(() => null) + } + router.push( + `/h/${hostId}/session/${encodeURIComponent(item.worktreeId)}?name=${encodeURIComponent(item.displayName || item.repo)}` + ) + }, + [client, connState, hostId, router] + ) + + const handleSortChange = useCallback( + (value: SortMode) => { + setSortMode(value) + if (hostId) void savePreferences(hostId, { sortMode: value }) + }, + [hostId] + ) + + const toggleActiveFilter = useCallback(() => { + setFilters((prev) => { + const next = { ...prev, activeOnly: !prev.activeOnly } + if (hostId) + void savePreferences(hostId, { + filterMode: next.activeOnly ? 'active' : 'all' + }) + return next + }) + }, [hostId]) + + const toggleRepoFilter = useCallback( + (repo: string) => { + setFilters((prev) => { + const next = new Set(prev.selectedRepos) + if (next.has(repo)) next.delete(repo) + else next.add(repo) + const updated = { ...prev, selectedRepos: next } + if (hostId) void savePreferences(hostId, { selectedRepos: [...next] }) + return updated + }) + }, + [hostId] + ) + + const clearFilters = useCallback(() => { + setFilters({ activeOnly: false, selectedRepos: new Set() }) + if (hostId) void savePreferences(hostId, { filterMode: 'all', selectedRepos: [] }) + }, [hostId]) + + const activeFilterCount = useMemo(() => { + let count = 0 + if (filters.activeOnly) count++ + count += filters.selectedRepos.size + return count + }, [filters]) + + const handleGroupChange = useCallback( + (value: GroupMode) => { + setGroupMode(value) + if (hostId) void savePreferences(hostId, { groupMode: value }) + }, + [hostId] + ) + + const displayWorktrees = useMemo(() => { + const base = + connState === 'disconnected' || connState === 'reconnecting' || connState === 'auth-failed' + ? lastKnownWorktrees + : worktrees + if (sleptIds.size === 0) { + return base + } + return base.map((w) => + sleptIds.has(w.worktreeId) + ? { ...w, liveTerminalCount: 0, hasAttachedPty: false, status: 'inactive' as const } + : w + ) + }, [connState, worktrees, lastKnownWorktrees, sleptIds]) + + const uniqueRepos = useMemo(() => { + const repos = new Map() + for (const w of displayWorktrees) { + if (!repos.has(w.repo)) repos.set(w.repo, repoColor(w.repo)) + } + return [...repos.entries()].map(([name, color]) => ({ name, color })) + }, [displayWorktrees]) + + const toggleCollapsed = useCallback( + (title: string) => { + setCollapsedGroups((prev) => { + const next = new Set(prev) + if (next.has(title)) next.delete(title) + else next.add(title) + if (hostId) void savePreferences(hostId, { collapsedGroups: [...next] }) + return next + }) + }, + [hostId] + ) + + const rawSections = useMemo( + () => buildSections(displayWorktrees, sortMode, filters, search, groupMode, pinnedIds), + [displayWorktrees, sortMode, filters, search, groupMode, pinnedIds] + ) + + const sections = useMemo( + () => + rawSections.map((s) => ({ + ...s, + data: collapsedGroups.has(s.title) ? [] : s.data + })), + [rawSections, collapsedGroups] + ) + + const isReadOnly = connState === 'auth-failed' + + if (error) { + return ( + + {error} + + ) + } + + return ( + + + + router.back()}> + + + + + + {hostName || 'Host'} + + + {connState !== 'connected' && ( + {STATUS_LABELS[connState]} + )} + + + {/* Filter/sort/group toolbar */} + + 0 && styles.filterChipActive]} + onPress={() => setShowFilterModal(true)} + > + 0 ? colors.textPrimary : colors.textSecondary} + /> + 0 && styles.filterChipTextActive]} + > + Filter{activeFilterCount > 0 ? ` (${activeFilterCount})` : ''} + + + + setShowSortPicker(true)}> + + + {sortMode === 'smart' ? 'Smart' : sortMode === 'name' ? 'Name' : 'Recent'} + + + + setShowGroupPicker(true)}> + + + {groupMode === 'none' ? 'Group' : groupMode === 'repo' ? 'Repo' : 'PR'} + + + + + + setShowNewWorktree(true)} + disabled={connState !== 'connected'} + > + + + + setShowSearch((s) => !s)}> + {showSearch ? ( + + ) : ( + + )} + + + + + {/* Auth failed banner */} + {connState === 'auth-failed' && ( + + + Pairing rejected — re-pair from desktop or remove this host. + + + router.push('/pair-scan')}> + Re-pair + + setConfirmRemoveHost(true)}> + Remove + + + + )} + + {/* Search bar */} + {showSearch && ( + + + + {search.length > 0 && ( + setSearch('')}> + + + )} + + )} + + {/* Loading state */} + {((connState === 'connecting' || connState === 'reconnecting') && + displayWorktrees.length === 0) || + (connState === 'connected' && !worktreesLoaded && displayWorktrees.length === 0) ? ( + + + + ) : null} + + {/* Empty state */} + {connState === 'connected' && worktreesLoaded && sections.length === 0 && ( + + + {search + ? 'No matching worktrees' + : activeFilterCount > 0 + ? 'No worktrees match filters' + : 'No worktrees'} + + + )} + + {/* Worktree list */} + {sections.length > 0 && ( + w.worktreeId} + stickySectionHeadersEnabled={false} + contentContainerStyle={styles.list} + renderSectionHeader={({ section }) => { + if (!section.title) return null + const isCollapsed = collapsedGroups.has(section.title) + const rawSection = rawSections.find((s) => s.title === section.title) + const count = rawSection?.data.length ?? 0 + return ( + toggleCollapsed(section.title)} + > + {isCollapsed ? ( + + ) : ( + + )} + {section.icon === 'pin' && ( + + )} + {section.title} + {count} + + ) + }} + ItemSeparatorComponent={ListSeparator} + renderItem={({ item }) => ( + [styles.worktreeRow, pressed && styles.worktreeRowPressed]} + disabled={isReadOnly} + onPress={() => openWorktreeSession(item)} + onLongPress={() => { + triggerMediumImpact() + setActionTarget(item) + }} + delayLongPress={400} + > + {/* Left indicator */} + + + {item.unread && ( + + )} + + + {/* Main content */} + + + + {item.displayName || item.repo} + + {item.linkedPR && ( + + + #{item.linkedPR.number} + + )} + + + + + {item.repo} + + + {item.branch} + + + {item.preview ? ( + + {item.preview} + + ) : null} + + + {/* Terminal count */} + {item.liveTerminalCount > 0 && ( + {item.liveTerminalCount} + )} + + )} + /> + )} + + {/* Sort picker modal */} + setShowSortPicker(false)} + /> + + {/* Group picker modal */} + setShowGroupPicker(false)} + /> + + {/* Filter modal — matches desktop's Status + Repositories dropdown */} + setShowFilterModal(false)}> + + Filter + {activeFilterCount > 0 && ( + + Clear filters + + )} + + + Status + + + Active only + {filters.activeOnly && } + + + + {uniqueRepos.length > 1 && ( + <> + Repositories + + {uniqueRepos.map((repo, i) => ( + + {i > 0 && } + toggleRepoFilter(repo.name)}> + + + {repo.name} + + {filters.selectedRepos.has(repo.name) && ( + + )} + + + ))} + + + )} + + + {/* Worktree long-press action sheet (inline confirm to avoid double-Modal lag) */} + { + setConfirmDelete(null) + setActionTarget(null) + }} + > + {confirmDelete ? ( + + + Delete Worktree + + Delete "{confirmDelete.displayName || confirmDelete.repo}" ({confirmDelete.branch})? + + + + [ + styles.confirmBtn, + styles.confirmBtnCancel, + pressed && styles.confirmBtnPressed + ]} + onPress={() => setConfirmDelete(null)} + > + Cancel + + [ + styles.confirmBtn, + styles.confirmBtnDestructive, + pressed && styles.confirmBtnPressed + ]} + onPress={() => { + if (confirmDelete) { + void handleDeleteWorktree(confirmDelete) + } + setConfirmDelete(null) + setActionTarget(null) + }} + > + Delete + + + + ) : ( + { + if (client) { + setSleptIds((prev) => new Set(prev).add(actionTarget.worktreeId)) + void client + .sendRequest('worktree.sleep', { + worktree: `id:${actionTarget.worktreeId}` + }) + .catch(() => null) + } + setActionTarget(null) + } + }, + { + label: isWorktreePinned(actionTarget, pinnedIds) ? 'Unpin' : 'Pin', + onPress: () => { + togglePin(actionTarget.worktreeId) + setActionTarget(null) + } + }, + { + label: 'Delete', + destructive: true, + onPress: () => setConfirmDelete(actionTarget) + } + ] + : [] + } + /> + )} + + + {/* Host remove confirmation */} + void handleRemoveHost()} + onCancel={() => setConfirmRemoveHost(false)} + /> + + { + void fetchWorktrees() + const params = new URLSearchParams({ name: worktreeName, created: '1' }) + router.push(`/h/${hostId}/session/${encodeURIComponent(worktreeId)}?${params.toString()}`) + }} + onClose={() => setShowNewWorktree(false)} + /> + + ) +} + +function ListSeparator() { + return +} + +function repoColor(name: string): string { + const palette = ['#f97316', '#8b5cf6', '#06b6d4', '#ec4899', '#84cc16', '#f59e0b', '#6366f1'] + let hash = 0 + for (let i = 0; i < name.length; i++) hash = (hash * 31 + name.charCodeAt(i)) | 0 + return palette[Math.abs(hash) % palette.length]! +} + +const styles = StyleSheet.create({ + container: { + flex: 1, + backgroundColor: colors.bgBase + }, + topChrome: { + backgroundColor: colors.bgPanel, + borderBottomWidth: 1, + borderBottomColor: colors.borderSubtle + }, + statusBar: { + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'space-between', + minHeight: 34, + paddingTop: spacing.xs, + paddingHorizontal: spacing.lg + }, + backButton: { + width: 32, + height: 32, + alignItems: 'center', + justifyContent: 'center', + marginRight: spacing.xs + }, + hostIdentity: { + flex: 1, + flexDirection: 'row', + alignItems: 'center', + minWidth: 0, + marginRight: spacing.md + }, + hostNameText: { + flex: 1, + fontSize: 15, + fontWeight: '600', + color: colors.textPrimary + }, + statusText: { + color: colors.textSecondary, + fontSize: typography.metaSize + }, + authBanner: { + backgroundColor: colors.bgPanel, + paddingVertical: spacing.sm, + paddingHorizontal: spacing.lg, + borderBottomWidth: 1, + borderBottomColor: colors.borderSubtle + }, + authBannerText: { + color: colors.statusRed, + fontSize: 13, + marginBottom: spacing.sm + }, + authActions: { + flexDirection: 'row', + gap: spacing.lg + }, + authAction: { + paddingVertical: spacing.xs + }, + authActionText: { + color: colors.accentBlue, + fontSize: 13, + fontWeight: '600' + }, + toolbar: { + flexDirection: 'row', + alignItems: 'center', + paddingVertical: spacing.xs + 2, + paddingHorizontal: spacing.md, + gap: spacing.sm, + borderBottomWidth: 1, + borderBottomColor: colors.borderSubtle + }, + filterChip: { + flexDirection: 'row', + alignItems: 'center', + gap: 4, + paddingHorizontal: spacing.sm + 2, + paddingVertical: spacing.xs, + borderRadius: 12, + borderWidth: 1, + borderColor: colors.borderSubtle + }, + filterChipActive: { + borderColor: colors.textSecondary, + backgroundColor: colors.bgRaised + }, + filterChipText: { + fontSize: 12, + color: colors.textSecondary + }, + filterChipTextActive: { + color: colors.textPrimary + }, + sortButton: { + flexDirection: 'row', + alignItems: 'center', + gap: 4, + paddingHorizontal: spacing.sm, + paddingVertical: spacing.xs + }, + groupButton: { + flexDirection: 'row', + alignItems: 'center', + gap: 4, + paddingHorizontal: spacing.sm, + paddingVertical: spacing.xs + }, + sortLabel: { + fontSize: 12, + color: colors.textSecondary + }, + toolbarSpacer: { + flex: 1 + }, + newButton: { + padding: spacing.xs + }, + searchToggle: { + padding: spacing.xs + }, + searchBar: { + flexDirection: 'row', + alignItems: 'center', + paddingHorizontal: spacing.md, + paddingVertical: spacing.xs + 2, + gap: spacing.sm, + borderBottomWidth: 1, + borderBottomColor: colors.borderSubtle, + backgroundColor: colors.bgPanel + }, + searchInput: { + flex: 1, + color: colors.textPrimary, + fontSize: 13, + paddingVertical: 2 + }, + centered: { + flex: 1, + alignItems: 'center', + justifyContent: 'center' + }, + emptyText: { + color: colors.textSecondary, + fontSize: typography.bodySize + }, + errorText: { + color: colors.statusRed, + fontSize: typography.bodySize + }, + list: { + paddingBottom: spacing.lg + }, + sectionHeader: { + flexDirection: 'row', + alignItems: 'center', + paddingHorizontal: spacing.lg, + paddingTop: spacing.md, + paddingBottom: spacing.xs + }, + sectionIcon: { + marginRight: spacing.xs + }, + sectionTitle: { + fontSize: 11, + fontWeight: '600', + color: colors.textMuted, + textTransform: 'uppercase', + letterSpacing: 0.5 + }, + sectionCount: { + fontSize: 11, + color: colors.textMuted, + marginLeft: spacing.xs + }, + separator: { + height: 1, + backgroundColor: colors.borderSubtle, + marginLeft: spacing.lg + 24, + marginRight: spacing.lg + }, + worktreeRow: { + flexDirection: 'row', + alignItems: 'flex-start', + paddingVertical: spacing.sm + 2, + paddingHorizontal: spacing.lg + }, + worktreeRowPressed: { + backgroundColor: colors.bgRaised + }, + indicatorCol: { + width: 20, + alignItems: 'center', + paddingTop: 6, + marginRight: spacing.sm, + gap: 4 + }, + unreadBell: { + marginTop: 2 + }, + worktreeMain: { + flex: 1, + marginRight: spacing.sm + }, + worktreeNameRow: { + flexDirection: 'row', + alignItems: 'center', + gap: spacing.sm + }, + worktreeName: { + fontSize: 14, + fontWeight: '600', + color: colors.textPrimary, + flexShrink: 1 + }, + textReadOnly: { + opacity: 0.5 + }, + prBadge: { + flexDirection: 'row', + alignItems: 'center', + gap: 3, + backgroundColor: colors.bgRaised, + paddingHorizontal: 5, + paddingVertical: 1, + borderRadius: 4 + }, + prNumber: { + fontSize: 10, + color: colors.textSecondary + }, + worktreeMetaRow: { + flexDirection: 'row', + alignItems: 'center', + marginTop: 2, + gap: spacing.xs + }, + repoDot: { + width: 6, + height: 6, + borderRadius: 3 + }, + repoName: { + fontSize: 11, + color: colors.textSecondary, + maxWidth: 100 + }, + branchName: { + fontSize: 11, + color: colors.textMuted, + fontFamily: typography.monoFamily, + flexShrink: 1 + }, + worktreePreview: { + fontSize: 11, + color: colors.textMuted, + fontFamily: typography.monoFamily, + marginTop: 2 + }, + terminalCount: { + fontSize: typography.metaSize, + color: colors.textMuted, + minWidth: 16, + textAlign: 'right', + paddingTop: 3 + }, + filterModalHeader: { + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'space-between', + paddingHorizontal: spacing.xs, + marginBottom: spacing.md + }, + filterModalTitle: { + fontSize: 15, + fontWeight: '600', + color: colors.textPrimary + }, + clearFiltersText: { + fontSize: 13, + color: colors.textSecondary + }, + filterSectionLabel: { + fontSize: 11, + fontWeight: '600', + color: colors.textMuted, + textTransform: 'uppercase', + letterSpacing: 0.5, + marginBottom: spacing.xs, + paddingHorizontal: spacing.xs + }, + filterGroup: { + backgroundColor: colors.bgPanel, + borderRadius: 12, + overflow: 'hidden', + marginBottom: spacing.md + }, + filterRow: { + flexDirection: 'row', + alignItems: 'center', + paddingVertical: spacing.md, + paddingHorizontal: spacing.md + 2, + gap: spacing.sm + }, + filterRowText: { + flex: 1, + fontSize: typography.bodySize, + color: colors.textPrimary + }, + filterSeparator: { + height: StyleSheet.hairlineWidth, + backgroundColor: colors.borderSubtle, + marginHorizontal: spacing.md + }, + filterRepoDot: { + width: 8, + height: 8, + borderRadius: 4 + }, + confirmContent: { + paddingBottom: spacing.lg + }, + confirmTitle: { + fontSize: 16, + fontWeight: '700', + color: colors.textPrimary + }, + confirmMessage: { + fontSize: typography.bodySize, + color: colors.textSecondary, + marginTop: spacing.xs, + lineHeight: 20 + }, + confirmButtons: { + flexDirection: 'row', + gap: spacing.sm + }, + confirmBtn: { + flex: 1, + paddingVertical: spacing.sm + 2, + borderRadius: 10, + alignItems: 'center' + }, + confirmBtnCancel: { + backgroundColor: colors.bgPanel + }, + confirmBtnDestructive: { + backgroundColor: colors.statusRed + }, + confirmBtnPressed: { + opacity: 0.7 + }, + confirmBtnCancelText: { + fontSize: typography.bodySize, + fontWeight: '600', + color: colors.textSecondary + }, + confirmBtnDestructiveText: { + fontSize: typography.bodySize, + fontWeight: '600', + color: '#fff' + } +}) diff --git a/mobile/app/h/[hostId]/session/[worktreeId].tsx b/mobile/app/h/[hostId]/session/[worktreeId].tsx new file mode 100644 index 00000000000..320dcfadf2e --- /dev/null +++ b/mobile/app/h/[hostId]/session/[worktreeId].tsx @@ -0,0 +1,1248 @@ +import { useState, useEffect, useRef, useCallback } from 'react' +import { + View, + Text, + StyleSheet, + ScrollView, + TextInput, + Pressable, + KeyboardAvoidingView, + Platform, + ActivityIndicator +} from 'react-native' +import { SafeAreaView } from 'react-native-safe-area-context' +import { useLocalSearchParams, useRouter } from 'expo-router' +import AsyncStorage from '@react-native-async-storage/async-storage' +import { ArrowUp, ChevronLeft, Monitor, Plus, Smartphone } from 'lucide-react-native' +import { connect, type RpcClient } from '../../../../src/transport/rpc-client' +import { loadHosts } from '../../../../src/transport/host-store' +import type { ConnectionState, RpcSuccess } from '../../../../src/transport/types' +import { triggerMediumImpact } from '../../../../src/platform/haptics' +import { + TerminalWebView, + type TerminalWebViewHandle +} from '../../../../src/terminal/TerminalWebView' +import { StatusDot } from '../../../../src/components/StatusDot' +import { ActionSheetModal } from '../../../../src/components/ActionSheetModal' +import { TextInputModal } from '../../../../src/components/TextInputModal' +import { + CustomKeyModal, + loadCustomKeys, + type CustomKey +} from '../../../../src/components/CustomKeyModal' +import { colors, spacing, radii, typography } from '../../../../src/theme/mobile-theme' + +type Terminal = { + handle: string + title: string + isActive: boolean +} + +type TerminalCreateResult = { + terminal: { + handle: string + title: string | null + } +} + +type MobileDisplayMode = 'auto' | 'phone' | 'desktop' + +type AccessoryKey = { label: string; bytes: string; accessibilityLabel?: string } + +const ACCESSORY_KEYS: AccessoryKey[] = [ + { label: 'Esc', bytes: '\x1b' }, + { label: 'Tab', bytes: '\t' }, + { label: '↑', bytes: '\x1b[A' }, + { label: '↓', bytes: '\x1b[B' }, + { label: '←', bytes: '\x1b[D' }, + { label: '→', bytes: '\x1b[C' }, + { label: 'Ctrl+C', bytes: '\x03', accessibilityLabel: 'Interrupt terminal' }, + { label: 'Ctrl+D', bytes: '\x04', accessibilityLabel: 'Send EOF' }, + { label: 'Ctrl+L', bytes: '\x0c', accessibilityLabel: 'Clear screen' }, + { label: 'Ctrl+Z', bytes: '\x1a', accessibilityLabel: 'Suspend process' }, + { label: 'Ctrl+R', bytes: '\x12', accessibilityLabel: 'Reverse search' }, + { label: 'Ctrl+A', bytes: '\x01', accessibilityLabel: 'Start of line' }, + { label: 'Ctrl+E', bytes: '\x05', accessibilityLabel: 'End of line' }, + { label: 'Ctrl+W', bytes: '\x17', accessibilityLabel: 'Delete word backward' }, + { label: 'Ctrl+U', bytes: '\x15', accessibilityLabel: 'Clear line before cursor' } +] + +const STATUS_LABELS: Record = { + connecting: 'Connecting', + handshaking: 'Securing', + connected: 'Connected', + disconnected: 'Disconnected', + reconnecting: 'Reconnecting', + 'auth-failed': 'Auth failed' +} + +function TerminalPaneView({ + handle, + active, + onRef, + onWebReady +}: { + handle: string + active: boolean + onRef: (handle: string, ref: TerminalWebViewHandle | null) => void + onWebReady: (handle: string) => void +}) { + const setRef = useCallback( + (ref: TerminalWebViewHandle | null) => { + onRef(handle, ref) + }, + [handle, onRef] + ) + + return ( + + onWebReady(handle)} + /> + + ) +} + +export default function SessionScreen() { + const { + hostId, + worktreeId, + name: worktreeName, + created + } = useLocalSearchParams<{ + hostId: string + worktreeId: string + name?: string + created?: string + }>() + const router = useRouter() + const [client, setClient] = useState(null) + const [connState, setConnState] = useState('disconnected') + const [terminals, setTerminals] = useState([]) + const [terminalsLoaded, setTerminalsLoaded] = useState(false) + const [input, setInput] = useState('') + const [activeHandle, setActiveHandle] = useState(null) + const [creating, setCreating] = useState(false) + const [createError, setCreateError] = useState('') + const [actionTarget, setActionTarget] = useState(null) + const [renameTarget, setRenameTarget] = useState(null) + const [customKeys, setCustomKeys] = useState([]) + const [showCustomKeyModal, setShowCustomKeyModal] = useState(false) + const [deleteKeyTarget, setDeleteKeyTarget] = useState(null) + // Why: server-authoritative display mode per terminal. The runtime is the + // single source of truth — this state is populated from subscribe responses. + const [terminalModes, setTerminalModes] = useState>(new Map()) + const deviceTokenRef = useRef(null) + const clientRef = useRef(null) + // Why: measured once from TerminalWebView on mount, then passed with every + // subscribe call so the server can auto-fit the PTY to phone dimensions. + const viewportRef = useRef<{ cols: number; rows: number } | null>(null) + const viewportMeasuredRef = useRef(false) + const terminalRefs = useRef>(new Map()) + const terminalUnsubsRef = useRef void>>(new Map()) + const subscribingHandlesRef = useRef>(new Set()) + const initializedHandlesRef = useRef>(new Set()) + // Why: WebViews load xterm.js from CDN asynchronously. Hidden WebViews + // (opacity:0) may have delayed JS execution on iOS. We must not subscribe + // until the WebView has fired web-ready, otherwise init() messages queue + // and may not render reliably. + const webReadyHandlesRef = useRef>(new Set()) + const activeHandleRef = useRef(null) + const subscribeSeqRef = useRef>(new Map()) + const sendingRef = useRef(false) + // Why: tracks the pixel height of the terminal frame so measureFitDimensions + // can use the exact container height instead of relying on window.innerHeight, + // which can overstate the visible area due to layout timing. + const terminalFrameHeightRef = useRef(0) + + const canSend = connState === 'connected' && activeHandle != null + + const getTerminalRef = useCallback((handle: string | null) => { + return handle ? terminalRefs.current.get(handle) : undefined + }, []) + + const unsubscribeTerminal = useCallback((handle: string) => { + terminalUnsubsRef.current.get(handle)?.() + terminalUnsubsRef.current.delete(handle) + subscribingHandlesRef.current.delete(handle) + subscribeSeqRef.current.set(handle, (subscribeSeqRef.current.get(handle) ?? 0) + 1) + }, []) + + const clearTerminalCache = useCallback(() => { + for (const unsub of terminalUnsubsRef.current.values()) { + unsub() + } + terminalUnsubsRef.current.clear() + subscribingHandlesRef.current.clear() + initializedHandlesRef.current.clear() + webReadyHandlesRef.current.clear() + subscribeSeqRef.current.clear() + for (const term of terminalRefs.current.values()) { + term.clear() + } + }, []) + + // Why: measures the phone viewport once from the first available TerminalWebView. + // The viewport dims are passed with every subscribe call so the server can + // auto-fit the PTY without a separate RPC round-trip. + const measureViewportOnce = useCallback( + async (handle: string) => { + if (viewportMeasuredRef.current) return + const dims = await getTerminalRef(handle)?.measureFitDimensions( + terminalFrameHeightRef.current || undefined + ) + if (dims) { + viewportRef.current = dims + viewportMeasuredRef.current = true + } + }, + [getTerminalRef] + ) + + const subscribeToTerminal = useCallback( + (handle: string) => { + if (!client) return + if (terminalUnsubsRef.current.has(handle)) return + if (subscribingHandlesRef.current.has(handle)) return + if (!getTerminalRef(handle)) { + return + } + + subscribingHandlesRef.current.add(handle) + const seq = (subscribeSeqRef.current.get(handle) ?? 0) + 1 + subscribeSeqRef.current.set(handle, seq) + + console.log( + `[mobile-fit] subscribeToTerminal handle=${handle} seq=${seq} viewport=${viewportRef.current ? `${viewportRef.current.cols}x${viewportRef.current.rows}` : 'none'} measured=${viewportMeasuredRef.current}` + ) + + // Why: server handles auto-fit on subscribe — no terminal.focus call needed. + // The viewport is embedded in the subscribe params so the server resizes + // the PTY before serializing scrollback. This eliminates the focus→safeFit + // race and the measure→resize→resubscribe pipeline. + const unsub = client.subscribe( + 'terminal.subscribe', + { + terminal: handle, + client: { id: deviceTokenRef.current!, type: 'mobile' as const }, + viewport: viewportRef.current ?? undefined + }, + (result) => { + if (subscribeSeqRef.current.get(handle) !== seq) return + const data = result as Record + if (data.type === 'scrollback') { + console.log( + `[mobile-fit] scrollback handle=${handle} cols=${data.cols} rows=${data.rows} displayMode=${data.displayMode} hasSerialized=${!!data.serialized} alreadyInit=${initializedHandlesRef.current.has(handle)}` + ) + if (initializedHandlesRef.current.has(handle)) return + const cols = (data.cols as number) || 80 + const rows = (data.rows as number) || 24 + const initialData = + typeof data.serialized === 'string' && data.serialized.length > 0 + ? data.serialized + : '' + getTerminalRef(handle)?.init(cols, rows, initialData) + initializedHandlesRef.current.add(handle) + if (data.displayMode) { + setTerminalModes((prev) => + new Map(prev).set(handle, data.displayMode as MobileDisplayMode) + ) + } + // Why: viewport measurement needs xterm to be initialized (cell + // dimensions come from the renderer). On the first subscribe the + // WebView hasn't loaded yet, so viewportRef is null and the server + // can't auto-fit. After the first init we can measure, then + // resubscribe so the server gets the viewport and phone-fits. + if (!viewportMeasuredRef.current) { + void (async () => { + const dims = await getTerminalRef(handle)?.measureFitDimensions( + terminalFrameHeightRef.current || undefined + ) + if (dims && !viewportMeasuredRef.current) { + viewportRef.current = dims + viewportMeasuredRef.current = true + unsubscribeTerminal(handle) + initializedHandlesRef.current.delete(handle) + subscribeToTerminal(handle) + } + })() + } + } else if (data.type === 'data') { + getTerminalRef(handle)?.write(data.chunk as string) + } else if (data.type === 'resized') { + console.log( + `[mobile-fit] resized handle=${handle} cols=${data.cols} rows=${data.rows} displayMode=${data.displayMode} reason=${(data as Record).reason}` + ) + // Why: inline resize event — the server changed the PTY dimensions + // (mode toggle or desktop restore). Reinitialize xterm at the new + // dims with fresh scrollback. No resubscribe needed. + const cols = (data.cols as number) || 80 + const rows = (data.rows as number) || 24 + const serialized = + typeof data.serialized === 'string' && data.serialized.length > 0 + ? data.serialized + : '' + getTerminalRef(handle)?.init(cols, rows, serialized) + if (data.displayMode) { + setTerminalModes((prev) => + new Map(prev).set(handle, data.displayMode as MobileDisplayMode) + ) + } + setTimeout(() => getTerminalRef(handle)?.resetZoom(), 200) + } + } + ) + + if (subscribeSeqRef.current.get(handle) === seq) { + terminalUnsubsRef.current.set(handle, unsub) + } else { + unsub() + } + subscribingHandlesRef.current.delete(handle) + }, + [client, getTerminalRef] + ) + + // Why: toggles between phone and desktop mode via server RPC. The server + // handles the actual resize and emits a 'resized' event on the existing + // subscription stream — no client-side state tracking needed. + const toggleInFlightRef = useRef>(new Set()) + const toggleDisplayMode = useCallback( + async (handle: string) => { + if (!client) return + if (toggleInFlightRef.current.has(handle)) return + const current = terminalModes.get(handle) ?? 'auto' + const next: MobileDisplayMode = current === 'auto' || current === 'phone' ? 'desktop' : 'auto' + toggleInFlightRef.current.add(handle) + try { + await client.sendRequest('terminal.setDisplayMode', { terminal: handle, mode: next }) + } catch { + // Mode change failed — server state unchanged, UI stays in sync. + } finally { + toggleInFlightRef.current.delete(handle) + } + }, + [client, terminalModes] + ) + + const lastKnownTerminalCountRef = useRef(0) + + const fetchTerminals = useCallback( + async (opts: { allowEmptyLoaded?: boolean } = {}) => { + if (!client) return + const allowEmptyLoaded = opts.allowEmptyLoaded ?? true + + try { + const response = await client.sendRequest('terminal.list', { + worktree: `id:${worktreeId}` + }) + if (response.ok) { + const result = (response as RpcSuccess).result as { terminals: Terminal[] } + console.log( + `[mobile-fit] fetchTerminals count=${result.terminals.length} allowEmpty=${allowEmptyLoaded} activeHandle=${activeHandleRef.current} lastKnown=${lastKnownTerminalCountRef.current}` + ) + + if (result.terminals.length === 0 && !allowEmptyLoaded) { + return + } + // Why: protect against transient empty responses from the server + // during rapid tab switching or RPC timing. If we previously had + // terminals and the server now says 0, require a second consecutive + // empty to confirm. This prevents the UI from flashing empty during + // rapid interactions while still allowing genuine cleanup. + if (result.terminals.length === 0 && lastKnownTerminalCountRef.current > 0) { + lastKnownTerminalCountRef.current = 0 + console.log( + `[mobile-fit] fetchTerminals SKIP first empty — will clear on next fetch if still empty` + ) + return + } + + const liveHandles = new Set(result.terminals.map((terminal) => terminal.handle)) + for (const handle of Array.from(terminalUnsubsRef.current.keys())) { + if (!liveHandles.has(handle)) { + unsubscribeTerminal(handle) + terminalRefs.current.delete(handle) + initializedHandlesRef.current.delete(handle) + } + } + lastKnownTerminalCountRef.current = result.terminals.length + const current = activeHandleRef.current + + setTerminals(result.terminals) + setTerminalsLoaded(true) + + if (!current || !result.terminals.some((t) => t.handle === current)) { + const active = result.terminals.find((t) => t.isActive) ?? result.terminals[0] + if (active) { + activeHandleRef.current = active.handle + setActiveHandle(active.handle) + subscribeToTerminal(active.handle) + } else { + activeHandleRef.current = null + setActiveHandle(null) + } + } + } + } catch { + // Failed to list terminals + } + }, + [client, worktreeId, subscribeToTerminal, unsubscribeTerminal] + ) + + useEffect(() => { + let disposed = false + let rpcClient: RpcClient | null = null + + void (async () => { + const hosts = await loadHosts() + const host = hosts.find((h) => h.id === hostId) + if (!host || disposed) return + + deviceTokenRef.current = host.deviceToken + rpcClient = connect(host.endpoint, host.deviceToken, host.publicKeyB64, setConnState) + if (disposed) { + rpcClient.close() + return + } + setClient(rpcClient) + clientRef.current = rpcClient + })() + + return () => { + disposed = true + clearTerminalCache() + rpcClient?.close() + if (clientRef.current === rpcClient) { + clientRef.current = null + } + } + }, [clearTerminalCache, hostId]) + + useEffect(() => { + void loadCustomKeys().then(setCustomKeys) + }, []) + + useEffect(() => { + if (hostId && worktreeId) { + void AsyncStorage.setItem( + 'orca:last-visited-worktree', + JSON.stringify({ hostId, worktreeId }) + ) + } + }, [hostId, worktreeId]) + + const handleDeleteCustomKey = useCallback( + async (key: CustomKey) => { + const updated = customKeys.filter((k) => k.id !== key.id) + setCustomKeys(updated) + await AsyncStorage.setItem('orca:custom-accessory-keys', JSON.stringify(updated)) + }, + [customKeys] + ) + + useEffect(() => { + clearTerminalCache() + activeHandleRef.current = null + setActiveHandle(null) + setTerminals([]) + }, [clearTerminalCache, worktreeId]) + + useEffect(() => { + if (connState !== 'connected') return + // Why: on reconnect the RPC client auto-resends terminal.subscribe, + // creating new server-side handlers. Clear local subscription state + // so subscribeToTerminal's guards don't block fresh subscriptions, + // and clear xterm buffers so the new scrollback snapshot replaces + // stale content (including data that arrived while disconnected). + clearTerminalCache() + setTerminalsLoaded(false) + let disposed = false + const timers: ReturnType[] = [] + function addTimer(fn: () => void, ms: number) { + if (disposed) return + timers.push(setTimeout(fn, ms)) + } + void (async () => { + if (client && created !== '1') { + await client + .sendRequest('worktree.activate', { + worktree: `id:${worktreeId}` + }) + .catch(() => null) + } + if (disposed) return + await fetchTerminals({ allowEmptyLoaded: false }) + if (disposed) return + addTimer(() => void fetchTerminals({ allowEmptyLoaded: false }), 750) + addTimer(() => void fetchTerminals({ allowEmptyLoaded: true }), 1500) + if (client && created === '1') { + addTimer(() => { + if (activeHandleRef.current) return + void (async () => { + await client + .sendRequest('worktree.activate', { + worktree: `id:${worktreeId}` + }) + .catch(() => null) + if (disposed) return + await fetchTerminals({ allowEmptyLoaded: true }) + addTimer(() => void fetchTerminals({ allowEmptyLoaded: true }), 750) + })() + }, 1800) + } + })() + return () => { + disposed = true + for (const t of timers) clearTimeout(t) + } + }, [client, connState, created, fetchTerminals, worktreeId]) + + useEffect(() => { + if (connState !== 'connected') return + const interval = setInterval(() => { + void fetchTerminals() + }, 2000) + return () => clearInterval(interval) + }, [connState, fetchTerminals]) + + // Why: unsubscribe the old terminal so the server restores its desktop dims + // (clearing the phone-fit banner), then subscribe the new terminal with the + // measured viewport so the server phone-fits it. Also call terminal.focus + // so the desktop renderer follows the mobile user's active terminal. + const switchTab = useCallback( + (handle: string) => { + const prev = activeHandleRef.current + console.log( + `[mobile-fit] switchTab prev=${prev} next=${handle} hasUnsub=${terminalUnsubsRef.current.has(handle)} hasRef=${!!terminalRefs.current.get(handle)}` + ) + activeHandleRef.current = handle + setActiveHandle(handle) + if (prev && prev !== handle) { + unsubscribeTerminal(prev) + initializedHandlesRef.current.delete(prev) + } + // Force a fresh subscribe even if eagerly subscribed without viewport + if (terminalUnsubsRef.current.has(handle)) { + unsubscribeTerminal(handle) + initializedHandlesRef.current.delete(handle) + } + subscribeToTerminal(handle) + if (client) { + void client.sendRequest('terminal.focus', { terminal: handle }).catch(() => {}) + } + }, + [client, subscribeToTerminal, unsubscribeTerminal] + ) + + // Why: just store the ref. Subscription is deferred to handleTerminalWebReady + // which fires after the WebView has loaded xterm.js and is ready to process + // init messages. This prevents the blank terminal race where init() was + // queued before the WebView loaded. + const setTerminalWebViewRef = useCallback((handle: string, ref: TerminalWebViewHandle | null) => { + if (ref) { + terminalRefs.current.set(handle, ref) + console.log( + `[mobile-fit] setTerminalWebViewRef handle=${handle} isActive=${handle === activeHandleRef.current} webReady=${webReadyHandlesRef.current.has(handle)}` + ) + } else { + terminalRefs.current.delete(handle) + } + }, []) + + const handleTerminalWebReady = useCallback( + (handle: string) => { + const wasAlreadyReady = webReadyHandlesRef.current.has(handle) + webReadyHandlesRef.current.add(handle) + console.log( + `[mobile-fit] handleTerminalWebReady handle=${handle} isActive=${handle === activeHandleRef.current} wasAlreadyReady=${wasAlreadyReady} wasInitialized=${initializedHandlesRef.current.has(handle)}` + ) + if (wasAlreadyReady && initializedHandlesRef.current.has(handle)) { + // Why: the native WebView reloaded (Metro hot reload or Android + // process churn). The old xterm buffer is gone, so force a fresh + // scrollback snapshot. Only resubscribe if this is a reload — on + // first load the subscription is already running and pendingMessages + // will flush the queued init after this callback returns. + unsubscribeTerminal(handle) + initializedHandlesRef.current.delete(handle) + if (handle === activeHandleRef.current) { + subscribeToTerminal(handle) + } + return + } + // Why: on first web-ready, the initial subscribeToTerminal call from + // fetchTerminals may have been skipped (reason=no-ref, WebView wasn't + // mounted yet). Now that the WebView is ready, subscribe if this is the + // active terminal and no subscription is running. + if (handle === activeHandleRef.current && !terminalUnsubsRef.current.has(handle)) { + void measureViewportOnce(handle) + subscribeToTerminal(handle) + } + }, + [measureViewportOnce, subscribeToTerminal, unsubscribeTerminal] + ) + + async function handleSend() { + if (!client || !activeHandle || sendingRef.current) return + sendingRef.current = true + + const text = input + setInput('') + + try { + await client.sendRequest('terminal.send', { + terminal: activeHandle, + text, + enter: true + }) + } catch { + setInput(text) + } finally { + sendingRef.current = false + } + } + + async function handleAccessoryKey(bytes: string) { + if (!client || !activeHandle || !canSend) return + + try { + await client.sendRequest('terminal.send', { + terminal: activeHandle, + text: bytes, + enter: false + }) + } catch { + // Transient failure + } + } + + async function handleCreateTerminal() { + if (!client || creating) return + + setCreating(true) + setCreateError('') + + try { + const response = await client.sendRequest('terminal.create', { + worktree: `id:${worktreeId}` + }) + if (response.ok) { + const result = (response as RpcSuccess).result as TerminalCreateResult + const created = result.terminal + // Why: unsubscribe the old active terminal so the server restores its + // desktop dims. Without this, the old terminal's mobile subscription + // stays alive and its restore timer is never set. + const prev = activeHandleRef.current + if (prev) { + unsubscribeTerminal(prev) + initializedHandlesRef.current.delete(prev) + } + activeHandleRef.current = created.handle + setActiveHandle(created.handle) + setTerminals((prev) => [ + ...prev, + { handle: created.handle, title: created.title || 'Terminal', isActive: true } + ]) + subscribeToTerminal(created.handle) + setTimeout(() => void fetchTerminals(), 500) + } else { + setCreateError('Failed to create terminal') + } + } catch { + setCreateError('Failed to create terminal') + } finally { + setCreating(false) + } + } + + async function handleRenameTerminal(value: string) { + if (!client || !renameTarget) return + const target = renameTarget + setRenameTarget(null) + + try { + const title = value.trim() + const response = await client.sendRequest('terminal.rename', { + terminal: target.handle, + title + }) + if (response.ok) { + setTerminals((prev) => + prev.map((terminal) => + terminal.handle === target.handle + ? { ...terminal, title: title || 'Terminal' } + : terminal + ) + ) + setTimeout(() => void fetchTerminals(), 300) + } + } catch { + // Rename failed — refresh will restore the server title. + } + } + + async function handleCloseTerminal(target: Terminal) { + if (!client) return + + try { + const response = await client.sendRequest('terminal.close', { + terminal: target.handle + }) + if (response.ok) { + unsubscribeTerminal(target.handle) + terminalRefs.current.delete(target.handle) + initializedHandlesRef.current.delete(target.handle) + const next = terminals.filter((terminal) => terminal.handle !== target.handle) + setTerminals(next) + if (activeHandleRef.current === target.handle) { + const replacement = next[0] ?? null + activeHandleRef.current = replacement?.handle ?? null + setActiveHandle(replacement?.handle ?? null) + if (replacement) { + subscribeToTerminal(replacement.handle) + } + } + setTimeout(() => void fetchTerminals(), 300) + } + } catch { + // Close failed — keep the local tab list unchanged. + } + } + + const isPhoneMode = (handle: string | null): boolean => { + if (!handle) return false + const mode = terminalModes.get(handle) + return mode === 'auto' || mode === 'phone' || mode === undefined + } + + const showLoadingState = connState === 'connected' && !terminalsLoaded + const showEmptyState = + connState === 'connected' && terminalsLoaded && terminals.length === 0 && !activeHandle + const terminalSummary = + connState === 'connected' + ? !terminalsLoaded + ? 'Loading terminals' + : terminals.length === 1 + ? '1 terminal' + : `${terminals.length} terminals` + : STATUS_LABELS[connState] + + return ( + + + + + [styles.backButton, pressed && styles.backButtonPressed]} + onPress={() => router.back()} + hitSlop={8} + accessibilityLabel="Back to worktrees" + > + + + + + + {worktreeName || 'Terminal'} + + + + + {terminalSummary} + + + + + + {terminals.length > 0 && ( + + + {terminals.map((t) => ( + switchTab(t.handle)} + onLongPress={() => { + triggerMediumImpact() + setActionTarget(t) + }} + delayLongPress={400} + > + + {t.title || 'Terminal'} + + + ))} + [ + styles.newTerminalButton, + pressed && styles.newTerminalButtonPressed, + (creating || connState !== 'connected') && styles.newTerminalButtonDisabled + ]} + disabled={creating || connState !== 'connected'} + onPress={() => void handleCreateTerminal()} + accessibilityLabel="New terminal" + > + + + + + )} + + + {showLoadingState ? ( + + + + ) : showEmptyState ? ( + + No terminals in this session + {createError ? {createError} : null} + void handleCreateTerminal()} + > + + {creating ? 'Creating…' : 'Create Terminal'} + + + + ) : ( + { + terminalFrameHeightRef.current = e.nativeEvent.layout.height + }} + > + {terminals.map((terminal) => ( + + ))} + + )} + + {/* Accessory keys */} + + + [ + styles.accessoryKey, + pressed && styles.accessoryKeyPressed, + !canSend && styles.accessoryKeyDisabled + ]} + disabled={!canSend} + onPress={() => { + if (activeHandle) { + void toggleDisplayMode(activeHandle) + } + }} + accessibilityLabel={ + isPhoneMode(activeHandle) ? 'Switch to desktop mode' : 'Switch to phone mode' + } + > + {isPhoneMode(activeHandle) ? ( + + ) : ( + + )} + + {ACCESSORY_KEYS.map((key) => ( + [ + styles.accessoryKey, + pressed && styles.accessoryKeyPressed, + !canSend && styles.accessoryKeyDisabled + ]} + disabled={!canSend} + onPress={() => void handleAccessoryKey(key.bytes)} + accessibilityLabel={key.accessibilityLabel ?? `Send ${key.label}`} + > + + {key.label} + + + ))} + {customKeys.map((key) => ( + [ + styles.accessoryKey, + styles.customAccessoryKey, + pressed && styles.accessoryKeyPressed, + !canSend && styles.accessoryKeyDisabled + ]} + disabled={!canSend} + onPress={() => void handleAccessoryKey(key.bytes)} + onLongPress={() => { + triggerMediumImpact() + setDeleteKeyTarget(key) + }} + delayLongPress={400} + accessibilityLabel={`Send ${key.label}`} + > + + {key.label} + + + ))} + [styles.accessoryKey, pressed && styles.accessoryKeyPressed]} + onPress={() => setShowCustomKeyModal(true)} + accessibilityLabel="Add custom shortcut" + > + + + + + + {/* Input bar */} + + void handleSend()} + /> + void handleSend()} + accessibilityLabel="Send command" + > + + + + + + { + const target = actionTarget + setActionTarget(null) + if (target) { + void toggleDisplayMode(target.handle) + } + } + } + ] + : []), + { + label: 'Rename', + onPress: () => { + const target = actionTarget + setActionTarget(null) + if (target) { + setRenameTarget(target) + } + } + }, + { + label: 'Close', + destructive: true, + onPress: () => { + const target = actionTarget + setActionTarget(null) + if (target) { + void handleCloseTerminal(target) + } + } + } + ]} + onClose={() => setActionTarget(null)} + /> + void handleRenameTerminal(value)} + onCancel={() => setRenameTarget(null)} + /> + setShowCustomKeyModal(false)} + onKeysChanged={setCustomKeys} + /> + { + if (deleteKeyTarget) { + void handleDeleteCustomKey(deleteKeyTarget) + } + setDeleteKeyTarget(null) + } + } + ]} + onClose={() => setDeleteKeyTarget(null)} + /> + + ) +} + +const styles = StyleSheet.create({ + container: { + flex: 1, + backgroundColor: colors.bgBase + }, + kavInner: { + flex: 1 + }, + sessionChrome: { + backgroundColor: colors.bgPanel, + borderBottomWidth: 1, + borderBottomColor: colors.borderSubtle + }, + sessionTopBar: { + minHeight: 44, + flexDirection: 'row', + alignItems: 'center', + paddingHorizontal: spacing.sm, + paddingVertical: spacing.xs + }, + backButton: { + width: 36, + height: 36, + borderRadius: 18, + alignItems: 'center', + justifyContent: 'center', + marginRight: spacing.xs + }, + backButtonPressed: { + backgroundColor: colors.bgRaised + }, + sessionTitleBlock: { + flex: 1, + minWidth: 0 + }, + sessionTitle: { + color: colors.textPrimary, + fontSize: 14, + fontWeight: '600' + }, + sessionMetaRow: { + flexDirection: 'row', + alignItems: 'center', + marginTop: 2 + }, + sessionMetaText: { + flexShrink: 1, + color: colors.textSecondary, + fontSize: typography.metaSize + }, + tabBar: { + flexDirection: 'row', + alignItems: 'center', + borderTopWidth: 1, + borderTopColor: colors.borderSubtle + }, + tabScroll: { + flex: 1, + maxHeight: 36 + }, + tabContent: { + paddingLeft: spacing.sm, + paddingRight: spacing.sm + }, + tab: { + width: 128, + maxWidth: 128, + minHeight: 36, + alignItems: 'center', + justifyContent: 'center', + paddingHorizontal: spacing.sm, + paddingVertical: spacing.sm, + borderBottomWidth: 2, + borderBottomColor: 'transparent' + }, + tabActive: { + borderBottomColor: colors.accentBlue + }, + tabText: { + maxWidth: '100%', + color: colors.textSecondary, + fontSize: 13 + }, + tabTextActive: { + color: colors.textPrimary + }, + newTerminalButton: { + width: 40, + height: 36, + alignItems: 'center', + justifyContent: 'center', + borderBottomWidth: 2, + borderBottomColor: 'transparent' + }, + newTerminalButtonPressed: { + backgroundColor: colors.bgRaised + }, + newTerminalButtonDisabled: { + opacity: 0.45 + }, + terminalFrame: { + flex: 1, + minHeight: 0, + position: 'relative', + overflow: 'hidden' + }, + terminalPane: { + ...StyleSheet.absoluteFillObject + }, + terminalPaneHidden: { + opacity: 0 + }, + terminalWebView: { + flex: 1 + }, + emptyState: { + flex: 1, + alignItems: 'center', + justifyContent: 'center', + padding: spacing.xl + }, + emptyText: { + color: colors.textSecondary, + fontSize: typography.bodySize, + marginBottom: spacing.lg + }, + createError: { + color: colors.statusRed, + fontSize: 13, + marginBottom: spacing.sm + }, + createButton: { + backgroundColor: colors.bgRaised, + borderWidth: 1, + borderColor: colors.borderSubtle, + paddingHorizontal: spacing.xl, + paddingVertical: spacing.sm + 2, + borderRadius: radii.button + }, + createButtonDisabled: { + opacity: 0.5 + }, + createButtonText: { + color: colors.textPrimary, + fontSize: typography.bodySize, + fontWeight: '600' + }, + accessoryBar: { + borderTopWidth: 1, + borderTopColor: colors.borderSubtle, + backgroundColor: colors.bgPanel + }, + accessoryContent: { + paddingHorizontal: spacing.sm, + paddingVertical: spacing.xs, + gap: spacing.xs + }, + accessoryKey: { + backgroundColor: colors.bgRaised, + paddingHorizontal: spacing.sm + 2, + paddingVertical: spacing.xs, + borderRadius: radii.button, + minWidth: 36, + alignItems: 'center' + }, + accessoryKeyPressed: { + backgroundColor: colors.borderSubtle + }, + customAccessoryKey: { + borderWidth: 1, + borderColor: colors.borderSubtle + }, + accessoryKeyDisabled: { + opacity: 0.35 + }, + accessoryKeyText: { + color: colors.textSecondary, + fontSize: 12, + fontFamily: typography.monoFamily + }, + accessoryKeyTextDisabled: { + color: colors.textMuted + }, + inputBar: { + flexDirection: 'row', + alignItems: 'center', + paddingVertical: spacing.xs + 2, + paddingHorizontal: spacing.md, + borderTopWidth: 1, + borderTopColor: colors.borderSubtle, + backgroundColor: colors.bgPanel + }, + textInput: { + flex: 1, + backgroundColor: colors.bgRaised, + color: colors.textPrimary, + borderRadius: radii.input, + paddingHorizontal: spacing.md, + paddingVertical: spacing.sm, + fontSize: 14, + fontFamily: typography.monoFamily, + marginRight: spacing.sm + }, + sendButton: { + backgroundColor: colors.bgRaised, + width: 34, + height: 34, + borderRadius: 17, + alignItems: 'center', + justifyContent: 'center' + }, + sendButtonDisabled: { + opacity: 0.35 + } +}) diff --git a/mobile/app/h/_layout.tsx b/mobile/app/h/_layout.tsx new file mode 100644 index 00000000000..59bbf7eeaf4 --- /dev/null +++ b/mobile/app/h/_layout.tsx @@ -0,0 +1,16 @@ +import { Stack } from 'expo-router' +import { colors } from '../../src/theme/mobile-theme' + +export default function HostGroupLayout() { + return ( + + + + + ) +} diff --git a/mobile/app/index.tsx b/mobile/app/index.tsx new file mode 100644 index 00000000000..046e8ddb01a --- /dev/null +++ b/mobile/app/index.tsx @@ -0,0 +1,947 @@ +import { useState, useCallback, useEffect, useMemo, useRef } from 'react' +import { View, Text, StyleSheet, Pressable, FlatList } from 'react-native' +import { SafeAreaView } from 'react-native-safe-area-context' +import { useRouter, useFocusEffect } from 'expo-router' +import { + Monitor, + QrCode, + Settings, + Bot, + Clock, + GitPullRequest, + ChevronRight, + Terminal, + Plus +} from 'lucide-react-native' +import AsyncStorage from '@react-native-async-storage/async-storage' +import { loadHosts, removeHost, renameHost } from '../src/transport/host-store' +import { connect, type RpcClient } from '../src/transport/rpc-client' +import { subscribeToDesktopNotifications } from '../src/notifications/mobile-notifications' +import type { ConnectionState, HostProfile } from '../src/transport/types' +import { triggerMediumImpact } from '../src/platform/haptics' +import { OrcaLogo } from '../src/components/OrcaLogo' +import { TextInputModal } from '../src/components/TextInputModal' +import { ActionSheetModal } from '../src/components/ActionSheetModal' +import { ConfirmModal } from '../src/components/ConfirmModal' +import { setCachedWorktrees, getCachedWorktrees } from '../src/cache/worktree-cache' +import { colors, spacing, radii } from '../src/theme/mobile-theme' + +function endpointLabel(endpoint: string): string { + try { + const url = new URL(endpoint) + return `${url.hostname}${url.port ? `:${url.port}` : ''}` + } catch { + return endpoint + } +} + +const STATUS_LABELS: Record = { + connected: 'Connected', + connecting: 'Connecting…', + disconnected: 'Disconnected', + reconnecting: 'Reconnecting…', + handshaking: 'Connecting…', + 'auth-failed': 'Auth failed' +} + +type StatsSummary = { + totalAgentsSpawned: number + totalPRsCreated: number + totalAgentTimeMs: number + firstEventAt: number | null +} + +type WorktreeSummary = { + worktreeId: string + repo: string + branch: string + displayName: string + liveTerminalCount: number + status?: 'working' | 'active' | 'permission' | 'done' | 'inactive' +} + +type HostWorktreeInfo = { + hostId: string + totalWorktrees: number + activeCount: number + lastActiveWorktree: WorktreeSummary | null +} + +function formatDuration(ms: number): string { + const totalMinutes = Math.floor(ms / 60_000) + const totalHours = Math.floor(totalMinutes / 60) + const days = Math.floor(totalHours / 24) + const hours = totalHours % 24 + if (days > 0) return `${days}d ${hours}h` + const minutes = totalMinutes % 60 + if (totalHours > 0) return `${totalHours}h ${minutes}m` + return `${totalMinutes}m` +} + +function fetchStats( + client: RpcClient, + setStats: (s: StatsSummary) => void, + disposed: () => boolean +) { + client + .sendRequest('stats.summary') + .then((response) => { + if (disposed()) return + if (response.ok) { + setStats(response.result as StatsSummary) + } + }) + .catch(() => {}) +} + +function fetchWorktreeInfo( + client: RpcClient, + hostId: string, + setInfo: ( + updater: (prev: Record) => Record + ) => void, + disposed: () => boolean +) { + const markLoaded = () => { + setInfo((prev) => ({ + ...prev, + [hostId]: { + hostId, + totalWorktrees: 0, + activeCount: 0, + lastActiveWorktree: null + } + })) + } + + client + .sendRequest('worktree.ps') + .then((response) => { + if (disposed()) return + if (response.ok) { + const result = response.result as { worktrees: WorktreeSummary[] } + const worktrees = result.worktrees ?? [] + setCachedWorktrees(hostId, worktrees) + const activeStatuses = new Set(['working', 'active', 'permission']) + const active = worktrees.filter((w) => w.status && activeStatuses.has(w.status)) + const lastActive = active.length > 0 ? active[0] : (worktrees[0] ?? null) + setInfo((prev) => ({ + ...prev, + [hostId]: { + hostId, + totalWorktrees: worktrees.length, + activeCount: active.length, + lastActiveWorktree: lastActive + } + })) + } else { + markLoaded() + } + }) + .catch(() => { + if (!disposed()) markLoaded() + }) +} + +// Why: repo names get a stable color derived from hashing, matching the +// host detail page's colored dots for visual consistency. +const REPO_COLORS = ['#8b5cf6', '#3b82f6', '#22c55e', '#f59e0b', '#ef4444', '#ec4899', '#06b6d4'] +function repoColor(name: string): string { + let hash = 0 + for (let i = 0; i < name.length; i++) { + hash = (hash * 31 + name.charCodeAt(i)) | 0 + } + return REPO_COLORS[Math.abs(hash) % REPO_COLORS.length] +} + +export default function HomeScreen() { + const router = useRouter() + const [hosts, setHosts] = useState([]) + const [actionTarget, setActionTarget] = useState(null) + const [renameTarget, setRenameTarget] = useState(null) + const [confirmRemove, setConfirmRemove] = useState(null) + const [hostStates, setHostStates] = useState>({}) + const [stats, setStats] = useState(null) + const [worktreeInfo, setWorktreeInfo] = useState>({}) + const [lastVisited, setLastVisited] = useState<{ hostId: string; worktreeId: string } | null>( + null + ) + const clientsRef = useRef>([]) + + useFocusEffect( + useCallback(() => { + let stale = false + void loadHosts().then((h) => { + if (!stale) setHosts(h) + }) + void AsyncStorage.getItem('orca:last-visited-worktree').then((raw) => { + if (stale || !raw) return + try { + setLastVisited(JSON.parse(raw)) + } catch {} + }) + for (const entry of clientsRef.current) { + if (entry.client.getState() === 'connected') { + fetchStats(entry.client, setStats, () => stale) + fetchWorktreeInfo(entry.client, entry.hostId, setWorktreeInfo, () => stale) + } + } + return () => { + stale = true + } + }, []) + ) + + const sortedHosts = useMemo( + () => [...hosts].sort((a, b) => b.lastConnected - a.lastConnected), + [hosts] + ) + + useEffect(() => { + let disposed = false + const notifCleanups: Array<() => void> = [] + const entries = hosts.flatMap((host) => { + if (!host.publicKeyB64 || !host.deviceToken) { + setHostStates((prev) => ({ ...prev, [host.id]: 'auth-failed' })) + return [] + } + setHostStates((prev) => ({ + ...prev, + [host.id]: prev[host.id] ?? 'connecting' + })) + let client: ReturnType + try { + client = connect(host.endpoint, host.deviceToken, host.publicKeyB64, (state) => { + if (disposed) return + setHostStates((prev) => ({ ...prev, [host.id]: state })) + }) + } catch { + setHostStates((prev) => ({ ...prev, [host.id]: 'auth-failed' })) + return [] + } + + let unsubNotif: (() => void) | null = null + let statsFetched = false + const unsubState = client.onStateChange((state) => { + if (state === 'connected') { + if (!unsubNotif) { + unsubNotif = subscribeToDesktopNotifications(client) + } + if (!statsFetched) { + statsFetched = true + fetchStats(client, setStats, () => disposed) + fetchWorktreeInfo(client, host.id, setWorktreeInfo, () => disposed) + } + } else if (unsubNotif) { + unsubNotif() + unsubNotif = null + } + }) + notifCleanups.push(() => { + unsubState() + unsubNotif?.() + }) + + return [{ hostId: host.id, client }] + }) + + clientsRef.current = entries + + return () => { + disposed = true + clientsRef.current = [] + for (const cleanup of notifCleanups) cleanup() + for (const entry of entries) entry.client.close() + } + }, [hosts]) + + // Why: prefer the worktree the user last opened on this device so the + // "Resume" card reflects their mobile session history, not just the + // desktop's most-recently-outputting worktree. + const resumeWorktree = useMemo(() => { + if (lastVisited && hostStates[lastVisited.hostId] === 'connected') { + const cached = getCachedWorktrees(lastVisited.hostId) as WorktreeSummary[] | null + const match = cached?.find((w) => w.worktreeId === lastVisited.worktreeId) + if (match) return { hostId: lastVisited.hostId, worktree: match } + } + for (const host of sortedHosts) { + if (hostStates[host.id] !== 'connected') continue + const info = worktreeInfo[host.id] + if (info?.lastActiveWorktree) { + return { hostId: host.id, worktree: info.lastActiveWorktree } + } + } + return null + }, [sortedHosts, hostStates, worktreeInfo, lastVisited]) + + const resumeLoading = useMemo( + () => + sortedHosts.some((host) => { + const state = hostStates[host.id] ?? 'connecting' + return ( + state === 'connecting' || + state === 'handshaking' || + state === 'reconnecting' || + (state === 'connected' && !worktreeInfo[host.id]) + ) + }), + [sortedHosts, hostStates, worktreeInfo] + ) + + async function handleRename(newName: string) { + if (!renameTarget) return + try { + await renameHost(renameTarget.id, newName) + setRenameTarget(null) + setHosts(await loadHosts()) + } catch { + setRenameTarget(null) + } + } + + async function handleRemove() { + if (!confirmRemove) return + try { + await removeHost(confirmRemove.id) + setConfirmRemove(null) + setHosts(await loadHosts()) + } catch { + setConfirmRemove(null) + } + } + + return ( + + {/* ─── Top bar ─── */} + + + + + + Orca + + [styles.iconButton, pressed && styles.iconButtonPressed]} + onPress={() => router.push('/settings')} + > + + + + + {hosts.length === 0 ? ( + /* ─── Empty state: onboarding ─── */ + + + Connect your desktop + + Pair with Orca on your computer to monitor worktrees, watch agents work, and manage + terminals — all from your phone. + + router.push('/pair-scan')}> + + Scan Pairing Code + + + + + How it works + {ONBOARDING_STEPS.map((step, i) => ( + 0 && styles.stepRowBorder]}> + + {i + 1} + + + {step.title} + {step.desc} + + + ))} + + + ) : ( + /* ─── Populated state ─── */ + h.id} + contentContainerStyle={styles.list} + ListHeaderComponent={ + + + Welcome back + + + {stats && ( + + + + + + + {stats.totalAgentsSpawned.toLocaleString()} + + Agents spawned + + + + + + {formatDuration(stats.totalAgentTimeMs)} + Agent time + + + + + + {stats.totalPRsCreated.toLocaleString()} + PRs created + + + )} + + Desktops + + } + ItemSeparatorComponent={CardGap} + renderItem={({ item }) => { + const state = hostStates[item.id] ?? 'connecting' + const connected = state === 'connected' + const info = worktreeInfo[item.id] + return ( + [styles.hostCard, pressed && styles.hostCardPressed]} + onPress={() => router.push(`/h/${item.id}`)} + onLongPress={() => { + triggerMediumImpact() + setActionTarget(item) + }} + delayLongPress={400} + > + + + + + + {item.name} + + + + + {STATUS_LABELS[state]} + {connected && info + ? ` · ${info.totalWorktrees} worktree${info.totalWorktrees !== 1 ? 's' : ''}${info.activeCount > 0 ? ` · ${info.activeCount} active` : ''}` + : ''} + + + + + + ) + }} + ListFooterComponent={ + + {/* ─── Resume card ─── */} + {resumeWorktree ? ( + <> + Resume + [styles.resumeCard, pressed && styles.hostCardPressed]} + onPress={() => + router.push( + `/h/${resumeWorktree.hostId}/session/${encodeURIComponent(resumeWorktree.worktree.worktreeId)}` + ) + } + > + + + + + + {resumeWorktree.worktree.displayName} + + + + + {resumeWorktree.worktree.repo} + {' · '} + {resumeWorktree.worktree.branch} + + + + + + + ) : hosts.length > 0 && resumeLoading ? ( + <> + Resume + + + + + + + + + ) : null} + + {/* ─── Quick actions ─── */} + Quick Actions + + [styles.quickAction, pressed && styles.hostCardPressed]} + onPress={() => router.push('/pair-scan')} + > + + + + Pair Desktop + + [styles.quickAction, pressed && styles.hostCardPressed]} + onPress={() => { + const connectedHost = sortedHosts.find((h) => hostStates[h.id] === 'connected') + if (connectedHost) { + router.push(`/h/${connectedHost.id}?action=newWorktree`) + } + }} + > + + + + New Worktree + + + + } + /> + )} + + {/* ─── Action sheets (shared by both states) ─── */} + { + const host = actionTarget + setActionTarget(null) + if (host) setRenameTarget(host) + } + }, + { + label: 'Remove', + destructive: true, + onPress: () => { + const host = actionTarget + setActionTarget(null) + if (host) setConfirmRemove(host) + } + } + ]} + onClose={() => setActionTarget(null)} + /> + + void handleRename(name)} + onCancel={() => setRenameTarget(null)} + /> + + void handleRemove()} + onCancel={() => setConfirmRemove(null)} + /> + + ) +} + +function CardGap() { + return +} + +const ONBOARDING_STEPS = [ + { + title: 'Open Orca desktop', + desc: 'Go to Settings → Mobile and generate a pairing QR code.' + }, + { + title: 'Scan the code', + desc: 'Tap the button above to open the scanner. Point at the QR code on your screen.' + }, + { + title: "You're connected", + desc: 'Your desktop will appear here. Everything is encrypted end-to-end.' + } +] + +const styles = StyleSheet.create({ + container: { + flex: 1, + backgroundColor: colors.bgBase + }, + + /* ─── Top bar ─── */ + topBar: { + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'space-between', + paddingHorizontal: spacing.lg, + paddingTop: spacing.sm, + paddingBottom: spacing.md + }, + brandLockup: { + flexDirection: 'row', + alignItems: 'center', + minWidth: 0 + }, + logoMark: { + marginRight: spacing.sm + }, + brandName: { + color: colors.textPrimary, + fontSize: 17, + fontWeight: '700' + }, + iconButton: { + width: 36, + height: 36, + borderRadius: 18, + alignItems: 'center', + justifyContent: 'center' + }, + iconButtonPressed: { + backgroundColor: colors.bgRaised + }, + + /* ─── Hero / greeting ─── */ + hero: { + paddingTop: spacing.md, + paddingBottom: spacing.lg + }, + heroTitle: { + color: colors.textPrimary, + fontSize: 26, + fontWeight: '800', + letterSpacing: -0.3 + }, + + /* ─── Stat cards ─── */ + statsRow: { + flexDirection: 'row', + gap: 10, + marginBottom: spacing.xl + }, + statCard: { + flex: 1, + backgroundColor: 'rgba(26,26,26,0.6)', + borderWidth: 1, + borderColor: colors.borderSubtle, + borderRadius: 10, + padding: spacing.md + }, + statIcon: { + width: 30, + height: 30, + borderRadius: 7, + backgroundColor: 'rgba(255,255,255,0.04)', + alignItems: 'center', + justifyContent: 'center', + marginBottom: 10 + }, + statValue: { + color: colors.textPrimary, + fontSize: 20, + fontWeight: '700', + letterSpacing: -0.3 + }, + statLabel: { + color: colors.textMuted, + fontSize: 11, + fontWeight: '500', + marginTop: 3 + }, + + /* ─── Section heading ─── */ + sectionHeading: { + fontSize: 11, + fontWeight: '600', + color: colors.textMuted, + textTransform: 'uppercase', + letterSpacing: 0.6, + marginBottom: spacing.sm, + paddingHorizontal: spacing.xs + }, + + /* ─── List ─── */ + list: { + paddingHorizontal: spacing.lg, + paddingBottom: spacing.xl + }, + cardGap: { + height: spacing.sm + }, + + /* ─── Host cards ─── */ + hostCard: { + flexDirection: 'row', + alignItems: 'center', + paddingLeft: spacing.md, + paddingRight: spacing.md, + paddingVertical: 14, + borderRadius: radii.card, + backgroundColor: colors.bgPanel, + borderWidth: 1, + borderColor: colors.borderSubtle + }, + hostCardPressed: { + backgroundColor: colors.bgRaised + }, + hostIcon: { + width: 46, + height: 46, + borderRadius: 13, + alignItems: 'center', + justifyContent: 'center', + backgroundColor: colors.bgRaised, + marginRight: 14, + position: 'relative' + }, + hostMain: { + flex: 1, + minWidth: 0, + marginRight: spacing.sm + }, + hostName: { + color: colors.textPrimary, + fontSize: 15, + fontWeight: '600', + lineHeight: 20 + }, + hostMeta: { + flexDirection: 'row', + alignItems: 'center', + gap: 6, + marginTop: 3 + }, + hostMetaItem: { + fontSize: 12, + color: colors.textSecondary + }, + hostMetaDot: { + width: 3, + height: 3, + borderRadius: 1.5, + backgroundColor: colors.textMuted, + marginHorizontal: 8 + }, + statusDot: { + width: 7, + height: 7, + borderRadius: 3.5 + }, + + /* ─── Resume card ─── */ + resumeCard: { + flexDirection: 'row', + alignItems: 'center', + backgroundColor: colors.bgPanel, + borderWidth: 1, + borderColor: colors.borderSubtle, + borderRadius: radii.card, + paddingLeft: spacing.md, + paddingRight: spacing.md, + paddingVertical: 14 + }, + resumeIcon: { + width: 46, + height: 46, + borderRadius: 13, + backgroundColor: colors.bgRaised, + alignItems: 'center', + justifyContent: 'center', + marginRight: 14 + }, + resumeMain: { + flex: 1, + minWidth: 0 + }, + resumeTitle: { + fontSize: 13, + fontWeight: '600', + color: colors.textPrimary + }, + resumeSub: { + flexDirection: 'row', + alignItems: 'center', + gap: 6, + marginTop: 3 + }, + repoDot: { + width: 7, + height: 7, + borderRadius: 3.5 + }, + resumeSubText: { + fontSize: 12, + color: colors.textSecondary, + flex: 1 + }, + + /* ─── Skeleton ─── */ + skeletonBlock: { + backgroundColor: colors.bgRaised, + opacity: 0.5 + }, + skeletonLine: { + height: 12, + borderRadius: 4, + backgroundColor: colors.bgRaised, + opacity: 0.5 + }, + + /* ─── Quick actions ─── */ + quickActions: { + flexDirection: 'row', + gap: spacing.sm + }, + quickAction: { + flex: 1, + backgroundColor: colors.bgPanel, + borderWidth: 1, + borderColor: colors.borderSubtle, + borderRadius: radii.card, + padding: spacing.lg, + alignItems: 'center', + gap: 10 + }, + quickActionIcon: { + width: 40, + height: 40, + borderRadius: 12, + backgroundColor: 'rgba(255,255,255,0.04)', + alignItems: 'center', + justifyContent: 'center' + }, + quickActionLabel: { + fontSize: 12, + fontWeight: '600', + color: colors.textSecondary, + textAlign: 'center' + }, + + /* ─── Empty state ─── */ + emptyContainer: { + flex: 1 + }, + emptyGreeting: { + paddingHorizontal: spacing.lg, + paddingTop: spacing.md, + paddingBottom: spacing.sm + }, + emptyHero: { + flex: 1, + alignItems: 'center', + justifyContent: 'center', + paddingHorizontal: 32, + paddingBottom: 40 + }, + emptyTitle: { + fontSize: 22, + fontWeight: '700', + color: colors.textPrimary, + textAlign: 'center', + marginBottom: 10 + }, + emptyBody: { + fontSize: 15, + color: colors.textSecondary, + textAlign: 'center', + lineHeight: 22, + marginBottom: 32 + }, + primaryButton: { + flexDirection: 'row', + alignItems: 'center', + gap: 10, + backgroundColor: colors.textPrimary, + paddingHorizontal: 28, + paddingVertical: 14, + borderRadius: radii.card + }, + primaryButtonText: { + color: colors.bgBase, + fontSize: 15, + fontWeight: '700' + }, + + /* ─── Onboarding steps ─── */ + stepsSection: { + paddingHorizontal: spacing.xl + }, + stepRow: { + flexDirection: 'row', + alignItems: 'flex-start', + gap: 14, + paddingVertical: spacing.lg + }, + stepRowBorder: { + borderTopWidth: 1, + borderTopColor: colors.borderSubtle + }, + stepNum: { + width: 28, + height: 28, + borderRadius: 8, + backgroundColor: 'rgba(255,255,255,0.04)', + borderWidth: 1, + borderColor: colors.borderSubtle, + alignItems: 'center', + justifyContent: 'center', + marginTop: 1 + }, + stepNumText: { + fontSize: 12, + fontWeight: '700', + color: colors.textSecondary + }, + stepText: { + flex: 1 + }, + stepTitle: { + fontSize: 14, + fontWeight: '600', + color: colors.textPrimary, + marginBottom: 3 + }, + stepDesc: { + fontSize: 12, + color: colors.textMuted, + lineHeight: 17 + } +}) diff --git a/mobile/app/notifications.tsx b/mobile/app/notifications.tsx new file mode 100644 index 00000000000..7b0fdf9cd43 --- /dev/null +++ b/mobile/app/notifications.tsx @@ -0,0 +1,109 @@ +import { useState, useCallback } from 'react' +import { View, Text, StyleSheet, Pressable, Switch } from 'react-native' +import { useSafeAreaInsets } from 'react-native-safe-area-context' +import { useRouter, useFocusEffect } from 'expo-router' +import { ChevronLeft } from 'lucide-react-native' +import { colors, spacing, typography } from '../src/theme/mobile-theme' +import { + loadPushNotificationsEnabled, + savePushNotificationsEnabled +} from '../src/storage/preferences' +import { ensureNotificationPermissions } from '../src/notifications/mobile-notifications' + +export default function NotificationsScreen() { + const router = useRouter() + const insets = useSafeAreaInsets() + const [pushEnabled, setPushEnabled] = useState(true) + + useFocusEffect( + useCallback(() => { + void loadPushNotificationsEnabled().then(setPushEnabled) + }, []) + ) + + const togglePush = async (value: boolean) => { + if (value) { + const granted = await ensureNotificationPermissions() + if (!granted) return + } + setPushEnabled(value) + await savePushNotificationsEnabled(value) + } + + return ( + + + router.back()}> + + + Notifications + + + + + Push Notifications + void togglePush(v)} + trackColor={{ false: colors.bgRaised, true: colors.textSecondary }} + thumbColor={colors.textPrimary} + /> + + + Receive notifications when an agent task completes on your desktop. + + + + ) +} + +const styles = StyleSheet.create({ + container: { + flex: 1, + backgroundColor: colors.bgBase, + padding: spacing.lg + }, + topRow: { + flexDirection: 'row', + alignItems: 'center', + marginBottom: spacing.xl + }, + backButton: { + width: 36, + height: 36, + borderRadius: 18, + alignItems: 'center', + justifyContent: 'center', + marginRight: spacing.sm + }, + heading: { + fontSize: 20, + fontWeight: '700', + color: colors.textPrimary + }, + section: { + backgroundColor: colors.bgPanel, + borderRadius: 12, + overflow: 'hidden' + }, + row: { + flexDirection: 'row', + alignItems: 'center', + gap: spacing.sm + 2, + paddingVertical: spacing.md, + paddingHorizontal: spacing.md + 2 + }, + rowLabel: { + flex: 1, + fontSize: typography.bodySize, + fontWeight: '500', + color: colors.textPrimary + }, + hint: { + fontSize: typography.metaSize, + color: colors.textMuted, + lineHeight: 18, + paddingHorizontal: spacing.md + 2, + paddingBottom: spacing.md + } +}) diff --git a/mobile/app/pair-scan.tsx b/mobile/app/pair-scan.tsx new file mode 100644 index 00000000000..ca522e7ddae --- /dev/null +++ b/mobile/app/pair-scan.tsx @@ -0,0 +1,307 @@ +import { useState, useRef, useCallback } from 'react' +import { View, Text, StyleSheet, Pressable, ActivityIndicator } from 'react-native' +import { useSafeAreaInsets } from 'react-native-safe-area-context' +import { CameraView, useCameraPermissions } from 'expo-camera' +import { useRouter } from 'expo-router' +import { ChevronLeft } from 'lucide-react-native' +import { decodePairingUrl } from '../src/transport/pairing' +import { connect } from '../src/transport/rpc-client' +import { saveHost, getNextHostName } from '../src/transport/host-store' +import type { PairingOffer } from '../src/transport/types' +import { colors, spacing, radii, typography } from '../src/theme/mobile-theme' + +function Step({ number, text }: { number: number; text: string }) { + return ( + + + {number} + + {text} + + ) +} + +export default function PairScanScreen() { + const router = useRouter() + const insets = useSafeAreaInsets() + const [permission, requestPermission] = useCameraPermissions() + const [status, setStatus] = useState<'scanning' | 'connecting' | 'error'>('scanning') + const [errorMessage, setErrorMessage] = useState('') + const processingRef = useRef(false) + + const handleBarCodeScanned = useCallback( + ({ data }: { data: string }) => { + if (processingRef.current) return + processingRef.current = true + + const offer = decodePairingUrl(data) + if (!offer) { + setStatus('error') + setErrorMessage('Not a valid Orca QR code') + processingRef.current = false + return + } + + void testAndSave(offer) + }, + [router] + ) + + async function testAndSave(offer: PairingOffer) { + setStatus('connecting') + let client: ReturnType | null = null + + try { + client = connect(offer.endpoint, offer.deviceToken, offer.publicKeyB64) + const response = await client.sendRequest('status.get') + client.close() + client = null + + if (!response.ok) { + if (response.error.code === 'unauthorized') { + setStatus('error') + setErrorMessage('Authentication failed — token may be expired') + processingRef.current = false + return + } + setStatus('error') + setErrorMessage(`Server error: ${response.error.message}`) + processingRef.current = false + return + } + + const hostId = `host-${Date.now()}` + const hostName = await getNextHostName() + + await saveHost({ + id: hostId, + name: hostName, + endpoint: offer.endpoint, + deviceToken: offer.deviceToken, + publicKeyB64: offer.publicKeyB64, + lastConnected: Date.now() + }) + + router.replace(`/h/${hostId}`) + } catch { + setStatus('error') + setErrorMessage('Cannot connect — check that your computer is on the same network') + processingRef.current = false + } finally { + client?.close() + } + } + + function retry() { + setStatus('scanning') + setErrorMessage('') + processingRef.current = false + } + + const containerPadding = { paddingTop: insets.top + spacing.sm } + + if (!permission) { + return ( + + + + ) + } + + if (!permission.granted) { + return ( + + router.back()}> + + + + Camera Permission + + Orca needs camera access to scan the pairing QR code from your desktop. + + + Grant Camera Access + + + + ) + } + + return ( + + router.back()}> + + + + + + + + + + {status === 'scanning' && ( + + + + + + + + + + )} + + {status === 'connecting' && ( + + + Connecting… + + )} + + {status === 'error' && ( + + {errorMessage} + + Try Again + + + )} + + ) +} + +const styles = StyleSheet.create({ + container: { + flex: 1, + backgroundColor: colors.bgBase, + padding: spacing.lg + }, + backButton: { + width: 36, + height: 36, + borderRadius: 18, + alignItems: 'center', + justifyContent: 'center', + marginBottom: spacing.sm + }, + steps: { + gap: spacing.sm, + marginBottom: spacing.lg, + marginLeft: 7 + }, + step: { + flexDirection: 'row', + alignItems: 'center', + gap: spacing.sm + }, + stepBadge: { + width: 22, + height: 22, + borderRadius: 11, + backgroundColor: colors.bgRaised, + alignItems: 'center', + justifyContent: 'center' + }, + stepNumber: { + fontSize: 12, + fontWeight: '700', + color: colors.textSecondary + }, + stepText: { + fontSize: typography.bodySize, + color: colors.textSecondary + }, + cameraWrap: { + flex: 1, + borderRadius: radii.camera, + overflow: 'hidden' + }, + camera: { + ...StyleSheet.absoluteFillObject + }, + reticle: { + ...StyleSheet.absoluteFillObject, + alignItems: 'center', + justifyContent: 'center' + }, + corner: { + position: 'absolute', + width: 28, + height: 28, + borderColor: 'rgba(255,255,255,0.7)' + }, + cornerTL: { + top: '30%', + left: '20%', + borderTopWidth: 2.5, + borderLeftWidth: 2.5, + borderTopLeftRadius: 6 + }, + cornerTR: { + top: '30%', + right: '20%', + borderTopWidth: 2.5, + borderRightWidth: 2.5, + borderTopRightRadius: 6 + }, + cornerBL: { + bottom: '30%', + left: '20%', + borderBottomWidth: 2.5, + borderLeftWidth: 2.5, + borderBottomLeftRadius: 6 + }, + cornerBR: { + bottom: '30%', + right: '20%', + borderBottomWidth: 2.5, + borderRightWidth: 2.5, + borderBottomRightRadius: 6 + }, + centered: { + flex: 1, + alignItems: 'center', + justifyContent: 'center' + }, + title: { + fontSize: typography.titleSize, + fontWeight: '600', + color: colors.textPrimary, + marginBottom: spacing.sm + }, + subtitle: { + fontSize: typography.bodySize, + color: colors.textSecondary, + textAlign: 'center', + marginBottom: spacing.xl, + lineHeight: 20 + }, + connectingText: { + color: colors.textSecondary, + fontSize: typography.bodySize, + marginTop: spacing.lg + }, + errorText: { + color: colors.statusRed, + fontSize: typography.bodySize, + textAlign: 'center', + marginBottom: spacing.xl, + lineHeight: 20 + }, + primaryButton: { + backgroundColor: colors.textPrimary, + paddingHorizontal: spacing.xl, + paddingVertical: spacing.sm + 2, + borderRadius: radii.button + }, + primaryButtonText: { + color: colors.bgBase, + fontSize: typography.bodySize, + fontWeight: '600' + } +}) diff --git a/mobile/app/settings.tsx b/mobile/app/settings.tsx new file mode 100644 index 00000000000..171bd004853 --- /dev/null +++ b/mobile/app/settings.tsx @@ -0,0 +1,102 @@ +import { View, Text, StyleSheet, Pressable } from 'react-native' +import { useSafeAreaInsets } from 'react-native-safe-area-context' +import { useRouter } from 'expo-router' +import { ChevronLeft, ChevronRight, Info, Bell, Wrench } from 'lucide-react-native' +import { colors, spacing, typography } from '../src/theme/mobile-theme' + +export default function SettingsScreen() { + const router = useRouter() + const insets = useSafeAreaInsets() + + return ( + + + router.back()}> + + + Settings + + + + [styles.row, pressed && styles.rowPressed]} + onPress={() => router.push('/notifications')} + > + + Notifications + + + + [styles.row, pressed && styles.rowPressed]} + onPress={() => router.push('/troubleshoot')} + > + + Troubleshooting + + + + [styles.row, pressed && styles.rowPressed]} + onPress={() => router.push('/about')} + > + + About + + + + + ) +} + +const styles = StyleSheet.create({ + container: { + flex: 1, + backgroundColor: colors.bgBase, + padding: spacing.lg + }, + topRow: { + flexDirection: 'row', + alignItems: 'center', + marginBottom: spacing.xl + }, + backButton: { + width: 36, + height: 36, + borderRadius: 18, + alignItems: 'center', + justifyContent: 'center', + marginRight: spacing.sm + }, + heading: { + fontSize: 20, + fontWeight: '700', + color: colors.textPrimary + }, + section: { + backgroundColor: colors.bgPanel, + borderRadius: 12, + overflow: 'hidden' + }, + row: { + flexDirection: 'row', + alignItems: 'center', + gap: spacing.sm + 2, + paddingVertical: spacing.md, + paddingHorizontal: spacing.md + 2 + }, + rowPressed: { + backgroundColor: colors.bgRaised + }, + rowLabel: { + flex: 1, + fontSize: typography.bodySize, + fontWeight: '500', + color: colors.textPrimary + }, + separator: { + height: StyleSheet.hairlineWidth, + backgroundColor: colors.borderSubtle, + marginHorizontal: spacing.md + } +}) diff --git a/mobile/app/troubleshoot.tsx b/mobile/app/troubleshoot.tsx new file mode 100644 index 00000000000..be800b74c31 --- /dev/null +++ b/mobile/app/troubleshoot.tsx @@ -0,0 +1,455 @@ +import { useState, useCallback, useEffect, useRef } from 'react' +import { + View, + Text, + StyleSheet, + Pressable, + ScrollView, + ActivityIndicator, + Platform +} from 'react-native' +import { useSafeAreaInsets } from 'react-native-safe-area-context' +import { useRouter } from 'expo-router' +import { + ChevronLeft, + ChevronDown, + ChevronUp, + WifiOff, + Shield, + Monitor, + Clock, + Globe, + Activity, + CheckCircle2, + XCircle, + AlertTriangle +} from 'lucide-react-native' +import { colors, spacing, typography } from '../src/theme/mobile-theme' +import { loadHosts } from '../src/transport/host-store' + +type DiagnosticStatus = 'idle' | 'running' | 'done' + +type CheckResult = { + label: string + status: 'pass' | 'fail' | 'warn' + detail: string +} + +type TroubleshootSection = { + id: string + icon: React.ReactNode + title: string + steps: string[] +} + +const sections: TroubleshootSection[] = [ + { + id: 'wifi', + icon: , + title: 'Different WiFi Networks', + steps: [ + 'Both devices must be on the same local network.', + 'Ethernet and WiFi must share the same subnet.', + 'Try reconnecting WiFi on both devices.' + ] + }, + { + id: 'firewall', + icon: , + title: 'Firewall Blocking Port 6768', + steps: [ + 'macOS: System Settings → Network → Firewall — allow Orca.', + 'Windows: Defender Firewall → Allow app — enable Orca for Private networks.', + 'Linux: sudo ufw allow 6768', + 'Corporate/school networks may block P2P — try a personal hotspot.' + ] + }, + { + id: 'desktop', + icon: , + title: 'Desktop App Not Running', + steps: [ + 'Orca must be open on your desktop to accept connections.', + 'Try restarting Orca — the companion server starts on launch.', + 'After an update, you may need to re-pair via QR code.' + ] + }, + { + id: 'timeout', + icon: , + title: 'Connection Timeout', + steps: [ + 'Check WiFi signal strength on your phone.', + 'Go back to the host list and tap your host to retry.', + 'Restart both apps if timeouts persist.' + ] + }, + { + id: 'vpn', + icon: , + title: 'VPN Interference', + steps: [ + 'VPNs can route local traffic through a remote server.', + 'Disable the VPN or enable split tunneling / "Allow LAN".' + ] + } +] + +function StatusIcon({ status }: { status: CheckResult['status'] }) { + switch (status) { + case 'pass': + return + case 'fail': + return + case 'warn': + return + } +} + +export default function TroubleshootScreen() { + const router = useRouter() + const insets = useSafeAreaInsets() + const [expandedId, setExpandedId] = useState(null) + const [diagnosticStatus, setDiagnosticStatus] = useState('idle') + const [checks, setChecks] = useState([]) + const abortRef = useRef(false) + + useEffect(() => { + return () => { + abortRef.current = true + } + }, []) + + const toggleSection = useCallback((id: string) => { + setExpandedId((prev) => (prev === id ? null : id)) + }, []) + + const runDiagnostics = useCallback(async () => { + abortRef.current = false + setDiagnosticStatus('running') + setChecks([]) + + const results: CheckResult[] = [] + + try { + const hosts = await loadHosts() + results.push( + hosts.length > 0 + ? { label: 'Paired hosts', status: 'pass', detail: `${hosts.length} paired` } + : { label: 'Paired hosts', status: 'fail', detail: 'None — scan a QR to pair' } + ) + } catch { + results.push({ label: 'Paired hosts', status: 'warn', detail: 'Could not read host data' }) + } + + if (abortRef.current) return + setChecks([...results]) + + try { + const controller = new AbortController() + const timeout = setTimeout(() => controller.abort(), 5000) + const resp = await fetch('https://dns.google/resolve?name=example.com&type=A', { + signal: controller.signal + }) + clearTimeout(timeout) + results.push( + resp.ok + ? { label: 'Internet', status: 'pass', detail: 'Connected' } + : { label: 'Internet', status: 'warn', detail: 'Unexpected response' } + ) + } catch { + results.push({ label: 'Internet', status: 'fail', detail: 'No connection' }) + } + + if (abortRef.current) return + setChecks([...results]) + + try { + const hosts = await loadHosts() + for (const host of hosts) { + if (abortRef.current) return + const reachable = await testHostReachability(host.endpoint) + results.push({ + label: host.name, + status: reachable ? 'pass' : 'fail', + detail: reachable + ? `Reachable at ${formatEndpoint(host.endpoint)}` + : `Cannot reach ${formatEndpoint(host.endpoint)}` + }) + setChecks([...results]) + } + } catch { + results.push({ label: 'Hosts', status: 'warn', detail: 'Could not test' }) + } + + if (abortRef.current) return + + results.push({ + label: 'Platform', + status: 'pass', + detail: `${Platform.OS} ${Platform.Version ?? ''}` + }) + + setChecks([...results]) + setDiagnosticStatus('done') + }, []) + + return ( + + + router.back()}> + + + Troubleshooting + + + + [ + styles.diagnosticButton, + pressed && styles.diagnosticButtonPressed, + diagnosticStatus === 'running' && styles.diagnosticButtonDisabled + ]} + onPress={runDiagnostics} + disabled={diagnosticStatus === 'running'} + > + {diagnosticStatus === 'running' ? ( + + ) : ( + + )} + + {diagnosticStatus === 'running' + ? 'Running…' + : diagnosticStatus === 'done' + ? 'Run again' + : 'Run diagnostics'} + + + + {checks.length > 0 && ( + + {checks.map((check, i) => ( + + {i > 0 && } + + + {check.label} + + {check.detail} + + + + ))} + + )} + + Common issues + + + {sections.map((section, i) => ( + + {i > 0 && } + [styles.accordionHeader, pressed && styles.rowPressed]} + onPress={() => toggleSection(section.id)} + > + {section.icon} + {section.title} + {expandedId === section.id ? ( + + ) : ( + + )} + + {expandedId === section.id && ( + + {section.steps.map((step, j) => ( + + + {step} + + ))} + + )} + + ))} + + + + + + ) +} + +// Why: WebSocket reachability is tested by opening a connection and waiting for +// the server to respond with any frame, or timing out after 4 seconds. +// We don't complete the E2EE handshake — just verify the endpoint is listening. +async function testHostReachability(endpoint: string): Promise { + return new Promise((resolve) => { + const timeout = setTimeout(() => { + ws.close() + resolve(false) + }, 4000) + + const ws = new WebSocket(endpoint) + + ws.onopen = () => { + clearTimeout(timeout) + ws.close() + resolve(true) + } + + ws.onerror = () => { + clearTimeout(timeout) + resolve(false) + } + }) +} + +function formatEndpoint(endpoint: string): string { + try { + const url = new URL(endpoint) + return url.host + } catch { + return endpoint + } +} + +const styles = StyleSheet.create({ + container: { + flex: 1, + backgroundColor: colors.bgBase, + padding: spacing.lg + }, + topRow: { + flexDirection: 'row', + alignItems: 'center', + marginBottom: spacing.lg + }, + backButton: { + width: 36, + height: 36, + borderRadius: 18, + alignItems: 'center', + justifyContent: 'center', + marginRight: spacing.sm + }, + heading: { + fontSize: 20, + fontWeight: '700', + color: colors.textPrimary + }, + scroll: { + flex: 1 + }, + scrollContent: { + paddingBottom: spacing.xl + }, + diagnosticButton: { + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'center', + gap: spacing.sm, + backgroundColor: colors.bgRaised, + borderRadius: 10, + paddingVertical: spacing.md, + paddingHorizontal: spacing.lg, + marginBottom: spacing.lg + }, + diagnosticButtonPressed: { + opacity: 0.7 + }, + diagnosticButtonDisabled: { + opacity: 0.5 + }, + diagnosticButtonLabel: { + fontSize: typography.bodySize, + fontWeight: '600', + color: colors.textPrimary + }, + checkRow: { + flexDirection: 'row', + alignItems: 'center', + gap: spacing.sm, + paddingVertical: spacing.sm + 2, + paddingHorizontal: spacing.md + 2 + }, + checkLabel: { + fontSize: typography.bodySize, + fontWeight: '500', + color: colors.textPrimary + }, + checkDetail: { + flex: 1, + textAlign: 'right', + fontSize: typography.metaSize, + color: colors.textMuted + }, + checkDetailFail: { + color: colors.statusRed + }, + sectionHeading: { + fontSize: typography.metaSize, + fontWeight: '600', + color: colors.textMuted, + textTransform: 'uppercase', + letterSpacing: 0.5, + marginBottom: spacing.sm, + marginTop: spacing.sm, + paddingHorizontal: spacing.xs + }, + section: { + backgroundColor: colors.bgPanel, + borderRadius: 12, + overflow: 'hidden', + marginBottom: spacing.lg + }, + separator: { + height: StyleSheet.hairlineWidth, + backgroundColor: colors.borderSubtle, + marginHorizontal: spacing.md + }, + rowPressed: { + backgroundColor: colors.bgRaised + }, + accordionHeader: { + flexDirection: 'row', + alignItems: 'center', + gap: spacing.sm + 2, + paddingVertical: spacing.md, + paddingHorizontal: spacing.md + 2 + }, + accordionTitle: { + flex: 1, + fontSize: typography.bodySize, + fontWeight: '500', + color: colors.textPrimary + }, + accordionBody: { + paddingHorizontal: spacing.md + 2, + paddingBottom: spacing.md, + gap: spacing.xs + 2 + }, + stepRow: { + flexDirection: 'row', + gap: spacing.sm + }, + bullet: { + fontSize: typography.metaSize, + color: colors.textMuted, + lineHeight: 18 + }, + stepText: { + flex: 1, + fontSize: typography.metaSize, + color: colors.textMuted, + lineHeight: 18 + } +}) diff --git a/mobile/assets/adaptive-icon.png b/mobile/assets/adaptive-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..0e8fe0ed19d5138ebaa1edb262d5d40b991d67e5 GIT binary patch literal 40835 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 UzQp>a0Zn7@boFyt=akR{0OIl`G5`Po literal 0 HcmV?d00001 diff --git a/mobile/assets/icon.png b/mobile/assets/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..d056b1f01e9f4f0b952123f0da1dffcd45e0f3a6 GIT binary patch literal 719234 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 z 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¥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 @T$cpYP`m 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 mNl_vua;PEqW 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 z z!oPa$wZk|6`JWFjz4)@m>lex~^TRh@>d67#=H38+=rNvr>4{LTy?Dxa%-IgdPxP^u zsh>Y#!e_OI&ypu<0z@fS58mbf>}NmgIWNS>!ySiT`ITR>$GqW&zrO5Sm^5&fK(f|JCf2MyvKA+_1@BMf5EQS8u zqh9d&{OAAt@ZyU<^$Q)Xf3N#NOSvICW`dQ z$VceQMv=Q=y^11DXL-6wzK%0{yH+ooq=;4XZ0X93+bEfR(S4s zpVPT+YlD-aPb${_o6#|+jlo5RCqti9oQb;6iS@fgI~n?<;v_hHjyYXtJsm}X&-HOy zLtQ7I-O#(jb-10;evWnw&k~&seNwSP^Iz`ZTvm;H(OlyYw47XtZIu*fBWyUA<$QKs zXN!vX|IY5pegYsDwoU>GS7Pg8?U6R~GGr+vt3?~SR%l^x;5xI|l+wm1G4ZAYv!sbL ziUFTA|4Bk%eese^mCzS*e+-R3a}z{6X0*udu&E>qb_Act~v+)>Ab<974=!M63Ac>gx$MeiKUYR4%&IPu=Yuoq{)ODaK_Iz!gm zs$!F4H|Hl@$rVx_wsj|VojFpHcR)Hvfw6}$1D8CF^r_=|5|o}UCaj`32J!4513CSuKjdxVv;3-CMK@NK7iebNKYK&DqZ8#akWQA8a*~xYPn9KBX z%r#Z#Y;fwiG$MQ|3sa7k+=aQfq2gQ)Z^?)=&Pk-rc14t-@$q2I5c6pe^Z)=r07*na zR78iIn?Z}>gm>j`=@dH!Q4KW*Fe^lGH8v6aqFc@b=rWJyz%?$B<96Gvw;z79ItVx62&RNS?|i%v~AXF+Q*z@*81jr zPe7THfBZ8{N3ps2|JC{vf0btG=;ekQeYe$(zl6l*9dup{NN%$4xe<;z1W(%v0RLp7 zHAM`u8Y^fw^>IO)d1g;Px+b!^Pe8R`OSa! z-1e}pOXkVsSTGmOZmb*J0M&+yHK7pJ$Qrb6tn08%GGl`;@9vLUTHcSsjJYQ+Fftwm zQJjngGRCpVE?(Xa0BdZDWjPo#>nA-XeGVv27L~p0yZK8opD%4U_~hk1WZeu4mud+^ zjc1M>*J{PS`J5?Bv79TFnUOzbRyoR2P<_f_C!2gyC;ET$Ia7))_bKFz941jWpEE7F z=<~{W&ZHELV_RlqEN8Ift_zX#hR@IB6dxBIHYrrL?zNl7SFK&y7XX|&5~~byp!*`q z(D_Yjb8^6>{*7m0N^_m4aj@1aQR$~IoLf1G(3P##Z0c_hW9d+JG9d~QWHf&|p%#8| z25YhKMPF`1WJjYK!7-81G`K~vTU!$?#j=ZOTjk^zndaa*qtnCve3mktDjIx37`HSN zjIHykvWw=yM+%5;*?Sv4{f0v70<&&rMDrbW)@*_O3ezy>i{Rv_v;%No6(_pa9rQbDMqn7 zzrIQBvi|cim-=VT`iX310NMoWHpp%2TY4jtc5KUT=43f4ET{TyPN(WSE4y$wS>Ny! z|G`(o!KwM61qw1#eQOj>Lm88BqF=|{&`*4j73Lx}V&q|gpvLFdHxZp90|tD_U)4k1 zWMo2t&y9y}b#Upm!m)@�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

d{C1eIB3aE~as;d}HrsJeX`O)K?5`S3G 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%0A=J5Y8H`tt@J-Ht&;D{ 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

gk@X;cPYjX|c1Jlgh<`DWH;%SLWQ36Dm` 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 literal 0 HcmV?d00001 diff --git a/mobile/mock-homepage.html b/mobile/mock-homepage.html new file mode 100644 index 00000000000..65f51a3dc8b --- /dev/null +++ b/mobile/mock-homepage.html @@ -0,0 +1,808 @@ + + + + + +Orca Mobile – Homepage Redesign + + + + +

+ + + +
+ + +
+
+
+ + Orca +
+
+ +
+
Welcome back
+
+ +
+
+ +
294
+
Agents
+
+
+ +
1d 14h
+
Agent time
+
+
+ +
277
+
PRs
+
+
+ +
Desktops
+
+
+
+ +
+
+
+
+
Host 1
+
+
Connected
+
+
+ +
+
+ +
+
+ +
+
+
Pair another desktop
+
Scan a QR code from Orca desktop
+
+
+
+ +
+ +
+ + Settings +
+
+ + +
+ + +
+
+
+ + Orca +
+
+ +
+
+ +
+ +
Welcome to Orca
+
+ +
+
+
+ + + + + + +
+
Connect your desktop
+
+ Pair with Orca on your computer to monitor worktrees, watch agents work, and manage terminals — all from your phone. +
+ +
+ +
+
How it works
+
+
1
+
+
Open Orca desktop
+
Go to Settings → Mobile and generate a pairing QR code.
+
+
+
+
2
+
+
Scan the code
+
Tap the button above to open the scanner. Point at the QR code on your screen.
+
+
+
+
3
+
+
You're connected
+
Your desktop will appear here. Everything is encrypted end-to-end.
+
+
+
+
+
+ + + + diff --git a/mobile/package.json b/mobile/package.json new file mode 100644 index 00000000000..6006461c802 --- /dev/null +++ b/mobile/package.json @@ -0,0 +1,53 @@ +{ + "name": "orca-mobile", + "version": "0.0.1", + "private": true, + "main": "expo-router/entry", + "scripts": { + "start": "expo start", + "android": "expo run:android", + "ios": "expo run:ios", + "lint": "oxlint", + "format": "oxfmt --write .", + "mock-server": "npx tsx scripts/mock-server.ts" + }, + "dependencies": { + "@react-native-async-storage/async-storage": "^2.2.0", + "expo": "^55.0.17", + "expo-build-properties": "^55.0.13", + "expo-camera": "^55.0.16", + "expo-constants": "^55.0.15", + "expo-crypto": "^55.0.14", + "expo-haptics": "^55.0.14", + "expo-linking": "^55.0.14", + "expo-notifications": "^55.0.21", + "expo-router": "^55.0.13", + "expo-splash-screen": "^55.0.19", + "expo-status-bar": "^55.0.5", + "lucide-react-native": "^1.11.0", + "react": "^19.2.0", + "react-native": "^0.83.6", + "react-native-gesture-handler": "^2.30.1", + "react-native-reanimated": "^4.2.1", + "react-native-safe-area-context": "^5.6.2", + "react-native-screens": "^4.23.0", + "react-native-svg": "^15.15.4", + "react-native-web": "^0.21.2", + "react-native-webview": "^13.16.1", + "react-native-worklets": "^0.7.4", + "tweetnacl": "^1.0.3", + "ws": "^8.18.0", + "zod": "^4.3.6", + "zustand": "^5.0.12" + }, + "devDependencies": { + "@types/react": "^19.2.14", + "@types/react-native": "^0.73.0", + "@types/ws": "^8.18.1", + "expo-module-scripts": "^55.0.2", + "oxfmt": "^0.47.0", + "oxlint": "^1.62.0", + "tsx": "^4.19.4", + "typescript": "^5.9.3" + } +} diff --git a/mobile/pnpm-lock.yaml b/mobile/pnpm-lock.yaml new file mode 100644 index 00000000000..11f0e80157f --- /dev/null +++ b/mobile/pnpm-lock.yaml @@ -0,0 +1,12571 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + '@react-native-async-storage/async-storage': + specifier: ^2.2.0 + version: 2.2.0(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0)) + expo: + specifier: ^55.0.17 + version: 55.0.17(@babel/core@7.29.0)(@expo/dom-webview@55.0.5)(@expo/metro-runtime@55.0.10)(expo-router@55.0.13)(react-dom@19.2.5(react@19.2.0))(react-native-webview@13.16.1(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0))(react-native-worklets@0.7.4(@babel/core@7.29.0)(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0))(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0)(typescript@5.9.3) + expo-build-properties: + specifier: ^55.0.13 + version: 55.0.13(expo@55.0.17) + expo-camera: + specifier: ^55.0.16 + version: 55.0.16(@types/emscripten@1.41.5)(expo@55.0.17)(react-native-web@0.21.2(react-dom@19.2.5(react@19.2.0))(react@19.2.0))(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0) + expo-constants: + specifier: ^55.0.15 + version: 55.0.15(expo@55.0.17)(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0)) + expo-crypto: + specifier: ^55.0.14 + version: 55.0.14(expo@55.0.17) + expo-haptics: + specifier: ^55.0.14 + version: 55.0.14(expo@55.0.17) + expo-linking: + specifier: ^55.0.14 + version: 55.0.14(expo@55.0.17)(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0) + expo-notifications: + specifier: ^55.0.21 + version: 55.0.21(expo@55.0.17)(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0)(typescript@5.9.3) + expo-router: + specifier: ^55.0.13 + version: 55.0.13(121105c3b042d5e83ab3c1d3b84ed55f) + expo-splash-screen: + specifier: ^55.0.19 + version: 55.0.19(expo@55.0.17)(typescript@5.9.3) + expo-status-bar: + specifier: ^55.0.5 + version: 55.0.5(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0) + lucide-react-native: + specifier: ^1.11.0 + version: 1.11.0(react-native-svg@15.15.4(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0))(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0) + react: + specifier: ^19.2.0 + version: 19.2.0 + react-native: + specifier: ^0.83.6 + version: 0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0) + react-native-gesture-handler: + specifier: ^2.30.1 + version: 2.30.1(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0) + react-native-reanimated: + specifier: ^4.2.1 + version: 4.2.1(react-native-worklets@0.7.4(@babel/core@7.29.0)(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0))(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0) + react-native-safe-area-context: + specifier: ^5.6.2 + version: 5.6.2(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0) + react-native-screens: + specifier: ^4.23.0 + version: 4.23.0(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0) + react-native-svg: + specifier: ^15.15.4 + version: 15.15.4(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0) + react-native-web: + specifier: ^0.21.2 + version: 0.21.2(react-dom@19.2.5(react@19.2.0))(react@19.2.0) + react-native-webview: + specifier: ^13.16.1 + version: 13.16.1(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0) + react-native-worklets: + specifier: ^0.7.4 + version: 0.7.4(@babel/core@7.29.0)(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0) + tweetnacl: + specifier: ^1.0.3 + version: 1.0.3 + ws: + specifier: ^8.18.0 + version: 8.20.0 + zod: + specifier: ^4.3.6 + version: 4.3.6 + zustand: + specifier: ^5.0.12 + version: 5.0.12(@types/react@19.2.14)(react@19.2.0)(use-sync-external-store@1.6.0(react@19.2.0)) + devDependencies: + '@types/react': + specifier: ^19.2.14 + version: 19.2.14 + '@types/react-native': + specifier: ^0.73.0 + version: 0.73.0(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0) + '@types/ws': + specifier: ^8.18.1 + version: 8.18.1 + expo-module-scripts: + specifier: ^55.0.2 + version: 55.0.2(@babel/core@7.29.0)(@babel/runtime@7.29.2)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.29.0))(eslint@9.39.4)(expo@55.0.17)(jest@29.7.0(@types/node@25.6.0))(prettier@2.8.8)(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react-refresh@0.14.2)(react-test-renderer@19.2.0(react@19.2.0))(react@19.2.0) + oxfmt: + specifier: ^0.47.0 + version: 0.47.0 + oxlint: + specifier: ^1.62.0 + version: 1.62.0 + tsx: + specifier: ^4.19.4 + version: 4.21.0 + typescript: + specifier: ^5.9.3 + version: 5.9.3 + +packages: + + '@babel/cli@7.28.6': + resolution: {integrity: sha512-6EUNcuBbNkj08Oj4gAZ+BUU8yLCgKzgVX4gaTh09Ya2C8ICM4P+G30g4m3akRxSYAp3A/gnWchrNst7px4/nUQ==} + engines: {node: '>=6.9.0'} + hasBin: true + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/code-frame@7.29.0': + resolution: {integrity: sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==} + engines: {node: '>=6.9.0'} + + '@babel/compat-data@7.29.0': + resolution: {integrity: sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg==} + engines: {node: '>=6.9.0'} + + '@babel/core@7.29.0': + resolution: {integrity: sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==} + engines: {node: '>=6.9.0'} + + '@babel/generator@7.29.1': + resolution: {integrity: sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-annotate-as-pure@7.27.3': + resolution: {integrity: sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==} + engines: {node: '>=6.9.0'} + + '@babel/helper-compilation-targets@7.28.6': + resolution: {integrity: sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-create-class-features-plugin@7.28.6': + resolution: {integrity: sha512-dTOdvsjnG3xNT9Y0AUg1wAl38y+4Rl4sf9caSQZOXdNqVn+H+HbbJ4IyyHaIqNR6SW9oJpA/RuRjsjCw2IdIow==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-create-regexp-features-plugin@7.28.5': + resolution: {integrity: sha512-N1EhvLtHzOvj7QQOUCCS3NrPJP8c5W6ZXCHDn7Yialuy1iu4r5EmIYkXlKNqT99Ciw+W0mDqWoR6HWMZlFP3hw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-define-polyfill-provider@0.6.8': + resolution: {integrity: sha512-47UwBLPpQi1NoWzLuHNjRoHlYXMwIJoBf7MFou6viC/sIHWYygpvr0B6IAyh5sBdA2nr2LPIRww8lfaUVQINBA==} + peerDependencies: + '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 + + '@babel/helper-globals@7.28.0': + resolution: {integrity: sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-member-expression-to-functions@7.28.5': + resolution: {integrity: sha512-cwM7SBRZcPCLgl8a7cY0soT1SptSzAlMH39vwiRpOQkJlh53r5hdHwLSCZpQdVLT39sZt+CRpNwYG4Y2v77atg==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-imports@7.28.6': + resolution: {integrity: sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-transforms@7.28.6': + resolution: {integrity: sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-optimise-call-expression@7.27.1': + resolution: {integrity: sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-plugin-utils@7.28.6': + resolution: {integrity: sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==} + engines: {node: '>=6.9.0'} + + '@babel/helper-remap-async-to-generator@7.27.1': + resolution: {integrity: sha512-7fiA521aVw8lSPeI4ZOD3vRFkoqkJcS+z4hFo82bFSH/2tNd6eJ5qCVMS5OzDmZh/kaHQeBaeyxK6wljcPtveA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-replace-supers@7.28.6': + resolution: {integrity: sha512-mq8e+laIk94/yFec3DxSjCRD2Z0TAjhVbEJY3UQrlwVo15Lmt7C2wAUbK4bjnTs4APkwsYLTahXRraQXhb1WCg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-skip-transparent-expression-wrappers@7.27.1': + resolution: {integrity: sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg==} + engines: {node: '>=6.9.0'} + + '@babel/helper-string-parser@7.27.1': + resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-identifier@7.28.5': + resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-option@7.27.1': + resolution: {integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==} + engines: {node: '>=6.9.0'} + + '@babel/helper-wrap-function@7.28.6': + resolution: {integrity: sha512-z+PwLziMNBeSQJonizz2AGnndLsP2DeGHIxDAn+wdHOGuo4Fo1x1HBPPXeE9TAOPHNNWQKCSlA2VZyYyyibDnQ==} + engines: {node: '>=6.9.0'} + + '@babel/helpers@7.29.2': + resolution: {integrity: sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw==} + engines: {node: '>=6.9.0'} + + '@babel/parser@7.29.2': + resolution: {integrity: sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA==} + engines: {node: '>=6.0.0'} + hasBin: true + + '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.28.5': + resolution: {integrity: sha512-87GDMS3tsmMSi/3bWOte1UblL+YUTFMV8SZPZ2eSEL17s74Cw/l63rR6NmGVKMYW2GYi85nE+/d6Hw5N0bEk2Q==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-bugfix-safari-class-field-initializer-scope@7.27.1': + resolution: {integrity: sha512-qNeq3bCKnGgLkEXUuFry6dPlGfCdQNZbn7yUAPCInwAJHMU7THJfrBSozkcWq5sNM6RcF3S8XyQL2A52KNR9IA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.27.1': + resolution: {integrity: sha512-g4L7OYun04N1WyqMNjldFwlfPCLVkgB54A/YCXICZYBsvJJE3kByKv9c9+R/nAfmIfjl2rKYLNyMHboYbZaWaA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.27.1': + resolution: {integrity: sha512-oO02gcONcD5O1iTLi/6frMJBIwWEHceWGSGqrpCmEL8nogiS6J9PBlE48CaK20/Jx1LuRml9aDftLgdjXT8+Cw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.13.0 + + '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.28.6': + resolution: {integrity: sha512-a0aBScVTlNaiUe35UtfxAN7A/tehvvG4/ByO6+46VPKTRSlfnAFsgKy0FUh+qAkQrDTmhDkT+IBOKlOoMUxQ0g==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-proposal-decorators@7.29.0': + resolution: {integrity: sha512-CVBVv3VY/XRMxRYq5dwr2DS7/MvqPm23cOCjbwNnVrfOqcWlnefua1uUs0sjdKOGjvPUG633o07uWzJq4oI6dA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-proposal-export-default-from@7.27.1': + resolution: {integrity: sha512-hjlsMBl1aJc5lp8MoCDEZCiYzlgdRAShOjAfRw6X+GlpLpUPU7c3XNLsKFZbQk/1cRzBlJ7CXg3xJAJMrFa1Uw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2': + resolution: {integrity: sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-async-generators@7.8.4': + resolution: {integrity: sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-bigint@7.8.3': + resolution: {integrity: sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-class-properties@7.12.13': + resolution: {integrity: sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-class-static-block@7.14.5': + resolution: {integrity: sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-decorators@7.28.6': + resolution: {integrity: sha512-71EYI0ONURHJBL4rSFXnITXqXrrY8q4P0q006DPfN+Rk+ASM+++IBXem/ruokgBZR8YNEWZ8R6B+rCb8VcUTqA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-dynamic-import@7.8.3': + resolution: {integrity: sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-export-default-from@7.28.6': + resolution: {integrity: sha512-Svlx1fjJFnNz0LZeUaybRukSxZI3KkpApUmIRzEdXC5k8ErTOz0OD0kNrICi5Vc3GlpP5ZCeRyRO+mfWTSz+iQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-flow@7.28.6': + resolution: {integrity: sha512-D+OrJumc9McXNEBI/JmFnc/0uCM2/Y3PEBG3gfV3QIYkKv5pvnpzFrl1kYCrcHJP8nOeFB/SHi1IHz29pNGuew==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-import-assertions@7.28.6': + resolution: {integrity: sha512-pSJUpFHdx9z5nqTSirOCMtYVP2wFgoWhP0p3g8ONK/4IHhLIBd0B9NYqAvIUAhq+OkhO4VM1tENCt0cjlsNShw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-import-attributes@7.28.6': + resolution: {integrity: sha512-jiLC0ma9XkQT3TKJ9uYvlakm66Pamywo+qwL+oL8HJOvc6TWdZXVfhqJr8CCzbSGUAbDOzlGHJC1U+vRfLQDvw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-import-meta@7.10.4': + resolution: {integrity: sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-json-strings@7.8.3': + resolution: {integrity: sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-jsx@7.28.6': + resolution: {integrity: sha512-wgEmr06G6sIpqr8YDwA2dSRTE3bJ+V0IfpzfSY3Lfgd7YWOaAdlykvJi13ZKBt8cZHfgH1IXN+CL656W3uUa4w==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-logical-assignment-operators@7.10.4': + resolution: {integrity: sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-nullish-coalescing-operator@7.8.3': + resolution: {integrity: sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-numeric-separator@7.10.4': + resolution: {integrity: sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-object-rest-spread@7.8.3': + resolution: {integrity: sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-optional-catch-binding@7.8.3': + resolution: {integrity: sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-optional-chaining@7.8.3': + resolution: {integrity: sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-private-property-in-object@7.14.5': + resolution: {integrity: sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-top-level-await@7.14.5': + resolution: {integrity: sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-typescript@7.28.6': + resolution: {integrity: sha512-+nDNmQye7nlnuuHDboPbGm00Vqg3oO8niRRL27/4LYHUsHYh0zJ1xWOz0uRwNFmM1Avzk8wZbc6rdiYhomzv/A==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-unicode-sets-regex@7.18.6': + resolution: {integrity: sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-transform-arrow-functions@7.27.1': + resolution: {integrity: sha512-8Z4TGic6xW70FKThA5HYEKKyBpOOsucTOD1DjU3fZxDg+K3zBJcXMFnt/4yQiZnf5+MiOMSXQ9PaEK/Ilh1DeA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-async-generator-functions@7.29.0': + resolution: {integrity: sha512-va0VdWro4zlBr2JsXC+ofCPB2iG12wPtVGTWFx2WLDOM3nYQZZIGP82qku2eW/JR83sD+k2k+CsNtyEbUqhU6w==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-async-to-generator@7.28.6': + resolution: {integrity: sha512-ilTRcmbuXjsMmcZ3HASTe4caH5Tpo93PkTxF9oG2VZsSWsahydmcEHhix9Ik122RcTnZnUzPbmux4wh1swfv7g==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-block-scoped-functions@7.27.1': + resolution: {integrity: sha512-cnqkuOtZLapWYZUYM5rVIdv1nXYuFVIltZ6ZJ7nIj585QsjKM5dhL2Fu/lICXZ1OyIAFc7Qy+bvDAtTXqGrlhg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-block-scoping@7.28.6': + resolution: {integrity: sha512-tt/7wOtBmwHPNMPu7ax4pdPz6shjFrmHDghvNC+FG9Qvj7D6mJcoRQIF5dy4njmxR941l6rgtvfSB2zX3VlUIw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-class-properties@7.27.1': + resolution: {integrity: sha512-D0VcalChDMtuRvJIu3U/fwWjf8ZMykz5iZsg77Nuj821vCKI3zCyRLwRdWbsuJ/uRwZhZ002QtCqIkwC/ZkvbA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-class-properties@7.28.6': + resolution: {integrity: sha512-dY2wS3I2G7D697VHndN91TJr8/AAfXQNt5ynCTI/MpxMsSzHp+52uNivYT5wCPax3whc47DR8Ba7cmlQMg24bw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-class-static-block@7.28.6': + resolution: {integrity: sha512-rfQ++ghVwTWTqQ7w8qyDxL1XGihjBss4CmTgGRCTAC9RIbhVpyp4fOeZtta0Lbf+dTNIVJer6ych2ibHwkZqsQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.12.0 + + '@babel/plugin-transform-classes@7.28.4': + resolution: {integrity: sha512-cFOlhIYPBv/iBoc+KS3M6et2XPtbT2HiCRfBXWtfpc9OAyostldxIf9YAYB6ypURBBbx+Qv6nyrLzASfJe+hBA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-classes@7.28.6': + resolution: {integrity: sha512-EF5KONAqC5zAqT783iMGuM2ZtmEBy+mJMOKl2BCvPZ2lVrwvXnB6o+OBWCS+CoeCCpVRF2sA2RBKUxvT8tQT5Q==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-computed-properties@7.28.6': + resolution: {integrity: sha512-bcc3k0ijhHbc2lEfpFHgx7eYw9KNXqOerKWfzbxEHUGKnS3sz9C4CNL9OiFN1297bDNfUiSO7DaLzbvHQQQ1BQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-destructuring@7.28.5': + resolution: {integrity: sha512-Kl9Bc6D0zTUcFUvkNuQh4eGXPKKNDOJQXVyyM4ZAQPMveniJdxi8XMJwLo+xSoW3MIq81bD33lcUe9kZpl0MCw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-dotall-regex@7.28.6': + resolution: {integrity: sha512-SljjowuNKB7q5Oayv4FoPzeB74g3QgLt8IVJw9ADvWy3QnUb/01aw8I4AVv8wYnPvQz2GDDZ/g3GhcNyDBI4Bg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-duplicate-keys@7.27.1': + resolution: {integrity: sha512-MTyJk98sHvSs+cvZ4nOauwTTG1JeonDjSGvGGUNHreGQns+Mpt6WX/dVzWBHgg+dYZhkC4X+zTDfkTU+Vy9y7Q==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-duplicate-named-capturing-groups-regex@7.29.0': + resolution: {integrity: sha512-zBPcW2lFGxdiD8PUnPwJjag2J9otbcLQzvbiOzDxpYXyCuYX9agOwMPGn1prVH0a4qzhCKu24rlH4c1f7yA8rw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-transform-dynamic-import@7.27.1': + resolution: {integrity: sha512-MHzkWQcEmjzzVW9j2q8LGjwGWpG2mjwaaB0BNQwst3FIjqsg8Ct/mIZlvSPJvfi9y2AC8mi/ktxbFVL9pZ1I4A==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-explicit-resource-management@7.28.6': + resolution: {integrity: sha512-Iao5Konzx2b6g7EPqTy40UZbcdXE126tTxVFr/nAIj+WItNxjKSYTEw3RC+A2/ZetmdJsgueL1KhaMCQHkLPIg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-exponentiation-operator@7.28.6': + resolution: {integrity: sha512-WitabqiGjV/vJ0aPOLSFfNY1u9U3R7W36B03r5I2KoNix+a3sOhJ3pKFB3R5It9/UiK78NiO0KE9P21cMhlPkw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-export-namespace-from@7.27.1': + resolution: {integrity: sha512-tQvHWSZ3/jH2xuq/vZDy0jNn+ZdXJeM8gHvX4lnJmsc3+50yPlWdZXIc5ay+umX+2/tJIqHqiEqcJvxlmIvRvQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-flow-strip-types@7.27.1': + resolution: {integrity: sha512-G5eDKsu50udECw7DL2AcsysXiQyB7Nfg521t2OAJ4tbfTJ27doHLeF/vlI1NZGlLdbb/v+ibvtL1YBQqYOwJGg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-for-of@7.27.1': + resolution: {integrity: sha512-BfbWFFEJFQzLCQ5N8VocnCtA8J1CLkNTe2Ms2wocj75dd6VpiqS5Z5quTYcUoo4Yq+DN0rtikODccuv7RU81sw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-function-name@7.27.1': + resolution: {integrity: sha512-1bQeydJF9Nr1eBCMMbC+hdwmRlsv5XYOMu03YSWFwNs0HsAmtSxxF1fyuYPqemVldVyFmlCU7w8UE14LupUSZQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-json-strings@7.28.6': + resolution: {integrity: sha512-Nr+hEN+0geQkzhbdgQVPoqr47lZbm+5fCUmO70722xJZd0Mvb59+33QLImGj6F+DkK3xgDi1YVysP8whD6FQAw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-literals@7.27.1': + resolution: {integrity: sha512-0HCFSepIpLTkLcsi86GG3mTUzxV5jpmbv97hTETW3yzrAij8aqlD36toB1D0daVFJM8NK6GvKO0gslVQmm+zZA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-logical-assignment-operators@7.28.6': + resolution: {integrity: sha512-+anKKair6gpi8VsM/95kmomGNMD0eLz1NQ8+Pfw5sAwWH9fGYXT50E55ZpV0pHUHWf6IUTWPM+f/7AAff+wr9A==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-member-expression-literals@7.27.1': + resolution: {integrity: sha512-hqoBX4dcZ1I33jCSWcXrP+1Ku7kdqXf1oeah7ooKOIiAdKQ+uqftgCFNOSzA5AMS2XIHEYeGFg4cKRCdpxzVOQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-modules-amd@7.27.1': + resolution: {integrity: sha512-iCsytMg/N9/oFq6n+gFTvUYDZQOMK5kEdeYxmxt91fcJGycfxVP9CnrxoliM0oumFERba2i8ZtwRUCMhvP1LnA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-modules-commonjs@7.28.6': + resolution: {integrity: sha512-jppVbf8IV9iWWwWTQIxJMAJCWBuuKx71475wHwYytrRGQ2CWiDvYlADQno3tcYpS/T2UUWFQp3nVtYfK/YBQrA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-modules-systemjs@7.29.0': + resolution: {integrity: sha512-PrujnVFbOdUpw4UHiVwKvKRLMMic8+eC0CuNlxjsyZUiBjhFdPsewdXCkveh2KqBA9/waD0W1b4hXSOBQJezpQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-modules-umd@7.27.1': + resolution: {integrity: sha512-iQBE/xC5BV1OxJbp6WG7jq9IWiD+xxlZhLrdwpPkTX3ydmXdvoCpyfJN7acaIBZaOqTfr76pgzqBJflNbeRK+w==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-named-capturing-groups-regex@7.29.0': + resolution: {integrity: sha512-1CZQA5KNAD6ZYQLPw7oi5ewtDNxH/2vuCh+6SmvgDfhumForvs8a1o9n0UrEoBD8HU4djO2yWngTQlXl1NDVEQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-transform-new-target@7.27.1': + resolution: {integrity: sha512-f6PiYeqXQ05lYq3TIfIDu/MtliKUbNwkGApPUvyo6+tc7uaR4cPjPe7DFPr15Uyycg2lZU6btZ575CuQoYh7MQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-nullish-coalescing-operator@7.27.1': + resolution: {integrity: sha512-aGZh6xMo6q9vq1JGcw58lZ1Z0+i0xB2x0XaauNIUXd6O1xXc3RwoWEBlsTQrY4KQ9Jf0s5rgD6SiNkaUdJegTA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-nullish-coalescing-operator@7.28.6': + resolution: {integrity: sha512-3wKbRgmzYbw24mDJXT7N+ADXw8BC/imU9yo9c9X9NKaLF1fW+e5H1U5QjMUBe4Qo4Ox/o++IyUkl1sVCLgevKg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-numeric-separator@7.28.6': + resolution: {integrity: sha512-SJR8hPynj8outz+SlStQSwvziMN4+Bq99it4tMIf5/Caq+3iOc0JtKyse8puvyXkk3eFRIA5ID/XfunGgO5i6w==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-object-rest-spread@7.28.6': + resolution: {integrity: sha512-5rh+JR4JBC4pGkXLAcYdLHZjXudVxWMXbB6u6+E9lRL5TrGVbHt1TjxGbZ8CkmYw9zjkB7jutzOROArsqtncEA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-object-super@7.27.1': + resolution: {integrity: sha512-SFy8S9plRPbIcxlJ8A6mT/CxFdJx/c04JEctz4jf8YZaVS2px34j7NXRrlGlHkN/M2gnpL37ZpGRGVFLd3l8Ng==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-optional-catch-binding@7.28.6': + resolution: {integrity: sha512-R8ja/Pyrv0OGAvAXQhSTmWyPJPml+0TMqXlO5w+AsMEiwb2fg3WkOvob7UxFSL3OIttFSGSRFKQsOhJ/X6HQdQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-optional-chaining@7.27.1': + resolution: {integrity: sha512-BQmKPPIuc8EkZgNKsv0X4bPmOoayeu4F1YCwx2/CfmDSXDbp7GnzlUH+/ul5VGfRg1AoFPsrIThlEBj2xb4CAg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-optional-chaining@7.28.6': + resolution: {integrity: sha512-A4zobikRGJTsX9uqVFdafzGkqD30t26ck2LmOzAuLL8b2x6k3TIqRiT2xVvA9fNmFeTX484VpsdgmKNA0bS23w==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-parameters@7.27.7': + resolution: {integrity: sha512-qBkYTYCb76RRxUM6CcZA5KRu8K4SM8ajzVeUgVdMVO9NN9uI/GaVmBg/WKJJGnNokV9SY8FxNOVWGXzqzUidBg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-private-methods@7.28.6': + resolution: {integrity: sha512-piiuapX9CRv7+0st8lmuUlRSmX6mBcVeNQ1b4AYzJxfCMuBfB0vBXDiGSmm03pKJw1v6cZ8KSeM+oUnM6yAExg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-private-property-in-object@7.28.6': + resolution: {integrity: sha512-b97jvNSOb5+ehyQmBpmhOCiUC5oVK4PMnpRvO7+ymFBoqYjeDHIU9jnrNUuwHOiL9RpGDoKBpSViarV+BU+eVA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-property-literals@7.27.1': + resolution: {integrity: sha512-oThy3BCuCha8kDZ8ZkgOg2exvPYUlprMukKQXI1r1pJ47NCvxfkEy8vK+r/hT9nF0Aa4H1WUPZZjHTFtAhGfmQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-react-display-name@7.28.0': + resolution: {integrity: sha512-D6Eujc2zMxKjfa4Zxl4GHMsmhKKZ9VpcqIchJLvwTxad9zWIYulwYItBovpDOoNLISpcZSXoDJ5gaGbQUDqViA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-react-jsx-development@7.27.1': + resolution: {integrity: sha512-ykDdF5yI4f1WrAolLqeF3hmYU12j9ntLQl/AOG1HAS21jxyg1Q0/J/tpREuYLfatGdGmXp/3yS0ZA76kOlVq9Q==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-react-jsx-self@7.27.1': + resolution: {integrity: sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-react-jsx-source@7.27.1': + resolution: {integrity: sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-react-jsx@7.28.6': + resolution: {integrity: sha512-61bxqhiRfAACulXSLd/GxqmAedUSrRZIu/cbaT18T1CetkTmtDN15it7i80ru4DVqRK1WMxQhXs+Lf9kajm5Ow==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-react-pure-annotations@7.27.1': + resolution: {integrity: sha512-JfuinvDOsD9FVMTHpzA/pBLisxpv1aSf+OIV8lgH3MuWrks19R27e6a6DipIg4aX1Zm9Wpb04p8wljfKrVSnPA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-regenerator@7.29.0': + resolution: {integrity: sha512-FijqlqMA7DmRdg/aINBSs04y8XNTYw/lr1gJ2WsmBnnaNw1iS43EPkJW+zK7z65auG3AWRFXWj+NcTQwYptUog==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-regexp-modifiers@7.28.6': + resolution: {integrity: sha512-QGWAepm9qxpaIs7UM9FvUSnCGlb8Ua1RhyM4/veAxLwt3gMat/LSGrZixyuj4I6+Kn9iwvqCyPTtbdxanYoWYg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-transform-reserved-words@7.27.1': + resolution: {integrity: sha512-V2ABPHIJX4kC7HegLkYoDpfg9PVmuWy/i6vUM5eGK22bx4YVFD3M5F0QQnWQoDs6AGsUWTVOopBiMFQgHaSkVw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-runtime@7.29.0': + resolution: {integrity: sha512-jlaRT5dJtMaMCV6fAuLbsQMSwz/QkvaHOHOSXRitGGwSpR1blCY4KUKoyP2tYO8vJcqYe8cEj96cqSztv3uF9w==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-shorthand-properties@7.27.1': + resolution: {integrity: sha512-N/wH1vcn4oYawbJ13Y/FxcQrWk63jhfNa7jef0ih7PHSIHX2LB7GWE1rkPrOnka9kwMxb6hMl19p7lidA+EHmQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-spread@7.28.6': + resolution: {integrity: sha512-9U4QObUC0FtJl05AsUcodau/RWDytrU6uKgkxu09mLR9HLDAtUMoPuuskm5huQsoktmsYpI+bGmq+iapDcriKA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-sticky-regex@7.27.1': + resolution: {integrity: sha512-lhInBO5bi/Kowe2/aLdBAawijx+q1pQzicSgnkB6dUPc1+RC8QmJHKf2OjvU+NZWitguJHEaEmbV6VWEouT58g==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-template-literals@7.27.1': + resolution: {integrity: sha512-fBJKiV7F2DxZUkg5EtHKXQdbsbURW3DZKQUWphDum0uRP6eHGGa/He9mc0mypL680pb+e/lDIthRohlv8NCHkg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-typeof-symbol@7.27.1': + resolution: {integrity: sha512-RiSILC+nRJM7FY5srIyc4/fGIwUhyDuuBSdWn4y6yT6gm652DpCHZjIipgn6B7MQ1ITOUnAKWixEUjQRIBIcLw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-typescript@7.28.6': + resolution: {integrity: sha512-0YWL2RFxOqEm9Efk5PvreamxPME8OyY0wM5wh5lHjF+VtVhdneCWGzZeSqzOfiobVqQaNCd2z0tQvnI9DaPWPw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-unicode-escapes@7.27.1': + resolution: {integrity: sha512-Ysg4v6AmF26k9vpfFuTZg8HRfVWzsh1kVfowA23y9j/Gu6dOuahdUVhkLqpObp3JIv27MLSii6noRnuKN8H0Mg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-unicode-property-regex@7.28.6': + resolution: {integrity: sha512-4Wlbdl/sIZjzi/8St0evF0gEZrgOswVO6aOzqxh1kDZOl9WmLrHq2HtGhnOJZmHZYKP8WZ1MDLCt5DAWwRo57A==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-unicode-regex@7.27.1': + resolution: {integrity: sha512-xvINq24TRojDuyt6JGtHmkVkrfVV3FPT16uytxImLeBZqW3/H52yN+kM1MGuyPkIQxrzKwPHs5U/MP3qKyzkGw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-unicode-sets-regex@7.28.6': + resolution: {integrity: sha512-/wHc/paTUmsDYN7SZkpWxogTOBNnlx7nBQYfy6JJlCT7G3mVhltk3e++N7zV0XfgGsrqBxd4rJQt9H16I21Y1Q==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/preset-env@7.29.2': + resolution: {integrity: sha512-DYD23veRYGvBFhcTY1iUvJnDNpuqNd/BzBwCvzOTKUnJjKg5kpUBh3/u9585Agdkgj+QuygG7jLfOPWMa2KVNw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/preset-modules@0.1.6-no-external-plugins': + resolution: {integrity: sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==} + peerDependencies: + '@babel/core': ^7.0.0-0 || ^8.0.0-0 <8.0.0 + + '@babel/preset-react@7.28.5': + resolution: {integrity: sha512-Z3J8vhRq7CeLjdC58jLv4lnZ5RKFUJWqH5emvxmv9Hv3BD1T9R/Im713R4MTKwvFaV74ejZ3sM01LyEKk4ugNQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/preset-typescript@7.27.1': + resolution: {integrity: sha512-l7WfQfX0WK4M0v2RudjuQK4u99BS6yLHYEmdtVPP7lKV013zr9DygFuWNlnbvQ9LR+LS0Egz/XAvGx5U9MX0fQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/preset-typescript@7.28.5': + resolution: {integrity: sha512-+bQy5WOI2V6LJZpPVxY+yp66XdZ2yifu0Mc1aP5CQKgjn4QM5IN2i5fAZ4xKop47pr8rpVhiAeu+nDQa12C8+g==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/runtime@7.29.2': + resolution: {integrity: sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==} + engines: {node: '>=6.9.0'} + + '@babel/template@7.28.6': + resolution: {integrity: sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==} + engines: {node: '>=6.9.0'} + + '@babel/traverse@7.29.0': + resolution: {integrity: sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==} + engines: {node: '>=6.9.0'} + + '@babel/types@7.29.0': + resolution: {integrity: sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==} + engines: {node: '>=6.9.0'} + + '@bcoe/v8-coverage@0.2.3': + resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==} + + '@egjs/hammerjs@2.0.17': + resolution: {integrity: sha512-XQsZgjm2EcVUiZQf11UBJQfmZeEmOW8DpI1gsFeln6w0ae0ii4dMQEQ0kjl6DspdWX1aGY1/loyXnP0JS06e/A==} + engines: {node: '>=0.8.0'} + + '@esbuild/aix-ppc64@0.27.7': + resolution: {integrity: sha512-EKX3Qwmhz1eMdEJokhALr0YiD0lhQNwDqkPYyPhiSwKrh7/4KRjQc04sZ8db+5DVVnZ1LmbNDI1uAMPEUBnQPg==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.27.7': + resolution: {integrity: sha512-62dPZHpIXzvChfvfLJow3q5dDtiNMkwiRzPylSCfriLvZeq0a1bWChrGx/BbUbPwOrsWKMn8idSllklzBy+dgQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.27.7': + resolution: {integrity: sha512-jbPXvB4Yj2yBV7HUfE2KHe4GJX51QplCN1pGbYjvsyCZbQmies29EoJbkEc+vYuU5o45AfQn37vZlyXy4YJ8RQ==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.27.7': + resolution: {integrity: sha512-x5VpMODneVDb70PYV2VQOmIUUiBtY3D3mPBG8NxVk5CogneYhkR7MmM3yR/uMdITLrC1ml/NV1rj4bMJuy9MCg==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.27.7': + resolution: {integrity: sha512-5lckdqeuBPlKUwvoCXIgI2D9/ABmPq3Rdp7IfL70393YgaASt7tbju3Ac+ePVi3KDH6N2RqePfHnXkaDtY9fkw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.27.7': + resolution: {integrity: sha512-rYnXrKcXuT7Z+WL5K980jVFdvVKhCHhUwid+dDYQpH+qu+TefcomiMAJpIiC2EM3Rjtq0sO3StMV/+3w3MyyqQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.27.7': + resolution: {integrity: sha512-B48PqeCsEgOtzME2GbNM2roU29AMTuOIN91dsMO30t+Ydis3z/3Ngoj5hhnsOSSwNzS+6JppqWsuhTp6E82l2w==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.27.7': + resolution: {integrity: sha512-jOBDK5XEjA4m5IJK3bpAQF9/Lelu/Z9ZcdhTRLf4cajlB+8VEhFFRjWgfy3M1O4rO2GQ/b2dLwCUGpiF/eATNQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.27.7': + resolution: {integrity: sha512-RZPHBoxXuNnPQO9rvjh5jdkRmVizktkT7TCDkDmQ0W2SwHInKCAV95GRuvdSvA7w4VMwfCjUiPwDi0ZO6Nfe9A==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.27.7': + resolution: {integrity: sha512-RkT/YXYBTSULo3+af8Ib0ykH8u2MBh57o7q/DAs3lTJlyVQkgQvlrPTnjIzzRPQyavxtPtfg0EopvDyIt0j1rA==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.27.7': + resolution: {integrity: sha512-GA48aKNkyQDbd3KtkplYWT102C5sn/EZTY4XROkxONgruHPU72l+gW+FfF8tf2cFjeHaRbWpOYa/uRBz/Xq1Pg==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.27.7': + resolution: {integrity: sha512-a4POruNM2oWsD4WKvBSEKGIiWQF8fZOAsycHOt6JBpZ+JN2n2JH9WAv56SOyu9X5IqAjqSIPTaJkqN8F7XOQ5Q==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.27.7': + resolution: {integrity: sha512-KabT5I6StirGfIz0FMgl1I+R1H73Gp0ofL9A3nG3i/cYFJzKHhouBV5VWK1CSgKvVaG4q1RNpCTR2LuTVB3fIw==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.27.7': + resolution: {integrity: sha512-gRsL4x6wsGHGRqhtI+ifpN/vpOFTQtnbsupUF5R5YTAg+y/lKelYR1hXbnBdzDjGbMYjVJLJTd2OFmMewAgwlQ==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.27.7': + resolution: {integrity: sha512-hL25LbxO1QOngGzu2U5xeXtxXcW+/GvMN3ejANqXkxZ/opySAZMrc+9LY/WyjAan41unrR3YrmtTsUpwT66InQ==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.27.7': + resolution: {integrity: sha512-2k8go8Ycu1Kb46vEelhu1vqEP+UeRVj2zY1pSuPdgvbd5ykAw82Lrro28vXUrRmzEsUV0NzCf54yARIK8r0fdw==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.27.7': + resolution: {integrity: sha512-hzznmADPt+OmsYzw1EE33ccA+HPdIqiCRq7cQeL1Jlq2gb1+OyWBkMCrYGBJ+sxVzve2ZJEVeePbLM2iEIZSxA==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-arm64@0.27.7': + resolution: {integrity: sha512-b6pqtrQdigZBwZxAn1UpazEisvwaIDvdbMbmrly7cDTMFnw/+3lVxxCTGOrkPVnsYIosJJXAsILG9XcQS+Yu6w==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.27.7': + resolution: {integrity: sha512-OfatkLojr6U+WN5EDYuoQhtM+1xco+/6FSzJJnuWiUw5eVcicbyK3dq5EeV/QHT1uy6GoDhGbFpprUiHUYggrw==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-arm64@0.27.7': + resolution: {integrity: sha512-AFuojMQTxAz75Fo8idVcqoQWEHIXFRbOc1TrVcFSgCZtQfSdc1RXgB3tjOn/krRHENUB4j00bfGjyl2mJrU37A==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.27.7': + resolution: {integrity: sha512-+A1NJmfM8WNDv5CLVQYJ5PshuRm/4cI6WMZRg1by1GwPIQPCTs1GLEUHwiiQGT5zDdyLiRM/l1G0Pv54gvtKIg==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + + '@esbuild/openharmony-arm64@0.27.7': + resolution: {integrity: sha512-+KrvYb/C8zA9CU/g0sR6w2RBw7IGc5J2BPnc3dYc5VJxHCSF1yNMxTV5LQ7GuKteQXZtspjFbiuW5/dOj7H4Yw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openharmony] + + '@esbuild/sunos-x64@0.27.7': + resolution: {integrity: sha512-ikktIhFBzQNt/QDyOL580ti9+5mL/YZeUPKU2ivGtGjdTYoqz6jObj6nOMfhASpS4GU4Q/Clh1QtxWAvcYKamA==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.27.7': + resolution: {integrity: sha512-7yRhbHvPqSpRUV7Q20VuDwbjW5kIMwTHpptuUzV+AA46kiPze5Z7qgt6CLCK3pWFrHeNfDd1VKgyP4O+ng17CA==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.27.7': + resolution: {integrity: sha512-SmwKXe6VHIyZYbBLJrhOoCJRB/Z1tckzmgTLfFYOfpMAx63BJEaL9ExI8x7v0oAO3Zh6D/Oi1gVxEYr5oUCFhw==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.27.7': + resolution: {integrity: sha512-56hiAJPhwQ1R4i+21FVF7V8kSD5zZTdHcVuRFMW0hn753vVfQN8xlx4uOPT4xoGH0Z/oVATuR82AiqSTDIpaHg==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + + '@eslint-community/eslint-utils@4.9.1': + resolution: {integrity: sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + + '@eslint-community/regexpp@4.12.2': + resolution: {integrity: sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==} + engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + + '@eslint/config-array@0.21.2': + resolution: {integrity: sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/config-helpers@0.4.2': + resolution: {integrity: sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/core@0.17.0': + resolution: {integrity: sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/eslintrc@3.3.5': + resolution: {integrity: sha512-4IlJx0X0qftVsN5E+/vGujTRIFtwuLbNsVUe7TO6zYPDR1O6nFwvwhIKEKSrl6dZchmYBITazxKoUYOjdtjlRg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/js@9.39.4': + resolution: {integrity: sha512-nE7DEIchvtiFTwBw4Lfbu59PG+kCofhjsKaCWzxTpt4lfRjRMqG6uMBzKXuEcyXhOHoUp9riAm7/aWYGhXZ9cw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/object-schema@2.1.7': + resolution: {integrity: sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/plugin-kit@0.4.1': + resolution: {integrity: sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@expo-google-fonts/material-symbols@0.4.34': + resolution: {integrity: sha512-PdwETUhvu1gHF1e8eIyEHnBJLq/dRNoTrT5yhsGUfGyRxH5pbm54dF3+QPknxwMKj0M1trN7PSelYz+yzlt3lA==} + + '@expo/cli@55.0.26': + resolution: {integrity: sha512-Ud9gpeGMF5RIL42LXvCw3k3mWK8rf/P2wu+Yrzz9Do1kcFKZeT9Vy2D/xukjdr/Xw+ELba87ThOot17GsPiWjw==} + hasBin: true + peerDependencies: + expo: '*' + expo-router: '*' + react-native: '*' + peerDependenciesMeta: + expo-router: + optional: true + react-native: + optional: true + + '@expo/code-signing-certificates@0.0.6': + resolution: {integrity: sha512-iNe0puxwBNEcuua9gmTGzq+SuMDa0iATai1FlFTMHJ/vUmKvN/V//drXoLJkVb5i5H3iE/n/qIJxyoBnXouD0w==} + + '@expo/config-plugins@55.0.8': + resolution: {integrity: sha512-8WfWTRntTCcowfOS+tHdB0z98gKetTwktg4G5TWkCkXVa8Jt1NUnvzaaU4UHk2vbR2U4N84RyZJFizSwfF6C9g==} + + '@expo/config-types@55.0.5': + resolution: {integrity: sha512-sCmSUZG4mZ/ySXvfyyBdhjivz8Q539X1NondwDdYG7s3SBsk+wsgPJzYsqgAG/P9+l0xWjUD2F+kQ1cAJ6NNLg==} + + '@expo/config@55.0.15': + resolution: {integrity: sha512-lHc0ELIQ8126jYOMZpLv3WIuvordW98jFg5aT/J1/12n2ycuXu01XLZkJsdw0avO34cusUYb1It+MvY8JiMduA==} + + '@expo/devcert@1.2.1': + resolution: {integrity: sha512-qC4eaxmKMTmJC2ahwyui6ud8f3W60Ss7pMkpBq40Hu3zyiAaugPXnZ24145U7K36qO9UHdZUVxsCvIpz2RYYCA==} + + '@expo/devtools@55.0.2': + resolution: {integrity: sha512-4VsFn9MUriocyuhyA+ycJP3TJhUsOFHDc270l9h3LhNpXMf6wvIdGcA0QzXkZtORXmlDybWXRP2KT1k36HcQkA==} + peerDependencies: + react: '*' + react-native: '*' + peerDependenciesMeta: + react: + optional: true + react-native: + optional: true + + '@expo/dom-webview@55.0.5': + resolution: {integrity: sha512-lt3uxYOCk3wmWvtOOvsC35CKGbDAOx5C2EaY8SH1JVSfBzqmF8Cs0Xp1MPxncDPMyxpMiWx5SvvV/iLF1rJU4A==} + peerDependencies: + expo: '*' + react: '*' + react-native: '*' + + '@expo/env@2.1.1': + resolution: {integrity: sha512-rVvHC4I6xlPcg+mAO09ydUi2Wjv1ZytpLmHOSzvXzBAz9mMrJggqCe4s4dubjJvi/Ino/xQCLhbaLCnTtLpikg==} + engines: {node: '>=20.12.0'} + + '@expo/fingerprint@0.16.6': + resolution: {integrity: sha512-nRITNbnu3RKSHPvKVehrSU4KG2VY9V8nvULOHBw98ukHCAU4bGrU5APvcblOkX3JAap+xEHsg/mZvqlvkLInmQ==} + hasBin: true + + '@expo/image-utils@0.8.13': + resolution: {integrity: sha512-1I//yBQeTY6p0u1ihqGNDAr35EbSG8uFEupFrIF0jd++h9EWH33521yZJU1yE+mwGlzCb61g3ehu78siMhXBlA==} + + '@expo/json-file@10.0.13': + resolution: {integrity: sha512-pX/XjQn7tgNw6zuuV2ikmegmwe/S7uiwhrs2wXrANMkq7ozrA+JcZwgW9Q/8WZgciBzfAhNp5hnackHcrmapQA==} + + '@expo/local-build-cache-provider@55.0.11': + resolution: {integrity: sha512-rJ4RTCrkeKaXaido/bVyhl90ZRtVTOEbj59F1PWVjIEIVgjdlfc1J3VD9v7hEsbf/+8Tbr/PgvWhT6Visi5sLQ==} + + '@expo/log-box@55.0.11': + resolution: {integrity: sha512-JQHFLWkskIbJi6cxYMjErx8lQqfFJilDQLKmdTO3m3YkdmN9GE/CrzjOfVlCG0DGEGZJ90br0pGKvGPdXNsHKw==} + peerDependencies: + '@expo/dom-webview': ^55.0.5 + expo: '*' + react: '*' + react-native: '*' + + '@expo/metro-config@55.0.17': + resolution: {integrity: sha512-o11VyNoRDXv0T5320D9cH+nSsrR/OMHTjtysKLIfDlidsBswDk1DMApPv9Kw0/gluArCSnbx8JC1G0Yh2Y4P3g==} + peerDependencies: + expo: '*' + peerDependenciesMeta: + expo: + optional: true + + '@expo/metro-runtime@55.0.10': + resolution: {integrity: sha512-7v+ldTvMWRa1ml83Jel9W2f8qT/NZZWrlHaEjf29nb72JTEO50+Xac9PWLo+X3LCDAAuyYuBGKYXOJwfqxV0fQ==} + peerDependencies: + expo: '*' + react: '*' + react-dom: '*' + react-native: '*' + peerDependenciesMeta: + react-dom: + optional: true + + '@expo/metro@55.1.0': + resolution: {integrity: sha512-bb/LOncsz9KiP6cHmMy0MCDG1COZOn+k+pRpDrvJUmxLdOOuniJSYyCc/Dgv1bR9E/6YR+fh3EXGg9MUrVNy4Q==} + + '@expo/npm-proofread@1.0.1': + resolution: {integrity: sha512-yDyBlNIgg+rKoKCOVM9ZyAtgqRx6gHw+JtmZTrYYW4auAfwS6kE/iInmCpVXPqRy0OhJHgXj6q5PCgWELLVMeg==} + hasBin: true + + '@expo/osascript@2.4.2': + resolution: {integrity: sha512-/XP7PSYF2hzOZzqfjgkoWtllyeTN8dW3aM4P6YgKcmmPikKL5FdoyQhti4eh6RK5a5VrUXJTOlTNIpIHsfB5Iw==} + engines: {node: '>=12'} + + '@expo/package-manager@1.10.4': + resolution: {integrity: sha512-y9Mr4Kmpk4abAVZrNNPCdzOZr8nLLyi18p1SXr0RCVA8IfzqZX/eY4H+50a0HTmXqIsPZrQdcdb4I3ekMS9GvQ==} + + '@expo/plist@0.5.2': + resolution: {integrity: sha512-o4xdVdBpe4aTl3sPMZ2u3fJH4iG1I768EIRk1xRZP+GaFI93MaR3JvoFibYqxeTmLQ1p1kNEVqylfUjezxx45g==} + + '@expo/prebuild-config@55.0.16': + resolution: {integrity: sha512-o4EAVgDGk1lISirtMD8hciO2vyMp7cWlPdfTtjjd5AXSfODVYDIDhygXrfvVQHmJXAztVqPUTKJT+BYOsVkYGQ==} + peerDependencies: + expo: '*' + + '@expo/require-utils@55.0.4': + resolution: {integrity: sha512-JAANvXqV7MOysWeVWgaiDzikoyDjJWOV/ulOW60Zb3kXJfrx2oZOtGtDXDFKD1mXuahQgoM5QOjuZhF7gFRNjA==} + peerDependencies: + typescript: ^5.0.0 || ^5.0.0-0 + peerDependenciesMeta: + typescript: + optional: true + + '@expo/router-server@55.0.15': + resolution: {integrity: sha512-6LksYO4Pg13qroL138KfUebt/x/EO07zVhdyT/nTgcxnpn6CS4ecTl3DciSKhxbaH+0BVLdANkxYeGdp43TMwQ==} + peerDependencies: + '@expo/metro-runtime': ^55.0.10 + expo: '*' + expo-constants: ^55.0.15 + expo-font: ^55.0.6 + expo-router: '*' + expo-server: ^55.0.8 + react: '*' + react-dom: '*' + react-server-dom-webpack: ~19.0.1 || ~19.1.2 || ~19.2.1 + peerDependenciesMeta: + '@expo/metro-runtime': + optional: true + expo-router: + optional: true + react-dom: + optional: true + react-server-dom-webpack: + optional: true + + '@expo/schema-utils@55.0.3': + resolution: {integrity: sha512-l9KHVjTo6MvoeyvwNr6AjckGJm8NIcqZ3QSAh51cWozXW9v2AUjyCyqYtFtyntLWRZ0x/ByYJishpQo4ZQq45Q==} + + '@expo/sdk-runtime-versions@1.0.0': + resolution: {integrity: sha512-Doz2bfiPndXYFPMRwPyGa1k5QaKDVpY806UJj570epIiMzWaYyCtobasyfC++qfIXVb5Ocy7r3tP9d62hAQ7IQ==} + + '@expo/spawn-async@1.7.2': + resolution: {integrity: sha512-QdWi16+CHB9JYP7gma19OVVg0BFkvU8zNj9GjWorYI8Iv8FUxjOCcYRuAmX4s/h91e4e7BPsskc8cSrZYho9Ew==} + engines: {node: '>=12'} + + '@expo/sudo-prompt@9.3.2': + resolution: {integrity: sha512-HHQigo3rQWKMDzYDLkubN5WQOYXJJE2eNqIQC2axC2iO3mHdwnIR7FgZVvHWtBwAdzBgAP0ECp8KqS8TiMKvgw==} + + '@expo/vector-icons@15.1.1': + resolution: {integrity: sha512-Iu2VkcoI5vygbtYngm7jb4ifxElNVXQYdDrYkT7UCEIiKLeWnQY0wf2ZhHZ+Wro6Sc5TaumpKUOqDRpLi5rkvw==} + peerDependencies: + expo-font: '>=14.0.4' + react: '*' + react-native: '*' + + '@expo/ws-tunnel@1.0.6': + resolution: {integrity: sha512-nDRbLmSrJar7abvUjp3smDwH8HcbZcoOEa5jVPUv9/9CajgmWw20JNRwTuBRzWIWIkEJDkz20GoNA+tSwUqk0Q==} + + '@expo/xcpretty@4.4.3': + resolution: {integrity: sha512-wC562eD3gS6vO2tWHToFhlFnmHKfKHgF1oyvojeSkLK/ZYop1bMU+7cOMiF9Sq70CzcsLy/EMRy/uRc76QmNRw==} + hasBin: true + + '@humanfs/core@0.19.2': + resolution: {integrity: sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==} + engines: {node: '>=18.18.0'} + + '@humanfs/node@0.16.8': + resolution: {integrity: sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==} + engines: {node: '>=18.18.0'} + + '@humanfs/types@0.15.0': + resolution: {integrity: sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==} + engines: {node: '>=18.18.0'} + + '@humanwhocodes/module-importer@1.0.1': + resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} + engines: {node: '>=12.22'} + + '@humanwhocodes/retry@0.4.3': + resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} + engines: {node: '>=18.18'} + + '@isaacs/ttlcache@1.4.1': + resolution: {integrity: sha512-RQgQ4uQ+pLbqXfOmieB91ejmLwvSgv9nLx6sT6sD83s7umBypgg+OIBOBbEUiJXrfpnp9j0mRhYYdzp9uqq3lA==} + engines: {node: '>=12'} + + '@istanbuljs/load-nyc-config@1.1.0': + resolution: {integrity: sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==} + engines: {node: '>=8'} + + '@istanbuljs/schema@0.1.6': + resolution: {integrity: sha512-+Sg6GCR/wy1oSmQDFq4LQDAhm3ETKnorxN+y5nbLULOR3P0c14f2Wurzj3/xqPXtasLFfHd5iRFQ7AJt4KH2cw==} + engines: {node: '>=8'} + + '@jest/console@29.7.0': + resolution: {integrity: sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + '@jest/core@29.7.0': + resolution: {integrity: sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + + '@jest/create-cache-key-function@29.7.0': + resolution: {integrity: sha512-4QqS3LY5PBmTRHj9sAg1HLoPzqAI0uOX6wI/TRqHIcOxlFidy6YEmCQJk6FSZjNLGCeubDMfmkWL+qaLKhSGQA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + '@jest/diff-sequences@30.3.0': + resolution: {integrity: sha512-cG51MVnLq1ecVUaQ3fr6YuuAOitHK1S4WUJHnsPFE/quQr33ADUx1FfrTCpMCRxvy0Yr9BThKpDjSlcTi91tMA==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + '@jest/environment@29.7.0': + resolution: {integrity: sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + '@jest/expect-utils@29.7.0': + resolution: {integrity: sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + '@jest/expect@29.7.0': + resolution: {integrity: sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + '@jest/fake-timers@29.7.0': + resolution: {integrity: sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + '@jest/get-type@30.1.0': + resolution: {integrity: sha512-eMbZE2hUnx1WV0pmURZY9XoXPkUYjpc55mb0CrhtdWLtzMQPFvu/rZkTLZFTsdaVQa+Tr4eWAteqcUzoawq/uA==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + '@jest/globals@29.7.0': + resolution: {integrity: sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + '@jest/reporters@29.7.0': + resolution: {integrity: sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + + '@jest/schemas@29.6.3': + resolution: {integrity: sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + '@jest/schemas@30.0.5': + resolution: {integrity: sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + '@jest/source-map@29.6.3': + resolution: {integrity: sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + '@jest/test-result@29.7.0': + resolution: {integrity: sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + '@jest/test-sequencer@29.7.0': + resolution: {integrity: sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + '@jest/transform@29.7.0': + resolution: {integrity: sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + '@jest/types@29.6.3': + resolution: {integrity: sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + '@jridgewell/gen-mapping@0.3.13': + resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} + + '@jridgewell/remapping@2.3.5': + resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==} + + '@jridgewell/resolve-uri@3.1.2': + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} + engines: {node: '>=6.0.0'} + + '@jridgewell/source-map@0.3.11': + resolution: {integrity: sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==} + + '@jridgewell/sourcemap-codec@1.5.5': + resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} + + '@jridgewell/trace-mapping@0.3.31': + resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} + + '@nicolo-ribaudo/chokidar-2@2.1.8-no-fsevents.3': + resolution: {integrity: sha512-s88O1aVtXftvp5bCPB7WnmXc5IwOZZ7YPuwNPt+GtOOXpPvad1LfbmjYv+qII7zP6RU2QGnqve27dnLycEnyEQ==} + + '@oxfmt/binding-android-arm-eabi@0.47.0': + resolution: {integrity: sha512-KrMQRdMi/upr81qT4ijK6X6BNp6jqpMY7FwILQnwIy9QLc3qpnhUx5rsCLGzn4ewsCQ0CNAspN2ogmP1GXLyLw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [android] + + '@oxfmt/binding-android-arm64@0.47.0': + resolution: {integrity: sha512-r4ixS/PeUpAFKgrpDoZ5pSkthjZzVzKd95525Aazj+aOv9H4ulK5zYHGb7wFY5n5kZxHK8TbOJUZgoEb1ohddQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [android] + + '@oxfmt/binding-darwin-arm64@0.47.0': + resolution: {integrity: sha512-CLWxiKpMl+195cm09CuaWEhJK0CirRkoMa07aR9+9AFPat2LfIKtwx1JqxZM0MTvcMe6+adlJNdVL6jdInvq3g==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [darwin] + + '@oxfmt/binding-darwin-x64@0.47.0': + resolution: {integrity: sha512-Xq5fjTYDC50faUeLSm0rZdBqoTgleXEdD7NpJdARtQIczkCJn3xNjMUSQQkUmh4CtxkKTNL68lytcOK3e/osgg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [darwin] + + '@oxfmt/binding-freebsd-x64@0.47.0': + resolution: {integrity: sha512-QOU9ZIJ52p5askcEC0QJvvr8trHAWoonul8bgISo6gYUL3s50zkqafBYcNAr9LJZQbsZtPfIWHk9+5+nUp1qJQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [freebsd] + + '@oxfmt/binding-linux-arm-gnueabihf@0.47.0': + resolution: {integrity: sha512-oJxDM1aBhPvz9gmElBv8UpxyiqhwfjcbrSxT5F0xtuUzY6dQI27/AQPIt3eu3Z5Yvn0kQl5R7MA3Z+MbnRvCBw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + + '@oxfmt/binding-linux-arm-musleabihf@0.47.0': + resolution: {integrity: sha512-g8Lh50VS4ibGz2q6v7r9UZY4D0dM16SdrFYOMzhqIoCwGcai8VMIRUAcqn1/jlCsOOzUXJ741+kCeJt0cofakQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + + '@oxfmt/binding-linux-arm64-gnu@0.47.0': + resolution: {integrity: sha512-YrNT1vQ0asaXoRbrvYENPqmBfOQ9Xr8enPNOULeYfg44VjCcrUowFy5QZr+WawE0zyP8cH9e9Gxxg0fDEFzhcg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + + '@oxfmt/binding-linux-arm64-musl@0.47.0': + resolution: {integrity: sha512-IxtQC/sbBi4ubbY+MdwdanRWrG9InQJVZqyMsBa5IUaQcnSg86gQme574HxXMC1p4bo4YhV99zQ+wNnGCvEgzw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + + '@oxfmt/binding-linux-ppc64-gnu@0.47.0': + resolution: {integrity: sha512-EWXEhOMbWO0q6eJSbu0QLkU8cKi0ljlYLngeDs2Ocu/pm1rrLwyQiYzlFbdnMRURI4w9ndr1sI9rSbhlJ5o23Q==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ppc64] + os: [linux] + + '@oxfmt/binding-linux-riscv64-gnu@0.47.0': + resolution: {integrity: sha512-tZrjS11TUiDuEpRaqdk8K9F9xETRyKXfuZKmdeW+Gj7coBnm7+8sBEfyt033EAFEQSlkniAXvBLh+Qja2ioGBQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [riscv64] + os: [linux] + + '@oxfmt/binding-linux-riscv64-musl@0.47.0': + resolution: {integrity: sha512-KBFy+2CFKUCZzYwX2ZOPQKck1vjQbz+hextuc19G4r0WRJwadfAeuQMQRQvB+Ivc8brlbOVg7et8K7E467440g==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [riscv64] + os: [linux] + + '@oxfmt/binding-linux-s390x-gnu@0.47.0': + resolution: {integrity: sha512-REUPFKVGSiK99B+9eaPhluEVglzaoj/SMykNC5SUiV2RSsBfV5lWN7Y0iCIc251Wz3GaeAGZsJ/zj3gjarxdFg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [s390x] + os: [linux] + + '@oxfmt/binding-linux-x64-gnu@0.47.0': + resolution: {integrity: sha512-KVftVSVEDeIfRW3TIeLe3aNI/iY4m1fu5mDwHcisKMZSCMKLkrhFsjowC7o9RoqNPxbbglm2+/6KAKBIts2t0Q==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + + '@oxfmt/binding-linux-x64-musl@0.47.0': + resolution: {integrity: sha512-DTsmGEaA2860Aq5VUyDO8/MT9NFxwVL93RnRYmpMwK6DsSkThmvEpqoUDDljziEpAedMRG19SCogrNbINSbLUQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + + '@oxfmt/binding-openharmony-arm64@0.47.0': + resolution: {integrity: sha512-8r5BDro7fLOBoq1JXHLVSs55OlrxQhEso4HVo0TcY7OXJUPYfjPoOaYL5us+yIwqyP9rQwN+rxuiNFSmaxSuOQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [openharmony] + + '@oxfmt/binding-win32-arm64-msvc@0.47.0': + resolution: {integrity: sha512-qtz/gzm8IjSPUlseZ0ofW8zyHLoZsuP5HTfcGGkWkUblB89JT8GNYH3ICqjbDsqsGqXum0/ZndXTFplSdXFIcg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [win32] + + '@oxfmt/binding-win32-ia32-msvc@0.47.0': + resolution: {integrity: sha512-5vIcdcIDE7nCx+MXN6sm8kbC4zajDB31E86rez4i45iHNH/2NjdKlJ720xcHTr3eeiMcttCGPHPhE1TjtBDGZw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ia32] + os: [win32] + + '@oxfmt/binding-win32-x64-msvc@0.47.0': + resolution: {integrity: sha512-Sr59Y5ms54ONBjxFeWhVlGyQcHXxcl9DxC23f6yXlRkcos7LXBLoO+KDfxexjHIOZh7cWqrWduzvUjJ+pHp8cQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [win32] + + '@oxlint/binding-android-arm-eabi@1.62.0': + resolution: {integrity: sha512-pKsthNECyvJh8lPTICz6VcwVy2jOqdhhsp1rlxCkhgZR47aKvXPmaRWQDv+zlXpRae4qm1MaaTnutkaOk5aofg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [android] + + '@oxlint/binding-android-arm64@1.62.0': + resolution: {integrity: sha512-b1AUNViByvgmR2xJDubvLIr+dSuu3uraG7bsAoKo+xrpspPvu6RIn6Fhr2JUhobfep3jwUTy18Huco6GkwdvGQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [android] + + '@oxlint/binding-darwin-arm64@1.62.0': + resolution: {integrity: sha512-iG+Tvf70UJ6otfwFYIHk36Sjq9cpPP5YLxkoggANNRtzgi3Tj3g8q6Ybqi6AtkU3+yg9QwF7bDCkCS6bbL4PCg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [darwin] + + '@oxlint/binding-darwin-x64@1.62.0': + resolution: {integrity: sha512-oOWI6YPPr5AJUx+yIDlxmuUbQjS5gZX3OH3QisawYvsZgLiQVvZtR0rPBcJTxLWqt2ClrWg0DlSrlUiG5SQNHg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [darwin] + + '@oxlint/binding-freebsd-x64@1.62.0': + resolution: {integrity: sha512-dLP33T7VLCmLVv4cvjkVX+rmkcwNk2UfxmsZPNur/7BQHoQR60zJ7XLiRvNUawlzn0u8ngCa3itjEG73MAMa/w==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [freebsd] + + '@oxlint/binding-linux-arm-gnueabihf@1.62.0': + resolution: {integrity: sha512-fl//LWNks6qo9chNY60UDYyIwtp7a5cEx4Y/rHPjaarhuwqx6jtbzEpD5V5AqmdL4a6Y5D8zeXg5HF2Cr0QmSQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + + '@oxlint/binding-linux-arm-musleabihf@1.62.0': + resolution: {integrity: sha512-i5vkAuxvueTODV3J2dL61/TXewDHhMFKvtD156cIsk7GsdfiAu7zW7kY0NJXhKeFHeiMZIh7eFNjkPYH6J47HQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + + '@oxlint/binding-linux-arm64-gnu@1.62.0': + resolution: {integrity: sha512-QwN19LLuIGuOjEflSeJkZmOTfBdBMlTmW8xbMf8TZhjd//cxVNYQPq75q7oKZBJc6hRx3gY7sX0Egc8cEIFZYg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + + '@oxlint/binding-linux-arm64-musl@1.62.0': + resolution: {integrity: sha512-8eCy3FCDuWUM5hWujAv6heMvfZPbcCOU3SdQUAkixZLu5bSzOkNfirJiLGoQFO943xceOKkiQRMQNzH++jM3WA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + + '@oxlint/binding-linux-ppc64-gnu@1.62.0': + resolution: {integrity: sha512-NjQ7K7tpTPDe9J+yq8p/s/J0E7lRCkK2uDBDqvT4XIT6f4Z0tlnr59OBg/WcrmVHER1AbrcfyxhGTXgcG8ytWg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ppc64] + os: [linux] + + '@oxlint/binding-linux-riscv64-gnu@1.62.0': + resolution: {integrity: sha512-oKZed9gmSwze29dEt3/Wnsv6l/Ygw/FUst+8Kfpv2SGeS/glEoTGZAMQw37SVyzFV76UTHJN2snGgxK2t2+8ow==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [riscv64] + os: [linux] + + '@oxlint/binding-linux-riscv64-musl@1.62.0': + resolution: {integrity: sha512-gBjBxQ+9lGpAYq+ELqw0w8QXsBnkZclFc7GRX2r0LnEVn3ZTEqeIKpKcGjucmp76Q53bvJD0i4qBWBhcfhSfGA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [riscv64] + os: [linux] + + '@oxlint/binding-linux-s390x-gnu@1.62.0': + resolution: {integrity: sha512-Ew2Kxs9EQ9/mbAIJ2hvocMC0wsOu6YKzStI2eFBDt+Td5O8seVC/oxgRIHqCcl5sf5ratA1nozQBAuv7tphkHg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [s390x] + os: [linux] + + '@oxlint/binding-linux-x64-gnu@1.62.0': + resolution: {integrity: sha512-5z25jcAA0gfKyVwz71A0VXgaPlocPoTAxhlv/hgoK6tlCrfoNuw7haWbDHvGMfjXhdic4EqVXGRv5XsTqFnbRQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + + '@oxlint/binding-linux-x64-musl@1.62.0': + resolution: {integrity: sha512-IWpHmMB6ZDllPvqWDkG6AmXrN7JF5e/c4g/0PuURsmlK+vHoYZPB70rr4u1bn3I4LsKCSpqqfveyx6UCOC8wdg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + + '@oxlint/binding-openharmony-arm64@1.62.0': + resolution: {integrity: sha512-fjlSxxrD5pA594vkyikCS9MnPRjQawW6/BLgyTYkO+73wwPlYjkcZ7LSd974l0Q2zkHQmu4DPvJFLYA7o8xrxQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [openharmony] + + '@oxlint/binding-win32-arm64-msvc@1.62.0': + resolution: {integrity: sha512-EiFXr8loNS0Ul3Gu80+9nr1T8jRmnKocqmHHg16tj5ZqTgUXyb97l2rrspVHdDluyFn9JfR4PoJFdNzw4paHww==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [win32] + + '@oxlint/binding-win32-ia32-msvc@1.62.0': + resolution: {integrity: sha512-IgOFvL73li1bFgab+hThXYA0N2Xms2kV2MvZN95cebV+fmrZ9AVui1JSxfeeqRLo3CpPxKZlzhyq4G0cnaAvIw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ia32] + os: [win32] + + '@oxlint/binding-win32-x64-msvc@1.62.0': + resolution: {integrity: sha512-6hMpyDWQ2zGA1OXFKBrdYMUveUCO8UJhkO6JdwZPd78xIdHZNhjx+pib+4fC2Cljuhjyl0QwA2F3df/bs4Bp6A==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [win32] + + '@pkgr/core@0.2.9': + resolution: {integrity: sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==} + engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} + + '@radix-ui/primitive@1.1.3': + resolution: {integrity: sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg==} + + '@radix-ui/react-collection@1.1.7': + resolution: {integrity: sha512-Fh9rGN0MoI4ZFUNyfFVNU4y9LUz93u9/0K+yLgA2bwRojxM8JU1DyvvMBabnZPBgMWREAJvU2jjVzq+LrFUglw==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-compose-refs@1.1.2': + resolution: {integrity: sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-context@1.1.2': + resolution: {integrity: sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-dialog@1.1.15': + resolution: {integrity: sha512-TCglVRtzlffRNxRMEyR36DGBLJpeusFcgMVD9PZEzAKnUs1lKCgX5u9BmC2Yg+LL9MgZDugFFs1Vl+Jp4t/PGw==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-direction@1.1.1': + resolution: {integrity: sha512-1UEWRX6jnOA2y4H5WczZ44gOOjTEmlqv1uNW4GAJEO5+bauCBhv8snY65Iw5/VOS/ghKN9gr2KjnLKxrsvoMVw==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-dismissable-layer@1.1.11': + resolution: {integrity: sha512-Nqcp+t5cTB8BinFkZgXiMJniQH0PsUt2k51FUhbdfeKvc4ACcG2uQniY/8+h1Yv6Kza4Q7lD7PQV0z0oicE0Mg==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-focus-guards@1.1.3': + resolution: {integrity: sha512-0rFg/Rj2Q62NCm62jZw0QX7a3sz6QCQU0LpZdNrJX8byRGaGVTqbrW9jAoIAHyMQqsNpeZ81YgSizOt5WXq0Pw==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-focus-scope@1.1.7': + resolution: {integrity: sha512-t2ODlkXBQyn7jkl6TNaw/MtVEVvIGelJDCG41Okq/KwUsJBwQ4XVZsHAVUkK4mBv3ewiAS3PGuUWuY2BoK4ZUw==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-id@1.1.1': + resolution: {integrity: sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-portal@1.1.9': + resolution: {integrity: sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-presence@1.1.5': + resolution: {integrity: sha512-/jfEwNDdQVBCNvjkGit4h6pMOzq8bHkopq458dPt2lMjx+eBQUohZNG9A7DtO/O5ukSbxuaNGXMjHicgwy6rQQ==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-primitive@2.1.3': + resolution: {integrity: sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-roving-focus@1.1.11': + resolution: {integrity: sha512-7A6S9jSgm/S+7MdtNDSb+IU859vQqJ/QAtcYQcfFC6W8RS4IxIZDldLR0xqCFZ6DCyrQLjLPsxtTNch5jVA4lA==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-slot@1.2.3': + resolution: {integrity: sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-slot@1.2.4': + resolution: {integrity: sha512-Jl+bCv8HxKnlTLVrcDE8zTMJ09R9/ukw4qBs/oZClOfoQk/cOTbDn+NceXfV7j09YPVQUryJPHurafcSg6EVKA==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-tabs@1.1.13': + resolution: {integrity: sha512-7xdcatg7/U+7+Udyoj2zodtI9H/IIopqo+YOIcZOq1nJwXWBZ9p8xiu5llXlekDbZkca79a/fozEYQXIA4sW6A==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-use-callback-ref@1.1.1': + resolution: {integrity: sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-controllable-state@1.2.2': + resolution: {integrity: sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-effect-event@0.0.2': + resolution: {integrity: sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-escape-keydown@1.1.1': + resolution: {integrity: sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-layout-effect@1.1.1': + resolution: {integrity: sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@react-native-async-storage/async-storage@2.2.0': + resolution: {integrity: sha512-gvRvjR5JAaUZF8tv2Kcq/Gbt3JHwbKFYfmb445rhOj6NUMx3qPLixmDx5pZAyb9at1bYvJ4/eTUipU5aki45xw==} + peerDependencies: + react-native: ^0.0.0-0 || >=0.65 <1.0 + + '@react-native/assets-registry@0.83.6': + resolution: {integrity: sha512-iljb4ue1yWJ3EhySz7EjV6CzSVrI2uNtR8BI2jzP5+QS5E4Cl3fdIJRmVwDEx1pu8uE97PGEusGRHnoaZ9Q3jg==} + engines: {node: '>= 20.19.4'} + + '@react-native/assets-registry@0.85.2': + resolution: {integrity: sha512-kauC/oPaxklU4Y+u9gBfCBJm51qX6WBZq4xx0USCdimtp+G8+554kpygfSWIjoqCJa2o06bWxBEjesiuCv+LzA==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} + + '@react-native/babel-plugin-codegen@0.83.6': + resolution: {integrity: sha512-qfRXsHGeucT5c6mK+8Q7v4Ly3zmygfVmFlEtkiq7q07W1OTreld6nib4rJ/DBEeNiKBoBTuHjWliYGNuDjLFQA==} + engines: {node: '>= 20.19.4'} + + '@react-native/babel-plugin-codegen@0.85.2': + resolution: {integrity: sha512-5Dqn08kRTUIxPLYju9hExI0cR1ESX+P5tEv5yv0q0UZcisRTw0VB8iUWDIph2LdY1i5Dc8PIvuaWMRNCw3vnKg==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} + + '@react-native/babel-preset@0.83.6': + resolution: {integrity: sha512-4/fXFDUvGOObETZq4+SUFkafld6OGgQWut5cQiqVghlhCB5z/p2lVhPgEUr/aTxTzeS3AmN+ztC+GpYPQ7tsTw==} + engines: {node: '>= 20.19.4'} + peerDependencies: + '@babel/core': '*' + + '@react-native/babel-preset@0.85.2': + resolution: {integrity: sha512-7d2yW23eKkVt0FbbnZLxqO7KybGLtQXOuvvcO1NUOYGtjzVh6ihNKn0TIHrhSNpMyHwYLDoiiuj95wLtcg3IwQ==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} + peerDependencies: + '@babel/core': '*' + + '@react-native/codegen@0.83.6': + resolution: {integrity: sha512-doB/Pq6Cf6IjF3wlQXTIiZOnsX9X8mEEk+CdGfyuCwZjWrf7IB8KaZEXXckJmfUcIwvJ9u/a72ZoTTCIoxAc9A==} + engines: {node: '>= 20.19.4'} + peerDependencies: + '@babel/core': '*' + + '@react-native/codegen@0.85.2': + resolution: {integrity: sha512-XCginmxh0//++EXVOEJHBVZxHla294FzLCFF6jXwAUjvXVhqyIKyxhABfz+r4OOmaiuWk4Rtd4arqdAzeHeprg==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} + peerDependencies: + '@babel/core': '*' + + '@react-native/community-cli-plugin@0.83.6': + resolution: {integrity: sha512-Mko6mywoHYJmpBnjwAC95vQWaUUh//71knFadH0BrhHDq2m7i/IrpLwcQsPAy8855ucXflBs5zQyGTpNbPBAaw==} + engines: {node: '>= 20.19.4'} + peerDependencies: + '@react-native-community/cli': '*' + '@react-native/metro-config': '*' + peerDependenciesMeta: + '@react-native-community/cli': + optional: true + '@react-native/metro-config': + optional: true + + '@react-native/community-cli-plugin@0.85.2': + resolution: {integrity: sha512-3KLgSg1kHvBpr93zMaQhvfYTgnCw7yZRED+3J4dMcYjfSjtD0Wf8SofU6uBmAw9JaVYvP43lpdwUpI4p0+ABsg==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} + peerDependencies: + '@react-native-community/cli': '*' + '@react-native/metro-config': 0.85.2 + peerDependenciesMeta: + '@react-native-community/cli': + optional: true + '@react-native/metro-config': + optional: true + + '@react-native/debugger-frontend@0.83.6': + resolution: {integrity: sha512-TyWXEpAjVundrc87fPWg91piOUg75+X9iutcfDe7cO3NrAEYCsl7Z09rKHuiAGkxfG9/rFD13dPsYIixUFkSFA==} + engines: {node: '>= 20.19.4'} + + '@react-native/debugger-frontend@0.85.2': + resolution: {integrity: sha512-j+0b9H5f5hGTLQxHIhJU/b/W6ijuxJF+ZTLHB0se2kzUBNxFKd7DkIc6753qk3CJdiv55vxG3XDgmlpbHxOpmA==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} + + '@react-native/debugger-shell@0.83.6': + resolution: {integrity: sha512-684TJMBCU0l0ZjJWzrnK0HH+ERaM9KLyxyArE1k7BrP+gVl4X9GO0Pi94RoInOxvW/nyV65sOU6Ip1F3ygS0cg==} + engines: {node: '>= 20.19.4'} + + '@react-native/debugger-shell@0.85.2': + resolution: {integrity: sha512-r5BkhqPMfg3LmaZS5zadHmBNVH5h4bhSpv4BEPGfK4gat9HABAMzUzybi+2wpgU3SoHxnyKGdExEJvoqVcjeRg==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} + + '@react-native/dev-middleware@0.83.6': + resolution: {integrity: sha512-22xoddLTelpcVnF385SNH2hdP7X2av5pu7yRl/WnM5jBznbcl0+M9Ce94cj+WVeomsoUF/vlfuB0Ooy+RMlRiA==} + engines: {node: '>= 20.19.4'} + + '@react-native/dev-middleware@0.85.2': + resolution: {integrity: sha512-3J+NaDUg+QEfDeLAUzgaWhpaxEg78g+KwbydlDCewh2G6WnHpsty8XooruxNHzyAsqVWywZMrzmbn78Ctc1O9Q==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} + + '@react-native/gradle-plugin@0.83.6': + resolution: {integrity: sha512-5prXv7WWR1RgZ/kWGZP+mi7/y/IE2ymfOHIZO5Pv14tMOmRAcQSgSYogcRmOiWw5mJs2K0UFeMiQD49ZO9oCug==} + engines: {node: '>= 20.19.4'} + + '@react-native/gradle-plugin@0.85.2': + resolution: {integrity: sha512-YXBOLeAqFrv7XwUeBPTKZeOV1FIxn4AW7UAEitScf3ibC8bu8+6NpJu4HWgbNQHg7vDbbTZVbcOl8EwGxsSq2w==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} + + '@react-native/js-polyfills@0.83.6': + resolution: {integrity: sha512-VSev0LV2i5X0ibduHBSLqKj0YU2F+waCgjl2uvaGHMGCSV1ZRKNFX/vJFqvLwjvdzLbkAZoFT1Rg7k7jDv44UA==} + engines: {node: '>= 20.19.4'} + + '@react-native/js-polyfills@0.85.2': + resolution: {integrity: sha512-esGEAmKVM40DV/yVmNljCKZTIeUo7qXqc+Hwffkv3TG+b3E24xyFovHrbP98gGxZr2ZsEyx+2sKLdXF5asY5nw==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} + + '@react-native/metro-babel-transformer@0.85.2': + resolution: {integrity: sha512-lU9XOGahpHvQff30H5lnvh9RYbVwC1zpSHpl84E+7BD2zj0FvW+pD7MBh7CWbmbWmegjtAb+U/2bokXcDVA+jA==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} + peerDependencies: + '@babel/core': '*' + + '@react-native/metro-config@0.85.2': + resolution: {integrity: sha512-YkTIMfTPeyMUrtpQo/7zd3oybVYJCfTp8626PqoakOvEiWi9PxsUpZ8j44a5GFtOIq8Nc6WWVBiFRn/6qdi1uQ==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} + + '@react-native/normalize-colors@0.74.89': + resolution: {integrity: sha512-qoMMXddVKVhZ8PA1AbUCk83trpd6N+1nF2A6k1i6LsQObyS92fELuk8kU/lQs6M7BsMHwqyLCpQJ1uFgNvIQXg==} + + '@react-native/normalize-colors@0.83.6': + resolution: {integrity: sha512-bTM24b5v4qN3h52oflnv+OujFORn/kVi06WaWhnQQw14/ycilPqIsqsa+DpIBqdBrXxvLa9fXtCRrQtGATZCEw==} + + '@react-native/normalize-colors@0.85.2': + resolution: {integrity: sha512-svuOLtjbFGXDdHsriHXuND5FgHg7XlkOXCbH/8+X4t76YLH6qSTffSIQQrKLDL5mn4EFU+Oh/PNO0/FfpnTOTg==} + + '@react-native/virtualized-lists@0.83.6': + resolution: {integrity: sha512-gNSFXeb4P7qHtauLvl+zESroULIyX6Ltpvau3dhwy/QmfanBv0KUcrIU/7aVXxtWcXgp+54oWJyu2LIrsZ9+LQ==} + engines: {node: '>= 20.19.4'} + peerDependencies: + '@types/react': ^19.2.0 + react: '*' + react-native: '*' + peerDependenciesMeta: + '@types/react': + optional: true + + '@react-native/virtualized-lists@0.85.2': + resolution: {integrity: sha512-wmVKpAlcr+UB0L5SpbrV865EdleUP7I5+X+48e1aRsQK8q+wsTRBXeUwWVip/1l+HZwlZFeO8iOILJ16VRu0Cw==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} + peerDependencies: + '@types/react': ^19.2.0 + react: '*' + react-native: 0.85.2 + peerDependenciesMeta: + '@types/react': + optional: true + + '@react-navigation/bottom-tabs@7.15.10': + resolution: {integrity: sha512-Ao/yYlrpr0cwYYGxt9FDMQk+tTSHNm4WTaszyhroINLdoEMuKH19k1tGFdYbRBKHJx1UIH8kD+EZTYW1w6LL3Q==} + peerDependencies: + '@react-navigation/native': ^7.2.2 + react: '>= 18.2.0' + react-native: '*' + react-native-safe-area-context: '>= 4.0.0' + react-native-screens: '>= 4.0.0' + + '@react-navigation/core@7.17.2': + resolution: {integrity: sha512-Rt2OZwcgOmjv401uLGAKaRM6xo0fiBce/A7LfRHI1oe5FV+KooWcgAoZ2XOtgKj6UzVMuQWt3b2e6rxo/mDJRA==} + peerDependencies: + react: '>= 18.2.0' + + '@react-navigation/elements@2.9.15': + resolution: {integrity: sha512-cyz/pPiyyC6gaTVLsGFc1g0MYgrmuCFqklAWGXMWPscr5YU3ui94vPI4vnZwcsEy0T758TQWLzmS5XudZeRKcA==} + peerDependencies: + '@react-native-masked-view/masked-view': '>= 0.2.0' + '@react-navigation/native': ^7.2.2 + react: '>= 18.2.0' + react-native: '*' + react-native-safe-area-context: '>= 4.0.0' + peerDependenciesMeta: + '@react-native-masked-view/masked-view': + optional: true + + '@react-navigation/native-stack@7.14.12': + resolution: {integrity: sha512-dUfpkrVeVKKV8iqXsmoUp3Rv0iH3YaB3eZwScru/FlcqAp/r3/qA6zEXkGX9hZK+/ziWAPFrf1frBSNbgOYSFQ==} + peerDependencies: + '@react-navigation/native': ^7.2.2 + react: '>= 18.2.0' + react-native: '*' + react-native-safe-area-context: '>= 4.0.0' + react-native-screens: '>= 4.0.0' + + '@react-navigation/native@7.2.2': + resolution: {integrity: sha512-kem1Ko2BcbAjmbQIv66dNmr6EtfDut3QU0qjsVhMnLLhktwyXb6FzZYp8gTrUb6AvkAbaJoi+BF5Pl55pAUa5w==} + peerDependencies: + react: '>= 18.2.0' + react-native: '*' + + '@react-navigation/routers@7.5.3': + resolution: {integrity: sha512-1tJHg4KKRJuQ1/EvJxatrMef3NZXEPzwUIUZ3n1yJ2t7Q97siwRtbynRpQG9/69ebbtiZ8W3ScOZF/OmhvM4Rg==} + + '@rtsao/scc@1.1.0': + resolution: {integrity: sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==} + + '@sinclair/typebox@0.27.10': + resolution: {integrity: sha512-MTBk/3jGLNB2tVxv6uLlFh1iu64iYOQ2PbdOSK3NW8JZsmlaOh2q6sdtKowBhfw8QFLmYNzTW4/oK4uATIi6ZA==} + + '@sinclair/typebox@0.34.49': + resolution: {integrity: sha512-brySQQs7Jtn0joV8Xh9ZV/hZb9Ozb0pmazDIASBkYKCjXrXU3mpcFahmK/z4YDhGkQvP9mWJbVyahdtU5wQA+A==} + + '@sinonjs/commons@3.0.1': + resolution: {integrity: sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==} + + '@sinonjs/fake-timers@10.3.0': + resolution: {integrity: sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==} + + '@testing-library/react-native@13.3.3': + resolution: {integrity: sha512-k6Mjsd9dbZgvY4Bl7P1NIpePQNi+dfYtlJ5voi9KQlynxSyQkfOgJmYGCYmw/aSgH/rUcFvG8u5gd4npzgRDyg==} + engines: {node: '>=18'} + peerDependencies: + jest: '>=29.0.0' + react: '>=18.2.0' + react-native: '>=0.71' + react-test-renderer: '>=18.2.0' + peerDependenciesMeta: + jest: + optional: true + + '@tootallnate/once@2.0.0': + resolution: {integrity: sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==} + engines: {node: '>= 10'} + + '@tsconfig/node18@18.2.6': + resolution: {integrity: sha512-eAWQzAjPj18tKnDzmWstz4OyWewLUNBm9tdoN9LayzoboRktYx3Enk1ZXPmThj55L7c4VWYq/Bzq0A51znZfhw==} + + '@types/babel__core@7.20.5': + resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} + + '@types/babel__generator@7.27.0': + resolution: {integrity: sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==} + + '@types/babel__template@7.4.4': + resolution: {integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==} + + '@types/babel__traverse@7.28.0': + resolution: {integrity: sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==} + + '@types/emscripten@1.41.5': + resolution: {integrity: sha512-cMQm7pxu6BxtHyqJ7mQZ2kXWV5SLmugybFdHCBbJ5eHzOo6VhBckEgAT3//rP5FwPHNPeEiq4SmQ5ucBwsOo4Q==} + + '@types/estree@1.0.8': + resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} + + '@types/graceful-fs@4.1.9': + resolution: {integrity: sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==} + + '@types/hammerjs@2.0.46': + resolution: {integrity: sha512-ynRvcq6wvqexJ9brDMS4BnBLzmr0e14d6ZJTEShTBWKymQiHwlAyGu0ZPEFI2Fh1U53F7tN9ufClWM5KvqkKOw==} + + '@types/istanbul-lib-coverage@2.0.6': + resolution: {integrity: sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==} + + '@types/istanbul-lib-report@3.0.3': + resolution: {integrity: sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==} + + '@types/istanbul-reports@3.0.4': + resolution: {integrity: sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==} + + '@types/jest@29.5.14': + resolution: {integrity: sha512-ZN+4sdnLUbo8EVvVc2ao0GFW6oVrQRPn4K2lglySj7APvSrgzxHiNNK99us4WDMi57xxA2yggblIAMNhXOotLQ==} + + '@types/jsdom@20.0.1': + resolution: {integrity: sha512-d0r18sZPmMQr1eG35u12FZfhIXNrnsPU/g5wvRKCUf/tOGilKKwYMYGqh33BNR6ba+2gkHw1EUiHoN3mn7E5IQ==} + + '@types/json-schema@7.0.15': + resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} + + '@types/json5@0.0.29': + resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==} + + '@types/node@25.6.0': + resolution: {integrity: sha512-+qIYRKdNYJwY3vRCZMdJbPLJAtGjQBudzZzdzwQYkEPQd+PJGixUL5QfvCLDaULoLv+RhT3LDkwEfKaAkgSmNQ==} + + '@types/react-native@0.73.0': + resolution: {integrity: sha512-6ZRPQrYM72qYKGWidEttRe6M5DZBEV5F+MHMHqd4TTYx0tfkcdrUFGdef6CCxY0jXU7wldvd/zA/b0A/kTeJmA==} + deprecated: This is a stub types definition. react-native provides its own type definitions, so you do not need this installed. + + '@types/react@19.2.14': + resolution: {integrity: sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==} + + '@types/stack-utils@2.0.3': + resolution: {integrity: sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==} + + '@types/tough-cookie@4.0.5': + resolution: {integrity: sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==} + + '@types/ws@8.18.1': + resolution: {integrity: sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==} + + '@types/yargs-parser@21.0.3': + resolution: {integrity: sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==} + + '@types/yargs@17.0.35': + resolution: {integrity: sha512-qUHkeCyQFxMXg79wQfTtfndEC+N9ZZg76HJftDJp+qH2tV7Gj4OJi7l+PiWwJ+pWtW8GwSmqsDj/oymhrTWXjg==} + + '@typescript-eslint/eslint-plugin@8.59.1': + resolution: {integrity: sha512-BOziFIfE+6osHO9FoJG4zjoHUcvI7fTNBSpdAwrNH0/TLvzjsk2oo8XSSOT2HhqUyhZPfHv4UOffoJ9oEEQ7Ag==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + '@typescript-eslint/parser': ^8.59.1 + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/parser@8.59.1': + resolution: {integrity: sha512-HDQH9O/47Dxi1ceDhBXdaldtf/WV9yRYMjbjCuNk3qnaTD564qwv61Y7+gTxwxRKzSrgO5uhtw584igXVuuZkA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/project-service@8.59.1': + resolution: {integrity: sha512-+MuHQlHiEr00Of/IQbE/MmEoi44znZHbR/Pz7Opq4HryUOlRi+/44dro9Ycy8Fyo+/024IWtw8m4JUMCGTYxDg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/scope-manager@8.59.1': + resolution: {integrity: sha512-LwuHQI4pDOYVKvmH2dkaJo6YZCSgouVgnS/z7yBPKBMvgtBvyLqiLy9Z6b7+m/TRcX1NFYUqZetI5Y+aT4GEfg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/tsconfig-utils@8.59.1': + resolution: {integrity: sha512-/0nEyPbX7gRsk0Uwfe4ALwwgxuA66d/l2mhRDNlAvaj4U3juhUtJNq0DsY8M2AYwwb9rEq2hrC3IcIcEt++iJA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/type-utils@8.59.1': + resolution: {integrity: sha512-klWPBR2ciQHS3f++ug/mVnWKPjBUo7icEL3FAO1lhAR1Z1i5NQYZ1EannMSRYcq5qCv5wNALlXr6fksRHyYl7w==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/types@8.59.1': + resolution: {integrity: sha512-ZDCjgccSdYPw5Bxh+my4Z0lJU96ZDN7jbBzvmEn0FZx3RtU1C7VWl6NbDx94bwY3V5YsgwRzJPOgeY2Q/nLG8A==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/typescript-estree@8.59.1': + resolution: {integrity: sha512-OUd+vJS05sSkOip+BkZ/2NS8RMxrAAJemsC6vU3kmfLyeaJT0TftHkV9mcx2107MmsBVXXexhVu4F0TZXyMl4g==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/utils@8.59.1': + resolution: {integrity: sha512-3pIeoXhCeYH9FSCBI8P3iNwJlGuzPlYKkTlen2O9T1DSeeg8UG8jstq6BLk+Mda0qup7mgk4z4XL4OzRaxZ8LA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/visitor-keys@8.59.1': + resolution: {integrity: sha512-LdDNl6C5iJExcM0Yh0PwAIBb9PrSiCsWamF/JyEZawm3kFDnRoaq3LGE4bpyRao/fWeGKKyw7icx0YxrLFC5Cg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@ungap/structured-clone@1.3.0': + resolution: {integrity: sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==} + + '@xmldom/xmldom@0.8.13': + resolution: {integrity: sha512-KRYzxepc14G/CEpEGc3Yn+JKaAeT63smlDr+vjB8jRfgTBBI9wRj/nkQEO+ucV8p8I9bfKLWp37uHgFrbntPvw==} + engines: {node: '>=10.0.0'} + + '@xmldom/xmldom@0.9.10': + resolution: {integrity: sha512-A9gOqLdi6cV4ibazAjcQufGj0B1y/vDqYrcuP6d/6x8P27gRS8643Dj9o1dEKtB6O7fwxb2FgBmJS2mX7gpvdw==} + engines: {node: '>=14.6'} + + abab@2.0.6: + resolution: {integrity: sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==} + deprecated: Use your platform's native atob() and btoa() methods instead + + abort-controller@3.0.0: + resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==} + engines: {node: '>=6.5'} + + accepts@1.3.8: + resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==} + engines: {node: '>= 0.6'} + + accepts@2.0.0: + resolution: {integrity: sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==} + engines: {node: '>= 0.6'} + + acorn-globals@7.0.1: + resolution: {integrity: sha512-umOSDSDrfHbTNPuNpC2NSnnA3LUrqpevPb4T9jRx4MagXNS0rs+gwiTcAvqCRmsD6utzsrzNt+ebm00SNWiC3Q==} + + acorn-jsx@5.3.2: + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + + acorn-walk@8.3.5: + resolution: {integrity: sha512-HEHNfbars9v4pgpW6SO1KSPkfoS0xVOM/9UzkJltjlsHZmJasxg8aXkuZa7SMf8vKGIBhpUsPluQSqhJFCqebw==} + engines: {node: '>=0.4.0'} + + acorn@8.16.0: + resolution: {integrity: sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==} + engines: {node: '>=0.4.0'} + hasBin: true + + agent-base@6.0.2: + resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==} + engines: {node: '>= 6.0.0'} + + agent-base@7.1.4: + resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==} + engines: {node: '>= 14'} + + ajv@6.15.0: + resolution: {integrity: sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==} + + anser@1.4.10: + resolution: {integrity: sha512-hCv9AqTQ8ycjpSd3upOJd7vFwW1JaoYQ7tpham03GJ1ca8/65rqn0RpaWpItOAd6ylW9wAw6luXYPJIyPFVOww==} + + ansi-escapes@4.3.2: + resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==} + engines: {node: '>=8'} + + ansi-escapes@6.2.1: + resolution: {integrity: sha512-4nJ3yixlEthEJ9Rk4vPcdBRkZvQZlYyu8j4/Mqz5sgIkddmEnH2Yj2ZrnP9S3tQOvSNRUIgVNF/1yPpRAGNRig==} + engines: {node: '>=14.16'} + + ansi-regex@4.1.1: + resolution: {integrity: sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==} + engines: {node: '>=6'} + + ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + + ansi-regex@6.2.2: + resolution: {integrity: sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==} + engines: {node: '>=12'} + + ansi-styles@3.2.1: + resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==} + engines: {node: '>=4'} + + ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + + ansi-styles@5.2.0: + resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} + engines: {node: '>=10'} + + anymatch@3.1.3: + resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} + engines: {node: '>= 8'} + + arg@5.0.2: + resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==} + + argparse@1.0.10: + resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} + + argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + + aria-hidden@1.2.6: + resolution: {integrity: sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==} + engines: {node: '>=10'} + + array-buffer-byte-length@1.0.2: + resolution: {integrity: sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==} + engines: {node: '>= 0.4'} + + array-includes@3.1.9: + resolution: {integrity: sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ==} + engines: {node: '>= 0.4'} + + array.prototype.findlast@1.2.5: + resolution: {integrity: sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==} + engines: {node: '>= 0.4'} + + array.prototype.findlastindex@1.2.6: + resolution: {integrity: sha512-F/TKATkzseUExPlfvmwQKGITM3DGTK+vkAsCZoDc5daVygbJBnjEUCbgkAvVFsgfXfX4YIqZ/27G3k3tdXrTxQ==} + engines: {node: '>= 0.4'} + + array.prototype.flat@1.3.3: + resolution: {integrity: sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==} + engines: {node: '>= 0.4'} + + array.prototype.flatmap@1.3.3: + resolution: {integrity: sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==} + engines: {node: '>= 0.4'} + + array.prototype.tosorted@1.1.4: + resolution: {integrity: sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==} + engines: {node: '>= 0.4'} + + arraybuffer.prototype.slice@1.0.4: + resolution: {integrity: sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==} + engines: {node: '>= 0.4'} + + asap@2.0.6: + resolution: {integrity: sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==} + + async-function@1.0.0: + resolution: {integrity: sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==} + engines: {node: '>= 0.4'} + + asynckit@0.4.0: + resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} + + available-typed-arrays@1.0.7: + resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} + engines: {node: '>= 0.4'} + + babel-jest@29.7.0: + resolution: {integrity: sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + peerDependencies: + '@babel/core': ^7.8.0 + + babel-plugin-dynamic-import-node@2.3.3: + resolution: {integrity: sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ==} + + babel-plugin-istanbul@6.1.1: + resolution: {integrity: sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==} + engines: {node: '>=8'} + + babel-plugin-jest-hoist@29.6.3: + resolution: {integrity: sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + babel-plugin-polyfill-corejs2@0.4.17: + resolution: {integrity: sha512-aTyf30K/rqAsNwN76zYrdtx8obu0E4KoUME29B1xj+B3WxgvWkp943vYQ+z8Mv3lw9xHXMHpvSPOBxzAkIa94w==} + peerDependencies: + '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 + + babel-plugin-polyfill-corejs3@0.13.0: + resolution: {integrity: sha512-U+GNwMdSFgzVmfhNm8GJUX88AadB3uo9KpJqS3FaqNIPKgySuvMb+bHPsOmmuWyIcuqZj/pzt1RUIUZns4y2+A==} + peerDependencies: + '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 + + babel-plugin-polyfill-corejs3@0.14.2: + resolution: {integrity: sha512-coWpDLJ410R781Npmn/SIBZEsAetR4xVi0SxLMXPaMO4lSf1MwnkGYMtkFxew0Dn8B3/CpbpYxN0JCgg8mn67g==} + peerDependencies: + '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 + + babel-plugin-polyfill-regenerator@0.6.8: + resolution: {integrity: sha512-M762rNHfSF1EV3SLtnCJXFoQbbIIz0OyRwnCmV0KPC7qosSfCO0QLTSuJX3ayAebubhE6oYBAYPrBA5ljowaZg==} + peerDependencies: + '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 + + babel-plugin-react-compiler@1.0.0: + resolution: {integrity: sha512-Ixm8tFfoKKIPYdCCKYTsqv+Fd4IJ0DQqMyEimo+pxUOMUR9cVPlwTrFt9Avu+3cb6Zp3mAzl+t1MrG2fxxKsxw==} + + babel-plugin-react-native-web@0.21.2: + resolution: {integrity: sha512-SPD0J6qjJn8231i0HZhlAGH6NORe+QvRSQM2mwQEzJ2Fb3E4ruWTiiicPlHjmeWShDXLcvoorOCXjeR7k/lyWA==} + + babel-plugin-syntax-hermes-parser@0.32.0: + resolution: {integrity: sha512-m5HthL++AbyeEA2FcdwOLfVFvWYECOBObLHNqdR8ceY4TsEdn4LdX2oTvbB2QJSSElE2AWA/b2MXZ/PF/CqLZg==} + + babel-plugin-syntax-hermes-parser@0.32.1: + resolution: {integrity: sha512-HgErPZTghW76Rkq9uqn5ESeiD97FbqpZ1V170T1RG2RDp+7pJVQV2pQJs7y5YzN0/gcT6GM5ci9apRnIwuyPdQ==} + + babel-plugin-syntax-hermes-parser@0.33.3: + resolution: {integrity: sha512-/Z9xYdaJ1lC0pT9do6TqCqhOSLfZ5Ot8D5za1p+feEfWYupCOfGbhhEXN9r2ZgJtDNUNRw/Z+T2CvAGKBqtqWA==} + + babel-plugin-transform-flow-enums@0.0.2: + resolution: {integrity: sha512-g4aaCrDDOsWjbm0PUUeVnkcVd6AKJsVc/MbnPhEotEpkeJQP6b8nzewohQi7+QS8UyPehOhGWn0nOwjvWpmMvQ==} + + babel-preset-current-node-syntax@1.2.0: + resolution: {integrity: sha512-E/VlAEzRrsLEb2+dv8yp3bo4scof3l9nR4lrld+Iy5NyVqgVYUJnDAmunkhPMisRI32Qc4iRiz425d8vM++2fg==} + peerDependencies: + '@babel/core': ^7.0.0 || ^8.0.0-0 + + babel-preset-expo@55.0.18: + resolution: {integrity: sha512-zmDwKxCFBTe4e/jQXuITRUZlbl8HTZOhsUlwcHGjwEUB0lKQfRdaSYXZckQ+jMOBC34MrOl3Cs7/6F6vNbj5Pw==} + peerDependencies: + '@babel/runtime': ^7.20.0 + expo: '*' + expo-widgets: ^55.0.14 + react-refresh: '>=0.14.0 <1.0.0' + peerDependenciesMeta: + '@babel/runtime': + optional: true + expo: + optional: true + expo-widgets: + optional: true + + babel-preset-jest@29.6.3: + resolution: {integrity: sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + peerDependencies: + '@babel/core': ^7.0.0 + + badgin@1.2.3: + resolution: {integrity: sha512-NQGA7LcfCpSzIbGRbkgjgdWkjy7HI+Th5VLxTJfW5EeaAf3fnS+xWQaQOCYiny+q6QSvxqoSO04vCx+4u++EJw==} + + balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + + balanced-match@4.0.4: + resolution: {integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==} + engines: {node: 18 || 20 || >=22} + + barcode-detector@3.1.2: + resolution: {integrity: sha512-Q5kjXpVH5I3ItykNzbWmfWnNryFN1ZTWp10k9/PKJuS0RnoKR7jTrHEJODR4fn04bRomq7TJwie/Dr9fj/GoGQ==} + + base64-js@1.5.1: + resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} + + baseline-browser-mapping@2.10.23: + resolution: {integrity: sha512-xwVXGqevyKPsiuQdLj+dZMVjidjJV508TBqexND5HrF89cGdCYCJFB3qhcxRHSeMctdCfbR1jrxBajhDy7o29g==} + engines: {node: '>=6.0.0'} + hasBin: true + + better-opn@3.0.2: + resolution: {integrity: sha512-aVNobHnJqLiUelTaHat9DZ1qM2w0C0Eym4LPI/3JxOnSokGVdsl1T1kN7TFvsEAD8G47A6VKQ0TVHqbBnYMJlQ==} + engines: {node: '>=12.0.0'} + + big-integer@1.6.52: + resolution: {integrity: sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg==} + engines: {node: '>=0.6'} + + binary-extensions@2.3.0: + resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} + engines: {node: '>=8'} + + boolbase@1.0.0: + resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} + + bplist-creator@0.1.0: + resolution: {integrity: sha512-sXaHZicyEEmY86WyueLTQesbeoH/mquvarJaQNbjuOQO+7gbFcDEWqKmcWA4cOTLzFlfgvkiVxolk1k5bBIpmg==} + + bplist-parser@0.3.1: + resolution: {integrity: sha512-PyJxiNtA5T2PlLIeBot4lbp7rj4OadzjnMZD/G5zuBNt8ei/yCU7+wW0h2bag9vr8c+/WuRWmSxbqAl9hL1rBA==} + engines: {node: '>= 5.10.0'} + + bplist-parser@0.3.2: + resolution: {integrity: sha512-apC2+fspHGI3mMKj+dGevkGo/tCqVB8jMb6i+OX+E29p0Iposz07fABkRIfVUPNd5A5VbuOz1bZbnmkKLYF+wQ==} + engines: {node: '>= 5.10.0'} + + brace-expansion@1.1.14: + resolution: {integrity: sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==} + + brace-expansion@5.0.5: + resolution: {integrity: sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==} + engines: {node: 18 || 20 || >=22} + + braces@3.0.3: + resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} + engines: {node: '>=8'} + + browserslist@4.28.2: + resolution: {integrity: sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + + bs-logger@0.2.6: + resolution: {integrity: sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==} + engines: {node: '>= 6'} + + bser@2.1.1: + resolution: {integrity: sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==} + + buffer-from@1.1.2: + resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} + + bytes@3.1.2: + resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} + engines: {node: '>= 0.8'} + + call-bind-apply-helpers@1.0.2: + resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} + engines: {node: '>= 0.4'} + + call-bind@1.0.9: + resolution: {integrity: sha512-a/hy+pNsFUTR+Iz8TCJvXudKVLAnz/DyeSUo10I5yvFDQJBFU2s9uqQpoSrJlroHUKoKqzg+epxyP9lqFdzfBQ==} + engines: {node: '>= 0.4'} + + call-bound@1.0.4: + resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==} + engines: {node: '>= 0.4'} + + callsites@3.1.0: + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} + engines: {node: '>=6'} + + camelcase@5.3.1: + resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==} + engines: {node: '>=6'} + + camelcase@6.3.0: + resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} + engines: {node: '>=10'} + + caniuse-lite@1.0.30001791: + resolution: {integrity: sha512-yk0l/YSrOnFZk3UROpDLQD9+kC1l4meK/wed583AXrzoarMGJcbRi2Q4RaUYbKxYAsZ8sWmaSa/DsLmdBeI1vQ==} + + chalk@2.4.2: + resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} + engines: {node: '>=4'} + + chalk@3.0.0: + resolution: {integrity: sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==} + engines: {node: '>=8'} + + chalk@4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + + char-regex@1.0.2: + resolution: {integrity: sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==} + engines: {node: '>=10'} + + char-regex@2.0.2: + resolution: {integrity: sha512-cbGOjAptfM2LVmWhwRFHEKTPkLwNddVmuqYZQt895yXwAsWsXObCG+YN4DGQ/JBtT4GP1a1lPPdio2z413LmTg==} + engines: {node: '>=12.20'} + + chokidar@3.6.0: + resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} + engines: {node: '>= 8.10.0'} + + chrome-launcher@0.15.2: + resolution: {integrity: sha512-zdLEwNo3aUVzIhKhTtXfxhdvZhUghrnmkvcAq2NoDd+LeOHKf03H5jwZ8T/STsAlzyALkBVK552iaG1fGf1xVQ==} + engines: {node: '>=12.13.0'} + hasBin: true + + chromium-edge-launcher@0.2.0: + resolution: {integrity: sha512-JfJjUnq25y9yg4FABRRVPmBGWPZZi+AQXT4mxupb67766/0UlhG8PAZCz6xzEMXTbW3CsSoE8PcCWA49n35mKg==} + + chromium-edge-launcher@0.3.0: + resolution: {integrity: sha512-p03azHlGjtyRvFEee3cyvtsRYdniSkwjkzmM/KmVnqT5d7QkkwpJBhis/zCLMYdQMVJ5tt140TBNqqrZPaWeFA==} + + ci-info@2.0.0: + resolution: {integrity: sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==} + + ci-info@3.9.0: + resolution: {integrity: sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==} + engines: {node: '>=8'} + + cjs-module-lexer@1.4.3: + resolution: {integrity: sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==} + + cli-cursor@2.1.0: + resolution: {integrity: sha512-8lgKz8LmCRYZZQDpRyT2m5rKJ08TnU4tR9FFFW2rxpxR1FzWi4PQ/NfyODchAatHaUgnSPVcx/R5w6NuTBzFiw==} + engines: {node: '>=4'} + + cli-spinners@2.9.2: + resolution: {integrity: sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==} + engines: {node: '>=6'} + + client-only@0.0.1: + resolution: {integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==} + + cliui@8.0.1: + resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} + engines: {node: '>=12'} + + clone@1.0.4: + resolution: {integrity: sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==} + engines: {node: '>=0.8'} + + co@4.6.0: + resolution: {integrity: sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==} + engines: {iojs: '>= 1.0.0', node: '>= 0.12.0'} + + collect-v8-coverage@1.0.3: + resolution: {integrity: sha512-1L5aqIkwPfiodaMgQunkF1zRhNqifHBmtbbbxcr6yVxxBnliw4TDOW6NxpO8DJLgJ16OT+Y4ztZqP6p/FtXnAw==} + + color-convert@1.9.3: + resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} + + color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + + color-name@1.1.3: + resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==} + + color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + + color-string@1.9.1: + resolution: {integrity: sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==} + + color@4.2.3: + resolution: {integrity: sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==} + engines: {node: '>=12.5.0'} + + combined-stream@1.0.8: + resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} + engines: {node: '>= 0.8'} + + commander@12.1.0: + resolution: {integrity: sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==} + engines: {node: '>=18'} + + commander@2.20.3: + resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} + + commander@6.2.1: + resolution: {integrity: sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==} + engines: {node: '>= 6'} + + commander@7.2.0: + resolution: {integrity: sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==} + engines: {node: '>= 10'} + + compressible@2.0.18: + resolution: {integrity: sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==} + engines: {node: '>= 0.6'} + + compression@1.8.1: + resolution: {integrity: sha512-9mAqGPHLakhCLeNyxPkK4xVo746zQ/czLH1Ky+vkitMnWfWZps8r0qXuwhwizagCRttsL4lfG4pIOvaWLpAP0w==} + engines: {node: '>= 0.8.0'} + + concat-map@0.0.1: + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + + connect@3.7.0: + resolution: {integrity: sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ==} + engines: {node: '>= 0.10.0'} + + convert-source-map@2.0.0: + resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + + core-js-compat@3.49.0: + resolution: {integrity: sha512-VQXt1jr9cBz03b331DFDCCP90b3fanciLkgiOoy8SBHy06gNf+vQ1A3WFLqG7I8TipYIKeYK9wxd0tUrvHcOZA==} + + create-jest@29.7.0: + resolution: {integrity: sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + hasBin: true + + cross-fetch@3.2.0: + resolution: {integrity: sha512-Q+xVJLoGOeIMXZmbUK4HYk+69cQH6LudR0Vu/pRm2YlU/hDV9CiS0gKUMaWY5f2NeUH9C1nV3bsTlCo0FsTV1Q==} + + cross-spawn@7.0.6: + resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} + engines: {node: '>= 8'} + + css-in-js-utils@3.1.0: + resolution: {integrity: sha512-fJAcud6B3rRu+KHYk+Bwf+WFL2MDCJJ1XG9x137tJQ0xYxor7XziQtuGFbWNdqrvF4Tk26O3H73nfVqXt/fW1A==} + + css-select@5.2.2: + resolution: {integrity: sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==} + + css-tree@1.1.3: + resolution: {integrity: sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==} + engines: {node: '>=8.0.0'} + + css-what@6.2.2: + resolution: {integrity: sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==} + engines: {node: '>= 6'} + + cssom@0.3.8: + resolution: {integrity: sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==} + + cssom@0.5.0: + resolution: {integrity: sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw==} + + cssstyle@2.3.0: + resolution: {integrity: sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==} + engines: {node: '>=8'} + + csstype@3.2.3: + resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} + + data-urls@3.0.2: + resolution: {integrity: sha512-Jy/tj3ldjZJo63sVAvg6LHt2mHvl4V6AgRAmNDtLdm7faqtsx+aJG42rsyCo9JCoRVKwPFzKlIPx3DIibwSIaQ==} + engines: {node: '>=12'} + + data-view-buffer@1.0.2: + resolution: {integrity: sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==} + engines: {node: '>= 0.4'} + + data-view-byte-length@1.0.2: + resolution: {integrity: sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==} + engines: {node: '>= 0.4'} + + data-view-byte-offset@1.0.1: + resolution: {integrity: sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==} + engines: {node: '>= 0.4'} + + debug@2.6.9: + resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + debug@3.2.7: + resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + debug@4.4.3: + resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + decimal.js@10.6.0: + resolution: {integrity: sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==} + + decode-uri-component@0.2.2: + resolution: {integrity: sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==} + engines: {node: '>=0.10'} + + dedent@1.7.2: + resolution: {integrity: sha512-WzMx3mW98SN+zn3hgemf4OzdmyNhhhKz5Ay0pUfQiMQ3e1g+xmTJWp/pKdwKVXhdSkAEGIIzqeuWrL3mV/AXbA==} + peerDependencies: + babel-plugin-macros: ^3.1.0 + peerDependenciesMeta: + babel-plugin-macros: + optional: true + + deep-is@0.1.4: + resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} + + deepmerge@4.3.1: + resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} + engines: {node: '>=0.10.0'} + + defaults@1.0.4: + resolution: {integrity: sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==} + + define-data-property@1.1.4: + resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} + engines: {node: '>= 0.4'} + + define-lazy-prop@2.0.0: + resolution: {integrity: sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==} + engines: {node: '>=8'} + + define-properties@1.2.1: + resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} + engines: {node: '>= 0.4'} + + delayed-stream@1.0.0: + resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} + engines: {node: '>=0.4.0'} + + depd@2.0.0: + resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} + engines: {node: '>= 0.8'} + + destroy@1.2.0: + resolution: {integrity: sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==} + engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} + + detect-libc@2.1.2: + resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} + engines: {node: '>=8'} + + detect-newline@3.1.0: + resolution: {integrity: sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==} + engines: {node: '>=8'} + + detect-node-es@1.1.0: + resolution: {integrity: sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==} + + diff-sequences@29.6.3: + resolution: {integrity: sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + dnssd-advertise@1.1.4: + resolution: {integrity: sha512-AmGyK9WpNf06WeP5TjHZq/wNzP76OuEeaiTlKr9E/EEelYLczywUKoqRz+DPRq/ErssjT4lU+/W7wzJW+7K/ZA==} + + doctrine@2.1.0: + resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==} + engines: {node: '>=0.10.0'} + + dom-serializer@2.0.0: + resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==} + + domelementtype@2.3.0: + resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==} + + domexception@4.0.0: + resolution: {integrity: sha512-A2is4PLG+eeSfoTMA95/s4pvAoSo2mKtiM5jlHkAVewmiO8ISFTFKZjH7UAM1Atli/OT/7JHOrJRJiMKUZKYBw==} + engines: {node: '>=12'} + deprecated: Use your platform's native DOMException instead + + domhandler@5.0.3: + resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==} + engines: {node: '>= 4'} + + domutils@3.2.2: + resolution: {integrity: sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==} + + dunder-proto@1.0.1: + resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} + engines: {node: '>= 0.4'} + + ee-first@1.1.1: + resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} + + electron-to-chromium@1.5.344: + resolution: {integrity: sha512-4MxfbmNDm+KPh066EZy+eUnkcDPcZ35wNmOWzFuh/ijvHsve6kbLTLURy88uCNK5FbpN+yk2nQY6BYh1GEt+wg==} + + emittery@0.13.1: + resolution: {integrity: sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==} + engines: {node: '>=12'} + + emoji-regex@8.0.0: + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + + encodeurl@1.0.2: + resolution: {integrity: sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==} + engines: {node: '>= 0.8'} + + encodeurl@2.0.0: + resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==} + engines: {node: '>= 0.8'} + + enhanced-resolve@5.21.0: + resolution: {integrity: sha512-otxSQPw4lkOZWkHpB3zaEQs6gWYEsmX4xQF68ElXC/TWvGxGMSGOvoNbaLXm6/cS/fSfHtsEdw90y20PCd+sCA==} + engines: {node: '>=10.13.0'} + + entities@4.5.0: + resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} + engines: {node: '>=0.12'} + + entities@6.0.1: + resolution: {integrity: sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==} + engines: {node: '>=0.12'} + + error-ex@1.3.4: + resolution: {integrity: sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==} + + error-stack-parser@2.1.4: + resolution: {integrity: sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ==} + + es-abstract@1.24.2: + resolution: {integrity: sha512-2FpH9Q5i2RRwyEP1AylXe6nYLR5OhaJTZwmlcP0dL/+JCbgg7yyEo/sEK6HeGZRf3dFpWwThaRHVApXSkW3xeg==} + engines: {node: '>= 0.4'} + + es-define-property@1.0.1: + resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} + engines: {node: '>= 0.4'} + + es-errors@1.3.0: + resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} + engines: {node: '>= 0.4'} + + es-iterator-helpers@1.3.2: + resolution: {integrity: sha512-HVLACW1TppGYjJ8H6/jqH/pqOtKRw6wMlrB23xfExmFWxFquAIWCmwoLsOyN96K4a5KbmOf5At9ZUO3GZbetAw==} + engines: {node: '>= 0.4'} + + es-object-atoms@1.1.1: + resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} + engines: {node: '>= 0.4'} + + es-set-tostringtag@2.1.0: + resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} + engines: {node: '>= 0.4'} + + es-shim-unscopables@1.1.0: + resolution: {integrity: sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==} + engines: {node: '>= 0.4'} + + es-to-primitive@1.3.0: + resolution: {integrity: sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==} + engines: {node: '>= 0.4'} + + esbuild@0.27.7: + resolution: {integrity: sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w==} + engines: {node: '>=18'} + hasBin: true + + escalade@3.2.0: + resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} + engines: {node: '>=6'} + + escape-html@1.0.3: + resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} + + escape-string-regexp@1.0.5: + resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} + engines: {node: '>=0.8.0'} + + escape-string-regexp@2.0.0: + resolution: {integrity: sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==} + engines: {node: '>=8'} + + escape-string-regexp@4.0.0: + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} + engines: {node: '>=10'} + + escodegen@2.1.0: + resolution: {integrity: sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==} + engines: {node: '>=6.0'} + hasBin: true + + eslint-compat-utils@0.5.1: + resolution: {integrity: sha512-3z3vFexKIEnjHE3zCMRo6fn/e44U7T1khUjg+Hp0ZQMCigh28rALD0nPFBcGZuiLC5rLZa2ubQHDRln09JfU2Q==} + engines: {node: '>=12'} + peerDependencies: + eslint: '>=6.0.0' + + eslint-config-prettier@9.1.2: + resolution: {integrity: sha512-iI1f+D2ViGn+uvv5HuHVUamg8ll4tN+JRHGc6IJi4TP9Kl976C57fzPXgseXNs8v0iA8aSJpHsTWjDb9QJamGQ==} + hasBin: true + peerDependencies: + eslint: '>=7.0.0' + + eslint-config-universe@15.0.3: + resolution: {integrity: sha512-fUMsNXp7GJBu7Sz9PXFBbXhkiixdQ5sbnViFIBbk6ORAfeokczJ+eVv5HQ2gwxPQdbfJarpkO9WZDtxIvJnEGw==} + peerDependencies: + eslint: '>=8.10' + prettier: '>=3' + peerDependenciesMeta: + prettier: + optional: true + + eslint-import-resolver-node@0.3.10: + resolution: {integrity: sha512-tRrKqFyCaKict5hOd244sL6EQFNycnMQnBe+j8uqGNXYzsImGbGUU4ibtoaBmv5FLwJwcFJNeg1GeVjQfbMrDQ==} + + eslint-module-utils@2.12.1: + resolution: {integrity: sha512-L8jSWTze7K2mTg0vos/RuLRS5soomksDPoJLXIslC7c8Wmut3bx7CPpJijDcBZtxQ5lrbUdM+s0OlNbz0DCDNw==} + engines: {node: '>=4'} + peerDependencies: + '@typescript-eslint/parser': '*' + eslint: '*' + eslint-import-resolver-node: '*' + eslint-import-resolver-typescript: '*' + eslint-import-resolver-webpack: '*' + peerDependenciesMeta: + '@typescript-eslint/parser': + optional: true + eslint: + optional: true + eslint-import-resolver-node: + optional: true + eslint-import-resolver-typescript: + optional: true + eslint-import-resolver-webpack: + optional: true + + eslint-plugin-es-x@7.8.0: + resolution: {integrity: sha512-7Ds8+wAAoV3T+LAKeu39Y5BzXCrGKrcISfgKEqTS4BDN8SFEDQd0S43jiQ8vIa3wUKD07qitZdfzlenSi8/0qQ==} + engines: {node: ^14.18.0 || >=16.0.0} + peerDependencies: + eslint: '>=8' + + eslint-plugin-es@3.0.1: + resolution: {integrity: sha512-GUmAsJaN4Fc7Gbtl8uOBlayo2DqhwWvEzykMHSCZHU3XdJ+NSzzZcVhXh3VxX5icqQ+oQdIEawXX8xkR3mIFmQ==} + engines: {node: '>=8.10.0'} + peerDependencies: + eslint: '>=4.19.1' + + eslint-plugin-import@2.32.0: + resolution: {integrity: sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==} + engines: {node: '>=4'} + peerDependencies: + '@typescript-eslint/parser': '*' + eslint: ^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9 + peerDependenciesMeta: + '@typescript-eslint/parser': + optional: true + + eslint-plugin-n@17.24.0: + resolution: {integrity: sha512-/gC7/KAYmfNnPNOb3eu8vw+TdVnV0zhdQwexsw6FLXbhzroVj20vRn2qL8lDWDGnAQ2J8DhdfvXxX9EoxvERvw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: '>=8.23.0' + + eslint-plugin-node@11.1.0: + resolution: {integrity: sha512-oUwtPJ1W0SKD0Tr+wqu92c5xuCeQqB3hSCHasn/ZgjFdA9iDGNkNf2Zi9ztY7X+hNuMib23LNGRm6+uN+KLE3g==} + engines: {node: '>=8.10.0'} + peerDependencies: + eslint: '>=5.16.0' + + eslint-plugin-prettier@5.5.5: + resolution: {integrity: sha512-hscXkbqUZ2sPithAuLm5MXL+Wph+U7wHngPBv9OMWwlP8iaflyxpjTYZkmdgB4/vPIhemRlBEoLrH7UC1n7aUw==} + engines: {node: ^14.18.0 || >=16.0.0} + peerDependencies: + '@types/eslint': '>=8.0.0' + eslint: '>=8.0.0' + eslint-config-prettier: '>= 7.0.0 <10.0.0 || >=10.1.0' + prettier: '>=3.0.0' + peerDependenciesMeta: + '@types/eslint': + optional: true + eslint-config-prettier: + optional: true + + eslint-plugin-react-hooks@5.2.0: + resolution: {integrity: sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg==} + engines: {node: '>=10'} + peerDependencies: + eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 + + eslint-plugin-react@7.37.5: + resolution: {integrity: sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA==} + engines: {node: '>=4'} + peerDependencies: + eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7 + + eslint-scope@8.4.0: + resolution: {integrity: sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + eslint-utils@2.1.0: + resolution: {integrity: sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==} + engines: {node: '>=6'} + + eslint-visitor-keys@1.3.0: + resolution: {integrity: sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==} + engines: {node: '>=4'} + + eslint-visitor-keys@3.4.3: + resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + eslint-visitor-keys@4.2.1: + resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + eslint-visitor-keys@5.0.1: + resolution: {integrity: sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + + eslint@9.39.4: + resolution: {integrity: sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + hasBin: true + peerDependencies: + jiti: '*' + peerDependenciesMeta: + jiti: + optional: true + + espree@10.4.0: + resolution: {integrity: sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + esprima@4.0.1: + resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} + engines: {node: '>=4'} + hasBin: true + + esquery@1.7.0: + resolution: {integrity: sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==} + engines: {node: '>=0.10'} + + esrecurse@4.3.0: + resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} + engines: {node: '>=4.0'} + + estraverse@5.3.0: + resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} + engines: {node: '>=4.0'} + + esutils@2.0.3: + resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} + engines: {node: '>=0.10.0'} + + etag@1.8.1: + resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} + engines: {node: '>= 0.6'} + + event-target-shim@5.0.1: + resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==} + engines: {node: '>=6'} + + execa@5.1.1: + resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==} + engines: {node: '>=10'} + + exit@0.1.2: + resolution: {integrity: sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==} + engines: {node: '>= 0.8.0'} + + expect@29.7.0: + resolution: {integrity: sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + expo-application@55.0.14: + resolution: {integrity: sha512-NgqDIt3eCf4aVLp1L6AcEanCYoyJeuBsGrgGSzOIvxAsOvp5X3SYKW3ROgpKUnLQEKMWlzwETpjsUGszcqkk8g==} + peerDependencies: + expo: '*' + + expo-asset@55.0.16: + resolution: {integrity: sha512-5IJyfJtYqvKGg04NKGQWiCIoK/fULDL9m15mXPPyfabD1jsToVj2hnWmo1r2SWNNmMwtQxi6jTpcGwVo2nLDxg==} + peerDependencies: + expo: '*' + react: '*' + react-native: '*' + + expo-build-properties@55.0.13: + resolution: {integrity: sha512-UYZhUKyh7YQhbJdkBvo68WUQ7fOtZeSV7F8kfYkjEiN/ADRHG0WfEIiddvGfi9cH/5iwpptv/+Lu5cx6uvfegA==} + peerDependencies: + expo: '*' + + expo-camera@55.0.16: + resolution: {integrity: sha512-9c6FGrLVwMLyQ08wqwkH7DXAC8Oj1VD0LXM4hFu62Nuq2f2zIAZwsXxG7J5ex+HHHAGyligGGi6VJWuiib9qNg==} + peerDependencies: + expo: '*' + react: '*' + react-native: '*' + react-native-web: '*' + peerDependenciesMeta: + react-native-web: + optional: true + + expo-constants@55.0.15: + resolution: {integrity: sha512-w394fcZLJjeKN+9ZnJzL/HiarE1nwZFDa+3S9frevh6Ur+MAAs9QDrcXhDrV8T3xqRzzYaqsP6Z8TFZ4efWN1A==} + peerDependencies: + expo: '*' + react-native: '*' + + expo-crypto@55.0.14: + resolution: {integrity: sha512-TfAADBGZNNv9OOmdKFJCz54wDj87ufxtzQNSY+Roycpm8e5tuCnDIL7EjqUOmNTGH99Jj8ftPGFt4KGG2Ii2fg==} + peerDependencies: + expo: '*' + + expo-file-system@55.0.17: + resolution: {integrity: sha512-d27K1cagUOt2BwxwPka9KW8Znu5kN1tnairozCzzCRZviZFtWnBxwFuJ3KU6MAbav/9UhSMkp5Ve/oZ+SR0UgQ==} + peerDependencies: + expo: '*' + react-native: '*' + + expo-font@55.0.6: + resolution: {integrity: sha512-x9czUA3UQWjIwa0ZUEs/eWJNqB4mAue/m4ltESlNPLZhHL0nWWqIfsyHmklTLFH7mVfcHSJvew6k+pR2FE1zVw==} + peerDependencies: + expo: '*' + react: '*' + react-native: '*' + + expo-glass-effect@55.0.10: + resolution: {integrity: sha512-5kL/jATvgJWdrqPdxixrECJqD2l8cfQ4ALr1DK7qi9XkyI97ejXvUjB2VsfEePNy3Fg+/VwzA3n3L7Nv3tAPkw==} + peerDependencies: + expo: '*' + react: '*' + react-native: '*' + + expo-haptics@55.0.14: + resolution: {integrity: sha512-KjDItBsA9mi1f5nRwf8g1wOdfEcLHwvEdt5Jl1sMCDETR/homcGOl+F3QIiPOl/PRlbGVieQsjTtF4DGtHOj6g==} + peerDependencies: + expo: '*' + + expo-image@55.0.9: + resolution: {integrity: sha512-+NVgWv+tr7a6EpBEaIIVVp+XfruRA2JL5xOxvd6ajvFGdH0rOhagwX1m1piAII6w7sh6uAnBr8X+fDZsav7B2w==} + peerDependencies: + expo: '*' + react: '*' + react-native: '*' + react-native-web: '*' + peerDependenciesMeta: + react-native-web: + optional: true + + expo-keep-awake@55.0.6: + resolution: {integrity: sha512-acJjeHqkNxMVckEcJhGQeIksqqsarscSHJtT559bNgyiM4r14dViQ66su7bb6qDVeBt0K7z3glXI1dHVck1Zgg==} + peerDependencies: + expo: '*' + react: '*' + + expo-linking@55.0.14: + resolution: {integrity: sha512-ZSqOvJyEquf04M5/ZpQo2diK9QRnNrzgqZo7p8gzxaPPHxP6IyUJnmcd12qT+dTxnRTVmUpxFQVHHWbvwPNIwQ==} + peerDependencies: + react: '*' + react-native: '*' + + expo-module-scripts@55.0.2: + resolution: {integrity: sha512-gtFQzs6wduZxYo4/1udcVLANDvQbVUVFjK1xJjgtZuUBlbJ+xFjQg5wOsQaffUDO3kqrG+DRXt1ihSxK72Wqkg==} + hasBin: true + + expo-modules-autolinking@55.0.18: + resolution: {integrity: sha512-olGTCWYkwVPj/momcgnF+z8MTzurGNFjopqPztQ4F53UkGPJnOFEuaM2/z4KbZtKbwHqeBv34OA5hxZP8uLdaQ==} + hasBin: true + + expo-modules-core@55.0.23: + resolution: {integrity: sha512-IGWT5N9MoV4zgWyrv686bElnKhzhE7E6pSazhaBNh3vgViAah5nnAz2o5h5YoUMR2B+ZTdHumRbGHN6gHLgwPA==} + peerDependencies: + react: '*' + react-native: '*' + react-native-worklets: ^0.7.4 || ^0.8.0 + peerDependenciesMeta: + react-native-worklets: + optional: true + + expo-notifications@55.0.21: + resolution: {integrity: sha512-nZKbvfjCoKFTZU435ihXcCCqCF3uCY1kcD5e2Tm2GMDLSt7HTiLZ1CQFGgckg/6QYIfMKhLFdHfOQHAEGakQug==} + peerDependencies: + expo: '*' + react: '*' + react-native: '*' + + expo-router@55.0.13: + resolution: {integrity: sha512-cIBR5RmQtbr+b535mlbMhmm7lweVZXFtjzJOgJTutoxIApRztl816kFRFNesnVyqQ0LZrEU0a6vqa3i0wdlRQw==} + peerDependencies: + '@expo/log-box': 55.0.11 + '@expo/metro-runtime': ^55.0.10 + '@react-navigation/drawer': ^7.9.4 + '@testing-library/react-native': '>= 13.2.0' + expo: '*' + expo-constants: ^55.0.15 + expo-linking: ^55.0.14 + react: '*' + react-dom: '*' + react-native: '*' + react-native-gesture-handler: '*' + react-native-reanimated: '*' + react-native-safe-area-context: '>= 5.4.0' + react-native-screens: '*' + react-native-web: '*' + react-server-dom-webpack: ~19.0.4 || ~19.1.5 || ~19.2.4 + peerDependenciesMeta: + '@react-navigation/drawer': + optional: true + '@testing-library/react-native': + optional: true + react-dom: + optional: true + react-native-gesture-handler: + optional: true + react-native-reanimated: + optional: true + react-native-web: + optional: true + react-server-dom-webpack: + optional: true + + expo-server@55.0.8: + resolution: {integrity: sha512-AoV5TKuO4biSzrhe/OVLyInfTT0pV9/OOc/g/oVq5vmCjL8SaSYTkES8PLt+67Tm7VqX+Dn0+kSx1nQcjEKaPw==} + engines: {node: '>=20.16.0'} + + expo-splash-screen@55.0.19: + resolution: {integrity: sha512-l8BWI/inLJW46Ojz5NgwvaM8LftrdXeFfZBUXhAoZxg44Qo2xKY76s0S1h3WIxWXT4sRKwK8YQzGr4k+zHubxQ==} + peerDependencies: + expo: '*' + + expo-status-bar@55.0.5: + resolution: {integrity: sha512-qb0c3rJO2b7CC0gUVGi1JYp92oLenWdYGyk8l4YQs6U+uaXUTPv6aaFa3KkT2HON10re3AxxPNJci8rsz6kPxg==} + peerDependencies: + react: '*' + react-native: '*' + + expo-symbols@55.0.7: + resolution: {integrity: sha512-y4ALLbncSGQzhFLw1PaIBbO39xzaw3ie249HmK6zK/WLJYfw4Z/9UU4iPKO3KCE4FyCKIzd+yRsvzvlri23YrQ==} + peerDependencies: + expo: '*' + expo-font: '*' + react: '*' + react-native: '*' + + expo@55.0.17: + resolution: {integrity: sha512-yVF2phiPw5XgOCedC/oQaL3j0XbwzsBLst3JiAF8bi9aFlxLOVvuDEM8BDg3E09XGSLaGCAclY4q5L+sFerXlQ==} + hasBin: true + peerDependencies: + '@expo/dom-webview': '*' + '@expo/metro-runtime': '*' + react: '*' + react-native: '*' + react-native-webview: '*' + peerDependenciesMeta: + '@expo/dom-webview': + optional: true + '@expo/metro-runtime': + optional: true + react-native-webview: + optional: true + + exponential-backoff@3.1.3: + resolution: {integrity: sha512-ZgEeZXj30q+I0EN+CbSSpIyPaJ5HVQD18Z1m+u1FXbAeT94mr1zw50q4q6jiiC447Nl/YTcIYSAftiGqetwXCA==} + + fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + + fast-diff@1.3.0: + resolution: {integrity: sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==} + + fast-json-stable-stringify@2.1.0: + resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} + + fast-levenshtein@2.0.6: + resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + + fb-dotslash@0.5.8: + resolution: {integrity: sha512-XHYLKk9J4BupDxi9bSEhkfss0m+Vr9ChTrjhf9l2iw3jB5C7BnY4GVPoMcqbrTutsKJso6yj2nAB6BI/F2oZaA==} + engines: {node: '>=20'} + hasBin: true + + fb-watchman@2.0.2: + resolution: {integrity: sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==} + + fbjs-css-vars@1.0.2: + resolution: {integrity: sha512-b2XGFAFdWZWg0phtAWLHCk836A1Xann+I+Dgd3Gk64MHKZO44FfoD1KxyvbSh0qZsIoXQGGlVztIY+oitJPpRQ==} + + fbjs@3.0.5: + resolution: {integrity: sha512-ztsSx77JBtkuMrEypfhgc3cI0+0h+svqeie7xHbh1k/IKdcydnvadp/mUaGgjAOXQmQSxsqgaRhS3q9fy+1kxg==} + + fdir@6.5.0: + resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} + engines: {node: '>=12.0.0'} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + + fetch-nodeshim@0.4.10: + resolution: {integrity: sha512-m6I8ALe4L4XpdETy7MJZWs6L1IVMbjs99bwbpIKphxX+0CTns4IKDWJY0LWfr4YsFjfg+z1TjzTMU8lKl8rG0w==} + + file-entry-cache@8.0.0: + resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} + engines: {node: '>=16.0.0'} + + fill-range@7.1.1: + resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} + engines: {node: '>=8'} + + filter-obj@1.1.0: + resolution: {integrity: sha512-8rXg1ZnX7xzy2NGDVkBVaAy+lSlPNwad13BtgSlLuxfIslyt5Vg64U7tFcCt4WS1R0hvtnQybT/IyCkGZ3DpXQ==} + engines: {node: '>=0.10.0'} + + finalhandler@1.1.2: + resolution: {integrity: sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==} + engines: {node: '>= 0.8'} + + find-up@4.1.0: + resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} + engines: {node: '>=8'} + + find-up@5.0.0: + resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} + engines: {node: '>=10'} + + flat-cache@4.0.1: + resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==} + engines: {node: '>=16'} + + flatted@3.4.2: + resolution: {integrity: sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==} + + flow-enums-runtime@0.0.6: + resolution: {integrity: sha512-3PYnM29RFXwvAN6Pc/scUfkI7RwhQ/xqyLUyPNlXUp9S40zI8nup9tUSrTLSVnWGBN38FNiGWbwZOB6uR4OGdw==} + + fontfaceobserver@2.3.0: + resolution: {integrity: sha512-6FPvD/IVyT4ZlNe7Wcn5Fb/4ChigpucKYSvD6a+0iMoLn2inpo711eyIcKjmDtE5XNcgAkSH9uN/nfAeZzHEfg==} + + for-each@0.3.5: + resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==} + engines: {node: '>= 0.4'} + + form-data@4.0.5: + resolution: {integrity: sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==} + engines: {node: '>= 6'} + + fresh@0.5.2: + resolution: {integrity: sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==} + engines: {node: '>= 0.6'} + + fs-readdir-recursive@1.1.0: + resolution: {integrity: sha512-GNanXlVr2pf02+sPN40XN8HG+ePaNcvM0q5mZBd668Obwb0yD5GiUbZOFgwn8kGMY6I3mdyDJzieUy3PTYyTRA==} + + fs.realpath@1.0.0: + resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + + function.prototype.name@1.1.8: + resolution: {integrity: sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==} + engines: {node: '>= 0.4'} + + functions-have-names@1.2.3: + resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} + + generator-function@2.0.1: + resolution: {integrity: sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==} + engines: {node: '>= 0.4'} + + gensync@1.0.0-beta.2: + resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} + engines: {node: '>=6.9.0'} + + get-caller-file@2.0.5: + resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} + engines: {node: 6.* || 8.* || >= 10.*} + + get-intrinsic@1.3.0: + resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} + engines: {node: '>= 0.4'} + + get-nonce@1.0.1: + resolution: {integrity: sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==} + engines: {node: '>=6'} + + get-package-type@0.1.0: + resolution: {integrity: sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==} + engines: {node: '>=8.0.0'} + + get-proto@1.0.1: + resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} + engines: {node: '>= 0.4'} + + get-stream@6.0.1: + resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} + engines: {node: '>=10'} + + get-symbol-description@1.1.0: + resolution: {integrity: sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==} + engines: {node: '>= 0.4'} + + get-tsconfig@4.14.0: + resolution: {integrity: sha512-yTb+8DXzDREzgvYmh6s9vHsSVCHeC0G3PI5bEXNBHtmshPnO+S5O7qgLEOn0I5QvMy6kpZN8K1NKGyilLb93wA==} + + getenv@2.0.0: + resolution: {integrity: sha512-VilgtJj/ALgGY77fiLam5iD336eSWi96Q15JSAG1zi8NRBysm3LXKdGnHb4m5cuyxvOLQQKWpBZAT6ni4FI2iQ==} + engines: {node: '>=6'} + + glob-parent@5.1.2: + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} + engines: {node: '>= 6'} + + glob-parent@6.0.2: + resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} + engines: {node: '>=10.13.0'} + + glob@13.0.6: + resolution: {integrity: sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==} + engines: {node: 18 || 20 || >=22} + + glob@7.2.3: + resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} + deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me + + globals@14.0.0: + resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} + engines: {node: '>=18'} + + globals@15.15.0: + resolution: {integrity: sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==} + engines: {node: '>=18'} + + globals@16.5.0: + resolution: {integrity: sha512-c/c15i26VrJ4IRt5Z89DnIzCGDn9EcebibhAOjw5ibqEHsE1wLUgkPn9RDmNcUKyU87GeaL633nyJ+pplFR2ZQ==} + engines: {node: '>=18'} + + globalthis@1.0.4: + resolution: {integrity: sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==} + engines: {node: '>= 0.4'} + + globrex@0.1.2: + resolution: {integrity: sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==} + + gopd@1.2.0: + resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} + engines: {node: '>= 0.4'} + + graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + + has-bigints@1.1.0: + resolution: {integrity: sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==} + engines: {node: '>= 0.4'} + + has-flag@3.0.0: + resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} + engines: {node: '>=4'} + + has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + + has-property-descriptors@1.0.2: + resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==} + + has-proto@1.2.0: + resolution: {integrity: sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==} + engines: {node: '>= 0.4'} + + has-symbols@1.1.0: + resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} + engines: {node: '>= 0.4'} + + has-tostringtag@1.0.2: + resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} + engines: {node: '>= 0.4'} + + hasown@2.0.3: + resolution: {integrity: sha512-ej4AhfhfL2Q2zpMmLo7U1Uv9+PyhIZpgQLGT1F9miIGmiCJIoCgSmczFdrc97mWT4kVY72KA+WnnhJ5pghSvSg==} + engines: {node: '>= 0.4'} + + hermes-compiler@0.14.1: + resolution: {integrity: sha512-+RPPQlayoZ9n6/KXKt5SFILWXCGJ/LV5d24L5smXrvTDrPS4L6dSctPczXauuvzFP3QEJbD1YO7Z3Ra4a+4IhA==} + + hermes-compiler@250829098.0.10: + resolution: {integrity: sha512-TcRlZ0/TlyfJqquRFAWoyElVNnkdYRi/sEp4/Qy8/GYxjg8j2cS9D4MjuaQ+qimkmLN7AmO+44IznRf06mAr0w==} + + hermes-estree@0.32.0: + resolution: {integrity: sha512-KWn3BqnlDOl97Xe1Yviur6NbgIZ+IP+UVSpshlZWkq+EtoHg6/cwiDj/osP9PCEgFE15KBm1O55JRwbMEm5ejQ==} + + hermes-estree@0.32.1: + resolution: {integrity: sha512-ne5hkuDxheNBAikDjqvCZCwihnz0vVu9YsBzAEO1puiyFR4F1+PAz/SiPHSsNTuOveCYGRMX8Xbx4LOubeC0Qg==} + + hermes-estree@0.33.3: + resolution: {integrity: sha512-6kzYZHCk8Fy1Uc+t3HGYyJn3OL4aeqKLTyina4UFtWl8I0kSL7OmKThaiX+Uh2f8nGw3mo4Ifxg0M5Zk3/Oeqg==} + + hermes-estree@0.35.0: + resolution: {integrity: sha512-xVx5Opwy8Oo1I5yGpVRhCvWL/iV3M+ylksSKVNlxxD90cpDpR/AR1jLYqK8HWihm065a6UI3HeyAmYzwS8NOOg==} + + hermes-parser@0.32.0: + resolution: {integrity: sha512-g4nBOWFpuiTqjR3LZdRxKUkij9iyveWeuks7INEsMX741f3r9xxrOe8TeQfUxtda0eXmiIFiMQzoeSQEno33Hw==} + + hermes-parser@0.32.1: + resolution: {integrity: sha512-175dz634X/W5AiwrpLdoMl/MOb17poLHyIqgyExlE8D9zQ1OPnoORnGMB5ltRKnpvQzBjMYvT2rN/sHeIfZW5Q==} + + hermes-parser@0.33.3: + resolution: {integrity: sha512-Yg3HgaG4CqgyowtYjX/FsnPAuZdHOqSMtnbpylbptsQ9nwwSKsy6uRWcGO5RK0EqiX12q8HvDWKgeAVajRO5DA==} + + hermes-parser@0.35.0: + resolution: {integrity: sha512-9JLjeHxBx8T4CAsydZR49PNZUaix+WpQJwu9p2010lu+7Kwl6D/7wYFFJxoz+aXkaaClp9Zfg6W6/zVlSJORaA==} + + hoist-non-react-statics@3.3.2: + resolution: {integrity: sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==} + + hosted-git-info@7.0.2: + resolution: {integrity: sha512-puUZAUKT5m8Zzvs72XWy3HtvVbTWljRE66cP60bxJzAqf2DgICo7lYTY2IHUmLnNpjYvw5bvmoHvPc0QO2a62w==} + engines: {node: ^16.14.0 || >=18.0.0} + + html-encoding-sniffer@3.0.0: + resolution: {integrity: sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==} + engines: {node: '>=12'} + + html-escaper@2.0.2: + resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} + + http-errors@2.0.1: + resolution: {integrity: sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==} + engines: {node: '>= 0.8'} + + http-proxy-agent@5.0.0: + resolution: {integrity: sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==} + engines: {node: '>= 6'} + + https-proxy-agent@5.0.1: + resolution: {integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==} + engines: {node: '>= 6'} + + https-proxy-agent@7.0.6: + resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==} + engines: {node: '>= 14'} + + human-signals@2.1.0: + resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} + engines: {node: '>=10.17.0'} + + hyphenate-style-name@1.1.0: + resolution: {integrity: sha512-WDC/ui2VVRrz3jOVi+XtjqkDjiVjTtFaAGiW37k6b+ohyQ5wYDOGkvCZa8+H0nx3gyvv0+BST9xuOgIyGQ00gw==} + + iconv-lite@0.6.3: + resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} + engines: {node: '>=0.10.0'} + + ignore@5.3.2: + resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} + engines: {node: '>= 4'} + + ignore@7.0.5: + resolution: {integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==} + engines: {node: '>= 4'} + + image-size@1.2.1: + resolution: {integrity: sha512-rH+46sQJ2dlwfjfhCyNx5thzrv+dtmBIhPHk0zgRUukHzZ/kRueTJXoYYsclBaKcSMBWuGbOFXtioLpzTb5euw==} + engines: {node: '>=16.x'} + hasBin: true + + import-fresh@3.3.1: + resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} + engines: {node: '>=6'} + + import-local@3.2.0: + resolution: {integrity: sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==} + engines: {node: '>=8'} + hasBin: true + + imurmurhash@0.1.4: + resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} + engines: {node: '>=0.8.19'} + + indent-string@4.0.0: + resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==} + engines: {node: '>=8'} + + inflight@1.0.6: + resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} + deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. + + inherits@2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + + inline-style-prefixer@7.0.1: + resolution: {integrity: sha512-lhYo5qNTQp3EvSSp3sRvXMbVQTLrvGV6DycRMJ5dm2BLMiJ30wpXKdDdgX+GmJZ5uQMucwRKHamXSst3Sj/Giw==} + + internal-slot@1.1.0: + resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==} + engines: {node: '>= 0.4'} + + invariant@2.2.4: + resolution: {integrity: sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==} + + is-array-buffer@3.0.5: + resolution: {integrity: sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==} + engines: {node: '>= 0.4'} + + is-arrayish@0.2.1: + resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} + + is-arrayish@0.3.4: + resolution: {integrity: sha512-m6UrgzFVUYawGBh1dUsWR5M2Clqic9RVXC/9f8ceNlv2IcO9j9J/z8UoCLPqtsPBFNzEpfR3xftohbfqDx8EQA==} + + is-async-function@2.1.1: + resolution: {integrity: sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==} + engines: {node: '>= 0.4'} + + is-bigint@1.1.0: + resolution: {integrity: sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==} + engines: {node: '>= 0.4'} + + is-binary-path@2.1.0: + resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} + engines: {node: '>=8'} + + is-boolean-object@1.2.2: + resolution: {integrity: sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==} + engines: {node: '>= 0.4'} + + is-callable@1.2.7: + resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} + engines: {node: '>= 0.4'} + + is-core-module@2.16.1: + resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==} + engines: {node: '>= 0.4'} + + is-data-view@1.0.2: + resolution: {integrity: sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==} + engines: {node: '>= 0.4'} + + is-date-object@1.1.0: + resolution: {integrity: sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==} + engines: {node: '>= 0.4'} + + is-docker@2.2.1: + resolution: {integrity: sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==} + engines: {node: '>=8'} + hasBin: true + + is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + + is-finalizationregistry@1.1.1: + resolution: {integrity: sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==} + engines: {node: '>= 0.4'} + + is-fullwidth-code-point@3.0.0: + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} + + is-generator-fn@2.1.0: + resolution: {integrity: sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==} + engines: {node: '>=6'} + + is-generator-function@1.1.2: + resolution: {integrity: sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==} + engines: {node: '>= 0.4'} + + is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + + is-map@2.0.3: + resolution: {integrity: sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==} + engines: {node: '>= 0.4'} + + is-negative-zero@2.0.3: + resolution: {integrity: sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==} + engines: {node: '>= 0.4'} + + is-number-object@1.1.1: + resolution: {integrity: sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==} + engines: {node: '>= 0.4'} + + is-number@7.0.0: + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} + + is-plain-obj@2.1.0: + resolution: {integrity: sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==} + engines: {node: '>=8'} + + is-potential-custom-element-name@1.0.1: + resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==} + + is-regex@1.2.1: + resolution: {integrity: sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==} + engines: {node: '>= 0.4'} + + is-set@2.0.3: + resolution: {integrity: sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==} + engines: {node: '>= 0.4'} + + is-shared-array-buffer@1.0.4: + resolution: {integrity: sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==} + engines: {node: '>= 0.4'} + + is-stream@2.0.1: + resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} + engines: {node: '>=8'} + + is-string@1.1.1: + resolution: {integrity: sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==} + engines: {node: '>= 0.4'} + + is-symbol@1.1.1: + resolution: {integrity: sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==} + engines: {node: '>= 0.4'} + + is-typed-array@1.1.15: + resolution: {integrity: sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==} + engines: {node: '>= 0.4'} + + is-weakmap@2.0.2: + resolution: {integrity: sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==} + engines: {node: '>= 0.4'} + + is-weakref@1.1.1: + resolution: {integrity: sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==} + engines: {node: '>= 0.4'} + + is-weakset@2.0.4: + resolution: {integrity: sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==} + engines: {node: '>= 0.4'} + + is-wsl@2.2.0: + resolution: {integrity: sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==} + engines: {node: '>=8'} + + isarray@2.0.5: + resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} + + isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + + istanbul-lib-coverage@3.2.2: + resolution: {integrity: sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==} + engines: {node: '>=8'} + + istanbul-lib-instrument@5.2.1: + resolution: {integrity: sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==} + engines: {node: '>=8'} + + istanbul-lib-instrument@6.0.3: + resolution: {integrity: sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==} + engines: {node: '>=10'} + + istanbul-lib-report@3.0.1: + resolution: {integrity: sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==} + engines: {node: '>=10'} + + istanbul-lib-source-maps@4.0.1: + resolution: {integrity: sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==} + engines: {node: '>=10'} + + istanbul-reports@3.2.0: + resolution: {integrity: sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==} + engines: {node: '>=8'} + + iterator.prototype@1.1.5: + resolution: {integrity: sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==} + engines: {node: '>= 0.4'} + + jest-changed-files@29.7.0: + resolution: {integrity: sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-circus@29.7.0: + resolution: {integrity: sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-cli@29.7.0: + resolution: {integrity: sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + hasBin: true + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + + jest-config@29.7.0: + resolution: {integrity: sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + peerDependencies: + '@types/node': '*' + ts-node: '>=9.0.0' + peerDependenciesMeta: + '@types/node': + optional: true + ts-node: + optional: true + + jest-diff@29.7.0: + resolution: {integrity: sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-diff@30.3.0: + resolution: {integrity: sha512-n3q4PDQjS4LrKxfWB3Z5KNk1XjXtZTBwQp71OP0Jo03Z6V60x++K5L8k6ZrW8MY8pOFylZvHM0zsjS1RqlHJZQ==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + jest-docblock@29.7.0: + resolution: {integrity: sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-each@29.7.0: + resolution: {integrity: sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-environment-jsdom@29.7.0: + resolution: {integrity: sha512-k9iQbsf9OyOfdzWH8HDmrRT0gSIcX+FLNW7IQq94tFX0gynPwqDTW0Ho6iMVNjGz/nb+l/vW3dWM2bbLLpkbXA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + peerDependencies: + canvas: ^2.5.0 + peerDependenciesMeta: + canvas: + optional: true + + jest-environment-node@29.7.0: + resolution: {integrity: sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-expo@55.0.16: + resolution: {integrity: sha512-bOvrTNyDaiaoTz9GhvnXib9v9rjX9PTJFvvoqRMRKEg4MoHghG82E7YF+pH71EWSXTaibQ07F46GS+fcUxTWEg==} + hasBin: true + peerDependencies: + expo: '*' + react-native: '*' + react-server-dom-webpack: ~19.0.4 || ~19.1.5 || ~19.2.4 + peerDependenciesMeta: + react-server-dom-webpack: + optional: true + + jest-get-type@29.6.3: + resolution: {integrity: sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-haste-map@29.7.0: + resolution: {integrity: sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-leak-detector@29.7.0: + resolution: {integrity: sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-matcher-utils@29.7.0: + resolution: {integrity: sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-matcher-utils@30.3.0: + resolution: {integrity: sha512-HEtc9uFQgaUHkC7nLSlQL3Tph4Pjxt/yiPvkIrrDCt9jhoLIgxaubo1G+CFOnmHYMxHwwdaSN7mkIFs6ZK8OhA==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + jest-message-util@29.7.0: + resolution: {integrity: sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-mock@29.7.0: + resolution: {integrity: sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-pnp-resolver@1.2.3: + resolution: {integrity: sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==} + engines: {node: '>=6'} + peerDependencies: + jest-resolve: '*' + peerDependenciesMeta: + jest-resolve: + optional: true + + jest-regex-util@29.6.3: + resolution: {integrity: sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-resolve-dependencies@29.7.0: + resolution: {integrity: sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-resolve@29.7.0: + resolution: {integrity: sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-runner@29.7.0: + resolution: {integrity: sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-runtime@29.7.0: + resolution: {integrity: sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-snapshot@29.7.0: + resolution: {integrity: sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-util@29.7.0: + resolution: {integrity: sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-validate@29.7.0: + resolution: {integrity: sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-watch-select-projects@2.0.0: + resolution: {integrity: sha512-j00nW4dXc2NiCW6znXgFLF9g8PJ0zP25cpQ1xRro/HU2GBfZQFZD0SoXnAlaoKkIY4MlfTMkKGbNXFpvCdjl1w==} + + jest-watch-typeahead@2.2.1: + resolution: {integrity: sha512-jYpYmUnTzysmVnwq49TAxlmtOAwp8QIqvZyoofQFn8fiWhEDZj33ZXzg3JA4nGnzWFm1hbWf3ADpteUokvXgFA==} + engines: {node: ^14.17.0 || ^16.10.0 || >=18.0.0} + peerDependencies: + jest: ^27.0.0 || ^28.0.0 || ^29.0.0 + + jest-watcher@29.7.0: + resolution: {integrity: sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-worker@29.7.0: + resolution: {integrity: sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest@29.7.0: + resolution: {integrity: sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + hasBin: true + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + + jimp-compact@0.16.1: + resolution: {integrity: sha512-dZ6Ra7u1G8c4Letq/B5EzAxj4tLFHL+cGtdpR+PVm4yzPDj+lCk+AbivWt1eOM+ikzkowtyV7qSqX6qr3t71Ww==} + + js-tokens@4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + + js-yaml@3.14.2: + resolution: {integrity: sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==} + hasBin: true + + js-yaml@4.1.1: + resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==} + hasBin: true + + jsc-safe-url@0.2.4: + resolution: {integrity: sha512-0wM3YBWtYePOjfyXQH5MWQ8H7sdk5EXSwZvmSLKk2RboVQ2Bu239jycHDz5J/8Blf3K0Qnoy2b6xD+z10MFB+Q==} + + jsdom@20.0.3: + resolution: {integrity: sha512-SYhBvTh89tTfCD/CRdSOm13mOBa42iTaTyfyEWBdKcGdPxPtLFBXuHR8XHb33YNYaP+lLbmSvBTsnoesCNJEsQ==} + engines: {node: '>=14'} + peerDependencies: + canvas: ^2.5.0 + peerDependenciesMeta: + canvas: + optional: true + + jsesc@3.1.0: + resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} + engines: {node: '>=6'} + hasBin: true + + json-buffer@3.0.1: + resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} + + json-parse-even-better-errors@2.3.1: + resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} + + json-schema-traverse@0.4.1: + resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + + json-stable-stringify-without-jsonify@1.0.1: + resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} + + json5@1.0.2: + resolution: {integrity: sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==} + hasBin: true + + json5@2.2.3: + resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} + engines: {node: '>=6'} + hasBin: true + + jsx-ast-utils@3.3.5: + resolution: {integrity: sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==} + engines: {node: '>=4.0'} + + keyv@4.5.4: + resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + + kleur@3.0.3: + resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==} + engines: {node: '>=6'} + + lan-network@0.2.1: + resolution: {integrity: sha512-ONPnazC96VKDntab9j9JKwIWhZ4ZUceB4A9Epu4Ssg0hYFmtHZSeQ+n15nIwTFmcBUKtExOer8WTJ4GF9MO64A==} + hasBin: true + + leven@3.1.0: + resolution: {integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==} + engines: {node: '>=6'} + + levn@0.4.1: + resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} + engines: {node: '>= 0.8.0'} + + lighthouse-logger@1.4.2: + resolution: {integrity: sha512-gPWxznF6TKmUHrOQjlVo2UbaL2EJ71mb2CCeRs/2qBpi4L/g4LUVc9+3lKQ6DTUZwJswfM7ainGrLO1+fOqa2g==} + + lightningcss-android-arm64@1.32.0: + resolution: {integrity: sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [android] + + lightningcss-darwin-arm64@1.32.0: + resolution: {integrity: sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [darwin] + + lightningcss-darwin-x64@1.32.0: + resolution: {integrity: sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [darwin] + + lightningcss-freebsd-x64@1.32.0: + resolution: {integrity: sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [freebsd] + + lightningcss-linux-arm-gnueabihf@1.32.0: + resolution: {integrity: sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==} + engines: {node: '>= 12.0.0'} + cpu: [arm] + os: [linux] + + lightningcss-linux-arm64-gnu@1.32.0: + resolution: {integrity: sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + + lightningcss-linux-arm64-musl@1.32.0: + resolution: {integrity: sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + + lightningcss-linux-x64-gnu@1.32.0: + resolution: {integrity: sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + + lightningcss-linux-x64-musl@1.32.0: + resolution: {integrity: sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + + lightningcss-win32-arm64-msvc@1.32.0: + resolution: {integrity: sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [win32] + + lightningcss-win32-x64-msvc@1.32.0: + resolution: {integrity: sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [win32] + + lightningcss@1.32.0: + resolution: {integrity: sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==} + engines: {node: '>= 12.0.0'} + + lines-and-columns@1.2.4: + resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + + locate-path@5.0.0: + resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} + engines: {node: '>=8'} + + locate-path@6.0.0: + resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} + engines: {node: '>=10'} + + lodash.debounce@4.0.8: + resolution: {integrity: sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==} + + lodash.memoize@4.1.2: + resolution: {integrity: sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==} + + lodash.merge@4.6.2: + resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + + lodash.throttle@4.1.1: + resolution: {integrity: sha512-wIkUCfVKpVsWo3JSZlc+8MB5it+2AN5W8J7YVMST30UrvcQNZ1Okbj+rbVniijTWE6FGYy4XJq/rHkas8qJMLQ==} + + lodash@4.18.1: + resolution: {integrity: sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==} + + log-symbols@2.2.0: + resolution: {integrity: sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg==} + engines: {node: '>=4'} + + loose-envify@1.4.0: + resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} + hasBin: true + + lru-cache@10.4.3: + resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} + + lru-cache@11.3.5: + resolution: {integrity: sha512-NxVFwLAnrd9i7KUBxC4DrUhmgjzOs+1Qm50D3oF1/oL+r1NpZ4gA7xvG0/zJ8evR7zIKn4vLf7qTNduWFtCrRw==} + engines: {node: 20 || >=22} + + lru-cache@5.1.1: + resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + + lucide-react-native@1.11.0: + resolution: {integrity: sha512-/dEt+/noOpsvgH1dbMHHSv+f8+npB1BJSKCqK9v9yvHbssN3OJJJXrih6N1prJGG3vj0e1zKiVLtet+OKYmiXg==} + peerDependencies: + react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0 + react-native: '*' + react-native-svg: ^12.0.0 || ^13.0.0 || ^14.0.0 || ^15.0.0 + + make-dir@2.1.0: + resolution: {integrity: sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==} + engines: {node: '>=6'} + + make-dir@4.0.0: + resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==} + engines: {node: '>=10'} + + make-error@1.3.6: + resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} + + makeerror@1.0.12: + resolution: {integrity: sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==} + + marky@1.3.0: + resolution: {integrity: sha512-ocnPZQLNpvbedwTy9kNrQEsknEfgvcLMvOtz3sFeWApDq1MXH1TqkCIx58xlpESsfwQOnuBO9beyQuNGzVvuhQ==} + + math-intrinsics@1.1.0: + resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} + engines: {node: '>= 0.4'} + + mdn-data@2.0.14: + resolution: {integrity: sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==} + + memoize-one@5.2.1: + resolution: {integrity: sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==} + + memoize-one@6.0.0: + resolution: {integrity: sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==} + + merge-options@3.0.4: + resolution: {integrity: sha512-2Sug1+knBjkaMsMgf1ctR1Ujx+Ayku4EdJN4Z+C2+JzoeF7A3OZ9KM2GY0CpQS51NR61LTurMJrRKPhSs3ZRTQ==} + engines: {node: '>=10'} + + merge-stream@2.0.0: + resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} + + metro-babel-transformer@0.83.6: + resolution: {integrity: sha512-1AnuazBpzY3meRMr04WUw14kRBkV0W3Ez+AA75FAeNpRyWNN5S3M3PHLUbZw7IXq7ZeOzceyRsHStaFrnWd+8w==} + engines: {node: '>=20.19.4'} + + metro-babel-transformer@0.84.3: + resolution: {integrity: sha512-svAA+yMLpeMiGcz/jKJs4oHpIGEx4nBqNEJ5AGj4CYIg1efvK+A0TjR6tgIuc6tKO5e8JmN/1lglpN2+f3/z/w==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} + + metro-cache-key@0.83.6: + resolution: {integrity: sha512-5gdK4PVpgNOHi7xCGrgesNP1AuOA2TiPqpcirGXZi4RLLzX1VMowpkgTVtBfpQQCqWoosQF9yrSo9/KDQg1eBg==} + engines: {node: '>=20.19.4'} + + metro-cache-key@0.84.3: + resolution: {integrity: sha512-TnSL1Fdvrw+2glTdBSRmA5TL8l/i16ECjsrUdf3E5HncA+sNx8KcwDG8r+3ct1UhfYcusJypzZqTN55FZZcwGg==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} + + metro-cache@0.83.6: + resolution: {integrity: sha512-DpvZE32feNkqfZkI4Fic7YI/Kw8QP9wdl1rC4YKPrA77wQbI9vXbxjmfkCT/EGwBTFOPKqvIXo+H3BNe93YyiQ==} + engines: {node: '>=20.19.4'} + + metro-cache@0.84.3: + resolution: {integrity: sha512-0QElxwLaHqLZf+Xqio8QrjVbuXP/8sJfQBGSPiITlKDVXrVLefuzYVSH9Sj+QL6lrPj2gYZd/iwQh1yZuVKnLA==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} + + metro-config@0.83.6: + resolution: {integrity: sha512-G5622400uNtnAMlppEA5zkFAZltEf7DSGhOu09BkisCxOlVMWfdosD/oPyh4f2YVQsc1MBYyp4w6OzbExTYarg==} + engines: {node: '>=20.19.4'} + + metro-config@0.84.3: + resolution: {integrity: sha512-JmCzZWOETR+O22q8oPBWyQppx3roU9EbkbGzD8Gf1jukQ4b5T1fTzqqHruu6K4sTiNq5zVQySmKF6bp4kVARew==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} + + metro-core@0.83.6: + resolution: {integrity: sha512-l+yQ2fuIgR//wszUlMrrAa9+Z+kbKazd0QOh0VQY7jC4ghb7yZBBSla/UMYRBZZ6fPg9IM+wD3+h+37a5f9etw==} + engines: {node: '>=20.19.4'} + + metro-core@0.84.3: + resolution: {integrity: sha512-cc0pvAa80ai1nDmqqz0P59a+0ZqCZ/YHU/3jEekZL6spFnYDfX8iDLdn9FR6kX+67rmzKxHNrbrSRFLX2AYocw==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} + + metro-file-map@0.83.6: + resolution: {integrity: sha512-Jg3oN604C7GWbQwFAUXt8KsbMXeKfsxbZ5HFy4XFM3ggTS+ja9QgUmq9B613kgXv3G4M6rwiI6cvh9TRly4x3w==} + engines: {node: '>=20.19.4'} + + metro-file-map@0.84.3: + resolution: {integrity: sha512-1cL4m4Jv1yRUt9RJExZQLfccscdlMNOcRG6LHLtmJhf3BG9j3MujPVc7CIpKYdFl+KUl+sdjge6oO3+meKCHQA==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} + + metro-minify-terser@0.83.6: + resolution: {integrity: sha512-Vx3/Ne9Q+EIEDLfKzZUOtn/rxSNa/QjlYxc42nvK4Mg8mB6XUgd3LXX5ZZVq7lzQgehgEqLrbgShJPGfeF8PnQ==} + engines: {node: '>=20.19.4'} + + metro-minify-terser@0.84.3: + resolution: {integrity: sha512-3ofrG2OQyJbO9RNhCfOcl8QU7EE2WrSsnN5dFkuZaJO5+4Imujr9bUXmspeNlXRsOVk0F/rVRbEFH98lFSCkBQ==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} + + metro-resolver@0.83.6: + resolution: {integrity: sha512-lAwR/FsT1uJ5iCt4AIsN3boKfJ88aN8bjvDT5FwBS0tKeKw4/sbdSTWlFxc7W/MUTN5RekJ3nQkJRIWsvs28tA==} + engines: {node: '>=20.19.4'} + + metro-resolver@0.84.3: + resolution: {integrity: sha512-pjEzGDtoM8DTHAIPK/9u9ZxszEiuRohYUVImWvgbnB91V4gqYJpQcoEYUugf2NIm1lrX5HNu0OvNqWmPBnGYjA==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} + + metro-runtime@0.83.6: + resolution: {integrity: sha512-WQPua1G2VgYbwRn6vSKxOhTX7CFbSf/JdUu6Nd8bZnPXckOf7HQ2y51NXNQHoEsiuawathrkzL8pBhv+zgZFmg==} + engines: {node: '>=20.19.4'} + + metro-runtime@0.84.3: + resolution: {integrity: sha512-o7HLRfMyVk9N2dUZ9VjQfB6xxUItL9Pi9WcqxURE7MEKOH6wbGt9/E92YdYLluTOtkzYAEVfdC6h6lcxqA+hMQ==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} + + metro-source-map@0.83.6: + resolution: {integrity: sha512-AqJbOMMpeyyM4iNI91pchqDIszzNuuHApEhg6OABqZ+9mjLEqzcIEQ/fboZ7x74fNU5DBd2K36FdUQYPqlGClA==} + engines: {node: '>=20.19.4'} + + metro-source-map@0.84.3: + resolution: {integrity: sha512-jS48CeSzw78M8y6VE0f9uy3lVmfbOS677j2VCxnlmlYmnahcXuC6IhoN9K6LynNvos9517yUadcfgioju38xYQ==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} + + metro-symbolicate@0.83.6: + resolution: {integrity: sha512-4nvkmv9T7ozhprlPwk/+xm0SVPsxly5kYyMHdNaOlFemFz4df9BanvD46Ac6OISu/4Idinzfk2KVb++6OfzPAQ==} + engines: {node: '>=20.19.4'} + hasBin: true + + metro-symbolicate@0.84.3: + resolution: {integrity: sha512-J9Tpo8NCycYrozRvBIUyOwGAu4xkawOsAppmTscFiaegK0WvuDGwIM53GbzVSnytCHjVAF0io5GQxpkrKTuc7g==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} + hasBin: true + + metro-transform-plugins@0.83.6: + resolution: {integrity: sha512-V+zoY2Ul0v0BW6IokJkTud3raXmDdbdwkUQ/5eiSoy0jKuKMhrDjdH+H5buCS5iiJdNbykOn69Eip+Sqymkodg==} + engines: {node: '>=20.19.4'} + + metro-transform-plugins@0.84.3: + resolution: {integrity: sha512-8S3baq2XhBaafHEH5Q8sJW6tmzsEJk80qKc3RU/nZV1MsnYq94RdjTUR6AyKjQd6Rfsk1BtBxhtiNnk7mgslCg==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} + + metro-transform-worker@0.83.6: + resolution: {integrity: sha512-G5kDJ/P0ZTIf57t3iyAd5qIXbj2Wb1j7WtIDh82uTFQHe2Mq2SO9aXG9j1wI+kxZlIe58Z22XEXIKMl89z0ibQ==} + engines: {node: '>=20.19.4'} + + metro-transform-worker@0.84.3: + resolution: {integrity: sha512-Wjba7PyYktNRsHbPmkx2J2UX32rAzcDXjCu49zPHeF/viJlYJhwRaNePQcHaCRqQ+kmgQT4ThprsnJfDj71ZMA==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} + + metro@0.83.6: + resolution: {integrity: sha512-pbdndsAZ2F/ceopDdhVbttpa/hfLzXPJ/husc+QvQ33R0D9UXJKzTn5+OzOXx4bpQNtAKF2bY88cCI3Zl44xDQ==} + engines: {node: '>=20.19.4'} + hasBin: true + + metro@0.84.3: + resolution: {integrity: sha512-1h3lbVrE6hGf1e/764HfhPGg/bGrWMJDDh7G2rc4gFYZboVuI40BlG/y+UhtbhQDNlO/csMvrcnK0YrTlHUVew==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} + hasBin: true + + micromatch@4.0.8: + resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} + engines: {node: '>=8.6'} + + mime-db@1.52.0: + resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} + engines: {node: '>= 0.6'} + + mime-db@1.54.0: + resolution: {integrity: sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==} + engines: {node: '>= 0.6'} + + mime-types@2.1.35: + resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} + engines: {node: '>= 0.6'} + + mime-types@3.0.2: + resolution: {integrity: sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==} + engines: {node: '>=18'} + + mime@1.6.0: + resolution: {integrity: sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==} + engines: {node: '>=4'} + hasBin: true + + mimic-fn@1.2.0: + resolution: {integrity: sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==} + engines: {node: '>=4'} + + mimic-fn@2.1.0: + resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} + engines: {node: '>=6'} + + min-indent@1.0.1: + resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==} + engines: {node: '>=4'} + + minimatch@10.2.5: + resolution: {integrity: sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==} + engines: {node: 18 || 20 || >=22} + + minimatch@3.1.5: + resolution: {integrity: sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==} + + minimist@1.2.8: + resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + + minipass@7.1.3: + resolution: {integrity: sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==} + engines: {node: '>=16 || 14 >=14.17'} + + mkdirp@1.0.4: + resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==} + engines: {node: '>=10'} + hasBin: true + + ms@2.0.0: + resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==} + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + multitars@1.0.0: + resolution: {integrity: sha512-H/J4fMLedtudftaYMOg7ajzLYgT3/rwbWVJbqr/iUgB8DQztn38ys5HOqI1CzSxx8QhXXwOOnnBvd4v3jG5+Mg==} + + nanoid@3.3.11: + resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + natural-compare@1.4.0: + resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + + negotiator@0.6.3: + resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==} + engines: {node: '>= 0.6'} + + negotiator@0.6.4: + resolution: {integrity: sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==} + engines: {node: '>= 0.6'} + + negotiator@1.0.0: + resolution: {integrity: sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==} + engines: {node: '>= 0.6'} + + node-exports-info@1.6.0: + resolution: {integrity: sha512-pyFS63ptit/P5WqUkt+UUfe+4oevH+bFeIiPPdfb0pFeYEu/1ELnJu5l+5EcTKYL5M7zaAa7S8ddywgXypqKCw==} + engines: {node: '>= 0.4'} + + node-fetch@2.7.0: + resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} + engines: {node: 4.x || >=6.0.0} + peerDependencies: + encoding: ^0.1.0 + peerDependenciesMeta: + encoding: + optional: true + + node-forge@1.4.0: + resolution: {integrity: sha512-LarFH0+6VfriEhqMMcLX2F7SwSXeWwnEAJEsYm5QKWchiVYVvJyV9v7UDvUv+w5HO23ZpQTXDv/GxdDdMyOuoQ==} + engines: {node: '>= 6.13.0'} + + node-int64@0.4.0: + resolution: {integrity: sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==} + + node-releases@2.0.38: + resolution: {integrity: sha512-3qT/88Y3FbH/Kx4szpQQ4HzUbVrHPKTLVpVocKiLfoYvw9XSGOX2FmD2d6DrXbVYyAQTF2HeF6My8jmzx7/CRw==} + + normalize-path@3.0.0: + resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} + engines: {node: '>=0.10.0'} + + npm-package-arg@11.0.3: + resolution: {integrity: sha512-sHGJy8sOC1YraBywpzQlIKBE4pBbGbiF95U6Auspzyem956E0+FtDtsx1ZxlOJkQCZ1AFXAY/yuvtFYrOxF+Bw==} + engines: {node: ^16.14.0 || >=18.0.0} + + npm-run-path@4.0.1: + resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==} + engines: {node: '>=8'} + + nth-check@2.1.1: + resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==} + + nullthrows@1.1.1: + resolution: {integrity: sha512-2vPPEi+Z7WqML2jZYddDIfy5Dqb0r2fze2zTxNNknZaFpVHU3mFB3R+DWeJWGVx0ecvttSGlJTI+WG+8Z4cDWw==} + + nwsapi@2.2.23: + resolution: {integrity: sha512-7wfH4sLbt4M0gCDzGE6vzQBo0bfTKjU7Sfpqy/7gs1qBfYz2vEJH6vXcBKpO3+6Yu1telwd0t9HpyOoLEQQbIQ==} + + ob1@0.83.6: + resolution: {integrity: sha512-m/xZYkwcjo6UqLMrUICEB3iHk7Bjt3RSR7KXMi6Y1MO/kGkPhoRmfUDF6KAan3rLAZ7ABRqnQyKUTwaqZgUV4w==} + engines: {node: '>=20.19.4'} + + ob1@0.84.3: + resolution: {integrity: sha512-J7554Ef8bzmKaDY365Afq6PF+qtdnY/d5PKUQFrsKlZHV/N3OGZewVrvDrQDyX5V5NJjTpcAKtlrFZcDr+HvpQ==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} + + object-assign@4.1.1: + resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} + engines: {node: '>=0.10.0'} + + object-inspect@1.13.4: + resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==} + engines: {node: '>= 0.4'} + + object-keys@1.1.1: + resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} + engines: {node: '>= 0.4'} + + object.assign@4.1.7: + resolution: {integrity: sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==} + engines: {node: '>= 0.4'} + + object.entries@1.1.9: + resolution: {integrity: sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw==} + engines: {node: '>= 0.4'} + + object.fromentries@2.0.8: + resolution: {integrity: sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==} + engines: {node: '>= 0.4'} + + object.groupby@1.0.3: + resolution: {integrity: sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==} + engines: {node: '>= 0.4'} + + object.values@1.2.1: + resolution: {integrity: sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==} + engines: {node: '>= 0.4'} + + on-finished@2.3.0: + resolution: {integrity: sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==} + engines: {node: '>= 0.8'} + + on-finished@2.4.1: + resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==} + engines: {node: '>= 0.8'} + + on-headers@1.1.0: + resolution: {integrity: sha512-737ZY3yNnXy37FHkQxPzt4UZ2UWPWiCZWLvFZ4fu5cueciegX0zGPnrlY6bwRg4FdQOe9YU8MkmJwGhoMybl8A==} + engines: {node: '>= 0.8'} + + once@1.4.0: + resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + + onetime@2.0.1: + resolution: {integrity: sha512-oyyPpiMaKARvvcgip+JV+7zci5L8D1W9RZIz2l1o08AM3pfspitVWnPt3mzHcBPp12oYMTy0pqrFs/C+m3EwsQ==} + engines: {node: '>=4'} + + onetime@5.1.2: + resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} + engines: {node: '>=6'} + + open@7.4.2: + resolution: {integrity: sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q==} + engines: {node: '>=8'} + + open@8.4.2: + resolution: {integrity: sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==} + engines: {node: '>=12'} + + optionator@0.9.4: + resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} + engines: {node: '>= 0.8.0'} + + ora@3.4.0: + resolution: {integrity: sha512-eNwHudNbO1folBP3JsZ19v9azXWtQZjICdr3Q0TDPIaeBQ3mXLrh54wM+er0+hSp+dWKf+Z8KM58CYzEyIYxYg==} + engines: {node: '>=6'} + + own-keys@1.0.1: + resolution: {integrity: sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==} + engines: {node: '>= 0.4'} + + oxfmt@0.47.0: + resolution: {integrity: sha512-OFbkbzxKCpooQEnRmpTDnuwTX8KHXzZTQ4Df/hz85fpS67Pl+lxPEFvUtin56HIIS0B1k4X8oIzTXRZPufA2CA==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + + oxlint@1.62.0: + resolution: {integrity: sha512-1uFkg6HakjsGIpW9wNdeW4/2LOHW9MEkoWjZUTUfQtIHyLIZPYt00w3Sg+H3lH+206FgBPHBbW5dVE5l2ExECQ==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + peerDependencies: + oxlint-tsgolint: '>=0.18.0' + peerDependenciesMeta: + oxlint-tsgolint: + optional: true + + p-limit@2.3.0: + resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} + engines: {node: '>=6'} + + p-limit@3.1.0: + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} + engines: {node: '>=10'} + + p-locate@4.1.0: + resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} + engines: {node: '>=8'} + + p-locate@5.0.0: + resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} + engines: {node: '>=10'} + + p-try@2.2.0: + resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} + engines: {node: '>=6'} + + parent-module@1.0.1: + resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} + engines: {node: '>=6'} + + parse-json@5.2.0: + resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} + engines: {node: '>=8'} + + parse-png@2.1.0: + resolution: {integrity: sha512-Nt/a5SfCLiTnQAjx3fHlqp8hRgTL3z7kTQZzvIMS9uCAepnCyjpdEc6M/sz69WqMBdaDBw9sF1F1UaHROYzGkQ==} + engines: {node: '>=10'} + + parse5@7.3.0: + resolution: {integrity: sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==} + + parseurl@1.3.3: + resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} + engines: {node: '>= 0.8'} + + path-exists@4.0.0: + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} + + path-is-absolute@1.0.1: + resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} + engines: {node: '>=0.10.0'} + + path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + + path-parse@1.0.7: + resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + + path-scurry@2.0.2: + resolution: {integrity: sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==} + engines: {node: 18 || 20 || >=22} + + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + + picomatch@2.3.2: + resolution: {integrity: sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==} + engines: {node: '>=8.6'} + + picomatch@4.0.4: + resolution: {integrity: sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==} + engines: {node: '>=12'} + + pify@4.0.1: + resolution: {integrity: sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==} + engines: {node: '>=6'} + + pirates@4.0.7: + resolution: {integrity: sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==} + engines: {node: '>= 6'} + + pkg-dir@4.2.0: + resolution: {integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==} + engines: {node: '>=8'} + + plist@3.1.1: + resolution: {integrity: sha512-ZIfcLJC+7E7FBFnDxm9MPmt7D+DidyQ26lewieO75AdhA2ayMtsJSES0iWzqJQbcVRSrTufQoy0DR94xHue0oA==} + engines: {node: '>=10.4.0'} + + pngjs@3.4.0: + resolution: {integrity: sha512-NCrCHhWmnQklfH4MtJMRjZ2a8c80qXeMlQMv2uVp9ISJMTt562SbGd6n2oq0PaPgKm7Z6pL9E2UlLIhC+SHL3w==} + engines: {node: '>=4.0.0'} + + possible-typed-array-names@1.1.0: + resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==} + engines: {node: '>= 0.4'} + + postcss-value-parser@4.2.0: + resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} + + postcss@8.4.49: + resolution: {integrity: sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==} + engines: {node: ^10 || ^12 || >=14} + + prelude-ls@1.2.1: + resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} + engines: {node: '>= 0.8.0'} + + prettier-linter-helpers@1.0.1: + resolution: {integrity: sha512-SxToR7P8Y2lWmv/kTzVLC1t/GDI2WGjMwNhLLE9qtH8Q13C+aEmuRlzDst4Up4s0Wc8sF2M+J57iB3cMLqftfg==} + engines: {node: '>=6.0.0'} + + prettier@2.8.8: + resolution: {integrity: sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==} + engines: {node: '>=10.13.0'} + hasBin: true + + pretty-format@29.7.0: + resolution: {integrity: sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + pretty-format@30.3.0: + resolution: {integrity: sha512-oG4T3wCbfeuvljnyAzhBvpN45E8iOTXCU/TD3zXW80HA3dQ4ahdqMkWGiPWZvjpQwlbyHrPTWUAqUzGzv4l1JQ==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + proc-log@4.2.0: + resolution: {integrity: sha512-g8+OnU/L2v+wyiVK+D5fA34J7EH8jZ8DDlvwhRCMxmMj7UCBvxiO1mGeN+36JXIKF4zevU4kRBd8lVgG9vLelA==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + progress@2.0.3: + resolution: {integrity: sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==} + engines: {node: '>=0.4.0'} + + promise@7.3.1: + resolution: {integrity: sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==} + + promise@8.3.0: + resolution: {integrity: sha512-rZPNPKTOYVNEEKFaq1HqTgOwZD+4/YHS5ukLzQCypkj+OkYx7iv0mA91lJlpPPZ8vMau3IIGj5Qlwrx+8iiSmg==} + + prompts@2.4.2: + resolution: {integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==} + engines: {node: '>= 6'} + + prop-types@15.8.1: + resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} + + psl@1.15.0: + resolution: {integrity: sha512-JZd3gMVBAVQkSs6HdNZo9Sdo0LNcQeMNP3CozBJb3JYC/QUYZTnKxP+f8oWRX4rHP5EurWxqAHTSwUCjlNKa1w==} + + punycode@2.3.1: + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} + engines: {node: '>=6'} + + pure-rand@6.1.0: + resolution: {integrity: sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==} + + query-string@7.1.3: + resolution: {integrity: sha512-hh2WYhq4fi8+b+/2Kg9CEge4fDPvHS534aOOvOZeQ3+Vf2mCFsaFBYj0i+iXcAq6I9Vzp5fjMFBlONvayDC1qg==} + engines: {node: '>=6'} + + querystringify@2.2.0: + resolution: {integrity: sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==} + + queue@6.0.2: + resolution: {integrity: sha512-iHZWu+q3IdFZFX36ro/lKBkSvfkztY5Y7HMiPlOUjhupPcG2JMfst2KKEpu5XndviX/3UhFbRngUPNKtgvtZiA==} + + range-parser@1.2.1: + resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==} + engines: {node: '>= 0.6'} + + react-devtools-core@6.1.5: + resolution: {integrity: sha512-ePrwPfxAnB+7hgnEr8vpKxL9cmnp7F322t8oqcPshbIQQhDKgFDW4tjhF2wjVbdXF9O/nyuy3sQWd9JGpiLPvA==} + + react-dom@19.2.5: + resolution: {integrity: sha512-J5bAZz+DXMMwW/wV3xzKke59Af6CHY7G4uYLN1OvBcKEsWOs4pQExj86BBKamxl/Ik5bx9whOrvBlSDfWzgSag==} + peerDependencies: + react: ^19.2.5 + + react-fast-compare@3.2.2: + resolution: {integrity: sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ==} + + react-freeze@1.0.4: + resolution: {integrity: sha512-r4F0Sec0BLxWicc7HEyo2x3/2icUTrRmDjaaRyzzn+7aDyFZliszMDOgLVwSnQnYENOlL1o569Ze2HZefk8clA==} + engines: {node: '>=10'} + peerDependencies: + react: '>=17.0.0' + + react-is@16.13.1: + resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} + + react-is@18.3.1: + resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==} + + react-is@19.2.5: + resolution: {integrity: sha512-Dn0t8IQhCmeIT3wu+Apm1/YVsJXsGWi6k4sPdnBIdqMVtHtv0IGi6dcpNpNkNac0zB2uUAqNX3MHzN8c+z2rwQ==} + + react-native-gesture-handler@2.30.1: + resolution: {integrity: sha512-xIUBDo5ktmJs++0fZlavQNvDEE4PsihWhSeJsJtoz4Q6p0MiTM9TgrTgfEgzRR36qGPytFoeq+ShLrVwGdpUdA==} + peerDependencies: + react: '*' + react-native: '*' + + react-native-is-edge-to-edge@1.2.1: + resolution: {integrity: sha512-FLbPWl/MyYQWz+KwqOZsSyj2JmLKglHatd3xLZWskXOpRaio4LfEDEz8E/A6uD8QoTHW6Aobw1jbEwK7KMgR7Q==} + peerDependencies: + react: '*' + react-native: '*' + + react-native-is-edge-to-edge@1.3.1: + resolution: {integrity: sha512-NIXU/iT5+ORyCc7p0z2nnlkouYKX425vuU1OEm6bMMtWWR9yvb+Xg5AZmImTKoF9abxCPqrKC3rOZsKzUYgYZA==} + peerDependencies: + react: '*' + react-native: '*' + + react-native-reanimated@4.2.1: + resolution: {integrity: sha512-/NcHnZMyOvsD/wYXug/YqSKw90P9edN0kEPL5lP4PFf1aQ4F1V7MKe/E0tvfkXKIajy3Qocp5EiEnlcrK/+BZg==} + peerDependencies: + react: '*' + react-native: '*' + react-native-worklets: '>=0.7.0' + + react-native-safe-area-context@5.6.2: + resolution: {integrity: sha512-4XGqMNj5qjUTYywJqpdWZ9IG8jgkS3h06sfVjfw5yZQZfWnRFXczi0GnYyFyCc2EBps/qFmoCH8fez//WumdVg==} + peerDependencies: + react: '*' + react-native: '*' + + react-native-screens@4.23.0: + resolution: {integrity: sha512-XhO3aK0UeLpBn4kLecd+J+EDeRRJlI/Ro9Fze06vo1q163VeYtzfU9QS09/VyDFMWR1qxDC1iazCArTPSFFiPw==} + peerDependencies: + react: '*' + react-native: '*' + + react-native-svg@15.15.4: + resolution: {integrity: sha512-boT/vIRgj6zZKBpfTPJJiYWMbZE9duBMOwPK6kCSTgxsS947IFMOq9OgIFkpWZTB7t229H24pDRkh3W9ZK/J1A==} + peerDependencies: + react: '*' + react-native: '*' + + react-native-web@0.21.2: + resolution: {integrity: sha512-SO2t9/17zM4iEnFvlu2DA9jqNbzNhoUP+AItkoCOyFmDMOhUnBBznBDCYN92fGdfAkfQlWzPoez6+zLxFNsZEg==} + peerDependencies: + react: ^18.0.0 || ^19.0.0 + react-dom: ^18.0.0 || ^19.0.0 + + react-native-webview@13.16.1: + resolution: {integrity: sha512-If0eHhoEdOYDcHsX+xBFwHMbWBGK1BvGDQDQdVkwtSIXiq1uiqjkpWVP2uQ1as94J0CzvFE9PUNDuhiX0Z6ubw==} + peerDependencies: + react: '*' + react-native: '*' + + react-native-worklets@0.7.4: + resolution: {integrity: sha512-NYOdM1MwBb3n+AtMqy1tFy3Mn8DliQtd8sbzAVRf9Gc+uvQ0zRfxN7dS8ZzoyX7t6cyQL5THuGhlnX+iFlQTag==} + peerDependencies: + '@babel/core': '*' + react: '*' + react-native: '*' + + react-native@0.83.6: + resolution: {integrity: sha512-H513+8VzviNFXOdPnStRzX9S3/jiJGg++QZ1zd+ROyAvBEKqFqKUPHH0d82y3QyRPct5qKjdOa7J6vNehCvXYA==} + engines: {node: '>= 20.19.4'} + hasBin: true + peerDependencies: + '@types/react': ^19.1.1 + react: ^19.2.0 + peerDependenciesMeta: + '@types/react': + optional: true + + react-native@0.85.2: + resolution: {integrity: sha512-GFWEPwLYirfj5X8gMtXOWtqX0cqUEURRHETZfFk37VCa4++izrKvGvv24anvuyulXV87NAhVkfNw93rLg3HByw==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} + hasBin: true + peerDependencies: + '@react-native/jest-preset': 0.85.2 + '@types/react': ^19.1.1 + react: ^19.2.3 + peerDependenciesMeta: + '@react-native/jest-preset': + optional: true + '@types/react': + optional: true + + react-refresh@0.14.2: + resolution: {integrity: sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==} + engines: {node: '>=0.10.0'} + + react-remove-scroll-bar@2.3.8: + resolution: {integrity: sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + + react-remove-scroll@2.7.2: + resolution: {integrity: sha512-Iqb9NjCCTt6Hf+vOdNIZGdTiH1QSqr27H/Ek9sv/a97gfueI/5h1s3yRi1nngzMUaOOToin5dI1dXKdXiF+u0Q==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + react-style-singleton@2.2.3: + resolution: {integrity: sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + react-test-renderer@19.2.0: + resolution: {integrity: sha512-zLCFMHFE9vy/w3AxO0zNxy6aAupnCuLSVOJYDe/Tp+ayGI1f2PLQsFVPANSD42gdSbmYx5oN+1VWDhcXtq7hAQ==} + peerDependencies: + react: ^19.2.0 + + react@19.2.0: + resolution: {integrity: sha512-tmbWg6W31tQLeB5cdIBOicJDJRR2KzXsV7uSK9iNfLWQ5bIZfxuPEHp7M8wiHyHnn0DD1i7w3Zmin0FtkrwoCQ==} + engines: {node: '>=0.10.0'} + + readdirp@3.6.0: + resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} + engines: {node: '>=8.10.0'} + + redent@3.0.0: + resolution: {integrity: sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==} + engines: {node: '>=8'} + + reflect.getprototypeof@1.0.10: + resolution: {integrity: sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==} + engines: {node: '>= 0.4'} + + regenerate-unicode-properties@10.2.2: + resolution: {integrity: sha512-m03P+zhBeQd1RGnYxrGyDAPpWX/epKirLrp8e3qevZdVkKtnCrjjWczIbYc8+xd6vcTStVlqfycTx1KR4LOr0g==} + engines: {node: '>=4'} + + regenerate@1.4.2: + resolution: {integrity: sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==} + + regenerator-runtime@0.13.11: + resolution: {integrity: sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==} + + regexp.prototype.flags@1.5.4: + resolution: {integrity: sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==} + engines: {node: '>= 0.4'} + + regexpp@3.2.0: + resolution: {integrity: sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==} + engines: {node: '>=8'} + + regexpu-core@6.4.0: + resolution: {integrity: sha512-0ghuzq67LI9bLXpOX/ISfve/Mq33a4aFRzoQYhnnok1JOFpmE/A2TBGkNVenOGEeSBCjIiWcc6MVOG5HEQv0sA==} + engines: {node: '>=4'} + + regjsgen@0.8.0: + resolution: {integrity: sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q==} + + regjsparser@0.13.1: + resolution: {integrity: sha512-dLsljMd9sqwRkby8zhO1gSg3PnJIBFid8f4CQj/sXx+7cKx+E7u0PKhZ+U4wmhx7EfmtvnA318oVaIkAB1lRJw==} + hasBin: true + + require-directory@2.1.1: + resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} + engines: {node: '>=0.10.0'} + + requires-port@1.0.0: + resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==} + + resolve-cwd@3.0.0: + resolution: {integrity: sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==} + engines: {node: '>=8'} + + resolve-from@4.0.0: + resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} + engines: {node: '>=4'} + + resolve-from@5.0.0: + resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} + engines: {node: '>=8'} + + resolve-pkg-maps@1.0.0: + resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} + + resolve-workspace-root@2.0.1: + resolution: {integrity: sha512-nR23LHAvaI6aHtMg6RWoaHpdR4D881Nydkzi2CixINyg9T00KgaJdJI6Vwty+Ps8WLxZHuxsS0BseWjxSA4C+w==} + + resolve.exports@2.0.3: + resolution: {integrity: sha512-OcXjMsGdhL4XnbShKpAcSqPMzQoYkYyhbEaeSko47MjRP9NfEQMhZkXL1DoFlt9LWQn4YttrdnV6X2OiyzBi+A==} + engines: {node: '>=10'} + + resolve@1.22.12: + resolution: {integrity: sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA==} + engines: {node: '>= 0.4'} + hasBin: true + + resolve@2.0.0-next.6: + resolution: {integrity: sha512-3JmVl5hMGtJ3kMmB3zi3DL25KfkCEyy3Tw7Gmw7z5w8M9WlwoPFnIvwChzu1+cF3iaK3sp18hhPz8ANeimdJfA==} + engines: {node: '>= 0.4'} + hasBin: true + + restore-cursor@2.0.0: + resolution: {integrity: sha512-6IzJLuGi4+R14vwagDHX+JrXmPVtPpn4mffDJ1UdR7/Edm87fl6yi8mMBIVvFtJaNTUvjughmW4hwLhRG7gC1Q==} + engines: {node: '>=4'} + + rimraf@3.0.2: + resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} + deprecated: Rimraf versions prior to v4 are no longer supported + hasBin: true + + safe-array-concat@1.1.4: + resolution: {integrity: sha512-wtZlHyOje6OZTGqAoaDKxFkgRtkF9CnHAVnCHKfuj200wAgL+bSJhdsCD2l0Qx/2ekEXjPWcyKkfGb5CPboslg==} + engines: {node: '>=0.4'} + + safe-buffer@5.2.1: + resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + + safe-push-apply@1.0.0: + resolution: {integrity: sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==} + engines: {node: '>= 0.4'} + + safe-regex-test@1.1.0: + resolution: {integrity: sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==} + engines: {node: '>= 0.4'} + + safer-buffer@2.1.2: + resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + + sax@1.6.0: + resolution: {integrity: sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA==} + engines: {node: '>=11.0.0'} + + saxes@6.0.0: + resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==} + engines: {node: '>=v12.22.7'} + + scheduler@0.27.0: + resolution: {integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==} + + semver@5.7.2: + resolution: {integrity: sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==} + hasBin: true + + semver@6.3.1: + resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} + hasBin: true + + semver@7.6.3: + resolution: {integrity: sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==} + engines: {node: '>=10'} + hasBin: true + + semver@7.7.3: + resolution: {integrity: sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==} + engines: {node: '>=10'} + hasBin: true + + semver@7.7.4: + resolution: {integrity: sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==} + engines: {node: '>=10'} + hasBin: true + + send@0.19.2: + resolution: {integrity: sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg==} + engines: {node: '>= 0.8.0'} + + serialize-error@2.1.0: + resolution: {integrity: sha512-ghgmKt5o4Tly5yEG/UJp8qTd0AN7Xalw4XBtDEKP655B699qMEtra1WlXeE6WIvdEG481JvRxULKsInq/iNysw==} + engines: {node: '>=0.10.0'} + + serve-static@1.16.3: + resolution: {integrity: sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA==} + engines: {node: '>= 0.8.0'} + + server-only@0.0.1: + resolution: {integrity: sha512-qepMx2JxAa5jjfzxG79yPPq+8BuFToHd1hm7kI+Z4zAq1ftQiP7HcxMhDDItrbtwVeLg/cY2JnKnrcFkmiswNA==} + + set-function-length@1.2.2: + resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} + engines: {node: '>= 0.4'} + + set-function-name@2.0.2: + resolution: {integrity: sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==} + engines: {node: '>= 0.4'} + + set-proto@1.0.0: + resolution: {integrity: sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==} + engines: {node: '>= 0.4'} + + setimmediate@1.0.5: + resolution: {integrity: sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==} + + setprototypeof@1.2.0: + resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} + + sf-symbols-typescript@2.2.0: + resolution: {integrity: sha512-TPbeg0b7ylrswdGCji8FRGFAKuqbpQlLbL8SOle3j1iHSs5Ob5mhvMAxWN2UItOjgALAB5Zp3fmMfj8mbWvXKw==} + engines: {node: '>=10'} + + shallowequal@1.1.0: + resolution: {integrity: sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==} + + shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + + shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + + shell-quote@1.8.3: + resolution: {integrity: sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==} + engines: {node: '>= 0.4'} + + side-channel-list@1.0.1: + resolution: {integrity: sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==} + engines: {node: '>= 0.4'} + + side-channel-map@1.0.1: + resolution: {integrity: sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==} + engines: {node: '>= 0.4'} + + side-channel-weakmap@1.0.2: + resolution: {integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==} + engines: {node: '>= 0.4'} + + side-channel@1.1.0: + resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==} + engines: {node: '>= 0.4'} + + signal-exit@3.0.7: + resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} + + simple-plist@1.3.1: + resolution: {integrity: sha512-iMSw5i0XseMnrhtIzRb7XpQEXepa9xhWxGUojHBL43SIpQuDQkh3Wpy67ZbDzZVr6EKxvwVChnVpdl8hEVLDiw==} + + simple-swizzle@0.2.4: + resolution: {integrity: sha512-nAu1WFPQSMNr2Zn9PGSZK9AGn4t/y97lEm+MXTtUDwfP0ksAIX4nO+6ruD9Jwut4C49SB1Ws+fbXsm/yScWOHw==} + + sisteransi@1.0.5: + resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} + + slash@2.0.0: + resolution: {integrity: sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==} + engines: {node: '>=6'} + + slash@3.0.0: + resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} + engines: {node: '>=8'} + + slash@5.1.0: + resolution: {integrity: sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==} + engines: {node: '>=14.16'} + + slugify@1.6.9: + resolution: {integrity: sha512-vZ7rfeehZui7wQs438JXBckYLkIIdfHOXsaVEUMyS5fHo1483l1bMdo0EDSWYclY0yZKFOipDy4KHuKs6ssvdg==} + engines: {node: '>=8.0.0'} + + source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + engines: {node: '>=0.10.0'} + + source-map-support@0.5.13: + resolution: {integrity: sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==} + + source-map-support@0.5.21: + resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==} + + source-map@0.5.6: + resolution: {integrity: sha512-MjZkVp0NHr5+TPihLcadqnlVoGIoWo4IBHptutGh9wI3ttUYvCG26HkSuDi+K6lsZ25syXJXcctwgyVCt//xqA==} + engines: {node: '>=0.10.0'} + + source-map@0.5.7: + resolution: {integrity: sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==} + engines: {node: '>=0.10.0'} + + source-map@0.6.1: + resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} + engines: {node: '>=0.10.0'} + + split-on-first@1.1.0: + resolution: {integrity: sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw==} + engines: {node: '>=6'} + + sprintf-js@1.0.3: + resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} + + stack-generator@2.0.10: + resolution: {integrity: sha512-mwnua/hkqM6pF4k8SnmZ2zfETsRUpWXREfA/goT8SLCV4iOFa4bzOX2nDipWAZFPTjLvQB82f5yaodMVhK0yJQ==} + + stack-utils@2.0.6: + resolution: {integrity: sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==} + engines: {node: '>=10'} + + stackframe@1.3.4: + resolution: {integrity: sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==} + + stacktrace-gps@3.1.2: + resolution: {integrity: sha512-GcUgbO4Jsqqg6RxfyTHFiPxdPqF+3LFmQhm7MgCuYQOYuWyqxo5pwRPz5d/u6/WYJdEnWfK4r+jGbyD8TSggXQ==} + + stacktrace-js@2.0.2: + resolution: {integrity: sha512-Je5vBeY4S1r/RnLydLl0TBTi3F2qdfWmYsGvtfZgEI+SCprPppaIhQf5nGcal4gI4cGpCV/duLcAzT1np6sQqg==} + + stacktrace-parser@0.1.11: + resolution: {integrity: sha512-WjlahMgHmCJpqzU8bIBy4qtsZdU9lRlcZE3Lvyej6t4tuOuv1vk57OW3MBrj6hXBFx/nNoC9MPMTcr5YA7NQbg==} + engines: {node: '>=6'} + + statuses@1.5.0: + resolution: {integrity: sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==} + engines: {node: '>= 0.6'} + + statuses@2.0.2: + resolution: {integrity: sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==} + engines: {node: '>= 0.8'} + + stop-iteration-iterator@1.1.0: + resolution: {integrity: sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==} + engines: {node: '>= 0.4'} + + stream-buffers@2.2.0: + resolution: {integrity: sha512-uyQK/mx5QjHun80FLJTfaWE7JtwfRMKBLkMne6udYOmvH0CawotVa7TfgYHzAnpphn4+TweIx1QKMnRIbipmUg==} + engines: {node: '>= 0.10.0'} + + strict-uri-encode@2.0.0: + resolution: {integrity: sha512-QwiXZgpRcKkhTj2Scnn++4PKtWsH0kpzZ62L2R6c/LUVYv7hVnZqcg2+sMuT6R7Jusu1vviK/MFsu6kNJfWlEQ==} + engines: {node: '>=4'} + + string-length@4.0.2: + resolution: {integrity: sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==} + engines: {node: '>=10'} + + string-length@5.0.1: + resolution: {integrity: sha512-9Ep08KAMUn0OadnVaBuRdE2l615CQ508kr0XMadjClfYpdCyvrbFp6Taebo8yyxokQ4viUd/xPPUA4FGgUa0ow==} + engines: {node: '>=12.20'} + + string-width@4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} + + string.prototype.matchall@4.0.12: + resolution: {integrity: sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA==} + engines: {node: '>= 0.4'} + + string.prototype.repeat@1.0.0: + resolution: {integrity: sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==} + + string.prototype.trim@1.2.10: + resolution: {integrity: sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==} + engines: {node: '>= 0.4'} + + string.prototype.trimend@1.0.9: + resolution: {integrity: sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==} + engines: {node: '>= 0.4'} + + string.prototype.trimstart@1.0.8: + resolution: {integrity: sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==} + engines: {node: '>= 0.4'} + + strip-ansi@5.2.0: + resolution: {integrity: sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==} + engines: {node: '>=6'} + + strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + + strip-ansi@7.2.0: + resolution: {integrity: sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==} + engines: {node: '>=12'} + + strip-bom@3.0.0: + resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} + engines: {node: '>=4'} + + strip-bom@4.0.0: + resolution: {integrity: sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==} + engines: {node: '>=8'} + + strip-final-newline@2.0.0: + resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==} + engines: {node: '>=6'} + + strip-indent@3.0.0: + resolution: {integrity: sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==} + engines: {node: '>=8'} + + strip-json-comments@3.1.1: + resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} + engines: {node: '>=8'} + + structured-headers@0.4.1: + resolution: {integrity: sha512-0MP/Cxx5SzeeZ10p/bZI0S6MpgD+yxAhi1BOQ34jgnMXsCq3j1t6tQnZu+KdlL7dvJTLT3g9xN8tl10TqgFMcg==} + + styleq@0.1.3: + resolution: {integrity: sha512-3ZUifmCDCQanjeej1f6kyl/BeP/Vae5EYkQ9iJfUm/QwZvlgnZzyflqAsAWYURdtea8Vkvswu2GrC57h3qffcA==} + + supports-color@5.5.0: + resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} + engines: {node: '>=4'} + + supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + + supports-color@8.1.1: + resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==} + engines: {node: '>=10'} + + supports-hyperlinks@2.3.0: + resolution: {integrity: sha512-RpsAZlpWcDwOPQA22aCH4J0t7L8JmAvsCxfOSEwm7cQs3LshN36QaTkwd70DnBOXDWGssw2eUoc8CaRWT0XunA==} + engines: {node: '>=8'} + + supports-preserve-symlinks-flag@1.0.0: + resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} + engines: {node: '>= 0.4'} + + symbol-tree@3.2.4: + resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} + + synckit@0.11.12: + resolution: {integrity: sha512-Bh7QjT8/SuKUIfObSXNHNSK6WHo6J1tHCqJsuaFDP7gP0fkzSfTxI8y85JrppZ0h8l0maIgc2tfuZQ6/t3GtnQ==} + engines: {node: ^14.18.0 || >=16.0.0} + + tagged-tag@1.0.0: + resolution: {integrity: sha512-yEFYrVhod+hdNyx7g5Bnkkb0G6si8HJurOoOEgC8B/O0uXLHlaey/65KRv6cuWBNhBgHKAROVpc7QyYqE5gFng==} + engines: {node: '>=20'} + + tapable@2.3.3: + resolution: {integrity: sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==} + engines: {node: '>=6'} + + terminal-link@2.1.1: + resolution: {integrity: sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ==} + engines: {node: '>=8'} + + terser@5.46.2: + resolution: {integrity: sha512-uxfo9fPcSgLDYob/w1FuL0c99MWiJDnv+5qXSQc5+Ki5NjVNsYi66INnMFBjf6uFz6OnX12piJQPF4IpjJTNTw==} + engines: {node: '>=10'} + hasBin: true + + test-exclude@6.0.0: + resolution: {integrity: sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==} + engines: {node: '>=8'} + + throat@5.0.0: + resolution: {integrity: sha512-fcwX4mndzpLQKBS1DVYhGAcYaYt7vsHNIvQV+WXMvnow5cgjPphq5CaayLaGsjRdSCKZFNGt7/GYAuXaNOiYCA==} + + tinyglobby@0.2.16: + resolution: {integrity: sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==} + engines: {node: '>=12.0.0'} + + tinypool@2.1.0: + resolution: {integrity: sha512-Pugqs6M0m7Lv1I7FtxN4aoyToKg1C4tu+/381vH35y8oENM/Ai7f7C4StcoK4/+BSw9ebcS8jRiVrORFKCALLw==} + engines: {node: ^20.0.0 || >=22.0.0} + + tmpl@1.0.5: + resolution: {integrity: sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==} + + to-regex-range@5.0.1: + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} + + toidentifier@1.0.1: + resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} + engines: {node: '>=0.6'} + + toqr@0.1.1: + resolution: {integrity: sha512-FWAPzCIHZHnrE/5/w9MPk0kK25hSQSH2IKhYh9PyjS3SG/+IEMvlwIHbhz+oF7xl54I+ueZlVnMjyzdSwLmAwA==} + + tough-cookie@4.1.4: + resolution: {integrity: sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==} + engines: {node: '>=6'} + + tr46@0.0.3: + resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} + + tr46@3.0.0: + resolution: {integrity: sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==} + engines: {node: '>=12'} + + ts-api-utils@2.5.0: + resolution: {integrity: sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==} + engines: {node: '>=18.12'} + peerDependencies: + typescript: '>=4.8.4' + + ts-declaration-location@1.0.7: + resolution: {integrity: sha512-EDyGAwH1gO0Ausm9gV6T2nUvBgXT5kGoCMJPllOaooZ+4VvJiKBdZE7wK18N1deEowhcUptS+5GXZK8U/fvpwA==} + peerDependencies: + typescript: '>=4.0.0' + + ts-jest@29.0.5: + resolution: {integrity: sha512-PL3UciSgIpQ7f6XjVOmbi96vmDHUqAyqDr8YxzopDqX3kfgYtX1cuNeBjP+L9sFXi6nzsGGA6R3fP3DDDJyrxA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + hasBin: true + peerDependencies: + '@babel/core': '>=7.0.0-beta.0 <8' + '@jest/types': ^29.0.0 + babel-jest: ^29.0.0 + esbuild: '*' + jest: ^29.0.0 + typescript: '>=4.3' + peerDependenciesMeta: + '@babel/core': + optional: true + '@jest/types': + optional: true + babel-jest: + optional: true + esbuild: + optional: true + + tsconfig-paths@3.15.0: + resolution: {integrity: sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==} + + tslib@2.8.1: + resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + + tsx@4.21.0: + resolution: {integrity: sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==} + engines: {node: '>=18.0.0'} + hasBin: true + + tweetnacl@1.0.3: + resolution: {integrity: sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==} + + type-check@0.4.0: + resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} + engines: {node: '>= 0.8.0'} + + type-detect@4.0.8: + resolution: {integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==} + engines: {node: '>=4'} + + type-fest@0.21.3: + resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==} + engines: {node: '>=10'} + + type-fest@0.7.1: + resolution: {integrity: sha512-Ne2YiiGN8bmrmJJEuTWTLJR32nh/JdL1+PSicowtNb0WFpn59GK8/lfD61bVtzguz7b3PBt74nxpv/Pw5po5Rg==} + engines: {node: '>=8'} + + type-fest@5.6.0: + resolution: {integrity: sha512-8ZiHFm91orbSAe2PSAiSVBVko18pbhbiB3U9GglSzF/zCGkR+rxpHx6sEMCUm4kxY4LjDIUGgCfUMtwfZfjfUA==} + engines: {node: '>=20'} + + typed-array-buffer@1.0.3: + resolution: {integrity: sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==} + engines: {node: '>= 0.4'} + + typed-array-byte-length@1.0.3: + resolution: {integrity: sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==} + engines: {node: '>= 0.4'} + + typed-array-byte-offset@1.0.4: + resolution: {integrity: sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==} + engines: {node: '>= 0.4'} + + typed-array-length@1.0.7: + resolution: {integrity: sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==} + engines: {node: '>= 0.4'} + + typescript@5.9.3: + resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} + engines: {node: '>=14.17'} + hasBin: true + + ua-parser-js@1.0.41: + resolution: {integrity: sha512-LbBDqdIC5s8iROCUjMbW1f5dJQTEFB1+KO9ogbvlb3nm9n4YHa5p4KTvFPWvh2Hs8gZMBuiB1/8+pdfe/tDPug==} + hasBin: true + + unbox-primitive@1.1.0: + resolution: {integrity: sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==} + engines: {node: '>= 0.4'} + + undici-types@7.19.2: + resolution: {integrity: sha512-qYVnV5OEm2AW8cJMCpdV20CDyaN3g0AjDlOGf1OW4iaDEx8MwdtChUp4zu4H0VP3nDRF/8RKWH+IPp9uW0YGZg==} + + unicode-canonical-property-names-ecmascript@2.0.1: + resolution: {integrity: sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg==} + engines: {node: '>=4'} + + unicode-match-property-ecmascript@2.0.0: + resolution: {integrity: sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==} + engines: {node: '>=4'} + + unicode-match-property-value-ecmascript@2.2.1: + resolution: {integrity: sha512-JQ84qTuMg4nVkx8ga4A16a1epI9H6uTXAknqxkGF/aFfRLw1xC/Bp24HNLaZhHSkWd3+84t8iXnp1J0kYcZHhg==} + engines: {node: '>=4'} + + unicode-property-aliases-ecmascript@2.2.0: + resolution: {integrity: sha512-hpbDzxUY9BFwX+UeBnxv3Sh1q7HFxj48DTmXchNgRa46lO8uj3/1iEn3MiNUYTg1g9ctIqXCCERn8gYZhHC5lQ==} + engines: {node: '>=4'} + + universalify@0.2.0: + resolution: {integrity: sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==} + engines: {node: '>= 4.0.0'} + + unpipe@1.0.0: + resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} + engines: {node: '>= 0.8'} + + update-browserslist-db@1.2.3: + resolution: {integrity: sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + + uri-js@4.4.1: + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + + url-parse@1.5.10: + resolution: {integrity: sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==} + + use-callback-ref@1.3.3: + resolution: {integrity: sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + use-latest-callback@0.2.6: + resolution: {integrity: sha512-FvRG9i1HSo0wagmX63Vrm8SnlUU3LMM3WyZkQ76RnslpBrX694AdG4A0zQBx2B3ZifFA0yv/BaEHGBnEax5rZg==} + peerDependencies: + react: '>=16.8' + + use-sidecar@1.1.3: + resolution: {integrity: sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + use-sync-external-store@1.6.0: + resolution: {integrity: sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + + utils-merge@1.0.1: + resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==} + engines: {node: '>= 0.4.0'} + + uuid@7.0.3: + resolution: {integrity: sha512-DPSke0pXhTZgoF/d+WSt2QaKMCFSfx7QegxEWT+JOuHF5aWrKEn0G+ztjuJg/gG8/ItK+rbPCD/yNv8yyih6Cg==} + hasBin: true + + v8-to-istanbul@9.3.0: + resolution: {integrity: sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==} + engines: {node: '>=10.12.0'} + + validate-npm-package-name@5.0.1: + resolution: {integrity: sha512-OljLrQ9SQdOUqTaQxqL5dEfZWrXExyyWsozYlAWFawPVNuD83igl7uJD2RTkNMbniIYgt8l81eCJGIdQF7avLQ==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + vary@1.1.2: + resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} + engines: {node: '>= 0.8'} + + vaul@1.1.2: + resolution: {integrity: sha512-ZFkClGpWyI2WUQjdLJ/BaGuV6AVQiJ3uELGk3OYtP+B6yCO7Cmn9vPFXVJkRaGkOJu3m8bQMgtyzNHixULceQA==} + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc + + vlq@1.0.1: + resolution: {integrity: sha512-gQpnTgkubC6hQgdIcRdYGDSDc+SaujOdyesZQMv6JlfQee/9Mp0Qhnys6WxDWvQnL5WZdT7o2Ul187aSt0Rq+w==} + + w3c-xmlserializer@4.0.0: + resolution: {integrity: sha512-d+BFHzbiCx6zGfz0HyQ6Rg69w9k19nviJspaj4yNscGjrHu94sVP+aRm75yEbCh+r2/yR+7q6hux9LVtbuTGBw==} + engines: {node: '>=14'} + + walker@1.0.8: + resolution: {integrity: sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==} + + warn-once@0.1.1: + resolution: {integrity: sha512-VkQZJbO8zVImzYFteBXvBOZEl1qL175WH8VmZcxF2fZAoudNhNDvHi+doCaAEdU2l2vtcIwa2zn0QK5+I1HQ3Q==} + + wcwidth@1.0.1: + resolution: {integrity: sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==} + + webidl-conversions@3.0.1: + resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} + + webidl-conversions@7.0.0: + resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==} + engines: {node: '>=12'} + + whatwg-encoding@2.0.0: + resolution: {integrity: sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==} + engines: {node: '>=12'} + deprecated: Use @exodus/bytes instead for a more spec-conformant and faster implementation + + whatwg-fetch@3.6.20: + resolution: {integrity: sha512-EqhiFU6daOA8kpjOWTL0olhVOF3i7OrFzSYiGsEMB8GcXS+RrzauAERX65xMeNWVqxA6HXH2m69Z9LaKKdisfg==} + + whatwg-mimetype@3.0.0: + resolution: {integrity: sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==} + engines: {node: '>=12'} + + whatwg-url-minimum@0.1.1: + resolution: {integrity: sha512-u2FNVjFVFZhdjb502KzXy1gKn1mEisQRJssmSJT8CPhZdZa0AP6VCbWlXERKyGu0l09t0k50FiDiralpGhBxgA==} + + whatwg-url@11.0.0: + resolution: {integrity: sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==} + engines: {node: '>=12'} + + whatwg-url@5.0.0: + resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} + + which-boxed-primitive@1.1.1: + resolution: {integrity: sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==} + engines: {node: '>= 0.4'} + + which-builtin-type@1.2.1: + resolution: {integrity: sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==} + engines: {node: '>= 0.4'} + + which-collection@1.0.2: + resolution: {integrity: sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==} + engines: {node: '>= 0.4'} + + which-typed-array@1.1.20: + resolution: {integrity: sha512-LYfpUkmqwl0h9A2HL09Mms427Q1RZWuOHsukfVcKRq9q95iQxdw0ix1JQrqbcDR9PH1QDwf5Qo8OZb5lksZ8Xg==} + engines: {node: '>= 0.4'} + + which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + + word-wrap@1.2.5: + resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} + engines: {node: '>=0.10.0'} + + wrap-ansi@7.0.0: + resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} + engines: {node: '>=10'} + + wrappy@1.0.2: + resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + + write-file-atomic@4.0.2: + resolution: {integrity: sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==} + engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + + ws@7.5.10: + resolution: {integrity: sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==} + engines: {node: '>=8.3.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: ^5.0.2 + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + + ws@8.20.0: + resolution: {integrity: sha512-sAt8BhgNbzCtgGbt2OxmpuryO63ZoDk/sqaB/znQm94T4fCEsy/yV+7CdC1kJhOU9lboAEU7R3kquuycDoibVA==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + + xcode@3.0.1: + resolution: {integrity: sha512-kCz5k7J7XbJtjABOvkc5lJmkiDh8VhjVCGNiqdKCscmVpdVUpEAyXv1xmCLkQJ5dsHqx3IPO4XW+NTDhU/fatA==} + engines: {node: '>=10.0.0'} + + xml-name-validator@4.0.0: + resolution: {integrity: sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==} + engines: {node: '>=12'} + + xml2js@0.6.0: + resolution: {integrity: sha512-eLTh0kA8uHceqesPqSE+VvO1CDDJWMwlQfB6LuN6T8w6MaDJ8Txm8P7s5cHD0miF0V+GGTZrDQfxPZQVsur33w==} + engines: {node: '>=4.0.0'} + + xmlbuilder@11.0.1: + resolution: {integrity: sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==} + engines: {node: '>=4.0'} + + xmlbuilder@15.1.1: + resolution: {integrity: sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg==} + engines: {node: '>=8.0'} + + xmlchars@2.2.0: + resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==} + + y18n@5.0.8: + resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} + engines: {node: '>=10'} + + yallist@3.1.1: + resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + + yaml@2.8.3: + resolution: {integrity: sha512-AvbaCLOO2Otw/lW5bmh9d/WEdcDFdQp2Z2ZUH3pX9U2ihyUY0nvLv7J6TrWowklRGPYbB/IuIMfYgxaCPg5Bpg==} + engines: {node: '>= 14.6'} + hasBin: true + + yargs-parser@21.1.1: + resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} + engines: {node: '>=12'} + + yargs@17.7.2: + resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} + engines: {node: '>=12'} + + yocto-queue@0.1.0: + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} + + zod@3.25.76: + resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==} + + zod@4.3.6: + resolution: {integrity: sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==} + + zustand@5.0.12: + resolution: {integrity: sha512-i77ae3aZq4dhMlRhJVCYgMLKuSiZAaUPAct2AksxQ+gOtimhGMdXljRT21P5BNpeT4kXlLIckvkPM029OljD7g==} + engines: {node: '>=12.20.0'} + peerDependencies: + '@types/react': '>=18.0.0' + immer: '>=9.0.6' + react: '>=18.0.0' + use-sync-external-store: '>=1.2.0' + peerDependenciesMeta: + '@types/react': + optional: true + immer: + optional: true + react: + optional: true + use-sync-external-store: + optional: true + + zxing-wasm@3.0.2: + resolution: {integrity: sha512-2YMAriaYHX9wrBY2k7H0epSo+dyCaCZg/vOtt+nEDXM9ul480gkXz/9SkwpOeHcD2H5qqDG8lWDSBwpTcZpa6w==} + peerDependencies: + '@types/emscripten': '>=1.39.6' + +snapshots: + + '@babel/cli@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@jridgewell/trace-mapping': 0.3.31 + commander: 6.2.1 + convert-source-map: 2.0.0 + fs-readdir-recursive: 1.1.0 + glob: 7.2.3 + make-dir: 2.1.0 + slash: 2.0.0 + optionalDependencies: + '@nicolo-ribaudo/chokidar-2': 2.1.8-no-fsevents.3 + chokidar: 3.6.0 + + '@babel/code-frame@7.29.0': + dependencies: + '@babel/helper-validator-identifier': 7.28.5 + js-tokens: 4.0.0 + picocolors: 1.1.1 + + '@babel/compat-data@7.29.0': {} + + '@babel/core@7.29.0': + dependencies: + '@babel/code-frame': 7.29.0 + '@babel/generator': 7.29.1 + '@babel/helper-compilation-targets': 7.28.6 + '@babel/helper-module-transforms': 7.28.6(@babel/core@7.29.0) + '@babel/helpers': 7.29.2 + '@babel/parser': 7.29.2 + '@babel/template': 7.28.6 + '@babel/traverse': 7.29.0 + '@babel/types': 7.29.0 + '@jridgewell/remapping': 2.3.5 + convert-source-map: 2.0.0 + debug: 4.4.3 + gensync: 1.0.0-beta.2 + json5: 2.2.3 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/generator@7.29.1': + dependencies: + '@babel/parser': 7.29.2 + '@babel/types': 7.29.0 + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + jsesc: 3.1.0 + + '@babel/helper-annotate-as-pure@7.27.3': + dependencies: + '@babel/types': 7.29.0 + + '@babel/helper-compilation-targets@7.28.6': + dependencies: + '@babel/compat-data': 7.29.0 + '@babel/helper-validator-option': 7.27.1 + browserslist: 4.28.2 + lru-cache: 5.1.1 + semver: 6.3.1 + + '@babel/helper-create-class-features-plugin@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-annotate-as-pure': 7.27.3 + '@babel/helper-member-expression-to-functions': 7.28.5 + '@babel/helper-optimise-call-expression': 7.27.1 + '@babel/helper-replace-supers': 7.28.6(@babel/core@7.29.0) + '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 + '@babel/traverse': 7.29.0 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/helper-create-regexp-features-plugin@7.28.5(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-annotate-as-pure': 7.27.3 + regexpu-core: 6.4.0 + semver: 6.3.1 + + '@babel/helper-define-polyfill-provider@0.6.8(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-compilation-targets': 7.28.6 + '@babel/helper-plugin-utils': 7.28.6 + debug: 4.4.3 + lodash.debounce: 4.0.8 + resolve: 1.22.12 + transitivePeerDependencies: + - supports-color + + '@babel/helper-globals@7.28.0': {} + + '@babel/helper-member-expression-to-functions@7.28.5': + dependencies: + '@babel/traverse': 7.29.0 + '@babel/types': 7.29.0 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-imports@7.28.6': + dependencies: + '@babel/traverse': 7.29.0 + '@babel/types': 7.29.0 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-transforms@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-module-imports': 7.28.6 + '@babel/helper-validator-identifier': 7.28.5 + '@babel/traverse': 7.29.0 + transitivePeerDependencies: + - supports-color + + '@babel/helper-optimise-call-expression@7.27.1': + dependencies: + '@babel/types': 7.29.0 + + '@babel/helper-plugin-utils@7.28.6': {} + + '@babel/helper-remap-async-to-generator@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-annotate-as-pure': 7.27.3 + '@babel/helper-wrap-function': 7.28.6 + '@babel/traverse': 7.29.0 + transitivePeerDependencies: + - supports-color + + '@babel/helper-replace-supers@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-member-expression-to-functions': 7.28.5 + '@babel/helper-optimise-call-expression': 7.27.1 + '@babel/traverse': 7.29.0 + transitivePeerDependencies: + - supports-color + + '@babel/helper-skip-transparent-expression-wrappers@7.27.1': + dependencies: + '@babel/traverse': 7.29.0 + '@babel/types': 7.29.0 + transitivePeerDependencies: + - supports-color + + '@babel/helper-string-parser@7.27.1': {} + + '@babel/helper-validator-identifier@7.28.5': {} + + '@babel/helper-validator-option@7.27.1': {} + + '@babel/helper-wrap-function@7.28.6': + dependencies: + '@babel/template': 7.28.6 + '@babel/traverse': 7.29.0 + '@babel/types': 7.29.0 + transitivePeerDependencies: + - supports-color + + '@babel/helpers@7.29.2': + dependencies: + '@babel/template': 7.28.6 + '@babel/types': 7.29.0 + + '@babel/parser@7.29.2': + dependencies: + '@babel/types': 7.29.0 + + '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.28.5(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/traverse': 7.29.0 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-bugfix-safari-class-field-initializer-scope@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 + '@babel/plugin-transform-optional-chaining': 7.28.6(@babel/core@7.29.0) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/traverse': 7.29.0 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-proposal-decorators@7.29.0(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-create-class-features-plugin': 7.28.6(@babel/core@7.29.0) + '@babel/helper-plugin-utils': 7.28.6 + '@babel/plugin-syntax-decorators': 7.28.6(@babel/core@7.29.0) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-proposal-export-default-from@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + + '@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-class-static-block@7.14.5(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-decorators@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-dynamic-import@7.8.3(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-export-default-from@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-flow@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-import-assertions@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-import-attributes@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-jsx@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-private-property-in-object@7.14.5(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-typescript@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-unicode-sets-regex@7.18.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.29.0) + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-arrow-functions@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-async-generator-functions@7.29.0(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-remap-async-to-generator': 7.27.1(@babel/core@7.29.0) + '@babel/traverse': 7.29.0 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-async-to-generator@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-module-imports': 7.28.6 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-remap-async-to-generator': 7.27.1(@babel/core@7.29.0) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-block-scoped-functions@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-block-scoping@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-class-properties@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-create-class-features-plugin': 7.28.6(@babel/core@7.29.0) + '@babel/helper-plugin-utils': 7.28.6 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-class-properties@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-create-class-features-plugin': 7.28.6(@babel/core@7.29.0) + '@babel/helper-plugin-utils': 7.28.6 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-class-static-block@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-create-class-features-plugin': 7.28.6(@babel/core@7.29.0) + '@babel/helper-plugin-utils': 7.28.6 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-classes@7.28.4(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-annotate-as-pure': 7.27.3 + '@babel/helper-compilation-targets': 7.28.6 + '@babel/helper-globals': 7.28.0 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-replace-supers': 7.28.6(@babel/core@7.29.0) + '@babel/traverse': 7.29.0 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-classes@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-annotate-as-pure': 7.27.3 + '@babel/helper-compilation-targets': 7.28.6 + '@babel/helper-globals': 7.28.0 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-replace-supers': 7.28.6(@babel/core@7.29.0) + '@babel/traverse': 7.29.0 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-computed-properties@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/template': 7.28.6 + + '@babel/plugin-transform-destructuring@7.28.5(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/traverse': 7.29.0 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-dotall-regex@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.29.0) + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-duplicate-keys@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-duplicate-named-capturing-groups-regex@7.29.0(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.29.0) + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-dynamic-import@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-explicit-resource-management@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/plugin-transform-destructuring': 7.28.5(@babel/core@7.29.0) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-exponentiation-operator@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-export-namespace-from@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-flow-strip-types@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/plugin-syntax-flow': 7.28.6(@babel/core@7.29.0) + + '@babel/plugin-transform-for-of@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-function-name@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-compilation-targets': 7.28.6 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/traverse': 7.29.0 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-json-strings@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-literals@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-logical-assignment-operators@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-member-expression-literals@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-modules-amd@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-module-transforms': 7.28.6(@babel/core@7.29.0) + '@babel/helper-plugin-utils': 7.28.6 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-modules-commonjs@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-module-transforms': 7.28.6(@babel/core@7.29.0) + '@babel/helper-plugin-utils': 7.28.6 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-modules-systemjs@7.29.0(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-module-transforms': 7.28.6(@babel/core@7.29.0) + '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-validator-identifier': 7.28.5 + '@babel/traverse': 7.29.0 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-modules-umd@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-module-transforms': 7.28.6(@babel/core@7.29.0) + '@babel/helper-plugin-utils': 7.28.6 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-named-capturing-groups-regex@7.29.0(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.29.0) + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-new-target@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-nullish-coalescing-operator@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-nullish-coalescing-operator@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-numeric-separator@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-object-rest-spread@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-compilation-targets': 7.28.6 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/plugin-transform-destructuring': 7.28.5(@babel/core@7.29.0) + '@babel/plugin-transform-parameters': 7.27.7(@babel/core@7.29.0) + '@babel/traverse': 7.29.0 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-object-super@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-replace-supers': 7.28.6(@babel/core@7.29.0) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-optional-catch-binding@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-optional-chaining@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-optional-chaining@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-parameters@7.27.7(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-private-methods@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-create-class-features-plugin': 7.28.6(@babel/core@7.29.0) + '@babel/helper-plugin-utils': 7.28.6 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-private-property-in-object@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-annotate-as-pure': 7.27.3 + '@babel/helper-create-class-features-plugin': 7.28.6(@babel/core@7.29.0) + '@babel/helper-plugin-utils': 7.28.6 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-property-literals@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-react-display-name@7.28.0(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-react-jsx-development@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/plugin-transform-react-jsx': 7.28.6(@babel/core@7.29.0) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-react-jsx-self@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-react-jsx-source@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-react-jsx@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-annotate-as-pure': 7.27.3 + '@babel/helper-module-imports': 7.28.6 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/plugin-syntax-jsx': 7.28.6(@babel/core@7.29.0) + '@babel/types': 7.29.0 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-react-pure-annotations@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-annotate-as-pure': 7.27.3 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-regenerator@7.29.0(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-regexp-modifiers@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.29.0) + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-reserved-words@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-runtime@7.29.0(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-module-imports': 7.28.6 + '@babel/helper-plugin-utils': 7.28.6 + babel-plugin-polyfill-corejs2: 0.4.17(@babel/core@7.29.0) + babel-plugin-polyfill-corejs3: 0.13.0(@babel/core@7.29.0) + babel-plugin-polyfill-regenerator: 0.6.8(@babel/core@7.29.0) + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-shorthand-properties@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-spread@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-sticky-regex@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-template-literals@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-typeof-symbol@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-typescript@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-annotate-as-pure': 7.27.3 + '@babel/helper-create-class-features-plugin': 7.28.6(@babel/core@7.29.0) + '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 + '@babel/plugin-syntax-typescript': 7.28.6(@babel/core@7.29.0) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-unicode-escapes@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-unicode-property-regex@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.29.0) + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-unicode-regex@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.29.0) + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-unicode-sets-regex@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.29.0) + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/preset-env@7.29.2(@babel/core@7.29.0)': + dependencies: + '@babel/compat-data': 7.29.0 + '@babel/core': 7.29.0 + '@babel/helper-compilation-targets': 7.28.6 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-validator-option': 7.27.1 + '@babel/plugin-bugfix-firefox-class-in-computed-class-key': 7.28.5(@babel/core@7.29.0) + '@babel/plugin-bugfix-safari-class-field-initializer-scope': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-proposal-private-property-in-object': 7.21.0-placeholder-for-preset-env.2(@babel/core@7.29.0) + '@babel/plugin-syntax-import-assertions': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-syntax-import-attributes': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-syntax-unicode-sets-regex': 7.18.6(@babel/core@7.29.0) + '@babel/plugin-transform-arrow-functions': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-async-generator-functions': 7.29.0(@babel/core@7.29.0) + '@babel/plugin-transform-async-to-generator': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-block-scoped-functions': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-block-scoping': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-class-properties': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-class-static-block': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-classes': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-computed-properties': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-destructuring': 7.28.5(@babel/core@7.29.0) + '@babel/plugin-transform-dotall-regex': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-duplicate-keys': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-duplicate-named-capturing-groups-regex': 7.29.0(@babel/core@7.29.0) + '@babel/plugin-transform-dynamic-import': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-explicit-resource-management': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-exponentiation-operator': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-export-namespace-from': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-for-of': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-function-name': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-json-strings': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-literals': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-logical-assignment-operators': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-member-expression-literals': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-modules-amd': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-modules-commonjs': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-modules-systemjs': 7.29.0(@babel/core@7.29.0) + '@babel/plugin-transform-modules-umd': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-named-capturing-groups-regex': 7.29.0(@babel/core@7.29.0) + '@babel/plugin-transform-new-target': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-nullish-coalescing-operator': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-numeric-separator': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-object-rest-spread': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-object-super': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-optional-catch-binding': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-optional-chaining': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-parameters': 7.27.7(@babel/core@7.29.0) + '@babel/plugin-transform-private-methods': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-private-property-in-object': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-property-literals': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-regenerator': 7.29.0(@babel/core@7.29.0) + '@babel/plugin-transform-regexp-modifiers': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-reserved-words': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-shorthand-properties': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-spread': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-sticky-regex': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-template-literals': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-typeof-symbol': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-unicode-escapes': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-unicode-property-regex': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-unicode-regex': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-unicode-sets-regex': 7.28.6(@babel/core@7.29.0) + '@babel/preset-modules': 0.1.6-no-external-plugins(@babel/core@7.29.0) + babel-plugin-polyfill-corejs2: 0.4.17(@babel/core@7.29.0) + babel-plugin-polyfill-corejs3: 0.14.2(@babel/core@7.29.0) + babel-plugin-polyfill-regenerator: 0.6.8(@babel/core@7.29.0) + core-js-compat: 3.49.0 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/preset-modules@0.1.6-no-external-plugins(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/types': 7.29.0 + esutils: 2.0.3 + + '@babel/preset-react@7.28.5(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-validator-option': 7.27.1 + '@babel/plugin-transform-react-display-name': 7.28.0(@babel/core@7.29.0) + '@babel/plugin-transform-react-jsx': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-react-jsx-development': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-react-pure-annotations': 7.27.1(@babel/core@7.29.0) + transitivePeerDependencies: + - supports-color + + '@babel/preset-typescript@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-validator-option': 7.27.1 + '@babel/plugin-syntax-jsx': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-modules-commonjs': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-typescript': 7.28.6(@babel/core@7.29.0) + transitivePeerDependencies: + - supports-color + + '@babel/preset-typescript@7.28.5(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-validator-option': 7.27.1 + '@babel/plugin-syntax-jsx': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-modules-commonjs': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-typescript': 7.28.6(@babel/core@7.29.0) + transitivePeerDependencies: + - supports-color + + '@babel/runtime@7.29.2': {} + + '@babel/template@7.28.6': + dependencies: + '@babel/code-frame': 7.29.0 + '@babel/parser': 7.29.2 + '@babel/types': 7.29.0 + + '@babel/traverse@7.29.0': + dependencies: + '@babel/code-frame': 7.29.0 + '@babel/generator': 7.29.1 + '@babel/helper-globals': 7.28.0 + '@babel/parser': 7.29.2 + '@babel/template': 7.28.6 + '@babel/types': 7.29.0 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + + '@babel/types@7.29.0': + dependencies: + '@babel/helper-string-parser': 7.27.1 + '@babel/helper-validator-identifier': 7.28.5 + + '@bcoe/v8-coverage@0.2.3': {} + + '@egjs/hammerjs@2.0.17': + dependencies: + '@types/hammerjs': 2.0.46 + + '@esbuild/aix-ppc64@0.27.7': + optional: true + + '@esbuild/android-arm64@0.27.7': + optional: true + + '@esbuild/android-arm@0.27.7': + optional: true + + '@esbuild/android-x64@0.27.7': + optional: true + + '@esbuild/darwin-arm64@0.27.7': + optional: true + + '@esbuild/darwin-x64@0.27.7': + optional: true + + '@esbuild/freebsd-arm64@0.27.7': + optional: true + + '@esbuild/freebsd-x64@0.27.7': + optional: true + + '@esbuild/linux-arm64@0.27.7': + optional: true + + '@esbuild/linux-arm@0.27.7': + optional: true + + '@esbuild/linux-ia32@0.27.7': + optional: true + + '@esbuild/linux-loong64@0.27.7': + optional: true + + '@esbuild/linux-mips64el@0.27.7': + optional: true + + '@esbuild/linux-ppc64@0.27.7': + optional: true + + '@esbuild/linux-riscv64@0.27.7': + optional: true + + '@esbuild/linux-s390x@0.27.7': + optional: true + + '@esbuild/linux-x64@0.27.7': + optional: true + + '@esbuild/netbsd-arm64@0.27.7': + optional: true + + '@esbuild/netbsd-x64@0.27.7': + optional: true + + '@esbuild/openbsd-arm64@0.27.7': + optional: true + + '@esbuild/openbsd-x64@0.27.7': + optional: true + + '@esbuild/openharmony-arm64@0.27.7': + optional: true + + '@esbuild/sunos-x64@0.27.7': + optional: true + + '@esbuild/win32-arm64@0.27.7': + optional: true + + '@esbuild/win32-ia32@0.27.7': + optional: true + + '@esbuild/win32-x64@0.27.7': + optional: true + + '@eslint-community/eslint-utils@4.9.1(eslint@9.39.4)': + dependencies: + eslint: 9.39.4 + eslint-visitor-keys: 3.4.3 + + '@eslint-community/regexpp@4.12.2': {} + + '@eslint/config-array@0.21.2': + dependencies: + '@eslint/object-schema': 2.1.7 + debug: 4.4.3 + minimatch: 3.1.5 + transitivePeerDependencies: + - supports-color + + '@eslint/config-helpers@0.4.2': + dependencies: + '@eslint/core': 0.17.0 + + '@eslint/core@0.17.0': + dependencies: + '@types/json-schema': 7.0.15 + + '@eslint/eslintrc@3.3.5': + dependencies: + ajv: 6.15.0 + debug: 4.4.3 + espree: 10.4.0 + globals: 14.0.0 + ignore: 5.3.2 + import-fresh: 3.3.1 + js-yaml: 4.1.1 + minimatch: 3.1.5 + strip-json-comments: 3.1.1 + transitivePeerDependencies: + - supports-color + + '@eslint/js@9.39.4': {} + + '@eslint/object-schema@2.1.7': {} + + '@eslint/plugin-kit@0.4.1': + dependencies: + '@eslint/core': 0.17.0 + levn: 0.4.1 + + '@expo-google-fonts/material-symbols@0.4.34': {} + + '@expo/cli@55.0.26(@expo/dom-webview@55.0.5)(@expo/metro-runtime@55.0.10)(expo-constants@55.0.15)(expo-font@55.0.6)(expo-router@55.0.13)(expo@55.0.17)(react-dom@19.2.5(react@19.2.0))(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0)(typescript@5.9.3)': + dependencies: + '@expo/code-signing-certificates': 0.0.6 + '@expo/config': 55.0.15(typescript@5.9.3) + '@expo/config-plugins': 55.0.8 + '@expo/devcert': 1.2.1 + '@expo/env': 2.1.1 + '@expo/image-utils': 0.8.13(typescript@5.9.3) + '@expo/json-file': 10.0.13 + '@expo/log-box': 55.0.11(@expo/dom-webview@55.0.5)(expo@55.0.17)(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0) + '@expo/metro': 55.1.0 + '@expo/metro-config': 55.0.17(expo@55.0.17)(typescript@5.9.3) + '@expo/osascript': 2.4.2 + '@expo/package-manager': 1.10.4 + '@expo/plist': 0.5.2 + '@expo/prebuild-config': 55.0.16(expo@55.0.17)(typescript@5.9.3) + '@expo/require-utils': 55.0.4(typescript@5.9.3) + '@expo/router-server': 55.0.15(@expo/metro-runtime@55.0.10)(expo-constants@55.0.15)(expo-font@55.0.6)(expo-router@55.0.13)(expo-server@55.0.8)(expo@55.0.17)(react-dom@19.2.5(react@19.2.0))(react@19.2.0) + '@expo/schema-utils': 55.0.3 + '@expo/spawn-async': 1.7.2 + '@expo/ws-tunnel': 1.0.6 + '@expo/xcpretty': 4.4.3 + '@react-native/dev-middleware': 0.83.6 + accepts: 1.3.8 + arg: 5.0.2 + better-opn: 3.0.2 + bplist-creator: 0.1.0 + bplist-parser: 0.3.2 + chalk: 4.1.2 + ci-info: 3.9.0 + compression: 1.8.1 + connect: 3.7.0 + debug: 4.4.3 + dnssd-advertise: 1.1.4 + expo: 55.0.17(@babel/core@7.29.0)(@expo/dom-webview@55.0.5)(@expo/metro-runtime@55.0.10)(expo-router@55.0.13)(react-dom@19.2.5(react@19.2.0))(react-native-webview@13.16.1(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0))(react-native-worklets@0.7.4(@babel/core@7.29.0)(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0))(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0)(typescript@5.9.3) + expo-server: 55.0.8 + fetch-nodeshim: 0.4.10 + getenv: 2.0.0 + glob: 13.0.6 + lan-network: 0.2.1 + multitars: 1.0.0 + node-forge: 1.4.0 + npm-package-arg: 11.0.3 + ora: 3.4.0 + picomatch: 4.0.4 + pretty-format: 29.7.0 + progress: 2.0.3 + prompts: 2.4.2 + resolve-from: 5.0.0 + semver: 7.7.4 + send: 0.19.2 + slugify: 1.6.9 + source-map-support: 0.5.21 + stacktrace-parser: 0.1.11 + structured-headers: 0.4.1 + terminal-link: 2.1.1 + toqr: 0.1.1 + wrap-ansi: 7.0.0 + ws: 8.20.0 + zod: 3.25.76 + optionalDependencies: + expo-router: 55.0.13(121105c3b042d5e83ab3c1d3b84ed55f) + react-native: 0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0) + transitivePeerDependencies: + - '@expo/dom-webview' + - '@expo/metro-runtime' + - bufferutil + - expo-constants + - expo-font + - react + - react-dom + - react-server-dom-webpack + - supports-color + - typescript + - utf-8-validate + + '@expo/code-signing-certificates@0.0.6': + dependencies: + node-forge: 1.4.0 + + '@expo/config-plugins@55.0.8': + dependencies: + '@expo/config-types': 55.0.5 + '@expo/json-file': 10.0.13 + '@expo/plist': 0.5.2 + '@expo/sdk-runtime-versions': 1.0.0 + chalk: 4.1.2 + debug: 4.4.3 + getenv: 2.0.0 + glob: 13.0.6 + resolve-from: 5.0.0 + semver: 7.7.4 + slugify: 1.6.9 + xcode: 3.0.1 + xml2js: 0.6.0 + transitivePeerDependencies: + - supports-color + + '@expo/config-types@55.0.5': {} + + '@expo/config@55.0.15(typescript@5.9.3)': + dependencies: + '@expo/config-plugins': 55.0.8 + '@expo/config-types': 55.0.5 + '@expo/json-file': 10.0.13 + '@expo/require-utils': 55.0.4(typescript@5.9.3) + deepmerge: 4.3.1 + getenv: 2.0.0 + glob: 13.0.6 + resolve-workspace-root: 2.0.1 + semver: 7.7.4 + slugify: 1.6.9 + transitivePeerDependencies: + - supports-color + - typescript + + '@expo/devcert@1.2.1': + dependencies: + '@expo/sudo-prompt': 9.3.2 + debug: 3.2.7 + transitivePeerDependencies: + - supports-color + + '@expo/devtools@55.0.2(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0)': + dependencies: + chalk: 4.1.2 + optionalDependencies: + react: 19.2.0 + react-native: 0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0) + + '@expo/dom-webview@55.0.5(expo@55.0.17)(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0)': + dependencies: + expo: 55.0.17(@babel/core@7.29.0)(@expo/dom-webview@55.0.5)(@expo/metro-runtime@55.0.10)(expo-router@55.0.13)(react-dom@19.2.5(react@19.2.0))(react-native-webview@13.16.1(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0))(react-native-worklets@0.7.4(@babel/core@7.29.0)(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0))(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0)(typescript@5.9.3) + react: 19.2.0 + react-native: 0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0) + + '@expo/env@2.1.1': + dependencies: + chalk: 4.1.2 + debug: 4.4.3 + getenv: 2.0.0 + transitivePeerDependencies: + - supports-color + + '@expo/fingerprint@0.16.6': + dependencies: + '@expo/env': 2.1.1 + '@expo/spawn-async': 1.7.2 + arg: 5.0.2 + chalk: 4.1.2 + debug: 4.4.3 + getenv: 2.0.0 + glob: 13.0.6 + ignore: 5.3.2 + minimatch: 10.2.5 + resolve-from: 5.0.0 + semver: 7.7.4 + transitivePeerDependencies: + - supports-color + + '@expo/image-utils@0.8.13(typescript@5.9.3)': + dependencies: + '@expo/require-utils': 55.0.4(typescript@5.9.3) + '@expo/spawn-async': 1.7.2 + chalk: 4.1.2 + getenv: 2.0.0 + jimp-compact: 0.16.1 + parse-png: 2.1.0 + semver: 7.7.4 + transitivePeerDependencies: + - supports-color + - typescript + + '@expo/json-file@10.0.13': + dependencies: + '@babel/code-frame': 7.29.0 + json5: 2.2.3 + + '@expo/local-build-cache-provider@55.0.11(typescript@5.9.3)': + dependencies: + '@expo/config': 55.0.15(typescript@5.9.3) + chalk: 4.1.2 + transitivePeerDependencies: + - supports-color + - typescript + + '@expo/log-box@55.0.11(@expo/dom-webview@55.0.5)(expo@55.0.17)(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0)': + dependencies: + '@expo/dom-webview': 55.0.5(expo@55.0.17)(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0) + anser: 1.4.10 + expo: 55.0.17(@babel/core@7.29.0)(@expo/dom-webview@55.0.5)(@expo/metro-runtime@55.0.10)(expo-router@55.0.13)(react-dom@19.2.5(react@19.2.0))(react-native-webview@13.16.1(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0))(react-native-worklets@0.7.4(@babel/core@7.29.0)(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0))(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0)(typescript@5.9.3) + react: 19.2.0 + react-native: 0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0) + stacktrace-parser: 0.1.11 + + '@expo/metro-config@55.0.17(expo@55.0.17)(typescript@5.9.3)': + dependencies: + '@babel/code-frame': 7.29.0 + '@babel/core': 7.29.0 + '@babel/generator': 7.29.1 + '@expo/config': 55.0.15(typescript@5.9.3) + '@expo/env': 2.1.1 + '@expo/json-file': 10.0.13 + '@expo/metro': 55.1.0 + '@expo/spawn-async': 1.7.2 + browserslist: 4.28.2 + chalk: 4.1.2 + debug: 4.4.3 + getenv: 2.0.0 + glob: 13.0.6 + hermes-parser: 0.32.1 + jsc-safe-url: 0.2.4 + lightningcss: 1.32.0 + picomatch: 4.0.4 + postcss: 8.4.49 + resolve-from: 5.0.0 + optionalDependencies: + expo: 55.0.17(@babel/core@7.29.0)(@expo/dom-webview@55.0.5)(@expo/metro-runtime@55.0.10)(expo-router@55.0.13)(react-dom@19.2.5(react@19.2.0))(react-native-webview@13.16.1(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0))(react-native-worklets@0.7.4(@babel/core@7.29.0)(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0))(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0)(typescript@5.9.3) + transitivePeerDependencies: + - bufferutil + - supports-color + - typescript + - utf-8-validate + + '@expo/metro-runtime@55.0.10(@expo/dom-webview@55.0.5)(expo@55.0.17)(react-dom@19.2.5(react@19.2.0))(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0)': + dependencies: + '@expo/log-box': 55.0.11(@expo/dom-webview@55.0.5)(expo@55.0.17)(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0) + anser: 1.4.10 + expo: 55.0.17(@babel/core@7.29.0)(@expo/dom-webview@55.0.5)(@expo/metro-runtime@55.0.10)(expo-router@55.0.13)(react-dom@19.2.5(react@19.2.0))(react-native-webview@13.16.1(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0))(react-native-worklets@0.7.4(@babel/core@7.29.0)(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0))(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0)(typescript@5.9.3) + pretty-format: 29.7.0 + react: 19.2.0 + react-native: 0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0) + stacktrace-parser: 0.1.11 + whatwg-fetch: 3.6.20 + optionalDependencies: + react-dom: 19.2.5(react@19.2.0) + transitivePeerDependencies: + - '@expo/dom-webview' + + '@expo/metro@55.1.0': + dependencies: + metro: 0.83.6 + metro-babel-transformer: 0.83.6 + metro-cache: 0.83.6 + metro-cache-key: 0.83.6 + metro-config: 0.83.6 + metro-core: 0.83.6 + metro-file-map: 0.83.6 + metro-minify-terser: 0.83.6 + metro-resolver: 0.83.6 + metro-runtime: 0.83.6 + metro-source-map: 0.83.6 + metro-symbolicate: 0.83.6 + metro-transform-plugins: 0.83.6 + metro-transform-worker: 0.83.6 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + + '@expo/npm-proofread@1.0.1': + dependencies: + semver: 5.7.2 + + '@expo/osascript@2.4.2': + dependencies: + '@expo/spawn-async': 1.7.2 + + '@expo/package-manager@1.10.4': + dependencies: + '@expo/json-file': 10.0.13 + '@expo/spawn-async': 1.7.2 + chalk: 4.1.2 + npm-package-arg: 11.0.3 + ora: 3.4.0 + resolve-workspace-root: 2.0.1 + + '@expo/plist@0.5.2': + dependencies: + '@xmldom/xmldom': 0.8.13 + base64-js: 1.5.1 + xmlbuilder: 15.1.1 + + '@expo/prebuild-config@55.0.16(expo@55.0.17)(typescript@5.9.3)': + dependencies: + '@expo/config': 55.0.15(typescript@5.9.3) + '@expo/config-plugins': 55.0.8 + '@expo/config-types': 55.0.5 + '@expo/image-utils': 0.8.13(typescript@5.9.3) + '@expo/json-file': 10.0.13 + '@react-native/normalize-colors': 0.83.6 + debug: 4.4.3 + expo: 55.0.17(@babel/core@7.29.0)(@expo/dom-webview@55.0.5)(@expo/metro-runtime@55.0.10)(expo-router@55.0.13)(react-dom@19.2.5(react@19.2.0))(react-native-webview@13.16.1(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0))(react-native-worklets@0.7.4(@babel/core@7.29.0)(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0))(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0)(typescript@5.9.3) + resolve-from: 5.0.0 + semver: 7.7.4 + xml2js: 0.6.0 + transitivePeerDependencies: + - supports-color + - typescript + + '@expo/require-utils@55.0.4(typescript@5.9.3)': + dependencies: + '@babel/code-frame': 7.29.0 + '@babel/core': 7.29.0 + '@babel/plugin-transform-modules-commonjs': 7.28.6(@babel/core@7.29.0) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@expo/router-server@55.0.15(@expo/metro-runtime@55.0.10)(expo-constants@55.0.15)(expo-font@55.0.6)(expo-router@55.0.13)(expo-server@55.0.8)(expo@55.0.17)(react-dom@19.2.5(react@19.2.0))(react@19.2.0)': + dependencies: + debug: 4.4.3 + expo: 55.0.17(@babel/core@7.29.0)(@expo/dom-webview@55.0.5)(@expo/metro-runtime@55.0.10)(expo-router@55.0.13)(react-dom@19.2.5(react@19.2.0))(react-native-webview@13.16.1(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0))(react-native-worklets@0.7.4(@babel/core@7.29.0)(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0))(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0)(typescript@5.9.3) + expo-constants: 55.0.15(expo@55.0.17)(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0)) + expo-font: 55.0.6(expo@55.0.17)(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0) + expo-server: 55.0.8 + react: 19.2.0 + optionalDependencies: + '@expo/metro-runtime': 55.0.10(@expo/dom-webview@55.0.5)(expo@55.0.17)(react-dom@19.2.5(react@19.2.0))(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0) + expo-router: 55.0.13(121105c3b042d5e83ab3c1d3b84ed55f) + react-dom: 19.2.5(react@19.2.0) + transitivePeerDependencies: + - supports-color + + '@expo/schema-utils@55.0.3': {} + + '@expo/sdk-runtime-versions@1.0.0': {} + + '@expo/spawn-async@1.7.2': + dependencies: + cross-spawn: 7.0.6 + + '@expo/sudo-prompt@9.3.2': {} + + '@expo/vector-icons@15.1.1(expo-font@55.0.6)(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0)': + dependencies: + expo-font: 55.0.6(expo@55.0.17)(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0) + react: 19.2.0 + react-native: 0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0) + + '@expo/ws-tunnel@1.0.6': {} + + '@expo/xcpretty@4.4.3': + dependencies: + '@babel/code-frame': 7.29.0 + chalk: 4.1.2 + js-yaml: 4.1.1 + + '@humanfs/core@0.19.2': + dependencies: + '@humanfs/types': 0.15.0 + + '@humanfs/node@0.16.8': + dependencies: + '@humanfs/core': 0.19.2 + '@humanfs/types': 0.15.0 + '@humanwhocodes/retry': 0.4.3 + + '@humanfs/types@0.15.0': {} + + '@humanwhocodes/module-importer@1.0.1': {} + + '@humanwhocodes/retry@0.4.3': {} + + '@isaacs/ttlcache@1.4.1': {} + + '@istanbuljs/load-nyc-config@1.1.0': + dependencies: + camelcase: 5.3.1 + find-up: 4.1.0 + get-package-type: 0.1.0 + js-yaml: 3.14.2 + resolve-from: 5.0.0 + + '@istanbuljs/schema@0.1.6': {} + + '@jest/console@29.7.0': + dependencies: + '@jest/types': 29.6.3 + '@types/node': 25.6.0 + chalk: 4.1.2 + jest-message-util: 29.7.0 + jest-util: 29.7.0 + slash: 3.0.0 + + '@jest/core@29.7.0': + dependencies: + '@jest/console': 29.7.0 + '@jest/reporters': 29.7.0 + '@jest/test-result': 29.7.0 + '@jest/transform': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 25.6.0 + ansi-escapes: 4.3.2 + chalk: 4.1.2 + ci-info: 3.9.0 + exit: 0.1.2 + graceful-fs: 4.2.11 + jest-changed-files: 29.7.0 + jest-config: 29.7.0(@types/node@25.6.0) + jest-haste-map: 29.7.0 + jest-message-util: 29.7.0 + jest-regex-util: 29.6.3 + jest-resolve: 29.7.0 + jest-resolve-dependencies: 29.7.0 + jest-runner: 29.7.0 + jest-runtime: 29.7.0 + jest-snapshot: 29.7.0 + jest-util: 29.7.0 + jest-validate: 29.7.0 + jest-watcher: 29.7.0 + micromatch: 4.0.8 + pretty-format: 29.7.0 + slash: 3.0.0 + strip-ansi: 6.0.1 + transitivePeerDependencies: + - babel-plugin-macros + - supports-color + - ts-node + + '@jest/create-cache-key-function@29.7.0': + dependencies: + '@jest/types': 29.6.3 + + '@jest/diff-sequences@30.3.0': {} + + '@jest/environment@29.7.0': + dependencies: + '@jest/fake-timers': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 25.6.0 + jest-mock: 29.7.0 + + '@jest/expect-utils@29.7.0': + dependencies: + jest-get-type: 29.6.3 + + '@jest/expect@29.7.0': + dependencies: + expect: 29.7.0 + jest-snapshot: 29.7.0 + transitivePeerDependencies: + - supports-color + + '@jest/fake-timers@29.7.0': + dependencies: + '@jest/types': 29.6.3 + '@sinonjs/fake-timers': 10.3.0 + '@types/node': 25.6.0 + jest-message-util: 29.7.0 + jest-mock: 29.7.0 + jest-util: 29.7.0 + + '@jest/get-type@30.1.0': {} + + '@jest/globals@29.7.0': + dependencies: + '@jest/environment': 29.7.0 + '@jest/expect': 29.7.0 + '@jest/types': 29.6.3 + jest-mock: 29.7.0 + transitivePeerDependencies: + - supports-color + + '@jest/reporters@29.7.0': + dependencies: + '@bcoe/v8-coverage': 0.2.3 + '@jest/console': 29.7.0 + '@jest/test-result': 29.7.0 + '@jest/transform': 29.7.0 + '@jest/types': 29.6.3 + '@jridgewell/trace-mapping': 0.3.31 + '@types/node': 25.6.0 + chalk: 4.1.2 + collect-v8-coverage: 1.0.3 + exit: 0.1.2 + glob: 7.2.3 + graceful-fs: 4.2.11 + istanbul-lib-coverage: 3.2.2 + istanbul-lib-instrument: 6.0.3 + istanbul-lib-report: 3.0.1 + istanbul-lib-source-maps: 4.0.1 + istanbul-reports: 3.2.0 + jest-message-util: 29.7.0 + jest-util: 29.7.0 + jest-worker: 29.7.0 + slash: 3.0.0 + string-length: 4.0.2 + strip-ansi: 6.0.1 + v8-to-istanbul: 9.3.0 + transitivePeerDependencies: + - supports-color + + '@jest/schemas@29.6.3': + dependencies: + '@sinclair/typebox': 0.27.10 + + '@jest/schemas@30.0.5': + dependencies: + '@sinclair/typebox': 0.34.49 + + '@jest/source-map@29.6.3': + dependencies: + '@jridgewell/trace-mapping': 0.3.31 + callsites: 3.1.0 + graceful-fs: 4.2.11 + + '@jest/test-result@29.7.0': + dependencies: + '@jest/console': 29.7.0 + '@jest/types': 29.6.3 + '@types/istanbul-lib-coverage': 2.0.6 + collect-v8-coverage: 1.0.3 + + '@jest/test-sequencer@29.7.0': + dependencies: + '@jest/test-result': 29.7.0 + graceful-fs: 4.2.11 + jest-haste-map: 29.7.0 + slash: 3.0.0 + + '@jest/transform@29.7.0': + dependencies: + '@babel/core': 7.29.0 + '@jest/types': 29.6.3 + '@jridgewell/trace-mapping': 0.3.31 + babel-plugin-istanbul: 6.1.1 + chalk: 4.1.2 + convert-source-map: 2.0.0 + fast-json-stable-stringify: 2.1.0 + graceful-fs: 4.2.11 + jest-haste-map: 29.7.0 + jest-regex-util: 29.6.3 + jest-util: 29.7.0 + micromatch: 4.0.8 + pirates: 4.0.7 + slash: 3.0.0 + write-file-atomic: 4.0.2 + transitivePeerDependencies: + - supports-color + + '@jest/types@29.6.3': + dependencies: + '@jest/schemas': 29.6.3 + '@types/istanbul-lib-coverage': 2.0.6 + '@types/istanbul-reports': 3.0.4 + '@types/node': 25.6.0 + '@types/yargs': 17.0.35 + chalk: 4.1.2 + + '@jridgewell/gen-mapping@0.3.13': + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/remapping@2.3.5': + dependencies: + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/resolve-uri@3.1.2': {} + + '@jridgewell/source-map@0.3.11': + dependencies: + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/sourcemap-codec@1.5.5': {} + + '@jridgewell/trace-mapping@0.3.31': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.5 + + '@nicolo-ribaudo/chokidar-2@2.1.8-no-fsevents.3': + optional: true + + '@oxfmt/binding-android-arm-eabi@0.47.0': + optional: true + + '@oxfmt/binding-android-arm64@0.47.0': + optional: true + + '@oxfmt/binding-darwin-arm64@0.47.0': + optional: true + + '@oxfmt/binding-darwin-x64@0.47.0': + optional: true + + '@oxfmt/binding-freebsd-x64@0.47.0': + optional: true + + '@oxfmt/binding-linux-arm-gnueabihf@0.47.0': + optional: true + + '@oxfmt/binding-linux-arm-musleabihf@0.47.0': + optional: true + + '@oxfmt/binding-linux-arm64-gnu@0.47.0': + optional: true + + '@oxfmt/binding-linux-arm64-musl@0.47.0': + optional: true + + '@oxfmt/binding-linux-ppc64-gnu@0.47.0': + optional: true + + '@oxfmt/binding-linux-riscv64-gnu@0.47.0': + optional: true + + '@oxfmt/binding-linux-riscv64-musl@0.47.0': + optional: true + + '@oxfmt/binding-linux-s390x-gnu@0.47.0': + optional: true + + '@oxfmt/binding-linux-x64-gnu@0.47.0': + optional: true + + '@oxfmt/binding-linux-x64-musl@0.47.0': + optional: true + + '@oxfmt/binding-openharmony-arm64@0.47.0': + optional: true + + '@oxfmt/binding-win32-arm64-msvc@0.47.0': + optional: true + + '@oxfmt/binding-win32-ia32-msvc@0.47.0': + optional: true + + '@oxfmt/binding-win32-x64-msvc@0.47.0': + optional: true + + '@oxlint/binding-android-arm-eabi@1.62.0': + optional: true + + '@oxlint/binding-android-arm64@1.62.0': + optional: true + + '@oxlint/binding-darwin-arm64@1.62.0': + optional: true + + '@oxlint/binding-darwin-x64@1.62.0': + optional: true + + '@oxlint/binding-freebsd-x64@1.62.0': + optional: true + + '@oxlint/binding-linux-arm-gnueabihf@1.62.0': + optional: true + + '@oxlint/binding-linux-arm-musleabihf@1.62.0': + optional: true + + '@oxlint/binding-linux-arm64-gnu@1.62.0': + optional: true + + '@oxlint/binding-linux-arm64-musl@1.62.0': + optional: true + + '@oxlint/binding-linux-ppc64-gnu@1.62.0': + optional: true + + '@oxlint/binding-linux-riscv64-gnu@1.62.0': + optional: true + + '@oxlint/binding-linux-riscv64-musl@1.62.0': + optional: true + + '@oxlint/binding-linux-s390x-gnu@1.62.0': + optional: true + + '@oxlint/binding-linux-x64-gnu@1.62.0': + optional: true + + '@oxlint/binding-linux-x64-musl@1.62.0': + optional: true + + '@oxlint/binding-openharmony-arm64@1.62.0': + optional: true + + '@oxlint/binding-win32-arm64-msvc@1.62.0': + optional: true + + '@oxlint/binding-win32-ia32-msvc@1.62.0': + optional: true + + '@oxlint/binding-win32-x64-msvc@1.62.0': + optional: true + + '@pkgr/core@0.2.9': {} + + '@radix-ui/primitive@1.1.3': {} + + '@radix-ui/react-collection@1.1.7(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.0))(react@19.2.0)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.14)(react@19.2.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.14)(react@19.2.0) + '@radix-ui/react-primitive': 2.1.3(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.0))(react@19.2.0) + '@radix-ui/react-slot': 1.2.3(@types/react@19.2.14)(react@19.2.0) + react: 19.2.0 + react-dom: 19.2.5(react@19.2.0) + optionalDependencies: + '@types/react': 19.2.14 + + '@radix-ui/react-compose-refs@1.1.2(@types/react@19.2.14)(react@19.2.0)': + dependencies: + react: 19.2.0 + optionalDependencies: + '@types/react': 19.2.14 + + '@radix-ui/react-context@1.1.2(@types/react@19.2.14)(react@19.2.0)': + dependencies: + react: 19.2.0 + optionalDependencies: + '@types/react': 19.2.14 + + '@radix-ui/react-dialog@1.1.15(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.0))(react@19.2.0)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.14)(react@19.2.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.14)(react@19.2.0) + '@radix-ui/react-dismissable-layer': 1.1.11(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.0))(react@19.2.0) + '@radix-ui/react-focus-guards': 1.1.3(@types/react@19.2.14)(react@19.2.0) + '@radix-ui/react-focus-scope': 1.1.7(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.0))(react@19.2.0) + '@radix-ui/react-id': 1.1.1(@types/react@19.2.14)(react@19.2.0) + '@radix-ui/react-portal': 1.1.9(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.0))(react@19.2.0) + '@radix-ui/react-presence': 1.1.5(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.0))(react@19.2.0) + '@radix-ui/react-primitive': 2.1.3(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.0))(react@19.2.0) + '@radix-ui/react-slot': 1.2.3(@types/react@19.2.14)(react@19.2.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.14)(react@19.2.0) + aria-hidden: 1.2.6 + react: 19.2.0 + react-dom: 19.2.5(react@19.2.0) + react-remove-scroll: 2.7.2(@types/react@19.2.14)(react@19.2.0) + optionalDependencies: + '@types/react': 19.2.14 + + '@radix-ui/react-direction@1.1.1(@types/react@19.2.14)(react@19.2.0)': + dependencies: + react: 19.2.0 + optionalDependencies: + '@types/react': 19.2.14 + + '@radix-ui/react-dismissable-layer@1.1.11(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.0))(react@19.2.0)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.14)(react@19.2.0) + '@radix-ui/react-primitive': 2.1.3(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.0))(react@19.2.0) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.14)(react@19.2.0) + '@radix-ui/react-use-escape-keydown': 1.1.1(@types/react@19.2.14)(react@19.2.0) + react: 19.2.0 + react-dom: 19.2.5(react@19.2.0) + optionalDependencies: + '@types/react': 19.2.14 + + '@radix-ui/react-focus-guards@1.1.3(@types/react@19.2.14)(react@19.2.0)': + dependencies: + react: 19.2.0 + optionalDependencies: + '@types/react': 19.2.14 + + '@radix-ui/react-focus-scope@1.1.7(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.0))(react@19.2.0)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.14)(react@19.2.0) + '@radix-ui/react-primitive': 2.1.3(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.0))(react@19.2.0) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.14)(react@19.2.0) + react: 19.2.0 + react-dom: 19.2.5(react@19.2.0) + optionalDependencies: + '@types/react': 19.2.14 + + '@radix-ui/react-id@1.1.1(@types/react@19.2.14)(react@19.2.0)': + dependencies: + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.14)(react@19.2.0) + react: 19.2.0 + optionalDependencies: + '@types/react': 19.2.14 + + '@radix-ui/react-portal@1.1.9(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.0))(react@19.2.0)': + dependencies: + '@radix-ui/react-primitive': 2.1.3(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.0))(react@19.2.0) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.14)(react@19.2.0) + react: 19.2.0 + react-dom: 19.2.5(react@19.2.0) + optionalDependencies: + '@types/react': 19.2.14 + + '@radix-ui/react-presence@1.1.5(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.0))(react@19.2.0)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.14)(react@19.2.0) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.14)(react@19.2.0) + react: 19.2.0 + react-dom: 19.2.5(react@19.2.0) + optionalDependencies: + '@types/react': 19.2.14 + + '@radix-ui/react-primitive@2.1.3(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.0))(react@19.2.0)': + dependencies: + '@radix-ui/react-slot': 1.2.3(@types/react@19.2.14)(react@19.2.0) + react: 19.2.0 + react-dom: 19.2.5(react@19.2.0) + optionalDependencies: + '@types/react': 19.2.14 + + '@radix-ui/react-roving-focus@1.1.11(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.0))(react@19.2.0)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-collection': 1.1.7(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.0))(react@19.2.0) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.14)(react@19.2.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.14)(react@19.2.0) + '@radix-ui/react-direction': 1.1.1(@types/react@19.2.14)(react@19.2.0) + '@radix-ui/react-id': 1.1.1(@types/react@19.2.14)(react@19.2.0) + '@radix-ui/react-primitive': 2.1.3(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.0))(react@19.2.0) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.14)(react@19.2.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.14)(react@19.2.0) + react: 19.2.0 + react-dom: 19.2.5(react@19.2.0) + optionalDependencies: + '@types/react': 19.2.14 + + '@radix-ui/react-slot@1.2.3(@types/react@19.2.14)(react@19.2.0)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.14)(react@19.2.0) + react: 19.2.0 + optionalDependencies: + '@types/react': 19.2.14 + + '@radix-ui/react-slot@1.2.4(@types/react@19.2.14)(react@19.2.0)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.14)(react@19.2.0) + react: 19.2.0 + optionalDependencies: + '@types/react': 19.2.14 + + '@radix-ui/react-tabs@1.1.13(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.0))(react@19.2.0)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-context': 1.1.2(@types/react@19.2.14)(react@19.2.0) + '@radix-ui/react-direction': 1.1.1(@types/react@19.2.14)(react@19.2.0) + '@radix-ui/react-id': 1.1.1(@types/react@19.2.14)(react@19.2.0) + '@radix-ui/react-presence': 1.1.5(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.0))(react@19.2.0) + '@radix-ui/react-primitive': 2.1.3(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.0))(react@19.2.0) + '@radix-ui/react-roving-focus': 1.1.11(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.0))(react@19.2.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.14)(react@19.2.0) + react: 19.2.0 + react-dom: 19.2.5(react@19.2.0) + optionalDependencies: + '@types/react': 19.2.14 + + '@radix-ui/react-use-callback-ref@1.1.1(@types/react@19.2.14)(react@19.2.0)': + dependencies: + react: 19.2.0 + optionalDependencies: + '@types/react': 19.2.14 + + '@radix-ui/react-use-controllable-state@1.2.2(@types/react@19.2.14)(react@19.2.0)': + dependencies: + '@radix-ui/react-use-effect-event': 0.0.2(@types/react@19.2.14)(react@19.2.0) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.14)(react@19.2.0) + react: 19.2.0 + optionalDependencies: + '@types/react': 19.2.14 + + '@radix-ui/react-use-effect-event@0.0.2(@types/react@19.2.14)(react@19.2.0)': + dependencies: + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.14)(react@19.2.0) + react: 19.2.0 + optionalDependencies: + '@types/react': 19.2.14 + + '@radix-ui/react-use-escape-keydown@1.1.1(@types/react@19.2.14)(react@19.2.0)': + dependencies: + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.14)(react@19.2.0) + react: 19.2.0 + optionalDependencies: + '@types/react': 19.2.14 + + '@radix-ui/react-use-layout-effect@1.1.1(@types/react@19.2.14)(react@19.2.0)': + dependencies: + react: 19.2.0 + optionalDependencies: + '@types/react': 19.2.14 + + '@react-native-async-storage/async-storage@2.2.0(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))': + dependencies: + merge-options: 3.0.4 + react-native: 0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0) + + '@react-native/assets-registry@0.83.6': {} + + '@react-native/assets-registry@0.85.2': {} + + '@react-native/babel-plugin-codegen@0.83.6(@babel/core@7.29.0)': + dependencies: + '@babel/traverse': 7.29.0 + '@react-native/codegen': 0.83.6(@babel/core@7.29.0) + transitivePeerDependencies: + - '@babel/core' + - supports-color + + '@react-native/babel-plugin-codegen@0.85.2(@babel/core@7.29.0)': + dependencies: + '@babel/traverse': 7.29.0 + '@react-native/codegen': 0.85.2(@babel/core@7.29.0) + transitivePeerDependencies: + - '@babel/core' + - supports-color + optional: true + + '@react-native/babel-preset@0.83.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/plugin-proposal-export-default-from': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.29.0) + '@babel/plugin-syntax-export-default-from': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.29.0) + '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.29.0) + '@babel/plugin-transform-arrow-functions': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-async-generator-functions': 7.29.0(@babel/core@7.29.0) + '@babel/plugin-transform-async-to-generator': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-block-scoping': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-class-properties': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-classes': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-computed-properties': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-destructuring': 7.28.5(@babel/core@7.29.0) + '@babel/plugin-transform-flow-strip-types': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-for-of': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-function-name': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-literals': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-logical-assignment-operators': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-modules-commonjs': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-named-capturing-groups-regex': 7.29.0(@babel/core@7.29.0) + '@babel/plugin-transform-nullish-coalescing-operator': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-numeric-separator': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-object-rest-spread': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-optional-catch-binding': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-optional-chaining': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-parameters': 7.27.7(@babel/core@7.29.0) + '@babel/plugin-transform-private-methods': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-private-property-in-object': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-react-display-name': 7.28.0(@babel/core@7.29.0) + '@babel/plugin-transform-react-jsx': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-react-jsx-source': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-regenerator': 7.29.0(@babel/core@7.29.0) + '@babel/plugin-transform-runtime': 7.29.0(@babel/core@7.29.0) + '@babel/plugin-transform-shorthand-properties': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-spread': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-sticky-regex': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-typescript': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-unicode-regex': 7.27.1(@babel/core@7.29.0) + '@babel/template': 7.28.6 + '@react-native/babel-plugin-codegen': 0.83.6(@babel/core@7.29.0) + babel-plugin-syntax-hermes-parser: 0.32.0 + babel-plugin-transform-flow-enums: 0.0.2(@babel/core@7.29.0) + react-refresh: 0.14.2 + transitivePeerDependencies: + - supports-color + + '@react-native/babel-preset@0.85.2(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/plugin-proposal-export-default-from': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.29.0) + '@babel/plugin-syntax-export-default-from': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.29.0) + '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.29.0) + '@babel/plugin-transform-async-generator-functions': 7.29.0(@babel/core@7.29.0) + '@babel/plugin-transform-async-to-generator': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-block-scoping': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-class-properties': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-classes': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-destructuring': 7.28.5(@babel/core@7.29.0) + '@babel/plugin-transform-flow-strip-types': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-for-of': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-modules-commonjs': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-named-capturing-groups-regex': 7.29.0(@babel/core@7.29.0) + '@babel/plugin-transform-nullish-coalescing-operator': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-optional-catch-binding': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-optional-chaining': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-private-methods': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-private-property-in-object': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-react-display-name': 7.28.0(@babel/core@7.29.0) + '@babel/plugin-transform-react-jsx': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-react-jsx-source': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-regenerator': 7.29.0(@babel/core@7.29.0) + '@babel/plugin-transform-runtime': 7.29.0(@babel/core@7.29.0) + '@babel/plugin-transform-typescript': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-unicode-regex': 7.27.1(@babel/core@7.29.0) + '@react-native/babel-plugin-codegen': 0.85.2(@babel/core@7.29.0) + babel-plugin-syntax-hermes-parser: 0.33.3 + babel-plugin-transform-flow-enums: 0.0.2(@babel/core@7.29.0) + react-refresh: 0.14.2 + transitivePeerDependencies: + - supports-color + optional: true + + '@react-native/codegen@0.83.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/parser': 7.29.2 + glob: 7.2.3 + hermes-parser: 0.32.0 + invariant: 2.2.4 + nullthrows: 1.1.1 + yargs: 17.7.2 + + '@react-native/codegen@0.85.2(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/parser': 7.29.2 + hermes-parser: 0.33.3 + invariant: 2.2.4 + nullthrows: 1.1.1 + tinyglobby: 0.2.16 + yargs: 17.7.2 + + '@react-native/community-cli-plugin@0.83.6(@react-native/metro-config@0.85.2(@babel/core@7.29.0))': + dependencies: + '@react-native/dev-middleware': 0.83.6 + debug: 4.4.3 + invariant: 2.2.4 + metro: 0.83.6 + metro-config: 0.83.6 + metro-core: 0.83.6 + semver: 7.7.4 + optionalDependencies: + '@react-native/metro-config': 0.85.2(@babel/core@7.29.0) + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + + '@react-native/community-cli-plugin@0.85.2(@react-native/metro-config@0.85.2(@babel/core@7.29.0))': + dependencies: + '@react-native/dev-middleware': 0.85.2 + debug: 4.4.3 + invariant: 2.2.4 + metro: 0.84.3 + metro-config: 0.84.3 + metro-core: 0.84.3 + semver: 7.7.4 + optionalDependencies: + '@react-native/metro-config': 0.85.2(@babel/core@7.29.0) + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + + '@react-native/debugger-frontend@0.83.6': {} + + '@react-native/debugger-frontend@0.85.2': {} + + '@react-native/debugger-shell@0.83.6': + dependencies: + cross-spawn: 7.0.6 + fb-dotslash: 0.5.8 + + '@react-native/debugger-shell@0.85.2': + dependencies: + cross-spawn: 7.0.6 + debug: 4.4.3 + fb-dotslash: 0.5.8 + transitivePeerDependencies: + - supports-color + + '@react-native/dev-middleware@0.83.6': + dependencies: + '@isaacs/ttlcache': 1.4.1 + '@react-native/debugger-frontend': 0.83.6 + '@react-native/debugger-shell': 0.83.6 + chrome-launcher: 0.15.2 + chromium-edge-launcher: 0.2.0 + connect: 3.7.0 + debug: 4.4.3 + invariant: 2.2.4 + nullthrows: 1.1.1 + open: 7.4.2 + serve-static: 1.16.3 + ws: 7.5.10 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + + '@react-native/dev-middleware@0.85.2': + dependencies: + '@isaacs/ttlcache': 1.4.1 + '@react-native/debugger-frontend': 0.85.2 + '@react-native/debugger-shell': 0.85.2 + chrome-launcher: 0.15.2 + chromium-edge-launcher: 0.3.0 + connect: 3.7.0 + debug: 4.4.3 + invariant: 2.2.4 + nullthrows: 1.1.1 + open: 7.4.2 + serve-static: 1.16.3 + ws: 7.5.10 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + + '@react-native/gradle-plugin@0.83.6': {} + + '@react-native/gradle-plugin@0.85.2': {} + + '@react-native/js-polyfills@0.83.6': {} + + '@react-native/js-polyfills@0.85.2': {} + + '@react-native/metro-babel-transformer@0.85.2(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@react-native/babel-preset': 0.85.2(@babel/core@7.29.0) + hermes-parser: 0.33.3 + nullthrows: 1.1.1 + transitivePeerDependencies: + - supports-color + optional: true + + '@react-native/metro-config@0.85.2(@babel/core@7.29.0)': + dependencies: + '@react-native/js-polyfills': 0.85.2 + '@react-native/metro-babel-transformer': 0.85.2(@babel/core@7.29.0) + metro-config: 0.84.3 + metro-runtime: 0.84.3 + transitivePeerDependencies: + - '@babel/core' + - bufferutil + - supports-color + - utf-8-validate + optional: true + + '@react-native/normalize-colors@0.74.89': {} + + '@react-native/normalize-colors@0.83.6': {} + + '@react-native/normalize-colors@0.85.2': {} + + '@react-native/virtualized-lists@0.83.6(@types/react@19.2.14)(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0)': + dependencies: + invariant: 2.2.4 + nullthrows: 1.1.1 + react: 19.2.0 + react-native: 0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0) + optionalDependencies: + '@types/react': 19.2.14 + + '@react-native/virtualized-lists@0.85.2(@types/react@19.2.14)(react-native@0.85.2(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0)': + dependencies: + invariant: 2.2.4 + nullthrows: 1.1.1 + react: 19.2.0 + react-native: 0.85.2(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0) + optionalDependencies: + '@types/react': 19.2.14 + + '@react-navigation/bottom-tabs@7.15.10(@react-navigation/native@7.2.2(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0))(react-native-safe-area-context@5.6.2(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0))(react-native-screens@4.23.0(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0))(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0)': + dependencies: + '@react-navigation/elements': 2.9.15(@react-navigation/native@7.2.2(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0))(react-native-safe-area-context@5.6.2(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0))(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0) + '@react-navigation/native': 7.2.2(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0) + color: 4.2.3 + react: 19.2.0 + react-native: 0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0) + react-native-safe-area-context: 5.6.2(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0) + react-native-screens: 4.23.0(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0) + sf-symbols-typescript: 2.2.0 + transitivePeerDependencies: + - '@react-native-masked-view/masked-view' + + '@react-navigation/core@7.17.2(react@19.2.0)': + dependencies: + '@react-navigation/routers': 7.5.3 + escape-string-regexp: 4.0.0 + fast-deep-equal: 3.1.3 + nanoid: 3.3.11 + query-string: 7.1.3 + react: 19.2.0 + react-is: 19.2.5 + use-latest-callback: 0.2.6(react@19.2.0) + use-sync-external-store: 1.6.0(react@19.2.0) + + '@react-navigation/elements@2.9.15(@react-navigation/native@7.2.2(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0))(react-native-safe-area-context@5.6.2(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0))(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0)': + dependencies: + '@react-navigation/native': 7.2.2(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0) + color: 4.2.3 + react: 19.2.0 + react-native: 0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0) + react-native-safe-area-context: 5.6.2(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0) + use-latest-callback: 0.2.6(react@19.2.0) + use-sync-external-store: 1.6.0(react@19.2.0) + + '@react-navigation/native-stack@7.14.12(@react-navigation/native@7.2.2(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0))(react-native-safe-area-context@5.6.2(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0))(react-native-screens@4.23.0(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0))(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0)': + dependencies: + '@react-navigation/elements': 2.9.15(@react-navigation/native@7.2.2(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0))(react-native-safe-area-context@5.6.2(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0))(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0) + '@react-navigation/native': 7.2.2(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0) + color: 4.2.3 + react: 19.2.0 + react-native: 0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0) + react-native-safe-area-context: 5.6.2(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0) + react-native-screens: 4.23.0(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0) + sf-symbols-typescript: 2.2.0 + warn-once: 0.1.1 + transitivePeerDependencies: + - '@react-native-masked-view/masked-view' + + '@react-navigation/native@7.2.2(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0)': + dependencies: + '@react-navigation/core': 7.17.2(react@19.2.0) + escape-string-regexp: 4.0.0 + fast-deep-equal: 3.1.3 + nanoid: 3.3.11 + react: 19.2.0 + react-native: 0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0) + use-latest-callback: 0.2.6(react@19.2.0) + + '@react-navigation/routers@7.5.3': + dependencies: + nanoid: 3.3.11 + + '@rtsao/scc@1.1.0': {} + + '@sinclair/typebox@0.27.10': {} + + '@sinclair/typebox@0.34.49': {} + + '@sinonjs/commons@3.0.1': + dependencies: + type-detect: 4.0.8 + + '@sinonjs/fake-timers@10.3.0': + dependencies: + '@sinonjs/commons': 3.0.1 + + '@testing-library/react-native@13.3.3(jest@29.7.0(@types/node@25.6.0))(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react-test-renderer@19.2.0(react@19.2.0))(react@19.2.0)': + dependencies: + jest-matcher-utils: 30.3.0 + picocolors: 1.1.1 + pretty-format: 30.3.0 + react: 19.2.0 + react-native: 0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0) + react-test-renderer: 19.2.0(react@19.2.0) + redent: 3.0.0 + optionalDependencies: + jest: 29.7.0(@types/node@25.6.0) + + '@tootallnate/once@2.0.0': {} + + '@tsconfig/node18@18.2.6': {} + + '@types/babel__core@7.20.5': + dependencies: + '@babel/parser': 7.29.2 + '@babel/types': 7.29.0 + '@types/babel__generator': 7.27.0 + '@types/babel__template': 7.4.4 + '@types/babel__traverse': 7.28.0 + + '@types/babel__generator@7.27.0': + dependencies: + '@babel/types': 7.29.0 + + '@types/babel__template@7.4.4': + dependencies: + '@babel/parser': 7.29.2 + '@babel/types': 7.29.0 + + '@types/babel__traverse@7.28.0': + dependencies: + '@babel/types': 7.29.0 + + '@types/emscripten@1.41.5': {} + + '@types/estree@1.0.8': {} + + '@types/graceful-fs@4.1.9': + dependencies: + '@types/node': 25.6.0 + + '@types/hammerjs@2.0.46': {} + + '@types/istanbul-lib-coverage@2.0.6': {} + + '@types/istanbul-lib-report@3.0.3': + dependencies: + '@types/istanbul-lib-coverage': 2.0.6 + + '@types/istanbul-reports@3.0.4': + dependencies: + '@types/istanbul-lib-report': 3.0.3 + + '@types/jest@29.5.14': + dependencies: + expect: 29.7.0 + pretty-format: 29.7.0 + + '@types/jsdom@20.0.1': + dependencies: + '@types/node': 25.6.0 + '@types/tough-cookie': 4.0.5 + parse5: 7.3.0 + + '@types/json-schema@7.0.15': {} + + '@types/json5@0.0.29': {} + + '@types/node@25.6.0': + dependencies: + undici-types: 7.19.2 + + '@types/react-native@0.73.0(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0)': + dependencies: + react-native: 0.85.2(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0) + transitivePeerDependencies: + - '@babel/core' + - '@react-native-community/cli' + - '@react-native/jest-preset' + - '@react-native/metro-config' + - '@types/react' + - bufferutil + - react + - supports-color + - utf-8-validate + + '@types/react@19.2.14': + dependencies: + csstype: 3.2.3 + + '@types/stack-utils@2.0.3': {} + + '@types/tough-cookie@4.0.5': {} + + '@types/ws@8.18.1': + dependencies: + '@types/node': 25.6.0 + + '@types/yargs-parser@21.0.3': {} + + '@types/yargs@17.0.35': + dependencies: + '@types/yargs-parser': 21.0.3 + + '@typescript-eslint/eslint-plugin@8.59.1(@typescript-eslint/parser@8.59.1(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4)(typescript@5.9.3)': + dependencies: + '@eslint-community/regexpp': 4.12.2 + '@typescript-eslint/parser': 8.59.1(eslint@9.39.4)(typescript@5.9.3) + '@typescript-eslint/scope-manager': 8.59.1 + '@typescript-eslint/type-utils': 8.59.1(eslint@9.39.4)(typescript@5.9.3) + '@typescript-eslint/utils': 8.59.1(eslint@9.39.4)(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.59.1 + eslint: 9.39.4 + ignore: 7.0.5 + natural-compare: 1.4.0 + ts-api-utils: 2.5.0(typescript@5.9.3) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/parser@8.59.1(eslint@9.39.4)(typescript@5.9.3)': + dependencies: + '@typescript-eslint/scope-manager': 8.59.1 + '@typescript-eslint/types': 8.59.1 + '@typescript-eslint/typescript-estree': 8.59.1(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.59.1 + debug: 4.4.3 + eslint: 9.39.4 + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/project-service@8.59.1(typescript@5.9.3)': + dependencies: + '@typescript-eslint/tsconfig-utils': 8.59.1(typescript@5.9.3) + '@typescript-eslint/types': 8.59.1 + debug: 4.4.3 + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/scope-manager@8.59.1': + dependencies: + '@typescript-eslint/types': 8.59.1 + '@typescript-eslint/visitor-keys': 8.59.1 + + '@typescript-eslint/tsconfig-utils@8.59.1(typescript@5.9.3)': + dependencies: + typescript: 5.9.3 + + '@typescript-eslint/type-utils@8.59.1(eslint@9.39.4)(typescript@5.9.3)': + dependencies: + '@typescript-eslint/types': 8.59.1 + '@typescript-eslint/typescript-estree': 8.59.1(typescript@5.9.3) + '@typescript-eslint/utils': 8.59.1(eslint@9.39.4)(typescript@5.9.3) + debug: 4.4.3 + eslint: 9.39.4 + ts-api-utils: 2.5.0(typescript@5.9.3) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/types@8.59.1': {} + + '@typescript-eslint/typescript-estree@8.59.1(typescript@5.9.3)': + dependencies: + '@typescript-eslint/project-service': 8.59.1(typescript@5.9.3) + '@typescript-eslint/tsconfig-utils': 8.59.1(typescript@5.9.3) + '@typescript-eslint/types': 8.59.1 + '@typescript-eslint/visitor-keys': 8.59.1 + debug: 4.4.3 + minimatch: 10.2.5 + semver: 7.7.4 + tinyglobby: 0.2.16 + ts-api-utils: 2.5.0(typescript@5.9.3) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/utils@8.59.1(eslint@9.39.4)(typescript@5.9.3)': + dependencies: + '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.4) + '@typescript-eslint/scope-manager': 8.59.1 + '@typescript-eslint/types': 8.59.1 + '@typescript-eslint/typescript-estree': 8.59.1(typescript@5.9.3) + eslint: 9.39.4 + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/visitor-keys@8.59.1': + dependencies: + '@typescript-eslint/types': 8.59.1 + eslint-visitor-keys: 5.0.1 + + '@ungap/structured-clone@1.3.0': {} + + '@xmldom/xmldom@0.8.13': {} + + '@xmldom/xmldom@0.9.10': {} + + abab@2.0.6: {} + + abort-controller@3.0.0: + dependencies: + event-target-shim: 5.0.1 + + accepts@1.3.8: + dependencies: + mime-types: 2.1.35 + negotiator: 0.6.3 + + accepts@2.0.0: + dependencies: + mime-types: 3.0.2 + negotiator: 1.0.0 + + acorn-globals@7.0.1: + dependencies: + acorn: 8.16.0 + acorn-walk: 8.3.5 + + acorn-jsx@5.3.2(acorn@8.16.0): + dependencies: + acorn: 8.16.0 + + acorn-walk@8.3.5: + dependencies: + acorn: 8.16.0 + + acorn@8.16.0: {} + + agent-base@6.0.2: + dependencies: + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + + agent-base@7.1.4: {} + + ajv@6.15.0: + dependencies: + fast-deep-equal: 3.1.3 + fast-json-stable-stringify: 2.1.0 + json-schema-traverse: 0.4.1 + uri-js: 4.4.1 + + anser@1.4.10: {} + + ansi-escapes@4.3.2: + dependencies: + type-fest: 0.21.3 + + ansi-escapes@6.2.1: {} + + ansi-regex@4.1.1: {} + + ansi-regex@5.0.1: {} + + ansi-regex@6.2.2: {} + + ansi-styles@3.2.1: + dependencies: + color-convert: 1.9.3 + + ansi-styles@4.3.0: + dependencies: + color-convert: 2.0.1 + + ansi-styles@5.2.0: {} + + anymatch@3.1.3: + dependencies: + normalize-path: 3.0.0 + picomatch: 2.3.2 + + arg@5.0.2: {} + + argparse@1.0.10: + dependencies: + sprintf-js: 1.0.3 + + argparse@2.0.1: {} + + aria-hidden@1.2.6: + dependencies: + tslib: 2.8.1 + + array-buffer-byte-length@1.0.2: + dependencies: + call-bound: 1.0.4 + is-array-buffer: 3.0.5 + + array-includes@3.1.9: + dependencies: + call-bind: 1.0.9 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-abstract: 1.24.2 + es-object-atoms: 1.1.1 + get-intrinsic: 1.3.0 + is-string: 1.1.1 + math-intrinsics: 1.1.0 + + array.prototype.findlast@1.2.5: + dependencies: + call-bind: 1.0.9 + define-properties: 1.2.1 + es-abstract: 1.24.2 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + es-shim-unscopables: 1.1.0 + + array.prototype.findlastindex@1.2.6: + dependencies: + call-bind: 1.0.9 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-abstract: 1.24.2 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + es-shim-unscopables: 1.1.0 + + array.prototype.flat@1.3.3: + dependencies: + call-bind: 1.0.9 + define-properties: 1.2.1 + es-abstract: 1.24.2 + es-shim-unscopables: 1.1.0 + + array.prototype.flatmap@1.3.3: + dependencies: + call-bind: 1.0.9 + define-properties: 1.2.1 + es-abstract: 1.24.2 + es-shim-unscopables: 1.1.0 + + array.prototype.tosorted@1.1.4: + dependencies: + call-bind: 1.0.9 + define-properties: 1.2.1 + es-abstract: 1.24.2 + es-errors: 1.3.0 + es-shim-unscopables: 1.1.0 + + arraybuffer.prototype.slice@1.0.4: + dependencies: + array-buffer-byte-length: 1.0.2 + call-bind: 1.0.9 + define-properties: 1.2.1 + es-abstract: 1.24.2 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + is-array-buffer: 3.0.5 + + asap@2.0.6: {} + + async-function@1.0.0: {} + + asynckit@0.4.0: {} + + available-typed-arrays@1.0.7: + dependencies: + possible-typed-array-names: 1.1.0 + + babel-jest@29.7.0(@babel/core@7.29.0): + dependencies: + '@babel/core': 7.29.0 + '@jest/transform': 29.7.0 + '@types/babel__core': 7.20.5 + babel-plugin-istanbul: 6.1.1 + babel-preset-jest: 29.6.3(@babel/core@7.29.0) + chalk: 4.1.2 + graceful-fs: 4.2.11 + slash: 3.0.0 + transitivePeerDependencies: + - supports-color + + babel-plugin-dynamic-import-node@2.3.3: + dependencies: + object.assign: 4.1.7 + + babel-plugin-istanbul@6.1.1: + dependencies: + '@babel/helper-plugin-utils': 7.28.6 + '@istanbuljs/load-nyc-config': 1.1.0 + '@istanbuljs/schema': 0.1.6 + istanbul-lib-instrument: 5.2.1 + test-exclude: 6.0.0 + transitivePeerDependencies: + - supports-color + + babel-plugin-jest-hoist@29.6.3: + dependencies: + '@babel/template': 7.28.6 + '@babel/types': 7.29.0 + '@types/babel__core': 7.20.5 + '@types/babel__traverse': 7.28.0 + + babel-plugin-polyfill-corejs2@0.4.17(@babel/core@7.29.0): + dependencies: + '@babel/compat-data': 7.29.0 + '@babel/core': 7.29.0 + '@babel/helper-define-polyfill-provider': 0.6.8(@babel/core@7.29.0) + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + babel-plugin-polyfill-corejs3@0.13.0(@babel/core@7.29.0): + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-define-polyfill-provider': 0.6.8(@babel/core@7.29.0) + core-js-compat: 3.49.0 + transitivePeerDependencies: + - supports-color + + babel-plugin-polyfill-corejs3@0.14.2(@babel/core@7.29.0): + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-define-polyfill-provider': 0.6.8(@babel/core@7.29.0) + core-js-compat: 3.49.0 + transitivePeerDependencies: + - supports-color + + babel-plugin-polyfill-regenerator@0.6.8(@babel/core@7.29.0): + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-define-polyfill-provider': 0.6.8(@babel/core@7.29.0) + transitivePeerDependencies: + - supports-color + + babel-plugin-react-compiler@1.0.0: + dependencies: + '@babel/types': 7.29.0 + + babel-plugin-react-native-web@0.21.2: {} + + babel-plugin-syntax-hermes-parser@0.32.0: + dependencies: + hermes-parser: 0.32.0 + + babel-plugin-syntax-hermes-parser@0.32.1: + dependencies: + hermes-parser: 0.32.1 + + babel-plugin-syntax-hermes-parser@0.33.3: + dependencies: + hermes-parser: 0.33.3 + + babel-plugin-transform-flow-enums@0.0.2(@babel/core@7.29.0): + dependencies: + '@babel/plugin-syntax-flow': 7.28.6(@babel/core@7.29.0) + transitivePeerDependencies: + - '@babel/core' + + babel-preset-current-node-syntax@1.2.0(@babel/core@7.29.0): + dependencies: + '@babel/core': 7.29.0 + '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.29.0) + '@babel/plugin-syntax-bigint': 7.8.3(@babel/core@7.29.0) + '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.29.0) + '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.29.0) + '@babel/plugin-syntax-import-attributes': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.29.0) + '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.29.0) + '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.29.0) + '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.29.0) + '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.29.0) + '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.29.0) + '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.29.0) + '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.29.0) + '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.29.0) + '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.29.0) + + babel-preset-expo@55.0.18(@babel/core@7.29.0)(@babel/runtime@7.29.2)(expo@55.0.17)(react-refresh@0.14.2): + dependencies: + '@babel/generator': 7.29.1 + '@babel/helper-module-imports': 7.28.6 + '@babel/plugin-proposal-decorators': 7.29.0(@babel/core@7.29.0) + '@babel/plugin-proposal-export-default-from': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-syntax-export-default-from': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-class-static-block': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-export-namespace-from': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-flow-strip-types': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-modules-commonjs': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-object-rest-spread': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-parameters': 7.27.7(@babel/core@7.29.0) + '@babel/plugin-transform-private-methods': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-private-property-in-object': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-runtime': 7.29.0(@babel/core@7.29.0) + '@babel/preset-react': 7.28.5(@babel/core@7.29.0) + '@babel/preset-typescript': 7.28.5(@babel/core@7.29.0) + '@react-native/babel-preset': 0.83.6(@babel/core@7.29.0) + babel-plugin-react-compiler: 1.0.0 + babel-plugin-react-native-web: 0.21.2 + babel-plugin-syntax-hermes-parser: 0.32.1 + babel-plugin-transform-flow-enums: 0.0.2(@babel/core@7.29.0) + debug: 4.4.3 + react-refresh: 0.14.2 + resolve-from: 5.0.0 + optionalDependencies: + '@babel/runtime': 7.29.2 + expo: 55.0.17(@babel/core@7.29.0)(@expo/dom-webview@55.0.5)(@expo/metro-runtime@55.0.10)(expo-router@55.0.13)(react-dom@19.2.5(react@19.2.0))(react-native-webview@13.16.1(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0))(react-native-worklets@0.7.4(@babel/core@7.29.0)(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0))(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0)(typescript@5.9.3) + transitivePeerDependencies: + - '@babel/core' + - supports-color + + babel-preset-jest@29.6.3(@babel/core@7.29.0): + dependencies: + '@babel/core': 7.29.0 + babel-plugin-jest-hoist: 29.6.3 + babel-preset-current-node-syntax: 1.2.0(@babel/core@7.29.0) + + badgin@1.2.3: {} + + balanced-match@1.0.2: {} + + balanced-match@4.0.4: {} + + barcode-detector@3.1.2(@types/emscripten@1.41.5): + dependencies: + zxing-wasm: 3.0.2(@types/emscripten@1.41.5) + transitivePeerDependencies: + - '@types/emscripten' + + base64-js@1.5.1: {} + + baseline-browser-mapping@2.10.23: {} + + better-opn@3.0.2: + dependencies: + open: 8.4.2 + + big-integer@1.6.52: {} + + binary-extensions@2.3.0: + optional: true + + boolbase@1.0.0: {} + + bplist-creator@0.1.0: + dependencies: + stream-buffers: 2.2.0 + + bplist-parser@0.3.1: + dependencies: + big-integer: 1.6.52 + + bplist-parser@0.3.2: + dependencies: + big-integer: 1.6.52 + + brace-expansion@1.1.14: + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + + brace-expansion@5.0.5: + dependencies: + balanced-match: 4.0.4 + + braces@3.0.3: + dependencies: + fill-range: 7.1.1 + + browserslist@4.28.2: + dependencies: + baseline-browser-mapping: 2.10.23 + caniuse-lite: 1.0.30001791 + electron-to-chromium: 1.5.344 + node-releases: 2.0.38 + update-browserslist-db: 1.2.3(browserslist@4.28.2) + + bs-logger@0.2.6: + dependencies: + fast-json-stable-stringify: 2.1.0 + + bser@2.1.1: + dependencies: + node-int64: 0.4.0 + + buffer-from@1.1.2: {} + + bytes@3.1.2: {} + + call-bind-apply-helpers@1.0.2: + dependencies: + es-errors: 1.3.0 + function-bind: 1.1.2 + + call-bind@1.0.9: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-define-property: 1.0.1 + get-intrinsic: 1.3.0 + set-function-length: 1.2.2 + + call-bound@1.0.4: + dependencies: + call-bind-apply-helpers: 1.0.2 + get-intrinsic: 1.3.0 + + callsites@3.1.0: {} + + camelcase@5.3.1: {} + + camelcase@6.3.0: {} + + caniuse-lite@1.0.30001791: {} + + chalk@2.4.2: + dependencies: + ansi-styles: 3.2.1 + escape-string-regexp: 1.0.5 + supports-color: 5.5.0 + + chalk@3.0.0: + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + + chalk@4.1.2: + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + + char-regex@1.0.2: {} + + char-regex@2.0.2: {} + + chokidar@3.6.0: + dependencies: + anymatch: 3.1.3 + braces: 3.0.3 + glob-parent: 5.1.2 + is-binary-path: 2.1.0 + is-glob: 4.0.3 + normalize-path: 3.0.0 + readdirp: 3.6.0 + optionalDependencies: + fsevents: 2.3.3 + optional: true + + chrome-launcher@0.15.2: + dependencies: + '@types/node': 25.6.0 + escape-string-regexp: 4.0.0 + is-wsl: 2.2.0 + lighthouse-logger: 1.4.2 + transitivePeerDependencies: + - supports-color + + chromium-edge-launcher@0.2.0: + dependencies: + '@types/node': 25.6.0 + escape-string-regexp: 4.0.0 + is-wsl: 2.2.0 + lighthouse-logger: 1.4.2 + mkdirp: 1.0.4 + rimraf: 3.0.2 + transitivePeerDependencies: + - supports-color + + chromium-edge-launcher@0.3.0: + dependencies: + '@types/node': 25.6.0 + escape-string-regexp: 4.0.0 + is-wsl: 2.2.0 + lighthouse-logger: 1.4.2 + mkdirp: 1.0.4 + transitivePeerDependencies: + - supports-color + + ci-info@2.0.0: {} + + ci-info@3.9.0: {} + + cjs-module-lexer@1.4.3: {} + + cli-cursor@2.1.0: + dependencies: + restore-cursor: 2.0.0 + + cli-spinners@2.9.2: {} + + client-only@0.0.1: {} + + cliui@8.0.1: + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 7.0.0 + + clone@1.0.4: {} + + co@4.6.0: {} + + collect-v8-coverage@1.0.3: {} + + color-convert@1.9.3: + dependencies: + color-name: 1.1.3 + + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 + + color-name@1.1.3: {} + + color-name@1.1.4: {} + + color-string@1.9.1: + dependencies: + color-name: 1.1.4 + simple-swizzle: 0.2.4 + + color@4.2.3: + dependencies: + color-convert: 2.0.1 + color-string: 1.9.1 + + combined-stream@1.0.8: + dependencies: + delayed-stream: 1.0.0 + + commander@12.1.0: {} + + commander@2.20.3: {} + + commander@6.2.1: {} + + commander@7.2.0: {} + + compressible@2.0.18: + dependencies: + mime-db: 1.54.0 + + compression@1.8.1: + dependencies: + bytes: 3.1.2 + compressible: 2.0.18 + debug: 2.6.9 + negotiator: 0.6.4 + on-headers: 1.1.0 + safe-buffer: 5.2.1 + vary: 1.1.2 + transitivePeerDependencies: + - supports-color + + concat-map@0.0.1: {} + + connect@3.7.0: + dependencies: + debug: 2.6.9 + finalhandler: 1.1.2 + parseurl: 1.3.3 + utils-merge: 1.0.1 + transitivePeerDependencies: + - supports-color + + convert-source-map@2.0.0: {} + + core-js-compat@3.49.0: + dependencies: + browserslist: 4.28.2 + + create-jest@29.7.0(@types/node@25.6.0): + dependencies: + '@jest/types': 29.6.3 + chalk: 4.1.2 + exit: 0.1.2 + graceful-fs: 4.2.11 + jest-config: 29.7.0(@types/node@25.6.0) + jest-util: 29.7.0 + prompts: 2.4.2 + transitivePeerDependencies: + - '@types/node' + - babel-plugin-macros + - supports-color + - ts-node + + cross-fetch@3.2.0: + dependencies: + node-fetch: 2.7.0 + transitivePeerDependencies: + - encoding + + cross-spawn@7.0.6: + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + + css-in-js-utils@3.1.0: + dependencies: + hyphenate-style-name: 1.1.0 + + css-select@5.2.2: + dependencies: + boolbase: 1.0.0 + css-what: 6.2.2 + domhandler: 5.0.3 + domutils: 3.2.2 + nth-check: 2.1.1 + + css-tree@1.1.3: + dependencies: + mdn-data: 2.0.14 + source-map: 0.6.1 + + css-what@6.2.2: {} + + cssom@0.3.8: {} + + cssom@0.5.0: {} + + cssstyle@2.3.0: + dependencies: + cssom: 0.3.8 + + csstype@3.2.3: {} + + data-urls@3.0.2: + dependencies: + abab: 2.0.6 + whatwg-mimetype: 3.0.0 + whatwg-url: 11.0.0 + + data-view-buffer@1.0.2: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-data-view: 1.0.2 + + data-view-byte-length@1.0.2: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-data-view: 1.0.2 + + data-view-byte-offset@1.0.1: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-data-view: 1.0.2 + + debug@2.6.9: + dependencies: + ms: 2.0.0 + + debug@3.2.7: + dependencies: + ms: 2.1.3 + + debug@4.4.3: + dependencies: + ms: 2.1.3 + + decimal.js@10.6.0: {} + + decode-uri-component@0.2.2: {} + + dedent@1.7.2: {} + + deep-is@0.1.4: {} + + deepmerge@4.3.1: {} + + defaults@1.0.4: + dependencies: + clone: 1.0.4 + + define-data-property@1.1.4: + dependencies: + es-define-property: 1.0.1 + es-errors: 1.3.0 + gopd: 1.2.0 + + define-lazy-prop@2.0.0: {} + + define-properties@1.2.1: + dependencies: + define-data-property: 1.1.4 + has-property-descriptors: 1.0.2 + object-keys: 1.1.1 + + delayed-stream@1.0.0: {} + + depd@2.0.0: {} + + destroy@1.2.0: {} + + detect-libc@2.1.2: {} + + detect-newline@3.1.0: {} + + detect-node-es@1.1.0: {} + + diff-sequences@29.6.3: {} + + dnssd-advertise@1.1.4: {} + + doctrine@2.1.0: + dependencies: + esutils: 2.0.3 + + dom-serializer@2.0.0: + dependencies: + domelementtype: 2.3.0 + domhandler: 5.0.3 + entities: 4.5.0 + + domelementtype@2.3.0: {} + + domexception@4.0.0: + dependencies: + webidl-conversions: 7.0.0 + + domhandler@5.0.3: + dependencies: + domelementtype: 2.3.0 + + domutils@3.2.2: + dependencies: + dom-serializer: 2.0.0 + domelementtype: 2.3.0 + domhandler: 5.0.3 + + dunder-proto@1.0.1: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-errors: 1.3.0 + gopd: 1.2.0 + + ee-first@1.1.1: {} + + electron-to-chromium@1.5.344: {} + + emittery@0.13.1: {} + + emoji-regex@8.0.0: {} + + encodeurl@1.0.2: {} + + encodeurl@2.0.0: {} + + enhanced-resolve@5.21.0: + dependencies: + graceful-fs: 4.2.11 + tapable: 2.3.3 + + entities@4.5.0: {} + + entities@6.0.1: {} + + error-ex@1.3.4: + dependencies: + is-arrayish: 0.2.1 + + error-stack-parser@2.1.4: + dependencies: + stackframe: 1.3.4 + + es-abstract@1.24.2: + dependencies: + array-buffer-byte-length: 1.0.2 + arraybuffer.prototype.slice: 1.0.4 + available-typed-arrays: 1.0.7 + call-bind: 1.0.9 + call-bound: 1.0.4 + data-view-buffer: 1.0.2 + data-view-byte-length: 1.0.2 + data-view-byte-offset: 1.0.1 + es-define-property: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + es-set-tostringtag: 2.1.0 + es-to-primitive: 1.3.0 + function.prototype.name: 1.1.8 + get-intrinsic: 1.3.0 + get-proto: 1.0.1 + get-symbol-description: 1.1.0 + globalthis: 1.0.4 + gopd: 1.2.0 + has-property-descriptors: 1.0.2 + has-proto: 1.2.0 + has-symbols: 1.1.0 + hasown: 2.0.3 + internal-slot: 1.1.0 + is-array-buffer: 3.0.5 + is-callable: 1.2.7 + is-data-view: 1.0.2 + is-negative-zero: 2.0.3 + is-regex: 1.2.1 + is-set: 2.0.3 + is-shared-array-buffer: 1.0.4 + is-string: 1.1.1 + is-typed-array: 1.1.15 + is-weakref: 1.1.1 + math-intrinsics: 1.1.0 + object-inspect: 1.13.4 + object-keys: 1.1.1 + object.assign: 4.1.7 + own-keys: 1.0.1 + regexp.prototype.flags: 1.5.4 + safe-array-concat: 1.1.4 + safe-push-apply: 1.0.0 + safe-regex-test: 1.1.0 + set-proto: 1.0.0 + stop-iteration-iterator: 1.1.0 + string.prototype.trim: 1.2.10 + string.prototype.trimend: 1.0.9 + string.prototype.trimstart: 1.0.8 + typed-array-buffer: 1.0.3 + typed-array-byte-length: 1.0.3 + typed-array-byte-offset: 1.0.4 + typed-array-length: 1.0.7 + unbox-primitive: 1.1.0 + which-typed-array: 1.1.20 + + es-define-property@1.0.1: {} + + es-errors@1.3.0: {} + + es-iterator-helpers@1.3.2: + dependencies: + call-bind: 1.0.9 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-abstract: 1.24.2 + es-errors: 1.3.0 + es-set-tostringtag: 2.1.0 + function-bind: 1.1.2 + get-intrinsic: 1.3.0 + globalthis: 1.0.4 + gopd: 1.2.0 + has-property-descriptors: 1.0.2 + has-proto: 1.2.0 + has-symbols: 1.1.0 + internal-slot: 1.1.0 + iterator.prototype: 1.1.5 + math-intrinsics: 1.1.0 + + es-object-atoms@1.1.1: + dependencies: + es-errors: 1.3.0 + + es-set-tostringtag@2.1.0: + dependencies: + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + has-tostringtag: 1.0.2 + hasown: 2.0.3 + + es-shim-unscopables@1.1.0: + dependencies: + hasown: 2.0.3 + + es-to-primitive@1.3.0: + dependencies: + is-callable: 1.2.7 + is-date-object: 1.1.0 + is-symbol: 1.1.1 + + esbuild@0.27.7: + optionalDependencies: + '@esbuild/aix-ppc64': 0.27.7 + '@esbuild/android-arm': 0.27.7 + '@esbuild/android-arm64': 0.27.7 + '@esbuild/android-x64': 0.27.7 + '@esbuild/darwin-arm64': 0.27.7 + '@esbuild/darwin-x64': 0.27.7 + '@esbuild/freebsd-arm64': 0.27.7 + '@esbuild/freebsd-x64': 0.27.7 + '@esbuild/linux-arm': 0.27.7 + '@esbuild/linux-arm64': 0.27.7 + '@esbuild/linux-ia32': 0.27.7 + '@esbuild/linux-loong64': 0.27.7 + '@esbuild/linux-mips64el': 0.27.7 + '@esbuild/linux-ppc64': 0.27.7 + '@esbuild/linux-riscv64': 0.27.7 + '@esbuild/linux-s390x': 0.27.7 + '@esbuild/linux-x64': 0.27.7 + '@esbuild/netbsd-arm64': 0.27.7 + '@esbuild/netbsd-x64': 0.27.7 + '@esbuild/openbsd-arm64': 0.27.7 + '@esbuild/openbsd-x64': 0.27.7 + '@esbuild/openharmony-arm64': 0.27.7 + '@esbuild/sunos-x64': 0.27.7 + '@esbuild/win32-arm64': 0.27.7 + '@esbuild/win32-ia32': 0.27.7 + '@esbuild/win32-x64': 0.27.7 + + escalade@3.2.0: {} + + escape-html@1.0.3: {} + + escape-string-regexp@1.0.5: {} + + escape-string-regexp@2.0.0: {} + + escape-string-regexp@4.0.0: {} + + escodegen@2.1.0: + dependencies: + esprima: 4.0.1 + estraverse: 5.3.0 + esutils: 2.0.3 + optionalDependencies: + source-map: 0.6.1 + + eslint-compat-utils@0.5.1(eslint@9.39.4): + dependencies: + eslint: 9.39.4 + semver: 7.7.4 + + eslint-config-prettier@9.1.2(eslint@9.39.4): + dependencies: + eslint: 9.39.4 + + eslint-config-universe@15.0.3(eslint@9.39.4)(prettier@2.8.8)(typescript@5.9.3): + dependencies: + '@typescript-eslint/eslint-plugin': 8.59.1(@typescript-eslint/parser@8.59.1(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4)(typescript@5.9.3) + '@typescript-eslint/parser': 8.59.1(eslint@9.39.4)(typescript@5.9.3) + eslint: 9.39.4 + eslint-config-prettier: 9.1.2(eslint@9.39.4) + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.59.1(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4) + eslint-plugin-n: 17.24.0(eslint@9.39.4)(typescript@5.9.3) + eslint-plugin-node: 11.1.0(eslint@9.39.4) + eslint-plugin-prettier: 5.5.5(eslint-config-prettier@9.1.2(eslint@9.39.4))(eslint@9.39.4)(prettier@2.8.8) + eslint-plugin-react: 7.37.5(eslint@9.39.4) + eslint-plugin-react-hooks: 5.2.0(eslint@9.39.4) + globals: 16.5.0 + optionalDependencies: + prettier: 2.8.8 + transitivePeerDependencies: + - '@types/eslint' + - eslint-import-resolver-typescript + - eslint-import-resolver-webpack + - supports-color + - typescript + + eslint-import-resolver-node@0.3.10: + dependencies: + debug: 3.2.7 + is-core-module: 2.16.1 + resolve: 2.0.0-next.6 + transitivePeerDependencies: + - supports-color + + eslint-module-utils@2.12.1(@typescript-eslint/parser@8.59.1(eslint@9.39.4)(typescript@5.9.3))(eslint-import-resolver-node@0.3.10)(eslint@9.39.4): + dependencies: + debug: 3.2.7 + optionalDependencies: + '@typescript-eslint/parser': 8.59.1(eslint@9.39.4)(typescript@5.9.3) + eslint: 9.39.4 + eslint-import-resolver-node: 0.3.10 + transitivePeerDependencies: + - supports-color + + eslint-plugin-es-x@7.8.0(eslint@9.39.4): + dependencies: + '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.4) + '@eslint-community/regexpp': 4.12.2 + eslint: 9.39.4 + eslint-compat-utils: 0.5.1(eslint@9.39.4) + + eslint-plugin-es@3.0.1(eslint@9.39.4): + dependencies: + eslint: 9.39.4 + eslint-utils: 2.1.0 + regexpp: 3.2.0 + + eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.59.1(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4): + dependencies: + '@rtsao/scc': 1.1.0 + array-includes: 3.1.9 + array.prototype.findlastindex: 1.2.6 + array.prototype.flat: 1.3.3 + array.prototype.flatmap: 1.3.3 + debug: 3.2.7 + doctrine: 2.1.0 + eslint: 9.39.4 + eslint-import-resolver-node: 0.3.10 + eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.59.1(eslint@9.39.4)(typescript@5.9.3))(eslint-import-resolver-node@0.3.10)(eslint@9.39.4) + hasown: 2.0.3 + is-core-module: 2.16.1 + is-glob: 4.0.3 + minimatch: 3.1.5 + object.fromentries: 2.0.8 + object.groupby: 1.0.3 + object.values: 1.2.1 + semver: 6.3.1 + string.prototype.trimend: 1.0.9 + tsconfig-paths: 3.15.0 + optionalDependencies: + '@typescript-eslint/parser': 8.59.1(eslint@9.39.4)(typescript@5.9.3) + transitivePeerDependencies: + - eslint-import-resolver-typescript + - eslint-import-resolver-webpack + - supports-color + + eslint-plugin-n@17.24.0(eslint@9.39.4)(typescript@5.9.3): + dependencies: + '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.4) + enhanced-resolve: 5.21.0 + eslint: 9.39.4 + eslint-plugin-es-x: 7.8.0(eslint@9.39.4) + get-tsconfig: 4.14.0 + globals: 15.15.0 + globrex: 0.1.2 + ignore: 5.3.2 + semver: 7.7.4 + ts-declaration-location: 1.0.7(typescript@5.9.3) + transitivePeerDependencies: + - typescript + + eslint-plugin-node@11.1.0(eslint@9.39.4): + dependencies: + eslint: 9.39.4 + eslint-plugin-es: 3.0.1(eslint@9.39.4) + eslint-utils: 2.1.0 + ignore: 5.3.2 + minimatch: 3.1.5 + resolve: 1.22.12 + semver: 6.3.1 + + eslint-plugin-prettier@5.5.5(eslint-config-prettier@9.1.2(eslint@9.39.4))(eslint@9.39.4)(prettier@2.8.8): + dependencies: + eslint: 9.39.4 + prettier: 2.8.8 + prettier-linter-helpers: 1.0.1 + synckit: 0.11.12 + optionalDependencies: + eslint-config-prettier: 9.1.2(eslint@9.39.4) + + eslint-plugin-react-hooks@5.2.0(eslint@9.39.4): + dependencies: + eslint: 9.39.4 + + eslint-plugin-react@7.37.5(eslint@9.39.4): + dependencies: + array-includes: 3.1.9 + array.prototype.findlast: 1.2.5 + array.prototype.flatmap: 1.3.3 + array.prototype.tosorted: 1.1.4 + doctrine: 2.1.0 + es-iterator-helpers: 1.3.2 + eslint: 9.39.4 + estraverse: 5.3.0 + hasown: 2.0.3 + jsx-ast-utils: 3.3.5 + minimatch: 3.1.5 + object.entries: 1.1.9 + object.fromentries: 2.0.8 + object.values: 1.2.1 + prop-types: 15.8.1 + resolve: 2.0.0-next.6 + semver: 6.3.1 + string.prototype.matchall: 4.0.12 + string.prototype.repeat: 1.0.0 + + eslint-scope@8.4.0: + dependencies: + esrecurse: 4.3.0 + estraverse: 5.3.0 + + eslint-utils@2.1.0: + dependencies: + eslint-visitor-keys: 1.3.0 + + eslint-visitor-keys@1.3.0: {} + + eslint-visitor-keys@3.4.3: {} + + eslint-visitor-keys@4.2.1: {} + + eslint-visitor-keys@5.0.1: {} + + eslint@9.39.4: + dependencies: + '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.4) + '@eslint-community/regexpp': 4.12.2 + '@eslint/config-array': 0.21.2 + '@eslint/config-helpers': 0.4.2 + '@eslint/core': 0.17.0 + '@eslint/eslintrc': 3.3.5 + '@eslint/js': 9.39.4 + '@eslint/plugin-kit': 0.4.1 + '@humanfs/node': 0.16.8 + '@humanwhocodes/module-importer': 1.0.1 + '@humanwhocodes/retry': 0.4.3 + '@types/estree': 1.0.8 + ajv: 6.15.0 + chalk: 4.1.2 + cross-spawn: 7.0.6 + debug: 4.4.3 + escape-string-regexp: 4.0.0 + eslint-scope: 8.4.0 + eslint-visitor-keys: 4.2.1 + espree: 10.4.0 + esquery: 1.7.0 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 8.0.0 + find-up: 5.0.0 + glob-parent: 6.0.2 + ignore: 5.3.2 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + json-stable-stringify-without-jsonify: 1.0.1 + lodash.merge: 4.6.2 + minimatch: 3.1.5 + natural-compare: 1.4.0 + optionator: 0.9.4 + transitivePeerDependencies: + - supports-color + + espree@10.4.0: + dependencies: + acorn: 8.16.0 + acorn-jsx: 5.3.2(acorn@8.16.0) + eslint-visitor-keys: 4.2.1 + + esprima@4.0.1: {} + + esquery@1.7.0: + dependencies: + estraverse: 5.3.0 + + esrecurse@4.3.0: + dependencies: + estraverse: 5.3.0 + + estraverse@5.3.0: {} + + esutils@2.0.3: {} + + etag@1.8.1: {} + + event-target-shim@5.0.1: {} + + execa@5.1.1: + dependencies: + cross-spawn: 7.0.6 + get-stream: 6.0.1 + human-signals: 2.1.0 + is-stream: 2.0.1 + merge-stream: 2.0.0 + npm-run-path: 4.0.1 + onetime: 5.1.2 + signal-exit: 3.0.7 + strip-final-newline: 2.0.0 + + exit@0.1.2: {} + + expect@29.7.0: + dependencies: + '@jest/expect-utils': 29.7.0 + jest-get-type: 29.6.3 + jest-matcher-utils: 29.7.0 + jest-message-util: 29.7.0 + jest-util: 29.7.0 + + expo-application@55.0.14(expo@55.0.17): + dependencies: + expo: 55.0.17(@babel/core@7.29.0)(@expo/dom-webview@55.0.5)(@expo/metro-runtime@55.0.10)(expo-router@55.0.13)(react-dom@19.2.5(react@19.2.0))(react-native-webview@13.16.1(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0))(react-native-worklets@0.7.4(@babel/core@7.29.0)(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0))(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0)(typescript@5.9.3) + + expo-asset@55.0.16(expo@55.0.17)(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0)(typescript@5.9.3): + dependencies: + '@expo/image-utils': 0.8.13(typescript@5.9.3) + expo: 55.0.17(@babel/core@7.29.0)(@expo/dom-webview@55.0.5)(@expo/metro-runtime@55.0.10)(expo-router@55.0.13)(react-dom@19.2.5(react@19.2.0))(react-native-webview@13.16.1(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0))(react-native-worklets@0.7.4(@babel/core@7.29.0)(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0))(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0)(typescript@5.9.3) + expo-constants: 55.0.15(expo@55.0.17)(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0)) + react: 19.2.0 + react-native: 0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0) + transitivePeerDependencies: + - supports-color + - typescript + + expo-build-properties@55.0.13(expo@55.0.17): + dependencies: + '@expo/schema-utils': 55.0.3 + expo: 55.0.17(@babel/core@7.29.0)(@expo/dom-webview@55.0.5)(@expo/metro-runtime@55.0.10)(expo-router@55.0.13)(react-dom@19.2.5(react@19.2.0))(react-native-webview@13.16.1(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0))(react-native-worklets@0.7.4(@babel/core@7.29.0)(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0))(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0)(typescript@5.9.3) + resolve-from: 5.0.0 + semver: 7.7.4 + + expo-camera@55.0.16(@types/emscripten@1.41.5)(expo@55.0.17)(react-native-web@0.21.2(react-dom@19.2.5(react@19.2.0))(react@19.2.0))(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0): + dependencies: + barcode-detector: 3.1.2(@types/emscripten@1.41.5) + expo: 55.0.17(@babel/core@7.29.0)(@expo/dom-webview@55.0.5)(@expo/metro-runtime@55.0.10)(expo-router@55.0.13)(react-dom@19.2.5(react@19.2.0))(react-native-webview@13.16.1(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0))(react-native-worklets@0.7.4(@babel/core@7.29.0)(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0))(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0)(typescript@5.9.3) + react: 19.2.0 + react-native: 0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0) + optionalDependencies: + react-native-web: 0.21.2(react-dom@19.2.5(react@19.2.0))(react@19.2.0) + transitivePeerDependencies: + - '@types/emscripten' + + expo-constants@55.0.15(expo@55.0.17)(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0)): + dependencies: + '@expo/env': 2.1.1 + expo: 55.0.17(@babel/core@7.29.0)(@expo/dom-webview@55.0.5)(@expo/metro-runtime@55.0.10)(expo-router@55.0.13)(react-dom@19.2.5(react@19.2.0))(react-native-webview@13.16.1(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0))(react-native-worklets@0.7.4(@babel/core@7.29.0)(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0))(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0)(typescript@5.9.3) + react-native: 0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0) + transitivePeerDependencies: + - supports-color + + expo-crypto@55.0.14(expo@55.0.17): + dependencies: + expo: 55.0.17(@babel/core@7.29.0)(@expo/dom-webview@55.0.5)(@expo/metro-runtime@55.0.10)(expo-router@55.0.13)(react-dom@19.2.5(react@19.2.0))(react-native-webview@13.16.1(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0))(react-native-worklets@0.7.4(@babel/core@7.29.0)(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0))(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0)(typescript@5.9.3) + + expo-file-system@55.0.17(expo@55.0.17)(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0)): + dependencies: + expo: 55.0.17(@babel/core@7.29.0)(@expo/dom-webview@55.0.5)(@expo/metro-runtime@55.0.10)(expo-router@55.0.13)(react-dom@19.2.5(react@19.2.0))(react-native-webview@13.16.1(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0))(react-native-worklets@0.7.4(@babel/core@7.29.0)(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0))(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0)(typescript@5.9.3) + react-native: 0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0) + + expo-font@55.0.6(expo@55.0.17)(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0): + dependencies: + expo: 55.0.17(@babel/core@7.29.0)(@expo/dom-webview@55.0.5)(@expo/metro-runtime@55.0.10)(expo-router@55.0.13)(react-dom@19.2.5(react@19.2.0))(react-native-webview@13.16.1(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0))(react-native-worklets@0.7.4(@babel/core@7.29.0)(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0))(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0)(typescript@5.9.3) + fontfaceobserver: 2.3.0 + react: 19.2.0 + react-native: 0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0) + + expo-glass-effect@55.0.10(expo@55.0.17)(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0): + dependencies: + expo: 55.0.17(@babel/core@7.29.0)(@expo/dom-webview@55.0.5)(@expo/metro-runtime@55.0.10)(expo-router@55.0.13)(react-dom@19.2.5(react@19.2.0))(react-native-webview@13.16.1(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0))(react-native-worklets@0.7.4(@babel/core@7.29.0)(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0))(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0)(typescript@5.9.3) + react: 19.2.0 + react-native: 0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0) + + expo-haptics@55.0.14(expo@55.0.17): + dependencies: + expo: 55.0.17(@babel/core@7.29.0)(@expo/dom-webview@55.0.5)(@expo/metro-runtime@55.0.10)(expo-router@55.0.13)(react-dom@19.2.5(react@19.2.0))(react-native-webview@13.16.1(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0))(react-native-worklets@0.7.4(@babel/core@7.29.0)(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0))(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0)(typescript@5.9.3) + + expo-image@55.0.9(expo@55.0.17)(react-native-web@0.21.2(react-dom@19.2.5(react@19.2.0))(react@19.2.0))(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0): + dependencies: + expo: 55.0.17(@babel/core@7.29.0)(@expo/dom-webview@55.0.5)(@expo/metro-runtime@55.0.10)(expo-router@55.0.13)(react-dom@19.2.5(react@19.2.0))(react-native-webview@13.16.1(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0))(react-native-worklets@0.7.4(@babel/core@7.29.0)(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0))(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0)(typescript@5.9.3) + react: 19.2.0 + react-native: 0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0) + sf-symbols-typescript: 2.2.0 + optionalDependencies: + react-native-web: 0.21.2(react-dom@19.2.5(react@19.2.0))(react@19.2.0) + + expo-keep-awake@55.0.6(expo@55.0.17)(react@19.2.0): + dependencies: + expo: 55.0.17(@babel/core@7.29.0)(@expo/dom-webview@55.0.5)(@expo/metro-runtime@55.0.10)(expo-router@55.0.13)(react-dom@19.2.5(react@19.2.0))(react-native-webview@13.16.1(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0))(react-native-worklets@0.7.4(@babel/core@7.29.0)(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0))(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0)(typescript@5.9.3) + react: 19.2.0 + + expo-linking@55.0.14(expo@55.0.17)(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0): + dependencies: + expo-constants: 55.0.15(expo@55.0.17)(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0)) + invariant: 2.2.4 + react: 19.2.0 + react-native: 0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0) + transitivePeerDependencies: + - expo + - supports-color + + expo-module-scripts@55.0.2(@babel/core@7.29.0)(@babel/runtime@7.29.2)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.29.0))(eslint@9.39.4)(expo@55.0.17)(jest@29.7.0(@types/node@25.6.0))(prettier@2.8.8)(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react-refresh@0.14.2)(react-test-renderer@19.2.0(react@19.2.0))(react@19.2.0): + dependencies: + '@babel/cli': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-export-namespace-from': 7.27.1(@babel/core@7.29.0) + '@babel/preset-env': 7.29.2(@babel/core@7.29.0) + '@babel/preset-typescript': 7.28.5(@babel/core@7.29.0) + '@expo/npm-proofread': 1.0.1 + '@expo/spawn-async': 1.7.2 + '@testing-library/react-native': 13.3.3(jest@29.7.0(@types/node@25.6.0))(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react-test-renderer@19.2.0(react@19.2.0))(react@19.2.0) + '@tsconfig/node18': 18.2.6 + '@types/jest': 29.5.14 + babel-plugin-dynamic-import-node: 2.3.3 + babel-preset-expo: 55.0.18(@babel/core@7.29.0)(@babel/runtime@7.29.2)(expo@55.0.17)(react-refresh@0.14.2) + commander: 12.1.0 + eslint-config-universe: 15.0.3(eslint@9.39.4)(prettier@2.8.8)(typescript@5.9.3) + glob: 13.0.6 + jest-expo: 55.0.16(@babel/core@7.29.0)(expo@55.0.17)(jest@29.7.0(@types/node@25.6.0))(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0)(typescript@5.9.3) + jest-snapshot-prettier: prettier@2.8.8 + jest-watch-typeahead: 2.2.1(jest@29.7.0(@types/node@25.6.0)) + resolve-workspace-root: 2.0.1 + ts-jest: 29.0.5(@babel/core@7.29.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.29.0))(jest@29.7.0(@types/node@25.6.0))(typescript@5.9.3) + typescript: 5.9.3 + transitivePeerDependencies: + - '@babel/core' + - '@babel/runtime' + - '@jest/types' + - '@types/eslint' + - babel-jest + - bufferutil + - canvas + - esbuild + - eslint + - eslint-import-resolver-typescript + - eslint-import-resolver-webpack + - expo + - expo-widgets + - jest + - prettier + - react + - react-native + - react-refresh + - react-server-dom-webpack + - react-test-renderer + - supports-color + - utf-8-validate + + expo-modules-autolinking@55.0.18(typescript@5.9.3): + dependencies: + '@expo/require-utils': 55.0.4(typescript@5.9.3) + '@expo/spawn-async': 1.7.2 + chalk: 4.1.2 + commander: 7.2.0 + transitivePeerDependencies: + - supports-color + - typescript + + expo-modules-core@55.0.23(react-native-worklets@0.7.4(@babel/core@7.29.0)(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0))(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0): + dependencies: + invariant: 2.2.4 + react: 19.2.0 + react-native: 0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0) + optionalDependencies: + react-native-worklets: 0.7.4(@babel/core@7.29.0)(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0) + + expo-notifications@55.0.21(expo@55.0.17)(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0)(typescript@5.9.3): + dependencies: + '@expo/image-utils': 0.8.13(typescript@5.9.3) + abort-controller: 3.0.0 + badgin: 1.2.3 + expo: 55.0.17(@babel/core@7.29.0)(@expo/dom-webview@55.0.5)(@expo/metro-runtime@55.0.10)(expo-router@55.0.13)(react-dom@19.2.5(react@19.2.0))(react-native-webview@13.16.1(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0))(react-native-worklets@0.7.4(@babel/core@7.29.0)(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0))(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0)(typescript@5.9.3) + expo-application: 55.0.14(expo@55.0.17) + expo-constants: 55.0.15(expo@55.0.17)(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0)) + react: 19.2.0 + react-native: 0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0) + transitivePeerDependencies: + - supports-color + - typescript + + expo-router@55.0.13(121105c3b042d5e83ab3c1d3b84ed55f): + dependencies: + '@expo/log-box': 55.0.11(@expo/dom-webview@55.0.5)(expo@55.0.17)(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0) + '@expo/metro-runtime': 55.0.10(@expo/dom-webview@55.0.5)(expo@55.0.17)(react-dom@19.2.5(react@19.2.0))(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0) + '@expo/schema-utils': 55.0.3 + '@radix-ui/react-slot': 1.2.4(@types/react@19.2.14)(react@19.2.0) + '@radix-ui/react-tabs': 1.1.13(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.0))(react@19.2.0) + '@react-navigation/bottom-tabs': 7.15.10(@react-navigation/native@7.2.2(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0))(react-native-safe-area-context@5.6.2(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0))(react-native-screens@4.23.0(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0))(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0) + '@react-navigation/native': 7.2.2(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0) + '@react-navigation/native-stack': 7.14.12(@react-navigation/native@7.2.2(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0))(react-native-safe-area-context@5.6.2(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0))(react-native-screens@4.23.0(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0))(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0) + client-only: 0.0.1 + debug: 4.4.3 + escape-string-regexp: 4.0.0 + expo: 55.0.17(@babel/core@7.29.0)(@expo/dom-webview@55.0.5)(@expo/metro-runtime@55.0.10)(expo-router@55.0.13)(react-dom@19.2.5(react@19.2.0))(react-native-webview@13.16.1(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0))(react-native-worklets@0.7.4(@babel/core@7.29.0)(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0))(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0)(typescript@5.9.3) + expo-constants: 55.0.15(expo@55.0.17)(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0)) + expo-glass-effect: 55.0.10(expo@55.0.17)(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0) + expo-image: 55.0.9(expo@55.0.17)(react-native-web@0.21.2(react-dom@19.2.5(react@19.2.0))(react@19.2.0))(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0) + expo-linking: 55.0.14(expo@55.0.17)(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0) + expo-server: 55.0.8 + expo-symbols: 55.0.7(expo-font@55.0.6)(expo@55.0.17)(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0) + fast-deep-equal: 3.1.3 + invariant: 2.2.4 + nanoid: 3.3.11 + query-string: 7.1.3 + react: 19.2.0 + react-fast-compare: 3.2.2 + react-native: 0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0) + react-native-is-edge-to-edge: 1.3.1(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0) + react-native-safe-area-context: 5.6.2(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0) + react-native-screens: 4.23.0(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0) + semver: 7.6.3 + server-only: 0.0.1 + sf-symbols-typescript: 2.2.0 + shallowequal: 1.1.0 + use-latest-callback: 0.2.6(react@19.2.0) + vaul: 1.1.2(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.0))(react@19.2.0) + optionalDependencies: + '@testing-library/react-native': 13.3.3(jest@29.7.0(@types/node@25.6.0))(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react-test-renderer@19.2.0(react@19.2.0))(react@19.2.0) + react-dom: 19.2.5(react@19.2.0) + react-native-gesture-handler: 2.30.1(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0) + react-native-reanimated: 4.2.1(react-native-worklets@0.7.4(@babel/core@7.29.0)(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0))(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0) + react-native-web: 0.21.2(react-dom@19.2.5(react@19.2.0))(react@19.2.0) + transitivePeerDependencies: + - '@react-native-masked-view/masked-view' + - '@types/react' + - '@types/react-dom' + - expo-font + - supports-color + + expo-server@55.0.8: {} + + expo-splash-screen@55.0.19(expo@55.0.17)(typescript@5.9.3): + dependencies: + '@expo/prebuild-config': 55.0.16(expo@55.0.17)(typescript@5.9.3) + expo: 55.0.17(@babel/core@7.29.0)(@expo/dom-webview@55.0.5)(@expo/metro-runtime@55.0.10)(expo-router@55.0.13)(react-dom@19.2.5(react@19.2.0))(react-native-webview@13.16.1(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0))(react-native-worklets@0.7.4(@babel/core@7.29.0)(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0))(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0)(typescript@5.9.3) + transitivePeerDependencies: + - supports-color + - typescript + + expo-status-bar@55.0.5(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0): + dependencies: + react: 19.2.0 + react-native: 0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0) + react-native-is-edge-to-edge: 1.3.1(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0) + + expo-symbols@55.0.7(expo-font@55.0.6)(expo@55.0.17)(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0): + dependencies: + '@expo-google-fonts/material-symbols': 0.4.34 + expo: 55.0.17(@babel/core@7.29.0)(@expo/dom-webview@55.0.5)(@expo/metro-runtime@55.0.10)(expo-router@55.0.13)(react-dom@19.2.5(react@19.2.0))(react-native-webview@13.16.1(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0))(react-native-worklets@0.7.4(@babel/core@7.29.0)(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0))(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0)(typescript@5.9.3) + expo-font: 55.0.6(expo@55.0.17)(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0) + react: 19.2.0 + react-native: 0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0) + sf-symbols-typescript: 2.2.0 + + expo@55.0.17(@babel/core@7.29.0)(@expo/dom-webview@55.0.5)(@expo/metro-runtime@55.0.10)(expo-router@55.0.13)(react-dom@19.2.5(react@19.2.0))(react-native-webview@13.16.1(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0))(react-native-worklets@0.7.4(@babel/core@7.29.0)(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0))(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0)(typescript@5.9.3): + dependencies: + '@babel/runtime': 7.29.2 + '@expo/cli': 55.0.26(@expo/dom-webview@55.0.5)(@expo/metro-runtime@55.0.10)(expo-constants@55.0.15)(expo-font@55.0.6)(expo-router@55.0.13)(expo@55.0.17)(react-dom@19.2.5(react@19.2.0))(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0)(typescript@5.9.3) + '@expo/config': 55.0.15(typescript@5.9.3) + '@expo/config-plugins': 55.0.8 + '@expo/devtools': 55.0.2(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0) + '@expo/fingerprint': 0.16.6 + '@expo/local-build-cache-provider': 55.0.11(typescript@5.9.3) + '@expo/log-box': 55.0.11(@expo/dom-webview@55.0.5)(expo@55.0.17)(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0) + '@expo/metro': 55.1.0 + '@expo/metro-config': 55.0.17(expo@55.0.17)(typescript@5.9.3) + '@expo/vector-icons': 15.1.1(expo-font@55.0.6)(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0) + '@ungap/structured-clone': 1.3.0 + babel-preset-expo: 55.0.18(@babel/core@7.29.0)(@babel/runtime@7.29.2)(expo@55.0.17)(react-refresh@0.14.2) + expo-asset: 55.0.16(expo@55.0.17)(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0)(typescript@5.9.3) + expo-constants: 55.0.15(expo@55.0.17)(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0)) + expo-file-system: 55.0.17(expo@55.0.17)(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0)) + expo-font: 55.0.6(expo@55.0.17)(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0) + expo-keep-awake: 55.0.6(expo@55.0.17)(react@19.2.0) + expo-modules-autolinking: 55.0.18(typescript@5.9.3) + expo-modules-core: 55.0.23(react-native-worklets@0.7.4(@babel/core@7.29.0)(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0))(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0) + pretty-format: 29.7.0 + react: 19.2.0 + react-native: 0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0) + react-refresh: 0.14.2 + whatwg-url-minimum: 0.1.1 + optionalDependencies: + '@expo/dom-webview': 55.0.5(expo@55.0.17)(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0) + '@expo/metro-runtime': 55.0.10(@expo/dom-webview@55.0.5)(expo@55.0.17)(react-dom@19.2.5(react@19.2.0))(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0) + react-native-webview: 13.16.1(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0) + transitivePeerDependencies: + - '@babel/core' + - bufferutil + - expo-router + - expo-widgets + - react-dom + - react-native-worklets + - react-server-dom-webpack + - supports-color + - typescript + - utf-8-validate + + exponential-backoff@3.1.3: {} + + fast-deep-equal@3.1.3: {} + + fast-diff@1.3.0: {} + + fast-json-stable-stringify@2.1.0: {} + + fast-levenshtein@2.0.6: {} + + fb-dotslash@0.5.8: {} + + fb-watchman@2.0.2: + dependencies: + bser: 2.1.1 + + fbjs-css-vars@1.0.2: {} + + fbjs@3.0.5: + dependencies: + cross-fetch: 3.2.0 + fbjs-css-vars: 1.0.2 + loose-envify: 1.4.0 + object-assign: 4.1.1 + promise: 7.3.1 + setimmediate: 1.0.5 + ua-parser-js: 1.0.41 + transitivePeerDependencies: + - encoding + + fdir@6.5.0(picomatch@4.0.4): + optionalDependencies: + picomatch: 4.0.4 + + fetch-nodeshim@0.4.10: {} + + file-entry-cache@8.0.0: + dependencies: + flat-cache: 4.0.1 + + fill-range@7.1.1: + dependencies: + to-regex-range: 5.0.1 + + filter-obj@1.1.0: {} + + finalhandler@1.1.2: + dependencies: + debug: 2.6.9 + encodeurl: 1.0.2 + escape-html: 1.0.3 + on-finished: 2.3.0 + parseurl: 1.3.3 + statuses: 1.5.0 + unpipe: 1.0.0 + transitivePeerDependencies: + - supports-color + + find-up@4.1.0: + dependencies: + locate-path: 5.0.0 + path-exists: 4.0.0 + + find-up@5.0.0: + dependencies: + locate-path: 6.0.0 + path-exists: 4.0.0 + + flat-cache@4.0.1: + dependencies: + flatted: 3.4.2 + keyv: 4.5.4 + + flatted@3.4.2: {} + + flow-enums-runtime@0.0.6: {} + + fontfaceobserver@2.3.0: {} + + for-each@0.3.5: + dependencies: + is-callable: 1.2.7 + + form-data@4.0.5: + dependencies: + asynckit: 0.4.0 + combined-stream: 1.0.8 + es-set-tostringtag: 2.1.0 + hasown: 2.0.3 + mime-types: 2.1.35 + + fresh@0.5.2: {} + + fs-readdir-recursive@1.1.0: {} + + fs.realpath@1.0.0: {} + + fsevents@2.3.3: + optional: true + + function-bind@1.1.2: {} + + function.prototype.name@1.1.8: + dependencies: + call-bind: 1.0.9 + call-bound: 1.0.4 + define-properties: 1.2.1 + functions-have-names: 1.2.3 + hasown: 2.0.3 + is-callable: 1.2.7 + + functions-have-names@1.2.3: {} + + generator-function@2.0.1: {} + + gensync@1.0.0-beta.2: {} + + get-caller-file@2.0.5: {} + + get-intrinsic@1.3.0: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-define-property: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + function-bind: 1.1.2 + get-proto: 1.0.1 + gopd: 1.2.0 + has-symbols: 1.1.0 + hasown: 2.0.3 + math-intrinsics: 1.1.0 + + get-nonce@1.0.1: {} + + get-package-type@0.1.0: {} + + get-proto@1.0.1: + dependencies: + dunder-proto: 1.0.1 + es-object-atoms: 1.1.1 + + get-stream@6.0.1: {} + + get-symbol-description@1.1.0: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + + get-tsconfig@4.14.0: + dependencies: + resolve-pkg-maps: 1.0.0 + + getenv@2.0.0: {} + + glob-parent@5.1.2: + dependencies: + is-glob: 4.0.3 + optional: true + + glob-parent@6.0.2: + dependencies: + is-glob: 4.0.3 + + glob@13.0.6: + dependencies: + minimatch: 10.2.5 + minipass: 7.1.3 + path-scurry: 2.0.2 + + glob@7.2.3: + dependencies: + fs.realpath: 1.0.0 + inflight: 1.0.6 + inherits: 2.0.4 + minimatch: 3.1.5 + once: 1.4.0 + path-is-absolute: 1.0.1 + + globals@14.0.0: {} + + globals@15.15.0: {} + + globals@16.5.0: {} + + globalthis@1.0.4: + dependencies: + define-properties: 1.2.1 + gopd: 1.2.0 + + globrex@0.1.2: {} + + gopd@1.2.0: {} + + graceful-fs@4.2.11: {} + + has-bigints@1.1.0: {} + + has-flag@3.0.0: {} + + has-flag@4.0.0: {} + + has-property-descriptors@1.0.2: + dependencies: + es-define-property: 1.0.1 + + has-proto@1.2.0: + dependencies: + dunder-proto: 1.0.1 + + has-symbols@1.1.0: {} + + has-tostringtag@1.0.2: + dependencies: + has-symbols: 1.1.0 + + hasown@2.0.3: + dependencies: + function-bind: 1.1.2 + + hermes-compiler@0.14.1: {} + + hermes-compiler@250829098.0.10: {} + + hermes-estree@0.32.0: {} + + hermes-estree@0.32.1: {} + + hermes-estree@0.33.3: {} + + hermes-estree@0.35.0: {} + + hermes-parser@0.32.0: + dependencies: + hermes-estree: 0.32.0 + + hermes-parser@0.32.1: + dependencies: + hermes-estree: 0.32.1 + + hermes-parser@0.33.3: + dependencies: + hermes-estree: 0.33.3 + + hermes-parser@0.35.0: + dependencies: + hermes-estree: 0.35.0 + + hoist-non-react-statics@3.3.2: + dependencies: + react-is: 16.13.1 + + hosted-git-info@7.0.2: + dependencies: + lru-cache: 10.4.3 + + html-encoding-sniffer@3.0.0: + dependencies: + whatwg-encoding: 2.0.0 + + html-escaper@2.0.2: {} + + http-errors@2.0.1: + dependencies: + depd: 2.0.0 + inherits: 2.0.4 + setprototypeof: 1.2.0 + statuses: 2.0.2 + toidentifier: 1.0.1 + + http-proxy-agent@5.0.0: + dependencies: + '@tootallnate/once': 2.0.0 + agent-base: 6.0.2 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + + https-proxy-agent@5.0.1: + dependencies: + agent-base: 6.0.2 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + + https-proxy-agent@7.0.6: + dependencies: + agent-base: 7.1.4 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + + human-signals@2.1.0: {} + + hyphenate-style-name@1.1.0: {} + + iconv-lite@0.6.3: + dependencies: + safer-buffer: 2.1.2 + + ignore@5.3.2: {} + + ignore@7.0.5: {} + + image-size@1.2.1: + dependencies: + queue: 6.0.2 + + import-fresh@3.3.1: + dependencies: + parent-module: 1.0.1 + resolve-from: 4.0.0 + + import-local@3.2.0: + dependencies: + pkg-dir: 4.2.0 + resolve-cwd: 3.0.0 + + imurmurhash@0.1.4: {} + + indent-string@4.0.0: {} + + inflight@1.0.6: + dependencies: + once: 1.4.0 + wrappy: 1.0.2 + + inherits@2.0.4: {} + + inline-style-prefixer@7.0.1: + dependencies: + css-in-js-utils: 3.1.0 + + internal-slot@1.1.0: + dependencies: + es-errors: 1.3.0 + hasown: 2.0.3 + side-channel: 1.1.0 + + invariant@2.2.4: + dependencies: + loose-envify: 1.4.0 + + is-array-buffer@3.0.5: + dependencies: + call-bind: 1.0.9 + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + + is-arrayish@0.2.1: {} + + is-arrayish@0.3.4: {} + + is-async-function@2.1.1: + dependencies: + async-function: 1.0.0 + call-bound: 1.0.4 + get-proto: 1.0.1 + has-tostringtag: 1.0.2 + safe-regex-test: 1.1.0 + + is-bigint@1.1.0: + dependencies: + has-bigints: 1.1.0 + + is-binary-path@2.1.0: + dependencies: + binary-extensions: 2.3.0 + optional: true + + is-boolean-object@1.2.2: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + + is-callable@1.2.7: {} + + is-core-module@2.16.1: + dependencies: + hasown: 2.0.3 + + is-data-view@1.0.2: + dependencies: + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + is-typed-array: 1.1.15 + + is-date-object@1.1.0: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + + is-docker@2.2.1: {} + + is-extglob@2.1.1: {} + + is-finalizationregistry@1.1.1: + dependencies: + call-bound: 1.0.4 + + is-fullwidth-code-point@3.0.0: {} + + is-generator-fn@2.1.0: {} + + is-generator-function@1.1.2: + dependencies: + call-bound: 1.0.4 + generator-function: 2.0.1 + get-proto: 1.0.1 + has-tostringtag: 1.0.2 + safe-regex-test: 1.1.0 + + is-glob@4.0.3: + dependencies: + is-extglob: 2.1.1 + + is-map@2.0.3: {} + + is-negative-zero@2.0.3: {} + + is-number-object@1.1.1: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + + is-number@7.0.0: {} + + is-plain-obj@2.1.0: {} + + is-potential-custom-element-name@1.0.1: {} + + is-regex@1.2.1: + dependencies: + call-bound: 1.0.4 + gopd: 1.2.0 + has-tostringtag: 1.0.2 + hasown: 2.0.3 + + is-set@2.0.3: {} + + is-shared-array-buffer@1.0.4: + dependencies: + call-bound: 1.0.4 + + is-stream@2.0.1: {} + + is-string@1.1.1: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + + is-symbol@1.1.1: + dependencies: + call-bound: 1.0.4 + has-symbols: 1.1.0 + safe-regex-test: 1.1.0 + + is-typed-array@1.1.15: + dependencies: + which-typed-array: 1.1.20 + + is-weakmap@2.0.2: {} + + is-weakref@1.1.1: + dependencies: + call-bound: 1.0.4 + + is-weakset@2.0.4: + dependencies: + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + + is-wsl@2.2.0: + dependencies: + is-docker: 2.2.1 + + isarray@2.0.5: {} + + isexe@2.0.0: {} + + istanbul-lib-coverage@3.2.2: {} + + istanbul-lib-instrument@5.2.1: + dependencies: + '@babel/core': 7.29.0 + '@babel/parser': 7.29.2 + '@istanbuljs/schema': 0.1.6 + istanbul-lib-coverage: 3.2.2 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + istanbul-lib-instrument@6.0.3: + dependencies: + '@babel/core': 7.29.0 + '@babel/parser': 7.29.2 + '@istanbuljs/schema': 0.1.6 + istanbul-lib-coverage: 3.2.2 + semver: 7.7.4 + transitivePeerDependencies: + - supports-color + + istanbul-lib-report@3.0.1: + dependencies: + istanbul-lib-coverage: 3.2.2 + make-dir: 4.0.0 + supports-color: 7.2.0 + + istanbul-lib-source-maps@4.0.1: + dependencies: + debug: 4.4.3 + istanbul-lib-coverage: 3.2.2 + source-map: 0.6.1 + transitivePeerDependencies: + - supports-color + + istanbul-reports@3.2.0: + dependencies: + html-escaper: 2.0.2 + istanbul-lib-report: 3.0.1 + + iterator.prototype@1.1.5: + dependencies: + define-data-property: 1.1.4 + es-object-atoms: 1.1.1 + get-intrinsic: 1.3.0 + get-proto: 1.0.1 + has-symbols: 1.1.0 + set-function-name: 2.0.2 + + jest-changed-files@29.7.0: + dependencies: + execa: 5.1.1 + jest-util: 29.7.0 + p-limit: 3.1.0 + + jest-circus@29.7.0: + dependencies: + '@jest/environment': 29.7.0 + '@jest/expect': 29.7.0 + '@jest/test-result': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 25.6.0 + chalk: 4.1.2 + co: 4.6.0 + dedent: 1.7.2 + is-generator-fn: 2.1.0 + jest-each: 29.7.0 + jest-matcher-utils: 29.7.0 + jest-message-util: 29.7.0 + jest-runtime: 29.7.0 + jest-snapshot: 29.7.0 + jest-util: 29.7.0 + p-limit: 3.1.0 + pretty-format: 29.7.0 + pure-rand: 6.1.0 + slash: 3.0.0 + stack-utils: 2.0.6 + transitivePeerDependencies: + - babel-plugin-macros + - supports-color + + jest-cli@29.7.0(@types/node@25.6.0): + dependencies: + '@jest/core': 29.7.0 + '@jest/test-result': 29.7.0 + '@jest/types': 29.6.3 + chalk: 4.1.2 + create-jest: 29.7.0(@types/node@25.6.0) + exit: 0.1.2 + import-local: 3.2.0 + jest-config: 29.7.0(@types/node@25.6.0) + jest-util: 29.7.0 + jest-validate: 29.7.0 + yargs: 17.7.2 + transitivePeerDependencies: + - '@types/node' + - babel-plugin-macros + - supports-color + - ts-node + + jest-config@29.7.0(@types/node@25.6.0): + dependencies: + '@babel/core': 7.29.0 + '@jest/test-sequencer': 29.7.0 + '@jest/types': 29.6.3 + babel-jest: 29.7.0(@babel/core@7.29.0) + chalk: 4.1.2 + ci-info: 3.9.0 + deepmerge: 4.3.1 + glob: 7.2.3 + graceful-fs: 4.2.11 + jest-circus: 29.7.0 + jest-environment-node: 29.7.0 + jest-get-type: 29.6.3 + jest-regex-util: 29.6.3 + jest-resolve: 29.7.0 + jest-runner: 29.7.0 + jest-util: 29.7.0 + jest-validate: 29.7.0 + micromatch: 4.0.8 + parse-json: 5.2.0 + pretty-format: 29.7.0 + slash: 3.0.0 + strip-json-comments: 3.1.1 + optionalDependencies: + '@types/node': 25.6.0 + transitivePeerDependencies: + - babel-plugin-macros + - supports-color + + jest-diff@29.7.0: + dependencies: + chalk: 4.1.2 + diff-sequences: 29.6.3 + jest-get-type: 29.6.3 + pretty-format: 29.7.0 + + jest-diff@30.3.0: + dependencies: + '@jest/diff-sequences': 30.3.0 + '@jest/get-type': 30.1.0 + chalk: 4.1.2 + pretty-format: 30.3.0 + + jest-docblock@29.7.0: + dependencies: + detect-newline: 3.1.0 + + jest-each@29.7.0: + dependencies: + '@jest/types': 29.6.3 + chalk: 4.1.2 + jest-get-type: 29.6.3 + jest-util: 29.7.0 + pretty-format: 29.7.0 + + jest-environment-jsdom@29.7.0: + dependencies: + '@jest/environment': 29.7.0 + '@jest/fake-timers': 29.7.0 + '@jest/types': 29.6.3 + '@types/jsdom': 20.0.1 + '@types/node': 25.6.0 + jest-mock: 29.7.0 + jest-util: 29.7.0 + jsdom: 20.0.3 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + + jest-environment-node@29.7.0: + dependencies: + '@jest/environment': 29.7.0 + '@jest/fake-timers': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 25.6.0 + jest-mock: 29.7.0 + jest-util: 29.7.0 + + jest-expo@55.0.16(@babel/core@7.29.0)(expo@55.0.17)(jest@29.7.0(@types/node@25.6.0))(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0)(typescript@5.9.3): + dependencies: + '@expo/config': 55.0.15(typescript@5.9.3) + '@expo/json-file': 10.0.13 + '@jest/create-cache-key-function': 29.7.0 + '@jest/globals': 29.7.0 + babel-jest: 29.7.0(@babel/core@7.29.0) + expo: 55.0.17(@babel/core@7.29.0)(@expo/dom-webview@55.0.5)(@expo/metro-runtime@55.0.10)(expo-router@55.0.13)(react-dom@19.2.5(react@19.2.0))(react-native-webview@13.16.1(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0))(react-native-worklets@0.7.4(@babel/core@7.29.0)(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0))(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0)(typescript@5.9.3) + jest-environment-jsdom: 29.7.0 + jest-snapshot: 29.7.0 + jest-watch-select-projects: 2.0.0 + jest-watch-typeahead: 2.2.1(jest@29.7.0(@types/node@25.6.0)) + json5: 2.2.3 + lodash: 4.18.1 + react-native: 0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0) + react-test-renderer: 19.2.0(react@19.2.0) + server-only: 0.0.1 + stacktrace-js: 2.0.2 + transitivePeerDependencies: + - '@babel/core' + - bufferutil + - canvas + - jest + - react + - supports-color + - typescript + - utf-8-validate + + jest-get-type@29.6.3: {} + + jest-haste-map@29.7.0: + dependencies: + '@jest/types': 29.6.3 + '@types/graceful-fs': 4.1.9 + '@types/node': 25.6.0 + anymatch: 3.1.3 + fb-watchman: 2.0.2 + graceful-fs: 4.2.11 + jest-regex-util: 29.6.3 + jest-util: 29.7.0 + jest-worker: 29.7.0 + micromatch: 4.0.8 + walker: 1.0.8 + optionalDependencies: + fsevents: 2.3.3 + + jest-leak-detector@29.7.0: + dependencies: + jest-get-type: 29.6.3 + pretty-format: 29.7.0 + + jest-matcher-utils@29.7.0: + dependencies: + chalk: 4.1.2 + jest-diff: 29.7.0 + jest-get-type: 29.6.3 + pretty-format: 29.7.0 + + jest-matcher-utils@30.3.0: + dependencies: + '@jest/get-type': 30.1.0 + chalk: 4.1.2 + jest-diff: 30.3.0 + pretty-format: 30.3.0 + + jest-message-util@29.7.0: + dependencies: + '@babel/code-frame': 7.29.0 + '@jest/types': 29.6.3 + '@types/stack-utils': 2.0.3 + chalk: 4.1.2 + graceful-fs: 4.2.11 + micromatch: 4.0.8 + pretty-format: 29.7.0 + slash: 3.0.0 + stack-utils: 2.0.6 + + jest-mock@29.7.0: + dependencies: + '@jest/types': 29.6.3 + '@types/node': 25.6.0 + jest-util: 29.7.0 + + jest-pnp-resolver@1.2.3(jest-resolve@29.7.0): + optionalDependencies: + jest-resolve: 29.7.0 + + jest-regex-util@29.6.3: {} + + jest-resolve-dependencies@29.7.0: + dependencies: + jest-regex-util: 29.6.3 + jest-snapshot: 29.7.0 + transitivePeerDependencies: + - supports-color + + jest-resolve@29.7.0: + dependencies: + chalk: 4.1.2 + graceful-fs: 4.2.11 + jest-haste-map: 29.7.0 + jest-pnp-resolver: 1.2.3(jest-resolve@29.7.0) + jest-util: 29.7.0 + jest-validate: 29.7.0 + resolve: 1.22.12 + resolve.exports: 2.0.3 + slash: 3.0.0 + + jest-runner@29.7.0: + dependencies: + '@jest/console': 29.7.0 + '@jest/environment': 29.7.0 + '@jest/test-result': 29.7.0 + '@jest/transform': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 25.6.0 + chalk: 4.1.2 + emittery: 0.13.1 + graceful-fs: 4.2.11 + jest-docblock: 29.7.0 + jest-environment-node: 29.7.0 + jest-haste-map: 29.7.0 + jest-leak-detector: 29.7.0 + jest-message-util: 29.7.0 + jest-resolve: 29.7.0 + jest-runtime: 29.7.0 + jest-util: 29.7.0 + jest-watcher: 29.7.0 + jest-worker: 29.7.0 + p-limit: 3.1.0 + source-map-support: 0.5.13 + transitivePeerDependencies: + - supports-color + + jest-runtime@29.7.0: + dependencies: + '@jest/environment': 29.7.0 + '@jest/fake-timers': 29.7.0 + '@jest/globals': 29.7.0 + '@jest/source-map': 29.6.3 + '@jest/test-result': 29.7.0 + '@jest/transform': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 25.6.0 + chalk: 4.1.2 + cjs-module-lexer: 1.4.3 + collect-v8-coverage: 1.0.3 + glob: 7.2.3 + graceful-fs: 4.2.11 + jest-haste-map: 29.7.0 + jest-message-util: 29.7.0 + jest-mock: 29.7.0 + jest-regex-util: 29.6.3 + jest-resolve: 29.7.0 + jest-snapshot: 29.7.0 + jest-util: 29.7.0 + slash: 3.0.0 + strip-bom: 4.0.0 + transitivePeerDependencies: + - supports-color + + jest-snapshot@29.7.0: + dependencies: + '@babel/core': 7.29.0 + '@babel/generator': 7.29.1 + '@babel/plugin-syntax-jsx': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-syntax-typescript': 7.28.6(@babel/core@7.29.0) + '@babel/types': 7.29.0 + '@jest/expect-utils': 29.7.0 + '@jest/transform': 29.7.0 + '@jest/types': 29.6.3 + babel-preset-current-node-syntax: 1.2.0(@babel/core@7.29.0) + chalk: 4.1.2 + expect: 29.7.0 + graceful-fs: 4.2.11 + jest-diff: 29.7.0 + jest-get-type: 29.6.3 + jest-matcher-utils: 29.7.0 + jest-message-util: 29.7.0 + jest-util: 29.7.0 + natural-compare: 1.4.0 + pretty-format: 29.7.0 + semver: 7.7.4 + transitivePeerDependencies: + - supports-color + + jest-util@29.7.0: + dependencies: + '@jest/types': 29.6.3 + '@types/node': 25.6.0 + chalk: 4.1.2 + ci-info: 3.9.0 + graceful-fs: 4.2.11 + picomatch: 2.3.2 + + jest-validate@29.7.0: + dependencies: + '@jest/types': 29.6.3 + camelcase: 6.3.0 + chalk: 4.1.2 + jest-get-type: 29.6.3 + leven: 3.1.0 + pretty-format: 29.7.0 + + jest-watch-select-projects@2.0.0: + dependencies: + ansi-escapes: 4.3.2 + chalk: 3.0.0 + prompts: 2.4.2 + + jest-watch-typeahead@2.2.1(jest@29.7.0(@types/node@25.6.0)): + dependencies: + ansi-escapes: 6.2.1 + chalk: 4.1.2 + jest: 29.7.0(@types/node@25.6.0) + jest-regex-util: 29.6.3 + jest-watcher: 29.7.0 + slash: 5.1.0 + string-length: 5.0.1 + strip-ansi: 7.2.0 + + jest-watcher@29.7.0: + dependencies: + '@jest/test-result': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 25.6.0 + ansi-escapes: 4.3.2 + chalk: 4.1.2 + emittery: 0.13.1 + jest-util: 29.7.0 + string-length: 4.0.2 + + jest-worker@29.7.0: + dependencies: + '@types/node': 25.6.0 + jest-util: 29.7.0 + merge-stream: 2.0.0 + supports-color: 8.1.1 + + jest@29.7.0(@types/node@25.6.0): + dependencies: + '@jest/core': 29.7.0 + '@jest/types': 29.6.3 + import-local: 3.2.0 + jest-cli: 29.7.0(@types/node@25.6.0) + transitivePeerDependencies: + - '@types/node' + - babel-plugin-macros + - supports-color + - ts-node + + jimp-compact@0.16.1: {} + + js-tokens@4.0.0: {} + + js-yaml@3.14.2: + dependencies: + argparse: 1.0.10 + esprima: 4.0.1 + + js-yaml@4.1.1: + dependencies: + argparse: 2.0.1 + + jsc-safe-url@0.2.4: {} + + jsdom@20.0.3: + dependencies: + abab: 2.0.6 + acorn: 8.16.0 + acorn-globals: 7.0.1 + cssom: 0.5.0 + cssstyle: 2.3.0 + data-urls: 3.0.2 + decimal.js: 10.6.0 + domexception: 4.0.0 + escodegen: 2.1.0 + form-data: 4.0.5 + html-encoding-sniffer: 3.0.0 + http-proxy-agent: 5.0.0 + https-proxy-agent: 5.0.1 + is-potential-custom-element-name: 1.0.1 + nwsapi: 2.2.23 + parse5: 7.3.0 + saxes: 6.0.0 + symbol-tree: 3.2.4 + tough-cookie: 4.1.4 + w3c-xmlserializer: 4.0.0 + webidl-conversions: 7.0.0 + whatwg-encoding: 2.0.0 + whatwg-mimetype: 3.0.0 + whatwg-url: 11.0.0 + ws: 8.20.0 + xml-name-validator: 4.0.0 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + + jsesc@3.1.0: {} + + json-buffer@3.0.1: {} + + json-parse-even-better-errors@2.3.1: {} + + json-schema-traverse@0.4.1: {} + + json-stable-stringify-without-jsonify@1.0.1: {} + + json5@1.0.2: + dependencies: + minimist: 1.2.8 + + json5@2.2.3: {} + + jsx-ast-utils@3.3.5: + dependencies: + array-includes: 3.1.9 + array.prototype.flat: 1.3.3 + object.assign: 4.1.7 + object.values: 1.2.1 + + keyv@4.5.4: + dependencies: + json-buffer: 3.0.1 + + kleur@3.0.3: {} + + lan-network@0.2.1: {} + + leven@3.1.0: {} + + levn@0.4.1: + dependencies: + prelude-ls: 1.2.1 + type-check: 0.4.0 + + lighthouse-logger@1.4.2: + dependencies: + debug: 2.6.9 + marky: 1.3.0 + transitivePeerDependencies: + - supports-color + + lightningcss-android-arm64@1.32.0: + optional: true + + lightningcss-darwin-arm64@1.32.0: + optional: true + + lightningcss-darwin-x64@1.32.0: + optional: true + + lightningcss-freebsd-x64@1.32.0: + optional: true + + lightningcss-linux-arm-gnueabihf@1.32.0: + optional: true + + lightningcss-linux-arm64-gnu@1.32.0: + optional: true + + lightningcss-linux-arm64-musl@1.32.0: + optional: true + + lightningcss-linux-x64-gnu@1.32.0: + optional: true + + lightningcss-linux-x64-musl@1.32.0: + optional: true + + lightningcss-win32-arm64-msvc@1.32.0: + optional: true + + lightningcss-win32-x64-msvc@1.32.0: + optional: true + + lightningcss@1.32.0: + dependencies: + detect-libc: 2.1.2 + optionalDependencies: + lightningcss-android-arm64: 1.32.0 + lightningcss-darwin-arm64: 1.32.0 + lightningcss-darwin-x64: 1.32.0 + lightningcss-freebsd-x64: 1.32.0 + lightningcss-linux-arm-gnueabihf: 1.32.0 + lightningcss-linux-arm64-gnu: 1.32.0 + lightningcss-linux-arm64-musl: 1.32.0 + lightningcss-linux-x64-gnu: 1.32.0 + lightningcss-linux-x64-musl: 1.32.0 + lightningcss-win32-arm64-msvc: 1.32.0 + lightningcss-win32-x64-msvc: 1.32.0 + + lines-and-columns@1.2.4: {} + + locate-path@5.0.0: + dependencies: + p-locate: 4.1.0 + + locate-path@6.0.0: + dependencies: + p-locate: 5.0.0 + + lodash.debounce@4.0.8: {} + + lodash.memoize@4.1.2: {} + + lodash.merge@4.6.2: {} + + lodash.throttle@4.1.1: {} + + lodash@4.18.1: {} + + log-symbols@2.2.0: + dependencies: + chalk: 2.4.2 + + loose-envify@1.4.0: + dependencies: + js-tokens: 4.0.0 + + lru-cache@10.4.3: {} + + lru-cache@11.3.5: {} + + lru-cache@5.1.1: + dependencies: + yallist: 3.1.1 + + lucide-react-native@1.11.0(react-native-svg@15.15.4(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0))(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0): + dependencies: + react: 19.2.0 + react-native: 0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0) + react-native-svg: 15.15.4(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0) + + make-dir@2.1.0: + dependencies: + pify: 4.0.1 + semver: 5.7.2 + + make-dir@4.0.0: + dependencies: + semver: 7.7.4 + + make-error@1.3.6: {} + + makeerror@1.0.12: + dependencies: + tmpl: 1.0.5 + + marky@1.3.0: {} + + math-intrinsics@1.1.0: {} + + mdn-data@2.0.14: {} + + memoize-one@5.2.1: {} + + memoize-one@6.0.0: {} + + merge-options@3.0.4: + dependencies: + is-plain-obj: 2.1.0 + + merge-stream@2.0.0: {} + + metro-babel-transformer@0.83.6: + dependencies: + '@babel/core': 7.29.0 + flow-enums-runtime: 0.0.6 + hermes-parser: 0.35.0 + metro-cache-key: 0.83.6 + nullthrows: 1.1.1 + transitivePeerDependencies: + - supports-color + + metro-babel-transformer@0.84.3: + dependencies: + '@babel/core': 7.29.0 + flow-enums-runtime: 0.0.6 + hermes-parser: 0.35.0 + metro-cache-key: 0.84.3 + nullthrows: 1.1.1 + transitivePeerDependencies: + - supports-color + + metro-cache-key@0.83.6: + dependencies: + flow-enums-runtime: 0.0.6 + + metro-cache-key@0.84.3: + dependencies: + flow-enums-runtime: 0.0.6 + + metro-cache@0.83.6: + dependencies: + exponential-backoff: 3.1.3 + flow-enums-runtime: 0.0.6 + https-proxy-agent: 7.0.6 + metro-core: 0.83.6 + transitivePeerDependencies: + - supports-color + + metro-cache@0.84.3: + dependencies: + exponential-backoff: 3.1.3 + flow-enums-runtime: 0.0.6 + https-proxy-agent: 7.0.6 + metro-core: 0.84.3 + transitivePeerDependencies: + - supports-color + + metro-config@0.83.6: + dependencies: + connect: 3.7.0 + flow-enums-runtime: 0.0.6 + jest-validate: 29.7.0 + metro: 0.83.6 + metro-cache: 0.83.6 + metro-core: 0.83.6 + metro-runtime: 0.83.6 + yaml: 2.8.3 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + + metro-config@0.84.3: + dependencies: + connect: 3.7.0 + flow-enums-runtime: 0.0.6 + jest-validate: 29.7.0 + metro: 0.84.3 + metro-cache: 0.84.3 + metro-core: 0.84.3 + metro-runtime: 0.84.3 + yaml: 2.8.3 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + + metro-core@0.83.6: + dependencies: + flow-enums-runtime: 0.0.6 + lodash.throttle: 4.1.1 + metro-resolver: 0.83.6 + + metro-core@0.84.3: + dependencies: + flow-enums-runtime: 0.0.6 + lodash.throttle: 4.1.1 + metro-resolver: 0.84.3 + + metro-file-map@0.83.6: + dependencies: + debug: 4.4.3 + fb-watchman: 2.0.2 + flow-enums-runtime: 0.0.6 + graceful-fs: 4.2.11 + invariant: 2.2.4 + jest-worker: 29.7.0 + micromatch: 4.0.8 + nullthrows: 1.1.1 + walker: 1.0.8 + transitivePeerDependencies: + - supports-color + + metro-file-map@0.84.3: + dependencies: + debug: 4.4.3 + fb-watchman: 2.0.2 + flow-enums-runtime: 0.0.6 + graceful-fs: 4.2.11 + invariant: 2.2.4 + jest-worker: 29.7.0 + micromatch: 4.0.8 + nullthrows: 1.1.1 + walker: 1.0.8 + transitivePeerDependencies: + - supports-color + + metro-minify-terser@0.83.6: + dependencies: + flow-enums-runtime: 0.0.6 + terser: 5.46.2 + + metro-minify-terser@0.84.3: + dependencies: + flow-enums-runtime: 0.0.6 + terser: 5.46.2 + + metro-resolver@0.83.6: + dependencies: + flow-enums-runtime: 0.0.6 + + metro-resolver@0.84.3: + dependencies: + flow-enums-runtime: 0.0.6 + + metro-runtime@0.83.6: + dependencies: + '@babel/runtime': 7.29.2 + flow-enums-runtime: 0.0.6 + + metro-runtime@0.84.3: + dependencies: + '@babel/runtime': 7.29.2 + flow-enums-runtime: 0.0.6 + + metro-source-map@0.83.6: + dependencies: + '@babel/traverse': 7.29.0 + '@babel/types': 7.29.0 + flow-enums-runtime: 0.0.6 + invariant: 2.2.4 + metro-symbolicate: 0.83.6 + nullthrows: 1.1.1 + ob1: 0.83.6 + source-map: 0.5.7 + vlq: 1.0.1 + transitivePeerDependencies: + - supports-color + + metro-source-map@0.84.3: + dependencies: + '@babel/traverse': 7.29.0 + '@babel/types': 7.29.0 + flow-enums-runtime: 0.0.6 + invariant: 2.2.4 + metro-symbolicate: 0.84.3 + nullthrows: 1.1.1 + ob1: 0.84.3 + source-map: 0.5.7 + vlq: 1.0.1 + transitivePeerDependencies: + - supports-color + + metro-symbolicate@0.83.6: + dependencies: + flow-enums-runtime: 0.0.6 + invariant: 2.2.4 + metro-source-map: 0.83.6 + nullthrows: 1.1.1 + source-map: 0.5.7 + vlq: 1.0.1 + transitivePeerDependencies: + - supports-color + + metro-symbolicate@0.84.3: + dependencies: + flow-enums-runtime: 0.0.6 + invariant: 2.2.4 + metro-source-map: 0.84.3 + nullthrows: 1.1.1 + source-map: 0.5.7 + vlq: 1.0.1 + transitivePeerDependencies: + - supports-color + + metro-transform-plugins@0.83.6: + dependencies: + '@babel/core': 7.29.0 + '@babel/generator': 7.29.1 + '@babel/template': 7.28.6 + '@babel/traverse': 7.29.0 + flow-enums-runtime: 0.0.6 + nullthrows: 1.1.1 + transitivePeerDependencies: + - supports-color + + metro-transform-plugins@0.84.3: + dependencies: + '@babel/core': 7.29.0 + '@babel/generator': 7.29.1 + '@babel/template': 7.28.6 + '@babel/traverse': 7.29.0 + flow-enums-runtime: 0.0.6 + nullthrows: 1.1.1 + transitivePeerDependencies: + - supports-color + + metro-transform-worker@0.83.6: + dependencies: + '@babel/core': 7.29.0 + '@babel/generator': 7.29.1 + '@babel/parser': 7.29.2 + '@babel/types': 7.29.0 + flow-enums-runtime: 0.0.6 + metro: 0.83.6 + metro-babel-transformer: 0.83.6 + metro-cache: 0.83.6 + metro-cache-key: 0.83.6 + metro-minify-terser: 0.83.6 + metro-source-map: 0.83.6 + metro-transform-plugins: 0.83.6 + nullthrows: 1.1.1 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + + metro-transform-worker@0.84.3: + dependencies: + '@babel/core': 7.29.0 + '@babel/generator': 7.29.1 + '@babel/parser': 7.29.2 + '@babel/types': 7.29.0 + flow-enums-runtime: 0.0.6 + metro: 0.84.3 + metro-babel-transformer: 0.84.3 + metro-cache: 0.84.3 + metro-cache-key: 0.84.3 + metro-minify-terser: 0.84.3 + metro-source-map: 0.84.3 + metro-transform-plugins: 0.84.3 + nullthrows: 1.1.1 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + + metro@0.83.6: + dependencies: + '@babel/code-frame': 7.29.0 + '@babel/core': 7.29.0 + '@babel/generator': 7.29.1 + '@babel/parser': 7.29.2 + '@babel/template': 7.28.6 + '@babel/traverse': 7.29.0 + '@babel/types': 7.29.0 + accepts: 2.0.0 + chalk: 4.1.2 + ci-info: 2.0.0 + connect: 3.7.0 + debug: 4.4.3 + error-stack-parser: 2.1.4 + flow-enums-runtime: 0.0.6 + graceful-fs: 4.2.11 + hermes-parser: 0.35.0 + image-size: 1.2.1 + invariant: 2.2.4 + jest-worker: 29.7.0 + jsc-safe-url: 0.2.4 + lodash.throttle: 4.1.1 + metro-babel-transformer: 0.83.6 + metro-cache: 0.83.6 + metro-cache-key: 0.83.6 + metro-config: 0.83.6 + metro-core: 0.83.6 + metro-file-map: 0.83.6 + metro-resolver: 0.83.6 + metro-runtime: 0.83.6 + metro-source-map: 0.83.6 + metro-symbolicate: 0.83.6 + metro-transform-plugins: 0.83.6 + metro-transform-worker: 0.83.6 + mime-types: 3.0.2 + nullthrows: 1.1.1 + serialize-error: 2.1.0 + source-map: 0.5.7 + throat: 5.0.0 + ws: 7.5.10 + yargs: 17.7.2 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + + metro@0.84.3: + dependencies: + '@babel/code-frame': 7.29.0 + '@babel/core': 7.29.0 + '@babel/generator': 7.29.1 + '@babel/parser': 7.29.2 + '@babel/template': 7.28.6 + '@babel/traverse': 7.29.0 + '@babel/types': 7.29.0 + accepts: 2.0.0 + chalk: 4.1.2 + ci-info: 2.0.0 + connect: 3.7.0 + debug: 4.4.3 + error-stack-parser: 2.1.4 + flow-enums-runtime: 0.0.6 + graceful-fs: 4.2.11 + hermes-parser: 0.35.0 + image-size: 1.2.1 + invariant: 2.2.4 + jest-worker: 29.7.0 + jsc-safe-url: 0.2.4 + lodash.throttle: 4.1.1 + metro-babel-transformer: 0.84.3 + metro-cache: 0.84.3 + metro-cache-key: 0.84.3 + metro-config: 0.84.3 + metro-core: 0.84.3 + metro-file-map: 0.84.3 + metro-resolver: 0.84.3 + metro-runtime: 0.84.3 + metro-source-map: 0.84.3 + metro-symbolicate: 0.84.3 + metro-transform-plugins: 0.84.3 + metro-transform-worker: 0.84.3 + mime-types: 3.0.2 + nullthrows: 1.1.1 + serialize-error: 2.1.0 + source-map: 0.5.7 + throat: 5.0.0 + ws: 7.5.10 + yargs: 17.7.2 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + + micromatch@4.0.8: + dependencies: + braces: 3.0.3 + picomatch: 2.3.2 + + mime-db@1.52.0: {} + + mime-db@1.54.0: {} + + mime-types@2.1.35: + dependencies: + mime-db: 1.52.0 + + mime-types@3.0.2: + dependencies: + mime-db: 1.54.0 + + mime@1.6.0: {} + + mimic-fn@1.2.0: {} + + mimic-fn@2.1.0: {} + + min-indent@1.0.1: {} + + minimatch@10.2.5: + dependencies: + brace-expansion: 5.0.5 + + minimatch@3.1.5: + dependencies: + brace-expansion: 1.1.14 + + minimist@1.2.8: {} + + minipass@7.1.3: {} + + mkdirp@1.0.4: {} + + ms@2.0.0: {} + + ms@2.1.3: {} + + multitars@1.0.0: {} + + nanoid@3.3.11: {} + + natural-compare@1.4.0: {} + + negotiator@0.6.3: {} + + negotiator@0.6.4: {} + + negotiator@1.0.0: {} + + node-exports-info@1.6.0: + dependencies: + array.prototype.flatmap: 1.3.3 + es-errors: 1.3.0 + object.entries: 1.1.9 + semver: 6.3.1 + + node-fetch@2.7.0: + dependencies: + whatwg-url: 5.0.0 + + node-forge@1.4.0: {} + + node-int64@0.4.0: {} + + node-releases@2.0.38: {} + + normalize-path@3.0.0: {} + + npm-package-arg@11.0.3: + dependencies: + hosted-git-info: 7.0.2 + proc-log: 4.2.0 + semver: 7.7.4 + validate-npm-package-name: 5.0.1 + + npm-run-path@4.0.1: + dependencies: + path-key: 3.1.1 + + nth-check@2.1.1: + dependencies: + boolbase: 1.0.0 + + nullthrows@1.1.1: {} + + nwsapi@2.2.23: {} + + ob1@0.83.6: + dependencies: + flow-enums-runtime: 0.0.6 + + ob1@0.84.3: + dependencies: + flow-enums-runtime: 0.0.6 + + object-assign@4.1.1: {} + + object-inspect@1.13.4: {} + + object-keys@1.1.1: {} + + object.assign@4.1.7: + dependencies: + call-bind: 1.0.9 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-object-atoms: 1.1.1 + has-symbols: 1.1.0 + object-keys: 1.1.1 + + object.entries@1.1.9: + dependencies: + call-bind: 1.0.9 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-object-atoms: 1.1.1 + + object.fromentries@2.0.8: + dependencies: + call-bind: 1.0.9 + define-properties: 1.2.1 + es-abstract: 1.24.2 + es-object-atoms: 1.1.1 + + object.groupby@1.0.3: + dependencies: + call-bind: 1.0.9 + define-properties: 1.2.1 + es-abstract: 1.24.2 + + object.values@1.2.1: + dependencies: + call-bind: 1.0.9 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-object-atoms: 1.1.1 + + on-finished@2.3.0: + dependencies: + ee-first: 1.1.1 + + on-finished@2.4.1: + dependencies: + ee-first: 1.1.1 + + on-headers@1.1.0: {} + + once@1.4.0: + dependencies: + wrappy: 1.0.2 + + onetime@2.0.1: + dependencies: + mimic-fn: 1.2.0 + + onetime@5.1.2: + dependencies: + mimic-fn: 2.1.0 + + open@7.4.2: + dependencies: + is-docker: 2.2.1 + is-wsl: 2.2.0 + + open@8.4.2: + dependencies: + define-lazy-prop: 2.0.0 + is-docker: 2.2.1 + is-wsl: 2.2.0 + + optionator@0.9.4: + dependencies: + deep-is: 0.1.4 + fast-levenshtein: 2.0.6 + levn: 0.4.1 + prelude-ls: 1.2.1 + type-check: 0.4.0 + word-wrap: 1.2.5 + + ora@3.4.0: + dependencies: + chalk: 2.4.2 + cli-cursor: 2.1.0 + cli-spinners: 2.9.2 + log-symbols: 2.2.0 + strip-ansi: 5.2.0 + wcwidth: 1.0.1 + + own-keys@1.0.1: + dependencies: + get-intrinsic: 1.3.0 + object-keys: 1.1.1 + safe-push-apply: 1.0.0 + + oxfmt@0.47.0: + dependencies: + tinypool: 2.1.0 + optionalDependencies: + '@oxfmt/binding-android-arm-eabi': 0.47.0 + '@oxfmt/binding-android-arm64': 0.47.0 + '@oxfmt/binding-darwin-arm64': 0.47.0 + '@oxfmt/binding-darwin-x64': 0.47.0 + '@oxfmt/binding-freebsd-x64': 0.47.0 + '@oxfmt/binding-linux-arm-gnueabihf': 0.47.0 + '@oxfmt/binding-linux-arm-musleabihf': 0.47.0 + '@oxfmt/binding-linux-arm64-gnu': 0.47.0 + '@oxfmt/binding-linux-arm64-musl': 0.47.0 + '@oxfmt/binding-linux-ppc64-gnu': 0.47.0 + '@oxfmt/binding-linux-riscv64-gnu': 0.47.0 + '@oxfmt/binding-linux-riscv64-musl': 0.47.0 + '@oxfmt/binding-linux-s390x-gnu': 0.47.0 + '@oxfmt/binding-linux-x64-gnu': 0.47.0 + '@oxfmt/binding-linux-x64-musl': 0.47.0 + '@oxfmt/binding-openharmony-arm64': 0.47.0 + '@oxfmt/binding-win32-arm64-msvc': 0.47.0 + '@oxfmt/binding-win32-ia32-msvc': 0.47.0 + '@oxfmt/binding-win32-x64-msvc': 0.47.0 + + oxlint@1.62.0: + optionalDependencies: + '@oxlint/binding-android-arm-eabi': 1.62.0 + '@oxlint/binding-android-arm64': 1.62.0 + '@oxlint/binding-darwin-arm64': 1.62.0 + '@oxlint/binding-darwin-x64': 1.62.0 + '@oxlint/binding-freebsd-x64': 1.62.0 + '@oxlint/binding-linux-arm-gnueabihf': 1.62.0 + '@oxlint/binding-linux-arm-musleabihf': 1.62.0 + '@oxlint/binding-linux-arm64-gnu': 1.62.0 + '@oxlint/binding-linux-arm64-musl': 1.62.0 + '@oxlint/binding-linux-ppc64-gnu': 1.62.0 + '@oxlint/binding-linux-riscv64-gnu': 1.62.0 + '@oxlint/binding-linux-riscv64-musl': 1.62.0 + '@oxlint/binding-linux-s390x-gnu': 1.62.0 + '@oxlint/binding-linux-x64-gnu': 1.62.0 + '@oxlint/binding-linux-x64-musl': 1.62.0 + '@oxlint/binding-openharmony-arm64': 1.62.0 + '@oxlint/binding-win32-arm64-msvc': 1.62.0 + '@oxlint/binding-win32-ia32-msvc': 1.62.0 + '@oxlint/binding-win32-x64-msvc': 1.62.0 + + p-limit@2.3.0: + dependencies: + p-try: 2.2.0 + + p-limit@3.1.0: + dependencies: + yocto-queue: 0.1.0 + + p-locate@4.1.0: + dependencies: + p-limit: 2.3.0 + + p-locate@5.0.0: + dependencies: + p-limit: 3.1.0 + + p-try@2.2.0: {} + + parent-module@1.0.1: + dependencies: + callsites: 3.1.0 + + parse-json@5.2.0: + dependencies: + '@babel/code-frame': 7.29.0 + error-ex: 1.3.4 + json-parse-even-better-errors: 2.3.1 + lines-and-columns: 1.2.4 + + parse-png@2.1.0: + dependencies: + pngjs: 3.4.0 + + parse5@7.3.0: + dependencies: + entities: 6.0.1 + + parseurl@1.3.3: {} + + path-exists@4.0.0: {} + + path-is-absolute@1.0.1: {} + + path-key@3.1.1: {} + + path-parse@1.0.7: {} + + path-scurry@2.0.2: + dependencies: + lru-cache: 11.3.5 + minipass: 7.1.3 + + picocolors@1.1.1: {} + + picomatch@2.3.2: {} + + picomatch@4.0.4: {} + + pify@4.0.1: {} + + pirates@4.0.7: {} + + pkg-dir@4.2.0: + dependencies: + find-up: 4.1.0 + + plist@3.1.1: + dependencies: + '@xmldom/xmldom': 0.9.10 + base64-js: 1.5.1 + xmlbuilder: 15.1.1 + + pngjs@3.4.0: {} + + possible-typed-array-names@1.1.0: {} + + postcss-value-parser@4.2.0: {} + + postcss@8.4.49: + dependencies: + nanoid: 3.3.11 + picocolors: 1.1.1 + source-map-js: 1.2.1 + + prelude-ls@1.2.1: {} + + prettier-linter-helpers@1.0.1: + dependencies: + fast-diff: 1.3.0 + + prettier@2.8.8: {} + + pretty-format@29.7.0: + dependencies: + '@jest/schemas': 29.6.3 + ansi-styles: 5.2.0 + react-is: 18.3.1 + + pretty-format@30.3.0: + dependencies: + '@jest/schemas': 30.0.5 + ansi-styles: 5.2.0 + react-is: 18.3.1 + + proc-log@4.2.0: {} + + progress@2.0.3: {} + + promise@7.3.1: + dependencies: + asap: 2.0.6 + + promise@8.3.0: + dependencies: + asap: 2.0.6 + + prompts@2.4.2: + dependencies: + kleur: 3.0.3 + sisteransi: 1.0.5 + + prop-types@15.8.1: + dependencies: + loose-envify: 1.4.0 + object-assign: 4.1.1 + react-is: 16.13.1 + + psl@1.15.0: + dependencies: + punycode: 2.3.1 + + punycode@2.3.1: {} + + pure-rand@6.1.0: {} + + query-string@7.1.3: + dependencies: + decode-uri-component: 0.2.2 + filter-obj: 1.1.0 + split-on-first: 1.1.0 + strict-uri-encode: 2.0.0 + + querystringify@2.2.0: {} + + queue@6.0.2: + dependencies: + inherits: 2.0.4 + + range-parser@1.2.1: {} + + react-devtools-core@6.1.5: + dependencies: + shell-quote: 1.8.3 + ws: 7.5.10 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + + react-dom@19.2.5(react@19.2.0): + dependencies: + react: 19.2.0 + scheduler: 0.27.0 + + react-fast-compare@3.2.2: {} + + react-freeze@1.0.4(react@19.2.0): + dependencies: + react: 19.2.0 + + react-is@16.13.1: {} + + react-is@18.3.1: {} + + react-is@19.2.5: {} + + react-native-gesture-handler@2.30.1(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0): + dependencies: + '@egjs/hammerjs': 2.0.17 + hoist-non-react-statics: 3.3.2 + invariant: 2.2.4 + react: 19.2.0 + react-native: 0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0) + + react-native-is-edge-to-edge@1.2.1(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0): + dependencies: + react: 19.2.0 + react-native: 0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0) + + react-native-is-edge-to-edge@1.3.1(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0): + dependencies: + react: 19.2.0 + react-native: 0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0) + + react-native-reanimated@4.2.1(react-native-worklets@0.7.4(@babel/core@7.29.0)(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0))(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0): + dependencies: + react: 19.2.0 + react-native: 0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0) + react-native-is-edge-to-edge: 1.2.1(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0) + react-native-worklets: 0.7.4(@babel/core@7.29.0)(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0) + semver: 7.7.3 + + react-native-safe-area-context@5.6.2(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0): + dependencies: + react: 19.2.0 + react-native: 0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0) + + react-native-screens@4.23.0(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0): + dependencies: + react: 19.2.0 + react-freeze: 1.0.4(react@19.2.0) + react-native: 0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0) + warn-once: 0.1.1 + + react-native-svg@15.15.4(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0): + dependencies: + css-select: 5.2.2 + css-tree: 1.1.3 + react: 19.2.0 + react-native: 0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0) + warn-once: 0.1.1 + + react-native-web@0.21.2(react-dom@19.2.5(react@19.2.0))(react@19.2.0): + dependencies: + '@babel/runtime': 7.29.2 + '@react-native/normalize-colors': 0.74.89 + fbjs: 3.0.5 + inline-style-prefixer: 7.0.1 + memoize-one: 6.0.0 + nullthrows: 1.1.1 + postcss-value-parser: 4.2.0 + react: 19.2.0 + react-dom: 19.2.5(react@19.2.0) + styleq: 0.1.3 + transitivePeerDependencies: + - encoding + + react-native-webview@13.16.1(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0): + dependencies: + escape-string-regexp: 4.0.0 + invariant: 2.2.4 + react: 19.2.0 + react-native: 0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0) + + react-native-worklets@0.7.4(@babel/core@7.29.0)(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0): + dependencies: + '@babel/core': 7.29.0 + '@babel/plugin-transform-arrow-functions': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-class-properties': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-classes': 7.28.4(@babel/core@7.29.0) + '@babel/plugin-transform-nullish-coalescing-operator': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-optional-chaining': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-shorthand-properties': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-template-literals': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-unicode-regex': 7.27.1(@babel/core@7.29.0) + '@babel/preset-typescript': 7.27.1(@babel/core@7.29.0) + convert-source-map: 2.0.0 + react: 19.2.0 + react-native: 0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0) + semver: 7.7.3 + transitivePeerDependencies: + - supports-color + + react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0): + dependencies: + '@jest/create-cache-key-function': 29.7.0 + '@react-native/assets-registry': 0.83.6 + '@react-native/codegen': 0.83.6(@babel/core@7.29.0) + '@react-native/community-cli-plugin': 0.83.6(@react-native/metro-config@0.85.2(@babel/core@7.29.0)) + '@react-native/gradle-plugin': 0.83.6 + '@react-native/js-polyfills': 0.83.6 + '@react-native/normalize-colors': 0.83.6 + '@react-native/virtualized-lists': 0.83.6(@types/react@19.2.14)(react-native@0.83.6(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0) + abort-controller: 3.0.0 + anser: 1.4.10 + ansi-regex: 5.0.1 + babel-jest: 29.7.0(@babel/core@7.29.0) + babel-plugin-syntax-hermes-parser: 0.32.0 + base64-js: 1.5.1 + commander: 12.1.0 + flow-enums-runtime: 0.0.6 + glob: 7.2.3 + hermes-compiler: 0.14.1 + invariant: 2.2.4 + jest-environment-node: 29.7.0 + memoize-one: 5.2.1 + metro-runtime: 0.83.6 + metro-source-map: 0.83.6 + nullthrows: 1.1.1 + pretty-format: 29.7.0 + promise: 8.3.0 + react: 19.2.0 + react-devtools-core: 6.1.5 + react-refresh: 0.14.2 + regenerator-runtime: 0.13.11 + scheduler: 0.27.0 + semver: 7.7.4 + stacktrace-parser: 0.1.11 + whatwg-fetch: 3.6.20 + ws: 7.5.10 + yargs: 17.7.2 + optionalDependencies: + '@types/react': 19.2.14 + transitivePeerDependencies: + - '@babel/core' + - '@react-native-community/cli' + - '@react-native/metro-config' + - bufferutil + - supports-color + - utf-8-validate + + react-native@0.85.2(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0): + dependencies: + '@react-native/assets-registry': 0.85.2 + '@react-native/codegen': 0.85.2(@babel/core@7.29.0) + '@react-native/community-cli-plugin': 0.85.2(@react-native/metro-config@0.85.2(@babel/core@7.29.0)) + '@react-native/gradle-plugin': 0.85.2 + '@react-native/js-polyfills': 0.85.2 + '@react-native/normalize-colors': 0.85.2 + '@react-native/virtualized-lists': 0.85.2(@types/react@19.2.14)(react-native@0.85.2(@babel/core@7.29.0)(@react-native/metro-config@0.85.2(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.0))(react@19.2.0) + abort-controller: 3.0.0 + anser: 1.4.10 + ansi-regex: 5.0.1 + babel-plugin-syntax-hermes-parser: 0.33.3 + base64-js: 1.5.1 + commander: 12.1.0 + flow-enums-runtime: 0.0.6 + hermes-compiler: 250829098.0.10 + invariant: 2.2.4 + memoize-one: 5.2.1 + metro-runtime: 0.84.3 + metro-source-map: 0.84.3 + nullthrows: 1.1.1 + pretty-format: 29.7.0 + promise: 8.3.0 + react: 19.2.0 + react-devtools-core: 6.1.5 + react-refresh: 0.14.2 + regenerator-runtime: 0.13.11 + scheduler: 0.27.0 + semver: 7.7.4 + stacktrace-parser: 0.1.11 + tinyglobby: 0.2.16 + whatwg-fetch: 3.6.20 + ws: 7.5.10 + yargs: 17.7.2 + optionalDependencies: + '@types/react': 19.2.14 + transitivePeerDependencies: + - '@babel/core' + - '@react-native-community/cli' + - '@react-native/metro-config' + - bufferutil + - supports-color + - utf-8-validate + + react-refresh@0.14.2: {} + + react-remove-scroll-bar@2.3.8(@types/react@19.2.14)(react@19.2.0): + dependencies: + react: 19.2.0 + react-style-singleton: 2.2.3(@types/react@19.2.14)(react@19.2.0) + tslib: 2.8.1 + optionalDependencies: + '@types/react': 19.2.14 + + react-remove-scroll@2.7.2(@types/react@19.2.14)(react@19.2.0): + dependencies: + react: 19.2.0 + react-remove-scroll-bar: 2.3.8(@types/react@19.2.14)(react@19.2.0) + react-style-singleton: 2.2.3(@types/react@19.2.14)(react@19.2.0) + tslib: 2.8.1 + use-callback-ref: 1.3.3(@types/react@19.2.14)(react@19.2.0) + use-sidecar: 1.1.3(@types/react@19.2.14)(react@19.2.0) + optionalDependencies: + '@types/react': 19.2.14 + + react-style-singleton@2.2.3(@types/react@19.2.14)(react@19.2.0): + dependencies: + get-nonce: 1.0.1 + react: 19.2.0 + tslib: 2.8.1 + optionalDependencies: + '@types/react': 19.2.14 + + react-test-renderer@19.2.0(react@19.2.0): + dependencies: + react: 19.2.0 + react-is: 19.2.5 + scheduler: 0.27.0 + + react@19.2.0: {} + + readdirp@3.6.0: + dependencies: + picomatch: 2.3.2 + optional: true + + redent@3.0.0: + dependencies: + indent-string: 4.0.0 + strip-indent: 3.0.0 + + reflect.getprototypeof@1.0.10: + dependencies: + call-bind: 1.0.9 + define-properties: 1.2.1 + es-abstract: 1.24.2 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + get-intrinsic: 1.3.0 + get-proto: 1.0.1 + which-builtin-type: 1.2.1 + + regenerate-unicode-properties@10.2.2: + dependencies: + regenerate: 1.4.2 + + regenerate@1.4.2: {} + + regenerator-runtime@0.13.11: {} + + regexp.prototype.flags@1.5.4: + dependencies: + call-bind: 1.0.9 + define-properties: 1.2.1 + es-errors: 1.3.0 + get-proto: 1.0.1 + gopd: 1.2.0 + set-function-name: 2.0.2 + + regexpp@3.2.0: {} + + regexpu-core@6.4.0: + dependencies: + regenerate: 1.4.2 + regenerate-unicode-properties: 10.2.2 + regjsgen: 0.8.0 + regjsparser: 0.13.1 + unicode-match-property-ecmascript: 2.0.0 + unicode-match-property-value-ecmascript: 2.2.1 + + regjsgen@0.8.0: {} + + regjsparser@0.13.1: + dependencies: + jsesc: 3.1.0 + + require-directory@2.1.1: {} + + requires-port@1.0.0: {} + + resolve-cwd@3.0.0: + dependencies: + resolve-from: 5.0.0 + + resolve-from@4.0.0: {} + + resolve-from@5.0.0: {} + + resolve-pkg-maps@1.0.0: {} + + resolve-workspace-root@2.0.1: {} + + resolve.exports@2.0.3: {} + + resolve@1.22.12: + dependencies: + es-errors: 1.3.0 + is-core-module: 2.16.1 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + + resolve@2.0.0-next.6: + dependencies: + es-errors: 1.3.0 + is-core-module: 2.16.1 + node-exports-info: 1.6.0 + object-keys: 1.1.1 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + + restore-cursor@2.0.0: + dependencies: + onetime: 2.0.1 + signal-exit: 3.0.7 + + rimraf@3.0.2: + dependencies: + glob: 7.2.3 + + safe-array-concat@1.1.4: + dependencies: + call-bind: 1.0.9 + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + has-symbols: 1.1.0 + isarray: 2.0.5 + + safe-buffer@5.2.1: {} + + safe-push-apply@1.0.0: + dependencies: + es-errors: 1.3.0 + isarray: 2.0.5 + + safe-regex-test@1.1.0: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-regex: 1.2.1 + + safer-buffer@2.1.2: {} + + sax@1.6.0: {} + + saxes@6.0.0: + dependencies: + xmlchars: 2.2.0 + + scheduler@0.27.0: {} + + semver@5.7.2: {} + + semver@6.3.1: {} + + semver@7.6.3: {} + + semver@7.7.3: {} + + semver@7.7.4: {} + + send@0.19.2: + dependencies: + debug: 2.6.9 + depd: 2.0.0 + destroy: 1.2.0 + encodeurl: 2.0.0 + escape-html: 1.0.3 + etag: 1.8.1 + fresh: 0.5.2 + http-errors: 2.0.1 + mime: 1.6.0 + ms: 2.1.3 + on-finished: 2.4.1 + range-parser: 1.2.1 + statuses: 2.0.2 + transitivePeerDependencies: + - supports-color + + serialize-error@2.1.0: {} + + serve-static@1.16.3: + dependencies: + encodeurl: 2.0.0 + escape-html: 1.0.3 + parseurl: 1.3.3 + send: 0.19.2 + transitivePeerDependencies: + - supports-color + + server-only@0.0.1: {} + + set-function-length@1.2.2: + dependencies: + define-data-property: 1.1.4 + es-errors: 1.3.0 + function-bind: 1.1.2 + get-intrinsic: 1.3.0 + gopd: 1.2.0 + has-property-descriptors: 1.0.2 + + set-function-name@2.0.2: + dependencies: + define-data-property: 1.1.4 + es-errors: 1.3.0 + functions-have-names: 1.2.3 + has-property-descriptors: 1.0.2 + + set-proto@1.0.0: + dependencies: + dunder-proto: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + + setimmediate@1.0.5: {} + + setprototypeof@1.2.0: {} + + sf-symbols-typescript@2.2.0: {} + + shallowequal@1.1.0: {} + + shebang-command@2.0.0: + dependencies: + shebang-regex: 3.0.0 + + shebang-regex@3.0.0: {} + + shell-quote@1.8.3: {} + + side-channel-list@1.0.1: + dependencies: + es-errors: 1.3.0 + object-inspect: 1.13.4 + + side-channel-map@1.0.1: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + object-inspect: 1.13.4 + + side-channel-weakmap@1.0.2: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + object-inspect: 1.13.4 + side-channel-map: 1.0.1 + + side-channel@1.1.0: + dependencies: + es-errors: 1.3.0 + object-inspect: 1.13.4 + side-channel-list: 1.0.1 + side-channel-map: 1.0.1 + side-channel-weakmap: 1.0.2 + + signal-exit@3.0.7: {} + + simple-plist@1.3.1: + dependencies: + bplist-creator: 0.1.0 + bplist-parser: 0.3.1 + plist: 3.1.1 + + simple-swizzle@0.2.4: + dependencies: + is-arrayish: 0.3.4 + + sisteransi@1.0.5: {} + + slash@2.0.0: {} + + slash@3.0.0: {} + + slash@5.1.0: {} + + slugify@1.6.9: {} + + source-map-js@1.2.1: {} + + source-map-support@0.5.13: + dependencies: + buffer-from: 1.1.2 + source-map: 0.6.1 + + source-map-support@0.5.21: + dependencies: + buffer-from: 1.1.2 + source-map: 0.6.1 + + source-map@0.5.6: {} + + source-map@0.5.7: {} + + source-map@0.6.1: {} + + split-on-first@1.1.0: {} + + sprintf-js@1.0.3: {} + + stack-generator@2.0.10: + dependencies: + stackframe: 1.3.4 + + stack-utils@2.0.6: + dependencies: + escape-string-regexp: 2.0.0 + + stackframe@1.3.4: {} + + stacktrace-gps@3.1.2: + dependencies: + source-map: 0.5.6 + stackframe: 1.3.4 + + stacktrace-js@2.0.2: + dependencies: + error-stack-parser: 2.1.4 + stack-generator: 2.0.10 + stacktrace-gps: 3.1.2 + + stacktrace-parser@0.1.11: + dependencies: + type-fest: 0.7.1 + + statuses@1.5.0: {} + + statuses@2.0.2: {} + + stop-iteration-iterator@1.1.0: + dependencies: + es-errors: 1.3.0 + internal-slot: 1.1.0 + + stream-buffers@2.2.0: {} + + strict-uri-encode@2.0.0: {} + + string-length@4.0.2: + dependencies: + char-regex: 1.0.2 + strip-ansi: 6.0.1 + + string-length@5.0.1: + dependencies: + char-regex: 2.0.2 + strip-ansi: 7.2.0 + + string-width@4.2.3: + dependencies: + emoji-regex: 8.0.0 + is-fullwidth-code-point: 3.0.0 + strip-ansi: 6.0.1 + + string.prototype.matchall@4.0.12: + dependencies: + call-bind: 1.0.9 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-abstract: 1.24.2 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + get-intrinsic: 1.3.0 + gopd: 1.2.0 + has-symbols: 1.1.0 + internal-slot: 1.1.0 + regexp.prototype.flags: 1.5.4 + set-function-name: 2.0.2 + side-channel: 1.1.0 + + string.prototype.repeat@1.0.0: + dependencies: + define-properties: 1.2.1 + es-abstract: 1.24.2 + + string.prototype.trim@1.2.10: + dependencies: + call-bind: 1.0.9 + call-bound: 1.0.4 + define-data-property: 1.1.4 + define-properties: 1.2.1 + es-abstract: 1.24.2 + es-object-atoms: 1.1.1 + has-property-descriptors: 1.0.2 + + string.prototype.trimend@1.0.9: + dependencies: + call-bind: 1.0.9 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-object-atoms: 1.1.1 + + string.prototype.trimstart@1.0.8: + dependencies: + call-bind: 1.0.9 + define-properties: 1.2.1 + es-object-atoms: 1.1.1 + + strip-ansi@5.2.0: + dependencies: + ansi-regex: 4.1.1 + + strip-ansi@6.0.1: + dependencies: + ansi-regex: 5.0.1 + + strip-ansi@7.2.0: + dependencies: + ansi-regex: 6.2.2 + + strip-bom@3.0.0: {} + + strip-bom@4.0.0: {} + + strip-final-newline@2.0.0: {} + + strip-indent@3.0.0: + dependencies: + min-indent: 1.0.1 + + strip-json-comments@3.1.1: {} + + structured-headers@0.4.1: {} + + styleq@0.1.3: {} + + supports-color@5.5.0: + dependencies: + has-flag: 3.0.0 + + supports-color@7.2.0: + dependencies: + has-flag: 4.0.0 + + supports-color@8.1.1: + dependencies: + has-flag: 4.0.0 + + supports-hyperlinks@2.3.0: + dependencies: + has-flag: 4.0.0 + supports-color: 7.2.0 + + supports-preserve-symlinks-flag@1.0.0: {} + + symbol-tree@3.2.4: {} + + synckit@0.11.12: + dependencies: + '@pkgr/core': 0.2.9 + + tagged-tag@1.0.0: {} + + tapable@2.3.3: {} + + terminal-link@2.1.1: + dependencies: + ansi-escapes: 4.3.2 + supports-hyperlinks: 2.3.0 + + terser@5.46.2: + dependencies: + '@jridgewell/source-map': 0.3.11 + acorn: 8.16.0 + commander: 2.20.3 + source-map-support: 0.5.21 + + test-exclude@6.0.0: + dependencies: + '@istanbuljs/schema': 0.1.6 + glob: 7.2.3 + minimatch: 3.1.5 + + throat@5.0.0: {} + + tinyglobby@0.2.16: + dependencies: + fdir: 6.5.0(picomatch@4.0.4) + picomatch: 4.0.4 + + tinypool@2.1.0: {} + + tmpl@1.0.5: {} + + to-regex-range@5.0.1: + dependencies: + is-number: 7.0.0 + + toidentifier@1.0.1: {} + + toqr@0.1.1: {} + + tough-cookie@4.1.4: + dependencies: + psl: 1.15.0 + punycode: 2.3.1 + universalify: 0.2.0 + url-parse: 1.5.10 + + tr46@0.0.3: {} + + tr46@3.0.0: + dependencies: + punycode: 2.3.1 + + ts-api-utils@2.5.0(typescript@5.9.3): + dependencies: + typescript: 5.9.3 + + ts-declaration-location@1.0.7(typescript@5.9.3): + dependencies: + picomatch: 4.0.4 + typescript: 5.9.3 + + ts-jest@29.0.5(@babel/core@7.29.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.29.0))(jest@29.7.0(@types/node@25.6.0))(typescript@5.9.3): + dependencies: + bs-logger: 0.2.6 + fast-json-stable-stringify: 2.1.0 + jest: 29.7.0(@types/node@25.6.0) + jest-util: 29.7.0 + json5: 2.2.3 + lodash.memoize: 4.1.2 + make-error: 1.3.6 + semver: 7.7.4 + typescript: 5.9.3 + yargs-parser: 21.1.1 + optionalDependencies: + '@babel/core': 7.29.0 + '@jest/types': 29.6.3 + babel-jest: 29.7.0(@babel/core@7.29.0) + + tsconfig-paths@3.15.0: + dependencies: + '@types/json5': 0.0.29 + json5: 1.0.2 + minimist: 1.2.8 + strip-bom: 3.0.0 + + tslib@2.8.1: {} + + tsx@4.21.0: + dependencies: + esbuild: 0.27.7 + get-tsconfig: 4.14.0 + optionalDependencies: + fsevents: 2.3.3 + + tweetnacl@1.0.3: {} + + type-check@0.4.0: + dependencies: + prelude-ls: 1.2.1 + + type-detect@4.0.8: {} + + type-fest@0.21.3: {} + + type-fest@0.7.1: {} + + type-fest@5.6.0: + dependencies: + tagged-tag: 1.0.0 + + typed-array-buffer@1.0.3: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-typed-array: 1.1.15 + + typed-array-byte-length@1.0.3: + dependencies: + call-bind: 1.0.9 + for-each: 0.3.5 + gopd: 1.2.0 + has-proto: 1.2.0 + is-typed-array: 1.1.15 + + typed-array-byte-offset@1.0.4: + dependencies: + available-typed-arrays: 1.0.7 + call-bind: 1.0.9 + for-each: 0.3.5 + gopd: 1.2.0 + has-proto: 1.2.0 + is-typed-array: 1.1.15 + reflect.getprototypeof: 1.0.10 + + typed-array-length@1.0.7: + dependencies: + call-bind: 1.0.9 + for-each: 0.3.5 + gopd: 1.2.0 + is-typed-array: 1.1.15 + possible-typed-array-names: 1.1.0 + reflect.getprototypeof: 1.0.10 + + typescript@5.9.3: {} + + ua-parser-js@1.0.41: {} + + unbox-primitive@1.1.0: + dependencies: + call-bound: 1.0.4 + has-bigints: 1.1.0 + has-symbols: 1.1.0 + which-boxed-primitive: 1.1.1 + + undici-types@7.19.2: {} + + unicode-canonical-property-names-ecmascript@2.0.1: {} + + unicode-match-property-ecmascript@2.0.0: + dependencies: + unicode-canonical-property-names-ecmascript: 2.0.1 + unicode-property-aliases-ecmascript: 2.2.0 + + unicode-match-property-value-ecmascript@2.2.1: {} + + unicode-property-aliases-ecmascript@2.2.0: {} + + universalify@0.2.0: {} + + unpipe@1.0.0: {} + + update-browserslist-db@1.2.3(browserslist@4.28.2): + dependencies: + browserslist: 4.28.2 + escalade: 3.2.0 + picocolors: 1.1.1 + + uri-js@4.4.1: + dependencies: + punycode: 2.3.1 + + url-parse@1.5.10: + dependencies: + querystringify: 2.2.0 + requires-port: 1.0.0 + + use-callback-ref@1.3.3(@types/react@19.2.14)(react@19.2.0): + dependencies: + react: 19.2.0 + tslib: 2.8.1 + optionalDependencies: + '@types/react': 19.2.14 + + use-latest-callback@0.2.6(react@19.2.0): + dependencies: + react: 19.2.0 + + use-sidecar@1.1.3(@types/react@19.2.14)(react@19.2.0): + dependencies: + detect-node-es: 1.1.0 + react: 19.2.0 + tslib: 2.8.1 + optionalDependencies: + '@types/react': 19.2.14 + + use-sync-external-store@1.6.0(react@19.2.0): + dependencies: + react: 19.2.0 + + utils-merge@1.0.1: {} + + uuid@7.0.3: {} + + v8-to-istanbul@9.3.0: + dependencies: + '@jridgewell/trace-mapping': 0.3.31 + '@types/istanbul-lib-coverage': 2.0.6 + convert-source-map: 2.0.0 + + validate-npm-package-name@5.0.1: {} + + vary@1.1.2: {} + + vaul@1.1.2(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.0))(react@19.2.0): + dependencies: + '@radix-ui/react-dialog': 1.1.15(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.0))(react@19.2.0) + react: 19.2.0 + react-dom: 19.2.5(react@19.2.0) + transitivePeerDependencies: + - '@types/react' + - '@types/react-dom' + + vlq@1.0.1: {} + + w3c-xmlserializer@4.0.0: + dependencies: + xml-name-validator: 4.0.0 + + walker@1.0.8: + dependencies: + makeerror: 1.0.12 + + warn-once@0.1.1: {} + + wcwidth@1.0.1: + dependencies: + defaults: 1.0.4 + + webidl-conversions@3.0.1: {} + + webidl-conversions@7.0.0: {} + + whatwg-encoding@2.0.0: + dependencies: + iconv-lite: 0.6.3 + + whatwg-fetch@3.6.20: {} + + whatwg-mimetype@3.0.0: {} + + whatwg-url-minimum@0.1.1: {} + + whatwg-url@11.0.0: + dependencies: + tr46: 3.0.0 + webidl-conversions: 7.0.0 + + whatwg-url@5.0.0: + dependencies: + tr46: 0.0.3 + webidl-conversions: 3.0.1 + + which-boxed-primitive@1.1.1: + dependencies: + is-bigint: 1.1.0 + is-boolean-object: 1.2.2 + is-number-object: 1.1.1 + is-string: 1.1.1 + is-symbol: 1.1.1 + + which-builtin-type@1.2.1: + dependencies: + call-bound: 1.0.4 + function.prototype.name: 1.1.8 + has-tostringtag: 1.0.2 + is-async-function: 2.1.1 + is-date-object: 1.1.0 + is-finalizationregistry: 1.1.1 + is-generator-function: 1.1.2 + is-regex: 1.2.1 + is-weakref: 1.1.1 + isarray: 2.0.5 + which-boxed-primitive: 1.1.1 + which-collection: 1.0.2 + which-typed-array: 1.1.20 + + which-collection@1.0.2: + dependencies: + is-map: 2.0.3 + is-set: 2.0.3 + is-weakmap: 2.0.2 + is-weakset: 2.0.4 + + which-typed-array@1.1.20: + dependencies: + available-typed-arrays: 1.0.7 + call-bind: 1.0.9 + call-bound: 1.0.4 + for-each: 0.3.5 + get-proto: 1.0.1 + gopd: 1.2.0 + has-tostringtag: 1.0.2 + + which@2.0.2: + dependencies: + isexe: 2.0.0 + + word-wrap@1.2.5: {} + + wrap-ansi@7.0.0: + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + + wrappy@1.0.2: {} + + write-file-atomic@4.0.2: + dependencies: + imurmurhash: 0.1.4 + signal-exit: 3.0.7 + + ws@7.5.10: {} + + ws@8.20.0: {} + + xcode@3.0.1: + dependencies: + simple-plist: 1.3.1 + uuid: 7.0.3 + + xml-name-validator@4.0.0: {} + + xml2js@0.6.0: + dependencies: + sax: 1.6.0 + xmlbuilder: 11.0.1 + + xmlbuilder@11.0.1: {} + + xmlbuilder@15.1.1: {} + + xmlchars@2.2.0: {} + + y18n@5.0.8: {} + + yallist@3.1.1: {} + + yaml@2.8.3: {} + + yargs-parser@21.1.1: {} + + yargs@17.7.2: + dependencies: + cliui: 8.0.1 + escalade: 3.2.0 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + string-width: 4.2.3 + y18n: 5.0.8 + yargs-parser: 21.1.1 + + yocto-queue@0.1.0: {} + + zod@3.25.76: {} + + zod@4.3.6: {} + + zustand@5.0.12(@types/react@19.2.14)(react@19.2.0)(use-sync-external-store@1.6.0(react@19.2.0)): + optionalDependencies: + '@types/react': 19.2.14 + react: 19.2.0 + use-sync-external-store: 1.6.0(react@19.2.0) + + zxing-wasm@3.0.2(@types/emscripten@1.41.5): + dependencies: + '@types/emscripten': 1.41.5 + type-fest: 5.6.0 diff --git a/mobile/scripts/mock-server.ts b/mobile/scripts/mock-server.ts new file mode 100644 index 00000000000..14c7ab2c3db --- /dev/null +++ b/mobile/scripts/mock-server.ts @@ -0,0 +1,321 @@ +#!/usr/bin/env npx tsx +// Why: standalone mock WebSocket server for developing the mobile app without +// a running Orca desktop instance. Responds to the same RPC methods the real +// runtime exposes, with realistic fake data. Supports E2EE handshake. +import { WebSocketServer, type WebSocket } from 'ws' +import nacl from 'tweetnacl' + +const PORT = Number(process.env.PORT) || 6768 +const AUTH_TOKEN = 'mock-device-token' + +// Why: generate a persistent server keypair for this mock session. +// The public key is printed at startup so it can be used in pairing QR data. +const serverKeyPair = nacl.box.keyPair() +const serverPublicKeyB64 = Buffer.from(serverKeyPair.publicKey).toString('base64') + +type E2EEState = { + sharedKey: Uint8Array + deviceToken: string | null + authenticated: boolean +} + +function deriveSharedKey(ourSecret: Uint8Array, peerPublic: Uint8Array): Uint8Array { + return nacl.box.before(peerPublic, ourSecret) +} + +function e2eeEncrypt(plaintext: string, sharedKey: Uint8Array): string { + const nonce = nacl.randomBytes(nacl.box.nonceLength) + const msg = new TextEncoder().encode(plaintext) + const ciphertext = nacl.box.after(msg, nonce, sharedKey) + const bundle = new Uint8Array(nonce.length + ciphertext.length) + bundle.set(nonce) + bundle.set(ciphertext, nonce.length) + return Buffer.from(bundle).toString('base64') +} + +function e2eeDecrypt(encrypted: string, sharedKey: Uint8Array): string | null { + const bundle = Uint8Array.from(Buffer.from(encrypted, 'base64')) + if (bundle.length < nacl.box.nonceLength + nacl.box.overheadLength) return null + const nonce = bundle.slice(0, nacl.box.nonceLength) + const ciphertext = bundle.slice(nacl.box.nonceLength) + const plaintext = nacl.box.open.after(ciphertext, nonce, sharedKey) + if (!plaintext) return null + return new TextDecoder().decode(plaintext) +} + +const FAKE_WORKTREES = [ + { + worktreeId: 'repo-1::/home/user/projects/acme-api', + repoId: 'repo-1', + repo: 'acme-api', + path: '/home/user/projects/acme-api', + branch: 'feature/auth-refactor', + linkedIssue: 42, + unread: true, + liveTerminalCount: 2, + hasAttachedPty: true, + lastOutputAt: Date.now() - 5000, + preview: '$ claude "refactor the auth module"' + }, + { + worktreeId: 'repo-1::/home/user/projects/acme-web', + repoId: 'repo-1', + repo: 'acme-web', + path: '/home/user/projects/acme-web', + branch: 'main', + linkedIssue: null, + unread: false, + liveTerminalCount: 1, + hasAttachedPty: true, + lastOutputAt: Date.now() - 60000, + preview: '$ npm test\nAll tests passed.' + } +] + +const FAKE_TERMINALS = [ + { + handle: 'term-1', + worktreeId: 'repo-1::/home/user/projects/acme-api', + title: 'Claude — auth refactor', + isActive: true, + hasRunningProcess: true + }, + { + handle: 'term-2', + worktreeId: 'repo-1::/home/user/projects/acme-api', + title: 'zsh', + isActive: false, + hasRunningProcess: false + } +] + +const FAKE_SCROLLBACK = [ + '$ claude "refactor the auth module to use JWT tokens"', + '', + '⏳ Working on it...', + '', + "I'll refactor the auth module. Here's my plan:", + '1. Replace session-based auth with JWT', + '2. Add token refresh endpoint', + '3. Update middleware', + '', + 'Let me start by reading the current auth module...', + '' +].join('\n') + +const STREAMING_CHUNKS = [ + 'Reading src/auth/middleware.ts...\n', + 'Reading src/auth/session.ts...\n', + '\nI see the current implementation uses express-session.\n', + "I'll replace it with jsonwebtoken.\n", + '\nUpdating src/auth/middleware.ts...\n' +] + +type RpcRequest = { + id: string + method: string + deviceToken?: string + params?: Record +} + +type RpcResponse = { + id: string + ok: boolean + result?: unknown + error?: { code: string; message: string } + streaming?: true + _meta: { runtimeId: string } +} + +function success(id: string, result: unknown, streaming?: boolean): RpcResponse { + const resp: RpcResponse = { id, ok: true, result, _meta: { runtimeId: 'mock-runtime' } } + if (streaming) { + resp.streaming = true + } + return resp +} + +function error(id: string, code: string, message: string): RpcResponse { + return { id, ok: false, error: { code, message }, _meta: { runtimeId: 'mock-runtime' } } +} + +function handleRequest( + request: RpcRequest, + send: (response: RpcResponse) => void, + ws: WebSocket +): void { + switch (request.method) { + case 'status.get': + send( + success(request.id, { + runtimeId: 'mock-runtime', + graphStatus: 'ready', + windowCount: 1, + tabCount: 2, + terminalCount: 2 + }) + ) + break + + case 'worktree.ps': + send( + success(request.id, { + worktrees: FAKE_WORKTREES, + totalCount: FAKE_WORKTREES.length, + truncated: false + }) + ) + break + + case 'terminal.list': + send( + success(request.id, { + terminals: FAKE_TERMINALS, + totalCount: FAKE_TERMINALS.length, + truncated: false + }) + ) + break + + case 'terminal.subscribe': { + send(success(request.id, { type: 'scrollback', lines: FAKE_SCROLLBACK, truncated: false })) + + let chunkIndex = 0 + const interval = setInterval(() => { + if (chunkIndex >= STREAMING_CHUNKS.length || ws.readyState !== ws.OPEN) { + clearInterval(interval) + if (ws.readyState === ws.OPEN) { + send(success(request.id, { type: 'end' })) + } + return + } + send(success(request.id, { type: 'data', chunk: STREAMING_CHUNKS[chunkIndex] }, true)) + chunkIndex++ + }, 500) + break + } + + case 'terminal.send': + send(success(request.id, { send: { handle: 'term-1', ok: true } })) + break + + case 'terminal.unsubscribe': + send(success(request.id, { unsubscribed: true })) + break + + default: + send(error(request.id, 'method_not_found', `Unknown method: ${request.method}`)) + } +} + +const wss = new WebSocketServer({ port: PORT }) + +// Why: each connection goes through an E2EE handshake before any RPC traffic. +// The first message must be e2ee_hello (plaintext), then all subsequent +// messages are encrypted with the derived shared key. +const connectionState = new Map() + +wss.on('connection', (ws) => { + console.log('[mock] Client connected — waiting for e2ee_hello') + + ws.on('message', (data) => { + const msg = typeof data === 'string' ? data : data.toString('utf-8') + const e2ee = connectionState.get(ws) + + if (!e2ee) { + // Handshake phase — expect e2ee_hello + let hello: { type?: string; publicKeyB64?: string } + try { + hello = JSON.parse(msg) + } catch { + ws.send(JSON.stringify({ type: 'e2ee_error', message: 'Invalid JSON' })) + ws.close() + return + } + + if (hello.type !== 'e2ee_hello' || !hello.publicKeyB64) { + ws.send(JSON.stringify({ type: 'e2ee_error', message: 'Expected e2ee_hello' })) + ws.close() + return + } + + const clientPublicKey = Uint8Array.from(Buffer.from(hello.publicKeyB64, 'base64')) + if (clientPublicKey.length !== 32) { + ws.send(JSON.stringify({ type: 'e2ee_error', message: 'Invalid public key' })) + ws.close() + return + } + + const sharedKey = deriveSharedKey(serverKeyPair.secretKey, clientPublicKey) + connectionState.set(ws, { sharedKey, deviceToken: null, authenticated: false }) + + ws.send(JSON.stringify({ type: 'e2ee_ready' })) + console.log('[mock] E2EE key exchange complete — waiting for encrypted auth') + return + } + + // Post-handshake — decrypt, handle, encrypt reply + const plaintext = e2eeDecrypt(msg, e2ee.sharedKey) + if (plaintext === null) { + console.log('[mock] Decryption failed — dropping message') + return + } + + let request: RpcRequest + try { + request = JSON.parse(plaintext) as RpcRequest + } catch { + const encrypted = e2eeEncrypt( + JSON.stringify(error('unknown', 'bad_request', 'Invalid JSON')), + e2ee.sharedKey + ) + ws.send(encrypted) + return + } + + if (!e2ee.authenticated) { + const auth = request as unknown as { type?: string; deviceToken?: string } + if (auth.type !== 'e2ee_auth' || auth.deviceToken !== AUTH_TOKEN) { + ws.send( + e2eeEncrypt( + JSON.stringify({ type: 'e2ee_error', error: { code: 'unauthorized' } }), + e2ee.sharedKey + ) + ) + ws.close() + return + } + e2ee.deviceToken = auth.deviceToken + e2ee.authenticated = true + ws.send(e2eeEncrypt(JSON.stringify({ type: 'e2ee_authenticated' }), e2ee.sharedKey)) + console.log('[mock] E2EE authentication complete') + return + } + + console.log(`[mock] ${request.method} (id: ${request.id})`) + handleRequest( + request, + (response) => { + if (ws.readyState === ws.OPEN) { + ws.send(e2eeEncrypt(JSON.stringify(response), e2ee.sharedKey)) + } + }, + ws + ) + }) + + ws.on('close', () => { + connectionState.delete(ws) + console.log('[mock] Client disconnected') + }) + + ws.on('error', () => { + connectionState.delete(ws) + ws.close() + }) +}) + +console.log(`[mock] Orca mock server listening on ws://localhost:${PORT}`) +console.log(`[mock] Auth token: ${AUTH_TOKEN}`) +console.log(`[mock] Server public key (base64): ${serverPublicKeyB64}`) +console.log(`[mock] E2EE enabled — clients must send e2ee_hello before RPC`) diff --git a/mobile/scripts/repro-terminal-colors.ts b/mobile/scripts/repro-terminal-colors.ts new file mode 100644 index 00000000000..89f0bb55d56 --- /dev/null +++ b/mobile/scripts/repro-terminal-colors.ts @@ -0,0 +1,362 @@ +/** + * Minimal mobile terminal color repro. + * + * Captures terminal.subscribe snapshots in the same sequence that tab switching + * uses: terminal A, terminal B, terminal A again. The report tells us whether + * ANSI SGR color attributes disappeared in the desktop serialized scrollback + * or are still present and therefore being lost during mobile WebView replay. + * + * Usage: + * ORCA_MOBILE_WS_URL=ws://127.0.0.1:6768 \ + * pnpm exec tsx scripts/repro-terminal-colors.ts [handleA] [handleB] + */ +import { mkdirSync, writeFileSync } from 'node:fs' +import { join } from 'node:path' +import nacl from 'tweetnacl' +import WebSocket from 'ws' + +const WS_URL = process.env.ORCA_MOBILE_WS_URL ?? 'ws://127.0.0.1:6768' +const token = process.argv[2] +const serverPublicKeyB64 = process.argv[3] +const worktreeSelector = process.argv[4] +const explicitHandleA = process.argv[5] +const explicitHandleB = process.argv[6] +const ESC = String.fromCharCode(27) + +type RpcResponse = { + id: string + ok: boolean + streaming?: true + result?: Record + error?: { code: string; message: string } +} + +type PendingRequest = { + method: string + resolve: (response: RpcResponse) => void + reject: (error: Error) => void +} + +type Snapshot = { + label: string + handle: string + cols: number | null + rows: number | null + serialized: string + lines: string +} + +if (!token || !serverPublicKeyB64 || !worktreeSelector) { + console.error( + 'Usage: pnpm exec tsx scripts/repro-terminal-colors.ts [handleA] [handleB]' + ) + process.exit(1) +} + +let reqId = 0 +const pending = new Map() +const streamListeners = new Map) => void>() +const clientKeys = nacl.box.keyPair() +const serverPublicKey = Buffer.from(serverPublicKeyB64, 'base64') +const sharedKey = nacl.box.before(new Uint8Array(serverPublicKey), clientKeys.secretKey) + +function nextId(): string { + reqId += 1 + return `color-repro-${reqId}` +} + +function toBase64(bytes: Uint8Array): string { + return Buffer.from(bytes).toString('base64') +} + +function fromBase64(value: string): Uint8Array { + return new Uint8Array(Buffer.from(value, 'base64')) +} + +function encrypt(plaintext: string): string { + const nonce = nacl.randomBytes(nacl.box.nonceLength) + const message = new TextEncoder().encode(plaintext) + const ciphertext = nacl.box.after(message, nonce, sharedKey) + const bundle = new Uint8Array(nonce.length + ciphertext.length) + bundle.set(nonce) + bundle.set(ciphertext, nonce.length) + return toBase64(bundle) +} + +function decrypt(payload: string): string | null { + const bundle = fromBase64(payload) + const nonce = bundle.subarray(0, nacl.box.nonceLength) + const ciphertext = bundle.subarray(nacl.box.nonceLength) + const plaintext = nacl.box.open.after(ciphertext, nonce, sharedKey) + return plaintext ? new TextDecoder().decode(plaintext) : null +} + +function sendRaw(ws: WebSocket, payload: unknown): void { + ws.send(encrypt(JSON.stringify(payload))) +} + +function send(ws: WebSocket, method: string, params?: unknown): Promise { + const id = nextId() + sendRaw(ws, { id, deviceToken: token, method, params }) + return new Promise((resolve, reject) => { + const timeout = setTimeout(() => { + pending.delete(id) + streamListeners.delete(id) + reject(new Error(`Timed out waiting for ${method}`)) + }, 10_000) + pending.set(id, { + method, + resolve: (response) => { + clearTimeout(timeout) + resolve(response) + }, + reject: (error) => { + clearTimeout(timeout) + reject(error) + } + }) + }) +} + +function formatError(response: RpcResponse): string { + return JSON.stringify(response.error ?? response.result ?? response).slice(0, 1000) +} + +async function listHandles( + ws: WebSocket +): Promise> { + const response = await send(ws, 'terminal.list', { worktree: worktreeSelector }) + if (!response.ok) { + throw new Error(`terminal.list failed: ${formatError(response)}`) + } + return ( + (response.result?.terminals ?? []) as Array<{ handle: string; title?: string | null }> + ).map((terminal) => ({ + handle: terminal.handle, + title: terminal.title ?? null + })) +} + +async function ensureSecondHandle(ws: WebSocket, handleA: string): Promise { + const terminals = await listHandles(ws) + const existing = terminals.find((terminal) => terminal.handle !== handleA) + if (existing) return existing.handle + + const created = await send(ws, 'terminal.create', { + worktree: worktreeSelector, + title: 'color-repro-switch-target' + }) + if (!created.ok) { + throw new Error(`terminal.create failed: ${formatError(created)}`) + } + const handle = (created.result?.terminal as { handle?: string } | undefined)?.handle + if (!handle) { + throw new Error(`terminal.create returned no handle: ${formatError(created)}`) + } + return handle +} + +async function captureSnapshot(ws: WebSocket, label: string, handle: string): Promise { + const id = nextId() + const snapshot = await new Promise((resolve, reject) => { + const timeout = setTimeout(() => { + pending.delete(id) + streamListeners.delete(id) + reject(new Error(`Timed out waiting for scrollback snapshot ${label}`)) + }, 10_000) + + pending.set(id, { + method: 'terminal.subscribe', + resolve: () => {}, + reject: (error) => { + clearTimeout(timeout) + reject(error) + } + }) + streamListeners.set(id, (result) => { + if (result.type !== 'scrollback') return + clearTimeout(timeout) + pending.delete(id) + streamListeners.delete(id) + const serialized = typeof result.serialized === 'string' ? result.serialized : '' + const rawLines = result.lines + const lines = Array.isArray(rawLines) ? rawLines.join('\n') : String(rawLines ?? '') + resolve({ + label, + handle, + cols: typeof result.cols === 'number' ? result.cols : null, + rows: typeof result.rows === 'number' ? result.rows : null, + serialized, + lines + }) + }) + + sendRaw(ws, { + id, + deviceToken: token, + method: 'terminal.subscribe', + params: { terminal: handle } + }) + }) + + await send(ws, 'terminal.unsubscribe', { subscriptionId: handle }).catch(() => null) + return snapshot +} + +function countMatches(value: string, pattern: RegExp): number { + return value.match(pattern)?.length ?? 0 +} + +function summarize(snapshot: Snapshot): Record { + const data = snapshot.serialized + return { + label: snapshot.label, + handle: snapshot.handle, + cols: snapshot.cols, + rows: snapshot.rows, + serializedBytes: Buffer.byteLength(data), + sgrTotal: countMatches(data, new RegExp(`${ESC}\\[[0-9;:]*m`, 'g')), + sgrColor: countMatches( + data, + new RegExp( + `${ESC}\\[(?:[0-9;:]*[;:])?(?:3[0-7]|4[0-7]|9[0-7]|10[0-7]|38[;:]|48[;:])[0-9;:]*m`, + 'g' + ) + ), + sgrReset: countMatches(data, new RegExp(`${ESC}\\[(?:0|39|49|0;39;49)m`, 'g')), + altScreen: data.includes(`${ESC}[?1049h`) ? 'yes' : 'no', + containsTruecolor: data.includes('38;2') || data.includes('38:2') ? 'yes' : 'no', + containsPaletteColor: data.includes('38;5') || data.includes('38:5') ? 'yes' : 'no' + } +} + +function saveSnapshots(snapshots: Snapshot[]): string { + const dir = join(process.cwd(), 'terminal-color-repro') + mkdirSync(dir, { recursive: true }) + for (const snapshot of snapshots) { + const base = snapshot.label.replace(/[^a-z0-9_-]/gi, '-') + writeFileSync(join(dir, `${base}.ansi`), snapshot.serialized || snapshot.lines) + writeFileSync( + join(dir, `${base}.escaped.txt`), + JSON.stringify(snapshot.serialized || snapshot.lines) + ) + } + writeFileSync(join(dir, 'summary.json'), JSON.stringify(snapshots.map(summarize), null, 2)) + return dir +} + +async function run(ws: WebSocket): Promise { + ws.send( + JSON.stringify({ + type: 'e2ee_hello', + publicKeyB64: toBase64(clientKeys.publicKey) + }) + ) + + await new Promise((resolve, reject) => { + const timeout = setTimeout(() => reject(new Error('Timed out waiting for e2ee_ready')), 5000) + ws.once('message', (data) => { + clearTimeout(timeout) + const msg = JSON.parse(data.toString()) as { type?: string } + if (msg.type !== 'e2ee_ready') { + reject(new Error(`Unexpected handshake response: ${data.toString()}`)) + return + } + resolve() + }) + }) + + sendRaw(ws, { type: 'e2ee_auth', deviceToken: token }) + await new Promise((resolve, reject) => { + const timeout = setTimeout( + () => reject(new Error('Timed out waiting for e2ee_authenticated')), + 5000 + ) + ws.once('message', (data) => { + clearTimeout(timeout) + const plaintext = decrypt(data.toString()) + const msg = plaintext ? (JSON.parse(plaintext) as { type?: string }) : null + if (msg?.type !== 'e2ee_authenticated') { + reject(new Error(`Unexpected auth response: ${data.toString()}`)) + return + } + resolve() + }) + }) + + const terminals = await listHandles(ws) + if (terminals.length === 0 && !explicitHandleA) { + throw new Error('No terminals found. Open a Claude Code terminal first, then rerun.') + } + + const handleA = explicitHandleA ?? terminals[0]!.handle + const handleB = explicitHandleB ?? (await ensureSecondHandle(ws, handleA)) + + console.log(`A: ${handleA}`) + console.log(`B: ${handleB}`) + + const firstA = await captureSnapshot(ws, 'a-before-switch', handleA) + const firstB = await captureSnapshot(ws, 'b-during-switch', handleB) + const secondA = await captureSnapshot(ws, 'a-after-switch', handleA) + const snapshots = [firstA, firstB, secondA] + const dir = saveSnapshots(snapshots) + + console.table(snapshots.map(summarize)) + console.log(`saved: ${dir}`) + if (summarize(firstA).sgrColor !== summarize(secondA).sgrColor) { + console.log( + 'color SGR count changed between A snapshots: serialization/state changed on desktop' + ) + } else { + console.log('A snapshots have the same color SGR count: likely mobile replay/render path') + } +} + +const ws = new WebSocket(WS_URL) + +ws.on('open', () => { + run(ws) + .then(() => { + ws.close() + process.exit(0) + }) + .catch((error) => { + console.error(error.message) + ws.close() + process.exit(1) + }) +}) + +ws.on('message', (data) => { + const raw = data.toString() + if (raw.startsWith('{')) { + return + } + const plaintext = decrypt(raw) + if (!plaintext) return + + const response = JSON.parse(plaintext) as RpcResponse + const result = response.result + const streamListener = streamListeners.get(response.id) + if (streamListener && response.ok && (response.streaming || result?.type === 'scrollback')) { + streamListener(result ?? {}) + return + } + + const request = pending.get(response.id) + if (!request || request.method === 'terminal.subscribe') return + pending.delete(response.id) + request.resolve(response) +}) + +ws.on('close', () => { + for (const request of pending.values()) { + request.reject(new Error('WebSocket closed')) + } + pending.clear() +}) + +ws.on('error', (error) => { + console.error(`WebSocket error: ${error.message}`) + process.exit(1) +}) diff --git a/mobile/scripts/repro-worktree-startup-stream.ts b/mobile/scripts/repro-worktree-startup-stream.ts new file mode 100644 index 00000000000..bc1ab769564 --- /dev/null +++ b/mobile/scripts/repro-worktree-startup-stream.ts @@ -0,0 +1,367 @@ +/** + * Captures the mobile WebSocket stream during worktree startup. + * + * Usage: + * pnpm exec tsx mobile/scripts/repro-worktree-startup-stream.ts [startupCommand] + */ +import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs' +import { join } from 'node:path' +import nacl from 'tweetnacl' +import WebSocket from 'ws' + +const WS_URL = process.env.ORCA_MOBILE_WS_URL ?? 'ws://127.0.0.1:6768' +const USER_DATA = + process.env.ORCA_USER_DATA ?? `${process.env.HOME}/Library/Application Support/orca-dev` +const repoSelector = process.argv[2] +const worktreeName = process.argv[3] +const startupCommand = process.argv[4] || 'claude' +const ESC = String.fromCharCode(27) + +type RpcResponse = { + id: string + ok: boolean + streaming?: true + result?: Record + error?: { code: string; message: string } +} + +type PendingRequest = { + method: string + resolve: (response: RpcResponse) => void + reject: (error: Error) => void +} + +type TerminalInfo = { + handle: string + title: string | null +} + +type Capture = { + handle: string + title: string | null + scrollback: Record | null + chunks: string[] +} + +if (!repoSelector || !worktreeName) { + console.error( + 'Usage: pnpm exec tsx mobile/scripts/repro-worktree-startup-stream.ts [startupCommand]' + ) + process.exit(1) +} + +function readJson(path: string): T { + if (!existsSync(path)) { + throw new Error(`Missing ${path}`) + } + return JSON.parse(readFileSync(path, 'utf8')) as T +} + +const devices = readJson>(join(USER_DATA, 'orca-devices.json')) +const token = devices[0]?.token +const keypair = readJson<{ publicKeyB64: string }>(join(USER_DATA, 'orca-e2ee-keypair.json')) + +if (!token || !keypair.publicKeyB64) { + throw new Error(`Missing mobile token or E2EE public key in ${USER_DATA}`) +} + +let reqId = 0 +const pending = new Map() +const streamListeners = new Map) => void>() +const clientKeys = nacl.box.keyPair() +const serverPublicKey = Buffer.from(keypair.publicKeyB64, 'base64') +const sharedKey = nacl.box.before(new Uint8Array(serverPublicKey), clientKeys.secretKey) + +function nextId(): string { + reqId += 1 + return `startup-repro-${reqId}` +} + +function toBase64(bytes: Uint8Array): string { + return Buffer.from(bytes).toString('base64') +} + +function fromBase64(value: string): Uint8Array { + return new Uint8Array(Buffer.from(value, 'base64')) +} + +function encrypt(plaintext: string): string { + const nonce = nacl.randomBytes(nacl.box.nonceLength) + const message = new TextEncoder().encode(plaintext) + const ciphertext = nacl.box.after(message, nonce, sharedKey) + const bundle = new Uint8Array(nonce.length + ciphertext.length) + bundle.set(nonce) + bundle.set(ciphertext, nonce.length) + return toBase64(bundle) +} + +function decrypt(payload: string): string | null { + const bundle = fromBase64(payload) + if (bundle.length < nacl.box.nonceLength + nacl.box.overheadLength) { + return null + } + const nonce = bundle.subarray(0, nacl.box.nonceLength) + const ciphertext = bundle.subarray(nacl.box.nonceLength) + const plaintext = nacl.box.open.after(ciphertext, nonce, sharedKey) + return plaintext ? new TextDecoder().decode(plaintext) : null +} + +function sendRaw(ws: WebSocket, payload: unknown): void { + ws.send(encrypt(JSON.stringify(payload))) +} + +function send( + ws: WebSocket, + method: string, + params?: unknown, + timeoutMs = 30_000 +): Promise { + const id = nextId() + sendRaw(ws, { id, deviceToken: token, method, params }) + return new Promise((resolve, reject) => { + const timeout = setTimeout(() => { + pending.delete(id) + reject(new Error(`Timed out waiting for ${method}`)) + }, timeoutMs) + pending.set(id, { + method, + resolve: (response) => { + clearTimeout(timeout) + resolve(response) + }, + reject: (error) => { + clearTimeout(timeout) + reject(error) + } + }) + }) +} + +function formatError(response: RpcResponse): string { + return JSON.stringify(response.error ?? response.result ?? response).slice(0, 1000) +} + +function stripAnsi(value: string): string { + return ( + value + // eslint-disable-next-line no-control-regex -- intentional terminal escape stripping for repro summaries + .replace(/\u001b\][^\u0007]*(?:\u0007|\u001b\\)/g, '') + // eslint-disable-next-line no-control-regex -- intentional terminal escape stripping for repro summaries + .replace(/\u001b\[[0-?]*[ -/]*[@-~]/g, '') + .replace(/\r/g, '\n') + ) +} + +function normalizePreview(value: string): string { + return stripAnsi(value) + .split('\n') + .map((line) => line.trim()) + .filter(Boolean) + .slice(0, 8) + .join('\n') +} + +async function waitForTerminals(ws: WebSocket, worktreeId: string): Promise { + const deadline = Date.now() + 60_000 + while (Date.now() < deadline) { + const response = await send(ws, 'terminal.list', { worktree: worktreeId }) + if (!response.ok) { + throw new Error(`terminal.list failed: ${formatError(response)}`) + } + const terminals = (response.result?.terminals ?? []) as Array<{ + handle?: string + title?: string | null + }> + const handles = terminals + .filter((terminal): terminal is { handle: string; title?: string | null } => + Boolean(terminal.handle) + ) + .map((terminal) => ({ handle: terminal.handle, title: terminal.title ?? null })) + if (handles.length > 0) { + return handles + } + await new Promise((resolve) => setTimeout(resolve, 500)) + } + throw new Error('Timed out waiting for startup terminals') +} + +async function subscribe(ws: WebSocket, capture: Capture): Promise { + const id = nextId() + streamListeners.set(id, (result) => { + if (result.type === 'scrollback') { + capture.scrollback = result + return + } + if (result.type === 'data' && typeof result.chunk === 'string') { + capture.chunks.push(result.chunk) + } + }) + sendRaw(ws, { + id, + deviceToken: token, + method: 'terminal.subscribe', + params: { terminal: capture.handle } + }) +} + +function summarize(capture: Capture): Record { + const serialized = + typeof capture.scrollback?.serialized === 'string' ? capture.scrollback.serialized : '' + const live = capture.chunks.join('') + const serializedPreview = normalizePreview(serialized) + const livePreview = normalizePreview(live) + return { + handle: capture.handle, + title: capture.title, + cols: capture.scrollback?.cols ?? null, + rows: capture.scrollback?.rows ?? null, + serializedBytes: Buffer.byteLength(serialized), + liveBytes: Buffer.byteLength(live), + liveChunks: capture.chunks.length, + serializedSgr: (serialized.match(new RegExp(`${ESC}\\[[0-9;:]*m`, 'g')) ?? []).length, + liveSgr: (live.match(new RegExp(`${ESC}\\[[0-9;:]*m`, 'g')) ?? []).length, + livePreviewContainedInSerialized: + livePreview.length > 0 && stripAnsi(serialized).includes(livePreview.split('\n')[0] ?? ''), + serializedPreview, + livePreview + } +} + +function save(captures: Capture[], worktreeName: string): string { + const dir = join(process.cwd(), 'terminal-startup-repro', worktreeName) + mkdirSync(dir, { recursive: true }) + for (const capture of captures) { + const base = capture.handle.replace(/[^a-z0-9_-]/gi, '-') + const serialized = + typeof capture.scrollback?.serialized === 'string' ? capture.scrollback.serialized : '' + writeFileSync(join(dir, `${base}.serialized.ansi`), serialized) + writeFileSync(join(dir, `${base}.live.ansi`), capture.chunks.join('')) + writeFileSync(join(dir, `${base}.json`), JSON.stringify(capture, null, 2)) + } + writeFileSync(join(dir, 'summary.json'), JSON.stringify(captures.map(summarize), null, 2)) + return dir +} + +async function run(ws: WebSocket): Promise { + ws.send( + JSON.stringify({ + type: 'e2ee_hello', + publicKeyB64: toBase64(clientKeys.publicKey) + }) + ) + + await new Promise((resolve, reject) => { + const timeout = setTimeout(() => reject(new Error('Timed out waiting for e2ee_ready')), 5000) + ws.once('message', (data) => { + clearTimeout(timeout) + const msg = JSON.parse(data.toString()) as { type?: string } + if (msg.type !== 'e2ee_ready') { + reject(new Error(`Unexpected handshake response: ${data.toString()}`)) + return + } + resolve() + }) + }) + + sendRaw(ws, { type: 'e2ee_auth', deviceToken: token }) + await new Promise((resolve, reject) => { + const timeout = setTimeout( + () => reject(new Error('Timed out waiting for e2ee_authenticated')), + 5000 + ) + ws.once('message', (data) => { + clearTimeout(timeout) + const plaintext = decrypt(data.toString()) + const msg = plaintext ? (JSON.parse(plaintext) as { type?: string }) : null + if (msg?.type !== 'e2ee_authenticated') { + reject(new Error(`Unexpected auth response: ${data.toString()}`)) + return + } + resolve() + }) + }) + + const created = await send( + ws, + 'worktree.create', + { repo: repoSelector, name: worktreeName, startupCommand }, + 120_000 + ) + if (!created.ok) { + throw new Error(`worktree.create failed: ${formatError(created)}`) + } + const worktree = created.result?.worktree as { id?: string; path?: string } | undefined + if (!worktree?.id) { + throw new Error(`worktree.create returned no worktree id: ${formatError(created)}`) + } + + console.log(`worktree: ${worktree.id}`) + const terminals = await waitForTerminals(ws, worktree.id) + console.log(`terminals: ${terminals.map((terminal) => terminal.handle).join(', ')}`) + const captures = terminals.map((terminal) => ({ + handle: terminal.handle, + title: terminal.title, + scrollback: null, + chunks: [] + })) + for (const capture of captures) { + await subscribe(ws, capture) + } + + await new Promise((resolve) => setTimeout(resolve, 15_000)) + for (const capture of captures) { + await send(ws, 'terminal.unsubscribe', { subscriptionId: capture.handle }).catch(() => null) + } + const dir = save(captures, worktreeName) + console.table(captures.map(summarize)) + console.log(`saved: ${dir}`) +} + +const ws = new WebSocket(WS_URL) + +ws.on('open', () => { + run(ws) + .then(() => { + ws.close() + process.exit(0) + }) + .catch((error) => { + console.error(error.message) + ws.close() + process.exit(1) + }) +}) + +ws.on('message', (data) => { + const raw = data.toString() + if (raw.startsWith('{')) { + return + } + const plaintext = decrypt(raw) + if (!plaintext) return + + const response = JSON.parse(plaintext) as RpcResponse + const result = response.result + const streamListener = streamListeners.get(response.id) + if (streamListener && response.ok && (response.streaming || result?.type)) { + streamListener(result ?? {}) + return + } + + const request = pending.get(response.id) + if (!request || request.method === 'terminal.subscribe') return + pending.delete(response.id) + request.resolve(response) +}) + +ws.on('close', () => { + for (const request of pending.values()) { + request.reject(new Error('WebSocket closed')) + } + pending.clear() +}) + +ws.on('error', (error) => { + console.error(`WebSocket error: ${error.message}`) + process.exit(1) +}) diff --git a/mobile/scripts/test-subscribe.ts b/mobile/scripts/test-subscribe.ts new file mode 100644 index 00000000000..6fa8ffe4b42 --- /dev/null +++ b/mobile/scripts/test-subscribe.ts @@ -0,0 +1,324 @@ +/** + * Lightweight terminal streaming repro for the mobile WebSocket RPC. + * + * Usage: + * pnpm exec tsx scripts/test-subscribe.ts [worktreeSelector] + * + * Example: + * pnpm exec tsx scripts/test-subscribe.ts "$TOKEN" "$SERVER_PUBLIC_KEY" \ + * "id:repo-id::/path/to/worktree" + */ +import nacl from 'tweetnacl' +import WebSocket from 'ws' + +const WS_URL = process.env.ORCA_MOBILE_WS_URL ?? 'ws://127.0.0.1:6768' +const token = process.argv[2] +const serverPublicKeyB64 = process.argv[3] +const worktreeSelector = process.argv[4] +const marker = `MOBILE_STREAM_${Date.now()}` + +type RpcResponse = { + id: string + ok: boolean + streaming?: true + result?: Record + error?: { code: string; message: string } + _meta?: { runtimeId: string } +} + +type PendingRequest = { + method: string + resolve: (response: RpcResponse) => void + reject: (error: Error) => void +} + +if (!token || !serverPublicKeyB64) { + console.error( + 'Usage: pnpm exec tsx scripts/test-subscribe.ts [worktreeSelector]' + ) + process.exit(1) +} + +let reqId = 0 +const pending = new Map() +let streamSawMarker = false +let readSawMarker = false +let activeHandle: string | null = null +let runtimeId = '' +let scrollbackCols: number | null = null +let scrollbackRows: number | null = null +let serializedLength = 0 +const clientKeys = nacl.box.keyPair() +const serverPublicKey = Buffer.from(serverPublicKeyB64, 'base64') +const sharedKey = nacl.box.before(new Uint8Array(serverPublicKey), clientKeys.secretKey) + +function nextId(): string { + reqId += 1 + return `test-${reqId}` +} + +function toBase64(bytes: Uint8Array): string { + return Buffer.from(bytes).toString('base64') +} + +function fromBase64(value: string): Uint8Array { + return new Uint8Array(Buffer.from(value, 'base64')) +} + +function encrypt(plaintext: string): string { + const nonce = nacl.randomBytes(nacl.box.nonceLength) + const message = new TextEncoder().encode(plaintext) + const ciphertext = nacl.box.after(message, nonce, sharedKey) + const bundle = new Uint8Array(nonce.length + ciphertext.length) + bundle.set(nonce) + bundle.set(ciphertext, nonce.length) + return toBase64(bundle) +} + +function decrypt(payload: string): string | null { + const bundle = fromBase64(payload) + const nonce = bundle.subarray(0, nacl.box.nonceLength) + const ciphertext = bundle.subarray(nacl.box.nonceLength) + const plaintext = nacl.box.open.after(ciphertext, nonce, sharedKey) + return plaintext ? new TextDecoder().decode(plaintext) : null +} + +function sendRaw(ws: WebSocket, payload: unknown): void { + ws.send(encrypt(JSON.stringify(payload))) +} + +function send(ws: WebSocket, method: string, params?: unknown): Promise { + const id = nextId() + sendRaw(ws, { id, deviceToken: token, method, params }) + return new Promise((resolve, reject) => { + const timeout = setTimeout(() => { + pending.delete(id) + reject(new Error(`Timed out waiting for ${method}`)) + }, 10_000) + pending.set(id, { + method, + resolve: (response) => { + clearTimeout(timeout) + resolve(response) + }, + reject: (error) => { + clearTimeout(timeout) + reject(error) + } + }) + }) +} + +function formatResponse(response: RpcResponse): string { + return JSON.stringify(response.result ?? response.error ?? response).slice(0, 1000) +} + +async function chooseWorktree(ws: WebSocket): Promise { + if (worktreeSelector) return worktreeSelector + + const response = await send(ws, 'worktree.ps') + if (!response.ok) { + throw new Error(`worktree.ps failed: ${formatResponse(response)}`) + } + + const worktrees = (response.result?.worktrees ?? []) as Array<{ + worktreeId: string + liveTerminalCount: number + branch: string + path: string + }> + const selected = worktrees.find((w) => w.liveTerminalCount > 0) ?? worktrees[0] + if (!selected) { + throw new Error('No worktrees returned by worktree.ps') + } + + console.log(`worktree: ${selected.branch || '(no branch)'} ${selected.path}`) + return `id:${selected.worktreeId}` +} + +async function chooseTerminal(ws: WebSocket, worktree: string): Promise { + const list = await send(ws, 'terminal.list', { worktree }) + if (!list.ok) { + throw new Error(`terminal.list failed: ${formatResponse(list)}`) + } + + const terminals = (list.result?.terminals ?? []) as Array<{ + handle: string + title: string + preview: string + lastOutputAt: number | null + }> + + if (terminals.length > 0) { + const selected = terminals[0]! + console.log(`terminal: ${selected.title || selected.handle} ${selected.handle}`) + return selected.handle + } + + const created = await send(ws, 'terminal.create', { + worktree, + title: 'mobile-stream-repro' + }) + if (!created.ok) { + throw new Error(`terminal.create failed: ${formatResponse(created)}`) + } + + const terminal = created.result?.terminal as { handle?: string; title?: string } | undefined + if (!terminal?.handle) { + throw new Error(`terminal.create returned no handle: ${formatResponse(created)}`) + } + console.log(`terminal: ${terminal.title || terminal.handle} ${terminal.handle}`) + return terminal.handle +} + +async function run(ws: WebSocket): Promise { + console.log(`connected: ${WS_URL}`) + ws.send(JSON.stringify({ type: 'e2ee_hello', publicKeyB64: toBase64(clientKeys.publicKey) })) + await new Promise((resolve, reject) => { + const timeout = setTimeout(() => reject(new Error('Timed out waiting for e2ee_ready')), 5000) + ws.once('message', (data) => { + clearTimeout(timeout) + const msg = JSON.parse(data.toString()) as { type?: string } + if (msg.type !== 'e2ee_ready') { + reject(new Error(`Unexpected handshake response: ${data.toString()}`)) + return + } + resolve() + }) + }) + + sendRaw(ws, { type: 'e2ee_auth', deviceToken: token }) + await new Promise((resolve, reject) => { + const timeout = setTimeout( + () => reject(new Error('Timed out waiting for e2ee_authenticated')), + 5000 + ) + ws.once('message', (data) => { + clearTimeout(timeout) + const plaintext = decrypt(data.toString()) + const msg = plaintext ? (JSON.parse(plaintext) as { type?: string }) : null + if (msg?.type !== 'e2ee_authenticated') { + reject(new Error(`Unexpected auth response: ${data.toString()}`)) + return + } + resolve() + }) + }) + + const worktree = await chooseWorktree(ws) + const handle = await chooseTerminal(ws, worktree) + activeHandle = handle + + void send(ws, 'terminal.subscribe', { terminal: handle }).catch((error) => { + console.error(`subscribe failed: ${error.message}`) + }) + + await new Promise((resolve) => setTimeout(resolve, 500)) + + const sendResponse = await send(ws, 'terminal.send', { + terminal: handle, + text: `echo ${marker}`, + enter: true + }) + if (!sendResponse.ok) { + throw new Error(`terminal.send failed: ${formatResponse(sendResponse)}`) + } + console.log(`sent marker: ${marker}`) + + const deadline = Date.now() + 5_000 + while (Date.now() < deadline && !streamSawMarker && !readSawMarker) { + await new Promise((resolve) => setTimeout(resolve, 500)) + const read = await send(ws, 'terminal.read', { terminal: handle }) + if (!read.ok) { + throw new Error(`terminal.read failed: ${formatResponse(read)}`) + } + const terminal = read.result?.terminal as { tail?: string[]; preview?: string } | undefined + const text = [...(terminal?.tail ?? []), terminal?.preview ?? ''].join('\n') + if (text.includes(marker)) { + readSawMarker = true + } + } + + console.log(`runtime: ${runtimeId || '(unknown)'}`) + console.log(`scrollbackCols: ${scrollbackCols ?? '(none)'}`) + console.log(`scrollbackRows: ${scrollbackRows ?? '(none)'}`) + console.log(`serializedLength: ${serializedLength}`) + console.log(`streamSawMarker: ${streamSawMarker}`) + console.log(`readSawMarker: ${readSawMarker}`) + + if (!streamSawMarker && !readSawMarker) { + throw new Error('terminal output did not reach stream or terminal.read') + } +} + +const ws = new WebSocket(WS_URL) + +ws.on('open', () => { + run(ws) + .then(() => { + ws.close() + process.exit(0) + }) + .catch((error) => { + console.error(error.message) + ws.close() + process.exit(1) + }) +}) + +ws.on('message', (data) => { + const plaintext = decrypt(data.toString()) + if (!plaintext) return + const response = JSON.parse(plaintext) as RpcResponse + if (response._meta?.runtimeId) { + runtimeId = response._meta.runtimeId + } + + const result = response.result + if (response.streaming && result?.type === 'data') { + const chunk = typeof result.chunk === 'string' ? result.chunk : '' + if (chunk.includes(marker)) { + streamSawMarker = true + } + } + + if (response.streaming && result?.type === 'scrollback') { + const lines = Array.isArray(result.lines) ? result.lines.join('\n') : String(result.lines ?? '') + scrollbackCols = typeof result.cols === 'number' ? result.cols : null + scrollbackRows = typeof result.rows === 'number' ? result.rows : null + serializedLength = typeof result.serialized === 'string' ? result.serialized.length : 0 + if (lines.includes(marker)) { + streamSawMarker = true + } + if (typeof result.serialized === 'string' && result.serialized.includes(marker)) { + streamSawMarker = true + } + } + + const pendingRequest = pending.get(response.id) + if (!pendingRequest) { + return + } + + if (response.streaming && pendingRequest.method === 'terminal.subscribe') { + return + } + + pending.delete(response.id) + pendingRequest.resolve(response) +}) + +ws.on('close', () => { + for (const request of pending.values()) { + request.reject(new Error('WebSocket closed')) + } + pending.clear() +}) + +ws.on('error', (error) => { + console.error(`WebSocket error: ${error.message}`) + if (activeHandle) { + console.error(`active terminal: ${activeHandle}`) + } + process.exit(1) +}) diff --git a/mobile/src/cache/worktree-cache.ts b/mobile/src/cache/worktree-cache.ts new file mode 100644 index 00000000000..9654dd357ad --- /dev/null +++ b/mobile/src/cache/worktree-cache.ts @@ -0,0 +1,35 @@ +// Why: module-level cache lets the home screen pre-populate worktree data +// so the host detail page can render instantly on navigation instead of +// waiting for a fresh RPC connection + fetch cycle. + +type CachedWorktrees = { + worktrees: unknown[] + at: number +} + +const cache = new Map() + +const MAX_AGE_MS = 30_000 +const MAX_ENTRIES = 20 + +export function setCachedWorktrees(hostId: string, worktrees: unknown[]): void { + // Why: Map.set on an existing key does not move it to the end of iteration + // order. Delete first so the re-inserted key becomes the newest entry, + // giving us true LRU eviction when the cap is hit. + cache.delete(hostId) + cache.set(hostId, { worktrees, at: Date.now() }) + if (cache.size > MAX_ENTRIES) { + const oldest = cache.keys().next().value + if (oldest) cache.delete(oldest) + } +} + +export function getCachedWorktrees(hostId: string): unknown[] | null { + const entry = cache.get(hostId) + if (!entry) return null + if (Date.now() - entry.at > MAX_AGE_MS) { + cache.delete(hostId) + return null + } + return entry.worktrees +} diff --git a/mobile/src/components/ActionSheetModal.tsx b/mobile/src/components/ActionSheetModal.tsx new file mode 100644 index 00000000000..a68b08ea637 --- /dev/null +++ b/mobile/src/components/ActionSheetModal.tsx @@ -0,0 +1,133 @@ +import { View, Text, Pressable, StyleSheet } from 'react-native' +import { Edit3, Trash2, type LucideIcon } from 'lucide-react-native' +import { colors, spacing, typography } from '../theme/mobile-theme' +import { BottomDrawer } from './BottomDrawer' + +export type ActionSheetAction = { + label: string + icon?: LucideIcon + destructive?: boolean + skipAutoClose?: boolean + onPress: () => void +} + +type Props = { + visible: boolean + title?: string + message?: string + actions: ActionSheetAction[] + onClose: () => void +} + +function iconForAction(label: string, destructive?: boolean, icon?: LucideIcon): LucideIcon { + if (icon) return icon + if (destructive || /delete|remove/i.test(label)) return Trash2 + return Edit3 +} + +type ContentProps = { + title?: string + message?: string + actions: ActionSheetAction[] + onClose?: () => void +} + +export function ActionSheetContent({ title, message, actions, onClose }: ContentProps) { + return ( + <> + {(title || message) && ( + + {title ? ( + + {title} + + ) : null} + {message ? {message} : null} + + )} + + + {actions.map((action, i) => { + const Icon = iconForAction(action.label, action.destructive, action.icon) + return ( + + {i > 0 && } + [styles.action, pressed && styles.actionPressed]} + onPress={() => { + action.onPress() + if (!action.skipAutoClose && onClose) { + onClose() + } + }} + > + + + {action.label} + + + + ) + })} + + + ) +} + +export function ActionSheetModal({ visible, title, message, actions, onClose }: Props) { + return ( + + + + ) +} + +const styles = StyleSheet.create({ + header: { + paddingHorizontal: spacing.xs, + paddingBottom: spacing.sm + }, + title: { + fontSize: 13, + fontWeight: '500', + color: colors.textMuted + }, + message: { + fontSize: 12, + color: colors.textMuted, + marginTop: 2 + }, + actionGroup: { + backgroundColor: colors.bgPanel, + borderRadius: 12, + overflow: 'hidden' + }, + separator: { + height: StyleSheet.hairlineWidth, + backgroundColor: colors.borderSubtle, + marginHorizontal: spacing.md + }, + action: { + flexDirection: 'row', + alignItems: 'center', + gap: spacing.sm + 2, + paddingVertical: spacing.md, + paddingHorizontal: spacing.md + 2 + }, + actionPressed: { + backgroundColor: colors.bgRaised + }, + actionText: { + fontSize: typography.bodySize, + fontWeight: '500', + color: colors.textPrimary + }, + actionTextDestructive: { + color: colors.statusRed + } +}) diff --git a/mobile/src/components/AgentSpinner.tsx b/mobile/src/components/AgentSpinner.tsx new file mode 100644 index 00000000000..734cb8bbc6d --- /dev/null +++ b/mobile/src/components/AgentSpinner.tsx @@ -0,0 +1,62 @@ +import { useEffect, useRef } from 'react' +import { Animated, Easing, StyleSheet, View } from 'react-native' +import { colors } from '../theme/mobile-theme' + +type WorktreeStatus = 'working' | 'active' | 'permission' | 'done' | 'inactive' + +const STATUS_COLORS: Record = { + working: colors.statusGreen, + active: colors.statusGreen, + permission: colors.statusRed, + done: '#38bdf8', + inactive: '#666666' +} + +export function AgentSpinner({ status }: { status: WorktreeStatus }) { + const spinValue = useRef(new Animated.Value(0)).current + + useEffect(() => { + if (status === 'working') { + const animation = Animated.loop( + Animated.timing(spinValue, { + toValue: 1, + duration: 1000, + easing: Easing.linear, + useNativeDriver: true + }) + ) + animation.start() + return () => animation.stop() + } + spinValue.setValue(0) + }, [status, spinValue]) + + const color = STATUS_COLORS[status] ?? STATUS_COLORS.inactive + + if (status === 'working') { + const rotate = spinValue.interpolate({ + inputRange: [0, 1], + outputRange: ['0deg', '360deg'] + }) + return ( + + ) + } + + return +} + +const styles = StyleSheet.create({ + dot: { + width: 8, + height: 8, + borderRadius: 4 + }, + spinner: { + width: 10, + height: 10, + borderRadius: 5, + borderWidth: 2, + borderTopColor: 'transparent' + } +}) diff --git a/mobile/src/components/BottomDrawer.tsx b/mobile/src/components/BottomDrawer.tsx new file mode 100644 index 00000000000..2698353fb92 --- /dev/null +++ b/mobile/src/components/BottomDrawer.tsx @@ -0,0 +1,252 @@ +import { type ReactNode, useCallback, useEffect, useState } from 'react' +import { + View, + Pressable, + StyleSheet, + Platform, + useWindowDimensions, + ScrollView, + Keyboard, + BackHandler +} from 'react-native' +import { useSafeAreaInsets } from 'react-native-safe-area-context' +import { Gesture, GestureDetector, GestureHandlerRootView } from 'react-native-gesture-handler' +import Animated, { + useSharedValue, + useAnimatedStyle, + withSpring, + withTiming, + runOnJS, + interpolate, + Extrapolation +} from 'react-native-reanimated' +import { colors, spacing } from '../theme/mobile-theme' + +const DISMISS_THRESHOLD = 80 +const SPRING_CONFIG = { damping: 28, stiffness: 400 } +// Why: negative translateY (pulling up) is damped with a rubber-band factor +// so the drawer resists upward dragging — a subtle polish touch that signals +// the drawer cannot expand further. +const RUBBER_BAND_FACTOR = 0.25 +const SHOW_DURATION = 180 +const HIDE_DURATION = 150 + +type Props = { + visible: boolean + onClose: () => void + children: ReactNode +} + +export function BottomDrawer({ visible, onClose, children }: Props) { + const [mounted, setMounted] = useState(visible) + const translateY = useSharedValue(0) + const progress = useSharedValue(0) + const keyboardOffset = useSharedValue(0) + const { height: screenHeight } = useWindowDimensions() + const insets = useSafeAreaInsets() + + useEffect(() => { + if (visible) { + setMounted(true) + } + }, [visible]) + + useEffect(() => { + if (!mounted) return + + if (visible) { + translateY.value = 0 + progress.value = withTiming(1, { duration: SHOW_DURATION }) + } else { + Keyboard.dismiss() + progress.value = withTiming(0, { duration: HIDE_DURATION }, (finished) => { + if (finished) { + runOnJS(setMounted)(false) + } + }) + } + }, [mounted, visible]) + + // Why: KeyboardAvoidingView and useAnimatedKeyboard are both unreliable + // inside Modal (iOS ignores KAV; Android needs adjustNothing for + // useAnimatedKeyboard). Keyboard event listeners work on both platforms + // and give us the exact height to shift the drawer by. + useEffect(() => { + if (!visible) return + + const showEvent = Platform.OS === 'ios' ? 'keyboardWillShow' : 'keyboardDidShow' + const hideEvent = Platform.OS === 'ios' ? 'keyboardWillHide' : 'keyboardDidHide' + + const onShow = Keyboard.addListener(showEvent, (e) => { + const height = e.endCoordinates.height - insets.bottom + keyboardOffset.value = withTiming(Math.max(height, 0), { duration: e.duration || 250 }) + }) + const onHide = Keyboard.addListener(hideEvent, (e) => { + keyboardOffset.value = withTiming(0, { duration: e.duration || 250 }) + }) + + return () => { + onShow.remove() + onHide.remove() + keyboardOffset.value = 0 + } + }, [visible, insets.bottom]) + + useEffect(() => { + if (!visible) return + + const sub = BackHandler.addEventListener('hardwareBackPress', () => { + onClose() + return true + }) + return () => sub.remove() + }, [visible, onClose]) + + const dismiss = useCallback(() => { + onClose() + }, [onClose]) + + const panGesture = Gesture.Pan() + .onUpdate((e) => { + if (e.translationY > 0) { + translateY.value = e.translationY + } else { + translateY.value = e.translationY * RUBBER_BAND_FACTOR + } + }) + .onEnd((e) => { + if (e.translationY > DISMISS_THRESHOLD || e.velocityY > 500) { + const velocity = Math.max(e.velocityY, 800) + const remaining = screenHeight - e.translationY + const duration = Math.min(Math.max((remaining / velocity) * 1000, 120), 300) + translateY.value = withTiming(screenHeight, { duration }) + progress.value = withTiming(0, { duration }, () => { + runOnJS(dismiss)() + }) + } else { + translateY.value = withSpring(0, SPRING_CONFIG) + } + }) + + const drawerStyle = useAnimatedStyle(() => ({ + transform: [ + { + translateY: + interpolate(progress.value, [0, 1], [screenHeight, 0], Extrapolation.CLAMP) + + translateY.value - + keyboardOffset.value + } + ] + })) + + const backdropStyle = useAnimatedStyle(() => { + const dragFade = interpolate(translateY.value, [0, 300], [1, 0], Extrapolation.CLAMP) + return { opacity: progress.value * dragFade } + }) + + const pointerStyle = useAnimatedStyle( + () => + ({ + pointerEvents: progress.value > 0 ? 'auto' : 'none' + }) as { pointerEvents: 'auto' | 'none' } + ) + + // Why: hidden drawers can contain auto-focused inputs; keeping them mounted + // lets Android open the keyboard even when the drawer is offscreen. + if (!mounted) return null + + return ( + + + + + + + + + + + + + + + {children} + + + + + + + ) +} + +const styles = StyleSheet.create({ + overlay: { + ...StyleSheet.absoluteFillObject, + zIndex: 1000 + }, + root: { + flex: 1 + }, + backdrop: { + ...StyleSheet.absoluteFillObject, + backgroundColor: 'rgba(0,0,0,0.5)' + }, + anchor: { + flex: 1, + justifyContent: 'flex-end' + }, + drawer: { + backgroundColor: colors.bgBase, + borderTopLeftRadius: 16, + borderTopRightRadius: 16, + paddingHorizontal: spacing.md, + ...Platform.select({ + ios: { + shadowColor: '#000', + shadowOffset: { width: 0, height: -2 }, + shadowOpacity: 0.2, + shadowRadius: 10 + }, + android: { elevation: 8 } + }) + }, + handle: { + alignSelf: 'center', + width: 36, + height: 4, + borderRadius: 2, + backgroundColor: colors.textMuted, + opacity: 0.4 + }, + handleHitArea: { + alignItems: 'center', + paddingTop: spacing.sm, + paddingBottom: spacing.md + }, + bottomExtension: { + position: 'absolute', + bottom: -500, + left: 0, + right: 0, + height: 500, + backgroundColor: colors.bgBase + } +}) diff --git a/mobile/src/components/ConfirmModal.tsx b/mobile/src/components/ConfirmModal.tsx new file mode 100644 index 00000000000..a393593b5cf --- /dev/null +++ b/mobile/src/components/ConfirmModal.tsx @@ -0,0 +1,111 @@ +import { View, Text, Pressable, StyleSheet } from 'react-native' +import { colors, spacing, radii, typography } from '../theme/mobile-theme' +import { BottomDrawer } from './BottomDrawer' + +type Props = { + visible: boolean + title: string + message?: string + confirmLabel?: string + cancelLabel?: string + destructive?: boolean + onConfirm: () => void + onCancel: () => void +} + +export function ConfirmModal({ + visible, + title, + message, + confirmLabel = 'Confirm', + cancelLabel = 'Cancel', + destructive = false, + onConfirm, + onCancel +}: Props) { + return ( + + + {title} + {message ? {message} : null} + + + [styles.button, styles.cancelButton, pressed && styles.pressed]} + onPress={onCancel} + > + {cancelLabel} + + [ + styles.button, + destructive ? styles.destructiveButton : styles.confirmButton, + pressed && styles.pressed + ]} + onPress={() => { + onConfirm() + onCancel() + }} + > + + {confirmLabel} + + + + + ) +} + +const styles = StyleSheet.create({ + content: { + paddingBottom: spacing.lg + }, + title: { + fontSize: 16, + fontWeight: '700', + color: colors.textPrimary + }, + message: { + fontSize: typography.bodySize, + color: colors.textSecondary, + marginTop: spacing.xs, + lineHeight: 20 + }, + buttons: { + flexDirection: 'row', + gap: spacing.sm + }, + button: { + flex: 1, + paddingVertical: spacing.sm + 2, + borderRadius: radii.button, + alignItems: 'center' + }, + cancelButton: { + backgroundColor: colors.bgPanel + }, + confirmButton: { + backgroundColor: colors.textPrimary + }, + destructiveButton: { + backgroundColor: colors.statusRed + }, + pressed: { + opacity: 0.7 + }, + cancelText: { + fontSize: typography.bodySize, + fontWeight: '600', + color: colors.textSecondary + }, + confirmText: { + fontSize: typography.bodySize, + fontWeight: '600', + color: colors.bgBase + }, + destructiveText: { + fontSize: typography.bodySize, + fontWeight: '600', + color: '#fff' + } +}) diff --git a/mobile/src/components/CustomKeyModal.tsx b/mobile/src/components/CustomKeyModal.tsx new file mode 100644 index 00000000000..ba264548dde --- /dev/null +++ b/mobile/src/components/CustomKeyModal.tsx @@ -0,0 +1,338 @@ +import { useState, useEffect, useCallback } from 'react' +import { View, Text, Pressable, TextInput, StyleSheet, ScrollView, Switch } from 'react-native' +import { ChevronLeft } from 'lucide-react-native' +import AsyncStorage from '@react-native-async-storage/async-storage' +import { colors, spacing, radii, typography } from '../theme/mobile-theme' +import { BottomDrawer } from './BottomDrawer' + +const STORAGE_KEY = 'orca:custom-accessory-keys' + +export type CustomKey = { + id: string + label: string + bytes: string + enter: boolean +} + +type Step = 'choose-type' | 'pick-ctrl' | 'pick-alt' | 'text-macro' + +const ALPHA_KEYS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.split('') + +function ctrlBytes(letter: string): string { + return String.fromCharCode(letter.toUpperCase().charCodeAt(0) - 64) +} + +function altBytes(letter: string): string { + return `\x1b${letter.toLowerCase()}` +} + +type Props = { + visible: boolean + onClose: () => void + onKeysChanged: (keys: CustomKey[]) => void +} + +export async function loadCustomKeys(): Promise { + try { + const raw = await AsyncStorage.getItem(STORAGE_KEY) + return raw ? (JSON.parse(raw) as CustomKey[]) : [] + } catch { + return [] + } +} + +async function saveCustomKeys(keys: CustomKey[]): Promise { + await AsyncStorage.setItem(STORAGE_KEY, JSON.stringify(keys)) +} + +export function CustomKeyModal({ visible, onClose, onKeysChanged }: Props) { + const [step, setStep] = useState('choose-type') + const [macroLabel, setMacroLabel] = useState('') + const [macroText, setMacroText] = useState('') + const [macroEnter, setMacroEnter] = useState(true) + + useEffect(() => { + if (visible) { + setStep('choose-type') + setMacroLabel('') + setMacroText('') + setMacroEnter(true) + } + }, [visible]) + + const addKey = useCallback( + async (key: Omit) => { + const existing = await loadCustomKeys() + const newKey: CustomKey = { ...key, id: `custom-${Date.now()}` } + const updated = [...existing, newKey] + await saveCustomKeys(updated) + onKeysChanged(updated) + onClose() + }, + [onClose, onKeysChanged] + ) + + const handleCtrlKey = useCallback( + (letter: string) => { + void addKey({ label: `Ctrl+${letter}`, bytes: ctrlBytes(letter), enter: false }) + }, + [addKey] + ) + + const handleAltKey = useCallback( + (letter: string) => { + void addKey({ label: `Alt+${letter}`, bytes: altBytes(letter), enter: false }) + }, + [addKey] + ) + + const handleMacroSave = useCallback(() => { + const label = macroLabel.trim() || macroText.trim().slice(0, 12) + const text = macroText + if (!label || !text) return + const bytes = macroEnter ? `${text}\r` : text + void addKey({ label, bytes, enter: false }) + }, [addKey, macroLabel, macroText, macroEnter]) + + const showBack = step !== 'choose-type' + + return ( + + + {showBack ? ( + [styles.backButton, pressed && styles.backButtonPressed]} + onPress={() => setStep('choose-type')} + accessibilityLabel="Back" + > + + + ) : ( + + )} + + {step === 'choose-type' && 'Add Shortcut'} + {step === 'pick-ctrl' && 'Ctrl + Key'} + {step === 'pick-alt' && 'Alt + Key'} + {step === 'text-macro' && 'Text Macro'} + + + + + {step === 'choose-type' && ( + + [styles.row, pressed && styles.rowPressed]} + onPress={() => setStep('pick-ctrl')} + > + Ctrl + Key + Control character shortcuts + + + [styles.row, pressed && styles.rowPressed]} + onPress={() => setStep('pick-alt')} + > + Alt + Key + Alt/Option key combos + + + [styles.row, pressed && styles.rowPressed]} + onPress={() => setStep('text-macro')} + > + Text Macro + Send custom text command + + + )} + + {(step === 'pick-ctrl' || step === 'pick-alt') && ( + + + {ALPHA_KEYS.map((letter) => ( + [styles.keyCell, pressed && styles.keyCellPressed]} + onPress={() => + step === 'pick-ctrl' ? handleCtrlKey(letter) : handleAltKey(letter) + } + > + {letter} + + ))} + + + )} + + {step === 'text-macro' && ( + + + Label + + Command + + + Press Enter + + + + Add Shortcut + + + + )} + + ) +} + +const styles = StyleSheet.create({ + header: { + flexDirection: 'row', + alignItems: 'center', + paddingBottom: spacing.sm + }, + backButton: { + width: 30, + height: 30, + borderRadius: 15, + alignItems: 'center', + justifyContent: 'center' + }, + backButtonPressed: { + backgroundColor: colors.bgRaised + }, + backSpacer: { + width: 30 + }, + title: { + flex: 1, + fontSize: 15, + fontWeight: '600', + color: colors.textPrimary, + textAlign: 'center' + }, + group: { + backgroundColor: colors.bgPanel, + borderRadius: 12, + overflow: 'hidden' + }, + separator: { + height: StyleSheet.hairlineWidth, + backgroundColor: colors.borderSubtle, + marginHorizontal: spacing.md + }, + row: { + paddingVertical: spacing.md, + paddingHorizontal: spacing.md + 2 + }, + rowPressed: { + backgroundColor: colors.bgRaised + }, + rowLabel: { + fontSize: typography.bodySize, + fontWeight: '500', + color: colors.textPrimary, + marginBottom: 1 + }, + rowHint: { + fontSize: 12, + color: colors.textMuted + }, + keyGridScroll: { + maxHeight: 240 + }, + keyGrid: { + flexDirection: 'row', + flexWrap: 'wrap', + gap: spacing.xs, + justifyContent: 'center', + padding: spacing.md + }, + keyCell: { + width: 42, + height: 38, + borderRadius: radii.button, + backgroundColor: colors.bgBase, + alignItems: 'center', + justifyContent: 'center' + }, + keyCellPressed: { + backgroundColor: colors.bgRaised + }, + keyCellText: { + fontSize: 15, + fontWeight: '600', + color: colors.textPrimary, + fontFamily: typography.monoFamily + }, + macroForm: { + padding: spacing.md, + gap: spacing.sm + }, + fieldLabel: { + fontSize: 13, + fontWeight: '500', + color: colors.textSecondary + }, + fieldInput: { + backgroundColor: colors.bgBase, + color: colors.textPrimary, + borderRadius: radii.input, + paddingHorizontal: spacing.md, + paddingVertical: spacing.sm, + fontSize: 14, + fontFamily: typography.monoFamily, + borderWidth: 1, + borderColor: colors.borderSubtle + }, + switchRow: { + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'space-between', + paddingVertical: spacing.xs + }, + switchLabel: { + fontSize: typography.bodySize, + color: colors.textPrimary + }, + saveButton: { + backgroundColor: colors.textPrimary, + paddingVertical: spacing.sm + 2, + borderRadius: radii.button, + alignItems: 'center' + }, + saveButtonDisabled: { + opacity: 0.5 + }, + saveButtonText: { + color: colors.bgBase, + fontSize: typography.bodySize, + fontWeight: '600' + } +}) diff --git a/mobile/src/components/NewWorktreeModal.tsx b/mobile/src/components/NewWorktreeModal.tsx new file mode 100644 index 00000000000..63d00cf8b0a --- /dev/null +++ b/mobile/src/components/NewWorktreeModal.tsx @@ -0,0 +1,756 @@ +import { useState, useEffect } from 'react' +import { + View, + Text, + TextInput, + Pressable, + Switch, + StyleSheet, + Platform, + ActivityIndicator, + Image +} from 'react-native' +import { ChevronDown, ChevronUp, Check, Terminal } from 'lucide-react-native' +import Svg, { Path, G } from 'react-native-svg' +import type { RpcClient } from '../transport/rpc-client' +import type { RpcSuccess } from '../transport/types' +import { colors, spacing, radii, typography } from '../theme/mobile-theme' +import { BottomDrawer } from './BottomDrawer' + +type Repo = { + id: string + displayName: string + path: string +} + +type AgentOption = { + id: string + label: string + faviconDomain?: string +} + +// Why: matches the AGENT_CATALOG ordering and faviconDomain values from +// src/renderer/src/lib/agent-catalog.tsx so mobile uses the same icon sources. +const AGENT_OPTIONS: AgentOption[] = [ + { id: 'claude', label: 'Claude' }, + { id: 'codex', label: 'Codex' }, + { id: 'copilot', label: 'GitHub Copilot', faviconDomain: 'github.com' }, + { id: 'opencode', label: 'OpenCode', faviconDomain: 'opencode.ai' }, + { id: 'pi', label: 'Pi' }, + { id: 'gemini', label: 'Gemini', faviconDomain: 'gemini.google.com' }, + { id: 'aider', label: 'Aider' }, + { id: 'goose', label: 'Goose', faviconDomain: 'goose-docs.ai' }, + { id: 'amp', label: 'Amp', faviconDomain: 'ampcode.com' }, + { id: 'kilo', label: 'Kilocode', faviconDomain: 'kilo.ai' }, + { id: 'kiro', label: 'Kiro', faviconDomain: 'kiro.dev' }, + { id: 'crush', label: 'Charm', faviconDomain: 'charm.sh' }, + { id: 'aug', label: 'Auggie', faviconDomain: 'augmentcode.com' }, + { id: 'cline', label: 'Cline', faviconDomain: 'cline.bot' }, + { id: 'codebuff', label: 'Codebuff', faviconDomain: 'codebuff.com' }, + { id: 'continue', label: 'Continue', faviconDomain: 'continue.dev' }, + { id: 'cursor', label: 'Cursor', faviconDomain: 'cursor.com' }, + { id: 'droid', label: 'Droid', faviconDomain: 'factory.ai' }, + { id: 'kimi', label: 'Kimi', faviconDomain: 'moonshot.cn' }, + { id: 'mistral-vibe', label: 'Mistral Vibe', faviconDomain: 'mistral.ai' }, + { id: 'qwen-code', label: 'Qwen Code', faviconDomain: 'qwenlm.github.io' }, + { id: 'rovo', label: 'Rovo Dev', faviconDomain: 'atlassian.com' }, + { id: 'hermes', label: 'Hermes', faviconDomain: 'nousresearch.com' } +] + +const BLANK_TERMINAL: AgentOption = { id: '__blank__', label: 'Blank Terminal' } +const ALL_AGENTS = [...AGENT_OPTIONS, BLANK_TERMINAL] + +// Why: mirrors launchCmd from src/shared/tui-agent-config.ts so terminal.create +// gets the correct binary name for each agent. +const AGENT_COMMANDS: Record = { + claude: 'claude', + codex: 'codex', + copilot: 'copilot', + opencode: 'opencode', + pi: 'pi', + gemini: 'gemini', + aider: 'aider', + goose: 'goose', + amp: 'amp', + kilo: 'kilo', + kiro: 'kiro', + crush: 'crush', + aug: 'auggie', + cline: 'cline', + codebuff: 'codebuff', + continue: 'continue', + cursor: 'cursor-agent', + droid: 'droid', + kimi: 'kimi', + 'mistral-vibe': 'mistral-vibe', + 'qwen-code': 'qwen-code', + rovo: 'rovo', + hermes: 'hermes' +} + +// ── Agent icons ───────────────────────────────────────────────────── +// SVG paths sourced from the desktop codebase: +// Claude & OpenAI: src/renderer/src/components/status-bar/icons.tsx +// Pi & Aider: src/renderer/src/lib/agent-catalog.tsx +// Agents with a faviconDomain use Google's favicon service (same as desktop). + +function ClaudeIcon({ size = 16 }: { size?: number }) { + return ( + + + + ) +} + +function OpenAIIcon({ size = 16 }: { size?: number }) { + return ( + + + + ) +} + +function PiIcon({ size = 16 }: { size?: number }) { + return ( + + + + + ) +} + +function AiderIcon({ size = 16 }: { size?: number }) { + return ( + + + + + + + ) +} + +function FaviconIcon({ domain, size = 16 }: { domain: string; size?: number }) { + return ( + + ) +} + +function AgentLetterIcon({ letter, size = 16 }: { letter: string; size?: number }) { + return ( + + + {letter} + + + ) +} + +function AgentIcon({ agentId, size = 16 }: { agentId: string; size?: number }) { + if (agentId === 'claude') return + if (agentId === 'codex') return + if (agentId === 'pi') return + if (agentId === 'aider') return + if (agentId === '__blank__') return + + const agent = AGENT_OPTIONS.find((a) => a.id === agentId) + if (agent?.faviconDomain) { + return + } + const label = agent?.label ?? agentId + return +} + +// ── Picker sub-modal ──────────────────────────────────────────────── +// Why: inline dropdowns with position:absolute + ScrollView have persistent +// touch-conflict issues in React Native. A separate modal for the picker +// list is the standard mobile pattern — it scrolls reliably and feels native. + +function PickerListModal({ + visible, + title, + items, + selectedId, + onSelect, + onClose, + renderIcon +}: { + visible: boolean + title: string + items: T[] + selectedId: string + onSelect: (item: T) => void + onClose: () => void + renderIcon?: (item: T) => React.ReactNode +}) { + return ( + + + {title} + + + {items.map((item, index) => { + const selected = item.id === selectedId + return ( + + {index > 0 && } + [styles.pickerItem, pressed && styles.pickerItemPressed]} + onPress={() => { + onSelect(item) + onClose() + }} + > + {renderIcon?.(item)} + + {item.label} + + {selected && } + + + ) + })} + + + ) +} + +// ── Main modal ────────────────────────────────────────────────────── + +type Props = { + visible: boolean + client: RpcClient | null + onCreated: (worktreeId: string, name: string) => void + onClose: () => void +} + +export function NewWorktreeModal({ visible, client, onCreated, onClose }: Props) { + const [repos, setRepos] = useState([]) + const [selectedRepo, setSelectedRepo] = useState(null) + const [showRepoPicker, setShowRepoPicker] = useState(false) + const [selectedAgent, setSelectedAgent] = useState(AGENT_OPTIONS[0]!) + const [showAgentPicker, setShowAgentPicker] = useState(false) + const [name, setName] = useState('') + const [note, setNote] = useState('') + const [showAdvanced, setShowAdvanced] = useState(false) + const [setupCommand, setSetupCommand] = useState(null) + const [setupSource, setSetupSource] = useState(null) + const [runSetup, setRunSetup] = useState(true) + const [creating, setCreating] = useState(false) + const [error, setError] = useState('') + const [loading, setLoading] = useState(true) + + useEffect(() => { + if (!visible) { + setShowRepoPicker(false) + setShowAgentPicker(false) + return + } + if (!client) return + let stale = false + setName('') + setNote('') + setShowAdvanced(false) + setSetupCommand(null) + setSetupSource(null) + setRunSetup(true) + setError('') + setCreating(false) + setShowRepoPicker(false) + setShowAgentPicker(false) + setSelectedAgent(AGENT_OPTIONS[0]!) + setLoading(true) + + void (async () => { + try { + const response = await client.sendRequest('repo.list') + if (stale) return + if (response.ok) { + const result = (response as RpcSuccess).result as { repos: Repo[] } + setRepos(result.repos) + if (result.repos.length === 1) { + setSelectedRepo(result.repos[0]!) + } else { + setSelectedRepo(null) + } + } + } catch { + if (!stale) setRepos([]) + } finally { + if (!stale) setLoading(false) + } + })() + return () => { + stale = true + } + }, [visible, client]) + + useEffect(() => { + if (!client || !selectedRepo) { + setSetupCommand(null) + setSetupSource(null) + return + } + let stale = false + void (async () => { + try { + const response = await client.sendRequest('repo.hooks', { + repo: `id:${selectedRepo.id}` + }) + if (stale) return + if (response.ok) { + const result = (response as RpcSuccess).result as { + hooks: { scripts: { setup?: string } } | null + source: string | null + setupRunPolicy: string + } + const cmd = result.hooks?.scripts.setup ?? null + setSetupCommand(cmd) + setSetupSource(result.source) + setRunSetup(result.setupRunPolicy !== 'skip-by-default') + } + } catch { + if (!stale) { + setSetupCommand(null) + setSetupSource(null) + } + } + })() + return () => { + stale = true + } + }, [client, selectedRepo]) + + async function handleCreate() { + if (!client || !selectedRepo) return + setCreating(true) + setError('') + + try { + const command = + selectedAgent.id !== '__blank__' ? AGENT_COMMANDS[selectedAgent.id] : undefined + + const params: Record = { + repo: `id:${selectedRepo.id}`, + startupCommand: command, + setupDecision: runSetup ? 'inherit' : 'skip' + } + if (name.trim()) params.name = name.trim() + if (note.trim()) params.comment = note.trim() + + const response = await client.sendRequest('worktree.create', params) + + if (response.ok) { + const result = (response as RpcSuccess).result as { worktree: { id: string } } + const worktreeId = result.worktree.id + + onClose() + onCreated(worktreeId, name.trim() || 'New workspace') + } else { + setError(response.error.message) + } + } catch (e) { + setError(e instanceof Error ? e.message : 'Failed to create workspace') + } finally { + setCreating(false) + } + } + + const canCreate = selectedRepo != null && !creating + + return ( + <> + + + Create Workspace + + Pick a repository and agent to spin up a new workspace. + + + + {loading ? ( + + + + ) : repos.length === 0 ? ( + + No repositories found + + ) : ( + <> + + Repository + setShowRepoPicker(true)}> + + {selectedRepo?.displayName ?? 'Select a repository'} + + + + + + + + Workspace Name [Optional] + + { + setName(t) + setError('') + }} + placeholder="Workspace name" + placeholderTextColor={colors.textMuted} + autoCapitalize="none" + autoCorrect={false} + autoFocus={repos.length <= 1} + returnKeyType="done" + onSubmitEditing={() => { + if (canCreate) void handleCreate() + }} + /> + + + + Agent + setShowAgentPicker(true)}> + + + {selectedAgent.label} + + + + + + setShowAdvanced(!showAdvanced)}> + Advanced + {showAdvanced ? ( + + ) : ( + + )} + + + {showAdvanced && ( + <> + + Note + + + + {setupCommand ? ( + + + Setup script + {setupSource && ( + + + {setupSource === 'orca.yaml' ? 'ORCA.YAML' : 'HOOKS'} + + + )} + + + + Run setup command + + + + {setupCommand} + + + + ) : null} + + )} + + {error ? {error} : null} + + + void handleCreate()} + > + {creating ? ( + + ) : ( + Create Workspace + )} + + + + )} + + + {/* Sub-modals for pickers — rendered outside the main modal so they + layer on top and scroll without touch conflicts. */} + ({ id: r.id, label: r.displayName, _repo: r }))} + selectedId={selectedRepo?.id ?? ''} + onSelect={(item) => setSelectedRepo((item as { _repo: Repo })._repo)} + onClose={() => setShowRepoPicker(false)} + /> + + setSelectedAgent(agent)} + onClose={() => setShowAgentPicker(false)} + renderIcon={(agent) => } + /> + + ) +} + +const styles = StyleSheet.create({ + header: { + paddingHorizontal: spacing.xs, + marginBottom: spacing.md + }, + title: { + fontSize: 15, + fontWeight: '600', + color: colors.textPrimary + }, + subtitle: { + fontSize: 13, + color: colors.textMuted, + marginTop: 2 + }, + loadingContainer: { + paddingVertical: spacing.xl, + alignItems: 'center' + }, + emptyText: { + color: colors.textSecondary, + fontSize: typography.bodySize + }, + field: { + marginBottom: spacing.md + }, + label: { + fontSize: 13, + fontWeight: '500', + color: colors.textSecondary, + marginBottom: spacing.xs + }, + labelHint: { + fontWeight: '400', + color: colors.textMuted + }, + fieldButton: { + flexDirection: 'row', + alignItems: 'center', + gap: spacing.sm, + backgroundColor: colors.bgRaised, + borderRadius: radii.input, + paddingHorizontal: spacing.md, + paddingVertical: Platform.OS === 'ios' ? spacing.sm + 2 : spacing.sm, + borderWidth: 1, + borderColor: colors.borderSubtle + }, + fieldButtonText: { + flex: 1, + fontSize: typography.bodySize, + color: colors.textPrimary + }, + fieldButtonPlaceholder: { + color: colors.textMuted + }, + input: { + backgroundColor: colors.bgRaised, + color: colors.textPrimary, + borderRadius: radii.input, + paddingHorizontal: spacing.md, + paddingVertical: Platform.OS === 'ios' ? spacing.sm + 2 : spacing.sm, + fontSize: typography.bodySize, + borderWidth: 1, + borderColor: colors.borderSubtle + }, + error: { + color: colors.statusRed, + fontSize: 13, + marginBottom: spacing.md + }, + advancedToggle: { + flexDirection: 'row', + alignItems: 'center', + gap: spacing.xs, + paddingVertical: spacing.sm, + marginBottom: spacing.xs + }, + advancedText: { + fontSize: typography.bodySize, + fontWeight: '500', + color: colors.textSecondary + }, + setupHeader: { + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'space-between', + marginBottom: spacing.xs + }, + sourceBadge: { + backgroundColor: colors.bgRaised, + borderRadius: 4, + paddingHorizontal: spacing.xs + 2, + paddingVertical: 2 + }, + sourceBadgeText: { + fontSize: 10, + fontWeight: '600', + color: colors.textMuted, + letterSpacing: 0.5 + }, + setupBox: { + backgroundColor: colors.bgRaised, + borderRadius: radii.input, + borderWidth: 1, + borderColor: colors.borderSubtle, + padding: spacing.md + }, + setupToggleRow: { + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'space-between', + marginBottom: spacing.sm + }, + setupToggleLabel: { + fontSize: 13, + color: colors.textSecondary + }, + setupSwitch: { + transform: [{ scaleX: 0.7 }, { scaleY: 0.7 }] + }, + setupCommandBlock: { + backgroundColor: colors.bgBase, + borderRadius: 6, + paddingHorizontal: spacing.sm + 2, + paddingVertical: spacing.sm + }, + setupCommand: { + fontSize: 13, + fontFamily: typography.monoFamily, + color: colors.textPrimary + }, + actions: { + flexDirection: 'row', + justifyContent: 'flex-end', + marginTop: spacing.sm + }, + createButton: { + backgroundColor: colors.textPrimary, + paddingHorizontal: spacing.lg, + paddingVertical: spacing.sm, + borderRadius: radii.button, + minWidth: 160, + alignItems: 'center' + }, + createButtonDisabled: { + opacity: 0.4 + }, + createText: { + color: colors.bgBase, + fontSize: typography.bodySize, + fontWeight: '600' + }, + letterIcon: { + alignItems: 'center', + justifyContent: 'center' + }, + letterIconText: { + fontWeight: '700' + }, + + // Picker sub-modal styles + pickerHeader: { + paddingHorizontal: spacing.xs, + paddingBottom: spacing.sm + }, + pickerTitle: { + fontSize: 13, + fontWeight: '500', + color: colors.textMuted + }, + pickerGroup: { + backgroundColor: colors.bgPanel, + borderRadius: 12, + overflow: 'hidden' + }, + pickerSeparator: { + height: StyleSheet.hairlineWidth, + backgroundColor: colors.borderSubtle, + marginHorizontal: spacing.md + }, + pickerList: { + flexGrow: 0 + }, + pickerItem: { + flexDirection: 'row', + alignItems: 'center', + gap: spacing.sm, + paddingVertical: spacing.md, + paddingHorizontal: spacing.md + 2 + }, + pickerItemPressed: { + backgroundColor: colors.bgRaised + }, + pickerItemText: { + flex: 1, + fontSize: typography.bodySize, + color: colors.textPrimary + }, + pickerItemTextSelected: { + fontWeight: '600' + } +}) diff --git a/mobile/src/components/OrcaLogo.tsx b/mobile/src/components/OrcaLogo.tsx new file mode 100644 index 00000000000..d05e4f2a968 --- /dev/null +++ b/mobile/src/components/OrcaLogo.tsx @@ -0,0 +1,23 @@ +import Svg, { G, Path, Defs } from 'react-native-svg' +import { colors } from '../theme/mobile-theme' + +type Props = { + size?: number + color?: string +} + +export function OrcaLogo({ size = 24, color = colors.textPrimary }: Props) { + const aspectRatio = 318.6 / 202.67 + const width = size * aspectRatio + return ( + + + + + + + ) +} diff --git a/mobile/src/components/PickerModal.tsx b/mobile/src/components/PickerModal.tsx new file mode 100644 index 00000000000..eecc653e8da --- /dev/null +++ b/mobile/src/components/PickerModal.tsx @@ -0,0 +1,108 @@ +import { View, Text, Pressable, StyleSheet } from 'react-native' +import { Check } from 'lucide-react-native' +import { colors, spacing, typography } from '../theme/mobile-theme' +import { BottomDrawer } from './BottomDrawer' + +export type PickerOption = { + value: T + label: string + subtitle?: string +} + +type Props = { + visible: boolean + title: string + options: PickerOption[] + selected: T + onSelect: (value: T) => void + onClose: () => void +} + +export function PickerModal({ + visible, + title, + options, + selected, + onSelect, + onClose +}: Props) { + return ( + + + {title} + + + + {options.map((opt, i) => { + const isSelected = opt.value === selected + return ( + + {i > 0 && } + [styles.row, pressed && styles.rowPressed]} + onPress={() => { + onSelect(opt.value) + onClose() + }} + > + + + {opt.label} + + {opt.subtitle ? {opt.subtitle} : null} + + {isSelected && } + + + ) + })} + + + ) +} + +const styles = StyleSheet.create({ + header: { + paddingHorizontal: spacing.xs, + paddingBottom: spacing.sm + }, + title: { + fontSize: 13, + fontWeight: '500', + color: colors.textMuted + }, + group: { + backgroundColor: colors.bgPanel, + borderRadius: 12, + overflow: 'hidden' + }, + separator: { + height: StyleSheet.hairlineWidth, + backgroundColor: colors.borderSubtle, + marginHorizontal: spacing.md + }, + row: { + flexDirection: 'row', + alignItems: 'center', + paddingVertical: spacing.md, + paddingHorizontal: spacing.md + 2 + }, + rowPressed: { + backgroundColor: colors.bgRaised + }, + rowContent: { + flex: 1 + }, + rowLabel: { + fontSize: typography.bodySize, + color: colors.textPrimary + }, + rowLabelSelected: { + fontWeight: '600' + }, + rowSubtitle: { + fontSize: 11, + color: colors.textMuted, + marginTop: 1 + } +}) diff --git a/mobile/src/components/StatusDot.tsx b/mobile/src/components/StatusDot.tsx new file mode 100644 index 00000000000..63777890d70 --- /dev/null +++ b/mobile/src/components/StatusDot.tsx @@ -0,0 +1,25 @@ +import { View, StyleSheet } from 'react-native' +import { colors } from '../theme/mobile-theme' +import type { ConnectionState } from '../transport/types' + +const stateColors: Record = { + connected: colors.statusGreen, + connecting: colors.statusAmber, + handshaking: colors.statusAmber, + reconnecting: colors.statusAmber, + disconnected: colors.statusRed, + 'auth-failed': colors.statusRed +} + +export function StatusDot({ state }: { state: ConnectionState }) { + return +} + +const styles = StyleSheet.create({ + dot: { + width: 8, + height: 8, + borderRadius: 4, + marginRight: 8 + } +}) diff --git a/mobile/src/components/TextInputModal.tsx b/mobile/src/components/TextInputModal.tsx new file mode 100644 index 00000000000..70a9f323f56 --- /dev/null +++ b/mobile/src/components/TextInputModal.tsx @@ -0,0 +1,151 @@ +import { useState, useEffect } from 'react' +import { View, Text, TextInput, Pressable, StyleSheet, Platform } from 'react-native' +import { colors, spacing, radii, typography } from '../theme/mobile-theme' +import { BottomDrawer } from './BottomDrawer' + +type Props = { + visible: boolean + title: string + message?: string + defaultValue?: string + placeholder?: string + onSubmit: (value: string) => void + onCancel: () => void +} + +export function TextInputModal({ + visible, + title, + message, + defaultValue = '', + placeholder, + onSubmit, + onCancel +}: Props) { + const [value, setValue] = useState(defaultValue) + + useEffect(() => { + if (visible) setValue(defaultValue) + }, [visible, defaultValue]) + + function handleSubmit() { + if (value.trim()) { + onSubmit(value.trim()) + } + } + + return ( + + + {title} + {message ? {message} : null} + + + + + + + + + + [styles.cancelButton, pressed && styles.buttonPressed]} + onPress={onCancel} + > + Cancel + + [ + styles.submitButton, + pressed && styles.buttonPressed, + !value.trim() && styles.submitButtonDisabled + ]} + disabled={!value.trim()} + onPress={handleSubmit} + > + Save + + + + ) +} + +const styles = StyleSheet.create({ + header: { + paddingHorizontal: spacing.xs, + paddingBottom: spacing.sm + }, + title: { + fontSize: 15, + fontWeight: '600', + color: colors.textPrimary + }, + message: { + fontSize: 13, + color: colors.textMuted, + marginTop: 2 + }, + group: { + backgroundColor: colors.bgPanel, + borderRadius: 12, + overflow: 'hidden' + }, + inputWrap: { + padding: spacing.md + }, + input: { + backgroundColor: colors.bgBase, + color: colors.textPrimary, + borderRadius: radii.input, + paddingHorizontal: spacing.md, + paddingVertical: Platform.OS === 'ios' ? spacing.sm + 2 : spacing.sm, + fontSize: typography.bodySize, + borderWidth: 1, + borderColor: colors.borderSubtle + }, + actions: { + flexDirection: 'row', + justifyContent: 'flex-end', + gap: spacing.sm, + marginTop: spacing.md + }, + cancelButton: { + paddingHorizontal: spacing.lg, + paddingVertical: spacing.sm, + borderRadius: radii.button + }, + submitButton: { + backgroundColor: colors.accentBlue, + paddingHorizontal: spacing.lg, + paddingVertical: spacing.sm, + borderRadius: radii.button + }, + buttonPressed: { + opacity: 0.7 + }, + submitButtonDisabled: { + opacity: 0.4 + }, + cancelText: { + color: colors.textSecondary, + fontSize: typography.bodySize, + fontWeight: '500' + }, + submitText: { + color: '#fff', + fontSize: typography.bodySize, + fontWeight: '600' + } +}) diff --git a/mobile/src/notifications/mobile-notifications.ts b/mobile/src/notifications/mobile-notifications.ts new file mode 100644 index 00000000000..287de38a0ec --- /dev/null +++ b/mobile/src/notifications/mobile-notifications.ts @@ -0,0 +1,111 @@ +import * as Notifications from 'expo-notifications' +import { Platform } from 'react-native' +import type { RpcClient } from '../transport/rpc-client' +import { loadPushNotificationsEnabled } from '../storage/preferences' + +type NotificationEvent = { + type: 'notification' + source: 'agent-task-complete' | 'terminal-bell' | 'test' + title: string + body: string + worktreeId?: string +} + +type SubscribeResult = { + type: 'ready' + subscriptionId: string +} + +let permissionGranted: boolean | null = null + +// Why: permissions must be requested before scheduling any local notification. +// Cache the result so we only prompt once per app session. +export async function ensureNotificationPermissions(): Promise { + if (permissionGranted !== null) return permissionGranted + + const { status: existingStatus } = await Notifications.getPermissionsAsync() + if (existingStatus === 'granted') { + permissionGranted = true + return true + } + + const { status } = await Notifications.requestPermissionsAsync() + permissionGranted = status === 'granted' + return permissionGranted +} + +function configureNotificationChannel(): void { + if (Platform.OS === 'android') { + void Notifications.setNotificationChannelAsync('orca-desktop', { + name: 'Desktop Notifications', + importance: Notifications.AndroidImportance.HIGH, + vibrationPattern: [0, 250], + lightColor: '#6366f1' + }) + } +} + +async function showLocalNotification(event: NotificationEvent): Promise { + const enabled = await loadPushNotificationsEnabled() + if (!enabled) return + + const granted = await ensureNotificationPermissions() + if (!granted) return + + await Notifications.scheduleNotificationAsync({ + content: { + title: event.title, + body: event.body, + data: { source: event.source, worktreeId: event.worktreeId }, + ...(Platform.OS === 'android' ? { channelId: 'orca-desktop' } : {}) + }, + trigger: null + }) +} + +// Why: each host connection gets its own notification subscription. When the +// connection drops, the unsubscribe function cleans up the streaming RPC. +// Returns an unsubscribe function. +export function subscribeToDesktopNotifications(client: RpcClient): () => void { + configureNotificationChannel() + + let subscriptionId: string | null = null + let disposed = false + function unsubscribeServer(id: string) { + if (client.getState() === 'connected') { + client.sendRequest('notifications.unsubscribe', { subscriptionId: id }).catch(() => {}) + } + } + + const unsubscribeStream = client.subscribe('notifications.subscribe', {}, (data: unknown) => { + const event = data as NotificationEvent | SubscribeResult | { type: 'end' } + if (event.type === 'ready') { + subscriptionId = (event as SubscribeResult).subscriptionId + if (disposed) { + unsubscribeServer(subscriptionId) + unsubscribeStream() + } + return + } + if (event.type === 'end') { + if (disposed) unsubscribeStream() + return + } + if (disposed) return + if (event.type === 'notification') { + void showLocalNotification(event as NotificationEvent) + } + }) + + return () => { + disposed = true + // Why: the client may already be closed when this cleanup runs (component + // unmount races with disconnect). sendRequest rejects immediately on a + // closed client — swallow it since server-side cleanup happens via + // connection-close anyway. + if (subscriptionId) { + unsubscribeStream() + unsubscribeServer(subscriptionId) + } + } +} diff --git a/mobile/src/platform/haptics.ts b/mobile/src/platform/haptics.ts new file mode 100644 index 00000000000..72bf2443a82 --- /dev/null +++ b/mobile/src/platform/haptics.ts @@ -0,0 +1,13 @@ +import { Platform } from 'react-native' +import * as Haptics from 'expo-haptics' + +export function triggerMediumImpact(): void { + if (Platform.OS === 'android') { + // Why: Android's Vibrator API (used by impactAsync) is unreliable for haptic + // feedback. performAndroidHapticsAsync uses the native HapticFeedbackConstants + // API which works without VIBRATE permission and feels more natural. + void Haptics.performAndroidHapticsAsync(Haptics.AndroidHaptics.Long_Press).catch(() => {}) + } else { + void Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Medium).catch(() => {}) + } +} diff --git a/mobile/src/storage/preferences.ts b/mobile/src/storage/preferences.ts new file mode 100644 index 00000000000..efd4dc75c9b --- /dev/null +++ b/mobile/src/storage/preferences.ts @@ -0,0 +1,88 @@ +import AsyncStorage from '@react-native-async-storage/async-storage' + +const PINS_PREFIX = 'orca:pins:' +const PREFS_PREFIX = 'orca:prefs:' +const NOTIF_KEY = 'orca:pushNotificationsEnabled' + +export async function loadPushNotificationsEnabled(): Promise { + try { + const raw = await AsyncStorage.getItem(NOTIF_KEY) + if (raw === null) return true + return raw === 'true' + } catch { + return true + } +} + +export async function savePushNotificationsEnabled(enabled: boolean): Promise { + await AsyncStorage.setItem(NOTIF_KEY, String(enabled)) +} + +export type HostPreferences = { + sortMode: string + filterMode: string + groupMode: string + collapsedGroups: string[] + selectedRepos: string[] +} + +const DEFAULT_PREFS: HostPreferences = { + sortMode: 'smart', + filterMode: 'all', + groupMode: 'none', + collapsedGroups: [], + selectedRepos: [] +} +const SORT_MODES = new Set(['smart', 'recent', 'name']) +const FILTER_MODES = new Set(['all', 'active']) +const GROUP_MODES = new Set(['none', 'repo', 'prStatus']) + +function stringArray(value: unknown): string[] { + return Array.isArray(value) + ? value.filter((item): item is string => typeof item === 'string') + : [] +} + +function allowedString(value: unknown, allowed: Set, fallback: string): string { + return typeof value === 'string' && allowed.has(value) ? value : fallback +} + +export async function loadPinnedIds(hostId: string): Promise> { + try { + const raw = await AsyncStorage.getItem(PINS_PREFIX + hostId) + if (!raw) return new Set() + return new Set(stringArray(JSON.parse(raw))) + } catch { + return new Set() + } +} + +export async function savePinnedIds(hostId: string, ids: Set): Promise { + await AsyncStorage.setItem(PINS_PREFIX + hostId, JSON.stringify([...ids])) +} + +export async function loadPreferences(hostId: string): Promise { + try { + const raw = await AsyncStorage.getItem(PREFS_PREFIX + hostId) + if (!raw) return DEFAULT_PREFS + const parsed = JSON.parse(raw) as Partial + return { + sortMode: allowedString(parsed.sortMode, SORT_MODES, DEFAULT_PREFS.sortMode), + filterMode: allowedString(parsed.filterMode, FILTER_MODES, DEFAULT_PREFS.filterMode), + groupMode: allowedString(parsed.groupMode, GROUP_MODES, DEFAULT_PREFS.groupMode), + collapsedGroups: stringArray(parsed.collapsedGroups), + selectedRepos: stringArray(parsed.selectedRepos) + } + } catch { + return DEFAULT_PREFS + } +} + +export async function savePreferences( + hostId: string, + prefs: Partial +): Promise { + const current = await loadPreferences(hostId) + const merged = { ...current, ...prefs } + await AsyncStorage.setItem(PREFS_PREFIX + hostId, JSON.stringify(merged)) +} diff --git a/mobile/src/terminal/TerminalWebView.tsx b/mobile/src/terminal/TerminalWebView.tsx new file mode 100644 index 00000000000..22a93bcc7da --- /dev/null +++ b/mobile/src/terminal/TerminalWebView.tsx @@ -0,0 +1,633 @@ +import { useRef, useCallback, forwardRef, useImperativeHandle } from 'react' +import { StyleSheet, type StyleProp, type ViewStyle } from 'react-native' +import { WebView } from 'react-native-webview' +import type { WebViewMessageEvent } from 'react-native-webview' +import { colors } from '../theme/mobile-theme' + +export type TerminalWebViewHandle = { + write: (data: string) => void + init: (cols: number, rows: number, initialData?: string) => void + clear: () => void + measureFitDimensions: (containerHeight?: number) => Promise<{ cols: number; rows: number } | null> + resetZoom: () => void +} + +type Props = { + style?: StyleProp + onWebReady?: () => void +} + +type TerminalMessage = + | { type: 'write'; id?: number; data: string } + | { type: 'init'; id?: number; cols: number; rows: number; initialData?: string } + | { type: 'clear'; id?: number } + | { type: 'measure'; id?: number; containerHeight?: number } + | { type: 'reset-zoom'; id?: number } + +// Why: TUI apps (Claude Code / Ink) emit escape codes with absolute cursor +// positioning designed for the desktop's terminal dimensions (~150+ cols). +// We initialize xterm at the desktop's exact cols/rows so those escape codes +// render correctly, then use a measured CSS transform: scale() to fit the +// canvas into the phone viewport. The scale is computed after xterm opens +// by measuring the rendered surface width, not hardcoded, so it adapts to +// any terminal column count (80, 150, 200+). All touch gestures (scroll, +// pinch-to-zoom, pan) are handled by custom JS rather than native WebView +// behavior, so they work correctly with the CSS scale transform. +const XTERM_HTML = ` + + + + + + + + +
+
+
+ + + +` + +export const TerminalWebView = forwardRef(function TerminalWebView( + { style, onWebReady }, + ref +) { + const webViewRef = useRef(null) + const isWebReadyRef = useRef(false) + const pendingMessagesRef = useRef([]) + const messageIdRef = useRef(0) + const measureResolveRef = useRef< + ((result: { cols: number; rows: number } | null) => void) | null + >(null) + + const sendToWebView = useCallback((msg: TerminalMessage) => { + messageIdRef.current += 1 + webViewRef.current?.postMessage(JSON.stringify({ ...msg, id: messageIdRef.current })) + }, []) + + const flushPendingMessages = useCallback(() => { + const pending = pendingMessagesRef.current + pendingMessagesRef.current = [] + for (const msg of pending) { + sendToWebView(msg) + } + }, [sendToWebView]) + + const postMessage = useCallback( + (msg: TerminalMessage) => { + if (!isWebReadyRef.current) { + pendingMessagesRef.current.push(msg) + return + } + sendToWebView(msg) + }, + [sendToWebView] + ) + + const handleMessage = useCallback( + (event: WebViewMessageEvent) => { + let msg: Record + try { + msg = JSON.parse(event.nativeEvent.data) as Record + } catch { + return + } + + if (msg.type === 'web-ready') { + isWebReadyRef.current = true + onWebReady?.() + flushPendingMessages() + } else if (msg.type === 'measure-result') { + const resolve = measureResolveRef.current + measureResolveRef.current = null + if (resolve) { + const cols = typeof msg.cols === 'number' ? msg.cols : null + const rows = typeof msg.rows === 'number' ? msg.rows : null + resolve(cols && rows && cols >= 20 && rows >= 8 ? { cols, rows } : null) + } + } + }, + [flushPendingMessages, onWebReady] + ) + + const handleLoadStart = useCallback(() => { + isWebReadyRef.current = false + }, []) + + useImperativeHandle( + ref, + () => ({ + write(data: string) { + postMessage({ type: 'write', data }) + }, + init(cols: number, rows: number, initialData?: string) { + postMessage({ type: 'init', cols, rows, initialData }) + }, + clear() { + postMessage({ type: 'clear' }) + }, + measureFitDimensions( + containerHeight?: number + ): Promise<{ cols: number; rows: number } | null> { + if (!isWebReadyRef.current) return Promise.resolve(null) + return new Promise((resolve) => { + measureResolveRef.current?.(null) + measureResolveRef.current = resolve + sendToWebView({ type: 'measure', containerHeight }) + // Why: if the WebView doesn't respond within 2s (e.g., xterm + // failed to load), resolve null so the caller can disable + // Fit to Phone rather than hanging indefinitely. + setTimeout(() => { + if (measureResolveRef.current === resolve) { + measureResolveRef.current = null + resolve(null) + } + }, 2000) + }) + }, + resetZoom() { + postMessage({ type: 'reset-zoom' }) + } + }), + [postMessage, sendToWebView] + ) + + return ( + + ) +}) + +const styles = StyleSheet.create({ + webview: { + flex: 1, + backgroundColor: colors.terminalBg + } +}) diff --git a/mobile/src/theme/mobile-theme.ts b/mobile/src/theme/mobile-theme.ts new file mode 100644 index 00000000000..d0f80c671e0 --- /dev/null +++ b/mobile/src/theme/mobile-theme.ts @@ -0,0 +1,45 @@ +// Orca mobile design tokens — matches desktop graphite/dark palette. +// All screen files should import from here instead of using inline hex values. + +export const colors = { + bgBase: '#111111', + bgPanel: '#1a1a1a', + bgRaised: '#242424', + borderSubtle: '#2a2a2a', + + textPrimary: '#e0e0e0', + textSecondary: '#888888', + textMuted: '#555555', + + accentBlue: '#3b82f6', + + statusGreen: '#22c55e', + statusAmber: '#f59e0b', + statusRed: '#ef4444', + + // Terminal WebView background (Tokyonight) — separate from app chrome + terminalBg: '#1a1b26' +} as const + +export const spacing = { + xs: 4, + sm: 8, + md: 12, + lg: 16, + xl: 24 +} as const + +export const radii = { + row: 6, + card: 14, + button: 6, + input: 6, + camera: 8 +} as const + +export const typography = { + titleSize: 18, + bodySize: 14, + metaSize: 12, + monoFamily: 'monospace' as const +} as const diff --git a/mobile/src/transport/e2ee.ts b/mobile/src/transport/e2ee.ts new file mode 100644 index 00000000000..87d599ec077 --- /dev/null +++ b/mobile/src/transport/e2ee.ts @@ -0,0 +1,91 @@ +// Why: E2EE primitives for the mobile side. Uses tweetnacl for Curve25519 ECDH +// key exchange and XSalsa20-Poly1305 authenticated encryption. Wire format is +// base64([24-byte nonce][ciphertext]) over WebSocket text frames. +import nacl from 'tweetnacl' +import * as ExpoCrypto from 'expo-crypto' + +// Why: Hermes (React Native's JS engine) lacks crypto.getRandomValues, +// which tweetnacl requires. expo-crypto provides a native secure RNG +// that works in Expo Go without a custom dev build. +nacl.setPRNG((_x: Uint8Array, n: number) => { + const bytes = ExpoCrypto.getRandomBytes(n) + _x.set(bytes) +}) + +// Why: tweetnacl uses `instanceof Uint8Array` checks internally. On Hermes, +// values from native modules (expo-crypto), TextEncoder, or typed array +// operations can return objects that fail this check despite being +// functionally identical. Wrapping with `new Uint8Array(x)` guarantees +// the correct prototype chain. +function u8(x: Uint8Array): Uint8Array { + return new Uint8Array(x) +} + +export function generateKeyPair(): { publicKey: Uint8Array; secretKey: Uint8Array } { + const kp = nacl.box.keyPair() + return { publicKey: u8(kp.publicKey), secretKey: u8(kp.secretKey) } +} + +export function deriveSharedKey(ourSecretKey: Uint8Array, peerPublicKey: Uint8Array): Uint8Array { + return u8(nacl.box.before(u8(peerPublicKey), u8(ourSecretKey))) +} + +function uint8ToBase64(bytes: Uint8Array): string { + let binary = '' + for (let i = 0; i < bytes.length; i++) { + binary += String.fromCharCode(bytes[i]!) + } + return btoa(binary) +} + +function base64ToUint8(b64: string): Uint8Array { + const binary = atob(b64) + const bytes = new Uint8Array(binary.length) + for (let i = 0; i < binary.length; i++) { + bytes[i] = binary.charCodeAt(i) + } + return bytes +} + +export function publicKeyFromBase64(b64: string): Uint8Array { + const key = base64ToUint8(b64) + if (key.length !== 32) { + throw new Error( + `Invalid public key: expected 32 bytes, got ${key.length} from "${b64.slice(0, 20)}..."` + ) + } + return key +} + +export function publicKeyToBase64(key: Uint8Array): string { + return uint8ToBase64(key) +} + +export function encrypt(plaintext: string, sharedKey: Uint8Array): string { + const nonce = u8(nacl.randomBytes(nacl.box.nonceLength)) + const messageBytes = u8(new TextEncoder().encode(plaintext)) + const ciphertext = nacl.box.after(messageBytes, nonce, u8(sharedKey)) + + const bundle = new Uint8Array(nonce.length + ciphertext.length) + bundle.set(nonce) + bundle.set(ciphertext, nonce.length) + + return uint8ToBase64(bundle) +} + +export function decrypt(encrypted: string, sharedKey: Uint8Array): string | null { + const bundle = base64ToUint8(encrypted) + if (bundle.length < nacl.box.nonceLength + nacl.box.overheadLength) { + return null + } + + const nonce = u8(bundle.subarray(0, nacl.box.nonceLength)) + const ciphertext = u8(bundle.subarray(nacl.box.nonceLength)) + const plaintext = nacl.box.open.after(ciphertext, nonce, u8(sharedKey)) + + if (!plaintext) { + return null + } + + return new TextDecoder().decode(plaintext) +} diff --git a/mobile/src/transport/host-store.ts b/mobile/src/transport/host-store.ts new file mode 100644 index 00000000000..7f35fb742a2 --- /dev/null +++ b/mobile/src/transport/host-store.ts @@ -0,0 +1,66 @@ +import AsyncStorage from '@react-native-async-storage/async-storage' +import { HostProfileSchema, type HostProfile } from './types' + +const STORAGE_KEY = 'orca:hosts' + +export async function loadHosts(): Promise { + const raw = await AsyncStorage.getItem(STORAGE_KEY) + if (!raw) return [] + try { + const parsed = JSON.parse(raw) as unknown + if (!Array.isArray(parsed)) return [] + return parsed.flatMap((item) => { + const result = HostProfileSchema.safeParse(item) + return result.success ? [result.data] : [] + }) + } catch { + return [] + } +} + +export async function saveHost(host: HostProfile): Promise { + const hosts = await loadHosts() + const index = hosts.findIndex((h) => h.id === host.id) + if (index >= 0) { + hosts[index] = host + } else { + hosts.push(host) + } + await AsyncStorage.setItem(STORAGE_KEY, JSON.stringify(hosts)) +} + +export async function removeHost(hostId: string): Promise { + const hosts = await loadHosts() + const filtered = hosts.filter((h) => h.id !== hostId) + await AsyncStorage.setItem(STORAGE_KEY, JSON.stringify(filtered)) +} + +export async function renameHost(hostId: string, newName: string): Promise { + const hosts = await loadHosts() + const host = hosts.find((h) => h.id === hostId) + if (host) { + host.name = newName + await AsyncStorage.setItem(STORAGE_KEY, JSON.stringify(hosts)) + } +} + +export async function getNextHostName(): Promise { + const hosts = await loadHosts() + const existingNumbers = hosts + .map((h) => { + const match = h.name.match(/^Host (\d+)$/) + return match ? parseInt(match[1]!, 10) : 0 + }) + .filter((n) => n > 0) + const next = existingNumbers.length > 0 ? Math.max(...existingNumbers) + 1 : 1 + return `Host ${next}` +} + +export async function updateLastConnected(hostId: string): Promise { + const hosts = await loadHosts() + const host = hosts.find((h) => h.id === hostId) + if (host) { + host.lastConnected = Date.now() + await AsyncStorage.setItem(STORAGE_KEY, JSON.stringify(hosts)) + } +} diff --git a/mobile/src/transport/index.ts b/mobile/src/transport/index.ts new file mode 100644 index 00000000000..8aacca59eff --- /dev/null +++ b/mobile/src/transport/index.ts @@ -0,0 +1,19 @@ +export { connect, type RpcClient } from './rpc-client' +export { + loadHosts, + saveHost, + removeHost, + renameHost, + getNextHostName, + updateLastConnected +} from './host-store' +export type { + RpcRequest, + RpcResponse, + RpcSuccess, + RpcFailure, + ConnectionState, + HostProfile, + PairingOffer +} from './types' +export { PairingOfferSchema, PAIRING_OFFER_VERSION } from './types' diff --git a/mobile/src/transport/pairing.ts b/mobile/src/transport/pairing.ts new file mode 100644 index 00000000000..ada3e2715eb --- /dev/null +++ b/mobile/src/transport/pairing.ts @@ -0,0 +1,16 @@ +import { PairingOfferSchema, type PairingOffer } from './types' + +export function decodePairingUrl(url: string): PairingOffer | null { + try { + const hashIndex = url.indexOf('#') + if (!url.startsWith('orca://pair') || hashIndex === -1) return null + + const base64url = url.slice(hashIndex + 1) + const base64 = base64url.replace(/-/g, '+').replace(/_/g, '/') + const json = atob(base64) + const parsed = JSON.parse(json) + return PairingOfferSchema.parse(parsed) + } catch { + return null + } +} diff --git a/mobile/src/transport/rpc-client.ts b/mobile/src/transport/rpc-client.ts new file mode 100644 index 00000000000..10b51ab19d2 --- /dev/null +++ b/mobile/src/transport/rpc-client.ts @@ -0,0 +1,371 @@ +import type { RpcResponse, RpcSuccess, ConnectionState } from './types' +import { + generateKeyPair, + deriveSharedKey, + publicKeyFromBase64, + publicKeyToBase64, + encrypt, + decrypt +} from './e2ee' + +type PendingRequest = { + resolve: (response: RpcResponse) => void + reject: (error: Error) => void +} + +type StreamingListener = (result: unknown) => void + +type StreamRequest = { + method: string + params: unknown + listener: StreamingListener +} + +export type RpcClient = { + sendRequest: (method: string, params?: unknown) => Promise + subscribe: (method: string, params: unknown, onData: StreamingListener) => () => void + getState: () => ConnectionState + onStateChange: (listener: (state: ConnectionState) => void) => () => void + close: () => void +} + +const RECONNECT_DELAYS = [1000, 2000, 4000, 8000, 16000] +const REQUEST_TIMEOUT_MS = 30_000 +const HANDSHAKE_TIMEOUT_MS = 5_000 + +export function connect( + endpoint: string, + deviceToken: string, + serverPublicKeyB64: string, + onStateChange?: (state: ConnectionState) => void +): RpcClient { + let ws: WebSocket | null = null + let state: ConnectionState = 'disconnected' + let requestCounter = 0 + let reconnectAttempt = 0 + let reconnectTimer: ReturnType | null = null + let handshakeTimer: ReturnType | null = null + let intentionallyClosed = false + + // Why: fresh ephemeral keypair per connection provides forward secrecy. + // The shared key is derived from our ephemeral secret + server's static public key. + let sharedKey: Uint8Array | null = null + const serverPublicKey = publicKeyFromBase64(serverPublicKeyB64) + + const pending = new Map() + const streamListeners = new Map() + const stateListeners = new Set<(state: ConnectionState) => void>() + const connectWaiters: Array<{ resolve: () => void; reject: (e: Error) => void }> = [] + + if (onStateChange) { + stateListeners.add(onStateChange) + } + + function setState(next: ConnectionState) { + if (state === next) return + state = next + if (next === 'connected') { + for (const w of connectWaiters.splice(0)) w.resolve() + } else if (next === 'disconnected' || next === 'auth-failed') { + const reason = + next === 'auth-failed' ? 'Unauthorized — pairing may be revoked' : 'Connection closed' + for (const w of connectWaiters.splice(0)) w.reject(new Error(reason)) + } + for (const listener of stateListeners) { + listener(next) + } + } + + function waitForConnected(): Promise { + if (state === 'connected') return Promise.resolve() + if (intentionallyClosed) return Promise.reject(new Error('Client closed')) + return new Promise((resolve, reject) => { + connectWaiters.push({ resolve, reject }) + }) + } + + function nextId(): string { + return `rpc-${++requestCounter}-${Date.now()}` + } + + function openConnection() { + if (intentionallyClosed) return + + setState('connecting') + sharedKey = null + + ws = new WebSocket(endpoint) + + ws.onopen = () => { + reconnectAttempt = 0 + setState('handshaking') + + // Why: generate a fresh ephemeral keypair for each connection. + // This provides forward secrecy — compromising one session's key + // doesn't compromise past or future sessions. + const ephemeral = generateKeyPair() + const hello = JSON.stringify({ + type: 'e2ee_hello', + publicKeyB64: publicKeyToBase64(ephemeral.publicKey) + }) + ws?.send(hello) + + sharedKey = deriveSharedKey(ephemeral.secretKey, serverPublicKey) + + handshakeTimer = setTimeout(() => { + handshakeTimer = null + ws?.close() + }, HANDSHAKE_TIMEOUT_MS) + } + + ws.onmessage = (event) => { + const raw = typeof event.data === 'string' ? event.data : String(event.data) + + // Why: during handshaking, e2ee_ready is plaintext because it precedes + // encrypted auth; e2ee_authenticated/e2ee_error are encrypted. + if (state === 'handshaking') { + try { + const msg = JSON.parse(raw) + if (msg.type === 'e2ee_ready') { + sendEncrypted({ type: 'e2ee_auth', deviceToken }) + return + } + } catch { + // Not plaintext JSON — fall through and try encrypted handshake messages. + } + + if (!sharedKey || sharedKey.length !== 32) { + return + } + + const plaintext = decrypt(raw, sharedKey) + if (plaintext === null) { + return + } + + try { + const msg = JSON.parse(plaintext) + if (msg.type === 'e2ee_authenticated') { + if (handshakeTimer) { + clearTimeout(handshakeTimer) + handshakeTimer = null + } + setState('connected') + for (const [id, stream] of streamListeners) { + sendEncrypted({ id, deviceToken, method: stream.method, params: stream.params }) + } + } else if (msg.type === 'e2ee_error' || (!msg.ok && msg.error?.code === 'unauthorized')) { + intentionallyClosed = true + ws?.close() + ws = null + setState('auth-failed') + rejectAllPending('Unauthorized — pairing may be revoked') + } + } catch { + // Not JSON — ignore during handshake. + } + return + } + + // Why: guard against decrypt with an invalid key — sharedKey can be null + // after destroy() or if a message arrives during a reconnect race. + if (!sharedKey || sharedKey.length !== 32) { + return + } + + const plaintext = decrypt(raw, sharedKey) + if (plaintext === null) { + return + } + + let response: RpcResponse + try { + response = JSON.parse(plaintext) + } catch { + return + } + + // Why: auth failure is distinct from transient disconnect — retrying + // with a rejected token causes infinite reconnect churn. + if (!response.ok && response.error.code === 'unauthorized') { + intentionallyClosed = true + ws?.close() + ws = null + setState('auth-failed') + rejectAllPending('Unauthorized — pairing may be revoked') + return + } + + const isStreaming = response.ok && (response as RpcSuccess).streaming === true + + if (isStreaming) { + const stream = streamListeners.get(response.id) + if (stream && response.ok) { + stream.listener((response as RpcSuccess).result) + } + return + } + + if (response.ok) { + const result = (response as RpcSuccess).result as Record | null + if (result && result.type === 'end') { + const stream = streamListeners.get(response.id) + if (stream) { + stream.listener(result) + streamListeners.delete(response.id) + return + } + } + if (result && result.type === 'scrollback') { + const stream = streamListeners.get(response.id) + if (stream) { + stream.listener(result) + return + } + } + } + + const req = pending.get(response.id) + if (req) { + pending.delete(response.id) + req.resolve(response) + } + } + + ws.onclose = () => { + ws = null + sharedKey = null + if (handshakeTimer) { + clearTimeout(handshakeTimer) + handshakeTimer = null + } + if (intentionallyClosed) { + setState('disconnected') + rejectAllPending('Connection closed') + return + } + rejectAllPending('Connection interrupted') + setState('reconnecting') + scheduleReconnect() + } + + ws.onerror = () => { + // onclose will fire after this + } + } + + function scheduleReconnect() { + const delay = RECONNECT_DELAYS[Math.min(reconnectAttempt, RECONNECT_DELAYS.length - 1)]! + reconnectAttempt++ + reconnectTimer = setTimeout(() => { + reconnectTimer = null + openConnection() + }, delay) + } + + function rejectAllPending(reason: string) { + const error = new Error(reason) + for (const [id, req] of pending) { + pending.delete(id) + queueMicrotask(() => req.reject(error)) + } + } + + function sendEncrypted(request: unknown): boolean { + if (ws && ws.readyState === WebSocket.OPEN && sharedKey) { + ws.send(encrypt(JSON.stringify(request), sharedKey)) + return true + } + return false + } + + openConnection() + + return { + async sendRequest(method: string, params?: unknown): Promise { + await waitForConnected() + + return new Promise((resolve, reject) => { + const id = nextId() + const timeout = setTimeout(() => { + pending.delete(id) + reject(new Error(`Request timed out: ${method}`)) + }, REQUEST_TIMEOUT_MS) + + pending.set(id, { + resolve: (response) => { + clearTimeout(timeout) + resolve(response) + }, + reject: (error) => { + clearTimeout(timeout) + reject(error) + } + }) + + if (!sendEncrypted({ id, deviceToken, method, params })) { + pending.delete(id) + clearTimeout(timeout) + reject(new Error('Connection interrupted')) + } + }) + }, + + subscribe(method: string, params: unknown, onData: StreamingListener): () => void { + const id = nextId() + streamListeners.set(id, { method, params, listener: onData }) + + if (state === 'connected') { + sendEncrypted({ id, deviceToken, method, params }) + } + + return () => { + const stream = streamListeners.get(id) + streamListeners.delete(id) + if ( + stream?.method === 'terminal.subscribe' && + stream.params && + typeof stream.params === 'object' && + typeof (stream.params as { terminal?: unknown }).terminal === 'string' + ) { + sendEncrypted({ + id: nextId(), + deviceToken, + method: 'terminal.unsubscribe', + // Why: the runtime keys terminal subscription cleanup by terminal + // handle, not by the RPC request id used to open the stream. + params: { subscriptionId: (stream.params as { terminal: string }).terminal } + }) + } + } + }, + + getState(): ConnectionState { + return state + }, + + onStateChange(listener: (state: ConnectionState) => void): () => void { + stateListeners.add(listener) + return () => stateListeners.delete(listener) + }, + + close() { + intentionallyClosed = true + if (reconnectTimer) { + clearTimeout(reconnectTimer) + reconnectTimer = null + } + if (handshakeTimer) { + clearTimeout(handshakeTimer) + handshakeTimer = null + } + if (ws) { + ws.close() + ws = null + } + sharedKey = null + setState('disconnected') + rejectAllPending('Client closed') + } + } +} diff --git a/mobile/src/transport/types.ts b/mobile/src/transport/types.ts new file mode 100644 index 00000000000..c73489a9751 --- /dev/null +++ b/mobile/src/transport/types.ts @@ -0,0 +1,62 @@ +import { z } from 'zod' + +export type RpcRequest = { + id: string + deviceToken: string + method: string + params?: unknown +} + +export type RpcSuccess = { + id: string + ok: true + result: unknown + streaming?: true + _meta: { runtimeId: string } +} + +export type RpcFailure = { + id: string + ok: false + error: { code: string; message: string; data?: unknown } + _meta: { runtimeId: string } +} + +export type RpcResponse = RpcSuccess | RpcFailure + +export const PAIRING_OFFER_VERSION = 2 + +export const PairingOfferSchema = z.object({ + v: z.literal(PAIRING_OFFER_VERSION), + endpoint: z.string().min(1), + deviceToken: z.string().min(1), + publicKeyB64: z.string().min(1) +}) + +export type PairingOffer = z.infer + +export type ConnectionState = + | 'connecting' + | 'handshaking' + | 'connected' + | 'disconnected' + | 'reconnecting' + | 'auth-failed' + +export type HostProfile = { + id: string + name: string + endpoint: string + deviceToken: string + publicKeyB64: string + lastConnected: number +} + +export const HostProfileSchema = z.object({ + id: z.string().min(1), + name: z.string().min(1), + endpoint: z.string().min(1), + deviceToken: z.string().min(1), + publicKeyB64: z.string().min(1), + lastConnected: z.number().finite() +}) diff --git a/mobile/terminal-output-streaming-findings.md b/mobile/terminal-output-streaming-findings.md new file mode 100644 index 00000000000..5f9aac2b23a --- /dev/null +++ b/mobile/terminal-output-streaming-findings.md @@ -0,0 +1,56 @@ +# Mobile Terminal Output Streaming Findings + +Date: 2026-04-28 + +## Scope + +The investigation was limited to `mobile/`. Server-side files under `src/main/` were read for context only and were not modified. + +## Verified Findings + +- The phone can connect to `ws://192.168.0.179:6768`, list worktrees, list terminals, and call `terminal.send`. +- The phone's saved host token is valid; a direct WebSocket probe from the desktop using the same token can call `worktree.ps`, `terminal.list`, `terminal.subscribe`, `terminal.send`, and `terminal.read`. +- `TerminalWebView` can render text when the React Native side writes to it after xterm initializes. A temporary marker written after `init()` appeared visibly in the WebView. +- Messages posted to the WebView before its page installs message handlers can be dropped. The mobile fix queues `init`, `write`, and `clear` until the WebView reports `web-ready`. +- The WebView also has an internal queue for writes that arrive after `web-ready` but before xterm finishes `init()`. +- The selected physical-phone test worktree was `refs/heads/tasks-improvements` at `/Users/jinwoohong/orca/workspaces/orca/pr-1172-review`. +- For that test worktree, `terminal.subscribe` produced an initial `scrollback` event with an empty `lines` array and no serialized buffer. +- Sending commands to that test terminal returned `ok:true`, but a direct WebSocket `terminal.read` for the same handle still returned an empty tail and no live `data` chunks were observed. +- Creating a fresh terminal with `terminal.create` in that same worktree also returned a writable handle, but `terminal.send` followed by delayed `terminal.read` still returned an empty tail. +- Adding a mobile-side `terminal.read` fallback did not make the current physical-phone test terminal display output, because the direct WebSocket `terminal.read` response for `mobile-output-test` remained `tail: []` and `lastOutputAt: null` after `echo hi`. +- Calling `terminal.show` before `terminal.send` on `mobile-output-test` returned a connected/writable terminal with a `ptyId`, but a delayed `terminal.read` still returned an empty tail. +- Root cause found after building a no-phone repro: daemon-backed PTYs were forwarding provider data to the desktop renderer, but not into `runtime.onPtyData`. The runtime tail buffer and `terminal.subscribe` listeners are fed by `runtime.onPtyData`, so the phone saw accepted sends with no output. +- Fix: `src/main/ipc/pty.ts` now forwards `provider.onData` into `runtime.onPtyData` for non-`LocalPtyProvider` providers. Local PTYs already use the LocalPtyProvider configure hook, so the guard avoids duplicate local output. +- After restarting Electron, `pnpm exec tsx mobile/scripts/test-subscribe.ts ` passed: both `streamSawMarker` and `readSawMarker` were `true`. +- After the backend fix, the physical phone rendered the repro marker in `TerminalWebView`. +- Because of the empty tail/no live stream on that specific desktop terminal, the physical `ls` test did not prove the WebView display path after real PTY output. + +## Changes Made + +- `mobile/src/terminal/TerminalWebView.tsx` + - Added a native-side queue so WebView messages are not sent until the HTML reports `web-ready`. + - Added a WebView-side queue so writes wait until xterm finishes `init()`. +- `mobile/src/transport/rpc-client.ts` + - Stored full stream request metadata so active streams can be replayed after reconnect. + - Avoided sending a subscription before the socket reaches `connected`. + - Fixed `terminal.unsubscribe` to use the terminal handle as `subscriptionId`, matching runtime cleanup behavior. +- `mobile/app/h/[hostId]/session/[worktreeId].tsx` + - Clears and resubscribes when the worktree changes. + - Tracks the active terminal handle in a ref so stale handles are replaced when `terminal.list` changes. + - Routes scrollback and live data into `TerminalWebView`. + +## Useful Commands + +```bash +agent-device devices --json +agent-device snapshot --platform android --serial R3CX105QXRH --json +agent-device fill @e33 "ls" --platform android --serial R3CX105QXRH --json +agent-device click @e34 --platform android --serial R3CX105QXRH --json +agent-device screenshot /tmp/mobile-terminal.png --platform android --serial R3CX105QXRH --json +orca terminal send --terminal term_926b8898-f843-461a-acd2-482f741327ad --text r --json +orca terminal read --terminal term_926b8898-f843-461a-acd2-482f741327ad --json +``` + +## Next Debug Boundary + +Do not repeat WebView readiness tests unless the WebView changes again; the WebView can render post-init writes. For future regressions, run the no-phone repro first. If it fails, debug the WebSocket/runtime/PTY bridge before touching the mobile UI. If it passes but the phone is blank, debug `TerminalWebView` or session-screen routing. diff --git a/mobile/tsconfig.json b/mobile/tsconfig.json new file mode 100644 index 00000000000..c07b7787f55 --- /dev/null +++ b/mobile/tsconfig.json @@ -0,0 +1,10 @@ +{ + "extends": "expo/tsconfig.base", + "compilerOptions": { + "strict": true, + "paths": { + "@/*": ["./src/*"] + } + }, + "include": ["**/*.ts", "**/*.tsx"] +} diff --git a/package.json b/package.json index 0f2c873ea14..a20272b3ea6 100644 --- a/package.json +++ b/package.json @@ -91,6 +91,7 @@ "node-pty": "^1.1.0", "pdfjs-dist": "^5.6.205", "posthog-node": "^5.33.0", + "qrcode": "^1.5.4", "radix-ui": "^1.4.3", "react-markdown": "^10.1.0", "rehype-highlight": "^7.0.2", @@ -108,6 +109,7 @@ "ssh2": "^1.17.0", "tailwind-merge": "^3.5.0", "tw-animate-css": "^1.4.0", + "tweetnacl": "^1.0.3", "ws": "^8.20.0", "zod": "^4.3.6", "zustand": "^5.0.12" @@ -120,6 +122,7 @@ "@tailwindcss/vite": "^4.2.2", "@types/better-sqlite3": "^7.6.13", "@types/node": "^25.5.0", + "@types/qrcode": "^1.5.6", "@types/react": "^19.2.14", "@types/react-dom": "^19.2.3", "@types/ssh2": "^1.15.5", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1eaf072407f..7f0204d0552 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -166,6 +166,9 @@ importers: posthog-node: specifier: ^5.33.0 version: 5.33.0 + qrcode: + specifier: ^1.5.4 + version: 1.5.4 radix-ui: specifier: ^1.4.3 version: 1.4.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) @@ -217,6 +220,9 @@ importers: tw-animate-css: specifier: ^1.4.0 version: 1.4.0 + tweetnacl: + specifier: ^1.0.3 + version: 1.0.3 ws: specifier: ^8.20.0 version: 8.20.0 @@ -248,6 +254,9 @@ importers: '@types/node': specifier: ^25.5.0 version: 25.5.0 + '@types/qrcode': + specifier: ^1.5.6 + version: 1.5.6 '@types/react': specifier: ^19.2.14 version: 19.2.14 @@ -2837,6 +2846,9 @@ packages: '@types/plist@3.0.5': resolution: {integrity: sha512-E6OCaRmAe4WDmWNsL/9RMqdkkzDCY1etutkflWk4c+AcjDU07Pcz1fQwTX0TQz+Pxqn9i4L1TU3UFpjnrcDgxA==} + '@types/qrcode@1.5.6': + resolution: {integrity: sha512-te7NQcV2BOvdj2b1hCAHzAoMNuj65kNBMz0KBaxM6c3VGBOhU0dURQKOtH8CFNI/dsKkwlv32p26qYQTWoB5bw==} + '@types/react-dom@19.2.3': resolution: {integrity: sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==} peerDependencies: @@ -3242,6 +3254,10 @@ packages: resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} engines: {node: '>=6'} + camelcase@5.3.1: + resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==} + engines: {node: '>=6'} + caniuse-lite@1.0.30001780: resolution: {integrity: sha512-llngX0E7nQci5BPJDqoZSbuZ5Bcs9F5db7EtgfwBerX9XGtkkiO4NwfDDIRzHTTwcYC8vC7bmeUEPGrKlR/TkQ==} @@ -3326,6 +3342,9 @@ packages: resolution: {integrity: sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==} engines: {node: '>= 12'} + cliui@6.0.0: + resolution: {integrity: sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==} + cliui@8.0.1: resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} engines: {node: '>=12'} @@ -3640,6 +3659,10 @@ packages: supports-color: optional: true + decamelize@1.2.0: + resolution: {integrity: sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==} + engines: {node: '>=0.10.0'} + decode-named-character-reference@1.3.0: resolution: {integrity: sha512-GtpQYB283KrPp6nRw50q3U9/VfOutZOe103qlN7BPP6Ad27xYnOIWv4lPzo8HCAL+mMZofJ9KEy30fq6MfaK6Q==} @@ -3722,6 +3745,9 @@ packages: resolution: {integrity: sha512-qejHi7bcSD4hQAZE0tNAawRK1ZtafHDmMTMkrrIGgSLl7hTnQHmKCeB45xAcbfTqK2zowkM3j3bHt/4b/ARbYQ==} engines: {node: '>=0.3.1'} + dijkstrajs@1.0.3: + resolution: {integrity: sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA==} + dir-compare@4.2.0: resolution: {integrity: sha512-2xMCmOoMrdQIPHdsTawECdNPwlVFB9zGcz3kuhmBO6U3oU+UQjsue0i8ayLKpgBcm+hcXPMVSGUN9d+pvJ6+VQ==} @@ -4020,6 +4046,10 @@ packages: resolution: {integrity: sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==} engines: {node: '>= 18.0.0'} + find-up@4.1.0: + resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} + engines: {node: '>=8'} + foreground-child@3.3.1: resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==} engines: {node: '>=14'} @@ -4655,6 +4685,10 @@ packages: resolution: {integrity: sha512-ME4Fb83LgEgwNw96RKNvKV4VTLuXfoKudAmm2lP8Kk87KaMK0/Xrx/aAkMWmT8mDb+3MlFDspfbCs7adjRxA2g==} engines: {node: '>=20.0.0'} + locate-path@5.0.0: + resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} + engines: {node: '>=8'} + lodash-es@4.18.1: resolution: {integrity: sha512-J8xewKD/Gk22OZbhpOVSwcs60zhd95ESDwezOFuA3/099925PdHJ7OFHNTGtajL3AlZkykD32HykiMo+BIBI8A==} @@ -5175,10 +5209,18 @@ packages: resolution: {integrity: sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==} engines: {node: '>=8'} + p-limit@2.3.0: + resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} + engines: {node: '>=6'} + p-limit@3.1.0: resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} engines: {node: '>=10'} + p-locate@4.1.0: + resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} + engines: {node: '>=8'} + p-map@7.0.4: resolution: {integrity: sha512-tkAQEw8ysMzmkhgw8k+1U/iPhWNhykKnSk4Rd5zLoPJCuJaGRPo6YposrZgaxHKzDHdDWWZvE/Sk7hsL2X/CpQ==} engines: {node: '>=18'} @@ -5187,6 +5229,10 @@ packages: resolution: {integrity: sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==} engines: {node: '>=8'} + p-try@2.2.0: + resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} + engines: {node: '>=6'} + package-json-from-dist@1.0.1: resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} @@ -5221,6 +5267,10 @@ packages: path-data-parser@0.1.0: resolution: {integrity: sha512-NOnmBpt5Y2RWbuv0LMzsayp3lVylAHLPUTut412ZA3l+C4uw4ZVkQbjShYCQ8TCpUMdPapr4YjUqLYD6v68j+w==} + path-exists@4.0.0: + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} + path-is-absolute@1.0.1: resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} engines: {node: '>=0.10.0'} @@ -5289,6 +5339,10 @@ packages: resolution: {integrity: sha512-uysumyrvkUX0rX/dEVqt8gC3sTBzd4zoWfLeS29nb53imdaXVvLINYXTI2GNqzaMuvacNx4uJQ8+b3zXR0pkgQ==} engines: {node: '>=10.4.0'} + pngjs@5.0.0: + resolution: {integrity: sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==} + engines: {node: '>=10.13.0'} + pngjs@7.0.0: resolution: {integrity: sha512-LKWqWJRhstyYo9pGvgor/ivk2w94eSjE3RGVuzLGlr3NmD8bf7RcYGze1mNdEHRP6TRP6rMuDHk5t44hnTRyow==} engines: {node: '>=14.19.0'} @@ -5404,6 +5458,11 @@ packages: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} + qrcode@1.5.4: + resolution: {integrity: sha512-1ca71Zgiu6ORjHqFBDpnSMTR2ReToX4l1Au1VFLyVeBTFavzQnv5JxMFr3ukHVKpSrSA2MCk0lNJSykjUfz7Zg==} + engines: {node: '>=10.13.0'} + hasBin: true + qs@6.15.0: resolution: {integrity: sha512-mAZTtNCeetKMH+pSjrb76NAM8V9a05I9aBZOHztWy/UqcJdQYNsf59vrRKWnojAT9Y+GbIvoTBC++CPHqpDBhQ==} engines: {node: '>=0.6'} @@ -5554,6 +5613,9 @@ packages: resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} engines: {node: '>=0.10.0'} + require-main-filename@2.0.0: + resolution: {integrity: sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==} + resedit@1.7.2: resolution: {integrity: sha512-vHjcY2MlAITJhC0eRD/Vv8Vlgmu9Sd3LX9zZvtGzU5ZImdTN3+d6e/4mnTyV8vEbyf1sgNIrWxhWlrys52OkEA==} engines: {node: '>=12', npm: '>=6'} @@ -5685,6 +5747,9 @@ packages: resolution: {integrity: sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==} engines: {node: '>= 18'} + set-blocking@2.0.0: + resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==} + setprototypeof@1.2.0: resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} @@ -6020,6 +6085,9 @@ packages: tweetnacl@0.14.5: resolution: {integrity: sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==} + tweetnacl@1.0.3: + resolution: {integrity: sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==} + type-fest@0.13.1: resolution: {integrity: sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==} engines: {node: '>=10'} @@ -6273,6 +6341,9 @@ packages: resolution: {integrity: sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==} engines: {node: '>= 8'} + which-module@2.0.1: + resolution: {integrity: sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==} + which@2.0.2: resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} engines: {node: '>= 8'} @@ -6332,6 +6403,9 @@ packages: resolution: {integrity: sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg==} engines: {node: '>=8.0'} + y18n@4.0.3: + resolution: {integrity: sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==} + y18n@5.0.8: resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} engines: {node: '>=10'} @@ -6351,10 +6425,18 @@ packages: engines: {node: '>= 14.6'} hasBin: true + yargs-parser@18.1.3: + resolution: {integrity: sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==} + engines: {node: '>=6'} + yargs-parser@21.1.1: resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} engines: {node: '>=12'} + yargs@15.4.1: + resolution: {integrity: sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==} + engines: {node: '>=8'} + yargs@17.7.2: resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} engines: {node: '>=12'} @@ -8774,6 +8856,10 @@ snapshots: xmlbuilder: 15.1.1 optional: true + '@types/qrcode@1.5.6': + dependencies: + '@types/node': 25.5.0 + '@types/react-dom@19.2.3(@types/react@19.2.14)': dependencies: '@types/react': 19.2.14 @@ -9238,6 +9324,8 @@ snapshots: callsites@3.1.0: {} + camelcase@5.3.1: {} + caniuse-lite@1.0.30001780: {} ccount@2.0.1: {} @@ -9309,6 +9397,12 @@ snapshots: cli-width@4.1.0: {} + cliui@6.0.0: + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 6.2.0 + cliui@8.0.1: dependencies: string-width: 4.2.3 @@ -9623,6 +9717,8 @@ snapshots: dependencies: ms: 2.1.3 + decamelize@1.2.0: {} + decode-named-character-reference@1.3.0: dependencies: character-entities: 2.0.2 @@ -9689,6 +9785,8 @@ snapshots: diff@8.0.3: {} + dijkstrajs@1.0.3: {} + dir-compare@4.2.0: dependencies: minimatch: 3.1.5 @@ -10128,6 +10226,11 @@ snapshots: transitivePeerDependencies: - supports-color + find-up@4.1.0: + dependencies: + locate-path: 5.0.0 + path-exists: 4.0.0 + foreground-child@3.3.1: dependencies: cross-spawn: 7.0.6 @@ -10765,6 +10868,10 @@ snapshots: rfdc: 1.4.1 wrap-ansi: 9.0.2 + locate-path@5.0.0: + dependencies: + p-locate: 4.1.0 + lodash-es@4.18.1: {} lodash.escaperegexp@4.1.2: {} @@ -11590,10 +11697,18 @@ snapshots: p-cancelable@2.1.1: {} + p-limit@2.3.0: + dependencies: + p-try: 2.2.0 + p-limit@3.1.0: dependencies: yocto-queue: 0.1.0 + p-locate@4.1.0: + dependencies: + p-limit: 2.3.0 + p-map@7.0.4: {} p-retry@4.6.2: @@ -11601,6 +11716,8 @@ snapshots: '@types/retry': 0.12.0 retry: 0.13.1 + p-try@2.2.0: {} + package-json-from-dist@1.0.1: {} package-manager-detector@1.6.0: {} @@ -11638,6 +11755,8 @@ snapshots: path-data-parser@0.1.0: {} + path-exists@4.0.0: {} + path-is-absolute@1.0.1: {} path-key@3.1.1: {} @@ -11692,6 +11811,8 @@ snapshots: base64-js: 1.5.1 xmlbuilder: 15.1.1 + pngjs@5.0.0: {} + pngjs@7.0.0: {} points-on-curve@0.2.0: {} @@ -11845,6 +11966,12 @@ snapshots: punycode@2.3.1: {} + qrcode@1.5.4: + dependencies: + dijkstrajs: 1.0.3 + pngjs: 5.0.0 + yargs: 15.4.1 + qs@6.15.0: dependencies: side-channel: 1.1.0 @@ -12116,6 +12243,8 @@ snapshots: require-from-string@2.0.2: {} + require-main-filename@2.0.0: {} + resedit@1.7.2: dependencies: pe-library: 0.4.1 @@ -12279,6 +12408,8 @@ snapshots: transitivePeerDependencies: - supports-color + set-blocking@2.0.0: {} + setprototypeof@1.2.0: {} shadcn@4.1.0(@types/node@25.5.0)(typescript@5.9.3): @@ -12664,6 +12795,8 @@ snapshots: tweetnacl@0.14.5: {} + tweetnacl@1.0.3: {} + type-fest@0.13.1: optional: true @@ -12878,6 +13011,8 @@ snapshots: web-streams-polyfill@3.3.3: {} + which-module@2.0.1: {} + which@2.0.2: dependencies: isexe: 2.0.0 @@ -12930,6 +13065,8 @@ snapshots: xmlbuilder@15.1.1: {} + y18n@4.0.3: {} + y18n@5.0.8: {} yallist@3.1.1: {} @@ -12940,8 +13077,27 @@ snapshots: yaml@2.8.3: {} + yargs-parser@18.1.3: + dependencies: + camelcase: 5.3.1 + decamelize: 1.2.0 + yargs-parser@21.1.1: {} + yargs@15.4.1: + dependencies: + cliui: 6.0.0 + decamelize: 1.2.0 + find-up: 4.1.0 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + require-main-filename: 2.0.0 + set-blocking: 2.0.0 + string-width: 4.2.3 + which-module: 2.0.1 + y18n: 4.0.3 + yargs-parser: 18.1.3 + yargs@17.7.2: dependencies: cliui: 8.0.1 diff --git a/skills/mobile-fit-debug/SKILL.md b/skills/mobile-fit-debug/SKILL.md new file mode 100644 index 00000000000..63535e24172 --- /dev/null +++ b/skills/mobile-fit-debug/SKILL.md @@ -0,0 +1,182 @@ +--- +name: mobile-fit-debug +description: Debug mobile terminal display-mode toggle and PTY resize issues. Use when investigating why a terminal restores to the wrong width after toggling between phone and desktop mode on mobile, or when PTY sizes are corrupted across tab switches. +--- + +# Mobile Fit Debug + +Use this skill when debugging mobile terminal display-mode toggle issues — wrong restore width, corrupted previousCols, stale PTY sizes, or collateral safeFit cascades. + +## Architecture Overview + +The mobile display-mode toggle involves **3 processes** communicating asynchronously: + +``` +Mobile App (React Native) Server (Electron main) Desktop Renderer (Electron renderer) +───────────────────────── ────────────────────── ──────────────────────────────────── +terminal.setDisplayMode ──RPC──► applyMobileDisplayMode + ├─ resize PTY (authoritative) + ├─ suppressResizesForMs(500) + ├─ terminalFitOverrideChanged ──IPC──► setOverrideTick (React re-render) + │ ├─ safeFit on ALL panes + │ └─ pty:resize IPC ──────► SUPPRESSED + └─ notifyTerminalResize ──stream──► mobile client updates +``` + +## Key Files + +| File | Role | +|------|------| +| `src/main/runtime/orca-runtime.ts` | Server-side state: `mobileSubscribers`, `mobileDisplayModes`, `lastRendererSizes`, suppress window. Core handlers: `handleMobileSubscribe`, `handleMobileUnsubscribe`, `applyMobileDisplayMode`, `onExternalPtyResize` | +| `src/main/ipc/pty.ts` | `pty:resize` IPC handler. Checks `isResizeSuppressed()` before processing. Calls `onExternalPtyResize` to update server state | +| `src/main/runtime/rpc/methods/terminal.ts` | RPC methods: `terminal.subscribe` (mobile subscription lifecycle), `terminal.setDisplayMode`, `terminal.getDisplayMode` | +| `src/renderer/src/lib/pane-manager/mobile-fit-overrides.ts` | Desktop renderer's override cache. `setFitOverride('desktop-fit')` clears the override. `notifyChange` fires `onOverrideChange` listeners | +| `src/renderer/src/lib/pane-manager/pane-tree-ops.ts` | `safeFit()` — measures pane DOM and resizes xterm. When a mobile-fit override exists, uses override dims instead of measuring | +| `src/renderer/src/components/terminal-pane/TerminalPane.tsx` | `onOverrideChange` handler triggers `setOverrideTick` re-render cascade. This is what causes safeFit to run on ALL panes | +| `mobile/app/h/[hostId]/session/[worktreeId].tsx` | Mobile session screen — tab switching, `toggleDisplayMode`, terminal subscription management | +| `src/main/runtime/mobile-subscribe-integration.test.ts` | Integration tests for the full subscribe/unsubscribe/toggle lifecycle | + +## Adding Debug Logging + +Production code has no debug logging. To investigate issues, temporarily add file-based logging to the key files. Here's a pattern that works well: + +### 1. Add mfLog helper to each file you want to instrument + +```typescript +// In orca-runtime.ts (no prefix): +import { appendFileSync } from 'fs' +function mfLog(msg: string): void { + try { appendFileSync('/tmp/mobile-fit-debug.log', `[${new Date().toISOString()}] ${msg}\n`) } catch {} +} + +// In pty.ts (prefix [pty-ipc]): +function mfLog(msg: string): void { + try { appendFileSync('/tmp/mobile-fit-debug.log', `[${new Date().toISOString()}] [pty-ipc] ${msg}\n`) } catch {} +} + +// In terminal.ts RPC methods (prefix [rpc]): +function mfLog(msg: string): void { + try { appendFileSync('/tmp/mobile-fit-debug.log', `[${new Date().toISOString()}] [rpc] ${msg}\n`) } catch {} +} +``` + +### 2. Useful log points + +**orca-runtime.ts — `handleMobileSubscribe`:** +```typescript +mfLog(`handleMobileSubscribe ptyId=${ptyId} mode=${mode} viewport=${viewport?.cols}x${viewport?.rows}`) +mfLog(` existing.prev=${existing?.previousCols}x${existing?.previousRows} currentSize=${currentSize?.cols}x${currentSize?.rows} rendererSize=${rendererSize?.cols}x${rendererSize?.rows}`) +mfLog(` → previousCols=${previousCols}`) +``` + +**orca-runtime.ts — `applyMobileDisplayMode`:** +```typescript +mfLog(`applyMobileDisplayMode ptyId=${ptyId} mode=${mode} sub.prev=${subscriber?.previousCols}x${subscriber?.previousRows}`) +mfLog(` DESKTOP RESTORE: previousCols=${previousCols}`) +``` + +**orca-runtime.ts — `onExternalPtyResize`:** +```typescript +mfLog(`onExternalPtyResize ptyId=${ptyId} cols=${cols} rows=${rows}`) +``` + +**pty.ts — `pty:resize` handler:** +```typescript +if (runtime?.isResizeSuppressed()) { + mfLog(`pty:resize SUPPRESSED id=${args.id} cols=${args.cols} rows=${args.rows}`) + return +} +mfLog(`pty:resize id=${args.id} cols=${args.cols} rows=${args.rows}`) +``` + +**terminal.ts — `terminal.subscribe`:** +```typescript +mfLog(`\n========== MOBILE SUBSCRIBE ==========`) +mfLog(`terminal.subscribe handle=${params.terminal} ptyId=${leaf?.ptyId} client=${params.client?.type}`) +``` + +### 3. Reading the log + +```bash +> /tmp/mobile-fit-debug.log # clear +# ... reproduce issue on mobile ... +cat /tmp/mobile-fit-debug.log # read +``` + +### 4. Remember to remove logging before committing + +Remove all `mfLog` functions, calls, and `appendFileSync` imports. + +## What to Look For in Logs + +**Wrong previousCols captured:** +``` +handleMobileSubscribe ptyId=...@@abc123 ... + existing.prev=null currentSize=105x72 rendererSize=214x72 <-- rendererSize is WRONG + → previousCols=214 <-- captured wrong value +``` +This means `lastRendererSizes` was polluted by a collateral safeFit cascade. + +**Suppress window too short:** +``` +[pty-ipc] pty:resize id=...@@abc123 cols=105 rows=72 <-- NOT suppressed, arrived after window +``` +If you see unsuppressed `pty:resize` events for terminals that weren't the one being toggled, the 500ms suppress window may be too short. Check the timestamp gap between `applyMobileDisplayMode` and the stale resize. + +**Suppress working correctly:** +``` +[pty-ipc] pty:resize SUPPRESSED id=...@@abc123 cols=214 rows=72 +``` + +**Collateral cascade pattern** (the root cause of most bugs): +``` +applyMobileDisplayMode ptyId=...@@split mode=desktop + DESKTOP RESTORE: previousCols=105 +[pty-ipc] pty:resize SUPPRESSED id=...@@split cols=105 <-- redundant, suppressed OK +[pty-ipc] pty:resize SUPPRESSED id=...@@other cols=214 <-- collateral, suppressed OK +``` +If the collateral line says `pty:resize id=` instead of `SUPPRESSED`, the cascade leaked through. + +## Known Gotchas + +### 1. The Collateral safeFit Cascade +When `terminalFitOverrideChanged` fires for ONE pane, the desktop renderer's `setOverrideTick` React state change triggers a re-render that runs `safeFit` on ALL panes across ALL tabs. Background-tab panes get measured at wrong widths because they may not be visible or may be in different split configurations than the active tab. + +### 2. Cascade Timing is ~360ms +The full cascade path: server sends IPC -> desktop renderer receives -> React re-render -> requestAnimationFrame -> DOM measurement -> `pty:resize` IPC back to server. This takes ~360ms, which is why the suppress window must be >360ms (currently 500ms). + +### 3. lastRendererSizes Persistence +`lastRendererSizes` stores every `pty:resize` from the desktop renderer. Stale values persist across mobile subscribe/unsubscribe cycles. The `lastRendererSizes.delete(ptyId)` in desktop restore is critical — without it, a stale 214 from a previous cascade gets used as `previousCols` on the next subscribe. + +### 4. Daemon Doesn't Hot-Reload +The daemon process (`out/main/daemon-entry.js`) loads compiled code once at startup. Changes to `orca-runtime.ts` or `pty.ts` require: +1. `npx electron-vite build --outDir out` (or wait for watcher) +2. **Restart the dev server** (`pnpm dev`) — the daemon subprocess won't pick up new builds + +Verify your code is live: `grep "your_unique_string" out/main/index.js` + +### 5. previousCols Priority Chain +```typescript +previousCols = existing?.previousCols ?? rendererSize?.cols ?? currentSize?.cols +``` +- `existing?.previousCols` — re-subscribe case (tab switch back), most trusted +- `rendererSize?.cols` — from `lastRendererSizes`, the desktop renderer's last `pty:resize` +- `currentSize?.cols` — from `getTerminalSize()`, the server-side PTY size + +If `rendererSize` is polluted (e.g. 214 for a split pane), it takes priority over the correct `currentSize`. That's why clearing `lastRendererSizes` on restore is essential. + +### 6. agent-device for Automated Testing +Use `/opt/homebrew/bin/agent-device` to automate mobile UI testing: +```bash +agent-device snapshot # accessibility tree +agent-device click @e45 # click element by ref +agent-device screenshot --out /tmp/s.png # capture screenshot +agent-device wait 2 # wait 2 seconds +``` +Typical test flow: click tab -> click "Switch to desktop mode" -> wait 3s -> click "Switch to phone mode" -> switch tabs -> repeat -> read `/tmp/mobile-fit-debug.log`. + +## Running Tests + +```bash +npx vitest run src/main/runtime/mobile-subscribe-integration.test.ts +``` diff --git a/src/cli/runtime-client.test.ts b/src/cli/runtime-client.test.ts index 74102e01e05..2064b937887 100644 --- a/src/cli/runtime-client.test.ts +++ b/src/cli/runtime-client.test.ts @@ -35,10 +35,12 @@ function writeMetadata( JSON.stringify({ runtimeId: 'runtime-1', pid, - transport: { - kind: 'unix', - endpoint - }, + transports: [ + { + kind: 'unix', + endpoint + } + ], authToken, startedAt: 1 }), diff --git a/src/cli/runtime/metadata.ts b/src/cli/runtime/metadata.ts index cd690ab0f14..df93adad8b5 100644 --- a/src/cli/runtime/metadata.ts +++ b/src/cli/runtime/metadata.ts @@ -1,14 +1,18 @@ import { homedir } from 'os' import { join } from 'path' import { readFileSync } from 'fs' -import { getRuntimeMetadataPath, type RuntimeMetadata } from '../../shared/runtime-bootstrap' +import { + findTransport, + getRuntimeMetadataPath, + type RuntimeMetadata +} from '../../shared/runtime-bootstrap' import { RuntimeClientError } from './types' export function readMetadata(userDataPath: string): RuntimeMetadata { const metadataPath = getRuntimeMetadataPath(userDataPath) try { const metadata = JSON.parse(readFileSync(metadataPath, 'utf8')) as RuntimeMetadata | null - if (!metadata?.transport || !metadata.authToken) { + if (!metadata || !findTransport(metadata, 'unix', 'named-pipe') || !metadata.authToken) { throw new RuntimeClientError( 'runtime_unavailable', `Orca runtime metadata is incomplete at ${metadataPath}` diff --git a/src/cli/runtime/status.ts b/src/cli/runtime/status.ts index 00daf4bfec6..3a0f445a40f 100644 --- a/src/cli/runtime/status.ts +++ b/src/cli/runtime/status.ts @@ -7,7 +7,7 @@ export async function getCliStatus( userDataPath: string ): Promise> { const metadata = tryReadMetadata(userDataPath) - if (!metadata?.transport || !metadata.authToken) { + if (!metadata?.transports?.length || !metadata.authToken) { return buildCliStatusResponse({ app: { running: false, diff --git a/src/cli/runtime/transport.ts b/src/cli/runtime/transport.ts index 7209a274c0f..ca8ca746274 100644 --- a/src/cli/runtime/transport.ts +++ b/src/cli/runtime/transport.ts @@ -1,6 +1,6 @@ import { createConnection } from 'net' import { randomUUID } from 'crypto' -import type { RuntimeMetadata, RuntimeTransportMetadata } from '../../shared/runtime-bootstrap' +import { findTransport, type RuntimeMetadata } from '../../shared/runtime-bootstrap' import { isKeepaliveFrame, RuntimeRpcEnvelopeSchema } from './envelope-schema' import { RuntimeClientError, type RuntimeRpcResponse } from './types' @@ -11,7 +11,17 @@ export async function sendRequest( timeoutMs: number ): Promise> { return await new Promise((resolve, reject) => { - const socket = createConnection(getTransportEndpoint(metadata.transport!)) + const transport = findTransport(metadata, 'unix', 'named-pipe') + if (!transport) { + reject( + new RuntimeClientError( + 'runtime_unavailable', + 'No compatible transport found in Orca runtime metadata.' + ) + ) + return + } + const socket = createConnection(transport.endpoint) let buffer = '' let settled = false const requestId = randomUUID() @@ -159,7 +169,3 @@ export async function sendRequest( }) }) } - -function getTransportEndpoint(transport: RuntimeTransportMetadata): string { - return transport.endpoint -} diff --git a/src/main/browser/cdp-bridge-integration.test.ts b/src/main/browser/cdp-bridge-integration.test.ts index 37a89caea3d..8475a0cbca8 100644 --- a/src/main/browser/cdp-bridge-integration.test.ts +++ b/src/main/browser/cdp-bridge-integration.test.ts @@ -273,7 +273,7 @@ describe('Browser automation pipeline (integration)', () => { await server.start() const metadata = readRuntimeMetadata(userDataPath)! - endpoint = metadata.transport!.endpoint + endpoint = metadata.transports[0]!.endpoint authToken = metadata.authToken! }) @@ -521,7 +521,7 @@ describe('Browser automation pipeline (integration)', () => { await server2.start() const metadata2 = readRuntimeMetadata(userDataPath2)! - const res = await sendRequest(metadata2.transport!.endpoint, { + const res = await sendRequest(metadata2.transports[0]!.endpoint, { id: 'req_no_tab', authToken: metadata2.authToken, method: 'browser.snapshot' diff --git a/src/main/codex-accounts/runtime-home-service.test.ts b/src/main/codex-accounts/runtime-home-service.test.ts index ed16d9ab18d..65d304f71b6 100644 --- a/src/main/codex-accounts/runtime-home-service.test.ts +++ b/src/main/codex-accounts/runtime-home-service.test.ts @@ -94,6 +94,7 @@ function createSettings(overrides: Partial = {}): GlobalSettings terminalMacOptionAsAlt: 'false', terminalMacOptionAsAltMigrated: true, experimentalAgentDashboard: false, + experimentalMobile: false, experimentalSidekick: false, terminalWindowsShell: 'powershell.exe', terminalWindowsPowerShellImplementation: 'powershell.exe', diff --git a/src/main/codex-accounts/service.test.ts b/src/main/codex-accounts/service.test.ts index d3673d56681..cff5f956d75 100644 --- a/src/main/codex-accounts/service.test.ts +++ b/src/main/codex-accounts/service.test.ts @@ -88,6 +88,7 @@ function createSettings(overrides: Partial = {}): GlobalSettings terminalMacOptionAsAlt: 'false', terminalMacOptionAsAltMigrated: true, experimentalAgentDashboard: false, + experimentalMobile: false, experimentalSidekick: false, terminalWindowsShell: 'powershell.exe', terminalWindowsPowerShellImplementation: 'powershell.exe', diff --git a/src/main/daemon/headless-emulator.ts b/src/main/daemon/headless-emulator.ts index 6491e435de5..12645ec1e0a 100644 --- a/src/main/daemon/headless-emulator.ts +++ b/src/main/daemon/headless-emulator.ts @@ -9,6 +9,10 @@ export type HeadlessEmulatorOptions = { scrollback?: number } +export type HeadlessSnapshotOptions = { + scrollbackRows?: number +} + const DEFAULT_SCROLLBACK = 5000 function parseFileUriPath(uri: string): string | null { @@ -87,10 +91,10 @@ export class HeadlessEmulator { this.terminal.resize(cols, rows) } - getSnapshot(): TerminalSnapshot { + getSnapshot(opts: HeadlessSnapshotOptions = {}): TerminalSnapshot { const modes = this.getModes() return { - snapshotAnsi: this.serializer.serialize(), + snapshotAnsi: this.serializer.serialize({ scrollback: opts.scrollbackRows }), scrollbackAnsi: '', rehydrateSequences: this.buildRehydrateSequences(modes), cwd: this.cwd, diff --git a/src/main/daemon/shell-ready.ts b/src/main/daemon/shell-ready.ts index f7a2e021fdb..1bccaf63263 100644 --- a/src/main/daemon/shell-ready.ts +++ b/src/main/daemon/shell-ready.ts @@ -134,7 +134,11 @@ if [[ "\${ORCA_SHELL_READY_MARKER:-0}" == "1" ]]; then __orca_prompt_mark() { printf "${SHELL_READY_MARKER}" } - precmd_functions=(\${precmd_functions[@]} __orca_prompt_mark) + # Why: zsh precmd fires before zle switches the PTY into line-editing mode, + # so writing startup input there can be echoed once outside the prompt. + autoload -Uz add-zle-hook-widget + zle -N __orca_prompt_mark + add-zle-hook-widget line-init __orca_prompt_mark fi ` const bashRc = `# Orca daemon bash shell-ready wrapper diff --git a/src/main/index.ts b/src/main/index.ts index 650ea14045b..ca89b89659b 100644 --- a/src/main/index.ts +++ b/src/main/index.ts @@ -15,6 +15,7 @@ import { initDaemonPtyProvider, disconnectDaemon } from './daemon/daemon-init' import { setAppRuntimeFlags } from './ipc/app' import { closeAllWatchers } from './ipc/filesystem-watcher' import { registerCoreHandlers } from './ipc/register-core-handlers' +import { registerMobileHandlers } from './ipc/mobile' import { initTelemetry, shutdownTelemetry } from './telemetry/client' import { triggerStartupNotificationRegistration } from './ipc/notifications' import { OrcaRuntimeService } from './runtime/orca-runtime' @@ -498,10 +499,18 @@ app.whenReady().then(async () => { } } }) + // Why: E2E tests launch parallel Electron instances that would all race to + // bind the default fixed port, crashing on EADDRINUSE. Port 0 lets the OS + // assign a random available port per instance while still exercising the + // full WebSocket startup path. + const isE2E = Boolean(process.env.ORCA_E2E_USER_DATA_DIR) runtimeRpc = new OrcaRuntimeRpcServer({ runtime, - userDataPath: app.getPath('userData') + userDataPath: app.getPath('userData'), + enableWebSocket: true, + ...(isE2E ? { wsPort: 0 } : {}) }) + registerMobileHandlers(runtimeRpc) // Why: the persistent-terminal daemon is always started. If it fails, the // LocalPtyProvider (initialized at module load in ipc/pty.ts) remains as the diff --git a/src/main/ipc/mobile.ts b/src/main/ipc/mobile.ts new file mode 100644 index 00000000000..c7ee87d07ee --- /dev/null +++ b/src/main/ipc/mobile.ts @@ -0,0 +1,125 @@ +import { ipcMain } from 'electron' +import { networkInterfaces } from 'os' +import QRCode from 'qrcode' +import type { OrcaRuntimeRpcServer } from '../runtime/runtime-rpc' +import { encodePairingOffer, PAIRING_OFFER_VERSION } from '../../shared/pairing' + +export type NetworkInterface = { + name: string + address: string +} + +// Why: the WebSocket transport advertises 0.0.0.0 as its endpoint, which isn't +// connectable from a mobile device. We enumerate all non-internal IPv4 +// addresses so the user can choose which one to advertise in the QR code +// (e.g. LAN vs Tailscale). +function getNetworkInterfaces(): NetworkInterface[] { + const result: NetworkInterface[] = [] + const interfaces = networkInterfaces() + for (const [name, addrs] of Object.entries(interfaces)) { + if (!addrs) { + continue + } + for (const addr of addrs) { + if (addr.family === 'IPv4' && !addr.internal) { + result.push({ name, address: addr.address }) + } + } + } + return result +} + +function getLanAddress(): string | null { + const ifaces = getNetworkInterfaces() + return ifaces.length > 0 ? ifaces[0]!.address : null +} + +// Why: the mobile IPC handlers provide the renderer with QR code pairing data, +// device management, and WebSocket readiness status. They depend on the +// OrcaRuntimeRpcServer because it owns the device registry and TLS state. + +export function registerMobileHandlers(rpcServer: OrcaRuntimeRpcServer): void { + ipcMain.handle('mobile:listNetworkInterfaces', (): { interfaces: NetworkInterface[] } => ({ + interfaces: getNetworkInterfaces() + })) + + ipcMain.handle('mobile:getPairingQR', async (_event, args?: { address?: string }) => { + const rawEndpoint = rpcServer.getWebSocketEndpoint() + const registry = rpcServer.getDeviceRegistry() + if (!rawEndpoint || !registry) { + return { available: false as const } + } + + // Why: allow the caller to specify which network interface address to + // embed in the QR code. This supports overlay networks (Tailscale, + // ZeroTier) where the default LAN IP isn't reachable from the phone. + const ip = args?.address ?? getLanAddress() + if (!ip) { + return { available: false as const } + } + const endpoint = rawEndpoint.replace('0.0.0.0', ip) + + const device = registry.addDevice(`Mobile ${new Date().toLocaleDateString()}`) + + const publicKeyB64 = rpcServer.getE2EEPublicKey() + if (!publicKeyB64) { + return { available: false as const } + } + + const url = encodePairingOffer({ + v: PAIRING_OFFER_VERSION, + endpoint, + deviceToken: device.token, + publicKeyB64 + }) + + const qrDataUrl = await QRCode.toDataURL(url, { + errorCorrectionLevel: 'M', + margin: 2, + width: 256 + }) + + return { + available: true as const, + qrDataUrl, + endpoint, + deviceId: device.deviceId + } + }) + + ipcMain.handle('mobile:listDevices', () => { + const registry = rpcServer.getDeviceRegistry() + if (!registry) { + return { devices: [] } + } + // Why: devices with lastSeenAt === 0 were created during QR generation + // but never actually scanned/connected. Showing them as "paired" is + // misleading, so we filter them out. + return { + devices: registry + .listDevices() + .filter((d) => d.lastSeenAt > 0) + .map((d) => ({ + deviceId: d.deviceId, + name: d.name, + pairedAt: d.pairedAt, + lastSeenAt: d.lastSeenAt + })) + } + }) + + ipcMain.handle('mobile:revokeDevice', (_event, args: { deviceId: string }) => { + const registry = rpcServer.getDeviceRegistry() + if (!registry) { + return { revoked: false } + } + return { revoked: registry.removeDevice(args.deviceId) } + }) + + ipcMain.handle('mobile:isWebSocketReady', () => { + return { + ready: rpcServer.getWebSocketEndpoint() !== null, + endpoint: rpcServer.getWebSocketEndpoint() + } + }) +} diff --git a/src/main/ipc/notifications.ts b/src/main/ipc/notifications.ts index eb69159bba8..c8f2856e5e8 100644 --- a/src/main/ipc/notifications.ts +++ b/src/main/ipc/notifications.ts @@ -1,6 +1,7 @@ import { app, BrowserWindow, Notification, ipcMain, shell } from 'electron' import type { Store } from '../persistence' import type { NotificationDispatchRequest, NotificationDispatchResult } from '../../shared/types' +import type { OrcaRuntimeService } from '../runtime/orca-runtime' const NOTIFICATION_COOLDOWN_MS = 5000 @@ -11,7 +12,7 @@ const NOTIFICATION_COOLDOWN_MS = 5000 // strong reference until the notification is clicked or closed. const activeNotifications = new Set() -export function registerNotificationHandlers(store: Store): void { +export function registerNotificationHandlers(store: Store, runtime?: OrcaRuntimeService): void { const recentNotifications = new Map() ipcMain.removeHandler('notifications:openSystemSettings') @@ -28,6 +29,21 @@ export function registerNotificationHandlers(store: Store): void { ipcMain.handle( 'notifications:dispatch', (_event, args: NotificationDispatchRequest): NotificationDispatchResult => { + // Why: mobile push is independent of desktop notification guards. + // The user's phone should receive the notification even when the desktop + // window is focused (suppressWhenFocused), Electron notifications aren't + // supported, or the desktop is in cooldown. The mobile client decides + // independently whether to show based on its own app state. + if (runtime) { + const opts = buildNotificationOptions(args) + runtime.dispatchMobileNotification({ + source: args.source, + title: opts.title, + body: opts.body, + worktreeId: args.worktreeId + }) + } + if (!Notification.isSupported()) { return { delivered: false, reason: 'not-supported' } } @@ -119,6 +135,7 @@ export function registerNotificationHandlers(store: Store): void { } notification.show() + return { delivered: true } } ) diff --git a/src/main/ipc/pty.ts b/src/main/ipc/pty.ts index 4519c99ac12..b8962fc823d 100644 --- a/src/main/ipc/pty.ts +++ b/src/main/ipc/pty.ts @@ -4,6 +4,7 @@ foreground-process inspection, and renderer IPC stay behind a single audited boundary. Splitting it by line count would scatter tightly coupled terminal process behavior across files without a cleaner ownership seam. */ import { join, delimiter } from 'path' +import { randomUUID } from 'crypto' import { type BrowserWindow, ipcMain, app } from 'electron' export { getBashShellReadyRcfileContent } from '../providers/local-pty-shell-ready' import type { OrcaRuntimeService } from '../runtime/orca-runtime' @@ -36,6 +37,9 @@ const sshProviders = new Map() // post-spawn operations to the correct provider without the renderer needing // to track connectionId per-PTY. const ptyOwnership = new Map() +// Why: mobile clients must mirror desktop PTY geometry even when the renderer +// cannot provide an xterm snapshot yet, such as immediately after tab creation. +const ptySizes = new Map() // Why: the agent-hooks server caches per-paneKey state (last prompt, last // tool) that otherwise grows unbounded as panes come and go. Track the // spawn-time paneKey so clearProviderPtyState can clear that cache on PTY @@ -284,6 +288,7 @@ export function clearProviderPtyState(id: string): void { // new teardown path forgets to remove one provider's overlay/hook state. openCodeHookService.clearPty(id) piTitlebarExtensionService.clearPty(id) + ptySizes.delete(id) // Why: drop the memory-collector registration so a dead PTY does not keep // trying to resolve its (now-dead) pid on every snapshot. Safe no-op for // PTYs that were never registered (SSH-owned). @@ -436,10 +441,15 @@ export function registerPtyHandlers( // Why: LocalPtyProvider routes data to the runtime via configure().onData, // but daemon-backed providers don't have configure(). Without this, daemon // PTY data never reaches the runtime's tail buffer, so terminal.read returns - // empty and agent-detection from raw data never fires. + // empty and agent-detection from raw data never fires. Runtime tails also + // power mobile read/stream, so they must be notified regardless of window + // state. const isLocalProvider = localProvider instanceof LocalPtyProvider localDataUnsub = localProvider.onData((payload) => { + if (!isLocalProvider) { + runtime?.onPtyData(payload.id, payload.data, Date.now()) + } if (mainWindow.isDestroyed()) { // Why: clear the pending flush timer so it doesn't fire after the window // is gone. Without this, macOS app re-activation leaks orphaned timers @@ -451,9 +461,6 @@ export function registerPtyHandlers( pendingData.clear() return } - if (!isLocalProvider) { - runtime?.onPtyData(payload.id, payload.data, Date.now()) - } const existing = pendingData.get(payload.id) pendingData.set(payload.id, existing ? existing + payload.data : payload.data) if (!flushTimer) { @@ -484,6 +491,54 @@ export function registerPtyHandlers( bindProviderListeners() rebindProviderListeners = bindProviderListeners + function requestSerializedBuffer( + ptyId: string + ): Promise<{ data: string; cols: number; rows: number } | null> { + if (mainWindow.isDestroyed()) { + return Promise.resolve(null) + } + + const requestId = randomUUID() + return new Promise((resolve) => { + const cleanup = (): void => { + clearTimeout(timeout) + ipcMain.removeListener('pty:serializeBuffer:response', onResponse) + } + + const timeout = setTimeout(() => { + cleanup() + resolve(null) + }, 750) + + const onResponse = ( + _event: Electron.IpcMainEvent, + args: { + requestId?: string + snapshot?: { data?: unknown; cols?: unknown; rows?: unknown } | null + } + ): void => { + if (args.requestId !== requestId) { + return + } + cleanup() + const snapshot = args.snapshot + if ( + snapshot && + typeof snapshot.data === 'string' && + typeof snapshot.cols === 'number' && + typeof snapshot.rows === 'number' + ) { + resolve({ data: snapshot.data, cols: snapshot.cols, rows: snapshot.rows }) + } else { + resolve(null) + } + } + + ipcMain.on('pty:serializeBuffer:response', onResponse) + mainWindow.webContents.send('pty:serializeBuffer:request', { requestId, ptyId }) + }) + } + // Kill orphaned PTY processes from previous page loads when the renderer reloads. // Why: only applies to LocalPtyProvider where PTYs live in the Electron main // process and can become orphaned on page reload. Daemon-backed sessions @@ -535,6 +590,28 @@ export function registerPtyHandlers( } catch { return null } + }, + listProcesses: async () => { + const providerSessions = await Promise.all([ + localProvider.listProcesses(), + ...Array.from(sshProviders.values(), (provider) => provider.listProcesses().catch(() => [])) + ]) + return providerSessions.flat() + }, + serializeBuffer: (ptyId) => { + // Why: mobile xterm must start from the desktop xterm's exact screen + // state and dimensions before live TUI chunks can render correctly. + return requestSerializedBuffer(ptyId) + }, + getSize: (ptyId) => ptySizes.get(ptyId) ?? null, + resize: (ptyId, cols, rows) => { + try { + ptySizes.set(ptyId, { cols, rows }) + getProviderForPty(ptyId).resize(ptyId, cols, rows) + return true + } catch { + return false + } } }) @@ -691,6 +768,12 @@ export function registerPtyHandlers( if (effectiveShellOverride !== undefined) { spawnOptions.shellOverride = effectiveShellOverride } + if (effectiveSessionId !== undefined) { + // Why: daemon PTYs can emit prompt/startup bytes before spawn() + // resolves. Runtime headless snapshots need the real pane geometry + // for those early bytes; otherwise they default to 80x24 and wrap TUIs. + ptySizes.set(effectiveSessionId, { cols: args.cols, rows: args.rows }) + } if (process.platform === 'win32' && !args.connectionId) { // Why: the renderer only models PowerShell as one shell family. Thread // the persisted implementation choice through spawnOptions so both the @@ -704,16 +787,11 @@ export function registerPtyHandlers( try { result = await provider.spawn(spawnOptions) } catch (err) { + if (effectiveSessionId !== undefined) { + ptySizes.delete(effectiveSessionId) + } // Why: when buildPtyHostEnv materialized a Pi overlay for this id - // but provider.spawn failed, the overlay would leak. Sweep per-PTY - // state for the minted id so it isn't orphaned. Safe to call even - // when no overlay was created (clearProviderPtyState is a no-op in - // that case). - // - // Only clean up when we MINTED the id in this request. Caller-supplied - // ids may correspond to existing PTYs whose state (OpenCode hooks, Pi - // overlay, agent-hook pane caches) we MUST NOT clear on a retry/attach - // failure. + // but provider.spawn failed, the overlay would leak. if (isMintedSessionId && effectiveSessionId !== undefined) { clearProviderPtyState(effectiveSessionId) } @@ -723,6 +801,14 @@ export function registerPtyHandlers( if (preAllocatedHandle) { runtime?.registerPreAllocatedHandleForPty(result.id, preAllocatedHandle) } + ptySizes.set(result.id, { cols: args.cols, rows: args.rows }) + if ( + typeof args.worktreeId === 'string' && + args.worktreeId.length > 0 && + args.worktreeId.length <= 512 + ) { + runtime?.registerPty(result.id, args.worktreeId) + } if (isClaudeLaunch) { markClaudePtySpawned(result.id) } @@ -789,7 +875,17 @@ export function registerPtyHandlers( // empty acknowledgement message back to the renderer. ipcMain.removeAllListeners('pty:resize') ipcMain.on('pty:resize', (_event, args: { id: string; cols: number; rows: number }) => { + // Why: after a desktop-fit override change, the desktop renderer's + // re-render cascade runs safeFit on ALL panes (not just the affected + // one). Background-tab panes get measured at full-width (214) instead + // of their correct split width. Suppressing ALL pty:resize during + // this window prevents the cascade from corrupting PTY dimensions. + if (runtime?.isResizeSuppressed()) { + return + } + ptySizes.set(args.id, { cols: args.cols, rows: args.rows }) getProviderForPty(args.id).resize(args.id, args.cols, args.rows) + runtime?.onExternalPtyResize(args.id, args.cols, args.rows) }) // Why: fire-and-forget — clears the DaemonPtyAdapter's sticky cold restore diff --git a/src/main/ipc/register-core-handlers.test.ts b/src/main/ipc/register-core-handlers.test.ts index 916c8e97859..b1d7cda9b9e 100644 --- a/src/main/ipc/register-core-handlers.test.ts +++ b/src/main/ipc/register-core-handlers.test.ts @@ -251,7 +251,7 @@ describe('registerCoreHandlers', () => { expect(registerFeedbackHandlersMock).toHaveBeenCalled() expect(registerStatsHandlersMock).toHaveBeenCalledWith(stats) expect(registerMemoryHandlersMock).toHaveBeenCalledWith(store) - expect(registerNotificationHandlersMock).toHaveBeenCalledWith(store) + expect(registerNotificationHandlersMock).toHaveBeenCalledWith(store, runtime) expect(registerDeveloperPermissionHandlersMock).toHaveBeenCalled() expect(registerSettingsHandlersMock).toHaveBeenCalledWith(store) expect(registerSessionHandlersMock).toHaveBeenCalledWith(store) diff --git a/src/main/ipc/register-core-handlers.ts b/src/main/ipc/register-core-handlers.ts index 704358fc214..273a679d4ff 100644 --- a/src/main/ipc/register-core-handlers.ts +++ b/src/main/ipc/register-core-handlers.ts @@ -80,7 +80,7 @@ export function registerCoreHandlers( registerExportHandlers() registerStatsHandlers(stats) registerMemoryHandlers(store) - registerNotificationHandlers(store) + registerNotificationHandlers(store, runtime) registerDeveloperPermissionHandlers() registerSettingsHandlers(store) registerTelemetryHandlers() diff --git a/src/main/ipc/runtime.ts b/src/main/ipc/runtime.ts index 1460d6c9599..3931869c7bb 100644 --- a/src/main/ipc/runtime.ts +++ b/src/main/ipc/runtime.ts @@ -20,4 +20,35 @@ export function registerRuntimeHandlers(runtime: OrcaRuntimeService): void { ipcMain.handle('runtime:getStatus', (): RuntimeStatus => { return runtime.getStatus() }) + + ipcMain.removeHandler('runtime:getTerminalFitOverrides') + ipcMain.handle( + 'runtime:getTerminalFitOverrides', + (): { ptyId: string; mode: 'mobile-fit'; cols: number; rows: number }[] => { + const overrides = runtime.getAllTerminalFitOverrides() + return Array.from(overrides.entries()).map(([ptyId, override]) => ({ + ptyId, + ...override + })) + } + ) + + // Why: the desktop "Restore" button sets the display mode to 'desktop' and + // applies it, which restores the PTY to its original dimensions and emits + // a 'resized' event to any active mobile subscriber. This uses the same + // code path as the mobile toggle button (terminal.setDisplayMode RPC). + ipcMain.removeHandler('runtime:restoreTerminalFit') + ipcMain.handle('runtime:restoreTerminalFit', (_event, args: { ptyId: string }) => { + const override = runtime.getTerminalFitOverride(args.ptyId) + if (!override) { + return { restored: false } + } + try { + runtime.setMobileDisplayMode(args.ptyId, 'desktop') + runtime.applyMobileDisplayMode(args.ptyId) + return { restored: true } + } catch { + return { restored: false } + } + }) } diff --git a/src/main/providers/local-pty-shell-ready.ts b/src/main/providers/local-pty-shell-ready.ts index 7ad3c09b902..b15d8f59bbc 100644 --- a/src/main/providers/local-pty-shell-ready.ts +++ b/src/main/providers/local-pty-shell-ready.ts @@ -210,13 +210,15 @@ __orca_restore_attribution_path() { export PATH="\${ORCA_ATTRIBUTION_SHIM_DIR}:$PATH" } __orca_restore_attribution_path -# Why: emit OSC 133;A only after the user's startup hooks finish so Orca knows -# the prompt is actually ready for a long startup command paste. +# Why: zsh precmd runs before the prompt is drawn and before zle owns input, +# which can double-echo startup commands. line-init fires when zle is ready. if [[ "\${ORCA_SHELL_READY_MARKER:-0}" == "1" ]]; then __orca_prompt_mark() { printf "\\033]133;A\\007" } - precmd_functions=(\${precmd_functions[@]} __orca_prompt_mark) + autoload -Uz add-zle-hook-widget + zle -N __orca_prompt_mark + add-zle-hook-widget line-init __orca_prompt_mark fi ` const bashRc = getBashShellReadyRcfileContent() diff --git a/src/main/runtime/device-registry.ts b/src/main/runtime/device-registry.ts new file mode 100644 index 00000000000..fed25b62212 --- /dev/null +++ b/src/main/runtime/device-registry.ts @@ -0,0 +1,83 @@ +// Why: per-device tokens replace the shared runtime auth token for WebSocket +// (mobile) connections. Each paired device gets its own revocable token so +// compromising one device doesn't expose others. The registry is a simple +// JSON file with hardened permissions matching the runtime metadata pattern. +import { randomBytes, randomUUID } from 'crypto' +import { existsSync, readFileSync, writeFileSync, chmodSync } from 'fs' +import { join } from 'path' + +const DEVICE_REGISTRY_FILENAME = 'orca-devices.json' + +export type DeviceEntry = { + deviceId: string + name: string + token: string + pairedAt: number + lastSeenAt: number +} + +export class DeviceRegistry { + private readonly registryPath: string + private devices: DeviceEntry[] = [] + + constructor(userDataPath: string) { + this.registryPath = join(userDataPath, DEVICE_REGISTRY_FILENAME) + this.load() + } + + addDevice(name: string): DeviceEntry { + const entry: DeviceEntry = { + deviceId: randomUUID(), + name, + token: randomBytes(24).toString('hex'), + pairedAt: Date.now(), + lastSeenAt: 0 + } + this.devices.push(entry) + this.save() + return entry + } + + removeDevice(deviceId: string): boolean { + const before = this.devices.length + this.devices = this.devices.filter((d) => d.deviceId !== deviceId) + if (this.devices.length < before) { + this.save() + return true + } + return false + } + + listDevices(): readonly DeviceEntry[] { + return this.devices + } + + validateToken(token: string): DeviceEntry | null { + return this.devices.find((d) => d.token === token) ?? null + } + + updateLastSeen(deviceId: string): void { + const device = this.devices.find((d) => d.deviceId === deviceId) + if (device) { + device.lastSeenAt = Date.now() + this.save() + } + } + + private load(): void { + if (!existsSync(this.registryPath)) { + this.devices = [] + return + } + try { + this.devices = JSON.parse(readFileSync(this.registryPath, 'utf-8')) as DeviceEntry[] + } catch { + this.devices = [] + } + } + + private save(): void { + writeFileSync(this.registryPath, JSON.stringify(this.devices, null, 2), { mode: 0o600 }) + chmodSync(this.registryPath, 0o600) + } +} diff --git a/src/main/runtime/e2ee-keypair.ts b/src/main/runtime/e2ee-keypair.ts new file mode 100644 index 00000000000..de0373ebafe --- /dev/null +++ b/src/main/runtime/e2ee-keypair.ts @@ -0,0 +1,50 @@ +// Why: the E2EE keypair enables application-layer encryption between mobile +// and desktop over plain ws://. The public key is embedded in the QR pairing +// offer so the mobile client can derive a shared secret via ECDH. +import { existsSync, readFileSync, writeFileSync, chmodSync } from 'fs' +import { join } from 'path' +import nacl from 'tweetnacl' + +const KEYPAIR_FILENAME = 'orca-e2ee-keypair.json' +const KEYPAIR_VERSION = 1 + +type KeypairFile = { + v: number + publicKeyB64: string + secretKeyB64: string +} + +export type E2EEKeypair = { + publicKey: Uint8Array + secretKey: Uint8Array + publicKeyB64: string +} + +export function loadOrCreateE2EEKeypair(userDataPath: string): E2EEKeypair { + const filePath = join(userDataPath, KEYPAIR_FILENAME) + + if (existsSync(filePath)) { + try { + const raw: KeypairFile = JSON.parse(readFileSync(filePath, 'utf-8')) + if (raw.v === KEYPAIR_VERSION && raw.publicKeyB64 && raw.secretKeyB64) { + const publicKey = Uint8Array.from(Buffer.from(raw.publicKeyB64, 'base64')) + const secretKey = Uint8Array.from(Buffer.from(raw.secretKeyB64, 'base64')) + if (publicKey.length === 32 && secretKey.length === 32) { + return { publicKey, secretKey, publicKeyB64: raw.publicKeyB64 } + } + } + } catch { + // Malformed file — regenerate below. + } + } + + const keypair = nacl.box.keyPair() + const publicKeyB64 = Buffer.from(keypair.publicKey).toString('base64') + const secretKeyB64 = Buffer.from(keypair.secretKey).toString('base64') + + const data: KeypairFile = { v: KEYPAIR_VERSION, publicKeyB64, secretKeyB64 } + writeFileSync(filePath, JSON.stringify(data, null, 2), 'utf-8') + chmodSync(filePath, 0o600) + + return { publicKey: keypair.publicKey, secretKey: keypair.secretKey, publicKeyB64 } +} diff --git a/src/main/runtime/fit-override-integration.test.ts b/src/main/runtime/fit-override-integration.test.ts new file mode 100644 index 00000000000..763b7d8cbf9 --- /dev/null +++ b/src/main/runtime/fit-override-integration.test.ts @@ -0,0 +1,275 @@ +/** + * Integration test for the mobile-fit override flow. + * Tests the full lifecycle: mobile-fit → restore → verify PTY resized. + */ +import { describe, expect, it, vi } from 'vitest' +import { OrcaRuntimeService } from './orca-runtime' + +vi.mock('../git/worktree', () => ({ + listWorktrees: vi.fn().mockResolvedValue([ + { + path: '/tmp/worktree-a', + head: 'abc', + branch: 'feature/foo', + isBare: false, + isMainWorktree: false + } + ]) +})) + +vi.mock('../hooks', () => ({ + createSetupRunnerScript: vi.fn(), + getEffectiveHooks: vi.fn().mockReturnValue(null), + runHook: vi.fn().mockResolvedValue({ success: true, output: '' }) +})) + +vi.mock('../ipc/worktree-logic', async (importOriginal) => { + const actual = (await importOriginal()) as Record + return { ...actual, computeWorktreePath: vi.fn(), ensurePathWithinWorkspace: vi.fn() } +}) + +vi.mock('../ipc/filesystem-auth', () => ({ + invalidateAuthorizedRootsCache: vi.fn() +})) + +vi.mock('../git/repo', async (importOriginal) => { + const actual = (await importOriginal()) as Record + return { + ...actual, + getDefaultBaseRef: vi.fn().mockReturnValue('origin/main'), + getBranchConflictKind: vi.fn().mockResolvedValue(null), + getGitUsername: vi.fn().mockReturnValue('') + } +}) + +const store = { + getRepo: () => ({ + id: 'repo-1', + path: '/tmp/repo', + displayName: 'repo', + badgeColor: 'blue', + addedAt: 1 + }), + getRepos: () => [store.getRepo()], + addRepo: () => {}, + updateRepo: () => undefined as never, + getAllWorktreeMeta: () => ({}), + getWorktreeMeta: () => undefined, + getGitHubCache: () => ({ pr: {}, issue: {} }), + setWorktreeMeta: () => undefined as never, + removeWorktreeMeta: () => {}, + getSettings: () => ({ + workspaceDir: '/tmp/workspaces', + nestWorkspaces: false, + refreshLocalBaseRefOnWorktreeCreate: false, + branchPrefix: 'none', + branchPrefixCustom: '' + }) +} + +describe('fit override integration', () => { + it('full lifecycle: fit → getSize → restore → verify PTY dims', () => { + const runtime = new OrcaRuntimeService(store) + const currentSize = { cols: 150, rows: 40 } + const resizes: { ptyId: string; cols: number; rows: number }[] = [] + const notifications: { ptyId: string; mode: string; cols: number; rows: number }[] = [] + + runtime.setPtyController({ + write: () => true, + kill: () => true, + getForegroundProcess: async () => null, + resize: (ptyId, cols, rows) => { + currentSize.cols = cols + currentSize.rows = rows + resizes.push({ ptyId, cols, rows }) + return true + }, + getSize: () => ({ ...currentSize }) + }) + runtime.setNotifier({ + worktreesChanged: vi.fn(), + reposChanged: vi.fn(), + activateWorktree: vi.fn(), + createTerminal: vi.fn(), + splitTerminal: vi.fn(), + renameTerminal: vi.fn(), + focusTerminal: vi.fn(), + closeTerminal: vi.fn(), + sleepWorktree: vi.fn(), + terminalFitOverrideChanged: (ptyId, mode, cols, rows) => { + notifications.push({ ptyId, mode, cols, rows }) + } + }) + + // Simulate a synced leaf (mounted desktop pane) + runtime.attachWindow(1) + runtime.syncWindowGraph(1, { + tabs: [ + { + tabId: 'tab-1', + worktreeId: 'repo-1::/tmp/worktree-a', + title: 'Terminal', + activeLeafId: 'pane:1', + layout: null + } + ], + leaves: [ + { + tabId: 'tab-1', + worktreeId: 'repo-1::/tmp/worktree-a', + leafId: 'pane:1', + paneRuntimeId: 1, + ptyId: 'pty-1' + } + ] + }) + + console.log('=== Step 1: Initial state ===') + console.log('PTY size:', currentSize) + expect(currentSize).toEqual({ cols: 150, rows: 40 }) + + console.log('\n=== Step 2: Mobile fit to 45x20 ===') + const fitResult = runtime.resizeForClient('pty-1', 'mobile-fit', 'client-phone', 45, 20) + console.log('Fit result:', fitResult) + console.log('PTY size after fit:', currentSize) + console.log('Override:', runtime.getTerminalFitOverride('pty-1')) + expect(currentSize).toEqual({ cols: 45, rows: 20 }) + expect(fitResult.previousCols).toBe(150) + expect(fitResult.previousRows).toBe(40) + + console.log('\n=== Step 3: Desktop Restore (via IPC handler path) ===') + // Simulate what runtime:restoreTerminalFit IPC handler does + const override = runtime.getTerminalFitOverride('pty-1') + expect(override).not.toBeNull() + const restoreResult = runtime.resizeForClient('pty-1', 'restore', override!.clientId) + console.log('Restore result:', restoreResult) + console.log('PTY size after restore:', currentSize) + console.log('Override after restore:', runtime.getTerminalFitOverride('pty-1')) + expect(currentSize).toEqual({ cols: 150, rows: 40 }) + expect(restoreResult.mode).toBe('desktop-fit') + + console.log('\n=== Step 4: Verify all resizes ===') + console.log('All resize calls:', resizes) + expect(resizes).toEqual([ + { ptyId: 'pty-1', cols: 45, rows: 20 }, + { ptyId: 'pty-1', cols: 150, rows: 40 } + ]) + + console.log('\n=== Step 5: Verify notifications ===') + console.log('All notifications:', notifications) + expect(notifications).toEqual([ + { ptyId: 'pty-1', mode: 'mobile-fit', cols: 45, rows: 20 }, + { ptyId: 'pty-1', mode: 'desktop-fit', cols: 150, rows: 40 } + ]) + + console.log('\n=== Step 6: Mobile restore via RPC path ===') + // Re-fit, then restore via the mobile RPC handler path + runtime.resizeForClient('pty-1', 'mobile-fit', 'client-phone', 45, 20) + expect(currentSize).toEqual({ cols: 45, rows: 20 }) + + // This is what terminal.resizeForClient RPC handler does + const mobileRestore = runtime.resizeForClient('pty-1', 'restore', 'client-phone') + console.log('Mobile restore result:', mobileRestore) + console.log('PTY size after mobile restore:', currentSize) + expect(currentSize).toEqual({ cols: 150, rows: 40 }) + }) + + it('restore resizes PTY even with mounted leaf (the bug fix)', () => { + const runtime = new OrcaRuntimeService(store) + let ptySize = { cols: 120, rows: 35 } + const resizes: string[] = [] + + runtime.setPtyController({ + write: () => true, + kill: () => true, + getForegroundProcess: async () => null, + resize: (ptyId, cols, rows) => { + ptySize = { cols, rows } + resizes.push(`${ptyId}:${cols}x${rows}`) + return true + }, + getSize: () => ({ ...ptySize }) + }) + runtime.setNotifier({ + worktreesChanged: vi.fn(), + reposChanged: vi.fn(), + activateWorktree: vi.fn(), + createTerminal: vi.fn(), + splitTerminal: vi.fn(), + renameTerminal: vi.fn(), + focusTerminal: vi.fn(), + closeTerminal: vi.fn(), + sleepWorktree: vi.fn(), + terminalFitOverrideChanged: vi.fn() + }) + + // Synced leaf = mounted desktop pane + runtime.attachWindow(1) + runtime.syncWindowGraph(1, { + tabs: [ + { + tabId: 'tab-1', + worktreeId: 'repo-1::/tmp/worktree-a', + title: 'Claude', + activeLeafId: 'pane:1', + layout: null + } + ], + leaves: [ + { + tabId: 'tab-1', + worktreeId: 'repo-1::/tmp/worktree-a', + leafId: 'pane:1', + paneRuntimeId: 1, + ptyId: 'pty-1' + } + ] + }) + + // Mobile fit + runtime.resizeForClient('pty-1', 'mobile-fit', 'phone-a', 42, 18) + expect(ptySize).toEqual({ cols: 42, rows: 18 }) + + // Restore — THIS is the critical assertion. + // Before the fix, mounted leaves skipped the PTY resize. + runtime.resizeForClient('pty-1', 'restore', 'phone-a') + expect(ptySize).toEqual({ cols: 120, rows: 35 }) + expect(resizes).toEqual(['pty-1:42x18', 'pty-1:120x35']) + }) + + it('disconnect auto-restore also resizes PTY', () => { + const runtime = new OrcaRuntimeService(store) + let ptySize = { cols: 100, rows: 30 } + + runtime.setPtyController({ + write: () => true, + kill: () => true, + getForegroundProcess: async () => null, + resize: (_ptyId, cols, rows) => { + ptySize = { cols, rows } + return true + }, + getSize: () => ({ ...ptySize }) + }) + runtime.setNotifier({ + worktreesChanged: vi.fn(), + reposChanged: vi.fn(), + activateWorktree: vi.fn(), + createTerminal: vi.fn(), + splitTerminal: vi.fn(), + renameTerminal: vi.fn(), + focusTerminal: vi.fn(), + closeTerminal: vi.fn(), + sleepWorktree: vi.fn(), + terminalFitOverrideChanged: vi.fn() + }) + + runtime.resizeForClient('pty-1', 'mobile-fit', 'phone-disconnect', 45, 20) + expect(ptySize).toEqual({ cols: 45, rows: 20 }) + + // Simulate WS disconnect + runtime.onClientDisconnected('phone-disconnect') + expect(ptySize).toEqual({ cols: 100, rows: 30 }) + expect(runtime.getTerminalFitOverride('pty-1')).toBeNull() + }) +}) diff --git a/src/main/runtime/mobile-subscribe-integration.test.ts b/src/main/runtime/mobile-subscribe-integration.test.ts new file mode 100644 index 00000000000..f5eed223526 --- /dev/null +++ b/src/main/runtime/mobile-subscribe-integration.test.ts @@ -0,0 +1,471 @@ +/* oxlint-disable max-lines -- Why: integration tests cover the full mobile subscribe lifecycle across many scenarios; splitting would scatter related assertions. */ +/** + * Integration tests for the server-authoritative mobile subscribe lifecycle. + * Tests handleMobileSubscribe, handleMobileUnsubscribe, applyMobileDisplayMode, + * debounced restore, inline restore on timer cancel, and cleanup paths. + */ +import { describe, expect, it, vi, beforeEach, afterEach } from 'vitest' +import { OrcaRuntimeService } from './orca-runtime' + +vi.mock('../git/worktree', () => ({ + listWorktrees: vi.fn().mockResolvedValue([ + { + path: '/tmp/worktree-a', + head: 'abc', + branch: 'feature/foo', + isBare: false, + isMainWorktree: false + } + ]) +})) + +vi.mock('../hooks', () => ({ + createSetupRunnerScript: vi.fn(), + getEffectiveHooks: vi.fn().mockReturnValue(null), + runHook: vi.fn().mockResolvedValue({ success: true, output: '' }) +})) + +vi.mock('../ipc/worktree-logic', async (importOriginal) => { + const actual = (await importOriginal()) as Record + return { ...actual, computeWorktreePath: vi.fn(), ensurePathWithinWorkspace: vi.fn() } +}) + +vi.mock('../ipc/filesystem-auth', () => ({ + invalidateAuthorizedRootsCache: vi.fn() +})) + +vi.mock('../git/repo', async (importOriginal) => { + const actual = (await importOriginal()) as Record + return { + ...actual, + getDefaultBaseRef: vi.fn().mockReturnValue('origin/main'), + getBranchConflictKind: vi.fn().mockResolvedValue(null), + getGitUsername: vi.fn().mockReturnValue('') + } +}) + +const store = { + getRepo: () => ({ + id: 'repo-1', + path: '/tmp/repo', + displayName: 'repo', + badgeColor: 'blue', + addedAt: 1 + }), + getRepos: () => [store.getRepo()], + addRepo: () => {}, + updateRepo: () => undefined as never, + getAllWorktreeMeta: () => ({}), + getWorktreeMeta: () => undefined, + getGitHubCache: () => ({ pr: {}, issue: {} }), + setWorktreeMeta: () => undefined as never, + removeWorktreeMeta: () => {}, + getSettings: () => ({ + workspaceDir: '/tmp/workspaces', + nestWorkspaces: false, + refreshLocalBaseRefOnWorktreeCreate: false, + branchPrefix: 'none', + branchPrefixCustom: '' + }) +} + +function createRuntime() { + const runtime = new OrcaRuntimeService(store) + const ptySizes = new Map() + ptySizes.set('pty-1', { cols: 150, rows: 40 }) + ptySizes.set('pty-2', { cols: 120, rows: 35 }) + ptySizes.set('pty-3', { cols: 100, rows: 30 }) + + const resizes: { ptyId: string; cols: number; rows: number }[] = [] + const notifications: { ptyId: string; mode: string; cols: number; rows: number }[] = [] + + runtime.setPtyController({ + write: () => true, + kill: () => true, + getForegroundProcess: async () => null, + resize: (ptyId, cols, rows) => { + ptySizes.set(ptyId, { cols, rows }) + resizes.push({ ptyId, cols, rows }) + return true + }, + getSize: (ptyId) => ptySizes.get(ptyId) ?? null + }) + runtime.setNotifier({ + worktreesChanged: vi.fn(), + reposChanged: vi.fn(), + activateWorktree: vi.fn(), + createTerminal: vi.fn(), + splitTerminal: vi.fn(), + renameTerminal: vi.fn(), + focusTerminal: vi.fn(), + closeTerminal: vi.fn(), + sleepWorktree: vi.fn(), + terminalFitOverrideChanged: (ptyId, mode, cols, rows) => { + notifications.push({ ptyId, mode, cols, rows }) + } + }) + + return { runtime, ptySizes, resizes, notifications } +} + +describe('mobile subscribe integration', () => { + beforeEach(() => { + vi.useFakeTimers() + }) + + afterEach(() => { + vi.useRealTimers() + }) + + it('handleMobileSubscribe resizes PTY to phone dims', () => { + const { runtime, ptySizes, resizes, notifications } = createRuntime() + + const result = runtime.handleMobileSubscribe('pty-1', 'client-a', { cols: 45, rows: 20 }) + + expect(result).toBe(true) + expect(ptySizes.get('pty-1')).toEqual({ cols: 45, rows: 20 }) + expect(resizes).toEqual([{ ptyId: 'pty-1', cols: 45, rows: 20 }]) + expect(notifications).toEqual([{ ptyId: 'pty-1', mode: 'mobile-fit', cols: 45, rows: 20 }]) + expect(runtime.isMobileSubscriberActive('pty-1')).toBe(true) + }) + + it('handleMobileSubscribe skips resize when mode is desktop', () => { + const { runtime, ptySizes, resizes } = createRuntime() + runtime.setMobileDisplayMode('pty-1', 'desktop') + + const result = runtime.handleMobileSubscribe('pty-1', 'client-a', { cols: 45, rows: 20 }) + + expect(result).toBe(false) + expect(ptySizes.get('pty-1')).toEqual({ cols: 150, rows: 40 }) + expect(resizes).toEqual([]) + }) + + it('handleMobileSubscribe skips resize when no viewport provided', () => { + const { runtime, ptySizes, resizes } = createRuntime() + + const result = runtime.handleMobileSubscribe('pty-1', 'client-a') + + expect(result).toBe(false) + expect(ptySizes.get('pty-1')).toEqual({ cols: 150, rows: 40 }) + expect(resizes).toEqual([]) + }) + + it('handleMobileUnsubscribe restores PTY after 300ms debounce in auto mode', () => { + const { runtime, ptySizes } = createRuntime() + runtime.handleMobileSubscribe('pty-1', 'client-a', { cols: 45, rows: 20 }) + expect(ptySizes.get('pty-1')).toEqual({ cols: 45, rows: 20 }) + + runtime.handleMobileUnsubscribe('pty-1', 'client-a') + // Not yet restored + expect(ptySizes.get('pty-1')).toEqual({ cols: 45, rows: 20 }) + + vi.advanceTimersByTime(300) + expect(ptySizes.get('pty-1')).toEqual({ cols: 150, rows: 40 }) + }) + + it('handleMobileUnsubscribe does not restore in phone mode', () => { + const { runtime, ptySizes } = createRuntime() + runtime.setMobileDisplayMode('pty-1', 'phone') + // In phone mode, handleMobileSubscribe still resizes because mode is 'phone' + runtime.handleMobileSubscribe('pty-1', 'client-a', { cols: 45, rows: 20 }) + expect(ptySizes.get('pty-1')).toEqual({ cols: 45, rows: 20 }) + + runtime.handleMobileUnsubscribe('pty-1', 'client-a') + vi.advanceTimersByTime(1000) + // Still at phone dims — no restore for phone mode + expect(ptySizes.get('pty-1')).toEqual({ cols: 45, rows: 20 }) + }) + + // TODO: inline restore on re-subscribe not yet implemented + it.skip('re-subscribe within 300ms cancels debounce timer and inline-restores old PTY', () => { + const { runtime, ptySizes } = createRuntime() + runtime.handleMobileSubscribe('pty-1', 'client-a', { cols: 45, rows: 20 }) + runtime.handleMobileUnsubscribe('pty-1', 'client-a') + + // Re-subscribe to a different terminal before the timer fires + vi.advanceTimersByTime(100) + runtime.handleMobileSubscribe('pty-2', 'client-a', { cols: 45, rows: 20 }) + + // pty-1 was inline-restored when pty-2 subscribed (timer cancelled + immediate restore) + expect(ptySizes.get('pty-1')).toEqual({ cols: 150, rows: 40 }) + expect(ptySizes.get('pty-2')).toEqual({ cols: 45, rows: 20 }) + + // Advancing past the 300ms debounce should not cause a second restore + vi.advanceTimersByTime(300) + expect(ptySizes.get('pty-1')).toEqual({ cols: 150, rows: 40 }) + }) + + // TODO: inline restore on re-subscribe not yet implemented + it.skip('rapid A→B→C tab navigation: inline restore of A when B subscribes', () => { + const { runtime, ptySizes } = createRuntime() + + // Subscribe to A + runtime.handleMobileSubscribe('pty-1', 'client-a', { cols: 45, rows: 20 }) + expect(ptySizes.get('pty-1')).toEqual({ cols: 45, rows: 20 }) + + // Unsubscribe A, subscribe B — A's pending timer is cancelled, A gets inline restore + runtime.handleMobileUnsubscribe('pty-1', 'client-a') + runtime.handleMobileSubscribe('pty-2', 'client-a', { cols: 45, rows: 20 }) + + // pty-1 should be restored inline (not waiting for timer) + expect(ptySizes.get('pty-1')).toEqual({ cols: 150, rows: 40 }) + expect(ptySizes.get('pty-2')).toEqual({ cols: 45, rows: 20 }) + + // Unsubscribe B, subscribe C — B's pending timer cancelled, B gets inline restore + runtime.handleMobileUnsubscribe('pty-2', 'client-a') + runtime.handleMobileSubscribe('pty-3', 'client-a', { cols: 45, rows: 20 }) + + expect(ptySizes.get('pty-2')).toEqual({ cols: 120, rows: 35 }) + expect(ptySizes.get('pty-3')).toEqual({ cols: 45, rows: 20 }) + + // Verify final state + vi.advanceTimersByTime(1000) + expect(ptySizes.get('pty-1')).toEqual({ cols: 150, rows: 40 }) + expect(ptySizes.get('pty-2')).toEqual({ cols: 120, rows: 35 }) + expect(ptySizes.get('pty-3')).toEqual({ cols: 45, rows: 20 }) + }) + + it('preserves previousDims across re-subscribes to same terminal', () => { + const { runtime, ptySizes } = createRuntime() + + // First subscribe at desktop 150x40 + runtime.handleMobileSubscribe('pty-1', 'client-a', { cols: 45, rows: 20 }) + expect(ptySizes.get('pty-1')).toEqual({ cols: 45, rows: 20 }) + + // Re-subscribe to the same terminal (e.g., after reconnect) + // The PTY is already at 45x20, but previousDims should still be 150x40 + runtime.handleMobileSubscribe('pty-1', 'client-a', { cols: 45, rows: 20 }) + + // Unsubscribe and let restore fire + runtime.handleMobileUnsubscribe('pty-1', 'client-a') + vi.advanceTimersByTime(300) + + // Should restore to original desktop dims, not 45x20 + expect(ptySizes.get('pty-1')).toEqual({ cols: 150, rows: 40 }) + }) + + it('clamps viewport to valid range', () => { + const { runtime, ptySizes } = createRuntime() + + runtime.handleMobileSubscribe('pty-1', 'client-a', { cols: 10, rows: 3 }) + // Should clamp to minimum 20x8 + expect(ptySizes.get('pty-1')).toEqual({ cols: 20, rows: 8 }) + }) + + describe('display mode', () => { + it('defaults to auto', () => { + const { runtime } = createRuntime() + expect(runtime.getMobileDisplayMode('pty-1')).toBe('auto') + }) + + it('set/get round-trip', () => { + const { runtime } = createRuntime() + runtime.setMobileDisplayMode('pty-1', 'phone') + expect(runtime.getMobileDisplayMode('pty-1')).toBe('phone') + + runtime.setMobileDisplayMode('pty-1', 'desktop') + expect(runtime.getMobileDisplayMode('pty-1')).toBe('desktop') + + // Setting to 'auto' deletes the entry (same as default) + runtime.setMobileDisplayMode('pty-1', 'auto') + expect(runtime.getMobileDisplayMode('pty-1')).toBe('auto') + }) + }) + + describe('applyMobileDisplayMode', () => { + it('desktop mode restores PTY when currently phone-fitted', () => { + const { runtime, ptySizes } = createRuntime() + runtime.handleMobileSubscribe('pty-1', 'client-a', { cols: 45, rows: 20 }) + expect(ptySizes.get('pty-1')).toEqual({ cols: 45, rows: 20 }) + + const resizeEvents: unknown[] = [] + runtime.subscribeToTerminalResize('pty-1', (event) => resizeEvents.push(event)) + + runtime.setMobileDisplayMode('pty-1', 'desktop') + runtime.applyMobileDisplayMode('pty-1') + + expect(ptySizes.get('pty-1')).toEqual({ cols: 150, rows: 40 }) + expect(resizeEvents).toHaveLength(1) + expect(resizeEvents[0]).toMatchObject({ + cols: 150, + rows: 40, + displayMode: 'desktop', + reason: 'mode-change' + }) + }) + + it('auto mode re-fits PTY when subscriber exists and not phone-fitted', () => { + const { runtime, ptySizes } = createRuntime() + runtime.handleMobileSubscribe('pty-1', 'client-a', { cols: 45, rows: 20 }) + + // Switch to desktop (restores to 150x40) + runtime.setMobileDisplayMode('pty-1', 'desktop') + runtime.applyMobileDisplayMode('pty-1') + expect(ptySizes.get('pty-1')).toEqual({ cols: 150, rows: 40 }) + + const resizeEvents: unknown[] = [] + runtime.subscribeToTerminalResize('pty-1', (event) => resizeEvents.push(event)) + + // Switch back to auto (should re-fit to phone dims) + runtime.setMobileDisplayMode('pty-1', 'auto') + runtime.applyMobileDisplayMode('pty-1') + + expect(ptySizes.get('pty-1')).toEqual({ cols: 45, rows: 20 }) + expect(resizeEvents).toHaveLength(1) + expect(resizeEvents[0]).toMatchObject({ + displayMode: 'auto', + reason: 'mode-change' + }) + }) + }) + + describe('cleanup paths', () => { + it('onClientDisconnected restores all PTYs immediately (no debounce)', () => { + const { runtime, ptySizes } = createRuntime() + + runtime.handleMobileSubscribe('pty-1', 'client-a', { cols: 45, rows: 20 }) + runtime.handleMobileSubscribe('pty-2', 'client-a', { cols: 45, rows: 20 }) + + runtime.onClientDisconnected('client-a') + + // Both PTYs restored immediately + expect(ptySizes.get('pty-1')).toEqual({ cols: 150, rows: 40 }) + expect(ptySizes.get('pty-2')).toEqual({ cols: 120, rows: 35 }) + expect(runtime.isMobileSubscriberActive('pty-1')).toBe(false) + expect(runtime.isMobileSubscriberActive('pty-2')).toBe(false) + }) + + it('onClientDisconnected cancels pending restore timers', () => { + const { runtime, ptySizes } = createRuntime() + runtime.handleMobileSubscribe('pty-1', 'client-a', { cols: 45, rows: 20 }) + runtime.handleMobileUnsubscribe('pty-1', 'client-a') + // Timer is pending + + runtime.onClientDisconnected('client-a') + // Timer should be cancelled, PTY already restored by disconnect handler + + vi.advanceTimersByTime(1000) + expect(ptySizes.get('pty-1')).toEqual({ cols: 150, rows: 40 }) + }) + + it('onPtyExit cleans up mobileSubscribers and pending timers', () => { + const { runtime } = createRuntime() + runtime.handleMobileSubscribe('pty-1', 'client-a', { cols: 45, rows: 20 }) + runtime.handleMobileUnsubscribe('pty-1', 'client-a') + // Timer pending for pty-1 + + runtime.onPtyExit('pty-1', 0) + expect(runtime.isMobileSubscriberActive('pty-1')).toBe(false) + expect(runtime.getMobileDisplayMode('pty-1')).toBe('auto') + + // Timer should have been cancelled — no crash from resizing a dead PTY + vi.advanceTimersByTime(1000) + }) + + it('onPtyExit does not cancel timers for other PTYs', () => { + const { runtime, ptySizes } = createRuntime() + runtime.handleMobileSubscribe('pty-1', 'client-a', { cols: 45, rows: 20 }) + runtime.handleMobileUnsubscribe('pty-1', 'client-a') + + // pty-2 exits — should not affect pty-1's pending restore + runtime.onPtyExit('pty-2', 0) + + vi.advanceTimersByTime(300) + expect(ptySizes.get('pty-1')).toEqual({ cols: 150, rows: 40 }) + }) + }) + + describe('resize listener system', () => { + it('subscribe/unsubscribe lifecycle', () => { + const { runtime } = createRuntime() + const events: unknown[] = [] + const unsubscribe = runtime.subscribeToTerminalResize('pty-1', (e) => events.push(e)) + + runtime.handleMobileSubscribe('pty-1', 'client-a', { cols: 45, rows: 20 }) + runtime.setMobileDisplayMode('pty-1', 'desktop') + runtime.applyMobileDisplayMode('pty-1') + + expect(events.length).toBeGreaterThan(0) + + const countBefore = events.length + unsubscribe() + + // After unsubscribe, no more events + runtime.setMobileDisplayMode('pty-1', 'auto') + runtime.applyMobileDisplayMode('pty-1') + expect(events.length).toBe(countBefore) + }) + }) + + describe('onExternalPtyResize', () => { + it('updates previousCols when desktop renderer resizes PTY after desktop restore', () => { + const { runtime, ptySizes } = createRuntime() + runtime.handleMobileSubscribe('pty-1', 'client-a', { cols: 45, rows: 20 }) + + // Toggle to desktop — restores to previousCols (150x40) + runtime.setMobileDisplayMode('pty-1', 'desktop') + runtime.applyMobileDisplayMode('pty-1') + expect(ptySizes.get('pty-1')).toEqual({ cols: 150, rows: 40 }) + + // Simulate desktop renderer's safeFit correcting to split-pane width + runtime.onExternalPtyResize('pty-1', 105, 40) + + // Toggle back to auto — should capture previousCols=105 (not 150) + runtime.setMobileDisplayMode('pty-1', 'auto') + runtime.applyMobileDisplayMode('pty-1') + expect(ptySizes.get('pty-1')).toEqual({ cols: 45, rows: 20 }) + + // Toggle to desktop again — should restore to 105 (the corrected value) + runtime.setMobileDisplayMode('pty-1', 'desktop') + runtime.applyMobileDisplayMode('pty-1') + expect(ptySizes.get('pty-1')).toEqual({ cols: 105, rows: 40 }) + }) + + it('uses lastRendererSize for previousCols on first subscribe', () => { + const { runtime, ptySizes } = createRuntime() + + // Simulate: PTY spawned at 214 (ptySizes), but renderer already fit to 105 + ptySizes.set('pty-1', { cols: 214, rows: 72 }) + runtime.onExternalPtyResize('pty-1', 105, 40) + + // First mobile subscribe — should use rendererSize (105) not ptySizes (214) + runtime.handleMobileSubscribe('pty-1', 'client-a', { cols: 45, rows: 20 }) + expect(ptySizes.get('pty-1')).toEqual({ cols: 45, rows: 20 }) + + // Toggle to desktop — should restore to 105, not 214 + runtime.setMobileDisplayMode('pty-1', 'desktop') + runtime.applyMobileDisplayMode('pty-1') + expect(ptySizes.get('pty-1')).toEqual({ cols: 105, rows: 40 }) + }) + + it('does not update previousCols when PTY is phone-fitted', () => { + const { runtime, ptySizes } = createRuntime() + runtime.handleMobileSubscribe('pty-1', 'client-a', { cols: 45, rows: 20 }) + + // PTY is phone-fitted (wasResizedToPhone=true) — external resize should not + // overwrite previousCols with phone dims + runtime.onExternalPtyResize('pty-1', 45, 20) + + // Toggle to desktop — should still restore to original 150x40 + runtime.setMobileDisplayMode('pty-1', 'desktop') + runtime.applyMobileDisplayMode('pty-1') + expect(ptySizes.get('pty-1')).toEqual({ cols: 150, rows: 40 }) + }) + }) + + describe('backward compatibility', () => { + it('old resizeForClient still works alongside new system', () => { + const { runtime, ptySizes } = createRuntime() + + // Old flow: explicit resizeForClient + const fitResult = runtime.resizeForClient('pty-1', 'mobile-fit', 'client-old', 45, 20) + expect(ptySizes.get('pty-1')).toEqual({ cols: 45, rows: 20 }) + expect(fitResult.mode).toBe('mobile-fit') + + // Old flow: restore + const restoreResult = runtime.resizeForClient('pty-1', 'restore', 'client-old') + expect(ptySizes.get('pty-1')).toEqual({ cols: 150, rows: 40 }) + expect(restoreResult.mode).toBe('desktop-fit') + }) + }) +}) diff --git a/src/main/runtime/orca-runtime.test.ts b/src/main/runtime/orca-runtime.test.ts index 463b778b709..3fb54c5a637 100644 --- a/src/main/runtime/orca-runtime.test.ts +++ b/src/main/runtime/orca-runtime.test.ts @@ -2,7 +2,12 @@ import { afterEach, describe, expect, it, vi } from 'vitest' import type { WorktreeMeta } from '../../shared/types' import { addWorktree, listWorktrees, removeWorktree } from '../git/worktree' -import { createSetupRunnerScript, getEffectiveHooks, runHook } from '../hooks' +import { + createSetupRunnerScript, + getEffectiveHooks, + runHook, + shouldRunSetupForCreate +} from '../hooks' import { OrchestrationDb } from './orchestration/db' import { OrcaRuntimeService } from './orca-runtime' @@ -39,7 +44,12 @@ vi.mock('../git/worktree', () => ({ vi.mock('../hooks', () => ({ createSetupRunnerScript: vi.fn(), getEffectiveHooks: vi.fn().mockReturnValue(null), - runHook: vi.fn().mockResolvedValue({ success: true, output: '' }) + runHook: vi.fn().mockResolvedValue({ success: true, output: '' }), + shouldRunSetupForCreate: vi + .fn() + .mockImplementation((_repo: never, decision: string) => decision === 'run'), + getEffectiveSetupRunPolicy: vi.fn().mockReturnValue('auto'), + hasHooksFile: vi.fn().mockReturnValue(false) })) vi.mock('../ipc/worktree-logic', async (importOriginal) => { @@ -77,6 +87,8 @@ afterEach(() => { vi.mocked(createSetupRunnerScript).mockReset() vi.mocked(getEffectiveHooks).mockReset() vi.mocked(runHook).mockReset() + vi.mocked(shouldRunSetupForCreate).mockReset() + vi.mocked(shouldRunSetupForCreate).mockImplementation((_repo, decision) => decision === 'run') vi.mocked(getEffectiveHooks).mockReturnValue(null) computeWorktreePathMock.mockReset() ensurePathWithinWorkspaceMock.mockReset() @@ -156,6 +168,7 @@ const store = { ...meta }) as never, removeWorktreeMeta: () => {}, + getGitHubCache: () => undefined as never, getSettings: () => ({ workspaceDir: '/tmp/workspaces', nestWorkspaces: false, @@ -763,7 +776,11 @@ describe('OrcaRuntimeService', () => { repo: 'repo', path: '/tmp/worktree-a', branch: 'feature/foo', + displayName: 'foo', linkedIssue: 123, + linkedPR: null, + isPinned: false, + status: 'active', unread: false, liveTerminalCount: 1, hasAttachedPty: true, @@ -926,7 +943,9 @@ describe('OrcaRuntimeService', () => { splitTerminal: vi.fn(), renameTerminal: vi.fn(), focusTerminal: vi.fn(), - closeTerminal: vi.fn() + closeTerminal: vi.fn(), + sleepWorktree: vi.fn(), + terminalFitOverrideChanged: vi.fn() }) runtime.attachWindow(1) @@ -1001,7 +1020,9 @@ describe('OrcaRuntimeService', () => { splitTerminal: vi.fn(), renameTerminal: vi.fn(), focusTerminal: vi.fn(), - closeTerminal: vi.fn() + closeTerminal: vi.fn(), + sleepWorktree: vi.fn(), + terminalFitOverrideChanged: vi.fn() }) runtime.attachWindow(1) @@ -1092,7 +1113,9 @@ describe('OrcaRuntimeService', () => { splitTerminal: vi.fn(), renameTerminal: vi.fn(), focusTerminal: vi.fn(), - closeTerminal: vi.fn() + closeTerminal: vi.fn(), + sleepWorktree: vi.fn(), + terminalFitOverrideChanged: vi.fn() }) computeWorktreePathMock.mockReturnValue('/tmp/workspaces/cli-worktree') @@ -1150,6 +1173,7 @@ describe('OrcaRuntimeService', () => { return nextMeta }, removeWorktreeMeta: () => {}, + getGitHubCache: () => undefined as never, getSettings: () => ({ workspaceDir: 'C:\\workspaces', nestWorkspaces: false, diff --git a/src/main/runtime/orca-runtime.ts b/src/main/runtime/orca-runtime.ts index 3d2492bf173..be4210e28f7 100644 --- a/src/main/runtime/orca-runtime.ts +++ b/src/main/runtime/orca-runtime.ts @@ -14,7 +14,12 @@ import { join } from 'path' import { rm } from 'fs/promises' import { OrchestrationDb } from './orchestration/db' import { formatMessagesForInjection } from './orchestration/formatter' -import type { CreateWorktreeResult, Repo } from '../../shared/types' +import type { + CreateWorktreeResult, + Repo, + StatsSummary, + WorktreeStartupLaunch +} from '../../shared/types' import { isFolderRepo } from '../../shared/repo-kind' import type { RuntimeGraphStatus, @@ -32,6 +37,7 @@ import type { RuntimeTerminalWait, RuntimeTerminalWaitCondition, RuntimeWorktreePsSummary, + RuntimeWorktreeStatus, RuntimeTerminalShow, RuntimeTerminalSummary, RuntimeSyncedLeaf, @@ -89,7 +95,14 @@ import { getRecentDriftSubjects } from '../git/repo' import { listWorktrees, addWorktree, removeWorktree } from '../git/worktree' -import { createSetupRunnerScript, getEffectiveHooks, runHook } from '../hooks' +import { + createSetupRunnerScript, + getEffectiveHooks, + getEffectiveSetupRunPolicy, + hasHooksFile, + runHook, + shouldRunSetupForCreate +} from '../hooks' import { REPO_COLORS } from '../../shared/constants' import { listRepoWorktrees } from '../repo-worktrees' import type { Store } from '../persistence' @@ -107,6 +120,7 @@ import { areWorktreePathsEqual } from '../ipc/worktree-logic' import { invalidateAuthorizedRootsCache } from '../ipc/filesystem-auth' +import { HeadlessEmulator } from '../daemon/headless-emulator' import { killAllProcessesForWorktree } from './worktree-teardown' import type { IPtyProvider } from '../providers/types' @@ -119,6 +133,8 @@ type RuntimeStore = { getWorktreeMeta: Store['getWorktreeMeta'] setWorktreeMeta: Store['setWorktreeMeta'] removeWorktreeMeta: Store['removeWorktreeMeta'] + getGitHubCache: Store['getGitHubCache'] + getWorkspaceSession?: Store['getWorkspaceSession'] getSettings(): { workspaceDir: string nestWorkspaces: boolean @@ -142,16 +158,42 @@ type RuntimeLeafRecord = RuntimeSyncedLeaf & { lastAgentStatus: AgentStatus | null } +type RuntimePtyWorktreeRecord = { + ptyId: string + worktreeId: string + connected: boolean + lastOutputAt: number | null + tailBuffer: string[] + tailPartialLine: string + tailTruncated: boolean + tailLinesTotal: number + preview: string +} + +type RuntimeHeadlessTerminal = { + emulator: HeadlessEmulator + writeChain: Promise +} + type RuntimePtyController = { write(ptyId: string, data: string): boolean kill(ptyId: string): boolean getForegroundProcess(ptyId: string): Promise + resize?(ptyId: string, cols: number, rows: number): boolean + listProcesses?(): Promise<{ id: string; cwd: string; title: string }[]> + serializeBuffer?(ptyId: string): Promise<{ data: string; cols: number; rows: number } | null> + getSize?(ptyId: string): { cols: number; rows: number } | null } type RuntimeNotifier = { worktreesChanged(repoId: string): void reposChanged(): void - activateWorktree(repoId: string, worktreeId: string, setup?: CreateWorktreeResult['setup']): void + activateWorktree( + repoId: string, + worktreeId: string, + setup?: CreateWorktreeResult['setup'], + startup?: WorktreeStartupLaunch + ): void createTerminal(worktreeId: string, opts: { command?: string; title?: string }): void splitTerminal( tabId: string, @@ -161,6 +203,13 @@ type RuntimeNotifier = { renameTerminal(tabId: string, title: string | null): void focusTerminal(tabId: string, worktreeId: string): void closeTerminal(tabId: string, paneRuntimeId?: number): void + sleepWorktree(worktreeId: string): void + terminalFitOverrideChanged( + ptyId: string, + mode: 'mobile-fit' | 'desktop-fit', + cols: number, + rows: number + ): void } type TerminalHandleRecord = { @@ -222,6 +271,13 @@ type ResolvedWorktreeCache = { worktrees: ResolvedWorktree[] } +export type MobileNotificationEvent = { + source: 'agent-task-complete' | 'terminal-bell' | 'test' + title: string + body: string + worktreeId?: string +} + export class OrcaRuntimeService { private readonly runtimeId = randomUUID() private readonly startedAt = Date.now() @@ -244,6 +300,97 @@ export class OrcaRuntimeService { private agentDetector: AgentDetector | null = null private _orchestrationDb: OrchestrationDb | null = null private messageWaitersByHandle = new Map>() + // Why: mobile clients subscribe to terminal output via terminal.subscribe. + // These listeners fire on every onPtyData call, enabling real-time streaming + // without polling. Keyed by ptyId for O(1) lookup per data event. + private dataListeners = new Map void>>() + // Why: mobile clients need to know when the desktop restores a terminal + // from mobile-fit so they can update their UI. These listeners are + // invoked from resizeForClient and onClientDisconnected/onPtyExit. + private fitOverrideListeners = new Map< + string, + Set<(event: { mode: 'mobile-fit' | 'desktop-fit'; cols: number; rows: number }) => void> + >() + private subscriptionCleanups = new Map void>() + // Why: mobile clients subscribe to desktop notifications via + // notifications.subscribe. This set enables fan-out — each connected + // mobile client gets its own listener, and dispatchMobileNotification + // iterates them all. Listeners are cleaned up via subscriptionCleanups. + private notificationListeners = new Set<(event: MobileNotificationEvent) => void>() + private ptysById = new Map() + private headlessTerminals = new Map() + // Why: mobile-fit overrides are keyed by ptyId (not terminal handle) because + // handles can be reissued while the PTY identity is stable. In-memory only — + // a stale phone override should not survive an app restart. + private terminalFitOverrides = new Map< + string, + { + mode: 'mobile-fit' + cols: number + rows: number + previousCols: number | null + previousRows: number | null + updatedAt: number + clientId: string + } + >() + + // Why: server-authoritative display mode per terminal. 'auto' (default) means + // phone-fit when mobile subscribes, desktop otherwise. 'phone'/'desktop' lock + // the mode regardless of subscriber state. In-memory only — modes reset on restart. + private mobileDisplayModes = new Map() + + // Why: tracks active mobile subscriber per PTY so the runtime can restore + // desktop dimensions on unsubscribe and prevent orphaned overrides during + // rapid tab switches. Keyed by ptyId (single mobile client per terminal). + private mobileSubscribers = new Map< + string, + { + clientId: string + viewport: { cols: number; rows: number } | null + wasResizedToPhone: boolean + previousCols: number | null + previousRows: number | null + } + >() + + // Why: tracks the last PTY size set by the desktop renderer (via pty:resize + // IPC). Unlike ptySizes (which is overwritten by server-side phone-fit + // resizes), this map preserves the actual pane geometry. Used as the + // preferred source for previousCols so desktop restore uses the correct + // split-pane width instead of a stale full-width value. + private lastRendererSizes = new Map() + + // Why: when a desktop-fit override change fires, the desktop renderer's + // re-render cascade (triggered by setOverrideTick) runs safeFit on ALL + // panes — not just the affected one. Background tab panes get measured at + // full-width (214) instead of their correct split width (105). The stale + // pty:resize IPCs overwrite both the actual PTY size and lastRendererSizes. + // This global window suppresses ALL pty:resize for 200ms after any + // desktop-fit notification. The server has already set the correct PTY + // size via ptyController.resize(), so desktop renderer resizes during + // this window are redundant (for the restored pane) or wrong (collateral). + private resizeSuppressedUntil = 0 + + // Why: delays PTY restore by 300ms after mobile unsubscribe so rapid tab + // switches don't cause unnecessary resize thrashing. Keyed by clientId + // Why: keyed by ptyId so each PTY gets its own independent restore timer. + // The old clientId-keyed design lost timers when two PTYs were unsubscribed + // back-to-back (only the last timer survived). + private pendingRestoreTimers = new Map< + string, + { timer: ReturnType; clientId: string } + >() + + // Why: inline resize events replace the unsubscribe→resubscribe pattern. + // Listeners are notified when mode changes or desktop restores, allowing + // the subscribe stream to emit a 'resized' event with fresh scrollback. + private resizeListeners = new Map< + string, + Set<(event: { cols: number; rows: number; displayMode: string; reason: string }) => void> + >() + + private stats: StatsCollector | null = null // Why (§3.3 + §7.1): the renderer-create path and coordinator // `probeWorktreeDrift` share this cache so a create that already fetched // `origin` within the last 30s does not re-fetch during dispatch, and @@ -270,6 +417,7 @@ export class OrcaRuntimeService { ) { this.store = store if (stats) { + this.stats = stats this.agentDetector = new AgentDetector(stats) } // Why: the daemon adapter is installed via `setLocalPtyProvider()` during @@ -285,6 +433,10 @@ export class OrcaRuntimeService { return this.getLocalProviderFn ? this.getLocalProviderFn() : null } + getStatsSummary(): StatsSummary | null { + return this.stats?.getSummary() ?? null + } + // Why: lazy initialization — the DB path depends on Electron's userData // which may not be finalized until after app.ready. Also allows unit tests // to inject an in-memory DB without touching the filesystem. @@ -387,6 +539,14 @@ export class OrcaRuntimeService { lastAgentStatus: existing?.ptyId === ptyId ? existing.lastAgentStatus : null }) + if (leaf.ptyId) { + this.recordPtyWorktree(leaf.ptyId, leaf.worktreeId, { + connected: true, + lastOutputAt: existing?.ptyId === leaf.ptyId ? existing.lastOutputAt : null, + preview: existing?.ptyId === leaf.ptyId ? existing.preview : '' + }) + } + if (existing && (existing.ptyId !== ptyId || existing.ptyGeneration !== ptyGeneration)) { this.invalidateLeafHandle(leafKey) } @@ -465,6 +625,10 @@ export class OrcaRuntimeService { } onPtySpawned(ptyId: string): void { + const pty = this.getOrCreatePtyWorktreeRecord(ptyId) + if (pty) { + pty.connected = true + } for (const leaf of this.leaves.values()) { if (leaf.ptyId === ptyId) { leaf.connected = true @@ -474,10 +638,15 @@ export class OrcaRuntimeService { } } + registerPty(ptyId: string, worktreeId: string): void { + this.recordPtyWorktree(ptyId, worktreeId, { connected: true }) + } + onPtyData(ptyId: string, data: string, at: number): void { // Agent detection runs on raw data before leaf processing, since the // tail buffer logic normalizes away the OSC sequences we need. this.agentDetector?.onData(ptyId, data, at) + this.trackHeadlessTerminalData(ptyId, data) // Why: extract OSC title from raw PTY data before tail-buffer processing // strips the escape sequences. Agent CLIs (Claude Code, Gemini, etc.) @@ -486,10 +655,27 @@ export class OrcaRuntimeService { const oscTitle = extractLastOscTitle(data) const agentStatus = oscTitle ? detectAgentStatusFromTitle(oscTitle) : null + const pty = this.getOrCreatePtyWorktreeRecord(ptyId) + if (pty) { + pty.connected = true + pty.lastOutputAt = at + const nextTail = appendToTailBuffer(pty.tailBuffer, pty.tailPartialLine, data) + pty.tailBuffer = nextTail.lines + pty.tailPartialLine = nextTail.partialLine + pty.tailTruncated = pty.tailTruncated || nextTail.truncated + pty.tailLinesTotal += nextTail.newCompleteLines + pty.preview = buildPreview(pty.tailBuffer, pty.tailPartialLine) + } + for (const leaf of this.leaves.values()) { if (leaf.ptyId !== ptyId) { continue } + this.recordPtyWorktree(ptyId, leaf.worktreeId, { + connected: true, + lastOutputAt: pty?.lastOutputAt ?? at, + preview: pty?.preview ?? leaf.preview + }) leaf.connected = true leaf.writable = this.graphStatus === 'ready' leaf.lastOutputAt = at @@ -515,10 +701,394 @@ export class OrcaRuntimeService { } } } + + const listeners = this.dataListeners.get(ptyId) + if (listeners) { + for (const listener of listeners) { + listener(data) + } + } + } + + subscribeToTerminalData(ptyId: string, listener: (data: string) => void): () => void { + let listeners = this.dataListeners.get(ptyId) + if (!listeners) { + listeners = new Set() + this.dataListeners.set(ptyId, listeners) + } + listeners.add(listener) + return () => { + listeners.delete(listener) + if (listeners.size === 0) { + this.dataListeners.delete(ptyId) + } + } + } + + subscribeToFitOverrideChanges( + ptyId: string, + listener: (event: { mode: 'mobile-fit' | 'desktop-fit'; cols: number; rows: number }) => void + ): () => void { + let listeners = this.fitOverrideListeners.get(ptyId) + if (!listeners) { + listeners = new Set() + this.fitOverrideListeners.set(ptyId, listeners) + } + listeners.add(listener) + return () => { + listeners.delete(listener) + if (listeners.size === 0) { + this.fitOverrideListeners.delete(ptyId) + } + } + } + + private notifyFitOverrideListeners( + ptyId: string, + mode: 'mobile-fit' | 'desktop-fit', + cols: number, + rows: number + ): void { + const listeners = this.fitOverrideListeners.get(ptyId) + if (!listeners) { + return + } + for (const listener of listeners) { + listener({ mode, cols, rows }) + } + } + + serializeTerminalBuffer( + ptyId: string + ): Promise<{ data: string; cols: number; rows: number } | null> { + return this.serializeTerminalBufferFromAvailableState(ptyId) + } + + getTerminalSize(ptyId: string): { cols: number; rows: number } | null { + return this.ptyController?.getSize?.(ptyId) ?? null + } + + private trackHeadlessTerminalData(ptyId: string, data: string): void { + const state = this.getOrCreateHeadlessTerminal(ptyId) + state.writeChain = state.writeChain + .then(() => state.emulator.write(data)) + .catch(() => { + // Best-effort state tracking; live streaming must continue even if + // xterm rejects a malformed or raced write during shutdown. + }) + } + + private getOrCreateHeadlessTerminal(ptyId: string): RuntimeHeadlessTerminal { + const existing = this.headlessTerminals.get(ptyId) + if (existing) { + return existing + } + const size = this.getTerminalSize(ptyId) ?? { cols: 80, rows: 24 } + const state: RuntimeHeadlessTerminal = { + emulator: new HeadlessEmulator({ cols: size.cols, rows: size.rows }), + writeChain: Promise.resolve() + } + this.headlessTerminals.set(ptyId, state) + return state + } + + private resizeHeadlessTerminal(ptyId: string, cols: number, rows: number): void { + this.headlessTerminals.get(ptyId)?.emulator.resize(cols, rows) + } + + private async serializeTerminalBufferFromAvailableState( + ptyId: string + ): Promise<{ data: string; cols: number; rows: number } | null> { + const headlessSnapshot = await this.serializeHeadlessTerminalBuffer(ptyId) + if (headlessSnapshot) { + return headlessSnapshot + } + + let rendererSnapshot: { data: string; cols: number; rows: number } | null = null + try { + rendererSnapshot = await (this.ptyController?.serializeBuffer?.(ptyId) ?? + Promise.resolve(null)) + } catch { + // Why: mobile scrollback should not depend on a mounted renderer pane. + // If renderer serialization races reload/unmount, the runtime snapshot + // below can still preserve colored terminal state. + } + if (rendererSnapshot && rendererSnapshot.data.length > 0) { + return rendererSnapshot + } + return rendererSnapshot + } + + private async serializeHeadlessTerminalBuffer( + ptyId: string + ): Promise<{ data: string; cols: number; rows: number } | null> { + const state = this.headlessTerminals.get(ptyId) + if (!state) { + return null + } + await state.writeChain + // Why: terminal.subscribe needs the current visible screen, not the full + // launch history. Full scrollback plus a normal-buffer TUI can replay the + // shell prompt and active TUI frame together, which looks duplicated. + const snapshot = state.emulator.getSnapshot({ scrollbackRows: 0 }) + const data = snapshot.rehydrateSequences + snapshot.snapshotAnsi + return data.length > 0 ? { data, cols: snapshot.cols, rows: snapshot.rows } : null + } + + private disposeHeadlessTerminal(ptyId: string): void { + const state = this.headlessTerminals.get(ptyId) + if (!state) { + return + } + this.headlessTerminals.delete(ptyId) + state.writeChain.finally(() => state.emulator.dispose()).catch(() => state.emulator.dispose()) + } + + resolveLeafForHandle(handle: string): { ptyId: string | null } | null { + const record = this.handles.get(handle) + if (!record) { + return null + } + if (record.tabId.startsWith('pty:')) { + return { ptyId: record.ptyId } + } + const leaf = this.leaves.get(this.getLeafKey(record.tabId, record.leafId)) + if (!leaf) { + return null + } + return { ptyId: leaf.ptyId } + } + + registerSubscriptionCleanup(subscriptionId: string, cleanup: () => void): void { + // Why: mobile clients reconnect frequently (phone lock, network switch). + // The RPC client re-sends terminal.subscribe on reconnect, creating a new + // handler before the old one is cleaned up. Without this, the old data + // listener leaks in dataListeners and duplicates every PTY data event. + const existing = this.subscriptionCleanups.get(subscriptionId) + if (existing) { + existing() + } + this.subscriptionCleanups.set(subscriptionId, cleanup) + } + + cleanupSubscription(subscriptionId: string): void { + const cleanup = this.subscriptionCleanups.get(subscriptionId) + if (cleanup) { + this.subscriptionCleanups.delete(subscriptionId) + cleanup() + } + } + + // Why: mobile clients subscribe via notifications.subscribe streaming RPC. + // Each subscriber gets its own listener. Returns an unsubscribe function + // that the subscription cleanup mechanism calls on disconnect. + onNotificationDispatched(listener: (event: MobileNotificationEvent) => void): () => void { + this.notificationListeners.add(listener) + return () => { + this.notificationListeners.delete(listener) + } + } + + getMobileNotificationListenerCount(): number { + return this.notificationListeners.size + } + + dispatchMobileNotification(event: MobileNotificationEvent): void { + for (const listener of this.notificationListeners) { + listener(event) + } + } + + // ─── Mobile Fit Override Management ───────────────────────── + + resizeForClient( + ptyId: string, + mode: 'mobile-fit' | 'restore', + clientId: string, + cols?: number, + rows?: number + ): { + cols: number + rows: number + previousCols: number | null + previousRows: number | null + mode: 'mobile-fit' | 'desktop-fit' + } { + if (mode === 'mobile-fit') { + if (cols == null || rows == null || !Number.isFinite(cols) || !Number.isFinite(rows)) { + throw new Error('invalid_dimensions') + } + const clampedCols = Math.max(20, Math.min(240, Math.round(cols))) + const clampedRows = Math.max(8, Math.min(120, Math.round(rows))) + + const currentSize = this.getTerminalSize(ptyId) + const existing = this.terminalFitOverrides.get(ptyId) + // Why: preserve the original desktop size from before any mobile-fit, + // so restore returns to the right dimensions even after multiple re-fits. + const previousCols = existing?.previousCols ?? currentSize?.cols ?? null + const previousRows = existing?.previousRows ?? currentSize?.rows ?? null + + this.terminalFitOverrides.set(ptyId, { + mode: 'mobile-fit', + cols: clampedCols, + rows: clampedRows, + previousCols, + previousRows, + updatedAt: Date.now(), + clientId + }) + + const resized = this.ptyController?.resize?.(ptyId, clampedCols, clampedRows) + if (!resized) { + this.terminalFitOverrides.delete(ptyId) + throw new Error('resize_failed') + } + this.resizeHeadlessTerminal(ptyId, clampedCols, clampedRows) + + console.log( + `[mobile-fit] handleMobileSubscribe notifier=${!!this.notifier} ptyId=${ptyId} mode=mobile-fit cols=${clampedCols} rows=${clampedRows}` + ) + this.notifier?.terminalFitOverrideChanged(ptyId, 'mobile-fit', clampedCols, clampedRows) + + return { + cols: clampedCols, + rows: clampedRows, + previousCols, + previousRows, + mode: 'mobile-fit' + } + } + + // restore mode + const override = this.terminalFitOverrides.get(ptyId) + if (!override) { + throw new Error('no_active_override') + } + // Why: only the owning client can restore, preventing one phone from + // undoing another phone's active fit. + if (override.clientId !== clientId) { + throw new Error('not_override_owner') + } + + const { previousCols: prevCols, previousRows: prevRows } = override + this.terminalFitOverrides.delete(ptyId) + + // Why: always resize the PTY back to pre-fit dimensions immediately, + // even for mounted leaves. Relying solely on the renderer chain + // (IPC notification → safeFit → fitAddon.fit → onResize → transport.resize) + // is fragile — any async gap leaves the PTY at phone dims while xterm + // looks correct, causing text to wrap at the wrong column. The renderer + // will still run safeFit and may send a second resize with the exact + // current pane geometry, which is harmless (SIGWINCH is idempotent). + if (prevCols != null && prevRows != null) { + this.ptyController?.resize?.(ptyId, prevCols, prevRows) + this.resizeHeadlessTerminal(ptyId, prevCols, prevRows) + } + + // Why: send the restored dimensions so the renderer can fall back to a + // direct terminal.resize() if fitAddon.fit() silently fails. The renderer + // normally computes desktop dims from the container, but passing them here + // provides a guaranteed fallback to avoid leaving xterm at phone dims. + this.notifier?.terminalFitOverrideChanged(ptyId, 'desktop-fit', prevCols ?? 0, prevRows ?? 0) + // Why: mobile clients subscribed to this terminal need to know the desktop + // restored, so they can update their UI (clear fitted state, resubscribe). + this.notifyFitOverrideListeners(ptyId, 'desktop-fit', prevCols ?? 0, prevRows ?? 0) + + return { + cols: prevCols ?? 0, + rows: prevRows ?? 0, + previousCols: null, + previousRows: null, + mode: 'desktop-fit' + } + } + + getTerminalFitOverride(ptyId: string) { + return this.terminalFitOverrides.get(ptyId) ?? null + } + + getAllTerminalFitOverrides(): Map { + const result = new Map() + for (const [ptyId, override] of this.terminalFitOverrides) { + result.set(ptyId, { mode: override.mode, cols: override.cols, rows: override.rows }) + } + return result + } + + onClientDisconnected(clientId: string): void { + // Cancel all pending restore timers for this client — the client is gone, + // so the debounce is meaningless and could fire against a stale PTY state. + for (const [ptyId, entry] of this.pendingRestoreTimers) { + if (entry.clientId === clientId) { + clearTimeout(entry.timer) + this.pendingRestoreTimers.delete(ptyId) + } + } + + // Immediately restore PTYs that this client had phone-fitted (no debounce — + // client is gone, no point waiting for a re-subscribe that won't come). + for (const [ptyId, subscriber] of this.mobileSubscribers) { + if (subscriber.clientId !== clientId) { + continue + } + + this.mobileSubscribers.delete(ptyId) + + if (subscriber.wasResizedToPhone) { + const { previousCols, previousRows } = subscriber + if (previousCols != null && previousRows != null) { + this.ptyController?.resize?.(ptyId, previousCols, previousRows) + this.resizeHeadlessTerminal(ptyId, previousCols, previousRows) + } + this.terminalFitOverrides.delete(ptyId) + this.notifier?.terminalFitOverrideChanged( + ptyId, + 'desktop-fit', + previousCols ?? 0, + previousRows ?? 0 + ) + this.notifyFitOverrideListeners(ptyId, 'desktop-fit', previousCols ?? 0, previousRows ?? 0) + } + } + + // Legacy cleanup for any terminalFitOverrides not covered by mobileSubscribers + for (const [ptyId, override] of this.terminalFitOverrides) { + if (override.clientId !== clientId) { + continue + } + try { + this.resizeForClient(ptyId, 'restore', clientId) + } catch { + this.terminalFitOverrides.delete(ptyId) + this.notifier?.terminalFitOverrideChanged(ptyId, 'desktop-fit', 0, 0) + this.notifyFitOverrideListeners(ptyId, 'desktop-fit', 0, 0) + } + } } onPtyExit(ptyId: string, exitCode: number): void { + // Clean up new mobile state for this PTY + this.mobileSubscribers.delete(ptyId) + this.mobileDisplayModes.delete(ptyId) + this.resizeListeners.delete(ptyId) + this.lastRendererSizes.delete(ptyId) + const pendingRestore = this.pendingRestoreTimers.get(ptyId) + if (pendingRestore) { + clearTimeout(pendingRestore.timer) + this.pendingRestoreTimers.delete(ptyId) + } + + if (this.terminalFitOverrides.has(ptyId)) { + this.terminalFitOverrides.delete(ptyId) + this.notifier?.terminalFitOverrideChanged(ptyId, 'desktop-fit', 0, 0) + this.notifyFitOverrideListeners(ptyId, 'desktop-fit', 0, 0) + } + this.disposeHeadlessTerminal(ptyId) this.agentDetector?.onExit(ptyId) + const pty = this.ptysById.get(ptyId) + if (pty) { + pty.connected = false + } for (const leaf of this.leaves.values()) { if (leaf.ptyId !== ptyId) { @@ -533,6 +1103,277 @@ export class OrcaRuntimeService { } } + // ─── Server-Authoritative Mobile Display Mode ───────────────────── + + setMobileDisplayMode(ptyId: string, mode: 'auto' | 'phone' | 'desktop'): void { + if (mode === 'auto') { + this.mobileDisplayModes.delete(ptyId) + } else { + this.mobileDisplayModes.set(ptyId, mode) + } + } + + getMobileDisplayMode(ptyId: string): 'auto' | 'phone' | 'desktop' { + return this.mobileDisplayModes.get(ptyId) ?? 'auto' + } + + isMobileSubscriberActive(ptyId: string): boolean { + return this.mobileSubscribers.has(ptyId) + } + + // Why: server-side auto-fit on mobile subscribe. The runtime is the single + // source of truth — the mobile client just passes its viewport and the runtime + // decides whether to resize. This eliminates the measure→RPC→resubscribe + // pipeline that caused race conditions. + handleMobileSubscribe( + ptyId: string, + clientId: string, + viewport?: { cols: number; rows: number } + ): boolean { + const mode = this.mobileDisplayModes.get(ptyId) ?? 'auto' + if (!viewport) { + return false + } + + // Why: only cancel the restore timer for THIS ptyId (re-subscribe case). + // Other terminals' timers must fire so their banners clear on the desktop. + const pendingRestore = this.pendingRestoreTimers.get(ptyId) + if (pendingRestore && pendingRestore.clientId === clientId) { + clearTimeout(pendingRestore.timer) + this.pendingRestoreTimers.delete(ptyId) + } + + // Why: prefer lastRendererSizes (the actual pane geometry reported by the + // desktop renderer's safeFit via pty:resize IPC) over getTerminalSize (the + // server-side PTY size, which may be stale — e.g. 214 full-width when the + // pane is actually in a split at ~105). Fall back to existing subscriber's + // previousCols (re-subscribe case) then currentSize (first subscribe). + const existing = this.mobileSubscribers.get(ptyId) + const currentSize = this.getTerminalSize(ptyId) + const rendererSize = this.lastRendererSizes.get(ptyId) + const previousCols = existing?.previousCols ?? rendererSize?.cols ?? currentSize?.cols ?? null + const previousRows = existing?.previousRows ?? rendererSize?.rows ?? currentSize?.rows ?? null + + // Why: always register the subscriber so applyMobileDisplayMode can find + // the viewport when the user later toggles from desktop to auto/phone. + // Without this, toggling to auto after subscribing in desktop mode sees + // hasSubscriber=false and can't perform the phone resize. + if (mode === 'desktop') { + // Why: set previousCols/Rows to null so we don't capture a stale PTY + // size that may not match the actual pane geometry (e.g. 214 when the + // pane is in a split at 105). When the user later toggles to auto/phone, + // handleMobileSubscribe will capture currentSize at that point, which + // will be correct because safeFit has had time to adjust the PTY. + this.mobileSubscribers.set(ptyId, { + clientId, + viewport, + wasResizedToPhone: false, + previousCols: null, + previousRows: null + }) + return false + } + + this.mobileSubscribers.set(ptyId, { + clientId, + viewport, + wasResizedToPhone: true, + previousCols, + previousRows + }) + + const clampedCols = Math.max(20, Math.min(240, Math.round(viewport.cols))) + const clampedRows = Math.max(8, Math.min(120, Math.round(viewport.rows))) + + // Why: skip the PTY resize if already at the target dims. Re-subscribing + // to a terminal that was left at phone dims (no restore on tab switch) + // should not trigger another SIGWINCH → shell prompt redraw. + const alreadyAtTarget = currentSize?.cols === clampedCols && currentSize?.rows === clampedRows + if (!alreadyAtTarget) { + this.ptyController?.resize?.(ptyId, clampedCols, clampedRows) + this.resizeHeadlessTerminal(ptyId, clampedCols, clampedRows) + } + this.notifier?.terminalFitOverrideChanged(ptyId, 'mobile-fit', clampedCols, clampedRows) + + // Update terminalFitOverrides for desktop safeFit compatibility + this.terminalFitOverrides.set(ptyId, { + mode: 'mobile-fit', + cols: clampedCols, + rows: clampedRows, + previousCols, + previousRows, + updatedAt: Date.now(), + clientId + }) + + return true + } + + // Why: delayed restore prevents resize thrashing during rapid tab switches. + // The 300ms debounce means only the final tab triggers a PTY restore; + // intermediate terminals keep their current dims harmlessly. + handleMobileUnsubscribe(ptyId: string, clientId: string): void { + const subscriber = this.mobileSubscribers.get(ptyId) + if (!subscriber || subscriber.clientId !== clientId) { + return + } + + const mode = this.mobileDisplayModes.get(ptyId) ?? 'auto' + this.mobileSubscribers.delete(ptyId) + + if (mode === 'auto' && subscriber.wasResizedToPhone) { + const existing = this.pendingRestoreTimers.get(ptyId) + if (existing) { + clearTimeout(existing.timer) + } + + const { previousCols, previousRows } = subscriber + const timer = setTimeout(() => { + this.pendingRestoreTimers.delete(ptyId) + if (this.mobileSubscribers.has(ptyId)) { + return + } + if (previousCols != null && previousRows != null) { + this.ptyController?.resize?.(ptyId, previousCols, previousRows) + this.resizeHeadlessTerminal(ptyId, previousCols, previousRows) + } + this.lastRendererSizes.delete(ptyId) + this.suppressResizesForMs(500) + this.terminalFitOverrides.delete(ptyId) + this.notifier?.terminalFitOverrideChanged( + ptyId, + 'desktop-fit', + previousCols ?? 0, + previousRows ?? 0 + ) + this.notifyFitOverrideListeners(ptyId, 'desktop-fit', previousCols ?? 0, previousRows ?? 0) + }, 300) + + this.pendingRestoreTimers.set(ptyId, { timer, clientId }) + } + // 'phone' mode: keep phone dims (no restore needed) + // 'desktop' mode: was never resized, nothing to restore + } + + // Why: called when mode changes via terminal.setDisplayMode. Applies the + // mode change immediately if there's an active subscriber, and emits a + // 'resized' event so the mobile client can reinitialize xterm inline. + applyMobileDisplayMode(ptyId: string): void { + const mode = this.mobileDisplayModes.get(ptyId) ?? 'auto' + const subscriber = this.mobileSubscribers.get(ptyId) + + if (mode === 'desktop') { + if (subscriber?.wasResizedToPhone) { + const { previousCols, previousRows } = subscriber + if (previousCols != null && previousRows != null) { + this.ptyController?.resize?.(ptyId, previousCols, previousRows) + this.resizeHeadlessTerminal(ptyId, previousCols, previousRows) + } + subscriber.wasResizedToPhone = false + // Why: clear stale renderer size so the next mobile subscribe falls + // through to currentSize (which is correct after the server restore). + // Without this, a polluted 214 from a prior collateral safeFit cascade + // persists in lastRendererSizes and gets used as previousCols. + this.lastRendererSizes.delete(ptyId) + // Why: 500ms not 200ms — the desktop renderer's collateral safeFit + // cascade (IPC → React re-render → rAF → DOM measure → IPC back) + // takes ~360ms to propagate to background-tab terminals. + this.suppressResizesForMs(500) + this.terminalFitOverrides.delete(ptyId) + this.notifier?.terminalFitOverrideChanged( + ptyId, + 'desktop-fit', + previousCols ?? 0, + previousRows ?? 0 + ) + } + const size = this.getTerminalSize(ptyId) + this.notifyTerminalResize(ptyId, { + cols: size?.cols ?? 0, + rows: size?.rows ?? 0, + displayMode: 'desktop', + reason: 'mode-change' + }) + } else if (mode === 'phone' || mode === 'auto') { + if (subscriber && !subscriber.wasResizedToPhone) { + const viewport = subscriber.viewport + if (viewport) { + this.handleMobileSubscribe(ptyId, subscriber.clientId, viewport) + } + } + // Why: always emit the mode change even when no resize occurred (e.g. + // subscriber missing, wasResizedToPhone already true, or no viewport). + // Without this the mobile client never learns the mode changed and its + // toggle button gets stuck showing the old state. + const size = this.getTerminalSize(ptyId) + this.notifyTerminalResize(ptyId, { + cols: size?.cols ?? 0, + rows: size?.rows ?? 0, + displayMode: mode, + reason: 'mode-change' + }) + } + } + + // Why: called from the pty:resize IPC handler whenever the desktop renderer + // resizes a PTY (e.g. via safeFit after window resize, split, or desktop-mode + // restore). Stores the renderer-reported size so handleMobileSubscribe can use + // the actual pane geometry instead of a stale PTY size for previousCols. + onExternalPtyResize(ptyId: string, cols: number, rows: number): void { + this.lastRendererSizes.set(ptyId, { cols, rows }) + + const subscriber = this.mobileSubscribers.get(ptyId) + if (!subscriber) { + return + } + if (!subscriber.wasResizedToPhone) { + subscriber.previousCols = cols + subscriber.previousRows = rows + } + } + + // Why: the pty:resize IPC handler calls this to check if the global + // suppress window is active. During this window, all desktop renderer + // pty:resize events are ignored to prevent collateral safeFit corruption. + isResizeSuppressed(): boolean { + return Date.now() < this.resizeSuppressedUntil + } + + private suppressResizesForMs(ms: number): void { + this.resizeSuppressedUntil = Date.now() + ms + } + + subscribeToTerminalResize( + ptyId: string, + listener: (event: { cols: number; rows: number; displayMode: string; reason: string }) => void + ): () => void { + let listeners = this.resizeListeners.get(ptyId) + if (!listeners) { + listeners = new Set() + this.resizeListeners.set(ptyId, listeners) + } + listeners.add(listener) + return () => { + listeners.delete(listener) + if (listeners.size === 0) { + this.resizeListeners.delete(ptyId) + } + } + } + + private notifyTerminalResize( + ptyId: string, + event: { cols: number; rows: number; displayMode: string; reason: string } + ): void { + const listeners = this.resizeListeners.get(ptyId) + if (!listeners) { + return + } + for (const listener of listeners) { + listener(event) + } + } + // Why: Section 7.2 — the runtime detects agent exit directly and updates // dispatch contexts immediately, rather than waiting for the coordinator's // next poll cycle. This catches agent crashes and unexpected exits within @@ -589,13 +1430,44 @@ export class OrcaRuntimeService { const worktreesById = await this.getResolvedWorktreeMap() this.assertStableReadyGraph(graphEpoch) + const resolvedWorktrees = [...worktreesById.values()] + await this.refreshPtyWorktreeRecordsFromController(resolvedWorktrees) + + const livePtyWorktreeIds = new Set() + for (const pty of this.ptysById.values()) { + if (pty.connected) { + livePtyWorktreeIds.add(pty.worktreeId) + } + } + const terminals: RuntimeTerminalSummary[] = [] + const ptyIdsFromLeaves = new Set() for (const leaf of this.leaves.values()) { if (targetWorktreeId && leaf.worktreeId !== targetWorktreeId) { continue } + if (!leaf.ptyId && livePtyWorktreeIds.has(leaf.worktreeId)) { + continue + } + if (leaf.ptyId) { + ptyIdsFromLeaves.add(leaf.ptyId) + } terminals.push(this.buildTerminalSummary(leaf, worktreesById)) } + + // Why: worktree.ps can classify active worktrees from PTY records even when + // the renderer graph is missing a leaf. terminal.list needs the same fallback + // so mobile does not show a false "No terminals" create flow. + for (const pty of this.ptysById.values()) { + if (!pty.connected || ptyIdsFromLeaves.has(pty.ptyId)) { + continue + } + if (targetWorktreeId && pty.worktreeId !== targetWorktreeId) { + continue + } + terminals.push(this.buildPtyTerminalSummary(pty, worktreesById)) + } + return { terminals: terminals.slice(0, limit), totalCount: terminals.length, @@ -642,6 +1514,15 @@ export class OrcaRuntimeService { const graphEpoch = this.captureReadyGraphEpoch() const worktreesById = await this.getResolvedWorktreeMap() this.assertStableReadyGraph(graphEpoch) + const pty = this.getLivePtyForHandle(handle) + if (pty) { + return { + ...this.buildPtyTerminalSummary(pty.pty, worktreesById), + paneRuntimeId: -1, + ptyId: pty.pty.ptyId, + rendererGraphEpoch: this.rendererGraphEpoch + } + } const { leaf } = this.getLiveLeafForHandle(handle) const summary = this.buildTerminalSummary(leaf, worktreesById) return { @@ -653,6 +1534,11 @@ export class OrcaRuntimeService { } async readTerminal(handle: string, opts: { cursor?: number } = {}): Promise { + const pty = this.getLivePtyForHandle(handle) + if (pty) { + return this.readPtyTerminal(handle, pty.pty, opts) + } + const { leaf } = this.getLiveLeafForHandle(handle) const allLines = buildTailLines(leaf.tailBuffer, leaf.tailPartialLine) @@ -700,6 +1586,26 @@ export class OrcaRuntimeService { interrupt?: boolean } ): Promise { + const pty = this.getLivePtyForHandle(handle) + if (pty) { + if (!pty.pty.connected) { + throw new Error('terminal_not_writable') + } + const payload = buildSendPayload(action) + if (payload === null) { + throw new Error('invalid_terminal_send') + } + const wrote = this.ptyController?.write(pty.pty.ptyId, payload) ?? false + if (!wrote) { + throw new Error('terminal_not_writable') + } + return { + handle, + accepted: true, + bytesWritten: Buffer.byteLength(payload, 'utf8') + } + } + const { leaf } = this.getLiveLeafForHandle(handle) if (!leaf.writable || !leaf.ptyId) { throw new Error('terminal_not_writable') @@ -839,36 +1745,71 @@ export class OrcaRuntimeService { throw new Error('invalid_limit') } const resolvedWorktrees = await this.listResolvedWorktrees() + await this.refreshPtyWorktreeRecordsFromController(resolvedWorktrees) const repoById = new Map((this.store?.getRepos() ?? []).map((repo) => [repo.id, repo])) const summaries = new Map() + // Why: the GitHub cache is keyed by `repoPath::branch` (no refs/heads/ prefix), + // matching how the renderer's fetchPRForBranch stores entries. We look up cached + // PR info so mobile clients can group worktrees by PR state without making + // expensive `gh` CLI calls. Falls back to meta.linkedPR if no cache entry exists. + const ghCache = this.store?.getGitHubCache?.() for (const worktree of resolvedWorktrees) { const meta = this.store?.getWorktreeMeta?.(worktree.id) ?? this.store?.getAllWorktreeMeta()[worktree.id] + const repo = repoById.get(worktree.repoId) + let linkedPR: { number: number; state: string } | null = null + const branch = worktree.branch.replace(/^refs\/heads\//, '') + if (repo?.path && branch && ghCache) { + const prCacheKey = `${repo.path}::${branch}` + const cached = ghCache.pr[prCacheKey] + if (cached?.data) { + linkedPR = { number: cached.data.number, state: cached.data.state } + } + } + if (!linkedPR && meta?.linkedPR != null) { + linkedPR = { number: meta.linkedPR, state: 'unknown' } + } summaries.set(worktree.id, { worktreeId: worktree.id, repoId: worktree.repoId, - repo: repoById.get(worktree.repoId)?.displayName ?? worktree.repoId, + repo: repo?.displayName ?? worktree.repoId, path: worktree.path, branch: worktree.branch, + displayName: worktree.displayName, linkedIssue: worktree.linkedIssue, + linkedPR, + isPinned: meta?.isPinned ?? false, unread: meta?.isUnread ?? false, liveTerminalCount: 0, hasAttachedPty: false, lastOutputAt: null, - preview: '' + preview: '', + status: 'inactive' }) } + const countedPtyIds = new Set() for (const leaf of this.leaves.values()) { - const summary = summaries.get(leaf.worktreeId) + const summary = this.getSummaryForRuntimeWorktreeId( + summaries, + resolvedWorktrees, + leaf.worktreeId + ) if (!summary) { continue } + if (leaf.ptyId) { + countedPtyIds.add(leaf.ptyId) + } const previousLastOutputAt = summary.lastOutputAt summary.liveTerminalCount += 1 summary.hasAttachedPty = summary.hasAttachedPty || leaf.connected summary.lastOutputAt = maxTimestamp(summary.lastOutputAt, leaf.lastOutputAt) + summary.status = mergeWorktreeStatus( + summary.status, + getLeafWorktreeStatus(leaf, this.tabs.get(leaf.tabId)?.title ?? null) + ) if ( leaf.preview && (summary.preview.length === 0 || (leaf.lastOutputAt ?? -1) >= (previousLastOutputAt ?? -1)) @@ -877,6 +1818,53 @@ export class OrcaRuntimeService { } } + for (const pty of this.ptysById.values()) { + if (!pty.connected || countedPtyIds.has(pty.ptyId)) { + continue + } + const summary = this.getSummaryForRuntimeWorktreeId( + summaries, + resolvedWorktrees, + pty.worktreeId + ) + if (!summary) { + continue + } + const previousLastOutputAt = summary.lastOutputAt + summary.liveTerminalCount += 1 + summary.hasAttachedPty = true + summary.lastOutputAt = maxTimestamp(summary.lastOutputAt, pty.lastOutputAt) + summary.status = mergeWorktreeStatus(summary.status, 'active') + if ( + pty.preview && + (summary.preview.length === 0 || (pty.lastOutputAt ?? -1) >= (previousLastOutputAt ?? -1)) + ) { + summary.preview = pty.preview + } + } + + const session = this.store?.getWorkspaceSession?.() + for (const [worktreeId, tabs] of Object.entries(session?.tabsByWorktree ?? {})) { + if (tabs.length === 0) { + continue + } + const summary = this.getSummaryForRuntimeWorktreeId(summaries, resolvedWorktrees, worktreeId) + if (!summary) { + continue + } + // Why: desktop can show terminal tabs that are not mounted as renderer + // leaves and are not currently visible in the PTY provider list. Mobile + // still needs those worktrees to show as terminal-bearing entries. + summary.liveTerminalCount = Math.max(summary.liveTerminalCount, tabs.length) + summary.hasAttachedPty = summary.hasAttachedPty || tabs.some((tab) => tab.ptyId !== null) + for (const tab of tabs) { + summary.status = mergeWorktreeStatus( + summary.status, + getSavedTabWorktreeStatus(tab.title, tab.ptyId !== null) + ) + } + } + const sorted = [...summaries.values()].sort(compareWorktreePs) return { worktrees: sorted.slice(0, limit), @@ -959,6 +1947,19 @@ export class OrcaRuntimeService { } } + async getRepoHooks(repoSelector: string) { + const repo = await this.resolveRepoSelector(repoSelector) + const hasFile = hasHooksFile(repo.path) + const hooks = getEffectiveHooks(repo) + const setupRunPolicy = getEffectiveSetupRunPolicy(repo) + return { + hasHooksFile: hasFile, + hooks, + setupRunPolicy, + source: hasFile ? 'orca.yaml' : hooks ? 'legacy' : null + } + } + async listManagedWorktrees( repoSelector?: string, limit = DEFAULT_WORKTREE_LIST_LIMIT @@ -980,6 +1981,33 @@ export class OrcaRuntimeService { return await this.resolveWorktreeSelector(worktreeSelector) } + async sleepManagedWorktree(worktreeSelector: string): Promise<{ worktreeId: string }> { + const worktree = await this.resolveWorktreeSelector(worktreeSelector) + // Why: sleep is renderer-initiated on desktop (it tears down tab state + // before killing PTYs). The notifier tells the renderer to run its own + // sleep flow so all cleanup happens in the correct order. + this.notifier?.sleepWorktree(worktree.id) + return { worktreeId: worktree.id } + } + + async activateManagedWorktree(worktreeSelector: string): Promise<{ + repoId: string + worktreeId: string + activated: boolean + }> { + this.assertGraphReady() + const worktree = await this.resolveWorktreeSelector(worktreeSelector) + const repo = this.store?.getRepo(worktree.repoId) + if (!repo) { + throw new Error('repo_not_found') + } + + // Why: inactive worktree terminal panes are renderer-owned and may not have + // live PTYs until the desktop activates the worktree and mounts them. + this.notifier?.activateWorktree(repo.id, worktree.id) + return { repoId: repo.id, worktreeId: worktree.id, activated: true } + } + async createManagedWorktree(args: { repoSelector: string name: string @@ -987,6 +2015,8 @@ export class OrcaRuntimeService { linkedIssue?: number | null comment?: string runHooks?: boolean + setupDecision?: 'run' | 'skip' | 'inherit' + startup?: WorktreeStartupLaunch }): Promise { if (!this.store) { throw new Error('runtime_unavailable') @@ -1085,7 +2115,13 @@ export class OrcaRuntimeService { // against. Trust is granted by the direct CLI invocation (`--run-hooks`), // so loading the setup hook from the created worktree is intentional here. const hooks = getEffectiveHooks(repo, worktreePath) - if (hooks?.scripts.setup && args.runHooks === true) { + // Why: setupDecision lets mobile/CLI callers control whether the setup + // script runs. 'skip' suppresses it, 'run' forces it, 'inherit' (default) + // defers to the repo's orca.yaml setupRunPolicy. runHooks === true maps + // to 'run' for backwards compatibility with the desktop create flow. + const effectiveDecision = args.runHooks ? 'run' : (args.setupDecision ?? 'inherit') + const shouldRunSetup = hooks?.scripts.setup && shouldRunSetupForCreate(repo, effectiveDecision) + if (shouldRunSetup && hooks?.scripts.setup) { if (this.authoritativeWindowId !== null) { try { // Why: CLI-created worktrees must use the same runner-script path as the @@ -1117,7 +2153,11 @@ export class OrcaRuntimeService { // renderer-side consequence of activating a worktree. CLI-created // worktrees must trigger that same activation path or they will exist on // disk without becoming the active workspace in the UI. - this.notifier?.activateWorktree(repo.id, worktree.id, setup) + if (args.startup) { + this.notifier?.activateWorktree(repo.id, worktree.id, setup, args.startup) + } else { + this.notifier?.activateWorktree(repo.id, worktree.id, setup) + } this.invalidateResolvedWorktreeCache() // Why: the filesystem-auth layer maintains a separate cache of registered // worktree roots used by git IPC handlers (branchCompare, diff, status, etc.) @@ -1228,6 +2268,7 @@ export class OrcaRuntimeService { displayName?: string linkedIssue?: number | null comment?: string + isPinned?: boolean } ) { if (!this.store) { @@ -1237,7 +2278,8 @@ export class OrcaRuntimeService { const meta = this.store.setWorktreeMeta(worktree.id, { ...(updates.displayName !== undefined ? { displayName: updates.displayName } : {}), ...(updates.linkedIssue !== undefined ? { linkedIssue: updates.linkedIssue } : {}), - ...(updates.comment !== undefined ? { comment: updates.comment } : {}) + ...(updates.comment !== undefined ? { comment: updates.comment } : {}), + ...(updates.isPinned !== undefined ? { isPinned: updates.isPinned } : {}) }) // Why: unlike renderer-initiated optimistic updates, CLI callers need an // explicit push so the editor refreshes metadata changed outside the UI. @@ -1430,6 +2472,54 @@ export class OrcaRuntimeService { }) } + // Why: mobile clients may subscribe before the PTY spawns (the left pane + // of a new workspace). Instead of bailing with a bare scrollback+end, + // wait for the PTY to appear so the subscribe can proceed with phone-fit. + waitForLeafPtyId(handle: string, timeoutMs = 10_000): Promise { + const leaf = this.resolveLeafForHandle(handle) + if (leaf?.ptyId) { + return Promise.resolve(leaf.ptyId) + } + + // Why: when the ptyId changes from null to a real value, the old handle + // is invalidated (deleted from this.handles). Capture the tabId+leafId + // now so we can look up the leaf directly even after handle invalidation. + const record = this.handles.get(handle) + const savedTabId = record?.tabId ?? null + const savedLeafId = record?.leafId ?? null + + return new Promise((resolve, reject) => { + const timer = setTimeout(() => { + const idx = this.graphSyncCallbacks.indexOf(check) + if (idx !== -1) { + this.graphSyncCallbacks.splice(idx, 1) + } + reject(new Error('Timed out waiting for PTY to spawn')) + }, timeoutMs) + + const check = (): void => { + // Try the handle first (works if handle wasn't invalidated yet) + let ptyId = this.resolveLeafForHandle(handle)?.ptyId + // Why: when ptyId transitions null→real, issueHandle invalidates the + // old handle. Fall back to direct leaf lookup by the saved coordinates. + if (!ptyId && savedTabId && savedLeafId) { + const directLeaf = this.leaves.get(this.getLeafKey(savedTabId, savedLeafId)) + ptyId = directLeaf?.ptyId ?? null + } + if (ptyId) { + clearTimeout(timer) + const idx = this.graphSyncCallbacks.indexOf(check) + if (idx !== -1) { + this.graphSyncCallbacks.splice(idx, 1) + } + resolve(ptyId) + } + } + this.graphSyncCallbacks.push(check) + check() + }) + } + // Why: a leaf appears in the graph before its PTY spawns. If we issue a // handle while ptyId is null, the next graph sync after PTY spawn will // change ptyId and invalidate the handle. Wait for a connected PTY so @@ -1455,6 +2545,10 @@ export class OrcaRuntimeService { async focusTerminal(handle: string): Promise { this.assertGraphReady() + const pty = this.getLivePtyForHandle(handle) + if (pty) { + return { handle, tabId: pty.record.tabId, worktreeId: pty.pty.worktreeId } + } const { leaf } = this.getLiveLeafForHandle(handle) this.notifier?.focusTerminal(leaf.tabId, leaf.worktreeId) return { handle, tabId: leaf.tabId, worktreeId: leaf.worktreeId } @@ -1588,6 +2682,10 @@ export class OrcaRuntimeService { this.rememberDetachedPreAllocatedLeaves() this.handles.clear() this.handleByLeafKey.clear() + // Why: handleByPtyId maps ptyId → pre-allocated CLI handle (ORCA_TERMINAL_HANDLE). + // These must survive renderer reloads so CLI agents can keep controlling the + // same terminal across graph rebuilds — adoptPreAllocatedHandle re-links + // them when the new graph arrives. this.rejectAllWaiters('terminal_handle_stale') this.refreshWritableFlags() } @@ -1616,6 +2714,8 @@ export class OrcaRuntimeService { this.leaves.clear() this.handles.clear() this.handleByLeafKey.clear() + // Why: same as markRendererReloading — pre-allocated CLI handles must + // survive graph unavailability so they can be re-adopted on reconnect. this.rejectAllWaiters('terminal_handle_stale') } @@ -1756,6 +2856,108 @@ export class OrcaRuntimeService { this.resolvedWorktreeCache = null } + private recordPtyWorktree( + ptyId: string, + worktreeId: string, + state: Partial> = {} + ): RuntimePtyWorktreeRecord { + let pty = this.ptysById.get(ptyId) + if (!pty) { + pty = { + ptyId, + worktreeId, + connected: state.connected ?? true, + lastOutputAt: state.lastOutputAt ?? null, + tailBuffer: [], + tailPartialLine: '', + tailTruncated: false, + tailLinesTotal: 0, + preview: state.preview ?? '' + } + this.ptysById.set(ptyId, pty) + return pty + } + + pty.worktreeId = worktreeId + if (state.connected !== undefined) { + pty.connected = state.connected + } + if (state.lastOutputAt !== undefined) { + pty.lastOutputAt = maxTimestamp(pty.lastOutputAt, state.lastOutputAt) + } + if (state.preview !== undefined && state.preview.length > 0) { + pty.preview = state.preview + } + return pty + } + + private getOrCreatePtyWorktreeRecord(ptyId: string): RuntimePtyWorktreeRecord | null { + const existing = this.ptysById.get(ptyId) + if (existing) { + return existing + } + const inferredWorktreeId = inferWorktreeIdFromPtyId(ptyId) + if (!inferredWorktreeId) { + return null + } + // Why: daemon-backed PTY session IDs are prefixed with the worktree ID so + // mobile summaries survive renderer graph gaps and Electron reloads. + return this.recordPtyWorktree(ptyId, inferredWorktreeId) + } + + private async refreshPtyWorktreeRecordsFromController( + resolvedWorktrees: ResolvedWorktree[] + ): Promise { + if (!this.ptyController?.listProcesses) { + return + } + const sessions = await this.ptyController.listProcesses().catch(() => []) + const livePtyIds = new Set(sessions.map((session) => session.id)) + for (const session of sessions) { + const worktreeId = + inferWorktreeIdFromPtyId(session.id) ?? + findResolvedWorktreeIdForPath(resolvedWorktrees, session.cwd) + if (worktreeId) { + this.recordPtyWorktree(session.id, worktreeId, { connected: true }) + } + } + for (const pty of this.ptysById.values()) { + if (!livePtyIds.has(pty.ptyId) && !this.leafExistsForPty(pty.ptyId)) { + pty.connected = false + } + } + } + + private leafExistsForPty(ptyId: string): boolean { + for (const leaf of this.leaves.values()) { + if (leaf.ptyId === ptyId) { + return true + } + } + return false + } + + private getSummaryForRuntimeWorktreeId( + summaries: Map, + resolvedWorktrees: ResolvedWorktree[], + runtimeWorktreeId: string + ): RuntimeWorktreePsSummary | null { + const exact = summaries.get(runtimeWorktreeId) + if (exact) { + return exact + } + const parsed = parseRuntimeWorktreeId(runtimeWorktreeId) + if (!parsed) { + return null + } + const resolved = resolvedWorktrees.find( + (worktree) => + worktree.repoId === parsed.repoId && + areWorktreePathsEqual(worktree.path, parsed.worktreePath) + ) + return resolved ? (summaries.get(resolved.id) ?? null) : null + } + private buildTerminalSummary( leaf: RuntimeLeafRecord, worktreesById: Map @@ -1915,6 +3117,27 @@ export class OrcaRuntimeService { } } + private buildPtyTerminalSummary( + pty: RuntimePtyWorktreeRecord, + worktreesById: Map + ): RuntimeTerminalSummary { + const worktree = worktreesById.get(pty.worktreeId) + + return { + handle: this.issuePtyHandle(pty), + worktreeId: pty.worktreeId, + worktreePath: worktree?.path ?? '', + branch: worktree?.branch ?? '', + tabId: `pty:${pty.ptyId}`, + leafId: `pty:${pty.ptyId}`, + title: null, + connected: pty.connected, + writable: pty.connected, + lastOutputAt: pty.lastOutputAt, + preview: pty.preview + } + } + private getLiveLeafForHandle(handle: string): { record: TerminalHandleRecord leaf: RuntimeLeafRecord @@ -1935,6 +3158,53 @@ export class OrcaRuntimeService { return { record, leaf } } + private getLivePtyForHandle(handle: string): { + record: TerminalHandleRecord + pty: RuntimePtyWorktreeRecord + } | null { + const record = this.handles.get(handle) + if (!record || record.runtimeId !== this.runtimeId || !record.tabId.startsWith('pty:')) { + return null + } + if (!record.ptyId) { + return null + } + const pty = this.ptysById.get(record.ptyId) + if (!pty || pty.ptyId !== record.ptyId) { + return null + } + return { record, pty } + } + + private readPtyTerminal( + handle: string, + pty: RuntimePtyWorktreeRecord, + opts: { cursor?: number } = {} + ): RuntimeTerminalRead { + const allLines = buildTailLines(pty.tailBuffer, pty.tailPartialLine) + + let tail: string[] + let truncated: boolean + + if (typeof opts.cursor === 'number' && opts.cursor >= 0) { + const bufferStart = pty.tailLinesTotal - pty.tailBuffer.length + const sliceFrom = Math.max(0, opts.cursor - bufferStart) + tail = pty.tailBuffer.slice(sliceFrom) + truncated = opts.cursor < bufferStart + } else { + tail = allLines + truncated = pty.tailTruncated + } + + return { + handle, + status: pty.connected ? 'running' : 'unknown', + tail, + truncated, + nextCursor: String(pty.tailLinesTotal) + } + } + private issueHandle(leaf: RuntimeLeafRecord): string { const leafKey = this.getLeafKey(leaf.tabId, leaf.leafId) const existingHandle = this.handleByLeafKey.get(leafKey) @@ -1991,6 +3261,35 @@ export class OrcaRuntimeService { return preAllocated } + private issuePtyHandle(pty: RuntimePtyWorktreeRecord): string { + const existingHandle = this.handleByPtyId.get(pty.ptyId) + if (existingHandle) { + const existingRecord = this.handles.get(existingHandle) + if ( + existingRecord && + existingRecord.runtimeId === this.runtimeId && + existingRecord.ptyId === pty.ptyId + ) { + return existingHandle + } + } + + const handle = `term_${randomUUID()}` + const syntheticId = `pty:${pty.ptyId}` + this.handles.set(handle, { + handle, + runtimeId: this.runtimeId, + rendererGraphEpoch: this.rendererGraphEpoch, + worktreeId: pty.worktreeId, + tabId: syntheticId, + leafId: syntheticId, + ptyId: pty.ptyId, + ptyGeneration: 0 + }) + this.handleByPtyId.set(pty.ptyId, handle) + return handle + } + private refreshWritableFlags(): void { for (const leaf of this.leaves.values()) { leaf.writable = this.graphStatus === 'ready' && leaf.connected && leaf.ptyId !== null @@ -3273,6 +4572,13 @@ const MAX_TAIL_LINES = 120 const MAX_TAIL_CHARS = 4000 const MAX_PREVIEW_LINES = 6 const MAX_PREVIEW_CHARS = 300 +const WORKTREE_STATUS_PRIORITY: Record = { + inactive: 0, + active: 1, + done: 2, + working: 3, + permission: 4 +} const DEFAULT_REPO_SEARCH_REFS_LIMIT = 25 const DEFAULT_TERMINAL_LIST_LIMIT = 200 const DEFAULT_WORKTREE_LIST_LIMIT = 200 @@ -3408,6 +4714,89 @@ function normalizeBranchRef(branch: string): string { return branch.startsWith('refs/heads/') ? branch.slice('refs/heads/'.length) : branch } +function inferWorktreeIdFromPtyId(ptyId: string): string | null { + const separatorIndex = ptyId.lastIndexOf('@@') + if (separatorIndex <= 0) { + return null + } + const worktreeId = ptyId.slice(0, separatorIndex) + return parseRuntimeWorktreeId(worktreeId) ? worktreeId : null +} + +function parseRuntimeWorktreeId( + worktreeId: string +): { repoId: string; worktreePath: string } | null { + const separatorIndex = worktreeId.indexOf('::') + if (separatorIndex <= 0) { + return null + } + const worktreePath = worktreeId.slice(separatorIndex + 2) + if (!worktreePath) { + return null + } + return { + repoId: worktreeId.slice(0, separatorIndex), + worktreePath + } +} + +function findResolvedWorktreeIdForPath( + resolvedWorktrees: ResolvedWorktree[], + cwd: string +): string | null { + if (!cwd) { + return null + } + const matches = resolvedWorktrees + .filter( + (worktree) => + areWorktreePathsEqual(worktree.path, cwd) || isPathInsideWorktree(cwd, worktree.path) + ) + .sort((left, right) => right.path.length - left.path.length) + return matches[0]?.id ?? null +} + +function isPathInsideWorktree(candidatePath: string, worktreePath: string): boolean { + if (candidatePath === worktreePath) { + return true + } + const normalizedCandidate = candidatePath.replace(/\\/g, '/').replace(/\/+$/, '') + const normalizedWorktree = worktreePath.replace(/\\/g, '/').replace(/\/+$/, '') + return normalizedCandidate.startsWith(`${normalizedWorktree}/`) +} + +function getLeafWorktreeStatus( + leaf: RuntimeLeafRecord, + tabTitle: string | null +): RuntimeWorktreeStatus { + const detected = leaf.lastAgentStatus ?? detectAgentStatusFromTitle(leaf.title ?? tabTitle ?? '') + if (detected === 'permission') { + return 'permission' + } + if (detected === 'working') { + return 'working' + } + return leaf.ptyId ? 'active' : 'inactive' +} + +function getSavedTabWorktreeStatus(title: string, hasPty: boolean): RuntimeWorktreeStatus { + const detected = detectAgentStatusFromTitle(title) + if (detected === 'permission') { + return 'permission' + } + if (detected === 'working') { + return 'working' + } + return hasPty ? 'active' : 'inactive' +} + +function mergeWorktreeStatus( + current: RuntimeWorktreeStatus, + next: RuntimeWorktreeStatus +): RuntimeWorktreeStatus { + return WORKTREE_STATUS_PRIORITY[next] > WORKTREE_STATUS_PRIORITY[current] ? next : current +} + function normalizeTerminalChunk(chunk: string): string { return chunk .replace(/\r\n/g, '\n') @@ -3433,6 +4822,13 @@ function compareWorktreePs( left: RuntimeWorktreePsSummary, right: RuntimeWorktreePsSummary ): number { + // Pinned and unread worktrees sort above others so they survive truncation. + if (left.isPinned !== right.isPinned) { + return left.isPinned ? -1 : 1 + } + if (left.unread !== right.unread) { + return left.unread ? -1 : 1 + } const leftLast = left.lastOutputAt ?? -1 const rightLast = right.lastOutputAt ?? -1 if (leftLast !== rightLast) { diff --git a/src/main/runtime/rpc/core.ts b/src/main/runtime/rpc/core.ts index a91745cc87a..9c6eab7212a 100644 --- a/src/main/runtime/rpc/core.ts +++ b/src/main/runtime/rpc/core.ts @@ -13,6 +13,7 @@ export type RpcSuccess = { id: string ok: true result: unknown + streaming?: true _meta: RpcEnvelopeMeta } @@ -76,10 +77,53 @@ export function defineMethod( } } -export type RpcRegistry = ReadonlyMap +export type RpcStreamingHandler = ( + params: TParams, + ctx: RpcContext, + emit: (result: unknown) => void +) => Promise -export function buildRegistry(methods: readonly RpcMethod[]): RpcRegistry { - const registry = new Map() +// Why: streaming methods emit multiple responses over a long-lived connection. +// The `stream` flag lets the dispatcher distinguish them from one-shot methods +// and route them to the emit-based call path instead of the Promise-based one. +export type RpcStreamingMethod = { + readonly name: string + readonly params: ZodType | null + readonly stream: true + readonly handler: ( + params: unknown, + ctx: RpcContext, + emit: (result: unknown) => void + ) => Promise +} + +type DefineStreamingMethodSpec = { + name: string + params: TSchema + handler: RpcStreamingHandler +} + +export function defineStreamingMethod( + spec: DefineStreamingMethodSpec +): RpcStreamingMethod { + return { + name: spec.name, + params: spec.params, + stream: true, + handler: spec.handler as RpcStreamingMethod['handler'] + } +} + +export type RpcAnyMethod = RpcMethod | RpcStreamingMethod + +export function isStreamingMethod(method: RpcAnyMethod): method is RpcStreamingMethod { + return 'stream' in method && method.stream === true +} + +export type RpcRegistry = ReadonlyMap + +export function buildRegistry(methods: readonly RpcAnyMethod[]): RpcRegistry { + const registry = new Map() for (const method of methods) { if (registry.has(method.name)) { throw new Error(`duplicate_rpc_method:${method.name}`) diff --git a/src/main/runtime/rpc/dispatcher.ts b/src/main/runtime/rpc/dispatcher.ts index 8d0d1dc5cd3..faa8c57e33d 100644 --- a/src/main/runtime/rpc/dispatcher.ts +++ b/src/main/runtime/rpc/dispatcher.ts @@ -6,8 +6,9 @@ import { ZodError, buildRegistry, formatZodError, + isStreamingMethod, + type RpcAnyMethod, type RpcEnvelopeMeta, - type RpcMethod, type RpcRegistry, type RpcRequest, type RpcResponse @@ -18,7 +19,7 @@ import type { OrcaRuntimeService } from '../orca-runtime' export type DispatcherOptions = { runtime: OrcaRuntimeService - methods?: readonly RpcMethod[] + methods?: readonly RpcAnyMethod[] } export class RpcDispatcher { @@ -42,39 +43,110 @@ export class RpcDispatcher { ) } - let parsedParams: unknown - if (method.params === null) { - parsedParams = undefined - } else { - const rawParams = request.params ?? {} - const result = method.params.safeParse(rawParams) - if (!result.success) { - return errorResponse(request.id, meta, 'invalid_argument', formatZodError(result.error)) - } - parsedParams = result.data + const parsedParams = this.parseParams(request, method, meta) + if (parsedParams.error) { + return parsedParams.error + } + + // Why: streaming methods are not supported over one-shot transports like + // Unix sockets. They require a reply function that can be called multiple + // times, which is only available via dispatchStreaming. + if (isStreamingMethod(method)) { + return errorResponse( + request.id, + meta, + 'method_not_supported', + `Method ${request.method} requires a streaming transport` + ) } try { - const result = await method.handler(parsedParams, { + const result = await method.handler(parsedParams.value, { runtime: this.runtime, signal: options?.signal }) return successResponse(request.id, meta, result) } catch (error) { - // Why: browser methods throw BrowserError with a structured `code`; - // every other runtime error has a plain-message code. Routing by method - // prefix keeps the mapping a single decision rather than a per-method - // flag callers must remember to set. - if (request.method.startsWith('browser.')) { - return mapBrowserError(request.id, meta, error) - } - if (error instanceof ZodError) { - return errorResponse(request.id, meta, 'invalid_argument', formatZodError(error)) - } - return mapRuntimeError(request.id, meta, error) + return this.mapError(request, meta, error) } } + // Why: streaming dispatch sends multiple responses through the reply callback + // instead of returning a single Promise. This enables terminal.subscribe and + // other subscription-style methods that push data over time. + async dispatchStreaming(request: RpcRequest, reply: (response: string) => void): Promise { + const meta = this.meta() + const method = this.registry.get(request.method) + if (!method) { + reply( + JSON.stringify( + errorResponse(request.id, meta, 'method_not_found', `Unknown method: ${request.method}`) + ) + ) + return + } + + const parsedParams = this.parseParams(request, method, meta) + if (parsedParams.error) { + reply(JSON.stringify(parsedParams.error)) + return + } + + if (!isStreamingMethod(method)) { + try { + const result = await method.handler(parsedParams.value, { runtime: this.runtime }) + reply(JSON.stringify(successResponse(request.id, meta, result))) + } catch (error) { + reply(JSON.stringify(this.mapError(request, meta, error))) + } + return + } + + const emit = (result: unknown): void => { + const response = successResponse(request.id, meta, result) + response.streaming = true + reply(JSON.stringify(response)) + } + + try { + await method.handler(parsedParams.value, { runtime: this.runtime }, emit) + } catch (error) { + reply(JSON.stringify(this.mapError(request, meta, error))) + } + } + + private parseParams( + request: RpcRequest, + method: RpcAnyMethod, + meta: RpcEnvelopeMeta + ): { value: unknown; error?: undefined } | { value?: undefined; error: RpcResponse } { + if (method.params === null) { + return { value: undefined } + } + const rawParams = request.params ?? {} + const result = method.params.safeParse(rawParams) + if (!result.success) { + return { + error: errorResponse(request.id, meta, 'invalid_argument', formatZodError(result.error)) + } + } + return { value: result.data } + } + + private mapError(request: RpcRequest, meta: RpcEnvelopeMeta, error: unknown): RpcResponse { + // Why: browser methods throw BrowserError with a structured `code`; + // every other runtime error has a plain-message code. Routing by method + // prefix keeps the mapping a single decision rather than a per-method + // flag callers must remember to set. + if (request.method.startsWith('browser.')) { + return mapBrowserError(request.id, meta, error) + } + if (error instanceof ZodError) { + return errorResponse(request.id, meta, 'invalid_argument', formatZodError(error)) + } + return mapRuntimeError(request.id, meta, error) + } + private meta(): RpcEnvelopeMeta { return { runtimeId: this.runtime.getRuntimeId() } } diff --git a/src/main/runtime/rpc/e2ee-channel.test.ts b/src/main/runtime/rpc/e2ee-channel.test.ts new file mode 100644 index 00000000000..43920ed4d79 --- /dev/null +++ b/src/main/runtime/rpc/e2ee-channel.test.ts @@ -0,0 +1,266 @@ +import { describe, expect, it, vi, beforeEach, afterEach } from 'vitest' +import type { WebSocket } from 'ws' +import { E2EEChannel, type E2EEChannelOptions } from './e2ee-channel' +import { generateKeyPair, deriveSharedKey, encrypt, decrypt } from './e2ee-crypto' + +function publicKeyToBase64(key: Uint8Array): string { + return Buffer.from(key).toString('base64') +} + +function createMockWs() { + const sent: string[] = [] + return { + OPEN: 1 as const, + readyState: 1, + send: vi.fn((data: string) => sent.push(data)), + close: vi.fn(), + sent + } +} + +function setup(overrides?: Partial) { + const serverKeys = generateKeyPair() + const clientKeys = generateKeyPair() + const ws = createMockWs() + const onReady = vi.fn() + const onError = vi.fn() + + const channel = new E2EEChannel(ws as unknown as WebSocket, { + serverSecretKey: serverKeys.secretKey, + validateToken: (token) => token === 'valid-token', + onReady, + onError, + ...overrides + }) + + return { channel, ws, serverKeys, clientKeys, onReady, onError } +} + +function doHandshake(ctx: ReturnType) { + const hello = JSON.stringify({ + type: 'e2ee_hello', + publicKeyB64: publicKeyToBase64(ctx.clientKeys.publicKey) + }) + ctx.channel.handleRawMessage(hello) + const sharedKey = deriveSharedKey(ctx.clientKeys.secretKey, ctx.serverKeys.publicKey) + ctx.channel.handleRawMessage( + encrypt(JSON.stringify({ type: 'e2ee_auth', deviceToken: 'valid-token' }), sharedKey) + ) + return sharedKey +} + +describe('E2EEChannel', () => { + beforeEach(() => { + vi.useFakeTimers() + }) + + afterEach(() => { + vi.useRealTimers() + }) + + describe('handshake', () => { + it('completes handshake with valid encrypted auth', () => { + const ctx = setup() + doHandshake(ctx) + + expect(ctx.onReady).toHaveBeenCalledWith(ctx.channel) + expect(ctx.onError).not.toHaveBeenCalled() + expect(ctx.channel.deviceToken).toBe('valid-token') + + const readyMsg = JSON.parse(ctx.ws.sent[0]!) + expect(readyMsg).toEqual({ type: 'e2ee_ready' }) + const authMsg = decrypt( + ctx.ws.sent[1]!, + deriveSharedKey(ctx.clientKeys.secretKey, ctx.serverKeys.publicKey) + ) + expect(JSON.parse(authMsg!)).toEqual({ type: 'e2ee_authenticated' }) + }) + + it('does not authenticate from plaintext hello alone', () => { + const ctx = setup() + ctx.channel.handleRawMessage( + JSON.stringify({ + type: 'e2ee_hello', + publicKeyB64: publicKeyToBase64(ctx.clientKeys.publicKey) + }) + ) + + expect(ctx.onReady).not.toHaveBeenCalled() + expect(JSON.parse(ctx.ws.sent[0]!)).toEqual({ type: 'e2ee_ready' }) + }) + + it('rejects invalid encrypted token', () => { + const ctx = setup() + ctx.channel.handleRawMessage( + JSON.stringify({ + type: 'e2ee_hello', + publicKeyB64: publicKeyToBase64(ctx.clientKeys.publicKey) + }) + ) + const sharedKey = deriveSharedKey(ctx.clientKeys.secretKey, ctx.serverKeys.publicKey) + ctx.channel.handleRawMessage( + encrypt(JSON.stringify({ type: 'e2ee_auth', deviceToken: 'bad-token' }), sharedKey) + ) + + expect(ctx.onError).toHaveBeenCalledWith(4001, 'Unauthorized') + expect(ctx.onReady).not.toHaveBeenCalled() + }) + + it('rejects malformed JSON', () => { + const ctx = setup() + ctx.channel.handleRawMessage('not json') + + expect(ctx.onError).toHaveBeenCalledWith(4001, 'Invalid handshake message') + }) + + it('rejects missing fields', () => { + const ctx = setup() + ctx.channel.handleRawMessage(JSON.stringify({ type: 'e2ee_hello' })) + + expect(ctx.onError).toHaveBeenCalledWith(4001, 'Invalid e2ee_hello') + }) + + it('rejects invalid public key length', () => { + const ctx = setup() + ctx.channel.handleRawMessage( + JSON.stringify({ + type: 'e2ee_hello', + publicKeyB64: Buffer.from('short').toString('base64') + }) + ) + + expect(ctx.onError).toHaveBeenCalledWith(4001, 'Invalid public key') + }) + + it('times out if no hello received', () => { + const ctx = setup() + + vi.advanceTimersByTime(10_001) + + expect(ctx.onError).toHaveBeenCalledWith(4002, 'E2EE handshake timeout') + }) + + it('clears timeout after successful handshake', () => { + const ctx = setup() + doHandshake(ctx) + + vi.advanceTimersByTime(10_001) + + expect(ctx.onError).not.toHaveBeenCalled() + }) + }) + + describe('post-handshake messaging', () => { + it('decrypts and forwards messages', () => { + const ctx = setup() + const sharedKey = doHandshake(ctx) + const received: string[] = [] + + ctx.channel.onMessage((plaintext) => { + received.push(plaintext) + }) + + const request = '{"id":"rpc-1","method":"status.get"}' + ctx.channel.handleRawMessage(encrypt(request, sharedKey)) + + expect(received).toEqual([request]) + }) + + it('provides encrypted reply function', () => { + const ctx = setup() + const sharedKey = doHandshake(ctx) + + ctx.channel.onMessage((_plaintext, encryptedReply) => { + encryptedReply('{"id":"rpc-1","ok":true}') + }) + + ctx.channel.handleRawMessage(encrypt('{"id":"rpc-1","method":"status.get"}', sharedKey)) + + // The reply (ws.sent[2], after ready + authenticated) should be encrypted + const replyEncrypted = ctx.ws.sent[2]! + const replyPlain = decrypt(replyEncrypted, sharedKey) + expect(replyPlain).toBe('{"id":"rpc-1","ok":true}') + }) + + it('silently drops messages with wrong key', () => { + const ctx = setup() + doHandshake(ctx) + const received: string[] = [] + + ctx.channel.onMessage((plaintext) => { + received.push(plaintext) + }) + + const attackerKey = deriveSharedKey(generateKeyPair().secretKey, generateKeyPair().publicKey) + ctx.channel.handleRawMessage(encrypt('attack', attackerKey)) + + expect(received).toEqual([]) + }) + + it('closes after too many consecutive decrypt failures', () => { + const ctx = setup() + doHandshake(ctx) + ctx.channel.onMessage(() => {}) + + const badKey = deriveSharedKey(generateKeyPair().secretKey, generateKeyPair().publicKey) + + for (let i = 0; i < 4; i++) { + ctx.channel.handleRawMessage(encrypt('bad', badKey)) + } + expect(ctx.onError).not.toHaveBeenCalled() + + ctx.channel.handleRawMessage(encrypt('bad', badKey)) + expect(ctx.onError).toHaveBeenCalledWith(4003, 'Too many decryption failures') + }) + + it('resets failure count on successful decrypt', () => { + const ctx = setup() + const sharedKey = doHandshake(ctx) + ctx.channel.onMessage(() => {}) + + const badKey = deriveSharedKey(generateKeyPair().secretKey, generateKeyPair().publicKey) + + for (let i = 0; i < 4; i++) { + ctx.channel.handleRawMessage(encrypt('bad', badKey)) + } + + // Successful decrypt resets the counter + ctx.channel.handleRawMessage(encrypt('good', sharedKey)) + + for (let i = 0; i < 4; i++) { + ctx.channel.handleRawMessage(encrypt('bad', badKey)) + } + expect(ctx.onError).not.toHaveBeenCalled() + }) + }) + + describe('cross-compatibility', () => { + it('desktop encrypt is decryptable by desktop decrypt (sanity)', () => { + const a = generateKeyPair() + const b = generateKeyPair() + const sharedA = deriveSharedKey(a.secretKey, b.publicKey) + const sharedB = deriveSharedKey(b.secretKey, a.publicKey) + + const msg = '{"method":"terminal.subscribe","params":{"terminal":"t1"}}' + const enc = encrypt(msg, sharedA) + expect(decrypt(enc, sharedB)).toBe(msg) + }) + }) + + describe('destroy', () => { + it('clears state and stops forwarding', () => { + const ctx = setup() + const sharedKey = doHandshake(ctx) + const received: string[] = [] + + ctx.channel.onMessage((plaintext) => { + received.push(plaintext) + }) + + ctx.channel.destroy() + ctx.channel.handleRawMessage(encrypt('after destroy', sharedKey)) + + expect(received).toEqual([]) + }) + }) +}) diff --git a/src/main/runtime/rpc/e2ee-channel.ts b/src/main/runtime/rpc/e2ee-channel.ts new file mode 100644 index 00000000000..25bfd1e72fb --- /dev/null +++ b/src/main/runtime/rpc/e2ee-channel.ts @@ -0,0 +1,178 @@ +// Why: the E2EE channel sits between the WebSocket transport and the RPC handler. +// It owns the handshake state machine and transparent encrypt/decrypt so the RPC +// handler only sees plaintext JSON, identical to the Unix socket path. +import type { WebSocket } from 'ws' +import { deriveSharedKey, encrypt, decrypt } from './e2ee-crypto' + +type ChannelState = 'awaiting_hello' | 'awaiting_auth' | 'ready' + +const HANDSHAKE_TIMEOUT_MS = 10_000 +const MAX_CONSECUTIVE_DECRYPT_FAILURES = 5 + +type E2EEHello = { + type: 'e2ee_hello' + publicKeyB64: string +} + +type E2EEAuth = { + type: 'e2ee_auth' + deviceToken: string +} + +export type E2EEChannelOptions = { + serverSecretKey: Uint8Array + validateToken: (token: string) => boolean + onReady: (channel: E2EEChannel) => void + onError: (code: number, reason: string) => void +} + +export class E2EEChannel { + private state: ChannelState = 'awaiting_hello' + private sharedKey: Uint8Array | null = null + private consecutiveFailures = 0 + private handshakeTimer: ReturnType | null = null + private readonly ws: WebSocket + private readonly serverSecretKey: Uint8Array + private readonly validateToken: (token: string) => boolean + private readonly onReady: (channel: E2EEChannel) => void + private readonly onError: (code: number, reason: string) => void + // Why: the RPC handler is set after the channel is ready, so the channel + // can forward decrypted messages. Kept as a callback rather than constructor + // param because the handler needs the encrypt function for replies. + private messageHandler: + | ((plaintext: string, encryptedReply: (response: string) => void) => void) + | null = null + + deviceToken: string | null = null + + constructor(ws: WebSocket, options: E2EEChannelOptions) { + this.ws = ws + this.serverSecretKey = options.serverSecretKey + this.validateToken = options.validateToken + this.onReady = options.onReady + this.onError = options.onError + + this.handshakeTimer = setTimeout(() => { + this.onError(4002, 'E2EE handshake timeout') + }, HANDSHAKE_TIMEOUT_MS) + } + + onMessage( + handler: (plaintext: string, encryptedReply: (response: string) => void) => void + ): void { + this.messageHandler = handler + } + + handleRawMessage(raw: string): void { + if (this.state === 'awaiting_hello') { + this.handleHello(raw) + return + } + + if (!this.sharedKey) { + return + } + + const plaintext = decrypt(raw, this.sharedKey) + if (plaintext === null) { + this.consecutiveFailures++ + if (this.consecutiveFailures >= MAX_CONSECUTIVE_DECRYPT_FAILURES) { + this.onError(4003, 'Too many decryption failures') + } + return + } + + this.consecutiveFailures = 0 + if (this.state === 'awaiting_auth') { + this.handleAuth(plaintext) + return + } + + const encryptedReply = (response: string) => { + if (this.ws.readyState === this.ws.OPEN) { + this.ws.send(encrypt(response, this.sharedKey!)) + } + } + this.messageHandler?.(plaintext, encryptedReply) + } + + private handleHello(raw: string): void { + let hello: E2EEHello + try { + hello = JSON.parse(raw) as E2EEHello + } catch { + this.onError(4001, 'Invalid handshake message') + return + } + + if (hello.type !== 'e2ee_hello' || !hello.publicKeyB64) { + this.onError(4001, 'Invalid e2ee_hello') + return + } + + // Why: derive the shared key from our secret + client's public key. + // Both sides compute the same shared secret via ECDH. + const clientPublicKey = Uint8Array.from(Buffer.from(hello.publicKeyB64, 'base64')) + if (clientPublicKey.length !== 32) { + this.onError(4001, 'Invalid public key') + return + } + + this.sharedKey = deriveSharedKey(this.serverSecretKey, clientPublicKey) + this.state = 'awaiting_auth' + + // Why: send e2ee_ready as plaintext — the client needs it to know the + // key exchange succeeded before it can send encrypted authentication. + if (this.ws.readyState === this.ws.OPEN) { + this.ws.send(JSON.stringify({ type: 'e2ee_ready' })) + } + } + + private handleAuth(plaintext: string): void { + let auth: E2EEAuth + try { + auth = JSON.parse(plaintext) as E2EEAuth + } catch { + this.sendEncryptedControl({ type: 'e2ee_error', error: { code: 'bad_auth' } }) + this.onError(4001, 'Invalid e2ee_auth') + return + } + + if (auth.type !== 'e2ee_auth' || !auth.deviceToken) { + this.sendEncryptedControl({ type: 'e2ee_error', error: { code: 'bad_auth' } }) + this.onError(4001, 'Invalid e2ee_auth') + return + } + if (!this.validateToken(auth.deviceToken)) { + this.sendEncryptedControl({ type: 'e2ee_error', error: { code: 'unauthorized' } }) + this.onError(4001, 'Unauthorized') + return + } + + this.deviceToken = auth.deviceToken + this.state = 'ready' + + if (this.handshakeTimer) { + clearTimeout(this.handshakeTimer) + this.handshakeTimer = null + } + + this.sendEncryptedControl({ type: 'e2ee_authenticated' }) + this.onReady(this) + } + + private sendEncryptedControl(message: unknown): void { + if (this.ws.readyState === this.ws.OPEN && this.sharedKey) { + this.ws.send(encrypt(JSON.stringify(message), this.sharedKey)) + } + } + + destroy(): void { + if (this.handshakeTimer) { + clearTimeout(this.handshakeTimer) + this.handshakeTimer = null + } + this.sharedKey = null + this.messageHandler = null + } +} diff --git a/src/main/runtime/rpc/e2ee-crypto.test.ts b/src/main/runtime/rpc/e2ee-crypto.test.ts new file mode 100644 index 00000000000..09e546db63c --- /dev/null +++ b/src/main/runtime/rpc/e2ee-crypto.test.ts @@ -0,0 +1,79 @@ +import { describe, expect, it } from 'vitest' +import { generateKeyPair, deriveSharedKey, encrypt, decrypt } from './e2ee-crypto' + +describe('e2ee-crypto', () => { + it('encrypt/decrypt round-trips with shared key', () => { + const server = generateKeyPair() + const client = generateKeyPair() + + const serverShared = deriveSharedKey(server.secretKey, client.publicKey) + const clientShared = deriveSharedKey(client.secretKey, server.publicKey) + + const message = '{"id":"rpc-1","method":"status.get"}' + const encrypted = encrypt(message, clientShared) + const decrypted = decrypt(encrypted, serverShared) + + expect(decrypted).toBe(message) + }) + + it('decrypt returns null with wrong key', () => { + const server = generateKeyPair() + const client = generateKeyPair() + const attacker = generateKeyPair() + + const clientShared = deriveSharedKey(client.secretKey, server.publicKey) + const attackerShared = deriveSharedKey(attacker.secretKey, server.publicKey) + + const encrypted = encrypt('secret data', clientShared) + expect(decrypt(encrypted, attackerShared)).toBeNull() + }) + + it('each encryption produces unique ciphertext (random nonce)', () => { + const server = generateKeyPair() + const client = generateKeyPair() + const shared = deriveSharedKey(client.secretKey, server.publicKey) + + const message = 'same message' + const a = encrypt(message, shared) + const b = encrypt(message, shared) + + expect(a).not.toBe(b) + }) + + it('handles empty string', () => { + const server = generateKeyPair() + const client = generateKeyPair() + const shared = deriveSharedKey(client.secretKey, server.publicKey) + + const encrypted = encrypt('', shared) + expect(decrypt(encrypted, shared)).toBe('') + }) + + it('handles unicode content', () => { + const server = generateKeyPair() + const client = generateKeyPair() + const shared = deriveSharedKey(client.secretKey, server.publicKey) + + const message = '日本語テスト 🎉 émojis' + const encrypted = encrypt(message, shared) + expect(decrypt(encrypted, shared)).toBe(message) + }) + + it('decrypt returns null for truncated data', () => { + const shared = deriveSharedKey(generateKeyPair().secretKey, generateKeyPair().publicKey) + expect(decrypt('dG9vc2hvcnQ=', shared)).toBeNull() + }) + + it('decrypt returns null for tampered data', () => { + const server = generateKeyPair() + const client = generateKeyPair() + const shared = deriveSharedKey(client.secretKey, server.publicKey) + + const encrypted = encrypt('hello', shared) + const bytes = Buffer.from(encrypted, 'base64') + bytes[bytes.length - 1] ^= 0xff + const tampered = bytes.toString('base64') + + expect(decrypt(tampered, shared)).toBeNull() + }) +}) diff --git a/src/main/runtime/rpc/e2ee-crypto.ts b/src/main/runtime/rpc/e2ee-crypto.ts new file mode 100644 index 00000000000..39adec27160 --- /dev/null +++ b/src/main/runtime/rpc/e2ee-crypto.ts @@ -0,0 +1,41 @@ +// Why: shared E2EE primitives for the desktop side. Wraps tweetnacl to provide +// encrypt/decrypt with the NaCl box format: [24-byte nonce][ciphertext], encoded +// as base64 for transmission over WebSocket text frames. +import nacl from 'tweetnacl' + +export function generateKeyPair(): nacl.BoxKeyPair { + return nacl.box.keyPair() +} + +export function deriveSharedKey(ourSecretKey: Uint8Array, peerPublicKey: Uint8Array): Uint8Array { + return nacl.box.before(peerPublicKey, ourSecretKey) +} + +export function encrypt(plaintext: string, sharedKey: Uint8Array): string { + const nonce = nacl.randomBytes(nacl.box.nonceLength) + const messageBytes = new TextEncoder().encode(plaintext) + const ciphertext = nacl.box.after(messageBytes, nonce, sharedKey) + + const bundle = new Uint8Array(nonce.length + ciphertext.length) + bundle.set(nonce) + bundle.set(ciphertext, nonce.length) + + return Buffer.from(bundle).toString('base64') +} + +export function decrypt(encrypted: string, sharedKey: Uint8Array): string | null { + const bundle = Uint8Array.from(Buffer.from(encrypted, 'base64')) + if (bundle.length < nacl.box.nonceLength + nacl.box.overheadLength) { + return null + } + + const nonce = bundle.slice(0, nacl.box.nonceLength) + const ciphertext = bundle.slice(nacl.box.nonceLength) + const plaintext = nacl.box.open.after(ciphertext, nonce, sharedKey) + + if (!plaintext) { + return null + } + + return new TextDecoder().decode(plaintext) +} diff --git a/src/main/runtime/rpc/e2ee-integration.test.ts b/src/main/runtime/rpc/e2ee-integration.test.ts new file mode 100644 index 00000000000..f4029b82473 --- /dev/null +++ b/src/main/runtime/rpc/e2ee-integration.test.ts @@ -0,0 +1,179 @@ +import { describe, expect, it, vi, beforeEach, afterEach } from 'vitest' +import type { WebSocket } from 'ws' +import { E2EEChannel } from './e2ee-channel' +import { generateKeyPair, deriveSharedKey, encrypt, decrypt } from './e2ee-crypto' + +// Why: this test simulates the full mobile → desktop E2EE flow without a real +// WebSocket. The "mobile" side generates an ephemeral keypair, sends e2ee_hello, +// the E2EEChannel (desktop) derives the shared key, and then both sides can +// exchange encrypted RPC messages. This validates that the handshake protocol +// and crypto are end-to-end compatible. + +function publicKeyToBase64(key: Uint8Array): string { + return Buffer.from(key).toString('base64') +} + +describe('E2EE integration (simulated mobile ↔ desktop)', () => { + let serverKeys: ReturnType + let mobileEphemeralKeys: ReturnType + let wsSent: string[] + let mockWs: { + OPEN: 1 + readyState: number + send: ReturnType + close: ReturnType + } + let channel: E2EEChannel + let onReady: (channel: E2EEChannel) => void + let onError: (code: number, reason: string) => void + + beforeEach(() => { + vi.useFakeTimers() + + serverKeys = generateKeyPair() + mobileEphemeralKeys = generateKeyPair() + wsSent = [] + mockWs = { + OPEN: 1, + readyState: 1, + send: vi.fn((data: string) => wsSent.push(data)), + close: vi.fn() + } + onReady = vi.fn() as unknown as (channel: E2EEChannel) => void + onError = vi.fn() as unknown as (code: number, reason: string) => void + + channel = new E2EEChannel(mockWs as unknown as WebSocket, { + serverSecretKey: serverKeys.secretKey, + validateToken: (token) => token === 'device-abc', + onReady, + onError + }) + }) + + afterEach(() => { + channel.destroy() + vi.useRealTimers() + }) + + function completeHandshake(targetChannel = channel, keys = mobileEphemeralKeys): Uint8Array { + targetChannel.handleRawMessage( + JSON.stringify({ + type: 'e2ee_hello', + publicKeyB64: publicKeyToBase64(keys.publicKey) + }) + ) + const sharedKey = deriveSharedKey(keys.secretKey, serverKeys.publicKey) + targetChannel.handleRawMessage( + encrypt(JSON.stringify({ type: 'e2ee_auth', deviceToken: 'device-abc' }), sharedKey) + ) + return sharedKey + } + + it('full handshake → encrypted RPC round-trip', () => { + const mobileShared = completeHandshake() + + // Desktop should respond with plaintext ready, then encrypted auth ack. + expect(onReady).toHaveBeenCalled() + expect(wsSent).toHaveLength(2) + expect(JSON.parse(wsSent[0]!)).toEqual({ type: 'e2ee_ready' }) + expect(JSON.parse(decrypt(wsSent[1]!, mobileShared)!)).toEqual({ + type: 'e2ee_authenticated' + }) + + // Set up the desktop message handler + const desktopReceived: string[] = [] + channel.onMessage((plaintext, encryptedReply) => { + desktopReceived.push(plaintext) + encryptedReply(JSON.stringify({ id: 'rpc-1', ok: true, result: { status: 'ready' } })) + }) + + // Mobile sends encrypted RPC request + const request = JSON.stringify({ id: 'rpc-1', method: 'status.get' }) + channel.handleRawMessage(encrypt(request, mobileShared)) + + // Desktop received the plaintext + expect(desktopReceived).toEqual([request]) + + // Desktop's encrypted reply (wsSent[2]) is decryptable by mobile + expect(wsSent).toHaveLength(3) + const replyPlain = decrypt(wsSent[2]!, mobileShared) + expect(JSON.parse(replyPlain!)).toEqual({ + id: 'rpc-1', + ok: true, + result: { status: 'ready' } + }) + }) + + it('mobile reconnects with fresh ephemeral key', () => { + // First connection + completeHandshake() + expect(onReady).toHaveBeenCalledTimes(1) + + const firstShared = deriveSharedKey(mobileEphemeralKeys.secretKey, serverKeys.publicKey) + + // Simulate disconnect + reconnect with new ephemeral key + channel.destroy() + wsSent.length = 0 + + const newMobileKeys = generateKeyPair() + const newChannel = new E2EEChannel(mockWs as unknown as WebSocket, { + serverSecretKey: serverKeys.secretKey, + validateToken: (token) => token === 'device-abc', + onReady, + onError + }) + + completeHandshake(newChannel, newMobileKeys) + + expect(onReady).toHaveBeenCalledTimes(2) + + const secondShared = deriveSharedKey(newMobileKeys.secretKey, serverKeys.publicKey) + + // Keys from different sessions must not be interchangeable + expect(Buffer.from(firstShared).toString('hex')).not.toBe( + Buffer.from(secondShared).toString('hex') + ) + + // Verify new session works + const received: string[] = [] + newChannel.onMessage((plaintext) => received.push(plaintext)) + newChannel.handleRawMessage(encrypt('{"id":"2","method":"test"}', secondShared)) + expect(received).toEqual(['{"id":"2","method":"test"}']) + + newChannel.destroy() + }) + + it('streaming messages work through E2EE', () => { + const mobileShared = completeHandshake() + const received: string[] = [] + + channel.onMessage((plaintext, encryptedReply) => { + received.push(plaintext) + // Simulate streaming: send multiple encrypted responses + for (let i = 0; i < 3; i++) { + encryptedReply( + JSON.stringify({ + id: 'stream-1', + ok: true, + streaming: true, + result: { type: 'data', chunk: `line ${i}\n` } + }) + ) + } + }) + + channel.handleRawMessage( + encrypt(JSON.stringify({ id: 'stream-1', method: 'terminal.subscribe' }), mobileShared) + ) + + // 1 plaintext ready + 1 encrypted auth ack + 3 streaming responses + expect(wsSent).toHaveLength(5) + + for (let i = 2; i < 5; i++) { + const plain = decrypt(wsSent[i]!, mobileShared) + const parsed = JSON.parse(plain!) + expect(parsed.streaming).toBe(true) + expect(parsed.result.chunk).toBe(`line ${i - 2}\n`) + } + }) +}) diff --git a/src/main/runtime/rpc/methods/index.ts b/src/main/runtime/rpc/methods/index.ts index 9bdcc514c26..cf3fbf34f86 100644 --- a/src/main/runtime/rpc/methods/index.ts +++ b/src/main/runtime/rpc/methods/index.ts @@ -1,4 +1,4 @@ -import type { RpcMethod } from '../core' +import type { RpcAnyMethod } from '../core' import { STATUS_METHODS } from './status' import { REPO_METHODS } from './repo' import { WORKTREE_METHODS } from './worktree' @@ -6,16 +6,20 @@ import { TERMINAL_METHODS } from './terminal' import { BROWSER_CORE_METHODS } from './browser-core' import { BROWSER_EXTRA_METHODS } from './browser-extras' import { ORCHESTRATION_METHODS } from './orchestration' +import { NOTIFICATION_METHODS } from './notifications' +import { STATS_METHODS } from './stats' // Why: a flat manifest keeps registration order explicit and provides one // grep-point for "what methods does the RPC server expose?" — useful when // auditing the security boundary or wiring new CLI commands. -export const ALL_RPC_METHODS: readonly RpcMethod[] = [ +export const ALL_RPC_METHODS: readonly RpcAnyMethod[] = [ ...STATUS_METHODS, ...REPO_METHODS, ...WORKTREE_METHODS, ...TERMINAL_METHODS, ...BROWSER_CORE_METHODS, ...BROWSER_EXTRA_METHODS, - ...ORCHESTRATION_METHODS + ...ORCHESTRATION_METHODS, + ...NOTIFICATION_METHODS, + ...STATS_METHODS ] diff --git a/src/main/runtime/rpc/methods/notifications.ts b/src/main/runtime/rpc/methods/notifications.ts new file mode 100644 index 00000000000..607474bb941 --- /dev/null +++ b/src/main/runtime/rpc/methods/notifications.ts @@ -0,0 +1,44 @@ +import { z } from 'zod' +import { defineStreamingMethod, defineMethod, type RpcAnyMethod } from '../core' + +const NotificationUnsubscribeParams = z.object({ + subscriptionId: z + .unknown() + .transform((value) => (typeof value === 'string' && value.length > 0 ? value : '')) + .pipe(z.string().min(1, 'Missing subscriptionId')) +}) + +// Why: notifications.subscribe streams desktop notification events to mobile +// clients over WebSocket. The mobile client shows a local push notification +// for each event. This avoids requiring Firebase/APNs — the existing +// persistent WebSocket connection doubles as the push channel. +export const NOTIFICATION_METHODS: readonly RpcAnyMethod[] = [ + defineStreamingMethod({ + name: 'notifications.subscribe', + params: null, + handler: async (_params, { runtime }, emit) => { + await new Promise((resolve) => { + const unsubscribe = runtime.onNotificationDispatched((event) => { + emit({ type: 'notification', ...event }) + }) + + const subscriptionId = `notifications-${Date.now()}` + runtime.registerSubscriptionCleanup(subscriptionId, () => { + unsubscribe() + emit({ type: 'end' }) + resolve() + }) + + emit({ type: 'ready', subscriptionId }) + }) + } + }), + defineMethod({ + name: 'notifications.unsubscribe', + params: NotificationUnsubscribeParams, + handler: async (params, { runtime }) => { + runtime.cleanupSubscription(params.subscriptionId) + return { unsubscribed: true } + } + }) +] diff --git a/src/main/runtime/rpc/methods/repo.ts b/src/main/runtime/rpc/methods/repo.ts index b3a851dac0d..3af1133f836 100644 --- a/src/main/runtime/rpc/methods/repo.ts +++ b/src/main/runtime/rpc/methods/repo.ts @@ -52,5 +52,10 @@ export const REPO_METHODS: RpcMethod[] = [ params: RepoSearchRefs, handler: async (params, { runtime }) => runtime.searchRepoRefs(params.repo, params.query, params.limit) + }), + defineMethod({ + name: 'repo.hooks', + params: RepoSelector, + handler: async (params, { runtime }) => runtime.getRepoHooks(params.repo) }) ] diff --git a/src/main/runtime/rpc/methods/stats.ts b/src/main/runtime/rpc/methods/stats.ts new file mode 100644 index 00000000000..59f71701c3a --- /dev/null +++ b/src/main/runtime/rpc/methods/stats.ts @@ -0,0 +1,11 @@ +import { defineMethod, type RpcMethod } from '../core' + +export const STATS_METHODS: RpcMethod[] = [ + defineMethod({ + name: 'stats.summary', + params: null, + handler: async (_params, { runtime }) => { + return runtime.getStatsSummary() ?? {} + } + }) +] diff --git a/src/main/runtime/rpc/methods/terminal.ts b/src/main/runtime/rpc/methods/terminal.ts index cc3db888eb9..f69a3fc9c84 100644 --- a/src/main/runtime/rpc/methods/terminal.ts +++ b/src/main/runtime/rpc/methods/terminal.ts @@ -1,5 +1,6 @@ +/* oxlint-disable max-lines -- Why: terminal RPC methods are co-located for discoverability; splitting would scatter related handlers across files. */ import { z } from 'zod' -import { defineMethod, type RpcMethod } from '../core' +import { defineMethod, defineStreamingMethod, type RpcAnyMethod } from '../core' import { OptionalFiniteNumber, OptionalString, requiredString } from '../schemas' const TerminalHandle = z.object({ @@ -77,7 +78,45 @@ const TerminalStop = z.object({ worktree: requiredString('Missing worktree selector') }) -export const TERMINAL_METHODS: RpcMethod[] = [ +const TerminalResizeForClient = z.discriminatedUnion('mode', [ + z.object({ + terminal: requiredString('Missing terminal handle'), + mode: z.literal('mobile-fit'), + cols: z.number().finite().positive(), + rows: z.number().finite().positive(), + clientId: requiredString('Missing client ID') + }), + z.object({ + terminal: requiredString('Missing terminal handle'), + mode: z.literal('restore'), + clientId: requiredString('Missing client ID') + }) +]) + +const TerminalSubscribe = TerminalHandle.extend({ + client: z + .object({ + id: requiredString('Missing client ID'), + type: z.enum(['mobile', 'desktop']).default('desktop') + }) + .optional(), + viewport: z + .object({ + cols: z.number().int().min(20).max(240), + rows: z.number().int().min(8).max(120) + }) + .optional() +}) + +const TerminalSetDisplayMode = TerminalHandle.extend({ + mode: z.enum(['auto', 'phone', 'desktop']) +}) + +const TerminalUnsubscribe = z.object({ + subscriptionId: requiredString('Missing subscription ID') +}) + +export const TERMINAL_METHODS: RpcAnyMethod[] = [ defineMethod({ name: 'terminal.list', params: TerminalListParams, @@ -157,6 +196,29 @@ export const TERMINAL_METHODS: RpcMethod[] = [ params: TerminalStop, handler: async (params, { runtime }) => runtime.stopTerminalsForWorktree(params.worktree) }), + defineMethod({ + name: 'terminal.resizeForClient', + params: TerminalResizeForClient, + handler: async (params, { runtime }) => { + const leaf = runtime.resolveLeafForHandle(params.terminal) + if (!leaf?.ptyId) { + throw new Error('no_connected_pty') + } + const result = runtime.resizeForClient( + leaf.ptyId, + params.mode, + params.clientId, + params.mode === 'mobile-fit' ? params.cols : undefined, + params.mode === 'mobile-fit' ? params.rows : undefined + ) + return { + terminal: { + handle: params.terminal, + ...result + } + } + } + }), defineMethod({ name: 'terminal.focus', params: TerminalHandle, @@ -170,5 +232,141 @@ export const TERMINAL_METHODS: RpcMethod[] = [ handler: async (params, { runtime }) => ({ close: await runtime.closeTerminal(params.terminal) }) + }), + defineMethod({ + name: 'terminal.setDisplayMode', + params: TerminalSetDisplayMode, + handler: async (params, { runtime }) => { + const leaf = runtime.resolveLeafForHandle(params.terminal) + if (!leaf?.ptyId) { + throw new Error('no_connected_pty') + } + runtime.setMobileDisplayMode(leaf.ptyId, params.mode) + runtime.applyMobileDisplayMode(leaf.ptyId) + return { mode: params.mode } + } + }), + defineMethod({ + name: 'terminal.getDisplayMode', + params: TerminalHandle, + handler: async (params, { runtime }) => { + const leaf = runtime.resolveLeafForHandle(params.terminal) + const mode = leaf?.ptyId ? runtime.getMobileDisplayMode(leaf.ptyId) : 'auto' + const isPhoneFitted = leaf?.ptyId ? runtime.isMobileSubscriberActive(leaf.ptyId) : false + return { mode, isPhoneFitted } + } + }), + // Why: terminal.subscribe streams live terminal output over WebSocket. + // It sends initial scrollback, then live data chunks as they arrive. + // Mobile clients pass client+viewport params for server-side auto-fit. + defineStreamingMethod({ + name: 'terminal.subscribe', + params: TerminalSubscribe, + handler: async (params, { runtime }, emit) => { + let leaf = runtime.resolveLeafForHandle(params.terminal) + const isMobile = params.client?.type === 'mobile' + + // Why: the left pane's PTY spawns asynchronously after the tab is created. + // Mobile clients that subscribe before the PTY is ready would get a bare + // scrollback+end with no live stream or phone-fit. Wait for the PTY so + // the subscribe can proceed normally. + if (!leaf?.ptyId && isMobile) { + try { + const ptyId = await runtime.waitForLeafPtyId(params.terminal) + leaf = { ptyId } + } catch { + // PTY wait timed out — fall through to scrollback-only path below + } + } + + if (!leaf?.ptyId) { + const read = await runtime.readTerminal(params.terminal) + emit({ + type: 'scrollback', + lines: read.tail, + truncated: read.truncated, + serialized: undefined, + cols: undefined, + rows: undefined + }) + emit({ type: 'end' }) + return + } + + const ptyId = leaf.ptyId + const clientId = params.client?.id + + // Server-side auto-fit: resize PTY to phone dims before serializing scrollback + if (isMobile && clientId) { + runtime.handleMobileSubscribe(ptyId, clientId, params.viewport) + } + + const read = await runtime.readTerminal(params.terminal) + const serialized = await runtime.serializeTerminalBuffer(ptyId) + const size = runtime.getTerminalSize(ptyId) + const displayMode = runtime.getMobileDisplayMode(ptyId) + emit({ + type: 'scrollback', + lines: read.tail, + truncated: read.truncated, + serialized: serialized?.data, + cols: serialized?.cols ?? size?.cols, + rows: serialized?.rows ?? size?.rows, + displayMode + }) + + await new Promise((resolve) => { + const unsubscribeData = runtime.subscribeToTerminalData(ptyId, (data) => { + emit({ type: 'data', chunk: data }) + }) + + // Inline resize events replace the old fit-override-changed event for + // mobile clients. They include fresh serialized scrollback so the client + // can reinitialize xterm without resubscribing. + const unsubscribeResize = runtime.subscribeToTerminalResize(ptyId, async (event) => { + const fresh = await runtime.serializeTerminalBuffer(ptyId) + emit({ + type: 'resized', + cols: event.cols, + rows: event.rows, + serialized: fresh?.data, + displayMode: event.displayMode, + reason: event.reason + }) + }) + + // Legacy fit-override-changed for non-mobile (desktop) subscribers + const unsubscribeFit = !isMobile + ? runtime.subscribeToFitOverrideChanges(ptyId, (event) => { + emit({ + type: 'fit-override-changed', + mode: event.mode, + cols: event.cols, + rows: event.rows + }) + }) + : () => {} + + const subscriptionId = params.terminal + runtime.registerSubscriptionCleanup(subscriptionId, () => { + unsubscribeData() + unsubscribeResize() + unsubscribeFit() + if (isMobile && clientId) { + runtime.handleMobileUnsubscribe(ptyId, clientId) + } + emit({ type: 'end' }) + resolve() + }) + }) + } + }), + defineMethod({ + name: 'terminal.unsubscribe', + params: TerminalUnsubscribe, + handler: async (params, { runtime }) => { + runtime.cleanupSubscription(params.subscriptionId) + return { unsubscribed: true } + } }) ] diff --git a/src/main/runtime/rpc/methods/worktree.ts b/src/main/runtime/rpc/methods/worktree.ts index 8f5bd832a5e..248d81f9c2b 100644 --- a/src/main/runtime/rpc/methods/worktree.ts +++ b/src/main/runtime/rpc/methods/worktree.ts @@ -28,20 +28,27 @@ const WorktreeCreate = z.object({ .unknown() .transform((v) => (typeof v === 'string' ? v : '')) .pipe(z.string().min(1, 'Missing repo selector')), - name: z - .unknown() - .transform((v) => (typeof v === 'string' ? v : '')) - .pipe(z.string().min(1, 'Missing worktree name')), + name: OptionalString, baseBranch: OptionalString, linkedIssue: TriStateLinkedIssue, comment: OptionalString, - runHooks: OptionalBoolean + runHooks: OptionalBoolean, + setupDecision: z + .unknown() + .transform((v) => + typeof v === 'string' && (v === 'run' || v === 'skip' || v === 'inherit') ? v : undefined + ) + .pipe(z.enum(['run', 'skip', 'inherit']).optional()), + // Why: mobile clients pass a startup command (e.g. 'claude') so the first + // terminal pane launches the selected agent instead of an idle shell. + startupCommand: OptionalString }) const WorktreeSet = WorktreeSelector.extend({ displayName: OptionalString, linkedIssue: TriStateLinkedIssue, - comment: OptionalString + comment: OptionalString, + isPinned: OptionalBoolean }) const WorktreeRemove = WorktreeSelector.extend({ @@ -67,17 +74,29 @@ export const WORKTREE_METHODS: RpcMethod[] = [ worktree: await runtime.showManagedWorktree(params.worktree) }) }), + defineMethod({ + name: 'worktree.sleep', + params: WorktreeSelector, + handler: async (params, { runtime }) => runtime.sleepManagedWorktree(params.worktree) + }), + defineMethod({ + name: 'worktree.activate', + params: WorktreeSelector, + handler: async (params, { runtime }) => runtime.activateManagedWorktree(params.worktree) + }), defineMethod({ name: 'worktree.create', params: WorktreeCreate, handler: async (params, { runtime }) => runtime.createManagedWorktree({ repoSelector: params.repo, - name: params.name, + name: params.name ?? '', baseBranch: params.baseBranch, linkedIssue: params.linkedIssue, comment: params.comment, - runHooks: params.runHooks === true + runHooks: params.runHooks === true, + setupDecision: params.setupDecision, + startup: params.startupCommand ? { command: params.startupCommand } : undefined }) }), defineMethod({ @@ -87,7 +106,8 @@ export const WORKTREE_METHODS: RpcMethod[] = [ worktree: await runtime.updateManagedWorktreeMeta(params.worktree, { displayName: params.displayName, linkedIssue: params.linkedIssue, - comment: params.comment + comment: params.comment, + isPinned: params.isPinned }) }) }), diff --git a/src/main/runtime/rpc/streaming.test.ts b/src/main/runtime/rpc/streaming.test.ts new file mode 100644 index 00000000000..7afa5ae7553 --- /dev/null +++ b/src/main/runtime/rpc/streaming.test.ts @@ -0,0 +1,266 @@ +import { describe, expect, it, vi } from 'vitest' +import { z } from 'zod' +import { RpcDispatcher } from './dispatcher' +import { defineMethod, defineStreamingMethod, type RpcRequest } from './core' +import type { OrcaRuntimeService } from '../orca-runtime' + +function stubRuntime(overrides: Partial = {}): OrcaRuntimeService { + return { + getRuntimeId: () => 'test-runtime', + ...overrides + } as OrcaRuntimeService +} + +function makeRequest(method: string, params?: unknown): RpcRequest { + return { id: 'req-1', authToken: 'tok', method, params } +} + +describe('RpcDispatcher streaming', () => { + it('sends initial scrollback via emit', async () => { + const messages: string[] = [] + const dispatcher = new RpcDispatcher({ + runtime: stubRuntime({ + readTerminal: vi.fn().mockResolvedValue({ tail: 'hello\nworld\n', truncated: false }), + resolveLeafForHandle: vi.fn().mockReturnValue({ ptyId: null }) + }), + methods: [ + defineStreamingMethod({ + name: 'terminal.subscribe', + params: z.object({ terminal: z.string() }), + handler: async (params, { runtime }, emit) => { + const read = await (runtime as OrcaRuntimeService).readTerminal(params.terminal) + emit({ type: 'scrollback', lines: read.tail, truncated: read.truncated }) + + const leaf = (runtime as OrcaRuntimeService).resolveLeafForHandle(params.terminal) + if (!leaf?.ptyId) { + emit({ type: 'end' }) + } + } + }) + ] + }) + + await dispatcher.dispatchStreaming( + makeRequest('terminal.subscribe', { terminal: 'h-1' }), + (msg) => messages.push(msg) + ) + + expect(messages).toHaveLength(2) + const scrollback = JSON.parse(messages[0]!) + expect(scrollback).toMatchObject({ + ok: true, + streaming: true, + result: { type: 'scrollback', lines: 'hello\nworld\n', truncated: false } + }) + const end = JSON.parse(messages[1]!) + expect(end).toMatchObject({ + ok: true, + streaming: true, + result: { type: 'end' } + }) + }) + + it('streams live data chunks via emit', async () => { + const messages: string[] = [] + let emitFn: ((result: unknown) => void) | null = null + let resolveHandler: (() => void) | null = null + + const dispatcher = new RpcDispatcher({ + runtime: stubRuntime(), + methods: [ + defineStreamingMethod({ + name: 'test.stream', + params: null, + handler: async (_params, _ctx, emit) => { + emitFn = emit + await new Promise((resolve) => { + resolveHandler = resolve + }) + } + }) + ] + }) + + const dispatchPromise = dispatcher.dispatchStreaming(makeRequest('test.stream'), (msg) => + messages.push(msg) + ) + + // Wait for handler to capture emit + await vi.waitFor(() => expect(emitFn).not.toBeNull()) + + emitFn!({ type: 'data', chunk: 'line 1\n' }) + emitFn!({ type: 'data', chunk: 'line 2\n' }) + emitFn!({ type: 'end' }) + resolveHandler!() + + await dispatchPromise + + expect(messages).toHaveLength(3) + expect(JSON.parse(messages[0]!)).toMatchObject({ + streaming: true, + result: { type: 'data', chunk: 'line 1\n' } + }) + expect(JSON.parse(messages[1]!)).toMatchObject({ + streaming: true, + result: { type: 'data', chunk: 'line 2\n' } + }) + expect(JSON.parse(messages[2]!)).toMatchObject({ + streaming: true, + result: { type: 'end' } + }) + }) + + it('unsubscribe stops further streaming', async () => { + const messages: string[] = [] + let cleanup: (() => void) | null = null + + const dispatcher = new RpcDispatcher({ + runtime: stubRuntime({ + registerSubscriptionCleanup: vi.fn().mockImplementation((_id: string, fn: () => void) => { + cleanup = fn + }), + cleanupSubscription: vi.fn().mockImplementation(() => { + cleanup?.() + }) + }), + methods: [ + defineStreamingMethod({ + name: 'test.subscribe', + params: null, + handler: async (_params, { runtime }, emit) => { + emit({ type: 'scrollback', lines: '' }) + + await new Promise((resolve) => { + ;(runtime as OrcaRuntimeService).registerSubscriptionCleanup('sub-1', () => { + emit({ type: 'end' }) + resolve() + }) + }) + } + }), + defineMethod({ + name: 'test.unsubscribe', + params: z.object({ subscriptionId: z.string() }), + handler: async (params, { runtime }) => { + ;(runtime as OrcaRuntimeService).cleanupSubscription(params.subscriptionId) + return { unsubscribed: true } + } + }) + ] + }) + + const subPromise = dispatcher.dispatchStreaming(makeRequest('test.subscribe'), (msg) => + messages.push(msg) + ) + + await vi.waitFor(() => expect(cleanup).not.toBeNull()) + + const unsubMessages: string[] = [] + await dispatcher.dispatchStreaming( + { + id: 'req-unsub', + authToken: 'tok', + method: 'test.unsubscribe', + params: { subscriptionId: 'sub-1' } + }, + (msg) => unsubMessages.push(msg) + ) + + await subPromise + + expect(unsubMessages).toHaveLength(1) + expect(JSON.parse(unsubMessages[0]!)).toMatchObject({ + ok: true, + result: { unsubscribed: true } + }) + + const streamMessages = messages.map((m) => JSON.parse(m)) + expect(streamMessages).toContainEqual( + expect.objectContaining({ result: { type: 'scrollback', lines: '' } }) + ) + expect(streamMessages).toContainEqual(expect.objectContaining({ result: { type: 'end' } })) + }) + + it('falls back to one-shot dispatch for non-streaming methods via dispatchStreaming', async () => { + const messages: string[] = [] + const dispatcher = new RpcDispatcher({ + runtime: stubRuntime(), + methods: [ + defineMethod({ + name: 'status.get', + params: null, + handler: async () => ({ status: 'ok' }) + }) + ] + }) + + await dispatcher.dispatchStreaming(makeRequest('status.get'), (msg) => messages.push(msg)) + + expect(messages).toHaveLength(1) + const response = JSON.parse(messages[0]!) + expect(response).toMatchObject({ ok: true, result: { status: 'ok' } }) + expect(response.streaming).toBeUndefined() + }) + + it('returns error for unknown method via dispatchStreaming', async () => { + const messages: string[] = [] + const dispatcher = new RpcDispatcher({ + runtime: stubRuntime(), + methods: [] + }) + + await dispatcher.dispatchStreaming(makeRequest('nonexistent.method'), (msg) => + messages.push(msg) + ) + + expect(messages).toHaveLength(1) + expect(JSON.parse(messages[0]!)).toMatchObject({ + ok: false, + error: { code: 'method_not_found' } + }) + }) + + it('returns error when streaming method is called via one-shot dispatch', async () => { + const dispatcher = new RpcDispatcher({ + runtime: stubRuntime(), + methods: [ + defineStreamingMethod({ + name: 'test.stream', + params: null, + handler: async () => {} + }) + ] + }) + + const response = await dispatcher.dispatch(makeRequest('test.stream')) + + expect(response).toMatchObject({ + ok: false, + error: { code: 'method_not_supported' } + }) + }) + + it('captures handler errors in streaming dispatch', async () => { + const messages: string[] = [] + const dispatcher = new RpcDispatcher({ + runtime: stubRuntime(), + methods: [ + defineStreamingMethod({ + name: 'test.explode', + params: null, + handler: async () => { + throw new Error('boom') + } + }) + ] + }) + + await dispatcher.dispatchStreaming(makeRequest('test.explode'), (msg) => messages.push(msg)) + + expect(messages).toHaveLength(1) + expect(JSON.parse(messages[0]!)).toMatchObject({ + ok: false, + error: { code: 'runtime_error' } + }) + }) +}) diff --git a/src/main/runtime/rpc/transport.ts b/src/main/runtime/rpc/transport.ts new file mode 100644 index 00000000000..8eedd20c0fb --- /dev/null +++ b/src/main/runtime/rpc/transport.ts @@ -0,0 +1,22 @@ +// Why: the transport interface decouples the RPC server from a specific +// transport mechanism (Unix socket, WebSocket, named pipe). Each transport +// owns its own connection lifecycle — the RPC server just binds message +// handling to whatever transports are registered. Individual transports +// override `onMessage` with their own richer signatures (e.g. Unix adds a +// `RpcMessageContext` with an abort signal; WebSocket adds the `ws` handle +// for auth association). Consumers hold a concrete transport type, not +// `RpcTransport`, when they need those extensions. + +// Why: per-message hook bag owned by the Unix transport. `signal` aborts +// when the underlying connection terminates so long-poll handlers can bail +// out. `startKeepalive` is opt-in per request — only long-poll dispatches +// call it, so short RPCs pay no timer overhead. See design doc §3.1. +export type RpcMessageContext = { + signal: AbortSignal + startKeepalive: () => void +} + +export type RpcTransport = { + start(): Promise + stop(): Promise +} diff --git a/src/main/runtime/rpc/unix-socket-transport.ts b/src/main/runtime/rpc/unix-socket-transport.ts new file mode 100644 index 00000000000..50c7d9822ca --- /dev/null +++ b/src/main/runtime/rpc/unix-socket-transport.ts @@ -0,0 +1,203 @@ +// Why: this is the original Unix socket / named pipe transport extracted from +// runtime-rpc.ts. It preserves the exact same behavior: newline-delimited JSON, +// 30s idle timeout, 1MB max message, 32 max connections, chmod 0o600 on Unix. +// It also owns the keepalive timer and per-connection abort signal so the +// server-side handler can cancel long-poll dispatches when the client goes +// away. See design doc §3.1. +import { createServer, type Server, type Socket } from 'net' +import { chmodSync, existsSync, rmSync } from 'fs' +import type { RpcMessageContext, RpcTransport } from './transport' + +const MAX_RUNTIME_RPC_MESSAGE_BYTES = 1024 * 1024 +const RUNTIME_RPC_SOCKET_IDLE_TIMEOUT_MS = 30_000 +const MAX_RUNTIME_RPC_CONNECTIONS = 32 +const DEFAULT_KEEPALIVE_INTERVAL_MS = 10_000 + +export type UnixSocketTransportOptions = { + endpoint: string + kind: 'unix' | 'named-pipe' + // Why: how often to write `{"_keepalive":true}\n` frames while a dispatch + // is pending. Each write resets both the server-side idle timer and, once + // the client honours them, the client-side idle timer. Tests override this + // to avoid waiting 10 s for a frame. + keepaliveIntervalMs?: number +} + +type MessageHandler = ( + msg: string, + reply: (response: string) => void, + context?: RpcMessageContext +) => void + +export class UnixSocketTransport implements RpcTransport { + private readonly endpoint: string + private readonly kind: 'unix' | 'named-pipe' + private readonly keepaliveIntervalMs: number + private server: Server | null = null + private messageHandler: MessageHandler | null = null + + constructor({ endpoint, kind, keepaliveIntervalMs }: UnixSocketTransportOptions) { + this.endpoint = endpoint + this.kind = kind + this.keepaliveIntervalMs = keepaliveIntervalMs ?? DEFAULT_KEEPALIVE_INTERVAL_MS + } + + onMessage(handler: MessageHandler): void { + this.messageHandler = handler + } + + async start(): Promise { + if (this.server) { + return + } + + if (this.kind === 'unix' && existsSync(this.endpoint)) { + rmSync(this.endpoint, { force: true }) + } + + const server = createServer((socket) => { + this.handleConnection(socket) + }) + server.maxConnections = MAX_RUNTIME_RPC_CONNECTIONS + + await new Promise((resolve, reject) => { + server.once('error', reject) + server.listen(this.endpoint, () => { + server.off('error', reject) + resolve() + }) + }) + + if (this.kind === 'unix') { + chmodSync(this.endpoint, 0o600) + } + + this.server = server + } + + async stop(): Promise { + const server = this.server + this.server = null + if (!server) { + return + } + await new Promise((resolve, reject) => { + server.close((error) => { + if (error) { + reject(error) + return + } + resolve() + }) + }) + if (this.kind === 'unix' && existsSync(this.endpoint)) { + rmSync(this.endpoint, { force: true }) + } + } + + private handleConnection(socket: Socket): void { + let buffer = '' + let oversized = false + // Why: each in-flight dispatch registers its own AbortController here so + // `socket.on('close')` can abort them all at once. Keeping the set scoped + // to the connection (rather than a single shared controller) means + // completing one dispatch does not abort any other dispatch still running + // on the same socket — future-proofing for a persistent CLI socket that + // multiplexes sequential requests. + const inflight = new Set() + + socket.setEncoding('utf8') + socket.setNoDelay(true) + socket.setTimeout(RUNTIME_RPC_SOCKET_IDLE_TIMEOUT_MS, () => { + socket.destroy() + }) + socket.on('error', () => { + socket.destroy() + }) + socket.on('close', () => { + for (const ctrl of inflight) { + ctrl.abort() + } + inflight.clear() + }) + socket.on('data', (chunk: string) => { + if (oversized) { + return + } + buffer += chunk + // Why: the Orca runtime lives in Electron main, so it must reject + // oversized local RPC frames instead of letting a local client grow an + // unbounded buffer and stall the app. + if (Buffer.byteLength(buffer, 'utf8') > MAX_RUNTIME_RPC_MESSAGE_BYTES) { + oversized = true + this.messageHandler?.('', (response) => { + socket.write(`${response}\n`) + socket.end() + }) + return + } + let newlineIndex = buffer.indexOf('\n') + while (newlineIndex !== -1) { + const rawMessage = buffer.slice(0, newlineIndex).trim() + buffer = buffer.slice(newlineIndex + 1) + if (rawMessage) { + this.dispatchMessage(socket, rawMessage, inflight) + } + newlineIndex = buffer.indexOf('\n') + } + }) + } + + // Why: the keepalive timer is opt-in per request via `startKeepalive()`. + // Short RPCs never call it and pay no timer overhead; only long-poll + // handlers (e.g. orchestration.check --wait) arm it. See §3.1. + private dispatchMessage( + socket: Socket, + rawMessage: string, + inflight: Set + ): void { + let replied = false + let keepaliveTimer: NodeJS.Timeout | null = null + // Why: per-dispatch AbortController so completing one request does not + // abort any sibling request running on the same connection. The + // connection-level `socket.on('close')` iterates `inflight` to cancel all + // outstanding dispatches at once. + const abortController = new AbortController() + inflight.add(abortController) + + const reply = (response: string): void => { + if (replied) { + return + } + replied = true + inflight.delete(abortController) + if (keepaliveTimer) { + clearInterval(keepaliveTimer) + } + if (!socket.destroyed && socket.writable) { + socket.write(`${response}\n`) + } + } + + const startKeepalive = (): void => { + if (keepaliveTimer || replied) { + return + } + keepaliveTimer = setInterval(() => { + if (replied || socket.destroyed || !socket.writable) { + return + } + socket.write('{"_keepalive":true}\n') + }, this.keepaliveIntervalMs) + // Why: don't hold the process open solely on the keepalive interval. + if (typeof keepaliveTimer.unref === 'function') { + keepaliveTimer.unref() + } + } + + this.messageHandler?.(rawMessage, reply, { + signal: abortController.signal, + startKeepalive + }) + } +} diff --git a/src/main/runtime/rpc/ws-transport.test.ts b/src/main/runtime/rpc/ws-transport.test.ts new file mode 100644 index 00000000000..ea3acd486fb --- /dev/null +++ b/src/main/runtime/rpc/ws-transport.test.ts @@ -0,0 +1,256 @@ +import { mkdtempSync } from 'fs' +import { tmpdir } from 'os' +import { join } from 'path' +import { describe, expect, it, afterEach } from 'vitest' +import WebSocket from 'ws' +import { WebSocketTransport } from './ws-transport' +import { loadOrCreateTlsCertificate } from '../tls-certificate' + +// Why: disable TLS verification for self-signed certs in tests. +process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0' + +function makeTls() { + const userDataPath = mkdtempSync(join(tmpdir(), 'ws-transport-test-')) + return loadOrCreateTlsCertificate(userDataPath) +} + +function findFreePort(): number { + // Why: use port 0 to let the OS assign an available port, but the transport + // needs an explicit port. Use a high random port to minimize collisions. + return 30000 + Math.floor(Math.random() * 20000) +} + +describe('WebSocketTransport', () => { + const transports: WebSocketTransport[] = [] + + afterEach(async () => { + await Promise.all(transports.map((t) => t.stop().catch(() => {}))) + transports.length = 0 + }) + + async function createTransport( + handler?: (msg: string, reply: (response: string) => void) => void + ) { + const tls = makeTls() + const port = findFreePort() + const transport = new WebSocketTransport({ + host: '127.0.0.1', + port, + tlsCert: tls.cert, + tlsKey: tls.key + }) + if (handler) { + transport.onMessage(handler) + } + transports.push(transport) + return { transport, port, tls } + } + + function connectWs(port: number): Promise { + return new Promise((resolve, reject) => { + const ws = new WebSocket(`wss://127.0.0.1:${port}`, { + rejectUnauthorized: false + }) + ws.once('open', () => resolve(ws)) + ws.once('error', reject) + }) + } + + function sendAndReceive(ws: WebSocket, message: string): Promise { + return new Promise((resolve) => { + ws.once('message', (data) => { + resolve(typeof data === 'string' ? data : data.toString('utf-8')) + }) + ws.send(message) + }) + } + + it('starts and stops cleanly', async () => { + const { transport } = await createTransport() + + await transport.start() + await transport.stop() + }) + + it('handles request/response round-trip', async () => { + const { transport, port } = await createTransport((msg, reply) => { + const request = JSON.parse(msg) + reply(JSON.stringify({ id: request.id, ok: true, result: { echo: true } })) + }) + + await transport.start() + + const ws = await connectWs(port) + const response = await sendAndReceive( + ws, + JSON.stringify({ id: 'req-1', method: 'test', deviceToken: 'tok' }) + ) + + expect(JSON.parse(response)).toMatchObject({ + id: 'req-1', + ok: true, + result: { echo: true } + }) + + ws.close() + }) + + it('supports multiple concurrent connections', async () => { + const { transport, port } = await createTransport((msg, reply) => { + const request = JSON.parse(msg) + reply(JSON.stringify({ id: request.id, ok: true })) + }) + + await transport.start() + + const clients = await Promise.all([connectWs(port), connectWs(port), connectWs(port)]) + + const responses = await Promise.all( + clients.map((ws, i) => sendAndReceive(ws, JSON.stringify({ id: `req-${i}`, method: 'test' }))) + ) + + for (let i = 0; i < 3; i++) { + expect(JSON.parse(responses[i]!)).toMatchObject({ id: `req-${i}`, ok: true }) + } + + for (const ws of clients) { + ws.close() + } + }) + + it('multiplexes multiple requests on a single connection', async () => { + const { transport, port } = await createTransport((msg, reply) => { + const request = JSON.parse(msg) + reply(JSON.stringify({ id: request.id, ok: true, result: { method: request.method } })) + }) + + await transport.start() + + const ws = await connectWs(port) + + const r1 = sendAndReceive(ws, JSON.stringify({ id: 'a', method: 'first' })) + const resp1 = JSON.parse(await r1) + expect(resp1).toMatchObject({ id: 'a', result: { method: 'first' } }) + + const r2 = sendAndReceive(ws, JSON.stringify({ id: 'b', method: 'second' })) + const resp2 = JSON.parse(await r2) + expect(resp2).toMatchObject({ id: 'b', result: { method: 'second' } }) + + ws.close() + }) + + it('sends multiple streaming responses via reply callback', async () => { + const { transport, port } = await createTransport((msg, reply) => { + const request = JSON.parse(msg) + reply(JSON.stringify({ id: request.id, ok: true, streaming: true, result: { chunk: 1 } })) + reply(JSON.stringify({ id: request.id, ok: true, streaming: true, result: { chunk: 2 } })) + reply(JSON.stringify({ id: request.id, ok: true, result: { type: 'end' } })) + }) + + await transport.start() + + const ws = await connectWs(port) + const messages: string[] = [] + + await new Promise((resolve) => { + ws.on('message', (data) => { + messages.push(typeof data === 'string' ? data : data.toString('utf-8')) + if (messages.length === 3) { + resolve() + } + }) + ws.send(JSON.stringify({ id: 'stream-1', method: 'terminal.subscribe' })) + }) + + expect(JSON.parse(messages[0]!)).toMatchObject({ streaming: true, result: { chunk: 1 } }) + expect(JSON.parse(messages[1]!)).toMatchObject({ streaming: true, result: { chunk: 2 } }) + expect(JSON.parse(messages[2]!)).toMatchObject({ result: { type: 'end' } }) + + ws.close() + }) + + it('rejects oversized messages by closing the connection', async () => { + const { transport, port } = await createTransport() + + await transport.start() + + const ws = await connectWs(port) + + // Why: ws maxPayload is 1MB — sending >1MB should trigger close. + const oversized = 'x'.repeat(1024 * 1024 + 100) + + await new Promise((resolve) => { + ws.once('close', () => resolve()) + ws.send(oversized) + }) + }) + + it('does not crash when replying to a closed connection', async () => { + let capturedReply: ((response: string) => void) | null = null + + const { transport, port } = await createTransport((_msg, reply) => { + capturedReply = reply + }) + + await transport.start() + + const ws = await connectWs(port) + ws.send(JSON.stringify({ id: 'req-1', method: 'test' })) + + // Why: wait for the handler to capture the reply function. + await new Promise((resolve) => { + const interval = setInterval(() => { + if (capturedReply) { + clearInterval(interval) + resolve() + } + }, 10) + }) + + ws.close() + + // Why: wait for the WebSocket to fully close before trying to reply. + await new Promise((resolve) => setTimeout(resolve, 50)) + + // Should not throw — guards with readyState check. + expect(() => capturedReply!(JSON.stringify({ id: 'req-1', ok: true }))).not.toThrow() + }) + + it('is idempotent on double start', async () => { + const { transport } = await createTransport() + + await transport.start() + await transport.start() + + await transport.stop() + }) + + it('is safe to stop without starting', async () => { + const { transport } = await createTransport() + await transport.stop() + }) + + it('falls back to OS-assigned port when preferred port is in use', async () => { + const { transport: first, port } = await createTransport() + await first.start() + + // Why: second transport requests the same port, which is now occupied. + // It should silently fall back to an OS-assigned port instead of throwing. + const tls = makeTls() + const second = new WebSocketTransport({ + host: '127.0.0.1', + port, + tlsCert: tls.cert, + tlsKey: tls.key + }) + transports.push(second) + + await second.start() + + expect(second.resolvedPort).not.toBe(port) + expect(second.resolvedPort).toBeGreaterThan(0) + + const ws = await connectWs(second.resolvedPort) + ws.close() + }) +}) diff --git a/src/main/runtime/rpc/ws-transport.ts b/src/main/runtime/rpc/ws-transport.ts new file mode 100644 index 00000000000..218bdfe2851 --- /dev/null +++ b/src/main/runtime/rpc/ws-transport.ts @@ -0,0 +1,193 @@ +// Why: the WebSocket transport enables mobile clients to connect to the Orca +// runtime over the local network. When TLS cert/key are provided it uses wss:// +// to prevent passive sniffing; otherwise it falls back to plain ws://. Per-device +// tokens (validated by the message handler in OrcaRuntimeRpcServer) provide auth +// regardless of transport encryption. +import { createServer as createHttpsServer, type Server as HttpsServer } from 'https' +import { createServer as createHttpServer, type Server as HttpServer } from 'http' +import { WebSocketServer, type WebSocket } from 'ws' +import type { RpcTransport } from './transport' + +const MAX_WS_MESSAGE_BYTES = 1024 * 1024 +const MAX_WS_CONNECTIONS = 32 + +export type WebSocketTransportOptions = { + host: string + port: number + tlsCert?: string + tlsKey?: string +} + +export class WebSocketTransport implements RpcTransport { + private readonly host: string + private readonly port: number + private readonly tlsCert: string | undefined + private readonly tlsKey: string | undefined + private httpServer: HttpsServer | HttpServer | null = null + private wss: WebSocketServer | null = null + private messageHandler: + | ((msg: string, reply: (response: string) => void, ws: WebSocket) => void) + | null = null + private connectionCloseHandler: ((clientId: string) => void) | null = null + // Why: maps each WebSocket to the clientId (deviceToken) that authenticated it, + // so ws.on('close') can notify the runtime which mobile client disconnected. + private wsClientIds = new Map() + + constructor({ host, port, tlsCert, tlsKey }: WebSocketTransportOptions) { + this.host = host + this.port = port + this.tlsCert = tlsCert + this.tlsKey = tlsKey + } + + onMessage( + handler: (msg: string, reply: (response: string) => void, ws: WebSocket) => void + ): void { + this.messageHandler = handler + } + + onConnectionClose(handler: (clientId: string) => void): void { + this.connectionCloseHandler = handler + } + + setClientId(ws: WebSocket, clientId: string): void { + this.wsClientIds.set(ws, clientId) + } + + // Why: when port 0 is passed the OS assigns a random available port. The + // runtime metadata and mobile QR code need the real port, so callers read + // it here after start() resolves. + get resolvedPort(): number { + const addr = this.httpServer?.address() + if (addr && typeof addr === 'object') { + return addr.port + } + return this.port + } + + async start(): Promise { + if (this.wss) { + return + } + + // Why: when the preferred port is occupied (e.g. another Orca instance is + // already running), fall back to an OS-assigned port so mobile pairing + // still works. The QR code reads resolvedPort after start, so it will + // advertise the correct port regardless. + let port = this.port + try { + await this.tryListen(port) + } catch (error: unknown) { + if (isEAddressInUse(error) && port !== 0) { + console.warn(`[ws-transport] Port ${port} is in use, falling back to OS-assigned port`) + port = 0 + await this.tryListen(port) + } else { + throw error + } + } + } + + private createHttpServer(): HttpServer | HttpsServer { + return this.tlsCert && this.tlsKey + ? createHttpsServer({ cert: this.tlsCert, key: this.tlsKey }) + : createHttpServer() + } + + // Why: the WebSocketServer is attached only after listen succeeds. If we + // attached it before, the WSS would re-emit the EADDRINUSE error from the + // httpServer as an uncatchable exception, preventing the fallback from working. + private async tryListen(port: number): Promise { + const httpServer = this.createHttpServer() + + await new Promise((resolve, reject) => { + httpServer.once('error', reject) + httpServer.listen(port, this.host, () => { + httpServer.off('error', reject) + resolve() + }) + }) + + const wss = new WebSocketServer({ + server: httpServer, + maxPayload: MAX_WS_MESSAGE_BYTES + }) + + wss.on('connection', (ws) => { + if (wss.clients.size > MAX_WS_CONNECTIONS) { + ws.close(1013, 'Maximum connections reached') + return + } + this.handleConnection(ws) + }) + + this.httpServer = httpServer + this.wss = wss + } + + async stop(): Promise { + const wss = this.wss + const httpServer = this.httpServer + this.wss = null + this.httpServer = null + + if (wss) { + for (const client of wss.clients) { + client.close(1001, 'Server shutting down') + } + wss.close() + } + + if (httpServer) { + await new Promise((resolve, reject) => { + httpServer.close((error) => { + if (error) { + reject(error) + return + } + resolve() + }) + }) + } + } + + // Why: WebSocket connections are long-lived (unlike Unix socket which is + // one-per-request). Multiple requests can be multiplexed on the same + // connection via the RPC `id` field. The transport delegates all auth + // and dispatch logic to the message handler set by OrcaRuntimeRpcServer. + private handleConnection(ws: WebSocket): void { + ws.on('message', (data) => { + const msg = typeof data === 'string' ? data : data.toString('utf-8') + this.messageHandler?.( + msg, + (response) => { + // Why: mobile clients disconnect frequently (backgrounding, network + // switch, phone locked). Guard writes to avoid errors on dead sockets. + if (ws.readyState === ws.OPEN) { + ws.send(response) + } + }, + ws + ) + }) + + // Why: mobile clients disconnect when the phone locks, loses wifi, or + // backgrounds the app. The runtime must clean up connection-scoped state + // (e.g., mobile-fit overrides) to prevent orphaned phone-fit on desktop. + ws.on('close', () => { + const clientId = this.wsClientIds.get(ws) + this.wsClientIds.delete(ws) + if (clientId) { + this.connectionCloseHandler?.(clientId) + } + }) + + ws.on('error', () => { + ws.close() + }) + } +} + +function isEAddressInUse(error: unknown): boolean { + return error instanceof Error && 'code' in error && error.code === 'EADDRINUSE' +} diff --git a/src/main/runtime/runtime-metadata.test.ts b/src/main/runtime/runtime-metadata.test.ts index 01774eca88f..4e93e909dd6 100644 --- a/src/main/runtime/runtime-metadata.test.ts +++ b/src/main/runtime/runtime-metadata.test.ts @@ -26,10 +26,12 @@ describe('runtime metadata', () => { writeRuntimeMetadata(userDataPath, { runtimeId: 'rt_123', pid: 42, - transport: { - kind: 'unix', - endpoint: '/tmp/orca.sock' - }, + transports: [ + { + kind: 'unix', + endpoint: '/tmp/orca.sock' + } + ], authToken: 'secret', startedAt: 100 }) @@ -37,10 +39,12 @@ describe('runtime metadata', () => { expect(readRuntimeMetadata(userDataPath)).toEqual({ runtimeId: 'rt_123', pid: 42, - transport: { - kind: 'unix', - endpoint: '/tmp/orca.sock' - }, + transports: [ + { + kind: 'unix', + endpoint: '/tmp/orca.sock' + } + ], authToken: 'secret', startedAt: 100 }) @@ -53,7 +57,7 @@ describe('runtime metadata', () => { writeRuntimeMetadata(userDataPath, { runtimeId: 'rt_123', pid: 42, - transport: null, + transports: [], authToken: null, startedAt: 100 }) @@ -71,7 +75,7 @@ describe('runtime metadata', () => { writeRuntimeMetadata(userDataPath, { runtimeId: 'rt_owner', pid: 42, - transport: null, + transports: [], authToken: null, startedAt: 100 }) @@ -87,7 +91,7 @@ describe('runtime metadata', () => { writeRuntimeMetadata(userDataPath, { runtimeId: 'rt_replacement', pid: 999, - transport: null, + transports: [], authToken: null, startedAt: 200 }) @@ -109,7 +113,7 @@ describe('runtime metadata', () => { writeRuntimeMetadata(userDataPath, { runtimeId: 'rt_replacement', pid: 42, - transport: null, + transports: [], authToken: null, startedAt: 200 }) @@ -140,10 +144,12 @@ describe('runtime metadata', () => { writeRuntimeMetadata(userDataPath, { runtimeId: 'rt_123', pid: 42, - transport: { - kind: 'unix', - endpoint: '/tmp/orca.sock' - }, + transports: [ + { + kind: 'unix', + endpoint: '/tmp/orca.sock' + } + ], authToken: 'secret', startedAt: 100 }) diff --git a/src/main/runtime/runtime-rpc.test.ts b/src/main/runtime/runtime-rpc.test.ts index 77857c19abd..d07fc93b01b 100644 --- a/src/main/runtime/runtime-rpc.test.ts +++ b/src/main/runtime/runtime-rpc.test.ts @@ -167,8 +167,8 @@ describe('OrcaRuntimeRpcServer', () => { const metadata = readRuntimeMetadata(userDataPath) expect(metadata?.runtimeId).toBe(runtime.getRuntimeId()) expect(metadata?.authToken).toBeTruthy() - expect(metadata?.transport?.endpoint).toBeTruthy() - expect(metadata?.transport).toEqual(server['transport']) + expect(metadata?.transports?.[0]?.endpoint).toBeTruthy() + expect(metadata?.transports).toEqual(server['transports']) await server.stop() expect(readRuntimeMetadata(userDataPath)).toMatchObject({ @@ -215,8 +215,8 @@ describe('OrcaRuntimeRpcServer', () => { await expect(server.start()).rejects.toThrow('write failed') expect(readRuntimeMetadata(userDataPath)).toBeNull() expect(existsSync(endpoint)).toBe(false) - expect(server['transport']).toBeNull() - expect(server['server']).toBeNull() + expect(server['transports']).toEqual([]) + expect(server['activeTransports']).toEqual([]) writeMetadataSpy.mockRestore() }) @@ -229,7 +229,7 @@ describe('OrcaRuntimeRpcServer', () => { await server.start() const metadata = readRuntimeMetadata(userDataPath) - const response = await sendRequest(metadata!.transport!.endpoint, { + const response = await sendRequest(metadata!.transports[0]!.endpoint, { id: 'req_1', authToken: metadata!.authToken, method: 'status.get' @@ -255,7 +255,7 @@ describe('OrcaRuntimeRpcServer', () => { await server.start() const metadata = readRuntimeMetadata(userDataPath) - const response = await sendRequest(metadata!.transport!.endpoint, { + const response = await sendRequest(metadata!.transports[0]!.endpoint, { id: 'req_1', authToken: 'wrong', method: 'status.get' @@ -280,7 +280,7 @@ describe('OrcaRuntimeRpcServer', () => { await server.start() const metadata = readRuntimeMetadata(userDataPath) - const response = await sendRequest(metadata!.transport!.endpoint, { + const response = await sendRequest(metadata!.transports[0]!.endpoint, { authToken: metadata!.authToken, method: 'status.get' }) @@ -336,7 +336,7 @@ describe('OrcaRuntimeRpcServer', () => { await server.start() const metadata = readRuntimeMetadata(userDataPath) - const listResponse = await sendRequest(metadata!.transport!.endpoint, { + const listResponse = await sendRequest(metadata!.transports[0]!.endpoint, { id: 'req_list', authToken: metadata!.authToken, method: 'terminal.list', @@ -360,7 +360,7 @@ describe('OrcaRuntimeRpcServer', () => { ).handle expect(handle).toBeTruthy() - const showResponse = await sendRequest(metadata!.transport!.endpoint, { + const showResponse = await sendRequest(metadata!.transports[0]!.endpoint, { id: 'req_show', authToken: metadata!.authToken, method: 'terminal.show', @@ -373,7 +373,7 @@ describe('OrcaRuntimeRpcServer', () => { ok: true }) - const readResponse = await sendRequest(metadata!.transport!.endpoint, { + const readResponse = await sendRequest(metadata!.transports[0]!.endpoint, { id: 'req_read', authToken: metadata!.authToken, method: 'terminal.read', @@ -386,7 +386,7 @@ describe('OrcaRuntimeRpcServer', () => { ok: true }) - const sendResponse = await sendRequest(metadata!.transport!.endpoint, { + const sendResponse = await sendRequest(metadata!.transports[0]!.endpoint, { id: 'req_send', authToken: metadata!.authToken, method: 'terminal.send', @@ -402,7 +402,7 @@ describe('OrcaRuntimeRpcServer', () => { }) expect(writes).toEqual(['continue', '\r']) - const waitPromise = sendRequest(metadata!.transport!.endpoint, { + const waitPromise = sendRequest(metadata!.transports[0]!.endpoint, { id: 'req_wait', authToken: metadata!.authToken, method: 'terminal.wait', @@ -462,7 +462,7 @@ describe('OrcaRuntimeRpcServer', () => { await server.start() const metadata = readRuntimeMetadata(userDataPath) - const response = await sendRequest(metadata!.transport!.endpoint, { + const response = await sendRequest(metadata!.transports[0]!.endpoint, { id: 'req_ps', authToken: metadata!.authToken, method: 'worktree.ps' @@ -503,7 +503,7 @@ describe('OrcaRuntimeRpcServer', () => { await server.start() const metadata = readRuntimeMetadata(userDataPath) - const response = await sendRequest(metadata!.transport!.endpoint, { + const response = await sendRequest(metadata!.transports[0]!.endpoint, { id: 'req_worktrees', authToken: metadata!.authToken, method: 'worktree.list', @@ -533,7 +533,7 @@ describe('OrcaRuntimeRpcServer', () => { const metadata = readRuntimeMetadata(userDataPath) const response = await new Promise>((resolve, reject) => { - const socket = createConnection(metadata!.transport!.endpoint) + const socket = createConnection(metadata!.transports[0]!.endpoint) let buffer = '' socket.setEncoding('utf8') socket.once('error', reject) @@ -581,7 +581,7 @@ describe('OrcaRuntimeRpcServer', () => { try { const metadata = readRuntimeMetadata(userDataPath) - const session = openFramedSession(metadata!.transport!.endpoint, { + const session = openFramedSession(metadata!.transports[0]!.endpoint, { id: 'req_wait', authToken: metadata!.authToken, method: 'orchestration.check', @@ -621,7 +621,7 @@ describe('OrcaRuntimeRpcServer', () => { try { const metadata = readRuntimeMetadata(userDataPath) - const endpoint = metadata!.transport!.endpoint + const endpoint = metadata!.transports[0]!.endpoint // Fill the cap with two long waits (10s each — we'll kill them). const a = openFramedSession(endpoint, { @@ -681,7 +681,7 @@ describe('OrcaRuntimeRpcServer', () => { try { const metadata = readRuntimeMetadata(userDataPath) - const endpoint = metadata!.transport!.endpoint + const endpoint = metadata!.transports[0]!.endpoint const a = openFramedSession(endpoint, { id: 'req_a', @@ -722,6 +722,73 @@ describe('OrcaRuntimeRpcServer', () => { await server.stop() } }) + + it('does not emit keepalive frames for short RPCs', async () => { + const userDataPath = mkdtempSync(join(tmpdir(), 'orca-runtime-rpc-')) + const runtime = new OrcaRuntimeService() + // Why: a 10ms interval means any frame in the first ~100ms of a short + // RPC would show up; `status.get` returns in <10ms so no keepalive + // should ever fire. Locks in the "keepalive is long-poll-only" invariant + // so a future refactor can't silently re-broaden the timer. + const server = new OrcaRuntimeRpcServer({ + runtime, + userDataPath, + keepaliveIntervalMs: 10 + }) + await server.start() + + try { + const metadata = readRuntimeMetadata(userDataPath) + const session = openFramedSession(metadata!.transports[0]!.endpoint, { + id: 'req_short', + authToken: metadata!.authToken, + method: 'status.get' + }) + await session.done + + const keepalives = session.frames.filter((f) => f._keepalive === true) + const terminals = session.frames.filter((f) => f.ok !== undefined) + expect(terminals).toHaveLength(1) + expect(terminals[0]).toMatchObject({ id: 'req_short', ok: true }) + expect(keepalives).toHaveLength(0) + } finally { + await server.stop() + } + }) + + it('returns an internal_error envelope when the dispatcher throws', async () => { + // Why: handlers are designed to return error envelopes, never to throw, + // but a bug somewhere in the RPC stack (e.g. JSON.stringify choking on + // a response with circular refs) must still produce a terminal frame. + // Without the `.catch` on handleMessage's promise, a throw would leave + // the client hanging until the 30s idle timer and leak the dispatch's + // AbortController in the transport's in-flight set. + const userDataPath = mkdtempSync(join(tmpdir(), 'orca-runtime-rpc-')) + const runtime = new OrcaRuntimeService() + const server = new OrcaRuntimeRpcServer({ runtime, userDataPath }) + await server.start() + + // Force the dispatcher to throw a non-envelope error. + const originalDispatch = server['dispatcher'].dispatch.bind(server['dispatcher']) + server['dispatcher'].dispatch = vi.fn().mockRejectedValue(new Error('boom')) + + try { + const metadata = readRuntimeMetadata(userDataPath) + const response = await sendRequest(metadata!.transports[0]!.endpoint, { + id: 'req_throw', + authToken: metadata!.authToken, + method: 'status.get' + }) + expect(response).toMatchObject({ + id: 'req_throw', + ok: false, + error: { code: 'internal_error', message: 'boom' } + }) + } finally { + server['dispatcher'].dispatch = originalDispatch + await server.stop() + } + }) }) // Why: §6 test for the idempotent + hard-fail schema migration. A broken diff --git a/src/main/runtime/runtime-rpc.ts b/src/main/runtime/runtime-rpc.ts index 70dbd4af1f3..b2e4cf27660 100644 --- a/src/main/runtime/runtime-rpc.ts +++ b/src/main/runtime/runtime-rpc.ts @@ -1,12 +1,11 @@ /* eslint-disable max-lines -- Why: this file is the single security boundary for the bundled CLI — transport setup, auth-token enforcement, admission control, keepalive framing, and orphan-socket sweeping all co-locate deliberately so a reviewer can audit the boundary in one sitting. Splitting this across files would scatter the invariants without reducing complexity. */ // Why: this is the single security boundary for the bundled CLI. It owns -// transport setup (unix socket / named pipe), auth-token enforcement, and -// bootstrap-metadata publication so a running runtime is always discoverable -// via exactly one on-disk file. Method handling lives in `rpc/` so this file -// stays easy to audit in one sitting. +// auth-token enforcement, bootstrap-metadata publication, and transport +// orchestration so a running runtime is always discoverable via exactly +// one on-disk file. Method handling lives in `rpc/` and transport specifics +// live in `rpc/unix-socket-transport.ts` and `rpc/ws-transport.ts`. import { randomBytes } from 'crypto' -import { createServer, type Server, type Socket } from 'net' -import { chmodSync, existsSync, readdirSync, rmSync } from 'fs' +import { readdirSync, rmSync } from 'fs' import { join } from 'path' import type { RuntimeMetadata, RuntimeTransportMetadata } from '../../shared/runtime-bootstrap' import type { OrcaRuntimeService } from './orca-runtime' @@ -14,12 +13,23 @@ import { writeRuntimeMetadata } from './runtime-metadata' import { RpcDispatcher } from './rpc/dispatcher' import type { RpcRequest, RpcResponse } from './rpc/core' import { errorResponse } from './rpc/errors' +import type { RpcMessageContext, RpcTransport } from './rpc/transport' +import { UnixSocketTransport } from './rpc/unix-socket-transport' +import { WebSocketTransport } from './rpc/ws-transport' +import type { WebSocket } from 'ws' +import { DeviceRegistry } from './device-registry' +import { loadOrCreateE2EEKeypair, type E2EEKeypair } from './e2ee-keypair' +import { E2EEChannel } from './rpc/e2ee-channel' + +const DEFAULT_WS_PORT = 6768 type OrcaRuntimeRpcServerOptions = { runtime: OrcaRuntimeService userDataPath: string pid?: number platform?: NodeJS.Platform + enableWebSocket?: boolean + wsPort?: number // Why: test-only overrides for the two time-bound constants below. // Production callers must not pass these — defaults are set by the design // doc (§3.1) and changing them in production would weaken the admission @@ -28,10 +38,6 @@ type OrcaRuntimeRpcServerOptions = { longPollCap?: number } -const MAX_RUNTIME_RPC_MESSAGE_BYTES = 1024 * 1024 -const RUNTIME_RPC_SOCKET_IDLE_TIMEOUT_MS = 30_000 -const MAX_RUNTIME_RPC_CONNECTIONS = 32 - // Why: after 10 s of a pending dispatch we emit a tiny `{"_keepalive":true}` // frame every 10 s until the handler resolves. Each write resets both the // server's own socket idle timer (30 s) and — once §3.1 ships on the client — @@ -69,11 +75,19 @@ export class OrcaRuntimeRpcServer { private readonly userDataPath: string private readonly pid: number private readonly platform: NodeJS.Platform + private readonly enableWebSocket: boolean + private readonly wsPort: number private readonly authToken = randomBytes(24).toString('hex') private readonly keepaliveIntervalMs: number private readonly longPollCap: number - private server: Server | null = null - private transport: RuntimeTransportMetadata | null = null + private deviceRegistry: DeviceRegistry | null = null + private e2eeKeypair: E2EEKeypair | null = null + private tlsFingerprint: string | null = null + private activeTransports: RpcTransport[] = [] + private transports: RuntimeTransportMetadata[] = [] + // Why: each WebSocket connection has its own E2EE channel that manages the + // handshake and encrypt/decrypt lifecycle. Keyed by WebSocket instance. + private e2eeChannels = new Map() // Why: separate from Node's server.maxConnections because we need to count // only long-running dispatches, not every in-flight short RPC. See §3.1 + // §7 risk #2. @@ -84,6 +98,8 @@ export class OrcaRuntimeRpcServer { userDataPath, pid = process.pid, platform = process.platform, + enableWebSocket = false, + wsPort = DEFAULT_WS_PORT, keepaliveIntervalMs = KEEPALIVE_INTERVAL_MS, longPollCap = LONG_POLL_CAP }: OrcaRuntimeRpcServerOptions) { @@ -92,12 +108,35 @@ export class OrcaRuntimeRpcServer { this.userDataPath = userDataPath this.pid = pid this.platform = platform + this.enableWebSocket = enableWebSocket + this.wsPort = wsPort this.keepaliveIntervalMs = keepaliveIntervalMs this.longPollCap = longPollCap } + getDeviceRegistry(): DeviceRegistry | null { + return this.deviceRegistry + } + + getTlsFingerprint(): string | null { + return this.tlsFingerprint + } + + getE2EEPublicKey(): string | null { + return this.e2eeKeypair?.publicKeyB64 ?? null + } + + getE2EEKeypair(): E2EEKeypair | null { + return this.e2eeKeypair + } + + getWebSocketEndpoint(): string | null { + const ws = this.transports.find((t) => t.kind === 'websocket') + return ws?.endpoint ?? null + } + async start(): Promise { - if (this.server) { + if (this.activeTransports.length > 0) { return } @@ -111,82 +150,160 @@ export class OrcaRuntimeRpcServer { sweepOrphanedRuntimeSockets(this.userDataPath, this.pid) } - const transport = createRuntimeTransportMetadata( + const transportMeta = createRuntimeTransportMetadata( this.userDataPath, this.pid, this.platform, this.runtime.getRuntimeId() ) - if (transport.kind === 'unix' && existsSync(transport.endpoint)) { - rmSync(transport.endpoint, { force: true }) - } - const server = createServer((socket) => { - this.handleConnection(socket) + const socketTransport = new UnixSocketTransport({ + endpoint: transportMeta.endpoint, + kind: transportMeta.kind as 'unix' | 'named-pipe', + keepaliveIntervalMs: this.keepaliveIntervalMs }) - server.maxConnections = MAX_RUNTIME_RPC_CONNECTIONS - await new Promise((resolve, reject) => { - server.once('error', reject) - server.listen(transport.endpoint, () => { - server.off('error', reject) - resolve() - }) + // Why: Unix socket transport uses the shared runtime auth token. This is + // the existing security model for CLI connections — the token lives in a + // 0o600-permissioned file on disk. + // Why: the `.catch` guarantees `reply()` always fires even if + // `handleMessage` (or `JSON.stringify` on a pathological response) throws. + // Without it, a throw would leave the client waiting for a terminal frame + // that never arrives AND leak the dispatch's AbortController in the + // transport's in-flight set until the 30 s socket idle timer closes the + // connection. + socketTransport.onMessage((msg, reply, context) => { + void this.handleMessage(msg, context) + .then((response) => { + reply(JSON.stringify(response)) + }) + .catch((error) => { + const message = error instanceof Error ? error.message : String(error) + // Why: best-effort id recovery so the client can correlate the + // error frame to its pending request. A malformed message would + // have been caught by handleMessage and returned an envelope + // instead of throwing, so in practice the id is always present. + let id = 'unknown' + try { + const parsed = JSON.parse(msg) as { id?: unknown } + if (typeof parsed.id === 'string' && parsed.id.length > 0) { + id = parsed.id + } + } catch { + // ignore — fall through with id='unknown' + } + reply(JSON.stringify(this.buildError(id, 'internal_error', message))) + }) }) - if (transport.kind === 'unix') { - chmodSync(transport.endpoint, 0o600) + + await socketTransport.start() + + const activeTransports: RpcTransport[] = [socketTransport] + const transportsMeta: RuntimeTransportMetadata[] = [transportMeta] + + // Why: WebSocket transport is opt-in and starts alongside the Unix socket. + // It uses per-device tokens and E2EE (application-layer encryption via + // tweetnacl) rather than TLS, since React Native can't pin self-signed certs. + if (this.enableWebSocket) { + try { + this.deviceRegistry = new DeviceRegistry(this.userDataPath) + this.e2eeKeypair = loadOrCreateE2EEKeypair(this.userDataPath) + + const wsTransport = new WebSocketTransport({ + host: '0.0.0.0', + port: this.wsPort + }) + + // Why: each WebSocket connection gets an E2EE channel that handles the + // handshake before any RPC messages are processed. The channel decrypts + // inbound messages and encrypts outbound replies transparently. + wsTransport.onMessage((msg, _reply, ws) => { + let channel = this.e2eeChannels.get(ws) + if (!channel) { + channel = new E2EEChannel(ws, { + serverSecretKey: this.e2eeKeypair!.secretKey, + validateToken: (token) => this.deviceRegistry?.validateToken(token) != null, + onReady: (ch) => { + if (ch.deviceToken) { + wsTransport.setClientId(ws, ch.deviceToken) + // Why: mark the device as actually connected so it appears + // in the "Paired Devices" list. Devices that were only + // generated as QR codes but never scanned stay hidden. + const device = this.deviceRegistry?.validateToken(ch.deviceToken) + if (device) { + this.deviceRegistry?.updateLastSeen(device.deviceId) + } + } + }, + onError: (code, reason) => { + this.e2eeChannels.get(ws)?.destroy() + this.e2eeChannels.delete(ws) + ws.close(code, reason) + } + }) + channel.onMessage((plaintext, encryptedReply) => { + void this.handleWebSocketMessage(plaintext, encryptedReply, wsTransport, ws) + }) + this.e2eeChannels.set(ws, channel) + } + channel.handleRawMessage(msg) + }) + + // Why: when a mobile client disconnects, the runtime must clean up + // connection-scoped state like mobile-fit overrides and the E2EE + // channel to prevent orphaned state. + wsTransport.onConnectionClose((clientId) => { + for (const [ws, channel] of this.e2eeChannels) { + if (channel.deviceToken === clientId) { + channel.destroy() + this.e2eeChannels.delete(ws) + break + } + } + this.runtime.onClientDisconnected(clientId) + }) + + await wsTransport.start() + activeTransports.push(wsTransport) + transportsMeta.push({ + kind: 'websocket', + endpoint: `ws://0.0.0.0:${wsTransport.resolvedPort}` + }) + } catch (error) { + // Why: WebSocket transport is supplementary — the runtime must still + // function if it fails to start (e.g., port in use). Log and continue + // with Unix socket only. + console.error('[runtime] Failed to start WebSocket transport:', error) + } } // Why: publish the transport into in-memory state before writing metadata // so the bootstrap file always contains the real endpoint/token pair. The // CLI only discovers the runtime through that file. - this.server = server - this.transport = transport + this.activeTransports = activeTransports + this.transports = transportsMeta try { this.writeMetadata() } catch (error) { // Why: a runtime that cannot publish bootstrap metadata is invisible to - // the `orca` CLI. Close the socket immediately instead of leaving behind - // a live but undiscoverable control plane. - this.server = null - this.transport = null - await new Promise((resolve, reject) => { - server.close((closeError) => { - if (closeError) { - reject(closeError) - return - } - resolve() - }) - }).catch(() => {}) - if (transport.kind === 'unix' && existsSync(transport.endpoint)) { - rmSync(transport.endpoint, { force: true }) - } + // the `orca` CLI. Close all transports immediately instead of leaving + // behind a live but undiscoverable control plane. + this.activeTransports = [] + this.transports = [] + await Promise.all(activeTransports.map((t) => t.stop().catch(() => {}))).catch(() => {}) throw error } } async stop(): Promise { - const server = this.server - const transport = this.transport - this.server = null - this.transport = null - if (!server) { + const transports = this.activeTransports + this.activeTransports = [] + this.transports = [] + if (transports.length === 0) { return } - await new Promise((resolve, reject) => { - server.close((error) => { - if (error) { - reject(error) - return - } - resolve() - }) - }) - if (transport?.kind === 'unix' && existsSync(transport.endpoint)) { - rmSync(transport.endpoint, { force: true }) - } + await Promise.all(transports.map((t) => t.stop())) // Why: we intentionally leave the last metadata file behind instead of // deleting it on shutdown. Shared userData paths can briefly host multiple // Orca processes during restarts, updates, or development, and stale @@ -194,120 +311,53 @@ export class OrcaRuntimeRpcServer { // bootstrap file. } - private handleConnection(socket: Socket): void { - let buffer = '' + // Why: Unix socket messages use one-shot dispatch (single response per + // request) and the shared runtime auth token from the 0o600 metadata file. + // The transport layer owns socket lifecycle, keepalive writes, and the + // per-connection abort signal — this method just parses, auths, and + // dispatches. See design doc §3.1. + private async handleMessage( + rawMessage: string, + context?: RpcMessageContext + ): Promise { + // Why: empty messages are sent by the Unix socket transport layer when a + // client exceeds the max message size. The transport closes the connection + // after this response. + if (!rawMessage) { + return this.buildError('unknown', 'request_too_large', 'RPC request exceeds the maximum size') + } - socket.setEncoding('utf8') - socket.setNoDelay(true) - socket.setTimeout(RUNTIME_RPC_SOCKET_IDLE_TIMEOUT_MS, () => { - socket.destroy() - }) - socket.on('error', () => { - socket.destroy() - }) - socket.on('data', (chunk: string) => { - buffer += chunk - // Why: the Orca runtime lives in Electron main, so it must reject - // oversized local RPC frames instead of letting a local client grow an - // unbounded buffer and stall the app. - if (Buffer.byteLength(buffer, 'utf8') > MAX_RUNTIME_RPC_MESSAGE_BYTES) { - socket.write( - `${JSON.stringify(this.buildError('unknown', 'request_too_large', 'RPC request exceeds the maximum size'))}\n` - ) - socket.end() - return - } - let newlineIndex = buffer.indexOf('\n') - while (newlineIndex !== -1) { - const rawMessage = buffer.slice(0, newlineIndex).trim() - buffer = buffer.slice(newlineIndex + 1) - if (rawMessage) { - void this.handleRequest(socket, rawMessage) - } - newlineIndex = buffer.indexOf('\n') - } - }) - } - - // Why: a single entry point per inbound request so keepalive + long-poll - // admission + AbortController wiring + response write all live in one - // place. See design doc §3.1. - private async handleRequest(socket: Socket, rawMessage: string): Promise { const parsed = this.parseAndAuth(rawMessage) if ('error' in parsed) { - this.safeWrite(socket, `${JSON.stringify(parsed.error)}\n`) - return + return parsed.error } const request = parsed.request // Why: long-poll admission fence. Short RPCs bypass the counter entirely // — it only guards handlers that can block for minutes. See §7 risk #2. const longPoll = isLongPollRequest(request) + if (longPoll && this.activeLongPolls >= this.longPollCap) { + return this.buildError( + request.id, + 'runtime_busy', + 'long-poll capacity reached; retry with backoff' + ) + } if (longPoll) { - if (this.activeLongPolls >= this.longPollCap) { - const busy = this.buildError( - request.id, - 'runtime_busy', - 'long-poll capacity reached; retry with backoff' - ) - this.safeWrite(socket, `${JSON.stringify(busy)}\n`) - socket.end() - return - } this.activeLongPolls += 1 - } - - // Why: `decremented` must guard against double-decrement when both - // `close` and a post-resolve cleanup path fire. `socket.on('close')` is - // the only path that fires for every termination (normal end, destroy, - // idle timer, client kill -9, OS reset), so it carries the decrement. - // Tying it to `.finally` alone would leak a slot any time a client dies - // mid-wait because the inner waitForMessage can keep counting down for - // minutes after the socket is gone. See §3.1 counter-lifecycle. - let decremented = !longPoll - const abortController = new AbortController() - const onClose = (): void => { - if (!decremented) { - decremented = true - this.activeLongPolls = Math.max(0, this.activeLongPolls - 1) - } - abortController.abort() - } - socket.on('close', onClose) - - // Why: for long-poll requests we start a keepalive ticker after 10 s. The - // first frame at 10 s resets both the server's 30 s idle timer and the - // client's configured timeout. Short RPCs never see a keepalive — the - // ticker never fires because the handler resolves first. - let keepaliveTimer: NodeJS.Timeout | null = null - if (longPoll) { - keepaliveTimer = setInterval(() => { - if (socket.writable && !socket.destroyed) { - socket.write('{"_keepalive":true}\n') - } - }, this.keepaliveIntervalMs) - // Why: don't hold the process open solely on the keepalive interval — - // .unref() lets the event loop exit when nothing else is pending. - if (typeof keepaliveTimer.unref === 'function') { - keepaliveTimer.unref() - } + // Why: arm the keepalive timer only for long-polls. Short RPCs never + // touch it so the `setInterval` is never created. See §3.1. + context?.startKeepalive() } try { - const response = await this.dispatcher.dispatch(request, { - signal: longPoll ? abortController.signal : undefined + return await this.dispatcher.dispatch(request, { + signal: longPoll ? context?.signal : undefined }) - if (!socket.destroyed) { - this.safeWrite(socket, `${JSON.stringify(response)}\n`) - } } finally { - if (keepaliveTimer) { - clearInterval(keepaliveTimer) + if (longPoll) { + this.activeLongPolls = Math.max(0, this.activeLongPolls - 1) } - // Why: the close-handler path is still what decrements the counter (the - // socket may be closed by the client before the response write flushes). - // We don't remove the listener here — `once` semantics are handled by - // the boolean guard. } } @@ -335,16 +385,51 @@ export class OrcaRuntimeRpcServer { return { request } } - private safeWrite(socket: Socket, payload: string): void { - if (socket.destroyed || !socket.writable) { + // Why: WebSocket messages go through streaming dispatch which can emit + // multiple responses. Auth uses per-device tokens from the device registry. + private async handleWebSocketMessage( + rawMessage: string, + reply: (response: string) => void, + wsTransport?: WebSocketTransport, + ws?: WebSocket + ): Promise { + let request: RpcRequest + try { + request = JSON.parse(rawMessage) as RpcRequest + } catch { + reply(JSON.stringify(this.buildError('unknown', 'bad_request', 'Invalid JSON request'))) return } - try { - socket.write(payload) - } catch { - // Socket was closed in between the writable check and the write — - // nothing we can do; the client already disconnected. + + if (typeof request.id !== 'string' || request.id.length === 0) { + reply(JSON.stringify(this.buildError('unknown', 'bad_request', 'Missing request id'))) + return } + if (typeof request.method !== 'string' || request.method.length === 0) { + reply(JSON.stringify(this.buildError(request.id, 'bad_request', 'Missing RPC method'))) + return + } + + const token = + typeof (request as Record).deviceToken === 'string' + ? ((request as Record).deviceToken as string) + : null + if (!token) { + reply(JSON.stringify(this.buildError(request.id, 'unauthorized', 'Missing device token'))) + return + } + if (!this.deviceRegistry?.validateToken(token)) { + reply(JSON.stringify(this.buildError(request.id, 'unauthorized', 'Invalid device token'))) + return + } + + // Why: associate the deviceToken with this WebSocket so ws.on('close') + // can notify the runtime which mobile client disconnected. + if (wsTransport && ws) { + wsTransport.setClientId(ws, token) + } + + await this.dispatcher.dispatchStreaming(request, reply) } private buildError(id: string, code: string, message: string): RpcResponse { @@ -355,7 +440,7 @@ export class OrcaRuntimeRpcServer { const metadata: RuntimeMetadata = { runtimeId: this.runtime.getRuntimeId(), pid: this.pid, - transport: this.transport, + transports: this.transports, authToken: this.authToken, startedAt: this.runtime.getStartedAt() } diff --git a/src/main/runtime/tls-certificate.ts b/src/main/runtime/tls-certificate.ts new file mode 100644 index 00000000000..1972d13d16e --- /dev/null +++ b/src/main/runtime/tls-certificate.ts @@ -0,0 +1,63 @@ +// Why: the WebSocket transport uses wss:// with a self-signed TLS certificate +// to prevent passive sniffing of device tokens on shared WiFi networks. The +// cert is generated once on first run and reused across restarts. The mobile +// app pins the certificate fingerprint received during QR pairing. +import { createHash } from 'crypto' +import { execSync } from 'child_process' +import { existsSync, readFileSync, chmodSync } from 'fs' +import { join } from 'path' + +const TLS_CERT_FILENAME = 'orca-tls-cert.pem' +const TLS_KEY_FILENAME = 'orca-tls-key.pem' + +export type TlsCertificate = { + cert: string + key: string + fingerprint: string +} + +export function loadOrCreateTlsCertificate(userDataPath: string): TlsCertificate { + const certPath = join(userDataPath, TLS_CERT_FILENAME) + const keyPath = join(userDataPath, TLS_KEY_FILENAME) + + if (existsSync(certPath) && existsSync(keyPath)) { + const cert = readFileSync(certPath, 'utf-8') + const key = readFileSync(keyPath, 'utf-8') + const fingerprint = computeFingerprint(cert) + if (fingerprint) { + return { cert, key, fingerprint } + } + // Why: if the existing cert is malformed (e.g., from a buggy earlier + // generation), regenerate rather than failing the WebSocket transport. + } + + const keyPath_ = join(userDataPath, TLS_KEY_FILENAME) + const certPath_ = join(userDataPath, TLS_CERT_FILENAME) + + // Why: openssl is available on macOS, Linux, and Windows (via Git Bash). + // Using it avoids hand-rolling ASN.1 DER encoding which is error-prone. + execSync( + `openssl req -new -x509 -newkey ec -pkeyopt ec_paramgen_curve:prime256v1 ` + + `-nodes -days 3650 -subj "/CN=Orca Runtime" ` + + `-keyout "${keyPath_}" -out "${certPath_}" 2>/dev/null` + ) + + chmodSync(keyPath_, 0o600) + chmodSync(certPath_, 0o600) + + const cert = readFileSync(certPath_, 'utf-8') + const key = readFileSync(keyPath_, 'utf-8') + return { cert, key, fingerprint: computeFingerprint(cert)! } +} + +function computeFingerprint(certPem: string): string | null { + const derMatch = certPem.match( + /-----BEGIN CERTIFICATE-----\n([\s\S]+?)\n-----END CERTIFICATE-----/ + ) + if (!derMatch?.[1]) { + return null + } + const der = Buffer.from(derMatch[1].replace(/\n/g, ''), 'base64') + const hash = createHash('sha256').update(der).digest('hex') + return `sha256:${hash}` +} diff --git a/src/main/window/attach-main-window-services.ts b/src/main/window/attach-main-window-services.ts index c5ba117331d..58099648cf4 100644 --- a/src/main/window/attach-main-window-services.ts +++ b/src/main/window/attach-main-window-services.ts @@ -4,7 +4,7 @@ import path from 'node:path' import { app, clipboard, ipcMain, nativeImage, session } from 'electron' import type { BrowserWindow } from 'electron' import type { Store } from '../persistence' -import type { CreateWorktreeResult } from '../../shared/types' +import type { CreateWorktreeResult, WorktreeStartupLaunch } from '../../shared/types' import { ORCA_BROWSER_PARTITION } from '../../shared/constants' import { registerRepoHandlers } from '../ipc/repos' import { registerWorktreeHandlers } from '../ipc/worktrees' @@ -199,56 +199,43 @@ function registerRuntimeWindowLifecycle( runtime: OrcaRuntimeService ): void { runtime.attachWindow(mainWindow.id) - runtime.setNotifier({ - worktreesChanged: (repoId) => { - if (!mainWindow.isDestroyed()) { - mainWindow.webContents.send('worktrees:changed', { repoId }) - } - }, - reposChanged: () => { - if (!mainWindow.isDestroyed()) { - mainWindow.webContents.send('repos:changed') - } - }, - activateWorktree: (repoId, worktreeId, setup?: CreateWorktreeResult['setup']) => { - if (!mainWindow.isDestroyed()) { - mainWindow.webContents.send('ui:activateWorktree', { repoId, worktreeId, setup }) - } - }, - createTerminal: (worktreeId, opts) => { - if (!mainWindow.isDestroyed()) { - mainWindow.webContents.send('ui:createTerminal', { - worktreeId, - command: opts.command, - title: opts.title - }) - } - }, - splitTerminal: (tabId, paneRuntimeId, opts) => { - if (!mainWindow.isDestroyed()) { - mainWindow.webContents.send('ui:splitTerminal', { - tabId, - paneRuntimeId, - direction: opts.direction, - command: opts.command - }) - } - }, - renameTerminal: (tabId, title) => { - if (!mainWindow.isDestroyed()) { - mainWindow.webContents.send('ui:renameTerminal', { tabId, title }) - } - }, - focusTerminal: (tabId, worktreeId) => { - if (!mainWindow.isDestroyed()) { - mainWindow.webContents.send('ui:focusTerminal', { tabId, worktreeId }) - } - }, - closeTerminal: (tabId, paneRuntimeId) => { - if (!mainWindow.isDestroyed()) { - mainWindow.webContents.send('ui:closeTerminal', { tabId, paneRuntimeId }) - } + const send = (channel: string, ...args: unknown[]): void => { + if (!mainWindow.isDestroyed()) { + mainWindow.webContents.send(channel, ...args) } + } + runtime.setNotifier({ + worktreesChanged: (repoId) => send('worktrees:changed', { repoId }), + reposChanged: () => send('repos:changed'), + activateWorktree: ( + repoId, + worktreeId, + setup?: CreateWorktreeResult['setup'], + startup?: WorktreeStartupLaunch + ) => { + send('ui:activateWorktree', { + repoId, + worktreeId, + ...(setup ? { setup } : {}), + ...(startup ? { startup } : {}) + }) + }, + createTerminal: (worktreeId, opts) => + send('ui:createTerminal', { worktreeId, command: opts.command, title: opts.title }), + splitTerminal: (tabId, paneRuntimeId, opts) => { + send('ui:splitTerminal', { + tabId, + paneRuntimeId, + direction: opts.direction, + command: opts.command + }) + }, + renameTerminal: (tabId, title) => send('ui:renameTerminal', { tabId, title }), + focusTerminal: (tabId, worktreeId) => send('ui:focusTerminal', { tabId, worktreeId }), + closeTerminal: (tabId, paneRuntimeId) => send('ui:closeTerminal', { tabId, paneRuntimeId }), + sleepWorktree: (worktreeId) => send('ui:sleepWorktree', { worktreeId }), + terminalFitOverrideChanged: (ptyId, mode, cols, rows) => + send('runtime:terminalFitOverrideChanged', { ptyId, mode, cols, rows }) }) // Why: the runtime must fail closed while the renderer graph is being torn // down or rebuilt, otherwise future CLI calls could act on stale terminal diff --git a/src/preload/api-types.ts b/src/preload/api-types.ts index 3c5551eb3b8..f687cb8d36f 100644 --- a/src/preload/api-types.ts +++ b/src/preload/api-types.ts @@ -58,6 +58,7 @@ import type { Worktree, WorktreeMeta, WorktreeSetupLaunch, + WorktreeStartupLaunch, WorkspaceSessionState } from '../shared/types' import type { @@ -440,6 +441,13 @@ export type PreloadApi = { onData: (callback: (data: { id: string; data: string }) => void) => () => void onReplay: (callback: (data: { id: string; data: string }) => void) => () => void onExit: (callback: (data: { id: string; code: number }) => void) => () => void + onSerializeBufferRequest: ( + callback: (data: { requestId: string; ptyId: string }) => void + ) => () => void + sendSerializedBuffer: ( + requestId: string, + snapshot: { data: string; cols: number; rows: number } | null + ) => void management: PtyManagementApi } feedback: { @@ -863,7 +871,12 @@ export type PreloadApi = { onToggleStatusBar: (callback: () => void) => () => void onExportPdfRequested: (callback: () => void) => () => void onActivateWorktree: ( - callback: (data: { repoId: string; worktreeId: string; setup?: WorktreeSetupLaunch }) => void + callback: (data: { + repoId: string + worktreeId: string + setup?: WorktreeSetupLaunch + startup?: WorktreeStartupLaunch + }) => void ) => () => void onCreateTerminal: ( callback: (data: { worktreeId: string; command?: string; title?: string }) => void @@ -897,6 +910,7 @@ export type PreloadApi = { onCloseTerminal: ( callback: (data: { tabId: string; paneRuntimeId?: number }) => void ) => () => void + onSleepWorktree: (callback: (data: { worktreeId: string }) => void) => () => void onTerminalZoom: (callback: (direction: 'in' | 'out' | 'reset') => void) => () => void readClipboardText: () => Promise saveClipboardImageAsTempFile: () => Promise @@ -922,6 +936,18 @@ export type PreloadApi = { runtime: { syncWindowGraph: (graph: RuntimeSyncWindowGraph) => Promise getStatus: () => Promise + getTerminalFitOverrides: () => Promise< + { ptyId: string; mode: 'mobile-fit'; cols: number; rows: number }[] + > + restoreTerminalFit: (ptyId: string) => Promise<{ restored: boolean }> + onTerminalFitOverrideChanged: ( + callback: (event: { + ptyId: string + mode: 'mobile-fit' | 'desktop-fit' + cols: number + rows: number + }) => void + ) => () => void } rateLimits: { get: () => Promise @@ -1011,6 +1037,22 @@ export type PreloadApi = { }) => void ) => () => void } + mobile: { + listNetworkInterfaces: () => Promise<{ + interfaces: { name: string; address: string }[] + }> + getPairingQR: (args?: { + address?: string + }) => Promise< + | { available: false } + | { available: true; qrDataUrl: string; endpoint: string; deviceId: string } + > + listDevices: () => Promise<{ + devices: { deviceId: string; name: string; pairedAt: number; lastSeenAt: number }[] + }> + revokeDevice: (args: { deviceId: string }) => Promise<{ revoked: boolean }> + isWebSocketReady: () => Promise<{ ready: boolean; endpoint: string | null }> + } } declare global { diff --git a/src/preload/index.ts b/src/preload/index.ts index c568c75a24d..0b4cac85b22 100644 --- a/src/preload/index.ts +++ b/src/preload/index.ts @@ -395,6 +395,24 @@ const api = { return () => ipcRenderer.removeListener('pty:exit', listener) }, + onSerializeBufferRequest: ( + callback: (data: { requestId: string; ptyId: string }) => void + ): (() => void) => { + const listener = ( + _event: Electron.IpcRendererEvent, + data: { requestId: string; ptyId: string } + ) => callback(data) + ipcRenderer.on('pty:serializeBuffer:request', listener) + return () => ipcRenderer.removeListener('pty:serializeBuffer:request', listener) + }, + + sendSerializedBuffer: ( + requestId: string, + snapshot: { data: string; cols: number; rows: number } | null + ): void => { + ipcRenderer.send('pty:serializeBuffer:response', { requestId, snapshot }) + }, + management: { listSessions: () => ipcRenderer.invoke('pty:management:listSessions'), killAll: () => ipcRenderer.invoke('pty:management:killAll'), @@ -1462,6 +1480,7 @@ const api = { repoId: string worktreeId: string setup?: { runnerScriptPath: string; envVars: Record } + startup?: { command: string; env?: Record } }) => void ): (() => void) => { const listener = ( @@ -1470,6 +1489,7 @@ const api = { repoId: string worktreeId: string setup?: { runnerScriptPath: string; envVars: Record } + startup?: { command: string; env?: Record } } ) => callback(data) ipcRenderer.on('ui:activateWorktree', listener) @@ -1558,6 +1578,12 @@ const api = { ipcRenderer.on('ui:closeTerminal', listener) return () => ipcRenderer.removeListener('ui:closeTerminal', listener) }, + onSleepWorktree: (callback: (data: { worktreeId: string }) => void): (() => void) => { + const listener = (_event: Electron.IpcRendererEvent, data: { worktreeId: string }) => + callback(data) + ipcRenderer.on('ui:sleepWorktree', listener) + return () => ipcRenderer.removeListener('ui:sleepWorktree', listener) + }, onTerminalZoom: (callback: (direction: 'in' | 'out' | 'reset') => void): (() => void) => { const listener = (_event: Electron.IpcRendererEvent, direction: 'in' | 'out' | 'reset') => callback(direction) @@ -1673,7 +1699,27 @@ const api = { runtime: { syncWindowGraph: (graph: RuntimeSyncWindowGraph): Promise => ipcRenderer.invoke('runtime:syncWindowGraph', graph), - getStatus: (): Promise => ipcRenderer.invoke('runtime:getStatus') + getStatus: (): Promise => ipcRenderer.invoke('runtime:getStatus'), + getTerminalFitOverrides: (): Promise< + { ptyId: string; mode: 'mobile-fit'; cols: number; rows: number }[] + > => ipcRenderer.invoke('runtime:getTerminalFitOverrides'), + restoreTerminalFit: (ptyId: string): Promise<{ restored: boolean }> => + ipcRenderer.invoke('runtime:restoreTerminalFit', { ptyId }), + onTerminalFitOverrideChanged: ( + callback: (event: { + ptyId: string + mode: 'mobile-fit' | 'desktop-fit' + cols: number + rows: number + }) => void + ): (() => void) => { + const listener = ( + _event: Electron.IpcRendererEvent, + data: { ptyId: string; mode: 'mobile-fit' | 'desktop-fit'; cols: number; rows: number } + ) => callback(data) + ipcRenderer.on('runtime:terminalFitOverrideChanged', listener) + return () => ipcRenderer.removeListener('runtime:terminalFitOverrideChanged', listener) + } }, rateLimits: { @@ -1824,6 +1870,29 @@ const api = { getConfig: () => preloadE2EConfig }, + mobile: { + listNetworkInterfaces: (): Promise<{ + interfaces: { name: string; address: string }[] + }> => ipcRenderer.invoke('mobile:listNetworkInterfaces'), + + getPairingQR: (args?: { + address?: string + }): Promise< + | { available: false } + | { available: true; qrDataUrl: string; endpoint: string; deviceId: string } + > => ipcRenderer.invoke('mobile:getPairingQR', args), + + listDevices: (): Promise<{ + devices: { deviceId: string; name: string; pairedAt: number; lastSeenAt: number }[] + }> => ipcRenderer.invoke('mobile:listDevices'), + + revokeDevice: (args: { deviceId: string }): Promise<{ revoked: boolean }> => + ipcRenderer.invoke('mobile:revokeDevice', args), + + isWebSocketReady: (): Promise<{ ready: boolean; endpoint: string | null }> => + ipcRenderer.invoke('mobile:isWebSocketReady') + }, + agentStatus: { /** Listen for agent status updates forwarded from native hook receivers. */ onSet: ( diff --git a/src/renderer/src/components/settings/ExperimentalPane.tsx b/src/renderer/src/components/settings/ExperimentalPane.tsx index 57c12765ed5..8046ab172db 100644 --- a/src/renderer/src/components/settings/ExperimentalPane.tsx +++ b/src/renderer/src/components/settings/ExperimentalPane.tsx @@ -1,7 +1,7 @@ import { useEffect, useState } from 'react' import { Copy, RotateCw } from 'lucide-react' import { toast } from 'sonner' -import type { GlobalSettings, TuiAgent } from '../../../../shared/types' +import type { GlobalSettings } from '../../../../shared/types' import { Button } from '../ui/button' import { Label } from '../ui/label' import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '../ui/tooltip' @@ -9,32 +9,9 @@ import { useAppStore } from '../../store' import { SearchableSetting } from './SearchableSetting' import { matchesSettingsSearch } from './settings-search' import { EXPERIMENTAL_PANE_SEARCH_ENTRIES } from './experimental-search' -import { AGENT_CATALOG, AgentIcon } from '@/lib/agent-catalog' - -// Why: agents with a per-agent hook-service module under src/main that posts -// status to the shared agent-hooks server. Keep this list in sync with the -// hook-service.ts files — any agent without one will not appear in the inline -// per-workspace-card agent activity list even when the experimental setting -// is on. -const AGENT_DASHBOARD_SUPPORTED_AGENTS: readonly TuiAgent[] = [ - 'claude', - 'codex', - 'gemini', - 'cursor', - 'opencode' -] as const - -// Why: both AGENT_DASHBOARD_SUPPORTED_AGENTS and AGENT_CATALOG are static -// module-level constants, so the resolved {id, label} pairs never change at -// runtime. Computing this inside SupportedAgentsDisclaimer was O(N×M) work on -// every parent re-render — notably on every keystroke in the settings search — -// for a list that can only change at build time. Hoisting it makes the cost -// a one-time module-load expense. -const SUPPORTED_AGENT_ENTRIES: readonly { id: TuiAgent; label: string }[] = - AGENT_DASHBOARD_SUPPORTED_AGENTS.map((id) => { - const entry = AGENT_CATALOG.find((a) => a.id === id) - return { id, label: entry?.label ?? id } - }) +import { MobilePane } from './MobilePane' +import { SupportedAgentsDisclaimer } from './SupportedAgentsDisclaimer' +import { HiddenExperimentalGroup } from './HiddenExperimentalGroup' export { EXPERIMENTAL_PANE_SEARCH_ENTRIES } @@ -91,9 +68,10 @@ export function ExperimentalPane({ const showAgentDashboard = matchesSettingsSearch(searchQuery, [ EXPERIMENTAL_PANE_SEARCH_ENTRIES[0] ]) - const showSidekick = matchesSettingsSearch(searchQuery, [EXPERIMENTAL_PANE_SEARCH_ENTRIES[1]]) + const showMobile = matchesSettingsSearch(searchQuery, [EXPERIMENTAL_PANE_SEARCH_ENTRIES[1]]) + const showSidekick = matchesSettingsSearch(searchQuery, [EXPERIMENTAL_PANE_SEARCH_ENTRIES[2]]) const showOrchestration = matchesSettingsSearch(searchQuery, [ - EXPERIMENTAL_PANE_SEARCH_ENTRIES[2] + EXPERIMENTAL_PANE_SEARCH_ENTRIES[3] ]) const [orchestrationEnabled, setOrchestrationEnabled] = useState(() => { @@ -229,11 +207,71 @@ export function ExperimentalPane({ ) : null} + {showMobile ? ( + +
+
+ +

+ Control Orca from your phone by scanning a QR code. Beta / early preview — + expect bugs and breaking changes. Get started from the{' '} + + . +

+
+ +
+ + {settings.experimentalMobile ? ( +
+ +
+ ) : null} +
+ ) : null} + {showSidekick ? ( @@ -272,7 +310,7 @@ export function ExperimentalPane({
@@ -353,62 +391,3 @@ export function ExperimentalPane({
) } - -function SupportedAgentsDisclaimer(): React.JSX.Element { - return ( -
-
- Supported agents: - {SUPPORTED_AGENT_ENTRIES.map(({ id, label }) => ( - - - {label} - - ))} -
-

- We're currently working on support for more agent CLIs. -

-
- ) -} - -// Why: anything in this group is deliberately unfinished or staff-only. The -// orange treatment (header tint, label colors) is the shared visual signal -// for hidden-experimental items so future entries inherit the same -// affordance without another round of styling decisions. -function HiddenExperimentalGroup(): React.JSX.Element { - return ( -
-
-

- Hidden experimental -

-

- Unlisted toggles for internal testing. Nothing here is supported. -

-
- -
-
- -

- Does nothing today. Reserved as the first slot for hidden experimental options. -

-
- -
-
- ) -} diff --git a/src/renderer/src/components/settings/HiddenExperimentalGroup.tsx b/src/renderer/src/components/settings/HiddenExperimentalGroup.tsx new file mode 100644 index 00000000000..74ab617c988 --- /dev/null +++ b/src/renderer/src/components/settings/HiddenExperimentalGroup.tsx @@ -0,0 +1,37 @@ +import { Label } from '../ui/label' + +// Why: anything in this group is deliberately unfinished or staff-only. The +// orange treatment (header tint, label colors) is the shared visual signal +// for hidden-experimental items so future entries inherit the same +// affordance without another round of styling decisions. +export function HiddenExperimentalGroup(): React.JSX.Element { + return ( +
+
+

+ Hidden experimental +

+

+ Unlisted toggles for internal testing. Nothing here is supported. +

+
+ +
+
+ +

+ Does nothing today. Reserved as the first slot for hidden experimental options. +

+
+ +
+
+ ) +} diff --git a/src/renderer/src/components/settings/MobilePane.tsx b/src/renderer/src/components/settings/MobilePane.tsx new file mode 100644 index 00000000000..2dcc31ee03e --- /dev/null +++ b/src/renderer/src/components/settings/MobilePane.tsx @@ -0,0 +1,244 @@ +import { useCallback, useEffect, useState } from 'react' +import { toast } from 'sonner' +import { Maximize2, RefreshCw, Trash2, Wifi } from 'lucide-react' +import { Button } from '../ui/button' +import { Dialog, DialogContent, DialogHeader, DialogTitle } from '../ui/dialog' +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '../ui/select' +import type { SettingsSearchEntry } from './settings-search' + +export const MOBILE_PANE_SEARCH_ENTRIES: SettingsSearchEntry[] = [ + { + title: 'Mobile Pairing', + description: 'Pair a mobile device by scanning a QR code.', + keywords: ['mobile', 'qr', 'code', 'pair', 'phone', 'scan'] + }, + { + title: 'Connected Devices', + description: 'Manage paired mobile devices.', + keywords: ['mobile', 'devices', 'revoke', 'paired', 'connected'] + }, + { + title: 'Network Interface', + description: 'Choose which network address to use for mobile pairing.', + keywords: ['network', 'interface', 'tailscale', 'vpn', 'overlay', 'ip', 'address', 'wifi'] + } +] + +type PairedDevice = { + deviceId: string + name: string + pairedAt: number + lastSeenAt: number +} + +type NetworkInterface = { + name: string + address: string +} + +export function MobilePane(): React.JSX.Element { + const [qrDataUrl, setQrDataUrl] = useState(null) + const [endpoint, setEndpoint] = useState(null) + const [loading, setLoading] = useState(false) + const [devices, setDevices] = useState([]) + const [qrEnlarged, setQrEnlarged] = useState(false) + const [networkInterfaces, setNetworkInterfaces] = useState([]) + const [selectedAddress, setSelectedAddress] = useState(undefined) + + const loadDevices = useCallback(async () => { + try { + const result = await window.api.mobile.listDevices() + setDevices(result.devices) + } catch { + // Silently fail — device list is non-critical + } + }, []) + + const loadNetworkInterfaces = useCallback(async () => { + try { + const result = await window.api.mobile.listNetworkInterfaces() + setNetworkInterfaces(result.interfaces) + if (result.interfaces.length > 0 && !selectedAddress) { + setSelectedAddress(result.interfaces[0]!.address) + } + } catch { + // Silently fail + } + }, [selectedAddress]) + + const generateQR = useCallback(async () => { + setLoading(true) + try { + const result = await window.api.mobile.getPairingQR( + selectedAddress ? { address: selectedAddress } : undefined + ) + if (result.available) { + setQrDataUrl(result.qrDataUrl) + setEndpoint(result.endpoint) + void loadDevices() + } else { + toast.error('WebSocket transport is not running') + } + } catch { + toast.error('Failed to generate QR code') + } finally { + setLoading(false) + } + }, [loadDevices, selectedAddress]) + + useEffect(() => { + void loadDevices() + void loadNetworkInterfaces() + }, [loadDevices, loadNetworkInterfaces]) + + // Why: after generating a QR code the device only appears once the phone + // actually connects (lastSeenAt > 0). Poll until a new device shows up. + const [deviceCountAtQr, setDeviceCountAtQr] = useState(null) + useEffect(() => { + if (!qrDataUrl) { + setDeviceCountAtQr(null) + return + } + setDeviceCountAtQr(devices.length) + }, [qrDataUrl]) // eslint-disable-line react-hooks/exhaustive-deps + + useEffect(() => { + if (deviceCountAtQr === null || devices.length > deviceCountAtQr) { + return + } + const interval = setInterval(() => void loadDevices(), 3000) + return () => clearInterval(interval) + }, [deviceCountAtQr, devices.length, loadDevices]) + + async function revokeDevice(deviceId: string) { + try { + await window.api.mobile.revokeDevice({ deviceId }) + setDevices((prev) => prev.filter((d) => d.deviceId !== deviceId)) + toast.success('Device revoked') + } catch { + toast.error('Failed to revoke device') + } + } + + function formatInterfaceLabel(iface: NetworkInterface): string { + return `${iface.address} (${iface.name})` + } + + return ( +
+ {/* Network interface selector + generate */} +
+
+ + Network Interface +
+

+ Choose which network address to advertise in the QR code. Use your LAN address for + same-network pairing, or an overlay network address (Tailscale, ZeroTier) for + cross-network access. +

+
+ + +
+
+ + {/* QR code display */} + {qrDataUrl && ( +
+ + {endpoint && {endpoint}} +

+ Scan this code with the Orca mobile app. Each code creates a unique device token. +

+
+ )} + + {/* Paired devices */} +
+

Paired Devices

+ {devices.length === 0 ? ( +

+ {qrDataUrl + ? 'No devices paired yet. Scan the QR code with the Orca mobile app.' + : 'No devices paired yet.'} +

+ ) : ( +
+ {devices.map((device) => ( +
+
+
{device.name}
+
+ Paired {new Date(device.pairedAt).toLocaleDateString()} +
+
+ +
+ ))} +
+ )} + {devices.length > 0 && ( +

+ Revoking a device disconnects it immediately. +

+ )} +
+ + {/* Enlarged QR dialog */} + + + + Scan with Orca Mobile + + {qrDataUrl && ( +
+
+ QR Code for mobile pairing +
+ {endpoint && ( + {endpoint} + )} +
+ )} +
+
+
+ ) +} diff --git a/src/renderer/src/components/settings/Settings.tsx b/src/renderer/src/components/settings/Settings.tsx index 34247e4bbd8..9e1ad0209e0 100644 --- a/src/renderer/src/components/settings/Settings.tsx +++ b/src/renderer/src/components/settings/Settings.tsx @@ -63,6 +63,7 @@ type SettingsNavTarget = | 'ssh' | 'experimental' | 'agents' + | 'mobile' | 'repo' type SettingsNavSection = { diff --git a/src/renderer/src/components/settings/SupportedAgentsDisclaimer.tsx b/src/renderer/src/components/settings/SupportedAgentsDisclaimer.tsx new file mode 100644 index 00000000000..66a609fd0eb --- /dev/null +++ b/src/renderer/src/components/settings/SupportedAgentsDisclaimer.tsx @@ -0,0 +1,50 @@ +import type { TuiAgent } from '../../../../shared/types' +import { AGENT_CATALOG, AgentIcon } from '@/lib/agent-catalog' + +// Why: agents with a per-agent hook-service module under src/main that posts +// status to the shared agent-hooks server. Keep this list in sync with the +// hook-service.ts files — any agent without one will not appear in the inline +// per-workspace-card agent activity list even when the experimental setting +// is on. +const AGENT_DASHBOARD_SUPPORTED_AGENTS: readonly TuiAgent[] = [ + 'claude', + 'codex', + 'gemini', + 'cursor', + 'opencode' +] as const + +// Why: both AGENT_DASHBOARD_SUPPORTED_AGENTS and AGENT_CATALOG are static +// module-level constants, so the resolved {id, label} pairs never change at +// runtime. Computing this inside SupportedAgentsDisclaimer was O(N×M) work on +// every parent re-render — notably on every keystroke in the settings search — +// for a list that can only change at build time. Hoisting it makes the cost +// a one-time module-load expense. +const SUPPORTED_AGENT_ENTRIES: readonly { id: TuiAgent; label: string }[] = + AGENT_DASHBOARD_SUPPORTED_AGENTS.map((id) => { + const entry = AGENT_CATALOG.find((a) => a.id === id) + return { id, label: entry?.label ?? id } + }) + +export function SupportedAgentsDisclaimer(): React.JSX.Element { + return ( +
+
+ Supported agents: + {SUPPORTED_AGENT_ENTRIES.map(({ id, label }) => ( + + + {label} + + ))} +
+

+ We're currently working on support for more agent CLIs. +

+
+ ) +} diff --git a/src/renderer/src/components/settings/experimental-search.ts b/src/renderer/src/components/settings/experimental-search.ts index 8f0b79c3b60..9066f38794d 100644 --- a/src/renderer/src/components/settings/experimental-search.ts +++ b/src/renderer/src/components/settings/experimental-search.ts @@ -21,6 +21,23 @@ export const EXPERIMENTAL_PANE_SEARCH_ENTRIES: SettingsSearchEntry[] = [ 'sidebar' ] }, + { + title: 'Mobile Pairing', + description: + 'Pair a mobile device to control Orca remotely. Experimental — requires the Orca mobile APK from GitHub Releases.', + keywords: [ + 'experimental', + 'mobile', + 'phone', + 'pair', + 'qr', + 'code', + 'scan', + 'remote', + 'android', + 'apk' + ] + }, { title: 'Sidekick', description: 'Floating animated sidekick in the bottom-right corner.', diff --git a/src/renderer/src/components/terminal-pane/TerminalPane.tsx b/src/renderer/src/components/terminal-pane/TerminalPane.tsx index 9d938fc5bb5..06e852651b7 100644 --- a/src/renderer/src/components/terminal-pane/TerminalPane.tsx +++ b/src/renderer/src/components/terminal-pane/TerminalPane.tsx @@ -29,6 +29,12 @@ import { useTerminalPaneLifecycle } from './use-terminal-pane-lifecycle' import { useTerminalPaneContextMenu } from './use-terminal-pane-context-menu' import { useNotificationDispatch } from './use-notification-dispatch' import { connectPanePty } from './pty-connection' +import { + getFitOverrideForPty, + getPaneIdsForPty, + onOverrideChange +} from '@/lib/pane-manager/mobile-fit-overrides' +import { safeFit } from '@/lib/pane-manager/pane-tree-ops' /** Global set of buffer-capture callbacks, one per mounted TerminalPane. * The beforeunload handler in App.tsx invokes every callback to populate @@ -100,6 +106,66 @@ export default function TerminalPane({ const searchStateRef = useRef({ query: '', caseSensitive: false, regex: false }) const [closeConfirmPaneId, setCloseConfirmPaneId] = useState(null) const [terminalError, setTerminalError] = useState(null) + // Why: override state lives in a plain Map for perf (safeFit reads it on + // every resize). This counter forces a re-render when overrides change so + // the mobile-fit banner appears/disappears. When an override is cleared + // (desktop-fit), we also trigger safeFit on affected panes so the terminal + // resizes back to desktop dimensions. + const [, setOverrideTick] = useState(0) + useEffect( + () => + onOverrideChange((event) => { + setOverrideTick((n) => n + 1) + if (event.mode === 'desktop-fit') { + const paneIds = getPaneIdsForPty(event.ptyId) + const manager = managerRef.current + if (!manager) { + return + } + // Why: fitAddon.fit() measures DOM dimensions, so it must run after + // the browser has settled layout. Running synchronously inside the + // IPC callback can produce stale measurements. rAF ensures the DOM + // is ready. The follow-up timeout acts as a safety net: if + // fitAddon.fit() silently threw (its errors are caught), the timeout + // falls back to a direct terminal.resize() using the restored + // dimensions from the runtime. This guarantees xterm exits mobile + // dims even when the DOM-based fit path fails. + const fitAffectedPanes = (): void => { + for (const paneId of paneIds) { + const pane = manager.getPanes().find((p) => p.id === paneId) + if (pane) { + safeFit(pane) + } + } + } + requestAnimationFrame(fitAffectedPanes) + // Why: belt-and-suspenders — if safeFit's fitAddon.fit() threw or + // was a no-op due to stale dimensions, this fallback uses the + // restored cols/rows from the runtime to force the resize. If + // safeFit already succeeded, the terminal is already at the right + // dims and this is a harmless no-op. + setTimeout(() => { + for (const paneId of paneIds) { + const pane = manager.getPanes().find((p) => p.id === paneId) + if (!pane) { + continue + } + safeFit(pane) + // Fallback: if terminal is still at mobile dims, force resize + // using the restored dimensions from the runtime notification. + if ( + event.cols > 0 && + event.rows > 0 && + (pane.terminal.cols !== event.cols || pane.terminal.rows !== event.rows) + ) { + pane.terminal.resize(event.cols, event.rows) + } + } + }, 100) + } + }), + [] + ) // Pane title state — keyed by ephemeral paneId, persisted via titlesByLeafId // in the layout snapshot. Ref keeps persistLayoutSnapshot closures fresh. @@ -1052,6 +1118,61 @@ export default function TerminalPane({ `pane-title-${pane.id}` ) })} + {(managerRef.current?.getPanes() ?? []).map((pane) => { + // Why: pane IDs can collide across tabs (e.g. tab 0 pane 1 and tab 1 + // pane 1). Using the transport's actual ptyId avoids showing banners + // on the wrong pane when IDs overlap. + const ptyId = paneTransportsRef.current.get(pane.id)?.getPtyId() + const override = ptyId ? getFitOverrideForPty(ptyId) : null + if (!override) { + return null + } + return createPortal( +
+ + Terminal resized for phone ({override.cols}×{override.rows}) + + +
, + pane.container, + `mobile-fit-banner-${pane.id}` + ) + })} setCloseConfirmPaneId(null)} diff --git a/src/renderer/src/components/terminal-pane/expand-collapse.ts b/src/renderer/src/components/terminal-pane/expand-collapse.ts index 655841f7402..83ad95e9ce4 100644 --- a/src/renderer/src/components/terminal-pane/expand-collapse.ts +++ b/src/renderer/src/components/terminal-pane/expand-collapse.ts @@ -1,4 +1,5 @@ import type { PaneManager } from '@/lib/pane-manager/pane-manager' +import { safeFit } from '@/lib/pane-manager/pane-tree-ops' type ExpandCollapseState = { expandedPaneIdRef: React.MutableRefObject @@ -107,11 +108,7 @@ export function createExpandCollapseActions(state: ExpandCollapseState) { } const panes = manager.getPanes() for (const p of panes) { - try { - p.fitAddon.fit() - } catch { - /* container may not have dimensions */ - } + safeFit(p) } if (focusActive) { const active = manager.getActivePane() ?? panes[0] diff --git a/src/renderer/src/components/terminal-pane/pty-buffer-serializer.ts b/src/renderer/src/components/terminal-pane/pty-buffer-serializer.ts new file mode 100644 index 00000000000..b5106e7349a --- /dev/null +++ b/src/renderer/src/components/terminal-pane/pty-buffer-serializer.ts @@ -0,0 +1,36 @@ +// Why: mobile terminal streaming needs the exact screen state from the +// desktop's xterm.js instance. This module maintains a global registry of +// serialize functions keyed by ptyId, and handles IPC requests from the +// main process to serialize a specific terminal's buffer. + +type SerializedBuffer = { data: string; cols: number; rows: number } +type SerializeFn = () => SerializedBuffer | null | Promise + +const serializersByPtyId = new Map() +let listenerAttached = false + +export function registerPtySerializer(ptyId: string, serialize: SerializeFn): () => void { + serializersByPtyId.set(ptyId, serialize) + ensureSerializerListener() + return () => { + serializersByPtyId.delete(ptyId) + } +} + +function ensureSerializerListener(): void { + if (listenerAttached) { + return + } + listenerAttached = true + + window.api.pty.onSerializeBufferRequest((request) => { + const serializer = serializersByPtyId.get(request.ptyId) + void Promise.resolve(serializer?.() ?? null) + .then((result) => { + window.api.pty.sendSerializedBuffer(request.requestId, result ?? null) + }) + .catch(() => { + window.api.pty.sendSerializedBuffer(request.requestId, null) + }) + }) +} diff --git a/src/renderer/src/components/terminal-pane/pty-connection.test.ts b/src/renderer/src/components/terminal-pane/pty-connection.test.ts index 1eacee1a982..c51539cd048 100644 --- a/src/renderer/src/components/terminal-pane/pty-connection.test.ts +++ b/src/renderer/src/components/terminal-pane/pty-connection.test.ts @@ -141,6 +141,7 @@ function createPane(paneId: number) { onData: vi.fn(() => ({ dispose: vi.fn() })), onResize: vi.fn(() => ({ dispose: vi.fn() })) }, + container: { dataset: {} }, fitAddon: { fit: vi.fn() } @@ -236,7 +237,8 @@ describe('connectPanePty', () => { }, pty: { signal: vi.fn(), - ackColdRestore: vi.fn() + ackColdRestore: vi.fn(), + onSerializeBufferRequest: vi.fn(() => vi.fn()) }, notifications: { dispatch: vi.fn() diff --git a/src/renderer/src/components/terminal-pane/pty-connection.ts b/src/renderer/src/components/terminal-pane/pty-connection.ts index 71fdaadc712..ce27e9a7b00 100644 --- a/src/renderer/src/components/terminal-pane/pty-connection.ts +++ b/src/renderer/src/components/terminal-pane/pty-connection.ts @@ -9,7 +9,9 @@ import type { PtyConnectResult } from './pty-transport' import { createIpcPtyTransport } from './pty-transport' import { shouldSeedCacheTimerOnInitialTitle } from './cache-timer-seeding' import type { PtyConnectionDeps } from './pty-connection-types' -import { isPaneReplaying, replayIntoTerminal } from './replay-guard' +import { safeFit } from '@/lib/pane-manager/pane-tree-ops' +import { getFitOverrideForPty, bindPanePtyId } from '@/lib/pane-manager/mobile-fit-overrides' +import { isPaneReplaying, replayIntoTerminal, replayIntoTerminalAsync } from './replay-guard' import { paneLeafId, POST_REPLAY_MODE_RESET, @@ -17,6 +19,7 @@ import { } from './layout-serialization' import { warnTerminalLifecycleAnomaly } from './terminal-lifecycle-diagnostics' import { detectDeveloperPermissionHint } from './developer-permission-hints' +import { registerPtySerializer } from './pty-buffer-serializer' const pendingSpawnByPaneKey = new Map>() const developerPermissionHintKeys = new Set() @@ -209,6 +212,8 @@ export function connectPanePty( } const onPtySpawn = (ptyId: string): void => { + bindPanePtyId(pane.id, ptyId, deps.tabId) + pane.container.dataset.ptyId = ptyId deps.syncPanePtyLayoutBinding(pane.id, ptyId) deps.updateTabPtyId(deps.tabId, ptyId) // Spawn completion is when a pane gains a concrete PTY ID. The initial @@ -389,6 +394,14 @@ export function connectPanePty( }) const onResizeDisposable = pane.terminal.onResize(({ cols, rows }) => { + // Why: when a mobile-fit override is active, the PTY is already at the + // correct phone dimensions. Suppress resize forwarding to avoid spurious + // SIGWINCH signals that could cause TUI flicker. Uses the transport's + // ptyId directly to avoid pane ID collisions across tabs. + const currentPtyId = transport.getPtyId() + if (currentPtyId && getFitOverrideForPty(currentPtyId)) { + return + } transport.resize(cols, rows) }) @@ -400,11 +413,7 @@ export function connectPanePty( if (disposed) { return } - try { - pane.fitAddon.fit() - } catch { - /* ignore */ - } + safeFit(pane) const cols = pane.terminal.cols const rows = pane.terminal.rows @@ -551,9 +560,39 @@ export function connectPanePty( startFreshSpawn() return } + bindPanePtyId(pane.id, ptyId, deps.tabId) + pane.container.dataset.ptyId = ptyId deps.syncPanePtyLayoutBinding(pane.id, ptyId) deps.updateTabPtyId(deps.tabId, ptyId) + // Why: mobile terminal streaming needs the exact screen state from + // xterm.js. Register a serializer for this ptyId so the main process + // can request a buffer snapshot when a mobile client subscribes. + const unregisterSerializer = registerPtySerializer(ptyId, async () => { + try { + const pending = deps.pendingWritesRef.current.get(pane.id) + if (pending) { + deps.pendingWritesRef.current.set(pane.id, '') + // Why: hidden/background panes buffer PTY output instead of writing + // to xterm. Mobile snapshots must include that pending output, and + // replay guard prevents xterm query auto-replies from hitting stdin. + await replayIntoTerminalAsync(pane, deps.replayingPanesRef, pending) + } + return { + data: pane.serializeAddon.serialize(), + cols: pane.terminal.cols, + rows: pane.terminal.rows + } + } catch { + return null + } + }) + const origOnDataDisposableDispose = onDataDisposable.dispose.bind(onDataDisposable) + onDataDisposable.dispose = () => { + unregisterSerializer() + origOnDataDisposableDispose() + } + if (connectResult?.coldRestore) { // Why: restoreScrollbackBuffers() already wrote the saved xterm // buffer before this rAF ran. The cold-restore scrollback from @@ -611,7 +650,12 @@ export function connectPanePty( }) } - transport.resize(cols, rows) + // Why: when a mobile-fit override is active, skip sending desktop dims + // to the PTY — the PTY is already at phone dimensions and must stay there. + const reattachPtyId = transport.getPtyId() + if (!reattachPtyId || !getFitOverrideForPty(reattachPtyId)) { + transport.resize(cols, rows) + } // Why: POSIX only delivers SIGWINCH when terminal dimensions actually // change. Sending it explicitly guarantees restored TUIs repaint at // the correct cursor position after snapshot replay. @@ -736,8 +780,17 @@ export function connectPanePty( isSessionOwnedByWorktree(candidateReattachSessionId, deps.worktreeId) ? candidateReattachSessionId : null + const _diagMsg = `pane=${pane.id} tab=${deps.tabId} restored=${restoredPtyId} existing=${existingPtyId} detached=${detachedLivePtyId} reattach=${deferredReattachSessionId} hasTransport=${hasExistingPaneTransport} pendingKey=${pendingSpawnKey}` + console.log(`[pty-connect] ${_diagMsg}`) + ;((globalThis as Record).__ptyConnectDiag ??= [] as string[]) as string[] + ;((globalThis as Record).__ptyConnectDiag as string[]).push(_diagMsg) + if (deferredReattachSessionId) { allowInitialIdleCacheSeed = true + console.log(`[pty-connect] pane=${pane.id} → REATTACH ${deferredReattachSessionId}`) + ;((globalThis as Record).__ptyConnectDiag as string[])?.push( + `pane=${pane.id} → REATTACH` + ) const reattachPromise = transport.connect({ url: '', @@ -771,6 +824,10 @@ export function connectPanePty( startFreshSpawn() }) } else if (detachedLivePtyId) { + console.log(`[pty-connect] pane=${pane.id} → ATTACH detached=${detachedLivePtyId}`) + ;((globalThis as Record).__ptyConnectDiag as string[])?.push( + `pane=${pane.id} → ATTACH ${detachedLivePtyId}` + ) allowInitialIdleCacheSeed = false // Why: surface synchronous attach failures (e.g., the PTY died between // mount and remount, so window.api.pty.resize rejects) through @@ -805,6 +862,10 @@ export function connectPanePty( ? undefined : pendingSpawnByPaneKey.get(pendingSpawnKey) if (pendingSpawn) { + console.log(`[pty-connect] pane=${pane.id} → PENDING SPAWN (waiting on sibling)`) + ;((globalThis as Record).__ptyConnectDiag as string[])?.push( + `pane=${pane.id} → PENDING SPAWN` + ) void pendingSpawn .then((spawnedPtyId) => { if (disposed) { @@ -845,6 +906,10 @@ export function connectPanePty( reportError(err instanceof Error ? err.message : String(err)) }) } else { + console.log(`[pty-connect] pane=${pane.id} → FRESH SPAWN`) + ;((globalThis as Record).__ptyConnectDiag as string[])?.push( + `pane=${pane.id} → FRESH SPAWN` + ) startFreshSpawn() } } diff --git a/src/renderer/src/components/terminal-pane/replay-guard.ts b/src/renderer/src/components/terminal-pane/replay-guard.ts index e949fae3efd..904f2e532e3 100644 --- a/src/renderer/src/components/terminal-pane/replay-guard.ts +++ b/src/renderer/src/components/terminal-pane/replay-guard.ts @@ -53,3 +53,26 @@ export function replayIntoTerminal( } }) } + +export function replayIntoTerminalAsync( + pane: ManagedPane, + replayingPanesRef: ReplayingPanesRef, + data: string +): Promise { + if (!data) { + return Promise.resolve() + } + const map = replayingPanesRef.current + map.set(pane.id, (map.get(pane.id) ?? 0) + 1) + return new Promise((resolve) => { + pane.terminal.write(data, () => { + const remaining = (map.get(pane.id) ?? 1) - 1 + if (remaining <= 0) { + map.delete(pane.id) + } else { + map.set(pane.id, remaining) + } + resolve() + }) + }) +} diff --git a/src/renderer/src/components/terminal-pane/terminal-appearance.ts b/src/renderer/src/components/terminal-pane/terminal-appearance.ts index ddb912d9ba7..b9b87976070 100644 --- a/src/renderer/src/components/terminal-pane/terminal-appearance.ts +++ b/src/renderer/src/components/terminal-pane/terminal-appearance.ts @@ -9,7 +9,8 @@ import { resolveEffectiveTerminalAppearance } from '@/lib/terminal-theme' import { buildFontFamily } from './layout-serialization' -import { captureScrollState, restoreScrollState } from '@/lib/pane-manager/pane-tree-ops' +import { captureScrollState, restoreScrollState, safeFit } from '@/lib/pane-manager/pane-tree-ops' +import { getFitOverrideForPty } from '@/lib/pane-manager/mobile-fit-overrides' import type { PtyTransport } from './pty-transport' import type { EffectiveMacOptionAsAlt } from '@/lib/keyboard-layout/detect-option-as-alt' import { HEX_COLOR_RE } from '../../../../shared/color-validation' @@ -221,13 +222,17 @@ export function applyTerminalAppearance( manager.setPaneLigaturesEnabled(pane.id, ligaturesEnabled) try { const state = captureScrollState(pane.terminal) - pane.fitAddon.fit() + safeFit(pane) restoreScrollState(pane.terminal, state) } catch { /* ignore */ } const transport = paneTransports.get(pane.id) - if (transport?.isConnected()) { + // Why: skip PTY resize when a mobile-fit override is active — the PTY + // is already at the correct phone dimensions and must not be resized + // back to desktop dimensions by an appearance change. + const appearancePtyId = transport?.getPtyId() + if (transport?.isConnected() && (!appearancePtyId || !getFitOverrideForPty(appearancePtyId))) { transport.resize(pane.terminal.cols, pane.terminal.rows) maybePushMode2031Flip(pane.id, appearance.mode, transport, paneMode2031, paneLastThemeMode) } diff --git a/src/renderer/src/components/terminal-pane/useTerminalFontZoom.ts b/src/renderer/src/components/terminal-pane/useTerminalFontZoom.ts index e1c353ab216..902b6561c6a 100644 --- a/src/renderer/src/components/terminal-pane/useTerminalFontZoom.ts +++ b/src/renderer/src/components/terminal-pane/useTerminalFontZoom.ts @@ -1,7 +1,7 @@ import { useEffect } from 'react' import type { PaneManager } from '@/lib/pane-manager/pane-manager' import { dispatchZoomLevelChanged } from '@/lib/zoom-events' -import { captureScrollState, restoreScrollState } from '@/lib/pane-manager/pane-tree-ops' +import { captureScrollState, restoreScrollState, safeFit } from '@/lib/pane-manager/pane-tree-ops' type FontZoomDeps = { isActive: boolean @@ -52,7 +52,7 @@ export function useTerminalFontZoom({ pane.terminal.options.fontSize = nextSize try { const state = captureScrollState(pane.terminal) - pane.fitAddon.fit() + safeFit(pane) restoreScrollState(pane.terminal, state) } catch { /* ignore */ diff --git a/src/renderer/src/hooks/useIpcEvents.test.ts b/src/renderer/src/hooks/useIpcEvents.test.ts index a66728403dc..ae6bf25071f 100644 --- a/src/renderer/src/hooks/useIpcEvents.test.ts +++ b/src/renderer/src/hooks/useIpcEvents.test.ts @@ -163,6 +163,7 @@ describe('useIpcEvents updater integration', () => { onRenameTerminal: () => () => {}, onFocusTerminal: () => () => {}, onCloseTerminal: () => () => {}, + onSleepWorktree: () => () => {}, onNewBrowserTab: () => () => {}, onRequestTabCreate: () => () => {}, replyTabCreate: () => {}, @@ -200,6 +201,10 @@ describe('useIpcEvents updater integration', () => { get: () => Promise.resolve({ limits: {}, lastUpdatedAt: Date.now() }), onUpdate: () => () => {} }, + runtime: { + getTerminalFitOverrides: () => Promise.resolve([]), + onTerminalFitOverrideChanged: () => () => {} + }, ssh: { listTargets: () => Promise.resolve([]), listPortForwards: () => Promise.resolve([]), @@ -354,6 +359,7 @@ describe('useIpcEvents updater integration', () => { onRenameTerminal: () => () => {}, onFocusTerminal: () => () => {}, onCloseTerminal: () => () => {}, + onSleepWorktree: () => () => {}, onNewBrowserTab: () => () => {}, onRequestTabCreate: () => () => {}, replyTabCreate: () => {}, @@ -388,6 +394,10 @@ describe('useIpcEvents updater integration', () => { get: () => Promise.resolve({ limits: {}, lastUpdatedAt: Date.now() }), onUpdate: () => () => {} }, + runtime: { + getTerminalFitOverrides: () => Promise.resolve([]), + onTerminalFitOverrideChanged: () => () => {} + }, ssh: { listTargets: () => Promise.resolve([]), listPortForwards: () => Promise.resolve([]), @@ -544,6 +554,7 @@ describe('useIpcEvents updater integration', () => { onRenameTerminal: () => () => {}, onFocusTerminal: () => () => {}, onCloseTerminal: () => () => {}, + onSleepWorktree: () => () => {}, onNewBrowserTab: () => () => {}, onRequestTabCreate: () => () => {}, replyTabCreate: () => {}, @@ -578,6 +589,10 @@ describe('useIpcEvents updater integration', () => { get: () => Promise.resolve({ limits: {}, lastUpdatedAt: Date.now() }), onUpdate: () => () => {} }, + runtime: { + getTerminalFitOverrides: () => Promise.resolve([]), + onTerminalFitOverrideChanged: () => () => {} + }, ssh: { listTargets: () => Promise.resolve([]), listPortForwards: () => Promise.resolve([]), @@ -736,6 +751,7 @@ describe('useIpcEvents browser tab close routing', () => { onRenameTerminal: () => () => {}, onFocusTerminal: () => () => {}, onCloseTerminal: () => () => {}, + onSleepWorktree: () => () => {}, onNewBrowserTab: () => () => {}, onRequestTabCreate: () => () => {}, replyTabCreate: () => {}, @@ -790,6 +806,10 @@ describe('useIpcEvents browser tab close routing', () => { onDetectedPortsChanged: () => () => {}, onCredentialResolved: () => () => {} }, + runtime: { + getTerminalFitOverrides: () => Promise.resolve([]), + onTerminalFitOverrideChanged: () => () => {} + }, agentStatus: { onSet: () => () => {} } } }) @@ -922,6 +942,7 @@ describe('useIpcEvents browser tab close routing', () => { onRenameTerminal: () => () => {}, onFocusTerminal: () => () => {}, onCloseTerminal: () => () => {}, + onSleepWorktree: () => () => {}, onNewBrowserTab: () => () => {}, onRequestTabCreate: () => () => {}, replyTabCreate: () => {}, @@ -976,6 +997,10 @@ describe('useIpcEvents browser tab close routing', () => { onDetectedPortsChanged: () => () => {}, onCredentialResolved: () => () => {} }, + runtime: { + getTerminalFitOverrides: () => Promise.resolve([]), + onTerminalFitOverrideChanged: () => () => {} + }, agentStatus: { onSet: () => () => {} } } }) @@ -1103,6 +1128,7 @@ describe('useIpcEvents browser tab close routing', () => { onRenameTerminal: () => () => {}, onFocusTerminal: () => () => {}, onCloseTerminal: () => () => {}, + onSleepWorktree: () => () => {}, onNewBrowserTab: () => () => {}, onRequestTabCreate: () => () => {}, replyTabCreate: () => {}, @@ -1157,6 +1183,10 @@ describe('useIpcEvents browser tab close routing', () => { onDetectedPortsChanged: () => () => {}, onCredentialResolved: () => () => {} }, + runtime: { + getTerminalFitOverrides: () => Promise.resolve([]), + onTerminalFitOverrideChanged: () => () => {} + }, agentStatus: { onSet: () => () => {} } } }) @@ -1302,6 +1332,7 @@ describe('useIpcEvents shortcut hint clearing', () => { onRenameTerminal: () => () => {}, onFocusTerminal: () => () => {}, onCloseTerminal: () => () => {}, + onSleepWorktree: () => () => {}, onNewBrowserTab: () => () => {}, onRequestTabCreate: () => () => {}, replyTabCreate: () => {}, @@ -1336,6 +1367,10 @@ describe('useIpcEvents shortcut hint clearing', () => { get: () => Promise.resolve({ limits: {}, lastUpdatedAt: Date.now() }), onUpdate: () => () => {} }, + runtime: { + getTerminalFitOverrides: () => Promise.resolve([]), + onTerminalFitOverrideChanged: () => () => {} + }, ssh: { listTargets: () => Promise.resolve([]), listPortForwards: () => Promise.resolve([]), @@ -1499,6 +1534,7 @@ describe('useIpcEvents CLI-created worktree activation', () => { onRenameTerminal: () => () => {}, onFocusTerminal: () => () => {}, onCloseTerminal: () => () => {}, + onSleepWorktree: () => () => {}, onNewBrowserTab: () => () => {}, onRequestTabCreate: () => () => {}, replyTabCreate: () => {}, @@ -1544,6 +1580,10 @@ describe('useIpcEvents CLI-created worktree activation', () => { onDetectedPortsChanged: () => () => {}, onCredentialResolved: () => () => {} }, + runtime: { + getTerminalFitOverrides: () => Promise.resolve([]), + onTerminalFitOverrideChanged: () => () => {} + }, agentStatus: { onSet: () => () => {} } } }) diff --git a/src/renderer/src/hooks/useIpcEvents.ts b/src/renderer/src/hooks/useIpcEvents.ts index 6bae8766f71..2a306409743 100644 --- a/src/renderer/src/hooks/useIpcEvents.ts +++ b/src/renderer/src/hooks/useIpcEvents.ts @@ -3,6 +3,7 @@ import { useEffect } from 'react' import { useAppStore } from '../store' import { applyUIZoom } from '@/lib/ui-zoom' import { activateAndRevealWorktree } from '@/lib/worktree-activation' +import { runSleepWorktree } from '@/components/sidebar/sleep-worktree-flow' import { SPLIT_TERMINAL_PANE_EVENT, CLOSE_TERMINAL_PANE_EVENT } from '@/constants/terminal' import type { SplitTerminalPaneDetail, CloseTerminalPaneDetail } from '@/constants/terminal' import { getVisibleWorktreeIds } from '@/components/sidebar/visible-worktrees' @@ -22,6 +23,7 @@ import { dispatchClearModifierHints } from './useModifierHint' import { normalizeAgentStatusPayload } from '../../../shared/agent-status-types' import { isGitRepoKind } from '../../../shared/repo-kind' import { focusTerminalTabSurface } from '@/lib/focus-terminal-tab-surface' +import { setFitOverride, hydrateOverrides } from '@/lib/pane-manager/mobile-fit-overrides' export { resolveZoomTarget } from './resolve-zoom-target' @@ -195,7 +197,7 @@ export function useIpcEvents(): void { ) unsubs.push( - window.api.ui.onActivateWorktree(({ repoId, worktreeId, setup }) => { + window.api.ui.onActivateWorktree(({ repoId, worktreeId, setup, startup }) => { void (async () => { // Why: fetch worktrees first so the activation helper can resolve // the CLI-created worktree via findWorktreeById — it arrived from @@ -206,7 +208,7 @@ export function useIpcEvents(): void { // ones. This records the visit in the back/forward history stack // (recordWorktreeVisit), without which the nav buttons would // ignore the CLI-driven workspace switch. - activateAndRevealWorktree(worktreeId, { setup }) + activateAndRevealWorktree(worktreeId, { setup, startup }) })().catch((error) => { console.error('Failed to activate CLI-created worktree:', error) }) @@ -320,6 +322,12 @@ export function useIpcEvents(): void { }) ) + unsubs.push( + window.api.ui.onSleepWorktree(({ worktreeId }) => { + void runSleepWorktree(worktreeId) + }) + ) + // Hydrate initial update status then subscribe to changes window.api.updater.getStatus().then((status) => { useAppStore.getState().setUpdateStatus(status as UpdateStatus) @@ -812,6 +820,18 @@ export function useIpcEvents(): void { }) ) + // Why: hydrate mobile-fit overrides before terminal panes run their first + // attach/fit logic, so a renderer reload doesn't undo active mobile fits. + void window.api.runtime.getTerminalFitOverrides().then((overrides) => { + hydrateOverrides(overrides) + }) + + unsubs.push( + window.api.runtime.onTerminalFitOverrideChanged((event) => { + setFitOverride(event.ptyId, event.mode, event.cols, event.rows) + }) + ) + return () => unsubs.forEach((fn) => fn()) }, []) } diff --git a/src/renderer/src/lib/pane-manager/mobile-fit-overrides.test.ts b/src/renderer/src/lib/pane-manager/mobile-fit-overrides.test.ts new file mode 100644 index 00000000000..66a4cf4cf28 --- /dev/null +++ b/src/renderer/src/lib/pane-manager/mobile-fit-overrides.test.ts @@ -0,0 +1,409 @@ +import { afterEach, describe, expect, it, vi } from 'vitest' +import { + setFitOverride, + getFitOverrideForPty, + getFitOverrideForPane, + bindPanePtyId, + unbindPane, + getPaneIdsForPty, + onOverrideChange, + hydrateOverrides, + getAllOverrides +} from './mobile-fit-overrides' + +afterEach(() => { + // Reset module-level maps between tests by clearing all overrides + // and unbinding all known panes. + hydrateOverrides([]) + // Unbind any panes bound during tests. We don't have direct access + // to the internal map, but we can unbind known test keys. + for (const tabId of ['tab-0', 'tab-1', 'tab-2']) { + for (let paneId = 0; paneId < 5; paneId++) { + unbindPane(paneId, tabId) + } + } +}) + +// --------------------------------------------------------------------------- +// setFitOverride + getFitOverrideForPty +// --------------------------------------------------------------------------- + +describe('setFitOverride / getFitOverrideForPty', () => { + it('stores a mobile-fit override keyed by ptyId', () => { + setFitOverride('pty-1', 'mobile-fit', 49, 20) + + const override = getFitOverrideForPty('pty-1') + expect(override).toEqual({ mode: 'mobile-fit', cols: 49, rows: 20 }) + }) + + it('removes the override when mode is desktop-fit', () => { + setFitOverride('pty-1', 'mobile-fit', 49, 20) + setFitOverride('pty-1', 'desktop-fit', 120, 40) + + expect(getFitOverrideForPty('pty-1')).toBeNull() + }) + + it('returns null for unknown ptyId', () => { + expect(getFitOverrideForPty('nonexistent')).toBeNull() + }) + + it('overwrites previous override dimensions', () => { + setFitOverride('pty-1', 'mobile-fit', 49, 20) + setFitOverride('pty-1', 'mobile-fit', 60, 25) + + expect(getFitOverrideForPty('pty-1')).toEqual({ mode: 'mobile-fit', cols: 60, rows: 25 }) + }) + + it('tracks multiple ptyIds independently', () => { + setFitOverride('pty-1', 'mobile-fit', 49, 20) + setFitOverride('pty-2', 'mobile-fit', 80, 30) + + expect(getFitOverrideForPty('pty-1')?.cols).toBe(49) + expect(getFitOverrideForPty('pty-2')?.cols).toBe(80) + }) +}) + +// --------------------------------------------------------------------------- +// bindPanePtyId + getFitOverrideForPane (tab-scoped composite key) +// --------------------------------------------------------------------------- + +describe('bindPanePtyId / getFitOverrideForPane', () => { + it('resolves override through tab:pane → ptyId → override chain', () => { + setFitOverride('pty-1', 'mobile-fit', 49, 20) + bindPanePtyId(1, 'pty-1', 'tab-0') + + expect(getFitOverrideForPane(1, 'tab-0')).toEqual({ mode: 'mobile-fit', cols: 49, rows: 20 }) + }) + + it('returns null when tabId is not provided', () => { + setFitOverride('pty-1', 'mobile-fit', 49, 20) + bindPanePtyId(1, 'pty-1', 'tab-0') + + expect(getFitOverrideForPane(1)).toBeNull() + }) + + it('returns null for unbound pane', () => { + setFitOverride('pty-1', 'mobile-fit', 49, 20) + + expect(getFitOverrideForPane(1, 'tab-0')).toBeNull() + }) + + it('returns null when ptyId has no override', () => { + bindPanePtyId(1, 'pty-1', 'tab-0') + + expect(getFitOverrideForPane(1, 'tab-0')).toBeNull() + }) + + it('does not collide when different tabs have the same pane ID', () => { + setFitOverride('pty-A', 'mobile-fit', 49, 20) + setFitOverride('pty-B', 'mobile-fit', 80, 30) + bindPanePtyId(1, 'pty-A', 'tab-0') + bindPanePtyId(1, 'pty-B', 'tab-1') + + expect(getFitOverrideForPane(1, 'tab-0')?.cols).toBe(49) + expect(getFitOverrideForPane(1, 'tab-1')?.cols).toBe(80) + }) + + it('clears binding when ptyId is null', () => { + bindPanePtyId(1, 'pty-1', 'tab-0') + bindPanePtyId(1, null, 'tab-0') + + setFitOverride('pty-1', 'mobile-fit', 49, 20) + expect(getFitOverrideForPane(1, 'tab-0')).toBeNull() + }) + + it('is a no-op when tabId is not provided', () => { + bindPanePtyId(1, 'pty-1') + setFitOverride('pty-1', 'mobile-fit', 49, 20) + + expect(getFitOverrideForPane(1, 'tab-0')).toBeNull() + }) +}) + +// --------------------------------------------------------------------------- +// unbindPane +// --------------------------------------------------------------------------- + +describe('unbindPane', () => { + it('removes the tab:pane binding', () => { + setFitOverride('pty-1', 'mobile-fit', 49, 20) + bindPanePtyId(1, 'pty-1', 'tab-0') + unbindPane(1, 'tab-0') + + expect(getFitOverrideForPane(1, 'tab-0')).toBeNull() + }) + + it('does not affect other tabs with the same pane ID', () => { + setFitOverride('pty-A', 'mobile-fit', 49, 20) + bindPanePtyId(1, 'pty-A', 'tab-0') + bindPanePtyId(1, 'pty-A', 'tab-1') + + unbindPane(1, 'tab-0') + + expect(getFitOverrideForPane(1, 'tab-0')).toBeNull() + expect(getFitOverrideForPane(1, 'tab-1')).toEqual({ mode: 'mobile-fit', cols: 49, rows: 20 }) + }) + + it('is a no-op when tabId is not provided', () => { + bindPanePtyId(1, 'pty-1', 'tab-0') + unbindPane(1) + + setFitOverride('pty-1', 'mobile-fit', 49, 20) + expect(getFitOverrideForPane(1, 'tab-0')).toEqual({ mode: 'mobile-fit', cols: 49, rows: 20 }) + }) +}) + +// --------------------------------------------------------------------------- +// getPaneIdsForPty +// --------------------------------------------------------------------------- + +describe('getPaneIdsForPty', () => { + it('returns pane IDs bound to a ptyId', () => { + bindPanePtyId(1, 'pty-1', 'tab-0') + bindPanePtyId(2, 'pty-1', 'tab-0') + + const ids = getPaneIdsForPty('pty-1') + expect(ids).toEqual(expect.arrayContaining([1, 2])) + expect(ids).toHaveLength(2) + }) + + it('returns pane IDs across different tabs', () => { + bindPanePtyId(1, 'pty-1', 'tab-0') + bindPanePtyId(1, 'pty-1', 'tab-1') + + const ids = getPaneIdsForPty('pty-1') + expect(ids).toEqual([1, 1]) + }) + + it('returns empty array for unknown ptyId', () => { + expect(getPaneIdsForPty('nonexistent')).toEqual([]) + }) + + it('does not include panes bound to a different ptyId', () => { + bindPanePtyId(1, 'pty-1', 'tab-0') + bindPanePtyId(2, 'pty-2', 'tab-0') + + expect(getPaneIdsForPty('pty-1')).toEqual([1]) + }) +}) + +// --------------------------------------------------------------------------- +// onOverrideChange +// --------------------------------------------------------------------------- + +describe('onOverrideChange', () => { + it('fires listener on mobile-fit override', () => { + const listener = vi.fn() + const unsub = onOverrideChange(listener) + + setFitOverride('pty-1', 'mobile-fit', 49, 20) + + expect(listener).toHaveBeenCalledWith({ + ptyId: 'pty-1', + mode: 'mobile-fit', + cols: 49, + rows: 20 + }) + + unsub() + }) + + it('fires listener on desktop-fit restore', () => { + const listener = vi.fn() + const unsub = onOverrideChange(listener) + + setFitOverride('pty-1', 'desktop-fit', 120, 40) + + expect(listener).toHaveBeenCalledWith({ + ptyId: 'pty-1', + mode: 'desktop-fit', + cols: 120, + rows: 40 + }) + + unsub() + }) + + it('unsubscribes cleanly', () => { + const listener = vi.fn() + const unsub = onOverrideChange(listener) + unsub() + + setFitOverride('pty-1', 'mobile-fit', 49, 20) + + expect(listener).not.toHaveBeenCalled() + }) + + it('supports multiple listeners', () => { + const a = vi.fn() + const b = vi.fn() + const unsubA = onOverrideChange(a) + const unsubB = onOverrideChange(b) + + setFitOverride('pty-1', 'mobile-fit', 49, 20) + + expect(a).toHaveBeenCalledTimes(1) + expect(b).toHaveBeenCalledTimes(1) + + unsubA() + unsubB() + }) +}) + +// --------------------------------------------------------------------------- +// hydrateOverrides +// --------------------------------------------------------------------------- + +describe('hydrateOverrides', () => { + it('replaces all overrides with the given list', () => { + setFitOverride('pty-old', 'mobile-fit', 49, 20) + + hydrateOverrides([{ ptyId: 'pty-new', mode: 'mobile-fit', cols: 60, rows: 25 }]) + + expect(getFitOverrideForPty('pty-old')).toBeNull() + expect(getFitOverrideForPty('pty-new')).toEqual({ mode: 'mobile-fit', cols: 60, rows: 25 }) + }) + + it('clears all overrides when given an empty list', () => { + setFitOverride('pty-1', 'mobile-fit', 49, 20) + + hydrateOverrides([]) + + expect(getFitOverrideForPty('pty-1')).toBeNull() + }) + + it('hydrates multiple overrides', () => { + hydrateOverrides([ + { ptyId: 'pty-1', mode: 'mobile-fit', cols: 49, rows: 20 }, + { ptyId: 'pty-2', mode: 'mobile-fit', cols: 80, rows: 30 } + ]) + + expect(getAllOverrides().size).toBe(2) + expect(getFitOverrideForPty('pty-1')?.cols).toBe(49) + expect(getFitOverrideForPty('pty-2')?.cols).toBe(80) + }) +}) + +// --------------------------------------------------------------------------- +// getAllOverrides +// --------------------------------------------------------------------------- + +describe('getAllOverrides', () => { + it('returns a copy of all current overrides', () => { + setFitOverride('pty-1', 'mobile-fit', 49, 20) + setFitOverride('pty-2', 'mobile-fit', 80, 30) + + const all = getAllOverrides() + expect(all.size).toBe(2) + + // Verify it's a copy, not the internal map + all.delete('pty-1') + expect(getFitOverrideForPty('pty-1')).not.toBeNull() + }) +}) + +// --------------------------------------------------------------------------- +// Scenario tests (from design doc verification matrix) +// --------------------------------------------------------------------------- + +describe('scenario: desktop window resize while mobile is viewing', () => { + it('override persists across setFitOverride calls — desktop safeFit will see it', () => { + setFitOverride('pty-1', 'mobile-fit', 49, 20) + bindPanePtyId(1, 'pty-1', 'tab-0') + + // Simulate desktop resize triggering a re-check — override should still be there + expect(getFitOverrideForPty('pty-1')).toEqual({ mode: 'mobile-fit', cols: 49, rows: 20 }) + expect(getFitOverrideForPane(1, 'tab-0')).toEqual({ mode: 'mobile-fit', cols: 49, rows: 20 }) + }) +}) + +describe('scenario: mobile disconnect restores all terminals', () => { + it('clearing all overrides for a disconnected client removes all traces', () => { + setFitOverride('pty-1', 'mobile-fit', 49, 20) + setFitOverride('pty-2', 'mobile-fit', 49, 20) + bindPanePtyId(1, 'pty-1', 'tab-0') + bindPanePtyId(2, 'pty-2', 'tab-0') + + // Simulate runtime clearing overrides on disconnect + setFitOverride('pty-1', 'desktop-fit', 120, 40) + setFitOverride('pty-2', 'desktop-fit', 120, 40) + + expect(getFitOverrideForPty('pty-1')).toBeNull() + expect(getFitOverrideForPty('pty-2')).toBeNull() + expect(getFitOverrideForPane(1, 'tab-0')).toBeNull() + expect(getFitOverrideForPane(2, 'tab-0')).toBeNull() + }) +}) + +describe('scenario: PTY exits while phone-fitted', () => { + it('clearing override for exited PTY leaves no stale state', () => { + setFitOverride('pty-1', 'mobile-fit', 49, 20) + bindPanePtyId(1, 'pty-1', 'tab-0') + + // Runtime clears override on PTY exit + setFitOverride('pty-1', 'desktop-fit', 0, 0) + unbindPane(1, 'tab-0') + + expect(getFitOverrideForPty('pty-1')).toBeNull() + expect(getFitOverrideForPane(1, 'tab-0')).toBeNull() + expect(getPaneIdsForPty('pty-1')).toEqual([]) + expect(getAllOverrides().size).toBe(0) + }) +}) + +describe('scenario: mobile reconnects after disconnect', () => { + it('new override after clear works correctly', () => { + // First session + setFitOverride('pty-1', 'mobile-fit', 49, 20) + bindPanePtyId(1, 'pty-1', 'tab-0') + + // Disconnect + setFitOverride('pty-1', 'desktop-fit', 120, 40) + + // Reconnect — new session sets override again + setFitOverride('pty-1', 'mobile-fit', 55, 22) + + expect(getFitOverrideForPty('pty-1')).toEqual({ mode: 'mobile-fit', cols: 55, rows: 22 }) + expect(getFitOverrideForPane(1, 'tab-0')).toEqual({ mode: 'mobile-fit', cols: 55, rows: 22 }) + }) +}) + +describe('scenario: multiple tabs with same pane IDs', () => { + it('override on one tab does not affect the other', () => { + bindPanePtyId(1, 'pty-A', 'tab-0') + bindPanePtyId(1, 'pty-B', 'tab-1') + + setFitOverride('pty-A', 'mobile-fit', 49, 20) + + expect(getFitOverrideForPane(1, 'tab-0')?.cols).toBe(49) + expect(getFitOverrideForPane(1, 'tab-1')).toBeNull() + }) + + it('clearing override for one tab does not affect the other', () => { + bindPanePtyId(1, 'pty-A', 'tab-0') + bindPanePtyId(1, 'pty-B', 'tab-1') + + setFitOverride('pty-A', 'mobile-fit', 49, 20) + setFitOverride('pty-B', 'mobile-fit', 60, 25) + + // Clear only tab-0's PTY + setFitOverride('pty-A', 'desktop-fit', 120, 40) + + expect(getFitOverrideForPane(1, 'tab-0')).toBeNull() + expect(getFitOverrideForPane(1, 'tab-1')?.cols).toBe(60) + }) +}) + +describe('scenario: change listener fires for both override and restore', () => { + it('tracks full lifecycle: mobile-fit → desktop-fit', () => { + const events: { mode: string }[] = [] + const unsub = onOverrideChange((e) => events.push({ mode: e.mode })) + + setFitOverride('pty-1', 'mobile-fit', 49, 20) + setFitOverride('pty-1', 'desktop-fit', 120, 40) + + expect(events).toEqual([{ mode: 'mobile-fit' }, { mode: 'desktop-fit' }]) + + unsub() + }) +}) diff --git a/src/renderer/src/lib/pane-manager/mobile-fit-overrides.ts b/src/renderer/src/lib/pane-manager/mobile-fit-overrides.ts new file mode 100644 index 00000000000..7e5a781a081 --- /dev/null +++ b/src/renderer/src/lib/pane-manager/mobile-fit-overrides.ts @@ -0,0 +1,111 @@ +// Why: mobile-fit overrides are runtime-owned state that the renderer must +// respect. When a mobile client resizes a PTY to phone dimensions, the desktop +// renderer must not auto-fit that PTY back to desktop size. This module stores +// the override state and provides lookup for safeFit() and transport.resize(). + +type FitOverride = { + mode: 'mobile-fit' + cols: number + rows: number +} + +const overridesByPtyId = new Map() +// Why: keyed by 'tabId:paneId' composite to avoid collisions when different +// tabs have panes with the same numeric ID (pane IDs are per-tab, not global). +const ptyIdByPaneKey = new Map() + +// Why: the override maps are plain JS — React components that read them +// (e.g. the desktop mobile-fit banner) have no way to know when entries +// change. This listener set lets TerminalPane subscribe for re-renders +// and trigger safeFit on affected panes. +type OverrideChangeEvent = { + ptyId: string + mode: 'mobile-fit' | 'desktop-fit' + cols: number + rows: number +} +type OverrideChangeListener = (event: OverrideChangeEvent) => void +const changeListeners = new Set() + +export function onOverrideChange(listener: OverrideChangeListener): () => void { + changeListeners.add(listener) + return () => changeListeners.delete(listener) +} + +function notifyChange(event: OverrideChangeEvent): void { + for (const listener of changeListeners) { + listener(event) + } +} + +export function setFitOverride( + ptyId: string, + mode: 'mobile-fit' | 'desktop-fit', + cols: number, + rows: number +): void { + if (mode === 'mobile-fit') { + overridesByPtyId.set(ptyId, { mode, cols, rows }) + } else { + overridesByPtyId.delete(ptyId) + } + notifyChange({ ptyId, mode, cols, rows }) +} + +export function getPaneIdsForPty(ptyId: string): number[] { + const result: number[] = [] + for (const [key, boundPtyId] of ptyIdByPaneKey) { + if (boundPtyId === ptyId) { + const paneId = Number(key.split(':').pop()) + if (!Number.isNaN(paneId)) { + result.push(paneId) + } + } + } + return result +} + +export function getFitOverrideForPty(ptyId: string): FitOverride | null { + return overridesByPtyId.get(ptyId) ?? null +} + +export function getFitOverrideForPane(paneId: number, tabId?: string): FitOverride | null { + if (tabId) { + const ptyId = ptyIdByPaneKey.get(`${tabId}:${paneId}`) + if (!ptyId) { + return null + } + return overridesByPtyId.get(ptyId) ?? null + } + return null +} + +export function bindPanePtyId(paneId: number, ptyId: string | null, tabId?: string): void { + if (tabId) { + const key = `${tabId}:${paneId}` + if (ptyId) { + ptyIdByPaneKey.set(key, ptyId) + } else { + ptyIdByPaneKey.delete(key) + } + } +} + +export function unbindPane(paneId: number, tabId?: string): void { + if (tabId) { + ptyIdByPaneKey.delete(`${tabId}:${paneId}`) + } +} + +export function hydrateOverrides( + overrides: { ptyId: string; mode: 'mobile-fit'; cols: number; rows: number }[] +): void { + overridesByPtyId.clear() + for (const o of overrides) { + overridesByPtyId.set(o.ptyId, { mode: o.mode, cols: o.cols, rows: o.rows }) + } +} + +export function getAllOverrides(): Map { + return new Map(overridesByPtyId) +} diff --git a/src/renderer/src/lib/pane-manager/pane-drag-reorder.ts b/src/renderer/src/lib/pane-manager/pane-drag-reorder.ts index dc6103eb717..295da95e9b2 100644 --- a/src/renderer/src/lib/pane-manager/pane-drag-reorder.ts +++ b/src/renderer/src/lib/pane-manager/pane-drag-reorder.ts @@ -1,4 +1,4 @@ -import type { DropZone, ManagedPaneInternal } from './pane-manager-types' +import type { DropZone, ManagedPane, ManagedPaneInternal } from './pane-manager-types' import type { PaneStyleOptions } from './pane-manager-types' import { detachPaneFromTree, insertPaneNextTo } from './pane-tree-ops' @@ -17,7 +17,7 @@ export type DragReorderCallbacks = { getRoot: () => HTMLElement getStyleOptions: () => PaneStyleOptions isDestroyed: () => boolean - safeFit: (pane: ManagedPaneInternal) => void + safeFit: (pane: ManagedPane) => void applyPaneOpacity: () => void applyDividerStyles: () => void refitPanesUnder: (el: HTMLElement) => void diff --git a/src/renderer/src/lib/pane-manager/pane-fit-resize-observer.test.ts b/src/renderer/src/lib/pane-manager/pane-fit-resize-observer.test.ts index 44ba61d5f19..802ffdb4237 100644 --- a/src/renderer/src/lib/pane-manager/pane-fit-resize-observer.test.ts +++ b/src/renderer/src/lib/pane-manager/pane-fit-resize-observer.test.ts @@ -39,7 +39,7 @@ function createPane(): ManagedPaneInternal { cols: 79, rows: 24 } as never, - container: {} as never, + container: { dataset: {} } as never, xtermContainer: {} as never, linkTooltip: {} as never, terminalGpuAcceleration: 'auto', diff --git a/src/renderer/src/lib/pane-manager/pane-manager.ts b/src/renderer/src/lib/pane-manager/pane-manager.ts index d6b9fe91256..9fe3c0fb7b5 100644 --- a/src/renderer/src/lib/pane-manager/pane-manager.ts +++ b/src/renderer/src/lib/pane-manager/pane-manager.ts @@ -335,7 +335,7 @@ export class PaneManager { getRoot: () => this.root, getStyleOptions: () => this.styleOptions, isDestroyed: () => this.destroyed, - safeFit: (pane: ManagedPaneInternal) => safeFit(pane), + safeFit, applyPaneOpacity: () => applyPaneOpacity(this.panes.values(), this.activePaneId, this.styleOptions), applyDividerStyles: () => applyDividerStyles(this.root, this.styleOptions), diff --git a/src/renderer/src/lib/pane-manager/pane-tree-ops.test.ts b/src/renderer/src/lib/pane-manager/pane-tree-ops.test.ts index 1ebc2c2bac3..57fe2cdd176 100644 --- a/src/renderer/src/lib/pane-manager/pane-tree-ops.test.ts +++ b/src/renderer/src/lib/pane-manager/pane-tree-ops.test.ts @@ -1,23 +1,35 @@ -import { describe, expect, it, vi } from 'vitest' +import { afterEach, describe, expect, it, vi } from 'vitest' import { safeFit } from './pane-tree-ops' import type { ManagedPaneInternal, ScrollState } from './pane-manager-types' +import { setFitOverride, hydrateOverrides } from './mobile-fit-overrides' + +afterEach(() => { + hydrateOverrides([]) +}) function createPane({ proposedCols, proposedRows, terminalCols, - terminalRows + terminalRows, + paneId = 1 }: { proposedCols: number proposedRows: number terminalCols: number terminalRows: number + paneId?: number }): ManagedPaneInternal { const fit = vi.fn() const proposeDimensions = vi.fn(() => ({ cols: proposedCols, rows: proposedRows })) const terminal = { cols: terminalCols, rows: terminalRows, + resize: vi.fn((cols: number, rows: number) => { + terminal.cols = cols + terminal.rows = rows + }), + refresh: vi.fn(), buffer: { active: { viewportY: 0, @@ -31,9 +43,9 @@ function createPane({ } return { - id: 1, + id: paneId, terminal: terminal as never, - container: {} as never, + container: { dataset: {} } as never, xtermContainer: {} as never, linkTooltip: {} as never, terminalGpuAcceleration: 'auto', @@ -103,4 +115,97 @@ describe('safeFit', () => { expect(pane.fitAddon.fit).toHaveBeenCalledTimes(1) }) + + it('resizes terminal to override dimensions when mobile-fit override is active', () => { + const pane = createPane({ + proposedCols: 120, + proposedRows: 40, + terminalCols: 120, + terminalRows: 40 + }) + pane.container.dataset.ptyId = 'pty-phone' + setFitOverride('pty-phone', 'mobile-fit', 49, 20) + + safeFit(pane) + + expect(pane.fitAddon.fit).not.toHaveBeenCalled() + expect(pane.terminal.resize).toHaveBeenCalledWith(49, 20) + }) + + it('skips resize when terminal already matches override dimensions', () => { + const pane = createPane({ + proposedCols: 120, + proposedRows: 40, + terminalCols: 49, + terminalRows: 20 + }) + pane.container.dataset.ptyId = 'pty-phone' + setFitOverride('pty-phone', 'mobile-fit', 49, 20) + + safeFit(pane) + + expect(pane.fitAddon.fit).not.toHaveBeenCalled() + expect(pane.terminal.resize).not.toHaveBeenCalled() + }) + + it('does not apply override when pane has no data-pty-id', () => { + const pane = createPane({ + proposedCols: 100, + proposedRows: 32, + terminalCols: 120, + terminalRows: 32 + }) + setFitOverride('pty-phone', 'mobile-fit', 49, 20) + + safeFit(pane) + + expect(pane.fitAddon.fit).toHaveBeenCalledTimes(1) + expect(pane.terminal.resize).not.toHaveBeenCalled() + }) + + it('falls through to normal fit when override is cleared', () => { + const pane = createPane({ + proposedCols: 100, + proposedRows: 32, + terminalCols: 49, + terminalRows: 20 + }) + pane.container.dataset.ptyId = 'pty-phone' + setFitOverride('pty-phone', 'mobile-fit', 49, 20) + setFitOverride('pty-phone', 'desktop-fit', 120, 40) + + safeFit(pane) + + expect(pane.fitAddon.fit).toHaveBeenCalledTimes(1) + }) + + it('does not cross-contaminate overrides between different ptyIds', () => { + const paneA = createPane({ + proposedCols: 120, + proposedRows: 40, + terminalCols: 120, + terminalRows: 40, + paneId: 1 + }) + paneA.container.dataset.ptyId = 'pty-A' + + const paneB = createPane({ + proposedCols: 100, + proposedRows: 32, + terminalCols: 120, + terminalRows: 40, + paneId: 2 + }) + paneB.container.dataset.ptyId = 'pty-B' + + setFitOverride('pty-A', 'mobile-fit', 49, 20) + + safeFit(paneA) + safeFit(paneB) + + expect(paneA.terminal.resize).toHaveBeenCalledWith(49, 20) + expect(paneA.fitAddon.fit).not.toHaveBeenCalled() + expect(paneB.fitAddon.fit).toHaveBeenCalledTimes(1) + expect(paneB.terminal.resize).not.toHaveBeenCalled() + }) }) diff --git a/src/renderer/src/lib/pane-manager/pane-tree-ops.ts b/src/renderer/src/lib/pane-manager/pane-tree-ops.ts index 71f1528545f..2667c2b8f9f 100644 --- a/src/renderer/src/lib/pane-manager/pane-tree-ops.ts +++ b/src/renderer/src/lib/pane-manager/pane-tree-ops.ts @@ -1,5 +1,11 @@ -import type { DropZone, ManagedPaneInternal, PaneStyleOptions } from './pane-manager-types' +import type { + DropZone, + ManagedPane, + ManagedPaneInternal, + PaneStyleOptions +} from './pane-manager-types' import { createDivider } from './pane-divider' +import { getFitOverrideForPty } from './mobile-fit-overrides' import { disposeWebgl, attachWebgl } from './pane-lifecycle' export { findLineByContent, captureScrollState, restoreScrollState } from './pane-scroll' @@ -11,12 +17,12 @@ export { findLineByContent, captureScrollState, restoreScrollState } from './pan type TreeOpsCallbacks = { getRoot: () => HTMLElement getStyleOptions: () => PaneStyleOptions - safeFit: (pane: ManagedPaneInternal) => void + safeFit: (pane: ManagedPane) => void refitPanesUnder: (el: HTMLElement) => void onLayoutChanged?: () => void } -function getProposedDimensions(pane: ManagedPaneInternal): { cols: number; rows: number } | null { +function getProposedDimensions(pane: ManagedPane): { cols: number; rows: number } | null { try { return pane.fitAddon.proposeDimensions() ?? null } catch { @@ -34,8 +40,22 @@ function getProposedDimensions(pane: ManagedPaneInternal): { cols: number; rows: // scrollTop to 0 asynchronously. splitPane captures the pre-split state and // scheduleSplitScrollRestore owns the authoritative restore on a timer, so // safeFit here just fits and lets the scheduled restore do its job. -export function safeFit(pane: ManagedPaneInternal): void { +export function safeFit(pane: ManagedPane): void { try { + // Why: when a mobile client has resized this PTY to phone dimensions, + // the desktop must keep xterm at those dimensions instead of fitting to + // the desktop pane geometry. This prevents desktop auto-fit from undoing + // the mobile resize. Uses data-pty-id (set by bindPanePtyId) to look up + // the override by ptyId directly, avoiding pane ID collisions across tabs. + const ptyId = pane.container.dataset.ptyId + const override = ptyId ? getFitOverrideForPty(ptyId) : null + if (override) { + if (pane.terminal.cols !== override.cols || pane.terminal.rows !== override.rows) { + pane.terminal.resize(override.cols, override.rows) + } + return + } + const dims = getProposedDimensions(pane) if (dims && dims.cols === pane.terminal.cols && dims.rows === pane.terminal.rows) { // Why: divider drags fire refits every frame, but most frames do not diff --git a/src/renderer/src/runtime/sync-runtime-graph.ts b/src/renderer/src/runtime/sync-runtime-graph.ts index 53dee0b6f6b..7bc11a1480c 100644 --- a/src/renderer/src/runtime/sync-runtime-graph.ts +++ b/src/renderer/src/runtime/sync-runtime-graph.ts @@ -115,7 +115,8 @@ async function syncRuntimeGraph(): Promise { leafId, paneRuntimeId: pane.id, ptyId, - paneTitle: paneTitles[pane.id] ?? null + paneTitle: paneTitles[pane.id] ?? null, + title: state.runtimePaneTitlesByTabId[tabId]?.[pane.id] ?? tab.customTitle ?? tab.title }) } } diff --git a/src/shared/constants.ts b/src/shared/constants.ts index 2f3d748e7c6..81b2b4d473f 100644 --- a/src/shared/constants.ts +++ b/src/shared/constants.ts @@ -199,6 +199,7 @@ export function getDefaultSettings(homedir: string): GlobalSettings { // (Claude/Codex/Gemini) stays dormant for existing users and upgraders // (persistence.ts merges defaults first, so upgraders inherit this). experimentalAgentDashboard: false, + experimentalMobile: false, // Why: off by default — opt-in cosmetic joke feature. Leaving the default // false keeps the overlay unmounted for users who never enable it. experimentalSidekick: false diff --git a/src/shared/pairing.test.ts b/src/shared/pairing.test.ts new file mode 100644 index 00000000000..de0738b5907 --- /dev/null +++ b/src/shared/pairing.test.ts @@ -0,0 +1,51 @@ +import { describe, expect, it } from 'vitest' +import { encodePairingOffer, decodePairingOffer, type PairingOffer } from './pairing' + +describe('pairing offer', () => { + const offer: PairingOffer = { + v: 2, + endpoint: 'ws://192.168.1.10:6768', + deviceToken: 'abcdef1234567890abcdef1234567890abcdef1234567890', + publicKeyB64: 'dGVzdC1wdWJsaWMta2V5LWJhc2U2NC1lbmNvZGVk' + } + + it('encode then decode round-trips correctly', () => { + const url = encodePairingOffer(offer) + expect(url).toMatch(/^orca:\/\/pair#/) + + const decoded = decodePairingOffer(url) + expect(decoded).toEqual(offer) + }) + + it('encoded URL uses base64url (no +, /, or = characters)', () => { + const url = encodePairingOffer(offer) + const fragment = url.split('#')[1]! + expect(fragment).not.toMatch(/[+/=]/) + }) + + it('rejects URLs with wrong scheme', () => { + expect(() => decodePairingOffer('https://example.com#abc')).toThrow('Invalid pairing URL') + }) + + it('rejects URLs without fragment', () => { + expect(() => decodePairingOffer('orca://pair')).toThrow('Invalid pairing URL') + }) + + it('rejects payloads with missing fields', () => { + const partial = { v: 2, endpoint: 'ws://host:1234' } + const base64 = Buffer.from(JSON.stringify(partial)).toString('base64') + expect(() => decodePairingOffer(`orca://pair#${base64}`)).toThrow() + }) + + it('rejects payloads with wrong version', () => { + const wrong = { ...offer, v: 1 } + const base64 = Buffer.from(JSON.stringify(wrong)).toString('base64') + expect(() => decodePairingOffer(`orca://pair#${base64}`)).toThrow() + }) + + it('rejects payloads with missing publicKeyB64', () => { + const wrong = { v: 2, endpoint: 'ws://host:1234', deviceToken: 'tok' } + const base64 = Buffer.from(JSON.stringify(wrong)).toString('base64') + expect(() => decodePairingOffer(`orca://pair#${base64}`)).toThrow() + }) +}) diff --git a/src/shared/pairing.ts b/src/shared/pairing.ts new file mode 100644 index 00000000000..5f8d4afc3b4 --- /dev/null +++ b/src/shared/pairing.ts @@ -0,0 +1,35 @@ +import { z } from 'zod' + +export const PAIRING_OFFER_VERSION = 2 + +export const PairingOfferSchema = z.object({ + v: z.literal(PAIRING_OFFER_VERSION), + endpoint: z.string().min(1), + deviceToken: z.string().min(1), + // Why: the desktop's Curve25519 public key, base64-encoded. The mobile client + // uses this to derive a shared secret via ECDH for end-to-end encryption. + publicKeyB64: z.string().min(1) +}) + +export type PairingOffer = z.infer + +export function encodePairingOffer(offer: PairingOffer): string { + const json = JSON.stringify(offer) + const base64url = Buffer.from(json, 'utf-8') + .toString('base64') + .replace(/\+/g, '-') + .replace(/\//g, '_') + .replace(/=+$/, '') + return `orca://pair#${base64url}` +} + +export function decodePairingOffer(url: string): PairingOffer { + const hashIndex = url.indexOf('#') + if (!url.startsWith('orca://pair') || hashIndex === -1) { + throw new Error('Invalid pairing URL: must start with orca://pair#') + } + const base64url = url.slice(hashIndex + 1) + const base64 = base64url.replace(/-/g, '+').replace(/_/g, '/') + const json = Buffer.from(base64, 'base64').toString('utf-8') + return PairingOfferSchema.parse(JSON.parse(json)) +} diff --git a/src/shared/runtime-bootstrap.ts b/src/shared/runtime-bootstrap.ts index 00ac192e522..72ad330d6aa 100644 --- a/src/shared/runtime-bootstrap.ts +++ b/src/shared/runtime-bootstrap.ts @@ -9,15 +9,39 @@ export type RuntimeTransportMetadata = kind: 'named-pipe' endpoint: string } + | { + kind: 'websocket' + endpoint: string + } export type RuntimeMetadata = { runtimeId: string pid: number - transport: RuntimeTransportMetadata | null + transports: RuntimeTransportMetadata[] authToken: string | null startedAt: number } +// Why: the CLI must handle metadata files written by older Orca versions that +// used a singular `transport` field. This helper extracts the first transport +// matching the given kinds from either the new `transports` array or the +// legacy `transport` field. +export function findTransport( + metadata: RuntimeMetadata, + ...kinds: RuntimeTransportMetadata['kind'][] +): RuntimeTransportMetadata | null { + const transports = metadata.transports + if (transports && Array.isArray(transports)) { + return transports.find((t) => kinds.includes(t.kind)) ?? null + } + // Why: backward compatibility with pre-transports-array metadata files. + const legacy = (metadata as Record).transport as RuntimeTransportMetadata | null + if (legacy && kinds.includes(legacy.kind)) { + return legacy + } + return null +} + const PRIMARY_RUNTIME_METADATA_FILE = 'orca-runtime.json' export function getRuntimeMetadataPath(userDataPath: string): string { diff --git a/src/shared/runtime-types.ts b/src/shared/runtime-types.ts index adba1447e6e..95c6cf54f03 100644 --- a/src/shared/runtime-types.ts +++ b/src/shared/runtime-types.ts @@ -50,6 +50,7 @@ export type RuntimeSyncedLeaf = { paneRuntimeId: number ptyId: string | null paneTitle?: string | null + title?: string | null } export type RuntimeSyncWindowGraph = { @@ -145,14 +146,20 @@ export type RuntimeWorktreePsSummary = { repo: string path: string branch: string + displayName: string linkedIssue: number | null + linkedPR: { number: number; state: string } | null + isPinned: boolean unread: boolean liveTerminalCount: number hasAttachedPty: boolean lastOutputAt: number | null preview: string + status: RuntimeWorktreeStatus } +export type RuntimeWorktreeStatus = 'active' | 'working' | 'permission' | 'done' | 'inactive' + export type RuntimeWorktreeRecord = { id: string repoId: string diff --git a/src/shared/types.ts b/src/shared/types.ts index b78869b81f7..a10f9a333d6 100644 --- a/src/shared/types.ts +++ b/src/shared/types.ts @@ -762,6 +762,11 @@ export type WorktreeSetupLaunch = { envVars: Record } +export type WorktreeStartupLaunch = { + command: string + env?: Record +} + export type CreateSparseCheckoutRequest = { directories: string[] /** Set when the directories came from a saved preset and the user did not @@ -1147,6 +1152,7 @@ export type GlobalSettings = { * takes effect on the next app launch. The in-pane status indicators and * the cursor-agent hook path are unaffected by this toggle. */ experimentalAgentDashboard: boolean + experimentalMobile: boolean /** Experimental: floating animated sidekick (claude.webp) in the bottom-right * corner. Opt-in because it's a cosmetic joke feature; users who leave it * off never mount the overlay. Toggling takes effect immediately in the