1import{N as q,r as k,d as z,u as G,A as W,o as Y,M as Q,c as o,b as a,e as i,h as v,j as V,F as E,n as R,aV as X,t as _,f as w,C as B,m as Z,E as D,s as r,g as F,v as O,_ as ee}from"./index-DZDB8Ktv.js";import{A as te}from"./arrow-left-mtBMlT8q.js";import{S as se}from"./shield-check-DJ5ByTTD.js";import{C as U}from"./circle-alert-DWvzG0qW.js";import{S as J}from"./save-Bp9sFRwC.js";import{C as ne}from"./check-BO2tBXAg.js";import"./mermaid/mermaid-C4Hqjsq-.js";import"./mermaid/d3-transition-D0vnb3LM.js";import"./mermaid/d3-timer-DdKHrDhs.js";import"./mermaid/d3-dispatch-kxCwF96_.js";import"./mermaid/d3-interpolate-KWMiVRAA.js";import"./mermaid/d3-color-amxIadob.js";import"./mermaid/d3-selection-BigU4G0v.js";import"./mermaid/d3-ease-DRPgKoYJ.js";import"./mermaid/d3-zoom-DBMJLqLL.js";import"./mermaid/ts-dedent-Bhqu_tAV.js";import"./vendor-dompurify-DedTAGkB.js";import"./mermaid/dayjs-CFLtobiY.js";import"./mermaid/d3-shape-uG6-ZGac.js";import"./mermaid/d3-path-CimkQT29.js";import"./mermaid/dagre-d3-es-CTYKOaVK.js";import"./mermaid/cytoscape-CtNdOvlO.js";import"./mermaid/cytoscape-cose-bilkent-DPBxdiQK.js";import"./mermaid/braintree-sanitize-url-CwNcbkkA.js";import"./mermaid/khroma-CpvsyfFH.js";import"./mermaid/mermaid-js-parser-B1ZV-Txg.js";import"./mermaid/d3-scale-BxiPRRlv.js";import"./mermaid/internmap-BkD7Hj8s.js";import"./mermaid/d3-array-CcF9N2B-.js";import"./mermaid/d3-format-DT7eUs_k.js";import"./mermaid/d3-time-format-BdgCNa1_.js";import"./mermaid/d3-time-Cv4lxCBJ.js";import"./mermaid/d3-axis-DSWTncID.js";import"./mermaid/d3-sankey-CgO6js3K.js";import"./mermaid/d3-scale-chromatic-CvOaoHrI.js";import"./mermaid/cytoscape-fcose-D2_BIFVk.js";import"./mermaid/roughjs-CycZv-lV.js";import"./mermaid/d3-hierarchy-CvYzv8kf.js";import"./mermaid/iconify-utils-AvPxgwwt.js";
1import"./mermaid/chevrotain-D10r72ei.js";const ae=/^\d+\.\d+\.\d+$/;async function K(u,p){return fetch(u,{credentials:"include",headers:{"Content-Type":"application/json",...p?.headers??{}},...p})}async function M(u,p){if(u.ok)return;let y;try{const n=await u.json();y=n?.detail||n?.error}catch{}throw new Error(y||p)}const ie=q("platformSettings",()=>{const u=k([]),p=k(!1),y=k(null),n=k(null),b=k(null);function f(){n.value=null}function $(){b.value=null}function d(l){const x=u.value.findIndex(m=>m.key===l.key);x>=0?u.value.splice(x,1,l):u.value.push(l)}async function S(){p.value=!0,n.value=null;try{const l=await K("/api/admin/settings",{method:"GET"});if(l.status===403)throw new Error("Super admin access required");await M(l,"Failed to load platform settings");const x=await l.json();u.value=Array.isArray(x?.settings)?x.settings:[]}catch(l){n.value=l?.message??"Failed to load platform settings",u.value=[]}finally{p.value=!1}}async function h(l,x){y.value=l,n.value=null,b.value=null;try{const m=await K(`/api/admin/settings/${encodeURIComponent(l)}`,{method:"PATCH",body:JSON.stringify({value:x})});await M(m,"Failed to update setting");const C=await m.json();return C?.setting&&d(C.setting),b.value=l,!0}catch(m){return n.value=m?.message??"Failed to update setting",!1}finally{y.value=null}}return{settings:u,loading:p,saving:y,error:n,successKey:b,clearError:f,clearSuccess:$,fetchAll:S,update:h}}),oe={class:"h-screen bg-page overflow-y-auto"},re={class:"bg-terminal border-b border-border flex items-center px-4 gap-3 shrink-0 sticky top-0 z-10 settings-header"},de={class:"flex items-center gap-2"},le={class:"max-w-4xl mx-auto px-6 py-6 space-y-6"},ce={key:0,class:"flex items-center gap-2 rounded-lg border border-red-500/30 bg-red-500/10 px-4 py-3 text-sm text-red-400","data-testid":"platform-settings-forbidden"},ue={class:"flex items-start justify-between gap-4"},me=["disabled"],pe={key:0,class:"flex items-start gap-2 rounded-lg border border-red-500/30 bg-red-500/10 px-4 py-3 text-sm text-red-400","data-testid":"platform-settings-error"},xe={class:"flex-1"},ge={class:"bg-terminal border border-border rounded-xl overflow-hidden","data-testid":"platform-settings-table"},fe={key:0,class:"px-4 py-6 text-sm text-text-muted text-center"},ye={key:1,class:"px-4 py-6 text-sm text-text-muted text-center"},be={key:2,class:"divide-y divide-border"},he=["data-testid"],ve={class:"flex items-start justify-between gap-4"},ke={class:"flex-1 min-w-0"},_e={class:"flex items-center gap-2"},Se={class:"text-sm font-semibold text-text-dark"},we={class:"text-[10px] text-text-faint"},Ce={class:"text-[11px] text-text-muted mt-0.5 max-w-prose"},Ve=["disabled","data-testid","aria-checked","onClick"],$e={key:1,class:"flex flex-col gap-1.5 shrink-0 items-end"},Te={class:"flex items-center gap-2"},Ne=["data-testid"],Ae=["data-testid"],Ee=["onUpdate:modelValue","data-testid","disabled","onInput"],Re=["disabled","data-testid","onClick"],De=["onClick"],Le={key:2,class:"flex items-center gap-2 shrink-0"},Ie=["onUpdate:modelValue","min","max","data-testid","disabled","onInput"],je=["disabled","data-testid","onClick"],Be=["onClick"],Fe={class:"flex items-center gap-3 text-[10px] text-text-faint"},Oe={key:0},Ue={key:1,class:"flex items-center gap-1 text-green-400"},Je={key:2,class:"text-accent"},Ke={key:1,class:"bg-terminal border border-border rounded-xl overflow-hidden"},Me={class:"w-full text-xs"},Pe={class:"px-4 py-2 font-mono text-text-dark"},He={class:"px-4 py-2 font-mono text-text-muted"},qe=z({__name:"PlatformSettingsView",setup(u){const p=Z(),y=G(),n=ie(),b={"the_link.latest_version":{kind:"text_version",label:"the-link latest version (update notifier)",description:"Leave empty to use the version the hub ships. Set a higher version to notify agents an update is available."},"contact_graph.admin_bypass_enabled":{kind:"boolean",label:"Admin bypass enabled",description:"When enabled, org admins and owners can send commands to any agent inside their org without an explicit contact-graph edge. Disable for strict graph-only enforcement."},"contact_graph.edge_cache_ttl_seconds":{kind:"integer_seconds",label:"Edge cache TTL",description:"Seconds to cache positive contact-graph edges in the in-memory / Redis hot path. Lower values = faster propagation of edge changes, higher values = lower DB load.",minimum:1,maximum:86400},"contact_graph.label_resolution_cache_ttl_seconds":{kind:"integer_seconds",label:"Label resolution cache TTL",description:"Seconds to cache the org-slug + agent-label â tokenId mapping used when routing messages by label.",minimum:1,maximum:86400},"contact_graph.negative_cache_ttl_seconds":{kind:"integer_seconds",label:"Negative cache TTL",description:'Seconds to remember "no edge exists" lookups to keep denied peers from hammering the DB.',minimum:1,maximum:3600},"messaging.max_content_length":{kind:"integer_chars",label:"Max message length",description:"Hard limit on a single agent message body, in characters. Requests above this are rejected with 413.",minimum:1,maximum:1048576},"messaging.retention_days":{kind:"integer_days",label:"Message retention window",description:"Days an agent message is retained before the scheduled cleanup sweep deletes it. Historical messages older than this are irrecoverable.",minimum:1,maximum:3650},"browser.annotation_retention_days":{kind:"integer_days",label:"Browser annotation retention window",description:"Days a browser annotation is kept before the hourly sweep deletes it (crops cascade). Historical annotations older than this are irrecoverable.",minimum:1,maximum:3650},"browser.session_purge_grace_days":{kind:"integer_days",label:"Browser session purge grace peri
1od",description:"Days after a browser session is expired or revoked before the hourly sweep deletes it, along with its grants, annotations and history. Guest sessions are unaffected â they are swept immediately on expiry.",minimum:0,maximum:365}};function f(s){return b[s]??null}const $=D(()=>y.isAdmin===!0),d=k({}),S=k(new Set),h=k(null);function l(s){const t={...d.value};for(const e of s)S.value.has(e.key)||(t[e.key]=x(e.value));d.value=t}function x(s){if(typeof s=="boolean"||typeof s=="number"||s===null||typeof s=="string")return s;try{return JSON.parse(JSON.stringify(s))}catch{return s}}function m(s){const t=n.settings.find(c=>c.key===s);if(!t)return!1;const e=d.value[s];return JSON.stringify(e)!==JSON.stringify(t.value)}function C(s){S.value.add(s)}W(()=>n.settings,s=>{l(s)},{deep:!0});async function L(){await n.fetchAll()}async function T(s){const t=f(s);if(!t)return;let e=d.value[s];if(t.kind==="text_version"){const g=typeof e=="string"?e.trim():"";if(g.length>32){n.error="Version must be at most 32 characters";return}if(g!==""&&!ae.test(g)){n.error="Version must be a plain version x.y.z (e.g. 3.18.2), or empty to clear";return}e=g}else if(t.kind!=="boolean"){if(typeof e=="string"){const g=e.trim();if(g===""){n.error="Value cannot be empty";return}const A=Number(g);if(!Number.isFinite(A)||!Number.isInteger(A)){n.error="Value must be a positive integer";return}e=A}if(typeof e=="number"){const g=t.minimum??1;if(e<g){n.error=`Value must be at least ${g}`;return}if(t.maximum!==void 0&&e>t.maximum){n.error=`Value must be at most ${t.maximum}`;return}}}await n.update(s,e)&&(S.value.delete(s),h.value&&clearTimeout(h.value),h.value=setTimeout(()=>{n.clearSuccess()},2e3))}async function P(s){const t=d.value[s];d.value[s]=!t,await T(s)}function I(s){const t=n.settings.find(e=>e.key===s);t&&(d.value[s]=x(t.value),S.value.delete(s),n.clearError())}function H(s){try{return new Date(s).toLocaleString()}catch{return s}}const N=D(()=>n.settings.filter(s=>b[s.key]).sort((s,t)=>s.key.localeCompare(t.key))),j=D(()=>n.settings.filter(s=>!b[s.key]));return Y(()=>{$.value&&L()}),Q(()=>{h.value&&(clearTimeout(h.value),h.value=null)}),(s,t)=>(r(),o("div",oe,[a("header",re,[a("button",{class:"flex items-center gap-1.5 px-2.5 py-1 rounded-md text-xs font-medium text-text-muted hover:bg-selection hover:text-text-dark transition-colors",onClick:t[0]||(t[0]=e=>i(p).push({name:"settings"}))},[v(i(te),{class:"w-3.5 h-3.5"}),t[2]||(t[2]=V(" Back to Settings ",-1))]),t[4]||(t[4]=a("div",{class:"w-px h-5 bg-border"},null,-1)),a("div",de,[v(i(se),{class:"w-5 h-5 text-accent"}),t[3]||(t[3]=a("h1",{class:"text-sm font-semibold text-text-dark"},"Platform Settings",-1))])]),a("div",le,[$.value?(r(),o(E,{key:1},[a("div",ue,[t[7]||(t[7]=a("div",null,[a("h2",{class:"text-base font-semibold text-text-dark"}," Runtime tuning knobs "),a("p",{class:"text-xs text-text-muted max-w-prose mt-1"}," Changes take effect across every hub-server instance within a few seconds via pub/sub invalidation. Every write is audited. ")],-1)),a("button",{class:"flex items-center gap-1.5 px-2.5 py-1.5 rounded-md text-xs font-medium text-text-muted hover:bg-selection hover:text-text-dark transition-colors",disabled:i(n).loading,onClick:L},[v(i(X),{class:R(["w-3.5 h-3.5",{"animate-spin":i(n).loading}])},null,8,["class"]),t[6]||(t[6]=V(" Refresh ",-1))],8,me)]),i(n).error?(r(),o("div",pe,[v(i(U),{class:"w-4 h-4 shrink-0 mt-0.5"}),a("div",xe,_(i(n).error),1),a("button",{class:"text-xs underline",onClick:t[1]||(t[1]=(...e)=>i(n).clearError&&i(n).clearError(...e))}," Dismiss ")])):w("",!0),a("div",ge,[i(n).loading&&N.value.length===0?(r(),o("div",fe," Loading⦠")):N.value.length===0?(r(),o("div",ye," No platform settings found. Run database migrations to seed them. ")):(r(),o("div",be,[(r(!0),o(E,null,B(N.value,e=>(r(),o("div",{key:e.key,class:"px-5 py-4 space-y-2","data-testid":`setting-row-${e.key}`},[a("div",ve,[a("div",ke,[a("div",_e,[a("h3",Se,_(f(e.key)?.label??e.key),1),a("code",we,_(e.key),1)]),a("p",Ce,_(f(e.key)?.description),1)]),f(e.key)?.kind==="boolean"?(r(),o("button",{key:0,t
1ype:"button",class:R(["relative inline-flex items-center h-6 w-11 shrink-0 rounded-full transition-colors",d.value[e.key]?"bg-accent":"bg-selection border border-border"]),disabled:i(n).saving===e.key,"data-testid":`setting-toggle-${e.key}`,"aria-checked":!!d.value[e.key],role:"switch",onClick:c=>P(e.key)},[a("span",{class:R(["inline-block h-4 w-4 rounded-full bg-white shadow transform transition-transform",d.value[e.key]?"translate-x-6":"translate-x-1"])},null,2)],10,Ve)):f(e.key)?.kind==="text_version"?(r(),o("div",$e,[a("div",Te,[d.value[e.key]&&String(d.value[e.key]).trim()!==""?(r(),o("span",{key:0,class:"text-[10px] font-medium px-1.5 py-0.5 rounded bg-accent/20 text-accent border border-accent/30","data-testid":`setting-override-badge-${e.key}`}," Override active ",8,Ne)):(r(),o("span",{key:1,class:"text-[10px] text-text-faint","data-testid":`setting-empty-badge-${e.key}`}," Using shipped version ",8,Ae)),F(a("input",{"onUpdate:modelValue":c=>d.value[e.key]=c,type:"text",placeholder:"e.g. 3.18.2",maxlength:"32",class:"w-36 px-2 py-1 rounded-md border border-border bg-page text-xs text-text-dark font-mono focus:outline-none focus:ring-2 focus:ring-accent/40","data-testid":`setting-input-${e.key}`,disabled:i(n).saving===e.key,onInput:c=>C(e.key)},null,40,Ee),[[O,d.value[e.key]]]),a("button",{type:"button",class:"flex items-center gap-1 px-2.5 py-1 rounded-md text-xs font-medium border border-border text-text-muted hover:bg-selection hover:text-text-dark transition-colors disabled:opacity-50 disabled:cursor-not-allowed",disabled:!m(e.key)||i(n).saving===e.key,"data-testid":`setting-save-${e.key}`,onClick:c=>T(e.key)},[v(i(J),{class:"w-3 h-3"}),t[8]||(t[8]=V(" Save ",-1))],8,Re),m(e.key)?(r(),o("button",{key:2,type:"button",class:"text-[11px] text-text-faint hover:text-text-dark underline",onClick:c=>I(e.key)}," Revert ",8,De)):w("",!0)])])):(r(),o("div",Le,[F(a("input",{"onUpdate:modelValue":c=>d.value[e.key]=c,type:"number",step:"1",min:f(e.key)?.minimum,max:f(e.key)?.maximum,class:"w-32 px-2 py-1 rounded-md border border-border bg-page text-xs text-text-dark focus:outline-none focus:ring-2 focus:ring-accent/40","data-testid":`setting-input-${e.key}`,disabled:i(n).saving===e.key,onInput:c=>C(e.key)},null,40,Ie),[[O,d.value[e.key]]]),a("button",{type:"button",class:"flex items-center gap-1 px-2.5 py-1 rounded-md text-xs font-medium border border-border text-text-muted hover:bg-selection hover:text-text-dark transition-colors disabled:opacity-50 disabled:cursor-not-allowed",disabled:!m(e.key)||i(n).saving===e.key,"data-testid":`setting-save-${e.key}`,onClick:c=>T(e.key)},[v(i(J),{class:"w-3 h-3"}),t[9]||(t[9]=V(" Save ",-1))],8,je),m(e.key)?(r(),o("button",{key:0,type:"button",class:"text-[11px] text-text-faint hover:text-text-dark underline",onClick:c=>I(e.key)}," Revert ",8,Be)):w("",!0)]))]),a("div",Fe,[e.updatedAt?(r(),o("span",Oe," Last updated: "+_(H(e.updatedAt)),1)):w("",!0),i(n).successKey===e.key?(r(),o("span",Ue,[v(i(ne),{class:"w-3 h-3"}),t[10]||(t[10]=V(" Saved ",-1))])):i(n).saving===e.key?(r(),o("span",Je," Saving⦠")):w("",!0)])],8,he))),128))]))]),j.value.length>0?(r(),o("div",Ke,[t[12]||(t[12]=a("div",{class:"px-5 py-3 border-b border-border"},[a("h3",{class:"text-sm font-semibold text-text-dark"}," Unknown keys (read-only) "),a("p",{class:"text-[11px] text-text-muted mt-0.5"}," These rows exist in the database but are not in this client's known-key set. They are shown for visibility only. ")],-1)),a("table",Me,[t[11]||(t[11]=a("thead",{class:"text-text-faint"},[a("tr",{class:"border-b border-border"},[a("th",{class:"text-left px-4 py-2 font-medium"},"Key"),a("th",{class:"text-left px-4 py-2 font-medium"},"Value")])],-1)),a("tbody",null,[(r(!0),o(E,null,B(j.value,e=>(r(),o("tr",{key:e.key,class:"border-b border-border/50"},[a("td",Pe,_(e.key),1),a("td",He,_(JSON.stringify(e.value)),1)]))),128))])])])):w("",!0)],64)):(r(),o("div",ce,[v(i(U),{class:"w-4 h-4 shrink-0"}),t[5]||(t[5]=a("span",null,"Super-admin access is required to view platform settings.",-1))]))])]))}}),Dt=ee(qe,[["__scopeId","data-v-dd8ea3bd"]]);export{Dt as default};
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.