clawdex-mobile 2.0.1 → 4.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 (81) hide show
  1. package/.github/workflows/ci.yml +4 -3
  2. package/.github/workflows/npm-release.yml +62 -2
  3. package/.github/workflows/pages.yml +41 -0
  4. package/AGENTS.md +263 -110
  5. package/README.md +15 -4
  6. package/apps/mobile/.env.example +2 -2
  7. package/apps/mobile/App.tsx +175 -14
  8. package/apps/mobile/app.json +27 -9
  9. package/apps/mobile/eas.json +14 -4
  10. package/apps/mobile/package.json +14 -13
  11. package/apps/mobile/src/api/__tests__/chatMapping.test.ts +219 -0
  12. package/apps/mobile/src/api/__tests__/client.test.ts +587 -6
  13. package/apps/mobile/src/api/__tests__/ws.test.ts +27 -0
  14. package/apps/mobile/src/api/account.ts +47 -0
  15. package/apps/mobile/src/api/chatMapping.ts +435 -18
  16. package/apps/mobile/src/api/client.ts +321 -36
  17. package/apps/mobile/src/api/rateLimits.ts +143 -0
  18. package/apps/mobile/src/api/types.ts +107 -0
  19. package/apps/mobile/src/api/ws.ts +10 -1
  20. package/apps/mobile/src/components/ChatHeader.tsx +12 -12
  21. package/apps/mobile/src/components/ChatInput.tsx +154 -88
  22. package/apps/mobile/src/components/ChatMessage.tsx +548 -93
  23. package/apps/mobile/src/components/ComposerUsageLimits.tsx +167 -0
  24. package/apps/mobile/src/components/SelectionSheet.tsx +466 -0
  25. package/apps/mobile/src/components/ToolBlock.tsx +17 -15
  26. package/apps/mobile/src/components/VoiceRecordingWaveform.tsx +181 -0
  27. package/apps/mobile/src/components/WorkspacePickerModal.tsx +812 -0
  28. package/apps/mobile/src/components/__tests__/chat-input-layout.test.ts +35 -0
  29. package/apps/mobile/src/components/__tests__/chatImageSource.test.ts +44 -0
  30. package/apps/mobile/src/components/__tests__/composerUsageLimits.test.ts +138 -0
  31. package/apps/mobile/src/components/__tests__/voiceWaveform.test.ts +31 -0
  32. package/apps/mobile/src/components/chat-input-layout.ts +59 -0
  33. package/apps/mobile/src/components/chatImageSource.ts +86 -0
  34. package/apps/mobile/src/components/usageLimitBadges.ts +109 -0
  35. package/apps/mobile/src/components/voiceWaveform.ts +46 -0
  36. package/apps/mobile/src/config.ts +9 -2
  37. package/apps/mobile/src/hooks/useVoiceRecorder.ts +8 -1
  38. package/apps/mobile/src/navigation/DrawerContent.tsx +607 -457
  39. package/apps/mobile/src/navigation/__tests__/chatThreadTree.test.ts +89 -0
  40. package/apps/mobile/src/navigation/__tests__/drawerChats.test.ts +65 -0
  41. package/apps/mobile/src/navigation/chatThreadTree.ts +191 -0
  42. package/apps/mobile/src/navigation/drawerChats.ts +9 -0
  43. package/apps/mobile/src/screens/GitScreen.tsx +2 -0
  44. package/apps/mobile/src/screens/MainScreen.tsx +4239 -1237
  45. package/apps/mobile/src/screens/OnboardingScreen.tsx +924 -310
  46. package/apps/mobile/src/screens/SettingsScreen.tsx +256 -226
  47. package/apps/mobile/src/screens/TerminalScreen.tsx +2 -5
  48. package/apps/mobile/src/screens/__tests__/agentThreadDisplay.test.ts +80 -0
  49. package/apps/mobile/src/screens/__tests__/agentThreads.test.ts +170 -0
  50. package/apps/mobile/src/screens/__tests__/planCardState.test.ts +88 -0
  51. package/apps/mobile/src/screens/__tests__/subAgentTranscript.test.ts +102 -0
  52. package/apps/mobile/src/screens/__tests__/transcriptMessages.test.ts +97 -0
  53. package/apps/mobile/src/screens/agentThreadDisplay.ts +261 -0
  54. package/apps/mobile/src/screens/agentThreads.ts +167 -0
  55. package/apps/mobile/src/screens/planCardState.ts +40 -0
  56. package/apps/mobile/src/screens/subAgentTranscript.ts +149 -0
  57. package/apps/mobile/src/screens/transcriptMessages.ts +102 -0
  58. package/apps/mobile/src/theme.ts +6 -12
  59. package/bin/clawdex.js +7 -6
  60. package/codex-rust-bridge +0 -0
  61. package/codex-rust-bridge.exe +0 -0
  62. package/docs/codex-app-server-cli-gap-tracker.md +14 -5
  63. package/docs/privacy-policy.md +54 -0
  64. package/docs/setup-and-operations.md +21 -15
  65. package/docs/terms-of-service.md +33 -0
  66. package/docs/troubleshooting.md +15 -19
  67. package/package.json +6 -5
  68. package/scripts/bridge-binary.js +194 -0
  69. package/scripts/setup-wizard.sh +17 -186
  70. package/scripts/start-bridge-secure.js +240 -0
  71. package/scripts/start-bridge-secure.sh +1 -40
  72. package/services/mac-bridge/package.json +6 -6
  73. package/services/rust-bridge/Cargo.lock +56 -47
  74. package/services/rust-bridge/Cargo.toml +1 -1
  75. package/services/rust-bridge/package.json +1 -1
  76. package/services/rust-bridge/src/main.rs +517 -9
  77. package/site/index.html +54 -0
  78. package/site/privacy/index.html +80 -0
  79. package/site/styles.css +135 -0
  80. package/site/support/index.html +51 -0
  81. package/site/terms/index.html +68 -0
