4a80bcb42c
* Add trace log level * Use active eval to implement spdlog * Split server/client active eval interfaces Since all properties are *not* valid on both sides * +200% fire resistance * Implement exec using active evaluations * Fully implement child process streams * Watch impl, move child_process back to explicitly adding events Automatically forwarding all events might be the right move, but wanna think/discuss it a bit more because it didn't come out very cleanly. * Would you like some args with that callback? * Implement the rest of child_process using active evals * Rampant memory leaks Emit "kill" to active evaluations when client disconnects in order to kill processes. Most likely won't be the final solution. * Resolve some minor issues with output panel * Implement node-pty with active evals * Provide clearTimeout to vm sandbox * Implement socket with active evals * Extract some callback logic Also remove some eval interfaces, need to re-think those. * Implement net.Server and remainder of net.Socket using active evals * Implement dispose for active evaluations * Use trace for express requests * Handle sending buffers through evaluation events * Make event logging a bit more clear * Fix some errors due to us not actually instantiating until connect/listen * is this a commit message? * We can just create the evaluator in the ctor Not sure what I was thinking. * memory leak for you, memory leak for everyone * it's a ternary now * Don't dispose automatically on close or error The code may or may not be disposable at that point. * Handle parsing buffers on the client side as well * Remove unused protobuf * Remove TypedValue * Remove unused forkProvider and test * Improve dispose pattern for active evals * Socket calls close after error; no need to bind both * Improve comment * Comment is no longer wishy washy due to explicit boolean * Simplify check for sendHandle and options * Replace _require with __non_webpack_require__ Webpack will then replace this with `require` which we then provide to the vm sandbox. * Provide path.parse * Prevent original-fs from loading * Start with a pid of -1 vscode immediately checks the PID to see if the debug process launch correctly, but of course we don't get the pid synchronously. * Pass arguments to bootstrap-fork * Fully implement streams Was causing errors because internally the stream would set this.writing to true and it would never become false, so subsequent messages would never send. * Fix serializing errors and streams emitting errors multiple times * Was emitting close to data * Fix missing path for spawned processes * Move evaluation onDispose call Now it's accurate and runs when the active evaluation has actually disposed. * Fix promisifying fs.exists * Fix some active eval callback issues * Patch existsSync in debug adapter
104 lines
2.8 KiB
TypeScript
104 lines
2.8 KiB
TypeScript
import { client } from "@coder/ide/src/fill/client";
|
|
import { EventEmitter } from "events";
|
|
import * as nodePty from "node-pty";
|
|
import { ActiveEval } from "@coder/protocol";
|
|
|
|
// Use this to prevent Webpack from hijacking require.
|
|
declare var __non_webpack_require__: typeof require;
|
|
|
|
/**
|
|
* Implementation of nodePty for the browser.
|
|
*/
|
|
class Pty implements nodePty.IPty {
|
|
private readonly emitter = new EventEmitter();
|
|
private readonly ae: ActiveEval;
|
|
private _pid = -1;
|
|
private _process = "";
|
|
|
|
public constructor(file: string, args: string[] | string, options: nodePty.IPtyForkOptions) {
|
|
this.ae = client.run((ae, file, args, options) => {
|
|
const nodePty = __non_webpack_require__("node-pty") as typeof import("node-pty");
|
|
const { preserveEnv } = __non_webpack_require__("@coder/ide/src/fill/evaluation") as typeof import("@coder/ide/src/fill/evaluation");
|
|
|
|
preserveEnv(options);
|
|
|
|
const ptyProc = nodePty.spawn(file, args, options);
|
|
|
|
let process = ptyProc.process;
|
|
ae.emit("process", process);
|
|
ae.emit("pid", ptyProc.pid);
|
|
|
|
const timer = setInterval(() => {
|
|
if (ptyProc.process !== process) {
|
|
process = ptyProc.process;
|
|
ae.emit("process", process);
|
|
}
|
|
}, 200);
|
|
|
|
ptyProc.on("exit", (code, signal) => {
|
|
clearTimeout(timer);
|
|
ae.emit("exit", code, signal);
|
|
});
|
|
|
|
ptyProc.on("data", (data) => ae.emit("data", data));
|
|
|
|
ae.on("resize", (cols, rows) => ptyProc.resize(cols, rows));
|
|
ae.on("write", (data) => ptyProc.write(data));
|
|
ae.on("kill", (signal) => ptyProc.kill(signal));
|
|
|
|
return {
|
|
onDidDispose: (cb): void => ptyProc.on("exit", cb),
|
|
dispose: (): void => {
|
|
ptyProc.kill();
|
|
setTimeout(() => ptyProc.kill("SIGKILL"), 5000); // Double tap.
|
|
},
|
|
};
|
|
}, file, Array.isArray(args) ? args : [args], {
|
|
...options,
|
|
tty: {
|
|
columns: options.cols || 100,
|
|
rows: options.rows || 100,
|
|
},
|
|
}, file, args, options);
|
|
|
|
this.ae.on("pid", (pid) => this._pid = pid);
|
|
this.ae.on("process", (process) => this._process = process);
|
|
|
|
this.ae.on("exit", (code, signal) => this.emitter.emit("exit", code, signal));
|
|
this.ae.on("data", (data) => this.emitter.emit("data", data));
|
|
}
|
|
|
|
public get pid(): number {
|
|
return this._pid;
|
|
}
|
|
|
|
public get process(): string {
|
|
return this._process;
|
|
}
|
|
|
|
// tslint:disable-next-line no-any
|
|
public on(event: string, listener: (...args: any[]) => void): void {
|
|
this.emitter.on(event, listener);
|
|
}
|
|
|
|
public resize(columns: number, rows: number): void {
|
|
this.ae.emit("resize", columns, rows);
|
|
}
|
|
|
|
public write(data: string): void {
|
|
this.ae.emit("write", data);
|
|
}
|
|
|
|
public kill(signal?: string): void {
|
|
this.ae.emit("kill", signal);
|
|
}
|
|
}
|
|
|
|
const ptyType: typeof nodePty = {
|
|
spawn: (file: string, args: string[] | string, options: nodePty.IPtyForkOptions): nodePty.IPty => {
|
|
return new Pty(file, args, options);
|
|
},
|
|
};
|
|
|
|
module.exports = ptyType;
|