Files
callstack__agent-device/package.json
T
Michał Pierzchała d85072d935 perf(cli): route command aliases through the help fast path (#1641)
* perf(cli): route command aliases through the help fast path

bin.ts's `--help` fast path resolved aliases through a hand-written
two-entry table that had drifted out of sync with the real
CLI_COMMAND_ALIASES registry (five entries). `tap`, `launch`, and
`relaunch` missed the table and silently fell through to a full
runCli() bootstrap just to print static help text (~150-165ms vs
~45-50ms for aliases already in the table).

Delegate to the shared normalizeCliCommandAlias registry instead of
the stale local table, so every alias the registry knows about gets
the fast path automatically.

* test(cli): add R12 layering guard for bin.ts's alias delegation

The unit test added for the alias fast-path fix (cli-help-alias-fast-path.test.ts)
calls normalizeCliCommandAlias directly, so it stays green even if bin.ts
itself reverts to a hand-rolled table — it pins the registry composition,
not bin.ts's own wiring, and bin.ts cannot be safely unit-imported (it runs
unguarded top-level dispatch on import and is deliberately excluded from
coverage).

Add an AST-based structural guard instead, in the style already established
by scripts/layering/session-state.ts, facade-exports.ts, and zero-dep-jobs.ts
(oxc-parser's module/program records, not a line scan, so a fixture's string
literal can't produce a false hit). R12 asserts two facts about src/bin.ts:
it holds a value import of normalizeCliCommandAlias from
commands/cli-command-aliases.ts, and it contains none of the registry's own
alias tokens as string literals. The token list is read out of the
registry's own source (CLI_COMMAND_ALIASES's `alias:` property values), not
hard-coded, so a future sixth alias is covered automatically. Both facts
were false on the pre-fix bin.ts, verified by reverting locally and
capturing the failure before restoring the fix.

Wired into the existing check:layering chain (already part of
check:tooling), next to R7's session-state ownership rule, which pins the
same "delegate to your single owner" shape.

* test(cli): pin the alias-resolver call into buildCommandUsageText (R12 P2)

