skeptic-cli 0.2.1 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. package/AGENTS.md +20 -53
  2. package/LICENSE +24 -0
  3. package/LICENSES.md +92 -3
  4. package/README.md +25 -49
  5. package/agent-skills/skeptic/SKILL.md +150 -38
  6. package/dist/adb-DUGGW3FV.mjs +2 -0
  7. package/dist/adb-driver-TBOCCKEO.mjs +3 -0
  8. package/dist/adb-session-AVVXL3QQ.mjs +2 -0
  9. package/dist/add-G7JFXU4S.mjs +67 -0
  10. package/dist/audit-ID2BSVYC.mjs +7 -0
  11. package/dist/auto-spawn-4TO4DBO6.mjs +2 -0
  12. package/dist/browser-verbs-U54BZACP.mjs +21 -0
  13. package/dist/browsers-install-F2ZWEBOX.mjs +3 -0
  14. package/dist/chokidar-KB6GLGX4.mjs +7 -0
  15. package/dist/chunk-2N64R5DC.mjs +457 -0
  16. package/dist/chunk-2VSGDT7T.mjs +7 -0
  17. package/dist/chunk-2YKSIUIN.mjs +3 -0
  18. package/dist/chunk-2ZORFJJP.mjs +11 -0
  19. package/dist/chunk-42J77CYA.mjs +3 -0
  20. package/dist/chunk-6TLKI7UN.mjs +4 -0
  21. package/dist/chunk-6U6H22OR.mjs +3 -0
  22. package/dist/chunk-7BFRKEFV.mjs +160 -0
  23. package/dist/chunk-7ZUWKIDM.mjs +3 -0
  24. package/dist/chunk-AH75LR2T.mjs +3 -0
  25. package/dist/chunk-B26AZRXU.mjs +41 -0
  26. package/dist/chunk-BIGNULF6.mjs +12 -0
  27. package/dist/chunk-COVGJJ47.mjs +3 -0
  28. package/dist/chunk-CWNYWHJ2.mjs +143 -0
  29. package/dist/chunk-EU3OXJX4.mjs +3 -0
  30. package/dist/chunk-EYVJTUBL.mjs +3 -0
  31. package/dist/chunk-G22LGRZ4.mjs +4 -0
  32. package/dist/chunk-I4JX25Y5.mjs +13 -0
  33. package/dist/chunk-IXBYLSGB.mjs +5 -0
  34. package/dist/chunk-IYLF56WL.mjs +3 -0
  35. package/dist/chunk-K65JNLTT.mjs +3 -0
  36. package/dist/chunk-LPJF33QP.mjs +3 -0
  37. package/dist/chunk-MHNEFL35.mjs +3 -0
  38. package/dist/chunk-N3533BCE.mjs +3 -0
  39. package/dist/chunk-NXTEMSUR.mjs +4 -0
  40. package/dist/chunk-OHVNABCL.mjs +10 -0
  41. package/dist/chunk-QICYK6XT.mjs +10 -0
  42. package/dist/chunk-RU7M6UGM.mjs +5 -0
  43. package/dist/chunk-S3M2RTHJ.mjs +7 -0
  44. package/dist/chunk-U3KRIAEU.mjs +3 -0
  45. package/dist/chunk-WBPZGXOC.mjs +8 -0
  46. package/dist/chunk-YB25SMQ2.mjs +141 -0
  47. package/dist/chunk-ZN6MI2TU.mjs +4 -0
  48. package/dist/client-UR65IKYX.mjs +2 -0
  49. package/dist/comment-F734YE5S.mjs +6 -0
  50. package/dist/cookies-X7W5U3VE.mjs +3 -0
  51. package/dist/daemon-M2J3L6JW.mjs +7 -0
  52. package/dist/device-fixture-MJSDIP75.mjs +2 -0
  53. package/dist/devices-NG4P5UPS.mjs +4 -0
  54. package/dist/devtools-N5AYAR54.mjs +12 -0
  55. package/dist/doctor-WRKNAK3W.mjs +4 -0
  56. package/dist/extractor-Y477MBN6.mjs +2 -0
  57. package/dist/html-reporter-7QHIRHEY.mjs +2 -0
  58. package/dist/index.d.ts +87 -120
  59. package/dist/index.mjs +26 -1741
  60. package/dist/init-4PXSHPNW.mjs +15 -0
  61. package/dist/ink-reporter-4EN7CRMK.mjs +3 -0
  62. package/dist/inspect-3XFCCSUL.mjs +19 -0
  63. package/dist/ios-tools-WK66CQ7Q.mjs +2 -0
  64. package/dist/json-reporter-SXWPAXRY.mjs +2 -0
  65. package/dist/junit-reporter-5AT3OFWV.mjs +12 -0
  66. package/dist/mail-GTOZFXJ5.mjs +14 -0
  67. package/dist/observe-MHEV7OVP.mjs +3 -0
  68. package/dist/render-MHOBDOSP.mjs +383 -0
  69. package/dist/run-GW3X5ANC.mjs +2 -0
  70. package/dist/scaffold-6ZF6K6Y5.mjs +38 -0
  71. package/dist/screenshot-GUJSIRQB.mjs +2 -0
  72. package/dist/session-daemon-cmd-23UUK6TD.mjs +4 -0
  73. package/dist/simctl-driver-PNUN7W7G.mjs +5 -0
  74. package/dist/skeptic.mjs +3 -1754
  75. package/dist/slack-reporter-EEFO66V6.mjs +8 -0
  76. package/dist/watch-A7ZLUYR2.mjs +3 -0
  77. package/dist/webhook-reporter-BXJGZS2I.mjs +3 -0
  78. package/dist/worker.mjs +3 -724
  79. package/package.json +13 -11
