Files
page-agent/packages/extension/src/entrypoints/background.ts
T

123 lines
3.0 KiB
TypeScript
Raw Normal View History

/**
2026-01-24 19:29:27 +08:00
* Background Script (Service Worker) - Stateless Message Relay
*
* Completely stateless. Only two responsibilities:
* 1. Relay AGENT_TO_PAGE messages from SidePanel to ContentScript
* 2. Broadcast TAB_CHANGE events to all extension pages
*/
import {
type AgentToPageMessage,
type TabChangeMessage,
2026-01-24 19:29:27 +08:00
isExtensionMessage,
2026-01-26 19:33:57 +08:00
} from '../agent/protocol'
2026-01-24 19:29:27 +08:00
// ============================================================================
// Message Relay
2026-01-24 19:29:27 +08:00
// ============================================================================
2026-01-24 19:29:27 +08:00
chrome.runtime.onMessage.addListener(
(
message: unknown,
_sender: chrome.runtime.MessageSender,
2026-01-24 19:29:27 +08:00
sendResponse: (response?: unknown) => void
): boolean => {
if (!isExtensionMessage(message)) {
return false
}
if (message.type === 'AGENT_TO_PAGE') {
handleAgentToPage(message as AgentToPageMessage, sendResponse)
return true // Async response
2026-01-24 19:29:27 +08:00
}
return false
2026-01-24 19:29:27 +08:00
}
)
/**
2026-01-24 19:29:27 +08:00
* Forward RPC call from SidePanel to ContentScript
*/
async function handleAgentToPage(
msg: AgentToPageMessage,
sendResponse: (response: { success: boolean; result?: unknown; error?: string }) => void
): Promise<void> {
const { tabId, method, args } = msg
2026-01-24 19:29:27 +08:00
try {
// Forward directly to content script, same message format
const result = await chrome.tabs.sendMessage(tabId, msg)
sendResponse({ success: true, result })
2026-01-24 19:29:27 +08:00
} catch (error) {
sendResponse({
2026-01-24 19:29:27 +08:00
success: false,
error: error instanceof Error ? error.message : String(error),
})
}
}
2026-01-24 19:29:27 +08:00
// ============================================================================
// Tab Event Broadcasting
2026-01-24 19:29:27 +08:00
// ============================================================================
function broadcastTabChange(message: TabChangeMessage): void {
chrome.runtime.sendMessage(message).catch(() => {
// No listeners (sidepanel not open)
})
}
2026-01-24 19:29:27 +08:00
chrome.tabs.onRemoved.addListener((tabId) => {
broadcastTabChange({
type: 'TAB_CHANGE',
2026-01-24 19:29:27 +08:00
eventType: 'removed',
tabId,
})
2026-01-24 19:29:27 +08:00
})
2026-01-24 19:29:27 +08:00
chrome.tabs.onUpdated.addListener((tabId, changeInfo) => {
if (!changeInfo.status) return
broadcastTabChange({
type: 'TAB_CHANGE',
2026-01-24 19:29:27 +08:00
eventType: 'updated',
tabId,
data: {
status: changeInfo.status,
url: changeInfo.url,
},
})
2026-01-24 19:29:27 +08:00
})
2026-01-24 19:37:46 +08:00
chrome.tabs.onActivated.addListener((activeInfo) => {
broadcastTabChange({
type: 'TAB_CHANGE',
2026-01-24 19:37:46 +08:00
eventType: 'activated',
tabId: activeInfo.tabId,
data: {
windowId: activeInfo.windowId,
},
})
})
chrome.windows.onFocusChanged.addListener((windowId) => {
const focused = windowId !== chrome.windows.WINDOW_ID_NONE
broadcastTabChange({
type: 'TAB_CHANGE',
2026-01-24 19:37:46 +08:00
eventType: 'windowFocusChanged',
tabId: -1,
2026-01-24 19:37:46 +08:00
data: {
windowId: focused ? windowId : undefined,
focused,
},
})
})
2026-01-24 19:29:27 +08:00
// ============================================================================
// Extension Setup
// ============================================================================
2026-01-24 19:29:27 +08:00
export default defineBackground(() => {
console.log('[Background] Service Worker started')
chrome.sidePanel.setPanelBehavior({ openPanelOnActionClick: true }).catch(() => {})
2026-01-24 19:29:27 +08:00
})