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
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
# Troubleshooting
|
|
2
|
+
|
|
3
|
+
## Onboarding looks stuck before Expo logs appear
|
|
4
|
+
|
|
5
|
+
- Expo startup can be slow on first launch.
|
|
6
|
+
- You should see: `Waiting for Expo output ...`
|
|
7
|
+
- Increase timeout if needed:
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
EXPO_OUTPUT_WAIT_SECS=180 clawdex init
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
- If Expo never emits logs:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
tail -n 120 .expo.log
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Expo starts but QR/network is wrong
|
|
20
|
+
|
|
21
|
+
- Re-run `npm run secure:setup`
|
|
22
|
+
- Confirm `.env.secure` has correct `BRIDGE_HOST`
|
|
23
|
+
- Restart `npm run mobile`
|
|
24
|
+
|
|
25
|
+
## Stop all running services quickly
|
|
26
|
+
|
|
27
|
+
Preferred:
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
clawdex stop
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
From repo checkout:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
npm run stop:services
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Bridge auth errors (`401`, invalid token)
|
|
40
|
+
|
|
41
|
+
- Ensure `BRIDGE_AUTH_TOKEN` in `.env.secure` matches `EXPO_PUBLIC_HOST_BRIDGE_TOKEN` in `apps/mobile/.env`
|
|
42
|
+
- Restart bridge + Expo after token changes
|
|
43
|
+
|
|
44
|
+
## Tailscale issues
|
|
45
|
+
|
|
46
|
+
- Verify host and phone are on the same Tailscale network
|
|
47
|
+
- Check host IP (`tailscale ip -4`) and mobile `.env` URL
|
|
48
|
+
|
|
49
|
+
## `codex` not found
|
|
50
|
+
|
|
51
|
+
- Ensure `codex` is in `PATH`
|
|
52
|
+
- Or set `CODEX_CLI_BIN` explicitly
|
|
53
|
+
|
|
54
|
+
## Bridge build fails with `linker 'cc' not found`
|
|
55
|
+
|
|
56
|
+
Install C build tools:
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
sudo apt-get update && sudo apt-get install -y build-essential
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Then retry `npm run secure:bridge`.
|
|
63
|
+
|
|
64
|
+
## iOS bundling error: `Unable to resolve "./BoundingDimensions"`
|
|
65
|
+
|
|
66
|
+
Manual recovery:
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
npm install --include=dev --force
|
|
70
|
+
npm install --include=dev --force -w apps/mobile
|
|
71
|
+
npm run -w apps/mobile start -- --clear
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## Runtime errors: `[runtime not ready]` / `property is not writable`
|
|
75
|
+
|
|
76
|
+
Manual recovery:
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
rm -rf node_modules apps/mobile/node_modules
|
|
80
|
+
npm install --include=dev --force
|
|
81
|
+
npm install --include=dev --force -w apps/mobile
|
|
82
|
+
npm run -w apps/mobile start -- --clear
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Also update Expo Go on your phone.
|
|
86
|
+
|
|
87
|
+
## Git operations fail
|
|
88
|
+
|
|
89
|
+
- Verify chat workspace is a valid git repo
|
|
90
|
+
- Verify remote auth/access for push
|
|
91
|
+
|
|
92
|
+
## Attachment upload issues
|
|
93
|
+
|
|
94
|
+
- Ensure mobile app has file/photo permissions
|
|
95
|
+
- File limit is `20 MB` per upload
|
|
96
|
+
- Uploads persist under `BRIDGE_WORKDIR/.clawdex-mobile-attachments`
|
|
97
|
+
- Ensure `BRIDGE_WORKDIR` is writable
|
|
98
|
+
|
|
99
|
+
## Worklets/Reanimated mismatch
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
cd apps/mobile
|
|
103
|
+
npx expo install --fix
|
|
104
|
+
npm run start -- --clear
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
## Plan mode errors (`RPC-32600` invalid `collaborationMode`)
|
|
108
|
+
|
|
109
|
+
- Restart Expo and reload app bundle
|
|
110
|
+
- Ensure bridge/mobile revisions match
|
|
111
|
+
- Run API test if needed:
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
npm run -w apps/mobile test -- --runInBand src/api/__tests__/client.test.ts
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
## Stop button does not interrupt a run
|
|
118
|
+
|
|
119
|
+
- Ensure revision supports `turn/interrupt`
|
|
120
|
+
- If run already finished, stop button disappears by design
|
|
121
|
+
- Pull latest, restart bridge, reload Expo bundle
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# Push-to-Talk Voice Transcription
|
|
2
|
+
|
|
3
|
+
Voice-to-text input for the mobile composer. Tap the mic button to record, tap again to stop — the audio is sent to the Rust bridge which calls OpenAI's transcription API and returns the text into the composer.
|
|
4
|
+
|
|
5
|
+
## Architecture
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
Phone (expo-audio) Rust Bridge OpenAI
|
|
9
|
+
record 16kHz mono --> base64 over WebSocket --> POST /v1/audio/transcriptions
|
|
10
|
+
(iOS WAV, Android M4A)
|
|
11
|
+
insert text in composer <-- { text: "..." } <-- gpt-4o-transcribe response
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
The flow mirrors Codex CLI 0.105.0's TUI push-to-talk feature — same API, same model (`gpt-4o-transcribe`), same auth. The bridge acts as the HTTP proxy since the phone doesn't hold API keys directly.
|
|
15
|
+
|
|
16
|
+
## Auth Resolution
|
|
17
|
+
|
|
18
|
+
The bridge resolves transcription credentials in order:
|
|
19
|
+
|
|
20
|
+
1. `OPENAI_API_KEY` env var → `https://api.openai.com/v1/audio/transcriptions` (model: `gpt-4o-transcribe`)
|
|
21
|
+
2. `BRIDGE_CHATGPT_ACCESS_TOKEN` env var → `https://chatgpt.com/backend-api/transcribe` (no model param)
|
|
22
|
+
3. `~/.codex/auth.json` fallback:
|
|
23
|
+
- `OPENAI_API_KEY` field present → same as path 1
|
|
24
|
+
- `auth_mode: "chatgpt"` with `tokens.access_token` → same as path 2
|
|
25
|
+
|
|
26
|
+
## Files
|
|
27
|
+
|
|
28
|
+
### Rust Bridge
|
|
29
|
+
|
|
30
|
+
| File | What |
|
|
31
|
+
|------|------|
|
|
32
|
+
| `services/rust-bridge/Cargo.toml` | Added `reqwest` (multipart, json, rustls-tls) |
|
|
33
|
+
| `services/rust-bridge/src/main.rs` | `bridge/voice/transcribe` JSON-RPC method, `transcribe_voice()`, `resolve_transcription_auth()`, `resolve_codex_auth_json_path()` |
|
|
34
|
+
|
|
35
|
+
### Mobile
|
|
36
|
+
|
|
37
|
+
| File | What |
|
|
38
|
+
|------|------|
|
|
39
|
+
| `apps/mobile/app.json` | `NSMicrophoneUsageDescription` (iOS), `RECORD_AUDIO` permission (Android), `expo-audio` plugin |
|
|
40
|
+
| `apps/mobile/src/api/types.ts` | `VoiceTranscribeRequest`, `VoiceTranscribeResponse` |
|
|
41
|
+
| `apps/mobile/src/api/client.ts` | `transcribeVoice()` method on `HostBridgeApiClient` |
|
|
42
|
+
| `apps/mobile/src/hooks/useVoiceRecorder.ts` | Recording state machine hook using `expo-audio` |
|
|
43
|
+
| `apps/mobile/src/components/ChatInput.tsx` | Mic button UI with three visual states |
|
|
44
|
+
| `apps/mobile/src/screens/MainScreen.tsx` | Wires hook to ChatInput |
|
|
45
|
+
|
|
46
|
+
## Recording Config
|
|
47
|
+
|
|
48
|
+
- Sample rate: 16,000 Hz
|
|
49
|
+
- Channels: 1 (mono)
|
|
50
|
+
- Format:
|
|
51
|
+
- iOS: LINEARPCM 16-bit (`audio/wav`)
|
|
52
|
+
- Android: MPEG-4 AAC (`audio/mp4`, `.m4a`)
|
|
53
|
+
- Minimum duration:
|
|
54
|
+
- Mobile guard: 1 second
|
|
55
|
+
- Bridge raw payload guard: ~0.5 seconds (16KB minimum)
|
|
56
|
+
- Maximum payload size:
|
|
57
|
+
- Mobile guard: 20MB
|
|
58
|
+
- Bridge guard: 100MB by default (override with `BRIDGE_MAX_VOICE_TRANSCRIPTION_BYTES`)
|
|
59
|
+
|
|
60
|
+
## UI States
|
|
61
|
+
|
|
62
|
+
The mic button occupies the send button slot when the composer is empty and no turn is running:
|
|
63
|
+
|
|
64
|
+
| State | Icon | Style |
|
|
65
|
+
|-------|------|-------|
|
|
66
|
+
| Idle | `mic-outline` | Muted color |
|
|
67
|
+
| Recording | `mic` | Red icon, red border |
|
|
68
|
+
| Transcribing | `ActivityIndicator` | Spinner |
|
|
69
|
+
|
|
70
|
+
Interaction: tap to start recording, tap again to stop and transcribe. The transcribed text is appended to the current draft.
|
|
71
|
+
|
|
72
|
+
Voice input is enabled on iOS and Android. The mic button is hidden on web.
|
|
73
|
+
|
|
74
|
+
## Dependencies
|
|
75
|
+
|
|
76
|
+
- **`expo-audio`** — Recording via `useAudioRecorder` hook. Works in Expo Go (unlike `expo-av` which requires a dev build in SDK 55).
|
|
77
|
+
- **`reqwest`** (Rust) — HTTP client for the multipart POST to OpenAI. Uses `rustls-tls` to avoid native-tls linking issues on macOS.
|
|
78
|
+
|
|
79
|
+
## Error Handling
|
|
80
|
+
|
|
81
|
+
- Mic permission denied → error message, stays idle
|
|
82
|
+
- Recording < 1 second → "Recording too short" error, discarded
|
|
83
|
+
- Audio payload < 16KB → bridge rejects with `invalid_params`
|
|
84
|
+
- Audio payload > 20MB → mobile rejects before upload
|
|
85
|
+
- Audio payload > bridge max bytes (default 100MB) → bridge rejects with `invalid_params`
|
|
86
|
+
- No credentials found → bridge returns error code `-32002`
|
|
87
|
+
- Transcription API HTTP error → bridge returns status + body in error data
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "clawdex-mobile",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"homepage": "https://github.com/Mohit-Patil/clawdex-mobile#readme",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -25,7 +25,9 @@
|
|
|
25
25
|
"stop:services": "./scripts/stop-services.sh",
|
|
26
26
|
"secure:setup": "./scripts/setup-secure-dev.sh",
|
|
27
27
|
"secure:bridge": "./scripts/start-bridge-secure.sh",
|
|
28
|
+
"secure:bridge:dev": "BRIDGE_RUN_MODE=dev ./scripts/start-bridge-secure.sh",
|
|
28
29
|
"bridge:ts": "BRIDGE_WORKDIR=$(pwd) npm run -w @codex/mac-bridge dev",
|
|
30
|
+
"version:sync": "node scripts/sync-versions.js",
|
|
29
31
|
"build": "npm run --workspaces build",
|
|
30
32
|
"typecheck": "npm run --workspaces typecheck",
|
|
31
33
|
"lint": "npm run --workspaces lint",
|
|
@@ -33,20 +35,10 @@
|
|
|
33
35
|
"teardown": "./scripts/teardown.sh"
|
|
34
36
|
},
|
|
35
37
|
"dependencies": {
|
|
36
|
-
"@types/react": "19.
|
|
37
|
-
"react": "19.
|
|
38
|
-
"react-
|
|
39
|
-
"
|
|
40
|
-
|
|
41
|
-
"overrides": {
|
|
42
|
-
"minimatch": "10.2.2",
|
|
43
|
-
"@react-navigation/drawer": {
|
|
44
|
-
"react-native-reanimated": "4.1.1"
|
|
45
|
-
},
|
|
46
|
-
"react-native-drawer-layout": {
|
|
47
|
-
"react-native-reanimated": "4.1.1"
|
|
48
|
-
},
|
|
49
|
-
"react-native-reanimated": "4.1.1",
|
|
50
|
-
"react-native-worklets": "0.5.1"
|
|
38
|
+
"@types/react": "~19.2.10",
|
|
39
|
+
"react": "19.2.0",
|
|
40
|
+
"react-dom": "19.2.0",
|
|
41
|
+
"react-native": "0.83.2",
|
|
42
|
+
"expo": "^55.0.2"
|
|
51
43
|
}
|
|
52
44
|
}
|
|
@@ -77,6 +77,34 @@ ensure_tailscale_cli() {
|
|
|
77
77
|
return 0
|
|
78
78
|
}
|
|
79
79
|
|
|
80
|
+
is_ipv4() {
|
|
81
|
+
local ip="$1"
|
|
82
|
+
local part=""
|
|
83
|
+
local -a octets=()
|
|
84
|
+
|
|
85
|
+
[[ "$ip" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]] || return 1
|
|
86
|
+
IFS='.' read -r -a octets <<<"$ip"
|
|
87
|
+
if [[ "${#octets[@]}" -ne 4 ]]; then
|
|
88
|
+
return 1
|
|
89
|
+
fi
|
|
90
|
+
|
|
91
|
+
for part in "${octets[@]}"; do
|
|
92
|
+
if (( part < 0 || part > 255 )); then
|
|
93
|
+
return 1
|
|
94
|
+
fi
|
|
95
|
+
done
|
|
96
|
+
|
|
97
|
+
return 0
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
is_non_loopback_ipv4() {
|
|
101
|
+
local ip="$1"
|
|
102
|
+
if ! is_ipv4 "$ip"; then
|
|
103
|
+
return 1
|
|
104
|
+
fi
|
|
105
|
+
[[ "$ip" != 127.* ]]
|
|
106
|
+
}
|
|
107
|
+
|
|
80
108
|
resolve_tailscale_ipv4() {
|
|
81
109
|
local ip
|
|
82
110
|
ip="$(tailscale ip -4 2>/dev/null | head -n1 | tr -d '[:space:]' || true)"
|
|
@@ -100,6 +128,77 @@ resolve_tailscale_ipv4() {
|
|
|
100
128
|
printf '%s' "$ip"
|
|
101
129
|
}
|
|
102
130
|
|
|
131
|
+
current_local_ipv4() {
|
|
132
|
+
local candidate=""
|
|
133
|
+
local iface=""
|
|
134
|
+
|
|
135
|
+
if [[ "$(uname -s)" == "Darwin" ]] && command -v ipconfig >/dev/null 2>&1; then
|
|
136
|
+
for iface in en0 en1; do
|
|
137
|
+
candidate="$(ipconfig getifaddr "$iface" 2>/dev/null | tr -d '[:space:]' || true)"
|
|
138
|
+
if is_non_loopback_ipv4 "$candidate"; then
|
|
139
|
+
printf '%s' "$candidate"
|
|
140
|
+
return 0
|
|
141
|
+
fi
|
|
142
|
+
done
|
|
143
|
+
fi
|
|
144
|
+
|
|
145
|
+
if command -v hostname >/dev/null 2>&1; then
|
|
146
|
+
while IFS= read -r candidate; do
|
|
147
|
+
candidate="$(printf '%s' "$candidate" | tr -d '[:space:]')"
|
|
148
|
+
if is_non_loopback_ipv4 "$candidate"; then
|
|
149
|
+
printf '%s' "$candidate"
|
|
150
|
+
return 0
|
|
151
|
+
fi
|
|
152
|
+
done < <(hostname -I 2>/dev/null | tr ' ' '\n' || true)
|
|
153
|
+
fi
|
|
154
|
+
|
|
155
|
+
if command -v ip >/dev/null 2>&1; then
|
|
156
|
+
candidate="$(ip route get 1.1.1.1 2>/dev/null | awk '{ for (i=1; i<=NF; i++) if ($i=="src") { print $(i+1); exit } }')"
|
|
157
|
+
candidate="$(printf '%s' "$candidate" | tr -d '[:space:]')"
|
|
158
|
+
if is_non_loopback_ipv4 "$candidate"; then
|
|
159
|
+
printf '%s' "$candidate"
|
|
160
|
+
return 0
|
|
161
|
+
fi
|
|
162
|
+
fi
|
|
163
|
+
|
|
164
|
+
if command -v ifconfig >/dev/null 2>&1; then
|
|
165
|
+
while IFS= read -r candidate; do
|
|
166
|
+
candidate="$(printf '%s' "$candidate" | tr -d '[:space:]')"
|
|
167
|
+
if is_non_loopback_ipv4 "$candidate"; then
|
|
168
|
+
printf '%s' "$candidate"
|
|
169
|
+
return 0
|
|
170
|
+
fi
|
|
171
|
+
done < <(ifconfig 2>/dev/null | awk '/inet /{print $2}' || true)
|
|
172
|
+
fi
|
|
173
|
+
|
|
174
|
+
return 1
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
resolve_local_ipv4() {
|
|
178
|
+
local ip=""
|
|
179
|
+
ip="$(current_local_ipv4 || true)"
|
|
180
|
+
|
|
181
|
+
if [[ -n "$ip" ]]; then
|
|
182
|
+
printf '%s' "$ip"
|
|
183
|
+
return 0
|
|
184
|
+
fi
|
|
185
|
+
|
|
186
|
+
echo "No active local/LAN IPv4 found automatically."
|
|
187
|
+
if ! confirm_prompt "Enter bridge host IP manually?"; then
|
|
188
|
+
echo "error: unable to resolve LAN IPv4. Connect to LAN and retry." >&2
|
|
189
|
+
return 1
|
|
190
|
+
fi
|
|
191
|
+
|
|
192
|
+
read -r -p "Bridge host IP: " ip
|
|
193
|
+
ip="$(printf '%s' "$ip" | tr -d '[:space:]')"
|
|
194
|
+
if ! is_non_loopback_ipv4 "$ip"; then
|
|
195
|
+
echo "error: invalid IPv4 '$ip'." >&2
|
|
196
|
+
return 1
|
|
197
|
+
fi
|
|
198
|
+
|
|
199
|
+
printf '%s' "$ip"
|
|
200
|
+
}
|
|
201
|
+
|
|
103
202
|
if ! command -v openssl >/dev/null 2>&1; then
|
|
104
203
|
echo "error: openssl not found. Install OpenSSL first." >&2
|
|
105
204
|
exit 1
|
|
@@ -117,13 +216,28 @@ fi
|
|
|
117
216
|
|
|
118
217
|
BRIDGE_HOST="${BRIDGE_HOST_OVERRIDE:-}"
|
|
119
218
|
HOST_SOURCE=""
|
|
219
|
+
BRIDGE_NETWORK_MODE="${BRIDGE_NETWORK_MODE:-tailscale}"
|
|
220
|
+
|
|
221
|
+
case "$BRIDGE_NETWORK_MODE" in
|
|
222
|
+
tailscale|local)
|
|
223
|
+
;;
|
|
224
|
+
*)
|
|
225
|
+
echo "error: BRIDGE_NETWORK_MODE must be 'tailscale' or 'local'." >&2
|
|
226
|
+
exit 1
|
|
227
|
+
;;
|
|
228
|
+
esac
|
|
120
229
|
|
|
121
230
|
if [[ -n "$BRIDGE_HOST" ]]; then
|
|
122
231
|
HOST_SOURCE="override"
|
|
123
232
|
else
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
233
|
+
if [[ "$BRIDGE_NETWORK_MODE" == "tailscale" ]]; then
|
|
234
|
+
ensure_tailscale_cli
|
|
235
|
+
BRIDGE_HOST="$(resolve_tailscale_ipv4)"
|
|
236
|
+
HOST_SOURCE="tailscale"
|
|
237
|
+
else
|
|
238
|
+
BRIDGE_HOST="$(resolve_local_ipv4)"
|
|
239
|
+
HOST_SOURCE="local"
|
|
240
|
+
fi
|
|
127
241
|
fi
|
|
128
242
|
|
|
129
243
|
BRIDGE_PORT="${BRIDGE_PORT_OVERRIDE:-8787}"
|
|
@@ -139,26 +253,26 @@ if [[ -z "$BRIDGE_TOKEN" ]]; then
|
|
|
139
253
|
fi
|
|
140
254
|
|
|
141
255
|
cat > "$SECURE_ENV_FILE" <<EOT
|
|
256
|
+
BRIDGE_NETWORK_MODE=$BRIDGE_NETWORK_MODE
|
|
142
257
|
BRIDGE_HOST=$BRIDGE_HOST
|
|
143
258
|
BRIDGE_PORT=$BRIDGE_PORT
|
|
144
259
|
BRIDGE_AUTH_TOKEN=$BRIDGE_TOKEN
|
|
145
|
-
BRIDGE_ALLOW_QUERY_TOKEN_AUTH=
|
|
260
|
+
BRIDGE_ALLOW_QUERY_TOKEN_AUTH=true
|
|
146
261
|
CODEX_CLI_BIN=codex
|
|
147
262
|
BRIDGE_WORKDIR=$ROOT_DIR
|
|
148
263
|
EOT
|
|
149
264
|
|
|
150
265
|
chmod 600 "$SECURE_ENV_FILE"
|
|
151
266
|
|
|
152
|
-
upsert_env_key "$MOBILE_ENV_FILE" "EXPO_PUBLIC_HOST_BRIDGE_URL" "http://$BRIDGE_HOST:$BRIDGE_PORT"
|
|
153
267
|
upsert_env_key "$MOBILE_ENV_FILE" "EXPO_PUBLIC_HOST_BRIDGE_TOKEN" "$BRIDGE_TOKEN"
|
|
154
|
-
# Backward compatibility for older app builds that still read MAC_BRIDGE
|
|
155
|
-
upsert_env_key "$MOBILE_ENV_FILE" "EXPO_PUBLIC_MAC_BRIDGE_URL" "http://$BRIDGE_HOST:$BRIDGE_PORT"
|
|
268
|
+
# Backward compatibility for older app builds that still read MAC_BRIDGE token key.
|
|
156
269
|
upsert_env_key "$MOBILE_ENV_FILE" "EXPO_PUBLIC_MAC_BRIDGE_TOKEN" "$BRIDGE_TOKEN"
|
|
157
|
-
upsert_env_key "$MOBILE_ENV_FILE" "EXPO_PUBLIC_ALLOW_QUERY_TOKEN_AUTH" "
|
|
270
|
+
upsert_env_key "$MOBILE_ENV_FILE" "EXPO_PUBLIC_ALLOW_QUERY_TOKEN_AUTH" "true"
|
|
158
271
|
upsert_env_key "$MOBILE_ENV_FILE" "EXPO_PUBLIC_ALLOW_INSECURE_REMOTE_BRIDGE" "true"
|
|
159
272
|
|
|
160
273
|
echo "Secure dev setup complete."
|
|
161
274
|
echo ""
|
|
275
|
+
echo "Bridge network mode: $BRIDGE_NETWORK_MODE"
|
|
162
276
|
echo "Bridge host: $BRIDGE_HOST ($HOST_SOURCE)"
|
|
163
277
|
echo "Bridge port: $BRIDGE_PORT"
|
|
164
278
|
echo "Token source: $SECURE_ENV_FILE"
|