@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
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Lyndon Leong
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
# Better UI - AUI (Assistant-UI) System
|
|
2
|
+
|
|
3
|
+
A concise and elegant tool system for Next.js that enables AI assistants to control both frontend and backend through a fluent API.
|
|
4
|
+
|
|
5
|
+
## 🚀 Quick Start
|
|
6
|
+
|
|
7
|
+
```tsx
|
|
8
|
+
// Simple tool - just 2 methods
|
|
9
|
+
const simpleTool = aui
|
|
10
|
+
.tool('weather')
|
|
11
|
+
.input(z.object({ city: z.string() }))
|
|
12
|
+
.execute(async ({ input }) => ({ temp: 72, city: input.city }))
|
|
13
|
+
.render(({ data }) => <div>{data.city}: {data.temp}°</div>);
|
|
14
|
+
|
|
15
|
+
// Complex tool - adds client optimization
|
|
16
|
+
const complexTool = aui
|
|
17
|
+
.tool('search')
|
|
18
|
+
.input(z.object({ query: z.string() }))
|
|
19
|
+
.execute(async ({ input }) => db.search(input.query))
|
|
20
|
+
.clientExecute(async ({ input, ctx }) => {
|
|
21
|
+
// Only when you need caching, offline, etc.
|
|
22
|
+
const cached = ctx.cache.get(input.query);
|
|
23
|
+
return cached || ctx.fetch('/api/tools/search', { body: input });
|
|
24
|
+
})
|
|
25
|
+
.render(({ data }) => <SearchResults results={data} />);
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## ✨ Features
|
|
29
|
+
|
|
30
|
+
- **Fluent API**: Chain methods for clean, readable tool definitions
|
|
31
|
+
- **Type Safety**: Full TypeScript support with Zod schemas
|
|
32
|
+
- **Minimal Boilerplate**: Only `input()` and `execute()` are required
|
|
33
|
+
- **Dual Execution**: Server-side execution with optional client-side optimization
|
|
34
|
+
- **React Integration**: Seamless rendering of tool results
|
|
35
|
+
- **AI-Ready**: Designed for AI assistants to control UI/backend
|
|
36
|
+
|
|
37
|
+
## 🏗️ Architecture
|
|
38
|
+
|
|
39
|
+
```
|
|
40
|
+
┌─────────────┐ ┌──────────────┐ ┌─────────────┐
|
|
41
|
+
│ Client │────▶│ AUI System │────▶│ Server │
|
|
42
|
+
│ Component │ │ │ │ Handler │
|
|
43
|
+
└─────────────┘ └──────────────┘ └─────────────┘
|
|
44
|
+
│ │ │
|
|
45
|
+
▼ ▼ ▼
|
|
46
|
+
ToolRenderer ClientExecutor ToolExecutor
|
|
47
|
+
(optional) (server-side)
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## 📦 Installation
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
npm install
|
|
54
|
+
npm run dev
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## 🛠️ API Reference
|
|
58
|
+
|
|
59
|
+
### Tool Builder
|
|
60
|
+
|
|
61
|
+
- `.tool(name)` - Create a new tool builder
|
|
62
|
+
- `.input(schema)` - Define input validation with Zod
|
|
63
|
+
- `.execute(handler)` - Server-side execution logic
|
|
64
|
+
- `.clientExecute(handler)` - Optional client-side execution
|
|
65
|
+
- `.render(component)` - React component for rendering results
|
|
66
|
+
- `.description(text)` - Optional tool description
|
|
67
|
+
- Returns a built tool object ready to use (no build step needed)
|
|
68
|
+
|
|
69
|
+
### React Components
|
|
70
|
+
|
|
71
|
+
```tsx
|
|
72
|
+
import { ToolExecutorProvider, ToolRenderer, useToolExecutor } from '@/lib/aui/client';
|
|
73
|
+
|
|
74
|
+
// Provider setup
|
|
75
|
+
<ToolExecutorProvider tools={[weatherTool, searchTool]}>
|
|
76
|
+
<App />
|
|
77
|
+
</ToolExecutorProvider>
|
|
78
|
+
|
|
79
|
+
// Render a tool result
|
|
80
|
+
<ToolRenderer toolCall={toolCall} tool={weatherTool} />
|
|
81
|
+
|
|
82
|
+
// Hook usage
|
|
83
|
+
const executor = useToolExecutor();
|
|
84
|
+
const result = await executor.execute(toolCall);
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## 🔧 Usage Examples
|
|
88
|
+
|
|
89
|
+
### Basic Tool
|
|
90
|
+
```tsx
|
|
91
|
+
const weatherTool = aui
|
|
92
|
+
.tool('weather')
|
|
93
|
+
.input(z.object({ city: z.string() }))
|
|
94
|
+
.execute(async ({ input }) => {
|
|
95
|
+
const response = await fetch(`/api/weather?city=${input.city}`);
|
|
96
|
+
return response.json();
|
|
97
|
+
})
|
|
98
|
+
.render(({ data }) => (
|
|
99
|
+
<div className="weather-card">
|
|
100
|
+
<h3>{data.city}</h3>
|
|
101
|
+
<p>{data.temp}°F</p>
|
|
102
|
+
</div>
|
|
103
|
+
));
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### Tool with Client Optimization
|
|
107
|
+
```tsx
|
|
108
|
+
const searchTool = aui
|
|
109
|
+
.tool('search')
|
|
110
|
+
.input(z.object({ query: z.string() }))
|
|
111
|
+
.execute(async ({ input }) => {
|
|
112
|
+
return await database.search(input.query);
|
|
113
|
+
})
|
|
114
|
+
.clientExecute(async ({ input, ctx }) => {
|
|
115
|
+
// Client-side caching and offline support
|
|
116
|
+
const cached = ctx.cache.get(input.query);
|
|
117
|
+
if (cached) return cached;
|
|
118
|
+
|
|
119
|
+
const result = await ctx.fetch('/api/aui', {
|
|
120
|
+
method: 'POST',
|
|
121
|
+
body: JSON.stringify({
|
|
122
|
+
toolCall: { toolName: 'search', input }
|
|
123
|
+
})
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
ctx.cache.set(input.query, result);
|
|
127
|
+
return result;
|
|
128
|
+
})
|
|
129
|
+
.render(({ data }) => <SearchResults results={data} />);
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
## 🗂️ Project Structure
|
|
133
|
+
|
|
134
|
+
```
|
|
135
|
+
better-ui/
|
|
136
|
+
├── app/ # Next.js app directory
|
|
137
|
+
│ ├── api/ # API routes
|
|
138
|
+
│ └── aui-demo/ # Demo pages
|
|
139
|
+
├── lib/aui/ # AUI system core
|
|
140
|
+
│ ├── core/ # Core builder and registry
|
|
141
|
+
│ ├── client/ # Client-side execution
|
|
142
|
+
│ ├── server/ # Server-side execution
|
|
143
|
+
│ ├── tools/ # Example tools
|
|
144
|
+
│ └── types/ # TypeScript definitions
|
|
145
|
+
├── examples/ # Usage examples
|
|
146
|
+
└── agent/ # Development metadata
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
## 🧪 Testing
|
|
150
|
+
|
|
151
|
+
```bash
|
|
152
|
+
npm test
|
|
153
|
+
npm run type-check
|
|
154
|
+
npm run lint
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
## 🚀 Development
|
|
158
|
+
|
|
159
|
+
```bash
|
|
160
|
+
# Start development server
|
|
161
|
+
npm run dev
|
|
162
|
+
|
|
163
|
+
# Build for production
|
|
164
|
+
npm run build
|
|
165
|
+
|
|
166
|
+
# Start production server
|
|
167
|
+
npm start
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
## 📚 Documentation
|
|
171
|
+
|
|
172
|
+
- [AUI System Overview](AUI.md) - Detailed system documentation
|
|
173
|
+
- [Examples](./examples/) - Complete usage examples
|
|
174
|
+
- [API Reference](./lib/aui/README.md) - Core API documentation
|
|
175
|
+
|
|
176
|
+
## 🤝 Contributing
|
|
177
|
+
|
|
178
|
+
1. Fork the repository
|
|
179
|
+
2. Create a feature branch
|
|
180
|
+
3. Make your changes
|
|
181
|
+
4. Add tests
|
|
182
|
+
5. Submit a pull request
|
|
183
|
+
|
|
184
|
+
## 📄 License
|
|
185
|
+
|
|
186
|
+
This project is private and proprietary.
|
|
187
|
+
|
|
188
|
+
---
|
|
189
|
+
|
|
190
|
+
Built with ❤️ using Next.js, TypeScript, and Zod.
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
# AUI (Assistant-UI) System
|
|
2
|
+
|
|
3
|
+
A concise, type-safe tool system for AI assistants to control frontend and backend operations in Next.js/Vercel applications.
|
|
4
|
+
|
|
5
|
+
## Quick Start
|
|
6
|
+
|
|
7
|
+
```tsx
|
|
8
|
+
import aui from '@/lib/aui';
|
|
9
|
+
import { z } from 'zod';
|
|
10
|
+
|
|
11
|
+
// Simple tool - just 2 methods
|
|
12
|
+
const simpleTool = aui
|
|
13
|
+
.tool('weather')
|
|
14
|
+
.input(z.object({ city: z.string() }))
|
|
15
|
+
.execute(async ({ input }) => ({ temp: 72, city: input.city }))
|
|
16
|
+
.render(({ data }) => <div>{data.city}: {data.temp}°</div>);
|
|
17
|
+
|
|
18
|
+
// Complex tool - adds client optimization
|
|
19
|
+
const complexTool = aui
|
|
20
|
+
.tool('search')
|
|
21
|
+
.input(z.object({ query: z.string() }))
|
|
22
|
+
.execute(async ({ input }) => db.search(input.query))
|
|
23
|
+
.clientExecute(async ({ input, ctx }) => {
|
|
24
|
+
// Only when you need caching, offline, etc.
|
|
25
|
+
const cached = ctx.cache.get(input.query);
|
|
26
|
+
return cached || ctx.fetch('/api/tools/search', { body: input });
|
|
27
|
+
})
|
|
28
|
+
.render(({ data }) => <SearchResults results={data} />);
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Core Features
|
|
32
|
+
|
|
33
|
+
- **Concise API**: Define tools in 2-4 method calls
|
|
34
|
+
- **Type Safety**: Full TypeScript support with Zod validation
|
|
35
|
+
- **Dual Execution**: Run on server (default) or client (optimized)
|
|
36
|
+
- **React Integration**: Built-in rendering and hooks
|
|
37
|
+
- **Context Management**: Caching, sessions, and state
|
|
38
|
+
- **AI-Ready**: Designed for AI assistant control
|
|
39
|
+
|
|
40
|
+
## API Methods
|
|
41
|
+
|
|
42
|
+
### Core Methods
|
|
43
|
+
- `.tool(name)` - Create a new tool
|
|
44
|
+
- `.input(schema)` - Define input validation with Zod
|
|
45
|
+
- `.execute(handler)` - Server-side execution logic
|
|
46
|
+
- `.render(component)` - React component for rendering results
|
|
47
|
+
|
|
48
|
+
### Optional Methods
|
|
49
|
+
- `.clientExecute(handler)` - Client-side execution with caching
|
|
50
|
+
- `.middleware(fn)` - Add middleware for auth, logging, etc.
|
|
51
|
+
- `.describe(text)` - Add description for documentation
|
|
52
|
+
- `.tag(...tags)` - Add tags for organization
|
|
53
|
+
|
|
54
|
+
## Examples
|
|
55
|
+
|
|
56
|
+
See `/lib/aui/examples/` for complete examples including:
|
|
57
|
+
- Weather tool (simple)
|
|
58
|
+
- Search tool (with caching)
|
|
59
|
+
- Calculator tool (with validation)
|
|
60
|
+
- Form tool (with middleware)
|
|
61
|
+
- Analytics tool (complex visualization)
|
|
62
|
+
|
|
63
|
+
## Using Tools in React
|
|
64
|
+
|
|
65
|
+
```tsx
|
|
66
|
+
import { useAUITool, AUIProvider } from '@/lib/aui';
|
|
67
|
+
|
|
68
|
+
function MyComponent() {
|
|
69
|
+
const { execute, data, loading, error } = useAUITool(weatherTool);
|
|
70
|
+
|
|
71
|
+
return (
|
|
72
|
+
<div>
|
|
73
|
+
<button onClick={() => execute({ city: 'NYC' })}>
|
|
74
|
+
Get Weather
|
|
75
|
+
</button>
|
|
76
|
+
{loading && <div>Loading...</div>}
|
|
77
|
+
{data && weatherTool.renderer({ data })}
|
|
78
|
+
</div>
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Wrap your app with AUIProvider
|
|
83
|
+
export default function App() {
|
|
84
|
+
return (
|
|
85
|
+
<AUIProvider>
|
|
86
|
+
<MyComponent />
|
|
87
|
+
</AUIProvider>
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## AI Control Example
|
|
93
|
+
|
|
94
|
+
Enable AI assistants to control both frontend and backend:
|
|
95
|
+
|
|
96
|
+
```tsx
|
|
97
|
+
const databaseTool = aui
|
|
98
|
+
.tool('database')
|
|
99
|
+
.input(z.object({
|
|
100
|
+
table: z.string(),
|
|
101
|
+
operation: z.enum(['select', 'insert', 'update', 'delete']),
|
|
102
|
+
conditions: z.record(z.any()).optional()
|
|
103
|
+
}))
|
|
104
|
+
.execute(async ({ input }) => {
|
|
105
|
+
// Server-side database operations
|
|
106
|
+
return await db[input.operation](input.table, input.conditions);
|
|
107
|
+
})
|
|
108
|
+
.clientExecute(async ({ input, ctx }) => {
|
|
109
|
+
// Client-side optimistic updates
|
|
110
|
+
if (input.operation === 'select') {
|
|
111
|
+
const cached = ctx.cache.get(`${input.table}:${input.operation}`);
|
|
112
|
+
if (cached) return cached;
|
|
113
|
+
}
|
|
114
|
+
return ctx.fetch('/api/database', { body: input });
|
|
115
|
+
});
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
## AI Control Examples
|
|
119
|
+
|
|
120
|
+
The system includes comprehensive examples for AI control in `/lib/aui/examples/ai-control-full.tsx`:
|
|
121
|
+
|
|
122
|
+
- **Database Operations**: Query and manipulate data
|
|
123
|
+
- **UI Control**: Manage modals, navigation, themes
|
|
124
|
+
- **File System**: Read/write files (server-side)
|
|
125
|
+
- **API Integration**: Call external APIs with CORS proxy
|
|
126
|
+
- **Real-time Streams**: WebSocket/SSE connections
|
|
127
|
+
- **Form Generation**: Dynamic form creation
|
|
128
|
+
- **Analytics**: Event tracking and analysis
|
|
129
|
+
|
|
130
|
+
## Testing
|
|
131
|
+
|
|
132
|
+
```bash
|
|
133
|
+
npm test -- --testPathPattern=aui
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
All 63 tests passing ✅
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach } from '@jest/globals';
|
|
2
|
+
import aui, { z } from '../index';
|
|
3
|
+
import { AIControlledTool, createAITool, aiControlSystem } from '../ai-control';
|
|
4
|
+
import { clientControlSystem } from '../client-control';
|
|
5
|
+
|
|
6
|
+
describe('AUI Complete System Tests', () => {
|
|
7
|
+
beforeEach(() => {
|
|
8
|
+
aui.clear();
|
|
9
|
+
aiControlSystem.clear();
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
describe('Simple Tool Pattern', () => {
|
|
13
|
+
it('should create a simple tool with execute and render', async () => {
|
|
14
|
+
const simpleTool = aui
|
|
15
|
+
.tool('weather')
|
|
16
|
+
.input(z.object({ city: z.string() }))
|
|
17
|
+
.execute(async ({ input }) => ({ temp: 72, city: input.city }))
|
|
18
|
+
.render(({ data }) => `${data.city}: ${data.temp}°` as any);
|
|
19
|
+
|
|
20
|
+
expect(simpleTool.name).toBe('weather');
|
|
21
|
+
|
|
22
|
+
const result = await simpleTool.run({ city: 'New York' });
|
|
23
|
+
expect(result).toEqual({ temp: 72, city: 'New York' });
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it('should validate input schema', async () => {
|
|
27
|
+
const tool = aui
|
|
28
|
+
.tool('validated')
|
|
29
|
+
.input(z.object({
|
|
30
|
+
required: z.string(),
|
|
31
|
+
optional: z.number().optional()
|
|
32
|
+
}))
|
|
33
|
+
.execute(async ({ input }) => input);
|
|
34
|
+
|
|
35
|
+
await expect(tool.run({ required: 'test' })).resolves.toEqual({ required: 'test' });
|
|
36
|
+
await expect(tool.run({ missing: 'field' } as any)).rejects.toThrow();
|
|
37
|
+
});
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
describe('Complex Tool Pattern', () => {
|
|
41
|
+
it('should support client and server execution', async () => {
|
|
42
|
+
const complexTool = aui
|
|
43
|
+
.tool('search')
|
|
44
|
+
.input(z.object({ query: z.string() }))
|
|
45
|
+
.execute(async ({ input }) => ({
|
|
46
|
+
results: [`server: ${input.query}`]
|
|
47
|
+
}))
|
|
48
|
+
.clientExecute(async ({ input, ctx }) => {
|
|
49
|
+
const cached = ctx.cache.get(input.query);
|
|
50
|
+
if (cached) return cached;
|
|
51
|
+
|
|
52
|
+
const result = { results: [`client: ${input.query}`] };
|
|
53
|
+
ctx.cache.set(input.query, result);
|
|
54
|
+
return result;
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
// Server execution
|
|
58
|
+
const serverResult = await complexTool.run(
|
|
59
|
+
{ query: 'test' },
|
|
60
|
+
{ isServer: true, cache: new Map(), fetch: fetch }
|
|
61
|
+
);
|
|
62
|
+
expect(serverResult.results[0]).toContain('server');
|
|
63
|
+
|
|
64
|
+
// Client execution
|
|
65
|
+
const clientResult = await complexTool.run(
|
|
66
|
+
{ query: 'test' },
|
|
67
|
+
{ isServer: false, cache: new Map(), fetch: fetch }
|
|
68
|
+
);
|
|
69
|
+
expect(clientResult.results[0]).toContain('client');
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it('should support middleware', async () => {
|
|
73
|
+
const middlewareTool = aui
|
|
74
|
+
.tool('with-middleware')
|
|
75
|
+
.input(z.object({ value: z.number() }))
|
|
76
|
+
.execute(async ({ input }) => ({ result: input.value }))
|
|
77
|
+
.middleware(async ({ input, next }) => {
|
|
78
|
+
input.value = input.value * 2;
|
|
79
|
+
const result = await next();
|
|
80
|
+
result.result = result.result + 10;
|
|
81
|
+
return result;
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
const result = await middlewareTool.run({ value: 5 });
|
|
85
|
+
expect(result).toEqual({ result: 20 }); // (5 * 2) + 10
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
describe('AI Control System', () => {
|
|
90
|
+
it('should create AI-controlled tools with permissions', () => {
|
|
91
|
+
const aiTool = createAITool('ai-tool', {
|
|
92
|
+
permissions: {
|
|
93
|
+
allowClientExecution: true,
|
|
94
|
+
allowServerExecution: false
|
|
95
|
+
}
|
|
96
|
+
})
|
|
97
|
+
.input(z.object({ action: z.string() }))
|
|
98
|
+
.execute(async ({ input }) => ({ executed: input.action }));
|
|
99
|
+
|
|
100
|
+
expect(aiTool).toBeInstanceOf(AIControlledTool);
|
|
101
|
+
expect(aiTool.name).toBe('ai-tool');
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it('should enforce rate limiting', async () => {
|
|
105
|
+
const rateLimitedTool = createAITool('rate-limited', {
|
|
106
|
+
rateLimit: {
|
|
107
|
+
requestsPerMinute: 2
|
|
108
|
+
},
|
|
109
|
+
audit: true // Enable audit to track execution for rate limiting
|
|
110
|
+
})
|
|
111
|
+
.input(z.object({ id: z.number() }))
|
|
112
|
+
.execute(async ({ input }) => ({ id: input.id }));
|
|
113
|
+
|
|
114
|
+
// First two requests should succeed
|
|
115
|
+
await expect(rateLimitedTool.run({ id: 1 })).resolves.toEqual({ id: 1 });
|
|
116
|
+
await expect(rateLimitedTool.run({ id: 2 })).resolves.toEqual({ id: 2 });
|
|
117
|
+
|
|
118
|
+
// Third request should fail
|
|
119
|
+
await expect(rateLimitedTool.run({ id: 3 })).rejects.toThrow('Rate limit exceeded');
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
it('should track execution with audit', async () => {
|
|
123
|
+
const auditedTool = createAITool('audited', { audit: true })
|
|
124
|
+
.input(z.object({ action: z.string() }))
|
|
125
|
+
.execute(async ({ input }) => ({ result: input.action }));
|
|
126
|
+
|
|
127
|
+
await auditedTool.run({ action: 'test1' });
|
|
128
|
+
await auditedTool.run({ action: 'test2' });
|
|
129
|
+
|
|
130
|
+
const log = auditedTool.getExecutionLog();
|
|
131
|
+
expect(log).toHaveLength(2);
|
|
132
|
+
expect(log[0].input).toEqual({ action: 'test1' });
|
|
133
|
+
expect(log[1].input).toEqual({ action: 'test2' });
|
|
134
|
+
});
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
describe('Tool Registry', () => {
|
|
138
|
+
it('should register and retrieve tools', () => {
|
|
139
|
+
const tool1 = aui.tool('tool1')
|
|
140
|
+
.input(z.object({ x: z.number() }))
|
|
141
|
+
.execute(async ({ input }) => input);
|
|
142
|
+
|
|
143
|
+
const tool2 = aui.tool('tool2')
|
|
144
|
+
.input(z.object({ y: z.string() }))
|
|
145
|
+
.execute(async ({ input }) => input);
|
|
146
|
+
|
|
147
|
+
expect(aui.has('tool1')).toBe(true);
|
|
148
|
+
expect(aui.has('tool2')).toBe(true);
|
|
149
|
+
expect(aui.getToolNames()).toEqual(['tool1', 'tool2']);
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
it('should support tool tags and discovery', () => {
|
|
153
|
+
const apiTool = aui.tool('api')
|
|
154
|
+
.tag('backend', 'api', 'rest')
|
|
155
|
+
.execute(async () => ({ success: true }));
|
|
156
|
+
|
|
157
|
+
const uiTool = aui.tool('ui')
|
|
158
|
+
.tag('frontend', 'ui', 'dom')
|
|
159
|
+
.execute(async () => ({ rendered: true }));
|
|
160
|
+
|
|
161
|
+
expect(aui.findByTag('backend')).toContain(apiTool);
|
|
162
|
+
expect(aui.findByTag('frontend')).toContain(uiTool);
|
|
163
|
+
expect(aui.findByTags('backend', 'api')).toContain(apiTool);
|
|
164
|
+
});
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
describe('Tool Execution', () => {
|
|
168
|
+
it('should execute tools through AUI instance', async () => {
|
|
169
|
+
aui.tool('executor')
|
|
170
|
+
.input(z.object({ value: z.number() }))
|
|
171
|
+
.execute(async ({ input }) => ({ doubled: input.value * 2 }));
|
|
172
|
+
|
|
173
|
+
const result = await aui.execute('executor', { value: 5 });
|
|
174
|
+
expect(result).toEqual({ doubled: 10 });
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
it('should handle execution errors', async () => {
|
|
178
|
+
aui.tool('error-tool')
|
|
179
|
+
.execute(async () => {
|
|
180
|
+
throw new Error('Execution failed');
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
await expect(aui.execute('error-tool', {})).rejects.toThrow('Execution failed');
|
|
184
|
+
});
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
describe('Client Control System', () => {
|
|
188
|
+
it('should list available client tools', () => {
|
|
189
|
+
const tools = clientControlSystem.listClientTools();
|
|
190
|
+
|
|
191
|
+
expect(tools).toContainEqual(
|
|
192
|
+
expect.objectContaining({
|
|
193
|
+
name: 'client-dom',
|
|
194
|
+
description: 'Manipulate DOM elements on the client'
|
|
195
|
+
})
|
|
196
|
+
);
|
|
197
|
+
|
|
198
|
+
expect(tools).toContainEqual(
|
|
199
|
+
expect.objectContaining({
|
|
200
|
+
name: 'client-forms',
|
|
201
|
+
description: 'Control and interact with forms'
|
|
202
|
+
})
|
|
203
|
+
);
|
|
204
|
+
});
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
describe('Tool Serialization', () => {
|
|
208
|
+
it('should serialize tool configuration', () => {
|
|
209
|
+
const tool = aui.tool('serializable')
|
|
210
|
+
.describe('A test tool')
|
|
211
|
+
.tag('test', 'demo')
|
|
212
|
+
.input(z.object({ x: z.number() }))
|
|
213
|
+
.execute(async ({ input }) => input)
|
|
214
|
+
.clientExecute(async ({ input }) => input)
|
|
215
|
+
.render(() => null as any);
|
|
216
|
+
|
|
217
|
+
const json = tool.toJSON();
|
|
218
|
+
|
|
219
|
+
expect(json).toEqual({
|
|
220
|
+
name: 'serializable',
|
|
221
|
+
description: 'A test tool',
|
|
222
|
+
tags: ['test', 'demo'],
|
|
223
|
+
hasInput: true,
|
|
224
|
+
hasExecute: true,
|
|
225
|
+
hasClientExecute: true,
|
|
226
|
+
hasRender: true,
|
|
227
|
+
hasMiddleware: false
|
|
228
|
+
});
|
|
229
|
+
});
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
describe('Context Management', () => {
|
|
233
|
+
it('should create and use context correctly', async () => {
|
|
234
|
+
const contextTool = aui.tool('context-test')
|
|
235
|
+
.execute(async ({ ctx }) => ({
|
|
236
|
+
isServer: ctx?.isServer,
|
|
237
|
+
hasCache: ctx?.cache instanceof Map,
|
|
238
|
+
hasFetch: typeof ctx?.fetch === 'function'
|
|
239
|
+
}));
|
|
240
|
+
|
|
241
|
+
const result = await contextTool.run({}, aui.createContext({
|
|
242
|
+
isServer: true,
|
|
243
|
+
user: { id: 1 }
|
|
244
|
+
}));
|
|
245
|
+
|
|
246
|
+
expect(result.isServer).toBe(true);
|
|
247
|
+
expect(result.hasCache).toBe(true);
|
|
248
|
+
expect(result.hasFetch).toBe(true);
|
|
249
|
+
});
|
|
250
|
+
});
|
|
251
|
+
});
|