fluxy-bot 0.2.33 → 0.2.35

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ import{c as P,r as a,j as e,m as He,A as st,M as nt,a as rt,R as it}from"./globals-DR8AapV6.js";const lt=[["path",{d:"M5 12h14",key:"1ays0h"}],["path",{d:"m12 5 7 7-7 7",key:"xquz4c"}]],x=P("arrow-right",lt);const ot=[["path",{d:"M20 6 9 17l-5-5",key:"1gmf2c"}]],D=P("check",ot);const ct=[["path",{d:"m6 9 6 6 6-6",key:"qrunsl"}]],dt=P("chevron-down",ct);const ht=[["path",{d:"M11 14h10",key:"1w8e9d"}],["path",{d:"M16 4h2a2 2 0 0 1 2 2v1.344",key:"1e62lh"}],["path",{d:"m17 18 4-4-4-4",key:"z2g111"}],["path",{d:"M8 4H6a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h12a2 2 0 0 0 1.793-1.113",key:"bjbb7m"}],["rect",{x:"8",y:"2",width:"8",height:"4",rx:"1",key:"ublpy"}]],xt=P("clipboard-paste",ht);const mt=[["path",{d:"M15 3h6v6",key:"1q9fwt"}],["path",{d:"M10 14 21 3",key:"gplh6r"}],["path",{d:"M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6",key:"a6xqqp"}]],pt=P("external-link",mt);const ut=[["path",{d:"M21 12a9 9 0 1 1-6.219-8.56",key:"13zald"}]],H=P("loader-circle",ut);const ft=[["path",{d:"M3 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8",key:"v9h5vc"}],["path",{d:"M21 3v5h-5",key:"1q7to0"}],["path",{d:"M21 12a9 9 0 0 1-9 9 9.75 9.75 0 0 1-6.74-2.74L3 16",key:"3uifl3"}],["path",{d:"M8 16H3v5",key:"1cv678"}]],bt=P("refresh-cw",ft),gt=[{id:"anthropic",name:"Claude",subtitle:"by Anthropic",icon:"/icons/claude.png"},{id:"openai",name:"OpenAI Codex",subtitle:"ChatGPT Plus / Pro",icon:"/icons/codex.png"},{id:"ollama",name:"Ollama",subtitle:"Run locally",icon:null}],wt={anthropic:[{id:"claude-opus-4-6",label:"Opus 4.6"},{id:"claude-sonnet-4-6",label:"Sonnet 4.6"},{id:"claude-haiku-4-5-20251001",label:"Haiku 4.5"}],openai:[{id:"gpt-5.2-codex:medium",label:"GPT-5.2 Codex Medium"},{id:"gpt-5.2-codex:high",label:"GPT-5.2 Codex High"},{id:"gpt-5.2-codex:xhigh",label:"GPT-5.2 Codex Extra High"},{id:"gpt-5.3-codex:medium",label:"GPT-5.3 Codex Medium (Pro)"},{id:"gpt-5.3-codex:high",label:"GPT-5.3 Codex High (Pro)"},{id:"gpt-5.3-codex:xhigh",label:"GPT-5.3 Codex Extra High (Pro)"}],ollama:[{id:"llama3.2",label:"Llama 3.2"},{id:"mistral",label:"Mistral"},{id:"codellama",label:"Code Llama"},{id:"phi3",label:"Phi-3"}]},Le=6,Re=[{tier:"premium",label:h=>`${h}.fluxy.bot`,badge:"$5",badgeCls:"bg-[#AF27E3]/15 text-[#AF27E3] border-[#AF27E3]/20",highlight:!0},{tier:"at",label:h=>`${h}.at.fluxy.bot`,badge:"Free",badgeCls:"bg-emerald-500/10 text-emerald-400 border-emerald-500/20",highlight:!1}];function jt({models:h,value:r,onChange:K}){const[f,A]=a.useState(!1),n=a.useRef(null);a.useEffect(()=>{if(!f)return;const i=L=>{n.current&&!n.current.contains(L.target)&&A(!1)};return document.addEventListener("mousedown",i),()=>document.removeEventListener("mousedown",i)},[f]);const E=h.find(i=>i.id===r);return e.jsxs("div",{className:"relative",ref:n,children:[e.jsxs("button",{type:"button",onClick:()=>A(i=>!i),className:"w-full flex items-center justify-between bg-white/[0.03] border border-white/[0.08] text-white rounded-xl px-4 py-2.5 text-[13px] outline-none hover:border-white/15 focus:border-[#AF27E3]/30 transition-colors",children:[e.jsx("span",{className:E?"text-white":"text-white/20",children:E?E.label:"Choose a model..."}),e.jsx(dt,{className:`h-4 w-4 text-white/30 transition-transform ${f?"rotate-180":""}`})]}),f&&e.jsx("div",{className:"absolute left-0 right-0 top-full mt-1 bg-[#222] border border-white/[0.08] rounded-xl shadow-xl py-1 z-10 max-h-48 overflow-y-auto",children:h.map(i=>e.jsx("button",{onClick:()=>{K(i.id),A(!1)},className:`w-full text-left px-4 py-2 text-[13px] transition-colors ${r===i.id?"text-[#AF27E3] bg-[#AF27E3]/10":"text-white/70 hover:bg-white/[0.04] hover:text-white"}`,children:i.label},i.id))})]})}function yt({onComplete:h}){const[r,K]=a.useState(0),[f,A]=a.useState(""),[n,E]=a.useState("anthropic"),[i,L]=a.useState(""),[le,oe]=a.useState(!1),[G,R]=a.useState({anthropic:"idle",openai:"idle",ollama:"connected"}),[Ue,ce]=a.useState(!1),[V,Z]=a.useState(""),[de,he]=a.useState(!1),[xe,m]=a.useState(),[ee,me]=a.useState(!1),[O,y]=a.useState(!1),[pe,z]=a.useState(),[ue,_e]=a.useState(""),[o,B]=a.useState(""),[b,d]=a.useState(null),[fe,N]=a.useState(""),[q,te]=a.useState({}),[g,J]=a.useState("at"),[be,ge]=a.useState(!1),[l,T]=a.useState(!1),[U,_]=a.useState(""),$=a.useRef(null),[p,we]=a.useState(null),[w,ae]=a.useState(!1),[je,ye]=a.useState(!1),[F,Ne]=a.useState(""),[v,$e]=a.useState(""),[se,We]=a.useState(""),[De,ve]=a.useState(!1),[M,Ke]=a.useState(!1),[ne,Ge]=a.useState(""),[Ce,Y]=a.useState(""),[Q,ke]=a.useState(!1),[Se,Pe]=a.useState(!1),[j,Ae]=a.useState(!1),[u,Ee]=a.useState(""),re=a.useRef(!1),C=G[n]==="connected";a.useEffect(()=>{fetch("/api/onboard/status").then(t=>t.json()).then(t=>{t.userName&&A(t.userName),t.handle&&(B(t.handle.username),J(t.handle.tier||"at"),we({username:t.handle.username,tier:t.handle.tier,url:t.handle.url}),T(!0),_(t.handle.url)),t.portalUser&&Ne(t.portalUser),t.portalConfigured&&Ke(!0),t.provider&&E(t.provider),t.model&&L(t.model),t.whisperEnabled&&(Ae(!0),Ee(t.whisperKey||"")),re.current=!0}).catch(()=>{re.current=!0})},[]),a.useEffect(()=>{n!=="anthropic"||G.anthropic==="connected"||fetch("/api/auth/claude/status").then(t=>t.json()).then(t=>{t.authenticated&&R(s=>({...s,anthropic:"connected"}))}).catch(()=>{})},[n]),a.useEffect(()=>{n!=="openai"||G.openai==="connected"||fetch("/api/auth/codex/status").then(t=>t.json()).then(t=>{t.authenticated&&R(s=>({...s,openai:"connected"}))}).catch(()=>{})},[n]),a.useEffect(()=>{if(!O)return;const t=setInterval(async()=>{try{(await(await fetch("/api/auth/codex/status")).json()).authenticated&&(y(!1),R(I=>({...I,openai:"connected"})))}catch{}},2e3);return()=>clearInterval(t)},[O]),a.useEffect(()=>{if($.current&&clearTimeout($.current),!re.current||p&&l&&o===p.username)return;d(null),N(""),te({}),T(!1),_("");const t=o.trim();if(t){if(t.length<3){d("invalid"),N("At least 3 characters");return}return d("checking"),$.current=setTimeout(async()=>{try{const c=await(await fetch(`/api/handle/check/${encodeURIComponent(t)}`)).json();if(!c.valid)d("invalid"),N(c.error);else{const I={};for(const X of c.handles)I[X.tier]=X.available;te(I),d("ready");const Ie=Re.find(X=>I[X.tier]);Ie&&!I[g]&&J(Ie.tier)}}catch{d(null)}},400),()=>{$.current&&clearTimeout($.current)}}},[o]);const Ve=t=>{B(t.toLowerCase().replace(/[^a-z0-9-]/g,""))},ze=async()=>{if(!(!o||b!=="ready"||!q[g])){ge(!0);try{const s=await(await fetch("/api/handle/register",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({username:o,tier:g})})).json();s.ok?(T(!0),_(s.url)):(N(s.error||"Registration failed"),d("invalid"))}catch{N("Could not reach server"),d("invalid")}finally{ge(!1)}}},Be=async()=>{if(!(!o||b!=="ready"||!q[g])){ye(!0);try{const s=await(await fetch("/api/handle/change",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({username:o,tier:g})})).json();s.ok?(T(!0),_(s.url),we({username:o,tier:g,url:s.url}),ae(!1)):(N(s.error||"Handle change failed"),d("invalid"))}catch{N("Could not reach server"),d("invalid")}finally{ye(!1)}}},qe=t=>{n==="openai"&&t!=="openai"&&O&&(fetch("/api/auth/codex/cancel",{method:"POST"}),y(!1)),E(t),L(""),ce(!1),Z(""),m(void 0),y(!1),z(void 0)},Je=async()=>{m(void 0);try{const s=await(await fetch("/api/auth/claude/start",{method:"POST"})).json();s.success&&s.authUrl?(window.open(s.authUrl,"_blank"),ce(!0)):m(s.error||"Failed to start authentication")}catch(t){m(t.message)}},Oe=async()=>{if(V.trim()){he(!0),m(void 0);try{const s=await(await fetch("/api/auth/claude/exchange",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({code:V.trim()})})).json();s.success?R(c=>({...c,anthropic:"connected"})):m(s.error||"Code exchange failed")}catch(t){m(t.message)}finally{he(!1)}}},Ye=async()=>{try{const t=await navigator.clipboard.readText();t&&Z(t.trim())}catch{}},Qe=async()=>{me(!0),m(void 0);try{(await(await fetch("/api/auth/claude/status")).json()).authenticated?R(c=>({...c,anthropic:"connected"})):m("No active session found. Please authenticate first.")}catch{}finally{me(!1)}},Xe=async()=>{y(!0),z(void 0);try{const s=await(await fetch("/api/auth/codex/start",{method:"POST"})).json();s.success&&s.authUrl?window.open(s.authUrl,"_blank"):(y(!1),z(s.error||"Failed to start authentication"))}catch(t){y(!1),z(t.message)}},Ze=()=>{y(!1),fetch("/api/auth/codex/cancel",{method:"POST"})},Te=v===se,Fe=F.trim().length>=3&&v.length>=6&&Te,et=M?F.trim().length>=3&&(v.length===0||Q&&Fe):Fe,W=(()=>{switch(r){case 0:return!0;case 1:return f.trim().length>0;case 2:return l;case 3:return et;case 4:return!!(n&&i&&C);case 5:return!0;default:return!1}})(),k=()=>{W&&r<Le-1&&K(t=>t+1)},tt=()=>{r>0&&K(t=>t-1)},ie=t=>{t.key==="Enter"&&W&&k()},at=async()=>{oe(!0);try{await fetch("/api/onboard",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({userName:f.trim(),agentName:o.trim()||"Fluxy",provider:n,model:i,apiKey:"",baseUrl:n==="ollama"&&ue||void 0,whisperEnabled:j,whisperKey:j?u:"",portalUser:F.trim(),portalPass:v})}),h()}catch(t){console.error("Onboard failed:",t),oe(!1)}},S="w-full bg-white/[0.05] border border-white/[0.08] text-white rounded-xl px-4 py-3 text-base outline-none input-glow placeholder:text-white/20 transition-all",Me="w-full bg-white/[0.03] border border-white/[0.08] text-white rounded-xl px-4 py-2.5 text-[13px] outline-none input-glow placeholder:text-white/20 transition-all";return e.jsxs("div",{className:"fixed inset-0 z-[200] flex items-center justify-center p-4",children:[e.jsx("div",{className:"absolute inset-0 bg-black/85 backdrop-blur-md"}),e.jsxs(He.div,{initial:{opacity:0,scale:.95},animate:{opacity:1,scale:1},transition:{duration:.3},className:"relative w-full max-w-[480px] bg-[#181818] border border-white/[0.06] rounded-[24px] shadow-2xl overflow-hidden",children:[e.jsx("div",{className:"flex justify-center gap-2 pt-6",children:Array.from({length:Le},(t,s)=>e.jsx("div",{className:`h-1.5 rounded-full transition-all duration-300 ${s===r?"w-7 bg-gradient-brand":s<r?"w-1.5 bg-gradient-brand opacity-60":"w-1.5 bg-white/10"}`},s))}),e.jsx(st,{mode:"wait",children:e.jsxs(He.div,{initial:{opacity:0,x:30},animate:{opacity:1,x:0},exit:{opacity:0,x:-30},transition:{duration:.2,ease:"easeOut"},className:"px-8 pt-6 pb-8",children:[r===0&&e.jsxs("div",{className:"flex flex-col items-center text-center",children:[e.jsx("video",{src:"/fluxy_say_hi.webm",autoPlay:!0,loop:!0,muted:!0,playsInline:!0,className:"h-[180px] mb-4"}),e.jsx("h1",{className:"text-2xl font-bold text-white tracking-tight",children:"Welcome to Fluxy"}),e.jsx("p",{className:"text-white/40 text-[14px] mt-2 leading-relaxed max-w-[320px]",children:"Let's set up your AI assistant in just a few steps."}),e.jsxs("button",{onClick:k,className:"mt-6 px-7 py-3 bg-gradient-brand hover:opacity-90 text-white text-[14px] font-semibold rounded-full transition-colors flex items-center gap-2",children:["Get Started",e.jsx(x,{className:"h-4 w-4"})]})]}),r===1&&e.jsxs("div",{children:[e.jsx("h1",{className:"text-xl font-bold text-white tracking-tight",children:"What's your name?"}),e.jsx("p",{className:"text-white/40 text-[13px] mt-1.5 leading-relaxed",children:"This is how your agent will address you."}),e.jsxs("div",{className:"mt-5 flex items-center gap-3",children:[e.jsx("input",{type:"text",value:f,onChange:t=>A(t.target.value),onKeyDown:ie,placeholder:"Enter your name",autoFocus:!0,className:S+" flex-1"}),e.jsx("button",{onClick:k,disabled:!W,className:"shrink-0 h-12 w-12 flex items-center justify-center rounded-full bg-gradient-brand hover:opacity-90 text-white transition-colors disabled:opacity-30",children:e.jsx(x,{className:"h-5 w-5"})})]})]}),r===2&&e.jsxs("div",{children:[e.jsx("h1",{className:"text-xl font-bold text-white tracking-tight",children:"Name your bot"}),e.jsx("p",{className:"text-white/40 text-[13px] mt-1.5 leading-relaxed",children:"This is your bot's name and permanent handle — access it from anywhere."}),p&&l&&!w&&e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:"mt-4 bg-emerald-500/8 border border-emerald-500/15 rounded-xl px-4 py-3",children:[e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx(D,{className:"h-4 w-4 text-emerald-400"}),e.jsx("p",{className:"text-emerald-400/90 text-[13px] font-medium",children:"Current handle"})]}),e.jsx("p",{className:"text-emerald-400/60 text-[12px] mt-1 font-mono",children:U})]}),e.jsxs("div",{className:"flex gap-2 mt-4",children:[e.jsxs("button",{onClick:k,className:"flex-1 py-3 bg-gradient-brand hover:opacity-90 text-white text-[14px] font-semibold rounded-full transition-colors flex items-center justify-center gap-2",children:["Continue",e.jsx(x,{className:"h-4 w-4"})]}),e.jsx("button",{onClick:()=>{ae(!0),T(!1),B(""),d(null),te({})},className:"px-5 py-3 bg-white/[0.04] hover:bg-white/[0.08] border border-white/[0.08] text-white/60 text-[13px] font-medium rounded-full transition-colors",children:"Change"})]})]}),w&&!l&&e.jsxs("div",{className:"mt-4 bg-amber-500/8 border border-amber-500/20 rounded-xl px-4 py-3",children:[e.jsx("p",{className:"text-amber-400/90 text-[13px] font-medium",children:"Changing your handle"}),e.jsxs("p",{className:"text-amber-400/60 text-[12px] mt-1",children:["Your current handle ",e.jsx("span",{className:"font-mono",children:p?.url})," will be released and become available for others."]})]}),(!p||w||!l)&&!(p&&l&&!w)&&e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:"relative mt-5",children:[e.jsx("input",{type:"text",value:o,onChange:t=>Ve(t.target.value),maxLength:30,placeholder:"your-bot-name",spellCheck:!1,autoCapitalize:"none",autoCorrect:"off",autoFocus:!0,disabled:l,className:S+" pr-10 font-mono"+(l?" opacity-50":"")}),b&&o.length>0&&!l&&e.jsxs("div",{className:"absolute right-4 top-1/2 -translate-y-1/2",children:[b==="checking"&&e.jsx("div",{className:"w-5 h-5 border-2 border-white/10 border-t-[#04D1FE] rounded-full animate-spin"}),b==="invalid"&&e.jsx("div",{className:"w-6 h-6 rounded-full bg-amber-500/15 flex items-center justify-center",children:e.jsx("svg",{className:"w-3.5 h-3.5 text-amber-400",fill:"none",viewBox:"0 0 24 24",stroke:"currentColor",strokeWidth:3,children:e.jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M12 9v3m0 4h.01"})})})]})]}),b==="invalid"&&fe&&e.jsx("p",{className:"text-amber-400 text-[12px] mt-2",children:fe}),b==="ready"&&o.length>0&&!l&&e.jsx("div",{className:"space-y-2 mt-4",children:Re.map(t=>{const c=q[t.tier]===!1;return e.jsxs("button",{onClick:()=>!c&&J(t.tier),disabled:c,className:`w-full flex items-center justify-between px-4 py-3 rounded-xl border transition-all duration-200 text-left ${c?"border-white/[0.04] bg-transparent opacity-50 cursor-not-allowed":g===t.tier?t.highlight?"border-[#AF27E3]/40 bg-[#AF27E3]/[0.06]":"border-[#AF27E3]/30 bg-white/[0.04]":"border-white/[0.06] bg-transparent hover:border-white/10 hover:bg-white/[0.02]"}`,children:[e.jsx("span",{className:"font-mono text-[13px] text-white/70",children:t.label(o)}),c?e.jsx("span",{className:"text-[11px] font-medium px-2.5 py-0.5 rounded-full border bg-red-500/10 text-red-400 border-red-500/20",children:"Taken"}):e.jsx("span",{className:`text-[11px] font-medium px-2.5 py-0.5 rounded-full border ${t.badgeCls}`,children:t.badge})]},t.tier)})}),l&&e.jsxs("div",{className:"mt-4 bg-emerald-500/8 border border-emerald-500/15 rounded-xl px-4 py-3",children:[e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx(D,{className:"h-4 w-4 text-emerald-400"}),e.jsx("p",{className:"text-emerald-400/90 text-[13px] font-medium",children:"Handle claimed!"})]}),e.jsx("p",{className:"text-emerald-400/60 text-[12px] mt-1 font-mono",children:U})]}),b==="ready"&&q[g]&&o.length>0&&!l&&e.jsx("button",{onClick:w?Be:ze,disabled:be||je,className:"w-full mt-4 py-3 bg-gradient-brand hover:opacity-90 text-white text-[14px] font-semibold rounded-full transition-colors flex items-center justify-center gap-2 disabled:opacity-40",children:be||je?e.jsxs(e.Fragment,{children:[e.jsx(H,{className:"h-4 w-4 animate-spin"}),w?"Changing...":"Claiming..."]}):e.jsxs(e.Fragment,{children:[w?"Change Handle":"Claim & Continue",e.jsx(x,{className:"h-4 w-4"})]})}),l&&e.jsxs("button",{onClick:k,className:"w-full mt-4 py-3 bg-gradient-brand hover:opacity-90 text-white text-[14px] font-semibold rounded-full transition-colors flex items-center justify-center gap-2",children:["Continue",e.jsx(x,{className:"h-4 w-4"})]}),w&&!l&&e.jsx("button",{onClick:()=>{ae(!1),B(p.username),T(!0),_(p.url),J(p.tier),d(null)},className:"w-full mt-2 py-2 text-white/25 hover:text-white/40 text-[12px] transition-colors",children:"Cancel — keep current handle"})]})]}),r===3&&e.jsxs("div",{children:[e.jsx("h1",{className:"text-xl font-bold text-white tracking-tight",children:"Secure your portal"}),e.jsx("p",{className:"text-white/40 text-[13px] mt-1.5 leading-relaxed",children:"Create credentials to protect your dashboard. Anyone with your URL will need these to log in."}),l&&U&&e.jsxs("div",{className:"mt-4 flex items-center gap-2 bg-white/[0.03] border border-white/[0.06] rounded-xl px-4 py-3",children:[e.jsx("span",{className:"font-mono text-[13px] text-white/60 truncate flex-1",children:U}),e.jsx("button",{onClick:()=>{navigator.clipboard.writeText(U),ve(!0),setTimeout(()=>ve(!1),2e3)},className:"shrink-0 text-white/30 hover:text-white/60 transition-colors",children:De?e.jsx(D,{className:"h-4 w-4 text-emerald-400"}):e.jsxs("svg",{className:"h-4 w-4",fill:"none",viewBox:"0 0 24 24",stroke:"currentColor",strokeWidth:2,children:[e.jsx("rect",{x:"9",y:"9",width:"13",height:"13",rx:"2"}),e.jsx("path",{d:"M5 15H4a2 2 0 01-2-2V4a2 2 0 012-2h9a2 2 0 012 2v1"})]})})]}),M&&e.jsx("div",{className:"mt-4 bg-white/[0.02] border border-white/[0.06] rounded-xl px-4 py-2.5",children:e.jsx("p",{className:"text-white/40 text-[12px]",children:"Credentials already set. Leave password fields empty to keep your current password, or enter your current password to change it."})}),e.jsxs("div",{className:"mt-5",children:[e.jsx("label",{className:"text-[12px] text-white/40 font-medium mb-1.5 block",children:"Username"}),e.jsx("input",{type:"text",value:F,onChange:t=>Ne(t.target.value.replace(/\s/g,"").toLowerCase()),placeholder:"admin",autoFocus:!0,autoComplete:"username",className:S+" font-mono"}),F.length>0&&F.trim().length<3&&e.jsx("p",{className:"text-amber-400/70 text-[11px] mt-1",children:"At least 3 characters"})]}),M&&e.jsxs("div",{className:"mt-3",children:[e.jsx("label",{className:"text-[12px] text-white/40 font-medium mb-1.5 block",children:"Current password"}),e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx("input",{type:"password",value:ne,onChange:t=>{Ge(t.target.value),Y(""),ke(!1)},placeholder:"Enter current password to change it",autoComplete:"current-password",className:S+" flex-1"}),ne.length>0&&!Q&&e.jsx("button",{onClick:async()=>{Pe(!0),Y("");try{(await(await fetch("/api/portal/verify-password",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({password:ne})})).json()).valid?ke(!0):Y("Incorrect password")}catch{Y("Could not verify")}finally{Pe(!1)}},disabled:Se,className:"shrink-0 px-4 py-3 bg-white/[0.06] hover:bg-white/[0.1] text-white/60 text-[13px] font-medium rounded-xl transition-colors disabled:opacity-40",children:Se?e.jsx(H,{className:"h-4 w-4 animate-spin"}):"Verify"}),Q&&e.jsx("div",{className:"shrink-0 w-10 h-10 flex items-center justify-center",children:e.jsx(D,{className:"h-4 w-4 text-emerald-400"})})]}),Ce&&e.jsx("p",{className:"text-red-400/70 text-[11px] mt-1",children:Ce})]}),(!M||Q)&&e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:"mt-3",children:[e.jsx("label",{className:"text-[12px] text-white/40 font-medium mb-1.5 block",children:M?"New password":"Password"}),e.jsx("input",{type:"password",value:v,onChange:t=>$e(t.target.value),placeholder:"••••••••",autoComplete:"new-password",onKeyDown:ie,className:S}),v.length>0&&v.length<6&&e.jsx("p",{className:"text-amber-400/70 text-[11px] mt-1",children:"At least 6 characters"})]}),e.jsxs("div",{className:"mt-3",children:[e.jsx("label",{className:"text-[12px] text-white/40 font-medium mb-1.5 block",children:M?"Confirm new password":"Confirm password"}),e.jsx("input",{type:"password",value:se,onChange:t=>We(t.target.value),placeholder:"••••••••",autoComplete:"new-password",onKeyDown:ie,className:S}),se.length>0&&!Te&&e.jsx("p",{className:"text-red-400/70 text-[11px] mt-1",children:"Passwords don't match"})]})]}),e.jsxs("button",{onClick:k,disabled:!W,className:"w-full mt-5 py-3 bg-gradient-brand hover:opacity-90 text-white text-[14px] font-semibold rounded-full transition-colors flex items-center justify-center gap-2 disabled:opacity-40",children:["Continue",e.jsx(x,{className:"h-4 w-4"})]})]}),r===4&&e.jsxs("div",{children:[e.jsx("h1",{className:"text-xl font-bold text-white tracking-tight",children:"Choose your AI provider"}),e.jsx("p",{className:"text-white/40 text-[13px] mt-1.5 leading-relaxed",children:"Pick one provider to power your bot, authenticate, and select a model."}),e.jsx("div",{className:"flex gap-2.5 mt-4",children:gt.map(t=>e.jsxs("button",{onClick:()=>qe(t.id),className:`flex-1 relative rounded-xl border transition-all duration-200 p-3 text-left ${n===t.id?"bg-white/[0.04] border-[#AF27E3]/40":"bg-transparent border-white/[0.06] hover:border-white/10 hover:bg-white/[0.02]"}`,children:[e.jsxs("div",{className:"flex flex-col items-center gap-1.5 py-0.5",children:[t.icon?e.jsx("img",{src:t.icon,alt:t.name,className:"w-8 h-8 rounded-lg"}):e.jsx("div",{className:"w-8 h-8 rounded-lg bg-white/[0.06] flex items-center justify-center text-white/50 text-sm font-bold",children:"O"}),e.jsxs("div",{className:"text-center",children:[e.jsx("div",{className:"text-[13px] font-medium text-white",children:t.name}),e.jsx("div",{className:"text-[10px] text-white/30",children:t.subtitle})]})]}),G[t.id]==="connected"?e.jsx("div",{className:"absolute top-2 right-2 w-4 h-4 rounded-full bg-emerald-500/15 flex items-center justify-center",children:e.jsx(D,{className:"h-2.5 w-2.5 text-emerald-400"})}):n===t.id?e.jsx("div",{className:"absolute top-2 right-2 w-2 h-2 rounded-full bg-gradient-brand"}):null]},t.id))}),e.jsx("div",{className:"border-t border-white/[0.06] mt-4 mb-3"}),n==="anthropic"&&e.jsxs("div",{className:"space-y-2.5",children:[C&&e.jsx("div",{className:"bg-emerald-500/8 border border-emerald-500/15 rounded-lg px-3.5 py-2.5",children:e.jsx("p",{className:"text-emerald-400/90 text-[12px]",children:"Connected — Anthropic subscription is active."})}),!C&&e.jsxs(e.Fragment,{children:[xe&&e.jsx("div",{className:"bg-red-500/8 border border-red-500/15 rounded-lg px-3.5 py-2.5",children:e.jsx("p",{className:"text-red-400/90 text-[12px]",children:xe})}),e.jsx("div",{className:"space-y-1.5",children:["Click the button below to open Anthropic's login page","Sign in with your Anthropic account — a code will be generated","Copy the code and paste it in the field below"].map((t,s)=>e.jsxs("div",{className:"flex items-start gap-2",children:[e.jsx("span",{className:"flex-shrink-0 w-[18px] h-[18px] rounded-full bg-white/[0.06] text-white/30 text-[10px] font-medium flex items-center justify-center mt-px",children:s+1}),e.jsx("p",{className:"text-white/40 text-[12px] leading-relaxed",children:t})]},s))}),e.jsx("button",{onClick:Je,className:"w-full py-2.5 px-4 bg-gradient-brand hover:opacity-90 text-white text-[13px] font-medium rounded-xl transition-colors flex items-center justify-center gap-2",children:Ue?e.jsxs(e.Fragment,{children:[e.jsx(pt,{className:"h-3.5 w-3.5 opacity-60"}),"Open authentication page again"]}):e.jsxs(e.Fragment,{children:["Authenticate with Anthropic",e.jsx(x,{className:"h-3.5 w-3.5 opacity-60"})]})}),e.jsxs("div",{className:"relative",children:[e.jsx("input",{type:"text",value:V,onChange:t=>Z(t.target.value),onKeyDown:t=>t.key==="Enter"&&Oe(),placeholder:"Paste your code here...",className:Me+" pr-10 font-mono"}),e.jsx("button",{onClick:Ye,className:"absolute right-3 top-1/2 -translate-y-1/2 text-white/20 hover:text-white/50 transition-colors",children:e.jsx(xt,{className:"h-3.5 w-3.5"})})]}),e.jsx("button",{onClick:Oe,disabled:!V.trim()||de,className:"w-full py-2.5 px-4 bg-gradient-brand hover:opacity-90 text-white text-[13px] font-medium rounded-xl transition-colors flex items-center justify-center gap-2 disabled:opacity-40",children:de?e.jsxs(e.Fragment,{children:[e.jsx(H,{className:"h-3.5 w-3.5 animate-spin"}),"Verifying..."]}):"Connect"}),e.jsxs("button",{onClick:Qe,disabled:ee,className:"w-full py-1.5 text-white/25 text-[11px] hover:text-white/40 transition-colors flex items-center justify-center gap-1.5 disabled:opacity-50",children:[ee?e.jsx(H,{className:"h-3 w-3 animate-spin"}):e.jsx(bt,{className:"h-3 w-3"}),ee?"Checking...":"I'm already authenticated"]})]})]}),n==="openai"&&e.jsxs("div",{className:"space-y-2.5",children:[C&&e.jsx("div",{className:"bg-emerald-500/8 border border-emerald-500/15 rounded-lg px-3.5 py-2.5",children:e.jsx("p",{className:"text-emerald-400/90 text-[12px]",children:"Connected — ChatGPT subscription is active."})}),!C&&e.jsxs(e.Fragment,{children:[pe&&e.jsx("div",{className:"bg-red-500/8 border border-red-500/15 rounded-lg px-3.5 py-2.5",children:e.jsx("p",{className:"text-red-400/90 text-[12px]",children:pe})}),e.jsx("div",{className:"space-y-1.5",children:["Click the button below — your browser will open for ChatGPT sign-in","Sign in with your ChatGPT Plus or Pro account","Authentication completes automatically — no code to copy"].map((t,s)=>e.jsxs("div",{className:"flex items-start gap-2",children:[e.jsx("span",{className:"flex-shrink-0 w-[18px] h-[18px] rounded-full bg-white/[0.06] text-white/30 text-[10px] font-medium flex items-center justify-center mt-px",children:s+1}),e.jsx("p",{className:"text-white/40 text-[12px] leading-relaxed",children:t})]},s))}),e.jsx("button",{onClick:Xe,disabled:O,className:"w-full py-2.5 px-4 bg-white/[0.06] hover:bg-white/[0.09] text-white text-[13px] font-medium rounded-xl transition-colors flex items-center justify-center gap-2 disabled:opacity-60",children:O?e.jsxs(e.Fragment,{children:[e.jsx(H,{className:"h-3.5 w-3.5 animate-spin opacity-60"}),"Waiting for sign-in..."]}):e.jsxs(e.Fragment,{children:["Authenticate with ChatGPT",e.jsx(x,{className:"h-3.5 w-3.5 opacity-60"})]})}),O&&e.jsx("button",{onClick:Ze,className:"w-full py-1.5 text-white/25 text-[11px] hover:text-white/40 transition-colors",children:"Cancel"})]})]}),n==="ollama"&&e.jsxs("div",{className:"space-y-2.5",children:[e.jsx("div",{className:"bg-emerald-500/8 border border-emerald-500/15 rounded-lg px-3.5 py-2.5",children:e.jsx("p",{className:"text-emerald-400/90 text-[12px]",children:"No authentication needed — Ollama runs locally."})}),e.jsxs("div",{children:[e.jsxs("label",{className:"text-[12px] text-white/40 font-medium mb-1.5 block",children:["Base URL ",e.jsx("span",{className:"text-white/20",children:"(optional)"})]}),e.jsx("input",{type:"text",value:ue,onChange:t=>_e(t.target.value),placeholder:"http://localhost:11434",className:Me})]})]}),C&&e.jsxs(e.Fragment,{children:[e.jsx("div",{className:"border-t border-white/[0.06] mt-4 mb-3"}),e.jsx("label",{className:"text-[12px] text-white/40 font-medium mb-1.5 block",children:"Select a model"}),e.jsx(jt,{models:wt[n]||[],value:i,onChange:L})]}),C&&e.jsxs("button",{onClick:k,disabled:!W,className:"w-full mt-4 py-3 bg-gradient-brand hover:opacity-90 text-white text-[14px] font-semibold rounded-full transition-colors flex items-center justify-center gap-2 disabled:opacity-40",children:["Continue",e.jsx(x,{className:"h-4 w-4"})]})]}),r===5&&e.jsxs("div",{children:[e.jsxs("div",{className:"flex items-center gap-2 mb-1",children:[e.jsx("h1",{className:"text-xl font-bold text-white tracking-tight",children:"Voice Messages"}),e.jsx("span",{className:"text-[11px] text-white/25 font-medium bg-white/[0.04] border border-white/[0.06] rounded-full px-2.5 py-0.5",children:"Optional"})]}),e.jsx("p",{className:"text-white/40 text-[13px] mt-1 leading-relaxed",children:"Allow users to send audio messages that are automatically transcribed."}),e.jsx("button",{onClick:()=>Ae(t=>!t),className:`w-full mt-5 rounded-xl border transition-all duration-200 p-4 text-left ${j?"bg-white/[0.04] border-[#AF27E3]/40":"bg-transparent border-white/[0.06] hover:border-white/10 hover:bg-white/[0.02]"}`,children:e.jsxs("div",{className:"flex items-center gap-3.5",children:[e.jsx("img",{src:"/icons/openai.svg",alt:"OpenAI",className:"w-10 h-10 rounded-xl bg-white/[0.04] p-1.5"}),e.jsxs("div",{className:"flex-1",children:[e.jsx("div",{className:"text-[14px] font-medium text-white",children:"OpenAI Whisper"}),e.jsx("div",{className:"text-[12px] text-white/35 mt-0.5 leading-relaxed",children:"Speech-to-text powered by OpenAI. Requires an OpenAI API key."})]}),e.jsx("div",{className:`w-10 h-[22px] rounded-full transition-colors duration-200 flex items-center px-0.5 shrink-0 ${j?"bg-gradient-brand":"bg-white/[0.08]"}`,children:e.jsx("div",{className:`w-[18px] h-[18px] rounded-full bg-white shadow-sm transition-transform duration-200 ${j?"translate-x-[18px]":"translate-x-0"}`})})]})}),j&&e.jsxs("div",{className:"mt-3",children:[e.jsx("label",{className:"text-[12px] text-white/40 font-medium mb-1.5 block",children:"OpenAI API Key"}),e.jsx("input",{type:"password",value:u,onChange:t=>Ee(t.target.value.trim()),placeholder:"sk-...",autoComplete:"off",className:S+" font-mono text-[13px]"}),u.length>0&&!u.startsWith("sk-")&&e.jsx("p",{className:"text-amber-400/70 text-[11px] mt-1",children:"Key should start with sk-"}),u.length>0&&u.startsWith("sk-")&&u.length<20&&e.jsx("p",{className:"text-amber-400/70 text-[11px] mt-1",children:"Key looks too short"}),e.jsxs("div",{className:"flex items-start gap-2.5 mt-3 bg-white/[0.02] border border-white/[0.06] rounded-xl px-4 py-3",children:[e.jsx(nt,{className:"h-4 w-4 text-[#AF27E3]/60 mt-0.5 shrink-0"}),e.jsx("p",{className:"text-white/35 text-[12px] leading-relaxed",children:"Users will see a microphone button in the chat. Audio is sent to OpenAI's Whisper API for transcription, then processed as a regular text message."})]})]}),e.jsx("button",{onClick:at,disabled:le||j&&(!u.startsWith("sk-")||u.length<20),className:"w-full mt-5 py-3 bg-gradient-brand hover:opacity-90 text-white text-[14px] font-semibold rounded-full transition-colors flex items-center justify-center gap-2 disabled:opacity-40",children:le?e.jsxs(e.Fragment,{children:[e.jsx(H,{className:"h-4 w-4 animate-spin"}),"Setting up..."]}):e.jsxs(e.Fragment,{children:["Complete Setup",e.jsx(x,{className:"h-4 w-4"})]})}),!j&&e.jsx("p",{className:"text-center text-white/20 text-[11px] mt-2.5",children:"You can enable this later in Settings."})]})]},r)}),r>0&&e.jsx("div",{className:"px-8 pb-5 -mt-3",children:e.jsx("button",{onClick:tt,className:"text-white/25 hover:text-white/50 text-[12px] transition-colors",children:"← Back"})})]})]})}function Nt(){const h=()=>{window.parent?.postMessage({type:"fluxy:onboard-complete"},"*")};return e.jsx(yt,{onComplete:h})}rt.createRoot(document.getElementById("root")).render(e.jsx(it.StrictMode,{children:e.jsx(Nt,{})}));
@@ -0,0 +1,14 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0, interactive-widget=resizes-content" />
6
+ <title>Fluxy Chat</title>
7
+ <script type="module" crossorigin src="/fluxy/assets/fluxy-CLwOT1vX.js"></script>
8
+ <link rel="modulepreload" crossorigin href="/fluxy/assets/globals-DR8AapV6.js">
9
+ <link rel="stylesheet" crossorigin href="/fluxy/assets/globals-BURMVQnd.css">
10
+ </head>
11
+ <body class="bg-background text-foreground">
12
+ <div id="root"></div>
13
+ </body>
14
+ </html>
@@ -0,0 +1,14 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0, interactive-widget=resizes-content" />
6
+ <title>Fluxy Setup</title>
7
+ <script type="module" crossorigin src="/fluxy/assets/onboard-UgN1C7y4.js"></script>
8
+ <link rel="modulepreload" crossorigin href="/fluxy/assets/globals-DR8AapV6.js">
9
+ <link rel="stylesheet" crossorigin href="/fluxy/assets/globals-BURMVQnd.css">
10
+ </head>
11
+ <body class="bg-background text-foreground">
12
+ <div id="root"></div>
13
+ </body>
14
+ </html>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fluxy-bot",
3
- "version": "0.2.33",
3
+ "version": "0.2.35",
4
4
  "description": "Self-hosted AI bot — run your own AI assistant from anywhere",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -9,6 +9,7 @@
9
9
  },
10
10
  "files": [
11
11
  "bin/",
12
+ "dist-fluxy/",
12
13
  "supervisor/",
13
14
  "worker/",
14
15
  "shared/",
@@ -156,6 +156,21 @@ function Install-Fluxy {
156
156
  try {
157
157
  & $NPM install --omit=dev 2>$null
158
158
  } catch {}
159
+
160
+ # Build fluxy chat + onboard (served as static files)
161
+ $distFluxy = Join-Path $FLUXY_HOME "dist-fluxy"
162
+ $onboardHtml = Join-Path $distFluxy "onboard.html"
163
+ if ((Test-Path $distFluxy) -and (Test-Path $onboardHtml)) {
164
+ Write-Host " ✔ Chat interface (pre-built)" -ForegroundColor Green
165
+ } else {
166
+ Write-Host " ↓ Building chat interface..." -ForegroundColor Cyan
167
+ try {
168
+ & $NPM run build:fluxy 2>$null
169
+ Write-Host " ✔ Chat interface built" -ForegroundColor Green
170
+ } catch {
171
+ Write-Host " ! Chat interface build failed — it will be built on first start" -ForegroundColor Yellow
172
+ }
173
+ }
159
174
  Pop-Location
160
175
 
161
176
  # Verify
@@ -73,9 +73,16 @@ npm install --omit=dev 2>/dev/null
73
73
  ok "Dependencies installed"
74
74
 
75
75
  # ── Build fluxy chat + onboard (served as static files) ──
76
- info "Building chat interface..."
77
- npm run build:fluxy 2>/dev/null
78
- ok "Chat interface built"
76
+ if [ -d "$FLUXY_HOME/dist-fluxy" ] && [ -f "$FLUXY_HOME/dist-fluxy/onboard.html" ]; then
77
+ ok "Chat interface (pre-built)"
78
+ else
79
+ info "Building chat interface..."
80
+ if npm run build:fluxy; then
81
+ ok "Chat interface built"
82
+ else
83
+ err "Chat interface build failed — it will be built on first start"
84
+ fi
85
+ fi
79
86
 
80
87
  # ── Create symlink ──
81
88
  info "Creating fluxy command..."
@@ -63,13 +63,16 @@ try {
63
63
 
64
64
  // ── Build fluxy chat + onboard (served as static files) ──
65
65
 
66
- try {
67
- execSync('npm run build:fluxy', {
68
- cwd: FLUXY_HOME,
69
- stdio: 'ignore',
70
- });
71
- } catch {
72
- // Non-fatal: will be built on first start if needed
66
+ const distFluxy = path.join(FLUXY_HOME, 'dist-fluxy', 'onboard.html');
67
+ if (!fs.existsSync(distFluxy)) {
68
+ try {
69
+ execSync('npm run build:fluxy', {
70
+ cwd: FLUXY_HOME,
71
+ stdio: 'ignore',
72
+ });
73
+ } catch {
74
+ // Non-fatal: supervisor will build on first start
75
+ }
73
76
  }
74
77
 
75
78
  // ── Create fluxy symlink ──
@@ -14,9 +14,24 @@ import { spawnBackend, stopBackend, getBackendPort, isBackendAlive, resetBackend
14
14
  import { updateTunnelUrl, startHeartbeat, stopHeartbeat, disconnect } from '../shared/relay.js';
15
15
  import { startFluxyAgentQuery, stopFluxyAgentQuery, clearFluxySession } from './fluxy-agent.js';
16
16
  import { startViteDevServers, stopViteDevServers } from './vite-dev.js';
17
+ import { execSync } from 'child_process';
17
18
 
18
19
  const DIST_FLUXY = path.join(PKG_DIR, 'dist-fluxy');
19
20
 
21
+ // Ensure dist-fluxy exists (postinstall may have failed silently)
22
+ if (!fs.existsSync(DIST_FLUXY)) {
23
+ log.info('Building fluxy chat UI (first run)...');
24
+ try {
25
+ execSync('npx vite build --config vite.fluxy.config.ts', {
26
+ cwd: PKG_DIR,
27
+ stdio: 'ignore',
28
+ });
29
+ log.ok('Fluxy chat UI built');
30
+ } catch (err) {
31
+ log.warn(`Failed to build fluxy chat UI: ${err instanceof Error ? err.message : err}`);
32
+ }
33
+ }
34
+
20
35
  const MIME_TYPES: Record<string, string> = {
21
36
  '.html': 'text/html',
22
37
  '.js': 'application/javascript',
@@ -120,32 +120,42 @@ export function readClaudeAccessToken(): string | null {
120
120
  /* ── Helpers ── */
121
121
 
122
122
  /**
123
- * Read the OAuth block from credentials file or macOS Keychain.
123
+ * Read the OAuth block from the most reliable source.
124
+ * On macOS: Keychain is the source of truth (Claude Code reads/writes there on refresh).
125
+ * If Keychain has no valid entry, stale files are not trusted.
126
+ * On Linux/Windows: credentials file is the source of truth.
124
127
  * Handles both formats:
125
128
  * - Claude Code format: { claudeAiOauth: { accessToken, refreshToken, expiresAt, ... } }
126
129
  * - Legacy flat format: { accessToken, refreshToken, expiresAt }
127
130
  */
128
131
  function readOAuthBlock(): { accessToken?: string; refreshToken?: string; expiresAt?: number } | null {
129
- // Primary: credentials file
130
- try {
131
- if (fs.existsSync(CREDENTIALS_FILE)) {
132
- const creds = JSON.parse(fs.readFileSync(CREDENTIALS_FILE, 'utf-8'));
133
- const oauth = creds.claudeAiOauth || creds;
134
- if (oauth.accessToken) return oauth;
135
- }
136
- } catch {}
137
-
138
- // macOS Keychain fallback
132
+ // macOS: Keychain is the source of truth
139
133
  if (process.platform === 'darwin') {
140
134
  try {
141
135
  const result = execFileSync('security', [
142
136
  'find-generic-password', '-s', 'Claude Code-credentials', '-w',
143
137
  ], { stdio: ['pipe', 'pipe', 'pipe'] }).toString().trim();
144
138
  const parsed = JSON.parse(result);
145
- if (parsed.claudeAiOauth?.accessToken) return parsed.claudeAiOauth;
139
+ const oauth = parsed.claudeAiOauth || parsed;
140
+ if (oauth.accessToken) {
141
+ log.ok('Read credentials from macOS Keychain');
142
+ return oauth;
143
+ }
146
144
  } catch {}
145
+ // On macOS, if Keychain has no valid entry, don't trust stale files
146
+ log.warn('No valid credentials in macOS Keychain');
147
+ return null;
147
148
  }
148
149
 
150
+ // Linux/Windows: credentials file
151
+ try {
152
+ if (fs.existsSync(CREDENTIALS_FILE)) {
153
+ const creds = JSON.parse(fs.readFileSync(CREDENTIALS_FILE, 'utf-8'));
154
+ const oauth = creds.claudeAiOauth || creds;
155
+ if (oauth.accessToken) return oauth;
156
+ }
157
+ } catch {}
158
+
149
159
  return null;
150
160
  }
151
161