feat(client): v2 REPL over SDK with categorized help and tab-completion
925-line REPL replacing the 3317-line monolith — delegates all crypto, MLS, and RPC to quicproquo-sdk. 20 commands across 6 categories (messaging, groups, account, keys, utility, debug), rustyline tab completion, background event listener, auto-server-launch. Also adds SDK accessor methods (server_addr_string, config_state_path), WS bridge register handler, and README table formatting cleanup.
This commit is contained in:
@@ -165,6 +165,7 @@ async fn dispatch(state: &WsBridgeState, req: RpcRequest) -> RpcResponse {
|
||||
"send" => handle_send(state, req.id, &req.params),
|
||||
"receive" => handle_receive(state, req.id, &req.params),
|
||||
"deleteAccount" => handle_delete_account(state, req.id, &req.params),
|
||||
"register" => handle_register(state, req.id, &req.params),
|
||||
_ => RpcResponse::error(req.id, format!("unknown method: {}", req.method)),
|
||||
}
|
||||
}
|
||||
@@ -175,6 +176,89 @@ fn handle_health(id: serde_json::Value) -> RpcResponse {
|
||||
RpcResponse::success(id, serde_json::json!("ok"))
|
||||
}
|
||||
|
||||
fn handle_register(
|
||||
state: &WsBridgeState,
|
||||
id: serde_json::Value,
|
||||
params: &serde_json::Value,
|
||||
) -> RpcResponse {
|
||||
// Only allow in insecure-auth mode (development/demo).
|
||||
if !state.allow_insecure_auth {
|
||||
return RpcResponse::error(id, "register is only available in --allow-insecure-auth mode");
|
||||
}
|
||||
|
||||
// Rate limit.
|
||||
let auth_ctx = match extract_auth(state, params) {
|
||||
Ok(ctx) => ctx,
|
||||
Err(e) => return RpcResponse::error(id, e),
|
||||
};
|
||||
if let Err(e) = ws_check_rate_limit(state, &auth_ctx) {
|
||||
return RpcResponse::error(id, e);
|
||||
}
|
||||
|
||||
// Validate username.
|
||||
let username = match params.get("username").and_then(|v| v.as_str()) {
|
||||
Some(u) if !u.is_empty() => u,
|
||||
_ => return RpcResponse::error(id, "missing or empty 'username' param"),
|
||||
};
|
||||
if username.len() > 32 {
|
||||
return RpcResponse::error(id, "username must be at most 32 characters");
|
||||
}
|
||||
if !username.chars().all(|c| c.is_ascii_alphanumeric() || c == '_') {
|
||||
return RpcResponse::error(id, "username must be alphanumeric or underscore only");
|
||||
}
|
||||
|
||||
// Validate identity key.
|
||||
let ik_b64 = match params.get("identityKey").and_then(|v| v.as_str()) {
|
||||
Some(s) if !s.is_empty() => s,
|
||||
_ => return RpcResponse::error(id, "missing or empty 'identityKey' param"),
|
||||
};
|
||||
let identity_key = match B64.decode(ik_b64) {
|
||||
Ok(k) => k,
|
||||
Err(e) => return RpcResponse::error(id, format!("bad base64 identityKey: {e}")),
|
||||
};
|
||||
if identity_key.len() != 32 {
|
||||
return RpcResponse::error(id, "identityKey must be 32 bytes");
|
||||
}
|
||||
|
||||
// Check if username is already taken by a different key.
|
||||
match state.store.get_user_identity_key(username) {
|
||||
Ok(Some(existing)) if existing == identity_key => {
|
||||
// Idempotent: same key, return success.
|
||||
return RpcResponse::success(
|
||||
id,
|
||||
serde_json::json!({
|
||||
"username": username,
|
||||
"identityKey": B64.encode(&identity_key),
|
||||
}),
|
||||
);
|
||||
}
|
||||
Ok(Some(_)) => {
|
||||
return RpcResponse::error(id, "username already taken");
|
||||
}
|
||||
Ok(None) => {} // Available, proceed.
|
||||
Err(e) => return RpcResponse::error(id, format!("storage error: {e}")),
|
||||
}
|
||||
|
||||
// Store the mapping.
|
||||
if let Err(e) = state.store.store_user_identity_key(username, identity_key.clone()) {
|
||||
return RpcResponse::error(id, format!("storage error: {e}"));
|
||||
}
|
||||
|
||||
tracing::info!(
|
||||
username = %username,
|
||||
key_prefix = %hex::encode(&identity_key[..4]),
|
||||
"audit: ws_bridge register"
|
||||
);
|
||||
|
||||
RpcResponse::success(
|
||||
id,
|
||||
serde_json::json!({
|
||||
"username": username,
|
||||
"identityKey": B64.encode(&identity_key),
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
async fn handle_resolve_user(
|
||||
state: &WsBridgeState,
|
||||
id: serde_json::Value,
|
||||
|
||||
Reference in New Issue
Block a user