clawdex-mobile 5.1.2 → 5.1.3-internal.1
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/README.md +41 -2
- package/bin/clawdex.js +6 -1
- package/docs/setup-and-operations.md +86 -3
- package/docs/troubleshooting.md +74 -0
- package/package.json +3 -1
- package/scripts/bridge-binary.js +6 -6
- package/scripts/codespaces-bootstrap.js +231 -0
- package/scripts/setup-secure-dev.sh +90 -5
- package/scripts/setup-wizard.sh +89 -7
- package/scripts/start-bridge-secure.js +315 -34
- package/scripts/stop-services.sh +10 -7
- package/services/rust-bridge/Cargo.lock +1 -1
- package/services/rust-bridge/Cargo.toml +1 -1
- package/services/rust-bridge/src/main.rs +810 -53
- package/vendor/bridge-binaries/darwin-arm64/codex-rust-bridge +0 -0
- package/vendor/bridge-binaries/darwin-x64/codex-rust-bridge +0 -0
- package/vendor/bridge-binaries/linux-arm64/codex-rust-bridge +0 -0
- package/vendor/bridge-binaries/linux-armv7l/codex-rust-bridge +0 -0
- package/vendor/bridge-binaries/linux-x64/codex-rust-bridge +0 -0
- package/vendor/bridge-binaries/win32-x64/codex-rust-bridge.exe +0 -0
package/README.md
CHANGED
|
@@ -4,9 +4,9 @@
|
|
|
4
4
|
<img src="https://raw.githubusercontent.com/Mohit-Patil/clawdex-mobile/main/screenshots/social/clawdex-social-poster-1200x675.png" alt="Clawdex social banner" width="100%" />
|
|
5
5
|
</p>
|
|
6
6
|
|
|
7
|
-
Run Codex or OpenCode from your phone. `clawdex-mobile` ships the bridge CLI plus bundled Rust bridge binaries for supported hosts, and the mobile app pairs to that bridge over Tailscale or
|
|
7
|
+
Run Codex or OpenCode from your phone. `clawdex-mobile` ships the bridge CLI plus bundled Rust bridge binaries for supported hosts, and the mobile app pairs to that bridge over Tailscale, local LAN, or GitHub Codespaces forwarded HTTPS URLs.
|
|
8
8
|
|
|
9
|
-
This project is for trusted/private networking
|
|
9
|
+
This project is for trusted/private networking by default. GitHub Codespaces is supported as an internet-reachable exception: keep bridge auth enabled, use only repos you trust, and remember forwarded public ports reset to private when a codespace restarts.
|
|
10
10
|
|
|
11
11
|
## What You Get
|
|
12
12
|
|
|
@@ -52,6 +52,45 @@ clawdex init
|
|
|
52
52
|
clawdex stop
|
|
53
53
|
```
|
|
54
54
|
|
|
55
|
+
## GitHub Codespaces
|
|
56
|
+
|
|
57
|
+
You can run the bridge inside a GitHub Codespace instead of keeping a laptop or server online.
|
|
58
|
+
|
|
59
|
+
From a repo checkout inside Codespaces:
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
npm run setup:wizard
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Pick `GitHub Codespaces` as the network mode. The setup flow writes forwarded HTTPS bridge URLs into `.env.secure`, and bridge startup will try to mark both bridge ports public automatically.
|
|
66
|
+
|
|
67
|
+
Notes:
|
|
68
|
+
|
|
69
|
+
- The mobile app should pair to the printed `https://<codespace>-8787.app.github.dev` URL, not `127.0.0.1`.
|
|
70
|
+
- Browser preview uses a second forwarded port (`8788` by default), so both ports need public visibility.
|
|
71
|
+
- GitHub resets public forwarded ports back to private when a codespace restarts. Restarting the bridge reruns the visibility step.
|
|
72
|
+
- If automatic visibility setup fails, run `gh codespace ports visibility 8787:public 8788:public`.
|
|
73
|
+
- If the mobile app is built with `EXPO_PUBLIC_GITHUB_CLIENT_ID`, users can now tap `Use GitHub Codespaces` in onboarding/settings, sign in with GitHub, pick a Codespace, and connect without manually copying the bridge token.
|
|
74
|
+
- The app can also create a new repo-backed Codespace directly. It prefers `<signed-in-user>/<EXPO_PUBLIC_GITHUB_CODESPACES_REPO_NAME>` first. If that repo does not exist, it automatically forks `EXPO_PUBLIC_GITHUB_CODESPACES_SOURCE_OWNER/<EXPO_PUBLIC_GITHUB_CODESPACES_REPO_NAME>` into the signed-in user account, then creates the Codespace there.
|
|
75
|
+
|
|
76
|
+
This repo now also includes a Codespaces bootstrap flow. On Codespace start/resume, `.devcontainer/devcontainer.json` runs:
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
npm run codespaces:bootstrap
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
During initial Codespace creation, the devcontainer also runs:
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
npm run codespaces:bootstrap -- --prepare-only
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
That pre-installs Codex and prebuilds the Rust bridge binary so the later startup path is faster. The normal bootstrap command rewrites `.env.secure` for Codespaces with `codex` as the only enabled engine and starts the bridge in the background. You can rerun either command manually any time.
|
|
89
|
+
|
|
90
|
+
The published npm package now includes that bootstrap script too, so a minimal Codespaces template repo can install `clawdex-mobile@latest` in `postCreateCommand` and call the packaged bootstrap without vendoring bridge source into the template itself.
|
|
91
|
+
|
|
92
|
+
In Codespaces mode, the bootstrap also enables bridge-side GitHub bearer auth for the current `CODESPACE_NAME`, so the mobile app can authenticate with the same GitHub OAuth token it used to discover and start the Codespace.
|
|
93
|
+
|
|
55
94
|
## OpenCode Setup
|
|
56
95
|
|
|
57
96
|
OpenCode is supported directly from the CLI now.
|
package/bin/clawdex.js
CHANGED
|
@@ -34,9 +34,14 @@ Commands:
|
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
function runCommand(command, args = [], options = {}) {
|
|
37
|
+
const workspaceRoot = process.env.CLAWDEX_WORKSPACE_ROOT || process.cwd();
|
|
37
38
|
const child = spawnSync(command, args, {
|
|
38
39
|
stdio: "inherit",
|
|
39
|
-
env:
|
|
40
|
+
env: {
|
|
41
|
+
...process.env,
|
|
42
|
+
CLAWDEX_WORKSPACE_ROOT: workspaceRoot,
|
|
43
|
+
INIT_CWD: process.env.INIT_CWD || workspaceRoot,
|
|
44
|
+
},
|
|
40
45
|
cwd: process.cwd(),
|
|
41
46
|
...options,
|
|
42
47
|
});
|
|
@@ -37,6 +37,77 @@ Published npm releases bundle prebuilt bridge binaries for `darwin-arm64`, `darw
|
|
|
37
37
|
|
|
38
38
|
Published CLI installs are bridge-only. They do not include the Expo workspace or mobile app source files.
|
|
39
39
|
|
|
40
|
+
## GitHub Codespaces Setup
|
|
41
|
+
|
|
42
|
+
Codespaces can replace a user-managed always-on machine for development and lightweight remote use.
|
|
43
|
+
|
|
44
|
+
From a repo checkout inside an active codespace:
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
npm run setup:wizard
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Choose `GitHub Codespaces` for the bridge network mode.
|
|
51
|
+
|
|
52
|
+
What that does:
|
|
53
|
+
|
|
54
|
+
- binds the bridge locally inside the codespace
|
|
55
|
+
- writes `BRIDGE_CONNECT_URL` and `BRIDGE_PREVIEW_CONNECT_URL` using the codespace forwarded HTTPS domain
|
|
56
|
+
- enables bridge-side GitHub bearer auth for the current codespace
|
|
57
|
+
- starts the bridge normally
|
|
58
|
+
- attempts to mark the bridge port and browser-preview port public on each startup
|
|
59
|
+
|
|
60
|
+
Important constraints:
|
|
61
|
+
|
|
62
|
+
- Pair the mobile app to the printed `https://<codespace>-8787.app.github.dev` URL, not `127.0.0.1`
|
|
63
|
+
- Browser preview uses the preview port (`8788` by default), so that forwarded port must also be public
|
|
64
|
+
- GitHub resets public forwarded ports back to private whenever the codespace restarts
|
|
65
|
+
- Keep bridge auth enabled and use Codespaces only for repos you trust, because public forwarded ports are internet-reachable
|
|
66
|
+
- If the mobile app build sets `EXPO_PUBLIC_GITHUB_CLIENT_ID`, onboarding/settings can now sign in with GitHub, start the Codespace, and connect directly with the same OAuth token instead of copying `BRIDGE_AUTH_TOKEN`
|
|
67
|
+
- That same in-app GitHub sign-in now also bootstraps GitHub git auth inside the Codespace so `git clone`, `git push`, GitHub HTTPS remotes, and common `git@github.com:...` SSH-style remotes can reuse the app login without extra account setup
|
|
68
|
+
- The same in-app GitHub flow can create a new Codespace. It prefers `<signed-in-user>/<EXPO_PUBLIC_GITHUB_CODESPACES_REPO_NAME>`. If that repo does not exist yet, Clawdex automatically forks `EXPO_PUBLIC_GITHUB_CODESPACES_SOURCE_OWNER/<EXPO_PUBLIC_GITHUB_CODESPACES_REPO_NAME>` into the signed-in user account and creates the Codespace from that fork
|
|
69
|
+
- Older saved GitHub Codespaces sessions may need one fresh sign-in from the app so the stored GitHub token includes repository access
|
|
70
|
+
|
|
71
|
+
Manual recovery if port visibility does not update automatically:
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
gh codespace ports visibility 8787:public 8788:public
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### Codespaces Bootstrap
|
|
78
|
+
|
|
79
|
+
The repo devcontainer now includes:
|
|
80
|
+
|
|
81
|
+
- `postCreateCommand`: `npm install --include=dev && npm run codespaces:bootstrap -- --prepare-only`
|
|
82
|
+
- `postStartCommand`: `npm run codespaces:bootstrap`
|
|
83
|
+
|
|
84
|
+
`npm run codespaces:bootstrap` does the following:
|
|
85
|
+
|
|
86
|
+
- installs the Codex CLI via `npm install -g @openai/codex` if it is missing
|
|
87
|
+
- in `--prepare-only` mode, prebuilds the Rust bridge binary without starting it
|
|
88
|
+
- rewrites `.env.secure` for `BRIDGE_NETWORK_MODE=codespaces` with `BRIDGE_ACTIVE_ENGINE=codex`, `BRIDGE_ENABLED_ENGINES=codex`, and `BRIDGE_GITHUB_CODESPACES_AUTH=true`
|
|
89
|
+
- starts the bridge in the background unless you set `CLAWDEX_CODESPACES_SKIP_START=true` or pass `--no-start`
|
|
90
|
+
|
|
91
|
+
That means the first Codespace create now front-loads the expensive bridge compile during `postCreateCommand`, so the later `postStartCommand` can usually start the bridge much faster.
|
|
92
|
+
|
|
93
|
+
The same bootstrap script is included in the published `clawdex-mobile` npm package. That lets the `clawdex-codespace` template stay minimal: it can install `clawdex-mobile@latest` globally in the devcontainer and invoke the packaged bootstrap against the current workspace instead of copying `scripts/*` and `services/rust-bridge/*` into the template repo.
|
|
94
|
+
|
|
95
|
+
Manual examples:
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
npm run codespaces:bootstrap -- --prepare-only
|
|
99
|
+
npm run codespaces:bootstrap
|
|
100
|
+
npm run codespaces:bootstrap -- --no-start
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Minimal template equivalent:
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
npm install -g clawdex-mobile@latest @openai/codex
|
|
107
|
+
CLAWDEX_WORKSPACE_ROOT="$PWD" node "$(npm root -g)/clawdex-mobile/scripts/codespaces-bootstrap.js" --prepare-only
|
|
108
|
+
CLAWDEX_WORKSPACE_ROOT="$PWD" node "$(npm root -g)/clawdex-mobile/scripts/codespaces-bootstrap.js"
|
|
109
|
+
```
|
|
110
|
+
|
|
40
111
|
## Manual Secure Setup (No Wizard)
|
|
41
112
|
|
|
42
113
|
### 1) Install dependencies
|
|
@@ -78,7 +149,7 @@ When both CLIs are selected, the bridge starts both backends and merges chat lis
|
|
|
78
149
|
|
|
79
150
|
### 4) Pair from the mobile app
|
|
80
151
|
|
|
81
|
-
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
|
|
152
|
+
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`, `http://192.168.x.y:8787`, or `https://<codespace>-8787.app.github.dev`). The chosen bridge URL is stored on-device and can be changed later in Settings.
|
|
82
153
|
|
|
83
154
|
### In-app Bridge Maintenance
|
|
84
155
|
|
|
@@ -173,11 +244,17 @@ npm run teardown -- --yes
|
|
|
173
244
|
|
|
174
245
|
| Variable | Purpose |
|
|
175
246
|
|---|---|
|
|
247
|
+
| `BRIDGE_NETWORK_MODE` | bridge connectivity mode (`tailscale`, `local`, or `codespaces`) |
|
|
176
248
|
| `BRIDGE_HOST` | bind host for rust bridge |
|
|
177
249
|
| `BRIDGE_PORT` | bridge port (default `8787`) |
|
|
178
250
|
| `BRIDGE_PREVIEW_PORT` | browser preview port for proxied localhost web apps (default `BRIDGE_PORT + 1`) |
|
|
251
|
+
| `BRIDGE_CONNECT_URL` | externally reachable bridge base URL used for pairing/QR output |
|
|
252
|
+
| `BRIDGE_PREVIEW_CONNECT_URL` | externally reachable browser preview base URL |
|
|
179
253
|
| `BRIDGE_AUTH_TOKEN` | required auth token |
|
|
180
254
|
| `BRIDGE_ALLOW_QUERY_TOKEN_AUTH` | query-token auth fallback |
|
|
255
|
+
| `BRIDGE_GITHUB_CODESPACES_AUTH` | accept GitHub bearer tokens for the current codespace |
|
|
256
|
+
| `BRIDGE_GITHUB_CODESPACE_NAME` | codespace name used when validating GitHub bearer tokens |
|
|
257
|
+
| `BRIDGE_GITHUB_API_URL` | GitHub REST API base URL for Codespaces auth checks |
|
|
181
258
|
| `CODEX_CLI_BIN` | codex executable |
|
|
182
259
|
| `BRIDGE_ACTIVE_ENGINE` | internal preferred routing backend used when multiple harnesses are enabled |
|
|
183
260
|
| `BRIDGE_ENABLED_ENGINES` | selected harnesses to expose (`codex`, `opencode`, or both) |
|
|
@@ -194,6 +271,11 @@ npm run teardown -- --yes
|
|
|
194
271
|
| Variable | Purpose |
|
|
195
272
|
|---|---|
|
|
196
273
|
| `EXPO_PUBLIC_HOST_BRIDGE_TOKEN` | token used by local mobile dev builds |
|
|
274
|
+
| `EXPO_PUBLIC_GITHUB_CLIENT_ID` | GitHub OAuth app client ID for in-app Codespaces sign-in |
|
|
275
|
+
| `EXPO_PUBLIC_GITHUB_CODESPACES_PORT_FORWARDING_DOMAIN` | forwarded port domain used to derive Codespaces bridge URLs (`app.github.dev` by default) |
|
|
276
|
+
| `EXPO_PUBLIC_GITHUB_CODESPACES_REPO_NAME` | repository name to sort matching Codespaces first in the in-app picker |
|
|
277
|
+
| `EXPO_PUBLIC_GITHUB_CODESPACES_SOURCE_OWNER` | template/source repository owner used for automatic forking when the signed-in user does not have a same-name repo |
|
|
278
|
+
| `EXPO_PUBLIC_GITHUB_CODESPACES_REPO_REF` | optional git ref/branch used when creating a new Codespace |
|
|
197
279
|
| `EXPO_PUBLIC_ALLOW_QUERY_TOKEN_AUTH` | query-token behavior for WebSocket auth fallback |
|
|
198
280
|
| `EXPO_PUBLIC_ALLOW_INSECURE_REMOTE_BRIDGE` | suppress insecure-HTTP warning |
|
|
199
281
|
| `EXPO_PUBLIC_PRIVACY_POLICY_URL` | in-app Privacy link |
|
|
@@ -213,8 +295,9 @@ If you enable the optional tip jar:
|
|
|
213
295
|
|
|
214
296
|
## Production Readiness Checklist
|
|
215
297
|
|
|
216
|
-
- Keep bridge network-private only (Tailscale/private LAN/VPN + host firewall)
|
|
217
|
-
-
|
|
298
|
+
- Keep bridge network-private only by default (Tailscale/private LAN/VPN + host firewall)
|
|
299
|
+
- If using GitHub Codespaces, remember the bridge is internet-reachable whenever its forwarded ports are public
|
|
300
|
+
- Require bridge auth of some kind (`BRIDGE_AUTH_TOKEN` or GitHub Codespaces auth)
|
|
218
301
|
- Keep `BRIDGE_ALLOW_QUERY_TOKEN_AUTH=true` only on private networks (required for Android WS auth fallback)
|
|
219
302
|
- Do not set `BRIDGE_ALLOW_INSECURE_NO_AUTH=true` outside local debugging
|
|
220
303
|
- Scope `BRIDGE_WORKDIR` to minimal required root
|
package/docs/troubleshooting.md
CHANGED
|
@@ -34,11 +34,84 @@ npm run stop:services
|
|
|
34
34
|
## Bridge auth errors (`401`, invalid token)
|
|
35
35
|
|
|
36
36
|
- For the shipped mobile app, rescan the bridge QR or update the stored token in Settings.
|
|
37
|
+
- For GitHub-auth Codespaces profiles, reopen `GitHub Codespaces` in the app and sign in with GitHub again if the OAuth token was revoked or expired.
|
|
37
38
|
- For a local dev build, also ensure `BRIDGE_AUTH_TOKEN` in `.env.secure` matches `EXPO_PUBLIC_HOST_BRIDGE_TOKEN` in `apps/mobile/.env`.
|
|
38
39
|
- Restart the bridge after token changes.
|
|
39
40
|
- On secure-launcher installs, `Settings > Bridge Maintenance > Restart bridge safely` can do that from the phone.
|
|
40
41
|
- If an in-app bridge update fails, inspect `.bridge-updater.log` and `.bridge-update-status.json` in the bridge install root.
|
|
41
42
|
|
|
43
|
+
## GitHub Codespaces bridge URL does not connect
|
|
44
|
+
|
|
45
|
+
- Pair to the printed forwarded HTTPS URL such as `https://<codespace>-8787.app.github.dev`, not `127.0.0.1`.
|
|
46
|
+
- Public forwarded ports reset back to private when the codespace restarts.
|
|
47
|
+
- Restart the bridge or rerun `npm run codespaces:bootstrap` to rerun the automatic visibility step.
|
|
48
|
+
- If needed, set both ports public manually:
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
gh codespace ports visibility 8787:public 8788:public
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
- If `gh` is unavailable in the codespace, use the Codespaces `Ports` panel and change both forwarded ports to `Public`.
|
|
55
|
+
- Keep bridge auth enabled. Public forwarded ports without bridge auth are not a safe setup.
|
|
56
|
+
- If GitHub direct sign-in is not showing in the app, confirm the build includes `EXPO_PUBLIC_GITHUB_CLIENT_ID`.
|
|
57
|
+
- If in-app Codespace creation forks or targets the wrong repo, check `EXPO_PUBLIC_GITHUB_CODESPACES_REPO_NAME`, `EXPO_PUBLIC_GITHUB_CODESPACES_SOURCE_OWNER`, and `EXPO_PUBLIC_GITHUB_CODESPACES_REPO_REF` in the mobile build env.
|
|
58
|
+
|
|
59
|
+
## GitHub Codespaces bootstrap did not start the bridge
|
|
60
|
+
|
|
61
|
+
- Check the post-start command output in the Codespace terminal or rerun it manually:
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
npm run codespaces:bootstrap -- --prepare-only
|
|
65
|
+
npm run codespaces:bootstrap
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
- On the minimal `clawdex-codespace` template, rerun the packaged bootstrap instead:
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
CLAWDEX_WORKSPACE_ROOT="$PWD" node "$(npm root -g)/clawdex-mobile/scripts/codespaces-bootstrap.js" --prepare-only
|
|
72
|
+
CLAWDEX_WORKSPACE_ROOT="$PWD" node "$(npm root -g)/clawdex-mobile/scripts/codespaces-bootstrap.js"
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
- The Codespaces bootstrap only prepares the `codex` engine. It will try to install Codex automatically with `npm install -g @openai/codex`.
|
|
76
|
+
- `--prepare-only` installs Codex if needed and prebuilds the Rust bridge binary without starting the bridge.
|
|
77
|
+
- The bootstrap also enables `BRIDGE_GITHUB_CODESPACES_AUTH=true` so GitHub bearer tokens can connect directly to the bridge.
|
|
78
|
+
- If that install fails, fix npm/global package permissions in the codespace and rerun the bootstrap.
|
|
79
|
+
- Bridge startup logs and runtime state live in the Codespace repo root:
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
tail -n 200 .bridge.log
|
|
83
|
+
ls -la .bridge.pid .bridge.log .env.secure
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
- To only rewrite `.env.secure` without starting the bridge:
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
npm run codespaces:bootstrap -- --no-start
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## Git push in a Codespace fails with `403` or permission denied
|
|
93
|
+
|
|
94
|
+
- The app now bootstraps GitHub HTTPS git credentials inside the Codespace after GitHub sign-in.
|
|
95
|
+
- If you signed in before this behavior shipped, reopen `GitHub Codespaces` in the app and sign in with GitHub once more so the saved token includes repository access.
|
|
96
|
+
- The bootstrap also rewrites common `git@github.com:...` and `ssh://git@github.com/...` remotes to HTTPS so they can use the same credential.
|
|
97
|
+
- After reconnecting, retry the clone/push from the app or from the Codespace shell.
|
|
98
|
+
|
|
99
|
+
## Voice transcription says no credentials were found
|
|
100
|
+
|
|
101
|
+
- The bridge can transcribe with either `OPENAI_API_KEY`, `BRIDGE_CHATGPT_ACCESS_TOKEN`, or the same ChatGPT auth tokens already used for Codex login.
|
|
102
|
+
- In GitHub Codespaces, finish the ChatGPT/Codex login step from the app first. The bridge will persist those tokens to `BRIDGE_WORKDIR/.clawdex-chatgpt-auth.json` and reuse them for voice transcription.
|
|
103
|
+
- If you still see the error after logging in, restart the bridge once so it reloads the persisted auth cache:
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
npm run secure:bridge
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
- You can inspect whether the bridge captured the token bundle:
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
ls -la .clawdex-chatgpt-auth.json
|
|
113
|
+
```
|
|
114
|
+
|
|
42
115
|
## Local browser preview does not open
|
|
43
116
|
|
|
44
117
|
- The in-app browser only supports loopback targets from the bridge host: `localhost`, `127.0.0.1`, or `::1`.
|
|
@@ -49,6 +122,7 @@ npm run stop:services
|
|
|
49
122
|
- By default the preview server binds to `BRIDGE_PORT + 1`.
|
|
50
123
|
- Restart the bridge after changing `BRIDGE_PREVIEW_PORT`.
|
|
51
124
|
- If the page shell loads but live reload does not, verify the target dev server is still serving its WebSocket/HMR endpoint locally.
|
|
125
|
+
- In GitHub Codespaces, the preview port (`8788` by default) must also be public or the Browser screen will fail even if the main bridge port works.
|
|
52
126
|
|
|
53
127
|
## Tailscale issues
|
|
54
128
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "clawdex-mobile",
|
|
3
|
-
"version": "5.1.
|
|
3
|
+
"version": "5.1.3-internal.1",
|
|
4
4
|
"description": "Private-network mobile bridge and CLI for Codex and OpenCode",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"codex",
|
|
@@ -34,6 +34,7 @@
|
|
|
34
34
|
"docs/troubleshooting.md",
|
|
35
35
|
"scripts/bridge-binary.js",
|
|
36
36
|
"scripts/bridge-self-update.js",
|
|
37
|
+
"scripts/codespaces-bootstrap.js",
|
|
37
38
|
"scripts/setup-secure-dev.sh",
|
|
38
39
|
"scripts/setup-wizard.sh",
|
|
39
40
|
"scripts/start-bridge-secure.js",
|
|
@@ -58,6 +59,7 @@
|
|
|
58
59
|
"secure:setup": "./scripts/setup-secure-dev.sh",
|
|
59
60
|
"secure:bridge": "node ./scripts/start-bridge-secure.js",
|
|
60
61
|
"secure:bridge:dev": "node ./scripts/start-bridge-secure.js --dev",
|
|
62
|
+
"codespaces:bootstrap": "node ./scripts/codespaces-bootstrap.js",
|
|
61
63
|
"bridge:ts": "BRIDGE_WORKDIR=$(pwd) npm run -w @codex/mac-bridge dev",
|
|
62
64
|
"version:sync": "node scripts/sync-versions.js",
|
|
63
65
|
"build": "npm run --workspaces build",
|
package/scripts/bridge-binary.js
CHANGED
|
@@ -81,9 +81,9 @@ function packagedBinaryPath(rootDir = repoRoot(), target = resolveRuntimeTarget(
|
|
|
81
81
|
return path.join(rootDir, "vendor", "bridge-binaries", target, binaryNameForTarget(target));
|
|
82
82
|
}
|
|
83
83
|
|
|
84
|
-
function builtBinaryPath(rootDir = repoRoot(), platform = os.platform()) {
|
|
84
|
+
function builtBinaryPath(rootDir = repoRoot(), platform = os.platform(), profile = "release") {
|
|
85
85
|
const binaryName = platform === "win32" ? "codex-rust-bridge.exe" : "codex-rust-bridge";
|
|
86
|
-
return path.join(rootDir, "services", "rust-bridge", "target",
|
|
86
|
+
return path.join(rootDir, "services", "rust-bridge", "target", profile, binaryName);
|
|
87
87
|
}
|
|
88
88
|
|
|
89
89
|
function ensureExecutable(filePath) {
|
|
@@ -168,13 +168,13 @@ function main() {
|
|
|
168
168
|
return;
|
|
169
169
|
}
|
|
170
170
|
case "current-built-path": {
|
|
171
|
-
console.log(builtBinaryPath(rootDir));
|
|
171
|
+
console.log(builtBinaryPath(rootDir, os.platform(), flags.profile || "release"));
|
|
172
172
|
return;
|
|
173
173
|
}
|
|
174
174
|
case "stage-current": {
|
|
175
175
|
const destination = stageBinary({
|
|
176
176
|
rootDir,
|
|
177
|
-
from: builtBinaryPath(rootDir),
|
|
177
|
+
from: builtBinaryPath(rootDir, os.platform(), flags.profile || "release"),
|
|
178
178
|
target: flags.target || resolveRuntimeTarget(),
|
|
179
179
|
});
|
|
180
180
|
console.log(destination);
|
|
@@ -194,8 +194,8 @@ function main() {
|
|
|
194
194
|
console.error(" node scripts/bridge-binary.js current-target");
|
|
195
195
|
console.error(" node scripts/bridge-binary.js has-current-packaged");
|
|
196
196
|
console.error(" node scripts/bridge-binary.js current-packaged-path [--target <target>]");
|
|
197
|
-
console.error(" node scripts/bridge-binary.js current-built-path");
|
|
198
|
-
console.error(" node scripts/bridge-binary.js stage-current [--target <target>]");
|
|
197
|
+
console.error(" node scripts/bridge-binary.js current-built-path [--profile <debug|release>]");
|
|
198
|
+
console.error(" node scripts/bridge-binary.js stage-current [--target <target>] [--profile <debug|release>]");
|
|
199
199
|
console.error(" node scripts/bridge-binary.js stage --from <binary> [--target <target>]");
|
|
200
200
|
process.exit(1);
|
|
201
201
|
}
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
|
|
4
|
+
const { spawnSync } = require("node:child_process");
|
|
5
|
+
const fs = require("node:fs");
|
|
6
|
+
const path = require("node:path");
|
|
7
|
+
|
|
8
|
+
function resolveRootDir() {
|
|
9
|
+
return path.resolve(__dirname, "..");
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function resolveWorkspaceDir() {
|
|
13
|
+
const candidates = [
|
|
14
|
+
process.env.CLAWDEX_WORKSPACE_ROOT,
|
|
15
|
+
process.env.INIT_CWD,
|
|
16
|
+
process.cwd(),
|
|
17
|
+
];
|
|
18
|
+
|
|
19
|
+
for (const candidate of candidates) {
|
|
20
|
+
if (typeof candidate !== "string" || !candidate.trim()) {
|
|
21
|
+
continue;
|
|
22
|
+
}
|
|
23
|
+
return path.resolve(candidate);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return resolveRootDir();
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function readEnvFile(filePath) {
|
|
30
|
+
const contents = fs.readFileSync(filePath, "utf8");
|
|
31
|
+
const nextEnv = {};
|
|
32
|
+
|
|
33
|
+
for (const rawLine of contents.split(/\r?\n/)) {
|
|
34
|
+
const line = rawLine.trim();
|
|
35
|
+
if (!line || line.startsWith("#")) {
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const match = line.match(/^(?:export\s+)?([A-Za-z_][A-Za-z0-9_]*)=(.*)$/);
|
|
40
|
+
if (!match) {
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const [, key, rawValue] = match;
|
|
45
|
+
let value = rawValue;
|
|
46
|
+
if (
|
|
47
|
+
(value.startsWith('"') && value.endsWith('"')) ||
|
|
48
|
+
(value.startsWith("'") && value.endsWith("'"))
|
|
49
|
+
) {
|
|
50
|
+
value = value.slice(1, -1);
|
|
51
|
+
}
|
|
52
|
+
nextEnv[key] = value;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return nextEnv;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function readNonEmptyEnv(env, key) {
|
|
59
|
+
const value = env[key];
|
|
60
|
+
return typeof value === "string" && value.trim() ? value.trim() : "";
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function runCommand(command, args, options = {}) {
|
|
64
|
+
return spawnSync(command, args, {
|
|
65
|
+
stdio: "inherit",
|
|
66
|
+
...options,
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function commandExists(command) {
|
|
71
|
+
if (typeof command === "string" && command.includes(path.sep)) {
|
|
72
|
+
try {
|
|
73
|
+
fs.accessSync(command, fs.constants.X_OK);
|
|
74
|
+
return true;
|
|
75
|
+
} catch {
|
|
76
|
+
return false;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const checker = process.platform === "win32" ? "where" : "which";
|
|
81
|
+
const result = spawnSync(checker, [command], { stdio: "ignore" });
|
|
82
|
+
return result.status === 0;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function parseArgs(argv) {
|
|
86
|
+
return {
|
|
87
|
+
noStart: argv.includes("--no-start"),
|
|
88
|
+
prepareOnly: argv.includes("--prepare-only"),
|
|
89
|
+
force: argv.includes("--force"),
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function ensureCodespacesContext({ force }) {
|
|
94
|
+
if (force) {
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (String(process.env.CODESPACES || "").trim().toLowerCase() === "true") {
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
console.log("Codespaces bootstrap skipped because this shell is not running inside GitHub Codespaces.");
|
|
103
|
+
process.exit(0);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function runSetup(rootDir, workspaceDir) {
|
|
107
|
+
const setupEnv = {
|
|
108
|
+
...process.env,
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
delete setupEnv.BRIDGE_NETWORK_MODE;
|
|
112
|
+
delete setupEnv.BRIDGE_HOST_OVERRIDE;
|
|
113
|
+
delete setupEnv.BRIDGE_ACTIVE_ENGINE;
|
|
114
|
+
delete setupEnv.BRIDGE_ENABLED_ENGINES;
|
|
115
|
+
|
|
116
|
+
setupEnv.INIT_CWD = rootDir;
|
|
117
|
+
setupEnv.CLAWDEX_WORKSPACE_ROOT = workspaceDir;
|
|
118
|
+
setupEnv.BRIDGE_NETWORK_MODE = "codespaces";
|
|
119
|
+
setupEnv.BRIDGE_HOST_OVERRIDE = "127.0.0.1";
|
|
120
|
+
setupEnv.BRIDGE_ACTIVE_ENGINE = "codex";
|
|
121
|
+
setupEnv.BRIDGE_ENABLED_ENGINES = "codex";
|
|
122
|
+
|
|
123
|
+
console.log("Preparing .env.secure for GitHub Codespaces...");
|
|
124
|
+
const result = runCommand(path.join(rootDir, "scripts", "setup-secure-dev.sh"), [], {
|
|
125
|
+
cwd: workspaceDir,
|
|
126
|
+
env: setupEnv,
|
|
127
|
+
shell: false,
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
if ((result.status ?? 1) !== 0) {
|
|
131
|
+
process.exit(result.status ?? 1);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function ensureCodexCliInstalled(secureEnv) {
|
|
136
|
+
const codexBinary = readNonEmptyEnv(secureEnv, "CODEX_CLI_BIN") || "codex";
|
|
137
|
+
if (commandExists(codexBinary)) {
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
console.log("Codex CLI not found in this codespace. Installing via npm...");
|
|
142
|
+
const installResult = runCommand("npm", ["install", "-g", "@openai/codex"], {
|
|
143
|
+
cwd: resolveWorkspaceDir(),
|
|
144
|
+
env: process.env,
|
|
145
|
+
});
|
|
146
|
+
if ((installResult.status ?? 1) !== 0) {
|
|
147
|
+
console.error("error: failed to install Codex CLI automatically.");
|
|
148
|
+
process.exit(installResult.status ?? 1);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
if (!commandExists(codexBinary)) {
|
|
152
|
+
console.error(`error: Codex CLI still not found after install attempt: ${codexBinary}`);
|
|
153
|
+
process.exit(1);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
console.log("Codex CLI installed.");
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function prepareBridgeBinary(rootDir, workspaceDir, secureEnv) {
|
|
160
|
+
console.log("Prebuilding bridge binary for faster Codespaces startup...");
|
|
161
|
+
const prepareEnv = {
|
|
162
|
+
...process.env,
|
|
163
|
+
...secureEnv,
|
|
164
|
+
CLAWDEX_WORKSPACE_ROOT: workspaceDir,
|
|
165
|
+
INIT_CWD: process.env.INIT_CWD || workspaceDir,
|
|
166
|
+
};
|
|
167
|
+
const result = runCommand(
|
|
168
|
+
process.execPath,
|
|
169
|
+
[path.join(rootDir, "scripts", "start-bridge-secure.js"), "--prepare-only"],
|
|
170
|
+
{
|
|
171
|
+
cwd: workspaceDir,
|
|
172
|
+
env: prepareEnv,
|
|
173
|
+
}
|
|
174
|
+
);
|
|
175
|
+
|
|
176
|
+
if ((result.status ?? 1) !== 0) {
|
|
177
|
+
process.exit(result.status ?? 1);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function startBridge(rootDir, workspaceDir) {
|
|
182
|
+
console.log("Starting bridge in background for this codespace...");
|
|
183
|
+
const result = runCommand(
|
|
184
|
+
process.execPath,
|
|
185
|
+
[path.join(rootDir, "scripts", "start-bridge-secure.js"), "--background"],
|
|
186
|
+
{
|
|
187
|
+
cwd: workspaceDir,
|
|
188
|
+
env: {
|
|
189
|
+
...process.env,
|
|
190
|
+
CLAWDEX_WORKSPACE_ROOT: workspaceDir,
|
|
191
|
+
INIT_CWD: process.env.INIT_CWD || workspaceDir,
|
|
192
|
+
},
|
|
193
|
+
}
|
|
194
|
+
);
|
|
195
|
+
|
|
196
|
+
if ((result.status ?? 1) !== 0) {
|
|
197
|
+
process.exit(result.status ?? 1);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
function main() {
|
|
202
|
+
const options = parseArgs(process.argv.slice(2));
|
|
203
|
+
ensureCodespacesContext(options);
|
|
204
|
+
|
|
205
|
+
const rootDir = resolveRootDir();
|
|
206
|
+
const workspaceDir = resolveWorkspaceDir();
|
|
207
|
+
const secureEnvPath = path.join(workspaceDir, ".env.secure");
|
|
208
|
+
|
|
209
|
+
runSetup(rootDir, workspaceDir);
|
|
210
|
+
|
|
211
|
+
if (!fs.existsSync(secureEnvPath)) {
|
|
212
|
+
console.error(`error: expected secure env at ${secureEnvPath}`);
|
|
213
|
+
process.exit(1);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
const secureEnv = readEnvFile(secureEnvPath);
|
|
217
|
+
if (options.noStart || String(process.env.CLAWDEX_CODESPACES_SKIP_START || "").trim().toLowerCase() === "true") {
|
|
218
|
+
console.log("Codespaces bootstrap configured bridge env only. Bridge auto-start skipped.");
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
ensureCodexCliInstalled(secureEnv);
|
|
223
|
+
if (options.prepareOnly) {
|
|
224
|
+
prepareBridgeBinary(rootDir, workspaceDir, secureEnv);
|
|
225
|
+
console.log("Codespaces bootstrap prepared Codex and the bridge binary without starting the bridge.");
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
startBridge(rootDir, workspaceDir);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
main();
|