refactor(#2352): route hivectl github set-token through a daemon wire command

This commit is contained in:
damocles 2026-07-14 22:17:09 +02:00
commit 6c654921a0
3 changed files with 49 additions and 14 deletions

View file

@ -696,7 +696,7 @@ async fn main() -> Result<()> {
agent,
token,
token_stdin,
} => github_set_token(&agent, token, token_stdin).await,
} => github_set_token(&socket, &agent, token, token_stdin).await,
},
Cmd::Gateway { cmd } => match cmd {
GatewayCmd::CreateUser {
@ -1234,7 +1234,15 @@ fn choom(name: &str, resume_session: Option<&str>) -> Result<()> {
/// into the agent's `github-token` state file (0600, agent-owned) via
/// hive-priv, so the agent's `gh` wrapper + git credential helper can
/// authenticate. Read live at invocation, so no rebuild/restart is needed.
async fn github_set_token(agent: &str, token: Option<String>, token_stdin: bool) -> Result<()> {
async fn github_set_token(
socket: &Path,
agent: &str,
token: Option<String>,
token_stdin: bool,
) -> Result<()> {
// Resolve + validate the token client-side (inline flag or stdin read);
// the daemon never touches this process's stdin. Persistence happens
// daemon-side via the privileged helper.
let token = match (token, token_stdin) {
(Some(t), _) => t,
(None, true) => {
@ -1250,12 +1258,15 @@ async fn github_set_token(agent: &str, token: Option<String>, token_stdin: bool)
if token.is_empty() {
bail!("refusing to write an empty GitHub token for agent '{agent}'");
}
hive_c0re::priv_client::write_agent_github_token(agent, &token).await?;
println!(
"wrote github-token for agent '{agent}' \
(read live by the gh wrapper / git credential helper no rebuild needed)"
);
Ok(())
daemon_request(
socket,
hive_host_sock::HostRequest::SetAgentGithubToken {
agent: agent.to_owned(),
token,
},
"github",
)
.await
}
async fn forge_create_user(
@ -1269,26 +1280,31 @@ async fn forge_create_user(
// agent-vs-operator branch, and token persistence now live in the
// daemon handler.
let password = resolve_password(password, password_stdin)?;
forge_request(
daemon_request(
socket,
hive_host_sock::HostRequest::ForgeCreateUser {
name: name.to_owned(),
password,
},
"forge",
)
.await
}
/// Send a forge provisioning request to the daemon and print its result
/// lines. Mirrors [`matrix_request`] — the daemon owns the provisioning
/// logic; hivectl just relays the outcome.
async fn forge_request(socket: &Path, req: hive_host_sock::HostRequest) -> Result<()> {
/// Send a provisioning request to the daemon and print its result lines.
/// The daemon owns the provisioning logic; hivectl just relays the outcome,
/// prefixing any error with `label` (e.g. `forge` / `github`).
async fn daemon_request(
socket: &Path,
req: hive_host_sock::HostRequest,
label: &str,
) -> Result<()> {
let resp = hive_c0re::client::request(socket, req)
.await
.with_context(|| format!("connect to daemon socket {}", socket.display()))?;
if !resp.ok {
bail!(
"forge: {}",
"{label}: {}",
resp.error.as_deref().unwrap_or("unknown error")
);
}

View file

@ -196,6 +196,9 @@ async fn dispatch(req: &HostRequest, coord: Arc<Coordinator>) -> HostResponse {
HostRequest::ForgeCreateUser { name, password } => {
handle_forge_create_user(name, password.as_deref()).await?
}
HostRequest::SetAgentGithubToken { agent, token } => {
handle_set_agent_github_token(agent, token).await?
}
})
}
.await;
@ -395,6 +398,16 @@ async fn handle_forge_create_user(name: &str, password: Option<&str>) -> Result<
Ok(HostResponse::messages(out))
}
async fn handle_set_agent_github_token(agent: &str, token: &str) -> Result<HostResponse> {
crate::priv_client::write_agent_github_token(agent, token)
.await
.with_context(|| format!("write github-token for agent {agent}"))?;
Ok(HostResponse::messages(vec![format!(
"wrote github-token for agent '{agent}' \
(read live by the gh wrapper / git credential helper no rebuild needed)"
)]))
}
async fn handle_matrix_sync_admin() -> Result<HostResponse> {
require_matrix_present().await?;
let register_token =

View file

@ -158,6 +158,12 @@ pub enum HostRequest {
#[serde(default)]
password: Option<String>,
},
/// Write (or overwrite) an agent's GitHub PAT under its state dir, via
/// the privileged helper. Daemon-side equivalent of `hivectl github
/// set-token`. `token` is resolved + non-empty-validated client-side
/// (inline flag or stdin); the daemon just persists it. Read live by
/// the agent's `gh` wrapper / git credential helper — no rebuild needed.
SetAgentGithubToken { agent: String, token: String },
}
/// Selects which container classes a hive-wide [`HostRequest::Stop`] /