feat: implement browser open functionality and related tests
This commit is contained in:
97
electron/gateway/browser-shortcut.ts
Normal file
97
electron/gateway/browser-shortcut.ts
Normal file
@@ -0,0 +1,97 @@
|
||||
import logManager from '@electron/service/logger';
|
||||
import { extractBrowserOpenIntent, openUrlInBrowser } from '@electron/service/browser-open-service';
|
||||
import { appendTranscriptLine } from '@electron/utils/token-usage-writer';
|
||||
import type { RawMessage } from '@runtime/shared/chat-model';
|
||||
import { sessionStore } from './session-store';
|
||||
import type { GatewayEvent } from './types';
|
||||
|
||||
function buildBrowserOpenResponseText(result: { pageUrl: string; title?: string }): string {
|
||||
const suffix = result.title ? `(${result.title})` : '';
|
||||
return `已为你打开 ${result.pageUrl}${suffix}`;
|
||||
}
|
||||
|
||||
function buildBrowserOpenErrorText(error: unknown): string {
|
||||
return `打开失败:${error instanceof Error ? error.message : String(error)}`;
|
||||
}
|
||||
|
||||
async function processBrowserOpen(
|
||||
sessionKey: string,
|
||||
runId: string,
|
||||
url: string,
|
||||
signal: AbortSignal,
|
||||
broadcast: (event: GatewayEvent) => void,
|
||||
) {
|
||||
let assistantText = '';
|
||||
|
||||
try {
|
||||
const result = await openUrlInBrowser(url, { signal });
|
||||
if (signal.aborted) {
|
||||
return;
|
||||
}
|
||||
assistantText = buildBrowserOpenResponseText(result);
|
||||
} catch (error) {
|
||||
if (signal.aborted) {
|
||||
return;
|
||||
}
|
||||
assistantText = buildBrowserOpenErrorText(error);
|
||||
}
|
||||
|
||||
sessionStore.clearActiveRun(sessionKey);
|
||||
|
||||
const finalMessage: RawMessage = {
|
||||
role: 'assistant',
|
||||
content: assistantText,
|
||||
timestamp: Date.now(),
|
||||
};
|
||||
sessionStore.appendMessage(sessionKey, finalMessage);
|
||||
|
||||
appendTranscriptLine(sessionKey, {
|
||||
type: 'message',
|
||||
timestamp: new Date().toISOString(),
|
||||
message: {
|
||||
role: 'assistant',
|
||||
content: assistantText,
|
||||
tool: 'browser.open_url',
|
||||
},
|
||||
});
|
||||
|
||||
broadcast({
|
||||
type: 'chat:final',
|
||||
sessionKey,
|
||||
runId,
|
||||
message: finalMessage,
|
||||
});
|
||||
}
|
||||
|
||||
export function maybeHandleBrowserOpenMessage(
|
||||
sessionKey: string,
|
||||
runId: string,
|
||||
message: RawMessage,
|
||||
broadcast: (event: GatewayEvent) => void,
|
||||
): boolean {
|
||||
const browserIntent = typeof message.content === 'string'
|
||||
? extractBrowserOpenIntent(message.content)
|
||||
: null;
|
||||
|
||||
if (!browserIntent) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const abortController = new AbortController();
|
||||
sessionStore.setActiveRun(sessionKey, runId, abortController);
|
||||
|
||||
processBrowserOpen(sessionKey, runId, browserIntent.url, abortController.signal, broadcast).catch(
|
||||
(error) => {
|
||||
logManager.error('Unexpected error in processBrowserOpen:', error);
|
||||
sessionStore.clearActiveRun(sessionKey);
|
||||
broadcast({
|
||||
type: 'chat:error',
|
||||
sessionKey,
|
||||
runId,
|
||||
error: error instanceof Error ? error.message : String(error),
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
Reference in New Issue
Block a user