llynt 0.2.3 → 0.2.5

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 (3) hide show
  1. package/README.md +2 -2
  2. package/cli.js +102 -25
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -21,7 +21,7 @@ export LLYNT_API_KEY=<your-key>
21
21
  npx llynt check https://your-app.dev
22
22
  ```
23
23
 
24
- Same command. 28 rules, SARIF output, PR gate ready.
24
+ Same command. 26 rules, SARIF output, PR gate ready.
25
25
 
26
26
  ## What it checks
27
27
 
@@ -29,7 +29,7 @@ Llynt checks what the browser actually renders -- computed layout, accessibility
29
29
 
30
30
  **Anonymous (7 rules):** contrast, hit targets, overflow, broken assets, text overflow, overlap, rendering
31
31
 
32
- **Free account (28 rules):** adds layout drift, focus order, occlusion, clipping, visual order, tab order, readability, and more
32
+ **Free account (26 rules):** adds layout drift, focus order, occlusion, clipping, visual order, tab order, readability, and more
33
33
 
34
34
  ## How it works
35
35
 
package/cli.js CHANGED
@@ -1,51 +1,128 @@
1
1
  #!/usr/bin/env node
2
- "use strict";var qe=Object.create;var ue=Object.defineProperty;var We=Object.getOwnPropertyDescriptor;var Ye=Object.getOwnPropertyNames;var Je=Object.getPrototypeOf,Ke=Object.prototype.hasOwnProperty;var z=(e,r)=>()=>(e&&(r=e(e=0)),r);var Ge=(e,r)=>{for(var t in r)ue(e,t,{get:r[t],enumerable:!0})},Xe=(e,r,t,s)=>{if(r&&typeof r=="object"||typeof r=="function")for(let o of Ye(r))!Ke.call(e,o)&&o!==t&&ue(e,o,{get:()=>r[o],enumerable:!(s=We(r,o))||s.enumerable});return e};var A=(e,r,t)=>(t=e!=null?qe(Je(e)):{},Xe(r||!e||!e.__esModule?ue(t,"default",{value:e,enumerable:!0}):t,e));function Ae(e){let r=e.trim();if(!r)throw new Error("LLYNT_API_URL is empty");return r.includes("/v1/check")?r:r.replace(/\/+$/g,"")+"/v1/check"}function at(e){return new Promise((r,t)=>{oe.gzip(e,(s,o)=>{s?t(s):r(o)})})}function lt(e){return new Promise((r,t)=>{oe.brotliCompress(e,(s,o)=>{s?t(s):r(o)})})}function ct(e,r,t,s){return new Promise((o,d)=>{let i=(e.protocol==="https:"?it:nt).request({...r,protocol:e.protocol,hostname:e.hostname,port:e.port,path:e.pathname+e.search},l=>{let f=[];l.on("data",y=>f.push(y)),l.on("end",()=>{o({statusCode:l.statusCode??0,body:Buffer.concat(f)})})});i.on("error",l=>d(l)),i.setTimeout(s,()=>{i.destroy(new Error(`Hosted API request timed out after ${s}ms`))}),i.end(t)})}async function Ce(e,r){let t=new URL(e.apiUrl),s=e.timeoutMs??3e4,o=Buffer.from(JSON.stringify(r),"utf8"),d=e.compression??"gzip",m=d==="identity"?o:d==="br"?await lt(o):await at(o),i={"content-type":"application/json",accept:"application/json","user-agent":"llynt-hosted-client/0","content-length":String(m.length)};d!=="identity"&&(i["content-encoding"]=d),e.apiKey&&(i.authorization=`Bearer ${e.apiKey}`);let{statusCode:l,body:f}=await ct(t,{method:"POST",headers:i},m,s),y=f.toString("utf8");if(l<200||l>=300)throw new Error(`Hosted API error (${l}): ${y.slice(0,500)}`);let u=JSON.parse(y);if(!u||u._schemaVersion!=="llynt-hosted-check-result@0"||!Array.isArray(u.issues))throw new Error("Hosted API response schema mismatch");return u}var nt,it,oe,de=z(()=>{"use strict";nt=A(require("http")),it=A(require("https")),oe=A(require("zlib"))});async function Re(e,r,t){let s=t?.maxDepth??gt,o=t?.maxNodes??yt,d=typeof t?.includeComputedStyles=="boolean"?t.includeComputedStyles?"all":"none":t?.computedStyleMode??"essential",m=Array.isArray(t?.computedStyleProps)?t.computedStyleProps:void 0,i=t?.internComputedStyles??!0,l=t?.includeUrlAttributes??!1,f=t?.redactUrlAttributes??!0,y=await e.evaluate(({selector:P,maxDepth:L,maxNodes:j,computedStyleMode:N,computedStyleProps:g,essentialProps:b,internComputedStyles:v,includeUrlAttributes:_,redactUrlAttributes:H})=>{function C(a){let c=2166136261;for(let h=0;h<a.length;h++)c^=a.charCodeAt(h),c=Math.imul(c,16777619);return(c>>>0).toString(36)}function M(a){let c=Object.keys(a).sort(),h=[];for(let n of c)h.push(`${n}=${a[n]}`);return h.join(";")}function le(a){try{let c=new URL(a,document.baseURI);return c.protocol==="http:"||c.protocol==="https:"?`${c.protocol}//${c.host}${c.pathname}`:c.protocol==="data:"?"data:[redacted]":c.protocol==="blob:"?"blob:[redacted]":`${c.protocol}[redacted]`}catch{return"[redacted]"}}function X(a,c){let h=[],n=a;for(;n&&n!==c;){let p=n.parentElement??null;if(!p)break;let w=n.tagName.toLowerCase(),S=Array.from(p.children).filter(V=>String(V?.tagName??"").toLowerCase()===w),O=Math.max(0,S.indexOf(n));h.push(`${w}:${O}`),n=p}return h.push(c.tagName.toLowerCase()),h.reverse().join("/")}function K(a,c){let h=[],n=a;for(;n&&n!==c;){let p=n.parentElement??null;if(!p)break;let w=n.tagName.toLowerCase(),S=n.id;if(S){h.push(`#${CSS.escape(S)}`);break}let O=Array.from(p.children);if(O.filter(D=>String(D?.tagName??"").toLowerCase()===w).length===1)h.push(w);else{let D=O.indexOf(n)+1;h.push(`${w}:nth-child(${D})`)}n=p}return h.length?h.reverse().join(" > "):c.tagName.toLowerCase()}function Q(a){return!!(a.getAttribute("data-figma-id")||a.getAttribute("data-llynt-node-id")||a.getAttribute("data-component")||a.getAttribute("data-testid")||a.id)}function Z(a,c){let h=a.getAttribute("data-figma-id");if(h)return h;let n=a.getAttribute("data-llynt-node-id");if(n)return n;let p=a.getAttribute("data-component");if(p)return p;let w=a.getAttribute("data-testid");if(w)return w;let S=a.id;if(S)return S;let O=X(a,c),V=C(O);return`llynt-${a.tagName.toLowerCase()}-${V}`}function F(a){let c={};for(let h of Array.from(a.attributes)){let n=h.name;if(n==="id"||n==="class"||n==="role"||n==="alt"||n==="type"||n==="width"||n==="height"||n==="target"||n==="rel"||n==="loading"||n==="name"||n==="autocomplete"||n==="disabled"||n==="required"||n==="placeholder"||n==="for"||n==="tabindex"||n==="lang"||n==="style"||n.startsWith("aria-")||n==="data-figma-id"||n==="data-component"||n==="data-testid"||n==="data-primary"||n.startsWith("data-llynt-")){let p=h.value;typeof p=="string"&&(c[n]=p)}if(n==="href"||n==="src"){let p=h.value;typeof p=="string"&&(p===""||p==="#"||p.startsWith("javascript:")?c[n]=p:p.length>0&&(c[n]=_&&!H?p:le(p)))}}return Object.keys(c).length>0?c:void 0}function ee(a){if(N==="none")return;let c=window.getComputedStyle(a),h={},n=Array.isArray(g)&&g.length?g:N==="essential"?b:null;if(n){for(let p of n){let w=c.getPropertyValue(p);typeof w=="string"&&w.trim()&&(h[p]=w.trim())}return Object.keys(h).length>0?h:void 0}for(let p=0;p<c.length;p++){let w=c[p],S=c.getPropertyValue(w);typeof S=="string"&&S.trim()&&(h[w]=S.trim())}return Object.keys(h).length>0?h:void 0}function k(a){let c=a.getBoundingClientRect();return{left:c.left,top:c.top,width:c.width,height:c.height}}let R=document.querySelector(P||"body")??document.body,G=0,me=!1,he=new Set,te={};function fe(a,c,h){if(G+=1,G>j)return me=!0,null;let n=Q(a),p=Z(a,R);if(he.has(p)){let U=C(p+":"+G);p=`${p}-${U}`}if(he.add(p),!n)try{a.setAttribute("data-llynt-node-id",p)}catch{}let w=ee(a),S;if(v&&w&&Object.keys(w).length){let U=M(w);S=`cs-${C(U)}`,te[S]||(te[S]=w)}let O=K(a,R),V=(a.textContent?.trim()??"").length,D,$=a;($.scrollWidth>$.clientWidth||$.scrollHeight>$.clientHeight)&&(D={scrollWidth:$.scrollWidth,clientWidth:$.clientWidth,scrollHeight:$.scrollHeight,clientHeight:$.clientHeight});let E={nodeId:p,parentId:h,tagName:a.tagName.toLowerCase(),attributes:F(a),bbox:k(a),computed:S?void 0:w,computedRef:S,cssSelector:O,textLen:V>0?V:void 0,scrollOverflow:D,children:[]};if(a.tagName==="IMG"){let U=a;E.naturalWidth=U.naturalWidth,E.naturalHeight=U.naturalHeight}if(c>=L)return E;for(let U of Array.from(a.children)){let ce=fe(U,c+1,p);if(!ce)break;E.children.push(ce)}return E.children.length===0&&delete E.children,E.parentId||delete E.parentId,E.attributes||delete E.attributes,E.computed||delete E.computed,E.computedRef||delete E.computedRef,E}let ge=fe(R,0);if(!ge)return{tree:{nodeId:Z(R,R),tagName:R.tagName.toLowerCase(),bbox:k(R)},stats:{nodeCount:Math.min(G,j),maxDepth:L,maxNodes:j,truncated:!0}};let ze=document.documentElement.getAttribute("lang")||void 0;return{tree:ge,computedStyles:Object.keys(te).length?te:void 0,stats:{nodeCount:Math.min(G,j),maxDepth:L,maxNodes:j,truncated:me},htmlLang:ze}},{selector:r.selector??"body",maxDepth:s,maxNodes:o,computedStyleMode:d,computedStyleProps:m,essentialProps:[...bt],internComputedStyles:i,includeUrlAttributes:l,redactUrlAttributes:f}),u={_schemaVersion:"dom-blob@0",_extractedAt:new Date().toISOString(),target:{url:r.url,selector:r.selector,viewport:r.viewport},tree:y.tree,computedStyles:y.computedStyles,stats:y.stats,htmlLang:y.htmlLang};if(u.stats){u.stats.coverage={completeness:u.stats.truncated?"truncated":"complete",reason:u.stats.truncated?"maxNodes":void 0},u.stats.confidence={extractionCompleteness:u.stats.truncated?"low":"high"};let P={...u,stats:{...u.stats}};delete P.stats.bytesRaw,delete P.stats.bytesGzip,delete P.stats.computedStyleMode,delete P.stats.urlAttributes;let L=Buffer.from(JSON.stringify(P),"utf8"),j=Pe.gzipSync(L);u.stats.bytesRaw=L.length,u.stats.bytesGzip=j.length,u.stats.computedStyleMode=d,u.stats.urlAttributes=l?f?"redacted":"raw":"omitted"}return u}var Pe,gt,yt,bt,Ie=z(()=>{"use strict";Pe=A(require("zlib")),gt=10,yt=1e4,bt=["display","position","overflow","overflow-x","overflow-y","visibility","opacity","width","height","min-width","min-height","max-width","max-height","margin-top","margin-right","margin-bottom","margin-left","padding-top","padding-right","padding-bottom","padding-left","gap","flex-basis","flex-shrink","flex-wrap","align-items","z-index","pointer-events","cursor","font-size","font-family","font-weight","line-height","letter-spacing","text-decoration","text-overflow","white-space","color","background-color","border-width","border-color","border-top-color","border-top-width","border-radius","box-shadow","outline","outline-style","transition-duration","transition-property","aspect-ratio","padding"]});function wt(){let e=ie.platform(),r=ie.homedir();if(e==="darwin")return q.join(r,"Library","Caches","llynt","playwright");if(e==="win32"){let o=process.env.LOCALAPPDATA||process.env.APPDATA||q.join(r,"AppData","Local");return q.join(o,"llynt","playwright")}let t=process.env.XDG_CACHE_HOME,s=t&&t.trim().length>0?t:q.join(r,".cache");return q.join(s,"llynt","playwright")}function Le(){return{source:"llynt",browsersPath:wt()}}function Ne(e){process.env.PLAYWRIGHT_BROWSERS_PATH=e.browsersPath}var ie,q,_e=z(()=>{"use strict";ie=A(require("os")),q=A(require("path"))});async function kt(){return ae||(ae=vt().then(e=>e.chromium).catch(e=>{throw ae=null,e})),ae}async function vt(){try{let{createRequire:e}=await import("module");return await import(e(process.cwd()+"/package.json").resolve("playwright"))}catch{return await import("playwright")}}function xt(){return["* {"," animation: none !important;"," transition: none !important;"," scroll-behavior: auto !important;","}","html {"," -webkit-text-size-adjust: 100% !important;","}"].join(`
3
- `)}async function Oe(){if(W&&W.isConnected())return W;if(Y)try{let e=await Y;if(e.isConnected())return e}catch{}return Y=jt().then(e=>(W=e,e.on("disconnected",()=>{W===e&&(W=null,Y=null)}),e)).catch(e=>{throw Y=null,e}),Y}async function jt(){let e=Le();Ne(e);let r=await kt(),t=["--no-sandbox","--disable-gpu","--disable-dev-shm-usage","--allow-file-access-from-files","--allow-file-access","--disable-lcd-text","--font-render-hinting=none"];try{return await r.launch({headless:!0,args:t})}catch{return await new Promise(s=>setTimeout(s,250)),await r.launch({headless:!0,args:t})}}async function St(e){try{await e.addStyleTag({content:xt()})}catch(r){console.warn("Failed to apply deterministic CSS:",r)}}async function $e(e,r,t){let o=await(await e.newContext({viewport:r||{width:1280,height:720},...t||{}})).newPage();return await St(o),o}var W,Y,ae,Ue=z(()=>{"use strict";_e();W=null,Y=null,ae=null});function At(e,r=1e4){let t=[];return t.push("(function(){"),t.push(`const selector = ${JSON.stringify(e)};`),t.push(`const maxNodes = ${JSON.stringify(r)};`),t.push("function fnv1aBase36(input){"),t.push(" let hash = 0x811c9dc5;"),t.push(" for (let i=0;i<input.length;i++){"),t.push(" hash ^= input.charCodeAt(i);"),t.push(" hash = Math.imul(hash, 0x01000193);"),t.push(" }"),t.push(" return (hash >>> 0).toString(36);"),t.push("}"),t.push("function domPathSignature(el, root){"),t.push(" const parts=[];"),t.push(" let cur=el;"),t.push(" while(cur && cur!==root){"),t.push(" const parent=cur.parentElement;"),t.push(" if(!parent) break;"),t.push(" const tag=cur.tagName.toLowerCase();"),t.push(" const siblings=Array.from(parent.children).filter(c=>c.tagName.toLowerCase()===tag);"),t.push(" const idx=Math.max(0,siblings.indexOf(cur));"),t.push(" parts.push(`${tag}:${idx}`);"),t.push(" cur=parent;"),t.push(" }"),t.push(" parts.push(root.tagName.toLowerCase());"),t.push(' return parts.reverse().join("/");'),t.push("}"),t.push("function hasStableIdentity(el){"),t.push(" return Boolean("),t.push(' el.getAttribute("data-figma-id") ||'),t.push(' el.getAttribute("data-llynt-node-id") ||'),t.push(' el.getAttribute("data-component") ||'),t.push(' el.getAttribute("data-testid") ||'),t.push(" el.id"),t.push(" );"),t.push("}"),t.push("function assignIds(){"),t.push(" const root = document.querySelector(selector) || document.body;"),t.push(" if(!root) return 0;"),t.push(" const used=new Set();"),t.push(" let assigned=0;"),t.push(" const walker=document.createTreeWalker(root, NodeFilter.SHOW_ELEMENT);"),t.push(" let node=walker.currentNode;"),t.push(" let seen=0;"),t.push(" while(node){"),t.push(" const el=node;"),t.push(" if(el && el instanceof Element){"),t.push(" seen++;"),t.push(" if(seen>maxNodes) break;"),t.push(" if(!hasStableIdentity(el)){"),t.push(" const sig=domPathSignature(el, root);"),t.push(" const hash=fnv1aBase36(sig);"),t.push(" const tag=el.tagName.toLowerCase();"),t.push(" let id=`llynt-${tag}-${hash}`;"),t.push(' if(used.has(id)){ id = `${id}-${fnv1aBase36(id+":"+seen)}`; }'),t.push(" used.add(id);"),t.push(' el.setAttribute("data-llynt-node-id", id);'),t.push(" assigned++;"),t.push(" } else {"),t.push(' const existing = el.getAttribute("data-llynt-node-id") || el.getAttribute("data-figma-id") || el.getAttribute("data-component") || el.getAttribute("data-testid") || el.id;'),t.push(" if(existing) used.add(existing);"),t.push(" }"),t.push(" }"),t.push(" node = walker.nextNode();"),t.push(" }"),t.push(" return assigned;"),t.push("}"),t.push("try { assignIds(); } catch {}"),t.push("})();"),t.join(`
4
- `)}async function Be(e,r,t){let s=At(r||"body",t?.maxNodes??1e4);await e.addInitScript(s),await e.evaluate(s)}var Te=z(()=>{"use strict"});var Ve={};Ge(Ve,{runHostedThinClientChecks:()=>Ct});async function Ct(e){let r=e.repoRoot??process.cwd(),t=e.artifactDir??"tools/artifacts/llynt",s=e.timeoutMs??3e4,o=e.computedStyleMode??"essential",d=e.internComputedStyles??!0,m=e.maxNodes,i=!!e.allowTruncated,l=!!(e.traceOnFailure??process.env.LLYNT_TRACE_ON_FAILURE==="1"),f=null;try{f=await Oe();let y=[];for(let u of e.targets){let P=u.selector??"body",L=u.viewports?.length?u.viewports:[{width:360,height:720},{width:768,height:720}],j=await $e(f),N=j.context(),g=!1,b;try{if(l)try{await N.tracing.start({screenshots:!0,snapshots:!0,sources:!1}),g=!0}catch{g=!1}j.setDefaultTimeout(s),await j.goto(u.url,{waitUntil:"domcontentloaded"});let v=[];for(let C of L){await j.setViewportSize(C),await j.waitForTimeout(50),await Be(j,P);let M=await Re(j,{url:u.url,selector:P,viewport:C},{computedStyleMode:o,internComputedStyles:d,...typeof m=="number"?{maxNodes:m}:{}});if(!i&&M.stats?.truncated)throw new Error(`Extraction truncated for ${u.url} at ${C.width}x${C.height}. Narrow the selector scope or increase limits (free-tier should be component-scoped).`);v.push(M)}let _=await Ce(e.client,{_schemaVersion:"llynt-hosted-check@0",blobs:v,rules:{overlap:!0,viewport:!0}}),H=_.issues.some(C=>C.severity==="error");if(g&&H){let C=u.url.toLowerCase().replace(/https?:\/\//g,"").replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"").slice(0,64)||"target";b=J.resolve(r,J.join(t,`trace-hosted-${C}.zip`))}y.push({url:u.url,selector:P,viewports:L,blobs:v,response:_,tracePath:b})}finally{if(g)try{b?(Me.mkdirSync(J.dirname(b),{recursive:!0}),await N.tracing.stop({path:b})):await N.tracing.stop()}catch{}await N.close().catch(()=>{})}}return y}finally{await f?.close().catch(()=>{})}}var Me,J,De=z(()=>{"use strict";Me=A(require("fs")),J=A(require("path"));Ie();Ue();de();Te()});var ye={name:"llynt",version:"0.1.2",private:!0,description:"Llynt \u2013 UI integrity engine (token/scale drift, SARIF, evidence packs)",license:"UNLICENSED",type:"commonjs",main:"dist/src/index.js",bin:{llynt:"dist/src/cli/index.js"},files:[],scripts:{build:`tsc -p tsconfig.json && node -e "const f='dist/src/cli/index.js';const c=require('fs').readFileSync(f,'utf8');if(!c.startsWith('#!'))require('fs').writeFileSync(f,'#!/usr/bin/env node\\n'+c)"`,typecheck:"tsc -p tsconfig.json --noEmit","build:cli":"npx esbuild src/cli/cli-thin.ts --bundle --platform=node --target=node18 --format=cjs --minify --tree-shaking=true --external:playwright --outfile=dist/npm/cli.js --banner:js='#!/usr/bin/env node' --legal-comments=none","build:lambda":"npx esbuild src/platform/hosted/lambda-handler.ts --bundle --platform=node --target=node22 --outfile=dist/lambda/handler.js --format=cjs --external:@aws-sdk/client-dynamodb --external:@aws-sdk/client-s3 --external:@aws-sdk/lib-dynamodb",cli:"node dist/src/cli/index.js","bundle:action":"npm run build && ncc build dist/src/cli/action.js -o dist-action --license licenses.txt","test:smoke:core":"node scripts/run-core-smoke-strict.js","routine:run":"npm run build && node dist/src/cli/index.js workflow routine-run --routine onboarding --dry-run --execution-class scaffolded_external","demo:no-figma":"npm run build && tsc -p tsconfig.tests.json && node dist/src/cli/index.js validate --repo-root . --artifact-dir tools/artifacts/evidence-lab/solidcopy --css-vars tools/evidence-lab/fixtures/instances/tokens.css --dtcg tools/evidence-lab/fixtures/instances/tokens.dtcg.json --tailwind-json tools/evidence-lab/fixtures/instances/tailwind.theme.json --include-json tools/evidence-lab/fixtures/instances/include.json && node dist-tests/tools/smoke/tests/runtime-smoke.js && node dist/tools/internal/run.js standardize --snapshot-dir tools/artifacts/evidence-lab/solidcopy","demo:evidence":"npm run demo:no-figma","demo:bundle":"node scripts/bundle-artifacts.js","demo:scoreboard":"node scripts/demo-scoreboard.js","demo:storybook":"node scripts/public-storybook-harness.js --dry-run","demo:verify":"node scripts/compare-ground-truth.js","demo:gallery":"node scripts/gallery-build.js --link","demos:build":"node scripts/demos-build.js","onboarding:setup-repo":"node tools/internal/scripts/setup-onboarding-repo.js","demo318:setup-repo":"npm run onboarding:setup-repo","demo:harvest":"node scripts/harvest-storybook.js","battle:bootstrap:external":"node tools/evidence-lab/scripts/bootstrap-external-corpus-truth.js","hosted:preflight":"node tools/internal/scripts/hosted-preflight.js","browser:preflight":"node dist/tools/internal/run.js browser preflight --json",doctor:"node dist/tools/internal/run.js doctor --json","hosted:smoke:remote":"node tools/internal/scripts/hosted-remote-smoke.js","check:capabilities":"node tools/internal/scripts/check-capability-registry.js","docs:ip-check":"node scripts/check-docs-publish-boundary.js","docs:reference":"npm run build && node scripts/generate-reference-docs.js","docs:site:prepare":"npm run docs:reference && node scripts/sync-reference-docs.js && npm run docs:ip-check","docs:dev":"npm run docs:site:prepare && vitepress dev docs/site","docs:build":"npm run docs:site:prepare && vitepress build docs/site","docs:preview":"vitepress preview docs/site","gate:real-external":"node tools/internal/scripts/verify-execution-class.js --manifest tools/artifacts/routines/latest-manifest.json --required real_external","test:routine-runtime-gates":"node dist-tests/tools/smoke/tests/routine-runtime-gates-smoke.js","gate:real-external-proof":"node tools/internal/scripts/require-real-external-proof.js","test:routine-real-external":"npm run build && node dist/src/cli/index.js workflow routine-run --routine onboarding --dry-run --execution-class real_external --out tools/artifacts/routines/latest-manifest.real_external.json && npm run gate:real-external-proof","test:onboarding-auth-mode":"node dist-tests/tools/smoke/tests/onboarding-auth-mode-smoke.js","test:hosted-runs-api-smoke":"node dist-tests/tools/smoke/tests/hosted-runs-api-smoke.js","ide:surface-pack":"node scripts/ide-surface-pack-build.js","ide:host-runtime-proof:capture":"node scripts/ide-host-runtime-proof-capture.js","ide:host-runtime-proof-lint":"node tools/internal/scripts/ide-host-runtime-proof-lint.js","github:agentic-workflow-pack":"node scripts/github-agentic-workflow-pack.js","github:agentic-runtime-proof:capture":"node scripts/github-agentic-runtime-proof-capture.js","github:agentic-runtime-proof-lint":"node tools/internal/scripts/github-agentic-runtime-proof-lint.js","test:ide-surface-pack-smoke":"node dist-tests/tools/smoke/tests/ide-surface-pack-smoke.js","test:ide-host-parity-audit-smoke":"node dist-tests/tools/smoke/tests/ide-host-parity-audit-smoke.js","test:ide-host-runtime-proof-capture-smoke":"node dist-tests/tools/smoke/tests/ide-host-runtime-proof-capture-smoke.js","test:ide-host-runtime-proof-lint-smoke":"node dist-tests/tools/smoke/tests/ide-host-runtime-proof-lint-smoke.js","test:github-agentic-workflow-pack-smoke":"node dist-tests/tools/smoke/tests/github-agentic-workflow-pack-smoke.js","test:github-agentic-runtime-proof-capture-smoke":"node dist-tests/tools/smoke/tests/github-agentic-runtime-proof-capture-smoke.js","test:onboarding-key-ops-contract":"node dist-tests/tools/smoke/tests/onboarding-key-ops-contract-smoke.js","test:routine-choreography":"node dist-tests/tools/smoke/tests/routine-choreography-smoke.js","test:onboarding-scorecard":"node tools/internal/scripts/onboarding-scorecard-build.js --enforce","test:real-external-scorecard":"node tools/internal/scripts/real-external-scorecard-build.js --enforce --min-passes 3","real-external:scorecard":"node tools/internal/scripts/real-external-scorecard-build.js","real-external:init-run":"node scripts/real-external-run-init.js","onboarding:scorecard":"node tools/internal/scripts/onboarding-scorecard-build.js","test:fixture-archetype-catalog":"node dist-tests/tools/smoke/tests/fixture-archetype-catalog-smoke.js","test:fixture-archetype-runtime":"node dist-tests/tools/smoke/tests/fixture-archetype-runtime-smoke.js","test:mcp-tier-guidance":"node dist-tests/tools/smoke/tests/mcp-tier-guidance-smoke.js","test:mcp-tier-guidance-retry":"node dist-tests/tools/smoke/tests/mcp-tier-guidance-retry-smoke.js","test:collections-runtime":"node dist-tests/tools/smoke/tests/collections-runtime-smoke.js","test:onboarding-preflight-runtime":"node dist-tests/tools/smoke/tests/onboarding-preflight-runtime-smoke.js","test:mcp-init-bootstrap-contract-smoke":"node dist-tests/tools/smoke/tests/mcp-init-bootstrap-contract-smoke.js","test:beta-scenario-coverage":"node tools/internal/scripts/beta-scenario-coverage-gate.js --enforce","beta:scenario-coverage":"node tools/internal/scripts/beta-scenario-coverage-gate.js","beta:closure-report":"node tools/internal/scripts/beta-recovery-closure-report.js","test:non-curated-pack-runner":"node dist-tests/tools/smoke/tests/non-curated-pack-runner-smoke.js","non-curated:plan":"node tools/evidence-lab/scripts/non-curated-pack-runner.js","routine:gate-decision":"node dist/src/cli/index.js workflow gate-decision","pr:execution-contract":"node dist/src/cli/index.js workflow pr-execution-contract","pr:impact-stateid-policy":"node dist/tools/internal/release/run.js pr-impact-stateid-policy","non-curated:metrics":"node tools/evidence-lab/scripts/non-curated-metrics-build.js","test:real-external-proof-lint":"node tools/internal/scripts/real-external-proof-lint.js --enforce","real-external:proof-lint":"node tools/internal/scripts/real-external-proof-lint.js","real-external:finalize-run":"node scripts/real-external-run-finalize.js","test:real-external-finalize":"node dist-tests/tools/smoke/tests/real-external-finalize-smoke.js","beta:next-actions":"node tools/internal/scripts/beta-recovery-next-actions.js","beta:status":"node tools/internal/scripts/beta-recovery-status.js","beta:refresh":"node tools/internal/scripts/beta-recovery-refresh.js","test:beta-refresh":"node dist-tests/tools/smoke/tests/beta-recovery-refresh-smoke.js","real-external:validate-run":"node scripts/real-external-run-validate.js","test:real-external-validate":"node dist-tests/tools/smoke/tests/real-external-run-validate-smoke.js","non-curated:init-run":"node tools/evidence-lab/scripts/non-curated-init-run.js","real-external:guide":"node scripts/real-external-run-guide.js","onboarding:attempt-import":"node scripts/onboarding-attempt-import.js","test:onboarding-attempt-import":"node dist-tests/tools/smoke/tests/onboarding-attempt-import-smoke.js","real-external:import-onboarding-attempt":"node scripts/real-external-import-onboarding-attempt.js","test:real-external-import-onboarding-attempt":"node dist-tests/tools/smoke/tests/real-external-import-onboarding-attempt-smoke.js","test:action-plan-rollup":"node dist-tests/tools/smoke/tests/action-plan-rollup-smoke.js","test:blocker-remediation-coverage-smoke":"node dist-tests/tools/smoke/tests/blocker-remediation-coverage-smoke.js","test:mcp-onboarding-contract-errors-smoke":"node dist-tests/tools/smoke/tests/mcp-onboarding-contract-errors-smoke.js","test:mcp-redaction-smoke":"node dist-tests/tools/smoke/tests/mcp-redaction-smoke.js","test:profile-surface-smoke":"node dist-tests/tools/smoke/tests/profile-surface-smoke.js","probabilistic:harness":"node scripts/run-probabilistic-agent-harness.js","mutation:audit":"node tools/evidence-lab/scripts/mutation-intent-audit.js","mutation:reconcile":"node tools/evidence-lab/scripts/mutation-truth-reconcile.js","correlation:root-cause":"node tools/evidence-lab/scripts/correlated-cluster-build.js","state-graph:build":"node tools/evidence-lab/scripts/state-graph-build.js","actionset:trace":"node tools/evidence-lab/scripts/actionset-trace-build.js","planner:seed-reduction":"node tools/evidence-lab/scripts/render-predicate-planner-build.js","discovery:flow-graph":"node tools/evidence-lab/scripts/discovery-flow-graph-build.js","l2:macro-coverage":"node dist/tools/internal/release/run.js l2-macro-coverage","l2:component-category-coverage":"node dist/tools/internal/release/run.js l2-component-category-coverage","l2:surface-bundle-map":"node dist/tools/internal/release/run.js l2-surface-bundle-map","network:scenario-runner":"node tools/evidence-lab/scripts/network-scenario-runner.js","seed:design-kit-harvest":"node tools/evidence-lab/scripts/design-kit-seed-harvester.js","chaos:mutation-sim":"node tools/evidence-lab/scripts/chaos-mutation-simulator.js","gauntlet:factory":"node tools/evidence-lab/scripts/gauntlet-factory.js","react:intake-normalize":"node tools/evidence-lab/scripts/real-react-intake-normalize.js","gallery:corpus-build":"node tools/evidence-lab/scripts/gallery-corpus-build.js","evidence-lab":"npx tsx tools/evidence-lab/pipeline.ts run","evidence-lab:status":"npx tsx tools/evidence-lab/pipeline.ts status","evidence-lab:gallery":"npx tsx tools/evidence-lab/pipeline.ts run --stage 4","evidence-lab:chaos":"npx tsx tools/evidence-lab/pipeline.ts run --stage 3","v0:operator-usability":"node tools/evidence-lab/scripts/v0-operator-usability-loop.js","audit:smoke-truth":"node scripts/smoke-truth-audit.js","audit:smoke-disposition":"node scripts/smoke-disposition-audit.js","replay:recipes":"node scripts/replay-recipe-build.js","replay:repro-gate":"node tools/internal/scripts/replay-repro-gate.js","core:quality-floor":"node dist/tools/internal/run.js core-quality-floor","invariant:coverage-gap":"node dist/tools/internal/run.js invariant-coverage-gap","test:core-invariant-quality-floor":"node dist/tools/internal/run.js core-quality-floor --enforce","test:invariant-coverage-gap":"node dist/tools/internal/run.js invariant-coverage-gap --enforce","test:l2-macro-profile-coverage-smoke":"node dist-tests/tools/smoke/tests/l2-macro-profile-coverage-smoke.js","test:l2-component-category-coverage-smoke":"node dist-tests/tools/smoke/tests/l2-component-category-coverage-smoke.js","test:l2-surface-bundle-map-smoke":"node dist-tests/tools/smoke/tests/l2-surface-bundle-map-smoke.js","test:network-scenario-runner-smoke":"node dist-tests/tools/smoke/tests/network-scenario-runner-smoke.js","test:chaos-mutation-simulator-smoke":"node dist-tests/tools/smoke/tests/chaos-mutation-simulator-smoke.js","test:gauntlet-factory-smoke":"node dist-tests/tools/smoke/tests/gauntlet-factory-smoke.js","test:real-react-intake-normalize-smoke":"node dist-tests/tools/smoke/tests/real-react-intake-normalize-smoke.js","test:gallery-corpus-build-smoke":"node dist-tests/tools/smoke/tests/gallery-corpus-build-smoke.js","test:replay-repro-gate-smoke":"node dist-tests/tools/smoke/tests/replay-repro-gate-smoke.js","test:pr-execution-contract-smoke":"node dist-tests/tools/smoke/tests/pr-execution-contract-smoke.js","test:pr-impact-stateid-policy-smoke":"node dist-tests/tools/smoke/tests/pr-impact-stateid-policy-smoke.js","onboarding:accuracy":"node tools/internal/scripts/onboarding-accuracy-eval.js","test:onboarding-accuracy":"node tools/internal/scripts/onboarding-accuracy-eval.js --enforce","onboarding:harness-matrix:run":"node tools/internal/scripts/onboarding-harness-matrix-run.js","onboarding:harness-matrix:eval":"node tools/internal/scripts/onboarding-harness-matrix-eval.js","test:onboarding-harness-matrix":"node dist-tests/tools/smoke/tests/onboarding-harness-matrix-smoke.js","evidence:freshness":"node dist/tools/internal/run.js evidence-freshness","test:evidence-freshness":"node dist/tools/internal/run.js evidence-freshness --enforce","canary:run":"node scripts/real-target-canary-run.js","canary:drift-gate":"node tools/internal/scripts/real-target-canary-drift-gate.js --enforce","canary:nightly":"npm run canary:run -- --enforce && npm run canary:drift-gate","benchmark:wall-time":"node tools/internal/scripts/wall-time-benchmark.js","test:concurrent-pr-isolation":"node tools/internal/scripts/concurrent-pr-isolation-test.js --enforce","test:real-target-canary-run-smoke":"node dist-tests/tools/smoke/tests/real-target-canary-run-smoke.js","gate:required-r1-claims":"node tools/internal/scripts/required-r1-claim-gate.js --enforce","test:unit":"vitest run","test:check-command-smoke":"npx tsc --target ES2022 --module commonjs --moduleResolution node --esModuleInterop --resolveJsonModule --skipLibCheck --types node --outDir dist-tests --rootDir . tools/smoke/tests/check-command-smoke.ts && node dist-tests/tools/smoke/tests/check-command-smoke.js","test:hosted-signup-smoke":"npx tsc --target ES2022 --module commonjs --moduleResolution node --esModuleInterop --resolveJsonModule --skipLibCheck --types node --outDir dist-tests --rootDir . tools/smoke/tests/hosted-signup-smoke.ts && node dist-tests/tools/smoke/tests/hosted-signup-smoke.js","site:llynt-dev:build":"npm --prefix src/brand/logo-animation run build",adversarial:"npm run build && npx tsx tools/evidence-lab/adversarial/run-all.ts"},dependencies:{"@actions/core":"^2.0.3","@modelcontextprotocol/sdk":"^1.26.0",commander:"^12.1.0",minimatch:"^9.0.5",playwright:"^1.45.0",zod:"^4.3.6"},devDependencies:{"@aws-sdk/client-dynamodb":"^3.1004.0","@aws-sdk/client-s3":"^3.1004.0","@aws-sdk/lib-dynamodb":"^3.1004.0","@types/node":"^20.11.30","@vercel/ncc":"^0.38.1",esbuild:"^0.27.3",typescript:"^5.4.5","vite-tsconfig-paths":"^4.3.2",vitepress:"^1.6.4",vitest:"^3.2.4"},overrides:{qs:"^6.14.2"}};function be(e){let r={};for(let t=0;t<e.length;t++){let s=e[t];if(!s.startsWith("--"))continue;let o=s.slice(2),d=e[t+1];!d||d.startsWith("--")?r[o]=!0:(r[o]=d,t++)}return r}function re(e){if(e==null)return!1;if(typeof e=="boolean")return e;let r=String(e).trim().toLowerCase();return!(r===""||r==="0"||r==="false"||r==="no"||r==="off")}function we(e){return e.replace(/\s+/g," ").trim()}function Ze(e){return e.replace(/\bllynt-[a-z]+-[a-z0-9]+\b/g,r=>{let t=r.split("-");return t.length>=2?`<${t[1]}>`:r})}function ke(e){let r=we(String(e.message??""));return r||`${we(String(e.ruleId??"unknown.rule"))} violation`}function et(e){return Ze(ke(e))}function tt(e){return e.length?e.map(r=>`${r.width}px`).join(", "):""}function ve(e){let r=e.issues.filter(l=>l.severity==="error"),t=e.issues.filter(l=>l.severity==="warning"),s=e.issues.filter(l=>l.severity==="info"),o=e.issues.length,d=e.viewports.length,m=tt(e.viewports),i=[];if(i.push(`llynt check ${e.url}`),i.push(""),e.rulesChecked!=null&&e.rulesAvailable!=null&&i.push(` ${e.rulesChecked} of ${e.rulesAvailable} rules checked${e.isAnonymous?" (anonymous)":""}`),i.push(` Capturing... ${d} viewport${d!==1?"s":""}${m?` (${m})`:""}`),i.push(` Analyzing... ${Math.max(0,Math.floor(e.nodeCount))} nodes, ${Math.max(0,Math.floor(e.costUnits))} cost units`),e.truncated&&i.push(" Note: Capture was truncated due to DOM limits."),i.push(""),o===0?i.push(" 0 issues found"):(i.push(` ${o} issues across ${d} viewport${d!==1?"s":""}`),i.push(` ${r.length} errors \xB7 ${t.length} warnings \xB7 ${s.length} info`)),i.push(""),e.reportUrl){let l=e.reportExpiresAt?` (expires ${e.reportExpiresAt.slice(0,10)})`:"";i.push(` Report: ${e.reportUrl}${l}`),i.push("")}if(o>0){let l=new Set,f=[];for(let y of[...r,...t,...s]){let u=et(y);!l.has(u)&&f.length<5&&(l.add(u),f.push(u))}if(f.length>0){i.push(" Top issues:");for(let u of f)i.push(` ${u}`);let y=o-f.length;y>0&&i.push(` ... and ${y} more (see report)`),i.push("")}e.gatedIssueCount&&e.gatedIssueCount>0&&(i.push(` ${e.gatedIssueCount} more issues found with full analysis \u2014 sign up at llynt.dev`),i.push(""))}return e.isAnonymous?(i.push(" Sign up free at https://llynt.dev for 28 rules, 2 viewports, 70 checks/week."),i.push(" Then: export LLYNT_API_KEY=<your-key>"),i.push("")):o>0&&(i.push(" Want full SARIF evidence + agent-ready fixes?"),i.push(" Set up the PR gate \u2014 your agent can do it:"),i.push(" npx llynt init --github")),i.join(`
5
- `).trimEnd()}function xe(e){let r={error:0,warning:1,info:2},t=e.issues.map(m=>({severity:m.severity,ruleId:m.ruleId,message:ke(m)})).sort((m,i)=>(r[m.severity]??3)-(r[i.severity]??3)),s=t.filter(m=>m.severity==="error").length,o=t.filter(m=>m.severity==="warning").length,d=t.length-s-o;return{url:e.url,viewports:e.viewports,nodeCount:Math.max(0,Math.floor(e.nodeCount)),costUnits:Math.max(0,Math.floor(e.costUnits)),issueCount:t.length,errors:s,warnings:o,infos:d,truncated:!!e.truncated,issues:t,gatedIssueCount:Math.max(0,Math.floor(e.gatedIssueCount??0)),...e.reportUrl?{reportUrl:e.reportUrl}:{},...e.reportExpiresAt?{reportExpiresAt:e.reportExpiresAt}:{},...e.rulesChecked!=null?{rulesChecked:e.rulesChecked,rulesAvailable:e.rulesAvailable}:{}}}async function je(e){try{let r=e.to.trim();if(!r||typeof fetch!="function")return;let t=`llynt check report \u2014 ${e.url}`,s=[`Your UI check found ${e.issueCount} issue${e.issueCount===1?"":"s"}.`,`View report: ${e.reportUrl}`,"","Want full SARIF evidence + agent-ready fixes?","Set up the PR gate: npx llynt init --github"].join(`
6
- `);await fetch("https://formsubmit.co/ajax/hello@llynt.dev",{method:"POST",headers:{"content-type":"application/json",accept:"application/json"},body:JSON.stringify({email:r,_subject:t,message:s})})}catch{}}var rt=A(require("http")),st=A(require("https"));function se(e){let r=e.trim();if(!r)throw new Error("LLYNT_API_URL is empty");let t=new URL(r),s=t.pathname.replace(/\/+$/g,"");s.endsWith("/v1/check")&&(s=s.slice(0,s.length-9)),t.pathname=s||"/",t.search="",t.hash="";let o=t.pathname==="/"?"":t.pathname.replace(/\/+$/g,"");return`${t.origin}${o}`}function ot(e,r,t,s){return new Promise((o,d)=>{let i=(e.protocol==="https:"?st:rt).request({...r,protocol:e.protocol,hostname:e.hostname,port:e.port,path:e.pathname+e.search},l=>{let f=[];l.on("data",y=>f.push(y)),l.on("end",()=>o({statusCode:l.statusCode??0,body:Buffer.concat(f)}))});i.on("error",d),i.setTimeout(s,()=>i.destroy(new Error(`Report upload timed out after ${s}ms`))),i.end(t)})}async function Se(e,r,t){try{let s=se(e),o=new URL(`${s}/reports`),d=1e4,m=Buffer.from(JSON.stringify(t),"utf8"),i=await ot(o,{method:"POST",headers:{"content-type":"application/json",accept:"application/json","content-length":String(m.length),"user-agent":"llynt-check-report-upload/0",...r?{authorization:`Bearer ${r}`}:{}}},m,d);if(i.statusCode<200||i.statusCode>=300)return null;let l=JSON.parse(i.body.toString("utf8"));return!l||typeof l.reportUrl!="string"||typeof l.expiresAt!="string"?null:l}catch{return null}}de();var ne=["marketing","app_shell","auth","onboarding","forms","checkout","billing","dashboard"];var B={profile:"PRODUCTION"},T={rules:{"token.color":"warning","token.spacing":"warning","token.typography":"warning"}},pe={marketing:{...B,surface:"marketing",...T},app_shell:{...B,surface:"app_shell",...T,ruleSeverityOverrides:{"dom.modal.scrollLock":"error"}},auth:{...B,surface:"auth",...T,ruleSeverityOverrides:{"dom.a11y.contrast":"error","dom.a11y.hitTarget":"error","dom.text.overflow":"error","dom.protected.clipping":"error","dom.modal.scrollLock":"error"}},onboarding:{...B,surface:"onboarding",...T,ruleSeverityOverrides:{"dom.a11y.hitTarget":"error","dom.text.overflow":"error","dom.protected.occlusion":"error","dom.modal.scrollLock":"error"}},forms:{...B,surface:"forms",...T,ruleSeverityOverrides:{"dom.a11y.hitTarget":"error","dom.a11y.contrast":"error","dom.text.overflow":"error","dom.form.errorAssociation":"error"}},checkout:{...B,surface:"checkout",...T,ruleSeverityOverrides:{"dom.a11y.hitTarget":"error","dom.a11y.contrast":"error","dom.protected.occlusion":"error","dom.protected.clipping":"error","dom.protected.overlap.interactive":"error","dom.form.errorAssociation":"error","dom.table.headerAlignment":"error","dom.modal.scrollLock":"error"}},billing:{...B,surface:"billing",...T,ruleSeverityOverrides:{"dom.text.overflow":"error","dom.viewport.horizontalOverflow":"error","dom.form.errorAssociation":"error","dom.table.headerAlignment":"error"}},dashboard:{...B,surface:"dashboard",...T,ruleSeverityOverrides:{"dom.viewport.horizontalOverflow":"error","dom.protected.occlusion":"error","dom.table.headerAlignment":"error"}}};function pt(e){return e.trim().toLowerCase().replace(/[\s-]+/g,"_")}function mt(e){return ne.includes(e)}function ht(e){if(!e)return null;let r=pt(e);return mt(r)?{name:r,policy:pe[r]}:null}function ft(e,r){if(!(!e&&!r))return e?r?{...e,...r,runtime:{...e.runtime??{},...r.runtime??{}},importanceOverrides:{...e.importanceOverrides??{},...r.importanceOverrides??{}},rules:{...e.rules??{},...r.rules??{}},ruleSeverityOverrides:{...e.ruleSeverityOverrides??{},...r.ruleSeverityOverrides??{}},allowRawValues:r.allowRawValues??e.allowRawValues}:e:r}function Ee(e,r){if(!e&&!r)return r;let t=ht(e);if(e&&!t)throw new Error(`Unknown surface profile: ${e}. Expected one of: ${ne.join(", ")}`);return ft(t?.policy,r)}var Et=0,Pt=2,I=3,He=[{width:360,height:720},{width:768,height:720}],Rt=[{width:360,height:720}];function It(e){for(let r=0;r<e.length;r++){let t=e[r];if(!t.startsWith("--"))return t;let s=e[r+1];s&&!s.startsWith("--")&&(r+=1)}}function Lt(e){if(typeof e!="string"||!e.trim())return He;let r=e.split(",").map(t=>t.trim()).filter(Boolean).map(t=>{let s=t.match(/^(\d{2,5})x(\d{2,5})$/i);if(!s)throw new Error(`Invalid viewport "${t}". Expected format WIDTHxHEIGHT.`);return{width:Number(s[1]),height:Number(s[2])}});return r.length?r:He}function Nt(e,r){if(!r)return e;let s=Ee(r,void 0)?.ruleSeverityOverrides;return s?e.map(o=>{let d=s[o.ruleId];return d!=="error"&&d!=="warning"||d===o.severity?o:{...o,severity:d}}):e}function _t(e){let r=e instanceof Error?e.message:String(e);return r.includes("Cannot find module 'playwright'")||r.includes("Cannot find package 'playwright'")||r.includes("ERR_MODULE_NOT_FOUND")}function Ot(e,r){let t=r instanceof Error?r.message:String(r);return t.includes("ERR_CONNECTION_REFUSED")?`Error: Could not reach ${e}
7
- Is your dev server running?`:t.includes("ERR_NAME_NOT_RESOLVED")?`Error: Could not resolve hostname for ${e}`:t.toLowerCase().includes("timed out")?`Error: Page load timed out after 30s: ${e}`:null}function x(e){process.stdout.write(e.trimEnd()+`
8
- `)}async function $t(e,r){let t=await import("http"),s=await import("https"),o=new URL(`${e}/signup`),d=Buffer.from(JSON.stringify({email:r}),"utf8");return new Promise(m=>{let l=(o.protocol==="https:"?s:t).request({method:"POST",protocol:o.protocol,hostname:o.hostname,port:o.port,path:o.pathname,headers:{"content-type":"application/json","content-length":String(d.length),"user-agent":"llynt-cli-signup/0"}},f=>{let y=[];f.on("data",u=>y.push(u)),f.on("end",()=>{try{if(f.statusCode&&f.statusCode>=200&&f.statusCode<300){let u=JSON.parse(Buffer.concat(y).toString("utf8"));m(typeof u.apiKey=="string"?u.apiKey:null)}else m(null)}catch{m(null)}})});l.on("error",()=>m(null)),l.setTimeout(1e4,()=>{l.destroy(),m(null)}),l.end(d)})}async function Fe(e){let r=be(e),t=It(e);if(!t)return x(`Error: Missing URL.
9
- Usage: llynt check <url>`),I;let s=(typeof r.key=="string"?String(r.key):process.env.LLYNT_API_KEY??"").trim(),o=re(r.signup),d=typeof r.email=="string"?String(r.email).trim():"",m=!s&&!o;if(o&&!s){if(!d)return x("Error: --signup requires --email <your@email.com>"),I;let g=(typeof process.env.LLYNT_API_URL=="string"?process.env.LLYNT_API_URL:"https://api.llynt.dev").trim(),b=se(g);x(` Signing up ${d}...`);let v=await $t(b,d);if(!v)return x(" Error: Signup failed. Try at https://llynt.dev"),I;s=v,x(` API key: ${v}`),x(` Save it: export LLYNT_API_KEY=${v}
10
- `)}try{let{createRequire:g}=await import("module"),b=g(process.cwd()+"/package.json");try{b.resolve("playwright")}catch{await import("playwright")}}catch(g){return _t(g)?(x(`llynt check ${t}
2
+ "use strict";var Zt=Object.create;var Ct=Object.defineProperty;var Qt=Object.getOwnPropertyDescriptor;var te=Object.getOwnPropertyNames;var ee=Object.getPrototypeOf,ne=Object.prototype.hasOwnProperty;var tt=(t,n)=>()=>(t&&(n=t(t=0)),n);var re=(t,n)=>{for(var e in n)Ct(t,e,{get:n[e],enumerable:!0})},oe=(t,n,e,o)=>{if(n&&typeof n=="object"||typeof n=="function")for(let r of te(n))!ne.call(t,r)&&r!==e&&Ct(t,r,{get:()=>n[r],enumerable:!(o=Qt(n,r))||o.enumerable});return t};var P=(t,n,e)=>(e=t!=null?Zt(ee(t)):{},oe(n||!t||!t.__esModule?Ct(e,"default",{value:t,enumerable:!0}):e,t));function kt(t){let n=t.trim();if(!n)throw new Error("LLYNT_API_URL is empty");return n.includes("/v1/check")?n:n.replace(/\/+$/g,"")+"/v1/check"}function fe(t){return new Promise((n,e)=>{ft.gzip(t,(o,r)=>{o?e(o):n(r)})})}function ye(t){return new Promise((n,e)=>{ft.brotliCompress(t,(o,r)=>{o?e(o):n(r)})})}function we(t,n,e,o){return new Promise((r,u)=>{let s=(t.protocol==="https:"?ge:he).request({...n,protocol:t.protocol,hostname:t.hostname,port:t.port,path:t.pathname+t.search},i=>{let g=[];i.on("data",m=>g.push(m)),i.on("end",()=>{r({statusCode:i.statusCode??0,body:Buffer.concat(g)})})});s.on("error",i=>u(i)),s.setTimeout(o,()=>{s.destroy(new Error(`Hosted API request timed out after ${o}ms`))}),s.end(e)})}async function Et(t,n){let e=new URL(t.apiUrl),o=t.timeoutMs??3e4,r=Buffer.from(JSON.stringify(n),"utf8"),u=t.compression??"gzip",l=u==="identity"?r:u==="br"?await ye(r):await fe(r),s={"content-type":"application/json",accept:"application/json","user-agent":"llynt-hosted-client/0","content-length":String(l.length)};u!=="identity"&&(s["content-encoding"]=u),t.apiKey&&(s.authorization=`Bearer ${t.apiKey}`);let{statusCode:i,body:g}=await we(e,{method:"POST",headers:s},l,o),m=g.toString("utf8");if(i<200||i>=300)throw new Error(`Hosted API error (${i}): ${m.slice(0,500)}`);let w=JSON.parse(m);if(!w||w._schemaVersion!=="llynt-hosted-check-result@0"||!Array.isArray(w.issues))throw new Error("Hosted API response schema mismatch");return w}var he,ge,ft,At=tt(()=>{"use strict";he=P(require("http")),ge=P(require("https")),ft=P(require("zlib"))});async function $t(t,n,e){let o=e?.maxDepth??Ce,r=e?.maxNodes??Se,u=typeof e?.includeComputedStyles=="boolean"?e.includeComputedStyles?"all":"none":e?.computedStyleMode??"essential",l=Array.isArray(e?.computedStyleProps)?e.computedStyleProps:void 0,s=e?.internComputedStyles??!0,i=e?.includeUrlAttributes??!1,g=e?.redactUrlAttributes??!0,m=e?.pierceShadowRoots??!1,w=await t.evaluate(({selector:p,maxDepth:A,maxNodes:h,computedStyleMode:L,computedStyleProps:b,essentialProps:x,internComputedStyles:T,includeUrlAttributes:M,redactUrlAttributes:D,pierceShadowRoots:U})=>{function B(d){let f=2166136261;for(let S=0;S<d.length;S++)f^=d.charCodeAt(S),f=Math.imul(f,16777619);return(f>>>0).toString(36)}function ut(d){let f=Object.keys(d).sort(),S=[];for(let c of f)S.push(`${c}=${d[c]}`);return S.join(";")}function G(d){try{let f=new URL(d,document.baseURI);return f.protocol==="http:"||f.protocol==="https:"?`${f.protocol}//${f.host}${f.pathname}`:f.protocol==="data:"?"data:[redacted]":f.protocol==="blob:"?"blob:[redacted]":`${f.protocol}[redacted]`}catch{return"[redacted]"}}function W(d,f){let S=[],c=d;for(;c&&c!==f;){let y=c.parentElement??null;if(!y)break;let I=c.tagName.toLowerCase(),E=Array.from(y.children).filter(Y=>String(Y?.tagName??"").toLowerCase()===I),V=Math.max(0,E.indexOf(c));S.push(`${I}:${V}`),c=y}return S.push(f.tagName.toLowerCase()),S.reverse().join("/")}function $(d,f){let S=[],c=d;for(;c&&c!==f;){let y=c.parentElement??null;if(!y)break;let I=c.tagName.toLowerCase(),E=c.id;if(E){S.push(`#${CSS.escape(E)}`);break}let V=Array.from(y.children);if(V.filter(q=>String(q?.tagName??"").toLowerCase()===I).length===1)S.push(I);else{let q=V.indexOf(c)+1;S.push(`${I}:nth-child(${q})`)}c=y}return S.length?S.reverse().join(" > "):f.tagName.toLowerCase()}function F(d){return!!(d.getAttribute("data-figma-id")||d.getAttribute("data-llynt-node-id")||d.getAttribute("data-component")||d.getAttribute("data-testid")||d.id)}function X(d,f){let S=d.getAttribute("data-figma-id");if(S)return S;let c=d.getAttribute("data-llynt-node-id");if(c)return c;let y=d.getAttribute("data-component");if(y)return y;let I=d.getAttribute("data-testid");if(I)return I;let E=d.id;if(E)return E;let V=W(d,f),Y=B(V);return`llynt-${d.tagName.toLowerCase()}-${Y}`}function ct(d){let f={};for(let S of Array.from(d.attributes)){let c=S.name;if(c==="id"||c==="class"||c==="role"||c==="alt"||c==="type"||c==="width"||c==="height"||c==="target"||c==="rel"||c==="loading"||c==="name"||c==="autocomplete"||c==="disabled"||c==="required"||c==="placeholder"||c==="for"||c==="tabindex"||c==="lang"||c==="style"||c.startsWith("aria-")||c==="data-figma-id"||c==="data-component"||c==="data-testid"||c==="data-primary"||c.startsWith("data-llynt-")){let y=S.value;typeof y=="string"&&(f[c]=y)}if(c==="href"||c==="src"){let y=S.value;typeof y=="string"&&(y===""||y==="#"||y.startsWith("javascript:")?f[c]=y:y.length>0&&(f[c]=M&&!D?y:G(y)))}}return Object.keys(f).length>0?f:void 0}function lt(d){if(L==="none")return;let f=window.getComputedStyle(d),S={},c=Array.isArray(b)&&b.length?b:L==="essential"?x:null;if(c){for(let y of c){let I=f.getPropertyValue(y);typeof I=="string"&&I.trim()&&(S[y]=I.trim())}return Object.keys(S).length>0?S:void 0}for(let y=0;y<f.length;y++){let I=f[y],E=f.getPropertyValue(I);typeof E=="string"&&E.trim()&&(S[I]=E.trim())}return Object.keys(S).length>0?S:void 0}function pt(d){let f=d.getBoundingClientRect();return{left:f.left,top:f.top,width:f.width,height:f.height}}let H=document.querySelector(p||"body")??document.body,j=0,dt=!1,mt=new Set,z={};function Z(d,f,S){if(j+=1,j>h)return dt=!0,null;let c=F(d),y=X(d,H);if(mt.has(y)){let N=B(y+":"+j);y=`${y}-${N}`}if(mt.add(y),!c)try{d.setAttribute("data-llynt-node-id",y)}catch{}let I=lt(d),E;if(T&&I&&Object.keys(I).length){let N=ut(I);E=`cs-${B(N)}`,z[E]||(z[E]=I)}let V=$(d,H),Y=(d.textContent?.trim()??"").length,q,K=d;(K.scrollWidth>K.clientWidth||K.scrollHeight>K.clientHeight)&&(q={scrollWidth:K.scrollWidth,clientWidth:K.clientWidth,scrollHeight:K.scrollHeight,clientHeight:K.clientHeight});let R={nodeId:y,parentId:S,tagName:d.tagName.toLowerCase(),attributes:ct(d),bbox:pt(d),computed:E?void 0:I,computedRef:E,cssSelector:V,textLen:Y>0?Y:void 0,scrollOverflow:q,children:[]};if(d.tagName==="IMG"){let N=d;R.naturalWidth=N.naturalWidth,R.naturalHeight=N.naturalHeight}if(f>=A)return R;for(let N of Array.from(d.children)){let Q=Z(N,f+1,y);if(!Q)break;R.children.push(Q)}if(U&&d.shadowRoot)for(let N of Array.from(d.shadowRoot.children)){let Q=Z(N,f+1,y);if(!Q)break;R.children.push(Q)}return R.children.length===0&&delete R.children,R.parentId||delete R.parentId,R.attributes||delete R.attributes,R.computed||delete R.computed,R.computedRef||delete R.computedRef,R}let C=Z(H,0);if(!C)return{tree:{nodeId:X(H,H),tagName:H.tagName.toLowerCase(),bbox:pt(H)},stats:{nodeCount:Math.min(j,h),maxDepth:A,maxNodes:h,truncated:!0}};let _=document.documentElement.getAttribute("lang")||void 0;return{tree:C,computedStyles:Object.keys(z).length?z:void 0,stats:{nodeCount:Math.min(j,h),maxDepth:A,maxNodes:h,truncated:dt},htmlLang:_}},{selector:n.selector??"body",maxDepth:o,maxNodes:r,computedStyleMode:u,computedStyleProps:l,essentialProps:[...Ae],internComputedStyles:s,includeUrlAttributes:i,redactUrlAttributes:g,pierceShadowRoots:m}),a={_schemaVersion:"dom-blob@0",_extractedAt:new Date().toISOString(),target:{url:n.url,selector:n.selector,viewport:n.viewport},tree:w.tree,computedStyles:w.computedStyles,stats:w.stats,htmlLang:w.htmlLang};if(a.stats){a.stats.coverage={completeness:a.stats.truncated?"truncated":"complete",reason:a.stats.truncated?"maxNodes":void 0},a.stats.confidence={extractionCompleteness:a.stats.truncated?"low":"high"};let p={...a,stats:{...a.stats}};delete p.stats.bytesRaw,delete p.stats.bytesGzip,delete p.stats.computedStyleMode,delete p.stats.urlAttributes;let A=Buffer.from(JSON.stringify(p),"utf8"),h=_t.gzipSync(A);a.stats.bytesRaw=A.length,a.stats.bytesGzip=h.length,a.stats.computedStyleMode=u,a.stats.urlAttributes=i?g?"redacted":"raw":"omitted"}return a}var _t,Ce,Se,Ae,Tt=tt(()=>{"use strict";_t=P(require("zlib")),Ce=10,Se=1e4,Ae=["display","position","overflow","overflow-x","overflow-y","visibility","opacity","width","height","min-width","min-height","max-width","max-height","margin-top","margin-right","margin-bottom","margin-left","padding-top","padding-right","padding-bottom","padding-left","gap","flex-basis","flex-shrink","flex-wrap","align-items","z-index","pointer-events","cursor","font-size","font-family","font-weight","line-height","letter-spacing","text-decoration","text-overflow","white-space","color","background-color","border-width","border-color","border-top-color","border-top-width","border-radius","box-shadow","outline","outline-style","transition-duration","transition-property","animation-name","animation-play-state","aspect-ratio","padding"]});function ve(){let t=yt.platform(),n=yt.homedir();if(t==="darwin")return rt.join(n,"Library","Caches","llynt","playwright");if(t==="win32"){let r=process.env.LOCALAPPDATA||process.env.APPDATA||rt.join(n,"AppData","Local");return rt.join(r,"llynt","playwright")}let e=process.env.XDG_CACHE_HOME,o=e&&e.trim().length>0?e:rt.join(n,".cache");return rt.join(o,"llynt","playwright")}function Mt(){return{source:"llynt",browsersPath:ve()}}function Nt(t){process.env.PLAYWRIGHT_BROWSERS_PATH=t.browsersPath}var yt,rt,Ot=tt(()=>{"use strict";yt=P(require("os")),rt=P(require("path"))});async function Ie(){return wt||(wt=xe().then(t=>t.chromium).catch(t=>{throw wt=null,t})),wt}async function xe(){try{let{createRequire:t}=await import("module");return t(process.cwd()+"/package.json")("playwright")}catch{return await import("playwright")}}function Pe(){return["* {"," animation: none !important;"," transition: none !important;"," scroll-behavior: auto !important;","}","html {"," -webkit-text-size-adjust: 100% !important;","}"].join(`
3
+ `)}async function Bt(){if(ot&&ot.isConnected())return ot;if(st)try{let t=await st;if(t.isConnected())return t}catch{}return st=ke().then(t=>(ot=t,t.on("disconnected",()=>{ot===t&&(ot=null,st=null)}),t)).catch(t=>{throw st=null,t}),st}async function ke(){let t=Mt();Nt(t);let n=await Ie(),e=["--no-sandbox","--disable-gpu","--disable-dev-shm-usage","--allow-file-access-from-files","--allow-file-access","--disable-lcd-text","--font-render-hinting=none"];try{return await n.launch({headless:!0,args:e})}catch{return await new Promise(o=>setTimeout(o,250)),await n.launch({headless:!0,args:e})}}async function Ee(t){try{await t.addStyleTag({content:Pe()})}catch(n){console.warn("Failed to apply deterministic CSS:",n)}}async function Ht(t,n,e){let r=await(await t.newContext({viewport:n||{width:1280,height:720},...e||{}})).newPage();return await Ee(r),r}var ot,st,wt,Dt=tt(()=>{"use strict";Ot();ot=null,st=null,wt=null});function Le(t,n=1e4){let e=[];return e.push("(function(){"),e.push(`const selector = ${JSON.stringify(t)};`),e.push(`const maxNodes = ${JSON.stringify(n)};`),e.push("function fnv1aBase36(input){"),e.push(" let hash = 0x811c9dc5;"),e.push(" for (let i=0;i<input.length;i++){"),e.push(" hash ^= input.charCodeAt(i);"),e.push(" hash = Math.imul(hash, 0x01000193);"),e.push(" }"),e.push(" return (hash >>> 0).toString(36);"),e.push("}"),e.push("function domPathSignature(el, root){"),e.push(" const parts=[];"),e.push(" let cur=el;"),e.push(" while(cur && cur!==root){"),e.push(" const parent=cur.parentElement;"),e.push(" if(!parent) break;"),e.push(" const tag=cur.tagName.toLowerCase();"),e.push(" const siblings=Array.from(parent.children).filter(c=>c.tagName.toLowerCase()===tag);"),e.push(" const idx=Math.max(0,siblings.indexOf(cur));"),e.push(" parts.push(`${tag}:${idx}`);"),e.push(" cur=parent;"),e.push(" }"),e.push(" parts.push(root.tagName.toLowerCase());"),e.push(' return parts.reverse().join("/");'),e.push("}"),e.push("function hasStableIdentity(el){"),e.push(" return Boolean("),e.push(' el.getAttribute("data-figma-id") ||'),e.push(' el.getAttribute("data-llynt-node-id") ||'),e.push(' el.getAttribute("data-component") ||'),e.push(' el.getAttribute("data-testid") ||'),e.push(" el.id"),e.push(" );"),e.push("}"),e.push("function assignIds(){"),e.push(" const root = document.querySelector(selector) || document.body;"),e.push(" if(!root) return 0;"),e.push(" const used=new Set();"),e.push(" let assigned=0;"),e.push(" const walker=document.createTreeWalker(root, NodeFilter.SHOW_ELEMENT);"),e.push(" let node=walker.currentNode;"),e.push(" let seen=0;"),e.push(" while(node){"),e.push(" const el=node;"),e.push(" if(el && el instanceof Element){"),e.push(" seen++;"),e.push(" if(seen>maxNodes) break;"),e.push(" if(!hasStableIdentity(el)){"),e.push(" const sig=domPathSignature(el, root);"),e.push(" const hash=fnv1aBase36(sig);"),e.push(" const tag=el.tagName.toLowerCase();"),e.push(" let id=`llynt-${tag}-${hash}`;"),e.push(' if(used.has(id)){ id = `${id}-${fnv1aBase36(id+":"+seen)}`; }'),e.push(" used.add(id);"),e.push(' el.setAttribute("data-llynt-node-id", id);'),e.push(" assigned++;"),e.push(" } else {"),e.push(' const existing = el.getAttribute("data-llynt-node-id") || el.getAttribute("data-figma-id") || el.getAttribute("data-component") || el.getAttribute("data-testid") || el.id;'),e.push(" if(existing) used.add(existing);"),e.push(" }"),e.push(" }"),e.push(" node = walker.nextNode();"),e.push(" }"),e.push(" return assigned;"),e.push("}"),e.push("try { assignIds(); } catch {}"),e.push("})();"),e.join(`
4
+ `)}async function Ut(t,n,e){let o=Le(n||"body",e?.maxNodes??1e4);await t.addInitScript(o),await t.evaluate(o)}var Ft=tt(()=>{"use strict"});var Kt={};re(Kt,{runHostedThinClientChecks:()=>Re});async function Re(t){let n=t.repoRoot??process.cwd(),e=t.artifactDir??"tools/artifacts/llynt",o=t.timeoutMs??3e4,r=t.computedStyleMode??"essential",u=t.internComputedStyles??!0,l=t.maxNodes,s=!!t.allowTruncated,i=!!(t.traceOnFailure??process.env.LLYNT_TRACE_ON_FAILURE==="1"),g=null;try{g=await Bt();let m=[];for(let w of t.targets){let a=Date.now(),p=w.selector??"body",A=w.viewports?.length?w.viewports:[{width:360,height:720},{width:768,height:720}],h=await Ht(g),L=h.context(),b=!1,x;try{if(i)try{await L.tracing.start({screenshots:!0,snapshots:!0,sources:!1}),b=!0}catch{b=!1}h.setDefaultTimeout(o);let T=Date.now();await h.goto(w.url,{waitUntil:"domcontentloaded"});let M=[];for(let $ of A){await h.setViewportSize($),await h.waitForTimeout(50),await Ut(h,p);let F=await $t(h,{url:w.url,selector:p,viewport:$},{computedStyleMode:r,internComputedStyles:u,...typeof l=="number"?{maxNodes:l}:{}});if(!s&&F.stats?.truncated)throw new Error(`Extraction truncated for ${w.url} at ${$.width}x${$.height}. Narrow the selector scope or increase limits (free-tier should be component-scoped).`);M.push(F)}let D=Date.now()-T,U=Date.now(),B=await Et(t.client,{_schemaVersion:"llynt-hosted-check@0",blobs:M,rules:{overlap:!0,viewport:!0}}),ut=Date.now()-U,G=Date.now()-a,W=B.issues.some($=>$.severity==="error");if(b&&W){let $=w.url.toLowerCase().replace(/https?:\/\//g,"").replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"").slice(0,64)||"target";x=it.resolve(n,it.join(e,`trace-hosted-${$}.zip`))}m.push({url:w.url,selector:p,viewports:A,blobs:M,response:B,timings:{captureMs:D,analyzeMs:ut,totalMs:G},tracePath:x})}finally{if(b)try{x?(Vt.mkdirSync(it.dirname(x),{recursive:!0}),await L.tracing.stop({path:x})):await L.tracing.stop()}catch{}await L.close().catch(()=>{})}}return m}finally{await g?.close().catch(()=>{})}}var Vt,it,Wt=tt(()=>{"use strict";Vt=P(require("fs")),it=P(require("path"));Tt();Dt();At();Ft()});function et(t){let n={};for(let e=0;e<t.length;e++){let o=t[e];if(!o.startsWith("--"))continue;let r=o.slice(2),u=t[e+1];!u||u.startsWith("--")?n[r]=!0:(n[r]=u,e++)}return n}function ht(t){if(t==null)return!1;if(typeof t=="boolean")return t;let n=String(t).trim().toLowerCase();return!(n===""||n==="0"||n==="false"||n==="no"||n==="off")}var se=new Set(["AccordionDisclosure","Tabs","Search","Form","ModalDialog"]);function gt(t){return t.replace(/\s+/g," ").trim()}function ie(t){return t.replace(/\bllynt-[a-z]+-[a-z0-9]+\b/g,n=>{let e=n.split("-");return e.length>=2?`<${e[1]}>`:n})}function ae(t){let n=gt(String(t.message??""));return n||`${gt(String(t.ruleId??"unknown.rule"))} violation`}function ue(t){return t.length?t.map(n=>`${n.width}px`).join(", "):""}function St(t){return!Number.isFinite(t)||t<=0?"0ms":t<1e3?`${Math.round(t)}ms`:`${(t/1e3).toFixed(2)}s`}function ce(t){return t.replace(/Low contrast \((\d+(?:\.\d+)?)\):/g,"Low contrast ratio $1:1:")}function le(t){return ce(ie(gt(String(t??""))))}function It(t){let n=t.issues.filter(i=>i.severity==="error"),e=t.issues.filter(i=>i.severity==="warning"),o=t.issues.filter(i=>i.severity==="info"),r=t.issues.length,u=t.viewports.length,l=ue(t.viewports),s=[];if(s.push(`llynt check ${t.url}`),s.push(""),t.rulesChecked!=null&&t.rulesAvailable!=null&&s.push(` ${t.rulesChecked} of ${t.rulesAvailable} rules checked${t.isAnonymous?" (anonymous)":""}`),s.push(` Capturing... ${u} viewport${u!==1?"s":""}${l?` (${l})`:""}`),s.push(` Analyzing... ${Math.max(0,Math.floor(t.nodeCount))} nodes, ${Math.max(0,Math.floor(t.costUnits))} cost units`),t.timings&&s.push(` Timing... capture ${St(t.timings.captureMs)} \xB7 hosted ${St(t.timings.analyzeMs)} \xB7 total ${St(t.timings.totalMs)}`),t.truncated&&s.push(" Note: Capture was truncated due to DOM limits."),s.push(""),r===0)s.push(" 0 issues found");else{let i=t.incidentCount!=null?t.incidentCount:r,m=` ${i} ${i===1?"incident":"incidents"} (${r} evidence points) across ${u} viewport${u!==1?"s":""}`;s.push(m),s.push(` ${n.length} errors \xB7 ${e.length} warnings \xB7 ${o.length} info`)}if(s.push(""),t.compositional&&t.compositional.componentCount>0){let i=t.compositional,g=i.componentSummaries.filter(w=>w.confidence==="high"&&w.missingParts.length>0&&se.has(w.type)).length;(i.routeStatus==="FAIL"||i.componentsWithIssues>0||g>0||i.pageLayoutSummary.severityCounts.error>0)&&(s.push(` ${i.componentCount} components analyzed, ${i.componentsWithIssues} with issues`),g>0&&s.push(` ${g} high-confidence structural gaps`),s.push(` Page Geometry: ${i.pageLayoutSummary.issueCount} findings`),s.push(""))}if(t.reportUrl&&(s.push(" Open full report:"),s.push(` ${t.reportUrl}`),t.reportExpiresAt&&s.push(` Expires ${t.reportExpiresAt.slice(0,10)}`),s.push("")),r>0&&t.showDetails){if(t.incidents?.length){let i=t.incidents.filter(a=>a.severity!=="info").slice(0,5);if(s.push(" Major findings:"),i.length===0)s.push(" No error/warning findings in this run.");else for(let a of i){let p=le(a.message);s.push(` ${a.count}x [${a.ruleId}] ${p}`)}s.push("");let g=new Map,m={error:0,warning:1,info:2};for(let a of t.incidents){if(a.severity==="info")continue;let p=(a.category||"Other").trim()||"Other",A=g.get(p);A?(A.count+=a.count,A.ruleIds.add(a.ruleId),m[a.severity]<m[A.severity]&&(A.severity=a.severity)):g.set(p,{severity:a.severity,count:a.count,ruleIds:new Set([a.ruleId])})}let w=Array.from(g.entries()).map(([a,p])=>({category:a,...p})).sort((a,p)=>{let A=m[a.severity]-m[p.severity];return A!==0?A:p.count-a.count}).slice(0,5);if(w.length>0){s.push(" Finding families:");for(let a of w){let p=Array.from(a.ruleIds).slice(0,2).join(", ");s.push(` ${a.count} evidence \xB7 ${a.category} (${p})`)}s.push("")}}if(t.actions?.length){s.push(" Top actions:");for(let i of t.actions.slice(0,5))s.push(` [${i.severity}/${i.confidence}] ${gt(i.title)}`);s.push("")}}return r>0&&!t.showDetails&&(t.incidents?.length||t.actions?.length)&&(s.push(" Details are in the report link above."),s.push(" For machine-readable findings, re-run with --json."),s.push("")),r>0&&(t.gatedIncidentCount&&t.gatedIncidentCount>0?(s.push(` ${t.gatedIncidentCount} more incidents found with full analysis \u2014 sign up at llynt.dev`),s.push("")):t.gatedIssueCount&&t.gatedIssueCount>0&&(s.push(` ${t.gatedIssueCount} more issues found with full analysis \u2014 sign up at llynt.dev`),s.push(""))),t.isAnonymous?(s.push(" Sign up free at https://llynt.dev for 26 rules, 2 viewports, 100 checks/day."),s.push(" Then: export LLYNT_API_KEY=<your-key>"),s.push("")):r>0&&(s.push(" Want full SARIF evidence + agent-ready fixes?"),s.push(" Set up the PR gate \u2014 your agent can do it:"),s.push(" npx llynt init --github")),s.join(`
5
+ `).trimEnd()}function xt(t){let n={error:0,warning:1,info:2},e=t.issues.map(l=>({severity:l.severity,ruleId:l.ruleId,message:ae(l)})).sort((l,s)=>(n[l.severity]??3)-(n[s.severity]??3)),o=e.filter(l=>l.severity==="error").length,r=e.filter(l=>l.severity==="warning").length,u=e.length-o-r;return{url:t.url,viewports:t.viewports,nodeCount:Math.max(0,Math.floor(t.nodeCount)),costUnits:Math.max(0,Math.floor(t.costUnits)),issueCount:e.length,...t.incidentCount!=null?{incidentCount:t.incidentCount}:{},...t.gatedIncidentCount!=null?{gatedIncidentCount:Math.max(0,Math.floor(t.gatedIncidentCount))}:{},errors:o,warnings:r,infos:u,truncated:!!t.truncated,issues:e,gatedIssueCount:Math.max(0,Math.floor(t.gatedIssueCount??0)),...t.reportUrl?{reportUrl:t.reportUrl}:{},...t.reportExpiresAt?{reportExpiresAt:t.reportExpiresAt}:{},...t.rulesChecked!=null?{rulesChecked:t.rulesChecked,rulesAvailable:t.rulesAvailable}:{},...t.incidents?.length?{incidents:t.incidents}:{},...t.actions?.length?{actions:t.actions}:{},...t.compositional?{compositional:t.compositional}:{},...t.timings?{timings:t.timings}:{}}}var pe=P(require("http")),de=P(require("https"));function nt(t){let n=t.trim();if(!n)throw new Error("LLYNT_API_URL is empty");let e=new URL(n),o=e.pathname.replace(/\/+$/g,"");o.endsWith("/v1/check")&&(o=o.slice(0,o.length-9)),e.pathname=o||"/",e.search="",e.hash="";let r=e.pathname==="/"?"":e.pathname.replace(/\/+$/g,"");return`${e.origin}${r}`}function me(t,n,e,o){return new Promise((r,u)=>{let s=(t.protocol==="https:"?de:pe).request({...n,protocol:t.protocol,hostname:t.hostname,port:t.port,path:t.pathname+t.search},i=>{let g=[];i.on("data",m=>g.push(m)),i.on("end",()=>r({statusCode:i.statusCode??0,body:Buffer.concat(g)}))});s.on("error",u),s.setTimeout(o,()=>s.destroy(new Error(`Report upload timed out after ${o}ms`))),s.end(e)})}async function Pt(t,n,e){try{let o=nt(t),r=new URL(`${o}/reports`),u=1e4,l=Buffer.from(JSON.stringify(e),"utf8"),s=await me(r,{method:"POST",headers:{"content-type":"application/json",accept:"application/json","content-length":String(l.length),"user-agent":"llynt-check-report-upload/0",...n?{authorization:`Bearer ${n}`}:{}}},l,u);if(s.statusCode<200||s.statusCode>=300)return null;let i=JSON.parse(s.body.toString("utf8"));return!i||typeof i.reportUrl!="string"||typeof i.expiresAt!="string"?null:i}catch{return null}}At();var Lt=/^llynt_(?:key|free|paid|pro|enterprise|internal)_[A-Za-z0-9_-]{16,}$/;var be=["LLYNT_API_KEY","LLYNT_PRO_API_KEY","LLYNT_INTERNAL_API_KEY"];function Rt(t=process.env){for(let n of be){let e=t[n];if(typeof e=="string"&&e.trim())return e.trim()}}var _e=0,$e=2,k=3,jt=[{width:360,height:720},{width:768,height:720}],Te=[{width:360,height:720}];function Me(){try{let t=require("node:fs"),e=require("node:path").resolve(__dirname,"package.json");if(!t.existsSync(e))return"0.0.0";let o=JSON.parse(t.readFileSync(e,"utf8"));return typeof o.version!="string"?"0.0.0":o.version.trim()||"0.0.0"}catch{return"0.0.0"}}function Ne(t){for(let n=0;n<t.length;n++){let e=t[n];if(!e.startsWith("--"))return e;let o=t[n+1];o&&!o.startsWith("--")&&(n+=1)}}function Oe(t){if(typeof t!="string"||!t.trim())return jt;let n=t.split(",").map(e=>e.trim()).filter(Boolean).map(e=>{let o=e.match(/^(\d{2,5})x(\d{2,5})$/i);if(!o)throw new Error(`Invalid viewport "${e}". Expected format WIDTHxHEIGHT.`);return{width:Number(o[1]),height:Number(o[2])}});return n.length?n:jt}function Be(t,n){return t}function He(t){let n=t instanceof Error?t.message:String(t);return n.includes("Cannot find module 'playwright'")||n.includes("Cannot find package 'playwright'")||n.includes("ERR_MODULE_NOT_FOUND")}function De(t,n){let e=n instanceof Error?n.message:String(n);return e.includes("ERR_CONNECTION_REFUSED")?`Error: Could not reach ${t}
6
+ Is your dev server running?`:e.includes("ERR_NAME_NOT_RESOLVED")?`Error: Could not resolve hostname for ${t}`:e.toLowerCase().includes("timed out")?`Error: Page load timed out after 30s: ${t}`:e.includes("eval is disabled")?`Error: This page blocks runtime script evaluation via CSP (eval disabled)
7
+ Try a different route, narrower selector, or run on a controlled preview environment.`:null}function v(t){process.stdout.write(t.trimEnd()+`
8
+ `)}function Ue(t){let n=t.match(/Hosted API error \((\d{3})\):\s*([\s\S]+)$/);if(!n)return null;let e=Number(n[1]),o=n[2]?.trim();if(!Number.isFinite(e))return null;if(!o)return{status:e};try{let r=JSON.parse(o);return{status:e,body:r}}catch{return{status:e}}}function Fe(t){let n=new Map;for(let o of t)if(Array.isArray(o))for(let r of o){if(!r||typeof r.ruleId!="string"||typeof r.message!="string")continue;let u=r.severity==="error"||r.severity==="warning"||r.severity==="info"?r.severity:"warning",l=`${r.ruleId}::${u}::${r.message}`,s=n.get(l);s?s.count+=Math.max(1,Math.floor(r.count||0)):n.set(l,{ruleId:r.ruleId,severity:u,count:Math.max(1,Math.floor(r.count||0)),message:r.message,category:typeof r.category=="string"&&r.category.trim()?r.category.trim():void 0})}if(n.size===0)return;let e={error:0,warning:1,info:2};return Array.from(n.values()).sort((o,r)=>{let u=e[o.severity]-e[r.severity];return u!==0?u:r.count-o.count})}function Ve(t){let n=new Map;for(let o of t)if(Array.isArray(o))for(let r of o){if(!r||typeof r.actionId!="string"||typeof r.title!="string")continue;let u=r.severity==="error"||r.severity==="warning"||r.severity==="info"?r.severity:"warning",l=r.confidence==="high"||r.confidence==="medium"||r.confidence==="low"?r.confidence:"low",s=r.actionId.trim()||`${r.title}::${u}`,i=n.get(s);if(i){let g=new Set([...i.incidentRuleIds,...r.incidentRuleIds.filter(Boolean)]);i.incidentRuleIds=Array.from(g).sort()}else n.set(s,{actionId:s,title:r.title,why:r.why,severity:u,confidence:l,incidentRuleIds:[...new Set(r.incidentRuleIds.filter(Boolean))].sort(),expectedImpact:r.expectedImpact})}if(n.size===0)return;let e={error:0,warning:1,info:2};return Array.from(n.values()).sort((o,r)=>{let u=e[o.severity]-e[r.severity];return u!==0?u:r.incidentRuleIds.length-o.incidentRuleIds.length})}function Ke(t){let n=t.filter(a=>!!a);if(n.length===0)return;let e=new Map,o=0,r=0,u={error:0,warning:0,info:0},l=[],s="PASS",i=new Set;for(let a of n){o=Math.max(o,Math.max(0,Math.floor(a.componentCount||0))),i.add(a.routePolicy),a.routeStatus==="FAIL"&&(s="FAIL"),r+=Math.max(0,Math.floor(a.pageLayoutSummary.issueCount||0)),u.error+=Math.max(0,Math.floor(a.pageLayoutSummary.severityCounts.error||0)),u.warning+=Math.max(0,Math.floor(a.pageLayoutSummary.severityCounts.warning||0)),u.info+=Math.max(0,Math.floor(a.pageLayoutSummary.severityCounts.info||0)),l.push(...a.pageLayoutSummary.topFindings);for(let p of a.componentSummaries){let A=`${p.type}:${p.rootId}`,h=e.get(A);h?(h.issueCount+=Math.max(0,Math.floor(p.issueCount||0)),h.severityCounts.error+=Math.max(0,Math.floor(p.severityCounts.error||0)),h.severityCounts.warning+=Math.max(0,Math.floor(p.severityCounts.warning||0)),h.severityCounts.info+=Math.max(0,Math.floor(p.severityCounts.info||0)),h.topFindings=[...new Set([...h.topFindings,...p.topFindings])].slice(0,3),h.presentParts=[...new Set([...h.presentParts,...p.presentParts])],h.missingParts=[...new Set([...h.missingParts,...p.missingParts])]):e.set(A,{...p,severityCounts:{...p.severityCounts},topFindings:[...p.topFindings],requiredParts:[...p.requiredParts],presentParts:[...p.presentParts],missingParts:[...p.missingParts]})}}let g=Array.from(e.values()).sort((a,p)=>a.severityCounts.error!==p.severityCounts.error?p.severityCounts.error-a.severityCounts.error:a.issueCount!==p.issueCount?p.issueCount-a.issueCount:a.type.localeCompare(p.type)),m=g.filter(a=>a.issueCount>0).length,w=g.filter(a=>a.missingParts.length>0).length;return{componentCount:Math.max(o,g.length),componentsWithIssues:m,componentsWithMissingParts:w,componentSummaries:g,pageLayoutSummary:{severityCounts:u,issueCount:r,topFindings:[...new Set(l.filter(Boolean))].slice(0,3)},routeStatus:s,routePolicy:Array.from(i).join(" | ")}}async function We(t,n){let e=await import("http"),o=await import("https"),r=new URL(`${t}/signup`),u=Buffer.from(JSON.stringify({email:n}),"utf8");return new Promise(l=>{let i=(r.protocol==="https:"?o:e).request({method:"POST",protocol:r.protocol,hostname:r.hostname,port:r.port,path:r.pathname,headers:{"content-type":"application/json","content-length":String(u.length),"user-agent":"llynt-cli-signup/0"}},g=>{let m=[];g.on("data",w=>m.push(w)),g.on("end",()=>{try{if(g.statusCode&&g.statusCode>=200&&g.statusCode<300){let w=JSON.parse(Buffer.concat(m).toString("utf8")),a=typeof w.apiKey=="string"?w.apiKey.trim():"";l(Lt.test(a)?a:null)}else l(null)}catch{l(null)}})});i.on("error",()=>l(null)),i.setTimeout(1e4,()=>{i.destroy(),l(null)}),i.end(u)})}async function zt(t){let n=et(t),e=Ne(t);if(!e)return v(`Error: Missing URL.
9
+ Usage: llynt check <url>`),k;let o=(typeof n.key=="string"?String(n.key):Rt()??"").trim(),r=ht(n.signup),u=typeof n.email=="string"?String(n.email).trim():"",l=!o&&!r;if(r&&!o){if(!u)return v("Error: --signup requires --email <your@email.com>"),k;let h=(typeof process.env.LLYNT_API_URL=="string"?process.env.LLYNT_API_URL:"https://api.llynt.dev").trim(),L=nt(h);v(` Signing up ${u}...`);let b=await We(L,u);if(!b)return v(" Error: Signup failed. Try at https://llynt.dev"),k;o=b,v(` API key: ${b}`),v(` Save it: export LLYNT_API_KEY=${b}
10
+ `)}try{let{createRequire:h}=await import("module"),L=h(process.cwd()+"/package.json");try{L.resolve("playwright")}catch{await import("playwright")}}catch(h){return He(h)?(v(`llynt check ${e}
11
11
 
12
12
  Error: Playwright not found.
13
- Install it: npm install -D playwright && npx playwright install chromium`),I):(x(`llynt check ${t}
13
+ Install it: npm install -D playwright && npx playwright install chromium`),k):(v(`llynt check ${e}
14
14
 
15
- Error: Failed to load Playwright runtime.`),I)}let i=(typeof process.env.LLYNT_API_URL=="string"?process.env.LLYNT_API_URL:"https://api.llynt.dev").trim(),l=Ae(i),f=se(i),y=(typeof r.selector=="string"?String(r.selector):"body").trim()||"body",u=typeof r.profile=="string"?String(r.profile):void 0,P=re(r.json),L=re(r.verbose),j=typeof r.email=="string"?String(r.email).trim():"",N;try{m?N=Rt:N=Lt(r.viewports)}catch(g){return x(`llynt check ${t}
15
+ Error: Failed to load Playwright runtime.`),k)}let s=(typeof process.env.LLYNT_API_URL=="string"?process.env.LLYNT_API_URL:"https://api.llynt.dev").trim(),i=kt(s),g=nt(s),m=(typeof n.selector=="string"?String(n.selector):"body").trim()||"body",w=typeof n.profile=="string"?String(n.profile):void 0,a=ht(n.json),p=ht(n.verbose),A;try{l?A=Te:A=Oe(n.viewports)}catch(h){return v(`llynt check ${e}
16
16
 
17
- Error: ${g instanceof Error?g.message:String(g)}`),I}try{let{runHostedThinClientChecks:g}=await Promise.resolve().then(()=>(De(),Ve)),b=await g({targets:[{url:t,selector:y,viewports:N}],timeoutMs:3e4,traceOnFailure:!1,computedStyleMode:"all",internComputedStyles:!0,maxNodes:1e4,allowTruncated:!0,client:{apiUrl:l,apiKey:s||void 0,timeoutMs:3e4,compression:"gzip"}}),v=b.flatMap(k=>k.response.issues),_=Nt(v,u),H=b.reduce((k,R)=>k+Math.max(0,Math.floor(R.response.meta.nodeCount??0)),0),C=b.reduce((k,R)=>k+Math.max(0,Math.floor(R.response.meta.costUnits??0)),0),M=b.some(k=>!!k.response.meta.truncated),le=b.reduce((k,R)=>k+(R.response.meta.gatedIssueCount??0),0),X=b.flatMap(k=>k.viewports),K=b[0]?.response.meta.rulesChecked,Q=b[0]?.response.meta.rulesAvailable,Z={url:t,viewports:X,issueCount:_.length,issues:_.map(k=>({severity:k.severity,ruleId:k.ruleId,message:k.message.replace(/\s+/g," ").trim()})),meta:{nodeCount:H,costUnits:C,truncated:M,capturedAt:new Date().toISOString(),clientVersion:String(ye.version??"0.0.0"),...K!=null?{rulesChecked:K,rulesAvailable:Q}:{}}},F=await Se(f,s||"",Z),ee={issues:_,url:t,viewports:X,nodeCount:H,costUnits:C,truncated:M,reportUrl:F?.reportUrl,reportExpiresAt:F?.expiresAt,gatedIssueCount:le,isAnonymous:m,rulesChecked:K,rulesAvailable:Q};if(P){let k=xe(ee);x(JSON.stringify(k,null,2))}else L&&x(`llynt check ${t}
17
+ Error: ${h instanceof Error?h.message:String(h)}`),k}try{let h=Date.now(),{runHostedThinClientChecks:L}=await Promise.resolve().then(()=>(Wt(),Kt)),b=await L({targets:[{url:e,selector:m,viewports:A}],timeoutMs:3e4,traceOnFailure:!1,computedStyleMode:"essential",internComputedStyles:!0,maxNodes:1e4,allowTruncated:!0,client:{apiUrl:i,apiKey:o||void 0,timeoutMs:3e4,compression:"gzip"}}),x=b.flatMap(C=>C.response.issues),T=Be(x,w),M=b.reduce((C,_)=>C+Math.max(0,Math.floor(_.response.meta.nodeCount??0)),0),D=b.reduce((C,_)=>C+Math.max(0,Math.floor(_.response.meta.costUnits??0)),0),U=b.some(C=>!!C.response.meta.truncated),B=b.reduce((C,_)=>C+(_.response.meta.gatedIssueCount??0),0),ut=b.reduce((C,_)=>C+(_.response.meta.gatedIncidentCount??0),0)||void 0,G=b.flatMap(C=>C.viewports),W=b[0]?.response.meta.rulesChecked,$=b[0]?.response.meta.rulesAvailable,F=b.reduce((C,_)=>C+(_.response.meta.incidentCount??0),0)||void 0,X=Fe(b.map(C=>C.response.meta.incidents)),ct=Ve(b.map(C=>C.response.meta.actions)),lt=Ke(b.map(C=>C.response.meta.compositional)),pt=b[0]?.response.meta.tier,H=b.reduce((C,_)=>C+Math.max(0,Math.floor(_.timings?.captureMs??0)),0),j=b.reduce((C,_)=>C+Math.max(0,Math.floor(_.timings?.analyzeMs??0)),0),dt=Date.now()-h,mt={url:e,viewports:G,issueCount:T.length,issues:T.map(C=>({severity:C.severity,ruleId:C.ruleId,message:C.message.replace(/\s+/g," ").trim()})),meta:{nodeCount:M,costUnits:D,truncated:U,capturedAt:new Date().toISOString(),clientVersion:Me(),...pt?{tier:pt}:{},...F!=null?{incidentCount:F}:{},...X?{incidents:X}:{},...ct?{actions:ct}:{},...lt?{compositional:lt}:{},...W!=null?{rulesChecked:W,rulesAvailable:$}:{}}},z=await Pt(g,o||"",mt),Z={issues:T,url:e,viewports:G,nodeCount:M,costUnits:D,truncated:U,reportUrl:z?.reportUrl,reportExpiresAt:z?.expiresAt,gatedIssueCount:B,gatedIncidentCount:ut,isAnonymous:l,rulesChecked:W,rulesAvailable:$,incidentCount:F,incidents:X,actions:ct,compositional:lt,showDetails:p,timings:{captureMs:H,analyzeMs:j,totalMs:dt}};if(a){let C=xt(Z);v(JSON.stringify(C,null,2))}else p&&v(`llynt check ${e}
18
18
 
19
- API: ${l}`),x(ve(ee));return j&&!o&&F?.reportUrl&&je({to:j,url:t,reportUrl:F.reportUrl,issueCount:_.length}),_.length>0?Pt:Et}catch(g){let b=Ot(t,g);if(b)return x(`llynt check ${t}
19
+ API: ${i}`),v(It(Z));return T.length>0?$e:_e}catch(h){let L=De(e,h);if(L)return v(`llynt check ${e}
20
20
 
21
- ${b.replace(/\n/g,`
22
- `)}`),I;let v=g instanceof Error?g.message:String(g);return v.includes("Hosted API request timed out")?(x(`llynt check ${t}
21
+ ${L.replace(/\n/g,`
22
+ `)}`),k;let b=h instanceof Error?h.message:String(h),x=Ue(b);if(x){let T=typeof x.body?.hint=="string"?x.body.hint:"",M=typeof x.body?.error=="string"?x.body.error:"";if(x.status===401)return v(`llynt check ${e}
23
23
 
24
- Error: Hosted API timeout (${l})`),I):v.includes("Hosted API error (401)")?(x(`llynt check ${t}
24
+ Error: Invalid API key. Sign up at https://llynt.dev`),k;if(x.status===403)return v(`llynt check ${e}
25
25
 
26
- Error: Invalid API key. Sign up at https://llynt.dev`),I):v.includes("Hosted API error (403)")?(x(`llynt check ${t}
26
+ Error: API key revoked. Sign up at https://llynt.dev for a new key.`),k;if(x.status===413)return v(`llynt check ${e}
27
27
 
28
- Error: API key revoked. Sign up at https://llynt.dev for a new key.`),I):v.includes("Hosted API error (429)")?(x(`llynt check ${t}
28
+ Error: Page payload exceeds current check limits.
29
+ ${T||"Tighten selector, reduce viewports, or run with a key on a smaller scope."}`),k;if(x.status===429)return v(`llynt check ${e}
29
30
 
30
- Error: Rate limit exceeded. ${m?"Sign up at https://llynt.dev for 70 checks/week.":"Try again later or upgrade at https://llynt.dev"}`),I):v.includes("Hosted API error")?(x(`llynt check ${t}
31
+ Error: ${M||"Rate limit exceeded"}.
32
+ ${T||(l?"Sign up at https://llynt.dev for higher limits.":"Try again later or upgrade at https://llynt.dev")}`),k;let D=T||`Hosted API returned ${x.status}`;return v(`llynt check ${e}
31
33
 
32
- Error: Hosted API failure at ${l}`),I):(x(`llynt check ${t}
34
+ Error: Hosted API failure.
35
+ ${D}`),k}return b.includes("Hosted API request timed out")?(v(`llynt check ${e}
33
36
 
34
- Error: ${v.split(`
35
- `)[0]}`),I)}}var Ut=`llynt - UI integrity checks for AI coding agents
37
+ Error: Hosted API timeout (${i})`),k):b.includes("Hosted API error (401)")?(v(`llynt check ${e}
38
+
39
+ Error: Invalid API key. Sign up at https://llynt.dev`),k):b.includes("Hosted API error (403)")?(v(`llynt check ${e}
40
+
41
+ Error: API key revoked. Sign up at https://llynt.dev for a new key.`),k):b.includes("Hosted API error (429)")?(v(`llynt check ${e}
42
+
43
+ Error: Rate limit exceeded. ${l?"Sign up at https://llynt.dev for 100 checks/day.":"Try again later or upgrade at https://llynt.dev"}`),k):b.includes("Hosted API error")?(v(`llynt check ${e}
44
+
45
+ Error: Hosted API failure at ${i}`),k):(v(`llynt check ${e}
46
+
47
+ Error: ${b.split(`
48
+ `)[0]}`),k)}}var at=P(require("node:fs")),O=P(require("node:path"));var Yt=`name: llynt-pr-gate
49
+
50
+ on:
51
+ pull_request:
52
+ paths:
53
+ - 'llynt/**'
54
+ - '.github/workflows/llynt-pr-gate.yml'
55
+
56
+ jobs:
57
+ check:
58
+ runs-on: ubuntu-latest
59
+ steps:
60
+ - name: Checkout
61
+ uses: actions/checkout@v4
62
+ - name: Setup Node
63
+ uses: actions/setup-node@v4
64
+ with:
65
+ node-version: '22'
66
+ - name: Install Playwright Chromium
67
+ run: npx playwright install --with-deps chromium
68
+ - name: llynt check
69
+ env:
70
+ LLYNT_API_KEY: \${{ secrets.LLYNT_API_KEY }}
71
+ run: npx llynt check \${{ vars.LLYNT_BASE_URL || 'http://localhost:3000' }}
72
+ `,qt=`name: llynt-repo-sweep
73
+
74
+ on:
75
+ workflow_dispatch:
76
+ schedule:
77
+ - cron: '0 12 * * 1'
78
+
79
+ jobs:
80
+ sweep:
81
+ runs-on: ubuntu-latest
82
+ steps:
83
+ - name: Checkout
84
+ uses: actions/checkout@v4
85
+ - name: Setup Node
86
+ uses: actions/setup-node@v4
87
+ with:
88
+ node-version: '22'
89
+ - name: Install Playwright Chromium
90
+ run: npx playwright install --with-deps chromium
91
+ - name: llynt check
92
+ env:
93
+ LLYNT_API_KEY: \${{ secrets.LLYNT_API_KEY }}
94
+ run: npx llynt check \${{ vars.LLYNT_BASE_URL || 'http://localhost:3000' }}
95
+ `;function bt(t,n,e,o){if(at.existsSync(t)&&!e){o.skipped.push(t);return}at.mkdirSync(O.dirname(t),{recursive:!0}),at.writeFileSync(t,n,"utf8"),o.created.push(t)}function Jt(t){if(!t)return;let n=t.trim();if(n)try{return new URL(n).toString().replace(/\/+$/,"")}catch{return}}function je(){return["version: v0","run:"," profile: guard-minimal"," depth: quick"," focus:"," - static",""].join(`
96
+ `)}function ze(t){let n={_schemaVersion:"llynt-targets@0",createdAt:new Date().toISOString(),updatedAt:new Date().toISOString(),...t?{baseUrlHint:t}:{},defaults:{selector:"body",viewports:[{width:360,height:720}]},targets:[{id:"home",path:"/",tags:["bootstrap"],lifecycleState:"approved"}],collections:[{id:"all",description:"Initial bootstrap target set.",targetIds:["home"]}]};return`${JSON.stringify(n,null,2)}
97
+ `}function Ye(t){process.stdout.write(`llynt init
98
+
99
+ `),process.stdout.write(` Created: ${t.created.length}
100
+ `);for(let n of t.created)process.stdout.write(` + ${n}
101
+ `);process.stdout.write(` Skipped: ${t.skipped.length}
102
+ `);for(let n of t.skipped)process.stdout.write(` = ${n}
103
+ `);process.stdout.write(`
104
+ `),process.stdout.write(`Next steps:
105
+ `),process.stdout.write(` 1. Export your API key: export LLYNT_API_KEY=<your-key>
106
+ `),process.stdout.write(` 2. Set your base URL in GitHub variable: LLYNT_BASE_URL
107
+ `),process.stdout.write(` 3. Run: npx llynt check <your-url>
108
+ `)}async function Gt(t){let n=et(t),e=typeof n["repo-root"]=="string"?String(n["repo-root"]):process.cwd(),o=typeof n.config=="string"?String(n.config):O.join("llynt","config.yml"),r=typeof n["targets-path"]=="string"?String(n["targets-path"]):O.join("llynt","targets.json"),u=!!n.force,l=!!n["skip-workflows"],s=!!n["skip-bootstrap-targets"],i=!!n.json,g=Jt(typeof n["base-url"]=="string"?String(n["base-url"]):void 0)??Jt(process.env.LLYNT_BASE_URL),m={configPath:O.resolve(e,o),targetsPath:O.resolve(e,r),prWorkflowPath:O.resolve(e,".github","workflows","llynt-pr-gate.yml"),sweepWorkflowPath:O.resolve(e,".github","workflows","llynt-repo-sweep.yml"),created:[],skipped:[]};return bt(m.configPath,je(),u,m),s||bt(m.targetsPath,ze(g),u,m),l||(bt(m.prWorkflowPath,Yt,u,m),bt(m.sweepWorkflowPath,qt,u,m)),i?process.stdout.write(JSON.stringify({ok:!0,created:m.created,skipped:m.skipped,paths:{config:m.configPath,targets:m.targetsPath,prWorkflow:m.prWorkflowPath,sweepWorkflow:m.sweepWorkflowPath}},null,2)+`
109
+ `):Ye(m),0}var qe=P(require("http")),Je=P(require("https"));var Ge=0,vt=3;function J(t){process.stdout.write(t.trimEnd()+`
110
+ `)}function Xe(t){return t.trim().toLowerCase()}function Ze(t){return/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(t)}async function Qe(t,n,e){let o=new URL(`${t}/v1/rotate-key`),r=Buffer.from(JSON.stringify({email:n,currentKey:e}),"utf8");return new Promise(u=>{let s=(o.protocol==="https:"?Je:qe).request({method:"POST",protocol:o.protocol,hostname:o.hostname,port:o.port,path:o.pathname,headers:{"content-type":"application/json","content-length":String(r.length),"user-agent":"llynt-cli-rotate/0"}},i=>{let g=[];i.on("data",m=>g.push(m)),i.on("end",()=>{let m=Buffer.concat(g).toString("utf8");try{let w=JSON.parse(m);if(i.statusCode&&i.statusCode>=200&&i.statusCode<300&&typeof w.apiKey=="string"){u({apiKey:w.apiKey});return}u({error:w.error??`Rotation failed (HTTP ${i.statusCode??0})`})}catch{u({error:`Rotation failed (HTTP ${i.statusCode??0})`})}})});s.on("error",()=>u({error:"Rotation request failed"})),s.setTimeout(1e4,()=>{s.destroy(),u({error:"Rotation request timed out"})}),s.end(r)})}async function Xt(t){let n=et(t),e=typeof n.email=="string"?String(n.email):"",o=typeof n.key=="string"?String(n.key).trim():"",r=Xe(e);if(!r||!Ze(r)||!o)return J("Error: Usage: llynt rotate --email <you@email.com> --key <current-key>"),vt;let u=(typeof process.env.LLYNT_API_URL=="string"?process.env.LLYNT_API_URL:"https://api.llynt.dev").trim(),l;try{l=nt(u)}catch(i){return J(`Error: ${i instanceof Error?i.message:String(i)}`),vt}J(` Rotating key for ${r}...`);let s=await Qe(l,r,o);return s.apiKey?(J(" New key issued:"),J(` export LLYNT_API_KEY=${s.apiKey}`),J(" npx llynt check <your-dev-url>"),Ge):(J(` Error: ${s.error??"Rotation failed."}`),vt)}var tn=`llynt - UI integrity checks for AI coding agents
36
111
 
37
112
  Usage:
38
113
  llynt check <url> Check a URL
114
+ llynt init Scaffold PR gate/bootstrap files
115
+ llynt rotate Rotate hosted API key
39
116
  llynt help Show this help
40
117
 
41
- 7 checks run instantly. Set LLYNT_API_KEY for 28 rules.
118
+ 7 checks run instantly. Set LLYNT_API_KEY for 26 rules.
42
119
  Sign up free at https://llynt.dev
43
120
 
44
121
  Examples:
45
122
  npx llynt check http://localhost:3000
46
123
  npx llynt check https://my-app.vercel.app
47
- `;async function Bt(){let e=process.argv.slice(2),r=e[0];if((!r||r==="help"||r==="--help"||r==="-h")&&(process.stdout.write(Ut),process.exit(0)),r==="check"){let t=await Fe(e.slice(1));process.exit(t)}process.stderr.write(`Unknown command: ${r}
124
+ `;async function en(){let t=process.argv.slice(2),n=t[0];if((!n||n==="help"||n==="--help"||n==="-h")&&(process.stdout.write(tn),process.exit(0)),n==="check"){let e=await zt(t.slice(1));process.exit(e)}if(n==="init"){let e=await Gt(t.slice(1));process.exit(e)}if(n==="rotate"){let e=await Xt(t.slice(1));process.exit(e)}process.stderr.write(`Unknown command: ${n}
48
125
 
49
126
  Run "llynt help" for usage.
50
- `),process.exit(3)}Bt().catch(e=>{process.stderr.write(`Fatal: ${e instanceof Error?e.message:String(e)}
127
+ `),process.exit(3)}en().catch(t=>{process.stderr.write(`Fatal: ${t instanceof Error?t.message:String(t)}
51
128
  `),process.exit(3)});
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "llynt",
3
- "version": "0.2.3",
3
+ "version": "0.2.5",
4
4
  "description": "Free UI quality checks for AI coding agents",
5
5
  "license": "SEE LICENSE IN LICENSE",
6
6
  "type": "commonjs",