weifuwu 0.24.0 → 0.24.2
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/README.md +970 -756
- package/cli/template/app.ts +5 -1
- package/cli/template/index.ts +4 -1
- package/cli/template/locales/en.json +6 -1
- package/cli/template/locales/zh-CN.json +6 -1
- package/cli/template/locales/zh-TW.json +6 -1
- package/cli/template/locales/zh.json +6 -1
- package/cli/template/ui/app/globals.css +1 -1
- package/cli/template/ui/app/page.tsx +55 -16
- package/cli.ts +148 -104
- package/dist/agent/rest.d.ts +1 -1
- package/dist/agent/run.d.ts +2 -2
- package/dist/agent/types.d.ts +2 -1
- package/dist/ai/workflow.d.ts +1 -1
- package/dist/ai-sdk.d.ts +1 -1
- package/dist/analytics.d.ts +2 -2
- package/dist/cache.d.ts +4 -4
- package/dist/cli.js +135 -97
- package/dist/cookie.d.ts +24 -0
- package/dist/cors.d.ts +2 -2
- package/dist/deploy/types.d.ts +2 -2
- package/dist/fts.d.ts +5 -5
- package/dist/helmet.d.ts +2 -2
- package/dist/hub.d.ts +2 -1
- package/dist/iii/index.d.ts +1 -1
- package/dist/iii/register-worker.d.ts +1 -1
- package/dist/iii/types.d.ts +4 -4
- package/dist/index.d.ts +5 -5
- package/dist/index.js +905 -442
- package/dist/kb/types.d.ts +8 -0
- package/dist/live.d.ts +2 -3
- package/dist/logdb/rest.d.ts +1 -1
- package/dist/logdb/types.d.ts +2 -1
- package/dist/mailer.d.ts +3 -2
- package/dist/messager/agent.d.ts +2 -2
- package/dist/messager/rest.d.ts +3 -3
- package/dist/messager/types.d.ts +2 -1
- package/dist/messager/ws.d.ts +3 -3
- package/dist/opencode/index.d.ts +1 -1
- package/dist/opencode/permissions.d.ts +1 -1
- package/dist/opencode/run.d.ts +1 -1
- package/dist/opencode/session.d.ts +9 -9
- package/dist/opencode/tools/web.d.ts +1 -1
- package/dist/opencode/types.d.ts +2 -1
- package/dist/opencode/ws.d.ts +1 -2
- package/dist/permissions.d.ts +4 -4
- package/dist/postgres/module.d.ts +5 -4
- package/dist/postgres/schema/index.d.ts +1 -1
- package/dist/postgres/schema/table.d.ts +22 -20
- package/dist/postgres/types.d.ts +6 -6
- package/dist/queue/types.d.ts +3 -3
- package/dist/rate-limit.d.ts +1 -1
- package/dist/react.d.ts +1 -1
- package/dist/react.js +141 -96
- package/dist/redis/types.d.ts +2 -2
- package/dist/router.d.ts +10 -10
- package/dist/seo.d.ts +2 -2
- package/dist/serve.d.ts +1 -1
- package/dist/session.d.ts +8 -5
- package/dist/tailwind.d.ts +9 -0
- package/dist/tenant/graphql.d.ts +2 -2
- package/dist/tenant/index.d.ts +1 -1
- package/dist/tenant/rest.d.ts +2 -2
- package/dist/tenant/types.d.ts +3 -3
- package/dist/test-utils.d.ts +3 -3
- package/dist/types.d.ts +8 -0
- package/dist/upload.d.ts +4 -2
- package/dist/user/index.d.ts +1 -1
- package/dist/user/oauth-login.d.ts +2 -2
- package/dist/user/types.d.ts +2 -2
- package/dist/vendor.d.ts +4 -0
- package/opencode/ui/app/globals.css +1 -1
- package/opencode/ui/app/layout.tsx +2 -3
- package/opencode/ui/app/page.tsx +302 -73
- package/package.json +33 -10
- package/cli/template/.weifuwu/ssr/2e3a7e60.js +0 -112
package/opencode/ui/app/page.tsx
CHANGED
|
@@ -1,9 +1,25 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-explicit-any, @typescript-eslint/no-unused-vars */
|
|
1
2
|
import { useState, useRef, useEffect } from 'react'
|
|
2
3
|
|
|
3
|
-
interface ToolCallEvent {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
interface ToolCallEvent {
|
|
5
|
+
toolName: string
|
|
6
|
+
input: unknown
|
|
7
|
+
}
|
|
8
|
+
interface ToolResultEvent {
|
|
9
|
+
toolName: string
|
|
10
|
+
output: unknown
|
|
11
|
+
}
|
|
12
|
+
interface SessionItem {
|
|
13
|
+
id: number
|
|
14
|
+
title: string
|
|
15
|
+
created_at?: string
|
|
16
|
+
}
|
|
17
|
+
interface MessageItem {
|
|
18
|
+
role: string
|
|
19
|
+
content: string
|
|
20
|
+
toolCalls?: ToolCallEvent[]
|
|
21
|
+
toolResults?: ToolResultEvent[]
|
|
22
|
+
}
|
|
7
23
|
|
|
8
24
|
function formatDate(s: string) {
|
|
9
25
|
const d = new Date(s)
|
|
@@ -27,56 +43,93 @@ export default function Page() {
|
|
|
27
43
|
const bottomRef = useRef<HTMLDivElement>(null)
|
|
28
44
|
const inputRef = useRef<HTMLInputElement>(null)
|
|
29
45
|
|
|
30
|
-
useEffect(() => {
|
|
31
|
-
|
|
46
|
+
useEffect(() => {
|
|
47
|
+
fetch('/opencode/sessions')
|
|
48
|
+
.then((r) => r.json())
|
|
49
|
+
.then(setSessions)
|
|
50
|
+
.catch(() => {})
|
|
51
|
+
}, [])
|
|
52
|
+
useEffect(() => {
|
|
53
|
+
bottomRef.current?.scrollIntoView({ behavior: 'smooth' })
|
|
54
|
+
}, [messages, streaming])
|
|
32
55
|
|
|
33
56
|
async function createSession() {
|
|
34
57
|
setError('')
|
|
35
|
-
const res = await fetch('/opencode/sessions', {
|
|
36
|
-
|
|
58
|
+
const res = await fetch('/opencode/sessions', {
|
|
59
|
+
method: 'POST',
|
|
60
|
+
headers: { 'Content-Type': 'application/json' },
|
|
61
|
+
body: JSON.stringify({}),
|
|
62
|
+
})
|
|
63
|
+
if (!res.ok) {
|
|
64
|
+
setError('Failed')
|
|
65
|
+
return
|
|
66
|
+
}
|
|
37
67
|
const s = await res.json()
|
|
38
|
-
setSessions(p => [s, ...p])
|
|
39
|
-
setCurrentId(s.id)
|
|
68
|
+
setSessions((p) => [s, ...p])
|
|
69
|
+
setCurrentId(s.id)
|
|
70
|
+
setMessages([])
|
|
71
|
+
setStreaming('')
|
|
72
|
+
textRef.current = ''
|
|
40
73
|
setTimeout(() => inputRef.current?.focus(), 100)
|
|
41
74
|
}
|
|
42
75
|
|
|
43
76
|
async function selectSession(id: number) {
|
|
44
77
|
abortRef.current?.abort()
|
|
45
|
-
setCurrentId(id)
|
|
78
|
+
setCurrentId(id)
|
|
79
|
+
setStreaming('')
|
|
80
|
+
textRef.current = ''
|
|
46
81
|
const res = await fetch(`/opencode/sessions/${id}`)
|
|
47
|
-
if (!res.ok) {
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
82
|
+
if (!res.ok) {
|
|
83
|
+
setError('Failed to load')
|
|
84
|
+
return
|
|
85
|
+
}
|
|
86
|
+
const { messages: msgs } = (await res.json()) as any
|
|
87
|
+
setMessages(
|
|
88
|
+
msgs.map((m: any) => ({
|
|
89
|
+
role: m.role,
|
|
90
|
+
content: m.content || '',
|
|
91
|
+
toolCalls: Array.isArray(m.tool_calls) ? m.tool_calls : undefined,
|
|
92
|
+
toolResults: Array.isArray(m.tool_results) ? m.tool_results : undefined,
|
|
93
|
+
})),
|
|
94
|
+
)
|
|
54
95
|
setTimeout(() => inputRef.current?.focus(), 100)
|
|
55
96
|
}
|
|
56
97
|
|
|
57
98
|
function deleteSession(e: React.MouseEvent, id: number) {
|
|
58
99
|
e.stopPropagation()
|
|
59
100
|
fetch('/opencode/sessions/' + id, { method: 'DELETE' })
|
|
60
|
-
setSessions(p => p.filter(s => s.id !== id))
|
|
61
|
-
if (currentId === id) {
|
|
101
|
+
setSessions((p) => p.filter((s) => s.id !== id))
|
|
102
|
+
if (currentId === id) {
|
|
103
|
+
setCurrentId(null)
|
|
104
|
+
setMessages([])
|
|
105
|
+
}
|
|
62
106
|
}
|
|
63
107
|
|
|
64
108
|
async function sendMessage() {
|
|
65
109
|
const content = input.trim()
|
|
66
110
|
if (!content || !currentId || loading) return
|
|
67
111
|
setError('')
|
|
68
|
-
setMessages(p => [...p, { role: 'user', content }])
|
|
69
|
-
setInput('')
|
|
112
|
+
setMessages((p) => [...p, { role: 'user', content }])
|
|
113
|
+
setInput('')
|
|
114
|
+
setLoading(true)
|
|
115
|
+
setStreaming('')
|
|
116
|
+
textRef.current = ''
|
|
70
117
|
abortRef.current?.abort()
|
|
71
118
|
const controller = new AbortController()
|
|
72
119
|
abortRef.current = controller
|
|
73
120
|
|
|
74
121
|
try {
|
|
75
122
|
const res = await fetch(`/opencode/sessions/${currentId}/message`, {
|
|
76
|
-
method: 'POST',
|
|
77
|
-
|
|
123
|
+
method: 'POST',
|
|
124
|
+
headers: { 'Content-Type': 'application/json' },
|
|
125
|
+
body: JSON.stringify({ content }),
|
|
126
|
+
signal: controller.signal,
|
|
78
127
|
})
|
|
79
|
-
if (!res.ok) {
|
|
128
|
+
if (!res.ok) {
|
|
129
|
+
setError('Request failed')
|
|
130
|
+
setLoading(false)
|
|
131
|
+
return
|
|
132
|
+
}
|
|
80
133
|
|
|
81
134
|
const reader = res.body!.getReader()
|
|
82
135
|
const decoder = new TextDecoder()
|
|
@@ -100,28 +153,51 @@ export default function Page() {
|
|
|
100
153
|
setStreaming(textRef.current)
|
|
101
154
|
break
|
|
102
155
|
case 'tool-call':
|
|
103
|
-
setMessages(p => {
|
|
156
|
+
setMessages((p) => {
|
|
104
157
|
const last = p[p.length - 1]
|
|
105
158
|
if (last?.role === 'assistant') {
|
|
106
159
|
const tcs = last.toolCalls || []
|
|
107
|
-
return [
|
|
160
|
+
return [
|
|
161
|
+
...p.slice(0, -1),
|
|
162
|
+
{ ...last, toolCalls: [...tcs, { toolName: d.toolName, input: d.input }] },
|
|
163
|
+
]
|
|
108
164
|
}
|
|
109
|
-
return [
|
|
165
|
+
return [
|
|
166
|
+
...p,
|
|
167
|
+
{
|
|
168
|
+
role: 'assistant',
|
|
169
|
+
content: '',
|
|
170
|
+
toolCalls: [{ toolName: d.toolName, input: d.input }],
|
|
171
|
+
},
|
|
172
|
+
]
|
|
110
173
|
})
|
|
111
174
|
break
|
|
112
175
|
case 'tool-result':
|
|
113
|
-
setMessages(p => {
|
|
176
|
+
setMessages((p) => {
|
|
114
177
|
const last = p[p.length - 1]
|
|
115
178
|
if (last?.role === 'assistant') {
|
|
116
179
|
const trs = last.toolResults || []
|
|
117
|
-
return [
|
|
180
|
+
return [
|
|
181
|
+
...p.slice(0, -1),
|
|
182
|
+
{
|
|
183
|
+
...last,
|
|
184
|
+
toolResults: [...trs, { toolName: d.toolName, output: d.output }],
|
|
185
|
+
},
|
|
186
|
+
]
|
|
118
187
|
}
|
|
119
|
-
return [
|
|
188
|
+
return [
|
|
189
|
+
...p,
|
|
190
|
+
{
|
|
191
|
+
role: 'assistant',
|
|
192
|
+
content: '',
|
|
193
|
+
toolResults: [{ toolName: d.toolName, output: d.output }],
|
|
194
|
+
},
|
|
195
|
+
]
|
|
120
196
|
})
|
|
121
197
|
break
|
|
122
|
-
case 'finish':
|
|
198
|
+
case 'finish': {
|
|
123
199
|
const finalContent = textRef.current
|
|
124
|
-
setMessages(p => {
|
|
200
|
+
setMessages((p) => {
|
|
125
201
|
const last = p[p.length - 1]
|
|
126
202
|
if (last?.role === 'assistant' && !last.content && finalContent) {
|
|
127
203
|
return [...p.slice(0, -1), { ...last, content: finalContent }]
|
|
@@ -129,9 +205,12 @@ export default function Page() {
|
|
|
129
205
|
if (last?.role === 'assistant' && last.content) return p
|
|
130
206
|
return [...p, { role: 'assistant', content: finalContent }]
|
|
131
207
|
})
|
|
132
|
-
setStreaming('')
|
|
208
|
+
setStreaming('')
|
|
209
|
+
textRef.current = ''
|
|
210
|
+
setLoading(false)
|
|
133
211
|
setTimeout(() => inputRef.current?.focus(), 50)
|
|
134
212
|
return
|
|
213
|
+
}
|
|
135
214
|
}
|
|
136
215
|
} catch {}
|
|
137
216
|
}
|
|
@@ -147,9 +226,18 @@ export default function Page() {
|
|
|
147
226
|
{/* Sidebar */}
|
|
148
227
|
<aside className="w-64 flex flex-col border-r border-zinc-800 bg-zinc-900/50 shrink-0">
|
|
149
228
|
<div className="p-3 border-b border-zinc-800">
|
|
150
|
-
<button
|
|
151
|
-
|
|
152
|
-
|
|
229
|
+
<button
|
|
230
|
+
onClick={createSession}
|
|
231
|
+
className="w-full flex items-center justify-center gap-2 py-2 px-3 bg-zinc-800 hover:bg-zinc-700 text-zinc-200 rounded-lg text-sm transition-colors cursor-pointer"
|
|
232
|
+
>
|
|
233
|
+
<svg className="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
234
|
+
<path
|
|
235
|
+
strokeLinecap="round"
|
|
236
|
+
strokeLinejoin="round"
|
|
237
|
+
strokeWidth={2}
|
|
238
|
+
d="M12 4v16m8-8H4"
|
|
239
|
+
/>
|
|
240
|
+
</svg>
|
|
153
241
|
New Chat
|
|
154
242
|
</button>
|
|
155
243
|
</div>
|
|
@@ -157,17 +245,42 @@ export default function Page() {
|
|
|
157
245
|
{sessions.length === 0 && (
|
|
158
246
|
<div className="text-xs text-zinc-600 text-center py-8">No sessions yet</div>
|
|
159
247
|
)}
|
|
160
|
-
{sessions.map(s => (
|
|
161
|
-
<div
|
|
248
|
+
{sessions.map((s) => (
|
|
249
|
+
<div
|
|
250
|
+
key={s.id}
|
|
162
251
|
onClick={() => selectSession(s.id)}
|
|
163
252
|
className={`group flex items-center gap-2 p-2 rounded-lg cursor-pointer text-sm transition-colors ${
|
|
164
|
-
currentId === s.id
|
|
165
|
-
|
|
166
|
-
|
|
253
|
+
currentId === s.id
|
|
254
|
+
? 'bg-zinc-700/60 text-zinc-100'
|
|
255
|
+
: 'text-zinc-400 hover:bg-zinc-800/60 hover:text-zinc-200'
|
|
256
|
+
}`}
|
|
257
|
+
>
|
|
258
|
+
<svg
|
|
259
|
+
className="w-4 h-4 shrink-0 opacity-60"
|
|
260
|
+
fill="none"
|
|
261
|
+
viewBox="0 0 24 24"
|
|
262
|
+
stroke="currentColor"
|
|
263
|
+
>
|
|
264
|
+
<path
|
|
265
|
+
strokeLinecap="round"
|
|
266
|
+
strokeLinejoin="round"
|
|
267
|
+
strokeWidth={2}
|
|
268
|
+
d="M8 10h.01M12 10h.01M16 10h.01M9 16H5a2 2 0 01-2-2V6a2 2 0 012-2h14a2 2 0 012 2v8a2 2 0 01-2 2h-5l-5 5v-5z"
|
|
269
|
+
/>
|
|
270
|
+
</svg>
|
|
167
271
|
<span className="truncate flex-1">{s.title || `Session ${s.id}`}</span>
|
|
168
|
-
<button
|
|
169
|
-
|
|
170
|
-
|
|
272
|
+
<button
|
|
273
|
+
onClick={(e) => deleteSession(e, s.id)}
|
|
274
|
+
className="opacity-0 group-hover:opacity-100 p-0.5 rounded hover:bg-zinc-600 transition-all cursor-pointer"
|
|
275
|
+
>
|
|
276
|
+
<svg className="w-3.5 h-3.5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
277
|
+
<path
|
|
278
|
+
strokeLinecap="round"
|
|
279
|
+
strokeLinejoin="round"
|
|
280
|
+
strokeWidth={2}
|
|
281
|
+
d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"
|
|
282
|
+
/>
|
|
283
|
+
</svg>
|
|
171
284
|
</button>
|
|
172
285
|
</div>
|
|
173
286
|
))}
|
|
@@ -179,10 +292,14 @@ export default function Page() {
|
|
|
179
292
|
{/* Header */}
|
|
180
293
|
{currentId && (
|
|
181
294
|
<header className="flex items-center gap-2 px-5 py-2.5 border-b border-zinc-800 bg-zinc-900/30">
|
|
182
|
-
<svg className="w-4 h-4 text-emerald-500" viewBox="0 0 24 24" fill="currentColor"
|
|
295
|
+
<svg className="w-4 h-4 text-emerald-500" viewBox="0 0 24 24" fill="currentColor">
|
|
296
|
+
<path d="M12 2L2 7l10 5 10-5-10-5zM2 17l10 5 10-5M2 12l10 5 10-5" />
|
|
297
|
+
</svg>
|
|
183
298
|
<span className="text-xs text-zinc-500 font-mono">opencode</span>
|
|
184
299
|
<span className="text-xs text-zinc-600 mx-1">/</span>
|
|
185
|
-
<span className="text-sm text-zinc-300 truncate">
|
|
300
|
+
<span className="text-sm text-zinc-300 truncate">
|
|
301
|
+
{sessions.find((s) => s.id === currentId)?.title || `Session ${currentId}`}
|
|
302
|
+
</span>
|
|
186
303
|
</header>
|
|
187
304
|
)}
|
|
188
305
|
|
|
@@ -191,38 +308,98 @@ export default function Page() {
|
|
|
191
308
|
<div className="max-w-3xl mx-auto px-4 py-6 space-y-4">
|
|
192
309
|
{!currentId && (
|
|
193
310
|
<div className="flex flex-col items-center justify-center h-[70vh] text-zinc-600">
|
|
194
|
-
<svg
|
|
311
|
+
<svg
|
|
312
|
+
className="w-12 h-12 mb-4 opacity-40"
|
|
313
|
+
fill="none"
|
|
314
|
+
viewBox="0 0 24 24"
|
|
315
|
+
stroke="currentColor"
|
|
316
|
+
>
|
|
317
|
+
<path
|
|
318
|
+
strokeLinecap="round"
|
|
319
|
+
strokeLinejoin="round"
|
|
320
|
+
strokeWidth={1.5}
|
|
321
|
+
d="M8 10h.01M12 10h.01M16 10h.01M9 16H5a2 2 0 01-2-2V6a2 2 0 012-2h14a2 2 0 012 2v8a2 2 0 01-2 2h-5l-5 5v-5z"
|
|
322
|
+
/>
|
|
323
|
+
</svg>
|
|
195
324
|
<p className="text-sm mb-2">Select a session or create a new one</p>
|
|
196
|
-
<button
|
|
325
|
+
<button
|
|
326
|
+
onClick={createSession}
|
|
327
|
+
className="mt-2 px-4 py-2 bg-zinc-800 hover:bg-zinc-700 text-zinc-300 rounded-lg text-sm transition-colors cursor-pointer"
|
|
328
|
+
>
|
|
197
329
|
+ New Chat
|
|
198
330
|
</button>
|
|
199
331
|
</div>
|
|
200
332
|
)}
|
|
201
333
|
|
|
202
334
|
{messages.map((m, i) => (
|
|
203
|
-
<div
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
335
|
+
<div
|
|
336
|
+
key={i}
|
|
337
|
+
className={`flex ${m.role === 'user' ? 'justify-end' : 'justify-start'}`}
|
|
338
|
+
>
|
|
339
|
+
<div
|
|
340
|
+
className={`max-w-[75%] rounded-2xl px-4 py-2.5 leading-relaxed whitespace-pre-wrap text-sm ${
|
|
341
|
+
m.role === 'user'
|
|
342
|
+
? 'bg-indigo-600 text-white rounded-br-md'
|
|
343
|
+
: 'bg-zinc-800/80 text-zinc-200 rounded-bl-md border border-zinc-700/50'
|
|
344
|
+
}`}
|
|
345
|
+
>
|
|
209
346
|
{m.content || <span className="text-zinc-500 italic">No response</span>}
|
|
210
347
|
{m.toolCalls?.map((tc, j) => (
|
|
211
|
-
<details
|
|
348
|
+
<details
|
|
349
|
+
key={j}
|
|
350
|
+
className="mt-2 rounded-lg overflow-hidden bg-black/20 border border-zinc-700/50"
|
|
351
|
+
>
|
|
212
352
|
<summary className="px-3 py-1.5 text-xs text-zinc-400 cursor-pointer hover:text-zinc-200 select-none flex items-center gap-1.5">
|
|
213
|
-
<svg
|
|
353
|
+
<svg
|
|
354
|
+
className="w-3.5 h-3.5"
|
|
355
|
+
fill="none"
|
|
356
|
+
viewBox="0 0 24 24"
|
|
357
|
+
stroke="currentColor"
|
|
358
|
+
>
|
|
359
|
+
<path
|
|
360
|
+
strokeLinecap="round"
|
|
361
|
+
strokeLinejoin="round"
|
|
362
|
+
strokeWidth={2}
|
|
363
|
+
d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.066 2.573c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.573 1.066c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.066-2.573c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z"
|
|
364
|
+
/>
|
|
365
|
+
<path
|
|
366
|
+
strokeLinecap="round"
|
|
367
|
+
strokeLinejoin="round"
|
|
368
|
+
strokeWidth={2}
|
|
369
|
+
d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"
|
|
370
|
+
/>
|
|
371
|
+
</svg>
|
|
214
372
|
{tc.toolName}
|
|
215
373
|
</summary>
|
|
216
|
-
<pre className="px-3 py-2 text-xs text-zinc-400 overflow-x-auto">
|
|
374
|
+
<pre className="px-3 py-2 text-xs text-zinc-400 overflow-x-auto">
|
|
375
|
+
{JSON.stringify(tc.input, null, 2)}
|
|
376
|
+
</pre>
|
|
217
377
|
</details>
|
|
218
378
|
))}
|
|
219
379
|
{m.toolResults?.map((tr, j) => (
|
|
220
|
-
<details
|
|
380
|
+
<details
|
|
381
|
+
key={j}
|
|
382
|
+
className="mt-1.5 rounded-lg overflow-hidden bg-black/20 border border-zinc-700/50"
|
|
383
|
+
>
|
|
221
384
|
<summary className="px-3 py-1.5 text-xs text-zinc-400 cursor-pointer hover:text-zinc-200 select-none flex items-center gap-1.5">
|
|
222
|
-
<svg
|
|
385
|
+
<svg
|
|
386
|
+
className="w-3.5 h-3.5"
|
|
387
|
+
fill="none"
|
|
388
|
+
viewBox="0 0 24 24"
|
|
389
|
+
stroke="currentColor"
|
|
390
|
+
>
|
|
391
|
+
<path
|
|
392
|
+
strokeLinecap="round"
|
|
393
|
+
strokeLinejoin="round"
|
|
394
|
+
strokeWidth={2}
|
|
395
|
+
d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"
|
|
396
|
+
/>
|
|
397
|
+
</svg>
|
|
223
398
|
{tr.toolName} result
|
|
224
399
|
</summary>
|
|
225
|
-
<pre className="px-3 py-2 text-xs text-zinc-400 overflow-x-auto max-h-48">
|
|
400
|
+
<pre className="px-3 py-2 text-xs text-zinc-400 overflow-x-auto max-h-48">
|
|
401
|
+
{JSON.stringify(tr.output, null, 2)}
|
|
402
|
+
</pre>
|
|
226
403
|
</details>
|
|
227
404
|
))}
|
|
228
405
|
</div>
|
|
@@ -232,7 +409,8 @@ export default function Page() {
|
|
|
232
409
|
{streaming && (
|
|
233
410
|
<div className="flex justify-start">
|
|
234
411
|
<div className="max-w-[75%] rounded-2xl px-4 py-2.5 bg-zinc-800/80 text-zinc-200 rounded-bl-md border border-zinc-700/50 leading-relaxed whitespace-pre-wrap text-sm">
|
|
235
|
-
{streaming}
|
|
412
|
+
{streaming}
|
|
413
|
+
<span className="inline-block w-1.5 h-4 bg-indigo-400 ml-0.5 animate-pulse rounded-sm" />
|
|
236
414
|
</div>
|
|
237
415
|
</div>
|
|
238
416
|
)}
|
|
@@ -241,9 +419,18 @@ export default function Page() {
|
|
|
241
419
|
<div className="flex justify-start">
|
|
242
420
|
<div className="flex items-center gap-2 px-4 py-3 bg-zinc-800/60 rounded-2xl rounded-bl-md border border-zinc-700/40">
|
|
243
421
|
<div className="flex gap-1">
|
|
244
|
-
<span
|
|
245
|
-
|
|
246
|
-
|
|
422
|
+
<span
|
|
423
|
+
className="w-2 h-2 bg-zinc-500 rounded-full animate-bounce"
|
|
424
|
+
style={{ animationDelay: '0ms' }}
|
|
425
|
+
/>
|
|
426
|
+
<span
|
|
427
|
+
className="w-2 h-2 bg-zinc-500 rounded-full animate-bounce"
|
|
428
|
+
style={{ animationDelay: '150ms' }}
|
|
429
|
+
/>
|
|
430
|
+
<span
|
|
431
|
+
className="w-2 h-2 bg-zinc-500 rounded-full animate-bounce"
|
|
432
|
+
style={{ animationDelay: '300ms' }}
|
|
433
|
+
/>
|
|
247
434
|
</div>
|
|
248
435
|
</div>
|
|
249
436
|
</div>
|
|
@@ -252,7 +439,19 @@ export default function Page() {
|
|
|
252
439
|
{error && (
|
|
253
440
|
<div className="flex justify-center">
|
|
254
441
|
<div className="flex items-center gap-2 px-4 py-2 bg-red-900/30 text-red-400 rounded-lg text-xs border border-red-800/40">
|
|
255
|
-
<svg
|
|
442
|
+
<svg
|
|
443
|
+
className="w-4 h-4 shrink-0"
|
|
444
|
+
fill="none"
|
|
445
|
+
viewBox="0 0 24 24"
|
|
446
|
+
stroke="currentColor"
|
|
447
|
+
>
|
|
448
|
+
<path
|
|
449
|
+
strokeLinecap="round"
|
|
450
|
+
strokeLinejoin="round"
|
|
451
|
+
strokeWidth={2}
|
|
452
|
+
d="M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
|
|
453
|
+
/>
|
|
454
|
+
</svg>
|
|
256
455
|
{error}
|
|
257
456
|
</div>
|
|
258
457
|
</div>
|
|
@@ -266,20 +465,50 @@ export default function Page() {
|
|
|
266
465
|
<div className="border-t border-zinc-800 bg-zinc-900/50">
|
|
267
466
|
<div className="max-w-3xl mx-auto px-4 py-3">
|
|
268
467
|
<div className="flex items-end gap-2 bg-zinc-800/80 rounded-xl border border-zinc-700/50 px-3 py-2 focus-within:border-zinc-500 transition-colors">
|
|
269
|
-
<input
|
|
468
|
+
<input
|
|
469
|
+
ref={inputRef}
|
|
270
470
|
value={input}
|
|
271
|
-
onChange={e => setInput(e.target.value)}
|
|
272
|
-
onKeyDown={e => {
|
|
471
|
+
onChange={(e) => setInput(e.target.value)}
|
|
472
|
+
onKeyDown={(e) => {
|
|
473
|
+
if (e.key === 'Enter' && !e.shiftKey) {
|
|
474
|
+
e.preventDefault()
|
|
475
|
+
sendMessage()
|
|
476
|
+
}
|
|
477
|
+
}}
|
|
273
478
|
placeholder={currentId ? 'Type a message...' : 'Create a session first'}
|
|
274
479
|
disabled={!currentId || loading}
|
|
275
480
|
className="flex-1 bg-transparent text-sm text-zinc-100 placeholder-zinc-500 outline-none resize-none disabled:opacity-40"
|
|
276
481
|
/>
|
|
277
|
-
<button
|
|
278
|
-
|
|
482
|
+
<button
|
|
483
|
+
onClick={sendMessage}
|
|
484
|
+
disabled={!currentId || loading || !input.trim()}
|
|
485
|
+
className="flex items-center justify-center p-1.5 rounded-lg bg-indigo-600 hover:bg-indigo-500 disabled:bg-zinc-700 disabled:text-zinc-500 text-white transition-colors cursor-pointer disabled:cursor-default shrink-0"
|
|
486
|
+
>
|
|
279
487
|
{loading ? (
|
|
280
|
-
<svg className="w-4 h-4 animate-spin" viewBox="0 0 24 24" fill="none"
|
|
488
|
+
<svg className="w-4 h-4 animate-spin" viewBox="0 0 24 24" fill="none">
|
|
489
|
+
<circle
|
|
490
|
+
className="opacity-25"
|
|
491
|
+
cx="12"
|
|
492
|
+
cy="12"
|
|
493
|
+
r="10"
|
|
494
|
+
stroke="currentColor"
|
|
495
|
+
strokeWidth="4"
|
|
496
|
+
/>
|
|
497
|
+
<path
|
|
498
|
+
className="opacity-75"
|
|
499
|
+
fill="currentColor"
|
|
500
|
+
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
|
|
501
|
+
/>
|
|
502
|
+
</svg>
|
|
281
503
|
) : (
|
|
282
|
-
<svg className="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor"
|
|
504
|
+
<svg className="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
505
|
+
<path
|
|
506
|
+
strokeLinecap="round"
|
|
507
|
+
strokeLinejoin="round"
|
|
508
|
+
strokeWidth={2}
|
|
509
|
+
d="M12 19V5m0 0l-7 7m7-7l7 7"
|
|
510
|
+
/>
|
|
511
|
+
</svg>
|
|
283
512
|
)}
|
|
284
513
|
</button>
|
|
285
514
|
</div>
|
package/package.json
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "weifuwu",
|
|
3
|
-
"
|
|
3
|
+
"type": "module",
|
|
4
|
+
"version": "0.24.2",
|
|
4
5
|
"description": "Web-standard HTTP framework for Node.js — (req, ctx) => Response",
|
|
5
6
|
"main": "./dist/index.js",
|
|
6
7
|
"types": "./dist/index.d.ts",
|
|
@@ -24,9 +25,15 @@
|
|
|
24
25
|
"start": "cd cli/template && node index.ts",
|
|
25
26
|
"build": "esbuild index.ts --bundle --format=esm --platform=node --outfile=dist/index.js --packages=external --define:__WFW_BUNDLED__=true && esbuild cli.ts --bundle --format=esm --platform=node --outfile=dist/cli.js --packages=external && esbuild react.ts --bundle --format=esm --outfile=dist/react.js --external:react --external:react-dom",
|
|
26
27
|
"prepublishOnly": "npm run build && tsc --emitDeclarationOnly --outdir dist",
|
|
28
|
+
"typecheck": "tsc --noEmit",
|
|
29
|
+
"format": "prettier --write .",
|
|
30
|
+
"format:check": "prettier --check .",
|
|
31
|
+
"lint": "eslint --ext .ts,.tsx .",
|
|
27
32
|
"test": "node --test 'test/**/*.test.ts'",
|
|
33
|
+
"test:coverage": "node --experimental-test-coverage --test 'test/**/*.test.ts'",
|
|
28
34
|
"test:unit": "bash scripts/test-unit.sh",
|
|
29
|
-
"test:ci": "node --test 'test/**/*.test.ts'"
|
|
35
|
+
"test:ci": "node --test 'test/**/*.test.ts'",
|
|
36
|
+
"prepare": "husky"
|
|
30
37
|
},
|
|
31
38
|
"dependencies": {
|
|
32
39
|
"@ai-sdk/openai": "^3.0.66",
|
|
@@ -48,15 +55,31 @@
|
|
|
48
55
|
"yaml": "^2.9.0",
|
|
49
56
|
"zod": "^4.4.3"
|
|
50
57
|
},
|
|
51
|
-
"
|
|
52
|
-
|
|
58
|
+
"lint-staged": {
|
|
59
|
+
"*.{ts,tsx}": [
|
|
60
|
+
"prettier --write",
|
|
61
|
+
"eslint --fix"
|
|
62
|
+
],
|
|
63
|
+
"*.{json,css,md}": [
|
|
64
|
+
"prettier --write"
|
|
65
|
+
]
|
|
66
|
+
},
|
|
53
67
|
"devDependencies": {
|
|
54
|
-
"@
|
|
55
|
-
"@types/
|
|
56
|
-
"@types/
|
|
57
|
-
"@types/
|
|
58
|
-
"@types/react
|
|
68
|
+
"@eslint/js": "^10.0.1",
|
|
69
|
+
"@types/jsonwebtoken": "^9.0.9",
|
|
70
|
+
"@types/node": "^25.9.3",
|
|
71
|
+
"@types/nodemailer": "^6.4.17",
|
|
72
|
+
"@types/react": "^19.2.17",
|
|
73
|
+
"@types/react-dom": "^19.2.3",
|
|
59
74
|
"@types/ws": "^8.18.1",
|
|
60
|
-
"
|
|
75
|
+
"eslint": "^10.5.0",
|
|
76
|
+
"globals": "^17.6.0",
|
|
77
|
+
"happy-dom": "^20.10.3",
|
|
78
|
+
"husky": "^9.1.7",
|
|
79
|
+
"lint-staged": "^17.0.7",
|
|
80
|
+
"postcss": "^8.5.3",
|
|
81
|
+
"prettier": "^3.8.4",
|
|
82
|
+
"tailwindcss": "^4.0.0",
|
|
83
|
+
"typescript-eslint": "^8.61.0"
|
|
61
84
|
}
|
|
62
85
|
}
|