@syrin/cli 1.3.1 → 1.4.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.
- package/README.md +36 -0
- package/dist/cli/commands/config.d.ts +47 -0
- package/dist/cli/commands/config.js +360 -0
- package/dist/cli/commands/dev.d.ts +6 -0
- package/dist/cli/commands/dev.js +67 -15
- package/dist/cli/commands/doctor.js +49 -13
- package/dist/cli/commands/init.d.ts +2 -0
- package/dist/cli/commands/init.js +89 -18
- package/dist/cli/commands/status.d.ts +10 -0
- package/dist/cli/commands/status.js +162 -0
- package/dist/cli/index.js +211 -12
- package/dist/cli/prompts/init-prompt.d.ts +18 -0
- package/dist/cli/prompts/init-prompt.js +159 -99
- package/dist/cli/utils/command-error-handler.js +2 -5
- package/dist/config/env-checker.d.ts +12 -2
- package/dist/config/env-checker.js +88 -38
- package/dist/config/env-templates.d.ts +15 -0
- package/dist/config/env-templates.js +49 -0
- package/dist/config/generator.js +17 -0
- package/dist/config/global-loader.d.ts +50 -0
- package/dist/config/global-loader.js +244 -0
- package/dist/config/loader.d.ts +28 -0
- package/dist/config/loader.js +95 -9
- package/dist/config/merger.d.ts +37 -0
- package/dist/config/merger.js +68 -0
- package/dist/config/schema.d.ts +26 -1
- package/dist/config/schema.js +73 -8
- package/dist/config/types.d.ts +19 -0
- package/dist/config/types.js +26 -1
- package/dist/constants/messages.d.ts +7 -0
- package/dist/constants/messages.js +8 -0
- package/dist/constants/paths.d.ts +6 -0
- package/dist/constants/paths.js +10 -0
- package/dist/events/emitter.js +7 -7
- package/dist/index.js +0 -0
- package/dist/presentation/config-ui.d.ts +34 -0
- package/dist/presentation/config-ui.js +139 -0
- package/dist/presentation/doctor-ui.d.ts +11 -0
- package/dist/presentation/doctor-ui.js +52 -1
- package/dist/presentation/init-ui.d.ts +9 -0
- package/dist/presentation/init-ui.js +33 -0
- package/dist/runtime/analysis/analyser.js +2 -2
- package/dist/runtime/analysis/rules/warnings/w104-generic-description.d.ts +1 -1
- package/dist/runtime/analysis/rules/warnings/w104-generic-description.js +1 -1
- package/dist/runtime/dev/event-mapper.js +19 -3
- package/dist/runtime/dev/session.d.ts +4 -0
- package/dist/runtime/dev/session.js +52 -3
- package/dist/runtime/llm/ollama.js +4 -4
- package/dist/runtime/mcp/client/manager.js +3 -3
- package/dist/runtime/sandbox/executor.js +5 -5
- package/dist/runtime/test/orchestrator.js +4 -4
- package/dist/utils/editor.d.ts +37 -0
- package/dist/utils/editor.js +137 -0
- package/dist/utils/logger.d.ts +24 -6
- package/dist/utils/logger.js +51 -8
- package/package.json +4 -4
- package/dist/runtime/analysis/rules/errors/e001-missing-output-schema.d.ts +0 -22
- package/dist/runtime/analysis/rules/errors/e001-missing-output-schema.js +0 -30
- package/dist/runtime/analysis/rules/errors/e002-underspecified-input.d.ts +0 -24
- package/dist/runtime/analysis/rules/errors/e002-underspecified-input.js +0 -52
- package/dist/runtime/analysis/rules/errors/e003-type-mismatch.d.ts +0 -23
- package/dist/runtime/analysis/rules/errors/e003-type-mismatch.js +0 -73
- package/dist/runtime/analysis/rules/errors/e004-free-text-propagation.d.ts +0 -23
- package/dist/runtime/analysis/rules/errors/e004-free-text-propagation.js +0 -47
- package/dist/runtime/analysis/rules/errors/e005-tool-ambiguity.d.ts +0 -25
- package/dist/runtime/analysis/rules/errors/e005-tool-ambiguity.js +0 -73
- package/dist/runtime/analysis/rules/errors/e006-param-not-in-description.d.ts +0 -22
- package/dist/runtime/analysis/rules/errors/e006-param-not-in-description.js +0 -57
- package/dist/runtime/analysis/rules/errors/e007-output-not-guaranteed.d.ts +0 -23
- package/dist/runtime/analysis/rules/errors/e007-output-not-guaranteed.js +0 -56
- package/dist/runtime/analysis/rules/errors/e008-circular-dependency.d.ts +0 -22
- package/dist/runtime/analysis/rules/errors/e008-circular-dependency.js +0 -84
- package/dist/runtime/analysis/rules/errors/e009-implicit-user-input.d.ts +0 -23
- package/dist/runtime/analysis/rules/errors/e009-implicit-user-input.js +0 -89
- package/dist/runtime/analysis/rules/errors/e010-non-serializable.d.ts +0 -25
- package/dist/runtime/analysis/rules/errors/e010-non-serializable.js +0 -46
- package/dist/runtime/analysis/rules/errors/e011-missing-tool-description.d.ts +0 -24
- package/dist/runtime/analysis/rules/errors/e011-missing-tool-description.js +0 -33
- package/dist/runtime/analysis/rules/errors/e012-side-effect-detected.d.ts +0 -39
- package/dist/runtime/analysis/rules/errors/e012-side-effect-detected.js +0 -40
- package/dist/runtime/analysis/rules/errors/e013-non-deterministic-output.d.ts +0 -37
- package/dist/runtime/analysis/rules/errors/e013-non-deterministic-output.js +0 -34
- package/dist/runtime/analysis/rules/errors/e013-output-explosion.d.ts +0 -39
- package/dist/runtime/analysis/rules/errors/e013-output-explosion.js +0 -36
- package/dist/runtime/analysis/rules/errors/e014-hidden-dependency.d.ts +0 -42
- package/dist/runtime/analysis/rules/errors/e014-hidden-dependency.js +0 -46
- package/dist/runtime/analysis/rules/errors/e014-output-explosion.d.ts +0 -39
- package/dist/runtime/analysis/rules/errors/e014-output-explosion.js +0 -36
- package/dist/runtime/analysis/rules/errors/e015-hidden-dependency.d.ts +0 -42
- package/dist/runtime/analysis/rules/errors/e015-hidden-dependency.js +0 -46
- package/dist/runtime/analysis/rules/errors/e015-unbounded-execution.d.ts +0 -44
- package/dist/runtime/analysis/rules/errors/e015-unbounded-execution.js +0 -66
- package/dist/runtime/analysis/rules/errors/e016-output-validation-failed.d.ts +0 -43
- package/dist/runtime/analysis/rules/errors/e016-output-validation-failed.js +0 -42
- package/dist/runtime/analysis/rules/errors/e016-unbounded-execution.d.ts +0 -44
- package/dist/runtime/analysis/rules/errors/e016-unbounded-execution.js +0 -66
- package/dist/runtime/analysis/rules/errors/e017-input-validation-failed.d.ts +0 -57
- package/dist/runtime/analysis/rules/errors/e017-input-validation-failed.js +0 -80
- package/dist/runtime/analysis/rules/errors/e017-output-validation-failed.d.ts +0 -43
- package/dist/runtime/analysis/rules/errors/e017-output-validation-failed.js +0 -42
- package/dist/runtime/analysis/rules/errors/e018-input-validation-failed.d.ts +0 -57
- package/dist/runtime/analysis/rules/errors/e018-input-validation-failed.js +0 -80
- package/dist/runtime/analysis/rules/errors/e018-tool-execution-failed.d.ts +0 -38
- package/dist/runtime/analysis/rules/errors/e018-tool-execution-failed.js +0 -37
- package/dist/runtime/analysis/rules/errors/e019-tool-execution-failed.d.ts +0 -38
- package/dist/runtime/analysis/rules/errors/e019-tool-execution-failed.js +0 -37
- package/dist/runtime/analysis/rules/errors/e019-unexpected-test-result.d.ts +0 -65
- package/dist/runtime/analysis/rules/errors/e019-unexpected-test-result.js +0 -109
- package/dist/runtime/analysis/rules/errors/e020-unexpected-test-result.d.ts +0 -65
- package/dist/runtime/analysis/rules/errors/e020-unexpected-test-result.js +0 -109
- package/dist/runtime/analysis/rules/warnings/w001-implicit-dependency.d.ts +0 -22
- package/dist/runtime/analysis/rules/warnings/w001-implicit-dependency.js +0 -39
- package/dist/runtime/analysis/rules/warnings/w002-free-text-without-normalization.d.ts +0 -24
- package/dist/runtime/analysis/rules/warnings/w002-free-text-without-normalization.js +0 -40
- package/dist/runtime/analysis/rules/warnings/w003-missing-examples.d.ts +0 -22
- package/dist/runtime/analysis/rules/warnings/w003-missing-examples.js +0 -84
- package/dist/runtime/analysis/rules/warnings/w004-overloaded-responsibility.d.ts +0 -23
- package/dist/runtime/analysis/rules/warnings/w004-overloaded-responsibility.js +0 -96
- package/dist/runtime/analysis/rules/warnings/w005-generic-description.d.ts +0 -53
- package/dist/runtime/analysis/rules/warnings/w005-generic-description.js +0 -108
- package/dist/runtime/analysis/rules/warnings/w006-optional-as-required.d.ts +0 -22
- package/dist/runtime/analysis/rules/warnings/w006-optional-as-required.js +0 -44
- package/dist/runtime/analysis/rules/warnings/w007-broad-output-schema.d.ts +0 -23
- package/dist/runtime/analysis/rules/warnings/w007-broad-output-schema.js +0 -37
- package/dist/runtime/analysis/rules/warnings/w008-multiple-entry-points.d.ts +0 -22
- package/dist/runtime/analysis/rules/warnings/w008-multiple-entry-points.js +0 -97
- package/dist/runtime/analysis/rules/warnings/w009-hidden-side-effects.d.ts +0 -23
- package/dist/runtime/analysis/rules/warnings/w009-hidden-side-effects.js +0 -88
- package/dist/runtime/analysis/rules/warnings/w010-output-not-reusable.d.ts +0 -22
- package/dist/runtime/analysis/rules/warnings/w010-output-not-reusable.js +0 -81
- package/dist/runtime/analysis/rules/warnings/w021-weak-schema.d.ts +0 -40
- package/dist/runtime/analysis/rules/warnings/w021-weak-schema.js +0 -32
- package/dist/runtime/analysis/rules/warnings/w022-high-entropy-output.d.ts +0 -39
- package/dist/runtime/analysis/rules/warnings/w022-high-entropy-output.js +0 -36
- package/dist/runtime/analysis/rules/warnings/w023-unstable-defaults.d.ts +0 -38
- package/dist/runtime/analysis/rules/warnings/w023-unstable-defaults.js +0 -36
- package/dist/runtime/test/dependency-tracker.d.ts +0 -66
- package/dist/runtime/test/dependency-tracker.js +0 -80
- package/dist/runtime/test/formatters.d.ts +0 -18
- package/dist/runtime/test/formatters.js +0 -172
- package/dist/runtime/test/input-generator.d.ts +0 -33
- package/dist/runtime/test/input-generator.js +0 -498
- package/dist/runtime/test/mcp-root-detector.d.ts +0 -31
- package/dist/runtime/test/mcp-root-detector.js +0 -105
- package/dist/runtime/test/retry-tester.d.ts +0 -44
- package/dist/runtime/test/retry-tester.js +0 -103
- package/dist/runtime/test/synthetic-input-generator.d.ts +0 -11
- package/dist/runtime/test/synthetic-input-generator.js +0 -154
- package/dist/runtime/test/test-runner.d.ts +0 -28
- package/dist/runtime/test/test-runner.js +0 -55
|
@@ -1,498 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Synthetic input generator.
|
|
3
|
-
* Generates test inputs from JSON Schema definitions.
|
|
4
|
-
*/
|
|
5
|
-
/**
|
|
6
|
-
* Generate test inputs from a JSON Schema.
|
|
7
|
-
* Includes both predictable patterns and agent-like unpredictable variations.
|
|
8
|
-
* @param schema - JSON Schema definition
|
|
9
|
-
* @returns Array of generated input objects
|
|
10
|
-
*/
|
|
11
|
-
export function generateInputsFromSchema(schema) {
|
|
12
|
-
const inputs = [];
|
|
13
|
-
// 1. Predictable patterns (for correctness testing)
|
|
14
|
-
// Generate minimal valid input (only required fields)
|
|
15
|
-
inputs.push(generateMinimalInput(schema));
|
|
16
|
-
// Generate input with defaults (optional fields with default values)
|
|
17
|
-
inputs.push(generateWithDefaults(schema));
|
|
18
|
-
// Generate edge cases
|
|
19
|
-
inputs.push(...generateEdgeCases(schema));
|
|
20
|
-
// 2. Agent-like unpredictable patterns (for agent safety testing)
|
|
21
|
-
// Generate random variations (agent-like inputs)
|
|
22
|
-
inputs.push(...generateAgentLikeInputs(schema));
|
|
23
|
-
// Generate invalid-but-close inputs (type coercion attempts)
|
|
24
|
-
inputs.push(...generateInvalidButCloseInputs(schema));
|
|
25
|
-
// Generate extreme values (very long strings, very large numbers)
|
|
26
|
-
inputs.push(...generateExtremeValueInputs(schema));
|
|
27
|
-
// Remove duplicates (by JSON string comparison)
|
|
28
|
-
const uniqueInputs = removeDuplicates(inputs);
|
|
29
|
-
return uniqueInputs;
|
|
30
|
-
}
|
|
31
|
-
/**
|
|
32
|
-
* Type guard for JSONSchemaField.
|
|
33
|
-
*/
|
|
34
|
-
function isJSONSchemaField(obj) {
|
|
35
|
-
return (typeof obj === 'object' &&
|
|
36
|
-
obj !== null &&
|
|
37
|
-
('type' in obj || 'properties' in obj || 'items' in obj || '$ref' in obj));
|
|
38
|
-
}
|
|
39
|
-
/**
|
|
40
|
-
* Generate minimal input (only required fields).
|
|
41
|
-
*/
|
|
42
|
-
function generateMinimalInput(schema) {
|
|
43
|
-
const input = {};
|
|
44
|
-
if (!isJSONSchemaField(schema)) {
|
|
45
|
-
return input;
|
|
46
|
-
}
|
|
47
|
-
const properties = schema.properties;
|
|
48
|
-
const required = schema.required;
|
|
49
|
-
if (!properties) {
|
|
50
|
-
return input;
|
|
51
|
-
}
|
|
52
|
-
// Only include required fields
|
|
53
|
-
if (required) {
|
|
54
|
-
for (const fieldName of required) {
|
|
55
|
-
const fieldSchema = properties[fieldName];
|
|
56
|
-
if (fieldSchema) {
|
|
57
|
-
input[fieldName] = generateValue(fieldSchema);
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
return input;
|
|
62
|
-
}
|
|
63
|
-
/**
|
|
64
|
-
* Generate input with default values.
|
|
65
|
-
*/
|
|
66
|
-
function generateWithDefaults(schema) {
|
|
67
|
-
const minimal = generateMinimalInput(schema);
|
|
68
|
-
if (!isJSONSchemaField(schema)) {
|
|
69
|
-
return minimal;
|
|
70
|
-
}
|
|
71
|
-
const properties = schema.properties;
|
|
72
|
-
if (!properties) {
|
|
73
|
-
return minimal;
|
|
74
|
-
}
|
|
75
|
-
// Add optional fields with defaults if they exist
|
|
76
|
-
for (const [fieldName, fieldSchema] of Object.entries(properties)) {
|
|
77
|
-
if (!(fieldName in minimal)) {
|
|
78
|
-
if (isJSONSchemaField(fieldSchema) && 'default' in fieldSchema) {
|
|
79
|
-
minimal[fieldName] = fieldSchema.default;
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
return minimal;
|
|
84
|
-
}
|
|
85
|
-
/**
|
|
86
|
-
* Generate edge case inputs.
|
|
87
|
-
*/
|
|
88
|
-
function generateEdgeCases(schema) {
|
|
89
|
-
const cases = [];
|
|
90
|
-
if (!isJSONSchemaField(schema)) {
|
|
91
|
-
return cases;
|
|
92
|
-
}
|
|
93
|
-
const properties = schema.properties;
|
|
94
|
-
const required = schema.required;
|
|
95
|
-
if (!properties) {
|
|
96
|
-
return cases;
|
|
97
|
-
}
|
|
98
|
-
// Generate edge cases for each field
|
|
99
|
-
for (const [fieldName, fieldSchema] of Object.entries(properties)) {
|
|
100
|
-
const isRequired = required?.includes(fieldName) ?? false;
|
|
101
|
-
// Generate edge case: empty string (if field is string)
|
|
102
|
-
if (isStringType(fieldSchema)) {
|
|
103
|
-
const edgeCase = {};
|
|
104
|
-
if (isRequired) {
|
|
105
|
-
edgeCase[fieldName] = '';
|
|
106
|
-
cases.push(edgeCase);
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
// Generate edge case: null (if nullable)
|
|
110
|
-
if (isNullable(fieldSchema)) {
|
|
111
|
-
const edgeCase = {};
|
|
112
|
-
if (isRequired) {
|
|
113
|
-
edgeCase[fieldName] = null;
|
|
114
|
-
cases.push(edgeCase);
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
// Generate edge case: empty array (if field is array)
|
|
118
|
-
if (isArrayType(fieldSchema)) {
|
|
119
|
-
const edgeCase = {};
|
|
120
|
-
if (isRequired) {
|
|
121
|
-
edgeCase[fieldName] = [];
|
|
122
|
-
cases.push(edgeCase);
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
return cases;
|
|
127
|
-
}
|
|
128
|
-
/**
|
|
129
|
-
* Generate a value from a field schema.
|
|
130
|
-
*/
|
|
131
|
-
function generateValue(schema) {
|
|
132
|
-
if (!isJSONSchemaField(schema)) {
|
|
133
|
-
return null;
|
|
134
|
-
}
|
|
135
|
-
// Check for enum
|
|
136
|
-
if (Array.isArray(schema.enum) && schema.enum.length > 0) {
|
|
137
|
-
return schema.enum[0];
|
|
138
|
-
}
|
|
139
|
-
// Check for default value
|
|
140
|
-
if ('default' in schema) {
|
|
141
|
-
return schema.default;
|
|
142
|
-
}
|
|
143
|
-
// Generate based on type
|
|
144
|
-
const type = schema.type;
|
|
145
|
-
if (Array.isArray(type)) {
|
|
146
|
-
// Use first non-null type
|
|
147
|
-
const firstType = type.find(t => t !== 'null') || type[0];
|
|
148
|
-
if (firstType) {
|
|
149
|
-
return generateValueByType(firstType, schema);
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
if (typeof type === 'string') {
|
|
153
|
-
return generateValueByType(type, schema);
|
|
154
|
-
}
|
|
155
|
-
// If no type specified, try to infer from other properties
|
|
156
|
-
if (isJSONSchemaField(schema)) {
|
|
157
|
-
if (schema.properties) {
|
|
158
|
-
return {};
|
|
159
|
-
}
|
|
160
|
-
if (schema.items) {
|
|
161
|
-
return [];
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
return null;
|
|
165
|
-
}
|
|
166
|
-
/**
|
|
167
|
-
* Generate value by type.
|
|
168
|
-
*/
|
|
169
|
-
function generateValueByType(type, schema) {
|
|
170
|
-
switch (type) {
|
|
171
|
-
case 'string':
|
|
172
|
-
if (typeof schema === 'object' &&
|
|
173
|
-
schema !== null &&
|
|
174
|
-
'pattern' in schema) {
|
|
175
|
-
// For patterns, return a simple string (pattern matching is complex)
|
|
176
|
-
return 'test_string';
|
|
177
|
-
}
|
|
178
|
-
return 'test_string';
|
|
179
|
-
case 'number':
|
|
180
|
-
case 'integer':
|
|
181
|
-
if (schema.minimum !== undefined) {
|
|
182
|
-
return schema.minimum;
|
|
183
|
-
}
|
|
184
|
-
if (schema.maximum !== undefined) {
|
|
185
|
-
return schema.maximum;
|
|
186
|
-
}
|
|
187
|
-
return 0;
|
|
188
|
-
case 'boolean':
|
|
189
|
-
return false;
|
|
190
|
-
case 'array':
|
|
191
|
-
return [];
|
|
192
|
-
case 'object':
|
|
193
|
-
return {};
|
|
194
|
-
case 'null':
|
|
195
|
-
return null;
|
|
196
|
-
default:
|
|
197
|
-
return null;
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
/**
|
|
201
|
-
* Check if schema is string type.
|
|
202
|
-
*/
|
|
203
|
-
function isStringType(schema) {
|
|
204
|
-
if (!isJSONSchemaField(schema)) {
|
|
205
|
-
return false;
|
|
206
|
-
}
|
|
207
|
-
const type = schema.type;
|
|
208
|
-
if (typeof type === 'string') {
|
|
209
|
-
return type === 'string';
|
|
210
|
-
}
|
|
211
|
-
if (Array.isArray(type)) {
|
|
212
|
-
return type.includes('string');
|
|
213
|
-
}
|
|
214
|
-
return false;
|
|
215
|
-
}
|
|
216
|
-
/**
|
|
217
|
-
* Check if schema is array type.
|
|
218
|
-
*/
|
|
219
|
-
function isArrayType(schema) {
|
|
220
|
-
if (!isJSONSchemaField(schema)) {
|
|
221
|
-
return false;
|
|
222
|
-
}
|
|
223
|
-
const type = schema.type;
|
|
224
|
-
if (typeof type === 'string') {
|
|
225
|
-
return type === 'array';
|
|
226
|
-
}
|
|
227
|
-
if (Array.isArray(type)) {
|
|
228
|
-
return type.includes('array');
|
|
229
|
-
}
|
|
230
|
-
return false;
|
|
231
|
-
}
|
|
232
|
-
/**
|
|
233
|
-
* Check if schema is nullable.
|
|
234
|
-
*/
|
|
235
|
-
function isNullable(schema) {
|
|
236
|
-
if (!isJSONSchemaField(schema)) {
|
|
237
|
-
return false;
|
|
238
|
-
}
|
|
239
|
-
if (schema.nullable === true) {
|
|
240
|
-
return true;
|
|
241
|
-
}
|
|
242
|
-
const type = schema.type;
|
|
243
|
-
if (Array.isArray(type)) {
|
|
244
|
-
return type.includes('null');
|
|
245
|
-
}
|
|
246
|
-
return false;
|
|
247
|
-
}
|
|
248
|
-
/**
|
|
249
|
-
* Generate agent-like unpredictable inputs (random variations).
|
|
250
|
-
* Simulates how agents might generate inputs.
|
|
251
|
-
*/
|
|
252
|
-
function generateAgentLikeInputs(schema) {
|
|
253
|
-
const inputs = [];
|
|
254
|
-
if (!isJSONSchemaField(schema)) {
|
|
255
|
-
return inputs;
|
|
256
|
-
}
|
|
257
|
-
const properties = schema.properties;
|
|
258
|
-
const required = schema.required;
|
|
259
|
-
if (!properties) {
|
|
260
|
-
return inputs;
|
|
261
|
-
}
|
|
262
|
-
// Generate 3 random variations with random values
|
|
263
|
-
const numVariations = 3;
|
|
264
|
-
for (let i = 0; i < numVariations; i++) {
|
|
265
|
-
const input = {};
|
|
266
|
-
// Include all required fields with random values
|
|
267
|
-
if (required) {
|
|
268
|
-
for (const fieldName of required) {
|
|
269
|
-
const fieldSchema = properties[fieldName];
|
|
270
|
-
if (fieldSchema) {
|
|
271
|
-
input[fieldName] = generateRandomValue(fieldSchema);
|
|
272
|
-
}
|
|
273
|
-
}
|
|
274
|
-
}
|
|
275
|
-
// Randomly include some optional fields (agent-like behavior)
|
|
276
|
-
for (const [fieldName, fieldSchema] of Object.entries(properties)) {
|
|
277
|
-
if (!required?.includes(fieldName) && Math.random() > 0.5) {
|
|
278
|
-
input[fieldName] = generateRandomValue(fieldSchema);
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
if (Object.keys(input).length > 0) {
|
|
282
|
-
inputs.push(input);
|
|
283
|
-
}
|
|
284
|
-
}
|
|
285
|
-
return inputs;
|
|
286
|
-
}
|
|
287
|
-
/**
|
|
288
|
-
* Generate invalid-but-close inputs (type coercion attempts).
|
|
289
|
-
* Simulates agents trying to pass slightly wrong types.
|
|
290
|
-
*/
|
|
291
|
-
function generateInvalidButCloseInputs(schema) {
|
|
292
|
-
const inputs = [];
|
|
293
|
-
if (!isJSONSchemaField(schema)) {
|
|
294
|
-
return inputs;
|
|
295
|
-
}
|
|
296
|
-
const properties = schema.properties;
|
|
297
|
-
const required = schema.required;
|
|
298
|
-
if (!properties) {
|
|
299
|
-
return inputs;
|
|
300
|
-
}
|
|
301
|
-
// For each required string field, try passing a number (agent might do type coercion)
|
|
302
|
-
if (required) {
|
|
303
|
-
for (const fieldName of required) {
|
|
304
|
-
const fieldSchema = properties[fieldName];
|
|
305
|
-
if (isStringType(fieldSchema)) {
|
|
306
|
-
const input = {};
|
|
307
|
-
// Try number instead of string (common agent mistake)
|
|
308
|
-
input[fieldName] = Math.floor(Math.random() * 1000);
|
|
309
|
-
// Fill other required fields with valid values
|
|
310
|
-
for (const otherField of required) {
|
|
311
|
-
if (otherField !== fieldName) {
|
|
312
|
-
const prop = properties[otherField];
|
|
313
|
-
if (!prop) {
|
|
314
|
-
throw new Error(`Malformed schema: required field "${otherField}" not found in properties`);
|
|
315
|
-
}
|
|
316
|
-
input[otherField] = generateValue(prop);
|
|
317
|
-
}
|
|
318
|
-
}
|
|
319
|
-
inputs.push(input);
|
|
320
|
-
}
|
|
321
|
-
// Try null for required non-nullable fields
|
|
322
|
-
if (!isNullable(fieldSchema)) {
|
|
323
|
-
const input = {};
|
|
324
|
-
input[fieldName] = null;
|
|
325
|
-
for (const otherField of required) {
|
|
326
|
-
if (otherField !== fieldName) {
|
|
327
|
-
const prop = properties[otherField];
|
|
328
|
-
if (!prop) {
|
|
329
|
-
throw new Error(`Malformed schema: required field "${otherField}" not found in properties`);
|
|
330
|
-
}
|
|
331
|
-
input[otherField] = generateValue(prop);
|
|
332
|
-
}
|
|
333
|
-
}
|
|
334
|
-
inputs.push(input);
|
|
335
|
-
}
|
|
336
|
-
}
|
|
337
|
-
}
|
|
338
|
-
return inputs;
|
|
339
|
-
}
|
|
340
|
-
/**
|
|
341
|
-
* Generate extreme value inputs (very long strings, very large numbers).
|
|
342
|
-
* Tests tools against agent-generated extreme inputs.
|
|
343
|
-
*/
|
|
344
|
-
function generateExtremeValueInputs(schema) {
|
|
345
|
-
const inputs = [];
|
|
346
|
-
if (!isJSONSchemaField(schema)) {
|
|
347
|
-
return inputs;
|
|
348
|
-
}
|
|
349
|
-
const properties = schema.properties;
|
|
350
|
-
const required = schema.required;
|
|
351
|
-
if (!properties) {
|
|
352
|
-
return inputs;
|
|
353
|
-
}
|
|
354
|
-
// Generate extreme string (1000+ characters)
|
|
355
|
-
if (required) {
|
|
356
|
-
for (const fieldName of required) {
|
|
357
|
-
const fieldSchema = properties[fieldName];
|
|
358
|
-
if (isStringType(fieldSchema)) {
|
|
359
|
-
const input = {};
|
|
360
|
-
// Very long string (agent might paste large text)
|
|
361
|
-
input[fieldName] =
|
|
362
|
-
'A'.repeat(1000) + ' very long string that agents might generate';
|
|
363
|
-
for (const otherField of required) {
|
|
364
|
-
if (otherField !== fieldName) {
|
|
365
|
-
const prop = properties[otherField];
|
|
366
|
-
if (!prop) {
|
|
367
|
-
throw new Error(`Malformed schema: required field "${otherField}" not found in properties`);
|
|
368
|
-
}
|
|
369
|
-
input[otherField] = generateValue(prop);
|
|
370
|
-
}
|
|
371
|
-
}
|
|
372
|
-
inputs.push(input);
|
|
373
|
-
}
|
|
374
|
-
// Very large number
|
|
375
|
-
if (isNumberType(fieldSchema)) {
|
|
376
|
-
const input = {};
|
|
377
|
-
input[fieldName] = Number.MAX_SAFE_INTEGER;
|
|
378
|
-
for (const otherField of required) {
|
|
379
|
-
if (otherField !== fieldName) {
|
|
380
|
-
const prop = properties[otherField];
|
|
381
|
-
if (!prop) {
|
|
382
|
-
throw new Error(`Malformed schema: required field "${otherField}" not found in properties`);
|
|
383
|
-
}
|
|
384
|
-
input[otherField] = generateValue(prop);
|
|
385
|
-
}
|
|
386
|
-
}
|
|
387
|
-
inputs.push(input);
|
|
388
|
-
// Very small number
|
|
389
|
-
const input2 = {};
|
|
390
|
-
input2[fieldName] = Number.MIN_SAFE_INTEGER;
|
|
391
|
-
for (const otherField of required) {
|
|
392
|
-
if (otherField !== fieldName) {
|
|
393
|
-
const prop = properties[otherField];
|
|
394
|
-
if (!prop) {
|
|
395
|
-
throw new Error(`Malformed schema: required field "${otherField}" not found in properties`);
|
|
396
|
-
}
|
|
397
|
-
input2[otherField] = generateValue(prop);
|
|
398
|
-
}
|
|
399
|
-
}
|
|
400
|
-
inputs.push(input2);
|
|
401
|
-
}
|
|
402
|
-
}
|
|
403
|
-
}
|
|
404
|
-
return inputs;
|
|
405
|
-
}
|
|
406
|
-
/**
|
|
407
|
-
* Generate a random value from a field schema (for agent-like inputs).
|
|
408
|
-
*/
|
|
409
|
-
function generateRandomValue(schema) {
|
|
410
|
-
if (!isJSONSchemaField(schema)) {
|
|
411
|
-
return null;
|
|
412
|
-
}
|
|
413
|
-
// Check for enum - pick random enum value
|
|
414
|
-
if (Array.isArray(schema.enum) && schema.enum.length > 0) {
|
|
415
|
-
return schema.enum[Math.floor(Math.random() * schema.enum.length)];
|
|
416
|
-
}
|
|
417
|
-
// Generate based on type with randomness
|
|
418
|
-
const type = schema.type;
|
|
419
|
-
if (typeof type === 'string') {
|
|
420
|
-
return generateRandomValueByType(type, schema);
|
|
421
|
-
}
|
|
422
|
-
if (Array.isArray(type)) {
|
|
423
|
-
const firstType = type.find(t => t !== 'null') || type[0];
|
|
424
|
-
if (firstType) {
|
|
425
|
-
return generateRandomValueByType(firstType, schema);
|
|
426
|
-
}
|
|
427
|
-
}
|
|
428
|
-
return null;
|
|
429
|
-
}
|
|
430
|
-
/**
|
|
431
|
-
* Generate random value by type.
|
|
432
|
-
*/
|
|
433
|
-
function generateRandomValueByType(type, schema) {
|
|
434
|
-
switch (type) {
|
|
435
|
-
case 'string': {
|
|
436
|
-
// Random string with various patterns (agent-like)
|
|
437
|
-
const patterns = [
|
|
438
|
-
`random_${Math.random().toString(36).substring(7)}`,
|
|
439
|
-
`Test String ${Date.now()}`,
|
|
440
|
-
`Mixed123String${Math.floor(Math.random() * 100)}`,
|
|
441
|
-
`special-chars-!@#$%^&*()`,
|
|
442
|
-
`unicode-测试-☀️🌧️`,
|
|
443
|
-
];
|
|
444
|
-
return patterns[Math.floor(Math.random() * patterns.length)];
|
|
445
|
-
}
|
|
446
|
-
case 'number':
|
|
447
|
-
case 'integer': {
|
|
448
|
-
const min = schema.minimum ?? -1000;
|
|
449
|
-
const max = schema.maximum ?? 1000;
|
|
450
|
-
return Math.floor(Math.random() * (max - min + 1)) + min;
|
|
451
|
-
}
|
|
452
|
-
case 'boolean':
|
|
453
|
-
return Math.random() > 0.5;
|
|
454
|
-
case 'array': {
|
|
455
|
-
// Random array with 0-5 elements
|
|
456
|
-
const length = Math.floor(Math.random() * 6);
|
|
457
|
-
return Array.from({ length }, () => generateRandomValue(schema.items || {}));
|
|
458
|
-
}
|
|
459
|
-
case 'object':
|
|
460
|
-
return {};
|
|
461
|
-
case 'null':
|
|
462
|
-
return null;
|
|
463
|
-
default:
|
|
464
|
-
return null;
|
|
465
|
-
}
|
|
466
|
-
}
|
|
467
|
-
/**
|
|
468
|
-
* Check if schema is number type.
|
|
469
|
-
*/
|
|
470
|
-
function isNumberType(schema) {
|
|
471
|
-
if (!isJSONSchemaField(schema)) {
|
|
472
|
-
return false;
|
|
473
|
-
}
|
|
474
|
-
const type = schema.type;
|
|
475
|
-
if (typeof type === 'string') {
|
|
476
|
-
return type === 'number' || type === 'integer';
|
|
477
|
-
}
|
|
478
|
-
if (Array.isArray(type)) {
|
|
479
|
-
return type.includes('number') || type.includes('integer');
|
|
480
|
-
}
|
|
481
|
-
return false;
|
|
482
|
-
}
|
|
483
|
-
/**
|
|
484
|
-
* Remove duplicate inputs.
|
|
485
|
-
*/
|
|
486
|
-
function removeDuplicates(inputs) {
|
|
487
|
-
const seen = new Set();
|
|
488
|
-
const unique = [];
|
|
489
|
-
for (const input of inputs) {
|
|
490
|
-
const key = JSON.stringify(input, Object.keys(input).sort());
|
|
491
|
-
if (!seen.has(key)) {
|
|
492
|
-
seen.add(key);
|
|
493
|
-
unique.push(input);
|
|
494
|
-
}
|
|
495
|
-
}
|
|
496
|
-
return unique;
|
|
497
|
-
}
|
|
498
|
-
//# sourceMappingURL=input-generator.js.map
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* MCP root detection utility.
|
|
3
|
-
* Detects the MCP server root directory using priority: CLI flag > config > auto-detect.
|
|
4
|
-
*/
|
|
5
|
-
/**
|
|
6
|
-
* Options for MCP root detection.
|
|
7
|
-
*/
|
|
8
|
-
export interface MCPRootDetectionOptions {
|
|
9
|
-
/** Explicit MCP root from CLI flag */
|
|
10
|
-
cliMCPRoot?: string;
|
|
11
|
-
/** MCP root from config file */
|
|
12
|
-
configMCPRoot?: string;
|
|
13
|
-
/** Current working directory (defaults to process.cwd()) */
|
|
14
|
-
cwd?: string;
|
|
15
|
-
}
|
|
16
|
-
/**
|
|
17
|
-
* Detect MCP root directory using priority order.
|
|
18
|
-
* Priority: CLI flag > Config > Auto-detect > Current working directory
|
|
19
|
-
*
|
|
20
|
-
* @param options - Detection options
|
|
21
|
-
* @returns Resolved MCP root directory (absolute path)
|
|
22
|
-
*/
|
|
23
|
-
export declare function detectMCPRoot(options?: MCPRootDetectionOptions): string;
|
|
24
|
-
/**
|
|
25
|
-
* Resolve tools directory path relative to MCP root.
|
|
26
|
-
* @param mcpRoot - MCP root directory
|
|
27
|
-
* @param toolsDir - Tools directory name (default: "tools")
|
|
28
|
-
* @returns Absolute path to tools directory
|
|
29
|
-
*/
|
|
30
|
-
export declare function resolveToolsDir(mcpRoot: string, toolsDir?: string): string;
|
|
31
|
-
//# sourceMappingURL=mcp-root-detector.d.ts.map
|
|
@@ -1,105 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* MCP root detection utility.
|
|
3
|
-
* Detects the MCP server root directory using priority: CLI flag > config > auto-detect.
|
|
4
|
-
*/
|
|
5
|
-
import * as fs from 'fs';
|
|
6
|
-
import * as path from 'path';
|
|
7
|
-
/**
|
|
8
|
-
* Auto-detect MCP root by looking for common indicators.
|
|
9
|
-
* @param startDir - Directory to start searching from
|
|
10
|
-
* @returns Detected MCP root, or null if not found
|
|
11
|
-
*/
|
|
12
|
-
function autoDetectMCPRoot(startDir) {
|
|
13
|
-
let currentDir = path.resolve(startDir);
|
|
14
|
-
// Look up the directory tree for indicators
|
|
15
|
-
while (currentDir !== path.dirname(currentDir)) {
|
|
16
|
-
// Check for tools directory
|
|
17
|
-
const toolsDir = path.join(currentDir, 'tools');
|
|
18
|
-
try {
|
|
19
|
-
if (fs.existsSync(toolsDir) && fs.statSync(toolsDir).isDirectory()) {
|
|
20
|
-
return currentDir;
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
catch (_error) {
|
|
24
|
-
// If fs.existsSync or fs.statSync throws, treat as not present
|
|
25
|
-
// Continue searching
|
|
26
|
-
}
|
|
27
|
-
// Check for common MCP server files
|
|
28
|
-
const serverFiles = [
|
|
29
|
-
'server.py',
|
|
30
|
-
'server.ts',
|
|
31
|
-
'server.js',
|
|
32
|
-
'package.json',
|
|
33
|
-
'requirements.txt',
|
|
34
|
-
];
|
|
35
|
-
for (const file of serverFiles) {
|
|
36
|
-
const filePath = path.join(currentDir, file);
|
|
37
|
-
try {
|
|
38
|
-
if (fs.existsSync(filePath)) {
|
|
39
|
-
// Server file found - return currentDir (tools directory check already handled above)
|
|
40
|
-
return currentDir;
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
catch (_error) {
|
|
44
|
-
// Ignore errors and continue
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
// Move up one directory
|
|
48
|
-
currentDir = path.dirname(currentDir);
|
|
49
|
-
}
|
|
50
|
-
return null;
|
|
51
|
-
}
|
|
52
|
-
/**
|
|
53
|
-
* Validate MCP root path and throw descriptive errors if invalid.
|
|
54
|
-
*/
|
|
55
|
-
function validateMCPRootPath(sourcePath, resolvedPath, source) {
|
|
56
|
-
if (!fs.existsSync(resolvedPath)) {
|
|
57
|
-
const prefix = source === 'CLI flag' ? '' : 'from config ';
|
|
58
|
-
throw new Error(`MCP root directory ${prefix}not found: ${sourcePath} (resolved to: ${resolvedPath})`);
|
|
59
|
-
}
|
|
60
|
-
// Ensure it's a directory, not a file
|
|
61
|
-
const stats = fs.statSync(resolvedPath);
|
|
62
|
-
if (!stats.isDirectory()) {
|
|
63
|
-
const prefix = source === 'CLI flag' ? '' : 'from config ';
|
|
64
|
-
throw new Error(`MCP root path ${prefix}is not a directory: ${sourcePath} (resolved to: ${resolvedPath})`);
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
/**
|
|
68
|
-
* Detect MCP root directory using priority order.
|
|
69
|
-
* Priority: CLI flag > Config > Auto-detect > Current working directory
|
|
70
|
-
*
|
|
71
|
-
* @param options - Detection options
|
|
72
|
-
* @returns Resolved MCP root directory (absolute path)
|
|
73
|
-
*/
|
|
74
|
-
export function detectMCPRoot(options = {}) {
|
|
75
|
-
const cwd = options.cwd || process.cwd();
|
|
76
|
-
// Priority 1: CLI flag (explicit)
|
|
77
|
-
if (options.cliMCPRoot) {
|
|
78
|
-
const resolved = path.resolve(cwd, options.cliMCPRoot);
|
|
79
|
-
validateMCPRootPath(options.cliMCPRoot, resolved, 'from CLI flag');
|
|
80
|
-
return resolved;
|
|
81
|
-
}
|
|
82
|
-
// Priority 2: Config file
|
|
83
|
-
if (options.configMCPRoot) {
|
|
84
|
-
const resolved = path.resolve(cwd, options.configMCPRoot);
|
|
85
|
-
validateMCPRootPath(options.configMCPRoot, resolved, 'from config');
|
|
86
|
-
return resolved;
|
|
87
|
-
}
|
|
88
|
-
// Priority 3: Auto-detect
|
|
89
|
-
const autoDetected = autoDetectMCPRoot(cwd);
|
|
90
|
-
if (autoDetected) {
|
|
91
|
-
return autoDetected;
|
|
92
|
-
}
|
|
93
|
-
// Priority 4: Default to current working directory
|
|
94
|
-
return path.resolve(cwd);
|
|
95
|
-
}
|
|
96
|
-
/**
|
|
97
|
-
* Resolve tools directory path relative to MCP root.
|
|
98
|
-
* @param mcpRoot - MCP root directory
|
|
99
|
-
* @param toolsDir - Tools directory name (default: "tools")
|
|
100
|
-
* @returns Absolute path to tools directory
|
|
101
|
-
*/
|
|
102
|
-
export function resolveToolsDir(mcpRoot, toolsDir = 'tools') {
|
|
103
|
-
return path.resolve(mcpRoot, toolsDir);
|
|
104
|
-
}
|
|
105
|
-
//# sourceMappingURL=mcp-root-detector.js.map
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Retry scenario tester.
|
|
3
|
-
* Tests tools against agent retry patterns (retry on failure, timeout, error).
|
|
4
|
-
*/
|
|
5
|
-
import type { SandboxExecutor, ToolExecutionResult } from '../../runtime/sandbox/index.js';
|
|
6
|
-
/**
|
|
7
|
-
* Retry test configuration.
|
|
8
|
-
*/
|
|
9
|
-
export interface RetryTestConfig {
|
|
10
|
-
/** Maximum number of retries to test (default: 3) */
|
|
11
|
-
maxRetries?: number;
|
|
12
|
-
/** Retry delay in milliseconds (default: 100) */
|
|
13
|
-
retryDelayMs?: number;
|
|
14
|
-
/** Test retry on failure (default: true) */
|
|
15
|
-
testRetryOnFailure?: boolean;
|
|
16
|
-
/** Test retry on timeout (default: true) */
|
|
17
|
-
testRetryOnTimeout?: boolean;
|
|
18
|
-
/** Test retry on connection error (default: true) */
|
|
19
|
-
testRetryOnConnectionError?: boolean;
|
|
20
|
-
}
|
|
21
|
-
/**
|
|
22
|
-
* Retry test result.
|
|
23
|
-
*/
|
|
24
|
-
export interface RetryTestResult {
|
|
25
|
-
/** Input tested */
|
|
26
|
-
testInput: Record<string, unknown>;
|
|
27
|
-
/** Retry attempts made */
|
|
28
|
-
attempts: number;
|
|
29
|
-
/** Whether tool eventually succeeded after retries */
|
|
30
|
-
eventuallySucceeded: boolean;
|
|
31
|
-
/** Results from all retry attempts */
|
|
32
|
-
results: ToolExecutionResult[];
|
|
33
|
-
/** Issues detected */
|
|
34
|
-
issues: string[];
|
|
35
|
-
}
|
|
36
|
-
/**
|
|
37
|
-
* Test tool retry behavior.
|
|
38
|
-
* Simulates agent retry patterns to ensure tools handle retries gracefully.
|
|
39
|
-
*
|
|
40
|
-
* Note: Idempotency (output consistency across retries) is checked separately
|
|
41
|
-
* This tester focuses on retry reliability.
|
|
42
|
-
*/
|
|
43
|
-
export declare function testRetryBehavior(executor: SandboxExecutor, toolName: string, testInput: Record<string, unknown>, config?: RetryTestConfig, toolTimeoutMs?: number): Promise<RetryTestResult>;
|
|
44
|
-
//# sourceMappingURL=retry-tester.d.ts.map
|