npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@tai2/aco

v0.2.6

Published

Appium Command-line Operator — drive an Appium session from the shell, one command at a time.

Readme

aco -- Appium Command-line Operator

ci npm

Drive an Appium session from the shell. Start a session against an app, then send each Appium command as a single shell invocation -- no client-script scaffolding required.

Prerequisites

Install Appium and at least one driver first; aco spawns the appium binary from your PATH. See the Appium install guide for details.

npm i -g appium                     # needs Node.js LTS and npm >=10
which appium                        # confirm it's on PATH

appium driver install xcuitest      # iOS (requires Xcode + command line tools)
appium driver install uiautomator2  # Android (requires Android SDK + JDK)

appium driver doctor xcuitest       # verify a driver's toolchain

Install

npm i -g @tai2/aco
# or run without installing:
npx @tai2/aco --help

Use with Claude Code

Install the aco plugin so Claude Code can drive sessions for you:

claude plugin marketplace add tai2/aco
claude plugin install aco@aco

Then ask your agent things like "tap the login button on the simulator" or "screenshot the current screen" — it shells out to aco.

Usage

Discover devices

List iOS Simulators, Android AVDs, and connected real devices. Pass a row's NAME to --device-name (iOS) / --avd (Android emulator), or its ID to --udid (real device or simulator).

aco device list                          # both platforms, only "available" rows
aco device list --platform ios           # iOS Simulators + connected iPhones/iPads
aco device list --state all --json       # everything, machine-readable

Start a session

# iOS simulator (foreground; Ctrl-C tears it down)
aco session start --platform ios --app /tmp/MyApp.app.zip --device-name "iPhone 15"

# Android emulator
aco session start --platform android --app com.example.app --app-activity .MainActivity --avd Pixel_8_API_34

# iOS real device (requires code signing for the on-device WebDriverAgent build)
aco session start --platform ios --udid <40-char-udid> \
  --app /tmp/MyApp.ipa --xcode-org-id ABCDE12345

# Android real device (auto-selected when one is plugged in and no --avd given)
aco session start --platform android --app com.example.app --app-activity .MainActivity

# Background instead of foreground
aco session start --detach --platform ios --app /tmp/MyApp.app

# Forward extra Appium server flags
aco session start --platform android --log-level debug --use-plugins images

# Remote server / device farm: --server-url attaches to a running Appium
# instead of spawning one. --auth sends BASIC auth (only on session creation;
# or via ACO_REMOTE_USERNAME / ACO_REMOTE_PASSWORD).
aco session start --platform android --server-url https://grid.example.com/wd/hub \
  --auth user:accessKey --cap deviceName="Pixel 8"

# Device farm with vendor-specific nested caps (LambdaTest's "lt:options",
# BrowserStack's "bstack:options", ...): pass the whole W3C caps object with
# --caps-json (or --caps-json @file). Replaces the aco-built caps; --platform
# is still required.
aco session start --platform android \
  --server-url https://mobile-hub.lambdatest.com/wd/hub --auth user:accessKey \
  --caps-json '{"platformName":"android","lt:options":{"isRealMobile":true,"platformVersion":"13","deviceName":"Pixel 8","app":"lt://APP_ID"}}'
  • With neither --udid nor --avd, a connected real device wins; otherwise the first Android AVD is auto-booted. Explicit --udid/--avd always win.
  • iOS real devices need --xcode-org-id (plus optional --xcode-signing-id, --allow-provisioning-device-registration, --updated-wda-bundle-id).
  • --log streams the Appium log to stdout. aco session start --help lists the forwarded server flags (--log-level, --relaxed-security, the *-timeout knobs, ...).

Drive the session

--session, --server-url, and --platform are optional with a live local session; pass them explicitly for remote/grid sessions.

aco source                                                    # full page source
aco source --xpath '//XCUIElementTypeButton[@name="Login"]'   # filter locally
aco elements                                                  # labelled elements + tap selectors
aco elements --json | jq -r '.[].selector'                    # just the selectors
aco screenshot --out ./shot.png
aco tap        --x 100 --y 200
aco tap        --selector 'accessibility id:login.button'
aco swipe      --direction up                                 # within the default scroll view
aco swipe      --direction left --label home.carousel         # within a labelled element
aco send-keys  --selector 'accessibility id:login.username' --text 'alice'   # clears, then types
aco send-keys  --label login.username --text '!' --no-clear                  # append instead
aco scroll-into-view "accessibility id:gestures.row.29" --direction up       # swipe until visible
aco actions    --gesture "move 200 600 0, down, move 200 200 300, up"        # raw W3C pointer
aco actions    --type "hello"                                                # raw W3C key
aco element find  --using "accessibility id" --value "Login"
aco element click --element <element-id>
aco context list
aco context switch --name WEBVIEW_com.example   # enter the web context, then:
aco web url https://example.com                 # navigate the active WebView

# Explicit targeting (remote/grid):
aco source --session <sid> --server-url http://10.0.0.5:4799 --platform ios

Platform mobile: extensions

Every mobile: extension the drivers advertise is a first-class command under a platform namespace. Flags map 1:1 to the driver's params and coerce to the declared type.

aco ios --help                 # all XCUITest mobile: extensions
aco android --help             # all UiAutomator2 / Android extensions
aco ios swipe --direction up
aco ios scroll --toVisible true --distance 0.5
aco android shell --command "getprop ro.product.model"
aco ios <cmd> --help           # see each param's type

mobile: names are snake-cased to subcommands (mobile: doubleTapaco ios double-tap); --<param> flags keep the driver's camelCase names.

aco mobile list                # what the connected driver actually advertises
aco mobile call --name "mobile: swipe" --args '{"direction":"up"}'   # unvalidated escape hatch

Inspect / stop sessions

aco session list                    # startedAt, platform, pid, alive, url, sessionId
aco session list --json
aco session list --prune            # delete records for dead servers / crashed children

aco session stop                    # stop the latest live session
aco session stop --session <sid>    # stop a specific one
aco session stop --all              # stop every stored session

Troubleshooting

  • Insecure feature 'adb_shell' not enabled when calling mobile: shell on Android: start the session with --cap appium:allowInsecure='["adb_shell"]'.
  • appium not found on PATH when running aco session start: install Appium (npm i -g appium) and verify which appium resolves.
  • unknown command (script) from aco mobile call or a generated aco ios/aco android command: the connected server runs a driver version that does not expose that extension. Run aco mobile list to see what it advertises.

Development

Tool versions are pinned in mise.toml -- install mise and run mise install to match.

pnpm install
pnpm dev --help                 # one-shot run via tsx
pnpm dev:watch --help           # rerun on file changes
pnpm typecheck                  # tsc --noEmit
pnpm test                       # vitest run
pnpm gen:extensions             # regenerate src/data/extensions-*.json from driver source
pnpm build                      # produce dist/cli.js via tsup

See CLAUDE.md for the driver-snapshot workflow and the rules for adding new mobile: wrappers.

Testing

An example Expo-based AUT (App Under Test) lives under aut/, with one screen per aco command family.

pnpm aut:install                # install AUT deps (~200MB, one-time)
pnpm aut:prebuild               # generate aut/ios and aut/android
pnpm aut:build:ios              # build .app for the iOS Simulator
pnpm aut:build:android          # build .apk for an Android emulator

See aut/README.md for the screen-to-command map.

License

MIT