@agentuity/workbench 0.0.93 → 0.0.95
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/components/App.d.ts.map +1 -1
- package/dist/components/App.js +3 -1
- package/dist/components/App.js.map +1 -1
- package/dist/components/internal/Chat.d.ts +3 -1
- package/dist/components/internal/Chat.d.ts.map +1 -1
- package/dist/components/internal/Chat.js +5 -5
- package/dist/components/internal/Chat.js.map +1 -1
- package/dist/components/internal/Header.d.ts +4 -0
- package/dist/components/internal/Header.d.ts.map +1 -1
- package/dist/components/internal/Header.js +1 -1
- package/dist/components/internal/Header.js.map +1 -1
- package/dist/components/internal/InputSection.d.ts.map +1 -1
- package/dist/components/internal/InputSection.js +10 -10
- package/dist/components/internal/InputSection.js.map +1 -1
- package/dist/components/internal/MonacoJsonEditor.d.ts.map +1 -1
- package/dist/components/internal/MonacoJsonEditor.js +10 -1
- package/dist/components/internal/MonacoJsonEditor.js.map +1 -1
- package/dist/components/internal/WorkbenchProvider.d.ts +6 -2
- package/dist/components/internal/WorkbenchProvider.d.ts.map +1 -1
- package/dist/components/internal/WorkbenchProvider.js +117 -46
- package/dist/components/internal/WorkbenchProvider.js.map +1 -1
- package/dist/hooks/useAgentSchemas.d.ts.map +1 -1
- package/dist/hooks/useAgentSchemas.js +17 -4
- package/dist/hooks/useAgentSchemas.js.map +1 -1
- package/dist/hooks/useWorkbenchWebsocket.d.ts +1 -0
- package/dist/hooks/useWorkbenchWebsocket.d.ts.map +1 -1
- package/dist/hooks/useWorkbenchWebsocket.js +1 -1
- package/dist/hooks/useWorkbenchWebsocket.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/types/config.d.ts +3 -0
- package/dist/types/config.d.ts.map +1 -1
- package/package.json +3 -4
- package/src/components/App.tsx +9 -4
- package/src/components/internal/Chat.tsx +100 -94
- package/src/components/internal/Header.tsx +1 -1
- package/src/components/internal/InputSection.tsx +107 -69
- package/src/components/internal/MonacoJsonEditor.tsx +8 -1
- package/src/components/internal/WorkbenchProvider.tsx +132 -50
- package/src/hooks/useAgentSchemas.ts +16 -4
- package/src/hooks/useWorkbenchWebsocket.ts +2 -1
- package/src/index.ts +2 -1
- package/src/types/config.ts +3 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, { createContext, useContext, useEffect, useState,
|
|
1
|
+
import React, { createContext, useContext, useEffect, useState, useCallback } from 'react';
|
|
2
2
|
import type { UIMessage } from 'ai';
|
|
3
3
|
import type { WorkbenchConfig } from '@agentuity/core/workbench';
|
|
4
4
|
import type { WorkbenchContextType, ConnectionStatus } from '../../types/config';
|
|
@@ -18,29 +18,22 @@ export function useWorkbench() {
|
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
interface WorkbenchProviderProps {
|
|
21
|
-
config: WorkbenchConfig
|
|
21
|
+
config: Omit<WorkbenchConfig, 'route'> & {
|
|
22
|
+
baseUrl?: string | null;
|
|
23
|
+
projectId?: string;
|
|
24
|
+
};
|
|
25
|
+
isAuthenticated: boolean;
|
|
22
26
|
children: React.ReactNode;
|
|
23
27
|
}
|
|
24
28
|
|
|
25
|
-
export function WorkbenchProvider({ config, children }: WorkbenchProviderProps) {
|
|
29
|
+
export function WorkbenchProvider({ config, isAuthenticated, children }: WorkbenchProviderProps) {
|
|
26
30
|
const logger = useLogger('WorkbenchProvider');
|
|
27
31
|
|
|
28
|
-
// Generate project identifier from config for localStorage scoping
|
|
29
|
-
const projectId = useMemo(() => {
|
|
30
|
-
// Use a combination of baseUrl and apiKey hash to create unique project identifier
|
|
31
|
-
const configHash = btoa(
|
|
32
|
-
JSON.stringify({
|
|
33
|
-
route: config.route,
|
|
34
|
-
apiKey: config.apiKey?.substring(0, 8), // Only first 8 chars for uniqueness without exposing key
|
|
35
|
-
})
|
|
36
|
-
)
|
|
37
|
-
.replace(/[^a-zA-Z0-9]/g, '')
|
|
38
|
-
.substring(0, 16);
|
|
39
|
-
return `project_${configHash}`;
|
|
40
|
-
}, [config]);
|
|
41
|
-
|
|
42
32
|
// localStorage utilities scoped by project
|
|
43
|
-
const getStorageKey = useCallback(
|
|
33
|
+
const getStorageKey = useCallback(
|
|
34
|
+
(key: string) => `agentuity_workbench_${config.projectId}_${key}`,
|
|
35
|
+
[config.projectId]
|
|
36
|
+
);
|
|
44
37
|
|
|
45
38
|
const saveSelectedAgent = useCallback(
|
|
46
39
|
(agentId: string) => {
|
|
@@ -66,22 +59,36 @@ export function WorkbenchProvider({ config, children }: WorkbenchProviderProps)
|
|
|
66
59
|
const [selectedAgent, setSelectedAgent] = useState<string>('');
|
|
67
60
|
const [inputMode, setInputMode] = useState<'text' | 'form'>('text');
|
|
68
61
|
const [isLoading, setIsLoading] = useState(false);
|
|
69
|
-
const [
|
|
62
|
+
const [isGeneratingSample, setIsGeneratingSample] = useState(false);
|
|
63
|
+
const [connectionStatus, setConnectionStatus] = useState<ConnectionStatus>('connected'); // Default to connected when websocket is disabled
|
|
70
64
|
|
|
71
65
|
// Config values
|
|
72
|
-
const baseUrl = config.baseUrl
|
|
66
|
+
const baseUrl = config.baseUrl === undefined ? defaultBaseUrl : config.baseUrl;
|
|
73
67
|
const apiKey = config.apiKey;
|
|
74
|
-
const
|
|
68
|
+
const isBaseUrlNull = config.baseUrl === null;
|
|
75
69
|
|
|
76
|
-
//
|
|
70
|
+
// Log baseUrl state
|
|
77
71
|
useEffect(() => {
|
|
78
|
-
if (
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
72
|
+
if (isBaseUrlNull) {
|
|
73
|
+
logger.debug('🚫 baseUrl is null - disabling API calls and websocket');
|
|
74
|
+
} else {
|
|
75
|
+
logger.debug('✅ baseUrl configured:', baseUrl);
|
|
76
|
+
}
|
|
77
|
+
}, [isBaseUrlNull, baseUrl, logger]);
|
|
78
|
+
|
|
79
|
+
// Set disconnected status if baseUrl is null
|
|
80
|
+
useEffect(() => {
|
|
81
|
+
if (isBaseUrlNull) {
|
|
82
|
+
logger.debug('🔌 Setting connection status to disconnected (baseUrl is null)');
|
|
83
|
+
setConnectionStatus('disconnected');
|
|
84
|
+
}
|
|
85
|
+
}, [isBaseUrlNull, logger]);
|
|
86
|
+
|
|
87
|
+
useEffect(() => {
|
|
88
|
+
if (isBaseUrlNull) {
|
|
89
|
+
logger.debug('📋 Schema fetching disabled (baseUrl is null)');
|
|
83
90
|
}
|
|
84
|
-
}, [
|
|
91
|
+
}, [isBaseUrlNull, logger]);
|
|
85
92
|
|
|
86
93
|
const {
|
|
87
94
|
data: schemaData,
|
|
@@ -91,12 +98,20 @@ export function WorkbenchProvider({ config, children }: WorkbenchProviderProps)
|
|
|
91
98
|
} = useAgentSchemas({
|
|
92
99
|
baseUrl,
|
|
93
100
|
apiKey,
|
|
94
|
-
enabled:
|
|
101
|
+
enabled: !isBaseUrlNull,
|
|
95
102
|
});
|
|
96
103
|
|
|
97
104
|
// WebSocket connection for dev server restart detection
|
|
105
|
+
const wsBaseUrl = isBaseUrlNull ? undefined : baseUrl;
|
|
106
|
+
useEffect(() => {
|
|
107
|
+
if (isBaseUrlNull) {
|
|
108
|
+
logger.debug('🔌 WebSocket connection disabled (baseUrl is null)');
|
|
109
|
+
}
|
|
110
|
+
}, [isBaseUrlNull, logger]);
|
|
111
|
+
|
|
98
112
|
const { connected } = useWorkbenchWebsocket({
|
|
99
|
-
|
|
113
|
+
enabled: !isBaseUrlNull,
|
|
114
|
+
baseUrl: wsBaseUrl,
|
|
100
115
|
apiKey,
|
|
101
116
|
onConnect: () => {
|
|
102
117
|
setConnectionStatus('connected');
|
|
@@ -115,12 +130,11 @@ export function WorkbenchProvider({ config, children }: WorkbenchProviderProps)
|
|
|
115
130
|
},
|
|
116
131
|
});
|
|
117
132
|
|
|
118
|
-
// Update connection status based on WebSocket connection state
|
|
119
133
|
useEffect(() => {
|
|
120
|
-
if (!connected && connectionStatus !== 'restarting') {
|
|
134
|
+
if (!isBaseUrlNull && !connected && connectionStatus !== 'restarting') {
|
|
121
135
|
setConnectionStatus('disconnected');
|
|
122
136
|
}
|
|
123
|
-
}, [connected, connectionStatus]);
|
|
137
|
+
}, [connected, connectionStatus, isBaseUrlNull]);
|
|
124
138
|
|
|
125
139
|
// Convert schema data to Agent format, no fallback
|
|
126
140
|
const agents = schemaData?.agents;
|
|
@@ -200,23 +214,33 @@ export function WorkbenchProvider({ config, children }: WorkbenchProviderProps)
|
|
|
200
214
|
logger.debug('✅ Validation passed, continuing with message submission...');
|
|
201
215
|
|
|
202
216
|
// Add user message
|
|
217
|
+
// Note: We also add a placeholder assistant message so only the last message
|
|
218
|
+
// shows a loading state while the request is in-flight.
|
|
219
|
+
const now = Date.now();
|
|
203
220
|
const displayText = hasInputSchema
|
|
204
221
|
? value
|
|
205
222
|
: `Running ${selectedAgentData?.metadata.name || 'agent'}...`;
|
|
206
223
|
const userMessage: UIMessage = {
|
|
207
|
-
id:
|
|
224
|
+
id: now.toString(),
|
|
208
225
|
role: 'user',
|
|
209
226
|
parts: [{ type: 'text', text: displayText }],
|
|
210
227
|
};
|
|
211
228
|
|
|
212
|
-
|
|
229
|
+
const assistantMessageId = (now + 1).toString();
|
|
230
|
+
const placeholderAssistantMessage: UIMessage = {
|
|
231
|
+
id: assistantMessageId,
|
|
232
|
+
role: 'assistant',
|
|
233
|
+
parts: [{ type: 'text', text: '', state: 'streaming' }],
|
|
234
|
+
};
|
|
235
|
+
|
|
236
|
+
setMessages((prev) => [...prev, userMessage, placeholderAssistantMessage]);
|
|
213
237
|
setIsLoading(true);
|
|
214
238
|
|
|
215
|
-
logger.debug('🔗 baseUrl:', baseUrl);
|
|
216
|
-
if (!baseUrl) {
|
|
217
|
-
logger.debug('❌
|
|
239
|
+
logger.debug('🔗 baseUrl:', baseUrl, 'isBaseUrlNull:', isBaseUrlNull);
|
|
240
|
+
if (!baseUrl || isBaseUrlNull) {
|
|
241
|
+
logger.debug('❌ Message submission blocked - baseUrl is null or missing');
|
|
218
242
|
const errorMessage: UIMessage = {
|
|
219
|
-
id:
|
|
243
|
+
id: assistantMessageId,
|
|
220
244
|
role: 'assistant',
|
|
221
245
|
parts: [
|
|
222
246
|
{
|
|
@@ -225,7 +249,7 @@ export function WorkbenchProvider({ config, children }: WorkbenchProviderProps)
|
|
|
225
249
|
},
|
|
226
250
|
],
|
|
227
251
|
};
|
|
228
|
-
setMessages((prev) =>
|
|
252
|
+
setMessages((prev) => prev.map((m) => (m.id === assistantMessageId ? errorMessage : m)));
|
|
229
253
|
setIsLoading(false);
|
|
230
254
|
return;
|
|
231
255
|
}
|
|
@@ -273,13 +297,24 @@ export function WorkbenchProvider({ config, children }: WorkbenchProviderProps)
|
|
|
273
297
|
clearTimeout(timeoutId);
|
|
274
298
|
|
|
275
299
|
if (!response.ok) {
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
300
|
+
let errorMessage = `Request failed with status ${response.status}`;
|
|
301
|
+
try {
|
|
302
|
+
const errorData = await response.json();
|
|
303
|
+
errorMessage = errorData.error || errorData.message || errorMessage;
|
|
304
|
+
} catch {
|
|
305
|
+
// If JSON parsing fails, use status text
|
|
306
|
+
errorMessage = response.statusText || errorMessage;
|
|
307
|
+
}
|
|
308
|
+
throw new Error(errorMessage);
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
let result;
|
|
312
|
+
try {
|
|
313
|
+
result = await response.json();
|
|
314
|
+
} catch (jsonError) {
|
|
315
|
+
throw new Error(`Invalid JSON response from server: ${jsonError}`);
|
|
280
316
|
}
|
|
281
317
|
|
|
282
|
-
const result = await response.json();
|
|
283
318
|
const endTime = performance.now();
|
|
284
319
|
const clientDuration = ((endTime - startTime) / 1000).toFixed(1); // Duration in seconds
|
|
285
320
|
|
|
@@ -297,14 +332,16 @@ export function WorkbenchProvider({ config, children }: WorkbenchProviderProps)
|
|
|
297
332
|
typeof result === 'object' ? JSON.stringify(result, null, 2) : String(result);
|
|
298
333
|
|
|
299
334
|
const assistantMessage: UIMessage & { tokens?: string; duration?: string } = {
|
|
300
|
-
id:
|
|
335
|
+
id: assistantMessageId,
|
|
301
336
|
role: 'assistant',
|
|
302
337
|
parts: [{ type: 'text', text: resultText }],
|
|
303
338
|
tokens: totalTokens?.toString(),
|
|
304
339
|
duration,
|
|
305
340
|
};
|
|
306
341
|
|
|
307
|
-
setMessages((prev) =>
|
|
342
|
+
setMessages((prev) =>
|
|
343
|
+
prev.map((m) => (m.id === assistantMessageId ? assistantMessage : m))
|
|
344
|
+
);
|
|
308
345
|
} catch (fetchError) {
|
|
309
346
|
clearTimeout(timeoutId);
|
|
310
347
|
throw fetchError;
|
|
@@ -319,7 +356,7 @@ export function WorkbenchProvider({ config, children }: WorkbenchProviderProps)
|
|
|
319
356
|
: 'Sorry, I encountered an error processing your message.';
|
|
320
357
|
|
|
321
358
|
const errorMessage: UIMessage = {
|
|
322
|
-
id:
|
|
359
|
+
id: assistantMessageId,
|
|
323
360
|
role: 'assistant',
|
|
324
361
|
parts: [
|
|
325
362
|
{
|
|
@@ -328,12 +365,54 @@ export function WorkbenchProvider({ config, children }: WorkbenchProviderProps)
|
|
|
328
365
|
},
|
|
329
366
|
],
|
|
330
367
|
};
|
|
331
|
-
setMessages((prev) =>
|
|
368
|
+
setMessages((prev) => prev.map((m) => (m.id === assistantMessageId ? errorMessage : m)));
|
|
332
369
|
} finally {
|
|
333
370
|
setIsLoading(false);
|
|
334
371
|
}
|
|
335
372
|
};
|
|
336
373
|
|
|
374
|
+
const generateSample = async (agentId: string): Promise<string> => {
|
|
375
|
+
if (!baseUrl || isBaseUrlNull) {
|
|
376
|
+
throw new Error('Base URL not configured');
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
setIsGeneratingSample(true);
|
|
380
|
+
try {
|
|
381
|
+
const url = `${baseUrl}/_agentuity/workbench/sample?agentId=${encodeURIComponent(agentId)}`;
|
|
382
|
+
const headers: HeadersInit = {
|
|
383
|
+
'Content-Type': 'application/json',
|
|
384
|
+
};
|
|
385
|
+
|
|
386
|
+
if (apiKey) {
|
|
387
|
+
headers['Authorization'] = `Bearer ${apiKey}`;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
const response = await fetch(url, {
|
|
391
|
+
method: 'GET',
|
|
392
|
+
headers,
|
|
393
|
+
});
|
|
394
|
+
|
|
395
|
+
if (!response.ok) {
|
|
396
|
+
let errorMessage = `Request failed with status ${response.status}`;
|
|
397
|
+
try {
|
|
398
|
+
const errorData = await response.json();
|
|
399
|
+
errorMessage = errorData.error || errorData.message || errorMessage;
|
|
400
|
+
} catch {
|
|
401
|
+
errorMessage = response.statusText || errorMessage;
|
|
402
|
+
}
|
|
403
|
+
throw new Error(errorMessage);
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
const sample = await response.json();
|
|
407
|
+
return JSON.stringify(sample, null, 2);
|
|
408
|
+
} catch (error) {
|
|
409
|
+
logger.error('Failed to generate sample JSON:', error);
|
|
410
|
+
throw error;
|
|
411
|
+
} finally {
|
|
412
|
+
setIsGeneratingSample(false);
|
|
413
|
+
}
|
|
414
|
+
};
|
|
415
|
+
|
|
337
416
|
const handleAgentSelect = async (agentId: string) => {
|
|
338
417
|
logger.debug('🔄 handleAgentSelect called with:', agentId);
|
|
339
418
|
setSelectedAgent(agentId);
|
|
@@ -351,8 +430,11 @@ export function WorkbenchProvider({ config, children }: WorkbenchProviderProps)
|
|
|
351
430
|
setSelectedAgent: handleAgentSelect,
|
|
352
431
|
inputMode,
|
|
353
432
|
setInputMode,
|
|
354
|
-
isLoading: isLoading ||
|
|
433
|
+
isLoading: isLoading || !!schemasLoading,
|
|
355
434
|
submitMessage,
|
|
435
|
+
generateSample,
|
|
436
|
+
isGeneratingSample,
|
|
437
|
+
isAuthenticated,
|
|
356
438
|
// Schema data from API
|
|
357
439
|
schemas: schemaData,
|
|
358
440
|
schemasLoading: !!schemasLoading,
|
|
@@ -93,14 +93,26 @@ export function useAgentSchemas(options: UseAgentSchemasOptions = {}): UseAgentS
|
|
|
93
93
|
});
|
|
94
94
|
|
|
95
95
|
if (!response.ok) {
|
|
96
|
+
// Handle 404/500 gracefully without throwing
|
|
96
97
|
if (response.status === 401) {
|
|
97
|
-
|
|
98
|
+
setError(new Error('Unauthorized: Invalid or missing API key'));
|
|
99
|
+
return;
|
|
98
100
|
}
|
|
99
|
-
|
|
101
|
+
if (response.status === 404 || response.status >= 500) {
|
|
102
|
+
setError(new Error(`Server error: ${response.status} ${response.statusText}`));
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
setError(new Error(`HTTP ${response.status}: ${response.statusText}`));
|
|
106
|
+
return;
|
|
100
107
|
}
|
|
101
108
|
|
|
102
|
-
|
|
103
|
-
|
|
109
|
+
try {
|
|
110
|
+
const result = (await response.json()) as AgentSchemasResponse;
|
|
111
|
+
setData(result);
|
|
112
|
+
} catch (jsonError) {
|
|
113
|
+
setError(new Error('Invalid JSON response from server'));
|
|
114
|
+
console.error('Failed to parse JSON response:', jsonError);
|
|
115
|
+
}
|
|
104
116
|
} catch (err) {
|
|
105
117
|
const error = err instanceof Error ? err : new Error('Unknown error occurred');
|
|
106
118
|
setError(error);
|
|
@@ -77,6 +77,7 @@ function createReconnectManager(opts: ReconnectOptions): ReconnectManager {
|
|
|
77
77
|
export interface UseWorkbenchWebsocketOptions {
|
|
78
78
|
baseUrl?: string;
|
|
79
79
|
apiKey?: string;
|
|
80
|
+
enabled?: boolean;
|
|
80
81
|
onConnect?: () => void;
|
|
81
82
|
onReconnect?: () => void;
|
|
82
83
|
onAlive?: () => void;
|
|
@@ -126,7 +127,7 @@ export function useWorkbenchWebsocket(
|
|
|
126
127
|
}, [baseUrl, apiKey]);
|
|
127
128
|
|
|
128
129
|
const connect = useCallback(() => {
|
|
129
|
-
if (manualClose.current) return;
|
|
130
|
+
if (manualClose.current || !options.enabled) return;
|
|
130
131
|
const url = wsUrl();
|
|
131
132
|
if (!url) return;
|
|
132
133
|
|
package/src/index.ts
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
// Export types
|
|
2
2
|
export type { WorkbenchInstance } from './types';
|
|
3
|
+
export type { ConnectionStatus } from './types/config';
|
|
3
4
|
|
|
4
5
|
// Export components
|
|
5
6
|
export { default as App } from './components/App';
|
|
6
7
|
export { Chat } from './components/internal/Chat';
|
|
7
8
|
export { Schema } from './components/internal/Schema';
|
|
9
|
+
export { StatusIndicator } from './components/internal/Header';
|
|
8
10
|
export { WorkbenchProvider, useWorkbench } from './components/internal/WorkbenchProvider';
|
|
9
|
-
|
|
10
11
|
// Export workbench functions
|
|
11
12
|
export { createWorkbench } from './workbench';
|
package/src/types/config.ts
CHANGED
|
@@ -24,6 +24,9 @@ export interface WorkbenchContextType {
|
|
|
24
24
|
setInputMode: (mode: 'text' | 'form') => void;
|
|
25
25
|
isLoading: boolean;
|
|
26
26
|
submitMessage: (value: string, mode?: 'text' | 'form') => Promise<void>;
|
|
27
|
+
generateSample: (agentId: string) => Promise<string>;
|
|
28
|
+
isGeneratingSample: boolean;
|
|
29
|
+
isAuthenticated: boolean;
|
|
27
30
|
// Schema data from API
|
|
28
31
|
schemas: AgentSchemasResponse | null;
|
|
29
32
|
schemasLoading: boolean;
|