crewswarm 0.9.3 → 0.9.4
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/apps/dashboard/dist/assets/chat-core-uXb_C0GM.js +1 -0
- package/apps/dashboard/dist/assets/chat-core-uXb_C0GM.js.br +0 -0
- package/apps/dashboard/dist/assets/cli-process-CNZ_UBCt.js +1 -0
- package/apps/dashboard/dist/assets/cli-process-CNZ_UBCt.js.br +0 -0
- package/apps/dashboard/dist/assets/components-BS9fQjE_.js.br +0 -0
- package/apps/dashboard/dist/assets/core-utils-CmOkXgzi.js.br +0 -0
- package/apps/dashboard/dist/assets/index-BeVllEj_.js +2 -0
- package/apps/dashboard/dist/assets/index-BeVllEj_.js.br +0 -0
- package/apps/dashboard/dist/assets/{index-CF0aJRtC.css → index-D-sRshvg.css} +1 -1
- package/apps/dashboard/dist/assets/index-D-sRshvg.css.br +0 -0
- package/apps/dashboard/dist/assets/orchestration-Ca2DLWN-.js.br +0 -0
- package/apps/dashboard/dist/assets/setup-wizard-CA0Or47w.js.br +0 -0
- package/apps/dashboard/dist/assets/tab-agents-tab-BgpIsjkw.js.br +0 -0
- package/apps/dashboard/dist/assets/tab-benchmarks-tab-BHjKCPm3.js.br +0 -0
- package/apps/dashboard/dist/assets/tab-comms-tab-kguqTIzD.js.br +0 -0
- package/apps/dashboard/dist/assets/tab-contacts-tab-DiOyMYth.js.br +0 -0
- package/apps/dashboard/dist/assets/tab-engines-tab-BsdZVvU0.js.br +0 -0
- package/apps/dashboard/dist/assets/tab-memory-tab-Cu6u13EQ.js.br +0 -0
- package/apps/dashboard/dist/assets/tab-models-tab-dNRgsTOO.js +1 -0
- package/apps/dashboard/dist/assets/tab-models-tab-dNRgsTOO.js.br +0 -0
- package/apps/dashboard/dist/assets/tab-pm-loop-tab-DiAPTJXu.js.br +0 -0
- package/apps/dashboard/dist/assets/tab-projects-tab-SFH4E--a.js.br +0 -0
- package/apps/dashboard/dist/assets/tab-prompts-tab-DVkUNaJd.js.br +0 -0
- package/apps/dashboard/dist/assets/tab-services-tab-DU_LH3uG.js.br +0 -0
- package/apps/dashboard/dist/assets/tab-settings-tab-CuvH_Fj_.js +1 -0
- package/apps/dashboard/dist/assets/tab-settings-tab-CuvH_Fj_.js.br +0 -0
- package/apps/dashboard/dist/assets/tab-skills-tab-DR7PJ7NB.js +1 -0
- package/apps/dashboard/dist/assets/tab-skills-tab-DR7PJ7NB.js.br +0 -0
- package/apps/dashboard/dist/assets/tab-spending-tab-DEccQHnt.js.br +0 -0
- package/apps/dashboard/dist/assets/tab-swarm-chat-tab-BNrd88-r.js.br +0 -0
- package/apps/dashboard/dist/assets/tab-swarm-tab-B1AcjL1W.js.br +0 -0
- package/apps/dashboard/dist/assets/tab-testing-tab-CezZOZcJ.js +1 -0
- package/apps/dashboard/dist/assets/tab-testing-tab-CezZOZcJ.js.br +0 -0
- 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-B-soSy1k.js.br +0 -0
- package/apps/dashboard/dist/index.html +56 -7
- package/apps/dashboard/dist/index.html.br +0 -0
- package/apps/dashboard/dist/index.html.gz +0 -0
- package/apps/vibe/server.mjs +98 -53
- package/lib/bridges/cli-executor.mjs +1 -1
- package/lib/browser/passthrough-stderr.js +1 -0
- package/lib/chat/project-messages.mjs +3 -5
- package/lib/cli-process-tracker.mjs +3 -2
- package/lib/contacts/identity-linker.mjs +1 -0
- package/lib/crew-judge/judge.mjs +19 -18
- package/lib/crew-lead/agent-manager.mjs +1 -1
- package/lib/crew-lead/background.mjs +14 -1
- package/lib/crew-lead/chat-handler.mjs +4 -1
- package/lib/crew-lead/http-server.mjs +73 -65
- package/lib/crew-lead/prompts.mjs +7 -1
- package/lib/crew-lead/tools.mjs +3 -2
- package/lib/crew-lead/wave-dispatcher.mjs +4 -2
- package/lib/engines/crew-cli.mjs +1 -1
- package/lib/engines/engine-registry.mjs +11 -9
- package/lib/engines/runners.mjs +23 -2
- package/lib/gemini-cli-passthrough-noise.mjs +1 -1
- package/lib/integrations/code-search.mjs +4 -3
- package/lib/memory/shared-adapter.mjs +23 -10
- package/lib/pipeline/manager.mjs +2 -1
- package/lib/runtime/config.mjs +1 -1
- package/lib/runtime/spending.mjs +2 -1
- package/package.json +17 -9
- package/scripts/dashboard-validation.mjs +2 -0
- package/scripts/dashboard.mjs +1110 -484
- package/scripts/generate-openapi.mjs +683 -277
- package/scripts/restart-service.sh +12 -9
- package/apps/dashboard/dist/assets/chat-core-3KirthZA.js +0 -1
- package/apps/dashboard/dist/assets/cli-process-COMRNPqr.js +0 -1
- package/apps/dashboard/dist/assets/index-GSWxxEPO.js +0 -2
- package/apps/dashboard/dist/assets/tab-models-tab-BLEjmd19.js +0 -1
- package/apps/dashboard/dist/assets/tab-settings-tab-BselH1c0.js +0 -1
- package/apps/dashboard/dist/assets/tab-skills-tab-BpY0uZHW.js +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{p as e,s as t,g as o,a as n,e as a}from"./core-utils-CmOkXgzi.js";import{s as r,u as s}from"./tab-projects-tab-SFH4E--a.js";let l=null,i=null;function c({getModels:e,populateModelDropdown:t}={}){l=e||l,i=t||i}async function d(){const n=document.getElementById("rtTokenInput").value.trim();if(n)try{await e("/api/settings/rt-token",{token:n}),t("RT Bus token saved"),document.getElementById("rtTokenInput").value="",async function(){try{const e=await o("/api/settings/rt-token"),t=document.getElementById("rtTokenBadge"),n=document.getElementById("rtTokenInput");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)",n.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{}}()}catch(a){t("Save failed: "+a.message,"error")}else t("Paste a token first","error")}async function u(){const e=document.getElementById("configLockBadge"),t=document.getElementById("configLockStatus"),n=document.querySelector('[data-action="lockConfig"]'),a=document.querySelector('[data-action="unlockConfig"]');try{(await o("/api/config/lock-status")).locked?(e.textContent="🔒 Locked",e.style.background="rgba(52,211,153,0.15)",e.style.color="var(--green)",e.style.borderColor="rgba(52,211,153,0.3)",t&&(t.textContent="✓ Config is protected from overwrites"),n&&(n.className="btn-primary",n.style.opacity="0.6",n.style.pointerEvents="none"),a&&(a.className="btn-ghost",a.style.opacity="1",a.style.pointerEvents="auto")):(e.textContent="🔓 Unlocked",e.style.background="rgba(251,191,36,0.15)",e.style.color="var(--yellow)",e.style.borderColor="rgba(251,191,36,0.3)",t&&(t.textContent="⚠️ Config can be modified — lock it after making changes"),n&&(n.className="btn-primary",n.style.opacity="1",n.style.pointerEvents="auto"),a&&(a.className="btn-ghost",a.style.opacity="0.6",a.style.pointerEvents="none"))}catch{e&&(e.textContent="? unknown")}}async function g(){try{await e("/api/config/lock",{}),t("✓ Config locked — protected from overwrites"),u()}catch(o){t("Lock failed: "+o.message,"error")}}async function m(){try{await e("/api/config/unlock",{}),t("✓ Config unlocked — you can now make changes"),u()}catch(o){t("Unlock failed: "+o.message,"error")}}async function y(){try{const e=await o("/api/settings/opencode-project"),t=document.getElementById("opencodeProjInput"),n=document.getElementById("opencodeProjStatus");t&&(t.placeholder=e.dir||"e.g. /Users/you/Desktop/myproject",t.value=e.dir||""),n&&(n.textContent=e.dir?"✅ Current: "+e.dir:"⚠️ Not set — OpenCode will write files to the crewswarm repo root. Set this to your project folder."),document.getElementById("opencodeFallbackSelect")&&l&&(await l(),i&&i("opencodeFallbackSelect",e.fallbackModel||""));const a=document.getElementById("opencodeFallbackStatus");a&&(a.textContent=e.fallbackModel?"✅ Fallback: "+e.fallbackModel:"⚠️ Using default groq/kimi-k2-instruct-0905"),document.getElementById("opencodeModelSelect")&&l&&(await l(),i&&i("opencodeModelSelect",e.opencodeModel||""));const r=document.getElementById("opencodeModelStatus");r&&(r.textContent=e.opencodeModel?"✅ Primary: "+e.opencodeModel:"⚠️ Using default groq/moonshotai/kimi-k2-instruct-0905");const s=document.getElementById("crewLeadModelSelect");s&&e.crewLeadModel&&(s.value=e.crewLeadModel)}catch{}}async function f(){var o,a;const l=((null==(o=document.getElementById("opencodeProjInput"))?void 0:o.value)||"").trim(),i=((null==(a=document.getElementById("opencodeFallbackSelect"))?void 0:a.value)||"").trim();try{if(await e("/api/settings/opencode-project",{dir:l||void 0,fallbackModel:i||void 0}),t("OpenCode settings saved — fallback takes effect on next task (no restart needed)"),y(),l&&n.projectsData){const e=Object.values(n.projectsData).find(e=>e.outputDir===l);if(e){n.chatActiveProjectId=e.id,r(e.id);const t=document.getElementById("chatProjectSelect");t&&(t.value=e.id),s()}}}catch(c){t("Save failed: "+c.message,"error")}}async function p(){const o=document.getElementById("opencodeModelSelect"),n=((null==o?void 0:o.value)||"").trim(),a=document.getElementById("opencodeModelStatus");try{await e("/api/settings/opencode-project",{opencodeModel:n||void 0}),a&&(a.textContent="✓ Saved",a.style.color="var(--green-hi)"),t(n?`Primary OpenCode model → ${n}`:"OpenCode model reset to default"),setTimeout(()=>{a&&(a.textContent=n?"✅ Primary: "+n:"⚠️ Using default groq/moonshotai/kimi-k2-instruct-0905")},3e3)}catch(r){a&&(a.textContent="Error: "+r.message,a.style.color="var(--red)"),t("Save failed: "+r.message,"error")}}async function v(){const e=document.getElementById("bgConsciousnessBtn"),t=document.getElementById("bgConsciousnessStatus"),n=document.getElementById("bgConsciousnessModel");try{const a=await o("/api/settings/bg-consciousness");if(!1===a.ok)return e&&(e.textContent="⚫ OFF"),void(t&&(t.textContent="⚠️ Could not reach crew-lead — restart services.",t.style.color="var(--amber)"));const r=a.enabled;e&&(e.textContent=r?"🟢 ON":"⚫ OFF",e.style.background=r?"rgba(34,197,94,0.15)":"var(--surface-2)",e.style.borderColor=r?"var(--green-hi)":"var(--border)",e.style.color=r?"var(--green-hi)":"var(--text-2)"),n&&a.model&&(n.placeholder=a.model),t&&(t.textContent=r?"Active — crew-lead reflects every "+Math.round(a.intervalMs/6e4)+"min when idle. Model: "+a.model:"Off — crew-lead will not self-reflect between tasks.")}catch(a){e&&(e.textContent="Error"),t&&(t.textContent="Could not load: "+a.message)}}async function C(){try{const n=await o("/api/settings/bg-consciousness");if(!1===n.ok)return void t("Cannot reach crew-lead — restart services first","error");const a=await e("/api/settings/bg-consciousness",{enabled:!n.enabled});t("Background consciousness "+(a.enabled?"ENABLED":"DISABLED")),v()}catch(n){t("Failed: "+n.message,"error")}}async function b(){const o=document.getElementById("bgConsciousnessModel"),n=((null==o?void 0:o.value)||"").trim();if(n)try{await e("/api/settings/bg-consciousness",{model:n}),t("Background consciousness model → "+n),o.value="",v()}catch(a){t("Failed: "+a.message,"error")}else t("Enter a model first (e.g. groq/llama-3.3-70b-versatile)","error")}async function h(){const e=document.getElementById("cursorWavesBtn"),t=document.getElementById("cursorWavesStatus");try{const n=await o("/api/settings/cursor-waves");if(!1===n.ok)return e&&(e.textContent="⚫ OFF"),void(t&&(t.textContent="⚠️ Could not reach crew-lead — restart services.",t.style.color="var(--amber)"));const a=n.enabled;e&&(e.textContent=a?"⚡ ON":"⚫ OFF",e.style.background=a?"rgba(168,85,247,0.15)":"var(--surface-2)",e.style.borderColor=a?"#a855f7":"var(--border)",e.style.color=a?"#c084fc":"var(--text-2)"),t&&(t.textContent=a?"Active — multi-agent waves fan out to Cursor subagents in parallel. crew-orchestrator coordinates each wave.":"Off — each agent in a wave dispatches independently through the standard gateway.")}catch(n){e&&(e.textContent="Error"),t&&(t.textContent="Could not load: "+n.message)}}async function E(){try{const n=await o("/api/settings/cursor-waves");if(!1===n.ok)return void t("Cannot reach crew-lead — restart services first","error");const a=await e("/api/settings/cursor-waves",{enabled:!n.enabled});t("Cursor Parallel Waves "+(a.enabled?"ENABLED ⚡":"DISABLED")),h()}catch(n){t("Failed: "+n.message,"error")}}async function k(){const e=document.getElementById("tmuxBridgeBtn"),t=document.getElementById("tmuxBridgeStatus");try{const n=await o("/api/settings/tmux-bridge");if(!1===n.ok)return e&&(e.textContent="⚫ OFF"),void(t&&(t.textContent="⚠️ Could not reach crew-lead — restart services.",t.style.color="var(--amber)"));const a=n.enabled;e&&(e.textContent=a?"🔌 ON":"⚫ OFF",e.style.background=a?"rgba(52,211,153,0.15)":"var(--surface-2)",e.style.borderColor=a?"rgba(52,211,153,0.3)":"var(--border)",e.style.color=a?"var(--green)":"var(--text-2)"),t&&(t.textContent=a?"Active — agents can share persistent tmux sessions across pipeline waves. Requires tmux + smux.":"Off — agents use standard cold-start execution (no session persistence).")}catch(n){e&&(e.textContent="Error"),t&&(t.textContent="Could not load: "+n.message)}}async function x(){try{const n=await o("/api/settings/tmux-bridge");if(!1===n.ok)return void t("Cannot reach crew-lead — restart services first","error");const a=await e("/api/settings/tmux-bridge",{enabled:!n.enabled});t("tmux-bridge "+(a.enabled?"ENABLED 🔌":"DISABLED")),k()}catch(n){t("Failed: "+n.message,"error")}}async function w(){const e=document.getElementById("autonomousMentionsBtn"),t=document.getElementById("autonomousMentionsStatus");try{const n=await o("/api/settings/autonomous-mentions");if(!1===n.ok)return e&&(e.textContent="⚫ OFF"),void(t&&(t.textContent="⚠️ Could not reach crew-lead — restart services.",t.style.color="var(--amber)"));const a=!1!==n.enabled;e&&(e.textContent=a?"🕸 ON":"⚫ OFF",e.style.background=a?"rgba(52,211,153,0.15)":"var(--surface-2)",e.style.borderColor=a?"rgba(52,211,153,0.3)":"var(--border)",e.style.color=a?"var(--green)":"var(--text-2)"),t&&(t.textContent=a?"Active — shared chat @mentions can auto-route to agents and CLI participants.":"Off — @mentions are recorded in chat history, but no autonomous routing will fire.",t.style.color="var(--text-3)")}catch(n){e&&(e.textContent="Error"),t&&(t.textContent="Could not load: "+n.message)}}async function I(){try{const n=await o("/api/settings/autonomous-mentions");if(!1===n.ok)return void t("Cannot reach crew-lead — restart services first","error");const a=await e("/api/settings/autonomous-mentions",{enabled:!n.enabled});t("Autonomous mention routing "+(a.enabled?"ENABLED 🕸":"DISABLED")),w()}catch(n){t("Failed: "+n.message,"error")}}async function M(){const e=document.getElementById("claudeCodeBtn"),t=document.getElementById("claudeCodeStatus");try{const n=await o("/api/settings/claude-code");if(!1===n.ok)return e&&(e.textContent="⚫ OFF"),void(t&&(t.textContent="⚠️ Could not reach crew-lead — restart services or check that crew-lead is running.",t.style.color="var(--amber)"));const a=n.enabled;e&&(e.textContent=a?"🤖 ON":"⚫ OFF",e.style.background=a?"rgba(245,158,11,0.15)":"var(--surface-2)",e.style.borderColor=a?"var(--amber)":"var(--border)",e.style.color=a?"var(--yellow)":"var(--text-2)"),t&&(n.hasKey?(t.textContent=a?"Active — tasks route through Claude Code CLI. Per-agent override: set useClaudeCode: true in crewswarm.json.":"Off — tasks use direct LLM or OpenCode. Enable to run agents through Claude Code CLI.",t.style.color="var(--text-3)"):(t.textContent='⚠️ No Claude auth found — run "claude" in terminal to authenticate via OAuth, or set ANTHROPIC_API_KEY.',t.style.color="var(--amber)"))}catch(n){e&&(e.textContent="Error"),t&&(t.textContent="Could not load: "+n.message)}}async function S(){try{const n=await o("/api/settings/claude-code");if(!1===n.ok)return void t("Cannot reach crew-lead — restart services first","error");if(!n.hasKey)return void t('No Claude auth found — run "claude" in terminal to authenticate via OAuth, or set ANTHROPIC_API_KEY',"error");const a=await e("/api/settings/claude-code",{enabled:!n.enabled});t("Claude Code executor "+(a.enabled?"ENABLED 🤖":"DISABLED")),M()}catch(n){t("Failed: "+n.message,"error")}}async function _(){const e=document.getElementById("codexBtn"),t=document.getElementById("codexStatus");try{const n=await o("/api/settings/codex");if(!1===n.ok)return e&&(e.textContent="⚫ OFF"),void(t&&(t.textContent="⚠️ Could not reach crew-lead — restart services.",t.style.color="var(--amber)"));const a=n.enabled;e&&(e.textContent=a?"🟣 ON":"⚫ OFF",e.style.background=a?"rgba(168,85,247,0.15)":"var(--surface-2)",e.style.borderColor=a?"#a855f7":"var(--border)",e.style.color=a?"#a855f7":"var(--text-2)"),t&&(t.textContent=a?"Active — tasks route through Codex CLI. Per-agent override: set useCodex: true in crewswarm.json.":"Off — tasks use direct LLM or other engine. Enable to route all coding agents through Codex CLI.",t.style.color="var(--text-3)")}catch(n){e&&(e.textContent="Error"),t&&(t.textContent="Could not load: "+n.message,t.style.color="var(--text-3)")}}async function O(){try{const n=await o("/api/settings/codex");if(!1===n.ok)return void t("Cannot reach crew-lead — restart services first","error");const a=await e("/api/settings/codex",{enabled:!n.enabled});t("Codex CLI executor "+(a.enabled?"ENABLED 🟣":"DISABLED")),_()}catch(n){t("Failed: "+n.message,"error")}}async function A(){const e=document.getElementById("geminiCliBtn"),t=document.getElementById("geminiCliStatus");try{const n=await o("/api/settings/gemini-cli");if(!1===n.ok)return e&&(e.textContent="⚫ OFF"),void(t&&(t.textContent="⚠️ Could not reach crew-lead — restart services.",t.style.color="var(--amber)"));const a=n.enabled;e&&(e.textContent=a?"🔵 ON":"⚫ OFF",e.style.background=a?"rgba(66,133,244,0.15)":"var(--surface-2)",e.style.borderColor=a?"#4285f4":"var(--border)",e.style.color=a?"#4285f4":"var(--text-2)"),t&&(n.installed?(t.textContent=a?"Active — tasks route through Gemini CLI. Run gemini auth login if you haven't authenticated yet.":"Off — tasks use direct LLM or other engine. Enable to route coding agents through Gemini CLI (free Google OAuth tier).",t.style.color="var(--text-3)"):(t.textContent="⚠️ gemini binary not found — run: npm install -g @google/gemini-cli",t.style.color="var(--amber)"))}catch(n){e&&(e.textContent="Error"),t&&(t.textContent="Could not load: "+n.message,t.style.color="var(--text-3)")}}async function R(){try{const n=await o("/api/settings/gemini-cli");if(!1===n.ok)return void t("Cannot reach crew-lead — restart services first","error");if(!n.installed)return void t("Install Gemini CLI first: npm install -g @google/gemini-cli","error");const a=await e("/api/settings/gemini-cli",{enabled:!n.enabled});t("Gemini CLI executor "+(a.enabled?"ENABLED 🔵":"DISABLED")),A()}catch(n){t("Failed: "+n.message,"error")}}async function B(){const e=document.getElementById("crewCliBtn"),t=document.getElementById("crewCliStatus");try{const n=await o("/api/settings/crew-cli");if(!1===n.ok)return e&&(e.textContent="⚫ OFF"),void(t&&(t.textContent="⚠️ Could not reach crew-lead — restart services.",t.style.color="var(--amber)"));const a=n.enabled;e&&(e.textContent=a?"🔧 ON":"⚫ OFF",e.style.background=a?"rgba(16,185,129,0.15)":"var(--surface-2)",e.style.borderColor=a?"#10b981":"var(--border)",e.style.color=a?"#10b981":"var(--text-2)"),t&&(t.textContent=a?"Active — multi-agent swarm tasks route through crew-cli with intelligent dispatch to specialists.":"Off — tasks use direct LLM or other engine. Enable to route all coding agents through crew-cli natively.")}catch(n){e&&(e.textContent="Error"),t&&(t.textContent="Could not load status")}}async function L(){try{const n=await o("/api/settings/crew-cli");if(!1===n.ok)return void t("Cannot reach crew-lead — restart services first","error");const a=await e("/api/settings/crew-cli",{enabled:!n.enabled});t("Crew CLI executor "+(a.enabled?"ENABLED 🔧":"DISABLED")),B()}catch(n){t("Failed: "+n.message,"error")}}async function D(){const e=document.getElementById("opencodeBtn"),t=document.getElementById("opencodeStatus");try{const n=await o("/api/settings/opencode");if(!1===n.ok)return e&&(e.textContent="⚫ OFF"),void(t&&(t.textContent="⚠️ Could not reach crew-lead — restart services.",t.style.color="var(--amber)"));const a=n.enabled;e&&(e.textContent=a?"⚡ ON":"⚫ OFF",e.style.background=a?"rgba(52,211,153,0.15)":"var(--surface-2)",e.style.borderColor=a?"rgba(52,211,153,0.3)":"var(--border)",e.style.color=a?"var(--green)":"var(--text-2)"),t&&(n.installed?(t.textContent=a?"⚡ Active — coding agents route through OpenCode for full IDE context and session persistence.":"⚫ Off — tasks use direct LLM or other configured engine. Enable to run agents through OpenCode CLI.",t.style.color="var(--text-3)"):(t.textContent="⚠️ opencode binary not found — install: npm install -g opencode",t.style.color="var(--amber)"))}catch(n){e&&(e.textContent="Error"),t&&(t.textContent="Could not load status",t.style.color="var(--text-3)")}}async function N(){try{const n=await o("/api/settings/opencode");if(!1===n.ok)return void t("Cannot reach crew-lead — restart services first","error");if(!n.installed)return void t("Install OpenCode CLI first: npm install -g opencode","error");const a=await e("/api/settings/opencode",{enabled:!n.enabled});t("OpenCode executor "+(a.enabled?"ENABLED ⚡":"DISABLED")),D()}catch(n){t("Failed: "+n.message,"error")}}async function W(){try{const e=await o("/api/settings/global-fallback"),t=document.getElementById("globalFallbackInput");t&&(t.value=e.globalFallbackModel||"");const n=document.getElementById("globalFallbackStatus");n&&(n.textContent=e.globalFallbackModel?"Active: any agent without a per-agent fallback will use "+e.globalFallbackModel:"Not set — agents without fallback will use the built-in default (groq/llama-3.3-70b-versatile).")}catch(e){console.warn("loadGlobalFallback:",e.message)}}async function T(){var o;const n=((null==(o=document.getElementById("globalFallbackInput"))?void 0:o.value)||"").trim();try{await e("/api/settings/global-fallback",{globalFallbackModel:n}),t(n?"Global fallback → "+n:"Global fallback cleared"),W()}catch(a){t("Failed: "+a.message,"error")}}async function F(){try{const e=await o("/api/settings/global-oc-loop"),t=document.getElementById("globalOcLoop"),n=document.getElementById("globalOcLoopRounds");t&&(t.checked=e.enabled||!1),n&&(n.value=e.maxRounds??10)}catch(e){}}async function P(){var o;const n=null==(o=document.getElementById("globalOcLoop"))?void 0:o.checked;try{await e("/api/settings/global-oc-loop",{enabled:n}),t("Global OC loop "+(n?"enabled":"disabled"))}catch(a){t("Failed: "+a.message,!0)}}async function G(){var o;const n=parseInt(null==(o=document.getElementById("globalOcLoopRounds"))?void 0:o.value)||10;try{await e("/api/settings/global-oc-loop",{maxRounds:n}),t("Max rounds set to "+n)}catch(a){t("Failed: "+a.message,!0)}}async function U(){try{const e=await o("/api/settings/passthrough-notify"),t=document.getElementById("passthroughNotifySelect");t&&(t.value=e.value||"both")}catch(e){}}async function j(){var o;const n=(null==(o=document.getElementById("passthroughNotifySelect"))?void 0:o.value)||"both",a=document.getElementById("passthroughNotifyStatus");try{await e("/api/settings/passthrough-notify",{value:n}),a&&(a.textContent="✓ Saved — takes effect on the next passthrough",a.style.color="var(--green-hi)"),t("Passthrough notifications → "+n)}catch(r){a&&(a.textContent="Error: "+r.message,a.style.color="var(--red)")}}async function H(){try{const e=await o("/api/settings/loop-brain"),t=document.getElementById("loopBrainModel");t&&e.loopBrain&&(t.value=e.loopBrain)}catch{}}const q=[{label:"Engine — OpenCode",vars:[{key:"CREWSWARM_OPENCODE_ENABLED",hint:"Route coding agents through OpenCode globally",default:"off"},{key:"CREWSWARM_OPENCODE_MODEL",hint:"Model passed to OpenCode — leave blank to use per-agent model",default:"per-agent"},{key:"CREWSWARM_OPENCODE_TIMEOUT_MS",hint:"ms before an OpenCode task is killed",default:"300000"},{key:"CREWSWARM_OPENCODE_AGENT",hint:"Override agent name passed to OpenCode",default:"auto"}]},{label:"Engine — Claude Code & Cursor",note:"Both use OAuth login (run claude or cursor once). No API key required.",vars:[{key:"CREWSWARM_CLAUDE_CODE_MODEL",hint:"Model passed to claude -p — leave blank for Claude Code default",default:"claude default"},{key:"CREWSWARM_CURSOR_MODEL",hint:"Cursor CLI --model when agent has no cursorCliModel (default: composer-2-fast)",default:"composer-2-fast"}]},{label:"Engine — Codex & crew-cli",note:"These are the dashboard-wide defaults when an agent does not have a per-route model override.",vars:[{key:"CREWSWARM_CODEX_MODEL",hint:"Model passed to codex exec --model (leave blank for Codex default)",default:"codex default"},{key:"CREWSWARM_CREW_CLI_MODEL",hint:"Model passed to crew chat --model and gateway crew-cli engine",default:"gemini-2.5-flash"}]},{label:"Engine — Gemini CLI",note:"Free tier via Google account — 60 req/min. Run gemini once to auth.",vars:[{key:"CREWSWARM_GEMINI_CLI_ENABLED",hint:"Route agents through Gemini CLI globally",default:"off"},{key:"CREWSWARM_GEMINI_CLI_MODEL",hint:"Model passed to gemini -p (e.g. gemini-2.0-flash) — blank for default",default:"gemini default"}]},{label:"Engine — Docker Sandbox",note:"Runs any inner engine inside an isolated Docker microVM. API keys injected by network proxy — never exposed to the agent.",vars:[{key:"CREWSWARM_DOCKER_SANDBOX",hint:"Route all coding agents through Docker Sandbox globally",default:"off"},{key:"CREWSWARM_DOCKER_SANDBOX_NAME",hint:"Pre-created sandbox name",default:"crewswarm"},{key:"CREWSWARM_DOCKER_SANDBOX_INNER_ENGINE",hint:"Engine inside the sandbox: claude, opencode, or codex",default:"claude"},{key:"CREWSWARM_DOCKER_SANDBOX_TIMEOUT_MS",hint:"ms before a sandboxed task is killed",default:"300000"}]},{label:"Engine Loop & Dispatch",vars:[{key:"CREWSWARM_ENGINE_LOOP",hint:"Enable Ouroboros engine loop for all agents",default:"off"},{key:"CREWSWARM_ENGINE_LOOP_MAX_ROUNDS",hint:"Max STEP iterations per loop run",default:"10"},{key:"CREWSWARM_ENGINE_IDLE_TIMEOUT_MS",hint:"Kill engine (Cursor/Claude) if no output for this many ms",default:"300000"},{key:"CREWSWARM_ENGINE_MAX_TOTAL_MS",hint:"Absolute max ms for any single engine task",default:"2700000"},{key:"CREWSWARM_DISPATCH_TIMEOUT_MS",hint:"ms before an unclaimed dispatch times out",default:"300000"},{key:"CREWSWARM_DISPATCH_CLAIMED_TIMEOUT_MS",hint:"ms before a claimed (in-progress) dispatch times out",default:"900000"},{key:"CREWSWARM_RT_AGENT",hint:"Agent ID used for the RT bus",default:"crew-coder"}]},{label:"Ports",vars:[{key:"CREW_LEAD_PORT",hint:"crew-lead HTTP server port",default:"5010"},{key:"SWARM_DASH_PORT",hint:"Dashboard port",default:"4319"},{key:"WA_HTTP_PORT",hint:"WhatsApp bridge HTTP port",default:"3000"}]},{label:"Background Consciousness",vars:[{key:"CREWSWARM_BG_CONSCIOUSNESS",hint:"Enable idle reflection loop",default:"off"},{key:"CREWSWARM_BG_CONSCIOUSNESS_INTERVAL_MS",hint:"Idle reflection interval in ms",default:"900000"},{key:"CREWSWARM_BG_CONSCIOUSNESS_MODEL",hint:"Model for background cycle (e.g. groq/llama-3.1-8b-instant)",default:"groq/llama-3.1-8b-instant"}]},{label:"Messaging",vars:[{key:"TELEGRAM_ALLOWED_USERNAMES",hint:"Comma-separated Telegram usernames allowed to message the bot",default:"all allowed"},{key:"WA_ALLOWED_NUMBERS",hint:"Comma-separated WhatsApp numbers in intl format (+1555…)",default:"all allowed"}]},{label:"Memory",vars:[{key:"SHARED_MEMORY_NAMESPACE",hint:"Namespace prefix for shared memory keys",default:"crewswarm"},{key:"SHARED_MEMORY_DIR",hint:"Directory for shared memory files",default:"~/.crewswarm/memory"}]},{label:"crew-cli — Streaming & Hooks",note:"Controls for crew-cli streaming output, tool hooks, and session token limits.",vars:[{key:"CREW_NO_STREAM",hint:"Disable streaming output — tokens arrive after full response (true/false)",default:"false"},{key:"CREW_HOOKS_FILE",hint:"Path to hooks.json for PreToolUse/PostToolUse hooks",default:".crew/hooks.json"},{key:"CREW_MAX_SESSION_TOKENS",hint:"Max estimated tokens per session before oldest turns are trimmed",default:"100000"}]},{label:"crew-cli — Codebase Index & RAG",note:"Codebase embedding index auto-builds on startup. Injects relevant file context into every worker prompt.",vars:[{key:"CREW_RAG_MODE",hint:"RAG mode: auto (use index when ready, else keyword), semantic, keyword, import-graph, off",default:"auto"},{key:"CREW_EMBEDDING_PROVIDER",hint:"Embedding provider: local (zero-cost), openai (best), gemini (free tier)",default:"local"},{key:"CREW_RAG_WORKER_BUDGET",hint:"Max tokens of RAG context injected per worker (approximate)",default:"4000"},{key:"CREW_RAG_MAX_FILES",hint:"Max code files to index (larger repos should increase this)",default:"2000"},{key:"CREW_RAG_BATCH_SIZE",hint:"Files per embedding batch (higher = faster but more API calls)",default:"20"}]},{label:"crew-cli — Checkpointing",note:"Automatic git checkpoints during pipeline execution for easy rollback.",vars:[{key:"CREW_AUTO_CHECKPOINT",hint:"Enable auto-commit at task boundaries (true/false)",default:"true"},{key:"CREW_CHECKPOINT_INTERVAL_MS",hint:"Periodic git stash snapshot interval during long tasks (ms, 0=off)",default:"60000"}]},{label:"PM Loop",vars:[{key:"PM_MAX_ITEMS",hint:"Max roadmap items per PM loop run",default:"10"},{key:"PM_MAX_CONCURRENT",hint:"Max concurrent agent tasks in PM loop",default:"20"},{key:"PM_USE_QA",hint:"Include crew-qa review after each PM task",default:"off"},{key:"PM_USE_SECURITY",hint:"Include crew-security review for auth/key tasks",default:"off"},{key:"PM_USE_SPECIALISTS",hint:"Route tasks to specialist agents (front/back/github) by keyword",default:"on"},{key:"PM_SELF_EXTEND",hint:"Auto-generate new roadmap items when queue is empty",default:"on"},{key:"PM_EXTEND_EVERY",hint:"Generate new items every N completions (0 = only when empty)",default:"5"},{key:"PM_CODER_AGENT",hint:"Override default coding agent for PM loop (e.g. crew-coder-front)",default:"crew-coder"},{key:"PM_AGENT_IDLE_TIMEOUT_MS",hint:"Kill PM dispatch if no activity for this many ms",default:"900000"},{key:"PHASED_TASK_TIMEOUT_MS",hint:"Overall timeout for a single agent task in the PM loop",default:"600000"}]}];async function $(){const e=document.getElementById("envAdvancedWidget");if(e)try{const[t,o]=await Promise.all([fetch("/api/env").then(e=>e.json()).catch(()=>({})),fetch("/api/env-advanced").then(e=>e.json()).catch(()=>({env:{}}))]),n=o.env||{},r=null!=t.uptime?t.uptime<60?t.uptime+"s":Math.floor(t.uptime/60)+"m":"—";let s=`<div style="display:flex;gap:24px;flex-wrap:wrap;font-size:11px;color:var(--text-3);margin-bottom:16px;padding-bottom:10px;border-bottom:1px solid var(--border);">\n <span>cwd: <code style="color:var(--text-2);">${a(t.cwd||"—")}</code></span>\n <span>node: <code style="color:var(--text-2);">${a(t.node||"—")}</code></span>\n <span>uptime: <code style="color:var(--text-2);">${r}</code></span>\n </div>`;e.innerHTML=s;for(const l of q){const t=document.createElement("div");t.style.cssText="margin-bottom:18px;",t.innerHTML=`<div style="font-size:11px;font-weight:700;color:var(--text-3);text-transform:uppercase;letter-spacing:.06em;margin-bottom:${l.note?"4px":"8px"};">${a(l.label)}</div>`+(l.note?`<div style="font-size:11px;color:var(--accent);margin-bottom:8px;line-height:1.4;">${a(l.note)}</div>`:"");for(const{key:e,hint:o,default:r}of l.vars){const s=n[e]??null,l=s??r??"",i=null===s,c=r?`default: ${r}`:"not set",d=document.createElement("div");d.style.cssText="margin-bottom:8px;",d.innerHTML=`\n <div style="display:flex;align-items:baseline;gap:6px;margin-bottom:3px;">\n <span style="font-size:11px;font-family:monospace;color:var(--accent);">${a(e)}</span>\n ${i&&r?'<span style="font-size:10px;color:var(--text-3);font-family:monospace;background:var(--bg-1);padding:1px 5px;border-radius:4px;border:1px solid var(--border);">default</span>':""}\n </div>\n <div style="font-size:10px;color:var(--text-3);margin-bottom:4px;">${a(o)}</div>\n <div style="display:flex;gap:6px;align-items:center;">\n <input data-env-key="${a(e)}" data-env-default="${a(r||"")}" type="text" value="${a(l)}"\n placeholder="${a(c)}"\n class="inp-sm inp-mono inp-flex" />\n <button data-env-save="${a(e)}" style="font-size:11px;padding:5px 10px;border-radius:6px;cursor:pointer;border:1px solid var(--border);background:var(--surface-2);color:var(--text-2);white-space:nowrap;">Save</button>\n <span data-env-status="${a(e)}" style="font-size:11px;min-width:50px;"></span>\n </div>`,t.appendChild(d)}e.appendChild(t)}e.querySelectorAll("[data-env-save]").forEach(t=>{t.addEventListener("click",()=>{const o=t.dataset.envSave,n=e.querySelector(`[data-env-key="${o}"]`),a=e.querySelector(`[data-env-status="${o}"]`);n&&a&&async function(e,t,o){const n=t.value.trim();o.textContent="Saving…",o.style.color="var(--text-3)";try{const t=await fetch("/api/env-advanced",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({[e]:n||null})}),a=await t.json();a.ok?(o.textContent=n?"✓ Saved":"✓ Cleared",o.style.color="var(--green)"):(o.textContent="Error: "+(a.error||"unknown"),o.style.color="var(--red, #f87171)")}catch(a){o.textContent="Error: "+a.message,o.style.color="var(--red, #f87171)"}setTimeout(()=>{o.textContent=""},3e3)}(o,n,a)})}),e.querySelectorAll("[data-env-key]").forEach(e=>{e.addEventListener("input",()=>{const t=e.value===(e.dataset.envDefault||"");e.style.color="var(--text-1)",e.style.opacity=t?"0.65":"1"})})}catch(t){e&&(e.textContent="Could not load: "+t.message)}}export{M as A,_ as B,A as C,B as D,D as E,F,H as G,U as H,$ as I,c as J,G as a,P as b,L as c,R as d,O as e,S as f,I as g,x as h,E as i,C as j,T as k,p as l,f as m,g as n,d as o,b as p,y as q,v as r,j as s,N as t,m as u,W as v,u as w,h as x,k as y,w as z};
|
|
Binary file
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{s as e}from"./core-utils-CmOkXgzi.js";let t=[];function n(){document.querySelectorAll(".view, .view-sessions").forEach(e=>{e.classList.remove("active"),e.style.display&&(e.style.display="")});const e=document.querySelector(".msg-bar");e&&(e.style.display="")}function l(){n(),document.getElementById("skillsView").classList.add("active"),document.querySelectorAll(".nav-item").forEach(e=>e.classList.remove("active"));const e=document.getElementById("navSkills");e&&e.classList.add("active"),i(),"function"==typeof loadPendingApprovals&&loadPendingApprovals()}function a(){n();const e=document.getElementById("runSkillsView");e&&e.classList.add("active"),document.querySelectorAll(".nav-item").forEach(e=>e.classList.remove("active"));const t=document.getElementById("navRunSkills");t&&t.classList.add("active"),s()}async function s(){const e=document.getElementById("runSkillsGrid");if(e)try{const t=((await(await fetch("/api/health")).json()).skills||[]).filter(e=>!e.error&&e.url);if(!t.length)return void(e.innerHTML='<div style="color:var(--text-3);font-size:13px;">No API skills found. Add API skills (with a URL endpoint) in the Skills tab.</div>');e.innerHTML=t.map(e=>{const t=e.defaultParams&&Object.keys(e.defaultParams).length?JSON.stringify(e.defaultParams,null,2):"{}",n=(e.paramNotes||e.description||"").slice(0,120),l=(e.name||"").replace(/"/g,""");return'<div class="card" style="display:flex;flex-direction:column;"><div class="card-title" style="margin-bottom:6px;">'+(e.name||"unnamed")+'</div><div style="font-size:12px;color:var(--text-3);margin-bottom:10px;line-height:1.4;">'+(e.description||"")+"</div>"+(n?'<div style="font-size:11px;color:var(--text-2);margin-bottom:8px;">'+n+"</div>":"")+'<label style="font-size:11px;color:var(--text-2);margin-bottom:4px;">Params (JSON)</label><textarea data-skill="'+l+'" rows="4" style="font-family:monospace;font-size:12px;width:100%;margin-bottom:10px;resize:vertical;" class="runskills-params">'+t.replace(/</g,"<")+'</textarea><div style="display:flex;align-items:center;gap:8px;margin-top:auto;"><button class="btn-green" style="font-size:12px;" data-action="runSkillFromUI" data-arg="'+l+'">Run</button><span class="runskills-result" data-skill="'+l+'" style="font-size:11px;color:var(--text-3);"></span></div></div>'}).join("")}catch(t){e.innerHTML='<div style="color:var(--red);font-size:12px;">Error loading health/skills: '+(t.message||"")+"</div>"}}async function o(e){const t=document.querySelector('.runskills-params[data-skill="'+(e||"").replace(/"/g,'\\"')+'"]'),n=document.querySelector('.runskills-result[data-skill="'+(e||"").replace(/"/g,'\\"')+'"]');if(!t)return;let l={};try{l=JSON.parse(t.value.trim()||"{}")}catch(a){return void(n&&(n.textContent="Invalid JSON"))}n&&(n.textContent="Running…");try{const t=await fetch("/api/skills/"+encodeURIComponent(e)+"/run",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({params:l})}),a=await t.json();if(n&&(a.ok?n.textContent="Done":n.textContent=a.error||"Error",n.style.color=a.ok?"var(--green)":"var(--red)"),!a.ok)return;if(void 0!==a.result&&n){const e="string"==typeof a.result?a.result:JSON.stringify(a.result).slice(0,120);n.textContent=e+(e.length>=120?"…":"")}}catch(a){n&&(n.textContent=a.message||"Request failed",n.style.color="var(--red)")}}async function i(){const e=document.getElementById("skillsList");try{const e=await(await fetch("/api/skills")).json();t=e.skills||[],r(t)}catch(n){e&&(e.innerHTML='<div style="color:var(--text-3);font-size:12px;">Error loading skills</div>')}}function r(e){const t=document.getElementById("skillsList");if(!t)return;if(!e.length)return void(t.innerHTML='<div style="color:var(--text-3);font-size:12px;padding:8px 0;">No skills match. Add one above or copy JSONs to ~/.crewswarm/skills/</div>');const n=e.filter(e=>"api"===e.type||!e.type&&e.url),l=e.filter(e=>"knowledge"===e.type||!e.type&&!e.url);function a(e){const t="knowledge"===e.type,n=e.requiresApproval?'<span style="margin-left:6px;font-size:10px;background:rgba(251,191,36,0.15);color:var(--yellow);padding:2px 6px;border-radius:4px;">⚠️ approval</span>':"",l=t?'<span style="margin-left:6px;font-size:10px;background:rgba(99,102,241,0.15);color:#818cf8;padding:2px 6px;border-radius:4px;">knowledge</span>':'<span style="margin-left:6px;font-size:10px;background:rgba(34,197,94,0.12);color:var(--green);padding:2px 6px;border-radius:4px;">API</span>',a=e.url?' · <code style="background:var(--bg-1);padding:1px 4px;border-radius:3px;">'+(e.method||"POST")+" "+(e.url||"").slice(0,55)+"</code>":"",s=e.aliases&&e.aliases.length?'<span style="margin-left:6px;font-size:10px;color:var(--text-3);">aliases: '+e.aliases.join(", ")+"</span>":"";return'<div style="display:flex;align-items:center;justify-content:space-between;padding:10px 12px;background:var(--bg-2);border-radius:var(--radius);border:1px solid var(--border);"><div style="min-width:0;"><div style="display:flex;align-items:center;flex-wrap:wrap;gap:2px;"><span style="font-weight:600;font-size:13px;">'+e.name+"</span>"+l+n+s+'</div><div style="font-size:11px;color:var(--text-3);margin-top:3px;">'+(e.description||"")+a+'</div></div><div style="display:flex;gap:6px;flex-shrink:0;margin-left:12px;">'+(t?"":'<button class="btn-ghost" style="font-size:11px;" data-action="editSkill" data-arg="'+e.name+'">Edit</button>')+'<button class="btn-ghost" style="font-size:11px;color:var(--red);" data-action="deleteSkill" data-arg="'+e.name+'">Delete</button></div></div>'}function s(e,t,n){return t.length?'<div style="margin-bottom:20px;"><div style="font-size:11px;font-weight:600;letter-spacing:.06em;text-transform:uppercase;color:var(--text-3);margin-bottom:8px;">'+e+' <span style="font-weight:400;opacity:.7;">('+t.length+')</span></div><div style="display:flex;flex-direction:column;gap:6px;">'+t.map(a).join("")+"</div></div>":""}t.innerHTML=s("Knowledge",l)+s("API Integrations",n)}function d(e){const n=e.toLowerCase();r(n?t.filter(e=>(e.name||"").toLowerCase().includes(n)||(e.description||"").toLowerCase().includes(n)||(e.url||"").toLowerCase().includes(n)||(e.aliases||[]).some(e=>e.toLowerCase().includes(n))):t)}function c(e){var n,l,a,s;const o=t.find(t=>t.name===e);if(!o)return;document.getElementById("skEditName").value=e,document.getElementById("addSkillFormTitle").textContent="Edit Skill",document.getElementById("saveSkillBtn").textContent="Update Skill",document.getElementById("skName").value=o.name||"",document.getElementById("skDesc").value=o.description||"",document.getElementById("skUrl").value=o.url||"";const i=document.getElementById("skMethod");for(let t=0;t<i.options.length;t++)if(i.options[t].value===o.method){i.selectedIndex=t;break}const r=(null==(n=o.auth)?void 0:n.type)||"";document.getElementById("skAuthType").value=r,document.getElementById("skAuthKey").value=(null==(l=o.auth)?void 0:l.keyFrom)||(null==(a=o.auth)?void 0:a.token)||"",document.getElementById("skAuthHeader").value=(null==(s=o.auth)?void 0:s.header)||"",document.getElementById("skRequiresApproval").checked=!!o.requiresApproval,document.getElementById("skDefaults").value=o.defaultParams&&Object.keys(o.defaultParams).length?JSON.stringify(o.defaultParams,null,2):"",g();const d=document.getElementById("addSkillForm");d.style.display="block",d.scrollIntoView({behavior:"smooth",block:"start"})}function m(){y(),document.getElementById("importSkillForm").style.display="none";const e=document.getElementById("addSkillForm");e.style.display="none"===e.style.display?"block":"none"}function u(){y();const e=document.getElementById("importSkillForm");e.style.display="none"===e.style.display?"block":"none","none"!==e.style.display&&setTimeout(()=>document.getElementById("importSkillUrl").focus(),50)}async function p(){const e=document.getElementById("importSkillUrl"),t=document.getElementById("importSkillStatus"),n=document.getElementById("importSkillBtn"),l=e.value.trim();if(!l)return t.style.color="var(--red)",void(t.textContent="Paste a URL first.");n.disabled=!0,n.textContent="Importing…",t.style.color="var(--text-3)",t.textContent="Fetching & scanning…";try{const n=await fetch("/api/skills/import",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({url:l})}),a=await n.json();if(!n.ok||a.error)throw new Error(a.error||"Import failed");if(a.warnings&&a.warnings.length){t.style.color="var(--yellow)";const e={cmd_skill:"⚠ executes shell commands",ssrf_risk:"⚠ targets private network",insecure_url:"⚠ non-HTTPS endpoint",no_approval:"⚠ no approval gate on write"},n=a.warnings.map(t=>e[t.split(":")[0]]||t);t.innerHTML='✓ Imported <strong>"'+a.name+'"</strong> — '+n.join(" · ")}else t.style.color="var(--green)",t.textContent='✓ Imported "'+a.name+'" — no security warnings';e.value="",await i(),a.warnings&&a.warnings.length||setTimeout(()=>{document.getElementById("importSkillForm").style.display="none",t.textContent=""},3e3)}catch(a){t.style.color="var(--red)",t.textContent="Error: "+a.message}finally{n.disabled=!1,n.textContent="Import"}}function y(){document.getElementById("skEditName").value="",document.getElementById("addSkillFormTitle").textContent="New Skill",document.getElementById("saveSkillBtn").textContent="Save Skill",document.getElementById("addSkillForm").style.display="none",["skName","skDesc","skUrl","skAuthKey","skAuthHeader","skDefaults"].forEach(e=>{const t=document.getElementById(e);t&&(t.value="")}),document.getElementById("skAuthType").value="",document.getElementById("skRequiresApproval").checked=!1,g()}function g(){const e=document.getElementById("skAuthType").value;document.getElementById("skAuthHeaderWrap").style.display="header"===e?"block":"none"}async function v(){const t=document.getElementById("skName").value.trim(),n=document.getElementById("skUrl").value.trim();if(!t||!n)return void alert("Skill name and URL are required");let l={};try{const e=document.getElementById("skDefaults").value.trim();e&&(l=JSON.parse(e))}catch{return void alert("Default Params must be valid JSON")}const a=document.getElementById("skAuthType").value,s=document.getElementById("skAuthKey").value.trim();let o={};a&&s&&(o={type:a},s.startsWith("providers.")||s.startsWith("env.")?o.keyFrom=s:o.token=s,"header"===a&&(o.header=document.getElementById("skAuthHeader").value.trim()||"X-API-Key"));const r=document.getElementById("skEditName").value.trim(),d={name:t,url:n,method:document.getElementById("skMethod").value,description:document.getElementById("skDesc").value.trim(),auth:Object.keys(o).length?o:void 0,defaultParams:l,requiresApproval:document.getElementById("skRequiresApproval").checked};try{r&&r!==t&&await fetch("/api/skills/"+r,{method:"DELETE"});const n=await fetch("/api/skills",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(d)});if(!n.ok)throw new Error(await n.text());y(),i(),e(r?"Skill updated":"Skill saved")}catch(c){e("Failed: "+c.message,"error")}}async function f(t){if(confirm('Delete skill "'+t+'"?'))try{const n=await fetch("/api/skills/"+t,{method:"DELETE"});if(!n.ok)throw new Error(await n.text());i(),e("Deleted")}catch(n){e("Delete failed: "+n.message,"error")}}export{l as a,v as b,y as c,m as d,c as e,d as f,f as g,p as i,s as l,o as r,a as s,u as t,g as u};
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{a as s,b as t,g as e,e as a,s as n,p as i}from"./core-utils-CmOkXgzi.js";let l=()=>{},r=()=>{};function o(s={}){l=s.hideAllViews||l,r=s.setNavActive||r}let d=null;function c(){l(),document.getElementById("testingView").classList.add("active"),r("navTesting"),s.activeTab="testing",t(),m(),g(),e("/api/tests/progress").then(s=>{s.running&&!y&&(b(),y=setInterval(b,2e3))}).catch(()=>{}),d&&clearInterval(d),d=setInterval(()=>{document.getElementById("testingView").classList.contains("active")?(m(),g()):(clearInterval(d),d=null)},3e4)}function u(s){return!s||s<=0?"-":s>=6e4?(s/6e4).toFixed(1)+"m":s>=1e3?(s/1e3).toFixed(1)+"s":Math.round(s)+"ms"}function p(s){if(!s)return"-";const t=new Date(s);return t.toLocaleDateString(void 0,{month:"short",day:"numeric"})+" "+t.toLocaleTimeString(void 0,{hour:"2-digit",minute:"2-digit"})}function v(s,t){const e=s+t;return 0===e?"-":(s/e*100).toFixed(0)+"%"}const f={unit:"Unit",integration:"Integration",e2e:"E2E",all:"All",unknown:"Other"},h={unit:"#818cf8",integration:"#34d399",e2e:"#fbbf24",all:"#60a5fa",unknown:"#94a3b8"};async function m(){var s;const t=document.getElementById("testingContent");if(t)try{const n=await e("/api/tests/summary");if(!n.latest&&!n.fileCounts)return void(t.innerHTML='<div class="empty-state">No test results found. Run tests to see results here.</div>');let i="";const l=n.fileCounts||{},r=n.testCounts||{};i+='<div class="test-launch-grid">';const o=[{key:"unit",label:"Unit",files:l.unit,tests:r.unit,cmd:"test:unit",color:h.unit},{key:"integration",label:"Integration",files:l.integration,tests:r.integration,cmd:"test:integration",color:h.integration},{key:"e2e",label:"E2E",files:l.e2e,tests:r.e2e,cmd:"test:e2e",color:h.e2e},{key:"playwright",label:"Playwright",files:l.playwright,tests:r.playwright,cmd:"test:e2e:vibe",color:"#f472b6"},{key:"crew-cli",label:"crew-cli",files:l["crew-cli"],tests:r["crew-cli"],cmd:"test",color:"#10b981"}];for(const s of o){const t=s.tests?`<span class="test-launch-tests">${s.tests} tests</span>`:"",e=s.cmd?`<button class="test-launch-btn" data-action="runTests" data-arg="${s.cmd}">▶ Run</button>`:'<span class="meta" style="font-size:10px">npx playwright test</span>';i+=`\n <div class="test-launch-card" style="border-color:${s.color}30">\n <div class="test-launch-header">\n <span class="test-launch-name" style="color:${s.color}">${s.label}</span>\n ${e}\n </div>\n <div class="test-launch-counts">\n <span class="test-launch-files">${s.files||0} files</span>\n ${t}\n </div>\n </div>`}const d=Object.values(r).reduce((s,t)=>s+(t||0),0);i+=`\n <div class="test-launch-card test-launch-total" style="border-color:var(--accent)">\n <div class="test-launch-header">\n <span class="test-launch-name" style="color:var(--accent)">All</span>\n <button class="test-launch-btn" data-action="runTests" data-arg="test:all" style="background:var(--accent);color:#fff">▶ Run All</button>\n </div>\n <div class="test-launch-counts">\n <span class="test-launch-files">${l.total||0} files</span>\n ${d?`<span class="test-launch-tests">${d}+ tests</span>`:""}\n </div>\n </div>`,i+="</div>",i+='<div class="test-section-title">Latest Results by Suite</div>',i+='<div class="test-suite-grid">';for(const t of["unit","integration","e2e","all"]){const e=null==(s=n.suites)?void 0:s[t];if(!e||!e.total&&!e.passed&&!e.failed)continue;const a=(e.passed||0)+(e.failed||0),l=e.failed>0?"test-status-fail":"test-status-pass",r=e.failed>0?"FAIL":"PASS";i+=`\n <div class="test-suite-card">\n <div class="test-suite-header">\n <span class="test-suite-name" style="color:${h[t]}">${f[t]}</span>\n <span class="test-summary-status ${l}">${r}</span>\n </div>\n <div class="test-suite-stats">\n <div><span class="test-color-pass">${e.passed||0}</span> pass</div>\n <div><span class="${e.failed>0?"test-color-fail":""}">${e.failed||0}</span> fail</div>\n <div><span class="${e.skipped>0?"test-color-skip":""}">${e.skipped||0}</span> skip</div>\n <div><strong>${e.total||0}</strong> total</div>\n </div>\n <div class="test-suite-meta">\n ${v(e.passed||0,e.failed||0)} pass rate · ${u(e.duration_ms)} · ${p(e.timestamp)}\n </div>\n <div class="test-progress-bar">\n <div class="test-progress-pass" style="width:${a>0?(e.passed||0)/a*100:0}%"></div>\n <div class="test-progress-fail" style="width:${a>0?(e.failed||0)/a*100:0}%"></div>\n </div>\n </div>`}i+="</div>";const c=[];for(const s of Object.values(n.suites||{}))s.failures&&c.push(...s.failures);if(c.length>0){i+=`<div class="test-section-title">Failures (${c.length})</div>`;for(const s of c)i+=`\n <div class="test-failure-card">\n <div class="test-failure-name">${a(s.name)}</div>\n <div class="test-failure-file">${a(s.file)}</div>\n ${s.classification&&"unknown"!==s.classification?`<span class="test-failure-class">${a(s.classification)}</span>`:""}\n ${s.error?`<pre class="test-failure-error">${a(String(s.error).slice(0,500))}</pre>`:""}\n ${s.rerun_command?`<div class="test-failure-rerun"><code>${a(s.rerun_command)}</code></div>`:""}\n </div>`}const m=[];for(const[s,t]of Object.entries(n.suites||{}))t.skips&&m.push(...t.skips.map(t=>({...t,suite:s})));if(m.length>0){i+='<details class="test-skips-section">',i+=`<summary class="test-section-title" style="cursor:pointer">Skipped (${m.length}) — click to expand</summary>`,i+='<table class="test-groups-table"><thead><tr><th>Test</th><th>File</th><th>Suite</th></tr></thead><tbody>';for(const s of m.slice(0,50))i+=`<tr><td>${a(s.name)}</td><td class="meta">${a(s.file)}</td><td><span class="test-cat-badge test-cat-${a(s.suite)}">${a(s.suite)}</span></td></tr>`;m.length>50&&(i+=`<tr><td colspan="3" class="meta">...and ${m.length-50} more</td></tr>`),i+="</tbody></table></details>"}t.innerHTML=i}catch(n){t.innerHTML=`<div class="empty-state">Failed to load test results: ${a(n.message)}</div>`}}async function g(){const s=document.getElementById("testingHistory");if(s)try{const t=await e("/api/tests/history");if(!t.history||0===t.history.length)return void(s.innerHTML='<div class="meta">No run history yet.</div>');let n='<div class="test-section-title">Run History</div>';n+='\n <table class="test-history-table">\n <thead>\n <tr>\n <th>When</th>\n <th>Suite</th>\n <th>Status</th>\n <th class="num">Pass</th>\n <th class="num">Fail</th>\n <th class="num">Skip</th>\n <th class="num">Total</th>\n <th class="num">Duration</th>\n <th class="num">Rate</th>\n </tr>\n </thead>\n <tbody>';for(const s of t.history.slice(0,25)){const t=s.failed>0?"test-color-fail":"test-color-pass",e=f[s.suite]||s.suite||"?",i=h[s.suite]||h.unknown;n+=`\n <tr data-action="loadRunDetail" data-arg="${a(s.runId)}" style="cursor:pointer" class="${s.failed>0?"test-row-fail":""}">\n <td class="meta" style="white-space:nowrap">${p(s.timestamp)}</td>\n <td><span class="test-cat-badge" style="background:${i}20;color:${i}">${e}</span></td>\n <td class="${t}" style="font-weight:600">${s.failed>0?"FAIL":"PASS"} ▸</td>\n <td class="num">${s.passed}</td>\n <td class="num ${s.failed>0?"test-color-fail":""}">${s.failed}</td>\n <td class="num ${s.skipped>0?"test-color-skip":""}">${s.skipped}</td>\n <td class="num"><strong>${s.total}</strong></td>\n <td class="num">${u(s.duration_ms)}</td>\n <td class="num">${v(s.passed,s.failed)}</td>\n </tr>`}n+="</tbody></table>",n+='<div id="testingRunDetail"></div>',s.innerHTML=n}catch(t){s.innerHTML=`<div class="meta">Failed to load history: ${a(t.message)}</div>`}}async function $(s){var t,n;const i=document.getElementById("testingRunDetail");if(i){i.innerHTML='<div class="meta" style="padding:12px">Loading run detail...</div>';try{const l=await e("/api/tests/run-detail?runId="+encodeURIComponent(s));if(l.error)return void(i.innerHTML=`<div class="meta">${a(l.error)}</div>`);let r=`<div class="test-section-title">Run Detail: ${a(s)} <span class="meta" style="font-weight:400;font-size:11px;text-transform:none">${p(l.timestamp)}</span></div>`;r+=`<div class="test-suite-meta" style="margin-bottom:12px">${l.passed} pass, ${l.failed} fail, ${l.skipped} skip, ${l.total} total · ${u(l.duration_ms)} · ${v(l.passed,l.failed)} pass rate</div>`;if(!((null==(t=l.failures)?void 0:t.length)>0||(null==(n=l.skips)?void 0:n.length)>0)&&l.total>0&&(r+='<div class="meta" style="padding:8px 0;color:var(--text-2)">No detailed failure/skip data saved for this run. Run with <code>npm run test:all</code> to generate full reports.</div>'),l.failures&&l.failures.length>0){r+=`<div class="test-section-title">Failures (${l.failures.length})</div>`;for(const s of l.failures)r+=`\n <div class="test-failure-card">\n <div class="test-failure-name">${a(s.name)}</div>\n <div class="test-failure-file">${a(s.file)}</div>\n ${s.error?`<pre class="test-failure-error">${a(String(s.error).slice(0,500))}</pre>`:""}\n ${s.rerun_command?`<div class="test-failure-rerun"><code>${a(s.rerun_command)}</code></div>`:""}\n </div>`}if(l.skips&&l.skips.length>0){r+=`<details><summary class="test-section-title" style="cursor:pointer">Skipped (${l.skips.length})</summary>`,r+='<table class="test-groups-table"><thead><tr><th>Test</th><th>File</th></tr></thead><tbody>';for(const s of l.skips.slice(0,50))r+=`<tr><td>${a(s.name)}</td><td class="meta">${a(s.file)}</td></tr>`;l.skips.length>50&&(r+=`<tr><td colspan="2" class="meta">...and ${l.skips.length-50} more</td></tr>`),r+="</tbody></table></details>"}i.innerHTML=r,i.scrollIntoView({behavior:"smooth",block:"nearest"})}catch(l){i.innerHTML=`<div class="meta">Failed: ${a(l.message)}</div>`}}}let y=null;function b(){const s=document.getElementById("testProgressBar");s&&e("/api/tests/progress").then(t=>{var e;if(!t.running&&!t.finished)return void(s.innerHTML="");const n=((t.finished||Date.now())-t.started)/1e3,i=n>=60?(n/60).toFixed(1)+"m":Math.round(n)+"s",l=t.passed+t.failed+t.skipped,r=f[null==(e=t.suite)?void 0:e.replace("test:","")]||t.suite||"Tests";if(t.running){const e=t.current_file?t.current_file.split("/").pop():"";s.innerHTML=`\n <div class="test-progress-live">\n <div class="test-progress-live-header">\n <span class="test-progress-live-status">⏳ Running ${a(r)}...</span>\n <span class="meta">${i}</span>\n </div>\n <div class="test-progress-live-stats">\n <span class="test-color-pass">${t.passed} pass</span>\n <span class="test-color-fail">${t.failed} fail</span>\n <span class="test-color-skip">${t.skipped} skip</span>\n <span>${t.files_done} files</span>\n <span>${l} tests</span>\n </div>\n ${e?`<div class="test-progress-live-file">${a(e)}</div>`:""}\n <div class="test-progress-bar" style="margin-top:6px">\n <div class="test-progress-pass" style="width:${l>0?t.passed/l*100:0}%;transition:width 0.3s"></div>\n <div class="test-progress-fail" style="width:${l>0?t.failed/l*100:0}%;transition:width 0.3s"></div>\n </div>\n </div>`}else{const e=t.failed>0?"test-color-fail":"test-color-pass",n=t.failed>0?"FAILED":"PASSED";s.innerHTML=`\n <div class="test-progress-live test-progress-done">\n <div class="test-progress-live-header">\n <span class="${e}" style="font-weight:700">✓ ${a(r)} ${n}</span>\n <span class="meta">${i}</span>\n </div>\n <div class="test-progress-live-stats">\n <span class="test-color-pass">${t.passed} pass</span>\n <span class="test-color-fail">${t.failed} fail</span>\n <span class="test-color-skip">${t.skipped} skip</span>\n <span>${t.files_done} files</span>\n </div>\n </div>`,y&&(clearInterval(y),y=null),m(),g(),setTimeout(()=>{s&&(s.innerHTML="")},1e4)}}).catch(()=>{})}async function k(s){try{n(`Starting ${s}...`),await i("/api/tests/run",{suite:s}),y&&clearInterval(y),b(),y=setInterval(b,2e3)}catch(t){n("Failed to start tests: "+t.message,!0)}}export{o as i,$ as l,k as r,c as s};
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -6,13 +6,13 @@
|
|
|
6
6
|
<title>crewswarm dashboard</title>
|
|
7
7
|
<link rel="icon" type="image/png" href="/favicon.png" />
|
|
8
8
|
<!-- Font: system stack only to avoid CORS when dashboard (4319) and studio (3333) both load Inter from Google -->
|
|
9
|
-
<script type="module" crossorigin src="/assets/index-
|
|
9
|
+
<script type="module" crossorigin src="/assets/index-BeVllEj_.js"></script>
|
|
10
10
|
<link rel="modulepreload" crossorigin href="/assets/core-utils-CmOkXgzi.js">
|
|
11
11
|
<link rel="modulepreload" crossorigin href="/assets/setup-wizard-CA0Or47w.js">
|
|
12
12
|
<link rel="modulepreload" crossorigin href="/assets/components-BS9fQjE_.js">
|
|
13
13
|
<link rel="modulepreload" crossorigin href="/assets/orchestration-Ca2DLWN-.js">
|
|
14
|
-
<link rel="modulepreload" crossorigin href="/assets/cli-process-
|
|
15
|
-
<link rel="modulepreload" crossorigin href="/assets/chat-core-
|
|
14
|
+
<link rel="modulepreload" crossorigin href="/assets/cli-process-CNZ_UBCt.js">
|
|
15
|
+
<link rel="modulepreload" crossorigin href="/assets/chat-core-uXb_C0GM.js">
|
|
16
16
|
<link rel="modulepreload" crossorigin href="/assets/tab-swarm-chat-tab-BNrd88-r.js">
|
|
17
17
|
<link rel="modulepreload" crossorigin href="/assets/tab-waves-tab-SaJDkb4x.js">
|
|
18
18
|
<link rel="modulepreload" crossorigin href="/assets/tab-workflows-tab-B-soSy1k.js">
|
|
@@ -20,18 +20,19 @@
|
|
|
20
20
|
<link rel="modulepreload" crossorigin href="/assets/tab-services-tab-DU_LH3uG.js">
|
|
21
21
|
<link rel="modulepreload" crossorigin href="/assets/tab-agents-tab-BgpIsjkw.js">
|
|
22
22
|
<link rel="modulepreload" crossorigin href="/assets/tab-prompts-tab-DVkUNaJd.js">
|
|
23
|
-
<link rel="modulepreload" crossorigin href="/assets/tab-
|
|
23
|
+
<link rel="modulepreload" crossorigin href="/assets/tab-testing-tab-CezZOZcJ.js">
|
|
24
|
+
<link rel="modulepreload" crossorigin href="/assets/tab-skills-tab-DR7PJ7NB.js">
|
|
24
25
|
<link rel="modulepreload" crossorigin href="/assets/tab-contacts-tab-DiOyMYth.js">
|
|
25
26
|
<link rel="modulepreload" crossorigin href="/assets/tab-engines-tab-BsdZVvU0.js">
|
|
26
27
|
<link rel="modulepreload" crossorigin href="/assets/tab-swarm-tab-B1AcjL1W.js">
|
|
27
|
-
<link rel="modulepreload" crossorigin href="/assets/tab-models-tab-
|
|
28
|
+
<link rel="modulepreload" crossorigin href="/assets/tab-models-tab-dNRgsTOO.js">
|
|
28
29
|
<link rel="modulepreload" crossorigin href="/assets/tab-projects-tab-SFH4E--a.js">
|
|
29
|
-
<link rel="modulepreload" crossorigin href="/assets/tab-settings-tab-
|
|
30
|
+
<link rel="modulepreload" crossorigin href="/assets/tab-settings-tab-CuvH_Fj_.js">
|
|
30
31
|
<link rel="modulepreload" crossorigin href="/assets/tab-comms-tab-kguqTIzD.js">
|
|
31
32
|
<link rel="modulepreload" crossorigin href="/assets/tab-usage-tab-BIOOnB-Y.js">
|
|
32
33
|
<link rel="modulepreload" crossorigin href="/assets/tab-spending-tab-DEccQHnt.js">
|
|
33
34
|
<link rel="modulepreload" crossorigin href="/assets/tab-pm-loop-tab-DiAPTJXu.js">
|
|
34
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
35
|
+
<link rel="stylesheet" crossorigin href="/assets/index-D-sRshvg.css">
|
|
35
36
|
</head>
|
|
36
37
|
<body>
|
|
37
38
|
<!-- Skip link for keyboard navigation -->
|
|
@@ -112,6 +113,10 @@
|
|
|
112
113
|
<button class="nav-item" id="navRunSkills" data-view="run-skills">
|
|
113
114
|
<span class="nav-icon">⚡</span> Run skills
|
|
114
115
|
</button>
|
|
116
|
+
<button class="nav-item" id="navTesting" data-view="testing">
|
|
117
|
+
<span class="nav-icon">🧪</span> Testing
|
|
118
|
+
<span class="nav-badge hidden" id="testingBadge">0</span>
|
|
119
|
+
</button>
|
|
115
120
|
<button class="nav-item" id="navBenchmarks" data-view="benchmarks">
|
|
116
121
|
<span class="nav-icon">📊</span> Benchmarks
|
|
117
122
|
</button>
|
|
@@ -1177,6 +1182,8 @@
|
|
|
1177
1182
|
<option value="deepseek-chat">DeepSeek Chat</option>
|
|
1178
1183
|
<option value="llama-3.3-70b-versatile">Llama 3.3 70B (Groq)</option>
|
|
1179
1184
|
<option value="gpt-4o">GPT-4o</option>
|
|
1185
|
+
<option value="claude-haiku-4-5-20251001">Claude Haiku 4.5 (OAuth)</option>
|
|
1186
|
+
<option value="claude-sonnet-4-6">Claude Sonnet 4.6 (OAuth)</option>
|
|
1180
1187
|
<option value="moonshot-v1-128k">Moonshot V1 128K</option>
|
|
1181
1188
|
<option value="grok-4.20-beta-0309-non-reasoning">Grok 4.2 Beta (non-reasoning)</option>
|
|
1182
1189
|
<option value="grok-4.20-beta-0309-reasoning">Grok 4.2 Beta (reasoning)</option>
|
|
@@ -2405,6 +2412,22 @@
|
|
|
2405
2412
|
</div>
|
|
2406
2413
|
</div>
|
|
2407
2414
|
|
|
2415
|
+
<!-- OAuth / Subscription Providers (no API key needed) -->
|
|
2416
|
+
<div
|
|
2417
|
+
style="
|
|
2418
|
+
font-size: 11px;
|
|
2419
|
+
font-weight: 600;
|
|
2420
|
+
color: var(--text-2);
|
|
2421
|
+
text-transform: uppercase;
|
|
2422
|
+
letter-spacing: 0.08em;
|
|
2423
|
+
margin: 18px 0 10px;
|
|
2424
|
+
padding: 0 2px;
|
|
2425
|
+
"
|
|
2426
|
+
>
|
|
2427
|
+
Subscription Providers (no API key)
|
|
2428
|
+
</div>
|
|
2429
|
+
<div id="oauthProvidersList"></div>
|
|
2430
|
+
|
|
2408
2431
|
<!-- Search & Research Tools -->
|
|
2409
2432
|
<div
|
|
2410
2433
|
style="
|
|
@@ -5536,6 +5559,32 @@
|
|
|
5536
5559
|
</div>
|
|
5537
5560
|
</div>
|
|
5538
5561
|
|
|
5562
|
+
<!-- Testing -->
|
|
5563
|
+
<div class="view" id="testingView">
|
|
5564
|
+
<div class="page-header">
|
|
5565
|
+
<div>
|
|
5566
|
+
<div class="page-title">Testing</div>
|
|
5567
|
+
<div class="page-sub">
|
|
5568
|
+
Test suite results, group breakdown, and run history
|
|
5569
|
+
</div>
|
|
5570
|
+
</div>
|
|
5571
|
+
<div style="display: flex; align-items: center; gap: 8px">
|
|
5572
|
+
<button
|
|
5573
|
+
data-action="refreshTesting"
|
|
5574
|
+
class="btn-ghost"
|
|
5575
|
+
style="font-size: 12px"
|
|
5576
|
+
>
|
|
5577
|
+
↻ Refresh
|
|
5578
|
+
</button>
|
|
5579
|
+
</div>
|
|
5580
|
+
</div>
|
|
5581
|
+
<div id="testProgressBar"></div>
|
|
5582
|
+
<div id="testingContent">
|
|
5583
|
+
<div class="meta" style="padding: 20px">Loading test results...</div>
|
|
5584
|
+
</div>
|
|
5585
|
+
<div id="testingHistory"></div>
|
|
5586
|
+
</div>
|
|
5587
|
+
|
|
5539
5588
|
<!-- Benchmarks — ZeroEval / llm-stats leaderboards -->
|
|
5540
5589
|
<div class="view" id="benchmarksView">
|
|
5541
5590
|
<div class="page-header">
|
|
Binary file
|
|
Binary file
|
package/apps/vibe/server.mjs
CHANGED
|
@@ -13,7 +13,7 @@ import http from "node:http";
|
|
|
13
13
|
import os from "node:os";
|
|
14
14
|
import path from "node:path";
|
|
15
15
|
import fs from "node:fs";
|
|
16
|
-
import { spawn } from "node:child_process";
|
|
16
|
+
import { spawn, execFileSync } from "node:child_process";
|
|
17
17
|
import { fileURLToPath } from "node:url";
|
|
18
18
|
import { WebSocketServer } from "ws";
|
|
19
19
|
import { shouldSkipGeminiPassthroughLine } from "../../lib/gemini-cli-passthrough-noise.mjs";
|
|
@@ -752,60 +752,54 @@ function createCodexStreamRelay(onChunk) {
|
|
|
752
752
|
}
|
|
753
753
|
|
|
754
754
|
function createCrewCliStreamRelay(onChunk) {
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
let
|
|
758
|
-
let collectingAssistant = false;
|
|
759
|
-
|
|
760
|
-
const appendAssistant = (text) => {
|
|
761
|
-
const normalized = String(text || "").replace(/\r/g, "").trimEnd();
|
|
762
|
-
if (!normalized) return;
|
|
763
|
-
transcript = appendNormalizedChunk(transcript, normalized);
|
|
764
|
-
onChunk?.(`${normalized}\n`);
|
|
765
|
-
};
|
|
766
|
-
|
|
767
|
-
const handleLine = (line) => {
|
|
768
|
-
const cleaned = stripAnsi(line).replace(/\r/g, "");
|
|
769
|
-
rawTranscript = appendNormalizedChunk(rawTranscript, cleaned);
|
|
770
|
-
const trimmed = cleaned.trim();
|
|
771
|
-
if (!trimmed) {
|
|
772
|
-
if (collectingAssistant && transcript) {
|
|
773
|
-
appendAssistant("");
|
|
774
|
-
}
|
|
775
|
-
return;
|
|
776
|
-
}
|
|
777
|
-
|
|
778
|
-
if (trimmed === "--- Agent Response ---") {
|
|
779
|
-
collectingAssistant = true;
|
|
780
|
-
return;
|
|
781
|
-
}
|
|
782
|
-
|
|
783
|
-
if (trimmed === "Pipeline timeline:") {
|
|
784
|
-
collectingAssistant = false;
|
|
785
|
-
return;
|
|
786
|
-
}
|
|
787
|
-
|
|
788
|
-
if (collectingAssistant) {
|
|
789
|
-
appendAssistant(cleaned);
|
|
790
|
-
}
|
|
791
|
-
};
|
|
755
|
+
// Buffer ALL output — don't stream anything until we can extract the response.
|
|
756
|
+
// crew-cli emits logs + a JSON envelope; we only want the response field.
|
|
757
|
+
let rawOutput = "";
|
|
792
758
|
|
|
793
759
|
return {
|
|
794
760
|
push(chunk) {
|
|
795
|
-
|
|
796
|
-
while (lineBuffer.includes("\n")) {
|
|
797
|
-
const newlineIndex = lineBuffer.indexOf("\n");
|
|
798
|
-
const line = lineBuffer.slice(0, newlineIndex);
|
|
799
|
-
lineBuffer = lineBuffer.slice(newlineIndex + 1);
|
|
800
|
-
handleLine(line);
|
|
801
|
-
}
|
|
761
|
+
rawOutput += chunk.toString("utf8");
|
|
802
762
|
},
|
|
803
763
|
finish() {
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
764
|
+
const cleaned = stripAnsi(rawOutput).replace(/\r/g, "");
|
|
765
|
+
|
|
766
|
+
// Strategy 1: Find JSON envelope with "response" field
|
|
767
|
+
const jsonMatch = cleaned.match(/\{[\s\S]*"kind":\s*"[^"]+\.result"[\s\S]*\}/);
|
|
768
|
+
if (jsonMatch) {
|
|
769
|
+
try {
|
|
770
|
+
const parsed = JSON.parse(jsonMatch[0]);
|
|
771
|
+
if (parsed.response) {
|
|
772
|
+
onChunk?.(parsed.response);
|
|
773
|
+
return parsed.response;
|
|
774
|
+
}
|
|
775
|
+
} catch { /* fall through to other strategies */ }
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
// Strategy 2: Extract "response" field via regex (handles malformed JSON)
|
|
779
|
+
const respMatch = cleaned.match(/"response":\s*"((?:[^"\\]|\\.)*)"/);
|
|
780
|
+
if (respMatch) {
|
|
781
|
+
const response = respMatch[1].replace(/\\n/g, "\n").replace(/\\"/g, '"').replace(/\\t/g, "\t");
|
|
782
|
+
onChunk?.(response);
|
|
783
|
+
return response;
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
// Strategy 3: Legacy "--- Agent Response ---" marker
|
|
787
|
+
const markerIdx = cleaned.indexOf("--- Agent Response ---");
|
|
788
|
+
if (markerIdx >= 0) {
|
|
789
|
+
let response = cleaned.slice(markerIdx + "--- Agent Response ---".length);
|
|
790
|
+
const timelineIdx = response.indexOf("Pipeline timeline:");
|
|
791
|
+
if (timelineIdx >= 0) response = response.slice(0, timelineIdx);
|
|
792
|
+
response = response.trim();
|
|
793
|
+
if (response) {
|
|
794
|
+
onChunk?.(response);
|
|
795
|
+
return response;
|
|
796
|
+
}
|
|
807
797
|
}
|
|
808
|
-
|
|
798
|
+
|
|
799
|
+
// Fallback: send raw output (stripped of common log prefixes)
|
|
800
|
+
const fallback = summarizeCliFailure("crew-cli", cleaned);
|
|
801
|
+
onChunk?.(fallback);
|
|
802
|
+
return fallback;
|
|
809
803
|
},
|
|
810
804
|
};
|
|
811
805
|
}
|
|
@@ -957,6 +951,20 @@ function createCliRelay(engine, onChunk, onDone) {
|
|
|
957
951
|
return createDefaultCliRelay(onChunk);
|
|
958
952
|
}
|
|
959
953
|
|
|
954
|
+
function isClaudeOauthAuthenticated() {
|
|
955
|
+
try {
|
|
956
|
+
const output = execFileSync("claude", ["auth", "status"], {
|
|
957
|
+
encoding: "utf8",
|
|
958
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
959
|
+
}).trim().toLowerCase();
|
|
960
|
+
if (!output) return false;
|
|
961
|
+
if (/"loggedin"\s*:\s*true/.test(output)) return true;
|
|
962
|
+
return output.includes("logged in");
|
|
963
|
+
} catch {
|
|
964
|
+
return false;
|
|
965
|
+
}
|
|
966
|
+
}
|
|
967
|
+
|
|
960
968
|
function broadcastTerminalMessage(sessionId, payload) {
|
|
961
969
|
const session = terminalSessions.get(sessionId);
|
|
962
970
|
if (!session) return;
|
|
@@ -1093,12 +1101,13 @@ export function getCliCommand(engine, projectDir, message, modelOverride, resume
|
|
|
1093
1101
|
command: binary,
|
|
1094
1102
|
args: codexArgs,
|
|
1095
1103
|
stdin: null,
|
|
1104
|
+
stripEnv: ["OPENAI_API_KEY"],
|
|
1096
1105
|
};
|
|
1097
1106
|
}
|
|
1098
1107
|
case "claude":
|
|
1099
1108
|
// Claude Code uses OAuth — no API key needed
|
|
1100
1109
|
{
|
|
1101
|
-
const args = ["-p", "--setting-sources", "user", "--output-format", "stream-json", "--verbose"];
|
|
1110
|
+
const args = ["-p", "--setting-sources", "user", "--output-format", "stream-json", "--verbose", "--permission-mode", "auto"];
|
|
1102
1111
|
const model = modelOverride || process.env.CREWSWARM_CLAUDE_CODE_MODEL || "";
|
|
1103
1112
|
// Add workspace directory context
|
|
1104
1113
|
if (projectDir) args.push("--add-dir", projectDir);
|
|
@@ -1110,7 +1119,7 @@ export function getCliCommand(engine, projectDir, message, modelOverride, resume
|
|
|
1110
1119
|
command: "claude",
|
|
1111
1120
|
args,
|
|
1112
1121
|
stdin: null,
|
|
1113
|
-
stripEnv: ["CLAUDECODE", "CLAUDE_CODE"],
|
|
1122
|
+
stripEnv: ["CLAUDECODE", "CLAUDE_CODE", "ANTHROPIC_API_KEY"],
|
|
1114
1123
|
};
|
|
1115
1124
|
}
|
|
1116
1125
|
case "cursor":
|
|
@@ -1153,6 +1162,7 @@ export function getCliCommand(engine, projectDir, message, modelOverride, resume
|
|
|
1153
1162
|
command: "gemini",
|
|
1154
1163
|
args,
|
|
1155
1164
|
stdin: null,
|
|
1165
|
+
stripEnv: ["GEMINI_API_KEY", "GOOGLE_API_KEY"],
|
|
1156
1166
|
};
|
|
1157
1167
|
}
|
|
1158
1168
|
case "opencode":
|
|
@@ -1165,7 +1175,7 @@ export function getCliCommand(engine, projectDir, message, modelOverride, resume
|
|
|
1165
1175
|
model = cfg.opencodeModel || "";
|
|
1166
1176
|
} catch {}
|
|
1167
1177
|
}
|
|
1168
|
-
if (!model) model = "
|
|
1178
|
+
if (!model) model = "openai/gpt-5.2-codex";
|
|
1169
1179
|
const args = ["run", "-m", model, message];
|
|
1170
1180
|
// Add workspace directory context
|
|
1171
1181
|
if (projectDir) args.push("--dir", projectDir);
|
|
@@ -1175,12 +1185,13 @@ export function getCliCommand(engine, projectDir, message, modelOverride, resume
|
|
|
1175
1185
|
command: "opencode",
|
|
1176
1186
|
args,
|
|
1177
1187
|
stdin: null,
|
|
1188
|
+
stripEnv: ["OPENAI_API_KEY", "ANTHROPIC_API_KEY", "GEMINI_API_KEY"],
|
|
1178
1189
|
};
|
|
1179
1190
|
}
|
|
1180
1191
|
case "crew-cli": {
|
|
1181
1192
|
const crewBin = path.join(__dirname, "..", "..", "crew-cli", "bin", "crew.js");
|
|
1182
1193
|
const model = modelOverride || process.env.CREWSWARM_CREW_CLI_MODEL || "";
|
|
1183
|
-
const crewArgs = [crewBin, "chat", message, ...(projectDir ? ["--project", projectDir] : []), ...(model ? ["--model", model] : [])];
|
|
1194
|
+
const crewArgs = [crewBin, "chat", message, "--apply", "--json", ...(projectDir ? ["--project", projectDir] : []), ...(model ? ["--model", model] : [])];
|
|
1184
1195
|
// Resume: crew-cli supports --session for conversation continuity
|
|
1185
1196
|
if (resumeSession?.sessionId) crewArgs.push("--session", resumeSession.sessionId);
|
|
1186
1197
|
return {
|
|
@@ -1214,6 +1225,10 @@ export function runCli({
|
|
|
1214
1225
|
resume = true,
|
|
1215
1226
|
}) {
|
|
1216
1227
|
return new Promise((resolve, reject) => {
|
|
1228
|
+
if (engine === "claude" && !isClaudeOauthAuthenticated()) {
|
|
1229
|
+
reject(new Error("Claude Code requires CLI OAuth login. Run `claude auth login` and try again."));
|
|
1230
|
+
return;
|
|
1231
|
+
}
|
|
1217
1232
|
// Look up existing session for resume
|
|
1218
1233
|
const resumeKey = getCliResumeKey(engine, projectDir);
|
|
1219
1234
|
const resumeSession = resume ? cliResumeSessions.get(resumeKey) : undefined;
|
|
@@ -1857,6 +1872,36 @@ export const server = http.createServer(async (req, res) => {
|
|
|
1857
1872
|
return;
|
|
1858
1873
|
}
|
|
1859
1874
|
|
|
1875
|
+
// GET /api/studio/git-diff — return changed files with old/new content for diff preview
|
|
1876
|
+
if (parsedUrl.pathname === "/api/studio/git-diff" && req.method === "GET") {
|
|
1877
|
+
const projDir = parsedUrl.searchParams.get("projectDir") || WORKSPACE_DIR;
|
|
1878
|
+
const resolved = resolveStudioProjectPath(projDir, WORKSPACE_DIR);
|
|
1879
|
+
try {
|
|
1880
|
+
const { execSync } = await import("node:child_process");
|
|
1881
|
+
const hasGit = fs.existsSync(path.join(resolved, ".git"));
|
|
1882
|
+
if (!hasGit) {
|
|
1883
|
+
sendJson(res, 200, { ok: true, files: [], message: "Not a git repository" });
|
|
1884
|
+
return;
|
|
1885
|
+
}
|
|
1886
|
+
const filesRaw = execSync("git diff --name-only", { cwd: resolved, encoding: "utf8", timeout: 3000 }).trim();
|
|
1887
|
+
const files = filesRaw ? filesRaw.split("\n").filter(Boolean) : [];
|
|
1888
|
+
// For each changed file, get old (HEAD) and new (working tree) content
|
|
1889
|
+
const changes = [];
|
|
1890
|
+
for (const f of files.slice(0, 20)) {
|
|
1891
|
+
try {
|
|
1892
|
+
let oldContent = "";
|
|
1893
|
+
try { oldContent = execSync(`git show HEAD:${f}`, { cwd: resolved, encoding: "utf8", timeout: 3000 }); } catch { /* new file */ }
|
|
1894
|
+
const newContent = fs.readFileSync(path.join(resolved, f), "utf8");
|
|
1895
|
+
changes.push({ path: f, oldContent, newContent });
|
|
1896
|
+
} catch { /* skip unreadable */ }
|
|
1897
|
+
}
|
|
1898
|
+
sendJson(res, 200, { ok: true, files, changes });
|
|
1899
|
+
} catch (e) {
|
|
1900
|
+
sendJson(res, 200, { ok: true, files: [], changes: [], error: e.message });
|
|
1901
|
+
}
|
|
1902
|
+
return;
|
|
1903
|
+
}
|
|
1904
|
+
|
|
1860
1905
|
if (parsedUrl.pathname === "/api/studio/chat/unified" && req.method === "POST") {
|
|
1861
1906
|
try {
|
|
1862
1907
|
const body = await readBody(req);
|
|
@@ -313,7 +313,7 @@ function existsSync(path) {
|
|
|
313
313
|
function getDefaultModelForCLI(cliName) {
|
|
314
314
|
switch (cliName) {
|
|
315
315
|
case "opencode":
|
|
316
|
-
return process.env.CREWSWARM_OPENCODE_MODEL || "
|
|
316
|
+
return process.env.CREWSWARM_OPENCODE_MODEL || "openai/gpt-5.4";
|
|
317
317
|
case "cursor":
|
|
318
318
|
return process.env.CURSOR_DEFAULT_MODEL || "gemini-3-flash";
|
|
319
319
|
case "claude":
|
|
@@ -14,6 +14,7 @@ export function shouldDropPassthroughStderrLine(engine, line) {
|
|
|
14
14
|
if (/rmcp::/i.test(l)) return true;
|
|
15
15
|
if (/error decoding response body.*initialized notification/i.test(l))
|
|
16
16
|
return true;
|
|
17
|
+
if (/\[Executor\]\s+\w+\s+\((OAuth|API)\)/i.test(l)) return true;
|
|
17
18
|
if (
|
|
18
19
|
engine === "codex" &&
|
|
19
20
|
/worker quit with fatal/i.test(l) &&
|
|
@@ -198,7 +198,7 @@ export function getProjectMessageStats(projectId) {
|
|
|
198
198
|
|
|
199
199
|
const stats = {
|
|
200
200
|
total: messages.length,
|
|
201
|
-
|
|
201
|
+
bySource: {},
|
|
202
202
|
byAgent: {},
|
|
203
203
|
oldestMessage: null,
|
|
204
204
|
newestMessage: null,
|
|
@@ -206,8 +206,6 @@ export function getProjectMessageStats(projectId) {
|
|
|
206
206
|
assistantMessages: 0
|
|
207
207
|
};
|
|
208
208
|
|
|
209
|
-
stats.bySource = {};
|
|
210
|
-
|
|
211
209
|
for (const msg of messages) {
|
|
212
210
|
// Count by source
|
|
213
211
|
stats.bySource[msg.source] = (stats.bySource[msg.source] || 0) + 1;
|
|
@@ -381,8 +379,8 @@ export function buildMessageTree(projectId, rootId = null) {
|
|
|
381
379
|
if (msg.parentId && messageMap.has(msg.parentId)) {
|
|
382
380
|
// Add to parent's children
|
|
383
381
|
messageMap.get(msg.parentId).children.push(node);
|
|
384
|
-
} else
|
|
385
|
-
// Root node (no parent) or
|
|
382
|
+
} else {
|
|
383
|
+
// Root node (no parent), requested root, or orphan (parentId references missing message)
|
|
386
384
|
roots.push(node);
|
|
387
385
|
}
|
|
388
386
|
}
|
|
@@ -19,7 +19,7 @@ import { homedir } from "node:os";
|
|
|
19
19
|
const CLI_PROCESS_LOG = join(homedir(), ".crewswarm", "logs", "cli-processes.jsonl");
|
|
20
20
|
const CLI_PROCESS_STATE = join(homedir(), ".crewswarm", "cli-process-state.json");
|
|
21
21
|
|
|
22
|
-
mkdirSync(join(homedir(), ".crewswarm", "logs"), { recursive: true });
|
|
22
|
+
try { mkdirSync(join(homedir(), ".crewswarm", "logs"), { recursive: true }); } catch { /* read-only homedir */ }
|
|
23
23
|
|
|
24
24
|
// Active processes: { processId: { pid, agent, cli, task, startTime, lastActivity, status, chatId, sessionId } }
|
|
25
25
|
let activeProcesses = new Map();
|
|
@@ -121,10 +121,11 @@ export function completeCLIProcess(processId, result) {
|
|
|
121
121
|
log("info", "CLI process completed", { processId, ...result });
|
|
122
122
|
|
|
123
123
|
// Remove from active list after 30s (keep recent history visible)
|
|
124
|
-
setTimeout(() => {
|
|
124
|
+
const timer = setTimeout(() => {
|
|
125
125
|
activeProcesses.delete(processId);
|
|
126
126
|
saveState();
|
|
127
127
|
}, 30000);
|
|
128
|
+
timer.unref(); // Don't prevent process exit in tests
|
|
128
129
|
}
|
|
129
130
|
|
|
130
131
|
/**
|