Files
archy-demo/web-dist/assets/Chat-BLEXivD6.js

2 lines
18 KiB
JavaScript

import{B as p,$ as W,r as A,j as w,z as x,d as j,y as V,o as K,A as G,c as L,a as d,E as b,D as E,h as Z,i as J,T as Q,G as M,x as X,q as z,t as Y}from"./index-DnbYEqLr.js";import{u as D}from"./aiPermissions-sblnS_DK.js";import{f as k}from"./filebrowser-client-DRXM9PC4.js";const v={async installApp(c){return p.call({method:"container-install",params:{manifest_path:c}})},async startContainer(c){return p.call({method:"container-start",params:{app_id:c}})},async stopContainer(c){return p.call({method:"container-stop",params:{app_id:c}})},async removeContainer(c){return p.call({method:"container-remove",params:{app_id:c}})},async getContainerStatus(c){return p.call({method:"container-status",params:{app_id:c}})},async getContainerLogs(c,e=100){return p.call({method:"container-logs",params:{app_id:c,lines:e}})},async listContainers(){return p.call({method:"container-list",params:{}})},async getHealthStatus(){return p.call({method:"container-health",params:{}})},async startBundledApp(c){return p.call({method:"bundled-app-start",params:{app_id:c.id,image:c.image,ports:c.ports,volumes:c.volumes}})},async stopBundledApp(c){return p.call({method:"bundled-app-stop",params:{app_id:c}})}},ee={"bitcoin-knots":["bitcoin-knots","bitcoin-ui"],lnd:["lnd","archy-lnd-ui"],"btcpay-server":["btcpay-server"],mempool:["archy-mempool-web"],electrumx:["archy-electrs-ui","electrumx","mempool-electrs"]},F=[{id:"bitcoin-knots",name:"Bitcoin Knots",image:"localhost/bitcoinknots/bitcoin:29",description:"Full Bitcoin node with additional features",icon:"₿",ports:[{host:8334,container:80}],volumes:[{host:"/var/lib/archipelago/bitcoin",container:"/data"}],category:"bitcoin"},{id:"lnd",name:"Lightning (LND)",image:"docker.io/lightninglabs/lnd:v0.18.4-beta",description:"Lightning Network Daemon for fast Bitcoin payments",icon:"⚡",ports:[{host:8081,container:80}],volumes:[{host:"/var/lib/archipelago/lnd",container:"/root/.lnd"}],category:"lightning"},{id:"homeassistant",name:"Home Assistant",image:"ghcr.io/home-assistant/home-assistant:stable",description:"Open source home automation platform",icon:"🏠",ports:[{host:8123,container:8123}],volumes:[{host:"/var/lib/archipelago/homeassistant",container:"/config"}],category:"home"},{id:"btcpay-server",name:"BTCPay Server",image:"docker.io/btcpayserver/btcpayserver:latest",description:"Self-hosted Bitcoin payment processor",icon:"💳",ports:[{host:23e3,container:49392}],volumes:[{host:"/var/lib/archipelago/btcpay",container:"/datadir"}],category:"bitcoin"},{id:"mempool",name:"Mempool Explorer",image:"docker.io/mempool/frontend:latest",description:"Bitcoin blockchain and mempool visualizer",icon:"🔍",ports:[{host:4080,container:8080}],volumes:[{host:"/var/lib/archipelago/mempool",container:"/data"}],category:"bitcoin"},{id:"tailscale",name:"Tailscale VPN",image:"docker.io/tailscale/tailscale:latest",description:"Zero-config VPN mesh network",icon:"🔒",ports:[],volumes:[{host:"/var/lib/archipelago/tailscale",container:"/var/lib/tailscale"}],category:"other"}],S=W("container",()=>{const c=A([]),e=A({}),a=A(!1),t=A(new Set),s=A(null),n=w(()=>c.value.filter(o=>o.state==="running")),i=w(()=>c.value.filter(o=>o.state==="stopped"||o.state==="exited")),l=w(()=>o=>c.value.find(r=>r.name.includes(o))),f=w(()=>o=>e.value[o]||"unknown"),u=w(()=>o=>{const r=ee[o];if(r)for(const y of r){const B=c.value.find(H=>H.name===y);if(B)return B}return c.value.find(y=>y.name===o)}),h=w(()=>o=>t.value.has(o)),m=w(()=>o=>{const r=u.value(o);return r?r.state:"not-installed"}),_=w(()=>F.map(o=>{const r=u.value(o.id);return{...o,lan_address:r?.lan_address}}));async function g(){a.value=!0,s.value=null;try{c.value=await v.listContainers()}catch(o){s.value=o instanceof Error?o.message:"Failed to fetch containers"}finally{a.value=!1}}async function C(){try{e.value=await v.getHealthStatus()}catch{}}async function I(o){a.value=!0,s.value=null;try{const r=await v.installApp(o);return await g(),r}catch(r){throw s.value=r instanceof Error?r.message:"Failed to install app",r}finally{a.value=!1}}async function N(o){t.value.add(o),s.value=null;try{await v.startContainer(o),await g(),await C()}catch(r){throw s.value=r instanceof Error?r.message:"Failed to start container",r}finally{t.value.delete(o)}}async function R(o){t.value.add(o),s.value=null;try{await v.stopContainer(o),await g()}catch(r){throw s.value=r instanceof Error?r.message:"Failed to stop container",r}finally{t.value.delete(o)}}async function P(o){t.value.add(o.id),s.value=null;try{await v.startBundledApp(o),await g(),await C()}catch(r){throw s.value=r instanceof Error?r.message:"Failed to start app",r}finally{t.value.delete(o.id)}}async function U(o){t.value.add(o),s.value=null;try{await v.stopBundledApp(o),await g()}catch(r){throw s.value=r instanceof Error?r.message:"Failed to stop app",r}finally{t.value.delete(o)}}async function q(o){a.value=!0,s.value=null;try{await v.removeContainer(o),await g()}catch(r){throw s.value=r instanceof Error?r.message:"Failed to remove container",r}finally{a.value=!1}}async function O(o,r=100){try{return await v.getContainerLogs(o,r)}catch(y){throw s.value=y instanceof Error?y.message:"Failed to get logs",y}}async function $(o){try{return await v.getContainerStatus(o)}catch(r){throw s.value=r instanceof Error?r.message:"Failed to get status",r}}return{containers:c,healthStatus:e,loading:a,loadingApps:t,error:s,runningContainers:n,stoppedContainers:i,getContainerById:l,getHealthStatus:f,getContainerForApp:u,isAppLoading:h,getAppState:m,enrichedBundledApps:_,fetchContainers:g,fetchHealthStatus:C,installApp:I,startContainer:N,stopContainer:R,removeContainer:q,getContainerLogs:O,getContainerStatus:$,startBundledApp:P,stopBundledApp:U}});class T{iframe;allowedOrigin;listener=null;constructor(e,a){this.iframe=e;try{const t=new URL(a,window.location.origin);this.allowedOrigin=t.origin}catch{this.allowedOrigin=window.location.origin}}start(){this.listener=e=>this.handleMessage(e),window.addEventListener("message",this.listener)}stop(){this.listener&&(window.removeEventListener("message",this.listener),this.listener=null)}sendPermissionsUpdate(){const e=D();this.postToIframe({type:"permissions:update",categories:e.enabledCategories})}sendTheme(){this.postToIframe({type:"theme:response",theme:{accent:"#fb923c",mode:"dark"}})}handleMessage(e){if(e.origin!==this.allowedOrigin)return;const a=e.data;if(!(!a||typeof a.type!="string"))switch(a.type){case"ready":this.sendPermissionsUpdate(),this.sendTheme();break;case"context:request":this.handleContextRequest(a.id,a.category,a.query);break;case"action:request":this.handleActionRequest(a.id,a.action,a.params);break;case"theme:request":this.sendTheme();break}}async handleContextRequest(e,a,t){if(!D().isEnabled(a)){this.postToIframe({type:"context:response",id:e,data:null,permitted:!1});return}const n=await this.fetchAndSanitize(a,t);this.postToIframe({type:"context:response",id:e,data:n,permitted:!0})}handleActionRequest(e,a,t){const s=x();let n=!1,i;try{switch(a){case"navigate":t.path?(window.dispatchEvent(new CustomEvent("aiui:navigate",{detail:t.path})),n=!0):i="Missing path parameter";break;case"open-app":case"launch-app":t.appId?this.getAppUrl(t.appId)?(window.dispatchEvent(new CustomEvent("aiui:open-app",{detail:t.appId})),n=!0):i=`App "${t.appId}" not found or not running`:i="Missing appId parameter";break;case"install-app":if(t.appId&&t.marketplaceUrl&&t.version){const f=(s.packages||{})[t.appId];if(f&&f.state==="installed"){this.postToIframe({type:"action:response",id:e,success:!1,error:`${t.appId} is already installed`});return}const u=t.appId,h=t.marketplaceUrl,m=t.version;window.dispatchEvent(new CustomEvent("aiui:install-request",{detail:{requestId:e,appId:u,marketplaceUrl:h,version:m}}));{const _=this,g=C=>{const I=C.detail;I.requestId===e&&(window.removeEventListener("aiui:install-response",g),I.confirmed?s.installPackage(u,h,m).then(()=>{_.postToIframe({type:"action:response",id:e,success:!0})}).catch(N=>{_.postToIframe({type:"action:response",id:e,success:!1,error:N.message})}):_.postToIframe({type:"action:response",id:e,success:!1,error:"User declined the installation"}))};window.addEventListener("aiui:install-response",g),setTimeout(()=>{window.removeEventListener("aiui:install-response",g)},6e4)}return}i="Missing required parameters (appId, marketplaceUrl, version)";break;case"search-web":if(t.query){this.handleSearchAction(e,t.query);return}i="Missing query parameter";break;case"read-file":this.handleReadFileAction(e,t.path);return;case"tail-logs":this.handleTailLogsAction(e,t.appId,t.lines);return;default:i=`Unknown action: ${a}`}}catch(l){i=l instanceof Error?l.message:"Unknown error"}this.postToIframe({type:"action:response",id:e,success:n,error:i})}async handleSearchAction(e,a){const n=(x().packages||{}).searxng;if(!n||n.state!=="installed"||n.installed?.status!=="running"){this.postToIframe({type:"action:response",id:e,success:!1,error:"SearXNG is not installed or not running"});return}try{const l=await(await fetch(`/apps/searxng/search?q=${encodeURIComponent(a)}&format=json`)).json();this.postToIframe({type:"action:response",id:e,success:!0,data:l})}catch(i){this.postToIframe({type:"action:response",id:e,success:!1,error:i instanceof Error?i.message:"Search failed"})}}getAppUrl(e){const s=(x().packages||{})[e];if(s?.installed?.status==="running"){const u=s.installed["interface-addresses"];if(u){const h=u.main||Object.values(u)[0];if(h?.["lan-address"])return h["lan-address"]}}const l=S().containers.find(u=>u.name===e||u.name===`archy-${e}`);return l?.lan_address?l.lan_address:F.find(u=>u.id===e)?.ports?.[0]?`/apps/${e}/`:null}async fetchAndSanitize(e,a){const t=x();switch(e){case"apps":return this.sanitizeApps(t);case"system":return await this.sanitizeSystem(t);case"network":return this.sanitizeNetwork(t);case"wallet":return this.sanitizeWallet(t);case"files":return this.sanitizeFiles();case"bitcoin":return this.sanitizeBitcoin(t);case"media":return this.sanitizeMedia(t);case"search":return this.sanitizeSearch(t);case"ai-local":return this.sanitizeAILocal(t);case"notes":return this.sanitizeNotes();default:return null}}sanitizeApps(e){const a=e.packages||{},t=S(),s=Object.entries(a).map(([i,l])=>{const f=!!l.manifest?.interfaces?.main?.ui,u=f?`/apps/${i}/`:null;return{id:i,name:l.manifest?.title||i,version:l.manifest?.version||"unknown",state:l.state||"unknown",status:l.installed?.status||"unknown",hasWebUI:f,url:u}}),n=t.containers.map(i=>({id:i.name,name:F.find(l=>l.id===i.name)?.name||i.name,state:i.state==="running"?"installed":"stopped",status:i.state,hasWebUI:!!F.find(l=>l.id===i.name)?.ports?.length,url:i.lan_address||null}));return[...s,...n]}async sanitizeSystem(e){const a=e.serverInfo,t={version:a?.version||"unknown",name:a?.name||"Archipelago"};try{const[s,n]=await Promise.all([p.call({method:"server.metrics"}),p.call({method:"server.time"})]);return{...t,cpu:s.cpu,memory:{used:s.memory.used,total:s.memory.total},disk:{used:s.disk.used,total:s.disk.total},uptime:n.uptime}}catch{return{...t,status:"metrics unavailable"}}}sanitizeNetwork(e){const a=e.serverInfo,s=S().containers.find(i=>i.name==="tailscale"),n=!!a?.["tor-address"];return{connected:e.isConnected,torConnected:n,tailscaleActive:s?.state==="running"}}async sanitizeBitcoin(e){const a=e.packages||{},t=S(),s=a.bitcoind||a["bitcoin-core"]||a.bitcoin,n=t.containers.find(l=>l.name==="bitcoin-knots"||l.name==="archy-bitcoin-knots");if(!(s?.installed?.status==="running"||n?.state==="running"))return{available:!1,message:"Bitcoin Core not running"};try{return{available:!0,status:"running",...await p.call({method:"bitcoin.getinfo"})}}catch{return{available:!0,status:"running",network:"mainnet"}}}sanitizeMedia(e){const a=e.packages||{},t=["plex","jellyfin","navidrome","nextcloud"],s=[];for(const n of t){const i=a[n];i&&(i.state==="installed"||i.state==="running"||i.state==="stopped")&&s.push({source:n,name:i.manifest?.title||n,status:i.state})}return s.length===0?{available:!1,libraries:[],message:"No media apps installed. Install Plex or Jellyfin from the App Store."}:{available:!0,libraries:s}}async sanitizeFiles(){try{if(!k.isAuthenticated&&!await k.login())return{available:!1,message:"File browser authentication failed"};const e=await k.getUsage(),a=await k.listDirectory("/"),t=a.filter(n=>n.isDir).map(n=>({name:n.name,path:n.path})),s=a.filter(n=>!n.isDir).sort((n,i)=>new Date(i.modified).getTime()-new Date(n.modified).getTime()).slice(0,10).map(n=>({name:n.name,path:n.path,size:n.size,modified:n.modified}));return{available:!0,totalSize:e.totalSize,folderCount:e.folderCount,fileCount:e.fileCount,folders:t,recentFiles:s}}catch{return{available:!1,message:"File browser not reachable"}}}sanitizeSearch(e){const t=(e.packages||{}).searxng;return!t||t.state!=="installed"||t.installed?.status!=="running"?{available:!1}:{available:!0,engine:"searxng",endpoint:"/apps/searxng/"}}sanitizeAILocal(e){const t=(e.packages||{}).ollama;return!t||t.state!=="installed"||t.installed?.status!=="running"?{available:!1}:{available:!0,models:[],message:"Ollama is running. Query /api/tags for model list."}}async sanitizeWallet(e){const a=e.packages||{},t=S(),s=a.lnd,n=t.containers.find(l=>l.name==="lnd"||l.name==="archy-lnd");if(!(s?.installed?.status==="running"||n?.state==="running"))return{available:!1,message:"Lightning (LND) not running"};try{return{available:!0,status:"running",...await p.call({method:"lnd.getinfo"})}}catch{return{available:!0,status:"running",message:"LND is running but detailed info unavailable"}}}sanitizeNotes(){return{available:!1,documents:[],message:"No note-taking apps installed"}}static ALLOWED_FILE_DIRS=["/var/lib/archipelago/","/var/log/","/opt/archipelago/","/home/archipelago/"];static SENSITIVE_PATH_PATTERNS=["id_rsa","id_ed25519","private","secret","password","seed",".env","wallet","macaroon","tls.key","tls.cert","credentials","keystore","mnemonic"];isPathAllowed(e){const a=e.replace(/\/+/g,"/").replace(/\.\.\//g,"");if(!T.ALLOWED_FILE_DIRS.some(n=>a.startsWith(n)))return!1;const s=a.toLowerCase();return!T.SENSITIVE_PATH_PATTERNS.some(n=>s.includes(n))}async handleReadFileAction(e,a){if(!D().isEnabled("files")){this.postToIframe({type:"action:response",id:e,success:!1,error:"File access not permitted"});return}if(!a){this.postToIframe({type:"action:response",id:e,success:!1,error:"Missing path parameter"});return}if(!this.isPathAllowed(a)){this.postToIframe({type:"action:response",id:e,success:!1,error:"Access denied: path is outside allowed directories or contains sensitive patterns"});return}try{if(!k.isAuthenticated&&!await k.login())throw new Error("FileBrowser authentication failed");const s=await k.readFileAsText(a);this.postToIframe({type:"action:response",id:e,success:!0,data:{content:s.content,truncated:s.truncated,size:s.size,path:a}})}catch(s){this.postToIframe({type:"action:response",id:e,success:!1,error:s instanceof Error?s.message:"Failed to read file"})}}async handleTailLogsAction(e,a,t){if(!D().isEnabled("apps")){this.postToIframe({type:"action:response",id:e,success:!1,error:"App access not permitted"});return}if(!a){this.postToIframe({type:"action:response",id:e,success:!1,error:"Missing appId parameter"});return}const n=Math.min(parseInt(t||"50",10)||50,200);try{const l=(await p.call({method:"container-logs",params:{app_id:a,lines:n}})).map(f=>T.redactLogLine(f));this.postToIframe({type:"action:response",id:e,success:!0,data:{appId:a,lines:l,count:l.length}})}catch(i){this.postToIframe({type:"action:response",id:e,success:!1,error:i instanceof Error?i.message:"Failed to fetch logs"})}}static redactLogLine(e){let a=e.replace(/(?:rpcpassword|rpcauth|password|passwd|secret|token|apikey|api_key|macaroon)[\s]*[=:]\s*\S+/gi,"$&".replace(/[=:]\s*\S+/,"=[REDACTED]"));return a=a.replace(/((?:password|secret|token|apikey|api_key|macaroon|rpcpassword|rpcauth)\s*[=:]\s*)\S+/gi,"$1[REDACTED]"),a=a.replace(/\b[0-9a-fA-F]{64,}\b/g,"[REDACTED_KEY]"),a=a.replace(/\b[A-Za-z0-9+/]{64,}={0,2}\b/g,"[REDACTED_TOKEN]"),a}postToIframe(e){this.iframe.value?.contentWindow&&this.iframe.value.contentWindow.postMessage(e,this.allowedOrigin)}}const te={class:"chat-fullscreen"},ae={class:"chat-mode-pill flex"},se=["aria-label"],ne={class:"text-xs font-medium"},ie={key:0,class:"chat-loading",role:"status","aria-live":"polite"},oe={class:"glass-card p-8 flex flex-col items-center gap-4"},re={class:"text-sm text-white/60"},le=["src","title"],ce={key:1,class:"chat-placeholder"},ue={class:"chat-placeholder-inner"},de={class:"text-2xl font-semibold text-white mb-2"},pe={class:"text-white/60 mb-4 leading-relaxed"},he={class:"text-xs text-white/30"},me=j({__name:"Chat",setup(c){const{t:e}=V(),a=X(),t=A(null);let s=null;const n=A(null),i=w(()=>n.value===!0?`/aiui/?embedded=true&hideClose=true&v=${Date.now()}`:"");async function l(){try{const h=await fetch("/aiui/",{method:"HEAD"});n.value=h.ok}catch{n.value=!1}}function f(){window.history.length>1?a.back():a.push("/dashboard")}function u(h){if(i.value){try{const m=new URL(i.value,window.location.origin).origin;if(h.origin!==m)return}catch{return}h.data?.type}}return K(async()=>{window.addEventListener("message",u),await l(),i.value&&(s=new T(t,i.value),s.start())}),G(()=>{window.removeEventListener("message",u),s?.stop(),s=null}),(h,m)=>(z(),L("div",te,[d("div",ae,[d("button",{class:"chat-close-btn","aria-label":b(e)("chat.closeAssistant"),onClick:f},[m[0]||(m[0]=d("svg",{class:"w-4 h-4","aria-hidden":"true",fill:"none",stroke:"currentColor",viewBox:"0 0 24 24"},[d("path",{"stroke-linecap":"round","stroke-linejoin":"round","stroke-width":"2",d:"M6 18L18 6M6 6l12 12"})],-1)),d("span",ne,E(b(e)("chat.close")),1)],8,se)]),Z(Q,{name:"fade"},{default:J(()=>[n.value===null?(z(),L("div",ie,[d("div",oe,[m[1]||(m[1]=d("div",{class:"chat-loading-spinner","aria-hidden":"true"},null,-1)),d("p",re,E(b(e)("chat.loadingAssistant")),1)])])):M("",!0)]),_:1}),i.value?(z(),L("iframe",{key:0,ref_key:"aiuiFrame",ref:t,src:i.value,title:b(e)("chat.aiAssistant"),class:"chat-iframe chat-iframe-mobile",allow:"microphone",style:{background:"transparent"}},null,8,le)):n.value===!1?(z(),L("div",ce,[d("div",ue,[m[2]||(m[2]=d("div",{class:"chat-placeholder-icon"},[d("svg",{class:"w-8 h-8 text-white/40","aria-hidden":"true",fill:"none",stroke:"currentColor",viewBox:"0 0 24 24"},[d("path",{"stroke-linecap":"round","stroke-linejoin":"round","stroke-width":"2",d:"M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z"})])],-1)),d("h2",de,E(b(e)("chat.aiAssistant")),1),d("p",pe,E(b(e)("chat.notConfigured")),1),d("p",he,E(b(e)("chat.deployCta")),1)])])):M("",!0)]))}}),we=Y(me,[["__scopeId","data-v-18b2b408"]]);export{we as default};