agentmux_srv/server/app_api/voice.rs
1// Copyright 2026, AgentMux Corp.
2// SPDX-License-Identifier: Apache-2.0
3
4//! `voice.checkPath` — live existence check for the local whisper.cpp CLI
5//! binary or GGML model file paths configured in Settings -> Recording.
6//!
7//! Exposes the exact same `Path::new(&p).exists()` check
8//! `crates/srv/src/server/voice.rs` already performs inline at actual
9//! transcription time (`voice.rs`'s `transcribe_local_whisper` /
10//! `ensure_local_model`) -- no new validation logic, just surfacing it
11//! proactively so the Settings UI can show live status instead of only
12//! failing at first-recording-attempt. See
13//! docs/specs/SPEC_SETTINGS_RECORDING_INPUT_SECTION_2026_08_19.md §3.
14
15use super::*;
16
17pub fn register(engine: &Arc<WshRpcEngine>, _state: &AppState) {
18 register_voice_check_path(engine);
19}
20
21fn register_voice_check_path(engine: &Arc<WshRpcEngine>) {
22 // Typed registration (SPEC_RPC_BINDINGS_CODEGEN_2026_09_07.md §3.1): the
23 // request/response structs are named in `rpc_types` and carry
24 // `#[derive(ts_rs::TS)]`, so `frontend/types/rpc/` gets them generated
25 // and `scripts/check-rpc-bindings.sh` fails the build if the two sides
26 // drift. Replaces a private `VoiceCheckPathReq` + an anonymous
27 // `json!({"exists": ..})`, neither of which the generator could see.
28 engine.register_typed(
29 COMMAND_VOICE_CHECK_PATH,
30 move |req: CommandVoiceCheckPathData, _ctx| async move {
31 let trimmed = req.path.trim();
32 let exists = !trimmed.is_empty() && std::path::Path::new(trimmed).exists();
33 Ok(VoiceCheckPathResult { exists })
34 },
35 );
36}