@@ -27,6 +27,30 @@ interface KernelCandidate {
2727 source : KernelSource ;
2828}
2929
30+ /**
31+ * Try to resolve a binary name to its absolute path via the system PATH.
32+ * Returns the original value if resolution fails or the path is already absolute.
33+ */
34+ function resolveToAbsolutePath ( binaryPath : string ) : string {
35+ if ( path . isAbsolute ( binaryPath ) ) {
36+ return binaryPath ;
37+ }
38+ try {
39+ const cmd = process . platform === 'win32' ? 'where' : 'which' ;
40+ const resolved = cp . execFileSync ( cmd , [ binaryPath ] , {
41+ encoding : 'utf8' ,
42+ timeout : 5000 ,
43+ } ) . trim ( ) . split ( / \r ? \n / ) [ 0 ] ;
44+ if ( resolved && path . isAbsolute ( resolved ) ) {
45+ log ( `Resolved '${ binaryPath } ' to '${ resolved } '` ) ;
46+ return resolved ;
47+ }
48+ } catch {
49+ log ( `Could not resolve '${ binaryPath } ' to absolute path, using as-is` ) ;
50+ }
51+ return binaryPath ;
52+ }
53+
3054/**
3155 * Discover all available ggsql-jupyter kernel binaries
3256 *
@@ -94,7 +118,7 @@ function discoverKernelPaths(): KernelCandidate[] {
94118 }
95119
96120 // 4. PATH fallback (last resort)
97- candidates . push ( { kernelPath : binaryName , source : 'Path' } ) ;
121+ candidates . push ( { kernelPath : resolveToAbsolutePath ( binaryName ) , source : 'Path' } ) ;
98122
99123 // Deduplicate by resolved absolute path
100124 const seen = new Set < string > ( ) ;
@@ -316,30 +340,6 @@ function writeKernelJson(kernelDir: string, kernelPath: string): void {
316340 }
317341}
318342
319- /**
320- * Try to resolve a binary name to its absolute path via the system PATH.
321- * Returns the original value if resolution fails.
322- */
323- function resolveKernelPath ( kernelPath : string ) : string {
324- if ( path . isAbsolute ( kernelPath ) ) {
325- return kernelPath ;
326- }
327- try {
328- const cmd = process . platform === 'win32' ? 'where' : 'which' ;
329- const resolved = cp . execFileSync ( cmd , [ kernelPath ] , {
330- encoding : 'utf8' ,
331- timeout : 5000 ,
332- } ) . trim ( ) . split ( / \r ? \n / ) [ 0 ] ;
333- if ( resolved && path . isAbsolute ( resolved ) ) {
334- log ( `Resolved '${ kernelPath } ' to '${ resolved } '` ) ;
335- return resolved ;
336- }
337- } catch {
338- log ( `Could not resolve '${ kernelPath } ' to absolute path, using as-is` ) ;
339- }
340- return kernelPath ;
341- }
342-
343343/**
344344 * Ensure a Jupyter kernel spec is installed so that external tools like
345345 * Quarto can discover ggsql. Called from session creation/restoration.
@@ -348,7 +348,7 @@ function resolveKernelPath(kernelPath: string): string {
348348 * user-level kernelspec directory.
349349 */
350350function ensureKernelSpecInstalled ( kernelPath : string ) : void {
351- writeKernelJson ( getJupyterKernelDir ( ) , resolveKernelPath ( kernelPath ) ) ;
351+ writeKernelJson ( getJupyterKernelDir ( ) , kernelPath ) ;
352352}
353353
354354/**
@@ -396,7 +396,7 @@ export class GgsqlRuntimeManager implements positron.LanguageRuntimeManager {
396396 // the user kernelspec dir immediately so that Quarto/Jupyter
397397 // can discover ggsql even if no session is ever started.
398398 if ( candidate . source === 'System' ) {
399- writeKernelJson ( getUserJupyterKernelDir ( ) , resolveKernelPath ( candidate . kernelPath ) ) ;
399+ writeKernelJson ( getUserJupyterKernelDir ( ) , candidate . kernelPath ) ;
400400 }
401401
402402 const metadata = generateMetadata ( context , candidate ) ;
0 commit comments