loki-mode 6.68.1 → 6.69.0

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.
Files changed (23) hide show
  1. package/SKILL.md +2 -2
  2. package/VERSION +1 -1
  3. package/dashboard/__init__.py +1 -1
  4. package/docs/INSTALLATION.md +1 -1
  5. package/mcp/__init__.py +1 -1
  6. package/package.json +1 -1
  7. package/web-app/dist/assets/{Badge-BDswso-T.js → Badge-CeqwGFM0.js} +1 -1
  8. package/web-app/dist/assets/{Button-Bjh5cEzV.js → Button-qiaMm3tV.js} +1 -1
  9. package/web-app/dist/assets/{Card-C64NpHs3.js → Card-Uav6bI4v.js} +1 -1
  10. package/web-app/dist/assets/{HomePage-DwxAu0-p.js → HomePage-CnHzys9u.js} +1 -1
  11. package/web-app/dist/assets/{LoginPage-DancuhW_.js → LoginPage-C5TN24fk.js} +1 -1
  12. package/web-app/dist/assets/{NotFoundPage-BPHM_nY0.js → NotFoundPage-CRF3pccO.js} +1 -1
  13. package/web-app/dist/assets/{ProjectPage-EXVpaoai.js → ProjectPage-reLTN0xY.js} +47 -47
  14. package/web-app/dist/assets/{ProjectsPage-Xs5iw-3h.js → ProjectsPage-BxvFm4PV.js} +1 -1
  15. package/web-app/dist/assets/{SettingsPage-B_5SorV_.js → SettingsPage-B-_VC6hV.js} +1 -1
  16. package/web-app/dist/assets/{TemplatesPage-YWtOXUNW.js → TemplatesPage-3h_qn6Ag.js} +1 -1
  17. package/web-app/dist/assets/{TerminalOutput-Df2D24ZV.js → TerminalOutput-Blk-nLSd.js} +1 -1
  18. package/web-app/dist/assets/{arrow-left-jnFtrB4w.js → arrow-left-oowTfEm_.js} +1 -1
  19. package/web-app/dist/assets/{clock-Bn329FKh.js → clock-DREWjyYa.js} +1 -1
  20. package/web-app/dist/assets/{external-link-B2gWzuEH.js → external-link-C6nfCPLP.js} +1 -1
  21. package/web-app/dist/assets/{index-DyZl3sXe.js → index-B79IKKoY.js} +2 -2
  22. package/web-app/dist/index.html +1 -1
  23. package/web-app/server.py +170 -0
