Path: blob/master/src/packages/jupyter/kernel/conat-service.ts
1447 views
/*123DEVELOPMENT:45Go to packages/project/c/open-files.ts and for a dev project, stop the built in open files service6and start your own in a terminal. If you then open a jupyter notebook in that project, you can use7your terminal running the open files service to interact with anything here from the server size.8In particular, set global.x = ..., etc.9*/1011import { createConatJupyterService } from "@cocalc/conat/service/jupyter";12import { kernels } from "./kernels";13import { bufferToBase64 } from "@cocalc/util/base64";14import { once } from "@cocalc/util/async-utils";15import { type JupyterKernel } from "./kernel";1617export async function initConatService({18path,19project_id,20timeout = 15000, // used for getKernel right now21}: {22path: string;23project_id: string;24timeout?: number;25}) {26const getExistingKernel = async (): Promise<JupyterKernel> => {27let kernel = kernels.get(path);28if (kernel != null) {29return kernel;30}31try {32[kernel] = await once(kernels, path, timeout);33return kernel!;34} catch {}35// timeout36// it doesn't exist right now, but it probably will in a few seconds, so wait37// in an event driven way for it to get opened.3839throw Error(`no Jupyter kernel with path '${path}'`);40};4142const impl = {43// Signal should be a string like "SIGINT", "SIGKILL".44signal: async (signal: string) => {45kernels.get(path)?.signal(signal);46},4748save_ipynb_file: async (opts?) => {49await (await getExistingKernel()).save_ipynb_file(opts);50},5152kernel_info: async () => {53return await (await getExistingKernel()).kernel_info();54},5556more_output: async (id) => {57return (await getExistingKernel()).more_output(id);58},5960complete: async (opts) => {61return await (62await getExistingKernel()63).complete(get_code_and_cursor_pos(opts));64},6566introspect: async (opts) => {67const { code, cursor_pos } = get_code_and_cursor_pos(opts);68let detail_level = 0;69if (opts.level != null) {70try {71detail_level = parseInt(opts.level);72if (detail_level < 0) {73detail_level = 0;74} else if (detail_level > 1) {75detail_level = 1;76}77} catch (err) {}78}79return await (80await getExistingKernel()81).introspect({82code,83cursor_pos,84detail_level,85});86},87store: async ({88key,89value,90}: {91key: string;92value?: any;93}): Promise<any> => {94const kernel = await getExistingKernel();95if (value === undefined) {96// undefined when getting the value97return kernel.store.get(key);98} else if (value === null) {99// null is used for deleting the value100kernel.store.delete(key);101return {};102} else {103kernel.store.set(key, value);104return {};105}106},107comm: async (opts) => {108(await getExistingKernel()).send_comm_message_to_kernel(opts);109},110111ipywidgetsGetBuffer: async ({ model_id, buffer_path }) => {112const buffer = (await getExistingKernel()).ipywidgetsGetBuffer(113model_id,114buffer_path,115);116if (buffer == null) {117throw Error(118`no buffer for model=${model_id}, buffer_path=${JSON.stringify(119buffer_path,120)}`,121);122}123return { buffer64: bufferToBase64(buffer) };124},125};126return await createConatJupyterService({127project_id,128path,129impl,130});131}132133function get_code_and_cursor_pos(opts): {134code: string;135cursor_pos: number;136} {137const code: string = opts.code;138if (!code) {139throw Error("must specify code");140}141let cursor_pos: number;142if (opts.cursor_pos != null) {143try {144cursor_pos = parseInt(opts.cursor_pos);145} catch (error) {146cursor_pos = code.length;147}148} else {149cursor_pos = code.length;150}151152return { code, cursor_pos };153}154155156