clawdex-mobile 1.3.2 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/ci.yml +1 -1
- package/.github/workflows/npm-release.yml +18 -0
- package/AGENTS.md +3 -3
- package/README.md +101 -541
- package/apps/mobile/.env.example +1 -2
- package/apps/mobile/App.tsx +261 -68
- package/apps/mobile/app.json +31 -5
- package/apps/mobile/assets/brand/splash-icon-white.png +0 -0
- package/apps/mobile/eas.json +30 -0
- package/apps/mobile/package.json +22 -21
- package/apps/mobile/plugins/withAndroidCleartextTraffic.js +14 -0
- package/apps/mobile/src/api/__tests__/ws.test.ts +44 -6
- package/apps/mobile/src/api/chatMapping.ts +48 -8
- package/apps/mobile/src/api/client.ts +6 -0
- package/apps/mobile/src/api/types.ts +11 -0
- package/apps/mobile/src/api/ws.ts +52 -10
- package/apps/mobile/src/bridgeUrl.ts +105 -0
- package/apps/mobile/src/components/ActivityBar.tsx +32 -13
- package/apps/mobile/src/components/ChatHeader.tsx +3 -2
- package/apps/mobile/src/components/ChatInput.tsx +246 -91
- package/apps/mobile/src/components/ChatMessage.tsx +108 -4
- package/apps/mobile/src/config.ts +11 -29
- package/apps/mobile/src/hooks/useVoiceRecorder.ts +264 -0
- package/apps/mobile/src/navigation/DrawerContent.tsx +18 -8
- package/apps/mobile/src/screens/GitScreen.tsx +1 -1
- package/apps/mobile/src/screens/MainScreen.tsx +906 -268
- package/apps/mobile/src/screens/OnboardingScreen.tsx +1132 -0
- package/apps/mobile/src/screens/PrivacyScreen.tsx +1 -1
- package/apps/mobile/src/screens/SettingsScreen.tsx +65 -1
- package/apps/mobile/src/screens/TerminalScreen.tsx +1 -1
- package/apps/mobile/src/screens/TermsScreen.tsx +1 -1
- package/docs/app-review-notes.md +7 -2
- package/docs/eas-builds.md +91 -0
- package/docs/realtime-streaming-limitations.md +84 -0
- package/docs/setup-and-operations.md +239 -0
- package/docs/troubleshooting.md +121 -0
- package/docs/voice-transcription.md +87 -0
- package/package.json +8 -16
- package/scripts/setup-secure-dev.sh +122 -8
- package/scripts/setup-wizard.sh +342 -122
- package/scripts/start-bridge-secure.sh +7 -1
- package/scripts/sync-versions.js +63 -0
- package/services/rust-bridge/.env.example +1 -1
- package/services/rust-bridge/Cargo.lock +1104 -23
- package/services/rust-bridge/Cargo.toml +3 -1
- package/services/rust-bridge/package.json +1 -1
- package/services/rust-bridge/src/main.rs +587 -12
- package/apps/mobile/metro.config.js +0 -3
|
@@ -4,7 +4,6 @@ import {
|
|
|
4
4
|
Alert,
|
|
5
5
|
Linking,
|
|
6
6
|
Pressable,
|
|
7
|
-
SafeAreaView,
|
|
8
7
|
ScrollView,
|
|
9
8
|
StyleSheet,
|
|
10
9
|
Text,
|
|
@@ -12,6 +11,7 @@ import {
|
|
|
12
11
|
} from 'react-native';
|
|
13
12
|
import { BlurView } from 'expo-blur';
|
|
14
13
|
import { LinearGradient } from 'expo-linear-gradient';
|
|
14
|
+
import { SafeAreaView } from 'react-native-safe-area-context';
|
|
15
15
|
|
|
16
16
|
import { colors, radius, spacing, typography } from '../theme';
|
|
17
17
|
|
|
@@ -6,12 +6,12 @@ import {
|
|
|
6
6
|
ActivityIndicator,
|
|
7
7
|
Modal,
|
|
8
8
|
Pressable,
|
|
9
|
-
SafeAreaView,
|
|
10
9
|
ScrollView,
|
|
11
10
|
StyleSheet,
|
|
12
11
|
Text,
|
|
13
12
|
View,
|
|
14
13
|
} from 'react-native';
|
|
14
|
+
import { SafeAreaView } from 'react-native-safe-area-context';
|
|
15
15
|
|
|
16
16
|
import type { HostBridgeApiClient } from '../api/client';
|
|
17
17
|
import type { ApprovalMode, ModelOption, ReasoningEffort } from '../api/types';
|
|
@@ -30,6 +30,8 @@ interface SettingsScreenProps {
|
|
|
30
30
|
effort: ReasoningEffort | null
|
|
31
31
|
) => void;
|
|
32
32
|
onApprovalModeChange?: (mode: ApprovalMode) => void;
|
|
33
|
+
onEditBridgeUrl?: () => void;
|
|
34
|
+
onResetOnboarding?: () => void;
|
|
33
35
|
onOpenDrawer: () => void;
|
|
34
36
|
onOpenPrivacy: () => void;
|
|
35
37
|
onOpenTerms: () => void;
|
|
@@ -44,6 +46,8 @@ export function SettingsScreen({
|
|
|
44
46
|
approvalMode,
|
|
45
47
|
onDefaultModelSettingsChange,
|
|
46
48
|
onApprovalModeChange,
|
|
49
|
+
onEditBridgeUrl,
|
|
50
|
+
onResetOnboarding,
|
|
47
51
|
onOpenDrawer,
|
|
48
52
|
onOpenPrivacy,
|
|
49
53
|
onOpenTerms,
|
|
@@ -301,6 +305,26 @@ export function SettingsScreen({
|
|
|
301
305
|
<Text selectable style={styles.valueText}>
|
|
302
306
|
{bridgeUrl}
|
|
303
307
|
</Text>
|
|
308
|
+
<Pressable
|
|
309
|
+
onPress={onEditBridgeUrl}
|
|
310
|
+
style={({ pressed }) => [
|
|
311
|
+
styles.bridgeEditBtn,
|
|
312
|
+
pressed && styles.bridgeEditBtnPressed,
|
|
313
|
+
]}
|
|
314
|
+
>
|
|
315
|
+
<Ionicons name="swap-horizontal-outline" size={15} color={colors.textPrimary} />
|
|
316
|
+
<Text style={styles.bridgeEditBtnText}>Change bridge URL</Text>
|
|
317
|
+
</Pressable>
|
|
318
|
+
<Pressable
|
|
319
|
+
onPress={onResetOnboarding}
|
|
320
|
+
style={({ pressed }) => [
|
|
321
|
+
styles.bridgeResetBtn,
|
|
322
|
+
pressed && styles.bridgeResetBtnPressed,
|
|
323
|
+
]}
|
|
324
|
+
>
|
|
325
|
+
<Ionicons name="refresh-circle-outline" size={15} color={colors.error} />
|
|
326
|
+
<Text style={styles.bridgeResetBtnText}>Reset onboarding</Text>
|
|
327
|
+
</Pressable>
|
|
304
328
|
</BlurView>
|
|
305
329
|
|
|
306
330
|
<Text style={[styles.sectionLabel, styles.sectionLabelGap]}>Health</Text>
|
|
@@ -569,6 +593,46 @@ const styles = StyleSheet.create({
|
|
|
569
593
|
paddingVertical: spacing.md,
|
|
570
594
|
fontSize: 14,
|
|
571
595
|
},
|
|
596
|
+
bridgeEditBtn: {
|
|
597
|
+
marginBottom: spacing.md,
|
|
598
|
+
borderRadius: radius.md,
|
|
599
|
+
borderWidth: 1,
|
|
600
|
+
borderColor: colors.border,
|
|
601
|
+
backgroundColor: colors.bgMain,
|
|
602
|
+
flexDirection: 'row',
|
|
603
|
+
alignItems: 'center',
|
|
604
|
+
justifyContent: 'center',
|
|
605
|
+
gap: spacing.xs,
|
|
606
|
+
paddingVertical: spacing.sm,
|
|
607
|
+
},
|
|
608
|
+
bridgeEditBtnPressed: {
|
|
609
|
+
opacity: 0.82,
|
|
610
|
+
},
|
|
611
|
+
bridgeEditBtnText: {
|
|
612
|
+
...typography.caption,
|
|
613
|
+
color: colors.textPrimary,
|
|
614
|
+
fontWeight: '600',
|
|
615
|
+
},
|
|
616
|
+
bridgeResetBtn: {
|
|
617
|
+
marginBottom: spacing.md,
|
|
618
|
+
borderRadius: radius.md,
|
|
619
|
+
borderWidth: 1,
|
|
620
|
+
borderColor: colors.error,
|
|
621
|
+
backgroundColor: 'rgba(239, 68, 68, 0.08)',
|
|
622
|
+
flexDirection: 'row',
|
|
623
|
+
alignItems: 'center',
|
|
624
|
+
justifyContent: 'center',
|
|
625
|
+
gap: spacing.xs,
|
|
626
|
+
paddingVertical: spacing.sm,
|
|
627
|
+
},
|
|
628
|
+
bridgeResetBtnPressed: {
|
|
629
|
+
opacity: 0.82,
|
|
630
|
+
},
|
|
631
|
+
bridgeResetBtnText: {
|
|
632
|
+
...typography.caption,
|
|
633
|
+
color: colors.error,
|
|
634
|
+
fontWeight: '700',
|
|
635
|
+
},
|
|
572
636
|
row: {
|
|
573
637
|
flexDirection: 'row',
|
|
574
638
|
justifyContent: 'space-between',
|
|
@@ -5,13 +5,13 @@ import {
|
|
|
5
5
|
KeyboardAvoidingView,
|
|
6
6
|
Platform,
|
|
7
7
|
Pressable,
|
|
8
|
-
SafeAreaView,
|
|
9
8
|
ScrollView,
|
|
10
9
|
StyleSheet,
|
|
11
10
|
Text,
|
|
12
11
|
TextInput,
|
|
13
12
|
View,
|
|
14
13
|
} from 'react-native';
|
|
14
|
+
import { SafeAreaView } from 'react-native-safe-area-context';
|
|
15
15
|
|
|
16
16
|
import type { HostBridgeApiClient } from '../api/client';
|
|
17
17
|
import type { HostBridgeWsClient } from '../api/ws';
|
|
@@ -4,7 +4,6 @@ import {
|
|
|
4
4
|
Alert,
|
|
5
5
|
Linking,
|
|
6
6
|
Pressable,
|
|
7
|
-
SafeAreaView,
|
|
8
7
|
ScrollView,
|
|
9
8
|
StyleSheet,
|
|
10
9
|
Text,
|
|
@@ -12,6 +11,7 @@ import {
|
|
|
12
11
|
} from 'react-native';
|
|
13
12
|
import { BlurView } from 'expo-blur';
|
|
14
13
|
import { LinearGradient } from 'expo-linear-gradient';
|
|
14
|
+
import { SafeAreaView } from 'react-native-safe-area-context';
|
|
15
15
|
|
|
16
16
|
import { colors, radius, spacing, typography } from '../theme';
|
|
17
17
|
|
package/docs/app-review-notes.md
CHANGED
|
@@ -57,12 +57,17 @@ BRIDGE_CORS_ORIGINS=http://localhost:19006,http://localhost:8081
|
|
|
57
57
|
- In `apps/mobile/.env`, set:
|
|
58
58
|
|
|
59
59
|
```env
|
|
60
|
-
EXPO_PUBLIC_HOST_BRIDGE_URL=http://<mac-lan-ip>:8787
|
|
61
60
|
EXPO_PUBLIC_HOST_BRIDGE_TOKEN=<review-token>
|
|
62
61
|
EXPO_PUBLIC_PRIVACY_POLICY_URL=https://<your-policy-url>
|
|
63
62
|
EXPO_PUBLIC_TERMS_OF_SERVICE_URL=https://<your-terms-url>
|
|
64
63
|
```
|
|
65
64
|
|
|
65
|
+
- In app onboarding, enter:
|
|
66
|
+
|
|
67
|
+
```text
|
|
68
|
+
http://<mac-lan-ip>:8787
|
|
69
|
+
```
|
|
70
|
+
|
|
66
71
|
## Reviewer Walkthrough
|
|
67
72
|
|
|
68
73
|
1. Launch app.
|
|
@@ -76,7 +81,7 @@ EXPO_PUBLIC_TERMS_OF_SERVICE_URL=https://<your-terms-url>
|
|
|
76
81
|
## Security And Privacy Notes For Review
|
|
77
82
|
|
|
78
83
|
- Bridge auth token is required by default.
|
|
79
|
-
- WebSocket auth uses Authorization
|
|
84
|
+
- WebSocket auth uses Authorization headers and supports query-token fallback for Android compatibility.
|
|
80
85
|
- Bridge can be run localhost-only; LAN mode is user-configured.
|
|
81
86
|
- Terminal commands are constrained by server-side allowlist and can be fully disabled.
|
|
82
87
|
- Requested command working directory is constrained within configured bridge root.
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# EAS Builds and Distribution
|
|
2
|
+
|
|
3
|
+
Use this guide for standalone app builds/distribution.
|
|
4
|
+
|
|
5
|
+
## Where To Run EAS Commands
|
|
6
|
+
|
|
7
|
+
Run EAS commands from `apps/mobile`.
|
|
8
|
+
|
|
9
|
+
Why:
|
|
10
|
+
|
|
11
|
+
- Expo app config is in `apps/mobile/app.json`
|
|
12
|
+
- EAS config is in `apps/mobile/eas.json`
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
cd apps/mobile
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
You can also run from repo root with workspace scoping:
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
npm exec --workspace apps/mobile -- eas <command>
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Prerequisites
|
|
25
|
+
|
|
26
|
+
- `eas-cli` installed (`npm install -g eas-cli`)
|
|
27
|
+
- Logged in (`eas login`)
|
|
28
|
+
- Expo project linked (`eas project:info`)
|
|
29
|
+
|
|
30
|
+
## Build Profiles
|
|
31
|
+
|
|
32
|
+
Current profiles in `apps/mobile/eas.json`:
|
|
33
|
+
|
|
34
|
+
- `development` (dev client, internal distribution)
|
|
35
|
+
- `preview` (internal distribution)
|
|
36
|
+
- `production` (store/prod, auto-increment)
|
|
37
|
+
|
|
38
|
+
## Common Build Commands
|
|
39
|
+
|
|
40
|
+
From `apps/mobile`:
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
# Internal/dev-client builds
|
|
44
|
+
eas build --platform ios --profile development
|
|
45
|
+
eas build --platform android --profile development
|
|
46
|
+
|
|
47
|
+
# Internal preview builds
|
|
48
|
+
eas build --platform ios --profile preview
|
|
49
|
+
eas build --platform android --profile preview
|
|
50
|
+
|
|
51
|
+
# Production builds
|
|
52
|
+
eas build --platform ios --profile production
|
|
53
|
+
eas build --platform android --profile production
|
|
54
|
+
|
|
55
|
+
# Both platforms
|
|
56
|
+
eas build --platform all --profile preview
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Track builds:
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
eas build:list --limit 10
|
|
63
|
+
eas build:view <BUILD_ID>
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Submit To Stores
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
eas submit --platform ios --latest --profile production
|
|
70
|
+
eas submit --platform android --latest --profile production
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## Local Native Build Option (No EAS Cloud)
|
|
74
|
+
|
|
75
|
+
If you want local native builds instead:
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
npx expo run:ios
|
|
79
|
+
npx expo run:android
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
For iOS local device/signed builds, Apple signing/tooling is still required.
|
|
83
|
+
|
|
84
|
+
## iOS Distribution Reality
|
|
85
|
+
|
|
86
|
+
Without a public App Store release, iOS distribution still requires Apple provisioning paths:
|
|
87
|
+
|
|
88
|
+
1. Internal/dev provisioning with device allowlist
|
|
89
|
+
2. TestFlight private testing
|
|
90
|
+
|
|
91
|
+
Cloud builds are possible without public App Store listing, but signing/provisioning requirements still apply.
|
|
@@ -75,3 +75,87 @@ To get strict realtime parity across CLI and mobile, move to a single live event
|
|
|
75
75
|
2. Make clients attach to the exact same running app-server stream/session boundary.
|
|
76
76
|
|
|
77
77
|
Without this architectural change, the hybrid model remains the pragmatic and low-risk approach.
|
|
78
|
+
|
|
79
|
+
## Incident Note: February 28, 2026 Live-Sync Regression
|
|
80
|
+
|
|
81
|
+
### Symptom
|
|
82
|
+
Mobile chat showed persisted messages, but did not show live activity/reasoning updates (`codex/event/*`, `thread/status/changed`) for CLI-origin turns.
|
|
83
|
+
|
|
84
|
+
### Root Cause
|
|
85
|
+
Rust bridge rollout discovery scheduler used a modulo condition that could never become true when the discovery interval was `1`.
|
|
86
|
+
|
|
87
|
+
- Previous behavior: discovery did not run for that interval value.
|
|
88
|
+
- Effect: rollout files were not tracked, so no CLI live-tail notifications were emitted.
|
|
89
|
+
|
|
90
|
+
### Fix Applied
|
|
91
|
+
Live-sync discovery scheduling was hardened so it is valid for all interval values and always runs on first tick.
|
|
92
|
+
|
|
93
|
+
Operational result:
|
|
94
|
+
|
|
95
|
+
1. Rollout files are discovered consistently.
|
|
96
|
+
2. CLI-origin event lines are tailed and converted into bridge notifications.
|
|
97
|
+
3. Mobile receives activity/reasoning status in realtime again.
|
|
98
|
+
|
|
99
|
+
## Optimization Backlog (Live-Sync Observability + Reliability)
|
|
100
|
+
|
|
101
|
+
### 1) Add `bridge/liveSync/status` RPC (recommended next)
|
|
102
|
+
|
|
103
|
+
Goal: make rollout tailing state visible from mobile/Postman without guessing.
|
|
104
|
+
|
|
105
|
+
Suggested response payload:
|
|
106
|
+
|
|
107
|
+
1. Sessions root currently in use (`CODEX_HOME` resolved path).
|
|
108
|
+
2. Discovery config (`pollIntervalMs`, `discoveryIntervalTicks`, `maxTrackedFiles`).
|
|
109
|
+
3. Tracked files list:
|
|
110
|
+
- file path
|
|
111
|
+
- thread id
|
|
112
|
+
- originator
|
|
113
|
+
- include/exclude flag
|
|
114
|
+
- include/exclude reason
|
|
115
|
+
- current offset
|
|
116
|
+
- last seen timestamp
|
|
117
|
+
4. Summary counters:
|
|
118
|
+
- files discovered
|
|
119
|
+
- files included
|
|
120
|
+
- lines parsed
|
|
121
|
+
- lines dropped (invalid JSON / filtered / duplicate)
|
|
122
|
+
- notifications emitted by method bucket
|
|
123
|
+
5. Last error (if any) in discovery/poll loop.
|
|
124
|
+
|
|
125
|
+
Why this helps:
|
|
126
|
+
|
|
127
|
+
1. Explains "why no live updates" immediately.
|
|
128
|
+
2. Reduces debugging time for CLI vs mobile parity issues.
|
|
129
|
+
3. Enables lightweight health checks and QA verification.
|
|
130
|
+
|
|
131
|
+
### 2) Add discovery/poll loop metrics
|
|
132
|
+
|
|
133
|
+
Expose counters for:
|
|
134
|
+
|
|
135
|
+
1. discovery runs
|
|
136
|
+
2. poll runs
|
|
137
|
+
3. per-file parse errors
|
|
138
|
+
4. event mapping misses
|
|
139
|
+
5. replay buffer writes/drops
|
|
140
|
+
|
|
141
|
+
These can be returned by the status RPC and optionally logged to stderr in dev mode.
|
|
142
|
+
|
|
143
|
+
### 3) Add dedup + mapping diagnostics for dropped events
|
|
144
|
+
|
|
145
|
+
Track bounded recent reasons for dropped lines:
|
|
146
|
+
|
|
147
|
+
1. duplicate hash
|
|
148
|
+
2. unsupported rollout record type
|
|
149
|
+
3. missing thread id
|
|
150
|
+
4. originator filtered
|
|
151
|
+
|
|
152
|
+
This will clarify whether events are not emitted due to filtering or malformed source lines.
|
|
153
|
+
|
|
154
|
+
### 4) Add a short runbook for operators
|
|
155
|
+
|
|
156
|
+
When live updates are missing, check in this order:
|
|
157
|
+
|
|
158
|
+
1. `bridge/events/replay` contains recent `codex/event/*` or not.
|
|
159
|
+
2. `bridge/liveSync/status` include/exclude reason for newest rollout file.
|
|
160
|
+
3. newest rollout file `session_meta.originator` and resolved thread id fields.
|
|
161
|
+
4. mobile selected thread id matches emitted thread id.
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
# Setup and Operations
|
|
2
|
+
|
|
3
|
+
This guide is the detailed companion to the top-level `README.md`.
|
|
4
|
+
|
|
5
|
+
## Onboarding Output Cues
|
|
6
|
+
|
|
7
|
+
After `clawdex init`, expected sequence:
|
|
8
|
+
|
|
9
|
+
1. Bridge health passes (`Bridge health check passed.`)
|
|
10
|
+
2. Expo starts (`Starting Expo (mobile) in background...`)
|
|
11
|
+
3. You may briefly see a spinner (`Waiting for Expo output - ...`)
|
|
12
|
+
4. Expo output begins (`expo start --host lan`, QR block, `Metro waiting on exp://...`)
|
|
13
|
+
5. Press Enter to detach onboarding while Expo + bridge keep running
|
|
14
|
+
|
|
15
|
+
## Manual Secure Setup (No Wizard)
|
|
16
|
+
|
|
17
|
+
### 1) Install dependencies
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npm install
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
### 2) Generate secure runtime config
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
npm run secure:setup
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Creates/updates:
|
|
30
|
+
|
|
31
|
+
- `.env.secure` (bridge runtime config + token)
|
|
32
|
+
- `apps/mobile/.env` (mobile token + optional runtime knobs)
|
|
33
|
+
|
|
34
|
+
### 3) Start bridge
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
npm run secure:bridge
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### 4) Start Expo
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
npm run mobile
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
`npm run mobile` uses `scripts/start-expo.sh`, which sets `REACT_NATIVE_PACKAGER_HOSTNAME` from your secure config so QR resolution is predictable.
|
|
47
|
+
|
|
48
|
+
On first app launch, onboarding will ask for your bridge URL (for example `http://100.x.y.z:8787` or `http://192.168.x.y:8787`). This URL is stored on-device and can be changed later in Settings.
|
|
49
|
+
|
|
50
|
+
## Advanced Knobs
|
|
51
|
+
|
|
52
|
+
Optional environment variables:
|
|
53
|
+
|
|
54
|
+
- `CLAWDEX_SETUP_VERBOSE=true` — show full installer output
|
|
55
|
+
- `BRIDGE_HEALTH_WAIT_SECS=300` — max wait for bridge `/health`
|
|
56
|
+
- `EXPO_OUTPUT_WAIT_SECS=90` — spinner timeout before streaming Expo logs
|
|
57
|
+
- `EXPO_AUTO_REPAIR=true` — auto-repair React Native runtime on `npm run mobile`
|
|
58
|
+
- `EXPO_CLEAR_CACHE=true` — force `expo start --clear` via `npm run mobile`
|
|
59
|
+
|
|
60
|
+
## Teardown / Cleanup
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
npm run teardown
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Can:
|
|
67
|
+
|
|
68
|
+
- stop Expo + bridge
|
|
69
|
+
- remove generated artifacts (`.env.secure`, `.bridge.log`, `.expo.log`, pid files)
|
|
70
|
+
- optionally reset `apps/mobile/.env` from `.env.example`
|
|
71
|
+
- optionally run `tailscale down`
|
|
72
|
+
|
|
73
|
+
Non-interactive mode:
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
npm run teardown -- --yes
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Environment Reference
|
|
80
|
+
|
|
81
|
+
### Bridge runtime (`.env.secure`, generated)
|
|
82
|
+
|
|
83
|
+
| Variable | Purpose |
|
|
84
|
+
|---|---|
|
|
85
|
+
| `BRIDGE_HOST` | bind host for rust bridge |
|
|
86
|
+
| `BRIDGE_PORT` | bridge port (default `8787`) |
|
|
87
|
+
| `BRIDGE_AUTH_TOKEN` | required auth token |
|
|
88
|
+
| `BRIDGE_ALLOW_QUERY_TOKEN_AUTH` | query-token auth fallback |
|
|
89
|
+
| `CODEX_CLI_BIN` | codex executable |
|
|
90
|
+
| `BRIDGE_WORKDIR` | absolute working directory for terminal/git |
|
|
91
|
+
| `BRIDGE_ALLOW_OUTSIDE_ROOT_CWD` | allow terminal/git `cwd` outside `BRIDGE_WORKDIR` |
|
|
92
|
+
|
|
93
|
+
### Mobile runtime (`apps/mobile/.env`, generated/updated)
|
|
94
|
+
|
|
95
|
+
| Variable | Purpose |
|
|
96
|
+
|---|---|
|
|
97
|
+
| `EXPO_PUBLIC_HOST_BRIDGE_TOKEN` | token sent by mobile client |
|
|
98
|
+
| `EXPO_PUBLIC_ALLOW_QUERY_TOKEN_AUTH` | query-token behavior for WebSocket auth fallback |
|
|
99
|
+
| `EXPO_PUBLIC_ALLOW_INSECURE_REMOTE_BRIDGE` | suppress insecure-HTTP warning |
|
|
100
|
+
| `EXPO_PUBLIC_PRIVACY_POLICY_URL` | in-app Privacy link |
|
|
101
|
+
| `EXPO_PUBLIC_TERMS_OF_SERVICE_URL` | in-app Terms link |
|
|
102
|
+
|
|
103
|
+
## Production Readiness Checklist
|
|
104
|
+
|
|
105
|
+
- Keep bridge network-private only (Tailscale/private LAN/VPN + host firewall)
|
|
106
|
+
- Require `BRIDGE_AUTH_TOKEN`
|
|
107
|
+
- Keep `BRIDGE_ALLOW_QUERY_TOKEN_AUTH=true` only on private networks (required for Android WS auth fallback)
|
|
108
|
+
- Do not set `BRIDGE_ALLOW_INSECURE_NO_AUTH=true` outside local debugging
|
|
109
|
+
- Scope `BRIDGE_WORKDIR` to minimal required root
|
|
110
|
+
- Use strict default approvals on mobile
|
|
111
|
+
- Treat `Session`/`Allow similar` approval actions as privileged
|
|
112
|
+
- Run bridge under a supervisor with restart policy
|
|
113
|
+
- Rotate bridge tokens periodically and on device loss
|
|
114
|
+
- Keep `codex`, Node deps, Expo SDK, and OS patches updated
|
|
115
|
+
|
|
116
|
+
## Verifying Setup
|
|
117
|
+
|
|
118
|
+
### Bridge health
|
|
119
|
+
|
|
120
|
+
```bash
|
|
121
|
+
source .env.secure
|
|
122
|
+
curl "http://$BRIDGE_HOST:$BRIDGE_PORT/health"
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
Expected response contains `"status":"ok"`.
|
|
126
|
+
|
|
127
|
+
### In-app smoke test
|
|
128
|
+
|
|
129
|
+
1. Open app and verify Settings reports bridge connected
|
|
130
|
+
2. Set `Start Directory` from sidebar (optional)
|
|
131
|
+
3. Create a chat and send a prompt
|
|
132
|
+
4. Switch to Plan mode and send prompt that triggers clarifying options
|
|
133
|
+
5. Verify clarification flow can submit
|
|
134
|
+
6. Open Git from header and verify status/diff/commit/push behavior
|
|
135
|
+
7. Test attachment menu (`+`) with workspace path + phone file/image
|
|
136
|
+
8. Run long task and verify stop button interrupts run and transcript logs stop
|
|
137
|
+
|
|
138
|
+
## Chat Controls (Workspace, Model, Mode, Approvals)
|
|
139
|
+
|
|
140
|
+
### Choosing Start Directory
|
|
141
|
+
|
|
142
|
+
1. Open sidebar
|
|
143
|
+
2. Under `Start Directory`, pick:
|
|
144
|
+
- `Bridge default workspace`, or
|
|
145
|
+
- a discovered workspace path from existing chats
|
|
146
|
+
|
|
147
|
+
Behavior:
|
|
148
|
+
|
|
149
|
+
- Applies to new chats
|
|
150
|
+
- Existing chats retain their own workspace unless changed
|
|
151
|
+
|
|
152
|
+
### Model and Slash Commands
|
|
153
|
+
|
|
154
|
+
Supported mobile slash commands:
|
|
155
|
+
|
|
156
|
+
- `/help`
|
|
157
|
+
- `/new`
|
|
158
|
+
- `/model [model-id]`
|
|
159
|
+
- `/plan [on|off|prompt]`
|
|
160
|
+
- `/status`
|
|
161
|
+
- `/rename <new-name>`
|
|
162
|
+
- `/compact`
|
|
163
|
+
- `/review`
|
|
164
|
+
- `/fork`
|
|
165
|
+
- `/diff`
|
|
166
|
+
|
|
167
|
+
### Plan Mode and Clarifications
|
|
168
|
+
|
|
169
|
+
- Plan mode is sent through `turn/start` via structured `collaborationMode`
|
|
170
|
+
- App can auto-switch to plan mode on plan events or when server requests it
|
|
171
|
+
- Structured clarifications open a dedicated modal
|
|
172
|
+
- Numbered plain-text options are rendered as tappable fallback choices
|
|
173
|
+
|
|
174
|
+
### Approval UX
|
|
175
|
+
|
|
176
|
+
Approval banner actions:
|
|
177
|
+
|
|
178
|
+
- `Deny`
|
|
179
|
+
- `Allow once`
|
|
180
|
+
- `Session`
|
|
181
|
+
- `Allow similar` (when available)
|
|
182
|
+
|
|
183
|
+
Approval events are surfaced via `bridge/approval.requested` and `bridge/approval.resolved`.
|
|
184
|
+
|
|
185
|
+
## NPM Release Automation
|
|
186
|
+
|
|
187
|
+
Workflow: `.github/workflows/npm-release.yml`
|
|
188
|
+
|
|
189
|
+
Required repo secret:
|
|
190
|
+
|
|
191
|
+
- `NPM_TOKEN`
|
|
192
|
+
|
|
193
|
+
Typical release flow (from `main`):
|
|
194
|
+
|
|
195
|
+
```bash
|
|
196
|
+
npm version patch
|
|
197
|
+
git push origin main --follow-tags
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
Automation verifies tag/version consistency and publishes to npm.
|
|
201
|
+
|
|
202
|
+
## API Summary (Rust Bridge)
|
|
203
|
+
|
|
204
|
+
### Endpoints
|
|
205
|
+
|
|
206
|
+
- `GET /health`
|
|
207
|
+
- `GET /rpc` (WebSocket JSON-RPC)
|
|
208
|
+
|
|
209
|
+
### Forwarded methods
|
|
210
|
+
|
|
211
|
+
- `thread/*`
|
|
212
|
+
- `turn/*` (includes `turn/interrupt`)
|
|
213
|
+
- `review/start`
|
|
214
|
+
- `model/list`
|
|
215
|
+
- `skills/list`
|
|
216
|
+
- `app/list`
|
|
217
|
+
|
|
218
|
+
### Bridge RPC methods
|
|
219
|
+
|
|
220
|
+
- `bridge/health/read`
|
|
221
|
+
- `bridge/terminal/exec`
|
|
222
|
+
- `bridge/attachments/upload`
|
|
223
|
+
- `bridge/voice/transcribe`
|
|
224
|
+
- `bridge/git/status`
|
|
225
|
+
- `bridge/git/diff`
|
|
226
|
+
- `bridge/git/commit`
|
|
227
|
+
- `bridge/git/push`
|
|
228
|
+
- `bridge/approvals/list`
|
|
229
|
+
- `bridge/approvals/resolve`
|
|
230
|
+
- `bridge/userInput/resolve`
|
|
231
|
+
|
|
232
|
+
### Notifications (examples)
|
|
233
|
+
|
|
234
|
+
- `turn/*`, `item/*`
|
|
235
|
+
- `bridge/approval.*`
|
|
236
|
+
- `bridge/userInput.*`
|
|
237
|
+
- `bridge/terminal/completed`
|
|
238
|
+
- `bridge/git/updated`
|
|
239
|
+
- `bridge/connection/state`
|