MCP 서비스
43eb4b5 기준입니다.open-computer-use는 AI 에이전트가 데스크톱 앱을 관찰하고 조작할 수 있게 해주는 오픈소스 MCP 서비스입니다. 앱 목록을 읽고, 창의 상태를 가져오고, 버튼을 누르고, 글자를 입력합니다. 스스로 모델을 학습하거나 다음 행동을 결정하는 부분은 이 서비스의 역할이 아닙니다.
이 저장소에서 배울 핵심은 “클릭 명령” 자체보다, 지금 보고 있는 앱과 실제로 조작할 앱이 같은지 확인하는 방법입니다.
운전자는 어디로 갈지 판단합니다. 계기판은 현재 속도와 상태를 보여주고, 조향 장치는 그 판단을 실제 움직임으로 바꿉니다.
여기서는 AI 클라이언트가 운전자, get_app_state가 계기판, click·type_text가 조향 장치에 해당합니다.
계기판이 2분 전 화면을 보여주면 위험하듯, 자동화에서도 오래된 버튼 번호와 좌표를 믿으면 엉뚱한 곳을 조작할 수 있습니다. 그래서 상태를 읽는 코드와 상태를 폐기하는 코드가 입력 코드만큼 중요합니다.
macOS 구현에서는 packages/OpenComputerUseKit/Sources/OpenComputerUseKit/ComputerUseService.swift가 이 흐름의 중심입니다.
getAppState()가 상태를 만들고, click() 등이 동작을 수행하며, actionResult()가 설정에 따라 후속 상태를 반환합니다.
// native MCP 클라이언트가 보내는 요청의 개념 예시
{"jsonrpc":"2.0","id":1,"method":"tools/call",
"params":{"name":"get_app_state","arguments":{"app":"TextEdit"}}}
// 결과에서 확인한 현재 요소 번호를 같은 세션에서 사용한다.
// 12는 설명용 번호이며, 실제 화면에서 얻은 값으로 바꿔야 한다.
{"jsonrpc":"2.0","id":2,"method":"tools/call",
"params":{"name":"click",
"arguments":{"app":"TextEdit","element_index":"12"}}}
위 JSON은 구조를 읽기 쉽게 펼친 표현입니다. 이 서버의 stdio 통신에서는 요청마다 JSON을 한 줄로 직렬화하고 끝에 줄바꿈을 붙입니다.
여러 줄로 펼친 JSON을 그대로 보내면 readLine()이 메시지를 나누어 읽습니다.
같은 서비스를 두 가지 방식으로 호출할 수 있습니다. 일반 MCP 클라이언트는 네이티브 도구를 직접 호출합니다.
Codex 플러그인 경로에서는 지속적인 JavaScript 실행 환경이 그 도구들을 감싸고, 모델에 js와 js_reset을 노출합니다.
“도구가 2개”라는 설명과 “도구가 여러 개”라는 설명은 서로 다른 계층을 가리킬 수 있습니다.
모델 호출보다 프로토콜 파싱, 세션 상태, 트리 탐색, 좌표 변환, 권한 진단, 오류 복구를 공부하기 좋은 저장소입니다. 이 개념들은 테스트 자동화, 개발 도구, 원격 제어 프로그램에도 그대로 이어집니다.
GitHub Trending Swift Weekly #4에 선정된 프로젝트로, 2026-10-23 기준 스타 2,645개와 포크 245개를 기록했습니다. README는 Codex Computer Use에서 영감을 받아 접근성 기반의 비침해적 자동화를 공개 구현했다고 설명합니다. 아래는 기능과 소스에 근거해 관심을 끌 만한 요인을 정리한 분석입니다.
첫째, 그림만 보고 좌표를 맞추는 방식에 구조화된 UI 정보를 더합니다.
AccessibilitySnapshot.swift의 ElementRecord에는 역할, 제목, 값, 액션, 창 내부 위치가 담깁니다.
“오른쪽 위의 파란 점”보다 “제목이 저장인 버튼”을 대상으로 삼을 근거가 생기는 셈입니다.
둘째, 사용 중인 데스크톱을 덜 방해하려는 설계가 구체적입니다. 앱을 대상으로 한 입력, 실제 포인터와 구분되는 소프트웨어 커서, 창을 별도 가상 디스플레이로 옮기는 경로가 있습니다. 다만 앱마다 접근성 지원이 다르고, 일반 복구 경로에는 활성화가 허용되는 경우도 있습니다.
셋째, 모델과 실행 엔진의 결합이 느슨합니다. 네이티브 MCP 서버와 여러 클라이언트용 설치 진입점이 있어, 하나의 AI 앱 전용 기능을 넘어 실행 도구를 재사용하는 구조를 볼 수 있습니다. 이것이 모든 클라이언트·모든 앱에서 같은 결과를 보장한다는 뜻은 아닙니다.
| 비교 대상 | 이 프로젝트의 장점 | 함께 볼 제약 |
|---|---|---|
| 일반 좌표 매크로 | 요소 역할·제목·액션과 좌표를 함께 사용합니다. macOS query는 텍스트로 요소를 찾습니다. | 대상 앱이 접근성 정보를 충분히 제공해야 의미 기반 조작이 유효합니다. |
| 브라우저 전용 자동화 | 네이티브 데스크톱 앱까지 다루는 운영체제 계층이 학습 대상입니다. | 웹페이지 내부만 다룰 때와는 창·권한·픽셀 배율 문제의 범위가 다릅니다. |
| Codex 번들 도구 | 소스를 읽고 수정할 수 있는 MIT 프로젝트이며, 직접 연결 가능한 MCP 서버를 제공합니다. | 공식 구현의 완전한 복제는 아닙니다. 저장소 문서는 서명·호스트 통합·세션 승인 경험의 차이를 명시합니다. |
| 앱의 전용 API | 전용 API로 노출되지 않은 UI 기능까지 접근할 수 있는 통로입니다. | UI 변화의 영향을 받습니다. 이 프로젝트의 서버 안내도 필요한 기능이 전용 도구에 있으면 그 도구를 우선하도록 권합니다. |
Codex와의 비교 범위는 README, docs/ARCHITECTURE.md, scripts/computer-use-cli/README.md의 설명 기준입니다.
이 자료는 경쟁 제품의 성능 순위나 성공률을 측정한 벤치마크가 아닙니다.
공개 구현의 교육적 가치는 “AI가 앱을 쓴다”는 결과를 관찰·검증·실행이라는 읽을 수 있는 코드로 나눠 보여준다는 데 있습니다.
README 후반은 네이티브 도구를 “9개 핵심 도구와 macOS의 query”로 설명하지만,
ToolDefinitions.swift의 실제 목록에는 select_text도 있어 macOS는 총 11개입니다.
Windows·Linux의 main.go는 select_text와 query를 제외한 9개를 노출합니다.
도구 이름이 같더라도 세부 옵션은 다릅니다. 예를 들어 Linux는 macOS의 sky_click 같은 전달 경로를 그대로 구현하지 않습니다.
실제 연결 시에는 해당 서버의 tools/list와 플랫폼별 오류를 기준으로 판단해야 합니다.
최신 릴리스 문서 docs/releases/github/v1.1.2.md에는 움직이는 창에 대한 커서 정렬,
Linux 동작 후 상태 읽기 설정 수정, 저장소 이름 변경에 따른 링크 정리가 기록되어 있습니다.
시각적 데모 뒤에서 상태 수명과 플랫폼 차이를 계속 다듬는 프로젝트라는 점도 읽을 거리입니다.
여기서 백엔드는 HTTP 서버와 데이터베이스가 중심인 웹 백엔드가 아닙니다.
macOS의 핵심은 OpenComputerUseKit 라이브러리와 OpenComputerUse 실행 파일입니다.
기본 MCP 전송은 표준 입력·출력이며, 앱 호스트 경로에는 로컬 소켓 통신도 사용됩니다.
| 계층 | 기술·버전 | 근거와 담당 역할 |
|---|---|---|
| macOS 언어 | Swift tools 6.2 | Package.swift 첫 줄. 설치된 컴파일러 버전의 실측값이 아니라 패키지 도구 버전 선언입니다. |
| 플랫폼 하한 | macOS 14 | Package.swift의 .macOS(.v14). 앱 빌드 스크립트도 macOS 14.0 타깃을 사용합니다. |
| 화면·입력 | AppKit, ApplicationServices, CoreGraphics, ScreenCaptureKit | 운영체제 SDK입니다. 접근성 요소, 창, 이미지, 입력 이벤트를 다룹니다. 별도 패키지 버전은 선언하지 않습니다. |
| 이미지 의존성 | libwebp-Xcode 1.6.0 | Package.swift가 exact로 고정하고 Package.resolved도 같은 버전을 기록합니다. |
| Windows | Go 1.22 선언 + PowerShell | apps/OpenComputerUseWindows/go.mod와 runtime.ps1. UI Automation 및 Win32 호출을 연결합니다. |
| Linux | Go 1.22 선언 + Python 3 | apps/OpenComputerUseLinux/go.mod와 runtime.py. PyGObject로 AT-SPI 2.0, 선택적으로 GDK 3.0 인터페이스를 요청합니다. |
| MCP 프로토콜 | 2025-03-26 | macOS MCPServer.swift의 초기화 응답에 적힌 버전입니다. 최신 규격 전체를 구현했다는 뜻은 아닙니다. |
Package.resolved의 libwebp 고정 리비전은 2b5256c29ff4e20f2a0d5ee863b62b1a22144434입니다.
WebPEncoding.swift의 losslessWebPData(for:)는 C 함수 WebPEncodeLosslessRGBA를 호출하고,
defer로 WebPFree를 보장합니다. 의존성 목록과 실제 사용처를 연결해 읽기 좋은 사례입니다.
루트 package.json에는 React·Vue 같은 웹 UI 의존성이 없습니다. 이 저장소의 사용자 인터페이스는 네이티브 앱과 CLI에 가깝습니다.
apps/OpenComputerUse/Sources/OpenComputerUse/PermissionOnboardingApp.swift는 AppKit 기반 권한 안내 창을 만듭니다.
PermissionOnboardingApp.launch()는 권한이 이미 충족되면 창을 열지 않습니다.SoftwareCursorOverlay.swift는 자동화의 커서를 표시합니다. 표시용 커서와 실제 시스템 포인터는 구분해서 읽어야 합니다.scripts/node-repl/open-computer-use-repl.mjs의 createCuaApi()가 cua.getApp(), app.click() 등을 제공합니다.open-computer-use와 짧은 별칭 ocu를 제공합니다. js는 단발 평가, repl은 상태를 유지하는 터미널 세션입니다.go 1.22는 모듈의 언어·도구 호환성 선언이고, CI가 설치하는 Go 버전과는 별개입니다.
exact: "1.6.0"은 의존성 버전을 고정하는 표현입니다. “Swift 프로젝트니까 Swift만 설치하면 된다”는 식으로 이 층들을 합치면 배포 구조를 놓칩니다.npm으로 설치된다는 사실과 실행 엔진이 JavaScript라는 사실은 같지 않습니다.
루트 package.json은 npm 게시를 막은 저장소 관리용 패키지이며, scripts/npm/build-packages.mjs가 배포용 패키지 메타데이터를 생성합니다.
| 항목 | 실제 설정 | 읽을 때의 의미 |
|---|---|---|
| npm 런처 | 생성 메타데이터의 Node.js >=18 | 네이티브 바이너리를 선택하고 JS 실행 환경을 연결하는 데 필요합니다. |
| 배포 대상 | macOS·Linux·Windows, 각각 arm64·x64 | 런처는 process.platform과 process.arch를 기준으로 동봉 런타임을 고릅니다. |
| 릴리스 작업 | macos-26, Node.js 24, Go 1.22.x | .github/workflows/release.yml의 패키지 작업. 서명·조건부 공증·아티팩트 업로드가 이어집니다. |
| Windows 검증 | windows-2025, Node.js 24, Go 1.26.1 | .github/workflows/windows-observation.yml. 테스트·빌드와 WPF 기반 실제 입력·관찰 검증 절차를 정의합니다. |
| 프로빙 CLI | Go 1.26.0, MCP Go SDK v1.4.1 | scripts/computer-use-cli/go.mod. Windows·Linux 제품 런타임과 별개의 조사용 CLI입니다. |
| 검증 스크립트 | scripts/ci.sh | 문서·저장소 위생·액션 SHA 고정, 셸·JS 구문, Node·Python 및 조건부 Go 테스트를 연결합니다. |
Go SDK v1.4.1을 macOS MCP 서버의 의존성으로 오해하면 안 됩니다.
macOS의 StdioMCPServer는 Foundation으로 JSON을 직접 처리하고, 해당 SDK는 별도 프로빙 CLI에 쓰입니다.
Windows·Linux의 제품 go.mod에는 외부 require가 없습니다.
제품 빌드의 중심은 Swift Package Manager, Go, npm 패키징과 운영체제별 스크립트입니다. 별도 웹 프론트 빌드나 컨테이너를 필수 실행 경로로 가정할 근거는 없습니다. CI 파일의 존재는 설정된 절차의 증거이며, 특정 실행이 성공했다는 증거는 아닙니다.
아래 그림에서 AI의 판단은 클라이언트 쪽에 있습니다. 로컬 실행 엔진은 관찰 결과를 돌려주고, 지정된 액션을 처리합니다. JavaScript 어댑터는 선택 가능한 상위 계층이며, 일반 MCP 클라이언트는 네이티브 서버로 바로 연결할 수 있습니다.
OpenComputerUseMain.run()은 명령을 파싱한 뒤 프록시 사용 여부를 먼저 판단합니다.
직접 mcp를 처리하는 분기에서는 ComputerUseService와 StdioMCPServer를 만들고,
커서 UI가 활성화되어 있으면 MCPAppRuntime.run(server:)으로 앱 이벤트 루프를 함께 유지합니다.
이것은 네트워크로 연결된 마이크로서비스 그림이 아닙니다. 라이브러리 내부 호출, 자식 프로세스의 파이프, 로컬 앱 호스트 통신을 한눈에 표시한 것입니다. 화살표마다 같은 종류의 통신이 일어난다고 생각하면 안 됩니다.
packages/OpenComputerUseKit/Sources/OpenComputerUseKit/MCPServer.swift의 run()은 줄 단위 입력을 읽습니다.
handle(line:)이 initialize, tools/list, tools/call 등을 구분하고,
도구 호출을 ComputerUseToolDispatcher.callTool(name:arguments:)에 넘깁니다.
// MCPServer.swift에서 도구 호출 분기만 발췌
case "tools/call":
let name = params["name"] as? String ?? ""
let arguments = params["arguments"] as? [String: Any] ?? [:]
let result = try dispatcher.callTool(name: name, arguments: arguments)
return try encodeJSONRPCResult(
id: id,
result: result.asDictionary
)
분배기는 필수 문자열, 요소 번호, 옵션을 해석합니다. 예를 들어 strictObservationBool()은
include_ax를 문자열이나 숫자에서 느슨하게 변환하지 않고 진짜 JSON 불리언인지 확인합니다.
서비스는 이 값들을 받아 앱 관찰과 조작을 수행합니다.
식당에서 전화 주문을 받는 직원이 MCP 서버, 주문표를 담당 부서로 나누는 직원이 분배기, 실제 요리사가 서비스입니다. 전화기 교체가 요리법 변경으로 이어지지 않도록 책임을 나눈 것입니다.
실패도 두 층으로 구분해 읽어야 합니다. 알 수 없는 메서드는 JSON-RPC 오류 -32601로 응답하지만,
도구 실행 실패는 content와 isError가 담긴 결과로 돌아올 수 있습니다.
응답이 JSON으로 정상 도착했다는 사실만으로 클릭 성공을 판단해서는 안 됩니다.
ComputerUseService.swift의 ClickMethod 열거형은
auto, accessibility, app_post, sky_click, global을 구분합니다.
인터페이스를 여러 클래스로 쪼갠 형태는 아니지만, 호출자가 선택한 전략에 따라 실행 경로가 달라지는 설계입니다.
요소를 지정한 auto 경로는 접근성 액션을 먼저 시도합니다.
처리되지 않으면 다른 입력 경로를 사용합니다. validateClickMethod()는
accessibility 방식에 요소 번호가 필요함을 검사하고, global에는 환경변수로 명시적 허용을 요구합니다.
여기서 중요한 조건은 OPEN_COMPUTER_USE_ALLOW_GLOBAL_POINTER_FALLBACKS=1입니다.
기본 비접근성 대체 경로는 InputSimulation.clickTargeted(..., pid:)를 시도하지만,
전역 입력을 허용하면 prepareAppForGlobalPointerInput()과 clickGlobally() 경로를 사용할 수 있습니다.
자동화의 대체 경로는 성공률을 높이지만, 사용자에게 미치는 영향까지 바꿀 수 있으므로 조건과 부작용을 함께 읽어야 합니다.
또한 clickActionSnapshotRecoveryPolicy()는 sky_click에 읽기 전용 정책을 주고,
다른 방식에는 활성화를 허용하는 정책을 줍니다. “실제 포인터를 기본으로 움직이지 않는다”와 “어떤 경우에도 포커스를 바꾸지 않는다”는 다른 약속입니다.
packages/OpenComputerUseKit/Sources/OpenComputerUseKit/TargetedQuery.swift의
TargetedElementRegistry<Value>는 query 결과를 현재 네이티브 세션의 번호로 저장합니다.
첫 발급 번호는 1,000,001이고, 최대 5,000개를 보관하며, 기본 수명은 120초입니다.
// TargetedQuery.swift의 기본 설정을 발췌
static var indexBase: Int { 1_000_000 }
init(capacity: Int = 5000,
lifetime: TimeInterval = 120,
now: @escaping () -> Date = Date.init) {
self.capacity = max(1, capacity)
self.lifetime = lifetime
self.now = now
}
이 번호는 영구 ID가 아닙니다. resolve()는 만료 여부와 앱 신원을 검사합니다.
신원에는 PID뿐 아니라 bundle identifier와 실행 시각도 들어갑니다. 프로세스가 재시작되어 같은 PID를 받더라도 이전 요소를 그대로 신뢰하지 않도록 하는 장치입니다.
ComputerUseService.snapshotForAction()은 조회 번호를 해석한 뒤,
SnapshotBuilder.currentGeometry(of:in:criteria:)로 창 소속·역할·식별자·검색 조건·현재 프레임을 다시 확인합니다.
현재 요소를 다시 검증하는 단계에서 실패하면 해당 조회 번호를 삭제하고 오류를 반환합니다. 이 경로는 오래된 좌표로 조작을 계속하지 않습니다.
탐색 자체도 무한정 이어지지 않습니다. boundedTargetedWalk()는 큐와 방문 집합을 쓰는 너비 우선 탐색이며,
노드 수·결과 수·시간 제한·접근성 오류로 중단합니다. targetedSearch()는 2초의 협력적 마감 시간을 둡니다.
진행 중인 운영체제 호출을 정확히 2초에 강제 중단하는 보장은 아닙니다.
| 결과 필드 | 질문 | 독자가 해석할 내용 |
|---|---|---|
matches | 무엇을 찾았나? | 현재까지 발견한 일치 요소입니다. |
truncated | 끝까지 살펴봤나? | true이면 검색이 불완전합니다. |
stop_reason | 왜 멈췄나? | limit, max_nodes, timeout, ax_error 등을 구별합니다. |
visited_nodes | 얼마나 조사했나? | 빈 결과와 작은 탐색 예산을 구분할 단서입니다. |
matches: []인데 truncated: true라면 “그 버튼이 없다”고 결론 내릴 수 없습니다.
찾지 못한 이유를 결과에 실어 보내는 설계는 검색 API와 데이터 수집기에도 유용합니다.
packages/OpenComputerUseKit/Sources/OpenComputerUseKit/LockedUseStateMachine.swift는
macOS 잠금 상태 작업 실험의 정책을 값 타입 struct로 표현합니다.
Phase에는 idle, preparing, authorizing, unlocking,
active, relocking, awaitingManualUnlock가 있습니다.
begin(), guardsPrepared(), consumePermit(), tick()은
전제 조건을 검사하고 상태를 바꾸거나 실행할 효과를 반환합니다.
Effect의 prepareGuards·requestUnlock·requestRelock 같은 값은 실제 시스템 동작과 정책 판단을 구분합니다.
교통 신호 제어기가 “빨간불로 바꿔야 한다”는 판단을 내리는 것과, 전선을 통해 실제 신호등을 켜는 일을 나누는 방식입니다. 판단 부분을 분리하면 실제 도로를 통제하지 않고도 잘못된 전환과 시간 초과를 검토할 수 있습니다.
이 상태 머신의 시간 기준은 호출자가 전달하는 단조 증가 시간입니다. 허가 수명 5초, heartbeat 제한 3초,
유휴 제한 30초, 최대 임대 300초가 선언되어 있습니다.
이는 Locked Use 실험의 정책값이며 일반 MCP 호출의 공통 타임아웃이 아닙니다.
docs/ARCHITECTURE.md는 전체 장애 행렬의 실증 검증이 아직 필요하다고 명시합니다.
screenshotPixelScale()은 반환된 이미지의 픽셀 크기를 창 크기로 나누고,
screenshotPixelToWindowPoint()는 클릭 픽셀을 그 비율로 나눕니다. 이미지 축소 설정이 있으므로 배율을 무조건 2로 고정하면 안 됩니다.
# ComputerUseService.swift의 좌표 계산을 숫자로 풀어쓴 예시
창 크기 = 1000 × 600 포인트
반환 이미지 = 1280 × 768 픽셀
x 배율, y 배율 = 1.28, 1.28
이미지 클릭점 = (640, 384)
창 내부 좌표 = (640 / 1.28, 384 / 1.28) = (500, 300)
# 이후 창의 전역 위치를 반영한다.
# AppKit 표시 좌표는 화면별 원점과 y축 방향도 구분한다.
screenStatePointToAppKitGlobalPoint()는 화면별 프레임을 사용해 커서 표시 좌표를 바꿉니다.
“좌표 두 개를 넘긴다”는 API 뒤에도 이미지 크기, 창 위치, 화면 배치라는 서로 다른 기준이 있다는 점이 핵심입니다.
일반 네이티브 경로는 기본적으로 액션 후 상태를 다시 읽습니다.
반면 REPL 어댑터는 OPEN_COMPUTER_USE_ACTION_READ_BACK=0을 사용해 액션의 자동 관찰을 줄이고,
코드가 필요한 지점에서 getAXState()를 호출하도록 합니다. 같은 클릭이라도 호출 계층에 따라 결과 사용법이 달라집니다.
WorkerJavaScriptSession.run()은 평가를 큐에 직렬로 넣습니다. 제한 시간을 넘으면 워커를 종료하고 새 워커를 시작하며 네이티브 세션도 끝냅니다.
이는 무한 반복문이 평가 워커를 붙잡는 문제에 대응하는 구조이지, 이미 대상 앱에 전달한 액션을 되돌리는 트랜잭션은 아닙니다.
StdioMCPServer.endSession()은 조회 인덱스를 비우고 커서·백그라운드 창 상태를 정리합니다.
각 ocu call 프로세스 사이에 요소 번호가 공유된다고 가정하면 안 됩니다. 여러 단계를 연결하려면 같은 네이티브 세션이 필요합니다.
apps/는 “어떻게 시작하고 어떤 프로세스로 실행하는가”, packages/는 “실제로 무엇을 하는가”에 가깝습니다.
experiments/는 이름만 보고 전부 제품 경로와 분리되어 있다고 단정하면 안 됩니다.
실제로 Package.swift는 그 안의 Locked Use 실행 파일도 타깃으로 선언합니다.
Package.swift: 제품·라이브러리·실험·테스트 타깃의 연결부터 그립니다.apps/OpenComputerUse/Sources/OpenComputerUse/OpenComputerUseMain.swift: 명령과 실행 경로를 찾습니다.packages/OpenComputerUseKit/Sources/OpenComputerUseKit/ToolDefinitions.swift: 외부에 약속한 입력 스키마를 읽습니다.MCPServer.swift → ComputerUseToolDispatcher.swift → ComputerUseService.swift: 도구 하나만 추적합니다.TargetedQuery.swift와 대응 테스트: 시스템 API와 독립적으로 이해할 수 있는 작은 알고리즘을 읽습니다.AccessibilitySnapshot.swift는 2,616줄, ComputerUseService.swift는 2,346줄입니다.
처음부터 끝까지 읽기보다 query나 click 한 갈래를 검색해 따라가면 책임의 경계를 파악하기 쉽습니다.
커밋 43eb4b5의 apps/, packages/, experiments/, scripts/에서
아래 확장자의 파일을 세었습니다. 빈 줄·주석·테스트를 포함한 줄바꿈 문자 수이며, 문서·외부 참고 번들·이미지·빌드 산출물은 집계 범위 밖입니다.
| 언어 | 파일 수 | 대략적인 줄 수 |
|---|---|---|
| Swift | 151 | 35,921 |
| Go | 11 | 6,592 |
| Python | 18 | 5,569 |
| JavaScript | 19 | 4,726 |
| Shell | 35 | 3,185 |
| PowerShell | 8 | 2,163 |
| Objective-C | 2 | 379 |
| C | 5 | 362 |
| 공용 헤더 | 2 | 53 |
| 합계 | 251 | 58,950 |
이 수치는 GitHub의 언어 비율과 다른 기준입니다. 예를 들어 Go 프로빙 CLI와 테스트용 Python 스크립트도 포함됩니다. “주 언어 Swift”라는 분류 아래 운영체제 연결과 배포를 위한 여러 언어가 있다는 점을 보여주는 지도입니다.
SnapshotObservationOptions는 불리언 두 개를 가진 struct이고,
ComputerUseService는 앱별 스냅샷을 유지하는 final class입니다.
작은 설정값과 공유 상태를 가진 서비스가 서로 다른 타입으로 표현되는 이유를 비교해 보세요.
// SnapshotObservationOptions.swift의 검증 메서드
public func validate() throws {
guard includeAX || includeScreenshot else {
throw ComputerUseError.invalidArguments(
"At least one of include_ax or include_screenshot must be true"
)
}
}
오류 문구는 “접근성 정보와 스크린샷 중 적어도 하나를 포함해야 한다”는 뜻입니다. 두 옵션을 모두 끄는 조합을 입력 경계에서 거부합니다. 실습 아이디어: 가능한 네 조합을 적고 어떤 조합이 왜 실패해야 하는지 설명해 보세요.
| 관찰 조합 | 수집하는 것 | 다음 액션의 조건 |
|---|---|---|
| 둘 다 켬 | 접근성 정보와 스크린샷을 요청합니다. | 실제로 반환된 요소와 이미지의 사용 가능 여부를 확인합니다. |
| AX만 켬 | 캡처와 이미지 인코딩을 생략합니다. | 이 관찰을 바탕으로 스크린샷 좌표 액션을 수행할 수는 없습니다. |
| 이미지만 켬 | 접근성 트리·포커스·선택 정보 수집을 생략합니다. | 이전 요소 번호는 무효화됩니다. 요소 액션 전 AX를 다시 읽습니다. |
| 둘 다 끔 | 요청을 거부합니다. | 적어도 하나를 켠 유효한 관찰 요청이 필요합니다. |
이는 응답에서 필드만 숨기는 옵션이 아닙니다. rememberSnapshot()은 AX를 생략한 앱의 이전 스냅샷 별칭과 조회 번호를 정리합니다.
비용을 줄이는 설정이 후속 액션의 유효성에도 영향을 준다는 점을 함께 배울 수 있습니다.
MCPAppRuntime.swift는 NSApplication.run()으로 UI 이벤트 루프를 유지하면서,
processStandardIO()를 별도 스레드에서 처리합니다. 입력 스트림을 기다리는 일이 UI 이벤트 처리를 막지 않게 하는 구조입니다.
PermissionOnboardingApp와 주 진입점에는 @MainActor가 보이지만,
핵심 전체가 Swift actor만으로 구성된 것은 아닙니다. 기존 Thread·DispatchQueue·잠금도 함께 쓰입니다.
실습 아이디어: 표준 입력 읽기, 앱 종료, 커서 표시 각각이 어느 실행 경계에 놓이는지 표시해 보세요.
async라는 단어가 보인다는 이유만으로 모든 작업이 별도 스레드에서 실행된다고 해석하면 안 됩니다.ToolDefinitions.swift는 도구의 이름·설명·스키마를 만들고,
ToolResult.swift는 텍스트와 이미지 블록을 결과로 묶습니다. 이미지 바이트는 base64 문자열로 전달됩니다.
실습 아이디어: 동일한 요청에 대해 ① 알 수 없는 메서드, ② 알 수 없는 도구, ③ 유효한 도구지만 잘못된 앱을 지정한 경우를 각각 어떤 경로가 처리할지 그려보세요. 통신 성공, 도구 성공, UI에서 의도한 변화는 구별해서 설명해야 합니다.
버전 선언도 확인할 가치가 있습니다. JS JsonLinePeer.initialize()는 2025-06-18을 요청하고,
macOS 서버는 2025-03-26을 응답합니다. 요청 문자열이 같지 않다는 사실과 실제 호환성 검증은 별개의 문제입니다.
boundedTargetedWalk()는 노드 조사와 자식 조회를 클로저로 받습니다.
진짜 창 대신 정수로 만든 가짜 그래프를 넣을 수 있습니다. 화면 자동화 코드 안에서도 순수한 탐색 로직을 뽑아낼 수 있다는 뜻입니다.
packages/OpenComputerUseKit/Tests/OpenComputerUseKitTests/TargetedQuerySafetyTests.swift에는
순환 그래프 중복 제거, 넓은 트리의 예산 제한, 만료, 다른 앱의 번호 거부를 검증하는 XCTest 코드가 있습니다.
실습 아이디어: testTraversalDeduplicatesCyclesAndStopsAtResultLimit()을 읽고 큐·방문 집합·결과 배열을 손으로 갱신해 보세요.
ImageCaptureConfig.swift의 기본값은 PNG, JPEG 품질 0.8, ScreenCaptureKit 캡처 제한 5초,
최대 긴 변 1,280픽셀, 초과 시 축소, 총 픽셀 수 64 미만 이미지 생략입니다.
64픽셀은 한 변의 길이가 아니라 면적입니다. 테스트는 7×9의 생략과 8×8의 허용을 구분합니다.
설정 우선순위는 환경변수 → JSON 파일 → 기본값입니다. 기본 파일 위치는 ~/.config/ocu/config.json이며
XDG_CONFIG_HOME이나 OPEN_COMPUTER_USE_CONFIG_FILE로 바뀔 수 있습니다.
실습 아이디어: 파일의 긴 변 제한이 640이고 유효한 환경변수가 800일 때의 결과를 예측하고 ImageCaptureConfigTests.swift와 대조해 보세요.
캡처 경로도 README의 짧은 설명보다 복잡합니다. WindowCapture.captureImage()는 허용되는 경우
SkyLight 하드웨어 캡처를 먼저 시도하고, 실패하거나 해당 경로를 사용할 수 없으면 ScreenCaptureKit으로 내려갑니다.
SDK 이름 하나만으로 전체 경로를 설명하지 않는 연습이 됩니다.
두 Go 런타임은 //go:embed로 각 플랫폼의 스크립트를 포함합니다.
Linux는 gi.repository.Atspi, Windows는 UIAutomationClient·UIAutomationTypes와 Win32를 사용합니다.
실행 파일에 스크립트가 들어 있어도 Python·PowerShell 등 실행 환경이 불필요해지는 것은 아닙니다.
실습 아이디어: 세 플랫폼의 get_app_state 옵션과 click_method를 표로 비교하세요.
Linux의 app_post·sky_click 거부처럼 이름만 공유하는 옵션을 찾아야 합니다.
교차 플랫폼 설계의 목표는 호출 모양의 공통화이지 운영체제 기능의 완전한 동일화가 아닙니다.
WorkerJavaScriptSession은 별도 워커를 사용하고, JsonLinePeer는 요청 ID와 대기 중인 응답을 관리합니다.
AsyncLocalStorage는 지연된 콜백의 출력이 어느 평가에 속하는지 추적하는 데 쓰입니다.
// 준비된 macOS REPL 세션에서 수행하는 조회 예시
// TextEdit가 이미 실행 중이어야 한다. 아래 코드는 클릭하지 않는다.
var editor = await cua.getApp("TextEdit", { initialState: false });
var found = await editor.query({ role: "AXTextArea", limit: 10 });
nodeRepl.write(found);
// 실제 역할 이름은 앱이 노출하는 AX 정보에 따라 확인한다.
// found.matches뿐 아니라 truncated와 stop_reason도 함께 읽는다.
initialState: false는 바인딩 시 전체 스냅샷을 건너뛰게 합니다.
이어지는 query()는 화면 전체를 전송하는 대신 조회 결과를 반환합니다. 이 API는 macOS 전용이며, 새 세션에서는 기존 변수를 다시 바인딩해야 합니다.
세션을 재시작하면 코드의 변수는 지울 수 있지만, 이미 앱에 전달한 클릭이나 입력까지 취소되지는 않습니다. 실습 아이디어: 시간 초과 직전에 클릭이 전달된 경우를 가정하고, 재시도 전 무엇을 관찰해야 중복 실행을 피할지 적어보세요.
release.yml은 GitHub Actions를 전체 커밋 SHA로 고정하고, 서명 인증서가 없으면 ad-hoc 서명으로 대체하는 분기를 둡니다.
이때 권한 식별자가 설치별로 달라질 수 있다는 안내도 있습니다. 공증 역시 자격 정보와 서명 조건에 따라 수행됩니다.
실습 아이디어: “태그 생성 → 릴리스 문서 검증 → 패키징 → 조건부 공증 → 아티팩트 업로드”를 그린 뒤, 인증서나 공증 자격 정보가 없을 때 어느 단계가 달라지는지 표시하세요. 워크플로 정의와 배포물의 실제 보증을 구분하는 연습입니다.
소스를 읽고 JSON·탐색 알고리즘을 공부하는 데는 대상 운영체제의 GUI 권한이 필요하지 않습니다. 실제 실행을 준비할 때는 플랫폼마다 다음 조건을 확인해야 합니다. 아래 조건은 저장소 선언과 구현에 근거합니다.
| 환경 | 확인할 조건 | 소스 근거 |
|---|---|---|
| macOS | macOS 14 이상. 배포 대상은 Apple Silicon arm64와 Intel x64입니다. | Package.swift, build-open-computer-use-app.sh, npm 플랫폼 목록. |
| macOS 권한 | 일반 관찰·조작에는 Accessibility와 Screen Recording을 확인합니다. | Permissions.swift의 PermissionDiagnostics.current(). |
| 소스 빌드 | Swift tools 6.2 선언을 지원하는 도구 체인과 macOS SDK가 필요합니다. | 패키지 선언. 실제 설치용 바이너리 사용과 소스 빌드는 준비물이 다릅니다. |
| npm·JS 경로 | npm 런처는 Node.js 18 이상. REPL 어댑터와 해당 OS용 네이티브 런타임도 필요합니다. | 생성 패키지의 engines, README의 capabilities 설명. |
| Windows | Windows의 powershell.exe, UI Automation·Win32를 이용할 수 있는 데스크톱 환경이 필요합니다. | main.go의 런타임 호출, runtime.ps1의 어셈블리 로드. |
| Linux | Python 3·PyGObject·AT-SPI와 활성 데스크톱 세션이 필요합니다. GDK는 선택적으로 로드합니다. | runtime.py의 import와 require_desktop_session(). |
| Linux 세션 | XDG_RUNTIME_DIR와 DBUS_SESSION_BUS_ADDRESS를 검사합니다. | Go 런타임의 세션 환경 자동 탐색 후에도 변수가 없으면 활성 데스크톱 세션이 필요하다는 오류를 반환합니다. |
| CPU·RAM·GPU | 저장소의 실행 안내와 빌드 선언에는 정량적인 최소 CPU·RAM·VRAM 사양이 없습니다. | 이 프로젝트 자체는 모델 가중치를 로드하는 추론 서버가 아닙니다. 연결할 AI 클라이언트의 요구사항은 별도입니다. |
이 프로젝트의 실행 조건은 GPU 용량보다 운영체제의 접근성 지원, 로그인 세션, 권한, 대상 앱의 상태에 더 직접적으로 연결됩니다.
macOS doctor --json은 platform, accessibilityTrusted,
screenCaptureGranted, allGranted, missingPermissions를 반환합니다.
이 경로는 권한 안내 창을 열지 않습니다. 권한이 없더라도 종료 코드가 0이므로 JSON의 필드를 확인해야 합니다.
PermissionDiagnostics.current()는 시스템 API와 저장된 TCC 진단 정보를 함께 사용합니다.
allGranted: true는 특정 앱의 특정 버튼이 성공적으로 눌린다는 증명은 아닙니다.
권한 변경 뒤 프로세스 재시작이 필요한 경우가 있다는 점도 README에 적혀 있습니다.
docs/locked-use.en.md는 서명된 앱과 Locked Use 구성요소를 요구하고,
Guardian에 Accessibility·Input Monitoring 권한을 설명합니다. 일반 권한 안내를 마쳤다고 Locked Use가 자동으로 켜지는 것은 아닙니다.
Linux의 AT-SPI 입력을 모든 Wayland 환경에서 통하는 백그라운드 조작으로 이해해서도 안 됩니다.
Linux 서버 안내는 의미 기반 액션을 우선하며, 좌표·키 합성은 환경에 따라 성공을 보장하지 않는 대체 경로로 설명합니다.
Windows·Linux는 docs/ARCHITECTURE.md에서 실험적 런타임으로 분류합니다.
아래 다섯 과제는 소스 읽기와 독립적인 작은 모형으로 수행할 수 있습니다. 실제 앱 조작과 시스템 잠금 설정은 요구하지 않습니다. 각 과제는 결과물을 남겨 이해 여부를 확인하도록 구성했습니다.
목표: 외부 스키마와 내부 함수가 만나는 위치를 찾습니다.
ToolDefinitions.swift에서 get_app_state와 click의 필수 인자를 적습니다.ComputerUseToolDispatcher.callTool()에서 생략된 옵션의 기본값을 찾습니다.ComputerUseService.click()에서 인자 조합에 따라 거부되는 경우를 두 개 찾습니다.완료 기준: “도구 이름 / 필수 인자 / 기본값 / 서비스 함수 / 실패 조건” 표를 완성합니다.
예를 들어 accessibility 방식에는 요소 번호가 필요하며, 좌표만으로는 그 조건을 충족하지 못합니다.
목표: 픽셀과 창 좌표를 구분합니다. screenshotPixelToWindowPoint()를 참고해 별도의 학습용 함수나 계산표를 만드세요.
# 운영체제 API를 부르지 않는 학습용 Python 함수
def to_window(point, image_size, window_size):
scale_x = image_size[0] / window_size[0]
scale_y = image_size[1] / window_size[1]
return (point[0] / scale_x, point[1] / scale_y)
# 유효한 크기를 전제로 한 예시
# point=(640,384), image_size=(1280,768), window_size=(1000,600)
# 기대 결과: (500.0, 300.0)
완료 기준: 1배·2배·1.28배 이미지에서 같은 창 내부 위치가 나오는 사례를 각각 만듭니다. 0 크기나 누락값에 대해서는 위 학습용 함수와 실제 Swift 함수의 방어 처리가 어떻게 다른지 별도로 적으세요.
목표: boundedTargetedWalk()의 예산·중복 제거·중단 이유를 손으로 재현합니다.
# 작은 가짜 그래프
0 -> [1, 2]
1 -> [0]
2 -> []
# 모든 노드가 일치하며, 자식 조회는 잘리지 않는다고 가정
maxNodes = 10
limit = 10
expired = false
1 → 0을 만나도 탐색이 끝나는 이유를 설명합니다.limit=1로 바꾸고 중단 이유와 결과 완전성이 어떻게 달라지는지 확인합니다.완료 기준: 기본 결과가 [0, 1, 2]이며, limit=1에서는
[0]과 stop_reason=limit가 됨을 설명합니다.
결과가 하나라는 사실만으로 전체에 일치 요소가 하나라고 결론 내릴 수 없는 이유도 적으세요.
목표: TargetedElementRegistry의 시간과 앱 신원을 가짜 값으로 모델링합니다.
TargetedQuerySafetyTests.swift의 만료·교체 테스트를 참고하세요.
완료 기준: 첫 기록의 축출, 경계 시각의 만료, 재시작 앱 거부, 비우기 뒤 증가하는 번호를 표로 설명합니다. 실제 기본 수명 120초와 테스트에서 바꾼 10초를 구별해야 합니다.
목표: 정책과 시스템 부작용을 분리합니다. LockedUseStateMachine.swift의
begin(), guardsPrepared(), consumePermit(), tick()을 읽으세요.
완료 기준: 정상 경로와 실패 경로에서 어떤 효과가 반환되어야 하는지 설명하고, 모형에서 검증한 정책과 실제 운영체제에서 검증해야 할 동작을 나눠 적습니다.
좋은 실습 결과물은 “한 번 성공했다”는 화면보다, 어떤 입력과 상태에서 성공하거나 거부되어야 하는지 설명하는 표입니다.
주당 3~5시간을 가정한 학습 순서입니다. 처음부터 모든 언어를 익히기보다, 익숙한 언어로 작은 모형을 만든 뒤 원래 Swift 구현과 비교하면 진도를 유지하기 쉽습니다.
| 주차 | 배울 내용과 소스 | 남길 결과물 |
|---|---|---|
| 1주차 | JSON, 표준 입출력, JSON-RPC 요청 ID. MCPServer.swift와 ToolDefinitions.swift를 읽습니다. | 도구 2개의 요청·응답·오류 경로 지도. |
| 2주차 | Swift의 struct·class·enum·guard·throws. 분배기와 관찰 옵션을 읽습니다. | 입력 조합·기본값·거부 조건 표. |
| 3주차 | 트리, BFS, 방문 집합, 탐색 예산. TargetedQuery.swift와 관련 테스트를 읽습니다. | 순환·시간 초과·결과 제한을 다루는 가짜 그래프 모형. |
| 4주차 | 접근성 API, 창 좌표와 픽셀, 이미지 축소. AccessibilitySnapshot.swift의 필요한 함수만 추적합니다. | 좌표 계산표와 이미지 설정 우선순위 사례. |
| 5주차 | Node.js 자식 프로세스·워커·Promise, 세션 수명. JsonLinePeer와 WorkerJavaScriptSession을 읽습니다. | 정상 응답·프로세스 종료·시간 초과의 순서도. |
| 6주차 | Go 스크립트 내장, UIA·AT-SPI 차이, 상태 머신, 서명과 릴리스 흐름을 비교합니다. | 플랫폼 지원표와 정책·부작용 분리 설계안. |
한 기능을 끝까지 추적하는 능력이 생기면, 익숙하지 않은 언어가 섞여 있어도 시스템의 경계부터 이해할 수 있습니다.
누가 판단하는가? 어디서 입력을 검증하는가? 지금 상태는 누가 보관하는가? 실패를 어떻게 표현하는가? 시간 초과 뒤 무엇을 정리하는가? 다른 운영체제에서 어떤 약속이 달라지는가? 각 답에 파일·함수 하나를 연결할 수 있다면 핵심 구조를 이해한 것입니다.
| 용어 | 쉬운 설명 |
|---|---|
| Computer Use | 앱의 화면과 UI 요소를 읽고 클릭·입력으로 조작하는 방식입니다. 이 저장소는 그 실행 도구를 제공합니다. |
| MCP | AI 클라이언트가 외부 도구의 목록을 확인하고 도구를 호출하는 데 쓰는 공통 규약입니다. |
| JSON-RPC | 메서드 이름·인자·요청 ID를 JSON에 담아 보내고, 같은 ID로 응답을 연결하는 호출 규칙입니다. |
| stdio | 표준 입력·표준 출력입니다. 이 프로젝트에서는 자식 프로세스와 줄 단위 메시지를 주고받는 통로가 됩니다. |
| 접근성 트리 | 창·버튼·입력칸의 관계를 계층으로 보여주는 구조입니다. 화면에 보이는 모든 것이 반드시 포함되지는 않습니다. |
| AXUIElement | macOS 접근성 API에서 앱이나 UI 요소를 가리키는 객체입니다. 역할·값을 읽거나 액션을 요청하는 데 사용합니다. |
| UI Automation | Windows의 UI 정보를 읽고 조작하는 접근성 체계입니다. 이 구현은 PowerShell에서 관련 .NET 어셈블리를 불러옵니다. |
| AT-SPI | Linux 데스크톱의 접근성 인터페이스입니다. 이 구현은 Python에서 요소 정보와 의미 기반 액션을 사용합니다. |
| 스냅샷 | 특정 시점에 읽은 앱 상태입니다. 앱이 바뀐 뒤에도 자동으로 항상 최신이 되는 실시간 객체는 아닙니다. |
| 요소 인덱스 | 현재 상태나 조회 결과의 요소를 가리키는 번호입니다. 영구 ID처럼 저장하고 나중에 재사용하면 안 됩니다. |
| 핸들 | 실제 객체를 직접 보내지 않고 가리키는 표식입니다. 조회 인덱스는 세션·수명·앱 신원의 제한을 받는 핸들입니다. |
| PID | 운영체제가 프로세스에 붙이는 번호입니다. 재사용될 수 있어 이 프로젝트의 조회 신원에는 실행 시각도 포함됩니다. |
| BFS | 가까운 단계부터 차례로 탐색하는 너비 우선 탐색입니다. 큐를 사용하며 방문 집합으로 순환을 막을 수 있습니다. |
| 탐색 예산 | 살펴볼 노드 수나 결과 수의 상한입니다. 시스템이 지나치게 오래 또는 많이 읽는 것을 제한합니다. |
| 협력적 제한 | 진행 중간에 시간을 확인해 멈추는 방식입니다. 이미 실행 중인 운영체제 호출을 즉시 강제 종료한다는 뜻은 아닙니다. |
| 대체 경로 | 첫 방법이 처리하지 못할 때 사용하는 다른 실행 방법입니다. 성공 조건뿐 아니라 부작용도 달라질 수 있습니다. |
| SPI | 여기서는 공개 API로 안정성이 약속되지 않은 내부 시스템 인터페이스를 가리킵니다. 운영체제 변경의 영향을 주의해서 봅니다. |
| 가상 디스플레이 | 물리 모니터 대신 운영체제에 추가하는 화면입니다. 여기서는 앱 창을 에이전트용 화면으로 옮기는 데 사용합니다. |
| REPL | 코드를 입력하고 결과를 읽는 반복 실행 환경입니다. 지속 세션은 이전에 만든 앱 변수 등을 유지할 수 있습니다. |
| Worker | Node.js의 별도 JavaScript 실행 스레드입니다. 여기서는 평가가 시간 초과되면 워커를 종료하고 다시 시작합니다. |
| 상태 머신 | 가능한 상태와 허용되는 전환을 명시한 모형입니다. 잠금 실험의 잘못된 순서와 허가 조건을 다룹니다. |
| 임대와 heartbeat | 일정 시간만 유효한 사용 권한과 주기적인 생존 신호입니다. Locked Use 정책의 유지·중단 판단에 쓰입니다. |
| TCC | macOS의 개인정보 보호 권한 체계입니다. 접근성·화면 기록 허용 여부가 실제 실행 조건에 영향을 줍니다. |
| base64 | 이미지 같은 바이트 데이터를 텍스트로 표현하는 인코딩입니다. 암호화와는 다르며, 전달 데이터 크기도 증가합니다. |
| 서명·공증 | 실행 파일의 발행 신원과 macOS 배포 검증에 관련된 절차입니다. 단순한 빌드 성공과는 다른 단계입니다. |
| 고정 의존성 | 항상 같은 버전이나 리비전을 선택하도록 정하는 것입니다. 이 프로젝트는 libwebp 의존성을 1.6.0으로 고정합니다. |
공식 전송 규격에서 stdio는 줄바꿈으로 메시지를 구분하고, 로그는 표준 오류로 분리합니다. 따라서 예시 JSON을 보기 좋게 여러 줄로 표시한 것과 실제 전송 형식은 구분해야 합니다. 실제 요청은 한 줄로 직렬화합니다. MCP stdio 규격
| 항목 | 상태 | 기준 |
|---|---|---|
| 선정 | GitHub Trending Swift Weekly #4 | 2026-10-23 분석 기준. |
| 스타 / 포크 | 2,645 / 245 | 2026-10-23 GitHub 기준. |
| 주 언어 | Swift | GitHub 저장소 분류. |
| 생성일 | 2026-04-17 | GitHub 저장소 메타데이터. |
| 최신 릴리스 | v1.1.2 · 2026-10-17 | GitHub 릴리스 기준. 플러그인 manifest도 1.1.2입니다. |
| 최근 push | 2026-10-19 | GitHub 저장소 메타데이터. |
| 최근 커밋 | 43eb4b5 · 2026-10-19 | 검증된 참고 번들 이전을 기록한 문서 커밋입니다. |
| 코드 규모 | 집계 대상 251파일 · 58,950줄 | 앱·패키지·실험·스크립트, 해당 코드 확장자, 테스트·빈 줄·주석 포함. |
| Swift 규모 | 151파일 · 35,921줄 | 같은 집계 기준. 외부 참고 번들은 제외합니다. |
| 네이티브 도구 | macOS 11개 / Windows·Linux 9개 | 실제 도구 정의. JS 플러그인이 노출하는 2개 도구와 구별합니다. |
| 라이선스 | MIT · Copyright 2026 Leo | 루트 LICENSE. 포함된 의존성과 참고 자료의 별도 고지도 함께 확인해야 합니다. |
해설 기준: 커밋 43eb4b5의 소스·설정·테스트 정의를 기준으로 합니다. 실제 설치·GUI 조작의 성공 여부와 성능은 실행 환경별 검증이 필요합니다. 코드 규모는 명시한 범위의 줄 수입니다.
호환성과 부작용: Windows·Linux는 실험적 런타임이며 기능·옵션이 macOS와 완전히 같지 않습니다. 백그라운드 조작을 지향해도 앱 활성화가 허용되는 복구 경로가 있고, 전역 입력을 허용하면 실제 포인터·포커스에 영향을 줄 수 있습니다.
내부 API와 실험: SkyLightSPI.swift와 가상 디스플레이 shim은 비공개 인터페이스를 사용합니다.
운영체제 업데이트에 따른 호환성은 공개 API와 같은 수준으로 기대할 수 없습니다. Locked Use의 전체 장애 시나리오 검증도 진행 중입니다.
데이터 경계: 실행 엔진은 로컬이어도 반환한 접근성 텍스트와 이미지가 AI 클라이언트로 전달될 수 있습니다. 로컬 실행과 정보의 기기 외 전송 여부는 구별해야 합니다. 민감한 앱을 다룰 때는 연결한 클라이언트의 데이터 흐름까지 확인하세요.
상태와 안전: 요소 번호는 세션과 관찰 상태에 종속됩니다. 시간 초과는 이미 발생한 입력을 취소하지 않습니다. 서버 안내의 사용자 확인 지침과 런타임의 기술적 차단도 서로 다릅니다. 도구가 노출되어 있다는 사실만으로 모든 액션에 자동 승인 절차가 있다고 가정하지 마세요.