@@ -28,16 +28,7 @@ NETWORK_MODE=""
28
28
  TAILSCALE_IP=""
29
29
  BRIDGE_HOST=""
30
30
  BRIDGE_PORT=""
31
- EXPO_MODE="mobile"
32
31
  AUTO_START="true"
33
- TARGET_PLATFORM="mobile"
34
- BRIDGE_PID=""
35
- EXPO_PID=""
36
- BRIDGE_LOG="$ROOT_DIR/.bridge.log"
37
- EXPO_LOG="$ROOT_DIR/.expo.log"
38
- BRIDGE_PID_FILE="$ROOT_DIR/.bridge.pid"
39
- EXPO_PID_FILE="$ROOT_DIR/.expo.pid"
40
- KEEP_SERVICES_RUNNING="false"
41
32
  SECURE_ENV_FILE="$ROOT_DIR/.env.secure"
42
33
  MENU_RESULT=""
43
34
  SECTION_COUNT=0
@@ -45,8 +36,6 @@ RAIL_GLYPH="${DIM}│${RESET}"
45
36
  RAIL_BRANCH="${DIM}├─${RESET}"
46
37
  RAIL_CHILD="${DIM}│${RESET}"
47
38
  OS_NAME="$(uname -s)"
48
- EXPO_STOP_PATTERN="$ROOT_DIR/.*/expo start|$ROOT_DIR/node_modules/.bin/expo start"
49
- BRIDGE_STOP_PATTERN="$ROOT_DIR/services/rust-bridge|codex-rust-bridge|@codex/rust-bridge"
50
39
 
51
40
  rail_echo() { printf "%s %s\n" "$RAIL_GLYPH" "$1"; }
52
41
  rail_blank() { printf "%s\n" "$RAIL_GLYPH"; }
@@ -81,46 +70,6 @@ run_quiet_command() {
81
70
  return 1
82
71
  }
83
72
 
