e2b
Version:
E2B SDK that give agents cloud environments
1 lines • 149 kB
Source Map (JSON)
{"version":3,"sources":["../src/api/index.ts","../src/api/metadata.ts","../package.json","../src/errors.ts","../src/logs.ts","../src/connectionConfig.ts","../src/sandbox/filesystem/index.ts","../src/envd/api.ts","../src/envd/rpc.ts","../src/envd/filesystem/filesystem_pb.ts","../src/sandbox/filesystem/watchHandle.ts","../src/envd/versions.ts","../src/sandbox/commands/commandHandle.ts","../src/sandbox/index.ts","../src/sandbox/commands/index.ts","../src/envd/process/process_pb.ts","../src/sandbox/commands/pty.ts","../src/sandbox/sandboxApi.ts","../src/index.ts"],"sourcesContent":["import createClient, { FetchResponse } from 'openapi-fetch'\n\nimport type { components, paths } from './schema.gen'\nimport { defaultHeaders } from './metadata'\nimport { ConnectionConfig } from '../connectionConfig'\nimport { AuthenticationError, RateLimitError, SandboxError } from '../errors'\nimport { createApiLogger } from '../logs'\n\nexport function handleApiError(\n response: FetchResponse<any, any, any>\n): Error | undefined {\n if (!response.error) {\n return\n }\n\n if (response.response.status === 429) {\n return new RateLimitError('Rate limit exceeded, please try again later.')\n }\n\n const message = response.error?.message ?? response.error\n return new SandboxError(`${response.response.status}: ${message}`)\n}\n\n/**\n * Client for interacting with the E2B API.\n */\nclass ApiClient {\n readonly api: ReturnType<typeof createClient<paths>>\n\n constructor(\n config: ConnectionConfig,\n opts: {\n requireAccessToken?: boolean\n requireApiKey?: boolean\n } = { requireAccessToken: false, requireApiKey: true }\n ) {\n if (!opts?.requireApiKey && !config.apiKey) {\n throw new AuthenticationError(\n 'API key is required, please visit the Team tab at https://e2b.dev/dashboard to get your API key. ' +\n 'You can either set the environment variable `E2B_API_KEY` ' +\n \"or you can pass it directly to the sandbox like Sandbox.create({ apiKey: 'e2b_...' })\"\n )\n }\n\n if (opts?.requireAccessToken && !config.accessToken) {\n throw new AuthenticationError(\n 'Access token is required, please visit the Personal tab at https://e2b.dev/dashboard to get your access token. ' +\n 'You can set the environment variable `E2B_ACCESS_TOKEN` or pass the `accessToken` in options.'\n )\n }\n\n this.api = createClient<paths>({\n baseUrl: config.apiUrl,\n // keepalive: true, // TODO: Return keepalive\n headers: {\n ...defaultHeaders,\n ...(config.apiKey && { 'X-API-KEY': config.apiKey }),\n ...(config.accessToken && {\n Authorization: `Bearer ${config.accessToken}`,\n }),\n ...config.headers,\n },\n })\n\n if (config.logger) {\n this.api.use(createApiLogger(config.logger))\n }\n }\n}\n\nexport type { components, paths }\nexport { ApiClient }\n","import platform from 'platform'\n\nimport { version } from '../../package.json'\n\ndeclare let window: any\n\ntype Runtime = 'node' | 'browser' | 'deno' | 'bun' | 'vercel-edge' | 'cloudflare-worker' | 'unknown'\n\nfunction getRuntime(): { runtime: Runtime; version: string } {\n // @ts-ignore\n if ((globalThis as any).Bun) {\n // @ts-ignore\n return { runtime: 'bun', version: globalThis.Bun.version }\n }\n\n // @ts-ignore\n if ((globalThis as any).Deno) {\n // @ts-ignore\n return { runtime: 'deno', version: globalThis.Deno.version.deno }\n }\n\n if ((globalThis as any).process?.release?.name === 'node') {\n return { runtime: 'node', version: platform.version || 'unknown' }\n }\n\n // @ts-ignore\n if (typeof EdgeRuntime === 'string') {\n return { runtime: 'vercel-edge', version: 'unknown' }\n }\n\n if ((globalThis as any).navigator?.userAgent === 'Cloudflare-Workers') {\n return { runtime: 'cloudflare-worker', version: 'unknown' }\n }\n\n if (typeof window !== 'undefined') {\n return { runtime: 'browser', version: platform.version || 'unknown' }\n }\n\n return { runtime: 'unknown', version: 'unknown' }\n}\n\nexport const { runtime, version: runtimeVersion } = getRuntime()\n\nexport const defaultHeaders = {\n browser: (typeof window !== 'undefined' && platform.name) || 'unknown',\n lang: 'js',\n lang_version: runtimeVersion,\n package_version: version,\n publisher: 'e2b',\n sdk_runtime: runtime,\n system: platform.os?.family || 'unknown',\n}\n\nexport function getEnvVar(name: string) {\n if (runtime === 'deno') {\n // @ts-ignore\n return Deno.env.get(name)\n }\n\n if (typeof process === 'undefined') {\n return ''\n }\n\n return process.env[name]\n}\n","{\n \"name\": \"e2b\",\n \"version\": \"1.2.2\",\n \"description\": \"E2B SDK that give agents cloud environments\",\n \"homepage\": \"https://e2b.dev\",\n \"license\": \"MIT\",\n \"author\": {\n \"name\": \"FoundryLabs, Inc.\",\n \"email\": \"hello@e2b.dev\",\n \"url\": \"https://e2b.dev\"\n },\n \"bugs\": \"https://github.com/e2b-dev/e2b/issues\",\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/e2b-dev/e2b\",\n \"directory\": \"packages/js-sdk\"\n },\n \"publishConfig\": {\n \"access\": \"public\"\n },\n \"sideEffects\": false,\n \"main\": \"dist/index.js\",\n \"module\": \"dist/index.mjs\",\n \"types\": \"dist/index.d.ts\",\n \"scripts\": {\n \"prepublishOnly\": \"pnpm build\",\n \"build\": \"tsc --noEmit && tsup\",\n \"dev\": \"tsup --watch\",\n \"example\": \"tsx example.mts\",\n \"test\": \"vitest run\",\n \"generate\": \"python ./../../spec/remove_extra_tags.py sandboxes templates && openapi-typescript ../../spec/openapi_generated.yml -x api_key --array-length --alphabetize --output src/api/schema.gen.ts\",\n \"generate-envd-api\": \"openapi-typescript ../../spec/envd/envd.yaml -x api_key --array-length --alphabetize --output src/envd/schema.gen.ts\",\n \"generate-ref\": \"./scripts/generate_sdk_ref.sh\",\n \"check-deps\": \"knip\",\n \"update-deps\": \"ncu -u && pnpm i\",\n \"postPublish\": \"./scripts/post-publish.sh || true\",\n \"test:bun\": \"bun test tests/runtimes/bun --env-file=.env\",\n \"test:deno\": \"deno test tests/runtimes/deno/ --allow-net --allow-read --allow-env --unstable-sloppy-imports --trace-leaks\",\n \"test:integration\": \"E2B_INTEGRATION_TEST=1 vitest run tests/integration/**\"\n },\n \"devDependencies\": {\n \"@testing-library/react\": \"^16.2.0\",\n \"@types/node\": \"^18.18.6\",\n \"@types/platform\": \"^1.3.6\",\n \"@types/react\": \"^18.3.11\",\n \"@typescript-eslint/eslint-plugin\": \"^7.11.0\",\n \"@typescript-eslint/parser\": \"^7.11.0\",\n \"@vitejs/plugin-react\": \"^4.3.4\",\n \"@vitest/browser\": \"^3.0.5\",\n \"dotenv\": \"^16.4.5\",\n \"knip\": \"^5.43.6\",\n \"npm-check-updates\": \"^16.14.20\",\n \"openapi-typescript\": \"^7.6.1\",\n \"playwright\": \"^1.48.0\",\n \"react\": \"^18.3.1\",\n \"tsup\": \"^8.3.6\",\n \"typedoc\": \"0.26.8\",\n \"typedoc-plugin-markdown\": \"4.2.7\",\n \"typescript\": \"^5.4.5\",\n \"vitest\": \"^3.0.5\",\n \"vitest-browser-react\": \"^0.0.4\"\n },\n \"files\": [\n \"dist\",\n \"README.md\",\n \"package.json\"\n ],\n \"keywords\": [\n \"e2b\",\n \"ai-agents\",\n \"agents\",\n \"ai\",\n \"code-interpreter\",\n \"sandbox\",\n \"code\",\n \"runtime\",\n \"vm\",\n \"nodejs\",\n \"javascript\",\n \"typescript\"\n ],\n \"dependencies\": {\n \"@bufbuild/protobuf\": \"^2.2.2\",\n \"@connectrpc/connect\": \"2.0.0-rc.3\",\n \"@connectrpc/connect-web\": \"2.0.0-rc.3\",\n \"compare-versions\": \"^6.1.0\",\n \"openapi-fetch\": \"^0.9.7\",\n \"platform\": \"^1.3.6\"\n },\n \"engines\": {\n \"node\": \">=18\"\n },\n \"browserslist\": [\n \"defaults\"\n ]\n}\n","// This is the message for the sandbox timeout error when the response code is 502/Unavailable\nexport function formatSandboxTimeoutError(message: string) {\n return new TimeoutError(\n `${message}: 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.`\n )\n}\n\n/**\n * Base class for all sandbox errors.\n *\n * Thrown when general sandbox errors occur.\n */\nexport class SandboxError extends Error {\n constructor(message: any) {\n super(message)\n this.name = 'SandboxError'\n }\n}\n\n/**\n * Thrown when a timeout error occurs.\n *\n * The [unavailable] error type is caused by sandbox timeout.\n *\n * The [canceled] error type is caused by exceeding request timeout.\n *\n * The [deadline_exceeded] error type is caused by exceeding the timeout for command execution, watch, etc.\n *\n * The [unknown] error type is sometimes caused by the sandbox timeout when the request is not processed correctly.\n */\nexport class TimeoutError extends SandboxError {\n constructor(message: string) {\n super(message)\n this.name = 'TimeoutError'\n }\n}\n\n/**\n * Thrown when an invalid argument is provided.\n */\nexport class InvalidArgumentError extends SandboxError {\n constructor(message: string) {\n super(message)\n this.name = 'InvalidArgumentError'\n }\n}\n\n/**\n * Thrown when there is not enough disk space.\n */\nexport class NotEnoughSpaceError extends SandboxError {\n constructor(message: string) {\n super(message)\n this.name = 'NotEnoughSpaceError'\n }\n}\n\n/**\n * Thrown when a resource is not found.\n */\nexport class NotFoundError extends SandboxError {\n constructor(message: string) {\n super(message)\n this.name = 'NotFoundError'\n }\n}\n\n/**\n * Thrown when authentication fails.\n */\nexport class AuthenticationError extends SandboxError {\n constructor(message: any) {\n super(message)\n this.name = 'AuthenticationError'\n }\n}\n\n/**\n * Thrown when the template uses old envd version. It isn't compatible with the new SDK.\n */\nexport class TemplateError extends SandboxError {\n constructor(message: string) {\n super(message)\n this.name = 'TemplateError'\n }\n}\n\n/**\n * Thrown when the API rate limit is exceeded.\n */\nexport class RateLimitError extends SandboxError {\n constructor(message: any) {\n super(message)\n this.name = 'RateLimitError'\n }\n}\n","import type { Interceptor } from '@connectrpc/connect'\nimport type { Middleware } from 'openapi-fetch'\n\n/**\n * Logger interface compatible with {@link console} used for logging Sandbox messages.\n */\nexport interface Logger {\n /**\n * Debug level logging method.\n */\n debug?: (...args: any[]) => void\n /**\n * Info level logging method.\n */\n info?: (...args: any[]) => void\n /**\n * Error level logging method.\n */\n error?: (...args: any[]) => void\n}\n\nfunction formatLog(log: any) {\n return JSON.parse(JSON.stringify(log))\n}\n\nexport function createRpcLogger(logger: Logger): Interceptor {\n async function* logEach(stream: AsyncIterable<any>) {\n for await (const m of stream) {\n logger.debug?.('Response stream:', formatLog(m))\n yield m\n }\n }\n\n return (next) => async (req) => {\n logger.info?.(`Request: POST ${req.url}`)\n\n const res = await next(req)\n if (res.stream) {\n return {\n ...res,\n message: logEach(res.message),\n }\n } else {\n logger.info?.('Response:', formatLog(res.message))\n }\n\n return res\n }\n}\n\nexport function createApiLogger(logger: Logger): Middleware {\n return {\n async onRequest(req) {\n logger.info?.(`Request ${req.method} ${req.url}`)\n\n return req\n },\n async onResponse(res) {\n if (res.status >= 400) {\n logger.error?.('Response:', res.status, res.statusText)\n } else {\n logger.info?.('Response:', res.status, res.statusText)\n }\n\n return res\n },\n }\n}\n","import { Logger } from './logs'\nimport { getEnvVar } from './api/metadata'\n\nconst REQUEST_TIMEOUT_MS = 30_000 // 30 seconds\nexport const KEEPALIVE_PING_INTERVAL_SEC = 50 // 50 seconds\n\nexport const KEEPALIVE_PING_HEADER = 'Keepalive-Ping-Interval'\n\n/**\n * Connection options for requests to the API.\n */\nexport interface ConnectionOpts {\n /**\n * E2B API key to use for authentication.\n *\n * @default E2B_API_KEY // environment variable\n */\n apiKey?: string\n /**\n * E2B access token to use for authentication.\n *\n * @default E2B_ACCESS_TOKEN // environment variable\n */\n accessToken?: string\n /**\n * Domain to use for the API.\n *\n * @default E2B_DOMAIN // environment variable or `e2b.app`\n */\n domain?: string\n /**\n * If true the SDK starts in the debug mode and connects to the local envd API server.\n * @internal\n * @default E2B_DEBUG // environment variable or `false`\n */\n debug?: boolean\n /**\n * Timeout for requests to the API in **milliseconds**.\n *\n * @default 30_000 // 30 seconds\n */\n requestTimeoutMs?: number\n /**\n * Logger to use for logging messages. It can accept any object that implements `Logger` interface—for example, {@link console}.\n */\n logger?: Logger\n\n /**\n * Additional headers to send with the request.\n */\n headers?: Record<string, string>\n}\n\n/**\n * Configuration for connecting to the API.\n */\nexport class ConnectionConfig {\n readonly debug: boolean\n readonly domain: string\n readonly apiUrl: string\n readonly logger?: Logger\n\n readonly requestTimeoutMs: number\n\n readonly apiKey?: string\n readonly accessToken?: string\n\n readonly headers?: Record<string, string>\n\n constructor(opts?: ConnectionOpts) {\n this.apiKey = opts?.apiKey || ConnectionConfig.apiKey\n this.debug = opts?.debug || ConnectionConfig.debug\n this.domain = opts?.domain || ConnectionConfig.domain\n this.accessToken = opts?.accessToken || ConnectionConfig.accessToken\n this.requestTimeoutMs = opts?.requestTimeoutMs ?? REQUEST_TIMEOUT_MS\n this.logger = opts?.logger\n this.headers = opts?.headers\n\n this.apiUrl = this.debug\n ? 'http://localhost:3000'\n : `https://api.${this.domain}`\n }\n\n private static get domain() {\n return getEnvVar('E2B_DOMAIN') || 'e2b.app'\n }\n\n private static get debug() {\n return (getEnvVar('E2B_DEBUG') || 'false').toLowerCase() === 'true'\n }\n\n private static get apiKey() {\n return getEnvVar('E2B_API_KEY')\n }\n\n private static get accessToken() {\n return getEnvVar('E2B_ACCESS_TOKEN')\n }\n\n getSignal(requestTimeoutMs?: number) {\n const timeout = requestTimeoutMs ?? this.requestTimeoutMs\n\n return timeout ? AbortSignal.timeout(timeout) : undefined\n }\n}\n\n/**\n * User used for the operation in the sandbox.\n */\nexport type Username = 'root' | 'user'\n\nexport const defaultUsername: Username = 'user'\n","import {\n createClient,\n Transport,\n Client,\n ConnectError,\n Code,\n} from '@connectrpc/connect'\nimport {\n ConnectionConfig,\n ConnectionOpts,\n defaultUsername,\n KEEPALIVE_PING_HEADER,\n KEEPALIVE_PING_INTERVAL_SEC,\n Username,\n} from '../../connectionConfig'\n\nimport { handleEnvdApiError, handleWatchDirStartEvent } from '../../envd/api'\nimport { authenticationHeader, handleRpcError } from '../../envd/rpc'\n\nimport { EnvdApiClient } from '../../envd/api'\nimport {\n FileType as FsFileType,\n Filesystem as FilesystemService,\n} from '../../envd/filesystem/filesystem_pb'\n\nimport { FilesystemEvent, WatchHandle } from './watchHandle'\n\nimport { compareVersions } from 'compare-versions'\nimport { TemplateError } from '../../errors'\nimport { ENVD_VERSION_RECURSIVE_WATCH } from '../../envd/versions'\n\n/**\n * Sandbox filesystem object information.\n */\nexport interface EntryInfo {\n /**\n * Name of the filesystem object.\n */\n name: string\n /**\n * Type of the filesystem object.\n */\n type?: FileType\n /**\n * Path to the filesystem object.\n */\n path: string\n}\n\n/**\n * Sandbox filesystem object type.\n */\nexport const enum FileType {\n /**\n * Filesystem object is a file.\n */\n FILE = 'file',\n /**\n * Filesystem object is a directory.\n */\n DIR = 'dir',\n}\n\nexport type WriteEntry = {\n path: string\n data: string | ArrayBuffer | Blob | ReadableStream\n}\n\nfunction mapFileType(fileType: FsFileType) {\n switch (fileType) {\n case FsFileType.DIRECTORY:\n return FileType.DIR\n case FsFileType.FILE:\n return FileType.FILE\n }\n}\n\n/**\n * Options for the sandbox filesystem operations.\n */\nexport interface FilesystemRequestOpts\n extends Partial<Pick<ConnectionOpts, 'requestTimeoutMs'>> {\n /**\n * User to use for the operation in the sandbox.\n * This affects the resolution of relative paths and ownership of the created filesystem objects.\n */\n user?: Username\n}\n\n/**\n * Options for watching a directory.\n */\nexport interface WatchOpts extends FilesystemRequestOpts {\n /**\n * Timeout for the watch operation in **milliseconds**.\n * You can pass `0` to disable the timeout.\n *\n * @default 60_000 // 60 seconds\n */\n timeoutMs?: number\n /**\n * Callback to call when the watch operation stops.\n */\n onExit?: (err?: Error) => void | Promise<void>\n /**\n * Watch the directory recursively\n */\n recursive?: boolean\n}\n\n/**\n * Module for interacting with the sandbox filesystem.\n */\nexport class Filesystem {\n private readonly rpc: Client<typeof FilesystemService>\n\n private readonly defaultWatchTimeout = 60_000 // 60 seconds\n private readonly defaultWatchRecursive = false\n\n constructor(\n transport: Transport,\n private readonly envdApi: EnvdApiClient,\n private readonly connectionConfig: ConnectionConfig\n ) {\n this.rpc = createClient(FilesystemService, transport)\n }\n\n /**\n * Read file content as a `string`.\n *\n * You can pass `text`, `bytes`, `blob`, or `stream` to `opts.format` to change the return type.\n *\n * @param path path to the file.\n * @param opts connection options.\n * @param [opts.format] format of the file content—`text` by default.\n *\n * @returns file content as string\n */\n async read(\n path: string,\n opts?: FilesystemRequestOpts & { format?: 'text' }\n ): Promise<string>\n /**\n * Read file content as a `Uint8Array`.\n *\n * You can pass `text`, `bytes`, `blob`, or `stream` to `opts.format` to change the return type.\n *\n * @param path path to the file.\n * @param opts connection options.\n * @param [opts.format] format of the file content—`bytes`.\n *\n * @returns file content as `Uint8Array`\n */\n async read(\n path: string,\n opts?: FilesystemRequestOpts & { format: 'bytes' }\n ): Promise<Uint8Array>\n /**\n * Read file content as a `Blob`.\n *\n * You can pass `text`, `bytes`, `blob`, or `stream` to `opts.format` to change the return type.\n *\n * @param path path to the file.\n * @param opts connection options.\n * @param [opts.format] format of the file content—`blob`.\n *\n * @returns file content as `Blob`\n */\n async read(\n path: string,\n opts?: FilesystemRequestOpts & { format: 'blob' }\n ): Promise<Blob>\n /**\n * Read file content as a `ReadableStream`.\n *\n * You can pass `text`, `bytes`, `blob`, or `stream` to `opts.format` to change the return type.\n *\n * @param path path to the file.\n * @param opts connection options.\n * @param [opts.format] format of the file content—`stream`.\n *\n * @returns file content as `ReadableStream`\n */\n async read(\n path: string,\n opts?: FilesystemRequestOpts & { format: 'stream' }\n ): Promise<ReadableStream<Uint8Array>>\n async read(\n path: string,\n opts?: FilesystemRequestOpts & {\n format?: 'text' | 'stream' | 'bytes' | 'blob'\n }\n ): Promise<unknown> {\n const format = opts?.format ?? 'text'\n\n const res = await this.envdApi.api.GET('/files', {\n params: {\n query: {\n path,\n username: opts?.user || defaultUsername,\n },\n },\n parseAs: format === 'bytes' ? 'arrayBuffer' : format,\n signal: this.connectionConfig.getSignal(opts?.requestTimeoutMs),\n })\n\n const err = await handleEnvdApiError(res)\n if (err) {\n throw err\n }\n\n if (format === 'bytes') {\n return new Uint8Array(res.data as ArrayBuffer)\n }\n\n // When the file is empty, res.data is parsed as `{}`. This is a workaround to return an empty string.\n if (res.response.headers.get('content-length') === '0') {\n return ''\n }\n\n return res.data\n }\n\n /**\n * Write content to a file.\n *\n *\n * Writing to a file that doesn't exist creates the file.\n *\n * Writing to a file that already exists overwrites the file.\n *\n * Writing to a file at path that doesn't exist creates the necessary directories.\n *\n * @param path path to file.\n * @param data data to write to the file. Data can be a string, `ArrayBuffer`, `Blob`, or `ReadableStream`.\n * @param opts connection options.\n *\n * @returns information about the written file\n */\n async write(\n path: string,\n data: string | ArrayBuffer | Blob | ReadableStream,\n opts?: FilesystemRequestOpts\n ): Promise<EntryInfo>\n async write(\n files: WriteEntry[],\n opts?: FilesystemRequestOpts\n ): Promise<EntryInfo[]>\n async write(\n pathOrFiles: string | WriteEntry[],\n dataOrOpts?:\n | string\n | ArrayBuffer\n | Blob\n | ReadableStream\n | FilesystemRequestOpts,\n opts?: FilesystemRequestOpts\n ): Promise<EntryInfo | EntryInfo[]> {\n if (typeof pathOrFiles !== 'string' && !Array.isArray(pathOrFiles)) {\n throw new Error('Path or files are required')\n }\n\n if (typeof pathOrFiles === 'string' && Array.isArray(dataOrOpts)) {\n throw new Error(\n 'Cannot specify both path and array of files. You have to specify either path and data for a single file or an array for multiple files.'\n )\n }\n\n const { path, writeOpts, writeFiles } =\n typeof pathOrFiles === 'string'\n ? {\n path: pathOrFiles,\n writeOpts: opts as FilesystemRequestOpts,\n writeFiles: [\n {\n data: dataOrOpts as\n | string\n | ArrayBuffer\n | Blob\n | ReadableStream,\n },\n ],\n }\n : {\n path: undefined,\n writeOpts: dataOrOpts as FilesystemRequestOpts,\n writeFiles: pathOrFiles as WriteEntry[],\n }\n\n if (writeFiles.length === 0) return [] as EntryInfo[]\n\n const blobs = await Promise.all(\n writeFiles.map((f) => new Response(f.data).blob())\n )\n\n const res = await this.envdApi.api.POST('/files', {\n params: {\n query: {\n path,\n username: writeOpts?.user || defaultUsername,\n },\n },\n bodySerializer() {\n return blobs.reduce((fd, blob, i) => {\n // Important: RFC 7578, Section 4.2 requires that if a filename is provided,\n // the directory path information must not be used.\n // BUT in our case we need to use the directory path information with a custom\n // muktipart part name getter in envd.\n fd.append('file', blob, writeFiles[i].path)\n\n return fd\n }, new FormData())\n },\n body: {},\n headers: {\n 'Content-Type': 'multipart/form-data',\n 'Bun-Content-Type': 'temporary-fix', // https://github.com/oven-sh/bun/issues/14988\n },\n })\n\n const err = await handleEnvdApiError(res)\n if (err) {\n throw err\n }\n\n const files = res.data as EntryInfo[]\n if (!files) {\n throw new Error('Expected to receive information about written file')\n }\n\n return files.length === 1 && path ? files[0] : files\n }\n\n /**\n * List entries in a directory.\n *\n * @param path path to the directory.\n * @param opts connection options.\n *\n * @returns list of entries in the sandbox filesystem directory.\n */\n async list(path: string, opts?: FilesystemRequestOpts): Promise<EntryInfo[]> {\n try {\n const res = await this.rpc.listDir(\n { path },\n {\n headers: authenticationHeader(opts?.user),\n signal: this.connectionConfig.getSignal(opts?.requestTimeoutMs),\n }\n )\n\n const entries: EntryInfo[] = []\n\n for (const e of res.entries) {\n const type = mapFileType(e.type)\n\n if (type) {\n entries.push({\n name: e.name,\n type,\n path: e.path,\n })\n }\n }\n\n return entries\n } catch (err) {\n throw handleRpcError(err)\n }\n }\n\n /**\n * Create a new directory and all directories along the way if needed on the specified path.\n *\n * @param path path to a new directory. For example '/dirA/dirB' when creating 'dirB'.\n * @param opts connection options.\n *\n * @returns `true` if the directory was created, `false` if it already exists.\n */\n async makeDir(path: string, opts?: FilesystemRequestOpts): Promise<boolean> {\n try {\n await this.rpc.makeDir(\n { path },\n {\n headers: authenticationHeader(opts?.user),\n signal: this.connectionConfig.getSignal(opts?.requestTimeoutMs),\n }\n )\n\n return true\n } catch (err) {\n if (err instanceof ConnectError) {\n if (err.code === Code.AlreadyExists) {\n return false\n }\n }\n\n throw handleRpcError(err)\n }\n }\n\n /**\n * Rename a file or directory.\n *\n * @param oldPath path to the file or directory to rename.\n * @param newPath new path for the file or directory.\n * @param opts connection options.\n *\n * @returns information about renamed file or directory.\n */\n async rename(\n oldPath: string,\n newPath: string,\n opts?: FilesystemRequestOpts\n ): Promise<EntryInfo> {\n try {\n const res = await this.rpc.move(\n {\n source: oldPath,\n destination: newPath,\n },\n {\n headers: authenticationHeader(opts?.user),\n signal: this.connectionConfig.getSignal(opts?.requestTimeoutMs),\n }\n )\n\n const entry = res.entry\n if (!entry) {\n throw new Error('Expected to receive information about moved object')\n }\n\n return {\n name: entry.name,\n type: mapFileType(entry.type),\n path: entry.path,\n }\n } catch (err) {\n throw handleRpcError(err)\n }\n }\n\n /**\n * Remove a file or directory.\n *\n * @param path path to a file or directory.\n * @param opts connection options.\n */\n async remove(path: string, opts?: FilesystemRequestOpts): Promise<void> {\n try {\n await this.rpc.remove(\n { path },\n {\n headers: authenticationHeader(opts?.user),\n signal: this.connectionConfig.getSignal(opts?.requestTimeoutMs),\n }\n )\n } catch (err) {\n throw handleRpcError(err)\n }\n }\n\n /**\n * Check if a file or a directory exists.\n *\n * @param path path to a file or a directory\n * @param opts connection options.\n *\n * @returns `true` if the file or directory exists, `false` otherwise\n */\n async exists(path: string, opts?: FilesystemRequestOpts): Promise<boolean> {\n try {\n await this.rpc.stat(\n { path },\n {\n headers: authenticationHeader(opts?.user),\n signal: this.connectionConfig.getSignal(opts?.requestTimeoutMs),\n }\n )\n\n return true\n } catch (err) {\n if (err instanceof ConnectError) {\n if (err.code === Code.NotFound) {\n return false\n }\n }\n\n throw handleRpcError(err)\n }\n }\n\n /**\n * Start watching a directory for filesystem events.\n *\n * @param path path to directory to watch.\n * @param onEvent callback to call when an event in the directory occurs.\n * @param opts connection options.\n *\n * @returns `WatchHandle` object for stopping watching directory.\n */\n async watchDir(\n path: string,\n onEvent: (event: FilesystemEvent) => void | Promise<void>,\n opts?: WatchOpts & {\n timeout?: number\n onExit?: (err?: Error) => void | Promise<void>\n }\n ): Promise<WatchHandle> {\n if (\n opts?.recursive &&\n this.envdApi.version &&\n compareVersions(this.envdApi.version, ENVD_VERSION_RECURSIVE_WATCH) < 0\n ) {\n throw new TemplateError(\n 'You need to update the template to use recursive watching. ' +\n 'You can do this by running `e2b template build` in the directory with the template.'\n )\n }\n\n const requestTimeoutMs =\n opts?.requestTimeoutMs ?? this.connectionConfig.requestTimeoutMs\n\n const controller = new AbortController()\n\n const reqTimeout = requestTimeoutMs\n ? setTimeout(() => {\n controller.abort()\n }, requestTimeoutMs)\n : undefined\n\n const events = this.rpc.watchDir(\n {\n path,\n recursive: opts?.recursive ?? this.defaultWatchRecursive,\n },\n {\n headers: {\n ...authenticationHeader(opts?.user),\n [KEEPALIVE_PING_HEADER]: KEEPALIVE_PING_INTERVAL_SEC.toString(),\n },\n signal: controller.signal,\n timeoutMs: opts?.timeoutMs ?? this.defaultWatchTimeout,\n }\n )\n\n try {\n await handleWatchDirStartEvent(events)\n\n clearTimeout(reqTimeout)\n\n return new WatchHandle(\n () => controller.abort(),\n events,\n onEvent,\n opts?.onExit\n )\n } catch (err) {\n throw handleRpcError(err)\n }\n }\n}\n","import createClient, { FetchResponse } from 'openapi-fetch'\n\nimport type { components, paths } from './schema.gen'\nimport { ConnectionConfig } from '../connectionConfig'\nimport { createApiLogger } from '../logs'\nimport {\n SandboxError,\n InvalidArgumentError,\n NotFoundError,\n NotEnoughSpaceError,\n formatSandboxTimeoutError,\n AuthenticationError,\n} from '../errors'\nimport { StartResponse, ConnectResponse } from './process/process_pb'\nimport { Code, ConnectError } from '@connectrpc/connect'\nimport { WatchDirResponse } from './filesystem/filesystem_pb'\n\nexport async function handleEnvdApiError<A, B, C extends `${string}/${string}`>(\n res: FetchResponse<A, B, C>\n) {\n if (!res.error) {\n return\n }\n\n const message: string =\n typeof res.error == 'string'\n ? res.error\n : res.error?.message || (await res.response.text())\n\n switch (res.response.status) {\n case 400:\n return new InvalidArgumentError(message)\n case 401:\n return new AuthenticationError(message)\n case 404:\n return new NotFoundError(message)\n case 429:\n return new SandboxError(\n `${res.response.status}: ${message}: The requests are being rate limited.`\n )\n case 502:\n return formatSandboxTimeoutError(message)\n case 507:\n return new NotEnoughSpaceError(message)\n default:\n return new SandboxError(`${res.response.status}: ${message}`)\n }\n}\n\nexport async function handleProcessStartEvent(\n events: AsyncIterable<StartResponse | ConnectResponse>\n) {\n let startEvent: StartResponse | ConnectResponse\n\n try {\n startEvent = (await events[Symbol.asyncIterator]().next()).value\n } catch (err) {\n if (err instanceof ConnectError) {\n if (err.code === Code.Unavailable) {\n throw new NotFoundError('Sandbox is probably not running anymore')\n }\n }\n\n throw err\n }\n if (startEvent.event?.event.case !== 'start') {\n throw new Error('Expected start event')\n }\n\n return startEvent.event.event.value.pid\n}\n\nexport async function handleWatchDirStartEvent(\n events: AsyncIterable<WatchDirResponse>\n) {\n let startEvent: WatchDirResponse\n\n try {\n startEvent = (await events[Symbol.asyncIterator]().next()).value\n } catch (err) {\n if (err instanceof ConnectError) {\n if (err.code === Code.Unavailable) {\n throw new NotFoundError('Sandbox is probably not running anymore')\n }\n }\n\n throw err\n }\n if (startEvent.event?.case !== 'start') {\n throw new Error('Expected start event')\n }\n\n return startEvent.event.value\n}\n\nclass EnvdApiClient {\n readonly api: ReturnType<typeof createClient<paths>>\n readonly version: string | undefined\n\n constructor(\n config: Pick<ConnectionConfig, 'apiUrl' | 'logger'>,\n metadata: {\n version?: string\n }\n ) {\n this.api = createClient({\n baseUrl: config.apiUrl,\n // keepalive: true, // TODO: Return keepalive\n })\n this.version = metadata.version\n\n if (config.logger) {\n this.api.use(createApiLogger(config.logger))\n }\n }\n}\n\nexport type { components, paths }\nexport { EnvdApiClient }\n","import { Code, ConnectError } from '@connectrpc/connect'\nimport { runtime } from '../api/metadata'\nimport { defaultUsername } from '../connectionConfig'\n\nimport { SandboxError, TimeoutError, formatSandboxTimeoutError, InvalidArgumentError, NotFoundError, AuthenticationError } from '../errors'\n\n\nexport function handleRpcError(err: unknown): Error {\n if (err instanceof ConnectError) {\n switch (err.code) {\n case Code.InvalidArgument:\n return new InvalidArgumentError(err.message)\n case Code.Unauthenticated:\n return new AuthenticationError(err.message)\n case Code.NotFound:\n return new NotFoundError(err.message)\n case Code.Unavailable:\n return formatSandboxTimeoutError(err.message)\n case Code.Canceled:\n return new TimeoutError(\n `${err.message}: This error is likely due to exceeding 'requestTimeoutMs'. You can pass the request timeout value as an option when making the request.`\n )\n case Code.DeadlineExceeded:\n return new TimeoutError(\n `${err.message}: This error is likely due to exceeding 'timeoutMs' — the total time a long running request (like command execution or directory watch) can be active. It can be modified by passing 'timeoutMs' when making the request. Use '0' to disable the timeout.`\n )\n default:\n return new SandboxError(`${err.code}: ${err.message}`)\n }\n }\n\n return err as Error\n}\n\nfunction encode64(value: string): string {\n switch (runtime) {\n case 'deno':\n return btoa(value)\n case 'node':\n return Buffer.from(value).toString('base64')\n case 'bun':\n return Buffer.from(value).toString('base64')\n default:\n return btoa(value)\n }\n}\n\nexport function authenticationHeader(username?: string): Record<string, string> {\n const value = `${username || defaultUsername}:`\n\n const encoded = encode64(value)\n\n return { 'Authorization': `Basic ${encoded}` }\n}\n","// @generated by protoc-gen-es v2.2.2 with parameter \"target=ts\"\n// @generated from file filesystem/filesystem.proto (package filesystem, syntax proto3)\n/* eslint-disable */\n\nimport type { GenEnum, GenFile, GenMessage, GenService } from \"@bufbuild/protobuf/codegenv1\";\nimport { enumDesc, fileDesc, messageDesc, serviceDesc } from \"@bufbuild/protobuf/codegenv1\";\nimport type { Message } from \"@bufbuild/protobuf\";\n\n/**\n * Describes the file filesystem/filesystem.proto.\n */\nexport const file_filesystem_filesystem: GenFile = /*@__PURE__*/\n fileDesc(\"ChtmaWxlc3lzdGVtL2ZpbGVzeXN0ZW0ucHJvdG8SCmZpbGVzeXN0ZW0iMgoLTW92ZVJlcXVlc3QSDgoGc291cmNlGAEgASgJEhMKC2Rlc3RpbmF0aW9uGAIgASgJIjQKDE1vdmVSZXNwb25zZRIkCgVlbnRyeRgBIAEoCzIVLmZpbGVzeXN0ZW0uRW50cnlJbmZvIh4KDk1ha2VEaXJSZXF1ZXN0EgwKBHBhdGgYASABKAkiNwoPTWFrZURpclJlc3BvbnNlEiQKBWVudHJ5GAEgASgLMhUuZmlsZXN5c3RlbS5FbnRyeUluZm8iHQoNUmVtb3ZlUmVxdWVzdBIMCgRwYXRoGAEgASgJIhAKDlJlbW92ZVJlc3BvbnNlIhsKC1N0YXRSZXF1ZXN0EgwKBHBhdGgYASABKAkiNAoMU3RhdFJlc3BvbnNlEiQKBWVudHJ5GAEgASgLMhUuZmlsZXN5c3RlbS5FbnRyeUluZm8iSwoJRW50cnlJbmZvEgwKBG5hbWUYASABKAkSIgoEdHlwZRgCIAEoDjIULmZpbGVzeXN0ZW0uRmlsZVR5cGUSDAoEcGF0aBgDIAEoCSIeCg5MaXN0RGlyUmVxdWVzdBIMCgRwYXRoGAEgASgJIjkKD0xpc3REaXJSZXNwb25zZRImCgdlbnRyaWVzGAEgAygLMhUuZmlsZXN5c3RlbS5FbnRyeUluZm8iMgoPV2F0Y2hEaXJSZXF1ZXN0EgwKBHBhdGgYASABKAkSEQoJcmVjdXJzaXZlGAIgASgIIkQKD0ZpbGVzeXN0ZW1FdmVudBIMCgRuYW1lGAEgASgJEiMKBHR5cGUYAiABKA4yFS5maWxlc3lzdGVtLkV2ZW50VHlwZSLgAQoQV2F0Y2hEaXJSZXNwb25zZRI4CgVzdGFydBgBIAEoCzInLmZpbGVzeXN0ZW0uV2F0Y2hEaXJSZXNwb25zZS5TdGFydEV2ZW50SAASMQoKZmlsZXN5c3RlbRgCIAEoCzIbLmZpbGVzeXN0ZW0uRmlsZXN5c3RlbUV2ZW50SAASOwoJa2VlcGFsaXZlGAMgASgLMiYuZmlsZXN5c3RlbS5XYXRjaERpclJlc3BvbnNlLktlZXBBbGl2ZUgAGgwKClN0YXJ0RXZlbnQaCwoJS2VlcEFsaXZlQgcKBWV2ZW50IjcKFENyZWF0ZVdhdGNoZXJSZXF1ZXN0EgwKBHBhdGgYASABKAkSEQoJcmVjdXJzaXZlGAIgASgIIisKFUNyZWF0ZVdhdGNoZXJSZXNwb25zZRISCgp3YXRjaGVyX2lkGAEgASgJIi0KF0dldFdhdGNoZXJFdmVudHNSZXF1ZXN0EhIKCndhdGNoZXJfaWQYASABKAkiRwoYR2V0V2F0Y2hlckV2ZW50c1Jlc3BvbnNlEisKBmV2ZW50cxgBIAMoCzIbLmZpbGVzeXN0ZW0uRmlsZXN5c3RlbUV2ZW50IioKFFJlbW92ZVdhdGNoZXJSZXF1ZXN0EhIKCndhdGNoZXJfaWQYASABKAkiFwoVUmVtb3ZlV2F0Y2hlclJlc3BvbnNlKlIKCEZpbGVUeXBlEhkKFUZJTEVfVFlQRV9VTlNQRUNJRklFRBAAEhIKDkZJTEVfVFlQRV9GSUxFEAESFwoTRklMRV9UWVBFX0RJUkVDVE9SWRACKpgBCglFdmVudFR5cGUSGgoWRVZFTlRfVFlQRV9VTlNQRUNJRklFRBAAEhUKEUVWRU5UX1RZUEVfQ1JFQVRFEAESFAoQRVZFTlRfVFlQRV9XUklURRACEhUKEUVWRU5UX1RZUEVfUkVNT1ZFEAMSFQoRRVZFTlRfVFlQRV9SRU5BTUUQBBIUChBFVkVOVF9UWVBFX0NITU9EEAUynwUKCkZpbGVzeXN0ZW0SOQoEU3RhdBIXLmZpbGVzeXN0ZW0uU3RhdFJlcXVlc3QaGC5maWxlc3lzdGVtLlN0YXRSZXNwb25zZRJCCgdNYWtlRGlyEhouZmlsZXN5c3RlbS5NYWtlRGlyUmVxdWVzdBobLmZpbGVzeXN0ZW0uTWFrZURpclJlc3BvbnNlEjkKBE1vdmUSFy5maWxlc3lzdGVtLk1vdmVSZXF1ZXN0GhguZmlsZXN5c3RlbS5Nb3ZlUmVzcG9uc2USQgoHTGlzdERpchIaLmZpbGVzeXN0ZW0uTGlzdERpclJlcXVlc3QaGy5maWxlc3lzdGVtLkxpc3REaXJSZXNwb25zZRI/CgZSZW1vdmUSGS5maWxlc3lzdGVtLlJlbW92ZVJlcXVlc3QaGi5maWxlc3lzdGVtLlJlbW92ZVJlc3BvbnNlEkcKCFdhdGNoRGlyEhsuZmlsZXN5c3RlbS5XYXRjaERpclJlcXVlc3QaHC5maWxlc3lzdGVtLldhdGNoRGlyUmVzcG9uc2UwARJUCg1DcmVhdGVXYXRjaGVyEiAuZmlsZXN5c3RlbS5DcmVhdGVXYXRjaGVyUmVxdWVzdBohLmZpbGVzeXN0ZW0uQ3JlYXRlV2F0Y2hlclJlc3BvbnNlEl0KEEdldFdhdGNoZXJFdmVudHMSIy5maWxlc3lzdGVtLkdldFdhdGNoZXJFdmVudHNSZXF1ZXN0GiQuZmlsZXN5c3RlbS5HZXRXYXRjaGVyRXZlbnRzUmVzcG9uc2USVAoNUmVtb3ZlV2F0Y2hlchIgLmZpbGVzeXN0ZW0uUmVtb3ZlV2F0Y2hlclJlcXVlc3QaIS5maWxlc3lzdGVtLlJlbW92ZVdhdGNoZXJSZXNwb25zZUJpCg5jb20uZmlsZXN5c3RlbUIPRmlsZXN5c3RlbVByb3RvUAGiAgNGWFiqAgpGaWxlc3lzdGVtygIKRmlsZXN5c3RlbeICFkZpbGVzeXN0ZW1cR1BCTWV0YWRhdGHqAgpGaWxlc3lzdGVtYgZwcm90bzM\");\n\n/**\n * @generated from message filesystem.MoveRequest\n */\nexport type MoveRequest = Message<\"filesystem.MoveRequest\"> & {\n /**\n * @generated from field: string source = 1;\n */\n source: string;\n\n /**\n * @generated from field: string destination = 2;\n */\n destination: string;\n};\n\n/**\n * Describes the message filesystem.MoveRequest.\n * Use `create(MoveRequestSchema)` to create a new message.\n */\nexport const MoveRequestSchema: GenMessage<MoveRequest> = /*@__PURE__*/\n messageDesc(file_filesystem_filesystem, 0);\n\n/**\n * @generated from message filesystem.MoveResponse\n */\nexport type MoveResponse = Message<\"filesystem.MoveResponse\"> & {\n /**\n * @generated from field: filesystem.EntryInfo entry = 1;\n */\n entry?: EntryInfo;\n};\n\n/**\n * Describes the message filesystem.MoveResponse.\n * Use `create(MoveResponseSchema)` to create a new message.\n */\nexport const MoveResponseSchema: GenMessage<MoveResponse> = /*@__PURE__*/\n messageDesc(file_filesystem_filesystem, 1);\n\n/**\n * @generated from message filesystem.MakeDirRequest\n */\nexport type MakeDirRequest = Message<\"filesystem.MakeDirRequest\"> & {\n /**\n * @generated from field: string path = 1;\n */\n path: string;\n};\n\n/**\n * Describes the message filesystem.MakeDirRequest.\n * Use `create(MakeDirRequestSchema)` to create a new message.\n */\nexport const MakeDirRequestSchema: GenMessage<MakeDirRequest> = /*@__PURE__*/\n messageDesc(file_filesystem_filesystem, 2);\n\n/**\n * @generated from message filesystem.MakeDirResponse\n */\nexport type MakeDirResponse = Message<\"filesystem.MakeDirResponse\"> & {\n /**\n * @generated from field: filesystem.EntryInfo entry = 1;\n */\n entry?: EntryInfo;\n};\n\n/**\n * Describes the message filesystem.MakeDirResponse.\n * Use `create(MakeDirResponseSchema)` to create a new message.\n */\nexport const MakeDirResponseSchema: GenMessage<MakeDirResponse> = /*@__PURE__*/\n messageDesc(file_filesystem_filesystem, 3);\n\n/**\n * @generated from message filesystem.RemoveRequest\n */\nexport type RemoveRequest = Message<\"filesystem.RemoveRequest\"> & {\n /**\n * @generated from field: string path = 1;\n */\n path: string;\n};\n\n/**\n * Describes the message filesystem.RemoveRequest.\n * Use `create(RemoveRequestSchema)` to create a new message.\n */\nexport const RemoveRequestSchema: GenMessage<RemoveRequest> = /*@__PURE__*/\n messageDesc(file_filesystem_filesystem, 4);\n\n/**\n * @generated from message filesystem.RemoveResponse\n */\nexport type RemoveResponse = Message<\"filesystem.RemoveResponse\"> & {\n};\n\n/**\n * Describes the message filesystem.RemoveResponse.\n * Use `create(RemoveResponseSchema)` to create a new message.\n */\nexport const RemoveResponseSchema: GenMessage<RemoveResponse> = /*@__PURE__*/\n messageDesc(file_filesystem_filesystem, 5);\n\n/**\n * @generated from message filesystem.StatRequest\n */\nexport type StatRequest = Message<\"filesystem.StatRequest\"> & {\n /**\n * @generated from field: string path = 1;\n */\n path: string;\n};\n\n/**\n * Describes the message filesystem.StatRequest.\n * Use `create(StatRequestSchema)` to create a new message.\n */\nexport const StatRequestSchema: GenMessage<StatRequest> = /*@__PURE__*/\n messageDesc(file_filesystem_filesystem, 6);\n\n/**\n * @generated from message filesystem.StatResponse\n */\nexport type StatResponse = Message<\"filesystem.StatResponse\"> & {\n /**\n * @generated from field: filesystem.EntryInfo entry = 1;\n */\n entry?: EntryInfo;\n};\n\n/**\n * Describes the message filesystem.StatResponse.\n * Use `create(StatResponseSchema)` to create a new message.\n */\nexport const StatResponseSchema: GenMessage<StatResponse> = /*@__PURE__*/\n messageDesc(file_filesystem_filesystem, 7);\n\n/**\n * @generated from message filesystem.EntryInfo\n */\nexport type EntryInfo = Message<\"filesystem.EntryInfo\"> & {\n /**\n * @generated from field: string name = 1;\n */\n name: string;\n\n /**\n * @generated from field: filesystem.FileType type = 2;\n */\n type: FileType;\n\n /**\n * @generated from field: string path = 3;\n */\n path: string;\n};\n\n/**\n * Describes the message filesystem.EntryInfo.\n * Use `create(EntryInfoSchema)` to create a new message.\n */\nexport const EntryInfoSchema: GenMessage<EntryInfo> = /*@__PURE__*/\n messageDesc(file_filesystem_filesystem, 8);\n\n/**\n * @generated from message filesystem.ListDirRequest\n */\nexport type ListDirRequest = Message<\"filesystem.ListDirRequest\"> & {\n /**\n * @generated from field: string path = 1;\n */\n path: string;\n};\n\n/**\n * Describes the message filesystem.ListDirRequest.\n * Use `create(ListDirRequestSchema)` to create a new message.\n */\nexport const ListDirRequestSchema: GenMessage<ListDirRequest> = /*@__PURE__*/\n messageDesc(file_filesystem_filesystem, 9);\n\n/**\n * @generated from message filesystem.ListDirResponse\n */\nexport type ListDirResponse = Message<\"filesystem.ListDirResponse\"> & {\n /**\n * @generated from field: repeated filesystem.EntryInfo entries = 1;\n */\n entries: EntryInfo[];\n};\n\n/**\n * Describes the message filesystem.ListDirResponse.\n * Use `create(ListDirResponseSchema)` to create a new message.\n */\nexport const ListDirResponseSchema: GenMessage<ListDirResponse> = /*@__PURE__*/\n messageDesc(file_filesystem_filesystem, 10);\n\n/**\n * @generated from message filesystem.WatchDirRequest\n */\nexport type WatchDirRequest = Message<\"filesystem.WatchDirRequest\"> & {\n /**\n * @generated from field: string path = 1;\n */\n path: string;\n\n /**\n * @generated from field: bool recursive = 2;\n */\n recursive: boolean;\n};\n\n/**\n * Describes the message filesystem.WatchDirRequest.\n * Use `create(WatchDirRequestSchema)` to create a new message.\n */\nexport const WatchDirRequestSchema: GenMessage<WatchDirRequest> = /*@__PURE__*/\n messageDesc(file_filesystem_filesystem, 11);\n\n/**\n * @generated from message filesystem.FilesystemEvent\n */\nexport type FilesystemEvent = Message<\"filesystem.FilesystemEvent\"> & {\n /**\n * @generated from field: string name = 1;\n */\n name: string;\n\n /**\n * @generated from field: filesystem.EventType type = 2;\n */\n type: EventType;\n};\n\n/**\n * Describes the message filesystem.FilesystemEvent.\n * Use `create(FilesystemEventSchema)` to create a new message.\n */\nexport const FilesystemEventSchema: GenMessage<FilesystemEvent> = /*@__PURE__*/\n messageDesc(file_filesystem_filesystem, 12);\n\n/**\n * @generated from message filesystem.WatchDirResponse\n */\nexport type WatchDirResponse = Message<\"filesystem.WatchDirResponse\"> & {\n /**\n * @generated from oneof filesystem.WatchDirResponse.event\n */\n event: {\n /**\n * @generated from field: filesystem.WatchDirResponse.StartEvent start = 1;\n */\n value: WatchDirResponse_StartEvent;\n case: \"start\";\n } | {\n /**\n * @generated from field: filesystem.FilesystemEvent filesystem = 2;\n */\n value: FilesystemEvent;\n case: \"filesystem\";\n } | {\n /**\n * @generated from field: filesystem.WatchDirResponse.KeepAlive keepalive = 3;\n */\n value: WatchDirResponse_KeepAlive;\n case: \"keepalive\";\n } | { case: undefined; value?: undefined };\n};\n\n/**\n * Describes the message filesystem.WatchDirResponse.\n * Use `create(WatchDirResponseSchema)` to create a new message.\n */\nexport const WatchDirResponseSchema: GenMessage<WatchDirResponse> = /*@__PURE__*/\n messageDesc(file_filesystem_filesystem, 13);\n\n/**\n * @generated from message filesystem.WatchDirResponse.StartEvent\n */\nexport type WatchDirResponse_StartEvent = Message<\"filesystem.WatchDirResponse.StartEvent\"> & {\n};\n\n/**\n * Describes the message filesystem.WatchDirResponse.StartEvent.\n * Use `create(WatchDirResponse_StartEventSchema)` to create a new message.\n */\nexport const WatchDirResponse_StartEventSchema: GenMessage<WatchDirResponse_StartEvent> = /*@__PURE__*/\n messageDesc(file_filesystem_filesystem, 13, 0);\n\n/**\n * @generated from message filesystem.WatchDirResponse.KeepAlive\n */\nexport type WatchDirResponse_KeepAlive = Message<\"filesystem.WatchDirResponse.KeepAlive\"> & {\n};\n\n/**\n * Describes the message filesystem.WatchDirResponse.KeepAlive.\n * Use `create(WatchDirResponse_KeepAliveSchema)` to create a new message.\n */\nexport const WatchDirResponse_KeepAliveSchema: GenMessage<WatchDirResponse_KeepAlive> = /*@__PURE__*/\n messageDesc(file_filesystem_filesystem, 13, 1);\n\n/**\n * @generated from message filesystem.CreateWatcherRequest\n */\nexport type CreateWatcherRequest = Message<\"filesystem.CreateWatcherRequest\"> & {\n /**\n * @generated from field: string path = 1;\n */\n path: string;\n\n /**\n * @generated from field: bool recursive = 2;\n */\n recursive: boolean;\n};\n\n/**\n * Describes the message filesystem.CreateWatcherRequest.\n * Use `create(CreateWatcherRequestSchema)` to create a new message.\n */\nexport const CreateWatcherRequestSchema: GenMessage<CreateWatcherRequest> = /*@__PURE__*/\n messageDesc(file_filesystem_filesystem, 14);\n\n/**\n * @generated from message filesystem.CreateWatcherResponse\n */\nexport type CreateWatcherResponse = Message<\"filesystem.CreateWatcherResponse\"> & {\n /**\n * @generated from field: string watcher_id = 1;\n */\n watcherId: string;\n};\n\n/**\n * Describes the message filesystem.CreateWatcherResponse.\n * Use `create(CreateWatcherResponseSchema)` to create a new message.\n */\nexport const CreateWatcherResponseSchema: GenMessage<CreateWatcherResponse> = /*@__PURE__*/\n messageDesc(file_filesystem_filesystem, 15);\n\n/**\n * @generated from message filesystem.GetWatcherEventsRequest\n */\nexport type GetWatcherEventsRequest = Message<\"filesystem.GetWatcherEventsRequest\"> & {\n /**\n * @generated from field: string watcher_id = 1;\n */\n watcherId: string;\n};\n\n/**\n * Describes the message filesystem.GetWatcherEventsRequest.\n * Use `create(GetWatcherEventsRequestSchema)` to create a new message.\n */\nexport const GetWatcherEventsRequestSchema: GenMessage<GetWatcherEventsRequest> = /*@__PURE__*/\n messageDesc(file_filesystem_filesystem, 16);\n\n/**\n * @generated from message filesystem.GetWatcherEventsResponse\n */\nexport type GetWatcherEventsResponse = Message<\"filesystem.GetWatcherEventsResponse\"> & {\n /**\n * @generated from field: repeated filesystem.FilesystemEvent events = 1;\n */\n events: FilesystemEvent[];\n};\n\n/**\n * Describes the message filesystem.GetWatcherEventsResponse.\n * Use `create(GetWatcherEventsResponseSchema)` to create a new message.\n */\nexport const GetWatcherEventsResponseSchema: GenMessage<GetWatcherEventsResponse> = /*@__PURE__*/\n messageDesc(file_filesystem_filesystem, 17);\n\n/**\n * @generated from message filesystem.RemoveWatcherRequest\n */\nexport type RemoveWatcherRequest = Message<\"filesystem.RemoveWatcherRequest\"> & {\n /**\n * @generated from field: string watcher_id = 1;\n */\n watcherId: string;\n};\n\n/**\n * Describes the messag