@robota-sdk/agent-tools 3.0.0-beta.6 → 3.0.0-beta.60
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 +38 -10
- package/dist/node/index.cjs +299 -228
- package/dist/node/index.d.cts +3 -24
- package/dist/node/index.d.ts +3 -24
- package/dist/node/index.js +308 -237
- package/package.json +14 -3
package/dist/node/index.js
CHANGED
|
@@ -165,7 +165,9 @@ function zodToJsonSchema(schema, options = {}) {
|
|
|
165
165
|
type: "object",
|
|
166
166
|
properties,
|
|
167
167
|
required,
|
|
168
|
-
...options.allowAdditionalProperties
|
|
168
|
+
...(options.allowAdditionalProperties || schemaDef.unknownKeys === "passthrough") && {
|
|
169
|
+
additionalProperties: true
|
|
170
|
+
}
|
|
169
171
|
};
|
|
170
172
|
}
|
|
171
173
|
function convertZodTypeToProperty(typeObj) {
|
|
@@ -244,6 +246,100 @@ function isRequiredField(typeObj) {
|
|
|
244
246
|
return typeDef.typeName !== "ZodOptional" && typeDef.typeName !== "ZodNullable" && typeDef.typeName !== "ZodDefault";
|
|
245
247
|
}
|
|
246
248
|
|
|
249
|
+
// src/implementations/function-tool/parameter-validator.ts
|
|
250
|
+
function validateParameterType(key, value, schema) {
|
|
251
|
+
const expectedType = schema["type"];
|
|
252
|
+
switch (expectedType) {
|
|
253
|
+
case "string":
|
|
254
|
+
if (typeof value !== "string") {
|
|
255
|
+
return `Parameter "${key}" must be a string, got ${typeof value}`;
|
|
256
|
+
}
|
|
257
|
+
break;
|
|
258
|
+
case "number":
|
|
259
|
+
if (typeof value !== "number" || isNaN(value)) {
|
|
260
|
+
return `Parameter "${key}" must be a number, got ${typeof value}`;
|
|
261
|
+
}
|
|
262
|
+
break;
|
|
263
|
+
case "boolean":
|
|
264
|
+
if (typeof value !== "boolean") {
|
|
265
|
+
return `Parameter "${key}" must be a boolean, got ${typeof value}`;
|
|
266
|
+
}
|
|
267
|
+
break;
|
|
268
|
+
case "array":
|
|
269
|
+
if (!Array.isArray(value)) {
|
|
270
|
+
return `Parameter "${key}" must be an array, got ${typeof value}`;
|
|
271
|
+
}
|
|
272
|
+
if (schema.items) {
|
|
273
|
+
for (let i = 0; i < value.length; i++) {
|
|
274
|
+
const itemError = validateParameterType(`${key}[${i}]`, value[i], schema.items);
|
|
275
|
+
if (itemError) {
|
|
276
|
+
return itemError;
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
break;
|
|
281
|
+
case "object":
|
|
282
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
283
|
+
return `Parameter "${key}" must be an object, got ${typeof value}`;
|
|
284
|
+
}
|
|
285
|
+
break;
|
|
286
|
+
}
|
|
287
|
+
if (schema.enum && schema.enum.length > 0) {
|
|
288
|
+
const enumValues = schema.enum;
|
|
289
|
+
let isValidEnum = false;
|
|
290
|
+
for (const enumValue of enumValues) {
|
|
291
|
+
if (value === enumValue) {
|
|
292
|
+
isValidEnum = true;
|
|
293
|
+
break;
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
if (!isValidEnum) {
|
|
297
|
+
return `Parameter "${key}" must be one of: ${enumValues.join(", ")}, got ${value}`;
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
return void 0;
|
|
301
|
+
}
|
|
302
|
+
function getValidationErrors(parameters, schemaRequired, schemaProperties, additionalProperties) {
|
|
303
|
+
const errors = [];
|
|
304
|
+
for (const field of schemaRequired) {
|
|
305
|
+
if (!(field in parameters)) {
|
|
306
|
+
errors.push(`Missing required parameter: ${field}`);
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
for (const [key, value] of Object.entries(parameters)) {
|
|
310
|
+
const paramSchema = schemaProperties[key];
|
|
311
|
+
if (!paramSchema) {
|
|
312
|
+
if (additionalProperties === true) {
|
|
313
|
+
continue;
|
|
314
|
+
}
|
|
315
|
+
if (additionalProperties && typeof additionalProperties === "object") {
|
|
316
|
+
const additionalTypeError = validateParameterType(key, value, additionalProperties);
|
|
317
|
+
if (additionalTypeError) errors.push(additionalTypeError);
|
|
318
|
+
continue;
|
|
319
|
+
}
|
|
320
|
+
errors.push(`Unknown parameter: ${key}`);
|
|
321
|
+
continue;
|
|
322
|
+
}
|
|
323
|
+
const typeError = validateParameterType(key, value, paramSchema);
|
|
324
|
+
if (typeError) {
|
|
325
|
+
errors.push(typeError);
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
return errors;
|
|
329
|
+
}
|
|
330
|
+
function validateToolParameters(parameters, schemaRequired, schemaProperties, additionalProperties) {
|
|
331
|
+
const errors = getValidationErrors(
|
|
332
|
+
parameters,
|
|
333
|
+
schemaRequired,
|
|
334
|
+
schemaProperties,
|
|
335
|
+
additionalProperties
|
|
336
|
+
);
|
|
337
|
+
return {
|
|
338
|
+
isValid: errors.length === 0,
|
|
339
|
+
errors
|
|
340
|
+
};
|
|
341
|
+
}
|
|
342
|
+
|
|
247
343
|
// src/implementations/function-tool.ts
|
|
248
344
|
var FunctionTool = class {
|
|
249
345
|
schema;
|
|
@@ -274,7 +370,12 @@ var FunctionTool = class {
|
|
|
274
370
|
async execute(parameters, context) {
|
|
275
371
|
const toolName = this.schema.name;
|
|
276
372
|
if (!this.validate(parameters)) {
|
|
277
|
-
const errors =
|
|
373
|
+
const errors = getValidationErrors(
|
|
374
|
+
parameters,
|
|
375
|
+
this.schema.parameters.required || [],
|
|
376
|
+
this.schema.parameters.properties || {},
|
|
377
|
+
this.schema.parameters.additionalProperties
|
|
378
|
+
);
|
|
278
379
|
throw new ValidationError2(`Invalid parameters for tool "${toolName}": ${errors.join(", ")}`);
|
|
279
380
|
}
|
|
280
381
|
const startTime = Date.now();
|
|
@@ -310,17 +411,23 @@ var FunctionTool = class {
|
|
|
310
411
|
* Validate parameters (simple boolean result)
|
|
311
412
|
*/
|
|
312
413
|
validate(parameters) {
|
|
313
|
-
return
|
|
414
|
+
return getValidationErrors(
|
|
415
|
+
parameters,
|
|
416
|
+
this.schema.parameters.required || [],
|
|
417
|
+
this.schema.parameters.properties || {},
|
|
418
|
+
this.schema.parameters.additionalProperties
|
|
419
|
+
).length === 0;
|
|
314
420
|
}
|
|
315
421
|
/**
|
|
316
422
|
* Validate tool parameters with detailed result
|
|
317
423
|
*/
|
|
318
424
|
validateParameters(parameters) {
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
425
|
+
return validateToolParameters(
|
|
426
|
+
parameters,
|
|
427
|
+
this.schema.parameters.required || [],
|
|
428
|
+
this.schema.parameters.properties || {},
|
|
429
|
+
this.schema.parameters.additionalProperties
|
|
430
|
+
);
|
|
324
431
|
}
|
|
325
432
|
/**
|
|
326
433
|
* Get tool description
|
|
@@ -328,86 +435,6 @@ var FunctionTool = class {
|
|
|
328
435
|
getDescription() {
|
|
329
436
|
return this.schema.description;
|
|
330
437
|
}
|
|
331
|
-
/**
|
|
332
|
-
* Get detailed validation errors
|
|
333
|
-
*/
|
|
334
|
-
getValidationErrors(parameters) {
|
|
335
|
-
const errors = [];
|
|
336
|
-
const required = this.schema.parameters.required || [];
|
|
337
|
-
const properties = this.schema.parameters.properties || {};
|
|
338
|
-
for (const field of required) {
|
|
339
|
-
if (!(field in parameters)) {
|
|
340
|
-
errors.push(`Missing required parameter: ${field}`);
|
|
341
|
-
}
|
|
342
|
-
}
|
|
343
|
-
for (const [key, value] of Object.entries(parameters)) {
|
|
344
|
-
const paramSchema = properties[key];
|
|
345
|
-
if (!paramSchema) {
|
|
346
|
-
errors.push(`Unknown parameter: ${key}`);
|
|
347
|
-
continue;
|
|
348
|
-
}
|
|
349
|
-
const typeError = this.validateParameterType(key, value, paramSchema);
|
|
350
|
-
if (typeError) {
|
|
351
|
-
errors.push(typeError);
|
|
352
|
-
}
|
|
353
|
-
}
|
|
354
|
-
return errors;
|
|
355
|
-
}
|
|
356
|
-
/**
|
|
357
|
-
* Validate individual parameter type
|
|
358
|
-
*/
|
|
359
|
-
validateParameterType(key, value, schema) {
|
|
360
|
-
const expectedType = schema["type"];
|
|
361
|
-
switch (expectedType) {
|
|
362
|
-
case "string":
|
|
363
|
-
if (typeof value !== "string") {
|
|
364
|
-
return `Parameter "${key}" must be a string, got ${typeof value}`;
|
|
365
|
-
}
|
|
366
|
-
break;
|
|
367
|
-
case "number":
|
|
368
|
-
if (typeof value !== "number" || isNaN(value)) {
|
|
369
|
-
return `Parameter "${key}" must be a number, got ${typeof value}`;
|
|
370
|
-
}
|
|
371
|
-
break;
|
|
372
|
-
case "boolean":
|
|
373
|
-
if (typeof value !== "boolean") {
|
|
374
|
-
return `Parameter "${key}" must be a boolean, got ${typeof value}`;
|
|
375
|
-
}
|
|
376
|
-
break;
|
|
377
|
-
case "array":
|
|
378
|
-
if (!Array.isArray(value)) {
|
|
379
|
-
return `Parameter "${key}" must be an array, got ${typeof value}`;
|
|
380
|
-
}
|
|
381
|
-
if (schema.items) {
|
|
382
|
-
for (let i = 0; i < value.length; i++) {
|
|
383
|
-
const itemError = this.validateParameterType(`${key}[${i}]`, value[i], schema.items);
|
|
384
|
-
if (itemError) {
|
|
385
|
-
return itemError;
|
|
386
|
-
}
|
|
387
|
-
}
|
|
388
|
-
}
|
|
389
|
-
break;
|
|
390
|
-
case "object":
|
|
391
|
-
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
392
|
-
return `Parameter "${key}" must be an object, got ${typeof value}`;
|
|
393
|
-
}
|
|
394
|
-
break;
|
|
395
|
-
}
|
|
396
|
-
if (schema.enum && schema.enum.length > 0) {
|
|
397
|
-
const enumValues = schema.enum;
|
|
398
|
-
let isValidEnum = false;
|
|
399
|
-
for (const enumValue of enumValues) {
|
|
400
|
-
if (value === enumValue) {
|
|
401
|
-
isValidEnum = true;
|
|
402
|
-
break;
|
|
403
|
-
}
|
|
404
|
-
}
|
|
405
|
-
if (!isValidEnum) {
|
|
406
|
-
return `Parameter "${key}" must be one of: ${enumValues.join(", ")}, got ${value}`;
|
|
407
|
-
}
|
|
408
|
-
}
|
|
409
|
-
return void 0;
|
|
410
|
-
}
|
|
411
438
|
/**
|
|
412
439
|
* Validate constructor inputs
|
|
413
440
|
*/
|
|
@@ -451,6 +478,132 @@ function createZodFunctionTool(name, description, zodSchema, fn) {
|
|
|
451
478
|
|
|
452
479
|
// src/implementations/openapi-tool.ts
|
|
453
480
|
import { ToolExecutionError as ToolExecutionError2, ValidationError as ValidationError3 } from "@robota-sdk/agent-core";
|
|
481
|
+
|
|
482
|
+
// src/implementations/openapi-schema-converter.ts
|
|
483
|
+
var HTTP_METHODS = [
|
|
484
|
+
"get",
|
|
485
|
+
"post",
|
|
486
|
+
"put",
|
|
487
|
+
"delete",
|
|
488
|
+
"patch",
|
|
489
|
+
"head",
|
|
490
|
+
"options"
|
|
491
|
+
];
|
|
492
|
+
function findOperation(apiSpec, operationId) {
|
|
493
|
+
for (const [path, pathItem] of Object.entries(apiSpec.paths || {})) {
|
|
494
|
+
if (!pathItem) continue;
|
|
495
|
+
for (const method of HTTP_METHODS) {
|
|
496
|
+
const operation = pathItem[method];
|
|
497
|
+
if (operation?.operationId === operationId) {
|
|
498
|
+
return { method, path, operation };
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
return void 0;
|
|
503
|
+
}
|
|
504
|
+
function mapOpenAPIType(type) {
|
|
505
|
+
switch (type) {
|
|
506
|
+
case "string":
|
|
507
|
+
return "string";
|
|
508
|
+
case "number":
|
|
509
|
+
return "number";
|
|
510
|
+
case "integer":
|
|
511
|
+
return "integer";
|
|
512
|
+
case "boolean":
|
|
513
|
+
return "boolean";
|
|
514
|
+
case "array":
|
|
515
|
+
return "array";
|
|
516
|
+
case "object":
|
|
517
|
+
return "object";
|
|
518
|
+
default:
|
|
519
|
+
return "string";
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
function convertOpenAPISchemaToParameterSchema(schema) {
|
|
523
|
+
if ("$ref" in schema) {
|
|
524
|
+
return { type: "object" };
|
|
525
|
+
}
|
|
526
|
+
const result = {
|
|
527
|
+
type: mapOpenAPIType(schema.type)
|
|
528
|
+
};
|
|
529
|
+
if (schema.description) {
|
|
530
|
+
result.description = schema.description;
|
|
531
|
+
}
|
|
532
|
+
if (schema.enum) {
|
|
533
|
+
result.enum = schema.enum;
|
|
534
|
+
}
|
|
535
|
+
if (schema.minimum !== void 0) {
|
|
536
|
+
result.minimum = schema.minimum;
|
|
537
|
+
}
|
|
538
|
+
if (schema.maximum !== void 0) {
|
|
539
|
+
result.maximum = schema.maximum;
|
|
540
|
+
}
|
|
541
|
+
if (schema.pattern) {
|
|
542
|
+
result.pattern = schema.pattern;
|
|
543
|
+
}
|
|
544
|
+
if (schema.format) {
|
|
545
|
+
result.format = schema.format;
|
|
546
|
+
}
|
|
547
|
+
if (schema.default !== void 0) {
|
|
548
|
+
result.default = schema.default;
|
|
549
|
+
}
|
|
550
|
+
if (schema.type === "array" && schema.items) {
|
|
551
|
+
result.items = convertOpenAPISchemaToParameterSchema(schema.items);
|
|
552
|
+
}
|
|
553
|
+
if (schema.type === "object" && schema.properties) {
|
|
554
|
+
result.properties = {};
|
|
555
|
+
for (const [propName, propSchema] of Object.entries(schema.properties)) {
|
|
556
|
+
result.properties[propName] = convertOpenAPISchemaToParameterSchema(propSchema);
|
|
557
|
+
}
|
|
558
|
+
if (schema.required && schema.required.length > 0) {
|
|
559
|
+
result.required = schema.required;
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
return result;
|
|
563
|
+
}
|
|
564
|
+
function convertOpenAPIParamToSchema(param) {
|
|
565
|
+
const schema = param.schema;
|
|
566
|
+
return convertOpenAPISchemaToParameterSchema(schema);
|
|
567
|
+
}
|
|
568
|
+
function createSchemaFromOperation(operationId, opSpec) {
|
|
569
|
+
const properties = {};
|
|
570
|
+
const required = [];
|
|
571
|
+
const params = opSpec.parameters || [];
|
|
572
|
+
for (const param of params) {
|
|
573
|
+
properties[param.name] = convertOpenAPIParamToSchema(param);
|
|
574
|
+
if (param.required) {
|
|
575
|
+
required.push(param.name);
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
if (opSpec.requestBody) {
|
|
579
|
+
const requestBody = opSpec.requestBody;
|
|
580
|
+
const jsonContent = requestBody.content?.["application/json"];
|
|
581
|
+
if (jsonContent?.schema) {
|
|
582
|
+
const bodySchema = convertOpenAPISchemaToParameterSchema(jsonContent.schema);
|
|
583
|
+
if (bodySchema.type === "object" && bodySchema.properties) {
|
|
584
|
+
Object.assign(properties, bodySchema.properties);
|
|
585
|
+
const schemaWithRequired = bodySchema;
|
|
586
|
+
if (schemaWithRequired.required) {
|
|
587
|
+
required.push(...schemaWithRequired.required);
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
const schemaParams = {
|
|
593
|
+
type: "object",
|
|
594
|
+
properties
|
|
595
|
+
};
|
|
596
|
+
if (required.length > 0) {
|
|
597
|
+
schemaParams.required = required;
|
|
598
|
+
}
|
|
599
|
+
return {
|
|
600
|
+
name: operationId,
|
|
601
|
+
description: opSpec.summary || opSpec.description || `OpenAPI operation: ${operationId}`,
|
|
602
|
+
parameters: schemaParams
|
|
603
|
+
};
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
// src/implementations/openapi-tool.ts
|
|
454
607
|
var OpenAPITool = class {
|
|
455
608
|
schema;
|
|
456
609
|
apiSpec;
|
|
@@ -557,36 +710,13 @@ var OpenAPITool = class {
|
|
|
557
710
|
* @private
|
|
558
711
|
*/
|
|
559
712
|
async executeAPICall(parameters, _context) {
|
|
560
|
-
const operation = this.
|
|
713
|
+
const operation = findOperation(this.apiSpec, this.operationId);
|
|
561
714
|
if (!operation) {
|
|
562
715
|
throw new Error(`Operation ${this.operationId} not found in OpenAPI spec`);
|
|
563
716
|
}
|
|
564
|
-
|
|
717
|
+
this.buildRequestConfig(operation, parameters);
|
|
565
718
|
throw new Error("Not implemented: actual API execution is not yet available");
|
|
566
719
|
}
|
|
567
|
-
/**
|
|
568
|
-
* Find the operation in the OpenAPI specification
|
|
569
|
-
*/
|
|
570
|
-
findOperation() {
|
|
571
|
-
for (const [path, pathItem] of Object.entries(this.apiSpec.paths || {})) {
|
|
572
|
-
if (!pathItem) continue;
|
|
573
|
-
for (const method of [
|
|
574
|
-
"get",
|
|
575
|
-
"post",
|
|
576
|
-
"put",
|
|
577
|
-
"delete",
|
|
578
|
-
"patch",
|
|
579
|
-
"head",
|
|
580
|
-
"options"
|
|
581
|
-
]) {
|
|
582
|
-
const operation = pathItem[method];
|
|
583
|
-
if (operation?.operationId === this.operationId) {
|
|
584
|
-
return { method, path, operation };
|
|
585
|
-
}
|
|
586
|
-
}
|
|
587
|
-
}
|
|
588
|
-
return void 0;
|
|
589
|
-
}
|
|
590
720
|
/**
|
|
591
721
|
* Build HTTP request configuration from OpenAPI operation and parameters
|
|
592
722
|
*/
|
|
@@ -658,121 +788,13 @@ var OpenAPITool = class {
|
|
|
658
788
|
* Create tool schema from OpenAPI operation specification
|
|
659
789
|
*/
|
|
660
790
|
createSchemaFromOpenAPI() {
|
|
661
|
-
const operation = this.
|
|
791
|
+
const operation = findOperation(this.apiSpec, this.operationId);
|
|
662
792
|
if (!operation) {
|
|
663
793
|
throw new Error(
|
|
664
794
|
`[STRICT-POLICY][EMITTER-CONTRACT] OpenAPI operation not found: ${this.operationId}. Emitter contract must provide a valid operationId present in the OpenAPI document.`
|
|
665
795
|
);
|
|
666
796
|
}
|
|
667
|
-
|
|
668
|
-
const properties = {};
|
|
669
|
-
const required = [];
|
|
670
|
-
const params = opSpec.parameters || [];
|
|
671
|
-
for (const param of params) {
|
|
672
|
-
properties[param.name] = this.convertOpenAPIParamToSchema(param);
|
|
673
|
-
if (param.required) {
|
|
674
|
-
required.push(param.name);
|
|
675
|
-
}
|
|
676
|
-
}
|
|
677
|
-
if (opSpec.requestBody) {
|
|
678
|
-
const requestBody = opSpec.requestBody;
|
|
679
|
-
const jsonContent = requestBody.content?.["application/json"];
|
|
680
|
-
if (jsonContent?.schema) {
|
|
681
|
-
const bodySchema = this.convertOpenAPISchemaToParameterSchema(jsonContent.schema);
|
|
682
|
-
if (bodySchema.type === "object" && bodySchema.properties) {
|
|
683
|
-
Object.assign(properties, bodySchema.properties);
|
|
684
|
-
const schemaWithRequired = bodySchema;
|
|
685
|
-
if (schemaWithRequired.required) {
|
|
686
|
-
required.push(...schemaWithRequired.required);
|
|
687
|
-
}
|
|
688
|
-
}
|
|
689
|
-
}
|
|
690
|
-
}
|
|
691
|
-
const schemaParams = {
|
|
692
|
-
type: "object",
|
|
693
|
-
properties
|
|
694
|
-
};
|
|
695
|
-
if (required.length > 0) {
|
|
696
|
-
schemaParams.required = required;
|
|
697
|
-
}
|
|
698
|
-
return {
|
|
699
|
-
name: this.operationId,
|
|
700
|
-
description: opSpec.summary || opSpec.description || `OpenAPI operation: ${this.operationId}`,
|
|
701
|
-
parameters: schemaParams
|
|
702
|
-
};
|
|
703
|
-
}
|
|
704
|
-
/**
|
|
705
|
-
* Convert OpenAPI parameter to tool parameter schema
|
|
706
|
-
*/
|
|
707
|
-
convertOpenAPIParamToSchema(param) {
|
|
708
|
-
const schema = param.schema;
|
|
709
|
-
return this.convertOpenAPISchemaToParameterSchema(schema);
|
|
710
|
-
}
|
|
711
|
-
/**
|
|
712
|
-
* Convert OpenAPI schema to parameter schema
|
|
713
|
-
*/
|
|
714
|
-
convertOpenAPISchemaToParameterSchema(schema) {
|
|
715
|
-
if ("$ref" in schema) {
|
|
716
|
-
return { type: "object" };
|
|
717
|
-
}
|
|
718
|
-
const result = {
|
|
719
|
-
type: this.mapOpenAPIType(schema.type)
|
|
720
|
-
};
|
|
721
|
-
if (schema.description) {
|
|
722
|
-
result.description = schema.description;
|
|
723
|
-
}
|
|
724
|
-
if (schema.enum) {
|
|
725
|
-
result.enum = schema.enum;
|
|
726
|
-
}
|
|
727
|
-
if (schema.minimum !== void 0) {
|
|
728
|
-
result.minimum = schema.minimum;
|
|
729
|
-
}
|
|
730
|
-
if (schema.maximum !== void 0) {
|
|
731
|
-
result.maximum = schema.maximum;
|
|
732
|
-
}
|
|
733
|
-
if (schema.pattern) {
|
|
734
|
-
result.pattern = schema.pattern;
|
|
735
|
-
}
|
|
736
|
-
if (schema.format) {
|
|
737
|
-
result.format = schema.format;
|
|
738
|
-
}
|
|
739
|
-
if (schema.default !== void 0) {
|
|
740
|
-
result.default = schema.default;
|
|
741
|
-
}
|
|
742
|
-
if (schema.type === "array" && schema.items) {
|
|
743
|
-
result.items = this.convertOpenAPISchemaToParameterSchema(schema.items);
|
|
744
|
-
}
|
|
745
|
-
if (schema.type === "object" && schema.properties) {
|
|
746
|
-
result.properties = {};
|
|
747
|
-
for (const [propName, propSchema] of Object.entries(schema.properties)) {
|
|
748
|
-
result.properties[propName] = this.convertOpenAPISchemaToParameterSchema(propSchema);
|
|
749
|
-
}
|
|
750
|
-
if (schema.required && schema.required.length > 0) {
|
|
751
|
-
result.required = schema.required;
|
|
752
|
-
}
|
|
753
|
-
}
|
|
754
|
-
return result;
|
|
755
|
-
}
|
|
756
|
-
/**
|
|
757
|
-
* Map OpenAPI type to JSON schema type
|
|
758
|
-
*/
|
|
759
|
-
mapOpenAPIType(type) {
|
|
760
|
-
switch (type) {
|
|
761
|
-
case "string":
|
|
762
|
-
return "string";
|
|
763
|
-
case "number":
|
|
764
|
-
return "number";
|
|
765
|
-
case "integer":
|
|
766
|
-
return "integer";
|
|
767
|
-
case "boolean":
|
|
768
|
-
return "boolean";
|
|
769
|
-
case "array":
|
|
770
|
-
return "array";
|
|
771
|
-
case "object":
|
|
772
|
-
return "object";
|
|
773
|
-
default:
|
|
774
|
-
return "string";
|
|
775
|
-
}
|
|
797
|
+
return createSchemaFromOperation(this.operationId, operation.operation);
|
|
776
798
|
}
|
|
777
799
|
};
|
|
778
800
|
function createOpenAPITool(config) {
|
|
@@ -809,6 +831,11 @@ async function runBash(args) {
|
|
|
809
831
|
const timer = setTimeout(() => {
|
|
810
832
|
timedOut = true;
|
|
811
833
|
child.kill("SIGTERM");
|
|
834
|
+
settle({
|
|
835
|
+
success: false,
|
|
836
|
+
output: Buffer.concat(stdoutChunks).toString("utf8"),
|
|
837
|
+
error: `Command timed out after ${timeout}ms`
|
|
838
|
+
});
|
|
812
839
|
}, timeout);
|
|
813
840
|
function settle(result) {
|
|
814
841
|
if (settled) return;
|
|
@@ -954,9 +981,51 @@ var readTool = createZodFunctionTool(
|
|
|
954
981
|
);
|
|
955
982
|
|
|
956
983
|
// src/builtins/write-tool.ts
|
|
957
|
-
import { writeFile, mkdir } from "fs/promises";
|
|
958
|
-
import { dirname } from "path";
|
|
959
984
|
import { z as z3 } from "zod";
|
|
985
|
+
|
|
986
|
+
// src/builtins/atomic-file-write.ts
|
|
987
|
+
import { randomBytes } from "crypto";
|
|
988
|
+
import { chmod, mkdir, rename, rm, stat as stat2, writeFile } from "fs/promises";
|
|
989
|
+
import { basename, dirname, join } from "path";
|
|
990
|
+
var TEMP_RANDOM_BYTES = 6;
|
|
991
|
+
var PRESERVED_MODE_BITS = 4095;
|
|
992
|
+
var MISSING_FILE_ERROR_CODE = "ENOENT";
|
|
993
|
+
function createTempFilePath(filePath) {
|
|
994
|
+
const dir = dirname(filePath);
|
|
995
|
+
const name = basename(filePath);
|
|
996
|
+
const suffix = randomBytes(TEMP_RANDOM_BYTES).toString("hex");
|
|
997
|
+
return join(dir, `.${name}.robota-tmp-${process.pid}-${Date.now()}-${suffix}`);
|
|
998
|
+
}
|
|
999
|
+
async function readExistingMode(filePath) {
|
|
1000
|
+
try {
|
|
1001
|
+
const fileStats = await stat2(filePath);
|
|
1002
|
+
return fileStats.mode & PRESERVED_MODE_BITS;
|
|
1003
|
+
} catch (error) {
|
|
1004
|
+
if (error instanceof Error && hasErrorCode(error, MISSING_FILE_ERROR_CODE)) return void 0;
|
|
1005
|
+
throw error;
|
|
1006
|
+
}
|
|
1007
|
+
}
|
|
1008
|
+
function hasErrorCode(error, code) {
|
|
1009
|
+
return "code" in error && error.code === code;
|
|
1010
|
+
}
|
|
1011
|
+
async function atomicWriteUtf8File(filePath, content) {
|
|
1012
|
+
const dir = dirname(filePath);
|
|
1013
|
+
await mkdir(dir, { recursive: true });
|
|
1014
|
+
const existingMode = await readExistingMode(filePath);
|
|
1015
|
+
const tempFilePath = createTempFilePath(filePath);
|
|
1016
|
+
try {
|
|
1017
|
+
await writeFile(tempFilePath, content, "utf8");
|
|
1018
|
+
if (existingMode !== void 0) {
|
|
1019
|
+
await chmod(tempFilePath, existingMode);
|
|
1020
|
+
}
|
|
1021
|
+
await rename(tempFilePath, filePath);
|
|
1022
|
+
} catch (error) {
|
|
1023
|
+
await rm(tempFilePath, { force: true }).catch(() => void 0);
|
|
1024
|
+
throw error;
|
|
1025
|
+
}
|
|
1026
|
+
}
|
|
1027
|
+
|
|
1028
|
+
// src/builtins/write-tool.ts
|
|
960
1029
|
var WriteSchema = z3.object({
|
|
961
1030
|
filePath: z3.string().describe("The absolute path to the file to write"),
|
|
962
1031
|
content: z3.string().describe("The content to write to the file")
|
|
@@ -964,8 +1033,7 @@ var WriteSchema = z3.object({
|
|
|
964
1033
|
async function writeFileTool(args) {
|
|
965
1034
|
const { filePath, content } = args;
|
|
966
1035
|
try {
|
|
967
|
-
await
|
|
968
|
-
await writeFile(filePath, content, "utf8");
|
|
1036
|
+
await atomicWriteUtf8File(filePath, content);
|
|
969
1037
|
const result = {
|
|
970
1038
|
success: true,
|
|
971
1039
|
output: `Written ${Buffer.byteLength(content, "utf8")} bytes to ${filePath}`
|
|
@@ -990,7 +1058,7 @@ var writeTool = createZodFunctionTool(
|
|
|
990
1058
|
);
|
|
991
1059
|
|
|
992
1060
|
// src/builtins/edit-tool.ts
|
|
993
|
-
import { readFile as readFile2
|
|
1061
|
+
import { readFile as readFile2 } from "fs/promises";
|
|
994
1062
|
import { z as z4 } from "zod";
|
|
995
1063
|
var EditSchema = z4.object({
|
|
996
1064
|
filePath: z4.string().describe("The absolute path to the file to modify"),
|
|
@@ -1036,7 +1104,7 @@ async function editFileTool(args) {
|
|
|
1036
1104
|
}
|
|
1037
1105
|
const updated = replaceAll ? content.split(oldString).join(newString) : content.slice(0, content.indexOf(oldString)) + newString + content.slice(content.indexOf(oldString) + oldString.length);
|
|
1038
1106
|
try {
|
|
1039
|
-
await
|
|
1107
|
+
await atomicWriteUtf8File(filePath, updated);
|
|
1040
1108
|
} catch (err) {
|
|
1041
1109
|
const result2 = {
|
|
1042
1110
|
success: false,
|
|
@@ -1046,9 +1114,12 @@ async function editFileTool(args) {
|
|
|
1046
1114
|
return JSON.stringify(result2);
|
|
1047
1115
|
}
|
|
1048
1116
|
const count = replaceAll ? content.split(oldString).length - 1 : 1;
|
|
1117
|
+
const matchIdx = content.indexOf(oldString);
|
|
1118
|
+
const startLine = matchIdx >= 0 ? content.substring(0, matchIdx).split("\n").length : 1;
|
|
1049
1119
|
const result = {
|
|
1050
1120
|
success: true,
|
|
1051
|
-
output: `Replaced ${count} occurrence(s) in ${filePath}
|
|
1121
|
+
output: `Replaced ${count} occurrence(s) in ${filePath}`,
|
|
1122
|
+
startLine
|
|
1052
1123
|
};
|
|
1053
1124
|
return JSON.stringify(result);
|
|
1054
1125
|
}
|
|
@@ -1062,7 +1133,7 @@ var editTool = createZodFunctionTool(
|
|
|
1062
1133
|
);
|
|
1063
1134
|
|
|
1064
1135
|
// src/builtins/glob-tool.ts
|
|
1065
|
-
import { stat as
|
|
1136
|
+
import { stat as stat3 } from "fs/promises";
|
|
1066
1137
|
import { resolve } from "path";
|
|
1067
1138
|
import fg from "fast-glob";
|
|
1068
1139
|
import { z as z5 } from "zod";
|
|
@@ -1099,7 +1170,7 @@ async function globFileTool(args) {
|
|
|
1099
1170
|
matches.map(async (p) => {
|
|
1100
1171
|
const absPath = resolve(cwd, p);
|
|
1101
1172
|
try {
|
|
1102
|
-
const s = await
|
|
1173
|
+
const s = await stat3(absPath);
|
|
1103
1174
|
return { path: p, mtime: s.mtimeMs };
|
|
1104
1175
|
} catch {
|
|
1105
1176
|
return { path: p, mtime: 0 };
|
|
@@ -1134,8 +1205,8 @@ var globTool = createZodFunctionTool(
|
|
|
1134
1205
|
);
|
|
1135
1206
|
|
|
1136
1207
|
// src/builtins/grep-tool.ts
|
|
1137
|
-
import { readFile as readFile3, readdir, stat as
|
|
1138
|
-
import { join, resolve as resolve2 } from "path";
|
|
1208
|
+
import { readFile as readFile3, readdir, stat as stat4 } from "fs/promises";
|
|
1209
|
+
import { join as join2, resolve as resolve2 } from "path";
|
|
1139
1210
|
import { z as z6 } from "zod";
|
|
1140
1211
|
var GrepSchema = z6.object({
|
|
1141
1212
|
pattern: z6.string().describe("The regular expression pattern to search for in file contents"),
|
|
@@ -1169,10 +1240,10 @@ async function collectFiles(dirPath, glob) {
|
|
|
1169
1240
|
}
|
|
1170
1241
|
for (const name of entryNames) {
|
|
1171
1242
|
if (name === "node_modules" || name === ".git") continue;
|
|
1172
|
-
const fullPath =
|
|
1243
|
+
const fullPath = join2(current, name);
|
|
1173
1244
|
let fileStat;
|
|
1174
1245
|
try {
|
|
1175
|
-
fileStat = await
|
|
1246
|
+
fileStat = await stat4(fullPath);
|
|
1176
1247
|
} catch {
|
|
1177
1248
|
continue;
|
|
1178
1249
|
}
|
|
@@ -1242,7 +1313,7 @@ async function grepFileTool(args) {
|
|
|
1242
1313
|
}
|
|
1243
1314
|
let targetStat;
|
|
1244
1315
|
try {
|
|
1245
|
-
targetStat = await
|
|
1316
|
+
targetStat = await stat4(targetPath);
|
|
1246
1317
|
} catch {
|
|
1247
1318
|
const result2 = {
|
|
1248
1319
|
success: false,
|