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.
Files changed (48) hide show
  1. package/.github/workflows/ci.yml +1 -1
  2. package/.github/workflows/npm-release.yml +18 -0
  3. package/AGENTS.md +3 -3
  4. package/README.md +101 -541
  5. package/apps/mobile/.env.example +1 -2
  6. package/apps/mobile/App.tsx +261 -68
  7. package/apps/mobile/app.json +31 -5
  8. package/apps/mobile/assets/brand/splash-icon-white.png +0 -0
  9. package/apps/mobile/eas.json +30 -0
  10. package/apps/mobile/package.json +22 -21
  11. package/apps/mobile/plugins/withAndroidCleartextTraffic.js +14 -0
  12. package/apps/mobile/src/api/__tests__/ws.test.ts +44 -6
  13. package/apps/mobile/src/api/chatMapping.ts +48 -8
  14. package/apps/mobile/src/api/client.ts +6 -0
  15. package/apps/mobile/src/api/types.ts +11 -0
  16. package/apps/mobile/src/api/ws.ts +52 -10
  17. package/apps/mobile/src/bridgeUrl.ts +105 -0
  18. package/apps/mobile/src/components/ActivityBar.tsx +32 -13
  19. package/apps/mobile/src/components/ChatHeader.tsx +3 -2
  20. package/apps/mobile/src/components/ChatInput.tsx +246 -91
  21. package/apps/mobile/src/components/ChatMessage.tsx +108 -4
  22. package/apps/mobile/src/config.ts +11 -29
  23. package/apps/mobile/src/hooks/useVoiceRecorder.ts +264 -0
  24. package/apps/mobile/src/navigation/DrawerContent.tsx +18 -8
  25. package/apps/mobile/src/screens/GitScreen.tsx +1 -1
  26. package/apps/mobile/src/screens/MainScreen.tsx +906 -268
  27. package/apps/mobile/src/screens/OnboardingScreen.tsx +1132 -0
  28. package/apps/mobile/src/screens/PrivacyScreen.tsx +1 -1
  29. package/apps/mobile/src/screens/SettingsScreen.tsx +65 -1
  30. package/apps/mobile/src/screens/TerminalScreen.tsx +1 -1
  31. package/apps/mobile/src/screens/TermsScreen.tsx +1 -1
  32. package/docs/app-review-notes.md +7 -2
  33. package/docs/eas-builds.md +91 -0
  34. package/docs/realtime-streaming-limitations.md +84 -0
  35. package/docs/setup-and-operations.md +239 -0
  36. package/docs/troubleshooting.md +121 -0
  37. package/docs/voice-transcription.md +87 -0
  38. package/package.json +8 -16
  39. package/scripts/setup-secure-dev.sh +122 -8
  40. package/scripts/setup-wizard.sh +342 -122
  41. package/scripts/start-bridge-secure.sh +7 -1
  42. package/scripts/sync-versions.js +63 -0
  43. package/services/rust-bridge/.env.example +1 -1
  44. package/services/rust-bridge/Cargo.lock +1104 -23
  45. package/services/rust-bridge/Cargo.toml +3 -1
  46. package/services/rust-bridge/package.json +1 -1
  47. package/services/rust-bridge/src/main.rs +587 -12
  48. package/apps/mobile/metro.config.js +0 -3
@@ -24,6 +24,7 @@ fi
24
24
 
25
25
  FLOW="quickstart"
26
26
  CONFIG_ACTION="configure"
27
+ NETWORK_MODE=""
27
28
  TAILSCALE_IP=""
28
29
  BRIDGE_HOST=""
29
30
  BRIDGE_PORT=""
@@ -125,8 +126,7 @@ print_usage() {
125
126
  Usage: $(basename "$0") [options]
126
127
 
127
128
  Options:
128
- --no-start Configure everything but do not start bridge/Expo
129
- --platform <name> Auto-start platform: mobile|ios|android (default: mobile)
129
+ --no-start Configure everything but do not start bridge
130
130
  -h, --help Show this help
131
131
  EOF
132
132
  }
@@ -138,15 +138,6 @@ parse_args() {
138
138
  AUTO_START="false"
139
139
  shift
140
140
  ;;
141
- --platform)
142
- if [[ $# -lt 2 ]]; then
143
- echo "error: --platform requires a value" >&2
144
- print_usage >&2
145
- exit 1
146
- fi
147
- TARGET_PLATFORM="$2"
148
- shift 2
149
- ;;
150
141
  -h|--help)
151
142
  print_usage
152
143
  exit 0
@@ -158,15 +149,6 @@ parse_args() {
158
149
  ;;
159
150
  esac
160
151
  done
161
-
162
- case "$TARGET_PLATFORM" in
163
- mobile|ios|android)
164
- ;;
165
- *)
166
- echo "error: invalid --platform '$TARGET_PLATFORM' (expected mobile|ios|android)" >&2
167
- exit 1
168
- ;;
169
- esac
170
152
  }
