@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.
Files changed (56) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +190 -0
  3. package/lib/aui/README.md +136 -0
  4. package/lib/aui/__tests__/aui-complete.test.ts +251 -0
  5. package/lib/aui/__tests__/aui-comprehensive.test.ts +376 -0
  6. package/lib/aui/__tests__/aui-concise.test.ts +278 -0
  7. package/lib/aui/__tests__/aui-integration.test.ts +309 -0
  8. package/lib/aui/__tests__/aui-simple.test.ts +116 -0
  9. package/lib/aui/__tests__/aui.test.ts +269 -0
  10. package/lib/aui/__tests__/concise-api.test.ts +165 -0
  11. package/lib/aui/__tests__/core.test.ts +265 -0
  12. package/lib/aui/__tests__/simple-api.test.ts +200 -0
  13. package/lib/aui/ai-assistant.ts +408 -0
  14. package/lib/aui/ai-control.ts +353 -0
  15. package/lib/aui/client/use-aui.ts +55 -0
  16. package/lib/aui/client-control.ts +551 -0
  17. package/lib/aui/client-executor.ts +417 -0
  18. package/lib/aui/components/ToolRenderer.tsx +22 -0
  19. package/lib/aui/core.ts +137 -0
  20. package/lib/aui/demo.tsx +89 -0
  21. package/lib/aui/examples/ai-complete-demo.tsx +359 -0
  22. package/lib/aui/examples/ai-control-demo.tsx +356 -0
  23. package/lib/aui/examples/ai-control-tools.ts +308 -0
  24. package/lib/aui/examples/concise-api.tsx +153 -0
  25. package/lib/aui/examples/index.tsx +163 -0
  26. package/lib/aui/examples/quick-demo.tsx +91 -0
  27. package/lib/aui/examples/simple-demo.tsx +71 -0
  28. package/lib/aui/examples/simple-tools.tsx +160 -0
  29. package/lib/aui/examples/user-api.tsx +208 -0
  30. package/lib/aui/examples/user-requested.tsx +174 -0
  31. package/lib/aui/examples/weather-search-tools.tsx +119 -0
  32. package/lib/aui/examples.tsx +367 -0
  33. package/lib/aui/hooks/useAUITool.ts +142 -0
  34. package/lib/aui/hooks/useAUIToolEnhanced.ts +343 -0
  35. package/lib/aui/hooks/useAUITools.ts +195 -0
  36. package/lib/aui/index.ts +156 -0
  37. package/lib/aui/provider.tsx +45 -0
  38. package/lib/aui/server-control.ts +386 -0
  39. package/lib/aui/server-executor.ts +165 -0
  40. package/lib/aui/server.ts +167 -0
  41. package/lib/aui/tool-registry.ts +380 -0
  42. package/lib/aui/tools/advanced-examples.tsx +86 -0
  43. package/lib/aui/tools/ai-complete.ts +375 -0
  44. package/lib/aui/tools/api-tools.tsx +230 -0
  45. package/lib/aui/tools/data-tools.tsx +232 -0
  46. package/lib/aui/tools/dom-tools.tsx +202 -0
  47. package/lib/aui/tools/examples.ts +43 -0
  48. package/lib/aui/tools/file-tools.tsx +202 -0
  49. package/lib/aui/tools/form-tools.tsx +233 -0
  50. package/lib/aui/tools/index.ts +8 -0
  51. package/lib/aui/tools/navigation-tools.tsx +172 -0
  52. package/lib/aui/tools/notification-tools.ts +213 -0
  53. package/lib/aui/tools/state-tools.tsx +209 -0
  54. package/lib/aui/types.ts +47 -0
  55. package/lib/aui/vercel-ai.ts +100 -0
  56. package/package.json +51 -0
