create-theokit 1.0.5 → 1.0.6
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
CHANGED
|
@@ -2,9 +2,17 @@
|
|
|
2
2
|
|
|
3
3
|
import { useState, useEffect, useCallback, useRef, type FormEvent } from 'react'
|
|
4
4
|
|
|
5
|
-
interface Task {
|
|
5
|
+
interface Task {
|
|
6
|
+
id: number
|
|
7
|
+
title: string
|
|
8
|
+
priority: 'high' | 'medium' | 'low'
|
|
9
|
+
done: boolean
|
|
10
|
+
}
|
|
6
11
|
type Role = '' | 'user' | 'admin'
|
|
7
|
-
interface ChatMsg {
|
|
12
|
+
interface ChatMsg {
|
|
13
|
+
role: 'user' | 'agent' | 'tool' | 'system' | 'error'
|
|
14
|
+
text: string
|
|
15
|
+
}
|
|
8
16
|
|
|
9
17
|
export default function Page() {
|
|
10
18
|
const [tasks, setTasks] = useState<Task[]>([])
|
|
@@ -12,7 +20,9 @@ export default function Page() {
|
|
|
12
20
|
const [title, setTitle] = useState('')
|
|
13
21
|
const [priority, setPriority] = useState<Task['priority']>('medium')
|
|
14
22
|
const [formError, setFormError] = useState('')
|
|
15
|
-
const [chat, setChat] = useState<ChatMsg[]>([
|
|
23
|
+
const [chat, setChat] = useState<ChatMsg[]>([
|
|
24
|
+
{ role: 'system', text: 'Ask me to list, create, or complete tasks...' },
|
|
25
|
+
])
|
|
16
26
|
const [chatInput, setChatInput] = useState('')
|
|
17
27
|
const [chatBusy, setChatBusy] = useState(false)
|
|
18
28
|
const chatRef = useRef<HTMLDivElement>(null)
|
|
@@ -28,15 +38,28 @@ export default function Page() {
|
|
|
28
38
|
if (res.ok) setTasks(await res.json())
|
|
29
39
|
}, [])
|
|
30
40
|
|
|
31
|
-
useEffect(() => {
|
|
41
|
+
useEffect(() => {
|
|
42
|
+
loadTasks()
|
|
43
|
+
}, [loadTasks])
|
|
32
44
|
|
|
33
45
|
const createTask = async (e: FormEvent) => {
|
|
34
46
|
e.preventDefault()
|
|
35
47
|
setFormError('')
|
|
36
48
|
if (!title.trim()) return
|
|
37
|
-
const res = await fetch('/api/tasks', {
|
|
38
|
-
|
|
39
|
-
|
|
49
|
+
const res = await fetch('/api/tasks', {
|
|
50
|
+
method: 'POST',
|
|
51
|
+
headers: hdrs(),
|
|
52
|
+
body: JSON.stringify({ title, priority }),
|
|
53
|
+
})
|
|
54
|
+
if (res.status === 403) {
|
|
55
|
+
setFormError('403 — Need User role')
|
|
56
|
+
return
|
|
57
|
+
}
|
|
58
|
+
if (!res.ok) {
|
|
59
|
+
const b = await res.json()
|
|
60
|
+
setFormError(b.error?.issues?.[0]?.message ?? `Error ${res.status}`)
|
|
61
|
+
return
|
|
62
|
+
}
|
|
40
63
|
setTitle('')
|
|
41
64
|
loadTasks()
|
|
42
65
|
}
|
|
@@ -45,42 +68,60 @@ export default function Page() {
|
|
|
45
68
|
const msg = chatInput.trim()
|
|
46
69
|
if (!msg || chatBusy) return
|
|
47
70
|
setChatInput('')
|
|
48
|
-
setChat(c => [...c, { role: 'user', text: msg }])
|
|
71
|
+
setChat((c) => [...c, { role: 'user', text: msg }])
|
|
49
72
|
setChatBusy(true)
|
|
50
73
|
try {
|
|
51
74
|
const res = await fetch('/api/agents/assistant/chat', {
|
|
52
|
-
method: 'POST',
|
|
75
|
+
method: 'POST',
|
|
76
|
+
headers: hdrs(),
|
|
53
77
|
body: JSON.stringify({ message: msg, sessionId: 'session-' + Date.now() }),
|
|
54
78
|
})
|
|
55
|
-
if (res.status === 403) {
|
|
79
|
+
if (res.status === 403) {
|
|
80
|
+
setChat((c) => [...c, { role: 'error', text: '403 — Need User role' }])
|
|
81
|
+
return
|
|
82
|
+
}
|
|
56
83
|
const reader = res.body?.getReader()
|
|
57
84
|
if (!reader) return
|
|
58
85
|
const decoder = new TextDecoder()
|
|
59
|
-
let buf = '',
|
|
86
|
+
let buf = '',
|
|
87
|
+
agentText = ''
|
|
60
88
|
while (true) {
|
|
61
89
|
const { done, value } = await reader.read()
|
|
62
90
|
if (done) break
|
|
63
91
|
buf += decoder.decode(value, { stream: true })
|
|
64
|
-
const lines = buf.split('\n')
|
|
92
|
+
const lines = buf.split('\n')
|
|
93
|
+
buf = lines.pop() ?? ''
|
|
65
94
|
for (const line of lines) {
|
|
66
95
|
if (!line.startsWith('data: ')) continue
|
|
67
96
|
try {
|
|
68
97
|
const ev = JSON.parse(line.slice(6))
|
|
69
98
|
if (ev.type === 'text_delta') agentText += ev.content
|
|
70
|
-
else if (ev.type === 'tool_call')
|
|
71
|
-
|
|
72
|
-
else if (ev.type === '
|
|
73
|
-
|
|
99
|
+
else if (ev.type === 'tool_call')
|
|
100
|
+
setChat((c) => [...c, { role: 'tool', text: `🔧 ${ev.toolName}` }])
|
|
101
|
+
else if (ev.type === 'tool_result')
|
|
102
|
+
setChat((c) => [...c, { role: 'tool', text: `✅ ${(ev.output ?? '').slice(0, 80)}` }])
|
|
103
|
+
else if (ev.type === 'error')
|
|
104
|
+
setChat((c) => [...c, { role: 'error', text: ev.message }])
|
|
105
|
+
} catch {
|
|
106
|
+
/* partial */
|
|
107
|
+
}
|
|
74
108
|
}
|
|
75
109
|
}
|
|
76
|
-
if (agentText) setChat(c => [...c, { role: 'agent', text: agentText }])
|
|
110
|
+
if (agentText) setChat((c) => [...c, { role: 'agent', text: agentText }])
|
|
77
111
|
loadTasks()
|
|
78
112
|
} catch (err) {
|
|
79
|
-
setChat(c => [
|
|
80
|
-
|
|
113
|
+
setChat((c) => [
|
|
114
|
+
...c,
|
|
115
|
+
{ role: 'error', text: `Error: ${err instanceof Error ? err.message : String(err)}` },
|
|
116
|
+
])
|
|
117
|
+
} finally {
|
|
118
|
+
setChatBusy(false)
|
|
119
|
+
}
|
|
81
120
|
}
|
|
82
121
|
|
|
83
|
-
useEffect(() => {
|
|
122
|
+
useEffect(() => {
|
|
123
|
+
chatRef.current?.scrollTo(0, chatRef.current.scrollHeight)
|
|
124
|
+
}, [chat])
|
|
84
125
|
|
|
85
126
|
return (
|
|
86
127
|
<div className="page">
|
|
@@ -91,10 +132,20 @@ export default function Page() {
|
|
|
91
132
|
<h1>TheoKit</h1>
|
|
92
133
|
<p className="tagline">Build the app your agent lives in.</p>
|
|
93
134
|
<nav className="ctas">
|
|
94
|
-
<a
|
|
135
|
+
<a
|
|
136
|
+
href="https://usetheo.dev"
|
|
137
|
+
target="_blank"
|
|
138
|
+
rel="noopener noreferrer"
|
|
139
|
+
className="btn primary"
|
|
140
|
+
>
|
|
95
141
|
Get Started
|
|
96
142
|
</a>
|
|
97
|
-
<a
|
|
143
|
+
<a
|
|
144
|
+
href="https://github.com/usetheodev/theokit"
|
|
145
|
+
target="_blank"
|
|
146
|
+
rel="noopener noreferrer"
|
|
147
|
+
className="btn secondary"
|
|
148
|
+
>
|
|
98
149
|
Documentation
|
|
99
150
|
</a>
|
|
100
151
|
</nav>
|
|
@@ -106,7 +157,7 @@ export default function Page() {
|
|
|
106
157
|
{/* Role */}
|
|
107
158
|
<div className="role-bar">
|
|
108
159
|
<label htmlFor="role">Role:</label>
|
|
109
|
-
<select id="role" value={role} onChange={e => setRole(e.target.value as Role)}>
|
|
160
|
+
<select id="role" value={role} onChange={(e) => setRole(e.target.value as Role)}>
|
|
110
161
|
<option value="">None (public)</option>
|
|
111
162
|
<option value="user">User</option>
|
|
112
163
|
<option value="admin">Admin</option>
|
|
@@ -116,22 +167,44 @@ export default function Page() {
|
|
|
116
167
|
{/* Content */}
|
|
117
168
|
<div className="grid">
|
|
118
169
|
<section className="card">
|
|
119
|
-
<h2>
|
|
170
|
+
<h2>
|
|
171
|
+
Tasks <span className="badge">@Controller</span>
|
|
172
|
+
</h2>
|
|
120
173
|
<table>
|
|
121
|
-
<thead
|
|
174
|
+
<thead>
|
|
175
|
+
<tr>
|
|
176
|
+
<th>Task</th>
|
|
177
|
+
<th>Priority</th>
|
|
178
|
+
<th>Status</th>
|
|
179
|
+
</tr>
|
|
180
|
+
</thead>
|
|
122
181
|
<tbody>
|
|
123
|
-
{tasks.map(t => (
|
|
182
|
+
{tasks.map((t) => (
|
|
124
183
|
<tr key={t.id} className={t.done ? 'done' : ''}>
|
|
125
|
-
<td>
|
|
126
|
-
|
|
184
|
+
<td>
|
|
185
|
+
{t.done ? '✅ ' : '○ '}
|
|
186
|
+
{t.title}
|
|
187
|
+
</td>
|
|
188
|
+
<td>
|
|
189
|
+
<span className={`prio prio-${t.priority}`}>{t.priority}</span>
|
|
190
|
+
</td>
|
|
127
191
|
<td>{t.done ? 'Done' : 'To do'}</td>
|
|
128
192
|
</tr>
|
|
129
193
|
))}
|
|
130
194
|
</tbody>
|
|
131
195
|
</table>
|
|
132
196
|
<form onSubmit={createTask} className="create-bar">
|
|
133
|
-
<input
|
|
134
|
-
|
|
197
|
+
<input
|
|
198
|
+
value={title}
|
|
199
|
+
onChange={(e) => setTitle(e.target.value)}
|
|
200
|
+
placeholder="New task..."
|
|
201
|
+
required
|
|
202
|
+
minLength={3}
|
|
203
|
+
/>
|
|
204
|
+
<select
|
|
205
|
+
value={priority}
|
|
206
|
+
onChange={(e) => setPriority(e.target.value as Task['priority'])}
|
|
207
|
+
>
|
|
135
208
|
<option value="medium">Medium</option>
|
|
136
209
|
<option value="high">High</option>
|
|
137
210
|
<option value="low">Low</option>
|
|
@@ -142,26 +215,45 @@ export default function Page() {
|
|
|
142
215
|
</section>
|
|
143
216
|
|
|
144
217
|
<section className="card">
|
|
145
|
-
<h2>
|
|
218
|
+
<h2>
|
|
219
|
+
AI Assistant <span className="badge badge-ai">@Agent + SSE</span>
|
|
220
|
+
</h2>
|
|
146
221
|
<div ref={chatRef} className="chat-box">
|
|
147
222
|
{chat.map((m, i) => (
|
|
148
|
-
<div key={i} className={`msg ${m.role}`}>
|
|
223
|
+
<div key={i} className={`msg ${m.role}`}>
|
|
224
|
+
{m.role === 'user' ? `You: ${m.text}` : m.text}
|
|
225
|
+
</div>
|
|
149
226
|
))}
|
|
150
227
|
</div>
|
|
151
228
|
<div className="chat-bar">
|
|
152
|
-
<input
|
|
153
|
-
|
|
229
|
+
<input
|
|
230
|
+
value={chatInput}
|
|
231
|
+
onChange={(e) => setChatInput(e.target.value)}
|
|
232
|
+
onKeyDown={(e) => e.key === 'Enter' && sendChat()}
|
|
233
|
+
placeholder="Message the AI assistant..."
|
|
234
|
+
disabled={chatBusy}
|
|
235
|
+
/>
|
|
236
|
+
<button type="button" onClick={sendChat} disabled={chatBusy}>
|
|
237
|
+
Send
|
|
238
|
+
</button>
|
|
154
239
|
</div>
|
|
155
240
|
</section>
|
|
156
241
|
</div>
|
|
157
242
|
|
|
158
243
|
{/* Footer */}
|
|
159
244
|
<footer className="footer">
|
|
160
|
-
Powered by
|
|
245
|
+
Powered by{' '}
|
|
246
|
+
<a href="https://usetheo.dev" target="_blank" rel="noopener noreferrer">
|
|
247
|
+
TheoKit
|
|
248
|
+
</a>
|
|
161
249
|
{' · '}
|
|
162
|
-
<a href="https://github.com/usetheodev/theokit" target="_blank" rel="noopener noreferrer">
|
|
250
|
+
<a href="https://github.com/usetheodev/theokit" target="_blank" rel="noopener noreferrer">
|
|
251
|
+
GitHub
|
|
252
|
+
</a>
|
|
163
253
|
{' · '}
|
|
164
|
-
<a href="https://discord.usetheo.dev" target="_blank" rel="noopener noreferrer">
|
|
254
|
+
<a href="https://discord.usetheo.dev" target="_blank" rel="noopener noreferrer">
|
|
255
|
+
Discord
|
|
256
|
+
</a>
|
|
165
257
|
</footer>
|
|
166
258
|
</div>
|
|
167
259
|
</div>
|