retestkit 1.4.1 → 1.5.0

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 (238) hide show
  1. package/README.md +59 -40
  2. package/dist/config.js +8 -8
  3. package/dist/config.js.map +1 -1
  4. package/dist/logger.js +1 -1
  5. package/dist/logger.js.map +1 -1
  6. package/dist/prompts/index.d.ts +1 -1
  7. package/dist/prompts/index.d.ts.map +1 -1
  8. package/dist/prompts/index.js +21 -21
  9. package/dist/prompts/index.js.map +1 -1
  10. package/dist/prompts/templates/mcp/retest-crawl.md +7 -0
  11. package/{src/prompts/templates/mcp/webtest-discover-flows.md → dist/prompts/templates/mcp/retest-discover-flows.md} +1 -1
  12. package/{src/prompts/templates/mcp/webtest-discover.md → dist/prompts/templates/mcp/retest-discover.md} +2 -2
  13. package/dist/prompts/templates/mcp/retest-full-workflow.md +12 -0
  14. package/{src/prompts/templates/mcp/webtest-generate-tests.md → dist/prompts/templates/mcp/retest-generate-tests.md} +1 -1
  15. package/{src/prompts/templates/mcp/webtest-run-test.md → dist/prompts/templates/mcp/retest-run-test.md} +1 -1
  16. package/{src/prompts/templates/mcp/webtest-start.md → dist/prompts/templates/mcp/retest-start.md} +1 -1
  17. package/{src → dist}/prompts/templates/sampling/system-prefix.md +1 -1
  18. package/dist/resources/index.js +7 -7
  19. package/dist/resources/index.js.map +1 -1
  20. package/dist/schemas/config.js +2 -2
  21. package/dist/schemas/config.js.map +1 -1
  22. package/dist/security/index.js +1 -1
  23. package/dist/security/index.js.map +1 -1
  24. package/dist/server.js +3 -3
  25. package/dist/server.js.map +1 -1
  26. package/dist/test-utils/mock-context.js +22 -22
  27. package/dist/test-utils/mock-context.js.map +1 -1
  28. package/dist/tools/index.d.ts +1 -1
  29. package/dist/tools/index.d.ts.map +1 -1
  30. package/dist/tools/index.js +5 -5
  31. package/dist/tools/index.js.map +1 -1
  32. package/dist/tools/retest/crawl.d.ts.map +1 -0
  33. package/dist/tools/{webtest → retest}/crawl.js +7 -7
  34. package/dist/tools/retest/crawl.js.map +1 -0
  35. package/dist/tools/retest/discover-features.d.ts.map +1 -0
  36. package/dist/tools/{webtest → retest}/discover-features.js +6 -6
  37. package/dist/tools/retest/discover-features.js.map +1 -0
  38. package/dist/tools/retest/discover-flows.d.ts.map +1 -0
  39. package/dist/tools/{webtest → retest}/discover-flows.js +6 -6
  40. package/dist/tools/retest/discover-flows.js.map +1 -0
  41. package/dist/tools/retest/generate-tests.d.ts.map +1 -0
  42. package/dist/tools/{webtest → retest}/generate-tests.js +5 -5
  43. package/dist/tools/retest/generate-tests.js.map +1 -0
  44. package/dist/tools/retest/index.d.ts.map +1 -0
  45. package/dist/tools/retest/index.js.map +1 -0
  46. package/dist/tools/retest/run-test-case.d.ts.map +1 -0
  47. package/dist/tools/{webtest → retest}/run-test-case.js +3 -3
  48. package/dist/tools/retest/run-test-case.js.map +1 -0
  49. package/dist/tools/retest/schemas.d.ts.map +1 -0
  50. package/dist/tools/retest/schemas.js.map +1 -0
  51. package/dist/tools/retest/start-analysis.d.ts.map +1 -0
  52. package/dist/tools/{webtest → retest}/start-analysis.js +5 -5
  53. package/dist/tools/retest/start-analysis.js.map +1 -0
  54. package/dist/workspace/index.js +8 -8
  55. package/dist/workspace/index.js.map +1 -1
  56. package/dist/workspace/types.d.ts +2 -2
  57. package/dist/workspace/types.d.ts.map +1 -1
  58. package/package.json +6 -2
  59. package/.claude/commands/openspec/apply.md +0 -23
  60. package/.claude/commands/openspec/archive.md +0 -27
  61. package/.claude/commands/openspec/proposal.md +0 -28
  62. package/.gemini/commands/openspec/apply.toml +0 -21
  63. package/.gemini/commands/openspec/archive.toml +0 -25
  64. package/.gemini/commands/openspec/proposal.toml +0 -26
  65. package/.github/prompts/openspec-apply.prompt.md +0 -22
  66. package/.github/prompts/openspec-archive.prompt.md +0 -26
  67. package/.github/prompts/openspec-proposal.prompt.md +0 -27
  68. package/.github/workflows/release.yml +0 -33
  69. package/.kilocode/workflows/openspec-apply.md +0 -17
  70. package/.kilocode/workflows/openspec-archive.md +0 -21
  71. package/.kilocode/workflows/openspec-proposal.md +0 -22
  72. package/.mcp.json +0 -23
  73. package/.opencode/command/openspec-apply.md +0 -25
  74. package/.opencode/command/openspec-archive.md +0 -28
  75. package/.opencode/command/openspec-proposal.md +0 -30
  76. package/.roo/commands/openspec-apply.md +0 -20
  77. package/.roo/commands/openspec-archive.md +0 -24
  78. package/.roo/commands/openspec-proposal.md +0 -25
  79. package/.vscode/mcp.json +0 -23
  80. package/AGENTS.md +0 -18
  81. package/CLAUDE.md +0 -18
  82. package/dist/tools/webtest/crawl.d.ts.map +0 -1
  83. package/dist/tools/webtest/crawl.js.map +0 -1
  84. package/dist/tools/webtest/discover-features.d.ts.map +0 -1
  85. package/dist/tools/webtest/discover-features.js.map +0 -1
  86. package/dist/tools/webtest/discover-flows.d.ts.map +0 -1
  87. package/dist/tools/webtest/discover-flows.js.map +0 -1
  88. package/dist/tools/webtest/generate-tests.d.ts.map +0 -1
  89. package/dist/tools/webtest/generate-tests.js.map +0 -1
  90. package/dist/tools/webtest/index.d.ts.map +0 -1
  91. package/dist/tools/webtest/index.js.map +0 -1
  92. package/dist/tools/webtest/run-test-case.d.ts.map +0 -1
  93. package/dist/tools/webtest/run-test-case.js.map +0 -1
  94. package/dist/tools/webtest/schemas.d.ts.map +0 -1
  95. package/dist/tools/webtest/schemas.js.map +0 -1
  96. package/dist/tools/webtest/start-analysis.d.ts.map +0 -1
  97. package/dist/tools/webtest/start-analysis.js.map +0 -1
  98. package/openspec/AGENTS.md +0 -456
  99. package/openspec/changes/archive/2025-12-18-add-hybrid-artifact-paths/proposal.md +0 -33
  100. package/openspec/changes/archive/2025-12-18-add-hybrid-artifact-paths/specs/webtest-resources/spec.md +0 -27
  101. package/openspec/changes/archive/2025-12-18-add-hybrid-artifact-paths/specs/webtest-tools/spec.md +0 -304
  102. package/openspec/changes/archive/2025-12-18-add-hybrid-artifact-paths/tasks.md +0 -43
  103. package/openspec/changes/archive/2025-12-18-add-mcp-server-foundation/design.md +0 -209
  104. package/openspec/changes/archive/2025-12-18-add-mcp-server-foundation/proposal.md +0 -41
  105. package/openspec/changes/archive/2025-12-18-add-mcp-server-foundation/specs/mcp-server-core/spec.md +0 -183
  106. package/openspec/changes/archive/2025-12-18-add-mcp-server-foundation/tasks.md +0 -112
  107. package/openspec/changes/archive/2025-12-18-add-webtest-orchestrator/design.md +0 -333
  108. package/openspec/changes/archive/2025-12-18-add-webtest-orchestrator/proposal.md +0 -66
  109. package/openspec/changes/archive/2025-12-18-add-webtest-orchestrator/specs/mcp-server-core/spec.md +0 -129
  110. package/openspec/changes/archive/2025-12-18-add-webtest-orchestrator/specs/webtest-lifecycle/spec.md +0 -138
  111. package/openspec/changes/archive/2025-12-18-add-webtest-orchestrator/specs/webtest-logging/spec.md +0 -211
  112. package/openspec/changes/archive/2025-12-18-add-webtest-orchestrator/specs/webtest-prompts/spec.md +0 -157
  113. package/openspec/changes/archive/2025-12-18-add-webtest-orchestrator/specs/webtest-resources/spec.md +0 -213
  114. package/openspec/changes/archive/2025-12-18-add-webtest-orchestrator/specs/webtest-sampling/spec.md +0 -257
  115. package/openspec/changes/archive/2025-12-18-add-webtest-orchestrator/specs/webtest-tools/spec.md +0 -501
  116. package/openspec/changes/archive/2025-12-18-add-webtest-orchestrator/tasks.md +0 -264
  117. package/openspec/changes/archive/2025-12-18-allow-analysis-of-incomplete-crawls/proposal.md +0 -24
  118. package/openspec/changes/archive/2025-12-18-allow-analysis-of-incomplete-crawls/specs/webtest-tools/spec.md +0 -80
  119. package/openspec/changes/archive/2025-12-18-allow-analysis-of-incomplete-crawls/tasks.md +0 -8
  120. package/openspec/changes/archive/2025-12-18-fix-crawl-loop-stability/design.md +0 -90
  121. package/openspec/changes/archive/2025-12-18-fix-crawl-loop-stability/proposal.md +0 -28
  122. package/openspec/changes/archive/2025-12-18-fix-crawl-loop-stability/specs/webtest-sampling/spec.md +0 -90
  123. package/openspec/changes/archive/2025-12-18-fix-crawl-loop-stability/tasks.md +0 -33
  124. package/openspec/changes/archive/2025-12-18-use-markdown-artifacts/design.md +0 -558
  125. package/openspec/changes/archive/2025-12-18-use-markdown-artifacts/proposal.md +0 -119
  126. package/openspec/changes/archive/2025-12-18-use-markdown-artifacts/specs/webtest-resources/spec.md +0 -109
  127. package/openspec/changes/archive/2025-12-18-use-markdown-artifacts/specs/webtest-tools/spec.md +0 -121
  128. package/openspec/changes/archive/2025-12-18-use-markdown-artifacts/tasks.md +0 -133
  129. package/openspec/changes/extract-prompts-to-markdown/design.md +0 -86
  130. package/openspec/changes/extract-prompts-to-markdown/proposal.md +0 -50
  131. package/openspec/changes/extract-prompts-to-markdown/specs/webtest-prompts/spec.md +0 -74
  132. package/openspec/changes/extract-prompts-to-markdown/tasks.md +0 -40
  133. package/openspec/changes/refactor-webtest-naming/design.md +0 -95
  134. package/openspec/changes/refactor-webtest-naming/proposal.md +0 -66
  135. package/openspec/changes/refactor-webtest-naming/specs/webtest-prompts/spec.md +0 -79
  136. package/openspec/changes/refactor-webtest-naming/specs/webtest-resources/spec.md +0 -80
  137. package/openspec/changes/refactor-webtest-naming/specs/webtest-sampling/spec.md +0 -122
  138. package/openspec/changes/refactor-webtest-naming/specs/webtest-tools/spec.md +0 -113
  139. package/openspec/changes/refactor-webtest-naming/tasks.md +0 -119
  140. package/openspec/changes/rename-package-to-retest/proposal.md +0 -52
  141. package/openspec/changes/rename-package-to-retest/specs/mcp-server-core/spec.md +0 -53
  142. package/openspec/changes/rename-package-to-retest/specs/retest-lifecycle/spec.md +0 -68
  143. package/openspec/changes/rename-package-to-retest/specs/retest-logging/spec.md +0 -35
  144. package/openspec/changes/rename-package-to-retest/specs/retest-prompts/spec.md +0 -159
  145. package/openspec/changes/rename-package-to-retest/specs/retest-resources/spec.md +0 -251
  146. package/openspec/changes/rename-package-to-retest/specs/retest-sampling/spec.md +0 -99
  147. package/openspec/changes/rename-package-to-retest/specs/retest-tools/spec.md +0 -295
  148. package/openspec/changes/rename-package-to-retest/tasks.md +0 -71
  149. package/openspec/project.md +0 -31
  150. package/openspec/specs/mcp-server-core/spec.md +0 -178
  151. package/openspec/specs/webtest-lifecycle/spec.md +0 -136
  152. package/openspec/specs/webtest-logging/spec.md +0 -209
  153. package/openspec/specs/webtest-prompts/spec.md +0 -155
  154. package/openspec/specs/webtest-resources/spec.md +0 -248
  155. package/openspec/specs/webtest-sampling/spec.md +0 -344
  156. package/openspec/specs/webtest-tools/spec.md +0 -282
  157. package/release.config.js +0 -9
  158. package/src/config.test.ts +0 -96
  159. package/src/config.ts +0 -32
  160. package/src/elicitation/index.test.ts +0 -399
  161. package/src/elicitation/index.ts +0 -171
  162. package/src/elicitation/types.ts +0 -68
  163. package/src/index.ts +0 -83
  164. package/src/lifecycle/index.test.ts +0 -260
  165. package/src/lifecycle/index.ts +0 -101
  166. package/src/logger.redaction.test.ts +0 -322
  167. package/src/logger.test.ts +0 -123
  168. package/src/logger.ts +0 -229
  169. package/src/playwright-client/index.ts +0 -392
  170. package/src/playwright-client/types.ts +0 -99
  171. package/src/progress/index.test.ts +0 -327
  172. package/src/progress/index.ts +0 -170
  173. package/src/progress/types.ts +0 -25
  174. package/src/prompts/index.test.ts +0 -451
  175. package/src/prompts/index.ts +0 -246
  176. package/src/prompts/loader.test.ts +0 -100
  177. package/src/prompts/loader.ts +0 -59
  178. package/src/prompts/templates/mcp/webtest-crawl.md +0 -7
  179. package/src/prompts/templates/mcp/webtest-full-workflow.md +0 -12
  180. package/src/resources/index.ts +0 -250
  181. package/src/resources/subscriptions.ts +0 -37
  182. package/src/sampling/index.test.ts +0 -414
  183. package/src/sampling/index.ts +0 -286
  184. package/src/sampling/prompts.ts +0 -194
  185. package/src/sampling/types.ts +0 -60
  186. package/src/schemas/config.ts +0 -39
  187. package/src/security/index.test.ts +0 -441
  188. package/src/security/index.ts +0 -361
  189. package/src/security/security-scenarios.test.ts +0 -468
  190. package/src/server.ts +0 -211
  191. package/src/test-utils/index.ts +0 -6
  192. package/src/test-utils/mock-context.ts +0 -426
  193. package/src/test-utils/mock-playwright-client.ts +0 -422
  194. package/src/tools/index.ts +0 -11
  195. package/src/tools/webtest/crawl.test.ts +0 -834
  196. package/src/tools/webtest/crawl.ts +0 -901
  197. package/src/tools/webtest/discover-features.ts +0 -412
  198. package/src/tools/webtest/discover-flows.ts +0 -408
  199. package/src/tools/webtest/generate-tests.test.ts +0 -532
  200. package/src/tools/webtest/generate-tests.ts +0 -425
  201. package/src/tools/webtest/index.ts +0 -7
  202. package/src/tools/webtest/integration.test.ts +0 -536
  203. package/src/tools/webtest/run-test-case.test.ts +0 -659
  204. package/src/tools/webtest/run-test-case.ts +0 -508
  205. package/src/tools/webtest/schemas.ts +0 -201
  206. package/src/tools/webtest/start-analysis.test.ts +0 -151
  207. package/src/tools/webtest/start-analysis.ts +0 -158
  208. package/src/transports/http.ts +0 -19
  209. package/src/transports/index.ts +0 -30
  210. package/src/transports/stdio.ts +0 -7
  211. package/src/types/capabilities.test.ts +0 -193
  212. package/src/types/capabilities.ts +0 -50
  213. package/src/types/context.ts +0 -21
  214. package/src/types/tool.ts +0 -11
  215. package/src/workspace/index.ts +0 -945
  216. package/src/workspace/markdown.ts +0 -272
  217. package/src/workspace/types.ts +0 -186
  218. package/tests/integration/server.test.ts +0 -89
  219. package/tests/integration/tools.test.ts +0 -99
  220. package/tsconfig.json +0 -20
  221. package/vitest.config.ts +0 -9
  222. package/vitest.integration.config.ts +0 -10
  223. /package/{src → dist}/prompts/templates/sampling/crawl-action.md +0 -0
  224. /package/{src → dist}/prompts/templates/sampling/feature-discovery.md +0 -0
  225. /package/{src → dist}/prompts/templates/sampling/flow-discovery.md +0 -0
  226. /package/{src → dist}/prompts/templates/sampling/page-content-wrapper.md +0 -0
  227. /package/{src → dist}/prompts/templates/sampling/test-evaluation.md +0 -0
  228. /package/{src → dist}/prompts/templates/sampling/test-generation.md +0 -0
  229. /package/dist/tools/{webtest → retest}/crawl.d.ts +0 -0
  230. /package/dist/tools/{webtest → retest}/discover-features.d.ts +0 -0
  231. /package/dist/tools/{webtest → retest}/discover-flows.d.ts +0 -0
  232. /package/dist/tools/{webtest → retest}/generate-tests.d.ts +0 -0
  233. /package/dist/tools/{webtest → retest}/index.d.ts +0 -0
  234. /package/dist/tools/{webtest → retest}/index.js +0 -0
  235. /package/dist/tools/{webtest → retest}/run-test-case.d.ts +0 -0
  236. /package/dist/tools/{webtest → retest}/schemas.d.ts +0 -0
  237. /package/dist/tools/{webtest → retest}/schemas.js +0 -0
  238. /package/dist/tools/{webtest → retest}/start-analysis.d.ts +0 -0
