crewswarm 0.9.2 → 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.
Files changed (228) hide show
  1. package/README.md +22 -9
  2. package/apps/dashboard/dist/assets/chat-core-uXb_C0GM.js +1 -0
  3. package/apps/dashboard/dist/assets/chat-core-uXb_C0GM.js.br +0 -0
  4. package/apps/dashboard/dist/assets/cli-process-CNZ_UBCt.js +1 -0
  5. package/apps/dashboard/dist/assets/cli-process-CNZ_UBCt.js.br +0 -0
  6. package/apps/dashboard/dist/assets/index-BeVllEj_.js +2 -0
  7. package/apps/dashboard/dist/assets/index-BeVllEj_.js.br +0 -0
  8. package/apps/dashboard/dist/assets/{index-CF0aJRtC.css → index-D-sRshvg.css} +1 -1
  9. package/apps/dashboard/dist/assets/index-D-sRshvg.css.br +0 -0
  10. package/apps/dashboard/dist/assets/tab-benchmarks-tab-BHjKCPm3.js.br +0 -0
  11. package/apps/dashboard/dist/assets/tab-models-tab-dNRgsTOO.js +1 -0
  12. package/apps/dashboard/dist/assets/tab-models-tab-dNRgsTOO.js.br +0 -0
  13. package/apps/dashboard/dist/assets/{tab-pm-loop-tab-Bfd449B4.js → tab-pm-loop-tab-DiAPTJXu.js} +1 -1
  14. package/apps/dashboard/dist/assets/tab-pm-loop-tab-DiAPTJXu.js.br +0 -0
  15. package/apps/dashboard/dist/assets/{tab-projects-tab-DhNWnlzt.js → tab-projects-tab-SFH4E--a.js} +1 -1
  16. package/apps/dashboard/dist/assets/tab-projects-tab-SFH4E--a.js.br +0 -0
  17. package/apps/dashboard/dist/assets/tab-settings-tab-CuvH_Fj_.js +1 -0
  18. package/apps/dashboard/dist/assets/tab-settings-tab-CuvH_Fj_.js.br +0 -0
  19. package/apps/dashboard/dist/assets/tab-skills-tab-DR7PJ7NB.js +1 -0
  20. package/apps/dashboard/dist/assets/tab-skills-tab-DR7PJ7NB.js.br +0 -0
  21. package/apps/dashboard/dist/assets/tab-testing-tab-CezZOZcJ.js +1 -0
  22. package/apps/dashboard/dist/assets/tab-testing-tab-CezZOZcJ.js.br +0 -0
  23. package/apps/dashboard/dist/index.html +135 -15
  24. package/apps/dashboard/dist/index.html.br +0 -0
  25. package/apps/dashboard/dist/index.html.gz +0 -0
  26. package/apps/vibe/README.md +2 -2
  27. package/apps/vibe/package.json +1 -1
  28. package/apps/vibe/server.mjs +101 -56
  29. package/crew-lead.mjs +34 -4
  30. package/lib/bridges/cli-executor.mjs +1 -1
  31. package/lib/bridges/gateway-ws.mjs +4 -0
  32. package/lib/browser/passthrough-stderr.js +1 -0
  33. package/lib/chat/project-messages.mjs +3 -5
  34. package/lib/cli-process-tracker.mjs +3 -2
  35. package/lib/contacts/identity-linker.mjs +1 -0
  36. package/lib/crew-judge/judge.mjs +19 -18
  37. package/lib/crew-lead/agent-manager.mjs +1 -1
  38. package/lib/crew-lead/background.mjs +14 -1
  39. package/lib/crew-lead/chat-handler.mjs +38 -1
  40. package/lib/crew-lead/http-server.mjs +106 -57
  41. package/lib/crew-lead/llm-caller.mjs +24 -8
  42. package/lib/crew-lead/prompts.mjs +14 -1
  43. package/lib/crew-lead/tools.mjs +3 -2
  44. package/lib/crew-lead/wave-dispatcher.mjs +19 -5
  45. package/lib/crew-lead/ws-router.mjs +219 -27
  46. package/lib/engines/crew-cli.mjs +1 -1
  47. package/lib/engines/engine-registry.mjs +14 -3
  48. package/lib/engines/rt-envelope.mjs +1 -0
  49. package/lib/engines/runners.mjs +28 -4
  50. package/lib/gemini-cli-passthrough-noise.mjs +1 -1
  51. package/lib/integrations/code-search.mjs +4 -3
  52. package/lib/memory/shared-adapter.mjs +23 -10
  53. package/lib/pipeline/manager.mjs +2 -1
  54. package/lib/runtime/config.mjs +1 -1
  55. package/lib/runtime/paths.mjs +12 -8
  56. package/lib/runtime/spending.mjs +2 -1
  57. package/package.json +42 -14
  58. package/scripts/capture-build-flow.mjs +118 -0
  59. package/scripts/coverage-report.mjs +209 -0
  60. package/scripts/coverage-summary.mjs +47 -0
  61. package/scripts/dashboard-validation.mjs +76 -0
  62. package/scripts/dashboard.mjs +1667 -551
  63. package/scripts/generate-openapi.mjs +683 -277
  64. package/scripts/live-bridge-matrix.mjs +79 -0
  65. package/scripts/live-cli-matrix.mjs +166 -0
  66. package/scripts/live-crewchat-check.mjs +42 -0
  67. package/scripts/live-engine-matrix.mjs +50 -0
  68. package/scripts/live-provider-failover-matrix.mjs +107 -0
  69. package/scripts/live-provider-matrix.mjs +228 -0
  70. package/scripts/restart-all-from-repo.sh +4 -4
  71. package/scripts/restart-service.sh +12 -9
  72. package/scripts/smoke-dispatch.mjs +4 -1
  73. package/scripts/test-blast-radius.mjs +204 -0
  74. package/scripts/test-report-summary.mjs +88 -0
  75. package/scripts/test-reporter.mjs +651 -0
  76. package/scripts/test-rerun.mjs +136 -0
  77. package/scripts/tmux-bridge +130 -0
  78. package/apps/dashboard/dist/assets/chat-core-Cx4sTxDd.js +0 -1
  79. package/apps/dashboard/dist/assets/chat-core-Cx4sTxDd.js.br +0 -0
  80. package/apps/dashboard/dist/assets/cli-process-COMRNPqr.js +0 -1
  81. package/apps/dashboard/dist/assets/cli-process-COMRNPqr.js.br +0 -0
  82. package/apps/dashboard/dist/assets/index-CF0aJRtC.css.br +0 -0
  83. package/apps/dashboard/dist/assets/index-DnClJ1ee.js +0 -2
  84. package/apps/dashboard/dist/assets/index-DnClJ1ee.js.br +0 -0
  85. package/apps/dashboard/dist/assets/tab-models-tab-BLEjmd19.js +0 -1
  86. package/apps/dashboard/dist/assets/tab-models-tab-BLEjmd19.js.br +0 -0
  87. package/apps/dashboard/dist/assets/tab-pm-loop-tab-Bfd449B4.js.br +0 -0
  88. package/apps/dashboard/dist/assets/tab-projects-tab-DhNWnlzt.js.br +0 -0
  89. package/apps/dashboard/dist/assets/tab-settings-tab-Bn4nXtDe.js +0 -1
  90. package/apps/dashboard/dist/assets/tab-settings-tab-Bn4nXtDe.js.br +0 -0
  91. package/apps/dashboard/dist/assets/tab-skills-tab-BpY0uZHW.js +0 -1
  92. package/apps/dashboard/dist/assets/tab-skills-tab-BpY0uZHW.js.br +0 -0
  93. package/apps/dashboard/index.html +0 -6529
  94. package/apps/dashboard/package.json +0 -15
  95. package/apps/dashboard/src/app.js +0 -2828
  96. package/apps/dashboard/src/app.js.br +0 -0
  97. package/apps/dashboard/src/app.js.gz +0 -0
  98. package/apps/dashboard/src/chat/chat-actions.js +0 -1847
  99. package/apps/dashboard/src/chat/chat-actions.js.br +0 -0
  100. package/apps/dashboard/src/chat/unified-messages.js +0 -327
  101. package/apps/dashboard/src/chat/unified-messages.js.br +0 -0
  102. package/apps/dashboard/src/cli-process.js +0 -208
  103. package/apps/dashboard/src/cli-process.js.br +0 -0
  104. package/apps/dashboard/src/cli-process.js.gz +0 -0
  105. package/apps/dashboard/src/components/active-tasks-panel.js +0 -175
  106. package/apps/dashboard/src/components/active-tasks-panel.js.br +0 -0
  107. package/apps/dashboard/src/core/api.js +0 -18
  108. package/apps/dashboard/src/core/api.js.br +0 -0
  109. package/apps/dashboard/src/core/dom.js +0 -228
  110. package/apps/dashboard/src/core/dom.js.br +0 -0
  111. package/apps/dashboard/src/core/state.js +0 -91
  112. package/apps/dashboard/src/core/state.js.br +0 -0
  113. package/apps/dashboard/src/core/task-manager.js +0 -134
  114. package/apps/dashboard/src/core/task-manager.js.br +0 -0
  115. package/apps/dashboard/src/orchestration-status.js +0 -127
  116. package/apps/dashboard/src/orchestration-status.js.br +0 -0
  117. package/apps/dashboard/src/setup-wizard.js +0 -562
  118. package/apps/dashboard/src/setup-wizard.js.br +0 -0
  119. package/apps/dashboard/src/styles.css +0 -2085
  120. package/apps/dashboard/src/styles.css.br +0 -0
  121. package/apps/dashboard/src/styles.css.gz +0 -0
  122. package/apps/dashboard/src/tabs/agents-tab.js +0 -2237
  123. package/apps/dashboard/src/tabs/agents-tab.js.br +0 -0
  124. package/apps/dashboard/src/tabs/benchmarks-tab.js +0 -229
  125. package/apps/dashboard/src/tabs/benchmarks-tab.js.br +0 -0
  126. package/apps/dashboard/src/tabs/comms-tab.js +0 -955
  127. package/apps/dashboard/src/tabs/comms-tab.js.br +0 -0
  128. package/apps/dashboard/src/tabs/contacts-tab.js +0 -654
  129. package/apps/dashboard/src/tabs/contacts-tab.js.br +0 -0
  130. package/apps/dashboard/src/tabs/engines-tab.js +0 -175
  131. package/apps/dashboard/src/tabs/engines-tab.js.br +0 -0
  132. package/apps/dashboard/src/tabs/memory-tab.js +0 -182
  133. package/apps/dashboard/src/tabs/memory-tab.js.br +0 -0
  134. package/apps/dashboard/src/tabs/models-tab.js +0 -450
  135. package/apps/dashboard/src/tabs/models-tab.js.br +0 -0
  136. package/apps/dashboard/src/tabs/pm-loop-tab.js +0 -185
  137. package/apps/dashboard/src/tabs/pm-loop-tab.js.br +0 -0
  138. package/apps/dashboard/src/tabs/projects-tab.js +0 -663
  139. package/apps/dashboard/src/tabs/projects-tab.js.br +0 -0
  140. package/apps/dashboard/src/tabs/projects-tab.js.gz +0 -0
  141. package/apps/dashboard/src/tabs/prompts-tab.js +0 -160
  142. package/apps/dashboard/src/tabs/prompts-tab.js.br +0 -0
  143. package/apps/dashboard/src/tabs/services-tab.js +0 -202
  144. package/apps/dashboard/src/tabs/services-tab.js.br +0 -0
  145. package/apps/dashboard/src/tabs/settings-tab.js +0 -861
  146. package/apps/dashboard/src/tabs/settings-tab.js.br +0 -0
  147. package/apps/dashboard/src/tabs/skills-tab.js +0 -284
  148. package/apps/dashboard/src/tabs/skills-tab.js.br +0 -0
  149. package/apps/dashboard/src/tabs/spending-tab.js +0 -173
  150. package/apps/dashboard/src/tabs/spending-tab.js.br +0 -0
  151. package/apps/dashboard/src/tabs/swarm-chat-tab.js +0 -660
  152. package/apps/dashboard/src/tabs/swarm-chat-tab.js.br +0 -0
  153. package/apps/dashboard/src/tabs/swarm-tab.js +0 -538
  154. package/apps/dashboard/src/tabs/swarm-tab.js.br +0 -0
  155. package/apps/dashboard/src/tabs/usage-tab.js +0 -390
  156. package/apps/dashboard/src/tabs/usage-tab.js.br +0 -0
  157. package/apps/dashboard/src/tabs/waves-tab.js +0 -238
  158. package/apps/dashboard/src/tabs/waves-tab.js.br +0 -0
  159. package/apps/dashboard/src/tabs/workflows-tab.js +0 -747
  160. package/apps/dashboard/src/tabs/workflows-tab.js.br +0 -0
  161. package/apps/vibe/.crew/agent-memory/pipeline.json +0 -304
  162. package/apps/vibe/.crew/cost.json +0 -17
  163. package/apps/vibe/.crew/json-parse-metrics.jsonl +0 -27
  164. package/apps/vibe/.crew/pipeline-metrics.jsonl +0 -27
  165. package/apps/vibe/.crew/pipeline-runs/pipeline-0f90c392-2425-4ae5-850c-bd9d17b1d690.jsonl +0 -5
  166. package/apps/vibe/.crew/pipeline-runs/pipeline-1c269dd9-a63f-4fba-af81-5cf08048ef06.jsonl +0 -5
  167. package/apps/vibe/.crew/pipeline-runs/pipeline-288a7765-da24-4a22-89bc-1f3cc9b0562c.jsonl +0 -5
  168. package/apps/vibe/.crew/pipeline-runs/pipeline-2c78fd22-a657-4bd1-bc49-0679fb384409.jsonl +0 -5
  169. package/apps/vibe/.crew/pipeline-runs/pipeline-3da23550-22ed-4904-9a0a-8e79c1f3024c.jsonl +0 -5
  170. package/apps/vibe/.crew/pipeline-runs/pipeline-3e6fe08d-3264-404a-8df3-aab7efef10e7.jsonl +0 -5
  171. package/apps/vibe/.crew/pipeline-runs/pipeline-42eec610-57fe-4e09-9e7e-b315038495c2.jsonl +0 -5
  172. package/apps/vibe/.crew/pipeline-runs/pipeline-4438eb4c-ae13-42b1-90e2-b043d8983be8.jsonl +0 -5
  173. package/apps/vibe/.crew/pipeline-runs/pipeline-4740a9f5-86e7-44b6-a394-de433e291727.jsonl +0 -5
  174. package/apps/vibe/.crew/pipeline-runs/pipeline-49e1da6a-957e-48fd-9220-415019e4f8e2.jsonl +0 -5
  175. package/apps/vibe/.crew/pipeline-runs/pipeline-4c9251db-be68-427b-a3fc-a264f2b5778d.jsonl +0 -5
  176. package/apps/vibe/.crew/pipeline-runs/pipeline-6413fa33-a802-4b57-a8c0-a9056ad67842.jsonl +0 -5
  177. package/apps/vibe/.crew/pipeline-runs/pipeline-65e29a57-664d-4196-8109-017e364f182e.jsonl +0 -5
  178. package/apps/vibe/.crew/pipeline-runs/pipeline-6aa04bc5-9593-4b1f-b58d-3bf2978cb602.jsonl +0 -5
  179. package/apps/vibe/.crew/pipeline-runs/pipeline-6e1cba53-9b70-457e-99e0-59199149dd21.jsonl +0 -5
  180. package/apps/vibe/.crew/pipeline-runs/pipeline-749f41cc-4dac-4204-be64-873a6080a0d2.jsonl +0 -5
  181. package/apps/vibe/.crew/pipeline-runs/pipeline-74d68121-e181-4864-bd9a-c3211341dfaf.jsonl +0 -5
  182. package/apps/vibe/.crew/pipeline-runs/pipeline-8509bc24-142d-4e07-b44a-a50bf99d1103.jsonl +0 -5
  183. package/apps/vibe/.crew/pipeline-runs/pipeline-960339c6-07ca-43ce-9900-f6e1702b39b9.jsonl +0 -5
  184. package/apps/vibe/.crew/pipeline-runs/pipeline-9bef2dd2-6122-42e5-b3d9-19f4d80f9e40.jsonl +0 -5
  185. package/apps/vibe/.crew/pipeline-runs/pipeline-9c6480a9-7031-4146-b241-825b9a2d1de1.jsonl +0 -5
  186. package/apps/vibe/.crew/pipeline-runs/pipeline-9fd42426-8492-4157-9d5f-e1537c060489.jsonl +0 -2
  187. package/apps/vibe/.crew/pipeline-runs/pipeline-ad6d40a3-2f5e-46a9-a345-47caaccc51aa.jsonl +0 -5
  188. package/apps/vibe/.crew/pipeline-runs/pipeline-bc606133-8d5b-4535-8d85-f1a29cdaa981.jsonl +0 -5
  189. package/apps/vibe/.crew/pipeline-runs/pipeline-c1418f4e-b773-4ca1-84a3-216acf36e2f2.jsonl +0 -5
  190. package/apps/vibe/.crew/pipeline-runs/pipeline-c1a13ccd-634a-4d01-a4a7-1177b8a752ff.jsonl +0 -5
  191. package/apps/vibe/.crew/pipeline-runs/pipeline-c7d27b42-249e-4bd4-8f26-6aa998110b8a.jsonl +0 -5
  192. package/apps/vibe/.crew/pipeline-runs/pipeline-cca2e9b9-4a34-4d25-a311-5c793fa7e91e.jsonl +0 -5
  193. package/apps/vibe/.crew/sandbox.json +0 -7
  194. package/apps/vibe/.crew/session.json +0 -330
  195. package/apps/vibe/.crew/training-data.jsonl +0 -0
  196. package/apps/vibe/.github/workflows/studio-quality.yml +0 -37
  197. package/apps/vibe/.studio-data/project-messages/chuck-norris.jsonl +0 -18
  198. package/apps/vibe/.studio-data/project-messages/general.jsonl +0 -81
  199. package/apps/vibe/.studio-data/project-messages/studio-local.jsonl +0 -18
  200. package/apps/vibe/ARCHITECTURE.md +0 -3393
  201. package/apps/vibe/QUICK-REFERENCE.md +0 -211
  202. package/apps/vibe/ROADMAP.md +0 -41
  203. package/apps/vibe/STUDIO-SETUP-COMPLETE.md +0 -35
  204. package/apps/vibe/VISUAL-GUIDE.md +0 -378
  205. package/apps/vibe/capture-demo.mjs +0 -160
  206. package/apps/vibe/capture-full-demo.mjs +0 -255
  207. package/apps/vibe/capture-quickstart.mjs +0 -256
  208. package/apps/vibe/capture-vibe-assets.mjs +0 -71
  209. package/apps/vibe/capture-vibe-video.mjs +0 -260
  210. package/apps/vibe/check-buttons.js +0 -41
  211. package/apps/vibe/diagnose.html +0 -106
  212. package/apps/vibe/fix-buttons.js +0 -103
  213. package/apps/vibe/index.html +0 -3404
  214. package/apps/vibe/package-lock.json +0 -920
  215. package/apps/vibe/scripts/studio-pty-host.py +0 -117
  216. package/apps/vibe/src/main.js +0 -2940
  217. package/apps/vibe/src/register-all-languages.js +0 -98
  218. package/apps/vibe/start-studio.sh +0 -11
  219. package/apps/vibe/test/accessibility-tests.js +0 -77
  220. package/apps/vibe/test/browser-performance-audit.mjs +0 -205
  221. package/apps/vibe/test/performance-tests.js +0 -120
  222. package/apps/vibe/test/security-tests.js +0 -213
  223. package/apps/vibe/tests/e2e.local.mjs +0 -54
  224. package/apps/vibe/tests/server.smoke.mjs +0 -106
  225. package/apps/vibe/update_website.mjs +0 -74
  226. package/apps/vibe/vite.config.js +0 -19
  227. package/lib/crew-lead/chat-handler.mjs.bak +0 -1274
  228. package/lib/engines/rt-envelope.mjs.backup-current +0 -870