171
153
 
172
154
  run_with_privilege() {
@@ -558,6 +540,79 @@ current_tailscale_ip() {
558
540
  done < <(tailscale ip -4 2>/dev/null || true)
559
541
  }
560
542
 
543
+ is_non_loopback_ipv4() {
544
+ local ip="$1"
545
+ if ! is_ipv4 "$ip"; then
546
+ return 1
547
+ fi
548
+ [[ "$ip" != 127.* ]]
549
+ }
550
+
551
+ current_lan_ip() {
552
+ local candidate=""
553
+ local iface=""
554
+
555
+ if [[ "$OS_NAME" == "Darwin" ]] && command -v ipconfig >/dev/null 2>&1; then
556
+ for iface in en0 en1; do
557
+ candidate="$(ipconfig getifaddr "$iface" 2>/dev/null | tr -d '[:space:]' || true)"
558
+ if is_non_loopback_ipv4 "$candidate"; then
559
+ printf '%s' "$candidate"
560
+ return 0
561
+ fi
562
+ done
563
+ fi
564
+
565
+ if command -v hostname >/dev/null 2>&1; then
566
+ while IFS= read -r candidate; do
567
+ candidate="$(printf '%s' "$candidate" | tr -d '[:space:]')"
568
+ if is_non_loopback_ipv4 "$candidate"; then
569
+ printf '%s' "$candidate"
570
+ return 0
571
+ fi
572
+ done < <(hostname -I 2>/dev/null | tr ' ' '\n' || true)
573
+ fi
574
+
575
+ if command -v ip >/dev/null 2>&1; then
576
+ 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 } }')"
577
+ candidate="$(printf '%s' "$candidate" | tr -d '[:space:]')"
578
+ if is_non_loopback_ipv4 "$candidate"; then
579
+ printf '%s' "$candidate"
580
+ return 0
581
+ fi
582
+ fi
583
+
584
+ if command -v ifconfig >/dev/null 2>&1; then
585
+ while IFS= read -r candidate; do
586
+ candidate="$(printf '%s' "$candidate" | tr -d '[:space:]')"
587
+ if is_non_loopback_ipv4 "$candidate"; then
588
+ printf '%s' "$candidate"
589
+ return 0
590
+ fi
591
+ done < <(ifconfig 2>/dev/null | awk '/inet /{print $2}' || true)
592
+ fi
593
+ }
594
+
595
+ prompt_manual_ipv4() {
596
+ local prompt="$1"
597
+ local ip=""
598
+
599
+ while true; do
600
+ rail_echo "$prompt"
601
+ IFS= read -r ip
602
+ ip="$(printf '%s' "$ip" | tr -d '[:space:]')"
603
+
604
+ if is_non_loopback_ipv4 "$ip"; then
605
+ printf '%s' "$ip"
606
+ return 0
607
+ fi
608
+
609
+ warn "Invalid IPv4 '$ip'."
610
+ if ! confirm_prompt "Try entering host IP again?" "Y"; then
611
+ return 1
612
+ fi
613
+ done
614
+ }
615
+
561
616
  extract_env_value() {
562
617
  local file="$1"
563
618
  local key="$2"
@@ -623,6 +678,7 @@ print_existing_setup_summary() {
623
678
  local host=""
624
679
  local port=""
625
680
  local token=""
681
+ local network_mode=""
626
682
  local source_path=""
627
683
 
628
684
  if [[ ! -f "$SECURE_ENV_FILE" ]]; then
@@ -632,6 +688,7 @@ print_existing_setup_summary() {
632
688
  host="$(extract_env_value "$SECURE_ENV_FILE" "BRIDGE_HOST")"
633
689
  port="$(extract_env_value "$SECURE_ENV_FILE" "BRIDGE_PORT")"
634
690
  token="$(extract_env_value "$SECURE_ENV_FILE" "BRIDGE_AUTH_TOKEN")"
691
+ network_mode="$(extract_env_value "$SECURE_ENV_FILE" "BRIDGE_NETWORK_MODE")"
635
692
 
636
693
  if [[ -z "$host" ]] && [[ -z "$port" ]] && [[ -z "$token" ]]; then
637
694
  return 1
@@ -644,6 +701,9 @@ print_existing_setup_summary() {
644
701
 
645
702
  echo "bridge.host: $host"
646
703
  echo "bridge.port: $port"
704
+ if [[ -n "$network_mode" ]]; then
705
+ echo "bridge.networkMode: $network_mode"
706
+ fi
647
707
  if [[ -n "$token" ]]; then
648
708
  echo "bridge.token: present"
649
709
  fi
@@ -709,6 +769,33 @@ choose_config_action() {
709
769
  esac
710
770
  }
711
771
 
772
+ choose_bridge_network_mode() {
773
+ menu_select "Bridge network mode" "Local (LAN)" "Tailscale"
774
+ case "$MENU_RESULT" in
775
+ "Local (LAN)")
776
+ NETWORK_MODE="local"
777
+ info "Local (LAN) mode selected."
778
+ ;;
779
+ "Tailscale")
780
+ NETWORK_MODE="tailscale"
781
+ info "Tailscale mode selected."
782
+ ;;
783
+ *)
784
+ abort_wizard "Unexpected bridge network mode."
785
+ ;;
786
+ esac
787
+ }
788
+
789
+ infer_network_mode_from_host() {
790
+ local host="$1"
791
+ host="$(printf '%s' "$host" | tr -d '[:space:]')"
792
+ if [[ "$host" == 100.* ]]; then
793
+ printf '%s' "tailscale"
794
+ return 0
795
+ fi
796
+ printf '%s' "local"
797
+ }
798
+
712
799
  ensure_core_tools() {
713
800
  local required_cmd=""
714
801
  for required_cmd in node npm; do
@@ -847,6 +934,77 @@ resolve_tailscale_ip() {
847
934
  resolve_tailscale_ip_manual
848
935
  }
849
936
 
937
+ resolve_local_ip_quickstart() {
938
+ local ip=""
939
+ ip="$(current_lan_ip || true)"
940
+
941
+ while [[ -z "$ip" ]]; do
942
+ warn "No active local/LAN IPv4 detected."
943
+ if confirm_prompt "Enter bridge host IP manually?" "Y"; then
944
+ ip="$(prompt_manual_ipv4 "Enter LAN IP for this machine (example: 192.168.1.30):" || true)"
945
+ fi
946
+
947
+ if [[ -n "$ip" ]]; then
948
+ break
949
+ fi
950
+
951
+ if ! confirm_prompt "Retry LAN IP detection?" "Y"; then
952
+ abort_wizard "Connect this machine to LAN/VPN, then rerun: npm run setup:wizard"
953
+ fi
954
+ ip="$(current_lan_ip || true)"
955
+ done
956
+
957
+ printf '%s' "$ip"
958
+ }
959
+
960
+ resolve_local_ip_manual() {
961
+ local ip=""
962
+ ip="$(current_lan_ip || true)"
963
+
964
+ while [[ -z "$ip" ]]; do
965
+ warn "No active local/LAN IPv4 detected."
966
+ menu_select "Local network action" "Retry auto-detect" "Enter host IP manually" "Show network interfaces" "Abort"
967
+
968
+ case "$MENU_RESULT" in
969
+ "Retry auto-detect")
970
+ ;;
971
+ "Enter host IP manually")
972
+ ip="$(prompt_manual_ipv4 "Enter LAN IP for this machine (example: 192.168.1.30):" || true)"
973
+ ;;
974
+ "Show network interfaces")
975
+ if command -v ifconfig >/dev/null 2>&1; then
976
+ ifconfig || true
977
+ elif command -v ip >/dev/null 2>&1; then
978
+ ip addr || true
979
+ else
980
+ warn "No network interface command available."
981
+ fi
982
+ press_enter_to_continue
983
+ ;;
984
+ "Abort")
985
+ abort_wizard "Resolve host LAN IP, then rerun: npm run setup:wizard"
986
+ ;;
987
+ *)
988
+ ;;
989
+ esac
990
+
991
+ if [[ -z "$ip" ]]; then
992
+ ip="$(current_lan_ip || true)"
993
+ fi
994
+ done
995
+
996
+ printf '%s' "$ip"
997
+ }
998
+
999
+ resolve_local_ip() {
1000
+ if [[ "$FLOW" == "quickstart" ]]; then
1001
+ resolve_local_ip_quickstart
1002
+ return 0
1003
+ fi
1004
+
1005
+ resolve_local_ip_manual
1006
+ }
1007
+
850
1008
  print_phone_tailscale_note() {
851
1009
  print_note_box "Phone setup (Tailscale)" "Install Tailscale on your phone and sign in to the same Tailscale account as this machine.
852
1010
 
@@ -940,6 +1098,100 @@ confirm_phone_tailscale_ready() {
940
1098
  confirm_phone_tailscale_manual
941
1099
  }
942
1100
 
1101
+ print_phone_local_note() {
1102
+ print_note_box "Phone setup (Local LAN)" "Connect your phone and this machine to the same local network.
1103
+
1104
+ Steps:
1105
+ - Ensure both are on the same Wi-Fi (or same private VPN segment).
1106
+ - Keep the bridge running on this machine.
1107
+ - Use the shown LAN bridge URL in app onboarding.
1108
+ - Scan the bridge token QR from the bridge terminal."
1109
+ }
1110
+
1111
+ confirm_phone_local_quickstart() {
1112
+ local note_shown="false"
1113
+
1114
+ while true; do
1115
+ if [[ "$note_shown" == "false" ]]; then
1116
+ print_phone_local_note
1117
+ note_shown="true"
1118
+ fi
1119
+
1120
+ if confirm_prompt "Is your phone on the same local network as this machine?" "Y"; then
1121
+ return 0
1122
+ fi
1123
+
1124
+ warn "Connect both devices to the same network, then continue."
1125
+ if ! confirm_prompt "Retry phone local network check?" "Y"; then
1126
+ abort_wizard "Connect phone to same network, then rerun: npm run setup:wizard"
1127
+ fi
1128
+ done
1129
+ }
1130
+
1131
+ confirm_phone_local_manual() {
1132
+ local show_note="true"
1133
+
1134
+ while true; do
1135
+ if [[ "$show_note" == "true" ]]; then
1136
+ print_phone_local_note
1137
+ show_note="false"
1138
+ fi
1139
+
1140
+ menu_select "Phone local network status" \
1141
+ "Phone is ready (same network)" \
1142
+ "Show instructions again" \
1143
+ "Show host network interfaces" \
1144
+ "Abort"
1145
+
1146
+ case "$MENU_RESULT" in
1147
+ "Phone is ready (same network)")
1148
+ return 0
1149
+ ;;
1150
+ "Show instructions again")
1151
+ show_note="true"
1152
+ ;;
1153
+ "Show host network interfaces")
1154
+ if command -v ifconfig >/dev/null 2>&1; then
1155
+ ifconfig || true
1156
+ elif command -v ip >/dev/null 2>&1; then
1157
+ ip addr || true
1158
+ else
1159
+ warn "No network interface command available."
1160
+ fi
1161
+ press_enter_to_continue
1162
+ ;;
1163
+ "Abort")
1164
+ abort_wizard "Connect phone to same network, then rerun: npm run setup:wizard"
1165
+ ;;
1166
+ *)
1167
+ ;;
1168
+ esac
1169
+ done
1170
+ }
1171
+
1172
+ confirm_phone_local_ready() {
1173
+ if [[ "$FLOW" == "quickstart" ]]; then
1174
+ confirm_phone_local_quickstart
1175
+ return 0
1176
+ fi
1177
+
1178
+ confirm_phone_local_manual
1179
+ }
1180
+
1181
+ confirm_phone_network_ready() {
1182
+ case "$NETWORK_MODE" in
1183
+ tailscale)
1184
+ confirm_phone_tailscale_ready
1185
+ ;;
1186
+ local)
1187
+ confirm_phone_local_ready
1188
+ ;;
1189
+ *)
1190
+ abort_wizard "Unknown network mode '$NETWORK_MODE'."
1191
+ ;;
1192
+ esac
1193
+ }
1194
+
943
1195
  has_mobile_react_native_runtime() {
944
1196
  local root_touchable="$ROOT_DIR/node_modules/react-native/Libraries/Components/Touchable/BoundingDimensions.js"
945
1197
  local workspace_touchable="$ROOT_DIR/apps/mobile/node_modules/react-native/Libraries/Components/Touchable/BoundingDimensions.js"
@@ -968,21 +1220,16 @@ install_project_dependencies() {
968
1220
  local should_install="false"
969
1221
  local need_install="false"
970
1222
 
971
- if [[ ! -d "$ROOT_DIR/node_modules" ]] || [[ ! -d "$ROOT_DIR/node_modules/expo" ]]; then
1223
+ if [[ ! -d "$ROOT_DIR/node_modules" ]] || [[ ! -d "$ROOT_DIR/node_modules/@codex" ]]; then
972
1224
  need_install="true"
973
1225
  fi
974
1226
 
975
1227
  if [[ "$need_install" == "true" ]]; then
976
- if [[ "$AUTO_START" == "true" ]]; then
977
- info "Project dependencies are missing. Installing automatically for one-stop onboarding..."
1228
+ if confirm_prompt "Install project npm dependencies now?" "Y"; then
978
1229
  should_install="true"
979
1230
  else
980
- if confirm_prompt "Install project npm dependencies now? (required for full onboarding)" "Y"; then
981
- should_install="true"
982
- else
983
- if [[ "$AUTO_START" == "true" ]]; then
984
- abort_wizard "Dependencies are required for auto-start. Re-run with --no-start or allow install."
985
- fi
1231
+ if [[ "$AUTO_START" == "true" ]]; then
1232
+ abort_wizard "Dependencies are required before starting the bridge."
986
1233
  fi
987
1234
  fi
988
1235
  else
@@ -996,36 +1243,6 @@ install_project_dependencies() {
996
1243
  run_quiet_command "Project dependency install" bash -lc "cd \"$ROOT_DIR\" && npm install --include=dev && npm dedupe"
997
1244
  ok "Dependencies installed."
998
1245
  fi
999
-
1000
- # Expo may prompt to install TypeScript if dev deps were skipped in prior installs.
1001
- # Ensure mobile workspace tooling is present before auto-start.
1002
- if [[ "$AUTO_START" == "true" ]]; then
1003
- if ! node -e "require.resolve('typescript/package.json', { paths: ['$ROOT_DIR/apps/mobile', '$ROOT_DIR'] })" >/dev/null 2>&1; then
1004
- info "Installing missing mobile TypeScript tooling..."
1005
- run_quiet_command "Mobile TypeScript tooling install" bash -lc "cd \"$ROOT_DIR\" && npm install --include=dev -w apps/mobile && npm dedupe"
1006
- ok "Mobile TypeScript tooling installed."
1007
- fi
1008
- fi
1009
-
1010
- if [[ "$AUTO_START" == "true" ]] && ! has_mobile_react_native_runtime; then
1011
- warn "React Native runtime appears incomplete (missing core runtime files)."
1012
- if [[ "$FLOW" == "quickstart" ]]; then
1013
- info "Running automatic dependency repair for QuickStart..."
1014
- repair_mobile_runtime_dependencies
1015
- elif confirm_prompt "Attempt dependency repair now?" "Y"; then
1016
- repair_mobile_runtime_dependencies
1017
- else
1018
- abort_wizard "Cannot auto-start Expo with incomplete React Native deps. Re-run setup and allow dependency repair."
1019
- fi
1020
- fi
1021
-
1022
- if [[ "$AUTO_START" == "true" ]] && [[ ! -d "$ROOT_DIR/node_modules/expo" ]]; then
1023
- abort_wizard "Expo dependency not found after installation step."
1024
- fi
1025
-
1026
- if [[ "$AUTO_START" == "true" ]] && ! has_mobile_react_native_runtime; then
1027
- abort_wizard "React Native runtime is still incomplete after repair. Run: npm install --include=dev --force && npm install --include=dev --force -w apps/mobile"
1028
- fi
1029
1246
  }
1030
1247
 
1031
1248
  cleanup_bridge() {
@@ -1159,57 +1376,14 @@ start_expo_background() {
1159
1376
  stream_expo_output_until_enter
1160
1377
  }
1161
1378
 
1162
- start_bridge_and_expo() {
1163
- trap cleanup_bridge EXIT INT TERM
1164
-
1165
- info "Starting bridge in background..."
1379
+ start_bridge_foreground() {
1380
+ rail_echo "Starting bridge in foreground."
1381
+ rail_echo "Press Ctrl+C to stop the bridge."
1382
+ echo ""
1166
1383
  (
1167
1384
  cd "$ROOT_DIR"
1168
1385
  npm run secure:bridge
1169
- ) >"$BRIDGE_LOG" 2>&1 &
1170
- BRIDGE_PID="$!"
1171
-
1172
- sleep 1
1173
- if ! kill -0 "$BRIDGE_PID" >/dev/null 2>&1; then
1174
- fail "Bridge failed to start. Recent logs:"
1175
- tail -n 80 "$BRIDGE_LOG" || true
1176
- exit 1
1177
- fi
1178
-
1179
- echo "$BRIDGE_PID" > "$BRIDGE_PID_FILE"
1180
-
1181
- if [[ -n "$BRIDGE_HOST" ]] && [[ -n "$BRIDGE_PORT" ]]; then
1182
- local health_status=0
1183
- while true; do
1184
- if wait_for_bridge_health "$BRIDGE_HOST" "$BRIDGE_PORT"; then
1185
- ok "Bridge health check passed."
1186
- break
1187
- fi
1188
-
1189
- health_status=$?
1190
- if [[ "$health_status" -eq 1 ]]; then
1191
- fail "Bridge process exited before becoming healthy. Recent logs:"
1192
- tail -n 80 "$BRIDGE_LOG" || true
1193
- exit 1
1194
- fi
1195
-
1196
- warn "Bridge health check has not passed yet (timeout reached)."
1197
- warn "Initial Rust compile on fresh hosts can take several minutes."
1198
- if confirm_prompt "Keep waiting for bridge health before starting Expo?" "Y"; then
1199
- continue
1200
- fi
1201
-
1202
- warn "Continuing to Expo before bridge is healthy."
1203
- warn "If app requests fail, wait for bridge compile to finish and retry."
1204
- break
1205
- done
1206
- fi
1207
-
1208
- ok "Bridge is running in background (pid $BRIDGE_PID)."
1209
- info "Bridge logs: $BRIDGE_LOG"
1210
- echo ""
1211
- start_expo_background
1212
- return 0
1386
+ )
1213
1387
  }
1214
1388
 
1215
1389
  parse_args "$@"
@@ -1220,7 +1394,7 @@ if [[ ! -t 0 ]]; then
1220
1394
  fi
1221
1395
 
1222
1396
  echo "${BOLD}Clawdex onboarding${RESET}"
1223
- rail_echo "Guided setup for secure bridge + mobile launch."
1397
+ rail_echo "Guided setup for secure bridge launch."
1224
1398
  rail_echo "Project root: $ROOT_DIR"
1225
1399
  rail_echo "${DIM}Use Up/Down (or j/k) and Enter to select.${RESET}"
1226
1400
 
@@ -1248,20 +1422,64 @@ if [[ "$CONFIG_ACTION" == "reset" ]]; then
1248
1422
  fi
1249
1423
 
1250
1424
  if [[ "$CONFIG_ACTION" != "keep" ]]; then
1251
- section "Tailscale connectivity"
1252
- ensure_tailscale_cli
1253
- TAILSCALE_IP="$(resolve_tailscale_ip)"
1254
- ok "Tailscale IPv4 detected: $TAILSCALE_IP"
1425
+ section "Bridge network mode"
1426
+ choose_bridge_network_mode
1427
+
1428
+ case "$NETWORK_MODE" in
1429
+ tailscale)
1430
+ section "Tailscale connectivity"
1431
+ ensure_tailscale_cli
1432
+ TAILSCALE_IP="$(resolve_tailscale_ip)"
1433
+ BRIDGE_HOST="$TAILSCALE_IP"
1434
+ ok "Tailscale IPv4 detected: $TAILSCALE_IP"
1435
+ ;;
1436
+ local)
1437
+ section "Local network connectivity"
1438
+ BRIDGE_HOST="$(resolve_local_ip)"
1439
+ ok "Local LAN IPv4 detected: $BRIDGE_HOST"
1440
+ ;;
1441
+ *)
1442
+ abort_wizard "Unknown network mode '$NETWORK_MODE'."
1443
+ ;;
1444
+ esac
1255
1445
 