84
- list_matching_pids() {
85
- local pattern="$1"
86
- pgrep -f "$pattern" 2>/dev/null || true
87
- }
88
-
89
- stop_process_group_by_pattern() {
90
- local label="$1"
91
- local pattern="$2"
92
- local pids=""
93
- local remaining=""
94
- local pid=""
95
-
96
- pids="$(list_matching_pids "$pattern")"
97
- if [[ -z "$pids" ]]; then
98
- return 0
99
- fi
100
-
101
- info "Stopping $label process group: $pids"
102
- while IFS= read -r pid; do
103
- [[ -z "$pid" ]] && continue
104
- kill -TERM "$pid" >/dev/null 2>&1 || true
105
- done <<<"$pids"
106
-
107
- sleep 1
108
-
109
- while IFS= read -r pid; do
110
- [[ -z "$pid" ]] && continue
111
- if kill -0 "$pid" >/dev/null 2>&1; then
112
- remaining+="$pid "
113
- kill -KILL "$pid" >/dev/null 2>&1 || true
114
- fi
115
- done <<<"$pids"
116
-
117
- if [[ -n "${remaining// }" ]]; then
118
- warn "Force-stopped $label processes: $remaining"
119
- fi
120
-
121
- return 0
122
- }
123
-
124
73
  print_usage() {
125
74
  cat <<EOF
126
75
  Usage: $(basename "$0") [options]
@@ -823,11 +772,22 @@ ensure_core_tools() {
823
772
  fail "openssl is required."
824
773
  exit 1
825
774
  fi
775
+ }
776
+
777
+ has_packaged_bridge_binary() {
778
+ node "$SCRIPT_DIR/bridge-binary.js" has-current-packaged >/dev/null 2>&1
779
+ }
826
780
 
781
+ ensure_local_rust_build_toolchain() {
827
782
  if ! ensure_or_install_command "cc" "C compiler/linker (cc)" install_c_toolchain_cli "Y"; then
828
783
  fail "C compiler/linker is required for Rust crate builds."
829
784
  exit 1
830
785
  fi
786
+
787
+ if ! ensure_or_install_command "cargo" "Rust/Cargo toolchain" install_rust_toolchain "Y"; then
788
+ fail "Rust/Cargo is required for the rust bridge."
789
+ exit 1
790
+ fi
831
791
  }
832
792
 
833
793
  ensure_codex_cli() {
@@ -1245,137 +1205,6 @@ install_project_dependencies() {
1245
1205
  fi
1246
1206
  }
1247
1207
 
1248
- cleanup_bridge() {
1249
- if [[ "$KEEP_SERVICES_RUNNING" == "true" ]]; then
1250
- return
1251
- fi
1252
-
1253
- stop_process_group_by_pattern "Expo" "$EXPO_STOP_PATTERN"
1254
- stop_process_group_by_pattern "Rust bridge" "$BRIDGE_STOP_PATTERN"
1255
-
1256
- rm -f "$BRIDGE_PID_FILE" "$EXPO_PID_FILE"
1257
- }
1258
-
1259
- wait_for_bridge_health() {
1260
- local host="$1"
1261
- local port="$2"
1262
- local max_wait_secs="${BRIDGE_HEALTH_WAIT_SECS:-300}"
1263
- local elapsed=0
1264
-
1265
- if ! command -v curl >/dev/null 2>&1; then
1266
- return 0
1267
- fi
1268
-
1269
- while true; do
1270
- if curl --max-time 1 -fsS "http://$host:$port/health" >/dev/null 2>&1; then
1271
- return 0
1272
- fi
1273
-
1274
- if [[ -n "$BRIDGE_PID" ]] && ! kill -0 "$BRIDGE_PID" >/dev/null 2>&1; then
1275
- return 1
1276
- fi
1277
-
1278
- sleep 1
1279
- elapsed=$((elapsed + 1))
1280
- if (( elapsed % 5 == 0 )); then
1281
- info "Waiting for bridge health... ${elapsed}s elapsed"
1282
- fi
1283
-
1284
- if (( elapsed >= max_wait_secs )); then
1285
- return 2
1286
- fi
1287
- done
1288
- }
1289
-
1290
- stream_expo_output_until_enter() {
1291
- local tail_pid=""
1292
- local _user_input=""
1293
- local waited=0
1294
- local max_wait_secs="${EXPO_OUTPUT_WAIT_SECS:-90}"
1295
- local -a spinner_frames=("-" "\\" "|" "/")
1296
- local frame="-"
1297
-
1298
- rail_echo "Expo output is live below."
1299
- rail_echo "Press Enter to exit onboarding and keep Expo + bridge running (Ctrl+D also detaches)."
1300
- echo ""
1301
-
1302
- if [[ ! -s "$EXPO_LOG" ]]; then
1303
- while true; do
1304
- if [[ -s "$EXPO_LOG" ]]; then
1305
- printf "\r\033[2K%s Expo output started.\n" "$RAIL_GLYPH"
1306
- break
1307
- fi
1308
-
1309
- if [[ -n "$EXPO_PID" ]] && ! kill -0 "$EXPO_PID" >/dev/null 2>&1; then
1310
- printf "\r\033[2K%s Expo process exited before output appeared.\n" "$RAIL_GLYPH"
1311
- return 1
1312
- fi
1313
-
1314
- if (( waited >= max_wait_secs )); then
1315
- printf "\r\033[2K%s Still waiting for Expo output...\n" "$RAIL_GLYPH"
1316
- break
1317
- fi
1318
-
1319
- frame="${spinner_frames[$((waited % ${#spinner_frames[@]}))]}"
1320
- printf "\r\033[2K%s Waiting for Expo output %s %ss" "$RAIL_GLYPH" "$frame" "$waited"
1321
- sleep 1
1322
- waited=$((waited + 1))
1323
- done
1324
- fi
1325
-
1326
- tail -n +1 -f "$EXPO_LOG" &
1327
- tail_pid="$!"
1328
- if ! IFS= read -r _user_input 2>/dev/null; then
1329
- rail_echo "Input stream closed; detaching onboarding."
1330
- fi
1331
- kill -TERM "$tail_pid" >/dev/null 2>&1 || true
1332
- wait "$tail_pid" 2>/dev/null || true
1333
- return 0
1334
- }
1335
-
1336
- start_expo_process_background() {
1337
- local log_file="$1"
1338
-
1339
- if command -v script >/dev/null 2>&1; then
1340
- if [[ "$OS_NAME" == "Darwin" ]]; then
1341
- # Feed script from a never-ending pipe to avoid EOF-triggered Expo shutdown.
1342
- nohup bash -lc "cd \"$ROOT_DIR\" && tail -f /dev/null | script -q \"$log_file\" npm run \"$EXPO_MODE\"" >/dev/null 2>&1 &
1343
- EXPO_PID="$!"
1344
- return 0
1345
- fi
1346
-
1347
- # util-linux script uses -c for command mode.
1348
- nohup bash -lc "cd \"$ROOT_DIR\" && tail -f /dev/null | script -q -f -c \"npm run $EXPO_MODE\" \"$log_file\"" >/dev/null 2>&1 &
1349
- EXPO_PID="$!"
1350
- return 0
1351
- fi
1352
-
1353
- nohup bash -lc "cd \"$ROOT_DIR\" && npm run \"$EXPO_MODE\"" >"$log_file" 2>&1 </dev/null &
1354
- EXPO_PID="$!"
1355
- }
1356
-
1357
- start_expo_background() {
1358
- info "Starting Expo ($EXPO_MODE) in background..."
1359
- : >"$EXPO_LOG"
1360
- start_expo_process_background "$EXPO_LOG"
1361
- echo "$EXPO_PID" > "$EXPO_PID_FILE"
1362
-
1363
- sleep 1
1364
- if ! kill -0 "$EXPO_PID" >/dev/null 2>&1; then
1365
- fail "Expo failed to start. Recent logs:"
1366
- tail -n 80 "$EXPO_LOG" || true
1367
- exit 1
1368
- fi
1369
-
1370
- KEEP_SERVICES_RUNNING="true"
1371
- ok "Bridge + Expo are running in background."
1372
- rail_echo "Bridge logs: $BRIDGE_LOG"
1373
- rail_echo "Expo logs: $EXPO_LOG"
1374
- rail_echo "To stop later:"
1375
- rail_echo "pkill -TERM -f '$EXPO_STOP_PATTERN'; pkill -TERM -f '$BRIDGE_STOP_PATTERN'"
1376
- stream_expo_output_until_enter
1377
- }
1378
-
1379
1208
  start_bridge_foreground() {
1380
1209
  rail_echo "Starting bridge in foreground."
1381
1210
  rail_echo "Press Ctrl+C to stop the bridge."
@@ -1406,9 +1235,11 @@ choose_flow
1406
1235
 
1407
1236
  section "Prerequisites"
1408
1237
  ensure_core_tools
1409
- if ! ensure_or_install_command "cargo" "Rust/Cargo toolchain" install_rust_toolchain "Y"; then
1410
- fail "Rust/Cargo is required for the rust bridge."
1411
- exit 1
1238
+ if has_packaged_bridge_binary; then
1239
+ ok "Found packaged Rust bridge binary for this host."
1240
+ else
1241
+ info "No packaged bridge binary found for this host. Falling back to local Rust build."
1242
+ ensure_local_rust_build_toolchain
1412
1243
  fi
1413
1244
  ensure_codex_cli
1414
1245
  install_project_dependencies
@@ -1517,6 +1348,6 @@ fi
1517
1348
 
1518
1349
  section "Next steps"
1519
1350
  rail_echo "1) cd $ROOT_DIR && npm run secure:bridge"
1520
- rail_echo "2) Open the iOS app and use onboarding to connect (mode + URL + token QR)."
1351
+ rail_echo "2) Open the mobile app and use onboarding to connect (URL + token QR)."
1521
1352
  rail_blank
1522
1353
  rail_echo "${DIM}You can rerun this anytime: npm run setup:wizard${RESET}"
@@ -0,0 +1,240 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+
4
+ const { spawn, spawnSync } = require("node:child_process");
5
+ const fs = require("node:fs");
6
+ const os = require("node:os");
7
+ const path = require("node:path");
8
+
9
+ const {
10
+ builtBinaryPath,
11
+ ensureExecutable,
12
+ packagedBinaryPath,
13
+ resolveRuntimeTarget,
14
+ } = require("./bridge-binary");
15
+
16
+ function resolveRootDir() {
17
+ let rootDir = process.env.INIT_CWD ? path.resolve(process.env.INIT_CWD) : path.resolve(__dirname, "..");
18
+ if (!fs.existsSync(path.join(rootDir, "package.json"))) {
19
+ rootDir = path.resolve(__dirname, "..");
20
+ }
21
+ return rootDir;
22
+ }
23
+
24
+ function readEnvFile(filePath) {
25
+ const contents = fs.readFileSync(filePath, "utf8");
26
+ const nextEnv = {};
27
+
28
+ for (const rawLine of contents.split(/\r?\n/)) {
29
+ const line = rawLine.trim();
30
+ if (!line || line.startsWith("#")) {
31
+ continue;
32
+ }
33
+
34
+ const match = line.match(/^(?:export\s+)?([A-Za-z_][A-Za-z0-9_]*)=(.*)$/);
35
+ if (!match) {
36
+ continue;
37
+ }
38
+
39
+ const [, key, rawValue] = match;
40
+ let value = rawValue;
41
+ if (
42
+ (value.startsWith('"') && value.endsWith('"')) ||
43
+ (value.startsWith("'") && value.endsWith("'"))
44
+ ) {
45
+ value = value.slice(1, -1);
46
+ }
47
+ nextEnv[key] = value;
48
+ }
49
+
50
+ return nextEnv;
51
+ }
52
+
53
+ function commandExists(command) {
54
+ const checker = process.platform === "win32" ? "where" : "which";
55
+ const result = spawnSync(checker, [command], { stdio: "ignore" });
56
+ return result.status === 0;
57
+ }
58
+
59
+ function walkFiles(directory) {
60
+ const entries = fs.readdirSync(directory, { withFileTypes: true });
61
+ const files = [];
62
+
63
+ for (const entry of entries) {
64
+ const entryPath = path.join(directory, entry.name);
65
+ if (entry.isDirectory()) {
66
+ files.push(...walkFiles(entryPath));
67
+ continue;
68
+ }
69
+ if (entry.isFile()) {
70
+ files.push(entryPath);
71
+ }
72
+ }
73
+
74
+ return files;
75
+ }
76
+
77
+ function isBuiltBinaryFresh(rootDir, binaryPath) {
78
+ if (!fs.existsSync(binaryPath)) {
79
+ return false;
80
+ }
81
+
82
+ const binaryMtime = fs.statSync(binaryPath).mtimeMs;
83
+ const watchPaths = [
84
+ path.join(rootDir, "services", "rust-bridge", "Cargo.toml"),
85
+ path.join(rootDir, "services", "rust-bridge", "Cargo.lock"),
86
+ ];
87
+ const sourceDir = path.join(rootDir, "services", "rust-bridge", "src");
88
+
89
+ if (fs.existsSync(sourceDir)) {
90
+ watchPaths.push(...walkFiles(sourceDir));
91
+ }
92
+
93
+ return watchPaths.every((watchPath) => {
94
+ if (!fs.existsSync(watchPath)) {
95
+ return true;
96
+ }
97
+ return fs.statSync(watchPath).mtimeMs <= binaryMtime;
98
+ });
99
+ }
100
+
101
+ function printMissingCompilerHint() {
102
+ if (process.platform === "win32") {
103
+ console.error("Install Visual Studio Build Tools (Desktop development with C++) and Rust, then retry.");
104
+ return;
105
+ }
106
+ if (commandExists("apt-get")) {
107
+ console.error("Install on Ubuntu/Debian: sudo apt-get update && sudo apt-get install -y build-essential");
108
+ return;
109
+ }
110
+ if (commandExists("dnf")) {
111
+ console.error("Install on Fedora/RHEL: sudo dnf install -y gcc gcc-c++ make");
112
+ return;
113
+ }
114
+ if (commandExists("yum")) {
115
+ console.error("Install on CentOS/RHEL: sudo yum install -y gcc gcc-c++ make");
116
+ return;
117
+ }
118
+ if (commandExists("apk")) {
119
+ console.error("Install on Alpine: sudo apk add build-base");
120
+ return;
121
+ }
122
+ if (commandExists("xcode-select")) {
123
+ console.error("Install on macOS: xcode-select --install");
124
+ }
125
+ }
126
+
127
+ function spawnAndRelay(command, args, options) {
128
+ const child = spawn(command, args, {
129
+ stdio: "inherit",
130
+ ...options,
131
+ });
132
+
133
+ child.on("error", (error) => {
134
+ console.error(`error: failed to start ${command}: ${error.message}`);
135
+ process.exit(1);
136
+ });
137
+
138
+ child.on("exit", (code, signal) => {
139
+ if (signal) {
140
+ process.kill(process.pid, signal);
141
+ return;
142
+ }
143
+ process.exit(code ?? 0);
144
+ });
145
+ }
146
+
147
+ function buildBridgeFromSource(rootDir, env) {
148
+ const cargoCmd = "cargo";
149
+ const args = ["build", "--release", "--locked"];
150
+ const result = spawnSync(cargoCmd, args, {
151
+ cwd: path.join(rootDir, "services", "rust-bridge"),
152
+ env,
153
+ stdio: "inherit",
154
+ });
155
+
156
+ if (result.error) {
157
+ console.error(`error: failed to run cargo build: ${result.error.message}`);
158
+ process.exit(1);
159
+ }
160
+
161
+ if ((result.status ?? 1) !== 0) {
162
+ process.exit(result.status ?? 1);
163
+ }
164
+ }
165
+
166
+ function start() {
167
+ const rootDir = resolveRootDir();
168
+ const secureEnvFile = path.join(rootDir, ".env.secure");
169
+ if (!fs.existsSync(secureEnvFile)) {
170
+ console.error(`error: ${secureEnvFile} not found. Run: npm run secure:setup`);
171
+ process.exit(1);
172
+ }
173
+
174
+ const fileEnv = readEnvFile(secureEnvFile);
175
+ const env = { ...fileEnv, ...process.env };
176
+ const devMode = process.argv.includes("--dev") || env.BRIDGE_RUN_MODE === "dev";
177
+ const forceSourceBuild = env.CLAWDEX_BRIDGE_FORCE_SOURCE_BUILD === "true";
178
+
179
+ if (devMode) {
180
+ if (!commandExists("cargo")) {
181
+ console.error("error: missing Rust/Cargo toolchain for dev bridge mode.");
182
+ process.exit(1);
183
+ }
184
+
185
+ spawnAndRelay("cargo", ["run"], {
186
+ cwd: path.join(rootDir, "services", "rust-bridge"),
187
+ env,
188
+ });
189
+ return;
190
+ }
191
+
192
+ const overrideBinary = env.CLAWDEX_BRIDGE_BINARY ? path.resolve(env.CLAWDEX_BRIDGE_BINARY) : "";
193
+ if (overrideBinary) {
194
+ if (!fs.existsSync(overrideBinary)) {
195
+ console.error(`error: CLAWDEX_BRIDGE_BINARY not found at ${overrideBinary}`);
196
+ process.exit(1);
197
+ }
198
+ ensureExecutable(overrideBinary);
199
+ spawnAndRelay(overrideBinary, [], { cwd: rootDir, env });
200
+ return;
201
+ }
202
+
203
+ const packagedBinary = packagedBinaryPath(rootDir, resolveRuntimeTarget());
204
+ if (!forceSourceBuild && packagedBinary && fs.existsSync(packagedBinary)) {
205
+ ensureExecutable(packagedBinary);
206
+ spawnAndRelay(packagedBinary, [], { cwd: rootDir, env });
207
+ return;
208
+ }
209
+
210
+ const builtBinary = builtBinaryPath(rootDir, os.platform());
211
+ if (isBuiltBinaryFresh(rootDir, builtBinary)) {
212
+ ensureExecutable(builtBinary);
213
+ spawnAndRelay(builtBinary, [], { cwd: rootDir, env });
214
+ return;
215
+ }
216
+
217
+ if (!commandExists("cargo")) {
218
+ console.error("error: no packaged bridge binary was found for this host, and cargo is not installed.");
219
+ console.error("Reinstall a published clawdex-mobile package with bundled bridge binaries, or install Rust and retry.");
220
+ process.exit(1);
221
+ }
222
+
223
+ if (process.platform !== "win32" && !commandExists("cc")) {
224
+ console.error("error: missing system C compiler/linker ('cc'). Rust bridge cannot compile without it.");
225
+ printMissingCompilerHint();
226
+ process.exit(1);
227
+ }
228
+
229
+ buildBridgeFromSource(rootDir, env);
230
+
231
+ if (!fs.existsSync(builtBinary)) {
232
+ console.error(`error: expected built bridge binary at ${builtBinary}, but it was not created.`);
233
+ process.exit(1);
234
+ }
235
+
236
+ ensureExecutable(builtBinary);
237
+ spawnAndRelay(builtBinary, [], { cwd: rootDir, env });
238
+ }
239
+
240
+ start();
@@ -2,43 +2,4 @@
2
2
  set -euo pipefail
3
3
 
4
4
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -L)"
5
- ROOT_DIR="${INIT_CWD:-$(cd "$SCRIPT_DIR/.." && pwd -L)}"
6
- if [[ ! -f "$ROOT_DIR/package.json" ]]; then
7
- ROOT_DIR="$(cd "$SCRIPT_DIR/.." && pwd -L)"
8
- fi
9
- SECURE_ENV_FILE="$ROOT_DIR/.env.secure"
10
-
11
- if [[ ! -f "$SECURE_ENV_FILE" ]]; then
12
- echo "error: $SECURE_ENV_FILE not found. Run: npm run secure:setup" >&2
13
- exit 1
14
- fi
15
-
16
- if ! command -v cc >/dev/null 2>&1; then
17
- echo "error: missing system C compiler/linker ('cc'). Rust bridge cannot compile without it." >&2
18
- if command -v apt-get >/dev/null 2>&1; then
19
- echo "Install on Ubuntu/Debian: sudo apt-get update && sudo apt-get install -y build-essential" >&2
20
- elif command -v dnf >/dev/null 2>&1; then
21
- echo "Install on Fedora/RHEL: sudo dnf install -y gcc gcc-c++ make" >&2
22
- elif command -v yum >/dev/null 2>&1; then
23
- echo "Install on CentOS/RHEL: sudo yum install -y gcc gcc-c++ make" >&2
24
- elif command -v apk >/dev/null 2>&1; then
25
- echo "Install on Alpine: sudo apk add build-base" >&2
26
- elif command -v xcode-select >/dev/null 2>&1; then
27
- echo "Install on macOS: xcode-select --install" >&2
28
- fi
29
- exit 1
30
- fi
31
-
32
- set -a
33
- # shellcheck disable=SC1090
34
- source "$SECURE_ENV_FILE"
35
- set +a
36
-
37
- BRIDGE_RUN_MODE="${BRIDGE_RUN_MODE:-release}"
38
-
39
- cd "$ROOT_DIR"
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
5
+ exec node "$SCRIPT_DIR/start-bridge-secure.js" "$@"
@@ -14,17 +14,17 @@
14
14
  "dependencies": {
15
15
  "@fastify/cors": "^10.0.2",
16
16
  "@fastify/websocket": "^11.0.2",
17
- "fastify": "^5.2.0",
17
+ "fastify": "^5.8.2",
18
18
  "zod": "^3.24.1"
19
19
  },
20
20
  "devDependencies": {
21
- "@types/node": "^22.10.1",
22
- "@typescript-eslint/eslint-plugin": "^8.18.1",
23
- "@typescript-eslint/parser": "^8.18.1",
24
- "eslint": "^10.0.0",
21
+ "@types/node": "^22.19.15",
22
+ "@typescript-eslint/eslint-plugin": "^8.57.1",
23
+ "@typescript-eslint/parser": "^8.57.1",
24
+ "eslint": "^10.0.3",
25
25
  "globals": "^16.5.0",
26
26
  "tsx": "^4.19.2",
27
27
  "typescript": "^5.7.2",
28
- "vitest": "^4.0.18"
28
+ "vitest": "^4.1.0"
29
29
  }
30
30
  }