@clef-sh/ui 0.1.21-beta.154 → 0.1.22-beta.160

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.
@@ -22,7 +22,7 @@ Error generating stack: `+e.message+`
22
22
  .clef-scan-line-1 { animation-delay: 0.3s; width: 90px; }
23
23
  .clef-scan-line-2 { animation-delay: 0.6s; width: 105px; }
24
24
  .clef-scan-glow { animation: clef-scan-glow 1.8s ease-in-out infinite; }
25
- `}),(0,L.jsx)(`div`,{className:`flex items-center justify-center px-6 py-12`,children:(0,L.jsxs)(`div`,{className:`min-w-[200px] rounded-card border border-edge bg-ink-850 px-10 py-7 text-center`,children:[(0,L.jsx)(`div`,{className:`mb-4 flex flex-col gap-1.5`,children:[0,1,2].map(e=>(0,L.jsx)(`div`,{className:`h-[3px] rounded-sm bg-gold-500 clef-scan-line clef-scan-line-${e}`},e))}),(0,L.jsx)(`div`,{className:`font-mono text-[11px] text-ash clef-scan-glow`,children:`Linting...`})]})})]}),!c&&y&&(0,L.jsx)(Fe,{"data-testid":`all-clear`,icon:(0,L.jsx)(`span`,{className:`text-go-500`,children:`✓`}),title:`All clear`,body:`No issues found across ${f} files`}),!c&&!y&&[`error`,`warning`,`info`].map(e=>{let t=_.filter(t=>t.severity===e);if(!t.length)return null;let n=kt[e];return(0,L.jsxs)(`div`,{className:`mb-6`,children:[(0,L.jsxs)(`div`,{className:`mb-2.5 flex items-center gap-2.5`,children:[(0,L.jsx)(`div`,{className:`flex h-[22px] w-[22px] items-center justify-center rounded-full border font-mono text-[11px] font-bold ${n.bg} ${n.border} ${n.text}`,children:n.icon}),(0,L.jsxs)(`span`,{className:`font-sans text-[13px] font-semibold ${n.text}`,children:[n.label,`s`]}),(0,L.jsx)(`span`,{className:`rounded-pill border px-2 py-px font-mono text-[10px] ${n.bg} ${n.border} ${n.text}`,children:t.length})]}),(0,L.jsx)(ve,{children:t.map((e,r)=>{let i=At[e.category??`matrix`]??{label:e.category,tone:`default`},o=e.file?.split(`/`)??[],s=o.length>=2?o[o.length-1]?.replace(`.enc.yaml`,``):void 0,c=r===t.length-1;return(0,L.jsxs)(`div`,{className:`flex items-start gap-3.5 px-[18px] py-3.5 ${n.rowStripe} ${c?``:`border-b border-edge`}`,children:[(0,L.jsx)(`div`,{className:`shrink-0 pt-0.5`,children:(0,L.jsx)(nt,{tone:i.tone,children:i.label})}),(0,L.jsxs)(`div`,{className:`min-w-0 flex-1`,children:[(0,L.jsxs)(`div`,{className:`mb-1 flex flex-wrap items-center gap-2`,children:[(0,L.jsx)(`span`,{"data-testid":`file-ref-${e.file}`,role:`link`,tabIndex:0,onClick:()=>v(e),onKeyDown:t=>{t.key===`Enter`&&v(e)},className:`font-mono text-[12px] font-semibold text-gold-500 ${e.file?`cursor-pointer underline decoration-gold-500/40 decoration-dotted`:`cursor-default no-underline`}`,children:e.file}),e.key&&(0,L.jsxs)(L.Fragment,{children:[(0,L.jsx)(`span`,{className:`font-mono text-[11px] text-ash-dim`,children:`→`}),(0,L.jsx)(`span`,{className:`rounded-sm border border-edge-strong bg-ink-900 px-[7px] py-px font-mono text-[11px] text-bone`,children:e.key})]}),s&&(0,L.jsx)(de,{env:s,small:!0})]}),(0,L.jsx)(`div`,{className:`font-sans text-[12px] text-ash ${e.fixCommand?`mb-2.5`:``}`,children:e.message}),e.fixCommand&&(0,L.jsxs)(`div`,{className:`flex w-fit items-center gap-2 rounded-md border border-edge-strong bg-ink-800 px-2.5 py-1.5`,children:[(0,L.jsx)(`span`,{className:`font-mono text-[11px] text-go-500`,children:`$`}),(0,L.jsx)(`span`,{className:`font-mono text-[11px] text-bone`,children:e.fixCommand}),(0,L.jsx)(Tt,{text:e.fixCommand})]})]}),(0,L.jsx)(`button`,{onClick:()=>a(t=>[...t,e._idx]),title:`Dismiss`,"aria-label":`Dismiss issue`,className:`shrink-0 cursor-pointer border-none bg-transparent px-1 text-[16px] leading-none text-ash-dim transition-colors hover:text-bone`,children:`×`})]},e._idx)})})]},e)}),!c&&!y&&(0,L.jsxs)(`div`,{className:`mt-2 flex items-center gap-3 rounded-md border border-edge bg-ink-850 px-4 py-3`,children:[(0,L.jsx)(`span`,{className:`text-[14px]`,children:`💡`}),(0,L.jsxs)(`span`,{className:`font-sans text-[12px] text-ash`,children:[`Fix all errors before committing. Warnings and info items won't block commits but should be reviewed. Run`,` `,(0,L.jsx)(`code`,{className:`rounded-sm bg-gold-500/15 px-1.5 py-px font-mono text-[11px] text-gold-500`,children:`clef lint --fix`}),` `,`to auto-resolve safe issues.`]})]})]})]})}function Nt(){let[e,t]=(0,g.useState)(`idle`),[n,r]=(0,g.useState)(`all`),[i,a]=(0,g.useState)(null),[o,s]=(0,g.useState)(null),[c,l]=(0,g.useState)([]),[u,d]=(0,g.useState)(`all`);(0,g.useEffect)(()=>{x(`/api/scan/status`).then(e=>e.ok?e.json():null).then(e=>{e?.lastRun&&(a(e.lastRun),s(e.lastRunAt),t(e.lastRun.matches.length>0||e.lastRun.unencryptedMatrixFiles.length>0?`issues`:`clean`))}).catch(()=>{})},[]);let f=(0,g.useCallback)(async()=>{t(`scanning`),l([]);try{let e=await x(`/api/scan`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({severity:n})});if(!e.ok)throw Error(`Scan failed`);let r=await e.json();a(r),s(new Date().toISOString()),t(r.matches.length>0||r.unencryptedMatrixFiles.length>0?`issues`:`clean`)}catch{t(`idle`)}},[n]),p=async e=>{try{await x(`/api/editor/open`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({file:e})})}catch{}},m=e=>{if(!e)return``;let t=Date.now()-new Date(e).getTime();return t<6e4?`just now`:`${Math.floor(t/6e4)}m ago`},h=(i?.matches??[]).map((e,t)=>({...e,_idx:t})).filter(e=>!c.includes(e._idx)).filter(e=>u===`pattern`?e.matchType===`pattern`:u===`entropy`?e.matchType===`entropy`:!0),_=c.length,v=(i?.matches.length??0)+(i?.unencryptedMatrixFiles.length??0),y=i?(i.durationMs/1e3).toFixed(1):`0.0`;return(0,L.jsxs)(`div`,{className:`flex flex-1 flex-col overflow-hidden`,children:[(0,L.jsxs)(B,{children:[(0,L.jsxs)(`div`,{children:[(0,L.jsx)(B.Title,{children:`Scan`}),(0,L.jsx)(B.Subtitle,{children:`clef scan — detect plaintext secrets`})]}),(e===`issues`||e===`clean`)&&(0,L.jsx)(B.Actions,{children:(0,L.jsx)(z,{onClick:f,children:`↺ Scan again`})})]}),(0,L.jsxs)(`div`,{className:`flex-1 overflow-auto p-6`,children:[e===`idle`&&(0,L.jsxs)(`div`,{"data-testid":`scan-idle`,className:`mx-auto max-w-[520px] pt-10`,children:[(0,L.jsxs)(`div`,{className:`mb-6 flex flex-col items-center gap-3 text-center`,children:[(0,L.jsx)(ne,{className:`text-ash-dim`,size:40,"aria-hidden":!0}),(0,L.jsx)(`div`,{className:`font-sans text-[14px] leading-relaxed text-ash`,children:`Scans your repository for secrets that have escaped the Clef matrix — plaintext values in files that should be encrypted.`})]}),(0,L.jsxs)(`div`,{className:`mb-6`,children:[(0,L.jsx)(`div`,{className:`mb-2.5 font-sans text-[12px] font-semibold uppercase tracking-[0.05em] text-ash`,children:`Severity`}),[`all`,`high`].map(e=>(0,L.jsxs)(`label`,{className:`mb-2 flex cursor-pointer items-center gap-2.5 font-sans text-[13px] ${n===e?`text-bone`:`text-ash`}`,children:[(0,L.jsx)(`input`,{type:`radio`,name:`severity`,value:e,checked:n===e,onChange:()=>r(e),className:`accent-gold-500`,"data-testid":`severity-${e}`}),e===`all`?`All (patterns + entropy)`:`High (patterns only)`]},e))]}),(0,L.jsx)(z,{variant:`primary`,onClick:f,"data-testid":`scan-button`,children:`Scan repository`}),(0,L.jsxs)(`div`,{className:`mt-6 rounded-md border border-edge bg-ink-850 px-4 py-3 font-sans text-[12px] text-ash`,children:[`ℹ️ `,(0,L.jsx)(`code`,{className:`font-mono`,children:`clef scan`}),` runs automatically on every commit via the pre-commit hook.`]})]}),e===`scanning`&&(0,L.jsxs)(`div`,{"data-testid":`scan-scanning`,className:`flex flex-col items-center justify-center gap-4 pt-20`,children:[(0,L.jsx)(`div`,{className:`h-10 w-10 animate-spin rounded-full border-[3px] border-gold-500/30 border-t-gold-500`}),(0,L.jsx)(`div`,{className:`font-sans text-[14px] text-ash`,children:`Scanning...`})]}),e===`clean`&&i&&(0,L.jsxs)(`div`,{"data-testid":`scan-clean`,className:`flex flex-col items-center justify-center gap-3.5 pt-14`,children:[(0,L.jsx)(`div`,{className:`flex h-14 w-14 items-center justify-center rounded-full border border-go-500/30 bg-go-500/15 text-[24px] text-go-500`,children:`✓`}),(0,L.jsx)(`div`,{className:`font-sans text-[16px] font-semibold text-go-500`,children:`No issues found`}),(0,L.jsxs)(`div`,{className:`font-mono text-[12px] text-ash`,children:[i.filesScanned,` files scanned in `,y,`s`]}),(0,L.jsxs)(`div`,{className:`font-mono text-[11px] text-ash-dim`,children:[`Last run: `,m(o)]})]}),e===`issues`&&i&&(0,L.jsxs)(`div`,{children:[(0,L.jsxs)(`div`,{className:`mb-5 flex flex-wrap items-center gap-3`,children:[(0,L.jsxs)(`span`,{className:`font-sans text-[14px] font-semibold text-bone`,children:[v,` issue`,v===1?``:`s`,` found in `,i.filesScanned,` `,`files (`,y,`s)`]}),(0,L.jsx)(`div`,{className:`flex-1`}),[{key:`all`,label:`All`},{key:`unencrypted`,label:`Unencrypted`},{key:`pattern`,label:`Pattern`},{key:`entropy`,label:`Entropy`}].map(({key:e,label:t})=>{let n=u===e;return(0,L.jsx)(`button`,{"data-testid":`filter-${e}`,onClick:()=>d(e),className:`cursor-pointer rounded-md border px-2.5 py-1 font-mono text-[11px] ${n?`border-gold-500/30 bg-gold-500/10 font-semibold text-gold-500`:`border-edge-strong bg-transparent text-ash`}`,children:t},e)})]}),(u===`all`||u===`unencrypted`)&&i.unencryptedMatrixFiles.map(e=>(0,L.jsx)(Pt,{type:`error`,typeLabel:`UNENCRYPTED FILE`,file:e,message:`Missing SOPS metadata — file is in plaintext.`,fixCommand:`clef encrypt ${e.replace(/\.enc\.(yaml|json)$/,``)}`,onViewFile:()=>p(e)},`unenc-${e}`)),h.map(e=>(0,L.jsx)(Pt,{type:`warning`,typeLabel:e.matchType===`pattern`?(e.patternName??`Pattern match`).toUpperCase():`HIGH ENTROPY (${e.entropy?.toFixed(1)})`,file:`${e.file}:${e.line}`,message:e.preview,fixCommand:e.matchType===`pattern`?`clef set <namespace>/<env> <KEY>`:`clef set <namespace>/<env> ${e.preview.split(`=`)[0]}`,onViewFile:()=>p(e.file),onDismiss:()=>l(t=>[...t,e._idx])},`match-${e._idx}`)),_>0&&(0,L.jsxs)(`div`,{className:`mt-3 font-mono text-[11px] text-ash-dim`,children:[_,` dismissed`]})]})]})]})}function Pt({type:e,typeLabel:t,file:n,message:r,fixCommand:i,onViewFile:a,onDismiss:o}){let s=e===`error`?`border-l-stop-500/40`:`border-l-warn-500/40`,c=e===`error`?`text-stop-500 bg-stop-500/15 border-stop-500/30`:`text-warn-500 bg-warn-500/15 border-warn-500/30`;return(0,L.jsxs)(`div`,{className:`mb-3 flex items-start gap-3.5 rounded-md border border-edge border-l-[3px] bg-ink-850 px-4.5 py-3.5 ${s}`,children:[(0,L.jsxs)(`div`,{className:`min-w-0 flex-1`,children:[(0,L.jsxs)(`div`,{className:`mb-1.5 flex flex-wrap items-center gap-2`,children:[(0,L.jsx)(`span`,{className:`rounded-sm border px-1.5 py-0.5 font-mono text-[9px] font-bold tracking-[0.07em] ${c}`,children:t}),(0,L.jsx)(`span`,{className:`font-mono text-[12px] text-gold-500 ${a?`cursor-pointer`:``}`,onClick:a,role:a?`button`:void 0,tabIndex:a?0:void 0,onKeyDown:a?e=>{e.key===`Enter`&&a()}:void 0,children:n})]}),(0,L.jsx)(`div`,{className:`mb-2.5 font-mono text-[12px] text-bone`,"data-testid":`match-preview`,children:r}),(0,L.jsxs)(`div`,{className:`flex w-fit items-center gap-2 rounded-md border border-edge-strong bg-ink-800 px-2.5 py-1.5`,children:[(0,L.jsx)(`span`,{className:`font-mono text-[11px] text-go-500`,children:`$`}),(0,L.jsx)(`span`,{className:`font-mono text-[11px] text-bone`,children:i}),(0,L.jsx)(Tt,{text:i})]}),a&&(0,L.jsx)(`button`,{"data-testid":`view-file-button`,onClick:a,className:`mt-2 cursor-pointer rounded-md border border-edge-strong bg-transparent px-2 py-0.5 font-sans text-[11px] text-ash`,children:`View file`})]}),o&&(0,L.jsx)(`button`,{"data-testid":`dismiss-button`,onClick:o,title:`Dismiss`,"aria-label":`Dismiss issue`,className:`shrink-0 cursor-pointer border-none bg-transparent px-1 text-[16px] leading-none text-ash-dim`,children:`×`})]})}var Ft=864e5,It={overdue:{label:`Overdue`,icon:`✕`,textClass:`text-stop-500`,bgClass:`bg-stop-500/15`,borderClass:`border-stop-500/40`,stripeClass:`border-l-stop-500/40`,badgeTone:`stop`},unknown:{label:`Unknown`,icon:`?`,textClass:`text-warn-500`,bgClass:`bg-warn-500/15`,borderClass:`border-warn-500/40`,stripeClass:`border-l-warn-500/40`,badgeTone:`warn`},ok:{label:`OK`,icon:`✓`,textClass:`text-go-500`,bgClass:`bg-go-500/15`,borderClass:`border-go-500/40`,stripeClass:`border-l-go-500/40`,badgeTone:`go`}},Lt={dev:{text:`text-go-500`,bg:`bg-go-500/10`,border:`border-go-500/20`},staging:{text:`text-warn-500`,bg:`bg-warn-500/10`,border:`border-warn-500/20`},production:{text:`text-stop-500`,bg:`bg-stop-500/10`,border:`border-stop-500/20`}};function W(e){return e.last_rotated_known?e.rotation_overdue?`overdue`:`ok`:`unknown`}function Rt(e){return Math.floor((Date.now()-new Date(e).getTime())/Ft)}function zt(e){if(!e.last_rotated_at||!e.rotation_due)return null;let t=new Date(e.rotation_due).getTime(),n=new Date(e.last_rotated_at).getTime();return Math.round((t-n)/Ft)}var Bt=[{key:`all`,label:`All keys`,textClass:`text-ash`,bgClass:`bg-ash/10`,borderClass:`border-ash/30`},{key:`overdue`,label:`Overdue`,textClass:`text-stop-500`,bgClass:`bg-stop-500/15`,borderClass:`border-stop-500/40`},{key:`unknown`,label:`Unknown`,textClass:`text-warn-500`,bgClass:`bg-warn-500/15`,borderClass:`border-warn-500/40`},{key:`ok`,label:`Compliant`,textClass:`text-go-500`,bgClass:`bg-go-500/15`,borderClass:`border-go-500/40`}];function Vt({setView:e,setNs:t}){let[n,r]=(0,g.useState)(null),[i,a]=(0,g.useState)(``),[o,s]=(0,g.useState)(!1),[c,l]=(0,g.useState)(`all`),[u,d]=(0,g.useState)(!1),f=(0,g.useCallback)(async()=>{s(!0);try{let[e,t]=await Promise.all([x(`/api/policy/check`),x(`/api/policy`)]);e.ok&&r(await e.json()),t.ok&&a((await t.json()).rawYaml)}catch{}finally{s(!1)}},[]);(0,g.useEffect)(()=>{f()},[f]);let p=e=>{let t=e.split(`/`);return t.length>=2?t[t.length-2]:t[0]},m=n=>{let r=p(n.path);r&&(t(r),e(`editor`))},h=n?.files??[],_=n?.summary,v=n?.policy,y=n?.source,b=(0,g.useMemo)(()=>h.flatMap(e=>e.keys.map(t=>({file:e,key:t}))),[h]),S=(0,g.useMemo)(()=>c===`all`?b:b.filter(e=>W(e.key)===c),[b,c]),C=(0,g.useMemo)(()=>{let e=0,t=0,n=0;for(let r of b){let i=W(r.key);i===`overdue`?e++:i===`unknown`?t++:n++}return{overdue:e,unknown:t,ok:n,total:b.length}},[b]),w=C.total>0&&C.overdue===0&&C.unknown===0,T=!o&&h.length===0,E={all:C.total,overdue:C.overdue,unknown:C.unknown,ok:C.ok};return(0,L.jsxs)(`div`,{className:`flex flex-1 flex-col overflow-hidden`,children:[(0,L.jsxs)(B,{children:[(0,L.jsxs)(`div`,{children:[(0,L.jsx)(B.Title,{children:`Policy`}),(0,L.jsx)(B.Subtitle,{children:`clef policy check — rotation verdicts`})]}),(0,L.jsx)(B.Actions,{children:(0,L.jsxs)(z,{onClick:f,children:[`↻`,` Re-run`]})})]}),v&&(0,L.jsxs)(`div`,{className:`border-b border-edge bg-ink-850 px-6 py-4`,children:[(0,L.jsxs)(`div`,{className:`flex flex-wrap items-center gap-3`,children:[(0,L.jsx)(`span`,{className:`font-sans text-[11px] font-semibold uppercase tracking-[0.08em] text-ash-dim`,children:`Default`}),(0,L.jsxs)(`span`,{className:`font-mono text-[13px] text-bone`,children:[v.rotation?.max_age_days??`—`,(0,L.jsx)(`span`,{className:`ml-0.5 text-ash`,children:`d`})]}),v.rotation?.environments&&Object.entries(v.rotation.environments).map(([e,t])=>{let n=Lt[e]??{text:`text-ash`,bg:`bg-transparent`,border:`border-ash/20`};return(0,L.jsxs)(`span`,{className:`inline-flex items-center gap-1.5 rounded-sm border px-2 py-0.5 font-mono text-[11px] ${n.text} ${n.bg} ${n.border}`,children:[(0,L.jsx)(`span`,{className:`font-bold tracking-[0.06em]`,children:e.toUpperCase()}),(0,L.jsxs)(`span`,{children:[t.max_age_days,`d`]})]},e)}),(0,L.jsx)(`div`,{className:`flex-1`}),(0,L.jsx)(`span`,{"data-testid":`policy-source`,className:`rounded-sm border px-2 py-0.5 font-mono text-[10px] ${y===`file`?`border-go-500/30 bg-go-500/10 text-go-500`:`border-edge bg-transparent text-ash`}`,children:y===`file`?`.clef/policy.yaml`:`Built-in default`}),i&&(0,L.jsx)(`button`,{"data-testid":`toggle-yaml`,onClick:()=>d(e=>!e),className:`cursor-pointer rounded-md border border-gold-500/30 bg-transparent px-2 py-0.5 font-sans text-[11px] text-gold-500 hover:bg-gold-500/10`,children:u?`Hide YAML`:`View YAML`})]}),u&&i&&(0,L.jsx)(`pre`,{"data-testid":`raw-yaml`,className:`mt-3 max-h-[200px] overflow-auto rounded-md border border-edge-strong bg-ink-800 px-3.5 py-3 font-mono text-[11px] text-bone`,children:i})]}),!o&&C.total>0&&(0,L.jsx)(`div`,{className:`flex flex-wrap items-center gap-2.5 border-b border-edge bg-ink-800 px-6 py-3.5`,children:Bt.map(e=>{let t=c===e.key,n=E[e.key];return(0,L.jsxs)(`button`,{"data-testid":`filter-${e.key}`,onClick:()=>l(e.key),className:`flex cursor-pointer items-center gap-1.5 rounded-pill border px-3 py-1 font-sans text-[12px] transition-colors ${t?`${e.textClass} ${e.bgClass} ${e.borderClass} font-semibold`:`border-edge bg-transparent text-ash hover:text-bone`}`,children:[(0,L.jsx)(`span`,{className:`font-mono text-[11px] font-bold ${e.textClass}`,children:n}),e.label]},e.key)})}),(0,L.jsxs)(`div`,{className:`flex-1 overflow-auto p-6`,children:[o&&(0,L.jsx)(Fe,{title:`Evaluating policy...`}),!o&&T&&(0,L.jsx)(Fe,{"data-testid":`no-files`,title:`No matrix files to evaluate.`,body:`Add a namespace to your manifest to start tracking rotation policy.`}),!o&&w&&(0,L.jsxs)(`div`,{"data-testid":`all-compliant`,className:`flex flex-col items-center justify-center gap-3.5 py-14`,children:[(0,L.jsx)(`div`,{className:`flex h-14 w-14 items-center justify-center rounded-full border border-go-500/30 bg-go-500/15 text-[24px] text-go-500`,children:`✓`}),(0,L.jsx)(`div`,{className:`font-sans text-[16px] font-semibold text-go-500`,children:`All compliant`}),(0,L.jsxs)(`div`,{className:`font-mono text-[12px] text-ash`,children:[C.total,` key`,C.total===1?``:`s`,` within rotation window across`,` `,_?.total_files??0,` file`,_?.total_files===1?``:`s`]})]}),!o&&!w&&!T&&v&&[`overdue`,`unknown`,`ok`].map(e=>{if(c!==`all`&&c!==e)return null;let t=S.filter(t=>W(t.key)===e);if(!t.length)return null;let n=It[e];return(0,L.jsxs)(`div`,{className:`mb-6`,children:[(0,L.jsxs)(`div`,{className:`mb-2.5 flex items-center gap-2.5`,children:[(0,L.jsx)(`div`,{className:`flex h-[22px] w-[22px] items-center justify-center rounded-full border font-mono text-[11px] font-bold ${n.borderClass} ${n.bgClass} ${n.textClass}`,children:n.icon}),(0,L.jsx)(`span`,{className:`font-sans text-[13px] font-semibold ${n.textClass}`,children:n.label}),(0,L.jsx)(`span`,{className:`rounded-pill border px-2 py-px font-mono text-[10px] ${n.borderClass} ${n.bgClass} ${n.textClass}`,children:t.length})]}),(0,L.jsx)(ve,{className:`overflow-hidden`,children:t.map((r,i)=>{let{file:a,key:o}=r,s=zt(o),c=p(a.path)??`<namespace>`,l=e===`unknown`?`No rotation record · run clef set ${c}/${a.environment} ${o.key} to establish`:o.last_rotated_at?`Last rotated ${Rt(o.last_rotated_at)}d ago · limit ${s??`?`}d · ${o.rotation_count} rotation${o.rotation_count===1?``:`s`}`:`Rotation state inconsistent`,u=e===`overdue`?`${n.label} ${o.days_overdue}d`:n.label;return(0,L.jsxs)(`div`,{className:`flex items-start gap-3.5 border-l-[3px] px-4.5 py-3.5 transition-colors ${n.stripeClass} ${i<t.length-1?`border-b border-edge`:``}`,children:[(0,L.jsx)(`div`,{className:`shrink-0 pt-0.5`,children:(0,L.jsx)(nt,{tone:n.badgeTone,variant:`solid`,children:u})}),(0,L.jsxs)(`div`,{className:`min-w-0 flex-1`,children:[(0,L.jsxs)(`div`,{className:`mb-1 flex flex-wrap items-center gap-2`,children:[(0,L.jsx)(`span`,{"data-testid":`key-ref-${o.key}`,className:`font-mono text-[13px] font-bold text-bone`,children:o.key}),(0,L.jsx)(`span`,{className:`font-mono text-[10px] text-ash`,children:`←`}),(0,L.jsx)(`span`,{"data-testid":`file-ref-${a.path}`,role:`link`,tabIndex:0,onClick:()=>m(a),onKeyDown:e=>{e.key===`Enter`&&m(a)},className:`cursor-pointer font-mono text-[11px] font-medium text-gold-500 underline decoration-gold-500/40 decoration-dotted`,children:a.path}),(0,L.jsx)(de,{env:a.environment,small:!0})]}),(0,L.jsx)(`div`,{className:`font-sans text-[12px] text-ash`,children:l})]})]},`${a.path}-${o.key}-${i}`)})})]},e)}),!o&&_&&_.total_files>0&&(0,L.jsxs)(`div`,{className:`mt-2 flex items-center gap-3 rounded-md border border-edge bg-ink-850 px-4 py-3`,children:[(0,L.jsx)(`span`,{className:`text-[14px]`,children:`💡`}),(0,L.jsxs)(`span`,{className:`font-sans text-[12px] text-ash`,children:[`Edit`,` `,(0,L.jsx)(`code`,{className:`rounded-sm bg-gold-500/10 px-1.5 py-px font-mono text-[11px] text-gold-500`,children:`.clef/policy.yaml`}),` `,`to change rotation limits. Run`,` `,(0,L.jsx)(`code`,{className:`rounded-sm bg-gold-500/10 px-1.5 py-px font-mono text-[11px] text-gold-500`,children:`clef policy check`}),` `,`locally to reproduce this verdict.`]})]})]})]})}var Ht=`w-full rounded-md border border-edge bg-ink-850 px-2.5 py-1.5 font-sans text-[13px] text-bone outline-none cursor-pointer focus-visible:border-gold-500`,Ut=`w-full box-border rounded-lg border border-edge bg-ink-850 p-3.5 font-mono text-[12px] text-bone outline-none resize-y focus-visible:border-gold-500`;function Wt({manifest:e,setView:t}){let[n,r]=(0,g.useState)(1),[i,a]=(0,g.useState)(``),[o,s]=(0,g.useState)(``),[c,l]=(0,g.useState)(``),[u,d]=(0,g.useState)(`auto`),[f,p]=(0,g.useState)(null),[m,h]=(0,g.useState)([]),[_,v]=(0,g.useState)(null),[y,b]=(0,g.useState)(!1),[S,C]=(0,g.useState)(null);(0,g.useEffect)(()=>{e&&(!i&&e.namespaces.length>0&&a(e.namespaces[0].name),!o&&e.environments.length>0&&s(e.environments[0].name))},[e,i,o]);let w=e?.namespaces??[],T=e?.environments??[],E=async()=>{if(!i||!o||!c.trim()){C(`Please select a namespace, environment, and paste content.`);return}b(!0),C(null);try{let e=await x(`/api/import/preview`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({target:`${i}/${o}`,content:c,format:u===`auto`?void 0:u,overwriteKeys:m})});if(!e.ok){C((await e.json()).error??`Preview failed`);return}p(await e.json()),h([]),r(2)}catch(e){C(e instanceof Error?e.message:`Preview failed`)}finally{b(!1)}},D=async()=>{if(!f)return;b(!0),C(null);let e=[...f.wouldImport,...f.wouldSkip.filter(e=>m.includes(e.key)).map(e=>e.key)];try{let t=await x(`/api/import/apply`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({target:`${i}/${o}`,content:c,format:u===`auto`?void 0:u,keys:e,overwriteKeys:m})});if(!t.ok){C((await t.json()).error??`Import failed`);return}v(await t.json()),l(``),r(3)}catch(e){C(e instanceof Error?e.message:`Import failed`)}finally{b(!1)}},O=()=>{r(1),l(``),p(null),v(null),h([]),C(null)},k=e=>{h(t=>t.includes(e)?t.filter(t=>t!==e):[...t,e])},A=(f?.wouldImport.length??0)+m.filter(e=>f?.wouldSkip.some(t=>t.key===e)).length;return(0,L.jsxs)(`div`,{className:`flex flex-1 flex-col overflow-hidden`,children:[(0,L.jsx)(B,{children:(0,L.jsxs)(`div`,{children:[(0,L.jsx)(B.Title,{children:`Import`}),(0,L.jsx)(B.Subtitle,{children:`clef import — bulk migrate secrets`})]})}),(0,L.jsx)(`div`,{className:`flex-1 overflow-auto p-6`,children:(0,L.jsxs)(`div`,{className:`mx-auto max-w-[620px]`,children:[(0,L.jsx)(`div`,{className:`mb-8 flex items-center`,children:[1,2,3].map((e,t)=>(0,L.jsxs)(g.Fragment,{children:[(0,L.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,L.jsx)(`div`,{className:`flex h-6 w-6 items-center justify-center rounded-full font-mono text-[11px] font-bold ${n>=e?`bg-gold-500 border border-gold-500 text-ink-950`:`bg-ink-850 border border-edge text-ash-dim`}`,children:e}),(0,L.jsx)(`span`,{className:`font-sans text-[12px] ${n>=e?`text-bone`:`text-ash-dim`} ${n===e?`font-semibold`:`font-normal`}`,children:e===1?`Source`:e===2?`Preview`:`Done`})]}),t<2&&(0,L.jsx)(`div`,{className:`mx-3 h-px min-w-[40px] flex-1 ${n>e?`bg-gold-500`:`bg-edge`}`})]},e))}),S&&(0,L.jsx)(`div`,{className:`mb-4 rounded-lg border border-stop-500/30 bg-stop-500/10 px-4 py-3 font-sans text-[13px] text-stop-500`,children:S}),n===1&&(0,L.jsxs)(`div`,{children:[(0,L.jsxs)(`div`,{className:`mb-5`,children:[(0,L.jsx)(Gt,{children:`Target`}),(0,L.jsxs)(`div`,{className:`flex gap-3`,children:[(0,L.jsxs)(`div`,{className:`flex-1`,children:[(0,L.jsx)(Kt,{children:`Namespace`}),(0,L.jsx)(`select`,{value:i,onChange:e=>a(e.target.value),className:Ht,children:w.map(e=>(0,L.jsx)(`option`,{value:e.name,children:e.name},e.name))})]}),(0,L.jsxs)(`div`,{className:`flex-1`,children:[(0,L.jsx)(Kt,{children:`Environment`}),(0,L.jsx)(`select`,{value:o,onChange:e=>s(e.target.value),className:Ht,children:T.map(e=>(0,L.jsx)(`option`,{value:e.name,children:e.name},e.name))})]})]})]}),(0,L.jsxs)(`div`,{className:`mb-5`,children:[(0,L.jsx)(Gt,{children:`Format`}),(0,L.jsx)(`div`,{className:`flex gap-4`,children:[`auto`,`dotenv`,`json`,`yaml`].map(e=>(0,L.jsxs)(`label`,{className:`flex cursor-pointer items-center gap-1.5 font-sans text-[13px] ${u===e?`text-bone`:`text-ash`}`,children:[(0,L.jsx)(`input`,{type:`radio`,name:`format`,value:e,checked:u===e,onChange:()=>d(e),className:`accent-gold-500`}),e===`auto`?`Auto`:e]},e))})]}),(0,L.jsxs)(`div`,{className:`mb-2`,children:[(0,L.jsx)(Gt,{children:`Paste secrets`}),(0,L.jsx)(`textarea`,{value:c,onChange:e=>l(e.target.value),placeholder:u===`json`?`{
25
+ `}),(0,L.jsx)(`div`,{className:`flex items-center justify-center px-6 py-12`,children:(0,L.jsxs)(`div`,{className:`min-w-[200px] rounded-card border border-edge bg-ink-850 px-10 py-7 text-center`,children:[(0,L.jsx)(`div`,{className:`mb-4 flex flex-col gap-1.5`,children:[0,1,2].map(e=>(0,L.jsx)(`div`,{className:`h-[3px] rounded-sm bg-gold-500 clef-scan-line clef-scan-line-${e}`},e))}),(0,L.jsx)(`div`,{className:`font-mono text-[11px] text-ash clef-scan-glow`,children:`Linting...`})]})})]}),!c&&y&&(0,L.jsx)(Fe,{"data-testid":`all-clear`,icon:(0,L.jsx)(`span`,{className:`text-go-500`,children:`✓`}),title:`All clear`,body:`No issues found across ${f} files`}),!c&&!y&&[`error`,`warning`,`info`].map(e=>{let t=_.filter(t=>t.severity===e);if(!t.length)return null;let n=kt[e];return(0,L.jsxs)(`div`,{className:`mb-6`,children:[(0,L.jsxs)(`div`,{className:`mb-2.5 flex items-center gap-2.5`,children:[(0,L.jsx)(`div`,{className:`flex h-[22px] w-[22px] items-center justify-center rounded-full border font-mono text-[11px] font-bold ${n.bg} ${n.border} ${n.text}`,children:n.icon}),(0,L.jsxs)(`span`,{className:`font-sans text-[13px] font-semibold ${n.text}`,children:[n.label,`s`]}),(0,L.jsx)(`span`,{className:`rounded-pill border px-2 py-px font-mono text-[10px] ${n.bg} ${n.border} ${n.text}`,children:t.length})]}),(0,L.jsx)(ve,{children:t.map((e,r)=>{let i=At[e.category??`matrix`]??{label:e.category,tone:`default`},o=e.file?.split(`/`)??[],s=o.length>=2?o[o.length-1]?.replace(`.enc.yaml`,``):void 0,c=r===t.length-1;return(0,L.jsxs)(`div`,{className:`flex items-start gap-3.5 px-[18px] py-3.5 ${n.rowStripe} ${c?``:`border-b border-edge`}`,children:[(0,L.jsx)(`div`,{className:`shrink-0 pt-0.5`,children:(0,L.jsx)(nt,{tone:i.tone,children:i.label})}),(0,L.jsxs)(`div`,{className:`min-w-0 flex-1`,children:[(0,L.jsxs)(`div`,{className:`mb-1 flex flex-wrap items-center gap-2`,children:[(0,L.jsx)(`span`,{"data-testid":`file-ref-${e.file}`,role:`link`,tabIndex:0,onClick:()=>v(e),onKeyDown:t=>{t.key===`Enter`&&v(e)},className:`font-mono text-[12px] font-semibold text-gold-500 ${e.file?`cursor-pointer underline decoration-gold-500/40 decoration-dotted`:`cursor-default no-underline`}`,children:e.file}),e.key&&(0,L.jsxs)(L.Fragment,{children:[(0,L.jsx)(`span`,{className:`font-mono text-[11px] text-ash-dim`,children:`→`}),(0,L.jsx)(`span`,{className:`rounded-sm border border-edge-strong bg-ink-900 px-[7px] py-px font-mono text-[11px] text-bone`,children:e.key})]}),s&&(0,L.jsx)(de,{env:s,small:!0})]}),(0,L.jsx)(`div`,{className:`font-sans text-[12px] text-ash ${e.fixCommand?`mb-2.5`:``}`,children:e.message}),e.fixCommand&&(0,L.jsxs)(`div`,{className:`flex w-fit items-center gap-2 rounded-md border border-edge-strong bg-ink-800 px-2.5 py-1.5`,children:[(0,L.jsx)(`span`,{className:`font-mono text-[11px] text-go-500`,children:`$`}),(0,L.jsx)(`span`,{className:`font-mono text-[11px] text-bone`,children:e.fixCommand}),(0,L.jsx)(Tt,{text:e.fixCommand})]})]}),(0,L.jsx)(`button`,{onClick:()=>a(t=>[...t,e._idx]),title:`Dismiss`,"aria-label":`Dismiss issue`,className:`shrink-0 cursor-pointer border-none bg-transparent px-1 text-[16px] leading-none text-ash-dim transition-colors hover:text-bone`,children:`×`})]},e._idx)})})]},e)}),!c&&!y&&(0,L.jsxs)(`div`,{className:`mt-2 flex items-center gap-3 rounded-md border border-edge bg-ink-850 px-4 py-3`,children:[(0,L.jsx)(`span`,{className:`text-[14px]`,children:`💡`}),(0,L.jsxs)(`span`,{className:`font-sans text-[12px] text-ash`,children:[`Fix all errors before committing. Warnings and info items won't block commits but should be reviewed. Run`,` `,(0,L.jsx)(`code`,{className:`rounded-sm bg-gold-500/15 px-1.5 py-px font-mono text-[11px] text-gold-500`,children:`clef lint --fix`}),` `,`to auto-resolve safe issues.`]})]})]})]})}function Nt(){let[e,t]=(0,g.useState)(`idle`),[n,r]=(0,g.useState)(`all`),[i,a]=(0,g.useState)(null),[o,s]=(0,g.useState)(null),[c,l]=(0,g.useState)([]),[u,d]=(0,g.useState)(`all`);(0,g.useEffect)(()=>{x(`/api/scan/status`).then(e=>e.ok?e.json():null).then(e=>{e?.lastRun&&(a(e.lastRun),s(e.lastRunAt),t(e.lastRun.matches.length>0||e.lastRun.unencryptedMatrixFiles.length>0?`issues`:`clean`))}).catch(()=>{})},[]);let f=(0,g.useCallback)(async()=>{t(`scanning`),l([]);try{let e=await x(`/api/scan`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({severity:n})});if(!e.ok)throw Error(`Scan failed`);let r=await e.json();a(r),s(new Date().toISOString()),t(r.matches.length>0||r.unencryptedMatrixFiles.length>0?`issues`:`clean`)}catch{t(`idle`)}},[n]),p=async e=>{try{await x(`/api/editor/open`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({file:e})})}catch{}},m=e=>{if(!e)return``;let t=Date.now()-new Date(e).getTime();return t<6e4?`just now`:`${Math.floor(t/6e4)}m ago`},h=(i?.matches??[]).map((e,t)=>({...e,_idx:t})).filter(e=>!c.includes(e._idx)).filter(e=>u===`pattern`?e.matchType===`pattern`:u===`entropy`?e.matchType===`entropy`:!0),_=c.length,v=(i?.matches.length??0)+(i?.unencryptedMatrixFiles.length??0),y=i?(i.durationMs/1e3).toFixed(1):`0.0`;return(0,L.jsxs)(`div`,{className:`flex flex-1 flex-col overflow-hidden`,children:[(0,L.jsxs)(B,{children:[(0,L.jsxs)(`div`,{children:[(0,L.jsx)(B.Title,{children:`Scan`}),(0,L.jsx)(B.Subtitle,{children:`clef scan — detect plaintext secrets`})]}),(e===`issues`||e===`clean`)&&(0,L.jsx)(B.Actions,{children:(0,L.jsx)(z,{onClick:f,children:`↺ Scan again`})})]}),(0,L.jsxs)(`div`,{className:`flex-1 overflow-auto p-6`,children:[e===`idle`&&(0,L.jsxs)(`div`,{"data-testid":`scan-idle`,className:`mx-auto max-w-[520px] pt-10`,children:[(0,L.jsxs)(`div`,{className:`mb-6 flex flex-col items-center gap-3 text-center`,children:[(0,L.jsx)(ne,{className:`text-ash-dim`,size:40,"aria-hidden":!0}),(0,L.jsx)(`div`,{className:`font-sans text-[14px] leading-relaxed text-ash`,children:`Scans your repository for secrets that have escaped the Clef matrix — plaintext values in files that should be encrypted.`})]}),(0,L.jsxs)(`div`,{className:`mb-6`,children:[(0,L.jsx)(`div`,{className:`mb-2.5 font-sans text-[12px] font-semibold uppercase tracking-[0.05em] text-ash`,children:`Severity`}),[`all`,`high`].map(e=>(0,L.jsxs)(`label`,{className:`mb-2 flex cursor-pointer items-center gap-2.5 font-sans text-[13px] ${n===e?`text-bone`:`text-ash`}`,children:[(0,L.jsx)(`input`,{type:`radio`,name:`severity`,value:e,checked:n===e,onChange:()=>r(e),className:`accent-gold-500`,"data-testid":`severity-${e}`}),e===`all`?`All (patterns + entropy)`:`High (patterns only)`]},e))]}),(0,L.jsx)(z,{variant:`primary`,onClick:f,"data-testid":`scan-button`,children:`Scan repository`}),(0,L.jsxs)(`div`,{className:`mt-6 rounded-md border border-edge bg-ink-850 px-4 py-3 font-sans text-[12px] text-ash`,children:[`ℹ️ `,(0,L.jsx)(`code`,{className:`font-mono`,children:`clef scan`}),` runs automatically on every commit via the pre-commit hook.`]})]}),e===`scanning`&&(0,L.jsxs)(`div`,{"data-testid":`scan-scanning`,className:`flex flex-col items-center justify-center gap-4 pt-20`,children:[(0,L.jsx)(`div`,{className:`h-10 w-10 animate-spin rounded-full border-[3px] border-gold-500/30 border-t-gold-500`}),(0,L.jsx)(`div`,{className:`font-sans text-[14px] text-ash`,children:`Scanning...`})]}),e===`clean`&&i&&(0,L.jsxs)(`div`,{"data-testid":`scan-clean`,className:`flex flex-col items-center justify-center gap-3.5 pt-14`,children:[(0,L.jsx)(`div`,{className:`flex h-14 w-14 items-center justify-center rounded-full border border-go-500/30 bg-go-500/15 text-[24px] text-go-500`,children:`✓`}),(0,L.jsx)(`div`,{className:`font-sans text-[16px] font-semibold text-go-500`,children:`No issues found`}),(0,L.jsxs)(`div`,{className:`font-mono text-[12px] text-ash`,children:[i.filesScanned,` files scanned in `,y,`s`]}),(0,L.jsxs)(`div`,{className:`font-mono text-[11px] text-ash-dim`,children:[`Last run: `,m(o)]})]}),e===`issues`&&i&&(0,L.jsxs)(`div`,{children:[(0,L.jsxs)(`div`,{className:`mb-5 flex flex-wrap items-center gap-3`,children:[(0,L.jsxs)(`span`,{className:`font-sans text-[14px] font-semibold text-bone`,children:[v,` issue`,v===1?``:`s`,` found in `,i.filesScanned,` `,`files (`,y,`s)`]}),(0,L.jsx)(`div`,{className:`flex-1`}),[{key:`all`,label:`All`},{key:`unencrypted`,label:`Unencrypted`},{key:`pattern`,label:`Pattern`},{key:`entropy`,label:`Entropy`}].map(({key:e,label:t})=>{let n=u===e;return(0,L.jsx)(`button`,{"data-testid":`filter-${e}`,onClick:()=>d(e),className:`cursor-pointer rounded-md border px-2.5 py-1 font-mono text-[11px] ${n?`border-gold-500/30 bg-gold-500/10 font-semibold text-gold-500`:`border-edge-strong bg-transparent text-ash`}`,children:t},e)})]}),(u===`all`||u===`unencrypted`)&&i.unencryptedMatrixFiles.map(e=>(0,L.jsx)(Pt,{type:`error`,typeLabel:`UNENCRYPTED FILE`,file:e,message:`Missing SOPS metadata — file is in plaintext.`,fixCommand:`clef encrypt ${e.replace(/\.enc\.(yaml|json)$/,``)}`,onViewFile:()=>p(e)},`unenc-${e}`)),h.map(e=>(0,L.jsx)(Pt,{type:`warning`,typeLabel:e.matchType===`pattern`?(e.patternName??`Pattern match`).toUpperCase():`HIGH ENTROPY (${e.entropy?.toFixed(1)})`,file:`${e.file}:${e.line}`,message:e.preview,fixCommand:e.matchType===`pattern`?`clef set <namespace>/<env> <KEY>`:`clef set <namespace>/<env> ${e.preview.split(`=`)[0]}`,onViewFile:()=>p(e.file),onDismiss:()=>l(t=>[...t,e._idx])},`match-${e._idx}`)),_>0&&(0,L.jsxs)(`div`,{className:`mt-3 font-mono text-[11px] text-ash-dim`,children:[_,` dismissed`]})]})]})]})}function Pt({type:e,typeLabel:t,file:n,message:r,fixCommand:i,onViewFile:a,onDismiss:o}){let s=e===`error`?`border-l-stop-500/40`:`border-l-warn-500/40`,c=e===`error`?`text-stop-500 bg-stop-500/15 border-stop-500/30`:`text-warn-500 bg-warn-500/15 border-warn-500/30`;return(0,L.jsxs)(`div`,{className:`mb-3 flex items-start gap-3.5 rounded-md border border-edge border-l-[3px] bg-ink-850 px-4.5 py-3.5 ${s}`,children:[(0,L.jsxs)(`div`,{className:`min-w-0 flex-1`,children:[(0,L.jsxs)(`div`,{className:`mb-1.5 flex flex-wrap items-center gap-2`,children:[(0,L.jsx)(`span`,{className:`rounded-sm border px-1.5 py-0.5 font-mono text-[9px] font-bold tracking-[0.07em] ${c}`,children:t}),(0,L.jsx)(`span`,{className:`font-mono text-[12px] text-gold-500 ${a?`cursor-pointer`:``}`,onClick:a,role:a?`button`:void 0,tabIndex:a?0:void 0,onKeyDown:a?e=>{e.key===`Enter`&&a()}:void 0,children:n})]}),(0,L.jsx)(`div`,{className:`mb-2.5 font-mono text-[12px] text-bone`,"data-testid":`match-preview`,children:r}),(0,L.jsxs)(`div`,{className:`flex w-fit items-center gap-2 rounded-md border border-edge-strong bg-ink-800 px-2.5 py-1.5`,children:[(0,L.jsx)(`span`,{className:`font-mono text-[11px] text-go-500`,children:`$`}),(0,L.jsx)(`span`,{className:`font-mono text-[11px] text-bone`,children:i}),(0,L.jsx)(Tt,{text:i})]}),a&&(0,L.jsx)(`button`,{"data-testid":`view-file-button`,onClick:a,className:`mt-2 cursor-pointer rounded-md border border-edge-strong bg-transparent px-2 py-0.5 font-sans text-[11px] text-ash`,children:`View file`})]}),o&&(0,L.jsx)(`button`,{"data-testid":`dismiss-button`,onClick:o,title:`Dismiss`,"aria-label":`Dismiss issue`,className:`shrink-0 cursor-pointer border-none bg-transparent px-1 text-[16px] leading-none text-ash-dim`,children:`×`})]})}var Ft=864e5,It={overdue:{label:`Overdue`,icon:`✕`,textClass:`text-stop-500`,bgClass:`bg-stop-500/15`,borderClass:`border-stop-500/40`,stripeClass:`border-l-stop-500/40`,badgeTone:`stop`},unknown:{label:`Unknown`,icon:`?`,textClass:`text-warn-500`,bgClass:`bg-warn-500/15`,borderClass:`border-warn-500/40`,stripeClass:`border-l-warn-500/40`,badgeTone:`warn`},ok:{label:`OK`,icon:`✓`,textClass:`text-go-500`,bgClass:`bg-go-500/15`,borderClass:`border-go-500/40`,stripeClass:`border-l-go-500/40`,badgeTone:`go`}},Lt={dev:{text:`text-go-500`,bg:`bg-go-500/10`,border:`border-go-500/20`},staging:{text:`text-warn-500`,bg:`bg-warn-500/10`,border:`border-warn-500/20`},production:{text:`text-stop-500`,bg:`bg-stop-500/10`,border:`border-stop-500/20`}};function W(e){return e.last_rotated_known?e.rotation_overdue?`overdue`:`ok`:`unknown`}function Rt(e){return Math.floor((Date.now()-new Date(e).getTime())/Ft)}function zt(e){if(!e.last_rotated_at||!e.rotation_due)return null;let t=new Date(e.rotation_due).getTime(),n=new Date(e.last_rotated_at).getTime();return Math.round((t-n)/Ft)}var Bt=[{key:`all`,label:`All keys`,textClass:`text-ash`,bgClass:`bg-ash/10`,borderClass:`border-ash/30`},{key:`overdue`,label:`Overdue`,textClass:`text-stop-500`,bgClass:`bg-stop-500/15`,borderClass:`border-stop-500/40`},{key:`unknown`,label:`Unknown`,textClass:`text-warn-500`,bgClass:`bg-warn-500/15`,borderClass:`border-warn-500/40`},{key:`ok`,label:`Compliant`,textClass:`text-go-500`,bgClass:`bg-go-500/15`,borderClass:`border-go-500/40`}];function Vt({setView:e,setNs:t}){let[n,r]=(0,g.useState)(null),[i,a]=(0,g.useState)(``),[o,s]=(0,g.useState)(!1),[c,l]=(0,g.useState)(`all`),[u,d]=(0,g.useState)(!1),[f,p]=(0,g.useState)(null),m=(0,g.useCallback)(async()=>{s(!0),p(null);try{let[e,t]=await Promise.all([x(`/api/policy/check`),x(`/api/policy`)]);e.ok?r(await e.json()):p(`policy check failed: HTTP ${e.status}`),t.ok&&a((await t.json()).rawYaml)}catch(e){p(e instanceof Error?e.message:`policy check failed`)}finally{s(!1)}},[]);(0,g.useEffect)(()=>{m()},[m]);let h=e=>{let t=e.split(`/`);return t.length>=2?t[t.length-2]:t[0]},_=n=>{let r=h(n.path);r&&(t(r),e(`editor`))},v=n?.files??[],y=n?.summary,b=n?.policy,S=n?.source,C=(0,g.useMemo)(()=>v.flatMap(e=>e.keys.map(t=>({file:e,key:t}))),[v]),w=(0,g.useMemo)(()=>c===`all`?C:C.filter(e=>W(e.key)===c),[C,c]),T=(0,g.useMemo)(()=>{let e=0,t=0,n=0;for(let r of C){let i=W(r.key);i===`overdue`?e++:i===`unknown`?t++:n++}return{overdue:e,unknown:t,ok:n,total:C.length}},[C]),E=T.total>0&&T.overdue===0&&T.unknown===0,D=!o&&v.length===0,O={all:T.total,overdue:T.overdue,unknown:T.unknown,ok:T.ok};return(0,L.jsxs)(`div`,{className:`flex flex-1 flex-col overflow-hidden`,children:[(0,L.jsxs)(B,{children:[(0,L.jsxs)(`div`,{children:[(0,L.jsx)(B.Title,{children:`Policy`}),(0,L.jsx)(B.Subtitle,{children:`clef policy check — rotation verdicts`})]}),(0,L.jsx)(B.Actions,{children:(0,L.jsxs)(z,{onClick:m,children:[`↻`,` Re-run`]})})]}),b&&(0,L.jsxs)(`div`,{className:`border-b border-edge bg-ink-850 px-6 py-4`,children:[(0,L.jsxs)(`div`,{className:`flex flex-wrap items-center gap-3`,children:[(0,L.jsx)(`span`,{className:`font-sans text-[11px] font-semibold uppercase tracking-[0.08em] text-ash-dim`,children:`Default`}),(0,L.jsxs)(`span`,{className:`font-mono text-[13px] text-bone`,children:[b.rotation?.max_age_days??`—`,(0,L.jsx)(`span`,{className:`ml-0.5 text-ash`,children:`d`})]}),b.rotation?.environments&&Object.entries(b.rotation.environments).map(([e,t])=>{let n=Lt[e]??{text:`text-ash`,bg:`bg-transparent`,border:`border-ash/20`};return(0,L.jsxs)(`span`,{className:`inline-flex items-center gap-1.5 rounded-sm border px-2 py-0.5 font-mono text-[11px] ${n.text} ${n.bg} ${n.border}`,children:[(0,L.jsx)(`span`,{className:`font-bold tracking-[0.06em]`,children:e.toUpperCase()}),(0,L.jsxs)(`span`,{children:[t.max_age_days,`d`]})]},e)}),(0,L.jsx)(`div`,{className:`flex-1`}),(0,L.jsx)(`span`,{"data-testid":`policy-source`,className:`rounded-sm border px-2 py-0.5 font-mono text-[10px] ${S===`file`?`border-go-500/30 bg-go-500/10 text-go-500`:`border-edge bg-transparent text-ash`}`,children:S===`file`?`.clef/policy.yaml`:`Built-in default`}),i&&(0,L.jsx)(`button`,{"data-testid":`toggle-yaml`,onClick:()=>d(e=>!e),className:`cursor-pointer rounded-md border border-gold-500/30 bg-transparent px-2 py-0.5 font-sans text-[11px] text-gold-500 hover:bg-gold-500/10`,children:u?`Hide YAML`:`View YAML`})]}),u&&i&&(0,L.jsx)(`pre`,{"data-testid":`raw-yaml`,className:`mt-3 max-h-[200px] overflow-auto rounded-md border border-edge-strong bg-ink-800 px-3.5 py-3 font-mono text-[11px] text-bone`,children:i})]}),!o&&T.total>0&&(0,L.jsx)(`div`,{className:`flex flex-wrap items-center gap-2.5 border-b border-edge bg-ink-800 px-6 py-3.5`,children:Bt.map(e=>{let t=c===e.key,n=O[e.key];return(0,L.jsxs)(`button`,{"data-testid":`filter-${e.key}`,onClick:()=>l(e.key),className:`flex cursor-pointer items-center gap-1.5 rounded-pill border px-3 py-1 font-sans text-[12px] transition-colors ${t?`${e.textClass} ${e.bgClass} ${e.borderClass} font-semibold`:`border-edge bg-transparent text-ash hover:text-bone`}`,children:[(0,L.jsx)(`span`,{className:`font-mono text-[11px] font-bold ${e.textClass}`,children:n}),e.label]},e.key)})}),(0,L.jsxs)(`div`,{className:`flex-1 overflow-auto p-6`,children:[o&&(0,L.jsx)(Fe,{title:`Evaluating policy...`}),!o&&f&&(0,L.jsx)(Fe,{"data-testid":`policy-load-error`,title:`Failed to load policy`,body:f}),!o&&!f&&D&&(0,L.jsx)(Fe,{"data-testid":`no-files`,title:`No matrix files to evaluate.`,body:`Add a namespace to your manifest to start tracking rotation policy.`}),!o&&E&&(0,L.jsxs)(`div`,{"data-testid":`all-compliant`,className:`flex flex-col items-center justify-center gap-3.5 py-14`,children:[(0,L.jsx)(`div`,{className:`flex h-14 w-14 items-center justify-center rounded-full border border-go-500/30 bg-go-500/15 text-[24px] text-go-500`,children:`✓`}),(0,L.jsx)(`div`,{className:`font-sans text-[16px] font-semibold text-go-500`,children:`All compliant`}),(0,L.jsxs)(`div`,{className:`font-mono text-[12px] text-ash`,children:[T.total,` key`,T.total===1?``:`s`,` within rotation window across`,` `,y?.total_files??0,` file`,y?.total_files===1?``:`s`]})]}),!o&&!E&&!D&&b&&[`overdue`,`unknown`,`ok`].map(e=>{if(c!==`all`&&c!==e)return null;let t=w.filter(t=>W(t.key)===e);if(!t.length)return null;let n=It[e];return(0,L.jsxs)(`div`,{className:`mb-6`,children:[(0,L.jsxs)(`div`,{className:`mb-2.5 flex items-center gap-2.5`,children:[(0,L.jsx)(`div`,{className:`flex h-[22px] w-[22px] items-center justify-center rounded-full border font-mono text-[11px] font-bold ${n.borderClass} ${n.bgClass} ${n.textClass}`,children:n.icon}),(0,L.jsx)(`span`,{className:`font-sans text-[13px] font-semibold ${n.textClass}`,children:n.label}),(0,L.jsx)(`span`,{className:`rounded-pill border px-2 py-px font-mono text-[10px] ${n.borderClass} ${n.bgClass} ${n.textClass}`,children:t.length})]}),(0,L.jsx)(ve,{className:`overflow-hidden`,children:t.map((r,i)=>{let{file:a,key:o}=r,s=zt(o),c=h(a.path)??`<namespace>`,l=e===`unknown`?`No rotation record · run clef set ${c}/${a.environment} ${o.key} to establish`:o.last_rotated_at?`Last rotated ${Rt(o.last_rotated_at)}d ago · limit ${s??`?`}d · ${o.rotation_count} rotation${o.rotation_count===1?``:`s`}`:`Rotation state inconsistent`,u=e===`overdue`?`${n.label} ${o.days_overdue}d`:n.label;return(0,L.jsxs)(`div`,{className:`flex items-start gap-3.5 border-l-[3px] px-4.5 py-3.5 transition-colors ${n.stripeClass} ${i<t.length-1?`border-b border-edge`:``}`,children:[(0,L.jsx)(`div`,{className:`shrink-0 pt-0.5`,children:(0,L.jsx)(nt,{tone:n.badgeTone,variant:`solid`,children:u})}),(0,L.jsxs)(`div`,{className:`min-w-0 flex-1`,children:[(0,L.jsxs)(`div`,{className:`mb-1 flex flex-wrap items-center gap-2`,children:[(0,L.jsx)(`span`,{"data-testid":`key-ref-${o.key}`,className:`font-mono text-[13px] font-bold text-bone`,children:o.key}),(0,L.jsx)(`span`,{className:`font-mono text-[10px] text-ash`,children:`←`}),(0,L.jsx)(`span`,{"data-testid":`file-ref-${a.path}`,role:`link`,tabIndex:0,onClick:()=>_(a),onKeyDown:e=>{e.key===`Enter`&&_(a)},className:`cursor-pointer font-mono text-[11px] font-medium text-gold-500 underline decoration-gold-500/40 decoration-dotted`,children:a.path}),(0,L.jsx)(de,{env:a.environment,small:!0})]}),(0,L.jsx)(`div`,{className:`font-sans text-[12px] text-ash`,children:l})]})]},`${a.path}-${o.key}-${i}`)})})]},e)}),!o&&y&&y.total_files>0&&(0,L.jsxs)(`div`,{className:`mt-2 flex items-center gap-3 rounded-md border border-edge bg-ink-850 px-4 py-3`,children:[(0,L.jsx)(`span`,{className:`text-[14px]`,children:`💡`}),(0,L.jsxs)(`span`,{className:`font-sans text-[12px] text-ash`,children:[`Edit`,` `,(0,L.jsx)(`code`,{className:`rounded-sm bg-gold-500/10 px-1.5 py-px font-mono text-[11px] text-gold-500`,children:`.clef/policy.yaml`}),` `,`to change rotation limits. Run`,` `,(0,L.jsx)(`code`,{className:`rounded-sm bg-gold-500/10 px-1.5 py-px font-mono text-[11px] text-gold-500`,children:`clef policy check`}),` `,`locally to reproduce this verdict.`]})]})]})]})}var Ht=`w-full rounded-md border border-edge bg-ink-850 px-2.5 py-1.5 font-sans text-[13px] text-bone outline-none cursor-pointer focus-visible:border-gold-500`,Ut=`w-full box-border rounded-lg border border-edge bg-ink-850 p-3.5 font-mono text-[12px] text-bone outline-none resize-y focus-visible:border-gold-500`;function Wt({manifest:e,setView:t}){let[n,r]=(0,g.useState)(1),[i,a]=(0,g.useState)(``),[o,s]=(0,g.useState)(``),[c,l]=(0,g.useState)(``),[u,d]=(0,g.useState)(`auto`),[f,p]=(0,g.useState)(null),[m,h]=(0,g.useState)([]),[_,v]=(0,g.useState)(null),[y,b]=(0,g.useState)(!1),[S,C]=(0,g.useState)(null);(0,g.useEffect)(()=>{e&&(!i&&e.namespaces.length>0&&a(e.namespaces[0].name),!o&&e.environments.length>0&&s(e.environments[0].name))},[e,i,o]);let w=e?.namespaces??[],T=e?.environments??[],E=async()=>{if(!i||!o||!c.trim()){C(`Please select a namespace, environment, and paste content.`);return}b(!0),C(null);try{let e=await x(`/api/import/preview`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({target:`${i}/${o}`,content:c,format:u===`auto`?void 0:u,overwriteKeys:m})});if(!e.ok){C((await e.json()).error??`Preview failed`);return}p(await e.json()),h([]),r(2)}catch(e){C(e instanceof Error?e.message:`Preview failed`)}finally{b(!1)}},D=async()=>{if(!f)return;b(!0),C(null);let e=[...f.wouldImport,...f.wouldSkip.filter(e=>m.includes(e.key)).map(e=>e.key)];try{let t=await x(`/api/import/apply`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({target:`${i}/${o}`,content:c,format:u===`auto`?void 0:u,keys:e,overwriteKeys:m})});if(!t.ok){C((await t.json()).error??`Import failed`);return}v(await t.json()),l(``),r(3)}catch(e){C(e instanceof Error?e.message:`Import failed`)}finally{b(!1)}},O=()=>{r(1),l(``),p(null),v(null),h([]),C(null)},k=e=>{h(t=>t.includes(e)?t.filter(t=>t!==e):[...t,e])},A=(f?.wouldImport.length??0)+m.filter(e=>f?.wouldSkip.some(t=>t.key===e)).length;return(0,L.jsxs)(`div`,{className:`flex flex-1 flex-col overflow-hidden`,children:[(0,L.jsx)(B,{children:(0,L.jsxs)(`div`,{children:[(0,L.jsx)(B.Title,{children:`Import`}),(0,L.jsx)(B.Subtitle,{children:`clef import — bulk migrate secrets`})]})}),(0,L.jsx)(`div`,{className:`flex-1 overflow-auto p-6`,children:(0,L.jsxs)(`div`,{className:`mx-auto max-w-[620px]`,children:[(0,L.jsx)(`div`,{className:`mb-8 flex items-center`,children:[1,2,3].map((e,t)=>(0,L.jsxs)(g.Fragment,{children:[(0,L.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,L.jsx)(`div`,{className:`flex h-6 w-6 items-center justify-center rounded-full font-mono text-[11px] font-bold ${n>=e?`bg-gold-500 border border-gold-500 text-ink-950`:`bg-ink-850 border border-edge text-ash-dim`}`,children:e}),(0,L.jsx)(`span`,{className:`font-sans text-[12px] ${n>=e?`text-bone`:`text-ash-dim`} ${n===e?`font-semibold`:`font-normal`}`,children:e===1?`Source`:e===2?`Preview`:`Done`})]}),t<2&&(0,L.jsx)(`div`,{className:`mx-3 h-px min-w-[40px] flex-1 ${n>e?`bg-gold-500`:`bg-edge`}`})]},e))}),S&&(0,L.jsx)(`div`,{className:`mb-4 rounded-lg border border-stop-500/30 bg-stop-500/10 px-4 py-3 font-sans text-[13px] text-stop-500`,children:S}),n===1&&(0,L.jsxs)(`div`,{children:[(0,L.jsxs)(`div`,{className:`mb-5`,children:[(0,L.jsx)(Gt,{children:`Target`}),(0,L.jsxs)(`div`,{className:`flex gap-3`,children:[(0,L.jsxs)(`div`,{className:`flex-1`,children:[(0,L.jsx)(Kt,{children:`Namespace`}),(0,L.jsx)(`select`,{value:i,onChange:e=>a(e.target.value),className:Ht,children:w.map(e=>(0,L.jsx)(`option`,{value:e.name,children:e.name},e.name))})]}),(0,L.jsxs)(`div`,{className:`flex-1`,children:[(0,L.jsx)(Kt,{children:`Environment`}),(0,L.jsx)(`select`,{value:o,onChange:e=>s(e.target.value),className:Ht,children:T.map(e=>(0,L.jsx)(`option`,{value:e.name,children:e.name},e.name))})]})]})]}),(0,L.jsxs)(`div`,{className:`mb-5`,children:[(0,L.jsx)(Gt,{children:`Format`}),(0,L.jsx)(`div`,{className:`flex gap-4`,children:[`auto`,`dotenv`,`json`,`yaml`].map(e=>(0,L.jsxs)(`label`,{className:`flex cursor-pointer items-center gap-1.5 font-sans text-[13px] ${u===e?`text-bone`:`text-ash`}`,children:[(0,L.jsx)(`input`,{type:`radio`,name:`format`,value:e,checked:u===e,onChange:()=>d(e),className:`accent-gold-500`}),e===`auto`?`Auto`:e]},e))})]}),(0,L.jsxs)(`div`,{className:`mb-2`,children:[(0,L.jsx)(Gt,{children:`Paste secrets`}),(0,L.jsx)(`textarea`,{value:c,onChange:e=>l(e.target.value),placeholder:u===`json`?`{
26
26
  "DB_HOST": "localhost",
