import * as path from "path" ; import * as fs from "fs" ; import { promisify } from "util" ; import * as uuid from "uuid" ; import { findAll } from "kernelspecs" ; import * as jupyter_paths from "jupyter-paths" ; import { getPorts as getPortsOrig } from "portfinder" ; const get_ports = promisify ( getPortsOrig ) ; import { writeFile } from "jsonfile" ; import execa from "execa" ; import mkdirp from "mkdirp" ; type StdIO = "pipe" | "ignore" | "inherit" | undefined ; export interface LaunchJupyterOpts { detached : boolean ; stdio ? : StdIO | ( StdIO | number ) [ ] ; env : { [ key ] : string ; } ; cwd ? : string ; cleanupConnectionFile ? : boolean ; cleanup ? : boolean ; preferLocal ? : boolean ; localDir ? : string ; execPath ? : string ; buffer ? : boolean ; reject ? : boolean ; stripFinalNewline ? : boolean ; shell ? : boolean | string ; } function connection_info ( ports ) { return { version : 5 , key : uuid . v4 ( ) , signature_scheme : "hmac-sha256" , transport : "tcp" , ip : "127.0.0.1" , hb_port : ports [ 0 ] , control_port : ports [ 1 ] , shell_port : ports [ 2 ] , stdin_port : ports [ 3 ] , iopub_port : ports [ 4 ] , } ; } const DEFAULT_PORT_OPTS = { port : 9000 , host : "127.0.0.1" } as const ; async function write_connection_file ( port_options ? ) { const options = { ... DEFAULT_PORT_OPTS , ... port_options } ; const ports = await get_ports ( 5 , options ) ; const runtimeDir = jupyter_paths . runtimeDir ( ) ; await mkdirp ( runtimeDir ) ; const config = connection_info ( ports ) ; const connection_file = path . join ( runtimeDir , `kernel-${ config . key }.json` ) ; await writeFile ( connection_file , config ) ; return { config , connection_file } ; } function cleanup ( connectionFile ) { try { fs . unlinkSync ( connectionFile ) ; } catch ( e ) { return ; } } const DEFAULT_SPAWN_OPTIONS = { cleanupConnectionFile : true , env : { } , } as const ; async function launch_kernel_spec ( kernel_spec , config , connection_file , spawn_options ) { const argv = kernel_spec . argv . map ( ( x ) => x . replace ( "{connection_file}" , connection_file ) ) ; const full_spawn_options = { ... DEFAULT_SPAWN_OPTIONS , ... spawn_options } ; full_spawn_options . env = { ... process . env , ... kernel_spec . env , ... spawn_options . env , } ; const running_kernel = execa ( argv [ 0 ] , argv . slice ( 1 ) , full_spawn_options ) ; if ( full_spawn_options . cleanupConnectionFile !== false ) { running_kernel . on ( "exit" , ( _code , _signal ) => cleanup ( connection_file ) ) ; running_kernel . on ( "error" , ( _code , _signal ) => cleanup ( connection_file ) ) ; } return { spawn : running_kernel , connection_file , config , kernel_spec , } ; } export async function launch_jupyter_kernel ( name , spawn_options , cached_specs ? ) { const specs = cached_specs ?? ( await findAll ( ) ) ; const kernel_spec = specs [ name ] ; if ( kernel_spec == null ) { throw new Error ( `No spec available for kernel "${ name }"` ) ; } const launch_info = await write_connection_file ( ) ; return await launch_kernel_spec ( kernel_spec . spec , launch_info . config , launch_info . connection_file , spawn_options ) ; }