@@ -1,4 +1,4 @@
1
- import{c as p,u as C,r as a,a as j,j as e}from"./index-DyZl3sXe.js";import{B as b,P as y,F,T as S}from"./Button-Bjh5cEzV.js";import{C as P}from"./Card-C64NpHs3.js";import{u as _,B}from"./Badge-BDswso-T.js";import{E as z}from"./external-link-B2gWzuEH.js";import"./clock-Bn329FKh.js";/**
1
+ import{c as p,u as C,r as a,a as j,j as e}from"./index-B79IKKoY.js";import{B as b,P as y,F,T as S}from"./Button-qiaMm3tV.js";import{C as P}from"./Card-Uav6bI4v.js";import{u as _,B}from"./Badge-CeqwGFM0.js";import{E as z}from"./external-link-C6nfCPLP.js";import"./clock-DREWjyYa.js";/**
2
2
  * @license lucide-react v0.577.0 - ISC
3
3
  *
4
4
  * This source code is licensed under the ISC license.
@@ -1 +1 @@
1
- import{r as t,a,j as e}from"./index-DyZl3sXe.js";import{C as l}from"./Card-C64NpHs3.js";import{E as c}from"./external-link-B2gWzuEH.js";const h=[{id:"claude",name:"Claude",description:"Anthropic Claude Code -- full features"},{id:"codex",name:"Codex",description:"OpenAI Codex CLI -- degraded mode"},{id:"gemini",name:"Gemini",description:"Google Gemini CLI -- degraded mode"}];function p(){const[i,n]=t.useState("claude"),[o,r]=t.useState(!1),[d,x]=t.useState("");t.useEffect(()=>{a.getCurrentProvider().then(s=>n(s.provider)).catch(()=>{}),a.getStatus().then(s=>x(s.version||"")).catch(()=>{})},[]);const m=async s=>{n(s),r(!0);try{await a.setProvider(s)}catch{}finally{r(!1)}};return e.jsxs("div",{className:"max-w-[800px] mx-auto px-6 py-8",children:[e.jsx("h1",{className:"font-heading text-h1 text-[#36342E] mb-8",children:"Settings"}),e.jsxs("section",{className:"mb-10",children:[e.jsx("h2",{className:"text-sm font-semibold text-[#36342E] uppercase tracking-wide mb-4",children:"Provider"}),e.jsx("div",{className:"flex flex-col gap-3",children:h.map(s=>e.jsx(l,{hover:!0,onClick:()=>m(s.id),className:i===s.id?"ring-2 ring-[#553DE9] border-[#553DE9]":"",children:e.jsxs("div",{className:"flex items-center gap-3",children:[e.jsx("div",{className:`w-4 h-4 rounded-full border-2 flex items-center justify-center flex-shrink-0 ${i===s.id?"border-[#553DE9]":"border-[#ECEAE3]"}`,children:i===s.id&&e.jsx("div",{className:"w-2 h-2 rounded-full bg-[#553DE9]"})}),e.jsxs("div",{children:[e.jsx("p",{className:"text-sm font-medium text-[#36342E]",children:s.name}),e.jsx("p",{className:"text-xs text-[#6B6960]",children:s.description})]})]})},s.id))}),o&&e.jsx("p",{className:"text-xs text-[#6B6960] mt-2",children:"Saving..."})]}),e.jsxs("section",{children:[e.jsx("h2",{className:"text-sm font-semibold text-[#36342E] uppercase tracking-wide mb-4",children:"About"}),e.jsx(l,{children:e.jsxs("div",{className:"flex flex-col gap-3",children:[d&&e.jsxs("div",{className:"flex items-center justify-between",children:[e.jsx("span",{className:"text-sm text-[#6B6960]",children:"Version"}),e.jsxs("span",{className:"text-sm font-medium text-[#36342E]",children:["v",d]})]}),e.jsxs("div",{className:"flex items-center justify-between",children:[e.jsx("span",{className:"text-sm text-[#6B6960]",children:"Documentation"}),e.jsxs("a",{href:"https://www.autonomi.dev/docs",target:"_blank",rel:"noopener noreferrer",className:"inline-flex items-center gap-1 text-sm text-[#553DE9] hover:underline",children:["autonomi.dev/docs ",e.jsx(c,{size:12})]})]}),e.jsxs("div",{className:"flex items-center justify-between",children:[e.jsx("span",{className:"text-sm text-[#6B6960]",children:"GitHub"}),e.jsxs("a",{href:"https://github.com/asklokesh/loki-mode",target:"_blank",rel:"noopener noreferrer",className:"inline-flex items-center gap-1 text-sm text-[#553DE9] hover:underline",children:["asklokesh/loki-mode ",e.jsx(c,{size:12})]})]})]})})]})]})}export{p as default};
1
+ import{r as t,a,j as e}from"./index-B79IKKoY.js";import{C as l}from"./Card-Uav6bI4v.js";import{E as c}from"./external-link-C6nfCPLP.js";const h=[{id:"claude",name:"Claude",description:"Anthropic Claude Code -- full features"},{id:"codex",name:"Codex",description:"OpenAI Codex CLI -- degraded mode"},{id:"gemini",name:"Gemini",description:"Google Gemini CLI -- degraded mode"}];function p(){const[i,n]=t.useState("claude"),[o,r]=t.useState(!1),[d,x]=t.useState("");t.useEffect(()=>{a.getCurrentProvider().then(s=>n(s.provider)).catch(()=>{}),a.getStatus().then(s=>x(s.version||"")).catch(()=>{})},[]);const m=async s=>{n(s),r(!0);try{await a.setProvider(s)}catch{}finally{r(!1)}};return e.jsxs("div",{className:"max-w-[800px] mx-auto px-6 py-8",children:[e.jsx("h1",{className:"font-heading text-h1 text-[#36342E] mb-8",children:"Settings"}),e.jsxs("section",{className:"mb-10",children:[e.jsx("h2",{className:"text-sm font-semibold text-[#36342E] uppercase tracking-wide mb-4",children:"Provider"}),e.jsx("div",{className:"flex flex-col gap-3",children:h.map(s=>e.jsx(l,{hover:!0,onClick:()=>m(s.id),className:i===s.id?"ring-2 ring-[#553DE9] border-[#553DE9]":"",children:e.jsxs("div",{className:"flex items-center gap-3",children:[e.jsx("div",{className:`w-4 h-4 rounded-full border-2 flex items-center justify-center flex-shrink-0 ${i===s.id?"border-[#553DE9]":"border-[#ECEAE3]"}`,children:i===s.id&&e.jsx("div",{className:"w-2 h-2 rounded-full bg-[#553DE9]"})}),e.jsxs("div",{children:[e.jsx("p",{className:"text-sm font-medium text-[#36342E]",children:s.name}),e.jsx("p",{className:"text-xs text-[#6B6960]",children:s.description})]})]})},s.id))}),o&&e.jsx("p",{className:"text-xs text-[#6B6960] mt-2",children:"Saving..."})]}),e.jsxs("section",{children:[e.jsx("h2",{className:"text-sm font-semibold text-[#36342E] uppercase tracking-wide mb-4",children:"About"}),e.jsx(l,{children:e.jsxs("div",{className:"flex flex-col gap-3",children:[d&&e.jsxs("div",{className:"flex items-center justify-between",children:[e.jsx("span",{className:"text-sm text-[#6B6960]",children:"Version"}),e.jsxs("span",{className:"text-sm font-medium text-[#36342E]",children:["v",d]})]}),e.jsxs("div",{className:"flex items-center justify-between",children:[e.jsx("span",{className:"text-sm text-[#6B6960]",children:"Documentation"}),e.jsxs("a",{href:"https://www.autonomi.dev/docs",target:"_blank",rel:"noopener noreferrer",className:"inline-flex items-center gap-1 text-sm text-[#553DE9] hover:underline",children:["autonomi.dev/docs ",e.jsx(c,{size:12})]})]}),e.jsxs("div",{className:"flex items-center justify-between",children:[e.jsx("span",{className:"text-sm text-[#6B6960]",children:"GitHub"}),e.jsxs("a",{href:"https://github.com/asklokesh/loki-mode",target:"_blank",rel:"noopener noreferrer",className:"inline-flex items-center gap-1 text-sm text-[#553DE9] hover:underline",children:["asklokesh/loki-mode ",e.jsx(c,{size:12})]})]})]})})]})]})}export{p as default};
@@ -1 +1 @@
1
- import{u as c,r,a as x,j as t}from"./index-DyZl3sXe.js";import{C as p}from"./Card-C64NpHs3.js";import{u as d,B as h}from"./Badge-BDswso-T.js";import"./clock-Bn329FKh.js";const g=[{key:"all",label:"All"},{key:"website",label:"Website"},{key:"api",label:"API"},{key:"cli",label:"CLI"},{key:"bot",label:"Bot"},{key:"data",label:"Data"},{key:"other",label:"Other"}];function u(l){return l.replace(/\.md$/i,"").replace(/[-_]/g," ").replace(/\b\w/g,a=>a.toUpperCase())}function k(){const l=c(),[a,o]=r.useState("all"),n=r.useCallback(()=>x.getTemplates(),[]),{data:s}=d(n,6e4,!0),i=r.useMemo(()=>s?a==="all"?s:s.filter(e=>(e.category||"other")===a):[],[s,a]),m=e=>{sessionStorage.setItem("pl_template",e),l("/")};return t.jsxs("div",{className:"max-w-[1400px] mx-auto px-6 py-8",children:[t.jsx("h1",{className:"font-heading text-h1 text-[#36342E] mb-6",children:"Templates"}),t.jsx("div",{className:"flex items-center gap-1 mb-6",role:"tablist",children:g.map(e=>t.jsx("button",{role:"tab","aria-selected":a===e.key,onClick:()=>o(e.key),className:`px-3 py-1.5 text-xs font-semibold rounded-[3px] transition-colors ${a===e.key?"bg-[#553DE9] text-white":"text-[#6B6960] hover:text-[#36342E] hover:bg-[#F8F4F0]"}`,children:e.label},e.key))}),s?i.length===0?t.jsx("p",{className:"text-sm text-[#6B6960] py-12 text-center",children:"No templates in this category."}):t.jsx("div",{className:"grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4",children:i.map(e=>t.jsxs(p,{hover:!0,onClick:()=>m(e.filename),children:[t.jsx("div",{className:"mb-2",children:t.jsx(h,{status:"version",children:e.category||"other"})}),t.jsx("h3",{className:"text-sm font-medium text-[#36342E] mb-1",children:u(e.name)}),t.jsx("p",{className:"text-xs text-[#6B6960]",children:e.filename})]},e.filename))}):t.jsx("p",{className:"text-sm text-[#6B6960]",children:"Loading templates..."})]})}export{k as default};
1
+ import{u as c,r,a as x,j as t}from"./index-B79IKKoY.js";import{C as p}from"./Card-Uav6bI4v.js";import{u as d,B as h}from"./Badge-CeqwGFM0.js";import"./clock-DREWjyYa.js";const g=[{key:"all",label:"All"},{key:"website",label:"Website"},{key:"api",label:"API"},{key:"cli",label:"CLI"},{key:"bot",label:"Bot"},{key:"data",label:"Data"},{key:"other",label:"Other"}];function u(l){return l.replace(/\.md$/i,"").replace(/[-_]/g," ").replace(/\b\w/g,a=>a.toUpperCase())}function k(){const l=c(),[a,o]=r.useState("all"),n=r.useCallback(()=>x.getTemplates(),[]),{data:s}=d(n,6e4,!0),i=r.useMemo(()=>s?a==="all"?s:s.filter(e=>(e.category||"other")===a):[],[s,a]),m=e=>{sessionStorage.setItem("pl_template",e),l("/")};return t.jsxs("div",{className:"max-w-[1400px] mx-auto px-6 py-8",children:[t.jsx("h1",{className:"font-heading text-h1 text-[#36342E] mb-6",children:"Templates"}),t.jsx("div",{className:"flex items-center gap-1 mb-6",role:"tablist",children:g.map(e=>t.jsx("button",{role:"tab","aria-selected":a===e.key,onClick:()=>o(e.key),className:`px-3 py-1.5 text-xs font-semibold rounded-[3px] transition-colors ${a===e.key?"bg-[#553DE9] text-white":"text-[#6B6960] hover:text-[#36342E] hover:bg-[#F8F4F0]"}`,children:e.label},e.key))}),s?i.length===0?t.jsx("p",{className:"text-sm text-[#6B6960] py-12 text-center",children:"No templates in this category."}):t.jsx("div",{className:"grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4",children:i.map(e=>t.jsxs(p,{hover:!0,onClick:()=>m(e.filename),children:[t.jsx("div",{className:"mb-2",children:t.jsx(h,{status:"version",children:e.category||"other"})}),t.jsx("h3",{className:"text-sm font-medium text-[#36342E] mb-1",children:u(e.name)}),t.jsx("p",{className:"text-xs text-[#6B6960]",children:e.filename})]},e.filename))}):t.jsx("p",{className:"text-sm text-[#6B6960]",children:"Loading templates..."})]})}export{k as default};
@@ -1,4 +1,4 @@
1
- import{c as o,r as m,j as e}from"./index-DyZl3sXe.js";/**
1
+ import{c as o,r as m,j as e}from"./index-B79IKKoY.js";/**
2
2
  * @license lucide-react v0.577.0 - ISC
3
3
  *
4
4
  * This source code is licensed under the ISC license.
@@ -1,4 +1,4 @@
1
- import{c as o}from"./index-DyZl3sXe.js";/**
1
+ import{c as o}from"./index-B79IKKoY.js";/**
2
2
  * @license lucide-react v0.577.0 - ISC
3
3
  *
4
4
  * This source code is licensed under the ISC license.
@@ -1,4 +1,4 @@
1
- import{c}from"./index-DyZl3sXe.js";/**
1
+ import{c}from"./index-B79IKKoY.js";/**
2
2
  * @license lucide-react v0.577.0 - ISC
3
3
  *
4
4
  * This source code is licensed under the ISC license.
@@ -1,4 +1,4 @@
1
- import{c as a}from"./index-DyZl3sXe.js";/**
1
+ import{c as a}from"./index-B79IKKoY.js";/**
2
2
  * @license lucide-react v0.577.0 - ISC
3
3
  *
4
4
  * This source code is licensed under the ISC license.
@@ -1,4 +1,4 @@
1
- const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/HomePage-DwxAu0-p.js","assets/Badge-BDswso-T.js","assets/clock-Bn329FKh.js","assets/TerminalOutput-Df2D24ZV.js","assets/ProjectPage-EXVpaoai.js","assets/Button-Bjh5cEzV.js","assets/arrow-left-jnFtrB4w.js","assets/external-link-B2gWzuEH.js","assets/ProjectPage-9CEnUXvW.css","assets/ProjectsPage-Xs5iw-3h.js","assets/Card-C64NpHs3.js","assets/TemplatesPage-YWtOXUNW.js","assets/SettingsPage-B_5SorV_.js","assets/NotFoundPage-BPHM_nY0.js"])))=>i.map(i=>d[i]);
1
+ const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/HomePage-CnHzys9u.js","assets/Badge-CeqwGFM0.js","assets/clock-DREWjyYa.js","assets/TerminalOutput-Blk-nLSd.js","assets/ProjectPage-reLTN0xY.js","assets/Button-qiaMm3tV.js","assets/arrow-left-oowTfEm_.js","assets/external-link-C6nfCPLP.js","assets/ProjectPage-9CEnUXvW.css","assets/ProjectsPage-BxvFm4PV.js","assets/Card-Uav6bI4v.js","assets/TemplatesPage-3h_qn6Ag.js","assets/SettingsPage-B-_VC6hV.js","assets/NotFoundPage-CRF3pccO.js"])))=>i.map(i=>d[i]);
2
2
  var S0=Object.defineProperty;var b0=(i,f,r)=>f in i?S0(i,f,{enumerable:!0,configurable:!0,writable:!0,value:r}):i[f]=r;var Bn=(i,f,r)=>b0(i,typeof f!="symbol"?f+"":f,r);(function(){const f=document.createElement("link").relList;if(f&&f.supports&&f.supports("modulepreload"))return;for(const d of document.querySelectorAll('link[rel="modulepreload"]'))o(d);new MutationObserver(d=>{for(const h of d)if(h.type==="childList")for(const g of h.addedNodes)g.tagName==="LINK"&&g.rel==="modulepreload"&&o(g)}).observe(document,{childList:!0,subtree:!0});function r(d){const h={};return d.integrity&&(h.integrity=d.integrity),d.referrerPolicy&&(h.referrerPolicy=d.referrerPolicy),d.crossOrigin==="use-credentials"?h.credentials="include":d.crossOrigin==="anonymous"?h.credentials="omit":h.credentials="same-origin",h}function o(d){if(d.ep)return;d.ep=!0;const h=r(d);fetch(d.href,h)}})();function E0(i){return i&&i.__esModule&&Object.prototype.hasOwnProperty.call(i,"default")?i.default:i}var Bf={exports:{}},Ln={};/**
3
3
  * @license React
4
4
  * react-jsx-runtime.production.js
@@ -183,4 +183,4 @@ Please change the parent <Route path="${B}"> to <Route path="${B==="/"?"*":`${B}
183
183
  *
184
184
  * This source code is licensed under the ISC license.
185
185
  * See the LICENSE file in the root directory of this source tree.
186
- */const k1=[["path",{d:"M18 6 6 18",key:"1bl5f8"}],["path",{d:"m6 6 12 12",key:"d8bk6v"}]],Kh=Jt("x",k1),Jh=`${window.location.origin}/api`,W1=`${window.location.protocol==="https:"?"wss:":"ws:"}//${window.location.host}/ws`;function F1(){const i={"Content-Type":"application/json"};try{const f=localStorage.getItem("pl_auth_token");f&&(i.Authorization=`Bearer ${f}`)}catch{}return i}async function $(i,f){const r=await fetch(`${Jh}${i}`,{...f,headers:{...F1(),...f==null?void 0:f.headers}});if(!r.ok){const o=await r.text().catch(()=>"");throw new Error(`API error ${r.status}: ${r.statusText}${o?` - ${o}`:""}`)}return r.json()}const qa={startSession:i=>$("/session/start",{method:"POST",body:JSON.stringify(i)}),stopSession:()=>$("/session/stop",{method:"POST"}),pauseSession:()=>$("/session/pause",{method:"POST"}),resumeSession:()=>$("/session/resume",{method:"POST"}),getPrdPrefill:()=>$("/session/prd-prefill"),getStatus:()=>$("/session/status"),getAgents:()=>$("/session/agents"),getLogs:(i=200)=>$(`/session/logs?lines=${i}`),getMemorySummary:()=>$("/session/memory"),getChecklist:()=>$("/session/checklist"),getFiles:()=>$("/session/files"),getFileContent:i=>$(`/session/files/content?path=${encodeURIComponent(i)}`),getSessionFileContent:(i,f)=>$(`/sessions/${encodeURIComponent(i)}/file?path=${encodeURIComponent(f)}`),getTemplates:()=>$("/templates"),getTemplateContent:i=>$(`/templates/${encodeURIComponent(i)}`),planSession:(i,f)=>$("/session/plan",{method:"POST",body:JSON.stringify({prd:i,provider:f})}),generateReport:(i="markdown")=>$("/session/report",{method:"POST",body:JSON.stringify({format:i})}),shareSession:()=>$("/session/share",{method:"POST"}),getCurrentProvider:()=>$("/provider/current"),setProvider:i=>$("/provider/set",{method:"POST",body:JSON.stringify({provider:i})}),getMetrics:()=>$("/session/metrics"),getSessionsHistory:()=>$("/sessions/history"),getSessionDetail:i=>$(`/sessions/${encodeURIComponent(i)}`),deleteSession:i=>$(`/sessions/${encodeURIComponent(i)}`,{method:"DELETE"}),onboardRepo:i=>$("/session/onboard",{method:"POST",body:JSON.stringify({path:i})}),saveSessionFile:(i,f,r)=>$(`/sessions/${encodeURIComponent(i)}/file`,{method:"PUT",body:JSON.stringify({path:f,content:r})}),createSessionFile:(i,f,r="")=>$(`/sessions/${encodeURIComponent(i)}/file`,{method:"POST",body:JSON.stringify({path:f,content:r})}),deleteSessionFile:(i,f)=>$(`/sessions/${encodeURIComponent(i)}/file`,{method:"DELETE",body:JSON.stringify({path:f})}),createSessionDirectory:(i,f)=>$(`/sessions/${encodeURIComponent(i)}/directory`,{method:"POST",body:JSON.stringify({path:f})}),reviewProject:i=>$(`/sessions/${encodeURIComponent(i)}/review`,{method:"POST"}),testProject:i=>$(`/sessions/${encodeURIComponent(i)}/test`,{method:"POST"}),explainProject:i=>$(`/sessions/${encodeURIComponent(i)}/explain`,{method:"POST"}),exportProject:i=>$(`/sessions/${encodeURIComponent(i)}/export`,{method:"POST"}),fixProject:i=>$(`/sessions/${encodeURIComponent(i)}/fix`,{method:"POST"}),chatMessage:(i,f,r="quick")=>$(`/sessions/${encodeURIComponent(i)}/chat`,{method:"POST",body:JSON.stringify({message:f,mode:r})}),chatStart:(i,f,r="quick")=>$(`/sessions/${encodeURIComponent(i)}/chat`,{method:"POST",body:JSON.stringify({message:f,mode:r})}),chatPoll:(i,f)=>$(`/sessions/${encodeURIComponent(i)}/chat/${encodeURIComponent(f)}`),chatStreamUrl:(i,f)=>`${Jh}/sessions/${encodeURIComponent(i)}/chat/${encodeURIComponent(f)}/stream`,chatCancel:(i,f)=>$(`/sessions/${encodeURIComponent(i)}/chat/${encodeURIComponent(f)}/cancel`,{method:"POST"}),getPreviewInfo:i=>$(`/sessions/${encodeURIComponent(i)}/preview-info`),devserver:{start:(i,f)=>$(`/sessions/${encodeURIComponent(i)}/devserver/start`,{method:"POST",body:JSON.stringify({command:f||null})}),stop:i=>$(`/sessions/${encodeURIComponent(i)}/devserver/stop`,{method:"POST"}),status:i=>$(`/sessions/${encodeURIComponent(i)}/devserver/status`)},getSecrets:()=>$("/secrets"),setSecret:(i,f)=>$("/secrets",{method:"POST",body:JSON.stringify({key:i,value:f})}),deleteSecret:i=>$(`/secrets/${encodeURIComponent(i)}`,{method:"DELETE"}),getServices:i=>$(`/sessions/${encodeURIComponent(i)}/services`),getServiceLogs:(i,f,r=50)=>$(`/sessions/${encodeURIComponent(i)}/devserver/logs${f?`?service=${encodeURIComponent(f)}&tail=${r}`:`?tail=${r}`}`),restartService:(i,f)=>$(`/sessions/${encodeURIComponent(i)}/devserver/restart-service`,{method:"POST",body:JSON.stringify({service:f})}),getMe:()=>$("/auth/me"),getGitHubAuthUrl:()=>$("/auth/github/url"),getGoogleAuthUrl:()=>$("/auth/google/url"),githubCallback:(i,f)=>$("/auth/github/callback",{method:"POST",body:JSON.stringify({code:i,state:f})}),googleCallback:(i,f,r)=>$("/auth/google/callback",{method:"POST",body:JSON.stringify({code:i,state:f,redirect_uri:r||`${window.location.origin}${window.location.pathname}`})})};class I1{constructor(f){Bn(this,"ws",null);Bn(this,"listeners",new Map);Bn(this,"reconnectTimer",null);Bn(this,"url");this.url=f||W1}connect(){var f;((f=this.ws)==null?void 0:f.readyState)!==WebSocket.OPEN&&(this.ws=new WebSocket(this.url),this.ws.onopen=()=>{this.emit("connected",{message:"WebSocket connected"})},this.ws.onmessage=r=>{try{const o=JSON.parse(r.data);this.emit(o.type,o.data||o)}catch{}},this.ws.onclose=()=>{this.emit("disconnected",{}),this.scheduleReconnect()},this.ws.onerror=()=>{var r;(r=this.ws)==null||r.close()})}scheduleReconnect(){this.reconnectTimer||(this.reconnectTimer=setTimeout(()=>{this.reconnectTimer=null,this.connect()},3e3))}on(f,r){return this.listeners.has(f)||this.listeners.set(f,new Set),this.listeners.get(f).add(r),()=>{var o;return(o=this.listeners.get(f))==null?void 0:o.delete(r)}}emit(f,r){var o,d;(o=this.listeners.get(f))==null||o.forEach(h=>h(r)),(d=this.listeners.get("*"))==null||d.forEach(h=>h({type:f,data:r}))}send(f){var r;((r=this.ws)==null?void 0:r.readyState)===WebSocket.OPEN&&this.ws.send(JSON.stringify(f))}disconnect(){var f;this.reconnectTimer&&(clearTimeout(this.reconnectTimer),this.reconnectTimer=null),(f=this.ws)==null||f.close(),this.ws=null}}const Vf="pl_auth_token",$h=E.createContext({user:null,loading:!0,login:()=>{},logout:()=>{},isLocalMode:!0});function P1(i,f,r){f(!1),r({email:i.sub||i.email||"",name:i.name||"",avatar_url:i.avatar||"",authenticated:!0})}function tg({children:i}){const[f,r]=E.useState(null),[o,d]=E.useState(!0),[h,g]=E.useState(!0),_=Ff(),S=je();E.useEffect(()=>{let j=!1;async function w(){const Z=new URLSearchParams(window.location.search),Y=Z.get("token"),G=Z.get("code");if(Y)localStorage.setItem(Vf,Y),window.history.replaceState({},"",window.location.pathname);else if(G)try{const B=sessionStorage.getItem("pl_oauth_provider")||"github",F=sessionStorage.getItem("pl_oauth_state")||"";sessionStorage.removeItem("pl_oauth_state"),sessionStorage.removeItem("pl_oauth_provider");let K;if(B==="google"?K=await qa.googleCallback(G,F):K=await qa.githubCallback(G,F),!j){localStorage.setItem(Vf,K.token),window.history.replaceState({},"",window.location.pathname),g(!1),r({email:K.user.email,name:K.user.name,avatar_url:K.user.avatar_url,authenticated:!0}),d(!1);return}}catch{window.history.replaceState({},"",window.location.pathname)}try{const B=await qa.getMe();if(j)return;B.local_mode?(g(!0),r(null)):B.authenticated?P1(B,g,r):(g(!1),r(null))}catch{if(j)return;g(!0),r(null)}finally{j||d(!1)}}return w(),()=>{j=!0}},[]);const y=E.useCallback(j=>{j==="github"?qa.getGitHubAuthUrl().then(w=>{try{const Y=new URL(w.url).searchParams.get("state");Y&&(sessionStorage.setItem("pl_oauth_state",Y),sessionStorage.setItem("pl_oauth_provider","github"))}catch{}window.location.href=w.url}).catch(()=>{}):j==="google"&&qa.getGoogleAuthUrl().then(w=>{try{const Y=new URL(w.url).searchParams.get("state");Y&&(sessionStorage.setItem("pl_oauth_state",Y),sessionStorage.setItem("pl_oauth_provider","google"))}catch{}window.location.href=w.url}).catch(()=>{})},[]),C=E.useCallback(()=>{localStorage.removeItem(Vf),r(null),S.pathname!=="/login"&&_("/login",{replace:!0})},[_,S.pathname]),z=E.useMemo(()=>({user:f,loading:o,login:y,logout:C,isLocalMode:h}),[f,o,y,C,h]);return O.jsx($h.Provider,{value:z,children:i})}function kh(){return E.useContext($h)}const Th="pl_sidebar_collapsed",eg=[{to:"/",label:"Home",icon:N1},{to:"/projects",label:"Projects",icon:C1},{to:"/templates",label:"Templates",icon:D1}],lg=[{to:"/settings",label:"Settings",icon:Vh}];function ag(){const[i,f]=E.useState(typeof window<"u"?window.innerWidth<768:!1);return E.useEffect(()=>{const r=()=>f(window.innerWidth<768);return window.addEventListener("resize",r),()=>window.removeEventListener("resize",r)},[]),i}function ng({wsConnected:i,version:f}){const r=ag(),o=je(),[d,h]=E.useState(!1),[g,_]=E.useState(()=>{try{return localStorage.getItem(Th)==="1"}catch{return!1}});E.useEffect(()=>{try{localStorage.setItem(Th,g?"1":"0")}catch{}},[g]),E.useEffect(()=>{r&&h(!1)},[o.pathname,r]);const S=r?d:!g,y=S?240:64,C=j=>["flex items-center gap-3 px-3 py-2 text-sm transition-colors rounded-[5px]",!S&&"justify-center",j?"bg-[#553DE9]/8 text-[#553DE9] font-medium border-l-2 border-[#553DE9]":"text-[#36342E] hover:bg-[#F8F4F0]"].filter(Boolean).join(" "),z=O.jsxs("aside",{className:"flex flex-col h-full border-r border-[#ECEAE3] bg-white transition-[width] duration-200",style:{width:y,minWidth:y},children:[O.jsxs("div",{className:"flex items-center justify-between px-4 h-14 border-b border-[#ECEAE3]",children:[S&&O.jsxs("div",{className:"flex flex-col",children:[O.jsx("span",{className:"font-heading text-lg font-bold leading-tight text-[#36342E]",children:"Purple Lab"}),O.jsx("span",{className:"text-xs text-[#6B6960]",children:"Powered by Loki"})]}),r?O.jsx("button",{type:"button","aria-label":d?"Close menu":"Open menu",title:d?"Close menu":"Open menu",onClick:()=>h(!d),className:"inline-flex items-center justify-center w-7 h-7 rounded-[3px] text-[#939084] hover:bg-[#F8F4F0] transition-colors",children:d?O.jsx(Kh,{size:16}):O.jsx(L1,{size:16})}):O.jsx("button",{type:"button","aria-label":g?"Expand sidebar":"Collapse sidebar",title:g?"Expand sidebar":"Collapse sidebar",onClick:()=>_(!g),className:"inline-flex items-center justify-center w-7 h-7 rounded-[3px] text-[#939084] hover:bg-[#F8F4F0] transition-colors",children:g?O.jsx(V1,{size:16}):O.jsx(Q1,{size:16})})]}),O.jsxs("nav",{className:"flex-1 px-2 py-3 flex flex-col gap-1","aria-label":"Main navigation",children:[eg.map(j=>O.jsxs(di,{to:j.to,end:j.to==="/",className:({isActive:w})=>C(w),title:S?void 0:j.label,children:[O.jsx(j.icon,{size:18}),S&&O.jsx("span",{children:j.label})]},j.to)),O.jsx("div",{className:"my-2 border-t border-[#ECEAE3]"}),lg.map(j=>O.jsxs(di,{to:j.to,className:({isActive:w})=>C(w),title:S?void 0:j.label,children:[O.jsx(j.icon,{size:18}),S&&O.jsx("span",{children:j.label})]},j.to))]}),O.jsxs("div",{className:"px-3 py-3 border-t border-[#ECEAE3] flex flex-col gap-2",children:[O.jsx(ug,{collapsed:!S}),O.jsxs("div",{className:["flex items-center gap-2 text-xs",!S&&"justify-center"].filter(Boolean).join(" "),children:[O.jsx("span",{className:`w-2 h-2 rounded-full flex-shrink-0 ${i?"bg-[#1FC5A8]":"bg-[#C45B5B]"}`}),S&&O.jsx("span",{className:"text-[#6B6960]",children:i?"Connected":"Disconnected"})]}),S&&f&&O.jsxs("span",{className:"text-xs text-[#6B6960]",children:["v",f]}),O.jsxs("a",{href:"https://www.autonomi.dev/docs",target:"_blank",rel:"noopener noreferrer",className:["flex items-center gap-2 text-xs text-[#6B6960] hover:text-[#36342E] transition-colors",!S&&"justify-center"].filter(Boolean).join(" "),title:S?void 0:"Documentation",children:[O.jsx(E1,{size:14}),S&&O.jsx("span",{children:"Docs"})]})]})]});return r&&d?O.jsxs(O.Fragment,{children:[O.jsx("div",{className:"fixed inset-0 z-40 bg-ink/20",onClick:()=>h(!1)}),O.jsx("div",{className:"fixed inset-y-0 left-0 z-50",children:z})]}):z}function ug({collapsed:i}){const{user:f,logout:r,isLocalMode:o}=kh(),[d,h]=E.useState(!1),g=E.useRef(null);return E.useEffect(()=>{function _(S){g.current&&!g.current.contains(S.target)&&h(!1)}if(d)return document.addEventListener("mousedown",_),()=>document.removeEventListener("mousedown",_)},[d]),o||!f?O.jsxs("div",{className:["flex items-center gap-2 text-xs",i&&"justify-center"].filter(Boolean).join(" "),title:i?"Local Mode":void 0,children:[O.jsx(G1,{size:14,className:"text-[#939084] flex-shrink-0"}),!i&&O.jsx("span",{className:"text-[#939084]",children:"Local Mode"})]}):O.jsxs("div",{className:"relative",ref:g,"data-testid":"user-section",children:[O.jsxs("button",{type:"button",onClick:()=>h(!d),className:["flex items-center gap-2 w-full text-left text-xs rounded-[3px] py-1 px-1 hover:bg-[#F8F4F0] transition-colors",i&&"justify-center"].filter(Boolean).join(" "),title:i?f.name||f.email:void 0,children:[f.avatar_url?O.jsx("img",{src:f.avatar_url,alt:"",className:"w-5 h-5 rounded-full flex-shrink-0"}):O.jsx("div",{className:"w-5 h-5 rounded-full bg-[#553DE9] flex items-center justify-center text-white text-[10px] font-bold flex-shrink-0",children:(f.name||f.email||"?")[0].toUpperCase()}),!i&&O.jsxs(O.Fragment,{children:[O.jsx("span",{className:"text-[#36342E] truncate flex-1",children:f.name||f.email}),O.jsx(T1,{size:12,className:`text-[#939084] transition-transform ${d?"":"rotate-180"}`})]})]}),d&&O.jsxs("div",{className:"absolute bottom-full left-0 mb-1 w-48 bg-white border border-[#ECEAE3] rounded-lg shadow-lg py-1 z-50",children:[O.jsxs("div",{className:"px-3 py-2 border-b border-[#ECEAE3]",children:[O.jsx("p",{className:"text-xs font-medium text-[#36342E] truncate",children:f.name}),O.jsx("p",{className:"text-xs text-[#939084] truncate",children:f.email})]}),O.jsxs(di,{to:"/settings",onClick:()=>h(!1),className:"flex items-center gap-2 px-3 py-2 text-xs text-[#36342E] hover:bg-[#F8F4F0] transition-colors",children:[O.jsx(Vh,{size:14}),"Settings"]}),O.jsxs("button",{type:"button",onClick:()=>{h(!1),r()},className:"flex items-center gap-2 px-3 py-2 text-xs text-[#C45B5B] hover:bg-[#F8F4F0] transition-colors w-full text-left",children:[O.jsx(H1,{size:14}),"Sign out"]})]})]})}const zh="pl_onboarding_complete",wn=[{icon:R1,title:"Write your PRD",description:"Describe what you want to build, or choose a template to get started quickly."},{icon:$1,title:"Use the terminal",description:"Run commands directly in the integrated terminal to install dependencies or debug."},{icon:_1,title:"Preview in real-time",description:"Switch to the Preview tab to see your app running with live reload."},{icon:Y1,title:"Iterate with AI Chat",description:"Ask the AI to modify, fix, or explain your code in the chat panel."}];function ig(){const[i,f]=E.useState(!1),[r,o]=E.useState(0);E.useEffect(()=>{try{localStorage.getItem(zh)!=="1"&&f(!0)}catch{}},[]);const d=()=>{f(!1);try{localStorage.setItem(zh,"1")}catch{}},h=()=>{r<wn.length-1?o(r+1):d()};if(!i)return null;const g=wn[r],_=g.icon;return O.jsx("div",{className:"fixed inset-0 z-50 flex items-center justify-center bg-ink/30",children:O.jsxs("div",{className:"bg-card rounded-card shadow-card-hover border border-border w-full max-w-sm mx-4",children:[O.jsxs("div",{className:"flex items-center justify-between px-5 pt-5 pb-2",children:[O.jsxs("span",{className:"text-[11px] font-mono text-muted-accessible",children:[r+1," / ",wn.length]}),O.jsx("button",{onClick:d,className:"text-muted hover:text-ink transition-colors p-1 rounded-btn hover:bg-hover",title:"Skip onboarding",children:O.jsx(Kh,{size:14})})]}),O.jsxs("div",{className:"px-5 pb-4 text-center",children:[O.jsx("div",{className:"w-12 h-12 rounded-full bg-primary/10 flex items-center justify-center mx-auto mb-4",children:O.jsx(_,{size:24,className:"text-primary"})}),O.jsx("h3",{className:"text-sm font-heading font-bold text-ink mb-1",children:g.title}),O.jsx("p",{className:"text-xs text-muted-accessible leading-relaxed",children:g.description})]}),O.jsx("div",{className:"flex items-center justify-center gap-1.5 pb-4",children:wn.map((S,y)=>O.jsx("div",{className:`w-1.5 h-1.5 rounded-full transition-colors ${y===r?"bg-primary":"bg-border"}`},y))}),O.jsxs("div",{className:"flex items-center justify-between px-5 py-3 border-t border-border",children:[O.jsx("button",{onClick:d,className:"text-xs text-muted hover:text-ink transition-colors",children:"Skip"}),O.jsx("button",{onClick:h,className:"inline-flex items-center gap-1.5 px-4 py-1.5 text-xs font-medium rounded-btn bg-primary text-white hover:bg-primary-hover transition-colors",children:r<wn.length-1?O.jsxs(O.Fragment,{children:["Next",O.jsx(S1,{size:12})]}):"Get Started"})]})]})})}function cg(i){const[f,r]=E.useState(!1),o=E.useRef(null),d=E.useRef(i);E.useEffect(()=>{d.current=i},[i]),E.useEffect(()=>{const g=new I1;return o.current=g,g.on("connected",()=>r(!0)),g.on("disconnected",()=>{r(!1),d.current&&d.current(null)}),g.on("state_update",_=>{d.current&&_&&typeof _=="object"&&d.current(_)}),g.connect(),()=>{g.disconnect(),o.current=null}},[]);const h=E.useCallback((g,_)=>{var S;return((S=o.current)==null?void 0:S.on(g,_))||(()=>{})},[]);return{connected:f,subscribe:h}}function fg(){const[i,f]=E.useState(""),{connected:r}=cg(()=>{});return E.useEffect(()=>{qa.getStatus().then(o=>{f(o.version||"")}).catch(()=>{})},[]),O.jsxs("div",{className:"flex h-screen bg-[#FAF9F6]",children:[O.jsx(ig,{}),O.jsx("a",{href:"#main-content",className:"sr-only focus:not-sr-only focus:absolute focus:z-50 focus:top-2 focus:left-2 focus:px-4 focus:py-2 focus:bg-white focus:text-[#553DE9] focus:rounded-[3px] focus:shadow-card",children:"Skip to main content"}),O.jsx(ng,{wsConnected:r,version:i}),O.jsx("main",{id:"main-content",className:"flex-1 overflow-auto",children:O.jsx(Mv,{})})]})}function _h({children:i}){const{user:f,loading:r,isLocalMode:o}=kh();return r?O.jsx("div",{className:"h-screen bg-[#FAF9F6] flex items-center justify-center text-[#6B6960] text-sm",children:"Loading..."}):o?O.jsx(O.Fragment,{children:i}):f?O.jsx(O.Fragment,{children:i}):O.jsx(Cv,{to:"/login",replace:!0})}function hi({variant:i="text",width:f,height:r,className:o=""}){const d="animate-pulse bg-[#ECEAE3]/60 rounded";if(i==="circle"){const h=f||"2rem";return O.jsx("div",{className:`${d} rounded-full flex-shrink-0 ${o}`,style:{width:h,height:h}})}return i==="block"?O.jsx("div",{className:`${d} rounded-btn ${o}`,style:{width:f||"100%",height:r||"4rem"}}):O.jsx("div",{className:`${d} rounded-btn ${o}`,style:{width:f||"100%",height:r||"0.75rem"}})}function bg(){return O.jsx("div",{className:"p-4 space-y-3",children:[...Array(12)].map((i,f)=>O.jsxs("div",{className:"flex items-center gap-3",children:[O.jsx(hi,{variant:"text",width:"1.5rem",height:"10px",className:"flex-shrink-0 opacity-40"}),O.jsx(hi,{variant:"text",width:`${20+Math.random()*60}%`,height:"10px"})]},f))})}typeof window<"u"&&window.addEventListener("beforeunload",i=>{delete i.returnValue});const og=E.lazy(()=>Pl(()=>import("./HomePage-DwxAu0-p.js"),__vite__mapDeps([0,1,2,3]))),sg=E.lazy(()=>Pl(()=>import("./ProjectPage-EXVpaoai.js"),__vite__mapDeps([4,3,5,2,6,7,8]))),rg=E.lazy(()=>Pl(()=>import("./ProjectsPage-Xs5iw-3h.js"),__vite__mapDeps([9,5,10,1,2,7]))),dg=E.lazy(()=>Pl(()=>import("./TemplatesPage-YWtOXUNW.js"),__vite__mapDeps([11,10,1,2]))),hg=E.lazy(()=>Pl(()=>import("./SettingsPage-B_5SorV_.js"),__vite__mapDeps([12,10,7]))),mg=E.lazy(()=>Pl(()=>import("./LoginPage-DancuhW_.js"),[])),yg=E.lazy(()=>Pl(()=>import("./NotFoundPage-BPHM_nY0.js"),__vite__mapDeps([13,6])));function Il(){return O.jsxs("div",{className:"h-screen bg-[#FAF9F6] flex flex-col items-center justify-center gap-3",children:[O.jsx(hi,{variant:"block",width:"200px",height:"24px"}),O.jsx(hi,{variant:"text",width:"140px",height:"12px",className:"opacity-50"})]})}function vg(){return O.jsx(tg,{children:O.jsxs(Uv,{children:[O.jsx(il,{path:"/login",element:O.jsx(E.Suspense,{fallback:O.jsx(Il,{}),children:O.jsx(mg,{})})}),O.jsx(il,{path:"/project/:sessionId",element:O.jsx(_h,{children:O.jsx(E.Suspense,{fallback:O.jsx(Il,{}),children:O.jsx(sg,{})})})}),O.jsxs(il,{element:O.jsx(_h,{children:O.jsx(fg,{})}),children:[O.jsx(il,{path:"/",element:O.jsx(E.Suspense,{fallback:O.jsx(Il,{}),children:O.jsx(og,{})})}),O.jsx(il,{path:"/projects",element:O.jsx(E.Suspense,{fallback:O.jsx(Il,{}),children:O.jsx(rg,{})})}),O.jsx(il,{path:"/templates",element:O.jsx(E.Suspense,{fallback:O.jsx(Il,{}),children:O.jsx(dg,{})})}),O.jsx(il,{path:"/settings",element:O.jsx(E.Suspense,{fallback:O.jsx(Il,{}),children:O.jsx(hg,{})})}),O.jsx(il,{path:"*",element:O.jsx(E.Suspense,{fallback:O.jsx(Il,{}),children:O.jsx(yg,{})})})]})]})})}N0.createRoot(document.getElementById("root")).render(O.jsx(E.StrictMode,{children:O.jsx(l1,{children:O.jsx(vg,{})})}));export{S1 as A,E1 as B,_1 as E,R1 as F,N1 as H,Xh as L,Y1 as M,Vh as S,$1 as T,pg as W,Kh as X,qa as a,cg as b,Jt as c,bg as d,hi as e,Sg as f,kh as g,O as j,E as r,Ff as u};
186
+ */const k1=[["path",{d:"M18 6 6 18",key:"1bl5f8"}],["path",{d:"m6 6 12 12",key:"d8bk6v"}]],Kh=Jt("x",k1),Jh=`${window.location.origin}/api`,W1=`${window.location.protocol==="https:"?"wss:":"ws:"}//${window.location.host}/ws`;function F1(){const i={"Content-Type":"application/json"};try{const f=localStorage.getItem("pl_auth_token");f&&(i.Authorization=`Bearer ${f}`)}catch{}return i}async function $(i,f){const r=await fetch(`${Jh}${i}`,{...f,headers:{...F1(),...f==null?void 0:f.headers}});if(!r.ok){const o=await r.text().catch(()=>"");throw new Error(`API error ${r.status}: ${r.statusText}${o?` - ${o}`:""}`)}return r.json()}const qa={startSession:i=>$("/session/start",{method:"POST",body:JSON.stringify(i)}),stopSession:()=>$("/session/stop",{method:"POST"}),pauseSession:()=>$("/session/pause",{method:"POST"}),resumeSession:()=>$("/session/resume",{method:"POST"}),getPrdPrefill:()=>$("/session/prd-prefill"),getStatus:()=>$("/session/status"),getAgents:()=>$("/session/agents"),getLogs:(i=200)=>$(`/session/logs?lines=${i}`),getMemorySummary:()=>$("/session/memory"),getChecklist:()=>$("/session/checklist"),getFiles:()=>$("/session/files"),getFileContent:i=>$(`/session/files/content?path=${encodeURIComponent(i)}`),getSessionFileContent:(i,f)=>$(`/sessions/${encodeURIComponent(i)}/file?path=${encodeURIComponent(f)}`),getTemplates:()=>$("/templates"),getTemplateContent:i=>$(`/templates/${encodeURIComponent(i)}`),planSession:(i,f)=>$("/session/plan",{method:"POST",body:JSON.stringify({prd:i,provider:f})}),generateReport:(i="markdown")=>$("/session/report",{method:"POST",body:JSON.stringify({format:i})}),shareSession:()=>$("/session/share",{method:"POST"}),getCurrentProvider:()=>$("/provider/current"),setProvider:i=>$("/provider/set",{method:"POST",body:JSON.stringify({provider:i})}),getMetrics:()=>$("/session/metrics"),getSessionsHistory:()=>$("/sessions/history"),getSessionDetail:i=>$(`/sessions/${encodeURIComponent(i)}`),deleteSession:i=>$(`/sessions/${encodeURIComponent(i)}`,{method:"DELETE"}),onboardRepo:i=>$("/session/onboard",{method:"POST",body:JSON.stringify({path:i})}),saveSessionFile:(i,f,r)=>$(`/sessions/${encodeURIComponent(i)}/file`,{method:"PUT",body:JSON.stringify({path:f,content:r})}),createSessionFile:(i,f,r="")=>$(`/sessions/${encodeURIComponent(i)}/file`,{method:"POST",body:JSON.stringify({path:f,content:r})}),deleteSessionFile:(i,f)=>$(`/sessions/${encodeURIComponent(i)}/file`,{method:"DELETE",body:JSON.stringify({path:f})}),createSessionDirectory:(i,f)=>$(`/sessions/${encodeURIComponent(i)}/directory`,{method:"POST",body:JSON.stringify({path:f})}),reviewProject:i=>$(`/sessions/${encodeURIComponent(i)}/review`,{method:"POST"}),testProject:i=>$(`/sessions/${encodeURIComponent(i)}/test`,{method:"POST"}),explainProject:i=>$(`/sessions/${encodeURIComponent(i)}/explain`,{method:"POST"}),exportProject:i=>$(`/sessions/${encodeURIComponent(i)}/export`,{method:"POST"}),fixProject:i=>$(`/sessions/${encodeURIComponent(i)}/fix`,{method:"POST"}),chatMessage:(i,f,r="quick")=>$(`/sessions/${encodeURIComponent(i)}/chat`,{method:"POST",body:JSON.stringify({message:f,mode:r})}),chatStart:(i,f,r="quick")=>$(`/sessions/${encodeURIComponent(i)}/chat`,{method:"POST",body:JSON.stringify({message:f,mode:r})}),chatPoll:(i,f)=>$(`/sessions/${encodeURIComponent(i)}/chat/${encodeURIComponent(f)}`),chatStreamUrl:(i,f)=>`${Jh}/sessions/${encodeURIComponent(i)}/chat/${encodeURIComponent(f)}/stream`,chatCancel:(i,f)=>$(`/sessions/${encodeURIComponent(i)}/chat/${encodeURIComponent(f)}/cancel`,{method:"POST"}),getPreviewInfo:i=>$(`/sessions/${encodeURIComponent(i)}/preview-info`),devserver:{start:(i,f)=>$(`/sessions/${encodeURIComponent(i)}/devserver/start`,{method:"POST",body:JSON.stringify({command:f||null})}),stop:i=>$(`/sessions/${encodeURIComponent(i)}/devserver/stop`,{method:"POST"}),status:i=>$(`/sessions/${encodeURIComponent(i)}/devserver/status`)},getSecrets:()=>$("/secrets"),setSecret:(i,f)=>$("/secrets",{method:"POST",body:JSON.stringify({key:i,value:f})}),deleteSecret:i=>$(`/secrets/${encodeURIComponent(i)}`,{method:"DELETE"}),getServices:i=>$(`/sessions/${encodeURIComponent(i)}/services`),getServiceLogs:(i,f,r=50)=>$(`/sessions/${encodeURIComponent(i)}/devserver/logs${f?`?service=${encodeURIComponent(f)}&tail=${r}`:`?tail=${r}`}`),restartService:(i,f)=>$(`/sessions/${encodeURIComponent(i)}/devserver/restart-service`,{method:"POST",body:JSON.stringify({service:f})}),getMe:()=>$("/auth/me"),getGitHubAuthUrl:()=>$("/auth/github/url"),getGoogleAuthUrl:()=>$("/auth/google/url"),githubCallback:(i,f)=>$("/auth/github/callback",{method:"POST",body:JSON.stringify({code:i,state:f})}),googleCallback:(i,f,r)=>$("/auth/google/callback",{method:"POST",body:JSON.stringify({code:i,state:f,redirect_uri:r||`${window.location.origin}${window.location.pathname}`})})};class I1{constructor(f){Bn(this,"ws",null);Bn(this,"listeners",new Map);Bn(this,"reconnectTimer",null);Bn(this,"url");this.url=f||W1}connect(){var f;((f=this.ws)==null?void 0:f.readyState)!==WebSocket.OPEN&&(this.ws=new WebSocket(this.url),this.ws.onopen=()=>{this.emit("connected",{message:"WebSocket connected"})},this.ws.onmessage=r=>{try{const o=JSON.parse(r.data);this.emit(o.type,o.data||o)}catch{}},this.ws.onclose=()=>{this.emit("disconnected",{}),this.scheduleReconnect()},this.ws.onerror=()=>{var r;(r=this.ws)==null||r.close()})}scheduleReconnect(){this.reconnectTimer||(this.reconnectTimer=setTimeout(()=>{this.reconnectTimer=null,this.connect()},3e3))}on(f,r){return this.listeners.has(f)||this.listeners.set(f,new Set),this.listeners.get(f).add(r),()=>{var o;return(o=this.listeners.get(f))==null?void 0:o.delete(r)}}emit(f,r){var o,d;(o=this.listeners.get(f))==null||o.forEach(h=>h(r)),(d=this.listeners.get("*"))==null||d.forEach(h=>h({type:f,data:r}))}send(f){var r;((r=this.ws)==null?void 0:r.readyState)===WebSocket.OPEN&&this.ws.send(JSON.stringify(f))}disconnect(){var f;this.reconnectTimer&&(clearTimeout(this.reconnectTimer),this.reconnectTimer=null),(f=this.ws)==null||f.close(),this.ws=null}}const Vf="pl_auth_token",$h=E.createContext({user:null,loading:!0,login:()=>{},logout:()=>{},isLocalMode:!0});function P1(i,f,r){f(!1),r({email:i.sub||i.email||"",name:i.name||"",avatar_url:i.avatar||"",authenticated:!0})}function tg({children:i}){const[f,r]=E.useState(null),[o,d]=E.useState(!0),[h,g]=E.useState(!0),_=Ff(),S=je();E.useEffect(()=>{let j=!1;async function w(){const Z=new URLSearchParams(window.location.search),Y=Z.get("token"),G=Z.get("code");if(Y)localStorage.setItem(Vf,Y),window.history.replaceState({},"",window.location.pathname);else if(G)try{const B=sessionStorage.getItem("pl_oauth_provider")||"github",F=sessionStorage.getItem("pl_oauth_state")||"";sessionStorage.removeItem("pl_oauth_state"),sessionStorage.removeItem("pl_oauth_provider");let K;if(B==="google"?K=await qa.googleCallback(G,F):K=await qa.githubCallback(G,F),!j){localStorage.setItem(Vf,K.token),window.history.replaceState({},"",window.location.pathname),g(!1),r({email:K.user.email,name:K.user.name,avatar_url:K.user.avatar_url,authenticated:!0}),d(!1);return}}catch{window.history.replaceState({},"",window.location.pathname)}try{const B=await qa.getMe();if(j)return;B.local_mode?(g(!0),r(null)):B.authenticated?P1(B,g,r):(g(!1),r(null))}catch{if(j)return;g(!0),r(null)}finally{j||d(!1)}}return w(),()=>{j=!0}},[]);const y=E.useCallback(j=>{j==="github"?qa.getGitHubAuthUrl().then(w=>{try{const Y=new URL(w.url).searchParams.get("state");Y&&(sessionStorage.setItem("pl_oauth_state",Y),sessionStorage.setItem("pl_oauth_provider","github"))}catch{}window.location.href=w.url}).catch(()=>{}):j==="google"&&qa.getGoogleAuthUrl().then(w=>{try{const Y=new URL(w.url).searchParams.get("state");Y&&(sessionStorage.setItem("pl_oauth_state",Y),sessionStorage.setItem("pl_oauth_provider","google"))}catch{}window.location.href=w.url}).catch(()=>{})},[]),C=E.useCallback(()=>{localStorage.removeItem(Vf),r(null),S.pathname!=="/login"&&_("/login",{replace:!0})},[_,S.pathname]),z=E.useMemo(()=>({user:f,loading:o,login:y,logout:C,isLocalMode:h}),[f,o,y,C,h]);return O.jsx($h.Provider,{value:z,children:i})}function kh(){return E.useContext($h)}const Th="pl_sidebar_collapsed",eg=[{to:"/",label:"Home",icon:N1},{to:"/projects",label:"Projects",icon:C1},{to:"/templates",label:"Templates",icon:D1}],lg=[{to:"/settings",label:"Settings",icon:Vh}];function ag(){const[i,f]=E.useState(typeof window<"u"?window.innerWidth<768:!1);return E.useEffect(()=>{const r=()=>f(window.innerWidth<768);return window.addEventListener("resize",r),()=>window.removeEventListener("resize",r)},[]),i}function ng({wsConnected:i,version:f}){const r=ag(),o=je(),[d,h]=E.useState(!1),[g,_]=E.useState(()=>{try{return localStorage.getItem(Th)==="1"}catch{return!1}});E.useEffect(()=>{try{localStorage.setItem(Th,g?"1":"0")}catch{}},[g]),E.useEffect(()=>{r&&h(!1)},[o.pathname,r]);const S=r?d:!g,y=S?240:64,C=j=>["flex items-center gap-3 px-3 py-2 text-sm transition-colors rounded-[5px]",!S&&"justify-center",j?"bg-[#553DE9]/8 text-[#553DE9] font-medium border-l-2 border-[#553DE9]":"text-[#36342E] hover:bg-[#F8F4F0]"].filter(Boolean).join(" "),z=O.jsxs("aside",{className:"flex flex-col h-full border-r border-[#ECEAE3] bg-white transition-[width] duration-200",style:{width:y,minWidth:y},children:[O.jsxs("div",{className:"flex items-center justify-between px-4 h-14 border-b border-[#ECEAE3]",children:[S&&O.jsxs("div",{className:"flex flex-col",children:[O.jsx("span",{className:"font-heading text-lg font-bold leading-tight text-[#36342E]",children:"Purple Lab"}),O.jsx("span",{className:"text-xs text-[#6B6960]",children:"Powered by Loki"})]}),r?O.jsx("button",{type:"button","aria-label":d?"Close menu":"Open menu",title:d?"Close menu":"Open menu",onClick:()=>h(!d),className:"inline-flex items-center justify-center w-7 h-7 rounded-[3px] text-[#939084] hover:bg-[#F8F4F0] transition-colors",children:d?O.jsx(Kh,{size:16}):O.jsx(L1,{size:16})}):O.jsx("button",{type:"button","aria-label":g?"Expand sidebar":"Collapse sidebar",title:g?"Expand sidebar":"Collapse sidebar",onClick:()=>_(!g),className:"inline-flex items-center justify-center w-7 h-7 rounded-[3px] text-[#939084] hover:bg-[#F8F4F0] transition-colors",children:g?O.jsx(V1,{size:16}):O.jsx(Q1,{size:16})})]}),O.jsxs("nav",{className:"flex-1 px-2 py-3 flex flex-col gap-1","aria-label":"Main navigation",children:[eg.map(j=>O.jsxs(di,{to:j.to,end:j.to==="/",className:({isActive:w})=>C(w),title:S?void 0:j.label,children:[O.jsx(j.icon,{size:18}),S&&O.jsx("span",{children:j.label})]},j.to)),O.jsx("div",{className:"my-2 border-t border-[#ECEAE3]"}),lg.map(j=>O.jsxs(di,{to:j.to,className:({isActive:w})=>C(w),title:S?void 0:j.label,children:[O.jsx(j.icon,{size:18}),S&&O.jsx("span",{children:j.label})]},j.to))]}),O.jsxs("div",{className:"px-3 py-3 border-t border-[#ECEAE3] flex flex-col gap-2",children:[O.jsx(ug,{collapsed:!S}),O.jsxs("div",{className:["flex items-center gap-2 text-xs",!S&&"justify-center"].filter(Boolean).join(" "),children:[O.jsx("span",{className:`w-2 h-2 rounded-full flex-shrink-0 ${i?"bg-[#1FC5A8]":"bg-[#C45B5B]"}`}),S&&O.jsx("span",{className:"text-[#6B6960]",children:i?"Connected":"Disconnected"})]}),S&&f&&O.jsxs("span",{className:"text-xs text-[#6B6960]",children:["v",f]}),O.jsxs("a",{href:"https://www.autonomi.dev/docs",target:"_blank",rel:"noopener noreferrer",className:["flex items-center gap-2 text-xs text-[#6B6960] hover:text-[#36342E] transition-colors",!S&&"justify-center"].filter(Boolean).join(" "),title:S?void 0:"Documentation",children:[O.jsx(E1,{size:14}),S&&O.jsx("span",{children:"Docs"})]})]})]});return r&&d?O.jsxs(O.Fragment,{children:[O.jsx("div",{className:"fixed inset-0 z-40 bg-ink/20",onClick:()=>h(!1)}),O.jsx("div",{className:"fixed inset-y-0 left-0 z-50",children:z})]}):z}function ug({collapsed:i}){const{user:f,logout:r,isLocalMode:o}=kh(),[d,h]=E.useState(!1),g=E.useRef(null);return E.useEffect(()=>{function _(S){g.current&&!g.current.contains(S.target)&&h(!1)}if(d)return document.addEventListener("mousedown",_),()=>document.removeEventListener("mousedown",_)},[d]),o||!f?O.jsxs("div",{className:["flex items-center gap-2 text-xs",i&&"justify-center"].filter(Boolean).join(" "),title:i?"Local Mode":void 0,children:[O.jsx(G1,{size:14,className:"text-[#939084] flex-shrink-0"}),!i&&O.jsx("span",{className:"text-[#939084]",children:"Local Mode"})]}):O.jsxs("div",{className:"relative",ref:g,"data-testid":"user-section",children:[O.jsxs("button",{type:"button",onClick:()=>h(!d),className:["flex items-center gap-2 w-full text-left text-xs rounded-[3px] py-1 px-1 hover:bg-[#F8F4F0] transition-colors",i&&"justify-center"].filter(Boolean).join(" "),title:i?f.name||f.email:void 0,children:[f.avatar_url?O.jsx("img",{src:f.avatar_url,alt:"",className:"w-5 h-5 rounded-full flex-shrink-0"}):O.jsx("div",{className:"w-5 h-5 rounded-full bg-[#553DE9] flex items-center justify-center text-white text-[10px] font-bold flex-shrink-0",children:(f.name||f.email||"?")[0].toUpperCase()}),!i&&O.jsxs(O.Fragment,{children:[O.jsx("span",{className:"text-[#36342E] truncate flex-1",children:f.name||f.email}),O.jsx(T1,{size:12,className:`text-[#939084] transition-transform ${d?"":"rotate-180"}`})]})]}),d&&O.jsxs("div",{className:"absolute bottom-full left-0 mb-1 w-48 bg-white border border-[#ECEAE3] rounded-lg shadow-lg py-1 z-50",children:[O.jsxs("div",{className:"px-3 py-2 border-b border-[#ECEAE3]",children:[O.jsx("p",{className:"text-xs font-medium text-[#36342E] truncate",children:f.name}),O.jsx("p",{className:"text-xs text-[#939084] truncate",children:f.email})]}),O.jsxs(di,{to:"/settings",onClick:()=>h(!1),className:"flex items-center gap-2 px-3 py-2 text-xs text-[#36342E] hover:bg-[#F8F4F0] transition-colors",children:[O.jsx(Vh,{size:14}),"Settings"]}),O.jsxs("button",{type:"button",onClick:()=>{h(!1),r()},className:"flex items-center gap-2 px-3 py-2 text-xs text-[#C45B5B] hover:bg-[#F8F4F0] transition-colors w-full text-left",children:[O.jsx(H1,{size:14}),"Sign out"]})]})]})}const zh="pl_onboarding_complete",wn=[{icon:R1,title:"Write your PRD",description:"Describe what you want to build, or choose a template to get started quickly."},{icon:$1,title:"Use the terminal",description:"Run commands directly in the integrated terminal to install dependencies or debug."},{icon:_1,title:"Preview in real-time",description:"Switch to the Preview tab to see your app running with live reload."},{icon:Y1,title:"Iterate with AI Chat",description:"Ask the AI to modify, fix, or explain your code in the chat panel."}];function ig(){const[i,f]=E.useState(!1),[r,o]=E.useState(0);E.useEffect(()=>{try{localStorage.getItem(zh)!=="1"&&f(!0)}catch{}},[]);const d=()=>{f(!1);try{localStorage.setItem(zh,"1")}catch{}},h=()=>{r<wn.length-1?o(r+1):d()};if(!i)return null;const g=wn[r],_=g.icon;return O.jsx("div",{className:"fixed inset-0 z-50 flex items-center justify-center bg-ink/30",children:O.jsxs("div",{className:"bg-card rounded-card shadow-card-hover border border-border w-full max-w-sm mx-4",children:[O.jsxs("div",{className:"flex items-center justify-between px-5 pt-5 pb-2",children:[O.jsxs("span",{className:"text-[11px] font-mono text-muted-accessible",children:[r+1," / ",wn.length]}),O.jsx("button",{onClick:d,className:"text-muted hover:text-ink transition-colors p-1 rounded-btn hover:bg-hover",title:"Skip onboarding",children:O.jsx(Kh,{size:14})})]}),O.jsxs("div",{className:"px-5 pb-4 text-center",children:[O.jsx("div",{className:"w-12 h-12 rounded-full bg-primary/10 flex items-center justify-center mx-auto mb-4",children:O.jsx(_,{size:24,className:"text-primary"})}),O.jsx("h3",{className:"text-sm font-heading font-bold text-ink mb-1",children:g.title}),O.jsx("p",{className:"text-xs text-muted-accessible leading-relaxed",children:g.description})]}),O.jsx("div",{className:"flex items-center justify-center gap-1.5 pb-4",children:wn.map((S,y)=>O.jsx("div",{className:`w-1.5 h-1.5 rounded-full transition-colors ${y===r?"bg-primary":"bg-border"}`},y))}),O.jsxs("div",{className:"flex items-center justify-between px-5 py-3 border-t border-border",children:[O.jsx("button",{onClick:d,className:"text-xs text-muted hover:text-ink transition-colors",children:"Skip"}),O.jsx("button",{onClick:h,className:"inline-flex items-center gap-1.5 px-4 py-1.5 text-xs font-medium rounded-btn bg-primary text-white hover:bg-primary-hover transition-colors",children:r<wn.length-1?O.jsxs(O.Fragment,{children:["Next",O.jsx(S1,{size:12})]}):"Get Started"})]})]})})}function cg(i){const[f,r]=E.useState(!1),o=E.useRef(null),d=E.useRef(i);E.useEffect(()=>{d.current=i},[i]),E.useEffect(()=>{const g=new I1;return o.current=g,g.on("connected",()=>r(!0)),g.on("disconnected",()=>{r(!1),d.current&&d.current(null)}),g.on("state_update",_=>{d.current&&_&&typeof _=="object"&&d.current(_)}),g.connect(),()=>{g.disconnect(),o.current=null}},[]);const h=E.useCallback((g,_)=>{var S;return((S=o.current)==null?void 0:S.on(g,_))||(()=>{})},[]);return{connected:f,subscribe:h}}function fg(){const[i,f]=E.useState(""),{connected:r}=cg(()=>{});return E.useEffect(()=>{qa.getStatus().then(o=>{f(o.version||"")}).catch(()=>{})},[]),O.jsxs("div",{className:"flex h-screen bg-[#FAF9F6]",children:[O.jsx(ig,{}),O.jsx("a",{href:"#main-content",className:"sr-only focus:not-sr-only focus:absolute focus:z-50 focus:top-2 focus:left-2 focus:px-4 focus:py-2 focus:bg-white focus:text-[#553DE9] focus:rounded-[3px] focus:shadow-card",children:"Skip to main content"}),O.jsx(ng,{wsConnected:r,version:i}),O.jsx("main",{id:"main-content",className:"flex-1 overflow-auto",children:O.jsx(Mv,{})})]})}function _h({children:i}){const{user:f,loading:r,isLocalMode:o}=kh();return r?O.jsx("div",{className:"h-screen bg-[#FAF9F6] flex items-center justify-center text-[#6B6960] text-sm",children:"Loading..."}):o?O.jsx(O.Fragment,{children:i}):f?O.jsx(O.Fragment,{children:i}):O.jsx(Cv,{to:"/login",replace:!0})}function hi({variant:i="text",width:f,height:r,className:o=""}){const d="animate-pulse bg-[#ECEAE3]/60 rounded";if(i==="circle"){const h=f||"2rem";return O.jsx("div",{className:`${d} rounded-full flex-shrink-0 ${o}`,style:{width:h,height:h}})}return i==="block"?O.jsx("div",{className:`${d} rounded-btn ${o}`,style:{width:f||"100%",height:r||"4rem"}}):O.jsx("div",{className:`${d} rounded-btn ${o}`,style:{width:f||"100%",height:r||"0.75rem"}})}function bg(){return O.jsx("div",{className:"p-4 space-y-3",children:[...Array(12)].map((i,f)=>O.jsxs("div",{className:"flex items-center gap-3",children:[O.jsx(hi,{variant:"text",width:"1.5rem",height:"10px",className:"flex-shrink-0 opacity-40"}),O.jsx(hi,{variant:"text",width:`${20+Math.random()*60}%`,height:"10px"})]},f))})}typeof window<"u"&&window.addEventListener("beforeunload",i=>{delete i.returnValue});const og=E.lazy(()=>Pl(()=>import("./HomePage-CnHzys9u.js"),__vite__mapDeps([0,1,2,3]))),sg=E.lazy(()=>Pl(()=>import("./ProjectPage-reLTN0xY.js"),__vite__mapDeps([4,3,5,2,6,7,8]))),rg=E.lazy(()=>Pl(()=>import("./ProjectsPage-BxvFm4PV.js"),__vite__mapDeps([9,5,10,1,2,7]))),dg=E.lazy(()=>Pl(()=>import("./TemplatesPage-3h_qn6Ag.js"),__vite__mapDeps([11,10,1,2]))),hg=E.lazy(()=>Pl(()=>import("./SettingsPage-B-_VC6hV.js"),__vite__mapDeps([12,10,7]))),mg=E.lazy(()=>Pl(()=>import("./LoginPage-C5TN24fk.js"),[])),yg=E.lazy(()=>Pl(()=>import("./NotFoundPage-CRF3pccO.js"),__vite__mapDeps([13,6])));function Il(){return O.jsxs("div",{className:"h-screen bg-[#FAF9F6] flex flex-col items-center justify-center gap-3",children:[O.jsx(hi,{variant:"block",width:"200px",height:"24px"}),O.jsx(hi,{variant:"text",width:"140px",height:"12px",className:"opacity-50"})]})}function vg(){return O.jsx(tg,{children:O.jsxs(Uv,{children:[O.jsx(il,{path:"/login",element:O.jsx(E.Suspense,{fallback:O.jsx(Il,{}),children:O.jsx(mg,{})})}),O.jsx(il,{path:"/project/:sessionId",element:O.jsx(_h,{children:O.jsx(E.Suspense,{fallback:O.jsx(Il,{}),children:O.jsx(sg,{})})})}),O.jsxs(il,{element:O.jsx(_h,{children:O.jsx(fg,{})}),children:[O.jsx(il,{path:"/",element:O.jsx(E.Suspense,{fallback:O.jsx(Il,{}),children:O.jsx(og,{})})}),O.jsx(il,{path:"/projects",element:O.jsx(E.Suspense,{fallback:O.jsx(Il,{}),children:O.jsx(rg,{})})}),O.jsx(il,{path:"/templates",element:O.jsx(E.Suspense,{fallback:O.jsx(Il,{}),children:O.jsx(dg,{})})}),O.jsx(il,{path:"/settings",element:O.jsx(E.Suspense,{fallback:O.jsx(Il,{}),children:O.jsx(hg,{})})}),O.jsx(il,{path:"*",element:O.jsx(E.Suspense,{fallback:O.jsx(Il,{}),children:O.jsx(yg,{})})})]})]})})}N0.createRoot(document.getElementById("root")).render(O.jsx(E.StrictMode,{children:O.jsx(l1,{children:O.jsx(vg,{})})}));export{S1 as A,E1 as B,_1 as E,R1 as F,N1 as H,Xh as L,Y1 as M,Vh as S,$1 as T,pg as W,Kh as X,qa as a,cg as b,Jt as c,bg as d,hi as e,Sg as f,kh as g,O as j,E as r,Ff as u};
@@ -8,7 +8,7 @@
8
8
  <link rel="preconnect" href="https://fonts.googleapis.com">
