agent-device 0.8.3 → 0.8.4

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 (42) hide show
  1. package/dist/src/224.js +2 -2
  2. package/dist/src/331.js +1 -1
  3. package/dist/src/bin.js +61 -60
  4. package/dist/src/client-shared.d.ts +20 -0
  5. package/dist/src/core/dispatch-resolve.d.ts +2 -2
  6. package/dist/src/daemon/handlers/session-batch.d.ts +2 -0
  7. package/dist/src/daemon/handlers/session-close.d.ts +31 -0
  8. package/dist/src/daemon/handlers/session-deploy.d.ts +37 -0
  9. package/dist/src/daemon/handlers/session-device-utils.d.ts +25 -0
  10. package/dist/src/daemon/handlers/session-open-target.d.ts +3 -0
  11. package/dist/src/daemon/handlers/session-open.d.ts +22 -0
  12. package/dist/src/daemon/handlers/session-perf.d.ts +2 -0
  13. package/dist/src/daemon/handlers/session-runtime-command.d.ts +9 -0
  14. package/dist/src/daemon/handlers/session-runtime.d.ts +41 -0
  15. package/dist/src/daemon/handlers/session-startup-metrics.d.ts +11 -0
  16. package/dist/src/daemon/handlers/session.d.ts +4 -29
  17. package/dist/src/daemon/selectors-build.d.ts +4 -0
  18. package/dist/src/daemon/selectors-match.d.ts +5 -0
  19. package/dist/src/daemon/selectors-parse.d.ts +29 -0
  20. package/dist/src/daemon/selectors-resolve.d.ts +32 -0
  21. package/dist/src/daemon/selectors.d.ts +5 -65
  22. package/dist/src/daemon.js +36 -36
  23. package/dist/src/platforms/android/index.d.ts +2 -1
  24. package/dist/src/platforms/android/screenshot.d.ts +16 -0
  25. package/dist/src/platforms/android/snapshot.d.ts +0 -1
  26. package/dist/src/platforms/ios/config.d.ts +1 -0
  27. package/dist/src/platforms/ios/index.d.ts +1 -1
  28. package/dist/src/platforms/ios/screenshot-status-bar.d.ts +2 -0
  29. package/dist/src/platforms/ios/screenshot.d.ts +2 -1
  30. package/dist/src/utils/command-schema.d.ts +2 -0
  31. package/dist/src/utils/device.d.ts +1 -1
  32. package/dist/src/utils/interactors.d.ts +7 -0
  33. package/dist/src/utils/output.d.ts +3 -1
  34. package/dist/src/utils/path-resolution.d.ts +8 -0
  35. package/dist/src/utils/screenshot-diff.d.ts +23 -0
  36. package/package.json +5 -5
  37. package/skills/agent-device/SKILL.md +4 -0
  38. package/skills/agent-device/references/snapshot-refs.md +1 -0
  39. package/skills/dogfood/SKILL.md +12 -12
  40. package/skills/dogfood/references/issue-taxonomy.md +5 -5
  41. package/skills/dogfood/templates/dogfood-report-template.md +19 -19
  42. package/dist/src/utils/interactive.d.ts +0 -1
