function pd(e,t){for(var n=0;nr[s]})}}}return Object.freeze(Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}))}(function(){const t=document.createElement("link").relList;if(t&&t.supports&&t.supports("modulepreload"))return;for(const s of document.querySelectorAll('link[rel="modulepreload"]'))r(s);new MutationObserver(s=>{for(const a of s)if(a.type==="childList")for(const o of a.addedNodes)o.tagName==="LINK"&&o.rel==="modulepreload"&&r(o)}).observe(document,{childList:!0,subtree:!0});function n(s){const a={};return s.integrity&&(a.integrity=s.integrity),s.referrerPolicy&&(a.referrerPolicy=s.referrerPolicy),s.crossOrigin==="use-credentials"?a.credentials="include":s.crossOrigin==="anonymous"?a.credentials="omit":a.credentials="same-origin",a}function r(s){if(s.ep)return;s.ep=!0;const a=n(s);fetch(s.href,a)}})();function hd(e){return e&&e.__esModule&&Object.prototype.hasOwnProperty.call(e,"default")?e.default:e}var Ml={exports:{}},gs={},jl={exports:{}},O={};/** * @license React * react.production.min.js * * Copyright (c) Facebook, Inc. and its affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */var ir=Symbol.for("react.element"),fd=Symbol.for("react.portal"),md=Symbol.for("react.fragment"),gd=Symbol.for("react.strict_mode"),vd=Symbol.for("react.profiler"),yd=Symbol.for("react.provider"),wd=Symbol.for("react.context"),_d=Symbol.for("react.forward_ref"),xd=Symbol.for("react.suspense"),kd=Symbol.for("react.memo"),bd=Symbol.for("react.lazy"),di=Symbol.iterator;function Sd(e){return e===null||typeof e!="object"?null:(e=di&&e[di]||e["@@iterator"],typeof e=="function"?e:null)}var Ul={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},Dl=Object.assign,Fl={};function hn(e,t,n){this.props=e,this.context=t,this.refs=Fl,this.updater=n||Ul}hn.prototype.isReactComponent={};hn.prototype.setState=function(e,t){if(typeof e!="object"&&typeof e!="function"&&e!=null)throw Error("setState(...): takes an object of state variables to update or a function which returns an object of state variables.");this.updater.enqueueSetState(this,e,t,"setState")};hn.prototype.forceUpdate=function(e){this.updater.enqueueForceUpdate(this,e,"forceUpdate")};function Bl(){}Bl.prototype=hn.prototype;function po(e,t,n){this.props=e,this.context=t,this.refs=Fl,this.updater=n||Ul}var ho=po.prototype=new Bl;ho.constructor=po;Dl(ho,hn.prototype);ho.isPureReactComponent=!0;var pi=Array.isArray,zl=Object.prototype.hasOwnProperty,fo={current:null},Vl={key:!0,ref:!0,__self:!0,__source:!0};function Wl(e,t,n){var r,s={},a=null,o=null;if(t!=null)for(r in t.ref!==void 0&&(o=t.ref),t.key!==void 0&&(a=""+t.key),t)zl.call(t,r)&&!Vl.hasOwnProperty(r)&&(s[r]=t[r]);var i=arguments.length-2;if(i===1)s.children=n;else if(1>>1,X=E[H];if(0>>1;Hs(Fs,R))kts(hr,Fs)?(E[H]=hr,E[kt]=R,H=kt):(E[H]=Fs,E[xt]=R,H=xt);else if(kts(hr,R))E[H]=hr,E[kt]=R,H=kt;else break e}}return N}function s(E,N){var R=E.sortIndex-N.sortIndex;return R!==0?R:E.id-N.id}if(typeof performance=="object"&&typeof performance.now=="function"){var a=performance;e.unstable_now=function(){return a.now()}}else{var o=Date,i=o.now();e.unstable_now=function(){return o.now()-i}}var l=[],c=[],m=1,f=null,g=3,v=!1,w=!1,_=!1,S=typeof setTimeout=="function"?setTimeout:null,p=typeof clearTimeout=="function"?clearTimeout:null,d=typeof setImmediate<"u"?setImmediate:null;typeof navigator<"u"&&navigator.scheduling!==void 0&&navigator.scheduling.isInputPending!==void 0&&navigator.scheduling.isInputPending.bind(navigator.scheduling);function h(E){for(var N=n(c);N!==null;){if(N.callback===null)r(c);else if(N.startTime<=E)r(c),N.sortIndex=N.expirationTime,t(l,N);else break;N=n(c)}}function y(E){if(_=!1,h(E),!w)if(n(l)!==null)w=!0,Us(b);else{var N=n(c);N!==null&&Ds(y,N.startTime-E)}}function b(E,N){w=!1,_&&(_=!1,p(L),L=-1),v=!0;var R=g;try{for(h(N),f=n(l);f!==null&&(!(f.expirationTime>N)||E&&!ge());){var H=f.callback;if(typeof H=="function"){f.callback=null,g=f.priorityLevel;var X=H(f.expirationTime<=N);N=e.unstable_now(),typeof X=="function"?f.callback=X:f===n(l)&&r(l),h(N)}else r(l);f=n(l)}if(f!==null)var pr=!0;else{var xt=n(c);xt!==null&&Ds(y,xt.startTime-N),pr=!1}return pr}finally{f=null,g=R,v=!1}}var P=!1,T=null,L=-1,A=5,I=-1;function ge(){return!(e.unstable_now()-IE||125H?(E.sortIndex=R,t(c,E),n(l)===null&&E===n(c)&&(_?(p(L),L=-1):_=!0,Ds(y,R-H))):(E.sortIndex=X,t(l,E),w||v||(w=!0,Us(b))),E},e.unstable_shouldYield=ge,e.unstable_wrapCallback=function(E){var N=g;return function(){var R=g;g=N;try{return E.apply(this,arguments)}finally{g=R}}}})(Kl);Ql.exports=Kl;var jd=Ql.exports;/** * @license React * react-dom.production.min.js * * Copyright (c) Facebook, Inc. and its affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */var Ud=k,_e=jd;function x(e){for(var t="https://reactjs.org/docs/error-decoder.html?invariant="+e,n=1;n"u"||typeof window.document>"u"||typeof window.document.createElement>"u"),fa=Object.prototype.hasOwnProperty,Dd=/^[:A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD][:A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD\-.0-9\u00B7\u0300-\u036F\u203F-\u2040]*$/,fi={},mi={};function Fd(e){return fa.call(mi,e)?!0:fa.call(fi,e)?!1:Dd.test(e)?mi[e]=!0:(fi[e]=!0,!1)}function Bd(e,t,n,r){if(n!==null&&n.type===0)return!1;switch(typeof t){case"function":case"symbol":return!0;case"boolean":return r?!1:n!==null?!n.acceptsBooleans:(e=e.toLowerCase().slice(0,5),e!=="data-"&&e!=="aria-");default:return!1}}function zd(e,t,n,r){if(t===null||typeof t>"u"||Bd(e,t,n,r))return!0;if(r)return!1;if(n!==null)switch(n.type){case 3:return!t;case 4:return t===!1;case 5:return isNaN(t);case 6:return isNaN(t)||1>t}return!1}function ce(e,t,n,r,s,a,o){this.acceptsBooleans=t===2||t===3||t===4,this.attributeName=r,this.attributeNamespace=s,this.mustUseProperty=n,this.propertyName=e,this.type=t,this.sanitizeURL=a,this.removeEmptyString=o}var ne={};"children dangerouslySetInnerHTML defaultValue defaultChecked innerHTML suppressContentEditableWarning suppressHydrationWarning style".split(" ").forEach(function(e){ne[e]=new ce(e,0,!1,e,null,!1,!1)});[["acceptCharset","accept-charset"],["className","class"],["htmlFor","for"],["httpEquiv","http-equiv"]].forEach(function(e){var t=e[0];ne[t]=new ce(t,1,!1,e[1],null,!1,!1)});["contentEditable","draggable","spellCheck","value"].forEach(function(e){ne[e]=new ce(e,2,!1,e.toLowerCase(),null,!1,!1)});["autoReverse","externalResourcesRequired","focusable","preserveAlpha"].forEach(function(e){ne[e]=new ce(e,2,!1,e,null,!1,!1)});"allowFullScreen async autoFocus autoPlay controls default defer disabled disablePictureInPicture disableRemotePlayback formNoValidate hidden loop noModule noValidate open playsInline readOnly required reversed scoped seamless itemScope".split(" ").forEach(function(e){ne[e]=new ce(e,3,!1,e.toLowerCase(),null,!1,!1)});["checked","multiple","muted","selected"].forEach(function(e){ne[e]=new ce(e,3,!0,e,null,!1,!1)});["capture","download"].forEach(function(e){ne[e]=new ce(e,4,!1,e,null,!1,!1)});["cols","rows","size","span"].forEach(function(e){ne[e]=new ce(e,6,!1,e,null,!1,!1)});["rowSpan","start"].forEach(function(e){ne[e]=new ce(e,5,!1,e.toLowerCase(),null,!1,!1)});var go=/[\-:]([a-z])/g;function vo(e){return e[1].toUpperCase()}"accent-height alignment-baseline arabic-form baseline-shift cap-height clip-path clip-rule color-interpolation color-interpolation-filters color-profile color-rendering dominant-baseline enable-background fill-opacity fill-rule flood-color flood-opacity font-family font-size font-size-adjust font-stretch font-style font-variant font-weight glyph-name glyph-orientation-horizontal glyph-orientation-vertical horiz-adv-x horiz-origin-x image-rendering letter-spacing lighting-color marker-end marker-mid marker-start overline-position overline-thickness paint-order panose-1 pointer-events rendering-intent shape-rendering stop-color stop-opacity strikethrough-position strikethrough-thickness stroke-dasharray stroke-dashoffset stroke-linecap stroke-linejoin stroke-miterlimit stroke-opacity stroke-width text-anchor text-decoration text-rendering underline-position underline-thickness unicode-bidi unicode-range units-per-em v-alphabetic v-hanging v-ideographic v-mathematical vector-effect vert-adv-y vert-origin-x vert-origin-y word-spacing writing-mode xmlns:xlink x-height".split(" ").forEach(function(e){var t=e.replace(go,vo);ne[t]=new ce(t,1,!1,e,null,!1,!1)});"xlink:actuate xlink:arcrole xlink:role xlink:show xlink:title xlink:type".split(" ").forEach(function(e){var t=e.replace(go,vo);ne[t]=new ce(t,1,!1,e,"http://www.w3.org/1999/xlink",!1,!1)});["xml:base","xml:lang","xml:space"].forEach(function(e){var t=e.replace(go,vo);ne[t]=new ce(t,1,!1,e,"http://www.w3.org/XML/1998/namespace",!1,!1)});["tabIndex","crossOrigin"].forEach(function(e){ne[e]=new ce(e,1,!1,e.toLowerCase(),null,!1,!1)});ne.xlinkHref=new ce("xlinkHref",1,!1,"xlink:href","http://www.w3.org/1999/xlink",!0,!1);["src","href","action","formAction"].forEach(function(e){ne[e]=new ce(e,1,!1,e.toLowerCase(),null,!0,!0)});function yo(e,t,n,r){var s=ne.hasOwnProperty(t)?ne[t]:null;(s!==null?s.type!==0:r||!(2i||s[o]!==a[i]){var l=` `+s[o].replace(" at new "," at ");return e.displayName&&l.includes("")&&(l=l.replace("",e.displayName)),l}while(1<=o&&0<=i);break}}}finally{Vs=!1,Error.prepareStackTrace=n}return(e=e?e.displayName||e.name:"")?Tn(e):""}function Vd(e){switch(e.tag){case 5:return Tn(e.type);case 16:return Tn("Lazy");case 13:return Tn("Suspense");case 19:return Tn("SuspenseList");case 0:case 2:case 15:return e=Ws(e.type,!1),e;case 11:return e=Ws(e.type.render,!1),e;case 1:return e=Ws(e.type,!0),e;default:return""}}function ya(e){if(e==null)return null;if(typeof e=="function")return e.displayName||e.name||null;if(typeof e=="string")return e;switch(e){case Bt:return"Fragment";case Ft:return"Portal";case ma:return"Profiler";case wo:return"StrictMode";case ga:return"Suspense";case va:return"SuspenseList"}if(typeof e=="object")switch(e.$$typeof){case Jl:return(e.displayName||"Context")+".Consumer";case Xl:return(e._context.displayName||"Context")+".Provider";case _o:var t=e.render;return e=e.displayName,e||(e=t.displayName||t.name||"",e=e!==""?"ForwardRef("+e+")":"ForwardRef"),e;case xo:return t=e.displayName||null,t!==null?t:ya(e.type)||"Memo";case et:t=e._payload,e=e._init;try{return ya(e(t))}catch{}}return null}function Wd(e){var t=e.type;switch(e.tag){case 24:return"Cache";case 9:return(t.displayName||"Context")+".Consumer";case 10:return(t._context.displayName||"Context")+".Provider";case 18:return"DehydratedFragment";case 11:return e=t.render,e=e.displayName||e.name||"",t.displayName||(e!==""?"ForwardRef("+e+")":"ForwardRef");case 7:return"Fragment";case 5:return t;case 4:return"Portal";case 3:return"Root";case 6:return"Text";case 16:return ya(t);case 8:return t===wo?"StrictMode":"Mode";case 22:return"Offscreen";case 12:return"Profiler";case 21:return"Scope";case 13:return"Suspense";case 19:return"SuspenseList";case 25:return"TracingMarker";case 1:case 0:case 17:case 2:case 14:case 15:if(typeof t=="function")return t.displayName||t.name||null;if(typeof t=="string")return t}return null}function gt(e){switch(typeof e){case"boolean":case"number":case"string":case"undefined":return e;case"object":return e;default:return""}}function eu(e){var t=e.type;return(e=e.nodeName)&&e.toLowerCase()==="input"&&(t==="checkbox"||t==="radio")}function Gd(e){var t=eu(e)?"checked":"value",n=Object.getOwnPropertyDescriptor(e.constructor.prototype,t),r=""+e[t];if(!e.hasOwnProperty(t)&&typeof n<"u"&&typeof n.get=="function"&&typeof n.set=="function"){var s=n.get,a=n.set;return Object.defineProperty(e,t,{configurable:!0,get:function(){return s.call(this)},set:function(o){r=""+o,a.call(this,o)}}),Object.defineProperty(e,t,{enumerable:n.enumerable}),{getValue:function(){return r},setValue:function(o){r=""+o},stopTracking:function(){e._valueTracker=null,delete e[t]}}}}function gr(e){e._valueTracker||(e._valueTracker=Gd(e))}function tu(e){if(!e)return!1;var t=e._valueTracker;if(!t)return!0;var n=t.getValue(),r="";return e&&(r=eu(e)?e.checked?"true":"false":e.value),e=r,e!==n?(t.setValue(e),!0):!1}function Gr(e){if(e=e||(typeof document<"u"?document:void 0),typeof e>"u")return null;try{return e.activeElement||e.body}catch{return e.body}}function wa(e,t){var n=t.checked;return G({},t,{defaultChecked:void 0,defaultValue:void 0,value:void 0,checked:n??e._wrapperState.initialChecked})}function vi(e,t){var n=t.defaultValue==null?"":t.defaultValue,r=t.checked!=null?t.checked:t.defaultChecked;n=gt(t.value!=null?t.value:n),e._wrapperState={initialChecked:r,initialValue:n,controlled:t.type==="checkbox"||t.type==="radio"?t.checked!=null:t.value!=null}}function nu(e,t){t=t.checked,t!=null&&yo(e,"checked",t,!1)}function _a(e,t){nu(e,t);var n=gt(t.value),r=t.type;if(n!=null)r==="number"?(n===0&&e.value===""||e.value!=n)&&(e.value=""+n):e.value!==""+n&&(e.value=""+n);else if(r==="submit"||r==="reset"){e.removeAttribute("value");return}t.hasOwnProperty("value")?xa(e,t.type,n):t.hasOwnProperty("defaultValue")&&xa(e,t.type,gt(t.defaultValue)),t.checked==null&&t.defaultChecked!=null&&(e.defaultChecked=!!t.defaultChecked)}function yi(e,t,n){if(t.hasOwnProperty("value")||t.hasOwnProperty("defaultValue")){var r=t.type;if(!(r!=="submit"&&r!=="reset"||t.value!==void 0&&t.value!==null))return;t=""+e._wrapperState.initialValue,n||t===e.value||(e.value=t),e.defaultValue=t}n=e.name,n!==""&&(e.name=""),e.defaultChecked=!!e._wrapperState.initialChecked,n!==""&&(e.name=n)}function xa(e,t,n){(t!=="number"||Gr(e.ownerDocument)!==e)&&(n==null?e.defaultValue=""+e._wrapperState.initialValue:e.defaultValue!==""+n&&(e.defaultValue=""+n))}var Ln=Array.isArray;function Xt(e,t,n,r){if(e=e.options,t){t={};for(var s=0;s"+t.valueOf().toString()+"",t=vr.firstChild;e.firstChild;)e.removeChild(e.firstChild);for(;t.firstChild;)e.appendChild(t.firstChild)}});function Wn(e,t){if(t){var n=e.firstChild;if(n&&n===e.lastChild&&n.nodeType===3){n.nodeValue=t;return}}e.textContent=t}var Rn={animationIterationCount:!0,aspectRatio:!0,borderImageOutset:!0,borderImageSlice:!0,borderImageWidth:!0,boxFlex:!0,boxFlexGroup:!0,boxOrdinalGroup:!0,columnCount:!0,columns:!0,flex:!0,flexGrow:!0,flexPositive:!0,flexShrink:!0,flexNegative:!0,flexOrder:!0,gridArea:!0,gridRow:!0,gridRowEnd:!0,gridRowSpan:!0,gridRowStart:!0,gridColumn:!0,gridColumnEnd:!0,gridColumnSpan:!0,gridColumnStart:!0,fontWeight:!0,lineClamp:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,tabSize:!0,widows:!0,zIndex:!0,zoom:!0,fillOpacity:!0,floodOpacity:!0,stopOpacity:!0,strokeDasharray:!0,strokeDashoffset:!0,strokeMiterlimit:!0,strokeOpacity:!0,strokeWidth:!0},qd=["Webkit","ms","Moz","O"];Object.keys(Rn).forEach(function(e){qd.forEach(function(t){t=t+e.charAt(0).toUpperCase()+e.substring(1),Rn[t]=Rn[e]})});function ou(e,t,n){return t==null||typeof t=="boolean"||t===""?"":n||typeof t!="number"||t===0||Rn.hasOwnProperty(e)&&Rn[e]?(""+t).trim():t+"px"}function iu(e,t){e=e.style;for(var n in t)if(t.hasOwnProperty(n)){var r=n.indexOf("--")===0,s=ou(n,t[n],r);n==="float"&&(n="cssFloat"),r?e.setProperty(n,s):e[n]=s}}var Hd=G({menuitem:!0},{area:!0,base:!0,br:!0,col:!0,embed:!0,hr:!0,img:!0,input:!0,keygen:!0,link:!0,meta:!0,param:!0,source:!0,track:!0,wbr:!0});function Sa(e,t){if(t){if(Hd[e]&&(t.children!=null||t.dangerouslySetInnerHTML!=null))throw Error(x(137,e));if(t.dangerouslySetInnerHTML!=null){if(t.children!=null)throw Error(x(60));if(typeof t.dangerouslySetInnerHTML!="object"||!("__html"in t.dangerouslySetInnerHTML))throw Error(x(61))}if(t.style!=null&&typeof t.style!="object")throw Error(x(62))}}function Ca(e,t){if(e.indexOf("-")===-1)return typeof t.is=="string";switch(e){case"annotation-xml":case"color-profile":case"font-face":case"font-face-src":case"font-face-uri":case"font-face-format":case"font-face-name":case"missing-glyph":return!1;default:return!0}}var Pa=null;function ko(e){return e=e.target||e.srcElement||window,e.correspondingUseElement&&(e=e.correspondingUseElement),e.nodeType===3?e.parentNode:e}var Ea=null,Jt=null,Zt=null;function xi(e){if(e=cr(e)){if(typeof Ea!="function")throw Error(x(280));var t=e.stateNode;t&&(t=xs(t),Ea(e.stateNode,e.type,t))}}function lu(e){Jt?Zt?Zt.push(e):Zt=[e]:Jt=e}function uu(){if(Jt){var e=Jt,t=Zt;if(Zt=Jt=null,xi(e),t)for(e=0;e>>=0,e===0?32:31-(rp(e)/sp|0)|0}var yr=64,wr=4194304;function An(e){switch(e&-e){case 1:return 1;case 2:return 2;case 4:return 4;case 8:return 8;case 16:return 16;case 32:return 32;case 64:case 128:case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:return e&4194240;case 4194304:case 8388608:case 16777216:case 33554432:case 67108864:return e&130023424;case 134217728:return 134217728;case 268435456:return 268435456;case 536870912:return 536870912;case 1073741824:return 1073741824;default:return e}}function Qr(e,t){var n=e.pendingLanes;if(n===0)return 0;var r=0,s=e.suspendedLanes,a=e.pingedLanes,o=n&268435455;if(o!==0){var i=o&~s;i!==0?r=An(i):(a&=o,a!==0&&(r=An(a)))}else o=n&~s,o!==0?r=An(o):a!==0&&(r=An(a));if(r===0)return 0;if(t!==0&&t!==r&&!(t&s)&&(s=r&-r,a=t&-t,s>=a||s===16&&(a&4194240)!==0))return t;if(r&4&&(r|=n&16),t=e.entangledLanes,t!==0)for(e=e.entanglements,t&=r;0n;n++)t.push(e);return t}function lr(e,t,n){e.pendingLanes|=t,t!==536870912&&(e.suspendedLanes=0,e.pingedLanes=0),e=e.eventTimes,t=31-Re(t),e[t]=n}function lp(e,t){var n=e.pendingLanes&~t;e.pendingLanes=t,e.suspendedLanes=0,e.pingedLanes=0,e.expiredLanes&=t,e.mutableReadLanes&=t,e.entangledLanes&=t,t=e.entanglements;var r=e.eventTimes;for(e=e.expirationTimes;0=Mn),Ai=" ",Ii=!1;function Lu(e,t){switch(e){case"keyup":return jp.indexOf(t.keyCode)!==-1;case"keydown":return t.keyCode!==229;case"keypress":case"mousedown":case"focusout":return!0;default:return!1}}function Au(e){return e=e.detail,typeof e=="object"&&"data"in e?e.data:null}var zt=!1;function Dp(e,t){switch(e){case"compositionend":return Au(t);case"keypress":return t.which!==32?null:(Ii=!0,Ai);case"textInput":return e=t.data,e===Ai&&Ii?null:e;default:return null}}function Fp(e,t){if(zt)return e==="compositionend"||!Ao&&Lu(e,t)?(e=Eu(),Or=Eo=st=null,zt=!1,e):null;switch(e){case"paste":return null;case"keypress":if(!(t.ctrlKey||t.altKey||t.metaKey)||t.ctrlKey&&t.altKey){if(t.char&&1=t)return{node:n,offset:t-e};e=r}e:{for(;n;){if(n.nextSibling){n=n.nextSibling;break e}n=n.parentNode}n=void 0}n=Mi(n)}}function Ou(e,t){return e&&t?e===t?!0:e&&e.nodeType===3?!1:t&&t.nodeType===3?Ou(e,t.parentNode):"contains"in e?e.contains(t):e.compareDocumentPosition?!!(e.compareDocumentPosition(t)&16):!1:!1}function Mu(){for(var e=window,t=Gr();t instanceof e.HTMLIFrameElement;){try{var n=typeof t.contentWindow.location.href=="string"}catch{n=!1}if(n)e=t.contentWindow;else break;t=Gr(e.document)}return t}function Io(e){var t=e&&e.nodeName&&e.nodeName.toLowerCase();return t&&(t==="input"&&(e.type==="text"||e.type==="search"||e.type==="tel"||e.type==="url"||e.type==="password")||t==="textarea"||e.contentEditable==="true")}function Qp(e){var t=Mu(),n=e.focusedElem,r=e.selectionRange;if(t!==n&&n&&n.ownerDocument&&Ou(n.ownerDocument.documentElement,n)){if(r!==null&&Io(n)){if(t=r.start,e=r.end,e===void 0&&(e=t),"selectionStart"in n)n.selectionStart=t,n.selectionEnd=Math.min(e,n.value.length);else if(e=(t=n.ownerDocument||document)&&t.defaultView||window,e.getSelection){e=e.getSelection();var s=n.textContent.length,a=Math.min(r.start,s);r=r.end===void 0?a:Math.min(r.end,s),!e.extend&&a>r&&(s=r,r=a,a=s),s=ji(n,a);var o=ji(n,r);s&&o&&(e.rangeCount!==1||e.anchorNode!==s.node||e.anchorOffset!==s.offset||e.focusNode!==o.node||e.focusOffset!==o.offset)&&(t=t.createRange(),t.setStart(s.node,s.offset),e.removeAllRanges(),a>r?(e.addRange(t),e.extend(o.node,o.offset)):(t.setEnd(o.node,o.offset),e.addRange(t)))}}for(t=[],e=n;e=e.parentNode;)e.nodeType===1&&t.push({element:e,left:e.scrollLeft,top:e.scrollTop});for(typeof n.focus=="function"&&n.focus(),n=0;n=document.documentMode,Vt=null,Ra=null,Un=null,Oa=!1;function Ui(e,t,n){var r=n.window===n?n.document:n.nodeType===9?n:n.ownerDocument;Oa||Vt==null||Vt!==Gr(r)||(r=Vt,"selectionStart"in r&&Io(r)?r={start:r.selectionStart,end:r.selectionEnd}:(r=(r.ownerDocument&&r.ownerDocument.defaultView||window).getSelection(),r={anchorNode:r.anchorNode,anchorOffset:r.anchorOffset,focusNode:r.focusNode,focusOffset:r.focusOffset}),Un&&Kn(Un,r)||(Un=r,r=Xr(Ra,"onSelect"),0qt||(e.current=Ba[qt],Ba[qt]=null,qt--)}function U(e,t){qt++,Ba[qt]=e.current,e.current=t}var vt={},oe=wt(vt),he=wt(!1),At=vt;function an(e,t){var n=e.type.contextTypes;if(!n)return vt;var r=e.stateNode;if(r&&r.__reactInternalMemoizedUnmaskedChildContext===t)return r.__reactInternalMemoizedMaskedChildContext;var s={},a;for(a in n)s[a]=t[a];return r&&(e=e.stateNode,e.__reactInternalMemoizedUnmaskedChildContext=t,e.__reactInternalMemoizedMaskedChildContext=s),s}function fe(e){return e=e.childContextTypes,e!=null}function Zr(){F(he),F(oe)}function Gi(e,t,n){if(oe.current!==vt)throw Error(x(168));U(oe,t),U(he,n)}function Gu(e,t,n){var r=e.stateNode;if(t=t.childContextTypes,typeof r.getChildContext!="function")return n;r=r.getChildContext();for(var s in r)if(!(s in t))throw Error(x(108,Wd(e)||"Unknown",s));return G({},n,r)}function es(e){return e=(e=e.stateNode)&&e.__reactInternalMemoizedMergedChildContext||vt,At=oe.current,U(oe,e),U(he,he.current),!0}function qi(e,t,n){var r=e.stateNode;if(!r)throw Error(x(169));n?(e=Gu(e,t,At),r.__reactInternalMemoizedMergedChildContext=e,F(he),F(oe),U(oe,e)):F(he),U(he,n)}var Ve=null,ks=!1,ra=!1;function qu(e){Ve===null?Ve=[e]:Ve.push(e)}function oh(e){ks=!0,qu(e)}function _t(){if(!ra&&Ve!==null){ra=!0;var e=0,t=j;try{var n=Ve;for(j=1;e>=o,s-=o,We=1<<32-Re(t)+s|n<L?(A=T,T=null):A=T.sibling;var I=g(p,T,h[L],y);if(I===null){T===null&&(T=A);break}e&&T&&I.alternate===null&&t(p,T),d=a(I,d,L),P===null?b=I:P.sibling=I,P=I,T=A}if(L===h.length)return n(p,T),B&&bt(p,L),b;if(T===null){for(;LL?(A=T,T=null):A=T.sibling;var ge=g(p,T,I.value,y);if(ge===null){T===null&&(T=A);break}e&&T&&ge.alternate===null&&t(p,T),d=a(ge,d,L),P===null?b=ge:P.sibling=ge,P=ge,T=A}if(I.done)return n(p,T),B&&bt(p,L),b;if(T===null){for(;!I.done;L++,I=h.next())I=f(p,I.value,y),I!==null&&(d=a(I,d,L),P===null?b=I:P.sibling=I,P=I);return B&&bt(p,L),b}for(T=r(p,T);!I.done;L++,I=h.next())I=v(T,p,L,I.value,y),I!==null&&(e&&I.alternate!==null&&T.delete(I.key===null?L:I.key),d=a(I,d,L),P===null?b=I:P.sibling=I,P=I);return e&&T.forEach(function(yn){return t(p,yn)}),B&&bt(p,L),b}function S(p,d,h,y){if(typeof h=="object"&&h!==null&&h.type===Bt&&h.key===null&&(h=h.props.children),typeof h=="object"&&h!==null){switch(h.$$typeof){case mr:e:{for(var b=h.key,P=d;P!==null;){if(P.key===b){if(b=h.type,b===Bt){if(P.tag===7){n(p,P.sibling),d=s(P,h.props.children),d.return=p,p=d;break e}}else if(P.elementType===b||typeof b=="object"&&b!==null&&b.$$typeof===et&&Qi(b)===P.type){n(p,P.sibling),d=s(P,h.props),d.ref=Cn(p,P,h),d.return=p,p=d;break e}n(p,P);break}else t(p,P);P=P.sibling}h.type===Bt?(d=Lt(h.props.children,p.mode,y,h.key),d.return=p,p=d):(y=Vr(h.type,h.key,h.props,null,p.mode,y),y.ref=Cn(p,d,h),y.return=p,p=y)}return o(p);case Ft:e:{for(P=h.key;d!==null;){if(d.key===P)if(d.tag===4&&d.stateNode.containerInfo===h.containerInfo&&d.stateNode.implementation===h.implementation){n(p,d.sibling),d=s(d,h.children||[]),d.return=p,p=d;break e}else{n(p,d);break}else t(p,d);d=d.sibling}d=da(h,p.mode,y),d.return=p,p=d}return o(p);case et:return P=h._init,S(p,d,P(h._payload),y)}if(Ln(h))return w(p,d,h,y);if(_n(h))return _(p,d,h,y);Pr(p,h)}return typeof h=="string"&&h!==""||typeof h=="number"?(h=""+h,d!==null&&d.tag===6?(n(p,d.sibling),d=s(d,h),d.return=p,p=d):(n(p,d),d=ca(h,p.mode,y),d.return=p,p=d),o(p)):n(p,d)}return S}var ln=Ku(!0),Yu=Ku(!1),rs=wt(null),ss=null,Qt=null,Mo=null;function jo(){Mo=Qt=ss=null}function Uo(e){var t=rs.current;F(rs),e._currentValue=t}function Wa(e,t,n){for(;e!==null;){var r=e.alternate;if((e.childLanes&t)!==t?(e.childLanes|=t,r!==null&&(r.childLanes|=t)):r!==null&&(r.childLanes&t)!==t&&(r.childLanes|=t),e===n)break;e=e.return}}function tn(e,t){ss=e,Mo=Qt=null,e=e.dependencies,e!==null&&e.firstContext!==null&&(e.lanes&t&&(pe=!0),e.firstContext=null)}function Ee(e){var t=e._currentValue;if(Mo!==e)if(e={context:e,memoizedValue:t,next:null},Qt===null){if(ss===null)throw Error(x(308));Qt=e,ss.dependencies={lanes:0,firstContext:e}}else Qt=Qt.next=e;return t}var Pt=null;function Do(e){Pt===null?Pt=[e]:Pt.push(e)}function Xu(e,t,n,r){var s=t.interleaved;return s===null?(n.next=n,Do(t)):(n.next=s.next,s.next=n),t.interleaved=n,Qe(e,r)}function Qe(e,t){e.lanes|=t;var n=e.alternate;for(n!==null&&(n.lanes|=t),n=e,e=e.return;e!==null;)e.childLanes|=t,n=e.alternate,n!==null&&(n.childLanes|=t),n=e,e=e.return;return n.tag===3?n.stateNode:null}var tt=!1;function Fo(e){e.updateQueue={baseState:e.memoizedState,firstBaseUpdate:null,lastBaseUpdate:null,shared:{pending:null,interleaved:null,lanes:0},effects:null}}function Ju(e,t){e=e.updateQueue,t.updateQueue===e&&(t.updateQueue={baseState:e.baseState,firstBaseUpdate:e.firstBaseUpdate,lastBaseUpdate:e.lastBaseUpdate,shared:e.shared,effects:e.effects})}function qe(e,t){return{eventTime:e,lane:t,tag:0,payload:null,callback:null,next:null}}function dt(e,t,n){var r=e.updateQueue;if(r===null)return null;if(r=r.shared,M&2){var s=r.pending;return s===null?t.next=t:(t.next=s.next,s.next=t),r.pending=t,Qe(e,n)}return s=r.interleaved,s===null?(t.next=t,Do(r)):(t.next=s.next,s.next=t),r.interleaved=t,Qe(e,n)}function jr(e,t,n){if(t=t.updateQueue,t!==null&&(t=t.shared,(n&4194240)!==0)){var r=t.lanes;r&=e.pendingLanes,n|=r,t.lanes=n,So(e,n)}}function Ki(e,t){var n=e.updateQueue,r=e.alternate;if(r!==null&&(r=r.updateQueue,n===r)){var s=null,a=null;if(n=n.firstBaseUpdate,n!==null){do{var o={eventTime:n.eventTime,lane:n.lane,tag:n.tag,payload:n.payload,callback:n.callback,next:null};a===null?s=a=o:a=a.next=o,n=n.next}while(n!==null);a===null?s=a=t:a=a.next=t}else s=a=t;n={baseState:r.baseState,firstBaseUpdate:s,lastBaseUpdate:a,shared:r.shared,effects:r.effects},e.updateQueue=n;return}e=n.lastBaseUpdate,e===null?n.firstBaseUpdate=t:e.next=t,n.lastBaseUpdate=t}function as(e,t,n,r){var s=e.updateQueue;tt=!1;var a=s.firstBaseUpdate,o=s.lastBaseUpdate,i=s.shared.pending;if(i!==null){s.shared.pending=null;var l=i,c=l.next;l.next=null,o===null?a=c:o.next=c,o=l;var m=e.alternate;m!==null&&(m=m.updateQueue,i=m.lastBaseUpdate,i!==o&&(i===null?m.firstBaseUpdate=c:i.next=c,m.lastBaseUpdate=l))}if(a!==null){var f=s.baseState;o=0,m=c=l=null,i=a;do{var g=i.lane,v=i.eventTime;if((r&g)===g){m!==null&&(m=m.next={eventTime:v,lane:0,tag:i.tag,payload:i.payload,callback:i.callback,next:null});e:{var w=e,_=i;switch(g=t,v=n,_.tag){case 1:if(w=_.payload,typeof w=="function"){f=w.call(v,f,g);break e}f=w;break e;case 3:w.flags=w.flags&-65537|128;case 0:if(w=_.payload,g=typeof w=="function"?w.call(v,f,g):w,g==null)break e;f=G({},f,g);break e;case 2:tt=!0}}i.callback!==null&&i.lane!==0&&(e.flags|=64,g=s.effects,g===null?s.effects=[i]:g.push(i))}else v={eventTime:v,lane:g,tag:i.tag,payload:i.payload,callback:i.callback,next:null},m===null?(c=m=v,l=f):m=m.next=v,o|=g;if(i=i.next,i===null){if(i=s.shared.pending,i===null)break;g=i,i=g.next,g.next=null,s.lastBaseUpdate=g,s.shared.pending=null}}while(!0);if(m===null&&(l=f),s.baseState=l,s.firstBaseUpdate=c,s.lastBaseUpdate=m,t=s.shared.interleaved,t!==null){s=t;do o|=s.lane,s=s.next;while(s!==t)}else a===null&&(s.shared.lanes=0);Rt|=o,e.lanes=o,e.memoizedState=f}}function Yi(e,t,n){if(e=t.effects,t.effects=null,e!==null)for(t=0;tn?n:4,e(!0);var r=aa.transition;aa.transition={};try{e(!1),t()}finally{j=n,aa.transition=r}}function mc(){return Te().memoizedState}function ch(e,t,n){var r=ht(e);if(n={lane:r,action:n,hasEagerState:!1,eagerState:null,next:null},gc(e))vc(t,n);else if(n=Xu(e,t,n,r),n!==null){var s=le();Oe(n,e,r,s),yc(n,t,r)}}function dh(e,t,n){var r=ht(e),s={lane:r,action:n,hasEagerState:!1,eagerState:null,next:null};if(gc(e))vc(t,s);else{var a=e.alternate;if(e.lanes===0&&(a===null||a.lanes===0)&&(a=t.lastRenderedReducer,a!==null))try{var o=t.lastRenderedState,i=a(o,n);if(s.hasEagerState=!0,s.eagerState=i,Me(i,o)){var l=t.interleaved;l===null?(s.next=s,Do(t)):(s.next=l.next,l.next=s),t.interleaved=s;return}}catch{}finally{}n=Xu(e,t,s,r),n!==null&&(s=le(),Oe(n,e,r,s),yc(n,t,r))}}function gc(e){var t=e.alternate;return e===V||t!==null&&t===V}function vc(e,t){Dn=is=!0;var n=e.pending;n===null?t.next=t:(t.next=n.next,n.next=t),e.pending=t}function yc(e,t,n){if(n&4194240){var r=t.lanes;r&=e.pendingLanes,n|=r,t.lanes=n,So(e,n)}}var ls={readContext:Ee,useCallback:re,useContext:re,useEffect:re,useImperativeHandle:re,useInsertionEffect:re,useLayoutEffect:re,useMemo:re,useReducer:re,useRef:re,useState:re,useDebugValue:re,useDeferredValue:re,useTransition:re,useMutableSource:re,useSyncExternalStore:re,useId:re,unstable_isNewReconciler:!1},ph={readContext:Ee,useCallback:function(e,t){return Ue().memoizedState=[e,t===void 0?null:t],e},useContext:Ee,useEffect:Ji,useImperativeHandle:function(e,t,n){return n=n!=null?n.concat([e]):null,Dr(4194308,4,cc.bind(null,t,e),n)},useLayoutEffect:function(e,t){return Dr(4194308,4,e,t)},useInsertionEffect:function(e,t){return Dr(4,2,e,t)},useMemo:function(e,t){var n=Ue();return t=t===void 0?null:t,e=e(),n.memoizedState=[e,t],e},useReducer:function(e,t,n){var r=Ue();return t=n!==void 0?n(t):t,r.memoizedState=r.baseState=t,e={pending:null,interleaved:null,lanes:0,dispatch:null,lastRenderedReducer:e,lastRenderedState:t},r.queue=e,e=e.dispatch=ch.bind(null,V,e),[r.memoizedState,e]},useRef:function(e){var t=Ue();return e={current:e},t.memoizedState=e},useState:Xi,useDebugValue:$o,useDeferredValue:function(e){return Ue().memoizedState=e},useTransition:function(){var e=Xi(!1),t=e[0];return e=uh.bind(null,e[1]),Ue().memoizedState=e,[t,e]},useMutableSource:function(){},useSyncExternalStore:function(e,t,n){var r=V,s=Ue();if(B){if(n===void 0)throw Error(x(407));n=n()}else{if(n=t(),Z===null)throw Error(x(349));Nt&30||nc(r,t,n)}s.memoizedState=n;var a={value:n,getSnapshot:t};return s.queue=a,Ji(sc.bind(null,r,a,e),[e]),r.flags|=2048,rr(9,rc.bind(null,r,a,n,t),void 0,null),n},useId:function(){var e=Ue(),t=Z.identifierPrefix;if(B){var n=Ge,r=We;n=(r&~(1<<32-Re(r)-1)).toString(32)+n,t=":"+t+"R"+n,n=tr++,0<\/script>",e=e.removeChild(e.firstChild)):typeof r.is=="string"?e=o.createElement(n,{is:r.is}):(e=o.createElement(n),n==="select"&&(o=e,r.multiple?o.multiple=!0:r.size&&(o.size=r.size))):e=o.createElementNS(e,n),e[De]=t,e[Jn]=r,Tc(e,t,!1,!1),t.stateNode=e;e:{switch(o=Ca(n,r),n){case"dialog":D("cancel",e),D("close",e),s=r;break;case"iframe":case"object":case"embed":D("load",e),s=r;break;case"video":case"audio":for(s=0;sdn&&(t.flags|=128,r=!0,Pn(a,!1),t.lanes=4194304)}else{if(!r)if(e=os(o),e!==null){if(t.flags|=128,r=!0,n=e.updateQueue,n!==null&&(t.updateQueue=n,t.flags|=4),Pn(a,!0),a.tail===null&&a.tailMode==="hidden"&&!o.alternate&&!B)return se(t),null}else 2*$()-a.renderingStartTime>dn&&n!==1073741824&&(t.flags|=128,r=!0,Pn(a,!1),t.lanes=4194304);a.isBackwards?(o.sibling=t.child,t.child=o):(n=a.last,n!==null?n.sibling=o:t.child=o,a.last=o)}return a.tail!==null?(t=a.tail,a.rendering=t,a.tail=t.sibling,a.renderingStartTime=$(),t.sibling=null,n=z.current,U(z,r?n&1|2:n&1),t):(se(t),null);case 22:case 23:return Zo(),r=t.memoizedState!==null,e!==null&&e.memoizedState!==null!==r&&(t.flags|=8192),r&&t.mode&1?ve&1073741824&&(se(t),t.subtreeFlags&6&&(t.flags|=8192)):se(t),null;case 24:return null;case 25:return null}throw Error(x(156,t.tag))}function _h(e,t){switch(Ro(t),t.tag){case 1:return fe(t.type)&&Zr(),e=t.flags,e&65536?(t.flags=e&-65537|128,t):null;case 3:return un(),F(he),F(oe),Vo(),e=t.flags,e&65536&&!(e&128)?(t.flags=e&-65537|128,t):null;case 5:return zo(t),null;case 13:if(F(z),e=t.memoizedState,e!==null&&e.dehydrated!==null){if(t.alternate===null)throw Error(x(340));on()}return e=t.flags,e&65536?(t.flags=e&-65537|128,t):null;case 19:return F(z),null;case 4:return un(),null;case 10:return Uo(t.type._context),null;case 22:case 23:return Zo(),null;case 24:return null;default:return null}}var Tr=!1,ae=!1,xh=typeof WeakSet=="function"?WeakSet:Set,C=null;function Kt(e,t){var n=e.ref;if(n!==null)if(typeof n=="function")try{n(null)}catch(r){q(e,t,r)}else n.current=null}function Ja(e,t,n){try{n()}catch(r){q(e,t,r)}}var ul=!1;function kh(e,t){if(Ma=Kr,e=Mu(),Io(e)){if("selectionStart"in e)var n={start:e.selectionStart,end:e.selectionEnd};else e:{n=(n=e.ownerDocument)&&n.defaultView||window;var r=n.getSelection&&n.getSelection();if(r&&r.rangeCount!==0){n=r.anchorNode;var s=r.anchorOffset,a=r.focusNode;r=r.focusOffset;try{n.nodeType,a.nodeType}catch{n=null;break e}var o=0,i=-1,l=-1,c=0,m=0,f=e,g=null;t:for(;;){for(var v;f!==n||s!==0&&f.nodeType!==3||(i=o+s),f!==a||r!==0&&f.nodeType!==3||(l=o+r),f.nodeType===3&&(o+=f.nodeValue.length),(v=f.firstChild)!==null;)g=f,f=v;for(;;){if(f===e)break t;if(g===n&&++c===s&&(i=o),g===a&&++m===r&&(l=o),(v=f.nextSibling)!==null)break;f=g,g=f.parentNode}f=v}n=i===-1||l===-1?null:{start:i,end:l}}else n=null}n=n||{start:0,end:0}}else n=null;for(ja={focusedElem:e,selectionRange:n},Kr=!1,C=t;C!==null;)if(t=C,e=t.child,(t.subtreeFlags&1028)!==0&&e!==null)e.return=t,C=e;else for(;C!==null;){t=C;try{var w=t.alternate;if(t.flags&1024)switch(t.tag){case 0:case 11:case 15:break;case 1:if(w!==null){var _=w.memoizedProps,S=w.memoizedState,p=t.stateNode,d=p.getSnapshotBeforeUpdate(t.elementType===t.type?_:Ae(t.type,_),S);p.__reactInternalSnapshotBeforeUpdate=d}break;case 3:var h=t.stateNode.containerInfo;h.nodeType===1?h.textContent="":h.nodeType===9&&h.documentElement&&h.removeChild(h.documentElement);break;case 5:case 6:case 4:case 17:break;default:throw Error(x(163))}}catch(y){q(t,t.return,y)}if(e=t.sibling,e!==null){e.return=t.return,C=e;break}C=t.return}return w=ul,ul=!1,w}function Fn(e,t,n){var r=t.updateQueue;if(r=r!==null?r.lastEffect:null,r!==null){var s=r=r.next;do{if((s.tag&e)===e){var a=s.destroy;s.destroy=void 0,a!==void 0&&Ja(t,n,a)}s=s.next}while(s!==r)}}function Cs(e,t){if(t=t.updateQueue,t=t!==null?t.lastEffect:null,t!==null){var n=t=t.next;do{if((n.tag&e)===e){var r=n.create;n.destroy=r()}n=n.next}while(n!==t)}}function Za(e){var t=e.ref;if(t!==null){var n=e.stateNode;switch(e.tag){case 5:e=n;break;default:e=n}typeof t=="function"?t(e):t.current=e}}function Ic(e){var t=e.alternate;t!==null&&(e.alternate=null,Ic(t)),e.child=null,e.deletions=null,e.sibling=null,e.tag===5&&(t=e.stateNode,t!==null&&(delete t[De],delete t[Jn],delete t[Fa],delete t[sh],delete t[ah])),e.stateNode=null,e.return=null,e.dependencies=null,e.memoizedProps=null,e.memoizedState=null,e.pendingProps=null,e.stateNode=null,e.updateQueue=null}function Nc(e){return e.tag===5||e.tag===3||e.tag===4}function cl(e){e:for(;;){for(;e.sibling===null;){if(e.return===null||Nc(e.return))return null;e=e.return}for(e.sibling.return=e.return,e=e.sibling;e.tag!==5&&e.tag!==6&&e.tag!==18;){if(e.flags&2||e.child===null||e.tag===4)continue e;e.child.return=e,e=e.child}if(!(e.flags&2))return e.stateNode}}function eo(e,t,n){var r=e.tag;if(r===5||r===6)e=e.stateNode,t?n.nodeType===8?n.parentNode.insertBefore(e,t):n.insertBefore(e,t):(n.nodeType===8?(t=n.parentNode,t.insertBefore(e,n)):(t=n,t.appendChild(e)),n=n._reactRootContainer,n!=null||t.onclick!==null||(t.onclick=Jr));else if(r!==4&&(e=e.child,e!==null))for(eo(e,t,n),e=e.sibling;e!==null;)eo(e,t,n),e=e.sibling}function to(e,t,n){var r=e.tag;if(r===5||r===6)e=e.stateNode,t?n.insertBefore(e,t):n.appendChild(e);else if(r!==4&&(e=e.child,e!==null))for(to(e,t,n),e=e.sibling;e!==null;)to(e,t,n),e=e.sibling}var ee=null,Ie=!1;function Ze(e,t,n){for(n=n.child;n!==null;)Rc(e,t,n),n=n.sibling}function Rc(e,t,n){if(Fe&&typeof Fe.onCommitFiberUnmount=="function")try{Fe.onCommitFiberUnmount(vs,n)}catch{}switch(n.tag){case 5:ae||Kt(n,t);case 6:var r=ee,s=Ie;ee=null,Ze(e,t,n),ee=r,Ie=s,ee!==null&&(Ie?(e=ee,n=n.stateNode,e.nodeType===8?e.parentNode.removeChild(n):e.removeChild(n)):ee.removeChild(n.stateNode));break;case 18:ee!==null&&(Ie?(e=ee,n=n.stateNode,e.nodeType===8?na(e.parentNode,n):e.nodeType===1&&na(e,n),$n(e)):na(ee,n.stateNode));break;case 4:r=ee,s=Ie,ee=n.stateNode.containerInfo,Ie=!0,Ze(e,t,n),ee=r,Ie=s;break;case 0:case 11:case 14:case 15:if(!ae&&(r=n.updateQueue,r!==null&&(r=r.lastEffect,r!==null))){s=r=r.next;do{var a=s,o=a.destroy;a=a.tag,o!==void 0&&(a&2||a&4)&&Ja(n,t,o),s=s.next}while(s!==r)}Ze(e,t,n);break;case 1:if(!ae&&(Kt(n,t),r=n.stateNode,typeof r.componentWillUnmount=="function"))try{r.props=n.memoizedProps,r.state=n.memoizedState,r.componentWillUnmount()}catch(i){q(n,t,i)}Ze(e,t,n);break;case 21:Ze(e,t,n);break;case 22:n.mode&1?(ae=(r=ae)||n.memoizedState!==null,Ze(e,t,n),ae=r):Ze(e,t,n);break;default:Ze(e,t,n)}}function dl(e){var t=e.updateQueue;if(t!==null){e.updateQueue=null;var n=e.stateNode;n===null&&(n=e.stateNode=new xh),t.forEach(function(r){var s=Ih.bind(null,e,r);n.has(r)||(n.add(r),r.then(s,s))})}}function Le(e,t){var n=t.deletions;if(n!==null)for(var r=0;rs&&(s=o),r&=~a}if(r=s,r=$()-r,r=(120>r?120:480>r?480:1080>r?1080:1920>r?1920:3e3>r?3e3:4320>r?4320:1960*Sh(r/1960))-r,10e?16:e,at===null)var r=!1;else{if(e=at,at=null,ds=0,M&6)throw Error(x(331));var s=M;for(M|=4,C=e.current;C!==null;){var a=C,o=a.child;if(C.flags&16){var i=a.deletions;if(i!==null){for(var l=0;l$()-Xo?Tt(e,0):Yo|=n),me(e,t)}function zc(e,t){t===0&&(e.mode&1?(t=wr,wr<<=1,!(wr&130023424)&&(wr=4194304)):t=1);var n=le();e=Qe(e,t),e!==null&&(lr(e,t,n),me(e,n))}function Ah(e){var t=e.memoizedState,n=0;t!==null&&(n=t.retryLane),zc(e,n)}function Ih(e,t){var n=0;switch(e.tag){case 13:var r=e.stateNode,s=e.memoizedState;s!==null&&(n=s.retryLane);break;case 19:r=e.stateNode;break;default:throw Error(x(314))}r!==null&&r.delete(t),zc(e,n)}var Vc;Vc=function(e,t,n){if(e!==null)if(e.memoizedProps!==t.pendingProps||he.current)pe=!0;else{if(!(e.lanes&n)&&!(t.flags&128))return pe=!1,yh(e,t,n);pe=!!(e.flags&131072)}else pe=!1,B&&t.flags&1048576&&Hu(t,ns,t.index);switch(t.lanes=0,t.tag){case 2:var r=t.type;Fr(e,t),e=t.pendingProps;var s=an(t,oe.current);tn(t,n),s=Go(null,t,r,e,s,n);var a=qo();return t.flags|=1,typeof s=="object"&&s!==null&&typeof s.render=="function"&&s.$$typeof===void 0?(t.tag=1,t.memoizedState=null,t.updateQueue=null,fe(r)?(a=!0,es(t)):a=!1,t.memoizedState=s.state!==null&&s.state!==void 0?s.state:null,Fo(t),s.updater=Ss,t.stateNode=s,s._reactInternals=t,qa(t,r,e,n),t=Qa(null,t,r,!0,a,n)):(t.tag=0,B&&a&&No(t),ie(null,t,s,n),t=t.child),t;case 16:r=t.elementType;e:{switch(Fr(e,t),e=t.pendingProps,s=r._init,r=s(r._payload),t.type=r,s=t.tag=Rh(r),e=Ae(r,e),s){case 0:t=$a(null,t,r,e,n);break e;case 1:t=ol(null,t,r,e,n);break e;case 11:t=sl(null,t,r,e,n);break e;case 14:t=al(null,t,r,Ae(r.type,e),n);break e}throw Error(x(306,r,""))}return t;case 0:return r=t.type,s=t.pendingProps,s=t.elementType===r?s:Ae(r,s),$a(e,t,r,s,n);case 1:return r=t.type,s=t.pendingProps,s=t.elementType===r?s:Ae(r,s),ol(e,t,r,s,n);case 3:e:{if(Cc(t),e===null)throw Error(x(387));r=t.pendingProps,a=t.memoizedState,s=a.element,Ju(e,t),as(t,r,null,n);var o=t.memoizedState;if(r=o.element,a.isDehydrated)if(a={element:r,isDehydrated:!1,cache:o.cache,pendingSuspenseBoundaries:o.pendingSuspenseBoundaries,transitions:o.transitions},t.updateQueue.baseState=a,t.memoizedState=a,t.flags&256){s=cn(Error(x(423)),t),t=il(e,t,r,n,s);break e}else if(r!==s){s=cn(Error(x(424)),t),t=il(e,t,r,n,s);break e}else for(ye=ct(t.stateNode.containerInfo.firstChild),we=t,B=!0,Ne=null,n=Yu(t,null,r,n),t.child=n;n;)n.flags=n.flags&-3|4096,n=n.sibling;else{if(on(),r===s){t=Ke(e,t,n);break e}ie(e,t,r,n)}t=t.child}return t;case 5:return Zu(t),e===null&&Va(t),r=t.type,s=t.pendingProps,a=e!==null?e.memoizedProps:null,o=s.children,Ua(r,s)?o=null:a!==null&&Ua(r,a)&&(t.flags|=32),Sc(e,t),ie(e,t,o,n),t.child;case 6:return e===null&&Va(t),null;case 13:return Pc(e,t,n);case 4:return Bo(t,t.stateNode.containerInfo),r=t.pendingProps,e===null?t.child=ln(t,null,r,n):ie(e,t,r,n),t.child;case 11:return r=t.type,s=t.pendingProps,s=t.elementType===r?s:Ae(r,s),sl(e,t,r,s,n);case 7:return ie(e,t,t.pendingProps,n),t.child;case 8:return ie(e,t,t.pendingProps.children,n),t.child;case 12:return ie(e,t,t.pendingProps.children,n),t.child;case 10:e:{if(r=t.type._context,s=t.pendingProps,a=t.memoizedProps,o=s.value,U(rs,r._currentValue),r._currentValue=o,a!==null)if(Me(a.value,o)){if(a.children===s.children&&!he.current){t=Ke(e,t,n);break e}}else for(a=t.child,a!==null&&(a.return=t);a!==null;){var i=a.dependencies;if(i!==null){o=a.child;for(var l=i.firstContext;l!==null;){if(l.context===r){if(a.tag===1){l=qe(-1,n&-n),l.tag=2;var c=a.updateQueue;if(c!==null){c=c.shared;var m=c.pending;m===null?l.next=l:(l.next=m.next,m.next=l),c.pending=l}}a.lanes|=n,l=a.alternate,l!==null&&(l.lanes|=n),Wa(a.return,n,t),i.lanes|=n;break}l=l.next}}else if(a.tag===10)o=a.type===t.type?null:a.child;else if(a.tag===18){if(o=a.return,o===null)throw Error(x(341));o.lanes|=n,i=o.alternate,i!==null&&(i.lanes|=n),Wa(o,n,t),o=a.sibling}else o=a.child;if(o!==null)o.return=a;else for(o=a;o!==null;){if(o===t){o=null;break}if(a=o.sibling,a!==null){a.return=o.return,o=a;break}o=o.return}a=o}ie(e,t,s.children,n),t=t.child}return t;case 9:return s=t.type,r=t.pendingProps.children,tn(t,n),s=Ee(s),r=r(s),t.flags|=1,ie(e,t,r,n),t.child;case 14:return r=t.type,s=Ae(r,t.pendingProps),s=Ae(r.type,s),al(e,t,r,s,n);case 15:return kc(e,t,t.type,t.pendingProps,n);case 17:return r=t.type,s=t.pendingProps,s=t.elementType===r?s:Ae(r,s),Fr(e,t),t.tag=1,fe(r)?(e=!0,es(t)):e=!1,tn(t,n),wc(t,r,s),qa(t,r,s,n),Qa(null,t,r,!0,e,n);case 19:return Ec(e,t,n);case 22:return bc(e,t,n)}throw Error(x(156,t.tag))};function Wc(e,t){return gu(e,t)}function Nh(e,t,n,r){this.tag=e,this.key=n,this.sibling=this.child=this.return=this.stateNode=this.type=this.elementType=null,this.index=0,this.ref=null,this.pendingProps=t,this.dependencies=this.memoizedState=this.updateQueue=this.memoizedProps=null,this.mode=r,this.subtreeFlags=this.flags=0,this.deletions=null,this.childLanes=this.lanes=0,this.alternate=null}function Ce(e,t,n,r){return new Nh(e,t,n,r)}function ti(e){return e=e.prototype,!(!e||!e.isReactComponent)}function Rh(e){if(typeof e=="function")return ti(e)?1:0;if(e!=null){if(e=e.$$typeof,e===_o)return 11;if(e===xo)return 14}return 2}function ft(e,t){var n=e.alternate;return n===null?(n=Ce(e.tag,t,e.key,e.mode),n.elementType=e.elementType,n.type=e.type,n.stateNode=e.stateNode,n.alternate=e,e.alternate=n):(n.pendingProps=t,n.type=e.type,n.flags=0,n.subtreeFlags=0,n.deletions=null),n.flags=e.flags&14680064,n.childLanes=e.childLanes,n.lanes=e.lanes,n.child=e.child,n.memoizedProps=e.memoizedProps,n.memoizedState=e.memoizedState,n.updateQueue=e.updateQueue,t=e.dependencies,n.dependencies=t===null?null:{lanes:t.lanes,firstContext:t.firstContext},n.sibling=e.sibling,n.index=e.index,n.ref=e.ref,n}function Vr(e,t,n,r,s,a){var o=2;if(r=e,typeof e=="function")ti(e)&&(o=1);else if(typeof e=="string")o=5;else e:switch(e){case Bt:return Lt(n.children,s,a,t);case wo:o=8,s|=8;break;case ma:return e=Ce(12,n,t,s|2),e.elementType=ma,e.lanes=a,e;case ga:return e=Ce(13,n,t,s),e.elementType=ga,e.lanes=a,e;case va:return e=Ce(19,n,t,s),e.elementType=va,e.lanes=a,e;case Zl:return Es(n,s,a,t);default:if(typeof e=="object"&&e!==null)switch(e.$$typeof){case Xl:o=10;break e;case Jl:o=9;break e;case _o:o=11;break e;case xo:o=14;break e;case et:o=16,r=null;break e}throw Error(x(130,e==null?e:typeof e,""))}return t=Ce(o,n,t,s),t.elementType=e,t.type=r,t.lanes=a,t}function Lt(e,t,n,r){return e=Ce(7,e,r,t),e.lanes=n,e}function Es(e,t,n,r){return e=Ce(22,e,r,t),e.elementType=Zl,e.lanes=n,e.stateNode={isHidden:!1},e}function ca(e,t,n){return e=Ce(6,e,null,t),e.lanes=n,e}function da(e,t,n){return t=Ce(4,e.children!==null?e.children:[],e.key,t),t.lanes=n,t.stateNode={containerInfo:e.containerInfo,pendingChildren:null,implementation:e.implementation},t}function Oh(e,t,n,r,s){this.tag=t,this.containerInfo=e,this.finishedWork=this.pingCache=this.current=this.pendingChildren=null,this.timeoutHandle=-1,this.callbackNode=this.pendingContext=this.context=null,this.callbackPriority=0,this.eventTimes=qs(0),this.expirationTimes=qs(-1),this.entangledLanes=this.finishedLanes=this.mutableReadLanes=this.expiredLanes=this.pingedLanes=this.suspendedLanes=this.pendingLanes=0,this.entanglements=qs(0),this.identifierPrefix=r,this.onRecoverableError=s,this.mutableSourceEagerHydrationData=null}function ni(e,t,n,r,s,a,o,i,l){return e=new Oh(e,t,n,i,l),t===1?(t=1,a===!0&&(t|=8)):t=0,a=Ce(3,null,null,t),e.current=a,a.stateNode=e,a.memoizedState={element:r,isDehydrated:n,cache:null,transitions:null,pendingSuspenseBoundaries:null},Fo(a),e}function Mh(e,t,n){var r=3"u"||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!="function"))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE($c)}catch(e){console.error(e)}}$c(),$l.exports=xe;var Bh=$l.exports,wl=Bh;ha.createRoot=wl.createRoot,ha.hydrateRoot=wl.hydrateRoot;/** * @remix-run/router v1.23.2 * * Copyright (c) Remix Software Inc. * * This source code is licensed under the MIT license found in the * LICENSE.md file in the root directory of this source tree. * * @license MIT */function ar(){return ar=Object.assign?Object.assign.bind():function(e){for(var t=1;t"u")throw new Error(t)}function oi(e,t){if(!e){typeof console<"u"&&console.warn(t);try{throw new Error(t)}catch{}}}function Vh(){return Math.random().toString(36).substr(2,8)}function xl(e,t){return{usr:e.state,key:e.key,idx:t}}function oo(e,t,n,r){return n===void 0&&(n=null),ar({pathname:typeof e=="string"?e:e.pathname,search:"",hash:""},typeof t=="string"?gn(t):t,{state:n,key:t&&t.key||r||Vh()})}function fs(e){let{pathname:t="/",search:n="",hash:r=""}=e;return n&&n!=="?"&&(t+=n.charAt(0)==="?"?n:"?"+n),r&&r!=="#"&&(t+=r.charAt(0)==="#"?r:"#"+r),t}function gn(e){let t={};if(e){let n=e.indexOf("#");n>=0&&(t.hash=e.substr(n),e=e.substr(0,n));let r=e.indexOf("?");r>=0&&(t.search=e.substr(r),e=e.substr(0,r)),e&&(t.pathname=e)}return t}function Wh(e,t,n,r){r===void 0&&(r={});let{window:s=document.defaultView,v5Compat:a=!1}=r,o=s.history,i=ot.Pop,l=null,c=m();c==null&&(c=0,o.replaceState(ar({},o.state,{idx:c}),""));function m(){return(o.state||{idx:null}).idx}function f(){i=ot.Pop;let S=m(),p=S==null?null:S-c;c=S,l&&l({action:i,location:_.location,delta:p})}function g(S,p){i=ot.Push;let d=oo(_.location,S,p);c=m()+1;let h=xl(d,c),y=_.createHref(d);try{o.pushState(h,"",y)}catch(b){if(b instanceof DOMException&&b.name==="DataCloneError")throw b;s.location.assign(y)}a&&l&&l({action:i,location:_.location,delta:1})}function v(S,p){i=ot.Replace;let d=oo(_.location,S,p);c=m();let h=xl(d,c),y=_.createHref(d);o.replaceState(h,"",y),a&&l&&l({action:i,location:_.location,delta:0})}function w(S){let p=s.location.origin!=="null"?s.location.origin:s.location.href,d=typeof S=="string"?S:fs(S);return d=d.replace(/ $/,"%20"),W(p,"No window.location.(origin|href) available to create URL for href: "+d),new URL(d,p)}let _={get action(){return i},get location(){return e(s,o)},listen(S){if(l)throw new Error("A history only accepts one active listener");return s.addEventListener(_l,f),l=S,()=>{s.removeEventListener(_l,f),l=null}},createHref(S){return t(s,S)},createURL:w,encodeLocation(S){let p=w(S);return{pathname:p.pathname,search:p.search,hash:p.hash}},push:g,replace:v,go(S){return o.go(S)}};return _}var kl;(function(e){e.data="data",e.deferred="deferred",e.redirect="redirect",e.error="error"})(kl||(kl={}));function Gh(e,t,n){return n===void 0&&(n="/"),qh(e,t,n)}function qh(e,t,n,r){let s=typeof t=="string"?gn(t):t,a=pn(s.pathname||"/",n);if(a==null)return null;let o=Qc(e);Hh(o);let i=null;for(let l=0;i==null&&l{let l={relativePath:i===void 0?a.path||"":i,caseSensitive:a.caseSensitive===!0,childrenIndex:o,route:a};l.relativePath.startsWith("/")&&(W(l.relativePath.startsWith(r),'Absolute route path "'+l.relativePath+'" nested under path '+('"'+r+'" is not valid. An absolute child route path ')+"must start with the combined path of all its parent routes."),l.relativePath=l.relativePath.slice(r.length));let c=mt([r,l.relativePath]),m=n.concat(l);a.children&&a.children.length>0&&(W(a.index!==!0,"Index routes must not have child routes. Please remove "+('all child routes from route path "'+c+'".')),Qc(a.children,t,m,c)),!(a.path==null&&!a.index)&&t.push({path:c,score:Zh(c,a.index),routesMeta:m})};return e.forEach((a,o)=>{var i;if(a.path===""||!((i=a.path)!=null&&i.includes("?")))s(a,o);else for(let l of Kc(a.path))s(a,o,l)}),t}function Kc(e){let t=e.split("/");if(t.length===0)return[];let[n,...r]=t,s=n.endsWith("?"),a=n.replace(/\?$/,"");if(r.length===0)return s?[a,""]:[a];let o=Kc(r.join("/")),i=[];return i.push(...o.map(l=>l===""?a:[a,l].join("/"))),s&&i.push(...o),i.map(l=>e.startsWith("/")&&l===""?"/":l)}function Hh(e){e.sort((t,n)=>t.score!==n.score?n.score-t.score:ef(t.routesMeta.map(r=>r.childrenIndex),n.routesMeta.map(r=>r.childrenIndex)))}const $h=/^:[\w-]+$/,Qh=3,Kh=2,Yh=1,Xh=10,Jh=-2,bl=e=>e==="*";function Zh(e,t){let n=e.split("/"),r=n.length;return n.some(bl)&&(r+=Jh),t&&(r+=Kh),n.filter(s=>!bl(s)).reduce((s,a)=>s+($h.test(a)?Qh:a===""?Yh:Xh),r)}function ef(e,t){return e.length===t.length&&e.slice(0,-1).every((r,s)=>r===t[s])?e[e.length-1]-t[t.length-1]:0}function tf(e,t,n){let{routesMeta:r}=e,s={},a="/",o=[];for(let i=0;i{let{paramName:g,isOptional:v}=m;if(g==="*"){let _=i[f]||"";o=a.slice(0,a.length-_.length).replace(/(.)\/+$/,"$1")}const w=i[f];return v&&!w?c[g]=void 0:c[g]=(w||"").replace(/%2F/g,"/"),c},{}),pathname:a,pathnameBase:o,pattern:e}}function nf(e,t,n){t===void 0&&(t=!1),n===void 0&&(n=!0),oi(e==="*"||!e.endsWith("*")||e.endsWith("/*"),'Route path "'+e+'" will be treated as if it were '+('"'+e.replace(/\*$/,"/*")+'" because the `*` character must ')+"always follow a `/` in the pattern. To get rid of this warning, "+('please change the route path to "'+e.replace(/\*$/,"/*")+'".'));let r=[],s="^"+e.replace(/\/*\*?$/,"").replace(/^\/*/,"/").replace(/[\\.*+^${}|()[\]]/g,"\\$&").replace(/\/:([\w-]+)(\?)?/g,(o,i,l)=>(r.push({paramName:i,isOptional:l!=null}),l?"/?([^\\/]+)?":"/([^\\/]+)"));return e.endsWith("*")?(r.push({paramName:"*"}),s+=e==="*"||e==="/*"?"(.*)$":"(?:\\/(.+)|\\/*)$"):n?s+="\\/*$":e!==""&&e!=="/"&&(s+="(?:(?=\\/|$))"),[new RegExp(s,t?void 0:"i"),r]}function rf(e){try{return e.split("/").map(t=>decodeURIComponent(t).replace(/\//g,"%2F")).join("/")}catch(t){return oi(!1,'The URL path "'+e+'" could not be decoded because it is is a malformed URL segment. This is probably due to a bad percent '+("encoding ("+t+").")),e}}function pn(e,t){if(t==="/")return e;if(!e.toLowerCase().startsWith(t.toLowerCase()))return null;let n=t.endsWith("/")?t.length-1:t.length,r=e.charAt(n);return r&&r!=="/"?null:e.slice(n)||"/"}const sf=/^(?:[a-z][a-z0-9+.-]*:|\/\/)/i,af=e=>sf.test(e);function of(e,t){t===void 0&&(t="/");let{pathname:n,search:r="",hash:s=""}=typeof e=="string"?gn(e):e,a;if(n)if(af(n))a=n;else{if(n.includes("//")){let o=n;n=n.replace(/\/\/+/g,"/"),oi(!1,"Pathnames cannot have embedded double slashes - normalizing "+(o+" -> "+n))}n.startsWith("/")?a=Sl(n.substring(1),"/"):a=Sl(n,t)}else a=t;return{pathname:a,search:cf(r),hash:df(s)}}function Sl(e,t){let n=t.replace(/\/+$/,"").split("/");return e.split("/").forEach(s=>{s===".."?n.length>1&&n.pop():s!=="."&&n.push(s)}),n.length>1?n.join("/"):"/"}function pa(e,t,n,r){return"Cannot include a '"+e+"' character in a manually specified "+("`to."+t+"` field ["+JSON.stringify(r)+"]. Please separate it out to the ")+("`to."+n+"` field. Alternatively you may provide the full path as ")+'a string in and the router will parse it for you.'}function lf(e){return e.filter((t,n)=>n===0||t.route.path&&t.route.path.length>0)}function ii(e,t){let n=lf(e);return t?n.map((r,s)=>s===n.length-1?r.pathname:r.pathnameBase):n.map(r=>r.pathnameBase)}function li(e,t,n,r){r===void 0&&(r=!1);let s;typeof e=="string"?s=gn(e):(s=ar({},e),W(!s.pathname||!s.pathname.includes("?"),pa("?","pathname","search",s)),W(!s.pathname||!s.pathname.includes("#"),pa("#","pathname","hash",s)),W(!s.search||!s.search.includes("#"),pa("#","search","hash",s)));let a=e===""||s.pathname==="",o=a?"/":s.pathname,i;if(o==null)i=n;else{let f=t.length-1;if(!r&&o.startsWith("..")){let g=o.split("/");for(;g[0]==="..";)g.shift(),f-=1;s.pathname=g.join("/")}i=f>=0?t[f]:"/"}let l=of(s,i),c=o&&o!=="/"&&o.endsWith("/"),m=(a||o===".")&&n.endsWith("/");return!l.pathname.endsWith("/")&&(c||m)&&(l.pathname+="/"),l}const mt=e=>e.join("/").replace(/\/\/+/g,"/"),uf=e=>e.replace(/\/+$/,"").replace(/^\/*/,"/"),cf=e=>!e||e==="?"?"":e.startsWith("?")?e:"?"+e,df=e=>!e||e==="#"?"":e.startsWith("#")?e:"#"+e;function pf(e){return e!=null&&typeof e.status=="number"&&typeof e.statusText=="string"&&typeof e.internal=="boolean"&&"data"in e}const Yc=["post","put","patch","delete"];new Set(Yc);const hf=["get",...Yc];new Set(hf);/** * React Router v6.30.3 * * Copyright (c) Remix Software Inc. * * This source code is licensed under the MIT license found in the * LICENSE.md file in the root directory of this source tree. * * @license MIT */function or(){return or=Object.assign?Object.assign.bind():function(e){for(var t=1;t{i.current=!0}),k.useCallback(function(c,m){if(m===void 0&&(m={}),!i.current)return;if(typeof c=="number"){r.go(c);return}let f=li(c,JSON.parse(o),a,m.relative==="path");e==null&&t!=="/"&&(f.pathname=f.pathname==="/"?t:mt([t,f.pathname])),(m.replace?r.replace:r.push)(f,m.state,m)},[t,r,o,a,e])}function gf(){let{matches:e}=k.useContext(Je),t=e[e.length-1];return t?t.params:{}}function Ms(e,t){let{relative:n}=t===void 0?{}:t,{future:r}=k.useContext(Xe),{matches:s}=k.useContext(Je),{pathname:a}=Ut(),o=JSON.stringify(ii(s,r.v7_relativeSplatPath));return k.useMemo(()=>li(e,JSON.parse(o),a,n==="path"),[e,o,a,n])}function vf(e,t){return yf(e,t)}function yf(e,t,n,r){vn()||W(!1);let{navigator:s}=k.useContext(Xe),{matches:a}=k.useContext(Je),o=a[a.length-1],i=o?o.params:{};o&&o.pathname;let l=o?o.pathnameBase:"/";o&&o.route;let c=Ut(),m;if(t){var f;let S=typeof t=="string"?gn(t):t;l==="/"||(f=S.pathname)!=null&&f.startsWith(l)||W(!1),m=S}else m=c;let g=m.pathname||"/",v=g;if(l!=="/"){let S=l.replace(/^\//,"").split("/");v="/"+g.replace(/^\//,"").split("/").slice(S.length).join("/")}let w=Gh(e,{pathname:v}),_=bf(w&&w.map(S=>Object.assign({},S,{params:Object.assign({},i,S.params),pathname:mt([l,s.encodeLocation?s.encodeLocation(S.pathname).pathname:S.pathname]),pathnameBase:S.pathnameBase==="/"?l:mt([l,s.encodeLocation?s.encodeLocation(S.pathnameBase).pathname:S.pathnameBase])})),a,n,r);return t&&_?k.createElement(Rs.Provider,{value:{location:or({pathname:"/",search:"",hash:"",state:null,key:"default"},m),navigationType:ot.Pop}},_):_}function wf(){let e=Ef(),t=pf(e)?e.status+" "+e.statusText:e instanceof Error?e.message:JSON.stringify(e),n=e instanceof Error?e.stack:null,s={padding:"0.5rem",backgroundColor:"rgba(200,200,200, 0.5)"};return k.createElement(k.Fragment,null,k.createElement("h2",null,"Unexpected Application Error!"),k.createElement("h3",{style:{fontStyle:"italic"}},t),n?k.createElement("pre",{style:s},n):null,null)}const _f=k.createElement(wf,null);class xf extends k.Component{constructor(t){super(t),this.state={location:t.location,revalidation:t.revalidation,error:t.error}}static getDerivedStateFromError(t){return{error:t}}static getDerivedStateFromProps(t,n){return n.location!==t.location||n.revalidation!=="idle"&&t.revalidation==="idle"?{error:t.error,location:t.location,revalidation:t.revalidation}:{error:t.error!==void 0?t.error:n.error,location:n.location,revalidation:t.revalidation||n.revalidation}}componentDidCatch(t,n){console.error("React Router caught the following error during render",t,n)}render(){return this.state.error!==void 0?k.createElement(Je.Provider,{value:this.props.routeContext},k.createElement(Jc.Provider,{value:this.state.error,children:this.props.component})):this.props.children}}function kf(e){let{routeContext:t,match:n,children:r}=e,s=k.useContext(Ns);return s&&s.static&&s.staticContext&&(n.route.errorElement||n.route.ErrorBoundary)&&(s.staticContext._deepestRenderedBoundaryId=n.route.id),k.createElement(Je.Provider,{value:t},r)}function bf(e,t,n,r){var s;if(t===void 0&&(t=[]),n===void 0&&(n=null),r===void 0&&(r=null),e==null){var a;if(!n)return null;if(n.errors)e=n.matches;else if((a=r)!=null&&a.v7_partialHydration&&t.length===0&&!n.initialized&&n.matches.length>0)e=n.matches;else return null}let o=e,i=(s=n)==null?void 0:s.errors;if(i!=null){let m=o.findIndex(f=>f.route.id&&(i==null?void 0:i[f.route.id])!==void 0);m>=0||W(!1),o=o.slice(0,Math.min(o.length,m+1))}let l=!1,c=-1;if(n&&r&&r.v7_partialHydration)for(let m=0;m=0?o=o.slice(0,c+1):o=[o[0]];break}}}return o.reduceRight((m,f,g)=>{let v,w=!1,_=null,S=null;n&&(v=i&&f.route.id?i[f.route.id]:void 0,_=f.route.errorElement||_f,l&&(c<0&&g===0?(Lf("route-fallback"),w=!0,S=null):c===g&&(w=!0,S=f.route.hydrateFallbackElement||null)));let p=t.concat(o.slice(0,g+1)),d=()=>{let h;return v?h=_:w?h=S:f.route.Component?h=k.createElement(f.route.Component,null):f.route.element?h=f.route.element:h=m,k.createElement(kf,{match:f,routeContext:{outlet:m,matches:p,isDataRoute:n!=null},children:h})};return n&&(f.route.ErrorBoundary||f.route.errorElement||g===0)?k.createElement(xf,{location:n.location,revalidation:n.revalidation,component:_,error:v,children:d(),routeContext:{outlet:null,matches:p,isDataRoute:!0}}):d()},null)}var ed=function(e){return e.UseBlocker="useBlocker",e.UseRevalidator="useRevalidator",e.UseNavigateStable="useNavigate",e}(ed||{}),td=function(e){return e.UseBlocker="useBlocker",e.UseLoaderData="useLoaderData",e.UseActionData="useActionData",e.UseRouteError="useRouteError",e.UseNavigation="useNavigation",e.UseRouteLoaderData="useRouteLoaderData",e.UseMatches="useMatches",e.UseRevalidator="useRevalidator",e.UseNavigateStable="useNavigate",e.UseRouteId="useRouteId",e}(td||{});function Sf(e){let t=k.useContext(Ns);return t||W(!1),t}function Cf(e){let t=k.useContext(Xc);return t||W(!1),t}function Pf(e){let t=k.useContext(Je);return t||W(!1),t}function nd(e){let t=Pf(),n=t.matches[t.matches.length-1];return n.route.id||W(!1),n.route.id}function Ef(){var e;let t=k.useContext(Jc),n=Cf(),r=nd();return t!==void 0?t:(e=n.errors)==null?void 0:e[r]}function Tf(){let{router:e}=Sf(ed.UseNavigateStable),t=nd(td.UseNavigateStable),n=k.useRef(!1);return Zc(()=>{n.current=!0}),k.useCallback(function(s,a){a===void 0&&(a={}),n.current&&(typeof s=="number"?e.navigate(s):e.navigate(s,or({fromRouteId:t},a)))},[e,t])}const Cl={};function Lf(e,t,n){Cl[e]||(Cl[e]=!0)}function Af(e,t){e==null||e.v7_startTransition,e==null||e.v7_relativeSplatPath}function If(e){let{to:t,replace:n,state:r,relative:s}=e;vn()||W(!1);let{future:a,static:o}=k.useContext(Xe),{matches:i}=k.useContext(Je),{pathname:l}=Ut(),c=Os(),m=li(t,ii(i,a.v7_relativeSplatPath),l,s==="path"),f=JSON.stringify(m);return k.useEffect(()=>c(JSON.parse(f),{replace:n,state:r,relative:s}),[c,f,s,n,r]),null}function Nn(e){W(!1)}function Nf(e){let{basename:t="/",children:n=null,location:r,navigationType:s=ot.Pop,navigator:a,static:o=!1,future:i}=e;vn()&&W(!1);let l=t.replace(/^\/*/,"/"),c=k.useMemo(()=>({basename:l,navigator:a,static:o,future:or({v7_relativeSplatPath:!1},i)}),[l,i,a,o]);typeof r=="string"&&(r=gn(r));let{pathname:m="/",search:f="",hash:g="",state:v=null,key:w="default"}=r,_=k.useMemo(()=>{let S=pn(m,l);return S==null?null:{location:{pathname:S,search:f,hash:g,state:v,key:w},navigationType:s}},[l,m,f,g,v,w,s]);return _==null?null:k.createElement(Xe.Provider,{value:c},k.createElement(Rs.Provider,{children:n,value:_}))}function Rf(e){let{children:t,location:n}=e;return vf(lo(t),n)}new Promise(()=>{});function lo(e,t){t===void 0&&(t=[]);let n=[];return k.Children.forEach(e,(r,s)=>{if(!k.isValidElement(r))return;let a=[...t,s];if(r.type===k.Fragment){n.push.apply(n,lo(r.props.children,a));return}r.type!==Nn&&W(!1),!r.props.index||!r.props.children||W(!1);let o={id:r.props.id||a.join("-"),caseSensitive:r.props.caseSensitive,element:r.props.element,Component:r.props.Component,index:r.props.index,path:r.props.path,loader:r.props.loader,action:r.props.action,errorElement:r.props.errorElement,ErrorBoundary:r.props.ErrorBoundary,hasErrorBoundary:r.props.ErrorBoundary!=null||r.props.errorElement!=null,shouldRevalidate:r.props.shouldRevalidate,handle:r.props.handle,lazy:r.props.lazy};r.props.children&&(o.children=lo(r.props.children,a)),n.push(o)}),n}/** * React Router DOM v6.30.3 * * Copyright (c) Remix Software Inc. * * This source code is licensed under the MIT license found in the * LICENSE.md file in the root directory of this source tree. * * @license MIT */function ms(){return ms=Object.assign?Object.assign.bind():function(e){for(var t=1;t=0)&&(n[s]=e[s]);return n}function Of(e){return!!(e.metaKey||e.altKey||e.ctrlKey||e.shiftKey)}function Mf(e,t){return e.button===0&&(!t||t==="_self")&&!Of(e)}const jf=["onClick","relative","reloadDocument","replace","state","target","to","preventScrollReset","viewTransition"],Uf=["aria-current","caseSensitive","className","end","style","to","viewTransition","children"],Df="6";try{window.__reactRouterVersion=Df}catch{}const Ff=k.createContext({isTransitioning:!1}),Bf="startTransition",Pl=Ld[Bf];function zf(e){let{basename:t,children:n,future:r,window:s}=e,a=k.useRef();a.current==null&&(a.current=zh({window:s,v5Compat:!0}));let o=a.current,[i,l]=k.useState({action:o.action,location:o.location}),{v7_startTransition:c}=r||{},m=k.useCallback(f=>{c&&Pl?Pl(()=>l(f)):l(f)},[l,c]);return k.useLayoutEffect(()=>o.listen(m),[o,m]),k.useEffect(()=>Af(r),[r]),k.createElement(Nf,{basename:t,children:n,location:i.location,navigationType:i.action,navigator:o,future:r})}const Vf=typeof window<"u"&&typeof window.document<"u"&&typeof window.document.createElement<"u",Wf=/^(?:[a-z][a-z0-9+.-]*:|\/\/)/i,Gf=k.forwardRef(function(t,n){let{onClick:r,relative:s,reloadDocument:a,replace:o,state:i,target:l,to:c,preventScrollReset:m,viewTransition:f}=t,g=rd(t,jf),{basename:v}=k.useContext(Xe),w,_=!1;if(typeof c=="string"&&Wf.test(c)&&(w=c,Vf))try{let h=new URL(window.location.href),y=c.startsWith("//")?new URL(h.protocol+c):new URL(c),b=pn(y.pathname,v);y.origin===h.origin&&b!=null?c=b+y.search+y.hash:_=!0}catch{}let S=ff(c,{relative:s}),p=Hf(c,{replace:o,state:i,target:l,preventScrollReset:m,relative:s,viewTransition:f});function d(h){r&&r(h),h.defaultPrevented||p(h)}return k.createElement("a",ms({},g,{href:w||S,onClick:_||a?r:d,ref:n,target:l}))}),El=k.forwardRef(function(t,n){let{"aria-current":r="page",caseSensitive:s=!1,className:a="",end:o=!1,style:i,to:l,viewTransition:c,children:m}=t,f=rd(t,Uf),g=Ms(l,{relative:f.relative}),v=Ut(),w=k.useContext(Xc),{navigator:_,basename:S}=k.useContext(Xe),p=w!=null&&$f(g)&&c===!0,d=_.encodeLocation?_.encodeLocation(g).pathname:g.pathname,h=v.pathname,y=w&&w.navigation&&w.navigation.location?w.navigation.location.pathname:null;s||(h=h.toLowerCase(),y=y?y.toLowerCase():null,d=d.toLowerCase()),y&&S&&(y=pn(y,S)||y);const b=d!=="/"&&d.endsWith("/")?d.length-1:d.length;let P=h===d||!o&&h.startsWith(d)&&h.charAt(b)==="/",T=y!=null&&(y===d||!o&&y.startsWith(d)&&y.charAt(d.length)==="/"),L={isActive:P,isPending:T,isTransitioning:p},A=P?r:void 0,I;typeof a=="function"?I=a(L):I=[a,P?"active":null,T?"pending":null,p?"transitioning":null].filter(Boolean).join(" ");let ge=typeof i=="function"?i(L):i;return k.createElement(Gf,ms({},f,{"aria-current":A,className:I,ref:n,style:ge,to:l,viewTransition:c}),typeof m=="function"?m(L):m)});var uo;(function(e){e.UseScrollRestoration="useScrollRestoration",e.UseSubmit="useSubmit",e.UseSubmitFetcher="useSubmitFetcher",e.UseFetcher="useFetcher",e.useViewTransitionState="useViewTransitionState"})(uo||(uo={}));var Tl;(function(e){e.UseFetcher="useFetcher",e.UseFetchers="useFetchers",e.UseScrollRestoration="useScrollRestoration"})(Tl||(Tl={}));function qf(e){let t=k.useContext(Ns);return t||W(!1),t}function Hf(e,t){let{target:n,replace:r,state:s,preventScrollReset:a,relative:o,viewTransition:i}=t===void 0?{}:t,l=Os(),c=Ut(),m=Ms(e,{relative:o});return k.useCallback(f=>{if(Mf(f,n)){f.preventDefault();let g=r!==void 0?r:fs(c)===fs(m);l(e,{replace:g,state:s,preventScrollReset:a,relative:o,viewTransition:i})}},[c,l,m,r,s,n,e,a,o,i])}function $f(e,t){t===void 0&&(t={});let n=k.useContext(Ff);n==null&&W(!1);let{basename:r}=qf(uo.useViewTransitionState),s=Ms(e,{relative:t.relative});if(!n.isTransitioning)return!1;let a=pn(n.currentLocation.pathname,r)||n.currentLocation.pathname,o=pn(n.nextLocation.pathname,r)||n.nextLocation.pathname;return io(s.pathname,o)!=null||io(s.pathname,a)!=null}function Qf(){const[e,t]=k.useState(()=>localStorage.getItem("theme")||"warm");return k.useEffect(()=>{document.documentElement.setAttribute("data-theme",e),localStorage.setItem("theme",e)},[e]),{theme:e,toggle:()=>t(r=>r==="warm"?"dark":"warm")}}const Kf=[{to:"/",label:"Apps",end:!0},{to:"/mcp-servers",label:"MCP Servers",end:!1}],Yf="https://github.com/cuga-project/cuga-apps/issues/new",Xf="https://github.com/cuga-project/cuga-apps",Jf="https://github.com/cuga-project/cuga-agent";function Zf({children:e}){const{theme:t,toggle:n}=Qf();return u.jsxs("div",{className:"flex flex-col h-screen overflow-hidden bg-tbg",children:[u.jsxs("header",{className:"h-16 flex-shrink-0 bg-tsurf border-b border-tborder flex items-center px-6 gap-5",children:[u.jsxs(El,{to:"/",className:"flex items-center gap-3 shrink-0",children:[u.jsx("div",{className:"w-8 h-8 bg-indigo-600 flex items-center justify-center text-base font-semibold text-white",children:"C"}),u.jsxs("span",{className:"text-t1 font-semibold text-lg tracking-tight",children:["CUGA ",u.jsx("span",{className:"text-t3 font-normal",children:"Apps"})]})]}),u.jsxs("nav",{className:"flex items-center gap-1 h-full",children:[Kf.map(r=>u.jsx(El,{to:r.to,end:r.end,className:({isActive:s})=>`relative h-full flex items-center px-4 text-sm font-medium transition-colors ${s?"text-t1 after:absolute after:inset-x-0 after:bottom-0 after:h-0.5 after:bg-indigo-600":"text-t3 hover:text-t1"}`,children:r.label},r.to)),u.jsx("a",{href:Yf,target:"_blank",rel:"noopener noreferrer",className:"relative h-full flex items-center px-4 text-sm font-medium text-t3 hover:text-t1 transition-colors",children:"Feedback ↗"})]}),u.jsx("div",{className:"flex-1"}),u.jsxs("a",{href:Jf,target:"_blank",rel:"noopener noreferrer",title:"Star CUGA on GitHub",className:"flex items-center gap-2 px-4 h-9 rounded-full text-sm font-semibold text-[#3a2c08] bg-[linear-gradient(135deg,#f5dca0_0%,#e6c074_50%,#d4a857_100%)] border border-[#d8b970] shadow-[0_2px_8px_rgba(180,130,40,0.22)] hover:brightness-105 transition",children:[u.jsx("span",{"aria-hidden":"true",className:"text-base leading-none",children:"⭐"}),u.jsx("span",{children:"Star CUGA on GitHub"})]}),u.jsxs("a",{href:Xf,target:"_blank",rel:"noopener noreferrer",title:"cuga-apps source on GitHub",className:"flex items-center gap-2 px-3 h-9 bg-tsurf border border-tborder text-sm font-medium text-t2 hover:bg-tsurf2 hover:text-t1 transition-colors",children:[u.jsx("svg",{viewBox:"0 0 16 16","aria-hidden":"true",className:"w-4 h-4 fill-current",children:u.jsx("path",{d:"M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.01 8.01 0 0016 8c0-4.42-3.58-8-8-8z"})}),u.jsx("span",{children:"GitHub"})]}),u.jsx("button",{onClick:n,className:"flex items-center gap-2 px-4 h-9 bg-tsurf border border-tborder text-sm font-medium text-t2 hover:bg-tsurf2 hover:text-t1 transition-colors",children:u.jsx("span",{children:t==="warm"?"☀ Light":"🌙 Gray 100"})})]}),u.jsx("div",{className:"flex-shrink-0 bg-amber-100 border-b border-amber-300 px-6 py-2 text-xs text-amber-900",children:"Heads up: these are public demo apps. Your requests are logged for usage analytics — please don't enter confidential information, credentials, or personal data."}),u.jsx("main",{className:"flex-1 overflow-y-auto",children:e})]})}const sd=[{id:"stock-alert",name:"Stock & Crypto Alert",tagline:"Ask market questions or set a threshold alert — browser UI, browser-only notifications",description:"A browser UI with two panels. Market Query: type any symbol and ask a free-form question — the agent fetches live data and answers with prices and % changes highlighted. Price Watch: add a watch (symbol, threshold, above/below); the browser polls every 5 minutes and triggered alerts surface in the Recent Alerts panel at the bottom of the page (in-UI, no browser notifications, no email). Watches, alert history, and the Alpha Vantage API key all live entirely in the user's own browser (localStorage). The key is sent on each request and forwarded to mcp-finance as a tool argument, then scrubbed from the agent's reply before returning — never persisted on the server. Different browsers see only their own watches and use their own Alpha Vantage quotas. Crypto via CoinGecko (no key needed); stocks via Alpha Vantage (per-user key).",category:"personal",type:"event-driven",surface:"pipeline",status:"working",channels:[],tools:["get_crypto_price()","get_stock_quote()"],demoPath:"apps/stock_alert",howToRun:{envVars:["LLM_PROVIDER","LLM_MODEL","ALPHA_VANTAGE_API_KEY"],setup:["cd apps/stock_alert","pip install -r requirements.txt"],command:"python main.py"},architecture:"FastAPI serves the single-page UI. Server is stateless w.r.t. watches — only two endpoints touch the agent: POST /ask (free-form market query) and POST /check (one threshold check for a single symbol). Each request opens a fresh agent thread (uuid-suffixed) so concurrent users do not share conversation state. Watches, alert history, and notification permission live in browser localStorage; a setInterval loop in the page polls /check every 5 min for each watch and calls Notification API on triggers. Per-user isolation is automatic — every browser is its own world.",diagram:`python main.py → http://127.0.0.1:28801 Panel 1 — Market Query (on-demand): User: "What is the current price and 24h change?" Symbol: BTC Type: Crypto │ POST /ask (stateless — fresh agent thread per call) ▼ CugaAgent + finance MCP │ get_crypto_price("BTC") ← CoinGecko public API ▼ "BTC is $84,230 (+2.3% in 24h)…" Panel 2 — Price Watch (browser-driven, per-user): Browser: addWatch(BTC, above, $90,000) → localStorage │ setInterval(5 min) │ POST /check { symbol, threshold, direction } ▼ CugaAgent + finance MCP │ agent.invoke("Check BTC price. Alert threshold: $90,000 (above).") ▼ { triggered: true, message: "PRICE ALERT — BTC crossed $90,000" } │ ▼ new Notification("Stock Alert — BTC", { body: ... }) + appended to localStorage alerts log (capped at 50)`,cugaContribution:["finance MCP wraps CoinGecko and Alpha Vantage — agent gets live prices, volume, and market cap without any HTTP code",'CugaAgent + inlined synthesis prompt defines the "PRICE ALERT" sentinel; the server parses one substring and the browser does the rest — no LLM-side delivery logic',"Stateless server: every /ask and /check opens a uuid-suffixed thread so concurrent users never share conversation history. No per-user backend identity needed","Per-user isolation by construction — watches and alert history live in browser localStorage, so a second user on a second browser sees only their own watches without an account system",'Browser Notification API + 30-min cooldown per watch — matches the "alert me when crossed" semantics without spamming every poll cycle while a price stays past threshold'],examples:["What is the current price and 24h change?","Is this a good entry point compared to recent range?","Compare BTC and ETH — which is performing better today?","Give me a quick bull or bear read on SOL right now.","Add Watch: BTC above $90,000 — surfaces in Recent Alerts when crossed","Add Watch: AAPL below $180"],appUrl:"http://localhost:28801",mcpUsage:[{server:"finance",tools:["get_crypto_price","get_stock_quote"]}],inlineTools:[]},{id:"server-monitor",name:"Server Monitor",tagline:"Real-time server health gauges, chat diagnostics, and threshold alerts",type:"event-driven",description:"A browser UI with four panels: Live Metrics (CPU/RAM/Disk/load gauges, colour-coded, auto-refreshed every 15s), Chat (ask the DevOps agent anything about system health), Alert Log (background asyncio monitor logs threshold breaches with full diagnoses), and Alert Settings (configure poll interval, cooldown, and thresholds — persisted to .store.json). No CugaHost, no channels — just CugaAgent + psutil + FastAPI.",category:"enterprise",surface:"pipeline",status:"working",channels:[],tools:["get_system_metrics()","list_top_processes()","check_disk_usage()","find_large_files()","get_service_status()","run_safe_command()"],demoPath:"apps/server_monitor",howToRun:{envVars:["LLM_PROVIDER","LLM_MODEL"],setup:["cd apps/server_monitor","pip install -r requirements.txt"],command:"python main.py"},architecture:"FastAPI serves the single-page UI. Chat: POST /ask → CugaAgent.invoke(question) → psutil-based tools → diagnosis. Background monitor: asyncio loop polls metrics every N seconds; when a threshold is breached and cooldown has elapsed, the agent diagnoses and appends to the Alert Log. Thresholds, poll interval, and cooldown are configurable in the UI and persisted to .store.json.",diagram:`python main.py → http://127.0.0.1:8767 Live Metrics panel (auto-refresh every 15s): CPU 45% ██████░░░░ RAM 61% ████████░░ Disk 72% █████████░ Chat panel (on-demand): User: "What's eating my disk?" │ POST /ask ▼ CugaAgent + check_disk_usage() + find_large_files() │ check_disk_usage("/") │ find_large_files("/", min_mb=500) ▼ "/var/log is 12 GB (47% of disk). Largest file: app.log 8.2 GB" Alert Log (background asyncio loop): asyncio loop (every 60s) │ get_system_metrics() → CPU 92% > critical threshold ▼ CugaAgent ("CPU critical: 92%. Diagnose and recommend action.") │ list_top_processes(sort_by="cpu") ▼ Alert entry: "python train.py consuming 88% CPU since 14:02"`,cugaContribution:["CugaAgent + skills/server_health.md — the skill defines severity levels, report format, and safety rules (never rm, never kill PIDs)","run_safe_command() enforces an allowlist (df, du, uptime, ps, netstat, …) — agent gets shell access without arbitrary execution risk","Background asyncio monitor replaces a separate cron daemon — threshold polling and cooldown logic are self-contained in main.py","All settings configurable from the UI without restart — thresholds, poll interval, and cooldown persist to .store.json"],examples:["What's the current server health?","What's using the most CPU right now?","What's eating my disk?","Why is the server slow?","Is nginx running?","Find files larger than 500MB","Give me a full health briefing"],appUrl:"http://localhost:28767",mcpUsage:[{server:"local",tools:["get_system_metrics_with_alerts"]}],inlineTools:["list_top_processes","check_disk_usage","find_large_files","get_service_status","run_safe_command"]},{id:"newsletter",name:"Newsletter Intelligence",tagline:"Monitor RSS feeds, ask questions over live articles, set keyword alerts — in-UI panel, no email",type:"event-driven",surface:"pipeline",description:"A browser UI with two panels. Feed Query: ask any question over your configured RSS feeds — the agent fetches live articles and answers in plain language. Scheduled Alerts: configure keyword monitors that run hourly or daily; the agent searches your feeds and matches surface in the Recent Alerts panel at the bottom of the page (no email). Feed list and alert rules persist in .store.json across restarts; the alerts log is in-memory and refreshed every 30s by the browser.",category:"personal",status:"working",channels:[],tools:["fetch_feed()","search_feeds()"],demoPath:"apps/newsletter",howToRun:{envVars:["LLM_PROVIDER","LLM_MODEL"],setup:["cd apps/newsletter","pip install -r requirements.txt"],command:"python main.py"},architecture:"FastAPI web server serves the single-page UI. Feed Query: POST /ask → CugaAgent.invoke(question) → make_feed_tools() fetches and parses RSS/Atom feeds → answer. Scheduled Alerts: asyncio background scheduler checks each alert on its cron interval → agent searches feeds for keyword matches → triggered alerts surface in the in-UI Recent Alerts panel. All state saved to .store.json.",diagram:`python main.py → http://127.0.0.1:18793 Panel 1 — Feed Query (on-demand): User: "Find anything about agentic AI this week" │ POST /ask ▼ CugaAgent + make_feed_tools() │ fetch_feed("https://arxiv.org/rss/cs.AI") │ search_feeds(keywords="agentic AI") ▼ "Found 3 matching articles: …" Panel 2 — Scheduled Alerts (background scheduler): Alert: keywords="LLM release" schedule=hourly │ asyncio scheduler fires ▼ agent.invoke("Check feeds for: LLM release …") │ "ALERT: Found 2 matches …" ▼ Recent Alerts panel (in-UI, refreshed every 30s)`,cugaContribution:["make_feed_tools() wraps feedparser — agent gets structured article lists (title, URL, summary, published) without any HTTP or XML code","CugaAgent + skills/newsletter.md — the skill file defines search format and alert rules; swap to change behaviour","Persistent state — .store.json restores feeds and alert schedules on restart","Alerts surface in-UI — no SMTP setup, no app passwords; the page polls and renders matches as they arrive"],examples:["Summarize the latest AI research papers from my feeds","Find anything about agentic AI or multi-agent systems","What new LLM releases happened this week?","What are the key AI trends from my feeds today?",'Add alert: keywords="agent frameworks", schedule=daily'],appUrl:"http://localhost:28793",mcpUsage:[{server:"web",tools:["fetch_feed","search_feeds"]}],inlineTools:[]},{id:"video-qa",name:"Video Q&A",tagline:"Transcribe a video, then ask questions with exact timestamps",type:"audio-video",surface:"pipeline",description:"Load a video or audio file and ask questions about it in natural language. faster-whisper transcribes locally (cached on disk after the first run), sentence-transformers embeds each segment into ChromaDB, and CugaAgent answers with bold timestamps. Runs as a CLI REPL or a browser UI with a searchable transcript panel.",category:"personal",status:"working",channels:[],tools:["transcribe_audio()","search_transcript()","get_segment_at_time()"],demoPath:"apps/video_qa",howToRun:{envVars:["LLM_PROVIDER","LLM_MODEL"],setup:["cd apps/video_qa","pip install -r requirements.txt","brew install ffmpeg"],command:"python run.py --web"},architecture:"Two phases: Phase 1 (Python only) — ffmpeg extracts audio, faster-whisper transcribes to timestamped segments, sentence-transformers embeds, ChromaDB stores on disk. Phase 2 (CugaAgent) — user question → search_transcript() cosine similarity → get_segment_at_time() timestamp lookup → answer with citations. The LLM only handles retrieval and reasoning.",diagram:`Phase 1 — Transcription (Python, no LLM, cached) meeting.mp4 │ ffmpeg extract audio ▼ faster-whisper → [{start, end, text}, ...] │ sentence-transformers embed ▼ ChromaDB (disk cache) Phase 2 — Q&A (CugaAgent) User: "Where was M3 discussed?" │ ▼ CugaAgent (guided by skills/video_qa.md) ├─ search_transcript("M3") ← ChromaDB cosine similarity └─ get_segment_at_time(600) ← timestamp lookup │ ▼ "M3 was introduced at **00:04** and benchmarks covered at **10:02–11:45**"`,cugaContribution:["CugaAgent + skills/video_qa.md — the skill file defines tool usage, timestamp format, and citation rules; swap it to change behaviour without touching agent code","Two-phase architecture — transcription is deterministic Python (zero LLM tokens); the LLM only reasons over search results","Transcript cached on disk — re-running the app on the same file skips transcription entirely","Browser UI (python run.py --web) adds a filterable transcript panel; clicking a segment pre-fills the question box"],examples:["python run.py meeting.mp4",'python run.py meeting.mp4 --ask "where was M3 discussed?"',"python run.py --web","Where was the Q2 budget discussed?","What decisions were made?","What was said around the 30-minute mark?","Summarise the key action items"],appUrl:"http://localhost:28766",mcpUsage:[],inlineTools:["transcribe_audio","search_transcript","get_segment_at_time"]},{id:"drop-summarizer",name:"Drop Summarizer",tagline:"Upload any file — get a plain-English summary instantly, in-UI feed only",type:"document-intelligence",surface:"gateway",description:"A browser UI with an upload panel and a summary feed. Upload any .txt, .md, .pdf, or image file via the browser — the app extracts text (docling in a subprocess for PDF/images; plain read for TXT/MD), passes it to the agent for summarisation, and the result appears in the feed within seconds. Click any file to ask follow-up questions over the stored content. No folder watcher, no email — every summary is the result of an active upload, and the feed itself is the alerts surface. Summaries stored in SQLite.",category:"personal",status:"working",channels:[],tools:[],demoPath:"apps/drop_summarizer",howToRun:{envVars:["LLM_PROVIDER","LLM_MODEL"],setup:["cd apps/drop_summarizer","pip install -r requirements.txt","pip install docling # optional: for PDF and image support"],command:"python main.py"},architecture:"FastAPI serves the single-page UI. Upload: user drops a file via the browser; the app extracts text (.txt/.md read directly; .pdf/images via docling running in a subprocess so OOM kills do not crash the server), then passes the content to the agent for summarisation. App stores content + summary in SQLite. Chat: POST /ask → for specific files, the stored content is injected into the prompt; for general queries, recent summaries are injected as context.",diagram:`python main.py → http://127.0.0.1:18794 User uploads ./inbox/report.pdf via the browser │ ▼ App extracts text │ .txt/.md → read directly │ .pdf/image → docling subprocess ▼ CugaAgent (content injected): summarise → summary │ ▼ SQLite: store { filename, summary, full_content } │ ▼ UI: summary card appears in feed Chat panel (click any file to focus): User: "What were the key risks in this report?" │ POST /ask (file content injected into prompt) ▼ CugaAgent → answer`,cugaContribution:["Docling extraction runs in a subprocess — large/complex PDFs cannot OOM-kill the server","Agent only sees clean extracted text — no token waste on tool plumbing or HTML noise","Background asyncio watcher replaces inotify/polling boilerplate — file arrives, summary appears automatically","Persistent SQLite store — summaries and full content survive restarts; click any past file to resume Q&A"],examples:["cp ~/Downloads/q1_report.pdf ./inbox/","cp ~/Downloads/meeting_notes.md ./inbox/","cp ~/Downloads/research_paper.pdf ./inbox/","What were the key risks in this report?","Summarise the action items from the meeting notes","What is the main conclusion?"],appUrl:"http://localhost:28794",mcpUsage:[{server:"text",tools:["extract_text"]}],inlineTools:[]},{id:"web-researcher",name:"Web Researcher",tagline:"Schedule recurring web research — results land in the in-UI Research Log",type:"event-driven",surface:"pipeline",description:"A browser UI for scheduling recurring web research tasks. Add topics with hourly / daily / weekly cadences — the background scheduler runs overdue topics every 5 minutes using Tavily and appends results to the Research Log panel (no email). Also supports ad-hoc searches via the chat panel. Research history persists across restarts in SQLite; the UI auto-refreshes every 30s so scheduled completions surface without a manual reload.",category:"personal",status:"working",channels:[],tools:["web_search()"],demoPath:"apps/web_researcher",howToRun:{envVars:["LLM_PROVIDER","LLM_MODEL","TAVILY_API_KEY"],setup:["cd apps/web_researcher","pip install -r requirements.txt"],command:"python main.py"},architecture:"FastAPI serves the single-page UI. Chat: POST /ask → CugaAgent calls web_search (Tavily) → answer. Scheduled topics: asyncio background scheduler checks every 5 minutes for overdue topics; when due, runs the agent and appends results to the in-UI Research Log (no email). Topic schedule persisted in .store.json.",diagram:`python main.py → http://127.0.0.1:18798 Chat panel (ad-hoc): User: "What's the latest news on quantum computing?" │ POST /ask ▼ CugaAgent + web_search() │ web_search("quantum computing news 2026") ▼ "IBM announced a 1000-qubit processor..." Scheduled topics panel: Topic: "AI agent frameworks" Schedule: daily │ asyncio scheduler fires (every 5 min — checks overdue) ▼ CugaAgent + web_search() │ web_search("AI agent frameworks 2026") ▼ SQLite: log result in research.db │ ▼ Research Log panel (in-UI, auto-refreshed every 30s)`,cugaContribution:["CugaAgent synthesises multiple Tavily search results into a structured report — not just a list of links","Background scheduler checks overdue topics every 5 minutes without a cron daemon or external task runner","Persistent log in SQLite — all research results survive restarts and are viewable in the history panel","Results surface in-UI — no SMTP setup, no app passwords; the Research Log auto-refreshes"],examples:["What's the latest news on quantum computing?","Search for Python 3.13 release notes","Find recent papers on RAG architectures","What are the top stories about climate policy this week?",'Add topic: "AI agent frameworks" → daily'],appUrl:"http://localhost:28798",mcpUsage:[{server:"web",tools:["web_search"]}],inlineTools:[]},{id:"voice-journal",name:"Voice Journal",tagline:"Drop a voice memo — Whisper transcribes, agent structures, SQLite stores",type:"audio-video",surface:"pipeline",description:"A personal journal that accepts audio recordings (.m4a, .mp3, .wav) and text entries via a browser UI. OpenAI Whisper API transcribes audio automatically (local Whisper as fallback). The agent structures each entry and stores it in SQLite alongside a Markdown file. A background watcher monitors ./inbox for new files. A configurable email digest sends a summary of recent entries on schedule.",category:"personal",status:"working",channels:["EmailChannel"],tools:["transcribe_audio()","save_journal_entry()","list_entries()","list_dates()"],demoPath:"apps/voice_journal",howToRun:{envVars:["LLM_PROVIDER","LLM_MODEL","OPENAI_API_KEY","SMTP_HOST","SMTP_USERNAME","SMTP_PASSWORD","DIGEST_TO"],setup:["cd apps/voice_journal","pip install -r requirements.txt","# optional local whisper fallback:","pip install openai-whisper"],command:"python main.py"},architecture:"FastAPI serves the single-page UI. Inbox watcher: asyncio loop monitors ./inbox for new audio/text files; audio is transcribed via OpenAI Whisper API, then the agent structures the entry and stores it in SQLite + a Markdown file under ./entries/. Email digest: configurable schedule sends a summary of recent entries via SMTP.",diagram:`python main.py → http://127.0.0.1:18799 Inbox watcher (background, auto): ./inbox/memo_20260421.m4a (new file detected) │ ▼ OpenAI Whisper API → clean transcript text │ ▼ CugaAgent ("Structure this journal entry: mood, topics, action items") │ ▼ SQLite (journal.db) + ./entries/memo_20260421.md Upload / quick-write panel: User uploads audio or types an entry │ POST /entry or file upload ▼ Same transcribe → structure → store pipeline Chat panel (on-demand): User: "What themes came up this week?" │ POST /ask (recent entries injected as context) ▼ CugaAgent → "You mentioned deadlines and team collaboration…"`,cugaContribution:["Agent structures raw transcripts into mood, topics, and action items — not just a plain text dump","Whisper API handles transcription; agent only sees clean text — zero transcription tokens wasted","Inbox watcher processes audio automatically on drop — no manual trigger needed","Configurable email digest keeps you connected to your journal without opening the app"],examples:["Click 📎 and upload a .m4a or .mp3 voice note","What did I write about last week?","Summarize my entries from this month","What themes keep coming up in my journal?","Show me everything I recorded on Monday"],appUrl:"http://localhost:28799",mcpUsage:[{server:"local",tools:["transcribe_audio"]}],inlineTools:["save_journal_entry","list_entries","list_dates"]},{id:"smart-todo",name:"Smart Todo",type:"event-driven",tagline:"AI-powered task management with natural-language input — in-UI reminders, no email",surface:"pipeline",description:'A conversational todo manager with a browser UI. Add tasks in natural language ("remind me to review the PR before EOD"), set due dates, and the background watcher polls SQLite every 60s for due reminders. When a reminder fires it surfaces in the Recent Alerts panel at the bottom of the page (no email, no per-user accounts). Tasks stored in SQLite survive restarts. The tabbed board shows Todos, Reminders, Notes, and Done.',category:"personal",status:"working",channels:[],tools:["save_todo()","list_todos()","mark_done()"],demoPath:"apps/smart_todo",howToRun:{envVars:["LLM_PROVIDER","LLM_MODEL"],setup:["cd apps/smart_todo","pip install -r requirements.txt"],command:"python main.py"},architecture:"FastAPI serves the single-page UI. Chat: POST /ask → CugaAgent parses natural language → adds todos to SQLite (todos.db) with due dates extracted from context. Background reminder watcher: asyncio loop checks every 60s for overdue items and surfaces them in the Recent Alerts panel. Todo board renders Todos / Reminders / Notes / Done tabs from SQLite on each load.",diagram:`python main.py → http://127.0.0.1:18800 Chat panel (on-demand): User: "Remind me to review the PR before EOD" │ POST /ask ▼ CugaAgent → parses due date, category, priority │ inserts todo into todos.db ▼ "Got it! PR review added — due today at 17:00." Todo board (tabbed, loaded from SQLite): Todos | Reminders | Notes | Done ───────────────────────────────── ● Review the PR due today 17:00 ● Deploy to production due Friday 17:00 Background reminder watcher: asyncio loop (every 60s) │ query todos.db for overdue items ▼ Recent Alerts panel (in-UI): "Reminder: Review the PR is due now"`,cugaContribution:['Natural language due-date extraction — "by EOD", "Friday at 5pm", "tomorrow morning" all resolve to timestamps',"Categorises input automatically into Todos, Reminders, or Notes based on phrasing","Persistent SQLite store — todos and notes survive restarts; no reconfiguration needed","Background reminder watcher fires on due time and surfaces alerts in-UI — no SMTP, no app passwords"],examples:["Remind me to review the PR by EOD","Add high priority: deploy to production by Friday at 5pm","What are my open todos?","What's due today?","Mark the PR review as done","Add a note: check with Alice about the project timeline"],appUrl:"http://localhost:28800",mcpUsage:[],inlineTools:["save_todo","list_todos","mark_done"]},{id:"travel-agent",name:"Travel Planner",tagline:"Plan a full trip in a conversation — live weather, attractions, and web search",type:"other",surface:"gateway",description:"A conversational travel planning agent with a browser UI. Describe your trip and the agent builds a day-by-day itinerary using live data: Wikipedia city overviews, real-time weather (wttr.in), geocoding (Nominatim/OSM), points of interest (OpenTripMap), and web search (Tavily). Also showcases CugaAgent vs LangGraph ReAct side-by-side on the same tools — same task, two architectures, one UI.",category:"personal",status:"working",channels:[],tools:["get_city_overview()","get_weather()","get_coordinates()","search_attractions()","search_web()"],demoPath:"apps/travel_planner",howToRun:{envVars:["ANTHROPIC_API_KEY","TAVILY_API_KEY","OPENTRIPMAP_API_KEY"],setup:["cd apps/travel_planner"],command:"uv run --project . main.py"},architecture:"FastAPI serves the single-page UI. POST /plan → CugaAgent calls get_city_overview(), get_weather(), search_attractions(), web_search() → full day-by-day itinerary with budget breakdown. POST /chat → multi-turn follow-up on the same plan. POST /configure injects API keys at runtime — no restart needed. The LangGraph ReAct backend is available as an alternative via the same UI toggle.",diagram:`uv run main.py → http://127.0.0.1:8090 User: "5 days in Kyoto in March, mid-range budget" │ POST /plan ▼ CugaAgent ├─ get_city_overview("Kyoto") ← Wikipedia REST API ├─ get_weather("Kyoto", "March") ← wttr.in ├─ search_attractions("Kyoto") ← OpenTripMap └─ web_search("Kyoto March tips")← Tavily │ ▼ Day-by-day itinerary + budget breakdown POST /chat — follow-up in the same session: User: "Move the temple visit to Day 2 and add a tea ceremony" │ ▼ CugaAgent (full plan in context) → updated itinerary`,cugaContribution:["CugaAgent coordinates four live data sources in a single pass — no orchestration glue code required","Side-by-side CugaAgent vs LangGraph ReAct on identical tools — same prompt, same task, toggle between architectures in the UI","POST /configure injects API keys at runtime — demo audience can provide keys without restarting the server","Multi-turn POST /chat preserves the full itinerary as conversation context — follow-up edits just work"],examples:["5 days in Kyoto in March, mid-range budget","Weekend in Barcelona — focus on food and architecture","10 days in Japan: Tokyo, Kyoto, and Osaka","Family trip to Rome, 7 days, two kids under 10","Move the temple visit to Day 2 and add a tea ceremony","What's the weather like during my trip?"],appUrl:"http://localhost:28090",mcpUsage:[{server:"web",tools:["web_search"]},{server:"knowledge",tools:["get_wikipedia_article"]},{server:"geo",tools:["geocode","search_attractions","get_weather"]}],inlineTools:[]},{id:"deck-forge",name:"Deck Forge",tagline:"Point at a folder of docs, PDFs, and recordings — get a polished slide deck",type:"document-intelligence",surface:"pipeline",description:"An AI presentation architect powered by a LangGraph ReAct agent and a RAG knowledge base. Give it a local directory (PDFs, slides, markdown, recordings) and a topic — the agent discovers every file, extracts and indexes the content with ChromaDB + sentence-transformers, reasons about a narrative arc, and builds a coherent slide deck with speaker notes. Output: a .pptx file and a structured Markdown report. Progress streams live to the browser via SSE.",category:"enterprise",status:"working",channels:[],tools:["list_directory()","extract_and_index()","search_knowledge_base()","add_slide()","finalize()"],demoPath:"apps/deck_forge",howToRun:{envVars:["LLM_PROVIDER","LLM_MODEL","ANTHROPIC_API_KEY","RITS_API_KEY","OPENAI_API_KEY"],setup:["cd apps/deck_forge","pip install -r requirements.txt"],command:"python main.py --port 18802"},architecture:"FastAPI serves the single-page UI. POST /api/generate creates a session and launches an asyncio task that runs the LangGraph ReAct agent. The agent calls five async tools (closed over the session): list_directory discovers files; extract_and_index uses pdfplumber / python-pptx / faster-whisper to extract text and chunk-embed it into an ephemeral ChromaDB collection; search_knowledge_base retrieves relevant chunks by semantic similarity; add_slide accumulates slides; finalize writes deck.pptx and deck.md. Each tool pushes typed events to session.queue, which the SSE endpoint streams to the browser in real time.",diagram:`python main.py → http://127.0.0.1:18802 User: directory=/research/transformers topic="Self-Attention in Transformers" │ POST /api/generate ▼ LangGraph ReAct Agent ├─ list_directory("/research/transformers") │ → 3 PDFs, 1 PPTX, 2 Markdown files │ ├─ extract_and_index("attention_paper.pdf") │ → pdfplumber → 847 chunks → ChromaDB ├─ extract_and_index("overview.md") → 23 chunks ├─ extract_and_index("slides.pptx") → 41 chunks │ ├─ search_knowledge_base("self-attention query key value") │ → top 5 chunks from 3 sources │ ├─ [agent reasons: 9 slides needed, sections: intro, mechanism, BERT, scaling, takeaways] │ ├─ add_slide("Introduction to Transformers", bullets=[...], notes="...") ├─ add_slide("The Self-Attention Mechanism", ...) ├─ ... (9 slides total) │ └─ finalize("Transformer Architecture") → deck.pptx (10 slides incl. title) → deck.md (structured text report) SSE stream → browser: live progress per tool call`,cugaContribution:["Agent owns all content decisions — which files are relevant, narrative arc, section structure, slide count, deduplication across sources; the app is a thin shell","Chunked RAG retrieval — each slide gets a targeted search query, pulling the most relevant content from the indexed corpus","Five async tools push typed SSE events (directory_scanned, indexed, search, slide_added, done) — live progress without polling","Works on any LLM provider via the shared _llm.py factory — RITS, Anthropic, OpenAI, WatsonX, or local Ollama","LangGraph ReAct graph with CugaAgent placeholder — toggle between architectures in the UI once CugaAgent is wired"],examples:['directory=/Users/me/research/transformers, topic="Self-Attention and BERT"','directory=/Users/me/project/design_docs, topic="Vakra Architecture Overview"','directory=/Users/me/talks/ai_summit, topic="Enterprise AI Deployment Challenges"','directory=/Users/me/papers/multimodal, topic="Vision Transformers and DALL-E"'],appUrl:"http://localhost:28802",mcpUsage:[],inlineTools:["list_directory","extract_and_index","search_knowledge_base","add_slide","finalize"]},{id:"youtube-research",name:"YouTube Research",tagline:"Research any topic via YouTube — find videos, fetch transcripts, synthesise with citations",type:"audio-video",surface:"gateway",description:"A browser UI for topic research powered by YouTube content. Topic mode: type a subject and the agent searches the web for relevant YouTube videos, fetches their transcripts, and synthesises findings organised by theme with citations and timestamps. URL mode: paste one or more YouTube links directly for instant summaries with key moments. Research history stored in SQLite.",category:"personal",status:"working",channels:[],tools:["web_search()","get_video_info()","get_transcript()"],demoPath:"apps/youtube_research",howToRun:{envVars:["LLM_PROVIDER","LLM_MODEL","TAVILY_API_KEY"],setup:["cd apps/youtube_research","pip install -r requirements.txt"],command:"python main.py"},architecture:"FastAPI serves the single-page UI. POST /ask → CugaAgent uses web_search (Tavily) to find YouTube videos, get_video_info (oEmbed) for metadata, get_transcript (youtube-transcript-api) for captions → synthesises across transcripts with citations and timestamps. Research log stored in SQLite.",diagram:`python main.py → http://127.0.0.1:18803 Topic mode: User: "Latest developments in AI agents" │ POST /ask ▼ CugaAgent ├─ web_search("AI agents youtube 2026") ├─ web_search("AI agent frameworks site:youtube.com") │ → 5 YouTube URLs found │ ├─ get_video_info(url1..url5) → titles, channels ├─ get_transcript(url1..url4) → timestamped captions │ (url5: no captions — skipped) ▼ Synthesis by theme with citations: "Both Channel A ([12:30]) and Channel B ([08:15]) emphasise…" URL mode: User: "https://youtube.com/watch?v=abc — summarise this" │ ▼ CugaAgent → get_video_info + get_transcript → summary with timestamps`,cugaContribution:["CugaAgent decides search strategy — generates 2-3 varied queries to surface the best YouTube results","Agent synthesises across multiple video transcripts by theme, not per-video — cross-referencing what different creators say","Citation format with channel attribution and timestamps is enforced by the skill prompt","Transcripts capped at ~5000 words per video to stay within context limits; agent handles truncation gracefully"],examples:["Latest developments in AI agents","How does RLHF work?","Best practices for RAG pipelines","https://youtube.com/watch?v=VIDEO_ID — summarise this video","Compare what these creators say about fine-tuning: [url1] [url2]","What did they say about scaling laws around the 20-minute mark?"],appUrl:"http://localhost:28803",mcpUsage:[{server:"web",tools:["web_search","get_youtube_video_info","get_youtube_transcript"]}],inlineTools:[]},{id:"arch-diagram",name:"Architecture Diagram Generator",tagline:"Describe a system in plain English, get a rendered architecture diagram",type:"document-intelligence",surface:"gateway",description:"A browser UI that turns natural-language system descriptions into rendered architecture diagrams. The agent generates Mermaid.js code (flowcharts, sequence diagrams, ER diagrams, state diagrams) and the browser renders it as interactive SVG. Supports iterative refinement — ask the agent to add, remove, or change components and it updates the diagram. Optionally uses web search to research unfamiliar technologies before diagramming. Diagrams downloadable as SVG.",category:"enterprise",status:"working",channels:[],tools:["web_search()"],demoPath:"apps/arch_diagram",howToRun:{envVars:["LLM_PROVIDER","LLM_MODEL","TAVILY_API_KEY"],setup:["cd apps/arch_diagram","pip install -r requirements.txt"],command:"python main.py"},architecture:"FastAPI serves the single-page UI with mermaid.js loaded from CDN. POST /ask → CugaAgent generates Mermaid code in a fenced code block → server extracts the code via regex → frontend renders SVG via mermaid.js. The system prompt includes full Mermaid syntax reference with examples for each diagram type to minimise invalid output. Iterative refinement works via the agent thread — the agent remembers the previous diagram and modifies it.",diagram:`python main.py → http://127.0.0.1:18804 User: "Design a microservices e-commerce platform" │ POST /ask ▼ CugaAgent (system prompt includes Mermaid syntax reference) │ (optional) web_search("microservices patterns") ▼ Response contains: \`\`\`mermaid graph TD Client["Browser"] -->|HTTPS| GW["API Gateway"] GW --> UserSvc["User Service"] GW --> OrderSvc["Order Service"] OrderSvc --> MQ["Message Queue"] MQ --> PaySvc["Payment Service"] \`\`\` + explanation of each component │ ▼ Frontend: mermaid.js renders SVG → Download SVG / Copy code User: "Add a Redis cache between services and the database" ▼ CugaAgent → updated diagram with cache node added`,cugaContribution:["CugaAgent picks the best diagram type (flowchart, sequence, ER, state) based on what the user describes","System prompt includes full Mermaid syntax reference with correct examples — minimises invalid diagram code",'Iterative refinement via conversation thread — "add a cache", "show as sequence diagram" modifies the existing diagram',"Optional web_search lets the agent research unfamiliar technologies before diagramming"],examples:["Microservices e-commerce platform with API gateway, user service, order service, and payment processing","CI/CD pipeline from git push to production with testing, staging, and rollback","Real-time chat system with WebSockets, load balancer, and Redis pub/sub","OAuth2 login flow as a sequence diagram","E-commerce database schema as an ER diagram","Order lifecycle as a state diagram","Add a Redis cache between the services and the database","Show me the auth flow as a sequence diagram instead"],appUrl:"http://localhost:28804",mcpUsage:[{server:"web",tools:["web_search"]}],inlineTools:[]},{id:"hiking-research",name:"Hiking Research",tagline:"Discover and compare hiking trails near any location with AI-synthesised reviews",type:"other",surface:"gateway",description:'A browser UI for exploring hiking trails. Type any location and the agent geocodes it, queries OpenStreetMap via the Overpass API for named hiking route relations, and presents trails filtered by difficulty and kid-friendliness. Click any trail name to view it on OpenStreetMap. Tap "Get Reviews" on any trail to get an AI-synthesised summary of hiker reviews from the web via Tavily.',category:"personal",status:"working",channels:[],tools:["geocode_location()","find_hikes()","get_review_summary()"],demoPath:"apps/hiking_research",howToRun:{envVars:["LLM_PROVIDER","LLM_MODEL","TAVILY_API_KEY"],setup:["cd apps/hiking_research","pip install -r requirements.txt"],command:"python main.py"},architecture:"FastAPI serves the single-page UI. POST /ask → CugaAgent calls geocode_location (Nominatim/OpenStreetMap) to convert a place name to lat/lon, then find_hikes (Overpass API) to fetch named hiking route relations, filtered by difficulty and kid-friendliness. GET /hikes returns the cached results for the live trail-card panel. get_review_summary uses Tavily to search for and synthesise hiker reviews for a specific trail.",diagram:`python main.py → http://127.0.0.1:18805 User: "Easy hikes near Yosemite, CA" │ POST /ask ▼ CugaAgent ├─ geocode_location("Yosemite, CA") │ → lat=37.7489, lon=-119.5885 │ ├─ find_hikes(lat, lon, radius_km=25, difficulty="easy") │ → Overpass API: hiking route relations │ → filter by sac_scale / distance │ → _last_hikes updated (30 results) ▼ Summary: "Found 12 easy trails near Yosemite…" GET /hikes → trail cards rendered in the right panel Each card: name (→ OSM link), difficulty, distance, kid-friendly badge User: "Tell me about reviews for: Mist Trail" │ ▼ CugaAgent → get_review_summary("Mist Trail", "Yosemite") → Tavily search → synthesised review summary`,cugaContribution:["Agent chains geocode → find_hikes automatically — user just names any place, no coordinates needed","Difficulty inferred from OSM sac_scale tag with a distance-based fallback for untagged routes","Kid-friendly flag combines difficulty, distance, and an explicit OSM child= tag","Review synthesis via Tavily search gives real hiker opinions without fabricating trail details"],examples:["Easy hikes near Yosemite, CA","Kid-friendly trails near Boulder, CO","Moderate hikes near Asheville, NC within 40 km","Hard hikes near Denver, CO","Family hikes near Lake Tahoe","Tell me about user reviews for: Half Dome Trail"],appUrl:"http://localhost:28805",mcpUsage:[{server:"geo",tools:["geocode","find_hikes"]},{server:"web",tools:["web_search"]}],inlineTools:[]},{id:"movie-recommender",name:"Movie Recommender",tagline:"Tell the agent what you love — get a personalised watch-next list",type:"other",surface:"gateway",description:"A conversational movie recommendation agent with a browser UI. Tell it about films you enjoy, genres, favourite directors and actors, or your current mood — the agent builds a taste profile and recommends 5–8 films you will love. Movie details are verified via the Wikipedia REST API (no extra API key needed). Recommendations appear as cards in the right panel alongside a live view of your taste profile.",category:"personal",status:"working",channels:[],tools:["lookup_movie()","save_preference()","get_preferences()","save_recommendations()"],demoPath:"apps/movie_recommender",howToRun:{envVars:["LLM_PROVIDER","LLM_MODEL","ANTHROPIC_API_KEY"],setup:["cd apps/movie_recommender","pip install -r requirements.txt"],command:"python main.py --port 18806"},architecture:"FastAPI serves the single-page UI. POST /ask → CugaAgent uses save_preference to record genres, liked/disliked films, actors, directors, and moods; get_preferences recalls the full profile; lookup_movie verifies details via Wikipedia; save_recommendations persists the structured card list. GET /session/{thread_id} returns the live profile and recommendation cards.",diagram:`python main.py → http://127.0.0.1:18806 User: "I love Inception and The Dark Knight" │ POST /ask ▼ CugaAgent ├─ save_preference(category="liked_movie", value="Inception") ├─ save_preference(category="liked_movie", value="The Dark Knight") │ User: "Recommend something similar" ├─ get_preferences() → liked_movies, genres, moods … ├─ lookup_movie("Memento") ← Wikipedia REST API ├─ lookup_movie("Prisoners") │ ├─ save_recommendations([{title, year, genre, reason, rating}, ...]) ▼ Recommendation cards rendered in the right panel`,cugaContribution:["save_preference / get_preferences build a persistent taste profile within the session — the agent never forgets what you said earlier","lookup_movie verifies film details via Wikipedia before suggesting — no hallucinated plot descriptions","save_recommendations pushes structured JSON to the UI so cards render automatically without UI polling logic","Warm, film-enthusiast persona defined in the skill prompt — swap the prompt to change tone or domain (books, games, etc.)"],examples:["I love Inception and The Dark Knight — what should I watch next?","I enjoy sci-fi and psychological thrillers, suggest 5 films","My favourite director is Denis Villeneuve","I'm in the mood for something light and funny tonight","I dislike jump-scare horror — what else is good?","Recommend something with Tom Hanks I might have missed"],appUrl:"http://localhost:28806",mcpUsage:[{server:"knowledge",tools:["get_wikipedia_article"]}],inlineTools:["save_preference","get_preferences","save_recommendations"]},{id:"webpage-summarizer",name:"Webpage Summarizer",tagline:"Paste any URL — get a structured plain-English summary instantly",type:"other",surface:"gateway",description:"A browser UI that fetches and summarises any webpage you provide. Paste a URL into the chat and the agent retrieves the page, strips HTML boilerplate (scripts, nav, footers), extracts readable text, and returns a structured summary: title, source URL, 2–3 sentence overview, key topics as bullet points, important facts, and a bottom-line takeaway. Also lists hyperlinks found on the page on request.",category:"personal",status:"working",channels:[],tools:["fetch_webpage()","fetch_webpage_links()"],demoPath:"apps/webpage_summarizer",howToRun:{envVars:["LLM_PROVIDER","LLM_MODEL","ANTHROPIC_API_KEY"],setup:["cd apps/webpage_summarizer","pip install -r requirements.txt"],command:"python main.py --port 8071"},architecture:"FastAPI serves the single-page UI. POST /ask → CugaAgent calls fetch_webpage (httpx + BeautifulSoup, truncated to 12 000 chars) → produces structured summary. fetch_webpage_links returns the list of external links for site exploration. No state is stored between requests.",diagram:`python main.py → http://127.0.0.1:8071 User: "Summarize https://en.wikipedia.org/wiki/Large_language_model" │ POST /ask ▼ CugaAgent └─ fetch_webpage("https://en.wikipedia.org/wiki/Large_language_model") │ httpx GET + BeautifulSoup strip │ title, meta description, body text (≤12 000 chars) ▼ Structured summary: Title: Large language model — Wikipedia Overview: A large language model (LLM) is … Key topics: • Architecture • Training • RLHF • Applications … Bottom line: LLMs are transformer-based models … User: "List all links on https://news.ycombinator.com" └─ fetch_webpage_links(url) → up to 40 external links`,cugaContribution:["fetch_webpage strips nav, header, footer, script, and style tags before sending text to the LLM — agent only sees signal, not boilerplate","Content truncated to 12 000 chars to stay within context limits; the agent handles truncation gracefully","Structured summary format (overview → bullets → bottom line) enforced by the system prompt — consistent output regardless of page type","fetch_webpage_links enables lightweight site exploration without a separate crawling tool"],examples:["Summarize https://en.wikipedia.org/wiki/Large_language_model","What is this page about? https://python.org","Key takeaways from https://openai.com/blog","List all links on https://news.ycombinator.com","https://github.com/langchain-ai/langchain — give me a one-paragraph overview"],appUrl:"http://localhost:28071",mcpUsage:[{server:"web",tools:["fetch_webpage","fetch_webpage_links"]}],inlineTools:[]},{id:"code-reviewer",name:"Code Reviewer",tagline:"Paste or upload code — get structured bug, security, and style feedback",type:"other",surface:"gateway",description:"An AI-powered code review tool with a browser UI. Paste a snippet or upload a source file (.py, .js, .ts, .java, .go, .rs, .cpp, .sql, .sh, and more) and choose a focus mode: Full Review, Security, Performance, Style, Bugs, Architecture, or Testability. The agent detects the language, validates Python syntax via AST, extracts code metrics (LOC, complexity, top-level definitions), and returns a structured review with severity-rated issues, concrete suggestions, and deeper insights. Ask follow-up questions about the loaded code without re-submitting. Session review history is collapsible and copyable.",category:"enterprise",status:"working",channels:[],tools:["check_python_syntax()","extract_code_metrics()","detect_language()"],demoPath:"apps/code_reviewer",howToRun:{envVars:["LLM_PROVIDER","LLM_MODEL","ANTHROPIC_API_KEY"],setup:["cd apps/code_reviewer","pip install -r requirements.txt"],command:"python main.py --port 18807"},architecture:"FastAPI serves the single-page UI. POST /review → CugaAgent calls detect_language, check_python_syntax (Python only, AST-based), and extract_code_metrics → structured review with severity badges. POST /ask → free-form follow-up on any loaded code. POST /upload → reads a source file and returns its text so the UI can populate the code area. GET /history → in-memory list of last 50 reviews (session-scoped).",diagram:`python main.py → http://127.0.0.1:18807 User: pastes Python function, selects "Security" focus, clicks Review │ POST /review {code, language:"python", focus:"security"} ▼ CugaAgent ├─ detect_language(code) → {"language":"python","confidence":"high"} ├─ check_python_syntax(code) → {"valid":true,"error":null} ├─ extract_code_metrics(code) → {total_lines:42, branch_complexity:7, ...} ▼ Structured review: ### Summary — Good overall, one injection risk ### Issues Found [HIGH] Unsanitised user input passed to subprocess.run() — line 14 ### Suggestions 1. Use shlex.quote() or subprocess list-form … ### Metrics — Lines: 42 (non-blank: 36), Complexity: 7 User: "How would you refactor this using the strategy pattern?" │ POST /ask ▼ CugaAgent (code injected as context) → refactoring walkthrough`,cugaContribution:["check_python_syntax runs AST.parse before the LLM sees the code — syntax errors reported instantly, no token waste","extract_code_metrics gives the agent concrete numbers (LOC, branch count, top-level defs) to ground its review in facts","Focus mode chips translate to a focus_hint injected into the prompt — same agent, different lens, no code duplication","Session review history (last 50) is maintained in-memory and displayed as collapsible cards with copy-to-clipboard"],examples:["Paste a Python function and select Bugs focus","Upload a JavaScript file and select Security focus","Paste a SQL query and ask: How could I optimise this for 10M rows?","Load a Go file and click Architecture","How would you refactor this using the strategy pattern?","Is there any XSS risk in the current code?"],appUrl:"http://localhost:28807",mcpUsage:[{server:"code",tools:["check_python_syntax","extract_code_metrics","detect_language"]}],inlineTools:[]},{id:"paper-scout",name:"Paper Scout",tagline:"Research academic papers via arXiv and Semantic Scholar — no API key needed",description:'A browser UI for academic research. Type a topic and the agent searches both arXiv (CS, ML, physics, math, biology) and Semantic Scholar (broader coverage with citation counts), then synthesises findings across papers with inline citations. Paste an arXiv ID or URL directly for an instant structured summary: contributions, method, results, limitations. Ask follow-up questions like "what does this build on?" to fetch reference lists.',category:"personal",type:"other",surface:"gateway",status:"working",channels:[],tools:["search_arxiv","get_arxiv_paper","search_semantic_scholar","get_paper_references"],demoPath:"apps/paper_scout",howToRun:{envVars:["LLM_PROVIDER","LLM_MODEL","AGENT_SETTING_CONFIG"],setup:["cd apps/paper_scout","pip install -r requirements.txt"],command:"python main.py --port 18808"},architecture:"FastAPI serves the single-page UI. POST /ask → CugaAgent calls search_arxiv and search_semantic_scholar in parallel, deduplicates results, then synthesises a structured report grouped by theme with inline citations (title, URL, citation count, year). For direct arXiv IDs, get_arxiv_paper is called immediately. get_paper_references fetches the Semantic Scholar reference graph for any paper. No API keys required — arXiv and Semantic Scholar both offer free public APIs.",diagram:`python main.py → http://127.0.0.1:18808 Mode 1 — Topic research: User: "LoRA and parameter-efficient fine-tuning" │ POST /ask ▼ CugaAgent ├─ search_arxiv("LoRA fine-tuning", category="cs.LG") │ → [2106.09685, 2305.14314, 2402.09353, …] ├─ search_semantic_scholar("parameter-efficient fine-tuning") │ → [papers with citation counts] ▼ Synthesised report: **Topic**: LoRA and Parameter-Efficient Fine-Tuning **Papers found**: 8 (5 arXiv, 3 Semantic Scholar) **Synthesis**: LoRA (Hu et al., 2021) introduces low-rank decomposition… **Key papers to read first**: … Mode 2 — Direct arXiv ID: User: "arxiv 2305.11206" │ POST /ask ▼ CugaAgent ├─ get_arxiv_paper("2305.11206") ▼ **Paper**: [Title](url) **Summary** / **Method** / **Key results** / **Limitations**`,cugaContribution:["Searches arXiv and Semantic Scholar independently then deduplicates — same paper cited once, never twice","Category filter on arXiv (cs.AI, cs.LG, stat.ML, etc.) lets users narrow to a field without knowing exact terminology","Citation counts from Semantic Scholar ground the synthesis in impact, not just recency","get_paper_references follows the citation graph to surface the foundational papers a new work builds on"],examples:["LoRA and parameter-efficient fine-tuning methods","Mixture of Experts in large language models","Retrieval-Augmented Generation for knowledge-intensive NLP","https://arxiv.org/abs/1706.03762","2310.01445","What papers does Attention Is All You Need build on?"],appUrl:"http://localhost:28808",mcpUsage:[{server:"knowledge",tools:["search_arxiv","get_arxiv_paper","search_semantic_scholar","get_paper_references"]}],inlineTools:[]},{id:"wiki-dive",name:"Wiki Dive",tagline:"Deep Wikipedia research — reads articles section by section, follows related links, synthesises with citations",description:`A browser UI for encyclopedic deep dives. Unlike a Wikipedia search that returns a snippet, Wiki Dive reads the full article section by section, follows "See Also" links to pull connected concepts, and synthesises a structured report with inline citations. Great for building mental models from first principles — complex topics, historical events, scientific concepts, philosophical ideas. No API keys required; uses Wikipedia's free public REST and action APIs.`,category:"personal",type:"other",surface:"gateway",status:"working",channels:[],tools:["search_wikipedia","get_article_summary","get_article_sections","get_related_articles"],demoPath:"apps/wiki_dive",howToRun:{envVars:["LLM_PROVIDER","LLM_MODEL","AGENT_SETTING_CONFIG"],setup:["cd apps/wiki_dive","pip install -r requirements.txt"],command:"python main.py --port 18809"},architecture:"FastAPI serves the single-page UI. POST /ask → CugaAgent calls search_wikipedia to identify relevant articles, get_article_summary for a quick relevance check, get_article_sections for deep section-by-section reading of the primary article, get_related_articles to discover connected concepts, then get_article_summary on 2-3 related articles for breadth. The agent synthesises across all content into a structured report. No API keys required — uses Wikipedia's free public REST API and MediaWiki action API.",diagram:`python main.py → http://127.0.0.1:18809 User: "How does transformer attention work?" │ POST /ask ▼ CugaAgent ├─ search_wikipedia("transformer attention mechanism") │ → ["Transformer (deep learning)", "Attention (machine learning)", …] ├─ get_article_summary("Transformer (deep learning)") │ → lead paragraph confirming relevance ├─ get_article_sections("Transformer (deep learning)") │ → Introduction / Architecture / Attention / Training / Applications / … ├─ get_related_articles("Transformer (deep learning)") │ → ["BERT", "GPT", "Self-attention", "Seq2seq", …] ├─ get_article_summary("Attention (machine learning)") │ → historical context: Bahdanau 2014, Vaswani 2017 ▼ Synthesised report: **Overview**: Transformers use self-attention to… **Key concepts**: Query/Key/Value matrices, Multi-head attention, Positional encoding **History**: Bahdanau (2014) introduced attention for NMT… **Related topics**: BERT, GPT, Vision Transformer`,cugaContribution:["get_article_sections reads every section of the article — not just the lead — giving the agent encyclopedic depth instead of snippet-level knowledge",'get_related_articles surfaces the Wikipedia editor-curated "See Also" graph, pulling in adjacent concepts the user may not have known to ask for',"Multi-article synthesis: agent reads 3-5 articles and synthesises across them, resolving overlaps and connecting ideas","Output is structured (Overview → Key concepts → History → Applications → Related topics) rather than raw article text"],examples:["How does transformer attention work?","The French Revolution — causes, events, and legacy","Quantum entanglement explained from first principles","CRISPR gene editing and its applications","Game theory and Nash equilibrium","The philosophy of consciousness and the hard problem"],appUrl:"http://localhost:28809",mcpUsage:[{server:"knowledge",tools:["search_wikipedia","get_article_summary","get_article_sections","get_related_articles"]}],inlineTools:[]},{id:"box-qa",name:"Box Document Q&A",tagline:"Ask questions across documents stored in your Box cloud storage",type:"document-intelligence",surface:"gateway",description:"A browser UI that connects to a Box folder and lets you ask natural-language questions across your documents. The agent lists files, fetches and extracts text from supported document types (PDF, DOCX, PPTX, XLSX, TXT, MD, CSV), and answers questions with citations to specific files and passages. Video/audio files are surfaced by name but noted as unsupported — a multimodal extension (Whisper transcription + keyframe vision) is planned for v2.",category:"enterprise",status:"not-working",channels:[],tools:["list_box_folder()","get_file_content()","search_box()"],demoPath:"apps/box_qa",howToRun:{envVars:["LLM_PROVIDER","LLM_MODEL","AGENT_SETTING_CONFIG","BOX_CONFIG_PATH","BOX_FOLDER_ID"],setup:["cd apps/box_qa","pip install -r requirements.txt"],command:"python main.py"},architecture:"FastAPI serves the single-page UI. POST /ask → CugaAgent uses list_box_folder (Box SDK JWT auth) to enumerate files, search_box to find relevant candidates, get_file_content to download and extract text (plain read for TXT/CSV/MD; docling OCR for PDF/DOCX/PPTX/XLSX) → answers with file citations. Two-panel UI: left is conversational chat (thread-aware, multi-turn), right shows the latest agent response in full.",diagram:`python main.py → http://127.0.0.1:18810 User: "What does the Q4 report say about revenue?" │ POST /ask ▼ CugaAgent ├─ list_box_folder("0") → sees Q4_Report.pdf, budget.xlsx, intro.mp4 ├─ get_file_content(id=Q4_Report.pdf) │ → docling extracts text │ → intro.mp4 skipped ("video/audio not supported") ▼ Answer with citation: "[Q4_Report.pdf] — 'Revenue grew 18% YoY, driven by…'"`,cugaContribution:["Agent decides which files are relevant before fetching — avoids downloading the entire folder",'Cross-document synthesis: "Both the Q4 report and the board brief mention X"',"Multi-turn thread memory: follow-up questions work without re-fetching already-read files","Graceful handling of unsupported types: media files are surfaced but not silently skipped"],examples:["What files are in my Box folder?","Summarize the most recent PDF","Find any documents about contracts and list key terms","What does the project brief say about timelines?","Compare the two most recent reports","List all files — which ones can you read?"],appUrl:"http://localhost:28810",mcpUsage:[{server:"text",tools:["extract_text_from_bytes"]}],inlineTools:["list_box_folder","get_file_content","search_box"]},{id:"ibm-whats-new",name:"IBM What's New Monitor",tagline:"Track IBM Cloud release notes across services — scheduled digest, email alerts, ad-hoc chat",description:"A browser UI that monitors IBM Cloud service release notes and What's New announcements. Add services to your watch list (Code Engine, watsonx.ai, Event Streams, etc.), set a daily or weekly schedule, and the agent searches ibm.com and cloud.ibm.com via Tavily for recent changes. Meaningful updates appear in the Digest Log and are emailed automatically. Chat panel for ad-hoc questions like 'what changed in Cloud Object Storage this month?'",category:"enterprise",type:"event-driven",surface:"pipeline",status:"working",channels:["EmailChannel"],tools:["search_ibm_updates()","fetch_release_notes()"],demoPath:"apps/ibm_whats_new",howToRun:{envVars:["LLM_PROVIDER","LLM_MODEL","AGENT_SETTING_CONFIG","TAVILY_API_KEY","SMTP_HOST","SMTP_USERNAME","SMTP_PASSWORD","DIGEST_TO"],setup:["cd apps/ibm_whats_new","pip install -r requirements.txt"],command:"python main.py --port 18814"},architecture:"FastAPI serves the single-page UI. Chat: POST /ask → CugaAgent calls search_ibm_updates (Tavily scoped to ibm.com/cloud.ibm.com) + fetch_release_notes (httpx + BeautifulSoup, IBM URLs only) → answer with sources. Background scheduler: asyncio task checks every 5 minutes whether a digest is due; when due, agent checks each tracked service in turn, collects UPDATE: responses, and sends an SMTP digest. State persisted in .store.json.",diagram:`python main.py → http://127.0.0.1:18814 Chat panel (on-demand): User: "What changed in Code Engine this month?" │ POST /ask ▼ CugaAgent ├─ search_ibm_updates("IBM Code Engine release notes 2026") │ → Tavily → cloud.ibm.com/docs/code-engine release notes ├─ fetch_release_notes("https://cloud.ibm.com/docs/code-engine?topic=...") │ → httpx + BeautifulSoup → clean release notes text ▼ "[Apr 2026] Custom domain mapping added for private visibility apps..." Background scheduler (daily): asyncio task (every 5 min — checks if digest is due) │ services: ["Code Engine", "watsonx.ai", "Event Streams"] ▼ agent.invoke("Check what is new for IBM Cloud service: Code Engine") │ → "UPDATE: [Apr 2026] ..." agent.invoke("Check what is new for IBM Cloud service: watsonx.ai") │ → "UPDATE: [Mar 2026] ..." ▼ SMTP digest → DIGEST_TO`,cugaContribution:["search_ibm_updates uses Tavily scoped to ibm.com + cloud.ibm.com — only real IBM sources, no hallucinated features","fetch_release_notes strips nav/header/footer before the LLM sees text — agent reads signal, not HTML noise",'UPDATE: protocol — agent decides if changes are meaningful; only real updates trigger email, not empty "no changes" noise',"Background asyncio scheduler fires digests without a cron daemon — daily or weekly, configurable from the UI","Per-service agent invocations keep context clean — one service per call, no cross-contamination of release notes"],examples:["What is new in IBM Code Engine in 2026?","Latest changes to IBM Cloud Object Storage","What changed in watsonx.ai recently?","Any IBM Cloud breaking changes in the last 30 days?","Summarize IBM Event Streams release notes from this month"],appUrl:"http://localhost:28814",mcpUsage:[{server:"web",tools:["web_search","fetch_webpage"]}],inlineTools:[]},{id:"ibm-cloud-advisor",name:"IBM Cloud Architecture Advisor",tagline:"Describe what you want to build — get real IBM Cloud services, CLI commands, and cost hints",description:"A browser UI powered by the IBM Global Catalog public API (no IBM account required). Describe your use case in plain English and the agent searches the live IBM service catalog, recommends 3–7 IBM Cloud services with roles and integration points, and generates ibmcloud CLI commands to provision them. Supports iterative refinement: ask for HA, HIPAA compliance, Terraform output, or AWS-to-IBM mappings.",category:"enterprise",type:"other",surface:"gateway",status:"working",channels:[],tools:["search_ibm_catalog()","search_ibm_docs()"],demoPath:"apps/ibm_cloud_advisor",howToRun:{envVars:["LLM_PROVIDER","LLM_MODEL","AGENT_SETTING_CONFIG","TAVILY_API_KEY"],setup:["cd apps/ibm_cloud_advisor","pip install -r requirements.txt"],command:"python main.py --port 18812"},architecture:"FastAPI serves the single-page UI. POST /ask → CugaAgent calls search_ibm_catalog (IBM Global Catalog public REST API — no key needed) with 2–3 keyword queries to find real services, optionally calls search_ibm_docs (Tavily restricted to ibm.com) for pricing and architecture patterns, then produces a structured recommendation with CLI commands. No state stored between sessions.",diagram:`python main.py → http://127.0.0.1:18812 User: "Event-driven microservices with a message queue and a managed database" │ POST /ask ▼ CugaAgent ├─ search_ibm_catalog("message queue event streaming") │ → IBM Event Streams, IBM MQ ├─ search_ibm_catalog("managed postgresql database") │ → Databases for PostgreSQL ├─ search_ibm_catalog("serverless container compute") │ → IBM Code Engine ├─ (optional) search_ibm_docs("IBM Event Streams pricing tiers") ▼ Architecture: Event-Driven Microservices on IBM Cloud IBM Cloud Services: - IBM Event Streams (event-streams): Kafka-compatible message bus - Databases for PostgreSQL (databases-for-postgresql): Persistent store - IBM Code Engine (codeengine): Serverless consumer microservices ibmcloud CLI: ibmcloud resource service-instance-create my-kafka event-streams standard us-south`,cugaContribution:["search_ibm_catalog hits the live IBM Global Catalog API — only real, current services are ever recommended","Agent runs 2–3 focused queries per use case (one per capability) to maximise catalog hit rate","search_ibm_docs uses Tavily restricted to ibm.com — pricing tiers, feature comparisons from official sources",'Iterative refinement via conversation thread — "make it HA" or "show Terraform" modifies the previous recommendation in context'],examples:["IoT sensor pipeline with real-time processing and dashboards","Serverless web app with auth and a managed database","Event-driven microservices with a message queue","ML model training and serving platform on IBM Cloud","AWS equivalent: S3 + Lambda + DynamoDB on IBM Cloud","HIPAA-compliant data processing pipeline","Show Terraform for a Kubernetes workload on IBM Cloud"],appUrl:"http://localhost:28812",mcpUsage:[{server:"web",tools:["web_search"]}],inlineTools:["search_ibm_catalog"]},{id:"ibm-docs-qa",name:"IBM Docs Q&A",tagline:"Ask any IBM Cloud question — get a precise answer from real IBM documentation with source links",description:"A browser UI that answers IBM Cloud questions by searching and reading real IBM documentation. Ask anything: setup procedures, plan limits, service comparisons, config options, pricing. The agent searches ibm.com and cloud.ibm.com via Tavily, fetches the most relevant doc pages in full, and synthesises a precise answer with inline citations. Multi-turn: ask follow-up questions without re-submitting context.",category:"enterprise",type:"other",surface:"gateway",status:"working",channels:[],tools:["search_ibm_docs()","fetch_doc_page()"],demoPath:"apps/ibm_docs_qa",howToRun:{envVars:["LLM_PROVIDER","LLM_MODEL","AGENT_SETTING_CONFIG","TAVILY_API_KEY"],setup:["cd apps/ibm_docs_qa","pip install -r requirements.txt"],command:"python main.py --port 18813"},architecture:"FastAPI serves the single-page UI. POST /ask → CugaAgent calls search_ibm_docs (Tavily restricted to ibm.com/cloud.ibm.com, search_depth=advanced) to find relevant pages, then optionally calls fetch_doc_page (httpx + BeautifulSoup, strips nav/header/footer, extracts main content up to 6000 chars) on the most relevant URL. Agent synthesises across sources and cites every claim with a page title and URL.",diagram:`python main.py → http://127.0.0.1:18813 User: "How do I set up a private endpoint for Cloud Object Storage?" │ POST /ask ▼ CugaAgent ├─ search_ibm_docs("IBM Cloud Object Storage private endpoint setup") │ → Tavily → 6 results from cloud.ibm.com/docs │ ├─ fetch_doc_page("https://cloud.ibm.com/docs/cloud-object-storage?topic=…") │ → httpx GET → BeautifulSoup strip → 4800 chars of clean doc text ▼ Answer: 1. Create a service credential with HMAC enabled 2. Use private endpoint: s3.private..cloud-object-storage.appdomain.cloud 3. Ensure your compute is in the same region VPC Sources: - [Cloud Object Storage Endpoints](https://cloud.ibm.com/docs/…) User: "What's the difference between private and direct endpoints?" ▼ CugaAgent (previous context retained) → follow-up with comparison table`,cugaContribution:["search_ibm_docs uses Tavily advanced mode with ibm.com domain restriction — results always from official IBM sources","fetch_doc_page strips nav, header, footer, and scripts before the LLM sees text — agent reads clean content, not HTML noise","URL safety check refuses non-IBM URLs — agent cannot be redirected off-domain","Multi-turn conversation thread — follow-up questions work without re-submitting context"],examples:["How do I set up a private endpoint for Cloud Object Storage?","What are the Lite plan limits for Watson Discovery?","How does IBM Cloud IAM service ID authentication work?","Code Engine: Dockerfile vs Buildpacks — which should I use?","How do I connect IBM Databases for PostgreSQL to Code Engine?","What is IBM watsonx.ai and how do I get started?"],appUrl:"http://localhost:28813",mcpUsage:[{server:"web",tools:["web_search","fetch_webpage"]}],inlineTools:[]},{id:"api-doc-gen",name:"API Doc Generator",tagline:"Upload an OpenAPI spec and get human-readable docs with realistic examples in seconds",type:"document-intelligence",surface:"gateway",description:"A browser UI that turns OpenAPI/Swagger specs into developer-friendly documentation. Upload a JSON or YAML spec, point to a URL, or pick from five built-in samples (Petstore, GitHub Issues, Stripe Payments, Slack Messaging, OpenWeather). The agent reads the spec endpoint by endpoint, resolves $ref schemas, and writes clear Markdown docs with realistic curl examples and example responses. Supports iterative refinement — ask for more examples, filter to specific endpoints, change tone, or generate a Postman collection structure.",category:"enterprise",status:"working",channels:[],tools:["list_endpoints()","get_endpoint_details()","get_schema()"],demoPath:"apps/api_doc_gen",howToRun:{envVars:["LLM_PROVIDER","LLM_MODEL","AGENT_SETTING_CONFIG"],setup:["cd apps/api_doc_gen","pip install -r requirements.txt"],command:"python main.py --port 18811"},architecture:"FastAPI serves the single-page UI. POST /ask → CugaAgent calls list_endpoints to survey the loaded spec, get_endpoint_details for each endpoint being documented, and get_schema to resolve $ref models — then writes structured Markdown with tables, curl examples, and example responses. POST /load-spec parses JSON/YAML and stores it server-side. POST /upload-spec handles file uploads. Five sample OpenAPI specs are embedded in the UI for one-click loading. No database — spec is in-memory, conversation is thread-aware.",diagram:`python main.py → http://127.0.0.1:18811 User loads Stripe Payments spec (sample), then: User: "Document all endpoints" │ POST /ask ▼ CugaAgent ├─ list_endpoints() │ → 9 endpoints across Customers, Charges, Refunds, Subscriptions ├─ get_endpoint_details("/charges", "POST") │ → amount, currency, customer, source, description fields ├─ get_schema("Charge") │ → id, amount, currency, status, paid, receipt_url ▼ Generated docs: ### POST /charges — Create a charge **Authentication:** Basic auth — Stripe secret key as username **Request body:** | Field | Type | Required | Description | |----------|---------|----------|--------------------------| | amount | integer | Yes | Amount in cents (e.g. 4999) | | currency | string | Yes | ISO 4217 code (e.g. "usd") | \`\`\`bash curl -X POST https://api.stripe.com/v1/charges \\ -u sk_live_…: \\ -d amount=4999 \\ -d currency=usd \\ -d customer=cus_Qk5fG2hJ8mPx3N \`\`\``,cugaContribution:["Agent decides how many tool calls to make — one endpoint at a time for large specs, or in bulk for small ones","get_schema resolves $ref chains the LLM would otherwise hallucinate around","Realistic example values are inferred by the LLM from field names — amount → 4999, email → alice@acme.com",'Multi-turn thread: "add more examples" or "document only POST endpoints" refines the previous output in context'],examples:["Document all endpoints","Show me the authentication details and how to get started","Generate a quick-start guide for a new developer","Document only the POST endpoints with example request bodies","List all endpoints with a one-line description of each","Generate a Postman collection structure for these endpoints"],appUrl:"http://localhost:28811",mcpUsage:[],inlineTools:["list_endpoints","get_endpoint_details","get_schema"]},{id:"bird-invocable-api",name:"Bird Invocable API Creator",tagline:"Turn a Bird-SQL database into a validated invocable API + ground-truth tool-call dataset",type:"document-intelligence",surface:"gateway",description:"A browser UI that turns a Bird-SQL database (sqlite + NL questions + gold SQL) into three artifacts: a small set of reusable, parametric Python tools; per-question ground-truth tool-call sequences; and a runnable MCP server you can plug into any tool-calling agent for benchmarking. Pick a database, watch live progress as the agent walks every question — orient on the schema, reason about decomposition, register or reuse tools, smoke-test them on real sqlite, record a sequence, and validate it end-to-end against the gold result. The Question Inspector shows the NL, evidence, gold SQL, recorded sequence, and pass/fail with diff. The Tool Browser shows every registered tool with reuse counts, slot pills, and the qids that reference it.",category:"enterprise",status:"working",channels:[],tools:["db_get_schema()","db_sample_rows()","db_run_sql()","bird_list_databases()","bird_list_questions()","bird_get_question()","bird_run_gold()","tool_register()","tool_list()","tool_get()","tool_call()","tool_delete()","seq_record()","seq_execute()","seq_validate()","ignore_set()","ignore_list()","dataset_emit()"],demoPath:"apps/bird_invocable_api_creator",howToRun:{envVars:["LLM_PROVIDER","LLM_MODEL","BIRD_DEV_JSON","BIRD_DBS_DIR"],setup:["cd apps/bird_invocable_api_creator","pip install -r requirements.txt"],command:"python main.py --port 28815"},architecture:"FastAPI serves the single-page UI. CugaAgent is bound to the 15 invocable_apis MCP tools and a long synthesis-policy system prompt. POST /question/{db}/{qid} runs the loop on a single question; POST /synthesize/{db} kicks off a background job that walks every question (live progress at GET /jobs/{id}). The agent calls db_* to orient, bird_* for the question + oracle, tool_* to register/reuse/call generated tools, and seq_* to record + validate the chain against the gold SQL result on the actual sqlite. dataset_emit freezes the registry to disk under output//. Server-side guards block forbidden names (question_*, *_wrapper, *_solver, gold_*) and near-duplicate SQL skeletons / name-token overlap, so the LLM cannot bloat the registry. Generated tool code runs in an exec() sandbox with sqlite3/json/re only and a read-only sqlite connection.",diagram:`python main.py --port 28815 → http://127.0.0.1:28815 User picks db='california_schools', clicks "▶ Run agent on this question" for qid=28 │ POST /question/california_schools/28 ▼ CugaAgent + 15 MCP tools (mcp-invocable_apis @ 29107) ├─ bird_get_question(qid=28) → NL + evidence + gold SQL ├─ bird_run_gold(qid=28) → 57-row canonical answer ├─ db_get_schema, db_sample_rows ├─ db_run_sql("SELECT DISTINCT FundingType FROM schools") │ → ['Directly funded', 'Locally funded', 'Not in CS funding model'] ├─ tool_list → see what's already registered ├─ Reasoning: "subquery in gold SQL → 2-step decomposition" ├─ tool_register avg_enrollment_difference_by_funding_type ├─ tool_call(funding_type='Locally funded') → {avg_difference: 16.7} ├─ tool_register schools_with_enrollment_difference_above ├─ tool_call(threshold=16.7, …) → 57 schools ├─ seq_record([step1 bind="avg", │ step2 threshold="{{avg.avg_difference}}"]) └─ seq_validate → passed=true (set-equal vs gold) dataset_emit → output/california_schools/california_schools_{tools.py, tools.json, dataset.json, mcp_server.py, tool_usage.json, validation_report.json}`,cugaContribution:["Closed-loop synthesis — orient (db_get_schema + DISTINCT), design (decomposition reasoning), register, smoke-test (tool_call on real sqlite), record sequence, validate against gold; up to 2 retries on failure","Server-side quality guards — forbidden-name patterns, SQL-skeleton equality, ≥85% name-token overlap → reject; the agent cannot bloat the registry with question_N wrappers or near-duplicates",'Cross-question accumulation — every tool registered for question N is visible when synthesizing N+1; reuse is mandatory whenever a tool with different args can answer the new question. The "tools reused across qs" tile is the meta-moat alarm',"Slot-value discovery without leaking the answer — examples come from SELECT DISTINCT but exclude the literal value used in this question's gold SQL, so the dataset still tests an agent's NL→args mapping","Validation comparator handles dict-of-list-of-dicts vs SQL tuples, set-equality, casing — every emitted record carries proof its sequence reproduces the gold SQL result on the real sqlite","Emitted artifacts include a runnable MCP server (output//_mcp_server.py) so the synthesized API can be benchmarked by any tool-calling agent — not just CUGA"],examples:["Run agent on this question (single-question demo from the inspector)","⚡ Batch synthesize — walk every question in the selected database","Filter the question list to ✗ fail and re-run the inspector to debug failures",'Open the Tool Browser, sort by "most reused" — the small reusable core of the API',"⊘ Mark ignore on questions with broken gold SQL so they're excluded from the keep set","📥 Re-emit to refresh output// artifacts after manual edits"],appUrl:"http://localhost:28815",mcpUsage:[{server:"invocable_apis",tools:["db_get_schema","db_sample_rows","db_run_sql","bird_list_databases","bird_list_questions","bird_get_question","bird_run_gold","tool_register","tool_list","tool_get","tool_call","tool_delete","seq_record","seq_execute","seq_validate","ignore_set","ignore_list","dataset_emit"]}],inlineTools:[]},{id:"brief-budget",name:"Brief Budget",tagline:"Research brief on a hard tool-call budget — planner-driven, light prompt",type:"other",surface:"gateway",description:"A research-brief generator with a hard tool-call budget. The system prompt is goal-shaped: no prescribed sub-topics, no prescribed tool order. The agent must decompose the question, allocate the budget across sub-topics, execute, replan if a sub-topic dries up, and synthesize a structured brief with citations. Designed specifically to exercise CUGA's planner — every other app in the lineup uses a procedural prompt that absorbs the planner's job. Plan + tool calls stream live to the UI over SSE.",category:"personal",status:"working",channels:[],tools:["propose_plan()"],demoPath:"apps/brief_budget",howToRun:{envVars:["LLM_PROVIDER","LLM_MODEL","AGENT_SETTING_CONFIG","TAVILY_API_KEY"],setup:["cd apps/brief_budget"],command:"python main.py --port 28816"},architecture:"FastAPI serves the single-page UI. POST /api/run starts a session with a question + budget; the app spins up a per-session CugaAgent whose tools are budget-wrapped (each non-plan call decrements session.used and streams a tool_call event). A free propose_plan tool records the agent's decomposition. The system prompt is deliberately goal-shaped: it prescribes the meta-process (plan → execute → replan → synthesize) but NOT sub-topics or tool order. SSE event stream feeds the UI: init / plan / tool_call / tool_result / budget_exhausted / brief / done. When budget hits 0, every further tool call returns {ok:false, code:budget_exhausted} and the agent must synthesize from what it has.",diagram:`python main.py --port 28816 → http://127.0.0.1:28816 User: question + budget=15 → POST /api/run │ ▼ BriefSession { id, budget=15, used=0, plan_history=[], queue } │ asyncio.create_task(_execute(session)) ▼ CugaAgent + tools ├─ propose_plan(plan) FREE ──► SSE: plan ├─ search_arxiv cost: 1 ──► SSE: tool_call (used=1) ├─ search_semantic_scholar cost: 1 ──► SSE: tool_call (used=2) ├─ propose_plan(replan) FREE ──► SSE: plan v2 ├─ web_search cost: 1 ──► SSE: tool_call (used=3) ├─ ... (used=14, used=15) └─ tool_call → {ok:false, code:"budget_exhausted"} │ ▼ agent synthesizes brief from observations ──► SSE: brief │ ▼ SSE: done { used=15, budget=15, plan_count=2 }`,cugaContribution:["Goal-shaped system prompt — under 50 lines, prescribes meta-process only (plan → execute → replan → synthesize). No sub-topics, no tool order. The planner decomposes per question.","First-class propose_plan tool — free; records the agent's decomposition + budget split + tool intentions; UI renders each plan version live so viewers see the planner work.","Hard budget enforcement at the tool boundary — every non-plan tool call decrements; budget_exhausted returns force the agent to stop calling and synthesize.","A/B-able demo — comparison between this (planner-driven, no procedural workflow) and procedural-prompt apps like paper_scout shows the difference in budget allocation across sub-topics."],examples:["What's the state of MoE architectures in LLMs?","Compare RAG benchmarks 2025–2026 (BEIR, BERGEN, etc.)","Open problems in agent observability","Recent advances in LoRA fine-tuning of code models","How are AI agents being applied to bug triage?"],appUrl:"http://localhost:28816",mcpUsage:[{server:"web",tools:["web_search","fetch_webpage","fetch_webpage_links","fetch_feed","search_feeds","get_youtube_video_info","get_youtube_transcript"]},{server:"knowledge",tools:["search_arxiv","get_arxiv_paper","search_semantic_scholar","get_paper_references","search_wikipedia","get_wikipedia_article","get_article_summary","get_article_sections","get_related_articles"]}],inlineTools:["propose_plan"]},{id:"trip-designer",name:"Trip Designer",tagline:"Travel itinerary planner with a light, goal-shaped prompt — CUGA decides the workflow",type:"other",surface:"gateway",description:"Same domain as travel_planner, but the system prompt is deliberately light (~25 lines, no prescribed workflow or tool order). The agent decides its own decomposition (days × themes? geographic zones? practicalities first?), the order of investigation, and the tool mix per sub-task. Plan + tool calls stream live so viewers can compare what this agent chose to do versus the prescribed-workflow travel_planner. Built to test whether CUGA can plan an itinerary when the prompt stops scripting it.",category:"personal",status:"working",channels:[],tools:["propose_plan()"],demoPath:"apps/trip_designer",howToRun:{envVars:["LLM_PROVIDER","LLM_MODEL","AGENT_SETTING_CONFIG","TAVILY_API_KEY","OPENTRIPMAP_API_KEY"],setup:["cd apps/trip_designer"],command:"python main.py --port 28817"},architecture:`FastAPI serves the single-page UI. POST /api/run starts a per-session CugaAgent with tools loaded from mcp-web + mcp-knowledge + mcp-geo. Each MCP tool is wrapped with a thin shim that streams tool_call + tool_result events to the session queue (no budget enforcement — this app is about visibility). A free propose_plan tool records the agent's plan; the UI renders each plan version. The system prompt is ~25 lines and prescribes nothing about workflow — only "call propose_plan first, cite real sources." Replanning is first-class: the agent can call propose_plan again at any time.`,diagram:`python main.py --port 28817 → http://127.0.0.1:28817 User: destination=Berlin, days=5, month=March, interests=[history, street food], constraints="must end at airport by 3pm Friday" │ ▼ POST /api/run TripSession { id, request, plan_history=[], tool_calls=[], queue } │ ▼ asyncio.create_task(_execute(session)) CugaAgent (system prompt: ~25 lines, no workflow prescribed) ├─ propose_plan(plan) ──► SSE: plan v1 ├─ ├─ tool_call A ──► SSE: tool_call + tool_result ├─ tool_call B ──► SSE: tool_call + tool_result ├─ propose_plan(replan) ──► SSE: plan v2 (if direction changes) ├─ tool_call C ... └─ synthesise itinerary ──► SSE: itinerary │ ▼ SSE: done { tool_call_count, plan_count }`,cugaContribution:["Light goal-shaped prompt — exposes whether CUGA can plan an itinerary without a procedural script. The contrast with travel_planner (which has a 6-step prescribed workflow) is the demo.","First-class propose_plan + replanning — agent can revise its decomposition mid-flight; the UI shows version history.","Cross-server orchestration — agent picks freely across mcp-web + mcp-knowledge + mcp-geo with no prompt-level guidance about which server is for what.","Live plan + tool-call visibility via SSE — viewers can see exactly what the agent chose to do, in what order, and why (replan rationales captured per version)."],examples:["Berlin · 5 days · March · history + street food","Kyoto · 4 days · November · temples + gardens + food","Lisbon · 3 days · May · azulejos + fado + viewpoints","Reykjavik · 6 days · February · northern lights + hot springs","Paris · 4 days · June · museums + boulangeries · constraint: vegetarian only"],appUrl:"http://localhost:28817",mcpUsage:[{server:"web",tools:["web_search","fetch_webpage","fetch_webpage_links"]},{server:"knowledge",tools:["search_wikipedia","get_wikipedia_article","get_article_summary","get_article_sections","get_related_articles"]},{server:"geo",tools:["geocode","find_hikes","search_attractions","get_weather"]}],inlineTools:["propose_plan"]},{id:"code-engine-deployer",name:"Code Engine Deployer",tagline:"Triage a docker-compose stack and deploy it to IBM Cloud Code Engine, with the agent running every CLI call under your eye",type:"other",surface:"gateway",description:"Hand the agent a docker-compose.yml path. It parses every service and classifies it for Code Engine readiness — CE-ready (single port, no bind mounts, sane memory), needs-work (env-secret bind mount → translate to CE Secret; large RO data → bake into image or COS), or won't-fit (multi-port containers, writable bind mounts to host paths). Then you tell it which subset to deploy. It walks the one-time setup (ICR namespace, registry secret so CE can pull, env secret from your .env), then per service: docker_build (always linux/amd64) → docker_push → ibmcloud ce app create — each command shown to you BEFORE it runs, results streamed back. On failure it pulls `ibmcloud ce app events` + logs and proposes one specific fix instead of retrying blindly. All shell calls are validated against allowlist regexes; no shell=True, no string interpolation.",category:"enterprise",status:"working",channels:[],tools:["classify_compose_services()","docker_build()","deploy_ce_app()"],demoPath:"apps/code_engine_deployer",howToRun:{envVars:["LLM_PROVIDER","LLM_MODEL","AGENT_SETTING_CONFIG"],setup:["cd apps/code_engine_deployer","# Host requirements: ibmcloud CLI + code-engine + container-registry plugins; docker CLI + daemon","# Auth: ibmcloud login --sso && ibmcloud target -r -g "],command:"python main.py --port 28818"},architecture:"FastAPI serves the single-page UI. /classify is a non-LLM endpoint that returns a parse + verdict for the supplied compose path so the table renders without burning an LLM call. /ask is the conversational path through CugaAgent. All tools are inline — this is a new ops domain (Code Engine + ICR) that doesn't fit any existing shared MCP server, and the spec's ≥2-consumers bar for a new MCP server isn't met. ce_ops.py wraps every CLI call in a strict validator: lowercase DNS-label regex for names, tag regex for image refs, region regex for ICR. subprocess.run with list args, never shell=True. Every tool returns a structured envelope including the executed command, returncode, stdout, stderr — so the agent can read the actual error from CE and propose targeted fixes.",diagram:`python main.py --port 28818 → http://127.0.0.1:28818 User: "Classify /path/to/docker-compose.yml" │ ▼ POST /classify (non-LLM, pure parse) compose_parser.parse_compose(path) → services[] compose_parser.classify_all(...) → verdicts[] │ ▼ rendered as table on the left panel User: "Deploy the CE-ready services to project foo" │ ▼ POST /ask (CugaAgent thread) agent.tools = [ parse_compose_file, classify_compose_services, check_prereqs, list_ce_projects, target_ce_project, list_ce_apps, get_ce_app, get_ce_app_logs, get_ce_app_events, create_ce_secret_from_env_file, create_ce_registry_secret, cr_login, cr_region_set, cr_namespace_add, docker_build, docker_push, deploy_ce_app, update_ce_app, delete_ce_app ] each tool → ce_ops. → subprocess.run(list_args) → {ok, command, returncode, stdout, stderr} On failure: agent reads stderr → calls get_ce_app_events / get_ce_app_logs → matches known patterns (ImagePullBackOff, OOM, port mismatch) → proposes ONE fix → asks user to confirm`,cugaContribution:["Conversational triage — classify-and-explain phase reads any compose file (not just cuga-apps shaped) and tells the user WHY a service won't fit before they try, instead of failing mid-deploy.","Diagnosis loop — on `ibmcloud ce app create` failure, the agent fetches events + logs, matches the error pattern, and proposes a specific fix; no blind retries.","Hard validation under the LLM — every command is built from validated args (regex allowlist + structured kwargs), so the LLM cannot shell-inject through a malformed service name or tag.","Confirmation gates by default — destructive tools (delete_ce_app) require force=True and the system prompt instructs the agent to confirm the exact name with the user first."],examples:["Classify the compose file at /home/me/work/agent-apps/cuga-apps/docker-compose.yml","Deploy just the 8 MCP servers to my cuga-apps Code Engine project","My mcp-web deploy is stuck in ImagePullBackOff — what is wrong?","Walk me through first-time setup: ICR namespace, registry secret, env secret","Update mcp-knowledge to image us.icr.io/cuga-apps/mcp:v2 and roll"],appUrl:"http://localhost:28818",mcpUsage:[],inlineTools:["parse_compose_file","classify_compose_services","check_prereqs","list_ce_projects","target_ce_project","list_ce_apps","get_ce_app","get_ce_app_logs","get_ce_app_events","delete_ce_app","create_ce_secret_from_env_file","create_ce_registry_secret","cr_login","cr_region_set","cr_namespace_add","docker_build","docker_push","deploy_ce_app","update_ce_app"]},{id:"chief-of-staff",name:"Chief of Staff",tagline:"One chat over every MCP server — and a Toolsmith that acquires new tools when it hits a gap",type:"other",surface:"gateway",description:"A single chat UI that aggregates every MCP server in mcp_servers/* through one cuga planner. When the agent hits a gap, a separate Toolsmith service (LangGraph ReAct) goes shopping: searches a curated OpenAPI catalog + APIs.guru, generates tool code with a swappable Coder (gpt-oss or Claude), probes it, and live-mounts it back into the cuga adapter. Auth-aware (api-key, bearer, oauth2 with auto-refresh on 401), vault-backed secrets, persistent ToolArtifacts that survive restarts, optional browser-driven tools for sites without APIs (Playwright + a YAML browser-task DSL).",category:"enterprise",status:"partial",channels:[],tools:["cuga adapter","Toolsmith","browser-runner"],demoPath:"chief_of_staff",howToRun:{envVars:["LLM_PROVIDER","LLM_MODEL","AGENT_SETTING_CONFIG","TOOLSMITH_CODER"],setup:["cd chief_of_staff","# MCP servers must be running first: python apps/launch.py"],command:"./start.sh # or: docker compose up"},architecture:"Five services: (1) cuga-adapter (port 8000) wraps cuga.sdk.CugaAgent and exec()s artifact code under an import allowlist; (2) Toolsmith (port 8001) is a LangGraph ReAct agent with its own tool belt — search_catalog, search_openapi_index, generate_tool_code, probe_generated_tool, register_tool_artifact; (3) browser-runner (port 8002) is a Playwright + Chromium service that executes a declarative YAML DSL (go_to / click_text / fill_field / extract_text / user_confirm) for sites without APIs; (4) backend (port 8765) is the FastAPI shell + MCP discovery + registry; (5) frontend (port 5174) is the chat surface plus a tools panel. ToolArtifacts persist on disk at data/tools// and are reloaded on restart. Secrets live in a vault (OS keyring → SQLite + base64-XOR fallback) and are injected at call time, never logged.",diagram:`./start.sh → http://127.0.0.1:5174 (frontend) │ ▼ backend :8765 (FastAPI shell, registry, MCP discovery) ├─► cuga-adapter :8000 (planner; exec()s tool code) ├─► Toolsmith :8001 (LangGraph ReAct, the brain) └─► browser-runner :8002 (Playwright; YAML DSL) User: "do X" │ ▼ cuga planner → tool call ──── existing MCP tool? ──► run it │ └── [[TOOL_GAP]] ──► Toolsmith.acquire(intent) ├─ search_catalog (curated) ├─ search_openapi_index / APIs.guru ├─ Coder.generate_tool_code (gpt-oss or Claude) ├─ probe (structural → exec → revise×3) └─ register ToolArtifact + reload cuga │ ▼ new tool live-mounted │ ▼ retry user's request`,cugaContribution:["Cuga planner is the swappable front end; Toolsmith is the durable brain. The planner can be replaced without losing acquired tools.","Adapter exec()s LLM-generated tool code under an import allowlist — disallowed imports register as error stubs that raise on call instead of crashing the adapter.","Auth-aware Coder + auto-refresh on 401 — the OAuth2 access token is swapped, retried once, and the refreshed token persisted back to the vault.","ToolArtifacts are the canonical disk format (manifest.yaml + tool.py + probe.json); LangChain / MCP / OpenAPI bindings are computed from one source of truth."],examples:["What's the weather in Berlin? (api_key_query — openweather, seeded)","Show me trending Hacker News stories (browser source — HN scrape template)","Send an email to alice@x.com with subject Y (oauth2 — Gmail Send, paste tokens)","List my Google Calendar events for next week (oauth2 — Calendar list-events)","What is the GitHub repo about for anthropics/claude-code? (browser source — GitHub About scrape)"],appUrl:"http://localhost:5174",mcpUsage:[],inlineTools:[]},{id:"recipe-composer",name:"Recipe Composer",tagline:"Tell the agent what's in your pantry — get 3–5 cookable recipes for tonight",type:"other",surface:"gateway",description:"A pantry-first home-cooking assistant with a browser UI. Tell the agent what ingredients you have, your dietary preferences (vegetarian, vegan, pescatarian, gluten-free, dairy-free, keto), and any allergies — it tracks all of it per session. When you ask for ideas, it brainstorms 3–5 dishes that use what you have, validates each against your diet/allergies, looks up rough macros from a built-in lookup table, and proposes substitutions for items you're missing. Recipes appear as cards in the right panel with cook time, difficulty, calorie estimate, and collapsible step-by-step instructions. All tools are inline — no MCP servers, no API keys beyond the LLM provider.",category:"personal",status:"working",channels:[],tools:["add_to_pantry()","remove_from_pantry()","list_pantry()","set_diet()","add_allergy()","estimate_macros()","suggest_substitution()","check_diet_compatibility()","save_recipes()"],demoPath:"apps/recipe_composer",howToRun:{envVars:["LLM_PROVIDER","LLM_MODEL","ANTHROPIC_API_KEY"],setup:["cd apps/recipe_composer","pip install -r requirements.txt"],command:"python main.py --port 28820"},architecture:"FastAPI serves the single-page UI. POST /ask → CugaAgent calls add_to_pantry / set_diet / add_allergy eagerly as the user mentions ingredients and preferences. On a recipe request: list_pantry → brainstorm dishes → check_diet_compatibility per candidate → optional estimate_macros → save_recipes (structured cards). GET /session/{thread_id} returns live pantry + diet + allergies + recipes for the right-panel poll. All tools are inline @tool defs over a per-thread session dict; no MCP, no external APIs.",diagram:`python main.py → http://127.0.0.1:28820 User: "I have chicken breast, rice, broccoli, and soy sauce" │ POST /ask ▼ CugaAgent ├─ add_to_pantry(thread_id=…, ingredient="chicken breast") ├─ add_to_pantry(thread_id=…, ingredient="rice") ├─ add_to_pantry(thread_id=…, ingredient="broccoli") ├─ add_to_pantry(thread_id=…, ingredient="soy sauce") │ User: "I'm vegetarian and what can I cook tonight?" ├─ set_diet(thread_id=…, diet="vegetarian") ├─ list_pantry(thread_id=…) → pantry, diet, allergies ├─ check_diet_compatibility(thread_id=…, ingredients_csv="chicken breast,…") │ → blocked_by_diet=["chicken breast"] ├─ suggest_substitution(ingredient="chicken breast") │ → tofu, chickpeas ├─ estimate_macros(ingredient="rice", grams=200) ├─ save_recipes([{title, time_minutes, difficulty, uses, missing, │ calories_est, why, steps}, …]) ▼ Right panel: pantry + diet pills + 3–5 recipe cards w/ collapsible steps`,cugaContribution:['Eagerly mutates per-session pantry state as the user mentions ingredients — no "add to pantry" button needed in the UI',"check_diet_compatibility short-circuits dishes that violate diet/allergies before they reach the user — fewer apologetic retries","estimate_macros uses a static lookup table baked into the tool body — no nutrition API key, no external network call","save_recipes pushes structured JSON to the UI; the right panel renders cards (with collapsible cook steps) automatically"],examples:["I have chicken breast, rice, broccoli, and soy sauce","Add eggs, spinach, and tomato to my pantry","I'm vegetarian and allergic to peanut butter","What can I cook tonight in under 25 minutes?","Roughly how many calories in a 200 g portion of pasta?","What can I substitute for butter in a sauté?","Anything with high protein?"],appUrl:"http://localhost:28820",mcpUsage:[],inlineTools:["add_to_pantry","remove_from_pantry","list_pantry","set_diet","add_allergy","estimate_macros","suggest_substitution","check_diet_compatibility","save_recipes"]},{id:"city-beat",name:"City Beat",tagline:"Name a city — get a one-screen briefing of weather, news, background, and (optional) crypto",type:"other",surface:"gateway",description:"Type a city name. The agent assembles a one-screen briefing pulling from four MCP servers: geocode + current weather (mcp-geo), today's news (mcp-web), encyclopedia background (mcp-knowledge), and an optional crypto market spotlight (mcp-finance). Inline session tools track the active city, an editable focus-topic list (biases the news search), a clickable watchlist of cities you've visited, and an optional crypto ticker. The briefing appears as a hero card on the right (city + tagline + lat/lon), followed by weather, news headlines (linked), Wikipedia background, optional nearby attractions, optional crypto sidebar.",category:"personal",status:"working",channels:[],tools:["geocode()","get_weather()","search_attractions()","web_search()","get_wikipedia_article()","get_crypto_price()","set_current_city()","add_focus_topic()","save_briefing()"],demoPath:"apps/city_beat",howToRun:{envVars:["LLM_PROVIDER","LLM_MODEL","ANTHROPIC_API_KEY"],setup:["cd apps/city_beat","pip install -r requirements.txt"],command:"python main.py --port 28821"},architecture:"FastAPI serves the single-page UI. POST /ask → CugaAgent (1) calls set_current_city + get_session_state, (2) geocodes via mcp-geo.geocode, (3) fans out to mcp-geo.get_weather + mcp-web.web_search + mcp-knowledge.get_wikipedia_article in any order, (4) optionally calls mcp-geo.search_attractions and mcp-finance.get_crypto_price, (5) calls inline save_briefing with a structured JSON object. GET /session/{thread_id} returns the live state for the right-panel poll. _mcp_bridge.load_tools resolves URLs automatically: Code Engine when CE_APP is set, docker-compose DNS in containers, localhost otherwise.",diagram:`python main.py → http://127.0.0.1:28821 User: "Brief me on Lisbon — focus on tech startups" │ POST /ask ▼ CugaAgent ├─ set_current_city(thread_id=…, city="Lisbon") ├─ add_focus_topic(thread_id=…, topic="tech startups") ├─ get_session_state(thread_id=…) ├─ geocode(place="Lisbon") [mcp-geo] │ → lat, lon, display_name ├─ get_weather(city="Lisbon") [mcp-geo] ├─ web_search(query="Lisbon news today tech startups", max_results=5) [mcp-web] ├─ get_wikipedia_article(title="Lisbon") [mcp-knowledge] ├─ (optional) search_attractions(lat, lon, category="cultural") [mcp-geo] ├─ (optional) get_crypto_price(symbol="eth") [mcp-finance] ├─ save_briefing({city, lat, lon, weather, wiki, news, attractions?, crypto?, tagline}) ▼ Right panel: hero card + weather + news (linked) + Wikipedia + (optional) attractions + (optional) crypto + clickable watchlist`,cugaContribution:["Composes tools from four MCP servers (geo, web, knowledge, finance) with six inline session-state tools — neither half stands alone","_mcp_bridge auto-resolves URLs across local / docker / Code Engine — same app code, three deployment targets","Inline focus_topics bias the web_search query string at composition time — no need to retrain or fork mcp-web","save_briefing's docstring is the contract: structured JSON the right panel renders without UI-side schema knowledge","Watchlist accumulates cities the user asks about — the UI re-asks on click, turning the right panel into a navigable history"],examples:["Brief me on Lisbon","What's happening in Tokyo today?","Brief me on Mexico City — focus on live music","Spotlight ETH on the briefing","What can I do in Berlin tonight?","Brief me on Bangalore — focus on weather and transit","Clear the focus and give me a fresh take on Paris"],appUrl:"http://localhost:28821",mcpUsage:[{server:"geo",tools:["geocode","get_weather","search_attractions"]},{server:"web",tools:["web_search"]},{server:"knowledge",tools:["get_wikipedia_article","search_wikipedia"]},{server:"finance",tools:["get_crypto_price"]}],inlineTools:["set_current_city","add_focus_topic","clear_focus_topics","set_crypto_spotlight","get_session_state","save_briefing"]},{id:"ouroboros",name:"Ouroboros",tagline:"Name a location — get a ranked board of local businesses that need a CUGA agent, with pitches and cold emails",type:"other",surface:"gateway",description:'CUGA finds its next client. Type a location and (optionally) a category — "find restaurants in pleasantville NY", "salons in brooklyn that need appointment booking" — and the app produces a ranked board of independent local businesses that would benefit from a CUGA-powered conversational agent, each with a tailored pitch and (for the top 3) a drafted cold email. Built on a CugaSupervisor orchestrating 7 specialist CugaAgents, each backed by a declarative skill (SKILL.md + tools.py): scout (geocode + Overpass/OSM), site_auditor, voice_of_customer, person_finder, stack_scanner, revenue_estimator, and pitch_email_writer. Three policies (intent_guard, tool_guide, output_formatter) attach once at startup. The writer emits a fenced JSON board that the dark two-panel UI parses and renders; a request runs scout → 5 specialist sweeps → writer (~15 LLM round-trips, 1–3 min).',category:"enterprise",status:"partial",channels:[],tools:["CugaSupervisor","scout","site_auditor","voice_of_customer","person_finder","stack_scanner","revenue_estimator","pitch_email_writer"],demoPath:"apps/ouroboros",howToRun:{envVars:["LLM_PROVIDER","LLM_MODEL","AGENT_SETTING_CONFIG","RITS_API_KEY","CUGA_TARGET"],setup:["cd apps/ouroboros","/opt/homebrew/bin/python3.12 -m venv .venv # brew Python 3.12 — needs sqlite enable_load_extension",".venv/bin/pip install -U 'cuga>=0.2.28'",".venv/bin/pip install -r requirements.txt"],command:".venv/bin/python main.py --port 28822"},architecture:"FastAPI serves a dark two-panel UI. POST /ask builds (lazily) a CugaSupervisor over 7 specialist CugaAgents, each loaded from one skill folder under skills/ (SKILL.md frontmatter + tools.py exporting TOOLS). A prescriptive 3-phase task prelude drives the cascade: scout geocodes the location and pulls candidate businesses from Overpass/OSM; phase 2 runs 5 specialist sweeps (site_auditor, voice_of_customer, person_finder, stack_scanner, revenue_estimator) over the top candidates; the pitch_email_writer consolidates everything into a fenced JSON leads board. The server defensively extracts the JSON (fenced, bare, or balanced-brace) and stores it per thread_id; the right panel polls /session/{thread_id} every 8s. Three policies attach exactly once. The LocalExecutor per-block timeout is monkey-patched up to 180s because specialist delegations regularly take 30–60s.",diagram:`.venv/bin/python main.py --port 28822 → http://127.0.0.1:28822 User: "find restaurants in pleasantville NY" │ POST /ask ▼ CugaSupervisor (planner delegates to specialists) ├─ scout → geocode + Overpass/OSM candidate list │ ├─ phase 2 sweeps (per top candidate): │ ├─ site_auditor → capability + freshness signals │ ├─ voice_of_customer → review-snippet friction mining │ ├─ person_finder → owner + email-pattern guesses │ ├─ stack_scanner → third-party widget fingerprint │ └─ revenue_estimator → size signals → coarse ARR band │ └─ pitch_email_writer → fenced JSON leads board (pitch + top-3 emails) │ ▼ server _extract_leads_json → per-thread session │ ▼ Right panel polls /session/{thread_id} every 8s → ranked lead cards`,cugaContribution:["CugaSupervisor A2A orchestration — one planner delegates to 7 specialist CugaAgents, each running in its own bounded plan/execute context","Skills are declarative: each specialist is a SKILL.md + tools.py folder host-loaded at startup; adding an 8th specialist is a factory + one list entry","Three policies (intent_guard, tool_guide, output_formatter) attach once and shape every specialist call","Defensive leads-JSON extraction tolerates the planner re-emitting the writer's fence in four different shapes — fenced, bare, balanced-brace first/last"],examples:["Find leads in Westchester, NY","Restaurants in HSR Layout, Bangalore — pitch order bots","Salons in Brooklyn that need appointment booking","Independent hotels in Lisbon — concierge agent angle","Clinics in Austin — patient FAQ + intake"],appUrl:"http://localhost:28822",mcpUsage:[],inlineTools:[]},{id:"github-trending",name:"GitHub Trending",tagline:"Ask what's trending on GitHub — and what each repo actually offers",type:"other",surface:"gateway",description:`Ask what's trending on GitHub — overall, by language, or by topic — and the agent returns a ranked board of repositories, each with a plain-English summary of what the project offers: what it's for, who it's for, and why it's gaining attention. Live data comes from GitHub's public REST API via direct httpx calls — no MCP servers, no keys (set GITHUB_TOKEN to raise the rate limit). "Trending" is approximated as recently-created repos gaining the most stars in a daily/weekly/monthly window. The agent finds the repos, reads the top READMEs and language breakdowns, writes a summary + an "offers" bullet list + a why-trending line per repo, and renders them as cards in the right panel.`,category:"personal",status:"working",channels:[],tools:["set_filters()","find_trending_repos()","get_repo_readme()","get_repo_languages()","save_repos()"],demoPath:"apps/github_trending",howToRun:{envVars:["LLM_PROVIDER","LLM_MODEL","GITHUB_TOKEN"],setup:["cd apps/github_trending","pip install -r requirements.txt"],command:"python main.py --port 28823"},architecture:"FastAPI serves the single-page UI. POST /ask → CugaAgent records any language/since/topic filter (set_filters), then find_trending_repos hits the GitHub Search API (created:>DATE sort=stars) for a ranked list. For the top repos it calls get_repo_readme (raw) and optionally get_repo_languages, writes a grounded summary + offers + why_trending per repo, and save_repos pushes the structured cards to the right-panel poll (/session/{thread_id}). All tools are inline @tool defs over httpx; the only external dependency is the public GitHub API.",diagram:`python main.py → http://127.0.0.1:28823 User: "New LLM agent repos trending this week" │ POST /ask ▼ CugaAgent ├─ set_filters(topic="llm", since="weekly") ├─ find_trending_repos() → GitHub Search API (created:>DATE, sort=stars) ├─ get_repo_readme(owner/repo) × top N ├─ get_repo_languages(owner/repo) (optional, for the stack) ├─ save_repos([{full_name, stars, summary, offers[], why_trending}, …]) ▼ Right panel: ranked repo cards w/ summary + what each offers`,cugaContribution:[`Approximates GitHub's key-free "trending" via the Search API (created:>DATE, sort=stars) — surfaces new repos rising fast, no scraping`,"Reads each repo's README to ground the summary in what the project actually does, instead of echoing the one-line description",'Per-repo "offers" bullets + a why-trending line make the board scannable',"Returns a rate_limited code the agent surfaces plainly (suggesting GITHUB_TOKEN) instead of failing silently"],examples:["What's trending this week?","Trending Python repos","New Rust CLI tools","New LLM agent frameworks gaining stars","Trending this month in the devtools topic"],appUrl:"http://localhost:28823",mcpUsage:[],inlineTools:["set_filters","find_trending_repos","get_repo_readme","get_repo_languages","save_repos"]},{id:"ai-labs-news",name:"AI Labs News",tagline:"A live digest of the latest posts from the major AI labs",type:"event-driven",surface:"gateway",description:`A curated, glanceable digest of the latest posts from the major AI labs and research groups — OpenAI, Anthropic, Google DeepMind, Google Research, Microsoft Research, IBM Research, Meta AI, Hugging Face, and Berkeley AI Research. Ask broadly ("what's new in AI this week?") or narrowly ("latest from Anthropic and OpenAI on agents"), and the agent pulls each lab's blog feed, merges newest-first, spots cross-lab themes, and summarizes. Live data comes from each lab's public RSS/Atom feed via httpx + feedparser — no MCP servers, no keys. Each lab has fallback feed URLs, so a moved/broken feed is reported rather than faked.`,category:"personal",status:"working",channels:[],tools:["list_labs()","set_focus()","fetch_lab_news()","fetch_all_news()","save_digest()"],demoPath:"apps/ai_labs_news",howToRun:{envVars:["LLM_PROVIDER","LLM_MODEL"],setup:["cd apps/ai_labs_news","pip install -r requirements.txt"],command:"python main.py --port 28824"},architecture:"FastAPI serves the single-page UI. POST /ask → CugaAgent optionally narrows to specific labs/topics (set_focus), then fetch_all_news fetches each lab's RSS/Atom feed (httpx, browser UA) and parses it with feedparser, trying fallback URLs per lab until one yields entries. Entries are tag-stripped, truncated, and merged newest-first. The agent identifies 2–4 cross-lab themes, writes a headline, and save_digest persists the structured digest the right panel renders (/session/{thread_id}). All tools are inline @tool defs; the lab registry (_LABS) is the single place to add a lab or fix a feed URL.",diagram:`python main.py → http://127.0.0.1:28824 User: "Latest from OpenAI, Anthropic, and Google DeepMind" │ POST /ask ▼ CugaAgent ├─ set_focus(labs="openai,anthropic,google-deepmind") ├─ fetch_all_news() → per-lab RSS/Atom (httpx + feedparser, fallback URLs) │ merge + sort newest-first; report any unreachable feed ├─ (identify 2–4 cross-lab themes; write a headline) ├─ save_digest({headline, themes[], items[{lab,title,url,published,summary,tags}]}) ▼ Right panel: headline + theme chips + per-post cards grouped by lab`,cugaContribution:["Resilient feed loading: multiple fallback URLs per lab; an unreachable feed is reported (labs_failed), never invented",'Cross-lab synthesis — themes + a one-line "state of AI this week" headline, not just a flat list',"Summaries are the feeds' own descriptions, tag-stripped and truncated — the agent does not embellish them","Adding a lab or fixing a moved feed is a one-entry change in the _LABS registry"],examples:["What's new in AI this week?","Latest from OpenAI and Anthropic","Recent Microsoft Research and IBM Research posts","AI agent news across the labs","Which labs do you cover?"],appUrl:"http://localhost:28824",mcpUsage:[],inlineTools:["list_labs","set_focus","fetch_lab_news","fetch_all_news","save_digest"]},{id:"find-a-doctor",name:"Find a Doctor",tagline:"Name a location + specialty — get ranked doctors with review-grounded pros & cons",type:"other",surface:"gateway",description:`Give a location and (optionally) a specialty or preference — "find a cardiologist in Boston", "a really experienced pediatric dentist in Austin who's good with kids" — and the agent assembles a ranked board of doctors with a review-grounded summary of pros and cons. Same spirit as Ouroboros (geocode → discover → enrich → synthesize) but with inline tools only. Keyless live data: Nominatim (OpenStreetMap) geocoding, Overpass (OpenStreetMap) for nearby listings, and DuckDuckGo HTML for review snippets from trusted health sites (Healthgrades, Vitals, Zocdoc, WebMD, RateMDs, Yelp, …). Two discovery paths are merged — structured OSM listings plus web search for named, well-regarded doctors — then per-candidate reviews drive the pros/cons. Informational only: every pro/con must trace to a real retrieved snippet; nothing is invented.`,category:"personal",status:"partial",channels:[],tools:["set_search()","geocode_location()","find_doctors()","web_search()","fetch_reviews()","save_doctors()"],demoPath:"apps/find_a_doctor",howToRun:{envVars:["LLM_PROVIDER","LLM_MODEL"],setup:["cd apps/find_a_doctor","pip install -r requirements.txt"],command:"python main.py --port 28825"},architecture:"FastAPI serves the single-page UI. POST /ask → CugaAgent records the location/specialty/preferences (set_search), geocodes via Nominatim (geocode_location), then discovers candidates two ways and merges them: find_doctors queries Overpass for amenity=doctors / healthcare=doctor|clinic within a radius (structured address/phone/website), and web_search hits DuckDuckGo HTML to surface named, well-regarded doctors. For the top candidates it calls fetch_reviews, which filters DuckDuckGo results to trusted health-review domains, then synthesizes a rating_summary + pros/cons + sources per doctor and ranks them. save_doctors persists the board the right panel renders. All tools are inline @tool defs over httpx; DuckDuckGo HTML is parsed with a small regex (no bs4).",diagram:`python main.py → http://127.0.0.1:28825 User: "A really experienced pediatric dentist in Austin who's good with kids" │ POST /ask ▼ CugaAgent ├─ set_search(location="Austin", specialty="pediatric dentist", │ preferences="very experienced, good with kids") ├─ geocode_location() → Nominatim (lat/lon + display name) ├─ find_doctors() → Overpass (OSM listings: address/phone/site) ├─ web_search("best pediatric dentist in Austin") → discover named doctors ├─ fetch_reviews(name) × top candidates → DuckDuckGo → trusted health sites ├─ save_doctors([{name, rating_summary, pros[], cons[], sources[]}, …]) ▼ Right panel: ranked doctor cards w/ pros/cons + sources + disclaimer`,cugaContribution:["Two discovery paths merged — structured OSM listings (address/phone) + web search for named, well-regarded doctors — covers gaps in either source","Reviews are filtered to trusted health domains; every pro/con must trace to a real retrieved snippet (no invented ratings or credentials)",'Nuanced asks ("really experienced", a sub-specialty) bias the search queries and the ranking',"Keyless end-to-end: Nominatim + Overpass + DuckDuckGo HTML (regex-parsed) — no places/reviews API key"],examples:["Find a cardiologist in Boston","A really experienced pediatric dentist in Austin who's good with kids","Top-rated orthopedic surgeon near San Mateo, CA","An OB-GYN in Chicago accepting new patients","Dermatologist in Seattle with good reviews"],appUrl:"http://localhost:28825",mcpUsage:[],inlineTools:["set_search","geocode_location","find_doctors","web_search","fetch_reviews","save_doctors"]},{id:"meetup-finder",name:"Meetup Finder",tagline:"Name a city + interests — the agent browses Meetup, Luma & Eventbrite and ranks the events",type:"other",surface:"gateway",description:`Give a location and your interests (tech/AI by default) — "AI meetups in San Francisco this week", "LLM and data-eng events near Austin this weekend" — and the agent returns a ranked board of upcoming events with date, venue, host, and an RSVP link. This is the app that exercises CUGA's browser capability: Meetup, Luma, and Eventbrite all deprecated their public search APIs but render rich, structured event pages, so the agent drives a real headless Chromium via Playwright, opens each discovery page, and extracts events from the page's embedded JSON-LD / Next.js data. Playwright is wrapped as inline @tool defs and the CugaAgent planner orchestrates them (same pattern as chief_of_staff's browser-runner) — no MCP servers, no API keys. CUGA policies (tool_guide + output_formatter) keep it honest and lock the board contract.`,category:"personal",status:"partial",channels:[],tools:["set_search()","build_event_urls()","fetch_events() [Playwright]","save_events()"],demoPath:"apps/meetup_finder",howToRun:{envVars:["LLM_PROVIDER","LLM_MODEL","MEETUP_HEADLESS"],setup:["cd apps/meetup_finder","pip install -r requirements.txt","python -m playwright install chromium # one-time: fetch the browser"],command:"python main.py --port 28826"},architecture:`FastAPI serves the single-page UI. POST /ask → CugaAgent records the search (set_search), builds Meetup/Luma/Eventbrite discovery URLs (build_event_urls, computed in Python), then for each URL calls fetch_events — which opens the page in a lazily-launched, reused headless Chromium (Playwright async API, one browser, a fresh context per fetch under an asyncio lock, mirroring chief_of_staff/browser_runner/executor.py) and extracts events from the page's embedded JSON-LD and __NEXT_DATA__ blobs. The agent merges/dedupes/filters by date, ranks by interest fit, and save_events persists the board the right panel polls (/session/{thread_id}). A tool_guide policy enforces "only report what fetch_events returned" and an output_formatter locks the save+reply contract. Browser-dependent, so it is local-only (not on the shared no-browser deploy image).`,diagram:`python main.py → http://127.0.0.1:28826 (headless Chromium via Playwright) User: "AI agent meetups in San Francisco this week" │ POST /ask ▼ CugaAgent ├─ set_search(interests="AI agents", location="San Francisco", when="this week") ├─ build_event_urls() → [meetup find, lu.ma/sf, eventbrite /d/…] ├─ fetch_events(url) × each ──► Playwright: launch Chromium → goto → │ extract JSON-LD + __NEXT_DATA__ events ├─ (merge + dedupe + filter to this week + rank by fit) ├─ save_events([{title, url, start, venue, host, source, why}, …]) ▼ Right panel: ranked event cards (date chip, venue, host, RSVP link)`,cugaContribution:["Exercises CUGA's browser capability — drives a real headless Chromium over no-search-API event sites, the use case browser automation exists for","Robust extraction from embedded JSON-LD / Next.js data instead of brittle CSS scraping; empty pages degrade gracefully and are reported, never faked","One lazily-launched browser reused across requests, a fresh context per fetch under a lock (the chief_of_staff browser-runner pattern)","Policies keep it grounded: a tool_guide forbids inventing events; an output_formatter locks the ranked-board save + reply"],examples:["AI agent meetups in San Francisco this week","LLM and data engineering events near New York","Startup / founder events in London this month","ML research talks in Boston this weekend","Developer conferences in Seattle"],appUrl:"http://localhost:28826",mcpUsage:[],inlineTools:["set_search","build_event_urls","fetch_events","get_session_state","save_events"]}],js={personal:{label:"Personal Productivity",color:"green"},enterprise:{label:"Enterprise",color:"indigo"}},em={working:{label:"Working",color:"green"},partial:{label:"Partial",color:"yellow"},"not-working":{label:"Not working",color:"orange"},gap:{label:"Gap",color:"red"}},ad="1gxwxi8kos9y",od="us-east",tm={"web-researcher":"cuga-apps-web-researcher","paper-scout":"cuga-apps-paper-scout","travel-agent":"cuga-apps-travel-planner","code-reviewer":"cuga-apps-code-reviewer","hiking-research":"cuga-apps-hiking-research","movie-recommender":"cuga-apps-movie-recommender","webpage-summarizer":"cuga-apps-webpage-summarizer","wiki-dive":"cuga-apps-wiki-dive","youtube-research":"cuga-apps-youtube-research","arch-diagram":"cuga-apps-arch-diagram","brief-budget":"cuga-apps-brief-budget","trip-designer":"cuga-apps-trip-designer","ibm-cloud-advisor":"cuga-apps-ibm-cloud-advisor","ibm-docs-qa":"cuga-apps-ibm-docs-qa","ibm-whats-new":"cuga-apps-ibm-whats-new","api-doc-gen":"cuga-apps-api-doc-gen","stock-alert":"cuga-apps-stock-alert","recipe-composer":"cuga-apps-recipe-composer","city-beat":"cuga-apps-city-beat","github-trending":"cuga-apps-github-trending","ai-labs-news":"cuga-apps-ai-labs-news","find-a-doctor":"cuga-apps-find-a-doctor",newsletter:"cuga-apps-newsletter","server-monitor":"cuga-apps-server-monitor",ouroboros:"cuga-apps-ouroboros","meetup-finder":"cuga-apps-meetup-finder"},Ll="https://cuga-agent-apps.1gxwxi8kos9y.us-east.codeengine.appdomain.cloud".replace(/\/+$/,"");function nm(e){const t=tm[e];return t?t.replace(/^cuga-apps-/,""):null}function ui(e){if(!e.appUrl)return null;{const t=nm(e.id);return t&&Ll?`${Ll}/a/${t}/`:null}}const rm="https://github.com/cuga-project/cuga-apps/tree/main/cuga-apps",id=e=>e?`${rm}/${e}`:"https://github.com/cuga-project/cuga-apps",sm=new Set(["bird-invocable-api","api-doc-gen","box-qa","code-reviewer","deck-forge","smart-todo","drop-summarizer"]),am=new Set(["chief-of-staff","code-engine-deployer","video-qa","voice-journal","ibm-whats-new","brief-budget","trip-designer"]),rn=e=>am.has(e)?"exploratory":sm.has(e)?"for-later":"ship-ready",om={"ship-ready":{glyph:"✦",cls:"text-amber-500"},exploratory:null,"for-later":null};function ld({id:e,ml:t}){const n=om[rn(e)];return n?u.jsx("span",{className:`${n.cls} ${t} font-bold`,children:n.glyph}):null}const im={all:"All",showcase:"✦ Showcase",additional:"Additional apps"},lm=(e,t)=>t==="all"||(t==="showcase"?rn(e)==="ship-ready":rn(e)!=="ship-ready");function um({value:e,onChange:t}){const n=["all","showcase","additional"];return u.jsxs("div",{className:"flex items-center gap-2.5 flex-wrap",children:[u.jsx("span",{className:"text-sm text-t4 font-semibold uppercase tracking-wider w-20 shrink-0",children:"Stage"}),n.map(r=>{const s=e===r,a=r==="showcase"?"bg-amber-500 text-white border-amber-500":r==="additional"?"bg-slate-500 text-white border-slate-500":"bg-indigo-600 text-white border-indigo-600";return u.jsx("button",{onClick:()=>t(r),className:`text-sm px-3.5 py-1.5 rounded-full font-medium border transition-all ${s?a:"bg-tsurf border-tborder text-t3 hover:text-t1 hover:border-t2"}`,children:im[r]},r)})]})}const cm=Object.keys(js);function dm({value:e,onChange:t}){return u.jsxs("div",{className:"flex items-center gap-2.5 flex-wrap",children:[u.jsx("span",{className:"text-sm text-t4 font-semibold uppercase tracking-wider w-20 shrink-0",children:"Category"}),cm.map(n=>{const r=e===n;return u.jsx("button",{onClick:()=>t(r?"all":n),className:`text-sm px-3.5 py-1.5 rounded-full font-medium border transition-all ${r?"bg-indigo-600 text-white border-indigo-600":"bg-tsurf border-tborder text-t3 hover:text-t1 hover:border-t2"}`,children:js[n].label},n)})]})}const pm={TAVILY_API_KEY:"https://app.tavily.com/home",ALPHA_VANTAGE_API_KEY:"https://www.alphavantage.co/support/#api-key",OPENTRIPMAP_API_KEY:"https://opentripmap.io/product",BOX_CONFIG_PATH:"https://developer.box.com/guides/authentication/jwt/jwt-setup/",BOX_FOLDER_ID:"https://support.box.com/hc/en-us/articles/360043697154-Finding-a-Folder-or-File-ID",SMTP_HOST:"https://support.google.com/mail/answer/7126229",SMTP_USERNAME:"https://support.google.com/accounts/answer/185833",SMTP_PASSWORD:"https://support.google.com/accounts/answer/185833",BIRD_DEV_JSON:"https://bird-bench.github.io/",BIRD_DBS_DIR:"https://bird-bench.github.io/"};function hm({name:e}){const t=pm[e],n="text-xs px-2 py-1 font-mono border whitespace-nowrap";return t?u.jsxs("a",{href:t,target:"_blank",rel:"noopener noreferrer",onClick:r=>r.stopPropagation(),title:`How to get ${e} ↗`,className:`${n} bg-amber-500/10 text-amber-600 border-amber-500/30 hover:border-amber-500 hover:underline`,children:[e," ↗"]}):u.jsx("span",{className:`${n} bg-amber-500/10 text-amber-600 border-amber-500/20`,children:e})}const fm=["travel-agent","city-beat","wiki-dive","paper-scout","find-a-doctor","hiking-research","arch-diagram","youtube-research","github-trending","ai-labs-news","recipe-composer","movie-recommender","web-researcher","newsletter","webpage-summarizer","server-monitor","stock-alert","ibm-cloud-advisor","ibm-docs-qa","meetup-finder","ouroboros"],Al=e=>{const t=fm.indexOf(e);return t===-1?Number.MAX_SAFE_INTEGER:t},mm=["code-reviewer","video-qa","bird-invocable-api","trip-designer","api-doc-gen","voice-journal","brief-budget","drop-summarizer","smart-todo","ibm-whats-new","chief-of-staff","box-qa","deck-forge","code-engine-deployer"],Il=e=>{const t=mm.indexOf(e);return t===-1?Number.MAX_SAFE_INTEGER:t},co=[{id:"research",title:"Research & Knowledge",accent:"indigo",ids:["paper-scout","wiki-dive","web-researcher","youtube-research","webpage-summarizer","hiking-research","movie-recommender"]},{id:"content",title:"Content Creation",accent:"emerald",ids:["newsletter","deck-forge","arch-diagram","api-doc-gen"]},{id:"documents",title:"Documents & Media Q&A",accent:"amber",ids:["box-qa","video-qa","drop-summarizer","voice-journal"]},{id:"productivity",title:"Productivity",accent:"pink",ids:["smart-todo","travel-agent"]},{id:"ops",title:"Ops & Alerts",accent:"cyan",ids:["server-monitor","stock-alert"]},{id:"developer",title:"Developer & Eval Tools",accent:"violet",ids:["code-reviewer","bird-invocable-api"]},{id:"ibm",title:"IBM Stack",accent:"slate",ids:["ibm-cloud-advisor","ibm-docs-qa","ibm-whats-new"]}],gm={indigo:{bar:"bg-indigo-500",badge:"text-indigo-500",pill:"border-indigo-500/30 hover:border-indigo-500 hover:bg-indigo-500/5",ring:"ring-indigo-500/40"},emerald:{bar:"bg-emerald-500",badge:"text-emerald-500",pill:"border-emerald-500/30 hover:border-emerald-500 hover:bg-emerald-500/5",ring:"ring-emerald-500/40"},amber:{bar:"bg-amber-500",badge:"text-amber-500",pill:"border-amber-500/30 hover:border-amber-500 hover:bg-amber-500/5",ring:"ring-amber-500/40"},pink:{bar:"bg-pink-500",badge:"text-pink-500",pill:"border-pink-500/30 hover:border-pink-500 hover:bg-pink-500/5",ring:"ring-pink-500/40"},cyan:{bar:"bg-sky-600",badge:"text-sky-700",pill:"border-sky-500/30 hover:border-sky-600 hover:bg-sky-500/5",ring:"ring-sky-500/40"},violet:{bar:"bg-violet-500",badge:"text-violet-500",pill:"border-violet-500/30 hover:border-violet-500 hover:bg-violet-500/5",ring:"ring-violet-500/40"},slate:{bar:"bg-slate-500",badge:"text-t3",pill:"border-tborder hover:border-slate-400 hover:bg-tsurf2",ring:"ring-slate-500/40"}};function vm({useCases:e,activeBucket:t,onSelectBucket:n}){const r=k.useMemo(()=>{const s=new Map;for(const a of e)s.set(a.id,a);return s},[e]);return u.jsxs("div",{className:"mb-8",children:[u.jsxs("div",{className:"flex items-center justify-between mb-4",children:[u.jsxs("div",{children:[u.jsx("h3",{className:"text-lg font-semibold text-t1",children:"Browse by domain"}),u.jsxs("p",{className:"text-sm text-t3",children:["7 capability domains · ",co.reduce((s,a)=>s+a.ids.length,0)," apps · click a bucket to filter the table below"]})]}),t&&u.jsx("button",{onClick:()=>n(null),className:"text-sm font-medium text-t3 hover:text-t1 px-3 py-1.5 bg-tsurf border border-tborder rounded-lg transition-colors",children:"← Show all apps"})]}),u.jsx("div",{className:"grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-3",children:co.map(s=>{const a=s.id===t,o=t!==null&&!a,i=gm[s.accent],l=s.ids.map(c=>r.get(c)).filter(Boolean);return u.jsxs("div",{className:`bg-tsurf border border-tborder rounded-xl overflow-hidden shadow-sm transition-all ${a?`ring-2 ${i.ring} shadow-md`:""} ${o?"opacity-50 hover:opacity-100":""}`,children:[u.jsxs("button",{onClick:()=>n(a?null:s.id),className:"w-full flex items-center gap-3 px-4 py-3 hover:bg-tsurf2 transition-colors",children:[u.jsx("div",{className:`w-1.5 h-9 rounded-full ${i.bar}`}),u.jsxs("div",{className:"flex-1 text-left",children:[u.jsx("div",{className:"text-sm font-semibold text-t1 leading-tight",children:s.title}),u.jsxs("div",{className:"text-xs text-t4 mt-0.5",children:[l.length," ",l.length===1?"app":"apps",a&&u.jsx("span",{className:`ml-1.5 font-semibold ${i.badge}`,children:"· filtering ↓"})]})]}),u.jsx("span",{className:`text-xs font-bold px-2 py-1 rounded-full bg-tsurf2 ${i.badge}`,children:l.length}),u.jsx("svg",{className:`w-4 h-4 text-t4 transition-transform ${a?"rotate-180":""}`,fill:"none",viewBox:"0 0 20 20",stroke:"currentColor",children:u.jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:2,d:"M5 8l5 5 5-5"})})]}),a&&u.jsx("div",{className:"px-4 pb-4 pt-3 border-t border-tborder/60 bg-tsurf2/40",children:u.jsx("div",{className:"flex flex-wrap gap-1.5",children:l.map(c=>u.jsxs("button",{onClick:m=>{m.stopPropagation(),window.open(ui(c)||id(c.demoPath),"_blank","noopener,noreferrer")},className:`text-xs font-medium px-2.5 py-1.5 rounded-md bg-tsurf border ${i.pill} text-t1 transition-colors whitespace-nowrap`,title:`Open ${c.name}`,children:[c.name,u.jsx(ld,{id:c.id,ml:"ml-1"})]},c.id))})})]},s.id)})})]})}function ym({useCases:e,search:t,filterCategory:n,filterBucket:r,filterShip:s}){const a=Os(),o=k.useMemo(()=>{if(!r)return null;const c=co.find(m=>m.id===r);return c?new Set(c.ids):null},[r]),i=e.filter(c=>{if(o&&!o.has(c.id))return!1;const m=!t||c.name.toLowerCase().includes(t.toLowerCase())||c.tagline.toLowerCase().includes(t.toLowerCase()),f=n==="all"||c.category===n,g=lm(c.id,s);return m&&f&&g}).sort((c,m)=>{const f=rn(c.id)==="ship-ready",g=rn(m.id)==="ship-ready";return f!==g?f?-1:1:f?Al(c.id)-Al(m.id):Il(c.id)-Il(m.id)});if(i.length===0)return u.jsx("div",{className:"py-12 text-center text-t3 text-base",children:"No use cases match your filters."});const l=["LLM_PROVIDER","LLM_MODEL","RITS_API_KEY","ANTHROPIC_API_KEY","OPENAI_API_KEY","AGENT_SETTING_CONFIG"];return u.jsx("div",{className:"bg-tsurf border border-tborder rounded-2xl overflow-x-auto shadow-sm",children:u.jsxs("table",{className:"w-full",children:[u.jsx("thead",{children:u.jsxs("tr",{className:"border-b-2 border-tborder bg-tsurf2",children:[u.jsx("th",{className:"text-left text-sm font-semibold text-t4 uppercase tracking-wider px-6 py-4 w-10",children:"#"}),u.jsx("th",{className:"text-left text-sm font-semibold text-t4 uppercase tracking-wider px-4 py-4",children:"Use Case"}),u.jsx("th",{className:"text-left text-sm font-semibold text-t4 uppercase tracking-wider px-4 py-4 hidden lg:table-cell w-[11rem]",children:"Examples"}),u.jsx("th",{className:"text-left text-sm font-semibold text-t4 uppercase tracking-wider px-4 py-4 hidden lg:table-cell",children:"Category"}),u.jsx("th",{className:"text-left text-sm font-semibold text-t4 uppercase tracking-wider px-4 py-4 hidden xl:table-cell",children:"Tools"}),u.jsx("th",{className:"text-left text-sm font-semibold text-t4 uppercase tracking-wider px-4 py-4 hidden xl:table-cell",children:"ENV Vars"}),u.jsx("th",{className:"px-4 py-4 w-10"})]})}),u.jsx("tbody",{className:"divide-y divide-tborder",children:i.map((c,m)=>{var y,b,P,T,L;const f=js[c.category],g=c.tools.slice(0,3),v=c.tools.length-g.length,w=c.howToRun.envVars.filter(A=>!l.includes(A)),_=w.slice(0,3),S=w.length-_.length,p=c.comingSoon?null:ui(c),d=rn(c.id)!=="ship-ready",h=id(c.demoPath);return u.jsxs("tr",{className:"hover:bg-tsurf2 transition-colors",children:[u.jsx("td",{className:"px-6 py-5 text-t4 text-sm font-mono",children:m+1}),u.jsxs("td",{className:"px-4 py-5",children:[u.jsxs("div",{className:"font-semibold text-t1 text-lg leading-snug",children:[c.name,u.jsx(ld,{id:c.id,ml:"ml-1.5"})]}),u.jsx("div",{className:"text-sm text-t3 mt-1 leading-relaxed",children:c.tagline})]}),u.jsx("td",{className:"px-4 py-5 hidden lg:table-cell align-top",children:(((y=c.examples)==null?void 0:y.length)??0)===0?u.jsx("span",{className:"text-sm text-t4",children:"—"}):u.jsx("div",{className:"flex flex-col gap-1.5 w-[11rem]",children:c.examples.slice(0,2).map(A=>u.jsxs("span",{title:A,className:"bg-tsurf2 text-t2 border border-tborder rounded px-2 py-1 text-xs italic whitespace-normal break-words leading-snug line-clamp-2",children:["“",A,"”"]},A))})}),u.jsx("td",{className:"px-4 py-5 hidden lg:table-cell",children:u.jsx("span",{className:"text-sm px-2.5 py-1 rounded-full font-medium bg-tsurf2 text-t3 border border-tborder",children:f.label})}),u.jsx("td",{className:"px-4 py-5 hidden xl:table-cell align-top",children:(!c.mcpUsage||c.mcpUsage.length===0)&&(((b=c.inlineTools)==null?void 0:b.length)??0)===0&&c.tools.length===0?u.jsx("span",{className:"text-sm text-t4",children:"—"}):u.jsxs("div",{className:"flex flex-col gap-1.5 max-w-[15rem]",children:[(P=c.mcpUsage)==null?void 0:P.map(A=>u.jsxs("div",{className:"flex flex-wrap items-center gap-1",children:[u.jsxs("button",{onClick:I=>{I.stopPropagation(),a(`/mcp-servers#mcp-${A.server}`)},title:`View the mcp-${A.server} server`,className:"text-[10px] uppercase tracking-wider px-1.5 py-0.5 rounded-md font-semibold bg-indigo-500/10 text-indigo-700 border border-indigo-500/30 whitespace-nowrap cursor-pointer hover:bg-indigo-500/20 hover:text-indigo-800 hover:underline",children:["mcp-",A.server," ↗"]}),A.tools.slice(0,3).map(I=>u.jsx("span",{className:"text-xs px-2 py-1 rounded-md font-mono bg-tsurf2 text-t3 border border-tborder whitespace-nowrap",children:I},I)),A.tools.length>3&&u.jsxs("span",{className:"text-xs px-2 py-1 rounded-md bg-tsurf2 text-t4 border border-tborder",children:["+",A.tools.length-3]})]},A.server)),(((T=c.inlineTools)==null?void 0:T.length)??0)>0&&u.jsxs("div",{className:"flex flex-wrap items-center gap-1",children:[u.jsx("span",{className:"text-[10px] uppercase tracking-wider px-1.5 py-0.5 rounded-md font-semibold bg-amber-500/10 text-amber-700 border border-amber-500/30 whitespace-nowrap",children:"inline"}),c.inlineTools.slice(0,3).map(A=>u.jsx("span",{className:"text-xs px-2 py-1 rounded-md font-mono bg-tsurf2 text-t3 border border-tborder whitespace-nowrap",children:A},A)),c.inlineTools.length>3&&u.jsxs("span",{className:"text-xs px-2 py-1 rounded-md bg-tsurf2 text-t4 border border-tborder",children:["+",c.inlineTools.length-3]})]}),(!c.mcpUsage||c.mcpUsage.length===0)&&(((L=c.inlineTools)==null?void 0:L.length)??0)===0&&c.tools.length>0&&u.jsxs("div",{className:"flex flex-wrap gap-1.5",children:[g.map(A=>u.jsx("span",{className:"text-xs px-2 py-1 rounded-md font-mono bg-tsurf2 text-t3 border border-tborder whitespace-nowrap",children:A},A)),v>0&&u.jsxs("span",{className:"text-xs px-2 py-1 rounded-md bg-tsurf2 text-t4 border border-tborder",children:["+",v]})]})]})}),u.jsx("td",{className:"px-4 py-5 hidden xl:table-cell align-top",children:w.length===0?u.jsx("span",{className:"text-sm text-t4",children:"—"}):u.jsxs("div",{className:"flex flex-wrap gap-1.5 max-w-[12rem]",children:[_.map(A=>u.jsx(hm,{name:A},A)),S>0&&u.jsxs("span",{className:"text-xs px-2 py-1 bg-tsurf2 text-t4 border border-tborder",children:["+",S]})]})}),u.jsx("td",{className:"px-4 py-5 text-right",onClick:A=>A.stopPropagation(),children:u.jsx("div",{className:"inline-flex flex-col items-end gap-1.5",children:d?u.jsx("a",{href:h,target:"_blank",rel:"noopener noreferrer",className:"inline-block px-3.5 py-1.5 text-sm font-semibold bg-tsurf border border-tborder text-t2 hover:text-t1 hover:border-t2 transition-colors whitespace-nowrap",children:"Check out code ↗"}):u.jsxs(u.Fragment,{children:[c.comingSoon?u.jsx("span",{className:"inline-block px-3 py-1.5 text-sm font-medium bg-tsurf2 text-t4 border border-tborder whitespace-nowrap",children:"Coming soon"}):p?u.jsx("a",{href:p,target:"_blank",rel:"noopener noreferrer",className:"inline-block px-3.5 py-1.5 text-sm font-semibold bg-indigo-600 hover:bg-indigo-700 text-white transition-colors whitespace-nowrap",children:"Try it →"}):null,u.jsx("a",{href:h,target:"_blank",rel:"noopener noreferrer",className:"text-xs font-medium text-t3 hover:text-indigo-600 hover:underline whitespace-nowrap",children:"Check out code ↗"})]})})})]},c.id)})})]})})}function wm(){const[e,t]=k.useState(""),[n,r]=k.useState("all"),[s,a]=k.useState(null),[o,i]=k.useState("showcase"),l=sd.filter(m=>!m.hidden),c={search:e,filterCategory:n,filterBucket:s,filterShip:o};return u.jsxs("div",{className:"p-6 md:p-8 max-w-screen-2xl mx-auto",children:[u.jsx("div",{className:"mb-10 bg-tsurf border border-tborder overflow-hidden",children:u.jsxs("div",{className:"flex flex-col lg:flex-row",children:[u.jsxs("div",{className:"flex-1 p-7 md:p-9",children:[u.jsxs("div",{className:"inline-flex items-center gap-2 mb-4",children:[u.jsx("span",{className:"text-xs font-semibold uppercase tracking-wider text-indigo-600 bg-indigo-500/10 border border-indigo-500/25 px-2 py-0.5",children:"IBM Research"}),u.jsx("span",{className:"text-xs text-t4 font-mono",children:"pip install cuga"})]}),u.jsx("h2",{className:"text-3xl md:text-4xl font-semibold text-t1 mb-3 tracking-tight leading-tight",children:"Real agentic apps, built on CUGA"}),u.jsxs("p",{className:"text-base text-t2 leading-relaxed max-w-2xl mb-5",children:[u.jsx("strong",{className:"text-t1",children:"CUGA"})," — the ",u.jsx("strong",{className:"text-t1",children:"C"}),"onfigurable"," ",u.jsx("strong",{className:"text-t1",children:"G"}),"eneralist ",u.jsx("strong",{className:"text-t1",children:"A"}),"gent — is a lightweight, open-source agent harness from IBM Research. It handles the unglamorous machinery around a model: planning a task out, calling tools, writing and running code, holding intermediate state together, and retrying when something fails. You start from a strong generalist and narrow it to your domain — your tools, your procedure, your guardrails."]}),u.jsxs("p",{className:"text-base text-t2 leading-relaxed max-w-2xl mb-6",children:[u.jsx("strong",{className:"text-t1",children:"CUGA Apps"})," is what that feels like in practice: ",l.length," ","working examples that double as copy-and-edit templates for real applications. Each one is a"," ",u.jsx("code",{className:"font-mono text-sm text-indigo-600",children:"CugaAgent"})," wrapped in a FastAPI UI, drawing on seven shared MCP servers."]}),u.jsxs("div",{className:"flex flex-wrap items-center gap-3",children:[u.jsx("a",{href:"https://cuga.dev",target:"_blank",rel:"noopener noreferrer",className:"px-4 py-2 text-sm font-semibold bg-indigo-600 hover:bg-indigo-700 text-white transition-colors",children:"cuga.dev ↗"}),u.jsx("a",{href:"https://github.com/cuga-project/cuga-agent",target:"_blank",rel:"noopener noreferrer",className:"px-4 py-2 text-sm font-semibold bg-tsurf border border-tborder text-t2 hover:text-t1 hover:border-t2 transition-colors",children:"GitHub ↗"})]})]}),u.jsxs("div",{className:"lg:w-72 shrink-0 border-t lg:border-t-0 lg:border-l border-tborder bg-tsurf2/40 p-7 md:p-9 flex flex-col justify-center gap-6",children:[u.jsxs("div",{children:[u.jsx("div",{className:"text-3xl font-semibold text-t1",children:"#2"}),u.jsxs("div",{className:"text-sm text-t3",children:["on ",u.jsx("a",{href:"https://appworld.dev/",target:"_blank",rel:"noopener noreferrer",className:"text-indigo-600 hover:underline",children:"AppWorld"})," — 750 tasks, 457 APIs"]})]}),u.jsxs("div",{children:[u.jsx("div",{className:"text-3xl font-semibold text-t1",children:"#1"}),u.jsxs("div",{className:"text-sm text-t3",children:["on ",u.jsx("a",{href:"https://webarena.dev/",target:"_blank",rel:"noopener noreferrer",className:"text-indigo-600 hover:underline",children:"WebArena"})," — Feb–Sep 2025"]})]}),u.jsxs("div",{children:[u.jsx("div",{className:"text-3xl font-semibold text-t1",children:l.length}),u.jsx("div",{className:"text-sm text-t3",children:"working example apps"})]})]})]})}),u.jsx(vm,{useCases:l,activeBucket:s,onSelectBucket:a}),u.jsxs("div",{className:"flex flex-col gap-4 mb-6",children:[u.jsxs("div",{className:"flex flex-wrap gap-3 items-center",children:[u.jsx("input",{type:"text",placeholder:"Search use cases...",value:e,onChange:m=>t(m.target.value),className:"px-4 py-2 bg-tsurf border border-tborder rounded-xl text-base text-t1 placeholder-t4 focus:outline-none focus:border-indigo-500 w-72"}),(e||n!=="all"||s||o!=="showcase")&&u.jsx("button",{onClick:()=>{t(""),r("all"),a(null),i("showcase")},className:"px-4 py-2 text-sm font-medium text-t3 hover:text-t1 bg-tsurf2 border border-tborder rounded-xl transition-colors",children:"Clear all"})]}),u.jsx(um,{value:o,onChange:i}),u.jsx(dm,{value:n,onChange:r})]}),u.jsx(ym,{useCases:l,...c}),u.jsxs("p",{className:"mt-5 text-sm text-t4",children:[u.jsx("span",{className:"text-amber-500",children:"✦"})," Showcase rows open architecture, run instructions, and how CUGA powers them · Additional apps open their source on GitHub."]})]})}function ud(e){return`https://cuga-apps-mcp-${e}.${ad}.${od}.codeengine.appdomain.cloud/mcp`}const cd=`https://cuga-apps-mcp-tool-explorer.${ad}.${od}.codeengine.appdomain.cloud`,Nl=[{id:"web",name:"Web",port:29100,blurb:"Search the live web, fetch pages, read feeds, pull video transcripts.",sources:["Tavily","BeautifulSoup","RSS / feedparser","YouTube"],tools:["web_search","fetch_webpage","fetch_webpage_links","fetch_feed","search_feeds","get_youtube_video_info","get_youtube_transcript"],accent:"blue",examples:["What are the latest developments in fusion energy?","Summarize https://openai.com/blog and list the key takeaways"]},{id:"knowledge",name:"Knowledge",port:29101,blurb:"Encyclopedic + academic lookup across Wikipedia, arXiv and Semantic Scholar.",sources:["Wikipedia","arXiv","Semantic Scholar"],tools:["search_wikipedia","get_wikipedia_article","get_article_summary","get_article_sections","get_related_articles","search_arxiv","get_arxiv_paper","search_semantic_scholar","get_paper_references"],accent:"violet",examples:["Explain how transformer attention works","Find recent arXiv papers on retrieval-augmented generation"]},{id:"geo",name:"Geo",port:29102,blurb:"Geocoding, points of interest, trails and weather for any location.",sources:["Nominatim","Overpass","OpenTripMap","wttr.in"],tools:["geocode","find_hikes","search_attractions","get_weather"],accent:"emerald",examples:["Find hiking trails near Boulder, Colorado","What's the weather and top attractions in Kyoto?"]},{id:"finance",name:"Finance",port:29103,blurb:"Live crypto and equity quotes.",sources:["CoinGecko","Alpha Vantage"],tools:["get_crypto_price","get_stock_quote"],accent:"amber",examples:["What's the price of NVDA and Bitcoin?","Compare BTC and ETH — which is up more today?"]},{id:"code",name:"Code",port:29104,blurb:"Static analysis of source — syntax checks, metrics, language detection.",sources:["Python stdlib (ast)"],tools:["check_python_syntax","extract_code_metrics","detect_language"],accent:"cyan",examples:["Check this Python snippet for syntax errors","What language is this source file and how complex is it?"]},{id:"local",name:"Local",port:29105,blurb:"Host telemetry and on-box audio transcription.",sources:["psutil","faster-whisper"],tools:["get_system_metrics","get_system_metrics_with_alerts","list_top_processes","check_disk_usage","find_large_files","get_service_status","transcribe_audio"],accent:"rose",examples:["What's using the most CPU right now?","How much disk space is left and what are the largest files?"]},{id:"text",name:"Text",port:29106,blurb:"Document extraction, token counting and chunking for RAG pipelines.",sources:["docling","tiktoken"],tools:["extract_text","extract_text_from_bytes","chunk_text","count_tokens"],accent:"pink",examples:["Extract the text from this PDF and chunk it for RAG","How many tokens is this document?"]}];function Wr({code:e,language:t="bash"}){const[n,r]=k.useState(!1),s=()=>{navigator.clipboard.writeText(e),r(!0),setTimeout(()=>r(!1),1500)};return u.jsxs("div",{className:"relative group bg-[#161616] border border-[#393939] overflow-hidden",children:[u.jsxs("div",{className:"flex items-center justify-between px-4 py-2 border-b border-[#393939] bg-[#262626]",children:[u.jsx("span",{className:"text-xs text-[#8d8d8d] font-mono",children:t}),u.jsx("button",{onClick:s,className:"text-xs text-[#8d8d8d] hover:text-[#f4f4f4] transition-colors px-2 py-0.5 hover:bg-[#393939]",children:n?"✓ copied":"copy"})]}),u.jsx("pre",{className:"p-4 overflow-x-auto text-[#f4f4f4] text-xs leading-relaxed whitespace-pre",children:e})]})}function _m(e){const t=e.tools.slice(0,4).join(", ")+(e.tools.length>4?", …":"");return`# pip install langchain-mcp-adapters from langchain_mcp_adapters.client import MultiServerMCPClient client = MultiServerMCPClient({ "${e.id}": { "transport": "streamable_http", "url": "${ud(e.id)}", }, }) tools = await client.get_tools() # → ${t}`}const xm={blue:{bar:"bg-indigo-600",text:"text-indigo-600",chip:"bg-indigo-500/10 text-indigo-600 border-indigo-500/25"},emerald:{bar:"bg-emerald-500",text:"text-emerald-600",chip:"bg-emerald-500/10 text-emerald-600 border-emerald-500/25"},amber:{bar:"bg-amber-500",text:"text-amber-600",chip:"bg-amber-500/10 text-amber-600 border-amber-500/25"},pink:{bar:"bg-pink-500",text:"text-pink-600",chip:"bg-pink-500/10 text-pink-600 border-pink-500/25"},cyan:{bar:"bg-sky-600",text:"text-sky-700",chip:"bg-sky-500/10 text-sky-700 border-sky-500/30"},violet:{bar:"bg-violet-500",text:"text-violet-600",chip:"bg-violet-500/10 text-violet-600 border-violet-500/25"},rose:{bar:"bg-rose-500",text:"text-rose-600",chip:"bg-rose-500/10 text-rose-600 border-rose-500/25"}};function km({s:e}){const t=xm[e.accent],n=ud(e.id);return u.jsxs("div",{id:`mcp-${e.id}`,className:"bg-tsurf border border-tborder flex flex-col overflow-hidden scroll-mt-24",children:[u.jsxs("div",{className:"flex items-stretch",children:[u.jsx("div",{className:`w-1 ${t.bar}`}),u.jsxs("div",{className:"flex-1 p-5",children:[u.jsxs("div",{className:"flex items-center gap-2.5 mb-1",children:[u.jsx("h3",{className:"text-lg font-semibold text-t1",children:e.name}),u.jsxs("span",{className:"font-mono text-xs text-t4",children:["mcp-",e.id]}),u.jsxs("span",{className:"ml-auto font-mono text-xs text-t4",children:[":",e.port]})]}),u.jsx("p",{className:"text-sm text-t2 leading-relaxed mb-4",children:e.blurb}),u.jsx("div",{className:"text-[11px] font-semibold uppercase tracking-wider text-t4 mb-1.5",children:"Sources"}),u.jsx("div",{className:"flex flex-wrap gap-1.5 mb-4",children:e.sources.map(r=>u.jsx("span",{className:`text-xs px-2 py-0.5 border ${t.chip}`,children:r},r))}),u.jsxs("div",{className:"text-[11px] font-semibold uppercase tracking-wider text-t4 mb-1.5",children:[e.tools.length," tools"]}),u.jsx("div",{className:"flex flex-wrap gap-1.5",children:e.tools.map(r=>u.jsx("span",{className:"text-xs font-mono px-2 py-0.5 bg-tsurf2 text-t3 border border-tborder",children:r},r))}),e.examples.length>0&&u.jsxs(u.Fragment,{children:[u.jsx("div",{className:"text-[11px] font-semibold uppercase tracking-wider text-t4 mt-4 mb-1.5",children:"Try asking"}),u.jsx("div",{className:"flex flex-col gap-1.5",children:e.examples.map(r=>u.jsxs("span",{className:`text-xs px-2 py-1 border ${t.chip} italic`,children:["“",r,"”"]},r))})]}),u.jsx("div",{className:"text-[11px] font-semibold uppercase tracking-wider text-t4 mt-4 mb-1.5",children:"Use it"}),u.jsx(Wr,{code:_m(e),language:"python"})]})]}),u.jsxs("div",{className:"mt-auto border-t border-tborder px-5 py-3 bg-tsurf2/40 flex items-center justify-between gap-3",children:[u.jsx("code",{className:"text-xs text-t3 font-mono truncate",title:n,children:n.replace("https://","")}),u.jsxs("div",{className:"flex items-center gap-3 shrink-0",children:[u.jsx("a",{href:cd,target:"_blank",rel:"noopener noreferrer",title:`Browse & invoke mcp-${e.id} tools in the MCP Tool Explorer`,className:"text-xs font-semibold text-t3 hover:text-t1 hover:underline whitespace-nowrap",children:"Tool Explorer ↗"}),u.jsx("a",{href:n,target:"_blank",rel:"noopener noreferrer",className:`text-xs font-semibold ${t.text} hover:underline whitespace-nowrap`,children:"Open endpoint ↗"})]})]})]})}function bm(){const e=Nl.reduce((n,r)=>n+r.tools.length,0),t=Ut();return k.useEffect(()=>{if(!t.hash)return;const n=document.getElementById(t.hash.slice(1));n&&requestAnimationFrame(()=>n.scrollIntoView({behavior:"smooth",block:"start"}))},[t.hash]),u.jsxs("div",{className:"p-6 md:p-8 max-w-screen-2xl mx-auto",children:[u.jsxs("div",{className:"mb-8",children:[u.jsxs("div",{className:"inline-flex items-center gap-2 mb-3",children:[u.jsx("span",{className:"text-xs font-semibold uppercase tracking-wider text-indigo-600 bg-indigo-500/10 border border-indigo-500/25 px-2 py-0.5",children:"Code Engine"}),u.jsx("span",{className:"text-xs text-t4",children:"IBM Cloud · us-east"})]}),u.jsx("h2",{className:"text-3xl md:text-4xl font-semibold text-t1 mb-2 tracking-tight",children:"MCP Servers"}),u.jsxs("p",{className:"text-base text-t2 max-w-3xl leading-relaxed",children:["Every shared tool primitive lives in its own ",u.jsx("strong",{className:"text-t1",children:"MCP server"})," — apps connect over streamable HTTP through a LangChain↔MCP bridge, so a REST API, a custom server, and a Python function all bind the same way. Seven servers are hosted on IBM Cloud Code Engine, exposing ",u.jsxs("strong",{className:"text-t1",children:[e," tools"]})," across web, knowledge, geo, finance, code, host and text domains."]})]}),u.jsxs("a",{href:cd,target:"_blank",rel:"noopener noreferrer",className:"group flex items-center gap-4 mb-8 p-4 bg-tsurf border border-tborder hover:border-indigo-500 transition-colors",children:[u.jsx("div",{className:"w-10 h-10 bg-indigo-600 text-white flex items-center justify-center text-lg shrink-0",children:"⛭"}),u.jsxs("div",{className:"flex-1",children:[u.jsx("div",{className:"text-sm font-semibold text-t1",children:"MCP Tool Explorer"}),u.jsx("div",{className:"text-sm text-t3",children:"Browse and invoke every tool across all seven servers from one UI."})]}),u.jsx("span",{className:"text-sm font-semibold text-indigo-600 group-hover:underline whitespace-nowrap",children:"Open ↗"})]}),u.jsx("div",{className:"grid grid-cols-1 lg:grid-cols-2 gap-4",children:Nl.map(n=>u.jsx(km,{s:n},n.id))}),u.jsxs("p",{className:"mt-6 text-sm text-t4",children:["Hosted at ",u.jsx("code",{className:"font-mono text-t3",children:"cuga-apps-mcp-.…codeengine.appdomain.cloud/mcp"}),". Run locally on ports 29100–29106 — see ",u.jsx("code",{className:"font-mono text-t3",children:"mcp_servers/README.md"}),"."]})]})}const Sm={green:"bg-green-500/10 text-green-600 border border-green-500/30",yellow:"bg-yellow-500/10 text-yellow-600 border border-yellow-500/30",red:"bg-red-500/10 text-red-600 border border-red-500/30",blue:"bg-blue-500/10 text-blue-600 border border-blue-500/30",purple:"bg-purple-500/10 text-purple-600 border border-purple-500/30",orange:"bg-orange-500/10 text-orange-600 border border-orange-500/30",pink:"bg-pink-500/10 text-pink-600 border border-pink-500/30",cyan:"bg-sky-500/10 text-sky-700 border border-sky-500/30",indigo:"bg-indigo-500/10 text-indigo-600 border border-indigo-500/30",gray:"bg-tsurf2 text-t3 border border-tborder"};function Rl({label:e,color:t="gray",small:n=!1}){return u.jsx("span",{className:`inline-flex items-center rounded font-mono ${n?"text-xs px-1.5 py-0.5":"text-xs px-2 py-0.5"} ${Sm[t]}`,children:e})}const Ol={"event-driven":{label:"Event-driven",cls:"bg-amber-900/40 text-amber-300 border border-amber-700/50"},"document-intelligence":{label:"Document Intelligence",cls:"bg-cyan-900/40 text-cyan-300 border border-cyan-700/50"},"audio-video":{label:"Audio / Video",cls:"bg-violet-900/40 text-violet-300 border border-violet-700/50"},other:{label:"Other",cls:"bg-gray-800/40 text-gray-300 border border-gray-600/50"}};function Cm({text:e}){const[t,n]=k.useState(!1),r=()=>{navigator.clipboard.writeText(e).then(()=>{n(!0),setTimeout(()=>n(!1),1800)})};return u.jsx("button",{onClick:r,title:"Copy to clipboard",className:"flex-shrink-0 px-2 py-0.5 text-xs rounded transition-colors border border-gray-700 hover:border-indigo-500 text-gray-500 hover:text-indigo-400",children:t?"✓ copied":"copy"})}function Pm(){var c,m,f,g;const{id:e}=gf(),t=Os(),n=sd.find(v=>v.id===e);if(!n)return u.jsxs("div",{className:"p-8 text-center",children:[u.jsx("p",{className:"text-gray-500",children:"Use case not found."}),u.jsx("button",{onClick:()=>t("/"),className:"mt-4 text-indigo-400 hover:text-indigo-300 text-sm",children:"← Back to use cases"})]});const r=js[n.category],s=em[n.status],a=[...n.howToRun.setup,"","# Environment variables",...n.howToRun.envVars.map(v=>`export ${v}=`)].join(` `),o=n.howToRun.command,i=n.howToRun.envVars.map(v=>`${v}=`).join(` `),l=ui(n);return u.jsxs("div",{className:"p-6 max-w-5xl mx-auto",children:[u.jsx("button",{onClick:()=>t("/"),className:"text-gray-500 hover:text-gray-300 text-sm mb-5 flex items-center gap-1.5 transition-colors",children:"← All use cases"}),u.jsxs("div",{className:"mb-8",children:[u.jsxs("div",{className:"flex items-start justify-between gap-4 flex-wrap",children:[u.jsxs("div",{children:[u.jsx("h2",{className:"text-2xl font-semibold text-white",children:n.name}),u.jsx("p",{className:"text-gray-400 mt-1",children:n.tagline})]}),u.jsxs("div",{className:"flex items-center gap-2 flex-shrink-0 flex-wrap",children:[u.jsx(Rl,{label:r.label,color:r.color}),u.jsx("span",{className:`text-xs px-2 py-0.5 rounded font-medium ${Ol[n.type].cls}`,children:Ol[n.type].label}),u.jsx(Rl,{label:s.label,color:s.color}),l?u.jsx("a",{href:l,target:"_blank",rel:"noopener noreferrer",className:"px-4 py-1.5 bg-indigo-600 hover:bg-indigo-500 text-white text-sm font-medium rounded-lg transition-colors",children:"Launch App →"}):u.jsx("button",{disabled:!0,className:"px-4 py-1.5 bg-gray-800 text-gray-500 text-sm font-medium rounded-lg cursor-not-allowed border border-gray-700",title:"Interactive app coming soon",children:"Launch App (soon)"})]})]}),u.jsx("p",{className:"text-gray-300 text-sm mt-4 leading-relaxed",children:n.description})]}),u.jsxs("div",{className:"grid grid-cols-2 gap-4 mb-8",children:[u.jsxs("div",{className:"bg-gray-900 border border-gray-800 rounded-xl p-4",children:[u.jsx("h3",{className:"text-xs font-semibold text-gray-500 uppercase tracking-wider mb-3",children:"Channels"}),u.jsxs("div",{className:"flex flex-wrap gap-1.5",children:[n.channels.map(v=>u.jsx("span",{className:"text-xs font-mono px-2 py-1 bg-indigo-900/30 text-indigo-300 border border-indigo-800/40 rounded",children:v},v)),n.channels.length===0&&u.jsx("span",{className:"text-xs text-gray-600",children:"—"})]})]}),u.jsxs("div",{className:"bg-gray-900 border border-gray-800 rounded-xl p-4",children:[u.jsx("h3",{className:"text-xs font-semibold text-gray-500 uppercase tracking-wider mb-3",children:"Tool Factories"}),u.jsxs("div",{className:"flex flex-wrap gap-1.5",children:[n.tools.map(v=>u.jsx("span",{className:"text-xs font-mono px-2 py-1 bg-purple-900/30 text-purple-300 border border-purple-800/40 rounded",children:v},v)),n.tools.length===0&&u.jsx("span",{className:"text-xs text-gray-600",children:"No additional tools"})]})]})]}),(c=n.mcpUsage)!=null&&c.length||(m=n.inlineTools)!=null&&m.length?u.jsxs("section",{className:"mb-8 bg-gray-900 border border-gray-800 rounded-xl p-4",children:[u.jsx("h3",{className:"text-xs font-semibold text-gray-500 uppercase tracking-wider mb-3",children:"MCP Servers & Tools"}),u.jsxs("div",{className:"flex flex-col gap-3",children:[(f=n.mcpUsage)==null?void 0:f.map(v=>u.jsxs("div",{className:"flex flex-wrap items-center gap-2",children:[u.jsxs("span",{className:"text-xs font-semibold uppercase tracking-wider px-2 py-1 rounded bg-emerald-900/30 text-emerald-300 border border-emerald-800/40",children:["mcp-",v.server]}),u.jsx("span",{className:"text-gray-600",children:"→"}),v.tools.map(w=>u.jsx("span",{className:"text-xs font-mono px-2 py-1 bg-emerald-950/30 text-emerald-200 border border-emerald-900/40 rounded",children:w},w))]},v.server)),(((g=n.inlineTools)==null?void 0:g.length)??0)>0&&u.jsxs("div",{className:"flex flex-wrap items-center gap-2",children:[u.jsx("span",{className:"text-xs font-semibold uppercase tracking-wider px-2 py-1 rounded bg-amber-900/30 text-amber-300 border border-amber-800/40",children:"inline"}),u.jsx("span",{className:"text-gray-600",children:"→"}),n.inlineTools.map(v=>u.jsx("span",{className:"text-xs font-mono px-2 py-1 bg-amber-950/30 text-amber-200 border border-amber-900/40 rounded",children:v},v))]})]}),u.jsxs("p",{className:"text-xs text-gray-500 mt-3",children:[u.jsx("span",{className:"text-emerald-400",children:"mcp-*"})," tools are served by shared MCP servers (browse them at ",u.jsx("a",{className:"underline",href:"http://localhost:28900",target:"_blank",rel:"noreferrer",children:"/tool-explorer"}),").",u.jsx("span",{className:"text-amber-400",children:" inline"})," tools live in the app process — they touch app-state (DB, sessions, vendor auth) that can't be shared."]})]}):null,u.jsxs("section",{className:"mb-8",children:[u.jsx("h3",{className:"text-base font-semibold text-white mb-3",children:"Architecture"}),u.jsx("p",{className:"text-gray-300 text-sm leading-relaxed mb-4",children:n.architecture}),u.jsx("div",{className:"bg-gray-950 border border-gray-800 rounded-xl p-4",children:u.jsx("pre",{className:"text-xs text-gray-400 font-mono leading-relaxed whitespace-pre",children:n.diagram})})]}),u.jsxs("section",{className:"mb-8",children:[u.jsx("h3",{className:"text-base font-semibold text-white mb-3",children:"What CUGA enables"}),u.jsx("ul",{className:"space-y-2",children:n.cugaContribution.map((v,w)=>u.jsxs("li",{className:"flex items-start gap-2.5 text-sm text-gray-300",children:[u.jsx("span",{className:"text-indigo-400 mt-0.5 flex-shrink-0",children:"▸"}),v]},w))})]}),n.examples&&n.examples.length>0&&u.jsxs("section",{className:"mb-8",children:[u.jsxs("div",{className:"flex items-center gap-3 mb-3",children:[u.jsx("h3",{className:"text-base font-semibold text-white",children:"Try these"}),l&&u.jsxs("span",{className:"text-xs text-gray-500",children:["— type into the app at"," ",u.jsx("a",{href:l,target:"_blank",rel:"noopener noreferrer",className:"text-indigo-400 hover:text-indigo-300 font-mono",children:l})]})]}),u.jsx("div",{className:"bg-gray-900 border border-gray-800 rounded-xl overflow-hidden divide-y divide-gray-800/60",children:n.examples.map((v,w)=>u.jsxs("div",{className:"flex items-start gap-3 px-4 py-2.5 group hover:bg-gray-800/30 transition-colors",children:[u.jsx("span",{className:"text-gray-600 text-xs font-mono mt-0.5 w-4 flex-shrink-0",children:w+1}),u.jsx("pre",{className:"flex-1 text-sm text-gray-300 font-mono whitespace-pre-wrap leading-relaxed min-w-0",children:v}),u.jsx("div",{className:"opacity-0 group-hover:opacity-100 transition-opacity mt-0.5",children:u.jsx(Cm,{text:v})})]},w))})]}),u.jsxs("section",{className:"mb-8",children:[u.jsx("h3",{className:"text-base font-semibold text-white mb-4",children:"How to run"}),u.jsxs("div",{className:"space-y-4",children:[u.jsxs("div",{children:[u.jsx("h4",{className:"text-xs font-semibold text-gray-500 uppercase tracking-wider mb-2",children:"1 — Setup & environment variables"}),u.jsx(Wr,{code:a,language:"bash"})]}),u.jsxs("div",{children:[u.jsx("h4",{className:"text-xs font-semibold text-gray-500 uppercase tracking-wider mb-2",children:"2 — Create a .env file"}),u.jsx(Wr,{code:i,language:".env"})]}),u.jsxs("div",{children:[u.jsx("h4",{className:"text-xs font-semibold text-gray-500 uppercase tracking-wider mb-2",children:"3 — Run"}),u.jsx(Wr,{code:o,language:"bash"})]})]})]}),n.demoPath&&u.jsxs("section",{className:"bg-gray-900 border border-gray-800 rounded-xl p-4 text-sm text-gray-400",children:[u.jsx("span",{className:"text-gray-600 text-xs font-semibold uppercase tracking-wider",children:"Source path"}),u.jsx("div",{className:"font-mono text-xs text-gray-400 mt-1",children:n.demoPath})]})]})}function Em(){return u.jsx(Zf,{children:u.jsxs(Rf,{children:[u.jsx(Nn,{path:"/",element:u.jsx(wm,{})}),u.jsx(Nn,{path:"/mcp-servers",element:u.jsx(bm,{})}),u.jsx(Nn,{path:"/use-case/:id",element:u.jsx(Pm,{})}),u.jsx(Nn,{path:"*",element:u.jsx(If,{to:"/",replace:!0})})]})})}ha.createRoot(document.getElementById("root")).render(u.jsx(ql.StrictMode,{children:u.jsx(zf,{children:u.jsx(Em,{})})}));