9
9
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
10
10
  <link href="https://fonts.googleapis.com/css2?family=DM+Serif+Display&family=Inter:wght@400;500;600;700;800&family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet">
11
- <script type="module" crossorigin src="/assets/index-DyZl3sXe.js"></script>
11
+ <script type="module" crossorigin src="/assets/index-B79IKKoY.js"></script>
12
12
  <link rel="stylesheet" crossorigin href="/assets/index-BChSxZgM.css">
13
13
  </head>
14
14
  <body class="bg-background text-ink font-sans antialiased">
package/web-app/server.py CHANGED
@@ -1088,6 +1088,7 @@ class DevServerManager:
1088
1088
 
1089
1089
  asyncio.create_task(self._monitor_output(session_id))
1090
1090
  asyncio.create_task(self._monitor_backend_output(session_id))
1091
+ asyncio.create_task(self._continuous_log_monitor(session_id))
1091
1092
 
1092
1093
  # Wait for either frontend or backend port (up to 30s)
1093
1094
  for _ in range(60):
@@ -1238,6 +1239,7 @@ class DevServerManager:
1238
1239
  self.servers[session_id] = server_info
1239
1240
 
1240
1241
  asyncio.create_task(self._monitor_output(session_id))
1242
+ asyncio.create_task(self._continuous_log_monitor(session_id))
1241
1243
 
