@clef-sh/ui 0.1.21 → 0.1.22
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
|
package/dist/client/index.html
CHANGED
|
@@ -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-
|
|
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.
|
|
3
|
+
"version": "0.1.22",
|
|
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.
|
|
38
|
-
"@clef-sh/design": "0.1.
|
|
39
|
-
"@clef-sh/runtime": "0.1.
|
|
37
|
+
"@clef-sh/core": "0.1.22",
|
|
38
|
+
"@clef-sh/design": "0.1.2",
|
|
39
|
+
"@clef-sh/runtime": "0.1.25",
|
|
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
|
-
|
|
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 &&
|
|
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."
|