clawdex-mobile 5.1.3-internal.1 → 5.1.3-internal.2
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 +2 -2
- package/docs/setup-and-operations.md +5 -4
- package/docs/troubleshooting.md +3 -2
- package/package.json +1 -1
- package/services/rust-bridge/Cargo.lock +1 -1
- package/services/rust-bridge/Cargo.toml +1 -1
- package/services/rust-bridge/src/main.rs +83 -2
- 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
|
@@ -70,7 +70,7 @@ Notes:
|
|
|
70
70
|
- Browser preview uses a second forwarded port (`8788` by default), so both ports need public visibility.
|
|
71
71
|
- GitHub resets public forwarded ports back to private when a codespace restarts. Restarting the bridge reruns the visibility step.
|
|
72
72
|
- If automatic visibility setup fails, run `gh codespace ports visibility 8787:public 8788:public`.
|
|
73
|
-
- If the mobile app is built with `
|
|
73
|
+
- If the mobile app is built with `EXPO_PUBLIC_GITHUB_APP_CLIENT_ID` and `EXPO_PUBLIC_GITHUB_APP_SLUG`, users can now tap `Use GitHub Codespaces` in onboarding/settings, sign in with GitHub, approve the Claudex GitHub App for only the repositories they want, pick a Codespace, and connect without manually copying the bridge token.
|
|
74
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
75
|
|
|
76
76
|
This repo now also includes a Codespaces bootstrap flow. On Codespace start/resume, `.devcontainer/devcontainer.json` runs:
|
|
@@ -89,7 +89,7 @@ That pre-installs Codex and prebuilds the Rust bridge binary so the later startu
|
|
|
89
89
|
|
|
90
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
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
|
|
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 App user token it used to discover and start the Codespace.
|
|
93
93
|
|
|
94
94
|
## OpenCode Setup
|
|
95
95
|
|
|
@@ -63,10 +63,10 @@ Important constraints:
|
|
|
63
63
|
- Browser preview uses the preview port (`8788` by default), so that forwarded port must also be public
|
|
64
64
|
- GitHub resets public forwarded ports back to private whenever the codespace restarts
|
|
65
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 `
|
|
67
|
-
- That same in-app GitHub sign-in
|
|
66
|
+
- If the mobile app build sets `EXPO_PUBLIC_GITHUB_APP_CLIENT_ID` and `EXPO_PUBLIC_GITHUB_APP_SLUG`, onboarding/settings can now sign in with GitHub, approve the Claudex GitHub App for only the repositories they want, start the Codespace, and connect directly with the same GitHub App user token instead of copying `BRIDGE_AUTH_TOKEN`
|
|
67
|
+
- That same in-app GitHub sign-in 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
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
|
|
69
|
+
- Older saved GitHub Codespaces sessions may need one fresh sign-in from the app so the stored GitHub App token and refresh token are updated
|
|
70
70
|
|
|
71
71
|
Manual recovery if port visibility does not update automatically:
|
|
72
72
|
|
|
@@ -271,7 +271,8 @@ npm run teardown -- --yes
|
|
|
271
271
|
| Variable | Purpose |
|
|
272
272
|
|---|---|
|
|
273
273
|
| `EXPO_PUBLIC_HOST_BRIDGE_TOKEN` | token used by local mobile dev builds |
|
|
274
|
-
| `
|
|
274
|
+
| `EXPO_PUBLIC_GITHUB_APP_CLIENT_ID` | GitHub App client ID for in-app Codespaces sign-in |
|
|
275
|
+
| `EXPO_PUBLIC_GITHUB_APP_SLUG` | GitHub App slug used to open install/manage-access pages for repository selection |
|
|
275
276
|
| `EXPO_PUBLIC_GITHUB_CODESPACES_PORT_FORWARDING_DOMAIN` | forwarded port domain used to derive Codespaces bridge URLs (`app.github.dev` by default) |
|
|
276
277
|
| `EXPO_PUBLIC_GITHUB_CODESPACES_REPO_NAME` | repository name to sort matching Codespaces first in the in-app picker |
|
|
277
278
|
| `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 |
|
package/docs/troubleshooting.md
CHANGED
|
@@ -34,7 +34,7 @@ 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
|
|
37
|
+
- For GitHub-auth Codespaces profiles, reopen `GitHub Codespaces` in the app and sign in with GitHub again if the GitHub App token or refresh token was revoked or expired.
|
|
38
38
|
- For a local dev build, also ensure `BRIDGE_AUTH_TOKEN` in `.env.secure` matches `EXPO_PUBLIC_HOST_BRIDGE_TOKEN` in `apps/mobile/.env`.
|
|
39
39
|
- Restart the bridge after token changes.
|
|
40
40
|
- On secure-launcher installs, `Settings > Bridge Maintenance > Restart bridge safely` can do that from the phone.
|
|
@@ -53,7 +53,8 @@ gh codespace ports visibility 8787:public 8788:public
|
|
|
53
53
|
|
|
54
54
|
- If `gh` is unavailable in the codespace, use the Codespaces `Ports` panel and change both forwarded ports to `Public`.
|
|
55
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 `
|
|
56
|
+
- If GitHub direct sign-in is not showing in the app, confirm the build includes `EXPO_PUBLIC_GITHUB_APP_CLIENT_ID` and `EXPO_PUBLIC_GITHUB_APP_SLUG`.
|
|
57
|
+
- If the app signs in but still cannot create a Codespace or clone/push inside it, reopen the GitHub App access step in the app and make sure the template repo and any target repos are selected for the installation.
|
|
57
58
|
- 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
|
|
|
59
60
|
## GitHub Codespaces bootstrap did not start the bridge
|
package/package.json
CHANGED
|
@@ -392,10 +392,10 @@ async fn install_github_git_auth(
|
|
|
392
392
|
access_token: &str,
|
|
393
393
|
) -> Result<GitHubAuthInstallResponse, BridgeError> {
|
|
394
394
|
let viewer = fetch_github_viewer(state, access_token).await?;
|
|
395
|
-
if !
|
|
395
|
+
if !github_token_can_be_used_for_git_auth(&viewer.scopes) {
|
|
396
396
|
return Err(BridgeError::forbidden(
|
|
397
397
|
"github_repo_scope_required",
|
|
398
|
-
"GitHub repository access is required. Sign in again from the app and approve repository access.",
|
|
398
|
+
"GitHub repository access is required. Sign in again from the app and approve the required repository access.",
|
|
399
399
|
));
|
|
400
400
|
}
|
|
401
401
|
|
|
@@ -485,6 +485,10 @@ fn github_scopes_allow_repo_access(scopes: &[String]) -> bool {
|
|
|
485
485
|
.any(|scope| scope == "repo" || scope == "public_repo")
|
|
486
486
|
}
|
|
487
487
|
|
|
488
|
+
fn github_token_can_be_used_for_git_auth(scopes: &[String]) -> bool {
|
|
489
|
+
scopes.is_empty() || github_scopes_allow_repo_access(scopes)
|
|
490
|
+
}
|
|
491
|
+
|
|
488
492
|
fn resolve_github_credentials_file_path() -> Result<PathBuf, BridgeError> {
|
|
489
493
|
let home = read_non_empty_env("HOME")
|
|
490
494
|
.ok_or_else(|| BridgeError::server("HOME is not set; cannot install GitHub auth"))?;
|
|
@@ -7326,7 +7330,27 @@ fn bridge_chatgpt_auth_cache() -> &'static StdRwLock<Option<BridgeChatGptAuthBun
|
|
|
7326
7330
|
CACHE.get_or_init(|| StdRwLock::new(None))
|
|
7327
7331
|
}
|
|
7328
7332
|
|
|
7333
|
+
#[cfg(test)]
|
|
7334
|
+
fn bridge_chatgpt_auth_cache_path_override() -> &'static StdRwLock<Option<PathBuf>> {
|
|
7335
|
+
static OVERRIDE: OnceLock<StdRwLock<Option<PathBuf>>> = OnceLock::new();
|
|
7336
|
+
OVERRIDE.get_or_init(|| StdRwLock::new(None))
|
|
7337
|
+
}
|
|
7338
|
+
|
|
7339
|
+
#[cfg(test)]
|
|
7340
|
+
fn set_bridge_chatgpt_auth_cache_path_override(path: Option<PathBuf>) {
|
|
7341
|
+
if let Ok(mut guard) = bridge_chatgpt_auth_cache_path_override().write() {
|
|
7342
|
+
*guard = path;
|
|
7343
|
+
}
|
|
7344
|
+
}
|
|
7345
|
+
|
|
7329
7346
|
fn resolve_bridge_chatgpt_auth_cache_path() -> Option<PathBuf> {
|
|
7347
|
+
#[cfg(test)]
|
|
7348
|
+
if let Ok(guard) = bridge_chatgpt_auth_cache_path_override().read() {
|
|
7349
|
+
if let Some(path) = guard.clone() {
|
|
7350
|
+
return Some(path);
|
|
7351
|
+
}
|
|
7352
|
+
}
|
|
7353
|
+
|
|
7330
7354
|
let workdir = read_non_empty_env("BRIDGE_WORKDIR").map(PathBuf::from)?;
|
|
7331
7355
|
Some(workdir.join(BRIDGE_CHATGPT_AUTH_CACHE_FILE_NAME))
|
|
7332
7356
|
}
|
|
@@ -11859,6 +11883,51 @@ fn normalize_path(path: &Path) -> PathBuf {
|
|
|
11859
11883
|
mod tests {
|
|
11860
11884
|
use super::*;
|
|
11861
11885
|
|
|
11886
|
+
fn bridge_chatgpt_auth_test_lock() -> &'static std::sync::Mutex<()> {
|
|
11887
|
+
static LOCK: OnceLock<std::sync::Mutex<()>> = OnceLock::new();
|
|
11888
|
+
LOCK.get_or_init(|| std::sync::Mutex::new(()))
|
|
11889
|
+
}
|
|
11890
|
+
|
|
11891
|
+
struct TestBridgeChatGptAuthCacheScope {
|
|
11892
|
+
_guard: std::sync::MutexGuard<'static, ()>,
|
|
11893
|
+
temp_dir: PathBuf,
|
|
11894
|
+
}
|
|
11895
|
+
|
|
11896
|
+
impl TestBridgeChatGptAuthCacheScope {
|
|
11897
|
+
fn new() -> Self {
|
|
11898
|
+
let guard = bridge_chatgpt_auth_test_lock()
|
|
11899
|
+
.lock()
|
|
11900
|
+
.unwrap_or_else(|poisoned| poisoned.into_inner());
|
|
11901
|
+
clear_cached_bridge_chatgpt_auth();
|
|
11902
|
+
|
|
11903
|
+
let nonce = SystemTime::now()
|
|
11904
|
+
.duration_since(SystemTime::UNIX_EPOCH)
|
|
11905
|
+
.expect("valid time")
|
|
11906
|
+
.as_nanos();
|
|
11907
|
+
let temp_dir = env::temp_dir().join(format!(
|
|
11908
|
+
"clawdex-bridge-chatgpt-auth-test-{}-{nonce}",
|
|
11909
|
+
std::process::id()
|
|
11910
|
+
));
|
|
11911
|
+
std::fs::create_dir_all(&temp_dir).expect("create auth cache test dir");
|
|
11912
|
+
set_bridge_chatgpt_auth_cache_path_override(Some(
|
|
11913
|
+
temp_dir.join(BRIDGE_CHATGPT_AUTH_CACHE_FILE_NAME),
|
|
11914
|
+
));
|
|
11915
|
+
|
|
11916
|
+
Self {
|
|
11917
|
+
_guard: guard,
|
|
11918
|
+
temp_dir,
|
|
11919
|
+
}
|
|
11920
|
+
}
|
|
11921
|
+
}
|
|
11922
|
+
|
|
11923
|
+
impl Drop for TestBridgeChatGptAuthCacheScope {
|
|
11924
|
+
fn drop(&mut self) {
|
|
11925
|
+
clear_cached_bridge_chatgpt_auth();
|
|
11926
|
+
set_bridge_chatgpt_auth_cache_path_override(None);
|
|
11927
|
+
let _ = std::fs::remove_dir_all(&self.temp_dir);
|
|
11928
|
+
}
|
|
11929
|
+
}
|
|
11930
|
+
|
|
11862
11931
|
async fn build_test_bridge(hub: Arc<ClientHub>) -> Arc<AppServerBridge> {
|
|
11863
11932
|
let mut child = Command::new("cat")
|
|
11864
11933
|
.stdin(Stdio::piped())
|
|
@@ -13859,6 +13928,7 @@ mod tests {
|
|
|
13859
13928
|
|
|
13860
13929
|
#[tokio::test]
|
|
13861
13930
|
async fn successful_chatgpt_auth_token_login_populates_bridge_auth_cache() {
|
|
13931
|
+
let _auth_cache_scope = TestBridgeChatGptAuthCacheScope::new();
|
|
13862
13932
|
clear_cached_bridge_chatgpt_auth();
|
|
13863
13933
|
|
|
13864
13934
|
let hub = Arc::new(ClientHub::new());
|
|
@@ -13906,6 +13976,7 @@ mod tests {
|
|
|
13906
13976
|
|
|
13907
13977
|
#[tokio::test]
|
|
13908
13978
|
async fn successful_account_logout_clears_cached_bridge_chatgpt_auth() {
|
|
13979
|
+
let _auth_cache_scope = TestBridgeChatGptAuthCacheScope::new();
|
|
13909
13980
|
clear_cached_bridge_chatgpt_auth();
|
|
13910
13981
|
cache_bridge_chatgpt_auth(BridgeChatGptAuthBundle {
|
|
13911
13982
|
access_token: "cached-before-logout".to_string(),
|
|
@@ -14370,4 +14441,14 @@ mod tests {
|
|
|
14370
14441
|
"read:user".to_string()
|
|
14371
14442
|
]));
|
|
14372
14443
|
}
|
|
14444
|
+
|
|
14445
|
+
#[test]
|
|
14446
|
+
fn github_git_auth_accepts_github_app_user_tokens_without_scope_headers() {
|
|
14447
|
+
assert!(github_token_can_be_used_for_git_auth(&[]));
|
|
14448
|
+
assert!(github_token_can_be_used_for_git_auth(&["repo".to_string()]));
|
|
14449
|
+
assert!(!github_token_can_be_used_for_git_auth(&[
|
|
14450
|
+
"codespace".to_string(),
|
|
14451
|
+
"read:user".to_string()
|
|
14452
|
+
]));
|
|
14453
|
+
}
|
|
14373
14454
|
}
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|