@crewhaus/tool-validate 0.1.3 → 0.1.5
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/dist/index.d.ts +19 -0
- package/dist/index.js +22 -0
- package/package.json +10 -7
- package/src/index.test.ts +0 -99
- package/src/index.ts +0 -37
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { CrewhausError } from "@crewhaus/errors";
|
|
2
|
+
import type { RegisteredTool } from "@crewhaus/tool-catalog";
|
|
3
|
+
export type ZodIssue = {
|
|
4
|
+
readonly path: ReadonlyArray<string | number>;
|
|
5
|
+
readonly message: string;
|
|
6
|
+
};
|
|
7
|
+
export declare class ToolValidationError extends CrewhausError {
|
|
8
|
+
readonly name = "ToolValidationError";
|
|
9
|
+
readonly issues: ReadonlyArray<ZodIssue>;
|
|
10
|
+
constructor(message: string, issues: ReadonlyArray<ZodIssue>, cause?: unknown);
|
|
11
|
+
}
|
|
12
|
+
export type ValidationResult = {
|
|
13
|
+
readonly ok: true;
|
|
14
|
+
readonly value: unknown;
|
|
15
|
+
} | {
|
|
16
|
+
readonly ok: false;
|
|
17
|
+
readonly error: ToolValidationError;
|
|
18
|
+
};
|
|
19
|
+
export declare function validateToolInput(tool: RegisteredTool, rawInput: unknown): ValidationResult;
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { CrewhausError } from "@crewhaus/errors";
|
|
2
|
+
export class ToolValidationError extends CrewhausError {
|
|
3
|
+
name = "ToolValidationError";
|
|
4
|
+
issues;
|
|
5
|
+
constructor(message, issues, cause) {
|
|
6
|
+
super("tool", message, cause);
|
|
7
|
+
this.issues = issues;
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
export function validateToolInput(tool, rawInput) {
|
|
11
|
+
const result = tool.inputSchema.safeParse(rawInput);
|
|
12
|
+
if (result.success)
|
|
13
|
+
return { ok: true, value: result.data };
|
|
14
|
+
const issues = result.error.issues.map((i) => ({
|
|
15
|
+
path: i.path,
|
|
16
|
+
message: i.message,
|
|
17
|
+
}));
|
|
18
|
+
return {
|
|
19
|
+
ok: false,
|
|
20
|
+
error: new ToolValidationError(`invalid input for tool "${tool.name}": ${issues.map((i) => i.message).join("; ")}`, issues, result.error),
|
|
21
|
+
};
|
|
22
|
+
}
|
package/package.json
CHANGED
|
@@ -1,19 +1,22 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@crewhaus/tool-validate",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.5",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Pre-permission input validation per tool using the tool's Zod schema",
|
|
6
|
-
"main": "
|
|
7
|
-
"types": "
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
8
|
"exports": {
|
|
9
|
-
".":
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.js"
|
|
12
|
+
}
|
|
10
13
|
},
|
|
11
14
|
"scripts": {
|
|
12
15
|
"test": "bun test src"
|
|
13
16
|
},
|
|
14
17
|
"dependencies": {
|
|
15
|
-
"@crewhaus/errors": "0.1.
|
|
16
|
-
"@crewhaus/tool-catalog": "0.1.
|
|
18
|
+
"@crewhaus/errors": "0.1.5",
|
|
19
|
+
"@crewhaus/tool-catalog": "0.1.5",
|
|
17
20
|
"zod": "^3.23.8"
|
|
18
21
|
},
|
|
19
22
|
"license": "Apache-2.0",
|
|
@@ -34,5 +37,5 @@
|
|
|
34
37
|
"publishConfig": {
|
|
35
38
|
"access": "public"
|
|
36
39
|
},
|
|
37
|
-
"files": ["
|
|
40
|
+
"files": ["dist", "README.md", "LICENSE", "NOTICE"]
|
|
38
41
|
}
|
package/src/index.test.ts
DELETED
|
@@ -1,99 +0,0 @@
|
|
|
1
|
-
import { describe, expect, test } from "bun:test";
|
|
2
|
-
import { CrewhausError } from "@crewhaus/errors";
|
|
3
|
-
import type { RegisteredTool } from "@crewhaus/tool-catalog";
|
|
4
|
-
import { z } from "zod";
|
|
5
|
-
import { ToolValidationError, validateToolInput } from "./index";
|
|
6
|
-
|
|
7
|
-
const schema = z.object({ command: z.string(), args: z.array(z.string()).optional() });
|
|
8
|
-
|
|
9
|
-
const tool: RegisteredTool = {
|
|
10
|
-
name: "Bash",
|
|
11
|
-
description: "Run shell commands",
|
|
12
|
-
inputSchema: schema as RegisteredTool["inputSchema"],
|
|
13
|
-
execute: async () => "",
|
|
14
|
-
concurrencySafe: false,
|
|
15
|
-
readOnly: false,
|
|
16
|
-
destructive: false,
|
|
17
|
-
requiresSandbox: false,
|
|
18
|
-
classifyOutput: true,
|
|
19
|
-
scope: "internal",
|
|
20
|
-
requireJustification: false,
|
|
21
|
-
};
|
|
22
|
-
|
|
23
|
-
describe("validateToolInput — success", () => {
|
|
24
|
-
test("returns ok:true with parsed value for valid input", () => {
|
|
25
|
-
const result = validateToolInput(tool, { command: "ls" });
|
|
26
|
-
expect(result.ok).toBe(true);
|
|
27
|
-
if (result.ok) {
|
|
28
|
-
expect((result.value as { command: string }).command).toBe("ls");
|
|
29
|
-
}
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
test("optional fields are present when provided", () => {
|
|
33
|
-
const result = validateToolInput(tool, { command: "git", args: ["status"] });
|
|
34
|
-
expect(result.ok).toBe(true);
|
|
35
|
-
if (result.ok) {
|
|
36
|
-
expect((result.value as { args: string[] }).args).toEqual(["status"]);
|
|
37
|
-
}
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
test("optional fields absent when omitted", () => {
|
|
41
|
-
const result = validateToolInput(tool, { command: "pwd" });
|
|
42
|
-
expect(result.ok).toBe(true);
|
|
43
|
-
if (result.ok) {
|
|
44
|
-
expect((result.value as { args?: unknown }).args).toBeUndefined();
|
|
45
|
-
}
|
|
46
|
-
});
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
describe("validateToolInput — failure", () => {
|
|
50
|
-
test("returns ok:false with ToolValidationError for wrong type", () => {
|
|
51
|
-
const result = validateToolInput(tool, { command: 42 });
|
|
52
|
-
expect(result.ok).toBe(false);
|
|
53
|
-
if (!result.ok) {
|
|
54
|
-
expect(result.error).toBeInstanceOf(ToolValidationError);
|
|
55
|
-
}
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
test("error message includes tool name", () => {
|
|
59
|
-
const result = validateToolInput(tool, null);
|
|
60
|
-
expect(result.ok).toBe(false);
|
|
61
|
-
if (!result.ok) {
|
|
62
|
-
expect(result.error.message).toContain("Bash");
|
|
63
|
-
}
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
test("error.issues contains path and message", () => {
|
|
67
|
-
const result = validateToolInput(tool, { command: 123 });
|
|
68
|
-
expect(result.ok).toBe(false);
|
|
69
|
-
if (!result.ok) {
|
|
70
|
-
expect(result.error.issues.length).toBeGreaterThan(0);
|
|
71
|
-
expect(result.error.issues[0]).toHaveProperty("path");
|
|
72
|
-
expect(result.error.issues[0]).toHaveProperty("message");
|
|
73
|
-
}
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
test("ToolValidationError is instanceof CrewhausError", () => {
|
|
77
|
-
const result = validateToolInput(tool, {});
|
|
78
|
-
expect(result.ok).toBe(false);
|
|
79
|
-
if (!result.ok) {
|
|
80
|
-
expect(result.error).toBeInstanceOf(CrewhausError);
|
|
81
|
-
}
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
test("ToolValidationError has code 'tool'", () => {
|
|
85
|
-
const result = validateToolInput(tool, {});
|
|
86
|
-
expect(result.ok).toBe(false);
|
|
87
|
-
if (!result.ok) {
|
|
88
|
-
expect(result.error.code).toBe("tool");
|
|
89
|
-
}
|
|
90
|
-
});
|
|
91
|
-
|
|
92
|
-
test("ToolValidationError name is stable", () => {
|
|
93
|
-
const result = validateToolInput(tool, {});
|
|
94
|
-
expect(result.ok).toBe(false);
|
|
95
|
-
if (!result.ok) {
|
|
96
|
-
expect(result.error.name).toBe("ToolValidationError");
|
|
97
|
-
}
|
|
98
|
-
});
|
|
99
|
-
});
|
package/src/index.ts
DELETED
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
import { CrewhausError } from "@crewhaus/errors";
|
|
2
|
-
import type { RegisteredTool } from "@crewhaus/tool-catalog";
|
|
3
|
-
|
|
4
|
-
export type ZodIssue = { readonly path: ReadonlyArray<string | number>; readonly message: string };
|
|
5
|
-
|
|
6
|
-
export class ToolValidationError extends CrewhausError {
|
|
7
|
-
override readonly name = "ToolValidationError";
|
|
8
|
-
readonly issues: ReadonlyArray<ZodIssue>;
|
|
9
|
-
|
|
10
|
-
constructor(message: string, issues: ReadonlyArray<ZodIssue>, cause?: unknown) {
|
|
11
|
-
super("tool", message, cause);
|
|
12
|
-
this.issues = issues;
|
|
13
|
-
}
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export type ValidationResult =
|
|
17
|
-
| { readonly ok: true; readonly value: unknown }
|
|
18
|
-
| { readonly ok: false; readonly error: ToolValidationError };
|
|
19
|
-
|
|
20
|
-
export function validateToolInput(tool: RegisteredTool, rawInput: unknown): ValidationResult {
|
|
21
|
-
const result = tool.inputSchema.safeParse(rawInput);
|
|
22
|
-
if (result.success) return { ok: true, value: result.data };
|
|
23
|
-
|
|
24
|
-
const issues: ZodIssue[] = result.error.issues.map((i) => ({
|
|
25
|
-
path: i.path as ReadonlyArray<string | number>,
|
|
26
|
-
message: i.message,
|
|
27
|
-
}));
|
|
28
|
-
|
|
29
|
-
return {
|
|
30
|
-
ok: false,
|
|
31
|
-
error: new ToolValidationError(
|
|
32
|
-
`invalid input for tool "${tool.name}": ${issues.map((i) => i.message).join("; ")}`,
|
|
33
|
-
issues,
|
|
34
|
-
result.error,
|
|
35
|
-
),
|
|
36
|
-
};
|
|
37
|
-
}
|