@@ -0,0 +1,353 @@
1
+ import { ReactElement } from 'react';
2
+ import { z } from 'zod';
3
+ import { AUITool, AUIContext } from './core';
4
+
5
+ export interface AIControlOptions {
6
+ permissions?: {
7
+ allowClientExecution?: boolean;
8
+ allowServerExecution?: boolean;
9
+ allowDatabaseAccess?: boolean;
10
+ allowFileSystemAccess?: boolean;
11
+ allowNetworkRequests?: boolean;
12
+ };
13
+ rateLimit?: {
14
+ requestsPerMinute?: number;
15
+ requestsPerHour?: number;
16
+ };
17
+ audit?: boolean;
18
+ sandbox?: boolean;
19
+ }
20
+
21
+ export class AIControlledTool<TInput = any, TOutput = any> extends AUITool<TInput, TOutput> {
22
+ private controlOptions: AIControlOptions = {};
23
+ private executionLog: Array<{
24
+ timestamp: Date;
25
+ input: TInput;
26
+ output?: TOutput;
27
+ error?: Error;
28
+ context?: Partial<AUIContext>;
29
+ }> = [];
30
+
31
+ constructor(name: string, options?: AIControlOptions) {
32
+ super(name);
33
+ this.controlOptions = options || {};
34
+ }
35
+
36
+ // Override methods to maintain correct type
37
+ input<T>(schema: z.ZodType<T>): AIControlledTool<T, TOutput> {
38
+ super.input(schema);
39
+ return this as any;
40
+ }
41
+
42
+ execute<O>(handler: (params: { input: TInput; ctx?: AUIContext }) => O | Promise<O>): AIControlledTool<TInput, O> {
43
+ super.execute(handler);
44
+ return this as any;
45
+ }
46
+
47
+ clientExecute(handler: (params: { input: TInput; ctx: AUIContext }) => TOutput | Promise<TOutput>): this {
48
+ super.clientExecute(handler);
49
+ return this;
50
+ }
51
+
52
+ render(component: (props: { data: TOutput; input?: TInput; loading?: boolean; error?: Error }) => ReactElement): this {
53
+ super.render(component);
54
+ return this;
55
+ }
56
+
57
+ middleware(fn: (params: { input: TInput; ctx: AUIContext; next: () => Promise<TOutput> }) => Promise<TOutput>): this {
58
+ super.middleware(fn);
59
+ return this;
60
+ }
61
+
62
+ describe(description: string): this {
63
+ super.describe(description);
64
+ return this;
65
+ }
66
+
67
+ tag(...tags: string[]): this {
68
+ super.tag(...tags);
69
+ return this;
70
+ }
71
+
72
+ withPermissions(permissions: AIControlOptions['permissions']): this {
73
+ this.controlOptions.permissions = {
74
+ ...this.controlOptions.permissions,
75
+ ...permissions
76
+ };
77
+ return this;
78
+ }
79
+
80
+ withRateLimit(limit: AIControlOptions['rateLimit']): this {
81
+ this.controlOptions.rateLimit = limit;
82
+ return this;
83
+ }
84
+
85
+ enableAudit(): this {
86
+ this.controlOptions.audit = true;
87
+ return this;
88
+ }
89
+
90
+ enableSandbox(): this {
91
+ this.controlOptions.sandbox = true;
92
+ return this;
93
+ }
94
+
95
+ async run(input: TInput, ctx?: AUIContext): Promise<TOutput> {
96
+ const startTime = Date.now();
97
+
98
+ try {
99
+ this.validatePermissions(ctx);
100
+ await this.checkRateLimit();
101
+
102
+ const result = await super.run(input, ctx);
103
+
104
+ if (this.controlOptions.audit) {
105
+ this.executionLog.push({
106
+ timestamp: new Date(),
107
+ input,
108
+ output: result,
109
+ context: ctx ? { user: ctx.user, session: ctx.session } : undefined
110
+ });
111
+ }
112
+
113
+ return result;
114
+ } catch (error) {
115
+ if (this.controlOptions.audit) {
116
+ this.executionLog.push({
117
+ timestamp: new Date(),
118
+ input,
119
+ error: error as Error,
120
+ context: ctx ? { user: ctx.user, session: ctx.session } : undefined
121
+ });
122
+ }
123
+ throw error;
124
+ }
125
+ }
126
+
127
+ private validatePermissions(ctx?: AUIContext): void {
128
+ if (!this.controlOptions.permissions) return;
129
+
130
+ const isClient = !ctx?.isServer;
131
+ const perms = this.controlOptions.permissions;
132
+
133
+ if (isClient && !perms.allowClientExecution) {
134
+ throw new Error('Client execution not allowed for this tool');
135
+ }
136
+
137
+ if (ctx?.isServer && !perms.allowServerExecution) {
138
+ throw new Error('Server execution not allowed for this tool');
139
+ }
140
+ }
141
+
142
+ private async checkRateLimit(): Promise<void> {
143
+ if (!this.controlOptions.rateLimit) return;
144
+
145
+ const now = Date.now();
146
+ const { requestsPerMinute, requestsPerHour } = this.controlOptions.rateLimit;
147
+
148
+ if (requestsPerMinute) {
149
+ const recentMinute = this.executionLog.filter(
150
+ log => now - log.timestamp.getTime() < 60000
151
+ );
152
+ if (recentMinute.length >= requestsPerMinute) {
153
+ throw new Error('Rate limit exceeded (per minute)');
154
+ }
155
+ }
156
+
157
+ if (requestsPerHour) {
158
+ const recentHour = this.executionLog.filter(
159
+ log => now - log.timestamp.getTime() < 3600000
160
+ );
161
+ if (recentHour.length >= requestsPerHour) {
162
+ throw new Error('Rate limit exceeded (per hour)');
163
+ }
164
+ }
165
+ }
166
+
167
+ getExecutionLog() {
168
+ return [...this.executionLog];
169
+ }
170
+
171
+ clearExecutionLog() {
172
+ this.executionLog = [];
173
+ }
174
+ }
175
+
176
+ export function createAITool(name: string, options?: AIControlOptions): AIControlledTool {
177
+ return new AIControlledTool(name, options);
178
+ }
179
+
180
+ export const aiTools = {
181
+ fileSystem: createAITool('fs-control', {
182
+ permissions: {
183
+ allowServerExecution: true,
184
+ allowFileSystemAccess: true
185
+ },
186
+ audit: true
187
+ })
188
+ .input(z.object({
189
+ action: z.enum(['read', 'write', 'delete', 'list']),
190
+ path: z.string(),
191
+ content: z.string().optional()
192
+ }))
193
+ .execute(async ({ input }) => {
194
+ switch (input.action) {
195
+ case 'read':
196
+ return { content: 'File content here' };
197
+ case 'write':
198
+ return { success: true, path: input.path };
199
+ case 'delete':
200
+ return { success: true, deleted: input.path };
201
+ case 'list':
202
+ return { files: ['file1.txt', 'file2.txt'] };
203
+ default:
204
+ throw new Error('Unknown action');
205
+ }
206
+ }),
207
+
208
+ database: createAITool('db-control', {
209
+ permissions: {
210
+ allowServerExecution: true,
211
+ allowDatabaseAccess: true
212
+ },
213
+ rateLimit: {
214
+ requestsPerMinute: 30,
215
+ requestsPerHour: 500
216
+ },
217
+ audit: true
218
+ })
219
+ .input(z.object({
220
+ query: z.string(),
221
+ params: z.array(z.any()).optional()
222
+ }))
223
+ .execute(async ({ input }) => {
224
+ return {
225
+ rows: [],
226
+ query: input.query,
227
+ executed: new Date().toISOString()
228
+ };
229
+ }),
230
+
231
+ uiManipulation: createAITool('ui-control', {
232
+ permissions: {
233
+ allowClientExecution: true
234
+ },
235
+ audit: true
236
+ })
237
+ .input(z.object({
238
+ selector: z.string(),
239
+ action: z.enum(['click', 'type', 'focus', 'scroll']),
240
+ value: z.string().optional()
241
+ }))
242
+ .clientExecute(async ({ input }) => {
243
+ const element = document.querySelector(input.selector);
244
+ if (!element) throw new Error(`Element not found: ${input.selector}`);
245
+
246
+ switch (input.action) {
247
+ case 'click':
248
+ (element as HTMLElement).click();
249
+ break;
250
+ case 'type':
251
+ if (element instanceof HTMLInputElement && input.value) {
252
+ element.value = input.value;
253
+ element.dispatchEvent(new Event('input', { bubbles: true }));
254
+ }
255
+ break;
256
+ case 'focus':
257
+ (element as HTMLElement).focus();
258
+ break;
259
+ case 'scroll':
260
+ element.scrollIntoView({ behavior: 'smooth' });
261
+ break;
262
+ }
263
+
264
+ return { success: true, action: input.action, selector: input.selector };
265
+ }),
266
+
267
+ apiCall: createAITool('api-control', {
268
+ permissions: {
269
+ allowNetworkRequests: true
270
+ },
271
+ rateLimit: {
272
+ requestsPerMinute: 20
273
+ },
274
+ audit: true
275
+ })
276
+ .input(z.object({
277
+ url: z.string().url(),
278
+ method: z.enum(['GET', 'POST', 'PUT', 'DELETE']),
279
+ headers: z.record(z.string()).optional(),
280
+ body: z.any().optional()
281
+ }))
282
+ .execute(async ({ input, ctx }) => {
283
+ const response = await ctx!.fetch(input.url, {
284
+ method: input.method,
285
+ headers: input.headers,
286
+ body: input.body ? JSON.stringify(input.body) : undefined
287
+ });
288
+
289
+ return {
290
+ status: response.status,
291
+ data: await response.json().catch(() => null),
292
+ headers: Object.fromEntries(response.headers.entries())
293
+ };
294
+ })
295
+ };
296
+
297
+ export function createAIControlSystem() {
298
+ const tools = new Map<string, AIControlledTool>();
299
+
300
+ return {
301
+ register(tool: AIControlledTool) {
302
+ tools.set(tool.name, tool);
303
+ return this;
304
+ },
305
+
306
+ get(name: string) {
307
+ return tools.get(name);
308
+ },
309
+
310
+ clear() {
311
+ tools.clear();
312
+ },
313
+
314
+ async execute(name: string, input: any, ctx?: AUIContext) {
315
+ const tool = tools.get(name);
316
+ if (!tool) throw new Error(`Tool ${name} not found`);
317
+ return await tool.run(input, ctx);
318
+ },
319
+
320
+ getAuditLog(toolName?: string) {
321
+ if (toolName) {
322
+ const tool = tools.get(toolName);
323
+ return tool ? tool.getExecutionLog() : [];
324
+ }
325
+
326
+ const allLogs: any[] = [];
327
+ tools.forEach((tool, name) => {
328
+ const logs = tool.getExecutionLog();
329
+ logs.forEach(log => allLogs.push({ tool: name, ...log }));
330
+ });
331
+ return allLogs.sort((a, b) =>
332
+ b.timestamp.getTime() - a.timestamp.getTime()
333
+ );
334
+ },
335
+
336
+ listTools() {
337
+ return Array.from(tools.values()).map(tool => ({
338
+ name: tool.name,
339
+ description: tool.description,
340
+ tags: tool.tags,
341
+ config: tool.toJSON()
342
+ }));
343
+ }
344
+ };
345
+ }
346
+
347
+ export const aiControlSystem = createAIControlSystem();
348
+
349
+ Object.values(aiTools).forEach(tool => {
350
+ aiControlSystem.register(tool);
351
+ });
352
+
353
+ export default aiControlSystem;
@@ -0,0 +1,55 @@
1
+ 'use client';
2
+
3
+ import { useState, useCallback } from 'react';
4
+ import { AUITool, AUIContext } from '../index';
5
+
6
+ interface UseAUIOptions {
7
+ cache?: boolean;
8
+ context?: Partial<AUIContext>;
9
+ }
10
+
11
+ interface UseAUIResult<TInput, TOutput> {
12
+ execute: (input: TInput) => Promise<void>;
13
+ data: TOutput | null;
14
+ loading: boolean;
15
+ error: Error | null;
16
+ reset: () => void;
17
+ }
18
+
19
+ export function useAUI<TInput = any, TOutput = any>(
20
+ tool: AUITool<TInput, TOutput>,
21
+ options: UseAUIOptions = {}
22
+ ): UseAUIResult<TInput, TOutput> {
23
+ const [data, setData] = useState<TOutput | null>(null);
24
+ const [loading, setLoading] = useState(false);
25
+ const [error, setError] = useState<Error | null>(null);
26
+
27
+ const execute = useCallback(async (input: TInput) => {
28
+ setLoading(true);
29
+ setError(null);
30
+
31
+ try {
32
+ const context: AUIContext = {
33
+ cache: options.cache ? new Map() : new Map(),
34
+ fetch: globalThis.fetch,
35
+ isServer: false,
36
+ ...options.context
37
+ };
38
+
39
+ const result = await tool.run(input, context);
40
+ setData(result);
41
+ } catch (err) {
42
+ setError(err instanceof Error ? err : new Error('Unknown error'));
43
+ } finally {
44
+ setLoading(false);
45
+ }
46
+ }, [tool, options.cache, options.context]);
47
+
48
+ const reset = useCallback(() => {
49
+ setData(null);
50
+ setError(null);
51
+ setLoading(false);
52
+ }, []);
53
+
54
+ return { execute, data, loading, error, reset };
55
+ }