1256
1446
  section "Write secure config"
1257
- BRIDGE_HOST_OVERRIDE="$TAILSCALE_IP" "$SCRIPT_DIR/setup-secure-dev.sh"
1447
+ BRIDGE_NETWORK_MODE="$NETWORK_MODE" BRIDGE_HOST_OVERRIDE="$BRIDGE_HOST" "$SCRIPT_DIR/setup-secure-dev.sh"
1258
1448
  else
1259
1449
  ok "Keeping existing secure config."
1450
+ NETWORK_MODE="$(extract_env_value "$SECURE_ENV_FILE" "BRIDGE_NETWORK_MODE")"
1451
+ BRIDGE_HOST="$(extract_env_value "$SECURE_ENV_FILE" "BRIDGE_HOST")"
1452
+ if [[ -z "$NETWORK_MODE" ]]; then
1453
+ NETWORK_MODE="$(infer_network_mode_from_host "$BRIDGE_HOST")"
1454
+ fi
1455
+
1456
+ if [[ "$BRIDGE_HOST" == "0.0.0.0" ]] || [[ "$BRIDGE_HOST" == "::" ]] || [[ "$BRIDGE_HOST" == "[::]" ]]; then
1457
+ warn "Existing BRIDGE_HOST=$BRIDGE_HOST is a bind address, not a phone-connectable host."
1458
+ if [[ "$NETWORK_MODE" == "tailscale" ]]; then
1459
+ section "Tailscale connectivity"
1460
+ ensure_tailscale_cli
1461
+ BRIDGE_HOST="$(resolve_tailscale_ip)"
1462
+ TAILSCALE_IP="$BRIDGE_HOST"
1463
+ ok "Tailscale IPv4 detected: $BRIDGE_HOST"
1464
+ else
1465
+ NETWORK_MODE="local"
1466
+ section "Local network connectivity"
1467
+ BRIDGE_HOST="$(resolve_local_ip)"
1468
+ ok "Local LAN IPv4 detected: $BRIDGE_HOST"
1469
+ fi
1470
+
1471
+ section "Write secure config"
1472
+ BRIDGE_NETWORK_MODE="$NETWORK_MODE" BRIDGE_HOST_OVERRIDE="$BRIDGE_HOST" "$SCRIPT_DIR/setup-secure-dev.sh"
1473
+ fi
1260
1474
  fi
