@jupyterlite/terminal 0.1.6 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -15
- package/lib/client.d.ts +28 -0
- package/lib/client.js +118 -0
- package/lib/index.d.ts +3 -3
- package/lib/index.js +54 -48
- package/lib/shell.d.ts +2 -0
- package/lib/tokens.d.ts +10 -44
- package/lib/tokens.js +1 -6
- package/lib/worker.js +24 -22
- package/package.json +11 -13
- package/src/client.ts +157 -0
- package/src/index.ts +79 -63
- package/src/shell.ts +4 -0
- package/src/tokens.ts +15 -53
- package/src/worker.ts +26 -38
- package/lib/manager.d.ts +0 -32
- package/lib/manager.js +0 -67
- package/lib/terminal.d.ts +0 -24
- package/lib/terminal.js +0 -101
- package/src/manager.ts +0 -82
- package/src/terminal.ts +0 -131
package/src/client.ts
ADDED
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import { PageConfig, URLExt } from '@jupyterlab/coreutils';
|
|
2
|
+
import { ServerConnection, Terminal } from '@jupyterlab/services';
|
|
3
|
+
import {
|
|
4
|
+
IExternalCommand,
|
|
5
|
+
IShellManager,
|
|
6
|
+
IStdinReply,
|
|
7
|
+
IStdinRequest,
|
|
8
|
+
ShellManager
|
|
9
|
+
} from '@jupyterlite/cockle';
|
|
10
|
+
import { JSONPrimitive } from '@lumino/coreutils';
|
|
11
|
+
|
|
12
|
+
import {
|
|
13
|
+
Server as WebSocketServer,
|
|
14
|
+
Client as WebSocketClient
|
|
15
|
+
} from 'mock-socket';
|
|
16
|
+
|
|
17
|
+
import { Shell } from './shell';
|
|
18
|
+
import { ILiteTerminalAPIClient } from './tokens';
|
|
19
|
+
|
|
20
|
+
export class LiteTerminalAPIClient implements ILiteTerminalAPIClient {
|
|
21
|
+
constructor(options: { serverSettings?: ServerConnection.ISettings } = {}) {
|
|
22
|
+
this.serverSettings =
|
|
23
|
+
options.serverSettings ?? ServerConnection.makeSettings();
|
|
24
|
+
this._shellManager = new ShellManager();
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Set identifier for communicating with service worker.
|
|
29
|
+
*/
|
|
30
|
+
set browsingContextId(browsingContextId: string) {
|
|
31
|
+
console.log('LiteTerminalAPIClient browsingContextId', browsingContextId);
|
|
32
|
+
this._browsingContextId = browsingContextId;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Function that handles stdin requests received from service worker.
|
|
37
|
+
*/
|
|
38
|
+
async handleStdin(request: IStdinRequest): Promise<IStdinReply> {
|
|
39
|
+
return await this._shellManager.handleStdin(request);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
get isAvailable(): boolean {
|
|
43
|
+
const available = String(PageConfig.getOption('terminalsAvailable'));
|
|
44
|
+
return available.toLowerCase() === 'true';
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
readonly serverSettings: ServerConnection.ISettings;
|
|
48
|
+
|
|
49
|
+
async startNew(
|
|
50
|
+
options?: Terminal.ITerminal.IOptions
|
|
51
|
+
): Promise<Terminal.IModel> {
|
|
52
|
+
// Create shell.
|
|
53
|
+
const name = options?.name ?? this._nextAvailableName();
|
|
54
|
+
const { baseUrl, wsUrl } = this.serverSettings;
|
|
55
|
+
const shell = new Shell({
|
|
56
|
+
mountpoint: '/drive',
|
|
57
|
+
baseUrl,
|
|
58
|
+
wasmBaseUrl: URLExt.join(
|
|
59
|
+
baseUrl,
|
|
60
|
+
'extensions/@jupyterlite/terminal/static/wasm/'
|
|
61
|
+
),
|
|
62
|
+
browsingContextId: this._browsingContextId,
|
|
63
|
+
shellId: name,
|
|
64
|
+
shellManager: this._shellManager,
|
|
65
|
+
outputCallback: text => {
|
|
66
|
+
const msg = JSON.stringify(['stdout', text]);
|
|
67
|
+
shell.socket?.send(msg);
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
this._shells.set(name, shell);
|
|
71
|
+
|
|
72
|
+
for (const externalCommand of this._externalCommands) {
|
|
73
|
+
shell.registerExternalCommand(externalCommand);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Hook to connect socket to shell.
|
|
77
|
+
const hook = async (
|
|
78
|
+
shell: Shell,
|
|
79
|
+
socket: WebSocketClient
|
|
80
|
+
): Promise<void> => {
|
|
81
|
+
shell.socket = socket;
|
|
82
|
+
|
|
83
|
+
socket.on('message', async (message: any) => {
|
|
84
|
+
// Message from xtermjs to pass to shell.
|
|
85
|
+
const data = JSON.parse(message) as JSONPrimitive[];
|
|
86
|
+
const message_type = data[0];
|
|
87
|
+
const content = data.slice(1);
|
|
88
|
+
await shell.ready;
|
|
89
|
+
if (message_type === 'stdin') {
|
|
90
|
+
await shell.input(content[0] as string);
|
|
91
|
+
} else if (message_type === 'set_size') {
|
|
92
|
+
const rows = content[0] as number;
|
|
93
|
+
const columns = content[1] as number;
|
|
94
|
+
await shell.setSize(rows, columns);
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
// Return handshake.
|
|
99
|
+
const res = JSON.stringify(['setup']);
|
|
100
|
+
console.log('Terminal returning handshake via socket');
|
|
101
|
+
socket.send(res);
|
|
102
|
+
|
|
103
|
+
shell.start();
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
const url = URLExt.join(wsUrl, 'terminals', 'websocket', name);
|
|
107
|
+
const wsServer = new WebSocketServer(url);
|
|
108
|
+
wsServer.on('connection', (socket: WebSocketClient): void => {
|
|
109
|
+
hook(shell, socket);
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
shell.disposed.connect(() => {
|
|
113
|
+
this.shutdown(name);
|
|
114
|
+
wsServer.close();
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
return { name };
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
async listRunning(): Promise<Terminal.IModel[]> {
|
|
121
|
+
return this._models;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
registerExternalCommand(options: IExternalCommand.IOptions): void {
|
|
125
|
+
this._externalCommands.push(options);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
async shutdown(name: string): Promise<void> {
|
|
129
|
+
const shell = this._shells.get(name);
|
|
130
|
+
if (shell !== undefined) {
|
|
131
|
+
shell.socket?.send(JSON.stringify(['disconnect']));
|
|
132
|
+
shell.socket?.close();
|
|
133
|
+
this._shells.delete(name);
|
|
134
|
+
shell.dispose();
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
private get _models(): Terminal.IModel[] {
|
|
139
|
+
return Array.from(this._shells.keys(), name => {
|
|
140
|
+
return { name };
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
private _nextAvailableName(): string {
|
|
145
|
+
for (let i = 1; ; ++i) {
|
|
146
|
+
const name = `${i}`;
|
|
147
|
+
if (!this._shells.has(name)) {
|
|
148
|
+
return name;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
private _browsingContextId?: string;
|
|
154
|
+
private _externalCommands: IExternalCommand.IOptions[] = [];
|
|
155
|
+
private _shellManager: IShellManager;
|
|
156
|
+
private _shells = new Map<string, Shell>();
|
|
157
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -2,84 +2,100 @@
|
|
|
2
2
|
// Distributed under the terms of the Modified BSD License.
|
|
3
3
|
|
|
4
4
|
import {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
5
|
+
JupyterFrontEnd,
|
|
6
|
+
JupyterFrontEndPlugin
|
|
7
|
+
} from '@jupyterlab/application';
|
|
8
|
+
import {
|
|
9
|
+
ITerminalManager,
|
|
10
|
+
ServiceManagerPlugin,
|
|
11
|
+
Terminal,
|
|
12
|
+
ServerConnection,
|
|
13
|
+
IServerSettings,
|
|
14
|
+
TerminalManager
|
|
15
|
+
} from '@jupyterlab/services';
|
|
16
|
+
import { IServiceWorkerManager } from '@jupyterlite/server';
|
|
17
|
+
|
|
18
|
+
import { WebSocket } from 'mock-socket';
|
|
19
|
+
|
|
20
|
+
import { LiteTerminalAPIClient } from './client';
|
|
21
|
+
import { ILiteTerminalAPIClient } from './tokens';
|
|
9
22
|
|
|
10
|
-
|
|
11
|
-
|
|
23
|
+
/**
|
|
24
|
+
* Plugin containing client for in-browser terminals.
|
|
25
|
+
*/
|
|
26
|
+
const terminalClientPlugin: ServiceManagerPlugin<Terminal.ITerminalAPIClient> =
|
|
27
|
+
{
|
|
28
|
+
id: '@jupyterlite/terminal:client',
|
|
29
|
+
description: 'The client for Lite terminals',
|
|
30
|
+
autoStart: true,
|
|
31
|
+
provides: ILiteTerminalAPIClient,
|
|
32
|
+
optional: [IServerSettings],
|
|
33
|
+
activate: (
|
|
34
|
+
_: null,
|
|
35
|
+
serverSettings?: ServerConnection.ISettings
|
|
36
|
+
): ILiteTerminalAPIClient => {
|
|
37
|
+
return new LiteTerminalAPIClient({
|
|
38
|
+
serverSettings: {
|
|
39
|
+
...ServerConnection.makeSettings(),
|
|
40
|
+
...serverSettings,
|
|
41
|
+
WebSocket
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
};
|
|
12
46
|
|
|
13
47
|
/**
|
|
14
|
-
*
|
|
48
|
+
* Plugin containing manager for in-browser terminals.
|
|
15
49
|
*/
|
|
16
|
-
const
|
|
17
|
-
id: '@jupyterlite/terminal:
|
|
18
|
-
description: 'A terminal
|
|
50
|
+
const terminalManagerPlugin: ServiceManagerPlugin<Terminal.IManager> = {
|
|
51
|
+
id: '@jupyterlite/terminal:manager',
|
|
52
|
+
description: 'A JupyterLite extension providing a custom terminal manager',
|
|
19
53
|
autoStart: true,
|
|
20
54
|
provides: ITerminalManager,
|
|
21
|
-
|
|
55
|
+
requires: [ILiteTerminalAPIClient],
|
|
56
|
+
activate: (
|
|
57
|
+
_: null,
|
|
58
|
+
terminalAPIClient: Terminal.ITerminalAPIClient
|
|
59
|
+
): Terminal.IManager => {
|
|
22
60
|
console.log(
|
|
23
|
-
'JupyterLite extension @jupyterlite/terminal:
|
|
61
|
+
'JupyterLite extension @jupyterlite/terminal:manager activated'
|
|
24
62
|
);
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
console.log('terminals ready:', terminals.isReady); // Not ready
|
|
30
|
-
console.log('terminals active:', terminals.isActive);
|
|
31
|
-
|
|
32
|
-
// Not sure this is necessary?
|
|
33
|
-
await terminals.ready;
|
|
34
|
-
console.log('terminals ready after await:', terminals.isReady); // Ready
|
|
35
|
-
|
|
36
|
-
return new TerminalManager(serverSettings.wsUrl);
|
|
63
|
+
return new TerminalManager({
|
|
64
|
+
terminalAPIClient,
|
|
65
|
+
serverSettings: terminalAPIClient.serverSettings
|
|
66
|
+
});
|
|
37
67
|
}
|
|
38
68
|
};
|
|
39
69
|
|
|
40
70
|
/**
|
|
41
|
-
*
|
|
71
|
+
* Plugin that connects in-browser terminals and service worker.
|
|
42
72
|
*/
|
|
43
|
-
const
|
|
44
|
-
id: '@jupyterlite/terminal:
|
|
73
|
+
const terminalServiceWorkerPlugin: JupyterFrontEndPlugin<void> = {
|
|
74
|
+
id: '@jupyterlite/terminal:service-worker',
|
|
45
75
|
autoStart: true,
|
|
46
|
-
requires: [
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
76
|
+
requires: [ILiteTerminalAPIClient],
|
|
77
|
+
optional: [IServiceWorkerManager],
|
|
78
|
+
activate: (
|
|
79
|
+
_: JupyterFrontEnd,
|
|
80
|
+
liteTerminalAPIClient: ILiteTerminalAPIClient,
|
|
81
|
+
serviceWorkerManager?: IServiceWorkerManager
|
|
82
|
+
): void => {
|
|
83
|
+
if (serviceWorkerManager !== undefined) {
|
|
84
|
+
liteTerminalAPIClient.browsingContextId =
|
|
85
|
+
serviceWorkerManager.browsingContextId;
|
|
52
86
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
// POST /api/terminals - Start a terminal
|
|
61
|
-
app.router.post('/api/terminals', async (req: Router.IRequest) => {
|
|
62
|
-
const res = await terminalManager.startNew();
|
|
63
|
-
// Should return last_activity too.
|
|
64
|
-
return new Response(JSON.stringify(res));
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
// DELETE /api/terminals/{terminal name} - Delete a terminal
|
|
68
|
-
app.router.delete(
|
|
69
|
-
'/api/terminals/(.+)',
|
|
70
|
-
async (req: Router.IRequest, name: string) => {
|
|
71
|
-
const exists = terminalManager.has(name);
|
|
72
|
-
if (exists) {
|
|
73
|
-
await terminalManager.shutdownTerminal(name);
|
|
74
|
-
} else {
|
|
75
|
-
const msg = `The terminal session "${name}"" does not exist`;
|
|
76
|
-
console.warn(msg);
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
return new Response(null, { status: exists ? 204 : 404 });
|
|
80
|
-
}
|
|
81
|
-
);
|
|
87
|
+
serviceWorkerManager.registerStdinHandler(
|
|
88
|
+
'terminal',
|
|
89
|
+
liteTerminalAPIClient.handleStdin.bind(liteTerminalAPIClient)
|
|
90
|
+
);
|
|
91
|
+
} else {
|
|
92
|
+
console.warn('Service worker is not available for terminals');
|
|
93
|
+
}
|
|
82
94
|
}
|
|
83
95
|
};
|
|
84
96
|
|
|
85
|
-
export default [
|
|
97
|
+
export default [
|
|
98
|
+
terminalClientPlugin,
|
|
99
|
+
terminalManagerPlugin,
|
|
100
|
+
terminalServiceWorkerPlugin
|
|
101
|
+
];
|
package/src/shell.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { BaseShell, IShell } from '@jupyterlite/cockle';
|
|
2
2
|
|
|
3
|
+
import { Client as WebSocketClient } from 'mock-socket';
|
|
4
|
+
|
|
3
5
|
/**
|
|
4
6
|
* Shell class that uses web worker that plugs into a DriveFS via the service worker.
|
|
5
7
|
*/
|
|
@@ -22,4 +24,6 @@ export class Shell extends BaseShell {
|
|
|
22
24
|
type: 'module'
|
|
23
25
|
});
|
|
24
26
|
}
|
|
27
|
+
|
|
28
|
+
socket?: WebSocketClient;
|
|
25
29
|
}
|
package/src/tokens.ts
CHANGED
|
@@ -1,66 +1,28 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
import { Terminal } from '@jupyterlab/services';
|
|
2
|
+
import {
|
|
3
|
+
IExternalCommand,
|
|
4
|
+
IStdinReply,
|
|
5
|
+
IStdinRequest
|
|
6
|
+
} from '@jupyterlite/cockle';
|
|
6
7
|
import { Token } from '@lumino/coreutils';
|
|
7
|
-
import { IObservableDisposable } from '@lumino/disposable';
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
*/
|
|
12
|
-
export const ITerminalManager = new Token<ITerminalManager>(
|
|
13
|
-
'@jupyterlite/terminal:ITerminalManager'
|
|
9
|
+
export const ILiteTerminalAPIClient = new Token<ILiteTerminalAPIClient>(
|
|
10
|
+
'@jupyterlite/terminal:client'
|
|
14
11
|
);
|
|
15
12
|
|
|
16
|
-
|
|
17
|
-
* An interface for the TerminalManager service.
|
|
18
|
-
*/
|
|
19
|
-
export interface ITerminalManager {
|
|
20
|
-
/**
|
|
21
|
-
* Return whether the named terminal exists.
|
|
22
|
-
*/
|
|
23
|
-
has(name: string): boolean;
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* List the running terminals.
|
|
27
|
-
*/
|
|
28
|
-
listRunning: () => Promise<TerminalAPI.IModel[]>;
|
|
29
|
-
|
|
13
|
+
export interface ILiteTerminalAPIClient extends Terminal.ITerminalAPIClient {
|
|
30
14
|
/**
|
|
31
|
-
*
|
|
15
|
+
* Identifier for communicating with service worker.
|
|
32
16
|
*/
|
|
33
|
-
|
|
17
|
+
browsingContextId: string;
|
|
34
18
|
|
|
35
19
|
/**
|
|
36
|
-
*
|
|
20
|
+
* Function that handles stdin requests received from service worker.
|
|
37
21
|
*/
|
|
38
|
-
|
|
39
|
-
}
|
|
22
|
+
handleStdin(request: IStdinRequest): Promise<IStdinReply>;
|
|
40
23
|
|
|
41
|
-
/**
|
|
42
|
-
* An interface for a server-side terminal running in the browser.
|
|
43
|
-
*/
|
|
44
|
-
export interface ITerminal extends IObservableDisposable {
|
|
45
24
|
/**
|
|
46
|
-
*
|
|
25
|
+
* Register an external command that will be available in all terminals.
|
|
47
26
|
*/
|
|
48
|
-
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* A namespace for ITerminal statics.
|
|
53
|
-
*/
|
|
54
|
-
export namespace ITerminal {
|
|
55
|
-
/**
|
|
56
|
-
* The instantiation options for an ITerminal.
|
|
57
|
-
*/
|
|
58
|
-
export interface IOptions {
|
|
59
|
-
/**
|
|
60
|
-
* The name of the terminal.
|
|
61
|
-
*/
|
|
62
|
-
name: string;
|
|
63
|
-
|
|
64
|
-
baseUrl: string;
|
|
65
|
-
}
|
|
27
|
+
registerExternalCommand(options: IExternalCommand.IOptions): void;
|
|
66
28
|
}
|
package/src/worker.ts
CHANGED
|
@@ -1,26 +1,7 @@
|
|
|
1
1
|
import { expose } from 'comlink';
|
|
2
2
|
|
|
3
|
-
import { BaseShellWorker,
|
|
4
|
-
import {
|
|
5
|
-
ContentsAPI,
|
|
6
|
-
DriveFS,
|
|
7
|
-
ServiceWorkerContentsAPI
|
|
8
|
-
} from '@jupyterlite/contents';
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Custom DriveFS implementation using the service worker.
|
|
12
|
-
*/
|
|
13
|
-
class MyDriveFS extends DriveFS {
|
|
14
|
-
createAPI(options: DriveFS.IOptions): ContentsAPI {
|
|
15
|
-
return new ServiceWorkerContentsAPI(
|
|
16
|
-
options.baseUrl,
|
|
17
|
-
options.driveName,
|
|
18
|
-
options.mountpoint,
|
|
19
|
-
options.FS,
|
|
20
|
-
options.ERRNO_CODES
|
|
21
|
-
);
|
|
22
|
-
}
|
|
23
|
-
}
|
|
3
|
+
import { BaseShellWorker, IDriveFSOptions } from '@jupyterlite/cockle';
|
|
4
|
+
import { DriveFS } from '@jupyterlite/contents';
|
|
24
5
|
|
|
25
6
|
/**
|
|
26
7
|
* Shell web worker that uses DriveFS via service worker.
|
|
@@ -28,24 +9,31 @@ class MyDriveFS extends DriveFS {
|
|
|
28
9
|
*/
|
|
29
10
|
class ShellWorker extends BaseShellWorker {
|
|
30
11
|
/**
|
|
31
|
-
* Initialize the DriveFS to mount an external file system.
|
|
12
|
+
* Initialize the DriveFS to mount an external file system, if available.
|
|
32
13
|
*/
|
|
33
|
-
protected override initDriveFS(
|
|
34
|
-
|
|
35
|
-
mountpoint
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
FS,
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
14
|
+
protected override initDriveFS(options: IDriveFSOptions): void {
|
|
15
|
+
const { baseUrl, browsingContextId, fileSystem, mountpoint } = options;
|
|
16
|
+
console.log('Terminal initDriveFS', baseUrl, mountpoint, browsingContextId);
|
|
17
|
+
if (
|
|
18
|
+
mountpoint !== '' &&
|
|
19
|
+
baseUrl !== undefined &&
|
|
20
|
+
browsingContextId !== undefined
|
|
21
|
+
) {
|
|
22
|
+
const { FS, ERRNO_CODES, PATH } = fileSystem;
|
|
23
|
+
const driveFS = new DriveFS({
|
|
24
|
+
FS,
|
|
25
|
+
PATH,
|
|
26
|
+
ERRNO_CODES,
|
|
27
|
+
baseUrl,
|
|
28
|
+
driveName: '',
|
|
29
|
+
mountpoint,
|
|
30
|
+
browsingContextId
|
|
31
|
+
});
|
|
32
|
+
FS.mount(driveFS, {}, mountpoint);
|
|
33
|
+
console.log('Terminal connected to shared drive');
|
|
34
|
+
} else {
|
|
35
|
+
console.warn('Terminal not connected to shared drive');
|
|
36
|
+
}
|
|
49
37
|
}
|
|
50
38
|
}
|
|
51
39
|
|
package/lib/manager.d.ts
DELETED
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
import { TerminalAPI } from '@jupyterlab/services';
|
|
2
|
-
import { ITerminalManager } from './tokens';
|
|
3
|
-
/**
|
|
4
|
-
* A class to handle requests to /api/terminals.
|
|
5
|
-
* Although this looks similar to a JupyterLab TerminalManager, it is really a class that
|
|
6
|
-
* implements the terminal REST API.
|
|
7
|
-
*/
|
|
8
|
-
export declare class TerminalManager implements ITerminalManager {
|
|
9
|
-
/**
|
|
10
|
-
* Construct a new TerminalManager object.
|
|
11
|
-
*/
|
|
12
|
-
constructor(wsUrl: string);
|
|
13
|
-
/**
|
|
14
|
-
* Return whether the named terminal exists.
|
|
15
|
-
*/
|
|
16
|
-
has(name: string): boolean;
|
|
17
|
-
/**
|
|
18
|
-
* List the running terminals.
|
|
19
|
-
*/
|
|
20
|
-
listRunning(): Promise<TerminalAPI.IModel[]>;
|
|
21
|
-
/**
|
|
22
|
-
* Shutdown a terminal by name.
|
|
23
|
-
*/
|
|
24
|
-
shutdownTerminal(name: string): Promise<void>;
|
|
25
|
-
/**
|
|
26
|
-
* Start a new kernel.
|
|
27
|
-
*/
|
|
28
|
-
startNew(): Promise<TerminalAPI.IModel>;
|
|
29
|
-
private _nextAvailableName;
|
|
30
|
-
private _wsUrl;
|
|
31
|
-
private _terminals;
|
|
32
|
-
}
|
package/lib/manager.js
DELETED
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
// Copyright (c) Jupyter Development Team.
|
|
2
|
-
// Distributed under the terms of the Modified BSD License.
|
|
3
|
-
import { PageConfig } from '@jupyterlab/coreutils';
|
|
4
|
-
import { Terminal } from './terminal';
|
|
5
|
-
/**
|
|
6
|
-
* A class to handle requests to /api/terminals.
|
|
7
|
-
* Although this looks similar to a JupyterLab TerminalManager, it is really a class that
|
|
8
|
-
* implements the terminal REST API.
|
|
9
|
-
*/
|
|
10
|
-
export class TerminalManager {
|
|
11
|
-
/**
|
|
12
|
-
* Construct a new TerminalManager object.
|
|
13
|
-
*/
|
|
14
|
-
constructor(wsUrl) {
|
|
15
|
-
this._terminals = new Map();
|
|
16
|
-
this._wsUrl = wsUrl;
|
|
17
|
-
console.log('==> TerminalManager.constructor', this._wsUrl);
|
|
18
|
-
}
|
|
19
|
-
/**
|
|
20
|
-
* Return whether the named terminal exists.
|
|
21
|
-
*/
|
|
22
|
-
has(name) {
|
|
23
|
-
return this._terminals.has(name);
|
|
24
|
-
}
|
|
25
|
-
/**
|
|
26
|
-
* List the running terminals.
|
|
27
|
-
*/
|
|
28
|
-
async listRunning() {
|
|
29
|
-
const ret = [...this._terminals.values()].map(terminal => ({
|
|
30
|
-
name: terminal.name
|
|
31
|
-
}));
|
|
32
|
-
return ret;
|
|
33
|
-
}
|
|
34
|
-
/**
|
|
35
|
-
* Shutdown a terminal by name.
|
|
36
|
-
*/
|
|
37
|
-
async shutdownTerminal(name) {
|
|
38
|
-
const terminal = this._terminals.get(name);
|
|
39
|
-
if (terminal !== undefined) {
|
|
40
|
-
console.log('==> TerminalManager.shutdownTerminal', name);
|
|
41
|
-
this._terminals.delete(name);
|
|
42
|
-
terminal.dispose();
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
/**
|
|
46
|
-
* Start a new kernel.
|
|
47
|
-
*/
|
|
48
|
-
async startNew() {
|
|
49
|
-
const name = this._nextAvailableName();
|
|
50
|
-
console.log('==> TerminalManager.startNew', name);
|
|
51
|
-
const baseUrl = PageConfig.getBaseUrl();
|
|
52
|
-
const terminal = new Terminal({ name, baseUrl });
|
|
53
|
-
this._terminals.set(name, terminal);
|
|
54
|
-
terminal.disposed.connect(() => this.shutdownTerminal(name));
|
|
55
|
-
const url = `${this._wsUrl}terminals/websocket/${name}`;
|
|
56
|
-
await terminal.wsConnect(url);
|
|
57
|
-
return { name };
|
|
58
|
-
}
|
|
59
|
-
_nextAvailableName() {
|
|
60
|
-
for (let i = 1;; ++i) {
|
|
61
|
-
const name = `${i}`;
|
|
62
|
-
if (!this._terminals.has(name)) {
|
|
63
|
-
return name;
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
}
|
package/lib/terminal.d.ts
DELETED
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import { ISignal } from '@lumino/signaling';
|
|
2
|
-
import { ITerminal } from './tokens';
|
|
3
|
-
export declare class Terminal implements ITerminal {
|
|
4
|
-
readonly options: ITerminal.IOptions;
|
|
5
|
-
/**
|
|
6
|
-
* Construct a new Terminal.
|
|
7
|
-
*/
|
|
8
|
-
constructor(options: ITerminal.IOptions);
|
|
9
|
-
private _outputCallback;
|
|
10
|
-
dispose(): void;
|
|
11
|
-
get disposed(): ISignal<this, void>;
|
|
12
|
-
get isDisposed(): boolean;
|
|
13
|
-
/**
|
|
14
|
-
* Get the name of the terminal.
|
|
15
|
-
*/
|
|
16
|
-
get name(): string;
|
|
17
|
-
wsConnect(url: string): Promise<void>;
|
|
18
|
-
private _disposed;
|
|
19
|
-
private _isDisposed;
|
|
20
|
-
private _server?;
|
|
21
|
-
private _socket?;
|
|
22
|
-
private _shell;
|
|
23
|
-
private _running;
|
|
24
|
-
}
|