Commit aa221aa
feat(rn-device): in-tree Android UIAutomator runner (MVP) (#165)
* docs(rn-device): reflect iOS-MVP runtime split — rn-fast-runner (iOS) + agent-device (Android)
Six docs updated to match the iOS-MVP architecture from PR #164 / D1219:
1. **skills/rn-setup/SKILL.md** (source of truth for /doctor + /setup
Phase 1) — split check #3 into "3. rn-fast-runner (iOS — in-tree
XCTest rig)" and "3b. agent-device CLI (Android — optional on
iOS-only setups)". New check verifies the xcodeproj + build
artifacts, surfaces the one-time `xcodebuild build-for-testing`
command on NEEDS_BUILD. Updated 14-row output table, common
rationalizations (banner WARNING no longer critical on iOS),
verification checklist. RN_DEVICE_KILL_LEGACY=1 documented as the
recommended iOS-only setting for stale daemons.
2. **commands/doctor.md** — frontmatter + body now mention both
rn-fast-runner (iOS) and agent-device (Android). Doctor delegates
to the rn-setup skill so most of the surface is inherited;
doctor.md just keeps the frontmatter / overview accurate.
3. **commands/setup.md** — Phase 1 abort-thresholds list split:
"CRITICAL" no longer hardcodes `agent-device` — instead lists the
platform-specific device-control row (rn-fast-runner on macOS+iOS
targets OR agent-device on Android targets). N/A rows for the
off-platform runtime are documented as OPTIONAL.
4. **CLAUDE.md** (plugin) — Prerequisites, Troubleshooting,
Architecture, Device tools, Key Technical Decisions sections all
updated. Architecture table now has separate rows for iOS
(in-tree rn-fast-runner /command HTTP) and Android (agent-device
CLI). Troubleshooting added rows for "no .xctestrun at expected
path" + "legacy AgentDeviceRunner re-appears". iOS-only quirks
list under Device tools documents the type-timeout shim and the
TS orchestrator routing for find/scrollintoview.
5. **README.md** — Setup prerequisites table now lists rn-fast-runner
(iOS) + agent-device (Android) as separate platform-conditional
rows. /doctor description bumped from 12-row to 14-row.
Architecture ASCII diagram split iOS/Android device-interaction
paths. Troubleshooting added three new rows: stale
AgentDeviceRunner respawn, rn-fast-runner build artifacts missing,
iOS device_fill timeout shim.
6. **CLAUDE-MD-TEMPLATE.md** (user-injected) — new "iOS Device
Runtime — In-tree rn-fast-runner" subsection between Multi-Device
Setups and Required Dev Setup. Documents the runtime split so
user agents working in projects with the template injected know
to ignore agent-device warnings on iOS, what RN_DEVICE_KILL_LEGACY
does, and how to interpret device_fill's runnerTimeoutShim meta
marker.
No code changes — pure documentation alignment with the runtime
already shipped in 8345f4b / 7f76411.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* feat(rn-device): scaffold in-tree Android UIAutomator runner
Greenfield Gradle Android instrumentation project at
scripts/rn-android-runner/ mirroring the iOS rn-fast-runner architecture
(PR #164). Long-lived @LargeTest mainLoop() runs under `am instrument`,
embeds NanoHTTPD on port 22089, dispatches the same POST /command JSON
contract as the iOS runner. Kotlin 2.0.21 / AGP 8.7.3 / UIAutomator 2.3.0
/ NanoHTTPD 2.3.1 / minSdk 23 / Java 17.
Configurator.setWaitForIdleTimeout(0) intentionally disables UIAutomator's
global idle wait — RN main thread never reports quiescence when
Reanimated/RAF worklets are active. Mirrors iOS's
withTemporaryScrollIdleTimeoutIfSupported shim.
CommandDispatcher covers all MVP verbs: snapshot, tap/press, type/fill,
drag/swipe/scroll, screenshot, back, dismissKeyboard, longPress, pinch,
findText. `find` is intentionally NOT dispatched here — TS-side
device_find is a snapshot-based orchestrator on Android too (mirrors iOS,
D1217 symmetry).
This is greenfield code (no MIT upstream vendoring) — see plan §File
Structure for the rationale vs iOS's rn-fast-runner import.
Task 1 of 11 per docs/superpowers/plans/2026-05-16-rn-android-runner-mvp-plan.md.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* fix(rn-device): Task 1 post-review hardening
Apply 6 code-quality review findings from the rn-android-runner Task 1
implementation:
1. (security MUST_FIX) Remove unsafe shell-fallback from type(). When no
text input has keyboard focus, throw NoFocusedInputException →
structured {code: NO_FOCUSED_INPUT} response instead of executing
`input text $escaped` via UiAutomation.executeShellCommand. The
2-line escaper only neutralized spaces + single-quotes; injection
vectors `;` `&` `|` backtick `$(…)` `>` `<` `\n` were all unguarded.
3. Wrap snapshot XML parser in try/catch; throw SnapshotParseException
→ {code: SNAPSHOT_PARSE_FAILED} instead of opaque 500.
6. Catch InterruptedException in mainLoop for clean shutdown; add
@after stopServer() so NanoHTTPD's socket releases without
TIME_WAIT lingering across test reruns.
7. Wrap uiObjectToJson in findText with StaleObjectException catch;
return {found: false, stale: true} when the UiObject2 was recycled
between query and serialization (common on RN's re-render-heavy
screens).
8. Correct findByTextOrId regex anchoring: `.*(:id/)?$safe$` matched
any id ending in the query (e.g. `submit` matched `cancel_submit`).
Now `^[^:]+:id/$safe$` requires the `:id/` separator and anchors
the query as the complete id-name suffix.
9. Add MIT SPDX license header to every Kotlin file under
app/src/androidTest/, per the plan's File Structure note.
Deferred to a later task (out of scope for this commit):
- [2] foreground() launcher-after-back-press edge case
- [4] pinch minimum ratio threshold
- [5] error message stack/cause chain enrichment
- [10]-[14] NIT-level refactors (magic constants, version catalog, etc.)
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* test(rn-device): pin Android foreground snapshot regression
SnapshotForegroundRegressionTest is the Android equivalent of iOS's
B155 regression test (rn-fast-runner/.../SnapshotForegroundRegressionTest.swift).
It activates com.rndevagent.testapp via the launcher intent, waits up
to 10s for the package to foreground, then calls CommandDispatcher.snapshot()
and asserts:
- The flat JSON snapshot contains "tab-home" (a stable testID in
test-app's bottom tab bar).
- The snapshot does NOT contain "rn-dev-agent Android runner" (the
runner's own app label), proving foreground() correctly activated
the target package and the dispatcher read its hierarchy, not the
runner's own UI.
This pins the same invariant on Android that B155 fixed on iOS: the
runner must always read the target app's accessibility tree per
request, regardless of which app foregrounded before the call.
Task 2 of 11 per docs/superpowers/plans/2026-05-16-rn-android-runner-mvp-plan.md.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* feat(rn-device): add TypeScript client for Android runner
Mirrors the iOS rn-fast-runner-client.ts pattern (PR #164):
- runAndroid() — single POST /command entry point speaking to the
in-tree UIAutomator runner from Task 1
- startAndroidRunner() — spawns `adb shell am instrument` with the
RN_ANDROID_RUNNER_PORT arg, sets up `adb forward tcp:22089`, parses
logcat for the RN_ANDROID_RUNNER_LISTENER_READY + port handshake
- stopAndroidRunner() — SIGTERM the instrument + logcat processes,
remove the `adb forward` rule
- postCommand() — POSTs to http://127.0.0.1:<port>/command
- _setFetchForTest / _setAndroidRunnerStateForTest — test seams
mirroring iOS test-seam pattern
State file at /tmp/rn-android-runner-state.json kept distinct from
iOS so both can run simultaneously. DEFAULT_PORT = 22089 (iOS uses
22088).
Includes the iOS runner-timeout shim ported for Android: when
UIAutomator returns "Could not detect idle state" / "Idle timeout
exceeded" on .type, treat it as success with
meta.runnerTimeoutShim: true. RN's main thread never reports
quiescence when Reanimated/RAF worklets are active, so this
particular timeout shape is benign — the text was appended before
the timeout fired.
Snapshot post-processing reuses the shared fast-runner-ref-map:
mapRunnerNodesToFlat builds @en refs from the runner's index;
updateRefMapFromFlat populates the same ref-map iOS uses.
Test coverage: 4 unit tests verifying snapshot dispatch + ref-map
population, tap coordinates, STALE_REF early-return, and runner
error code surfacing. All passing.
Also adds 'SCREENSHOT_FAILED' to the ToolErrorCode union in
src/types.ts — the runner-client uses that literal as a failure
code, mirroring how 'SNAPSHOT_FAILED' is defined.
Task 3 of 11 per docs/superpowers/plans/2026-05-16-rn-android-runner-mvp-plan.md.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* feat(rn-device): gate Android runner short-circuit + add buildRunAndroidArgs
Combines Tasks 4 + 5 of the Android MVP plan because Task 4's short-circuit
calls buildRunAndroidArgs (defined in Task 5) — splitting would break the
build mid-task-sequence.
Task 4 — Android short-circuit in runAgentDevice():
Adds an Android short-circuit immediately after the existing iOS block in
agent-device-wrapper.ts. Gated behind `RN_ANDROID_RUNNER=1` so the legacy
agent-device daemon/CLI path stays the default until Task 11's default-flip.
RN_ANDROID_RUNNER_COMMANDS covers all 13 MVP verbs: snapshot, tap, press,
fill, type, back, screenshot, keyboard, swipe, scroll, drag, longpress,
pinch. `find` is deliberately NOT included so device_find on Android
routes through the same TS-side findInLatestSnapshot orchestrator iOS uses
(D1217 symmetry — UIAutomator's regex-match By.text() would diverge from
iOS's exact-or-substring semantics).
Task 5 — buildRunAndroidArgs() + helpers:
Translates runAgentDevice CLI argv shape into RunAndroidArgs that the TS
client (Task 3) understands. Mirrors buildRunIOSArgs structurally. New
helpers: optionValue() (parses --foo bar), androidPositionals() (filters
flag args out of the positional list). Stale-ref handling injects the
sentinel into press/tap/type/longpress paths so the runner returns
STALE_REF without an HTTP round-trip.
Test coverage: 4 new unit tests verifying the short-circuit is env-gated
and platform-scoped, the command set covers MVP verbs, the case-block
fragments are present in buildRunAndroidArgs, and stale-ref/screenshot
out-path sentinels are wired correctly.
Tasks 4 + 5 of 11 per docs/superpowers/plans/2026-05-16-rn-android-runner-mvp-plan.md.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* feat(rn-device): Android flatten helper + stale-ref Android coverage
Tasks 6 + 7 of the Android MVP plan — both small and touch the same
fast-runner-ref-map module, so combining the commit keeps history tight.
Task 6 — flattenAndroidAccessibilityTree:
Adds an exported pass-through helper for Android because the Kotlin
runner's snapshot endpoint already returns flat `nodes` with `@eN` refs
(unlike iOS's XCUITree which needs a depth-first walk). The helper
mirrors the iOS flattenXCUITree shape but is a no-op over the node list
itself; the only work is building a sliced ref-map keyed without the
leading '@' for refCenter() lookups.
Task 7 — stale-ref detection for Android:
Adds one regression test to test/unit/stale-ref-detection.test.js
proving the existing isRefStale + findNewRefByMetadata helpers handle
Android flat-node shapes without any platform branching. Same ref
identity rules (testID, label, rect) carry over. Verifies that:
1. Identical refs are not stale.
2. Refs with a changed identifier are stale.
3. Metadata-based lookup finds the new ref after a re-render.
Together these two tasks confirm the iOS-built fast-runner-ref-map is
genuinely generic (D1217 — testID is the durable anchor). No new
isAndroidRefStale or per-platform code needed.
Tasks 6 + 7 of 11 per docs/superpowers/plans/2026-05-16-rn-android-runner-mvp-plan.md.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* feat(rn-device): runner-backed find + scrollintoview orchestration on Android
Task 8 of the Android MVP plan. iOS already routes device_find (non-exact)
and device_scrollintoview through TS-side orchestrators that compose
runAgentDevice('snapshot') + findInLatestSnapshot + a swipe loop. This
commit extends the same path to Android behind RN_ANDROID_RUNNER=1.
Changes:
- Rename scrollIntoViewIOS -> scrollIntoViewWithRunner (platform-neutral
name now that both platforms use it).
- Entry gate becomes:
session?.platform === 'ios' ||
(session?.platform === 'android' && process.env.RN_ANDROID_RUNNER === '1')
so Android opts in once the env flag is set.
- Inside scrollIntoViewWithRunner, replace direct fastSwipe() calls with
runAgentDevice(['swipe', x1, y1, x2, y2, duration]). On iOS this still
hits the iOS short-circuit -> runIOS('drag'); on Android with the env
flag set it hits the new Android short-circuit -> runAndroid('drag').
- Mirror device_find's existing iOS branch for Android — non-exact text
finds route through fetchFindCandidates (snapshot-based) instead of
the legacy agent-device find CLI, so the Android dispatcher never
re-spawns the upstream daemon during a find.
Removed the previous runAgentDevice(['scrollintoview', ...]) fallback
for Android-without-env (replaced with structured IN_TREE_RUNNER_REQUIRED
error). Task 11 flips the env default, so the legacy delegate is removed
early rather than late.
Test coverage: 2 new tests; full suite 1461/1461 passing.
Task 8 of 11 per docs/superpowers/plans/2026-05-16-rn-android-runner-mvp-plan.md.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* feat(rn-device): warn on competing Android UIAutomator runners
Task 9 of the Android MVP plan. The iOS-side external-runner detector
(PR #164) warned at session-open about a stale ~/.agent-device daemon.
This commit adds the Android counterpart: detectAndroidExternalRunner
runs `adb shell ps -A` and filters for processes matching
/uiautomator|agent-device|AgentDevice/i while excluding our own
`dev.lykhoyda.rndevagent.androidrunner` package.
When `device_snapshot action=open` runs against an Android session AND
RN_ANDROID_RUNNER=1 is set, the diagnostic fires and pushes an
ANDROID_UIAUTOMATOR_COMPETITOR warning into the session's warnings
list. Users see this in the session-open response so they can stop the
competing process before our runner contends for focus.
Test coverage: 2 new unit tests cover (1) detection of a competing
upstream `uiautomator runtest` process and (2) correct exclusion of
our own runner package from the warning.
Task 9 of 11 per docs/superpowers/plans/2026-05-16-rn-android-runner-mvp-plan.md.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* feat(rn-device): enable Android in-tree runner by default
Task 11 of the Android MVP plan. Flip the RN_ANDROID_RUNNER env gate
from opt-in (=== '1') to opt-out (!== '0'). Android device automation
now uses the in-tree scripts/rn-android-runner/ UIAutomator runner by
default. Set RN_ANDROID_RUNNER=0 to revert to the legacy agent-device
daemon/CLI path during rollback or diagnosis.
Changes:
- agent-device-wrapper.ts - Android short-circuit gate becomes default-on
- device-interact.ts - find + scrollintoview Android-branch gates flip
- device-session.ts - Android external-runner diagnostic gate flips
- Unit tests updated to match the new env semantics
- BUGS.md (plugin) - record deferred Task 10 live smoke-test
- ROADMAP.md (plugin) - Phase 138 entry with task-by-task summary
Live smoke-test (Task 10) deferred - host had insufficient disk space
to boot the Pixel_9_Pro AVD. Unit + Gradle layers all green (1464/1464
tests pass). See BUGS.md + ROADMAP Phase 138 for re-run instructions.
Task 11 of 11 per docs/superpowers/plans/2026-05-16-rn-android-runner-mvp-plan.md.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* fix(rn-device): grant INTERNET to runner target manifest (Android smoke-test finding)
Live Task 10 smoke-test surfaced this real plan defect:
java.net.SocketException: socket failed: EPERM (Operation not permitted)
on NanoHTTPD's ServerSocket.bind() at runner startup.
Root cause: am instrument injects test code into the TARGET app's
process (not the test APK's UID). ServerSocket.bind() checks the
target UID's permissions, not the test APK's. The androidTest
manifest had android.permission.INTERNET; the target app manifest
did not. The runner's main AndroidManifest.xml now declares it too.
Verified live: after the fix, instrumentation logs
RN_ANDROID_RUNNER_LISTENER_READY + RN_ANDROID_RUNNER_PORT=22089
within seconds of am instrument start, curl /health returns
{"ok":true}, and a /command snapshot of com.rndevagent.testapp
returns the expected tab-home / tab-tasks / tab-notifications /
tab-profile identifiers.
Plan reference: docs/superpowers/plans/2026-05-16-rn-android-runner-mvp-plan.md
Task 10 (live smoke-test) - now passes end-to-end.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* chore(rn-device): Android runner emulator smoke test passed
Live verification on Pixel_9_Pro AVD (emulator-5554):
- ./gradlew :app:assembleDebugAndroidTest -> BUILD SUCCESSFUL
- am instrument readiness handshake: RN_ANDROID_RUNNER_LISTENER_READY
+ RN_ANDROID_RUNNER_PORT=22089 within ~3s of spawn
- curl http://127.0.0.1:22089/health -> {"ok":true}
- curl /command snapshot of com.rndevagent.testapp -> 148 nodes
including tab-home, tab-tasks, tab-notifications, tab-profile
identifiers from the React Native bottom tab bar
- ps -A | grep agent-device|AgentDevice|uiautomator (excluding our
androidrunner package) -> empty (no upstream contention)
Smoke-test surfaced one real plan defect (INTERNET permission missing
from target app manifest); fixed in the preceding commit and verified
post-fix.
Closes Task 10 of 11 per docs/superpowers/plans/2026-05-16-rn-android-runner-mvp-plan.md.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* docs(rn-device): Task 10 smoke-test passed live - clear deferred notes
BUGS.md: move the Task 10 "deferred" entry to a Fixed section with the
live verification details (handshake, /health, snapshot returning tab-*
identifiers, zero upstream agent-device processes). Document the one
plan defect surfaced + fixed during the run (target-app INTERNET
permission - commit f5d4e0a).
ROADMAP.md: update Phase 138 Task 10 row from "(deferred)" to
"d0367da (+ f5d4e0a plan-defect fix)" with passing criteria summary.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* fix(rn-device): use relative paths in Android test source-grep helpers (CI fix)
CI exposed a real plan defect that local runs missed: three Android tests
loaded their source files via hardcoded absolute paths
(/Users/anton_personal/GitHub/...) inherited verbatim from the plan's
test snippets. CI runs at /home/runner/work/... so readFileSync threw
ENOENT and the test files failed at module load.
Switch all three to import.meta.url + path.resolve to derive the
plugin-root-relative path at runtime:
- test/unit/android-runner-short-circuit.test.js (2 read sites)
- test/unit/build-run-android-args.test.js (1 read site)
- test/unit/tools/scrollintoview-orchestration.test.js (1 read site)
Also dedupe the inner readFileSync inside the third
android-runner-short-circuit test — it was re-reading the same file
with the same hardcoded path; the top-level `source` constant is the
right reference.
No production code changes; tests-only patch. Full suite remains
1464/1464 locally and should now match on CI.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>1 parent d137eea commit aa221aa
44 files changed
Lines changed: 2338 additions & 79 deletions
File tree
- commands
- scripts
- cdp-bridge
- dist
- runners
- tools
- src
- runners
- tools
- test/unit
- runners
- tools
- rn-android-runner
- app
- src
- androidTest
- java/dev/lykhoyda/rndevagent/androidrunner
- main
- gradle/wrapper
- skills/rn-setup
Some content is hidden
Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
369 | 369 | | |
370 | 370 | | |
371 | 371 | | |
| 372 | + | |
| 373 | + | |
| 374 | + | |
| 375 | + | |
| 376 | + | |
| 377 | + | |
| 378 | + | |
| 379 | + | |
| 380 | + | |
| 381 | + | |
| 382 | + | |
| 383 | + | |
| 384 | + | |
| 385 | + | |
| 386 | + | |
| 387 | + | |
| 388 | + | |
| 389 | + | |
| 390 | + | |
| 391 | + | |
| 392 | + | |
| 393 | + | |
| 394 | + | |
372 | 395 | | |
373 | 396 | | |
374 | 397 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
37 | 37 | | |
38 | 38 | | |
39 | 39 | | |
40 | | - | |
41 | | - | |
42 | | - | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
43 | 44 | | |
44 | 45 | | |
45 | 46 | | |
| |||
65 | 66 | | |
66 | 67 | | |
67 | 68 | | |
68 | | - | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
69 | 72 | | |
70 | 73 | | |
71 | 74 | | |
| |||
89 | 92 | | |
90 | 93 | | |
91 | 94 | | |
92 | | - | |
| 95 | + | |
| 96 | + | |
93 | 97 | | |
94 | 98 | | |
95 | 99 | | |
96 | | - | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
97 | 105 | | |
98 | 106 | | |
99 | 107 | | |
| |||
118 | 126 | | |
119 | 127 | | |
120 | 128 | | |
121 | | - | |
| 129 | + | |
122 | 130 | | |
123 | 131 | | |
124 | 132 | | |
125 | 133 | | |
126 | 134 | | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
127 | 139 | | |
128 | 140 | | |
129 | 141 | | |
| |||
136 | 148 | | |
137 | 149 | | |
138 | 150 | | |
139 | | - | |
| 151 | + | |
| 152 | + | |
140 | 153 | | |
141 | 154 | | |
142 | 155 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
26 | 26 | | |
27 | 27 | | |
28 | 28 | | |
29 | | - | |
| 29 | + | |
30 | 30 | | |
31 | 31 | | |
32 | 32 | | |
33 | 33 | | |
34 | 34 | | |
35 | | - | |
| 35 | + | |
| 36 | + | |
36 | 37 | | |
37 | 38 | | |
38 | 39 | | |
| |||
93 | 94 | | |
94 | 95 | | |
95 | 96 | | |
96 | | - | |
| 97 | + | |
97 | 98 | | |
98 | 99 | | |
99 | 100 | | |
| |||
160 | 161 | | |
161 | 162 | | |
162 | 163 | | |
163 | | - | |
164 | | - | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
165 | 167 | | |
166 | 168 | | |
| 169 | + | |
| 170 | + | |
| 171 | + | |
167 | 172 | | |
168 | 173 | | |
169 | 174 | | |
| |||
194 | 199 | | |
195 | 200 | | |
196 | 201 | | |
| 202 | + | |
| 203 | + | |
| 204 | + | |
197 | 205 | | |
198 | 206 | | |
199 | 207 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
0 commit comments