CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
sagemathinc

Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.

GitHub Repository: sagemathinc/cocalc
Path: blob/master/src/packages/terminal/index.ts
Views: 687
1
/*
2
* This file is part of CoCalc: Copyright © 2020 Sagemath, Inc.
3
* License: MS-RSL – see LICENSE.md for details
4
*/
5
6
/*
7
Terminal server
8
*/
9
10
import { getLogger } from "@cocalc/backend/logger";
11
import type { Options, PrimusChannel, PrimusWithChannels } from "./lib/types";
12
export { PrimusChannel, PrimusWithChannels };
13
import { getChannelName, getRemotePtyChannelName } from "./lib/util";
14
import { Terminal } from "./lib/terminal";
15
export { RemoteTerminal } from "./lib/remote-terminal";
16
export { getRemotePtyChannelName };
17
import { EventEmitter } from "events";
18
19
const logger = getLogger("terminal:index");
20
21
const terminals: { [name: string]: Terminal } = {};
22
23
class Terminals extends EventEmitter {
24
private paths: { [path: string]: true } = {};
25
26
getOpenPaths = (): string[] => Object.keys(this.paths);
27
28
isOpen = (path) => this.paths[path] != null;
29
30
add = (path: string) => {
31
this.emit("open", path);
32
this.paths[path] = true;
33
};
34
35
// not used YET:
36
close = (path: string) => {
37
this.emit("close", path);
38
delete this.paths[path];
39
};
40
}
41
42
export const terminalTracker = new Terminals();
43
44
// this is used to know which path belongs to which terminal
45
// (this is the overall tab, not the individual frame -- it's
46
// used for the processes page)
47
export function pidToPath(pid: number): string | undefined {
48
for (const terminal of Object.values(terminals)) {
49
if (terminal.getPid() == pid) {
50
return terminal.getPath();
51
}
52
}
53
}
54
55
// INPUT: primus and description of a terminal session (the path)
56
// OUTPUT: the name of a websocket channel that serves that terminal session.
57
export async function terminal(
58
primus: PrimusWithChannels,
59
path: string,
60
options: Options,
61
): Promise<string> {
62
const name = getChannelName(path);
63
if (terminals[name] != null) {
64
if (
65
options.command != null &&
66
options.command != terminals[name].getCommand()
67
) {
68
logger.debug(
69
"changing command/args for existing terminal and restarting",
70
path,
71
);
72
terminals[name].setCommand(options.command, options.args);
73
}
74
return name;
75
}
76
77
logger.debug("creating terminal for ", { path });
78
const terminal = new Terminal(primus, path, options);
79
terminals[name] = terminal;
80
await terminal.init();
81
terminalTracker.add(path);
82
83
return name;
84
}
85
86