crewswarm 0.9.0 → 0.9.2

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 (84) hide show
  1. package/README.md +2 -2
  2. package/apps/dashboard/dist/assets/{chat-core-CMoqlR6D.js → chat-core-Cx4sTxDd.js} +1 -1
  3. package/apps/dashboard/dist/assets/chat-core-Cx4sTxDd.js.br +0 -0
  4. package/apps/dashboard/dist/assets/cli-process-COMRNPqr.js.br +0 -0
  5. package/apps/dashboard/dist/assets/{components-CSUb80ze.js → components-BS9fQjE_.js} +1 -1
  6. package/apps/dashboard/dist/assets/components-BS9fQjE_.js.br +0 -0
  7. package/apps/dashboard/dist/assets/core-utils-CmOkXgzi.js +1 -0
  8. package/apps/dashboard/dist/assets/core-utils-CmOkXgzi.js.br +0 -0
  9. package/apps/dashboard/dist/assets/index-CF0aJRtC.css.br +0 -0
  10. package/apps/dashboard/dist/assets/{index-DqVVQLTW.js → index-DnClJ1ee.js} +2 -2
  11. package/apps/dashboard/dist/assets/index-DnClJ1ee.js.br +0 -0
  12. package/apps/dashboard/dist/assets/orchestration-Ca2DLWN-.js.br +0 -0
  13. package/apps/dashboard/dist/assets/{setup-wizard-D4g5DMhW.js → setup-wizard-CA0Or47w.js} +1 -1
  14. package/apps/dashboard/dist/assets/setup-wizard-CA0Or47w.js.br +0 -0
  15. package/apps/dashboard/dist/assets/{tab-agents-tab-BThdsdJY.js → tab-agents-tab-BgpIsjkw.js} +1 -1
  16. package/apps/dashboard/dist/assets/tab-agents-tab-BgpIsjkw.js.br +0 -0
  17. package/apps/dashboard/dist/assets/{tab-benchmarks-tab-DfCuAClu.js → tab-benchmarks-tab-BHjKCPm3.js} +1 -1
  18. package/apps/dashboard/dist/assets/{tab-comms-tab-eHpOSBhG.js → tab-comms-tab-kguqTIzD.js} +1 -1
  19. package/apps/dashboard/dist/assets/tab-comms-tab-kguqTIzD.js.br +0 -0
  20. package/apps/dashboard/dist/assets/{tab-contacts-tab-5LHSthJM.js → tab-contacts-tab-DiOyMYth.js} +1 -1
  21. package/apps/dashboard/dist/assets/tab-contacts-tab-DiOyMYth.js.br +0 -0
  22. package/apps/dashboard/dist/assets/{tab-engines-tab-C3DYxTwy.js → tab-engines-tab-BsdZVvU0.js} +1 -1
  23. package/apps/dashboard/dist/assets/tab-engines-tab-BsdZVvU0.js.br +0 -0
  24. package/apps/dashboard/dist/assets/{tab-memory-tab-C59BYFQD.js → tab-memory-tab-Cu6u13EQ.js} +1 -1
  25. package/apps/dashboard/dist/assets/tab-memory-tab-Cu6u13EQ.js.br +0 -0
  26. package/apps/dashboard/dist/assets/{tab-models-tab-CQzvaeVh.js → tab-models-tab-BLEjmd19.js} +1 -1
  27. package/apps/dashboard/dist/assets/tab-models-tab-BLEjmd19.js.br +0 -0
  28. package/apps/dashboard/dist/assets/{tab-pm-loop-tab-D7mnDelU.js → tab-pm-loop-tab-Bfd449B4.js} +1 -1
  29. package/apps/dashboard/dist/assets/tab-pm-loop-tab-Bfd449B4.js.br +0 -0
  30. package/apps/dashboard/dist/assets/{tab-projects-tab-C6h2Mv1K.js → tab-projects-tab-DhNWnlzt.js} +1 -1
  31. package/apps/dashboard/dist/assets/tab-projects-tab-DhNWnlzt.js.br +0 -0
  32. package/apps/dashboard/dist/assets/{tab-prompts-tab-C0wZvWK3.js → tab-prompts-tab-DVkUNaJd.js} +1 -1
  33. package/apps/dashboard/dist/assets/tab-prompts-tab-DVkUNaJd.js.br +0 -0
  34. package/apps/dashboard/dist/assets/{tab-services-tab-DBj_w3bc.js → tab-services-tab-DU_LH3uG.js} +1 -1
  35. package/apps/dashboard/dist/assets/tab-services-tab-DU_LH3uG.js.br +0 -0
  36. package/apps/dashboard/dist/assets/{tab-settings-tab-ezeqAjZk.js → tab-settings-tab-Bn4nXtDe.js} +1 -1
  37. package/apps/dashboard/dist/assets/tab-settings-tab-Bn4nXtDe.js.br +0 -0
  38. package/apps/dashboard/dist/assets/{tab-skills-tab-BYdU2whk.js → tab-skills-tab-BpY0uZHW.js} +1 -1
  39. package/apps/dashboard/dist/assets/tab-skills-tab-BpY0uZHW.js.br +0 -0
  40. package/apps/dashboard/dist/assets/{tab-spending-tab-Bg6w9t_p.js → tab-spending-tab-DEccQHnt.js} +1 -1
  41. package/apps/dashboard/dist/assets/tab-spending-tab-DEccQHnt.js.br +0 -0
  42. package/apps/dashboard/dist/assets/{tab-swarm-chat-tab-BBV9HB2X.js → tab-swarm-chat-tab-BNrd88-r.js} +1 -1
  43. package/apps/dashboard/dist/assets/tab-swarm-chat-tab-BNrd88-r.js.br +0 -0
  44. package/apps/dashboard/dist/assets/{tab-swarm-tab-ChqLlEVs.js → tab-swarm-tab-B1AcjL1W.js} +1 -1
  45. package/apps/dashboard/dist/assets/tab-swarm-tab-B1AcjL1W.js.br +0 -0
  46. package/apps/dashboard/dist/assets/{tab-usage-tab-B2UWXenJ.js → tab-usage-tab-BIOOnB-Y.js} +1 -1
  47. package/apps/dashboard/dist/assets/tab-usage-tab-BIOOnB-Y.js.br +0 -0
  48. package/apps/dashboard/dist/assets/tab-waves-tab-SaJDkb4x.js.br +0 -0
  49. package/apps/dashboard/dist/assets/{tab-workflows-tab-6QSXLJ0i.js → tab-workflows-tab-B-soSy1k.js} +1 -1
  50. package/apps/dashboard/dist/assets/tab-workflows-tab-B-soSy1k.js.br +0 -0
  51. package/apps/dashboard/dist/index.html +23 -23
  52. package/apps/dashboard/dist/index.html.br +0 -0
  53. package/apps/dashboard/dist/index.html.gz +0 -0
  54. package/apps/dashboard/index.html +71 -1
  55. package/apps/dashboard/src/app.js +5 -0
  56. package/apps/dashboard/src/core/dom.js +8 -0
  57. package/apps/dashboard/src/tabs/settings-tab.js +58 -0
  58. package/apps/vibe/.crew/agent-memory/pipeline.json +12 -1
  59. package/apps/vibe/.crew/cost.json +3 -3
  60. package/apps/vibe/.crew/json-parse-metrics.jsonl +1 -0
  61. package/apps/vibe/.crew/pipeline-metrics.jsonl +1 -0
  62. package/apps/vibe/.crew/pipeline-runs/pipeline-c1418f4e-b773-4ca1-84a3-216acf36e2f2.jsonl +5 -0
  63. package/apps/vibe/.crew/session.json +10 -1
  64. package/apps/vibe/.studio-data/project-messages/general.jsonl +3 -0
  65. package/apps/vibe/index.html +4 -2
  66. package/apps/vibe/server.mjs +75 -3
  67. package/apps/vibe/src/main.js +126 -53
  68. package/crew-lead.mjs +14 -1
  69. package/lib/bridges/cli-executor.mjs +0 -2
  70. package/lib/bridges/tmux-bridge.mjs +200 -0
  71. package/lib/chat/unified-history.mjs +1 -1
  72. package/lib/cli-process-tracker.mjs +2 -1
  73. package/lib/crew-lead/http-server.mjs +286 -1
  74. package/lib/crew-lead/wave-dispatcher.mjs +40 -3
  75. package/lib/engines/crew-cli.mjs +3 -2
  76. package/lib/engines/llm-direct.mjs +4 -1
  77. package/lib/engines/rt-envelope.mjs +14 -5
  78. package/lib/engines/runners.mjs +30 -4
  79. package/lib/runtime/config.mjs +7 -0
  80. package/lib/sessions/session-manager.mjs +287 -0
  81. package/package.json +1 -1
  82. package/scripts/bench/performance_optimization.py +81 -0
  83. package/whatsapp-bridge.mjs +54 -10
  84. package/apps/dashboard/dist/assets/core-utils-CAVnDoe1.js +0 -1
