@theia/remote 1.45.1 → 1.46.0-next.241

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.
Files changed (142) hide show
  1. package/README.md +1 -1
  2. package/lib/electron-browser/local-backend-services.d.ts +11 -0
  3. package/lib/electron-browser/local-backend-services.d.ts.map +1 -0
  4. package/lib/electron-browser/local-backend-services.js +37 -0
  5. package/lib/electron-browser/local-backend-services.js.map +1 -0
  6. package/lib/electron-browser/port-forwarding/port-forwading-contribution.d.ts +6 -0
  7. package/lib/electron-browser/port-forwarding/port-forwading-contribution.d.ts.map +1 -0
  8. package/lib/electron-browser/port-forwarding/port-forwading-contribution.js +40 -0
  9. package/lib/electron-browser/port-forwarding/port-forwading-contribution.js.map +1 -0
  10. package/lib/electron-browser/port-forwarding/port-forwarding-service.d.ts +20 -0
  11. package/lib/electron-browser/port-forwarding/port-forwarding-service.d.ts.map +1 -0
  12. package/lib/electron-browser/port-forwarding/port-forwarding-service.js +89 -0
  13. package/lib/electron-browser/port-forwarding/port-forwarding-service.js.map +1 -0
  14. package/lib/electron-browser/port-forwarding/port-forwarding-widget.d.ts +17 -0
  15. package/lib/electron-browser/port-forwarding/port-forwarding-widget.d.ts.map +1 -0
  16. package/lib/electron-browser/port-forwarding/port-forwarding-widget.js +125 -0
  17. package/lib/electron-browser/port-forwarding/port-forwarding-widget.js.map +1 -0
  18. package/lib/electron-browser/remote-electron-file-dialog-service.js +5 -13
  19. package/lib/electron-browser/remote-electron-file-dialog-service.js.map +1 -1
  20. package/lib/electron-browser/remote-frontend-contribution.d.ts +1 -1
  21. package/lib/electron-browser/remote-frontend-contribution.d.ts.map +1 -1
  22. package/lib/electron-browser/remote-frontend-contribution.js +27 -33
  23. package/lib/electron-browser/remote-frontend-contribution.js.map +1 -1
  24. package/lib/electron-browser/remote-frontend-module.d.ts +1 -0
  25. package/lib/electron-browser/remote-frontend-module.d.ts.map +1 -1
  26. package/lib/electron-browser/remote-frontend-module.js +29 -2
  27. package/lib/electron-browser/remote-frontend-module.js.map +1 -1
  28. package/lib/electron-browser/remote-preferences.d.ts +1 -1
  29. package/lib/electron-browser/remote-preferences.d.ts.map +1 -1
  30. package/lib/electron-browser/remote-registry-contribution.d.ts +1 -1
  31. package/lib/electron-browser/remote-registry-contribution.d.ts.map +1 -1
  32. package/lib/electron-browser/remote-registry-contribution.js +11 -16
  33. package/lib/electron-browser/remote-registry-contribution.js.map +1 -1
  34. package/lib/electron-browser/remote-service.js +3 -8
  35. package/lib/electron-browser/remote-service.js.map +1 -1
  36. package/lib/electron-browser/remote-ssh-contribution.js +12 -20
  37. package/lib/electron-browser/remote-ssh-contribution.js.map +1 -1
  38. package/lib/electron-browser/remote-user-storage-provider.d.ts +21 -0
  39. package/lib/electron-browser/remote-user-storage-provider.d.ts.map +1 -0
  40. package/lib/electron-browser/remote-user-storage-provider.js +75 -0
  41. package/lib/electron-browser/remote-user-storage-provider.js.map +1 -0
  42. package/lib/electron-common/remote-port-forwarding-provider.d.ts +12 -0
  43. package/lib/electron-common/remote-port-forwarding-provider.d.ts.map +1 -0
  44. package/lib/electron-common/remote-port-forwarding-provider.js +21 -0
  45. package/lib/electron-common/remote-port-forwarding-provider.js.map +1 -0
  46. package/lib/electron-common/remote-status-service.d.ts +2 -1
  47. package/lib/electron-common/remote-status-service.d.ts.map +1 -1
  48. package/lib/electron-node/backend-remote-service-impl.d.ts +1 -1
  49. package/lib/electron-node/backend-remote-service-impl.d.ts.map +1 -1
  50. package/lib/electron-node/backend-remote-service-impl.js +4 -9
  51. package/lib/electron-node/backend-remote-service-impl.js.map +1 -1
  52. package/lib/electron-node/remote-backend-module.d.ts.map +1 -1
  53. package/lib/electron-node/remote-backend-module.js +10 -4
  54. package/lib/electron-node/remote-backend-module.js.map +1 -1
  55. package/lib/electron-node/remote-connection-service.d.ts +2 -2
  56. package/lib/electron-node/remote-connection-service.d.ts.map +1 -1
  57. package/lib/electron-node/remote-connection-service.js +17 -19
  58. package/lib/electron-node/remote-connection-service.js.map +1 -1
  59. package/lib/electron-node/remote-connection-socket-provider.js +3 -8
  60. package/lib/electron-node/remote-connection-socket-provider.js.map +1 -1
  61. package/lib/electron-node/remote-port-forwarding-provider.d.ts +19 -0
  62. package/lib/electron-node/remote-port-forwarding-provider.d.ts.map +1 -0
  63. package/lib/electron-node/remote-port-forwarding-provider.js +59 -0
  64. package/lib/electron-node/remote-port-forwarding-provider.js.map +1 -0
  65. package/lib/electron-node/remote-proxy-server-provider.js +3 -8
  66. package/lib/electron-node/remote-proxy-server-provider.js.map +1 -1
  67. package/lib/electron-node/remote-status-service.d.ts +1 -0
  68. package/lib/electron-node/remote-status-service.d.ts.map +1 -1
  69. package/lib/electron-node/remote-status-service.js +11 -13
  70. package/lib/electron-node/remote-status-service.js.map +1 -1
  71. package/lib/electron-node/remote-types.d.ts +21 -8
  72. package/lib/electron-node/remote-types.d.ts.map +1 -1
  73. package/lib/electron-node/setup/app-native-dependency-contribution.d.ts +1 -1
  74. package/lib/electron-node/setup/app-native-dependency-contribution.d.ts.map +1 -1
  75. package/lib/electron-node/setup/app-native-dependency-contribution.js +3 -8
  76. package/lib/electron-node/setup/app-native-dependency-contribution.js.map +1 -1
  77. package/lib/electron-node/setup/main-copy-contribution.d.ts +1 -1
  78. package/lib/electron-node/setup/main-copy-contribution.d.ts.map +1 -1
  79. package/lib/electron-node/setup/main-copy-contribution.js +3 -8
  80. package/lib/electron-node/setup/main-copy-contribution.js.map +1 -1
  81. package/lib/electron-node/setup/remote-copy-contribution.d.ts +3 -19
  82. package/lib/electron-node/setup/remote-copy-contribution.d.ts.map +1 -1
  83. package/lib/electron-node/setup/remote-copy-contribution.js +20 -25
  84. package/lib/electron-node/setup/remote-copy-contribution.js.map +1 -1
  85. package/lib/electron-node/setup/remote-copy-service.d.ts +5 -3
  86. package/lib/electron-node/setup/remote-copy-service.d.ts.map +1 -1
  87. package/lib/electron-node/setup/remote-copy-service.js +14 -21
  88. package/lib/electron-node/setup/remote-copy-service.js.map +1 -1
  89. package/lib/electron-node/setup/remote-native-dependency-contribution.d.ts +2 -2
  90. package/lib/electron-node/setup/remote-native-dependency-contribution.d.ts.map +1 -1
  91. package/lib/electron-node/setup/remote-native-dependency-contribution.js +2 -2
  92. package/lib/electron-node/setup/remote-native-dependency-contribution.js.map +1 -1
  93. package/lib/electron-node/setup/remote-native-dependency-service.d.ts +1 -1
  94. package/lib/electron-node/setup/remote-native-dependency-service.d.ts.map +1 -1
  95. package/lib/electron-node/setup/remote-native-dependency-service.js +7 -15
  96. package/lib/electron-node/setup/remote-native-dependency-service.js.map +1 -1
  97. package/lib/electron-node/setup/remote-node-setup-service.d.ts +1 -1
  98. package/lib/electron-node/setup/remote-node-setup-service.d.ts.map +1 -1
  99. package/lib/electron-node/setup/remote-node-setup-service.js +7 -15
  100. package/lib/electron-node/setup/remote-node-setup-service.js.map +1 -1
  101. package/lib/electron-node/setup/remote-setup-script-service.d.ts +1 -1
  102. package/lib/electron-node/setup/remote-setup-script-service.d.ts.map +1 -1
  103. package/lib/electron-node/setup/remote-setup-script-service.js +11 -19
  104. package/lib/electron-node/setup/remote-setup-script-service.js.map +1 -1
  105. package/lib/electron-node/setup/remote-setup-service.d.ts +9 -2
  106. package/lib/electron-node/setup/remote-setup-service.d.ts.map +1 -1
  107. package/lib/electron-node/setup/remote-setup-service.js +39 -23
  108. package/lib/electron-node/setup/remote-setup-service.js.map +1 -1
  109. package/lib/electron-node/ssh/remote-ssh-connection-provider.d.ts +1 -1
  110. package/lib/electron-node/ssh/remote-ssh-connection-provider.d.ts.map +1 -1
  111. package/lib/electron-node/ssh/remote-ssh-connection-provider.js +28 -34
  112. package/lib/electron-node/ssh/remote-ssh-connection-provider.js.map +1 -1
  113. package/lib/electron-node/ssh/ssh-identity-file-collector.js +3 -8
  114. package/lib/electron-node/ssh/ssh-identity-file-collector.js.map +1 -1
  115. package/package.json +11 -10
  116. package/src/electron-browser/local-backend-services.ts +31 -0
  117. package/src/electron-browser/port-forwarding/port-forwading-contribution.ts +33 -0
  118. package/src/electron-browser/port-forwarding/port-forwarding-service.ts +93 -0
  119. package/src/electron-browser/port-forwarding/port-forwarding-widget.tsx +140 -0
  120. package/src/electron-browser/remote-frontend-contribution.ts +9 -7
  121. package/src/electron-browser/remote-frontend-module.ts +39 -3
  122. package/src/electron-browser/remote-registry-contribution.ts +9 -6
  123. package/src/electron-browser/remote-user-storage-provider.ts +64 -0
  124. package/src/electron-browser/style/port-forwarding-widget.css +44 -0
  125. package/src/electron-common/remote-port-forwarding-provider.ts +30 -0
  126. package/src/electron-common/remote-status-service.ts +3 -1
  127. package/src/electron-node/backend-remote-service-impl.ts +1 -1
  128. package/src/electron-node/remote-backend-module.ts +10 -3
  129. package/src/electron-node/remote-connection-service.ts +9 -4
  130. package/src/electron-node/remote-port-forwarding-provider.ts +66 -0
  131. package/src/electron-node/remote-status-service.ts +7 -0
  132. package/src/electron-node/remote-types.ts +20 -7
  133. package/src/electron-node/setup/app-native-dependency-contribution.ts +1 -1
  134. package/src/electron-node/setup/main-copy-contribution.ts +1 -1
  135. package/src/electron-node/setup/remote-copy-contribution.ts +14 -30
  136. package/src/electron-node/setup/remote-copy-service.ts +6 -4
  137. package/src/electron-node/setup/remote-native-dependency-contribution.ts +1 -1
  138. package/src/electron-node/setup/remote-native-dependency-service.ts +1 -1
  139. package/src/electron-node/setup/remote-node-setup-service.ts +1 -1
  140. package/src/electron-node/setup/remote-setup-script-service.ts +1 -1
  141. package/src/electron-node/setup/remote-setup-service.ts +31 -6
  142. package/src/electron-node/ssh/remote-ssh-connection-provider.ts +10 -8
