@agent-device/testmu 0.0.0-stage → 0.21.24

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.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Callstack
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,17 @@
1
- # Temporary Holding Version
1
+ # @agent-device/testmu
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Use TestMu AI Android and iOS virtual and real devices with [agent-device](https://agent-device.dev).
4
+ You need a TestMu AI account and its API credentials.
5
+
6
+ ```sh
7
+ npm install -g agent-device
8
+ agent-device plugins add @agent-device/testmu
9
+ export LT_USERNAME=your-username
10
+ export LT_ACCESS_KEY=your-access-key
11
+ agent-device connect testmu --platform ios --device "iPhone 16" --provider-os-version 18.0 --provider-app ./MyApp.zip
12
+ ```
13
+
14
+ See the [TestMu AI guide](https://oss.callstack.com/agent-device/docs/testmu) for setup and supported operations.
15
+ To update the plugin, run `agent-device plugins update @agent-device/testmu`.
16
+
17
+ After you add or update the plugin, close your sessions and run `agent-device daemon stop` before reconnecting.
@@ -0,0 +1 @@
1
+ import e from"node:fs";import t,{readFile as n,stat as r}from"node:fs/promises";import i from"node:path";const a=[`portrait`,`landscape`],o=[`real`,`virtual`],s={providerApp:`--provider-app`,providerOsVersion:`--provider-os-version`,providerDeviceType:`--provider-device-type`,providerProject:`--provider-project`,providerBuild:`--provider-build`,providerSessionName:`--provider-session-name`,providerDeviceOrientation:`--provider-device-orientation`,providerGeoLocation:`--provider-geo-location`,providerTimezone:`--provider-timezone`,providerAppiumVersion:`--provider-appium-version`,providerLanguage:`--provider-language`,providerLocale:`--provider-locale`,providerNetworkProfile:`--provider-network-profile`,providerCustomNetwork:`--provider-custom-network`,providerNoResignApp:`--provider-no-resign-app`,awsProjectArn:`--aws-project-arn`,awsDeviceArn:`--aws-device-arn`,awsAppArn:`--aws-app-arn`,awsRegion:`--aws-region`,awsInteractionMode:`--aws-interaction-mode`},c={providerOsVersion:[`--os-version`],providerDeviceOrientation:[`--device-orientation`],providerGeoLocation:[`--geo-location`],providerTimezone:[`--timezone`],providerAppiumVersion:[`--appium-version`],providerLanguage:[`--language`],providerLocale:[`--locale`],providerNetworkProfile:[`--network-profile`],providerCustomNetwork:[`--custom-network`]},l=Symbol.for(`agent-device.AppError`);var u=class e extends Error{static[Symbol.hasInstance](t){return this===e?typeof t==`object`&&!!t&&t[l]===!0:Function.prototype[Symbol.hasInstance].call(this,t)}code;details;cause;constructor(e,t,n,r){super(t),Object.defineProperty(this,l,{value:!0}),this.code=e,this.details=n,this.cause=r}};function d(e){return{"x-agent-device-client":`agent-device-cli`,"x-agent-device-version":e}}function f(e){return`Basic ${Buffer.from(`${e.username}:${e.accessKey}`).toString(`base64`)}`}function p(e){let t=e.length-1;for(;t>=0&&e.charCodeAt(t)===47;)--t;return t===e.length-1?e:e.slice(0,t+1)}function m(e,t){let n=new URL(e);return n.pathname=`${p(n.pathname)}/${t}`,n}async function ee(e,t,a){if(!(await r(e).catch(()=>void 0))?.isFile())throw new u(`INVALID_ARGS`,`${a.service} can only upload a regular app file: ${e}`,{provider:a.provider,appPath:e,hint:`Use an existing .ipa, .apk, or .aab file, or a .zip of the iOS simulator .app bundle.`});let o=new FormData;return o.set(t,new Blob([await n(e)]),i.basename(e)),o}async function h(e,t,n){let r=await fetch(t.endpoint,{method:`POST`,headers:{...d(t.clientVersion),Authorization:f(t.auth)},body:e,signal:n}),i=await y(r),a=t.readAppReference(i)?.trim();if(!r.ok||!a)throw new u(`COMMAND_FAILED`,`${t.service} app upload failed.`,{status:r.status,response:i});return a}function te(e){return async({appPath:t,options:n,signal:r})=>({appReference:await e(t,r),bundleId:n?.appIdentifierHint,packageName:n?.packageNameHint,launchTarget:n?.appIdentifierHint??n?.packageNameHint})}async function ne(t){let{app:n}=t,r=t.parseReference(n);if(r!==void 0)return r;if(/^https?:\/\//i.test(n))return n;let a=i.resolve(t.cwd??process.cwd(),n);if(!e.existsSync(a))throw new u(`INVALID_ARGS`,`${t.service} --provider-app must be ${t.referenceLabel}, URL, or existing local app path.`,{providerApp:n});return await t.uploadFile(a,t.signal)}const g=15e3;async function _(e,t){let{service:n,unauthorizedHint:r,networkHint:i}=t.hints,a=`Retry connect or check the ${n} service status.`;try{let i=await fetch(e,{headers:{...d(t.clientVersion),...t.auth?{Authorization:f(t.auth)}:{}},signal:AbortSignal.timeout(g)});if(!i.ok){let e=i.status===401||i.status===403;throw new u(e?`UNAUTHORIZED`:`COMMAND_FAILED`,`${n} rejected connection verification.`,{status:i.status,hint:e?r:a})}let o=await y(i);if(o===void 0)throw new u(`COMMAND_FAILED`,`${n} connection verification answer was not JSON.`,{status:i.status,hint:a});return o}catch(e){throw e instanceof u?e:new u(`COMMAND_FAILED`,`${n} connection verification failed.`,{hint:i},e)}}async function v(e,t){let n,r;try{n=await fetch(e,{headers:{...d(t.clientVersion),Authorization:f(t.auth)},signal:AbortSignal.timeout(g)}),r=await y(n)}catch(e){throw new u(`COMMAND_FAILED`,`${t.service} session details lookup failed.`,{hint:`Check network access to the ${t.service} API, then retry.`},e)}let i=r&&typeof r==`object`&&!Array.isArray(r)?r:void 0;if(!n.ok||!i)throw new u(`COMMAND_FAILED`,`${t.service} session details lookup failed.`,{status:n.status,response:r});return i}async function y(e){let t=await e.text();if(t.length!==0)try{return JSON.parse(t)}catch{return}}function re(e,t){let n=a.find(e=>e===t);if(n)return n;throw new u(`INVALID_ARGS`,`Invalid ${e.flag} value: ${t}.`,{hint:`Use ${a.join(`|`)}.`,flag:e.flag,capability:e.capability})}function ie(e,t){if(e)return e;throw new u(`INVALID_ARGS`,`${t} lease allocation requires provider profile flags on the request.`)}function ae(e,t){let n=e.flags?.platform;if(n===`android`||n===`ios`)return n;throw new u(`INVALID_ARGS`,`${t} requires --platform ios|android.`)}function b(e,t,n){let r=x(e,t);if(r)return r;throw new u(`INVALID_ARGS`,n)}function x(e,t){let n=e.flags?.[t];return typeof n==`string`&&n.length>0?n:void 0}function S(e,t,n){let r=e[t];if(r?.trim())return r;throw new u(`INVALID_ARGS`,`${n} requires ${t} in the environment.`)}function C(e,t){if(e.platform===`android`||e.platform===`ios`)return e.platform;throw new u(`INVALID_ARGS`,`connect ${t} requires --platform ios|android.`)}function w(e,t,n){if(e?.trim())return e;throw new u(`INVALID_ARGS`,`connect ${t} requires ${n}.`)}function T(t,n,r){let a=i.resolve(n,t);if(e.statSync(a,{throwIfNoEntry:!1})?.isFile())return a;throw new u(`INVALID_ARGS`,`${r} app file not found: ${a}`)}function E(e){return{provider:e.provider,providerSessionId:e.providerSessionId,status:e.artifacts.length>0?`ready`:`pending`,cloudArtifacts:e.artifacts,...e.artifacts.length>0?{}:{message:e.pendingMessage}}}function D(e,t,n,r,i,a){let o=n[r],s=typeof o==`string`?o.trim():``;if(s.length!==0)return{provider:e,providerSessionId:t,kind:i,name:a,url:s,availability:`ready`}}function oe(e,t,n={}){return{platformName:e===`ios`?`iOS`:`Android`,"appium:deviceName":t,...n}}function se(e,t){let n=Object.keys(t.fields).filter(n=>t.fields[n]===`refused`&&ue(e?.[n]));if(n.length===0)return;let r=n.length!==1;throw new u(`INVALID_ARGS`,`${n.map(le).join(`, `)} ${r?`are`:`is`} not supported by ${t.label}.`,{hint:`Drop ${r?`those flags`:`the flag`} or use a provider that supports ${r?`them`:`it`}.`,provider:t.provider,flags:n.map(e=>s[e])})}function O(e,t){if(e!==void 0)return e;throw new u(`COMMAND_FAILED`,t,{hint:`Reconnect to regenerate and validate the provider profile.`})}function ce(e,t){if(e===`android`||e===`ios`)return e;throw new u(`COMMAND_FAILED`,`${t} profile missed a mobile platform.`,{hint:`Reconnect with --platform ios|android.`})}function le(e){let t=c[e],n=s[e];return t?.length?`${n} (${t.join(`, `)})`:n}function ue(e){return e!=null&&e!==!1&&e!==``}const k={browserStack:`browserstack`,awsDeviceFarm:`aws-device-farm`},de={username:`BROWSERSTACK_USERNAME`,accessKey:`BROWSERSTACK_ACCESS_KEY`},A={leaseKind:`direct-device-provider`,requiresAppAttachment:!1,requiresRemoteDaemon:!1,supportsArtifacts:!0,supportsDeferredAppSelection:!1,supportsDirectPortReverse:!1,usesCloudWebDriverLease:!0};Object.freeze([{provider:k.browserStack,connection:A,credentialVariables:Object.values(de)},{provider:k.awsDeviceFarm,connection:{...A,requiresAppAttachment:!0}}]);function j(e,t){if(e.slice(0,t.length).toLowerCase()===t)return`${t}${e.slice(t.length)}`}function M(e,t){return{username:S(e,`LT_USERNAME`,t),accessKey:S(e,`LT_ACCESS_KEY`,t)}}function N(e){return/^lt:\/\/[\w.-]+$/.test(e)}function P(e){return j(e,`lt://`)??e}function F(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function I(e){return F(e)?e:void 0}const L={real:`https://manual-api.lambdatest.com/app/upload/realDevice`,virtual:`https://manual-api.lambdatest.com/app/upload/virtualDevice`};async function R(e,t,n){return t?E({provider:e,providerSessionId:t,artifacts:he(e,t,await me(t,n)),pendingMessage:`TestMu AI artifacts are not ready yet.`}):void 0}async function z(e,n,r){if(r?.throwIfAborted(),!(await t.stat(e).catch(()=>void 0))?.isFile())throw new u(`INVALID_ARGS`,`TestMu AI can only upload an app file: ${e}`,{appPath:e,hint:n.deviceType===`real`?`Real iOS devices install a signed .ipa; pass the .ipa file.`:`Zip the .app bundle of an iOS simulator build and pass the .zip.`});let a=await ee(e,`appFile`,{provider:`testmu`,service:`TestMu AI`});return a.set(`name`,i.parse(e).name),await V(a,n,r)}async function B(e,t,n){n?.throwIfAborted();let r=new FormData;return r.set(`url`,e),r.set(`storage`,`url`),r.set(`name`,i.basename(new URL(e).pathname)||`app`),await V(r,t,n)}async function V(e,t,n){return await h(e,{service:`TestMu AI`,endpoint:t.endpoint??L[t.deviceType??`virtual`],clientVersion:t.clientVersion,auth:t,readAppReference:ge},n)}function H(e){return te(async(t,n)=>await z(t,e,n))}async function fe(e,t){let n=W(e);return n===void 0?/^https?:\/\//i.test(e)?await B(e,t,t.signal):await ne({service:`TestMu AI`,app:e,cwd:t.cwd,referenceLabel:`an lt:// app id`,parseReference:W,uploadFile:async(e,n)=>await z(e,t,n),signal:t.signal}):n}const U=`lt://`;function W(e){let t=P(e);if(t.startsWith(U)){if(N(t))return t;throw new u(`INVALID_ARGS`,`TestMu AI --provider-app ${e} is not an lt:// app id.`,{providerApp:e})}}function pe(e){let{"lt:options":t,...n}=e.configured??{},r=e.deviceFeatures??{};return{"appium:deviceName":e.deviceName,"appium:platformVersion":e.osVersion,...e.app?{"appium:app":e.app}:{},...n,"lt:options":{platformName:e.platform===`ios`?`iOS`:`Android`,deviceName:e.deviceName,platformVersion:e.osVersion,...e.app?{app:e.app}:{},...e.projectName?{project:e.projectName}:{},build:e.buildName,name:e.sessionName,video:!0,devicelog:!0,...r,...I(t)??{},isRealMobile:e.deviceType===`real`,w3c:!0}}}function G(e){let t=P(e);return t.startsWith(U)?t:`${U}${e}`}async function me(e,t){let n=m(t.endpoint??`https://mobile-api.lambdatest.com/mobile-automation/api/v1`,`sessions/${encodeURIComponent(e)}`),r;try{r=await v(n,{clientVersion:t.clientVersion,auth:t,service:`TestMu AI`})}catch(e){if(e instanceof u&&e.details?.status===404)return{};throw e}let i=I(r.data);if(!i)throw new u(`COMMAND_FAILED`,`TestMu AI session details response had no data.`,{response:r});return i}function he(e,t,n){let r=[[`video_url`,`video`,`Session video`],[`appium_logs_url`,`appium-log`,`Appium logs`],[typeof n.console_logs_url==`string`&&n.console_logs_url.trim().length>0?`console_logs_url`:`device_logs_url`,`device-log`,`Device logs`],[`network_logs_url`,`raw`,`Network logs`],[`command_logs_url`,`automation-log`,`Command logs`],[`screenshot_url`,`raw`,`Screenshots`]].map(([r,i,a])=>D(e,t,n,r,i,a)),i={provider:e,providerSessionId:t,kind:`provider-session`,name:`TestMu AI dashboard`,url:`https://appautomation.lambdatest.com/test?testID=${encodeURIComponent(t)}`,availability:`ready`},a=r.filter(e=>e!==void 0);return a.length>0?[...a,i]:[]}function ge(e){let{app_url:t,app_id:n}=I(e)??{},r=typeof t==`string`?P(t):void 0;if(r&&N(r))return r;if(typeof n!=`string`)return;let i=G(n);return N(i)?i:void 0}const _e={real:{android:`android`,ios:`ios`},virtual:{android:`emulator`,ios:`simulator`}};async function ve(e,t){let n={username:e.username,accessKey:e.accessKey},r=e.deviceType??`virtual`,i=e.devicesEndpoint?new URL(e.devicesEndpoint):m(e.apiEndpoint??`https://mobile-api.lambdatest.com/mobile-automation/api/v1`,`capability/generator`);i.searchParams.set(`isVirtualDevice`,String(r===`virtual`));let a=be(await K(i,void 0,t),e.platform,r).filter(t=>t.name===e.deviceName),o=a.find(t=>t.osVersions.includes(e.osVersion));if(!o){let t=[...new Set(a.flatMap(e=>e.osVersions))].sort((e,t)=>e.localeCompare(t,void 0,{numeric:!0}));throw new u(`INVALID_ARGS`,`TestMu AI ${r} device "${e.deviceName}" with ${e.platform} ${e.osVersion} is not available${t.length>0?`; ${e.deviceName} offers ${t.join(`, `)}`:``}.`,{hint:`Choose an exact device name and OS version from the TestMu AI ${r}-device capability generator.`,deviceType:r,...t.length>0?{availableOsVersions:t}:{}})}let s=await ye(e,r,n,t);return{provider:`testmu`,service:`TestMu AI`,verificationMessage:s.status===`verified`?`Credentials, ${r} device, and uploaded app verified.`:`Credentials and ${r} device verified; app availability is checked when the session is created.`,device:{status:`verified`,name:o.name,platform:e.platform,osVersion:e.osVersion},app:s}}async function ye(e,t,n,r){let{app:a}=e,o=new URL(e.appsEndpoint??`https://manual-api.lambdatest.com/app/data`);o.searchParams.set(`type`,_e[t][e.platform]),o.searchParams.set(`level`,`user`);let s=await K(o,n,r);if(N(a)){let e=q(s).find(e=>e.reference===a);return e?{status:`verified`,...e}:{status:`configured`,reference:a,message:`App reference was not found among your ${t}-device uploads; TestMu AI validates it when creating the session.`}}return/^https?:\/\//i.test(a)?{status:`configured`,reference:a,message:`Public app URL configured; TestMu AI fetches it when creating the session.`}:{status:`configured`,name:i.basename(a),reference:a,message:`Local app artifact is ready and will be uploaded when creating the session.`}}async function K(e,t,n){return await _(e,{clientVersion:n,auth:t,hints:{service:`TestMu AI`,unauthorizedHint:`Check LT_USERNAME and LT_ACCESS_KEY.`,networkHint:`Check network access to mobile-api.lambdatest.com and manual-api.lambdatest.com, then retry connect.`}})}function be(e,t,n){let r=I(I(n===`real`?I(e)?.[t]:I(I(I(e)?.app)?.devices)?.[t])?.brands);if(!r)throw new u(`COMMAND_FAILED`,`TestMu AI ${n}-device catalog response did not list devices for the platform.`,{platform:t,deviceType:n});return Object.values(r).flatMap(e=>Array.isArray(e)?e.flatMap(e=>{let t=I(e);if(!t||typeof t.name!=`string`||!Array.isArray(t.osVersion))return[];let n=t.osVersion.flatMap(e=>typeof e==`string`||typeof e==`number`?[String(e)]:[]);return[{name:t.name,osVersions:n}]}):[])}function q(e){let t=I(e)?.data;if(!Array.isArray(t))throw new u(`COMMAND_FAILED`,`TestMu AI app listing response was not a list.`);return t.flatMap(e=>{let t=I(e);return!t||typeof t.app_id!=`string`?[]:[{reference:G(t.app_id),...typeof t.name==`string`?{name:t.name}:{},...typeof t.version==`string`?{version:t.version}:{}}]})}const J=[{field:`providerDeviceOrientation`,capability:`deviceOrientation`,flag:`--provider-device-orientation`,project:e=>e.toUpperCase()},{field:`providerGeoLocation`,capability:`geoLocation`,flag:`--provider-geo-location`},{field:`providerTimezone`,capability:`timezone`,flag:`--provider-timezone`},{field:`providerAppiumVersion`,capability:`appiumVersion`,flag:`--provider-appium-version`},{field:`providerLanguage`,capability:`language`,flag:`--provider-language`},{field:`providerLocale`,capability:`locale`,flag:`--provider-locale`}];function xe(e){let t={};for(let n of J){let r=e[n.field];r!==void 0&&r!==``&&(t[n.capability]=n.project?n.project(r):r)}return t}function Y(e){let t={};for(let n of J){let r=e?.[n.field];if(typeof r==`string`&&r.length!==0){if(n.field===`providerDeviceOrientation`){t.providerDeviceOrientation=re(n,r);continue}t[n.field]=r}}return t}function X(e){let t=e?.providerDeviceType;if(t===void 0||t===``)return`virtual`;let n=o.find(e=>e===t);if(n)return n;throw new u(`INVALID_ARGS`,`Invalid --provider-device-type value: ${String(t)}.`,{hint:`Use ${o.join(`|`)}.`,flag:`--provider-device-type`})}const Z=`testmu`,Q=`TestMu AI`;function Se(e,t){return{resolve:({flags:n,cwd:r})=>{se(n,t),M(e.env,`connect ${Z}`);let i=C(n,Z),a=Ce(w(n.providerApp,Z,`--provider-app <lt://app-id, URL, or local path>`),r,e);return{profile:{leaseProvider:Z,platform:i,device:w(n.device,Z,`--device <name>`),providerOsVersion:w(n.providerOsVersion,Z,`--provider-os-version <version>`),providerApp:a,providerDeviceType:X(n),providerProject:n.providerProject,providerBuild:n.providerBuild,providerSessionName:n.providerSessionName,...Y(n)},extraFlags:{providerApp:a}}},verify:async({flags:t})=>await ve({provider:Z,...M(e.env,`connect ${Z}`),platform:ce(t.platform,Q),deviceName:O(t.device,`TestMu AI profile missed device.`),osVersion:O(t.providerOsVersion,`TestMu AI profile missed OS version.`),app:P(O(t.providerApp,`TestMu AI profile missed app.`)),deviceType:X(t),apiEndpoint:e.env.TESTMU_API_ENDPOINT},e.clientVersion)}}function Ce(e,t,n){let r=P(e);if(r.startsWith(`lt://`)){if(N(r))return r;throw n.createError(`INVALID_ARGS`,`connect testmu requires a valid lt:// app reference.`)}return/^https?:\/\//i.test(r)?r:T(r,t,Q)}const we={install:{support:`partial`,note:`Local app artifacts are uploaded to TestMu AI as real- or virtual-device apps (lt://), then installed with Appium.`},portReverse:{support:`unsupported`,note:`Use the TestMu AI tunnel for network access to local hosts; agent-device port reverse is not available.`},artifacts:{support:`supported`,note:`TestMu AI session details expose provider-hosted video, Appium logs, device logs, network logs, and dashboard links.`}},$={provider:`testmu`,label:`TestMu AI`,fields:{providerApp:`consumed`,providerOsVersion:`consumed`,providerDeviceType:`consumed`,providerProject:`consumed`,providerBuild:`consumed`,providerSessionName:`consumed`,providerDeviceOrientation:`consumed`,providerGeoLocation:`consumed`,providerTimezone:`consumed`,providerAppiumVersion:`consumed`,providerLanguage:`consumed`,providerLocale:`consumed`,providerNetworkProfile:`refused`,providerCustomNetwork:`refused`,providerNoResignApp:`refused`,awsProjectArn:`refused`,awsDeviceArn:`refused`,awsAppArn:`refused`,awsRegion:`refused`,awsInteractionMode:`refused`}};function Te(e){let t=e.env;async function n(t,n,r){return await R(t,n,{clientVersion:e.clientVersion,...M(r,`TestMu AI artifact lookup`),endpoint:r.TESTMU_API_ENDPOINT})}return{webDriver:{provider:`testmu`,profileFields:$,platform:`android`,deviceName:`TestMu AI device`,endpoint:t.TESTMU_WEBDRIVER_ENDPOINT??`https://mobile-hub.lambdatest.com/wd/hub/`,capabilityOverrides:we,listArtifacts:async({provider:e,providerSessionId:r})=>await n(e,r,t),prepareSession:async({req:n,lease:r,base:i})=>{let a=ie(n,`TestMu AI`),o=X(a.flags),s=Ee(t,o),c=M(t,`TestMu AI`),l=ae(a,`TestMu AI`),u=b(a,`device`,`TestMu AI requires --device <name>.`),d=b(a,`providerOsVersion`,`TestMu AI requires --provider-os-version <version>.`),f={clientVersion:e.clientVersion,...c,deviceType:o,endpoint:s},p=await fe(b(a,`providerApp`,`TestMu AI requires --provider-app <lt://app-id, URL, or local path>.`),{...f,cwd:a.cwd,signal:a.signal});return{...i,platform:l,deviceName:u,auth:c,uploadApp:H(f),webdriverCapabilities:pe({platform:l,deviceType:o,deviceName:u,osVersion:d,app:p,projectName:x(a,`providerProject`),buildName:x(a,`providerBuild`)??r.runId,sessionName:x(a,`providerSessionName`)??r.leaseId,deviceFeatures:xe(Y(a.flags)),configured:oe(l,u)})}}},connection:Se(e,$)}}function Ee(e,t){return t===`real`?e.TESTMU_REAL_DEVICE_APP_UPLOAD_ENDPOINT:e.TESTMU_APP_UPLOAD_ENDPOINT}export{Te as default};
package/package.json CHANGED
@@ -1,6 +1,51 @@
1
1
  {
2
2
  "name": "@agent-device/testmu",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
3
+ "version": "0.21.24",
4
+ "type": "module",
5
+ "description": "TestMu AI real and virtual mobile device plugin for agent-device.",
6
+ "files": [
7
+ "dist"
8
+ ],
9
+ "exports": {
10
+ ".": {
11
+ "import": "./dist/plugin.mjs"
12
+ }
13
+ },
14
+ "devDependencies": {
15
+ "@agent-device/contracts": "0.0.0",
16
+ "agent-device": "0.21.24",
17
+ "@agent-device/kernel": "0.0.0",
18
+ "@agent-device/provider-webdriver": "0.0.0"
19
+ },
20
+ "agentDevicePlugin": {
21
+ "apiVersion": 1,
22
+ "provider": "testmu",
23
+ "entry": "./dist/plugin.mjs",
24
+ "connection": {
25
+ "leaseKind": "direct-device-provider",
26
+ "requiresAppAttachment": false,
27
+ "requiresRemoteDaemon": false,
28
+ "supportsArtifacts": true,
29
+ "supportsDeferredAppSelection": false,
30
+ "supportsDirectPortReverse": false,
31
+ "usesCloudWebDriverLease": true
32
+ },
33
+ "credentialVariables": [
34
+ "LT_USERNAME",
35
+ "LT_ACCESS_KEY"
36
+ ]
37
+ },
38
+ "publishConfig": {
39
+ "access": "public"
40
+ },
41
+ "license": "MIT",
42
+ "repository": {
43
+ "type": "git",
44
+ "url": "git+https://github.com/callstack/agent-device.git",
45
+ "directory": "packages/provider-testmu"
46
+ },
47
+ "homepage": "https://oss.callstack.com/agent-device/docs/testmu",
48
+ "scripts": {
49
+ "build": "tsdown --config tsdown.config.ts"
50
+ }
6
51
  }