@@ -1 +1 @@
1
- import{a as e,b as t,h as n,g as s,e as o,i,j as a,s as r,p as l}from"./core-utils-CAVnDoe1.js";let c=()=>{},d=()=>{};function p({hideAllViews:e,setNavActive:t}={}){c=e||c,d=t||d}let m=e.selected||null,u=e.selectedEngine||"opencode";async function g(){const n=document.getElementById("sessions");n&&(n.innerHTML='<div style="padding:20px;">Loading…</div>');const i=n.parentElement;let a=document.getElementById("engine-selector");a||(a=document.createElement("div"),a.id="engine-selector",a.style.cssText="padding:12px 16px;border-bottom:1px solid var(--border);display:flex;align-items:center;gap:8px;",a.innerHTML='<label style="font-size:13px;font-weight:500;color:var(--text-2);">CLI:</label><select id="engine-select" style="padding:6px 10px;border:1px solid var(--border);border-radius:6px;background:var(--bg-1);color:var(--text-1);font-size:13px;cursor:pointer;"><option value="opencode">OpenCode</option><option value="claude">Claude Code</option><option value="codex">Codex CLI</option><option value="gemini">Gemini CLI</option><option value="crew-cli">crew-cli</option></select><span style="font-size:12px;color:var(--text-3);margin-left:8px;" id="session-count"></span>',i.insertBefore(a,n),document.getElementById("engine-select").addEventListener("change",n=>{u=n.target.value,e.selectedEngine=u,t(),m=null,g()}));const r=document.getElementById("engine-select");r&&(r.value=u);try{let n=function(e){if(!e||"string"!=typeof e)return null;const t=e.match(/\[?(crew-\w+)\]?/);return t?t[1]:null},i=function(e){return e&&"string"==typeof e?/\bFixer\b|fixer\s+task|fix\s+.*\.py|syntax\s+error/i.test(e)?"fixer":/\bQA\b|qa\s+audit|audit:/i.test(e)?"qa":/\bPM\b|crew-pm|roadmap\b/i.test(e)?"pm":/\bCoder\b|coder\s+task|frontend\b|backend\b/i.test(e)?"coder":/\bSecurity\b|security\s+review/i.test(e)?"security":/\bCopywriter\b|copy\s+task/i.test(e)?"copywriter":null:null},a=function(e){return e&&/^[a-z]+-[a-z]+$/.test(e)&&!e.startsWith("crew-")};const r=e.chatActiveProjectId||"general",l="/api/engine-sessions?engine="+encodeURIComponent(u)+"&projectId="+encodeURIComponent(r),c=await s(l),d=c.sessions||c||[],p=document.getElementById("sessions"),f=document.getElementById("session-count");if(p.innerHTML="",!d.length){const e={opencode:"OpenCode",claude:"Claude Code",codex:"Codex CLI",gemini:"Gemini CLI","crew-cli":"crew-cli"}[u]||u;return p.innerHTML=`<div style="padding:20px 16px;"><div style="font-size:13px;font-weight:600;margin-bottom:6px;">No ${e} sessions</div><div style="font-size:12px;color:var(--text-3);line-height:1.6;">No session history found for <strong>${e}</strong>. Run a task using this engine to see sessions here.</div></div>`,void(f&&(f.textContent=""))}f&&(f.textContent=`${d.length} session${1!==d.length?"s":""}`),!m&&d[0]&&(m=d[0].id),d.forEach(s=>{const r=document.createElement("div"),l=s.id||s.sessionId||"";r.className="row"+(l===m?" active":""),r.onclick=()=>{m=l,e.selected=l,t(),g(),x()};let c=s.title||s.slug||l,d=s.directory||"",f="";if("opencode"===u){const e=n(c),t=i(c),o=s.slug||"",r=e||(o&&!a(o)?o:null)||t,l=a(o)?" ("+o+")":"";f=r?"Assigned to: "+r+l:o?"Assigned to: "+o+" (OpenCode session)":""}else"claude"===u?d=s.file?s.file.split("/").pop().replace(".jsonl",""):"":"codex"===u?d=s.file||"":"gemini"===u?d="Project: "+l:"crew-cli"===u&&(f=s.engine+" / "+s.project,d=s.file||"");c.length>80&&(c=c.slice(0,77)+"..."),r.innerHTML="<div><strong>"+o(c)+"</strong></div>"+(d?'<div class="meta">'+o(d)+"</div>":"")+(f?'<div class="meta" style="font-size:11px;color:var(--accent);">'+o(f)+"</div>":""),p.appendChild(r)})}catch(l){const e=document.getElementById("sessions");e&&(e.innerHTML='<div class="meta" style="padding:20px; color:var(--red-hi);">Error loading sessions.</div>')}}async function x(){const e=document.getElementById("messages");if(m)try{if("opencode"===u){const t=await s("/api/messages?session="+encodeURIComponent(m));e.innerHTML="",t.slice(-40).forEach(t=>{const n=(t.parts||[]).filter(e=>"text"===e.type).map(e=>e.text).join("").trim();if(!n)return;const s=document.createElement("div");s.className="msg "+("assistant"===(t.info&&t.info.role)?"a":"u"),s.innerHTML='<div class="meta">'+(t.info&&t.info.role)+" • "+i(a(t.info))+'</div><div class="t"></div>',s.querySelector(".t").textContent=n,e.appendChild(s)})}else{const t={claude:"/api/claude-sessions",codex:"/api/codex-sessions",gemini:"/api/gemini-sessions","crew-cli":"/api/crew-cli-sessions"}[u];if(!t)return void(e.innerHTML='<div class="meta">Engine not supported</div>');const n=await s(t),o=(n.sessions||n||[]).find(e=>e.id===m||e.sessionId===m);if(!o||!o.messages)return void(e.innerHTML='<div class="meta">No messages found</div>');e.innerHTML="",o.messages.slice(-40).forEach(t=>{const n=document.createElement("div");n.className="msg "+("assistant"===t.role?"a":"u");const s=t.ts?new Date(t.ts).toLocaleString():"";n.innerHTML='<div class="meta">'+t.role+(s?" • "+s:"")+'</div><div class="t"></div>',n.querySelector(".t").textContent=t.text||"",e.appendChild(n)})}e.scrollTop=e.scrollHeight}catch(t){e&&(e.innerHTML='<div class="meta">Error: '+t.message+"</div>")}else e&&(e.innerHTML='<div class="meta">No session selected.</div>')}function f(){c(),document.getElementById("sessionsView").classList.add("active"),d("navSwarm"),e.activeTab="swarm",t();const s=document.getElementById("sessions");s&&s.children.length>1?n("swarm"):(g(),x())}let y=!1,v="tasks",h="";const b=new Set(["agent.heartbeat","agent.online","agent.offline"]),w=new Set(["task.dispatched","task.done","task.completed","task.failed","task.cancelled","task.started","task.reply"]);function C(e){if(b.has(e.type))return!1;const t=e.payload||{},n=t.reply||t.prompt||t.message||t.content||"";if(!n||"run_task"===n)return!1;if("tasks"===v&&!w.has(e.type))return!1;if("replies"===v&&!(t.reply||t.message||t.content))return!1;if(h){const t=h.toLowerCase();if(!((e.from||"").toLowerCase().includes(t)||(e.to||"").toLowerCase().includes(t)||n.toLowerCase().includes(t)||(e.type||"").toLowerCase().includes(t)))return!1}return!0}const E={"task.dispatched":{color:"var(--purple)",label:"dispatched"},"task.started":{color:"var(--amber)",label:"started"},"task.done":{color:"var(--green-hi)",label:"done"},"task.completed":{color:"var(--green-hi)",label:"completed"},"task.reply":{color:"var(--accent)",label:"reply"},"task.failed":{color:"var(--red-hi)",label:"failed"},"task.cancelled":{color:"var(--text-3)",label:"cancelled"}};async function L(){if(y)return;const e=document.getElementById("rtMessages"),t=document.getElementById("rtView");if(!e||!t)return;const n=0===e.children.length;if(n){const t=document.createElement("div");t.style.cssText="padding:20px;",t.textContent="Loading…",e.replaceChildren(t)}const o=(await s("/api/rt-messages")).filter(C).slice(-100),i=o.map(e=>{const t=e.payload||{},n=t.reply||t.prompt||t.message||t.content||"";return`${e.type}|${e.from}|${e.to}|${n.slice(0,100)}`}).join("::");if(i===window._rtLastHash&&!n)return;window._rtLastHash=i;const a=()=>t.scrollHeight-t.scrollTop-t.clientHeight<100,r=a(),l=t.scrollTop,c=document.createDocumentFragment();if(o.length){const e=document.createElement("div");e.style.cssText="display:grid;grid-template-columns:auto auto 1fr auto;gap:10px;padding:4px 10px 6px;font-size:10px;font-weight:600;color:var(--text-3);letter-spacing:.06em;text-transform:uppercase;border-bottom:2px solid var(--border);margin-bottom:2px;",["Agent","Phase","Summary","Time"].forEach(t=>{const n=document.createElement("span");n.textContent=t,e.appendChild(n)}),c.appendChild(e),o.forEach(e=>c.appendChild(function(e){const t=e.payload||{},n=t.reply||t.prompt||t.message||t.content||"",s=e.type||"",o=E[s],i=e.ts?new Date(e.ts).toLocaleTimeString([],{hour:"2-digit",minute:"2-digit"}):"",a=n.split("\n").map(e=>e.trim()).find(e=>e.length>2)||n,r=a.length>90?a.slice(0,90)+"…":a,l=n.length>r.length||n.split("\n").length>1,c=document.createElement("div");c.style.cssText="display:grid;grid-template-columns:auto auto 1fr auto;align-items:center;gap:10px;padding:7px 10px;border-radius:6px;cursor:"+(l?"pointer":"default")+";transition:background .12s;border-bottom:1px solid var(--border);",c.onmouseenter=()=>{c.style.background="var(--bg-2)"},c.onmouseleave=()=>{c.style.background=""};const d=document.createElement("div");d.style.cssText="display:flex;align-items:center;gap:5px;white-space:nowrap;min-width:0;";const p=document.createElement("span");if(p.style.cssText="font-size:11px;font-weight:600;color:var(--text-1);max-width:110px;overflow:hidden;text-overflow:ellipsis;",p.textContent=(e.from||"?").replace("crew-",""),p.title=e.from||"",d.appendChild(p),e.to&&e.to!==e.from){const t=document.createElement("span");t.style.cssText="font-size:10px;color:var(--text-3);flex-shrink:0;",t.textContent="→";const n=document.createElement("span");n.style.cssText="font-size:11px;color:var(--text-2);max-width:110px;overflow:hidden;text-overflow:ellipsis;",n.textContent=(e.to||"").replace("crew-",""),n.title=e.to||"",d.appendChild(t),d.appendChild(n)}const m=document.createElement("div");m.style.cssText="display:flex;align-items:center;gap:4px;flex-shrink:0;";const u=document.createElement("span"),g=o||{color:"var(--text-3)",label:s.split(".").pop()||s};if(u.style.cssText="font-size:10px;font-weight:600;padding:2px 7px;border-radius:20px;white-space:nowrap;flex-shrink:0;color:#fff;background:"+g.color+";letter-spacing:.03em;",u.textContent=g.label,m.appendChild(u),"task.done"===s&&t.engineUsed){const e={claude:"#e07a5f",codex:"#8338ec",cursor:"#3d405b",opencode:"#06d6a0",gemini:"#4285f4","docker-sandbox":"#0db7ed"},n={claude:"🤖",codex:"🟣",cursor:"🖱",opencode:"⚡",gemini:"✨","docker-sandbox":"🐳"},s=t.engineUsed,o=document.createElement("span");o.style.cssText="font-size:10px;font-weight:600;padding:2px 6px;border-radius:20px;white-space:nowrap;flex-shrink:0;color:#fff;background:"+(e[s]||"var(--text-3)")+";",o.textContent=(n[s]||"")+" "+s,o.title="Executed by "+s,m.appendChild(o)}const x=document.createElement("span");x.style.cssText="font-size:12px;color:var(--text-2);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0;",x.textContent=r;const f=document.createElement("div");f.style.cssText="display:flex;align-items:center;gap:6px;flex-shrink:0;";const y=document.createElement("span");if(y.style.cssText="font-size:10px;color:var(--text-3);white-space:nowrap;",y.textContent=i,f.appendChild(y),l){const e=document.createElement("span");e.style.cssText="font-size:10px;color:var(--text-3);",e.textContent="▸",f.appendChild(e)}if(c.appendChild(d),c.appendChild(m),c.appendChild(x),c.appendChild(f),l){const e=document.createElement("div");e.style.cssText="display:none;grid-column:1/-1;padding:8px 6px 4px;font-size:12px;color:var(--text-2);white-space:pre-wrap;word-break:break-word;max-height:300px;overflow-y:auto;border-top:1px solid var(--border);margin-top:4px;font-family:monospace;",e.textContent=n;const t=document.createElement("div");t.style.cssText="display:grid;grid-template-columns:1fr;border-radius:6px;overflow:hidden;border-bottom:1px solid var(--border);",c.style.borderBottom="none";let s=!1;return c.onclick=()=>{s=!s,e.style.display=s?"block":"none";const t=f.querySelector("span:last-child");t&&(t.textContent=s?"▾":"▸")},t.appendChild(c),t.appendChild(e),t}return c}(e)))}else{const e=document.createElement("div");e.style.cssText="padding:24px;text-align:center;font-size:12px;color:var(--text-3);",e.textContent="No events match the current filter.",c.appendChild(e)}if(e.replaceChildren(c),r)t.scrollTop=t.scrollHeight;else{const e=Math.max(0,t.scrollHeight-t.clientHeight);t.scrollTop=Math.min(l,e)}const d=document.getElementById("rtScrollBtn");d&&(d.style.display=a()?"none":"block"),t._scrollListenerBound||(t._scrollListenerBound=!0,t.addEventListener("scroll",()=>{d&&(d.style.display=a()?"none":"block")}))}function k(){y=!y;const e=document.getElementById("rtPauseBtn");e&&(e.textContent=y?"▶ Resume":"⏸ Pause",e.style.background=y?"var(--accent)":"",e.style.color=y?"#fff":"")}function T(){const e=document.getElementById("rtMessages");e&&(e.innerHTML='<div class="meta" style="padding:20px;text-align:center;opacity:.6;">Cleared. New messages will appear on next poll.</div>')}function I(){c(),document.getElementById("rtView").classList.add("active"),d("navRT"),function(){document.querySelectorAll(".rt-filter-chip").forEach(e=>{e.addEventListener("click",()=>{v=e.dataset.filter,document.querySelectorAll(".rt-filter-chip").forEach(t=>{const n=t===e;t.style.background=n?"var(--accent)":"transparent",t.style.color=n?"#fff":"var(--text-2)",t.classList.toggle("active",n)}),L()})});const e=document.getElementById("rtSearch");e&&e.addEventListener("input",()=>{h=e.value.trim(),L()})}(),L();const e=document.getElementById("rtScrollBtn");e&&(e.style.display="none")}async function B(){const e=document.getElementById("dlqMessages");e&&(e.innerHTML='<div style="padding:20px;">Loading…</div>');const t=await s("/api/dlq"),n=document.getElementById("dlqBadge");n&&(n.textContent=t.length,n.classList.toggle("hidden",!t.length)),e&&(e.innerHTML=t.length?t.map(e=>{const t=e.key||(e.filename||"").replace(".json","")||"?",n=o(t);return'<div class="msg dlq-item"><div class="meta"><strong>⚠️ Failed</strong> | '+(e.agent||"?")+" | "+(e.failedAt?new Date(e.failedAt).toLocaleString():"")+' <button class="replay-btn" data-action="replayDLQ" data-arg="'+n+'">Replay</button> <button data-action="deleteDLQ" data-arg="'+n+'" style="font-size:11px;padding:3px 8px;border-radius:4px;border:1px solid var(--red-hi);background:transparent;color:var(--red-hi);cursor:pointer;">Delete</button></div><div class="t">'+(e.error||"")+"</div></div>"}).join(""):'<div class="meta" style="padding:20px; text-align:center;">✓ DLQ empty</div>')}async function H(e){confirm("Replay?")&&(await l("/api/dlq/replay",{key:e}),r("Replayed"),B())}async function M(e){if(confirm("Delete this DLQ entry?"))try{await fetch("/api/dlq/"+encodeURIComponent(e),{method:"DELETE"}),r("DLQ entry deleted"),B()}catch(t){r("Failed: "+t.message,!0)}}function z(){c(),document.getElementById("dlqView").classList.add("active"),d("navDLQ"),B()}export{I as a,f as b,T as c,M as d,p as i,H as r,z as s,k as t};
1
+ import{a as e,b as t,h as n,g as s,e as o,i,j as a,s as r,p as l}from"./core-utils-CmOkXgzi.js";let c=()=>{},d=()=>{};function p({hideAllViews:e,setNavActive:t}={}){c=e||c,d=t||d}let m=e.selected||null,u=e.selectedEngine||"opencode";async function g(){const n=document.getElementById("sessions");n&&(n.innerHTML='<div style="padding:20px;">Loading…</div>');const i=n.parentElement;let a=document.getElementById("engine-selector");a||(a=document.createElement("div"),a.id="engine-selector",a.style.cssText="padding:12px 16px;border-bottom:1px solid var(--border);display:flex;align-items:center;gap:8px;",a.innerHTML='<label style="font-size:13px;font-weight:500;color:var(--text-2);">CLI:</label><select id="engine-select" style="padding:6px 10px;border:1px solid var(--border);border-radius:6px;background:var(--bg-1);color:var(--text-1);font-size:13px;cursor:pointer;"><option value="opencode">OpenCode</option><option value="claude">Claude Code</option><option value="codex">Codex CLI</option><option value="gemini">Gemini CLI</option><option value="crew-cli">crew-cli</option></select><span style="font-size:12px;color:var(--text-3);margin-left:8px;" id="session-count"></span>',i.insertBefore(a,n),document.getElementById("engine-select").addEventListener("change",n=>{u=n.target.value,e.selectedEngine=u,t(),m=null,g()}));const r=document.getElementById("engine-select");r&&(r.value=u);try{let n=function(e){if(!e||"string"!=typeof e)return null;const t=e.match(/\[?(crew-\w+)\]?/);return t?t[1]:null},i=function(e){return e&&"string"==typeof e?/\bFixer\b|fixer\s+task|fix\s+.*\.py|syntax\s+error/i.test(e)?"fixer":/\bQA\b|qa\s+audit|audit:/i.test(e)?"qa":/\bPM\b|crew-pm|roadmap\b/i.test(e)?"pm":/\bCoder\b|coder\s+task|frontend\b|backend\b/i.test(e)?"coder":/\bSecurity\b|security\s+review/i.test(e)?"security":/\bCopywriter\b|copy\s+task/i.test(e)?"copywriter":null:null},a=function(e){return e&&/^[a-z]+-[a-z]+$/.test(e)&&!e.startsWith("crew-")};const r=e.chatActiveProjectId||"general",l="/api/engine-sessions?engine="+encodeURIComponent(u)+"&projectId="+encodeURIComponent(r),c=await s(l),d=c.sessions||c||[],p=document.getElementById("sessions"),f=document.getElementById("session-count");if(p.innerHTML="",!d.length){const e={opencode:"OpenCode",claude:"Claude Code",codex:"Codex CLI",gemini:"Gemini CLI","crew-cli":"crew-cli"}[u]||u;return p.innerHTML=`<div style="padding:20px 16px;"><div style="font-size:13px;font-weight:600;margin-bottom:6px;">No ${e} sessions</div><div style="font-size:12px;color:var(--text-3);line-height:1.6;">No session history found for <strong>${e}</strong>. Run a task using this engine to see sessions here.</div></div>`,void(f&&(f.textContent=""))}f&&(f.textContent=`${d.length} session${1!==d.length?"s":""}`),!m&&d[0]&&(m=d[0].id),d.forEach(s=>{const r=document.createElement("div"),l=s.id||s.sessionId||"";r.className="row"+(l===m?" active":""),r.onclick=()=>{m=l,e.selected=l,t(),g(),x()};let c=s.title||s.slug||l,d=s.directory||"",f="";if("opencode"===u){const e=n(c),t=i(c),o=s.slug||"",r=e||(o&&!a(o)?o:null)||t,l=a(o)?" ("+o+")":"";f=r?"Assigned to: "+r+l:o?"Assigned to: "+o+" (OpenCode session)":""}else"claude"===u?d=s.file?s.file.split("/").pop().replace(".jsonl",""):"":"codex"===u?d=s.file||"":"gemini"===u?d="Project: "+l:"crew-cli"===u&&(f=s.engine+" / "+s.project,d=s.file||"");c.length>80&&(c=c.slice(0,77)+"..."),r.innerHTML="<div><strong>"+o(c)+"</strong></div>"+(d?'<div class="meta">'+o(d)+"</div>":"")+(f?'<div class="meta" style="font-size:11px;color:var(--accent);">'+o(f)+"</div>":""),p.appendChild(r)})}catch(l){const e=document.getElementById("sessions");e&&(e.innerHTML='<div class="meta" style="padding:20px; color:var(--red-hi);">Error loading sessions.</div>')}}async function x(){const e=document.getElementById("messages");if(m)try{if("opencode"===u){const t=await s("/api/messages?session="+encodeURIComponent(m));e.innerHTML="",t.slice(-40).forEach(t=>{const n=(t.parts||[]).filter(e=>"text"===e.type).map(e=>e.text).join("").trim();if(!n)return;const s=document.createElement("div");s.className="msg "+("assistant"===(t.info&&t.info.role)?"a":"u"),s.innerHTML='<div class="meta">'+(t.info&&t.info.role)+" • "+i(a(t.info))+'</div><div class="t"></div>',s.querySelector(".t").textContent=n,e.appendChild(s)})}else{const t={claude:"/api/claude-sessions",codex:"/api/codex-sessions",gemini:"/api/gemini-sessions","crew-cli":"/api/crew-cli-sessions"}[u];if(!t)return void(e.innerHTML='<div class="meta">Engine not supported</div>');const n=await s(t),o=(n.sessions||n||[]).find(e=>e.id===m||e.sessionId===m);if(!o||!o.messages)return void(e.innerHTML='<div class="meta">No messages found</div>');e.innerHTML="",o.messages.slice(-40).forEach(t=>{const n=document.createElement("div");n.className="msg "+("assistant"===t.role?"a":"u");const s=t.ts?new Date(t.ts).toLocaleString():"";n.innerHTML='<div class="meta">'+t.role+(s?" • "+s:"")+'</div><div class="t"></div>',n.querySelector(".t").textContent=t.text||"",e.appendChild(n)})}e.scrollTop=e.scrollHeight}catch(t){e&&(e.innerHTML='<div class="meta">Error: '+t.message+"</div>")}else e&&(e.innerHTML='<div class="meta">No session selected.</div>')}function f(){c(),document.getElementById("sessionsView").classList.add("active"),d("navSwarm"),e.activeTab="swarm",t();const s=document.getElementById("sessions");s&&s.children.length>1?n("swarm"):(g(),x())}let y=!1,v="tasks",h="";const b=new Set(["agent.heartbeat","agent.online","agent.offline"]),w=new Set(["task.dispatched","task.done","task.completed","task.failed","task.cancelled","task.started","task.reply"]);function C(e){if(b.has(e.type))return!1;const t=e.payload||{},n=t.reply||t.prompt||t.message||t.content||"";if(!n||"run_task"===n)return!1;if("tasks"===v&&!w.has(e.type))return!1;if("replies"===v&&!(t.reply||t.message||t.content))return!1;if(h){const t=h.toLowerCase();if(!((e.from||"").toLowerCase().includes(t)||(e.to||"").toLowerCase().includes(t)||n.toLowerCase().includes(t)||(e.type||"").toLowerCase().includes(t)))return!1}return!0}const E={"task.dispatched":{color:"var(--purple)",label:"dispatched"},"task.started":{color:"var(--amber)",label:"started"},"task.done":{color:"var(--green-hi)",label:"done"},"task.completed":{color:"var(--green-hi)",label:"completed"},"task.reply":{color:"var(--accent)",label:"reply"},"task.failed":{color:"var(--red-hi)",label:"failed"},"task.cancelled":{color:"var(--text-3)",label:"cancelled"}};async function L(){if(y)return;const e=document.getElementById("rtMessages"),t=document.getElementById("rtView");if(!e||!t)return;const n=0===e.children.length;if(n){const t=document.createElement("div");t.style.cssText="padding:20px;",t.textContent="Loading…",e.replaceChildren(t)}const o=(await s("/api/rt-messages")).filter(C).slice(-100),i=o.map(e=>{const t=e.payload||{},n=t.reply||t.prompt||t.message||t.content||"";return`${e.type}|${e.from}|${e.to}|${n.slice(0,100)}`}).join("::");if(i===window._rtLastHash&&!n)return;window._rtLastHash=i;const a=()=>t.scrollHeight-t.scrollTop-t.clientHeight<100,r=a(),l=t.scrollTop,c=document.createDocumentFragment();if(o.length){const e=document.createElement("div");e.style.cssText="display:grid;grid-template-columns:auto auto 1fr auto;gap:10px;padding:4px 10px 6px;font-size:10px;font-weight:600;color:var(--text-3);letter-spacing:.06em;text-transform:uppercase;border-bottom:2px solid var(--border);margin-bottom:2px;",["Agent","Phase","Summary","Time"].forEach(t=>{const n=document.createElement("span");n.textContent=t,e.appendChild(n)}),c.appendChild(e),o.forEach(e=>c.appendChild(function(e){const t=e.payload||{},n=t.reply||t.prompt||t.message||t.content||"",s=e.type||"",o=E[s],i=e.ts?new Date(e.ts).toLocaleTimeString([],{hour:"2-digit",minute:"2-digit"}):"",a=n.split("\n").map(e=>e.trim()).find(e=>e.length>2)||n,r=a.length>90?a.slice(0,90)+"…":a,l=n.length>r.length||n.split("\n").length>1,c=document.createElement("div");c.style.cssText="display:grid;grid-template-columns:auto auto 1fr auto;align-items:center;gap:10px;padding:7px 10px;border-radius:6px;cursor:"+(l?"pointer":"default")+";transition:background .12s;border-bottom:1px solid var(--border);",c.onmouseenter=()=>{c.style.background="var(--bg-2)"},c.onmouseleave=()=>{c.style.background=""};const d=document.createElement("div");d.style.cssText="display:flex;align-items:center;gap:5px;white-space:nowrap;min-width:0;";const p=document.createElement("span");if(p.style.cssText="font-size:11px;font-weight:600;color:var(--text-1);max-width:110px;overflow:hidden;text-overflow:ellipsis;",p.textContent=(e.from||"?").replace("crew-",""),p.title=e.from||"",d.appendChild(p),e.to&&e.to!==e.from){const t=document.createElement("span");t.style.cssText="font-size:10px;color:var(--text-3);flex-shrink:0;",t.textContent="→";const n=document.createElement("span");n.style.cssText="font-size:11px;color:var(--text-2);max-width:110px;overflow:hidden;text-overflow:ellipsis;",n.textContent=(e.to||"").replace("crew-",""),n.title=e.to||"",d.appendChild(t),d.appendChild(n)}const m=document.createElement("div");m.style.cssText="display:flex;align-items:center;gap:4px;flex-shrink:0;";const u=document.createElement("span"),g=o||{color:"var(--text-3)",label:s.split(".").pop()||s};if(u.style.cssText="font-size:10px;font-weight:600;padding:2px 7px;border-radius:20px;white-space:nowrap;flex-shrink:0;color:#fff;background:"+g.color+";letter-spacing:.03em;",u.textContent=g.label,m.appendChild(u),"task.done"===s&&t.engineUsed){const e={claude:"#e07a5f",codex:"#8338ec",cursor:"#3d405b",opencode:"#06d6a0",gemini:"#4285f4","docker-sandbox":"#0db7ed"},n={claude:"🤖",codex:"🟣",cursor:"🖱",opencode:"⚡",gemini:"✨","docker-sandbox":"🐳"},s=t.engineUsed,o=document.createElement("span");o.style.cssText="font-size:10px;font-weight:600;padding:2px 6px;border-radius:20px;white-space:nowrap;flex-shrink:0;color:#fff;background:"+(e[s]||"var(--text-3)")+";",o.textContent=(n[s]||"")+" "+s,o.title="Executed by "+s,m.appendChild(o)}const x=document.createElement("span");x.style.cssText="font-size:12px;color:var(--text-2);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0;",x.textContent=r;const f=document.createElement("div");f.style.cssText="display:flex;align-items:center;gap:6px;flex-shrink:0;";const y=document.createElement("span");if(y.style.cssText="font-size:10px;color:var(--text-3);white-space:nowrap;",y.textContent=i,f.appendChild(y),l){const e=document.createElement("span");e.style.cssText="font-size:10px;color:var(--text-3);",e.textContent="▸",f.appendChild(e)}if(c.appendChild(d),c.appendChild(m),c.appendChild(x),c.appendChild(f),l){const e=document.createElement("div");e.style.cssText="display:none;grid-column:1/-1;padding:8px 6px 4px;font-size:12px;color:var(--text-2);white-space:pre-wrap;word-break:break-word;max-height:300px;overflow-y:auto;border-top:1px solid var(--border);margin-top:4px;font-family:monospace;",e.textContent=n;const t=document.createElement("div");t.style.cssText="display:grid;grid-template-columns:1fr;border-radius:6px;overflow:hidden;border-bottom:1px solid var(--border);",c.style.borderBottom="none";let s=!1;return c.onclick=()=>{s=!s,e.style.display=s?"block":"none";const t=f.querySelector("span:last-child");t&&(t.textContent=s?"▾":"▸")},t.appendChild(c),t.appendChild(e),t}return c}(e)))}else{const e=document.createElement("div");e.style.cssText="padding:24px;text-align:center;font-size:12px;color:var(--text-3);",e.textContent="No events match the current filter.",c.appendChild(e)}if(e.replaceChildren(c),r)t.scrollTop=t.scrollHeight;else{const e=Math.max(0,t.scrollHeight-t.clientHeight);t.scrollTop=Math.min(l,e)}const d=document.getElementById("rtScrollBtn");d&&(d.style.display=a()?"none":"block"),t._scrollListenerBound||(t._scrollListenerBound=!0,t.addEventListener("scroll",()=>{d&&(d.style.display=a()?"none":"block")}))}function k(){y=!y;const e=document.getElementById("rtPauseBtn");e&&(e.textContent=y?"▶ Resume":"⏸ Pause",e.style.background=y?"var(--accent)":"",e.style.color=y?"#fff":"")}function T(){const e=document.getElementById("rtMessages");e&&(e.innerHTML='<div class="meta" style="padding:20px;text-align:center;opacity:.6;">Cleared. New messages will appear on next poll.</div>')}function I(){c(),document.getElementById("rtView").classList.add("active"),d("navRT"),function(){document.querySelectorAll(".rt-filter-chip").forEach(e=>{e.addEventListener("click",()=>{v=e.dataset.filter,document.querySelectorAll(".rt-filter-chip").forEach(t=>{const n=t===e;t.style.background=n?"var(--accent)":"transparent",t.style.color=n?"#fff":"var(--text-2)",t.classList.toggle("active",n)}),L()})});const e=document.getElementById("rtSearch");e&&e.addEventListener("input",()=>{h=e.value.trim(),L()})}(),L();const e=document.getElementById("rtScrollBtn");e&&(e.style.display="none")}async function B(){const e=document.getElementById("dlqMessages");e&&(e.innerHTML='<div style="padding:20px;">Loading…</div>');const t=await s("/api/dlq"),n=document.getElementById("dlqBadge");n&&(n.textContent=t.length,n.classList.toggle("hidden",!t.length)),e&&(e.innerHTML=t.length?t.map(e=>{const t=e.key||(e.filename||"").replace(".json","")||"?",n=o(t);return'<div class="msg dlq-item"><div class="meta"><strong>⚠️ Failed</strong> | '+(e.agent||"?")+" | "+(e.failedAt?new Date(e.failedAt).toLocaleString():"")+' <button class="replay-btn" data-action="replayDLQ" data-arg="'+n+'">Replay</button> <button data-action="deleteDLQ" data-arg="'+n+'" style="font-size:11px;padding:3px 8px;border-radius:4px;border:1px solid var(--red-hi);background:transparent;color:var(--red-hi);cursor:pointer;">Delete</button></div><div class="t">'+(e.error||"")+"</div></div>"}).join(""):'<div class="meta" style="padding:20px; text-align:center;">✓ DLQ empty</div>')}async function H(e){confirm("Replay?")&&(await l("/api/dlq/replay",{key:e}),r("Replayed"),B())}async function M(e){if(confirm("Delete this DLQ entry?"))try{await fetch("/api/dlq/"+encodeURIComponent(e),{method:"DELETE"}),r("DLQ entry deleted"),B()}catch(t){r("Failed: "+t.message,!0)}}function z(){c(),document.getElementById("dlqView").classList.add("active"),d("navDLQ"),B()}export{I as a,f as b,T as c,M as d,p as i,H as r,z as s,k as t};
@@ -1 +1 @@
1
- import{d as t,s as e,g as o,k as i,c as a}from"./core-utils-CAVnDoe1.js";const n={"grok-4-1-fast":[.2,.5],"grok-4-fast":[.2,.5],"grok-4":[3,15],"grok-3-mini":[.3,.5],"grok-3":[3,15],"grok-code-fast":[.2,1.5],"grok-beta":[5,15],"gpt-5.3-codex":[2.5,20],"gpt-5.2-codex":[1.75,14],"gpt-5.2":[1.75,14],"gpt-5.1-codex-max":[2.5,20],"gpt-5.1-codex-mini":[.25,2],"gpt-5.1-codex":[1.25,10],"gpt-5.1":[1.25,10],"gpt-5-codex":[1.25,10],"gpt-5-nano":[.15,.6],"gpt-5":[1.25,10],"codex-mini":[.25,2],"gpt-oss-120b":[.9,.9],"gpt-oss-20b":[.2,.2],"gpt-4o-mini":[.15,.6],"gpt-4o":[2.5,10],"gpt-4":[30,60],"deepseek-reasoner":[.7,2.5],"deepseek-chat":[.27,1.1],"mistral-large":[.5,1.5],"mistral-small":[.1,.3],"gemini-3.1-pro":[2.5,15],"gemini-3.1-flash":[.075,.3],"gemini-3-pro":[2.5,15],"gemini-3-flash":[.075,.3],"gemini-2.5-pro":[1.25,10],"gemini-2.5-flash-lite":[.04,.15],"gemini-2.5-flash":[.075,.3],"gemini-2.0-flash-lite":[.075,.3],"gemini-2.0-flash":[.1,.4],"claude-opus-4":[15,75],"claude-sonnet-4":[3,15],"claude-haiku-4":[.8,4],"claude-3-5-haiku":[.8,4],"claude-3-haiku":[.25,1.25],"claude-3-5-sonnet":[3,15],"claude-3-7-sonnet":[3,15],"kimi-k2-instruct":[1,3],"kimi-k2":[.6,2.5],"llama-4-maverick":[.5,.77],"llama-4-scout":[.11,.34],"llama-3.3-70b":[.59,.79],"llama-3.1-70b":[.59,.79],"llama3.1-70b":[.59,.79],"llama-3.1-8b":[.05,.08],"llama3.1-8b":[.1,.1],"qwen3-32b":[.29,.39],"llama-guard":[.2,.2],"sonar-pro":[3,15],sonar:[1,1],"big-pickle":[0,0],"trinity-large-preview":[0,0],"minimax-m2.5-free":[0,0],"glm-":[.1,.1],minimax:[.3,1],default:[1,3]};function s(t){let e=0;for(const[o,i]of Object.entries(t||{})){const t=Object.keys(n).find(t=>o.toLowerCase().includes(t))||"default",[a,s]=n[t];e+=i.prompt/1e6*a+i.completion/1e6*s}return e}async function r(){const t=document.getElementById("tokenUsageWidget");if(!t)return;const e=await o("/api/token-usage").catch(()=>({})),i=(e.prompt||0)+(e.completion||0),a=s(e.byModel);let r='<div style="display:grid;grid-template-columns:repeat(3,1fr);gap:10px;margin-bottom:12px;"><div style="text-align:center;"><div style="font-size:20px;font-weight:700;color:var(--accent);">'+(e.calls||0).toLocaleString()+'</div><div style="font-size:11px;color:var(--text-3);margin-top:2px;">LLM calls</div></div><div style="text-align:center;"><div style="font-size:20px;font-weight:700;color:var(--green);">'+(i/1e3).toFixed(1)+'k</div><div style="font-size:11px;color:var(--text-3);margin-top:2px;">total tokens</div></div><div style="text-align:center;"><div style="font-size:20px;font-weight:700;color:var(--yellow);">$'+a.toFixed(4)+'</div><div style="font-size:11px;color:var(--text-3);margin-top:2px;">est. cost (all-time)</div></div></div>';const l=e.byDay||{},d=Object.keys(l).sort().reverse().slice(0,14);if(d.length){const t=Math.max(...d.map(function(t){return s(l[t].byModel||{})}),1e-4);r+='<div style="font-size:11px;font-weight:600;color:var(--text-2);margin:12px 0 6px;">Daily cost (last '+d.length+" days)</div>",r+='<div style="display:flex;flex-direction:column;gap:3px;">',d.forEach(function(e){const o=l[e],i=s(o.byModel||{}),a=Math.max(i/t*100,2),n=((o.prompt||0)+(o.completion||0))/1e3,d=e===(new Date).toISOString().slice(0,10);r+='<div style="display:flex;align-items:center;gap:8px;font-size:11px;"><span style="width:70px;color:var(--text-3);flex-shrink:0;">'+(d?"today":e.slice(5))+'</span><div style="flex:1;background:var(--bg-1);border-radius:3px;height:14px;overflow:hidden;"><div style="width:'+a.toFixed(1)+"%;height:100%;background:"+(d?"var(--accent)":"var(--green)")+';border-radius:3px;"></div></div><span style="width:52px;text-align:right;color:var(--yellow);font-weight:600;">$'+i.toFixed(4)+'</span><span style="width:44px;text-align:right;color:var(--text-3);">'+n.toFixed(1)+"k</span></div>"}),r+="</div>"}else r+='<div style="font-size:11px;color:var(--text-3);margin-top:8px;">No daily history yet — data accumulates with next LLM call after restart.</div>';Object.keys(e.byModel||{}).length&&(r+='<div style="font-size:11px;color:var(--text-3);margin:12px 0 6px;">By model (all-time)</div>',Object.entries(e.byModel||{}).sort((t,e)=>e[1].prompt+e[1].completion-(t[1].prompt+t[1].completion)).forEach(function(t){const e=t[0],o=t[1],i=Object.keys(n).find(function(t){return e.toLowerCase().includes(t)})||"default",a=n[i],s=o.prompt/1e6*a[0]+o.completion/1e6*a[1];r+='<div style="display:flex;justify-content:space-between;font-size:11px;padding:3px 0;border-bottom:1px solid var(--border);"><code style="color:var(--accent);">'+e+'</code><span style="color:var(--text-2);">'+((o.prompt+o.completion)/1e3).toFixed(1)+"k tok · $"+s.toFixed(4)+"</span></div>"})),t.innerHTML=r}async function l(e){var n;const s=document.getElementById("ocStatsWidget");if(!s)return;const r=(null==(n=document.getElementById("ocStatsDays"))?void 0:n.value)||"14";e&&e(null),i(s);try{const t=await o("/api/opencode-stats?days="+r);if(!t.ok||!Object.keys(t.byDay||{}).length)return void a(s,t.error||"No OpenCode data found");const i=t.byDay,n=Object.keys(i).sort().reverse(),l=n.reduce(function(t,e){return t+i[e].cost},0),d=n.reduce(function(t,e){return t+i[e].input_tok},0),c=n.reduce(function(t,e){return t+i[e].output_tok},0),p=n.reduce(function(t,e){return t+i[e].calls},0),g=Math.max(...n.map(function(t){return i[t].cost}),1e-4);let x='<div style="display:grid;grid-template-columns:repeat(4,1fr);gap:10px;margin-bottom:16px;"><div style="text-align:center;"><div style="font-size:18px;font-weight:700;color:var(--yellow);">$'+l.toFixed(4)+'</div><div style="font-size:11px;color:var(--text-3);">total cost</div></div><div style="text-align:center;"><div style="font-size:18px;font-weight:700;color:var(--accent);">'+p.toLocaleString()+'</div><div style="font-size:11px;color:var(--text-3);">messages</div></div><div style="text-align:center;"><div style="font-size:18px;font-weight:700;color:var(--green);">'+(d/1e6).toFixed(1)+'M</div><div style="font-size:11px;color:var(--text-3);">input tokens</div></div><div style="text-align:center;"><div style="font-size:18px;font-weight:700;color:var(--green);">'+(c/1e6).toFixed(2)+'M</div><div style="font-size:11px;color:var(--text-3);">output tokens</div></div></div>';x+='<div style="display:flex;flex-direction:column;gap:4px;margin-bottom:16px;">';const y=(new Date).toISOString().slice(0,10);n.forEach(function(t){const e=i[t],o=Math.max(e.cost/g*100,e.cost>0?2:0),a=t===y,n=(e.input_tok+e.output_tok)/1e6;x+='<div style="display:flex;align-items:center;gap:8px;font-size:11px;"><span style="width:70px;color:var(--text-3);flex-shrink:0;">'+(a?"today":t.slice(5))+'</span><div style="flex:1;background:var(--bg-1);border-radius:3px;height:16px;overflow:hidden;"><div style="width:'+o.toFixed(1)+"%;height:100%;background:"+(a?"var(--accent)":"var(--green)")+';border-radius:3px;opacity:0.85;"></div></div><span style="width:60px;text-align:right;color:var(--yellow);font-weight:600;">$'+e.cost.toFixed(4)+'</span><span style="width:50px;text-align:right;color:var(--text-3);">'+n.toFixed(2)+'M</span><span style="width:36px;text-align:right;color:var(--text-3);">'+e.calls+"</span></div>"}),x+="</div>";const v={};n.forEach(function(t){Object.entries(i[t].byModel||{}).forEach(function(t){const e=t[0],o=t[1];v[e]||(v[e]={cost:0,input_tok:0,output_tok:0,calls:0}),v[e].cost+=o.cost,v[e].input_tok+=o.input_tok,v[e].output_tok+=o.output_tok,v[e].calls+=o.calls})});const f=Object.entries(v).sort(function(t,e){return e[1].cost-t[1].cost});f.length&&(x+='<div style="font-size:11px;color:var(--text-3);margin-bottom:6px;">By model</div>',f.forEach(function(t){const e=t[0],o=t[1],i=(o.input_tok+o.output_tok)/1e6;x+='<div style="display:flex;justify-content:space-between;align-items:center;font-size:11px;padding:3px 0;border-bottom:1px solid var(--border);"><code style="color:var(--accent);">'+e+'</code><span style="color:var(--text-2);">'+i.toFixed(2)+"M tok · "+o.calls+' calls · <span style="color:var(--yellow);font-weight:600;">$'+o.cost.toFixed(4)+"</span></span></div>"})),e&&e(l),s.innerHTML=x}catch(l){t(s,"Error: "+l.message)}}async function d(){try{const t=await o("/api/crew-lead/status"),e=document.getElementById("crewLeadBadge");t.online?(e.textContent="● online",e.className="status-badge status-running"):(e.textContent="● offline",e.className="status-badge status-stopped")}catch{}}function c(t){const e=document.getElementById("taskLifecycleContainer");if(!e)return;if(!(t=t||[]).length)return void(e.innerHTML='<div class="card" style="padding:12px;"><div class="meta" style="font-size:12px;">Recent task lifecycle (dispatched → completed/failed/cancelled). Dispatch a task to see events.</div></div>');const o=t.slice().reverse().slice(0,15).map(t=>{const e=t.data||{},o=e.phase||"",i="completed"===o?"var(--green)":"failed"===o||"cancelled"===o?"var(--red)":"var(--accent)";return'<tr style="border-bottom:1px solid var(--border);"><td style="padding:6px 10px;font-size:11px;color:var(--text-3);">'+(t.occurredAt||"").replace("T"," ").slice(0,19)+'</td><td style="padding:6px 10px;font-size:12px;"><span style="color:'+i+';">'+o+'</span></td><td style="padding:6px 10px;font-size:12px;">'+(e.agentId||"")+'</td><td style="padding:6px 10px;font-size:11px;color:var(--text-3);">'+(e.taskId||"").slice(0,20)+"</td></tr>"}).join("");e.innerHTML='<div class="card" style="overflow:auto;"><div style="font-size:12px;font-weight:600;padding:8px 12px;border-bottom:1px solid var(--border);">Task lifecycle (schema 1.1)</div><table style="width:100%;border-collapse:collapse;font-size:12px;"><thead><tr style="border-bottom:1px solid var(--border);"><th style="text-align:left;padding:6px 10px;">Time</th><th style="text-align:left;padding:6px 10px;">Phase</th><th style="text-align:left;padding:6px 10px;">Agent</th><th style="text-align:left;padding:6px 10px;">Task ID</th></tr></thead><tbody>'+o+"</tbody></table></div>"}const p={read_file:"read",write_file:"write",mkdir:"mkdir",run_cmd:"run",dispatch:"dispatch",skill:"skill",define_skill:"define_skill",git:"git",telegram:"tg",whatsapp:"wa"};async function g(){const e=document.getElementById("toolMatrixContainer");if(e)try{const t=await fetch("/api/health"),o=await t.json().catch(()=>({}));if(!t.ok||!o.ok){const i=o.error||(401===t.status?"Unauthorized":t.statusText||"Request failed");return void(e.innerHTML='<div class="card" style="padding:16px;"><div style="color:var(--yellow);font-size:13px;font-weight:600;">Health check failed</div><div style="color:var(--text-2);font-size:12px;margin-top:8px;">'+(401===t.status?"RT token missing or invalid. Set it in Settings → System (RT token) or in ~/.crewswarm/crewswarm.json (rt.authToken).":i)+'</div><div style="color:var(--text-3);font-size:11px;margin-top:8px;">Ensure crew-lead is running on :5010 (Services tab).</div></div>')}window._telemetryEvents=o.telemetry||[],c(o.telemetry||[]);const i=(o.agents||[]).filter(t=>"crew-lead"!==(t.id||"").toLowerCase()),a=window._crewLeadInfo||{name:"Crew Lead",emoji:"🧠"},n=[{id:"crew-lead",name:a.name,emoji:a.emoji,tools:["read_file","write_file","mkdir","run_cmd","web_search","web_fetch","skill","define_skill","dispatch","telegram","whatsapp"]},...i],s=[...new Set(["define_skill","skill",...n.flatMap(t=>Array.isArray(t.tools)?t.tools:Object.keys(t.tools||{}))])].sort(),r=s.map(t=>p[t]||t);if(!n.length)return void(e.innerHTML='<div class="card" style="padding:16px;"><div style="color:var(--text-2);font-size:13px;">No agents in roster.</div><div style="color:var(--text-3);font-size:12px;margin-top:6px;">Add agents in Settings → Agents (or ~/.crewswarm/crewswarm.json), then start bridges from Services.</div></div>');let l='<div class="card" style="overflow:auto;"><table style="width:100%;border-collapse:collapse;font-size:12px;"><thead><tr style="border-bottom:1px solid var(--border);"><th style="text-align:left;padding:8px 12px;">Agent</th>';s.forEach((t,e)=>{l+='<th style="text-align:center;padding:8px 8px;" title="'+(t||"")+'">'+(r[e]||t)+"</th>"}),l+='<th style="text-align:right;padding:8px 12px;">Quick action</th></tr></thead><tbody>',n.forEach(t=>{const e=Array.isArray(t.tools)?t.tools:t.tools?Object.keys(t.tools).filter(e=>t.tools[e]):[],o=(t.emoji||"")+" "+(t.name||t.id||"");l+='<tr style="border-bottom:1px solid var(--border);">',l+='<td style="padding:8px 12px;"><strong>'+(o||t.id).replace(/</g,"&lt;")+"</strong></td>",s.forEach(t=>{const o=e.includes(t);l+='<td style="text-align:center;padding:6px 8px;">'+(o?'<span style="color:var(--green);" title="'+t+'">✓</span>':'<span style="color:var(--text-3);">—</span>')+"</td>"}),l+='<td style="text-align:right;padding:8px 12px;"><button class="btn-ghost" style="font-size:11px;" data-action="restartAgentFromUI" data-arg="'+(t.id||"").replace(/"/g,"&quot;")+'">Restart</button></td></tr>'}),l+="</tbody></table></div>",e.innerHTML=l}catch(o){t(e,"Error loading health: "+(o.message||""))}}async function x(t){if(!t)return;const o=document.querySelector(`button[data-action="restartAgentFromUI"][data-arg="${t}"]`);o&&(o.disabled=!0,o.style.opacity="0.5",o.style.cursor="not-allowed");const i=Date.now(),a=window._lastAgentRestart||{};if(a[t]&&i-a[t]<3e3)return e("⏳ Restart already in progress...","warning"),void(o&&(o.disabled=!1,o.style.opacity="",o.style.cursor=""));window._lastAgentRestart||(window._lastAgentRestart={}),window._lastAgentRestart[t]=i;try{const i=await fetch("/api/agents/"+encodeURIComponent(t)+"/restart",{method:"POST",headers:{"Content-Type":"application/json"}}),a=await i.json();a.ok?(e("Restarting "+t+"…"),setTimeout(()=>{var t;if("usage"===(null==(t=window.appState)?void 0:t.activeTab)){const t=document.getElementById("healthAgentList");t&&null!==t.offsetParent&&window.loadAgentHealth&&window.loadAgentHealth()}},3e3)):(e(a.error||"Restart failed","error"),o&&(o.disabled=!1,o.style.opacity="",o.style.cursor=""))}catch(n){e(n.message||"Request failed","error"),o&&(o.disabled=!1,o.style.opacity="",o.style.cursor="")}}export{g as a,r as b,d as c,c as d,s as e,l,x as r};
1
+ import{d as t,s as e,g as o,k as i,c as a}from"./core-utils-CmOkXgzi.js";const n={"grok-4-1-fast":[.2,.5],"grok-4-fast":[.2,.5],"grok-4":[3,15],"grok-3-mini":[.3,.5],"grok-3":[3,15],"grok-code-fast":[.2,1.5],"grok-beta":[5,15],"gpt-5.3-codex":[2.5,20],"gpt-5.2-codex":[1.75,14],"gpt-5.2":[1.75,14],"gpt-5.1-codex-max":[2.5,20],"gpt-5.1-codex-mini":[.25,2],"gpt-5.1-codex":[1.25,10],"gpt-5.1":[1.25,10],"gpt-5-codex":[1.25,10],"gpt-5-nano":[.15,.6],"gpt-5":[1.25,10],"codex-mini":[.25,2],"gpt-oss-120b":[.9,.9],"gpt-oss-20b":[.2,.2],"gpt-4o-mini":[.15,.6],"gpt-4o":[2.5,10],"gpt-4":[30,60],"deepseek-reasoner":[.7,2.5],"deepseek-chat":[.27,1.1],"mistral-large":[.5,1.5],"mistral-small":[.1,.3],"gemini-3.1-pro":[2.5,15],"gemini-3.1-flash":[.075,.3],"gemini-3-pro":[2.5,15],"gemini-3-flash":[.075,.3],"gemini-2.5-pro":[1.25,10],"gemini-2.5-flash-lite":[.04,.15],"gemini-2.5-flash":[.075,.3],"gemini-2.0-flash-lite":[.075,.3],"gemini-2.0-flash":[.1,.4],"claude-opus-4":[15,75],"claude-sonnet-4":[3,15],"claude-haiku-4":[.8,4],"claude-3-5-haiku":[.8,4],"claude-3-haiku":[.25,1.25],"claude-3-5-sonnet":[3,15],"claude-3-7-sonnet":[3,15],"kimi-k2-instruct":[1,3],"kimi-k2":[.6,2.5],"llama-4-maverick":[.5,.77],"llama-4-scout":[.11,.34],"llama-3.3-70b":[.59,.79],"llama-3.1-70b":[.59,.79],"llama3.1-70b":[.59,.79],"llama-3.1-8b":[.05,.08],"llama3.1-8b":[.1,.1],"qwen3-32b":[.29,.39],"llama-guard":[.2,.2],"sonar-pro":[3,15],sonar:[1,1],"big-pickle":[0,0],"trinity-large-preview":[0,0],"minimax-m2.5-free":[0,0],"glm-":[.1,.1],minimax:[.3,1],default:[1,3]};function s(t){let e=0;for(const[o,i]of Object.entries(t||{})){const t=Object.keys(n).find(t=>o.toLowerCase().includes(t))||"default",[a,s]=n[t];e+=i.prompt/1e6*a+i.completion/1e6*s}return e}async function r(){const t=document.getElementById("tokenUsageWidget");if(!t)return;const e=await o("/api/token-usage").catch(()=>({})),i=(e.prompt||0)+(e.completion||0),a=s(e.byModel);let r='<div style="display:grid;grid-template-columns:repeat(3,1fr);gap:10px;margin-bottom:12px;"><div style="text-align:center;"><div style="font-size:20px;font-weight:700;color:var(--accent);">'+(e.calls||0).toLocaleString()+'</div><div style="font-size:11px;color:var(--text-3);margin-top:2px;">LLM calls</div></div><div style="text-align:center;"><div style="font-size:20px;font-weight:700;color:var(--green);">'+(i/1e3).toFixed(1)+'k</div><div style="font-size:11px;color:var(--text-3);margin-top:2px;">total tokens</div></div><div style="text-align:center;"><div style="font-size:20px;font-weight:700;color:var(--yellow);">$'+a.toFixed(4)+'</div><div style="font-size:11px;color:var(--text-3);margin-top:2px;">est. cost (all-time)</div></div></div>';const l=e.byDay||{},d=Object.keys(l).sort().reverse().slice(0,14);if(d.length){const t=Math.max(...d.map(function(t){return s(l[t].byModel||{})}),1e-4);r+='<div style="font-size:11px;font-weight:600;color:var(--text-2);margin:12px 0 6px;">Daily cost (last '+d.length+" days)</div>",r+='<div style="display:flex;flex-direction:column;gap:3px;">',d.forEach(function(e){const o=l[e],i=s(o.byModel||{}),a=Math.max(i/t*100,2),n=((o.prompt||0)+(o.completion||0))/1e3,d=e===(new Date).toISOString().slice(0,10);r+='<div style="display:flex;align-items:center;gap:8px;font-size:11px;"><span style="width:70px;color:var(--text-3);flex-shrink:0;">'+(d?"today":e.slice(5))+'</span><div style="flex:1;background:var(--bg-1);border-radius:3px;height:14px;overflow:hidden;"><div style="width:'+a.toFixed(1)+"%;height:100%;background:"+(d?"var(--accent)":"var(--green)")+';border-radius:3px;"></div></div><span style="width:52px;text-align:right;color:var(--yellow);font-weight:600;">$'+i.toFixed(4)+'</span><span style="width:44px;text-align:right;color:var(--text-3);">'+n.toFixed(1)+"k</span></div>"}),r+="</div>"}else r+='<div style="font-size:11px;color:var(--text-3);margin-top:8px;">No daily history yet — data accumulates with next LLM call after restart.</div>';Object.keys(e.byModel||{}).length&&(r+='<div style="font-size:11px;color:var(--text-3);margin:12px 0 6px;">By model (all-time)</div>',Object.entries(e.byModel||{}).sort((t,e)=>e[1].prompt+e[1].completion-(t[1].prompt+t[1].completion)).forEach(function(t){const e=t[0],o=t[1],i=Object.keys(n).find(function(t){return e.toLowerCase().includes(t)})||"default",a=n[i],s=o.prompt/1e6*a[0]+o.completion/1e6*a[1];r+='<div style="display:flex;justify-content:space-between;font-size:11px;padding:3px 0;border-bottom:1px solid var(--border);"><code style="color:var(--accent);">'+e+'</code><span style="color:var(--text-2);">'+((o.prompt+o.completion)/1e3).toFixed(1)+"k tok · $"+s.toFixed(4)+"</span></div>"})),t.innerHTML=r}async function l(e){var n;const s=document.getElementById("ocStatsWidget");if(!s)return;const r=(null==(n=document.getElementById("ocStatsDays"))?void 0:n.value)||"14";e&&e(null),i(s);try{const t=await o("/api/opencode-stats?days="+r);if(!t.ok||!Object.keys(t.byDay||{}).length)return void a(s,t.error||"No OpenCode data found");const i=t.byDay,n=Object.keys(i).sort().reverse(),l=n.reduce(function(t,e){return t+i[e].cost},0),d=n.reduce(function(t,e){return t+i[e].input_tok},0),c=n.reduce(function(t,e){return t+i[e].output_tok},0),p=n.reduce(function(t,e){return t+i[e].calls},0),g=Math.max(...n.map(function(t){return i[t].cost}),1e-4);let x='<div style="display:grid;grid-template-columns:repeat(4,1fr);gap:10px;margin-bottom:16px;"><div style="text-align:center;"><div style="font-size:18px;font-weight:700;color:var(--yellow);">$'+l.toFixed(4)+'</div><div style="font-size:11px;color:var(--text-3);">total cost</div></div><div style="text-align:center;"><div style="font-size:18px;font-weight:700;color:var(--accent);">'+p.toLocaleString()+'</div><div style="font-size:11px;color:var(--text-3);">messages</div></div><div style="text-align:center;"><div style="font-size:18px;font-weight:700;color:var(--green);">'+(d/1e6).toFixed(1)+'M</div><div style="font-size:11px;color:var(--text-3);">input tokens</div></div><div style="text-align:center;"><div style="font-size:18px;font-weight:700;color:var(--green);">'+(c/1e6).toFixed(2)+'M</div><div style="font-size:11px;color:var(--text-3);">output tokens</div></div></div>';x+='<div style="display:flex;flex-direction:column;gap:4px;margin-bottom:16px;">';const y=(new Date).toISOString().slice(0,10);n.forEach(function(t){const e=i[t],o=Math.max(e.cost/g*100,e.cost>0?2:0),a=t===y,n=(e.input_tok+e.output_tok)/1e6;x+='<div style="display:flex;align-items:center;gap:8px;font-size:11px;"><span style="width:70px;color:var(--text-3);flex-shrink:0;">'+(a?"today":t.slice(5))+'</span><div style="flex:1;background:var(--bg-1);border-radius:3px;height:16px;overflow:hidden;"><div style="width:'+o.toFixed(1)+"%;height:100%;background:"+(a?"var(--accent)":"var(--green)")+';border-radius:3px;opacity:0.85;"></div></div><span style="width:60px;text-align:right;color:var(--yellow);font-weight:600;">$'+e.cost.toFixed(4)+'</span><span style="width:50px;text-align:right;color:var(--text-3);">'+n.toFixed(2)+'M</span><span style="width:36px;text-align:right;color:var(--text-3);">'+e.calls+"</span></div>"}),x+="</div>";const v={};n.forEach(function(t){Object.entries(i[t].byModel||{}).forEach(function(t){const e=t[0],o=t[1];v[e]||(v[e]={cost:0,input_tok:0,output_tok:0,calls:0}),v[e].cost+=o.cost,v[e].input_tok+=o.input_tok,v[e].output_tok+=o.output_tok,v[e].calls+=o.calls})});const f=Object.entries(v).sort(function(t,e){return e[1].cost-t[1].cost});f.length&&(x+='<div style="font-size:11px;color:var(--text-3);margin-bottom:6px;">By model</div>',f.forEach(function(t){const e=t[0],o=t[1],i=(o.input_tok+o.output_tok)/1e6;x+='<div style="display:flex;justify-content:space-between;align-items:center;font-size:11px;padding:3px 0;border-bottom:1px solid var(--border);"><code style="color:var(--accent);">'+e+'</code><span style="color:var(--text-2);">'+i.toFixed(2)+"M tok · "+o.calls+' calls · <span style="color:var(--yellow);font-weight:600;">$'+o.cost.toFixed(4)+"</span></span></div>"})),e&&e(l),s.innerHTML=x}catch(l){t(s,"Error: "+l.message)}}async function d(){try{const t=await o("/api/crew-lead/status"),e=document.getElementById("crewLeadBadge");t.online?(e.textContent="● online",e.className="status-badge status-running"):(e.textContent="● offline",e.className="status-badge status-stopped")}catch{}}function c(t){const e=document.getElementById("taskLifecycleContainer");if(!e)return;if(!(t=t||[]).length)return void(e.innerHTML='<div class="card" style="padding:12px;"><div class="meta" style="font-size:12px;">Recent task lifecycle (dispatched → completed/failed/cancelled). Dispatch a task to see events.</div></div>');const o=t.slice().reverse().slice(0,15).map(t=>{const e=t.data||{},o=e.phase||"",i="completed"===o?"var(--green)":"failed"===o||"cancelled"===o?"var(--red)":"var(--accent)";return'<tr style="border-bottom:1px solid var(--border);"><td style="padding:6px 10px;font-size:11px;color:var(--text-3);">'+(t.occurredAt||"").replace("T"," ").slice(0,19)+'</td><td style="padding:6px 10px;font-size:12px;"><span style="color:'+i+';">'+o+'</span></td><td style="padding:6px 10px;font-size:12px;">'+(e.agentId||"")+'</td><td style="padding:6px 10px;font-size:11px;color:var(--text-3);">'+(e.taskId||"").slice(0,20)+"</td></tr>"}).join("");e.innerHTML='<div class="card" style="overflow:auto;"><div style="font-size:12px;font-weight:600;padding:8px 12px;border-bottom:1px solid var(--border);">Task lifecycle (schema 1.1)</div><table style="width:100%;border-collapse:collapse;font-size:12px;"><thead><tr style="border-bottom:1px solid var(--border);"><th style="text-align:left;padding:6px 10px;">Time</th><th style="text-align:left;padding:6px 10px;">Phase</th><th style="text-align:left;padding:6px 10px;">Agent</th><th style="text-align:left;padding:6px 10px;">Task ID</th></tr></thead><tbody>'+o+"</tbody></table></div>"}const p={read_file:"read",write_file:"write",mkdir:"mkdir",run_cmd:"run",dispatch:"dispatch",skill:"skill",define_skill:"define_skill",git:"git",telegram:"tg",whatsapp:"wa"};async function g(){const e=document.getElementById("toolMatrixContainer");if(e)try{const t=await fetch("/api/health"),o=await t.json().catch(()=>({}));if(!t.ok||!o.ok){const i=o.error||(401===t.status?"Unauthorized":t.statusText||"Request failed");return void(e.innerHTML='<div class="card" style="padding:16px;"><div style="color:var(--yellow);font-size:13px;font-weight:600;">Health check failed</div><div style="color:var(--text-2);font-size:12px;margin-top:8px;">'+(401===t.status?"RT token missing or invalid. Set it in Settings → System (RT token) or in ~/.crewswarm/crewswarm.json (rt.authToken).":i)+'</div><div style="color:var(--text-3);font-size:11px;margin-top:8px;">Ensure crew-lead is running on :5010 (Services tab).</div></div>')}window._telemetryEvents=o.telemetry||[],c(o.telemetry||[]);const i=(o.agents||[]).filter(t=>"crew-lead"!==(t.id||"").toLowerCase()),a=window._crewLeadInfo||{name:"Crew Lead",emoji:"🧠"},n=[{id:"crew-lead",name:a.name,emoji:a.emoji,tools:["read_file","write_file","mkdir","run_cmd","web_search","web_fetch","skill","define_skill","dispatch","telegram","whatsapp"]},...i],s=[...new Set(["define_skill","skill",...n.flatMap(t=>Array.isArray(t.tools)?t.tools:Object.keys(t.tools||{}))])].sort(),r=s.map(t=>p[t]||t);if(!n.length)return void(e.innerHTML='<div class="card" style="padding:16px;"><div style="color:var(--text-2);font-size:13px;">No agents in roster.</div><div style="color:var(--text-3);font-size:12px;margin-top:6px;">Add agents in Settings → Agents (or ~/.crewswarm/crewswarm.json), then start bridges from Services.</div></div>');let l='<div class="card" style="overflow:auto;"><table style="width:100%;border-collapse:collapse;font-size:12px;"><thead><tr style="border-bottom:1px solid var(--border);"><th style="text-align:left;padding:8px 12px;">Agent</th>';s.forEach((t,e)=>{l+='<th style="text-align:center;padding:8px 8px;" title="'+(t||"")+'">'+(r[e]||t)+"</th>"}),l+='<th style="text-align:right;padding:8px 12px;">Quick action</th></tr></thead><tbody>',n.forEach(t=>{const e=Array.isArray(t.tools)?t.tools:t.tools?Object.keys(t.tools).filter(e=>t.tools[e]):[],o=(t.emoji||"")+" "+(t.name||t.id||"");l+='<tr style="border-bottom:1px solid var(--border);">',l+='<td style="padding:8px 12px;"><strong>'+(o||t.id).replace(/</g,"&lt;")+"</strong></td>",s.forEach(t=>{const o=e.includes(t);l+='<td style="text-align:center;padding:6px 8px;">'+(o?'<span style="color:var(--green);" title="'+t+'">✓</span>':'<span style="color:var(--text-3);">—</span>')+"</td>"}),l+='<td style="text-align:right;padding:8px 12px;"><button class="btn-ghost" style="font-size:11px;" data-action="restartAgentFromUI" data-arg="'+(t.id||"").replace(/"/g,"&quot;")+'">Restart</button></td></tr>'}),l+="</tbody></table></div>",e.innerHTML=l}catch(o){t(e,"Error loading health: "+(o.message||""))}}async function x(t){if(!t)return;const o=document.querySelector(`button[data-action="restartAgentFromUI"][data-arg="${t}"]`);o&&(o.disabled=!0,o.style.opacity="0.5",o.style.cursor="not-allowed");const i=Date.now(),a=window._lastAgentRestart||{};if(a[t]&&i-a[t]<3e3)return e("⏳ Restart already in progress...","warning"),void(o&&(o.disabled=!1,o.style.opacity="",o.style.cursor=""));window._lastAgentRestart||(window._lastAgentRestart={}),window._lastAgentRestart[t]=i;try{const i=await fetch("/api/agents/"+encodeURIComponent(t)+"/restart",{method:"POST",headers:{"Content-Type":"application/json"}}),a=await i.json();a.ok?(e("Restarting "+t+"…"),setTimeout(()=>{var t;if("usage"===(null==(t=window.appState)?void 0:t.activeTab)){const t=document.getElementById("healthAgentList");t&&null!==t.offsetParent&&window.loadAgentHealth&&window.loadAgentHealth()}},3e3)):(e(a.error||"Restart failed","error"),o&&(o.disabled=!1,o.style.opacity="",o.style.cursor=""))}catch(n){e(n.message||"Request failed","error"),o&&(o.disabled=!1,o.style.opacity="",o.style.cursor="")}}export{g as a,r as b,d as c,c as d,s as e,l,x as r};
@@ -1 +1 @@
1
- import{g as e,e as t,s as n,p as o}from"./core-utils-CAVnDoe1.js";let a=()=>{},l=()=>{},s=[],i=[];const d={selectedName:"",list:[],runStatus:{},editorWorkflow:null},r=[{id:"daily-research",name:"Daily Research Brief",description:"Research, summarize, then QA-check every weekday morning.",schedule:"0 9 * * 1-5",stages:[{agent:"crew-researcher",task:"Research top 5 updates for our current project and summarize key signals."},{agent:"crew-pm",task:"Turn the research into a concise daily brief with priorities and risks."},{agent:"crew-qa",task:"Review the brief for factual clarity and missing edge cases."}]},{id:"seo-content",name:"SEO Content Pipeline",description:"Generate SEO topic ideas, draft copy, then edit.",schedule:"30 10 * * 1,3,5",stages:[{agent:"crew-seo",task:"Find one high-intent keyword cluster and propose a short content outline."},{agent:"crew-copywriter",task:"Write a first draft from the outline. Keep it scannable and conversion-focused."},{agent:"crew-main",task:"Polish the draft and produce final publish-ready copy."}]},{id:"code-health",name:"Code Health Sweep",description:"Automated PM->Coder->QA quality pass.",schedule:"0 14 * * 1-5",stages:[{agent:"crew-pm",task:"Pick one high-value backlog item from project context and define acceptance criteria."},{agent:"crew-coder",task:"Implement the scoped item in small safe changes and summarize files touched."},{agent:"crew-qa",task:"Audit the changes, run tests, and report any regressions with severity."}]}];function c(e=""){return{name:e,description:"",enabled:!1,schedule:"",timezone:Intl.DateTimeFormat().resolvedOptions().timeZone,stages:[{agent:"crew-main",task:"",tool:""}]}}function p(e={}){a=e.hideAllViews||a,l=e.setNavActive||l}async function m(){var t;a(),null==(t=document.getElementById("workflowsView"))||t.classList.add("active"),l("navWorkflows"),await async function(){try{const t=await e("/api/agents");s=(t||[]).map(e=>"string"==typeof e?e:e.id||e.agent).filter(Boolean).sort()}catch{s=[]}}(),await async function(){try{const t=await e("/api/skills");i=(t.skills||[]).map(e=>({name:e.name||"",description:e.description||"",type:e.type||(e.url?"api":"knowledge")})).filter(e=>e.name).sort((e,t)=>e.name.localeCompare(t.name))}catch{i=[]}}(),await f()}async function f(){const n=document.getElementById("workflowList");if(n){n.innerHTML='<div class="meta" style="padding:10px;">Loading workflows...</div>';try{const t=await e("/api/workflows/list");d.list=t.workflows||[],u(),d.selectedName?await w(d.selectedName):y(c());const n=t.timezone||Intl.DateTimeFormat().resolvedOptions().timeZone,o=document.getElementById("workflowTimezoneLabel");o&&(o.textContent=`Local timezone: ${n}`)}catch(o){n.innerHTML=`<div class="meta" style="padding:10px;color:var(--red-hi);">Failed to load workflows: ${t(o.message)}</div>`}}}function u(){const e=document.getElementById("workflowList");if(!e)return;const n=d.list||[];n.length?(e.innerHTML=n.map(e=>{var n;const o=e.name===d.selectedName?"background:var(--bg-2);border-color:var(--accent);":"",a=e.schedule?t(e.schedule):'<span style="opacity:0.7;">no schedule</span>',l=(null==(n=e.runState)?void 0:n.running)?'<span style="color:var(--green-hi);font-size:11px;">running</span>':"";return`\n <button class="btn-ghost workflow-row" data-workflow-name="${t(e.name)}" style="width:100%;text-align:left;display:flex;flex-direction:column;gap:4px;padding:10px;margin-bottom:8px;${o}">\n <div style="display:flex;align-items:center;justify-content:space-between;gap:8px;">\n <strong style="font-size:13px;">${t(e.name)}</strong>\n ${l}\n </div>\n <div style="font-size:11px;color:var(--text-3);">${e.enabled?"enabled":"disabled"} · ${e.stageCount||0} stage(s)</div>\n <div style="font-size:11px;color:var(--text-2);font-family:monospace;">${a}</div>\n </button>\n `}).join(""),e.querySelectorAll(".workflow-row").forEach(e=>{e.addEventListener("click",async()=>{const t=e.dataset.workflowName||"";await w(t)})})):e.innerHTML='<div class="meta" style="padding:10px;">No workflows yet.</div>'}async function w(t){if(t)try{const n=await e(`/api/workflows/item?name=${encodeURIComponent(t)}`);d.selectedName=t,d.runStatus=n.runState||{},u(),y({name:t,...n.workflow||{}},n),await async function(t){const n=document.getElementById("workflowLog");if(!n||!t)return;try{const o=(await e(`/api/workflows/log?name=${encodeURIComponent(t)}&limit=120`)).lines||[];n.textContent=o.length?o.join("\n"):"No log lines yet.",n.scrollTop=n.scrollHeight}catch(o){n.textContent=`Failed to load log: ${o.message}`}}(t)}catch(o){n(`Failed to load workflow: ${o.message}`,"error")}}function g(e){const t=String(e||"").trim();if(!t)return"No schedule set. Add a cron expression or use a preset.";return{"*/15 * * * *":"Runs every 15 minutes","0 * * * *":"Runs hourly at minute 0","0 9 * * 1-5":"Runs weekdays at 9:00","0 9 * * *":"Runs daily at 9:00","0 0 * * 1":"Runs every Monday at midnight","0 8 1 * *":"Runs monthly on day 1 at 8:00"}[t]||"Custom cron schedule"}function y(e,o={}){const a=document.getElementById("workflowEditor");if(!a)return;const l={...c(),...e},p=Array.isArray(l.stages)&&l.stages.length?l.stages:c().stages;d.editorWorkflow={...l,stages:p.map(e=>({...e}))};const m=o.cronExample?t(o.cronExample):`*/15 * * * * cd ${t(window.location.pathname||".")} && node scripts/run-scheduled-pipeline.mjs ${t(l.name||"my-workflow")}`,u={agents:s.length?s.map(e=>`<code style="font-size:11px;">${t(e)}</code>`).join(" "):'<span style="font-size:11px;color:var(--text-3);">No agents loaded</span>',skills:i.length?i.map(e=>`<div style="font-size:11px;line-height:1.4;"><code>${t(e.name)}</code> <span style="color:var(--text-3);">(${t(e.type)})</span></div>`).join(""):'<span style="font-size:11px;color:var(--text-3);">No skills loaded</span>'};a.innerHTML=`\n <div class="card" style="display:flex;flex-direction:column;gap:12px;">\n <div style="display:flex;gap:8px;flex-wrap:wrap;">\n <button id="wfOpenTemplateLibraryBtn" class="btn-ghost" style="font-size:12px;">📚 Job Library</button>\n <button id="wfOpenSkillGuideBtn" class="btn-ghost" style="font-size:12px;">🧩 Skills & Agent Options</button>\n <button id="wfOpenJsonEditorBtn" class="btn-ghost" style="font-size:12px;">{ } Advanced JSON</button>\n </div>\n\n <div style="display:flex;gap:10px;flex-wrap:wrap;">\n <div style="flex:1;min-width:260px;">\n <label style="font-size:12px;font-weight:600;">Name</label>\n <input id="wfName" type="text" value="${t(l.name||"")}" placeholder="daily-research" style="width:100%;margin-top:4px;padding:8px 10px;" />\n </div>\n <div style="flex:1;min-width:260px;">\n <label style="font-size:12px;font-weight:600;">Description</label>\n <input id="wfDescription" type="text" value="${t(l.description||"")}" placeholder="What this workflow does" style="width:100%;margin-top:4px;padding:8px 10px;" />\n </div>\n </div>\n\n <div style="display:flex;gap:10px;align-items:flex-end;flex-wrap:wrap;">\n <div style="min-width:280px;flex:1;">\n <label style="font-size:12px;font-weight:600;">Cron Schedule</label>\n <input id="wfSchedule" type="text" value="${t(l.schedule||"")}" placeholder="0 9 * * 1-5" style="width:100%;margin-top:4px;padding:8px 10px;font-family:monospace;" />\n <div style="font-size:11px;color:var(--text-3);margin-top:4px;">Format: minute hour day month weekday</div>\n <div style="display:flex;gap:6px;flex-wrap:wrap;margin-top:8px;">${[{label:"Every 15m",cron:"*/15 * * * *"},{label:"Hourly",cron:"0 * * * *"},{label:"Daily 9am",cron:"0 9 * * *"},{label:"Weekdays 9am",cron:"0 9 * * 1-5"},{label:"Weekly Mon",cron:"0 0 * * 1"},{label:"Monthly",cron:"0 8 1 * *"}].map(e=>`<button class="btn-ghost wf-cron-preset" data-cron="${t(e.cron)}" style="font-size:11px;padding:4px 8px;">${t(e.label)}</button>`).join("")}</div>\n <div id="wfScheduleHint" style="font-size:11px;color:var(--text-2);margin-top:6px;">${t(g(l.schedule))}</div>\n </div>\n <label style="display:flex;align-items:center;gap:8px;font-size:12px;font-weight:600;padding-bottom:8px;">\n <input id="wfEnabled" type="checkbox" ${l.enabled?"checked":""} />\n Enabled\n </label>\n </div>\n\n <div id="workflowTimezoneLabel" style="font-size:11px;color:var(--text-3);"></div>\n\n <div>\n <div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:8px;">\n <label style="font-size:12px;font-weight:600;">Stages (wave-like, runs top to bottom)</label>\n <button id="wfAddStageBtn" class="btn-ghost" style="font-size:12px;">+ Add Stage</button>\n </div>\n <div id="wfStagesWrap" style="display:flex;flex-direction:column;gap:8px;">\n ${p.map((e,n)=>{return`\n <div class="wf-stage-row" data-stage-index="${n}" style="border:1px solid var(--border);border-radius:8px;padding:10px;background:var(--bg-2);">\n <div style="display:flex;align-items:center;justify-content:space-between;gap:10px;margin-bottom:8px;">\n <strong style="font-size:12px;">Stage ${n+1}</strong>\n <div style="display:flex;gap:6px;flex-wrap:wrap;">\n <button class="btn-ghost wf-move-stage-up" data-stage-index="${n}" style="font-size:11px;padding:4px 8px;">↑</button>\n <button class="btn-ghost wf-move-stage-down" data-stage-index="${n}" style="font-size:11px;padding:4px 8px;">↓</button>\n <button class="btn-ghost wf-duplicate-stage" data-stage-index="${n}" style="font-size:11px;padding:4px 8px;">Duplicate</button>\n <button class="btn-red wf-remove-stage" data-stage-index="${n}" style="font-size:11px;padding:4px 8px;">Remove</button>\n </div>\n </div>\n <div style="display:flex;gap:8px;flex-wrap:wrap;margin-bottom:8px;">\n <div style="flex:1;min-width:220px;">\n <label style="font-size:11px;font-weight:600;">Agent</label>\n <select class="wf-agent" style="width:100%;margin-top:4px;padding:6px 8px;">${o=e.agent,Array.from(new Set([...s||[],"crew-main","crew-pm","crew-qa","crew-coder","crew-coder-front","crew-coder-back","crew-copywriter",o||""])).filter(Boolean).sort().map(e=>`<option value="${t(e)}" ${e===o?"selected":""}>${t(e)}</option>`).join("")}</select>\n </div>\n <div style="width:180px;">\n <label style="font-size:11px;font-weight:600;">Tool hint (optional)</label>\n <input class="wf-tool" type="text" value="${t(e.tool||"")}" placeholder="write_file" style="width:100%;margin-top:4px;padding:6px 8px;" />\n </div>\n </div>\n <div>\n <label style="font-size:11px;font-weight:600;">Task</label>\n <textarea class="wf-task" rows="3" style="width:100%;margin-top:4px;padding:8px 10px;font-family:monospace;">${t(e.task||"")}</textarea>\n </div>\n </div>\n `;var o}).join("")}\n </div>\n </div>\n\n <div style="display:flex;gap:8px;flex-wrap:wrap;">\n <button id="wfSaveBtn" class="btn">Save Workflow</button>\n <button id="wfRunBtn" class="btn-green">Run Now</button>\n <button id="wfDeleteBtn" class="btn-red">Delete</button>\n <button id="wfNewBtn" class="btn-ghost">New</button>\n <button id="wfRefreshBtn" class="btn-ghost">Refresh</button>\n </div>\n\n <div>\n <div style="font-size:11px;font-weight:600;color:var(--text-2);margin-bottom:4px;">Crontab example</div>\n <code style="display:block;background:var(--bg-2);border:1px solid var(--border);padding:8px 10px;border-radius:6px;overflow:auto;white-space:nowrap;">${m}</code>\n </div>\n\n <div id="wfLibraryModal" style="display:none;position:fixed;inset:0;background:rgba(0,0,0,0.55);z-index:12000;align-items:center;justify-content:center;padding:18px;">\n <div style="width:min(960px,100%);max-height:85vh;overflow:auto;background:var(--bg-card);border:1px solid var(--border);border-radius:12px;padding:14px;">\n <div style="display:flex;align-items:center;justify-content:space-between;gap:10px;margin-bottom:10px;">\n <div style="font-size:14px;font-weight:700;">Workflow Job Library</div>\n <button id="wfCloseLibraryBtn" class="btn-ghost" style="font-size:12px;">Close</button>\n </div>\n <div style="font-size:12px;color:var(--text-2);margin-bottom:10px;">Pick a starter template, then customize stages/tasks.</div>\n <div style="display:grid;grid-template-columns:repeat(auto-fill,minmax(250px,1fr));gap:10px;">${r.map(e=>`\n <div style="border:1px solid var(--border);border-radius:8px;padding:10px;background:var(--bg-2);">\n <div style="font-size:12px;font-weight:700;">${t(e.name)}</div>\n <div style="font-size:11px;color:var(--text-3);margin-top:4px;">${t(e.description)}</div>\n <div style="font-size:11px;color:var(--text-2);font-family:monospace;margin-top:6px;">${t(e.schedule)}</div>\n <div style="margin-top:8px;">\n <button class="btn-ghost wf-apply-template" data-template-id="${t(e.id)}" style="font-size:11px;">Use Template</button>\n </div>\n </div>\n `).join("")}</div>\n <hr style="border:none;border-top:1px solid var(--border);margin:14px 0;" />\n <div style="font-size:12px;font-weight:700;margin-bottom:6px;">Available Agents</div>\n <div style="display:flex;gap:6px;flex-wrap:wrap;">${u.agents}</div>\n <div style="font-size:12px;font-weight:700;margin:12px 0 6px;">Available Skills</div>\n <div style="display:grid;grid-template-columns:repeat(auto-fill,minmax(230px,1fr));gap:6px;">${u.skills}</div>\n </div>\n </div>\n\n <div id="wfJsonModal" style="display:none;position:fixed;inset:0;background:rgba(0,0,0,0.55);z-index:12000;align-items:center;justify-content:center;padding:18px;">\n <div style="width:min(920px,100%);max-height:85vh;overflow:auto;background:var(--bg-card);border:1px solid var(--border);border-radius:12px;padding:14px;">\n <div style="display:flex;align-items:center;justify-content:space-between;gap:10px;margin-bottom:10px;">\n <div style="font-size:14px;font-weight:700;">Advanced Workflow JSON</div>\n <button id="wfCloseJsonBtn" class="btn-ghost" style="font-size:12px;">Close</button>\n </div>\n <div style="font-size:12px;color:var(--text-2);margin-bottom:8px;">Edit raw JSON. Supports both <code>stages</code> and <code>steps</code>.</div>\n <textarea id="wfJsonTextarea" rows="18" style="width:100%;font-family:monospace;font-size:12px;padding:10px;border:1px solid var(--border);border-radius:8px;background:var(--bg-2);"></textarea>\n <div style="margin-top:10px;display:flex;gap:8px;flex-wrap:wrap;">\n <button id="wfApplyJsonBtn" class="btn-green">Apply JSON</button>\n </div>\n </div>\n </div>\n </div>\n `;const w=document.getElementById("workflowTimezoneLabel");var k,E,B,I,z,S,$,L,A,N,D,T,J,C,O;w&&(w.textContent=`Local timezone: ${Intl.DateTimeFormat().resolvedOptions().timeZone}`),null==(k=document.getElementById("wfOpenTemplateLibraryBtn"))||k.addEventListener("click",e=>{e.preventDefault();const t=document.getElementById("wfLibraryModal");t&&(t.style.display="flex")}),null==(E=document.getElementById("wfOpenSkillGuideBtn"))||E.addEventListener("click",e=>{e.preventDefault();const t=document.getElementById("wfLibraryModal");t&&(t.style.display="flex")}),null==(B=document.getElementById("wfCloseLibraryBtn"))||B.addEventListener("click",e=>{e.preventDefault();const t=document.getElementById("wfLibraryModal");t&&(t.style.display="none")}),null==(I=document.getElementById("wfLibraryModal"))||I.addEventListener("click",e=>{var t;"wfLibraryModal"===(null==(t=e.target)?void 0:t.id)&&(e.currentTarget.style.display="none")}),null==(z=document.getElementById("wfOpenJsonEditorBtn"))||z.addEventListener("click",e=>{var t;e.preventDefault();const n={...v().workflow,...(null==(t=d.editorWorkflow)?void 0:t.steps)?{steps:d.editorWorkflow.steps}:{}},o=document.getElementById("wfJsonTextarea");o&&(o.value=JSON.stringify(n,null,2));const a=document.getElementById("wfJsonModal");a&&(a.style.display="flex")}),null==(S=document.getElementById("wfCloseJsonBtn"))||S.addEventListener("click",e=>{e.preventDefault();const t=document.getElementById("wfJsonModal");t&&(t.style.display="none")}),null==($=document.getElementById("wfJsonModal"))||$.addEventListener("click",e=>{var t;"wfJsonModal"===(null==(t=e.target)?void 0:t.id)&&(e.currentTarget.style.display="none")}),null==(L=document.getElementById("wfApplyJsonBtn"))||L.addEventListener("click",e=>{e.preventDefault();try{const e=document.getElementById("wfJsonTextarea"),t=JSON.parse((null==e?void 0:e.value)||"{}"),o=v();y({name:o.name,description:t.description||o.workflow.description,enabled:t.enabled??o.workflow.enabled,schedule:t.schedule||o.workflow.schedule,timezone:t.timezone||Intl.DateTimeFormat().resolvedOptions().timeZone,stages:Array.isArray(t.stages)&&t.stages.length?t.stages:o.workflow.stages,...Array.isArray(t.steps)?{steps:t.steps}:{}});const a=document.getElementById("wfJsonModal");a&&(a.style.display="none"),n("Applied advanced JSON","success")}catch(t){n(`Invalid JSON: ${t.message}`,"error")}}),document.querySelectorAll(".wf-apply-template").forEach(e=>{e.addEventListener("click",t=>{t.preventDefault();const o=e.dataset.templateId||"",a=r.find(e=>e.id===o);if(!a)return;const l=v();y({name:l.name||a.id,description:a.description,enabled:l.workflow.enabled,schedule:a.schedule,timezone:Intl.DateTimeFormat().resolvedOptions().timeZone,stages:a.stages.map(e=>({...e,tool:e.tool||""}))});const s=document.getElementById("wfLibraryModal");s&&(s.style.display="none"),n(`Applied template: ${a.name}`,"success")})}),document.querySelectorAll(".wf-cron-preset").forEach(e=>{e.addEventListener("click",t=>{t.preventDefault();const n=e.dataset.cron||"",o=document.getElementById("wfSchedule"),a=document.getElementById("wfScheduleHint");o&&(o.value=n),a&&(a.textContent=g(n))})}),null==(A=document.getElementById("wfSchedule"))||A.addEventListener("input",e=>{const t=document.getElementById("wfScheduleHint");t&&(t.textContent=g(e.target.value))}),null==(N=document.getElementById("wfAddStageBtn"))||N.addEventListener("click",e=>{e.preventDefault();const t=v({includeIncompleteStages:!0});t.workflow.stages.push({agent:"crew-main",task:"",tool:""}),y({name:t.name,...t.workflow})}),document.querySelectorAll(".wf-remove-stage").forEach(e=>{e.addEventListener("click",t=>{t.preventDefault();const n=Number(e.dataset.stageIndex||"-1"),o=v({includeIncompleteStages:!0});o.workflow.stages=o.workflow.stages.filter((e,t)=>t!==n),o.workflow.stages.length||(o.workflow.stages=[{agent:"crew-main",task:"",tool:""}]),y({name:o.name,...o.workflow})})}),document.querySelectorAll(".wf-move-stage-up").forEach(e=>{e.addEventListener("click",t=>{t.preventDefault();const n=Number(e.dataset.stageIndex||"-1");if(n<=0)return;const o=v({includeIncompleteStages:!0}),[a]=o.workflow.stages.splice(n,1);o.workflow.stages.splice(n-1,0,a),y({name:o.name,...o.workflow})})}),document.querySelectorAll(".wf-move-stage-down").forEach(e=>{e.addEventListener("click",t=>{t.preventDefault();const n=Number(e.dataset.stageIndex||"-1"),o=v({includeIncompleteStages:!0});if(n<0||n>=o.workflow.stages.length-1)return;const[a]=o.workflow.stages.splice(n,1);o.workflow.stages.splice(n+1,0,a),y({name:o.name,...o.workflow})})}),document.querySelectorAll(".wf-duplicate-stage").forEach(e=>{e.addEventListener("click",t=>{t.preventDefault();const n=Number(e.dataset.stageIndex||"-1"),o=v({includeIncompleteStages:!0});if(n<0||n>=o.workflow.stages.length)return;const a=o.workflow.stages[n];o.workflow.stages.splice(n+1,0,{...a}),y({name:o.name,...o.workflow})})}),null==(D=document.getElementById("wfSaveBtn"))||D.addEventListener("click",x),null==(T=document.getElementById("wfRunBtn"))||T.addEventListener("click",b),null==(J=document.getElementById("wfDeleteBtn"))||J.addEventListener("click",h),null==(C=document.getElementById("wfNewBtn"))||C.addEventListener("click",()=>{d.selectedName="",y(c());const e=document.getElementById("workflowLog");e&&(e.textContent="")}),null==(O=document.getElementById("wfRefreshBtn"))||O.addEventListener("click",async()=>{await f()})}function v(e={}){var t,n,o,a,l;const{includeIncompleteStages:s=!1}=e,i=((null==(t=document.getElementById("wfName"))?void 0:t.value)||"").trim(),r=((null==(n=document.getElementById("wfDescription"))?void 0:n.value)||"").trim(),c=((null==(o=document.getElementById("wfSchedule"))?void 0:o.value)||"").trim(),p=!!(null==(a=document.getElementById("wfEnabled"))?void 0:a.checked),m=Array.from(document.querySelectorAll(".wf-stage-row")).map(e=>{var t,n,o,a,l,s;const i=(null==(n=null==(t=e.querySelector(".wf-agent"))?void 0:t.value)?void 0:n.trim())||"",d=(null==(a=null==(o=e.querySelector(".wf-tool"))?void 0:o.value)?void 0:a.trim())||"";return{agent:i,task:(null==(s=null==(l=e.querySelector(".wf-task"))?void 0:l.value)?void 0:s.trim())||"",...d?{tool:d}:{}}}).filter(e=>s||e.agent&&e.task),f=Array.isArray(null==(l=d.editorWorkflow)?void 0:l.steps)?d.editorWorkflow.steps:[];return{name:i,workflow:{description:r,enabled:p,schedule:c,timezone:Intl.DateTimeFormat().resolvedOptions().timeZone,stages:m,...f.length?{steps:f}:{}}}}async function x(){const e=v();if(e.name)if(e.workflow.stages.length)try{await o("/api/workflows/save",e),d.selectedName=e.name,n(`Saved workflow: ${e.name}`,"success"),await f(),await w(e.name)}catch(t){n(`Save failed: ${t.message}`,"error")}else n("Add at least one stage","error");else n("Workflow name is required","error")}async function b(){const e=v();if(e.name)try{const t=await o("/api/workflows/run",{name:e.name});n(`Started ${e.name}${t.pid?` (pid ${t.pid})`:""}`,"success"),await w(e.name)}catch(t){n(`Run failed: ${t.message}`,"error")}else n("Save workflow first (name required)","warning")}async function h(){const e=v();if(e.name){if(confirm(`Delete workflow "${e.name}"?`))try{await o("/api/workflows/delete",{name:e.name}),n(`Deleted ${e.name}`,"success"),d.selectedName="",await f(),y(c());const t=document.getElementById("workflowLog");t&&(t.textContent="")}catch(t){n(`Delete failed: ${t.message}`,"error")}}else n("No workflow selected","warning")}export{p as i,m as s};
1
+ import{g as e,e as t,s as n,p as o}from"./core-utils-CmOkXgzi.js";let a=()=>{},l=()=>{},s=[],i=[];const d={selectedName:"",list:[],runStatus:{},editorWorkflow:null},r=[{id:"daily-research",name:"Daily Research Brief",description:"Research, summarize, then QA-check every weekday morning.",schedule:"0 9 * * 1-5",stages:[{agent:"crew-researcher",task:"Research top 5 updates for our current project and summarize key signals."},{agent:"crew-pm",task:"Turn the research into a concise daily brief with priorities and risks."},{agent:"crew-qa",task:"Review the brief for factual clarity and missing edge cases."}]},{id:"seo-content",name:"SEO Content Pipeline",description:"Generate SEO topic ideas, draft copy, then edit.",schedule:"30 10 * * 1,3,5",stages:[{agent:"crew-seo",task:"Find one high-intent keyword cluster and propose a short content outline."},{agent:"crew-copywriter",task:"Write a first draft from the outline. Keep it scannable and conversion-focused."},{agent:"crew-main",task:"Polish the draft and produce final publish-ready copy."}]},{id:"code-health",name:"Code Health Sweep",description:"Automated PM->Coder->QA quality pass.",schedule:"0 14 * * 1-5",stages:[{agent:"crew-pm",task:"Pick one high-value backlog item from project context and define acceptance criteria."},{agent:"crew-coder",task:"Implement the scoped item in small safe changes and summarize files touched."},{agent:"crew-qa",task:"Audit the changes, run tests, and report any regressions with severity."}]}];function c(e=""){return{name:e,description:"",enabled:!1,schedule:"",timezone:Intl.DateTimeFormat().resolvedOptions().timeZone,stages:[{agent:"crew-main",task:"",tool:""}]}}function p(e={}){a=e.hideAllViews||a,l=e.setNavActive||l}async function m(){var t;a(),null==(t=document.getElementById("workflowsView"))||t.classList.add("active"),l("navWorkflows"),await async function(){try{const t=await e("/api/agents");s=(t||[]).map(e=>"string"==typeof e?e:e.id||e.agent).filter(Boolean).sort()}catch{s=[]}}(),await async function(){try{const t=await e("/api/skills");i=(t.skills||[]).map(e=>({name:e.name||"",description:e.description||"",type:e.type||(e.url?"api":"knowledge")})).filter(e=>e.name).sort((e,t)=>e.name.localeCompare(t.name))}catch{i=[]}}(),await f()}async function f(){const n=document.getElementById("workflowList");if(n){n.innerHTML='<div class="meta" style="padding:10px;">Loading workflows...</div>';try{const t=await e("/api/workflows/list");d.list=t.workflows||[],u(),d.selectedName?await w(d.selectedName):y(c());const n=t.timezone||Intl.DateTimeFormat().resolvedOptions().timeZone,o=document.getElementById("workflowTimezoneLabel");o&&(o.textContent=`Local timezone: ${n}`)}catch(o){n.innerHTML=`<div class="meta" style="padding:10px;color:var(--red-hi);">Failed to load workflows: ${t(o.message)}</div>`}}}function u(){const e=document.getElementById("workflowList");if(!e)return;const n=d.list||[];n.length?(e.innerHTML=n.map(e=>{var n;const o=e.name===d.selectedName?"background:var(--bg-2);border-color:var(--accent);":"",a=e.schedule?t(e.schedule):'<span style="opacity:0.7;">no schedule</span>',l=(null==(n=e.runState)?void 0:n.running)?'<span style="color:var(--green-hi);font-size:11px;">running</span>':"";return`\n <button class="btn-ghost workflow-row" data-workflow-name="${t(e.name)}" style="width:100%;text-align:left;display:flex;flex-direction:column;gap:4px;padding:10px;margin-bottom:8px;${o}">\n <div style="display:flex;align-items:center;justify-content:space-between;gap:8px;">\n <strong style="font-size:13px;">${t(e.name)}</strong>\n ${l}\n </div>\n <div style="font-size:11px;color:var(--text-3);">${e.enabled?"enabled":"disabled"} · ${e.stageCount||0} stage(s)</div>\n <div style="font-size:11px;color:var(--text-2);font-family:monospace;">${a}</div>\n </button>\n `}).join(""),e.querySelectorAll(".workflow-row").forEach(e=>{e.addEventListener("click",async()=>{const t=e.dataset.workflowName||"";await w(t)})})):e.innerHTML='<div class="meta" style="padding:10px;">No workflows yet.</div>'}async function w(t){if(t)try{const n=await e(`/api/workflows/item?name=${encodeURIComponent(t)}`);d.selectedName=t,d.runStatus=n.runState||{},u(),y({name:t,...n.workflow||{}},n),await async function(t){const n=document.getElementById("workflowLog");if(!n||!t)return;try{const o=(await e(`/api/workflows/log?name=${encodeURIComponent(t)}&limit=120`)).lines||[];n.textContent=o.length?o.join("\n"):"No log lines yet.",n.scrollTop=n.scrollHeight}catch(o){n.textContent=`Failed to load log: ${o.message}`}}(t)}catch(o){n(`Failed to load workflow: ${o.message}`,"error")}}function g(e){const t=String(e||"").trim();if(!t)return"No schedule set. Add a cron expression or use a preset.";return{"*/15 * * * *":"Runs every 15 minutes","0 * * * *":"Runs hourly at minute 0","0 9 * * 1-5":"Runs weekdays at 9:00","0 9 * * *":"Runs daily at 9:00","0 0 * * 1":"Runs every Monday at midnight","0 8 1 * *":"Runs monthly on day 1 at 8:00"}[t]||"Custom cron schedule"}function y(e,o={}){const a=document.getElementById("workflowEditor");if(!a)return;const l={...c(),...e},p=Array.isArray(l.stages)&&l.stages.length?l.stages:c().stages;d.editorWorkflow={...l,stages:p.map(e=>({...e}))};const m=o.cronExample?t(o.cronExample):`*/15 * * * * cd ${t(window.location.pathname||".")} && node scripts/run-scheduled-pipeline.mjs ${t(l.name||"my-workflow")}`,u={agents:s.length?s.map(e=>`<code style="font-size:11px;">${t(e)}</code>`).join(" "):'<span style="font-size:11px;color:var(--text-3);">No agents loaded</span>',skills:i.length?i.map(e=>`<div style="font-size:11px;line-height:1.4;"><code>${t(e.name)}</code> <span style="color:var(--text-3);">(${t(e.type)})</span></div>`).join(""):'<span style="font-size:11px;color:var(--text-3);">No skills loaded</span>'};a.innerHTML=`\n <div class="card" style="display:flex;flex-direction:column;gap:12px;">\n <div style="display:flex;gap:8px;flex-wrap:wrap;">\n <button id="wfOpenTemplateLibraryBtn" class="btn-ghost" style="font-size:12px;">📚 Job Library</button>\n <button id="wfOpenSkillGuideBtn" class="btn-ghost" style="font-size:12px;">🧩 Skills & Agent Options</button>\n <button id="wfOpenJsonEditorBtn" class="btn-ghost" style="font-size:12px;">{ } Advanced JSON</button>\n </div>\n\n <div style="display:flex;gap:10px;flex-wrap:wrap;">\n <div style="flex:1;min-width:260px;">\n <label style="font-size:12px;font-weight:600;">Name</label>\n <input id="wfName" type="text" value="${t(l.name||"")}" placeholder="daily-research" style="width:100%;margin-top:4px;padding:8px 10px;" />\n </div>\n <div style="flex:1;min-width:260px;">\n <label style="font-size:12px;font-weight:600;">Description</label>\n <input id="wfDescription" type="text" value="${t(l.description||"")}" placeholder="What this workflow does" style="width:100%;margin-top:4px;padding:8px 10px;" />\n </div>\n </div>\n\n <div style="display:flex;gap:10px;align-items:flex-end;flex-wrap:wrap;">\n <div style="min-width:280px;flex:1;">\n <label style="font-size:12px;font-weight:600;">Cron Schedule</label>\n <input id="wfSchedule" type="text" value="${t(l.schedule||"")}" placeholder="0 9 * * 1-5" style="width:100%;margin-top:4px;padding:8px 10px;font-family:monospace;" />\n <div style="font-size:11px;color:var(--text-3);margin-top:4px;">Format: minute hour day month weekday</div>\n <div style="display:flex;gap:6px;flex-wrap:wrap;margin-top:8px;">${[{label:"Every 15m",cron:"*/15 * * * *"},{label:"Hourly",cron:"0 * * * *"},{label:"Daily 9am",cron:"0 9 * * *"},{label:"Weekdays 9am",cron:"0 9 * * 1-5"},{label:"Weekly Mon",cron:"0 0 * * 1"},{label:"Monthly",cron:"0 8 1 * *"}].map(e=>`<button class="btn-ghost wf-cron-preset" data-cron="${t(e.cron)}" style="font-size:11px;padding:4px 8px;">${t(e.label)}</button>`).join("")}</div>\n <div id="wfScheduleHint" style="font-size:11px;color:var(--text-2);margin-top:6px;">${t(g(l.schedule))}</div>\n </div>\n <label style="display:flex;align-items:center;gap:8px;font-size:12px;font-weight:600;padding-bottom:8px;">\n <input id="wfEnabled" type="checkbox" ${l.enabled?"checked":""} />\n Enabled\n </label>\n </div>\n\n <div id="workflowTimezoneLabel" style="font-size:11px;color:var(--text-3);"></div>\n\n <div>\n <div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:8px;">\n <label style="font-size:12px;font-weight:600;">Stages (wave-like, runs top to bottom)</label>\n <button id="wfAddStageBtn" class="btn-ghost" style="font-size:12px;">+ Add Stage</button>\n </div>\n <div id="wfStagesWrap" style="display:flex;flex-direction:column;gap:8px;">\n ${p.map((e,n)=>{return`\n <div class="wf-stage-row" data-stage-index="${n}" style="border:1px solid var(--border);border-radius:8px;padding:10px;background:var(--bg-2);">\n <div style="display:flex;align-items:center;justify-content:space-between;gap:10px;margin-bottom:8px;">\n <strong style="font-size:12px;">Stage ${n+1}</strong>\n <div style="display:flex;gap:6px;flex-wrap:wrap;">\n <button class="btn-ghost wf-move-stage-up" data-stage-index="${n}" style="font-size:11px;padding:4px 8px;">↑</button>\n <button class="btn-ghost wf-move-stage-down" data-stage-index="${n}" style="font-size:11px;padding:4px 8px;">↓</button>\n <button class="btn-ghost wf-duplicate-stage" data-stage-index="${n}" style="font-size:11px;padding:4px 8px;">Duplicate</button>\n <button class="btn-red wf-remove-stage" data-stage-index="${n}" style="font-size:11px;padding:4px 8px;">Remove</button>\n </div>\n </div>\n <div style="display:flex;gap:8px;flex-wrap:wrap;margin-bottom:8px;">\n <div style="flex:1;min-width:220px;">\n <label style="font-size:11px;font-weight:600;">Agent</label>\n <select class="wf-agent" style="width:100%;margin-top:4px;padding:6px 8px;">${o=e.agent,Array.from(new Set([...s||[],"crew-main","crew-pm","crew-qa","crew-coder","crew-coder-front","crew-coder-back","crew-copywriter",o||""])).filter(Boolean).sort().map(e=>`<option value="${t(e)}" ${e===o?"selected":""}>${t(e)}</option>`).join("")}</select>\n </div>\n <div style="width:180px;">\n <label style="font-size:11px;font-weight:600;">Tool hint (optional)</label>\n <input class="wf-tool" type="text" value="${t(e.tool||"")}" placeholder="write_file" style="width:100%;margin-top:4px;padding:6px 8px;" />\n </div>\n </div>\n <div>\n <label style="font-size:11px;font-weight:600;">Task</label>\n <textarea class="wf-task" rows="3" style="width:100%;margin-top:4px;padding:8px 10px;font-family:monospace;">${t(e.task||"")}</textarea>\n </div>\n </div>\n `;var o}).join("")}\n </div>\n </div>\n\n <div style="display:flex;gap:8px;flex-wrap:wrap;">\n <button id="wfSaveBtn" class="btn">Save Workflow</button>\n <button id="wfRunBtn" class="btn-green">Run Now</button>\n <button id="wfDeleteBtn" class="btn-red">Delete</button>\n <button id="wfNewBtn" class="btn-ghost">New</button>\n <button id="wfRefreshBtn" class="btn-ghost">Refresh</button>\n </div>\n\n <div>\n <div style="font-size:11px;font-weight:600;color:var(--text-2);margin-bottom:4px;">Crontab example</div>\n <code style="display:block;background:var(--bg-2);border:1px solid var(--border);padding:8px 10px;border-radius:6px;overflow:auto;white-space:nowrap;">${m}</code>\n </div>\n\n <div id="wfLibraryModal" style="display:none;position:fixed;inset:0;background:rgba(0,0,0,0.55);z-index:12000;align-items:center;justify-content:center;padding:18px;">\n <div style="width:min(960px,100%);max-height:85vh;overflow:auto;background:var(--bg-card);border:1px solid var(--border);border-radius:12px;padding:14px;">\n <div style="display:flex;align-items:center;justify-content:space-between;gap:10px;margin-bottom:10px;">\n <div style="font-size:14px;font-weight:700;">Workflow Job Library</div>\n <button id="wfCloseLibraryBtn" class="btn-ghost" style="font-size:12px;">Close</button>\n </div>\n <div style="font-size:12px;color:var(--text-2);margin-bottom:10px;">Pick a starter template, then customize stages/tasks.</div>\n <div style="display:grid;grid-template-columns:repeat(auto-fill,minmax(250px,1fr));gap:10px;">${r.map(e=>`\n <div style="border:1px solid var(--border);border-radius:8px;padding:10px;background:var(--bg-2);">\n <div style="font-size:12px;font-weight:700;">${t(e.name)}</div>\n <div style="font-size:11px;color:var(--text-3);margin-top:4px;">${t(e.description)}</div>\n <div style="font-size:11px;color:var(--text-2);font-family:monospace;margin-top:6px;">${t(e.schedule)}</div>\n <div style="margin-top:8px;">\n <button class="btn-ghost wf-apply-template" data-template-id="${t(e.id)}" style="font-size:11px;">Use Template</button>\n </div>\n </div>\n `).join("")}</div>\n <hr style="border:none;border-top:1px solid var(--border);margin:14px 0;" />\n <div style="font-size:12px;font-weight:700;margin-bottom:6px;">Available Agents</div>\n <div style="display:flex;gap:6px;flex-wrap:wrap;">${u.agents}</div>\n <div style="font-size:12px;font-weight:700;margin:12px 0 6px;">Available Skills</div>\n <div style="display:grid;grid-template-columns:repeat(auto-fill,minmax(230px,1fr));gap:6px;">${u.skills}</div>\n </div>\n </div>\n\n <div id="wfJsonModal" style="display:none;position:fixed;inset:0;background:rgba(0,0,0,0.55);z-index:12000;align-items:center;justify-content:center;padding:18px;">\n <div style="width:min(920px,100%);max-height:85vh;overflow:auto;background:var(--bg-card);border:1px solid var(--border);border-radius:12px;padding:14px;">\n <div style="display:flex;align-items:center;justify-content:space-between;gap:10px;margin-bottom:10px;">\n <div style="font-size:14px;font-weight:700;">Advanced Workflow JSON</div>\n <button id="wfCloseJsonBtn" class="btn-ghost" style="font-size:12px;">Close</button>\n </div>\n <div style="font-size:12px;color:var(--text-2);margin-bottom:8px;">Edit raw JSON. Supports both <code>stages</code> and <code>steps</code>.</div>\n <textarea id="wfJsonTextarea" rows="18" style="width:100%;font-family:monospace;font-size:12px;padding:10px;border:1px solid var(--border);border-radius:8px;background:var(--bg-2);"></textarea>\n <div style="margin-top:10px;display:flex;gap:8px;flex-wrap:wrap;">\n <button id="wfApplyJsonBtn" class="btn-green">Apply JSON</button>\n </div>\n </div>\n </div>\n </div>\n `;const w=document.getElementById("workflowTimezoneLabel");var k,E,B,I,z,S,$,L,A,N,D,T,J,C,O;w&&(w.textContent=`Local timezone: ${Intl.DateTimeFormat().resolvedOptions().timeZone}`),null==(k=document.getElementById("wfOpenTemplateLibraryBtn"))||k.addEventListener("click",e=>{e.preventDefault();const t=document.getElementById("wfLibraryModal");t&&(t.style.display="flex")}),null==(E=document.getElementById("wfOpenSkillGuideBtn"))||E.addEventListener("click",e=>{e.preventDefault();const t=document.getElementById("wfLibraryModal");t&&(t.style.display="flex")}),null==(B=document.getElementById("wfCloseLibraryBtn"))||B.addEventListener("click",e=>{e.preventDefault();const t=document.getElementById("wfLibraryModal");t&&(t.style.display="none")}),null==(I=document.getElementById("wfLibraryModal"))||I.addEventListener("click",e=>{var t;"wfLibraryModal"===(null==(t=e.target)?void 0:t.id)&&(e.currentTarget.style.display="none")}),null==(z=document.getElementById("wfOpenJsonEditorBtn"))||z.addEventListener("click",e=>{var t;e.preventDefault();const n={...v().workflow,...(null==(t=d.editorWorkflow)?void 0:t.steps)?{steps:d.editorWorkflow.steps}:{}},o=document.getElementById("wfJsonTextarea");o&&(o.value=JSON.stringify(n,null,2));const a=document.getElementById("wfJsonModal");a&&(a.style.display="flex")}),null==(S=document.getElementById("wfCloseJsonBtn"))||S.addEventListener("click",e=>{e.preventDefault();const t=document.getElementById("wfJsonModal");t&&(t.style.display="none")}),null==($=document.getElementById("wfJsonModal"))||$.addEventListener("click",e=>{var t;"wfJsonModal"===(null==(t=e.target)?void 0:t.id)&&(e.currentTarget.style.display="none")}),null==(L=document.getElementById("wfApplyJsonBtn"))||L.addEventListener("click",e=>{e.preventDefault();try{const e=document.getElementById("wfJsonTextarea"),t=JSON.parse((null==e?void 0:e.value)||"{}"),o=v();y({name:o.name,description:t.description||o.workflow.description,enabled:t.enabled??o.workflow.enabled,schedule:t.schedule||o.workflow.schedule,timezone:t.timezone||Intl.DateTimeFormat().resolvedOptions().timeZone,stages:Array.isArray(t.stages)&&t.stages.length?t.stages:o.workflow.stages,...Array.isArray(t.steps)?{steps:t.steps}:{}});const a=document.getElementById("wfJsonModal");a&&(a.style.display="none"),n("Applied advanced JSON","success")}catch(t){n(`Invalid JSON: ${t.message}`,"error")}}),document.querySelectorAll(".wf-apply-template").forEach(e=>{e.addEventListener("click",t=>{t.preventDefault();const o=e.dataset.templateId||"",a=r.find(e=>e.id===o);if(!a)return;const l=v();y({name:l.name||a.id,description:a.description,enabled:l.workflow.enabled,schedule:a.schedule,timezone:Intl.DateTimeFormat().resolvedOptions().timeZone,stages:a.stages.map(e=>({...e,tool:e.tool||""}))});const s=document.getElementById("wfLibraryModal");s&&(s.style.display="none"),n(`Applied template: ${a.name}`,"success")})}),document.querySelectorAll(".wf-cron-preset").forEach(e=>{e.addEventListener("click",t=>{t.preventDefault();const n=e.dataset.cron||"",o=document.getElementById("wfSchedule"),a=document.getElementById("wfScheduleHint");o&&(o.value=n),a&&(a.textContent=g(n))})}),null==(A=document.getElementById("wfSchedule"))||A.addEventListener("input",e=>{const t=document.getElementById("wfScheduleHint");t&&(t.textContent=g(e.target.value))}),null==(N=document.getElementById("wfAddStageBtn"))||N.addEventListener("click",e=>{e.preventDefault();const t=v({includeIncompleteStages:!0});t.workflow.stages.push({agent:"crew-main",task:"",tool:""}),y({name:t.name,...t.workflow})}),document.querySelectorAll(".wf-remove-stage").forEach(e=>{e.addEventListener("click",t=>{t.preventDefault();const n=Number(e.dataset.stageIndex||"-1"),o=v({includeIncompleteStages:!0});o.workflow.stages=o.workflow.stages.filter((e,t)=>t!==n),o.workflow.stages.length||(o.workflow.stages=[{agent:"crew-main",task:"",tool:""}]),y({name:o.name,...o.workflow})})}),document.querySelectorAll(".wf-move-stage-up").forEach(e=>{e.addEventListener("click",t=>{t.preventDefault();const n=Number(e.dataset.stageIndex||"-1");if(n<=0)return;const o=v({includeIncompleteStages:!0}),[a]=o.workflow.stages.splice(n,1);o.workflow.stages.splice(n-1,0,a),y({name:o.name,...o.workflow})})}),document.querySelectorAll(".wf-move-stage-down").forEach(e=>{e.addEventListener("click",t=>{t.preventDefault();const n=Number(e.dataset.stageIndex||"-1"),o=v({includeIncompleteStages:!0});if(n<0||n>=o.workflow.stages.length-1)return;const[a]=o.workflow.stages.splice(n,1);o.workflow.stages.splice(n+1,0,a),y({name:o.name,...o.workflow})})}),document.querySelectorAll(".wf-duplicate-stage").forEach(e=>{e.addEventListener("click",t=>{t.preventDefault();const n=Number(e.dataset.stageIndex||"-1"),o=v({includeIncompleteStages:!0});if(n<0||n>=o.workflow.stages.length)return;const a=o.workflow.stages[n];o.workflow.stages.splice(n+1,0,{...a}),y({name:o.name,...o.workflow})})}),null==(D=document.getElementById("wfSaveBtn"))||D.addEventListener("click",x),null==(T=document.getElementById("wfRunBtn"))||T.addEventListener("click",b),null==(J=document.getElementById("wfDeleteBtn"))||J.addEventListener("click",h),null==(C=document.getElementById("wfNewBtn"))||C.addEventListener("click",()=>{d.selectedName="",y(c());const e=document.getElementById("workflowLog");e&&(e.textContent="")}),null==(O=document.getElementById("wfRefreshBtn"))||O.addEventListener("click",async()=>{await f()})}function v(e={}){var t,n,o,a,l;const{includeIncompleteStages:s=!1}=e,i=((null==(t=document.getElementById("wfName"))?void 0:t.value)||"").trim(),r=((null==(n=document.getElementById("wfDescription"))?void 0:n.value)||"").trim(),c=((null==(o=document.getElementById("wfSchedule"))?void 0:o.value)||"").trim(),p=!!(null==(a=document.getElementById("wfEnabled"))?void 0:a.checked),m=Array.from(document.querySelectorAll(".wf-stage-row")).map(e=>{var t,n,o,a,l,s;const i=(null==(n=null==(t=e.querySelector(".wf-agent"))?void 0:t.value)?void 0:n.trim())||"",d=(null==(a=null==(o=e.querySelector(".wf-tool"))?void 0:o.value)?void 0:a.trim())||"";return{agent:i,task:(null==(s=null==(l=e.querySelector(".wf-task"))?void 0:l.value)?void 0:s.trim())||"",...d?{tool:d}:{}}}).filter(e=>s||e.agent&&e.task),f=Array.isArray(null==(l=d.editorWorkflow)?void 0:l.steps)?d.editorWorkflow.steps:[];return{name:i,workflow:{description:r,enabled:p,schedule:c,timezone:Intl.DateTimeFormat().resolvedOptions().timeZone,stages:m,...f.length?{steps:f}:{}}}}async function x(){const e=v();if(e.name)if(e.workflow.stages.length)try{await o("/api/workflows/save",e),d.selectedName=e.name,n(`Saved workflow: ${e.name}`,"success"),await f(),await w(e.name)}catch(t){n(`Save failed: ${t.message}`,"error")}else n("Add at least one stage","error");else n("Workflow name is required","error")}async function b(){const e=v();if(e.name)try{const t=await o("/api/workflows/run",{name:e.name});n(`Started ${e.name}${t.pid?` (pid ${t.pid})`:""}`,"success"),await w(e.name)}catch(t){n(`Run failed: ${t.message}`,"error")}else n("Save workflow first (name required)","warning")}async function h(){const e=v();if(e.name){if(confirm(`Delete workflow "${e.name}"?`))try{await o("/api/workflows/delete",{name:e.name}),n(`Deleted ${e.name}`,"success"),d.selectedName="",await f(),y(c());const t=document.getElementById("workflowLog");t&&(t.textContent="")}catch(t){n(`Delete failed: ${t.message}`,"error")}}else n("No workflow selected","warning")}export{p as i,m as s};
@@ -6,31 +6,31 @@
6
6
  <title>crewswarm dashboard</title>
7
7
  <link rel="icon" type="image/png" href="/favicon.png" />
8
8
  <!-- Font: system stack only to avoid CORS when dashboard (4319) and studio (3333) both load Inter from Google -->
9
- <script type="module" crossorigin src="/assets/index-DqVVQLTW.js"></script>
10
- <link rel="modulepreload" crossorigin href="/assets/core-utils-CAVnDoe1.js">
11
- <link rel="modulepreload" crossorigin href="/assets/setup-wizard-D4g5DMhW.js">
12
- <link rel="modulepreload" crossorigin href="/assets/components-CSUb80ze.js">
9
+ <script type="module" crossorigin src="/assets/index-DnClJ1ee.js"></script>
10
+ <link rel="modulepreload" crossorigin href="/assets/core-utils-CmOkXgzi.js">
11
+ <link rel="modulepreload" crossorigin href="/assets/setup-wizard-CA0Or47w.js">
12
+ <link rel="modulepreload" crossorigin href="/assets/components-BS9fQjE_.js">
13
13
  <link rel="modulepreload" crossorigin href="/assets/orchestration-Ca2DLWN-.js">
14
14
  <link rel="modulepreload" crossorigin href="/assets/cli-process-COMRNPqr.js">
15
- <link rel="modulepreload" crossorigin href="/assets/chat-core-CMoqlR6D.js">
16
- <link rel="modulepreload" crossorigin href="/assets/tab-swarm-chat-tab-BBV9HB2X.js">
15
+ <link rel="modulepreload" crossorigin href="/assets/chat-core-Cx4sTxDd.js">
16
+ <link rel="modulepreload" crossorigin href="/assets/tab-swarm-chat-tab-BNrd88-r.js">
17
17
  <link rel="modulepreload" crossorigin href="/assets/tab-waves-tab-SaJDkb4x.js">
18
- <link rel="modulepreload" crossorigin href="/assets/tab-workflows-tab-6QSXLJ0i.js">
19
- <link rel="modulepreload" crossorigin href="/assets/tab-memory-tab-C59BYFQD.js">
20
- <link rel="modulepreload" crossorigin href="/assets/tab-services-tab-DBj_w3bc.js">
21
- <link rel="modulepreload" crossorigin href="/assets/tab-agents-tab-BThdsdJY.js">
22
- <link rel="modulepreload" crossorigin href="/assets/tab-prompts-tab-C0wZvWK3.js">
23
- <link rel="modulepreload" crossorigin href="/assets/tab-skills-tab-BYdU2whk.js">
24
- <link rel="modulepreload" crossorigin href="/assets/tab-contacts-tab-5LHSthJM.js">
25
- <link rel="modulepreload" crossorigin href="/assets/tab-engines-tab-C3DYxTwy.js">
26
- <link rel="modulepreload" crossorigin href="/assets/tab-swarm-tab-ChqLlEVs.js">
27
- <link rel="modulepreload" crossorigin href="/assets/tab-models-tab-CQzvaeVh.js">
28
- <link rel="modulepreload" crossorigin href="/assets/tab-projects-tab-C6h2Mv1K.js">
29
- <link rel="modulepreload" crossorigin href="/assets/tab-settings-tab-ezeqAjZk.js">
30
- <link rel="modulepreload" crossorigin href="/assets/tab-comms-tab-eHpOSBhG.js">
31
- <link rel="modulepreload" crossorigin href="/assets/tab-usage-tab-B2UWXenJ.js">
32
- <link rel="modulepreload" crossorigin href="/assets/tab-spending-tab-Bg6w9t_p.js">
33
- <link rel="modulepreload" crossorigin href="/assets/tab-pm-loop-tab-D7mnDelU.js">
18
+ <link rel="modulepreload" crossorigin href="/assets/tab-workflows-tab-B-soSy1k.js">
19
+ <link rel="modulepreload" crossorigin href="/assets/tab-memory-tab-Cu6u13EQ.js">
20
+ <link rel="modulepreload" crossorigin href="/assets/tab-services-tab-DU_LH3uG.js">
21
+ <link rel="modulepreload" crossorigin href="/assets/tab-agents-tab-BgpIsjkw.js">
22
+ <link rel="modulepreload" crossorigin href="/assets/tab-prompts-tab-DVkUNaJd.js">
23
+ <link rel="modulepreload" crossorigin href="/assets/tab-skills-tab-BpY0uZHW.js">
24
+ <link rel="modulepreload" crossorigin href="/assets/tab-contacts-tab-DiOyMYth.js">
25
+ <link rel="modulepreload" crossorigin href="/assets/tab-engines-tab-BsdZVvU0.js">
26
+ <link rel="modulepreload" crossorigin href="/assets/tab-swarm-tab-B1AcjL1W.js">
27
+ <link rel="modulepreload" crossorigin href="/assets/tab-models-tab-BLEjmd19.js">
28
+ <link rel="modulepreload" crossorigin href="/assets/tab-projects-tab-DhNWnlzt.js">
29
+ <link rel="modulepreload" crossorigin href="/assets/tab-settings-tab-Bn4nXtDe.js">
30
+ <link rel="modulepreload" crossorigin href="/assets/tab-comms-tab-kguqTIzD.js">
31
+ <link rel="modulepreload" crossorigin href="/assets/tab-usage-tab-BIOOnB-Y.js">
32
+ <link rel="modulepreload" crossorigin href="/assets/tab-spending-tab-DEccQHnt.js">
33
+ <link rel="modulepreload" crossorigin href="/assets/tab-pm-loop-tab-Bfd449B4.js">
34
34
  <link rel="stylesheet" crossorigin href="/assets/index-CF0aJRtC.css">
35
35
  </head>
36
36
  <body>
@@ -3519,7 +3519,7 @@
3519
3519
  >
3520
3520
  <input
3521
3521
  id="tgAllowedIds"
3522
- placeholder="1693963111, 987654321"
3522
+ placeholder="123456789, 987654321"
3523
3523
  style="width: 100%; margin-bottom: 12px"
3524
3524
  />
3525
3525
  <div id="tgContactNamesList" style="margin-bottom: 12px"></div>
@@ -3494,7 +3494,7 @@
3494
3494
  >
3495
3495
  <input
3496
3496
  id="tgAllowedIds"
3497
- placeholder="1693963111, 987654321"
3497
+ placeholder="123456789, 987654321"
3498
3498
  style="width: 100%; margin-bottom: 12px"
3499
3499
  />
3500
3500
  <div id="tgContactNamesList" style="margin-bottom: 12px"></div>
@@ -4402,6 +4402,76 @@
4402
4402
  ></div>
4403
4403
  </div>
4404
4404
 
4405
+ <div class="card" style="margin-top: 16px">
4406
+ <div
4407
+ style="
4408
+ display: flex;
4409
+ align-items: center;
4410
+ justify-content: space-between;
4411
+ flex-wrap: wrap;
4412
+ gap: 12px;
4413
+ "
4414
+ >
4415
+ <div>
4416
+ <div class="card-title" style="margin-bottom: 2px">
4417
+ 🔌 tmux-bridge Sessions
4418
+ </div>
4419
+ <div
4420
+ style="
4421
+ font-size: 11px;
4422
+ color: var(--text-3);
4423
+ line-height: 1.5;
4424
+ "
4425
+ >
4426
+ Enable persistent tmux sessions that survive across pipeline
4427
+ waves. Agents can hand off live execution context (running
4428
+ servers, env vars, cwd) to the next wave instead of
4429
+ cold-starting. Requires
4430
+ <code
4431
+ style="
4432
+ background: var(--bg-1);
4433
+ padding: 1px 4px;
4434
+ border-radius: 3px;
4435
+ "
4436
+ >tmux</code
4437
+ >
4438
+ +
4439
+ <code
4440
+ style="
4441
+ background: var(--bg-1);
4442
+ padding: 1px 4px;
4443
+ border-radius: 3px;
4444
+ "
4445
+ >smux</code
4446
+ >
4447
+ installed. One writer per session (lock enforced).
4448
+ </div>
4449
+ </div>
4450
+ <button
4451
+ id="tmuxBridgeBtn"
4452
+ data-action="toggleTmuxBridge"
4453
+ style="
4454
+ font-size: 12px;
4455
+ font-weight: 700;
4456
+ padding: 8px 18px;
4457
+ border-radius: 8px;
4458
+ cursor: pointer;
4459
+ border: 1px solid var(--border);
4460
+ background: var(--surface-2);
4461
+ color: var(--text-2);
4462
+ white-space: nowrap;
4463
+ min-width: 80px;
4464
+ "
4465
+ >
4466
+ Loading…
4467
+ </button>
4468
+ </div>
4469
+ <div
4470
+ id="tmuxBridgeStatus"
4471
+ style="margin-top: 8px; font-size: 12px; color: var(--text-3)"
4472
+ ></div>
4473
+ </div>
4474
+
4405
4475
  <div class="card" style="margin-top: 16px">
4406
4476
  <div
4407
4477
  style="
@@ -181,6 +181,8 @@ import {
181
181
  loadLoopBrain,
182
182
  saveLoopBrain,
183
183
  loadEnvAdvanced,
184
+ loadTmuxBridge,
185
+ toggleTmuxBridge,
184
186
  } from "./tabs/settings-tab.js";
185
187
  import {
186
188
  initCommsTab,
@@ -1739,6 +1741,7 @@ function showSettingsTab(tab) {
1739
1741
  loadGlobalFallback();
1740
1742
  loadConfigLockStatus();
1741
1743
  loadCursorWaves();
1744
+ loadTmuxBridge();
1742
1745
  loadAutonomousMentions();
1743
1746
  loadClaudeCode();
1744
1747
  loadCodexExecutor();
@@ -2181,6 +2184,7 @@ const ACTION_REGISTRY = {
2181
2184
  saveGlobalFallback,
2182
2185
  toggleBgConsciousness,
2183
2186
  toggleCursorWaves,
2187
+ toggleTmuxBridge,
2184
2188
  toggleAutonomousMentions,
2185
2189
  toggleClaudeCode,
2186
2190
  toggleCodexExecutor,
@@ -2772,6 +2776,7 @@ Object.assign(window, {
2772
2776
  toggleAddSkill,
2773
2777
  toggleBgConsciousness,
2774
2778
  toggleCursorWaves,
2779
+ toggleTmuxBridge,
2775
2780
  toggleClaudeCode,
2776
2781
  toggleEmojiPicker,
2777
2782
  updateSkillAuthFields,
@@ -172,19 +172,27 @@ export function appendChatBubble(
172
172
  if (engineUsed) {
173
173
  const engineColors = {
174
174
  claude: "#e07a5f",
175
+ "claude-code": "#e07a5f",
175
176
  codex: "#8338ec",
176
177
  cursor: "#3d405b",
177
178
  opencode: "#06d6a0",
178
179
  gemini: "#4285f4",
180
+ "gemini-cli": "#4285f4",
179
181
  "docker-sandbox": "#0db7ed",
182
+ "crew-cli": "#8b5cf6",
183
+ "direct-llm": "#6b7280",
180
184
  };
181
185
  const engineLabels = {
182
186
  claude: "🤖 Claude Code",
187
+ "claude-code": "🤖 Claude Code",
183
188
  codex: "🟣 Codex",
184
189
  cursor: "🖱 Cursor",
185
190
  opencode: "⚡ OpenCode",
186
191
  gemini: "✨ Gemini",
192
+ "gemini-cli": "✨ Gemini",
187
193
  "docker-sandbox": "🐳 Docker",
194
+ "crew-cli": "🔧 crew-cli",
195
+ "direct-llm": "💬 LLM Direct",
188
196
  };
189
197
  const engineBadge = document.createElement("span");
190
198
  engineBadge.title =
@@ -290,6 +290,36 @@ export async function toggleCursorWaves() {
290
290
  } catch(e) { showNotification('Failed: ' + e.message, 'error'); }
291
291
  }
292
292
 
293
+ export async function loadTmuxBridge() {
294
+ const btn = document.getElementById('tmuxBridgeBtn');
295
+ const status = document.getElementById('tmuxBridgeStatus');
296
+ try {
297
+ const d = await getJSON('/api/settings/tmux-bridge');
298
+ const on = d.enabled;
299
+ if (btn) {
300
+ btn.textContent = on ? '🔌 ON' : '⚫ OFF';
301
+ btn.style.background = on ? 'rgba(52,211,153,0.15)' : 'var(--surface-2)';
302
+ btn.style.borderColor = on ? 'rgba(52,211,153,0.3)' : 'var(--border)';
303
+ btn.style.color = on ? 'var(--green)' : 'var(--text-2)';
304
+ }
305
+ if (status) status.textContent = on
306
+ ? 'Active — agents can share persistent tmux sessions across pipeline waves. Requires tmux + smux.'
307
+ : 'Off — agents use standard cold-start execution (no session persistence).';
308
+ } catch(e) {
309
+ if (btn) btn.textContent = 'Error';
310
+ if (status) status.textContent = 'Could not load: ' + e.message;
311
+ }
312
+ }
313
+
314
+ export async function toggleTmuxBridge() {
315
+ try {
316
+ const current = await getJSON('/api/settings/tmux-bridge');
317
+ const d = await postJSON('/api/settings/tmux-bridge', { enabled: !current.enabled });
318
+ showNotification('tmux-bridge ' + (d.enabled ? 'ENABLED 🔌' : 'DISABLED'));
319
+ loadTmuxBridge();
320
+ } catch(e) { showNotification('Failed: ' + e.message, 'error'); }
321
+ }
322
+
293
323
  export async function loadAutonomousMentions() {
294
324
  const btn = document.getElementById('autonomousMentionsBtn');
295
325
  const status = document.getElementById('autonomousMentionsStatus');
@@ -691,6 +721,34 @@ const ENV_GROUPS = [
691
721
  { key: 'SHARED_MEMORY_DIR', hint: 'Directory for shared memory files', default: '~/.crewswarm/memory' },
692
722
  ],
693
723
  },
724
+ {
725
+ label: 'crew-cli — Streaming & Hooks',
726
+ note: 'Controls for crew-cli streaming output, tool hooks, and session token limits.',
727
+ vars: [
728
+ { key: 'CREW_NO_STREAM', hint: 'Disable streaming output — tokens arrive after full response (true/false)', default: 'false' },
729
+ { key: 'CREW_HOOKS_FILE', hint: 'Path to hooks.json for PreToolUse/PostToolUse hooks', default: '.crew/hooks.json' },
730
+ { key: 'CREW_MAX_SESSION_TOKENS', hint: 'Max estimated tokens per session before oldest turns are trimmed', default: '100000' },
731
+ ],
732
+ },
733
+ {
734
+ label: 'crew-cli — Codebase Index & RAG',
735
+ note: 'Codebase embedding index auto-builds on startup. Injects relevant file context into every worker prompt.',
736
+ vars: [
737
+ { key: 'CREW_RAG_MODE', hint: 'RAG mode: auto (use index when ready, else keyword), semantic, keyword, import-graph, off', default: 'auto' },
738
+ { key: 'CREW_EMBEDDING_PROVIDER', hint: 'Embedding provider: local (zero-cost), openai (best), gemini (free tier)', default: 'local' },
739
+ { key: 'CREW_RAG_WORKER_BUDGET', hint: 'Max tokens of RAG context injected per worker (approximate)', default: '4000' },
740
+ { key: 'CREW_RAG_MAX_FILES', hint: 'Max code files to index (larger repos should increase this)', default: '2000' },
741
+ { key: 'CREW_RAG_BATCH_SIZE', hint: 'Files per embedding batch (higher = faster but more API calls)', default: '20' },
742
+ ],
743
+ },
744
+ {
745
+ label: 'crew-cli — Checkpointing',
746
+ note: 'Automatic git checkpoints during pipeline execution for easy rollback.',
747
+ vars: [
748
+ { key: 'CREW_AUTO_CHECKPOINT', hint: 'Enable auto-commit at task boundaries (true/false)', default: 'true' },
749
+ { key: 'CREW_CHECKPOINT_INTERVAL_MS', hint: 'Periodic git stash snapshot interval during long tasks (ms, 0=off)', default: '60000' },
750
+ ],
751
+ },
694
752
  {
695
753
  label: 'PM Loop',
696
754
  vars: [