1242
1244
  # For Docker projects, also start the service health monitor
1243
1245
  if framework == "docker":
@@ -1408,6 +1410,170 @@ class DevServerManager:
1408
1410
  if fe_proc and fe_proc.poll() is not None:
1409
1411
  info["status"] = "error"
1410
1412
 
1413
+ async def _continuous_log_monitor(self, session_id: str) -> None:
1414
+ """Continuously monitor dev server output for errors and auto-fix.
1415
+
1416
+ This runs alongside _monitor_output and watches for error patterns
1417
+ in the accumulated log lines. When errors are detected while the
1418
+ process is still running (not yet crashed), it triggers the AI
1419
+ provider to diagnose and fix the issue proactively.
1420
+ """
1421
+ info = self.servers.get(session_id)
1422
+ if not info:
1423
+ return
1424
+
1425
+ last_error_check: float = 0
1426
+ error_cooldown = 30 # Don't check more than every 30 seconds
1427
+
1428
+ while True:
1429
+ info = self.servers.get(session_id)
1430
+ if not info:
1431
+ break
1432
+
1433
+ await asyncio.sleep(5)
1434
+
1435
+ now = time.time()
1436
+ if now - last_error_check < error_cooldown:
1437
+ continue
1438
+
1439
+ # Check recent output for error indicators
1440
+ recent_lines = info.get("output_lines", [])[-30:]
1441
+ if not recent_lines:
1442
+ continue
1443
+
1444
+ recent_text = "\n".join(recent_lines)
1445
+
1446
+ # Look for error indicators in the output
1447
+ error_indicators = [
1448
+ "Error:", "ERROR", "FATAL", "error:", "failed",
1449
+ "TypeError:", "SyntaxError:", "ModuleNotFoundError:",
1450
+ "Cannot find module", "ENOENT", "EACCES", "EADDRINUSE",
1451
+ "Connection refused", "Build failed", "Compilation failed",
1452
+ "npm ERR!", "pip install failed",
1453
+ ]
1454
+
1455
+ has_error = any(indicator in recent_text for indicator in error_indicators)
1456
+
1457
+ # Also check if the process exited
1458
+ proc = info.get("process")
1459
+ process_dead = proc and proc.poll() is not None
1460
+
1461
+ if not has_error and not process_dead:
1462
+ continue
1463
+
1464
+ last_error_check = now
1465
+
1466
+ # Don't fix if already fixing (either from this monitor or _auto_fix)
1467
+ if info.get("_auto_fixing"):
1468
+ continue
1469
+
1470
+ # Don't fix if the _monitor_output auto-fix already handled it
1471
+ # (process exited = _monitor_output triggers _auto_fix, skip here)
1472
+ if process_dead:
1473
+ continue
1474
+
1475
+ project_dir = info.get("project_dir", ".")
1476
+
1477
+ logger.info("Error detected in dev server output for session %s, triggering AI fix", session_id)
1478
+
1479
+ # Broadcast to frontend that we are auto-fixing
1480
+ try:
1481
+ await _broadcast({
1482
+ "type": "auto_fix",
1483
+ "data": {
1484
+ "session_id": session_id,
1485
+ "status": "detecting",
1486
+ "message": "Error detected in dev server output. AI is analyzing...",
1487
+ }
1488
+ })
1489
+ except Exception:
1490
+ pass
1491
+
1492
+ # Gather context and trigger fix
1493
+ info["_auto_fixing"] = True
1494
+ try:
1495
+ # Get Docker context if applicable
1496
+ docker_ctx: dict = {}
1497
+ if info.get("framework") == "docker":
1498
+ try:
1499
+ docker_ctx = await _gather_docker_context(Path(project_dir))
1500
+ except Exception:
1501
+ pass
1502
+
1503
+ # Build AI prompt with full context
1504
+ compose_content = ""
1505
+ compose_file = Path(project_dir) / "docker-compose.yml"
1506
+ if not compose_file.exists():
1507
+ compose_file = Path(project_dir) / "docker-compose.yaml"
1508
+ if compose_file.exists():
1509
+ try:
1510
+ compose_content = compose_file.read_text(errors="replace")[:5000]
1511
+ except OSError:
1512
+ pass
1513
+
1514
+ error_lines = "\n".join(info.get("output_lines", [])[-50:])
1515
+
1516
+ fix_prompt = (
1517
+ "The dev server has errors. Analyze and fix them.\n\n"
1518
+ f"DEV SERVER OUTPUT (last 50 lines):\n{error_lines[:3000]}\n"
1519
+ )
1520
+ if docker_ctx:
1521
+ svc_status = docker_ctx.get("service_status", [])
1522
+ if svc_status:
1523
+ fix_prompt += f"\nDOCKER SERVICE STATUS: {json.dumps(svc_status)}\n"
1524
+ if compose_content:
1525
+ fix_prompt += f"\nDOCKER-COMPOSE.YML:\n{compose_content}\n"
1526
+ fix_prompt += (
1527
+ f"\nPROJECT DIRECTORY: {project_dir}\n\n"
1528
+ "INSTRUCTIONS:\n"
1529
+ "1. Analyze the error in the output above\n"
1530
+ "2. Fix the root cause (edit code, config, Dockerfile, etc.)\n"
1531
+ "3. The system will automatically restart/rebuild after your fix\n"
1532
+ "4. Make the fix work on any platform"
1533
+ )
1534
+
1535
+ # Trigger AI fix
1536
+ loki = _find_loki_cli()
1537
+ if loki:
1538
+ fix_env = {**os.environ, **_load_secrets()}
1539
+ fix_env["LOKI_MAX_ITERATIONS"] = "5"
1540
+ fix_env["LOKI_AUTO_FIX"] = "true"
1541
+
1542
+ await asyncio.to_thread(
1543
+ subprocess.run,
1544
+ [loki, "quick", fix_prompt],
1545
+ capture_output=True, text=True, cwd=project_dir, timeout=300,
1546
+ env=fix_env,
1547
+ )
1548
+
1549
+ # Rebuild if Docker
1550
+ if info.get("framework") == "docker":
1551
+ await asyncio.to_thread(
1552
+ subprocess.run,
1553
+ ["docker", "compose", "up", "-d", "--build", "--no-deps"],
1554
+ capture_output=True, cwd=project_dir, timeout=120,
1555
+ )
1556
+
1557
+ # Broadcast fix complete
1558
+ try:
1559
+ await _broadcast({
1560
+ "type": "auto_fix",
1561
+ "data": {
1562
+ "session_id": session_id,
1563
+ "status": "completed",
1564
+ "message": "AI fix applied. Rebuilding...",
1565
+ }
1566
+ })
1567
+ except Exception:
1568
+ pass
1569
+ except Exception as e:
1570
+ logger.error("Continuous log monitor fix failed: %s", e)
1571
+ finally:
1572
+ # Re-fetch info in case it was replaced during the fix
1573
+ info = self.servers.get(session_id)
1574
+ if info:
1575
+ info["_auto_fixing"] = False
1576
+
1411
1577
  async def _auto_fix(self, session_id: str, error_context: str) -> None:
1412
1578
  """Auto-fix a crashed dev server by invoking loki quick with the error."""
1413
1579
  info = self.servers.get(session_id)
@@ -4305,6 +4471,10 @@ async def get_preview_info(session_id: str) -> JSONResponse:
4305
4471
  info["preview_url"] = None
4306
4472
  info["entry_file"] = None
4307
4473
 
4474
+ # Indicate whether AI-driven continuous log monitoring is active for this session
4475
+ server_info = dev_server_manager.servers.get(session_id)
4476
+ info["ai_detected"] = server_info is not None
4477
+
4308
4478
  return JSONResponse(content=info)
4309
4479
 
4310
4480