@@ -0,0 +1,2 @@
1
+ import {createRequire}from'node:module';export{c as buildWorkerConfig,d as ensureJsonReporter,b as parseVideoSize,e as resolveRunExitCode,f as runRun}from'./chunk-WBPZGXOC.mjs';import'./chunk-EU3OXJX4.mjs';import'./chunk-CWNYWHJ2.mjs';import'./chunk-6U6H22OR.mjs';import'./chunk-AH75LR2T.mjs';import'./chunk-RU7M6UGM.mjs';import'./chunk-2VSGDT7T.mjs';import'./chunk-IYLF56WL.mjs';import'./chunk-ZN6MI2TU.mjs';import'./chunk-MHNEFL35.mjs';import'./chunk-S3M2RTHJ.mjs';import'./chunk-2YKSIUIN.mjs';/*! @license skeptic-cli — see LICENSES.md for third-party attributions */
2
+ createRequire(import.meta.url);
@@ -0,0 +1,38 @@
1
+ import {createRequire}from'node:module';import {a as a$2}from'./chunk-COVGJJ47.mjs';import {b}from'./chunk-2ZORFJJP.mjs';import'./chunk-NXTEMSUR.mjs';import'./chunk-OHVNABCL.mjs';import'./chunk-B26AZRXU.mjs';import'./chunk-7BFRKEFV.mjs';import'./chunk-N3533BCE.mjs';import {a as a$1}from'./chunk-IYLF56WL.mjs';import {d}from'./chunk-ZN6MI2TU.mjs';import'./chunk-MHNEFL35.mjs';import {a}from'./chunk-S3M2RTHJ.mjs';import'./chunk-2YKSIUIN.mjs';import*as $ from'fs';import $__default from'fs';import*as l from'path';import l__default from'path';/*! @license skeptic-cli — see LICENSES.md for third-party attributions */
2
+ createRequire(import.meta.url);
3
+ function k(e){return e.toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/(^-|-$)/g,"").slice(0,60).replace(/-+$/g,"")||"test"}function S(e,t){let s=k(e);if(!$__default.existsSync(l__default.join(t,`${s}.spec.ts`)))return s;let i=2;for(;$__default.existsSync(l__default.join(t,`${s}-${i}.spec.ts`));)i++;return `${s}-${i}`}var q=async(e,t)=>{let s=l.resolve(process.cwd(),t.output??"tests");$.mkdirSync(s,{recursive:true});let i=t.platform==="android"||t.platform==="ios-sim"?t.platform:"web",n=i!=="web",{title:r,refs:o}=i==="android"?await x(e,t,s):i==="ios-sim"?await O(e,t,s):await D(e,t),a$1=t.name??r??(n?e:new URL(e).hostname),c=S(a$1||"scaffold",s),f=l.join(s,`${c}.spec.ts`),g=n?H({target:e,title:r,slug:c,refs:o,platform:i}):J({url:e,title:r,slug:c,refs:o});$.writeFileSync(f,g,"utf-8");let d$1=n?`skeptic run ${l.relative(process.cwd(),f)} --platform ${i}`:`skeptic run ${l.relative(process.cwd(),f)}`;d.success(`Scaffolded ${a.cyan(l.relative(process.cwd(),f))}`),d.info(a.dim(`Discovered ${o.length} interactive element(s). Fill in the commented interactions and assertions, then \`${d$1}\`.`));},D=async(e,t)=>{let i=await(await a$1()).chromium.launch({headless:!t.headed}),n=a$2.fromBrowser(i,true);try{let r=await n.newSession();await r.open(e,{waitUntil:"load"});let o=await r.title(),a=await r.snapshot({viewport:!0,includeCursorInteractive:!0,extractLinkHrefs:!0}),c=b(a,{interactive:!0}).refs.slice(0,12);return await r.close(),{title:o,refs:c}}finally{await n.close();}},x=async(e,t,s)=>{let{createAdb:i,listDevices:n}=await import('./adb-DUGGW3FV.mjs'),{AndroidAdbDriverSession:r}=await import('./adb-session-AVVXL3QQ.mjs'),o=t.target;if(!o){let d=(await n().catch(()=>[])).filter(b=>b.state==="device");if(d.length===0)throw new Error("[android] no device/emulator found. Start one (or pass --target <serial>); `skeptic devices` lists them.");o=d[0].serial;}let a=new r(i({serial:o}),o,s);await a.open(e);let c=await a.title(),f=await a.snapshot(),g=b(f,{interactive:true}).refs.slice(0,12);return await a.close(),{title:c,refs:g}},O=async(e,t,s)=>{let{IosSimDriver:i}=await import('./simctl-driver-PNUN7W7G.mjs'),n=await i.create({...t.target?{udid:t.target}:{}});try{let r=await n.newSession({artifactDir:s});await r.open(e);let o=await r.title(),a=await r.snapshot(),c=b(a,{interactive:!0}).refs.slice(0,12);return await r.close(),{title:o,refs:c}}finally{await n.close();}},N=e=>{let t=e.trim();return t?`/${t.slice(0,40).replace(/[.*+?^${}()|[\]\\/]/g,"\\$&")}/`:"/.*/"},H=e=>{let t=e.refs.length?e.refs.map(i=>{let n=`${i.role}${i.name?` "${i.name}"`:""}`,r=i.selectorHint||`@${i.ref}`;return ` // ${n}
4
+ // await device.click(${JSON.stringify(r)});`}).join(`
5
+ `):" // (no interactive elements discovered \u2014 add your own selectorHints)",s=e.refs.find(i=>i.selectorHint)?.selectorHint;return `import { test, expect } from "skeptic-cli";
6
+
7
+ // Scaffolded by \`skeptic scaffold ${e.target} --platform ${e.platform}\`. Fill in
8
+ // the real interactions + assertions, then run with
9
+ // \`skeptic run tests/${e.slug}.spec.ts --platform ${e.platform}\`.
10
+ test(${JSON.stringify(`${e.title||e.slug} smoke`)}, async ({ device }) => {
11
+ await device.open(${JSON.stringify(e.target)});
12
+ const snap = await device.snapshot();
13
+
14
+ // Discovered interactive elements (uncomment + adapt; re-snapshot after each screen change):
15
+ ${t}
16
+
17
+ ${s?`expect(snap.has(${JSON.stringify(s)})).toBe(true);`:'// expect(snap.has("text=...")).toBe(true);'}
18
+ await device.screenshot(${JSON.stringify(e.slug)});
19
+ });
20
+ `},J=e=>{let t=e.refs.length?e.refs.map(s=>` // ${`${s.role}${s.name?` "${s.name}"`:""}`}
21
+ // await tree.byRole(${JSON.stringify(s.role)}, { name: ${JSON.stringify(s.name)} }).click();`).join(`
22
+ `):" // (no interactive elements discovered \u2014 add your own locators)";return `import { test, expect } from "skeptic-cli";
23
+
24
+ // Scaffolded by \`skeptic scaffold ${e.url}\`. Fill in the real interactions
25
+ // and assertions below, then run with \`skeptic run\`.
26
+ test(${JSON.stringify(`${e.title||e.slug} smoke`)}, async ({ page, snapshot, screenshot, observability }) => {
27
+ await page.goto(${JSON.stringify(e.url)});
28
+ await expect(page).toHaveTitle(${N(e.title)});
29
+
30
+ const tree = await snapshot(page, { interactive: true, compact: true });
31
+
32
+ // Discovered interactive elements (uncomment + adapt the ones you need):
33
+ ${t}
34
+
35
+ await screenshot(${JSON.stringify(e.slug)}, { fullPage: true });
36
+ await observability.expectNoConsoleErrors();
37
+ });
38
+ `};export{q as runScaffold};
@@ -0,0 +1,2 @@
1
+ import {createRequire}from'node:module';export{b as captureAnnotatedScreenshot,a as takeScreenshot}from'./chunk-B26AZRXU.mjs';import'./chunk-7BFRKEFV.mjs';import'./chunk-N3533BCE.mjs';import'./chunk-ZN6MI2TU.mjs';import'./chunk-MHNEFL35.mjs';import'./chunk-S3M2RTHJ.mjs';import'./chunk-2YKSIUIN.mjs';/*! @license skeptic-cli — see LICENSES.md for third-party attributions */
2
+ createRequire(import.meta.url);
@@ -0,0 +1,4 @@
1
+ import {createRequire}from'node:module';import {a as a$2}from'./chunk-COVGJJ47.mjs';import {b,a}from'./chunk-IXBYLSGB.mjs';import {b as b$2}from'./chunk-2ZORFJJP.mjs';import {c as c$1,b as b$1}from'./chunk-6TLKI7UN.mjs';import'./chunk-NXTEMSUR.mjs';import'./chunk-OHVNABCL.mjs';import {m as m$1,j as j$1,k,h as h$1,n,p as p$1,i,l,o}from'./chunk-2VSGDT7T.mjs';import'./chunk-B26AZRXU.mjs';import'./chunk-7BFRKEFV.mjs';import'./chunk-N3533BCE.mjs';import {a as a$1}from'./chunk-IYLF56WL.mjs';import {c}from'./chunk-ZN6MI2TU.mjs';import'./chunk-MHNEFL35.mjs';import'./chunk-S3M2RTHJ.mjs';import'./chunk-2YKSIUIN.mjs';import*as m from'fs';/*! @license skeptic-cli — see LICENSES.md for third-party attributions */
2
+ createRequire(import.meta.url);
3
+ function H(){c(process.stderr);}var y=class{tail=Promise.resolve();run(e){let s=this.tail.then(e,e);return this.tail=s.then(()=>{},()=>{}),s}},p=class{constructor(e){this.opts=e;this.idleSeconds=e.sessionIdleSeconds??180,this.now=e.now??Date.now;}opts;driver=null;sessions=new Map;pending=new Map;driverPromise=null;idleSeconds;now;get size(){return this.sessions.size}has(e){return this.sessions.has(e)}ensureDriver(){return this.driver?Promise.resolve(this.driver):this.driverPromise?this.driverPromise:(this.driverPromise=(async()=>{if(this.opts.createDriver)this.driver=await this.opts.createDriver();else {let s=await(await a$1())[this.opts.engine].launch({headless:!this.opts.headed});this.driver=a$2.fromBrowser(s,true);}return this.driver})(),this.driverPromise)}getOrCreate(e){let s=this.sessions.get(e);if(s)return this.touch(s),Promise.resolve(s);let t=this.pending.get(e);if(t)return t;let r=(async()=>{try{let o=await(await this.ensureDriver()).newSession({artifactDir:l(e)});this.opts.onSessionCreate&&await this.opts.onSessionCreate(o);let a={name:e,session:o,mutex:new y,idle:null,createdAt:this.now(),lastUsedAt:this.now(),url:""};return this.sessions.set(e,a),this.touch(a),this.opts.onChange?.(),a}finally{this.pending.delete(e);}})();return this.pending.set(e,r),r}async run(e,s){let t=await this.getOrCreate(e);return t.mutex.run(async()=>{this.touch(t);let r=await s(t.session);return t.url=t.session.url(),r})}async close(e){let s=this.sessions.get(e);return s?(s.idle&&clearTimeout(s.idle),this.sessions.delete(e),await s.session.close().catch(()=>{}),this.opts.onChange?.(),true):false}async closeAll(){let e=[...this.sessions.keys()];for(let s of e)await this.close(s);this.driver&&(await this.driver.close().catch(()=>{}),this.driver=null);}list(){let e=this.now();return [...this.sessions.values()].map(s=>({name:s.name,url:s.url,ageMs:e-s.createdAt,idleMs:e-s.lastUsedAt}))}touch(e){e.lastUsedAt=this.now(),e.idle&&clearTimeout(e.idle),this.idleSeconds>0&&(e.idle=setTimeout(()=>{this.close(e.name);},this.idleSeconds*1e3),e.idle.unref?.());}};var u=n=>({result:n}),d=n=>({error:n}),F="Stable artifact: copy a selectorHint into your test. In this session @eN refs persist until navigation or the next `skeptic snapshot`.",j=n=>typeof n.session=="string"&&n.session?n.session:"default",h=(n,e)=>n.has(e)?null:`no open session "${e}" \u2014 run \`skeptic open <url>\` first`,_=async(n,e)=>{if(typeof e.ref=="string"&&e.ref)return n.resolveRef(e.ref);if(typeof e.selector=="string"&&e.selector)return n.resolveSelector(e.selector);throw new Error("a ref (@eN) or --selector is required")},q=async(n,e,s)=>{switch(e){case "click":return n.click();case "fill":return n.fill(String(s.text??""));case "type":return n.type(String(s.text??""));case "press":return n.press(String(s.key??""));case "hover":return n.hover();case "check":return n.check();case "uncheck":return n.uncheck();case "select":return n.selectOption(s.value??"");case "scrollIntoView":return n.scrollIntoView();default:throw new Error(`unknown act verb "${e}"`)}};async function L(n,e){let s=n.params??{},t=j(s);try{switch(n.method){case "session.open":{let r=String(s.url??"");if(!r)return d("session.open: a url is required");let i=await e.run(t,async o=>(await o.open(r,{...typeof s.waitUntil=="string"?{waitUntil:s.waitUntil}:{},...s.timeoutMs!==void 0?{timeoutMs:Number(s.timeoutMs)}:{}}),{session:t,url:o.url(),title:await o.title()}));return u(i)}case "session.snapshot":{let r=h(e,t);if(r)return d(r);let i=await e.run(t,async o=>{let a=await o.snapshot({viewport:s.viewport!==!1,includeCursorInteractive:!0,extractLinkHrefs:!0}),c=b$2(a,{interactive:!!s.interactive,compact:!!s.compact});return {session:t,url:o.url(),title:await o.title(),yaml:c.yaml,refs:c.refs,stats:c.stats,truncated:a.truncated,note:F}});return u(i)}case "session.act":{let r=h(e,t);if(r)return d(r);let i=String(s.verb??""),o=await e.run(t,async a=>{if(i==="scroll")return await a.scroll({dx:Number(s.dx??0),dy:Number(s.dy??0)}),{ok:!0,verb:i};let c=await _(a,s);return await q(c,i,s),{ok:!0,verb:i,target:s.ref??s.selector}});return u(o)}case "session.query":{let r=h(e,t);if(r)return d(r);let i=String(s.query??"text"),o=await e.run(t,async a=>{if(i==="url")return {value:a.url()};if(i==="title")return {value:await a.title()};let c=await _(a,s);if(i==="text")return {value:await c.textContent()};if(i==="box")return {value:await c.boundingBox()};if(i==="visible")return {value:await c.isVisible()};if(i==="enabled")return {value:await c.isEnabled()};if(i==="checked")return {value:await c.isChecked()};if(i==="value")return {value:await c.inputValue()};throw new Error(`unsupported query "${i}" (supported: text, value, box, visible, enabled, checked, url, title)`)});return u(o)}case "session.screenshot":{let r=h(e,t);if(r)return d(r);let i=String(s.name??"screenshot"),o=await e.run(t,async a=>{let c=await a.screenshot(i,{fullPage:!!s.fullPage,annotate:!!s.annotate});return {path:c.path,...c.annotations?{annotations:c.annotations}:{}}});return u(o)}case "session.record":{let r=h(e,t);if(r)return d(r);let i=Number(s.durationSec??3),o=await e.run(t,async a=>{if(!a.recordVideo)throw new Error("video recording is only supported on the Android driver; for web use `skeptic run --video`");return a.recordVideo(i)});return u(o)}case "session.observe":{let r=h(e,t);if(r)return d(r);let i=String(s.collector??"console"),o=await e.run(t,async a=>{let c=await a.collectEvidence();if(i==="errors"){let f=(c.console?.messages??[]).filter(l=>l.type==="error");return {collector:"errors",errors:f,count:f.length}}return {collector:i,snapshot:c[i]??null}});return u(o)}case "session.wait":{let r=h(e,t);if(r)return d(r);let i=await e.run(t,async o=>{if(s.ms!==void 0)return await o.wait(Number(s.ms)),{ok:!0};if(typeof s.selector=="string"&&s.selector)return await(await o.resolveSelector(s.selector)).waitFor({...typeof s.state=="string"?{state:s.state}:{},...s.timeoutMs!==void 0?{timeoutMs:Number(s.timeoutMs)}:{}}),{ok:!0};throw new Error("session.wait needs --ms or a selector")});return u(i)}case "session.close":{let r=await e.close(t);return u({closed:r,session:t})}case "session.list":return u({sessions:e.list()});default:return d(`unknown session method: ${n.method}`)}}catch(r){return d(r instanceof Error?r.message:String(r))}}var $=n=>n==="chromium"||n==="firefox"||n==="webkit",z=1024*1024,G=()=>{let n=i(),e=null;try{e=m.readFileSync(n,"utf8").trim();}catch{}if(e){let s=Number(e);if(Number.isFinite(s)&&s>0&&a(s)&&s!==process.pid)throw new Error(`session daemon already running at PID ${s}`);B();}m.writeFileSync(n,String(process.pid),{mode:384});},B=()=>{for(let n of [i(),j$1(),k()])try{m.unlinkSync(n);}catch{}},J=async n=>{await n.attachCollectors([new c$1({captureLimit:500,redact:true}),new b$1({captureLimit:500,duplicateWindowMs:50})]);},U=async n$1=>{m$1(),G(),m.writeFileSync(j$1(),n$1.cliVersion,{mode:384}),m.writeFileSync(k(),`${n$1.engine}
4
+ ${n$1.headed?"headed":"headless"}`,{mode:384});let e=null,t=$(n$1.engine)?void 0:n$1.engine==="ios-sim"?async()=>{let{IosSimDriver:l}=await import('./simctl-driver-PNUN7W7G.mjs');return l.create()}:async()=>{let{AdbDriver:l}=await import('./adb-driver-TBOCCKEO.mjs');return l.create()},r=new p({engine:$(n$1.engine)?n$1.engine:"chromium",headed:n$1.headed,sessionIdleSeconds:n$1.sessionIdleSeconds??180,...t?{createDriver:t}:{onSessionCreate:J},onChange:()=>o.reset()}),i=h$1();n(i);let o=new b(n$1.idleTimeoutSeconds??600,()=>{if(r.size>0){o.arm();return}a();}),a=l=>e||(e=(async()=>{o.disarm();try{await r.closeAll();}catch{}try{await g.close();}catch{}B();try{m.unlinkSync(i);}catch{}})(),e),g=await p$1(i,(l,P)=>(P.onActivity(),o.reset(),l.method==="daemon.ping"?Promise.resolve(W(l.params,n$1)):l.method==="daemon.shutdown"?(P.close(),a(),Promise.resolve({result:{ok:true}})):l.method==="daemon.status"?Promise.resolve({result:{ok:true,engine:n$1.engine,headed:n$1.headed,sessions:r.size}}):L(l,r)),{onAccept:()=>o.reset(),maxLineBytes:z});o.arm();let f=l=>{a().then(()=>process.exit(0));};return process.on("SIGTERM",f),process.on("SIGINT",f),process.on("SIGHUP",f),{registry:r,socket:g,shutdown:a}},W=(n,e)=>{let s=n??{};return o(s.authToken)?typeof s.engine=="string"&&s.engine!==e.engine?{result:{ok:false,reason:"engine-mismatch"}}:typeof s.headed=="boolean"&&s.headed!==e.headed?{result:{ok:false,reason:"headed-mismatch"}}:typeof s.cliVersion=="string"&&s.cliVersion!==e.cliVersion?{result:{ok:false,reason:"version-mismatch"}}:{result:{ok:true,sessions:e.engine}}:{result:{ok:false,reason:"auth-failed"}}};var K=["chromium","firefox","webkit","android","ios-sim"],he=async n=>{H();let e=K.includes(n.engine)?n.engine:"chromium",s=n.headless!==true;try{await U({engine:e,headed:s,cliVersion:"1.0.0",...typeof n.idleTimeout=="number"?{idleTimeoutSeconds:n.idleTimeout}:{},...typeof n.sessionIdle=="number"?{sessionIdleSeconds:n.sessionIdle}:{}});}catch(t){console.error(`session-daemon: ${t instanceof Error?t.message:String(t)}`),process.exit(1);}await new Promise(()=>{});};export{he as runSessionDaemon};
@@ -0,0 +1,5 @@
1
+ import {createRequire}from'node:module';import {b as b$1}from'./chunk-BIGNULF6.mjs';import'./chunk-G22LGRZ4.mjs';import {g as g$1,c,b}from'./chunk-LPJF33QP.mjs';import'./chunk-N3533BCE.mjs';import'./chunk-ZN6MI2TU.mjs';import'./chunk-MHNEFL35.mjs';import'./chunk-S3M2RTHJ.mjs';import'./chunk-2YKSIUIN.mjs';import*as g from'fs';import*as B from'path';/*! @license skeptic-cli — see LICENSES.md for third-party attributions */
2
+ createRequire(import.meta.url);
3
+ var W=s=>{switch(s.type){case "Button":return "button";case "TextField":case "SecureTextField":return "textbox";case "SearchField":return "searchbox";case "StaticText":return "text";case "Image":return "image";case "Slider":return "slider";case "Switch":case "Toggle":return "switch";case "Heading":return "heading";case "Link":return "link";case "Cell":return "listitem";case "Application":return "application";case "NavigationBar":return "navigation";case "TabBar":return "tablist";default:return (s.role_description||"generic").toString()}},X=new Set(["Button","TextField","SecureTextField","SearchField","Slider","Switch","Toggle","Link","Cell","Stepper","SegmentedControl"]),L=s=>X.has(s.type??"")||(s.custom_actions?.length??0)>0,T=s=>(s.AXLabel??s.title??s.AXValue??"").toString().trim(),C=s=>{for(let e of s.children??[]){let t=T(e);if(t)return t}for(let e of s.children??[]){let t=C(e);if(t)return t}return ""},V=(s,e)=>{let t=(s.AXUniqueId??"").trim();return t?`id=${t}`:e?`label=${e}`:`type=${s.type??"?"}`},$=(s,e={})=>{let t;try{let i=JSON.parse(s);t=Array.isArray(i)?i:[i];}catch{t=[];}let r=[],n=new Map,a=[],o=0,d=e.bundleId?`app=${e.bundleId}`:"app=*",w=(i,l,u)=>{let c=W(i),S=L(i),I=T(i),F=S||!u&&I.length>0&&(i.type==="StaticText"||i.type==="Heading"),A=u,D=l;if(F){let f=`e${++o}`,b=I||(S?C(i):""),h=i.frame,y=h?{x1:h.x,y1:h.y,x2:h.x+h.width,y2:h.y+h.height}:null,_=V(i,b),H=b.length>0&&c!=="generic"?"aria":"cursor-interactive";r.push({ref:f,kind:H,role:c,name:b,nth:0,scopeSelector:d,selectorHint:_,matchCountAtSnapshot:0}),y&&n.set(f,{ref:f,bounds:y,center:{x:Math.round((y.x1+y.x2)/2),y:Math.round((y.y1+y.y2)/2)},type:i.type??"",axUniqueId:(i.AXUniqueId??"").trim(),enabled:i.enabled!==false,value:i.AXValue??null}),a.push(`${" ".repeat(l)}- ${c}${b?` "${b}"`:""} [ref=${f}]`),D=l+1,S&&(A=true);}for(let f of i.children??[])w(f,D,A);};for(let i of t)w(i,0,false);let p=new Map;for(let i of r){let l=`${i.role} ${i.name}`,u=p.get(l)??0;i.nth=u,p.set(l,u+1);}for(let i of r)i.matchCountAtSnapshot=p.get(`${i.role} ${i.name}`)??1;let m;if(e.screen){let{width:i,height:l}=e.screen;m=new Set;for(let[u,c]of n)(c.center.x<0||c.center.y<0||c.center.x>i||c.center.y>l)&&m.add(u);m.size===0&&(m=void 0);}return {capture:{yaml:a.join(`
4
+ `),entries:r,truncated:false,...m?{offViewportRefs:m}:{}},nodes:n}},P=s=>{try{let e=JSON.parse(s),t=Array.isArray(e)?e:[e];for(let r of t)if(r.frame&&r.frame.width>0&&r.frame.height>0)return {width:Math.round(r.frame.width),height:Math.round(r.frame.height)}}catch{}return null};var U={Enter:"40",Return:"40",Tab:"43",Backspace:"42",Delete:"42",Escape:"41",Space:"44",ArrowUp:"82",ArrowDown:"81",ArrowLeft:"80",ArrowRight:"79"},v=class{constructor(e,t,r){this.tools=e;this.udid=t;this.node=r;}tools;udid;node;tap(e,t){return this.tools.axe(["tap","-x",String(e),"-y",String(t),"--udid",this.udid])}async click(){await this.tap(this.node.center.x,this.node.center.y);}async fill(e){await this.click(),await this.clearField(),await this.typeText(e);}async type(e){await this.typeText(e);}async press(e){let t=U[e]??(/^\d+$/.test(e)?e:null);if(!t)throw new Error(`[axeKey] unsupported key "${e}" (use a name like Enter/Backspace or an HID keycode)`);await this.tools.axe(["key",t,"--udid",this.udid]);}hover(){return Promise.resolve()}async check(){await this.click();}async uncheck(){await this.click();}selectOption(){return this.click()}scrollIntoView(){return Promise.resolve()}waitFor(e){return Promise.resolve()}boundingBox(){let e=this.node.bounds;return Promise.resolve({x:e.x1,y:e.y1,width:e.x2-e.x1,height:e.y2-e.y1})}textContent(){return Promise.resolve(this.node.value??null)}isVisible(){let e=this.node.bounds;return Promise.resolve(e.x2>e.x1&&e.y2>e.y1)}isEnabled(){return Promise.resolve(this.node.enabled)}isChecked(){let e=(this.node.value??"").toString().toLowerCase();return Promise.resolve(e==="1"||e==="true"||e==="on")}inputValue(){return Promise.resolve((this.node.value??"").toString())}async typeText(e){e.length!==0&&await this.tools.axe(["type",e,"--udid",this.udid]);}async clearField(){await this.tools.axe(["key-combo","--modifiers","227","--key","4","--udid",this.udid]).catch(()=>{}),await this.tools.axe(["key","42","--udid",this.udid]).catch(()=>{});}};var O=(s,e,t)=>{let r=o=>{let d=t.get(o.ref);if(!d)return false;if(s.startsWith("id="))return d.axUniqueId===s.slice(3);if(s.startsWith("label="))return o.name===s.slice(6);if(s.startsWith("type="))return d.type===s.slice(5);if(s.startsWith("role=")){let[w,p]=s.slice(5).split(":");return o.role===w&&(p===void 0||o.name===p)}return o.name===s},n=e.find(o=>o.selectorHint===s);if(n)return t.get(n.ref)??null;let a=e.find(r);return a?t.get(a.ref)??null:null};var x=class{constructor(e,t,r){this.tools=e;this.udid=t;this.artifactDir=r;}tools;udid;artifactDir;nodes=new Map;lastEntries=[];lastYaml=null;currentTarget="";bundleId="";screen=null;async open(e,t){this.currentTarget=e,e.includes("://")?await this.tools.simctl(["openurl",this.udid,e]):(this.bundleId=e,await this.tools.simctl(["terminate",this.udid,e]).catch(()=>{}),await this.tools.simctl(["launch",this.udid,e])),this.invalidate(),await this.wait(800);}url(){return this.bundleId||this.currentTarget}title(){return Promise.resolve(this.bundleId||this.currentTarget)}async snapshot(e){let t=await this.describeStable();this.screen||(this.screen=P(t));let{capture:r,nodes:n}=$(t,{...this.bundleId?{bundleId:this.bundleId}:{},...this.screen?{screen:this.screen}:{}});return this.nodes=n,this.lastEntries=r.entries,this.lastYaml=r.yaml,r}async resolveRef(e){let t=e.startsWith("@")?e.slice(1):e,r=this.nodes.get(t);if(!r)throw new Error(`[iosRef:not_found] {"ref":"${t}","available":${JSON.stringify([...this.nodes.keys()])},"hasSnapshot":${this.lastYaml!==null}}`);return new v(this.tools,this.udid,r)}async resolveSelector(e){let t=O(e,this.lastEntries,this.nodes);if(!t)throw new Error(`[iosSelector:not_found] no node matches "${e}" in the last snapshot`);return new v(this.tools,this.udid,t)}async screenshot(e,t){if(t?.annotate)throw new Error("[iosScreenshot:annotate_unsupported] [ios-sim] annotated screenshots are not supported on the mobile driver yet; use the web driver or take a plain screenshot.");g.mkdirSync(this.artifactDir,{recursive:true});let r=B.join(this.artifactDir,`${e.replace(/[^a-zA-Z0-9_-]/g,"_")}.png`);await this.tools.axe(["screenshot","--udid",this.udid,"--output",r]);let n=g.existsSync(r)?g.readFileSync(r):Buffer.alloc(0);return {path:r,diagnostics:n.length>0?b$1(n):[]}}async scroll(e){this.screen||(this.screen=await this.snapshotScreen()??this.screen);let t=this.screen?.width??402,r=this.screen?.height??874,n=Math.round(t/2),a=e.dy??400,o=Math.round(r*.6);await this.tools.axe(["swipe","--start-x",String(n),"--start-y",String(o),"--end-x",String(n),"--end-y",String(o-a),"--duration","0.3","--udid",this.udid]);}wait(e){return new Promise(t=>setTimeout(t,e))}attachCollectors(e){return Promise.resolve()}async collectEvidence(){let t=(await this.tools.simctl(["spawn",this.udid,"log","show","--last","8s","--style","compact"],2e4).catch(()=>"")).split(`
5
+ `).map(r=>r.trim()).filter(r=>r.length>0&&(this.bundleId===""||r.includes(this.bundleId))).slice(-200).map(r=>({type:q(r),text:r,timestamp:Date.now()}));return {console:{messages:t,summary:{total:t.length,errorCount:t.filter(r=>r.type==="error").length,warningCount:t.filter(r=>r.type==="warning").length,infoCount:t.filter(r=>r.type==="info").length,redactionDisabled:false}}}}detachCollectors(){return Promise.resolve()}close(){return this.invalidate(),Promise.resolve()}invalidate(){this.nodes=new Map,this.lastEntries=[],this.lastYaml=null;}async describeStable(){let e=await this.tools.axe(["describe-ui","--udid",this.udid]),t=j(e);for(let r=0;r<5;r++){await this.wait(300);let n=await this.tools.axe(["describe-ui","--udid",this.udid]);e=n;let a=j(n);if(a===t)break;t=a;}return e}async snapshotScreen(){let e=await this.tools.axe(["describe-ui","--udid",this.udid]).catch(()=>"");return e?P(e):null}},j=s=>{try{let e=JSON.parse(s),t=[],r=n=>{let a=n.frame,o=n.AXLabel;typeof o=="string"&&o&&a&&t.push(`${o}@${Math.round(a.x)},${Math.round(a.y)}`);for(let d of n.children??[])r(d);};for(let n of Array.isArray(e)?e:[e])r(n);return t.join("|")}catch{return String(s.length)}},q=s=>/\bError\b|\bFault\b/.test(s)?"error":/\bWarning\b/.test(s)?"warning":/\bInfo\b/.test(s)?"info":"log";var M=class s{constructor(e,t){this.udid=e;this.tools=t;}udid;tools;static async create(e={}){e.tools||g$1();let t=e.udid;if(!t){let n=await c();if(n.length===0)throw new Error("no booted iOS simulator (simctl list devices booted is empty); boot one first");t=n[0].udid;}let r=e.tools??b();return new s(t,r)}static fromTools(e,t){return new s(e,t)}newSession(e){let t=e?.artifactDir??process.cwd();return Promise.resolve(new x(this.tools,this.udid,t))}close(){return Promise.resolve()}};export{M as IosSimDriver};