@jupyterlite/terminal 0.2.0-a0 → 0.2.1

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/src/index.ts CHANGED
@@ -8,52 +8,97 @@ import {
8
8
  import {
9
9
  ITerminalManager,
10
10
  ServiceManagerPlugin,
11
- Terminal
11
+ Terminal,
12
+ ServerConnection,
13
+ IServerSettings,
14
+ TerminalManager
12
15
  } from '@jupyterlab/services';
13
16
  import { IServiceWorkerManager } from '@jupyterlite/server';
14
17
 
15
- import { isILiteTerminalManager, LiteTerminalManager } from './manager';
18
+ import { WebSocket } from 'mock-socket';
19
+
20
+ import { LiteTerminalAPIClient } from './client';
21
+ import { ILiteTerminalAPIClient } from './tokens';
22
+
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
+ };
16
46
 
17
47
  /**
18
- * The terminal manager plugin, replacing the JupyterLab terminal manager.
48
+ * Plugin containing manager for in-browser terminals.
19
49
  */
20
50
  const terminalManagerPlugin: ServiceManagerPlugin<Terminal.IManager> = {
21
- id: '@jupyterlite/terminal:plugin',
51
+ id: '@jupyterlite/terminal:manager',
22
52
  description: 'A JupyterLite extension providing a custom terminal manager',
23
53
  autoStart: true,
24
54
  provides: ITerminalManager,
25
- activate: (_: null): Terminal.IManager => {
55
+ requires: [ILiteTerminalAPIClient],
56
+ activate: (
57
+ _: null,
58
+ terminalAPIClient: Terminal.ITerminalAPIClient
59
+ ): Terminal.IManager => {
26
60
  console.log(
27
- 'JupyterLite extension @jupyterlite/terminal:plugin is activated!'
61
+ 'JupyterLite extension @jupyterlite/terminal:manager activated'
28
62
  );
29
- return new LiteTerminalManager();
63
+ return new TerminalManager({
64
+ terminalAPIClient,
65
+ serverSettings: terminalAPIClient.serverSettings
66
+ });
30
67
  }
31
68
  };
32
69
 
33
70
  /**
34
- * A plugin that sets the browsingContextId of the terminal manager.
71
+ * Plugin that connects in-browser terminals and service worker.
35
72
  */
36
- const browsingContextIdSetter: JupyterFrontEndPlugin<void> = {
37
- id: '@jupyterlite/terminal:browsing-context-id',
73
+ const terminalServiceWorkerPlugin: JupyterFrontEndPlugin<void> = {
74
+ id: '@jupyterlite/terminal:service-worker',
38
75
  autoStart: true,
76
+ requires: [ILiteTerminalAPIClient],
39
77
  optional: [IServiceWorkerManager],
40
- requires: [ITerminalManager],
41
78
  activate: (
42
79
  _: JupyterFrontEnd,
43
- terminalManager: Terminal.IManager,
80
+ liteTerminalAPIClient: ILiteTerminalAPIClient,
44
81
  serviceWorkerManager?: IServiceWorkerManager
45
82
  ): void => {
46
83
  if (serviceWorkerManager !== undefined) {
47
- if (isILiteTerminalManager(terminalManager)) {
48
- const { browsingContextId } = serviceWorkerManager;
49
- terminalManager.browsingContextId = browsingContextId;
50
- } else {
51
- console.warn(
52
- 'Terminal manager does not support setting browsingContextId'
53
- );
54
- }
84
+ liteTerminalAPIClient.browsingContextId =
85
+ serviceWorkerManager.browsingContextId;
86
+
87
+ serviceWorkerManager.registerStdinHandler(
88
+ 'terminal',
89
+ liteTerminalAPIClient.handleStdin.bind(liteTerminalAPIClient)
90
+ );
91
+ } else {
92
+ console.warn('Service worker is not available for terminals');
55
93
  }
56
94
  }
57
95
  };
58
96
 
59
- export default [terminalManagerPlugin, browsingContextIdSetter];
97
+ export default [
98
+ terminalClientPlugin,
99
+ terminalManagerPlugin,
100
+ terminalServiceWorkerPlugin
101
+ ];
102
+
103
+ // Export ILiteTerminalAPIClient so that other extensions can register external commands.
104
+ export { ILiteTerminalAPIClient };
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 ADDED
@@ -0,0 +1,28 @@
1
+ import { Terminal } from '@jupyterlab/services';
2
+ import {
3
+ IExternalCommand,
4
+ IStdinReply,
5
+ IStdinRequest
6
+ } from '@jupyterlite/cockle';
7
+ import { Token } from '@lumino/coreutils';
8
+
9
+ export const ILiteTerminalAPIClient = new Token<ILiteTerminalAPIClient>(
10
+ '@jupyterlite/terminal:client'
11
+ );
12
+
13
+ export interface ILiteTerminalAPIClient extends Terminal.ITerminalAPIClient {
14
+ /**
15
+ * Identifier for communicating with service worker.
16
+ */
17
+ browsingContextId: string;
18
+
19
+ /**
20
+ * Function that handles stdin requests received from service worker.
21
+ */
22
+ handleStdin(request: IStdinRequest): Promise<IStdinReply>;
23
+
24
+ /**
25
+ * Register an external command that will be available in all terminals.
26
+ */
27
+ registerExternalCommand(options: IExternalCommand.IOptions): void;
28
+ }
package/src/worker.ts CHANGED
@@ -12,17 +12,11 @@ class ShellWorker extends BaseShellWorker {
12
12
  * Initialize the DriveFS to mount an external file system, if available.
13
13
  */
14
14
  protected override initDriveFS(options: IDriveFSOptions): void {
15
- const { browsingContextId, driveFsBaseUrl, fileSystem, mountpoint } =
16
- options;
17
- console.log(
18
- 'Terminal initDriveFS',
19
- driveFsBaseUrl,
20
- mountpoint,
21
- browsingContextId
22
- );
15
+ const { baseUrl, browsingContextId, fileSystem, mountpoint } = options;
16
+ console.log('Terminal initDriveFS', baseUrl, mountpoint, browsingContextId);
23
17
  if (
24
18
  mountpoint !== '' &&
25
- driveFsBaseUrl !== undefined &&
19
+ baseUrl !== undefined &&
26
20
  browsingContextId !== undefined
27
21
  ) {
28
22
  const { FS, ERRNO_CODES, PATH } = fileSystem;
@@ -30,7 +24,7 @@ class ShellWorker extends BaseShellWorker {
30
24
  FS,
31
25
  PATH,
32
26
  ERRNO_CODES,
33
- baseUrl: driveFsBaseUrl,
27
+ baseUrl,
34
28
  driveName: '',
35
29
  mountpoint,
36
30
  browsingContextId
package/lib/manager.d.ts DELETED
@@ -1,93 +0,0 @@
1
- import { BaseManager, Terminal, TerminalManager } from '@jupyterlab/services';
2
- import { ISignal } from '@lumino/signaling';
3
- /**
4
- * Interface for Lite terminal manager, supports setting browserContextId.
5
- */
6
- interface ILiteTerminalManager extends Terminal.IManager {
7
- browsingContextId: string;
8
- }
9
- /**
10
- * Type guard for ILiteTerminalManager.
11
- */
12
- export declare function isILiteTerminalManager(obj: Terminal.IManager): obj is ILiteTerminalManager;
13
- /**
14
- * A terminal session manager.
15
- */
16
- export declare class LiteTerminalManager extends BaseManager implements ILiteTerminalManager {
17
- /**
18
- * Construct a new terminal manager.
19
- */
20
- constructor(options?: TerminalManager.IOptions);
21
- /**
22
- * Set identifier for communicating with service worker.
23
- */
24
- set browsingContextId(browsingContextId: string);
25
- /**
26
- * A signal emitted when there is a connection failure.
27
- */
28
- get connectionFailure(): ISignal<this, Error>;
29
- connectTo(options: Omit<Terminal.ITerminalConnection.IOptions, 'serverSettings'>): Terminal.ITerminalConnection;
30
- /**
31
- * Whether the terminal service is available.
32
- */
33
- isAvailable(): boolean;
34
- /**
35
- * Test whether the manager is ready.
36
- */
37
- get isReady(): boolean;
38
- /**
39
- * A promise that fulfills when the manager is ready.
40
- */
41
- get ready(): Promise<void>;
42
- /**
43
- * Force a refresh of the running terminals.
44
- *
45
- * @returns A promise that with the list of running terminals.
46
- *
47
- * #### Notes
48
- * This is intended to be called only in response to a user action,
49
- * since the manager maintains its internal state.
50
- */
51
- refreshRunning(): Promise<void>;
52
- /**
53
- * Create an iterator over the most recent running terminals.
54
- *
55
- * @returns A new iterator over the running terminals.
56
- */
57
- running(): IterableIterator<Terminal.IModel>;
58
- /**
59
- * A signal emitted when the running terminals change.
60
- */
61
- get runningChanged(): ISignal<this, Terminal.IModel[]>;
62
- /**
63
- * Shut down a terminal session by name.
64
- */
65
- shutdown(name: string): Promise<void>;
66
- /**
67
- * Shut down all terminal sessions.
68
- *
69
- * @returns A promise that resolves when all of the sessions are shut down.
70
- */
71
- shutdownAll(): Promise<void>;
72
- /**
73
- * Create a new terminal session.
74
- *
75
- * @param options - The options used to create the terminal.
76
- *
77
- * @returns A promise that resolves with the terminal connection instance.
78
- *
79
- * #### Notes
80
- * The manager `serverSettings` will be used unless overridden in the
81
- * options.
82
- */
83
- startNew(options: Terminal.ITerminal.IOptions): Promise<Terminal.ITerminalConnection>;
84
- private get _models();
85
- private _nextAvailableName;
86
- private _browsingContextId?;
87
- private _connectionFailure;
88
- private _isReady;
89
- private _ready;
90
- private _runningChanged;
91
- private _terminalConnections;
92
- }
93
- export {};
package/lib/manager.js DELETED
@@ -1,167 +0,0 @@
1
- import { BaseManager } from '@jupyterlab/services';
2
- import { Signal } from '@lumino/signaling';
3
- import { LiteTerminalConnection } from './terminal';
4
- /**
5
- * Type guard for ILiteTerminalManager.
6
- */
7
- export function isILiteTerminalManager(obj) {
8
- return 'browsingContextId' in obj;
9
- }
10
- /**
11
- * A terminal session manager.
12
- */
13
- export class LiteTerminalManager extends BaseManager {
14
- /**
15
- * Construct a new terminal manager.
16
- */
17
- constructor(options = {}) {
18
- super(options);
19
- this._connectionFailure = new Signal(this);
20
- this._isReady = false;
21
- this._runningChanged = new Signal(this);
22
- this._terminalConnections = new Map();
23
- // Initialize internal data.
24
- this._ready = (async () => {
25
- this._isReady = true;
26
- })();
27
- }
28
- /**
29
- * Set identifier for communicating with service worker.
30
- */
31
- set browsingContextId(browsingContextId) {
32
- console.log('==> LiteTerminalManager browsingContextId', browsingContextId);
33
- this._browsingContextId = browsingContextId;
34
- }
35
- /**
36
- * A signal emitted when there is a connection failure.
37
- */
38
- get connectionFailure() {
39
- return this._connectionFailure;
40
- }
41
- /*
42
- * Connect to a running terminal.
43
- *
44
- * @param options - The options used to connect to the terminal.
45
- *
46
- * @returns The new terminal connection instance.
47
- *
48
- * #### Notes
49
- * The manager `serverSettings` will be used.
50
- */
51
- connectTo(options) {
52
- const { model } = options;
53
- const { name } = model;
54
- console.log('==> LiteTerminalManager.connectTo', name);
55
- const { serverSettings } = this;
56
- const terminal = new LiteTerminalConnection({
57
- browsingContextId: this._browsingContextId,
58
- model,
59
- serverSettings
60
- });
61
- terminal.disposed.connect(() => this.shutdown(name));
62
- return terminal;
63
- }
64
- /**
65
- * Whether the terminal service is available.
66
- */
67
- isAvailable() {
68
- return true;
69
- }
70
- /**
71
- * Test whether the manager is ready.
72
- */
73
- get isReady() {
74
- return this._isReady;
75
- }
76
- /**
77
- * A promise that fulfills when the manager is ready.
78
- */
79
- get ready() {
80
- return this._ready;
81
- }
82
- /**
83
- * Force a refresh of the running terminals.
84
- *
85
- * @returns A promise that with the list of running terminals.
86
- *
87
- * #### Notes
88
- * This is intended to be called only in response to a user action,
89
- * since the manager maintains its internal state.
90
- */
91
- async refreshRunning() {
92
- this._runningChanged.emit(this._models);
93
- }
94
- /**
95
- * Create an iterator over the most recent running terminals.
96
- *
97
- * @returns A new iterator over the running terminals.
98
- */
99
- running() {
100
- return this._models[Symbol.iterator]();
101
- }
102
- /**
103
- * A signal emitted when the running terminals change.
104
- */
105
- get runningChanged() {
106
- return this._runningChanged;
107
- }
108
- /**
109
- * Shut down a terminal session by name.
110
- */
111
- async shutdown(name) {
112
- const terminal = this._terminalConnections.get(name);
113
- if (terminal !== undefined) {
114
- this._terminalConnections.delete(name);
115
- terminal.dispose();
116
- this.refreshRunning();
117
- }
118
- }
119
- /**
120
- * Shut down all terminal sessions.
121
- *
122
- * @returns A promise that resolves when all of the sessions are shut down.
123
- */
124
- async shutdownAll() {
125
- await Promise.all(this._models.map(model => this.shutdown(model.name)));
126
- this.refreshRunning();
127
- }
128
- /**
129
- * Create a new terminal session.
130
- *
131
- * @param options - The options used to create the terminal.
132
- *
133
- * @returns A promise that resolves with the terminal connection instance.
134
- *
135
- * #### Notes
136
- * The manager `serverSettings` will be used unless overridden in the
137
- * options.
138
- */
139
- async startNew(options) {
140
- var _a;
141
- const name = (_a = options.name) !== null && _a !== void 0 ? _a : this._nextAvailableName();
142
- const model = { name };
143
- const { serverSettings } = this;
144
- const terminal = new LiteTerminalConnection({
145
- browsingContextId: this._browsingContextId,
146
- model,
147
- serverSettings
148
- });
149
- terminal.disposed.connect(() => this.shutdown(name));
150
- this._terminalConnections.set(name, terminal);
151
- await this.refreshRunning();
152
- return terminal;
153
- }
154
- get _models() {
155
- return Array.from(this._terminalConnections, ([name, value]) => {
156
- return { name };
157
- });
158
- }
159
- _nextAvailableName() {
160
- for (let i = 1;; ++i) {
161
- const name = `${i}`;
162
- if (!this._terminalConnections.has(name)) {
163
- return name;
164
- }
165
- }
166
- }
167
- }
package/lib/terminal.d.ts DELETED
@@ -1,88 +0,0 @@
1
- import { ServerConnection, Terminal } from '@jupyterlab/services';
2
- import { ISignal } from '@lumino/signaling';
3
- /**
4
- * An implementation of a terminal interface.
5
- */
6
- export declare class LiteTerminalConnection implements Terminal.ITerminalConnection {
7
- /**
8
- * Construct a new terminal session.
9
- */
10
- constructor(options: LiteTerminalConnection.IOptions);
11
- /**
12
- * The current connection status of the terminal connection.
13
- */
14
- get connectionStatus(): Terminal.ConnectionStatus;
15
- /**
16
- * A signal emitted when the terminal connection status changes.
17
- */
18
- get connectionStatusChanged(): ISignal<this, Terminal.ConnectionStatus>;
19
- /**
20
- * Dispose of the resources held by the session.
21
- */
22
- dispose(): void;
23
- /**
24
- * A signal emitted when the session is disposed.
25
- */
26
- get disposed(): ISignal<this, void>;
27
- /**
28
- * Test whether the session is disposed.
29
- */
30
- get isDisposed(): boolean;
31
- /**
32
- * A signal emitted when a message is received from the server.
33
- */
34
- get messageReceived(): ISignal<Terminal.ITerminalConnection, Terminal.IMessage>;
35
- /**
36
- * Get the model for the terminal session.
37
- */
38
- get model(): Terminal.IModel;
39
- /**
40
- * Get the name of the terminal session.
41
- */
42
- get name(): string;
43
- /**
44
- * Reconnect to a terminal.
45
- *
46
- * #### Notes
47
- * This may try multiple times to reconnect to a terminal, and will sever
48
- * any existing connection.
49
- */
50
- reconnect(): Promise<void>;
51
- /**
52
- * Send a message to the terminal session.
53
- *
54
- * #### Notes
55
- * If the connection is down, the message will be queued for sending when
56
- * the connection comes back up.
57
- */
58
- send(message: Terminal.IMessage): void;
59
- /**
60
- * The server settings for the session.
61
- */
62
- get serverSettings(): ServerConnection.ISettings;
63
- /**
64
- * Shut down the terminal session.
65
- */
66
- shutdown(): Promise<void>;
67
- private _outputCallback;
68
- /**
69
- * Handle connection status changes.
70
- */
71
- private _updateConnectionStatus;
72
- private _isDisposed;
73
- private _disposed;
74
- private _name;
75
- private _serverSettings;
76
- private _connectionStatus;
77
- private _connectionStatusChanged;
78
- private _messageReceived;
79
- private _shell;
80
- }
81
- export declare namespace LiteTerminalConnection {
82
- interface IOptions extends Terminal.ITerminalConnection.IOptions {
83
- /**
84
- * The ID of the browsing context where the request originated.
85
- */
86
- browsingContextId?: string;
87
- }
88
- }
package/lib/terminal.js DELETED
@@ -1,147 +0,0 @@
1
- import { Signal } from '@lumino/signaling';
2
- import { Shell } from './shell';
3
- /**
4
- * An implementation of a terminal interface.
5
- */
6
- export class LiteTerminalConnection {
7
- /**
8
- * Construct a new terminal session.
9
- */
10
- constructor(options) {
11
- this._isDisposed = false;
12
- this._disposed = new Signal(this);
13
- this._connectionStatus = 'connecting';
14
- this._connectionStatusChanged = new Signal(this);
15
- this._messageReceived = new Signal(this);
16
- this._name = options.model.name;
17
- this._serverSettings = options.serverSettings;
18
- const { baseUrl } = this._serverSettings;
19
- const { browsingContextId } = options;
20
- this._shell = new Shell({
21
- mountpoint: '/drive',
22
- driveFsBaseUrl: baseUrl,
23
- wasmBaseUrl: baseUrl + 'extensions/@jupyterlite/terminal/static/wasm/',
24
- outputCallback: this._outputCallback.bind(this),
25
- browsingContextId
26
- });
27
- this._shell.disposed.connect(() => this.dispose());
28
- this._shell.start().then(() => this._updateConnectionStatus('connected'));
29
- }
30
- /**
31
- * The current connection status of the terminal connection.
32
- */
33
- get connectionStatus() {
34
- return this._connectionStatus;
35
- }
36
- /**
37
- * A signal emitted when the terminal connection status changes.
38
- */
39
- get connectionStatusChanged() {
40
- return this._connectionStatusChanged;
41
- }
42
- /**
43
- * Dispose of the resources held by the session.
44
- */
45
- dispose() {
46
- if (this._isDisposed) {
47
- return;
48
- }
49
- this._isDisposed = true;
50
- this._shell.dispose();
51
- this._disposed.emit();
52
- this._updateConnectionStatus('disconnected');
53
- Signal.clearData(this);
54
- }
55
- /**
56
- * A signal emitted when the session is disposed.
57
- */
58
- get disposed() {
59
- return this._disposed;
60
- }
61
- /**
62
- * Test whether the session is disposed.
63
- */
64
- get isDisposed() {
65
- return this._isDisposed;
66
- }
67
- /**
68
- * A signal emitted when a message is received from the server.
69
- */
70
- get messageReceived() {
71
- return this._messageReceived;
72
- }
73
- /**
74
- * Get the model for the terminal session.
75
- */
76
- get model() {
77
- return { name: this._name };
78
- }
79
- /**
80
- * Get the name of the terminal session.
81
- */
82
- get name() {
83
- return this._name;
84
- }
85
- /**
86
- * Reconnect to a terminal.
87
- *
88
- * #### Notes
89
- * This may try multiple times to reconnect to a terminal, and will sever
90
- * any existing connection.
91
- */
92
- async reconnect() {
93
- console.log('==> LiteTerminalConnection.reconnect not implemented');
94
- }
95
- /**
96
- * Send a message to the terminal session.
97
- *
98
- * #### Notes
99
- * If the connection is down, the message will be queued for sending when
100
- * the connection comes back up.
101
- */
102
- send(message) {
103
- const { content } = message;
104
- if (content === undefined) {
105
- return;
106
- }
107
- switch (message.type) {
108
- case 'stdin':
109
- this._shell.input(content[0]); // async
110
- break;
111
- case 'set_size': {
112
- const rows = content[0];
113
- const columns = content[1];
114
- this._shell.setSize(rows, columns); // async
115
- break;
116
- }
117
- }
118
- }
119
- /**
120
- * The server settings for the session.
121
- */
122
- get serverSettings() {
123
- return this._serverSettings;
124
- }
125
- /**
126
- * Shut down the terminal session.
127
- */
128
- async shutdown() {
129
- this.dispose();
130
- }
131
- _outputCallback(text) {
132
- // 'stdout' or 'disconnect' as MessageType.
133
- // Cockle is not yet using the 'disconnect'.
134
- this._messageReceived.emit({ type: 'stdout', content: [text] });
135
- }
136
- /**
137
- * Handle connection status changes.
138
- */
139
- _updateConnectionStatus(connectionStatus) {
140
- if (this._connectionStatus === connectionStatus) {
141
- return;
142
- }
143
- this._connectionStatus = connectionStatus;
144
- // Notify others that the connection status changed.
145
- this._connectionStatusChanged.emit(connectionStatus);
146
- }
147
- }