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
package/scripts/setup-wizard.sh
CHANGED
|
@@ -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
|
|
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
|
|
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
|
|
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
|
|
981
|
-
|
|
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
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
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
|
-
)
|
|
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
|
|
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 "
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
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="$
|
|
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
|
-
|
|
1264
|
-
|
|
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
|
|
1290
|
-
|
|
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
|
|
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)
|
|
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
|
-
|
|
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=
|
|
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
|