@taqwright/lime-cli 0.11.0 → 0.11.1

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/README.md CHANGED
@@ -278,6 +278,35 @@ flow on a cloud device, adapts the test into your **test-suite** repo, verifies
278
278
  and opens a pull request there linking back. LIME holds no GitHub credential — the runner
279
279
  already has yours.
280
280
 
281
+ On **GitLab CI/CD**, `docs/gitlab/automate.yml` is the same loop, started from a manual job
282
+ rather than a comment — GitLab CI cannot be triggered by a comment from `.gitlab-ci.yml`
283
+ alone. See [GitLab CI/CD](#gitlab-cicd) below.
284
+
285
+ ### GitLab CI/CD
286
+
287
+ `docs/gitlab/` (ask Taqwright for a copy) holds three ready-made templates and a guide:
288
+
289
+ | | |
290
+ |---|---|
291
+ | `preflight.yml` | **Run this first.** Proves a runner can reach LIME at all — outbound HTTPS, WebSocket upgrade through the egress proxy, and whether the package proxy serves our scoped packages. About five minutes, with no device, no licence and no data of yours. The job log is the deliverable |
292
+ | `taqwright.yml` | Runs the generated suite as a pipeline step and reports the results to the LIME board |
293
+ | `automate.yml` | The whole loop — record, adapt, verify, open a merge request |
294
+ | `README.md` | The guide, including a GovTech SHIP-HATS 2.0 section and a GitHub Actions → GitLab map |
295
+
296
+ **One GitLab-specific trap, because it fails silently.** `--run-id`, `--build-marker` and
297
+ `--pr` default from GitHub's environment (`$GITHUB_RUN_ID`, `$GITHUB_REF`), which is unset on
298
+ GitLab. Nothing errors — the run just lands on the board anonymous, and with no run id to
299
+ match on, cloud session videos cannot be linked to their tests. Pass
300
+ `--run-id "$CI_PIPELINE_ID"`, and `--pr "$CI_MERGE_REQUEST_IID"` on a merge-request pipeline.
301
+
302
+ **And one thing worth knowing before promising an end-to-end run.** `lime-cli` as a pipeline
303
+ step is undemanding — one JavaScript file, no runtime dependencies, HTTPS only. Reaching a
304
+ *device* from that step is a separate question, and on a shared or unprivileged runner the
305
+ answer is usually no: an Android emulator needs `/dev/kvm` and so a privileged container, and
306
+ iOS needs macOS. The two routes that do work are a cloud device (BrowserStack or LambdaTest,
307
+ which runs fine on a shared runner) or a runner the team provisions itself. `docs/gitlab/README.md`
308
+ sets out both, and what each costs.
309
+
281
310
  ### `report` — send a finished suite's results back
282
311
 
283
312
  LIME generated the specs; your CI owns them from then on, and runs them without LIME in the
@@ -368,7 +397,9 @@ an unreachable server all warn and exit 0 — a reporting problem must not becom
368
397
  pipeline on top of whatever actually happened.
369
398
 
370
399
  `docs/github/report.yml` (ask Taqwright for a copy) is a ready-made workflow with the
371
- matrix, other-runner and other-CI variants commented in.
400
+ matrix, other-runner and other-CI variants commented in. For GitLab, `docs/gitlab/taqwright.yml`
401
+ is the equivalent — mind the `--run-id` note in [GitLab CI/CD](#gitlab-cicd), which is the one
402
+ difference that degrades the board without saying so.
372
403
 
373
404
  ## Server API contract
374
405
 
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- "use strict";var $=(e,t)=>()=>{try{return t||e((t={exports:{}}).exports,t),t.exports}catch(n){throw t=0,n}};var Pe=$((qi,Dn)=>{Dn.exports={name:"@taqwright/lime-cli",version:"0.11.0",description:"Thin CLI client for the LIME server \u2014 fire a natural-language QA Agent goal against a connected device and write back a taqwright spec.",license:"SEE LICENSE IN LICENSE",author:"Taqwright",homepage:"https://www.taqwright.ai",bugs:{url:"https://www.taqwright.ai/contact"},keywords:["appium","mobile-testing","e2e","qa","test-automation","taqwright","lime","cli"],bin:{"lime-cli":"dist/index.js"},main:"dist/index.js",files:["dist"],publishConfig:{access:"public"},scripts:{"lime-cli":"node src/index.js",start:"node src/index.js",test:"node --test test/*.test.js",bundle:"esbuild src/index.js --bundle --platform=node --target=node18 --minify --outfile=dist/index.js",prepack:"npm run bundle",prepublishOnly:"npm test"},engines:{node:">=18"},devDependencies:{esbuild:"^0.28.1"}}});var P=$((Ui,De)=>{"use strict";var Mn="https://www.taqwright.ai";De.exports={POLL_INTERVAL_MS:1e3,POLL_TIMEOUT_MS:18e5,BOOT_POLL_INTERVAL_MS:5e3,BOOT_TIMEOUT_MS:3e5,CONNECT_POLL_INTERVAL_MS:2e3,CONNECT_TIMEOUT_MS:3e5,DEFAULT_SERVER:Mn,HTTP_TIMEOUT_MS:3e5,MAX_RESPONSE_BYTES:52428800}});var z=$((Bi,Fe)=>{"use strict";var{URL:qn}=require("url"),{HTTP_TIMEOUT_MS:Me,MAX_RESPONSE_BYTES:qe}=P();function Ue(e){let t=String(e||"").replace(/^\[|\]$/g,"").toLowerCase();return t==="localhost"||t==="127.0.0.1"||t==="::1"||t.endsWith(".localhost")}var Un=["just a moment","attention required! | cloudflare","checking your browser","/cdn-cgi/challenge-platform","cf-error-details","cf-browser-verification","_incapsula_resource"];function Be(e,t){if(!t||typeof t!="string")return null;let n=t.slice(0,4e3).toLowerCase();return Un.some(r=>n.includes(r))?`the LIME server is behind a bot challenge that blocked this request (HTTP ${e}). The request never reached LIME \u2014 it was stopped at the edge, so your token and arguments are probably fine. CI runners use datacenter IPs, which Cloudflare's Bot Fight Mode challenges by default; it cannot be excepted with a WAF skip rule, so the server's operator has to turn it off or move to Super Bot Fight Mode (which supports skip rules) for the /api/ paths.`:null}var ue=class{constructor(t,n){this.server=t,this.token=n}_url(t){return/^https?:\/\//i.test(t)?t:`${this.server}${t}`}json(t,n,r,s={}){let i=r?JSON.stringify(r):null,o={...s};return i&&(o["Content-Type"]="application/json",o["Content-Length"]=Buffer.byteLength(i)),this._send(t,this._url(n),o,i)}raw(t,n,r,s={}){let i={...s,"Content-Type":"application/octet-stream","Content-Length":r.length};return this._send(t,this._url(n),i,r)}_send(t,n,r,s){let i=new qn(n);if(i.protocol==="http:"&&!Ue(i.hostname))return Promise.reject(new Error(`refusing to send your LIME_CI_TOKEN over cleartext http to ${i.hostname} \u2014 use https (http is allowed only for localhost).`));let o=i.protocol==="https:"?require("https"):require("http"),a={method:t,hostname:i.hostname,port:i.port||(i.protocol==="https:"?443:80),path:i.pathname+(i.search||""),headers:{...r,Authorization:`Bearer ${this.token}`,Origin:`${i.protocol}//${i.host}`}};return new Promise((l,d)=>{let c=o.request(a,u=>{let p="",h=0;u.setEncoding("utf8"),u.on("data",y=>{if(h+=Buffer.byteLength(y),h>qe){c.destroy(new Error(`Server response exceeded ${qe} bytes`));return}p+=y}),u.on("end",()=>{let y;try{y=p?JSON.parse(p):{}}catch{let b=Be(u.statusCode,p);return d(new Error(b||`Server returned non-JSON (${u.statusCode}): ${p.substring(0,200)}`))}l({status:u.statusCode,body:y})})});c.setTimeout(Me,()=>{c.destroy(new Error(`request timed out after ${Me/1e3}s`))}),c.on("error",d),s&&c.write(s),c.end()})}};function Bn(e,t){if(!e.body||!e.body.success)throw new Error(`${t} \u2014 ${e.body&&e.body.error||"unknown"}`);return e.body}Fe.exports={isLoopbackHost:Ue,HttpClient:ue,expectSuccess:Bn,describeEdgeBlock:Be}});var D=$((Fi,Ke)=>{"use strict";var Fn={browserstack:{label:"BrowserStack",envUsername:"BROWSERSTACK_USERNAME",envAccessKey:"BROWSERSTACK_ACCESS_KEY",hubHost:"hub.browserstack.com"},lambdatest:{label:"LambdaTest",envUsername:"LT_USERNAME",envAccessKey:"LT_ACCESS_KEY",hubHost:"mobile-hub.lambdatest.com"}};function Kn({provider:e,platform:t,device:n,os:r,appUrl:s,username:i,accessKey:o,capabilities:a}){let l={connectionMode:"cloud",cloudProvider:e||"browserstack",cloudUsername:i,cloudAccessKey:o,platform:t,deviceName:n,deviceOsVersion:r,cloudAppUrl:s,automationName:t==="ios"?"XCUITest":"UiAutomator2"};return a&&(l.cloudCapabilities=a),l}Ke.exports={CLOUD_PROVIDERS:Fn,buildCloudSettingsPayload:Kn}});var fe=$((Ki,Ve)=>{"use strict";var Z=require("fs"),pe=require("path"),Hn=["taqwright","webdriverio-js","appium-java-junit5","appium-python-pytest","appium-ruby-rspec","appium-csharp-nunit"],Vn={taqwright:"deterministic","webdriverio-js":"gemini","appium-java-junit5":"gemini","appium-python-pytest":"gemini","appium-ruby-rspec":"gemini","appium-csharp-nunit":"gemini"},j={taqwright:"taqwright (native)","webdriverio-js":"WebdriverIO (JavaScript)","appium-java-junit5":"Appium Java (JUnit 5)","appium-python-pytest":"Appium Python (pytest)","appium-ruby-rspec":"Appium Ruby (RSpec)","appium-csharp-nunit":"Appium C# (NUnit)"},Wn=5*1024*1024,he={exists:e=>Z.existsSync(e),readText:e=>{try{return Z.statSync(e).size>Wn?null:Z.readFileSync(e,"utf8")}catch{return null}},list:e=>{try{return Z.readdirSync(e)}catch{return[]}}};function Yn(e,t){let n=e.readText(pe.join(t,"package.json"));if(!n)return{};try{let r=JSON.parse(n);return{...r.dependencies||{},...r.devDependencies||{}}}catch{return{}}}var Gn=new Set(["node_modules","dist","build","target","bin","obj","vendor","out","coverage"]);function Jn(e,t){let n=new Set([""]),r=[];try{r=e.list(t)}catch{r=[]}for(let s of r)s.startsWith(".")||Gn.has(s)||n.add(s);return[...n]}function Xn(e,t,n){let r=n?pe.join(t,n):t,s=u=>pe.join(r,u),i=u=>n?`${n}/${u}`:u,o=[],a=Yn(e,r),l=[];try{l=e.list(r)}catch{l=[]}(a["@taqwright/taqwright"]||a.taqwright||e.exists(s("taqwright.config.ts"))||e.exists(s("taqwright.config.js")))&&o.push({id:"taqwright",label:j.taqwright,reason:"taqwright dependency/config found",manifest:i("package.json")});let d=l.some(u=>/^wdio\.conf\.(js|ts|mjs|cjs)$/.test(u));(a.webdriverio||a["@wdio/cli"]||d)&&o.push({id:"webdriverio-js",label:j["webdriverio-js"],reason:d?"wdio.conf.* found":"webdriverio dependency found",manifest:i("package.json")});for(let u of["pom.xml","build.gradle","build.gradle.kts"]){let p=e.readText(s(u));if(p&&/io\.appium|java-client/.test(p)){o.push({id:"appium-java-junit5",label:j["appium-java-junit5"],reason:`io.appium:java-client in ${i(u)}`,manifest:i(u)});break}}for(let u of["requirements.txt","pyproject.toml"]){let p=e.readText(s(u));if(p&&/appium-python-client/i.test(p)){o.push({id:"appium-python-pytest",label:j["appium-python-pytest"],reason:`Appium-Python-Client in ${i(u)}`,manifest:i(u)});break}}let c=e.readText(s("Gemfile"));c&&/appium_lib(_core)?/.test(c)&&o.push({id:"appium-ruby-rspec",label:j["appium-ruby-rspec"],reason:"appium_lib in Gemfile",manifest:i("Gemfile")});for(let u of l){if(!/\.csproj$/i.test(u))continue;let p=e.readText(s(u));if(p&&/Appium\.WebDriver/i.test(p)){o.push({id:"appium-csharp-nunit",label:j["appium-csharp-nunit"],reason:`Appium.WebDriver in ${i(u)}`,manifest:i(u)});break}}return o}function He(e,t=he){let n=new Map;for(let r of Jn(t,e))for(let s of Xn(t,e,r))n.has(s.id)||n.set(s.id,s);return[...n.values()]}function zn(e,t=he){let n=He(e,t);return n.some(r=>r.id==="taqwright")||n.push({id:"taqwright",label:j.taqwright,reason:"default output"}),n}function Zn(e){let{requested:t,configured:n,remembered:r,offeredIds:s,detectedRealIds:i,taqwrightDetected:o,javaSignal:a}=e,l=d=>!!d&&s.includes(d);return l(t)?t:l(n)?n:l(r)&&(i.includes(r)||r==="taqwright"&&o)?r:i.length?i[0]:o?"taqwright":a?"appium-java-junit5":"taqwright"}Ve.exports={FRAMEWORK_IDS:Hn,FRAMEWORK_ENGINES:Vn,FRAMEWORK_LABELS:j,nodeFs:he,detectFrameworks:He,rankFrameworks:zn,pickDefaultFramework:Zn}});var Ee=$((Hi,Xe)=>{"use strict";var me=require("path"),{nodeFs:Qn}=fe(),We={taqwright:"tests","webdriverio-js":"test/specs","appium-java-junit5":"src/test/java","appium-python-pytest":"tests","appium-ruby-rspec":"spec","appium-csharp-nunit":"Tests"},er="tests",tr={taqwright:".spec.ts","webdriverio-js":".spec.ts","appium-java-junit5":".java","appium-python-pytest":".py","appium-ruby-rspec":"_spec.rb","appium-csharp-nunit":".cs"},nr=new Set(["appium-python-pytest","appium-ruby-rspec"]);function ge(e){return We[e]||er}function we(e){let t=(e||"").split(/[^A-Za-z0-9]+/).filter(Boolean);return t.length?t.map(n=>/^[A-Z0-9]+$/.test(n)?n[0]+n.slice(1).toLowerCase():n[0].toUpperCase()+n.slice(1)).join(""):""}function ye(e){return(e||"").replace(/([a-z0-9])([A-Z])/g,"$1_$2").split(/[^A-Za-z0-9]+/).filter(Boolean).join("_").toLowerCase()}function rr(e){return ye(e).split("_").filter(Boolean).join("-")}var sr=6,ir=new Set(["and","or","the","a","an","to","of","in","on","at","as","with","for","then","into","from","by","that","is"]),or=/@|\d{6,}/;function ar(e){let t=String(e||"").trim().split(/\s+/).filter(Boolean).filter(n=>!or.test(n)).slice(0,sr);for(;t.length>1&&ir.has(t[t.length-1].toLowerCase());)t.pop();return we(t.join(" "))||"RecordedTest"}function cr(e){let t=String(e||"").trim();return t?/[A-Za-z]/.test(t)?t.length>80?"Keep the name under 80 characters.":/[/\\]/.test(t)?"Use the destination field for the folder.":null:"Use at least one letter.":"Enter a test name."}function lr(e){if(e==null||e==="")return null;let t=String(e);return t!==t.trim()?"must not have leading or trailing spaces.":t.length>120?"is longer than any key LIME mints (120 characters).":/^[a-z0-9][a-z0-9._/:-]*$/.test(t)?t.includes("..")?'must not contain "..".':null:"is not a scenario key \u2014 expected lower-case letters, digits and . _ - / : only."}function Ye(e,t){let n;return nr.has(e)?n=ye(t):n=we(t),`${n}${tr[e]||""}`}function K(e){return String(e||"").split(/[/\\]+/).filter(t=>t&&t!=="."&&t!=="..").join("/")}function dr(e){if(e==null)return null;let t=String(e);return t.trim()===""?null:/^([A-Za-z]:)?[/\\]/.test(t)?"must be a path inside the project, not an absolute path.":t.split(/[/\\]+/).includes("..")?'must not contain ".." \u2014 the test belongs inside the project.':null}function be(e){let t=K(e).split("/");return t.pop(),t.join("/")}function Ge(e){let t=/(?:^|\/)src\/(?:test|main)\/java\/(.+)$/.exec(K(e));return t&&t[1].split("/").filter(Boolean).join(".")||void 0}function ur(e){let t=K(e),n=t.search(/\/src\/(?:test|main)\/java(?:\/|$)/);return n>0?t.slice(0,n):""}function pr(e){let t=new Map;for(let r of e){let s=be(r);t.set(s,(t.get(s)||0)+1)}if(!t.size)return null;let n=[...t.entries()].sort((r,s)=>s[1]-r[1]||r[0].localeCompare(s[0]));return{dir:n[0][0],count:n[0][1]}}var Je={taqwright:{segment:"tests",file:/\.spec\.(ts|js)$/},"webdriverio-js":{segment:"test/specs",file:/\.(ts|js)$/},"appium-java-junit5":{segment:"src/test/java",file:/\.java$/},"appium-python-pytest":{segment:"tests",file:/^(test_.+|.+_test)\.py$/},"appium-ruby-rspec":{segment:"spec",file:/_spec\.rb$/},"appium-csharp-nunit":{segment:"",file:/Tests\.cs$/}},hr=new Set(["node_modules","dist","build","target","bin","obj","vendor","out","coverage",".git"]),fr=2e4;function mr(e,t,n=Qn){let r=Je[t];if(!r)return[];let s=[],i=fr,o=a=>{if(i<=0)return;let l=a?me.join(e,a):e,d=[];try{d=n.list(l)}catch{return}for(let c of d){if(i--<=0)return;if(c.startsWith(".")||hr.has(c))continue;let u=a?`${a}/${c}`:c;if(n.isDir?n.isDir(me.join(e,u)):n.list(me.join(e,u)).length>0){o(u);continue}r.file.test(c)&&(r.segment&&!`/${be(u)}/`.includes(`/${r.segment}/`)||s.push(u))}};return o(""),s.sort()}function gr(e){let{framework:t,testName:n}=e,r=Ye(t,n),s=(a,l,d=0)=>{let c=K(a),u=t==="appium-java-junit5"?Ge(c):void 0;return{dir:c,fileName:r,...u?{packageName:u}:{},source:l,neighbourCount:d}};if(e.remembered!=null)return s(e.remembered,"remembered");let i=pr(e.existingTests||[]);if(i&&i.dir)return s(i.dir,"existing",i.count);let o=e.detection&&e.detection.manifest?be(e.detection.manifest):"";return o?s(`${o}/${ge(t)}`,"module"):s(ge(t),"convention")}Xe.exports={CONVENTIONAL_DIRS:We,TEST_PATTERNS:Je,conventionalDir:ge,pascalCase:we,snakeCase:ye,kebabCase:rr,testNameFromGoal:ar,validateTestName:cr,validateDest:dr,validateScenarioKey:lr,fileNameFor:Ye,normalizeDir:K,javaPackageOf:Ge,moduleRootOf:ur,findExistingTests:mr,resolveExportTarget:gr}});var et=$((Vi,Qe)=>{"use strict";var{CLOUD_PROVIDERS:Q}=D(),{validateScenarioKey:wr}=Ee();function yr(e){let t=Object.create(null);for(let n=0;n<e.length;n++){let r=e[n];if(!r.startsWith("--"))continue;let s=r.slice(2),i=e[n+1];i===void 0||i.startsWith("--")?t[s]=!0:(t[s]=i,n++)}return t}var Ze=["export","adapt","verify","check-locators","check-pom","disconnect","report"];function br(e){let t=Array.isArray(e)?e:[],n=[];for(let r=0;r<t.length;r+=1){let s=String(t[r]==null?"":t[r]);!s||s.startsWith("-")||(r>0?String(t[r-1]||""):"").startsWith("--")||n.push(s)}return n}function Er(e){let t=e[0];return t&&Ze.includes(t)?{command:t,rest:e.slice(1)}:{command:"run",rest:e}}function vr(e="taqwright"){return e==="json"?"recorded-steps.json":"RecordedTest.spec.ts"}function Sr(e,t=process.env){let n=[],r=!!e["list-devices"],s=!!e["keep-session"],i=!!e["no-boot"],o=e["lime-dir"]===!0?".lime":typeof e["lime-dir"]=="string"&&e["lime-dir"].trim()?e["lime-dir"].trim():void 0,a=e.format?String(e.format).toLowerCase():"taqwright";if(["taqwright","json"].includes(a)||n.push(`--format must be taqwright or json (got "${e.format}")`),r)return{errors:n,listDevices:r,keepSession:s,noBoot:i,format:a,limeDir:o};(!e.goal||typeof e.goal!="string"||!e.goal.trim())&&n.push("--goal is required");let l;e.project!==void 0&&(typeof e.project!="string"||!e.project.trim()?n.push("--project requires a project name"):l=e.project.trim());let d;if(e.scenario!==void 0){let g=typeof e.scenario=="string"?e.scenario.trim():"",w=g?wr(g):"requires a scenario key";w?n.push(`--scenario ${w}`):d=g}for(let g of["gemini-key","qwen-key"])e[g]!==void 0&&n.push(`--${g} is no longer supported: runs use the AI key provisioned for your LIME account`);if(e["max-steps"]!==void 0){let g=Number(e["max-steps"]);(!Number.isFinite(g)||g<1||g>200)&&n.push("--max-steps must be a number between 1 and 200")}let c;e.platform&&(c=String(e.platform).toLowerCase(),["android","ios"].includes(c)||n.push(`--platform must be android or ios (got "${e.platform}")`));let u=!!e.cloud,p=typeof e.os=="string"?e.os:void 0,h=typeof e["app-url"]=="string"?e["app-url"]:void 0,y=typeof e.app=="string"?e.app:void 0,I=typeof e.caps=="string"&&e.caps.trim()?e.caps.trim():void 0,b,T,A;if(u&&(b=e.cloud===!0?"browserstack":String(e.cloud).toLowerCase(),Q[b]||n.push(`--cloud must be ${Object.keys(Q).join(" or ")} (got "${e.cloud}")`)),u){(!e.device||typeof e.device!="string")&&n.push('--cloud requires --device (the cloud device name, e.g. "Google Pixel 8")'),c||n.push("--cloud requires --platform (android or ios)"),p||n.push("--cloud requires --os (the device OS version, e.g. 14.0)"),y?n.push("--app is not supported for cloud runs. Upload the build to your provider \u2014 LambdaTest: POST https://manual-api.lambdatest.com/app/upload/realDevice (-F appFile=@<file>), BrowserStack: POST https://api-cloud.browserstack.com/app-automate/upload (-F file=@<file>) \u2014 then pass the returned id with --app-url. (--app is still supported for local runs, where it clean-reinstalls on the agent device.)"):h||n.push("--cloud requires --app-url <bs://\u2026 | lt://\u2026> (the id from your provider\u2019s app upload API)"),e["app-url"]!==void 0&&(typeof e["app-url"]!="string"||!e["app-url"].trim())&&n.push("--app-url requires a bs:// or lt:// app id"),h&&Q[b]&&(b==="browserstack"&&h.startsWith("lt://")?n.push(`--app-url "${h}" is a LambdaTest app id, but the provider is browserstack`):b==="lambdatest"&&h.startsWith("bs://")&&n.push(`--app-url "${h}" is a BrowserStack app id, but the provider is lambdatest`)),e["cloud-username"]!==void 0&&(typeof e["cloud-username"]!="string"||!e["cloud-username"].trim())&&n.push("--cloud-username requires a value"),e["cloud-access-key"]!==void 0&&(typeof e["cloud-access-key"]!="string"||!e["cloud-access-key"].trim())&&n.push("--cloud-access-key requires a value"),e.caps!==void 0&&(typeof e.caps!="string"||!e.caps.trim())&&n.push("--caps requires a path to a JSON file");let g=Q[b];g&&(T=typeof e["cloud-username"]=="string"&&e["cloud-username"].trim()||t[g.envUsername]||void 0,A=typeof e["cloud-access-key"]=="string"&&e["cloud-access-key"].trim()||t[g.envAccessKey]||void 0,T||n.push(`cloud runs require a username \u2014 pass --cloud-username or set ${g.envUsername} (credentials are never read from your LIME account for CLI runs)`),A||n.push(`cloud runs require an access key \u2014 pass --cloud-access-key or set ${g.envAccessKey}`))}else e.app!==void 0&&(typeof e.app!="string"||!e.app.trim()?n.push("--app requires a path to an APK"):e.device?c&&c!=="android"&&n.push("--app is Android only"):n.push("--app requires --device (lime-cli must connect to a device to reinstall the app)")),e["app-url"]!==void 0&&n.push("--app-url requires --cloud"),e.os!==void 0&&n.push("--os requires --cloud"),e["cloud-username"]!==void 0&&n.push("--cloud-username requires --cloud"),e["cloud-access-key"]!==void 0&&n.push("--cloud-access-key requires --cloud"),e.caps!==void 0&&n.push("--caps requires --cloud");return{errors:n,listDevices:r,keepSession:s,noBoot:i,limeDir:o,device:e.device,platform:c,projectName:l,scenarioKey:d,app:y,cloud:u,cloudProvider:b,cloudUsername:T,cloudAccessKey:A,os:p,appUrl:h,capsPath:I,format:a}}var ze=["browserstack","lambdatest","appium"];function Tr(e){let t=[],n=[];for(let[r,s]of Object.entries(e||{}))!s||typeof s!="object"||Array.isArray(s)?t.push(`--caps block "${r}" must be a JSON object, e.g. { "${r}": { "someCap": true } }`):ze.includes(r)||n.push(`--caps block "${r}" is not one of ${ze.join("/")} \u2014 it will be ignored (typo?)`);return{errors:t,warnings:n}}Qe.exports={parseArgs:yr,parseCommand:Er,positionalPaths:br,validateArgs:Sr,extToFilename:vr,validateCapsObject:Tr,COMMANDS:Ze}});var rt=$((Wi,nt)=>{"use strict";var{DEFAULT_SERVER:Ar}=P(),tt={export:`lime-cli export \u2014 work out what this repo is, and where the test belongs.
2
+ "use strict";var $=(e,t)=>()=>{try{return t||e((t={exports:{}}).exports,t),t.exports}catch(n){throw t=0,n}};var Pe=$((qi,Dn)=>{Dn.exports={name:"@taqwright/lime-cli",version:"0.11.1",description:"Thin CLI client for the LIME server \u2014 fire a natural-language QA Agent goal against a connected device and write back a taqwright spec.",license:"SEE LICENSE IN LICENSE",author:"Taqwright",homepage:"https://www.taqwright.ai",bugs:{url:"https://www.taqwright.ai/contact"},keywords:["appium","mobile-testing","e2e","qa","test-automation","taqwright","lime","cli"],bin:{"lime-cli":"dist/index.js"},main:"dist/index.js",files:["dist"],publishConfig:{access:"public"},scripts:{"lime-cli":"node src/index.js",start:"node src/index.js",test:"node --test test/*.test.js",bundle:"esbuild src/index.js --bundle --platform=node --target=node18 --minify --outfile=dist/index.js",prepack:"npm run bundle",prepublishOnly:"npm test"},engines:{node:">=18"},devDependencies:{esbuild:"^0.28.1"}}});var P=$((Ui,De)=>{"use strict";var Mn="https://www.taqwright.ai";De.exports={POLL_INTERVAL_MS:1e3,POLL_TIMEOUT_MS:18e5,BOOT_POLL_INTERVAL_MS:5e3,BOOT_TIMEOUT_MS:3e5,CONNECT_POLL_INTERVAL_MS:2e3,CONNECT_TIMEOUT_MS:3e5,DEFAULT_SERVER:Mn,HTTP_TIMEOUT_MS:3e5,MAX_RESPONSE_BYTES:52428800}});var z=$((Bi,Fe)=>{"use strict";var{URL:qn}=require("url"),{HTTP_TIMEOUT_MS:Me,MAX_RESPONSE_BYTES:qe}=P();function Ue(e){let t=String(e||"").replace(/^\[|\]$/g,"").toLowerCase();return t==="localhost"||t==="127.0.0.1"||t==="::1"||t.endsWith(".localhost")}var Un=["just a moment","attention required! | cloudflare","checking your browser","/cdn-cgi/challenge-platform","cf-error-details","cf-browser-verification","_incapsula_resource"];function Be(e,t){if(!t||typeof t!="string")return null;let n=t.slice(0,4e3).toLowerCase();return Un.some(r=>n.includes(r))?`the LIME server is behind a bot challenge that blocked this request (HTTP ${e}). The request never reached LIME \u2014 it was stopped at the edge, so your token and arguments are probably fine. CI runners use datacenter IPs, which Cloudflare's Bot Fight Mode challenges by default; it cannot be excepted with a WAF skip rule, so the server's operator has to turn it off or move to Super Bot Fight Mode (which supports skip rules) for the /api/ paths.`:null}var ue=class{constructor(t,n){this.server=t,this.token=n}_url(t){return/^https?:\/\//i.test(t)?t:`${this.server}${t}`}json(t,n,r,s={}){let i=r?JSON.stringify(r):null,o={...s};return i&&(o["Content-Type"]="application/json",o["Content-Length"]=Buffer.byteLength(i)),this._send(t,this._url(n),o,i)}raw(t,n,r,s={}){let i={...s,"Content-Type":"application/octet-stream","Content-Length":r.length};return this._send(t,this._url(n),i,r)}_send(t,n,r,s){let i=new qn(n);if(i.protocol==="http:"&&!Ue(i.hostname))return Promise.reject(new Error(`refusing to send your LIME_CI_TOKEN over cleartext http to ${i.hostname} \u2014 use https (http is allowed only for localhost).`));let o=i.protocol==="https:"?require("https"):require("http"),a={method:t,hostname:i.hostname,port:i.port||(i.protocol==="https:"?443:80),path:i.pathname+(i.search||""),headers:{...r,Authorization:`Bearer ${this.token}`,Origin:`${i.protocol}//${i.host}`}};return new Promise((l,d)=>{let c=o.request(a,u=>{let p="",h=0;u.setEncoding("utf8"),u.on("data",y=>{if(h+=Buffer.byteLength(y),h>qe){c.destroy(new Error(`Server response exceeded ${qe} bytes`));return}p+=y}),u.on("end",()=>{let y;try{y=p?JSON.parse(p):{}}catch{let b=Be(u.statusCode,p);return d(new Error(b||`Server returned non-JSON (${u.statusCode}): ${p.substring(0,200)}`))}l({status:u.statusCode,body:y})})});c.setTimeout(Me,()=>{c.destroy(new Error(`request timed out after ${Me/1e3}s`))}),c.on("error",d),s&&c.write(s),c.end()})}};function Bn(e,t){if(!e.body||!e.body.success)throw new Error(`${t} \u2014 ${e.body&&e.body.error||"unknown"}`);return e.body}Fe.exports={isLoopbackHost:Ue,HttpClient:ue,expectSuccess:Bn,describeEdgeBlock:Be}});var D=$((Fi,Ke)=>{"use strict";var Fn={browserstack:{label:"BrowserStack",envUsername:"BROWSERSTACK_USERNAME",envAccessKey:"BROWSERSTACK_ACCESS_KEY",hubHost:"hub.browserstack.com"},lambdatest:{label:"LambdaTest",envUsername:"LT_USERNAME",envAccessKey:"LT_ACCESS_KEY",hubHost:"mobile-hub.lambdatest.com"}};function Kn({provider:e,platform:t,device:n,os:r,appUrl:s,username:i,accessKey:o,capabilities:a}){let l={connectionMode:"cloud",cloudProvider:e||"browserstack",cloudUsername:i,cloudAccessKey:o,platform:t,deviceName:n,deviceOsVersion:r,cloudAppUrl:s,automationName:t==="ios"?"XCUITest":"UiAutomator2"};return a&&(l.cloudCapabilities=a),l}Ke.exports={CLOUD_PROVIDERS:Fn,buildCloudSettingsPayload:Kn}});var fe=$((Ki,Ve)=>{"use strict";var Z=require("fs"),pe=require("path"),Hn=["taqwright","webdriverio-js","appium-java-junit5","appium-python-pytest","appium-ruby-rspec","appium-csharp-nunit"],Vn={taqwright:"deterministic","webdriverio-js":"gemini","appium-java-junit5":"gemini","appium-python-pytest":"gemini","appium-ruby-rspec":"gemini","appium-csharp-nunit":"gemini"},j={taqwright:"taqwright (native)","webdriverio-js":"WebdriverIO (JavaScript)","appium-java-junit5":"Appium Java (JUnit 5)","appium-python-pytest":"Appium Python (pytest)","appium-ruby-rspec":"Appium Ruby (RSpec)","appium-csharp-nunit":"Appium C# (NUnit)"},Wn=5*1024*1024,he={exists:e=>Z.existsSync(e),readText:e=>{try{return Z.statSync(e).size>Wn?null:Z.readFileSync(e,"utf8")}catch{return null}},list:e=>{try{return Z.readdirSync(e)}catch{return[]}}};function Yn(e,t){let n=e.readText(pe.join(t,"package.json"));if(!n)return{};try{let r=JSON.parse(n);return{...r.dependencies||{},...r.devDependencies||{}}}catch{return{}}}var Gn=new Set(["node_modules","dist","build","target","bin","obj","vendor","out","coverage"]);function Jn(e,t){let n=new Set([""]),r=[];try{r=e.list(t)}catch{r=[]}for(let s of r)s.startsWith(".")||Gn.has(s)||n.add(s);return[...n]}function Xn(e,t,n){let r=n?pe.join(t,n):t,s=u=>pe.join(r,u),i=u=>n?`${n}/${u}`:u,o=[],a=Yn(e,r),l=[];try{l=e.list(r)}catch{l=[]}(a["@taqwright/taqwright"]||a.taqwright||e.exists(s("taqwright.config.ts"))||e.exists(s("taqwright.config.js")))&&o.push({id:"taqwright",label:j.taqwright,reason:"taqwright dependency/config found",manifest:i("package.json")});let d=l.some(u=>/^wdio\.conf\.(js|ts|mjs|cjs)$/.test(u));(a.webdriverio||a["@wdio/cli"]||d)&&o.push({id:"webdriverio-js",label:j["webdriverio-js"],reason:d?"wdio.conf.* found":"webdriverio dependency found",manifest:i("package.json")});for(let u of["pom.xml","build.gradle","build.gradle.kts"]){let p=e.readText(s(u));if(p&&/io\.appium|java-client/.test(p)){o.push({id:"appium-java-junit5",label:j["appium-java-junit5"],reason:`io.appium:java-client in ${i(u)}`,manifest:i(u)});break}}for(let u of["requirements.txt","pyproject.toml"]){let p=e.readText(s(u));if(p&&/appium-python-client/i.test(p)){o.push({id:"appium-python-pytest",label:j["appium-python-pytest"],reason:`Appium-Python-Client in ${i(u)}`,manifest:i(u)});break}}let c=e.readText(s("Gemfile"));c&&/appium_lib(_core)?/.test(c)&&o.push({id:"appium-ruby-rspec",label:j["appium-ruby-rspec"],reason:"appium_lib in Gemfile",manifest:i("Gemfile")});for(let u of l){if(!/\.csproj$/i.test(u))continue;let p=e.readText(s(u));if(p&&/Appium\.WebDriver/i.test(p)){o.push({id:"appium-csharp-nunit",label:j["appium-csharp-nunit"],reason:`Appium.WebDriver in ${i(u)}`,manifest:i(u)});break}}return o}function He(e,t=he){let n=new Map;for(let r of Jn(t,e))for(let s of Xn(t,e,r))n.has(s.id)||n.set(s.id,s);return[...n.values()]}function zn(e,t=he){let n=He(e,t);return n.some(r=>r.id==="taqwright")||n.push({id:"taqwright",label:j.taqwright,reason:"default output"}),n}function Zn(e){let{requested:t,configured:n,remembered:r,offeredIds:s,detectedRealIds:i,taqwrightDetected:o,javaSignal:a}=e,l=d=>!!d&&s.includes(d);return l(t)?t:l(n)?n:l(r)&&(i.includes(r)||r==="taqwright"&&o)?r:i.length?i[0]:o?"taqwright":a?"appium-java-junit5":"taqwright"}Ve.exports={FRAMEWORK_IDS:Hn,FRAMEWORK_ENGINES:Vn,FRAMEWORK_LABELS:j,nodeFs:he,detectFrameworks:He,rankFrameworks:zn,pickDefaultFramework:Zn}});var Ee=$((Hi,Xe)=>{"use strict";var me=require("path"),{nodeFs:Qn}=fe(),We={taqwright:"tests","webdriverio-js":"test/specs","appium-java-junit5":"src/test/java","appium-python-pytest":"tests","appium-ruby-rspec":"spec","appium-csharp-nunit":"Tests"},er="tests",tr={taqwright:".spec.ts","webdriverio-js":".spec.ts","appium-java-junit5":".java","appium-python-pytest":".py","appium-ruby-rspec":"_spec.rb","appium-csharp-nunit":".cs"},nr=new Set(["appium-python-pytest","appium-ruby-rspec"]);function ge(e){return We[e]||er}function we(e){let t=(e||"").split(/[^A-Za-z0-9]+/).filter(Boolean);return t.length?t.map(n=>/^[A-Z0-9]+$/.test(n)?n[0]+n.slice(1).toLowerCase():n[0].toUpperCase()+n.slice(1)).join(""):""}function ye(e){return(e||"").replace(/([a-z0-9])([A-Z])/g,"$1_$2").split(/[^A-Za-z0-9]+/).filter(Boolean).join("_").toLowerCase()}function rr(e){return ye(e).split("_").filter(Boolean).join("-")}var sr=6,ir=new Set(["and","or","the","a","an","to","of","in","on","at","as","with","for","then","into","from","by","that","is"]),or=/@|\d{6,}/;function ar(e){let t=String(e||"").trim().split(/\s+/).filter(Boolean).filter(n=>!or.test(n)).slice(0,sr);for(;t.length>1&&ir.has(t[t.length-1].toLowerCase());)t.pop();return we(t.join(" "))||"RecordedTest"}function cr(e){let t=String(e||"").trim();return t?/[A-Za-z]/.test(t)?t.length>80?"Keep the name under 80 characters.":/[/\\]/.test(t)?"Use the destination field for the folder.":null:"Use at least one letter.":"Enter a test name."}function lr(e){if(e==null||e==="")return null;let t=String(e);return t!==t.trim()?"must not have leading or trailing spaces.":t.length>120?"is longer than any key LIME mints (120 characters).":/^[a-z0-9][a-z0-9._/:-]*$/.test(t)?t.includes("..")?'must not contain "..".':null:"is not a scenario key \u2014 expected lower-case letters, digits and . _ - / : only."}function Ye(e,t){let n;return nr.has(e)?n=ye(t):n=we(t),`${n}${tr[e]||""}`}function K(e){return String(e||"").split(/[/\\]+/).filter(t=>t&&t!=="."&&t!=="..").join("/")}function dr(e){if(e==null)return null;let t=String(e);return t.trim()===""?null:/^([A-Za-z]:)?[/\\]/.test(t)?"must be a path inside the project, not an absolute path.":t.split(/[/\\]+/).includes("..")?'must not contain ".." \u2014 the test belongs inside the project.':null}function be(e){let t=K(e).split("/");return t.pop(),t.join("/")}function Ge(e){let t=/(?:^|\/)src\/(?:test|main)\/java\/(.+)$/.exec(K(e));return t&&t[1].split("/").filter(Boolean).join(".")||void 0}function ur(e){let t=K(e),n=t.search(/\/src\/(?:test|main)\/java(?:\/|$)/);return n>0?t.slice(0,n):""}function pr(e){let t=new Map;for(let r of e){let s=be(r);t.set(s,(t.get(s)||0)+1)}if(!t.size)return null;let n=[...t.entries()].sort((r,s)=>s[1]-r[1]||r[0].localeCompare(s[0]));return{dir:n[0][0],count:n[0][1]}}var Je={taqwright:{segment:"tests",file:/\.spec\.(ts|js)$/},"webdriverio-js":{segment:"test/specs",file:/\.(ts|js)$/},"appium-java-junit5":{segment:"src/test/java",file:/\.java$/},"appium-python-pytest":{segment:"tests",file:/^(test_.+|.+_test)\.py$/},"appium-ruby-rspec":{segment:"spec",file:/_spec\.rb$/},"appium-csharp-nunit":{segment:"",file:/Tests\.cs$/}},hr=new Set(["node_modules","dist","build","target","bin","obj","vendor","out","coverage",".git"]),fr=2e4;function mr(e,t,n=Qn){let r=Je[t];if(!r)return[];let s=[],i=fr,o=a=>{if(i<=0)return;let l=a?me.join(e,a):e,d=[];try{d=n.list(l)}catch{return}for(let c of d){if(i--<=0)return;if(c.startsWith(".")||hr.has(c))continue;let u=a?`${a}/${c}`:c;if(n.isDir?n.isDir(me.join(e,u)):n.list(me.join(e,u)).length>0){o(u);continue}r.file.test(c)&&(r.segment&&!`/${be(u)}/`.includes(`/${r.segment}/`)||s.push(u))}};return o(""),s.sort()}function gr(e){let{framework:t,testName:n}=e,r=Ye(t,n),s=(a,l,d=0)=>{let c=K(a),u=t==="appium-java-junit5"?Ge(c):void 0;return{dir:c,fileName:r,...u?{packageName:u}:{},source:l,neighbourCount:d}};if(e.remembered!=null)return s(e.remembered,"remembered");let i=pr(e.existingTests||[]);if(i&&i.dir)return s(i.dir,"existing",i.count);let o=e.detection&&e.detection.manifest?be(e.detection.manifest):"";return o?s(`${o}/${ge(t)}`,"module"):s(ge(t),"convention")}Xe.exports={CONVENTIONAL_DIRS:We,TEST_PATTERNS:Je,conventionalDir:ge,pascalCase:we,snakeCase:ye,kebabCase:rr,testNameFromGoal:ar,validateTestName:cr,validateDest:dr,validateScenarioKey:lr,fileNameFor:Ye,normalizeDir:K,javaPackageOf:Ge,moduleRootOf:ur,findExistingTests:mr,resolveExportTarget:gr}});var et=$((Vi,Qe)=>{"use strict";var{CLOUD_PROVIDERS:Q}=D(),{validateScenarioKey:wr}=Ee();function yr(e){let t=Object.create(null);for(let n=0;n<e.length;n++){let r=e[n];if(!r.startsWith("--"))continue;let s=r.slice(2),i=e[n+1];i===void 0||i.startsWith("--")?t[s]=!0:(t[s]=i,n++)}return t}var Ze=["export","adapt","verify","check-locators","check-pom","disconnect","report"];function br(e){let t=Array.isArray(e)?e:[],n=[];for(let r=0;r<t.length;r+=1){let s=String(t[r]==null?"":t[r]);!s||s.startsWith("-")||(r>0?String(t[r-1]||""):"").startsWith("--")||n.push(s)}return n}function Er(e){let t=e[0];return t&&Ze.includes(t)?{command:t,rest:e.slice(1)}:{command:"run",rest:e}}function vr(e="taqwright"){return e==="json"?"recorded-steps.json":"RecordedTest.spec.ts"}function Sr(e,t=process.env){let n=[],r=!!e["list-devices"],s=!!e["keep-session"],i=!!e["no-boot"],o=e["lime-dir"]===!0?".lime":typeof e["lime-dir"]=="string"&&e["lime-dir"].trim()?e["lime-dir"].trim():void 0,a=e.format?String(e.format).toLowerCase():"taqwright";if(["taqwright","json"].includes(a)||n.push(`--format must be taqwright or json (got "${e.format}")`),r)return{errors:n,listDevices:r,keepSession:s,noBoot:i,format:a,limeDir:o};(!e.goal||typeof e.goal!="string"||!e.goal.trim())&&n.push("--goal is required");let l;e.project!==void 0&&(typeof e.project!="string"||!e.project.trim()?n.push("--project requires a project name"):l=e.project.trim());let d;if(e.scenario!==void 0){let g=typeof e.scenario=="string"?e.scenario.trim():"",w=g?wr(g):"requires a scenario key";w?n.push(`--scenario ${w}`):d=g}for(let g of["gemini-key","qwen-key"])e[g]!==void 0&&n.push(`--${g} is no longer supported: runs use the AI key provisioned for your LIME account`);if(e["max-steps"]!==void 0){let g=Number(e["max-steps"]);(!Number.isFinite(g)||g<1||g>200)&&n.push("--max-steps must be a number between 1 and 200")}let c;e.platform&&(c=String(e.platform).toLowerCase(),["android","ios"].includes(c)||n.push(`--platform must be android or ios (got "${e.platform}")`));let u=!!e.cloud,p=typeof e.os=="string"?e.os:void 0,h=typeof e["app-url"]=="string"?e["app-url"]:void 0,y=typeof e.app=="string"?e.app:void 0,I=typeof e.caps=="string"&&e.caps.trim()?e.caps.trim():void 0,b,T,A;if(u&&(b=e.cloud===!0?"browserstack":String(e.cloud).toLowerCase(),Q[b]||n.push(`--cloud must be ${Object.keys(Q).join(" or ")} (got "${e.cloud}")`)),u){(!e.device||typeof e.device!="string")&&n.push('--cloud requires --device (the cloud device name, e.g. "Google Pixel 8")'),c||n.push("--cloud requires --platform (android or ios)"),p||n.push("--cloud requires --os (the device OS version, e.g. 14.0)"),y?n.push("--app is not supported for cloud runs. Upload the build to your provider \u2014 LambdaTest: POST https://manual-api.lambdatest.com/app/upload/realDevice (-F appFile=@<file>), BrowserStack: POST https://api-cloud.browserstack.com/app-automate/upload (-F file=@<file>) \u2014 then pass the returned id with --app-url. (--app is still supported for local runs, where it clean-reinstalls on the agent device.)"):h||n.push("--cloud requires --app-url <bs://\u2026 | lt://\u2026> (the id from your provider\u2019s app upload API)"),e["app-url"]!==void 0&&(typeof e["app-url"]!="string"||!e["app-url"].trim())&&n.push("--app-url requires a bs:// or lt:// app id"),h&&Q[b]&&(b==="browserstack"&&h.startsWith("lt://")?n.push(`--app-url "${h}" is a LambdaTest app id, but the provider is browserstack`):b==="lambdatest"&&h.startsWith("bs://")&&n.push(`--app-url "${h}" is a BrowserStack app id, but the provider is lambdatest`)),e["cloud-username"]!==void 0&&(typeof e["cloud-username"]!="string"||!e["cloud-username"].trim())&&n.push("--cloud-username requires a value"),e["cloud-access-key"]!==void 0&&(typeof e["cloud-access-key"]!="string"||!e["cloud-access-key"].trim())&&n.push("--cloud-access-key requires a value"),e.caps!==void 0&&(typeof e.caps!="string"||!e.caps.trim())&&n.push("--caps requires a path to a JSON file");let g=Q[b];g&&(T=typeof e["cloud-username"]=="string"&&e["cloud-username"].trim()||t[g.envUsername]||void 0,A=typeof e["cloud-access-key"]=="string"&&e["cloud-access-key"].trim()||t[g.envAccessKey]||void 0,T||n.push(`cloud runs require a username \u2014 pass --cloud-username or set ${g.envUsername} (credentials are never read from your LIME account for CLI runs)`),A||n.push(`cloud runs require an access key \u2014 pass --cloud-access-key or set ${g.envAccessKey}`))}else e.app!==void 0&&(typeof e.app!="string"||!e.app.trim()?n.push("--app requires a path to an APK"):e.device?c&&c!=="android"&&n.push("--app is Android only"):n.push("--app requires --device (lime-cli must connect to a device to reinstall the app)")),e["app-url"]!==void 0&&n.push("--app-url requires --cloud"),e.os!==void 0&&n.push("--os requires --cloud"),e["cloud-username"]!==void 0&&n.push("--cloud-username requires --cloud"),e["cloud-access-key"]!==void 0&&n.push("--cloud-access-key requires --cloud"),e.caps!==void 0&&n.push("--caps requires --cloud");return{errors:n,listDevices:r,keepSession:s,noBoot:i,limeDir:o,device:e.device,platform:c,projectName:l,scenarioKey:d,app:y,cloud:u,cloudProvider:b,cloudUsername:T,cloudAccessKey:A,os:p,appUrl:h,capsPath:I,format:a}}var ze=["browserstack","lambdatest","appium"];function Tr(e){let t=[],n=[];for(let[r,s]of Object.entries(e||{}))!s||typeof s!="object"||Array.isArray(s)?t.push(`--caps block "${r}" must be a JSON object, e.g. { "${r}": { "someCap": true } }`):ze.includes(r)||n.push(`--caps block "${r}" is not one of ${ze.join("/")} \u2014 it will be ignored (typo?)`);return{errors:t,warnings:n}}Qe.exports={parseArgs:yr,parseCommand:Er,positionalPaths:br,validateArgs:Sr,extToFilename:vr,validateCapsObject:Tr,COMMANDS:Ze}});var rt=$((Wi,nt)=>{"use strict";var{DEFAULT_SERVER:Ar}=P(),tt={export:`lime-cli export \u2014 work out what this repo is, and where the test belongs.
3
3
 
4
4
  Detects the test framework from the project's own manifests (pytest, JUnit, RSpec, WebdriverIO,
5
5
  C#, taqwright), picks the directory its existing tests live in, and writes the adaptation brief.
@@ -502,6 +502,6 @@ Run lime-cli --help for usage.
502
502
  `),2}try{await Ni(_,{provider:p,platform:d,device:l,os:I,appUrl:b,username:h,accessKey:y,capabilities:k}),le=!0,J.target="cloud",J.cloud={provider:p,hubHost:($n[p]||{}).hubHost||null,appUrl:b,device:l,os:I}}catch(X){return process.stderr.write(`lime-cli: ${X.message}
503
503
  `),1}}else try{let k=await Li(_,{device:l,platform:d,noBoot:a});le=!0,k&&k.platform&&(J.platform=k.platform)}catch(k){return process.stderr.write(`lime-cli: ${k.message}
504
504
  `),1}try{if(c&&!u)try{await Ci(_,c)}catch(k){return process.stderr.write(`lime-cli: ${k.message}
505
- `),1}return await ji(_,r,ce,A,{limeDir:E?G.resolve(E):void 0,manifest:J})}finally{le&&!o&&(process.stdout.write(`lime-cli: disconnecting\u2026
505
+ `),1}return await ji(_,{...r,scenarioKey:w},ce,A,{limeDir:E?G.resolve(E):void 0,manifest:J})}finally{le&&!o&&(process.stdout.write(`lime-cli: disconnecting\u2026
506
506
  `),await _n(_))}}module.exports={CLOUD_PROVIDERS:$n,parseArgs:Ce,parseCommand:On,validateArgs:xn,extToFilename:Ln,validateCapsObject:Nn,checkServerSecurity:jn,matchDevice:ki,pickDevice:_i,buildSettingsPayload:$i,buildCloudSettingsPayload:Ii,buildBootRequest:Oi,formatDeviceList:Cn,run:Pn};require.main===module&&Pn(process.argv.slice(2)).then(e=>process.exit(e),e=>{process.stderr.write(`lime-cli: ${e.message}
507
507
  `),process.exit(1)});
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@taqwright/lime-cli",
3
- "version": "0.11.0",
3
+ "version": "0.11.1",
4
4
  "description": "Thin CLI client for the LIME server — fire a natural-language QA Agent goal against a connected device and write back a taqwright spec.",
5
5
  "license": "SEE LICENSE IN LICENSE",
6
6
  "author": "Taqwright",