@kirrosh/zond 0.7.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 (102) hide show
  1. package/CHANGELOG.md +130 -0
  2. package/LICENSE +21 -0
  3. package/README.md +130 -0
  4. package/package.json +53 -0
  5. package/src/bun-types.d.ts +5 -0
  6. package/src/cli/commands/add-api.ts +51 -0
  7. package/src/cli/commands/ai-generate.ts +106 -0
  8. package/src/cli/commands/chat.ts +43 -0
  9. package/src/cli/commands/ci-init.ts +163 -0
  10. package/src/cli/commands/collections.ts +41 -0
  11. package/src/cli/commands/compare.ts +129 -0
  12. package/src/cli/commands/coverage.ts +156 -0
  13. package/src/cli/commands/doctor.ts +127 -0
  14. package/src/cli/commands/init.ts +84 -0
  15. package/src/cli/commands/mcp.ts +16 -0
  16. package/src/cli/commands/run.ts +156 -0
  17. package/src/cli/commands/runs.ts +108 -0
  18. package/src/cli/commands/serve.ts +22 -0
  19. package/src/cli/commands/update.ts +142 -0
  20. package/src/cli/commands/validate.ts +18 -0
  21. package/src/cli/index.ts +529 -0
  22. package/src/cli/output.ts +24 -0
  23. package/src/cli/runtime.ts +7 -0
  24. package/src/core/agent/agent-loop.ts +116 -0
  25. package/src/core/agent/context-manager.ts +41 -0
  26. package/src/core/agent/system-prompt.ts +28 -0
  27. package/src/core/agent/tools/diagnose-failure.ts +51 -0
  28. package/src/core/agent/tools/explore-api.ts +40 -0
  29. package/src/core/agent/tools/index.ts +46 -0
  30. package/src/core/agent/tools/query-results.ts +40 -0
  31. package/src/core/agent/tools/run-tests.ts +38 -0
  32. package/src/core/agent/tools/send-request.ts +44 -0
  33. package/src/core/agent/tools/validate-tests.ts +23 -0
  34. package/src/core/agent/types.ts +22 -0
  35. package/src/core/diagnostics/failure-hints.ts +63 -0
  36. package/src/core/generator/ai/ai-generator.ts +61 -0
  37. package/src/core/generator/ai/llm-client.ts +159 -0
  38. package/src/core/generator/ai/output-parser.ts +307 -0
  39. package/src/core/generator/ai/prompt-builder.ts +153 -0
  40. package/src/core/generator/ai/types.ts +56 -0
  41. package/src/core/generator/chunker.ts +47 -0
  42. package/src/core/generator/coverage-scanner.ts +87 -0
  43. package/src/core/generator/data-factory.ts +115 -0
  44. package/src/core/generator/endpoint-warnings.ts +43 -0
  45. package/src/core/generator/index.ts +12 -0
  46. package/src/core/generator/openapi-reader.ts +143 -0
  47. package/src/core/generator/schema-utils.ts +52 -0
  48. package/src/core/generator/serializer.ts +189 -0
  49. package/src/core/generator/types.ts +48 -0
  50. package/src/core/parser/filter.ts +14 -0
  51. package/src/core/parser/index.ts +21 -0
  52. package/src/core/parser/schema.ts +175 -0
  53. package/src/core/parser/types.ts +52 -0
  54. package/src/core/parser/variables.ts +154 -0
  55. package/src/core/parser/yaml-parser.ts +85 -0
  56. package/src/core/reporter/console.ts +175 -0
  57. package/src/core/reporter/index.ts +23 -0
  58. package/src/core/reporter/json.ts +9 -0
  59. package/src/core/reporter/junit.ts +78 -0
  60. package/src/core/reporter/types.ts +12 -0
  61. package/src/core/runner/assertions.ts +173 -0
  62. package/src/core/runner/execute-run.ts +97 -0
  63. package/src/core/runner/executor.ts +183 -0
  64. package/src/core/runner/http-client.ts +69 -0
  65. package/src/core/runner/index.ts +12 -0
  66. package/src/core/runner/types.ts +48 -0
  67. package/src/core/setup-api.ts +113 -0
  68. package/src/core/utils.ts +9 -0
  69. package/src/db/queries.ts +774 -0
  70. package/src/db/schema.ts +159 -0
  71. package/src/mcp/descriptions.ts +88 -0
  72. package/src/mcp/server.ts +52 -0
  73. package/src/mcp/tools/ci-init.ts +54 -0
  74. package/src/mcp/tools/coverage-analysis.ts +141 -0
  75. package/src/mcp/tools/describe-endpoint.ts +241 -0
  76. package/src/mcp/tools/explore-api.ts +84 -0
  77. package/src/mcp/tools/generate-and-save.ts +129 -0
  78. package/src/mcp/tools/generate-missing-tests.ts +91 -0
  79. package/src/mcp/tools/generate-tests-guide.ts +391 -0
  80. package/src/mcp/tools/manage-server.ts +86 -0
  81. package/src/mcp/tools/query-db.ts +255 -0
  82. package/src/mcp/tools/run-tests.ts +71 -0
  83. package/src/mcp/tools/save-test-suite.ts +218 -0
  84. package/src/mcp/tools/send-request.ts +63 -0
  85. package/src/mcp/tools/set-work-dir.ts +35 -0
  86. package/src/mcp/tools/setup-api.ts +84 -0
  87. package/src/mcp/tools/validate-tests.ts +43 -0
  88. package/src/tui/chat-ui.ts +150 -0
  89. package/src/web/data/collection-state.ts +360 -0
  90. package/src/web/routes/api.ts +234 -0
  91. package/src/web/routes/dashboard.ts +313 -0
  92. package/src/web/routes/runs.ts +64 -0
  93. package/src/web/schemas.ts +121 -0
  94. package/src/web/server.ts +134 -0
  95. package/src/web/static/htmx.min.js +1 -0
  96. package/src/web/static/style.css +827 -0
  97. package/src/web/views/endpoints-tab.ts +170 -0
  98. package/src/web/views/health-strip.ts +92 -0
  99. package/src/web/views/layout.ts +48 -0
  100. package/src/web/views/results.ts +209 -0
  101. package/src/web/views/runs-tab.ts +126 -0
  102. package/src/web/views/suites-tab.ts +153 -0
