clawdex-mobile 3.0.0 → 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.
- package/.github/workflows/ci.yml +4 -3
- package/.github/workflows/npm-release.yml +62 -2
- package/.github/workflows/pages.yml +1 -1
- package/README.md +14 -3
- package/apps/mobile/app.json +1 -1
- package/apps/mobile/package.json +2 -1
- package/apps/mobile/src/api/__tests__/client.test.ts +13 -5
- package/apps/mobile/src/api/client.ts +25 -0
- package/apps/mobile/src/api/types.ts +1 -0
- package/apps/mobile/src/components/WorkspacePickerModal.tsx +555 -315
- package/apps/mobile/src/screens/MainScreen.tsx +0 -5
- package/apps/mobile/src/screens/OnboardingScreen.tsx +924 -312
- package/bin/clawdex.js +7 -6
- package/codex-rust-bridge +0 -0
- package/codex-rust-bridge.exe +0 -0
- package/docs/setup-and-operations.md +17 -12
- package/docs/troubleshooting.md +15 -19
- package/package.json +4 -3
- package/scripts/bridge-binary.js +194 -0
- package/scripts/setup-wizard.sh +17 -186
- package/scripts/start-bridge-secure.js +240 -0
- package/scripts/start-bridge-secure.sh +1 -40
- package/services/rust-bridge/Cargo.lock +1 -1
- package/services/rust-bridge/Cargo.toml +1 -1
- package/services/rust-bridge/package.json +1 -1
- package/services/rust-bridge/src/main.rs +11 -1
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]
|
|
14
|
-
Run interactive onboarding and secure setup.
|
|
15
|
-
By default, this also starts
|
|
16
|
-
Use --no-start to
|
|
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
|
|
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
|
|
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
|
|
@@ -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.
|
|
10
|
-
2.
|
|
11
|
-
3.
|
|
12
|
-
4.
|
|
13
|
-
|
|
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)
|
|
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
|
-
- `
|
|
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
|
|
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
|
|
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 |
|
package/docs/troubleshooting.md
CHANGED
|
@@ -1,23 +1,16 @@
|
|
|
1
1
|
# Troubleshooting
|
|
2
2
|
|
|
3
|
-
##
|
|
3
|
+
## Bridge startup seems slow
|
|
4
4
|
|
|
5
|
-
-
|
|
6
|
-
-
|
|
7
|
-
-
|
|
8
|
-
|
|
9
|
-
|
|
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
|
-
-
|
|
42
|
-
-
|
|
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
|
|
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
|
|
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,
|
|
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": "
|
|
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.
|
|
28
|
-
"secure:bridge:dev": "
|
|
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",
|
|
@@ -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
|
+
}
|
package/scripts/setup-wizard.sh
CHANGED
|
@@ -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
|
|
1410
|
-
|
|
1411
|
-
|
|
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
|
|
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}"
|