@ahnseungchan/device-farm 0.1.0 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/README.md +213 -122
  3. package/package.json +5 -2
package/CHANGELOG.md CHANGED
@@ -2,6 +2,25 @@
2
2
 
3
3
  `@ahnseungchan/device-farm`의 바뀐 점을 적는다.
4
4
 
5
+ ## [0.1.2] - 2026-11-11
6
+
7
+ ### Docs
8
+
9
+ - README를 영어로 다시 쓰고 webview-kit README 형식(배지·한 줄 소개·데모 GIF·Live demo·Features·Install·FAQ·Contributing)으로 맞춤. Why 표는 빼고 기능·사용법만 둠. npm 패키지 설명을 영어로 바꿈. 한국어 문서는 `docs/README.ko.md`로 옮김
10
+ - `docs/assets/demo.gif` 추가: 데모 화면을 Playwright로 녹화해 ffmpeg로 만든다(`tools/record-demo-gif.ts`)
11
+
12
+ ### Added
13
+
14
+ - 데모 모드(`apps/web/demo/`): 서버·기기 없이 가상 앱 「Sample Shop」 가짜 데이터로 화면을 띄운다. 바꾸는 버튼은 「Demo mode: read-only」 토스트만 띄운다. `npm run demo:dev`·`demo:build`·`demo:preview`(127.0.0.1:5499). npm 패키지에는 들어가지 않는다
15
+ - GitHub Actions: CI(typecheck·test·build·데모 빌드), 데모를 GitHub Pages(https://ahnseungc.github.io/device-farm/)에 배포
16
+
17
+ ## [0.1.1] - 2026-11-11
18
+
19
+ ### Docs
20
+
21
+ - README를 WebViewKit 형식으로 개편: 가운데 정렬 로고·배지·바로가기, 구조 다이어그램(SVG), Why 비교표, 긴 내용은 접기(`<details>`), Contributing
22
+ - 로고·구조 다이어그램을 `docs/assets/`에 추가(npm에서도 보이도록 GitHub 절대 URL로 링크, 패키지에는 넣지 않음)
23
+
5
24
  ## [0.1.0] - 2026-11-11
6
25
 
7
26
  ### Added
package/README.md CHANGED
@@ -1,72 +1,70 @@
1
1
  # device-farm
2
2
 
3
3
  [![npm](https://img.shields.io/npm/v/@ahnseungchan/device-farm)](https://www.npmjs.com/package/@ahnseungchan/device-farm)
4
+ [![CI](https://github.com/Ahnseungc/device-farm/actions/workflows/ci.yml/badge.svg)](https://github.com/Ahnseungc/device-farm/actions/workflows/ci.yml)
4
5
  [![license](https://img.shields.io/badge/license-MIT-blue)](https://github.com/Ahnseungc/device-farm/blob/main/LICENSE)
5
- [![node](https://img.shields.io/node/v/@ahnseungchan/device-farm)](https://nodejs.org/)
6
6
 
7
- **WebView로 만든 모바일 앱을 실기기에서 QA 자동화하는 디바이스팜. 내 프로젝트에 설치하고 `farm.config.ts` 하나로 점검할 앱을 정한다.**
7
+ **A device farm that runs QA on real phones for mobile apps built with WebViews. Install it in your project and describe your app in one `farm.config.ts`.**
8
8
 
9
- Android는 adb + Chrome DevTools Protocol(Playwright), iOS는 Appium(XCUITest·WebDriverAgent)으로 앱 안 WebView를 직접 조작합니다. 기준 화면 비교, 버튼 눌러보기·화면 지도, 플로우 녹화, 카오스 탐색, 정책·피그마 대조를 웹 화면과 CLI로 돌리고, Claude Code가 MCP 도구로 기기를 탐색할 수 있습니다.
9
+ <p align="center">
10
+ <a href="https://ahnseungc.github.io/device-farm/"><img src="https://raw.githubusercontent.com/Ahnseungc/device-farm/main/docs/assets/demo.gif" width="800" alt="device-farm demo: device list, a failed screen compared with its baseline, the screen map, chaos candidate weights, and extracted policy rules" /></a>
11
+ </p>
10
12
 
11
- ## Why
13
+ <p align="center">
14
+ <b><a href="https://ahnseungc.github.io/device-farm/">Live demo →</a></b> (fixture data for a fictional "Sample Shop" app, no server or device needed, read-only)
15
+ </p>
12
16
 
13
- | 손으로 하는 QA | device-farm |
14
- | --- | --- |
15
- | 새 빌드가 나오면 기기마다 깔고 화면을 하나씩 열어 본다 | Firebase App Distribution·TestFlight 새 빌드를 받아 연결된 기기 전부에 깔고, 고정 주소 화면을 딥링크로 열어 기준 화면과 픽셀 비교 |
16
- | 버튼을 다 눌러 봤는지 기억에 의존한다 | 화면의 버튼을 하나씩 눌러 반응(이동·팝업·반응 없음·오류)을 남기고, 결과가 **화면 지도**가 된다 |
17
- | 시나리오 테스트를 코드로 짜고 고친다 | 사람이 기기를 움직이면 녹화해 플로우 후보로 올리고, 확정한 것만 돈다 |
18
- | 뒤로·연타·백그라운드 같은 엣지 케이스는 운에 맡긴다 | 시드 고정 카오스 탐색으로 재현 가능한 무작위 조작 |
19
- | 정책 문서·피그마 문구와 화면이 맞는지 눈으로 대조한다 | Confluence 정책·피그마 문구를 화면 글자와 맞춰 본다 |
20
- | 탐색 자동화가 실제 결제·탈퇴를 누를까 무섭다 | 쓰기 요청 가드(허용 목록 밖 POST/PUT/PATCH/DELETE 차단)와 금지 버튼 목록. 가드를 못 걸면 시작하지 않는다 |
17
+ The app under test is not in the code. Package ids, web origins, bottom tabs, forbidden words, and scenarios are all **injected** through `farm.config.ts`.
18
+ Android is driven with adb + Chrome DevTools Protocol (Playwright), iOS with Appium (XCUITest, WebDriverAgent), both inside the app's WebView.
21
19
 
22
- 점검할 앱은 코드에 없습니다. 패키지 id·웹 주소·하단 탭·금지어·시나리오처럼 앱마다 다른 값은 전부 `farm.config.ts`로 **주입**합니다.
20
+ [한국어 문서](https://github.com/Ahnseungc/device-farm/blob/main/docs/README.ko.md)
23
21
 
24
22
  ## Features
25
23
 
26
- - **빌드 점검**: 새 빌드 감시 → 설치 → 고정 주소 화면 점검. 앱 오류 문구·로그인 튕김·빈 화면·기준 화면 픽셀 비교로 판정
27
- - **버튼 눌러보기·화면 지도**: 버튼별 반응을 기록하고, 화면 간 이동을 지도로 쌓는다. 지도 블록을 이어 붙여 플로우를 만든다
28
- - **플로우**: 홈에서 버튼·링크만 눌러 가는 시나리오. 녹화, 화면 지도 조립, PRD로 LLM 후보 생성(선택)
29
- - **데이터 QA**: 화면이 부르는 GET API를 다시 불러 합계·표시 값을 규칙으로 맞춰 본다(LLM 없음)
30
- - **탐색·카오스 탐색**: 너비 우선 탐색과 시드 고정 카오스(뒤로·연타·로딩 중 뒤로·백그라운드·재시작)
31
- - **정책·피그마 대조**: Confluence 정책 바뀜 감지, 규칙 뽑기, 피그마 문구 대조
32
- - **원격 조작**: 웹 화면에서 기기 화면을 보며(Android H.264, iOS MJPEG) 직접 누른다. 조작 중인 기기는 잠근다
33
- - **Claude Code MCP**: `device_start`·`device_look`·`device_press`… 도구로 Claude가 기기를 탐색하고, 찾은 경로를 플로우 후보로 올린다
24
+ - **Build checks**: watch for new builds → install → check fixed screens. Fails on app error text, login bounce, blank screens, or a pixel diff against the baseline.
25
+ - **Button check and screen map**: records how every button reacts and stitches screens together into a map. Build flows by chaining map blocks.
26
+ - **Flows**: scenarios that start at home and move only by tapping buttons and links. Record them, assemble them from the map, or generate candidates from a PRD with an LLM (optional).
27
+ - **Data QA**: re-calls the GET APIs a screen uses and checks totals and displayed values with plain rules (no LLM).
28
+ - **Exploration and chaos**: breadth-first exploration, plus seeded chaos (back, double tap, back while loading, background, restart).
29
+ - **Policy and Figma checks**: detects Confluence policy changes, extracts rules, and compares Figma copy.
30
+ - **Remote control**: watch the device screen in the browser (Android H.264, iOS MJPEG) and tap it yourself. A device you control is locked for others.
31
+ - **Claude Code (MCP)**: Claude explores the device through MCP tools and turns the paths it finds into flow candidates.
34
32
 
35
- ## 준비물
33
+ ## Install
36
34
 
37
- | 무엇 | 언제 필요 |
38
- | --- | --- |
39
- | macOS, Node.js 22 이상 | 항상 |
40
- | `adb`(Android platform-tools), USB 디버깅을 켠 기기 | Android |
41
- | WebView 디버깅을 켠 dev 빌드: `WebView.setWebContentsDebuggingEnabled(true)` | Android |
42
- | Xcode, Appium + XCUITest 드라이버(WebDriverAgent), `isInspectable = true`인 빌드 | iOS |
43
- | Firebase 서비스 계정, App Store Connect API 키 | 새 빌드 자동 받기(선택) |
44
- | Confluence API 토큰, Figma 토큰 | 정책·피그마 대조(선택) |
35
+ ```bash
36
+ npm install -D @ahnseungchan/device-farm
37
+ ```
45
38
 
46
- 점검할 앱이 맞춰야 할 것:
39
+ | Requirement | When |
40
+ | --------------------------------------------------------------------------------- | ---------------------------- |
41
+ | macOS, Node.js 22+ | Always |
42
+ | `adb` (Android platform-tools) and a device with USB debugging on | Android |
43
+ | A dev build with WebView debugging: `WebView.setWebContentsDebuggingEnabled(true)` | Android |
44
+ | Xcode, Appium + XCUITest driver (WebDriverAgent), a build with `isInspectable = true` | iOS |
45
+ | Firebase service account, App Store Connect API key | Auto-installing new builds (optional) |
46
+ | Confluence API token, Figma token | Policy and Figma checks (optional) |
47
47
 
48
- 1. **WebView 디버깅**이 켜진 빌드. 디바이스팜은 이 디버깅 소켓으로 들어간다.
49
- 2. **경로형 딥링크**: `<scheme>://<웹 경로>`를 받으면 그 웹 경로를 WebView로 연다.
50
- 3. **하단 탭이 웹 링크**(`a[href]` 또는 `role=link`)이고, 화면 상단 뒤로 버튼에 이름(`aria-label`)이 있다.
51
- 4. 점검용 계정으로 로그인해 둔다. 로그인 화면으로 튕기면 「로그인 필요」로 남는다.
48
+ Host Mac and device setup: [docs/operations.md](https://github.com/Ahnseungc/device-farm/blob/main/docs/operations.md) (Korean)
52
49
 
53
- 호스트 맥·기기 준비는 [docs/operations.md](https://github.com/Ahnseungc/device-farm/blob/main/docs/operations.md)에 있습니다.
50
+ <details>
51
+ <summary><b>What the app under test needs</b></summary>
54
52
 
55
- ## 설치
53
+ 1. A build with **WebView debugging** on. device-farm connects through that debugging socket.
54
+ 2. **Path deep links**: `<scheme>://<web path>` opens that web path in the WebView.
55
+ 3. **Bottom tabs are web links** (`a[href]` or `role=link`), and the in-page back button has a name (`aria-label`).
56
+ 4. A test account that is already signed in. If a screen bounces to login, it is reported as "login required".
56
57
 
57
- ```bash
58
- npm install -D @ahnseungchan/device-farm
59
- ```
58
+ </details>
60
59
 
61
60
  ## Quick start
62
61
 
63
62
  ```bash
64
- npx device-farm init # farm.config.ts, .env.example 생성(이미 있으면 건너뜀)
65
- # farm.config.ts를 내 앱 값으로 고친다. 토큰이 필요하면 .env.example → .env
66
- npx device-farm dev # http://127.0.0.1:5173 (--port 로 변경)
63
+ npx device-farm init # creates farm.config.ts and .env.example (skips files that exist)
64
+ npx device-farm dev # http://127.0.0.1:5173 (change with --port)
67
65
  ```
68
66
 
69
- `init`이 만드는 설정은 가상의 쇼핑 앱 「Sample Shop」 예시입니다. 필수 값만 남기면 이 정도입니다.
67
+ Edit the `farm.config.ts` that `init` created. With only the required fields it looks like this:
70
68
 
71
69
  ```ts
72
70
  // farm.config.ts
@@ -77,119 +75,212 @@ export default defineAppConfig({
77
75
  appIds: { dev: "com.example.app.dev", prod: "com.example.app" },
78
76
  web: { dev: "https://dev.app.example.com", prod: "https://app.example.com" },
79
77
  homePath: "/home",
80
- homeTabName: "홈",
81
- tabPaths: { 검색: "/search", 마이: "/my" },
78
+ homeTabName: "Home",
79
+ tabPaths: { Search: "/search", My: "/my" },
82
80
  deepLink: { scheme: "myapp", homeHost: "home" },
83
81
  });
84
82
  ```
85
83
 
86
- `.gitignore`에 `.env`와 `test-results/`를 넣어 두세요. 결과(실행 기록·스크린샷·기준 화면·화면 지도)는 전부 작업 폴더의 `test-results/`에 쌓입니다.
87
-
88
- ## 앱 설정 (`farm.config.ts`)
89
-
90
- `defineAppConfig()`가 빠진 값을 기본값으로 채웁니다. 타입은 `AppConfigInput`·`AppConfig`로 패키지에서 가져올 수 있습니다.
91
-
92
- | 필드 | 필수 | 설명 |
93
- | --- | --- | --- |
94
- | `name` | ✓ | 앱 이름. 화면 제목·프롬프트에 쓴다 |
95
- | `appIds` | ✓ | `{ dev, prod }` Android 패키지 = iOS 번들 id. 다르면 `iosBundleIds` |
96
- | `homePath`·`homeTabName`·`tabPaths` | ✓ | 홈 경로, 홈 탭 이름, 하단 탭 이름 → 경로 |
97
- | `web` | | `{ dev, prod }` WebView 웹 주소. 비면 `FARM_WEB_ORIGIN_DEV`·`FARM_WEB_ORIGIN_PROD` |
98
- | `apiOrigin` | | API 호스트(웹과 다를 때). 가드가 내부로 보고 데이터 QA가 부른다 |
99
- | `inPageBackNames` | | 화면 상단 뒤로 버튼 이름. 기본 `["뒤로가기", "Back"]` |
100
- | `loginPaths`·`appErrorText` | | 로그인 화면 경로, 앱 오류 문구. 기본 `["/login"]`, `"Application error"` |
101
- | `deepLink` | | `{ scheme, homeHost }` 경로형 딥링크 |
102
- | `android.mainActivity` | | WebView 액티비티. 다른 네이티브 화면이 위에 있으면 뒤로 키로 닫는다 |
103
- | `webview` | | `stackResetScript`(WebView 스택 닫기 브리지), `sheetCloseName`(시트 닫기 버튼) |
104
- | `guard` | | 기본 금지어·금지 API에 더할 `extraForbiddenButtonWords`·`extraForbiddenClickNames`·`extraForbiddenApiPatterns`, 늘 통과시킬 `pushTokenRequests`, 누르지 않을 `avoidedNamePattern`·`noPressPathPattern` |
105
- | `scenarios`·`excludedScenarioPaths` | | 고정 주소 화면 점검 목록과 뺄 경로 |
106
- | `qa` | | `{ screens, sessionPath }` 데이터 QA 화면·규칙, 로그인 세션 경로 |
107
- | `policy.confluence` | | `{ cloudId, spaceKey, titleQuery, siteUrl }` 정책 정본 |
108
- | `figma` | | `{ frontRepo, ref, appDir }` 화면 → 피그마 노드를 뽑을 프론트 체크아웃 |
109
- | `install` | | `sources`(플랫폼·빌드별 `firebase`/`testflight`), `firebase`(앱 id), `testflightDevAppName` |
110
- | `ios` | | `{ teamId, wdaBundleId }` WDA 서명. 비면 `IOS_TEAM_ID`·`IOS_WDA_BUNDLE_ID` |
111
- | `access.allowedDomains` | | 화면에 들어올 수 있는 Tailscale 계정 도메인. 비면 이 맥에서만 |
112
-
113
- 데이터 QA 규칙용 도우미 `equalWon`·`formatWon`·`numberAt`·`textHas`·`pick`도 패키지 루트에서 내보냅니다.
114
-
115
- 설정 파일은 `FARM_CONFIG` → 작업 폴더 `farm.config.ts` → `farm.config.example.ts` 순으로 찾습니다. 비밀 값과 이 맥에만 해당하는 값은 작업 폴더 `.env`에 둡니다(항목은 `.env.example` 참고).
84
+ If you need tokens, copy `.env.example` to `.env` and fill it in. Add `.env` and `test-results/` to `.gitignore`.
85
+ Everything the farm produces (run history, screenshots, baselines, the screen map) goes to `test-results/` in your working directory.
86
+
87
+ <details>
88
+ <summary><b>Data QA rule example</b></summary>
89
+
90
+ ```ts
91
+ import { defineAppConfig, formatWon, numberAt, textHas } from "@ahnseungchan/device-farm";
92
+
93
+ export default defineAppConfig({
94
+ // ...required fields
95
+ apiOrigin: "https://api-dev.app.example.com",
96
+ qa: {
97
+ sessionPath: "/api/auth/session",
98
+ screens: [
99
+ {
100
+ path: "/cart",
101
+ name: "Cart",
102
+ apis: { cart: "/v1/users/{userId}/cart" },
103
+ rules: [
104
+ {
105
+ id: "total-shown",
106
+ title: "The total from the API is shown on screen",
107
+ check: (context) => {
108
+ const shown = `${formatWon(numberAt(context, "cart", "totalWon"))}원`;
109
+ return textHas(context.text, shown) ? null : `Not on screen: total "${shown}"`;
110
+ },
111
+ },
112
+ ],
113
+ },
114
+ ],
115
+ },
116
+ });
117
+ ```
118
+
119
+ Run it with `npx device-farm qa --device <id> [--path /cart]`. The full example is in the `farm.config.ts` that `init` creates.
120
+
121
+ </details>
122
+
123
+ ## Configuration
124
+
125
+ `defineAppConfig()` fills in defaults for anything you leave out. The `AppConfigInput` and `AppConfig` types are exported from the package.
126
+ The config file is resolved as `FARM_CONFIG` → `farm.config.ts` in the working directory → `farm.config.example.ts`. Secrets are read from `.env` in the working directory.
127
+
128
+ | Field | Required | What it is |
129
+ | ----------------------------------- | -------- | ---------------------------------------------------------------------------------- |
130
+ | `name` | ✓ | App name, used in the UI title and prompts |
131
+ | `appIds` | ✓ | `{ dev, prod }` Android package = iOS bundle id. Use `iosBundleIds` if they differ |
132
+ | `homePath`, `homeTabName`, `tabPaths` | ✓ | Home path, home tab name, and bottom tab name → path |
133
+ | `web` | | `{ dev, prod }` WebView origins. Falls back to `FARM_WEB_ORIGIN_DEV` / `FARM_WEB_ORIGIN_PROD` |
134
+ | `apiOrigin` | | API host when it differs from the web origin. The guard treats it as internal, and data QA calls it |
135
+ | `deepLink` | | `{ scheme, homeHost }` for path deep links |
136
+ | `guard` | | Words and API patterns **added** to the built-in deny lists, and `pushTokenRequests` that always pass |
137
+ | `scenarios`, `excludedScenarioPaths` | | Fixed screens to check, and paths to skip |
138
+
139
+ <details>
140
+ <summary><b>All fields</b></summary>
141
+
142
+ | Field | What it is |
143
+ | --------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
144
+ | `inPageBackNames` | Names of the in-page back button. Default `["뒤로가기", "Back"]` |
145
+ | `loginPaths`, `appErrorText` | Login screen paths and the app's error text. Default `["/login"]`, `"Application error"` |
146
+ | `android.mainActivity` | The WebView activity. If another native screen is on top, it is closed with the back key |
147
+ | `webview` | `stackResetScript` (bridge call that closes the WebView stack), `sheetCloseName` (bottom sheet close button) |
148
+ | `guard` | `extraForbiddenButtonWords`, `extraForbiddenClickNames`, `extraForbiddenApiPatterns`, `pushTokenRequests`, and `avoidedNamePattern` / `noPressPathPattern` to never press |
149
+ | `qa` | `{ screens, sessionPath }`: data QA screens and rules, and the signed-in session path |
150
+ | `policy.confluence` | `{ cloudId, spaceKey, titleQuery, siteUrl }`: where the policy docs live |
151
+ | `figma` | `{ frontRepo, ref, appDir }`: the front-end checkout used to map screens to Figma nodes |
152
+ | `install` | `sources` (`firebase` / `testflight` per platform and build), `firebase` (app ids), `testflightDevAppName` |
153
+ | `ios` | `{ teamId, wdaBundleId }` for WDA signing. Falls back to `IOS_TEAM_ID` / `IOS_WDA_BUNDLE_ID` |
154
+ | `access.allowedDomains` | Tailscale account domains allowed to open the UI. Empty means this Mac only |
155
+
156
+ The data QA helpers `equalWon`, `formatWon`, `numberAt`, `textHas`, and `pick` are exported from the package root. Environment variables are listed in `.env.example`.
157
+
158
+ </details>
116
159
 
117
160
  ## CLI
118
161
 
119
- 모든 명령은 작업 폴더(내 프로젝트) 기준으로 설정·`.env`를 읽고 결과를 씁니다. `npx device-farm <명령> --help`로 옵션을 볼 수 있습니다.
162
+ Every command reads config and `.env` from the working directory (your project) and writes results there. Run `npx device-farm <command> --help` for options.
163
+
164
+ | Command | What it does |
165
+ | ---------------------------------------------------- | -------------------------------------------------------------------- |
166
+ | `device-farm init` | Create `farm.config.ts` and `.env.example` |
167
+ | `device-farm dev [--port 5173]` | UI + API server on 127.0.0.1. New-build and policy watchers run with it |
168
+ | `device-farm check --build dev\|prod --version 2.1.5` | Ask the running server for a build check and print progress until it ends |
169
+ | `device-farm explore` | Exploration check (needs the server) |
170
+ | `device-farm chaos --device <serial>` | Chaos exploration (Android dev) |
171
+ | `device-farm record --device <serial>` | Record a person's actions → screen map and flow candidates |
172
+ | `device-farm qa --device <id>` | Data QA only |
173
+ | `device-farm policy-watch` | Detect policy changes, extract rules, cache Figma copy |
174
+ | `device-farm policy-check --device <id>` | Policy and Figma comparison on one device |
175
+ | `device-farm explore-allowlist` | Add write APIs from OpenAPI (`FARM_OPENAPI_URL`) to the allowlist as deny |
176
+ | `device-farm mcp` | MCP server for Claude Code (stdio) |
177
+
178
+ <details>
179
+ <summary><b>Options per command</b></summary>
120
180
 
121
- | 명령 | 내용 |
122
- | --- | --- |
123
- | `device-farm init` | `farm.config.ts`·`.env.example` 생성 |
124
- | `device-farm dev [--port 5173]` | 화면 + API 서버(127.0.0.1). 새 빌드 감시·정책 감시가 함께 돈다 |
125
- | `device-farm check --build dev\|prod --version 2.1.5 [--devices all\|id,id] [--port 5173]` | 떠 있는 서버에 빌드 점검을 시키고 끝날 때까지 진행을 출력 |
126
- | `device-farm explore [--devices all\|id,id] [--build dev] [--port 5173]` | 탐색 점검(서버 필요) |
127
- | `device-farm chaos --device <serial> [--seed 42] [--episodes 3] [--steps 50] [--minutes 60] [--no-guard]` | 카오스 탐색(Android dev) |
128
- | `device-farm record --device <serial> [--seconds 300] [--name "플로우 이름"]` | 사람 조작 녹화 → 화면 지도·플로우 후보 |
129
- | `device-farm qa --device <id> [--path /cart]` | 데이터 QA만 |
130
- | `device-farm policy-watch [--extract <pageId>] [--figma]` | 정책 바뀜 감지·규칙 뽑기·피그마 문구 |
131
- | `device-farm policy-check --device <id> [--path /my/orders]` | 정책·피그마 대조를 기기 한 대에서 |
132
- | `device-farm explore-allowlist` | OpenAPI(`FARM_OPENAPI_URL`)의 쓰기 API를 `explore/allowlist.json`에 deny로 추가 |
133
- | `device-farm mcp` | Claude Code용 MCP 서버(stdio) |
181
+ ```bash
182
+ device-farm dev [--port 5173]
183
+ device-farm check --build dev|prod --version 2.1.5 [--devices all|id,id] [--port 5173]
184
+ device-farm explore [--devices all|id,id] [--build dev] [--port 5173]
185
+ device-farm chaos --device <Android serial> [--seed 42] [--episodes 3] [--steps 50] [--minutes 60] [--no-guard]
186
+ device-farm record --device <Android serial> [--seconds 300] [--name "flow name"]
187
+ device-farm qa --device <id> [--path /cart]
188
+ device-farm policy-watch [--extract <pageId>] [--figma]
189
+ device-farm policy-check --device <id> [--path /my/orders]
190
+ ```
134
191
 
135
- 탐색 허용 목록 `explore/allowlist.json`, 입력칸 테스트 값 `explore/form-values.json`, 정책 등록 `policy/sources.json`은 작업 폴더에 두고 커밋합니다. 허용 목록이 없으면 모든 쓰기 요청을 막습니다.
192
+ `check` and `explore` send work to a running `dev` server. If you changed the port, pass the same `--port`.
193
+ Keep the exploration allowlist `explore/allowlist.json`, input test values `explore/form-values.json`, and policy sources `policy/sources.json` in your working directory and commit them. Without an allowlist, every write request is blocked.
136
194
 
137
- ## Claude Code (MCP)
195
+ </details>
138
196
 
139
- 내 프로젝트 폴더에서 MCP 서버를 등록합니다.
197
+ ## Claude Code (MCP)
140
198
 
141
199
  ```bash
200
+ # in your project folder
142
201
  claude mcp add --scope project device-farm -- npx device-farm mcp
143
202
  ```
144
203
 
145
- 도구 흐름: `device_start` → `device_look` / `device_press` / `device_go_home` / `device_report_issue` → `device_finish`. 끝나면 화면 지도에 합치고, 홈부터 다시 돌 수 있는 경로면 플로우 후보로 올립니다. 보고서는 `test-results/agent/<시각>/report.json`. 탐색은 Android dev 빌드에서 쓰기 요청 가드를 켠 채로만 돕니다.
204
+ - Tool flow: `device_start` → `device_look` / `device_press` / `device_go_home` / `device_report_issue` → `device_finish`.
205
+ - When it finishes, results are merged into the screen map, and paths that can be replayed from home become flow candidates. The report is written to `test-results/agent/<time>/report.json`.
206
+ - Exploration runs only on Android dev builds with the write-request guard on. It runs on your Claude Code account; no API key is needed.
146
207
 
147
- 이 저장소는 Claude Code 플러그인(`.claude-plugin`, `skills/run-check`·`chaos-explore`·`policy-figma-check`)으로도 쓸 수 있습니다. 저장소를 받아 `npm install`한 뒤 플러그인으로 추가하세요.
208
+ This repository also works as a Claude Code plugin (`.claude-plugin`, skills `run-check`, `chaos-explore`, `policy-figma-check`). Clone it, run `npm install`, and add it as a plugin.
148
209
 
149
- ## 안전
210
+ ## Safety
150
211
 
151
- - 탐색·버튼 눌러보기·카오스·MCP 탐색은 **Android dev 빌드**에서, 쓰기 요청 가드를 켠 채로 돈다. 가드를 못 걸면 시작하지 않는다(fail closed).
152
- - 가드 판단 순서: 읽기(GET·HEAD·OPTIONS) 통과 → `pushTokenRequests` 통과 → 금지 경로 차단 → 허용 목록 allow 통과 → deny·모름 차단.
153
- - 기본 금지 버튼(구매·결제·매도·매수·송금·출금·탈퇴·주문·환불·해지·삭제·로그아웃 등)은 목록에서 빠지고, 눌러도 거절한다. 앱 설정은 금지어를 **더할** 뿐 빼지 못한다. 비밀번호·PIN 화면은 누르지 않고 나가기만 한다.
154
- - `chaos --no-guard`는 쓰기 요청을 막지 않는다. **dev 서버에서만** 쓴다. 금지 버튼은 그대로 누르지 않는다.
155
- - 화면 서버는 127.0.0.1에만 열린다. 밖에서는 `tailscale serve`로만 들어오고, `access.allowedDomains`로 계정 도메인을 제한한다.
212
+ - Exploration, button checks, chaos, and MCP exploration run on **Android dev builds** with the write-request guard on. If the guard cannot attach, they do not start (fail closed).
213
+ - Guard order: reads (GET, HEAD, OPTIONS) pass → `pushTokenRequests` pass → forbidden paths are blocked → allowlist `allow` passes → `deny` and unknown are blocked.
214
+ - Built-in forbidden buttons (buy, pay, sell, transfer, withdraw, delete account, order, refund, cancel, delete, log out, and more) are removed from the candidate list and refused if pressed. App config can only add forbidden words, never remove them. Password and PIN screens are never pressed, only left.
215
+ - `chaos --no-guard` does not block write requests. Use it **only against a dev server**. Forbidden buttons are still never pressed.
216
+ - The UI server listens on 127.0.0.1 only. Remote access goes through `tailscale serve`, limited to `access.allowedDomains`.
156
217
 
157
218
  ## FAQ
158
219
 
159
- **기기 없이 화면만 열어 볼 수 있나요?**
160
- 네. `npx device-farm init && npx device-farm dev`면 예시 설정으로 화면이 열립니다. 기기 목록은 비어 있고 점검은 돌지 않습니다.
220
+ <details>
221
+ <summary><b>Can I open the UI without a device?</b></summary>
222
+
223
+ Yes. `npx device-farm init && npx device-farm dev` opens the UI with the example config. The device list is empty and no checks run.
224
+ To see the UI filled with data, open the [live demo](https://ahnseungc.github.io/device-farm/).
225
+
226
+ </details>
227
+
228
+ <details>
229
+ <summary><b>Does iOS support exploration and chaos?</b></summary>
230
+
231
+ Build checks and remote control support iOS (Appium). Exploration, chaos, recording, and MCP exploration support Android dev builds only for now.
161
232
 
162
- **iOS도 탐색·카오스가 되나요?**
163
- 빌드 점검·원격 조작은 iOS(Appium)를 지원합니다. 탐색·카오스·녹화·MCP 탐색은 지금 Android dev 빌드만 지원합니다.
233
+ </details>
164
234
 
165
- **`check`가 서버에 연결할 수 없다고 합니다.**
166
- `check`·`explore`는 떠 있는 `device-farm dev` 서버에 작업을 시킵니다. 다른 터미널에서 `dev`를 먼저 켜고, 포트를 바꿨다면 같은 `--port`를 넘기세요.
235
+ <details>
236
+ <summary><b>Why is the UI in Korean?</b></summary>
167
237
 
168
- **LLM 키가 꼭 필요한가요?**
169
- 아니요. 점검·탐색·카오스·데이터 QA는 규칙으로만 판정합니다. PRD로 플로우 후보를 만들 때(`FLOW_LLM_*`)와 정책 판정에만 씁니다. MCP는 사용자의 Claude Code 계정으로 돕니다.
238
+ It was built for a Korean team first. The CLI help and UI text are Korean for now; an English UI PR is welcome.
170
239
 
171
- **결과 파일은 어디에 생기나요?**
172
- 작업 폴더의 `test-results/`. 커밋하지 마세요.
240
+ </details>
173
241
 
174
- ## 문서
242
+ <details>
243
+ <summary><b><code>check</code> says it cannot reach the server.</b></summary>
175
244
 
176
- - [운영: 호스트 맥과 기기 준비](https://github.com/Ahnseungc/device-farm/blob/main/docs/operations.md)
177
- - [설계: 빌드 점검·기준 화면·플로우·화면 지도](https://github.com/Ahnseungc/device-farm/blob/main/docs/design-device-farm.md)
178
- - [설계: 탐색 점검과 쓰기 요청 가드](https://github.com/Ahnseungc/device-farm/blob/main/docs/design-explore.md)
179
- - [설계: 카오스 탐색](https://github.com/Ahnseungc/device-farm/blob/main/docs/design-chaos.md)
180
- - [설계: 정책·피그마 대조](https://github.com/Ahnseungc/device-farm/blob/main/docs/design-policy-figma.md)
245
+ `check` and `explore` send work to a running `device-farm dev` server. Start `dev` in another terminal first, and pass the same `--port` if you changed it.
181
246
 
182
- ## 개발
247
+ </details>
248
+
249
+ <details>
250
+ <summary><b>Do I need an LLM API key?</b></summary>
251
+
252
+ No. Checks, exploration, chaos, and data QA are judged by rules only. An LLM is used only to generate flow candidates from a PRD (`FLOW_LLM_*`) and to judge policy behavior rules.
253
+
254
+ </details>
255
+
256
+ ## Docs
257
+
258
+ Design notes are in Korean for now.
259
+
260
+ - [Operations: host Mac and device setup](https://github.com/Ahnseungc/device-farm/blob/main/docs/operations.md)
261
+ - [Design: build checks, baselines, flows, screen map](https://github.com/Ahnseungc/device-farm/blob/main/docs/design-device-farm.md)
262
+ - [Design: exploration and the write-request guard](https://github.com/Ahnseungc/device-farm/blob/main/docs/design-explore.md)
263
+ - [Design: chaos exploration](https://github.com/Ahnseungc/device-farm/blob/main/docs/design-chaos.md)
264
+ - [Design: policy and Figma checks](https://github.com/Ahnseungc/device-farm/blob/main/docs/design-policy-figma.md)
265
+
266
+ ## Contributing
267
+
268
+ Issues and PRs are welcome. Bug reports with a repro, docs and translations, and demo improvements all help.
183
269
 
184
270
  ```bash
185
271
  git clone https://github.com/Ahnseungc/device-farm.git && cd device-farm
186
272
  npm install
187
- npm run dev # 화면 개발(vite dev). 저장소 루트 farm.config.ts → farm.config.example.ts
188
- npm run typecheck && npm test
189
- npm run build # dist/: bin·설정 타입(tsup), 화면(vite build), init 템플릿
273
+ npm run typecheck && npm test && npm run build
274
+ npm run dev # UI development (vite dev), config: farm.config.ts → farm.config.example.ts
275
+ npm run demo:dev # demo UI with fixture data at http://127.0.0.1:5499
190
276
  ```
191
277
 
192
- npm workspaces 모노레포입니다. 의존 방향은 `core ← devices ← engine ← server ← apps`이고 `tools/boundaries.test.ts`가 지킵니다. 배포 패키지는 워크스페이스를 `dist/`에 묶어 냅니다.
278
+ It is an npm workspaces monorepo. Dependencies point one way, `core ← devices ← engine ← server ← apps`, enforced by `tools/boundaries.test.ts`. The published package bundles the workspaces into `dist/` (tsup + vite build).
279
+
280
+ The demo (`apps/web/demo/`) swaps only `./api-client` and `./live-view` for fixture modules and reuses the real screens; it is not part of the package build.
281
+ `npm run demo:build && npx tsx tools/record-demo-gif.ts` regenerates `docs/assets/demo.gif` (needs ffmpeg). Pushing to `main` deploys the demo to GitHub Pages via `.github/workflows/pages.yml`.
282
+
283
+ Releases: bump `package.json` and `CHANGELOG.md`, run `npm run publish:npm` (`prepublishOnly` runs typecheck, test, and build), then push a `v<version>` tag.
193
284
 
194
285
  ## License
195
286
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@ahnseungchan/device-farm",
3
- "version": "0.1.0",
4
- "description": "실기기 WebView 앱 QA 자동화 디바이스팜. Android(adb+CDP)·iOS(Appium) 기기에서 기준 화면 비교, 버튼 눌러보기·화면 지도, 플로우 녹화, 카오스 탐색, 정책·피그마 대조를 돌리고 Claude Code MCP로 연결한다.",
3
+ "version": "0.1.2",
4
+ "description": "A device farm that runs QA on real phones for WebView-based mobile apps: baseline screen diffs, button crawl and screen map, flow recording, seeded chaos exploration, policy/Figma copy checks, and a Claude Code MCP server. Configure your app in one farm.config.ts.",
5
5
  "type": "module",
6
6
  "bin": {
7
7
  "device-farm": "dist/bin.js"
@@ -21,6 +21,9 @@
21
21
  ],
22
22
  "scripts": {
23
23
  "dev": "vite --config apps/web/vite.config.ts --configLoader runner",
24
+ "demo:dev": "vite --config apps/web/demo/vite.config.ts --configLoader runner",
25
+ "demo:build": "vite build --config apps/web/demo/vite.config.ts --configLoader runner",
26
+ "demo:preview": "vite preview --config apps/web/demo/vite.config.ts --configLoader runner",
24
27
  "build": "rm -rf dist && tsup && vite build --config apps/web/vite.config.ts --configLoader runner --outDir ../../dist/web --emptyOutDir && tsx tools/build-templates.ts",
25
28
  "test": "for f in $(find packages apps tools -name '*.test.ts' -not -path '*/node_modules/*'); do npx tsx \"$f\" || exit 1; done",
26
29
  "typecheck": "tsc --noEmit",