@@ -1,100 +0,0 @@
1
- import { describe, it, expect, beforeEach, afterEach } from "vitest";
2
- import { writeFileSync, mkdirSync, rmSync } from "node:fs";
3
- import { resolve, dirname } from "node:path";
4
- import { fileURLToPath } from "node:url";
5
- import { loadTemplate, interpolate, renderTemplate, clearTemplateCache } from "./loader.js";
6
-
7
- const __dirname = dirname(fileURLToPath(import.meta.url));
8
-
9
- describe("Template Loader", () => {
10
- describe("interpolate", () => {
11
- it("replaces single variable", () => {
12
- const result = interpolate("Hello ${name}!", { name: "World" });
13
- expect(result).toBe("Hello World!");
14
- });
15
-
16
- it("replaces multiple variables", () => {
17
- const result = interpolate("${greeting} ${name}!", { greeting: "Hello", name: "World" });
18
- expect(result).toBe("Hello World!");
19
- });
20
-
21
- it("replaces same variable multiple times", () => {
22
- const result = interpolate("${x} + ${x} = ${y}", { x: "1", y: "2" });
23
- expect(result).toBe("1 + 1 = 2");
24
- });
25
-
26
- it("keeps unmatched placeholders unchanged", () => {
27
- const result = interpolate("Hello ${name}! Your id is ${id}.", { name: "World" });
28
- expect(result).toBe("Hello World! Your id is ${id}.");
29
- });
30
-
31
- it("handles empty vars object", () => {
32
- const result = interpolate("Hello ${name}!", {});
33
- expect(result).toBe("Hello ${name}!");
34
- });
35
-
36
- it("converts numbers to strings", () => {
37
- const result = interpolate("Count: ${count}", { count: 42 });
38
- expect(result).toBe("Count: 42");
39
- });
40
-
41
- it("converts booleans to strings", () => {
42
- const result = interpolate("Active: ${active}", { active: true });
43
- expect(result).toBe("Active: true");
44
- });
45
-
46
- it("handles undefined values by keeping placeholder", () => {
47
- const result = interpolate("Value: ${val}", { val: undefined });
48
- expect(result).toBe("Value: ${val}");
49
- });
50
-
51
- it("preserves text without placeholders", () => {
52
- const result = interpolate("No variables here", { name: "ignored" });
53
- expect(result).toBe("No variables here");
54
- });
55
-
56
- it("handles multiline templates", () => {
57
- const template = `Line 1: \${a}
58
- Line 2: \${b}
59
- Line 3: \${c}`;
60
- const result = interpolate(template, { a: "A", b: "B", c: "C" });
61
- expect(result).toBe("Line 1: A\nLine 2: B\nLine 3: C");
62
- });
63
- });
64
-
65
- describe("loadTemplate", () => {
66
- beforeEach(() => {
67
- clearTemplateCache();
68
- });
69
-
70
- it("loads existing template file", () => {
71
- // This test depends on actual template files existing
72
- // Will be validated after templates are created
73
- });
74
-
75
- it("throws error for non-existent template", () => {
76
- expect(() => loadTemplate("sampling", "non-existent-template")).toThrow();
77
- });
78
-
79
- it("caches loaded templates", () => {
80
- // Cache behavior is internal, but we can verify it doesn't throw on repeated calls
81
- // Will be validated after templates are created
82
- });
83
- });
84
-
85
- describe("renderTemplate", () => {
86
- beforeEach(() => {
87
- clearTemplateCache();
88
- });
89
-
90
- it("loads and interpolates template in one call", () => {
91
- // Will be validated after templates are created
92
- });
93
- });
94
-
95
- describe("clearTemplateCache", () => {
96
- it("clears the cache without error", () => {
97
- expect(() => clearTemplateCache()).not.toThrow();
98
- });
99
- });
100
- });
@@ -1,59 +0,0 @@
1
- import { readFileSync } from "node:fs";
2
- import { resolve, dirname } from "node:path";
3
- import { fileURLToPath } from "node:url";
4
-
5
- const __dirname = dirname(fileURLToPath(import.meta.url));
6
-
7
- const templateCache = new Map<string, string>();
8
-
9
- /**
10
- * Load a template file from the templates directory.
11
- * Templates are cached after first load.
12
- */
13
- export function loadTemplate(category: "sampling" | "mcp", name: string): string {
14
- const cacheKey = `${category}/${name}`;
15
-
16
- if (templateCache.has(cacheKey)) {
17
- return templateCache.get(cacheKey)!;
18
- }
19
-
20
- const templatePath = resolve(__dirname, "templates", category, `${name}.md`);
21
- const content = readFileSync(templatePath, "utf-8");
22
- templateCache.set(cacheKey, content);
23
-
24
- return content;
25
- }
26
-
27
- /**
28
- * Interpolate variables in a template string.
29
- * Replaces ${varName} patterns with values from the vars object.
30
- * Unmatched placeholders are left unchanged.
31
- */
32
- export function interpolate(template: string, vars: Record<string, string | number | boolean | undefined>): string {
33
- return template.replace(/\$\{(\w+)\}/g, (match, varName) => {
34
- const value = vars[varName];
35
- if (value === undefined) {
36
- return match; // Keep placeholder if var not provided
37
- }
38
- return String(value);
39
- });
40
- }
41
-
42
- /**
43
- * Load a template and interpolate variables in one step.
44
- */
45
- export function renderTemplate(
46
- category: "sampling" | "mcp",
47
- name: string,
48
- vars: Record<string, string | number | boolean | undefined> = {}
49
- ): string {
50
- const template = loadTemplate(category, name);
51
- return interpolate(template, vars);
52
- }
53
-
54
- /**
55
- * Clear the template cache (useful for testing).
56
- */
57
- export function clearTemplateCache(): void {
58
- templateCache.clear();
59
- }
@@ -1,7 +0,0 @@
1
- Please crawl the web application with these parameters:
2
-
3
- Analysis ID: ${analysisId}
4
- Goal: ${goal}
5
- Strategy: ${strategy}
6
-
7
- Use webtest_crawl_app to explore the application. Report progress and let me know if you encounter any obstacles (cookie banners, modals, authentication).
@@ -1,12 +0,0 @@
1
- Please run a complete web testing workflow for: ${url}
2
- Focus: ${focus}
3
-
4
- Execute these steps in order:
5
- 1. webtest_init - Initialize the workspace
6
- 2. webtest_crawl_app - Explore the application with a goal related to "${focus}"
7
- 3. webtest_discover_features - Identify application features
8
- 4. webtest_discover_flows - Discover flows for each feature
9
- 5. webtest_generate_tests - Create test cases
10
- 6. webtest_run_test - Run at least one critical test
11
-
12
- Report progress at each step and summarize the overall testing results at the end.
@@ -1,250 +0,0 @@
1
- import { readFile } from "node:fs/promises";
2
- import { join, extname } from "node:path";
3
- import type { Logger } from "../logger.js";
4
- import type { Config } from "../schemas/config.js";
5
- import type { ClientCapabilities } from "../types/capabilities.js";
6
- import type { WorkspaceManager } from "../workspace/index.js";
7
- import { createResourceSubscriptions, type ResourceSubscriptions } from "./subscriptions.js";
8
-
9
- export { createResourceSubscriptions, type ResourceSubscriptions };
10
-
11
- export interface Resource {
12
- uri: string;
13
- name: string;
14
- description?: string;
15
- mimeType?: string;
16
- }
17
-
18
- export interface ResourceContent {
19
- uri: string;
20
- mimeType: string;
21
- text?: string;
22
- blob?: string;
23
- }
24
-
25
- export interface ResourceManager {
26
- listResources(analysisId?: string): Promise<Resource[]>;
27
- readResource(uri: string): Promise<ResourceContent>;
28
- notifyListChanged(): Promise<void>;
29
- notifyResourceUpdated(uri: string): Promise<void>;
30
- subscriptions: ResourceSubscriptions;
31
- }
32
-
33
- const MIME_TYPES: Record<string, string> = {
34
- ".json": "application/json",
35
- ".md": "text/markdown",
36
- ".html": "text/html",
37
- ".png": "image/png",
38
- ".jpg": "image/jpeg",
39
- ".jpeg": "image/jpeg",
40
- };
41
-
42
- export function createResourceManager(
43
- config: Config,
44
- workspaceManager: WorkspaceManager,
45
- sendNotification: (method: string, params: unknown) => Promise<void>,
46
- capabilities: ClientCapabilities,
47
- logger: Logger
48
- ): ResourceManager {
49
- const subscriptions = createResourceSubscriptions(logger);
50
- const hasListChanged = capabilities.resourcesListChanged;
51
- const hasSubscribe = capabilities.resourcesSubscribe;
52
-
53
- function parseWebtestUri(uri: string): { analysisId: string; path: string } | null {
54
- if (!uri.startsWith("webtest://")) {
55
- return null;
56
- }
57
-
58
- const withoutScheme = uri.slice("webtest://".length);
59
- const slashIndex = withoutScheme.indexOf("/");
60
-
61
- if (slashIndex === -1) {
62
- return { analysisId: withoutScheme, path: "" };
63
- }
64
-
65
- return {
66
- analysisId: withoutScheme.slice(0, slashIndex),
67
- path: withoutScheme.slice(slashIndex + 1),
68
- };
69
- }
70
-
71
- function getMimeType(path: string): string {
72
- const ext = extname(path).toLowerCase();
73
- return MIME_TYPES[ext] || "application/octet-stream";
74
- }
75
-
76
- return {
77
- subscriptions,
78
-
79
- async listResources(analysisId?: string): Promise<Resource[]> {
80
- const resources: Resource[] = [];
81
-
82
- const workspaces = analysisId
83
- ? [analysisId]
84
- : await workspaceManager.listWorkspaces();
85
-
86
- for (const wsId of workspaces) {
87
- if (!(await workspaceManager.workspaceExists(wsId))) {
88
- continue;
89
- }
90
-
91
- const index = await workspaceManager.readWorkspaceIndex(wsId);
92
-
93
- // Add workspace index (now markdown)
94
- resources.push({
95
- uri: `webtest://${wsId}/index.md`,
96
- name: `Analysis: ${index.url}`,
97
- description: `Analysis workspace for ${index.domain}`,
98
- mimeType: "text/markdown",
99
- });
100
-
101
- // Add crawl resources
102
- for (const crawl of index.crawls) {
103
- resources.push({
104
- uri: `webtest://${wsId}/crawls/${crawl.crawlId}/index.md`,
105
- name: `Crawl: ${crawl.goal.slice(0, 50)}`,
106
- description: `Crawl ${crawl.status} - ${crawl.pagesVisited} pages`,
107
- mimeType: "text/markdown",
108
- });
109
-
110
- // Read crawl index to get page resources
111
- try {
112
- const crawlIndex = await workspaceManager.readCrawlIndex(
113
- wsId,
114
- crawl.crawlId
115
- );
116
-
117
- for (const page of crawlIndex.pages) {
118
- resources.push({
119
- uri: page.screenshotUri,
120
- name: `Screenshot: ${page.title || page.url}`,
121
- mimeType: "image/png",
122
- });
123
- resources.push({
124
- uri: page.snapshotUri,
125
- name: `Snapshot: ${page.title || page.url}`,
126
- mimeType: "text/markdown",
127
- });
128
- }
129
- } catch {
130
- // Crawl index might not exist yet
131
- }
132
- }
133
-
134
- // Add features resources (new structure)
135
- if (index.features) {
136
- resources.push({
137
- uri: index.features.featuresUri,
138
- name: `Features (${index.features.featureCount})`,
139
- description: "Discovered application features",
140
- mimeType: "text/markdown",
141
- });
142
- }
143
-
144
- // Add feature flows resources
145
- if (index.featureFlows) {
146
- for (const flow of index.featureFlows) {
147
- resources.push({
148
- uri: flow.flowsUri,
149
- name: `Flows: ${flow.featureSlug}`,
150
- description: `${flow.flowCount} flows for ${flow.featureSlug} feature`,
151
- mimeType: "text/markdown",
152
- });
153
- }
154
- }
155
-
156
-
157
- // Add test resources
158
- if (index.tests) {
159
- resources.push({
160
- uri: index.tests.testsUri,
161
- name: `Tests (${index.tests.testCount} cases)`,
162
- description: "Generated test cases",
163
- mimeType: "text/markdown",
164
- });
165
- }
166
-
167
- // Add test run resources
168
- for (const run of index.runs) {
169
- resources.push({
170
- uri: `webtest://${wsId}/runs/${run.runId}/report.md`,
171
- name: `Test Run: ${run.testCaseId}`,
172
- description: `Test run ${run.status}`,
173
- mimeType: "text/markdown",
174
- });
175
- }
176
- }
177
-
178
- return resources;
179
- },
180
-
181
- async readResource(uri: string): Promise<ResourceContent> {
182
- const parsed = parseWebtestUri(uri);
183
-
184
- if (!parsed) {
185
- throw new Error(`Invalid resource URI: ${uri}`);
186
- }
187
-
188
- const { analysisId, path } = parsed;
189
- const workspacePath = workspaceManager.getWorkspacePath(analysisId);
190
- const filePath = join(workspacePath, path);
191
- const mimeType = getMimeType(path);
192
-
193
- try {
194
- if (mimeType.startsWith("image/")) {
195
- const data = await readFile(filePath);
196
- return {
197
- uri,
198
- mimeType,
199
- blob: data.toString("base64"),
200
- };
201
- } else {
202
- const text = await readFile(filePath, "utf-8");
203
- return {
204
- uri,
205
- mimeType,
206
- text,
207
- };
208
- }
209
- } catch (error) {
210
- throw new Error(
211
- `Resource not found: ${uri} (${error instanceof Error ? error.message : "Unknown error"})`
212
- );
213
- }
214
- },
215
-
216
- async notifyListChanged(): Promise<void> {
217
- if (!hasListChanged) {
218
- logger.debug("listChanged notification skipped (not supported)");
219
- return;
220
- }
221
-
222
- logger.debug("Emitting resources/list_changed notification");
223
-
224
- try {
225
- await sendNotification("notifications/resources/list_changed", {});
226
- } catch (error) {
227
- logger.warn("Failed to emit list_changed notification", {
228
- error: error instanceof Error ? error.message : "Unknown error",
229
- });
230
- }
231
- },
232
-
233
- async notifyResourceUpdated(uri: string): Promise<void> {
234
- if (!hasSubscribe || !subscriptions.isSubscribed(uri)) {
235
- return;
236
- }
237
-
238
- logger.debug("Emitting resources/updated notification", { uri });
239
-
240
- try {
241
- await sendNotification("notifications/resources/updated", { uri });
242
- } catch (error) {
243
- logger.warn("Failed to emit resource updated notification", {
244
- uri,
245
- error: error instanceof Error ? error.message : "Unknown error",
246
- });
247
- }
248
- },
249
- };
250
- }
@@ -1,37 +0,0 @@
1
- import type { Logger } from "../logger.js";
2
-
3
- export interface ResourceSubscriptions {
4
- subscribe(uri: string): void;
5
- unsubscribe(uri: string): void;
6
- isSubscribed(uri: string): boolean;
7
- getSubscribers(): string[];
8
- clear(): void;
9
- }
10
-
11
- export function createResourceSubscriptions(logger: Logger): ResourceSubscriptions {
12
- const subscriptions = new Set<string>();
13
-
14
- return {
15
- subscribe(uri: string): void {
16
- subscriptions.add(uri);
17
- logger.debug("Resource subscribed", { uri });
18
- },
19
-
20
- unsubscribe(uri: string): void {
21
- subscriptions.delete(uri);
22
- logger.debug("Resource unsubscribed", { uri });
23
- },
24
-
25
- isSubscribed(uri: string): boolean {
26
- return subscriptions.has(uri);
27
- },
28
-
29
- getSubscribers(): string[] {
30
- return Array.from(subscriptions);
31
- },
32
-
33
- clear(): void {
34
- subscriptions.clear();
35
- },
36
- };
37
- }