Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
65 commits
Select commit Hold shift + click to select a range
a2ecbaa
refactor(provider-webdriver): share hub upload and app-reference helpers
amankansal-lt Oct 2, 2026
364bfe7
fix(provider-webdriver): bound and type session-details lookups
amankansal-lt Oct 2, 2026
465a0a7
feat(provider-webdriver): add TestMu AI emulator and simulator provider
amankansal-lt Oct 2, 2026
95c21f7
feat(provider-webdriver): support TestMu AI real devices
amankansal-lt Oct 2, 2026
add8062
fix(provider-webdriver): upload the archive of materialized iOS builds
amankansal-lt Oct 2, 2026
abf7a0a
docs: add the TestMu AI device cloud guide
amankansal-lt Oct 2, 2026
2228167
Merge pull request #1 from amankansal-lt/feat/testmu-ai-provider
anurag-lambdatest Oct 2, 2026
5495cfa
fix(daemon): start a lease's TTL when its allocation completes
amankansal-lt Oct 2, 2026
313a7d7
refactor(provider-webdriver): declare the profile fields each provide…
amankansal-lt Oct 2, 2026
9b5e818
fix(provider-webdriver): let the Apple materializer name the uploadab…
amankansal-lt Oct 2, 2026
402370e
fix(provider-webdriver): tighten app-reference, endpoint and upload i…
amankansal-lt Oct 2, 2026
12bab7a
test(provider-webdriver): exercise in-flight deadlines and aborts for…
amankansal-lt Oct 2, 2026
0d70437
fix(provider-webdriver): refuse profile fields on a repeat allocation…
amankansal-lt Oct 2, 2026
80aa05f
fix(provider-webdriver): validate and canonicalize app references at …
amankansal-lt Oct 2, 2026
720e892
fix(provider-webdriver): refuse a directory before any hosted upload
amankansal-lt Oct 2, 2026
417ff5b
fix(cli): refuse a non-URL install-from-source source with a typed error
amankansal-lt Oct 2, 2026
a696834
Merge pull request #2 from amankansal-lt/fix/testmu-review-followups
amankansal-lt Oct 2, 2026
42644ce
Merge remote-tracking branch 'origin/main' into migrate/testmu-plugin
thymikee Oct 3, 2026
4b81820
feat: support provider-owned plugin connections and WebDriver adapters
thymikee Oct 3, 2026
f3e6d04
chore(gates): expose the optional WebDriver plugin configuration
thymikee Oct 3, 2026
f2b8172
feat: share plugin connection and WebDriver adapter infrastructure
thymikee Oct 3, 2026
5f362bc
feat: load TestMu from its separately installed provider plugin
thymikee Oct 3, 2026
1175906
chore(gates): declare TestMu plugin build dependencies and analysis e…
thymikee Oct 3, 2026
d6bee90
fix: retain plugin tests through development-only package exports
thymikee Oct 3, 2026
48fef3b
chore(gates): track TestMu packaging fixtures and workspace test depe…
thymikee Oct 3, 2026
c33719a
chore(gates): smoke the optional packed WebDriver plugin SDK
thymikee Oct 3, 2026
ec53bcb
perf: load the shared WebDriver engine on demand
thymikee Oct 3, 2026
3632ba8
chore(gates): bound the optional plugin SDK declarations
thymikee Oct 3, 2026
915e572
fix: consolidate TestMu plugin helper imports
thymikee Oct 3, 2026
fd86378
Merge callstack/main into the TestMu plugin migration
amankansal-lt Oct 5, 2026
0cc1af4
fix(testmu): trim credentials and log URLs, canonicalize lt:// everyw…
amankansal-lt Oct 5, 2026
229bf7e
docs(testmu): export credentials in help and note the fixed app listing
amankansal-lt Oct 5, 2026
c553ed7
fix(kernel): require a real Error behind the AppError brand
amankansal-lt Oct 5, 2026
2b2370f
fix(plugins): refuse a primitive factory result as an invalid plugin
amankansal-lt Oct 5, 2026
1d52862
fix(plugins): archive into a fresh zip, not over a stale one
amankansal-lt Oct 5, 2026
694e4d5
fix(connect): look plugin capabilities up in the caller's environment
amankansal-lt Oct 5, 2026
aab5684
fix(kernel): type the AppError brand lookup on a narrowed Error
amankansal-lt Oct 5, 2026
7279a63
refactor(plugins): reserve provider ids from one list
amankansal-lt Oct 5, 2026
acf35f4
refactor(plugins): name the WebDriver factory result check
amankansal-lt Oct 5, 2026
027b848
chore(fallow): stop suppressing statically imported TestMu helpers
amankansal-lt Oct 5, 2026
244c131
test: allocate through a WebDriver plugin and assert typed ownership
amankansal-lt Oct 5, 2026
62e814c
fix(connect): refuse a WebDriver plugin's refused fields before resolve
amankansal-lt Oct 5, 2026
cee110c
docs(testing): say when to run the packed provider plugin check
amankansal-lt Oct 5, 2026
f3f05e0
docs: document the plugin package check in its script, not testing.md
amankansal-lt Oct 5, 2026
3986976
revert: let resolve and allocate own plugin profile-field refusal
amankansal-lt Oct 5, 2026
5e1319d
revert: keep the AppError brand check as it was
amankansal-lt Oct 5, 2026
6ad12eb
revert: drop the plugin host archive fix with the apple host block
amankansal-lt Oct 5, 2026
e881abd
refactor(plugins): remove the unused apple block from the plugin host
amankansal-lt Oct 5, 2026
b7456b4
refactor(webdriver): one owner for lease-flag and credential readers
amankansal-lt Oct 5, 2026
add0994
refactor(testmu): import session and device-feature modules statically
amankansal-lt Oct 5, 2026
ffb76e0
build(testmu): typecheck the plugin against core source, not dist
amankansal-lt Oct 5, 2026
789f997
chore(fallow): credit the testmu plugin's type-only agent-device link
amankansal-lt Oct 5, 2026
8b20167
Merge callstack/main into the TestMu plugin migration
amankansal-lt Oct 5, 2026
16669ec
Merge callstack/main: let #3168 own the profile-field admission
amankansal-lt Oct 5, 2026
891df76
test(testmu): assert only Limrun's device-type refusal at connect
amankansal-lt Oct 5, 2026
11b40ec
refactor(providers): share the lower-case scheme rule for hub app ref…
amankansal-lt Oct 5, 2026
6b19fde
refactor(provider-webdriver): export the plugin's webdriver-utils rea…
amankansal-lt Oct 5, 2026
15c3b48
Merge callstack/agent-device main into testmu-plugin-merge
amankansal-lt Oct 5, 2026
ada0ae9
fix(providers): refuse a lease when the daemon holds other plugin cre…
amankansal-lt Oct 6, 2026
c44bef8
Merge callstack/agent-device main into testmu-plugin-merge
amankansal-lt Oct 6, 2026
2041269
refactor(plugins): split manifest connection and credential checks
amankansal-lt Oct 6, 2026
06a37ac
chore: update TestMu plugin with workspace release infrastructure
thymikee Oct 6, 2026
7045310
docs: explain installation of the TestMu package
thymikee Oct 6, 2026
77579cf
chore(gates): align TestMu package with workspace releases
thymikee Oct 6, 2026
5db1dd8
docs: explain daemon restart after plugin installation
thymikee Oct 6, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 15 additions & 1 deletion .fallowrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@
"$schema": "https://raw.githubusercontent.com/fallow-rs/fallow/main/schema.json",
"entry": [
"tsdown.config.ts",
"scripts/check-provider-plugin.mjs",
"packages/provider-testmu/test/package-smoke.mjs",
"packages/provider-testmu/tsdown.config.ts",
"packages/provider-testmu/src/plugin.ts",
"vitest.mutation.config.ts",
"src/sdk/index.ts",
"src/sdk/io.ts",
Expand All @@ -10,6 +14,7 @@
"src/sdk/remote-config.ts",
"src/sdk/install-source.ts",
"src/sdk/plugins.ts",
"src/sdk/plugin-webdriver.ts",
"src/sdk/android-adb.ts",
"src/sdk/contracts.ts",
"src/sdk/selectors.ts",
Expand Down Expand Up @@ -56,6 +61,10 @@
// @agent-device/provider-limrun owns the source import, while the published
// root build externalizes @limrun/api and retains runtime imports in packed chunks.
"@limrun/api",
// @agent-device/testmu imports agent-device type-only, and its tsconfig maps those specifiers
// through this workspace link to core source so typecheck needs no build; Fallow then credits
// the source files rather than the package.
"agent-device",
// Oxlint resolves the shared config's JS plugins dynamically from their package names.
"@nkzw/eslint-plugin",
"eslint-plugin-no-only-tests",
Expand All @@ -66,6 +75,11 @@
"@arethetypeswrong/cli"
],
"ignoreExports": [
{
"comment": "The standalone packed-install harness loads this fixture from the selected package path and reads its request scenarios dynamically.",
"file": "packages/provider-*/test/package-smoke.mjs",
"exports": ["*"]
},
{
"comment": "Android perf mechanics are selected through the lazy platform host so importing agent-device does not eagerly load adb mechanics. Fallow cannot follow the dynamic property read in src/platform-runtime-perf-host.ts.",
"file": "packages/platform-android/src/perf.ts",
Expand Down Expand Up @@ -287,7 +301,7 @@
},
{
"comment": "Tool config default exports, loaded by the tool rather than imported.",
"file": "{oxlint.config.ts,tsdown.config.ts,vitest.mutation.config.ts,website/rspress.config.ts}",
"file": "{oxlint.config.ts,tsdown.config.ts,packages/provider-*/tsdown.config.ts,vitest.mutation.config.ts,website/rspress.config.ts}",
"exports": ["default"]
},
{
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -137,15 +137,15 @@ The same session and evidence model works at every step: the agent explores the
| --- | --- | --- |
| Local | Trying commands and debugging apps on simulators, emulators, physical devices, macOS, and Linux. | Follow the Quick Start. |
| CI/CD | Automated pull request and merge validation with replay scripts and captured artifacts. | Try the [EAS workflow template](https://github.com/callstackincubator/eas-agent-device/blob/main/.eas/workflows/agent-qa-mobile.yml). |
| Cloud / remote | Linux runners, managed devices, and remote jobs. | Set up a [remote proxy](https://oss.callstack.com/agent-device/docs/remote-proxy), connect a [device cloud](https://oss.callstack.com/agent-device/docs/device-clouds) (BrowserStack, AWS Device Farm, Limrun), or [contact Callstack](mailto:hello@callstack.com) for team QA. |
| Cloud / remote | Linux runners, managed devices, and remote jobs. | Set up a [remote proxy](https://oss.callstack.com/agent-device/docs/remote-proxy), connect a [device cloud](https://oss.callstack.com/agent-device/docs/device-clouds) (BrowserStack, AWS Device Farm, TestMu AI, Limrun), or [contact Callstack](mailto:hello@callstack.com) for team QA. |

## How it works

`agent-device` keeps device state in sessions. It uses a local accessibility bridge for iOS Simulator snapshots and XCTest for iOS interactions, physical iOS, and tvOS; ADB and the snapshot helper on Android; HDC and ArkUI `uitest` on HarmonyOS; Vega CLI/VDA on the Vega Virtual Device; a local helper on macOS; and AT-SPI on Linux.

Support depth varies by target. Newer backends such as HarmonyOS and Vega OS cover a subset of commands; run `agent-device capabilities --platform <platform>` to see what a target supports.

Sessions are scoped to the caller's git worktree, and host-local device claims stop parallel agents from taking over each other's simulators and emulators. Inspect ownership without a daemon via `agent-device device status`, and settle provably dead owners with `agent-device device release --stale`. The same commands drive hosted devices on [BrowserStack, AWS Device Farm, and Limrun](https://oss.callstack.com/agent-device/docs/device-clouds).
Sessions are scoped to the caller's git worktree, and host-local device claims stop parallel agents from taking over each other's simulators and emulators. Inspect ownership without a daemon via `agent-device device status`, and settle provably dead owners with `agent-device device release --stale`. The same commands drive hosted devices on [BrowserStack, AWS Device Farm, TestMu AI, and Limrun](https://oss.callstack.com/agent-device/docs/device-clouds).

`agent-device` uses the inspect-act-verify process from Vercel's [agent-browser](https://github.com/vercel-labs/agent-browser) for mobile, TV, and desktop apps. Basic `--platform web` support runs `agent-browser` in the same session and replay system.

Expand Down
9 changes: 7 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,10 @@
"./plugins": {
"types": "./dist/src/plugins.d.ts",
"import": "./dist/src/plugins.js"
},
"./plugins/webdriver": {
"types": "./dist/src/plugins/webdriver.d.ts",
"import": "./dist/src/plugins/webdriver.js"
}
},
"engines": {
Expand Down Expand Up @@ -175,7 +179,7 @@
"check:unit": "pnpm test:unit && pnpm check:tmpdir-leaks && pnpm test:smoke",
"check": "pnpm check:tooling && pnpm check:fallow && pnpm check:unit",
"prepack": "npm_config_dry_run=false pnpm release:prepare",
"typecheck": "tsc -b packages/xml packages/kernel packages/contracts packages/device-selection packages/host-kit packages/capture-kit packages/managed-allocation packages/provision-kit packages/platform-apple packages/platform-android packages/platform-harmonyos packages/platform-vega packages/platform-linux packages/platform-web packages/ad-script packages/selectors packages/command-registry packages/session-journal packages/ad-replay packages/maestro packages/replay-port packages/replay-test packages/provider-webdriver packages/provider-limrun && tsc -p tsconfig.json && tsc -p examples/sdk/tsconfig.json",
"typecheck": "tsc -b packages/xml packages/kernel packages/contracts packages/device-selection packages/host-kit packages/capture-kit packages/managed-allocation packages/provision-kit packages/platform-apple packages/platform-android packages/platform-harmonyos packages/platform-vega packages/platform-linux packages/platform-web packages/ad-script packages/selectors packages/command-registry packages/session-journal packages/ad-replay packages/maestro packages/replay-port packages/replay-test packages/provider-webdriver packages/provider-limrun packages/provider-testmu && 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",
Expand Down Expand Up @@ -341,6 +345,7 @@
"vite": "^8.2.1",
"vitest": "^4.1.11",
"yaml": "^2.9.0",
"yauzl": "^3.4.0"
"yauzl": "^3.4.0",
"@agent-device/testmu": "workspace:*"
}
}
18 changes: 16 additions & 2 deletions packages/command-registry/src/flag-definitions-connection.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import { LEASE_BACKENDS } from '@agent-device/kernel/contracts';
import { PROVIDER_DEVICE_ORIENTATIONS } from '@agent-device/contracts/remote';
import {
PROVIDER_DEVICE_ORIENTATIONS,
PROVIDER_DEVICE_TYPES,
} from '@agent-device/contracts/remote';
import type { FlagDefinition } from './flag-types.ts';

export const CONNECTION_FLAG_DEFINITIONS: readonly FlagDefinition[] = [
Expand Down Expand Up @@ -175,6 +178,17 @@ export const CONNECTION_FLAG_DEFINITIONS: readonly FlagDefinition[] = [
projectConfig: false,
recorded: false,
},
{
key: 'providerDeviceType',
names: ['--provider-device-type'],
type: 'enum',
enumValues: PROVIDER_DEVICE_TYPES,
usageLabel: '--provider-device-type real|virtual',
usageDescription:
'TestMu AI device pool: real devices or virtual devices (emulators and simulators). Defaults to virtual',
projectConfig: false,
recorded: false,
},
{
key: 'providerProject',
names: ['--provider-project'],
Expand Down Expand Up @@ -237,7 +251,7 @@ export const CONNECTION_FLAG_DEFINITIONS: readonly FlagDefinition[] = [
type: 'string',
usageLabel: '--provider-appium-version <version>',
usageDescription:
'Hosted cloud provider Appium server version, for example 3.2.0. Without it BrowserStack falls back to its default (Appium 1.x)',
'Hosted cloud provider Appium server version, for example 3.2.0. Without it each provider starts its own default (Appium 1.x on BrowserStack)',
projectConfig: false,
recorded: false,
},
Expand Down
1 change: 1 addition & 0 deletions packages/command-registry/src/flag-groups.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ export const COMMON_COMMAND_SUPPORTED_FLAG_KEYS = flagKeys(
'device',
'providerApp',
'providerOsVersion',
'providerDeviceType',
'providerProject',
'providerBuild',
'providerSessionName',
Expand Down
2 changes: 2 additions & 0 deletions packages/contracts/src/__tests__/lease-scope.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,7 @@ test('readLeaseAllocateProviderFlags carries the provider-allocation flags and d
device: 'iPhone 15',
providerApp: 'bs://abc',
providerOsVersion: '17',
providerDeviceType: 'real',
providerProject: 'MyProject',
providerBuild: 'Build-1',
providerSessionName: 'smoke',
Expand All @@ -220,6 +221,7 @@ test('readLeaseAllocateProviderFlags carries the provider-allocation flags and d
device: 'iPhone 15',
providerApp: 'bs://abc',
providerOsVersion: '17',
providerDeviceType: 'real',
providerProject: 'MyProject',
providerBuild: 'Build-1',
providerSessionName: 'smoke',
Expand Down
1 change: 1 addition & 0 deletions packages/contracts/src/client-connection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ export type AgentDeviceRequestOverrides = Pick<
| 'clientId'
| 'providerApp'
| 'providerOsVersion'
| 'providerDeviceType'
| 'providerProject'
| 'providerBuild'
| 'providerSessionName'
Expand Down
3 changes: 3 additions & 0 deletions packages/contracts/src/facades/remote.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,20 @@ export type {
ResolvedMetroKind,
} from '../metro.ts';
export type {
ConnectionProviderCapabilities,
ProviderConnectionResource,
ProviderConnectionVerification,
} from '../provider-connection.ts';
export {
PROVIDER_DEVICE_ORIENTATIONS,
PROVIDER_DEVICE_TYPES,
PROVIDER_PROFILE_FIELD_FLAG_ALIASES,
PROVIDER_PROFILE_FIELD_FLAGS,
} from '../remote-config-fields.ts';
export type {
CloudProviderProfileFields,
ProviderDeviceOrientation,
ProviderDeviceType,
RemoteConfigMetroOptions,
RemoteConnectionProfileFields,
} from '../remote-config-fields.ts';
1 change: 1 addition & 0 deletions packages/contracts/src/lease-scope.ts
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,7 @@ const LEASE_ALLOCATE_PROVIDER_FLAG_KEYS = [
// The Cloud provider profile fields; pinned exhaustive against that vocabulary below.
'providerApp',
'providerOsVersion',
'providerDeviceType',
'providerProject',
'providerBuild',
'providerSessionName',
Expand Down
10 changes: 10 additions & 0 deletions packages/contracts/src/provider-connection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,13 @@ export type ProviderConnectionVerification = {
device: ProviderConnectionResource;
app: ProviderConnectionResource;
};

export type ConnectionProviderCapabilities = {
leaseKind: 'proxy' | 'direct-device-provider' | 'remote-provider';
requiresAppAttachment: boolean;
requiresRemoteDaemon: boolean;
supportsArtifacts: boolean;
supportsDeferredAppSelection: boolean;
supportsDirectPortReverse: boolean;
usesCloudWebDriverLease: boolean;
};
1 change: 1 addition & 0 deletions packages/contracts/src/provider-profile-fields.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ const DECLARATION: ProviderProfileFieldDeclaration = {
fields: {
providerApp: 'consumed',
providerOsVersion: 'refused',
providerDeviceType: 'consumed',
providerProject: 'consumed',
providerBuild: 'consumed',
providerSessionName: 'consumed',
Expand Down
6 changes: 6 additions & 0 deletions packages/contracts/src/remote-config-fields.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,14 @@ import type { MetroPrepareKind } from './metro.ts';
export const PROVIDER_DEVICE_ORIENTATIONS = ['portrait', 'landscape'] as const;
export type ProviderDeviceOrientation = (typeof PROVIDER_DEVICE_ORIENTATIONS)[number];

/** Device pool a hosted provider session is created in: physical devices or emulators/simulators. */
export const PROVIDER_DEVICE_TYPES = ['real', 'virtual'] as const;
export type ProviderDeviceType = (typeof PROVIDER_DEVICE_TYPES)[number];

export type CloudProviderProfileFields = {
providerApp?: string;
providerOsVersion?: string;
providerDeviceType?: ProviderDeviceType;
providerProject?: string;
providerBuild?: string;
providerSessionName?: string;
Expand All @@ -48,6 +53,7 @@ export const PROVIDER_PROFILE_FIELD_FLAGS: Readonly<
> = {
providerApp: '--provider-app',
providerOsVersion: '--provider-os-version',
providerDeviceType: '--provider-device-type',
providerProject: '--provider-project',
providerBuild: '--provider-build',
providerSessionName: '--provider-session-name',
Expand Down
17 changes: 17 additions & 0 deletions packages/kernel/src/errors.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -178,3 +178,20 @@ test('discloseDispatchAfterSteps keeps no only while no step of the series was d
const plain = new Error('socket closed');
assert.equal(discloseDispatchAfterSteps(plain, 4), plain);
});

test('bundled plugin errors preserve codes and details without changing subclass checks', async () => {
const copyPath = './errors.ts?plugin-copy';
const { AppError: PluginError } = await import(copyPath);
assert.notEqual(PluginError, AppError);
const foreign = new PluginError('INVALID_ARGS', 'bad plugin profile', { provider: 'example' });
assert.ok(foreign instanceof AppError);
const normalized = normalizeError(foreign);
assert.equal(normalized.code, 'INVALID_ARGS');
assert.equal(normalized.message, 'bad plugin profile');
assert.deepEqual(normalized.details, { provider: 'example' });
const ordinary = Object.assign(new Error('bad plugin profile'), { code: 'INVALID_ARGS' });
assert.equal(ordinary instanceof AppError, false);
class SpecificError extends AppError {}
assert.ok(new SpecificError('COMMAND_FAILED', 'specific') instanceof SpecificError);
assert.equal(foreign instanceof SpecificError, false);
});
38 changes: 25 additions & 13 deletions packages/kernel/src/errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,31 @@ export type KnownAppErrorCode = (typeof KNOWN_APP_ERROR_CODES)[number];
// include a default branch.
export type AppErrorCode = KnownAppErrorCode | (string & {});

const APP_ERROR_BRAND = Symbol.for('agent-device.AppError');

export class AppError extends Error {
static [Symbol.hasInstance](value: unknown): boolean {
if (this !== AppError) return Function.prototype[Symbol.hasInstance].call(this, value);
return (
typeof value === 'object' &&
Comment thread
amankansal-lt marked this conversation as resolved.
value !== null &&
(value as Record<symbol, unknown>)[APP_ERROR_BRAND] === true
);
}

code: AppErrorCode;
details?: AppErrorDetails;
cause?: unknown;

constructor(code: AppErrorCode, message: string, details?: AppErrorDetails, cause?: unknown) {
super(message);
Object.defineProperty(this, APP_ERROR_BRAND, { value: true });
this.code = code;
this.details = details;
this.cause = cause;
}
}

export function toAppErrorCode(
code: string | undefined,
fallback: AppErrorCode = 'COMMAND_FAILED',
Expand Down Expand Up @@ -219,19 +244,6 @@ export type DaemonError = {
supportedOn?: string;
};

export class AppError extends Error {
code: AppErrorCode;
details?: AppErrorDetails;
cause?: unknown;

constructor(code: AppErrorCode, message: string, details?: AppErrorDetails, cause?: unknown) {
super(message);
this.code = code;
this.details = details;
this.cause = cause;
}
}

/** Rehydrate a daemon transport error into the error type used by local callers. */
export function throwDaemonError(error: DaemonError): never {
throw new AppError(
Expand Down
1 change: 1 addition & 0 deletions packages/provider-limrun/src/device.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export const LIMRUN_PROFILE_FIELDS: ProviderProfileFieldDeclaration = {
fields: {
providerApp: 'consumed',
Comment thread
amankansal-lt marked this conversation as resolved.
providerOsVersion: 'refused',
providerDeviceType: 'refused',
providerProject: 'refused',
providerBuild: 'refused',
providerSessionName: 'refused',
Expand Down
17 changes: 17 additions & 0 deletions packages/provider-testmu/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# @agent-device/testmu

Use TestMu AI Android and iOS virtual and real devices with [agent-device](https://agent-device.dev).
You need a TestMu AI account and its API credentials.

```sh
npm install -g agent-device
agent-device plugins add @agent-device/testmu
export LT_USERNAME=your-username
export LT_ACCESS_KEY=your-access-key
agent-device connect testmu --platform ios --device "iPhone 16" --provider-os-version 18.0 --provider-app ./MyApp.zip
```

See the [TestMu AI guide](https://agent-device.dev/docs/testmu) for setup and supported operations.
To update the plugin, run `agent-device plugins update @agent-device/testmu`.

After adding or updating a plugin, close your sessions and run `agent-device daemon stop` before reconnecting.
Loading
Loading