clawdex-mobile 5.1.3-internal.5 → 5.1.3-internal.6

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clawdex-mobile",
3
- "version": "5.1.3-internal.5",
3
+ "version": "5.1.3-internal.6",
4
4
  "description": "Private-network mobile bridge and CLI for Codex and OpenCode",
5
5
  "keywords": [
6
6
  "codex",
@@ -149,7 +149,7 @@ dependencies = [
149
149
 
150
150
  [[package]]
151
151
  name = "codex-rust-bridge"
152
- version = "5.1.3-internal.5"
152
+ version = "5.1.3-internal.6"
153
153
  dependencies = [
154
154
  "axum",
155
155
  "base64",
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "codex-rust-bridge"
3
- version = "5.1.3-internal.5"
3
+ version = "5.1.3-internal.6"
4
4
  edition = "2021"
5
5
 
6
6
  [dependencies]
@@ -388,42 +388,91 @@ struct GitHubViewer {
388
388
  scopes: Vec<String>,
389
389
  }
390
390
 
391
+ #[derive(Debug, Clone)]
392
+ struct ResolvedGitHubAuthGrant {
393
+ access_token: String,
394
+ repositories: Vec<String>,
395
+ }
396
+
391
397
  async fn install_github_git_auth(
392
398
  state: &Arc<AppState>,
393
- access_token: &str,
394
- repositories: &[String],
399
+ request: GitHubAuthInstallRequest,
395
400
  ) -> Result<GitHubAuthInstallResponse, BridgeError> {
396
- let viewer = fetch_github_viewer(state, access_token).await?;
397
- if !github_token_can_be_used_for_git_auth(&viewer.scopes) {
398
- return Err(BridgeError::forbidden(
399
- "github_repo_scope_required",
400
- "GitHub repository access is required. Sign in again from the app and approve the required repository access.",
401
+ let resolved_grants = resolve_github_auth_grants(request)?;
402
+ if resolved_grants.is_empty() {
403
+ return Err(BridgeError::invalid_params(
404
+ "At least one GitHub auth grant is required",
401
405
  ));
402
406
  }
403
407
 
404
- let normalized_repositories = normalize_github_auth_repositories(repositories);
408
+ let mut login = None;
409
+ let mut scopes = Vec::new();
410
+ if let Some(first_grant) = resolved_grants.first() {
411
+ if let Ok(viewer) = fetch_github_viewer(state, &first_grant.access_token).await {
412
+ if !github_token_can_be_used_for_git_auth(&viewer.scopes) {
413
+ return Err(BridgeError::forbidden(
414
+ "github_repo_scope_required",
415
+ "GitHub repository access is required. Sign in again from the app and approve the required repository access.",
416
+ ));
417
+ }
418
+ login = Some(viewer.login);
419
+ scopes = viewer.scopes;
420
+ }
421
+ }
422
+
405
423
  let credentials_file = resolve_github_credentials_file_path()?;
406
424
  let git_config_file = resolve_github_git_config_file_path()?;
407
425
  ensure_private_parent_dir(&credentials_file).await?;
408
- write_github_credentials_file(&credentials_file, access_token, &normalized_repositories)
409
- .await?;
410
- write_github_git_config_file(
411
- &git_config_file,
412
- &credentials_file,
413
- &normalized_repositories,
414
- )
415
- .await?;
426
+ write_github_credentials_file(&credentials_file, &resolved_grants).await?;
427
+ write_github_git_config_file(&git_config_file, &credentials_file, &resolved_grants).await?;
416
428
  configure_git_credential_store(state, &credentials_file, &git_config_file).await?;
417
429
 
418
430
  Ok(GitHubAuthInstallResponse {
419
431
  installed: true,
420
432
  host: GITHUB_HOST.to_string(),
421
- login: viewer.login,
422
- scopes: viewer.scopes,
433
+ login,
434
+ scopes,
423
435
  credential_file: credentials_file.to_string_lossy().to_string(),
436
+ grants_installed: resolved_grants.len(),
424
437
  })
425
438
  }
426
439
 
440
+ fn resolve_github_auth_grants(
441
+ request: GitHubAuthInstallRequest,
442
+ ) -> Result<Vec<ResolvedGitHubAuthGrant>, BridgeError> {
443
+ let raw_grants = if let Some(grants) = request.grants {
444
+ grants
445
+ } else if let Some(access_token) = request.access_token {
446
+ vec![GitHubAuthGrantInput {
447
+ access_token,
448
+ repositories: request.repositories,
449
+ }]
450
+ } else {
451
+ Vec::new()
452
+ };
453
+
454
+ let mut grants = Vec::new();
455
+ for grant in raw_grants {
456
+ let access_token = grant.access_token.trim().to_string();
457
+ if access_token.is_empty() {
458
+ continue;
459
+ }
460
+
461
+ let repositories =
462
+ normalize_github_auth_repositories(grant.repositories.as_deref().unwrap_or(&[]));
463
+ if repositories.is_empty() {
464
+ continue;
465
+ }
466
+
467
+ grants.push(ResolvedGitHubAuthGrant {
468
+ access_token,
469
+ repositories,
470
+ });
471
+ }
472
+
473
+ Ok(grants)
474
+ }
475
+
427
476
  async fn fetch_github_viewer(
428
477
  state: &Arc<AppState>,
429
478
  access_token: &str,
@@ -565,18 +614,20 @@ async fn ensure_private_parent_dir(path: &Path) -> Result<(), BridgeError> {
565
614
 
566
615
  async fn write_github_credentials_file(
567
616
  credentials_file: &Path,
568
- access_token: &str,
569
- repositories: &[String],
617
+ grants: &[ResolvedGitHubAuthGrant],
570
618
  ) -> Result<(), BridgeError> {
571
619
  let mut content = String::new();
572
- let trimmed_access_token = access_token.trim();
573
- for repository in repositories {
574
- content.push_str(&format!(
575
- "https://x-access-token:{trimmed_access_token}@{GITHUB_HOST}/{repository}\n"
576
- ));
577
- content.push_str(&format!(
578
- "https://x-access-token:{trimmed_access_token}@{GITHUB_HOST}/{repository}.git\n"
579
- ));
620
+ for grant in grants {
621
+ for repository in &grant.repositories {
622
+ content.push_str(&format!(
623
+ "https://x-access-token:{}@{GITHUB_HOST}/{repository}\n",
624
+ grant.access_token
625
+ ));
626
+ content.push_str(&format!(
627
+ "https://x-access-token:{}@{GITHUB_HOST}/{repository}.git\n",
628
+ grant.access_token
629
+ ));
630
+ }
580
631
  }
581
632
 
582
633
  fs::write(credentials_file, content)
@@ -600,21 +651,23 @@ async fn write_github_credentials_file(
600
651
  async fn write_github_git_config_file(
601
652
  git_config_file: &Path,
602
653
  credentials_file: &Path,
603
- repositories: &[String],
654
+ grants: &[ResolvedGitHubAuthGrant],
604
655
  ) -> Result<(), BridgeError> {
605
656
  let helper_value = format!("store --file {}", credentials_file.to_string_lossy());
606
657
  let mut content = String::from(
607
658
  "[credential \"https://github.com\"]\n\tuseHttpPath = true\n[url \"https://github.com/\"]\n\tinsteadOf = git@github.com:\n\tinsteadOf = ssh://git@github.com/\n",
608
659
  );
609
660
 
610
- for repository in repositories {
611
- for context in [
612
- format!("https://{GITHUB_HOST}/{repository}"),
613
- format!("https://{GITHUB_HOST}/{repository}.git"),
614
- ] {
615
- content.push_str(&format!(
616
- "[credential \"{context}\"]\n\thelper =\n\thelper = {helper_value}\n\tusername = x-access-token\n"
617
- ));
661
+ for grant in grants {
662
+ for repository in &grant.repositories {
663
+ for context in [
664
+ format!("https://{GITHUB_HOST}/{repository}"),
665
+ format!("https://{GITHUB_HOST}/{repository}.git"),
666
+ ] {
667
+ content.push_str(&format!(
668
+ "[credential \"{context}\"]\n\thelper =\n\thelper = {helper_value}\n\tusername = x-access-token\n"
669
+ ));
670
+ }
618
671
  }
619
672
  }
620
673
 
@@ -5569,6 +5622,14 @@ struct GitQueryRequest {
5569
5622
  #[derive(Debug, Clone, Serialize, Deserialize)]
5570
5623
  #[serde(rename_all = "camelCase")]
5571
5624
  struct GitHubAuthInstallRequest {
5625
+ access_token: Option<String>,
5626
+ repositories: Option<Vec<String>>,
5627
+ grants: Option<Vec<GitHubAuthGrantInput>>,
5628
+ }
5629
+
5630
+ #[derive(Debug, Clone, Serialize, Deserialize)]
5631
+ #[serde(rename_all = "camelCase")]
5632
+ struct GitHubAuthGrantInput {
5572
5633
  access_token: String,
5573
5634
  repositories: Option<Vec<String>>,
5574
5635
  }
@@ -5578,9 +5639,10 @@ struct GitHubAuthInstallRequest {
5578
5639
  struct GitHubAuthInstallResponse {
5579
5640
  installed: bool,
5580
5641
  host: String,
5581
- login: String,
5642
+ login: Option<String>,
5582
5643
  scopes: Vec<String>,
5583
5644
  credential_file: String,
5645
+ grants_installed: usize,
5584
5646
  }
5585
5647
 
5586
5648
  #[derive(Debug, Clone, Default, Serialize, Deserialize)]
@@ -6943,12 +7005,7 @@ async fn handle_bridge_method(
6943
7005
  let request: GitHubAuthInstallRequest =
6944
7006
  serde_json::from_value(params.unwrap_or_else(|| json!({})))
6945
7007
  .map_err(|error| BridgeError::invalid_params(&error.to_string()))?;
6946
- let result = install_github_git_auth(
6947
- state,
6948
- &request.access_token,
6949
- request.repositories.as_deref().unwrap_or(&[]),
6950
- )
6951
- .await?;
7008
+ let result = install_github_git_auth(state, request).await?;
6952
7009
  serde_json::to_value(result).map_err(|error| BridgeError::server(&error.to_string()))
6953
7010
  }
6954
7011
  "bridge/attachments/upload" => {