diff --git a/hive-c0re/src/dashboard/matrix_accounts.rs b/hive-c0re/src/dashboard/matrix_accounts.rs index 6d0e5cfa..e526d7fd 100644 --- a/hive-c0re/src/dashboard/matrix_accounts.rs +++ b/hive-c0re/src/dashboard/matrix_accounts.rs @@ -262,6 +262,45 @@ pub(super) async fn post_matrix_account_login(Form(f): Form) -> axum::Json(MatrixLoginResult { ok: true, user_id }).into_response() } +/// Form body for `POST /api/github-account` (urlencoded, the dashboard's +/// mutation convention). Writes the operator-supplied PAT to the agent's +/// `github-token` file. The GitHub counterpart of the matrix login form, but +/// far simpler: no account creation, no homeserver, no login modes — the +/// operator pastes a PAT for an existing account. +#[derive(Deserialize)] +pub(super) struct GithubAccountForm { + agent: String, + token: String, +} + +#[derive(Serialize)] +struct GithubAccountResult { + ok: bool, +} + +/// Provision (or refresh) an agent's GitHub PAT from the dashboard +/// credentials tab. Validates the agent name, then writes the PAT to +/// `/github-token` (`0600`, agent-owned) via hive-priv. No account +/// creation and no daemon to kick — the agent's `gh` wrapper / git credential +/// helper read the file live, so the new token takes effect immediately. +/// Operator-authenticated (dashboard). Never echoes the token back — only +/// `{ ok: true }`. +pub(super) async fn post_github_account(Form(f): Form) -> Response { + let agent = f.agent.trim(); + let token = f.token.trim(); + if !is_plain_ident(agent) { + return error_response(&format!("github-account: invalid agent {agent:?}")); + } + if token.is_empty() { + return error_response("github-account: token is required"); + } + if let Err(e) = crate::priv_client::write_agent_github_token(agent, token).await { + return error_response(&format!("github-account: write token failed: {e:#}")); + } + tracing::info!(%agent, "github-account: provisioned github PAT"); + axum::Json(GithubAccountResult { ok: true }).into_response() +} + /// POST `m.login.password` to `/_matrix/client/v3/login`. /// Returns `(access_token, user_id)`. async fn matrix_password_login( diff --git a/hive-c0re/src/dashboard/mod.rs b/hive-c0re/src/dashboard/mod.rs index 18abd4e5..b3909047 100644 --- a/hive-c0re/src/dashboard/mod.rs +++ b/hive-c0re/src/dashboard/mod.rs @@ -188,6 +188,10 @@ pub async fn serve(port: u16, coord: Arc) -> Result<()> { "/api/matrix-account-login", post(matrix_accounts::post_matrix_account_login), ) + .route( + "/api/github-account", + post(matrix_accounts::post_github_account), + ) .route( "/api/cancel-reminder/{id}", post(reminders::post_cancel_reminder),