@@ -0,0 +1,113 @@
1
+ import { resolve, join } from "path";
2
+ import { mkdirSync, writeFileSync, existsSync, readFileSync } from "fs";
3
+ import { getDb } from "../db/schema.ts";
4
+ import { createCollection, deleteCollection, findCollectionByNameOrId, normalizePath } from "../db/queries.ts";
5
+ import { readOpenApiSpec, extractEndpoints } from "./generator/index.ts";
6
+
7
+ function toYaml(vars: Record<string, string>): string {
8
+ const lines: string[] = [];
9
+ for (const [k, v] of Object.entries(vars)) {
10
+ const needsQuote = /[:#\[\]{}&*!|>'"@`,%]/.test(v) || v.includes(" ") || v === "";
11
+ lines.push(`${k}: ${needsQuote ? `"${v.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"` : v}`);
12
+ }
13
+ return lines.join("\n");
14
+ }
15
+
16
+ export interface SetupApiOptions {
17
+ name: string;
18
+ spec?: string;
19
+ dir?: string;
20
+ envVars?: Record<string, string>;
21
+ dbPath?: string;
22
+ force?: boolean;
23
+ }
24
+
25
+ export interface SetupApiResult {
26
+ created: true;
27
+ collectionId: number;
28
+ baseDir: string;
29
+ testPath: string;
30
+ baseUrl: string;
31
+ specEndpoints: number;
32
+ }
33
+
34
+ export async function setupApi(options: SetupApiOptions): Promise<SetupApiResult> {
35
+ const { name, spec, dbPath } = options;
36
+
37
+ getDb(dbPath);
38
+
39
+ // Validate name uniqueness (or force-replace)
40
+ const existing = findCollectionByNameOrId(name);
41
+ if (existing) {
42
+ if (options.force) {
43
+ deleteCollection(existing.id, true);
44
+ } else {
45
+ throw new Error(`API '${name}' already exists (id=${existing.id})`);
46
+ }
47
+ }
48
+
49
+ // Sanitize name for directory use
50
+ const dirName = name.replace(/[^a-zA-Z0-9_\-\.]/g, "-").toLowerCase();
51
+ const baseDir = resolve(options.dir ?? `./apis/${dirName}/`);
52
+ const testPath = join(baseDir, "tests");
53
+
54
+ // Create directories
55
+ mkdirSync(testPath, { recursive: true });
56
+
57
+ // Try to load and validate spec, extract base_url
58
+ let openapiSpec: string | null = null;
59
+ let baseUrl = "";
60
+ let endpointCount = 0;
61
+ if (spec) {
62
+ const doc = await readOpenApiSpec(spec);
63
+ openapiSpec = spec;
64
+ if ((doc as any).servers?.[0]?.url) {
65
+ baseUrl = (doc as any).servers[0].url;
66
+ }
67
+ endpointCount = extractEndpoints(doc).length;
68
+ }
69
+
70
+ // Build environment variables
71
+ const envVars: Record<string, string> = {};
72
+ if (baseUrl) envVars.base_url = baseUrl;
73
+ if (options.envVars) {
74
+ Object.assign(envVars, options.envVars);
75
+ }
76
+
77
+ // Write .env.yaml in base_dir
78
+ if (Object.keys(envVars).length > 0) {
79
+ const envFilePath = join(baseDir, ".env.yaml");
80
+ writeFileSync(envFilePath, toYaml(envVars) + "\n", "utf-8");
81
+ }
82
+
83
+ // Create/update .gitignore to exclude env files
84
+ const gitignorePath = join(baseDir, ".gitignore");
85
+ const gitignoreContent = existsSync(gitignorePath) ? readFileSync(gitignorePath, "utf-8") : "";
86
+ if (!gitignoreContent.includes(".env*.yaml")) {
87
+ writeFileSync(
88
+ gitignorePath,
89
+ gitignoreContent + (gitignoreContent.endsWith("\n") || !gitignoreContent ? "" : "\n") + ".env*.yaml\n",
90
+ "utf-8",
91
+ );
92
+ }
93
+
94
+ const normalizedTestPath = normalizePath(testPath);
95
+ const normalizedBaseDir = normalizePath(baseDir);
96
+
97
+ // Create collection in DB
98
+ const collectionId = createCollection({
99
+ name,
100
+ base_dir: normalizedBaseDir,
101
+ test_path: normalizedTestPath,
102
+ openapi_spec: openapiSpec ?? undefined,
103
+ });
104
+
105
+ return {
106
+ created: true,
107
+ collectionId,
108
+ baseDir: normalizedBaseDir,
109
+ testPath: normalizedTestPath,
110
+ baseUrl,
111
+ specEndpoints: endpointCount,
112
+ };
113
+ }
@@ -0,0 +1,9 @@
1
+ export function getByPath(obj: unknown, path: string, defaultVal?: unknown): unknown {
2
+ const keys = path.split(".");
3
+ let result: unknown = obj;
4
+ for (const key of keys) {
5
+ result = (result as Record<string, unknown>)?.[key];
6
+ if (result === undefined) return defaultVal;
7
+ }
8
+ return result;
9
+ }