|
16 | 16 |
|
17 | 17 | import path from 'node:path'; |
18 | 18 | import fs from 'node:fs'; |
| 19 | +import * as process from 'node:process'; |
19 | 20 | import React from 'react'; |
20 | 21 | import { Box, Text, useInput } from 'ink'; |
21 | 22 | import TextInput from 'ink-text-input'; |
22 | | -import { Connection } from '@salesforce/core'; |
| 23 | +import { Connection, SfError } from '@salesforce/core'; |
23 | 24 | import { AgentPreview, AgentPreviewSendResponse, writeDebugLog } from '@salesforce/agents'; |
24 | 25 | import { sleep } from '@salesforce/kit'; |
25 | 26 |
|
@@ -106,26 +107,39 @@ export function AgentPreviewReact(props: { |
106 | 107 | React.useEffect(() => { |
107 | 108 | const endSession = async (): Promise<void> => { |
108 | 109 | if (sessionEnded) { |
109 | | - // TODO: Support other end types (such as Escalate) |
110 | | - await agent.end(sessionId, 'UserRequest'); |
111 | | - process.exit(0); |
| 110 | + try { |
| 111 | + // TODO: Support other end types (such as Escalate) |
| 112 | + await agent.end(sessionId, 'UserRequest'); |
| 113 | + process.exit(0); |
| 114 | + } catch (e) { |
| 115 | + // in case the agent session never started, calling agent.end will throw an error, but we've already shown the error to the user |
| 116 | + process.exit(0); |
| 117 | + } |
112 | 118 | } |
113 | 119 | }; |
114 | 120 | void endSession(); |
115 | 121 | }, [sessionEnded]); |
116 | 122 |
|
117 | 123 | React.useEffect(() => { |
118 | 124 | const startSession = async (): Promise<void> => { |
119 | | - const session = await agent.start(); |
120 | | - setSessionId(session.sessionId); |
121 | | - setHeader(`New session started with "${props.name}" (${session.sessionId})`); |
122 | | - await sleep(500); // Add a short delay to make it feel more natural |
123 | | - setIsTyping(false); |
124 | | - if (outputDir) { |
125 | | - const dateForDir = new Date().toISOString().replace(/:/g, '-').split('.')[0]; |
126 | | - setTempDir(path.join(outputDir, `${dateForDir}--${session.sessionId}`)); |
| 125 | + try { |
| 126 | + const session = await agent.start(); |
| 127 | + setSessionId(session.sessionId); |
| 128 | + setHeader(`New session started with "${props.name}" (${session.sessionId})`); |
| 129 | + await sleep(500); // Add a short delay to make it feel more natural |
| 130 | + setIsTyping(false); |
| 131 | + if (outputDir) { |
| 132 | + const dateForDir = new Date().toISOString().replace(/:/g, '-').split('.')[0]; |
| 133 | + setTempDir(path.join(outputDir, `${dateForDir}--${session.sessionId}`)); |
| 134 | + } |
| 135 | + setMessages([{ role: name, content: session.messages[0].message, timestamp: new Date() }]); |
| 136 | + } catch (e) { |
| 137 | + const sfError = SfError.wrap(e); |
| 138 | + setIsTyping(false); |
| 139 | + setHeader('Error starting session'); |
| 140 | + setMessages([{ role: name, content: `${sfError.name} - ${sfError.message}`, timestamp: new Date() }]); |
| 141 | + setSessionEnded(true); |
127 | 142 | } |
128 | | - setMessages([{ role: name, content: session.messages[0].message, timestamp: new Date() }]); |
129 | 143 | }; |
130 | 144 |
|
131 | 145 | void startSession(); |
@@ -194,45 +208,55 @@ export function AgentPreviewReact(props: { |
194 | 208 | <Text dimColor>{'─'.repeat(process.stdout.columns - 2)}</Text> |
195 | 209 | </Box> |
196 | 210 |
|
197 | | - <Box marginBottom={1}> |
198 | | - <Text>> </Text> |
199 | | - <TextInput |
200 | | - showCursor |
201 | | - value={query} |
202 | | - placeholder="Start typing (press ESC to exit)" |
203 | | - onChange={setQuery} |
204 | | - // eslint-disable-next-line @typescript-eslint/no-misused-promises |
205 | | - onSubmit={async (content) => { |
206 | | - if (!content) return; |
207 | | - setQuery(''); |
208 | | - |
209 | | - // Add the most recent user message to the chat window |
210 | | - setMessages((prev) => [...prev, { role: 'user', content, timestamp: new Date() }]); |
211 | | - setIsTyping(true); |
212 | | - const response = await agent.send(sessionId, content); |
213 | | - setResponses((prev) => [...prev, response]); |
214 | | - const message = response.messages[0].message; |
215 | | - |
216 | | - if (!message) { |
217 | | - throw new Error('Failed to send message'); |
218 | | - } |
219 | | - setIsTyping(false); |
220 | | - |
221 | | - // Add the agent's response to the chat |
222 | | - setMessages((prev) => [...prev, { role: name, content: message, timestamp: new Date() }]); |
223 | | - |
224 | | - // If there is an apex debug log entry, get the log and write it to the output dir |
225 | | - if (response.apexDebugLog && tempDir) { |
226 | | - // Write the apex debug to the output dir |
227 | | - await writeDebugLog(connection, response.apexDebugLog, tempDir); |
228 | | - const logId = response.apexDebugLog.Id; |
229 | | - if (logId) { |
230 | | - setApexDebugLogs((prev) => [...prev, path.join(tempDir, `${logId}.log`)]); |
| 211 | + {sessionEnded ? null : ( |
| 212 | + <Box marginBottom={1}> |
| 213 | + <Text>> </Text> |
| 214 | + <TextInput |
| 215 | + showCursor |
| 216 | + value={query} |
| 217 | + placeholder="Start typing (press ESC to exit)" |
| 218 | + onChange={setQuery} |
| 219 | + // eslint-disable-next-line @typescript-eslint/no-misused-promises |
| 220 | + onSubmit={async (content) => { |
| 221 | + if (!content) return; |
| 222 | + setQuery(''); |
| 223 | + |
| 224 | + try { |
| 225 | + // Add the most recent user message to the chat window |
| 226 | + setMessages((prev) => [...prev, { role: 'user', content, timestamp: new Date() }]); |
| 227 | + setIsTyping(true); |
| 228 | + const response = await agent.send(sessionId, content); |
| 229 | + setResponses((prev) => [...prev, response]); |
| 230 | + const message = response.messages[0].message; |
| 231 | + |
| 232 | + if (!message) { |
| 233 | + throw new Error('Failed to send message'); |
| 234 | + } |
| 235 | + setIsTyping(false); |
| 236 | + |
| 237 | + // Add the agent's response to the chat |
| 238 | + setMessages((prev) => [...prev, { role: name, content: message, timestamp: new Date() }]); |
| 239 | + |
| 240 | + // If there is an apex debug log entry, get the log and write it to the output dir |
| 241 | + if (response.apexDebugLog && tempDir) { |
| 242 | + // Write the apex debug to the output dir |
| 243 | + await writeDebugLog(connection, response.apexDebugLog, tempDir); |
| 244 | + const logId = response.apexDebugLog.Id; |
| 245 | + if (logId) { |
| 246 | + setApexDebugLogs((prev) => [...prev, path.join(tempDir, `${logId}.log`)]); |
| 247 | + } |
| 248 | + } |
| 249 | + } catch (e) { |
| 250 | + const sfError = SfError.wrap(e); |
| 251 | + setIsTyping(false); |
| 252 | + setHeader(`Error: ${sfError.name}`); |
| 253 | + setMessages([{ role: name, content: `${sfError.name} - ${sfError.message}`, timestamp: new Date() }]); |
| 254 | + setSessionEnded(true); |
231 | 255 | } |
232 | | - } |
233 | | - }} |
234 | | - /> |
235 | | - </Box> |
| 256 | + }} |
| 257 | + /> |
| 258 | + </Box> |
| 259 | + )} |
236 | 260 |
|
237 | 261 | {sessionEnded ? ( |
238 | 262 | <Box |
|
0 commit comments