
Tauri + Rust + UI Automation으로 Codex의 승인 피로를 줄이는 상주 데스크톱 앱 만들기
요약
Codex Desktop 사용자의 승인 피로를 줄이기 위해 Tauri, Rust, UI Automation을 활용한 데스크톱 보조 도구 개발 사례를 소개합니다. UI 텍스트 분석과 정책 엔진을 통해 승인 요청을 자동으로 판정하고 실행하며, 감사 로그를 남기는 구조를 제안합니다.
핵심 포인트
- Tauri와 Rust를 활용한 안전한 네이티브 데스크톱 앱 구조 설계
- UI Automation을 통한 Codex 승인 요청의 정밀한 검출 및 데이터 추출
- Policy Engine 기반의 자동 승인/거부/종료 판정 로직 구현
- 오작동 방지를 위한 Idle Guard 및 감사 로그(Audit Log) 시스템 구축
또한, GitHub 주소는 다음과 같습니다.
codex-approval-guard
는 Codex Desktop의 승인 요청을 모니터링하여, 조건에 맞는 것만 자동으로 승인(approve)·거부(deny)·종료(dismiss)하는 데스크톱 보조 도구입니다.
단순히 "Yes 버튼을 누르는 도구"가 아닙니다.
수행하는 작업은 크게 5가지입니다.
- UI Automation으로 Codex의 창(window)만 검출
- UI 텍스트로부터 command / cwd / target path / permission 추출
- policy engine을 통해
approve/deny/dismiss/prompt를 판정 - UI Automation / Win32 API로 승인·거부·종료 실행
- 실행 결과를 JSONL audit log(감사 로그)에 기록
이 부분이 중요합니다.
승인 피로를 줄이고 싶다.
하지만, Codex 이외의 확인 다이얼로그를 실수로 누르고 싶지는 않다.
이를 위해 이 프로젝트에서는 "버튼 문구"뿐만 아니라 프로세스, 윈도우, UIA tree, 승인 문맥, policy, 감사 로그를 조합하고 있습니다.
-
Tauri + React + Rust로 상주형 데스크톱 보조 도구를 만드는 구성
-
Windows UI Automation으로 Codex의 승인 요청을 검출하는 방법
-
승인 UI의 raw text로부터 command / cwd / permission을 추출하는 parser(파서) 설계
-
자동 승인·자동 거부·자동 종료를 구분하는 policy engine
-
사용자 조작 중에는 자동 조작을 하지 않기 위한 idle guard
-
audit log에 무엇을 남기고 무엇을 남기지 않을 것인가
-
Codex Desktop을 일상적으로 사용하는 분
-
Tauri로 상주형 데스크톱 도구를 만들고 싶은 분
-
Windows UI Automation을 Rust에서 다루고 싶은 분
-
AI coding agent의 승인 UX를 개선하고 싶은 분
-
"자동화하고 싶지만, 오작동은 피하고 싶다"고 생각하는 분
대략적인 도식은 다음과 같습니다.
포인트는 모니터링(monitoring), 해석(analysis), 판정(judgment), 실행(execution), 감사(audit)가 분리되어 있다는 점입니다.
UI는 React.
네이티브 조작은 Rust.
Windows의 실제 관측과 클릭은 UI Automation / Win32 API.
이러한 역할 분담을 통해, 화면은 일반적인 데스크톱 앱으로 만들면서 위험한 OS 조작은 Rust 측에 가둘 수 있습니다.
이 프로젝트의 구성은 다음과 같습니다.
| 레이어 | 기술 | 역할 |
|---|---|---|
| Desktop shell | Tauri v2 | 데스크톱 앱화, Rust bridge, 배포 |
| ... |
의존성을 보면, Windows 측은 uiautomation, windows-sys, windows를 사용하고 있습니다.
[target.'cfg(target_os = "windows")'.dependencies]
uiautomation = "0.24.4"
windows-sys = { version = "0.61.2", features = [
...
현 시점에서는 자동 승인의 본체는 Windows 구현이 중심입니다.
macOS adapter의 경계는 있지만, 자동 승인은 연결되지 않은 상태입니다.
상주 처리는 OS의 서비스로 동작하는 것이 아니라, Tauri 앱을 실행하는 동안 React 측의 interval(인터벌)을 통해 백그라운드에서 모니터링합니다.
실제 UI 측 코드는 여기 있습니다.
const BACKGROUND_POLL_MS = 600;
const AUTO_APPROVE_COOLDOWN_MS = 1500;
const runObservation = useCallback(
...
여기서 중요한 점은 3가지입니다.
- 600ms마다 관찰
pollingRef를 사용하여 다중 실행 방지- 직전의 자동 조작으로부터 1500ms 동안은 cooldown(쿨다운) 적용
상주형 도구에서는 동일한 승인 요청을 연타로 처리하지 않는 것이 매우 중요합니다.
React에서 호출되는 backend는 얇게 구성되어 있습니다.
import { invoke } from "@tauri-apps/api/core";
export async function callBackend<T>(
command: string,
...
본체는 Rust 측의 Tauri command입니다.
observe_approval_request
에서 감지하고, auto_approve_observed_request
에서 실제로 조작합니다.
#[tauri::command]
async fn observe_approval_request(
state: State<'_, AppState>,
...
관찰 (Observation) 단계에서는 audit log (감사 로그)에 기록하지 않습니다.
이유는 간단합니다.
Codex의 승인 창이 떠 있는 동안, 폴링 (polling)을 할 때마다 동일한 내용이 기록되기 때문입니다.
audit log는 실제로 승인, 거부, 폐쇄를 실행한 타이밍에만 추가합니다.
Windows adapter의 첫 번째 포인트는 UI Automation을 전용 스레드에서 실행하고 있다는 점입니다.
fn run_uia_task<T, F>(task: F) -> Result<T, String>
where
T: Send + 'static,
...
UI Automation은 외부 앱의 UI tree를 읽기 때문에, 병목 현상이 발생할 가능성이 있습니다.
따라서 전용 스레드 + 타임아웃 (timeout) 방식을 사용합니다.
다음으로, 최상위 윈도우 (top-level window)를 스캔합니다.
fn find_observed_approval(
automation: &UIAutomation,
top_level_walker: &UITreeWalker,
...
이 부분이 안전성의 큰 경계선입니다.
"Codex 같은 글자가 화면에 있다"는 것만으로는 처리하지 않습니다.
프로세스 이름이나 실행 경로를 통해 Codex / OpenAI / ChatGPT 계열의 프로세스인지 확인합니다.
fn looks_like_codex_process(&self) -> bool {
let process_image = self.process_image.as_deref().unwrap_or_default().to_lowercase();
let process_name = self.process_name.as_deref().unwrap_or_default().to_lowercase();
...
이를 통해 일반적인 UAC (사용자 계정 컨트롤), 브라우저의 권한 확인, 파일 탐색기 등을 실수로 승인하는 일을 방지합니다.
Codex의 UI는 승인 내용이 일반적인 OS 다이얼로그처럼 구조화되어 있지 않을 수도 있습니다.
그래서 UI Automation으로부터 가져온 raw text를 정규화하여 승인 문맥 (context)을 확인합니다.
pub fn parse_observed_approval_with_context(
title: &str,
raw_text: Vec<String>,
...
파서 (parser)는 다음과 같은 정보를 생성합니다.
| 필드 (field) | 내용 |
|---|---|
window_title | Codex의 대상 윈도우 타이틀 |
prompt_text | UIA로부터 가져온 승인 문맥의 raw text |
command | npm test 또는 cargo test 등 |
cwd | 작업 디렉토리 (working directory) |
target_paths | 변경 대상 파일 또는 Windows 경로 |
requested_permission | shell / file / network 등 |
단순한 클릭 자동화가 아니라, 승인 대상을 구조화된 데이터로 만든 뒤 정책 (policy)에 전달한다는 점이 핵심입니다.
정책 (policy) 코드는 상당히 읽기 쉽습니다.
pub fn evaluate(&self, request: &ApprovalRequest) -> ApprovalDecision {
if self.paused {
return ApprovalDecision {
...
현재의 구상은 다음과 같습니다.
-
일시 정지 (pause) 중에는 아무것도 하지 않음
-
git commit 다이얼로그는 자동으로 닫음
-
git add -
git add는 기본적으로 거부 -
git commit도 기본적으로 거부 - 그 외에는 자동 승인
이러한 설계는 실용적입니다.
Codex의 반복 작업에서는 npm test, cargo test, rg, node 등의 확인 명령(confirmation command) 승인 요청이 수없이 발생합니다.
반면, git add나 git commit은 사용자가 의도를 확인한 후 수행하고 싶은 작업입니다.
따라서 모든 것을 무분별하게 승인(approve)하는 것이 아니라, 일상적인 반복 승인 작업만을 가볍게 처리합니다.
자동 조작에서 가장 위험한 것은 사용자가 마우스나 키보드를 조작하고 있는 순간에 앱 측에서 클릭을 가로채는 것입니다.
이 프로젝트에서는 직전 사용자 입력으로부터 1500ms 미만일 경우 자동 조작을 건너뜁니다.
const USER_ACTIVITY_GUARD_MS: u32 = 1500;
#[tauri::command]
async fn auto_approve_observed_request(
...
이 부분도 중요합니다.
'자동화'라고 해서 언제든 마음대로 클릭해도 된다는 뜻은 아닙니다.
상주 데스크톱 앱(resident desktop app)에서는 사용자 입력과의 충돌을 피하는 설계가 필요합니다.
Codex의 승인 UI는 언어나 종류에 따라 문구가 달라집니다.
예를 들어:
1. 是
1. はい
Approve
1. Option A (Recommended)
送信
提交
Continue
続行
이 때문에 매처(matcher)는 다국어를 지원하도록 되어 있습니다.
fn is_recommended_option(name: &str) -> bool {
let trimmed = name.trim();
let lower = trimmed.to_lowercase();
...
여기서 알 수 있는 점은, 실용적인 데스크톱 자동화(desktop automation)에서는 '영어 UI만' 상정하면 금방 망가진다는 것입니다.
Codex를 일본어 UI, 중국어 UI, 영어 UI 중 무엇으로 사용하더라도 동작할 수 있도록 승인 키워드, 거부 키워드, 전송 버튼을 폭넓게 포착하고 있습니다.
이 프로젝트에서는 일반적인 승인(approve)뿐만 아니라 거절(dismiss) 기능도 있습니다.
특히 git commit 다이얼로그는 독립된 윈도우(window)로 나타나는 경우와 WebView 내 모달(modal)로 나타나는 경우가 있습니다.
독립된 HWND인 경우에는 WM_CLOSE를 사용하는 것이 빠릅니다.
fn try_close_window_via_wm_close(
window: &UIElement,
outcome: &mut ClickOutcome,
...
WebView 내 모달인 경우에는 Escape 키를 보내는 경로도 사용합니다.
그마저도 실패하면 UIA의 close / cancel 버튼으로 폴백(fallback)합니다.
이 부분이 실제 데스크톱 앱다운 부분입니다.
UI Automation(UIA)만으로 완결 지으려 하면 WebView 내의 모달이나 독립 윈도우의 차이 때문에 막히게 됩니다.
그래서 UIA, WM_CLOSE, VK_ESCAPE를 조합하여 사용합니다.
자동 조작을 수행하면 JSONL에 감사 로그(audit log)를 남깁니다.
pub fn append(
&self,
request: &ApprovalRequest,
...
저장하기 전에는 ApprovalRequest::redacted()를 거칩니다.
token, secret, password, credential과 같은 단어를 포함하는 값은 [REDACTED]로 치환됩니다.
상주 도구에서는 로그가 편리할수록, 로그에 무엇을 남기지 않을지도 중요해집니다.
이 프로젝트에서는 감시 루프(monitoring loop), UIA 관측, 파서(parser), 정책(policy), 클릭(click), 감사(audit)가 분리되어 있습니다.
이를 통해 어디에서 오탐(false positive)이 발생했는지, 어디에서 정책(policy)이 차단했는지, 어떤 클릭 경로가 성공했는지를 추적하기 쉽습니다.
"Approve라는 버튼이 있으니까 누른다"는 방식은 위험합니다.
이 구현에서는 먼저 Codex 프로세스인지 확인합니다.
그다음 UI 텍스트가 승인 문맥(approval context)인지 확인합니다.
그러고 나서 정책(policy)을 통과시킵니다.
자동화라고 하면 흔히 승인(approve)에만 눈길이 가기 마련입니다.
하지만 실제 운영에서는 거부(deny)하고 싶은 것이나, 자동으로 닫고(close) 싶은 것도 있습니다.
이 프로젝트에서는 다음과 같이 자동 조작의 종류를 나누고 있습니다:
- 일반적인 확인 명령은
approve git add는denygit commit은denygit commit대화 상자(dialog)는dismiss
데스크톱 자동화(desktop automation)는 사용자의 조작과 충돌할 수 있습니다.
GetLastInputInfo를 통해 유휴 시간(idle time)을 확인하여, 최근 입력으로부터 시간이 너무 짧을 경우에는 자동 조작을 중단합니다.
이는 사소해 보일 수 있지만, 사용자 경험(UX)을 크게 좌우하는 설계입니다.
자동 승인은 편리하지만, 나중에 어떤 일이 일어났는지 알 수 없다면 불안해질 수 있습니다.
따라서 요청(request), 결정(decision), 일치하는 규칙(matched rule), 방식(method)을 JSONL 형식으로 남깁니다.
반대로, 민감한(sensitive) 값은 마스킹(redaction) 처리합니다.
로컬에서 실행하는 방법은 다음과 같습니다.
npm install
npm run tauri:dev
검증은 다음 명령어를 사용합니다.
npm run build
npm run cargo:test
npm run tauri:build
Windows에서는 NSIS 설치 프로그램(installer)을 생성합니다.
macOS는 앱(app) / dmg 번들(bundle) 생성이 준비되어 있습니다.
codex-approval-guard는 Codex의 승인 피로를 줄이기 위한 작은 데스크톱 앱입니다.
하지만 내부를 살펴보면 상당히 실전적인 설계로 이루어져 있습니다.
- React가 상주 모니터링 루프(loop)를 가짐
- Tauri 커맨드(command)를 통해 Rust로 전달
- Windows UI Automation (UIA)으로 Codex 창(window)을 감지
- 파서(parser)가 가공되지 않은 UI 텍스트(raw UI text)를 구조화
- 정책(policy)이
approve/deny/dismiss/prompt를 결정 - UIA / Win32 API로 실제로 조작
- 감사 로그(audit log)에 결과 기록
단순한 클릭 자동화가 아니라, AI 코딩 에이전트(AI coding agent)의 승인 UX를 어떻게 안전하고 가볍게 만들 것인가에 대한 구현입니다.
Codex를 장시간 사용하는 사람일수록 승인 요청이 쌓이는 것이 큰 부담으로 다가옵니다.
그 반복되는 부분을 작게 자동화하면서도, 일시 정지(pause), 정책(policy), 감사(audit)를 통해 제어할 수 있도록 만든 점이 이 프로젝트의 가장 흥미로운 부분입니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Qiita AI의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기