crewswarm 0.9.1 → 0.9.3

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 (210) hide show
  1. package/README.md +22 -9
  2. package/apps/dashboard/dist/assets/{chat-core-Cx4sTxDd.js → chat-core-3KirthZA.js} +1 -1
  3. package/apps/dashboard/dist/assets/index-GSWxxEPO.js +2 -0
  4. package/apps/dashboard/dist/assets/{tab-pm-loop-tab-Bfd449B4.js → tab-pm-loop-tab-DiAPTJXu.js} +1 -1
  5. package/apps/dashboard/dist/assets/{tab-projects-tab-DhNWnlzt.js → tab-projects-tab-SFH4E--a.js} +1 -1
  6. package/apps/dashboard/dist/assets/tab-settings-tab-BselH1c0.js +1 -0
  7. package/apps/dashboard/dist/index.html +82 -11
  8. package/apps/vibe/README.md +2 -2
  9. package/apps/vibe/package.json +1 -1
  10. package/apps/vibe/server.mjs +3 -3
  11. package/crew-lead.mjs +48 -5
  12. package/lib/bridges/gateway-ws.mjs +4 -0
  13. package/lib/bridges/tmux-bridge.mjs +200 -0
  14. package/lib/cli-process-tracker.mjs +2 -1
  15. package/lib/crew-lead/chat-handler.mjs +34 -0
  16. package/lib/crew-lead/http-server.mjs +340 -14
  17. package/lib/crew-lead/llm-caller.mjs +24 -8
  18. package/lib/crew-lead/prompts.mjs +7 -0
  19. package/lib/crew-lead/wave-dispatcher.mjs +53 -3
  20. package/lib/crew-lead/ws-router.mjs +219 -27
  21. package/lib/engines/engine-registry.mjs +9 -0
  22. package/lib/engines/rt-envelope.mjs +1 -0
  23. package/lib/engines/runners.mjs +26 -2
  24. package/lib/runtime/config.mjs +7 -0
  25. package/lib/runtime/paths.mjs +12 -8
  26. package/lib/sessions/session-manager.mjs +287 -0
  27. package/package.json +35 -15
  28. package/scripts/capture-build-flow.mjs +118 -0
  29. package/scripts/coverage-report.mjs +209 -0
  30. package/scripts/coverage-summary.mjs +47 -0
  31. package/scripts/dashboard-validation.mjs +74 -0
  32. package/scripts/dashboard.mjs +560 -70
  33. package/scripts/live-bridge-matrix.mjs +79 -0
  34. package/scripts/live-cli-matrix.mjs +166 -0
  35. package/scripts/live-crewchat-check.mjs +42 -0
  36. package/scripts/live-engine-matrix.mjs +50 -0
  37. package/scripts/live-provider-failover-matrix.mjs +107 -0
  38. package/scripts/live-provider-matrix.mjs +228 -0
  39. package/scripts/restart-all-from-repo.sh +4 -4
  40. package/scripts/smoke-dispatch.mjs +4 -1
  41. package/scripts/test-blast-radius.mjs +204 -0
  42. package/scripts/test-report-summary.mjs +88 -0
  43. package/scripts/test-reporter.mjs +651 -0
  44. package/scripts/test-rerun.mjs +136 -0
  45. package/scripts/tmux-bridge +130 -0
  46. package/apps/dashboard/dist/assets/chat-core-Cx4sTxDd.js.br +0 -0
  47. package/apps/dashboard/dist/assets/cli-process-COMRNPqr.js.br +0 -0
  48. package/apps/dashboard/dist/assets/components-BS9fQjE_.js.br +0 -0
  49. package/apps/dashboard/dist/assets/core-utils-CmOkXgzi.js.br +0 -0
  50. package/apps/dashboard/dist/assets/index-CF0aJRtC.css.br +0 -0
  51. package/apps/dashboard/dist/assets/index-DnClJ1ee.js +0 -2
  52. package/apps/dashboard/dist/assets/index-DnClJ1ee.js.br +0 -0
  53. package/apps/dashboard/dist/assets/orchestration-Ca2DLWN-.js.br +0 -0
  54. package/apps/dashboard/dist/assets/setup-wizard-CA0Or47w.js.br +0 -0
  55. package/apps/dashboard/dist/assets/tab-agents-tab-BgpIsjkw.js.br +0 -0
  56. package/apps/dashboard/dist/assets/tab-comms-tab-kguqTIzD.js.br +0 -0
  57. package/apps/dashboard/dist/assets/tab-contacts-tab-DiOyMYth.js.br +0 -0
  58. package/apps/dashboard/dist/assets/tab-engines-tab-BsdZVvU0.js.br +0 -0
  59. package/apps/dashboard/dist/assets/tab-memory-tab-Cu6u13EQ.js.br +0 -0
  60. package/apps/dashboard/dist/assets/tab-models-tab-BLEjmd19.js.br +0 -0
  61. package/apps/dashboard/dist/assets/tab-pm-loop-tab-Bfd449B4.js.br +0 -0
  62. package/apps/dashboard/dist/assets/tab-projects-tab-DhNWnlzt.js.br +0 -0
  63. package/apps/dashboard/dist/assets/tab-prompts-tab-DVkUNaJd.js.br +0 -0
  64. package/apps/dashboard/dist/assets/tab-services-tab-DU_LH3uG.js.br +0 -0
  65. package/apps/dashboard/dist/assets/tab-settings-tab-Bn4nXtDe.js +0 -1
  66. package/apps/dashboard/dist/assets/tab-settings-tab-Bn4nXtDe.js.br +0 -0
  67. package/apps/dashboard/dist/assets/tab-skills-tab-BpY0uZHW.js.br +0 -0
  68. package/apps/dashboard/dist/assets/tab-spending-tab-DEccQHnt.js.br +0 -0
  69. package/apps/dashboard/dist/assets/tab-swarm-chat-tab-BNrd88-r.js.br +0 -0
  70. package/apps/dashboard/dist/assets/tab-swarm-tab-B1AcjL1W.js.br +0 -0
  71. package/apps/dashboard/dist/assets/tab-usage-tab-BIOOnB-Y.js.br +0 -0
  72. package/apps/dashboard/dist/assets/tab-waves-tab-SaJDkb4x.js.br +0 -0
  73. package/apps/dashboard/dist/assets/tab-workflows-tab-B-soSy1k.js.br +0 -0
  74. package/apps/dashboard/dist/index.html.br +0 -0
  75. package/apps/dashboard/index.html +0 -6459
  76. package/apps/dashboard/package.json +0 -15
  77. package/apps/dashboard/src/app.js +0 -2823
  78. package/apps/dashboard/src/app.js.br +0 -0
  79. package/apps/dashboard/src/app.js.gz +0 -0
  80. package/apps/dashboard/src/chat/chat-actions.js +0 -1847
  81. package/apps/dashboard/src/chat/chat-actions.js.br +0 -0
  82. package/apps/dashboard/src/chat/unified-messages.js +0 -327
  83. package/apps/dashboard/src/chat/unified-messages.js.br +0 -0
  84. package/apps/dashboard/src/cli-process.js +0 -208
  85. package/apps/dashboard/src/cli-process.js.br +0 -0
  86. package/apps/dashboard/src/cli-process.js.gz +0 -0
  87. package/apps/dashboard/src/components/active-tasks-panel.js +0 -175
  88. package/apps/dashboard/src/components/active-tasks-panel.js.br +0 -0
  89. package/apps/dashboard/src/core/api.js +0 -18
  90. package/apps/dashboard/src/core/api.js.br +0 -0
  91. package/apps/dashboard/src/core/dom.js +0 -228
  92. package/apps/dashboard/src/core/dom.js.br +0 -0
  93. package/apps/dashboard/src/core/state.js +0 -91
  94. package/apps/dashboard/src/core/state.js.br +0 -0
  95. package/apps/dashboard/src/core/task-manager.js +0 -134
  96. package/apps/dashboard/src/core/task-manager.js.br +0 -0
  97. package/apps/dashboard/src/orchestration-status.js +0 -127
  98. package/apps/dashboard/src/orchestration-status.js.br +0 -0
  99. package/apps/dashboard/src/setup-wizard.js +0 -562
  100. package/apps/dashboard/src/setup-wizard.js.br +0 -0
  101. package/apps/dashboard/src/styles.css +0 -2085
  102. package/apps/dashboard/src/styles.css.br +0 -0
  103. package/apps/dashboard/src/styles.css.gz +0 -0
  104. package/apps/dashboard/src/tabs/agents-tab.js +0 -2237
  105. package/apps/dashboard/src/tabs/agents-tab.js.br +0 -0
  106. package/apps/dashboard/src/tabs/benchmarks-tab.js +0 -229
  107. package/apps/dashboard/src/tabs/benchmarks-tab.js.br +0 -0
  108. package/apps/dashboard/src/tabs/comms-tab.js +0 -955
  109. package/apps/dashboard/src/tabs/comms-tab.js.br +0 -0
  110. package/apps/dashboard/src/tabs/contacts-tab.js +0 -654
  111. package/apps/dashboard/src/tabs/contacts-tab.js.br +0 -0
  112. package/apps/dashboard/src/tabs/engines-tab.js +0 -175
  113. package/apps/dashboard/src/tabs/engines-tab.js.br +0 -0
  114. package/apps/dashboard/src/tabs/memory-tab.js +0 -182
  115. package/apps/dashboard/src/tabs/memory-tab.js.br +0 -0
  116. package/apps/dashboard/src/tabs/models-tab.js +0 -450
  117. package/apps/dashboard/src/tabs/models-tab.js.br +0 -0
  118. package/apps/dashboard/src/tabs/pm-loop-tab.js +0 -185
  119. package/apps/dashboard/src/tabs/pm-loop-tab.js.br +0 -0
  120. package/apps/dashboard/src/tabs/projects-tab.js +0 -663
  121. package/apps/dashboard/src/tabs/projects-tab.js.br +0 -0
  122. package/apps/dashboard/src/tabs/projects-tab.js.gz +0 -0
  123. package/apps/dashboard/src/tabs/prompts-tab.js +0 -160
  124. package/apps/dashboard/src/tabs/prompts-tab.js.br +0 -0
  125. package/apps/dashboard/src/tabs/services-tab.js +0 -202
  126. package/apps/dashboard/src/tabs/services-tab.js.br +0 -0
  127. package/apps/dashboard/src/tabs/settings-tab.js +0 -803
  128. package/apps/dashboard/src/tabs/settings-tab.js.br +0 -0
  129. package/apps/dashboard/src/tabs/skills-tab.js +0 -284
  130. package/apps/dashboard/src/tabs/skills-tab.js.br +0 -0
  131. package/apps/dashboard/src/tabs/spending-tab.js +0 -173
  132. package/apps/dashboard/src/tabs/spending-tab.js.br +0 -0
  133. package/apps/dashboard/src/tabs/swarm-chat-tab.js +0 -660
  134. package/apps/dashboard/src/tabs/swarm-chat-tab.js.br +0 -0
  135. package/apps/dashboard/src/tabs/swarm-tab.js +0 -538
  136. package/apps/dashboard/src/tabs/swarm-tab.js.br +0 -0
  137. package/apps/dashboard/src/tabs/usage-tab.js +0 -390
  138. package/apps/dashboard/src/tabs/usage-tab.js.br +0 -0
  139. package/apps/dashboard/src/tabs/waves-tab.js +0 -238
  140. package/apps/dashboard/src/tabs/waves-tab.js.br +0 -0
  141. package/apps/dashboard/src/tabs/workflows-tab.js +0 -747
  142. package/apps/dashboard/src/tabs/workflows-tab.js.br +0 -0
  143. package/apps/vibe/.crew/agent-memory/pipeline.json +0 -304
  144. package/apps/vibe/.crew/cost.json +0 -17
  145. package/apps/vibe/.crew/json-parse-metrics.jsonl +0 -27
  146. package/apps/vibe/.crew/pipeline-metrics.jsonl +0 -27
  147. package/apps/vibe/.crew/pipeline-runs/pipeline-0f90c392-2425-4ae5-850c-bd9d17b1d690.jsonl +0 -5
  148. package/apps/vibe/.crew/pipeline-runs/pipeline-1c269dd9-a63f-4fba-af81-5cf08048ef06.jsonl +0 -5
  149. package/apps/vibe/.crew/pipeline-runs/pipeline-288a7765-da24-4a22-89bc-1f3cc9b0562c.jsonl +0 -5
  150. package/apps/vibe/.crew/pipeline-runs/pipeline-2c78fd22-a657-4bd1-bc49-0679fb384409.jsonl +0 -5
  151. package/apps/vibe/.crew/pipeline-runs/pipeline-3da23550-22ed-4904-9a0a-8e79c1f3024c.jsonl +0 -5
  152. package/apps/vibe/.crew/pipeline-runs/pipeline-3e6fe08d-3264-404a-8df3-aab7efef10e7.jsonl +0 -5
  153. package/apps/vibe/.crew/pipeline-runs/pipeline-42eec610-57fe-4e09-9e7e-b315038495c2.jsonl +0 -5
  154. package/apps/vibe/.crew/pipeline-runs/pipeline-4438eb4c-ae13-42b1-90e2-b043d8983be8.jsonl +0 -5
  155. package/apps/vibe/.crew/pipeline-runs/pipeline-4740a9f5-86e7-44b6-a394-de433e291727.jsonl +0 -5
  156. package/apps/vibe/.crew/pipeline-runs/pipeline-49e1da6a-957e-48fd-9220-415019e4f8e2.jsonl +0 -5
  157. package/apps/vibe/.crew/pipeline-runs/pipeline-4c9251db-be68-427b-a3fc-a264f2b5778d.jsonl +0 -5
  158. package/apps/vibe/.crew/pipeline-runs/pipeline-6413fa33-a802-4b57-a8c0-a9056ad67842.jsonl +0 -5
  159. package/apps/vibe/.crew/pipeline-runs/pipeline-65e29a57-664d-4196-8109-017e364f182e.jsonl +0 -5
  160. package/apps/vibe/.crew/pipeline-runs/pipeline-6aa04bc5-9593-4b1f-b58d-3bf2978cb602.jsonl +0 -5
  161. package/apps/vibe/.crew/pipeline-runs/pipeline-6e1cba53-9b70-457e-99e0-59199149dd21.jsonl +0 -5
  162. package/apps/vibe/.crew/pipeline-runs/pipeline-749f41cc-4dac-4204-be64-873a6080a0d2.jsonl +0 -5
  163. package/apps/vibe/.crew/pipeline-runs/pipeline-74d68121-e181-4864-bd9a-c3211341dfaf.jsonl +0 -5
  164. package/apps/vibe/.crew/pipeline-runs/pipeline-8509bc24-142d-4e07-b44a-a50bf99d1103.jsonl +0 -5
  165. package/apps/vibe/.crew/pipeline-runs/pipeline-960339c6-07ca-43ce-9900-f6e1702b39b9.jsonl +0 -5
  166. package/apps/vibe/.crew/pipeline-runs/pipeline-9bef2dd2-6122-42e5-b3d9-19f4d80f9e40.jsonl +0 -5
  167. package/apps/vibe/.crew/pipeline-runs/pipeline-9c6480a9-7031-4146-b241-825b9a2d1de1.jsonl +0 -5
  168. package/apps/vibe/.crew/pipeline-runs/pipeline-9fd42426-8492-4157-9d5f-e1537c060489.jsonl +0 -2
  169. package/apps/vibe/.crew/pipeline-runs/pipeline-ad6d40a3-2f5e-46a9-a345-47caaccc51aa.jsonl +0 -5
  170. package/apps/vibe/.crew/pipeline-runs/pipeline-bc606133-8d5b-4535-8d85-f1a29cdaa981.jsonl +0 -5
  171. package/apps/vibe/.crew/pipeline-runs/pipeline-c1418f4e-b773-4ca1-84a3-216acf36e2f2.jsonl +0 -5
  172. package/apps/vibe/.crew/pipeline-runs/pipeline-c1a13ccd-634a-4d01-a4a7-1177b8a752ff.jsonl +0 -5
  173. package/apps/vibe/.crew/pipeline-runs/pipeline-c7d27b42-249e-4bd4-8f26-6aa998110b8a.jsonl +0 -5
  174. package/apps/vibe/.crew/pipeline-runs/pipeline-cca2e9b9-4a34-4d25-a311-5c793fa7e91e.jsonl +0 -5
  175. package/apps/vibe/.crew/sandbox.json +0 -7
  176. package/apps/vibe/.crew/session.json +0 -330
  177. package/apps/vibe/.crew/training-data.jsonl +0 -0
  178. package/apps/vibe/.github/workflows/studio-quality.yml +0 -37
  179. package/apps/vibe/.studio-data/project-messages/chuck-norris.jsonl +0 -18
  180. package/apps/vibe/.studio-data/project-messages/general.jsonl +0 -81
  181. package/apps/vibe/.studio-data/project-messages/studio-local.jsonl +0 -18
  182. package/apps/vibe/ARCHITECTURE.md +0 -3393
  183. package/apps/vibe/QUICK-REFERENCE.md +0 -211
  184. package/apps/vibe/ROADMAP.md +0 -41
  185. package/apps/vibe/STUDIO-SETUP-COMPLETE.md +0 -35
  186. package/apps/vibe/VISUAL-GUIDE.md +0 -378
  187. package/apps/vibe/capture-demo.mjs +0 -160
  188. package/apps/vibe/capture-full-demo.mjs +0 -255
  189. package/apps/vibe/capture-quickstart.mjs +0 -256
  190. package/apps/vibe/capture-vibe-assets.mjs +0 -71
  191. package/apps/vibe/capture-vibe-video.mjs +0 -260
  192. package/apps/vibe/check-buttons.js +0 -41
  193. package/apps/vibe/diagnose.html +0 -106
  194. package/apps/vibe/fix-buttons.js +0 -103
  195. package/apps/vibe/index.html +0 -3404
  196. package/apps/vibe/package-lock.json +0 -920
  197. package/apps/vibe/scripts/studio-pty-host.py +0 -117
  198. package/apps/vibe/src/main.js +0 -2940
  199. package/apps/vibe/src/register-all-languages.js +0 -98
  200. package/apps/vibe/start-studio.sh +0 -11
  201. package/apps/vibe/test/accessibility-tests.js +0 -77
  202. package/apps/vibe/test/browser-performance-audit.mjs +0 -205
  203. package/apps/vibe/test/performance-tests.js +0 -120
  204. package/apps/vibe/test/security-tests.js +0 -213
  205. package/apps/vibe/tests/e2e.local.mjs +0 -54
  206. package/apps/vibe/tests/server.smoke.mjs +0 -106
  207. package/apps/vibe/update_website.mjs +0 -74
  208. package/apps/vibe/vite.config.js +0 -19
  209. package/lib/crew-lead/chat-handler.mjs.bak +0 -1274
  210. package/lib/engines/rt-envelope.mjs.backup-current +0 -870
