@chaaskit/client 0.1.0 → 0.1.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/LICENSE +21 -0
- package/dist/lib/index.js +1023 -160
- package/dist/lib/index.js.map +1 -1
- package/dist/lib/routes/AcceptInviteRoute.js +1 -1
- package/dist/lib/routes/AcceptInviteRoute.js.map +1 -1
- package/dist/lib/routes/AdminDashboardRoute.js +1 -1
- package/dist/lib/routes/AdminDashboardRoute.js.map +1 -1
- package/dist/lib/routes/AdminPromoCodesRoute.js +19 -0
- package/dist/lib/routes/AdminPromoCodesRoute.js.map +1 -0
- package/dist/lib/routes/AdminTeamRoute.js +1 -1
- package/dist/lib/routes/AdminTeamRoute.js.map +1 -1
- package/dist/lib/routes/AdminTeamsRoute.js +1 -1
- package/dist/lib/routes/AdminTeamsRoute.js.map +1 -1
- package/dist/lib/routes/AdminUsersRoute.js +1 -1
- package/dist/lib/routes/AdminUsersRoute.js.map +1 -1
- package/dist/lib/routes/AdminWaitlistRoute.js +19 -0
- package/dist/lib/routes/AdminWaitlistRoute.js.map +1 -0
- package/dist/lib/routes/ApiKeysRoute.js +1 -1
- package/dist/lib/routes/ApiKeysRoute.js.map +1 -1
- package/dist/lib/routes/AutomationsRoute.js +1 -1
- package/dist/lib/routes/AutomationsRoute.js.map +1 -1
- package/dist/lib/routes/ChatRoute.js +1 -1
- package/dist/lib/routes/ChatRoute.js.map +1 -1
- package/dist/lib/routes/DocumentsRoute.js +1 -1
- package/dist/lib/routes/DocumentsRoute.js.map +1 -1
- package/dist/lib/routes/OAuthConsentRoute.js +1 -1
- package/dist/lib/routes/OAuthConsentRoute.js.map +1 -1
- package/dist/lib/routes/PricingRoute.js +1 -1
- package/dist/lib/routes/PricingRoute.js.map +1 -1
- package/dist/lib/routes/PrivacyRoute.js +1 -1
- package/dist/lib/routes/PrivacyRoute.js.map +1 -1
- package/dist/lib/routes/TeamSettingsRoute.js +1 -1
- package/dist/lib/routes/TeamSettingsRoute.js.map +1 -1
- package/dist/lib/routes/TermsRoute.js +1 -1
- package/dist/lib/routes/TermsRoute.js.map +1 -1
- package/dist/lib/routes/VerifyEmailRoute.js +1 -1
- package/dist/lib/routes/VerifyEmailRoute.js.map +1 -1
- package/dist/lib/routes.js +47 -37
- package/dist/lib/routes.js.map +1 -1
- package/dist/lib/ssr-utils.js +64 -1
- package/dist/lib/ssr-utils.js.map +1 -1
- package/dist/lib/ssr.js +23 -0
- package/dist/lib/ssr.js.map +1 -1
- package/dist/lib/styles.css +58 -62
- package/dist/lib/useExtensions-B5nX_8XD.js.map +1 -1
- package/package.json +25 -12
- package/src/components/MessageItem.tsx +35 -4
- package/src/components/MessageList.tsx +51 -5
- package/src/components/OAuthAppsSection.tsx +1 -1
- package/src/components/Sidebar.tsx +1 -3
- package/src/components/ToolCallDisplay.tsx +102 -11
- package/src/components/tool-renderers/DocumentListRenderer.tsx +44 -0
- package/src/components/tool-renderers/DocumentReadRenderer.tsx +33 -0
- package/src/components/tool-renderers/DocumentSaveRenderer.tsx +32 -0
- package/src/components/tool-renderers/DocumentSearchRenderer.tsx +33 -0
- package/src/components/tool-renderers/index.ts +36 -0
- package/src/components/tool-renderers/utils.ts +7 -0
- package/src/contexts/AuthContext.tsx +16 -6
- package/src/contexts/ConfigContext.tsx +60 -28
- package/src/contexts/ThemeContext.tsx +39 -68
- package/src/extensions/registry.ts +2 -1
- package/src/hooks/__tests__/basePath.test.ts +42 -0
- package/src/index.tsx +11 -2
- package/src/pages/AdminDashboardPage.tsx +15 -1
- package/src/pages/AdminPromoCodesPage.tsx +378 -0
- package/src/pages/AdminTeamPage.tsx +29 -1
- package/src/pages/AdminTeamsPage.tsx +15 -1
- package/src/pages/AdminUsersPage.tsx +15 -1
- package/src/pages/AdminWaitlistPage.tsx +156 -0
- package/src/pages/RegisterPage.tsx +91 -9
- package/src/routes/AcceptInviteRoute.tsx +1 -1
- package/src/routes/AdminDashboardRoute.tsx +1 -1
- package/src/routes/AdminPromoCodesRoute.tsx +24 -0
- package/src/routes/AdminTeamRoute.tsx +1 -1
- package/src/routes/AdminTeamsRoute.tsx +1 -1
- package/src/routes/AdminUsersRoute.tsx +1 -1
- package/src/routes/AdminWaitlistRoute.tsx +24 -0
- package/src/routes/ApiKeysRoute.tsx +1 -1
- package/src/routes/AutomationsRoute.tsx +1 -1
- package/src/routes/ChatRoute.tsx +2 -1
- package/src/routes/DocumentsRoute.tsx +1 -1
- package/src/routes/OAuthConsentRoute.tsx +1 -1
- package/src/routes/PricingRoute.tsx +1 -1
- package/src/routes/PrivacyRoute.tsx +1 -1
- package/src/routes/TeamSettingsRoute.tsx +1 -1
- package/src/routes/TermsRoute.tsx +1 -1
- package/src/routes/VerifyEmailRoute.tsx +1 -1
- package/src/routes/index.ts +2 -0
- package/src/ssr-utils.tsx +100 -1
- package/src/ssr.ts +59 -0
- package/src/stores/chatStore.ts +5 -0
- package/src/styles/index.css +16 -63
- package/src/tailwind-preset.js +360 -0
- package/dist/favicon.svg +0 -11
- package/dist/index.html +0 -17
- package/dist/logo.svg +0 -12
|
@@ -19,6 +19,8 @@ const AUTO_APPROVE_LABELS: Record<AutoApproveReason, string> = {
|
|
|
19
19
|
thread_allowed: 'Allowed for this chat',
|
|
20
20
|
};
|
|
21
21
|
|
|
22
|
+
const TOOL_UI_MESSAGE_SOURCE = 'chaaskit-tool-ui';
|
|
23
|
+
|
|
22
24
|
// Generate the window.openai initialization script for OpenAI format resources
|
|
23
25
|
function generateOpenAiScript(
|
|
24
26
|
toolInput: Record<string, unknown>,
|
|
@@ -68,6 +70,46 @@ function generateOpenAiScript(
|
|
|
68
70
|
</style>
|
|
69
71
|
<script>
|
|
70
72
|
(function() {
|
|
73
|
+
const MESSAGE_SOURCE = '${TOOL_UI_MESSAGE_SOURCE}';
|
|
74
|
+
let requestId = 0;
|
|
75
|
+
const pending = new Map();
|
|
76
|
+
|
|
77
|
+
function resolvePending(id, data) {
|
|
78
|
+
const entry = pending.get(id);
|
|
79
|
+
if (!entry) return;
|
|
80
|
+
pending.delete(id);
|
|
81
|
+
if (data && data.ok) {
|
|
82
|
+
entry.resolve(data.result);
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
entry.resolve({ error: (data && data.error) || 'Tool UI bridge error' });
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
window.addEventListener('message', (event) => {
|
|
89
|
+
const data = event.data || {};
|
|
90
|
+
if (data.source !== MESSAGE_SOURCE || !data.id) return;
|
|
91
|
+
resolvePending(data.id, data);
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
function sendToParent(type, payload, timeoutMs = 15000) {
|
|
95
|
+
return new Promise((resolve) => {
|
|
96
|
+
const id = String(Date.now()) + '-' + String(requestId++);
|
|
97
|
+
pending.set(id, { resolve });
|
|
98
|
+
try {
|
|
99
|
+
window.parent.postMessage({ source: MESSAGE_SOURCE, type, id, payload }, '*');
|
|
100
|
+
} catch (err) {
|
|
101
|
+
pending.delete(id);
|
|
102
|
+
resolve({ error: 'Unable to reach host window' });
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
setTimeout(() => {
|
|
106
|
+
if (!pending.has(id)) return;
|
|
107
|
+
pending.delete(id);
|
|
108
|
+
resolve({ error: 'Tool UI request timed out' });
|
|
109
|
+
}, timeoutMs);
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
|
|
71
113
|
// Initialize window.openai with the OpenAI Apps SDK spec
|
|
72
114
|
window.openai = {
|
|
73
115
|
// Core globals
|
|
@@ -92,17 +134,20 @@ function generateOpenAiScript(
|
|
|
92
134
|
// API methods
|
|
93
135
|
callTool: async (name, args) => {
|
|
94
136
|
console.log('window.openai.callTool called:', { name, args });
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
137
|
+
const response = await sendToParent('callTool', { name, args });
|
|
138
|
+
if (response && response.error) {
|
|
139
|
+
return {
|
|
140
|
+
content: [{ type: 'text', text: response.error }],
|
|
141
|
+
isError: true
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
return response || { content: [{ type: 'text', text: 'No response from host' }], isError: true };
|
|
100
145
|
},
|
|
101
146
|
|
|
102
147
|
sendFollowUpMessage: async (args) => {
|
|
103
148
|
console.log('window.openai.sendFollowUpMessage called:', args);
|
|
104
|
-
|
|
105
|
-
return {};
|
|
149
|
+
const response = await sendToParent('sendFollowUpMessage', args);
|
|
150
|
+
return response && response.error ? { error: response.error } : (response || {});
|
|
106
151
|
},
|
|
107
152
|
|
|
108
153
|
openExternal: (payload) => {
|
|
@@ -114,27 +159,41 @@ function generateOpenAiScript(
|
|
|
114
159
|
|
|
115
160
|
requestDisplayMode: async (args) => {
|
|
116
161
|
console.log('window.openai.requestDisplayMode called:', args);
|
|
117
|
-
|
|
162
|
+
const response = await sendToParent('requestDisplayMode', args);
|
|
163
|
+
if (response && response.error) {
|
|
164
|
+
return { mode: args.mode };
|
|
165
|
+
}
|
|
166
|
+
return response || { mode: args.mode };
|
|
118
167
|
},
|
|
119
168
|
|
|
120
169
|
setWidgetState: async (state) => {
|
|
121
170
|
console.log('window.openai.setWidgetState called:', state);
|
|
122
171
|
window.openai.widgetState = state;
|
|
123
|
-
|
|
172
|
+
const response = await sendToParent('setWidgetState', state);
|
|
173
|
+
return response && response.error ? {} : (response || {});
|
|
124
174
|
},
|
|
125
175
|
|
|
126
176
|
requestClose: () => {
|
|
127
177
|
console.log('window.openai.requestClose called');
|
|
178
|
+
sendToParent('requestClose', {});
|
|
128
179
|
},
|
|
129
180
|
|
|
130
181
|
getFileDownloadUrl: async ({ fileId }) => {
|
|
131
182
|
console.log('window.openai.getFileDownloadUrl called:', fileId);
|
|
132
|
-
|
|
183
|
+
const response = await sendToParent('getFileDownloadUrl', { fileId });
|
|
184
|
+
if (response && response.error) {
|
|
185
|
+
return { url: '' };
|
|
186
|
+
}
|
|
187
|
+
return response || { url: '' };
|
|
133
188
|
},
|
|
134
189
|
|
|
135
190
|
uploadFile: async (file) => {
|
|
136
191
|
console.log('window.openai.uploadFile called:', file);
|
|
137
|
-
|
|
192
|
+
const response = await sendToParent('uploadFile', { file });
|
|
193
|
+
if (response && response.error) {
|
|
194
|
+
return { fileId: '' };
|
|
195
|
+
}
|
|
196
|
+
return response || { fileId: '' };
|
|
138
197
|
}
|
|
139
198
|
};
|
|
140
199
|
|
|
@@ -199,6 +258,38 @@ export function UIResourceWidget({ uiResource, theme }: { uiResource: UIResource
|
|
|
199
258
|
}
|
|
200
259
|
|
|
201
260
|
export default function ToolCallDisplay({ toolCall, toolResult, isPending, uiResource, hideUiResource, autoApproveReason }: ToolCallDisplayProps) {
|
|
261
|
+
useEffect(() => {
|
|
262
|
+
async function handleMessage(event: MessageEvent) {
|
|
263
|
+
const data = event.data || {};
|
|
264
|
+
if (data.source !== TOOL_UI_MESSAGE_SOURCE || !data.id) return;
|
|
265
|
+
|
|
266
|
+
const targetOrigin = event.origin && event.origin !== 'null' ? event.origin : '*';
|
|
267
|
+
|
|
268
|
+
try {
|
|
269
|
+
const handler = (window as unknown as { chaaskitToolUiHandler?: (payload: unknown) => Promise<unknown> }).chaaskitToolUiHandler;
|
|
270
|
+
const result = typeof handler === 'function'
|
|
271
|
+
? await handler({ type: data.type, payload: data.payload })
|
|
272
|
+
: { error: 'Tool UI bridge not configured' };
|
|
273
|
+
|
|
274
|
+
const sourceWindow = event.source as Window | null;
|
|
275
|
+
sourceWindow?.postMessage(
|
|
276
|
+
{ source: TOOL_UI_MESSAGE_SOURCE, id: data.id, ok: true, result },
|
|
277
|
+
targetOrigin
|
|
278
|
+
);
|
|
279
|
+
} catch (err) {
|
|
280
|
+
const message = err instanceof Error ? err.message : 'Tool UI bridge error';
|
|
281
|
+
const sourceWindow = event.source as Window | null;
|
|
282
|
+
sourceWindow?.postMessage(
|
|
283
|
+
{ source: TOOL_UI_MESSAGE_SOURCE, id: data.id, ok: false, error: message },
|
|
284
|
+
targetOrigin
|
|
285
|
+
);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
window.addEventListener('message', handleMessage);
|
|
290
|
+
return () => window.removeEventListener('message', handleMessage);
|
|
291
|
+
}, []);
|
|
292
|
+
|
|
202
293
|
// Check if we have HTML content to render
|
|
203
294
|
const hasHtmlResource = !hideUiResource && uiResource?.text &&
|
|
204
295
|
(uiResource.mimeType?.includes('html') || uiResource.text.trim().startsWith('<'));
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { ToolCall, ToolResult } from '@chaaskit/shared';
|
|
2
|
+
import { getTextContent } from './utils';
|
|
3
|
+
|
|
4
|
+
interface DocumentSummary {
|
|
5
|
+
id?: string;
|
|
6
|
+
path?: string;
|
|
7
|
+
name?: string;
|
|
8
|
+
mimeType?: string;
|
|
9
|
+
charCount?: number;
|
|
10
|
+
teamId?: string | null;
|
|
11
|
+
projectId?: string | null;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export default function DocumentListRenderer({ toolCall, toolResult }: { toolCall: ToolCall; toolResult: ToolResult }) {
|
|
15
|
+
const structured = toolResult.structuredContent as { documents?: DocumentSummary[] } | undefined;
|
|
16
|
+
const documents = structured?.documents ?? [];
|
|
17
|
+
const fallback = getTextContent(toolResult.content);
|
|
18
|
+
|
|
19
|
+
return (
|
|
20
|
+
<div className="space-y-2">
|
|
21
|
+
<div className="text-sm font-semibold text-text-primary">Documents</div>
|
|
22
|
+
{documents.length > 0 ? (
|
|
23
|
+
<ul className="space-y-2">
|
|
24
|
+
{documents.map((doc, index) => (
|
|
25
|
+
<li key={doc.id ?? doc.path ?? index} className="rounded-md border border-border bg-background px-3 py-2">
|
|
26
|
+
<div className="text-sm font-medium text-text-primary">{doc.path ?? doc.name ?? 'Untitled document'}</div>
|
|
27
|
+
<div className="text-xs text-text-muted">
|
|
28
|
+
{doc.mimeType ?? 'text/plain'}
|
|
29
|
+
{typeof doc.charCount === 'number' ? ` • ${doc.charCount} chars` : ''}
|
|
30
|
+
{doc.teamId ? ' • team' : ''}
|
|
31
|
+
{doc.projectId ? ' • project' : ''}
|
|
32
|
+
</div>
|
|
33
|
+
</li>
|
|
34
|
+
))}
|
|
35
|
+
</ul>
|
|
36
|
+
) : (
|
|
37
|
+
<div className="text-sm text-text-secondary">
|
|
38
|
+
{fallback ?? 'No documents found.'}
|
|
39
|
+
</div>
|
|
40
|
+
)}
|
|
41
|
+
<div className="text-xs text-text-muted">Tool: {toolCall.toolName}</div>
|
|
42
|
+
</div>
|
|
43
|
+
);
|
|
44
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { ToolCall, ToolResult } from '@chaaskit/shared';
|
|
2
|
+
import { getTextContent } from './utils';
|
|
3
|
+
|
|
4
|
+
interface ReadSummary {
|
|
5
|
+
path?: string;
|
|
6
|
+
offset?: number;
|
|
7
|
+
linesReturned?: number;
|
|
8
|
+
totalLines?: number;
|
|
9
|
+
truncated?: boolean;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export default function DocumentReadRenderer({ toolCall, toolResult }: { toolCall: ToolCall; toolResult: ToolResult }) {
|
|
13
|
+
const structured = toolResult.structuredContent as ReadSummary | undefined;
|
|
14
|
+
const fallback = getTextContent(toolResult.content);
|
|
15
|
+
const rangeText = structured
|
|
16
|
+
? `Lines ${Number(structured.offset ?? 0) + 1}-${Number(structured.offset ?? 0) + Number(structured.linesReturned ?? 0)} of ${structured.totalLines ?? 'unknown'}`
|
|
17
|
+
: null;
|
|
18
|
+
|
|
19
|
+
return (
|
|
20
|
+
<div className="space-y-2">
|
|
21
|
+
<div className="text-sm font-semibold text-text-primary">Document Preview</div>
|
|
22
|
+
{structured ? (
|
|
23
|
+
<div className="rounded-md border border-border bg-background px-3 py-2 text-sm text-text-secondary">
|
|
24
|
+
<div className="font-medium text-text-primary">{structured.path ?? 'Document'}</div>
|
|
25
|
+
{rangeText && <div className="text-xs text-text-muted">{rangeText}{structured.truncated ? ' (truncated)' : ''}</div>}
|
|
26
|
+
</div>
|
|
27
|
+
) : (
|
|
28
|
+
<div className="text-sm text-text-secondary">{fallback ?? 'No structured document details.'}</div>
|
|
29
|
+
)}
|
|
30
|
+
<div className="text-xs text-text-muted">Tool: {toolCall.toolName}</div>
|
|
31
|
+
</div>
|
|
32
|
+
);
|
|
33
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { ToolCall, ToolResult } from '@chaaskit/shared';
|
|
2
|
+
import { getTextContent } from './utils';
|
|
3
|
+
|
|
4
|
+
interface SaveSummary {
|
|
5
|
+
success?: boolean;
|
|
6
|
+
path?: string;
|
|
7
|
+
id?: string;
|
|
8
|
+
charCount?: number;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export default function DocumentSaveRenderer({ toolCall, toolResult }: { toolCall: ToolCall; toolResult: ToolResult }) {
|
|
12
|
+
const structured = toolResult.structuredContent as SaveSummary | undefined;
|
|
13
|
+
const fallback = getTextContent(toolResult.content);
|
|
14
|
+
const success = structured?.success !== false && !toolResult.isError;
|
|
15
|
+
|
|
16
|
+
return (
|
|
17
|
+
<div className="space-y-2">
|
|
18
|
+
<div className="text-sm font-semibold text-text-primary">Save Document</div>
|
|
19
|
+
{structured ? (
|
|
20
|
+
<div className={`rounded-md border px-3 py-2 text-sm ${success ? 'border-success/30 bg-success/10 text-success' : 'border-error/30 bg-error/10 text-error'}`}>
|
|
21
|
+
<div className="font-medium">{success ? 'Saved' : 'Failed'}</div>
|
|
22
|
+
{structured.path && <div className="text-xs">Path: {structured.path}</div>}
|
|
23
|
+
{structured.id && <div className="text-xs">ID: {structured.id}</div>}
|
|
24
|
+
{typeof structured.charCount === 'number' && <div className="text-xs">Size: {structured.charCount} chars</div>}
|
|
25
|
+
</div>
|
|
26
|
+
) : (
|
|
27
|
+
<div className="text-sm text-text-secondary">{fallback ?? 'No structured save data.'}</div>
|
|
28
|
+
)}
|
|
29
|
+
<div className="text-xs text-text-muted">Tool: {toolCall.toolName}</div>
|
|
30
|
+
</div>
|
|
31
|
+
);
|
|
32
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { ToolCall, ToolResult } from '@chaaskit/shared';
|
|
2
|
+
import { getTextContent } from './utils';
|
|
3
|
+
|
|
4
|
+
interface SearchSummary {
|
|
5
|
+
path?: string;
|
|
6
|
+
query?: string;
|
|
7
|
+
matchCount?: number;
|
|
8
|
+
matchLines?: number[];
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export default function DocumentSearchRenderer({ toolCall, toolResult }: { toolCall: ToolCall; toolResult: ToolResult }) {
|
|
12
|
+
const structured = toolResult.structuredContent as SearchSummary | undefined;
|
|
13
|
+
const fallback = getTextContent(toolResult.content);
|
|
14
|
+
|
|
15
|
+
return (
|
|
16
|
+
<div className="space-y-2">
|
|
17
|
+
<div className="text-sm font-semibold text-text-primary">Document Search</div>
|
|
18
|
+
{structured ? (
|
|
19
|
+
<div className="rounded-md border border-border bg-background px-3 py-2 text-sm text-text-secondary">
|
|
20
|
+
<div className="font-medium text-text-primary">{structured.path ?? 'Document'}</div>
|
|
21
|
+
<div className="text-xs text-text-muted">Query: {structured.query ?? 'unknown'}</div>
|
|
22
|
+
<div className="text-xs text-text-muted">Matches: {structured.matchCount ?? 0}</div>
|
|
23
|
+
{structured.matchLines && structured.matchLines.length > 0 && (
|
|
24
|
+
<div className="text-xs text-text-muted">Lines: {structured.matchLines.join(', ')}</div>
|
|
25
|
+
)}
|
|
26
|
+
</div>
|
|
27
|
+
) : (
|
|
28
|
+
<div className="text-sm text-text-secondary">{fallback ?? 'No structured search data.'}</div>
|
|
29
|
+
)}
|
|
30
|
+
<div className="text-xs text-text-muted">Tool: {toolCall.toolName}</div>
|
|
31
|
+
</div>
|
|
32
|
+
);
|
|
33
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { clientRegistry } from '../../extensions/registry';
|
|
2
|
+
import DocumentListRenderer from './DocumentListRenderer';
|
|
3
|
+
import DocumentReadRenderer from './DocumentReadRenderer';
|
|
4
|
+
import DocumentSearchRenderer from './DocumentSearchRenderer';
|
|
5
|
+
import DocumentSaveRenderer from './DocumentSaveRenderer';
|
|
6
|
+
|
|
7
|
+
clientRegistry.registerTool({
|
|
8
|
+
name: 'list_documents',
|
|
9
|
+
description: 'Render document list results',
|
|
10
|
+
resultRenderer: DocumentListRenderer,
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
clientRegistry.registerTool({
|
|
14
|
+
name: 'read_document',
|
|
15
|
+
description: 'Render document read results',
|
|
16
|
+
resultRenderer: DocumentReadRenderer,
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
clientRegistry.registerTool({
|
|
20
|
+
name: 'search_in_document',
|
|
21
|
+
description: 'Render document search results',
|
|
22
|
+
resultRenderer: DocumentSearchRenderer,
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
clientRegistry.registerTool({
|
|
26
|
+
name: 'save_document',
|
|
27
|
+
description: 'Render document save results',
|
|
28
|
+
resultRenderer: DocumentSaveRenderer,
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
export {
|
|
32
|
+
DocumentListRenderer,
|
|
33
|
+
DocumentReadRenderer,
|
|
34
|
+
DocumentSearchRenderer,
|
|
35
|
+
DocumentSaveRenderer,
|
|
36
|
+
};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { MCPContent } from '@chaaskit/shared';
|
|
2
|
+
|
|
3
|
+
export function getTextContent(content?: MCPContent[]): string | null {
|
|
4
|
+
if (!content) return null;
|
|
5
|
+
const firstText = content.find((item) => item.type === 'text' && typeof item.text === 'string');
|
|
6
|
+
return firstText?.text ?? null;
|
|
7
|
+
}
|
|
@@ -20,9 +20,14 @@ interface AuthContextType {
|
|
|
20
20
|
user: UserSession | null;
|
|
21
21
|
isLoading: boolean;
|
|
22
22
|
login: (email: string, password: string) => Promise<LoginResult>;
|
|
23
|
-
register: (
|
|
23
|
+
register: (
|
|
24
|
+
email: string,
|
|
25
|
+
password: string,
|
|
26
|
+
name?: string,
|
|
27
|
+
options?: { inviteToken?: string; referralCode?: string }
|
|
28
|
+
) => Promise<RegisterResult>;
|
|
24
29
|
logout: () => Promise<void>;
|
|
25
|
-
sendMagicLink: (email: string) => Promise<void>;
|
|
30
|
+
sendMagicLink: (email: string, inviteToken?: string) => Promise<void>;
|
|
26
31
|
verifyEmail: (code: string) => Promise<void>;
|
|
27
32
|
resendVerification: () => Promise<void>;
|
|
28
33
|
}
|
|
@@ -59,12 +64,17 @@ export function AuthProvider({ children }: { children: ReactNode }) {
|
|
|
59
64
|
return { requiresVerification: response.requiresVerification ?? false };
|
|
60
65
|
}
|
|
61
66
|
|
|
62
|
-
async function register(
|
|
67
|
+
async function register(
|
|
68
|
+
email: string,
|
|
69
|
+
password: string,
|
|
70
|
+
name?: string,
|
|
71
|
+
options?: { inviteToken?: string; referralCode?: string }
|
|
72
|
+
): Promise<RegisterResult> {
|
|
63
73
|
const response = await api.post<{
|
|
64
74
|
user: UserSession;
|
|
65
75
|
token: string;
|
|
66
76
|
requiresVerification?: boolean;
|
|
67
|
-
}>('/api/auth/register', { email, password, name });
|
|
77
|
+
}>('/api/auth/register', { email, password, name, ...options });
|
|
68
78
|
setUser(response.user);
|
|
69
79
|
return { requiresVerification: response.requiresVerification ?? false };
|
|
70
80
|
}
|
|
@@ -74,8 +84,8 @@ export function AuthProvider({ children }: { children: ReactNode }) {
|
|
|
74
84
|
setUser(null);
|
|
75
85
|
}
|
|
76
86
|
|
|
77
|
-
async function sendMagicLink(email: string) {
|
|
78
|
-
await api.post('/api/auth/magic-link', { email });
|
|
87
|
+
async function sendMagicLink(email: string, inviteToken?: string) {
|
|
88
|
+
await api.post('/api/auth/magic-link', { email, inviteToken });
|
|
79
89
|
}
|
|
80
90
|
|
|
81
91
|
async function verifyEmail(code: string) {
|
|
@@ -1,12 +1,32 @@
|
|
|
1
1
|
import { createContext, useContext, useState, useEffect, type ReactNode } from 'react';
|
|
2
|
-
import type {
|
|
2
|
+
import type { PublicAppConfig } from '@chaaskit/shared';
|
|
3
|
+
|
|
4
|
+
// Declare global window property for SSR-injected config
|
|
5
|
+
declare global {
|
|
6
|
+
interface Window {
|
|
7
|
+
__CHAASKIT_CONFIG__?: PublicAppConfig;
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Get config injected via ConfigScript in the HTML head.
|
|
13
|
+
* This allows the config to be available immediately on page load,
|
|
14
|
+
* avoiding flash of default values.
|
|
15
|
+
*/
|
|
16
|
+
function getInjectedConfig(): PublicAppConfig | undefined {
|
|
17
|
+
if (typeof window !== 'undefined' && window.__CHAASKIT_CONFIG__) {
|
|
18
|
+
return window.__CHAASKIT_CONFIG__;
|
|
19
|
+
}
|
|
20
|
+
return undefined;
|
|
21
|
+
}
|
|
3
22
|
|
|
4
23
|
// Default config - used as fallback while loading
|
|
5
|
-
const defaultConfig:
|
|
24
|
+
const defaultConfig: PublicAppConfig = {
|
|
6
25
|
app: {
|
|
7
26
|
name: 'AI Chat',
|
|
8
27
|
description: 'Your AI assistant',
|
|
9
28
|
url: 'http://localhost:5173',
|
|
29
|
+
basePath: '/chat',
|
|
10
30
|
},
|
|
11
31
|
ui: {
|
|
12
32
|
welcomeTitle: 'Welcome to AI Chat',
|
|
@@ -90,27 +110,15 @@ const defaultConfig: AppConfig = {
|
|
|
90
110
|
enabled: true,
|
|
91
111
|
expiresInMinutes: 15,
|
|
92
112
|
},
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
systemPrompt: 'You are a helpful AI assistant.',
|
|
99
|
-
maxTokens: 4096,
|
|
113
|
+
gating: {
|
|
114
|
+
mode: 'open',
|
|
115
|
+
waitlistEnabled: false,
|
|
116
|
+
},
|
|
117
|
+
isAdmin: false,
|
|
100
118
|
},
|
|
101
119
|
payments: {
|
|
102
120
|
enabled: false,
|
|
103
121
|
provider: 'stripe',
|
|
104
|
-
plans: [
|
|
105
|
-
{
|
|
106
|
-
id: 'free',
|
|
107
|
-
name: 'Free',
|
|
108
|
-
type: 'free',
|
|
109
|
-
params: {
|
|
110
|
-
monthlyMessageLimit: 20,
|
|
111
|
-
},
|
|
112
|
-
},
|
|
113
|
-
],
|
|
114
122
|
},
|
|
115
123
|
legal: {
|
|
116
124
|
privacyPolicyUrl: '/privacy',
|
|
@@ -139,7 +147,6 @@ const defaultConfig: AppConfig = {
|
|
|
139
147
|
},
|
|
140
148
|
promptTemplates: {
|
|
141
149
|
enabled: true,
|
|
142
|
-
builtIn: [],
|
|
143
150
|
allowUserTemplates: true,
|
|
144
151
|
},
|
|
145
152
|
teams: {
|
|
@@ -147,9 +154,6 @@ const defaultConfig: AppConfig = {
|
|
|
147
154
|
},
|
|
148
155
|
documents: {
|
|
149
156
|
enabled: false,
|
|
150
|
-
storage: {
|
|
151
|
-
provider: 'database',
|
|
152
|
-
},
|
|
153
157
|
maxFileSizeMB: 10,
|
|
154
158
|
hybridThreshold: 1000,
|
|
155
159
|
acceptedTypes: ['text/plain', 'text/markdown', 'application/json'],
|
|
@@ -161,10 +165,19 @@ const defaultConfig: AppConfig = {
|
|
|
161
165
|
api: {
|
|
162
166
|
enabled: false,
|
|
163
167
|
},
|
|
168
|
+
credits: {
|
|
169
|
+
enabled: false,
|
|
170
|
+
expiryEnabled: false,
|
|
171
|
+
promoEnabled: false,
|
|
172
|
+
},
|
|
173
|
+
metering: {
|
|
174
|
+
enabled: false,
|
|
175
|
+
recordPromptCompletion: true,
|
|
176
|
+
},
|
|
164
177
|
};
|
|
165
178
|
|
|
166
179
|
interface ConfigContextValue {
|
|
167
|
-
config:
|
|
180
|
+
config: PublicAppConfig;
|
|
168
181
|
configLoaded: boolean;
|
|
169
182
|
}
|
|
170
183
|
|
|
@@ -173,11 +186,30 @@ const ConfigContext = createContext<ConfigContextValue>({
|
|
|
173
186
|
configLoaded: false,
|
|
174
187
|
});
|
|
175
188
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
189
|
+
interface ConfigProviderProps {
|
|
190
|
+
children: ReactNode;
|
|
191
|
+
/**
|
|
192
|
+
* Initial config to use immediately, avoiding a flash of default values.
|
|
193
|
+
* If provided, the config will not be fetched from /api/config.
|
|
194
|
+
* Useful when config is available from SSR loaders.
|
|
195
|
+
*/
|
|
196
|
+
initialConfig?: PublicAppConfig;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
export function ConfigProvider({ children, initialConfig }: ConfigProviderProps) {
|
|
200
|
+
// Priority: 1. initialConfig prop, 2. injected window config, 3. defaults + fetch
|
|
201
|
+
const injectedConfig = getInjectedConfig();
|
|
202
|
+
const preloadedConfig = initialConfig || injectedConfig;
|
|
203
|
+
|
|
204
|
+
const [config, setConfig] = useState<PublicAppConfig>(
|
|
205
|
+
preloadedConfig ? { ...defaultConfig, ...preloadedConfig } : defaultConfig
|
|
206
|
+
);
|
|
207
|
+
const [configLoaded, setConfigLoaded] = useState(!!preloadedConfig);
|
|
179
208
|
|
|
180
209
|
useEffect(() => {
|
|
210
|
+
// Skip fetching if we have preloaded config
|
|
211
|
+
if (preloadedConfig) return;
|
|
212
|
+
|
|
181
213
|
async function loadConfig() {
|
|
182
214
|
try {
|
|
183
215
|
const response = await fetch('/api/config');
|
|
@@ -196,7 +228,7 @@ export function ConfigProvider({ children }: { children: ReactNode }) {
|
|
|
196
228
|
}
|
|
197
229
|
}
|
|
198
230
|
loadConfig();
|
|
199
|
-
}, []);
|
|
231
|
+
}, [preloadedConfig]);
|
|
200
232
|
|
|
201
233
|
return (
|
|
202
234
|
<ConfigContext.Provider value={{ config, configLoaded }}>
|