1261
1475
 
1262
1476
  section "Phone pairing"
1263
- confirm_phone_tailscale_ready
1264
- ok "Phone Tailscale readiness confirmed."
1477
+ confirm_phone_network_ready
1478
+ if [[ "$NETWORK_MODE" == "tailscale" ]]; then
1479
+ ok "Phone Tailscale readiness confirmed."
1480
+ else
1481
+ ok "Phone local network readiness confirmed."
1482
+ fi
1265
1483
 
1266
1484
  if [[ ! -f "$SECURE_ENV_FILE" ]]; then
1267
1485
  fail "error: $SECURE_ENV_FILE not found."
@@ -1271,32 +1489,34 @@ fi
1271
1489
 
1272
1490
  BRIDGE_HOST="$(extract_env_value "$SECURE_ENV_FILE" "BRIDGE_HOST")"
1273
1491
  BRIDGE_PORT="$(extract_env_value "$SECURE_ENV_FILE" "BRIDGE_PORT")"
1492
+ NETWORK_MODE="$(extract_env_value "$SECURE_ENV_FILE" "BRIDGE_NETWORK_MODE")"
1493
+ if [[ -z "$NETWORK_MODE" ]]; then
1494
+ NETWORK_MODE="$(infer_network_mode_from_host "$BRIDGE_HOST")"
1495
+ fi
1274
1496
  BRIDGE_HOST="${BRIDGE_HOST:-${TAILSCALE_IP:-127.0.0.1}}"
