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
@@ -1,4 +1,4 @@
1
- import { Platform, StyleSheet } from 'react-native';
1
+ import { Platform } from 'react-native';
2
2
 
3
3
  export const colors = {
4
4
  // Backgrounds
@@ -68,17 +68,11 @@ export const radius = {
68
68
  full: 999,
69
69
  };
70
70
 
71
- export const shadow = StyleSheet.create({
72
- sm: Platform.select({
73
- ios: {
74
- shadowColor: '#000',
75
- shadowOffset: { width: 0, height: 2 },
76
- shadowOpacity: 0.3,
77
- shadowRadius: 6,
78
- },
79
- default: { elevation: 3 },
80
- }) as object,
81
- });
71
+ export const shadow = {
72
+ sm: {
73
+ boxShadow: '0px 2px 6px rgba(0, 0, 0, 0.3)',
74
+ },
75
+ } as const;
82
76
 
83
77
  export const typography = {
84
78
  largeTitle: {
package/bin/clawdex.js CHANGED
@@ -10,13 +10,14 @@ function printUsage() {
10
10
  console.log(`Usage: clawdex <command> [options]
11
11
 
12
12
  Commands:
13
- init [--no-start] [--platform <mobile|ios|android>]
14
- Run interactive onboarding and secure setup.
15
- By default, this also starts bridge + Expo at the end.
16
- Use --no-start to skip auto-launch.
13
+ init [--no-start]
14
+ Run interactive bridge onboarding and secure setup.
15
+ By default, this also starts the secure bridge in the foreground.
16
+ Use --no-start to configure only.
17
17
 
18
18
  stop
19
- Stop bridge + Expo services for this project.
19
+ Stop bridge services for this project.
20
+ Also stops a local Expo dev process if one was started from this checkout.
20
21
 
21
22
  upgrade [--version <latest|x.y.z>] [--restart]
22
23
  update [--version <latest|x.y.z>] [--restart]
@@ -146,7 +147,7 @@ function runUpgrade(args) {
146
147
 
147
148
  console.log(`Current clawdex-mobile version: ${previousVersion}`);
148
149
  if (!options.noStop) {
149
- console.log("Stopping running bridge/Expo services before upgrade...");
150
+ console.log("Stopping running local services before upgrade...");
150
151
  const stopResult = runScript("stop-services.sh", [], { exitOnComplete: false });
151
152
  if (!stopResult.ok) {
152
153
  console.error("error: failed to stop services before upgrade.");
Binary file
Binary file
@@ -1,6 +1,6 @@
1
1
  # Codex App-Server + CLI Gap Tracker
2
2
 
3
- Last updated: February 24, 2026
3
+ Last updated: March 20, 2026
4
4
 
5
5
  ## Scope
6
6
  This tracker compares `clawdex-mobile` against current Codex app-server + CLI capabilities and records what still needs to be added.
@@ -49,12 +49,21 @@ Status: In progress (first implementation pass completed)
49
49
  ## Remaining Gaps (Beyond Gap 1)
50
50
 
51
51
  ### Gap 2: Slash Command Coverage in Mobile
52
- - Many Codex CLI slash commands are not exposed as first-class actions in mobile UX.
53
- - Mobile currently has partial command shortcuts and multiple unsupported command branches.
52
+ - Status: In progress
53
+ - Mobile now supports `/agent` thread switching, sub-agent transcript cards, and nested sub-agent rows in the drawer so spawned workers are visible in the main conversation model instead of being hidden behind generic tool-call traces.
54
+ - Mobile still does not expose the full Codex desktop slash-command surface as dedicated UI actions.
55
+ - Agent management remains lightweight:
56
+ - no dedicated create/configure sub-agent surface beyond `/agent`
57
+ - no richer per-agent live status/dashboard view
54
58
 
55
59
  ### Gap 3: Account/Auth UX
56
- - No dedicated mobile flow for login state, logout, account details, or rate limits.
57
- - Auth refresh is still operationally env-driven in bridge, not user-driven in app.
60
+ - Status: In progress
61
+ - Mobile Settings now exposes read-only account state via `account/read`, including ChatGPT email + plan type when available.
62
+ - Remaining:
63
+ - no dedicated standalone account screen outside Settings
64
+ - login/logout is not user-driven in mobile UI yet
65
+ - auth refresh is still operationally env-driven in bridge, not user-driven in app
66
+ - no API-key entry flow in mobile UI
58
67
 
59
68
  ### Gap 4: MCP + Tooling UX
60
69
  - No end-to-end UI for MCP server status, reload, OAuth login, or remote skills list/export.
@@ -0,0 +1,54 @@
1
+ # Privacy Policy
2
+
3
+ Last updated: March 6, 2026
4
+
5
+ ## Overview
6
+
7
+ Clawdex Mobile is a companion app for connecting to a bridge service that you run on your own machine. The app is designed for trusted private networking, such as LAN, VPN, or Tailscale. It is not a public multi-tenant shell service.
8
+
9
+ ## Information Processed
10
+
11
+ Clawdex Mobile can process:
12
+
13
+ - Chat prompts and assistant responses
14
+ - Bridge connection details you enter in the app
15
+ - Terminal command text and command output returned by your bridge
16
+ - Git repository status, diffs, commit messages, and related metadata
17
+ - File or image attachments you choose to send
18
+ - Voice input audio used for speech-to-text features
19
+
20
+ ## How Information Is Used
21
+
22
+ The app uses this information to:
23
+
24
+ - connect your phone to your self-hosted bridge
25
+ - display and continue assistant threads
26
+ - execute approved terminal and Git workflows on infrastructure you control
27
+ - upload user-selected files and images to your own workflow
28
+ - support optional voice-to-text input
29
+
30
+ ## Storage and Retention
31
+
32
+ Clawdex Mobile does not define a separate cloud retention layer for your project data. Data is generally stored by services and infrastructure you control, including your local bridge, repository, logs, caches, and any model providers or integrations that you configure.
33
+
34
+ ## Sharing
35
+
36
+ Clawdex Mobile does not include advertising SDKs. Data may be transmitted to third-party model or infrastructure providers only when you configure and use those services as part of your own setup.
37
+
38
+ ## Security
39
+
40
+ Security depends on how you configure your bridge and network. The app is intended for trusted-network use. You are responsible for protecting bridge tokens, provider credentials, repository access, and device access.
41
+
42
+ ## Your Responsibility
43
+
44
+ You are responsible for:
45
+
46
+ - operating the bridge only on systems you own or are authorized to control
47
+ - securing your network path and credentials
48
+ - reviewing commands, approvals, and repository actions before execution
49
+
50
+ ## Contact
51
+
52
+ For support, use the project support channel:
53
+
54
+ https://github.com/Mohit-Patil/clawdex-mobile/issues
@@ -6,11 +6,12 @@ This guide is the detailed companion to the top-level `README.md`.
6
6
 
7
7
  After `clawdex init`, expected sequence:
8
8
 
9
- 1. Bridge health passes (`Bridge health check passed.`)
10
- 2. Expo starts (`Starting Expo (mobile) in background...`)
11
- 3. You may briefly see a spinner (`Waiting for Expo output - ...`)
12
- 4. Expo output begins (`expo start --host lan`, QR block, `Metro waiting on exp://...`)
13
- 5. Press Enter to detach onboarding while Expo + bridge keep running
9
+ 1. Secure config is written or reused
10
+ 2. The bridge starts in the foreground
11
+ 3. A pairing QR is printed for the mobile app
12
+ 4. Bridge logs stay attached until you stop the process
13
+
14
+ Published npm releases bundle prebuilt bridge binaries for `darwin-arm64`, `linux-x64`, and `win32-x64`. On those hosts, normal bridge startup does not require a Rust compile. Intel Macs fall back to a local Rust build.
14
15
 
15
16
  ## Manual Secure Setup (No Wizard)
16
17
 
@@ -37,7 +38,13 @@ Creates/updates:
37
38
  npm run secure:bridge
38
39
  ```
39
40
 
40
- ### 4) Start Expo
41
+ ### 4) Pair from the mobile app
42
+
43
+ Open the installed mobile app on your phone, then scan the bridge QR. If needed, enter the bridge URL manually (for example `http://100.x.y.z:8787` or `http://192.168.x.y:8787`). The chosen bridge URL is stored on-device and can be changed later in Settings.
44
+
45
+ ## Local Mobile Development Only
46
+
47
+ If you are developing the mobile app from this repo, start Expo separately:
41
48
 
42
49
  ```bash
43
50
  npm run mobile
@@ -45,15 +52,12 @@ npm run mobile
45
52
 
46
53
  `npm run mobile` uses `scripts/start-expo.sh`, which sets `REACT_NATIVE_PACKAGER_HOSTNAME` from your secure config so QR resolution is predictable.
47
54
 
48
- On first app launch, onboarding will ask for your bridge URL (for example `http://100.x.y.z:8787` or `http://192.168.x.y:8787`). This URL is stored on-device and can be changed later in Settings.
49
-
50
55
  ## Advanced Knobs
51
56
 
52
57
  Optional environment variables:
53
58
 
54
59
  - `CLAWDEX_SETUP_VERBOSE=true` — show full installer output
55
- - `BRIDGE_HEALTH_WAIT_SECS=300` — max wait for bridge `/health`
56
- - `EXPO_OUTPUT_WAIT_SECS=90` — spinner timeout before streaming Expo logs
60
+ - `CLAWDEX_BRIDGE_FORCE_SOURCE_BUILD=true` — ignore a bundled bridge binary and build from local Rust sources instead
57
61
  - `EXPO_AUTO_REPAIR=true` — auto-repair React Native runtime on `npm run mobile`
58
62
  - `EXPO_CLEAR_CACHE=true` — force `expo start --clear` via `npm run mobile`
59
63
 
@@ -65,7 +69,8 @@ npm run teardown
65
69
 
66
70
  Can:
67
71
 
68
- - stop Expo + bridge
72
+ - stop the bridge
73
+ - also stop local Expo if you started it from this repo
69
74
  - remove generated artifacts (`.env.secure`, `.bridge.log`, `.expo.log`, pid files)
70
75
  - optionally reset `apps/mobile/.env` from `.env.example`
71
76
  - optionally run `tailscale down`
@@ -94,7 +99,7 @@ npm run teardown -- --yes
94
99
 
95
100
  | Variable | Purpose |
96
101
  |---|---|
97
- | `EXPO_PUBLIC_HOST_BRIDGE_TOKEN` | token sent by mobile client |
102
+ | `EXPO_PUBLIC_HOST_BRIDGE_TOKEN` | token used by local mobile dev builds |
98
103
  | `EXPO_PUBLIC_ALLOW_QUERY_TOKEN_AUTH` | query-token behavior for WebSocket auth fallback |
99
104
  | `EXPO_PUBLIC_ALLOW_INSECURE_REMOTE_BRIDGE` | suppress insecure-HTTP warning |
100
105
  | `EXPO_PUBLIC_PRIVACY_POLICY_URL` | in-app Privacy link |
@@ -140,9 +145,10 @@ Expected response contains `"status":"ok"`.
140
145
  ### Choosing Start Directory
141
146
 
142
147
  1. Open sidebar
143
- 2. Under `Start Directory`, pick:
144
- - `Bridge default workspace`, or
145
- - a discovered workspace path from existing chats
148
+ 2. Under `Start Directory`, pick either:
149
+ - `Bridge default workspace`
150
+ - a discovered workspace path from existing Codex chats
151
+ - any folder on the bridge host via the built-in folder browser or manual path entry
146
152
 
147
153
  Behavior:
148
154
 
@@ -0,0 +1,33 @@
1
+ # Terms of Service
2
+
3
+ Last updated: March 6, 2026
4
+
5
+ ## Overview
6
+
7
+ Clawdex Mobile is a companion client for interacting with a bridge service and repository environment that you control. By using the app, you agree to use it only with systems, repositories, and infrastructure that you own or are explicitly authorized to access.
8
+
9
+ ## Acceptable Use
10
+
11
+ You may not use Clawdex Mobile to access, control, or modify machines, repositories, or services without authorization. You are responsible for all actions initiated through the app, including approvals, terminal commands, Git actions, and file transfers.
12
+
13
+ ## Credentials and Access
14
+
15
+ You must keep bridge tokens, provider credentials, repository credentials, and device access secure. If you believe credentials have been exposed, you should rotate them immediately and stop using the affected bridge until it is secured.
16
+
17
+ ## Operational Risk
18
+
19
+ Clawdex Mobile can initiate workflows that change files, repositories, and machine state on your host environment. You are responsible for reviewing and approving actions before execution and for maintaining backups or recovery procedures appropriate to your environment.
20
+
21
+ ## Availability
22
+
23
+ The app depends on your bridge, network, repositories, model providers, and related services. Functionality may be unavailable if those dependencies are unavailable or misconfigured.
24
+
25
+ ## No Warranty
26
+
27
+ The software is provided on an as-is basis. You assume responsibility for how it is configured and used in your environment.
28
+
29
+ ## Contact
30
+
31
+ For support, use the project support channel:
32
+
33
+ https://github.com/Mohit-Patil/clawdex-mobile/issues
@@ -1,23 +1,16 @@
1
1
  # Troubleshooting
2
2
 
3
- ## Onboarding looks stuck before Expo logs appear
3
+ ## Bridge startup seems slow
4
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
- ```
5
+ - `clawdex init` no longer starts Expo for the shipped app.
6
+ - Published npm installs should use a bundled bridge binary on `darwin-arm64`, `linux-x64`, and `win32-x64`.
7
+ - If startup is still compiling Rust, you are usually on a source checkout, an unsupported host, or a package without bundled bridge binaries.
8
+ - The slow parts are usually npm dependency install/repair or the first Rust bridge build on source-based setups.
9
+ - If you want to skip the interactive wizard after initial setup, use `npm run secure:bridge`.
18
10
 
19
11
  ## Expo starts but QR/network is wrong
20
12
 
13
+ - This only applies when you are developing the mobile app locally from the repo.
21
14
  - Re-run `npm run secure:setup`
22
15
  - Confirm `.env.secure` has correct `BRIDGE_HOST`
23
16
  - Restart `npm run mobile`
@@ -38,13 +31,14 @@ npm run stop:services
38
31
 
39
32
  ## Bridge auth errors (`401`, invalid token)
40
33
 
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
34
+ - For the shipped mobile app, rescan the bridge QR or update the stored token in Settings.
35
+ - For a local dev build, also ensure `BRIDGE_AUTH_TOKEN` in `.env.secure` matches `EXPO_PUBLIC_HOST_BRIDGE_TOKEN` in `apps/mobile/.env`.
36
+ - Restart the bridge after token changes.
43
37
 
44
38
  ## Tailscale issues
45
39
 
46
40
  - Verify host and phone are on the same Tailscale network
47
- - Check host IP (`tailscale ip -4`) and mobile `.env` URL
41
+ - Check host IP (`tailscale ip -4`) and the bridge URL saved in the mobile app
48
42
 
49
43
  ## `codex` not found
50
44
 
@@ -53,6 +47,8 @@ npm run stop:services
53
47
 
54
48
  ## Bridge build fails with `linker 'cc' not found`
55
49
 
50
+ This only applies when the bridge is building from Rust source instead of using a bundled binary.
51
+
56
52
  Install C build tools:
57
53
 
58
54
  ```bash
@@ -106,7 +102,7 @@ npm run start -- --clear
106
102
 
107
103
  ## Plan mode errors (`RPC-32600` invalid `collaborationMode`)
108
104
 
109
- - Restart Expo and reload app bundle
105
+ - Restart the app and reconnect to the bridge
110
106
  - Ensure bridge/mobile revisions match
111
107
  - Run API test if needed:
112
108
 
@@ -118,4 +114,4 @@ npm run -w apps/mobile test -- --runInBand src/api/__tests__/client.test.ts
118
114
 
119
115
  - Ensure revision supports `turn/interrupt`
120
116
  - If run already finished, stop button disappears by design
121
- - Pull latest, restart bridge, reload Expo bundle
117
+ - Pull latest, restart bridge, then retry from the mobile app
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clawdex-mobile",
3
- "version": "2.0.1",
3
+ "version": "4.0.0",
4
4
  "homepage": "https://github.com/Mohit-Patil/clawdex-mobile#readme",
5
5
  "repository": {
6
6
  "type": "git",
@@ -21,11 +21,12 @@
21
21
  "ios": "./scripts/start-expo.sh ios",
22
22
  "android": "./scripts/start-expo.sh android",
23
23
  "bridge": "BRIDGE_WORKDIR=$(pwd) npm run -w @codex/rust-bridge dev",
24
+ "bridge:package:stage-current": "node ./scripts/bridge-binary.js stage-current",
24
25
  "setup:wizard": "./scripts/setup-wizard.sh",
25
26
  "stop:services": "./scripts/stop-services.sh",
26
27
  "secure:setup": "./scripts/setup-secure-dev.sh",
27
- "secure:bridge": "./scripts/start-bridge-secure.sh",
28
- "secure:bridge:dev": "BRIDGE_RUN_MODE=dev ./scripts/start-bridge-secure.sh",
28
+ "secure:bridge": "node ./scripts/start-bridge-secure.js",
29
+ "secure:bridge:dev": "node ./scripts/start-bridge-secure.js --dev",
29
30
  "bridge:ts": "BRIDGE_WORKDIR=$(pwd) npm run -w @codex/mac-bridge dev",
30
31
  "version:sync": "node scripts/sync-versions.js",
31
32
  "build": "npm run --workspaces build",
@@ -36,9 +37,9 @@
36
37
  },
37
38
  "dependencies": {
38
39
  "@types/react": "~19.2.10",
40
+ "expo": "^55.0.8",
39
41
  "react": "19.2.0",
40
42
  "react-dom": "19.2.0",
41
- "react-native": "0.83.2",
42
- "expo": "^55.0.2"
43
+ "react-native": "0.83.2"
43
44
  }
44
45
  }
@@ -0,0 +1,194 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+
4
+ const fs = require("node:fs");
5
+ const os = require("node:os");
6
+ const path = require("node:path");
7
+
8
+ const SUPPORTED_RUNTIME_TARGETS = {
9
+ "darwin-arm64": {
10
+ platform: "darwin",
11
+ arch: "arm64",
12
+ rustTarget: "aarch64-apple-darwin",
13
+ binaryName: "codex-rust-bridge",
14
+ },
15
+ "linux-x64": {
16
+ platform: "linux",
17
+ arch: "x64",
18
+ rustTarget: "x86_64-unknown-linux-gnu",
19
+ binaryName: "codex-rust-bridge",
20
+ },
21
+ "win32-x64": {
22
+ platform: "win32",
23
+ arch: "x64",
24
+ rustTarget: "x86_64-pc-windows-msvc",
25
+ binaryName: "codex-rust-bridge.exe",
26
+ },
27
+ };
28
+
29
+ function repoRoot() {
30
+ return path.resolve(__dirname, "..");
31
+ }
32
+
33
+ function resolveRuntimeTarget(platform = os.platform(), arch = os.arch()) {
34
+ for (const [target, metadata] of Object.entries(SUPPORTED_RUNTIME_TARGETS)) {
35
+ if (metadata.platform === platform && metadata.arch === arch) {
36
+ return target;
37
+ }
38
+ }
39
+ return null;
40
+ }
41
+
42
+ function binaryNameForTarget(target) {
43
+ const metadata = SUPPORTED_RUNTIME_TARGETS[target];
44
+ if (!metadata) {
45
+ throw new Error(`Unsupported bridge target '${target}'.`);
46
+ }
47
+ return metadata.binaryName;
48
+ }
49
+
50
+ function packagedBinaryPath(rootDir = repoRoot(), target = resolveRuntimeTarget()) {
51
+ if (!target) {
52
+ return null;
53
+ }
54
+ return path.join(rootDir, "vendor", "bridge-binaries", target, binaryNameForTarget(target));
55
+ }
56
+
57
+ function builtBinaryPath(rootDir = repoRoot(), platform = os.platform()) {
58
+ const binaryName = platform === "win32" ? "codex-rust-bridge.exe" : "codex-rust-bridge";
59
+ return path.join(rootDir, "services", "rust-bridge", "target", "release", binaryName);
60
+ }
61
+
62
+ function ensureExecutable(filePath) {
63
+ if (process.platform !== "win32") {
64
+ fs.chmodSync(filePath, 0o755);
65
+ }
66
+ }
67
+
68
+ function stageBinary({ rootDir = repoRoot(), from, target = resolveRuntimeTarget() }) {
69
+ if (!from) {
70
+ throw new Error("--from is required.");
71
+ }
72
+ if (!target) {
73
+ throw new Error("No supported runtime target for this host. Pass --target explicitly.");
74
+ }
75
+
76
+ const destination = packagedBinaryPath(rootDir, target);
77
+ if (!destination) {
78
+ throw new Error(`Unable to compute packaged path for target '${target}'.`);
79
+ }
80
+
81
+ fs.mkdirSync(path.dirname(destination), { recursive: true });
82
+ fs.copyFileSync(from, destination);
83
+ ensureExecutable(destination);
84
+ return destination;
85
+ }
86
+
87
+ function parseArgs(argv) {
88
+ const positional = [];
89
+ const flags = {};
90
+
91
+ for (let index = 0; index < argv.length; index += 1) {
92
+ const value = argv[index];
93
+ if (!value.startsWith("--")) {
94
+ positional.push(value);
95
+ continue;
96
+ }
97
+
98
+ const key = value.slice(2);
99
+ const next = argv[index + 1];
100
+ if (!next || next.startsWith("--")) {
101
+ flags[key] = true;
102
+ continue;
103
+ }
104
+ flags[key] = next;
105
+ index += 1;
106
+ }
107
+
108
+ return { positional, flags };
109
+ }
110
+
111
+ function main() {
112
+ const { positional, flags } = parseArgs(process.argv.slice(2));
113
+ const command = positional[0];
114
+ const rootDir = flags.root ? path.resolve(flags.root) : repoRoot();
115
+
116
+ switch (command) {
117
+ case "current-target": {
118
+ const target = resolveRuntimeTarget();
119
+ if (!target) {
120
+ process.exit(1);
121
+ }
122
+ console.log(target);
123
+ return;
124
+ }
125
+ case "has-current-packaged": {
126
+ const filePath = packagedBinaryPath(rootDir);
127
+ if (filePath && fs.existsSync(filePath)) {
128
+ console.log(filePath);
129
+ return;
130
+ }
131
+ process.exit(1);
132
+ return;
133
+ }
134
+ case "current-packaged-path": {
135
+ const target = flags.target || resolveRuntimeTarget();
136
+ const filePath = packagedBinaryPath(rootDir, target);
137
+ if (!filePath) {
138
+ process.exit(1);
139
+ }
140
+ console.log(filePath);
141
+ return;
142
+ }
143
+ case "current-built-path": {
144
+ console.log(builtBinaryPath(rootDir));
145
+ return;
146
+ }
147
+ case "stage-current": {
148
+ const destination = stageBinary({
149
+ rootDir,
150
+ from: builtBinaryPath(rootDir),
151
+ target: flags.target || resolveRuntimeTarget(),
152
+ });
153
+ console.log(destination);
154
+ return;
155
+ }
156
+ case "stage": {
157
+ const destination = stageBinary({
158
+ rootDir,
159
+ from: flags.from ? path.resolve(flags.from) : "",
160
+ target: flags.target || resolveRuntimeTarget(),
161
+ });
162
+ console.log(destination);
163
+ return;
164
+ }
165
+ default:
166
+ console.error("Usage:");
167
+ console.error(" node scripts/bridge-binary.js current-target");
168
+ console.error(" node scripts/bridge-binary.js has-current-packaged");
169
+ console.error(" node scripts/bridge-binary.js current-packaged-path [--target <target>]");
170
+ console.error(" node scripts/bridge-binary.js current-built-path");
171
+ console.error(" node scripts/bridge-binary.js stage-current [--target <target>]");
172
+ console.error(" node scripts/bridge-binary.js stage --from <binary> [--target <target>]");
173
+ process.exit(1);
174
+ }
175
+ }
176
+
177
+ module.exports = {
178
+ SUPPORTED_RUNTIME_TARGETS,
179
+ binaryNameForTarget,
180
+ builtBinaryPath,
181
+ ensureExecutable,
182
+ packagedBinaryPath,
183
+ resolveRuntimeTarget,
184
+ stageBinary,
185
+ };
186
+
187
+ if (require.main === module) {
188
+ try {
189
+ main();
190
+ } catch (error) {
191
+ console.error(error instanceof Error ? error.message : String(error));
192
+ process.exit(1);
193
+ }
194
+ }