From a98fb01118e22cd5a5d075a8b30f5cb615a0e4ff Mon Sep 17 00:00:00 2001 From: mrorigo Date: Sat, 3 Oct 2026 18:00:27 +0200 Subject: [PATCH] fix(ui): consolidate HTML escaping into one helper that escapes single quotes The UI carried eleven copies of `escapeHtml`. Ten of them replaced `&`, `<`, `>` and `"` but not `'`, and every one of those ten is interpolated into a single-quoted JavaScript string literal inside an HTML attribute, for example: onclick="window.app.deleteAlias('tool', '${escapeHtml(alias)}')" A value containing `'` closes that JavaScript string literal early, so the remainder of the value is parsed as code. HTML escaping was applied and was nonetheless insufficient, because it does not neutralise the quoting character of the context the value actually lands in. An alias, profile name, server id, task id, or capability key containing `'); window.app.someMethod('` therefore executed arbitrary script in the control-plane origin, where it could call any `window.app` method including the approval and policy functions. `components/audit.ts` already had a correct copy that escaped the single quote. Move that implementation to `ui/src/escape.ts` and import it from all eleven modules, deleting the ten unsound copies. --- ui/dist/bundle.js | 1022 +++++++++++++++--------------- ui/dist/index.html | 1024 +++++++++++++++---------------- ui/src/components/aliases.ts | 5 +- ui/src/components/approvals.ts | 5 +- ui/src/components/audit.ts | 11 +- ui/src/components/overview.ts | 5 +- ui/src/components/playground.ts | 9 +- ui/src/components/policy.ts | 5 +- ui/src/components/profiles.ts | 5 +- ui/src/components/secrets.ts | 5 +- ui/src/components/servers.ts | 5 +- ui/src/components/tasks.ts | 5 +- ui/src/escape.ts | 21 + ui/src/main.ts | 5 +- 14 files changed, 1055 insertions(+), 1077 deletions(-) create mode 100644 ui/src/escape.ts diff --git a/ui/dist/bundle.js b/ui/dist/bundle.js index 61e0e86..f453bc6 100644 --- a/ui/dist/bundle.js +++ b/ui/dist/bundle.js @@ -1,4 +1,4 @@ -class ae{state={configPath:"mcp_servers.json",config:{mcpServers:{}},serverStatuses:{},circuitBreakers:[],clients:[],clientFilterCategory:"all",clientSearchQuery:"",secrets:[],clientsCollapsed:!1,capabilities:[],capabilitiesHiddenByPolicy:0,resources:[],resourcesHiddenByPolicy:0,prompts:[],promptsHiddenByPolicy:0,catalogEvents:[],tasks:[],selectedTaskId:null,taskFilterStatus:"all",approvals:[],auditEvents:[],auditTotal:0,auditFilters:{search:"",status:"all",eventType:"all",serverId:"all",limit:25,offset:0},auditSelectedEvent:null,auditStats:null,auditVerification:null,selectedCapabilityId:null,selectedResourceId:null,selectedPromptId:null,playgroundMode:"tools",playgroundArgs:{},isExecuting:!1,playgroundAsyncTask:!1,activeRequestId:null,isBatchModalOpen:!1,batchSteps:[{id:"step_1",capability_id:"",argsJson:"{}",continue_on_error:!1},{id:"step_2",capability_id:"",argsJson:"{}",continue_on_error:!0}],activeTab:"overview",activeProfile:null,eventLogs:[],executionResult:null,resourceReadResult:null,promptGetResult:null,metrics:{totalCatalogRequests:0,totalEtagHits:0,totalToolCalls:0,totalToolDurationUs:0}};listeners=[];getState(){return this.state}setState(e){this.state={...this.state,...e},this.listeners.forEach((t)=>t(this.state))}subscribe(e){return this.listeners.push(e),()=>{this.listeners=this.listeners.filter((t)=>t!==e)}}addEventLog(e,t,a,o){let r=[{time:new Date().toLocaleTimeString(),method:e,target:t,status:a,latency:o},...this.state.eventLogs].slice(0,50);this.setState({eventLogs:r})}}var c=new ae;class ne{baseUrl;constructor(e=""){this.baseUrl=e}async getConfig(){return(await fetch(`${this.baseUrl}/v1/config`)).json()}async listCapabilities(e){let t={};if(e)t["X-Warmplane-Profile"]=e;return(await fetch(`${this.baseUrl}/v1/capabilities`,{headers:t})).json()}async listResources(e){let t={};if(e)t["X-Warmplane-Profile"]=e;return(await fetch(`${this.baseUrl}/v1/resources`,{headers:t})).json()}async readResource(e,t){let a=performance.now(),o={"Content-Type":"application/json"};if(t)o["X-Warmplane-Profile"]=t;let n=await fetch(`${this.baseUrl}/v1/resources/read`,{method:"POST",headers:o,body:JSON.stringify(e)}),r=performance.now()-a,s=await n.json();return{status:n.status,durationMs:r,data:s}}async listPrompts(e){let t={};if(e)t["X-Warmplane-Profile"]=e;return(await fetch(`${this.baseUrl}/v1/prompts`,{headers:t})).json()}async getPrompt(e,t){let a=performance.now(),o={"Content-Type":"application/json"};if(t)o["X-Warmplane-Profile"]=t;let n=await fetch(`${this.baseUrl}/v1/prompts/get`,{method:"POST",headers:o,body:JSON.stringify(e)}),r=performance.now()-a,s=await n.json();return{status:n.status,durationMs:r,data:s}}async getCatalogEvents(e){let t=e?`?after=${encodeURIComponent(e)}`:"";return(await fetch(`${this.baseUrl}/v1/catalog/events${t}`)).json()}async callCapability(e,t){let a=performance.now(),o={"Content-Type":"application/json"};if(t)o["X-Warmplane-Profile"]=t;let n=await fetch(`${this.baseUrl}/v1/tools/call`,{method:"POST",headers:o,body:JSON.stringify(e)}),r=performance.now()-a,s=await n.json();return{status:n.status,durationMs:r,data:s}}async batchCallCapabilities(e,t){let a=performance.now(),o={"Content-Type":"application/json"};if(t)o["X-Warmplane-Profile"]=t;let n=await fetch(`${this.baseUrl}/v1/tools/batch_call`,{method:"POST",headers:o,body:JSON.stringify({steps:e})}),r=performance.now()-a,s=await n.json();return{status:n.status,durationMs:r,data:s}}async cancelOperation(e){return(await fetch(`${this.baseUrl}/v1/operations/${encodeURIComponent(e)}/cancel`,{method:"POST"})).json()}async completeArgument(e){return(await fetch(`${this.baseUrl}/v1/completion/complete`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(e)})).json()}async upsertServer(e,t){return(await fetch(`${this.baseUrl}/v1/config/servers`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({name:e,server:t})})).json()}async deleteServer(e){return(await fetch(`${this.baseUrl}/v1/config/servers/${encodeURIComponent(e)}`,{method:"DELETE"})).json()}async restartServer(e){return(await fetch(`${this.baseUrl}/v1/config/servers/${encodeURIComponent(e)}/restart`,{method:"POST"})).json()}async upsertProfile(e,t,a,o){return(await fetch(`${this.baseUrl}/v1/config/profiles`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({name:e,servers:t,description:a,policy:o})})).json()}async deleteProfile(e){return(await fetch(`${this.baseUrl}/v1/config/profiles/${encodeURIComponent(e)}`,{method:"DELETE"})).json()}async getEcosystemSources(){return(await fetch(`${this.baseUrl}/v1/config/ecosystem`)).json()}async importConfig(e,t=!1){return(await fetch(`${this.baseUrl}/v1/config/import`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({source_path:e,overwrite:t})})).json()}async savePolicy(e){let t={allow:e.allow||[],deny:e.deny||[],redactKeys:e.redact_keys||e.redactKeys||[],requireApproval:e.require_approval||e.requireApproval||[],approvalTimeoutSecs:e.approvalTimeoutSecs||e.approval_timeout_secs||300,webhook:e.webhook};return(await fetch(`${this.baseUrl}/v1/config/policy`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(t)})).json()}async listTasks(){return(await fetch(`${this.baseUrl}/v1/tasks`)).json()}async getTask(e){return(await fetch(`${this.baseUrl}/v1/tasks/${encodeURIComponent(e)}`)).json()}async updateTask(e,t){return(await fetch(`${this.baseUrl}/v1/tasks/${encodeURIComponent(e)}/update`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({inputResponses:t})})).json()}async cancelTask(e,t){return(await fetch(`${this.baseUrl}/v1/tasks/${encodeURIComponent(e)}/cancel`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({reason:t})})).json()}async listApprovals(){return(await fetch(`${this.baseUrl}/v1/approvals`)).json()}async approveTicket(e,t,a){return(await fetch(`${this.baseUrl}/v1/approvals/${encodeURIComponent(e)}/approve`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({operator:t,modified_args:a})})).json()}async rejectTicket(e,t,a){return(await fetch(`${this.baseUrl}/v1/approvals/${encodeURIComponent(e)}/reject`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({operator:t,reason:a})})).json()}async updateAlias(e,t,a,o,n,r){return(await fetch(`${this.baseUrl}/v1/config/alias`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({kind:e,alias:t,target:a,summary:o,description:n,passthrough:r})})).json()}async reloadConfig(){return(await fetch(`${this.baseUrl}/v1/config/reload`,{method:"POST"})).json()}async listAuditEvents(e){let t=new URLSearchParams;if(e?.actor_id)t.set("actor_id",e.actor_id);if(e?.server_id&&e.server_id!=="all")t.set("server_id",e.server_id);if(e?.capability_id)t.set("capability_id",e.capability_id);if(e?.event_type&&e.event_type!=="all")t.set("event_type",e.event_type);if(e?.status&&e.status!=="all")t.set("status",e.status);if(e?.trace_id)t.set("trace_id",e.trace_id);if(e?.request_id)t.set("request_id",e.request_id);if(e?.search)t.set("search",e.search);if(e?.limit)t.set("limit",String(e.limit));if(e?.offset!==void 0)t.set("offset",String(e.offset));let a=t.toString();return(await fetch(`${this.baseUrl}/v1/audit/events${a?`?${a}`:""}`)).json()}getAuditExportUrl(e,t="csv"){let a=new URLSearchParams;if(a.set("format",t),e?.actor_id)a.set("actor_id",e.actor_id);if(e?.server_id&&e.server_id!=="all")a.set("server_id",e.server_id);if(e?.capability_id)a.set("capability_id",e.capability_id);if(e?.event_type&&e.event_type!=="all")a.set("event_type",e.event_type);if(e?.status&&e.status!=="all")a.set("status",e.status);if(e?.trace_id)a.set("trace_id",e.trace_id);if(e?.request_id)a.set("request_id",e.request_id);if(e?.search)a.set("search",e.search);return`${this.baseUrl}/v1/audit/export?${a.toString()}`}async verifyAuditChain(){return(await fetch(`${this.baseUrl}/v1/audit/verify`)).json()}async getAuditStats(){return(await fetch(`${this.baseUrl}/v1/audit/stats`)).json()}async getClients(){return(await fetch(`${this.baseUrl}/v1/clients`)).json()}async attachClient(e,t,a="stdio",o){return(await fetch(`${this.baseUrl}/v1/clients/${encodeURIComponent(e)}/attach`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({profile:t||void 0,transport:a,http_url:o||void 0})})).json()}async detachClient(e){return(await fetch(`${this.baseUrl}/v1/clients/${encodeURIComponent(e)}/detach`,{method:"POST",headers:{"Content-Type":"application/json"}})).json()}async testWebhook(e,t){return(await fetch(`${this.baseUrl}/v1/webhooks/test`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({url:e||void 0,format:t||void 0})})).json()}async getSecrets(){return(await fetch(`${this.baseUrl}/v1/secrets`)).json()}async saveSecret(e,t,a){return(await fetch(`${this.baseUrl}/v1/secrets`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({key:e,value:t,service:a})})).json()}async deleteSecret(e){return(await fetch(`${this.baseUrl}/v1/secrets/${encodeURIComponent(e)}`,{method:"DELETE"})).json()}}var h=new ne;function oe(){let e=c.getState(),t=e.config.mcpServers||{},a=Object.keys(t),o=a.length,n="";if(a.length===0)n=` +function i(e){if(e===null||e===void 0)return"";return String(e).replace(/&/g,"&").replace(//g,">").replace(/"/g,""").replace(/'/g,"'")}class G{state={configPath:"mcp_servers.json",config:{mcpServers:{}},serverStatuses:{},circuitBreakers:[],clients:[],clientFilterCategory:"all",clientSearchQuery:"",secrets:[],clientsCollapsed:!1,capabilities:[],capabilitiesHiddenByPolicy:0,resources:[],resourcesHiddenByPolicy:0,prompts:[],promptsHiddenByPolicy:0,catalogEvents:[],tasks:[],selectedTaskId:null,taskFilterStatus:"all",approvals:[],auditEvents:[],auditTotal:0,auditFilters:{search:"",status:"all",eventType:"all",serverId:"all",limit:25,offset:0},auditSelectedEvent:null,auditStats:null,auditVerification:null,selectedCapabilityId:null,selectedResourceId:null,selectedPromptId:null,playgroundMode:"tools",playgroundArgs:{},isExecuting:!1,playgroundAsyncTask:!1,activeRequestId:null,isBatchModalOpen:!1,batchSteps:[{id:"step_1",capability_id:"",argsJson:"{}",continue_on_error:!1},{id:"step_2",capability_id:"",argsJson:"{}",continue_on_error:!0}],activeTab:"overview",activeProfile:null,eventLogs:[],executionResult:null,resourceReadResult:null,promptGetResult:null,metrics:{totalCatalogRequests:0,totalEtagHits:0,totalToolCalls:0,totalToolDurationUs:0}};listeners=[];getState(){return this.state}setState(e){this.state={...this.state,...e},this.listeners.forEach((t)=>t(this.state))}subscribe(e){return this.listeners.push(e),()=>{this.listeners=this.listeners.filter((t)=>t!==e)}}addEventLog(e,t,a,n){let r=[{time:new Date().toLocaleTimeString(),method:e,target:t,status:a,latency:n},...this.state.eventLogs].slice(0,50);this.setState({eventLogs:r})}}var p=new G;class Q{baseUrl;constructor(e=""){this.baseUrl=e}async getConfig(){return(await fetch(`${this.baseUrl}/v1/config`)).json()}async listCapabilities(e){let t={};if(e)t["X-Warmplane-Profile"]=e;return(await fetch(`${this.baseUrl}/v1/capabilities`,{headers:t})).json()}async listResources(e){let t={};if(e)t["X-Warmplane-Profile"]=e;return(await fetch(`${this.baseUrl}/v1/resources`,{headers:t})).json()}async readResource(e,t){let a=performance.now(),n={"Content-Type":"application/json"};if(t)n["X-Warmplane-Profile"]=t;let o=await fetch(`${this.baseUrl}/v1/resources/read`,{method:"POST",headers:n,body:JSON.stringify(e)}),r=performance.now()-a,s=await o.json();return{status:o.status,durationMs:r,data:s}}async listPrompts(e){let t={};if(e)t["X-Warmplane-Profile"]=e;return(await fetch(`${this.baseUrl}/v1/prompts`,{headers:t})).json()}async getPrompt(e,t){let a=performance.now(),n={"Content-Type":"application/json"};if(t)n["X-Warmplane-Profile"]=t;let o=await fetch(`${this.baseUrl}/v1/prompts/get`,{method:"POST",headers:n,body:JSON.stringify(e)}),r=performance.now()-a,s=await o.json();return{status:o.status,durationMs:r,data:s}}async getCatalogEvents(e){let t=e?`?after=${encodeURIComponent(e)}`:"";return(await fetch(`${this.baseUrl}/v1/catalog/events${t}`)).json()}async callCapability(e,t){let a=performance.now(),n={"Content-Type":"application/json"};if(t)n["X-Warmplane-Profile"]=t;let o=await fetch(`${this.baseUrl}/v1/tools/call`,{method:"POST",headers:n,body:JSON.stringify(e)}),r=performance.now()-a,s=await o.json();return{status:o.status,durationMs:r,data:s}}async batchCallCapabilities(e,t){let a=performance.now(),n={"Content-Type":"application/json"};if(t)n["X-Warmplane-Profile"]=t;let o=await fetch(`${this.baseUrl}/v1/tools/batch_call`,{method:"POST",headers:n,body:JSON.stringify({steps:e})}),r=performance.now()-a,s=await o.json();return{status:o.status,durationMs:r,data:s}}async cancelOperation(e){return(await fetch(`${this.baseUrl}/v1/operations/${encodeURIComponent(e)}/cancel`,{method:"POST"})).json()}async completeArgument(e){return(await fetch(`${this.baseUrl}/v1/completion/complete`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(e)})).json()}async upsertServer(e,t){return(await fetch(`${this.baseUrl}/v1/config/servers`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({name:e,server:t})})).json()}async deleteServer(e){return(await fetch(`${this.baseUrl}/v1/config/servers/${encodeURIComponent(e)}`,{method:"DELETE"})).json()}async restartServer(e){return(await fetch(`${this.baseUrl}/v1/config/servers/${encodeURIComponent(e)}/restart`,{method:"POST"})).json()}async upsertProfile(e,t,a,n){return(await fetch(`${this.baseUrl}/v1/config/profiles`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({name:e,servers:t,description:a,policy:n})})).json()}async deleteProfile(e){return(await fetch(`${this.baseUrl}/v1/config/profiles/${encodeURIComponent(e)}`,{method:"DELETE"})).json()}async getEcosystemSources(){return(await fetch(`${this.baseUrl}/v1/config/ecosystem`)).json()}async importConfig(e,t=!1){return(await fetch(`${this.baseUrl}/v1/config/import`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({source_path:e,overwrite:t})})).json()}async savePolicy(e){let t={allow:e.allow||[],deny:e.deny||[],redactKeys:e.redact_keys||e.redactKeys||[],requireApproval:e.require_approval||e.requireApproval||[],approvalTimeoutSecs:e.approvalTimeoutSecs||e.approval_timeout_secs||300,webhook:e.webhook};return(await fetch(`${this.baseUrl}/v1/config/policy`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(t)})).json()}async listTasks(){return(await fetch(`${this.baseUrl}/v1/tasks`)).json()}async getTask(e){return(await fetch(`${this.baseUrl}/v1/tasks/${encodeURIComponent(e)}`)).json()}async updateTask(e,t){return(await fetch(`${this.baseUrl}/v1/tasks/${encodeURIComponent(e)}/update`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({inputResponses:t})})).json()}async cancelTask(e,t){return(await fetch(`${this.baseUrl}/v1/tasks/${encodeURIComponent(e)}/cancel`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({reason:t})})).json()}async listApprovals(){return(await fetch(`${this.baseUrl}/v1/approvals`)).json()}async approveTicket(e,t,a){return(await fetch(`${this.baseUrl}/v1/approvals/${encodeURIComponent(e)}/approve`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({operator:t,modified_args:a})})).json()}async rejectTicket(e,t,a){return(await fetch(`${this.baseUrl}/v1/approvals/${encodeURIComponent(e)}/reject`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({operator:t,reason:a})})).json()}async updateAlias(e,t,a,n,o,r){return(await fetch(`${this.baseUrl}/v1/config/alias`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({kind:e,alias:t,target:a,summary:n,description:o,passthrough:r})})).json()}async reloadConfig(){return(await fetch(`${this.baseUrl}/v1/config/reload`,{method:"POST"})).json()}async listAuditEvents(e){let t=new URLSearchParams;if(e?.actor_id)t.set("actor_id",e.actor_id);if(e?.server_id&&e.server_id!=="all")t.set("server_id",e.server_id);if(e?.capability_id)t.set("capability_id",e.capability_id);if(e?.event_type&&e.event_type!=="all")t.set("event_type",e.event_type);if(e?.status&&e.status!=="all")t.set("status",e.status);if(e?.trace_id)t.set("trace_id",e.trace_id);if(e?.request_id)t.set("request_id",e.request_id);if(e?.search)t.set("search",e.search);if(e?.limit)t.set("limit",String(e.limit));if(e?.offset!==void 0)t.set("offset",String(e.offset));let a=t.toString();return(await fetch(`${this.baseUrl}/v1/audit/events${a?`?${a}`:""}`)).json()}getAuditExportUrl(e,t="csv"){let a=new URLSearchParams;if(a.set("format",t),e?.actor_id)a.set("actor_id",e.actor_id);if(e?.server_id&&e.server_id!=="all")a.set("server_id",e.server_id);if(e?.capability_id)a.set("capability_id",e.capability_id);if(e?.event_type&&e.event_type!=="all")a.set("event_type",e.event_type);if(e?.status&&e.status!=="all")a.set("status",e.status);if(e?.trace_id)a.set("trace_id",e.trace_id);if(e?.request_id)a.set("request_id",e.request_id);if(e?.search)a.set("search",e.search);return`${this.baseUrl}/v1/audit/export?${a.toString()}`}async verifyAuditChain(){return(await fetch(`${this.baseUrl}/v1/audit/verify`)).json()}async getAuditStats(){return(await fetch(`${this.baseUrl}/v1/audit/stats`)).json()}async getClients(){return(await fetch(`${this.baseUrl}/v1/clients`)).json()}async attachClient(e,t,a="stdio",n){return(await fetch(`${this.baseUrl}/v1/clients/${encodeURIComponent(e)}/attach`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({profile:t||void 0,transport:a,http_url:n||void 0})})).json()}async detachClient(e){return(await fetch(`${this.baseUrl}/v1/clients/${encodeURIComponent(e)}/detach`,{method:"POST",headers:{"Content-Type":"application/json"}})).json()}async testWebhook(e,t){return(await fetch(`${this.baseUrl}/v1/webhooks/test`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({url:e||void 0,format:t||void 0})})).json()}async getSecrets(){return(await fetch(`${this.baseUrl}/v1/secrets`)).json()}async saveSecret(e,t,a){return(await fetch(`${this.baseUrl}/v1/secrets`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({key:e,value:t,service:a})})).json()}async deleteSecret(e){return(await fetch(`${this.baseUrl}/v1/secrets/${encodeURIComponent(e)}`,{method:"DELETE"})).json()}}var h=new Q;function Y(){let e=p.getState(),t=e.config.mcpServers||{},a=Object.keys(t),n=a.length,o="";if(a.length===0)o=`
No Upstream MCP Servers Connected
Initialize connections by adding a server or syncing existing IDE configurations.
@@ -8,21 +8,21 @@ class ae{state={configPath:"mcp_servers.json",config:{mcpServers:{}},serverStatu
- `;else n=a.map((E)=>{let O=t[E],W=O.command?"stdio":"http / sse",K=O.command?`${O.command} ${(O.args||[]).join(" ")}`:O.url,F=e.serverStatuses[E]||{status:"connected",protocol_version:"2026-07-28"},G=F.status==="degraded",re=F.status==="error"||F.status==="disconnected",Z=G?"var(--amber-400)":re?"var(--red-400)":"var(--green-400)";return` + `;else o=a.map((S)=>{let P=t[S],q=P.command?"stdio":"http / sse",H=P.command?`${P.command} ${(P.args||[]).join(" ")}`:P.url,z=e.serverStatuses[S]||{status:"connected",protocol_version:"2026-07-28"},F=z.status==="degraded",V=z.status==="error"||z.status==="disconnected",U=F?"var(--amber-400)":V?"var(--red-400)":"var(--green-400)";return`
- - ${M(E)} + + ${i(S)} - ${W} + ${q}
-
- ${M(K||"")} +
+ ${i(H||"")}
- Status: ${M(F.status)} - Protocol: ${F.protocol_version} + Status: ${i(z.status)} + Protocol: ${z.protocol_version}
`}).join("");let r=e.eventLogs.length===0?` @@ -33,62 +33,62 @@ class ae{state={configPath:"mcp_servers.json",config:{mcpServers:{}},serverStatu CONNECTED 0.0ms
- `:e.eventLogs.map((E)=>` + `:e.eventLogs.map((S)=>`
- ${M(E.time)} - ${M(E.method)} - ${M(E.target)} - ${M(E.status)} - ${M(E.latency)} + ${i(S.time)} + ${i(S.method)} + ${i(S.target)} + ${i(S.status)} + ${i(S.latency)}
- `).join(""),s=e.metrics,i=s.totalCatalogRequests,l=s.totalEtagHits,u=i>0?`${(l/i*100).toFixed(1)}%`:"0.0%",p=i>0?`${l} of ${i} requests served via HTTP 304`:"Waiting for client requests",g=s.totalToolCalls,m=g>0?`${(s.totalToolDurationUs/g/1000).toFixed(1)}ms`:"0.0ms",d=g>0?`${g} tool executions processed`:"Local worker task queues warm",y=Object.keys(e.config.capabilityAliases||{}).length+Object.keys(e.config.resourceAliases||{}).length+Object.keys(e.config.promptAliases||{}).length,b=y>0?`${y*18}B / call`:"0B",v=y>0?`${y} active facade aliases pruning prompt size`:"Configure aliases in Studio to reduce prompt size",x=e.tasks||[],A=x.filter((E)=>E.status==="input_required").length,R=x.filter((E)=>E.status==="working"||E.status==="input_required").length,T=e.clients||[],S=T.filter((E)=>E.is_attached).length,w=T.filter((E)=>E.config_exists&&!E.is_attached).length,z=e.clientsCollapsed,L=S>0?`⚡ ${S} Connected`:w>0?`○ ${w} Ready to Connect`:'No Apps Detected',N=Object.keys(e.config.profiles||{}),D=e.activeProfile,q=e.clientFilterCategory||"all",U=(e.clientSearchQuery||"").toLowerCase().trim(),V=T.filter((E)=>{if(U){let O=E.name.toLowerCase().includes(U),W=E.id.toLowerCase().includes(U),K=E.category.toLowerCase().includes(U);if(!O&&!W&&!K)return!1}if(q==="connected")return E.is_attached;if(q==="ready")return E.config_exists||E.app_installed;if(q==="ides")return E.category.toLowerCase().includes("ide")||E.category.toLowerCase().includes("extension");if(q==="agents")return E.category.toLowerCase().includes("agent")||E.category.toLowerCase().includes("cli")||E.category.toLowerCase().includes("platform");return!0}),te=V.length===0?'
No AI clients match current filter.
':V.map((E)=>{let{is_attached:O,config_exists:W,app_installed:K}=E,F="rgba(255, 255, 255, 0.2)",G="Not Found";if(O){F="var(--green-400)";let J=E.attached_transport==="http"?"HTTP":"stdio";G=E.attached_profile?`Connected · ${J} (${E.attached_profile})`:`Connected · ${J} (All Tools)`}else if(W)F="var(--amber-300)",G="Ready to Attach";else if(K)F="var(--cyan-400)",G="Installed";let re=N.map((J)=>` - - `).join(""),Z=O?``:W||K?` + `).join(""),s=e.metrics,l=s.totalCatalogRequests,d=s.totalEtagHits,g=l>0?`${(d/l*100).toFixed(1)}%`:"0.0%",u=l>0?`${d} of ${l} requests served via HTTP 304`:"Waiting for client requests",m=s.totalToolCalls,v=m>0?`${(s.totalToolDurationUs/m/1000).toFixed(1)}ms`:"0.0ms",c=m>0?`${m} tool executions processed`:"Local worker task queues warm",y=Object.keys(e.config.capabilityAliases||{}).length+Object.keys(e.config.resourceAliases||{}).length+Object.keys(e.config.promptAliases||{}).length,b=y>0?`${y*18}B / call`:"0B",f=y>0?`${y} active facade aliases pruning prompt size`:"Configure aliases in Studio to reduce prompt size",x=e.tasks||[],T=x.filter((S)=>S.status==="input_required").length,I=x.filter((S)=>S.status==="working"||S.status==="input_required").length,E=e.clients||[],k=E.filter((S)=>S.is_attached).length,w=E.filter((S)=>S.config_exists&&!S.is_attached).length,C=e.clientsCollapsed,A=k>0?`⚡ ${k} Connected`:w>0?`○ ${w} Ready to Connect`:'No Apps Detected',L=Object.keys(e.config.profiles||{}),M=e.activeProfile,R=e.clientFilterCategory||"all",B=(e.clientSearchQuery||"").toLowerCase().trim(),O=E.filter((S)=>{if(B){let P=S.name.toLowerCase().includes(B),q=S.id.toLowerCase().includes(B),H=S.category.toLowerCase().includes(B);if(!P&&!q&&!H)return!1}if(R==="connected")return S.is_attached;if(R==="ready")return S.config_exists||S.app_installed;if(R==="ides")return S.category.toLowerCase().includes("ide")||S.category.toLowerCase().includes("extension");if(R==="agents")return S.category.toLowerCase().includes("agent")||S.category.toLowerCase().includes("cli")||S.category.toLowerCase().includes("platform");return!0}),J=O.length===0?'
No AI clients match current filter.
':O.map((S)=>{let{is_attached:P,config_exists:q,app_installed:H}=S,z="rgba(255, 255, 255, 0.2)",F="Not Found";if(P){z="var(--green-400)";let j=S.attached_transport==="http"?"HTTP":"stdio";F=S.attached_profile?`Connected · ${j} (${S.attached_profile})`:`Connected · ${j} (All Tools)`}else if(q)z="var(--amber-300)",F="Ready to Attach";else if(H)z="var(--cyan-400)",F="Installed";let V=L.map((j)=>` + + `).join(""),U=P?``:q||H?`
- ${N.length>0?` - + + ${V} `:""} - +
`:"";return`
- +
-
${M(E.name)}
-
${M(G)}
+
${i(S.name)}
+
${i(F)}
- ${Z} + ${U}
- `}).join(""),se=[{id:"all",label:"All"},{id:"ready",label:"Ready / Installed"},{id:"connected",label:"Connected"},{id:"ides",label:"IDEs"},{id:"agents",label:"Agents & CLIs"}].map((E)=>``).join(""),B=` + `}).join(""),W=[{id:"all",label:"All"},{id:"ready",label:"Ready / Installed"},{id:"connected",label:"Connected"},{id:"ides",label:"IDEs"},{id:"agents",label:"Agents & CLIs"}].map((S)=>``).join(""),_=`
⚡ 1-Click AI Client Integrations - ${L} + ${A}
- ${z?"▼ Show":"▲ Hide"} + ${C?"▼ Show":"▲ Hide"}
- ${!z?` + ${!C?`
- ${se} + ${W}
- +
- ${te} + ${J}
`:""} @@ -98,34 +98,34 @@ class ae{state={configPath:"mcp_servers.json",config:{mcpServers:{}},serverStatu
Token Savings Rate
${b}
-
${v}
+
${f}
ETag Cache Hit Rate
-
${u}
-
${p}
+
${g}
+
${u}
Tasks & HITL State
-
${A>0?`${A} Action Req`:`${R} Active`}
-
${A>0?"Awaiting Human-in-the-Loop decision":`${x.length} total registered tasks`}
+
${T>0?`${T} Action Req`:`${I} Active`}
+
${T>0?"Awaiting Human-in-the-Loop decision":`${x.length} total registered tasks`}
Connected Upstreams
-
${o} Active
-
${o>0?"Persistent worker task channels":"No active upstream servers"}
+
${n} Active
+
${n>0?"Persistent worker task channels":"No active upstream servers"}
- ${B} + ${_}
Connected Upstream Servers
- +
- ${n} + ${o}
@@ -143,9 +143,9 @@ class ae{state={configPath:"mcp_servers.json",config:{mcpServers:{}},serverStatu ${r}
- `}function M(e){return String(e||"").replace(/&/g,"&").replace(//g,">").replace(/"/g,""")}var X=[{id:"github",name:"GitHub",category:"devtools",description:"Explore repositories, issues, pull requests, branches, and commit histories.",badge:"Official / Stdio",command:"npx",defaultArgs:["-y","@modelcontextprotocol/server-github"],envFields:[{key:"GITHUB_PERSONAL_ACCESS_TOKEN",label:"GitHub Personal Access Token",placeholder:"ghp_...",required:!0,description:"Classic or fine-grained token with repo scope."}]},{id:"git",name:"Git (Local)",category:"devtools",description:"Read local Git repository status, diffs, log histories, and commit changes.",badge:"Official / uvx",command:"uvx",defaultArgs:["--with","mcp<2","mcp-server-git","--repository","."],argsPlaceholder:"--with mcp<2 mcp-server-git --repository /path/to/repo",envFields:[]},{id:"filesystem",name:"Filesystem",category:"devtools",description:"Secure, sandboxed access to local files and directories for AI workflows.",badge:"Official / Stdio",command:"npx",defaultArgs:["-y","@modelcontextprotocol/server-filesystem","."],argsPlaceholder:"-y @modelcontextprotocol/server-filesystem /allowed/dir1 /allowed/dir2",envFields:[]},{id:"memory",name:"Memory Graph",category:"devtools",description:"Persistent knowledge-graph based memory for multi-turn agent learning.",badge:"Official / Stdio",command:"npx",defaultArgs:["-y","@modelcontextprotocol/server-memory"],envFields:[]},{id:"chrome-devtools",name:"Chrome DevTools",category:"devtools",description:"Inspect live DOM, execute scripts, read console logs, and capture network traces in Chrome.",badge:"Official / Stdio",command:"npx",defaultArgs:["-y","@modelcontextprotocol/server-puppeteer"],envFields:[]},{id:"sentry",name:"Sentry",category:"devtools",description:"Query production error events, stack traces, and issue frequencies directly from Sentry.",badge:"uvx / Telemetry",command:"uvx",defaultArgs:["--with","mcp<2","--with","httpx","mcp-server-sentry","--auth-token","sntrys_token"],argsPlaceholder:"--with mcp<2 --with httpx mcp-server-sentry --auth-token YOUR_SENTRY_TOKEN",envFields:[{key:"SENTRY_AUTH_TOKEN",label:"Sentry Auth Token",placeholder:"sntrys_...",required:!0}]},{id:"playwright",name:"Playwright Browser",category:"browser",description:"Headless / headed browser automation for scraping, form filling, and UI interaction.",badge:"Popular #1 / npx",command:"npx",defaultArgs:["-y","@executeautomation/playwright-mcp-server"],envFields:[]},{id:"puppeteer",name:"Puppeteer",category:"browser",description:"Official browser automation server for web page scraping and screenshot capture.",badge:"Official / Stdio",command:"npx",defaultArgs:["-y","@modelcontextprotocol/server-puppeteer"],envFields:[]},{id:"brave-search",name:"Brave Search",category:"browser",description:"Real-time privacy-preserving web search and local point-of-interest query engine.",badge:"Official / Search",command:"npx",defaultArgs:["-y","@modelcontextprotocol/server-brave-search"],envFields:[{key:"BRAVE_API_KEY",label:"Brave Search API Key",placeholder:"BSA...",required:!0}]},{id:"tavily",name:"Tavily Search",category:"browser",description:"AI-optimized web search engine structured specifically for LLM context injection.",badge:"Community / Stdio",command:"npx",defaultArgs:["-y","tavily-mcp"],envFields:[{key:"TAVILY_API_KEY",label:"Tavily API Key",placeholder:"tvly-...",required:!0}]},{id:"fetch",name:"Fetch / Web Markdown",category:"browser",description:"Download web pages, strip clutter, and convert raw HTML to clean markdown text.",badge:"Official / uvx",command:"uvx",defaultArgs:["mcp-server-fetch"],envFields:[]},{id:"postgres",name:"PostgreSQL",category:"database",description:"Read schemas, inspect tables, and execute SQL queries against PostgreSQL databases.",badge:"Official / Database",command:"npx",defaultArgs:["-y","@modelcontextprotocol/server-postgres","postgresql://user:pass@localhost:5432/mydb"],argsPlaceholder:"-y @modelcontextprotocol/server-postgres postgresql://user:pass@localhost:5432/dbname",envFields:[]},{id:"sqlite",name:"SQLite",category:"database",description:"Local embedded SQLite query runner and schema inspector.",badge:"Official / uvx",command:"uvx",defaultArgs:["--with","mcp<2","mcp-server-sqlite","--db-path","./app.db"],argsPlaceholder:"--with mcp<2 mcp-server-sqlite --db-path /path/to/database.sqlite",envFields:[]},{id:"supabase",name:"Supabase",category:"database",description:"Query database tables, manage auth policies, and inspect storage in Supabase.",badge:"Official / Stdio",command:"npx",defaultArgs:["-y","@supabase/mcp-server-supabase@latest"],envFields:[{key:"SUPABASE_ACCESS_TOKEN",label:"Supabase Personal Access Token",placeholder:"sbp_...",required:!0},{key:"SUPABASE_PROJECT_REF",label:"Supabase Project Reference ID",placeholder:"abcdefghijklmnop",required:!1}]},{id:"redis",name:"Redis",category:"database",description:"Inspect cached keys, hash sets, lists, TTLs, and pub/sub channels in Redis.",badge:"Official / Key-Value",command:"npx",defaultArgs:["-y","@modelcontextprotocol/server-redis","redis://localhost:6379"],argsPlaceholder:"-y @modelcontextprotocol/server-redis redis://localhost:6379",envFields:[]},{id:"s3",name:"AWS S3 / Cloud Storage",category:"database",description:"Browse S3 buckets, fetch object metadata, and download files from cloud storage.",badge:"Community / Stdio",command:"npx",defaultArgs:["-y","@geunoh/s3-mcp-server"],argsPlaceholder:"-y @geunoh/s3-mcp-server",envFields:[{key:"AWS_ACCESS_KEY_ID",label:"AWS Access Key ID",placeholder:"AKIA...",required:!0},{key:"AWS_SECRET_ACCESS_KEY",label:"AWS Secret Access Key",placeholder:"...",required:!0},{key:"AWS_REGION",label:"AWS Region",placeholder:"us-east-1",required:!1}]},{id:"linear",name:"Linear",category:"productivity",description:"Search, create, and triage Linear issues, cycles, teams, and project roadmaps.",badge:"Productivity / Stdio",command:"npx",defaultArgs:["-y","mcp-linear"],envFields:[{key:"LINEAR_API_KEY",label:"Linear API Key",placeholder:"lin_api_...",required:!0}]},{id:"slack",name:"Slack",category:"productivity",description:"Read channels, post messages, inspect threads, and search team discussions.",badge:"Official / Stdio",command:"npx",defaultArgs:["-y","@modelcontextprotocol/server-slack"],envFields:[{key:"SLACK_BOT_TOKEN",label:"Slack Bot User Token",placeholder:"xoxb-...",required:!0},{key:"SLACK_TEAM_ID",label:"Slack Team ID",placeholder:"T01234567",required:!0}]},{id:"notion",name:"Notion",category:"productivity",description:"Search Notion workspace pages, read nested blocks, and query database entries.",badge:"Official / Stdio",command:"npx",defaultArgs:["-y","@notionhq/notion-mcp-server"],envFields:[{key:"NOTION_TOKEN",label:"Notion Internal Integration Token",placeholder:"secret_...",required:!0}]},{id:"jira",name:"Jira / Atlassian",category:"productivity",description:"Manage Jira issues, search JQL, read sprint statuses, and inspect boards.",badge:"uvx / Atlassian",command:"uvx",defaultArgs:["--with","mcp<2","mcp-server-jira","--jira-base-url","https://your-domain.atlassian.net"],argsPlaceholder:"--with mcp<2 mcp-server-jira --jira-base-url https://org.atlassian.net",envFields:[{key:"JIRA_TOKEN",label:"Atlassian API Token",placeholder:"ATATT3...",required:!0}]},{id:"google-drive",name:"Google Drive",category:"productivity",description:"Search, list, and read documents, spreadsheets, and drive files.",badge:"Community / Stdio",command:"npx",defaultArgs:["-y","@piotr-agier/google-drive-mcp"],envFields:[{key:"GOOGLE_APPLICATION_CREDENTIALS",label:"Google Credentials JSON Path",placeholder:"/path/to/credentials.json",required:!0}]},{id:"docker",name:"Docker",category:"cloud",description:"Inspect running containers, tail container logs, list images, and manage compose services.",badge:"uvx / DevOps",command:"uvx",defaultArgs:["mcp-server-docker"],envFields:[]},{id:"kubernetes",name:"Kubernetes (K8s)",category:"cloud",description:"Query cluster pods, services, deployment status, and inspect Kubernetes logs.",badge:"Popular / Stdio",command:"npx",defaultArgs:["-y","@strowk/mcp-k8s"],envFields:[{key:"KUBECONFIG",label:"Kubeconfig File Path (Optional)",placeholder:"~/.kube/config",required:!1}]},{id:"cloudflare",name:"Cloudflare",category:"cloud",description:"Manage Cloudflare Workers, KV namespaces, D1 databases, Vectorize indexes, and DNS.",badge:"Official / Cloudflare",command:"npx",defaultArgs:["-y","@cloudflare/mcp-server-cloudflare","run","dummy_account_id"],argsPlaceholder:"-y @cloudflare/mcp-server-cloudflare run YOUR_ACCOUNT_ID",envFields:[{key:"CLOUDFLARE_API_TOKEN",label:"Cloudflare API Token",placeholder:"...",required:!0},{key:"CLOUDFLARE_ACCOUNT_ID",label:"Cloudflare Account ID",placeholder:"...",required:!0}]},{id:"terraform",name:"Terraform",category:"cloud",description:"Inspect Terraform state files, resource dependency graphs, and plan previews.",badge:"Community / IaC",command:"npx",defaultArgs:["-y","@mseep/terraform-mcp-server"],envFields:[]}];function Q(e,t,a){let o=X.find((r)=>r.id.toLowerCase()===e.toLowerCase());if(o)return o;let n=`${t||""} ${(a||[]).join(" ")}`.toLowerCase();return X.find((r)=>{let s=`${r.command} ${r.defaultArgs.join(" ")}`.toLowerCase();if(n.includes(r.id.toLowerCase()))return!0;if(r.command&&n.includes(r.command.toLowerCase())&&r.defaultArgs.some((i)=>n.includes(i.toLowerCase())))return!0;return!1})}function ie(){let e=c.getState(),t=e.config.mcpServers||{},a=Object.keys(t),o=e.activeProfile,n=o?e.config.profiles?.[o]:void 0,r=!!n,s=n?.servers||[],i="";if(a.length===0)i=` + `}var D=[{id:"github",name:"GitHub",category:"devtools",description:"Explore repositories, issues, pull requests, branches, and commit histories.",badge:"Official / Stdio",command:"npx",defaultArgs:["-y","@modelcontextprotocol/server-github"],envFields:[{key:"GITHUB_PERSONAL_ACCESS_TOKEN",label:"GitHub Personal Access Token",placeholder:"ghp_...",required:!0,description:"Classic or fine-grained token with repo scope."}]},{id:"git",name:"Git (Local)",category:"devtools",description:"Read local Git repository status, diffs, log histories, and commit changes.",badge:"Official / uvx",command:"uvx",defaultArgs:["--with","mcp<2","mcp-server-git","--repository","."],argsPlaceholder:"--with mcp<2 mcp-server-git --repository /path/to/repo",envFields:[]},{id:"filesystem",name:"Filesystem",category:"devtools",description:"Secure, sandboxed access to local files and directories for AI workflows.",badge:"Official / Stdio",command:"npx",defaultArgs:["-y","@modelcontextprotocol/server-filesystem","."],argsPlaceholder:"-y @modelcontextprotocol/server-filesystem /allowed/dir1 /allowed/dir2",envFields:[]},{id:"memory",name:"Memory Graph",category:"devtools",description:"Persistent knowledge-graph based memory for multi-turn agent learning.",badge:"Official / Stdio",command:"npx",defaultArgs:["-y","@modelcontextprotocol/server-memory"],envFields:[]},{id:"chrome-devtools",name:"Chrome DevTools",category:"devtools",description:"Inspect live DOM, execute scripts, read console logs, and capture network traces in Chrome.",badge:"Official / Stdio",command:"npx",defaultArgs:["-y","@modelcontextprotocol/server-puppeteer"],envFields:[]},{id:"sentry",name:"Sentry",category:"devtools",description:"Query production error events, stack traces, and issue frequencies directly from Sentry.",badge:"uvx / Telemetry",command:"uvx",defaultArgs:["--with","mcp<2","--with","httpx","mcp-server-sentry","--auth-token","sntrys_token"],argsPlaceholder:"--with mcp<2 --with httpx mcp-server-sentry --auth-token YOUR_SENTRY_TOKEN",envFields:[{key:"SENTRY_AUTH_TOKEN",label:"Sentry Auth Token",placeholder:"sntrys_...",required:!0}]},{id:"playwright",name:"Playwright Browser",category:"browser",description:"Headless / headed browser automation for scraping, form filling, and UI interaction.",badge:"Popular #1 / npx",command:"npx",defaultArgs:["-y","@executeautomation/playwright-mcp-server"],envFields:[]},{id:"puppeteer",name:"Puppeteer",category:"browser",description:"Official browser automation server for web page scraping and screenshot capture.",badge:"Official / Stdio",command:"npx",defaultArgs:["-y","@modelcontextprotocol/server-puppeteer"],envFields:[]},{id:"brave-search",name:"Brave Search",category:"browser",description:"Real-time privacy-preserving web search and local point-of-interest query engine.",badge:"Official / Search",command:"npx",defaultArgs:["-y","@modelcontextprotocol/server-brave-search"],envFields:[{key:"BRAVE_API_KEY",label:"Brave Search API Key",placeholder:"BSA...",required:!0}]},{id:"tavily",name:"Tavily Search",category:"browser",description:"AI-optimized web search engine structured specifically for LLM context injection.",badge:"Community / Stdio",command:"npx",defaultArgs:["-y","tavily-mcp"],envFields:[{key:"TAVILY_API_KEY",label:"Tavily API Key",placeholder:"tvly-...",required:!0}]},{id:"fetch",name:"Fetch / Web Markdown",category:"browser",description:"Download web pages, strip clutter, and convert raw HTML to clean markdown text.",badge:"Official / uvx",command:"uvx",defaultArgs:["mcp-server-fetch"],envFields:[]},{id:"postgres",name:"PostgreSQL",category:"database",description:"Read schemas, inspect tables, and execute SQL queries against PostgreSQL databases.",badge:"Official / Database",command:"npx",defaultArgs:["-y","@modelcontextprotocol/server-postgres","postgresql://user:pass@localhost:5432/mydb"],argsPlaceholder:"-y @modelcontextprotocol/server-postgres postgresql://user:pass@localhost:5432/dbname",envFields:[]},{id:"sqlite",name:"SQLite",category:"database",description:"Local embedded SQLite query runner and schema inspector.",badge:"Official / uvx",command:"uvx",defaultArgs:["--with","mcp<2","mcp-server-sqlite","--db-path","./app.db"],argsPlaceholder:"--with mcp<2 mcp-server-sqlite --db-path /path/to/database.sqlite",envFields:[]},{id:"supabase",name:"Supabase",category:"database",description:"Query database tables, manage auth policies, and inspect storage in Supabase.",badge:"Official / Stdio",command:"npx",defaultArgs:["-y","@supabase/mcp-server-supabase@latest"],envFields:[{key:"SUPABASE_ACCESS_TOKEN",label:"Supabase Personal Access Token",placeholder:"sbp_...",required:!0},{key:"SUPABASE_PROJECT_REF",label:"Supabase Project Reference ID",placeholder:"abcdefghijklmnop",required:!1}]},{id:"redis",name:"Redis",category:"database",description:"Inspect cached keys, hash sets, lists, TTLs, and pub/sub channels in Redis.",badge:"Official / Key-Value",command:"npx",defaultArgs:["-y","@modelcontextprotocol/server-redis","redis://localhost:6379"],argsPlaceholder:"-y @modelcontextprotocol/server-redis redis://localhost:6379",envFields:[]},{id:"s3",name:"AWS S3 / Cloud Storage",category:"database",description:"Browse S3 buckets, fetch object metadata, and download files from cloud storage.",badge:"Community / Stdio",command:"npx",defaultArgs:["-y","@geunoh/s3-mcp-server"],argsPlaceholder:"-y @geunoh/s3-mcp-server",envFields:[{key:"AWS_ACCESS_KEY_ID",label:"AWS Access Key ID",placeholder:"AKIA...",required:!0},{key:"AWS_SECRET_ACCESS_KEY",label:"AWS Secret Access Key",placeholder:"...",required:!0},{key:"AWS_REGION",label:"AWS Region",placeholder:"us-east-1",required:!1}]},{id:"linear",name:"Linear",category:"productivity",description:"Search, create, and triage Linear issues, cycles, teams, and project roadmaps.",badge:"Productivity / Stdio",command:"npx",defaultArgs:["-y","mcp-linear"],envFields:[{key:"LINEAR_API_KEY",label:"Linear API Key",placeholder:"lin_api_...",required:!0}]},{id:"slack",name:"Slack",category:"productivity",description:"Read channels, post messages, inspect threads, and search team discussions.",badge:"Official / Stdio",command:"npx",defaultArgs:["-y","@modelcontextprotocol/server-slack"],envFields:[{key:"SLACK_BOT_TOKEN",label:"Slack Bot User Token",placeholder:"xoxb-...",required:!0},{key:"SLACK_TEAM_ID",label:"Slack Team ID",placeholder:"T01234567",required:!0}]},{id:"notion",name:"Notion",category:"productivity",description:"Search Notion workspace pages, read nested blocks, and query database entries.",badge:"Official / Stdio",command:"npx",defaultArgs:["-y","@notionhq/notion-mcp-server"],envFields:[{key:"NOTION_TOKEN",label:"Notion Internal Integration Token",placeholder:"secret_...",required:!0}]},{id:"jira",name:"Jira / Atlassian",category:"productivity",description:"Manage Jira issues, search JQL, read sprint statuses, and inspect boards.",badge:"uvx / Atlassian",command:"uvx",defaultArgs:["--with","mcp<2","mcp-server-jira","--jira-base-url","https://your-domain.atlassian.net"],argsPlaceholder:"--with mcp<2 mcp-server-jira --jira-base-url https://org.atlassian.net",envFields:[{key:"JIRA_TOKEN",label:"Atlassian API Token",placeholder:"ATATT3...",required:!0}]},{id:"google-drive",name:"Google Drive",category:"productivity",description:"Search, list, and read documents, spreadsheets, and drive files.",badge:"Community / Stdio",command:"npx",defaultArgs:["-y","@piotr-agier/google-drive-mcp"],envFields:[{key:"GOOGLE_APPLICATION_CREDENTIALS",label:"Google Credentials JSON Path",placeholder:"/path/to/credentials.json",required:!0}]},{id:"docker",name:"Docker",category:"cloud",description:"Inspect running containers, tail container logs, list images, and manage compose services.",badge:"uvx / DevOps",command:"uvx",defaultArgs:["mcp-server-docker"],envFields:[]},{id:"kubernetes",name:"Kubernetes (K8s)",category:"cloud",description:"Query cluster pods, services, deployment status, and inspect Kubernetes logs.",badge:"Popular / Stdio",command:"npx",defaultArgs:["-y","@strowk/mcp-k8s"],envFields:[{key:"KUBECONFIG",label:"Kubeconfig File Path (Optional)",placeholder:"~/.kube/config",required:!1}]},{id:"cloudflare",name:"Cloudflare",category:"cloud",description:"Manage Cloudflare Workers, KV namespaces, D1 databases, Vectorize indexes, and DNS.",badge:"Official / Cloudflare",command:"npx",defaultArgs:["-y","@cloudflare/mcp-server-cloudflare","run","dummy_account_id"],argsPlaceholder:"-y @cloudflare/mcp-server-cloudflare run YOUR_ACCOUNT_ID",envFields:[{key:"CLOUDFLARE_API_TOKEN",label:"Cloudflare API Token",placeholder:"...",required:!0},{key:"CLOUDFLARE_ACCOUNT_ID",label:"Cloudflare Account ID",placeholder:"...",required:!0}]},{id:"terraform",name:"Terraform",category:"cloud",description:"Inspect Terraform state files, resource dependency graphs, and plan previews.",badge:"Community / IaC",command:"npx",defaultArgs:["-y","@mseep/terraform-mcp-server"],envFields:[]}];function N(e,t,a){let n=D.find((r)=>r.id.toLowerCase()===e.toLowerCase());if(n)return n;let o=`${t||""} ${(a||[]).join(" ")}`.toLowerCase();return D.find((r)=>{let s=`${r.command} ${r.defaultArgs.join(" ")}`.toLowerCase();if(o.includes(r.id.toLowerCase()))return!0;if(r.command&&o.includes(r.command.toLowerCase())&&r.defaultArgs.some((l)=>o.includes(l.toLowerCase())))return!0;return!1})}function X(){let e=p.getState(),t=e.config.mcpServers||{},a=Object.keys(t),n=e.activeProfile,o=n?e.config.profiles?.[n]:void 0,r=!!o,s=o?.servers||[],l="";if(a.length===0)l=`
-
No Servers Configured in ${I(e.configPath)}
+
No Servers Configured in ${i(e.configPath)}

Warmplane bridges local tools and remote MCP servers into one unified facade. Add your first server or import existing configs from Claude Desktop or Cursor.

@@ -155,56 +155,56 @@ class ae{state={configPath:"mcp_servers.json",config:{mcpServers:{}},serverStatu
- `;else i=a.map((u)=>{let p=t[u],g=p.command?"stdio":"http / sse",m=p.command?`${p.command} ${(p.args||[]).join(" ")}`:p.url,d=e.serverStatuses[u]||{status:"connected",protocol_version:"2026-07-28"},y=!r||s.includes(u),b=Q(u,p.command,p.args),v=Object.keys(p.env||{}),x=(b?.envFields||[]).filter((B)=>B.required&&!v.includes(B.key)),A=p.env?Object.entries(p.env).map(([B,E])=>{if(E.startsWith("keychain://"))return`\uD83D\uDD12 ${I(B)} (Keychain)`;if(E.startsWith("op://"))return`\uD83D\uDD12 ${I(B)} (1Password)`;if(E.startsWith("env://"))return`\uD83D\uDD12 ${I(B)} (Env)`;return`${I(B)}=***`}):[];for(let B of x)A.push(`⚠️ Missing ${I(B.key)}`);let R=A.length>0?A.join(" "):"None",T=(e.circuitBreakers||[]).find((B)=>B.server_id===u),S='Circuit: CLOSED';if(T){if(T.state==="open")S=`Circuit: OPEN (${T.consecutive_failures} failures)`;else if(T.state==="half_open")S=`Circuit: HALF-OPEN (${T.consecutive_successes} probe)`}let w=p.resilience||e.config.resilience,z=w?`FT: ${w.failureThreshold||3} · Cooldown: ${(w.cooldownMs||30000)/1000}s · AutoRestart: ${w.autoRestart!==!1?"ON":"OFF"}`:"Default Resilience",L=x.length>0,N=d.status==="degraded"||L,D=d.status==="error"||d.status==="disconnected",q=L?"var(--amber-400)":N?"var(--amber-400)":D?"var(--red-400)":"var(--green-400)",U=L?`Status: ${I(d.status)} (Missing Keys)`:`Status: ${I(d.status)}`,V=(N||D)&&(d.error||L)?` + `;else l=a.map((g)=>{let u=t[g],m=u.command?"stdio":"http / sse",v=u.command?`${u.command} ${(u.args||[]).join(" ")}`:u.url,c=e.serverStatuses[g]||{status:"connected",protocol_version:"2026-07-28"},y=!r||s.includes(g),b=N(g,u.command,u.args),f=Object.keys(u.env||{}),x=(b?.envFields||[]).filter((_)=>_.required&&!f.includes(_.key)),T=u.env?Object.entries(u.env).map(([_,S])=>{if(S.startsWith("keychain://"))return`\uD83D\uDD12 ${i(_)} (Keychain)`;if(S.startsWith("op://"))return`\uD83D\uDD12 ${i(_)} (1Password)`;if(S.startsWith("env://"))return`\uD83D\uDD12 ${i(_)} (Env)`;return`${i(_)}=***`}):[];for(let _ of x)T.push(`⚠️ Missing ${i(_.key)}`);let I=T.length>0?T.join(" "):"None",E=(e.circuitBreakers||[]).find((_)=>_.server_id===g),k='Circuit: CLOSED';if(E){if(E.state==="open")k=`Circuit: OPEN (${E.consecutive_failures} failures)`;else if(E.state==="half_open")k=`Circuit: HALF-OPEN (${E.consecutive_successes} probe)`}let w=u.resilience||e.config.resilience,C=w?`FT: ${w.failureThreshold||3} · Cooldown: ${(w.cooldownMs||30000)/1000}s · AutoRestart: ${w.autoRestart!==!1?"ON":"OFF"}`:"Default Resilience",A=x.length>0,L=c.status==="degraded"||A,M=c.status==="error"||c.status==="disconnected",R=A?"var(--amber-400)":L?"var(--amber-400)":M?"var(--red-400)":"var(--green-400)",B=A?`Status: ${i(c.status)} (Missing Keys)`:`Status: ${i(c.status)}`,O=(L||M)&&(c.error||A)?`
- ⚠️ Diagnostics: ${I(d.error||`Missing required environment variable(s): ${x.map((B)=>B.key).join(", ")}`)} + ⚠️ Diagnostics: ${i(c.error||`Missing required environment variable(s): ${x.map((_)=>_.key).join(", ")}`)}
- +
- `:"",te=r?y?` + `:"",J=r?y?` ✔ IN CONSTELLATION - + `:` - \uD83D\uDEAB EXCLUDED FROM PROFILE: ${I(o)} - + \uD83D\uDEAB EXCLUDED FROM PROFILE: ${i(n)} + `:"";return` -
+
- - ${I(u)} - ${g} - ${U} - Protocol: ${d.protocol_version} - ${S} - ${te} + + ${i(g)} + ${m} + ${B} + Protocol: ${c.protocol_version} + ${k} + ${J}
- ${p.command?"Command: ":"URL: "}${I(m||"")} + ${u.command?"Command: ":"URL: "}${i(v||"")}
- \uD83D\uDEE1️ ${I(z)} - ${p.env&&Object.keys(p.env).length>0?`Env: ${R}`:""} + \uD83D\uDEE1️ ${i(C)} + ${u.env&&Object.keys(u.env).length>0?`Env: ${I}`:""}
- - - - + + + +
- ${V} + ${O}
- `}).join("");let l=r?` + `}).join("");let d=r?`
\uD83C\uDF0C
- Active Profile Constellation: ${I(o)} + Active Profile Constellation: ${i(n)} ${s.length} of ${a.length} servers included
@@ -221,52 +221,52 @@ class ae{state={configPath:"mcp_servers.json",config:{mcpServers:{}},serverStatu
Configured MCP Upstream Servers
-
Active configuration file: ${I(e.configPath)}
+
Active configuration file: ${i(e.configPath)}
- ${l} + ${d} - ${i} + ${l} - ${ye()} - `}function ye(){let e=c.getState(),t=e.clients||[],a=Object.keys(e.config.profiles||{}),o=e.clientsCollapsed,n=e.clientFilterCategory||"all",r=(e.clientSearchQuery||"").toLowerCase().trim();if(t.length===0)return"";let s=t.filter((d)=>d.is_attached).length,i=t.filter((d)=>d.config_exists&&!d.is_attached).length,l=t.filter((d)=>d.category.toLowerCase().includes("ide")||d.category.toLowerCase().includes("extension")).length,u=t.filter((d)=>d.category.toLowerCase().includes("agent")||d.category.toLowerCase().includes("cli")||d.category.toLowerCase().includes("platform")).length,p=t.filter((d)=>{if(r){let y=d.name.toLowerCase().includes(r),b=d.id.toLowerCase().includes(r),v=d.category.toLowerCase().includes(r),x=d.config_path.toLowerCase().includes(r);if(!y&&!b&&!v&&!x)return!1}if(n==="connected")return d.is_attached;if(n==="ready")return d.config_exists||d.app_installed;if(n==="ides")return d.category.toLowerCase().includes("ide")||d.category.toLowerCase().includes("extension");if(n==="agents")return d.category.toLowerCase().includes("agent")||d.category.toLowerCase().includes("cli")||d.category.toLowerCase().includes("platform");return!0}),g=[{id:"all",label:`All Ecosystems (${t.length})`},{id:"ready",label:`Ready / Installed (${i+s})`},{id:"connected",label:`⚡ Connected (${s})`},{id:"ides",label:`IDEs & Editors (${l})`},{id:"agents",label:`Agents & CLIs (${u})`}].map((d)=>` - - `).join(""),m=p.length===0?`
No AI clients match the filter "${I(r||n)}".
`:p.map((d)=>{let{is_attached:y,config_exists:b,app_installed:v}=d,x='Not Found';if(y){let S=d.attached_profile?` · ${d.attached_profile}`:"";x=`⚡ Connected · ${d.attached_transport==="http"?"HTTP":"stdio"}${I(S)}`}else if(b)x='○ Ready';else if(v)x='○ Installed';let A=e.activeProfile,R=a.map((S)=>` - - `).join(""),T=y?``:``;return` + `).join(""),v=u.length===0?`
No AI clients match the filter "${i(r||o)}".
`:u.map((c)=>{let{is_attached:y,config_exists:b,app_installed:f}=c,x='Not Found';if(y){let k=c.attached_profile?` · ${c.attached_profile}`:"";x=`⚡ Connected · ${c.attached_transport==="http"?"HTTP":"stdio"}${i(k)}`}else if(b)x='○ Ready';else if(f)x='○ Installed';let T=e.activeProfile,I=a.map((k)=>` + + `).join(""),E=y?``:``;return`
- ${I(d.name)} + ${i(c.name)}
-
${I(d.category)}
+
${i(c.category)}
${x}
-
- ${I(d.config_path)} +
+ ${i(c.config_path)}
${a.length>0&&!y?` - - ${R} + ${I} - `:`${d.other_servers_count>0?`${d.other_servers_count} other tools`:"Single facade"}`} + `:`${c.other_servers_count>0?`${c.other_servers_count} other tools`:"Single facade"}`}
- ${T} + ${E}
`}).join("");return` @@ -283,18 +283,18 @@ class ae{state={configPath:"mcp_servers.json",config:{mcpServers:{}},serverStatu
- ${o?"▼ Show":"▲ Hide"} + ${n?"▼ Show":"▲ Hide"}
- ${!o?` + ${!n?`
- ${g} + ${m}
- +
@@ -306,12 +306,12 @@ class ae{state={configPath:"mcp_servers.json",config:{mcpServers:{}},serverStatu Constellation Scope Action
- ${m} + ${v}
`:""}
- `}function I(e){return String(e||"").replace(/&/g,"&").replace(//g,">").replace(/"/g,""")}function le(){let e=c.getState(),t=e.playgroundMode||"tools",a=e.capabilities||[],o=e.resources||[],n=e.prompts||[],r=e.capabilitiesHiddenByPolicy||0,s=e.resourcesHiddenByPolicy||0,i=e.promptsHiddenByPolicy||0,l=e.activeProfile,p=!!(l?e.config.profiles?.[l]:void 0),g=` + `}function Z(){let e=p.getState(),t=e.playgroundMode||"tools",a=e.capabilities||[],n=e.resources||[],o=e.prompts||[],r=e.capabilitiesHiddenByPolicy||0,s=e.resourcesHiddenByPolicy||0,l=e.promptsHiddenByPolicy||0,d=e.activeProfile,u=!!(d?e.config.profiles?.[d]:void 0),m=`
${t==="tools"&&r>0?`
- \uD83D\uDEE1️ ${r} tool${r>1?"s":""} filtered ${p?`(Profile: ${k(l)})`:"by policy"} + \uD83D\uDEE1️ ${r} tool${r>1?"s":""} filtered ${u?`(Profile: ${i(d)})`:"by policy"} View Policy - ${p?`Server Hub`:""} + ${u?`Server Hub`:""}
`:t==="resources"&&s>0?`
- \uD83D\uDEE1️ ${s} resource${s>1?"s":""} filtered ${p?`(Profile: ${k(l)})`:"by policy"} + \uD83D\uDEE1️ ${s} resource${s>1?"s":""} filtered ${u?`(Profile: ${i(d)})`:"by policy"} View Policy - ${p?`Server Hub`:""} + ${u?`Server Hub`:""}
- `:t==="prompts"&&i>0?` + `:t==="prompts"&&l>0?`
- \uD83D\uDEE1️ ${i} prompt${i>1?"s":""} filtered ${p?`(Profile: ${k(l)})`:"by policy"} + \uD83D\uDEE1️ ${l} prompt${l>1?"s":""} filtered ${u?`(Profile: ${i(d)})`:"by policy"} View Policy - ${p?`Server Hub`:""} + ${u?`Server Hub`:""}
`:`
@@ -367,43 +367,43 @@ class ae{state={configPath:"mcp_servers.json",config:{mcpServers:{}},serverStatu
`;if(t==="resources")return` - ${g} - ${xe(e)} + ${m} + ${ce(e)} `;if(t==="prompts")return` - ${g} - ${he(e)} + ${m} + ${pe(e)} `;return` - ${g} - ${be(e)} - ${e.isBatchModalOpen?we(e):""} - `}function ee(e,t=!1){if(!e||!e.properties)return{};let a=e.properties||{},o=Array.isArray(e.required)?e.required:[],n={};for(let[r,s]of Object.entries(a)){let i=o.includes(r);if(t&&!i)continue;if(s.default!==void 0)n[r]=s.default;else if(Array.isArray(s.enum)&&s.enum.length>0)n[r]=s.enum[0];else if(s.examples&&Array.isArray(s.examples)&&s.examples.length>0)n[r]=s.examples[0];else if(s.example!==void 0)n[r]=s.example;else switch(s.type||"string"){case"string":n[r]=i?`sample_${r}`:"";break;case"number":case"integer":n[r]=0;break;case"boolean":n[r]=!0;break;case"array":n[r]=[];break;case"object":n[r]={};break;default:n[r]=`sample_${r}`}}return n}function be(e){let t=e.capabilities||[],a=e.selectedCapabilityId||(t.length>0?t[0].id:null),o=t.find((b)=>b.id===a),n=e.isExecutingCapability,r=e.capabilitiesHiddenByPolicy||0,s=e.activeProfile,i=!!(s&&e.config.profiles?.[s]),l="";if(t.length===0)l=` + ${m} + ${de(e)} + ${e.isBatchModalOpen?ue(e):""} + `}function K(e,t=!1){if(!e||!e.properties)return{};let a=e.properties||{},n=Array.isArray(e.required)?e.required:[],o={};for(let[r,s]of Object.entries(a)){let l=n.includes(r);if(t&&!l)continue;if(s.default!==void 0)o[r]=s.default;else if(Array.isArray(s.enum)&&s.enum.length>0)o[r]=s.enum[0];else if(s.examples&&Array.isArray(s.examples)&&s.examples.length>0)o[r]=s.examples[0];else if(s.example!==void 0)o[r]=s.example;else switch(s.type||"string"){case"string":o[r]=l?`sample_${r}`:"";break;case"number":case"integer":o[r]=0;break;case"boolean":o[r]=!0;break;case"array":o[r]=[];break;case"object":o[r]={};break;default:o[r]=`sample_${r}`}}return o}function de(e){let t=e.capabilities||[],a=e.selectedCapabilityId||(t.length>0?t[0].id:null),n=t.find((b)=>b.id===a),o=e.isExecutingCapability,r=e.capabilitiesHiddenByPolicy||0,s=e.activeProfile,l=!!(s&&e.config.profiles?.[s]),d="";if(t.length===0)d=`
No tools or capabilities discovered from connected servers.
- `;else l=t.map((b)=>` -
+ `;else d=t.map((b)=>` +
- ${k(b.id)} - ${k(b.mode||"read")} + ${i(b.id)} + ${i(b.mode||"read")}
-
server: ${k(b.server||"local")}
+
server: ${i(b.server||"local")}
- `).join("");let u=o?.input_schema,p=u?.properties||{},g=Array.isArray(u?.required)?u.required:[],m=Object.entries(p),d="";if(m.length>0)d=` + `).join("");let g=n?.input_schema,u=g?.properties||{},m=Array.isArray(g?.required)?g.required:[],v=Object.entries(u),c="";if(v.length>0)c=`
Fields: - ${m.map(([b,v])=>{let x=g.includes(b),A=v.type||(v.enum?"enum":"any"),R=x?"rgba(239, 68, 68, 0.15)":"rgba(148, 163, 184, 0.1)",T=x?"var(--red-400)":"var(--text-muted)",S=x?"rgba(239, 68, 68, 0.3)":"var(--border)",w=v.description?` - ${v.description}`:"";return` + ${v.map(([b,f])=>{let x=m.includes(b),T=f.type||(f.enum?"enum":"any"),I=x?"rgba(239, 68, 68, 0.15)":"rgba(148, 163, 184, 0.1)",E=x?"var(--red-400)":"var(--text-muted)",k=x?"rgba(239, 68, 68, 0.3)":"var(--border)",w=f.description?` - ${f.description}`:"";return` `}).join("")}
- `;let y="{}";if(a&&e.playgroundArgs&&e.playgroundArgs[a]!==void 0)y=e.playgroundArgs[a];else{let b=ee(u,!1);y=JSON.stringify(b,null,2)}return` + `;let y="{}";if(a&&e.playgroundArgs&&e.playgroundArgs[a]!==void 0)y=e.playgroundArgs[a];else{let b=K(g,!1);y=JSON.stringify(b,null,2)}return`
@@ -411,14 +411,14 @@ class ae{state={configPath:"mcp_servers.json",config:{mcpServers:{}},serverStatu
- ${l} + ${d}
${r>0?`
- \uD83D\uDEE1️ ${r} tool${r>1?"s":""} filtered ${i?`(${k(s)})`:""} + \uD83D\uDEE1️ ${r} tool${r>1?"s":""} filtered ${l?`(${i(s)})`:""}
Policy - ${i?`Servers`:""} + ${l?`Servers`:""}
`:""} @@ -429,15 +429,15 @@ class ae{state={configPath:"mcp_servers.json",config:{mcpServers:{}},serverStatu
- ${k(o?o.id:"No Capability Selected")} + ${i(n?n.id:"No Capability Selected")}
- ${k(o?o.summary||o.description:"Connect servers to inspect and execute tools")} + ${i(n?n.summary||n.description:"Connect servers to inspect and execute tools")}
- ${n?` + ${o?`
@@ -449,7 +449,7 @@ class ae{state={configPath:"mcp_servers.json",config:{mcpServers:{}},serverStatu
`:` - @@ -464,7 +464,7 @@ class ae{state={configPath:"mcp_servers.json",config:{mcpServers:{}},serverStatu
- ${g.length>0?` + ${m.length>0?` `:""} @@ -472,9 +472,9 @@ class ae{state={configPath:"mcp_servers.json",config:{mcpServers:{}},serverStatu
- ${d} + ${c} - +
@@ -515,13 +515,13 @@ class ae{state={configPath:"mcp_servers.json",config:{mcpServers:{}},serverStatu
- ${o&&o.input_schema?` + ${n&&n.input_schema?`
- ${m.length} field${m.length===1?"":"s"} (${g.length} required) + ${v.length} field${v.length===1?"":"s"} (${m.length} required)
-
${k(JSON.stringify(o.input_schema,null,2))}
+
${i(JSON.stringify(n.input_schema,null,2))}
`:""}
@@ -542,9 +542,9 @@ class ae{state={configPath:"mcp_servers.json",config:{mcpServers:{}},serverStatu
- ${k(e.executionResult.data?.task?.status||e.executionResult.data?.status||"TASK_CREATED").toUpperCase()} + ${i(e.executionResult.data?.task?.status||e.executionResult.data?.status||"TASK_CREATED").toUpperCase()} - ${k(e.executionResult.data?.task?.taskId||e.executionResult.data?.taskId||"")} + ${i(e.executionResult.data?.task?.taskId||e.executionResult.data?.taskId||"")}
Execution suspended for Human-in-the-Loop approval or async resolution. @@ -556,25 +556,25 @@ class ae{state={configPath:"mcp_servers.json",config:{mcpServers:{}},serverStatu
`:""} -
${e.executionResult?k(JSON.stringify(e.executionResult.data,null,2)):"// Response envelope output will be formatted here"}
+
${e.executionResult?i(JSON.stringify(e.executionResult.data,null,2)):"// Response envelope output will be formatted here"}
- `}function xe(e){let t=e.resources||[],a=e.resourcesHiddenByPolicy||0,o=e.selectedResourceId||(t.length>0?t[0].id:null),n=t.find((i)=>i.id===o),r=e.resourceReadResult,s="";if(t.length===0)s=` + `}function ce(e){let t=e.resources||[],a=e.resourcesHiddenByPolicy||0,n=e.selectedResourceId||(t.length>0?t[0].id:null),o=t.find((l)=>l.id===n),r=e.resourceReadResult,s="";if(t.length===0)s=`
No resources exposed by connected MCP servers.
- `;else s=t.map((i)=>{let l=i.id===o?"active":"",u=i.uri?i.uri.split(":")[0]:"res";return` -
+ `;else s=t.map((l)=>{let d=l.id===n?"active":"",g=l.uri?l.uri.split(":")[0]:"res";return` +
- ${k(i.name||i.id)} - ${k(u)} + ${i(l.name||l.id)} + ${i(g)}
-
${k(i.uri)}
+
${i(l.uri)}
- server: ${k(i.server||"local")} - ${k(i.mime_type||"text/plain")} + server: ${i(l.server||"local")} + ${i(l.mime_type||"text/plain")}
`}).join("");return` @@ -600,13 +600,13 @@ class ae{state={configPath:"mcp_servers.json",config:{mcpServers:{}},serverStatu
- ${k(n?n.name||n.id:"No Resource Selected")} + ${i(o?o.name||o.id:"No Resource Selected")}
- ${k(n?n.uri:"Select a resource from the list to read live content")} + ${i(o?o.uri:"Select a resource from the list to read live content")}
- @@ -615,16 +615,16 @@ class ae{state={configPath:"mcp_servers.json",config:{mcpServers:{}},serverStatu
- ${n?` + ${o?`
Resource Metadata
-
Server: ${k(n.server)}
-
MIME Type: ${k(n.mime_type||"text/plain")}
+
Server: ${i(o.server)}
+
MIME Type: ${i(o.mime_type||"text/plain")}
- ${n.description?` + ${o.description?`
- ${k(n.description)} + ${i(o.description)}
`:""}
@@ -659,34 +659,34 @@ class ae{state={configPath:"mcp_servers.json",config:{mcpServers:{}},serverStatu ${r?`HTTP ${r.status} · ${r.durationMs.toFixed(1)}ms`:"READY"}
-
${r?k(JSON.stringify(r.data,null,2)):'// Click "Read Resource Content" to inspect live payload'}
+
${r?i(JSON.stringify(r.data,null,2)):'// Click "Read Resource Content" to inspect live payload'}
- `}function he(e){let t=e.prompts||[],a=e.promptsHiddenByPolicy||0,o=e.selectedPromptId||(t.length>0?t[0].id:null),n=t.find((l)=>l.id===o),r=e.promptGetResult,s="";if(t.length===0)s=` + `}function pe(e){let t=e.prompts||[],a=e.promptsHiddenByPolicy||0,n=e.selectedPromptId||(t.length>0?t[0].id:null),o=t.find((d)=>d.id===n),r=e.promptGetResult,s="";if(t.length===0)s=`
No prompt templates registered by connected MCP servers.
- `;else s=t.map((l)=>{let u=l.id===o?"active":"",p=l.arguments?l.arguments.length:0;return` -
+ `;else s=t.map((d)=>{let g=d.id===n?"active":"",u=d.arguments?d.arguments.length:0;return` +
- ${k(l.name||l.id)} - ${p} args + ${i(d.name||d.id)} + ${u} args
-
${k(l.description||l.title||"Prompt template")}
-
server: ${k(l.server||"local")}
+
${i(d.description||d.title||"Prompt template")}
+
server: ${i(d.server||"local")}
- `}).join("");let i="";if(n&&n.arguments&&n.arguments.length>0)i=n.arguments.map((l)=>` + `}).join("");let l="";if(o&&o.arguments&&o.arguments.length>0)l=o.arguments.map((d)=>`
- - ${l.required?'REQUIRED':'optional'} + + ${d.required?'REQUIRED':'optional'}
- ${l.description?`
${k(l.description)}
`:""} - + ${d.description?`
${i(d.description)}
`:""} +
- `).join("");else if(n)i=` + `).join("");else if(o)l=`
This prompt template does not require any input arguments.
@@ -713,13 +713,13 @@ class ae{state={configPath:"mcp_servers.json",config:{mcpServers:{}},serverStatu
- ${k(n?n.name||n.id:"No Prompt Selected")} + ${i(o?o.name||o.id:"No Prompt Selected")}
- ${k(n?n.description||n.title||"Bind variables and render messages":"Select a prompt from the list to test")} + ${i(o?o.description||o.title||"Bind variables and render messages":"Select a prompt from the list to test")}
- @@ -731,7 +731,7 @@ class ae{state={configPath:"mcp_servers.json",config:{mcpServers:{}},serverStatu
Template Arguments
- ${i} + ${l}
@@ -742,12 +742,12 @@ class ae{state={configPath:"mcp_servers.json",config:{mcpServers:{}},serverStatu ${r?`HTTP ${r.status} · ${r.durationMs.toFixed(1)}ms`:"READY"}
-
${r?k(JSON.stringify(r.data,null,2)):'// Click "Render Prompt Messages" to view resolved system/user messages'}
+
${r?i(JSON.stringify(r.data,null,2)):'// Click "Render Prompt Messages" to view resolved system/user messages'}
- `}function we(e){let t=e.capabilities||[],a=e.batchSteps||[];return` + `}function ue(e){let t=e.capabilities||[],a=e.batchSteps||[];return`
@@ -763,16 +763,16 @@ class ae{state={configPath:"mcp_servers.json",config:{mcpServers:{}},serverStatu
- ${a.map((n,r)=>{let s=t.find((d)=>d.id===n.capability_id),i=s?.input_schema,l=i?.properties||{},u=Array.isArray(i?.required)?i.required:[],p=Object.entries(l),g=t.map((d)=>` - - `).join(""),m="";if(p.length>0)m=` + `).join(""),v="";if(u.length>0)v=`
Parameters: - ${p.map(([d,y])=>{let b=u.includes(d),v=y.type||(y.enum?"enum":"any");return` - - ${k(d)} (${v}${b?" *":""}) + ${u.map(([c,y])=>{let b=g.includes(c),f=y.type||(y.enum?"enum":"any");return` + + ${i(c)} (${f}${b?" *":""}) `}).join("")}
@@ -781,7 +781,7 @@ class ae{state={configPath:"mcp_servers.json",config:{mcpServers:{}},serverStatu
STEP ${r+1} - id: ${k(n.id)} + id: ${i(o.id)}
- ${m} + ${v}
@@ -826,7 +826,7 @@ class ae{state={configPath:"mcp_servers.json",config:{mcpServers:{}},serverStatu rows="3" style="font-size: 11px; font-family: var(--ff-mono);" oninput="window.app.updateBatchStepArgs(${r}, this.value)" - >${k(n.argsJson)} + >${i(o.argsJson)}
`}).join("")} @@ -852,7 +852,7 @@ class ae{state={configPath:"mcp_servers.json",config:{mcpServers:{}},serverStatu
- `}function k(e){return String(e||"").replace(/&/g,"&").replace(//g,">").replace(/"/g,""")}function de(e){let t=e.tasks||[],a=e.taskFilterStatus||"all",o=t.filter((d)=>d.status==="input_required"),n=t.filter((d)=>d.status==="working"),r=t.filter((d)=>d.status==="completed"),s=t.filter((d)=>d.status==="cancelled"),i=t.filter((d)=>d.status==="failed"),l=a==="all"?t:t.filter((d)=>d.status===a),u=e.config.policy?.require_approval||e.config.policy?.requireApproval||[],p=o.length===0?` + `}function ee(e){let t=e.tasks||[],a=e.taskFilterStatus||"all",n=t.filter((c)=>c.status==="input_required"),o=t.filter((c)=>c.status==="working"),r=t.filter((c)=>c.status==="completed"),s=t.filter((c)=>c.status==="cancelled"),l=t.filter((c)=>c.status==="failed"),d=a==="all"?t:t.filter((c)=>c.status===a),g=e.config.policy?.require_approval||e.config.policy?.requireApproval||[],u=n.length===0?`
✓ @@ -862,7 +862,7 @@ class ae{state={configPath:"mcp_servers.json",config:{mcpServers:{}},serverStatu Tool calls requiring Human-in-the-Loop approval or returning asynchronous input_required tasks will suspend here for operator inspection, parameter editing, and response submission.
- `:o.map((d)=>{let y=d.inputRequests||{},b=Object.keys(y),v=b.length>0,x=Math.floor(Date.now()/1000),A=d.expiresAtEpochSecs?Math.max(0,d.expiresAtEpochSecs-x):d.ttlMs?Math.max(0,Math.floor(d.ttlMs/1000)):d.ttlSeconds||300,R=d.createdAtEpochSecs?new Date(d.createdAtEpochSecs*1000).toLocaleTimeString():d.createdAt?new Date(d.createdAt).toLocaleTimeString():"—";return` + `:n.map((c)=>{let y=c.inputRequests||{},b=Object.keys(y),f=b.length>0,x=Math.floor(Date.now()/1000),T=c.expiresAtEpochSecs?Math.max(0,c.expiresAtEpochSecs-x):c.ttlMs?Math.max(0,Math.floor(c.ttlMs/1000)):c.ttlSeconds||300,I=c.createdAtEpochSecs?new Date(c.createdAtEpochSecs*1000).toLocaleTimeString():c.createdAt?new Date(c.createdAt).toLocaleTimeString():"—";return`
@@ -870,143 +870,143 @@ class ae{state={configPath:"mcp_servers.json",config:{mcpServers:{}},serverStatu INPUT REQUIRED - ${C(d.taskId)} + ${i(c.taskId)}
- ${C(d.capabilityId||"Tool Execution")} + ${i(c.capabilityId||"Tool Execution")} - ${d.serverId?`via ${C(d.serverId)}`:""} + ${c.serverId?`via ${i(c.serverId)}`:""}
-
Created: ${R}
-
TTL Remaining: ${A}s
+
Created: ${I}
+
TTL Remaining: ${T}s
- ${d.context?` + ${c.context?`
- ${d.context.actor_id?`
Actor: ${C(d.context.actor_id)}
`:""} - ${d.context.operation_id?`
Operation: ${C(d.context.operation_id)}
`:""} - ${d.context.grant_id?`
Grant: ${C(d.context.grant_id)}
`:""} + ${c.context.actor_id?`
Actor: ${i(c.context.actor_id)}
`:""} + ${c.context.operation_id?`
Operation: ${i(c.context.operation_id)}
`:""} + ${c.context.grant_id?`
Grant: ${i(c.context.grant_id)}
`:""}
`:""}
- ${v?"Required Input Responses (MRTR / HITL)":"Input Responses Payload (JSON)"} + ${f?"Required Input Responses (MRTR / HITL)":"Input Responses Payload (JSON)"}
- ${v?` + ${f?`
- ${b.map((T)=>{let S=y[T]||{},w=typeof S==="string"?S:S.prompt||S.description||S.title||T,z=S.type||"text",L=S.default!==void 0?JSON.stringify(S.default):S.value!==void 0?JSON.stringify(S.value):S.sanitized_args?JSON.stringify(S.sanitized_args,null,2):"";if(z==="approval_review")return` + ${b.map((E)=>{let k=y[E]||{},w=typeof k==="string"?k:k.prompt||k.description||k.title||E,C=k.type||"text",A=k.default!==void 0?JSON.stringify(k.default):k.value!==void 0?JSON.stringify(k.value):k.sanitized_args?JSON.stringify(k.sanitized_args,null,2):"";if(C==="approval_review")return`
- + APPROVAL GATED
-
${C(w)}
+
${i(w)}
-
- +
`;return`
- - ${C(z)} + + ${i(C)}
-
${C(w)}
- ${z==="confirmation"||z==="boolean"?` - - `:S.sanitized_args||z==="object"||z==="json"?` - + `:k.sanitized_args||C==="object"||C==="json"?` + `:` - + `}
`}).join("")}
`:` - + `}
- +
- -
- `}).join(""),g=u.length===0?` + `}).join(""),m=g.length===0?`
No explicit require_approval rules active. Gated execution rules convert matching tool calls into tasks in real-time.
- `:u.map((d)=>` + `:g.map((c)=>`
- \uD83D\uDEE1️ ${C(d)} + \uD83D\uDEE1️ ${i(c)} GATED
- `).join(""),m=l.length===0?` + `).join(""),v=d.length===0?` - No tasks found matching filter "${C(a)}". + No tasks found matching filter "${i(a)}". - `:l.map((d)=>{let y=d.status==="completed"?"background: rgba(52, 211, 153, 0.12); color: var(--green-400); border-color: rgba(52, 211, 153, 0.3);":d.status==="working"?"background: rgba(56, 189, 248, 0.15); color: var(--cyan-400); border-color: rgba(56, 189, 248, 0.4);":d.status==="input_required"?"background: rgba(245, 158, 11, 0.2); color: var(--amber-300); border-color: rgba(245, 158, 11, 0.5);":d.status==="cancelled"?"background: rgba(148, 163, 184, 0.15); color: var(--text-muted); border-color: rgba(148, 163, 184, 0.3);":"background: rgba(248, 113, 113, 0.12); color: var(--red-400); border-color: rgba(248, 113, 113, 0.3);",b=d.progress!==void 0?Math.round(d.progress*100):d.status==="completed"?100:d.status==="working"?50:0,v=d.createdAtEpochSecs?new Date(d.createdAtEpochSecs*1000).toLocaleTimeString():d.createdAt?new Date(d.createdAt).toLocaleTimeString():"—";return` + `:d.map((c)=>{let y=c.status==="completed"?"background: rgba(52, 211, 153, 0.12); color: var(--green-400); border-color: rgba(52, 211, 153, 0.3);":c.status==="working"?"background: rgba(56, 189, 248, 0.15); color: var(--cyan-400); border-color: rgba(56, 189, 248, 0.4);":c.status==="input_required"?"background: rgba(245, 158, 11, 0.2); color: var(--amber-300); border-color: rgba(245, 158, 11, 0.5);":c.status==="cancelled"?"background: rgba(148, 163, 184, 0.15); color: var(--text-muted); border-color: rgba(148, 163, 184, 0.3);":"background: rgba(248, 113, 113, 0.12); color: var(--red-400); border-color: rgba(248, 113, 113, 0.3);",b=c.progress!==void 0?Math.round(c.progress*100):c.status==="completed"?100:c.status==="working"?50:0,f=c.createdAtEpochSecs?new Date(c.createdAtEpochSecs*1000).toLocaleTimeString():c.createdAt?new Date(c.createdAt).toLocaleTimeString():"—";return` - ${d.status.toUpperCase()} + ${c.status.toUpperCase()} - ${C(d.capabilityId||"Tool Execution")} + ${i(c.capabilityId||"Tool Execution")} - ${C(d.taskId)} + ${i(c.taskId)}
-
+
${b}%
- ${v} + ${f}
- - ${d.status==="input_required"||d.status==="working"?` - + + ${c.status==="input_required"||c.status==="working"?` + `:""}
@@ -1015,8 +1015,8 @@ class ae{state={configPath:"mcp_servers.json",config:{mcpServers:{}},serverStatu
- 0?"rgba(245, 158, 11, 0.4)":"rgba(52, 211, 153, 0.4)"}; background: ${o.length>0?"rgba(245, 158, 11, 0.1)":"rgba(52, 211, 153, 0.1)"};"> - ${o.length} ACTION REQUIRED + 0?"rgba(245, 158, 11, 0.4)":"rgba(52, 211, 153, 0.4)"}; background: ${n.length>0?"rgba(245, 158, 11, 0.1)":"rgba(52, 211, 153, 0.1)"};"> + ${n.length} ACTION REQUIRED SEP-2663 Tasks Extension (io.modelcontextprotocol/tasks) and Unified Human-in-the-Loop execution control. @@ -1034,12 +1034,12 @@ class ae{state={configPath:"mcp_servers.json",config:{mcpServers:{}},serverStatu
Input Required (HITL)
-
${o.length}
+
${n.length}
Awaiting operator decision or response
Working / In-Flight
-
${n.length}
+
${o.length}
Asynchronous active executions
@@ -1049,7 +1049,7 @@ class ae{state={configPath:"mcp_servers.json",config:{mcpServers:{}},serverStatu
Cancelled / Failed
-
${s.length+i.length}
+
${s.length+l.length}
Terminated or errored
@@ -1059,10 +1059,10 @@ class ae{state={configPath:"mcp_servers.json",config:{mcpServers:{}},serverStatu
- ⚡ Awaiting Operator Action (${o.length}) + ⚡ Awaiting Operator Action (${n.length})
- ${p} + ${u}
@@ -1073,7 +1073,7 @@ class ae{state={configPath:"mcp_servers.json",config:{mcpServers:{}},serverStatu
- ${g} + ${m}
@@ -1089,7 +1089,7 @@ class ae{state={configPath:"mcp_servers.json",config:{mcpServers:{}},serverStatu
- \uD83D\uDCDC Task Registry (${l.length}) + \uD83D\uDCDC Task Registry (${d.length})
Filter Status: @@ -1117,26 +1117,26 @@ class ae{state={configPath:"mcp_servers.json",config:{mcpServers:{}},serverStatu - ${m} + ${v}
- `}function C(e){return String(e||"").replace(/&/g,"&").replace(//g,">").replace(/"/g,""")}function ce(){let e=c.getState(),t=e.auditEvents||[],a=e.auditStats||{total_events:0,by_status:{success:0,failed:0,denied:0,intercepted:0}},o=e.auditVerification,n=e.auditFilters,r=e.auditTotal??t.length,s=e.auditSelectedEvent,i=Object.keys(e.config?.mcpServers||{}),l=n.limit||25,u=n.offset||0,p=Math.floor(u/l)+1,g=Math.max(1,Math.ceil(r/l)),m=r===0?0:u+1,d=Math.min(u+l,r),y=h.getAuditExportUrl({actor_id:n.search?void 0:void 0,server_id:n.serverId!=="all"?n.serverId:void 0,event_type:n.eventType!=="all"?n.eventType:void 0,status:n.status!=="all"?n.status:void 0,search:n.search.trim()?n.search.trim():void 0},"csv"),b=h.getAuditExportUrl({server_id:n.serverId!=="all"?n.serverId:void 0,event_type:n.eventType!=="all"?n.eventType:void 0,status:n.status!=="all"?n.status:void 0,search:n.search.trim()?n.search.trim():void 0},"jsonl"),v=o?o.is_valid?` + `}function te(){let e=p.getState(),t=e.auditEvents||[],a=e.auditStats||{total_events:0,by_status:{success:0,failed:0,denied:0,intercepted:0}},n=e.auditVerification,o=e.auditFilters,r=e.auditTotal??t.length,s=e.auditSelectedEvent,l=Object.keys(e.config?.mcpServers||{}),d=o.limit||25,g=o.offset||0,u=Math.floor(g/d)+1,m=Math.max(1,Math.ceil(r/d)),v=r===0?0:g+1,c=Math.min(g+d,r),y=h.getAuditExportUrl({actor_id:o.search?void 0:void 0,server_id:o.serverId!=="all"?o.serverId:void 0,event_type:o.eventType!=="all"?o.eventType:void 0,status:o.status!=="all"?o.status:void 0,search:o.search.trim()?o.search.trim():void 0},"csv"),b=h.getAuditExportUrl({server_id:o.serverId!=="all"?o.serverId:void 0,event_type:o.eventType!=="all"?o.eventType:void 0,status:o.status!=="all"?o.status:void 0,search:o.search.trim()?o.search.trim():void 0},"jsonl"),f=n?n.is_valid?`
\uD83D\uDEE1️ - Chain Verified: 100% Tamper Free (${o.total_records} events) + Chain Verified: 100% Tamper Free (${n.total_records} events)
`:`
⚠️ - TAMPER DETECTED at Record #${o.corrupted_at_index} + TAMPER DETECTED at Record #${n.corrupted_at_index}
`:` - `,x=i.map((w)=>``).join(""),A=` + `,x=l.map((w)=>``).join(""),T=`
@@ -1147,7 +1147,7 @@ class ae{state={configPath:"mcp_servers.json",config:{mcpServers:{}},serverStatu class="form-input" style="width: 100%; padding-left: 28px; font-size: 12px; height: 32px;" placeholder="Search trace, actor, capability, hash, error..." - value="${_(n.search)}" + value="${i(o.search)}" oninput="window.app.handleAuditSearchInput(this.value)" /> \uD83D\uDD0D @@ -1160,12 +1160,12 @@ class ae{state={configPath:"mcp_servers.json",config:{mcpServers:{}},serverStatu style="width: 100%; font-size: 12px; height: 32px;" onchange="window.app.handleAuditStatusFilter(this.value)" > - - - - - - + + + + + +
@@ -1176,16 +1176,16 @@ class ae{state={configPath:"mcp_servers.json",config:{mcpServers:{}},serverStatu style="width: 100%; font-size: 12px; height: 32px;" onchange="window.app.handleAuditEventTypeFilter(this.value)" > - - - - - - - - - - + + + + + + + + + +
@@ -1196,7 +1196,7 @@ class ae{state={configPath:"mcp_servers.json",config:{mcpServers:{}},serverStatu style="width: 100%; font-size: 12px; height: 32px;" onchange="window.app.handleAuditServerFilter(this.value)" > - + ${x}
@@ -1214,10 +1214,10 @@ class ae{state={configPath:"mcp_servers.json",config:{mcpServers:{}},serverStatu
- `,R=` + `,I=`
- Showing ${m}–${d} of ${r} events + Showing ${v}–${c} of ${r} events | Page Size:
@@ -1236,7 +1236,7 @@ class ae{state={configPath:"mcp_servers.json",config:{mcpServers:{}},serverStatu - Page ${p} of ${g} + Page ${u} of ${m}
- `,T="";if(t.length===0)T=` + `,E="";if(t.length===0)E=`
\uD83D\uDD0D
No Matching Audit Events
No audit records match your currently selected filters. Try broadening your search or resetting filters.
- `;else T=t.map((w)=>{let z=new Date(Math.floor(w.timestamp_ns/1e6)).toLocaleString(),L='SUCCESS';if(w.status==="denied")L='DENIED';else if(w.status==="intercepted")L='HITL INTERCEPT';else if(w.status==="failed")L='FAILED';else if(w.status==="cancelled")L='CANCELLED';let N=w.sanitized_args?JSON.stringify(w.sanitized_args):"-",D=w.actor_id||w.operator_id||"anonymous",q=w.server_id||"system",U=w.capability_id||w.event_type,V=w.execution_latency_us?`${(w.execution_latency_us/1000).toFixed(1)}ms`:"-";return` + `;else E=t.map((w)=>{let C=new Date(Math.floor(w.timestamp_ns/1e6)).toLocaleString(),A='SUCCESS';if(w.status==="denied")A='DENIED';else if(w.status==="intercepted")A='HITL INTERCEPT';else if(w.status==="failed")A='FAILED';else if(w.status==="cancelled")A='CANCELLED';let L=w.sanitized_args?JSON.stringify(w.sanitized_args):"-",M=w.actor_id||w.operator_id||"anonymous",R=w.server_id||"system",B=w.capability_id||w.event_type,O=w.execution_latency_us?`${(w.execution_latency_us/1000).toFixed(1)}ms`:"-";return`
- ${_(w.id)} - ${L} - ${_(U)} + ${i(w.id)} + ${A} + ${i(B)}
-
${_(z)}
+
${i(C)}
-
Actor: ${_(D)}
-
Server: ${_(q)}
-
Trace: ${_(w.trace_id)}
-
Latency: ${V}
+
Actor: ${i(M)}
+
Server: ${i(R)}
+
Trace: ${i(w.trace_id)}
+
Latency: ${O}
- Args: ${_(N)} + Args: ${i(L)}
-
prev_hash: ${_(w.prev_hash.slice(0,16))}...
-
hash: ${_(w.hash.slice(0,16))}...
+
prev_hash: ${i(w.prev_hash.slice(0,16))}...
+
hash: ${i(w.hash.slice(0,16))}...
- `}).join("");let S="";if(s){let w=new Date(Math.floor(s.timestamp_ns/1e6)).toISOString();S=` + `}).join("");let k="";if(s){let w=new Date(Math.floor(s.timestamp_ns/1e6)).toISOString();k=`
\uD83D\uDD12 -

Audit Event Details (${_(s.id)})

+

Audit Event Details (${i(s.id)})

@@ -1331,36 +1331,36 @@ class ae{state={configPath:"mcp_servers.json",config:{mcpServers:{}},serverStatu
-
Timestamp: ${_(w)}
-
Status: ${_(s.status.toUpperCase())}
-
Event Type: ${_(s.event_type)}
-
Server: ${_(s.server_id||"system")}
-
Capability: ${_(s.capability_id||"-")}
-
Actor / Operator: ${_(s.actor_id||s.operator_id||"anonymous")}
-
Trace ID: ${_(s.trace_id)}
-
Request ID: ${_(s.request_id||"-")}
-
Client IP: ${_(s.client_ip||"-")}
+
Timestamp: ${i(w)}
+
Status: ${i(s.status.toUpperCase())}
+
Event Type: ${i(s.event_type)}
+
Server: ${i(s.server_id||"system")}
+
Capability: ${i(s.capability_id||"-")}
+
Actor / Operator: ${i(s.actor_id||s.operator_id||"anonymous")}
+
Trace ID: ${i(s.trace_id)}
+
Request ID: ${i(s.request_id||"-")}
+
Client IP: ${i(s.client_ip||"-")}
Latency: ${s.execution_latency_us?`${(s.execution_latency_us/1000).toFixed(2)} ms`:"-"}
${s.error_message?`
-
Error (${_(s.error_code||"ERROR")}):
-
${_(s.error_message)}
+
Error (${i(s.error_code||"ERROR")}):
+
${i(s.error_message)}
`:""}
Sanitized Arguments
-
${_(JSON.stringify(s.sanitized_args||{},null,2))}
+
${i(JSON.stringify(s.sanitized_args||{},null,2))}
${s.sanitized_response?`
Sanitized Response
-
${_(JSON.stringify(s.sanitized_response,null,2))}
+
${i(JSON.stringify(s.sanitized_response,null,2))}
`:""} @@ -1369,11 +1369,11 @@ class ae{state={configPath:"mcp_servers.json",config:{mcpServers:{}},serverStatu
Tamper-Evidence Cryptographic Hashes
Previous Chain Hash (prev_hash): -
${_(s.prev_hash)}
+
${i(s.prev_hash)}
Record Hash Signature (hash): -
${_(s.hash)}
+
${i(s.hash)}
@@ -1391,7 +1391,7 @@ class ae{state={configPath:"mcp_servers.json",config:{mcpServers:{}},serverStatu Cryptographically tamper-evident, append-only execution log for SOC2 & ISO 27001 compliance.
- ${v} + ${f} \uD83D\uDCE5 Export CSV \uD83D\uDCE5 Export JSONL @@ -1419,7 +1419,7 @@ class ae{state={configPath:"mcp_servers.json",config:{mcpServers:{}},serverStatu
- ${A} + ${T}
@@ -1429,49 +1429,49 @@ class ae{state={configPath:"mcp_servers.json",config:{mcpServers:{}},serverStatu
- ${T} + ${E}
- ${r>0?R:""} + ${r>0?I:""} - ${S} - `}function _(e){if(!e)return"";return String(e).replace(/&/g,"&").replace(//g,">").replace(/"/g,""").replace(/'/g,"'")}function pe(){let e=c.getState(),t=e.activeProfile,a=t?e.config.profiles?.[t]:void 0,o=!!a,n=e.config.policy||{},r=a?.policy,s=o?r||{}:n,i=s.allow||[],l=s.deny||[],u=s.redact_keys||s.redactKeys||[],p=s.require_approval||s.requireApproval||[],g=i.length===0?` -
${o?"No profile allow list (inherits global rules)":"No allow list (all non-denied operations permitted)"}
- `:i.map((S,w)=>` + ${k} + `}function re(){let e=p.getState(),t=e.activeProfile,a=t?e.config.profiles?.[t]:void 0,n=!!a,o=e.config.policy||{},r=a?.policy,s=n?r||{}:o,l=s.allow||[],d=s.deny||[],g=s.redact_keys||s.redactKeys||[],u=s.require_approval||s.requireApproval||[],m=l.length===0?` +
${n?"No profile allow list (inherits global rules)":"No allow list (all non-denied operations permitted)"}
+ `:l.map((k,w)=>`
- ✔ ${j(S)} + ✔ ${i(k)}
- `).join(""),m=l.length===0?` -
${o?"No profile deny rules configured":"No deny rules configured"}
- `:l.map((S,w)=>` + `).join(""),v=d.length===0?` +
${n?"No profile deny rules configured":"No deny rules configured"}
+ `:d.map((k,w)=>`
- ✖ ${j(S)} + ✖ ${i(k)}
- `).join(""),d=p.length===0?` -
${o?"No profile human-in-the-loop triggers configured":"No human-in-the-loop approval rules configured"}
- `:p.map((S,w)=>` + `).join(""),c=u.length===0?` +
${n?"No profile human-in-the-loop triggers configured":"No human-in-the-loop approval rules configured"}
+ `:u.map((k,w)=>`
- \uD83D\uDEE1️ ${j(S)} + \uD83D\uDEE1️ ${i(k)}
- `).join(""),y=u.length===0?` -
${o?"No profile key redaction patterns configured":"No key redaction patterns configured"}
- `:u.map((S,w)=>` + `).join(""),y=g.length===0?` +
${n?"No profile key redaction patterns configured":"No key redaction patterns configured"}
+ `:g.map((k,w)=>` - ${j(S)} + ${i(k)} ✕ - `).join(""),b=o?` + `).join(""),b=n?`
\uD83D\uDEE1️
- Viewing & Editing Policy for Profile Constellation: ${j(t)} + Viewing & Editing Policy for Profile Constellation: ${i(t)}
Rules defined here apply specifically when requests target this profile. Deny and HITL rules are strictly additive with global rules. @@ -1484,11 +1484,11 @@ class ae{state={configPath:"mcp_servers.json",config:{mcpServers:{}},serverStatu
Global security policy rules governing wildcard access control, human-in-the-loop triggers, and sensitive key masking. (Select an active profile in the top bar to edit per-profile rules).
- `,v=Object.keys(e.config.mcpServers||{}),x=a?.servers||[],A=o?v.filter((S)=>!x.includes(S)):[],R=o?` + `,f=Object.keys(e.config.mcpServers||{}),x=a?.servers||[],T=n?f.filter((k)=>!x.includes(k)):[],I=n?`
- Constellation Server Boundaries (Profile: ${j(t)}) - ${x.length} of ${v.length} servers active + Constellation Server Boundaries (Profile: ${i(t)}) + ${x.length} of ${f.length} servers active
@@ -1496,9 +1496,9 @@ class ae{state={configPath:"mcp_servers.json",config:{mcpServers:{}},serverStatu ✔ Included Servers (${x.length})
- ${x.length>0?x.map((S)=>` + ${x.length>0?x.map((k)=>` - ${j(S)} + ${i(k)} `).join(""):'No servers included'}
@@ -1506,28 +1506,28 @@ class ae{state={configPath:"mcp_servers.json",config:{mcpServers:{}},serverStatu
- \uD83D\uDEAB Excluded Servers (${A.length}) · Implicitly Denied + \uD83D\uDEAB Excluded Servers (${T.length}) · Implicitly Denied
- ${A.length>0?A.map((S)=>` + ${T.length>0?T.map((k)=>` - ${j(S)} - + ${i(k)} + `).join(""):'All servers included in constellation'}
- `:"",T=o&&A.length>0?` + `:"",E=n&&T.length>0?`
- Implicit Boundary Denials (${A.length}) + Implicit Boundary Denials (${T.length})
- ${A.map((S)=>` + ${T.map((k)=>`
- ✖ ${j(S)}.* + ✖ ${i(k)}.* server excluded
`).join("")} @@ -1536,7 +1536,7 @@ class ae{state={configPath:"mcp_servers.json",config:{mcpServers:{}},serverStatu `:"";return` ${b} - ${R} + ${I}
@@ -1545,7 +1545,7 @@ class ae{state={configPath:"mcp_servers.json",config:{mcpServers:{}},serverStatu Allow List Patterns
- ${g} + ${m}
@@ -1559,8 +1559,8 @@ class ae{state={configPath:"mcp_servers.json",config:{mcpServers:{}},serverStatu Deny List Patterns (Strict Precedence)
- ${m} - ${T} + ${v} + ${E}
@@ -1574,7 +1574,7 @@ class ae{state={configPath:"mcp_servers.json",config:{mcpServers:{}},serverStatu Human-in-the-Loop Triggers
- ${d} + ${c}
@@ -1610,7 +1610,7 @@ class ae{state={configPath:"mcp_servers.json",config:{mcpServers:{}},serverStatu
- +
@@ -1623,7 +1623,7 @@ class ae{state={configPath:"mcp_servers.json",config:{mcpServers:{}},serverStatu
- +
@@ -1633,7 +1633,7 @@ class ae{state={configPath:"mcp_servers.json",config:{mcpServers:{}},serverStatu
- ${typeof s.webhook==="object"&&s.webhook?.url?`Active Target: ${j(s.webhook.url)}`:"No webhook configured"} + ${typeof s.webhook==="object"&&s.webhook?.url?`Active Target: ${i(s.webhook.url)}`:"No webhook configured"}
@@ -1652,47 +1652,47 @@ class ae{state={configPath:"mcp_servers.json",config:{mcpServers:{}},serverStatu
- `}function j(e){return String(e||"").replace(/&/g,"&").replace(//g,">").replace(/"/g,""")}function ue(){let e=c.getState(),t=e.config,a=Object.entries(t.capabilityAliases||{}),o=Object.entries(t.resourceAliases||{}),n=Object.entries(t.promptAliases||{}),r="";if(a.length===0&&o.length===0&&n.length===0)r=` + `}function se(){let e=p.getState(),t=e.config,a=Object.entries(t.capabilityAliases||{}),n=Object.entries(t.resourceAliases||{}),o=Object.entries(t.promptAliases||{}),r="";if(a.length===0&&n.length===0&&o.length===0)r=`
- No facade aliases configured in ${P(e.configPath)}. Add short names or custom descriptions to prune token payload sizes. + No facade aliases configured in ${i(e.configPath)}. Add short names or custom descriptions to prune token payload sizes.
- `;else{for(let[s,i]of a){let l=typeof i==="string"?i:i.target,u=typeof i==="object"&&i.summary?i.summary:"",p=typeof i==="object"&&!!i.passthrough,g=p?'⚡ passthrough':"",m=u?`
\uD83D\uDCAC ${P(u)}
`:"";r+=` -
+ `;else{for(let[s,l]of a){let d=typeof l==="string"?l:l.target,g=typeof l==="object"&&l.summary?l.summary:"",u=typeof l==="object"&&!!l.passthrough,m=u?'⚡ passthrough':"",v=g?`
\uD83D\uDCAC ${i(g)}
`:"";r+=` +
Tool
- ${P(s)} - ${g} + ${i(s)} + ${m}
- ${P(l)} - ${m} + ${i(d)} + ${v}
- +
- `}for(let[s,i]of o){let l=typeof i==="string"?i:i.target,u=typeof i==="object"&&i.summary?i.summary:"",p=u?`
\uD83D\uDCAC ${P(u)}
`:"";r+=` -
+ `}for(let[s,l]of n){let d=typeof l==="string"?l:l.target,g=typeof l==="object"&&l.summary?l.summary:"",u=g?`
\uD83D\uDCAC ${i(g)}
`:"";r+=` +
Resource - ${P(s)} + ${i(s)}
- ${P(l)} - ${p} + ${i(d)} + ${u}
- +
- `}for(let[s,i]of n){let l=typeof i==="string"?i:i.target,u=typeof i==="object"&&i.summary?i.summary:"",p=u?`
\uD83D\uDCAC ${P(u)}
`:"";r+=` -
+ `}for(let[s,l]of o){let d=typeof l==="string"?l:l.target,g=typeof l==="object"&&l.summary?l.summary:"",u=g?`
\uD83D\uDCAC ${i(g)}
`:"";r+=` +
Prompt - ${P(s)} + ${i(s)}
- ${P(l)} - ${p} + ${i(d)} + ${u}
- +
`}}return` @@ -1739,7 +1739,7 @@ class ae{state={configPath:"mcp_servers.json",config:{mcpServers:{}},serverStatu
${r}
- `}function P(e){return String(e||"").replace(/&/g,"&").replace(//g,">").replace(/"/g,""")}function ge(){let e=c.getState(),t=e.config,a=t.profiles||{},o=Object.entries(a),n=t.mcpServers||{},r=e.activeProfile,s="";if(o.length===0)s=` + `}function ae(){let e=p.getState(),t=e.config,a=t.profiles||{},n=Object.entries(a),o=t.mcpServers||{},r=e.activeProfile,s="";if(n.length===0)s=`
No Profiles Configured

@@ -1747,50 +1747,50 @@ class ae{state={configPath:"mcp_servers.json",config:{mcpServers:{}},serverStatu

- `;else s=o.map(([i,l])=>{let u=r===i,p=l.servers.map((x)=>`${Y(x)}`).join(" "),g=(e.capabilities||[]).filter((x)=>l.servers.includes(x.server)).length,m=!!l.policy,d=l.policy?.allow?.length||0,y=l.policy?.deny?.length||0,b=(l.policy?.require_approval||l.policy?.requireApproval||[]).length,v=(l.policy?.redact_keys||l.policy?.redactKeys||[]).length;return` -
+ `;else s=n.map(([l,d])=>{let g=r===l,u=d.servers.map((x)=>`${i(x)}`).join(" "),m=(e.capabilities||[]).filter((x)=>d.servers.includes(x.server)).length,v=!!d.policy,c=d.policy?.allow?.length||0,y=d.policy?.deny?.length||0,b=(d.policy?.require_approval||d.policy?.requireApproval||[]).length,f=(d.policy?.redact_keys||d.policy?.redactKeys||[]).length;return` +
- ${Y(i)} - ${u?'ACTIVE IN UI':""} - ${l.servers.length} server${l.servers.length===1?"":"s"} - ${g} capabilities - ${m?'CUSTOM POLICY':""} + ${i(l)} + ${g?'ACTIVE IN UI':""} + ${d.servers.length} server${d.servers.length===1?"":"s"} + ${m} capabilities + ${v?'CUSTOM POLICY':""}
- ${Y(l.description||"No description provided")} + ${i(d.description||"No description provided")}
-
+
Servers: - ${p||'None'} + ${u||'None'}
- ${m?` + ${v?`
Policy Overlay: - ${d>0?`Allow: ${d}`:""} + ${c>0?`Allow: ${c}`:""} ${y>0?`Deny: ${y}`:""} ${b>0?`HITL: ${b}`:""} - ${v>0?`Redact: ${v}`:""} - ${d===0&&y===0&&b===0&&v===0?'Configured':""} + ${f>0?`Redact: ${f}`:""} + ${c===0&&y===0&&b===0&&f===0?'Configured':""}
`:""}
- ${u?` + ${g?` `:` - `} - -
@@ -1818,26 +1818,26 @@ class ae{state={configPath:"mcp_servers.json",config:{mcpServers:{}},serverStatu
${s} - `}function Y(e){return String(e||"").replace(/&/g,"&").replace(//g,">").replace(/"/g,""")}function me(){let e=c.getState(),t=e.secrets||[],a=e.config.mcpServers||{},o=[];for(let[p,g]of Object.entries(a)){let m=Q(p,g.command,g.args);if(m){let d=Object.keys(g.env||{});for(let y of m.envFields)if(y.required&&!d.includes(y.key))o.push({server:p,key:y.key,uri:"(Not Configured)",is_vault:!1,exists:!1,backend:"Required Variable",display:`Required by ${m.name} template (${y.label})`,is_unconfigured_requirement:!0})}}let n=[...t,...o],r=n.length,s=n.filter((p)=>p.is_vault&&p.exists!==!1).length,i=n.filter((p)=>p.exists===!1).length,l=n.filter((p)=>!p.is_vault&&p.exists!==!1).length,u=n.length===0?` + `}function oe(){let e=p.getState(),t=e.secrets||[],a=e.config.mcpServers||{},n=[];for(let[u,m]of Object.entries(a)){let v=N(u,m.command,m.args);if(v){let c=Object.keys(m.env||{});for(let y of v.envFields)if(y.required&&!c.includes(y.key))n.push({server:u,key:y.key,uri:"(Not Configured)",is_vault:!1,exists:!1,backend:"Required Variable",display:`Required by ${v.name} template (${y.label})`,is_unconfigured_requirement:!0})}}let o=[...t,...n],r=o.length,s=o.filter((u)=>u.is_vault&&u.exists!==!1).length,l=o.filter((u)=>u.exists===!1).length,d=o.filter((u)=>!u.is_vault&&u.exists!==!1).length,g=o.length===0?`
No environment variables or secrets configured in active servers.
- `:n.map((p)=>{let g='Plaintext (Unsecured)',m=p.exists===!1;if(p.is_unconfigured_requirement)g='⚠️ Not Configured (Required)';else if(m)g=`⚠️ Missing from ${H(p.backend)}`;else if(p.is_vault)g=`\uD83D\uDD12 ${H(p.backend)}`;return` + `:o.map((u)=>{let m='Plaintext (Unsecured)',v=u.exists===!1;if(u.is_unconfigured_requirement)m='⚠️ Not Configured (Required)';else if(v)m=`⚠️ Missing from ${i(u.backend)}`;else if(u.is_vault)m=`\uD83D\uDD12 ${i(u.backend)}`;return`
- ${H(p.server)} - ${H(p.key)} - ${H(p.display)} -
${g}
+ ${i(u.server)} + ${i(u.key)} + ${i(u.display)} +
${m}
- ${p.is_unconfigured_requirement?` - - `:m?` - - - `:!p.is_vault?` - + ${u.is_unconfigured_requirement?` + + `:v?` + + + `:!u.is_vault?` + `:` - + `}
@@ -1860,8 +1860,8 @@ class ae{state={configPath:"mcp_servers.json",config:{mcpServers:{}},serverStatu
Missing or Unsecured
-
${i+l}
-
${i>0?`${i} missing required key(s)`:l>0?"Recommend migrating to Keychain":"All credentials protected"}
+
${l+d}
+
${l>0?`${l} missing required key(s)`:d>0?"Recommend migrating to Keychain":"All credentials protected"}
@@ -1888,39 +1888,39 @@ class ae{state={configPath:"mcp_servers.json",config:{mcpServers:{}},serverStatu ACTION
- ${u} + ${g}
- `}function H(e){return String(e||"").replace(/&/g,"&").replace(//g,">").replace(/"/g,""")}class ve{activeTemplateCategory="all";activeTemplateFilter="";selectedTemplate=null;pendingClientId=null;async init(){let e=window.location.port?`:${window.location.port}`:"",t=document.getElementById("daemon-port-label");if(t)t.textContent=`Daemon ${e}`;await this.refreshData(),this.initSSE(),this.render(),c.subscribe(()=>{this.render()})}auditSearchTimeout=null;async refreshData(){try{let e=c.getState(),t=e.auditFilters,a=e.activeProfile||void 0,[o,n,r,s,i,l,u,p,g,m,d]=await Promise.all([h.getConfig(),h.listCapabilities(a),h.listResources(a),h.listPrompts(a),h.getCatalogEvents(),h.listApprovals(),h.listTasks(),h.listAuditEvents({server_id:t.serverId!=="all"?t.serverId:void 0,event_type:t.eventType!=="all"?t.eventType:void 0,status:t.status!=="all"?t.status:void 0,search:t.search.trim()?t.search.trim():void 0,limit:t.limit,offset:t.offset}),h.getAuditStats(),h.getClients().catch(()=>({ok:!1,clients:[]})),h.getSecrets().catch(()=>({ok:!1,secrets:[],keychain_service:"warmplane"}))]);if(m&&m.ok&&Array.isArray(m.clients))c.setState({clients:m.clients});if(d&&d.ok&&Array.isArray(d.secrets))c.setState({secrets:d.secrets});if(o.ok)c.setState({configPath:o.config_path,config:o.config,serverStatuses:o.server_statuses||{},circuitBreakers:o.circuit_breakers||[],metrics:{totalCatalogRequests:o.metrics?.total_catalog_requests||0,totalEtagHits:o.metrics?.total_etag_hits||0,totalToolCalls:o.metrics?.total_tool_calls||0,totalToolDurationUs:o.metrics?.total_tool_duration_us||0}});if(n&&Array.isArray(n.capabilities)){let y=c.getState().selectedCapabilityId,v=n.capabilities.some((x)=>x.id===y)?y:n.capabilities.length>0?n.capabilities[0].id:null;c.setState({capabilities:n.capabilities,capabilitiesHiddenByPolicy:n.hidden_by_policy||0,selectedCapabilityId:v})}if(r&&Array.isArray(r.resources)){let y=c.getState().selectedResourceId,v=r.resources.some((x)=>x.uri===y||x.id===y)?y:r.resources.length>0?r.resources[0].uri||r.resources[0].id||null:null;c.setState({resources:r.resources,resourcesHiddenByPolicy:r.hidden_by_policy||0,selectedResourceId:v})}if(s&&Array.isArray(s.prompts)){let y=c.getState().selectedPromptId,v=s.prompts.some((x)=>x.name===y||x.id===y)?y:s.prompts.length>0?s.prompts[0].name||s.prompts[0].id||null:null;c.setState({prompts:s.prompts,promptsHiddenByPolicy:s.hidden_by_policy||0,selectedPromptId:v})}if(i&&Array.isArray(i.events))c.setState({catalogEvents:i.events});if(l&&Array.isArray(l.approvals))c.setState({approvals:l.approvals});if(u&&Array.isArray(u.tasks))c.setState({tasks:u.tasks});if(p&&Array.isArray(p.events))c.setState({auditEvents:p.events,auditTotal:p.total??p.events.length});if(g&&g.ok)c.setState({auditStats:g})}catch(e){console.error("Failed to fetch daemon state:",e)}}async refreshAuditEvents(){try{let t=c.getState().auditFilters,[a,o]=await Promise.all([h.listAuditEvents({server_id:t.serverId!=="all"?t.serverId:void 0,event_type:t.eventType!=="all"?t.eventType:void 0,status:t.status!=="all"?t.status:void 0,search:t.search.trim()?t.search.trim():void 0,limit:t.limit,offset:t.offset}),h.getAuditStats()]);if(a&&Array.isArray(a.events))c.setState({auditEvents:a.events,auditTotal:a.total??a.events.length});if(o&&o.ok)c.setState({auditStats:o})}catch(e){console.error("Failed to refresh audit events:",e)}}handleAuditSearchInput(e){let a={...c.getState().auditFilters,search:e,offset:0};c.setState({auditFilters:a}),clearTimeout(this.auditSearchTimeout),this.auditSearchTimeout=setTimeout(()=>{this.refreshAuditEvents()},250)}handleAuditStatusFilter(e){let t=c.getState();c.setState({auditFilters:{...t.auditFilters,status:e,offset:0}}),this.refreshAuditEvents()}handleAuditEventTypeFilter(e){let t=c.getState();c.setState({auditFilters:{...t.auditFilters,eventType:e,offset:0}}),this.refreshAuditEvents()}handleAuditServerFilter(e){let t=c.getState();c.setState({auditFilters:{...t.auditFilters,serverId:e,offset:0}}),this.refreshAuditEvents()}handleAuditPageSize(e){let t=parseInt(e,10)||25,a=c.getState();c.setState({auditFilters:{...a.auditFilters,limit:t,offset:0}}),this.refreshAuditEvents()}clearAuditFilters(){let e=c.getState();c.setState({auditFilters:{search:"",status:"all",eventType:"all",serverId:"all",limit:e.auditFilters.limit||25,offset:0}}),this.refreshAuditEvents()}auditPrevPage(){let e=c.getState(),{limit:t,offset:a}=e.auditFilters,o=Math.max(0,a-t);if(o!==a)c.setState({auditFilters:{...e.auditFilters,offset:o}}),this.refreshAuditEvents()}auditNextPage(){let e=c.getState(),{limit:t,offset:a}=e.auditFilters,o=e.auditTotal;if(a+to.id===e)||null;c.setState({auditSelectedEvent:a})}async verifyAuditChain(){try{let e=await h.verifyAuditChain();if(e&&e.report)c.setState({auditVerification:e.report})}catch(e){console.error("Failed to verify audit chain:",e)}}async refreshApprovals(){try{let e=await h.listApprovals();if(e&&Array.isArray(e.approvals))c.setState({approvals:e.approvals})}catch(e){console.error("Failed to refresh approvals:",e)}}initSSE(){try{let e=new EventSource("/v1/resources/updates");e.onmessage=(t)=>{c.addEventLog("SSE","/v1/resources/updates","UPDATED","0.1ms"),this.refreshData()}}catch(e){console.warn("SSE connection unavailable")}}switchTab(e){c.setState({activeTab:e}),this.refreshData()}render(){let e=c.getState(),t=document.getElementById("app-main");if(!t)return;let a=(e.tasks||[]).filter((l)=>l.status==="input_required").length,o=(e.approvals||[]).filter((l)=>l.status==="pending").length,n=Math.max(a,o),r=document.getElementById("nav-approvals-badge");if(r)r.textContent=n>0?`${n}`:"",r.style.display=n>0?"inline-block":"none";document.querySelectorAll(".nav-item").forEach((l)=>{let u=l.getAttribute("data-tab");if(u===e.activeTab||e.activeTab==="tasks"&&u==="approvals"||e.activeTab==="approvals"&&u==="tasks")l.classList.add("active");else l.classList.remove("active")});let s=document.getElementById("top-title"),i={overview:"Overview Cockpit",servers:"Server Hub & Connections",playground:"MCP Capability Playground",tasks:"SEP-2663 Tasks & HITL Review",approvals:"SEP-2663 Tasks & HITL Review",audit:"WORM Audit & Compliance Ledger",policy:"Security Governance & Redaction",secrets:"Native OS Keychain & Secrets Vault",aliases:"Facade & Alias Studio",profiles:"Server Constellation Profiles"};if(s)s.textContent=i[e.activeTab]||"Control Deck";switch(this.renderTopProfileSelector(),e.activeTab){case"overview":t.innerHTML=oe();break;case"servers":t.innerHTML=ie();break;case"playground":t.innerHTML=le();break;case"tasks":case"approvals":t.innerHTML=de(e);break;case"audit":t.innerHTML=ce();break;case"policy":t.innerHTML=pe();break;case"secrets":t.innerHTML=me();break;case"aliases":t.innerHTML=ue();break;case"profiles":t.innerHTML=ge();break}}toggleClientsCollapse(){let e=c.getState().clientsCollapsed;c.setState({clientsCollapsed:!e}),this.render()}async saveNewVaultSecret(){let e=document.getElementById("vault-new-key"),t=document.getElementById("vault-new-val"),a=document.getElementById("vault-new-service"),o=e?.value.trim(),n=t?.value.trim(),r=a?.value.trim()||"warmplane";if(!o||!n){alert("Key and secret value are required");return}try{let s=await h.saveSecret(o,n,r);if(s.ok){if(alert(`Secret '${o}' saved securely into OS Keychain! -Reference: ${s.uri}`),e)e.value="";if(t)t.value="";await this.refreshData()}else alert(`Failed to save secret: ${s.error}`)}catch(s){alert(`Error saving secret: ${s.message}`)}}async deleteVaultSecret(e,t){let a=t?`Are you sure you want to remove secret '${e}' from OS Keychain and server '${t}'?`:`Are you sure you want to remove secret '${e}' from OS Keychain?`;if(!confirm(a))return;try{let o=await h.deleteSecret(e);if(o.ok||o.error?.includes("not found")){if(t){let s=(c.getState().config.mcpServers||{})[t];if(s&&s.env&&s.env[e]){let i={...s.env};delete i[e];let l={...s,env:i};await h.upsertServer(t,l)}}await this.refreshData()}else alert(`Failed to delete secret: ${o.error}`)}catch(o){alert(`Error deleting secret: ${o.message}`)}}async removeSecretFromConfig(e,t){if(!confirm(`Remove environment variable '${t}' from '${e}' configuration?`))return;try{let n=(c.getState().config.mcpServers||{})[e];if(n&&n.env&&n.env[t]){let r={...n.env};delete r[t];let s={...n,env:r},i=await h.upsertServer(e,s);if(i.ok)await this.refreshData();else alert(`Failed to update server config: ${i.error}`)}}catch(a){alert(`Error removing secret from config: ${a.message}`)}}async quickVaultEnv(e,t){let a=prompt(`Enter secret value to store in OS Keychain for ${e}.${t}:`);if(!a)return;try{let o=await h.saveSecret(t,a,"warmplane");if(!o.ok){alert(`Failed to save to Keychain: ${o.error}`);return}let s=(c.getState().config.mcpServers||{})[e];if(s){let i={...s.env||{},[t]:`keychain://warmplane/${t}`},l={...s,env:i},u=await h.upsertServer(e,l);if(u.ok)await this.refreshData(),alert(`Successfully configured ${e}.${t} in OS Keychain!`);else alert(`Failed to update server config: ${u.error}`)}}catch(o){alert(`Error during migration: ${o.message}`)}}async refreshTasks(){try{let e=await h.listTasks();if(e&&Array.isArray(e.tasks))c.setState({tasks:e.tasks})}catch(e){console.error("Failed to refresh tasks:",e)}}filterTasksByStatus(e){c.setState({taskFilterStatus:e})}togglePlaygroundAsyncTask(e){c.setState({playgroundAsyncTask:e})}async submitTaskInputResponses(e){let a=c.getState().tasks.find((r)=>r.taskId===e)?.inputRequests||{},o=Object.keys(a),n={};if(o.length>0)for(let r of o){let s=a[r];if(s&&s.type==="approval_review"){let i=document.getElementById(`task-input-${e}-${r}-decision`),l=document.getElementById(`task-input-${e}-${r}`),u=i?i.value==="true":!0,p=void 0;if(l&&l.value.trim())try{p=JSON.parse(l.value.trim())}catch{alert("Invalid JSON in parameters editor");return}n[r]={approved:u,modified_args:p,reason:u?void 0:"Operator rejected execution via Tasks review"}}else{let i=document.getElementById(`task-input-${e}-${r}`);if(i){let l=i.value.trim();try{n[r]=JSON.parse(l)}catch{n[r]=l}}}}else{let r=document.getElementById(`task-raw-input-${e}`);if(r&&r.value.trim())try{Object.assign(n,JSON.parse(r.value.trim()))}catch{alert("Invalid JSON in raw input responses");return}}try{let r=await h.updateTask(e,n);if(r.ok)await this.refreshTasks();else alert(`Task update failed: ${r.error?.message||r.error||"Unknown error"}`)}catch(r){alert(`Error updating task: ${r.message}`)}}async promptCancelTask(e){let t=prompt("Reason for cancelling task:");if(t===null)return;try{let a=await h.cancelTask(e,t||void 0);if(a.ok)await this.refreshTasks();else alert(`Task cancellation failed: ${a.error?.message||a.error||"Unknown error"}`)}catch(a){alert(`Error cancelling task: ${a.message}`)}}async openTaskInspectorModal(e){this.closeModals();let t=document.getElementById("modal-task-inspector");if(!t)return;let a=document.getElementById("modal-task-title"),o=document.getElementById("modal-task-body"),n=document.getElementById("modal-task-footer");if(a)a.textContent=`Task Inspector: ${e}`;if(o)o.innerHTML=` + `}class ne{activeTemplateCategory="all";activeTemplateFilter="";selectedTemplate=null;pendingClientId=null;async init(){let e=window.location.port?`:${window.location.port}`:"",t=document.getElementById("daemon-port-label");if(t)t.textContent=`Daemon ${e}`;await this.refreshData(),this.initSSE(),this.render(),p.subscribe(()=>{this.render()})}auditSearchTimeout=null;async refreshData(){try{let e=p.getState(),t=e.auditFilters,a=e.activeProfile||void 0,[n,o,r,s,l,d,g,u,m,v,c]=await Promise.all([h.getConfig(),h.listCapabilities(a),h.listResources(a),h.listPrompts(a),h.getCatalogEvents(),h.listApprovals(),h.listTasks(),h.listAuditEvents({server_id:t.serverId!=="all"?t.serverId:void 0,event_type:t.eventType!=="all"?t.eventType:void 0,status:t.status!=="all"?t.status:void 0,search:t.search.trim()?t.search.trim():void 0,limit:t.limit,offset:t.offset}),h.getAuditStats(),h.getClients().catch(()=>({ok:!1,clients:[]})),h.getSecrets().catch(()=>({ok:!1,secrets:[],keychain_service:"warmplane"}))]);if(v&&v.ok&&Array.isArray(v.clients))p.setState({clients:v.clients});if(c&&c.ok&&Array.isArray(c.secrets))p.setState({secrets:c.secrets});if(n.ok)p.setState({configPath:n.config_path,config:n.config,serverStatuses:n.server_statuses||{},circuitBreakers:n.circuit_breakers||[],metrics:{totalCatalogRequests:n.metrics?.total_catalog_requests||0,totalEtagHits:n.metrics?.total_etag_hits||0,totalToolCalls:n.metrics?.total_tool_calls||0,totalToolDurationUs:n.metrics?.total_tool_duration_us||0}});if(o&&Array.isArray(o.capabilities)){let y=p.getState().selectedCapabilityId,f=o.capabilities.some((x)=>x.id===y)?y:o.capabilities.length>0?o.capabilities[0].id:null;p.setState({capabilities:o.capabilities,capabilitiesHiddenByPolicy:o.hidden_by_policy||0,selectedCapabilityId:f})}if(r&&Array.isArray(r.resources)){let y=p.getState().selectedResourceId,f=r.resources.some((x)=>x.uri===y||x.id===y)?y:r.resources.length>0?r.resources[0].uri||r.resources[0].id||null:null;p.setState({resources:r.resources,resourcesHiddenByPolicy:r.hidden_by_policy||0,selectedResourceId:f})}if(s&&Array.isArray(s.prompts)){let y=p.getState().selectedPromptId,f=s.prompts.some((x)=>x.name===y||x.id===y)?y:s.prompts.length>0?s.prompts[0].name||s.prompts[0].id||null:null;p.setState({prompts:s.prompts,promptsHiddenByPolicy:s.hidden_by_policy||0,selectedPromptId:f})}if(l&&Array.isArray(l.events))p.setState({catalogEvents:l.events});if(d&&Array.isArray(d.approvals))p.setState({approvals:d.approvals});if(g&&Array.isArray(g.tasks))p.setState({tasks:g.tasks});if(u&&Array.isArray(u.events))p.setState({auditEvents:u.events,auditTotal:u.total??u.events.length});if(m&&m.ok)p.setState({auditStats:m})}catch(e){console.error("Failed to fetch daemon state:",e)}}async refreshAuditEvents(){try{let t=p.getState().auditFilters,[a,n]=await Promise.all([h.listAuditEvents({server_id:t.serverId!=="all"?t.serverId:void 0,event_type:t.eventType!=="all"?t.eventType:void 0,status:t.status!=="all"?t.status:void 0,search:t.search.trim()?t.search.trim():void 0,limit:t.limit,offset:t.offset}),h.getAuditStats()]);if(a&&Array.isArray(a.events))p.setState({auditEvents:a.events,auditTotal:a.total??a.events.length});if(n&&n.ok)p.setState({auditStats:n})}catch(e){console.error("Failed to refresh audit events:",e)}}handleAuditSearchInput(e){let a={...p.getState().auditFilters,search:e,offset:0};p.setState({auditFilters:a}),clearTimeout(this.auditSearchTimeout),this.auditSearchTimeout=setTimeout(()=>{this.refreshAuditEvents()},250)}handleAuditStatusFilter(e){let t=p.getState();p.setState({auditFilters:{...t.auditFilters,status:e,offset:0}}),this.refreshAuditEvents()}handleAuditEventTypeFilter(e){let t=p.getState();p.setState({auditFilters:{...t.auditFilters,eventType:e,offset:0}}),this.refreshAuditEvents()}handleAuditServerFilter(e){let t=p.getState();p.setState({auditFilters:{...t.auditFilters,serverId:e,offset:0}}),this.refreshAuditEvents()}handleAuditPageSize(e){let t=parseInt(e,10)||25,a=p.getState();p.setState({auditFilters:{...a.auditFilters,limit:t,offset:0}}),this.refreshAuditEvents()}clearAuditFilters(){let e=p.getState();p.setState({auditFilters:{search:"",status:"all",eventType:"all",serverId:"all",limit:e.auditFilters.limit||25,offset:0}}),this.refreshAuditEvents()}auditPrevPage(){let e=p.getState(),{limit:t,offset:a}=e.auditFilters,n=Math.max(0,a-t);if(n!==a)p.setState({auditFilters:{...e.auditFilters,offset:n}}),this.refreshAuditEvents()}auditNextPage(){let e=p.getState(),{limit:t,offset:a}=e.auditFilters,n=e.auditTotal;if(a+tn.id===e)||null;p.setState({auditSelectedEvent:a})}async verifyAuditChain(){try{let e=await h.verifyAuditChain();if(e&&e.report)p.setState({auditVerification:e.report})}catch(e){console.error("Failed to verify audit chain:",e)}}async refreshApprovals(){try{let e=await h.listApprovals();if(e&&Array.isArray(e.approvals))p.setState({approvals:e.approvals})}catch(e){console.error("Failed to refresh approvals:",e)}}initSSE(){try{let e=new EventSource("/v1/resources/updates");e.onmessage=(t)=>{p.addEventLog("SSE","/v1/resources/updates","UPDATED","0.1ms"),this.refreshData()}}catch(e){console.warn("SSE connection unavailable")}}switchTab(e){p.setState({activeTab:e}),this.refreshData()}render(){let e=p.getState(),t=document.getElementById("app-main");if(!t)return;let a=(e.tasks||[]).filter((d)=>d.status==="input_required").length,n=(e.approvals||[]).filter((d)=>d.status==="pending").length,o=Math.max(a,n),r=document.getElementById("nav-approvals-badge");if(r)r.textContent=o>0?`${o}`:"",r.style.display=o>0?"inline-block":"none";document.querySelectorAll(".nav-item").forEach((d)=>{let g=d.getAttribute("data-tab");if(g===e.activeTab||e.activeTab==="tasks"&&g==="approvals"||e.activeTab==="approvals"&&g==="tasks")d.classList.add("active");else d.classList.remove("active")});let s=document.getElementById("top-title"),l={overview:"Overview Cockpit",servers:"Server Hub & Connections",playground:"MCP Capability Playground",tasks:"SEP-2663 Tasks & HITL Review",approvals:"SEP-2663 Tasks & HITL Review",audit:"WORM Audit & Compliance Ledger",policy:"Security Governance & Redaction",secrets:"Native OS Keychain & Secrets Vault",aliases:"Facade & Alias Studio",profiles:"Server Constellation Profiles"};if(s)s.textContent=l[e.activeTab]||"Control Deck";switch(this.renderTopProfileSelector(),e.activeTab){case"overview":t.innerHTML=Y();break;case"servers":t.innerHTML=X();break;case"playground":t.innerHTML=Z();break;case"tasks":case"approvals":t.innerHTML=ee(e);break;case"audit":t.innerHTML=te();break;case"policy":t.innerHTML=re();break;case"secrets":t.innerHTML=oe();break;case"aliases":t.innerHTML=se();break;case"profiles":t.innerHTML=ae();break}}toggleClientsCollapse(){let e=p.getState().clientsCollapsed;p.setState({clientsCollapsed:!e}),this.render()}async saveNewVaultSecret(){let e=document.getElementById("vault-new-key"),t=document.getElementById("vault-new-val"),a=document.getElementById("vault-new-service"),n=e?.value.trim(),o=t?.value.trim(),r=a?.value.trim()||"warmplane";if(!n||!o){alert("Key and secret value are required");return}try{let s=await h.saveSecret(n,o,r);if(s.ok){if(alert(`Secret '${n}' saved securely into OS Keychain! +Reference: ${s.uri}`),e)e.value="";if(t)t.value="";await this.refreshData()}else alert(`Failed to save secret: ${s.error}`)}catch(s){alert(`Error saving secret: ${s.message}`)}}async deleteVaultSecret(e,t){let a=t?`Are you sure you want to remove secret '${e}' from OS Keychain and server '${t}'?`:`Are you sure you want to remove secret '${e}' from OS Keychain?`;if(!confirm(a))return;try{let n=await h.deleteSecret(e);if(n.ok||n.error?.includes("not found")){if(t){let s=(p.getState().config.mcpServers||{})[t];if(s&&s.env&&s.env[e]){let l={...s.env};delete l[e];let d={...s,env:l};await h.upsertServer(t,d)}}await this.refreshData()}else alert(`Failed to delete secret: ${n.error}`)}catch(n){alert(`Error deleting secret: ${n.message}`)}}async removeSecretFromConfig(e,t){if(!confirm(`Remove environment variable '${t}' from '${e}' configuration?`))return;try{let o=(p.getState().config.mcpServers||{})[e];if(o&&o.env&&o.env[t]){let r={...o.env};delete r[t];let s={...o,env:r},l=await h.upsertServer(e,s);if(l.ok)await this.refreshData();else alert(`Failed to update server config: ${l.error}`)}}catch(a){alert(`Error removing secret from config: ${a.message}`)}}async quickVaultEnv(e,t){let a=prompt(`Enter secret value to store in OS Keychain for ${e}.${t}:`);if(!a)return;try{let n=await h.saveSecret(t,a,"warmplane");if(!n.ok){alert(`Failed to save to Keychain: ${n.error}`);return}let s=(p.getState().config.mcpServers||{})[e];if(s){let l={...s.env||{},[t]:`keychain://warmplane/${t}`},d={...s,env:l},g=await h.upsertServer(e,d);if(g.ok)await this.refreshData(),alert(`Successfully configured ${e}.${t} in OS Keychain!`);else alert(`Failed to update server config: ${g.error}`)}}catch(n){alert(`Error during migration: ${n.message}`)}}async refreshTasks(){try{let e=await h.listTasks();if(e&&Array.isArray(e.tasks))p.setState({tasks:e.tasks})}catch(e){console.error("Failed to refresh tasks:",e)}}filterTasksByStatus(e){p.setState({taskFilterStatus:e})}togglePlaygroundAsyncTask(e){p.setState({playgroundAsyncTask:e})}async submitTaskInputResponses(e){let a=p.getState().tasks.find((r)=>r.taskId===e)?.inputRequests||{},n=Object.keys(a),o={};if(n.length>0)for(let r of n){let s=a[r];if(s&&s.type==="approval_review"){let l=document.getElementById(`task-input-${e}-${r}-decision`),d=document.getElementById(`task-input-${e}-${r}`),g=l?l.value==="true":!0,u=void 0;if(d&&d.value.trim())try{u=JSON.parse(d.value.trim())}catch{alert("Invalid JSON in parameters editor");return}o[r]={approved:g,modified_args:u,reason:g?void 0:"Operator rejected execution via Tasks review"}}else{let l=document.getElementById(`task-input-${e}-${r}`);if(l){let d=l.value.trim();try{o[r]=JSON.parse(d)}catch{o[r]=d}}}}else{let r=document.getElementById(`task-raw-input-${e}`);if(r&&r.value.trim())try{Object.assign(o,JSON.parse(r.value.trim()))}catch{alert("Invalid JSON in raw input responses");return}}try{let r=await h.updateTask(e,o);if(r.ok)await this.refreshTasks();else alert(`Task update failed: ${r.error?.message||r.error||"Unknown error"}`)}catch(r){alert(`Error updating task: ${r.message}`)}}async promptCancelTask(e){let t=prompt("Reason for cancelling task:");if(t===null)return;try{let a=await h.cancelTask(e,t||void 0);if(a.ok)await this.refreshTasks();else alert(`Task cancellation failed: ${a.error?.message||a.error||"Unknown error"}`)}catch(a){alert(`Error cancelling task: ${a.message}`)}}async openTaskInspectorModal(e){this.closeModals();let t=document.getElementById("modal-task-inspector");if(!t)return;let a=document.getElementById("modal-task-title"),n=document.getElementById("modal-task-body"),o=document.getElementById("modal-task-footer");if(a)a.textContent=`Task Inspector: ${e}`;if(n)n.innerHTML=`
⏳ Fetching task execution record...
- `;t.classList.add("active");try{let r=await h.getTask(e);if(!r.ok||!r.task){if(o)o.innerHTML=` + `;t.classList.add("active");try{let r=await h.getTask(e);if(!r.ok||!r.task){if(n)n.innerHTML=`
Failed to load task snapshot
-
${f(r.error?.message||"Task not found in runtime registry")}
+
${i(r.error?.message||"Task not found in runtime registry")}
- `;return}let s=r.task,i=s.progress!==void 0?Math.round(s.progress*100):s.status==="completed"?100:s.status==="working"?50:0,l=Math.floor(Date.now()/1000),u=s.expiresAtEpochSecs?Math.max(0,s.expiresAtEpochSecs-l):s.ttlSeconds||300,p=s.status==="completed"||s.status==="cancelled"||s.status==="failed",g=s.status==="completed"?"var(--green-400)":s.status==="working"?"var(--cyan-400)":s.status==="input_required"?"var(--amber-300)":s.status==="cancelled"?"var(--text-muted)":"var(--red-400)",m=s.status==="completed"?"rgba(52, 211, 153, 0.15)":s.status==="working"?"rgba(56, 189, 248, 0.15)":s.status==="input_required"?"rgba(245, 158, 11, 0.18)":s.status==="cancelled"?"rgba(148, 163, 184, 0.15)":"rgba(248, 113, 113, 0.15)",d=!!s.error,y=s.result!==void 0&&s.result!==null,b=s.inputRequests&&Object.keys(s.inputRequests).length>0;if(o)o.innerHTML=` + `;return}let s=r.task,l=s.progress!==void 0?Math.round(s.progress*100):s.status==="completed"?100:s.status==="working"?50:0,d=Math.floor(Date.now()/1000),g=s.expiresAtEpochSecs?Math.max(0,s.expiresAtEpochSecs-d):s.ttlSeconds||300,u=s.status==="completed"||s.status==="cancelled"||s.status==="failed",m=s.status==="completed"?"var(--green-400)":s.status==="working"?"var(--cyan-400)":s.status==="input_required"?"var(--amber-300)":s.status==="cancelled"?"var(--text-muted)":"var(--red-400)",v=s.status==="completed"?"rgba(52, 211, 153, 0.15)":s.status==="working"?"rgba(56, 189, 248, 0.15)":s.status==="input_required"?"rgba(245, 158, 11, 0.18)":s.status==="cancelled"?"rgba(148, 163, 184, 0.15)":"rgba(248, 113, 113, 0.15)",c=!!s.error,y=s.result!==void 0&&s.result!==null,b=s.inputRequests&&Object.keys(s.inputRequests).length>0;if(n)n.innerHTML=`
- - ${f(s.status.toUpperCase())} + + ${i(s.status.toUpperCase())} - ${f(s.capabilityId||"Tool Execution")} + ${i(s.capabilityId||"Tool Execution")} - ${s.serverId?`via ${f(s.serverId)}`:""} + ${s.serverId?`via ${i(s.serverId)}`:""}
- Task ID: ${f(s.taskId)} + Task ID: ${i(s.taskId)}
Created: ${s.createdAtEpochSecs?new Date(s.createdAtEpochSecs*1000).toLocaleString():s.createdAt?new Date(s.createdAt).toLocaleString():"—"}
- ${!p?`
TTL: ${u}s remaining
`:""} + ${!u?`
TTL: ${g}s remaining
`:""}
@@ -1928,29 +1928,29 @@ Reference: ${s.uri}`),e)e.value="";if(t)t.value="";await this.refreshData()}else
Execution Progress - ${i}% ${s.total?`(step ${Math.round((s.progress||0)*s.total)} of ${s.total})`:""} + ${l}% ${s.total?`(step ${Math.round((s.progress||0)*s.total)} of ${s.total})`:""}
-
+
${s.context?`
- ${s.context.actor_id?`
Actor: ${f(s.context.actor_id)}
`:""} - ${s.context.operation_id?`
Operation: ${f(s.context.operation_id)}
`:""} - ${s.context.grant_id?`
Grant: ${f(s.context.grant_id)}
`:""} + ${s.context.actor_id?`
Actor: ${i(s.context.actor_id)}
`:""} + ${s.context.operation_id?`
Operation: ${i(s.context.operation_id)}
`:""} + ${s.context.grant_id?`
Grant: ${i(s.context.grant_id)}
`:""}
`:""} - ${d?` + ${c?`
⚠️ Failure / Cancellation Trace
-
${f(typeof s.error==="string"?s.error:JSON.stringify(s.error,null,2))}
+
${i(typeof s.error==="string"?s.error:JSON.stringify(s.error,null,2))}
`:""} @@ -1960,7 +1960,7 @@ Reference: ${s.uri}`),e)e.value="";if(t)t.value="";await this.refreshData()}else
⚡ Pending Input Requests (MRTR / HITL)
-
${f(JSON.stringify(s.inputRequests,null,2))}
+
${i(JSON.stringify(s.inputRequests,null,2))}
`:""} @@ -1969,103 +1969,103 @@ Reference: ${s.uri}`),e)e.value="";if(t)t.value="";await this.refreshData()}else
${y?"\uD83D\uDCE6 Output Result Payload":"State Details"}
-
${f(y?JSON.stringify(s.result,null,2):s.status==="working"?"Task execution is currently in-flight in background worker pool.":"No output payload recorded.")}
+
${i(y?JSON.stringify(s.result,null,2):s.status==="working"?"Task execution is currently in-flight in background worker pool.":"No output payload recorded.")}
- `;if(n)n.innerHTML=` + `;if(o)o.innerHTML=`
- ${!p?` - `:` - Task is terminal (${f(s.status)}) + Task is terminal (${i(s.status)}) `}
-
- `}catch(r){if(o)o.innerHTML=` + `}catch(r){if(n)n.innerHTML=`
- Failed to inspect task: ${f(r.message)} + Failed to inspect task: ${i(r.message)}
- `}}async submitApproval(e){let t=document.getElementById(`appr-operator-${e}`),a=document.getElementById(`appr-args-${e}`),o=t?.value.trim()||"security-operator",n=void 0;if(a&&a.value.trim())try{n=JSON.parse(a.value.trim())}catch{alert("Invalid JSON in arguments editor");return}let r=await h.approveTicket(e,o,n);if(r.ok)await this.refreshApprovals(),await this.refreshTasks();else alert(`Approval failed: ${r.error||"Unknown error"}`)}async promptReject(e){let t=prompt("Reason for rejection (will be returned to the calling agent):");if(t===null)return;let o=document.getElementById(`appr-operator-${e}`)?.value.trim()||"security-operator",n=await h.rejectTicket(e,o,t);if(n.ok)await this.refreshApprovals(),await this.refreshTasks();else alert(`Rejection failed: ${n.error||"Unknown error"}`)}setPlaygroundMode(e){c.setState({playgroundMode:e})}selectCapability(e){c.setState({selectedCapabilityId:e});let t=c.getState().capabilities.find((o)=>o.id===e),a=document.getElementById("pg-args-input");if(t){let o=ee(t.input_schema,!1),n=JSON.stringify(o,null,2);if(a)a.value=n;let r={...c.getState().playgroundArgs||{}};r[e]=n,c.getState().playgroundArgs=r}}selectResource(e){c.setState({selectedResourceId:e})}selectPrompt(e){c.setState({selectedPromptId:e})}filterResources(e){let t=e.toLowerCase().trim(),o=(c.getState().resources||[]).filter((r)=>r.id.toLowerCase().includes(t)||r.name&&r.name.toLowerCase().includes(t)||r.uri&&r.uri.toLowerCase().includes(t)||r.server&&r.server.toLowerCase().includes(t)),n=document.getElementById("pg-res-list");if(n)if(o.length===0)n.innerHTML=` + `}}async submitApproval(e){let t=document.getElementById(`appr-operator-${e}`),a=document.getElementById(`appr-args-${e}`),n=t?.value.trim()||"security-operator",o=void 0;if(a&&a.value.trim())try{o=JSON.parse(a.value.trim())}catch{alert("Invalid JSON in arguments editor");return}let r=await h.approveTicket(e,n,o);if(r.ok)await this.refreshApprovals(),await this.refreshTasks();else alert(`Approval failed: ${r.error||"Unknown error"}`)}async promptReject(e){let t=prompt("Reason for rejection (will be returned to the calling agent):");if(t===null)return;let n=document.getElementById(`appr-operator-${e}`)?.value.trim()||"security-operator",o=await h.rejectTicket(e,n,t);if(o.ok)await this.refreshApprovals(),await this.refreshTasks();else alert(`Rejection failed: ${o.error||"Unknown error"}`)}setPlaygroundMode(e){p.setState({playgroundMode:e})}selectCapability(e){p.setState({selectedCapabilityId:e});let t=p.getState().capabilities.find((n)=>n.id===e),a=document.getElementById("pg-args-input");if(t){let n=K(t.input_schema,!1),o=JSON.stringify(n,null,2);if(a)a.value=o;let r={...p.getState().playgroundArgs||{}};r[e]=o,p.getState().playgroundArgs=r}}selectResource(e){p.setState({selectedResourceId:e})}selectPrompt(e){p.setState({selectedPromptId:e})}filterResources(e){let t=e.toLowerCase().trim(),n=(p.getState().resources||[]).filter((r)=>r.id.toLowerCase().includes(t)||r.name&&r.name.toLowerCase().includes(t)||r.uri&&r.uri.toLowerCase().includes(t)||r.server&&r.server.toLowerCase().includes(t)),o=document.getElementById("pg-res-list");if(o)if(n.length===0)o.innerHTML=`
- No resources match "${f(e)}" + No resources match "${i(e)}"
- `;else n.innerHTML=o.map((r)=>{let s=r.id===c.getState().selectedResourceId?"active":"",i=r.uri?r.uri.split(":")[0]:"res";return` -
+ `;else o.innerHTML=n.map((r)=>{let s=r.id===p.getState().selectedResourceId?"active":"",l=r.uri?r.uri.split(":")[0]:"res";return` +
- ${f(r.name||r.id)} - ${f(i)} + ${i(r.name||r.id)} + ${i(l)}
-
${f(r.uri)}
+
${i(r.uri)}
- server: ${f(r.server||"local")} - ${f(r.mime_type||"text/plain")} + server: ${i(r.server||"local")} + ${i(r.mime_type||"text/plain")}
- `}).join("")}filterPrompts(e){let t=e.toLowerCase().trim(),o=(c.getState().prompts||[]).filter((r)=>r.id.toLowerCase().includes(t)||r.name&&r.name.toLowerCase().includes(t)||r.description&&r.description.toLowerCase().includes(t)||r.server&&r.server.toLowerCase().includes(t)),n=document.getElementById("pg-prompt-list");if(n)if(o.length===0)n.innerHTML=` + `}).join("")}filterPrompts(e){let t=e.toLowerCase().trim(),n=(p.getState().prompts||[]).filter((r)=>r.id.toLowerCase().includes(t)||r.name&&r.name.toLowerCase().includes(t)||r.description&&r.description.toLowerCase().includes(t)||r.server&&r.server.toLowerCase().includes(t)),o=document.getElementById("pg-prompt-list");if(o)if(n.length===0)o.innerHTML=`
- No prompts match "${f(e)}" + No prompts match "${i(e)}"
- `;else n.innerHTML=o.map((r)=>{let s=r.id===c.getState().selectedPromptId?"active":"",i=r.arguments?r.arguments.length:0;return` -
+ `;else o.innerHTML=n.map((r)=>{let s=r.id===p.getState().selectedPromptId?"active":"",l=r.arguments?r.arguments.length:0;return` +
- ${f(r.name||r.id)} - ${i} args + ${i(r.name||r.id)} + ${l} args
-
${f(r.description||r.title||"Prompt template")}
-
server: ${f(r.server||"local")}
+
${i(r.description||r.title||"Prompt template")}
+
server: ${i(r.server||"local")}
- `}).join("")}updatePlaygroundArgs(e){let t=c.getState(),a=t.selectedCapabilityId||(t.capabilities[0]?t.capabilities[0].id:null);if(!a)return;let o={...t.playgroundArgs||{}};o[a]=e,t.playgroundArgs=o}fillPlaygroundSampleArgs(e=!1){let t=c.getState(),a=t.selectedCapabilityId||(t.capabilities[0]?t.capabilities[0].id:null),o=t.capabilities.find((i)=>i.id===a),n=document.getElementById("pg-args-input");if(!n)return;if(!o||!o.input_schema){if(n.value="{}",a){let i={...t.playgroundArgs||{}};i[a]="{}",t.playgroundArgs=i}return}let r=ee(o.input_schema,e),s=JSON.stringify(r,null,2);if(n.value=s,a){let i={...t.playgroundArgs||{}};i[a]=s,t.playgroundArgs=i}}formatPlaygroundArgs(){let e=c.getState(),t=e.selectedCapabilityId||(e.capabilities[0]?e.capabilities[0].id:null),a=document.getElementById("pg-args-input");if(a)try{let o=JSON.parse(a.value||"{}"),n=JSON.stringify(o,null,2);if(a.value=n,t){let r={...e.playgroundArgs||{}};r[t]=n,e.playgroundArgs=r}}catch(o){alert(`Cannot format JSON: ${o.message}`)}}insertPlaygroundArgKey(e,t,a){let o=c.getState(),n=o.selectedCapabilityId||(o.capabilities[0]?o.capabilities[0].id:null),r=document.getElementById("pg-args-input");if(r){let s={};try{s=JSON.parse(r.value||"{}")}catch{s={}}if(s[e]===void 0)if(a!==null&&a!==void 0)s[e]=a;else switch(t){case"string":s[e]=`sample_${e}`;break;case"number":case"integer":s[e]=0;break;case"boolean":s[e]=!0;break;case"array":s[e]=[];break;case"object":s[e]={};break;default:s[e]=`sample_${e}`}let i=JSON.stringify(s,null,2);if(r.value=i,n){let l={...o.playgroundArgs||{}};l[n]=i,o.playgroundArgs=l}}}fillBatchStepSampleArgs(e){let t=c.getState(),a=[...t.batchSteps||[]],o=a[e];if(!o||!o.capability_id)return;let n=t.capabilities.find((l)=>l.id===o.capability_id);if(!n||!n.input_schema)return;let r=n.input_schema.properties||{},s={};for(let[l,u]of Object.entries(r))if(u.default!==void 0)s[l]=u.default;else if(Array.isArray(u.enum)&&u.enum.length>0)s[l]=u.enum[0];else switch(u.type||"string"){case"string":s[l]=`sample_${l}`;break;case"number":case"integer":s[l]=0;break;case"boolean":s[l]=!0;break;case"array":s[l]=[];break;case"object":s[l]={};break;default:s[l]=`sample_${l}`}let i=JSON.stringify(s,null,2);a[e]={...a[e],argsJson:i},c.setState({batchSteps:a})}filterCapabilities(e){let t=e.toLowerCase().trim(),o=c.getState().capabilities.filter((r)=>r.id.toLowerCase().includes(t)||r.summary&&r.summary.toLowerCase().includes(t)||r.server&&r.server.toLowerCase().includes(t)),n=document.getElementById("pg-cap-list");if(n)if(o.length===0)n.innerHTML=` + `}).join("")}updatePlaygroundArgs(e){let t=p.getState(),a=t.selectedCapabilityId||(t.capabilities[0]?t.capabilities[0].id:null);if(!a)return;let n={...t.playgroundArgs||{}};n[a]=e,t.playgroundArgs=n}fillPlaygroundSampleArgs(e=!1){let t=p.getState(),a=t.selectedCapabilityId||(t.capabilities[0]?t.capabilities[0].id:null),n=t.capabilities.find((l)=>l.id===a),o=document.getElementById("pg-args-input");if(!o)return;if(!n||!n.input_schema){if(o.value="{}",a){let l={...t.playgroundArgs||{}};l[a]="{}",t.playgroundArgs=l}return}let r=K(n.input_schema,e),s=JSON.stringify(r,null,2);if(o.value=s,a){let l={...t.playgroundArgs||{}};l[a]=s,t.playgroundArgs=l}}formatPlaygroundArgs(){let e=p.getState(),t=e.selectedCapabilityId||(e.capabilities[0]?e.capabilities[0].id:null),a=document.getElementById("pg-args-input");if(a)try{let n=JSON.parse(a.value||"{}"),o=JSON.stringify(n,null,2);if(a.value=o,t){let r={...e.playgroundArgs||{}};r[t]=o,e.playgroundArgs=r}}catch(n){alert(`Cannot format JSON: ${n.message}`)}}insertPlaygroundArgKey(e,t,a){let n=p.getState(),o=n.selectedCapabilityId||(n.capabilities[0]?n.capabilities[0].id:null),r=document.getElementById("pg-args-input");if(r){let s={};try{s=JSON.parse(r.value||"{}")}catch{s={}}if(s[e]===void 0)if(a!==null&&a!==void 0)s[e]=a;else switch(t){case"string":s[e]=`sample_${e}`;break;case"number":case"integer":s[e]=0;break;case"boolean":s[e]=!0;break;case"array":s[e]=[];break;case"object":s[e]={};break;default:s[e]=`sample_${e}`}let l=JSON.stringify(s,null,2);if(r.value=l,o){let d={...n.playgroundArgs||{}};d[o]=l,n.playgroundArgs=d}}}fillBatchStepSampleArgs(e){let t=p.getState(),a=[...t.batchSteps||[]],n=a[e];if(!n||!n.capability_id)return;let o=t.capabilities.find((d)=>d.id===n.capability_id);if(!o||!o.input_schema)return;let r=o.input_schema.properties||{},s={};for(let[d,g]of Object.entries(r))if(g.default!==void 0)s[d]=g.default;else if(Array.isArray(g.enum)&&g.enum.length>0)s[d]=g.enum[0];else switch(g.type||"string"){case"string":s[d]=`sample_${d}`;break;case"number":case"integer":s[d]=0;break;case"boolean":s[d]=!0;break;case"array":s[d]=[];break;case"object":s[d]={};break;default:s[d]=`sample_${d}`}let l=JSON.stringify(s,null,2);a[e]={...a[e],argsJson:l},p.setState({batchSteps:a})}filterCapabilities(e){let t=e.toLowerCase().trim(),n=p.getState().capabilities.filter((r)=>r.id.toLowerCase().includes(t)||r.summary&&r.summary.toLowerCase().includes(t)||r.server&&r.server.toLowerCase().includes(t)),o=document.getElementById("pg-cap-list");if(o)if(n.length===0)o.innerHTML=`
- No capabilities match "${f(e)}" + No capabilities match "${i(e)}"
- `;else n.innerHTML=o.map((r)=>` -
+ `;else o.innerHTML=n.map((r)=>` +
- ${f(r.id)} - ${f(r.mode||"read")} + ${i(r.id)} + ${i(r.mode||"read")}
-
server: ${f(r.server||"local")}
+
server: ${i(r.server||"local")}
- `).join("")}async executePlaygroundTool(){let e=c.getState(),t=e.selectedCapabilityId||(e.capabilities[0]?e.capabilities[0].id:null);if(!t)return;let a=document.getElementById("pg-args-input")?.value||"{}",o=document.getElementById("pg-context-input")?.value||void 0,n=document.getElementById("pg-jsonpath-input")?.value.trim()||void 0,r=document.getElementById("pg-limit-lines-input")?.value.trim()||void 0,s=document.getElementById("pg-truncate-bytes-input")?.value.trim()||void 0,i={};try{i=JSON.parse(a)}catch{alert("Invalid arguments JSON object");return}if(n)i._jsonpath=n;if(r&&!isNaN(Number(r)))i._limit_lines=Number(r);if(s&&!isNaN(Number(s)))i._truncate_bytes=Number(s);let l=`op-${Date.now()}`;c.setState({isExecuting:!0,activeRequestId:l});let u=e.activeProfile||void 0,p=e.playgroundAsyncTask||!1;try{let g=await h.callCapability({capability_id:t,args:i,request_id:l,async_task:p?!0:void 0,context:{operation_id:o||l}},u);if(c.setState({isExecuting:!1,activeRequestId:null,executionResult:{status:g.status,durationMs:g.durationMs,data:g.data}}),g.status===202||g.data?.resultType==="task")this.refreshTasks();c.addEventLog("POST",`/v1/tools/call → ${t}`,g.status===200?"200 OK":`HTTP ${g.status}`,`${g.durationMs.toFixed(1)}ms`),h.getConfig().then((m)=>{if(m.ok&&m.circuit_breakers)c.setState({circuitBreakers:m.circuit_breakers})})}catch(g){c.setState({isExecuting:!1,activeRequestId:null,executionResult:{status:500,durationMs:0,data:{error:g.toString()}}})}}async cancelActiveOperation(){let t=c.getState().activeRequestId;if(t)try{await h.cancelOperation(t)}catch(a){console.warn("Failed to send cancel signal:",a)}c.setState({isExecuting:!1,activeRequestId:null,executionResult:{status:499,durationMs:0,data:{ok:!1,error:{code:"CANCELLED",message:"Operation cancelled by operator"}}}})}openBatchModal(){let e=c.getState(),t=e.batchSteps;if(!t||t.length===0)t=[{id:"step_1",capability_id:e.selectedCapabilityId||(e.capabilities[0]?e.capabilities[0].id:""),argsJson:"{}",continue_on_error:!1},{id:"step_2",capability_id:"",argsJson:"{}",continue_on_error:!0}],c.setState({batchSteps:t});c.setState({isBatchModalOpen:!0})}closeBatchModal(){c.setState({isBatchModalOpen:!1})}addBatchStep(){let t=[...c.getState().batchSteps||[]],a=t.length+1;t.push({id:`step_${a}`,capability_id:"",argsJson:"{}",continue_on_error:!1}),c.setState({batchSteps:t})}removeBatchStep(e){let a=[...c.getState().batchSteps||[]];if(a.length<=1){alert("Pipeline must contain at least one execution step.");return}a.splice(e,1);let o=a.map((n,r)=>({...n,id:`step_${r+1}`}));c.setState({batchSteps:o})}updateBatchStepCapability(e,t){let o=[...c.getState().batchSteps||[]];if(o[e])o[e]={...o[e],capability_id:t},c.setState({batchSteps:o})}updateBatchStepContinueOnError(e,t){let o=[...c.getState().batchSteps||[]];if(o[e])o[e]={...o[e],continue_on_error:t},c.setState({batchSteps:o})}updateBatchStepArgs(e,t){let a=c.getState(),o=[...a.batchSteps||[]];if(o[e])o[e]={...o[e],argsJson:t},a.batchSteps[e].argsJson=t}appendBatchVariable(e,t){let o=[...c.getState().batchSteps||[]],n=document.getElementById(`batch-step-args-${e}`);if(n){let r=n.value,s=n.selectionStart||r.length,i=n.selectionEnd||r.length,l=r.substring(0,s)+t+r.substring(i);if(n.value=l,o[e])o[e]={...o[e],argsJson:l},c.setState({batchSteps:o})}}async executeBatchPipeline(){let e=c.getState(),t=e.batchSteps||[],a=[];for(let n=0;n{let s=r,i=s.getAttribute("data-arg-name");if(i&&s.value.trim())o[i]=s.value.trim()});let n=e.activeProfile||void 0;try{let r=await h.getPrompt({prompt_id:t,arguments:o},n);c.setState({promptGetResult:{status:r.status,durationMs:r.durationMs,data:r.data}}),c.addEventLog("POST",`/v1/prompts/get → ${t}`,r.status===200?"200 OK":`HTTP ${r.status}`,`${r.durationMs.toFixed(1)}ms`)}catch(r){c.setState({promptGetResult:{status:500,durationMs:0,data:{error:r.toString()}}})}}toggleBatchPlayground(){let e=document.getElementById("pg-args-input");if(!e)return;let t=[{id:"step_1",capability_id:"sqlite.read_query",args:{query:"SELECT * FROM users LIMIT 2"}},{id:"step_2",capability_id:"github.issues.search",args:{query:"label:bug"},continue_on_error:!0}];e.value=JSON.stringify(t,null,2)}async submitPolicyRule(e){let t=e==="allow"?"policy-new-allow":e==="deny"?"policy-new-deny":e==="redact"?"policy-new-redact":"policy-new-requireApproval",a=document.getElementById(t);if(!a)return;let o=a.value.trim();if(!o)return;await this.addPolicyRule(e,o),a.value=""}async addPolicyRule(e,t){let a=(t||"").trim();if(!a)return;let o=c.getState(),n=o.activeProfile,r=n?o.config.profiles?.[n]:void 0;if(r&&n){let s=r.policy||{},i=[...s.allow||[]],l=[...s.deny||[]],u=[...s.redact_keys||s.redactKeys||[]],p=[...s.require_approval||s.requireApproval||[]];if(e==="allow"&&!i.includes(a))i.push(a);if(e==="deny"&&!l.includes(a))l.push(a);if(e==="redact"&&!u.includes(a))u.push(a);if(e==="requireApproval"&&!p.includes(a))p.push(a);let g={...s,allow:i,deny:l,redactKeys:u,requireApproval:p},m=await h.upsertProfile(n,r.servers,r.description,g);if(!m.ok)alert(`Failed to save profile policy rule: ${m.error||"Unknown error"}`)}else{let s=o.config.policy||{},i=[...s.allow||[]],l=[...s.deny||[]],u=[...s.redact_keys||s.redactKeys||[]],p=[...s.require_approval||s.requireApproval||[]];if(e==="allow"&&!i.includes(a))i.push(a);if(e==="deny"&&!l.includes(a))l.push(a);if(e==="redact"&&!u.includes(a))u.push(a);if(e==="requireApproval"&&!p.includes(a))p.push(a);let g=await h.savePolicy({...s,allow:i,deny:l,redact_keys:u,redactKeys:u,require_approval:p,requireApproval:p});if(!g.ok)alert(`Failed to save policy rule: ${g.error||"Unknown error"}`)}await this.refreshData()}async removePolicyRule(e,t){let a=c.getState(),o=a.activeProfile,n=o?a.config.profiles?.[o]:void 0;if(n&&o){let r=n.policy||{},s=[...r.allow||[]],i=[...r.deny||[]],l=[...r.redact_keys||r.redactKeys||[]],u=[...r.require_approval||r.requireApproval||[]];if(e==="allow")s.splice(t,1);if(e==="deny")i.splice(t,1);if(e==="redact")l.splice(t,1);if(e==="requireApproval")u.splice(t,1);let p={...r,allow:s,deny:i,redactKeys:l,requireApproval:u},g=await h.upsertProfile(o,n.servers,n.description,p);if(!g.ok)alert(`Failed to update profile policy: ${g.error||"Unknown error"}`)}else{let r=a.config.policy||{},s=[...r.allow||[]],i=[...r.deny||[]],l=[...r.redact_keys||r.redactKeys||[]],u=[...r.require_approval||r.requireApproval||[]];if(e==="allow")s.splice(t,1);if(e==="deny")i.splice(t,1);if(e==="redact")l.splice(t,1);if(e==="requireApproval")u.splice(t,1);let p=await h.savePolicy({...r,allow:s,deny:i,redact_keys:l,redactKeys:l,require_approval:u,requireApproval:u});if(!p.ok)alert(`Failed to update policy: ${p.error||"Unknown error"}`)}await this.refreshData()}async saveWebhookConfig(){let e=document.getElementById("policy-webhook-url"),t=document.getElementById("policy-webhook-format"),a=document.getElementById("policy-webhook-secret"),o=e?e.value.trim():"",n=t?t.value:"generic",r=a?a.value.trim():"",i=c.getState().config.policy||{},l=o?{url:o,format:n,secret:r&&!r.startsWith("WARMPLANE_")&&!r.includes("_")?r:void 0,secret_env:r&&(r.startsWith("WARMPLANE_")||r.includes("_"))?r:void 0,events:["approval.requested","circuit_breaker.tripped","policy.violation"]}:void 0,u=await h.savePolicy({...i,webhook:l});if(u.ok)alert("Webhook settings saved successfully");else alert(`Failed to save webhook settings: ${u.error||"Unknown error"}`);await this.refreshData()}async testWebhook(){let e=document.getElementById("policy-webhook-url"),t=document.getElementById("policy-webhook-format"),a=e?e.value.trim():void 0,o=t?t.value:void 0,n=c.getState(),r=typeof n.config.policy?.webhook==="object"?n.config.policy.webhook?.url:void 0;if(a&&a!==r)await this.saveWebhookConfig();let s=document.getElementById("policy-webhook-status");if(s)s.textContent="Sending test event...",s.style.color="var(--cyan-400)";try{let i=await h.testWebhook(a,o);if(i.ok){if(alert(`Test webhook sent successfully! (${i.message})`),s)s.textContent=`✔ Test sent (HTTP ${i.status_code||200})`,s.style.color="var(--green-400)"}else if(alert(`Test webhook failed: ${i.error||"Unknown error"}`),s)s.textContent=`✖ Failed: ${i.error}`,s.style.color="var(--red-400)"}catch(i){alert(`Error sending test webhook: ${i.message}`)}}testPolicySandbox(e){let t=document.getElementById("policy-test-verdict");if(!t)return;let a=e.trim();if(!a){t.textContent="ENTER ID",t.style.color="var(--text-dim)";return}let o=c.getState(),n=o.activeProfile,r=n?o.config.profiles?.[n]:void 0,s=o.config.policy||{},i=r?.policy,l=s.deny||[],u=i?.deny||[],p=Array.from(new Set([...l,...u])),g=s.require_approval||s.requireApproval||[],m=i?.require_approval||i?.requireApproval||[],d=Array.from(new Set([...g,...m])),y=i&&i.allow&&i.allow.length>0?i.allow:s.allow||[],b=(v,x)=>{if(v==="*")return!0;if(v.endsWith("*"))return x.startsWith(v.slice(0,-1));return v===x};if(p.some((v)=>b(v,a))){t.textContent="DENIED (Strict Block)",t.style.color="var(--red-400)";return}if(y.length>0&&!y.some((v)=>b(v,a))){t.textContent="DENIED (Not in Allow List)",t.style.color="var(--red-400)";return}if(d.some((v)=>b(v,a))){t.textContent="REQUIRE APPROVAL (HITL Gate)",t.style.color="var(--amber-400)";return}t.textContent="ALLOWED",t.style.color="var(--green-400)"}async deleteServer(e){let t=c.getState().activeProfile,a=t?`Permanently delete server '${e}' globally from warmplane configuration? (This will also unbind it from profile '${t}')`:`Are you sure you want to permanently remove server '${e}' from configuration?`;if(!confirm(a))return;try{let o=await h.deleteServer(e);if(o.ok)await this.refreshData();else alert(`Failed to remove server '${e}': ${o.error||"Unknown error"}`)}catch(o){alert(`Error removing server '${e}': ${o.message}`)}}async restartServer(e){try{let t=await h.restartServer(e);if(t.ok)await this.refreshData();else alert(`Failed to restart server '${e}': ${t.error||"Unknown error"}`)}catch(t){alert(`Error restarting server '${e}': ${t.message}`)}}openServerDiagnosticsModal(e){this.closeModals();let t=c.getState(),a=t.config.mcpServers?.[e],o=t.serverStatuses?.[e],n=(t.circuitBreakers||[]).find((l)=>l.server_id===e),r=document.getElementById("modal-server-diagnostics");if(!r)return;let s=document.getElementById("modal-diag-title"),i=document.getElementById("modal-diag-body");if(s)s.textContent=`Live Diagnostics: ${e}`;if(i){let l=o?.status==="degraded",u=l?"var(--amber-400)":o?.status==="connected"?"var(--green-400)":"var(--red-400)",p=o?.error||"No active crash or error reported. Server is healthy.",g=Q(e,a?.command,a?.args),m=a?.env||{},d=Object.keys(m),b=(g?.envFields||[]).filter((x)=>x.required).filter((x)=>!d.includes(x.key)),v="";if(g||d.length>0){let x=(g?.envFields||[]).map((T)=>{let w=m[T.key]!==void 0,z='Configured';if(!w&&T.required)z='Required / Missing';else if(!w)z='Optional / Not Set';return` + `).join("")}async executePlaygroundTool(){let e=p.getState(),t=e.selectedCapabilityId||(e.capabilities[0]?e.capabilities[0].id:null);if(!t)return;let a=document.getElementById("pg-args-input")?.value||"{}",n=document.getElementById("pg-context-input")?.value||void 0,o=document.getElementById("pg-jsonpath-input")?.value.trim()||void 0,r=document.getElementById("pg-limit-lines-input")?.value.trim()||void 0,s=document.getElementById("pg-truncate-bytes-input")?.value.trim()||void 0,l={};try{l=JSON.parse(a)}catch{alert("Invalid arguments JSON object");return}if(o)l._jsonpath=o;if(r&&!isNaN(Number(r)))l._limit_lines=Number(r);if(s&&!isNaN(Number(s)))l._truncate_bytes=Number(s);let d=`op-${Date.now()}`;p.setState({isExecuting:!0,activeRequestId:d});let g=e.activeProfile||void 0,u=e.playgroundAsyncTask||!1;try{let m=await h.callCapability({capability_id:t,args:l,request_id:d,async_task:u?!0:void 0,context:{operation_id:n||d}},g);if(p.setState({isExecuting:!1,activeRequestId:null,executionResult:{status:m.status,durationMs:m.durationMs,data:m.data}}),m.status===202||m.data?.resultType==="task")this.refreshTasks();p.addEventLog("POST",`/v1/tools/call → ${t}`,m.status===200?"200 OK":`HTTP ${m.status}`,`${m.durationMs.toFixed(1)}ms`),h.getConfig().then((v)=>{if(v.ok&&v.circuit_breakers)p.setState({circuitBreakers:v.circuit_breakers})})}catch(m){p.setState({isExecuting:!1,activeRequestId:null,executionResult:{status:500,durationMs:0,data:{error:m.toString()}}})}}async cancelActiveOperation(){let t=p.getState().activeRequestId;if(t)try{await h.cancelOperation(t)}catch(a){console.warn("Failed to send cancel signal:",a)}p.setState({isExecuting:!1,activeRequestId:null,executionResult:{status:499,durationMs:0,data:{ok:!1,error:{code:"CANCELLED",message:"Operation cancelled by operator"}}}})}openBatchModal(){let e=p.getState(),t=e.batchSteps;if(!t||t.length===0)t=[{id:"step_1",capability_id:e.selectedCapabilityId||(e.capabilities[0]?e.capabilities[0].id:""),argsJson:"{}",continue_on_error:!1},{id:"step_2",capability_id:"",argsJson:"{}",continue_on_error:!0}],p.setState({batchSteps:t});p.setState({isBatchModalOpen:!0})}closeBatchModal(){p.setState({isBatchModalOpen:!1})}addBatchStep(){let t=[...p.getState().batchSteps||[]],a=t.length+1;t.push({id:`step_${a}`,capability_id:"",argsJson:"{}",continue_on_error:!1}),p.setState({batchSteps:t})}removeBatchStep(e){let a=[...p.getState().batchSteps||[]];if(a.length<=1){alert("Pipeline must contain at least one execution step.");return}a.splice(e,1);let n=a.map((o,r)=>({...o,id:`step_${r+1}`}));p.setState({batchSteps:n})}updateBatchStepCapability(e,t){let n=[...p.getState().batchSteps||[]];if(n[e])n[e]={...n[e],capability_id:t},p.setState({batchSteps:n})}updateBatchStepContinueOnError(e,t){let n=[...p.getState().batchSteps||[]];if(n[e])n[e]={...n[e],continue_on_error:t},p.setState({batchSteps:n})}updateBatchStepArgs(e,t){let a=p.getState(),n=[...a.batchSteps||[]];if(n[e])n[e]={...n[e],argsJson:t},a.batchSteps[e].argsJson=t}appendBatchVariable(e,t){let n=[...p.getState().batchSteps||[]],o=document.getElementById(`batch-step-args-${e}`);if(o){let r=o.value,s=o.selectionStart||r.length,l=o.selectionEnd||r.length,d=r.substring(0,s)+t+r.substring(l);if(o.value=d,n[e])n[e]={...n[e],argsJson:d},p.setState({batchSteps:n})}}async executeBatchPipeline(){let e=p.getState(),t=e.batchSteps||[],a=[];for(let o=0;o{let s=r,l=s.getAttribute("data-arg-name");if(l&&s.value.trim())n[l]=s.value.trim()});let o=e.activeProfile||void 0;try{let r=await h.getPrompt({prompt_id:t,arguments:n},o);p.setState({promptGetResult:{status:r.status,durationMs:r.durationMs,data:r.data}}),p.addEventLog("POST",`/v1/prompts/get → ${t}`,r.status===200?"200 OK":`HTTP ${r.status}`,`${r.durationMs.toFixed(1)}ms`)}catch(r){p.setState({promptGetResult:{status:500,durationMs:0,data:{error:r.toString()}}})}}toggleBatchPlayground(){let e=document.getElementById("pg-args-input");if(!e)return;let t=[{id:"step_1",capability_id:"sqlite.read_query",args:{query:"SELECT * FROM users LIMIT 2"}},{id:"step_2",capability_id:"github.issues.search",args:{query:"label:bug"},continue_on_error:!0}];e.value=JSON.stringify(t,null,2)}async submitPolicyRule(e){let t=e==="allow"?"policy-new-allow":e==="deny"?"policy-new-deny":e==="redact"?"policy-new-redact":"policy-new-requireApproval",a=document.getElementById(t);if(!a)return;let n=a.value.trim();if(!n)return;await this.addPolicyRule(e,n),a.value=""}async addPolicyRule(e,t){let a=(t||"").trim();if(!a)return;let n=p.getState(),o=n.activeProfile,r=o?n.config.profiles?.[o]:void 0;if(r&&o){let s=r.policy||{},l=[...s.allow||[]],d=[...s.deny||[]],g=[...s.redact_keys||s.redactKeys||[]],u=[...s.require_approval||s.requireApproval||[]];if(e==="allow"&&!l.includes(a))l.push(a);if(e==="deny"&&!d.includes(a))d.push(a);if(e==="redact"&&!g.includes(a))g.push(a);if(e==="requireApproval"&&!u.includes(a))u.push(a);let m={...s,allow:l,deny:d,redactKeys:g,requireApproval:u},v=await h.upsertProfile(o,r.servers,r.description,m);if(!v.ok)alert(`Failed to save profile policy rule: ${v.error||"Unknown error"}`)}else{let s=n.config.policy||{},l=[...s.allow||[]],d=[...s.deny||[]],g=[...s.redact_keys||s.redactKeys||[]],u=[...s.require_approval||s.requireApproval||[]];if(e==="allow"&&!l.includes(a))l.push(a);if(e==="deny"&&!d.includes(a))d.push(a);if(e==="redact"&&!g.includes(a))g.push(a);if(e==="requireApproval"&&!u.includes(a))u.push(a);let m=await h.savePolicy({...s,allow:l,deny:d,redact_keys:g,redactKeys:g,require_approval:u,requireApproval:u});if(!m.ok)alert(`Failed to save policy rule: ${m.error||"Unknown error"}`)}await this.refreshData()}async removePolicyRule(e,t){let a=p.getState(),n=a.activeProfile,o=n?a.config.profiles?.[n]:void 0;if(o&&n){let r=o.policy||{},s=[...r.allow||[]],l=[...r.deny||[]],d=[...r.redact_keys||r.redactKeys||[]],g=[...r.require_approval||r.requireApproval||[]];if(e==="allow")s.splice(t,1);if(e==="deny")l.splice(t,1);if(e==="redact")d.splice(t,1);if(e==="requireApproval")g.splice(t,1);let u={...r,allow:s,deny:l,redactKeys:d,requireApproval:g},m=await h.upsertProfile(n,o.servers,o.description,u);if(!m.ok)alert(`Failed to update profile policy: ${m.error||"Unknown error"}`)}else{let r=a.config.policy||{},s=[...r.allow||[]],l=[...r.deny||[]],d=[...r.redact_keys||r.redactKeys||[]],g=[...r.require_approval||r.requireApproval||[]];if(e==="allow")s.splice(t,1);if(e==="deny")l.splice(t,1);if(e==="redact")d.splice(t,1);if(e==="requireApproval")g.splice(t,1);let u=await h.savePolicy({...r,allow:s,deny:l,redact_keys:d,redactKeys:d,require_approval:g,requireApproval:g});if(!u.ok)alert(`Failed to update policy: ${u.error||"Unknown error"}`)}await this.refreshData()}async saveWebhookConfig(){let e=document.getElementById("policy-webhook-url"),t=document.getElementById("policy-webhook-format"),a=document.getElementById("policy-webhook-secret"),n=e?e.value.trim():"",o=t?t.value:"generic",r=a?a.value.trim():"",l=p.getState().config.policy||{},d=n?{url:n,format:o,secret:r&&!r.startsWith("WARMPLANE_")&&!r.includes("_")?r:void 0,secret_env:r&&(r.startsWith("WARMPLANE_")||r.includes("_"))?r:void 0,events:["approval.requested","circuit_breaker.tripped","policy.violation"]}:void 0,g=await h.savePolicy({...l,webhook:d});if(g.ok)alert("Webhook settings saved successfully");else alert(`Failed to save webhook settings: ${g.error||"Unknown error"}`);await this.refreshData()}async testWebhook(){let e=document.getElementById("policy-webhook-url"),t=document.getElementById("policy-webhook-format"),a=e?e.value.trim():void 0,n=t?t.value:void 0,o=p.getState(),r=typeof o.config.policy?.webhook==="object"?o.config.policy.webhook?.url:void 0;if(a&&a!==r)await this.saveWebhookConfig();let s=document.getElementById("policy-webhook-status");if(s)s.textContent="Sending test event...",s.style.color="var(--cyan-400)";try{let l=await h.testWebhook(a,n);if(l.ok){if(alert(`Test webhook sent successfully! (${l.message})`),s)s.textContent=`✔ Test sent (HTTP ${l.status_code||200})`,s.style.color="var(--green-400)"}else if(alert(`Test webhook failed: ${l.error||"Unknown error"}`),s)s.textContent=`✖ Failed: ${l.error}`,s.style.color="var(--red-400)"}catch(l){alert(`Error sending test webhook: ${l.message}`)}}testPolicySandbox(e){let t=document.getElementById("policy-test-verdict");if(!t)return;let a=e.trim();if(!a){t.textContent="ENTER ID",t.style.color="var(--text-dim)";return}let n=p.getState(),o=n.activeProfile,r=o?n.config.profiles?.[o]:void 0,s=n.config.policy||{},l=r?.policy,d=s.deny||[],g=l?.deny||[],u=Array.from(new Set([...d,...g])),m=s.require_approval||s.requireApproval||[],v=l?.require_approval||l?.requireApproval||[],c=Array.from(new Set([...m,...v])),y=l&&l.allow&&l.allow.length>0?l.allow:s.allow||[],b=(f,x)=>{if(f==="*")return!0;if(f.endsWith("*"))return x.startsWith(f.slice(0,-1));return f===x};if(u.some((f)=>b(f,a))){t.textContent="DENIED (Strict Block)",t.style.color="var(--red-400)";return}if(y.length>0&&!y.some((f)=>b(f,a))){t.textContent="DENIED (Not in Allow List)",t.style.color="var(--red-400)";return}if(c.some((f)=>b(f,a))){t.textContent="REQUIRE APPROVAL (HITL Gate)",t.style.color="var(--amber-400)";return}t.textContent="ALLOWED",t.style.color="var(--green-400)"}async deleteServer(e){let t=p.getState().activeProfile,a=t?`Permanently delete server '${e}' globally from warmplane configuration? (This will also unbind it from profile '${t}')`:`Are you sure you want to permanently remove server '${e}' from configuration?`;if(!confirm(a))return;try{let n=await h.deleteServer(e);if(n.ok)await this.refreshData();else alert(`Failed to remove server '${e}': ${n.error||"Unknown error"}`)}catch(n){alert(`Error removing server '${e}': ${n.message}`)}}async restartServer(e){try{let t=await h.restartServer(e);if(t.ok)await this.refreshData();else alert(`Failed to restart server '${e}': ${t.error||"Unknown error"}`)}catch(t){alert(`Error restarting server '${e}': ${t.message}`)}}openServerDiagnosticsModal(e){this.closeModals();let t=p.getState(),a=t.config.mcpServers?.[e],n=t.serverStatuses?.[e],o=(t.circuitBreakers||[]).find((d)=>d.server_id===e),r=document.getElementById("modal-server-diagnostics");if(!r)return;let s=document.getElementById("modal-diag-title"),l=document.getElementById("modal-diag-body");if(s)s.textContent=`Live Diagnostics: ${e}`;if(l){let d=n?.status==="degraded",g=d?"var(--amber-400)":n?.status==="connected"?"var(--green-400)":"var(--red-400)",u=n?.error||"No active crash or error reported. Server is healthy.",m=N(e,a?.command,a?.args),v=a?.env||{},c=Object.keys(v),b=(m?.envFields||[]).filter((x)=>x.required).filter((x)=>!c.includes(x.key)),f="";if(m||c.length>0){let x=(m?.envFields||[]).map((E)=>{let w=v[E.key]!==void 0,C='Configured';if(!w&&E.required)C='Required / Missing';else if(!w)C='Optional / Not Set';return`
- ${f(T.key)} - ${T.label?`(${f(T.label)})`:""} + ${i(E.key)} + ${E.label?`(${i(E.label)})`:""}
- ${z} + ${C} ${!w?` - + `:""}
- `}).join(""),A=(g?.envFields||[]).map((T)=>T.key),R=Object.entries(m).filter(([T])=>!A.includes(T)).map(([T,S])=>` + `}).join(""),T=(m?.envFields||[]).map((E)=>E.key),I=Object.entries(v).filter(([E])=>!T.includes(E)).map(([E,k])=>`
- ${f(T)} + ${i(E)} Custom Configured
- `).join("");v=` + `).join("");f=`
\uD83D\uDD11 Environment Variables & Secrets ${b.length>0?`⚠️ ${b.length} required key(s) missing`:""}
${x} - ${R} + ${I}
- `}i.innerHTML=` + `}l.innerHTML=`
- - Current Status: ${f(o?.status||"unknown")} - Protocol: ${f(o?.protocol_version||"2026-07-28")} + + Current Status: ${i(n?.status||"unknown")} + Protocol: ${i(n?.protocol_version||"2026-07-28")} ${b.length>0?'⚠️ Missing Required Keys':""}
@@ -2073,16 +2073,16 @@ Reference: ${s.uri}`),e)e.value="";if(t)t.value="";await this.refreshData()}else
⚠️ Diagnostic Details / Failure Root Cause
-
${f(p)}
+
${i(u)}
- ${v} + ${f}
Circuit Breaker State
- ${n?`${n.state.toUpperCase()} (${n.consecutive_failures} failures)`:"CLOSED (Healthy)"} + ${o?`${o.state.toUpperCase()} (${o.consecutive_failures} failures)`:"CLOSED (Healthy)"}
@@ -2096,71 +2096,71 @@ Reference: ${s.uri}`),e)e.value="";if(t)t.value="";await this.refreshData()}else
Configured Execution Target
- ${a?.command?`${f(a.command)} ${f((a.args||[]).join(" "))}`:f(a?.url||"")} + ${a?.command?`${i(a.command)} ${i((a.args||[]).join(" "))}`:i(a?.url||"")}
- +
- `}r.classList.add("active")}openAddServerModal(){this.closeModals();let e=document.getElementById("modal-srv-title"),t=document.getElementById("modal-srv-template-banner"),a=document.getElementById("modal-srv-name"),o=document.getElementById("modal-srv-transport"),n=document.getElementById("modal-srv-command"),r=document.getElementById("modal-srv-url"),s=document.getElementById("modal-srv-ft"),i=document.getElementById("modal-srv-cd"),l=document.getElementById("modal-srv-autorestart"),u=document.getElementById("modal-srv-maxrestarts");if(e)e.textContent="Add Upstream MCP Server";if(t)t.style.display="flex";if(a)a.value="",a.disabled=!1;if(o)o.value="stdio";if(n)n.value="";if(r)r.value="";let p=document.getElementById("modal-group-cmd"),g=document.getElementById("modal-group-url");if(p)p.style.display="block";if(g)g.style.display="none";if(s)s.value="3";if(i)i.value="30000";if(l)l.value="true";if(u)u.value="5";let m=document.getElementById("modal-add-server");if(m)m.classList.add("active")}openEditServerModal(e){this.closeModals();let t=c.getState(),a=t.config.mcpServers?.[e];if(!a){alert(`Server '${e}' not found in configuration.`);return}let o=document.getElementById("modal-srv-title"),n=document.getElementById("modal-srv-template-banner"),r=document.getElementById("modal-srv-name"),s=document.getElementById("modal-srv-transport"),i=document.getElementById("modal-srv-command"),l=document.getElementById("modal-srv-url"),u=document.getElementById("modal-srv-ft"),p=document.getElementById("modal-srv-cd"),g=document.getElementById("modal-srv-autorestart"),m=document.getElementById("modal-srv-maxrestarts");if(o)o.textContent=`Edit Server '${e}'`;if(n)n.style.display="none";if(r)r.value=e,r.disabled=!0;let d=!!a.command;if(s)s.value=d?"stdio":"http";let y=document.getElementById("modal-group-cmd"),b=document.getElementById("modal-group-url");if(y)y.style.display=d?"block":"none";if(b)b.style.display=d?"none":"block";if(i)i.value=d?`${a.command} ${(a.args||[]).join(" ")}`.trim():"";if(l)l.value=a.url||"";let v=a.resilience||t.config.resilience;if(u)u.value=String(v?.failureThreshold??3);if(p)p.value=String(v?.cooldownMs??30000);if(g)g.value=v?.autoRestart===!1?"false":"true";if(m)m.value=String(v?.maxRestarts??5);let x=document.getElementById("modal-add-server");if(x)x.classList.add("active")}async submitAddServer(){let e=document.getElementById("modal-srv-name"),t=e?.value.trim(),a=document.getElementById("modal-srv-transport")?.value;if(!t){alert("Server name is required");return}if(e&&!e.disabled){if((c.getState().config.mcpServers||{})[t]){if(!confirm(`Server '${t}' already exists in configuration. Do you want to overwrite it?`))return}}let o={};if(a==="stdio"){let p=(document.getElementById("modal-srv-command")?.value.trim()).split(/\s+/).filter(Boolean);if(p.length===0){alert("Command is required");return}o.command=p[0],o.args=p.slice(1)}else{let u=document.getElementById("modal-srv-url")?.value.trim();if(!u){alert("URL is required");return}o.url=u}let n=document.getElementById("modal-srv-ft")?.value.trim(),r=document.getElementById("modal-srv-cd")?.value.trim(),s=document.getElementById("modal-srv-autorestart")?.value,i=document.getElementById("modal-srv-maxrestarts")?.value.trim();if(n||r||s||i)o.resilience={failureThreshold:n?Number(n):3,cooldownMs:r?Number(r):30000,autoRestart:s!=="false",maxRestarts:i?Number(i):5};let l=await h.upsertServer(t,o);if(l.ok)this.closeModals(),await this.refreshData();else alert(`Failed to save server: ${l.error}`)}openTemplateCatalog(){this.closeModals();let e=document.getElementById("modal-templates");if(e)e.classList.add("active");this.renderTemplateGrid()}setTemplateCategory(e){this.activeTemplateCategory=e,document.querySelectorAll(".tmpl-cat-btn").forEach((t)=>{if(t.getAttribute("data-category")===e)t.classList.add("active"),t.style.background="var(--surface-elevated)",t.style.color="var(--amber-400)";else t.classList.remove("active"),t.style.background="var(--surface-card)",t.style.color="var(--text-main)"}),this.renderTemplateGrid()}filterTemplates(e){this.activeTemplateFilter=e.toLowerCase().trim(),this.renderTemplateGrid()}renderTemplateGrid(){let e=document.getElementById("tmpl-grid");if(!e)return;let t=X.filter((n)=>{let r=this.activeTemplateCategory==="all"||n.category===this.activeTemplateCategory,s=!this.activeTemplateFilter||n.name.toLowerCase().includes(this.activeTemplateFilter)||n.id.toLowerCase().includes(this.activeTemplateFilter)||n.description.toLowerCase().includes(this.activeTemplateFilter)||n.command.toLowerCase().includes(this.activeTemplateFilter)||n.envFields.some((i)=>i.key.toLowerCase().includes(this.activeTemplateFilter));return r&&s});if(t.length===0){e.innerHTML=` + `}r.classList.add("active")}openAddServerModal(){this.closeModals();let e=document.getElementById("modal-srv-title"),t=document.getElementById("modal-srv-template-banner"),a=document.getElementById("modal-srv-name"),n=document.getElementById("modal-srv-transport"),o=document.getElementById("modal-srv-command"),r=document.getElementById("modal-srv-url"),s=document.getElementById("modal-srv-ft"),l=document.getElementById("modal-srv-cd"),d=document.getElementById("modal-srv-autorestart"),g=document.getElementById("modal-srv-maxrestarts");if(e)e.textContent="Add Upstream MCP Server";if(t)t.style.display="flex";if(a)a.value="",a.disabled=!1;if(n)n.value="stdio";if(o)o.value="";if(r)r.value="";let u=document.getElementById("modal-group-cmd"),m=document.getElementById("modal-group-url");if(u)u.style.display="block";if(m)m.style.display="none";if(s)s.value="3";if(l)l.value="30000";if(d)d.value="true";if(g)g.value="5";let v=document.getElementById("modal-add-server");if(v)v.classList.add("active")}openEditServerModal(e){this.closeModals();let t=p.getState(),a=t.config.mcpServers?.[e];if(!a){alert(`Server '${e}' not found in configuration.`);return}let n=document.getElementById("modal-srv-title"),o=document.getElementById("modal-srv-template-banner"),r=document.getElementById("modal-srv-name"),s=document.getElementById("modal-srv-transport"),l=document.getElementById("modal-srv-command"),d=document.getElementById("modal-srv-url"),g=document.getElementById("modal-srv-ft"),u=document.getElementById("modal-srv-cd"),m=document.getElementById("modal-srv-autorestart"),v=document.getElementById("modal-srv-maxrestarts");if(n)n.textContent=`Edit Server '${e}'`;if(o)o.style.display="none";if(r)r.value=e,r.disabled=!0;let c=!!a.command;if(s)s.value=c?"stdio":"http";let y=document.getElementById("modal-group-cmd"),b=document.getElementById("modal-group-url");if(y)y.style.display=c?"block":"none";if(b)b.style.display=c?"none":"block";if(l)l.value=c?`${a.command} ${(a.args||[]).join(" ")}`.trim():"";if(d)d.value=a.url||"";let f=a.resilience||t.config.resilience;if(g)g.value=String(f?.failureThreshold??3);if(u)u.value=String(f?.cooldownMs??30000);if(m)m.value=f?.autoRestart===!1?"false":"true";if(v)v.value=String(f?.maxRestarts??5);let x=document.getElementById("modal-add-server");if(x)x.classList.add("active")}async submitAddServer(){let e=document.getElementById("modal-srv-name"),t=e?.value.trim(),a=document.getElementById("modal-srv-transport")?.value;if(!t){alert("Server name is required");return}if(e&&!e.disabled){if((p.getState().config.mcpServers||{})[t]){if(!confirm(`Server '${t}' already exists in configuration. Do you want to overwrite it?`))return}}let n={};if(a==="stdio"){let u=(document.getElementById("modal-srv-command")?.value.trim()).split(/\s+/).filter(Boolean);if(u.length===0){alert("Command is required");return}n.command=u[0],n.args=u.slice(1)}else{let g=document.getElementById("modal-srv-url")?.value.trim();if(!g){alert("URL is required");return}n.url=g}let o=document.getElementById("modal-srv-ft")?.value.trim(),r=document.getElementById("modal-srv-cd")?.value.trim(),s=document.getElementById("modal-srv-autorestart")?.value,l=document.getElementById("modal-srv-maxrestarts")?.value.trim();if(o||r||s||l)n.resilience={failureThreshold:o?Number(o):3,cooldownMs:r?Number(r):30000,autoRestart:s!=="false",maxRestarts:l?Number(l):5};let d=await h.upsertServer(t,n);if(d.ok)this.closeModals(),await this.refreshData();else alert(`Failed to save server: ${d.error}`)}openTemplateCatalog(){this.closeModals();let e=document.getElementById("modal-templates");if(e)e.classList.add("active");this.renderTemplateGrid()}setTemplateCategory(e){this.activeTemplateCategory=e,document.querySelectorAll(".tmpl-cat-btn").forEach((t)=>{if(t.getAttribute("data-category")===e)t.classList.add("active"),t.style.background="var(--surface-elevated)",t.style.color="var(--amber-400)";else t.classList.remove("active"),t.style.background="var(--surface-card)",t.style.color="var(--text-main)"}),this.renderTemplateGrid()}filterTemplates(e){this.activeTemplateFilter=e.toLowerCase().trim(),this.renderTemplateGrid()}renderTemplateGrid(){let e=document.getElementById("tmpl-grid");if(!e)return;let t=D.filter((o)=>{let r=this.activeTemplateCategory==="all"||o.category===this.activeTemplateCategory,s=!this.activeTemplateFilter||o.name.toLowerCase().includes(this.activeTemplateFilter)||o.id.toLowerCase().includes(this.activeTemplateFilter)||o.description.toLowerCase().includes(this.activeTemplateFilter)||o.command.toLowerCase().includes(this.activeTemplateFilter)||o.envFields.some((l)=>l.key.toLowerCase().includes(this.activeTemplateFilter));return r&&s});if(t.length===0){e.innerHTML=`
No matching MCP server templates found.
- `;return}let o=c.getState().config.mcpServers||{};e.innerHTML=t.map((n)=>{let r=!!o[n.id],s=`${n.command} ${n.defaultArgs.join(" ")}`;return` + `;return}let n=p.getState().config.mcpServers||{};e.innerHTML=t.map((o)=>{let r=!!n[o.id],s=`${o.command} ${o.defaultArgs.join(" ")}`;return`
- ${f(n.name)} - ${f(n.badge)} + ${i(o.name)} + ${i(o.badge)}
${r?'CONNECTED':""}
- ${f(n.description)} + ${i(o.description)}
- ${f(s)} + ${i(s)}
- ${n.envFields.length>0?` + ${o.envFields.length>0?`
⚡ Needs: - ${n.envFields.map((i)=>f(i.key)).join(", ")} + ${o.envFields.map((l)=>i(l.key)).join(", ")}
`:""}
-
- `}).join("")}selectTemplate(e){let t=X.find((l)=>l.id===e);if(!t)return;this.selectedTemplate=t,this.closeModals();let a=document.getElementById("modal-configure-template");if(a)a.classList.add("active");let o=document.getElementById("cfg-tmpl-title"),n=document.getElementById("cfg-tmpl-desc"),r=document.getElementById("cfg-tmpl-form");if(o)o.textContent=`Configure ${t.name} Server`;if(n)n.textContent=t.description;let s=c.getState().config.mcpServers||{},i=t.id;if(s[i]){let l=2;while(s[`${t.id}-${l}`])l++;i=`${t.id}-${l}`}if(r){let l="";if(t.envFields.length>0)l=` + `}).join("")}selectTemplate(e){let t=D.find((d)=>d.id===e);if(!t)return;this.selectedTemplate=t,this.closeModals();let a=document.getElementById("modal-configure-template");if(a)a.classList.add("active");let n=document.getElementById("cfg-tmpl-title"),o=document.getElementById("cfg-tmpl-desc"),r=document.getElementById("cfg-tmpl-form");if(n)n.textContent=`Configure ${t.name} Server`;if(o)o.textContent=t.description;let s=p.getState().config.mcpServers||{},l=t.id;if(s[l]){let d=2;while(s[`${t.id}-${d}`])d++;l=`${t.id}-${d}`}if(r){let d="";if(t.envFields.length>0)d=`
Environment Variables & API Keys
- ${t.envFields.map((u)=>` + ${t.envFields.map((g)=>`
- - - ${u.description?`
${f(u.description)}
`:""} + + + ${g.description?`
${i(g.description)}
`:""}
`).join("")} `;r.innerHTML=`
- +
Must be unique across all configured servers.
- -
Executable: ${f(t.command)}
+ +
Executable: ${i(t.command)}
- ${l} + ${d}
\uD83D\uDEE1️ Fault Tolerance & Process Supervision (Optional) @@ -2187,37 +2187,37 @@ Reference: ${s.uri}`),e)e.value="";if(t)t.value="";await this.refreshData()}else
- `}}async submitTemplateServer(){if(!this.selectedTemplate)return;let e=this.selectedTemplate,t=document.getElementById("cfg-srv-id")?.value.trim(),a=document.getElementById("cfg-srv-args")?.value.trim();if(!t){alert("Server identifier is required");return}if((c.getState().config.mcpServers||{})[t]){if(!confirm(`Server '${t}' already exists. Do you want to overwrite its configuration?`))return}let r=a?a.split(/\s+/).filter(Boolean):[],s={},i=document.querySelectorAll(".tmpl-env-input");for(let y of Array.from(i)){let b=y.getAttribute("data-key"),v=y.value.trim(),x=e.envFields.find((A)=>A.key===b);if(x?.required&&!v){alert(`Required field '${x.label}' is missing.`);return}if(b&&v)s[b]=v}let l={command:e.command,args:r};if(Object.keys(s).length>0)l.env=s;let u=document.getElementById("cfg-srv-ft")?.value.trim(),p=document.getElementById("cfg-srv-cd")?.value.trim(),g=document.getElementById("cfg-srv-autorestart")?.value,m=document.getElementById("cfg-srv-maxrestarts")?.value.trim();if(u||p||g||m)l.resilience={failureThreshold:u?Number(u):3,cooldownMs:p?Number(p):30000,autoRestart:g!=="false",maxRestarts:m?Number(m):5};let d=await h.upsertServer(t,l);if(d.ok)this.closeModals(),await this.refreshData();else alert(`Failed to save server: ${d.error}`)}async openImportModal(){this.closeModals();let e=document.getElementById("modal-import");if(e)e.classList.add("active");let t=document.getElementById("modal-eco-list");if(!t)return;t.innerHTML='
Scanning IDE configs...
';try{let a=await h.getEcosystemSources();if(a.sources&&a.sources.length>0)t.innerHTML=a.sources.map((o)=>` + `}}async submitTemplateServer(){if(!this.selectedTemplate)return;let e=this.selectedTemplate,t=document.getElementById("cfg-srv-id")?.value.trim(),a=document.getElementById("cfg-srv-args")?.value.trim();if(!t){alert("Server identifier is required");return}if((p.getState().config.mcpServers||{})[t]){if(!confirm(`Server '${t}' already exists. Do you want to overwrite its configuration?`))return}let r=a?a.split(/\s+/).filter(Boolean):[],s={},l=document.querySelectorAll(".tmpl-env-input");for(let y of Array.from(l)){let b=y.getAttribute("data-key"),f=y.value.trim(),x=e.envFields.find((T)=>T.key===b);if(x?.required&&!f){alert(`Required field '${x.label}' is missing.`);return}if(b&&f)s[b]=f}let d={command:e.command,args:r};if(Object.keys(s).length>0)d.env=s;let g=document.getElementById("cfg-srv-ft")?.value.trim(),u=document.getElementById("cfg-srv-cd")?.value.trim(),m=document.getElementById("cfg-srv-autorestart")?.value,v=document.getElementById("cfg-srv-maxrestarts")?.value.trim();if(g||u||m||v)d.resilience={failureThreshold:g?Number(g):3,cooldownMs:u?Number(u):30000,autoRestart:m!=="false",maxRestarts:v?Number(v):5};let c=await h.upsertServer(t,d);if(c.ok)this.closeModals(),await this.refreshData();else alert(`Failed to save server: ${c.error}`)}async openImportModal(){this.closeModals();let e=document.getElementById("modal-import");if(e)e.classList.add("active");let t=document.getElementById("modal-eco-list");if(!t)return;t.innerHTML='
Scanning IDE configs...
';try{let a=await h.getEcosystemSources();if(a.sources&&a.sources.length>0)t.innerHTML=a.sources.map((n)=>` - `).join("");else t.innerHTML='
No external MCP configuration files found on this system.
'}catch{t.innerHTML='
Failed to scan ecosystem sources.
'}}async submitImport(){let e=document.querySelectorAll(".eco-checkbox:checked");if(e.length===0){alert("No sources selected");return}for(let t of Array.from(e))await h.importConfig(t.value,!1);this.closeModals(),await this.refreshData()}async refreshClients(){try{let e=await h.getClients();if(e.ok&&Array.isArray(e.clients))c.setState({clients:e.clients})}catch(e){console.error("Failed to scan clients:",e)}}setClientCategoryFilter(e){c.setState({clientFilterCategory:e})}setClientSearchQuery(e){c.setState({clientSearchQuery:e})}async attachClient(e,t){let a=t;if(!a){let o=document.getElementById(`client-prof-${e}`)||document.getElementById(`overview-client-prof-${e}`);if(o)a=o.value||void 0;else a=c.getState().activeProfile||void 0}this.openClientAttachModal(e,a)}openClientAttachModal(e,t){this.closeModals(),this.pendingClientId=e;let a=c.getState().config,o=a.mcpHttpServer,n=o?`http://${o.bind==="0.0.0.0"||o.bind==="::"?"127.0.0.1":o.bind||"127.0.0.1"}:${o.port||9191}/mcp`:"",r=document.getElementById("modal-client-title"),s=document.getElementById("modal-client-transport"),i=document.getElementById("modal-client-url"),l=document.getElementById("modal-client-profile"),u=document.getElementById("modal-client-profile-wrap");if(r)r.textContent=`Connect ${e}`;if(s)s.value="stdio";if(i)i.value=n;if(l){let p=Object.keys(a.profiles||{});if(l.innerHTML=`${p.map((g)=>``).join("")}`,l.value=t||"",u)u.style.display=p.length>0?"block":"none"}this.updateClientTransportForm(),document.getElementById("modal-client-attach")?.classList.add("active")}updateClientTransportForm(){let t=(document.getElementById("modal-client-transport")?.value||"stdio")==="http",a=document.getElementById("modal-client-http-group"),o=document.getElementById("modal-client-http-status"),n=!!c.getState().config.mcpHttpServer;if(a)a.style.display=t?"block":"none";if(o)o.textContent=n?"Uses the daemon Streamable HTTP endpoint. The daemon must already be running.":"HTTP is unavailable until mcpHttpServer is configured and the daemon is restarted.",o.style.color=n?"var(--text-dim)":"var(--amber-300)"}async submitClientAttach(){let e=this.pendingClientId;if(!e)return;let t=document.getElementById("modal-client-transport")?.value||"stdio",a=document.getElementById("modal-client-profile")?.value||void 0,o=document.getElementById("modal-client-url")?.value.trim()||void 0;if(t==="http"&&!c.getState().config.mcpHttpServer&&!o){alert("Configure mcpHttpServer and restart the daemon, or provide an explicit HTTP endpoint.");return}if(t==="http"&&!o){alert("Enter an HTTP endpoint URL.");return}let n=await h.attachClient(e,a,t,o);if(!n.ok)alert(`Failed to attach client: ${n.error||n.message||"Unknown error"}`);else this.pendingClientId=null,this.closeModals(),await this.refreshData()}async detachClient(e){if(!confirm("Disconnect Warmplane from this client?"))return;let t=await h.detachClient(e);if(!t.ok)alert(`Failed to detach client: ${t.error||t.message||"Unknown error"}`);else await this.refreshData()}handleAliasTargetInput(e){let t=document.getElementById("alias-suggestions-dropdown");if(!t)return;let a=(e||"").trim().toLowerCase();if(a.length<2){t.style.display="none";return}let n=c.getState().capabilities.filter((r)=>r.id.toLowerCase().includes(a)||r.summary&&r.summary.toLowerCase().includes(a)||r.description&&r.description.toLowerCase().includes(a)||r.server&&r.server.toLowerCase().includes(a)).slice(0,8);if(n.length===0){t.style.display="none";return}t.innerHTML=n.map((r)=>` + `).join("");else t.innerHTML='
No external MCP configuration files found on this system.
'}catch{t.innerHTML='
Failed to scan ecosystem sources.
'}}async submitImport(){let e=document.querySelectorAll(".eco-checkbox:checked");if(e.length===0){alert("No sources selected");return}for(let t of Array.from(e))await h.importConfig(t.value,!1);this.closeModals(),await this.refreshData()}async refreshClients(){try{let e=await h.getClients();if(e.ok&&Array.isArray(e.clients))p.setState({clients:e.clients})}catch(e){console.error("Failed to scan clients:",e)}}setClientCategoryFilter(e){p.setState({clientFilterCategory:e})}setClientSearchQuery(e){p.setState({clientSearchQuery:e})}async attachClient(e,t){let a=t;if(!a){let n=document.getElementById(`client-prof-${e}`)||document.getElementById(`overview-client-prof-${e}`);if(n)a=n.value||void 0;else a=p.getState().activeProfile||void 0}this.openClientAttachModal(e,a)}openClientAttachModal(e,t){this.closeModals(),this.pendingClientId=e;let a=p.getState().config,n=a.mcpHttpServer,o=n?`http://${n.bind==="0.0.0.0"||n.bind==="::"?"127.0.0.1":n.bind||"127.0.0.1"}:${n.port||9191}/mcp`:"",r=document.getElementById("modal-client-title"),s=document.getElementById("modal-client-transport"),l=document.getElementById("modal-client-url"),d=document.getElementById("modal-client-profile"),g=document.getElementById("modal-client-profile-wrap");if(r)r.textContent=`Connect ${e}`;if(s)s.value="stdio";if(l)l.value=o;if(d){let u=Object.keys(a.profiles||{});if(d.innerHTML=`${u.map((m)=>``).join("")}`,d.value=t||"",g)g.style.display=u.length>0?"block":"none"}this.updateClientTransportForm(),document.getElementById("modal-client-attach")?.classList.add("active")}updateClientTransportForm(){let t=(document.getElementById("modal-client-transport")?.value||"stdio")==="http",a=document.getElementById("modal-client-http-group"),n=document.getElementById("modal-client-http-status"),o=!!p.getState().config.mcpHttpServer;if(a)a.style.display=t?"block":"none";if(n)n.textContent=o?"Uses the daemon Streamable HTTP endpoint. The daemon must already be running.":"HTTP is unavailable until mcpHttpServer is configured and the daemon is restarted.",n.style.color=o?"var(--text-dim)":"var(--amber-300)"}async submitClientAttach(){let e=this.pendingClientId;if(!e)return;let t=document.getElementById("modal-client-transport")?.value||"stdio",a=document.getElementById("modal-client-profile")?.value||void 0,n=document.getElementById("modal-client-url")?.value.trim()||void 0;if(t==="http"&&!p.getState().config.mcpHttpServer&&!n){alert("Configure mcpHttpServer and restart the daemon, or provide an explicit HTTP endpoint.");return}if(t==="http"&&!n){alert("Enter an HTTP endpoint URL.");return}let o=await h.attachClient(e,a,t,n);if(!o.ok)alert(`Failed to attach client: ${o.error||o.message||"Unknown error"}`);else this.pendingClientId=null,this.closeModals(),await this.refreshData()}async detachClient(e){if(!confirm("Disconnect Warmplane from this client?"))return;let t=await h.detachClient(e);if(!t.ok)alert(`Failed to detach client: ${t.error||t.message||"Unknown error"}`);else await this.refreshData()}handleAliasTargetInput(e){let t=document.getElementById("alias-suggestions-dropdown");if(!t)return;let a=(e||"").trim().toLowerCase();if(a.length<2){t.style.display="none";return}let o=p.getState().capabilities.filter((r)=>r.id.toLowerCase().includes(a)||r.summary&&r.summary.toLowerCase().includes(a)||r.description&&r.description.toLowerCase().includes(a)||r.server&&r.server.toLowerCase().includes(a)).slice(0,8);if(o.length===0){t.style.display="none";return}t.innerHTML=o.map((r)=>`
+ onmousedown="window.app.selectAliasSuggestion('${i(r.id)}')">
-
${f(r.id)}
-
${f(r.summary||r.description||"")}
+
${i(r.id)}
+
${i(r.summary||r.description||"")}
- ${f(r.server||"local")} + ${i(r.server||"local")}
- `).join(""),t.style.display="block"}selectAliasSuggestion(e){let t=document.getElementById("alias-target");if(t)t.value=e;this.hideAliasDropdown()}hideAliasDropdown(){let e=document.getElementById("alias-suggestions-dropdown");if(e)e.style.display="none"}startEditAlias(e,t,a,o="",n=!1){let r=document.getElementById("alias-kind"),s=document.getElementById("alias-name"),i=document.getElementById("alias-target"),l=document.getElementById("alias-summary"),u=document.getElementById("alias-passthrough"),p=document.getElementById("alias-passthrough-container"),g=document.getElementById("alias-form-title"),m=document.getElementById("alias-save-btn"),d=document.getElementById("alias-cancel-btn");if(r)r.value=e;if(s)s.value=t;if(i)i.value=a;if(l)l.value=o;if(u)u.checked=n;if(p)p.style.display=e==="tool"?"flex":"none";if(g)g.innerHTML=`✏️ EDIT ALIAS: ${t}`;if(m)m.textContent="✓ Update";if(d)d.style.display="inline-block";s?.focus(),s?.scrollIntoView({behavior:"smooth",block:"nearest"})}resetAliasForm(){let e=document.getElementById("alias-kind"),t=document.getElementById("alias-name"),a=document.getElementById("alias-target"),o=document.getElementById("alias-summary"),n=document.getElementById("alias-passthrough"),r=document.getElementById("alias-passthrough-container"),s=document.getElementById("alias-form-title"),i=document.getElementById("alias-save-btn"),l=document.getElementById("alias-cancel-btn");if(e)e.value="tool";if(t)t.value="";if(a)a.value="";if(o)o.value="";if(n)n.checked=!1;if(r)r.style.display="flex";if(s)s.textContent="Create New Alias";if(i)i.textContent="+ Save";if(l)l.style.display="none"}async createAlias(){let e=document.getElementById("alias-kind")?.value,t=document.getElementById("alias-name")?.value.trim(),a=document.getElementById("alias-target")?.value.trim(),o=document.getElementById("alias-summary")?.value.trim()||void 0,n=document.getElementById("alias-passthrough")?.checked||!1;if(!t||!a){alert("Please provide both alias name and canonical target");return}if(e==="tool"&&n&&!/^[a-zA-Z0-9_-]{1,64}$/.test(t)){let r=t.replace(/[^a-zA-Z0-9_-]/g,"_").substring(0,64);if(!confirm(`MCP tool names must match ^[a-zA-Z0-9_-]{1,64}$. + `).join(""),t.style.display="block"}selectAliasSuggestion(e){let t=document.getElementById("alias-target");if(t)t.value=e;this.hideAliasDropdown()}hideAliasDropdown(){let e=document.getElementById("alias-suggestions-dropdown");if(e)e.style.display="none"}startEditAlias(e,t,a,n="",o=!1){let r=document.getElementById("alias-kind"),s=document.getElementById("alias-name"),l=document.getElementById("alias-target"),d=document.getElementById("alias-summary"),g=document.getElementById("alias-passthrough"),u=document.getElementById("alias-passthrough-container"),m=document.getElementById("alias-form-title"),v=document.getElementById("alias-save-btn"),c=document.getElementById("alias-cancel-btn");if(r)r.value=e;if(s)s.value=t;if(l)l.value=a;if(d)d.value=n;if(g)g.checked=o;if(u)u.style.display=e==="tool"?"flex":"none";if(m)m.innerHTML=`✏️ EDIT ALIAS: ${t}`;if(v)v.textContent="✓ Update";if(c)c.style.display="inline-block";s?.focus(),s?.scrollIntoView({behavior:"smooth",block:"nearest"})}resetAliasForm(){let e=document.getElementById("alias-kind"),t=document.getElementById("alias-name"),a=document.getElementById("alias-target"),n=document.getElementById("alias-summary"),o=document.getElementById("alias-passthrough"),r=document.getElementById("alias-passthrough-container"),s=document.getElementById("alias-form-title"),l=document.getElementById("alias-save-btn"),d=document.getElementById("alias-cancel-btn");if(e)e.value="tool";if(t)t.value="";if(a)a.value="";if(n)n.value="";if(o)o.checked=!1;if(r)r.style.display="flex";if(s)s.textContent="Create New Alias";if(l)l.textContent="+ Save";if(d)d.style.display="none"}async createAlias(){let e=document.getElementById("alias-kind")?.value,t=document.getElementById("alias-name")?.value.trim(),a=document.getElementById("alias-target")?.value.trim(),n=document.getElementById("alias-summary")?.value.trim()||void 0,o=document.getElementById("alias-passthrough")?.checked||!1;if(!t||!a){alert("Please provide both alias name and canonical target");return}if(e==="tool"&&o&&!/^[a-zA-Z0-9_-]{1,64}$/.test(t)){let r=t.replace(/[^a-zA-Z0-9_-]/g,"_").substring(0,64);if(!confirm(`MCP tool names must match ^[a-zA-Z0-9_-]{1,64}$. '${t}' will be exported to MCP clients as '${r}'. -Do you want to proceed?`))return}await h.updateAlias(e,t,a,o,void 0,n),this.resetAliasForm(),await this.refreshData()}async deleteAlias(e,t){await h.updateAlias(e,t,void 0),this.resetAliasForm(),await this.refreshData()}async reloadFromDisk(){try{let e=await h.reloadConfig();if(e.ok){let t="Hot-reload completed successfully!";if(e.mounted&&e.mounted.length>0)t+=` +Do you want to proceed?`))return}await h.updateAlias(e,t,a,n,void 0,o),this.resetAliasForm(),await this.refreshData()}async deleteAlias(e,t){await h.updateAlias(e,t,void 0),this.resetAliasForm(),await this.refreshData()}async reloadFromDisk(){try{let e=await h.reloadConfig();if(e.ok){let t="Hot-reload completed successfully!";if(e.mounted&&e.mounted.length>0)t+=` Mounted: ${e.mounted.join(", ")}`;if(e.unmounted&&e.unmounted.length>0)t+=` Unmounted: ${e.unmounted.join(", ")}`;if(e.warnings&&e.warnings.length>0)t+=` Warnings: ${e.warnings.join(` -`)}`;alert(t)}else alert(`Hot-reload failed: ${e.error||"Unknown error"}`)}catch(e){alert(`Error reaching daemon: ${e.message}`)}await this.refreshData()}renderTopProfileSelector(){let e=document.getElementById("top-profile-selector");if(!e)return;let t=c.getState(),a=t.config.profiles||{},o=Object.keys(a),n=t.activeProfile,r='';for(let s of o){let i=n===s?"selected":"";r+=``}e.innerHTML=r}async setActiveProfile(e){c.setState({activeProfile:e||null}),await this.refreshData()}openAddProfileModal(){let e=document.getElementById("modal-prof-title");if(e)e.textContent="Create Server Constellation Profile";let t=document.getElementById("modal-prof-name"),a=document.getElementById("modal-prof-desc"),o=document.getElementById("modal-prof-mode");if(t)t.value="",t.disabled=!1;if(a)a.value="";if(o)o.value="create";let n=document.getElementById("modal-prof-allow"),r=document.getElementById("modal-prof-deny"),s=document.getElementById("modal-prof-hitl"),i=document.getElementById("modal-prof-redact");if(n)n.value="";if(r)r.value="";if(s)s.value="";if(i)i.value="";this.renderProfileServerCheckboxes([]);let l=document.getElementById("modal-add-profile");if(l)l.classList.add("active")}openEditProfileModal(e){let a=c.getState().config.profiles?.[e];if(!a)return;let o=document.getElementById("modal-prof-title");if(o)o.textContent=`Edit Profile: ${e}`;let n=document.getElementById("modal-prof-name"),r=document.getElementById("modal-prof-desc"),s=document.getElementById("modal-prof-mode");if(n)n.value=e,n.disabled=!0;if(r)r.value=a.description||"";if(s)s.value="edit";let i=document.getElementById("modal-prof-allow"),l=document.getElementById("modal-prof-deny"),u=document.getElementById("modal-prof-hitl"),p=document.getElementById("modal-prof-redact"),g=a.policy;if(i)i.value=(g?.allow||[]).join(", ");if(l)l.value=(g?.deny||[]).join(", ");if(u)u.value=(g?.require_approval||g?.requireApproval||[]).join(", ");if(p)p.value=(g?.redact_keys||g?.redactKeys||[]).join(", ");this.renderProfileServerCheckboxes(a.servers||[]);let m=document.getElementById("modal-add-profile");if(m)m.classList.add("active")}renderProfileServerCheckboxes(e){let t=document.getElementById("modal-prof-servers-list");if(!t)return;let a=c.getState(),o=Object.keys(a.config.mcpServers||{});if(o.length===0){t.innerHTML='
No MCP servers configured yet. Add servers first.
';return}t.innerHTML=o.map((n)=>{let r=e.includes(n)?"checked":"";return` +`)}`;alert(t)}else alert(`Hot-reload failed: ${e.error||"Unknown error"}`)}catch(e){alert(`Error reaching daemon: ${e.message}`)}await this.refreshData()}renderTopProfileSelector(){let e=document.getElementById("top-profile-selector");if(!e)return;let t=p.getState(),a=t.config.profiles||{},n=Object.keys(a),o=t.activeProfile,r='';for(let s of n){let l=o===s?"selected":"";r+=``}e.innerHTML=r}async setActiveProfile(e){p.setState({activeProfile:e||null}),await this.refreshData()}openAddProfileModal(){let e=document.getElementById("modal-prof-title");if(e)e.textContent="Create Server Constellation Profile";let t=document.getElementById("modal-prof-name"),a=document.getElementById("modal-prof-desc"),n=document.getElementById("modal-prof-mode");if(t)t.value="",t.disabled=!1;if(a)a.value="";if(n)n.value="create";let o=document.getElementById("modal-prof-allow"),r=document.getElementById("modal-prof-deny"),s=document.getElementById("modal-prof-hitl"),l=document.getElementById("modal-prof-redact");if(o)o.value="";if(r)r.value="";if(s)s.value="";if(l)l.value="";this.renderProfileServerCheckboxes([]);let d=document.getElementById("modal-add-profile");if(d)d.classList.add("active")}openEditProfileModal(e){let a=p.getState().config.profiles?.[e];if(!a)return;let n=document.getElementById("modal-prof-title");if(n)n.textContent=`Edit Profile: ${e}`;let o=document.getElementById("modal-prof-name"),r=document.getElementById("modal-prof-desc"),s=document.getElementById("modal-prof-mode");if(o)o.value=e,o.disabled=!0;if(r)r.value=a.description||"";if(s)s.value="edit";let l=document.getElementById("modal-prof-allow"),d=document.getElementById("modal-prof-deny"),g=document.getElementById("modal-prof-hitl"),u=document.getElementById("modal-prof-redact"),m=a.policy;if(l)l.value=(m?.allow||[]).join(", ");if(d)d.value=(m?.deny||[]).join(", ");if(g)g.value=(m?.require_approval||m?.requireApproval||[]).join(", ");if(u)u.value=(m?.redact_keys||m?.redactKeys||[]).join(", ");this.renderProfileServerCheckboxes(a.servers||[]);let v=document.getElementById("modal-add-profile");if(v)v.classList.add("active")}renderProfileServerCheckboxes(e){let t=document.getElementById("modal-prof-servers-list");if(!t)return;let a=p.getState(),n=Object.keys(a.config.mcpServers||{});if(n.length===0){t.innerHTML='
No MCP servers configured yet. Add servers first.
';return}t.innerHTML=n.map((o)=>{let r=e.includes(o)?"checked":"";return` - `}).join("")}async saveProfile(){let e=document.getElementById("modal-prof-name"),t=document.getElementById("modal-prof-desc"),a=e?.value.trim(),o=t?.value.trim();if(!a){alert("Please enter a profile name");return}let n=document.querySelectorAll(".prof-server-checkbox:checked"),r=[];if(n.forEach((v)=>{r.push(v.value)}),r.length===0){alert("Please select at least one server to include in this constellation");return}let s=(v)=>{if(!v)return[];return v.split(",").map((x)=>x.trim()).filter((x)=>x.length>0)},i=document.getElementById("modal-prof-allow"),l=document.getElementById("modal-prof-deny"),u=document.getElementById("modal-prof-hitl"),p=document.getElementById("modal-prof-redact"),g=s(i?.value),m=s(l?.value),d=s(u?.value),y=s(p?.value),b=void 0;if(g.length>0||m.length>0||d.length>0||y.length>0)b={allow:g,deny:m,requireApproval:d,redactKeys:y};try{let v=await h.upsertProfile(a,r,o||void 0,b);if(v.ok)this.closeModals(),await this.refreshData();else alert(`Failed to save profile: ${v.error||"Unknown error"}`)}catch(v){alert(`Error saving profile: ${v.message}`)}}async deleteProfile(e){if(!confirm(`Are you sure you want to delete profile '${e}'?`))return;try{let t=await h.deleteProfile(e);if(t.ok){if(c.getState().activeProfile===e)c.setState({activeProfile:null});await this.refreshData()}else alert(`Failed to delete profile: ${t.error||"Unknown error"}`)}catch(t){alert(`Error deleting profile: ${t.message}`)}}async toggleServerInProfile(e,t,a){let n=c.getState().config.profiles?.[e];if(!n)return;let r=[...n.servers||[]];if(a){if(!r.includes(t))r.push(t)}else if(r=r.filter((s)=>s!==t),r.length===0){alert("A profile must contain at least one server. To remove the profile, delete it in the Profiles tab.");return}try{let s=await h.upsertProfile(e,r,n.description,n.policy);if(s.ok)await this.refreshData();else alert(`Failed to update profile constellation: ${s.error||"Unknown error"}`)}catch(s){alert(`Error updating profile constellation: ${s.message}`)}}closeModals(){document.querySelectorAll(".modal-backdrop").forEach((e)=>e.classList.remove("active"))}}function f(e){return String(e||"").replace(/&/g,"&").replace(//g,">").replace(/"/g,""")}var fe=new ve;window.app=fe;window.addEventListener("DOMContentLoaded",()=>fe.init()); + `}).join("")}async saveProfile(){let e=document.getElementById("modal-prof-name"),t=document.getElementById("modal-prof-desc"),a=e?.value.trim(),n=t?.value.trim();if(!a){alert("Please enter a profile name");return}let o=document.querySelectorAll(".prof-server-checkbox:checked"),r=[];if(o.forEach((f)=>{r.push(f.value)}),r.length===0){alert("Please select at least one server to include in this constellation");return}let s=(f)=>{if(!f)return[];return f.split(",").map((x)=>x.trim()).filter((x)=>x.length>0)},l=document.getElementById("modal-prof-allow"),d=document.getElementById("modal-prof-deny"),g=document.getElementById("modal-prof-hitl"),u=document.getElementById("modal-prof-redact"),m=s(l?.value),v=s(d?.value),c=s(g?.value),y=s(u?.value),b=void 0;if(m.length>0||v.length>0||c.length>0||y.length>0)b={allow:m,deny:v,requireApproval:c,redactKeys:y};try{let f=await h.upsertProfile(a,r,n||void 0,b);if(f.ok)this.closeModals(),await this.refreshData();else alert(`Failed to save profile: ${f.error||"Unknown error"}`)}catch(f){alert(`Error saving profile: ${f.message}`)}}async deleteProfile(e){if(!confirm(`Are you sure you want to delete profile '${e}'?`))return;try{let t=await h.deleteProfile(e);if(t.ok){if(p.getState().activeProfile===e)p.setState({activeProfile:null});await this.refreshData()}else alert(`Failed to delete profile: ${t.error||"Unknown error"}`)}catch(t){alert(`Error deleting profile: ${t.message}`)}}async toggleServerInProfile(e,t,a){let o=p.getState().config.profiles?.[e];if(!o)return;let r=[...o.servers||[]];if(a){if(!r.includes(t))r.push(t)}else if(r=r.filter((s)=>s!==t),r.length===0){alert("A profile must contain at least one server. To remove the profile, delete it in the Profiles tab.");return}try{let s=await h.upsertProfile(e,r,o.description,o.policy);if(s.ok)await this.refreshData();else alert(`Failed to update profile constellation: ${s.error||"Unknown error"}`)}catch(s){alert(`Error updating profile constellation: ${s.message}`)}}closeModals(){document.querySelectorAll(".modal-backdrop").forEach((e)=>e.classList.remove("active"))}}var ie=new ne;window.app=ie;window.addEventListener("DOMContentLoaded",()=>ie.init()); diff --git a/ui/dist/index.html b/ui/dist/index.html index c894494..969a4e7 100644 --- a/ui/dist/index.html +++ b/ui/dist/index.html @@ -822,7 +822,7 @@
diff --git a/ui/src/components/aliases.ts b/ui/src/components/aliases.ts index 75f2849..3f66275 100644 --- a/ui/src/components/aliases.ts +++ b/ui/src/components/aliases.ts @@ -1,3 +1,4 @@ +import { escapeHtml } from '../escape'; import { store } from '../state'; import { api } from '../api'; @@ -128,7 +129,3 @@ export function renderAliases(): string {
`; } - -function escapeHtml(str: string): string { - return String(str || '').replace(/&/g, '&').replace(//g, '>').replace(/"/g, '"'); -} diff --git a/ui/src/components/approvals.ts b/ui/src/components/approvals.ts index 93e6011..eb56deb 100644 --- a/ui/src/components/approvals.ts +++ b/ui/src/components/approvals.ts @@ -1,3 +1,4 @@ +import { escapeHtml } from '../escape'; import { AppState } from '../state'; export function renderApprovals(state: AppState): string { @@ -213,8 +214,4 @@ export function renderApprovals(state: AppState): string { `; } -function escapeHtml(str: string): string { - return String(str || '').replace(/&/g, '&').replace(//g, '>').replace(/"/g, '"'); -} - diff --git a/ui/src/components/audit.ts b/ui/src/components/audit.ts index a0fd7c2..b2c32de 100644 --- a/ui/src/components/audit.ts +++ b/ui/src/components/audit.ts @@ -1,3 +1,4 @@ +import { escapeHtml } from '../escape'; import { store } from '../state'; import { api, AuditEventItem } from '../api'; @@ -408,13 +409,3 @@ export function renderAudit(): string { `; } -function escapeHtml(str: string): string { - if (!str) return ''; - return String(str) - .replace(/&/g, '&') - .replace(//g, '>') - .replace(/"/g, '"') - .replace(/'/g, '''); -} - diff --git a/ui/src/components/overview.ts b/ui/src/components/overview.ts index 9a68590..e7b6b7e 100644 --- a/ui/src/components/overview.ts +++ b/ui/src/components/overview.ts @@ -1,3 +1,4 @@ +import { escapeHtml } from '../escape'; import { store } from '../state'; export function renderOverview(): string { @@ -278,7 +279,3 @@ export function renderOverview(): string {
`; } - -function escapeHtml(str: string): string { - return String(str || '').replace(/&/g, '&').replace(//g, '>').replace(/"/g, '"'); -} diff --git a/ui/src/components/playground.ts b/ui/src/components/playground.ts index f0b35cc..44b2e82 100644 --- a/ui/src/components/playground.ts +++ b/ui/src/components/playground.ts @@ -1,3 +1,4 @@ +import { escapeHtml } from '../escape'; import { store } from '../state'; import { api } from '../api'; @@ -747,12 +748,4 @@ function renderBatchModal(state: any): string { `; } -function escapeHtml(str: string): string { - return String(str || '') - .replace(/&/g, '&') - .replace(//g, '>') - .replace(/"/g, '"'); -} - diff --git a/ui/src/components/policy.ts b/ui/src/components/policy.ts index e2d7b31..a34bb4c 100644 --- a/ui/src/components/policy.ts +++ b/ui/src/components/policy.ts @@ -1,3 +1,4 @@ +import { escapeHtml } from '../escape'; import { store } from '../state'; import { api } from '../api'; @@ -253,7 +254,3 @@ export function renderPolicy(): string { `; } - -function escapeHtml(str: string): string { - return String(str || '').replace(/&/g, '&').replace(//g, '>').replace(/"/g, '"'); -} diff --git a/ui/src/components/profiles.ts b/ui/src/components/profiles.ts index 8e6a119..48a2e6f 100644 --- a/ui/src/components/profiles.ts +++ b/ui/src/components/profiles.ts @@ -1,3 +1,4 @@ +import { escapeHtml } from '../escape'; import { store } from '../state'; import { api } from '../api'; @@ -118,7 +119,3 @@ export function renderProfiles(): string { ${profilesListHtml} `; } - -function escapeHtml(str: string): string { - return String(str || '').replace(/&/g, '&').replace(//g, '>').replace(/"/g, '"'); -} diff --git a/ui/src/components/secrets.ts b/ui/src/components/secrets.ts index dfdac34..c7e5740 100644 --- a/ui/src/components/secrets.ts +++ b/ui/src/components/secrets.ts @@ -1,3 +1,4 @@ +import { escapeHtml } from '../escape'; import { store } from '../state'; import { api } from '../api'; import { findTemplateForServer } from '../templates'; @@ -136,7 +137,3 @@ export function renderSecrets(): string { `; } - -function escapeHtml(str: string): string { - return String(str || '').replace(/&/g, '&').replace(//g, '>').replace(/"/g, '"'); -} diff --git a/ui/src/components/servers.ts b/ui/src/components/servers.ts index 68c0008..8d4467e 100644 --- a/ui/src/components/servers.ts +++ b/ui/src/components/servers.ts @@ -1,3 +1,4 @@ +import { escapeHtml } from '../escape'; import { store } from '../state'; import { api } from '../api'; import { findTemplateForServer } from '../templates'; @@ -332,7 +333,3 @@ function renderClientIntegrations(): string { `; } - -function escapeHtml(str: string): string { - return String(str || '').replace(/&/g, '&').replace(//g, '>').replace(/"/g, '"'); -} diff --git a/ui/src/components/tasks.ts b/ui/src/components/tasks.ts index fb959d0..d77eb99 100644 --- a/ui/src/components/tasks.ts +++ b/ui/src/components/tasks.ts @@ -1,3 +1,4 @@ +import { escapeHtml } from '../escape'; import { AppState } from '../state'; import { TaskItem } from '../api'; @@ -337,7 +338,3 @@ export function renderTasks(state: AppState): string { `; } - -function escapeHtml(str: string): string { - return String(str || '').replace(/&/g, '&').replace(//g, '>').replace(/"/g, '"'); -} diff --git a/ui/src/escape.ts b/ui/src/escape.ts new file mode 100644 index 0000000..f96c93e --- /dev/null +++ b/ui/src/escape.ts @@ -0,0 +1,21 @@ +// Escapes a value for safe interpolation into HTML text or an attribute. + +/** + * Escapes a value for safe interpolation into HTML text or an attribute. + * + * Also escapes the single quote, so the value is safe inside the single-quoted + * JavaScript string literals used by inline `on*` handlers. + * + * @param value Any value. Non-strings are coerced; nullish values become an + * empty string. + * @returns The escaped string. + */ +export function escapeHtml(value: unknown): string { + if (value === null || value === undefined) return ''; + return String(value) + .replace(/&/g, '&') + .replace(//g, '>') + .replace(/"/g, '"') + .replace(/'/g, '''); +} \ No newline at end of file diff --git a/ui/src/main.ts b/ui/src/main.ts index bad613c..076a5b2 100644 --- a/ui/src/main.ts +++ b/ui/src/main.ts @@ -1,3 +1,4 @@ +import { escapeHtml } from './escape'; import { store } from './state'; import { api } from './api'; import { renderOverview } from './components/overview'; @@ -2777,10 +2778,6 @@ class WarmplaneApp { } } -function escapeHtml(str: string): string { - return String(str || '').replace(/&/g, '&').replace(//g, '>').replace(/"/g, '"'); -} - const app = new WarmplaneApp(); (window as any).app = app; window.addEventListener('DOMContentLoaded', () => app.init());