1275
1497
  BRIDGE_PORT="${BRIDGE_PORT:-8787}"
1276
1498
 
1277
1499
  section "Summary"
1500
+ rail_echo "Bridge mode: $NETWORK_MODE"
1278
1501
  rail_echo "Bridge endpoint: http://$BRIDGE_HOST:$BRIDGE_PORT"
1279
1502
  rail_echo "Secure env: $SECURE_ENV_FILE"
1280
- rail_echo "Bridge logs: $BRIDGE_LOG"
1281
1503
  if [[ "$FLOW" == "quickstart" ]]; then
1282
1504
  rail_echo "${DIM}Tip: re-run with Manual mode for full control at each step.${RESET}"
1283
1505
  fi
1284
1506
 
1285
1507
  section "Hatch"
1286
1508
  if [[ "$AUTO_START" == "true" ]]; then
1287
- EXPO_MODE="$TARGET_PLATFORM"
1288
1509
  rail_echo "Auto-start enabled."
1289
- rail_echo "Launching bridge + Expo ($EXPO_MODE)..."
1290
- start_bridge_and_expo
1510
+ rail_echo "Launching bridge in foreground..."
1511
+ start_bridge_foreground
1291
1512
  exit $?
1292
1513
  else
1293
1514
  rail_echo "Auto-start disabled by --no-start."
