/** * This file is responsible for interacting with bit through a long-running background process "bit-server" rather than directly. * Why not directly? * 1. startup cost. currently it takes around 1 second to bootstrap bit. * 2. an experimental package-manager saves node_modules in-memory. if a client starts a new process, it won't have the node_modules in-memory. * * In this file, there are three ways to achieve this. It's outlined in the order it was evolved. * The big challenge here is to show the output correctly to the client even though the server is running in a different process. * * 1. process.env.BIT_CLI_SERVER === 'true' * This method uses SSE - Server Send Events. The server sends events to the client with the output to print. The client listens to * these events and prints them. It's cumbersome. For this, the logger was changed and every time the logger needs to print to the console, * it was using this SSE to send events. Same with the loader. * Cons: Other output, such as pnpm, needs an extra effort to print - for pnpm, the "process" object was passed to pnpm * and its stdout was modified to use the SSE. * However, other tools that print directly to the console, such as Jest, won't work. * * 2. process.env.BIT_CLI_SERVER_TTY === 'true' * Because the terminal - tty is a fd (file descriptor) on mac/linux, it can be passed to the server. The server can write to this * fd and it will be printed to the client terminal. On the server, the process.stdout.write was monkey-patched to * write to the tty. (see cli-raw.route.ts file). * It solves the problem of Jest and other tools that print directly to the console. * Cons: * A. It doesn't work on Windows. Windows doesn't treat tty as a file descriptor. * B. We need two ways communication. Commands such as "bit update", display a prompt with option to select using the arrow keys. * This is not possible with the tty approach. Also, if the client hits Ctrl+C, the server won't know about it and it * won't kill the process. * * 3. process.env.BIT_CLI_SERVER_PTY === 'true' * This is the most advanced approach. It spawns a pty (pseudo terminal) process to communicate between the client and the server. * The client connects to the server using a socket. The server writes to the socket and the client reads from it. * The client also writes to the socket and the server reads from it. See server-forever.ts to understand better. * In order to pass terminal sequences, such as arrow keys or Ctrl+C, the stdin of the client is set to raw mode. * In theory, this approach could work by spawning a normal process, not pty, however, then, the stdin/stdout are non-tty, * and as a result, loaders such as Ora and chalk won't work. * With this new approach, we also support terminating and reloading the server. A new command is added * "bit server-forever", which spawns the pty-process. If the client hits Ctrl+C, this server-forever process will kill * the pty-process and re-load it. * Keep in mind, that to send the command and get the results, we still using http. The usage of the pty is only for * the input/output during the command. * I was trying to avoid the http, and use only the pty, by implementing readline to get the command from the socket, * but then I wasn't able to return the prompt to the user easily. So, I decided to keep the http for the request/response part. */ type CommandResult = { data: any; exitCode: number; }; export declare class ServerCommander { execute(): Promise; private shouldUseTTYPath; runCommandWithHttpServer(): Promise; private connectToSocket; /** * Initialize the server-sent events (SSE) connection to the server. * This is used to print the logs and show the loader during the command. * Without this, it only shows the response from http server, but not the "logger.console" or "logger.setStatusLine" texts. * * I wasn't able to find a better way to do it. The challenge here is that the http server is running in a different * process, which is not connected to the current process in any way. (unlike the IDE which is its child process and * can access its stdout). * One of the attempts I made is sending the "tty" path to the server and let the server console log to that path, but * it didn't work well. It was printed only after the response came back from the server. */ private initSSE; private printPortAndExit; private deletePortAndExit; private printSocketPortAndExit; /** * Print the per-server bearer token written by bit-server at startup, used * by clients (e.g. the bit-vscode extension) to authenticate to the local * HTTP API. Prints empty if no token file exists (older bit-server with no * auth requirement). */ private printServerTokenAndExit; private getServerTokenFilePath; /** * Read the server's bearer token, returning undefined if no token file * exists (older bit-server with no auth requirement) or scope can't be * resolved. Used by HTTP/SSE callers in this file to authenticate to the * running bit-server. * * Only ENOENT and ScopeNotFound are swallowed — other read errors * (EACCES, EPERM, corrupted file, …) are surfaced so the user sees the * real cause instead of a misleading 401/upgrade message from the server. */ private getServerTokenIfExists; private getExistingUsedPort; private isPortInUseForCurrentDir; private getExistingPort; private deleteServerPortFile; private getServerPortFilePath; } export declare function shouldUseBitServer(): boolean; export {};