setupPortable.ts
utils/claudeInChrome/setupPortable.ts
No strong subsystem tag
234
Lines
6767
Bytes
7
Exports
4
Imports
10
Keywords
What this is
This page documents one file from the repository and includes its full source so you can read it without leaving the docs site.
Beginner explanation
This file is one piece of the larger system. Its name, directory, imports, and exports show where it fits. Start by reading the exports and related files first.
How it is used
Start from the exports list and related files. Those are the easiest clues for where this file fits into the system.
Expert explanation
Architecturally, this file intersects with general runtime concerns. It contains 234 lines, 4 detected imports, and 7 detected exports.
Important relationships
Detected exports
CHROME_EXTENSION_URLChromiumBrowserBrowserPathgetAllBrowserDataPathsPortabledetectExtensionInstallationPortableisChromeExtensionInstalledPortableisChromeExtensionInstalled
Keywords
browserpathconfigchromeextensionwindowslinuxmacosdatapaths
Detected imports
fs/promisesospath../errors.js
Source notes
This page embeds the full file contents. Small or leaf files are still indexed honestly instead of being over-explained.
Full source
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)
}