1294
- rail_echo "Skipping bridge/Expo launch."
1515
+ rail_echo "Skipping bridge launch."
1295
1516
  fi
1296
1517
 
1297
1518
  section "Next steps"
1298
1519
  rail_echo "1) cd $ROOT_DIR && npm run secure:bridge"
1299
- rail_echo "2) cd $ROOT_DIR && npm run mobile"
1300
- rail_echo "3) Optional: npm run ios (or) npm run android"
1520
+ rail_echo "2) Open the iOS app and use onboarding to connect (mode + URL + token QR)."
1301
1521
  rail_blank
1302
1522
  rail_echo "${DIM}You can rerun this anytime: npm run setup:wizard${RESET}"
@@ -34,5 +34,11 @@ set -a
34
34
  source "$SECURE_ENV_FILE"
35
35
  set +a
36
36
 
37
+ BRIDGE_RUN_MODE="${BRIDGE_RUN_MODE:-release}"
38
+
37
39
  cd "$ROOT_DIR"
38
- exec npm run -w @codex/rust-bridge dev
40
+ if [[ "$BRIDGE_RUN_MODE" == "dev" ]]; then
41
+ exec npm run -w @codex/rust-bridge dev
42
+ fi
43
+
44
+ exec npm run -w @codex/rust-bridge start
@@ -0,0 +1,63 @@
1
+ #!/usr/bin/env node
2
+
3
+ const fs = require('fs');
4
+ const path = require('path');
5
+
6
+ const rootDir = path.resolve(__dirname, '..');
7
+
8
+ function readJson(relativePath) {
9
+ const fullPath = path.join(rootDir, relativePath);
10
+ return JSON.parse(fs.readFileSync(fullPath, 'utf8'));
11
+ }
12
+
13
+ function writeJson(relativePath, value) {
14
+ const fullPath = path.join(rootDir, relativePath);
15
+ fs.writeFileSync(fullPath, `${JSON.stringify(value, null, 2)}\n`);
16
+ }
17
+
18
+ function updateCargoTomlVersion(relativePath, version) {
19
+ const fullPath = path.join(rootDir, relativePath);
20
+ const current = fs.readFileSync(fullPath, 'utf8');
21
+ const next = current.replace(/^version\s*=\s*".*"$/m, `version = "${version}"`);
22
+ if (next === current) {
23
+ throw new Error(`Could not find Cargo.toml version field in ${relativePath}`);
24
+ }
25
+ fs.writeFileSync(fullPath, next);
26
+ }
27
+
28
+ function syncVersions() {
29
+ const rootPkg = readJson('package.json');
30
+ const version = rootPkg.version;
31
+ if (typeof version !== 'string' || version.trim().length === 0) {
32
+ throw new Error('Root package.json version is empty.');
33
+ }
34
+
35
+ const mobilePkgPath = 'apps/mobile/package.json';
36
+ const mobilePkg = readJson(mobilePkgPath);
37
+ mobilePkg.version = version;
38
+ writeJson(mobilePkgPath, mobilePkg);
39
+
40
+ const bridgePkgPath = 'services/rust-bridge/package.json';
41
+ const bridgePkg = readJson(bridgePkgPath);
42
+ bridgePkg.version = version;
43
+ writeJson(bridgePkgPath, bridgePkg);
44
+
45
+ updateCargoTomlVersion('services/rust-bridge/Cargo.toml', version);
46
+
47
+ const appJsonPath = 'apps/mobile/app.json';
48
+ const appConfig = readJson(appJsonPath);
49
+ if (!appConfig.expo || typeof appConfig.expo !== 'object') {
50
+ throw new Error('apps/mobile/app.json is missing expo config.');
51
+ }
52
+ appConfig.expo.version = version;
53
+ writeJson(appJsonPath, appConfig);
54
+
55
+ console.log(`Synchronized app and bridge versions to ${version}`);
56
+ }
57
+
58
+ try {
59
+ syncVersions();
60
+ } catch (error) {
61
+ console.error(String(error instanceof Error ? error.message : error));
62
+ process.exit(1);
63
+ }
@@ -3,7 +3,7 @@ BRIDGE_PORT=8787
3
3
  BRIDGE_WORKDIR=/path/to/target/repo
4
4
  BRIDGE_AUTH_TOKEN=change-me
5
5
  BRIDGE_ALLOW_INSECURE_NO_AUTH=false
6
- BRIDGE_ALLOW_QUERY_TOKEN_AUTH=false
6
+ BRIDGE_ALLOW_QUERY_TOKEN_AUTH=true
7
7
  BRIDGE_ALLOW_OUTSIDE_ROOT_CWD=true
8
8
  BRIDGE_DISABLE_TERMINAL_EXEC=false
9
9
  BRIDGE_TERMINAL_ALLOWED_COMMANDS=pwd,ls,cat,git