@e2b/code-interpreter
Advanced tools
Comparing version 0.0.9-beta.4 to 0.0.9-beta.5
@@ -1,8 +0,5 @@ | ||
import { Sandbox } from 'e2b'; | ||
import { ConnectionConfig, Sandbox } from 'e2b'; | ||
export * from 'e2b'; | ||
/** | ||
* A message from a process. | ||
*/ | ||
declare class CellMessage { | ||
declare class OutputMessage { | ||
readonly line: string; | ||
@@ -37,3 +34,3 @@ /** | ||
**/ | ||
tracebackRaw: string[]; | ||
traceback: string; | ||
constructor( | ||
@@ -51,7 +48,3 @@ /** | ||
**/ | ||
tracebackRaw: string[]); | ||
/** | ||
* Returns the traceback of the error as a string. | ||
*/ | ||
get traceback(): string; | ||
traceback: string); | ||
} | ||
@@ -124,3 +117,3 @@ /** | ||
readonly raw: RawData; | ||
constructor(data: RawData, isMainResult: boolean); | ||
constructor(rawData: RawData, isMainResult: boolean); | ||
/** | ||
@@ -186,7 +179,7 @@ * Returns all the formats available for the result. | ||
*/ | ||
results: Result[], | ||
results?: Result[], | ||
/** | ||
* Logs printed to stdout and stderr during execution. | ||
*/ | ||
logs: Logs, | ||
logs?: Logs, | ||
/** | ||
@@ -214,103 +207,42 @@ * An Error object if an error occurred, null otherwise. | ||
/** | ||
* E2B code interpreter sandbox extension. | ||
*/ | ||
declare class CodeInterpreter extends Sandbox { | ||
protected static readonly defaultTemplate: string; | ||
readonly notebook: JupyterExtension; | ||
protected onInit(opts: { | ||
requestTimeoutMs?: number; | ||
}): Promise<void>; | ||
close(): Promise<void>; | ||
getProtocol(baseProtocol?: string): string; | ||
} | ||
declare class JupyterExtension { | ||
private sandbox; | ||
private readonly connectedKernels; | ||
private readonly kernelIDPromise; | ||
private readonly setDefaultKernelID; | ||
private get defaultKernelID(); | ||
constructor(sandbox: CodeInterpreter); | ||
connect(requestTimeoutMs?: number): Promise<void>; | ||
/** | ||
* Executes a code cell in a notebool cell. | ||
* | ||
* This method sends the provided code to a specified kernel in a remote notebook for execution. | ||
* @param code The code to be executed in the notebook cell. | ||
* @param kernelID The ID of the kernel to execute the code on. If not provided, the default kernel is used. | ||
* @param onStdout A callback function to handle standard output messages from the code execution. | ||
* @param onStderr A callback function to handle standard error messages from the code execution. | ||
* @param onResult A callback function to handle display data messages from the code execution. | ||
* @param timeoutMs The maximum time to wait for the code execution to complete, in milliseconds. | ||
* @returns A promise that resolves with the result of the code execution. | ||
*/ | ||
execCell(code: string, { kernelID, onStdout, onStderr, onResult, timeoutMs: timeout }?: { | ||
private readonly url; | ||
private readonly connectionConfig; | ||
private static readonly execTimeoutMs; | ||
private static readonly defaultKernelID; | ||
constructor(url: string, connectionConfig: ConnectionConfig); | ||
execCell(code: string, opts?: { | ||
kernelID?: string; | ||
onStdout?: (msg: CellMessage) => any; | ||
onStderr?: (msg: CellMessage) => any; | ||
onResult?: (data: Result) => any; | ||
onStdout?: (output: OutputMessage) => (Promise<any> | any); | ||
onStderr?: (output: OutputMessage) => (Promise<any> | any); | ||
onResult?: (data: Result) => (Promise<any> | any); | ||
timeoutMs?: number; | ||
requestTimeoutMs?: number; | ||
}): Promise<Execution>; | ||
private startConnectingToDefaultKernel; | ||
/** | ||
* Connects to a kernel's WebSocket. | ||
* | ||
* This method establishes a WebSocket connection to the specified kernel. It is used internally | ||
* to facilitate real-time communication with the kernel, enabling operations such as executing | ||
* code and receiving output. The connection details are managed within the method, including | ||
* the retrieval of the necessary WebSocket URL from the kernel's information. | ||
* | ||
* @param kernelID The unique identifier of the kernel to connect to. | ||
* @param sessionID The unique identifier of the session to connect to. | ||
* @throws {Error} Throws an error if the connection to the kernel's WebSocket cannot be established. | ||
*/ | ||
private connectToKernelWS; | ||
/** | ||
* Creates a new Jupyter kernel. It can be useful if you want to have multiple independent code execution environments. | ||
* | ||
* The kernel can be optionally configured to start in a specific working directory and/or | ||
* with a specific kernel name. If no kernel name is provided, the default kernel will be used. | ||
* Once the kernel is created, this method establishes a WebSocket connection to the new kernel for | ||
* real-time communication. | ||
* | ||
* @param cwd Sets the current working directory where the kernel should start. Defaults to "/home/user". | ||
* @param kernelName The name of the kernel to create, useful if you have multiple kernel types. If not provided, the default kernel will be used. | ||
* @returns A promise that resolves with the ID of the newly created kernel. | ||
* @throws {Error} Throws an error if the kernel creation fails. | ||
*/ | ||
createKernel(cwd?: string, kernelName?: string): Promise<string>; | ||
/** | ||
* Restarts an existing Jupyter kernel. This can be useful to reset the kernel's state or to recover from errors. | ||
* | ||
* @param kernelID The unique identifier of the kernel to restart. If not provided, the default kernel is restarted. | ||
* @throws {Error} Throws an error if the kernel restart fails or if the operation times out. | ||
*/ | ||
restartKernel(kernelID?: string): Promise<void>; | ||
/** | ||
* Shuts down an existing Jupyter kernel. This method is used to gracefully terminate a kernel's process. | ||
* @param kernelID The unique identifier of the kernel to shutdown. If not provided, the default kernel is shutdown. | ||
* @throws {Error} Throws an error if the kernel shutdown fails or if the operation times out. | ||
*/ | ||
shutdownKernel(kernelID?: string): Promise<void>; | ||
/** | ||
* Lists all available Jupyter kernels. | ||
* | ||
* This method fetches a list of all currently available Jupyter kernels from the server. It can be used | ||
* to retrieve the IDs of all kernels that are currently running or available for connection. | ||
* | ||
* @returns A promise that resolves to an array of objects containing a kernel ID and kernel name. | ||
* @throws {Error} Throws an error if the request to list kernels fails. | ||
*/ | ||
listKernels(): Promise<{ | ||
id: string; | ||
createKernel({ cwd, kernelName, requestTimeoutMs, }?: { | ||
cwd?: string; | ||
kernelName?: string; | ||
requestTimeoutMs?: number; | ||
}): Promise<string>; | ||
restartKernel({ kernelID, requestTimeoutMs, }?: { | ||
kernelID?: string; | ||
requestTimeoutMs?: number; | ||
}): Promise<void>; | ||
shutdownKernel({ kernelID, requestTimeoutMs, }?: { | ||
kernelID?: string; | ||
requestTimeoutMs?: number; | ||
}): Promise<void>; | ||
listKernels({ requestTimeoutMs, }?: { | ||
requestTimeoutMs?: number; | ||
}): Promise<{ | ||
kernelID: string; | ||
name: string; | ||
}[]>; | ||
/** | ||
* Close all the websocket connections to the kernels. It doesn't shutdown the kernels. | ||
*/ | ||
close(): Promise<void>; | ||
} | ||
declare class CodeInterpreter extends Sandbox { | ||
protected static readonly defaultTemplate: string; | ||
protected static readonly jupyterPort = 49999; | ||
readonly notebook: JupyterExtension; | ||
} | ||
export { CellMessage, CodeInterpreter, Execution, ExecutionError, JupyterExtension, type Logs, type MIMEType, type RawData, Result, CodeInterpreter as default }; | ||
export { CodeInterpreter, Execution, ExecutionError, JupyterExtension, type Logs, type MIMEType, OutputMessage, type RawData, Result, CodeInterpreter as default }; |
"use strict"; | ||
var __create = Object.create; | ||
var __defProp = Object.defineProperty; | ||
var __defProps = Object.defineProperties; | ||
var __getOwnPropDesc = Object.getOwnPropertyDescriptor; | ||
var __getOwnPropDescs = Object.getOwnPropertyDescriptors; | ||
var __getOwnPropNames = Object.getOwnPropertyNames; | ||
var __getOwnPropSymbols = Object.getOwnPropertySymbols; | ||
var __getProtoOf = Object.getPrototypeOf; | ||
var __hasOwnProp = Object.prototype.hasOwnProperty; | ||
var __propIsEnum = Object.prototype.propertyIsEnumerable; | ||
var __knownSymbol = (name, symbol) => (symbol = Symbol[name]) ? symbol : Symbol.for("Symbol." + name); | ||
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; | ||
@@ -22,2 +23,3 @@ var __spreadValues = (a, b) => { | ||
}; | ||
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b)); | ||
var __export = (target, all) => { | ||
@@ -36,10 +38,2 @@ for (var name in all) | ||
var __reExport = (target, mod, secondTarget) => (__copyProps(target, mod, "default"), secondTarget && __copyProps(secondTarget, mod, "default")); | ||
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( | ||
// If the importer is in node compatibility mode or this is not an ESM | ||
// file that has been converted to a CommonJS file using a Babel- | ||
// compatible transform (i.e. "__esModule" has not been set), then set | ||
// "default" to the CommonJS "module.exports" for node compatibility. | ||
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, | ||
mod | ||
)); | ||
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); | ||
@@ -66,2 +60,18 @@ var __async = (__this, __arguments, generator) => { | ||
}; | ||
var __await = function(promise, isYieldStar) { | ||
this[0] = promise; | ||
this[1] = isYieldStar; | ||
}; | ||
var __asyncGenerator = (__this, __arguments, generator) => { | ||
var resume = (k, v, yes, no) => { | ||
try { | ||
var x = generator[k](v), isAwait = (v = x.value) instanceof __await, done = x.done; | ||
Promise.resolve(isAwait ? v[0] : v).then((y) => isAwait ? resume(k === "return" ? k : "next", v[1] ? { done: y.done, value: y.value } : y, yes, no) : yes({ value: y, done })).catch((e) => resume("throw", e, yes, no)); | ||
} catch (e) { | ||
no(e); | ||
} | ||
}, method = (k) => it[k] = (x) => new Promise((yes, no) => resume(k, x, yes, no)), it = {}; | ||
return generator = generator.apply(__this, __arguments), it[__knownSymbol("asyncIterator")] = () => it, method("next"), method("throw"), method("return"), it; | ||
}; | ||
var __forAwait = (obj, it, method) => (it = obj[__knownSymbol("asyncIterator")]) ? it.call(obj) : (obj = obj[__knownSymbol("iterator")](), it = {}, method = (key, fn) => (fn = obj[key]) && (it[key] = (arg) => new Promise((yes, no, done) => (arg = fn.call(obj, arg), done = arg.done, Promise.resolve(arg.value).then((value) => yes({ value, done }), no)))), method("next"), method("return"), it); | ||
@@ -78,70 +88,46 @@ // src/index.ts | ||
// src/codeInterpreter.ts | ||
var import_e2b = require("e2b"); | ||
var import_e2b2 = require("e2b"); | ||
// src/messaging.ts | ||
var import_isomorphic_ws = __toESM(require("isomorphic-ws")); | ||
// src/utils.ts | ||
function createDeferredPromise() { | ||
let resolve; | ||
let reject; | ||
const promise = new Promise((res, rej) => { | ||
resolve = res; | ||
reject = rej; | ||
var import_e2b = require("e2b"); | ||
function extractError(res) { | ||
return __async(this, null, function* () { | ||
if (res.ok) { | ||
return; | ||
} | ||
switch (res.status) { | ||
case 502: | ||
return new import_e2b.TimeoutError( | ||
`${yield res.text()}: This error is likely due to sandbox timeout. You can modify the sandbox timeout by passing 'timeoutMs' when starting the sandbox or calling '.setTimeout' on the sandbox with the desired timeout.` | ||
); | ||
case 404: | ||
return new import_e2b.NotFoundError(yield res.text()); | ||
default: | ||
return new import_e2b.SandboxError(`${res.status} ${res.statusText}`); | ||
} | ||
}); | ||
return { | ||
promise, | ||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion | ||
reject, | ||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion | ||
resolve | ||
}; | ||
} | ||
function id(length) { | ||
let result = ""; | ||
const characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; | ||
const charactersLength = characters.length; | ||
for (let i = 0; i < length; i++) { | ||
result += characters.charAt(Math.floor(Math.random() * charactersLength)); | ||
} | ||
return result; | ||
} | ||
// src/messaging.ts | ||
var CellMessage = class { | ||
constructor(line, timestamp, error) { | ||
this.line = line; | ||
this.timestamp = timestamp; | ||
this.error = error; | ||
} | ||
toString() { | ||
return this.line; | ||
} | ||
}; | ||
var ExecutionError = class { | ||
constructor(name, value, tracebackRaw) { | ||
constructor(name, value, traceback) { | ||
this.name = name; | ||
this.value = value; | ||
this.tracebackRaw = tracebackRaw; | ||
this.traceback = traceback; | ||
} | ||
/** | ||
* Returns the traceback of the error as a string. | ||
*/ | ||
get traceback() { | ||
return this.tracebackRaw.join("\n"); | ||
} | ||
}; | ||
var Result = class { | ||
constructor(data, isMainResult) { | ||
constructor(rawData, isMainResult) { | ||
this.isMainResult = isMainResult; | ||
this.text = data["text/plain"]; | ||
this.html = data["text/html"]; | ||
this.markdown = data["text/markdown"]; | ||
this.svg = data["image/svg+xml"]; | ||
this.png = data["image/png"]; | ||
this.jpeg = data["image/jpeg"]; | ||
this.pdf = data["application/pdf"]; | ||
this.latex = data["text/latex"]; | ||
this.json = data["application/json"]; | ||
this.javascript = data["application/javascript"]; | ||
const data = __spreadValues({}, rawData); | ||
delete data["type"]; | ||
delete data["is_main_result"]; | ||
this.text = data["text"]; | ||
this.html = data["html"]; | ||
this.markdown = data["markdown"]; | ||
this.svg = data["svg"]; | ||
this.png = data["png"]; | ||
this.jpeg = data["jpeg"]; | ||
this.pdf = data["pdf"]; | ||
this.latex = data["latex"]; | ||
this.json = data["json"]; | ||
this.javascript = data["javascript"]; | ||
this.isMainResult = isMainResult; | ||
@@ -152,12 +138,12 @@ this.raw = data; | ||
if (![ | ||
"text/plain", | ||
"text/html", | ||
"text/markdown", | ||
"image/svg+xml", | ||
"image/png", | ||
"image/jpeg", | ||
"application/pdf", | ||
"text/latex", | ||
"application/json", | ||
"application/javascript" | ||
"plain", | ||
"html", | ||
"markdown", | ||
"svg", | ||
"png", | ||
"jpeg", | ||
"pdf", | ||
"latex", | ||
"json", | ||
"javascript" | ||
].includes(key)) { | ||
@@ -226,3 +212,3 @@ this.extra[key] = data[key]; | ||
var Execution = class { | ||
constructor(results, logs, error, executionCount) { | ||
constructor(results = [], logs = { stdout: [], stderr: [] }, error, executionCount) { | ||
this.results = results; | ||
@@ -254,455 +240,227 @@ this.logs = logs; | ||
}; | ||
var CellExecution = class { | ||
constructor(onStdout, onStderr, onResult) { | ||
this.inputAccepted = false; | ||
this.execution = new Execution([], { stdout: [], stderr: [] }); | ||
this.onStdout = onStdout; | ||
this.onStderr = onStderr; | ||
this.onResult = onResult; | ||
} | ||
}; | ||
var JupyterKernelWebSocket = class { | ||
// constructor | ||
/** | ||
* Does not start WebSocket connection! | ||
* You need to call connect() method first. | ||
*/ | ||
constructor(url, sessionID) { | ||
this.url = url; | ||
this.sessionID = sessionID; | ||
this.idAwaiter = {}; | ||
this.cells = {}; | ||
} | ||
set ws(ws) { | ||
this._ws = ws; | ||
} | ||
get ws() { | ||
if (!this._ws) { | ||
throw new Error("WebSocket is not connected."); | ||
} | ||
return this._ws; | ||
} | ||
// public | ||
/** | ||
* Starts WebSocket connection. | ||
*/ | ||
connect() { | ||
this._ws = new import_isomorphic_ws.default(this.url); | ||
return this.listen(); | ||
} | ||
// events | ||
/** | ||
* Listens for messages from WebSocket server. | ||
* | ||
* Message types: | ||
* https://jupyter-client.readthedocs.io/en/stable/messaging.html | ||
* | ||
*/ | ||
listenMessages() { | ||
this.ws.onmessage = (e) => { | ||
const message = JSON.parse(e.data.toString()); | ||
const parentMsgId = message.parent_header.msg_id; | ||
if (parentMsgId == void 0) { | ||
console.warn(`Parent message ID not found. | ||
Message: ${message}`); | ||
return; | ||
} | ||
const cell = this.cells[parentMsgId]; | ||
if (!cell) { | ||
return; | ||
} | ||
const execution = cell.execution; | ||
if (message.msg_type == "error") { | ||
execution.error = new ExecutionError( | ||
message.content.ename, | ||
message.content.evalue, | ||
message.content.traceback | ||
); | ||
} else if (message.msg_type == "stream") { | ||
if (message.content.name == "stdout") { | ||
execution.logs.stdout.push(message.content.text); | ||
if (cell == null ? void 0 : cell.onStdout) { | ||
cell.onStdout( | ||
new CellMessage( | ||
message.content.text, | ||
(/* @__PURE__ */ new Date()).getTime() * 1e6, | ||
false | ||
) | ||
); | ||
} | ||
} else if (message.content.name == "stderr") { | ||
execution.logs.stderr.push(message.content.text); | ||
if (cell == null ? void 0 : cell.onStderr) { | ||
cell.onStderr( | ||
new CellMessage( | ||
message.content.text, | ||
(/* @__PURE__ */ new Date()).getTime() * 1e6, | ||
true | ||
) | ||
); | ||
} | ||
} | ||
} else if (message.msg_type == "display_data") { | ||
const result = new Result(message.content.data, false); | ||
function parseOutput(execution, line, onStdout, onStderr, onResult) { | ||
return __async(this, null, function* () { | ||
const msg = JSON.parse(line); | ||
switch (msg.type) { | ||
case "result": | ||
const result = new Result(__spreadProps(__spreadValues({}, msg), { type: void 0, is_main_result: void 0 }), msg.is_main_result); | ||
execution.results.push(result); | ||
if (cell.onResult) { | ||
cell.onResult(result); | ||
if (onResult) { | ||
yield onResult(result); | ||
} | ||
} else if (message.msg_type == "execute_result") { | ||
const result = new Result(message.content.data, true); | ||
execution.results.push(result); | ||
if (cell.onResult) { | ||
cell.onResult(result); | ||
break; | ||
case "stdout": | ||
execution.logs.stdout.push(msg.text); | ||
if (onStdout) { | ||
yield onStdout({ | ||
error: false, | ||
line: msg.text, | ||
timestamp: (/* @__PURE__ */ new Date()).getTime() * 1e3 | ||
}); | ||
} | ||
} else if (message.msg_type == "status") { | ||
if (message.content.execution_state == "idle") { | ||
if (cell.inputAccepted) { | ||
this.idAwaiter[parentMsgId](execution); | ||
break; | ||
case "stderr": | ||
execution.logs.stderr.push(msg.text); | ||
if (onStderr) { | ||
yield onStderr({ | ||
error: true, | ||
line: msg.text, | ||
timestamp: (/* @__PURE__ */ new Date()).getTime() * 1e3 | ||
}); | ||
} | ||
break; | ||
case "error": | ||
execution.error = new ExecutionError(msg.name, msg.value, msg.traceback); | ||
break; | ||
case "number_of_executions": | ||
execution.executionCount = msg.execution_count; | ||
break; | ||
} | ||
}); | ||
} | ||
// src/codeInterpreter.ts | ||
function readLines(stream) { | ||
return __asyncGenerator(this, null, function* () { | ||
const reader = stream.getReader(); | ||
let buffer = ""; | ||
try { | ||
while (true) { | ||
const { done, value } = yield new __await(reader.read()); | ||
if (value !== void 0) { | ||
buffer += new TextDecoder().decode(value); | ||
} | ||
if (done) { | ||
if (buffer.length > 0) { | ||
yield buffer; | ||
} | ||
} else if (message.content.execution_state == "error") { | ||
execution.error = new ExecutionError( | ||
message.content.ename, | ||
message.content.evalue, | ||
message.content.traceback | ||
); | ||
this.idAwaiter[parentMsgId](execution); | ||
break; | ||
} | ||
} else if (message.msg_type == "execute_reply") { | ||
if (message.content.status == "error") { | ||
execution.error = new ExecutionError( | ||
message.content.ename, | ||
message.content.evalue, | ||
message.content.traceback | ||
); | ||
} else if (message.content.status == "ok") { | ||
return; | ||
} | ||
} else if (message.msg_type == "execute_input") { | ||
cell.inputAccepted = true; | ||
cell.execution.executionCount = message.content.execution_count; | ||
} else { | ||
console.warn("[UNHANDLED MESSAGE TYPE]:", message.msg_type); | ||
let newlineIdx = -1; | ||
do { | ||
newlineIdx = buffer.indexOf("\n"); | ||
if (newlineIdx !== -1) { | ||
yield buffer.slice(0, newlineIdx); | ||
buffer = buffer.slice(newlineIdx + 1); | ||
} | ||
} while (newlineIdx !== -1); | ||
} | ||
}; | ||
} finally { | ||
reader.releaseLock(); | ||
} | ||
}); | ||
} | ||
var _JupyterExtension = class _JupyterExtension { | ||
constructor(url, connectionConfig) { | ||
this.url = url; | ||
this.connectionConfig = connectionConfig; | ||
} | ||
// communication | ||
/** | ||
* Sends code to be executed by Jupyter kernel. | ||
* @param code Code to be executed. | ||
* @param onStdout Callback for stdout messages. | ||
* @param onStderr Callback for stderr messages. | ||
* @param onResult Callback function to handle the result and display calls of the code execution. | ||
* @param timeoutMs Time in milliseconds to wait for response. | ||
* @returns Promise with execution result. | ||
*/ | ||
sendExecutionMessage(code, onStdout, onStderr, onResult, timeoutMs) { | ||
return new Promise((resolve, reject) => { | ||
const msgID = id(16); | ||
const data = this.sendExecuteRequest(msgID, code); | ||
let timeoutSet; | ||
if (timeoutMs) { | ||
timeoutSet = setTimeout(() => { | ||
delete this.idAwaiter[msgID]; | ||
reject( | ||
new Error( | ||
`Awaiting response to "${code}" with id: ${msgID} timed out.` | ||
) | ||
); | ||
}, timeoutMs); | ||
execCell(code, opts) { | ||
return __async(this, null, function* () { | ||
var _a, _b; | ||
const controller = new AbortController(); | ||
const requestTimeout = (_a = opts == null ? void 0 : opts.requestTimeoutMs) != null ? _a : this.connectionConfig.requestTimeoutMs; | ||
const reqTimer = requestTimeout ? setTimeout(() => { | ||
controller.abort(); | ||
}, requestTimeout) : void 0; | ||
const res = yield fetch(`${this.url}/execute`, { | ||
method: "POST", | ||
headers: { | ||
"Content-Type": "application/json" | ||
}, | ||
body: JSON.stringify({ | ||
code, | ||
context_id: opts == null ? void 0 : opts.kernelID | ||
}), | ||
keepalive: true | ||
}); | ||
const error = yield extractError(res); | ||
if (error) { | ||
throw error; | ||
} | ||
this.cells[msgID] = new CellExecution(onStdout, onStderr, onResult); | ||
this.idAwaiter[msgID] = (responseData) => { | ||
clearInterval(timeoutSet); | ||
delete this.idAwaiter[msgID]; | ||
resolve(responseData); | ||
}; | ||
const json = JSON.stringify(data); | ||
this.ws.send(json); | ||
}); | ||
} | ||
/** | ||
* Listens for messages from WebSocket server. | ||
*/ | ||
listen() { | ||
return new Promise((resolve, reject) => { | ||
this.ws.onopen = (e) => { | ||
resolve(e); | ||
}; | ||
this.listenMessages(); | ||
this.ws.onclose = (e) => { | ||
reject( | ||
new Error( | ||
`WebSocket closed with code: ${e.code} and reason: ${e.reason}` | ||
) | ||
); | ||
}; | ||
}); | ||
} | ||
/** | ||
* Creates a websocket message for code execution. | ||
* @param msg_id Unique message id. | ||
* @param code Code to be executed. | ||
*/ | ||
sendExecuteRequest(msg_id, code) { | ||
return { | ||
header: { | ||
msg_id, | ||
username: "e2b", | ||
session: this.sessionID, | ||
msg_type: "execute_request", | ||
version: "5.3" | ||
}, | ||
parent_header: {}, | ||
metadata: {}, | ||
content: { | ||
code, | ||
silent: false, | ||
store_history: true, | ||
user_expressions: {}, | ||
allow_stdin: false | ||
if (!res.body) { | ||
throw new Error(`Not response body: ${res.statusText} ${yield res == null ? void 0 : res.text()}`); | ||
} | ||
}; | ||
} | ||
/** | ||
* Closes WebSocket connection. | ||
*/ | ||
close() { | ||
this.ws.close(); | ||
} | ||
}; | ||
// src/codeInterpreter.ts | ||
var CodeInterpreter = class extends import_e2b.Sandbox { | ||
constructor() { | ||
super(...arguments); | ||
this.notebook = new JupyterExtension(this); | ||
} | ||
onInit(opts) { | ||
return __async(this, null, function* () { | ||
yield this.notebook.connect(opts == null ? void 0 : opts.requestTimeoutMs); | ||
clearTimeout(reqTimer); | ||
const bodyTimeout = (_b = opts == null ? void 0 : opts.timeoutMs) != null ? _b : _JupyterExtension.execTimeoutMs; | ||
const bodyTimer = bodyTimeout ? setTimeout(() => { | ||
controller.abort(); | ||
}, bodyTimeout) : void 0; | ||
const execution = new Execution(); | ||
try { | ||
try { | ||
for (var iter = __forAwait(readLines(res.body)), more, temp, error2; more = !(temp = yield iter.next()).done; more = false) { | ||
const chunk = temp.value; | ||
yield parseOutput(execution, chunk, opts == null ? void 0 : opts.onStdout, opts == null ? void 0 : opts.onStderr, opts == null ? void 0 : opts.onResult); | ||
} | ||
} catch (temp) { | ||
error2 = [temp]; | ||
} finally { | ||
try { | ||
more && (temp = iter.return) && (yield temp.call(iter)); | ||
} finally { | ||
if (error2) | ||
throw error2[0]; | ||
} | ||
} | ||
} finally { | ||
clearTimeout(bodyTimer); | ||
} | ||
return execution; | ||
}); | ||
} | ||
close() { | ||
return __async(this, null, function* () { | ||
yield this.notebook.close(); | ||
}); | ||
} | ||
getProtocol(baseProtocol = "http") { | ||
return this.connectionConfig.debug ? baseProtocol : `${baseProtocol}s`; | ||
} | ||
}; | ||
CodeInterpreter.defaultTemplate = "code-interpreter-stateful"; | ||
var JupyterExtension = class { | ||
constructor(sandbox) { | ||
this.sandbox = sandbox; | ||
this.connectedKernels = {}; | ||
this.kernelIDPromise = createDeferredPromise(); | ||
this.setDefaultKernelID = this.kernelIDPromise.resolve; | ||
} | ||
get defaultKernelID() { | ||
return this.kernelIDPromise.promise; | ||
} | ||
connect(requestTimeoutMs) { | ||
return __async(this, null, function* () { | ||
return this.startConnectingToDefaultKernel(this.setDefaultKernelID, { | ||
requestTimeoutMs | ||
createKernel() { | ||
return __async(this, arguments, function* ({ | ||
cwd, | ||
kernelName, | ||
requestTimeoutMs | ||
} = {}) { | ||
const res = yield fetch(`${this.url}/contexts`, { | ||
method: "POST", | ||
headers: { | ||
"Content-Type": "application/json" | ||
}, | ||
body: JSON.stringify({ | ||
name: kernelName, | ||
cwd | ||
}), | ||
keepalive: true, | ||
signal: this.connectionConfig.getSignal(requestTimeoutMs) | ||
}); | ||
const error = yield extractError(res); | ||
if (error) { | ||
throw error; | ||
} | ||
const data = yield res.json(); | ||
return data.id; | ||
}); | ||
} | ||
/** | ||
* Executes a code cell in a notebool cell. | ||
* | ||
* This method sends the provided code to a specified kernel in a remote notebook for execution. | ||
* @param code The code to be executed in the notebook cell. | ||
* @param kernelID The ID of the kernel to execute the code on. If not provided, the default kernel is used. | ||
* @param onStdout A callback function to handle standard output messages from the code execution. | ||
* @param onStderr A callback function to handle standard error messages from the code execution. | ||
* @param onResult A callback function to handle display data messages from the code execution. | ||
* @param timeoutMs The maximum time to wait for the code execution to complete, in milliseconds. | ||
* @returns A promise that resolves with the result of the code execution. | ||
*/ | ||
execCell(_0) { | ||
return __async(this, arguments, function* (code, { | ||
restartKernel() { | ||
return __async(this, arguments, function* ({ | ||
kernelID, | ||
onStdout, | ||
onStderr, | ||
onResult, | ||
timeoutMs: timeout | ||
requestTimeoutMs | ||
} = {}) { | ||
kernelID = kernelID || (yield this.defaultKernelID); | ||
const ws = this.connectedKernels[kernelID] || (yield this.connectToKernelWS(kernelID)); | ||
return yield ws.sendExecutionMessage( | ||
code, | ||
onStdout, | ||
onStderr, | ||
onResult, | ||
timeout | ||
); | ||
}); | ||
} | ||
startConnectingToDefaultKernel(resolve, opts) { | ||
return __async(this, null, function* () { | ||
const kernelID = (yield this.sandbox.files.read("/root/.jupyter/kernel_id", opts)).trim(); | ||
yield this.connectToKernelWS(kernelID); | ||
resolve(kernelID); | ||
}); | ||
} | ||
/** | ||
* Connects to a kernel's WebSocket. | ||
* | ||
* This method establishes a WebSocket connection to the specified kernel. It is used internally | ||
* to facilitate real-time communication with the kernel, enabling operations such as executing | ||
* code and receiving output. The connection details are managed within the method, including | ||
* the retrieval of the necessary WebSocket URL from the kernel's information. | ||
* | ||
* @param kernelID The unique identifier of the kernel to connect to. | ||
* @param sessionID The unique identifier of the session to connect to. | ||
* @throws {Error} Throws an error if the connection to the kernel's WebSocket cannot be established. | ||
*/ | ||
connectToKernelWS(kernelID, sessionID) { | ||
return __async(this, null, function* () { | ||
const url = `${this.sandbox.getProtocol("ws")}://${this.sandbox.getHost( | ||
8888 | ||
)}/api/kernels/${kernelID}/channels`; | ||
sessionID = sessionID || id(16); | ||
const ws = new JupyterKernelWebSocket(url, sessionID); | ||
yield ws.connect(); | ||
this.connectedKernels[kernelID] = ws; | ||
return ws; | ||
}); | ||
} | ||
/** | ||
* Creates a new Jupyter kernel. It can be useful if you want to have multiple independent code execution environments. | ||
* | ||
* The kernel can be optionally configured to start in a specific working directory and/or | ||
* with a specific kernel name. If no kernel name is provided, the default kernel will be used. | ||
* Once the kernel is created, this method establishes a WebSocket connection to the new kernel for | ||
* real-time communication. | ||
* | ||
* @param cwd Sets the current working directory where the kernel should start. Defaults to "/home/user". | ||
* @param kernelName The name of the kernel to create, useful if you have multiple kernel types. If not provided, the default kernel will be used. | ||
* @returns A promise that resolves with the ID of the newly created kernel. | ||
* @throws {Error} Throws an error if the kernel creation fails. | ||
*/ | ||
createKernel(cwd = "/home/user", kernelName) { | ||
return __async(this, null, function* () { | ||
kernelName = kernelName || "python3"; | ||
const data = { path: id(16), kernel: { name: kernelName }, type: "notebook", name: id(16) }; | ||
const response = yield fetch( | ||
`${this.sandbox.getProtocol()}://${this.sandbox.getHost( | ||
8888 | ||
)}/api/sessions`, | ||
{ | ||
method: "POST", | ||
body: JSON.stringify(data) | ||
} | ||
); | ||
if (!response.ok) { | ||
throw new Error(`Failed to create kernel: ${response.statusText}`); | ||
kernelID = kernelID || _JupyterExtension.defaultKernelID; | ||
const res = yield fetch(`${this.url}/contexts/${kernelID}/restart`, { | ||
method: "POST", | ||
headers: { | ||
"Content-Type": "application/json" | ||
}, | ||
keepalive: true, | ||
signal: this.connectionConfig.getSignal(requestTimeoutMs) | ||
}); | ||
const error = yield extractError(res); | ||
if (error) { | ||
throw error; | ||
} | ||
const sessionInfo = yield response.json(); | ||
const kernelID = sessionInfo.kernel.id; | ||
const sessionID = sessionInfo.id; | ||
const patchResponse = yield fetch( | ||
`${this.sandbox.getProtocol()}://${this.sandbox.getHost( | ||
8888 | ||
)}/api/sessions/${sessionID}`, | ||
{ | ||
method: "PATCH", | ||
body: JSON.stringify({ path: cwd }) | ||
} | ||
); | ||
if (!patchResponse.ok) { | ||
throw new Error(`Failed to create kernel: ${response.statusText}`); | ||
} | ||
yield this.connectToKernelWS(kernelID, sessionID); | ||
return kernelID; | ||
}); | ||
} | ||
/** | ||
* Restarts an existing Jupyter kernel. This can be useful to reset the kernel's state or to recover from errors. | ||
* | ||
* @param kernelID The unique identifier of the kernel to restart. If not provided, the default kernel is restarted. | ||
* @throws {Error} Throws an error if the kernel restart fails or if the operation times out. | ||
*/ | ||
restartKernel(kernelID) { | ||
return __async(this, null, function* () { | ||
kernelID = kernelID || (yield this.defaultKernelID); | ||
this.connectedKernels[kernelID].close(); | ||
delete this.connectedKernels[kernelID]; | ||
const response = yield fetch( | ||
`${this.sandbox.getProtocol()}://${this.sandbox.getHost( | ||
8888 | ||
)}/api/kernels/${kernelID}/restart`, | ||
{ | ||
method: "POST" | ||
} | ||
); | ||
if (!response.ok) { | ||
throw new Error(`Failed to restart kernel ${kernelID}`); | ||
shutdownKernel() { | ||
return __async(this, arguments, function* ({ | ||
kernelID, | ||
requestTimeoutMs | ||
} = {}) { | ||
kernelID = kernelID || _JupyterExtension.defaultKernelID; | ||
const res = yield fetch(`${this.url}/contexts/${kernelID}`, { | ||
method: "DELETE", | ||
keepalive: true, | ||
signal: this.connectionConfig.getSignal(requestTimeoutMs) | ||
}); | ||
const error = yield extractError(res); | ||
if (error) { | ||
throw error; | ||
} | ||
yield this.connectToKernelWS(kernelID); | ||
}); | ||
} | ||
/** | ||
* Shuts down an existing Jupyter kernel. This method is used to gracefully terminate a kernel's process. | ||
* @param kernelID The unique identifier of the kernel to shutdown. If not provided, the default kernel is shutdown. | ||
* @throws {Error} Throws an error if the kernel shutdown fails or if the operation times out. | ||
*/ | ||
shutdownKernel(kernelID) { | ||
return __async(this, null, function* () { | ||
kernelID = kernelID || (yield this.defaultKernelID); | ||
this.connectedKernels[kernelID].close(); | ||
delete this.connectedKernels[kernelID]; | ||
const response = yield fetch( | ||
`${this.sandbox.getProtocol()}://${this.sandbox.getHost( | ||
8888 | ||
)}/api/kernels/${kernelID}`, | ||
{ | ||
method: "DELETE" | ||
} | ||
); | ||
if (!response.ok) { | ||
throw new Error(`Failed to shutdown kernel ${kernelID}`); | ||
} | ||
}); | ||
} | ||
/** | ||
* Lists all available Jupyter kernels. | ||
* | ||
* This method fetches a list of all currently available Jupyter kernels from the server. It can be used | ||
* to retrieve the IDs of all kernels that are currently running or available for connection. | ||
* | ||
* @returns A promise that resolves to an array of objects containing a kernel ID and kernel name. | ||
* @throws {Error} Throws an error if the request to list kernels fails. | ||
*/ | ||
listKernels() { | ||
return __async(this, null, function* () { | ||
const response = yield fetch( | ||
`${this.sandbox.getProtocol()}://${this.sandbox.getHost( | ||
8888 | ||
)}/api/kernels`, | ||
{ | ||
method: "GET" | ||
} | ||
); | ||
if (!response.ok) { | ||
throw new Error(`Failed to list kernels: ${response.statusText}`); | ||
return __async(this, arguments, function* ({ | ||
requestTimeoutMs | ||
} = {}) { | ||
const res = yield fetch(`${this.url}/contexts`, { | ||
keepalive: true, | ||
signal: this.connectionConfig.getSignal(requestTimeoutMs) | ||
}); | ||
const error = yield extractError(res); | ||
if (error) { | ||
throw error; | ||
} | ||
return (yield response.json()).map((kernel) => ({ id: kernel.id, name: kernel.name })); | ||
return (yield res.json()).map((kernel) => ({ kernelID: kernel.id, name: kernel.name })); | ||
}); | ||
} | ||
/** | ||
* Close all the websocket connections to the kernels. It doesn't shutdown the kernels. | ||
*/ | ||
close() { | ||
return __async(this, null, function* () { | ||
for (const kernelID of Object.keys(this.connectedKernels)) { | ||
this.connectedKernels[kernelID].close(); | ||
} | ||
}); | ||
}; | ||
_JupyterExtension.execTimeoutMs = 3e5; | ||
_JupyterExtension.defaultKernelID = "default"; | ||
var JupyterExtension = _JupyterExtension; | ||
var _CodeInterpreter = class _CodeInterpreter extends import_e2b2.Sandbox { | ||
constructor() { | ||
super(...arguments); | ||
this.notebook = new JupyterExtension( | ||
`${this.connectionConfig.debug ? "http" : "https"}://${this.getHost(_CodeInterpreter.jupyterPort)}`, | ||
this.connectionConfig | ||
); | ||
} | ||
}; | ||
_CodeInterpreter.defaultTemplate = "ci-no-ws"; | ||
_CodeInterpreter.jupyterPort = 49999; | ||
var CodeInterpreter = _CodeInterpreter; | ||
@@ -709,0 +467,0 @@ // src/index.ts |
{ | ||
"name": "@e2b/code-interpreter", | ||
"version": "0.0.9-beta.4", | ||
"version": "0.0.9-beta.5", | ||
"description": "E2B Code Interpreter - Stateful code execution", | ||
@@ -39,7 +39,7 @@ "homepage": "https://e2b.dev", | ||
"dotenv": "^16.4.5", | ||
"knip": "^5.23.2", | ||
"knip": "^5.25.1", | ||
"npm-check-updates": "^16.14.20", | ||
"tsup": "^8.1.0", | ||
"typescript": "^5.5.2", | ||
"vitest": "^1.6.0" | ||
"typescript": "^5.5.3", | ||
"vitest": "^2.0.1" | ||
}, | ||
@@ -65,10 +65,4 @@ "files": [ | ||
"dependencies": { | ||
"e2b": "0.16.2-beta.15", | ||
"isomorphic-ws": "^5.0.0", | ||
"ws": "^8.17.1" | ||
"e2b": "0.16.2-beta.23" | ||
}, | ||
"optionalDependencies": { | ||
"bufferutil": "^4.0.8", | ||
"utf-8-validate": "^6.0.4" | ||
}, | ||
"engines": { | ||
@@ -75,0 +69,0 @@ "node": ">=18" |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
1
87408
1142
+ Addede2b@0.16.2-beta.23(transitive)
- Removedisomorphic-ws@^5.0.0
- Removedws@^8.17.1
- Removedbufferutil@4.0.8(transitive)
- Removede2b@0.16.2-beta.15(transitive)
- Removedisomorphic-ws@5.0.0(transitive)
- Removednode-gyp-build@4.8.4(transitive)
- Removedutf-8-validate@6.0.5(transitive)
- Removedws@8.18.0(transitive)
Updatede2b@0.16.2-beta.23