@@ -1,284 +0,0 @@
1
- /**
2
- * Skills tab — extracted from app.js
3
- * Deps: getJSON, postJSON, showNotification (from core/)
4
- */
5
-
6
- import { getJSON, postJSON } from '../core/api.js';
7
- import { showNotification } from '../core/dom.js';
8
-
9
- let _skillsCache = [];
10
-
11
- export function showSkills() {
12
- document.querySelectorAll('.view').forEach(v => v.classList.remove('active'));
13
- document.getElementById('skillsView').classList.add('active');
14
- document.querySelectorAll('.nav-item').forEach(n => n.classList.remove('active'));
15
- const nav = document.getElementById('navSkills');
16
- if (nav) nav.classList.add('active');
17
- loadSkills();
18
- if (typeof loadPendingApprovals === 'function') loadPendingApprovals();
19
- }
20
-
21
- export function showRunSkills() {
22
- document.querySelectorAll('.view').forEach(v => v.classList.remove('active'));
23
- const view = document.getElementById('runSkillsView');
24
- if (view) view.classList.add('active');
25
- document.querySelectorAll('.nav-item').forEach(n => n.classList.remove('active'));
26
- const nav = document.getElementById('navRunSkills');
27
- if (nav) nav.classList.add('active');
28
- loadRunSkills();
29
- }
30
-
31
- export async function loadRunSkills() {
32
- const el = document.getElementById('runSkillsGrid');
33
- if (!el) return;
34
- try {
35
- const d = await (await fetch('/api/health')).json();
36
- const skills = (d.skills || []).filter(s => !s.error && s.url);
37
- if (!skills.length) {
38
- el.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>';
39
- return;
40
- }
41
- el.innerHTML = skills.map(s => {
42
- const defaults = s.defaultParams && Object.keys(s.defaultParams).length
43
- ? JSON.stringify(s.defaultParams, null, 2) : '{}';
44
- const paramHint = (s.paramNotes || s.description || '').slice(0, 120);
45
- const safeName = (s.name || '').replace(/"/g, '&quot;');
46
- return '<div class="card" style="display:flex;flex-direction:column;">'
47
- + '<div class="card-title" style="margin-bottom:6px;">' + (s.name || 'unnamed') + '</div>'
48
- + '<div style="font-size:12px;color:var(--text-3);margin-bottom:10px;line-height:1.4;">' + (s.description || '') + '</div>'
49
- + (paramHint ? '<div style="font-size:11px;color:var(--text-2);margin-bottom:8px;">' + paramHint + '</div>' : '')
50
- + '<label style="font-size:11px;color:var(--text-2);margin-bottom:4px;">Params (JSON)</label>'
51
- + '<textarea data-skill="' + safeName + '" rows="4" style="font-family:monospace;font-size:12px;width:100%;margin-bottom:10px;resize:vertical;" class="runskills-params">' + defaults.replace(/</g, '&lt;') + '</textarea>'
52
- + '<div style="display:flex;align-items:center;gap:8px;margin-top:auto;">'
53
- + '<button class="btn-green" style="font-size:12px;" data-action="runSkillFromUI" data-arg="' + safeName + '">Run</button>'
54
- + '<span class="runskills-result" data-skill="' + safeName + '" style="font-size:11px;color:var(--text-3);"></span>'
55
- + '</div></div>';
56
- }).join('');
57
- } catch (e) {
58
- el.innerHTML = '<div style="color:var(--red);font-size:12px;">Error loading health/skills: ' + (e.message || '') + '</div>';
59
- }
60
- }
61
-
62
- export async function runSkillFromUI(skillName) {
63
- const textarea = document.querySelector('.runskills-params[data-skill="' + (skillName || '').replace(/"/g, '\\"') + '"]');
64
- const resultEl = document.querySelector('.runskills-result[data-skill="' + (skillName || '').replace(/"/g, '\\"') + '"]');
65
- if (!textarea) return;
66
- let params = {};
67
- try { params = JSON.parse(textarea.value.trim() || '{}'); } catch (e) {
68
- if (resultEl) resultEl.textContent = 'Invalid JSON';
69
- return;
70
- }
71
- if (resultEl) resultEl.textContent = 'Running…';
72
- try {
73
- const r = await fetch('/api/skills/' + encodeURIComponent(skillName) + '/run', {
74
- method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ params })
75
- });
76
- const data = await r.json();
77
- if (resultEl) {
78
- if (data.ok) resultEl.textContent = 'Done';
79
- else resultEl.textContent = data.error || 'Error';
80
- resultEl.style.color = data.ok ? 'var(--green)' : 'var(--red)';
81
- }
82
- if (!data.ok) return;
83
- if (data.result !== undefined && resultEl) {
84
- const preview = typeof data.result === 'string' ? data.result : JSON.stringify(data.result).slice(0, 120);
85
- resultEl.textContent = preview + (preview.length >= 120 ? '…' : '');
86
- }
87
- } catch (e) {
88
- if (resultEl) { resultEl.textContent = e.message || 'Request failed'; resultEl.style.color = 'var(--red)'; }
89
- }
90
- }
91
-
92
- export async function loadSkills() {
93
- const el = document.getElementById('skillsList');
94
- try {
95
- const d = await (await fetch('/api/skills')).json();
96
- _skillsCache = d.skills || [];
97
- renderSkillsList(_skillsCache);
98
- } catch(e) {
99
- if (el) el.innerHTML = '<div style="color:var(--text-3);font-size:12px;">Error loading skills</div>';
100
- }
101
- }
102
-
103
- export function renderSkillsList(skills) {
104
- const el = document.getElementById('skillsList');
105
- if (!el) return;
106
- if (!skills.length) {
107
- el.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>';
108
- return;
109
- }
110
-
111
- const apiSkills = skills.filter(s => s.type === 'api' || (!s.type && s.url));
112
- const knowledgeSkills = skills.filter(s => s.type === 'knowledge' || (!s.type && !s.url));
113
-
114
- function renderSkillRow(s) {
115
- const isKnowledge = s.type === 'knowledge';
116
- const approvalBadge = s.requiresApproval
117
- ? '<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>' : '';
118
- const typeBadge = isKnowledge
119
- ? '<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>'
120
- : '<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>';
121
- const urlNote = s.url
122
- ? ' · <code style="background:var(--bg-1);padding:1px 4px;border-radius:3px;">' + (s.method||'POST') + ' ' + (s.url||'').slice(0,55) + '</code>' : '';
123
- const aliasNote = (s.aliases && s.aliases.length)
124
- ? '<span style="margin-left:6px;font-size:10px;color:var(--text-3);">aliases: ' + s.aliases.join(', ') + '</span>' : '';
125
- 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);">'
126
- + '<div style="min-width:0;">'
127
- + '<div style="display:flex;align-items:center;flex-wrap:wrap;gap:2px;">'
128
- + '<span style="font-weight:600;font-size:13px;">' + s.name + '</span>'
129
- + typeBadge + approvalBadge + aliasNote
130
- + '</div>'
131
- + '<div style="font-size:11px;color:var(--text-3);margin-top:3px;">' + (s.description||'') + urlNote + '</div>'
132
- + '</div>'
133
- + '<div style="display:flex;gap:6px;flex-shrink:0;margin-left:12px;">'
134
- + (isKnowledge ? '' : '<button class="btn-ghost" style="font-size:11px;" data-action="editSkill" data-arg="' + s.name + '">Edit</button>')
135
- + '<button class="btn-ghost" style="font-size:11px;color:var(--red);" data-action="deleteSkill" data-arg="' + s.name + '">Delete</button>'
136
- + '</div></div>';
137
- }
138
-
139
- function renderSection(title, items, emptyMsg) {
140
- if (!items.length) return '';
141
- return '<div style="margin-bottom:20px;">'
142
- + '<div style="font-size:11px;font-weight:600;letter-spacing:.06em;text-transform:uppercase;color:var(--text-3);margin-bottom:8px;">' + title + ' <span style="font-weight:400;opacity:.7;">(' + items.length + ')</span></div>'
143
- + '<div style="display:flex;flex-direction:column;gap:6px;">' + items.map(renderSkillRow).join('') + '</div>'
144
- + '</div>';
145
- }
146
-
147
- el.innerHTML = renderSection('Knowledge', knowledgeSkills)
148
- + renderSection('API Integrations', apiSkills);
149
- }
150
-
151
- export function filterSkills(q) {
152
- const lower = q.toLowerCase();
153
- renderSkillsList(lower ? _skillsCache.filter(s =>
154
- (s.name||'').toLowerCase().includes(lower) ||
155
- (s.description||'').toLowerCase().includes(lower) ||
156
- (s.url||'').toLowerCase().includes(lower) ||
157
- (s.aliases||[]).some(a => a.toLowerCase().includes(lower))
158
- ) : _skillsCache);
159
- }
160
-
161
- export function editSkill(name) {
162
- const s = _skillsCache.find(x => x.name === name);
163
- if (!s) return;
164
- document.getElementById('skEditName').value = name;
165
- document.getElementById('addSkillFormTitle').textContent = 'Edit Skill';
166
- document.getElementById('saveSkillBtn').textContent = 'Update Skill';
167
- document.getElementById('skName').value = s.name || '';
168
- document.getElementById('skDesc').value = s.description || '';
169
- document.getElementById('skUrl').value = s.url || '';
170
- const meth = document.getElementById('skMethod');
171
- for (let i = 0; i < meth.options.length; i++) if (meth.options[i].value === s.method) { meth.selectedIndex = i; break; }
172
- const authType = s.auth?.type || '';
173
- document.getElementById('skAuthType').value = authType;
174
- document.getElementById('skAuthKey').value = s.auth?.keyFrom || s.auth?.token || '';
175
- document.getElementById('skAuthHeader').value = s.auth?.header || '';
176
- document.getElementById('skRequiresApproval').checked = !!s.requiresApproval;
177
- document.getElementById('skDefaults').value = s.defaultParams && Object.keys(s.defaultParams).length ? JSON.stringify(s.defaultParams, null, 2) : '';
178
- updateSkillAuthFields();
179
- const f = document.getElementById('addSkillForm');
180
- f.style.display = 'block';
181
- f.scrollIntoView({ behavior: 'smooth', block: 'start' });
182
- }
183
-
184
- export function toggleAddSkill() {
185
- cancelSkillForm();
186
- document.getElementById('importSkillForm').style.display = 'none';
187
- const f = document.getElementById('addSkillForm');
188
- f.style.display = f.style.display === 'none' ? 'block' : 'none';
189
- }
190
-
191
- export function toggleImportSkill() {
192
- cancelSkillForm();
193
- const f = document.getElementById('importSkillForm');
194
- f.style.display = f.style.display === 'none' ? 'block' : 'none';
195
- if (f.style.display !== 'none') setTimeout(() => document.getElementById('importSkillUrl').focus(), 50);
196
- }
197
-
198
- export async function importSkillFromUrl() {
199
- const urlInput = document.getElementById('importSkillUrl');
200
- const status = document.getElementById('importSkillStatus');
201
- const btn = document.getElementById('importSkillBtn');
202
- const skillUrl = urlInput.value.trim();
203
- if (!skillUrl) { status.style.color = 'var(--red)'; status.textContent = 'Paste a URL first.'; return; }
204
- btn.disabled = true; btn.textContent = 'Importing…';
205
- status.style.color = 'var(--text-3)'; status.textContent = 'Fetching & scanning…';
206
- try {
207
- const r = await fetch('/api/skills/import', { method: 'POST', headers: {'content-type':'application/json'}, body: JSON.stringify({ url: skillUrl }) });
208
- const d = await r.json();
209
- if (!r.ok || d.error) throw new Error(d.error || 'Import failed');
210
- if (d.warnings && d.warnings.length) {
211
- status.style.color = 'var(--yellow)';
212
- const warnLabels = { cmd_skill: '⚠ executes shell commands', ssrf_risk: '⚠ targets private network', insecure_url: '⚠ non-HTTPS endpoint', no_approval: '⚠ no approval gate on write' };
213
- const msgs = d.warnings.map(w => warnLabels[w.split(':')[0]] || w);
214
- status.innerHTML = '✓ Imported <strong>"' + d.name + '"</strong> — ' + msgs.join(' · ');
215
- } else {
216
- status.style.color = 'var(--green)';
217
- status.textContent = '✓ Imported "' + d.name + '" — no security warnings';
218
- }
219
- urlInput.value = '';
220
- await loadSkills();
221
- if (!d.warnings || !d.warnings.length) {
222
- setTimeout(() => { document.getElementById('importSkillForm').style.display = 'none'; status.textContent = ''; }, 3000);
223
- }
224
- } catch(e) {
225
- status.style.color = 'var(--red)';
226
- status.textContent = 'Error: ' + e.message;
227
- } finally { btn.disabled = false; btn.textContent = 'Import'; }
228
- }
229
-
230
- export function cancelSkillForm() {
231
- document.getElementById('skEditName').value = '';
232
- document.getElementById('addSkillFormTitle').textContent = 'New Skill';
233
- document.getElementById('saveSkillBtn').textContent = 'Save Skill';
234
- document.getElementById('addSkillForm').style.display = 'none';
235
- ['skName','skDesc','skUrl','skAuthKey','skAuthHeader','skDefaults'].forEach(id => {
236
- const el = document.getElementById(id); if(el) el.value = '';
237
- });
238
- document.getElementById('skAuthType').value = '';
239
- document.getElementById('skRequiresApproval').checked = false;
240
- updateSkillAuthFields();
241
- }
242
-
243
- export function updateSkillAuthFields() {
244
- const t = document.getElementById('skAuthType').value;
245
- document.getElementById('skAuthHeaderWrap').style.display = t === 'header' ? 'block' : 'none';
246
- }
247
-
248
- export async function saveSkill() {
249
- const name = document.getElementById('skName').value.trim();
250
- const url = document.getElementById('skUrl').value.trim();
251
- if (!name || !url) { alert('Skill name and URL are required'); return; }
252
- let defaultParams = {};
253
- try { const v = document.getElementById('skDefaults').value.trim(); if(v) defaultParams = JSON.parse(v); }
254
- catch { alert('Default Params must be valid JSON'); return; }
255
- const authType = document.getElementById('skAuthType').value;
256
- const authKeyRaw = document.getElementById('skAuthKey').value.trim();
257
- let auth = {};
258
- if (authType && authKeyRaw) {
259
- auth = { type: authType };
260
- if (authKeyRaw.startsWith('providers.') || authKeyRaw.startsWith('env.')) auth.keyFrom = authKeyRaw;
261
- else auth.token = authKeyRaw;
262
- if (authType === 'header') auth.header = document.getElementById('skAuthHeader').value.trim() || 'X-API-Key';
263
- }
264
- const editingName = document.getElementById('skEditName').value.trim();
265
- const body = { name, url, method: document.getElementById('skMethod').value, description: document.getElementById('skDesc').value.trim(), auth: Object.keys(auth).length ? auth : undefined, defaultParams, requiresApproval: document.getElementById('skRequiresApproval').checked };
266
- try {
267
- if (editingName && editingName !== name) await fetch('/api/skills/' + editingName, { method: 'DELETE' });
268
- const r = await fetch('/api/skills', { method: 'POST', headers: {'content-type':'application/json'}, body: JSON.stringify(body) });
269
- if (!r.ok) throw new Error(await r.text());
270
- cancelSkillForm();
271
- loadSkills();
272
- showNotification(editingName ? 'Skill updated' : 'Skill saved');
273
- } catch(e) { showNotification('Failed: ' + e.message, 'error'); }
274
- }
275
-
276
- export async function deleteSkill(name) {
277
- if (!confirm('Delete skill "' + name + '"?')) return;
278
- try {
279
- const r = await fetch('/api/skills/' + name, { method: 'DELETE' });
280
- if(!r.ok) throw new Error(await r.text());
281
- loadSkills();
282
- showNotification('Deleted');
283
- } catch(e) { showNotification('Delete failed: ' + e.message, 'error'); }
284
- }
@@ -1,173 +0,0 @@
1
- /**
2
- * Spending dashboard — extracted from app.js
3
- * Deps: getJSON (core/api), showNotification (core/dom), estimateCost (usage-tab)
4
- */
5
- import { getJSON } from '../core/api.js';
6
- import { showNotification, showError } from '../core/dom.js';
7
- import { estimateCost, loadOcStats as loadOcStatsFromUsage } from './usage-tab.js';
8
-
9
- // ── Spending ──────────────────────────────────────────────────────────────────
10
- var _agentTotalCost = null;
11
- var _ocTotalCost = null;
12
-
13
- function updateGrandTotal() {
14
- var a = _agentTotalCost, o = _ocTotalCost;
15
- var aEl = document.getElementById('gtAgentCost');
16
- var oEl = document.getElementById('gtOcCost');
17
- var tEl = document.getElementById('gtTotal');
18
- if (!aEl) return;
19
- if (a !== null) aEl.textContent = '$' + a.toFixed(4);
20
- if (o !== null) oEl.textContent = '$' + o.toFixed(4);
21
- if (a !== null && o !== null) tEl.textContent = '$' + (a + o).toFixed(4);
22
- }
23
-
24
- export function reportOcCost(cost) {
25
- _ocTotalCost = cost;
26
- updateGrandTotal();
27
- }
28
-
29
- export async function loadAllUsage() {
30
- var days = parseInt(document.getElementById('grandTotalDays')?.value || '14');
31
- var ocSel = document.getElementById('ocStatsDays');
32
- var spSel = document.getElementById('spendingDays');
33
- if (ocSel) ocSel.value = String(days);
34
- if (spSel) spSel.value = String(days === 1 ? 1 : days);
35
- _agentTotalCost = null;
36
- _ocTotalCost = null;
37
- document.getElementById('gtAgentCost').textContent = '—';
38
- document.getElementById('gtOcCost').textContent = '—';
39
- document.getElementById('gtTotal').textContent = '—';
40
- loadSpending();
41
- loadOcStatsFromUsage(reportOcCost);
42
- }
43
-
44
- export async function loadSpending(){
45
- const el = document.getElementById('spendingWidget');
46
- const days = parseInt(document.getElementById('spendingDays')?.value || '1');
47
- try {
48
- if (days <= 1) {
49
- // Today: real-time from crew-lead
50
- const d = await (await fetch('/api/spending')).json();
51
- const { spending, caps } = d;
52
- const gTokens = spending.global?.tokens || 0;
53
- const gCost = spending.global?.costUSD || 0;
54
- const gCapTok = caps.global?.dailyTokenLimit;
55
- const gCapCost = caps.global?.dailyCostLimitUSD;
56
- let out = '<div style="margin-bottom:10px;">'
57
- + '<div style="font-size:11px;font-weight:600;color:var(--text-2);margin-bottom:4px;text-transform:uppercase;letter-spacing:.06em;">Global &middot; ' + (spending.date||'today') + '</div>'
58
- + '<div style="display:flex;gap:20px;"><span>' + gTokens.toLocaleString() + ' tokens' + (gCapTok ? ' / ' + Number(gCapTok).toLocaleString() : '') + '</span>'
59
- + '<span style="color:var(--yellow);font-weight:600;">$' + gCost.toFixed(4) + '</span>' + (gCapCost ? '<span> / $' + gCapCost + '</span>' : '') + '</div>';
60
- if (gCapTok) {
61
- const pct = Math.min(100, (gTokens/gCapTok)*100);
62
- const barColor = pct > 80 ? 'var(--red)' : pct > 50 ? 'var(--yellow)' : 'var(--green)';
63
- out += '<div style="margin-top:4px;height:4px;background:var(--border);border-radius:2px;"><div style="width:' + pct + '%;height:100%;background:' + barColor + ';border-radius:2px;transition:width .3s;"></div></div>';
64
- }
65
- out += '</div>';
66
- const agents = Object.entries(spending.agents || {});
67
- if (agents.length) {
68
- out += '<div style="font-size:11px;font-weight:600;color:var(--text-2);margin-bottom:6px;text-transform:uppercase;letter-spacing:.06em;">Per Agent</div>';
69
- out += agents.map(function(entry) {
70
- var id = entry[0], v = entry[1];
71
- const agentCap = caps.agents && caps.agents[id];
72
- const toks = v.tokens || 0;
73
- const cost = (v.costUSD||0).toFixed(4);
74
- const capTok = agentCap && agentCap.dailyTokenLimit;
75
- const pct = capTok ? Math.min(100, (toks/capTok)*100) : null;
76
- let row = '<div style="display:flex;align-items:center;gap:10px;margin-bottom:4px;">'
77
- + '<span style="min-width:140px;font-size:12px;">' + id + '</span>'
78
- + '<span style="font-size:12px;">' + toks.toLocaleString() + ' tok' + (capTok ? ' / ' + Number(capTok).toLocaleString() : '') + ' &middot; <span style="color:var(--yellow);">$' + cost + '</span></span>';
79
- if (pct !== null) {
80
- const barColor = pct > 80 ? 'var(--red)' : 'var(--accent)';
81
- row += '<div style="flex:1;height:3px;background:var(--border);border-radius:2px;"><div style="width:' + pct + '%;height:100%;background:' + barColor + ';border-radius:2px;"></div></div>';
82
- }
83
- return row + '</div>';
84
- }).join('');
85
- } else { out += '<div style="color:var(--text-3);">No per-agent data yet for today.</div>'; }
86
- if (gCapTok) document.getElementById('gcapTokens').value = gCapTok;
87
- if (gCapCost) document.getElementById('gcapCost').value = gCapCost;
88
- _agentTotalCost = gCost;
89
- updateGrandTotal();
90
- el.innerHTML = out;
91
- } else {
92
- // Multi-day: compute from token-usage.json byDay
93
- const u = await getJSON('/api/token-usage').catch(function(){ return {}; });
94
- const byDay = u.byDay || {};
95
- const cutoff = new Date(Date.now() - days * 86400000).toISOString().slice(0, 10);
96
- const filteredDays = Object.keys(byDay).filter(function(d){ return d >= cutoff; }).sort().reverse();
97
- if (!filteredDays.length) {
98
- el.innerHTML = '<div style="color:var(--text-3);">No data for this period.</div>';
99
- _agentTotalCost = 0;
100
- updateGrandTotal();
101
- return;
102
- }
103
- // Aggregate byModel across days
104
- const aggByModel = {};
105
- var totalTok = 0, totalCost = 0;
106
- filteredDays.forEach(function(day) {
107
- const dm = byDay[day].byModel || {};
108
- Object.entries(dm).forEach(function(e) {
109
- var m = e[0], s = e[1];
110
- if (!aggByModel[m]) aggByModel[m] = { prompt: 0, completion: 0 };
111
- aggByModel[m].prompt += s.prompt || 0;
112
- aggByModel[m].completion += s.completion || 0;
113
- totalTok += (s.prompt||0) + (s.completion||0);
114
- });
115
- });
116
- totalCost = estimateCost(aggByModel);
117
- let out = '<div style="margin-bottom:10px;display:flex;justify-content:space-between;align-items:center;">'
118
- + '<span style="font-size:12px;color:var(--text-3);">Last ' + days + ' days &middot; ' + filteredDays.length + ' days of data</span>'
119
- + '<span style="font-size:16px;font-weight:700;color:var(--yellow);">$' + totalCost.toFixed(4) + '</span>'
120
- + '</div>';
121
- // Daily breakdown bar chart
122
- const maxDayCost = Math.max(...filteredDays.map(function(d){ return estimateCost(byDay[d].byModel||{}); }), 0.0001);
123
- const today = new Date().toISOString().slice(0,10);
124
- out += '<div style="display:flex;flex-direction:column;gap:3px;margin-bottom:12px;">';
125
- filteredDays.forEach(function(day) {
126
- const dc = estimateCost(byDay[day].byModel||{});
127
- const pct = Math.max((dc/maxDayCost)*100, dc > 0 ? 2 : 0);
128
- const isToday = day === today;
129
- const tok = ((byDay[day].prompt||0)+(byDay[day].completion||0))/1000;
130
- out += '<div style="display:flex;align-items:center;gap:8px;font-size:11px;">'
131
- + '<span style="width:64px;color:var(--text-3);flex-shrink:0;">' + (isToday ? 'today' : day.slice(5)) + '</span>'
132
- + '<div style="flex:1;background:var(--bg-1);border-radius:3px;height:12px;overflow:hidden;">'
133
- + '<div style="width:' + pct.toFixed(1) + '%;height:100%;background:' + (isToday ? 'var(--accent)' : 'var(--green)') + ';border-radius:3px;opacity:.8;"></div>'
134
- + '</div>'
135
- + '<span style="width:58px;text-align:right;color:var(--yellow);font-weight:600;">$' + dc.toFixed(4) + '</span>'
136
- + '<span style="width:40px;text-align:right;color:var(--text-3);">' + tok.toFixed(0) + 'k</span>'
137
- + '</div>';
138
- });
139
- out += '</div>';
140
- // Top models
141
- const sortedModels = Object.entries(aggByModel).sort(function(a,b){
142
- return estimateCost({b:b[1]}) - estimateCost({a:a[1]});
143
- });
144
- if (sortedModels.length) {
145
- out += '<div style="font-size:11px;color:var(--text-3);margin-bottom:4px;">By model</div>';
146
- sortedModels.slice(0,8).forEach(function(e) {
147
- var m = e[0], s = e[1];
148
- const mc = estimateCost({x:s});
149
- const tok = ((s.prompt||0)+(s.completion||0))/1000;
150
- out += '<div style="display:flex;justify-content:space-between;font-size:11px;padding:2px 0;border-bottom:1px solid var(--border);">'
151
- + '<code style="color:var(--accent);">' + m + '</code>'
152
- + '<span style="color:var(--text-2);">' + tok.toFixed(1) + 'k tok &middot; <span style="color:var(--yellow);">$' + mc.toFixed(4) + '</span></span>'
153
- + '</div>';
154
- });
155
- }
156
- _agentTotalCost = totalCost;
157
- updateGrandTotal();
158
- el.innerHTML = out;
159
- }
160
- } catch(e) { showError(el, 'Error: ' + e.message); }
161
- }
162
-
163
- export async function resetSpending(){
164
- if (!confirm("Reset today's spending counters?")) return;
165
- try { await fetch('/api/spending/reset', { method: 'POST', headers:{'content-type':'application/json'}, body: '{}' }); loadSpending(); showNotification('Spending reset'); }
166
- catch(e) { showNotification('Reset failed', true); }
167
- }
168
-
169
- export async function saveGlobalCaps(){
170
- const tokens = parseInt(document.getElementById('gcapTokens').value) || null;
171
- const cost = parseFloat(document.getElementById('gcapCost').value) || null;
172
- showNotification('Add to ~/.crewswarm/crewswarm.json: "globalSpendingCaps": {"dailyTokenLimit":' + (tokens||'null') + ',"dailyCostLimitUSD":' + (cost||'null') + '}', 'warning');
173
- }