Add commands (#2)
* Add remote command execution * Add tests for environment variables and resize * Fix tab spacing, add newlines * Remove extra newline * Add fork
This commit is contained in:
parent
a06854a937
commit
d61873e8da
@ -2,11 +2,14 @@
|
|||||||
"name": "server",
|
"name": "server",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"express": "^4.16.4",
|
"express": "^4.16.4",
|
||||||
|
"node-pty": "^0.8.0",
|
||||||
"ws": "^6.1.2"
|
"ws": "^6.1.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/express": "^4.16.0",
|
"@types/express": "^4.16.0",
|
||||||
|
"@types/text-encoding": "^0.0.35",
|
||||||
"@types/ws": "^6.0.1",
|
"@types/ws": "^6.0.1",
|
||||||
|
"text-encoding": "^0.7.0",
|
||||||
"ts-protoc-gen": "^0.8.0"
|
"ts-protoc-gen": "^0.8.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
0
packages/server/scripts/generate_proto.sh
Normal file → Executable file
0
packages/server/scripts/generate_proto.sh
Normal file → Executable file
@ -1,15 +1,17 @@
|
|||||||
import { ReadWriteConnection } from "../common/connection";
|
import { ReadWriteConnection } from "../common/connection";
|
||||||
import { NewEvalMessage, ServerMessage, EvalDoneMessage, EvalFailedMessage, TypedValue, ClientMessage } from "../proto";
|
import { NewEvalMessage, ServerMessage, EvalDoneMessage, EvalFailedMessage, TypedValue, ClientMessage, NewSessionMessage, TTYDimensions, SessionOutputMessage, CloseSessionInputMessage } from "../proto";
|
||||||
import { Emitter } from "@coder/events";
|
import { Emitter } from "@coder/events";
|
||||||
import { logger, field } from "@coder/logger";
|
import { logger, field } from "@coder/logger";
|
||||||
|
import { ChildProcess, SpawnOptions, ServerProcess } from "./command";
|
||||||
|
|
||||||
export class Client {
|
export class Client {
|
||||||
|
|
||||||
private evalId: number = 0;
|
private evalId: number = 0;
|
||||||
private evalDoneEmitter: Emitter<EvalDoneMessage> = new Emitter();
|
private evalDoneEmitter: Emitter<EvalDoneMessage> = new Emitter();
|
||||||
private evalFailedEmitter: Emitter<EvalFailedMessage> = new Emitter();
|
private evalFailedEmitter: Emitter<EvalFailedMessage> = new Emitter();
|
||||||
|
|
||||||
|
private sessionId: number = 0;
|
||||||
|
private sessions: Map<number, ServerProcess> = new Map();
|
||||||
|
|
||||||
public constructor(
|
public constructor(
|
||||||
private readonly connection: ReadWriteConnection,
|
private readonly connection: ReadWriteConnection,
|
||||||
) {
|
) {
|
||||||
@ -86,20 +88,108 @@ export class Client {
|
|||||||
if (failedMsg.getId() === id) {
|
if (failedMsg.getId() === id) {
|
||||||
d1.dispose();
|
d1.dispose();
|
||||||
d2.dispose();
|
d2.dispose();
|
||||||
|
|
||||||
rej(failedMsg.getMessage());
|
rej(failedMsg.getMessage());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return prom;
|
return prom;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Spawns a process from a command. _Somewhat_ reflects the "child_process" API.
|
||||||
|
* @param command
|
||||||
|
* @param args Arguments
|
||||||
|
* @param options Options to execute for the command
|
||||||
|
*/
|
||||||
|
public spawn(command: string, args: string[] = [], options?: SpawnOptions): ChildProcess {
|
||||||
|
return this.doSpawn(command, args, options, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fork a module.
|
||||||
|
* @param modulePath Path of the module
|
||||||
|
* @param args Args to add for the module
|
||||||
|
* @param options Options to execute
|
||||||
|
*/
|
||||||
|
public fork(modulePath: string, args: string[] = [], options?: SpawnOptions): ChildProcess {
|
||||||
|
return this.doSpawn(modulePath, args, options, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private doSpawn(command: string, args: string[] = [], options?: SpawnOptions, isFork: boolean = false): ChildProcess {
|
||||||
|
const id = this.sessionId++;
|
||||||
|
const newSess = new NewSessionMessage();
|
||||||
|
newSess.setId(id);
|
||||||
|
newSess.setCommand(command);
|
||||||
|
newSess.setArgsList(args);
|
||||||
|
newSess.setIsFork(isFork);
|
||||||
|
if (options) {
|
||||||
|
if (options.cwd) {
|
||||||
|
newSess.setCwd(options.cwd);
|
||||||
|
}
|
||||||
|
if (options.env) {
|
||||||
|
Object.keys(options.env).forEach((envKey) => {
|
||||||
|
newSess.getEnvMap().set(envKey, options.env![envKey]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (options.tty) {
|
||||||
|
const tty = new TTYDimensions();
|
||||||
|
tty.setHeight(options.tty.rows);
|
||||||
|
tty.setWidth(options.tty.columns);
|
||||||
|
newSess.setTtyDimensions(tty);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const clientMsg = new ClientMessage();
|
||||||
|
clientMsg.setNewSession(newSess);
|
||||||
|
this.connection.send(clientMsg.serializeBinary());
|
||||||
|
|
||||||
|
const serverProc = new ServerProcess(this.connection, id, options ? options.tty !== undefined : false);
|
||||||
|
serverProc.stdin.on("close", () => {
|
||||||
|
console.log("stdin closed");
|
||||||
|
const c = new CloseSessionInputMessage();
|
||||||
|
c.setId(id);
|
||||||
|
const cm = new ClientMessage();
|
||||||
|
cm.setCloseSessionInput(c);
|
||||||
|
this.connection.send(cm.serializeBinary());
|
||||||
|
});
|
||||||
|
this.sessions.set(id, serverProc);
|
||||||
|
return serverProc;
|
||||||
|
}
|
||||||
|
|
||||||
private handleMessage(message: ServerMessage): void {
|
private handleMessage(message: ServerMessage): void {
|
||||||
if (message.hasEvalDone()) {
|
if (message.hasEvalDone()) {
|
||||||
this.evalDoneEmitter.emit(message.getEvalDone()!);
|
this.evalDoneEmitter.emit(message.getEvalDone()!);
|
||||||
} else if (message.hasEvalFailed()) {
|
} else if (message.hasEvalFailed()) {
|
||||||
this.evalFailedEmitter.emit(message.getEvalFailed()!);
|
this.evalFailedEmitter.emit(message.getEvalFailed()!);
|
||||||
|
} else if (message.hasNewSessionFailure()) {
|
||||||
|
const s = this.sessions.get(message.getNewSessionFailure()!.getId());
|
||||||
|
if (!s) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
s.emit("error", new Error(message.getNewSessionFailure()!.getMessage()));
|
||||||
|
this.sessions.delete(message.getNewSessionFailure()!.getId());
|
||||||
|
} else if (message.hasSessionDone()) {
|
||||||
|
const s = this.sessions.get(message.getSessionDone()!.getId());
|
||||||
|
if (!s) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
s.emit("exit", message.getSessionDone()!.getExitStatus());
|
||||||
|
this.sessions.delete(message.getSessionDone()!.getId());
|
||||||
|
} else if (message.hasSessionOutput()) {
|
||||||
|
const output = message.getSessionOutput()!;
|
||||||
|
const s = this.sessions.get(output.getId());
|
||||||
|
if (!s) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const data = new TextDecoder().decode(output.getData_asU8());
|
||||||
|
const stream = output.getFd() === SessionOutputMessage.FD.STDOUT ? s.stdout : s.stderr;
|
||||||
|
stream.emit("data", data);
|
||||||
|
} else if (message.hasIdentifySession()) {
|
||||||
|
const s = this.sessions.get(message.getIdentifySession()!.getId());
|
||||||
|
if (!s) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
s.pid = message.getIdentifySession()!.getPid();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
91
packages/server/src/browser/command.ts
Normal file
91
packages/server/src/browser/command.ts
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
import * as events from "events";
|
||||||
|
import * as stream from "stream";
|
||||||
|
import { SendableConnection } from "../common/connection";
|
||||||
|
import { ShutdownSessionMessage, ClientMessage, SessionOutputMessage, WriteToSessionMessage, ResizeSessionTTYMessage, TTYDimensions as ProtoTTYDimensions } from "../proto";
|
||||||
|
|
||||||
|
export interface TTYDimensions {
|
||||||
|
readonly columns: number;
|
||||||
|
readonly rows: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SpawnOptions {
|
||||||
|
cwd?: string;
|
||||||
|
env?: { readonly [key: string]: string };
|
||||||
|
tty?: TTYDimensions;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ChildProcess {
|
||||||
|
readonly stdin: stream.Writable;
|
||||||
|
readonly stdout: stream.Readable;
|
||||||
|
readonly stderr: stream.Readable;
|
||||||
|
|
||||||
|
readonly killed?: boolean;
|
||||||
|
readonly pid: number | undefined;
|
||||||
|
|
||||||
|
kill(signal?: string): void;
|
||||||
|
send(message: string | Uint8Array): void;
|
||||||
|
|
||||||
|
on(event: "error", listener: (err: Error) => void): void;
|
||||||
|
on(event: "exit", listener: (code: number, signal: string) => void): void;
|
||||||
|
|
||||||
|
resize?(dimensions: TTYDimensions): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ServerProcess extends events.EventEmitter implements ChildProcess {
|
||||||
|
public readonly stdin = new stream.Writable();
|
||||||
|
public readonly stdout = new stream.Readable({ read: () => true });
|
||||||
|
public readonly stderr = new stream.Readable({ read: () => true });
|
||||||
|
public pid: number | undefined;
|
||||||
|
|
||||||
|
private _killed: boolean = false;
|
||||||
|
|
||||||
|
public constructor(
|
||||||
|
private readonly connection: SendableConnection,
|
||||||
|
private readonly id: number,
|
||||||
|
private readonly hasTty: boolean = false,
|
||||||
|
) {
|
||||||
|
super();
|
||||||
|
|
||||||
|
if (!this.hasTty) {
|
||||||
|
delete this.resize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public get killed(): boolean {
|
||||||
|
return this._killed;
|
||||||
|
}
|
||||||
|
|
||||||
|
public kill(signal?: string): void {
|
||||||
|
const kill = new ShutdownSessionMessage();
|
||||||
|
kill.setId(this.id);
|
||||||
|
if (signal) {
|
||||||
|
kill.setSignal(signal);
|
||||||
|
}
|
||||||
|
const client = new ClientMessage();
|
||||||
|
client.setShutdownSession(kill);
|
||||||
|
this.connection.send(client.serializeBinary());
|
||||||
|
|
||||||
|
this._killed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public send(message: string | Uint8Array): void {
|
||||||
|
const send = new WriteToSessionMessage();
|
||||||
|
send.setId(this.id);
|
||||||
|
send.setData(typeof message === "string" ? new TextEncoder().encode(message) : message);
|
||||||
|
const client = new ClientMessage();
|
||||||
|
client.setWriteToSession(send);
|
||||||
|
this.connection.send(client.serializeBinary());
|
||||||
|
}
|
||||||
|
|
||||||
|
public resize(dimensions: TTYDimensions) {
|
||||||
|
const resize = new ResizeSessionTTYMessage();
|
||||||
|
resize.setId(this.id);
|
||||||
|
const tty = new ProtoTTYDimensions();
|
||||||
|
tty.setHeight(dimensions.rows);
|
||||||
|
tty.setWidth(dimensions.columns);
|
||||||
|
resize.setTtyDimensions(tty);
|
||||||
|
const client = new ClientMessage();
|
||||||
|
client.setResizeSessionTty(resize);
|
||||||
|
this.connection.send(client.serializeBinary());
|
||||||
|
}
|
||||||
|
}
|
105
packages/server/src/node/command.ts
Normal file
105
packages/server/src/node/command.ts
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
import * as cp from "child_process";
|
||||||
|
import * as nodePty from "node-pty";
|
||||||
|
import * as stream from "stream";
|
||||||
|
import { TextEncoder } from "text-encoding";
|
||||||
|
import { NewSessionMessage, ServerMessage, SessionDoneMessage, SessionOutputMessage, ShutdownSessionMessage, IdentifySessionMessage, ClientMessage } from "../proto";
|
||||||
|
import { SendableConnection } from "../common/connection";
|
||||||
|
|
||||||
|
export interface Process {
|
||||||
|
stdin?: stream.Writable;
|
||||||
|
stdout?: stream.Readable;
|
||||||
|
stderr?: stream.Readable;
|
||||||
|
|
||||||
|
pid: number;
|
||||||
|
killed?: boolean;
|
||||||
|
|
||||||
|
on(event: "data", cb: (data: string) => void): void;
|
||||||
|
on(event: 'exit', listener: (exitCode: number, signal?: number) => void): void;
|
||||||
|
write(data: string | Uint8Array): void;
|
||||||
|
resize?(cols: number, rows: number): void;
|
||||||
|
kill(signal?: string): void;
|
||||||
|
title?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const handleNewSession = (connection: SendableConnection, newSession: NewSessionMessage, onExit: () => void): Process => {
|
||||||
|
let process: Process;
|
||||||
|
|
||||||
|
const env = {} as any;
|
||||||
|
newSession.getEnvMap().forEach((value: any, key: any) => {
|
||||||
|
env[key] = value;
|
||||||
|
});
|
||||||
|
if (newSession.getTtyDimensions()) {
|
||||||
|
// Spawn with node-pty
|
||||||
|
process = nodePty.spawn(newSession.getCommand(), newSession.getArgsList(), {
|
||||||
|
cols: newSession.getTtyDimensions()!.getWidth(),
|
||||||
|
rows: newSession.getTtyDimensions()!.getHeight(),
|
||||||
|
cwd: newSession.getCwd(),
|
||||||
|
env,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
const options = {
|
||||||
|
cwd: newSession.getCwd(),
|
||||||
|
env,
|
||||||
|
};
|
||||||
|
let proc: cp.ChildProcess;
|
||||||
|
if (newSession.getIsFork()) {
|
||||||
|
proc = cp.fork(newSession.getCommand(), newSession.getArgsList());
|
||||||
|
} else {
|
||||||
|
proc = cp.spawn(newSession.getCommand(), newSession.getArgsList(), options);
|
||||||
|
}
|
||||||
|
|
||||||
|
process = {
|
||||||
|
stdin: proc.stdin,
|
||||||
|
stderr: proc.stderr,
|
||||||
|
stdout: proc.stdout,
|
||||||
|
on: (...args: any[]) => (<any>proc.on)(...args),
|
||||||
|
write: (d) => proc.stdin.write(d),
|
||||||
|
kill: (s) => proc.kill(s || "SIGTERM"),
|
||||||
|
pid: proc.pid,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const sendOutput = (fd: SessionOutputMessage.FD, msg: string | Uint8Array): void => {
|
||||||
|
const serverMsg = new ServerMessage();
|
||||||
|
const d = new SessionOutputMessage();
|
||||||
|
d.setId(newSession.getId());
|
||||||
|
d.setData(typeof msg === "string" ? new TextEncoder().encode(msg) : msg);
|
||||||
|
d.setFd(SessionOutputMessage.FD.STDOUT);
|
||||||
|
serverMsg.setSessionOutput(d);
|
||||||
|
connection.send(serverMsg.serializeBinary());
|
||||||
|
};
|
||||||
|
|
||||||
|
if (process.stdout && process.stderr) {
|
||||||
|
process.stdout.on("data", (data) => {
|
||||||
|
sendOutput(SessionOutputMessage.FD.STDOUT, data);
|
||||||
|
});
|
||||||
|
|
||||||
|
process.stderr.on("data", (data) => {
|
||||||
|
sendOutput(SessionOutputMessage.FD.STDERR, data);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
process.on("data", (data) => {
|
||||||
|
sendOutput(SessionOutputMessage.FD.STDOUT, Buffer.from(data));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const id = new IdentifySessionMessage();
|
||||||
|
id.setId(newSession.getId());
|
||||||
|
id.setPid(process.pid);
|
||||||
|
const sm = new ServerMessage();
|
||||||
|
sm.setIdentifySession(id);
|
||||||
|
connection.send(sm.serializeBinary());
|
||||||
|
|
||||||
|
process.on("exit", (code, signal) => {
|
||||||
|
const serverMsg = new ServerMessage();
|
||||||
|
const exit = new SessionDoneMessage();
|
||||||
|
exit.setId(newSession.getId());
|
||||||
|
exit.setExitStatus(code);
|
||||||
|
serverMsg.setSessionDone(exit);
|
||||||
|
connection.send(serverMsg.serializeBinary());
|
||||||
|
|
||||||
|
onExit();
|
||||||
|
});
|
||||||
|
|
||||||
|
return process;
|
||||||
|
};
|
@ -1,13 +1,19 @@
|
|||||||
import { logger, field } from "@coder/logger";
|
import { logger, field } from "@coder/logger";
|
||||||
|
import { TextDecoder } from "text-encoding";
|
||||||
import { ClientMessage } from "../proto";
|
import { ClientMessage } from "../proto";
|
||||||
import { evaluate } from "./evaluate";
|
import { evaluate } from "./evaluate";
|
||||||
import { ReadWriteConnection } from "../common/connection";
|
import { ReadWriteConnection } from "../common/connection";
|
||||||
|
import { Process, handleNewSession } from "./command";
|
||||||
|
|
||||||
export class Server {
|
export class Server {
|
||||||
|
|
||||||
|
private readonly sessions: Map<number, Process>;
|
||||||
|
|
||||||
public constructor(
|
public constructor(
|
||||||
private readonly connection: ReadWriteConnection,
|
private readonly connection: ReadWriteConnection,
|
||||||
) {
|
) {
|
||||||
|
this.sessions = new Map();
|
||||||
|
|
||||||
connection.onMessage((data) => {
|
connection.onMessage((data) => {
|
||||||
try {
|
try {
|
||||||
this.handleMessage(ClientMessage.deserializeBinary(data));
|
this.handleMessage(ClientMessage.deserializeBinary(data));
|
||||||
@ -20,7 +26,42 @@ export class Server {
|
|||||||
private handleMessage(message: ClientMessage): void {
|
private handleMessage(message: ClientMessage): void {
|
||||||
if (message.hasNewEval()) {
|
if (message.hasNewEval()) {
|
||||||
evaluate(this.connection, message.getNewEval()!);
|
evaluate(this.connection, message.getNewEval()!);
|
||||||
|
} else if (message.hasNewSession()) {
|
||||||
|
const session = handleNewSession(this.connection, message.getNewSession()!, () => {
|
||||||
|
this.sessions.delete(message.getNewSession()!.getId());
|
||||||
|
});
|
||||||
|
|
||||||
|
this.sessions.set(message.getNewSession()!.getId(), session);
|
||||||
|
} else if (message.hasCloseSessionInput()) {
|
||||||
|
const s = this.getSession(message.getCloseSessionInput()!.getId());
|
||||||
|
if (!s || !s.stdin) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
s.stdin.end();
|
||||||
|
} else if (message.hasResizeSessionTty()) {
|
||||||
|
const s = this.getSession(message.getResizeSessionTty()!.getId());
|
||||||
|
if (!s || !s.resize) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const tty = message.getResizeSessionTty()!.getTtyDimensions()!;
|
||||||
|
s.resize(tty.getWidth(), tty.getHeight());
|
||||||
|
} else if (message.hasShutdownSession()) {
|
||||||
|
const s = this.getSession(message.getShutdownSession()!.getId());
|
||||||
|
if (!s) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
s.kill(message.getShutdownSession()!.getSignal());
|
||||||
|
} else if (message.hasWriteToSession()) {
|
||||||
|
const s = this.getSession(message.getWriteToSession()!.getId());
|
||||||
|
if (!s) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
s.write(new TextDecoder().decode(message.getWriteToSession()!.getData_asU8()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
private getSession(id: number): Process | undefined {
|
||||||
|
return this.sessions.get(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
@ -3,29 +3,29 @@ import "command.proto";
|
|||||||
import "node.proto";
|
import "node.proto";
|
||||||
|
|
||||||
message ClientMessage {
|
message ClientMessage {
|
||||||
oneof msg {
|
oneof msg {
|
||||||
// command.proto
|
// command.proto
|
||||||
NewSessionMessage new_session = 1;
|
NewSessionMessage new_session = 1;
|
||||||
ShutdownSessionMessage shutdown_session = 2;
|
ShutdownSessionMessage shutdown_session = 2;
|
||||||
WriteToSessionMessage write_to_session = 3;
|
WriteToSessionMessage write_to_session = 3;
|
||||||
CloseSessionInputMessage close_session_input = 4;
|
CloseSessionInputMessage close_session_input = 4;
|
||||||
ResizeSessionTTYMessage resize_session_tty = 5;
|
ResizeSessionTTYMessage resize_session_tty = 5;
|
||||||
|
|
||||||
// node.proto
|
// node.proto
|
||||||
NewEvalMessage new_eval = 6;
|
NewEvalMessage new_eval = 6;
|
||||||
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
message ServerMessage {
|
message ServerMessage {
|
||||||
oneof msg {
|
oneof msg {
|
||||||
// command.proto
|
// command.proto
|
||||||
NewSessionFailureMessage new_session_failure = 1;
|
NewSessionFailureMessage new_session_failure = 1;
|
||||||
SessionDoneMessage session_done = 2;
|
SessionDoneMessage session_done = 2;
|
||||||
SessionOutputMessage session_output = 3;
|
SessionOutputMessage session_output = 3;
|
||||||
|
IdentifySessionMessage identify_session = 4;
|
||||||
// node.proto
|
|
||||||
EvalFailedMessage eval_failed = 4;
|
// node.proto
|
||||||
EvalDoneMessage eval_done = 5;
|
EvalFailedMessage eval_failed = 5;
|
||||||
}
|
EvalDoneMessage eval_done = 6;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
11
packages/server/src/proto/client_pb.d.ts
vendored
11
packages/server/src/proto/client_pb.d.ts
vendored
@ -84,6 +84,11 @@ export class ServerMessage extends jspb.Message {
|
|||||||
getSessionOutput(): command_pb.SessionOutputMessage | undefined;
|
getSessionOutput(): command_pb.SessionOutputMessage | undefined;
|
||||||
setSessionOutput(value?: command_pb.SessionOutputMessage): void;
|
setSessionOutput(value?: command_pb.SessionOutputMessage): void;
|
||||||
|
|
||||||
|
hasIdentifySession(): boolean;
|
||||||
|
clearIdentifySession(): void;
|
||||||
|
getIdentifySession(): command_pb.IdentifySessionMessage | undefined;
|
||||||
|
setIdentifySession(value?: command_pb.IdentifySessionMessage): void;
|
||||||
|
|
||||||
hasEvalFailed(): boolean;
|
hasEvalFailed(): boolean;
|
||||||
clearEvalFailed(): void;
|
clearEvalFailed(): void;
|
||||||
getEvalFailed(): node_pb.EvalFailedMessage | undefined;
|
getEvalFailed(): node_pb.EvalFailedMessage | undefined;
|
||||||
@ -110,6 +115,7 @@ export namespace ServerMessage {
|
|||||||
newSessionFailure?: command_pb.NewSessionFailureMessage.AsObject,
|
newSessionFailure?: command_pb.NewSessionFailureMessage.AsObject,
|
||||||
sessionDone?: command_pb.SessionDoneMessage.AsObject,
|
sessionDone?: command_pb.SessionDoneMessage.AsObject,
|
||||||
sessionOutput?: command_pb.SessionOutputMessage.AsObject,
|
sessionOutput?: command_pb.SessionOutputMessage.AsObject,
|
||||||
|
identifySession?: command_pb.IdentifySessionMessage.AsObject,
|
||||||
evalFailed?: node_pb.EvalFailedMessage.AsObject,
|
evalFailed?: node_pb.EvalFailedMessage.AsObject,
|
||||||
evalDone?: node_pb.EvalDoneMessage.AsObject,
|
evalDone?: node_pb.EvalDoneMessage.AsObject,
|
||||||
}
|
}
|
||||||
@ -119,8 +125,9 @@ export namespace ServerMessage {
|
|||||||
NEW_SESSION_FAILURE = 1,
|
NEW_SESSION_FAILURE = 1,
|
||||||
SESSION_DONE = 2,
|
SESSION_DONE = 2,
|
||||||
SESSION_OUTPUT = 3,
|
SESSION_OUTPUT = 3,
|
||||||
EVAL_FAILED = 4,
|
IDENTIFY_SESSION = 4,
|
||||||
EVAL_DONE = 5,
|
EVAL_FAILED = 5,
|
||||||
|
EVAL_DONE = 6,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -465,7 +465,7 @@ if (goog.DEBUG && !COMPILED) {
|
|||||||
* @private {!Array<!Array<number>>}
|
* @private {!Array<!Array<number>>}
|
||||||
* @const
|
* @const
|
||||||
*/
|
*/
|
||||||
proto.ServerMessage.oneofGroups_ = [[1,2,3,4,5]];
|
proto.ServerMessage.oneofGroups_ = [[1,2,3,4,5,6]];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @enum {number}
|
* @enum {number}
|
||||||
@ -475,8 +475,9 @@ proto.ServerMessage.MsgCase = {
|
|||||||
NEW_SESSION_FAILURE: 1,
|
NEW_SESSION_FAILURE: 1,
|
||||||
SESSION_DONE: 2,
|
SESSION_DONE: 2,
|
||||||
SESSION_OUTPUT: 3,
|
SESSION_OUTPUT: 3,
|
||||||
EVAL_FAILED: 4,
|
IDENTIFY_SESSION: 4,
|
||||||
EVAL_DONE: 5
|
EVAL_FAILED: 5,
|
||||||
|
EVAL_DONE: 6
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -517,6 +518,7 @@ proto.ServerMessage.toObject = function(includeInstance, msg) {
|
|||||||
newSessionFailure: (f = msg.getNewSessionFailure()) && command_pb.NewSessionFailureMessage.toObject(includeInstance, f),
|
newSessionFailure: (f = msg.getNewSessionFailure()) && command_pb.NewSessionFailureMessage.toObject(includeInstance, f),
|
||||||
sessionDone: (f = msg.getSessionDone()) && command_pb.SessionDoneMessage.toObject(includeInstance, f),
|
sessionDone: (f = msg.getSessionDone()) && command_pb.SessionDoneMessage.toObject(includeInstance, f),
|
||||||
sessionOutput: (f = msg.getSessionOutput()) && command_pb.SessionOutputMessage.toObject(includeInstance, f),
|
sessionOutput: (f = msg.getSessionOutput()) && command_pb.SessionOutputMessage.toObject(includeInstance, f),
|
||||||
|
identifySession: (f = msg.getIdentifySession()) && command_pb.IdentifySessionMessage.toObject(includeInstance, f),
|
||||||
evalFailed: (f = msg.getEvalFailed()) && node_pb.EvalFailedMessage.toObject(includeInstance, f),
|
evalFailed: (f = msg.getEvalFailed()) && node_pb.EvalFailedMessage.toObject(includeInstance, f),
|
||||||
evalDone: (f = msg.getEvalDone()) && node_pb.EvalDoneMessage.toObject(includeInstance, f)
|
evalDone: (f = msg.getEvalDone()) && node_pb.EvalDoneMessage.toObject(includeInstance, f)
|
||||||
};
|
};
|
||||||
@ -571,11 +573,16 @@ proto.ServerMessage.deserializeBinaryFromReader = function(msg, reader) {
|
|||||||
msg.setSessionOutput(value);
|
msg.setSessionOutput(value);
|
||||||
break;
|
break;
|
||||||
case 4:
|
case 4:
|
||||||
|
var value = new command_pb.IdentifySessionMessage;
|
||||||
|
reader.readMessage(value,command_pb.IdentifySessionMessage.deserializeBinaryFromReader);
|
||||||
|
msg.setIdentifySession(value);
|
||||||
|
break;
|
||||||
|
case 5:
|
||||||
var value = new node_pb.EvalFailedMessage;
|
var value = new node_pb.EvalFailedMessage;
|
||||||
reader.readMessage(value,node_pb.EvalFailedMessage.deserializeBinaryFromReader);
|
reader.readMessage(value,node_pb.EvalFailedMessage.deserializeBinaryFromReader);
|
||||||
msg.setEvalFailed(value);
|
msg.setEvalFailed(value);
|
||||||
break;
|
break;
|
||||||
case 5:
|
case 6:
|
||||||
var value = new node_pb.EvalDoneMessage;
|
var value = new node_pb.EvalDoneMessage;
|
||||||
reader.readMessage(value,node_pb.EvalDoneMessage.deserializeBinaryFromReader);
|
reader.readMessage(value,node_pb.EvalDoneMessage.deserializeBinaryFromReader);
|
||||||
msg.setEvalDone(value);
|
msg.setEvalDone(value);
|
||||||
@ -642,18 +649,26 @@ proto.ServerMessage.prototype.serializeBinaryToWriter = function (writer) {
|
|||||||
command_pb.SessionOutputMessage.serializeBinaryToWriter
|
command_pb.SessionOutputMessage.serializeBinaryToWriter
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
f = this.getEvalFailed();
|
f = this.getIdentifySession();
|
||||||
if (f != null) {
|
if (f != null) {
|
||||||
writer.writeMessage(
|
writer.writeMessage(
|
||||||
4,
|
4,
|
||||||
f,
|
f,
|
||||||
|
command_pb.IdentifySessionMessage.serializeBinaryToWriter
|
||||||
|
);
|
||||||
|
}
|
||||||
|
f = this.getEvalFailed();
|
||||||
|
if (f != null) {
|
||||||
|
writer.writeMessage(
|
||||||
|
5,
|
||||||
|
f,
|
||||||
node_pb.EvalFailedMessage.serializeBinaryToWriter
|
node_pb.EvalFailedMessage.serializeBinaryToWriter
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
f = this.getEvalDone();
|
f = this.getEvalDone();
|
||||||
if (f != null) {
|
if (f != null) {
|
||||||
writer.writeMessage(
|
writer.writeMessage(
|
||||||
5,
|
6,
|
||||||
f,
|
f,
|
||||||
node_pb.EvalDoneMessage.serializeBinaryToWriter
|
node_pb.EvalDoneMessage.serializeBinaryToWriter
|
||||||
);
|
);
|
||||||
@ -761,18 +776,48 @@ proto.ServerMessage.prototype.hasSessionOutput = function() {
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* optional EvalFailedMessage eval_failed = 4;
|
* optional IdentifySessionMessage identify_session = 4;
|
||||||
|
* @return {proto.IdentifySessionMessage}
|
||||||
|
*/
|
||||||
|
proto.ServerMessage.prototype.getIdentifySession = function() {
|
||||||
|
return /** @type{proto.IdentifySessionMessage} */ (
|
||||||
|
jspb.Message.getWrapperField(this, command_pb.IdentifySessionMessage, 4));
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/** @param {proto.IdentifySessionMessage|undefined} value */
|
||||||
|
proto.ServerMessage.prototype.setIdentifySession = function(value) {
|
||||||
|
jspb.Message.setOneofWrapperField(this, 4, proto.ServerMessage.oneofGroups_[0], value);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
proto.ServerMessage.prototype.clearIdentifySession = function() {
|
||||||
|
this.setIdentifySession(undefined);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether this field is set.
|
||||||
|
* @return{!boolean}
|
||||||
|
*/
|
||||||
|
proto.ServerMessage.prototype.hasIdentifySession = function() {
|
||||||
|
return jspb.Message.getField(this, 4) != null;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* optional EvalFailedMessage eval_failed = 5;
|
||||||
* @return {proto.EvalFailedMessage}
|
* @return {proto.EvalFailedMessage}
|
||||||
*/
|
*/
|
||||||
proto.ServerMessage.prototype.getEvalFailed = function() {
|
proto.ServerMessage.prototype.getEvalFailed = function() {
|
||||||
return /** @type{proto.EvalFailedMessage} */ (
|
return /** @type{proto.EvalFailedMessage} */ (
|
||||||
jspb.Message.getWrapperField(this, node_pb.EvalFailedMessage, 4));
|
jspb.Message.getWrapperField(this, node_pb.EvalFailedMessage, 5));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/** @param {proto.EvalFailedMessage|undefined} value */
|
/** @param {proto.EvalFailedMessage|undefined} value */
|
||||||
proto.ServerMessage.prototype.setEvalFailed = function(value) {
|
proto.ServerMessage.prototype.setEvalFailed = function(value) {
|
||||||
jspb.Message.setOneofWrapperField(this, 4, proto.ServerMessage.oneofGroups_[0], value);
|
jspb.Message.setOneofWrapperField(this, 5, proto.ServerMessage.oneofGroups_[0], value);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -786,23 +831,23 @@ proto.ServerMessage.prototype.clearEvalFailed = function() {
|
|||||||
* @return{!boolean}
|
* @return{!boolean}
|
||||||
*/
|
*/
|
||||||
proto.ServerMessage.prototype.hasEvalFailed = function() {
|
proto.ServerMessage.prototype.hasEvalFailed = function() {
|
||||||
return jspb.Message.getField(this, 4) != null;
|
return jspb.Message.getField(this, 5) != null;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* optional EvalDoneMessage eval_done = 5;
|
* optional EvalDoneMessage eval_done = 6;
|
||||||
* @return {proto.EvalDoneMessage}
|
* @return {proto.EvalDoneMessage}
|
||||||
*/
|
*/
|
||||||
proto.ServerMessage.prototype.getEvalDone = function() {
|
proto.ServerMessage.prototype.getEvalDone = function() {
|
||||||
return /** @type{proto.EvalDoneMessage} */ (
|
return /** @type{proto.EvalDoneMessage} */ (
|
||||||
jspb.Message.getWrapperField(this, node_pb.EvalDoneMessage, 5));
|
jspb.Message.getWrapperField(this, node_pb.EvalDoneMessage, 6));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/** @param {proto.EvalDoneMessage|undefined} value */
|
/** @param {proto.EvalDoneMessage|undefined} value */
|
||||||
proto.ServerMessage.prototype.setEvalDone = function(value) {
|
proto.ServerMessage.prototype.setEvalDone = function(value) {
|
||||||
jspb.Message.setOneofWrapperField(this, 5, proto.ServerMessage.oneofGroups_[0], value);
|
jspb.Message.setOneofWrapperField(this, 6, proto.ServerMessage.oneofGroups_[0], value);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -816,7 +861,7 @@ proto.ServerMessage.prototype.clearEvalDone = function() {
|
|||||||
* @return{!boolean}
|
* @return{!boolean}
|
||||||
*/
|
*/
|
||||||
proto.ServerMessage.prototype.hasEvalDone = function() {
|
proto.ServerMessage.prototype.hasEvalDone = function() {
|
||||||
return jspb.Message.getField(this, 5) != null;
|
return jspb.Message.getField(this, 6) != null;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -6,70 +6,73 @@ syntax = "proto3";
|
|||||||
// If env is provided, the environment variables will be set.
|
// If env is provided, the environment variables will be set.
|
||||||
// If tty_dimensions is included, we will spawn a tty for the command using the given dimensions.
|
// If tty_dimensions is included, we will spawn a tty for the command using the given dimensions.
|
||||||
message NewSessionMessage {
|
message NewSessionMessage {
|
||||||
uint64 id = 1;
|
uint64 id = 1;
|
||||||
string command = 2;
|
string command = 2;
|
||||||
repeated string args = 3;
|
repeated string args = 3;
|
||||||
map<string, string> env = 4;
|
map<string, string> env = 4;
|
||||||
TTYDimensions tty_dimensions = 5;
|
string cwd = 5;
|
||||||
|
TTYDimensions tty_dimensions = 6;
|
||||||
|
bool is_fork = 7;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sent when starting a session failed.
|
// Sent when starting a session failed.
|
||||||
message NewSessionFailureMessage {
|
message NewSessionFailureMessage {
|
||||||
uint64 id = 1;
|
uint64 id = 1;
|
||||||
enum Reason {
|
enum Reason {
|
||||||
Prohibited = 0;
|
Prohibited = 0;
|
||||||
ResourceShortage = 1;
|
ResourceShortage = 1;
|
||||||
}
|
}
|
||||||
Reason reason = 2;
|
Reason reason = 2;
|
||||||
string message = 3;
|
string message = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sent when a session has completed
|
// Sent when a session has completed
|
||||||
message SessionDoneMessage {
|
message SessionDoneMessage {
|
||||||
uint64 id = 1;
|
uint64 id = 1;
|
||||||
int64 exit_status = 2;
|
int64 exit_status = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Identifies a session with a PID.
|
// Identifies a session with a PID.
|
||||||
message IdentifySessionMessage {
|
message IdentifySessionMessage {
|
||||||
uint64 id = 1;
|
uint64 id = 1;
|
||||||
uint64 pid = 2;
|
uint64 pid = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Writes data to a session.
|
// Writes data to a session.
|
||||||
message WriteToSessionMessage {
|
message WriteToSessionMessage {
|
||||||
uint64 id = 1;
|
uint64 id = 1;
|
||||||
bytes data = 2;
|
bytes data = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Resizes the TTY of the session identified by the id.
|
// Resizes the TTY of the session identified by the id.
|
||||||
// The connection will be closed if a TTY was not requested when the session was created.
|
// The connection will be closed if a TTY was not requested when the session was created.
|
||||||
message ResizeSessionTTYMessage {
|
message ResizeSessionTTYMessage {
|
||||||
uint64 id = 1;
|
uint64 id = 1;
|
||||||
TTYDimensions tty_dimensions = 2;
|
TTYDimensions tty_dimensions = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
// CloseSessionInputMessage closes the stdin of the session by the ID.
|
// CloseSessionInputMessage closes the stdin of the session by the ID.
|
||||||
message CloseSessionInputMessage {
|
message CloseSessionInputMessage {
|
||||||
uint64 id = 1;
|
uint64 id = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
message ShutdownSessionMessage {
|
message ShutdownSessionMessage {
|
||||||
uint64 id = 1;
|
uint64 id = 1;
|
||||||
|
string signal = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
// SessionOutputMessage carries data read from the stdout or stderr of the session identified by the id.
|
// SessionOutputMessage carries data read from the stdout or stderr of the session identified by the id.
|
||||||
message SessionOutputMessage {
|
message SessionOutputMessage {
|
||||||
uint64 id = 1;
|
uint64 id = 1;
|
||||||
enum FD {
|
enum FD {
|
||||||
Stdout = 0;
|
Stdout = 0;
|
||||||
Stderr = 1;
|
Stderr = 1;
|
||||||
}
|
}
|
||||||
FD fd = 2;
|
FD fd = 2;
|
||||||
bytes data = 3;
|
bytes data = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
message TTYDimensions {
|
message TTYDimensions {
|
||||||
uint32 height = 1;
|
uint32 height = 1;
|
||||||
uint32 width = 2;
|
uint32 width = 2;
|
||||||
}
|
}
|
||||||
|
12
packages/server/src/proto/command_pb.d.ts
vendored
12
packages/server/src/proto/command_pb.d.ts
vendored
@ -17,11 +17,17 @@ export class NewSessionMessage extends jspb.Message {
|
|||||||
|
|
||||||
getEnvMap(): jspb.Map<string, string>;
|
getEnvMap(): jspb.Map<string, string>;
|
||||||
clearEnvMap(): void;
|
clearEnvMap(): void;
|
||||||
|
getCwd(): string;
|
||||||
|
setCwd(value: string): void;
|
||||||
|
|
||||||
hasTtyDimensions(): boolean;
|
hasTtyDimensions(): boolean;
|
||||||
clearTtyDimensions(): void;
|
clearTtyDimensions(): void;
|
||||||
getTtyDimensions(): TTYDimensions | undefined;
|
getTtyDimensions(): TTYDimensions | undefined;
|
||||||
setTtyDimensions(value?: TTYDimensions): void;
|
setTtyDimensions(value?: TTYDimensions): void;
|
||||||
|
|
||||||
|
getIsFork(): boolean;
|
||||||
|
setIsFork(value: boolean): void;
|
||||||
|
|
||||||
serializeBinary(): Uint8Array;
|
serializeBinary(): Uint8Array;
|
||||||
toObject(includeInstance?: boolean): NewSessionMessage.AsObject;
|
toObject(includeInstance?: boolean): NewSessionMessage.AsObject;
|
||||||
static toObject(includeInstance: boolean, msg: NewSessionMessage): NewSessionMessage.AsObject;
|
static toObject(includeInstance: boolean, msg: NewSessionMessage): NewSessionMessage.AsObject;
|
||||||
@ -38,7 +44,9 @@ export namespace NewSessionMessage {
|
|||||||
command: string,
|
command: string,
|
||||||
argsList: Array<string>,
|
argsList: Array<string>,
|
||||||
envMap: Array<[string, string]>,
|
envMap: Array<[string, string]>,
|
||||||
|
cwd: string,
|
||||||
ttyDimensions?: TTYDimensions.AsObject,
|
ttyDimensions?: TTYDimensions.AsObject,
|
||||||
|
isFork: boolean,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -199,6 +207,9 @@ export class ShutdownSessionMessage extends jspb.Message {
|
|||||||
getId(): number;
|
getId(): number;
|
||||||
setId(value: number): void;
|
setId(value: number): void;
|
||||||
|
|
||||||
|
getSignal(): string;
|
||||||
|
setSignal(value: string): void;
|
||||||
|
|
||||||
serializeBinary(): Uint8Array;
|
serializeBinary(): Uint8Array;
|
||||||
toObject(includeInstance?: boolean): ShutdownSessionMessage.AsObject;
|
toObject(includeInstance?: boolean): ShutdownSessionMessage.AsObject;
|
||||||
static toObject(includeInstance: boolean, msg: ShutdownSessionMessage): ShutdownSessionMessage.AsObject;
|
static toObject(includeInstance: boolean, msg: ShutdownSessionMessage): ShutdownSessionMessage.AsObject;
|
||||||
@ -212,6 +223,7 @@ export class ShutdownSessionMessage extends jspb.Message {
|
|||||||
export namespace ShutdownSessionMessage {
|
export namespace ShutdownSessionMessage {
|
||||||
export type AsObject = {
|
export type AsObject = {
|
||||||
id: number,
|
id: number,
|
||||||
|
signal: string,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -78,7 +78,9 @@ proto.NewSessionMessage.toObject = function(includeInstance, msg) {
|
|||||||
command: msg.getCommand(),
|
command: msg.getCommand(),
|
||||||
argsList: jspb.Message.getField(msg, 3),
|
argsList: jspb.Message.getField(msg, 3),
|
||||||
envMap: (f = msg.getEnvMap(true)) ? f.toArray() : [],
|
envMap: (f = msg.getEnvMap(true)) ? f.toArray() : [],
|
||||||
ttyDimensions: (f = msg.getTtyDimensions()) && proto.TTYDimensions.toObject(includeInstance, f)
|
cwd: msg.getCwd(),
|
||||||
|
ttyDimensions: (f = msg.getTtyDimensions()) && proto.TTYDimensions.toObject(includeInstance, f),
|
||||||
|
isFork: msg.getIsFork()
|
||||||
};
|
};
|
||||||
|
|
||||||
if (includeInstance) {
|
if (includeInstance) {
|
||||||
@ -135,10 +137,18 @@ proto.NewSessionMessage.deserializeBinaryFromReader = function(msg, reader) {
|
|||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
case 5:
|
case 5:
|
||||||
|
var value = /** @type {string} */ (reader.readString());
|
||||||
|
msg.setCwd(value);
|
||||||
|
break;
|
||||||
|
case 6:
|
||||||
var value = new proto.TTYDimensions;
|
var value = new proto.TTYDimensions;
|
||||||
reader.readMessage(value,proto.TTYDimensions.deserializeBinaryFromReader);
|
reader.readMessage(value,proto.TTYDimensions.deserializeBinaryFromReader);
|
||||||
msg.setTtyDimensions(value);
|
msg.setTtyDimensions(value);
|
||||||
break;
|
break;
|
||||||
|
case 7:
|
||||||
|
var value = /** @type {boolean} */ (reader.readBool());
|
||||||
|
msg.setIsFork(value);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
reader.skipField();
|
reader.skipField();
|
||||||
break;
|
break;
|
||||||
@ -202,14 +212,28 @@ proto.NewSessionMessage.prototype.serializeBinaryToWriter = function (writer) {
|
|||||||
if (f && f.getLength() > 0) {
|
if (f && f.getLength() > 0) {
|
||||||
f.serializeBinary(4, writer, jspb.BinaryWriter.prototype.writeString, jspb.BinaryWriter.prototype.writeString);
|
f.serializeBinary(4, writer, jspb.BinaryWriter.prototype.writeString, jspb.BinaryWriter.prototype.writeString);
|
||||||
}
|
}
|
||||||
|
f = this.getCwd();
|
||||||
|
if (f.length > 0) {
|
||||||
|
writer.writeString(
|
||||||
|
5,
|
||||||
|
f
|
||||||
|
);
|
||||||
|
}
|
||||||
f = this.getTtyDimensions();
|
f = this.getTtyDimensions();
|
||||||
if (f != null) {
|
if (f != null) {
|
||||||
writer.writeMessage(
|
writer.writeMessage(
|
||||||
5,
|
6,
|
||||||
f,
|
f,
|
||||||
proto.TTYDimensions.serializeBinaryToWriter
|
proto.TTYDimensions.serializeBinaryToWriter
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
f = this.getIsFork();
|
||||||
|
if (f) {
|
||||||
|
writer.writeBool(
|
||||||
|
7,
|
||||||
|
f
|
||||||
|
);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -288,18 +312,33 @@ proto.NewSessionMessage.prototype.getEnvMap = function(opt_noLazyCreate) {
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* optional TTYDimensions tty_dimensions = 5;
|
* optional string cwd = 5;
|
||||||
|
* @return {string}
|
||||||
|
*/
|
||||||
|
proto.NewSessionMessage.prototype.getCwd = function() {
|
||||||
|
return /** @type {string} */ (jspb.Message.getFieldProto3(this, 5, ""));
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/** @param {string} value */
|
||||||
|
proto.NewSessionMessage.prototype.setCwd = function(value) {
|
||||||
|
jspb.Message.setField(this, 5, value);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* optional TTYDimensions tty_dimensions = 6;
|
||||||
* @return {proto.TTYDimensions}
|
* @return {proto.TTYDimensions}
|
||||||
*/
|
*/
|
||||||
proto.NewSessionMessage.prototype.getTtyDimensions = function() {
|
proto.NewSessionMessage.prototype.getTtyDimensions = function() {
|
||||||
return /** @type{proto.TTYDimensions} */ (
|
return /** @type{proto.TTYDimensions} */ (
|
||||||
jspb.Message.getWrapperField(this, proto.TTYDimensions, 5));
|
jspb.Message.getWrapperField(this, proto.TTYDimensions, 6));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/** @param {proto.TTYDimensions|undefined} value */
|
/** @param {proto.TTYDimensions|undefined} value */
|
||||||
proto.NewSessionMessage.prototype.setTtyDimensions = function(value) {
|
proto.NewSessionMessage.prototype.setTtyDimensions = function(value) {
|
||||||
jspb.Message.setWrapperField(this, 5, value);
|
jspb.Message.setWrapperField(this, 6, value);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -313,7 +352,24 @@ proto.NewSessionMessage.prototype.clearTtyDimensions = function() {
|
|||||||
* @return{!boolean}
|
* @return{!boolean}
|
||||||
*/
|
*/
|
||||||
proto.NewSessionMessage.prototype.hasTtyDimensions = function() {
|
proto.NewSessionMessage.prototype.hasTtyDimensions = function() {
|
||||||
return jspb.Message.getField(this, 5) != null;
|
return jspb.Message.getField(this, 6) != null;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* optional bool is_fork = 7;
|
||||||
|
* Note that Boolean fields may be set to 0/1 when serialized from a Java server.
|
||||||
|
* You should avoid comparisons like {@code val === true/false} in those cases.
|
||||||
|
* @return {boolean}
|
||||||
|
*/
|
||||||
|
proto.NewSessionMessage.prototype.getIsFork = function() {
|
||||||
|
return /** @type {boolean} */ (jspb.Message.getFieldProto3(this, 7, false));
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/** @param {boolean} value */
|
||||||
|
proto.NewSessionMessage.prototype.setIsFork = function(value) {
|
||||||
|
jspb.Message.setField(this, 7, value);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -1528,7 +1584,8 @@ proto.ShutdownSessionMessage.prototype.toObject = function(opt_includeInstance)
|
|||||||
*/
|
*/
|
||||||
proto.ShutdownSessionMessage.toObject = function(includeInstance, msg) {
|
proto.ShutdownSessionMessage.toObject = function(includeInstance, msg) {
|
||||||
var f, obj = {
|
var f, obj = {
|
||||||
id: msg.getId()
|
id: msg.getId(),
|
||||||
|
signal: msg.getSignal()
|
||||||
};
|
};
|
||||||
|
|
||||||
if (includeInstance) {
|
if (includeInstance) {
|
||||||
@ -1569,6 +1626,10 @@ proto.ShutdownSessionMessage.deserializeBinaryFromReader = function(msg, reader)
|
|||||||
var value = /** @type {number} */ (reader.readUint64());
|
var value = /** @type {number} */ (reader.readUint64());
|
||||||
msg.setId(value);
|
msg.setId(value);
|
||||||
break;
|
break;
|
||||||
|
case 2:
|
||||||
|
var value = /** @type {string} */ (reader.readString());
|
||||||
|
msg.setSignal(value);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
reader.skipField();
|
reader.skipField();
|
||||||
break;
|
break;
|
||||||
@ -1614,6 +1675,13 @@ proto.ShutdownSessionMessage.prototype.serializeBinaryToWriter = function (write
|
|||||||
f
|
f
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
f = this.getSignal();
|
||||||
|
if (f.length > 0) {
|
||||||
|
writer.writeString(
|
||||||
|
2,
|
||||||
|
f
|
||||||
|
);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -1641,6 +1709,21 @@ proto.ShutdownSessionMessage.prototype.setId = function(value) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* optional string signal = 2;
|
||||||
|
* @return {string}
|
||||||
|
*/
|
||||||
|
proto.ShutdownSessionMessage.prototype.getSignal = function() {
|
||||||
|
return /** @type {string} */ (jspb.Message.getFieldProto3(this, 2, ""));
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/** @param {string} value */
|
||||||
|
proto.ShutdownSessionMessage.prototype.setSignal = function(value) {
|
||||||
|
jspb.Message.setField(this, 2, value);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generated by JsPbCodeGenerator.
|
* Generated by JsPbCodeGenerator.
|
||||||
|
@ -1,36 +1,36 @@
|
|||||||
syntax = "proto3";
|
syntax = "proto3";
|
||||||
|
|
||||||
message TypedValue {
|
message TypedValue {
|
||||||
enum Type {
|
enum Type {
|
||||||
String = 0;
|
String = 0;
|
||||||
Number = 1;
|
Number = 1;
|
||||||
Object = 2;
|
Object = 2;
|
||||||
Boolean = 3;
|
Boolean = 3;
|
||||||
}
|
}
|
||||||
Type type = 1;
|
Type type = 1;
|
||||||
string value = 2;
|
string value = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
message NewEvalMessage {
|
message NewEvalMessage {
|
||||||
uint64 id = 1;
|
uint64 id = 1;
|
||||||
string function = 2;
|
string function = 2;
|
||||||
repeated string args = 3;
|
repeated string args = 3;
|
||||||
// Timeout in ms
|
// Timeout in ms
|
||||||
uint32 timeout = 4;
|
uint32 timeout = 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
message EvalFailedMessage {
|
message EvalFailedMessage {
|
||||||
uint64 id = 1;
|
uint64 id = 1;
|
||||||
enum Reason {
|
enum Reason {
|
||||||
Timeout = 0;
|
Timeout = 0;
|
||||||
Exception = 1;
|
Exception = 1;
|
||||||
Conflict = 2;
|
Conflict = 2;
|
||||||
}
|
}
|
||||||
Reason reason = 2;
|
Reason reason = 2;
|
||||||
string message = 3;
|
string message = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
message EvalDoneMessage {
|
message EvalDoneMessage {
|
||||||
uint64 id = 1;
|
uint64 id = 1;
|
||||||
TypedValue response = 2;
|
TypedValue response = 2;
|
||||||
}
|
}
|
||||||
|
132
packages/server/test/command.test.ts
Normal file
132
packages/server/test/command.test.ts
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
import * as path from "path";
|
||||||
|
import { TextEncoder, TextDecoder } from "text-encoding";
|
||||||
|
import { createClient } from "./helpers";
|
||||||
|
|
||||||
|
(<any>global).TextDecoder = TextDecoder;
|
||||||
|
(<any>global).TextEncoder = TextEncoder;
|
||||||
|
|
||||||
|
describe("Command", () => {
|
||||||
|
const client = createClient();
|
||||||
|
|
||||||
|
it("should execute command and return output", (done) => {
|
||||||
|
const proc = client.spawn("echo", ["test"]);
|
||||||
|
proc.stdout.on("data", (data) => {
|
||||||
|
expect(data).toEqual("test\n");
|
||||||
|
});
|
||||||
|
proc.on("exit", (code) => {
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should create shell", (done) => {
|
||||||
|
const proc = client.spawn("/bin/bash", [], {
|
||||||
|
tty: {
|
||||||
|
columns: 100,
|
||||||
|
rows: 10,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
let first = true;
|
||||||
|
proc.stdout.on("data", (data) => {
|
||||||
|
if (first) {
|
||||||
|
// First piece of data is a welcome msg. Second is the prompt
|
||||||
|
first = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
expect(data.toString().endsWith("$ ")).toBeTruthy();
|
||||||
|
proc.kill();
|
||||||
|
});
|
||||||
|
proc.on("exit", () => done());
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should cat", (done) => {
|
||||||
|
const proc = client.spawn("cat", []);
|
||||||
|
expect(proc.pid).toBeUndefined();
|
||||||
|
proc.stdout.on("data", (data) => {
|
||||||
|
expect(data).toEqual("banana");
|
||||||
|
expect(proc.pid).toBeDefined();
|
||||||
|
proc.kill();
|
||||||
|
});
|
||||||
|
proc.on("exit", () => done());
|
||||||
|
proc.send("banana");
|
||||||
|
proc.stdin.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should print env variable", (done) => {
|
||||||
|
const proc = client.spawn("env", [], {
|
||||||
|
env: { hi: "donkey" },
|
||||||
|
});
|
||||||
|
proc.stdout.on("data", (data) => {
|
||||||
|
expect(data).toEqual("hi=donkey\n");
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should resize", (done) => {
|
||||||
|
// Requires the `tput lines` cmd to be available
|
||||||
|
|
||||||
|
const proc = client.spawn("/bin/bash", [], {
|
||||||
|
tty: {
|
||||||
|
columns: 10,
|
||||||
|
rows: 10,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
let output: number = 0; // Number of outputs parsed
|
||||||
|
proc.stdout.on("data", (data) => {
|
||||||
|
output++;
|
||||||
|
|
||||||
|
if (output === 1) {
|
||||||
|
// First is welcome msg
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (output === 2) {
|
||||||
|
proc.send("tput lines\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (output === 3) {
|
||||||
|
// Echo of tput lines
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (output === 4) {
|
||||||
|
expect(data.toString().trim()).toEqual("10");
|
||||||
|
proc.resize!({
|
||||||
|
columns: 10,
|
||||||
|
rows: 50,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (output === 5) {
|
||||||
|
// Primpt
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (output === 6) {
|
||||||
|
proc.send("tput lines\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (output === 7) {
|
||||||
|
// Echo of tput lines
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (output === 8) {
|
||||||
|
expect(data.toString().trim()).toEqual("50");
|
||||||
|
proc.kill();
|
||||||
|
expect(proc.killed).toBeTruthy();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
proc.on("exit", () => done());
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should fork", (done) => {
|
||||||
|
const proc = client.fork(path.join(__dirname, "forker.js"));
|
||||||
|
proc.stdout.on("data", (data) => {
|
||||||
|
expect(data).toEqual("test");
|
||||||
|
});
|
||||||
|
proc.on("exit", () => done());
|
||||||
|
});
|
||||||
|
});
|
1
packages/server/test/forker.js
Executable file
1
packages/server/test/forker.js
Executable file
@ -0,0 +1 @@
|
|||||||
|
console.log("test");
|
@ -63,6 +63,11 @@
|
|||||||
"@types/express-serve-static-core" "*"
|
"@types/express-serve-static-core" "*"
|
||||||
"@types/mime" "*"
|
"@types/mime" "*"
|
||||||
|
|
||||||
|
"@types/text-encoding@^0.0.35":
|
||||||
|
version "0.0.35"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/text-encoding/-/text-encoding-0.0.35.tgz#6f14474e0b232bc70c59677aadc65dcc5a99c3a9"
|
||||||
|
integrity sha512-jfo/A88XIiAweUa8np+1mPbm3h2w0s425YrI8t3wk5QxhH6UI7w517MboNVnGDeMSuoFwA8Rwmklno+FicvV4g==
|
||||||
|
|
||||||
"@types/ws@^6.0.1":
|
"@types/ws@^6.0.1":
|
||||||
version "6.0.1"
|
version "6.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/@types/ws/-/ws-6.0.1.tgz#ca7a3f3756aa12f62a0a62145ed14c6db25d5a28"
|
resolved "https://registry.yarnpkg.com/@types/ws/-/ws-6.0.1.tgz#ca7a3f3756aa12f62a0a62145ed14c6db25d5a28"
|
||||||
@ -295,11 +300,23 @@ ms@2.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
|
resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
|
||||||
integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=
|
integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=
|
||||||
|
|
||||||
|
nan@2.10.0:
|
||||||
|
version "2.10.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/nan/-/nan-2.10.0.tgz#96d0cd610ebd58d4b4de9cc0c6828cda99c7548f"
|
||||||
|
integrity sha512-bAdJv7fBLhWC+/Bls0Oza+mvTaNQtP+1RyhhhvD95pgUJz6XM5IzgmxOkItJ9tkoCiplvAnXI1tNmmUD/eScyA==
|
||||||
|
|
||||||
negotiator@0.6.1:
|
negotiator@0.6.1:
|
||||||
version "0.6.1"
|
version "0.6.1"
|
||||||
resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.1.tgz#2b327184e8992101177b28563fb5e7102acd0ca9"
|
resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.1.tgz#2b327184e8992101177b28563fb5e7102acd0ca9"
|
||||||
integrity sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=
|
integrity sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=
|
||||||
|
|
||||||
|
node-pty@^0.8.0:
|
||||||
|
version "0.8.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/node-pty/-/node-pty-0.8.0.tgz#08bccb633f49e2e3f7245eb56ea6b40f37ccd64f"
|
||||||
|
integrity sha512-g5ggk3gN4gLrDmAllee5ScFyX3YzpOC/U8VJafha4pE7do0TIE1voiIxEbHSRUOPD1xYqmY+uHhOKAd3avbxGQ==
|
||||||
|
dependencies:
|
||||||
|
nan "2.10.0"
|
||||||
|
|
||||||
on-finished@~2.3.0:
|
on-finished@~2.3.0:
|
||||||
version "2.3.0"
|
version "2.3.0"
|
||||||
resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947"
|
resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947"
|
||||||
@ -399,6 +416,11 @@ statuses@~1.4.0:
|
|||||||
resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.4.0.tgz#bb73d446da2796106efcc1b601a253d6c46bd087"
|
resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.4.0.tgz#bb73d446da2796106efcc1b601a253d6c46bd087"
|
||||||
integrity sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==
|
integrity sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==
|
||||||
|
|
||||||
|
text-encoding@^0.7.0:
|
||||||
|
version "0.7.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/text-encoding/-/text-encoding-0.7.0.tgz#f895e836e45990624086601798ea98e8f36ee643"
|
||||||
|
integrity sha512-oJQ3f1hrOnbRLOcwKz0Liq2IcrvDeZRHXhd9RgLrsT+DjWY/nty1Hi7v3dtkaEYbPYe0mUoOfzRrMwfXXwgPUA==
|
||||||
|
|
||||||
ts-protoc-gen@^0.8.0:
|
ts-protoc-gen@^0.8.0:
|
||||||
version "0.8.0"
|
version "0.8.0"
|
||||||
resolved "https://registry.yarnpkg.com/ts-protoc-gen/-/ts-protoc-gen-0.8.0.tgz#2a9a31ee8a4d4760c484f1d0c7199633afaa5e3e"
|
resolved "https://registry.yarnpkg.com/ts-protoc-gen/-/ts-protoc-gen-0.8.0.tgz#2a9a31ee8a4d4760c484f1d0c7199633afaa5e3e"
|
||||||
|
Reference in New Issue
Block a user