Maintainer review of R12 (PR #1641): import-presence and literal-absence
alone let bin.ts regress to buildCommandUsageText(helpTarget) while the
normalizeCliCommandAlias import stays in place, used harmlessly elsewhere
(or not at all) — the real-tree gate stayed green through that exact
regression.

Add a third fact: bin.ts's call to buildCommandUsageText must receive, as
its argument, a call to the LOCAL binding the resolver was imported as
(aliasResolverLocalName + usageTextCallsResolver, both AST-based). Binding
by local name rather than the literal export name means a renamed import
(`as resolveAlias`) still verifies, and an unrelated same-named local
cannot be mistaken for it.

Verified by reverting locally to exactly the missed regression — import
left in place, call reverted to buildCommandUsageText(helpTarget) — and
confirming R12 now fails where the two-fact version passed; restored after.
Two negative fixtures pin the scenario going forward: import present but
unused, and import present but used only unrelated to the call.

* test(cli): make R12's delegation fact universal and value-bound

The previous fact 3 asked whether *any* `buildCommandUsageText(resolver(...))`
existed in bin.ts. That quantifier is satisfied by a decoy call while the line
that actually ships resolves nothing:

    void buildCommandUsageText(normalizeCliCommandAlias('open'));
    const commandHelp = buildCommandUsageText(helpTarget);

Fact 3 now requires EVERY `buildCommandUsageText` call to receive the imported
resolver applied to the fast path's own help-target binding, which rejects both
lines above independently. The help-target name is read from bin.ts (the
variable initialized by `resolveSimpleHelpTarget`), so renaming it re-points
the guard instead of disarming it.

Because fact 3 claims binding identity by name, it also now rejects a local
shadow of the resolver and an ambiguous second help-target declaration — a
same-named local would otherwise let the composition read as delegation while
calling something that resolves nothing.

The predicate returns the reason rather than a boolean, so the gate names which
of the several distinct failures happened.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017Rva4YGtSCAKJqH5PbpcCU

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-08-06 21:00:22 +02:00

281 lines
15 KiB
JSON

{
"name": "agent-device",
"version": "0.20.5",
"description": "Agent-native CLI for AI app automation across iOS, Android, tvOS, Android TV, macOS, Linux, and web.",
"mcpName": "io.github.callstack/agent-device",
"license": "MIT",
"author": "Callstack",
"homepage": "https://agent-device.dev/",
"repository": {
"type": "git",
"url": "git+https://github.com/callstack/agent-device.git"
},
"bugs": {
"url": "https://github.com/callstack/agent-device/issues"
},
"type": "module",
"packageManager": "pnpm@11.17.0",
"main": "dist/src/index.js",
"types": "dist/src/index.d.ts",
"exports": {
".": {
"types": "./dist/src/index.d.ts",
"import": "./dist/src/index.js"
},
"./io": {
"types": "./dist/src/io.d.ts",
"import": "./dist/src/io.js"
},
"./artifacts": {
"types": "./dist/src/artifacts.d.ts",
"import": "./dist/src/artifacts.js"
},
"./metro": {
"types": "./dist/src/metro.d.ts",
"import": "./dist/src/metro.js"
},
"./batch": {
"types": "./dist/src/batch.d.ts",
"import": "./dist/src/batch.js"
},
"./remote-config": {
"types": "./dist/src/remote-config.d.ts",
"import": "./dist/src/remote-config.js"
},
"./install-source": {
"types": "./dist/src/install-source.d.ts",
"import": "./dist/src/install-source.js"
},
"./android-adb": {
"types": "./dist/src/android-adb.d.ts",
"import": "./dist/src/android-adb.js"
},
"./limrun": {
"types": "./dist/src/limrun.d.ts",
"import": "./dist/src/limrun.js"
},
"./contracts": {
"types": "./dist/src/contracts.d.ts",
"import": "./dist/src/contracts.js"
},
"./selectors": {
"types": "./dist/src/selectors.d.ts",
"import": "./dist/src/selectors.js"
},
"./finders": {
"types": "./dist/src/finders.d.ts",
"import": "./dist/src/finders.js"
}
},
"engines": {
"node": ">=22.12"
},
"bin": {
"agent-device": "bin/agent-device.mjs"
},
"scripts": {
"build": "tsdown",
"clean:daemon": "node --experimental-strip-types scripts/clean-daemon.ts",
"daemon:state-dir": "node --experimental-strip-types scripts/print-daemon-state-dir.ts",
"explain:command": "node --experimental-strip-types scripts/explain-command.ts",
"clean:xcuitest": "node scripts/clean-xcuitest-derived.mjs",
"clean:xcuitest:ios": "node scripts/clean-xcuitest-derived.mjs ios",
"clean:xcuitest:macos": "node scripts/clean-xcuitest-derived.mjs macos",
"clean:xcuitest:tvos": "node scripts/clean-xcuitest-derived.mjs tvos",
"clean:xcuitest:visionos": "node scripts/clean-xcuitest-derived.mjs visionos",
"rebuild:cli": "pnpm build && pnpm clean:daemon",
"build:xcuitest": "pnpm build:xcuitest:ios && pnpm build:xcuitest:macos",
"build:xcuitest:ios": "AGENT_DEVICE_XCUITEST_PLATFORM=ios sh ./scripts/build-xcuitest-apple.sh",
"build:xcuitest:ios:clean": "pnpm clean:xcuitest:ios && pnpm build:xcuitest:ios",
"build:xcuitest:macos": "AGENT_DEVICE_XCUITEST_PLATFORM=macos sh ./scripts/build-xcuitest-apple.sh",
"build:xcuitest:macos:clean": "pnpm clean:xcuitest:macos && pnpm build:xcuitest:macos",
"build:xcuitest:tvos": "AGENT_DEVICE_XCUITEST_PLATFORM=tvos sh ./scripts/build-xcuitest-apple.sh",
"build:xcuitest:tvos:clean": "pnpm clean:xcuitest:tvos && pnpm build:xcuitest:tvos",
"build:xcuitest:visionos": "AGENT_DEVICE_XCUITEST_PLATFORM=visionos sh ./scripts/build-xcuitest-apple.sh",
"build:xcuitest:visionos:clean": "pnpm clean:xcuitest:visionos && pnpm build:xcuitest:visionos",
"build:android-snapshot-helper": "AGENT_DEVICE_ANDROID_HELPER=snapshot sh ./scripts/build-android-helper.sh $(node -p \"require('./package.json').version\") .tmp/android-snapshot-helper",
"package:android-snapshot-helper": "AGENT_DEVICE_ANDROID_HELPER=snapshot sh ./scripts/package-android-helper.sh $(node -p \"require('./package.json').version\") v$(node -p \"require('./package.json').version\") .tmp/android-snapshot-helper",
"package:android-snapshot-helper:npm": "rm -rf android/snapshot-helper/dist && AGENT_DEVICE_ANDROID_HELPER=snapshot sh ./scripts/package-android-helper.sh $(node -p \"require('./package.json').version\") v$(node -p \"require('./package.json').version\") android/snapshot-helper/dist",
"package:apple-runner:npm": "node scripts/package-apple-runner-source.mjs",
"build:android": "pnpm package:android-snapshot-helper:npm && pnpm package:android-ime-helper:npm",
"build:android-ime-helper": "AGENT_DEVICE_ANDROID_HELPER=ime sh ./scripts/build-android-helper.sh $(node -p \"require('./package.json').version\") .tmp/android-ime-helper",
"package:android-ime-helper": "AGENT_DEVICE_ANDROID_HELPER=ime sh ./scripts/package-android-helper.sh $(node -p \"require('./package.json').version\") .tmp/android-ime-helper",
"package:android-ime-helper:npm": "rm -rf android/ime-helper/dist && AGENT_DEVICE_ANDROID_HELPER=ime sh ./scripts/package-android-helper.sh $(node -p \"require('./package.json').version\") android/ime-helper/dist",
"build:macos-helper": "swift build -c release --package-path apple/macos-helper",
"build:macos-helper:clean": "swift package --package-path apple/macos-helper clean && pnpm build:macos-helper",
"build:package": "pnpm build && pnpm build:xcuitest:ios && pnpm build:xcuitest:macos && pnpm build:xcuitest:tvos && pnpm build:xcuitest:visionos && pnpm build:macos-helper:clean && pnpm package:apple-runner:npm && pnpm build:android",
"package:npm": "pnpm build:package && pnpm check:package",
"release:prepare": "rm -rf .tmp/release && pnpm check:mcp-metadata && pnpm build:package && pnpm check:package -- --pack-destination .tmp/release",
"release:publish": "pnpm release:prepare && npm publish --ignore-scripts .tmp/release/*.tgz",
"ad": "node bin/agent-device.mjs",
"bench:help-conformance": "node scripts/help-conformance-bench.mjs",
"maestro:conformance": "node --experimental-strip-types --test packages/maestro/test/conformance/verify.test.ts packages/maestro/test/conformance/differential/run.test.ts packages/maestro/test/conformance/differential/invariants.test.ts",
"maestro:conformance:regenerate": "node --experimental-strip-types scripts/maestro-conformance/regenerate.mjs",
"maestro:conformance:differential": "node --experimental-strip-types packages/maestro/test/conformance/differential/run.ts",
"size": "node scripts/size-report.mjs",
"perf": "node --experimental-strip-types scripts/perf/run.ts",
"mutation:run": "node --experimental-strip-types scripts/mutation/run.ts",
"mutation:baseline": "node --experimental-strip-types scripts/mutation/run.ts --update",
"mutation:check": "node --experimental-strip-types scripts/mutation/run.ts --no-run",
"mutation:affected": "node --experimental-strip-types scripts/mutation/run.ts --affected",
"mutation:test": "node --experimental-strip-types --test scripts/mutation/*.test.ts",
"lint": "oxlint . --deny-warnings",
"format": "node ./node_modules/oxfmt/bin/oxfmt --write",
"format:check": "node ./node_modules/oxfmt/bin/oxfmt --check",
"fuzz:parsers": "node --experimental-strip-types scripts/fuzz/run.ts",
"fallow:all": "fallow --summary",
"fallow:baseline": "fallow dead-code --save-baseline fallow-baselines/dead-code.json --summary && fallow health --report-only --save-baseline fallow-baselines/health.json --summary",
"check:fallow": "fallow audit",
"check:affected": "node --experimental-strip-types scripts/check-affected/run.ts",
"check:affected:test": "node --experimental-strip-types --test scripts/check-affected/model.test.ts scripts/check-affected/run.test.ts",
"check:coverage-changed": "node --experimental-strip-types scripts/coverage-changed/run.ts",
"check:coverage-changed:test": "node --experimental-strip-types --test scripts/coverage-changed/model.test.ts scripts/coverage-changed/run.test.ts",
"check:layering": "node --experimental-strip-types --test scripts/layering/model.test.ts scripts/layering/zone-policy.test.ts scripts/layering/daemon-modularity.test.ts scripts/layering/package-boundaries.test.ts scripts/layering/facade-exports.test.ts scripts/layering/bin-alias-fast-path.test.ts && node --experimental-strip-types scripts/layering/check.ts",
"depgraph": "node --experimental-strip-types scripts/depgraph/build.ts",
"depgraph:test": "node --experimental-strip-types --test scripts/depgraph/model.test.ts scripts/depgraph/affected.test.ts",
"check:production-exports": "fallow dead-code --config fallow-production-exports.json --production --unused-exports --fail-on-issues",
"check:bundle-owner-files": "node --experimental-strip-types scripts/check-bundle-owner-files.ts",
"check:package": "node --experimental-strip-types scripts/check-package.ts",
"check:command-docs": "vitest run --project unit-core src/__tests__/command-doc-coverage.test.ts",
"check:replay-compat": "node --experimental-strip-types scripts/check-replay-compat-provenance.ts",
"check:tmpdir-leaks": "node --experimental-strip-types scripts/check-tmpdir-leaks.ts",
"check:tmpdir-leaks:test": "node --experimental-strip-types --test scripts/check-tmpdir-leaks-model.test.ts scripts/vitest-tmpdir-global-setup.test.ts",
"check:freerange": "fr",
"check:quick": "pnpm lint && pnpm typecheck",
"sync:mcp-metadata": "node scripts/sync-mcp-metadata.mjs",
"check:mcp-metadata": "node scripts/sync-mcp-metadata.mjs --check",
"version": "pnpm sync:mcp-metadata && git add server.json",
"check:tooling": "pnpm format:check && pnpm lint && pnpm typecheck && pnpm check:layering && pnpm depgraph:test && pnpm check:production-exports && pnpm check:tmpdir-leaks:test && pnpm check:mcp-metadata && pnpm build && pnpm check:bundle-owner-files && pnpm check:package",
"check:unit": "pnpm check:contention-retry && pnpm test:unit && pnpm check:tmpdir-leaks && pnpm test:smoke",
"check": "pnpm check:tooling && pnpm check:fallow && pnpm check:unit",
"prepack": "pnpm check:mcp-metadata && pnpm package:npm",
"typecheck": "tsc -b packages/xml packages/kernel packages/contracts packages/ad-script packages/selectors packages/ad-replay packages/maestro packages/replay-test packages/provider-webdriver packages/provider-limrun && tsc -p tsconfig.json && tsc -p examples/sdk/tsconfig.json",
"test-app:install": "pnpm install --dir examples/test-app",
"test-app:start": "pnpm --dir examples/test-app start",
"test-app:ios": "pnpm --dir examples/test-app ios",
"test-app:android": "pnpm --dir examples/test-app android",
"test-app:typecheck": "pnpm --dir examples/test-app typecheck",
"test-app:replay:ios": "pnpm ad test examples/test-app/replays --platform ios --artifacts-dir .tmp/test-app-replay/ios",
"test-app:replay:android": "pnpm ad test examples/test-app/replays --platform android --artifacts-dir .tmp/test-app-replay/android",
"test-app:maestro": "node scripts/run-test-app-maestro-suite.mjs",
"test-app:maestro:ios": "pnpm test-app:maestro --platform ios",
"test-app:maestro:android": "pnpm test-app:maestro --platform android",
"test": "pnpm test:unit",
"test:unit": "vitest run --project unit-core --project subprocess-stub",
"test:maestro-compat": "vitest run --project unit-core packages/maestro src/daemon/adapters/maestro src/compat/__tests__/replay-input.test.ts",
"test:coverage": "vitest run --coverage",
"test:coverage:ci": "node --experimental-strip-types scripts/lib/contention-retry-run.ts --coverage",
"check:contention-retry": "node --experimental-strip-types --test scripts/lib/contention-retry-policy.test.ts",
"test:integration:provider": "vitest run --project provider-integration",
"test:integration:progress": "node --experimental-strip-types scripts/integration-progress.ts",
"test:integration:progress:check": "node --experimental-strip-types scripts/integration-progress.ts --check",
"test:fixture-cache": "node --test test/ci/trusted-fixture-artifact.test.mjs",
"test:output-economy": "vitest run --project output-economy",
"test:smoke:web": "pnpm build && node --test test/integration/smoke-web-platform.test.ts",
"test:smoke": "node --test test/integration/smoke-*.test.ts",
"test:integration:node": "node --test test/integration/*.test.ts",
"test:integration": "pnpm test:integration:node && pnpm test:integration:provider",
"test:concurrency-torture": "node --test test/integration/nightly/concurrency-torture.test.ts",
"test:replay:ios": "node --experimental-strip-types src/bin.ts test test/integration/replays/ios/simulator",
"test:replay:ios-device": "node --experimental-strip-types src/bin.ts test test/integration/replays/ios/device",
"test:replay:android": "node --experimental-strip-types src/bin.ts test test/integration/replays/android",
"test:replay:macos": "node --experimental-strip-types src/bin.ts test test/integration/replays/macos",
"test:replay:linux": "node --experimental-strip-types src/bin.ts test test/integration/replays/linux"
},
"files": [
"bin",
"dist",
"apple/macos-helper",
"!apple/macos-helper/**/.build",
"android/snapshot-helper/dist",
"!android/snapshot-helper/dist/*.idsig",
"!android/snapshot-helper/README.md",
"android/ime-helper/dist",
"!android/ime-helper/dist/*.idsig",
"!android/ime-helper/README.md",
"linux/atspi-dump.py",
"skills",
"server.json",
"smithery.yaml",
"README.md",
"LICENSE"
],
"keywords": [
"agent",
"device",
"cli",
"automation",
"adb",
"simctl",
"devicectl",
"ios",
"android",
"tvos",
"macos",
"web",
"browser",
"react-native",
"observability",
"diagnostics",
"network",
"profiling",
"performance",
"mcp",
"model-context-protocol",
"mcp-server",
"ai-agent",
"mobile-automation",
"ios-simulator",
"android-emulator",
"xcuitest",
"e2e-testing",
"cursor",
"claude-code",
"expo",
"mobile-testing",
"qa-automation",
"ai-testing",
"ios-automation",
"android-automation",
"simulator",
"emulator",
"appium",
"maestro",
"detox"
],
"dependencies": {
"@limrun/api": "^0.24.5",
"yaml": "^2.9.0"
},
"devDependencies": {
"@agent-device/ad-replay": "workspace:*",
"@agent-device/ad-script": "workspace:*",
"@agent-device/contracts": "workspace:*",
"@agent-device/kernel": "workspace:*",
"@agent-device/selectors": "workspace:*",
"@agent-device/maestro": "workspace:*",
"@agent-device/provider-limrun": "workspace:*",
"@agent-device/provider-webdriver": "workspace:*",
"@agent-device/replay-test": "workspace:*",
"@agent-device/xml": "workspace:*",
"@arethetypeswrong/cli": "^0.18.5",
"@chenglou/freerange": "^0.0.1",
"@stryker-mutator/core": "9.6.1",
"@stryker-mutator/vitest-runner": "9.6.1",
"@types/node": "^22.19.21",
"@types/pngjs": "^6.0.5",
"@vitest/coverage-v8": "4.1.8",
"fallow": "^2.95.0",
"fast-check": "^4.9.0",
"oxc-parser": "^0.138.0",
"oxfmt": "^0.42.0",
"oxlint": "^1.69.0",
"pngjs": "^7.0.0",
"publint": "^0.3.22",
"tsdown": "^0.22.4",
"typescript": "^7.0.2",
"vite": "^8.0.16",
"vitest": "^4.1.8"
}
}