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.
- package/README.md +2 -2
- package/apps/dashboard/dist/assets/{chat-core-CMoqlR6D.js → chat-core-Cx4sTxDd.js} +1 -1
- package/apps/dashboard/dist/assets/chat-core-Cx4sTxDd.js.br +0 -0
- package/apps/dashboard/dist/assets/cli-process-COMRNPqr.js.br +0 -0
- package/apps/dashboard/dist/assets/{components-CSUb80ze.js → components-BS9fQjE_.js} +1 -1
- package/apps/dashboard/dist/assets/components-BS9fQjE_.js.br +0 -0
- package/apps/dashboard/dist/assets/core-utils-CmOkXgzi.js +1 -0
- package/apps/dashboard/dist/assets/core-utils-CmOkXgzi.js.br +0 -0
- package/apps/dashboard/dist/assets/index-CF0aJRtC.css.br +0 -0
- package/apps/dashboard/dist/assets/{index-DqVVQLTW.js → index-DnClJ1ee.js} +2 -2
- package/apps/dashboard/dist/assets/index-DnClJ1ee.js.br +0 -0
- package/apps/dashboard/dist/assets/orchestration-Ca2DLWN-.js.br +0 -0
- package/apps/dashboard/dist/assets/{setup-wizard-D4g5DMhW.js → setup-wizard-CA0Or47w.js} +1 -1
- package/apps/dashboard/dist/assets/setup-wizard-CA0Or47w.js.br +0 -0
- package/apps/dashboard/dist/assets/{tab-agents-tab-BThdsdJY.js → tab-agents-tab-BgpIsjkw.js} +1 -1
- package/apps/dashboard/dist/assets/tab-agents-tab-BgpIsjkw.js.br +0 -0
- package/apps/dashboard/dist/assets/{tab-benchmarks-tab-DfCuAClu.js → tab-benchmarks-tab-BHjKCPm3.js} +1 -1
- package/apps/dashboard/dist/assets/{tab-comms-tab-eHpOSBhG.js → tab-comms-tab-kguqTIzD.js} +1 -1
- package/apps/dashboard/dist/assets/tab-comms-tab-kguqTIzD.js.br +0 -0
- package/apps/dashboard/dist/assets/{tab-contacts-tab-5LHSthJM.js → tab-contacts-tab-DiOyMYth.js} +1 -1
- package/apps/dashboard/dist/assets/tab-contacts-tab-DiOyMYth.js.br +0 -0
- package/apps/dashboard/dist/assets/{tab-engines-tab-C3DYxTwy.js → tab-engines-tab-BsdZVvU0.js} +1 -1
- package/apps/dashboard/dist/assets/tab-engines-tab-BsdZVvU0.js.br +0 -0
- package/apps/dashboard/dist/assets/{tab-memory-tab-C59BYFQD.js → tab-memory-tab-Cu6u13EQ.js} +1 -1
- package/apps/dashboard/dist/assets/tab-memory-tab-Cu6u13EQ.js.br +0 -0
- package/apps/dashboard/dist/assets/{tab-models-tab-CQzvaeVh.js → tab-models-tab-BLEjmd19.js} +1 -1
- package/apps/dashboard/dist/assets/tab-models-tab-BLEjmd19.js.br +0 -0
- package/apps/dashboard/dist/assets/{tab-pm-loop-tab-D7mnDelU.js → tab-pm-loop-tab-Bfd449B4.js} +1 -1
- package/apps/dashboard/dist/assets/tab-pm-loop-tab-Bfd449B4.js.br +0 -0
- package/apps/dashboard/dist/assets/{tab-projects-tab-C6h2Mv1K.js → tab-projects-tab-DhNWnlzt.js} +1 -1
- package/apps/dashboard/dist/assets/tab-projects-tab-DhNWnlzt.js.br +0 -0
- package/apps/dashboard/dist/assets/{tab-prompts-tab-C0wZvWK3.js → tab-prompts-tab-DVkUNaJd.js} +1 -1
- package/apps/dashboard/dist/assets/tab-prompts-tab-DVkUNaJd.js.br +0 -0
- package/apps/dashboard/dist/assets/{tab-services-tab-DBj_w3bc.js → tab-services-tab-DU_LH3uG.js} +1 -1
- package/apps/dashboard/dist/assets/tab-services-tab-DU_LH3uG.js.br +0 -0
- package/apps/dashboard/dist/assets/{tab-settings-tab-ezeqAjZk.js → tab-settings-tab-Bn4nXtDe.js} +1 -1
- package/apps/dashboard/dist/assets/tab-settings-tab-Bn4nXtDe.js.br +0 -0
- package/apps/dashboard/dist/assets/{tab-skills-tab-BYdU2whk.js → tab-skills-tab-BpY0uZHW.js} +1 -1
- package/apps/dashboard/dist/assets/tab-skills-tab-BpY0uZHW.js.br +0 -0
- package/apps/dashboard/dist/assets/{tab-spending-tab-Bg6w9t_p.js → tab-spending-tab-DEccQHnt.js} +1 -1
- package/apps/dashboard/dist/assets/tab-spending-tab-DEccQHnt.js.br +0 -0
- package/apps/dashboard/dist/assets/{tab-swarm-chat-tab-BBV9HB2X.js → tab-swarm-chat-tab-BNrd88-r.js} +1 -1
- package/apps/dashboard/dist/assets/tab-swarm-chat-tab-BNrd88-r.js.br +0 -0
- package/apps/dashboard/dist/assets/{tab-swarm-tab-ChqLlEVs.js → tab-swarm-tab-B1AcjL1W.js} +1 -1
- package/apps/dashboard/dist/assets/tab-swarm-tab-B1AcjL1W.js.br +0 -0
- package/apps/dashboard/dist/assets/{tab-usage-tab-B2UWXenJ.js → tab-usage-tab-BIOOnB-Y.js} +1 -1
- package/apps/dashboard/dist/assets/tab-usage-tab-BIOOnB-Y.js.br +0 -0
- package/apps/dashboard/dist/assets/tab-waves-tab-SaJDkb4x.js.br +0 -0
- package/apps/dashboard/dist/assets/{tab-workflows-tab-6QSXLJ0i.js → tab-workflows-tab-B-soSy1k.js} +1 -1
- package/apps/dashboard/dist/assets/tab-workflows-tab-B-soSy1k.js.br +0 -0
- package/apps/dashboard/dist/index.html +23 -23
- package/apps/dashboard/dist/index.html.br +0 -0
- package/apps/dashboard/dist/index.html.gz +0 -0
- package/apps/dashboard/index.html +71 -1
- package/apps/dashboard/src/app.js +5 -0
- package/apps/dashboard/src/core/dom.js +8 -0
- package/apps/dashboard/src/tabs/settings-tab.js +58 -0
- package/apps/vibe/.crew/agent-memory/pipeline.json +12 -1
- package/apps/vibe/.crew/cost.json +3 -3
- package/apps/vibe/.crew/json-parse-metrics.jsonl +1 -0
- package/apps/vibe/.crew/pipeline-metrics.jsonl +1 -0
- package/apps/vibe/.crew/pipeline-runs/pipeline-c1418f4e-b773-4ca1-84a3-216acf36e2f2.jsonl +5 -0
- package/apps/vibe/.crew/session.json +10 -1
- package/apps/vibe/.studio-data/project-messages/general.jsonl +3 -0
- package/apps/vibe/index.html +4 -2
- package/apps/vibe/server.mjs +75 -3
- package/apps/vibe/src/main.js +126 -53
- package/crew-lead.mjs +14 -1
- package/lib/bridges/cli-executor.mjs +0 -2
- package/lib/bridges/tmux-bridge.mjs +200 -0
- package/lib/chat/unified-history.mjs +1 -1
- package/lib/cli-process-tracker.mjs +2 -1
- package/lib/crew-lead/http-server.mjs +286 -1
- package/lib/crew-lead/wave-dispatcher.mjs +40 -3
- package/lib/engines/crew-cli.mjs +3 -2
- package/lib/engines/llm-direct.mjs +4 -1
- package/lib/engines/rt-envelope.mjs +14 -5
- package/lib/engines/runners.mjs +30 -4
- package/lib/runtime/config.mjs +7 -0
- package/lib/sessions/session-manager.mjs +287 -0
- package/package.json +1 -1
- package/scripts/bench/performance_optimization.py +81 -0
- package/whatsapp-bridge.mjs +54 -10
- package/apps/dashboard/dist/assets/core-utils-CAVnDoe1.js +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
import{g as e,e as t,p as n,s as o}from"./core-utils-CAVnDoe1.js";let a=()=>{},r=()=>{},s={},i={},c={},d={},l={};function p(e={}){a=e.showSettings||a,r=e.showSettingsTab||r}function m(){a(),r("comms")}async function g(){await Promise.allSettled([u(),z(),S(),v(),w(),C(),N()])}async function u(){try{const t=await e("/api/telegram/status"),n=document.getElementById("tgStatusBadge");if(!n)return;t.running?(n.textContent=t.botName?"● @"+t.botName:"● running",n.className="status-badge status-active"):(n.textContent="● stopped",n.className="status-badge status-stopped")}catch{}}function x(){var e;const t=document.getElementById("tgContactNamesList");if(!t)return;const n=((null==(e=document.getElementById("tgAllowedIds"))?void 0:e.value)||"").trim(),o=n?n.split(",").map(e=>parseInt(e.trim(),10)).filter(e=>!isNaN(e)):[];if(t.innerHTML="",!o.length)return;const a=document.createElement("label");a.style.cssText="display:block;margin-bottom:6px;font-size:12px;color:var(--text-2);",a.textContent="Contact names & routing",t.appendChild(a),o.forEach(e=>{const n=document.createElement("div");n.style.cssText="margin-bottom:12px;padding:12px;background:var(--bg-1);border:1px solid var(--border);border-radius:6px;";const o=document.createElement("div");o.style.cssText="display:grid;grid-template-columns:100px 1fr;gap:8px;margin-bottom:8px;align-items:center;";const a=document.createElement("span");a.style.cssText="font-size:11px;color:var(--text-3);font-family:monospace;",a.textContent=String(e);const r=document.createElement("input");r.id="tgContact-"+e,r.placeholder="Name (e.g. Jeff)",r.value=c[String(e)]||"",r.style.cssText="font-size:12px;padding:6px 8px;background:var(--bg-card);border:1px solid var(--border);border-radius:4px;color:var(--text-1);",o.appendChild(a),o.appendChild(r);const s=document.createElement("div");s.style.cssText="display:grid;grid-template-columns:100px 1fr;gap:8px;align-items:center;";const i=document.createElement("span");i.style.cssText="font-size:10px;color:var(--text-3);text-transform:uppercase;letter-spacing:0.05em;",i.textContent="Routes to →";const l=document.createElement("select");l.id="tgRoute-"+e,l.style.cssText="font-size:12px;padding:6px 8px;background:var(--bg-card);border:1px solid var(--border);border-radius:4px;color:var(--text-1);";const p=d[String(e)]||"",m=document.createElement("option");m.value="",m.textContent="— default (crew-lead) —",l.appendChild(m),["crew-lead","crew-main","crew-coder","crew-pm","crew-qa","crew-fixer","crew-security","crew-frontend","crew-coder-front","crew-coder-back","crew-github","crew-copywriter","crew-researcher","crew-architect","crew-seo","crew-ml","crew-mega","crew-loco"].forEach(e=>{const t=document.createElement("option");t.value=e,t.textContent=e,e===p&&(t.selected=!0),l.appendChild(t)}),l.addEventListener("change",t=>{const n=t.target.value;n?d[String(e)]=n:delete d[String(e)]}),s.appendChild(i),s.appendChild(l),n.appendChild(o),n.appendChild(s),t.appendChild(n)})}async function v(){try{const t=await e("/api/telegram/config");t.token&&(document.getElementById("tgTokenInput").value=t.token);const n=t.allowedChatIds&&t.allowedChatIds.length?t.allowedChatIds:[];document.getElementById("tgAllowedIds").value=n.join(", "),c=t.contactNames||{},d=t.userRouting||{},l=t.topicRouting||{},x(),function(){const t=document.getElementById("tgTopicRoutingContainer");if(!t)return;t.innerHTML="";const n=document.createElement("div");n.style.cssText="margin-bottom:12px;",n.innerHTML='\n <div style="font-size:13px;font-weight:600;margin-bottom:4px;">📌 Topic Routing (Optional)</div>\n <div style="font-size:11px;color:var(--text-3);line-height:1.4;">\n Route different topics in Forum groups to different agents.\n </div>\n ',t.appendChild(n);const a=document.createElement("div");a.style.cssText="display:flex;gap:8px;margin-bottom:12px;";const r=document.createElement("button");r.textContent="🔍 Auto-discover Topics",r.className="btn-ghost",r.style.cssText="flex:1;font-size:12px;",r.onclick=()=>async function(){try{const t=await e("/api/telegram/discover-topics");if(!t.length)return void o("No topics found in logs. Send messages to topics first.",!0);const n={};t.forEach(e=>{n[e.chatId]||(n[e.chatId]=[]),n[e.chatId].push(e)});Object.keys(l).filter(e=>!e.startsWith("_"));let a='<div style="max-height:400px;overflow-y:auto;">';Object.entries(n).forEach(([e,t])=>{a+=`\n <div style="background:var(--bg-1);border:1px solid var(--border);border-radius:6px;padding:12px;margin-bottom:12px;">\n <div style="font-weight:600;margin-bottom:8px;font-family:monospace;font-size:12px;">\n Group ${e}\n </div>\n <div style="margin-left:12px;">\n `,t.forEach(t=>{var n;const o=String(t.threadId),r=null==(n=l[e])?void 0:n[o];a+=`\n <div style="margin-bottom:6px;display:flex;align-items:center;gap:8px;">\n <input type="checkbox" class="discover-topic-check" data-chat-id="${e}" data-thread-id="${o}" ${r?"":"checked"} ${r?"disabled":""}>\n <span style="font-size:11px;color:var(--text-2);">Topic ${o}${r?' <span style="font-size:10px;color:var(--text-3);">(already configured)</span>':""}</span>\n </div>\n `}),a+="\n </div>\n </div>\n "}),a+="</div>";const r=document.createElement("div");r.style.cssText="position:fixed;top:0;left:0;right:0;bottom:0;background:rgba(0,0,0,0.6);display:flex;align-items:center;justify-content:center;z-index:9999;",r.innerHTML=`\n <div style="background:var(--bg-card);border:1px solid var(--border);border-radius:8px;padding:20px;max-width:600px;width:90%;">\n <h3 style="margin:0 0 16px;font-size:16px;">Discovered Topics</h3>\n <div style="font-size:11px;color:var(--text-3);margin-bottom:12px;">\n ✓ Select which topics to add. Already configured topics are disabled.\n </div>\n ${a}\n <div style="display:flex;gap:8px;margin-top:16px;justify-content:flex-end;">\n <button class="btn-ghost" id="discoverCancel">Cancel</button>\n <button class="btn-primary" id="discoverConfirm">Add Selected</button>\n </div>\n </div>\n `,document.body.appendChild(r),document.getElementById("discoverCancel").onclick=()=>{document.body.removeChild(r)},document.getElementById("discoverConfirm").onclick=()=>{const e=Array.from(r.querySelectorAll(".discover-topic-check:checked"));let t=0;e.forEach(e=>{const n=e.dataset.chatId,o=e.dataset.threadId;l[n]||(l[n]={}),l[n][o]||(l[n][o]="crew-lead",t++)}),document.body.removeChild(r),f(),o(`Added ${t} new topic${1!==t?"s":""}! Set agents and click Save.`)}}catch(t){o("Error discovering topics: "+t.message,!0)}}();const s=document.createElement("button");s.textContent="➕ Add New Group",s.className="btn-ghost",s.style.cssText="flex:1;font-size:12px;",s.onclick=()=>function(){const e=prompt("Enter Group ID (e.g., -100123456789):");if(!e||!e.trim())return;const t=e.trim();l[t]||(l[t]={});const n="1";l[t][n]="crew-lead",f(),o(`Added group ${t}`)}(),a.appendChild(r),a.appendChild(s),t.appendChild(a);const i=document.createElement("div");i.id="tgTopicsList",i.style.cssText="margin-bottom:12px;",t.appendChild(i),f();const c=document.createElement("details");c.style.cssText="margin-top:12px;",c.innerHTML='\n <summary style="cursor:pointer;font-size:11px;color:var(--text-3);padding:6px 0;">\n ⚙️ Advanced: Edit JSON directly\n </summary>\n ';const d=document.createElement("textarea");d.id="tgTopicRoutingJson",d.placeholder='{\n "-100123456789": {\n "5": "crew-coder",\n "8": "crew-copywriter"\n }\n}',d.value=Object.keys(l).length?JSON.stringify(l,null,2):"",d.style.cssText="width:100%;min-height:100px;font-family:monospace;font-size:11px;padding:8px;background:var(--bg-1);border:1px solid var(--border);border-radius:4px;color:var(--text-1);resize:vertical;margin-top:8px;",c.appendChild(d),t.appendChild(c)}()}catch{}}function f(){const e=document.getElementById("tgTopicsList");if(!e)return;e.innerHTML="";const t=["crew-lead","crew-main","crew-coder","crew-pm","crew-qa","crew-fixer","crew-security","crew-frontend","crew-coder-front","crew-coder-back","crew-github","crew-copywriter","crew-researcher","crew-architect","crew-seo","crew-ml","crew-mega","crew-loco"],n={};if(Object.entries(l).forEach(([e,t])=>{if(!e.startsWith("_"))if("object"==typeof t)Object.entries(t).forEach(([t,o])=>{n[e]||(n[e]=[]),n[e].push({topicId:t,agent:o})});else{const[o,a]=e.split(":");n[o]||(n[o]=[]),n[o].push({topicId:a,agent:t})}}),0===Object.keys(n).length){const t=document.createElement("div");return t.style.cssText="padding:12px;text-align:center;color:var(--text-3);font-size:11px;background:var(--bg-1);border:1px dashed var(--border);border-radius:4px;",t.textContent='No topics configured. Click "Auto-discover" or "Add Manually" above.',void e.appendChild(t)}let a=0;Object.entries(n).forEach(([n,r])=>{const s=document.createElement("div");s.className="tg-topic-group",s.style.cssText="margin-bottom:16px;padding:12px;background:var(--bg-1);border:1px solid var(--border);border-radius:6px;";const i=document.createElement("div");i.style.cssText="display:flex;align-items:center;gap:8px;margin-bottom:10px;padding-bottom:8px;border-bottom:1px solid var(--border);";const c=document.createElement("span");c.textContent="Group ID:",c.style.cssText="font-size:11px;color:var(--text-3);text-transform:uppercase;letter-spacing:0.05em;";const d=document.createElement("input");d.value=n,d.className="tg-topic-group-chatid",d.dataset.groupChatId=n,d.style.cssText="flex:1;font-size:11px;padding:5px 8px;background:var(--bg-card);border:1px solid var(--border);border-radius:3px;color:var(--text-1);font-family:monospace;";const p=document.createElement("button");p.textContent="➕",p.className="btn-ghost",p.title="Add topic to this group",p.style.cssText="font-size:14px;padding:4px 8px;width:32px;height:28px;",p.addEventListener("click",e=>{e.preventDefault(),function(e){l[e]||(l[e]={});const t=Object.keys(l[e]).map(e=>parseInt(e,10)).filter(e=>!isNaN(e)),n=t.length>0?String(Math.max(...t)+1):"1";l[e][n]="crew-lead",f(),o(`Added topic ${n} to group`)}(n)});const m=document.createElement("button");m.textContent="🗑",m.className="btn-ghost",m.title="Delete entire group and all topics",m.style.cssText="font-size:14px;padding:4px 8px;width:32px;height:28px;",m.addEventListener("click",e=>{e.preventDefault(),e.stopPropagation(),window.confirm(`Delete group ${n} and all topics?`)&&(delete l[String(n)],f(),o("Group removed - click Save to persist"))}),i.appendChild(c),i.appendChild(d),i.appendChild(p),i.appendChild(m),s.appendChild(i),r.forEach(e=>{const r=document.createElement("div");r.style.cssText="display:grid;grid-template-columns:80px 1fr 36px;gap:8px;align-items:center;padding:6px 8px;margin-bottom:4px;",r.dataset.idx=a,r.dataset.chatId=n,r.dataset.originalTopicId=e.topicId;const i=document.createElement("input");i.value=e.topicId,i.placeholder="Topic 5",i.className="tg-topic-id",i.style.cssText="font-size:11px;padding:5px 8px;background:var(--bg-card);border:1px solid var(--border);border-radius:3px;color:var(--text-1);font-family:monospace;text-align:center;";const c=document.createElement("select");c.className="tg-topic-agent",c.style.cssText="font-size:11px;padding:5px 8px;background:var(--bg-card);border:1px solid var(--border);border-radius:3px;color:var(--text-1);",t.forEach(t=>{const n=document.createElement("option");n.value=t,n.textContent=t,t===e.agent&&(n.selected=!0),c.appendChild(n)}),c.addEventListener("change",t=>{const o=t.target.value,a=r.querySelector(".tg-topic-id"),s=a?a.value.trim():e.topicId;if(s&&l[n]){l[n][s]=o;const e=document.getElementById("tgTopicRoutingJson");e&&(e.value=JSON.stringify(l,null,2))}});const d=document.createElement("button");d.textContent="🗑",d.className="btn-ghost",d.title="Remove this topic",d.style.cssText="font-size:14px;padding:4px;width:28px;height:28px;",d.addEventListener("click",t=>{t.preventDefault(),t.stopPropagation(),window.confirm(`Remove topic ${e.topicId}?`)&&l[n]&&l[n][e.topicId]&&(delete l[n][e.topicId],0===Object.keys(l[n]).length&&delete l[n],f(),o(`Topic ${e.topicId} removed - click Save to persist`))}),r.appendChild(i),r.appendChild(c),r.appendChild(d),s.appendChild(r),a++}),e.appendChild(s)});const r=document.getElementById("tgTopicRoutingJson");r&&(r.value=Object.keys(l).length?JSON.stringify(l,null,2):"")}async function y(){const e=document.getElementById("tgTokenInput").value.trim(),t=document.getElementById("tgAllowedIds").value.trim(),a=t?t.split(",").map(e=>parseInt(e.trim(),10)).filter(e=>!isNaN(e)):[];if(!e)return void o("Enter a bot token first",!0);const r={},s={};a.forEach(e=>{const t=document.getElementById("tgContact-"+e);t&&t.value.trim()&&(r[String(e)]=t.value.trim());const n=document.getElementById("tgRoute-"+e);n&&n.value&&(s[String(e)]=n.value)});const i=document.getElementById("tgTopicsList");if(i){i.querySelectorAll(".tg-topic-group").forEach(e=>{const t=e.querySelector("[data-group-chat-id]");if(!t)return;const n=t.dataset.groupChatId,o=t.value.trim();n&&o&&n!==o&&l[n]&&(l[o]=l[n],delete l[n]);const a=o||n;if(!a)return;l[a]||(l[a]={});e.querySelectorAll("[data-chat-id]").forEach(e=>{const t=e.querySelector(".tg-topic-id"),n=e.querySelector(".tg-topic-agent");if(t&&n){const o=e.dataset.originalTopicId||t.value.trim(),r=t.value.trim(),s=n.value;if(!r)return;o&&r&&o!==r?(l[a][o]&&delete l[a][o],l[a][r]=s):l[a][r]=s}})})}const p={...l},m=document.getElementById("tgTopicRoutingJson");if(m&&m.value.trim())try{const e=JSON.parse(m.value.trim());Object.assign(p,e)}catch(g){if(0===Object.keys(p).length)return void o("Invalid topic routing JSON: "+g.message,!0)}c=r,d=s,l=p,await n("/api/telegram/config",{token:e,targetAgent:"crew-lead",allowedChatIds:a,contactNames:r,userRouting:s,topicRouting:p}),o("Telegram config saved"),x(),f()}async function h(){const e=document.getElementById("tgTokenInput").value.trim(),t={targetAgent:"crew-lead"};e&&(t.token=e);const a=await n("/api/telegram/start",t);a&&a.error?o(a.error,!0):(o(a&&"Already running"===a.message?"Already running":"Telegram bridge starting..."),setTimeout(u,2e3))}async function b(){await n("/api/telegram/stop",{}),o("Telegram bridge stopped"),setTimeout(u,1e3)}async function w(){try{const t=await e("/api/whatsapp/status"),n=document.getElementById("waStatusBadge");if(!n)return;t.running?(n.textContent=t.number?"● +"+t.number:"● running",n.className="status-badge status-active"):(n.textContent="● stopped",n.className="status-badge status-stopped");const o=document.getElementById("waAuthStatus");o&&(o.textContent=t.authSaved?"✅ Auth saved — no QR scan needed on restart":"⚠️ No auth saved — run npm run whatsapp in terminal to scan QR")}catch{}}function E(){var e;const t=document.getElementById("waContactNamesList");if(!t)return;const n=((null==(e=document.getElementById("waAllowedNumbers"))?void 0:e.value)||"").trim(),o=n?n.split(",").map(e=>e.trim()).filter(Boolean):[];if(t.innerHTML="",!o.length)return;const a=document.createElement("label");a.style.cssText="display:block;margin-bottom:6px;font-size:12px;color:var(--text-2);",a.textContent="Contact names (address book)",t.appendChild(a),o.forEach(e=>{const n=e.replace(/\D/g,""),o=document.createElement("div");o.style.cssText="margin-bottom:12px;padding:12px;background:var(--bg-1);border:1px solid var(--border);border-radius:6px;";const a=document.createElement("div");a.style.cssText="display:grid;grid-template-columns:140px 1fr;gap:8px;margin-bottom:8px;align-items:center;";const r=document.createElement("span");r.style.cssText="font-size:11px;color:var(--text-3);font-family:monospace;",r.textContent=e;const c=document.createElement("input");c.id="waContact-"+n,c.placeholder="Name (e.g. Jeff)",c.value=s[n]||s[e]||"",c.style.cssText="font-size:12px;padding:6px 8px;background:var(--bg-card);border:1px solid var(--border);border-radius:4px;color:var(--text-1);",a.appendChild(r),a.appendChild(c);const d=document.createElement("div");d.style.cssText="display:grid;grid-template-columns:140px 1fr;gap:8px;align-items:center;";const l=document.createElement("span");l.style.cssText="font-size:10px;color:var(--text-3);text-transform:uppercase;letter-spacing:0.05em;",l.textContent="Routes to →";const p=document.createElement("select");p.id="waRoute-"+n,p.style.cssText="font-size:12px;padding:6px 8px;background:var(--bg-card);border:1px solid var(--border);border-radius:4px;color:var(--text-1);";const m=i[e]||i["+"+n]||i[n]||"",g=document.createElement("option");g.value="",g.textContent="— default (see above) —",p.appendChild(g),["crew-lead","crew-main","crew-coder","crew-pm","crew-qa","crew-fixer","crew-security","crew-frontend","crew-coder-front","crew-coder-back","crew-github","crew-copywriter","crew-researcher","crew-architect","crew-seo","crew-ml","crew-mega","crew-loco"].forEach(e=>{const t=document.createElement("option");t.value=e,t.textContent=e,e===m&&(t.selected=!0),p.appendChild(t)}),p.addEventListener("change",t=>{const n=t.target.value;n?i[e]=n:delete i[e]}),d.appendChild(l),d.appendChild(p),o.appendChild(a),o.appendChild(d),t.appendChild(o)})}async function C(){try{const t=await e("/api/whatsapp/config"),n=document.getElementById("waAllowedNumbers"),o=document.getElementById("waTargetAgent");s=t.contactNames||{},i=t.userRouting||{},n&&(n.value=(t.allowedNumbers||[]).join(", ")),o&&(o.value=t.targetAgent||"crew-lead"),E()}catch{}}async function T(){const e=document.getElementById("waAllowedNumbers").value.trim(),t=e?e.split(",").map(e=>e.trim()).filter(Boolean):[],a=document.getElementById("waTargetAgent").value.trim()||"crew-lead",r={},c={};t.forEach(e=>{const t=e.replace(/\D/g,""),n=document.getElementById("waContact-"+t);n&&n.value.trim()&&(r[t]=n.value.trim());const o=document.getElementById("waRoute-"+t);o&&o.value&&(c[e]=o.value)}),s=r,i=c,await n("/api/whatsapp/config",{allowedNumbers:t,targetAgent:a,contactNames:r,userRouting:c}),o("WhatsApp config saved"),E()}async function I(){const e=await n("/api/whatsapp/start",{});e&&e.error?o(e.error,!0):(o(e&&"Already running"===e.message?"Already running":"WhatsApp bridge starting…"),setTimeout(w,2e3))}async function k(){await n("/api/whatsapp/stop",{}),o("WhatsApp bridge stopped"),setTimeout(w,1e3)}async function N(){const n=document.getElementById("waMessageFeed");if(n)try{const o=await e("/api/whatsapp/messages");if(!o.length)return void(n.innerHTML='<div class="meta" style="padding:20px;text-align:center;">No messages yet. Send a WhatsApp message to your linked number.</div>');n.innerHTML=o.slice(-50).reverse().map(e=>{const n="inbound"===e.direction,o=e.ts?new Date(e.ts).toLocaleTimeString():"",a=(e.jid||"").split("@")[0]||"";return'<div style="display:flex;gap:10px;padding:8px;background:var(--bg-2);border-radius:6px;align-items:flex-start;"><span style="font-size:18px;">'+(n?"📲":"🤖")+'</span><div style="flex:1;min-width:0;"><div style="font-size:11px;color:var(--text-3);margin-bottom:2px;">'+t(n?"+"+a:"crewswarm")+(o?" · "+o:"")+'</div><div style="font-size:13px;word-break:break-word;">'+t((e.text||"").slice(0,300))+"</div></div></div>"}).join("")}catch{n.innerHTML='<div style="color:var(--text-3);font-size:12px;padding:8px;">Could not load messages.</div>'}}async function S(){const n=document.getElementById("tgMessageFeed");if(n)try{const o=await e("/api/telegram/messages");if(!o.length)return void(n.innerHTML='<div class="meta" style="padding:20px;text-align:center;">No messages yet. Send something to your bot on Telegram.</div>');n.innerHTML=o.slice(-50).reverse().map(e=>{const n="inbound"===e.direction,o=e.ts?new Date(e.ts).toLocaleTimeString():"",a=n?e.firstName||e.username||"User":"crewswarm";return'<div class="card" style="padding:12px;gap:4px;display:flex;flex-direction:column;"><div style="display:flex;justify-content:space-between;font-size:11px;color:var(--text-3);"><span>'+(n?"👤":"⚡")+" "+t(a)+(e.username?" @"+t(e.username):"")+"</span><span>"+o+'</span></div><div style="font-size:13px;white-space:pre-wrap;">'+t(e.text||"")+"</div></div>"}).join("")}catch{n.innerHTML='<div class="meta" style="padding:20px;color:var(--red-hi);">Error loading messages</div>'}}async function z(){const t=document.getElementById("tgSessionsList");if(!t)return;const n=await e("/api/telegram-sessions").catch(()=>[]);if(t.innerHTML="",n.length)for(const e of n){const n=document.createElement("div");n.style.cssText="background:var(--bg-1);border:1px solid var(--border);border-radius:8px;padding:12px;margin-bottom:10px;";const o=e.lastTs?Math.round((Date.now()-e.lastTs)/6e4)+"m ago":"unknown",a=e.messages.slice(-6).map(e=>'<div style="margin-bottom:4px;"><span style="color:'+("user"===e.role?"var(--accent)":"var(--green)")+';">'+("user"===e.role?"👤":"🤖")+"</span> <span>"+String(e.content||"").slice(0,100).replace(/</g,"<")+"</span></div>").join("");n.innerHTML='<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:8px;"><span style="font-size:13px;font-weight:600;">chat '+e.chatId+'</span><span style="font-size:11px;color:var(--text-3);">'+e.messageCount+" msgs · "+o+'</span></div><div style="font-size:12px;color:var(--text-2);border-top:1px solid var(--border);padding-top:8px;max-height:120px;overflow-y:auto;">'+a+"</div>",t.appendChild(n)}else t.innerHTML='<div style="color:var(--text-3);font-size:12px;padding:8px;">No Telegram sessions yet — send a message to your bot to start one.</div>'}export{T as a,k as b,I as c,S as d,z as e,y as f,b as g,h,p as i,g as j,N as l,E as r,m as s};
|
|
1
|
+
import{g as e,e as t,p as n,s as o}from"./core-utils-CmOkXgzi.js";let a=()=>{},r=()=>{},s={},i={},c={},d={},l={};function p(e={}){a=e.showSettings||a,r=e.showSettingsTab||r}function m(){a(),r("comms")}async function g(){await Promise.allSettled([u(),z(),S(),v(),w(),C(),N()])}async function u(){try{const t=await e("/api/telegram/status"),n=document.getElementById("tgStatusBadge");if(!n)return;t.running?(n.textContent=t.botName?"● @"+t.botName:"● running",n.className="status-badge status-active"):(n.textContent="● stopped",n.className="status-badge status-stopped")}catch{}}function x(){var e;const t=document.getElementById("tgContactNamesList");if(!t)return;const n=((null==(e=document.getElementById("tgAllowedIds"))?void 0:e.value)||"").trim(),o=n?n.split(",").map(e=>parseInt(e.trim(),10)).filter(e=>!isNaN(e)):[];if(t.innerHTML="",!o.length)return;const a=document.createElement("label");a.style.cssText="display:block;margin-bottom:6px;font-size:12px;color:var(--text-2);",a.textContent="Contact names & routing",t.appendChild(a),o.forEach(e=>{const n=document.createElement("div");n.style.cssText="margin-bottom:12px;padding:12px;background:var(--bg-1);border:1px solid var(--border);border-radius:6px;";const o=document.createElement("div");o.style.cssText="display:grid;grid-template-columns:100px 1fr;gap:8px;margin-bottom:8px;align-items:center;";const a=document.createElement("span");a.style.cssText="font-size:11px;color:var(--text-3);font-family:monospace;",a.textContent=String(e);const r=document.createElement("input");r.id="tgContact-"+e,r.placeholder="Name (e.g. Jeff)",r.value=c[String(e)]||"",r.style.cssText="font-size:12px;padding:6px 8px;background:var(--bg-card);border:1px solid var(--border);border-radius:4px;color:var(--text-1);",o.appendChild(a),o.appendChild(r);const s=document.createElement("div");s.style.cssText="display:grid;grid-template-columns:100px 1fr;gap:8px;align-items:center;";const i=document.createElement("span");i.style.cssText="font-size:10px;color:var(--text-3);text-transform:uppercase;letter-spacing:0.05em;",i.textContent="Routes to →";const l=document.createElement("select");l.id="tgRoute-"+e,l.style.cssText="font-size:12px;padding:6px 8px;background:var(--bg-card);border:1px solid var(--border);border-radius:4px;color:var(--text-1);";const p=d[String(e)]||"",m=document.createElement("option");m.value="",m.textContent="— default (crew-lead) —",l.appendChild(m),["crew-lead","crew-main","crew-coder","crew-pm","crew-qa","crew-fixer","crew-security","crew-frontend","crew-coder-front","crew-coder-back","crew-github","crew-copywriter","crew-researcher","crew-architect","crew-seo","crew-ml","crew-mega","crew-loco"].forEach(e=>{const t=document.createElement("option");t.value=e,t.textContent=e,e===p&&(t.selected=!0),l.appendChild(t)}),l.addEventListener("change",t=>{const n=t.target.value;n?d[String(e)]=n:delete d[String(e)]}),s.appendChild(i),s.appendChild(l),n.appendChild(o),n.appendChild(s),t.appendChild(n)})}async function v(){try{const t=await e("/api/telegram/config");t.token&&(document.getElementById("tgTokenInput").value=t.token);const n=t.allowedChatIds&&t.allowedChatIds.length?t.allowedChatIds:[];document.getElementById("tgAllowedIds").value=n.join(", "),c=t.contactNames||{},d=t.userRouting||{},l=t.topicRouting||{},x(),function(){const t=document.getElementById("tgTopicRoutingContainer");if(!t)return;t.innerHTML="";const n=document.createElement("div");n.style.cssText="margin-bottom:12px;",n.innerHTML='\n <div style="font-size:13px;font-weight:600;margin-bottom:4px;">📌 Topic Routing (Optional)</div>\n <div style="font-size:11px;color:var(--text-3);line-height:1.4;">\n Route different topics in Forum groups to different agents.\n </div>\n ',t.appendChild(n);const a=document.createElement("div");a.style.cssText="display:flex;gap:8px;margin-bottom:12px;";const r=document.createElement("button");r.textContent="🔍 Auto-discover Topics",r.className="btn-ghost",r.style.cssText="flex:1;font-size:12px;",r.onclick=()=>async function(){try{const t=await e("/api/telegram/discover-topics");if(!t.length)return void o("No topics found in logs. Send messages to topics first.",!0);const n={};t.forEach(e=>{n[e.chatId]||(n[e.chatId]=[]),n[e.chatId].push(e)});Object.keys(l).filter(e=>!e.startsWith("_"));let a='<div style="max-height:400px;overflow-y:auto;">';Object.entries(n).forEach(([e,t])=>{a+=`\n <div style="background:var(--bg-1);border:1px solid var(--border);border-radius:6px;padding:12px;margin-bottom:12px;">\n <div style="font-weight:600;margin-bottom:8px;font-family:monospace;font-size:12px;">\n Group ${e}\n </div>\n <div style="margin-left:12px;">\n `,t.forEach(t=>{var n;const o=String(t.threadId),r=null==(n=l[e])?void 0:n[o];a+=`\n <div style="margin-bottom:6px;display:flex;align-items:center;gap:8px;">\n <input type="checkbox" class="discover-topic-check" data-chat-id="${e}" data-thread-id="${o}" ${r?"":"checked"} ${r?"disabled":""}>\n <span style="font-size:11px;color:var(--text-2);">Topic ${o}${r?' <span style="font-size:10px;color:var(--text-3);">(already configured)</span>':""}</span>\n </div>\n `}),a+="\n </div>\n </div>\n "}),a+="</div>";const r=document.createElement("div");r.style.cssText="position:fixed;top:0;left:0;right:0;bottom:0;background:rgba(0,0,0,0.6);display:flex;align-items:center;justify-content:center;z-index:9999;",r.innerHTML=`\n <div style="background:var(--bg-card);border:1px solid var(--border);border-radius:8px;padding:20px;max-width:600px;width:90%;">\n <h3 style="margin:0 0 16px;font-size:16px;">Discovered Topics</h3>\n <div style="font-size:11px;color:var(--text-3);margin-bottom:12px;">\n ✓ Select which topics to add. Already configured topics are disabled.\n </div>\n ${a}\n <div style="display:flex;gap:8px;margin-top:16px;justify-content:flex-end;">\n <button class="btn-ghost" id="discoverCancel">Cancel</button>\n <button class="btn-primary" id="discoverConfirm">Add Selected</button>\n </div>\n </div>\n `,document.body.appendChild(r),document.getElementById("discoverCancel").onclick=()=>{document.body.removeChild(r)},document.getElementById("discoverConfirm").onclick=()=>{const e=Array.from(r.querySelectorAll(".discover-topic-check:checked"));let t=0;e.forEach(e=>{const n=e.dataset.chatId,o=e.dataset.threadId;l[n]||(l[n]={}),l[n][o]||(l[n][o]="crew-lead",t++)}),document.body.removeChild(r),f(),o(`Added ${t} new topic${1!==t?"s":""}! Set agents and click Save.`)}}catch(t){o("Error discovering topics: "+t.message,!0)}}();const s=document.createElement("button");s.textContent="➕ Add New Group",s.className="btn-ghost",s.style.cssText="flex:1;font-size:12px;",s.onclick=()=>function(){const e=prompt("Enter Group ID (e.g., -100123456789):");if(!e||!e.trim())return;const t=e.trim();l[t]||(l[t]={});const n="1";l[t][n]="crew-lead",f(),o(`Added group ${t}`)}(),a.appendChild(r),a.appendChild(s),t.appendChild(a);const i=document.createElement("div");i.id="tgTopicsList",i.style.cssText="margin-bottom:12px;",t.appendChild(i),f();const c=document.createElement("details");c.style.cssText="margin-top:12px;",c.innerHTML='\n <summary style="cursor:pointer;font-size:11px;color:var(--text-3);padding:6px 0;">\n ⚙️ Advanced: Edit JSON directly\n </summary>\n ';const d=document.createElement("textarea");d.id="tgTopicRoutingJson",d.placeholder='{\n "-100123456789": {\n "5": "crew-coder",\n "8": "crew-copywriter"\n }\n}',d.value=Object.keys(l).length?JSON.stringify(l,null,2):"",d.style.cssText="width:100%;min-height:100px;font-family:monospace;font-size:11px;padding:8px;background:var(--bg-1);border:1px solid var(--border);border-radius:4px;color:var(--text-1);resize:vertical;margin-top:8px;",c.appendChild(d),t.appendChild(c)}()}catch{}}function f(){const e=document.getElementById("tgTopicsList");if(!e)return;e.innerHTML="";const t=["crew-lead","crew-main","crew-coder","crew-pm","crew-qa","crew-fixer","crew-security","crew-frontend","crew-coder-front","crew-coder-back","crew-github","crew-copywriter","crew-researcher","crew-architect","crew-seo","crew-ml","crew-mega","crew-loco"],n={};if(Object.entries(l).forEach(([e,t])=>{if(!e.startsWith("_"))if("object"==typeof t)Object.entries(t).forEach(([t,o])=>{n[e]||(n[e]=[]),n[e].push({topicId:t,agent:o})});else{const[o,a]=e.split(":");n[o]||(n[o]=[]),n[o].push({topicId:a,agent:t})}}),0===Object.keys(n).length){const t=document.createElement("div");return t.style.cssText="padding:12px;text-align:center;color:var(--text-3);font-size:11px;background:var(--bg-1);border:1px dashed var(--border);border-radius:4px;",t.textContent='No topics configured. Click "Auto-discover" or "Add Manually" above.',void e.appendChild(t)}let a=0;Object.entries(n).forEach(([n,r])=>{const s=document.createElement("div");s.className="tg-topic-group",s.style.cssText="margin-bottom:16px;padding:12px;background:var(--bg-1);border:1px solid var(--border);border-radius:6px;";const i=document.createElement("div");i.style.cssText="display:flex;align-items:center;gap:8px;margin-bottom:10px;padding-bottom:8px;border-bottom:1px solid var(--border);";const c=document.createElement("span");c.textContent="Group ID:",c.style.cssText="font-size:11px;color:var(--text-3);text-transform:uppercase;letter-spacing:0.05em;";const d=document.createElement("input");d.value=n,d.className="tg-topic-group-chatid",d.dataset.groupChatId=n,d.style.cssText="flex:1;font-size:11px;padding:5px 8px;background:var(--bg-card);border:1px solid var(--border);border-radius:3px;color:var(--text-1);font-family:monospace;";const p=document.createElement("button");p.textContent="➕",p.className="btn-ghost",p.title="Add topic to this group",p.style.cssText="font-size:14px;padding:4px 8px;width:32px;height:28px;",p.addEventListener("click",e=>{e.preventDefault(),function(e){l[e]||(l[e]={});const t=Object.keys(l[e]).map(e=>parseInt(e,10)).filter(e=>!isNaN(e)),n=t.length>0?String(Math.max(...t)+1):"1";l[e][n]="crew-lead",f(),o(`Added topic ${n} to group`)}(n)});const m=document.createElement("button");m.textContent="🗑",m.className="btn-ghost",m.title="Delete entire group and all topics",m.style.cssText="font-size:14px;padding:4px 8px;width:32px;height:28px;",m.addEventListener("click",e=>{e.preventDefault(),e.stopPropagation(),window.confirm(`Delete group ${n} and all topics?`)&&(delete l[String(n)],f(),o("Group removed - click Save to persist"))}),i.appendChild(c),i.appendChild(d),i.appendChild(p),i.appendChild(m),s.appendChild(i),r.forEach(e=>{const r=document.createElement("div");r.style.cssText="display:grid;grid-template-columns:80px 1fr 36px;gap:8px;align-items:center;padding:6px 8px;margin-bottom:4px;",r.dataset.idx=a,r.dataset.chatId=n,r.dataset.originalTopicId=e.topicId;const i=document.createElement("input");i.value=e.topicId,i.placeholder="Topic 5",i.className="tg-topic-id",i.style.cssText="font-size:11px;padding:5px 8px;background:var(--bg-card);border:1px solid var(--border);border-radius:3px;color:var(--text-1);font-family:monospace;text-align:center;";const c=document.createElement("select");c.className="tg-topic-agent",c.style.cssText="font-size:11px;padding:5px 8px;background:var(--bg-card);border:1px solid var(--border);border-radius:3px;color:var(--text-1);",t.forEach(t=>{const n=document.createElement("option");n.value=t,n.textContent=t,t===e.agent&&(n.selected=!0),c.appendChild(n)}),c.addEventListener("change",t=>{const o=t.target.value,a=r.querySelector(".tg-topic-id"),s=a?a.value.trim():e.topicId;if(s&&l[n]){l[n][s]=o;const e=document.getElementById("tgTopicRoutingJson");e&&(e.value=JSON.stringify(l,null,2))}});const d=document.createElement("button");d.textContent="🗑",d.className="btn-ghost",d.title="Remove this topic",d.style.cssText="font-size:14px;padding:4px;width:28px;height:28px;",d.addEventListener("click",t=>{t.preventDefault(),t.stopPropagation(),window.confirm(`Remove topic ${e.topicId}?`)&&l[n]&&l[n][e.topicId]&&(delete l[n][e.topicId],0===Object.keys(l[n]).length&&delete l[n],f(),o(`Topic ${e.topicId} removed - click Save to persist`))}),r.appendChild(i),r.appendChild(c),r.appendChild(d),s.appendChild(r),a++}),e.appendChild(s)});const r=document.getElementById("tgTopicRoutingJson");r&&(r.value=Object.keys(l).length?JSON.stringify(l,null,2):"")}async function y(){const e=document.getElementById("tgTokenInput").value.trim(),t=document.getElementById("tgAllowedIds").value.trim(),a=t?t.split(",").map(e=>parseInt(e.trim(),10)).filter(e=>!isNaN(e)):[];if(!e)return void o("Enter a bot token first",!0);const r={},s={};a.forEach(e=>{const t=document.getElementById("tgContact-"+e);t&&t.value.trim()&&(r[String(e)]=t.value.trim());const n=document.getElementById("tgRoute-"+e);n&&n.value&&(s[String(e)]=n.value)});const i=document.getElementById("tgTopicsList");if(i){i.querySelectorAll(".tg-topic-group").forEach(e=>{const t=e.querySelector("[data-group-chat-id]");if(!t)return;const n=t.dataset.groupChatId,o=t.value.trim();n&&o&&n!==o&&l[n]&&(l[o]=l[n],delete l[n]);const a=o||n;if(!a)return;l[a]||(l[a]={});e.querySelectorAll("[data-chat-id]").forEach(e=>{const t=e.querySelector(".tg-topic-id"),n=e.querySelector(".tg-topic-agent");if(t&&n){const o=e.dataset.originalTopicId||t.value.trim(),r=t.value.trim(),s=n.value;if(!r)return;o&&r&&o!==r?(l[a][o]&&delete l[a][o],l[a][r]=s):l[a][r]=s}})})}const p={...l},m=document.getElementById("tgTopicRoutingJson");if(m&&m.value.trim())try{const e=JSON.parse(m.value.trim());Object.assign(p,e)}catch(g){if(0===Object.keys(p).length)return void o("Invalid topic routing JSON: "+g.message,!0)}c=r,d=s,l=p,await n("/api/telegram/config",{token:e,targetAgent:"crew-lead",allowedChatIds:a,contactNames:r,userRouting:s,topicRouting:p}),o("Telegram config saved"),x(),f()}async function h(){const e=document.getElementById("tgTokenInput").value.trim(),t={targetAgent:"crew-lead"};e&&(t.token=e);const a=await n("/api/telegram/start",t);a&&a.error?o(a.error,!0):(o(a&&"Already running"===a.message?"Already running":"Telegram bridge starting..."),setTimeout(u,2e3))}async function b(){await n("/api/telegram/stop",{}),o("Telegram bridge stopped"),setTimeout(u,1e3)}async function w(){try{const t=await e("/api/whatsapp/status"),n=document.getElementById("waStatusBadge");if(!n)return;t.running?(n.textContent=t.number?"● +"+t.number:"● running",n.className="status-badge status-active"):(n.textContent="● stopped",n.className="status-badge status-stopped");const o=document.getElementById("waAuthStatus");o&&(o.textContent=t.authSaved?"✅ Auth saved — no QR scan needed on restart":"⚠️ No auth saved — run npm run whatsapp in terminal to scan QR")}catch{}}function E(){var e;const t=document.getElementById("waContactNamesList");if(!t)return;const n=((null==(e=document.getElementById("waAllowedNumbers"))?void 0:e.value)||"").trim(),o=n?n.split(",").map(e=>e.trim()).filter(Boolean):[];if(t.innerHTML="",!o.length)return;const a=document.createElement("label");a.style.cssText="display:block;margin-bottom:6px;font-size:12px;color:var(--text-2);",a.textContent="Contact names (address book)",t.appendChild(a),o.forEach(e=>{const n=e.replace(/\D/g,""),o=document.createElement("div");o.style.cssText="margin-bottom:12px;padding:12px;background:var(--bg-1);border:1px solid var(--border);border-radius:6px;";const a=document.createElement("div");a.style.cssText="display:grid;grid-template-columns:140px 1fr;gap:8px;margin-bottom:8px;align-items:center;";const r=document.createElement("span");r.style.cssText="font-size:11px;color:var(--text-3);font-family:monospace;",r.textContent=e;const c=document.createElement("input");c.id="waContact-"+n,c.placeholder="Name (e.g. Jeff)",c.value=s[n]||s[e]||"",c.style.cssText="font-size:12px;padding:6px 8px;background:var(--bg-card);border:1px solid var(--border);border-radius:4px;color:var(--text-1);",a.appendChild(r),a.appendChild(c);const d=document.createElement("div");d.style.cssText="display:grid;grid-template-columns:140px 1fr;gap:8px;align-items:center;";const l=document.createElement("span");l.style.cssText="font-size:10px;color:var(--text-3);text-transform:uppercase;letter-spacing:0.05em;",l.textContent="Routes to →";const p=document.createElement("select");p.id="waRoute-"+n,p.style.cssText="font-size:12px;padding:6px 8px;background:var(--bg-card);border:1px solid var(--border);border-radius:4px;color:var(--text-1);";const m=i[e]||i["+"+n]||i[n]||"",g=document.createElement("option");g.value="",g.textContent="— default (see above) —",p.appendChild(g),["crew-lead","crew-main","crew-coder","crew-pm","crew-qa","crew-fixer","crew-security","crew-frontend","crew-coder-front","crew-coder-back","crew-github","crew-copywriter","crew-researcher","crew-architect","crew-seo","crew-ml","crew-mega","crew-loco"].forEach(e=>{const t=document.createElement("option");t.value=e,t.textContent=e,e===m&&(t.selected=!0),p.appendChild(t)}),p.addEventListener("change",t=>{const n=t.target.value;n?i[e]=n:delete i[e]}),d.appendChild(l),d.appendChild(p),o.appendChild(a),o.appendChild(d),t.appendChild(o)})}async function C(){try{const t=await e("/api/whatsapp/config"),n=document.getElementById("waAllowedNumbers"),o=document.getElementById("waTargetAgent");s=t.contactNames||{},i=t.userRouting||{},n&&(n.value=(t.allowedNumbers||[]).join(", ")),o&&(o.value=t.targetAgent||"crew-lead"),E()}catch{}}async function T(){const e=document.getElementById("waAllowedNumbers").value.trim(),t=e?e.split(",").map(e=>e.trim()).filter(Boolean):[],a=document.getElementById("waTargetAgent").value.trim()||"crew-lead",r={},c={};t.forEach(e=>{const t=e.replace(/\D/g,""),n=document.getElementById("waContact-"+t);n&&n.value.trim()&&(r[t]=n.value.trim());const o=document.getElementById("waRoute-"+t);o&&o.value&&(c[e]=o.value)}),s=r,i=c,await n("/api/whatsapp/config",{allowedNumbers:t,targetAgent:a,contactNames:r,userRouting:c}),o("WhatsApp config saved"),E()}async function I(){const e=await n("/api/whatsapp/start",{});e&&e.error?o(e.error,!0):(o(e&&"Already running"===e.message?"Already running":"WhatsApp bridge starting…"),setTimeout(w,2e3))}async function k(){await n("/api/whatsapp/stop",{}),o("WhatsApp bridge stopped"),setTimeout(w,1e3)}async function N(){const n=document.getElementById("waMessageFeed");if(n)try{const o=await e("/api/whatsapp/messages");if(!o.length)return void(n.innerHTML='<div class="meta" style="padding:20px;text-align:center;">No messages yet. Send a WhatsApp message to your linked number.</div>');n.innerHTML=o.slice(-50).reverse().map(e=>{const n="inbound"===e.direction,o=e.ts?new Date(e.ts).toLocaleTimeString():"",a=(e.jid||"").split("@")[0]||"";return'<div style="display:flex;gap:10px;padding:8px;background:var(--bg-2);border-radius:6px;align-items:flex-start;"><span style="font-size:18px;">'+(n?"📲":"🤖")+'</span><div style="flex:1;min-width:0;"><div style="font-size:11px;color:var(--text-3);margin-bottom:2px;">'+t(n?"+"+a:"crewswarm")+(o?" · "+o:"")+'</div><div style="font-size:13px;word-break:break-word;">'+t((e.text||"").slice(0,300))+"</div></div></div>"}).join("")}catch{n.innerHTML='<div style="color:var(--text-3);font-size:12px;padding:8px;">Could not load messages.</div>'}}async function S(){const n=document.getElementById("tgMessageFeed");if(n)try{const o=await e("/api/telegram/messages");if(!o.length)return void(n.innerHTML='<div class="meta" style="padding:20px;text-align:center;">No messages yet. Send something to your bot on Telegram.</div>');n.innerHTML=o.slice(-50).reverse().map(e=>{const n="inbound"===e.direction,o=e.ts?new Date(e.ts).toLocaleTimeString():"",a=n?e.firstName||e.username||"User":"crewswarm";return'<div class="card" style="padding:12px;gap:4px;display:flex;flex-direction:column;"><div style="display:flex;justify-content:space-between;font-size:11px;color:var(--text-3);"><span>'+(n?"👤":"⚡")+" "+t(a)+(e.username?" @"+t(e.username):"")+"</span><span>"+o+'</span></div><div style="font-size:13px;white-space:pre-wrap;">'+t(e.text||"")+"</div></div>"}).join("")}catch{n.innerHTML='<div class="meta" style="padding:20px;color:var(--red-hi);">Error loading messages</div>'}}async function z(){const t=document.getElementById("tgSessionsList");if(!t)return;const n=await e("/api/telegram-sessions").catch(()=>[]);if(t.innerHTML="",n.length)for(const e of n){const n=document.createElement("div");n.style.cssText="background:var(--bg-1);border:1px solid var(--border);border-radius:8px;padding:12px;margin-bottom:10px;";const o=e.lastTs?Math.round((Date.now()-e.lastTs)/6e4)+"m ago":"unknown",a=e.messages.slice(-6).map(e=>'<div style="margin-bottom:4px;"><span style="color:'+("user"===e.role?"var(--accent)":"var(--green)")+';">'+("user"===e.role?"👤":"🤖")+"</span> <span>"+String(e.content||"").slice(0,100).replace(/</g,"<")+"</span></div>").join("");n.innerHTML='<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:8px;"><span style="font-size:13px;font-weight:600;">chat '+e.chatId+'</span><span style="font-size:11px;color:var(--text-3);">'+e.messageCount+" msgs · "+o+'</span></div><div style="font-size:12px;color:var(--text-2);border-top:1px solid var(--border);padding-top:8px;max-height:120px;overflow-y:auto;">'+a+"</div>",t.appendChild(n)}else t.innerHTML='<div style="color:var(--text-3);font-size:12px;padding:8px;">No Telegram sessions yet — send a message to your bot to start one.</div>'}export{T as a,k as b,I as c,S as d,z as e,y as f,b as g,h,p as i,g as j,N as l,E as r,m as s};
|
|
Binary file
|
package/apps/dashboard/dist/assets/{tab-contacts-tab-5LHSthJM.js → tab-contacts-tab-DiOyMYth.js}
RENAMED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{g as t,e,s as n,p as o}from"./core-utils-CAVnDoe1.js";let a={},l=[],i={search:"",platform:"",sortBy:"name",letter:"all"};function d(){document.querySelectorAll(".view").forEach(t=>t.classList.remove("active")),document.getElementById("contactsView").classList.add("active"),document.querySelectorAll(".nav-item").forEach(t=>t.classList.remove("active"));const t=document.getElementById("navContacts");t&&t.classList.add("active"),r()}async function r(){const n=document.getElementById("contactsList");n.innerHTML='<div class="meta" style="padding:20px;">Loading contacts...</div>';try{const e=(await t("/api/contacts")).contacts||[];if(a={},e.forEach(t=>{a[t.contact_id]=t}),l=e,!e.length)return n.innerHTML='<div class="meta" style="padding:20px;">No contacts yet. Click "+ New Contact" to add manually, or contacts are created automatically when someone messages the bot.</div>',void(document.getElementById("contactsCount").innerHTML="");c()}catch(o){n.innerHTML='<div class="meta" style="padding:20px;color:var(--red-hi);">Failed to load contacts: '+e(o.message)+"</div>"}}function s(){const t=document.getElementById("contactsSearch"),e=document.getElementById("contactsPlatformFilter"),n=document.getElementById("contactsSortBy");i.search=t.value.toLowerCase().trim(),i.platform=e.value,i.sortBy=n.value,c()}function p(t){i.letter=t,document.querySelectorAll(".alpha-filter").forEach(t=>{t.classList.remove("active"),t.style.background="var(--bg-1)",t.style.color="var(--text-2)"});const e=document.querySelector(`[data-letter="${t}"]`);e&&(e.classList.add("active"),e.style.background="var(--purple)",e.style.color="#fff"),c()}function c(){let t=[...l];i.search&&(t=t.filter(t=>`${t.display_name} ${t.phone_number||""} ${t.email||""} ${t.notes||""} ${JSON.stringify(t.preferences)} ${JSON.stringify(t.tags)}`.toLowerCase().includes(i.search))),i.platform&&(t=t.filter(t=>{if(t.platform===i.platform)return!0;return!!(t.platform_links||{})[i.platform]})),"all"!==i.letter&&(t=t.filter(t=>{const e=(t.display_name||"").charAt(0).toUpperCase();return"#"===i.letter?!/[A-Z]/.test(e):e===i.letter})),"name"===i.sortBy?t.sort((t,e)=>{const n=(t.display_name||t.phone_number||"").toLowerCase(),o=(e.display_name||e.phone_number||"").toLowerCase();return n.localeCompare(o)}):"recent"===i.sortBy?t.sort((t,e)=>e.last_seen-t.last_seen):"messages"===i.sortBy&&t.sort((t,e)=>e.message_count-t.message_count);const n=document.getElementById("contactsCount");if(n){const e=l.length,o=t.length;n.innerHTML=o===e?`Showing all ${e} contacts`:`Showing ${o} of ${e} contacts`}!function(t){const n=document.getElementById("contactsList");if(!t.length)return void(n.innerHTML='<div class="meta" style="padding:20px;">No contacts match your filters.</div>');n.innerHTML=t.map(t=>{var n;const o=t.contact_id,a=e(o),l=e(t.display_name||t.phone_number||"Unknown"),i=t.platform_links||{},d=t.preferences||{},r=t.tags||[],s=[];("whatsapp"===t.platform||i.whatsapp)&&s.push('<span style="background:#25D366;color:#fff;padding:2px 8px;border-radius:12px;font-size:10px;font-weight:600;">WhatsApp</span>'),("telegram"===t.platform||i.telegram)&&s.push('<span style="background:#0088cc;color:#fff;padding:2px 8px;border-radius:12px;font-size:10px;font-weight:600;">Telegram</span>'),("twitter"===t.platform||i.twitter)&&s.push('<span style="background:#1DA1F2;color:#fff;padding:2px 8px;border-radius:12px;font-size:10px;font-weight:600;">Twitter</span>'),("instagram"===t.platform||i.instagram)&&s.push('<span style="background:#E4405F;color:#fff;padding:2px 8px;border-radius:12px;font-size:10px;font-weight:600;">Instagram</span>'),("tiktok"===t.platform||i.tiktok)&&s.push('<span style="background:#000000;color:#fff;padding:2px 8px;border-radius:12px;font-size:10px;font-weight:600;">TikTok</span>'),("slack"===t.platform||i.slack)&&s.push('<span style="background:#4A154B;color:#fff;padding:2px 8px;border-radius:12px;font-size:10px;font-weight:600;">Slack</span>'),("web"===t.platform||i.web)&&s.push('<span style="background:#666;color:#fff;padding:2px 8px;border-radius:12px;font-size:10px;font-weight:600;">Web</span>'),("website"===t.platform||i.website)&&s.push('<span style="background:#666;color:#fff;padding:2px 8px;border-radius:12px;font-size:10px;font-weight:600;">Website</span>');const p=function(t){const e=Date.now()-t,n=Math.floor(e/6e4),o=Math.floor(e/36e5),a=Math.floor(e/864e5);return n<1?"just now":n<60?`${n}m ago`:o<24?`${o}h ago`:a<7?`${a}d ago`:new Date(t).toLocaleDateString()}(t.last_seen),c=[];(null==d?void 0:d.diet)&&c.push(`🍴 ${e(d.diet)}`),(null==(n=null==d?void 0:d.allergies)?void 0:n.length)&&c.push(`⚠️ ${e(d.allergies.join(", "))}`),(null==d?void 0:d.spiceLevel)&&c.push(`🌶️ ${e(d.spiceLevel)}`);const m=r.length?r.map(t=>`<span style="background:var(--bg-1);color:var(--text-3);padding:2px 6px;border-radius:4px;font-size:10px;">${e(t)}</span>`).join(" "):"",u=t.phone_number?`<div><strong>Phone:</strong> ${e(t.phone_number)}</div>`:"",v=t.email?`<div><strong>Email:</strong> ${e(t.email)}</div>`:"",g=t.notes?`<div style="margin-top:8px;"><strong>Notes:</strong> ${e(t.notes)}</div>`:"",x=d&&Object.keys(d).length>0?`<div style="margin-top:8px;"><strong>Preferences:</strong> <pre style="font-size:11px;margin-top:4px;background:var(--bg-1);padding:8px;border-radius:4px;overflow:auto;">${e(JSON.stringify(d,null,2))}</pre></div>`:"";return`\n <div class="card contact-card" id="contact-card-${a}" data-contact-id="${a}">\n <div id="contact-view-${a}">\n <div style="display:flex;justify-content:space-between;align-items:flex-start;margin-bottom:8px;">\n <div style="flex:1;">\n <div style="display:flex;align-items:center;gap:8px;flex-wrap:wrap;margin-bottom:6px;">\n <strong style="font-size:15px;">${l}</strong>\n ${s.join(" ")}\n </div>\n <div class="meta" style="font-size:12px;">\n ${t.message_count} messages · Last seen: ${p}\n </div>\n </div>\n </div>\n \n ${c.length?`<div style="margin-bottom:10px;font-size:12px;color:var(--text-2);">${c.join(" · ")}</div>`:""}\n ${m?`<div style="margin-bottom:10px;display:flex;gap:4px;flex-wrap:wrap;">${m}</div>`:""}\n \n <div style="display:flex;gap:8px;flex-wrap:wrap;align-items:center;">\n <button data-action="send-message" data-id="${a}" class="btn-green" style="font-size:12px;">💬 Send Message</button>\n <button data-action="toggle-details" data-id="${a}" class="btn-ghost" style="font-size:12px;">📋 Details</button>\n <button data-action="edit" data-id="${a}" class="btn-ghost" style="font-size:12px;">✏️ Edit</button>\n <button data-action="delete" data-id="${a}" style="background:transparent;color:var(--text-3);border:1px solid var(--border);border-radius:6px;padding:4px 10px;cursor:pointer;font-size:12px;">🗑 Delete</button>\n </div>\n </div>\n \n \x3c!-- Expandable details --\x3e\n <div id="contact-details-${a}" style="display:none;margin-top:14px;padding-top:14px;border-top:1px solid var(--border);font-size:13px;color:var(--text-2);">\n ${u}\n ${v}\n ${g}\n ${x}\n </div>\n \n \x3c!-- Edit form --\x3e\n <div id="contact-edit-${a}" style="display:none;padding:12px;border-top:1px solid var(--border);margin-top:12px;">\n <div style="margin-bottom:8px;"><label style="font-size:11px;color:var(--text-3);">Display Name</label><input id="contact-name-${a}" type="text" value="${e(t.display_name||"")}" style="margin-top:4px;width:100%;" /></div>\n <div style="margin-bottom:8px;"><label style="font-size:11px;color:var(--text-3);">Phone</label><input id="contact-phone-${a}" type="text" value="${e(t.phone_number||"")}" style="margin-top:4px;width:100%;" /></div>\n <div style="margin-bottom:8px;"><label style="font-size:11px;color:var(--text-3);">Email</label><input id="contact-email-${a}" type="text" value="${e(t.email||"")}" style="margin-top:4px;width:100%;" /></div>\n <div style="margin-bottom:8px;"><label style="font-size:11px;color:var(--text-3);">📍 Location</label><input id="contact-location-${a}" type="text" value="${e(t.last_location||"")}" placeholder="Grand Bend, Ontario, Canada" style="margin-top:4px;width:100%;" /></div>\n <div style="margin-bottom:12px;padding:12px;background:var(--bg-1);border-radius:6px;">\n <label style="font-size:12px;color:var(--text-2);display:block;margin-bottom:8px;font-weight:600;">🔗 Platform IDs</label>\n <div style="font-size:10px;color:var(--text-3);margin-bottom:12px;">Primary: ${e(t.contact_id)}</div>\n <div style="display:grid;grid-template-columns:1fr 1fr;gap:8px;">\n <div><label style="font-size:10px;color:var(--text-3);display:block;margin-bottom:2px;">Telegram</label><input id="platform-telegram-${a}" type="text" placeholder="12345678" value="${e(i.telegram||"")}" style="padding:6px 8px;border-radius:4px;border:1px solid var(--border);background:var(--bg-card);font-size:12px;width:100%;" /></div>\n <div><label style="font-size:10px;color:var(--text-3);display:block;margin-bottom:2px;">WhatsApp</label><input id="platform-whatsapp-${a}" type="text" placeholder="1234@s.whatsapp.net" value="${e(i.whatsapp||"")}" style="padding:6px 8px;border-radius:4px;border:1px solid var(--border);background:var(--bg-card);font-size:12px;width:100%;" /></div>\n <div><label style="font-size:10px;color:var(--text-3);display:block;margin-bottom:2px;">Twitter</label><input id="platform-twitter-${a}" type="text" placeholder="@username" value="${e(i.twitter||"")}" style="padding:6px 8px;border-radius:4px;border:1px solid var(--border);background:var(--bg-card);font-size:12px;width:100%;" /></div>\n <div><label style="font-size:10px;color:var(--text-3);display:block;margin-bottom:2px;">Instagram</label><input id="platform-instagram-${a}" type="text" placeholder="@username" value="${e(i.instagram||"")}" style="padding:6px 8px;border-radius:4px;border:1px solid var(--border);background:var(--bg-card);font-size:12px;width:100%;" /></div>\n <div><label style="font-size:10px;color:var(--text-3);display:block;margin-bottom:2px;">TikTok</label><input id="platform-tiktok-${a}" type="text" placeholder="@username" value="${e(i.tiktok||"")}" style="padding:6px 8px;border-radius:4px;border:1px solid var(--border);background:var(--bg-card);font-size:12px;width:100%;" /></div>\n <div><label style="font-size:10px;color:var(--text-3);display:block;margin-bottom:2px;">Slack</label><input id="platform-slack-${a}" type="text" placeholder="U01234ABC" value="${e(i.slack||"")}" style="padding:6px 8px;border-radius:4px;border:1px solid var(--border);background:var(--bg-card);font-size:12px;width:100%;" /></div>\n <div><label style="font-size:10px;color:var(--text-3);display:block;margin-bottom:2px;">Website</label><input id="platform-website-${a}" type="text" placeholder="https://example.com" value="${e(i.website||"")}" style="padding:6px 8px;border-radius:4px;border:1px solid var(--border);background:var(--bg-card);font-size:12px;width:100%;" /></div>\n <div><label style="font-size:10px;color:var(--text-3);display:block;margin-bottom:2px;">Other</label><input id="platform-other-${a}" type="text" placeholder="Custom ID" value="${e(i.other||"")}" style="padding:6px 8px;border-radius:4px;border:1px solid var(--border);background:var(--bg-card);font-size:12px;width:100%;" /></div>\n </div>\n </div>\n \n \x3c!-- Preferences Section --\x3e\n <div style="margin-bottom:12px;padding:12px;background:var(--bg-1);border-radius:6px;">\n <label style="font-size:12px;color:var(--text-2);display:block;margin-bottom:8px;font-weight:600;">🍴 Preferences</label>\n <div style="display:grid;grid-template-columns:1fr 1fr;gap:8px;">\n <div>\n <label style="font-size:10px;color:var(--text-3);display:block;margin-bottom:2px;">Diet</label>\n <select id="pref-diet-${a}" style="padding:6px 8px;border-radius:4px;border:1px solid var(--border);background:var(--bg-card);font-size:12px;width:100%;">\n <option value="">None</option>\n <option value="omnivore" ${"omnivore"===(null==d?void 0:d.diet)?"selected":""}>Omnivore</option>\n <option value="vegetarian" ${"vegetarian"===(null==d?void 0:d.diet)?"selected":""}>Vegetarian</option>\n <option value="vegan" ${"vegan"===(null==d?void 0:d.diet)?"selected":""}>Vegan</option>\n <option value="pescatarian" ${"pescatarian"===(null==d?void 0:d.diet)?"selected":""}>Pescatarian</option>\n </select>\n </div>\n <div>\n <label style="font-size:10px;color:var(--text-3);display:block;margin-bottom:2px;">Spice Level</label>\n <select id="pref-spice-${a}" style="padding:6px 8px;border-radius:4px;border:1px solid var(--border);background:var(--bg-card);font-size:12px;width:100%;">\n <option value="">Any</option>\n <option value="mild" ${"mild"===(null==d?void 0:d.spiceLevel)?"selected":""}>Mild</option>\n <option value="medium" ${"medium"===(null==d?void 0:d.spiceLevel)?"selected":""}>Medium</option>\n <option value="hot" ${"hot"===(null==d?void 0:d.spiceLevel)?"selected":""}>Hot</option>\n </select>\n </div>\n <div>\n <label style="font-size:10px;color:var(--text-3);display:block;margin-bottom:2px;">Budget</label>\n <select id="pref-budget-${a}" style="padding:6px 8px;border-radius:4px;border:1px solid var(--border);background:var(--bg-card);font-size:12px;width:100%;">\n <option value="">Any</option>\n <option value="budget" ${"budget"===(null==d?void 0:d.budget)?"selected":""}>Budget ($)</option>\n <option value="moderate" ${"moderate"===(null==d?void 0:d.budget)?"selected":""}>Moderate ($$)</option>\n <option value="upscale" ${"upscale"===(null==d?void 0:d.budget)?"selected":""}>Upscale ($$$)</option>\n </select>\n </div>\n <div>\n <label style="font-size:10px;color:var(--text-3);display:block;margin-bottom:2px;">Atmosphere</label>\n <select id="pref-atmosphere-${a}" style="padding:6px 8px;border-radius:4px;border:1px solid var(--border);background:var(--bg-card);font-size:12px;width:100%;">\n <option value="">Any</option>\n <option value="quiet" ${"quiet"===(null==d?void 0:d.atmosphere)?"selected":""}>Quiet</option>\n <option value="lively" ${"lively"===(null==d?void 0:d.atmosphere)?"selected":""}>Lively</option>\n <option value="romantic" ${"romantic"===(null==d?void 0:d.atmosphere)?"selected":""}>Romantic</option>\n <option value="family-friendly" ${"family-friendly"===(null==d?void 0:d.atmosphere)?"selected":""}>Family-friendly</option>\n </select>\n </div>\n <div style="grid-column:1/-1;">\n <label style="font-size:10px;color:var(--text-3);display:block;margin-bottom:2px;">Allergies (comma-separated)</label>\n <input id="pref-allergies-${a}" type="text" placeholder="shellfish, peanuts, gluten, dairy" value="${e(((null==d?void 0:d.allergies)||[]).join(", "))}" style="padding:6px 8px;border-radius:4px;border:1px solid var(--border);background:var(--bg-card);font-size:12px;width:100%;" />\n </div>\n <div style="grid-column:1/-1;">\n <label style="font-size:10px;color:var(--text-3);display:block;margin-bottom:2px;">Favorite Cuisines (comma-separated)</label>\n <input id="pref-cuisines-${a}" type="text" placeholder="Thai, Mexican, Italian, Japanese" value="${e(((null==d?void 0:d.favCuisines)||[]).join(", "))}" style="padding:6px 8px;border-radius:4px;border:1px solid var(--border);background:var(--bg-card);font-size:12px;width:100%;" />\n </div>\n </div>\n </div>\n \n <div style="margin-bottom:8px;"><label style="font-size:11px;color:var(--text-3);">Notes</label><textarea id="contact-notes-${a}" rows="3" style="margin-top:4px;width:100%;">${e(t.notes||"")}</textarea></div>\n <div style="margin-bottom:8px;"><label style="font-size:11px;color:var(--text-3);">Tags (comma-separated)</label><input id="contact-tags-${a}" type="text" value="${e(r.join(", "))}" style="margin-top:4px;width:100%;" /></div>\n <div style="display:flex;gap:8px;">\n <button data-action="save-edit" data-id="${a}" class="btn-green" style="font-size:12px;">💾 Save</button>\n <button data-action="cancel-edit" data-id="${a}" class="btn-ghost" style="font-size:12px;">Cancel</button>\n </div>\n </div>\n </div>\n `}).join("")}(t)}function m(t){const e=document.getElementById("contact-view-"+t),n=document.getElementById("contact-edit-"+t),o=document.getElementById("contact-details-"+t);if(!e||!n)return;const a="none"!==n.style.display;e.style.display=a?"":"none",n.style.display=a?"none":"block",o&&(o.style.display="none")}function u(){const t=document.getElementById("modalOverlay")||v();t.innerHTML='\n <div style="background:var(--bg-card);border-radius:12px;padding:24px;max-width:500px;width:90%;box-shadow:0 8px 32px rgba(0,0,0,0.4);">\n <h3 style="margin:0 0 16px 0;">➕ New Contact</h3>\n \n <div style="margin-bottom:12px;">\n <label style="font-size:12px;color:var(--text-3);display:block;margin-bottom:4px;">Platform *</label>\n <select id="newContactPlatform" style="width:100%;padding:8px;border-radius:6px;border:1px solid var(--border);background:var(--bg-1);font-size:13px;">\n <option value="telegram">Telegram</option>\n <option value="whatsapp">WhatsApp</option>\n <option value="twitter">Twitter</option>\n <option value="instagram">Instagram</option>\n <option value="tiktok">TikTok</option>\n <option value="slack">Slack</option>\n <option value="website">Website</option>\n <option value="web">Web</option>\n </select>\n </div>\n \n <div style="margin-bottom:12px;">\n <label style="font-size:12px;color:var(--text-3);display:block;margin-bottom:4px;">Platform ID *</label>\n <input id="newContactId" type="text" placeholder="12345678 (Telegram), @username (Twitter/IG/TikTok), URL (Website)" style="width:100%;padding:8px;border-radius:6px;border:1px solid var(--border);background:var(--bg-1);font-size:13px;" />\n <div style="font-size:10px;color:var(--text-3);margin-top:4px;">\n • Telegram: chat ID (e.g., 12345678)<br>\n • WhatsApp: phone@s.whatsapp.net (e.g., 15551234567@s.whatsapp.net)<br>\n • Twitter/Instagram/TikTok: @username<br>\n • Website: URL (e.g., https://example.com)<br>\n • Slack: user ID (e.g., U01234ABC)\n </div>\n </div>\n \n <div style="margin-bottom:12px;">\n <label style="font-size:12px;color:var(--text-3);display:block;margin-bottom:4px;">Display Name *</label>\n <input id="newContactName" type="text" placeholder="John Doe" style="width:100%;padding:8px;border-radius:6px;border:1px solid var(--border);background:var(--bg-1);font-size:13px;" />\n </div>\n \n <div style="margin-bottom:12px;">\n <label style="font-size:12px;color:var(--text-3);display:block;margin-bottom:4px;">Phone</label>\n <input id="newContactPhone" type="text" placeholder="+1 310 905 0857" style="width:100%;padding:8px;border-radius:6px;border:1px solid var(--border);background:var(--bg-1);font-size:13px;" />\n </div>\n \n <div style="margin-bottom:12px;">\n <label style="font-size:12px;color:var(--text-3);display:block;margin-bottom:4px;">Email</label>\n <input id="newContactEmail" type="text" placeholder="john@example.com" style="width:100%;padding:8px;border-radius:6px;border:1px solid var(--border);background:var(--bg-1);font-size:13px;" />\n </div>\n \n <div style="margin-bottom:16px;">\n <label style="font-size:12px;color:var(--text-3);display:block;margin-bottom:4px;">Notes</label>\n <textarea id="newContactNotes" rows="3" placeholder="Important client, prefers morning calls..." style="width:100%;padding:8px;border-radius:6px;border:1px solid var(--border);background:var(--bg-1);font-size:13px;"></textarea>\n </div>\n \n <div style="display:flex;gap:8px;justify-content:flex-end;">\n <button onclick="document.getElementById(\'modalOverlay\').style.display=\'none\'" class="btn-ghost">Cancel</button>\n <button onclick="window.createContact()" class="btn-green">Create Contact</button>\n </div>\n </div>\n ',t.style.display="flex"}function v(){let t=document.getElementById("modalOverlay");return t||(t=document.createElement("div"),t.id="modalOverlay",t.style.cssText="display:none;position:fixed;top:0;left:0;right:0;bottom:0;background:rgba(0,0,0,0.7);z-index:9999;align-items:center;justify-content:center;",t.onclick=e=>{e.target===t&&(t.style.display="none")},document.body.appendChild(t)),t}function g(){document.addEventListener("click",t=>{const i=t.target.getAttribute("data-action"),d=t.target.getAttribute("data-id"),g=t.target.getAttribute("data-letter");"toggle-details"===i?function(t){const e=document.getElementById("contact-details-"+t);if(!e)return;const n="none"!==e.style.display;e.style.display=n?"none":"block"}(d):"edit"===i?m(d):"save-edit"===i?async function(e){var a,l,i,d,s,p,c,u,v,g,x,b,y,f,h,w,k,$,z,B,E,I,C,L,_,T,M,S,A,P,D,N,j,O,F,W,H,q,U,J;const R=null==(l=null==(a=document.getElementById("contact-name-"+e))?void 0:a.value)?void 0:l.trim(),V=null==(d=null==(i=document.getElementById("contact-phone-"+e))?void 0:i.value)?void 0:d.trim(),G=null==(p=null==(s=document.getElementById("contact-email-"+e))?void 0:s.value)?void 0:p.trim(),Q=null==(u=null==(c=document.getElementById("contact-location-"+e))?void 0:c.value)?void 0:u.trim(),Y=null==(g=null==(v=document.getElementById("contact-notes-"+e))?void 0:v.value)?void 0:g.trim(),Z=null==(b=null==(x=document.getElementById("contact-tags-"+e))?void 0:x.value)?void 0:b.trim(),K=Z?Z.split(",").map(t=>t.trim()).filter(Boolean):[],X={},tt=null==(f=null==(y=document.getElementById("platform-telegram-"+e))?void 0:y.value)?void 0:f.trim(),et=null==(w=null==(h=document.getElementById("platform-whatsapp-"+e))?void 0:h.value)?void 0:w.trim(),nt=null==($=null==(k=document.getElementById("platform-twitter-"+e))?void 0:k.value)?void 0:$.trim(),ot=null==(B=null==(z=document.getElementById("platform-instagram-"+e))?void 0:z.value)?void 0:B.trim(),at=null==(I=null==(E=document.getElementById("platform-tiktok-"+e))?void 0:E.value)?void 0:I.trim(),lt=null==(L=null==(C=document.getElementById("platform-slack-"+e))?void 0:C.value)?void 0:L.trim(),it=null==(T=null==(_=document.getElementById("platform-website-"+e))?void 0:_.value)?void 0:T.trim(),dt=null==(S=null==(M=document.getElementById("platform-other-"+e))?void 0:M.value)?void 0:S.trim();tt&&(X.telegram=tt),et&&(X.whatsapp=et),nt&&(X.twitter=nt),ot&&(X.instagram=ot),at&&(X.tiktok=at),lt&&(X.slack=lt),it&&(X.website=it),dt&&(X.other=dt);const rt={},st=null==(P=null==(A=document.getElementById("pref-diet-"+e))?void 0:A.value)?void 0:P.trim(),pt=null==(N=null==(D=document.getElementById("pref-spice-"+e))?void 0:D.value)?void 0:N.trim(),ct=null==(O=null==(j=document.getElementById("pref-budget-"+e))?void 0:j.value)?void 0:O.trim(),mt=null==(W=null==(F=document.getElementById("pref-atmosphere-"+e))?void 0:F.value)?void 0:W.trim(),ut=null==(q=null==(H=document.getElementById("pref-allergies-"+e))?void 0:H.value)?void 0:q.trim(),vt=null==(J=null==(U=document.getElementById("pref-cuisines-"+e))?void 0:U.value)?void 0:J.trim();st&&(rt.diet=st),pt&&(rt.spiceLevel=pt),ct&&(rt.budget=ct),mt&&(rt.atmosphere=mt),ut&&(rt.allergies=ut.split(",").map(t=>t.trim()).filter(Boolean)),vt&&(rt.favCuisines=vt.split(",").map(t=>t.trim()).filter(Boolean));try{await o("/api/contacts/update",{contactId:e,display_name:R,phone_number:V,email:G,last_location:Q,notes:Y,tags:K,platform_links:X,preferences:rt}),n("Contact saved"),m(e),r()}catch(t){n("Failed: "+t.message,!0)}}(d):"cancel-edit"===i?m(d):"delete"===i?async function(e){const i=a[e],d=i?i.display_name:e;if(confirm(`Delete contact "${d}"?\n\nMessage history will also be deleted.`))try{await o("/api/contacts/delete",{contactId:e}),n("Contact deleted"),delete a[e],l=l.filter(t=>t.contact_id!==e),c()}catch(t){n("Failed: "+t.message,!0)}}(d):"send-message"===i?function(t){const o=a[t];if(!o)return void n("Contact not found",!0);const l=document.getElementById("modalOverlay")||v(),i=o.platform_links||{},d="whatsapp"===o.platform||i.whatsapp,r="telegram"===o.platform||i.telegram;l.innerHTML=`\n <div style="background:var(--bg-card);border-radius:12px;padding:24px;max-width:500px;width:90%;box-shadow:0 8px 32px rgba(0,0,0,0.4);">\n <h3 style="margin:0 0 16px 0;">💬 Send Message</h3>\n <div style="margin-bottom:12px;">\n <div style="font-size:13px;color:var(--text-2);margin-bottom:8px;">To: <strong>${e(o.display_name||o.contact_id)}</strong></div>\n </div>\n <div style="margin-bottom:12px;">\n <label style="font-size:12px;color:var(--text-3);display:block;margin-bottom:4px;">Platform</label>\n <select id="sendMessagePlatform" style="width:100%;padding:8px;border-radius:6px;border:1px solid var(--border);background:var(--bg-1);font-size:13px;">\n ${d?'<option value="whatsapp">WhatsApp</option>':""}\n ${r?'<option value="telegram">Telegram</option>':""}\n ${d&&r?'<option value="both">Both</option>':""}\n </select>\n </div>\n <div style="margin-bottom:16px;">\n <label style="font-size:12px;color:var(--text-3);display:block;margin-bottom:4px;">Message</label>\n <textarea id="sendMessageText" rows="4" placeholder="Your message here..." style="width:100%;padding:8px;border-radius:6px;border:1px solid var(--border);background:var(--bg-1);font-size:13px;"></textarea>\n </div>\n <div style="display:flex;gap:8px;justify-content:flex-end;">\n <button onclick="document.getElementById('modalOverlay').style.display='none'" class="btn-ghost">Cancel</button>\n <button onclick="window.sendContactMessage('${e(t)}')" class="btn-green">Send</button>\n </div>\n </div>\n `,l.style.display="flex"}(d):"newContact"===i?u():"applyContactFilters"===i?s():"filterByLetter"===i?p(g):"loadContacts"===i&&r()});const t=document.getElementById("contactsSearch");t&&t.addEventListener("input",()=>{i.search=t.value.toLowerCase().trim(),c()});const d=document.getElementById("contactsPlatformFilter");d&&d.addEventListener("change",s);const g=document.getElementById("contactsSortBy");g&&g.addEventListener("change",s)}"undefined"!=typeof window&&(window.loadContacts=r,window.searchContacts=s,window.newContact=u,window.createContact=async function(){const t=document.getElementById("newContactPlatform").value,e=document.getElementById("newContactId").value.trim(),a=document.getElementById("newContactName").value.trim(),l=document.getElementById("newContactPhone").value.trim(),i=document.getElementById("newContactEmail").value.trim(),d=document.getElementById("newContactNotes").value.trim();if(!t||!e||!a)return void n("Platform, Platform ID, and Display Name are required",!0);const s=`${t}:${e}`;try{await o("/api/contacts/create",{contact_id:s,platform:t,display_name:a,phone_number:l||null,email:i||null,notes:d||null}),n("✅ Contact created"),document.getElementById("modalOverlay").style.display="none",r()}catch(p){n("Failed: "+p.message,!0)}},window.sendContactMessage=async function(t){const e=document.getElementById("sendMessagePlatform").value,a=document.getElementById("sendMessageText").value.trim();if(a)try{await o("/api/contacts/send",{contactId:t,platform:e,message:a}),n("✅ Message sent"),document.getElementById("modalOverlay").style.display="none"}catch(l){n("Failed: "+l.message,!0)}else n("Message cannot be empty",!0)},window.filterByLetter=p);export{s as a,g as i,r as l,d as s};
|
|
1
|
+
import{g as t,e,s as n,p as o}from"./core-utils-CmOkXgzi.js";let a={},l=[],i={search:"",platform:"",sortBy:"name",letter:"all"};function d(){document.querySelectorAll(".view").forEach(t=>t.classList.remove("active")),document.getElementById("contactsView").classList.add("active"),document.querySelectorAll(".nav-item").forEach(t=>t.classList.remove("active"));const t=document.getElementById("navContacts");t&&t.classList.add("active"),r()}async function r(){const n=document.getElementById("contactsList");n.innerHTML='<div class="meta" style="padding:20px;">Loading contacts...</div>';try{const e=(await t("/api/contacts")).contacts||[];if(a={},e.forEach(t=>{a[t.contact_id]=t}),l=e,!e.length)return n.innerHTML='<div class="meta" style="padding:20px;">No contacts yet. Click "+ New Contact" to add manually, or contacts are created automatically when someone messages the bot.</div>',void(document.getElementById("contactsCount").innerHTML="");c()}catch(o){n.innerHTML='<div class="meta" style="padding:20px;color:var(--red-hi);">Failed to load contacts: '+e(o.message)+"</div>"}}function s(){const t=document.getElementById("contactsSearch"),e=document.getElementById("contactsPlatformFilter"),n=document.getElementById("contactsSortBy");i.search=t.value.toLowerCase().trim(),i.platform=e.value,i.sortBy=n.value,c()}function p(t){i.letter=t,document.querySelectorAll(".alpha-filter").forEach(t=>{t.classList.remove("active"),t.style.background="var(--bg-1)",t.style.color="var(--text-2)"});const e=document.querySelector(`[data-letter="${t}"]`);e&&(e.classList.add("active"),e.style.background="var(--purple)",e.style.color="#fff"),c()}function c(){let t=[...l];i.search&&(t=t.filter(t=>`${t.display_name} ${t.phone_number||""} ${t.email||""} ${t.notes||""} ${JSON.stringify(t.preferences)} ${JSON.stringify(t.tags)}`.toLowerCase().includes(i.search))),i.platform&&(t=t.filter(t=>{if(t.platform===i.platform)return!0;return!!(t.platform_links||{})[i.platform]})),"all"!==i.letter&&(t=t.filter(t=>{const e=(t.display_name||"").charAt(0).toUpperCase();return"#"===i.letter?!/[A-Z]/.test(e):e===i.letter})),"name"===i.sortBy?t.sort((t,e)=>{const n=(t.display_name||t.phone_number||"").toLowerCase(),o=(e.display_name||e.phone_number||"").toLowerCase();return n.localeCompare(o)}):"recent"===i.sortBy?t.sort((t,e)=>e.last_seen-t.last_seen):"messages"===i.sortBy&&t.sort((t,e)=>e.message_count-t.message_count);const n=document.getElementById("contactsCount");if(n){const e=l.length,o=t.length;n.innerHTML=o===e?`Showing all ${e} contacts`:`Showing ${o} of ${e} contacts`}!function(t){const n=document.getElementById("contactsList");if(!t.length)return void(n.innerHTML='<div class="meta" style="padding:20px;">No contacts match your filters.</div>');n.innerHTML=t.map(t=>{var n;const o=t.contact_id,a=e(o),l=e(t.display_name||t.phone_number||"Unknown"),i=t.platform_links||{},d=t.preferences||{},r=t.tags||[],s=[];("whatsapp"===t.platform||i.whatsapp)&&s.push('<span style="background:#25D366;color:#fff;padding:2px 8px;border-radius:12px;font-size:10px;font-weight:600;">WhatsApp</span>'),("telegram"===t.platform||i.telegram)&&s.push('<span style="background:#0088cc;color:#fff;padding:2px 8px;border-radius:12px;font-size:10px;font-weight:600;">Telegram</span>'),("twitter"===t.platform||i.twitter)&&s.push('<span style="background:#1DA1F2;color:#fff;padding:2px 8px;border-radius:12px;font-size:10px;font-weight:600;">Twitter</span>'),("instagram"===t.platform||i.instagram)&&s.push('<span style="background:#E4405F;color:#fff;padding:2px 8px;border-radius:12px;font-size:10px;font-weight:600;">Instagram</span>'),("tiktok"===t.platform||i.tiktok)&&s.push('<span style="background:#000000;color:#fff;padding:2px 8px;border-radius:12px;font-size:10px;font-weight:600;">TikTok</span>'),("slack"===t.platform||i.slack)&&s.push('<span style="background:#4A154B;color:#fff;padding:2px 8px;border-radius:12px;font-size:10px;font-weight:600;">Slack</span>'),("web"===t.platform||i.web)&&s.push('<span style="background:#666;color:#fff;padding:2px 8px;border-radius:12px;font-size:10px;font-weight:600;">Web</span>'),("website"===t.platform||i.website)&&s.push('<span style="background:#666;color:#fff;padding:2px 8px;border-radius:12px;font-size:10px;font-weight:600;">Website</span>');const p=function(t){const e=Date.now()-t,n=Math.floor(e/6e4),o=Math.floor(e/36e5),a=Math.floor(e/864e5);return n<1?"just now":n<60?`${n}m ago`:o<24?`${o}h ago`:a<7?`${a}d ago`:new Date(t).toLocaleDateString()}(t.last_seen),c=[];(null==d?void 0:d.diet)&&c.push(`🍴 ${e(d.diet)}`),(null==(n=null==d?void 0:d.allergies)?void 0:n.length)&&c.push(`⚠️ ${e(d.allergies.join(", "))}`),(null==d?void 0:d.spiceLevel)&&c.push(`🌶️ ${e(d.spiceLevel)}`);const m=r.length?r.map(t=>`<span style="background:var(--bg-1);color:var(--text-3);padding:2px 6px;border-radius:4px;font-size:10px;">${e(t)}</span>`).join(" "):"",u=t.phone_number?`<div><strong>Phone:</strong> ${e(t.phone_number)}</div>`:"",v=t.email?`<div><strong>Email:</strong> ${e(t.email)}</div>`:"",g=t.notes?`<div style="margin-top:8px;"><strong>Notes:</strong> ${e(t.notes)}</div>`:"",x=d&&Object.keys(d).length>0?`<div style="margin-top:8px;"><strong>Preferences:</strong> <pre style="font-size:11px;margin-top:4px;background:var(--bg-1);padding:8px;border-radius:4px;overflow:auto;">${e(JSON.stringify(d,null,2))}</pre></div>`:"";return`\n <div class="card contact-card" id="contact-card-${a}" data-contact-id="${a}">\n <div id="contact-view-${a}">\n <div style="display:flex;justify-content:space-between;align-items:flex-start;margin-bottom:8px;">\n <div style="flex:1;">\n <div style="display:flex;align-items:center;gap:8px;flex-wrap:wrap;margin-bottom:6px;">\n <strong style="font-size:15px;">${l}</strong>\n ${s.join(" ")}\n </div>\n <div class="meta" style="font-size:12px;">\n ${t.message_count} messages · Last seen: ${p}\n </div>\n </div>\n </div>\n \n ${c.length?`<div style="margin-bottom:10px;font-size:12px;color:var(--text-2);">${c.join(" · ")}</div>`:""}\n ${m?`<div style="margin-bottom:10px;display:flex;gap:4px;flex-wrap:wrap;">${m}</div>`:""}\n \n <div style="display:flex;gap:8px;flex-wrap:wrap;align-items:center;">\n <button data-action="send-message" data-id="${a}" class="btn-green" style="font-size:12px;">💬 Send Message</button>\n <button data-action="toggle-details" data-id="${a}" class="btn-ghost" style="font-size:12px;">📋 Details</button>\n <button data-action="edit" data-id="${a}" class="btn-ghost" style="font-size:12px;">✏️ Edit</button>\n <button data-action="delete" data-id="${a}" style="background:transparent;color:var(--text-3);border:1px solid var(--border);border-radius:6px;padding:4px 10px;cursor:pointer;font-size:12px;">🗑 Delete</button>\n </div>\n </div>\n \n \x3c!-- Expandable details --\x3e\n <div id="contact-details-${a}" style="display:none;margin-top:14px;padding-top:14px;border-top:1px solid var(--border);font-size:13px;color:var(--text-2);">\n ${u}\n ${v}\n ${g}\n ${x}\n </div>\n \n \x3c!-- Edit form --\x3e\n <div id="contact-edit-${a}" style="display:none;padding:12px;border-top:1px solid var(--border);margin-top:12px;">\n <div style="margin-bottom:8px;"><label style="font-size:11px;color:var(--text-3);">Display Name</label><input id="contact-name-${a}" type="text" value="${e(t.display_name||"")}" style="margin-top:4px;width:100%;" /></div>\n <div style="margin-bottom:8px;"><label style="font-size:11px;color:var(--text-3);">Phone</label><input id="contact-phone-${a}" type="text" value="${e(t.phone_number||"")}" style="margin-top:4px;width:100%;" /></div>\n <div style="margin-bottom:8px;"><label style="font-size:11px;color:var(--text-3);">Email</label><input id="contact-email-${a}" type="text" value="${e(t.email||"")}" style="margin-top:4px;width:100%;" /></div>\n <div style="margin-bottom:8px;"><label style="font-size:11px;color:var(--text-3);">📍 Location</label><input id="contact-location-${a}" type="text" value="${e(t.last_location||"")}" placeholder="Grand Bend, Ontario, Canada" style="margin-top:4px;width:100%;" /></div>\n <div style="margin-bottom:12px;padding:12px;background:var(--bg-1);border-radius:6px;">\n <label style="font-size:12px;color:var(--text-2);display:block;margin-bottom:8px;font-weight:600;">🔗 Platform IDs</label>\n <div style="font-size:10px;color:var(--text-3);margin-bottom:12px;">Primary: ${e(t.contact_id)}</div>\n <div style="display:grid;grid-template-columns:1fr 1fr;gap:8px;">\n <div><label style="font-size:10px;color:var(--text-3);display:block;margin-bottom:2px;">Telegram</label><input id="platform-telegram-${a}" type="text" placeholder="12345678" value="${e(i.telegram||"")}" style="padding:6px 8px;border-radius:4px;border:1px solid var(--border);background:var(--bg-card);font-size:12px;width:100%;" /></div>\n <div><label style="font-size:10px;color:var(--text-3);display:block;margin-bottom:2px;">WhatsApp</label><input id="platform-whatsapp-${a}" type="text" placeholder="1234@s.whatsapp.net" value="${e(i.whatsapp||"")}" style="padding:6px 8px;border-radius:4px;border:1px solid var(--border);background:var(--bg-card);font-size:12px;width:100%;" /></div>\n <div><label style="font-size:10px;color:var(--text-3);display:block;margin-bottom:2px;">Twitter</label><input id="platform-twitter-${a}" type="text" placeholder="@username" value="${e(i.twitter||"")}" style="padding:6px 8px;border-radius:4px;border:1px solid var(--border);background:var(--bg-card);font-size:12px;width:100%;" /></div>\n <div><label style="font-size:10px;color:var(--text-3);display:block;margin-bottom:2px;">Instagram</label><input id="platform-instagram-${a}" type="text" placeholder="@username" value="${e(i.instagram||"")}" style="padding:6px 8px;border-radius:4px;border:1px solid var(--border);background:var(--bg-card);font-size:12px;width:100%;" /></div>\n <div><label style="font-size:10px;color:var(--text-3);display:block;margin-bottom:2px;">TikTok</label><input id="platform-tiktok-${a}" type="text" placeholder="@username" value="${e(i.tiktok||"")}" style="padding:6px 8px;border-radius:4px;border:1px solid var(--border);background:var(--bg-card);font-size:12px;width:100%;" /></div>\n <div><label style="font-size:10px;color:var(--text-3);display:block;margin-bottom:2px;">Slack</label><input id="platform-slack-${a}" type="text" placeholder="U01234ABC" value="${e(i.slack||"")}" style="padding:6px 8px;border-radius:4px;border:1px solid var(--border);background:var(--bg-card);font-size:12px;width:100%;" /></div>\n <div><label style="font-size:10px;color:var(--text-3);display:block;margin-bottom:2px;">Website</label><input id="platform-website-${a}" type="text" placeholder="https://example.com" value="${e(i.website||"")}" style="padding:6px 8px;border-radius:4px;border:1px solid var(--border);background:var(--bg-card);font-size:12px;width:100%;" /></div>\n <div><label style="font-size:10px;color:var(--text-3);display:block;margin-bottom:2px;">Other</label><input id="platform-other-${a}" type="text" placeholder="Custom ID" value="${e(i.other||"")}" style="padding:6px 8px;border-radius:4px;border:1px solid var(--border);background:var(--bg-card);font-size:12px;width:100%;" /></div>\n </div>\n </div>\n \n \x3c!-- Preferences Section --\x3e\n <div style="margin-bottom:12px;padding:12px;background:var(--bg-1);border-radius:6px;">\n <label style="font-size:12px;color:var(--text-2);display:block;margin-bottom:8px;font-weight:600;">🍴 Preferences</label>\n <div style="display:grid;grid-template-columns:1fr 1fr;gap:8px;">\n <div>\n <label style="font-size:10px;color:var(--text-3);display:block;margin-bottom:2px;">Diet</label>\n <select id="pref-diet-${a}" style="padding:6px 8px;border-radius:4px;border:1px solid var(--border);background:var(--bg-card);font-size:12px;width:100%;">\n <option value="">None</option>\n <option value="omnivore" ${"omnivore"===(null==d?void 0:d.diet)?"selected":""}>Omnivore</option>\n <option value="vegetarian" ${"vegetarian"===(null==d?void 0:d.diet)?"selected":""}>Vegetarian</option>\n <option value="vegan" ${"vegan"===(null==d?void 0:d.diet)?"selected":""}>Vegan</option>\n <option value="pescatarian" ${"pescatarian"===(null==d?void 0:d.diet)?"selected":""}>Pescatarian</option>\n </select>\n </div>\n <div>\n <label style="font-size:10px;color:var(--text-3);display:block;margin-bottom:2px;">Spice Level</label>\n <select id="pref-spice-${a}" style="padding:6px 8px;border-radius:4px;border:1px solid var(--border);background:var(--bg-card);font-size:12px;width:100%;">\n <option value="">Any</option>\n <option value="mild" ${"mild"===(null==d?void 0:d.spiceLevel)?"selected":""}>Mild</option>\n <option value="medium" ${"medium"===(null==d?void 0:d.spiceLevel)?"selected":""}>Medium</option>\n <option value="hot" ${"hot"===(null==d?void 0:d.spiceLevel)?"selected":""}>Hot</option>\n </select>\n </div>\n <div>\n <label style="font-size:10px;color:var(--text-3);display:block;margin-bottom:2px;">Budget</label>\n <select id="pref-budget-${a}" style="padding:6px 8px;border-radius:4px;border:1px solid var(--border);background:var(--bg-card);font-size:12px;width:100%;">\n <option value="">Any</option>\n <option value="budget" ${"budget"===(null==d?void 0:d.budget)?"selected":""}>Budget ($)</option>\n <option value="moderate" ${"moderate"===(null==d?void 0:d.budget)?"selected":""}>Moderate ($$)</option>\n <option value="upscale" ${"upscale"===(null==d?void 0:d.budget)?"selected":""}>Upscale ($$$)</option>\n </select>\n </div>\n <div>\n <label style="font-size:10px;color:var(--text-3);display:block;margin-bottom:2px;">Atmosphere</label>\n <select id="pref-atmosphere-${a}" style="padding:6px 8px;border-radius:4px;border:1px solid var(--border);background:var(--bg-card);font-size:12px;width:100%;">\n <option value="">Any</option>\n <option value="quiet" ${"quiet"===(null==d?void 0:d.atmosphere)?"selected":""}>Quiet</option>\n <option value="lively" ${"lively"===(null==d?void 0:d.atmosphere)?"selected":""}>Lively</option>\n <option value="romantic" ${"romantic"===(null==d?void 0:d.atmosphere)?"selected":""}>Romantic</option>\n <option value="family-friendly" ${"family-friendly"===(null==d?void 0:d.atmosphere)?"selected":""}>Family-friendly</option>\n </select>\n </div>\n <div style="grid-column:1/-1;">\n <label style="font-size:10px;color:var(--text-3);display:block;margin-bottom:2px;">Allergies (comma-separated)</label>\n <input id="pref-allergies-${a}" type="text" placeholder="shellfish, peanuts, gluten, dairy" value="${e(((null==d?void 0:d.allergies)||[]).join(", "))}" style="padding:6px 8px;border-radius:4px;border:1px solid var(--border);background:var(--bg-card);font-size:12px;width:100%;" />\n </div>\n <div style="grid-column:1/-1;">\n <label style="font-size:10px;color:var(--text-3);display:block;margin-bottom:2px;">Favorite Cuisines (comma-separated)</label>\n <input id="pref-cuisines-${a}" type="text" placeholder="Thai, Mexican, Italian, Japanese" value="${e(((null==d?void 0:d.favCuisines)||[]).join(", "))}" style="padding:6px 8px;border-radius:4px;border:1px solid var(--border);background:var(--bg-card);font-size:12px;width:100%;" />\n </div>\n </div>\n </div>\n \n <div style="margin-bottom:8px;"><label style="font-size:11px;color:var(--text-3);">Notes</label><textarea id="contact-notes-${a}" rows="3" style="margin-top:4px;width:100%;">${e(t.notes||"")}</textarea></div>\n <div style="margin-bottom:8px;"><label style="font-size:11px;color:var(--text-3);">Tags (comma-separated)</label><input id="contact-tags-${a}" type="text" value="${e(r.join(", "))}" style="margin-top:4px;width:100%;" /></div>\n <div style="display:flex;gap:8px;">\n <button data-action="save-edit" data-id="${a}" class="btn-green" style="font-size:12px;">💾 Save</button>\n <button data-action="cancel-edit" data-id="${a}" class="btn-ghost" style="font-size:12px;">Cancel</button>\n </div>\n </div>\n </div>\n `}).join("")}(t)}function m(t){const e=document.getElementById("contact-view-"+t),n=document.getElementById("contact-edit-"+t),o=document.getElementById("contact-details-"+t);if(!e||!n)return;const a="none"!==n.style.display;e.style.display=a?"":"none",n.style.display=a?"none":"block",o&&(o.style.display="none")}function u(){const t=document.getElementById("modalOverlay")||v();t.innerHTML='\n <div style="background:var(--bg-card);border-radius:12px;padding:24px;max-width:500px;width:90%;box-shadow:0 8px 32px rgba(0,0,0,0.4);">\n <h3 style="margin:0 0 16px 0;">➕ New Contact</h3>\n \n <div style="margin-bottom:12px;">\n <label style="font-size:12px;color:var(--text-3);display:block;margin-bottom:4px;">Platform *</label>\n <select id="newContactPlatform" style="width:100%;padding:8px;border-radius:6px;border:1px solid var(--border);background:var(--bg-1);font-size:13px;">\n <option value="telegram">Telegram</option>\n <option value="whatsapp">WhatsApp</option>\n <option value="twitter">Twitter</option>\n <option value="instagram">Instagram</option>\n <option value="tiktok">TikTok</option>\n <option value="slack">Slack</option>\n <option value="website">Website</option>\n <option value="web">Web</option>\n </select>\n </div>\n \n <div style="margin-bottom:12px;">\n <label style="font-size:12px;color:var(--text-3);display:block;margin-bottom:4px;">Platform ID *</label>\n <input id="newContactId" type="text" placeholder="12345678 (Telegram), @username (Twitter/IG/TikTok), URL (Website)" style="width:100%;padding:8px;border-radius:6px;border:1px solid var(--border);background:var(--bg-1);font-size:13px;" />\n <div style="font-size:10px;color:var(--text-3);margin-top:4px;">\n • Telegram: chat ID (e.g., 12345678)<br>\n • WhatsApp: phone@s.whatsapp.net (e.g., 15551234567@s.whatsapp.net)<br>\n • Twitter/Instagram/TikTok: @username<br>\n • Website: URL (e.g., https://example.com)<br>\n • Slack: user ID (e.g., U01234ABC)\n </div>\n </div>\n \n <div style="margin-bottom:12px;">\n <label style="font-size:12px;color:var(--text-3);display:block;margin-bottom:4px;">Display Name *</label>\n <input id="newContactName" type="text" placeholder="John Doe" style="width:100%;padding:8px;border-radius:6px;border:1px solid var(--border);background:var(--bg-1);font-size:13px;" />\n </div>\n \n <div style="margin-bottom:12px;">\n <label style="font-size:12px;color:var(--text-3);display:block;margin-bottom:4px;">Phone</label>\n <input id="newContactPhone" type="text" placeholder="+1 310 905 0857" style="width:100%;padding:8px;border-radius:6px;border:1px solid var(--border);background:var(--bg-1);font-size:13px;" />\n </div>\n \n <div style="margin-bottom:12px;">\n <label style="font-size:12px;color:var(--text-3);display:block;margin-bottom:4px;">Email</label>\n <input id="newContactEmail" type="text" placeholder="john@example.com" style="width:100%;padding:8px;border-radius:6px;border:1px solid var(--border);background:var(--bg-1);font-size:13px;" />\n </div>\n \n <div style="margin-bottom:16px;">\n <label style="font-size:12px;color:var(--text-3);display:block;margin-bottom:4px;">Notes</label>\n <textarea id="newContactNotes" rows="3" placeholder="Important client, prefers morning calls..." style="width:100%;padding:8px;border-radius:6px;border:1px solid var(--border);background:var(--bg-1);font-size:13px;"></textarea>\n </div>\n \n <div style="display:flex;gap:8px;justify-content:flex-end;">\n <button onclick="document.getElementById(\'modalOverlay\').style.display=\'none\'" class="btn-ghost">Cancel</button>\n <button onclick="window.createContact()" class="btn-green">Create Contact</button>\n </div>\n </div>\n ',t.style.display="flex"}function v(){let t=document.getElementById("modalOverlay");return t||(t=document.createElement("div"),t.id="modalOverlay",t.style.cssText="display:none;position:fixed;top:0;left:0;right:0;bottom:0;background:rgba(0,0,0,0.7);z-index:9999;align-items:center;justify-content:center;",t.onclick=e=>{e.target===t&&(t.style.display="none")},document.body.appendChild(t)),t}function g(){document.addEventListener("click",t=>{const i=t.target.getAttribute("data-action"),d=t.target.getAttribute("data-id"),g=t.target.getAttribute("data-letter");"toggle-details"===i?function(t){const e=document.getElementById("contact-details-"+t);if(!e)return;const n="none"!==e.style.display;e.style.display=n?"none":"block"}(d):"edit"===i?m(d):"save-edit"===i?async function(e){var a,l,i,d,s,p,c,u,v,g,x,b,y,f,h,w,k,$,z,B,E,I,C,L,_,T,M,S,A,P,D,N,j,O,F,W,H,q,U,J;const R=null==(l=null==(a=document.getElementById("contact-name-"+e))?void 0:a.value)?void 0:l.trim(),V=null==(d=null==(i=document.getElementById("contact-phone-"+e))?void 0:i.value)?void 0:d.trim(),G=null==(p=null==(s=document.getElementById("contact-email-"+e))?void 0:s.value)?void 0:p.trim(),Q=null==(u=null==(c=document.getElementById("contact-location-"+e))?void 0:c.value)?void 0:u.trim(),Y=null==(g=null==(v=document.getElementById("contact-notes-"+e))?void 0:v.value)?void 0:g.trim(),Z=null==(b=null==(x=document.getElementById("contact-tags-"+e))?void 0:x.value)?void 0:b.trim(),K=Z?Z.split(",").map(t=>t.trim()).filter(Boolean):[],X={},tt=null==(f=null==(y=document.getElementById("platform-telegram-"+e))?void 0:y.value)?void 0:f.trim(),et=null==(w=null==(h=document.getElementById("platform-whatsapp-"+e))?void 0:h.value)?void 0:w.trim(),nt=null==($=null==(k=document.getElementById("platform-twitter-"+e))?void 0:k.value)?void 0:$.trim(),ot=null==(B=null==(z=document.getElementById("platform-instagram-"+e))?void 0:z.value)?void 0:B.trim(),at=null==(I=null==(E=document.getElementById("platform-tiktok-"+e))?void 0:E.value)?void 0:I.trim(),lt=null==(L=null==(C=document.getElementById("platform-slack-"+e))?void 0:C.value)?void 0:L.trim(),it=null==(T=null==(_=document.getElementById("platform-website-"+e))?void 0:_.value)?void 0:T.trim(),dt=null==(S=null==(M=document.getElementById("platform-other-"+e))?void 0:M.value)?void 0:S.trim();tt&&(X.telegram=tt),et&&(X.whatsapp=et),nt&&(X.twitter=nt),ot&&(X.instagram=ot),at&&(X.tiktok=at),lt&&(X.slack=lt),it&&(X.website=it),dt&&(X.other=dt);const rt={},st=null==(P=null==(A=document.getElementById("pref-diet-"+e))?void 0:A.value)?void 0:P.trim(),pt=null==(N=null==(D=document.getElementById("pref-spice-"+e))?void 0:D.value)?void 0:N.trim(),ct=null==(O=null==(j=document.getElementById("pref-budget-"+e))?void 0:j.value)?void 0:O.trim(),mt=null==(W=null==(F=document.getElementById("pref-atmosphere-"+e))?void 0:F.value)?void 0:W.trim(),ut=null==(q=null==(H=document.getElementById("pref-allergies-"+e))?void 0:H.value)?void 0:q.trim(),vt=null==(J=null==(U=document.getElementById("pref-cuisines-"+e))?void 0:U.value)?void 0:J.trim();st&&(rt.diet=st),pt&&(rt.spiceLevel=pt),ct&&(rt.budget=ct),mt&&(rt.atmosphere=mt),ut&&(rt.allergies=ut.split(",").map(t=>t.trim()).filter(Boolean)),vt&&(rt.favCuisines=vt.split(",").map(t=>t.trim()).filter(Boolean));try{await o("/api/contacts/update",{contactId:e,display_name:R,phone_number:V,email:G,last_location:Q,notes:Y,tags:K,platform_links:X,preferences:rt}),n("Contact saved"),m(e),r()}catch(t){n("Failed: "+t.message,!0)}}(d):"cancel-edit"===i?m(d):"delete"===i?async function(e){const i=a[e],d=i?i.display_name:e;if(confirm(`Delete contact "${d}"?\n\nMessage history will also be deleted.`))try{await o("/api/contacts/delete",{contactId:e}),n("Contact deleted"),delete a[e],l=l.filter(t=>t.contact_id!==e),c()}catch(t){n("Failed: "+t.message,!0)}}(d):"send-message"===i?function(t){const o=a[t];if(!o)return void n("Contact not found",!0);const l=document.getElementById("modalOverlay")||v(),i=o.platform_links||{},d="whatsapp"===o.platform||i.whatsapp,r="telegram"===o.platform||i.telegram;l.innerHTML=`\n <div style="background:var(--bg-card);border-radius:12px;padding:24px;max-width:500px;width:90%;box-shadow:0 8px 32px rgba(0,0,0,0.4);">\n <h3 style="margin:0 0 16px 0;">💬 Send Message</h3>\n <div style="margin-bottom:12px;">\n <div style="font-size:13px;color:var(--text-2);margin-bottom:8px;">To: <strong>${e(o.display_name||o.contact_id)}</strong></div>\n </div>\n <div style="margin-bottom:12px;">\n <label style="font-size:12px;color:var(--text-3);display:block;margin-bottom:4px;">Platform</label>\n <select id="sendMessagePlatform" style="width:100%;padding:8px;border-radius:6px;border:1px solid var(--border);background:var(--bg-1);font-size:13px;">\n ${d?'<option value="whatsapp">WhatsApp</option>':""}\n ${r?'<option value="telegram">Telegram</option>':""}\n ${d&&r?'<option value="both">Both</option>':""}\n </select>\n </div>\n <div style="margin-bottom:16px;">\n <label style="font-size:12px;color:var(--text-3);display:block;margin-bottom:4px;">Message</label>\n <textarea id="sendMessageText" rows="4" placeholder="Your message here..." style="width:100%;padding:8px;border-radius:6px;border:1px solid var(--border);background:var(--bg-1);font-size:13px;"></textarea>\n </div>\n <div style="display:flex;gap:8px;justify-content:flex-end;">\n <button onclick="document.getElementById('modalOverlay').style.display='none'" class="btn-ghost">Cancel</button>\n <button onclick="window.sendContactMessage('${e(t)}')" class="btn-green">Send</button>\n </div>\n </div>\n `,l.style.display="flex"}(d):"newContact"===i?u():"applyContactFilters"===i?s():"filterByLetter"===i?p(g):"loadContacts"===i&&r()});const t=document.getElementById("contactsSearch");t&&t.addEventListener("input",()=>{i.search=t.value.toLowerCase().trim(),c()});const d=document.getElementById("contactsPlatformFilter");d&&d.addEventListener("change",s);const g=document.getElementById("contactsSortBy");g&&g.addEventListener("change",s)}"undefined"!=typeof window&&(window.loadContacts=r,window.searchContacts=s,window.newContact=u,window.createContact=async function(){const t=document.getElementById("newContactPlatform").value,e=document.getElementById("newContactId").value.trim(),a=document.getElementById("newContactName").value.trim(),l=document.getElementById("newContactPhone").value.trim(),i=document.getElementById("newContactEmail").value.trim(),d=document.getElementById("newContactNotes").value.trim();if(!t||!e||!a)return void n("Platform, Platform ID, and Display Name are required",!0);const s=`${t}:${e}`;try{await o("/api/contacts/create",{contact_id:s,platform:t,display_name:a,phone_number:l||null,email:i||null,notes:d||null}),n("✅ Contact created"),document.getElementById("modalOverlay").style.display="none",r()}catch(p){n("Failed: "+p.message,!0)}},window.sendContactMessage=async function(t){const e=document.getElementById("sendMessagePlatform").value,a=document.getElementById("sendMessageText").value.trim();if(a)try{await o("/api/contacts/send",{contactId:t,platform:e,message:a}),n("✅ Message sent"),document.getElementById("modalOverlay").style.display="none"}catch(l){n("Failed: "+l.message,!0)}else n("Message cannot be empty",!0)},window.filterByLetter=p);export{s as a,g as i,r as l,d as s};
|
|
Binary file
|
package/apps/dashboard/dist/assets/{tab-engines-tab-C3DYxTwy.js → tab-engines-tab-BsdZVvU0.js}
RENAMED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{p as e,g as t,e as n}from"./core-utils-
|
|
1
|
+
import{p as e,g as t,e as n}from"./core-utils-CmOkXgzi.js";const o={opencode:'<svg viewBox="0 0 24 30" width="20" height="24" fill="#38bdf8"><path d="M12 2L2 7l10 5 10-5-10-5zM2 17l10 5 10-5M2 12l10 5 10-5"/></svg>',cursor:'<svg viewBox="0 0 24 24" width="20" height="20" fill="#818cf8"><path d="M4 4l8 16 3-7 7-3L4 4z"/></svg>',claude:'<svg viewBox="0 0 24 24" width="20" height="20" fill="#d4a853"><path d="M17.3041 3.541h-3.6718l6.696 16.918H24Zm-10.6082 0L0 20.459h3.7442l1.3693-3.5527h7.0052l1.3693 3.5528h3.7442L10.5363 3.5409Zm-.3712 10.2232 2.2914-5.9456 2.2914 5.9456Z"/></svg>',codex:'<svg viewBox="0 0 24 24" width="20" height="20" fill="none"><circle cx="12" cy="12" r="10" stroke="#a78bfa" stroke-width="1.5"/><path d="M8 12l3 3 5-5" stroke="#a78bfa" stroke-width="1.5" fill="none" stroke-linecap="round" stroke-linejoin="round"/></svg>',"docker-sandbox":'<svg viewBox="0 0 24 24" width="20" height="20" fill="#2496ed"><path d="M13.983 11.078h2.119a.186.186 0 00.186-.185V9.006a.186.186 0 00-.186-.186h-2.119a.185.185 0 00-.185.185v1.888c0 .102.083.185.185.185m-2.954-5.43h2.118a.186.186 0 00.186-.186V3.574a.186.186 0 00-.186-.185h-2.118a.185.185 0 00-.185.185v1.888c0 .102.082.185.185.185m0 2.716h2.118a.187.187 0 00.186-.186V6.29a.186.186 0 00-.186-.185h-2.118a.185.185 0 00-.185.185v1.887c0 .102.082.186.185.186m-2.93 0h2.12a.186.186 0 00.184-.186V6.29a.185.185 0 00-.185-.185H8.1a.185.185 0 00-.185.185v1.887c0 .102.083.186.185.186m-2.943 0h2.119a.186.186 0 00.185-.186V6.29a.185.185 0 00-.185-.185H5.157a.185.185 0 00-.185.185v1.887c0 .102.083.186.185.186m8.763 2.714h2.119a.186.186 0 00.186-.185V9.006a.186.186 0 00-.186-.186h-2.119a.185.185 0 00-.185.185v1.888c0 .102.083.185.185.185m-2.93 0h2.12a.185.185 0 00.184-.185V9.006a.185.185 0 00-.184-.186h-2.12a.185.185 0 00-.184.185v1.888c0 .102.083.185.185.185M23.763 9.89c-.065-.051-.672-.51-1.954-.51-.338.001-.676.03-1.01.087-.248-1.7-1.653-2.53-1.716-2.566l-.344-.199-.226.327c-.284.438-.49.922-.612 1.43-.23.97-.09 1.882.403 2.661-.595.332-1.55.413-1.744.42H.751a.751.751 0 00-.75.748 11.376 11.376 0 00.692 4.062c.545 1.428 1.355 2.48 2.41 3.124 1.18.723 3.1 1.137 5.275 1.137.983.003 1.963-.086 2.93-.266a12.248 12.248 0 003.823-1.389c.98-.567 1.86-1.288 2.61-2.136 1.252-1.418 1.998-2.997 2.553-4.4h.221c1.372 0 2.215-.549 2.68-1.009.309-.293.55-.65.707-1.046l.098-.288Z"/></svg>'};function i(){const e=document.getElementById("importEngineForm");e&&(e.style.display="none"===e.style.display?"block":"none")}async function a(){var t;const n=document.getElementById("importEngineUrl"),o=document.getElementById("importEngineStatus"),i=null==(t=null==n?void 0:n.value)?void 0:t.trim();if(i&&o){o.textContent="Importing…",o.style.color="var(--text-3)";try{const t=await e("/api/engines/import",{url:i});t.ok?(o.textContent=`✓ Imported ${t.label}`,o.style.color="var(--green)",n.value="",r()):(o.textContent="Error: "+(t.error||"unknown"),o.style.color="var(--red,#f87171)")}catch(a){o.textContent="Error: "+a.message,o.style.color="var(--red,#f87171)"}}}async function l(e){confirm(`Remove engine "${e}"?`)&&(await fetch(`/api/engines/${encodeURIComponent(e)}`,{method:"DELETE"}),r())}async function r(){var e,i,a;const l=document.getElementById("enginesGrid");if(l){l.innerHTML='<div style="color:var(--text-3);font-size:13px;padding:8px;">Loading…</div>';try{const{engines:r=[]}=await t("/api/engines");if(!r.length)return void(l.innerHTML='<div style="color:var(--text-3);font-size:13px;padding:8px;">No engines found.</div>');l.innerHTML="";for(const t of r){const r=document.createElement("div");r.className="card",r.style.cssText="display:flex;flex-direction:column;gap:10px;";const d=o[t.icon||t.id]||'<span style="font-size:20px;">⚙️</span>',s=t.ready?"🟢":t.installed?"🟡":"⚫",p=t.ready?"Ready":t.installed&&t.requiresAuth?"Installed — run auth to activate":t.installed?"Installed — missing env vars":"Not installed",c=t.ready?"var(--green)":t.installed?"var(--yellow,#fbbf24)":"var(--text-3)",g=(t.traits||[]).map(e=>`<li style="font-size:11px;color:var(--text-3);list-style:none;padding:2px 0;">▸ ${n(e)}</li>`).join(""),v=(null==(e=t.missingEnv)?void 0:e.length)?`<div style="font-size:11px;color:var(--yellow,#fbbf24);margin-top:4px;">Missing env: ${t.missingEnv.map(e=>`<code style="background:var(--bg-1);padding:1px 3px;border-radius:3px;">${n(e)}</code>`).join(", ")}</div>`:"",x=t.installed?"":`<div style="margin-top:6px;"><div style="font-size:11px;color:var(--text-3);margin-bottom:4px;">Install:</div>\n <code style="font-size:11px;background:var(--bg-1);padding:4px 8px;border-radius:4px;display:block;word-break:break-all;">${n(t.installCmd||"")}</code>\n ${t.installUrl?`<a href="${n(t.installUrl)}" target="_blank" style="font-size:11px;color:var(--accent);margin-top:4px;display:inline-block;">↗ Install guide</a>`:""}\n </div>`,h=(null==(i=t.authMethods)?void 0:i.length)?`<div style="margin-top:8px;border-top:1px solid var(--border);padding-top:10px;">\n <div style="font-size:11px;font-weight:600;color:var(--text-2);margin-bottom:8px;display:flex;align-items:center;gap:6px;">\n <span style="font-size:13px;">🔑</span> Auth setup\n ${t.authNote?`<span style="font-weight:400;color:var(--text-3);">— ${n(t.authNote)}</span>`:""}\n </div>\n ${t.authMethods.map((e,t)=>`\n <div style="margin-bottom:10px;">\n <div style="font-size:11px;font-weight:600;color:var(--text-2);margin-bottom:4px;">${n(e.label)}</div>\n <div style="position:relative;display:flex;align-items:stretch;gap:0;">\n <code style="flex:1;font-size:11px;background:var(--bg-1);padding:6px 8px;border-radius:4px 0 0 4px;display:block;word-break:break-all;border:1px solid var(--border);border-right:none;">${n(e.cmd)}</code>\n <button onclick="navigator.clipboard.writeText(${n(JSON.stringify(e.cmd))}).then(()=>{this.textContent='✓';setTimeout(()=>this.textContent='Copy',1200)})" style="font-size:10px;padding:0 8px;border-radius:0 4px 4px 0;border:1px solid var(--border);background:var(--bg-card2);color:var(--text-2);cursor:pointer;white-space:nowrap;flex-shrink:0;">Copy</button>\n </div>\n ${e.note?`<div style="font-size:10px;color:var(--text-3);margin-top:3px;">${n(e.note)}</div>`:""}\n </div>\n `).join("")}\n </div>`:t.authNote?`<div style="font-size:11px;color:var(--text-3);margin-top:6px;">🔑 ${n(t.authNote)}</div>`:"",y=(null==(a=t.bestFor)?void 0:a.length)?`<div style="font-size:11px;color:var(--text-3);">Best for: ${t.bestFor.map(e=>`<code style="background:var(--bg-1);padding:1px 3px;border-radius:3px;">${n(e)}</code>`).join(" ")}</div>`:"",f="user"===t.source?`<button onclick="deleteEngine('${n(t.id)}')" style="font-size:11px;padding:4px 8px;border-radius:5px;cursor:pointer;border:1px solid var(--border);background:transparent;color:var(--text-3);">Remove</button>`:"",b=t.envToggle&&t.ready?`<label style="display:flex;align-items:center;gap:8px;cursor:pointer;margin-top:10px;padding:8px;background:var(--surface-2);border-radius:6px;border:1px solid var(--border);">\n <input type="checkbox" id="toggle-${t.id}" ${t.enabled?"checked":""} onchange="toggleEngineGlobal('${t.id}')" style="width:14px;height:14px;cursor:pointer;" />\n <span style="font-size:12px;font-weight:600;color:var(--text-1);">Enable Globally</span>\n <span style="font-size:11px;color:var(--text-3);">Sets ${n(t.envToggle)}</span>\n </label>`:"";r.innerHTML=`\n <div style="display:flex;align-items:center;justify-content:space-between;gap:10px;">\n <div style="display:flex;align-items:center;gap:10px;">\n ${d}\n <div>\n <div style="font-weight:700;font-size:14px;">${n(t.label)}</div>\n <div style="font-size:11px;color:${c};">${s} ${n(p)}</div>\n </div>\n </div>\n <div style="display:flex;gap:6px;align-items:center;">\n ${t.docsUrl?`<a href="${n(t.docsUrl)}" target="_blank" class="btn-ghost" style="font-size:11px;padding:4px 8px;text-decoration:none;">Docs ↗</a>`:""}\n ${f}\n </div>\n </div>\n <div style="font-size:12px;color:var(--text-2);line-height:1.5;">${n(t.description||"")}</div>\n ${v}\n ${x}\n <ul style="margin:0;padding:0;">${g}</ul>\n ${y}\n ${b}\n ${h}\n `,l.appendChild(r)}}catch(r){l.innerHTML=`<div style="color:var(--red,#f87171);font-size:13px;">Error: ${n(r.message)}</div>`}}}"undefined"!=typeof window&&(window.toggleEngineGlobal=async function(t){const n=document.getElementById(`toggle-${t}`),o=(null==n?void 0:n.checked)??!1;try{await e("/api/engines/toggle",{engineId:t,enabled:o});const n=o?"enabled":"disabled";console.log(`Engine ${t} ${n} globally`),window.showNotification&&window.showNotification(`${t} ${n} globally`)}catch(i){console.error("Toggle failed:",i),n&&(n.checked=!o),window.showNotification&&window.showNotification(`Toggle failed: ${i.message}`,!0)}});export{l as d,a as i,r as l,i as t};
|
|
Binary file
|
package/apps/dashboard/dist/assets/{tab-memory-tab-C59BYFQD.js → tab-memory-tab-Cu6u13EQ.js}
RENAMED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{p as e,s as t,g as r}from"./core-utils-
|
|
1
|
+
import{p as e,s as t,g as r}from"./core-utils-CmOkXgzi.js";async function n(){await o()}async function o(){var e;try{const t=await r("/api/memory/stats"),n=document.getElementById("memoryFactStats");if(n&&t.agentMemory){const r=t.agentMemory;n.innerHTML=`\n Total facts: <strong>${r.totalFacts||0}</strong><br>\n Critical facts: <strong>${r.criticalFacts||0}</strong><br>\n Providers: ${(null==(e=r.providers)?void 0:e.join(", "))||"none"}<br>\n ${r.oldestFact?`Oldest: ${new Date(r.oldestFact).toLocaleDateString()}<br>`:""}\n ${r.newestFact?`Newest: ${new Date(r.newestFact).toLocaleDateString()}`:""}\n `}const o=document.getElementById("memoryKeeperStats");if(o&&t.agentKeeper){const e=t.agentKeeper;o.innerHTML=`\n Total entries: <strong>${e.entries||0}</strong><br>\n Storage: <strong>${e.bytes?(e.bytes/1024).toFixed(1)+"KB":"0KB"}</strong><br>\n ${e.byTier?`By tier: ${Object.entries(e.byTier).map(([e,t])=>`${e}=${t}`).join(", ")}<br>`:""}\n ${e.byAgent?`By agent: ${Object.entries(e.byAgent).map(([e,t])=>`${e}=${t}`).join(", ")}`:""}\n `}const i=document.getElementById("memoryStorageInfo");i&&(i.innerHTML=`\n Location: <code style="font-size:11px;background:var(--bg-2);padding:2px 6px;border-radius:3px;">${t.storageDir||"N/A"}</code><br>\n Status: <strong style="color:var(--green);">${t.available?"✅ Active":"⚠️ Unavailable"}</strong><br>\n <span style="font-size:10px;color:var(--text-3);">Set CREW_MEMORY_DIR to customize location</span>\n `)}catch(n){t(`Failed to load memory stats: ${n.message}`,"error"),console.error("[memory] Stats load failed:",n)}}async function i(){const r=document.getElementById("memorySearchQuery");if(!r)return;const n=r.value.trim();if(!n)return void t("Enter a search query","error");const o=document.getElementById("memorySearchResults");if(o){o.style.display="block",o.innerHTML='<div style="padding:12px;color:var(--text-2);">Searching...</div>';try{const t=await e("/api/memory/search",{query:n,maxResults:20});if(!t.hits||0===t.hits.length)return void(o.innerHTML='<div style="padding:12px;color:var(--text-2);">No results found</div>');const r=t.hits.map(e=>{const t="agentkeeper"===e.source?"var(--blue)":"agent-memory"===e.source?"var(--green)":"var(--purple)",r=e.text.length>300?e.text.slice(0,300)+"...":e.text;return`\n <div style="padding:12px;border-left:3px solid ${t};background:var(--bg-2);border-radius:6px;margin-bottom:8px;">\n <div style="display:flex;align-items:center;gap:8px;margin-bottom:6px;">\n <span style="font-size:10px;font-weight:600;color:${t};text-transform:uppercase;">${e.source}</span>\n <span style="font-size:11px;font-weight:600;color:var(--text-1);flex:1;">${c(e.title)}</span>\n <span style="font-size:10px;color:var(--text-3);font-family:monospace;">score: ${e.score.toFixed(3)}</span>\n </div>\n <div style="font-size:11px;color:var(--text-2);line-height:1.5;white-space:pre-wrap;">${c(r)}</div>\n ${e.metadata?`<div style="font-size:10px;color:var(--text-3);margin-top:6px;">${JSON.stringify(e.metadata)}</div>`:""}\n </div>\n `}).join("");o.innerHTML=`\n <div style="padding:8px 0;font-size:12px;color:var(--text-2);">\n Found <strong>${t.hits.length}</strong> result(s) for "<strong>${c(n)}</strong>"\n </div>\n ${r}\n `}catch(i){o.innerHTML=`<div style="padding:12px;color:var(--red);">Search failed: ${c(i.message)}</div>`,t(`Memory search failed: ${i.message}`,"error")}}}async function a(){const r=document.getElementById("memoryActionResult");if(r){r.style.display="block",r.innerHTML='<div style="color:var(--text-2);">Migrating brain.md entries to shared memory...</div>';try{const n=await e("/api/memory/migrate",{});n.ok?(r.innerHTML=`\n <div style="color:var(--green);">✅ Migration complete</div>\n <div style="margin-top:6px;font-size:11px;">\n Imported: ${n.imported}, Skipped: ${n.skipped}, Errors: ${n.errors}\n </div>\n `,t("Brain.md migrated successfully","success"),await o()):(r.innerHTML=`<div style="color:var(--red);">❌ Migration failed: ${c(n.error||"unknown error")}</div>`,t(`Migration failed: ${n.error}`,"error"))}catch(n){r.innerHTML=`<div style="color:var(--red);">❌ Migration failed: ${c(n.message)}</div>`,t(`Migration failed: ${n.message}`,"error")}}}async function s(){const r=document.getElementById("memoryActionResult");if(r){r.style.display="block",r.innerHTML='<div style="color:var(--text-2);">Compacting AgentKeeper store...</div>';try{const n=await e("/api/memory/compact",{});if(void 0!==n.entriesBefore){const e=(n.bytesFreed/1024).toFixed(1);r.innerHTML=`\n <div style="color:var(--green);">✅ Compaction complete</div>\n <div style="margin-top:6px;font-size:11px;">\n Entries: ${n.entriesBefore} → ${n.entriesAfter}<br>\n Space freed: ${e}KB\n </div>\n `,t("AgentKeeper compacted successfully","success"),await o()}else r.innerHTML=`<div style="color:var(--red);">❌ Compaction failed: ${c(n.error||"unknown error")}</div>`,t(`Compaction failed: ${n.error}`,"error")}catch(n){r.innerHTML=`<div style="color:var(--red);">❌ Compaction failed: ${c(n.message)}</div>`,t(`Compaction failed: ${n.message}`,"error")}}}function c(e){const t=document.createElement("div");return t.textContent=e,t.innerHTML}export{n as a,s as c,o as l,a as m,i as s};
|
|
Binary file
|
package/apps/dashboard/dist/assets/{tab-models-tab-CQzvaeVh.js → tab-models-tab-BLEjmd19.js}
RENAMED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{s as e,p as t,g as n,k as o,d as a}from"./core-utils-CAVnDoe1.js";let s=()=>{},i=()=>{},r=()=>{};function l({hideAllViews:e,setNavActive:t,loadAgents:n}={}){s=e||s,i=t||i,r=n||r}const d=[{id:"groq",label:"Groq",icon:"⚡",url:"https://console.groq.com/keys",hint:"Fast inference — great for crew-coder, crew-fixer"},{id:"fireworks",label:"Fireworks AI",icon:"🎆",url:"https://fireworks.ai/",hint:"OpenAI-compatible inference platform — fast serverless models, custom deployments, and easy model discovery"},{id:"anthropic",label:"Anthropic",icon:"🟣",url:"https://console.anthropic.com/",hint:"Claude models — best for complex reasoning tasks"},{id:"openai",label:"OpenAI (API)",icon:"🟢",url:"https://platform.openai.com/api-keys",hint:"GPT-4o and o-series — pay per use with API key"},{id:"cerebras",label:"Cerebras",icon:"🧠",url:"https://cloud.cerebras.ai/",hint:"Ultra-fast inference on Cerebras hardware — llama-3.3-70b at 2,000 tok/s"},{id:"nvidia",label:"NVIDIA NIM",icon:"🎮",url:"https://build.nvidia.com/explore/discover",hint:"NVIDIA NIM microservices — Llama, Mistral, Phi and more"},{id:"openrouter",label:"OpenRouter",icon:"🔀",url:"https://openrouter.ai/keys",hint:"One API key for 400+ models — Claude, GPT-4, Gemini, Hunter Alpha, Llama and more"},{id:"perplexity",label:"Perplexity",icon:"🔍",url:"https://www.perplexity.ai/settings/api",hint:"Sonar Pro — ideal for crew-pm research tasks"},{id:"mistral",label:"Mistral",icon:"🌀",url:"https://console.mistral.ai/",hint:"Open-weight models, efficient mid-tier tasks"},{id:"deepseek",label:"DeepSeek",icon:"🌊",url:"https://platform.deepseek.com/",hint:"Low cost, strong coding performance"},{id:"together",label:"Together AI",icon:"🤝",url:"https://api.together.ai/",hint:"OpenAI-compatible access to strong open models like Qwen, DeepSeek, Llama, and more"},{id:"xai",label:"xAI (Grok)",icon:"𝕏",url:"https://console.x.ai/",hint:"Grok models with real-time X/Twitter access, vision (grok-vision-beta), 128K context — ideal for research, social media analysis"},{id:"huggingface",label:"Hugging Face",icon:"🤗",url:"https://huggingface.co/settings/tokens",hint:"Open-source model hub — access thousands of models via Inference API"},{id:"venice",label:"Venice AI",icon:"🏖️",url:"https://venice.ai/settings/api",hint:"Privacy-focused inference — no logging, no training on your data"},{id:"moonshot",label:"Moonshot / Kimi",icon:"🌙",url:"https://platform.moonshot.cn/console/api-keys",hint:"128K+ context windows — strong on long codebases, Chinese + English"},{id:"minimax",label:"MiniMax",icon:"✨",url:"https://www.minimaxi.com/",hint:"Chinese LLM provider — competitive pricing, multilingual"},{id:"volcengine",label:"Volcengine",icon:"🌋",url:"https://console.volcengine.com/ark",hint:"ByteDance Doubao models — fast inference"},{id:"qianfan",label:"Baidu Qianfan",icon:"🔵",url:"https://console.bce.baidu.com/qianfan/",hint:"Baidu ERNIE models — strong on Chinese language and reasoning"},{id:"ollama",label:"Ollama",icon:"🏠",url:"https://ollama.com/download",hint:"Local models — no API key needed, runs offline"},{id:"vllm",label:"vLLM",icon:"⚡",url:"https://docs.vllm.ai/",hint:"Self-hosted inference server — any open model, OpenAI-compatible"},{id:"sglang",label:"SGLang",icon:"⚡",url:"https://github.com/sgl-project/sglang",hint:"Self-hosted inference server — fast structured generation"},{id:"openai-local",label:"OpenAI (local)",icon:"🟢",url:"https://github.com/RayBytes/ChatMock",hint:"ChatMock — use ChatGPT Plus/Pro subscription. Run ChatMock server first (e.g. port 8000). Key ignored."}],p=[{id:"parallel",label:"Parallel",icon:"🔬",url:"https://platform.parallel.ai/signup",hint:"Deep research & web synthesis — used by crew-pm for project planning",envKey:"PARALLEL_API_KEY"},{id:"brave",label:"Brave Search",icon:"🦁",url:"https://api.search.brave.com/",hint:"Fast web search (~700ms) — best for quick agent lookups",envKey:"BRAVE_API_KEY"}],c={opencode:"🚀",groq:"⚡",fireworks:"🎆",nvidia:"🎮",ollama:"🏠","openai-local":"🟢",xai:"𝕏",google:"🔵",deepseek:"🌊",openai:"🟢",perplexity:"🔍",cerebras:"🧠",mistral:"🌀",together:"🤝",cohere:"🔶",anthropic:"🟣",openrouter:"🔀",huggingface:"🤗",venice:"🏖️",moonshot:"🌙",minimax:"✨",volcengine:"🌋",qianfan:"🔵",vllm:"⚡",sglang:"⚡"};function m(){s(),document.getElementById("modelsView").classList.add("active"),i("navModels"),async function(){try{const e=await n("/api/settings/rt-token"),t=document.getElementById("rtTokenBadge"),o=document.getElementById("rtTokenInput");if(!t)return;e.token?(t.textContent="set ✓",t.style.background="rgba(52,211,153,0.15)",t.style.color="var(--green)",t.style.borderColor="rgba(52,211,153,0.3)",o&&(o.placeholder="••••••••••••••••••••••• (saved)")):(t.textContent="not set",t.style.background="rgba(251,191,36,0.15)",t.style.color="var(--yellow)",t.style.borderColor="rgba(251,191,36,0.3)")}catch{}}(),v(),g()}async function g(){const e=document.getElementById("searchToolsList");if(!e)return;let t={};try{t=(await n("/api/search-tools")).keys||{}}catch{}e.innerHTML=p.map(e=>{const n=!!t[e.id],o=n?'<span style="font-size:11px;padding:2px 8px;border-radius:999px;font-weight:600;background:rgba(52,211,153,0.15);color:var(--green);border:1px solid rgba(52,211,153,0.3);">set ✓</span>':'<span style="font-size:11px;padding:2px 8px;border-radius:999px;font-weight:600;background:rgba(107,114,128,0.12);color:var(--text-2);border:1px solid var(--border);">no key</span>';return`<div class="card" style="margin-bottom:8px;">\n <div style="display:flex;align-items:center;gap:10px;cursor:pointer;" data-toggle-child=".st-body">\n <span style="font-size:18px;width:24px;text-align:center;">${e.icon}</span>\n <div style="flex:1;">\n <div style="font-weight:600;font-size:13px;">${e.label}</div>\n <div style="font-size:11px;color:var(--text-2);">${e.hint}</div>\n </div>\n ${o}\n <span style="color:var(--text-2);font-size:12px;">▾</span>\n </div>\n <div class="st-body" style="display:none;margin-top:12px;padding-top:12px;border-top:1px solid var(--border);">\n <div style="display:flex;gap:8px;">\n <input id="st_${e.id}" type="password" autocomplete="new-password" placeholder="${n?"••••••••••••••• (saved — paste to update)":"Paste API key"}" style="flex:1;" />\n <button data-action="saveSearchTool" data-arg="${e.id}" class="btn-purple">Save</button>\n <button data-action="testSearchTool" data-arg="${e.id}" class="btn-ghost">Test</button>\n <a href="${e.url}" target="_blank" class="btn-ghost" style="text-decoration:none;font-size:12px;">Keys ↗</a>\n </div>\n <div style="font-size:11px;color:var(--text-2);margin-top:6px;">Saved as <code style="background:rgba(255,255,255,0.06);padding:1px 5px;border-radius:4px;">${e.envKey}</code> in environment</div>\n <div id="st_status_${e.id}" style="font-size:12px;margin-top:8px;color:var(--text-2);"></div>\n </div>\n </div>`}).join("")}async function y(n){var o;const a=document.getElementById("st_"+n),s=null==(o=null==a?void 0:a.value)?void 0:o.trim();if(s)try{await t("/api/search-tools/save",{toolId:n,key:s}),e("Key saved","success"),g()}catch(i){e("Save failed: "+i.message,"error")}else e("Paste an API key first","error")}async function u(e){const n=document.getElementById("st_status_"+e);n.textContent="Testing…";try{const o=await t("/api/search-tools/test",{toolId:e});n.style.color=o.ok?"var(--green)":"var(--red)",n.textContent=o.ok?"✓ "+(o.message||"Connected"):"✗ "+(o.error||"Failed")}catch(o){n.style.color="var(--red)",n.textContent="✗ "+o.message}}async function v(){const e=document.getElementById("builtinProvidersList");if(!e)return;let t={};try{t=(await n("/api/providers/builtin")).keys||{}}catch{}const o=new Set(d.map(e=>e.id));let a=d.map(e=>{const n=!!t[e.id],o="ollama"===e.id,a="openai-local"===e.id,s=n||o||a?`<span style="font-size:11px;padding:2px 8px;border-radius:999px;font-weight:600;background:rgba(52,211,153,0.15);color:var(--green);border:1px solid rgba(52,211,153,0.3);">${!o&&!a||n?"set ✓":"local"}</span>`:'<span style="font-size:11px;padding:2px 8px;border-radius:999px;font-weight:600;background:rgba(107,114,128,0.12);color:var(--text-2);border:1px solid var(--border);">no key</span>';return`<div class="card" style="margin-bottom:8px;">\n <div style="display:flex;align-items:center;gap:10px;cursor:pointer;" data-toggle-child=".bp-body">\n <span style="font-size:18px;width:24px;text-align:center;">${e.icon}</span>\n <div style="flex:1;">\n <div style="font-weight:600;font-size:13px;">${e.label}</div>\n <div style="font-size:11px;color:var(--text-2);">${e.hint}</div>\n </div>\n ${s}\n <span style="color:var(--text-2);font-size:12px;">▾</span>\n </div>\n <div class="bp-body" style="display:none;margin-top:12px;padding-top:12px;border-top:1px solid var(--border);">\n ${o?'<div style="font-size:12px;color:var(--text-2);margin-bottom:8px;">Ollama runs locally — no API key required. Make sure Ollama is running on port 11434.</div>':""}\n <div style="display:flex;gap:8px;flex-wrap:wrap;">\n ${o?"":`<input id="bp_${e.id}" type="password" autocomplete="new-password" placeholder="${n?"••••••••••••••• (saved — paste to update)":"Paste API key"}" style="flex:1;min-width:180px;" />`}\n ${o?`<button data-action="testBuiltinProvider" data-arg="${e.id}" class="btn-ghost">Test Connection</button>\n <button data-action="fetchBuiltinModels" data-arg="${e.id}" data-self="1" class="btn-ghost" style="background:#0f766e20;color:var(--green);border-color:#0f766e40;">↻ Models</button>`:`<button data-action="saveBuiltinKey" data-arg="${e.id}" class="btn-purple">Save</button>\n <button data-action="testBuiltinProvider" data-arg="${e.id}" class="btn-ghost">Test</button>\n <button data-action="fetchBuiltinModels" data-arg="${e.id}" data-self="1" class="btn-ghost" style="background:#0f766e20;color:var(--green);border-color:#0f766e40;">↻ Models</button>\n <a href="${e.url}" target="_blank" class="btn-ghost" style="text-decoration:none;font-size:12px;">Keys ↗</a>`}\n </div>\n <div id="bp_status_${e.id}" style="font-size:12px;margin-top:8px;color:var(--text-2);"></div>\n <div id="bp_models_${e.id}" style="margin-top:8px;display:none;">\n <span style="font-size:11px;color:var(--text-2);display:block;margin-bottom:4px;">Models (<span id="bp_mcount_${e.id}">0</span>):</span>\n <span id="bp_mtags_${e.id}"></span>\n </div>\n </div>\n </div>`}).join("");try{const e=((await n("/api/providers")).providers||[]).filter(e=>!o.has(e.id)&&"greptile"!==e.id);e.length&&(a+='<div style="font-size:11px;font-weight:600;color:var(--text-2);text-transform:uppercase;letter-spacing:0.08em;margin:14px 0 8px;padding:0 2px;">Custom Providers</div>',a+=e.map(e=>{var t;const n=c[e.id]||"🔌",o=e.hasKey,a=o?'<span style="font-size:11px;padding:2px 8px;border-radius:999px;font-weight:600;background:rgba(52,211,153,0.15);color:var(--green);border:1px solid rgba(52,211,153,0.3);">key set ✓</span>':'<span style="font-size:11px;padding:2px 8px;border-radius:999px;font-weight:600;background:rgba(107,114,128,0.12);color:var(--text-2);border:1px solid var(--border);">no key</span>',s=(null==(t=e.models)?void 0:t.length)||0;return`<div class="card" style="margin-bottom:8px;">\n <div style="display:flex;align-items:center;gap:10px;cursor:pointer;" data-toggle-child=".cp-body">\n <span style="font-size:18px;width:24px;text-align:center;">${n}</span>\n <div style="flex:1;">\n <div style="font-weight:600;font-size:13px;">${e.id}</div>\n <div style="font-size:11px;color:var(--text-2);">${e.baseUrl}${s?" · "+s+" models":""}</div>\n </div>\n ${a}\n <span style="color:var(--text-2);font-size:12px;">▾</span>\n </div>\n <div class="cp-body" style="display:none;margin-top:12px;padding-top:12px;border-top:1px solid var(--border);">\n <div style="display:flex;gap:8px;flex-wrap:wrap;">\n <input id="key_${e.id}" type="password" autocomplete="new-password" placeholder="${o?"••••••••••••••• (saved — paste to update)":"Paste API key"}" style="flex:1;min-width:180px;" />\n <button data-action="saveKey" data-arg="${e.id}" class="btn-purple">Save</button>\n <button data-action="testKey" data-arg="${e.id}" class="btn-ghost">Test</button>\n <button data-action="fetchModels" data-arg="${e.id}" data-self="1" class="btn-ghost" style="background:#0f766e20;color:var(--green);border-color:#0f766e40;">↻ Models</button>\n </div>\n <div style="font-size:11px;color:var(--text-2);margin-top:6px;">Base URL: <code style="font-size:10px;">${e.baseUrl}</code></div>\n <div id="test_${e.id}" style="font-size:12px;margin-top:8px;color:var(--text-2);"></div>\n <div id="mwrap_${e.id}" style="margin-top:8px;${s?"":"display:none;"}">\n <span style="font-size:11px;color:var(--text-2);">Models (<span id="mcount_${e.id}">${s}</span>):</span>\n <span id="mtags_${e.id}">${(e.models||[]).map(e=>'<span class="model-tag">'+(e.id||e)+"</span>").join("")}</span>\n </div>\n </div>\n </div>`}).join(""))}catch{}e.innerHTML=a}async function b(n){var o;const a=document.getElementById("bp_"+n),s=null==(o=null==a?void 0:a.value)?void 0:o.trim();if(s||"openai-local"===n){await t("/api/providers/builtin/save",{providerId:n,apiKey:s||""}),a&&(a.value=""),e("Key saved — fetching models…"),await v();try{const o=await t("/api/providers/fetch-models",{providerId:n});if(o.ok){const t=document.getElementById("bp_mtags_"+n),a=document.getElementById("bp_mcount_"+n),s=document.getElementById("bp_models_"+n),i=document.getElementById("bp_status_"+n);t&&(t.innerHTML=o.models.map(e=>'<span class="model-tag">'+e+"</span>").join("")),a&&(a.textContent=o.models.length),s&&(s.style.display="block"),i&&(i.style.color="var(--green)",i.textContent="✓ "+o.models.length+" models"),e("Key saved for "+n+" — "+o.models.length+" models ready"),r()}else e("Key saved — could not fetch models: "+(o.error||"unknown"),"warning")}catch(i){e("Key saved — model fetch failed: "+i.message,"warning")}}else e("Paste an API key first","error")}async function x(e){const n=document.getElementById("bp_status_"+e);n.textContent="Testing…";try{const o=await t("/api/providers/builtin/test",{providerId:e});n.style.color=o.ok?"var(--green)":"var(--red)",n.textContent=o.ok?"✓ Connected — "+(o.model||"OK"):"✗ "+(o.error||"Failed")}catch(o){n.style.color="var(--red)",n.textContent="✗ "+o.message}}async function h(e,n){const o=document.getElementById("bp_status_"+e),a=n.textContent;n.textContent="Fetching…",n.disabled=!0,o.textContent="";try{const n=await t("/api/providers/fetch-models",{providerId:e});if(n.ok){const t=document.getElementById("bp_mtags_"+e),a=document.getElementById("bp_mcount_"+e),s=document.getElementById("bp_models_"+e);t&&(t.innerHTML=n.models.map(e=>'<span class="model-tag">'+e+"</span>").join("")),a&&(a.textContent=n.models.length),s&&(s.style.display="block"),o.style.color="var(--green)",o.textContent="✓ "+n.models.length+" models fetched"+(n.note?" — "+n.note:""),r()}else o.style.color="var(--red)",o.textContent="✗ "+(n.error||"Failed")}catch(s){o.style.color="var(--red)",o.textContent="✗ "+s.message}finally{n.textContent=a,n.disabled=!1}}function f(e,t){const n=document.getElementById(e);n.type="password"===n.type?"text":"password",t.textContent="password"===n.type?"👁":"🙈"}async function k(s){const i=document.getElementById("key_"+s).value.trim();if(i)try{await t("/api/providers/save",{providerId:s,apiKey:i}),e("Saved key for "+s),async function(){const e=document.getElementById("providersList");if(e){o(e,"Loading providers...");try{const t=(await n("/api/providers")).providers||[];if(!t.length)return void showEmpty(e,"No providers found. Check ~/.crewswarm/crewswarm.json");e.innerHTML="",t.forEach(t=>{const n=c[t.id]||"🔌",o=t.hasKey,a=o?"#10b981":"var(--red-hi)",s=o?"✓ key set":"✗ no key",i=document.createElement("div");i.className="provider-card",i.innerHTML=`\n <div class="provider-header" data-toggle-sibling="open">\n <span style="font-size:20px;">${n}</span>\n <div style="flex:1;">\n <strong style="font-size:15px;">${t.id}</strong>\n <span class="meta" style="margin-left:10px;">${t.baseUrl}</span>\n </div>\n <span class="provider-badge" style="background:${a}20; color:${a}; border:1px solid ${a}40;">${s}</span>\n <span class="meta" style="margin-left:12px;">${t.models.length} model${1!==t.models.length?"s":""}</span>\n <span style="color:#64748b; margin-left:8px;">▼</span>\n </div>\n <div class="provider-body">\n <div class="key-row">\n <input class="key-input" type="password" autocomplete="new-password" id="key_${t.id}" value="${t.maskedKey||""}" placeholder="Paste API key…" />\n <button data-action="toggleKeyVis" data-arg="key_${t.id}" data-self="1" style="background:#334155; padding:6px 10px; font-size:12px;">👁</button>\n <button data-action="saveKey" data-arg="${t.id}" style="background:#6366f1; padding:6px 14px; font-size:12px;">Save</button>\n <button data-action="testKey" data-arg="${t.id}" style="background:#334155; padding:6px 10px; font-size:12px;">Test</button>\n <button data-action="fetchModels" data-arg="${t.id}" data-self="1" style="background:#0f766e; padding:6px 10px; font-size:12px;">↻ Fetch models</button>\n <span id="test_${t.id}"></span>\n </div>\n <div style="margin-bottom:8px;"><span class="meta">Base URL: </span><code style="font-size:11px; color:#94a3b8;">${t.baseUrl}</code></div>\n <div><span class="meta" style="display:block; margin-bottom:6px;">Models (<span id="mcount_${t.id}">${t.models.length}</span>):</span><span id="mtags_${t.id}">${t.models.map(e=>'<span class="model-tag">'+e.id+"</span>").join("")}</span></div>\n ${0===t.models.length?`<div class="meta" style="margin-top:8px; color:var(--amber);" id="mnone_${t.id}">No models yet — click ↻ Fetch models</div>`:""}\n </div>\n `,e.appendChild(i)})}catch(t){a(e,"Error: "+t.message)}}}(),r()}catch(l){e("Failed: "+l.message,!0)}else e("Key is empty",!0)}async function w(e){const n=document.getElementById("test_"+e);n.textContent="testing…",n.className="meta";try{const o=await t("/api/providers/test",{providerId:e});n.textContent=o.ok?"✓ "+(o.model||"ok"):"✗ "+o.error,n.className=o.ok?"test-ok":"test-err"}catch(o){n.textContent="✗ "+o.message,n.className="test-err"}}async function I(e,n){const o=document.getElementById("test_"+e),a=n.textContent;n.textContent="Fetching…",n.disabled=!0,o&&(o.textContent="");try{const n=await t("/api/providers/fetch-models",{providerId:e});if(n.ok){const t=document.getElementById("mtags_"+e),a=document.getElementById("mcount_"+e),s=document.getElementById("mnone_"+e),i=document.getElementById("mwrap_"+e);t&&(t.innerHTML=n.models.map(e=>'<span class="model-tag">'+e+"</span>").join("")),a&&(a.textContent=n.models.length),s&&(s.style.display="none"),i&&(i.style.display="block"),o&&(o.textContent="✓ "+n.models.length+" models",o.className="test-ok"),r()}else o&&(o.textContent="✗ "+n.error,o.className="test-err")}catch(s){o&&(o.textContent="✗ "+s.message,o.className="test-err")}finally{n.textContent=a,n.disabled=!1}}function $(){const n=document.getElementById("addProviderBtn");n&&(n.onclick=()=>{const e=document.getElementById("addProviderForm");e.style.display="block",setTimeout(()=>e.scrollIntoView({behavior:"smooth",block:"start"}),50);const t=e.querySelector("input");t&&setTimeout(()=>t.focus(),150)});const o=document.getElementById("apCancelBtn");o&&(o.onclick=()=>{document.getElementById("addProviderForm").style.display="none"});const a=document.getElementById("apSaveBtn");a&&(a.onclick=async()=>{const n=document.getElementById("apId").value.trim(),o=document.getElementById("apBaseUrl").value.trim(),a=document.getElementById("apKey").value.trim(),s=document.getElementById("apApi").value;if(n&&o)try{await t("/api/providers/add",{id:n,baseUrl:o,apiKey:a,api:s}),e("Provider added: "+n),document.getElementById("addProviderForm").style.display="none",v()}catch(i){e("Failed: "+i.message,!0)}else e("ID and Base URL are required",!0)});const s=document.getElementById("refreshProvidersBtn");s&&(s.onclick=v)}export{u as a,w as b,x as c,y as d,k as e,b as f,I as g,h,$ as i,l as j,m as s,f as t};
|
|
1
|
+
import{s as e,p as t,g as n,k as o,d as a}from"./core-utils-CmOkXgzi.js";let s=()=>{},i=()=>{},r=()=>{};function l({hideAllViews:e,setNavActive:t,loadAgents:n}={}){s=e||s,i=t||i,r=n||r}const d=[{id:"groq",label:"Groq",icon:"⚡",url:"https://console.groq.com/keys",hint:"Fast inference — great for crew-coder, crew-fixer"},{id:"fireworks",label:"Fireworks AI",icon:"🎆",url:"https://fireworks.ai/",hint:"OpenAI-compatible inference platform — fast serverless models, custom deployments, and easy model discovery"},{id:"anthropic",label:"Anthropic",icon:"🟣",url:"https://console.anthropic.com/",hint:"Claude models — best for complex reasoning tasks"},{id:"openai",label:"OpenAI (API)",icon:"🟢",url:"https://platform.openai.com/api-keys",hint:"GPT-4o and o-series — pay per use with API key"},{id:"cerebras",label:"Cerebras",icon:"🧠",url:"https://cloud.cerebras.ai/",hint:"Ultra-fast inference on Cerebras hardware — llama-3.3-70b at 2,000 tok/s"},{id:"nvidia",label:"NVIDIA NIM",icon:"🎮",url:"https://build.nvidia.com/explore/discover",hint:"NVIDIA NIM microservices — Llama, Mistral, Phi and more"},{id:"openrouter",label:"OpenRouter",icon:"🔀",url:"https://openrouter.ai/keys",hint:"One API key for 400+ models — Claude, GPT-4, Gemini, Hunter Alpha, Llama and more"},{id:"perplexity",label:"Perplexity",icon:"🔍",url:"https://www.perplexity.ai/settings/api",hint:"Sonar Pro — ideal for crew-pm research tasks"},{id:"mistral",label:"Mistral",icon:"🌀",url:"https://console.mistral.ai/",hint:"Open-weight models, efficient mid-tier tasks"},{id:"deepseek",label:"DeepSeek",icon:"🌊",url:"https://platform.deepseek.com/",hint:"Low cost, strong coding performance"},{id:"together",label:"Together AI",icon:"🤝",url:"https://api.together.ai/",hint:"OpenAI-compatible access to strong open models like Qwen, DeepSeek, Llama, and more"},{id:"xai",label:"xAI (Grok)",icon:"𝕏",url:"https://console.x.ai/",hint:"Grok models with real-time X/Twitter access, vision (grok-vision-beta), 128K context — ideal for research, social media analysis"},{id:"huggingface",label:"Hugging Face",icon:"🤗",url:"https://huggingface.co/settings/tokens",hint:"Open-source model hub — access thousands of models via Inference API"},{id:"venice",label:"Venice AI",icon:"🏖️",url:"https://venice.ai/settings/api",hint:"Privacy-focused inference — no logging, no training on your data"},{id:"moonshot",label:"Moonshot / Kimi",icon:"🌙",url:"https://platform.moonshot.cn/console/api-keys",hint:"128K+ context windows — strong on long codebases, Chinese + English"},{id:"minimax",label:"MiniMax",icon:"✨",url:"https://www.minimaxi.com/",hint:"Chinese LLM provider — competitive pricing, multilingual"},{id:"volcengine",label:"Volcengine",icon:"🌋",url:"https://console.volcengine.com/ark",hint:"ByteDance Doubao models — fast inference"},{id:"qianfan",label:"Baidu Qianfan",icon:"🔵",url:"https://console.bce.baidu.com/qianfan/",hint:"Baidu ERNIE models — strong on Chinese language and reasoning"},{id:"ollama",label:"Ollama",icon:"🏠",url:"https://ollama.com/download",hint:"Local models — no API key needed, runs offline"},{id:"vllm",label:"vLLM",icon:"⚡",url:"https://docs.vllm.ai/",hint:"Self-hosted inference server — any open model, OpenAI-compatible"},{id:"sglang",label:"SGLang",icon:"⚡",url:"https://github.com/sgl-project/sglang",hint:"Self-hosted inference server — fast structured generation"},{id:"openai-local",label:"OpenAI (local)",icon:"🟢",url:"https://github.com/RayBytes/ChatMock",hint:"ChatMock — use ChatGPT Plus/Pro subscription. Run ChatMock server first (e.g. port 8000). Key ignored."}],p=[{id:"parallel",label:"Parallel",icon:"🔬",url:"https://platform.parallel.ai/signup",hint:"Deep research & web synthesis — used by crew-pm for project planning",envKey:"PARALLEL_API_KEY"},{id:"brave",label:"Brave Search",icon:"🦁",url:"https://api.search.brave.com/",hint:"Fast web search (~700ms) — best for quick agent lookups",envKey:"BRAVE_API_KEY"}],c={opencode:"🚀",groq:"⚡",fireworks:"🎆",nvidia:"🎮",ollama:"🏠","openai-local":"🟢",xai:"𝕏",google:"🔵",deepseek:"🌊",openai:"🟢",perplexity:"🔍",cerebras:"🧠",mistral:"🌀",together:"🤝",cohere:"🔶",anthropic:"🟣",openrouter:"🔀",huggingface:"🤗",venice:"🏖️",moonshot:"🌙",minimax:"✨",volcengine:"🌋",qianfan:"🔵",vllm:"⚡",sglang:"⚡"};function m(){s(),document.getElementById("modelsView").classList.add("active"),i("navModels"),async function(){try{const e=await n("/api/settings/rt-token"),t=document.getElementById("rtTokenBadge"),o=document.getElementById("rtTokenInput");if(!t)return;e.token?(t.textContent="set ✓",t.style.background="rgba(52,211,153,0.15)",t.style.color="var(--green)",t.style.borderColor="rgba(52,211,153,0.3)",o&&(o.placeholder="••••••••••••••••••••••• (saved)")):(t.textContent="not set",t.style.background="rgba(251,191,36,0.15)",t.style.color="var(--yellow)",t.style.borderColor="rgba(251,191,36,0.3)")}catch{}}(),v(),g()}async function g(){const e=document.getElementById("searchToolsList");if(!e)return;let t={};try{t=(await n("/api/search-tools")).keys||{}}catch{}e.innerHTML=p.map(e=>{const n=!!t[e.id],o=n?'<span style="font-size:11px;padding:2px 8px;border-radius:999px;font-weight:600;background:rgba(52,211,153,0.15);color:var(--green);border:1px solid rgba(52,211,153,0.3);">set ✓</span>':'<span style="font-size:11px;padding:2px 8px;border-radius:999px;font-weight:600;background:rgba(107,114,128,0.12);color:var(--text-2);border:1px solid var(--border);">no key</span>';return`<div class="card" style="margin-bottom:8px;">\n <div style="display:flex;align-items:center;gap:10px;cursor:pointer;" data-toggle-child=".st-body">\n <span style="font-size:18px;width:24px;text-align:center;">${e.icon}</span>\n <div style="flex:1;">\n <div style="font-weight:600;font-size:13px;">${e.label}</div>\n <div style="font-size:11px;color:var(--text-2);">${e.hint}</div>\n </div>\n ${o}\n <span style="color:var(--text-2);font-size:12px;">▾</span>\n </div>\n <div class="st-body" style="display:none;margin-top:12px;padding-top:12px;border-top:1px solid var(--border);">\n <div style="display:flex;gap:8px;">\n <input id="st_${e.id}" type="password" autocomplete="new-password" placeholder="${n?"••••••••••••••• (saved — paste to update)":"Paste API key"}" style="flex:1;" />\n <button data-action="saveSearchTool" data-arg="${e.id}" class="btn-purple">Save</button>\n <button data-action="testSearchTool" data-arg="${e.id}" class="btn-ghost">Test</button>\n <a href="${e.url}" target="_blank" class="btn-ghost" style="text-decoration:none;font-size:12px;">Keys ↗</a>\n </div>\n <div style="font-size:11px;color:var(--text-2);margin-top:6px;">Saved as <code style="background:rgba(255,255,255,0.06);padding:1px 5px;border-radius:4px;">${e.envKey}</code> in environment</div>\n <div id="st_status_${e.id}" style="font-size:12px;margin-top:8px;color:var(--text-2);"></div>\n </div>\n </div>`}).join("")}async function y(n){var o;const a=document.getElementById("st_"+n),s=null==(o=null==a?void 0:a.value)?void 0:o.trim();if(s)try{await t("/api/search-tools/save",{toolId:n,key:s}),e("Key saved","success"),g()}catch(i){e("Save failed: "+i.message,"error")}else e("Paste an API key first","error")}async function u(e){const n=document.getElementById("st_status_"+e);n.textContent="Testing…";try{const o=await t("/api/search-tools/test",{toolId:e});n.style.color=o.ok?"var(--green)":"var(--red)",n.textContent=o.ok?"✓ "+(o.message||"Connected"):"✗ "+(o.error||"Failed")}catch(o){n.style.color="var(--red)",n.textContent="✗ "+o.message}}async function v(){const e=document.getElementById("builtinProvidersList");if(!e)return;let t={};try{t=(await n("/api/providers/builtin")).keys||{}}catch{}const o=new Set(d.map(e=>e.id));let a=d.map(e=>{const n=!!t[e.id],o="ollama"===e.id,a="openai-local"===e.id,s=n||o||a?`<span style="font-size:11px;padding:2px 8px;border-radius:999px;font-weight:600;background:rgba(52,211,153,0.15);color:var(--green);border:1px solid rgba(52,211,153,0.3);">${!o&&!a||n?"set ✓":"local"}</span>`:'<span style="font-size:11px;padding:2px 8px;border-radius:999px;font-weight:600;background:rgba(107,114,128,0.12);color:var(--text-2);border:1px solid var(--border);">no key</span>';return`<div class="card" style="margin-bottom:8px;">\n <div style="display:flex;align-items:center;gap:10px;cursor:pointer;" data-toggle-child=".bp-body">\n <span style="font-size:18px;width:24px;text-align:center;">${e.icon}</span>\n <div style="flex:1;">\n <div style="font-weight:600;font-size:13px;">${e.label}</div>\n <div style="font-size:11px;color:var(--text-2);">${e.hint}</div>\n </div>\n ${s}\n <span style="color:var(--text-2);font-size:12px;">▾</span>\n </div>\n <div class="bp-body" style="display:none;margin-top:12px;padding-top:12px;border-top:1px solid var(--border);">\n ${o?'<div style="font-size:12px;color:var(--text-2);margin-bottom:8px;">Ollama runs locally — no API key required. Make sure Ollama is running on port 11434.</div>':""}\n <div style="display:flex;gap:8px;flex-wrap:wrap;">\n ${o?"":`<input id="bp_${e.id}" type="password" autocomplete="new-password" placeholder="${n?"••••••••••••••• (saved — paste to update)":"Paste API key"}" style="flex:1;min-width:180px;" />`}\n ${o?`<button data-action="testBuiltinProvider" data-arg="${e.id}" class="btn-ghost">Test Connection</button>\n <button data-action="fetchBuiltinModels" data-arg="${e.id}" data-self="1" class="btn-ghost" style="background:#0f766e20;color:var(--green);border-color:#0f766e40;">↻ Models</button>`:`<button data-action="saveBuiltinKey" data-arg="${e.id}" class="btn-purple">Save</button>\n <button data-action="testBuiltinProvider" data-arg="${e.id}" class="btn-ghost">Test</button>\n <button data-action="fetchBuiltinModels" data-arg="${e.id}" data-self="1" class="btn-ghost" style="background:#0f766e20;color:var(--green);border-color:#0f766e40;">↻ Models</button>\n <a href="${e.url}" target="_blank" class="btn-ghost" style="text-decoration:none;font-size:12px;">Keys ↗</a>`}\n </div>\n <div id="bp_status_${e.id}" style="font-size:12px;margin-top:8px;color:var(--text-2);"></div>\n <div id="bp_models_${e.id}" style="margin-top:8px;display:none;">\n <span style="font-size:11px;color:var(--text-2);display:block;margin-bottom:4px;">Models (<span id="bp_mcount_${e.id}">0</span>):</span>\n <span id="bp_mtags_${e.id}"></span>\n </div>\n </div>\n </div>`}).join("");try{const e=((await n("/api/providers")).providers||[]).filter(e=>!o.has(e.id)&&"greptile"!==e.id);e.length&&(a+='<div style="font-size:11px;font-weight:600;color:var(--text-2);text-transform:uppercase;letter-spacing:0.08em;margin:14px 0 8px;padding:0 2px;">Custom Providers</div>',a+=e.map(e=>{var t;const n=c[e.id]||"🔌",o=e.hasKey,a=o?'<span style="font-size:11px;padding:2px 8px;border-radius:999px;font-weight:600;background:rgba(52,211,153,0.15);color:var(--green);border:1px solid rgba(52,211,153,0.3);">key set ✓</span>':'<span style="font-size:11px;padding:2px 8px;border-radius:999px;font-weight:600;background:rgba(107,114,128,0.12);color:var(--text-2);border:1px solid var(--border);">no key</span>',s=(null==(t=e.models)?void 0:t.length)||0;return`<div class="card" style="margin-bottom:8px;">\n <div style="display:flex;align-items:center;gap:10px;cursor:pointer;" data-toggle-child=".cp-body">\n <span style="font-size:18px;width:24px;text-align:center;">${n}</span>\n <div style="flex:1;">\n <div style="font-weight:600;font-size:13px;">${e.id}</div>\n <div style="font-size:11px;color:var(--text-2);">${e.baseUrl}${s?" · "+s+" models":""}</div>\n </div>\n ${a}\n <span style="color:var(--text-2);font-size:12px;">▾</span>\n </div>\n <div class="cp-body" style="display:none;margin-top:12px;padding-top:12px;border-top:1px solid var(--border);">\n <div style="display:flex;gap:8px;flex-wrap:wrap;">\n <input id="key_${e.id}" type="password" autocomplete="new-password" placeholder="${o?"••••••••••••••• (saved — paste to update)":"Paste API key"}" style="flex:1;min-width:180px;" />\n <button data-action="saveKey" data-arg="${e.id}" class="btn-purple">Save</button>\n <button data-action="testKey" data-arg="${e.id}" class="btn-ghost">Test</button>\n <button data-action="fetchModels" data-arg="${e.id}" data-self="1" class="btn-ghost" style="background:#0f766e20;color:var(--green);border-color:#0f766e40;">↻ Models</button>\n </div>\n <div style="font-size:11px;color:var(--text-2);margin-top:6px;">Base URL: <code style="font-size:10px;">${e.baseUrl}</code></div>\n <div id="test_${e.id}" style="font-size:12px;margin-top:8px;color:var(--text-2);"></div>\n <div id="mwrap_${e.id}" style="margin-top:8px;${s?"":"display:none;"}">\n <span style="font-size:11px;color:var(--text-2);">Models (<span id="mcount_${e.id}">${s}</span>):</span>\n <span id="mtags_${e.id}">${(e.models||[]).map(e=>'<span class="model-tag">'+(e.id||e)+"</span>").join("")}</span>\n </div>\n </div>\n </div>`}).join(""))}catch{}e.innerHTML=a}async function b(n){var o;const a=document.getElementById("bp_"+n),s=null==(o=null==a?void 0:a.value)?void 0:o.trim();if(s||"openai-local"===n){await t("/api/providers/builtin/save",{providerId:n,apiKey:s||""}),a&&(a.value=""),e("Key saved — fetching models…"),await v();try{const o=await t("/api/providers/fetch-models",{providerId:n});if(o.ok){const t=document.getElementById("bp_mtags_"+n),a=document.getElementById("bp_mcount_"+n),s=document.getElementById("bp_models_"+n),i=document.getElementById("bp_status_"+n);t&&(t.innerHTML=o.models.map(e=>'<span class="model-tag">'+e+"</span>").join("")),a&&(a.textContent=o.models.length),s&&(s.style.display="block"),i&&(i.style.color="var(--green)",i.textContent="✓ "+o.models.length+" models"),e("Key saved for "+n+" — "+o.models.length+" models ready"),r()}else e("Key saved — could not fetch models: "+(o.error||"unknown"),"warning")}catch(i){e("Key saved — model fetch failed: "+i.message,"warning")}}else e("Paste an API key first","error")}async function x(e){const n=document.getElementById("bp_status_"+e);n.textContent="Testing…";try{const o=await t("/api/providers/builtin/test",{providerId:e});n.style.color=o.ok?"var(--green)":"var(--red)",n.textContent=o.ok?"✓ Connected — "+(o.model||"OK"):"✗ "+(o.error||"Failed")}catch(o){n.style.color="var(--red)",n.textContent="✗ "+o.message}}async function h(e,n){const o=document.getElementById("bp_status_"+e),a=n.textContent;n.textContent="Fetching…",n.disabled=!0,o.textContent="";try{const n=await t("/api/providers/fetch-models",{providerId:e});if(n.ok){const t=document.getElementById("bp_mtags_"+e),a=document.getElementById("bp_mcount_"+e),s=document.getElementById("bp_models_"+e);t&&(t.innerHTML=n.models.map(e=>'<span class="model-tag">'+e+"</span>").join("")),a&&(a.textContent=n.models.length),s&&(s.style.display="block"),o.style.color="var(--green)",o.textContent="✓ "+n.models.length+" models fetched"+(n.note?" — "+n.note:""),r()}else o.style.color="var(--red)",o.textContent="✗ "+(n.error||"Failed")}catch(s){o.style.color="var(--red)",o.textContent="✗ "+s.message}finally{n.textContent=a,n.disabled=!1}}function f(e,t){const n=document.getElementById(e);n.type="password"===n.type?"text":"password",t.textContent="password"===n.type?"👁":"🙈"}async function k(s){const i=document.getElementById("key_"+s).value.trim();if(i)try{await t("/api/providers/save",{providerId:s,apiKey:i}),e("Saved key for "+s),async function(){const e=document.getElementById("providersList");if(e){o(e,"Loading providers...");try{const t=(await n("/api/providers")).providers||[];if(!t.length)return void showEmpty(e,"No providers found. Check ~/.crewswarm/crewswarm.json");e.innerHTML="",t.forEach(t=>{const n=c[t.id]||"🔌",o=t.hasKey,a=o?"#10b981":"var(--red-hi)",s=o?"✓ key set":"✗ no key",i=document.createElement("div");i.className="provider-card",i.innerHTML=`\n <div class="provider-header" data-toggle-sibling="open">\n <span style="font-size:20px;">${n}</span>\n <div style="flex:1;">\n <strong style="font-size:15px;">${t.id}</strong>\n <span class="meta" style="margin-left:10px;">${t.baseUrl}</span>\n </div>\n <span class="provider-badge" style="background:${a}20; color:${a}; border:1px solid ${a}40;">${s}</span>\n <span class="meta" style="margin-left:12px;">${t.models.length} model${1!==t.models.length?"s":""}</span>\n <span style="color:#64748b; margin-left:8px;">▼</span>\n </div>\n <div class="provider-body">\n <div class="key-row">\n <input class="key-input" type="password" autocomplete="new-password" id="key_${t.id}" value="${t.maskedKey||""}" placeholder="Paste API key…" />\n <button data-action="toggleKeyVis" data-arg="key_${t.id}" data-self="1" style="background:#334155; padding:6px 10px; font-size:12px;">👁</button>\n <button data-action="saveKey" data-arg="${t.id}" style="background:#6366f1; padding:6px 14px; font-size:12px;">Save</button>\n <button data-action="testKey" data-arg="${t.id}" style="background:#334155; padding:6px 10px; font-size:12px;">Test</button>\n <button data-action="fetchModels" data-arg="${t.id}" data-self="1" style="background:#0f766e; padding:6px 10px; font-size:12px;">↻ Fetch models</button>\n <span id="test_${t.id}"></span>\n </div>\n <div style="margin-bottom:8px;"><span class="meta">Base URL: </span><code style="font-size:11px; color:#94a3b8;">${t.baseUrl}</code></div>\n <div><span class="meta" style="display:block; margin-bottom:6px;">Models (<span id="mcount_${t.id}">${t.models.length}</span>):</span><span id="mtags_${t.id}">${t.models.map(e=>'<span class="model-tag">'+e.id+"</span>").join("")}</span></div>\n ${0===t.models.length?`<div class="meta" style="margin-top:8px; color:var(--amber);" id="mnone_${t.id}">No models yet — click ↻ Fetch models</div>`:""}\n </div>\n `,e.appendChild(i)})}catch(t){a(e,"Error: "+t.message)}}}(),r()}catch(l){e("Failed: "+l.message,!0)}else e("Key is empty",!0)}async function w(e){const n=document.getElementById("test_"+e);n.textContent="testing…",n.className="meta";try{const o=await t("/api/providers/test",{providerId:e});n.textContent=o.ok?"✓ "+(o.model||"ok"):"✗ "+o.error,n.className=o.ok?"test-ok":"test-err"}catch(o){n.textContent="✗ "+o.message,n.className="test-err"}}async function I(e,n){const o=document.getElementById("test_"+e),a=n.textContent;n.textContent="Fetching…",n.disabled=!0,o&&(o.textContent="");try{const n=await t("/api/providers/fetch-models",{providerId:e});if(n.ok){const t=document.getElementById("mtags_"+e),a=document.getElementById("mcount_"+e),s=document.getElementById("mnone_"+e),i=document.getElementById("mwrap_"+e);t&&(t.innerHTML=n.models.map(e=>'<span class="model-tag">'+e+"</span>").join("")),a&&(a.textContent=n.models.length),s&&(s.style.display="none"),i&&(i.style.display="block"),o&&(o.textContent="✓ "+n.models.length+" models",o.className="test-ok"),r()}else o&&(o.textContent="✗ "+n.error,o.className="test-err")}catch(s){o&&(o.textContent="✗ "+s.message,o.className="test-err")}finally{n.textContent=a,n.disabled=!1}}function $(){const n=document.getElementById("addProviderBtn");n&&(n.onclick=()=>{const e=document.getElementById("addProviderForm");e.style.display="block",setTimeout(()=>e.scrollIntoView({behavior:"smooth",block:"start"}),50);const t=e.querySelector("input");t&&setTimeout(()=>t.focus(),150)});const o=document.getElementById("apCancelBtn");o&&(o.onclick=()=>{document.getElementById("addProviderForm").style.display="none"});const a=document.getElementById("apSaveBtn");a&&(a.onclick=async()=>{const n=document.getElementById("apId").value.trim(),o=document.getElementById("apBaseUrl").value.trim(),a=document.getElementById("apKey").value.trim(),s=document.getElementById("apApi").value;if(n&&o)try{await t("/api/providers/add",{id:n,baseUrl:o,apiKey:a,api:s}),e("Provider added: "+n),document.getElementById("addProviderForm").style.display="none",v()}catch(i){e("Failed: "+i.message,!0)}else e("ID and Base URL are required",!0)});const s=document.getElementById("refreshProvidersBtn");s&&(s.onclick=v)}export{u as a,w as b,x as c,y as d,k as e,b as f,I as g,h,$ as i,l as j,m as s,f as t};
|
|
Binary file
|
package/apps/dashboard/dist/assets/{tab-pm-loop-tab-D7mnDelU.js → tab-pm-loop-tab-Bfd449B4.js}
RENAMED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{s as e}from"./core-utils-
|
|
1
|
+
import{s as e}from"./core-utils-CmOkXgzi.js";import{l as t,g as n}from"./tab-projects-tab-DhNWnlzt.js";let o=null;function d(){const e=document.getElementById("buildProjectPicker");return e?e.value:""}async function l(){try{const e=d(),t=e?"?projectId="+encodeURIComponent(e):"",n=await fetch("/api/pm-loop/status"+t).then(e=>e.json()),l=document.getElementById("pmLoopBadge"),i=document.getElementById("pmStartBtn"),p=document.getElementById("pmDryRunBtn"),s=document.getElementById("pmLiveLog");n.running?(l.textContent="running (pid "+n.pid+")",l.classList.add("running"),i.disabled=!0,p.disabled=!0,s.style.display="block",o||a()):l.textContent.startsWith("running")&&(l.textContent="idle",l.classList.remove("running"),i.disabled=!1,p.disabled=!1)}catch(e){}}function a(){o||(o=setInterval(async()=>{try{const e=await fetch("/api/pm-loop/log").then(e=>e.json()),t=document.getElementById("pmLiveLog"),n=document.getElementById("pmLoopBadge"),d=document.getElementById("pmStartBtn"),l=document.getElementById("pmDryRunBtn");if(e.lines&&e.lines.length){t.textContent=e.lines.map(e=>{if("finish"===e.event)return`🏁 Done ✓${e.done} ✗${e.failed} ⏳${e.pending}`;if("stopped_by_file"===e.event)return"⛔ Stopped by user";if("all_done"===e.event)return`🏁 All ${e.total} items complete!`;return`${"done"===e.status?"✅":"failed"===e.status?"❌":(e.event,"·")} ${e.item?`${e.item.substring(0,60)}`:e.event||""}`}).join("\n"),t.scrollTop=t.scrollHeight;const a=e.lines[e.lines.length-1];!a||"finish"!==a.event&&"all_done"!==a.event&&"stopped_by_file"!==a.event||(clearInterval(o),o=null,n.textContent="all_done"===a.event?"✓ complete":"idle",n.classList.remove("running"),d.disabled=!1,l.disabled=!1)}}catch(e){}},5e3))}async function i(t=!1){var o,l,i,p,s,m,c,r,u,g;const y=d(),I=document.getElementById("pmLoopBadge");document.getElementById("pmStatus");const B=document.getElementById("pmLiveLog"),f=document.getElementById("pmStartBtn"),h=document.getElementById("pmDryRunBtn"),v=n(y);if(y)try{I.textContent=t?"dry run...":"starting...",I.classList.add("running"),f.disabled=!0,h.disabled=!0,B.style.display="block",B.textContent="⚙ Starting PM Loop for "+(v?v.name:y)+(t?" (dry run)":"")+"...\n";const n=await fetch("/api/pm-loop/start",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({dryRun:t,projectId:y,pmOptions:{useQA:(null==(o=document.getElementById("pmOptQA"))?void 0:o.checked)??!0,useSecurity:(null==(l=document.getElementById("pmOptSecurity"))?void 0:l.checked)??!0,useSpecialists:(null==(i=document.getElementById("pmOptSpecialists"))?void 0:i.checked)??!0,selfExtend:(null==(p=document.getElementById("pmOptSelfExtend"))?void 0:p.checked)??!0,maxItems:parseInt((null==(s=document.getElementById("pmOptMaxItems"))?void 0:s.value)||"200"),taskTimeoutMin:parseInt((null==(m=document.getElementById("pmOptTimeout"))?void 0:m.value)||"10"),extendEveryN:parseInt((null==(c=document.getElementById("pmOptExtendN"))?void 0:c.value)||"5"),pauseSec:parseInt((null==(r=document.getElementById("pmOptPause"))?void 0:r.value)||"5"),maxRetries:parseInt((null==(u=document.getElementById("pmOptMaxRetries"))?void 0:u.value)||"2"),coderAgent:(null==(g=document.getElementById("pmOptCoder"))?void 0:g.value.trim())||"crew-coder"}})}),d=await n.json();if(409===n.status||d.alreadyRunning)return B.textContent="⚠ Already running (pid "+d.pid+"). Watch the log below.\n",I.textContent="running (pid "+d.pid+")",e("PM Loop already running for this project (pid "+d.pid+")",!0),void a();B.textContent+="✅ Spawned (pid "+d.pid+"). PM is reading roadmap...\n",I.textContent="running (pid "+d.pid+")",e("PM Loop started"+(t?" (dry run)":"")+" for "+(v?v.name:y)),a()}catch(E){e("PM Loop failed: "+E.message,!0),I.textContent="idle",I.classList.remove("running"),f.disabled=!1,h.disabled=!1}else e("Select a project first from the Project picker above",!0)}async function p(){const t=d();try{await fetch("/api/pm-loop/stop",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({projectId:t})}),e("Stop signal sent — PM will finish current task then halt."),document.getElementById("pmLoopBadge").textContent="stopping..."}catch(n){e("Stop failed: "+n.message,!0)}}async function s(){const e=document.getElementById("pmRoadmapPanel");if("none"===e.style.display)try{await t();const o=d(),l=n(o);console.log("[toggleRoadmap] projectId:",o,"proj:",l);let a="";if(l&&l.roadmapFile){console.log("[toggleRoadmap] Fetching project roadmap:",l.roadmapFile);a=(await fetch("/api/file-content?path="+encodeURIComponent(l.roadmapFile)).then(e=>e.json())).content||"(empty)"}else{console.log("[toggleRoadmap] Using default roadmap (no project selected or no roadmapFile)");a=(await fetch("/api/pm-loop/roadmap").then(e=>e.json())).content||"(empty)"}e.textContent=a,e.style.display="block"}catch(o){console.error("[toggleRoadmap] Error:",o),e.textContent="Could not load roadmap: "+o.message,e.style.display="block"}else e.style.display="none"}function m(){document.getElementById("pmStartBtn").onclick=()=>i(!1),document.getElementById("pmDryRunBtn").onclick=()=>i(!0),document.getElementById("pmStopBtn").onclick=p,document.getElementById("pmRoadmapBtn").onclick=s,t().then(()=>l()),document.getElementById("buildProjectPicker").addEventListener("change",()=>{o&&(clearInterval(o),o=null),l()})}export{m as i};
|
|
Binary file
|