98 lines
2.6 KiB
TypeScript
98 lines
2.6 KiB
TypeScript
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;
|
||
}
|