@@ -1,98 +0,0 @@
1
- import * as monaco from "monaco-editor/esm/vs/editor/editor.api";
2
-
3
- const languageContributionModules = {
4
- css: () => import("monaco-editor/esm/vs/basic-languages/css/css.contribution.js"),
5
- html: () => import("monaco-editor/esm/vs/basic-languages/html/html.contribution.js"),
6
- javascript: () => import("monaco-editor/esm/vs/basic-languages/javascript/javascript.contribution.js"),
7
- markdown: () => import("monaco-editor/esm/vs/basic-languages/markdown/markdown.contribution.js"),
8
- python: () => import("monaco-editor/esm/vs/basic-languages/python/python.contribution.js"),
9
- typescript: () => import("monaco-editor/esm/vs/basic-languages/typescript/typescript.contribution.js"),
10
- };
11
-
12
- const contributionIds = Object.keys(languageContributionModules).sort();
13
- const registeredLanguageIds = new Set(
14
- monaco.languages.getLanguages().map((language) => language.id),
15
- );
16
- const loadingLanguagePromises = new Map();
17
-
18
- function syncBootstrapState() {
19
- const readyLanguageIds = contributionIds
20
- .filter((languageId) => registeredLanguageIds.has(languageId))
21
- .sort();
22
-
23
- window.__studioMonacoLanguageBootstrap = {
24
- expectedLanguageIds: contributionIds,
25
- registeredLanguageIds: readyLanguageIds,
26
- assetFiles: contributionIds.map((languageId) => `${languageId}.contribution.js`),
27
- hasLanguage(id) {
28
- return registeredLanguageIds.has(id);
29
- },
30
- };
31
-
32
- return readyLanguageIds;
33
- }
34
-
35
- async function ensureLanguageRegistered(languageId) {
36
- if (!languageId || registeredLanguageIds.has(languageId)) {
37
- return registeredLanguageIds.has(languageId);
38
- }
39
-
40
- const load = languageContributionModules[languageId];
41
- if (!load) {
42
- return false;
43
- }
44
-
45
- if (!loadingLanguagePromises.has(languageId)) {
46
- loadingLanguagePromises.set(
47
- languageId,
48
- load()
49
- .then(() => {
50
- registeredLanguageIds.add(languageId);
51
- syncBootstrapState();
52
- return true;
53
- })
54
- .catch((error) => {
55
- loadingLanguagePromises.delete(languageId);
56
- throw error;
57
- }),
58
- );
59
- }
60
-
61
- return loadingLanguagePromises.get(languageId);
62
- }
63
-
64
- function scheduleBackgroundRegistration() {
65
- const run = async () => {
66
- for (const languageId of contributionIds) {
67
- await ensureLanguageRegistered(languageId);
68
- }
69
-
70
- const readyLanguageIds = syncBootstrapState();
71
- console.info("[studio] Monaco language bootstrap ready", readyLanguageIds);
72
- return readyLanguageIds;
73
- };
74
-
75
- const start = () =>
76
- run().catch((error) => {
77
- console.error("[studio] Monaco language bootstrap failed", error);
78
- throw error;
79
- });
80
-
81
- if (typeof window.requestIdleCallback === "function") {
82
- return new Promise((resolve, reject) => {
83
- window.requestIdleCallback(() => {
84
- start().then(resolve).catch(reject);
85
- });
86
- });
87
- }
88
-
89
- return new Promise((resolve, reject) => {
90
- window.setTimeout(() => {
91
- start().then(resolve).catch(reject);
92
- }, 0);
93
- });
94
- }
95
-
96
- syncBootstrapState();
97
- window.__studioEnsureLanguageRegistered = ensureLanguageRegistered;
98
- window.__studioLanguageRegistrationReady = scheduleBackgroundRegistration();
@@ -1,11 +0,0 @@
1
- #!/usr/bin/env bash
2
- set -euo pipefail
3
-
4
- cd "$(dirname "$0")"
5
-
6
- if [ ! -d node_modules ]; then
7
- npm install
8
- fi
9
-
10
- npm run build
11
- exec npm start
@@ -1,77 +0,0 @@
1
- import assert from "node:assert/strict";
2
- import fs from "node:fs/promises";
3
- import path from "node:path";
4
- import { fileURLToPath } from "node:url";
5
-
6
- const __dirname = path.dirname(fileURLToPath(import.meta.url));
7
- const rootDir = path.resolve(__dirname, "..");
8
-
9
- async function read(relativePath) {
10
- return fs.readFile(path.join(rootDir, relativePath), "utf8");
11
- }
12
-
13
- function test(name, fn) {
14
- return { name, fn };
15
- }
16
-
17
- async function runTests(label, tests) {
18
- let passed = 0;
19
- for (const current of tests) {
20
- try {
21
- await current.fn();
22
- passed += 1;
23
- console.log(`PASS ${label}: ${current.name}`);
24
- } catch (error) {
25
- console.error(`FAIL ${label}: ${current.name}`);
26
- throw error;
27
- }
28
- }
29
- console.log(`${label} suite passed (${passed}/${tests.length})`);
30
- }
31
-
32
- export async function runAccessibilityTests() {
33
- const [html, appSource] = await Promise.all([
34
- read("index.html"),
35
- read("src/main.js"),
36
- ]);
37
-
38
- const tests = [
39
- test("document declares language metadata", () => {
40
- assert.match(html, /<html[^>]*lang="en"/i);
41
- assert.match(html, /<meta name="viewport" content="width=device-width,\s*initial-scale=1\.0"/i);
42
- }),
43
- test("primary application shell is present", () => {
44
- assert.match(html, /<div[^>]+id="app"/i);
45
- }),
46
- test("icon-only controls expose accessible names", () => {
47
- assert.match(html, /aria-label="Toggle color theme"/);
48
- assert.match(html, /aria-label="Open settings panel"/);
49
- assert.match(html, /aria-label="Toggle keyboard shortcuts guide"/);
50
- assert.match(html, /aria-label="Interactive terminal"/);
51
- }),
52
- test("overlay panels are labelled by their titles", () => {
53
- assert.match(html, /id="settings-panel"[\s\S]*aria-labelledby="settings-panel-title"/);
54
- assert.match(html, /id="shortcuts-panel"[\s\S]*aria-labelledby="shortcuts-panel-title"/);
55
- assert.match(html, /id="diff-preview-overlay"[\s\S]*aria-labelledby="diff-preview-title"/);
56
- }),
57
- test("terminal panel starts hidden for assistive tech", () => {
58
- assert.match(html, /id="bottom-terminal-panel"[^>]*aria-hidden="true"/);
59
- }),
60
- test("keyboard escape handling closes open overlays", () => {
61
- assert.match(html, /if \(event\.key === "Escape" && settingsPanel\?\.classList\.contains\("visible"\)\)/);
62
- assert.match(html, /setSettingsPanelOpen\(false\)/);
63
- assert.match(html, /setShortcutsPanelOpen\(false\)/);
64
- }),
65
- ];
66
-
67
- await runTests("accessibility", tests);
68
- }
69
-
70
- const invokedDirectly = process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url);
71
-
72
- if (invokedDirectly) {
73
- runAccessibilityTests().catch((error) => {
74
- console.error(error.stack || error.message);
75
- process.exitCode = 1;
76
- });
77
- }
@@ -1,205 +0,0 @@
1
- import assert from "node:assert/strict";
2
- import { once } from "node:events";
3
- import fs from "node:fs/promises";
4
- import path from "node:path";
5
- import { spawn } from "node:child_process";
6
- import { setTimeout as delay } from "node:timers/promises";
7
- import { fileURLToPath } from "node:url";
8
- import { chromium } from "playwright";
9
-
10
- const __dirname = path.dirname(fileURLToPath(import.meta.url));
11
- const rootDir = path.resolve(__dirname, "..");
12
- const reportDir = path.join(rootDir, "output");
13
- const reportPath = path.join(reportDir, "performance-audit.json");
14
- const port = Number(process.env.STUDIO_AUDIT_PORT || 3345);
15
- const baseUrl = `http://127.0.0.1:${port}`;
16
-
17
- const BUDGETS = {
18
- domContentLoadedMs: 2500,
19
- loadMs: 4000,
20
- lcpMs: 2500,
21
- resourceCount: 40,
22
- totalTransferBytes: 1_500_000,
23
- jsHeapUsedBytes: 80 * 1024 * 1024,
24
- longTasks: 3,
25
- };
26
-
27
- async function ensureBuildOutput() {
28
- await fs.access(path.join(rootDir, "dist", "index.html"));
29
- }
30
-
31
- async function waitForServer(url, timeoutMs = 15_000) {
32
- const deadline = Date.now() + timeoutMs;
33
- let lastError = null;
34
-
35
- while (Date.now() < deadline) {
36
- try {
37
- const response = await fetch(url);
38
- if (response.ok) {
39
- return;
40
- }
41
- lastError = new Error(`HTTP ${response.status}`);
42
- } catch (error) {
43
- lastError = error;
44
- }
45
- await delay(250);
46
- }
47
-
48
- throw lastError || new Error("Studio server did not become ready");
49
- }
50
-
51
- function startServer() {
52
- const child = spawn(process.execPath, ["server.mjs"], {
53
- cwd: rootDir,
54
- env: {
55
- ...process.env,
56
- STUDIO_PORT: String(port),
57
- },
58
- stdio: "ignore",
59
- });
60
- return child;
61
- }
62
-
63
- async function collectMetrics() {
64
- const browser = await chromium.launch({ headless: true });
65
- const page = await browser.newPage();
66
- const cdp = await page.context().newCDPSession(page);
67
-
68
- await page.addInitScript(() => {
69
- window.__studioPerf = {
70
- lcp: 0,
71
- cls: 0,
72
- longTasks: 0,
73
- longTaskTime: 0,
74
- };
75
-
76
- new PerformanceObserver((list) => {
77
- const entries = list.getEntries();
78
- const lastEntry = entries.at(-1);
79
- if (lastEntry) {
80
- window.__studioPerf.lcp = lastEntry.startTime;
81
- }
82
- }).observe({ type: "largest-contentful-paint", buffered: true });
83
-
84
- new PerformanceObserver((list) => {
85
- for (const entry of list.getEntries()) {
86
- if (!entry.hadRecentInput) {
87
- window.__studioPerf.cls += entry.value;
88
- }
89
- }
90
- }).observe({ type: "layout-shift", buffered: true });
91
-
92
- new PerformanceObserver((list) => {
93
- for (const entry of list.getEntries()) {
94
- window.__studioPerf.longTasks += 1;
95
- window.__studioPerf.longTaskTime += entry.duration;
96
- }
97
- }).observe({ type: "longtask", buffered: true });
98
- });
99
-
100
- await cdp.send("Performance.enable");
101
- await page.goto(baseUrl, { waitUntil: "domcontentloaded", timeout: 15_000 });
102
- await page.waitForLoadState("load", { timeout: 15_000 });
103
- await delay(750);
104
-
105
- const browserMetrics = await cdp.send("Performance.getMetrics");
106
- const runtimeMetrics = await page.evaluate(() => {
107
- const navigation = performance.getEntriesByType("navigation")[0];
108
- const resources = performance.getEntriesByType("resource");
109
- const totals = resources.reduce(
110
- (acc, entry) => {
111
- acc.transferSize += entry.transferSize || 0;
112
- if (entry.initiatorType === "script") {
113
- acc.scriptTransferSize += entry.transferSize || 0;
114
- }
115
- return acc;
116
- },
117
- { transferSize: 0, scriptTransferSize: 0 },
118
- );
119
-
120
- return {
121
- domContentLoadedMs: navigation?.domContentLoadedEventEnd || 0,
122
- loadMs: navigation?.loadEventEnd || 0,
123
- resourceCount: resources.length,
124
- totalTransferBytes: totals.transferSize,
125
- scriptTransferBytes: totals.scriptTransferSize,
126
- lcpMs: window.__studioPerf?.lcp || 0,
127
- cls: window.__studioPerf?.cls || 0,
128
- longTasks: window.__studioPerf?.longTasks || 0,
129
- longTaskTimeMs: window.__studioPerf?.longTaskTime || 0,
130
- };
131
- });
132
-
133
- await browser.close();
134
-
135
- const cdpMetricMap = Object.fromEntries(
136
- browserMetrics.metrics.map((metric) => [metric.name, metric.value]),
137
- );
138
-
139
- return {
140
- collectedAt: new Date().toISOString(),
141
- url: baseUrl,
142
- budgets: BUDGETS,
143
- metrics: {
144
- ...runtimeMetrics,
145
- jsHeapUsedBytes: cdpMetricMap.JSHeapUsedSize || 0,
146
- nodes: cdpMetricMap.Nodes || 0,
147
- documents: cdpMetricMap.Documents || 0,
148
- },
149
- };
150
- }
151
-
152
- function evaluateBudgets(report) {
153
- const { metrics } = report;
154
-
155
- const checks = [
156
- ["domContentLoadedMs", metrics.domContentLoadedMs <= BUDGETS.domContentLoadedMs],
157
- ["loadMs", metrics.loadMs <= BUDGETS.loadMs],
158
- ["resourceCount", metrics.resourceCount <= BUDGETS.resourceCount],
159
- ["totalTransferBytes", metrics.totalTransferBytes <= BUDGETS.totalTransferBytes],
160
- ["jsHeapUsedBytes", metrics.jsHeapUsedBytes <= BUDGETS.jsHeapUsedBytes],
161
- ["longTasks", metrics.longTasks <= BUDGETS.longTasks],
162
- ];
163
-
164
- if (metrics.lcpMs > 0) {
165
- checks.push(["lcpMs", metrics.lcpMs <= BUDGETS.lcpMs]);
166
- }
167
-
168
- report.checks = checks.map(([name, pass]) => ({ name, pass }));
169
- report.ok = report.checks.every((check) => check.pass);
170
- }
171
-
172
- async function main() {
173
- await ensureBuildOutput();
174
- await fs.mkdir(reportDir, { recursive: true });
175
-
176
- console.log("Starting Studio server for performance audit...");
177
- const server = startServer();
178
-
179
- try {
180
- console.log("Waiting for Studio server...");
181
- await waitForServer(baseUrl);
182
- console.log("Collecting browser metrics...");
183
- const report = await collectMetrics();
184
- console.log("Evaluating budgets...");
185
- evaluateBudgets(report);
186
- await fs.writeFile(reportPath, `${JSON.stringify(report, null, 2)}\n`, "utf8");
187
-
188
- console.log(`Performance audit written to ${reportPath}`);
189
- report.checks.forEach((check) => {
190
- console.log(`${check.pass ? "PASS" : "FAIL"} ${check.name}`);
191
- });
192
-
193
- assert.ok(report.ok, "Studio performance audit exceeded one or more budgets");
194
- } finally {
195
- if (server.exitCode === null && !server.killed) {
196
- server.kill("SIGTERM");
197
- await Promise.race([once(server, "exit"), delay(2_000)]);
198
- }
199
- }
200
- }
201
-
202
- main().catch((error) => {
203
- console.error(error.stack || error.message);
204
- process.exitCode = 1;
205
- });
@@ -1,120 +0,0 @@
1
- import assert from "node:assert/strict";
2
- import fs from "node:fs/promises";
3
- import path from "node:path";
4
- import { fileURLToPath } from "node:url";
5
-
6
- const __dirname = path.dirname(fileURLToPath(import.meta.url));
7
- const rootDir = path.resolve(__dirname, "..");
8
-
9
- async function read(relativePath) {
10
- return fs.readFile(path.join(rootDir, relativePath), "utf8");
11
- }
12
-
13
- async function stat(relativePath) {
14
- return fs.stat(path.join(rootDir, relativePath));
15
- }
16
-
17
- function test(name, fn) {
18
- return { name, fn };
19
- }
20
-
21
- async function runTests(label, tests) {
22
- let passed = 0;
23
- for (const current of tests) {
24
- try {
25
- await current.fn();
26
- passed += 1;
27
- console.log(`PASS ${label}: ${current.name}`);
28
- } catch (error) {
29
- console.error(`FAIL ${label}: ${current.name}`);
30
- throw error;
31
- }
32
- }
33
- console.log(`${label} suite passed (${passed}/${tests.length})`);
34
- }
35
-
36
- export async function runPerformanceTests() {
37
- const [html, appSource, serverSource, viteConfig] = await Promise.all([
38
- read("index.html"),
39
- read("src/main.js"),
40
- read("server.mjs"),
41
- read("vite.config.js"),
42
- ]);
43
- const distAssets = await fs.readdir(path.join(rootDir, "dist", "assets"));
44
- const jsAssets = distAssets.filter((name) => name.endsWith(".js"));
45
- const cssAssets = distAssets.filter((name) => name.endsWith(".css"));
46
- const indexBundle = jsAssets.find((name) => /^index-.*\.js$/.test(name));
47
- const indexBundleStat = indexBundle ? await stat(path.join("dist", "assets", indexBundle)) : null;
48
-
49
- const tests = [
50
- test("build output includes JavaScript and CSS assets", () => {
51
- assert.ok(jsAssets.length > 0, "expected built JavaScript assets");
52
- assert.ok(cssAssets.length > 0, "expected built CSS assets");
53
- }),
54
- test("main bundle stays below a 3 MB guardrail", () => {
55
- assert.ok(indexBundleStat, "expected a built index bundle");
56
- assert.ok(indexBundleStat.size < 3 * 1024 * 1024, `bundle too large: ${indexBundleStat.size}`);
57
- }),
58
- test("index.html avoids external font and CDN preconnects", () => {
59
- assert.match(html, /System font stack only to avoid CORS/i);
60
- assert.doesNotMatch(html, /<link rel="preconnect"/i);
61
- }),
62
- test("Monaco is loaded lazily through dynamic imports", () => {
63
- assert.match(appSource, /async function loadMonaco\(/);
64
- assert.match(appSource, /import\("monaco-editor\/esm\/vs\/editor\/editor\.api"\)/);
65
- }),
66
- test("editor renders a lightweight placeholder before Monaco loads", () => {
67
- assert.match(appSource, /function renderEditorPlaceholder\(/);
68
- assert.match(appSource, /Editor loads on demand/);
69
- }),
70
- test("explorer hides heavy generated directories from scans", () => {
71
- assert.match(appSource, /relativePath\.startsWith\("dist\/"\)/);
72
- assert.match(appSource, /relativePath\.startsWith\("node_modules\/"\)/);
73
- assert.match(appSource, /relativePath\.startsWith\("output\/"\)/);
74
- }),
75
- test("static server serves immutable cache headers for hashed assets", () => {
76
- assert.match(serverSource, /return "public, max-age=31536000, immutable";/);
77
- }),
78
- test("static server can serve precompressed assets", () => {
79
- assert.match(serverSource, /if \(acceptEncoding\.includes\("br"\)\)/);
80
- assert.match(serverSource, /if \(acceptEncoding\.includes\("gzip"\)\)/);
81
- }),
82
- test("Vite build targets the dist assets directory explicitly", () => {
83
- assert.match(viteConfig, /outDir:\s*["']dist["']/);
84
- assert.match(viteConfig, /assetsDir:\s*["']assets["']/);
85
- }),
86
- test("workspace scans cap file enumeration to avoid runaway traversal", () => {
87
- assert.match(serverSource, /const MAX_FILES = 800;/);
88
- assert.match(serverSource, /if \(depth > 6 \|\| results\.length >= MAX_FILES\) return;/);
89
- }),
90
- test("workspace scans use a short-lived cache with targeted invalidation", () => {
91
- assert.match(serverSource, /const WORKSPACE_SCAN_CACHE_TTL_MS = Number\(process\.env\.STUDIO_SCAN_CACHE_TTL_MS \|\| 1_500\);/);
92
- assert.match(serverSource, /workspaceScanCache\.set\(resolvedScanDir,\s*\{/);
93
- assert.match(serverSource, /invalidateWorkspaceScanCache\(resolvedPath\);/);
94
- }),
95
- test("audit cache is tracked and invalidated alongside workspace scans", () => {
96
- assert.match(serverSource, /const auditFileCache = new Map\(\);/);
97
- assert.match(serverSource, /for \(const cache of \[workspaceScanCache, auditFileCache\]\)/);
98
- }),
99
- test("file tree refreshes are debounced during bursty CLI updates", () => {
100
- assert.match(appSource, /const FILE_TREE_REFRESH_DEBOUNCE_MS = 150;/);
101
- assert.match(appSource, /function scheduleFileTreeRefresh\(/);
102
- assert.match(appSource, /scheduleFileTreeRefresh\(\);/);
103
- }),
104
- test("terminal output is capped to avoid long-session DOM bloat", () => {
105
- assert.match(appSource, /const MAX_TERMINAL_ENTRIES = 250;/);
106
- assert.match(appSource, /while \(container\.children\.length > MAX_TERMINAL_ENTRIES\)/);
107
- }),
108
- ];
109
-
110
- await runTests("performance", tests);
111
- }
112
-
113
- const invokedDirectly = process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url);
114
-
115
- if (invokedDirectly) {
116
- runPerformanceTests().catch((error) => {
117
- console.error(error.stack || error.message);
118
- process.exitCode = 1;
119
- });
120
- }