@lantos1618/better-ui 0.1.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/LICENSE +21 -0
- package/README.md +190 -0
- package/lib/aui/README.md +136 -0
- package/lib/aui/__tests__/aui-complete.test.ts +251 -0
- package/lib/aui/__tests__/aui-comprehensive.test.ts +376 -0
- package/lib/aui/__tests__/aui-concise.test.ts +278 -0
- package/lib/aui/__tests__/aui-integration.test.ts +309 -0
- package/lib/aui/__tests__/aui-simple.test.ts +116 -0
- package/lib/aui/__tests__/aui.test.ts +269 -0
- package/lib/aui/__tests__/concise-api.test.ts +165 -0
- package/lib/aui/__tests__/core.test.ts +265 -0
- package/lib/aui/__tests__/simple-api.test.ts +200 -0
- package/lib/aui/ai-assistant.ts +408 -0
- package/lib/aui/ai-control.ts +353 -0
- package/lib/aui/client/use-aui.ts +55 -0
- package/lib/aui/client-control.ts +551 -0
- package/lib/aui/client-executor.ts +417 -0
- package/lib/aui/components/ToolRenderer.tsx +22 -0
- package/lib/aui/core.ts +137 -0
- package/lib/aui/demo.tsx +89 -0
- package/lib/aui/examples/ai-complete-demo.tsx +359 -0
- package/lib/aui/examples/ai-control-demo.tsx +356 -0
- package/lib/aui/examples/ai-control-tools.ts +308 -0
- package/lib/aui/examples/concise-api.tsx +153 -0
- package/lib/aui/examples/index.tsx +163 -0
- package/lib/aui/examples/quick-demo.tsx +91 -0
- package/lib/aui/examples/simple-demo.tsx +71 -0
- package/lib/aui/examples/simple-tools.tsx +160 -0
- package/lib/aui/examples/user-api.tsx +208 -0
- package/lib/aui/examples/user-requested.tsx +174 -0
- package/lib/aui/examples/weather-search-tools.tsx +119 -0
- package/lib/aui/examples.tsx +367 -0
- package/lib/aui/hooks/useAUITool.ts +142 -0
- package/lib/aui/hooks/useAUIToolEnhanced.ts +343 -0
- package/lib/aui/hooks/useAUITools.ts +195 -0
- package/lib/aui/index.ts +156 -0
- package/lib/aui/provider.tsx +45 -0
- package/lib/aui/server-control.ts +386 -0
- package/lib/aui/server-executor.ts +165 -0
- package/lib/aui/server.ts +167 -0
- package/lib/aui/tool-registry.ts +380 -0
- package/lib/aui/tools/advanced-examples.tsx +86 -0
- package/lib/aui/tools/ai-complete.ts +375 -0
- package/lib/aui/tools/api-tools.tsx +230 -0
- package/lib/aui/tools/data-tools.tsx +232 -0
- package/lib/aui/tools/dom-tools.tsx +202 -0
- package/lib/aui/tools/examples.ts +43 -0
- package/lib/aui/tools/file-tools.tsx +202 -0
- package/lib/aui/tools/form-tools.tsx +233 -0
- package/lib/aui/tools/index.ts +8 -0
- package/lib/aui/tools/navigation-tools.tsx +172 -0
- package/lib/aui/tools/notification-tools.ts +213 -0
- package/lib/aui/tools/state-tools.tsx +209 -0
- package/lib/aui/types.ts +47 -0
- package/lib/aui/vercel-ai.ts +100 -0
- package/package.json +51 -0
|
@@ -0,0 +1,356 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import React from 'react';
|
|
4
|
+
import aui from '../index';
|
|
5
|
+
import { z } from 'zod';
|
|
6
|
+
|
|
7
|
+
// ============================================
|
|
8
|
+
// BACKEND CONTROL TOOLS
|
|
9
|
+
// ============================================
|
|
10
|
+
|
|
11
|
+
// Database query tool - AI can query your database
|
|
12
|
+
const queryDatabaseTool = aui
|
|
13
|
+
.tool('queryDatabase')
|
|
14
|
+
.input(z.object({
|
|
15
|
+
table: z.string(),
|
|
16
|
+
query: z.object({
|
|
17
|
+
where: z.record(z.any()).optional(),
|
|
18
|
+
limit: z.number().optional(),
|
|
19
|
+
orderBy: z.string().optional()
|
|
20
|
+
})
|
|
21
|
+
}))
|
|
22
|
+
.execute(async ({ input }) => {
|
|
23
|
+
// Server-side database query (would connect to your actual DB)
|
|
24
|
+
console.log('AI executing database query:', input);
|
|
25
|
+
return {
|
|
26
|
+
rows: [
|
|
27
|
+
{ id: 1, name: 'User 1', email: 'user1@example.com' },
|
|
28
|
+
{ id: 2, name: 'User 2', email: 'user2@example.com' }
|
|
29
|
+
],
|
|
30
|
+
count: 2
|
|
31
|
+
};
|
|
32
|
+
})
|
|
33
|
+
.render(({ data }) => (
|
|
34
|
+
<div className="p-2 bg-gray-100 rounded">
|
|
35
|
+
<h4 className="font-bold">Database Results</h4>
|
|
36
|
+
<pre className="text-sm">{JSON.stringify(data.rows, null, 2)}</pre>
|
|
37
|
+
<p className="text-xs mt-2">Found {data.count} records</p>
|
|
38
|
+
</div>
|
|
39
|
+
));
|
|
40
|
+
|
|
41
|
+
// API endpoint control - AI can call your API endpoints
|
|
42
|
+
const callAPITool = aui
|
|
43
|
+
.tool('callAPI')
|
|
44
|
+
.input(z.object({
|
|
45
|
+
endpoint: z.string(),
|
|
46
|
+
method: z.enum(['GET', 'POST', 'PUT', 'DELETE']),
|
|
47
|
+
body: z.any().optional(),
|
|
48
|
+
headers: z.record(z.string()).optional()
|
|
49
|
+
}))
|
|
50
|
+
.execute(async ({ input }) => {
|
|
51
|
+
// Server-side API call
|
|
52
|
+
const response = await fetch(input.endpoint, {
|
|
53
|
+
method: input.method,
|
|
54
|
+
headers: input.headers,
|
|
55
|
+
body: input.body ? JSON.stringify(input.body) : undefined
|
|
56
|
+
});
|
|
57
|
+
return await response.json();
|
|
58
|
+
})
|
|
59
|
+
.clientExecute(async ({ input, ctx }) => {
|
|
60
|
+
// Client-side with caching
|
|
61
|
+
const cacheKey = `${input.method}:${input.endpoint}`;
|
|
62
|
+
if (input.method === 'GET') {
|
|
63
|
+
const cached = ctx.cache.get(cacheKey);
|
|
64
|
+
if (cached) return cached;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const result = await ctx.fetch(input.endpoint, {
|
|
68
|
+
method: input.method,
|
|
69
|
+
headers: input.headers,
|
|
70
|
+
body: input.body ? JSON.stringify(input.body) : undefined
|
|
71
|
+
}).then(r => r.json());
|
|
72
|
+
|
|
73
|
+
if (input.method === 'GET') {
|
|
74
|
+
ctx.cache.set(cacheKey, result);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return result;
|
|
78
|
+
})
|
|
79
|
+
.render(({ data }) => (
|
|
80
|
+
<div className="p-2 bg-blue-50 rounded">
|
|
81
|
+
<h4 className="font-bold">API Response</h4>
|
|
82
|
+
<pre className="text-sm">{JSON.stringify(data, null, 2)}</pre>
|
|
83
|
+
</div>
|
|
84
|
+
));
|
|
85
|
+
|
|
86
|
+
// ============================================
|
|
87
|
+
// FRONTEND CONTROL TOOLS
|
|
88
|
+
// ============================================
|
|
89
|
+
|
|
90
|
+
// Update DOM - AI can modify page content
|
|
91
|
+
const updateDOMTool = aui
|
|
92
|
+
.tool('updateDOM')
|
|
93
|
+
.input(z.object({
|
|
94
|
+
selector: z.string(),
|
|
95
|
+
action: z.enum(['setText', 'setHTML', 'addClass', 'removeClass', 'setStyle', 'setAttribute']),
|
|
96
|
+
value: z.string()
|
|
97
|
+
}))
|
|
98
|
+
.clientExecute(async ({ input }) => {
|
|
99
|
+
const element = document.querySelector(input.selector);
|
|
100
|
+
if (!element) throw new Error(`Element not found: ${input.selector}`);
|
|
101
|
+
|
|
102
|
+
switch (input.action) {
|
|
103
|
+
case 'setText':
|
|
104
|
+
element.textContent = input.value;
|
|
105
|
+
break;
|
|
106
|
+
case 'setHTML':
|
|
107
|
+
element.innerHTML = input.value;
|
|
108
|
+
break;
|
|
109
|
+
case 'addClass':
|
|
110
|
+
element.classList.add(input.value);
|
|
111
|
+
break;
|
|
112
|
+
case 'removeClass':
|
|
113
|
+
element.classList.remove(input.value);
|
|
114
|
+
break;
|
|
115
|
+
case 'setStyle':
|
|
116
|
+
Object.assign(element as HTMLElement, { style: input.value });
|
|
117
|
+
break;
|
|
118
|
+
case 'setAttribute':
|
|
119
|
+
const [attr, val] = input.value.split('=');
|
|
120
|
+
element.setAttribute(attr, val);
|
|
121
|
+
break;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
return { success: true, selector: input.selector, action: input.action };
|
|
125
|
+
})
|
|
126
|
+
.render(({ data }) => (
|
|
127
|
+
<div className="p-2 bg-green-50 rounded">
|
|
128
|
+
<p className="text-sm">✅ DOM updated: {data.action} on {data.selector}</p>
|
|
129
|
+
</div>
|
|
130
|
+
));
|
|
131
|
+
|
|
132
|
+
// Form submission - AI can submit forms
|
|
133
|
+
const submitFormTool = aui
|
|
134
|
+
.tool('submitForm')
|
|
135
|
+
.input(z.object({
|
|
136
|
+
formId: z.string(),
|
|
137
|
+
data: z.record(z.any())
|
|
138
|
+
}))
|
|
139
|
+
.clientExecute(async ({ input }) => {
|
|
140
|
+
const form = document.getElementById(input.formId) as HTMLFormElement;
|
|
141
|
+
if (!form) throw new Error(`Form not found: ${input.formId}`);
|
|
142
|
+
|
|
143
|
+
// Set form values
|
|
144
|
+
Object.entries(input.data).forEach(([name, value]) => {
|
|
145
|
+
const field = form.elements.namedItem(name) as HTMLInputElement;
|
|
146
|
+
if (field) field.value = String(value);
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
// Trigger submit event
|
|
150
|
+
const submitEvent = new Event('submit', { bubbles: true, cancelable: true });
|
|
151
|
+
form.dispatchEvent(submitEvent);
|
|
152
|
+
|
|
153
|
+
return { submitted: true, formId: input.formId, data: input.data };
|
|
154
|
+
})
|
|
155
|
+
.render(({ data }) => (
|
|
156
|
+
<div className="p-2 bg-purple-50 rounded">
|
|
157
|
+
<p className="text-sm">📝 Form submitted: {data.formId}</p>
|
|
158
|
+
</div>
|
|
159
|
+
));
|
|
160
|
+
|
|
161
|
+
// Navigate - AI can navigate the app
|
|
162
|
+
const navigateTool = aui
|
|
163
|
+
.tool('navigate')
|
|
164
|
+
.input(z.object({
|
|
165
|
+
path: z.string(),
|
|
166
|
+
params: z.record(z.string()).optional()
|
|
167
|
+
}))
|
|
168
|
+
.clientExecute(async ({ input }) => {
|
|
169
|
+
const url = new URL(input.path, window.location.origin);
|
|
170
|
+
if (input.params) {
|
|
171
|
+
Object.entries(input.params).forEach(([key, value]) => {
|
|
172
|
+
url.searchParams.set(key, value);
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// For Next.js navigation
|
|
177
|
+
if (typeof window !== 'undefined' && (window as any).next?.router) {
|
|
178
|
+
await (window as any).next.router.push(url.pathname + url.search);
|
|
179
|
+
} else {
|
|
180
|
+
window.location.href = url.toString();
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
return { navigatedTo: url.pathname, params: input.params };
|
|
184
|
+
})
|
|
185
|
+
.render(({ data }) => (
|
|
186
|
+
<div className="p-2 bg-indigo-50 rounded">
|
|
187
|
+
<p className="text-sm">🔗 Navigated to: {data.navigatedTo}</p>
|
|
188
|
+
</div>
|
|
189
|
+
));
|
|
190
|
+
|
|
191
|
+
// Local storage - AI can persist data
|
|
192
|
+
const localStorageTool = aui
|
|
193
|
+
.tool('localStorage')
|
|
194
|
+
.input(z.object({
|
|
195
|
+
action: z.enum(['get', 'set', 'remove', 'clear']),
|
|
196
|
+
key: z.string().optional(),
|
|
197
|
+
value: z.any().optional()
|
|
198
|
+
}))
|
|
199
|
+
.clientExecute(async ({ input }) => {
|
|
200
|
+
switch (input.action) {
|
|
201
|
+
case 'get':
|
|
202
|
+
return { value: localStorage.getItem(input.key!) };
|
|
203
|
+
case 'set':
|
|
204
|
+
localStorage.setItem(input.key!, JSON.stringify(input.value));
|
|
205
|
+
return { stored: true, key: input.key };
|
|
206
|
+
case 'remove':
|
|
207
|
+
localStorage.removeItem(input.key!);
|
|
208
|
+
return { removed: true, key: input.key };
|
|
209
|
+
case 'clear':
|
|
210
|
+
localStorage.clear();
|
|
211
|
+
return { cleared: true };
|
|
212
|
+
default:
|
|
213
|
+
throw new Error(`Unknown action: ${input.action}`);
|
|
214
|
+
}
|
|
215
|
+
})
|
|
216
|
+
.render(({ data }) => (
|
|
217
|
+
<div className="p-2 bg-yellow-50 rounded">
|
|
218
|
+
<p className="text-sm">💾 Storage: {JSON.stringify(data)}</p>
|
|
219
|
+
</div>
|
|
220
|
+
));
|
|
221
|
+
|
|
222
|
+
// ============================================
|
|
223
|
+
// DEMO COMPONENT
|
|
224
|
+
// ============================================
|
|
225
|
+
|
|
226
|
+
export function AIControlDemo() {
|
|
227
|
+
const [results, setResults] = React.useState<any[]>([]);
|
|
228
|
+
const [loading, setLoading] = React.useState(false);
|
|
229
|
+
|
|
230
|
+
const executeAICommand = async (tool: any, input: any) => {
|
|
231
|
+
setLoading(true);
|
|
232
|
+
try {
|
|
233
|
+
const result = await tool.run(input);
|
|
234
|
+
setResults(prev => [...prev, { tool: tool.name, result, timestamp: new Date() }]);
|
|
235
|
+
} catch (error) {
|
|
236
|
+
console.error('AI command failed:', error);
|
|
237
|
+
setResults(prev => [...prev, { tool: tool.name, error: String(error), timestamp: new Date() }]);
|
|
238
|
+
} finally {
|
|
239
|
+
setLoading(false);
|
|
240
|
+
}
|
|
241
|
+
};
|
|
242
|
+
|
|
243
|
+
return (
|
|
244
|
+
<div className="p-6 max-w-4xl mx-auto">
|
|
245
|
+
<h1 className="text-3xl font-bold mb-6">AI Control Demo - Frontend & Backend</h1>
|
|
246
|
+
|
|
247
|
+
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
|
248
|
+
{/* Backend Control Examples */}
|
|
249
|
+
<div className="border rounded-lg p-4">
|
|
250
|
+
<h2 className="text-xl font-semibold mb-4">Backend Control</h2>
|
|
251
|
+
|
|
252
|
+
<div className="space-y-3">
|
|
253
|
+
<button
|
|
254
|
+
onClick={() => executeAICommand(queryDatabaseTool, {
|
|
255
|
+
table: 'users',
|
|
256
|
+
query: { limit: 10, orderBy: 'created_at' }
|
|
257
|
+
})}
|
|
258
|
+
className="w-full px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600"
|
|
259
|
+
disabled={loading}
|
|
260
|
+
>
|
|
261
|
+
AI: Query Database
|
|
262
|
+
</button>
|
|
263
|
+
|
|
264
|
+
<button
|
|
265
|
+
onClick={() => executeAICommand(callAPITool, {
|
|
266
|
+
endpoint: '/api/health',
|
|
267
|
+
method: 'GET'
|
|
268
|
+
})}
|
|
269
|
+
className="w-full px-4 py-2 bg-green-500 text-white rounded hover:bg-green-600"
|
|
270
|
+
disabled={loading}
|
|
271
|
+
>
|
|
272
|
+
AI: Call API Endpoint
|
|
273
|
+
</button>
|
|
274
|
+
</div>
|
|
275
|
+
</div>
|
|
276
|
+
|
|
277
|
+
{/* Frontend Control Examples */}
|
|
278
|
+
<div className="border rounded-lg p-4">
|
|
279
|
+
<h2 className="text-xl font-semibold mb-4">Frontend Control</h2>
|
|
280
|
+
|
|
281
|
+
<div className="space-y-3">
|
|
282
|
+
<button
|
|
283
|
+
onClick={() => executeAICommand(updateDOMTool, {
|
|
284
|
+
selector: 'h1',
|
|
285
|
+
action: 'setText',
|
|
286
|
+
value: 'AI Modified This Title!'
|
|
287
|
+
})}
|
|
288
|
+
className="w-full px-4 py-2 bg-purple-500 text-white rounded hover:bg-purple-600"
|
|
289
|
+
disabled={loading}
|
|
290
|
+
>
|
|
291
|
+
AI: Update Page Title
|
|
292
|
+
</button>
|
|
293
|
+
|
|
294
|
+
<button
|
|
295
|
+
onClick={() => executeAICommand(localStorageTool, {
|
|
296
|
+
action: 'set',
|
|
297
|
+
key: 'ai_data',
|
|
298
|
+
value: { message: 'AI was here', timestamp: Date.now() }
|
|
299
|
+
})}
|
|
300
|
+
className="w-full px-4 py-2 bg-yellow-500 text-white rounded hover:bg-yellow-600"
|
|
301
|
+
disabled={loading}
|
|
302
|
+
>
|
|
303
|
+
AI: Store Data Locally
|
|
304
|
+
</button>
|
|
305
|
+
|
|
306
|
+
<button
|
|
307
|
+
onClick={() => executeAICommand(navigateTool, {
|
|
308
|
+
path: '/',
|
|
309
|
+
params: { ai_action: 'demo' }
|
|
310
|
+
})}
|
|
311
|
+
className="w-full px-4 py-2 bg-indigo-500 text-white rounded hover:bg-indigo-600"
|
|
312
|
+
disabled={loading}
|
|
313
|
+
>
|
|
314
|
+
AI: Navigate Home
|
|
315
|
+
</button>
|
|
316
|
+
</div>
|
|
317
|
+
</div>
|
|
318
|
+
</div>
|
|
319
|
+
|
|
320
|
+
{/* Results Display */}
|
|
321
|
+
<div className="mt-8">
|
|
322
|
+
<h2 className="text-xl font-semibold mb-4">AI Execution Results</h2>
|
|
323
|
+
<div className="space-y-2 max-h-96 overflow-y-auto">
|
|
324
|
+
{results.map((item, idx) => (
|
|
325
|
+
<div key={idx} className="border rounded p-3">
|
|
326
|
+
<div className="flex justify-between text-sm text-gray-600 mb-2">
|
|
327
|
+
<span className="font-medium">{item.tool}</span>
|
|
328
|
+
<span>{item.timestamp.toLocaleTimeString()}</span>
|
|
329
|
+
</div>
|
|
330
|
+
{item.error ? (
|
|
331
|
+
<div className="text-red-600 text-sm">Error: {item.error}</div>
|
|
332
|
+
) : (
|
|
333
|
+
<pre className="text-sm bg-gray-50 p-2 rounded overflow-x-auto">
|
|
334
|
+
{JSON.stringify(item.result, null, 2)}
|
|
335
|
+
</pre>
|
|
336
|
+
)}
|
|
337
|
+
</div>
|
|
338
|
+
))}
|
|
339
|
+
{results.length === 0 && (
|
|
340
|
+
<p className="text-gray-500 text-center py-4">No AI commands executed yet</p>
|
|
341
|
+
)}
|
|
342
|
+
</div>
|
|
343
|
+
</div>
|
|
344
|
+
</div>
|
|
345
|
+
);
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
// Export all tools for AI integration
|
|
349
|
+
export const aiControlTools = {
|
|
350
|
+
queryDatabaseTool,
|
|
351
|
+
callAPITool,
|
|
352
|
+
updateDOMTool,
|
|
353
|
+
submitFormTool,
|
|
354
|
+
navigateTool,
|
|
355
|
+
localStorageTool
|
|
356
|
+
};
|
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
import aui, { z } from '../index';
|
|
2
|
+
|
|
3
|
+
// AI Control Tools for Frontend/Backend Operations
|
|
4
|
+
|
|
5
|
+
// Database operations tool
|
|
6
|
+
export const databaseTool = aui
|
|
7
|
+
.tool('database')
|
|
8
|
+
.input(z.object({
|
|
9
|
+
operation: z.enum(['find', 'create', 'update', 'delete']),
|
|
10
|
+
collection: z.string(),
|
|
11
|
+
query: z.any().optional(),
|
|
12
|
+
data: z.any().optional()
|
|
13
|
+
}))
|
|
14
|
+
.execute(async ({ input }) => {
|
|
15
|
+
// Server-side database operations
|
|
16
|
+
switch (input.operation) {
|
|
17
|
+
case 'find':
|
|
18
|
+
return { results: [], count: 0 }; // Mock implementation
|
|
19
|
+
case 'create':
|
|
20
|
+
return { id: crypto.randomUUID(), ...input.data };
|
|
21
|
+
case 'update':
|
|
22
|
+
return { modified: 1 };
|
|
23
|
+
case 'delete':
|
|
24
|
+
return { deleted: 1 };
|
|
25
|
+
}
|
|
26
|
+
})
|
|
27
|
+
.clientExecute(async ({ input, ctx }) => {
|
|
28
|
+
// Client-side API call
|
|
29
|
+
return ctx.fetch('/api/tools/database', {
|
|
30
|
+
method: 'POST',
|
|
31
|
+
headers: { 'Content-Type': 'application/json' },
|
|
32
|
+
body: JSON.stringify(input)
|
|
33
|
+
}).then(r => r.json());
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
// File system operations tool
|
|
37
|
+
export const fileSystemTool = aui
|
|
38
|
+
.tool('filesystem')
|
|
39
|
+
.input(z.object({
|
|
40
|
+
operation: z.enum(['read', 'write', 'list', 'delete']),
|
|
41
|
+
path: z.string(),
|
|
42
|
+
content: z.string().optional()
|
|
43
|
+
}))
|
|
44
|
+
.execute(async ({ input }) => {
|
|
45
|
+
// Server-only file operations
|
|
46
|
+
switch (input.operation) {
|
|
47
|
+
case 'read':
|
|
48
|
+
return { content: 'file content', path: input.path };
|
|
49
|
+
case 'write':
|
|
50
|
+
return { success: true, path: input.path };
|
|
51
|
+
case 'list':
|
|
52
|
+
return { files: [], directories: [] };
|
|
53
|
+
case 'delete':
|
|
54
|
+
return { success: true };
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
// API request tool for external services
|
|
59
|
+
export const apiRequestTool = aui
|
|
60
|
+
.tool('api_request')
|
|
61
|
+
.input(z.object({
|
|
62
|
+
url: z.string().url(),
|
|
63
|
+
method: z.enum(['GET', 'POST', 'PUT', 'DELETE']),
|
|
64
|
+
headers: z.record(z.string()).optional(),
|
|
65
|
+
body: z.any().optional()
|
|
66
|
+
}))
|
|
67
|
+
.execute(async ({ input }) => {
|
|
68
|
+
const response = await fetch(input.url, {
|
|
69
|
+
method: input.method,
|
|
70
|
+
headers: input.headers,
|
|
71
|
+
body: input.body ? JSON.stringify(input.body) : undefined
|
|
72
|
+
});
|
|
73
|
+
return response.json();
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
// DOM manipulation tool
|
|
77
|
+
export const domTool = aui
|
|
78
|
+
.tool('dom_manipulate')
|
|
79
|
+
.input(z.object({
|
|
80
|
+
selector: z.string(),
|
|
81
|
+
action: z.enum(['click', 'type', 'scroll', 'focus', 'getValue']),
|
|
82
|
+
value: z.string().optional()
|
|
83
|
+
}))
|
|
84
|
+
.clientExecute(async ({ input }) => {
|
|
85
|
+
// Client-only DOM operations
|
|
86
|
+
const element = document.querySelector(input.selector) as HTMLElement;
|
|
87
|
+
if (!element) throw new Error(`Element not found: ${input.selector}`);
|
|
88
|
+
|
|
89
|
+
switch (input.action) {
|
|
90
|
+
case 'click':
|
|
91
|
+
element.click();
|
|
92
|
+
return { success: true };
|
|
93
|
+
case 'type':
|
|
94
|
+
if (element instanceof HTMLInputElement && input.value) {
|
|
95
|
+
element.value = input.value;
|
|
96
|
+
element.dispatchEvent(new Event('input', { bubbles: true }));
|
|
97
|
+
}
|
|
98
|
+
return { success: true };
|
|
99
|
+
case 'scroll':
|
|
100
|
+
element.scrollIntoView({ behavior: 'smooth' });
|
|
101
|
+
return { success: true };
|
|
102
|
+
case 'focus':
|
|
103
|
+
element.focus();
|
|
104
|
+
return { success: true };
|
|
105
|
+
case 'getValue':
|
|
106
|
+
return { value: (element as HTMLInputElement).value || element.textContent };
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
// State management tool
|
|
111
|
+
export const stateTool = aui
|
|
112
|
+
.tool('state')
|
|
113
|
+
.input(z.object({
|
|
114
|
+
action: z.enum(['get', 'set', 'update', 'subscribe']),
|
|
115
|
+
key: z.string(),
|
|
116
|
+
value: z.any().optional()
|
|
117
|
+
}))
|
|
118
|
+
.clientExecute(async ({ input, ctx }) => {
|
|
119
|
+
// Client-side state management
|
|
120
|
+
const state = (ctx as any).state || new Map();
|
|
121
|
+
|
|
122
|
+
switch (input.action) {
|
|
123
|
+
case 'get':
|
|
124
|
+
return state.get(input.key);
|
|
125
|
+
case 'set':
|
|
126
|
+
state.set(input.key, input.value);
|
|
127
|
+
return { success: true };
|
|
128
|
+
case 'update':
|
|
129
|
+
const current = state.get(input.key) || {};
|
|
130
|
+
state.set(input.key, { ...current, ...input.value });
|
|
131
|
+
return { success: true };
|
|
132
|
+
default:
|
|
133
|
+
return null;
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
// Navigation tool
|
|
138
|
+
export const navigationTool = aui
|
|
139
|
+
.tool('navigate')
|
|
140
|
+
.input(z.object({
|
|
141
|
+
url: z.string(),
|
|
142
|
+
type: z.enum(['push', 'replace', 'reload']).default('push')
|
|
143
|
+
}))
|
|
144
|
+
.clientExecute(async ({ input }) => {
|
|
145
|
+
if (typeof window === 'undefined') {
|
|
146
|
+
throw new Error('Navigation only available on client');
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
switch (input.type) {
|
|
150
|
+
case 'push':
|
|
151
|
+
window.history.pushState({}, '', input.url);
|
|
152
|
+
break;
|
|
153
|
+
case 'replace':
|
|
154
|
+
window.history.replaceState({}, '', input.url);
|
|
155
|
+
break;
|
|
156
|
+
case 'reload':
|
|
157
|
+
window.location.href = input.url;
|
|
158
|
+
break;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
return { success: true, url: input.url };
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
// Form submission tool
|
|
165
|
+
export const formTool = aui
|
|
166
|
+
.tool('form')
|
|
167
|
+
.input(z.object({
|
|
168
|
+
formId: z.string().optional(),
|
|
169
|
+
data: z.record(z.any()),
|
|
170
|
+
endpoint: z.string().optional()
|
|
171
|
+
}))
|
|
172
|
+
.execute(async ({ input }) => {
|
|
173
|
+
// Server-side form processing
|
|
174
|
+
return {
|
|
175
|
+
success: true,
|
|
176
|
+
data: input.data,
|
|
177
|
+
timestamp: new Date().toISOString()
|
|
178
|
+
};
|
|
179
|
+
})
|
|
180
|
+
.clientExecute(async ({ input, ctx }) => {
|
|
181
|
+
const endpoint = input.endpoint || '/api/tools/form';
|
|
182
|
+
return ctx.fetch(endpoint, {
|
|
183
|
+
method: 'POST',
|
|
184
|
+
headers: { 'Content-Type': 'application/json' },
|
|
185
|
+
body: JSON.stringify(input.data)
|
|
186
|
+
}).then(r => r.json());
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
// WebSocket communication tool
|
|
190
|
+
export const websocketTool = aui
|
|
191
|
+
.tool('websocket')
|
|
192
|
+
.input(z.object({
|
|
193
|
+
action: z.enum(['connect', 'send', 'close']),
|
|
194
|
+
url: z.string().optional(),
|
|
195
|
+
message: z.any().optional()
|
|
196
|
+
}))
|
|
197
|
+
.clientExecute(async ({ input }) => {
|
|
198
|
+
// Client-only WebSocket operations
|
|
199
|
+
const wsMap = (globalThis as any).__wsConnections || new Map();
|
|
200
|
+
|
|
201
|
+
switch (input.action) {
|
|
202
|
+
case 'connect':
|
|
203
|
+
if (!input.url) throw new Error('URL required for connect');
|
|
204
|
+
const ws = new WebSocket(input.url);
|
|
205
|
+
wsMap.set(input.url, ws);
|
|
206
|
+
return { connected: true, url: input.url };
|
|
207
|
+
|
|
208
|
+
case 'send':
|
|
209
|
+
if (!input.url) throw new Error('URL required for send');
|
|
210
|
+
const connection = wsMap.get(input.url);
|
|
211
|
+
if (!connection) throw new Error('No connection found');
|
|
212
|
+
connection.send(JSON.stringify(input.message));
|
|
213
|
+
return { sent: true };
|
|
214
|
+
|
|
215
|
+
case 'close':
|
|
216
|
+
if (!input.url) throw new Error('URL required for close');
|
|
217
|
+
const conn = wsMap.get(input.url);
|
|
218
|
+
if (conn) {
|
|
219
|
+
conn.close();
|
|
220
|
+
wsMap.delete(input.url);
|
|
221
|
+
}
|
|
222
|
+
return { closed: true };
|
|
223
|
+
}
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
// Email sending tool
|
|
227
|
+
export const emailTool = aui
|
|
228
|
+
.tool('email')
|
|
229
|
+
.input(z.object({
|
|
230
|
+
to: z.string().email(),
|
|
231
|
+
subject: z.string(),
|
|
232
|
+
body: z.string(),
|
|
233
|
+
html: z.boolean().optional()
|
|
234
|
+
}))
|
|
235
|
+
.execute(async ({ input }) => {
|
|
236
|
+
// Server-only email sending
|
|
237
|
+
console.log('Sending email:', input);
|
|
238
|
+
return {
|
|
239
|
+
success: true,
|
|
240
|
+
messageId: crypto.randomUUID(),
|
|
241
|
+
timestamp: new Date().toISOString()
|
|
242
|
+
};
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
// Cache management tool
|
|
246
|
+
export const cacheTool = aui
|
|
247
|
+
.tool('cache')
|
|
248
|
+
.input(z.object({
|
|
249
|
+
action: z.enum(['get', 'set', 'delete', 'clear']),
|
|
250
|
+
key: z.string().optional(),
|
|
251
|
+
value: z.any().optional(),
|
|
252
|
+
ttl: z.number().optional()
|
|
253
|
+
}))
|
|
254
|
+
.execute(async ({ input, ctx }) => {
|
|
255
|
+
const cache = ctx?.cache || new Map();
|
|
256
|
+
|
|
257
|
+
switch (input.action) {
|
|
258
|
+
case 'get':
|
|
259
|
+
return cache.get(input.key);
|
|
260
|
+
case 'set':
|
|
261
|
+
cache.set(input.key, input.value);
|
|
262
|
+
return { success: true };
|
|
263
|
+
case 'delete':
|
|
264
|
+
return { success: cache.delete(input.key) };
|
|
265
|
+
case 'clear':
|
|
266
|
+
cache.clear();
|
|
267
|
+
return { success: true };
|
|
268
|
+
}
|
|
269
|
+
})
|
|
270
|
+
.clientExecute(async ({ input, ctx }) => {
|
|
271
|
+
// Use browser cache APIs when available
|
|
272
|
+
if ('caches' in globalThis) {
|
|
273
|
+
const cache = await caches.open('aui-cache');
|
|
274
|
+
|
|
275
|
+
switch (input.action) {
|
|
276
|
+
case 'get':
|
|
277
|
+
const response = await cache.match(input.key!);
|
|
278
|
+
return response ? await response.json() : null;
|
|
279
|
+
case 'set':
|
|
280
|
+
await cache.put(input.key!, new Response(JSON.stringify(input.value)));
|
|
281
|
+
return { success: true };
|
|
282
|
+
case 'delete':
|
|
283
|
+
return { success: await cache.delete(input.key!) };
|
|
284
|
+
case 'clear':
|
|
285
|
+
// Clear all cache entries
|
|
286
|
+
return { success: true };
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
// Fallback to context cache
|
|
291
|
+
return ctx.cache;
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
// Export all tools for easy import
|
|
295
|
+
export const aiControlTools = {
|
|
296
|
+
database: databaseTool,
|
|
297
|
+
filesystem: fileSystemTool,
|
|
298
|
+
apiRequest: apiRequestTool,
|
|
299
|
+
dom: domTool,
|
|
300
|
+
state: stateTool,
|
|
301
|
+
navigation: navigationTool,
|
|
302
|
+
form: formTool,
|
|
303
|
+
websocket: websocketTool,
|
|
304
|
+
email: emailTool,
|
|
305
|
+
cache: cacheTool
|
|
306
|
+
};
|
|
307
|
+
|
|
308
|
+
export default aiControlTools;
|