@@ -0,0 +1,140 @@
1
+ // *****************************************************************************
2
+ // Copyright (C) 2024 TypeFox and others.
3
+ //
4
+ // This program and the accompanying materials are made available under the
5
+ // terms of the Eclipse Public License v. 2.0 which is available at
6
+ // http://www.eclipse.org/legal/epl-2.0.
7
+ //
8
+ // This Source Code may also be made available under the following Secondary
9
+ // Licenses when the conditions for such availability set forth in the Eclipse
10
+ // Public License v. 2.0 are satisfied: GNU General Public License, version 2
11
+ // with the GNU Classpath Exception which is available at
12
+ // https://www.gnu.org/software/classpath/license.html.
13
+ //
14
+ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
15
+ // *****************************************************************************
16
+
17
+ import * as React from '@theia/core/shared/react';
18
+ import { ReactNode } from '@theia/core/shared/react';
19
+ import { OpenerService, ReactWidget } from '@theia/core/lib/browser';
20
+ import { nls, URI } from '@theia/core';
21
+ import { inject, injectable, postConstruct } from '@theia/core/shared/inversify';
22
+ import { ForwardedPort, PortForwardingService } from './port-forwarding-service';
23
+ import { ClipboardService } from '@theia/core/lib/browser/clipboard-service';
24
+
25
+ export const PORT_FORWARDING_WIDGET_ID = 'port-forwarding-widget';
26
+
27
+ @injectable()
28
+ export class PortForwardingWidget extends ReactWidget {
29
+
30
+ @inject(PortForwardingService)
31
+ protected readonly portForwardingService: PortForwardingService;
32
+
33
+ @inject(OpenerService)
34
+ protected readonly openerService: OpenerService;
35
+
36
+ @inject(ClipboardService)
37
+ protected readonly clipboardService: ClipboardService;
38
+
39
+ @postConstruct()
40
+ protected init(): void {
41
+ this.id = PORT_FORWARDING_WIDGET_ID;
42
+ this.node.tabIndex = -1;
43
+ this.title.label = nls.localizeByDefault('Ports');
44
+ this.title.caption = this.title.label;
45
+ this.title.closable = true;
46
+ this.update();
47
+
48
+ this.portForwardingService.onDidChangePorts(() => this.update());
49
+ }
50
+
51
+ protected render(): ReactNode {
52
+ if (this.portForwardingService.forwardedPorts.length === 0) {
53
+ return <div>
54
+ <p style={{ marginLeft: 'calc(var(--theia-ui-padding) * 2)' }}>
55
+ {nls.localizeByDefault('No forwarded ports. Forward a port to access your locally running services over the internet.\n[Forward a Port]({0})').split('\n')[0]}
56
+ </p>
57
+ {this.renderForwardPortButton()}
58
+ </div>;
59
+ }
60
+
61
+ return <div>
62
+ <table className='port-table'>
63
+ <thead>
64
+ <tr>
65
+ <th className='port-table-header'>{nls.localizeByDefault('Port')}</th>
66
+ <th className='port-table-header'>{nls.localizeByDefault('Address')}</th>
67
+ <th className='port-table-header'>{nls.localizeByDefault('Running Process')}</th>
68
+ <th className='port-table-header'>{nls.localizeByDefault('Origin')}</th>
69
+ </tr>
70
+ </thead>
71
+ <tbody>
72
+ {this.portForwardingService.forwardedPorts.map(port => (
73
+ <tr key={port.localPort ?? 'editing'}>
74
+ {this.renderPortColumn(port)}
75
+ {this.renderAddressColumn(port)}
76
+ <td></td>
77
+ <td>{port.origin ? nls.localizeByDefault(port.origin) : ''}</td>
78
+ </tr>
79
+ ))}
80
+ {!this.portForwardingService.forwardedPorts.some(port => port.editing) && <tr><td>{this.renderForwardPortButton()}</td></tr>}
81
+ </tbody>
82
+ </table>
83
+ </div>;
84
+ }
85
+
86
+ protected renderForwardPortButton(): ReactNode {
87
+ return <button className='theia-button' onClick={() => {
88
+ this.portForwardingService.forwardNewPort('User Forwarded');
89
+ this.update();
90
+ }
91
+ }>{nls.localizeByDefault('Forward a Port')}</button>;
92
+ }
93
+
94
+ protected renderAddressColumn(port: ForwardedPort): ReactNode {
95
+ const address = `${port.address ?? '0.0.0.0'}:${port.localPort}`;
96
+ return <td>
97
+ <div className='button-cell'>
98
+ <span style={{ flexGrow: 1 }} className='forwarded-address' onClick={async e => {
99
+ if (e.ctrlKey) {
100
+ const uri = new URI(`http://${address}`);
101
+ (await this.openerService.getOpener(uri)).open(uri);
102
+ }
103
+ }} title={nls.localizeByDefault('Follow link') + ' (ctrl/cmd + click)'}>
104
+ {port.localPort ? address : ''}
105
+ </span>
106
+ {
107
+ port.localPort &&
108
+ <span className='codicon codicon-clippy action-label' title={nls.localizeByDefault('Copy Local Address')} onClick={() => {
109
+ this.clipboardService.writeText(address);
110
+ }}></span>
111
+ }
112
+ </div>
113
+ </td>;
114
+ }
115
+
116
+ protected renderPortColumn(port: ForwardedPort): ReactNode {
117
+ return port.editing ?
118
+ <td><PortEditingInput port={port} service={this.portForwardingService} /></td> :
119
+ <td>
120
+ <div className='button-cell'>
121
+ <span style={{ flexGrow: 1 }}>{port.localPort}</span>
122
+ <span className='codicon codicon-close action-label' title={nls.localizeByDefault('Stop Forwarding Port')} onClick={() => {
123
+ this.portForwardingService.removePort(port);
124
+ this.update();
125
+ }}></span>
126
+ </div>
127
+ </td>;
128
+ }
129
+
130
+ }
131
+
132
+ function PortEditingInput({ port, service }: { port: ForwardedPort, service: PortForwardingService }): React.JSX.Element {
133
+ const [error, setError] = React.useState(false);
134
+ return <input className={`theia-input forward-port-button${error ? ' port-edit-input-error' : ''}`} port-edit-input-error={error}
135
+ autoFocus defaultValue={port.address ? `${port.address}:${port.localPort}` : port.localPort ?? ''}
136
+ placeholder={nls.localizeByDefault('Port number or address (eg. 3000 or 10.10.10.10:2000).')}
137
+ onKeyDown={e => e.key === 'Enter' && !error && service.updatePort(port, e.currentTarget.value)}
138
+ onKeyUp={e => setError(!service.isValidAddress(e.currentTarget.value))}></input>;
139
+
140
+ }
@@ -21,6 +21,7 @@ import { RemoteStatus, RemoteStatusService } from '../electron-common/remote-sta
21
21
  import { RemoteRegistry, RemoteRegistryContribution } from './remote-registry-contribution';