27
27
  "DB_PORT": "5432"
28
28
  }`:u===`yaml`?`DB_HOST: localhost
@@ -13,7 +13,7 @@
13
13
  href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@400;600;700&display=swap"
14
14
  rel="stylesheet"
15
15
  />
16
- <script type="module" crossorigin src="/assets/index-DPWHjBbB.js"></script>
16
+ <script type="module" crossorigin src="/assets/index-BVC8eF9m.js"></script>
17
17
  <link rel="stylesheet" crossorigin href="/assets/index-qsLTYpc9.css">
18
18
  </head>
19
19
  <body>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clef-sh/ui",
3
- "version": "0.1.21-beta.154",
3
+ "version": "0.1.22-beta.160",
4
4
  "description": "Local web UI for Clef — git-native secrets management",
5
5
  "repository": {
6
6
  "type": "git",
@@ -34,9 +34,9 @@
34
34
  "test:coverage": "jest --coverage"
35
35
  },
36
36
  "dependencies": {
37
- "@clef-sh/core": "0.1.21-beta.154",
38
- "@clef-sh/design": "0.1.1-beta.154",
39
- "@clef-sh/runtime": "0.1.24-beta.154",
37
+ "@clef-sh/core": "0.1.22-beta.160",
38
+ "@clef-sh/design": "0.1.2-beta.160",
39
+ "@clef-sh/runtime": "0.1.25-beta.160",
40
40
  "express": "^5.1.0",
41
41
  "express-rate-limit": "^8.3.2",
42
42
  "lucide-react": "^0.468.0",
@@ -141,9 +141,11 @@ export function PolicyView({ setView, setNs }: PolicyViewProps) {
141
141
  const [loading, setLoading] = useState(false);
142
142
  const [filter, setFilter] = useState<StatusFilter>("all");
143
143
  const [showYaml, setShowYaml] = useState(false);
144
+ const [loadError, setLoadError] = useState<string | null>(null);
144
145
 
145
146
  const loadPolicy = useCallback(async () => {
146
147
  setLoading(true);
148
+ setLoadError(null);
147
149
  try {
148
150
  const [checkRes, policyRes] = await Promise.all([
149
151
  apiFetch("/api/policy/check"),
@@ -151,13 +153,15 @@ export function PolicyView({ setView, setNs }: PolicyViewProps) {
151
153
  ]);
152
154
  if (checkRes.ok) {
153
155
  setData((await checkRes.json()) as PolicyCheckResponse);
156
+ } else {
157
+ setLoadError(`policy check failed: HTTP ${checkRes.status}`);
154
158
  }
155
159
  if (policyRes.ok) {
156
160
  const p = (await policyRes.json()) as { rawYaml: string };
157
161
  setRawYaml(p.rawYaml);
158
162
  }
159
- } catch {
160
- // Silently fail user can retry
163
+ } catch (err) {
164
+ setLoadError(err instanceof Error ? err.message : "policy check failed");
161
165
  } finally {
162
166
  setLoading(false);
163
167
  }
@@ -324,7 +328,15 @@ export function PolicyView({ setView, setNs }: PolicyViewProps) {
324
328
  <div className="flex-1 overflow-auto p-6">
325
329
  {loading && <EmptyState title="Evaluating policy..." />}
326
330
 
327
- {!loading && noFiles && (
331
+ {!loading && loadError && (
332
+ <EmptyState
333
+ data-testid="policy-load-error"
334
+ title="Failed to load policy"
335
+ body={loadError}
336
+ />
337
+ )}
338
+
339
+ {!loading && !loadError && noFiles && (
328
340
  <EmptyState
329
341
  data-testid="no-files"
330
342
  title="No matrix files to evaluate."