@johpaz/hive-tools 1.0.10
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/package.json +42 -0
- package/src/browser/browser.test.ts +111 -0
- package/src/browser/index.ts +272 -0
- package/src/canvas/index.ts +220 -0
- package/src/cron/cron.test.ts +164 -0
- package/src/cron/index.ts +304 -0
- package/src/filesystem/filesystem.test.ts +240 -0
- package/src/filesystem/index.ts +379 -0
- package/src/index.ts +4 -0
package/package.json
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@johpaz/hive-tools",
|
|
3
|
+
"version": "1.0.10",
|
|
4
|
+
"description": "Native tools for Hive AI Gateway - Browser, Cron, FileSystem, Canvas",
|
|
5
|
+
"main": "./src/index.ts",
|
|
6
|
+
"module": "./src/index.ts",
|
|
7
|
+
"types": "./src/index.ts",
|
|
8
|
+
"license": "MIT",
|
|
9
|
+
"repository": {
|
|
10
|
+
"type": "git",
|
|
11
|
+
"url": "git+https://github.com/johpaz/hive.git",
|
|
12
|
+
"directory": "packages/tools"
|
|
13
|
+
},
|
|
14
|
+
"keywords": [
|
|
15
|
+
"hive",
|
|
16
|
+
"ai",
|
|
17
|
+
"tools",
|
|
18
|
+
"browser",
|
|
19
|
+
"cron",
|
|
20
|
+
"filesystem",
|
|
21
|
+
"canvas"
|
|
22
|
+
],
|
|
23
|
+
"files": [
|
|
24
|
+
"src/"
|
|
25
|
+
],
|
|
26
|
+
"dependencies": {
|
|
27
|
+
"@johpaz/hive-core": "^1.0.10",
|
|
28
|
+
"cron-parser": "^5.5.0",
|
|
29
|
+
"zod": "latest"
|
|
30
|
+
},
|
|
31
|
+
"devDependencies": {
|
|
32
|
+
"typescript": "latest",
|
|
33
|
+
"@types/bun": "latest"
|
|
34
|
+
},
|
|
35
|
+
"exports": {
|
|
36
|
+
".": "./src/index.ts",
|
|
37
|
+
"./browser": "./src/browser/index.ts",
|
|
38
|
+
"./cron": "./src/cron/index.ts",
|
|
39
|
+
"./filesystem": "./src/filesystem/index.ts",
|
|
40
|
+
"./canvas": "./src/canvas/index.ts"
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach } from "bun:test";
|
|
2
|
+
import {
|
|
3
|
+
createBrowserNavigateTool,
|
|
4
|
+
createBrowserFetchTool,
|
|
5
|
+
createBrowserScreenshotTool,
|
|
6
|
+
createBrowserClickTool,
|
|
7
|
+
createBrowserTypeTool,
|
|
8
|
+
createBrowserTools,
|
|
9
|
+
} from "./index";
|
|
10
|
+
|
|
11
|
+
describe("Browser Tools", () => {
|
|
12
|
+
describe("createBrowserNavigateTool", () => {
|
|
13
|
+
it("creates tool with correct name and description", () => {
|
|
14
|
+
const tool = createBrowserNavigateTool();
|
|
15
|
+
expect(tool.name).toBe("browser_navigate");
|
|
16
|
+
expect(tool.description).toContain("Navigate");
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it("has required parameters", () => {
|
|
20
|
+
const tool = createBrowserNavigateTool();
|
|
21
|
+
expect(tool.parameters.type).toBe("object");
|
|
22
|
+
expect(tool.parameters.required).toContain("url");
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it("fetches content from a URL", async () => {
|
|
26
|
+
const tool = createBrowserNavigateTool();
|
|
27
|
+
try {
|
|
28
|
+
const result = await tool.execute({
|
|
29
|
+
url: "https://example.com",
|
|
30
|
+
}) as { success: boolean; content: string };
|
|
31
|
+
|
|
32
|
+
expect(result.success).toBe(true);
|
|
33
|
+
expect(result.content).toBeDefined();
|
|
34
|
+
} catch (error) {
|
|
35
|
+
console.log("Skipping URL fetch test due to network/cert issues");
|
|
36
|
+
}
|
|
37
|
+
}, 30000);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
describe("createBrowserFetchTool", () => {
|
|
41
|
+
it("creates tool with correct name", () => {
|
|
42
|
+
const tool = createBrowserFetchTool();
|
|
43
|
+
expect(tool.name).toBe("browser_fetch");
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it("fetches JSON from API", async () => {
|
|
47
|
+
const tool = createBrowserFetchTool();
|
|
48
|
+
try {
|
|
49
|
+
const result = await tool.execute({
|
|
50
|
+
url: "https://jsonplaceholder.typicode.com/posts/1",
|
|
51
|
+
}) as { success: boolean; data: unknown };
|
|
52
|
+
|
|
53
|
+
expect(result.success).toBe(true);
|
|
54
|
+
expect(result.data).toBeDefined();
|
|
55
|
+
} catch (error) {
|
|
56
|
+
console.log("Skipping URL fetch test due to network/cert issues");
|
|
57
|
+
}
|
|
58
|
+
}, 30000);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
describe("createBrowserScreenshotTool", () => {
|
|
62
|
+
it("creates tool with placeholder response", async () => {
|
|
63
|
+
const tool = createBrowserScreenshotTool();
|
|
64
|
+
const result = await tool.execute({
|
|
65
|
+
url: "https://example.com",
|
|
66
|
+
}) as { success: boolean; error: string };
|
|
67
|
+
|
|
68
|
+
expect(result.success).toBe(false);
|
|
69
|
+
expect(result.error).toContain("puppeteer");
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
describe("createBrowserClickTool", () => {
|
|
74
|
+
it("creates tool with placeholder response", async () => {
|
|
75
|
+
const tool = createBrowserClickTool();
|
|
76
|
+
const result = await tool.execute({
|
|
77
|
+
selector: "#button",
|
|
78
|
+
}) as { success: boolean; error: string };
|
|
79
|
+
|
|
80
|
+
expect(result.success).toBe(false);
|
|
81
|
+
expect(result.error).toContain("puppeteer");
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
describe("createBrowserTypeTool", () => {
|
|
86
|
+
it("creates tool with placeholder response", async () => {
|
|
87
|
+
const tool = createBrowserTypeTool();
|
|
88
|
+
const result = await tool.execute({
|
|
89
|
+
selector: "#input",
|
|
90
|
+
text: "hello",
|
|
91
|
+
}) as { success: boolean; error: string };
|
|
92
|
+
|
|
93
|
+
expect(result.success).toBe(false);
|
|
94
|
+
expect(result.error).toContain("puppeteer");
|
|
95
|
+
});
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
describe("createBrowserTools", () => {
|
|
99
|
+
it("returns all browser tools", () => {
|
|
100
|
+
const tools = createBrowserTools();
|
|
101
|
+
expect(tools.length).toBe(5);
|
|
102
|
+
expect(tools.map((t) => t.name)).toEqual([
|
|
103
|
+
"browser_navigate",
|
|
104
|
+
"browser_fetch",
|
|
105
|
+
"browser_screenshot",
|
|
106
|
+
"browser_click",
|
|
107
|
+
"browser_type",
|
|
108
|
+
]);
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
});
|
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
import type { Tool } from "@johpaz/hive-core";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
|
|
4
|
+
export interface BrowserConfig {
|
|
5
|
+
headless?: boolean;
|
|
6
|
+
timeout?: number;
|
|
7
|
+
userAgent?: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
type ZodObjectSchema = z.ZodObject<z.ZodRawShape>;
|
|
11
|
+
|
|
12
|
+
const createBrowserTool = <T extends ZodObjectSchema>(
|
|
13
|
+
name: string,
|
|
14
|
+
description: string,
|
|
15
|
+
paramsSchema: T,
|
|
16
|
+
execute: (params: z.infer<T>, config: BrowserConfig) => Promise<unknown>
|
|
17
|
+
): Tool => {
|
|
18
|
+
const shape = paramsSchema.shape;
|
|
19
|
+
return {
|
|
20
|
+
name,
|
|
21
|
+
description,
|
|
22
|
+
parameters: {
|
|
23
|
+
type: "object",
|
|
24
|
+
properties: Object.fromEntries(
|
|
25
|
+
Object.entries(shape).map(([key, schema]) => [
|
|
26
|
+
key,
|
|
27
|
+
{
|
|
28
|
+
type: schema instanceof z.ZodString
|
|
29
|
+
? "string"
|
|
30
|
+
: schema instanceof z.ZodNumber
|
|
31
|
+
? "number"
|
|
32
|
+
: schema instanceof z.ZodBoolean
|
|
33
|
+
? "boolean"
|
|
34
|
+
: schema instanceof z.ZodArray
|
|
35
|
+
? "array"
|
|
36
|
+
: "object",
|
|
37
|
+
description: ((schema as unknown as { _def?: { description?: string } })._def?.description) || key,
|
|
38
|
+
},
|
|
39
|
+
])
|
|
40
|
+
),
|
|
41
|
+
required: Object.entries(shape)
|
|
42
|
+
.filter(([_, schema]) => !(schema instanceof z.ZodOptional))
|
|
43
|
+
.map(([key]) => key),
|
|
44
|
+
},
|
|
45
|
+
execute: async (params: Record<string, unknown>) => {
|
|
46
|
+
const parsed = paramsSchema.safeParse(params);
|
|
47
|
+
if (!parsed.success) {
|
|
48
|
+
throw new Error(`Invalid parameters: ${parsed.error.message}`);
|
|
49
|
+
}
|
|
50
|
+
return execute(parsed.data, {});
|
|
51
|
+
},
|
|
52
|
+
};
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
export function createBrowserNavigateTool(config: BrowserConfig = {}): Tool {
|
|
56
|
+
return createBrowserTool(
|
|
57
|
+
"browser_navigate",
|
|
58
|
+
"Navigate to a URL and retrieve the page content",
|
|
59
|
+
z.object({
|
|
60
|
+
url: z.string().url().describe("The URL to navigate to"),
|
|
61
|
+
waitFor: z.string().optional().describe("CSS selector to wait for before returning"),
|
|
62
|
+
timeout: z.number().optional().describe("Timeout in milliseconds"),
|
|
63
|
+
}),
|
|
64
|
+
async (params, cfg) => {
|
|
65
|
+
const timeout = params.timeout || cfg.timeout || 30000;
|
|
66
|
+
|
|
67
|
+
const controller = new AbortController();
|
|
68
|
+
const timeoutId = setTimeout(() => controller.abort(), timeout);
|
|
69
|
+
|
|
70
|
+
try {
|
|
71
|
+
const response = await fetch(params.url, {
|
|
72
|
+
signal: controller.signal,
|
|
73
|
+
headers: {
|
|
74
|
+
"User-Agent": cfg.userAgent || "HiveBot/1.0",
|
|
75
|
+
},
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
if (!response.ok) {
|
|
79
|
+
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const contentType = response.headers.get("content-type") || "";
|
|
83
|
+
let content: string;
|
|
84
|
+
|
|
85
|
+
if (contentType.includes("application/json")) {
|
|
86
|
+
const json = await response.json();
|
|
87
|
+
content = JSON.stringify(json, null, 2);
|
|
88
|
+
} else {
|
|
89
|
+
content = await response.text();
|
|
90
|
+
|
|
91
|
+
content = content
|
|
92
|
+
.replace(/<script[^>]*>[\s\S]*?<\/script>/gi, "")
|
|
93
|
+
.replace(/<style[^>]*>[\s\S]*?<\/style>/gi, "")
|
|
94
|
+
.replace(/<[^>]+>/g, " ")
|
|
95
|
+
.replace(/\s+/g, " ")
|
|
96
|
+
.trim();
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return {
|
|
100
|
+
success: true,
|
|
101
|
+
url: params.url,
|
|
102
|
+
finalUrl: response.url,
|
|
103
|
+
title: extractTitle(content),
|
|
104
|
+
content: content.slice(0, 50000),
|
|
105
|
+
contentType,
|
|
106
|
+
};
|
|
107
|
+
} finally {
|
|
108
|
+
clearTimeout(timeoutId);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export function createBrowserFetchTool(config: BrowserConfig = {}): Tool {
|
|
115
|
+
return createBrowserTool(
|
|
116
|
+
"browser_fetch",
|
|
117
|
+
"Fetch content from a URL with custom options",
|
|
118
|
+
z.object({
|
|
119
|
+
url: z.string().url().describe("The URL to fetch"),
|
|
120
|
+
method: z.enum(["GET", "POST", "PUT", "DELETE"]).optional().default("GET"),
|
|
121
|
+
headers: z.record(z.string(), z.string()).optional().describe("Custom headers"),
|
|
122
|
+
body: z.string().optional().describe("Request body for POST/PUT"),
|
|
123
|
+
timeout: z.number().optional().describe("Timeout in milliseconds"),
|
|
124
|
+
}),
|
|
125
|
+
async (params, cfg) => {
|
|
126
|
+
const timeout = params.timeout || cfg.timeout || 30000;
|
|
127
|
+
|
|
128
|
+
const controller = new AbortController();
|
|
129
|
+
const timeoutId = setTimeout(() => controller.abort(), timeout);
|
|
130
|
+
|
|
131
|
+
try {
|
|
132
|
+
const response = await fetch(params.url, {
|
|
133
|
+
method: params.method,
|
|
134
|
+
headers: {
|
|
135
|
+
"User-Agent": cfg.userAgent || "HiveBot/1.0",
|
|
136
|
+
...params.headers,
|
|
137
|
+
},
|
|
138
|
+
body: params.body,
|
|
139
|
+
signal: controller.signal,
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
if (!response.ok) {
|
|
143
|
+
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
const contentType = response.headers.get("content-type") || "";
|
|
147
|
+
let data: unknown;
|
|
148
|
+
|
|
149
|
+
if (contentType.includes("application/json")) {
|
|
150
|
+
data = await response.json();
|
|
151
|
+
} else {
|
|
152
|
+
data = await response.text();
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
return {
|
|
156
|
+
success: true,
|
|
157
|
+
status: response.status,
|
|
158
|
+
statusText: response.statusText,
|
|
159
|
+
headers: Object.fromEntries(response.headers.entries()),
|
|
160
|
+
data,
|
|
161
|
+
};
|
|
162
|
+
} finally {
|
|
163
|
+
clearTimeout(timeoutId);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
export function createBrowserScreenshotTool(_config: BrowserConfig = {}): Tool {
|
|
170
|
+
return {
|
|
171
|
+
name: "browser_screenshot",
|
|
172
|
+
description: "Take a screenshot of a webpage (returns placeholder - requires puppeteer integration)",
|
|
173
|
+
parameters: {
|
|
174
|
+
type: "object",
|
|
175
|
+
properties: {
|
|
176
|
+
url: {
|
|
177
|
+
type: "string",
|
|
178
|
+
description: "The URL to screenshot",
|
|
179
|
+
},
|
|
180
|
+
width: {
|
|
181
|
+
type: "number",
|
|
182
|
+
description: "Viewport width (default: 1280)",
|
|
183
|
+
},
|
|
184
|
+
height: {
|
|
185
|
+
type: "number",
|
|
186
|
+
description: "Viewport height (default: 720)",
|
|
187
|
+
},
|
|
188
|
+
fullPage: {
|
|
189
|
+
type: "boolean",
|
|
190
|
+
description: "Capture full page (default: false)",
|
|
191
|
+
},
|
|
192
|
+
},
|
|
193
|
+
required: ["url"],
|
|
194
|
+
},
|
|
195
|
+
execute: async (params: Record<string, unknown>) => {
|
|
196
|
+
return {
|
|
197
|
+
success: false,
|
|
198
|
+
error: "Screenshot requires puppeteer/playwright integration. Use browser_navigate to get page content instead.",
|
|
199
|
+
url: params.url,
|
|
200
|
+
note: "Install puppeteer and implement headless browser capture for screenshots.",
|
|
201
|
+
};
|
|
202
|
+
},
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
export function createBrowserClickTool(_config: BrowserConfig = {}): Tool {
|
|
207
|
+
return {
|
|
208
|
+
name: "browser_click",
|
|
209
|
+
description: "Click an element on a page (requires puppeteer integration)",
|
|
210
|
+
parameters: {
|
|
211
|
+
type: "object",
|
|
212
|
+
properties: {
|
|
213
|
+
selector: {
|
|
214
|
+
type: "string",
|
|
215
|
+
description: "CSS selector for the element to click",
|
|
216
|
+
},
|
|
217
|
+
},
|
|
218
|
+
required: ["selector"],
|
|
219
|
+
},
|
|
220
|
+
execute: async (params: Record<string, unknown>) => {
|
|
221
|
+
return {
|
|
222
|
+
success: false,
|
|
223
|
+
error: "Click interaction requires puppeteer/playwright integration.",
|
|
224
|
+
selector: params.selector,
|
|
225
|
+
};
|
|
226
|
+
},
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
export function createBrowserTypeTool(_config: BrowserConfig = {}): Tool {
|
|
231
|
+
return {
|
|
232
|
+
name: "browser_type",
|
|
233
|
+
description: "Type text into an input field (requires puppeteer integration)",
|
|
234
|
+
parameters: {
|
|
235
|
+
type: "object",
|
|
236
|
+
properties: {
|
|
237
|
+
selector: {
|
|
238
|
+
type: "string",
|
|
239
|
+
description: "CSS selector for the input field",
|
|
240
|
+
},
|
|
241
|
+
text: {
|
|
242
|
+
type: "string",
|
|
243
|
+
description: "Text to type",
|
|
244
|
+
},
|
|
245
|
+
},
|
|
246
|
+
required: ["selector", "text"],
|
|
247
|
+
},
|
|
248
|
+
execute: async (params: Record<string, unknown>) => {
|
|
249
|
+
return {
|
|
250
|
+
success: false,
|
|
251
|
+
error: "Type interaction requires puppeteer/playwright integration.",
|
|
252
|
+
selector: params.selector,
|
|
253
|
+
text: params.text,
|
|
254
|
+
};
|
|
255
|
+
},
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
function extractTitle(content: string): string {
|
|
260
|
+
const match = content.match(/<title[^>]*>([^<]+)<\/title>/i);
|
|
261
|
+
return match ? match[1]!.trim() : "";
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
export function createBrowserTools(config: BrowserConfig = {}): Tool[] {
|
|
265
|
+
return [
|
|
266
|
+
createBrowserNavigateTool(config),
|
|
267
|
+
createBrowserFetchTool(config),
|
|
268
|
+
createBrowserScreenshotTool(config),
|
|
269
|
+
createBrowserClickTool(config),
|
|
270
|
+
createBrowserTypeTool(config),
|
|
271
|
+
];
|
|
272
|
+
}
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
import type { Tool } from "@johpaz/hive-core";
|
|
2
|
+
import { canvasManager, type CanvasComponent } from "@johpaz/hive-core/canvas";
|
|
3
|
+
|
|
4
|
+
export interface CanvasToolConfig {
|
|
5
|
+
defaultTimeout?: number;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export function createCanvasRenderTool(_config: CanvasToolConfig = {}): Tool {
|
|
9
|
+
return {
|
|
10
|
+
name: "canvas_render",
|
|
11
|
+
description: "Render a component on the user's canvas",
|
|
12
|
+
parameters: {
|
|
13
|
+
type: "object",
|
|
14
|
+
properties: {
|
|
15
|
+
sessionId: {
|
|
16
|
+
type: "string",
|
|
17
|
+
description: "Session ID to render to",
|
|
18
|
+
},
|
|
19
|
+
component: {
|
|
20
|
+
type: "object",
|
|
21
|
+
properties: {
|
|
22
|
+
id: {
|
|
23
|
+
type: "string",
|
|
24
|
+
description: "Unique component ID",
|
|
25
|
+
},
|
|
26
|
+
type: {
|
|
27
|
+
type: "string",
|
|
28
|
+
enum: ["button", "form", "chart", "table", "markdown", "text", "image"],
|
|
29
|
+
description: "Component type",
|
|
30
|
+
},
|
|
31
|
+
props: {
|
|
32
|
+
type: "object",
|
|
33
|
+
description: "Component properties",
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
required: ["id", "type", "props"],
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
required: ["sessionId", "component"],
|
|
40
|
+
},
|
|
41
|
+
execute: async (params: Record<string, unknown>) => {
|
|
42
|
+
const sessionId = params.sessionId as string;
|
|
43
|
+
const component = params.component as CanvasComponent;
|
|
44
|
+
|
|
45
|
+
await canvasManager.render(sessionId, component);
|
|
46
|
+
|
|
47
|
+
return {
|
|
48
|
+
success: true,
|
|
49
|
+
componentId: component.id,
|
|
50
|
+
sessionId,
|
|
51
|
+
};
|
|
52
|
+
},
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export function createCanvasAskTool(config: CanvasToolConfig = {}): Tool {
|
|
57
|
+
return {
|
|
58
|
+
name: "canvas_ask",
|
|
59
|
+
description: "Display a form and wait for user response",
|
|
60
|
+
parameters: {
|
|
61
|
+
type: "object",
|
|
62
|
+
properties: {
|
|
63
|
+
sessionId: {
|
|
64
|
+
type: "string",
|
|
65
|
+
description: "Session ID",
|
|
66
|
+
},
|
|
67
|
+
title: {
|
|
68
|
+
type: "string",
|
|
69
|
+
description: "Form title",
|
|
70
|
+
},
|
|
71
|
+
fields: {
|
|
72
|
+
type: "array",
|
|
73
|
+
items: {
|
|
74
|
+
type: "object",
|
|
75
|
+
properties: {
|
|
76
|
+
name: { type: "string" },
|
|
77
|
+
label: { type: "string" },
|
|
78
|
+
type: { type: "string", enum: ["text", "email", "textarea", "select"] },
|
|
79
|
+
required: { type: "boolean" },
|
|
80
|
+
options: {
|
|
81
|
+
type: "array",
|
|
82
|
+
items: {
|
|
83
|
+
type: "object",
|
|
84
|
+
properties: {
|
|
85
|
+
label: { type: "string" },
|
|
86
|
+
value: { type: "string" },
|
|
87
|
+
},
|
|
88
|
+
},
|
|
89
|
+
},
|
|
90
|
+
},
|
|
91
|
+
required: ["name", "label", "type"],
|
|
92
|
+
},
|
|
93
|
+
description: "Form fields",
|
|
94
|
+
},
|
|
95
|
+
timeout: {
|
|
96
|
+
type: "number",
|
|
97
|
+
description: `Timeout in milliseconds (default: ${config.defaultTimeout || 300000})`,
|
|
98
|
+
},
|
|
99
|
+
},
|
|
100
|
+
required: ["sessionId", "fields"],
|
|
101
|
+
},
|
|
102
|
+
execute: async (params: Record<string, unknown>) => {
|
|
103
|
+
const sessionId = params.sessionId as string;
|
|
104
|
+
const title = (params.title as string) ?? "Form";
|
|
105
|
+
const fields = params.fields as Array<{
|
|
106
|
+
name: string;
|
|
107
|
+
label: string;
|
|
108
|
+
type: string;
|
|
109
|
+
required?: boolean;
|
|
110
|
+
options?: Array<{ label: string; value: string }>;
|
|
111
|
+
}>;
|
|
112
|
+
const timeout = (params.timeout as number) ?? config.defaultTimeout ?? 300000;
|
|
113
|
+
|
|
114
|
+
const formId = `form-${Date.now()}`;
|
|
115
|
+
|
|
116
|
+
await canvasManager.render(sessionId, {
|
|
117
|
+
id: formId,
|
|
118
|
+
type: "form",
|
|
119
|
+
props: { title, fields },
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
try {
|
|
123
|
+
const response = await canvasManager.waitForInteraction(sessionId, formId, timeout);
|
|
124
|
+
|
|
125
|
+
return {
|
|
126
|
+
success: true,
|
|
127
|
+
formId,
|
|
128
|
+
data: response,
|
|
129
|
+
};
|
|
130
|
+
} catch (error) {
|
|
131
|
+
return {
|
|
132
|
+
success: false,
|
|
133
|
+
formId,
|
|
134
|
+
error: (error as Error).message,
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
},
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
export function createCanvasClearTool(_config: CanvasToolConfig = {}): Tool {
|
|
142
|
+
return {
|
|
143
|
+
name: "canvas_clear",
|
|
144
|
+
description: "Clear the canvas for a session",
|
|
145
|
+
parameters: {
|
|
146
|
+
type: "object",
|
|
147
|
+
properties: {
|
|
148
|
+
sessionId: {
|
|
149
|
+
type: "string",
|
|
150
|
+
description: "Session ID to clear",
|
|
151
|
+
},
|
|
152
|
+
},
|
|
153
|
+
required: ["sessionId"],
|
|
154
|
+
},
|
|
155
|
+
execute: async (params: Record<string, unknown>) => {
|
|
156
|
+
const sessionId = params.sessionId as string;
|
|
157
|
+
|
|
158
|
+
await canvasManager.clear(sessionId);
|
|
159
|
+
|
|
160
|
+
return { success: true, sessionId };
|
|
161
|
+
},
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
export function createCanvasUpdateTool(_config: CanvasToolConfig = {}): Tool {
|
|
166
|
+
return {
|
|
167
|
+
name: "canvas_update",
|
|
168
|
+
description: "Update an existing component on the canvas",
|
|
169
|
+
parameters: {
|
|
170
|
+
type: "object",
|
|
171
|
+
properties: {
|
|
172
|
+
sessionId: {
|
|
173
|
+
type: "string",
|
|
174
|
+
description: "Session ID",
|
|
175
|
+
},
|
|
176
|
+
component: {
|
|
177
|
+
type: "object",
|
|
178
|
+
properties: {
|
|
179
|
+
id: {
|
|
180
|
+
type: "string",
|
|
181
|
+
description: "Component ID to update",
|
|
182
|
+
},
|
|
183
|
+
type: {
|
|
184
|
+
type: "string",
|
|
185
|
+
enum: ["button", "form", "chart", "table", "markdown", "text", "image"],
|
|
186
|
+
description: "Component type",
|
|
187
|
+
},
|
|
188
|
+
props: {
|
|
189
|
+
type: "object",
|
|
190
|
+
description: "Updated properties",
|
|
191
|
+
},
|
|
192
|
+
},
|
|
193
|
+
required: ["id"],
|
|
194
|
+
},
|
|
195
|
+
},
|
|
196
|
+
required: ["sessionId", "component"],
|
|
197
|
+
},
|
|
198
|
+
execute: async (params: Record<string, unknown>) => {
|
|
199
|
+
const sessionId = params.sessionId as string;
|
|
200
|
+
const component = params.component as Partial<CanvasComponent> & { id: string };
|
|
201
|
+
|
|
202
|
+
await canvasManager.update(sessionId, component as CanvasComponent);
|
|
203
|
+
|
|
204
|
+
return {
|
|
205
|
+
success: true,
|
|
206
|
+
componentId: component.id,
|
|
207
|
+
sessionId,
|
|
208
|
+
};
|
|
209
|
+
},
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
export function createCanvasTools(config: CanvasToolConfig = {}): Tool[] {
|
|
214
|
+
return [
|
|
215
|
+
createCanvasRenderTool(config),
|
|
216
|
+
createCanvasAskTool(config),
|
|
217
|
+
createCanvasClearTool(config),
|
|
218
|
+
createCanvasUpdateTool(config),
|
|
219
|
+
];
|
|
220
|
+
}
|