background.js 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124
  1. // Fixed URI for WebSocket server
  2. const WS_URL = 'ws://localhost:6768/ws';
  3. const TEN_SECONDS_MS = 10000;
  4. let webSocket = null;
  5. // Setup side panel behavior
  6. chrome.sidePanel
  7. .setPanelBehavior({ openPanelOnActionClick: true })
  8. .catch((error) => console.error(error));
  9. // WebSocket connection management
  10. function connectWebSocket() {
  11. webSocket = new WebSocket(WS_URL);
  12. webSocket.onopen = () => {
  13. console.log('WebSocket connected');
  14. broadcastConnectionStatus(true);
  15. keepAlive();
  16. };
  17. webSocket.onclose = () => {
  18. console.log('WebSocket disconnected');
  19. broadcastConnectionStatus(false);
  20. // Attempt to reconnect after 5 seconds
  21. setTimeout(connectWebSocket, 5000);
  22. };
  23. webSocket.onmessage = (event) => {
  24. try {
  25. const message = JSON.parse(event.data);
  26. const kind = message.kind;
  27. if (kind === 'state') {
  28. // Broadcast task progress to sidebar
  29. broadcastToSidebar({
  30. type: 'state',
  31. data: message.data
  32. });
  33. } else if (kind === 'ack') {
  34. // console.log('ACK:', message);
  35. }
  36. } catch (error) {
  37. console.error('Failed to parse WebSocket message:', error);
  38. }
  39. };
  40. }
  41. function keepAlive() {
  42. const keepAliveIntervalId = setInterval(
  43. () => {
  44. if (webSocket && webSocket.readyState === WebSocket.OPEN) {
  45. console.log('sending heartbeat');
  46. const heartbeatMessage = {
  47. kind: "hb",
  48. data: {
  49. timestamp: Math.floor(new Date().getTime() / 1000)
  50. }
  51. };
  52. webSocket.send(JSON.stringify(heartbeatMessage));
  53. } else {
  54. clearInterval(keepAliveIntervalId);
  55. }
  56. },
  57. TEN_SECONDS_MS
  58. );
  59. }
  60. // Broadcast helpers
  61. function broadcastConnectionStatus(isConnected) {
  62. broadcastToSidebar({
  63. type: 'connection_status',
  64. data: { isConnected }
  65. });
  66. }
  67. function broadcastToSidebar(message) {
  68. chrome.runtime.sendMessage(message).catch(err => {
  69. console.log('Failed to send message to sidebar:', err);
  70. });
  71. }
  72. function generateTaskId() {
  73. const timestamp = Date.now();
  74. const random = Math.floor(Math.random() * 900000) + 100000; // 6-digit random number
  75. return `${timestamp}-${random}`;
  76. }
  77. // Message handling from sidebar
  78. chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
  79. if (message.type === 'SEND_MESSAGE' && webSocket) {
  80. const taskMessage = {
  81. kind: "create",
  82. data: {
  83. task_id: generateTaskId(),
  84. intent: message.text,
  85. args: { tab_id: message.tabId }
  86. }
  87. };
  88. webSocket.send(JSON.stringify(taskMessage));
  89. sendResponse({ success: true });
  90. }
  91. return true;
  92. });
  93. // Initialize WebSocket connection
  94. connectWebSocket();
  95. chrome.tabs.onUpdated.addListener((tabId, changeInfo, tab) => {
  96. if (changeInfo.status === 'complete' && tab.url?.startsWith('http')) {
  97. chrome.scripting.executeScript({
  98. target: { tabId: tabId },
  99. files: ['content.js']
  100. }).catch(err => console.error('Failed to inject content script:', err));
  101. }
  102. });
  103. chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
  104. if (message.type === 'GET_TAB_ID') {
  105. if (sender.tab && sender.tab.id !== undefined) {
  106. sendResponse({ tabId: sender.tab.id });
  107. } else {
  108. sendResponse({ tabId: null });
  109. }
  110. }
  111. });