8 Commits

Author SHA1 Message Date
Uber Veng
80d2e7fd71 Fix first startup on macOS 2 2026-05-29 13:17:30 +07:00
Uber Veng
03e1311e90 update install script 5 2026-05-29 13:03:35 +07:00
Uber Veng
a93f3ad9f6 Fix first startup on macOS 2026-05-29 12:58:15 +07:00
Uber Veng
b0407d6519 update install script 4 2026-05-29 12:49:58 +07:00
Uber Veng
c484f2deb8 update install script 3 2026-05-29 11:55:33 +07:00
Uber Veng
958a729880 update install script 2 2026-05-29 11:52:08 +07:00
Uber Veng
8c6ed94850 update install script 2026-05-29 11:35:20 +07:00
Uber Veng
d4ca9e7542 first-run script fix 2026-05-29 05:54:10 +07:00
4 changed files with 53 additions and 21 deletions

2
Cargo.lock generated
View File

@@ -1203,7 +1203,7 @@ dependencies = [
[[package]]
name = "ostiary"
version = "1.1.1"
version = "1.1.4"
dependencies = [
"anyhow",
"base64",

View File

@@ -1,6 +1,6 @@
[package]
name = "ostiary"
version = "1.1.1"
version = "1.1.4"
edition = "2024"
[dependencies]

View File

@@ -108,4 +108,8 @@ fi
echo ""
ok "Launching ${BINARY}..."
exec "$INSTALL_DIR/$BINARY"
if { true </dev/tty; } 2>/dev/null; then
"$INSTALL_DIR/$BINARY"
else
ok "Run: $BINARY"
fi

View File

@@ -12,6 +12,8 @@ mod updater;
use std::io;
use anyhow::Result;
#[cfg(unix)]
use libc;
use crossterm::{
event::{DisableMouseCapture, EnableMouseCapture},
execute,
@@ -29,6 +31,18 @@ async fn main() -> Result<()> {
config::Config::create_with_endpoint(&url, &name)?
};
// Before initializing the TUI, ensure our process group is the terminal's
// foreground group. When launched from a non-interactive install script on
// macOS, the parent shell may not have granted terminal ownership, causing
// crossterm's kqueue-based input reader to fail silently.
#[cfg(unix)]
{
use std::os::unix::io::AsRawFd;
if let Ok(tty) = std::fs::File::open("/dev/tty") {
unsafe { libc::tcsetpgrp(tty.as_raw_fd(), libc::getpgrp()); }
}
}
enable_raw_mode()?;
let mut stdout = io::stdout();
execute!(stdout, EnterAlternateScreen, EnableMouseCapture)?;
@@ -170,44 +184,58 @@ async fn run_exec(
/// Runs before ratatui starts. Prompts for endpoint URL and name on first launch.
fn prompt_first_endpoint() -> Result<(String, String)> {
use std::io::Write;
use std::io::{BufRead, BufReader, Write};
println!();
println!(" ╔══════════════════════════════════╗");
println!(" ║ Ostiary — первый запуск ║");
println!(" ╚══════════════════════════════════╝");
println!();
println!(" Конфиг будет сохранён в:");
println!(" {}", config::config_path().display());
println!();
// Open /dev/tty read-only for keyboard input so the wizard works even when
// stdin is a pipe (e.g. `curl | bash`). Writing goes through stdout so we
// don't touch the terminal's write-side state before crossterm takes over.
let tty_in = std::fs::File::open("/dev/tty")
.map_err(|e| anyhow::anyhow!(
"Не удалось открыть терминал ({e}). Запустите ostiary напрямую."
))?;
let mut reader = BufReader::new(tty_in);
let mut out = std::io::stdout();
writeln!(out)?;
writeln!(out, " ╔══════════════════════════════════╗")?;
writeln!(out, " ║ Ostiary — первый запуск ║")?;
writeln!(out, " ╚══════════════════════════════════╝")?;
writeln!(out)?;
writeln!(out, " Конфиг будет сохранён в:")?;
writeln!(out, " {}", config::config_path().display())?;
writeln!(out)?;
let name = loop {
print!(" Название эндпоинта: ");
std::io::stdout().flush()?;
write!(out, " Название эндпоинта: ")?;
out.flush()?;
let mut buf = String::new();
std::io::stdin().read_line(&mut buf)?;
if reader.read_line(&mut buf)? == 0 {
anyhow::bail!("Неожиданный конец ввода.");
}
let val = buf.trim().to_string();
if val.is_empty() {
println!(" Название не может быть пустым. Попробуйте ещё раз.\n");
writeln!(out, " Название не может быть пустым. Попробуйте ещё раз.\n")?;
continue;
}
break val;
};
let url = loop {
print!(" URL эндпоинта: ");
std::io::stdout().flush()?;
write!(out, " URL эндпоинта: ")?;
out.flush()?;
let mut buf = String::new();
std::io::stdin().read_line(&mut buf)?;
if reader.read_line(&mut buf)? == 0 {
anyhow::bail!("Неожиданный конец ввода.");
}
let val = buf.trim().to_string();
if val.is_empty() {
println!(" URL не может быть пустым. Попробуйте ещё раз.\n");
writeln!(out, " URL не может быть пустым. Попробуйте ещё раз.\n")?;
continue;
}
break val;
};
println!();
writeln!(out)?;
Ok((url, name))
}