@@ -4,4 +4,5 @@ export { pressAndroid, swipeAndroid, backAndroid, homeAndroid, appSwitcherAndroi
4
4
  export { type AndroidKeyboardState, getAndroidKeyboardState, dismissAndroidKeyboard, readAndroidClipboardText, writeAndroidClipboardText, } from './device-input-state.ts';
5
5
  export { setAndroidSetting } from './settings.ts';
6
6
  export { pushAndroidNotification } from './notifications.ts';
7
- export { snapshotAndroid, screenshotAndroid } from './snapshot.ts';
7
+ export { snapshotAndroid } from './snapshot.ts';
8
+ export { screenshotAndroid } from './screenshot.ts';
@@ -0,0 +1,16 @@
1
+ import type { DeviceInfo } from '../../utils/device.ts';
2
+ type ScreenshotAndroidDeps = {
3
+ enableDemoMode: (device: DeviceInfo) => Promise<void>;
4
+ settle: (ms: number) => Promise<void>;
5
+ capture: (device: DeviceInfo, outPath: string) => Promise<void>;
6
+ disableDemoMode: (device: DeviceInfo) => Promise<void>;
7
+ };
8
+ export declare function screenshotAndroid(device: DeviceInfo, outPath: string, deps?: ScreenshotAndroidDeps): Promise<void>;
9
+ /**
10
+ * Enable Android demo mode and set deterministic time in status bar
11
+ * for consistent screenshots.
12
+ */
13
+ export declare function enableAndroidDemoMode(device: DeviceInfo): Promise<void>;
14
+ /** Disable demo mode and restore the live status bar. */
15
+ export declare function disableAndroidDemoMode(device: DeviceInfo): Promise<void>;
16
+ export {};
@@ -4,5 +4,4 @@ export declare function snapshotAndroid(device: DeviceInfo, options?: SnapshotOp
4
4
  nodes: RawSnapshotNode[];
5
5
  truncated?: boolean;
6
6
  }>;
7
- export declare function screenshotAndroid(device: DeviceInfo, outPath: string): Promise<void>;
8
7
  export declare function dumpUiHierarchy(device: DeviceInfo): Promise<string>;
@@ -2,6 +2,7 @@ export declare const IOS_BOOT_TIMEOUT_MS: number;
2
2
  export declare const IOS_SIMCTL_LIST_TIMEOUT_MS: number;
3
3
  export declare const IOS_APP_LAUNCH_TIMEOUT_MS: number;
4
4
  export declare const IOS_DEVICECTL_TIMEOUT_MS: number;
5
+ export declare const IOS_SIMULATOR_FOCUS_TIMEOUT_MS: number;
5
6
  export declare const IOS_SIMULATOR_SCREENSHOT_TIMEOUT_MS: number;
6
7
  export declare const IOS_RUNNER_SCREENSHOT_COPY_TIMEOUT_MS: number;
7
8
  export declare const IOS_SIMULATOR_SCREENSHOT_RETRY_MAX_ATTEMPTS = 5;
@@ -1,3 +1,3 @@
1
1
  export { closeIosApp, installIosApp, installIosInstallablePath, listIosApps, listSimulatorApps, openIosApp, openIosDevice, pushIosNotification, readIosClipboardText, reinstallIosApp, resolveIosApp, screenshotIos, setIosSetting, uninstallIosApp, writeIosClipboardText, } from './apps.ts';
2
2
  export { ensureBootedSimulator } from './simulator.ts';
3
- export { parseIosDeviceAppsPayload, type IosAppInfo, } from './devicectl.ts';
3
+ export { parseIosDeviceAppsPayload, type IosAppInfo } from './devicectl.ts';
@@ -0,0 +1,2 @@
1
+ import type { DeviceInfo } from '../../utils/device.ts';
2
+ export declare function prepareSimulatorStatusBarForScreenshot(device: DeviceInfo): Promise<() => Promise<void>>;
@@ -1,6 +1,7 @@
1
1
  import type { DeviceInfo } from '../../utils/device.ts';
2
2
  type SimulatorScreenshotFlowDeps = {
3
3
  ensureBooted: (device: DeviceInfo) => Promise<void>;
4
+ prepareStatusBarForScreenshot: (device: DeviceInfo) => Promise<() => Promise<void>>;
4
5
  captureWithRetry: (device: DeviceInfo, outPath: string) => Promise<void>;
5
6
  captureWithRunner: (device: DeviceInfo, outPath: string, appBundleId?: string) => Promise<void>;
6
7
  shouldFallbackToRunner: (error: unknown) => boolean;
@@ -10,4 +11,4 @@ export declare function captureSimulatorScreenshotWithFallback(device: DeviceInf
10
11
  export declare function resolveSimulatorRunnerScreenshotCandidatePaths(containerPath: string, remoteFileName: string): string[];
11
12
  export declare function shouldFallbackToRunnerForIosScreenshot(error: unknown): boolean;
12
13
  export declare function shouldRetryIosSimulatorScreenshot(error: unknown): boolean;
13
- export {};
14
+ export { prepareSimulatorStatusBarForScreenshot } from './screenshot-status-bar.ts';
@@ -35,6 +35,8 @@ export type CliFlags = {
35
35
  snapshotDepth?: number;
36
36
  snapshotScope?: string;
37
37
  snapshotRaw?: boolean;
38
+ baseline?: string;
39
+ threshold?: string;
38
40
  appsFilter?: 'user-installed' | 'all';
39
41
  count?: number;
40
42
  fps?: number;
@@ -23,5 +23,5 @@ type DeviceSelectionContext = {
23
23
  };
24
24
  export declare function normalizePlatformSelector(platform: PlatformSelector | undefined): Platform | undefined;
25
25
  export declare function resolveApplePlatformName(target: DeviceTarget | undefined): 'iOS' | 'tvOS';
26
- export declare function selectDevice(devices: DeviceInfo[], selector: DeviceSelector, context?: DeviceSelectionContext): Promise<DeviceInfo>;
26
+ export declare function resolveDevice(devices: DeviceInfo[], selector: DeviceSelector, context?: DeviceSelectionContext): Promise<DeviceInfo>;
27
27
  export {};
@@ -1,4 +1,5 @@
1
1
  import type { DeviceInfo } from './device.ts';
2
+ import type { PermissionSettingOptions } from '../platforms/permission-utils.ts';
2
3
  export type RunnerContext = {
3
4
  requestId?: string;
4
5
  appBundleId?: string;
@@ -26,6 +27,12 @@ type Interactor = {
26
27
  attempts?: number;
27
28
  } | void>;
28
29
  screenshot(outPath: string, appBundleId?: string): Promise<void>;
30
+ back(): Promise<void>;
31
+ home(): Promise<void>;
32
+ appSwitcher(): Promise<void>;
33
+ readClipboard(): Promise<string>;
34
+ writeClipboard(text: string): Promise<void>;
35
+ setSetting(setting: string, state: string, appId?: string, options?: PermissionSettingOptions): Promise<void>;
29
36
  };
30
37
  export declare function getInteractor(device: DeviceInfo, runnerContext: RunnerContext): Interactor;
31
38
  export {};
@@ -1,7 +1,8 @@
1
1
  import { AppError, type NormalizedError } from './errors.ts';
2
+ import type { ScreenshotDiffResult } from './screenshot-diff.ts';
2
3
  type JsonResult = {
3
4
  success: true;
4
- data?: Record<string, unknown>;
5
+ data?: unknown;
5
6
  } | {
6
7
  success: false;
7
8
  error: {
@@ -22,4 +23,5 @@ export declare function formatSnapshotText(data: Record<string, unknown>, option
22
23
  flatten?: boolean;
23
24
  }): string;
24
25
  export declare function formatSnapshotDiffText(data: Record<string, unknown>): string;
26
+ export declare function formatScreenshotDiffText(data: ScreenshotDiffResult): string;
25
27
  export {};
@@ -0,0 +1,8 @@
1
+ type EnvMap = Record<string, string | undefined>;
2
+ type PathResolutionOptions = {
3
+ cwd?: string;
4
+ env?: EnvMap;
5
+ };
6
+ export declare function expandUserHomePath(inputPath: string, options?: PathResolutionOptions): string;
7
+ export declare function resolveUserPath(inputPath: string, options?: PathResolutionOptions): string;
8
+ export {};
@@ -0,0 +1,23 @@
1
+ export type ScreenshotDimensionMismatch = {
2
+ expected: {
3
+ width: number;
4
+ height: number;
5
+ };
6
+ actual: {
7
+ width: number;
8
+ height: number;
9
+ };
10
+ };
11
+ export type ScreenshotDiffResult = {
12
+ diffPath?: string;
13
+ totalPixels: number;
14
+ differentPixels: number;
15
+ mismatchPercentage: number;
16
+ match: boolean;
17
+ dimensionMismatch?: ScreenshotDimensionMismatch;
18
+ };
19
+ export type ScreenshotDiffOptions = {
20
+ threshold?: number;
21
+ outputPath?: string;
22
+ };
23
+ export declare function compareScreenshots(baselinePath: string, currentPath: string, options?: ScreenshotDiffOptions): Promise<ScreenshotDiffResult>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-device",
3
- "version": "0.8.3",
3
+ "version": "0.8.4",
4
4
  "description": "Unified control plane for physical and virtual devices via an agent-driven CLI.",
5
5
  "license": "MIT",
6
6
  "author": "Callstack",
@@ -20,7 +20,6 @@
20
20
  "agent-device": "bin/agent-device.mjs"
21
21
  },
22
22
  "scripts": {
23
- "lint": "node --eval \"console.log('no lint')\"",
24
23
  "build": "rslib build",
25
24
  "clean:daemon": "rm -f ~/.agent-device/daemon.json && rm -f ~/.agent-device/daemon.lock",
26
25
  "build:node": "pnpm build && pnpm clean:daemon",
@@ -29,7 +28,7 @@
29
28
  "build:xcuitest:tvos": "rm -rf ~/.agent-device/ios-runner/derived/tvos && xcodebuild build-for-testing -project ios-runner/AgentDeviceRunner/AgentDeviceRunner.xcodeproj -scheme AgentDeviceRunner -destination \"generic/platform=tvOS Simulator\" -derivedDataPath ~/.agent-device/ios-runner/derived/tvos",
30
29
  "build:all": "pnpm build:node && pnpm build:xcuitest",
31
30
  "ad": "node bin/agent-device.mjs",
32
- "format": "prettier --write .",
31
+ "format": "prettier --write src test skills",
33
32
  "prepublishOnly": "pnpm build:all",
34
33
  "prepack": "pnpm build:all",
35
34
  "typecheck": "tsc -p tsconfig.json",
@@ -67,11 +66,12 @@
67
66
  "printWidth": 100
68
67
  },
69
68
  "dependencies": {
70
- "@clack/prompts": "^1.0.0"
69
+ "pngjs": "^7.0.0"
71
70
  },
72
71
  "devDependencies": {
73
- "@types/node": "^22.0.0",
74
72
  "@rslib/core": "0.19.4",
73
+ "@types/node": "^22.0.0",
74
+ "@types/pngjs": "^6.0.5",
75
75
  "prettier": "^3.3.3",
76
76
  "typescript": "^5.9.3"
77
77
  }
@@ -155,6 +155,7 @@ curl -sS "${AGENT_DEVICE_DAEMON_BASE_URL}/rpc" \
155
155
  ```
156
156
 
157
157
  Notes:
158
+
158
159
  - `AGENT_DEVICE_DAEMON_BASE_URL` makes the CLI skip local daemon discovery/startup and call the remote HTTP daemon directly.
159
160
  - `AGENT_DEVICE_DAEMON_AUTH_TOKEN` is sent in both the JSON-RPC request token and HTTP auth headers.
160
161
  - In remote daemon mode, `--debug` does not tail a local `daemon.log`; inspect logs on the remote host instead.
@@ -189,6 +190,7 @@ For local iOS QA in mixed simulator/device environments, use `ensure-simulator`
189
190
  For session-bound automation, prefer `AGENT_DEVICE_SESSION` + `AGENT_DEVICE_PLATFORM`; that bound-session default now enables lock mode automatically.
190
191
 
191
192
  Isolation scoping quick reference:
193
+
192
194
  - `--ios-simulator-device-set <path>` scopes iOS simulator discovery + command execution to one simulator set.
193
195
  - `--android-device-allowlist <serials>` scopes Android discovery/selection to comma/space separated serials.
194
196
  - Scope is applied before selectors (`--device`, `--udid`, `--serial`); out-of-scope selectors fail with `DEVICE_NOT_FOUND`.
@@ -196,12 +198,14 @@ Isolation scoping quick reference:
196
198
  - In bound-session `strip` mode, conflicting per-call scope/selectors are ignored and the configured binding is restored for the request. Batch steps still inherit the parent `--platform` when they do not set their own.
197
199
 
198
200
  Simulator provisioning quick reference:
201
+
199
202
  - Use `ensure-simulator` to create or reuse a named iOS simulator inside a device set before starting a session.
200
203
  - `--device <name>` is required (e.g. `"iPhone 16 Pro"`). `--runtime <id>` pins the runtime; omit to use the newest compatible one.
201
204
  - `--boot` boots it immediately. Returns `udid`, `device`, `runtime`, `ios_simulator_device_set`, `created`, `booted`.
202
205
  - Idempotent: safe to call repeatedly; reuses an existing matching simulator by default.
203
206
 
204
207
  TV quick reference:
208
+
205
209
  - AndroidTV: `open`/`apps` use TV launcher discovery automatically.
206
210
  - TV target selection works on emulators/simulators and connected physical devices (AndroidTV + AppleTV).
207
211
  - tvOS: runner-driven interactions and snapshots are supported (`snapshot`, `wait`, `press`, `fill`, `get`, `scroll`, `back`, `home`, `app-switcher`, `record` and related selector flows).
@@ -62,6 +62,7 @@ agent-device diff snapshot -i # Shows +/− structural lines vs prior
62
62
  ```
63
63
 
64
64
  Efficient pattern:
65
+
65
66
  - Initialize once at a stable point.
66
67
  - Mutate UI (`press`, `fill`, `swipe`).
67
68
  - Run `diff snapshot` after interactions to confirm expected change shape with bounded output.
@@ -12,14 +12,14 @@ Systematically explore a mobile app, find issues, and produce a report with full
12
12
 
13
13
  Only the **Target app** is required. Everything else has sensible defaults.
14
14
 
15
- | Parameter | Default | Example override |
16
- |-----------|---------|-----------------|
17
- | **Target app** | _(required)_ | `Settings`, `com.example.app`, deep link URL |
18
- | **Platform** | Infer from user context; otherwise ask (`ios` or `android`) | `--platform ios` |
19
- | **Session name** | Slugified app/platform (for example `settings-ios`) | `--session my-session` |
20
- | **Output directory** | `./dogfood-output/` | `Output directory: /tmp/mobile-qa` |
21
- | **Scope** | Full app | `Focus on onboarding and profile` |
22
- | **Authentication** | None | `Sign in to user@example.com` |
15
+ | Parameter | Default | Example override |
16
+ | -------------------- | ----------------------------------------------------------- | -------------------------------------------- |
17
+ | **Target app** | _(required)_ | `Settings`, `com.example.app`, deep link URL |
18
+ | **Platform** | Infer from user context; otherwise ask (`ios` or `android`) | `--platform ios` |
19
+ | **Session name** | Slugified app/platform (for example `settings-ios`) | `--session my-session` |
20
+ | **Output directory** | `./dogfood-output/` | `Output directory: /tmp/mobile-qa` |
21
+ | **Scope** | Full app | `Focus on onboarding and profile` |
22
+ | **Authentication** | None | `Sign in to user@example.com` |
23
23
 
24
24
  If the user gives enough context to start, begin immediately with defaults. Ask follow-up only when a required detail is missing (for example platform or credentials).
25
25
 
@@ -172,12 +172,12 @@ agent-device --session {SESSION} close
172
172
 
173
173
  ## References
174
174
 
175
- | Reference | When to Read |
176
- |-----------|--------------|
175
+ | Reference | When to Read |
176
+ | ------------------------------------------------------------ | ----------------------------------------------- |
177
177
  | [references/issue-taxonomy.md](references/issue-taxonomy.md) | Start of session; severity/categories/checklist |
178
178
 
179
179
  ## Templates
180
180
 
181
- | Template | Purpose |
182
- |----------|---------|
181
+ | Template | Purpose |
182
+ | ---------------------------------------------------------------------------- | --------------------------------------------- |
183
183
  | [templates/dogfood-report-template.md](templates/dogfood-report-template.md) | Copy into output directory as the report file |
@@ -4,12 +4,12 @@ Reference for categorizing issues found during mobile dogfooding.
4
4
 
5
5
  ## Severity Levels
6
6
 
7
- | Severity | Definition |
8
- |----------|------------|
7
+ | Severity | Definition |
8
+ | ------------ | ------------------------------------------------------------------------- |
9
9
  | **critical** | Blocks a core workflow, causes data loss, or crashes/freeze loops the app |
10
- | **high** | Major feature broken or unusable, no practical workaround |
11
- | **medium** | Feature works with notable friction or partial failure; workaround exists |
12
- | **low** | Minor cosmetic or polish issue |
10
+ | **high** | Major feature broken or unusable, no practical workaround |
11
+ | **medium** | Feature works with notable friction or partial failure; workaround exists |
12
+ | **low** | Minor cosmetic or polish issue |
13
13
 
14
14
  ## Categories
15
15
 
@@ -1,21 +1,21 @@
1
1
  # Dogfood Report: {APP_NAME}
2
2
 
3
- | Field | Value |
4
- |-------|-------|
5
- | **Date** | {DATE} |
6
- | **Platform** | {PLATFORM} |
7
- | **Target App** | {TARGET_APP} |
8
- | **Session** | {SESSION_NAME} |
9
- | **Scope** | {SCOPE} |
3
+ | Field | Value |
4
+ | -------------- | -------------- |
5
+ | **Date** | {DATE} |
6
+ | **Platform** | {PLATFORM} |
7
+ | **Target App** | {TARGET_APP} |
8
+ | **Session** | {SESSION_NAME} |
9
+ | **Scope** | {SCOPE} |
10
10
 
11
11
  ## Summary
12
12
 
13
- | Severity | Count |
14
- |----------|-------|
15
- | Critical | 0 |
16
- | High | 0 |
17
- | Medium | 0 |
18
- | Low | 0 |
13
+ | Severity | Count |
14
+ | --------- | ----- |
15
+ | Critical | 0 |
16
+ | High | 0 |
17
+ | Medium | 0 |
18
+ | Low | 0 |
19
19
  | **Total** | **0** |
20
20
 
21
21
  ## Issues
@@ -24,12 +24,12 @@
24
24
 
25
25
  ### ISSUE-001: {Short title}
26
26
 
27
- | Field | Value |
28
- |-------|-------|
29
- | **Severity** | critical / high / medium / low |
30
- | **Category** | visual / functional / ux / content / performance / diagnostics / permissions / accessibility |
31
- | **Screen / Route** | {screen where issue was found} |
32
- | **Repro Video** | {path to video, or N/A for static issues} |
27
+ | Field | Value |
28
+ | ------------------ | -------------------------------------------------------------------------------------------- |
29
+ | **Severity** | critical / high / medium / low |
30
+ | **Category** | visual / functional / ux / content / performance / diagnostics / permissions / accessibility |
31
+ | **Screen / Route** | {screen where issue was found} |
32
+ | **Repro Video** | {path to video, or N/A for static issues} |
33
33
 
34
34
  **Description**
35
35
 
@@ -1 +0,0 @@
1
- export declare function isInteractive(): boolean;