feat(github): read a repository with whichever gh account can see it (#987)

The GitHub panel borrowed only gh's active account, so a private repository
owned by an organisation another signed-in account belongs to read as a 404.

When the active account gets a 404, 401 or 403, retry with gh's other
github.com accounts and remember, per owner, the one that got through. The
other accounts are listed lazily (gh auth status checks each online), and a
token from GH_TOKEN/GITHUB_TOKEN keeps its no-fallback meaning.
This commit is contained in:
l0ng-ai
2026-09-28 10:59:25 +08:00
committed by GitHub
parent ce76de975c
commit 39776012e2
7 changed files with 519 additions and 35 deletions
+31 -10
View File
@@ -10,7 +10,9 @@
//! through the `Host` — the tree can be on another machine.
//! - **Which token.** Resolved once, on a worker, from the environment or the
//! GitHub CLI (`tty7_core::core::github::token`). Refresh resolves it again,
//! so a `gh auth login` in a pane takes effect without a restart.
//! so a `gh auth login` in a pane takes effect without a restart. A
//! repository the active `gh` account cannot see is retried with the
//! CLI's other accounts (`tty7_core::core::github::accounts`).
//! - **The requests.** Made from *this* machine, never the remote host, on a
//! dedicated thread each — a slow link must never hold a UI frame.
//!
@@ -141,18 +143,37 @@ where
}
/// The real connector: resolve a token (env, then `gh`), build the client.
/// A token from `gh` comes with the CLI's other accounts behind it, listed
/// only if the active one comes up short.
fn system_connector(proxy: Option<String>) -> Connector {
use tty7_core::core::github::accounts::{Account, FallbackTransport};
use tty7_core::core::github::http::HttpTransport;
use tty7_core::core::github::token::{self, TokenSource};
Arc::new(move || {
let token =
tty7_core::core::github::token::resolve_token_from_system().map(|(t, source)| {
log::info!("github: signed in via {source:?}");
t
});
let resolved = token::resolve_token_from_system();
if let Some((_, source)) = &resolved {
log::info!("github: signed in via {source:?}");
}
let from_gh = matches!(resolved, Some((_, TokenSource::GhCli)));
let first: Arc<dyn Transport> = Arc::new(HttpTransport::new(
resolved.map(|(t, _)| t),
proxy.as_deref(),
));
if !from_gh {
return Connection { transport: first };
}
let proxy = proxy.clone();
let load = Box::new(move || {
token::other_gh_accounts_from_system()
.into_iter()
.map(|(login, t)| Account {
login,
transport: Arc::new(HttpTransport::new(Some(t), proxy.as_deref())),
})
.collect()
});
Connection {
transport: Arc::new(tty7_core::core::github::http::HttpTransport::new(
token,
proxy.as_deref(),
)),
transport: Arc::new(FallbackTransport::new(first, load)),
}
})
}