에이전트 앱
8c012e4 기준입니다.OpenBitFun은 Rust 기반 에이전트 실행 계층을 여러 사용 화면으로 제공하는 오픈소스 작업 환경입니다. 모델과 대화하는 기능에 파일·터미널·검색·승인·세션 저장·원격 연결을 묶습니다. README는 코드 작성 밖의 일반 업무와 Mini App 제작까지 제품의 범위로 설명합니다.
이 저장소에서 배울 핵심은 모델을 호출하는 방법보다, 모델이 시작한 일을 사람이 통제하며 이어 가게 만드는 방법입니다.
작업자는 다음 행동을 제안합니다. 작업실은 어떤 파일을 읽을 수 있는지 확인하고, 필요한 공구를 건네며, 진행 상황을 기록합니다. 작업자가 “벽을 뚫자”고 말했다고 바로 드릴을 켜면 안 되듯, 도구 요청과 실행 허가는 별도로 다뤄야 합니다.
휴대전화는 멀리서 보는 제어판에 가깝습니다. 제어판의 화면 전환과 실제 작업의 종료를 같은 사건으로 취급하지 않는 설계가 중요합니다.
“이 코드의 오류를 찾아줘”라는 요청은 모델 응답 한 번으로 끝나지 않을 수 있습니다.
모델이 파일 읽기를 요청하고, 실행 결과를 받은 뒤 다른 파일을 조사하며, 마지막에 답을 만듭니다.
ExecutionEngine::execute_dialog_turn()과 RoundExecutor::execute_round_with_lifecycle()가 이 구분을 보여 줍니다.
사용자 요청 하나 = 대화 턴 하나 │ ├─ 모델 라운드 1: 조사할 파일 선택 │ └─ 도구 실행 결과를 대화 기록에 추가 ├─ 모델 라운드 2: 결과를 읽고 추가 조사 │ └─ 도구 실행 결과를 대화 기록에 추가 └─ 모델 라운드 3: 최종 설명 작성 각 단계의 진행 사실 ──> 이벤트 ──> 데스크톱 / CLI / 원격 화면
| 이름 | 쉬운 의미 | 코드에서 찾을 단서 |
|---|---|---|
| Session | 대화와 작업 상태를 묶는 오래가는 단위 | session_id, SessionState |
| Dialog Turn | 한 번의 요청을 처리하는 작업 단위 | dialog_turn_id, execute_dialog_turn() |
| Model Round | 모델을 한 차례 호출하고 결과를 처리하는 단위 | round_id, RoundExecutor |
출발점은 src/apps/desktop/src/api/agentic_api.rs입니다.
그 안의 start_dialog_turn()은 요청을 변환해 공통 런타임에 넘깁니다.
화면에서 사용하는 명령과 실제 작업 엔진을 분리하는 첫 경계를 여기서 볼 수 있습니다.
2026-10-23 GitHub Trending Rust Weekly 5위에 오른 프로젝트입니다. 스타 3,099개와 포크 278개는 관심의 크기를 보여 주지만, 사용자의 만족도나 실행 속도를 직접 증명하지는 않습니다. 순위 상승의 원인을 분리해 측정한 자료는 없으므로, 아래는 현재 기능과 구현을 바탕으로 해석한 관심 요인입니다.
학습자는 Rust 비동기 코드뿐 아니라 편집기·터미널·메시지 표시가 연결되는 제품 전체를 읽을 수 있습니다.
src/web-ui/package.json에는 Monaco·xterm·Tiptap·Markdown 관련 의존성이 함께 등장합니다.
채팅 화면 하나를 넘어 다양한 결과물을 다루려는 제품 구성이 의존성에도 드러납니다.
OpenBitFun의 비교 우위로 읽을 지점은 Rust라는 언어 하나가 아니라, 공통 실행 계층을 여러 작업 화면과 연결한 범위입니다.
| 비교할 제품 유형 | 주로 집중하는 경험 | OpenBitFun에서 눈여겨볼 점 |
|---|---|---|
| 터미널 중심 코딩 도구 | 명령 입력과 텍스트 중심 작업 | CLI에 더해 Tauri 데스크톱, 편집기와 별도 도구 화면이 같은 저장소에 있음 |
| IDE 중심 AI 확장 | 기존 편집기 안에서 코드 보조 | 독립 앱으로 세션·원격 제어·Mini App의 제품 수명주기까지 관리 |
| 채팅형 AI 클라이언트 | 대화와 모델 선택 중심 | 파일·프로세스·도구 실행과 승인·취소 흐름을 함께 학습 가능 |
| 에이전트 SDK | 개발자가 조립할 실행 부품 | 실행 부품과 실제 배포 앱을 함께 읽으며 추상화의 사용처를 확인 가능 |
이 표는 제품 유형의 관점 차이를 비교한 것입니다. 특정 경쟁 제품의 기능 부재나 우열을 판정한 표가 아닙니다. 통합 범위가 넓은 만큼 의존성과 빌드 과정도 복잡해진다는 비용이 따릅니다.
README는 Minimal·Standard·Ultimate·Creative 네 모드를 소개합니다. 각각 빠른 직접 협업, 일상적인 다단계 작업, 복잡한 협업, 제작·맞춤화에 초점을 둔다고 설명합니다. 학습할 때는 이를 “서로 다른 모델 네 개”로 이해하지 말고, 에이전트 정의·프롬프트·도구·정책을 조합하는 제품 선택으로 보세요.
관련 코드는 src/crates/assembly/core/src/agentic/agents/definitions/modes/와
src/crates/assembly/agent-content/에서 이어집니다.
MCP·Skills·Hooks·Mini App은 같은 종류의 확장이 아니며, 연결되는 경계도 다릅니다.
AGENTS.md는 원격 작업 공간, 모바일·메신저 원격 제어, 같은 계정의 기기 전환,
제출자가 연결을 끊어도 대상 호스트가 맡는 작업 전달을 각각 구분합니다.
초보자에게 유익한 질문은 “기능이 몇 개인가”보다 “파일이 있는 기기와 화면이 있는 기기가 다르면 누가 일을 해야 하는가”입니다.
“오래 일한다”는 말은 취소·재접속·문맥 압축이 어떻게 연결되는지 묻는 출발점입니다. “원격 제어”는 작업 소유자와 화면 소유자를 어디서 나누는지 묻는 출발점입니다.
GitHub 설명은 고성능 Rust 런타임을 내세웁니다. README의 자체 DeepSWE v1.1 평가에는 GLM-5.3-Flash 통과율 64.6%·실행 시간 중앙값 42.9분, DeepSeek-V4-Flash 통과율 56.6%·중앙값 19.6분이 적혀 있습니다. 이는 README에 게시된 자체 평가 결과이며, 독립적으로 검증된 측정값과 구분해야 합니다.
모델·도구·평가 환경이 함께 작용하므로 이 수치만으로 Rust의 효과나 다른 제품보다 빠른 정도를 계산할 수 없습니다. 소스에서 확인할 수 있는 것은 취소 토큰, 이벤트 압축, 문맥 예산처럼 성능과 지속 작업을 다루는 장치의 존재입니다.
루트 Cargo.toml은 Rust 워크스페이스 멤버 54개와 공통 버전을 관리합니다.
pnpm-workspace.yaml은 React 앱·공유 패키지·디자인 시스템을 묶습니다.
의존성 선언은 버전 조건을 나타내고, 잠금 파일의 버전은 해당 스냅샷에서 선택된 구체적인 버전입니다.
version = "1.52"는
1.52.0만 고정한다는 뜻이 아니므로, 실제 선택 버전은 Cargo.lock에서 확인해야 합니다.| 기술 | 선언값 → 잠금값 | 이 저장소에서의 역할 |
|---|---|---|
| Rust | edition = "2021" | 공통 패키지 버전 1.0.7. 언어 에디션이며 컴파일러 최소 버전과는 다름 |
| Tokio | 1.52 → 1.53.1 | 비동기 실행, 프로세스·네트워크·동기화. 루트 기본 기능 비활성화 |
| Serde | 1.0 → 1.0.229 | 요청·응답·이벤트를 직렬화. serde_json = "1.0"도 공통 관리 |
| Reqwest | 0.13.4 → 0.13.4 | 모델 등 외부 HTTP 통신. 필요한 TLS·스트림 기능은 소비자가 선택 |
| Axum | 0.8 → 0.8.9 | 서버 진입점의 HTTP·WebSocket. tower-http = "0.6.11"와 조합 |
| Tauri | 2.11 → 2.11.5 | 데스크톱 창·명령·이벤트·패키징 |
| RMCP | 1.7 → 1.8.0 | MCP 연결에 쓰는 Rust SDK. 루트에서 기본 기능을 끄고 필요한 기능 명시 |
| ts-rs | 12 → 12.0.1 | Rust 계약에서 TypeScript 타입을 생성하는 경로에 사용 |
| Git·터미널 | git2 = "0.21", portable-pty = "0.8" | Git 처리와 의사 터미널. git2 잠금값은 0.21.0 |
| CLI·TUI | clap = "4.6.1", ratatui = "0.29", crossterm = "0.28" | 명령 인자와 터미널 화면. 이 행은 선언값 |
위 Rust 버전 출처는 루트 Cargo.toml과 Cargo.lock입니다.
src/apps/desktop/Cargo.toml은 Tokio의 fs·process·rt-multi-thread 등을 선택하지만,
더 작은 소비자는 다른 기능 조합을 고릅니다. 같은 의존성을 쓴다고 모든 기능이 필요한 것은 아닙니다.
# Cargo.toml의 공통 정책
[workspace.dependencies]
tokio = { version = "1.52", default-features = false }
# src/crates/execution/tool-execution/Cargo.toml의 선택
tokio = { workspace = true, features = ["io-util", "rt", "sync", "time"] }
| 영역 | 실제 선언 버전 | 읽으면서 배울 것 |
|---|---|---|
| 화면 | react·react-dom ^18.3.1 | 대화·편집기·설정을 컴포넌트로 구성 |
| 타입·빌드 | typescript ~5.8.3, vite ^7.0.4 | 타입 검사와 개발·배포 빌드의 역할 구분 |
| 상태 | zustand ^5.0.10, immer ^11.1.3 | 컴포넌트 밖의 공유 상태와 변경 표현 |
| 코드 편집기 | monaco-editor ^0.52.2, @monaco-editor/react ^4.6.0 | 전문 편집기 엔진과 React 연결 계층 구분 |
| 문서 편집기 | @tiptap/react ^3.20.4 | 서식 있는 문서 편집을 코드 편집과 구분 |
| 터미널 | @xterm/xterm ^6.1.0-beta.101 | 웹 화면에 터미널 데이터를 표시. 운영체제 프로세스 실행은 별도 계층 |
| 긴 목록 | react-virtuoso ^4.18.11, @tanstack/react-virtual ^3.13.23 | 보이는 항목 중심으로 그리는 가상화 |
| 문서 표시 | react-markdown ^10.1.0, mermaid ^11.10.1, pdfjs-dist 5.4.624 | 대화 결과가 텍스트·도식·문서로 확장되는 구성 |
| 국제화 | i18next ^25.8.0, react-i18next ^16.5.3 | 번역 자원과 화면 코드를 나누는 경계 |
| 화면 테스트 | vitest ^4.1.0, jsdom ^29.0.1 | 브라우저 화면을 다루는 단위 테스트 도구 |
이 표는 src/web-ui/package.json의 선언값입니다.
^와 ~를 지우고 정확한 설치 버전처럼 읽지 마세요.
모바일 웹은 별도 패키지이며, xterm 선언도 데스크톱용 Web UI와 동일하지 않습니다.
| 구성 | 파일과 설정값 | 의미 |
|---|---|---|
| Node.js·pnpm | 루트 package.json: >=22.12.0, packageManager: pnpm@10.15.0 | 웹 개발 도구의 기준. 별도 의존성 pnpm ^10.32.1과 구분 |
| 플러그인 호스트 | src/apps/extension-host/package.json: bun@1.3.14, SDK·plugin 1.17.18 | 별도 Bun 패키지와 bun.lock. 루트 pnpm 설치만으로 설명되지 않는 경로 |
| 데스크톱 빌드 | 루트 @tauri-apps/cli 2.11.4, src/apps/desktop/tauri.conf.json의 개발 주소 포트 1422 | Rust 크레이트·웹 API·빌드 CLI는 서로 다른 패키지 |
| CI | .github/workflows/ci.yml: Linux·macOS·Windows 검사, --locked, --frozen-lockfile | 잠금 파일을 지키며 플랫폼별 회귀를 검사 |
| 중계 서버 컨테이너 | src/apps/relay-server/Dockerfile: rust:1.96-bookworm → debian:bookworm-slim | 다단계 빌드. 빌드 도구와 배포 이미지를 분리 |
| 중계 서버 운영 | src/apps/relay-server/docker-compose.yml: 기본 포트 9700, SQLite 경로와 볼륨 지정 | 데스크톱 앱 전체를 컨테이너에 넣는 설정이 아니라 중계 서비스용 |
루트 빌드 스크립트의 plugin-host:prepare는 Bun 설치·빌드를 연결하고,
데스크톱 설정은 그 결과인 extension-host.js를 리소스로 포함합니다.
pnpm-lock.yaml과 Cargo.lock 외에 별도 패키지의 잠금 파일이 존재하는 이유입니다.
또한 루트 [patch.crates-io]는 eioc·tao·wry를
third_party/ 경로로 교체하고, 일부 Tauri 런타임을 Git 리비전으로 지정합니다.
버전 번호만 보고 표준 배포본과 완전히 같은 의존성 트리라고 가정할 수 없습니다.
[사용 화면 / 진입점]
React Web UI CLI / TUI 모바일·다른 기기의 제어 화면
│ │ │
Tauri 명령 또는 WS │ 중계·원격 연결
│ │ │
└───────────────┬──┴───────────────────────┘
v
[호스트별 어댑터 · 앱 초기화 · 제품 조립]
src/apps/desktop · src/apps/cli · src/apps/server
src/crates/interfaces · src/crates/assembly/core
│ 포트 구현 주입
v
[공통 AgentRuntime 파사드]
요청 제출 / 세션 / 취소 / 승인 / 이벤트 조회
│
┌──────────────┼────────────────┐
v v v
상태·조정·실행 문맥·대화 기록 이벤트 저널
│ │ │
├─ 모델 요청 ──┴──> AI 어댑터 ──> 모델 서비스
│
└─ 도구 요청 ──> 목록·정책 검사 ──> 도구 실행
│
파일 / 터미널 / Git / MCP
[계층을 잇는 계약] core-types · events · runtime-ports
[운영체제 구현] services 및 각 호스트의 구체 구현
위 도식은 역할을 간추린 논리 구조도이며, 상자 하나가 프로세스 하나라는 뜻은 아닙니다.
현재 ExecutionEngine과 RoundExecutor의 제품 실행 코드는 여전히
src/crates/assembly/core/src/agentic/execution/에 있습니다.
src/crates/execution/agent-runtime/ 폴더 하나에 모든 로직이 옮겨져 있다고 설명하면 실제 코드와 어긋납니다.
src/apps/server/src/main.rs는 내장 런타임을 쓰는 로컬 단일 사용자 호스트로 자신을 설명하며,
127.0.0.1:8080에 바인딩합니다.
relay-server의 포트 9700과 역할도 다릅니다. 공통 런타임 사용은 모든 호스트가 한 프로세스를 공유한다는 보장과 다릅니다.
src/crates/execution/agent-runtime/src/runtime.rs의 AgentRuntime은
Arc<dyn AgentSubmissionPort> 같은 인터페이스를 보관합니다.
실제 파일 접근이나 화면 객체를 직접 요구하는 대신, 필요한 기능의 계약을 받습니다.
AgentRuntimeBuilder::build()는 필수 제출 포트가 없으면 MissingSubmissionPort를 반환합니다.
// runtime.rs에서 발췌: 제출 동작은 주입된 구현에 위임한다.
pub async fn submit_turn(
&self,
request: AgentSubmissionRequest,
) -> Result<AgentSubmissionResult, RuntimeError> {
self.submission
.submit_message(request)
.await
.map_err(RuntimeError::from)
}
프론트엔드에도 대응되는 구조가 있습니다.
src/web-ui/src/infrastructure/api/adapters/base.ts의 ITransportAdapter는
request()·listen()·disconnect()를 정의합니다.
같은 폴더의 index.ts에 있는 createTransportAdapter()가 Tauri 또는 WebSocket 구현을 선택합니다.
TransportSurfaceBinding은 surfaceId·generation·surfaceEpoch를 함께 기록합니다.
기기 A에 보낸 비동기 요청이 끝날 때 화면이 기기 B로 바뀌어 있을 수 있기 때문입니다.
assertTransportSurface()처럼 결과를 적용할 대상 기기가 바뀌었는지 확인하는 장치도 어댑터 설계의 일부입니다.
src/crates/execution/agent-runtime/src/session_state.rs는 세션을
Idle, Processing, Error로 모델링합니다.
처리 상태에는 턴 ID와 단계가 들어가고, 오류에는 복구 가능 여부가 들어갑니다.
// session_state.rs: 속성과 데이터가 결합된 Rust enum
pub enum SessionState {
Idle,
Processing {
current_turn_id: String,
phase: ProcessingPhase,
},
Error {
error: String,
recoverable: bool,
},
}
ProcessingPhase는 Starting·Compacting·Thinking·
Streaming·ToolCalling·ToolConfirming입니다.
모두가 매번 정해진 순서로 한 번씩 나타나는 파이프라인은 아닙니다. 도구 실행 뒤 다시 모델을 호출할 수도 있습니다.
session_state_manager.rs의 can_start_new_turn()은
대기 상태 또는 복구 가능한 오류에서만 참을 반환합니다.
update_state()는 등록된 상태를 갱신한 뒤 상태 변경 이벤트를 넣습니다.
다만 이 자료형 하나가 모든 전이 규칙을 강제하는 것은 아닙니다. 조정자·스케줄러의 제어 흐름도 함께 읽어야 합니다.
실제 엔진의 execute_dialog_turn_impl()에는 라운드 반복문이 있고, 매 반복 시작에 취소 상태를 확인합니다.
바깥의 execute_dialog_turn()은 내부 결과가 오류여도 뒤이어 자신이 소유한 화면 제어 자원을 해제하고 취소 토큰을 정리합니다.
이 흐름은 일을 시작하는 코드와 끝맺는 코드를 함께 설계해야 한다는 교훈을 줍니다.
취소 토큰은 전원선을 뽑는 것보다 정지 신호를 보내는 일에 가깝습니다. 실행 중인 코드가 신호를 확인하고 빠져나와야 합니다. 화면의 로딩 표시를 끄는 것만으로 작업 프로세스나 자원이 정리되지는 않습니다.
src/crates/execution/agent-runtime/src/session_event_journal.rs의
SessionEventJournal은 화면에 전달할 실행 이벤트를 기록하고 재접속에 필요한 상태를 만듭니다.
record()는 투영 대상 이벤트에 커서를 부여하고, events_since()는 이후 변경분을 조회합니다.
| 장치 | 실제 설정·이름 | 필요한 이유 |
|---|---|---|
| 최근 개별 이벤트 | MAX_REPLAYABLE_TAIL_EVENTS = 1024 | 짧은 연결 끊김에는 빠진 이벤트만 순서대로 보충 |
| 현재 상태 투영 | MAX_PROJECTED_EVENTS_PER_SESSION = 2048 | 텍스트 조각 등을 합쳐 현재 턴의 상태를 표현 |
| 종료된 투영 보존 | MAX_RETAINED_TERMINAL_SESSION_PROJECTIONS = 64 | 종료 세션 투영을 무한히 쌓지 않도록 정리 |
| 프로세스 구분 | stream_id와 cursor | 재시작 전 커서를 새 프로세스의 진행도와 혼동하지 않음 |
화면이 마지막으로 적용한 위치: stream=A, cursor=40
같은 stream, 41번 이후 기록이 남음
└─ events_since() ──> Delta { events, cursor }
└─ 빠진 변경분 적용
기록이 너무 오래됐거나 stream이 바뀜
└─ events_since() ──> SnapshotRequired
└─ snapshot()으로 상태 재구성
누적 텍스트가 “안녕하세요”라는 사실만으로는 화면이 놓친 조각이 “하세요”인지 알 수 없습니다.
그래서 현재 모습을 나타내는 투영과 원래 이벤트를 보관하는 최근 기록을 따로 둡니다.
같은 파일의 a_delta_replays_each_chunk_the_projection_merged_away() 테스트가 바로 이 차이를 다룹니다.
메모리 밖 보존은 SessionEventProjectionStore 포트로 분리합니다.
src/crates/assembly/core/src/service/session_projection_store.rs에는
FileSessionProjectionStore 구현이 있습니다. 저장소를 붙이는 with_store() 경로와
단순 메모리 저널을 구분해야 하며, 이를 앱 전체의 완전한 이벤트 소싱이라고 확대해 부르지는 않는 편이 정확합니다.
src/crates/execution/tool-contracts/src/framework.rs의 ToolRegistryItem은
이름·설명·입력 스키마·읽기 전용 여부 같은 도구 정보를 정의합니다.
제품의 실행 인터페이스는 src/crates/assembly/core/src/agentic/tools/framework.rs의 Tool에서 더해집니다.
목록에 필요한 계약과 구체 실행의 책임을 구분해 읽으세요.
src/crates/assembly/core/src/agentic/tools/registry.rs의 register_mcp_tools()는 MCP 도구를 동적으로 등록합니다.
같은 이름이 있으면 경고 후 덮어쓰는 경로도 있어, 플러그인을 연결할 때 이름과 소유자 정보가 중요한 이유를 보여 줍니다.
// execution_gate.rs의 검사 순서를 간추린 학습용 의사 코드
허용된_도구_목록_검사(도구_이름)?;
런타임_제한_검사(도구_이름)?;
지연_도구_명세와_카탈로그_세대_검사(도구_이름)?;
// 이후 제품 파이프라인의 검증·권한 판단·실행 단계로 이어진다.
위 세 검사를 묶는 실제 함수는 validate_tool_execution_admission()입니다.
src/crates/assembly/core/src/agentic/tools/pipeline/tool_pipeline.rs가 이를 호출하고,
round_executor.rs는 전역·프로젝트·에이전트 규칙과 런타임 상한으로 실행 정책을 구성합니다.
GetToolSpec 관련 검사는 명세를 아직 확인하지 않은 지연 도구의 사용도 제어합니다.
모델이 도구 호출을 출력했다는 사실과, 프로그램이 그 행동을 허용한다는 판단은 서로 다른 단계여야 합니다.
도구 허용 목록·승인·취소는 중요한 통제 장치지만, 악성 코드를 운영체제 차원에서 완전히 격리한다는 증거는 아닙니다.
docs/architecture/product-architecture.md도 Sandbox를 계획 단계로 표시합니다.
파일·프로세스 도구와 외부 확장을 읽을 때는 실제 실행 권한과 격리 경계를 따로 확인해야 합니다.
아래 경로는 모두 저장소 루트 기준입니다.
트리는 주요 폴더를 추린 것이며, 작은 패키지나 플랫폼 하위 폴더까지 전부 펼치지는 않았습니다.
먼저 apps와 crates를 구분하고, 그다음 웹 화면과 공통 패키지를 읽으면 됩니다.
OpenBitFun/ ├── Cargo.toml / Cargo.lock Rust 패키지 묶음과 잠금 결과 ├── package.json / pnpm-lock.yaml 웹·제품 빌드 명령과 잠금 결과 ├── pnpm-workspace.yaml 웹 패키지 묶음 ├── src/ │ ├── apps/ │ │ ├── desktop/ Tauri 호스트·명령·운영체제 연결 │ │ ├── cli/ 명령행·터미널 화면·호스트 │ │ ├── server/ 로컬 WebSocket·App Server 진입점 │ │ ├── sdk-host/ SDK 호스트 실행 파일 │ │ ├── extension-host/ Bun 기반 확장 호스트 │ │ ├── relay-server/ 기기 연결을 위한 중계 서비스 │ │ ├── miniapp-shell/ Mini App의 별도 창을 위한 셸 │ │ ├── mobile/ 네이티브 모바일 관련 코드 │ │ └── data-tools/ 데이터 마이그레이션 도구 │ ├── crates/ │ │ ├── interfaces/ ACP·App Server·SDK 인터페이스 │ │ ├── assembly/ 제품 능력 선택과 구현 조립 │ │ ├── adapters/ AI·전송·외부 생태계 변환 │ │ ├── services/ 파일·프로세스·원격 연결 등 구현 │ │ ├── execution/ 런타임·스트림·도구 실행의 공통 부품 │ │ └── contracts/ 타입·이벤트·포트·제품 도메인 계약 │ ├── web-ui/ 데스크톱·웹용 주 React 화면 │ ├── mobile-web/ 원격 제어용 모바일 웹 │ └── shared/ 국제화·기능 목록 등 공유 계약 ├── design-system/ 토큰·테마·UI·디자인 실험실 ├── packages/ 공유 표현 패키지와 외부 연결 패키지 ├── sdk/typescript/ TypeScript SDK ├── products/ 제품 구성 정의 ├── extensions/ 제품 확장 자산 ├── MiniApp/ Mini App 예제·관련 자산 ├── OpenBitFun-Installer/ 별도 설치 프로그램 ├── deploy/ 배포 설정과 운영 스크립트 ├── scripts/ 생성·검사·패키징 보조 코드 ├── tests/e2e/ 제품 통합 시나리오 검사 ├── docs/ 구조·기능·설계 문서 ├── website/ 공식 웹사이트 ├── third_party/ 수정한 외부 의존성 코드 └── .github/workflows/ CI·릴리스·플랫폼별 패키징
| 계층 | 주로 맡는 일 | 읽을 때 할 질문 |
|---|---|---|
| apps·interfaces | 사용자·프로토콜의 진입점 | 입력을 어떤 공통 요청으로 바꾸는가? |
| assembly | 제품 구성과 구현 연결 | 필요한 포트에 어떤 구현을 넣는가? |
| adapters | 외부 형식을 내부 계약으로 번역 | 외부 규칙이 내부 도메인으로 새어 들어오지 않는가? |
| services | 운영체제·네트워크·저장소 처리 | 작업은 실제로 어느 기기에서 일어나는가? |
| execution | 공통 실행 상태·이벤트·도구 부품 | 화면 없이도 동작을 설명할 수 있는가? |
| contracts | 타입·오류·이벤트·포트 약속 | 계약을 바꾸면 어떤 소비자가 영향을 받는가? |
의도한 의존 방향은 위 계층에서 아래 계층으로 내려갑니다.
다만 제품 실행 로직 일부가 src/crates/assembly/core/에 남아 있다는 점을 함께 기억하세요.
구조 문서가 제시하는 목표와 실제 함수가 놓인 위치를 함께 보는 것이 대형 저장소를 읽는 방법입니다.
| 언어·확장자 | 파일 수 | 물리적 줄 수 |
|---|---|---|
| Rust · .rs | 2,033 | 1,079,373줄 · 약 108만 줄 |
| TypeScript · .ts + .tsx | 4,084 | 723,050줄 · 약 72만 줄 |
| JavaScript · .js + .mjs + .cjs | 488 | 194,567줄 · 약 19만 줄 |
| 스타일 · .css + .scss | 531 | 135,001줄 |
| Kotlin · .kt + .kts | 349 | 60,162줄 |
| Swift · .swift | 55 | 17,979줄 |
| Python · .py | 21 | 7,881줄 |
커밋 8c012e4의 Git 추적 파일을 확장자별로 집계한 값이며, 마지막 줄바꿈이 없는 행도 한 줄로 셉니다.
third_party/와 심볼릭 링크는 제외하고 빈 줄·주석·테스트·생성 코드는 포함한 수치입니다. Git이 추적하는 내장 번들 자산도 포함됩니다.
따라서 “사람이 작성한 제품 로직의 줄 수”나 순수 실행문 수로 해석하면 안 됩니다.
작은 언어 파일이 있다고 그 언어가 주 백엔드라는 뜻도 아닙니다.
루트에는 pyproject.toml이나 go.mod가 없으며, 주 실행 구조는 Rust와 웹 패키지 묶음으로 설명됩니다.
큰 저장소는 처음부터 끝까지 읽기보다, 질문 하나를 정하고 그 답을 가진 함수와 계약만 연결해 읽을 때 배울 수 있습니다.
SessionState를 보면 문자열 하나 대신 enum을 쓰는 이유를 알 수 있습니다.
처리 중이라면 턴 ID와 단계가 함께 있고, 오류라면 원인과 복구 가능 여부가 함께 있습니다.
RuntimeBuildError와 RuntimeError에서는 조립 실패와 실행 중 실패를 별도 타입으로 나눕니다.
실습 아이디어: 대기·실행·실패 상태를 불리언 세 개로 표현하면 몇 가지 모순 조합이 가능한지 적어 보세요. 그다음 enum으로 바꿔 동시에 실행 중이면서 대기 중인 상태를 표현할 수 있는지 비교합니다.
Arc는 여러 소유자가 같은 값을 공유할 때 쓰는 참조 계수 포인터입니다.
dyn Trait은 구체 타입 대신 약속된 동작을 통해 객체를 사용하게 합니다.
Arc<dyn AgentSubmissionPort>는 공유 가능한 제출 기능의 구현을 가리킵니다.
Arc만으로 내부 데이터 변경까지 자동으로 안전해지는 것은 아닙니다.execution_engine.rs에는 CancellationToken과 tokio::select!가 등장합니다.
오래 걸리는 준비 작업도 취소 요청과 함께 기다리도록 만드는 방식입니다.
await를 쓴다는 사실만으로 취소나 자원 정리가 저절로 해결되지는 않습니다.
실습 아이디어: “모델 연결 대기 중 취소”와 “도구 실행 중 취소”를 나눠 순서도를 그리세요.
어느 자원이 만들어졌고 누가 정리하는지 적은 뒤, execute_dialog_turn()의 정리 경로와 대조합니다.
start_dialog_turn()은 Rust 호스트 명령이고,
ITransportAdapter.request<T>()는 화면이 결과를 요청하는 공통 통로입니다.
Rust 쪽 Serde 이름과 TypeScript 요청 필드가 어긋나면 컴파일되는 두 프로그램 사이에서도 통신이 실패할 수 있습니다.
실습 아이디어: StartDialogTurnRequest와 변환 함수 desktop_dialog_turn_request()를 읽고
세션 ID·워크스페이스 정보·원격 연결 정보가 어디로 이동하는지 표로 만드세요.
모든 필드가 단순 복사되는지, 호환성을 위해 남은 필드가 있는지도 확인합니다.
// base.ts의 형태를 줄인 예시: 실제 인터페이스에는 다른 메서드도 있다.
interface ITransportAdapter {
request<T>(action: string, params?: any): Promise<T>;
listen<T>(event: string, callback: (data: T) => void): () => void;
}
여기서 <T>는 호출자가 기대하는 타입을 표현하지만, 실제 네트워크 입력을 자동 검증해 주지는 않습니다.
정적 타입과 실행 시 검증을 구분하는 것도 좋은 학습 주제입니다.
listen()의 반환값은 구독 해제 함수이므로 화면을 떠날 때의 정리까지 생각해야 합니다.
src/web-ui/src/infrastructure/api/adapters/index.ts의 기기 바인딩과
src/web-ui/src/infrastructure/api/adapters/tauri-adapter.ts의 registerSharedTauriEventListener()를 함께 읽으세요.
후자는 기기별 이벤트를 분류하고, 이전 구독이나 이전 화면 범위에 속한 결과가 현재 화면으로 흘러오지 않게 확인합니다.
실습 아이디어: A 기기의 요청을 보낸 직후 B 기기로 전환하고 A의 응답이 늦게 도착하는 상황을 그려 보세요. 요청 ID만 확인할 때와 기기·화면 세대까지 확인할 때 어떤 차이가 생기는지 설명합니다. 채팅뿐 아니라 검색 자동완성·탭 전환 화면에도 적용할 수 있는 문제입니다.
src/crates/assembly/core/src/agentic/session/compression/compressor.rs의
ContextCompressor는 압축 계획과 결과 변환을 다룹니다.
CompressionPlan에는 요약 대상, 유지할 사용자 메시지, 최근 대화 원문, 토큰 예산이 따로 있습니다.
코드의 DEFAULT_RECENT_CONTEXT_TOKENS = 10_000,
RECENT_CONTEXT_RETRY_STEP_TOKENS = 10_000,
MAX_RETAINED_USER_TOKENS = 20_000은 이 압축기의 정책 상수입니다.
모델의 전체 문맥 창 크기나 모든 상황에서 항상 쓰는 최종 예산과 혼동하지 마세요.
실습 아이디어: “목표·금지 조건·도구 결과·최근 오류”가 들어 있는 가상 대화를 만들고 요약으로 남길 것과 원문을 유지할 것을 나눠 보세요. 이후 작업을 다시 시작할 수 있는지가 압축 품질의 기준입니다.
ToolRegistryItem::is_readonly()의 기본값은 거짓이고,
is_concurrency_safe()의 기본 구현은 읽기 전용 여부를 따릅니다.
도구가 수정 작업인지, 동시에 실행해도 되는지 같은 정보가 실행 정책의 재료가 됩니다.
이름이나 설명만 등록하는 단순한 함수 목록보다 책임이 많습니다.
실습 아이디어: 파일 읽기와 파일 이름 변경 도구를 상상하고, 같은 디렉토리에서 동시에 실행해도 되는 조합을 적어 보세요. 등록·노출·허용·승인·실행 결과 중 어느 단계에서 실패해야 하는지도 구분합니다.
src/crates/execution/agent-runtime/Cargo.toml은 default = []이고 기능별로 의존성을 선택합니다.
src/crates/execution/tool-execution/Cargo.toml도 문서 읽기와 웹 본문 처리를 선택 기능으로 나눕니다.
학습 포인트는 “전부 켜서 성공시키기”보다 필요한 소비자에게 필요한 기능만 연결하는 방법입니다.
실습 아이디어: native-hook-runtime 기능이 활성화하는 Tokio 세부 기능을 표시하고,
단순 계약만 읽는 소비자에게 프로세스 실행 의존성이 왜 불필요할 수 있는지 설명합니다.
구조 검사가 CI에 있는 이유도 함께 연결해 보세요.
README와 기여 안내는 Node.js·pnpm·Rust·Tauri 사전 조건을 제시하지만, 모든 기능에 통용되는 공식 최소 RAM·CPU·디스크 수치를 제시하지는 않습니다. 앱을 실행하는 컴퓨터의 요구사항과 모델을 직접 추론하는 컴퓨터의 요구사항은 별도로 판단해야 합니다.
| 상황 | 확인한 요구 조건·설정 | 해석할 때 주의할 점 |
|---|---|---|
| 배포 앱 사용 | README 안내 대상은 Windows·macOS·Linux | 지원 플랫폼 표시는 모든 운영체제 버전과 모든 기능의 동일한 검증을 뜻하지 않음 |
| 소스 빌드 | Node.js 22.12 이상, pnpm 10.15.0, Rust 도구 모음, Tauri 사전 조건 | README·CONTRIBUTING·루트 manifest 기준. Rust 2021은 최소 컴파일러 버전 표기가 아님 |
| 플러그인 호스트 | Bun 1.3.14를 패키지와 CI에서 지정 | 해당 호스트 리소스 준비가 제품 빌드 경로에 포함됨 |
| Linux 개발 | CI가 GTK 3·WebKitGTK·GStreamer·Tesseract 등의 개발 패키지 설치 | GUI·화면 캡처·OCR의 시스템 라이브러리가 Rust 도구와 별도로 필요 |
| macOS 화면 캡처 | 데스크톱 README: 해당 ScreenCaptureKit 경로는 macOS 12.3 이상 | 캡처 기능의 조건이며 전체 앱의 최소 OS 버전이라고 확대하면 안 됨 |
| macOS 배포 번들 제작 | 데스크톱 README: 전체 Xcode 26 이상 | 개발 실행과 배포 번들 제작의 도구 조건이 다름 |
| 모델 연결 | README는 모델을 설정한 뒤 시작하도록 안내 | 선택한 모델 서비스의 연결·인증·요금과 기능 제약은 별도 확인 |
| GPU | README·기여 안내에 전용 GPU 필수 조건 없음 | 외부 모델 호출과 로컬 모델 추론을 구분. 자체 추론 비용은 모델마다 다름 |
| 중계 서버 | 별도 서비스, 기본 포트 9700, 데이터 볼륨과 SQLite 파일 설정 | 계정·기기 연결 구성에 필요하며 모든 로컬 코드 읽기에 필요한 서비스는 아님 |
.github/workflows/ci.yml의 Rust 빌드 작업은 CARGO_INCREMENTAL=0,
CARGO_PROFILE_DEV_DEBUG=0, CARGO_PROFILE_TEST_DEBUG=0을 설정합니다.
주석은 호스팅 러너의 디스크 한도 안에 유지하기 위한 설정이라고 설명합니다.
큰 Rust 제품에서는 실행 속도뿐 아니라 개발 산출물의 크기도 관리 대상이라는 사례입니다.
루트의 profile.release는 opt-level = 3, lto = "thin",
codegen-units = 1, strip = true를 설정합니다.
이는 컴파일·링크·배포 산출물에 관한 선택이지, 앱이 특정 용량 이하의 RAM에서 반드시 동작한다는 보장은 아닙니다.
src/apps/relay-server/Dockerfile은 CARGO_BUILD_JOBS 인자를 받아
빌드 병렬도를 낮출 수 있게 합니다. 이 컨테이너의 Rust 1.96 이미지는 해당 빌더의 선택이며,
전체 저장소의 공식 최소 Rust 버전이라고 읽으면 안 됩니다.
이 글의 요청 추적·상태표·커서 복구 연습은 파일 검색과 코드 읽기로 시작할 수 있습니다. 실제 앱을 사용할 때에는 운영체제별 사전 조건과 선택한 기능의 권한을 해당 안내에서 확인하면 됩니다.
다음 과제는 소스를 읽고 기록하거나, 모의 입력만 쓰는 별도 학습 예제로 진행할 수 있습니다. 실제 모델·터미널·중계 서버 연결을 전제로 하지 않습니다. 실습의 완료 기준은 코드를 많이 바꾸는 것이 아니라, 정상 경로와 실패 경로를 같은 자료로 설명하는 것입니다.
할 일: start_dialog_turn()에서 시작해 요청 변환과
AgentRuntime::submit_dialog_turn()까지 연결하고, 제출 결과의 세션 ID를 다시 확인하는 부분을 찾습니다.
결과물: 함수 이름·입력 타입·출력 타입·책임이 있는 네 칸짜리 표와 호출 화살표.
완료 기준: 명령 응답의 “시작 성공”이 전체 작업 완료와 같은 뜻인지 설명합니다. 등록되지 않은 포트와 다른 세션 ID를 돌려준 구현이 각각 어떤 오류로 이어지는지도 적습니다.
# 저장소 루트에서 읽기 전용으로 위치 찾기
rg -n 'start_dialog_turn|desktop_dialog_turn_request' src/apps/desktop/src/api/agentic_api.rs
rg -n 'submit_dialog_turn|MissingDialogTurnPort' src/crates/execution/agent-runtime/src/runtime.rs
할 일: SessionStateManager::can_start_new_turn()을 읽고
미등록·대기·처리·복구 가능 오류·복구 불가능 오류의 반환값을 표로 만듭니다.
결과물: 다섯 상태의 진리표와 ProcessingPhase를 한국어로 풀이한 단계 카드.
완료 기준: 미등록 상태가 대기 상태와 다르다는 점, 오류라고 항상 재시도할 수 있는 것은 아니라는 점을 설명합니다.
같은 파일의 session_state_manager_keeps_turn_start_guard_semantics()와 대조합니다.
할 일: 세션 하나·최근 기록 한도 3개인 모의 저널을 설계하고, 같은 스트림·너무 오래된 커서·다른 스트림·미래 커서를 각각 입력합니다. 한도 3은 연습을 위한 값이며 실제 코드의 한도 1,024와 구분합니다.
결과물: 입력 커서와 남은 기록, 예상 응답 Delta 또는 SnapshotRequired를 적은 사례집.
완료 기준: 문자열을 합친 현재 투영만으로 누락 이벤트를 복구할 수 없는 이유를 예제로 보입니다.
tail_since()의 oldest.cursor <= cursor.saturating_add(1) 조건을 자신의 말로 설명합니다.
# 연습용 데이터: 전체 제품을 실행하지 않고 결과를 예측한다.
남은 기록 = [8, 9, 10]
같은 스트림, 커서 8 => [9, 10]을 담은 Delta
같은 스트림, 커서 6 => SnapshotRequired
다른 스트림, 커서 8 => SnapshotRequired
같은 스트림, 커서 11 => SnapshotRequired
할 일: 실제 파일에 접근하지 않고 고정 문자열만 돌려주는 가상 도구를 그립니다. 허용 목록 제외, 런타임 제한, 지연 명세 미확인, 승인 대기를 서로 다른 상태로 표현합니다.
결과물: 모의 입력·거절 위치·오류 종류·부작용 유무를 담은 표와
validate_tool_execution_admission() 세 검사와의 대응표.
완료 기준: 모델에게 도구 설명을 보여 준 상태와 실행을 승인한 상태를 구분합니다. 실행 관문이 통과해도 제품 권한 정책의 별도 판단이 남는다는 점을 표시합니다.
할 일: 기기 A의 작업을 보다가 B로 전환하는 상황에서
A의 늦은 응답·이벤트·승인 요청을 어느 상태 저장소로 보내야 하는지 설계합니다.
TransportSurfaceBinding과 registerSharedTauriEventListener()를 근거로 삼습니다.
결과물: 두 기기와 화면을 분리한 시퀀스도, 화면 전환·연결 끊김·호스트 재시작의 차이표.
완료 기준: 화면을 닫는 일, 전송 연결을 끊는 일, 작업을 취소하는 일을 세 사건으로 설명합니다. 복구 때 커서 외에 기기와 스트림의 식별 정보가 필요한 이유도 적습니다.
과제 결과를 설명할 때에는 “실행해 봤다”와 “소스의 조건문으로 예상했다”를 구분해 적으세요. 특히 원격 복구와 운영체제 자동화는 코드가 존재한다는 사실만으로 모든 플랫폼에서 검증됐다고 말할 수 없습니다.
한 주에 저장소 전체를 읽는 방식보다 작은 결과물을 하나씩 완성하는 순서를 권합니다. Rust를 처음 접한다면 1주차를 늘리고, 웹 개발 경험이 있다면 3주차에서 익숙한 지식과 호스트 경계를 연결해 보세요. 각 주의 결과물이 다음 주에 사용할 설명 자료가 되도록 구성했습니다.
| 주차 | 핵심 개념 | 이 저장소의 읽을 곳 | 완성할 결과물 |
|---|---|---|---|
| 1주차 | 소유권·참조·enum·Result·패턴 매칭 | session_state.rs, session_state_manager.rs | 상태별 시작 가능 여부와 오류 복구 표 |
| 2주차 | Trait·Arc·의존성 주입·빌더 | runtime.rs, runtime-ports, src/crates/execution/agent-runtime/Cargo.toml | 필수·선택 포트 지도와 누락 오류 예시 |
| 3주차 | TypeScript 제네릭·IPC·비동기 화면 갱신 | ITransportAdapter, start_dialog_turn(), TransportSurfaceBinding | 화면→호스트→런타임 요청 추적표 |
| 4주차 | Tokio·취소·턴과 라운드·정리 | execution_engine.rs, round_executor.rs | 모델 호출과 도구 호출 사이의 취소 시퀀스도 |
| 5주차 | 커서·차분 복구·투영·스냅샷 | session_event_journal.rs, session_projection_store.rs | 기록 한도·재시작·미래 커서의 모의 사례집 |
| 6주차 | 도구 스키마·권한·문맥 예산·선택 기능 | execution_gate.rs, tool_pipeline.rs, compressor.rs, CI | 도구 실행 설계서와 압축 후 작업 재개 계획 |
표의 짧은 파일명은 4~6절에서 설명한 전체 경로를 가리킵니다. 읽다가 낯선 타입이 나오면 모든 구현을 펼치기 전에 입력·출력·오류부터 적어 보세요. 해당 함수가 무엇을 책임지는지 알면 긴 구현에서 필요한 구간을 고르기 쉬워집니다.
Arc로 공유하는 객체가 무엇인지 찾습니다.학습을 마쳤다는 기준은 기능을 외우는 것이 아니라, 연결이 끊기거나 권한이 거절됐을 때 시스템이 어떻게 행동하는지 설명하는 것입니다.
모델 호출을 한 번 더 해야 하는 이유는 무엇인가요? 화면의 연결 종료가 작업 취소와 다른 이유는 무엇인가요? 이벤트 커서는 왜 숫자 하나로 충분하지 않나요? 도구를 등록했는데도 실행이 거절될 수 있는 이유는 무엇인가요?
각 질문에 관련 함수 하나와 반례 하나를 적을 수 있다면, 자신의 작은 에이전트 앱에도 같은 설계 질문을 적용할 준비가 된 것입니다.
| 용어 | 쉬운 설명 |
|---|---|
| Agent Runtime | 요청·상태·도구·이벤트·취소를 연결하는 실행 계층. 모델 자체와 구분합니다. |
| Harness | 모델이 작업하도록 둘러싼 계획·문맥·도구·권한 장치의 묶음입니다. |
| Session | 여러 요청과 대화 기록이 이어지는 단위입니다. |
| Dialog Turn | 사용자 요청 등을 처리하는 한 작업 단위입니다. 여러 모델 라운드를 포함할 수 있습니다. |
| Model Round | 모델을 한 번 호출하고 응답·도구 요청을 처리하는 단위입니다. |
| Port | 구체 구현 없이 필요한 기능을 약속한 인터페이스입니다. |
| Adapter | 특정 운영체제·프로토콜·제품을 공통 계약에 맞춰 주는 연결 구현입니다. |
| 파사드 | 여러 내부 기능을 사용할 수 있도록 정리한 공통 진입점입니다. |
| 의존성 주입 | 객체가 구현을 직접 만드는 대신 바깥에서 받아 사용하는 방식입니다. |
| Trait | Rust에서 타입이 제공해야 할 동작을 정의하는 약속입니다. |
| enum | 가능한 경우들을 이름 붙여 표현하는 타입입니다. 각 경우에 서로 다른 데이터를 넣을 수 있습니다. |
| Arc | 여러 소유자가 같은 객체를 공유하도록 돕는 참조 계수 포인터입니다. |
| Send·Sync | 스레드 사이에서 값을 옮기거나 참조를 공유할 수 있는 성질을 나타내는 Rust의 약속입니다. |
| async·await | 완료를 기다리는 동안 실행 기회를 다른 작업에 넘길 수 있게 표현하는 문법입니다. |
| CancellationToken | 작업이 정지 요청을 전달받고 확인하도록 쓰는 취소 신호입니다. |
| IPC | 프로세스 사이의 통신입니다. 데스크톱 화면이 호스트 명령을 호출하는 경계를 이해할 때 등장합니다. |
| WebSocket | 연결을 유지하면서 양방향으로 데이터를 주고받는 통신 방식입니다. |
| 직렬화 | 타입이 있는 값을 JSON 같은 전달·저장 형식으로 바꾸는 일입니다. |
| 이벤트 | 텍스트 조각 수신·도구 진행·상태 변경처럼 이미 일어난 사실을 알리는 데이터입니다. |
| 커서 | 클라이언트가 이벤트를 어디까지 적용했는지 나타내는 위치 정보입니다. |
| stream_id | 커서가 어느 런타임 스트림에 속하는지 구분하는 값입니다. |
| 투영 · Projection | 이벤트를 합치거나 정리해 현재 상태를 표현한 결과입니다. |
| 스냅샷 | 어느 시점의 상태를 한 번에 읽을 수 있게 묶은 자료입니다. |
| Delta | 이미 아는 위치 이후에 새로 생긴 변경분입니다. |
| 레지스트리 | 이름으로 도구·구현을 찾고 관리하는 목록입니다. 그 자체로 실행 허가를 뜻하지는 않습니다. |
| JSON Schema | 입력 데이터에 어떤 필드와 타입이 필요한지 기술하는 형식입니다. |
| MCP | 모델 애플리케이션이 외부 도구·자원에 연결할 때 사용하는 규약입니다. |
| 지연 도구 | 모든 상세 명세를 처음부터 보내지 않고 필요할 때 확인하도록 관리하는 도구입니다. |
| 문맥 압축 | 모델 입력 한도 안에서 작업을 이어 가도록 과거 내용을 요약하고 필요한 부분을 유지하는 처리입니다. |
| Cargo feature | 컴파일에 포함할 선택 기능과 관련 의존성을 구성하는 스위치입니다. |
| 잠금 파일 | 의존성 해석에서 선택된 구체 버전을 기록한 파일입니다. |
| CI | 변경이 들어왔을 때 빌드·검사·테스트 등을 자동으로 수행하는 과정입니다. |
익숙하지 않은 단어가 많아도 모두 한꺼번에 외울 필요는 없습니다. “입력은 어디로 들어오는가, 상태는 누가 갖는가, 실패하면 어떻게 되돌아오는가”라는 질문에 필요한 용어부터 익히면 됩니다.
설계 문서가 제시하는 목표 구조와 현재 구현은 다를 수 있습니다. 현재 동작을 이해할 때에는 함수의 실제 호출 경로를 함께 살펴보세요.
| 항목 | 분석 기준 정보 |
|---|---|
| 저장소 | GCWing/OpenBitFun |
| 선정 | GitHub Trending Rust Weekly #5 · 2026-10-23 |
| 스타 / 포크 | 3,099 / 278 · 2026-10-23 GitHub 기준 |
| 주 언어 | Rust · 화면은 TypeScript·React 중심 |
| 생성일 | 2026-02-02 |
| 최근 push | 2026-10-23 |
| 최신 릴리스 | v1.0.7 · 2026-10-21 |
| 최근 커밋 | 8c012e4 · 2026-10-23Merge pull request #3535 from bobleer/bob/tab-label-action-space |
| manifest 버전 | 루트 Rust·웹 패키지 모두 1.0.7. 릴리스 태그와 분석 커밋의 코드가 같다는 뜻은 아님 |
| 코드 규모 | Rust 약 108만 줄, TypeScript·TSX 약 72만 줄. 테스트·생성 코드 포함, third_party/ 제외 |
| 패키지 구성 | 루트 Rust 워크스페이스 멤버 54개. 별도 Installer 등은 이 멤버 수에 포함되지 않음 |
| 라이선스 | 핵심 코드 MIT. LICENSE의 저작권 표기: 2026 CWing. 외부 의존성·자산의 별도 조건은 유지 |
코드와 설정의 기준은 커밋 8c012e4입니다. 구현의 존재가 실행·빌드·플랫폼별 동작의 성공을 보장하지는 않습니다.
README의 고성능 표현과 자체 벤치마크를 독립 검증값으로 읽지 마세요. 저장소 크기도 실행 성능이나 품질 점수는 아닙니다.
제품 아키텍처 문서는 일부 기능과 Linux 지원을 부분 구현, Sandbox와 OpenHarmony를 계획으로 구분합니다. 공통 포트가 있다는 사실만으로 모든 화면·원격 경로가 같은 수준으로 완성됐다고 볼 수 없습니다. 로컬 서버 진입점도 공개 다중 사용자 서버 API로 설명하지 않습니다.
README는 1.0.0과 기존 0.2.x 데이터가 호환되지 않는다고 명시합니다. 기존 사용자라면 안내된 마이그레이션 경로를 먼저 확인하세요. 파일·프로세스·외부 도구를 사용할 때는 승인 정책과 실제 실행 권한을 검토하고, MIT와 외부 자산의 라이선스 조건을 구분해야 합니다.