22
22
  import { RemoteService } from './remote-service';
23
23
  import { WindowService } from '@theia/core/lib/browser/window/window-service';
24
+ import { getLocalPort, getCurrentPort } from '@theia/core/lib/electron-browser/messaging/electron-local-ws-connection-source';
24
25
 
25
26
  export namespace RemoteCommands {
26
27
  export const REMOTE_SELECT: Command = {
@@ -59,7 +60,7 @@ export class RemoteFrontendContribution implements CommandContribution, Frontend
59
60
  protected remoteRegistry = new RemoteRegistry();
60
61
 
61
62
  async configure(): Promise<void> {
62
- const port = new URLSearchParams(location.search).get('port');
63
+ const port = getCurrentPort();
63
64
  if (port) {
64
65
  const status = await this.remoteStatusService.getStatus(Number(port));
65
66
  await this.setStatusBar(status);
@@ -105,12 +106,13 @@ export class RemoteFrontendContribution implements CommandContribution, Frontend
105
106
  });
106
107
  }
107
108
 
108
- protected disconnectRemote(): void {
109
- const port = new URLSearchParams(location.search).get('localPort');
110
- if (port) {
111
- this.windowService.reload({
112
- port
113
- });
109
+ protected async disconnectRemote(): Promise<void> {
110
+ const localPort = getLocalPort();
111
+ if (localPort) {
112
+ const searchParams = new URLSearchParams(location.search);
113
+ const currentPort = searchParams.get('port');
114
+ this.remoteStatusService.connectionClosed(parseInt(currentPort ?? '0'));
115
+ this.windowService.reload({ search: { port: localPort } });
114
116
  }
115
117
  }
116
118
 
@@ -16,7 +16,7 @@
16
16
 
17
17
  import { bindContributionProvider, CommandContribution } from '@theia/core';
18
18
  import { ContainerModule } from '@theia/core/shared/inversify';
19
- import { FrontendApplicationContribution, WebSocketConnectionProvider } from '@theia/core/lib/browser';
19
+ import { bindViewContribution, FrontendApplicationContribution, isRemote, WidgetFactory } from '@theia/core/lib/browser';
20
20
  import { RemoteSSHContribution } from './remote-ssh-contribution';
21
21
  import { RemoteSSHConnectionProvider, RemoteSSHConnectionProviderPath } from '../electron-common/remote-ssh-connection-provider';
22
22
  import { RemoteFrontendContribution } from './remote-frontend-contribution';
@@ -26,6 +26,17 @@ import { RemoteStatusService, RemoteStatusServicePath } from '../electron-common
26
26
  import { ElectronFileDialogService } from '@theia/filesystem/lib/electron-browser/file-dialog/electron-file-dialog-service';
27
27
  import { RemoteElectronFileDialogService } from './remote-electron-file-dialog-service';
28
28
  import { bindRemotePreferences } from './remote-preferences';
29
+ import { PortForwardingWidget, PORT_FORWARDING_WIDGET_ID } from './port-forwarding/port-forwarding-widget';
30
+ import { PortForwardingContribution } from './port-forwarding/port-forwading-contribution';
31
+ import { PortForwardingService } from './port-forwarding/port-forwarding-service';
32
+ import { RemotePortForwardingProvider, RemoteRemotePortForwardingProviderPath } from '../electron-common/remote-port-forwarding-provider';
33
+ import { ServiceConnectionProvider } from '@theia/core/lib/browser/messaging/service-connection-provider';
34
+ import '../../src/electron-browser/style/port-forwarding-widget.css';
35
+ import { UserStorageContribution } from '@theia/userstorage/lib/browser/user-storage-contribution';
36
+ import { RemoteUserStorageContribution } from './remote-user-storage-provider';
37
+ import { remoteFileSystemPath, RemoteFileSystemProxyFactory, RemoteFileSystemServer } from '@theia/filesystem/lib/common/remote-file-system-provider';
38
+ import { LocalEnvVariablesServer, LocalRemoteFileSystemProvider, LocalRemoteFileSytemServer } from './local-backend-services';
39
+ import { envVariablesPath, EnvVariablesServer } from '@theia/core/lib/common/env-variables';
29
40
 
30
41
  export default new ContainerModule((bind, _, __, rebind) => {
31
42
  bind(RemoteFrontendContribution).toSelf().inSingletonScope();
@@ -42,8 +53,33 @@ export default new ContainerModule((bind, _, __, rebind) => {
42
53
 
43
54
  bind(RemoteService).toSelf().inSingletonScope();
44
55
 
56
+ bind(PortForwardingWidget).toSelf();
57
+ bind(WidgetFactory).toDynamicValue(context => ({
58
+ id: PORT_FORWARDING_WIDGET_ID,
59
+ createWidget: () => context.container.get<PortForwardingWidget>(PortForwardingWidget)
60
+ }));
61
+
62
+ bindViewContribution(bind, PortForwardingContribution);
63
+ bind(PortForwardingService).toSelf().inSingletonScope();
64
+
45
65
  bind(RemoteSSHConnectionProvider).toDynamicValue(ctx =>
46
- WebSocketConnectionProvider.createLocalProxy<RemoteSSHConnectionProvider>(ctx.container, RemoteSSHConnectionProviderPath)).inSingletonScope();
66
+ ServiceConnectionProvider.createLocalProxy<RemoteSSHConnectionProvider>(ctx.container, RemoteSSHConnectionProviderPath)).inSingletonScope();
47
67
  bind(RemoteStatusService).toDynamicValue(ctx =>
48
- WebSocketConnectionProvider.createLocalProxy<RemoteStatusService>(ctx.container, RemoteStatusServicePath)).inSingletonScope();
68
+ ServiceConnectionProvider.createLocalProxy<RemoteStatusService>(ctx.container, RemoteStatusServicePath)).inSingletonScope();
69
+
70
+ bind(RemotePortForwardingProvider).toDynamicValue(ctx =>
71
+ ServiceConnectionProvider.createLocalProxy<RemotePortForwardingProvider>(ctx.container, RemoteRemotePortForwardingProviderPath)).inSingletonScope();
72
+
73
+ bind(LocalRemoteFileSytemServer).toDynamicValue(ctx =>
74
+ isRemote ?
75
+ ServiceConnectionProvider.createLocalProxy(ctx.container, remoteFileSystemPath, new RemoteFileSystemProxyFactory()) :
76
+ ctx.container.get(RemoteFileSystemServer));
77
+ bind(LocalEnvVariablesServer).toDynamicValue(ctx =>
78
+ isRemote ?
79
+ ServiceConnectionProvider.createLocalProxy<EnvVariablesServer>(ctx.container, envVariablesPath) :
80
+ ctx.container.get(EnvVariablesServer));
81
+ bind(LocalRemoteFileSystemProvider).toSelf().inSingletonScope();
82
+ rebind(UserStorageContribution).to(RemoteUserStorageContribution);
83
+
49
84
  });
85
+
@@ -16,8 +16,7 @@
16
16
 
17
17
  import { Command, CommandHandler, Emitter, Event } from '@theia/core';
18
18
  import { inject, injectable } from '@theia/core/shared/inversify';
19
- import { WindowService } from '@theia/core/lib/browser/window/window-service';
20
- import { WindowSearchParams } from '@theia/core/lib/common/window';
19
+ import { WindowService, WindowReloadOptions } from '@theia/core/lib/browser/window/window-service';
21
20
 
22
21
  export const RemoteRegistryContribution = Symbol('RemoteRegistryContribution');
23
22
 
@@ -33,15 +32,19 @@ export abstract class AbstractRemoteRegistryContribution implements RemoteRegist
33
32
 
34
33
  abstract registerRemoteCommands(registry: RemoteRegistry): void;
35
34
 
36
- protected openRemote(port: string, newWindow: boolean): void {
35
+ protected openRemote(port: string, newWindow: boolean, workspace?: string): void {
37
36
  const searchParams = new URLSearchParams(location.search);
38
37
  const localPort = searchParams.get('localPort') || searchParams.get('port');
39
- const options: WindowSearchParams = {
40
- port
38
+ const options: WindowReloadOptions = {
39
+ search: { port }
41
40
  };
42
41
  if (localPort) {
43
- options.localPort = localPort;
42
+ options.search!.localPort = localPort;
44
43
  }
44
+ if (workspace) {
45
+ options.hash = workspace;
46
+ }
47
+
45
48
  if (newWindow) {
46
49
  this.windowService.openNewDefaultWindow(options);
47
50
  } else {
@@ -0,0 +1,64 @@
1
+ // *****************************************************************************
2
+ // Copyright (C) 2024 TypeFox and others.
3
+ //
4
+ // This program and the accompanying materials are made available under the
5
+ // terms of the Eclipse Public License v. 2.0 which is available at
6
+ // http://www.eclipse.org/legal/epl-2.0.
7
+ //
8
+ // This Source Code may also be made available under the following Secondary
9
+ // Licenses when the conditions for such availability set forth in the Eclipse
10
+ // Public License v. 2.0 are satisfied: GNU General Public License, version 2
11
+ // with the GNU Classpath Exception which is available at
12
+ // https://www.gnu.org/software/classpath/license.html.
13
+ //
14
+ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
15
+ // *****************************************************************************
16
+
17
+ import { injectable, inject, postConstruct } from '@theia/core/shared/inversify';
18
+ import { FileService } from '@theia/filesystem/lib/browser/file-service';
19
+ import { FileSystemProvider } from '@theia/filesystem/lib/common/files';
20
+ import { UserStorageContribution } from '@theia/userstorage/lib/browser/user-storage-contribution';
21
+ import { RemoteStatusService } from '../electron-common/remote-status-service';
22
+ import { LocalEnvVariablesServer, LocalRemoteFileSystemProvider } from './local-backend-services';
23
+ import { Deferred } from '@theia/core/lib/common/promise-util';
24
+ import { URI } from '@theia/core';
25
+ import { EnvVariablesServer } from '@theia/core/lib/common/env-variables';
26
+ import { getCurrentPort } from '@theia/core/lib/electron-browser/messaging/electron-local-ws-connection-source';
27
+
28
+ /**
29
+ * This overide is to have remote connections still use settings, keymaps, etc. from the local machine.
30
+ */
31
+ @injectable()
32
+ export class RemoteUserStorageContribution extends UserStorageContribution {
33
+ @inject(RemoteStatusService)
34
+ protected readonly remoteStatusService: RemoteStatusService;
35
+
36
+ @inject(LocalRemoteFileSystemProvider)
37
+ protected readonly localRemoteFileSystemProvider: LocalRemoteFileSystemProvider;
38
+
39
+ @inject(LocalEnvVariablesServer)
40
+ protected readonly localEnvironments: EnvVariablesServer;
41
+
42
+ isRemoteConnection: Deferred<boolean> = new Deferred();
43
+
44
+ @postConstruct()
45
+ protected init(): void {
46
+ const port = getCurrentPort();
47
+ if (port) {
48
+ this.remoteStatusService.getStatus(Number(port)).then(status => this.isRemoteConnection.resolve(status.alive));
49
+ }
50
+ }
51
+
52
+ protected override async getDelegate(service: FileService): Promise<FileSystemProvider> {
53
+ return await this.isRemoteConnection.promise ?
54
+ this.localRemoteFileSystemProvider
55
+ : service.activateProvider('file');
56
+ }
57
+
58
+ protected override async getCongigDirUri(): Promise<URI> {
59
+ return await this.isRemoteConnection.promise ?
60
+ new URI(await this.localEnvironments.getConfigDirUri())
61
+ : super.getCongigDirUri();
62
+ }
63
+
64
+ }
@@ -0,0 +1,44 @@
1
+ /********************************************************************************
2
+ * Copyright (C) 2024 TypeFox and others.
3
+ *
4
+ * This program and the accompanying materials are made available under the
5
+ * terms of the Eclipse Public License v. 2.0 which is available at
6
+ * http://www.eclipse.org/legal/epl-2.0.
7
+ *
8
+ * This Source Code may also be made available under the following Secondary
9
+ * Licenses when the conditions for such availability set forth in the Eclipse
10
+ * Public License v. 2.0 are satisfied: GNU General Public License, version 2
11
+ * with the GNU Classpath Exception which is available at
12
+ * https://www.gnu.org/software/classpath/license.html.
13
+ *
14
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
15
+ ********************************************************************************/
16
+
17
+ .port-table {
18
+ width: 100%;
19
+ margin: calc(var(--theia-ui-padding) * 2);
20
+ table-layout: fixed;
21
+ }
22
+
23
+ .port-table-header {
24
+ text-align: left;
25
+ }
26
+
27
+ .forward-port-button {
28
+ margin-left: 0;
29
+ width: 100%;
30
+ }
31
+
32
+ .button-cell {
33
+ display: flex;
34
+ padding-right: var(--theia-ui-padding);
35
+ }
36
+
37
+ .forwarded-address:hover {
38
+ cursor: pointer;
39
+ text-decoration: underline;
40
+ }
41
+
42
+ .port-edit-input-error {
43
+ outline-color: var(--theia-inputValidation-errorBorder);
44
+ }
@@ -0,0 +1,30 @@
1
+ // *****************************************************************************
2
+ // Copyright (C) 2024 TypeFox and others.
3
+ //
4
+ // This program and the accompanying materials are made available under the
5
+ // terms of the Eclipse Public License v. 2.0 which is available at
6
+ // http://www.eclipse.org/legal/epl-2.0.
7
+ //
8
+ // This Source Code may also be made available under the following Secondary
9
+ // Licenses when the conditions for such availability set forth in the Eclipse
10
+ // Public License v. 2.0 are satisfied: GNU General Public License, version 2
11
+ // with the GNU Classpath Exception which is available at
12
+ // https://www.gnu.org/software/classpath/license.html.
13
+ //
14
+ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
15
+ // *****************************************************************************
16
+
17
+ export const RemoteRemotePortForwardingProviderPath = '/remote/port-forwarding';
18
+
19
+ export const RemotePortForwardingProvider = Symbol('RemoteSSHConnectionProvider');
20
+
21
+ export interface ForwardedPort {
22
+ port: number;
23
+ address?: string;
24
+ }
25
+
26
+ export interface RemotePortForwardingProvider {
27
+ forwardPort(connectionPort: number, portToForward: ForwardedPort): Promise<void>;
28
+ portRemoved(port: ForwardedPort): Promise<void>;
29
+ getForwardedPorts(): Promise<ForwardedPort[]>
30
+ }
@@ -31,5 +31,7 @@ export const RemoteStatusServicePath = '/remote/status';
31
31
  export const RemoteStatusService = Symbol('RemoteStatusService');
32
32
 
33
33
  export interface RemoteStatusService {
34
- getStatus(localPort: number): Promise<RemoteStatus>
34
+ getStatus(localPort: number): Promise<RemoteStatus>;
35
+
36
+ connectionClosed(localPort: number): Promise<void>;
35
37
  }
@@ -17,7 +17,7 @@
17
17
  import { CliContribution } from '@theia/core/lib/node';
18
18
  import { injectable } from '@theia/core/shared/inversify';
19
19
  import { Arguments, Argv } from '@theia/core/shared/yargs';
20
- import { BackendRemoteService } from '@theia/core/lib/node/backend-remote-service';
20
+ import { BackendRemoteService } from '@theia/core/lib/node/remote/backend-remote-service';
21
21
 
22
22
  export const REMOTE_START = 'remote';
23
23
 
@@ -27,21 +27,28 @@ import { RemoteCopyService } from './setup/remote-copy-service';
27
27
  import { RemoteSetupService } from './setup/remote-setup-service';
28
28
  import { RemoteNativeDependencyService } from './setup/remote-native-dependency-service';
29
29
  import { BackendRemoteServiceImpl } from './backend-remote-service-impl';
30
- import { BackendRemoteService } from '@theia/core/lib/node/backend-remote-service';
30
+ import { BackendRemoteService } from '@theia/core/lib/node/remote/backend-remote-service';
31
31
  import { RemoteNodeSetupService } from './setup/remote-node-setup-service';
32
32
  import { RemotePosixScriptStrategy, RemoteSetupScriptService, RemoteWindowsScriptStrategy } from './setup/remote-setup-script-service';
33
33
  import { RemoteStatusService, RemoteStatusServicePath } from '../electron-common/remote-status-service';
34
34
  import { RemoteStatusServiceImpl } from './remote-status-service';
35
35
  import { ConnectionHandler, RpcConnectionHandler, bindContributionProvider } from '@theia/core';
36
- import { RemoteCopyContribution, RemoteCopyRegistry } from './setup/remote-copy-contribution';
36
+ import { RemoteCopyRegistryImpl } from './setup/remote-copy-contribution';
37
+ import { RemoteCopyContribution } from '@theia/core/lib/node/remote/remote-copy-contribution';
37
38
  import { MainCopyContribution } from './setup/main-copy-contribution';
38
39
  import { RemoteNativeDependencyContribution } from './setup/remote-native-dependency-contribution';
39
40
  import { AppNativeDependencyContribution } from './setup/app-native-dependency-contribution';
41
+ import { RemotePortForwardingProviderImpl } from './remote-port-forwarding-provider';
42
+ import { RemotePortForwardingProvider, RemoteRemotePortForwardingProviderPath } from '../electron-common/remote-port-forwarding-provider';
40
43
 
41
44
  export const remoteConnectionModule = ConnectionContainerModule.create(({ bind, bindBackendService }) => {
42
45
  bind(RemoteSSHConnectionProviderImpl).toSelf().inSingletonScope();
43
46
  bind(RemoteSSHConnectionProvider).toService(RemoteSSHConnectionProviderImpl);
44
47
  bindBackendService(RemoteSSHConnectionProviderPath, RemoteSSHConnectionProvider);
48
+
49
+ bind(RemotePortForwardingProviderImpl).toSelf().inSingletonScope();
50
+ bind(RemotePortForwardingProvider).toService(RemotePortForwardingProviderImpl);
51
+ bindBackendService(RemoteRemotePortForwardingProviderPath, RemotePortForwardingProvider);
45
52
  });
46
53
 
47
54
  export default new ContainerModule((bind, _unbind, _isBound, rebind) => {
@@ -62,7 +69,7 @@ export default new ContainerModule((bind, _unbind, _isBound, rebind) => {
62
69
  bind(RemotePosixScriptStrategy).toSelf().inSingletonScope();
63
70
  bind(RemoteSetupScriptService).toSelf().inSingletonScope();
64
71
  bind(RemoteNativeDependencyService).toSelf().inSingletonScope();
65
- bind(RemoteCopyRegistry).toSelf().inSingletonScope();
72
+ bind(RemoteCopyRegistryImpl).toSelf().inSingletonScope();
66
73
  bindContributionProvider(bind, RemoteCopyContribution);
67
74
  bindContributionProvider(bind, RemoteNativeDependencyContribution);
68
75
  bind(MainCopyContribution).toSelf().inSingletonScope();
@@ -18,8 +18,8 @@ import { inject, injectable } from '@theia/core/shared/inversify';
18
18
  import { RemoteConnection } from './remote-types';
19
19
  import { Disposable } from '@theia/core';
20
20
  import { RemoteCopyService } from './setup/remote-copy-service';
21
- import { RemoteNativeDependencyService } from './setup/remote-native-dependency-service';
22
21
  import { BackendApplicationContribution } from '@theia/core/lib/node';
22
+ import { RemoteSetupService } from './setup/remote-setup-service';
23
23
 
24
24
  @injectable()
25
25
  export class RemoteConnectionService implements BackendApplicationContribution {
@@ -27,8 +27,9 @@ export class RemoteConnectionService implements BackendApplicationContribution {
27
27
  @inject(RemoteCopyService)
28
28
  protected readonly copyService: RemoteCopyService;
29
29
 
30
- @inject(RemoteNativeDependencyService)
31
- protected readonly nativeDependencyService: RemoteNativeDependencyService;
30
+ // Workaround for the fact that connection scoped services cannot directly inject these services.
31
+ @inject(RemoteSetupService)
32
+ protected readonly remoteSetupService: RemoteSetupService;
32
33
 
33
34
  protected readonly connections = new Map<string, RemoteConnection>();
34
35
 
@@ -49,7 +50,11 @@ export class RemoteConnectionService implements BackendApplicationContribution {
49
50
 
50
51
  onStop(): void {
51
52
  for (const connection of this.connections.values()) {
52
- connection.dispose();
53
+ if (connection.disposeSync) {
54
+ connection.disposeSync();
55
+ } else {
56
+ connection.dispose();
57
+ };
53
58
  }
54
59
  }
55
60
  }
@@ -0,0 +1,66 @@
1
+ // *****************************************************************************
2
+ // Copyright (C) 2024 TypeFox and others.
3
+ //
4
+ // This program and the accompanying materials are made available under the
5
+ // terms of the Eclipse Public License v. 2.0 which is available at
6
+ // http://www.eclipse.org/legal/epl-2.0.
7
+ //
8
+ // This Source Code may also be made available under the following Secondary
9
+ // Licenses when the conditions for such availability set forth in the Eclipse
10
+ // Public License v. 2.0 are satisfied: GNU General Public License, version 2
11
+ // with the GNU Classpath Exception which is available at
12
+ // https://www.gnu.org/software/classpath/license.html.
13
+ //
14
+ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
15
+ // *****************************************************************************
16
+
17
+ import { inject, injectable } from '@theia/core/shared/inversify';
18
+ import { ForwardedPort, RemotePortForwardingProvider } from '../electron-common/remote-port-forwarding-provider';
19
+ import { createServer, Server } from 'net';
20
+ import { RemoteConnectionService } from './remote-connection-service';
21
+ import { RemoteConnection } from './remote-types';
22
+
23
+ interface ForwardInfo {
24
+ connection: RemoteConnection
25
+ port: ForwardedPort
26
+ server: Server
27
+ }
28
+
29
+ @injectable()
30
+ export class RemotePortForwardingProviderImpl implements RemotePortForwardingProvider {
31
+
32
+ @inject(RemoteConnectionService)
33
+ protected readonly connectionService: RemoteConnectionService;
34
+
35
+ protected static forwardedPorts: ForwardInfo[] = [];
36
+
37
+ async forwardPort(connectionPort: number, portToForward: ForwardedPort): Promise<void> {
38
+ const currentConnection = this.connectionService.getConnectionFromPort(connectionPort);
39
+ if (!currentConnection) {
40
+ throw new Error(`No connection found for port ${connectionPort}`);
41
+ }
42
+
43
+ const server = createServer(socket => {
44
+ currentConnection?.forwardOut(socket, portToForward.port);
45
+ }).listen(portToForward.port, portToForward.address);
46
+
47
+ currentConnection.onDidDisconnect(() => {
48
+ this.portRemoved(portToForward);
49
+ });
50
+
51
+ RemotePortForwardingProviderImpl.forwardedPorts.push({ connection: currentConnection, port: portToForward, server });
52
+ }
53
+
54
+ async portRemoved(forwardedPort: ForwardedPort): Promise<void> {
55
+ const forwardInfo = RemotePortForwardingProviderImpl.forwardedPorts.find(info => info.port.port === forwardedPort.port);
56
+ if (forwardInfo) {
57
+ forwardInfo.server.close();
58
+ RemotePortForwardingProviderImpl.forwardedPorts.splice(RemotePortForwardingProviderImpl.forwardedPorts.indexOf(forwardInfo), 1);
59
+ }
60
+ }
61
+
62
+ async getForwardedPorts(): Promise<ForwardedPort[]> {
63
+ return Array.from(RemotePortForwardingProviderImpl.forwardedPorts)
64
+ .map(forwardInfo => ({ ...forwardInfo.port, editing: false }));
65
+ }
66
+ }
@@ -38,4 +38,11 @@ export class RemoteStatusServiceImpl implements RemoteStatusService {
38
38
  };
39
39
  }
40
40
  }
41
+
42
+ async connectionClosed(localPort: number): Promise<void> {
43
+ const connection = this.remoteConnectionService.getConnectionFromPort(localPort);
44
+ if (connection) {
45
+ connection.dispose();
46
+ }
47
+ }
41
48
  }
@@ -14,14 +14,9 @@
14
14
  // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
15
15
  // *****************************************************************************
16
16
 
17
- import { Disposable, Event, OS } from '@theia/core';
17
+ import { Disposable, Event } from '@theia/core';
18
18
  import * as net from 'net';
19
19
 
20
- export interface RemotePlatform {
21
- os: OS.Type
22
- arch: string
23
- }
24
-
25
20
  export type RemoteStatusReport = (message: string) => void;
26
21
 
27
22
  export interface ExpressLayer {
@@ -49,8 +44,26 @@ export interface RemoteConnection extends Disposable {
49
44
  localPort: number;
50
45
  remotePort: number;
51
46
  onDidDisconnect: Event<void>;
52
- forwardOut(socket: net.Socket): void;
47
+ forwardOut(socket: net.Socket, port?: number): void;
48
+
49
+ /**
50
+ * execute a single command on the remote machine
51
+ */
53
52
  exec(cmd: string, args?: string[], options?: RemoteExecOptions): Promise<RemoteExecResult>;
53
+
54
+ /**
55
+ * execute a command on the remote machine and wait for a specific output
56
+ * @param tester function which returns true if the output is as expected
57
+ */
54
58
  execPartial(cmd: string, tester: RemoteExecTester, args?: string[], options?: RemoteExecOptions): Promise<RemoteExecResult>;
59
+
60
+ /**
61
+ * copy files from local to remote
62
+ */
55
63
  copy(localPath: string | Buffer | NodeJS.ReadableStream, remotePath: string): Promise<void>;
64
+
65
+ /**
66
+ * used for disposing when theia is shutting down
67
+ */
68
+ disposeSync?(): void;
56
69
  }
@@ -16,7 +16,7 @@
16
16
 
17
17
  import { injectable } from '@theia/core/shared/inversify';
18
18
  import { RemoteNativeDependencyContribution, DownloadOptions, DependencyDownload } from './remote-native-dependency-contribution';
19
- import { RemotePlatform } from '../remote-types';
19
+ import { RemotePlatform } from '@theia/core/lib/node/remote/remote-cli-contribution';
20
20
  import { OS } from '@theia/core';
21
21
 
22
22
  @injectable()
@@ -15,7 +15,7 @@
15
15
  // *****************************************************************************
16
16
 
17
17
  import { injectable } from '@theia/core/shared/inversify';
18
- import { RemoteCopyContribution, RemoteCopyRegistry } from './remote-copy-contribution';
18
+ import { RemoteCopyContribution, RemoteCopyRegistry } from '@theia/core/lib/node/remote/remote-copy-contribution';
19
19
 
20
20
  @injectable()
21
21
  export class MainCopyContribution implements RemoteCopyContribution {