ๆฌๆๆกฃๆ่ฟฐ Sayso ็ๆๆฏๆถๆใๅ ณ้ฎ่ฎพ่ฎกๅณ็ญๅ็ปไปถไบคไบใ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Sayso Desktop App โ
โ (Tauri v2: Rust + React) โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ โโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโ โ
โ โ Frontend โ โ Backend โ โ Platform โ โ External โ โ
โ โ (React) โ โ (Rust) โ โ APIs โ โ APIs โ โ
โ โโโโโโโโฌโโโโโโโโ โโโโโโโโฌโโโโโโโโ โโโโโโโโฌโโโโโโโโ โโโโโโโโฌโโโโโโโโ โ
โ โ โ โ โ โ
โ Settings UI Core Engine Audio/Mic STT/LLM โ
โ Statistics โโโโโโโโโโโโโโโ โโโโโโโโโโโ โโโโโโโโโโโโ โ
โ Onboarding โ 5-State โ โ cpal โ โ OpenAI โ โ
โ โ FSM โ โ โ โ Groq โ โ
โ โโโโโโโโฌโโโโโโโ โโโโโโฌโโโโโ โ etc. โ โ
โ โ โ โโโโโโโโโโโโ โ
โ โโโโโโโโดโโโโโโโ โโโโโโดโโโโโ โ
โ โ Recording โ โ Audio โ โ
โ โ Pipeline โ โ Capture โ โ
โ โโโโโโโโฌโโโโโโโ โโโโโโโโโโโ โ
โ โ โ
โ โโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโ โ
โ โผ โผ โผ โ
โ โโโโโโโโโโโ โโโโโโโโโโโโ โโโโโโโโโโโโ โ
โ โ Mode A โ โ Mode B โ โ Mode C โ โ
โ โ(Type) โ โ(Type+Sendโ โ(Command) โ โ
โ โโโโโโฌโโโโโ โโโโโโฌโโโโโโ โโโโโโฌโโโโโโ โ
โ โ โ โ โ
โ โโโโโโดโโโโโ โโโโโโดโโโโโ โโโโโโโดโโโโโโโ โ
โ โ enigo โ โ enigo โ โ Safety โ โ
โ โ CGEvent โ โ CGEvent โ โ Filter โ โ
โ โFallback โ โ + Enter โ โ(Rule+LLM) โ โ
โ โโโโโโโโโโโ โโโโโโโโโโโ โโโโโโโฌโโโโโโโ โ
โ โ โ
โ โโโโโโโดโโโโโโโ โ
โ โ Intent โ โ
โ โ Parser โ โ
โ โโโโโโโฌโโโโโโโ โ
โ โ โ
โ โโโโโโโดโโโโโโโ โ
โ โ Shell โ โ
โ โ Executor โ โ
โ โ (Direct) โ โ
โ โโโโโโโโโโโโโโ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
ๆไปถ: src-tauri/src/fsm.rs
pub enum FsmState {
Idle,
Recording { handle: RecordingHandle },
SttWaiting { audio_data: Vec<u8> },
Injecting { text: String },
Done,
Error { message: String },
}็ถๆ่ฝฌๆข่งๅ๏ผ
| ไป็ถๆ | ่งฆๅๆกไปถ | ็ฎๆ ็ถๆ |
|---|---|---|
| Idle | ็ญ้ฎๆไธ | Recording |
| Recording | ็ญ้ฎ้ๆพ | SttWaiting |
| SttWaiting | STT ๅฎๆ | Injecting |
| SttWaiting | STT ๅคฑ่ดฅ | Error |
| Injecting | ๆณจๅ ฅๅฎๆ | Done |
| Injecting | ๆณจๅ ฅๅคฑ่ดฅ | Error |
| Done/Error | ้็ฝฎ | Idle |
ๅ ณ้ฎ่ฎพ่ฎก: ๆๆ็ถๆ่ฝฌๆข้ฝๆฏๅๆญฅ็๏ผๅชๆๆฐๆฎๅค็ๆฏๅผๆญฅ็ใ่ฟ็กฎไฟไบ FSM ็็บฟ็จๅฎๅ จๆงใ
ๆไปถ: src-tauri/src/audio.rs
- ๆ่ท: ไฝฟ็จ
cpalcrate ่ฟ่ก่ทจๅนณๅฐ้ณ้ขๆ่ท - ๆ ผๅผ: ๆฏๆ f32 ๆ ผๅผ๏ผi16/u16 ้่ฆ้ขๅคๅค็๏ผ
- ้ๆทท: ๅคๅฃฐ้้ณ้ข่ชๅจๅนณๅไธบๅๅฃฐ้
- ็ผ็ : ๅฝๅถๅฎๆๅ็ผ็ ไธบ WAV ๆ ผๅผ (16 kHz, 16-bit, mono)
// ้ณ้ขๅ่ฐไธญๆง่ก้ๆทท
let sample_sum: f32 = data.iter().step_by(channels).sum();
let mixed_sample = sample_sum / channels as f32;ๆไปถ: src-tauri/src/stt.rs
- ๅ่ฎฎ: OpenAI
/audio/transcriptionsAPI ๅ ผๅฎน - ่ถ ๆถ: 120 ็ง๏ผ2รๆๅคงๅฝ้ณๆถ้ฟ๏ผ
- ้่ฏ: ๆ ๏ผfail-fast๏ผ้่ฏฏ้่ฟ Toast ้็ฅ๏ผ
ๆไปถ: src-tauri/src/llm.rs
ๅ ฑไบซ HTTP ๅฎขๆท็ซฏไพไธไธช็ปไปถไฝฟ็จ๏ผ
- TextPolisher: ๆถฆ่ฒ่ฏญ้ณ่ฝฌๅฝๆๅญ๏ผๅฏ้๏ผMode A/B๏ผ
- SafetyFilter: ๅฝไปค่ฏญไนๅฎๅ จๆฃๆฅ๏ผMode C๏ผ
- IntentParser: ่งฃๆ่ช็ถ่ฏญ่จไธบ็ปๆๅๅฝไปค๏ผMode C๏ผ
่ถ ๆถ่ฎพ็ฝฎ๏ผ
- ๆฎ้่ฏทๆฑ: 15 ็ง
- ๅฝไปคๆง่ก: 30 ็ง
ๆไปถ: src-tauri/src/injector.rs
ไธคๅฑๆณจๅ ฅ็ญ็ฅ๏ผ
pub enum InjectStrategy {
Enigo, // ้ฆ้๏ผ่ทจๅนณๅฐ keystroke ๆจกๆ
Clipboard, // ๅ้๏ผๅช่ดดๆฟ + ็ฒ่ดด
}macOS ็นๆ: CGEvent ๅ้๏ผ็จไบๅค็ๆไบไธๆฏๆ enigo ็ๅบ็จใ
็ฆ็นๆฃๆฅ: ๆณจๅ ฅๅๆฃๆฅๅฝๅ็ชๅฃๆฏๅฆไธ็ญ้ฎ้ๆพๆถไธ่ด๏ผ้ฒๆญขๆๅญๆณจๅ ฅๅฐ้่ฏฏ็ชๅฃใ
ๅช่ดดๆฟ็ญ็ฅ: ไธๅๆขๅคๅๅช่ดดๆฟๅ ๅฎน๏ผ่ฎพ่ฎกๅณ็ญ๏ผ้ฟๅ ๆฐๆฎ็ซไบ้ฃ้ฉ๏ผใ
ๆไปถ: src-tauri/src/safety.rs
ไธคๅฑๅฎๅ จๆถๆ๏ผ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Safety Pipeline โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ Layer 1: Rule-based Filter (O(1)) โ
โ - Block list: rm -rf /, mkfs.*, dd if=/dev/zero, etc. โ
โ - Allow list: ls, cat, pwd, git status, etc. โ
โ โ
โ Layer 2: LLM Semantic Filter โ
โ - Gray-zone commands sent to LLM for judgment โ
โ - Fail-closed: LLM unavailable โ reject โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
ๆไปถ: src-tauri/src/executor.rs
ๅฎๅ จ่ฎพ่ฎก: ็ดๆฅๆง่ก็จๅบ๏ผไธ้่ฟ shell
// ๅฎๅ
จ๏ผ็ดๆฅๆง่ก๏ผๆ shell ๆณจๅ
ฅ้ฃ้ฉ
let output = tokio::process::Command::new(&program)
.args(&args)
.kill_on_drop(true) // ่ถ
ๆถๅ่ชๅจ็ปๆญข
.output()
.await?;ๅๆฐ่งฃๆไฝฟ็จ shell-words crate ๅค็ๅธฆๅผๅท็ๅๆฐ๏ผๅฆ่ทฏๅพๅซ็ฉบๆ ผ๏ผใ
PATH ๅค็: ่ชๅจ่กฅๅ
จๅธธ่ง่ทฏๅพ๏ผ/opt/homebrew/bin, /usr/local/bin ็ญ๏ผ๏ผ่งฃๅณไป Finder ๅฏๅจๆถ็ผบๅฐ PATH ็้ฎ้ขใ
ๆไปถ: src-tauri/src/stats.rs
ๅญๅจไบ ~/Library/Application Support/com.sayso.app/stats.json๏ผ
{
"total_sessions": 100,
"total_chars": 50000,
"total_words": 8000,
"speaking_time_seconds": 3600,
"time_saved_seconds": 7200,
"commands_executed": 50,
"last_updated": "2026-03-23T12:00:00Z"
}ๅ ๅญ็ผๅญ: ๅฏๅจๆถๅ ่ฝฝๅฐๅ ๅญ๏ผๆดๆฐๆถๅ ๅๅ ๅญๅๅผๆญฅๅท็ใ
Hotkey Press โโโ
โผ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ 1. FSM: Idle โ Recording โ
โ - Start cpal audio stream โ
โ - Begin capturing samples โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
Hotkey Release โผ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ 2. FSM: Recording โ SttWaiting โ
โ - Stop audio stream โ
โ - Encode to WAV โ
โ - Send to STT API โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
STT Response โผ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ 3. FSM: SttWaiting โ Injecting โ
โ - Receive transcribed text โ
โ - Optional: TextPolisher (if enabled) โ
โ - Inject text via enigo/CGEvent โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
Injection Done โผ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ 4. FSM: Injecting โ Done โ Idle โ
โ - Update stats (chars, words, speaking time) โ
โ - Show success toast โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
STT Response โผ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ 1. Intent Parsing โ
โ - Send text to LLM for intent extraction โ
โ - Receive {"command": "...", "description": "..."} โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ 2. Safety Filter (Rule + LLM) โ
โ - Layer 1: Check allow/block lists โ
โ - Layer 2: LLM semantic judgment (if gray-zone) โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ 3. Command Execution โ
โ - Parse command with shell-words โ
โ - Execute with 30s timeout โ
โ - Capture stdout/stderr โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ 4. Result Notification โ
โ - Show toast with command output (truncated) โ
โ - Update stats โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
- ๅฎๅ จๆง: Rust ็ๅ ๅญๅฎๅ จไฟ่ฏ
- ๆง่ฝ: ๅ็ไบ่ฟๅถ๏ผๆ Electron ็ๅ ๅญๅผ้
- ไฝ็งฏ: ๅบ็จๅ ไฝ็งฏๅฐ๏ผ~10MB vs Electron ็ ~100MB๏ผ
- ็ณป็ป้ๆ: ่ฝปๆพ่ฎฟ้ฎ็ณป็ป API๏ผKeychainใๅ จๅฑ็ญ้ฎใ้ณ้ข๏ผ
่ฎพ่ฎกๅณ็ญ๏ผไธๅๆขๅคๅๅช่ดดๆฟๅ ๅฎน
ๅๅ ๏ผ
- ้ฟๅ ๆฐๆฎ็ซไบ๏ผ็จๆทๅฏ่ฝๅจๆณจๅ ฅๆ้ดๆไฝๅช่ดดๆฟ๏ผ
- ็ฎๅๅฎ็ฐ
- ๅช่ดดๆฟๅ้ๆฌ่บซๅทฒๆฏ่พน็ผๆ ๅต
// ๅฑ้ฉ๏ผ้่ฟ shell ๆง่ก
tokio::process::Command::new("sh")
.arg("-c")
.arg(user_input) // CVE-2024-24576 ๆณจๅ
ฅ้ฃ้ฉ
// ๅฎๅ
จ๏ผ็ดๆฅๆง่ก็จๅบ
let args = shell_words::split(user_input)?;
let program = &args[0];
let program_args = &args[1..];
tokio::process::Command::new(program)
.args(program_args)ๆ่กก๏ผไธๆฏๆ็ฎก้ใ้ๅฎๅใshell ๅ ็ฝฎๅฝไปคใ่ฆ็ 95% ็่ฏญ้ณๅฝไปคๅบๆฏใ
็ป่ฎกๆฐๆฎไฝฟ็จ JSON ๆไปถๅญๅจ๏ผ
- ้็ง: ็จๆทๅฏไปฅ็ดๆฅๆฅ็/ๅ ้ค่ชๅทฑ็ๆฐๆฎ
- ้ๆ: ๆ ้ๅทฅๅ ทๅณๅฏ่ฏปๅ
- ็ฎๅ: ่ถณๅคๆปก่ถณ็ป่ฎกๆฐๆฎ้ๆฑ
- ๆ ไพ่ต: ๆ ้ SQLite ๅบ
ๅจ Mode C ไธญ๏ผๆๆๅฎๅ จๆฃๆฅ้ฝๆฏ fail-closed๏ผ
| ๆฃๆฅ็น | ๅคฑ่ดฅ่กไธบ |
|---|---|
| ่งๅ่ฟๆปคๅจ | ๆ็ปๆง่ก |
| LLM ๅฎๅ จๆฃๆฅ | ๆ็ปๆง่ก |
| Intent ่งฃๆๅคฑ่ดฅ | ๆ็ปๆง่ก |
| ๅฝไปคๆง่ก่ถ ๆถ | ็ปๆญข่ฟ็จ๏ผ่ฟๅ้่ฏฏ |
ๅฎๅ จ > ไพฟๅฉๆงใ
ๆๆ้่ฏฏ้่ฟ SaysoError ๆไธพ่กจ็คบ๏ผ
pub enum SaysoError {
Audio(String),
Stt(String),
Llm(String),
Injection(String),
SafetyViolation(String),
Execution(String),
Config(String),
// ...
}้่ฏฏ้่ฟ Tauri event ๅ้ๅฐๅ็ซฏๆพ็คบ Toast ้็ฅใ
cd src-tauri
cargo test่ฆ็๏ผ
- FSM ็ถๆ่ฝฌๆข
- ๅฎๅ จ่ฟๆปคๅจ่งๅๅน้
- LLM ๅฎขๆท็ซฏ่ถ ๆถๅค็
- Intent ่งฃๆ JSON ้ช่ฏ
- ๅฝไปคๆง่กๅๆฐ่งฃๆ
- ๆๅญๆถฆ่ฒ fallback
- STT ๅๅบ่งฃๆ
- ้ณ้ข็ผ็ WAV ๆ ผๅผ
- ็ป่ฎกๆฐๆฎ่ฎก็ฎ
- ็ญ้ฎ โ ๅฝ้ณ โ STT โ ๆณจๅ ฅๅฎๆดๆต็จ
- ้่ฆๅจๆ้บฆๅ ้ฃ็็ฏๅขไธญๆๅจ้ช่ฏ
v1.1 ๅ้่ฟ Playwright ๆต่ฏๅ ณ้ฎ็จๆทๆต็จใ
- ้ณ้ขๆฐๆฎ: ๆๅคง 60s ร 16kHz ร 2bytes โ 1.9MB
- ็ป่ฎกๆฐๆฎ: ๅธธ้ฉปๅ ๅญ๏ผ<1KB
- HTTP ๅฎขๆท็ซฏ: ๅ
ฑไบซ
reqwest::Client๏ผ่ฟๆฅๆฑ ๅค็จ
- Rust ไบ่ฟๅถ: <100ms
- React ๅ็ซฏ: ็ฑ Tauri ๅ ่ฝฝ๏ผ<50ms
- ๆปๅฏๅจๆถ้ด: <1s๏ผๅ ๆฌ้ ็ฝฎๅ ่ฝฝใKeychain ่ฏปๅ๏ผ
็ญ้ฎๅค็ๅจไธ็จ็บฟ็จ่ฟ่ก๏ผๅปถ่ฟ <10msใ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ ๅฎๅ
จ่พน็ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ Untrusted โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ - User voice input โ โ
โ โ - STT API response โ โ
โ โ - LLM API response โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ โ
โ โผ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ SafetyFilter (Rule + LLM) โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ โ
โ Trusted โผ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ - Shell execution โ โ
โ โ - Keystroke injection โ โ
โ โ - File system access (stats.json) โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
ๆๆๅค้จ่พๅ ฅ๏ผ่ฏญ้ณ่ฝฌๅฝใLLM ่พๅบ๏ผ้ฝ็ป่ฟๅฎๅ จๆฃๆฅๅๆๆง่กๆๆๆไฝใ