/** * Cross-platform open-with-default-application used by the local GUI carrier. * * A document a browser RENDERS is opened with the user's default browser * rather than the default application for its type, when the platform can name * one: a developer who binds `.html` to an editor would otherwise click a * produced page and get source code. The contract is uniform — prefer the * default browser, fall back to the default application — while how completely * a platform can answer "which browser" differs, and every failure falls back * rather than surfacing. */ import { extname } from 'node:path' import { runNativeCommand, type NativeCommandRunner } from '@deepseek-ai/dsh-native-command' /** Testable command boundary; native implementations never invoke a shell. */ export type PathOpenerRunner = NativeCommandRunner /** Injectable platform facts for deterministic adapter tests. */ export interface PathOpenerInternals { platform?: NodeJS.Platform run?: PathOpenerRunner /** Environment the linux browser convention reads; defaults to the process env. */ env?: NodeJS.ProcessEnv } /** Documents a browser renders, as opposed to ones an editor merely edits. */ const BROWSER_DOCUMENTS = new Set(['.html', '.htm', '.xhtml', '.svg']) /** * The macOS bundle registered for `https` — the default browser, as * LaunchServices records it. The nested version dict is stripped first * because it carries its own `LSHandlerRoleAll`. */ function macBundleForHttps(plist: string): string | undefined { const stripped = plist.replace(/LSHandlerPreferredVersions\s*=\s*\{[^}]*\};/g, '') const block = /\{[^{}]*LSHandlerURLScheme\s*=\s*"?https"?;[^{}]*\}/.exec(stripped)?.[0] if (block === undefined) return undefined return /LSHandlerRoleAll\s*=\s*"?([\w.-]+)"?;/.exec(block)?.[1] } /** * Open one browser-renderable document with the default browser. * @returns true when a browser took it; false when this platform cannot name * one, or naming it failed — the caller then uses the default application. */ async function openInBrowser( path: string, signal: AbortSignal, platform: NodeJS.Platform, run: PathOpenerRunner, env: NodeJS.ProcessEnv, ): Promise { if (platform === 'darwin') { let bundle: string | undefined try { const { stdout } = await run( 'defaults', ['read', 'com.apple.LaunchServices/com.apple.launchservices.secure'], signal) bundle = macBundleForHttps(stdout) } catch { // No LaunchServices record (a fresh account never changed a default): // the content-type handler is then the system's own choice anyway. return false } if (bundle === undefined) return false await run('open', ['-b', bundle, path], signal) return true } if (platform === 'linux') { // $BROWSER is the portable convention; desktop-entry resolution through // xdg-settings needs a launcher this package has no business shipping. const browser = env.BROWSER if (browser === undefined || browser === '') return false await run(browser, [path], signal) return true } // Windows names no browser without reading the UserChoice registry, and its // .html association is the browser in the ordinary case. return false } /** PowerShell single-quoted literal (doubles embedded quotes). */ function powershellLiteral(path: string): string { return `'${path.replace(/'/g, "''")}'` } /** * Open a filesystem path with the operating system's default application, or * with the default browser when the path names a document a browser renders. * @param path - absolute or host-resolvable path (caller owns resolution). * @param signal - caller/connection lifetime; abort terminates the native command. * @param internals - platform, environment, and runner seam for deterministic tests. */ export async function openNativePath( path: string, signal: AbortSignal, internals: PathOpenerInternals = {}, ): Promise { const platform = internals.platform ?? process.platform const run = internals.run ?? runNativeCommand const env = internals.env ?? process.env if (BROWSER_DOCUMENTS.has(extname(path).toLowerCase()) && await openInBrowser(path, signal, platform, run, env)) return if (platform === 'darwin') { await run('open', [path], signal) return } if (platform === 'win32') { await run('powershell.exe', [ '-NoProfile', '-Command', `Invoke-Item -LiteralPath ${powershellLiteral(path)}`, ], signal) return } if (platform === 'linux') { await run('xdg-open', [path], signal) return } throw new Error(`native path opener is unsupported on ${platform}`) }