veryfront 0.0.51 → 0.0.52
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/dist/ai/index.js +1 -1
- package/dist/ai/index.js.map +1 -1
- package/dist/ai/workflow.js +1 -1
- package/dist/ai/workflow.js.map +1 -1
- package/dist/cli.js +119 -25
- package/dist/components.js +2 -1
- package/dist/components.js.map +2 -2
- package/dist/config.js +1 -1
- package/dist/config.js.map +1 -1
- package/dist/data.js +1 -1
- package/dist/data.js.map +1 -1
- package/dist/index.js +6 -1
- package/dist/index.js.map +2 -2
- package/dist/integrations/_base/files/app/page.tsx +128 -0
- package/dist/integrations/gmail/files/lib/gmail-client.ts +2 -8
- package/dist/templates/ai/app/page.tsx +6 -1
- package/package.json +1 -1
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { Chat } from 'veryfront/ai/components'
|
|
4
|
+
import { useChat } from 'veryfront/ai/react'
|
|
5
|
+
import { ServiceConnections } from './components/ServiceConnections'
|
|
6
|
+
|
|
7
|
+
// Define services for this project - automatically populated by scaffolding
|
|
8
|
+
// Each integration adds its service here when installed
|
|
9
|
+
const SERVICES = [
|
|
10
|
+
// Services will be dynamically populated based on installed integrations
|
|
11
|
+
// For now, we fetch from the status API instead
|
|
12
|
+
]
|
|
13
|
+
|
|
14
|
+
export default function ChatPage() {
|
|
15
|
+
const chat = useChat({ api: '/api/chat' })
|
|
16
|
+
|
|
17
|
+
return (
|
|
18
|
+
<div className="flex flex-col h-screen bg-white dark:bg-neutral-900">
|
|
19
|
+
{/* Header - sticky at top, full width */}
|
|
20
|
+
<header className="sticky top-0 z-10 flex-shrink-0 border-b border-neutral-200 dark:border-neutral-800 bg-white dark:bg-neutral-900">
|
|
21
|
+
<div className="px-4 py-3 flex items-center justify-between">
|
|
22
|
+
<h1 className="font-medium text-neutral-900 dark:text-white">AI Assistant</h1>
|
|
23
|
+
<div className="flex items-center gap-4">
|
|
24
|
+
<IntegrationStatus />
|
|
25
|
+
<a
|
|
26
|
+
href="/setup"
|
|
27
|
+
className="text-sm text-neutral-500 hover:text-neutral-700 dark:text-neutral-400 dark:hover:text-neutral-200"
|
|
28
|
+
>
|
|
29
|
+
Setup
|
|
30
|
+
</a>
|
|
31
|
+
</div>
|
|
32
|
+
</div>
|
|
33
|
+
</header>
|
|
34
|
+
|
|
35
|
+
{/* Chat - fills remaining space with scrollable content */}
|
|
36
|
+
<Chat {...chat} className="flex-1 min-h-0" placeholder="Message" />
|
|
37
|
+
</div>
|
|
38
|
+
)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Component to show integration status from the API
|
|
42
|
+
function IntegrationStatus() {
|
|
43
|
+
// Note: ServiceConnections fetches from /api/auth/status
|
|
44
|
+
// We'll use the integrations/status API which has the full list
|
|
45
|
+
return <ServiceStatusFromAPI />
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
import { useEffect, useState } from 'react'
|
|
49
|
+
|
|
50
|
+
interface Integration {
|
|
51
|
+
id: string
|
|
52
|
+
name: string
|
|
53
|
+
connected: boolean
|
|
54
|
+
connectUrl: string
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function ServiceStatusFromAPI() {
|
|
58
|
+
const [integrations, setIntegrations] = useState<Integration[]>([])
|
|
59
|
+
const [loading, setLoading] = useState(true)
|
|
60
|
+
|
|
61
|
+
useEffect(() => {
|
|
62
|
+
async function fetchStatus() {
|
|
63
|
+
try {
|
|
64
|
+
const res = await fetch('/api/integrations/status')
|
|
65
|
+
if (res.ok) {
|
|
66
|
+
const data = await res.json()
|
|
67
|
+
setIntegrations(data.integrations || [])
|
|
68
|
+
}
|
|
69
|
+
} catch (error) {
|
|
70
|
+
console.error('Failed to fetch integration status:', error)
|
|
71
|
+
} finally {
|
|
72
|
+
setLoading(false)
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
fetchStatus()
|
|
76
|
+
}, [])
|
|
77
|
+
|
|
78
|
+
if (loading) {
|
|
79
|
+
return (
|
|
80
|
+
<div className="flex items-center gap-2">
|
|
81
|
+
<div className="animate-pulse h-6 w-24 bg-neutral-200 dark:bg-neutral-700 rounded-full" />
|
|
82
|
+
</div>
|
|
83
|
+
)
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if (integrations.length === 0) {
|
|
87
|
+
return null
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const connected = integrations.filter(i => i.connected)
|
|
91
|
+
const disconnected = integrations.filter(i => !i.connected)
|
|
92
|
+
|
|
93
|
+
return (
|
|
94
|
+
<div className="flex items-center gap-2">
|
|
95
|
+
{/* Show connected services as green badges */}
|
|
96
|
+
{connected.map(service => (
|
|
97
|
+
<span
|
|
98
|
+
key={service.id}
|
|
99
|
+
className="inline-flex items-center gap-1.5 px-2.5 py-1 rounded-full text-xs font-medium bg-green-100 text-green-800 dark:bg-green-900/30 dark:text-green-400"
|
|
100
|
+
title={`${service.name} connected`}
|
|
101
|
+
>
|
|
102
|
+
<span className="w-1.5 h-1.5 rounded-full bg-green-500" />
|
|
103
|
+
{service.name}
|
|
104
|
+
</span>
|
|
105
|
+
))}
|
|
106
|
+
|
|
107
|
+
{/* Show disconnected services as clickable grey badges */}
|
|
108
|
+
{disconnected.map(service => (
|
|
109
|
+
<a
|
|
110
|
+
key={service.id}
|
|
111
|
+
href={service.connectUrl}
|
|
112
|
+
className="inline-flex items-center gap-1.5 px-2.5 py-1 rounded-full text-xs font-medium bg-neutral-100 text-neutral-600 hover:bg-neutral-200 dark:bg-neutral-800 dark:text-neutral-400 dark:hover:bg-neutral-700 transition-colors"
|
|
113
|
+
title={`Connect ${service.name}`}
|
|
114
|
+
>
|
|
115
|
+
<span className="w-1.5 h-1.5 rounded-full bg-neutral-400" />
|
|
116
|
+
{service.name}
|
|
117
|
+
</a>
|
|
118
|
+
))}
|
|
119
|
+
|
|
120
|
+
{/* Show count if not all connected */}
|
|
121
|
+
{disconnected.length > 0 && (
|
|
122
|
+
<span className="text-xs text-neutral-500 dark:text-neutral-400">
|
|
123
|
+
{connected.length}/{integrations.length}
|
|
124
|
+
</span>
|
|
125
|
+
)}
|
|
126
|
+
</div>
|
|
127
|
+
)
|
|
128
|
+
}
|
|
@@ -46,18 +46,12 @@ const gmailService = new OAuthService(gmailConfig, memoryTokenStore);
|
|
|
46
46
|
* Create a Gmail client for API operations
|
|
47
47
|
*/
|
|
48
48
|
export function createGmailClient() {
|
|
49
|
+
// OAuthService.fetch() already handles auth, error checking, and JSON parsing
|
|
49
50
|
async function apiRequest<T>(
|
|
50
51
|
endpoint: string,
|
|
51
52
|
options: RequestInit = {},
|
|
52
53
|
): Promise<T> {
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
if (!response.ok) {
|
|
56
|
-
const error = await response.text();
|
|
57
|
-
throw new Error(`Gmail API error: ${response.status} - ${error}`);
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
return response.json();
|
|
54
|
+
return gmailService.fetch<T>(endpoint, options);
|
|
61
55
|
}
|
|
62
56
|
|
|
63
57
|
return {
|
|
@@ -12,7 +12,12 @@ export default function ChatPage() {
|
|
|
12
12
|
<header className="sticky top-0 z-10 flex-shrink-0 border-b border-neutral-200 dark:border-neutral-800 bg-white dark:bg-neutral-900">
|
|
13
13
|
<div className="px-4 py-3 flex items-center justify-between">
|
|
14
14
|
<h1 className="font-medium text-neutral-900 dark:text-white">AI Assistant</h1>
|
|
15
|
-
|
|
15
|
+
<a
|
|
16
|
+
href="/setup"
|
|
17
|
+
className="text-sm text-neutral-500 hover:text-neutral-700 dark:text-neutral-400 dark:hover:text-neutral-200"
|
|
18
|
+
>
|
|
19
|
+
Setup
|
|
20
|
+
</a>
|
|
16
21
|
</div>
|
|
17
22
|
</header>
|
|
18
23
|
|