Download src/utils/claudeInChrome/setupPortable.ts from chenbhao/codev: direct link, hf CLI and curl.
- Browser
- Download file 6.77 kB
-
https://huggingface.co/chenbhao/codev/resolve/afbc7edaae4e6bb0102ba024b49ce8be89d2264c/src/utils/claudeInChrome/setupPortable.ts
- Command line
-
hf download hf://chenbhao/codev@afbc7edaae4e6bb0102ba024b49ce8be89d2264c/src/utils/claudeInChrome/setupPortable.ts
-
curl -L -o setupPortable.ts https://huggingface.co/chenbhao/codev/resolve/afbc7edaae4e6bb0102ba024b49ce8be89d2264c/src/utils/claudeInChrome/setupPortable.ts
6.77 kB
| import { readdir } from 'fs/promises' | |
| import { homedir } from 'os' | |
| import { join } from 'path' | |
| import { isFsInaccessible } from '../errors.js' | |
| export const CHROME_EXTENSION_URL = 'https://claude.ai/chrome' | |
| // Production extension ID | |
| const PROD_EXTENSION_ID = 'fcoeoabgfenejglbffodgkkbkcdhcgfn' | |
| // Dev extension IDs (for internal use) | |
| const DEV_EXTENSION_ID = 'dihbgbndebgnbjfmelmegjepbnkhlgni' | |
| const ANT_EXTENSION_ID = 'dngcpimnedloihjnnfngkgjoidhnaolf' | |
| function getExtensionIds(): string[] { | |
| return process.env.USER_TYPE === 'ant' | |
| ? [PROD_EXTENSION_ID, DEV_EXTENSION_ID, ANT_EXTENSION_ID] | |
| : [PROD_EXTENSION_ID] | |
| } | |
| // Must match ChromiumBrowser from common.ts | |
| export type ChromiumBrowser = | |
| | 'chrome' | |
| | 'brave' | |
| | 'arc' | |
| | 'chromium' | |
| | 'edge' | |
| | 'vivaldi' | |
| | 'opera' | |
| export type BrowserPath = { | |
| browser: ChromiumBrowser | |
| path: string | |
| } | |
| type Logger = (message: string) => void | |
| // Browser detection order - must match BROWSER_DETECTION_ORDER from common.ts | |
| const BROWSER_DETECTION_ORDER: ChromiumBrowser[] = [ | |
| 'chrome', | |
| 'brave', | |
| 'arc', | |
| 'edge', | |
| 'chromium', | |
| 'vivaldi', | |
| 'opera', | |
| ] | |
| type BrowserDataConfig = { | |
| macos: string[] | |
| linux: string[] | |
| windows: { path: string[]; useRoaming?: boolean } | |
| } | |
| // Must match CHROMIUM_BROWSERS dataPath from common.ts | |
| const CHROMIUM_BROWSERS: Record<ChromiumBrowser, BrowserDataConfig> = { | |
| chrome: { | |
| macos: ['Library', 'Application Support', 'Google', 'Chrome'], | |
| linux: ['.config', 'google-chrome'], | |
| windows: { path: ['Google', 'Chrome', 'User Data'] }, | |
| }, | |
| brave: { | |
| macos: ['Library', 'Application Support', 'BraveSoftware', 'Brave-Browser'], | |
| linux: ['.config', 'BraveSoftware', 'Brave-Browser'], | |
| windows: { path: ['BraveSoftware', 'Brave-Browser', 'User Data'] }, | |
| }, | |
| arc: { | |
| macos: ['Library', 'Application Support', 'Arc', 'User Data'], | |
| linux: [], | |
| windows: { path: ['Arc', 'User Data'] }, | |
| }, | |
| chromium: { | |
| macos: ['Library', 'Application Support', 'Chromium'], | |
| linux: ['.config', 'chromium'], | |
| windows: { path: ['Chromium', 'User Data'] }, | |
| }, | |
| edge: { | |
| macos: ['Library', 'Application Support', 'Microsoft Edge'], | |
| linux: ['.config', 'microsoft-edge'], | |
| windows: { path: ['Microsoft', 'Edge', 'User Data'] }, | |
| }, | |
| vivaldi: { | |
| macos: ['Library', 'Application Support', 'Vivaldi'], | |
| linux: ['.config', 'vivaldi'], | |
| windows: { path: ['Vivaldi', 'User Data'] }, | |
| }, | |
| opera: { | |
| macos: ['Library', 'Application Support', 'com.operasoftware.Opera'], | |
| linux: ['.config', 'opera'], | |
| windows: { path: ['Opera Software', 'Opera Stable'], useRoaming: true }, | |
| }, | |
| } | |
| /** | |
| * Get all browser data paths to check for extension installation. | |
| * Portable version that uses process.platform directly. | |
| */ | |
| export function getAllBrowserDataPathsPortable(): BrowserPath[] { | |
| const home = homedir() | |
| const paths: BrowserPath[] = [] | |
| for (const browserId of BROWSER_DETECTION_ORDER) { | |
| const config = CHROMIUM_BROWSERS[browserId] | |
| let dataPath: string[] | undefined | |
| switch (process.platform) { | |
| case 'darwin': | |
| dataPath = config.macos | |
| break | |
| case 'linux': | |
| dataPath = config.linux | |
| break | |
| case 'win32': { | |
| if (config.windows.path.length > 0) { | |
| const appDataBase = config.windows.useRoaming | |
| ? join(home, 'AppData', 'Roaming') | |
| : join(home, 'AppData', 'Local') | |
| paths.push({ | |
| browser: browserId, | |
| path: join(appDataBase, ...config.windows.path), | |
| }) | |
| } | |
| continue | |
| } | |
| } | |
| if (dataPath && dataPath.length > 0) { | |
| paths.push({ | |
| browser: browserId, | |
| path: join(home, ...dataPath), | |
| }) | |
| } | |
| } | |
| return paths | |
| } | |
| /** | |
| * Detects if the Claude in Chrome extension is installed by checking the Extensions | |
| * directory across all supported Chromium-based browsers and their profiles. | |
| * | |
| * This is a portable version that can be used by both TUI and VS Code extension. | |
| * | |
| * @param browserPaths - Array of browser data paths to check (from getAllBrowserDataPaths) | |
| * @param log - Optional logging callback for debug messages | |
| * @returns Object with isInstalled boolean and the browser where the extension was found | |
| */ | |
| export async function detectExtensionInstallationPortable( | |
| browserPaths: BrowserPath[], | |
| log?: Logger, | |
| ): Promise<{ | |
| isInstalled: boolean | |
| browser: ChromiumBrowser | null | |
| }> { | |
| if (browserPaths.length === 0) { | |
| log?.(`[Claude in Chrome] No browser paths to check`) | |
| return { isInstalled: false, browser: null } | |
| } | |
| const extensionIds = getExtensionIds() | |
| // Check each browser for the extension | |
| for (const { browser, path: browserBasePath } of browserPaths) { | |
| let browserProfileEntries = [] | |
| try { | |
| browserProfileEntries = await readdir(browserBasePath, { | |
| withFileTypes: true, | |
| }) | |
| } catch (e) { | |
| // Browser not installed or path doesn't exist, continue to next browser | |
| if (isFsInaccessible(e)) continue | |
| throw e | |
| } | |
| const profileDirs = browserProfileEntries | |
| .filter(entry => entry.isDirectory()) | |
| .filter( | |
| entry => entry.name === 'Default' || entry.name.startsWith('Profile '), | |
| ) | |
| .map(entry => entry.name) | |
| if (profileDirs.length > 0) { | |
| log?.( | |
| `[Claude in Chrome] Found ${browser} profiles: ${profileDirs.join(', ')}`, | |
| ) | |
| } | |
| // Check each profile for any of the extension IDs | |
| for (const profile of profileDirs) { | |
| for (const extensionId of extensionIds) { | |
| const extensionPath = join( | |
| browserBasePath, | |
| profile, | |
| 'Extensions', | |
| extensionId, | |
| ) | |
| try { | |
| await readdir(extensionPath) | |
| log?.( | |
| `[Claude in Chrome] Extension ${extensionId} found in ${browser} ${profile}`, | |
| ) | |
| return { isInstalled: true, browser } | |
| } catch { | |
| // Extension not found in this profile, continue checking | |
| } | |
| } | |
| } | |
| } | |
| log?.(`[Claude in Chrome] Extension not found in any browser`) | |
| return { isInstalled: false, browser: null } | |
| } | |
| /** | |
| * Simple wrapper that returns just the boolean result | |
| */ | |
| export async function isChromeExtensionInstalledPortable( | |
| browserPaths: BrowserPath[], | |
| log?: Logger, | |
| ): Promise<boolean> { | |
| const result = await detectExtensionInstallationPortable(browserPaths, log) | |
| return result.isInstalled | |
| } | |
| /** | |
| * Convenience function that gets browser paths automatically. | |
| * Use this when you don't need to provide custom browser paths. | |
| */ | |
| export function isChromeExtensionInstalled(log?: Logger): Promise<boolean> { | |
| const browserPaths = getAllBrowserDataPathsPortable() | |
| return isChromeExtensionInstalledPortable(browserPaths, log) | |
| } | |