@taqwright/lime-cli 0.7.18 → 0.7.19

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
@@ -294,6 +294,47 @@ flow on a cloud device, adapts the test into your **test-suite** repo, verifies
294
294
  and opens a pull request there linking back. LIME holds no GitHub credential — the runner
295
295
  already has yours.
296
296
 
297
+ ### `report` — send a finished suite's results back
298
+
299
+ LIME generated the specs; your CI owns them from then on, and runs them without LIME in the
300
+ room. **One step after your regression run closes the loop**, so the Reports board shows
301
+ which journeys are green, which broke, and on which build.
302
+
303
+ ```sh
304
+ pytest tests/ --junitxml=results.xml -o junit_family=xunit1
305
+ lime-cli report --junit results.xml --platform android
306
+ ```
307
+
308
+ It works from **any framework**, because it reads the report your runner already wrote:
309
+
310
+ | | |
311
+ |---|---|
312
+ | `--junit <file>` | JUnit XML — pytest, JUnit 5, WebdriverIO, RSpec, NUnit, Maestro |
313
+ | `--results <file>` | A Playwright JSON report, which is what taqwright writes |
314
+
315
+ The Playwright form carries more: cloud session links, and the `taqwright-scenario`
316
+ annotation that ties a test to its card. JUnit XML carries neither, so LIME matches those
317
+ by spec path instead — run a repo scan (console → Scenarios → Sync) once, and it knows
318
+ which file holds which journey.
319
+
320
+ **Two things to get right, and one of them is easy to miss.**
321
+
322
+ `if: always()` on the reporting step, or the CI equivalent — `when: always` on GitLab,
323
+ `post { always { … } }` on Jenkins. A failing suite fails the job, and a normal step is
324
+ skipped once that happens, so **without it you only ever report passes**: the runs you most
325
+ want to see are exactly the ones that never arrive.
326
+
327
+ `--commit` should name the **app under test**, not this repository. The board pairs a pull
328
+ request to a run by comparing them, so a test repo's own HEAD pairs with nothing. If your
329
+ app and tests share a repo, the default is already right.
330
+
331
+ Reporting never fails your build. A missing file, an unreadable report, a refused post and
332
+ an unreachable server all warn and exit 0 — a reporting problem must not become a red
333
+ pipeline on top of whatever actually happened.
334
+
335
+ `docs/github/report.yml` (ask Taqwright for a copy) is a ready-made workflow with the
336
+ matrix, other-runner and other-CI variants commented in.
337
+
297
338
  ## Server API contract
298
339
 
