Compare commits
3 changed files with 30 additions and 94 deletions
|
|
@ -688,7 +688,7 @@ async fn main() -> Result<()> {
|
||||||
name,
|
name,
|
||||||
password,
|
password,
|
||||||
password_stdin,
|
password_stdin,
|
||||||
} => forge_create_user(&socket, &name, password.as_deref(), password_stdin).await,
|
} => forge_create_user(&name, password.as_deref(), password_stdin).await,
|
||||||
},
|
},
|
||||||
Cmd::Matrix { cmd } => run_matrix_cmd(&socket, cmd).await,
|
Cmd::Matrix { cmd } => run_matrix_cmd(&socket, cmd).await,
|
||||||
Cmd::Github { cmd } => match cmd {
|
Cmd::Github { cmd } => match cmd {
|
||||||
|
|
@ -1258,42 +1258,38 @@ async fn github_set_token(agent: &str, token: Option<String>, token_stdin: bool)
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn forge_create_user(
|
async fn forge_create_user(name: &str, password: Option<&str>, password_stdin: bool) -> Result<()> {
|
||||||
socket: &Path,
|
if !hive_c0re::forge::is_present().await {
|
||||||
name: &str,
|
|
||||||
password: Option<&str>,
|
|
||||||
password_stdin: bool,
|
|
||||||
) -> Result<()> {
|
|
||||||
// Resolve the password client-side (inline flag or stdin read); the
|
|
||||||
// daemon never touches this process's stdin. The is-present check, the
|
|
||||||
// agent-vs-operator branch, and token persistence now live in the
|
|
||||||
// daemon handler.
|
|
||||||
let password = resolve_password(password, password_stdin)?;
|
|
||||||
forge_request(
|
|
||||||
socket,
|
|
||||||
hive_host_sock::HostRequest::ForgeCreateUser {
|
|
||||||
name: name.to_owned(),
|
|
||||||
password,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.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<()> {
|
|
||||||
let resp = hive_c0re::client::request(socket, req)
|
|
||||||
.await
|
|
||||||
.with_context(|| format!("connect to daemon socket {}", socket.display()))?;
|
|
||||||
if !resp.ok {
|
|
||||||
bail!(
|
bail!(
|
||||||
"forge: {}",
|
"hive-forge container not running — wait for hive-c0re to start it before provisioning forge users"
|
||||||
resp.error.as_deref().unwrap_or("unknown error")
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
for line in &resp.messages {
|
let user_password = resolve_password(password, password_stdin)?;
|
||||||
println!("{line}");
|
if agent_exists(name)? {
|
||||||
|
if user_password.is_some() {
|
||||||
|
bail!(
|
||||||
|
"forge create-user: --password / --password-stdin is for non-agent (operator) accounts only; '{name}' is an agent which authenticates via API token"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
hive_c0re::forge::ensure_user_for(name)
|
||||||
|
.await
|
||||||
|
.with_context(|| format!("forge create-user {name}"))?;
|
||||||
|
let path = Coordinator::agent_notes_dir(name).join("forge-token");
|
||||||
|
println!("forge: provisioned agent user '{name}'");
|
||||||
|
println!("token persisted at: {}", path.display());
|
||||||
|
} else {
|
||||||
|
let token = hive_c0re::forge::provision_user_token(name, user_password.as_deref())
|
||||||
|
.await
|
||||||
|
.with_context(|| format!("forge create-user {name}"))?;
|
||||||
|
println!("forge: provisioned user '{name}' (not an agent — token not persisted)");
|
||||||
|
println!("token: {token}");
|
||||||
|
if password.is_some() || password_stdin {
|
||||||
|
println!("password: set as supplied — use it to log into the forge web UI");
|
||||||
|
} else {
|
||||||
|
println!(
|
||||||
|
"password: random throwaway (not surfaced — pass --password or --password-stdin to set one you can use)"
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -74,12 +74,6 @@ async fn handle(stream: UnixStream, coord: Arc<Coordinator>) -> Result<()> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(
|
|
||||||
clippy::too_many_lines,
|
|
||||||
reason = "flat one-arm-per-HostRequest-variant router; each arm just \
|
|
||||||
delegates to a handler. Splitting the match would scatter the \
|
|
||||||
wire-command routing without shrinking it."
|
|
||||||
)]
|
|
||||||
async fn dispatch(req: &HostRequest, coord: Arc<Coordinator>) -> HostResponse {
|
async fn dispatch(req: &HostRequest, coord: Arc<Coordinator>) -> HostResponse {
|
||||||
let result: anyhow::Result<HostResponse> = async {
|
let result: anyhow::Result<HostResponse> = async {
|
||||||
Ok(match req {
|
Ok(match req {
|
||||||
|
|
@ -193,9 +187,6 @@ async fn dispatch(req: &HostRequest, coord: Arc<Coordinator>) -> HostResponse {
|
||||||
HostRequest::MatrixInvite { user, room } => {
|
HostRequest::MatrixInvite { user, room } => {
|
||||||
handle_matrix_invite(user, room.as_deref()).await?
|
handle_matrix_invite(user, room.as_deref()).await?
|
||||||
}
|
}
|
||||||
HostRequest::ForgeCreateUser { name, password } => {
|
|
||||||
handle_forge_create_user(name, password.as_deref()).await?
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
.await;
|
.await;
|
||||||
|
|
@ -355,46 +346,6 @@ async fn handle_matrix_create_user(name: &str, password: Option<&str>) -> Result
|
||||||
Ok(HostResponse::messages(out))
|
Ok(HostResponse::messages(out))
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn handle_forge_create_user(name: &str, password: Option<&str>) -> Result<HostResponse> {
|
|
||||||
if !crate::forge::is_present().await {
|
|
||||||
anyhow::bail!(
|
|
||||||
"hive-forge container not running — wait for hive-c0re to start it before provisioning forge users"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
let mut out = Vec::new();
|
|
||||||
if agent_exists(name)? {
|
|
||||||
if password.is_some() {
|
|
||||||
// Agents authenticate by API token, never by password — refuse
|
|
||||||
// rather than silently dropping a supplied one.
|
|
||||||
anyhow::bail!(
|
|
||||||
"forge create-user: a password is for non-agent (operator) accounts only; '{name}' is an agent which authenticates via API token"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
crate::forge::ensure_user_for(name)
|
|
||||||
.await
|
|
||||||
.with_context(|| format!("forge create-user {name}"))?;
|
|
||||||
let path = Coordinator::agent_notes_dir(name).join("forge-token");
|
|
||||||
out.push(format!("forge: provisioned agent user '{name}'"));
|
|
||||||
out.push(format!("token persisted at: {}", path.display()));
|
|
||||||
} else {
|
|
||||||
let token = crate::forge::provision_user_token(name, password)
|
|
||||||
.await
|
|
||||||
.with_context(|| format!("forge create-user {name}"))?;
|
|
||||||
out.push(format!(
|
|
||||||
"forge: provisioned user '{name}' (not an agent — token not persisted)"
|
|
||||||
));
|
|
||||||
out.push(format!("token: {token}"));
|
|
||||||
if password.is_some() {
|
|
||||||
out.push("password: set as supplied — use it to log into the forge web UI".to_owned());
|
|
||||||
} else {
|
|
||||||
out.push(
|
|
||||||
"password: random throwaway (not surfaced — pass --password or --password-stdin to set one you can use)".to_owned(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(HostResponse::messages(out))
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn handle_matrix_sync_admin() -> Result<HostResponse> {
|
async fn handle_matrix_sync_admin() -> Result<HostResponse> {
|
||||||
require_matrix_present().await?;
|
require_matrix_present().await?;
|
||||||
let register_token =
|
let register_token =
|
||||||
|
|
|
||||||
|
|
@ -147,17 +147,6 @@ pub enum HostRequest {
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
room: Option<String>,
|
room: Option<String>,
|
||||||
},
|
},
|
||||||
/// Create or refresh a forge account + API token for `name`. Daemon-side
|
|
||||||
/// equivalent of `hivectl forge create-user`: for an existing agent it
|
|
||||||
/// provisions the account and persists the token to `<notes>/forge-token`;
|
|
||||||
/// for a non-agent (operator/human) it mints a user and returns the token
|
|
||||||
/// in [`HostResponse::messages`]. `password` is resolved client-side
|
|
||||||
/// (inline flag or stdin) and only meaningful for non-agent accounts.
|
|
||||||
ForgeCreateUser {
|
|
||||||
name: String,
|
|
||||||
#[serde(default)]
|
|
||||||
password: Option<String>,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Selects which container classes a hive-wide [`HostRequest::Stop`] /
|
/// Selects which container classes a hive-wide [`HostRequest::Stop`] /
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue