feat: auto-start local server from REPL

When no server is reachable, the REPL now automatically spawns a
qpq-server child process with dev defaults (--allow-insecure-auth,
matching TLS cert paths). The server is killed on REPL exit via a
Drop guard. Use --no-server to opt out (e.g. for remote servers).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-01 23:03:31 +01:00
parent c2762f93f6
commit e24497bf90
2 changed files with 330 additions and 13 deletions

View File

@@ -70,6 +70,10 @@ struct Args {
#[arg(long, env = "QPQ_PASSWORD")]
password: Option<String>,
/// Do not auto-start a local qpq-server (useful when connecting to a remote server).
#[arg(long, env = "QPQ_NO_SERVER")]
no_server: bool,
#[command(subcommand)]
command: Option<Command>,
}
@@ -301,6 +305,9 @@ enum Command {
/// OPAQUE password (prompted securely if --username is set but --password is not).
#[arg(long, env = "QPQ_PASSWORD")]
password: Option<String>,
/// Do not auto-start a local qpq-server.
#[arg(long, env = "QPQ_NO_SERVER")]
no_server: bool,
},
/// Interactive 1:1 chat: type to send, incoming messages printed as [peer] <msg>. Ctrl+D to exit.
@@ -350,11 +357,13 @@ async fn main() -> anyhow::Result<()> {
let state_pw = args.state_password.as_deref();
// Default to REPL when no subcommand is given.
let no_server = args.no_server;
let command = args.command.unwrap_or_else(|| Command::Repl {
state: args.state,
server: args.server,
username: args.username,
password: args.password,
no_server,
});
match command {
@@ -553,6 +562,7 @@ async fn main() -> anyhow::Result<()> {
server,
username,
password,
no_server,
} => {
let local = tokio::task::LocalSet::new();
local
@@ -566,6 +576,7 @@ async fn main() -> anyhow::Result<()> {
password.as_deref(),
&args.access_token,
args.device_id.as_deref(),
no_server,
))
.await
}