299
340
  `lime-cli` is the only client of the LIME server's `/api/cli/*` endpoints, so
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- "use strict";var R=(e,t)=>()=>{try{return t||e((t={exports:{}}).exports,t),t.exports}catch(n){throw t=0,n}};var De=R((xi,jn)=>{jn.exports={name:"@taqwright/lime-cli",version:"0.7.18",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 D=R((Ni,qe)=>{"use strict";var Pn="https://www.taqwright.ai";qe.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:Pn,HTTP_TIMEOUT_MS:3e5,MAX_RESPONSE_BYTES:52428800}});var Q=R((Ci,Ke)=>{"use strict";var{URL:Dn}=require("url"),{HTTP_TIMEOUT_MS:Me,MAX_RESPONSE_BYTES:Ue}=D();function Be(e){let t=String(e||"").replace(/^\[|\]$/g,"").toLowerCase();return t==="localhost"||t==="127.0.0.1"||t==="::1"||t.endsWith(".localhost")}var qn=["just a moment","attention required! | cloudflare","checking your browser","/cdn-cgi/challenge-platform","cf-error-details","cf-browser-verification","_incapsula_resource"];function Fe(e,t){if(!t||typeof t!="string")return null;let n=t.slice(0,4e3).toLowerCase();return qn.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 Dn(n);if(i.protocol==="http:"&&!Be(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,u)=>{let c=o.request(a,d=>{let p="",g=0;d.setEncoding("utf8"),d.on("data",w=>{if(g+=Buffer.byteLength(w),g>Ue){c.destroy(new Error(`Server response exceeded ${Ue} bytes`));return}p+=w}),d.on("end",()=>{let w;try{w=p?JSON.parse(p):{}}catch{let m=Fe(d.statusCode,p);return u(new Error(m||`Server returned non-JSON (${d.statusCode}): ${p.substring(0,200)}`))}l({status:d.statusCode,body:w})})});c.setTimeout(Me,()=>{c.destroy(new Error(`request timed out after ${Me/1e3}s`))}),c.on("error",u),s&&c.write(s),c.end()})}};function Mn(e,t){if(!e.body||!e.body.success)throw new Error(`${t} \u2014 ${e.body&&e.body.error||"unknown"}`);return e.body}Ke.exports={isLoopbackHost:Be,HttpClient:ue,expectSuccess:Mn,describeEdgeBlock:Fe}});var q=R((Li,Ye)=>{"use strict";var Un={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 Bn({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}Ye.exports={CLOUD_PROVIDERS:Un,buildCloudSettingsPayload:Bn}});var fe=R((ji,We)=>{"use strict";var Z=require("fs"),pe=require("path"),Fn=["taqwright","webdriverio-js","appium-java-junit5","appium-python-pytest","appium-ruby-rspec","appium-csharp-nunit"],Kn={taqwright:"deterministic","webdriverio-js":"gemini","appium-java-junit5":"gemini","appium-python-pytest":"gemini","appium-ruby-rspec":"gemini","appium-csharp-nunit":"gemini"},P={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)"},Yn=5*1024*1024,he={exists:e=>Z.existsSync(e),readText:e=>{try{return Z.statSync(e).size>Yn?null:Z.readFileSync(e,"utf8")}catch{return null}},list:e=>{try{return Z.readdirSync(e)}catch{return[]}}};function Hn(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 Wn=new Set(["node_modules","dist","build","target","bin","obj","vendor","out","coverage"]);function Vn(e,t){let n=new Set([""]),r=[];try{r=e.list(t)}catch{r=[]}for(let s of r)s.startsWith(".")||Wn.has(s)||n.add(s);return[...n]}function Gn(e,t,n){let r=n?pe.join(t,n):t,s=d=>pe.join(r,d),i=d=>n?`${n}/${d}`:d,o=[],a=Hn(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:P.taqwright,reason:"taqwright dependency/config found",manifest:i("package.json")});let u=l.some(d=>/^wdio\.conf\.(js|ts|mjs|cjs)$/.test(d));(a.webdriverio||a["@wdio/cli"]||u)&&o.push({id:"webdriverio-js",label:P["webdriverio-js"],reason:u?"wdio.conf.* found":"webdriverio dependency found",manifest:i("package.json")});for(let d of["pom.xml","build.gradle","build.gradle.kts"]){let p=e.readText(s(d));if(p&&/io\.appium|java-client/.test(p)){o.push({id:"appium-java-junit5",label:P["appium-java-junit5"],reason:`io.appium:java-client in ${i(d)}`,manifest:i(d)});break}}for(let d of["requirements.txt","pyproject.toml"]){let p=e.readText(s(d));if(p&&/appium-python-client/i.test(p)){o.push({id:"appium-python-pytest",label:P["appium-python-pytest"],reason:`Appium-Python-Client in ${i(d)}`,manifest:i(d)});break}}let c=e.readText(s("Gemfile"));c&&/appium_lib(_core)?/.test(c)&&o.push({id:"appium-ruby-rspec",label:P["appium-ruby-rspec"],reason:"appium_lib in Gemfile",manifest:i("Gemfile")});for(let d of l){if(!/\.csproj$/i.test(d))continue;let p=e.readText(s(d));if(p&&/Appium\.WebDriver/i.test(p)){o.push({id:"appium-csharp-nunit",label:P["appium-csharp-nunit"],reason:`Appium.WebDriver in ${i(d)}`,manifest:i(d)});break}}return o}function He(e,t=he){let n=new Map;for(let r of Vn(t,e))for(let s of Gn(t,e,r))n.has(s.id)||n.set(s.id,s);return[...n.values()]}function Jn(e,t=he){let n=He(e,t);return n.some(r=>r.id==="taqwright")||n.push({id:"taqwright",label:P.taqwright,reason:"default output"}),n}function Xn(e){let{requested:t,configured:n,remembered:r,offeredIds:s,detectedRealIds:i,taqwrightDetected:o,javaSignal:a}=e,l=u=>!!u&&s.includes(u);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"}We.exports={FRAMEWORK_IDS:Fn,FRAMEWORK_ENGINES:Kn,FRAMEWORK_LABELS:P,nodeFs:he,detectFrameworks:He,rankFrameworks:Jn,pickDefaultFramework:Xn}});var ve=R((Pi,ze)=>{"use strict";var me=require("path"),{nodeFs:zn}=fe(),Ve={taqwright:"tests","webdriverio-js":"test/specs","appium-java-junit5":"src/test/java","appium-python-pytest":"tests","appium-ruby-rspec":"spec","appium-csharp-nunit":"Tests"},Qn="tests",Zn={taqwright:".spec.ts","webdriverio-js":".spec.ts","appium-java-junit5":".java","appium-python-pytest":".py","appium-ruby-rspec":"_spec.rb","appium-csharp-nunit":".cs"},er=new Set(["appium-python-pytest","appium-ruby-rspec"]);function ge(e){return Ve[e]||Qn}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 tr(e){return ye(e).split("_").filter(Boolean).join("-")}var nr=6,rr=new Set(["and","or","the","a","an","to","of","in","on","at","as","with","for","then","into","from","by","that","is"]),sr=/@|\d{6,}/;function ir(e){let t=String(e||"").trim().split(/\s+/).filter(Boolean).filter(n=>!sr.test(n)).slice(0,nr);for(;t.length>1&&rr.has(t[t.length-1].toLowerCase());)t.pop();return we(t.join(" "))||"RecordedTest"}function or(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 ar(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 Ge(e,t){let n;return er.has(e)?n=ye(t):n=we(t),`${n}${Zn[e]||""}`}function Y(e){return String(e||"").split(/[/\\]+/).filter(t=>t&&t!=="."&&t!=="..").join("/")}function cr(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=Y(e).split("/");return t.pop(),t.join("/")}function Je(e){let t=/(?:^|\/)src\/(?:test|main)\/java\/(.+)$/.exec(Y(e));return t&&t[1].split("/").filter(Boolean).join(".")||void 0}function lr(e){let t=Y(e),n=t.search(/\/src\/(?:test|main)\/java(?:\/|$)/);return n>0?t.slice(0,n):""}function dr(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 Xe={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$/}},ur=new Set(["node_modules","dist","build","target","bin","obj","vendor","out","coverage",".git"]),pr=2e4;function hr(e,t,n=zn){let r=Xe[t];if(!r)return[];let s=[],i=pr,o=a=>{if(i<=0)return;let l=a?me.join(e,a):e,u=[];try{u=n.list(l)}catch{return}for(let c of u){if(i--<=0)return;if(c.startsWith(".")||ur.has(c))continue;let d=a?`${a}/${c}`:c;if(n.isDir?n.isDir(me.join(e,d)):n.list(me.join(e,d)).length>0){o(d);continue}r.file.test(c)&&(r.segment&&!`/${be(d)}/`.includes(`/${r.segment}/`)||s.push(d))}};return o(""),s.sort()}function fr(e){let{framework:t,testName:n}=e,r=Ge(t,n),s=(a,l,u=0)=>{let c=Y(a),d=t==="appium-java-junit5"?Je(c):void 0;return{dir:c,fileName:r,...d?{packageName:d}:{},source:l,neighbourCount:u}};if(e.remembered!=null)return s(e.remembered,"remembered");let i=dr(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")}ze.exports={CONVENTIONAL_DIRS:Ve,TEST_PATTERNS:Xe,conventionalDir:ge,pascalCase:we,snakeCase:ye,kebabCase:tr,testNameFromGoal:ir,validateTestName:or,validateDest:cr,validateScenarioKey:ar,fileNameFor:Ge,normalizeDir:Y,javaPackageOf:Je,moduleRootOf:lr,findExistingTests:hr,resolveExportTarget:fr}});var tt=R((Di,et)=>{"use strict";var{CLOUD_PROVIDERS:ee}=q(),{validateScenarioKey:mr}=ve();function gr(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 wr(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 yr(e){let t=e[0];return t&&Ze.includes(t)?{command:t,rest:e.slice(1)}:{command:"run",rest:e}}function br(e="taqwright"){return e==="json"?"recorded-steps.json":"RecordedTest.spec.ts"}function vr(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 u;if(e.scenario!==void 0){let h=typeof e.scenario=="string"?e.scenario.trim():"",S=h?mr(h):"requires a scenario key";S?n.push(`--scenario ${S}`):u=h}let c,d;e["gemini-key"]!==void 0&&(typeof e["gemini-key"]!="string"||!e["gemini-key"].trim()?n.push("--gemini-key requires a value (or set GEMINI_API_KEY)"):(c=e["gemini-key"].trim(),d="flag")),!c&&typeof t.GEMINI_API_KEY=="string"&&t.GEMINI_API_KEY.trim()&&(c=t.GEMINI_API_KEY.trim(),d="env");let p,g;if(e["qwen-key"]!==void 0&&(typeof e["qwen-key"]!="string"||!e["qwen-key"].trim()?n.push("--qwen-key requires a value (or set QWEN_API_KEY)"):(p=e["qwen-key"].trim(),g="flag")),!p&&typeof t.QWEN_API_KEY=="string"&&t.QWEN_API_KEY.trim()&&(p=t.QWEN_API_KEY.trim(),g="env"),e["max-steps"]!==void 0){let h=Number(e["max-steps"]);(!Number.isFinite(h)||h<1||h>200)&&n.push("--max-steps must be a number between 1 and 200")}let w;e.platform&&(w=String(e.platform).toLowerCase(),["android","ios"].includes(w)||n.push(`--platform must be android or ios (got "${e.platform}")`));let I=!!e.cloud,m=typeof e.os=="string"?e.os:void 0,y=typeof e["app-url"]=="string"?e["app-url"]:void 0,k=typeof e.app=="string"?e.app:void 0,b=typeof e.caps=="string"&&e.caps.trim()?e.caps.trim():void 0,v,T,E;if(I&&(v=e.cloud===!0?"browserstack":String(e.cloud).toLowerCase(),ee[v]||n.push(`--cloud must be ${Object.keys(ee).join(" or ")} (got "${e.cloud}")`)),I){(!e.device||typeof e.device!="string")&&n.push('--cloud requires --device (the cloud device name, e.g. "Google Pixel 8")'),w||n.push("--cloud requires --platform (android or ios)"),m||n.push("--cloud requires --os (the device OS version, e.g. 14.0)"),k?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.)"):y||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"),y&&ee[v]&&(v==="browserstack"&&y.startsWith("lt://")?n.push(`--app-url "${y}" is a LambdaTest app id, but the provider is browserstack`):v==="lambdatest"&&y.startsWith("bs://")&&n.push(`--app-url "${y}" 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 h=ee[v];h&&(T=typeof e["cloud-username"]=="string"&&e["cloud-username"].trim()||t[h.envUsername]||void 0,E=typeof e["cloud-access-key"]=="string"&&e["cloud-access-key"].trim()||t[h.envAccessKey]||void 0,T||n.push(`cloud runs require a username \u2014 pass --cloud-username or set ${h.envUsername} (credentials are never read from your LIME account for CLI runs)`),E||n.push(`cloud runs require an access key \u2014 pass --cloud-access-key or set ${h.envAccessKey}`))}else e.app!==void 0&&(typeof e.app!="string"||!e.app.trim()?n.push("--app requires a path to an APK"):e.device?w&&w!=="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:w,projectName:l,scenarioKey:u,geminiKey:c,geminiKeySource:d,qwenKey:p,qwenKeySource:g,app:k,cloud:I,cloudProvider:v,cloudUsername:T,cloudAccessKey:E,os:m,appUrl:y,capsPath:b,format:a}}var Qe=["browserstack","lambdatest","appium"];function Er(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 } }`):Qe.includes(r)||n.push(`--caps block "${r}" is not one of ${Qe.join("/")} \u2014 it will be ignored (typo?)`);return{errors:t,warnings:n}}et.exports={parseArgs:gr,parseCommand:yr,positionalPaths:wr,validateArgs:vr,extToFilename:br,validateCapsObject:Er,COMMANDS:Ze}});var st=R((qi,rt)=>{"use strict";var{DEFAULT_SERVER:Sr}=D(),nt={export:`lime-cli export \u2014 work out what this repo is, and where the test belongs.
2
+ "use strict";var R=(e,t)=>()=>{try{return t||e((t={exports:{}}).exports,t),t.exports}catch(n){throw t=0,n}};var De=R((xi,jn)=>{jn.exports={name:"@taqwright/lime-cli",version:"0.7.19",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 D=R((Ni,qe)=>{"use strict";var Pn="https://www.taqwright.ai";qe.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:Pn,HTTP_TIMEOUT_MS:3e5,MAX_RESPONSE_BYTES:52428800}});var Q=R((Ci,Ke)=>{"use strict";var{URL:Dn}=require("url"),{HTTP_TIMEOUT_MS:Me,MAX_RESPONSE_BYTES:Ue}=D();function Be(e){let t=String(e||"").replace(/^\[|\]$/g,"").toLowerCase();return t==="localhost"||t==="127.0.0.1"||t==="::1"||t.endsWith(".localhost")}var qn=["just a moment","attention required! | cloudflare","checking your browser","/cdn-cgi/challenge-platform","cf-error-details","cf-browser-verification","_incapsula_resource"];function Fe(e,t){if(!t||typeof t!="string")return null;let n=t.slice(0,4e3).toLowerCase();return qn.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 Dn(n);if(i.protocol==="http:"&&!Be(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,u)=>{let c=o.request(a,d=>{let p="",g=0;d.setEncoding("utf8"),d.on("data",w=>{if(g+=Buffer.byteLength(w),g>Ue){c.destroy(new Error(`Server response exceeded ${Ue} bytes`));return}p+=w}),d.on("end",()=>{let w;try{w=p?JSON.parse(p):{}}catch{let m=Fe(d.statusCode,p);return u(new Error(m||`Server returned non-JSON (${d.statusCode}): ${p.substring(0,200)}`))}l({status:d.statusCode,body:w})})});c.setTimeout(Me,()=>{c.destroy(new Error(`request timed out after ${Me/1e3}s`))}),c.on("error",u),s&&c.write(s),c.end()})}};function Mn(e,t){if(!e.body||!e.body.success)throw new Error(`${t} \u2014 ${e.body&&e.body.error||"unknown"}`);return e.body}Ke.exports={isLoopbackHost:Be,HttpClient:ue,expectSuccess:Mn,describeEdgeBlock:Fe}});var q=R((Li,Ye)=>{"use strict";var Un={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 Bn({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}Ye.exports={CLOUD_PROVIDERS:Un,buildCloudSettingsPayload:Bn}});var fe=R((ji,We)=>{"use strict";var Z=require("fs"),pe=require("path"),Fn=["taqwright","webdriverio-js","appium-java-junit5","appium-python-pytest","appium-ruby-rspec","appium-csharp-nunit"],Kn={taqwright:"deterministic","webdriverio-js":"gemini","appium-java-junit5":"gemini","appium-python-pytest":"gemini","appium-ruby-rspec":"gemini","appium-csharp-nunit":"gemini"},P={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)"},Yn=5*1024*1024,he={exists:e=>Z.existsSync(e),readText:e=>{try{return Z.statSync(e).size>Yn?null:Z.readFileSync(e,"utf8")}catch{return null}},list:e=>{try{return Z.readdirSync(e)}catch{return[]}}};function Hn(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 Wn=new Set(["node_modules","dist","build","target","bin","obj","vendor","out","coverage"]);function Vn(e,t){let n=new Set([""]),r=[];try{r=e.list(t)}catch{r=[]}for(let s of r)s.startsWith(".")||Wn.has(s)||n.add(s);return[...n]}function Gn(e,t,n){let r=n?pe.join(t,n):t,s=d=>pe.join(r,d),i=d=>n?`${n}/${d}`:d,o=[],a=Hn(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:P.taqwright,reason:"taqwright dependency/config found",manifest:i("package.json")});let u=l.some(d=>/^wdio\.conf\.(js|ts|mjs|cjs)$/.test(d));(a.webdriverio||a["@wdio/cli"]||u)&&o.push({id:"webdriverio-js",label:P["webdriverio-js"],reason:u?"wdio.conf.* found":"webdriverio dependency found",manifest:i("package.json")});for(let d of["pom.xml","build.gradle","build.gradle.kts"]){let p=e.readText(s(d));if(p&&/io\.appium|java-client/.test(p)){o.push({id:"appium-java-junit5",label:P["appium-java-junit5"],reason:`io.appium:java-client in ${i(d)}`,manifest:i(d)});break}}for(let d of["requirements.txt","pyproject.toml"]){let p=e.readText(s(d));if(p&&/appium-python-client/i.test(p)){o.push({id:"appium-python-pytest",label:P["appium-python-pytest"],reason:`Appium-Python-Client in ${i(d)}`,manifest:i(d)});break}}let c=e.readText(s("Gemfile"));c&&/appium_lib(_core)?/.test(c)&&o.push({id:"appium-ruby-rspec",label:P["appium-ruby-rspec"],reason:"appium_lib in Gemfile",manifest:i("Gemfile")});for(let d of l){if(!/\.csproj$/i.test(d))continue;let p=e.readText(s(d));if(p&&/Appium\.WebDriver/i.test(p)){o.push({id:"appium-csharp-nunit",label:P["appium-csharp-nunit"],reason:`Appium.WebDriver in ${i(d)}`,manifest:i(d)});break}}return o}function He(e,t=he){let n=new Map;for(let r of Vn(t,e))for(let s of Gn(t,e,r))n.has(s.id)||n.set(s.id,s);return[...n.values()]}function Jn(e,t=he){let n=He(e,t);return n.some(r=>r.id==="taqwright")||n.push({id:"taqwright",label:P.taqwright,reason:"default output"}),n}function Xn(e){let{requested:t,configured:n,remembered:r,offeredIds:s,detectedRealIds:i,taqwrightDetected:o,javaSignal:a}=e,l=u=>!!u&&s.includes(u);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"}We.exports={FRAMEWORK_IDS:Fn,FRAMEWORK_ENGINES:Kn,FRAMEWORK_LABELS:P,nodeFs:he,detectFrameworks:He,rankFrameworks:Jn,pickDefaultFramework:Xn}});var ve=R((Pi,ze)=>{"use strict";var me=require("path"),{nodeFs:zn}=fe(),Ve={taqwright:"tests","webdriverio-js":"test/specs","appium-java-junit5":"src/test/java","appium-python-pytest":"tests","appium-ruby-rspec":"spec","appium-csharp-nunit":"Tests"},Qn="tests",Zn={taqwright:".spec.ts","webdriverio-js":".spec.ts","appium-java-junit5":".java","appium-python-pytest":".py","appium-ruby-rspec":"_spec.rb","appium-csharp-nunit":".cs"},er=new Set(["appium-python-pytest","appium-ruby-rspec"]);function ge(e){return Ve[e]||Qn}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 tr(e){return ye(e).split("_").filter(Boolean).join("-")}var nr=6,rr=new Set(["and","or","the","a","an","to","of","in","on","at","as","with","for","then","into","from","by","that","is"]),sr=/@|\d{6,}/;function ir(e){let t=String(e||"").trim().split(/\s+/).filter(Boolean).filter(n=>!sr.test(n)).slice(0,nr);for(;t.length>1&&rr.has(t[t.length-1].toLowerCase());)t.pop();return we(t.join(" "))||"RecordedTest"}function or(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 ar(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 Ge(e,t){let n;return er.has(e)?n=ye(t):n=we(t),`${n}${Zn[e]||""}`}function Y(e){return String(e||"").split(/[/\\]+/).filter(t=>t&&t!=="."&&t!=="..").join("/")}function cr(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=Y(e).split("/");return t.pop(),t.join("/")}function Je(e){let t=/(?:^|\/)src\/(?:test|main)\/java\/(.+)$/.exec(Y(e));return t&&t[1].split("/").filter(Boolean).join(".")||void 0}function lr(e){let t=Y(e),n=t.search(/\/src\/(?:test|main)\/java(?:\/|$)/);return n>0?t.slice(0,n):""}function dr(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 Xe={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$/}},ur=new Set(["node_modules","dist","build","target","bin","obj","vendor","out","coverage",".git"]),pr=2e4;function hr(e,t,n=zn){let r=Xe[t];if(!r)return[];let s=[],i=pr,o=a=>{if(i<=0)return;let l=a?me.join(e,a):e,u=[];try{u=n.list(l)}catch{return}for(let c of u){if(i--<=0)return;if(c.startsWith(".")||ur.has(c))continue;let d=a?`${a}/${c}`:c;if(n.isDir?n.isDir(me.join(e,d)):n.list(me.join(e,d)).length>0){o(d);continue}r.file.test(c)&&(r.segment&&!`/${be(d)}/`.includes(`/${r.segment}/`)||s.push(d))}};return o(""),s.sort()}function fr(e){let{framework:t,testName:n}=e,r=Ge(t,n),s=(a,l,u=0)=>{let c=Y(a),d=t==="appium-java-junit5"?Je(c):void 0;return{dir:c,fileName:r,...d?{packageName:d}:{},source:l,neighbourCount:u}};if(e.remembered!=null)return s(e.remembered,"remembered");let i=dr(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")}ze.exports={CONVENTIONAL_DIRS:Ve,TEST_PATTERNS:Xe,conventionalDir:ge,pascalCase:we,snakeCase:ye,kebabCase:tr,testNameFromGoal:ir,validateTestName:or,validateDest:cr,validateScenarioKey:ar,fileNameFor:Ge,normalizeDir:Y,javaPackageOf:Je,moduleRootOf:lr,findExistingTests:hr,resolveExportTarget:fr}});var tt=R((Di,et)=>{"use strict";var{CLOUD_PROVIDERS:ee}=q(),{validateScenarioKey:mr}=ve();function gr(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 wr(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 yr(e){let t=e[0];return t&&Ze.includes(t)?{command:t,rest:e.slice(1)}:{command:"run",rest:e}}function br(e="taqwright"){return e==="json"?"recorded-steps.json":"RecordedTest.spec.ts"}function vr(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 u;if(e.scenario!==void 0){let h=typeof e.scenario=="string"?e.scenario.trim():"",S=h?mr(h):"requires a scenario key";S?n.push(`--scenario ${S}`):u=h}let c,d;e["gemini-key"]!==void 0&&(typeof e["gemini-key"]!="string"||!e["gemini-key"].trim()?n.push("--gemini-key requires a value (or set GEMINI_API_KEY)"):(c=e["gemini-key"].trim(),d="flag")),!c&&typeof t.GEMINI_API_KEY=="string"&&t.GEMINI_API_KEY.trim()&&(c=t.GEMINI_API_KEY.trim(),d="env");let p,g;if(e["qwen-key"]!==void 0&&(typeof e["qwen-key"]!="string"||!e["qwen-key"].trim()?n.push("--qwen-key requires a value (or set QWEN_API_KEY)"):(p=e["qwen-key"].trim(),g="flag")),!p&&typeof t.QWEN_API_KEY=="string"&&t.QWEN_API_KEY.trim()&&(p=t.QWEN_API_KEY.trim(),g="env"),e["max-steps"]!==void 0){let h=Number(e["max-steps"]);(!Number.isFinite(h)||h<1||h>200)&&n.push("--max-steps must be a number between 1 and 200")}let w;e.platform&&(w=String(e.platform).toLowerCase(),["android","ios"].includes(w)||n.push(`--platform must be android or ios (got "${e.platform}")`));let I=!!e.cloud,m=typeof e.os=="string"?e.os:void 0,y=typeof e["app-url"]=="string"?e["app-url"]:void 0,k=typeof e.app=="string"?e.app:void 0,b=typeof e.caps=="string"&&e.caps.trim()?e.caps.trim():void 0,v,T,E;if(I&&(v=e.cloud===!0?"browserstack":String(e.cloud).toLowerCase(),ee[v]||n.push(`--cloud must be ${Object.keys(ee).join(" or ")} (got "${e.cloud}")`)),I){(!e.device||typeof e.device!="string")&&n.push('--cloud requires --device (the cloud device name, e.g. "Google Pixel 8")'),w||n.push("--cloud requires --platform (android or ios)"),m||n.push("--cloud requires --os (the device OS version, e.g. 14.0)"),k?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.)"):y||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"),y&&ee[v]&&(v==="browserstack"&&y.startsWith("lt://")?n.push(`--app-url "${y}" is a LambdaTest app id, but the provider is browserstack`):v==="lambdatest"&&y.startsWith("bs://")&&n.push(`--app-url "${y}" 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 h=ee[v];h&&(T=typeof e["cloud-username"]=="string"&&e["cloud-username"].trim()||t[h.envUsername]||void 0,E=typeof e["cloud-access-key"]=="string"&&e["cloud-access-key"].trim()||t[h.envAccessKey]||void 0,T||n.push(`cloud runs require a username \u2014 pass --cloud-username or set ${h.envUsername} (credentials are never read from your LIME account for CLI runs)`),E||n.push(`cloud runs require an access key \u2014 pass --cloud-access-key or set ${h.envAccessKey}`))}else e.app!==void 0&&(typeof e.app!="string"||!e.app.trim()?n.push("--app requires a path to an APK"):e.device?w&&w!=="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:w,projectName:l,scenarioKey:u,geminiKey:c,geminiKeySource:d,qwenKey:p,qwenKeySource:g,app:k,cloud:I,cloudProvider:v,cloudUsername:T,cloudAccessKey:E,os:m,appUrl:y,capsPath:b,format:a}}var Qe=["browserstack","lambdatest","appium"];function Er(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 } }`):Qe.includes(r)||n.push(`--caps block "${r}" is not one of ${Qe.join("/")} \u2014 it will be ignored (typo?)`);return{errors:t,warnings:n}}et.exports={parseArgs:gr,parseCommand:yr,positionalPaths:wr,validateArgs:vr,extToFilename:br,validateCapsObject:Er,COMMANDS:Ze}});var st=R((qi,rt)=>{"use strict";var{DEFAULT_SERVER:Sr}=D(),nt={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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@taqwright/lime-cli",
3
- "version": "0.7.18",
3
+ "version": "0.7.19",
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",