mirror of
https://github.com/Eugeny/tabby.git
synced 2025-01-05 09:34:56 +03:00
fixed #5000 - native socksv5 connection support in ssh and connection mode UI overhaul
This commit is contained in:
parent
246ae9fe77
commit
9856249c88
@ -29,7 +29,7 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"run-script-os": "^1.1.3",
|
"run-script-os": "^1.1.3",
|
||||||
"socksv5": "^0.0.6"
|
"@luminati-io/socksv5": "^0.0.7"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@angular/animations": "^9.1.9",
|
"@angular/animations": "^9.1.9",
|
||||||
|
@ -30,6 +30,8 @@ export interface SSHProfileOptions extends LoginScriptsOptions {
|
|||||||
algorithms?: Record<string, string[]>
|
algorithms?: Record<string, string[]>
|
||||||
proxyCommand?: string
|
proxyCommand?: string
|
||||||
forwardedPorts?: ForwardedPortConfig[]
|
forwardedPorts?: ForwardedPortConfig[]
|
||||||
|
socksProxyHost?: string
|
||||||
|
socksProxyPort?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum PortForwardType {
|
export enum PortForwardType {
|
||||||
|
@ -2,15 +2,48 @@ ul.nav-tabs(ngbNav, #nav='ngbNav')
|
|||||||
li(ngbNavItem)
|
li(ngbNavItem)
|
||||||
a(ngbNavLink) General
|
a(ngbNavLink) General
|
||||||
ng-template(ngbNavContent)
|
ng-template(ngbNavContent)
|
||||||
.d-flex.w-100(*ngIf='!useProxyCommand')
|
.d-flex.w-100.mt-3
|
||||||
.form-group.w-100.mr-4
|
.form-group.mr-2(
|
||||||
|
ngbDropdown
|
||||||
|
)
|
||||||
|
label Connection
|
||||||
|
button.btn.btn-secondary.d-block(ngbDropdownToggle) {{getConnectionDropdownTitle()}}
|
||||||
|
div(ngbDropdownMenu)
|
||||||
|
button.dropdown-item(
|
||||||
|
(click)='connectionMode = "direct"',
|
||||||
|
) Direct
|
||||||
|
button.dropdown-item(
|
||||||
|
*ngIf='hostApp.platform !== Platform.Web',
|
||||||
|
(click)='connectionMode = "proxyCommand"',
|
||||||
|
)
|
||||||
|
div Proxy command
|
||||||
|
.text-muted Command's stdin/stdout is used instead of a network connection
|
||||||
|
button.dropdown-item(
|
||||||
|
(click)='connectionMode = "jumpHost"',
|
||||||
|
)
|
||||||
|
div Jump host
|
||||||
|
.text-muted Connect to a different host first and use it as a proxy
|
||||||
|
button.dropdown-item(
|
||||||
|
(click)='connectionMode = "socksProxy"',
|
||||||
|
)
|
||||||
|
div SOCKS proxy
|
||||||
|
.text-muted Connect through a proxy server
|
||||||
|
|
||||||
|
.form-group.w-100(*ngIf='connectionMode === "proxyCommand"')
|
||||||
|
label Proxy command
|
||||||
|
input.form-control(
|
||||||
|
type='text',
|
||||||
|
[(ngModel)]='profile.options.proxyCommand',
|
||||||
|
)
|
||||||
|
|
||||||
|
.form-group.w-100.mr-2(*ngIf='connectionMode !== "proxyCommand"')
|
||||||
label Host
|
label Host
|
||||||
input.form-control(
|
input.form-control(
|
||||||
type='text',
|
type='text',
|
||||||
[(ngModel)]='profile.options.host',
|
[(ngModel)]='profile.options.host',
|
||||||
)
|
)
|
||||||
|
|
||||||
.form-group
|
.form-group(*ngIf='connectionMode !== "proxyCommand"')
|
||||||
label Port
|
label Port
|
||||||
input.form-control(
|
input.form-control(
|
||||||
type='number',
|
type='number',
|
||||||
@ -18,8 +51,28 @@ ul.nav-tabs(ngbNav, #nav='ngbNav')
|
|||||||
[(ngModel)]='profile.options.port',
|
[(ngModel)]='profile.options.port',
|
||||||
)
|
)
|
||||||
|
|
||||||
.alert.alert-info(*ngIf='useProxyCommand')
|
.form-group(*ngIf='connectionMode === "jumpHost"')
|
||||||
.mr-auto Using a proxy command instead of a network connection
|
label Jump host
|
||||||
|
select.form-control([(ngModel)]='profile.options.jumpHost')
|
||||||
|
option([ngValue]='null') Select
|
||||||
|
option([ngValue]='x.id', *ngFor='let x of jumpHosts') {{x.name}}
|
||||||
|
|
||||||
|
|
||||||
|
.d-flex.w-100(*ngIf='connectionMode === "socksProxy"')
|
||||||
|
.form-group.w-100.mr-2
|
||||||
|
label SOCKS proxy host
|
||||||
|
input.form-control(
|
||||||
|
type='text',
|
||||||
|
[(ngModel)]='profile.options.socksProxyHost',
|
||||||
|
)
|
||||||
|
|
||||||
|
.form-group
|
||||||
|
label SOCKS proxy port
|
||||||
|
input.form-control(
|
||||||
|
type='number',
|
||||||
|
placeholder='5000',
|
||||||
|
[(ngModel)]='profile.options.socksProxyPort',
|
||||||
|
)
|
||||||
|
|
||||||
.form-group
|
.form-group
|
||||||
label Username
|
label Username
|
||||||
@ -93,13 +146,6 @@ ul.nav-tabs(ngbNav, #nav='ngbNav')
|
|||||||
li(ngbNavItem)
|
li(ngbNavItem)
|
||||||
a(ngbNavLink) Advanced
|
a(ngbNavLink) Advanced
|
||||||
ng-template(ngbNavContent)
|
ng-template(ngbNavContent)
|
||||||
.form-line(*ngIf='!useProxyCommand')
|
|
||||||
.header
|
|
||||||
.title Jump host
|
|
||||||
select.form-control([(ngModel)]='profile.options.jumpHost')
|
|
||||||
option(value='') None
|
|
||||||
option([ngValue]='x.id', *ngFor='let x of jumpHosts') {{x.name}}
|
|
||||||
|
|
||||||
.form-line(ng:if='hostApp.platform !== Platform.Web')
|
.form-line(ng:if='hostApp.platform !== Platform.Web')
|
||||||
.header
|
.header
|
||||||
.title X11 forwarding
|
.title X11 forwarding
|
||||||
@ -143,19 +189,6 @@ ul.nav-tabs(ngbNav, #nav='ngbNav')
|
|||||||
[(ngModel)]='profile.options.readyTimeout',
|
[(ngModel)]='profile.options.readyTimeout',
|
||||||
)
|
)
|
||||||
|
|
||||||
.form-line(*ngIf='!profile.options.jumpHost && hostApp.platform !== Platform.Web')
|
|
||||||
.header
|
|
||||||
.title Use a proxy command
|
|
||||||
.description Command's stdin/stdout is used instead of a network connection
|
|
||||||
toggle([(ngModel)]='useProxyCommand')
|
|
||||||
|
|
||||||
.form-group(*ngIf='useProxyCommand && !profile.options.jumpHost')
|
|
||||||
label Proxy command
|
|
||||||
input.form-control(
|
|
||||||
type='text',
|
|
||||||
[(ngModel)]='profile.options.proxyCommand',
|
|
||||||
)
|
|
||||||
|
|
||||||
li(ngbNavItem)
|
li(ngbNavItem)
|
||||||
a(ngbNavLink) Ciphers
|
a(ngbNavLink) Ciphers
|
||||||
ng-template(ngbNavContent)
|
ng-template(ngbNavContent)
|
||||||
|
@ -16,7 +16,8 @@ export class SSHProfileSettingsComponent {
|
|||||||
Platform = Platform
|
Platform = Platform
|
||||||
profile: SSHProfile
|
profile: SSHProfile
|
||||||
hasSavedPassword: boolean
|
hasSavedPassword: boolean
|
||||||
useProxyCommand: boolean
|
|
||||||
|
connectionMode: 'direct'|'proxyCommand'|'jumpHost'|'socksProxy' = 'direct'
|
||||||
|
|
||||||
supportedAlgorithms = supportedAlgorithms
|
supportedAlgorithms = supportedAlgorithms
|
||||||
algorithms: Record<string, Record<string, boolean>> = {}
|
algorithms: Record<string, Record<string, boolean>> = {}
|
||||||
@ -43,7 +44,14 @@ export class SSHProfileSettingsComponent {
|
|||||||
this.profile.options.auth = this.profile.options.auth ?? null
|
this.profile.options.auth = this.profile.options.auth ?? null
|
||||||
this.profile.options.privateKeys ??= []
|
this.profile.options.privateKeys ??= []
|
||||||
|
|
||||||
this.useProxyCommand = !!this.profile.options.proxyCommand
|
if (this.profile.options.proxyCommand) {
|
||||||
|
this.connectionMode = 'proxyCommand'
|
||||||
|
} else if (this.profile.options.jumpHost) {
|
||||||
|
this.connectionMode = 'jumpHost'
|
||||||
|
} else if (this.profile.options.socksProxyHost) {
|
||||||
|
this.connectionMode = 'socksProxy'
|
||||||
|
}
|
||||||
|
|
||||||
if (this.profile.options.user) {
|
if (this.profile.options.user) {
|
||||||
try {
|
try {
|
||||||
this.hasSavedPassword = !!await this.passwordStorage.loadPassword(this.profile)
|
this.hasSavedPassword = !!await this.passwordStorage.loadPassword(this.profile)
|
||||||
@ -90,9 +98,18 @@ export class SSHProfileSettingsComponent {
|
|||||||
.map(([key, _]) => key)
|
.map(([key, _]) => key)
|
||||||
this.profile.options.algorithms![k].sort()
|
this.profile.options.algorithms![k].sort()
|
||||||
}
|
}
|
||||||
if (!this.useProxyCommand) {
|
|
||||||
|
if (this.connectionMode !== 'jumpHost') {
|
||||||
|
this.profile.options.jumpHost = undefined
|
||||||
|
}
|
||||||
|
if (this.connectionMode !== 'proxyCommand') {
|
||||||
this.profile.options.proxyCommand = undefined
|
this.profile.options.proxyCommand = undefined
|
||||||
}
|
}
|
||||||
|
if (this.connectionMode !== 'socksProxy') {
|
||||||
|
this.profile.options.socksProxyHost = undefined
|
||||||
|
this.profile.options.socksProxyPort = undefined
|
||||||
|
}
|
||||||
|
|
||||||
this.loginScriptsSettings?.save()
|
this.loginScriptsSettings?.save()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -104,4 +121,13 @@ export class SSHProfileSettingsComponent {
|
|||||||
onForwardRemoved (fw: ForwardedPortConfig) {
|
onForwardRemoved (fw: ForwardedPortConfig) {
|
||||||
this.profile.options.forwardedPorts = this.profile.options.forwardedPorts?.filter(x => x !== fw)
|
this.profile.options.forwardedPorts = this.profile.options.forwardedPorts?.filter(x => x !== fw)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getConnectionDropdownTitle () {
|
||||||
|
return {
|
||||||
|
direct: 'Direct',
|
||||||
|
proxyCommand: 'Proxy command',
|
||||||
|
jumpHost: 'Jump host',
|
||||||
|
socksProxy: 'SOCKS proxy',
|
||||||
|
}[this.connectionMode]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -37,6 +37,8 @@ export class SSHProfilesService extends ProfileProvider<SSHProfile> {
|
|||||||
proxyCommand: null,
|
proxyCommand: null,
|
||||||
forwardedPorts: [],
|
forwardedPorts: [],
|
||||||
scripts: [],
|
scripts: [],
|
||||||
|
socksProxyHost: null,
|
||||||
|
socksProxyPort: null,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import * as shellQuote from 'shell-quote'
|
import * as shellQuote from 'shell-quote'
|
||||||
|
import socksv5 from '@luminati-io/socksv5'
|
||||||
import { Duplex } from 'stream'
|
import { Duplex } from 'stream'
|
||||||
import { Injectable } from '@angular/core'
|
import { Injectable } from '@angular/core'
|
||||||
import { spawn } from 'child_process'
|
import { spawn } from 'child_process'
|
||||||
@ -52,6 +53,61 @@ export class SSHService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class SocksProxyStream extends Duplex {
|
||||||
|
private client: Duplex|null
|
||||||
|
private header: Buffer|null
|
||||||
|
|
||||||
|
constructor (private profile: SSHProfile) {
|
||||||
|
super({
|
||||||
|
allowHalfOpen: false,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async start (): Promise<void> {
|
||||||
|
this.client = await new Promise((resolve, reject) => {
|
||||||
|
const connector = socksv5.connect({
|
||||||
|
host: this.profile.options.host,
|
||||||
|
port: this.profile.options.port,
|
||||||
|
proxyHost: this.profile.options.socksProxyHost ?? '127.0.0.1',
|
||||||
|
proxyPort: this.profile.options.socksProxyPort ?? 5000,
|
||||||
|
auths: [socksv5.auth.None()],
|
||||||
|
}, s => {
|
||||||
|
resolve(s)
|
||||||
|
this.header = s.read()
|
||||||
|
this.push(this.header)
|
||||||
|
})
|
||||||
|
connector.on('error', (err) => {
|
||||||
|
reject(err)
|
||||||
|
this.destroy(err)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
this.client?.on('data', data => {
|
||||||
|
if (data !== this.header) {
|
||||||
|
// socksv5 doesn't reliably emit the first data event
|
||||||
|
this.push(data)
|
||||||
|
this.header = null
|
||||||
|
}
|
||||||
|
})
|
||||||
|
this.client?.on('close', (err) => {
|
||||||
|
this.destroy(err)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
_read (size: number): void {
|
||||||
|
this.client?.read(size)
|
||||||
|
}
|
||||||
|
|
||||||
|
_write (chunk: Buffer, _encoding: string, callback: (error?: Error | null) => void): void {
|
||||||
|
this.client?.write(chunk, callback)
|
||||||
|
}
|
||||||
|
|
||||||
|
_destroy (error: Error|null, callback: (error: Error|null) => void): void {
|
||||||
|
this.client?.destroy()
|
||||||
|
callback(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
export class ProxyCommandStream extends Duplex {
|
export class ProxyCommandStream extends Duplex {
|
||||||
private process: ChildProcess
|
private process: ChildProcess
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import socksv5 from 'socksv5'
|
import socksv5 from '@luminati-io/socksv5'
|
||||||
import { Server, Socket, createServer } from 'net'
|
import { Server, Socket, createServer } from 'net'
|
||||||
|
|
||||||
import { ForwardedPortConfig, PortForwardType } from '../api'
|
import { ForwardedPortConfig, PortForwardType } from '../api'
|
||||||
|
@ -12,7 +12,7 @@ import { BaseSession } from 'tabby-terminal'
|
|||||||
import { Socket } from 'net'
|
import { Socket } from 'net'
|
||||||
import { Client, ClientChannel, SFTPWrapper } from 'ssh2'
|
import { Client, ClientChannel, SFTPWrapper } from 'ssh2'
|
||||||
import { Subject, Observable } from 'rxjs'
|
import { Subject, Observable } from 'rxjs'
|
||||||
import { ProxyCommandStream } from '../services/ssh.service'
|
import { ProxyCommandStream, SocksProxyStream } from '../services/ssh.service'
|
||||||
import { PasswordStorageService } from '../services/passwordStorage.service'
|
import { PasswordStorageService } from '../services/passwordStorage.service'
|
||||||
import { promisify } from 'util'
|
import { promisify } from 'util'
|
||||||
import { SFTPSession } from './sftp'
|
import { SFTPSession } from './sftp'
|
||||||
@ -50,6 +50,7 @@ export class SSHSession extends BaseSession {
|
|||||||
forwardedPorts: ForwardedPort[] = []
|
forwardedPorts: ForwardedPort[] = []
|
||||||
jumpStream: any
|
jumpStream: any
|
||||||
proxyCommandStream: ProxyCommandStream|null = null
|
proxyCommandStream: ProxyCommandStream|null = null
|
||||||
|
socksProxyStream: SocksProxyStream|null = null
|
||||||
savedPassword?: string
|
savedPassword?: string
|
||||||
get serviceMessage$ (): Observable<string> { return this.serviceMessage }
|
get serviceMessage$ (): Observable<string> { return this.serviceMessage }
|
||||||
get keyboardInteractivePrompt$ (): Observable<KeyboardInteractivePrompt> { return this.keyboardInteractivePrompt }
|
get keyboardInteractivePrompt$ (): Observable<KeyboardInteractivePrompt> { return this.keyboardInteractivePrompt }
|
||||||
@ -231,6 +232,11 @@ export class SSHSession extends BaseSession {
|
|||||||
})
|
})
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
if (this.profile.options.socksProxyHost) {
|
||||||
|
this.emitServiceMessage(colors.bgBlue.black(' Proxy ') + ` Using ${this.profile.options.socksProxyHost}:${this.profile.options.socksProxyPort}`)
|
||||||
|
this.socksProxyStream = new SocksProxyStream(this.profile)
|
||||||
|
await this.socksProxyStream.start()
|
||||||
|
}
|
||||||
if (this.profile.options.proxyCommand) {
|
if (this.profile.options.proxyCommand) {
|
||||||
this.emitServiceMessage(colors.bgBlue.black(' Proxy command ') + ` Using ${this.profile.options.proxyCommand}`)
|
this.emitServiceMessage(colors.bgBlue.black(' Proxy command ') + ` Using ${this.profile.options.proxyCommand}`)
|
||||||
this.proxyCommandStream = new ProxyCommandStream(this.profile.options.proxyCommand)
|
this.proxyCommandStream = new ProxyCommandStream(this.profile.options.proxyCommand)
|
||||||
@ -262,7 +268,7 @@ export class SSHSession extends BaseSession {
|
|||||||
ssh.connect({
|
ssh.connect({
|
||||||
host: this.profile.options.host.trim(),
|
host: this.profile.options.host.trim(),
|
||||||
port: this.profile.options.port ?? 22,
|
port: this.profile.options.port ?? 22,
|
||||||
sock: this.proxyCommandStream ?? this.jumpStream,
|
sock: this.proxyCommandStream ?? this.jumpStream ?? this.socksProxyStream,
|
||||||
username: this.authUsername ?? undefined,
|
username: this.authUsername ?? undefined,
|
||||||
tryKeyboard: true,
|
tryKeyboard: true,
|
||||||
agent: this.agentPath,
|
agent: this.agentPath,
|
||||||
@ -279,9 +285,7 @@ export class SSHSession extends BaseSession {
|
|||||||
algorithms,
|
algorithms,
|
||||||
authHandler: (methodsLeft, partialSuccess, callback) => {
|
authHandler: (methodsLeft, partialSuccess, callback) => {
|
||||||
this.zone.run(async () => {
|
this.zone.run(async () => {
|
||||||
const a = await this.handleAuth(methodsLeft)
|
callback(await this.handleAuth(methodsLeft))
|
||||||
console.warn(a)
|
|
||||||
callback(a)
|
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
@ -2,6 +2,13 @@
|
|||||||
# yarn lockfile v1
|
# yarn lockfile v1
|
||||||
|
|
||||||
|
|
||||||
|
"@luminati-io/socksv5@^0.0.7":
|
||||||
|
version "0.0.7"
|
||||||
|
resolved "https://registry.yarnpkg.com/@luminati-io/socksv5/-/socksv5-0.0.7.tgz#87414177d473c97aaefa907a3fe454d62d2fceca"
|
||||||
|
integrity sha512-paEEbcstjMZb2SvFHsSUOzimkx80/pFmMG5T3XR6Keb4NeBfYWEAtlVeiF39OrHRf9AjpNxahhwzdCAlLXZ4Hw==
|
||||||
|
dependencies:
|
||||||
|
ipv6 "*"
|
||||||
|
|
||||||
"@types/node@*", "@types/node@16.0.1":
|
"@types/node@*", "@types/node@16.0.1":
|
||||||
version "16.0.1"
|
version "16.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-16.0.1.tgz#70cedfda26af7a2ca073fdcc9beb2fff4aa693f8"
|
resolved "https://registry.yarnpkg.com/@types/node/-/node-16.0.1.tgz#70cedfda26af7a2ca073fdcc9beb2fff4aa693f8"
|
||||||
@ -215,13 +222,6 @@ safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0:
|
|||||||
resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
|
resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
|
||||||
integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
|
integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
|
||||||
|
|
||||||
socksv5@^0.0.6:
|
|
||||||
version "0.0.6"
|
|
||||||
resolved "https://registry.yarnpkg.com/socksv5/-/socksv5-0.0.6.tgz#1327235ff7e8de21ac434a0a579dc69c3f071061"
|
|
||||||
integrity sha1-EycjX/fo3iGsQ0oKV53GnD8HEGE=
|
|
||||||
dependencies:
|
|
||||||
ipv6 "*"
|
|
||||||
|
|
||||||
sprintf@0.1.x:
|
sprintf@0.1.x:
|
||||||
version "0.1.5"
|
version "0.1.5"
|
||||||
resolved "https://registry.yarnpkg.com/sprintf/-/sprintf-0.1.5.tgz#8f83e39a9317c1a502cb7db8050e51c679f6edcf"
|
resolved "https://registry.yarnpkg.com/sprintf/-/sprintf-0.1.5.tgz#8f83e39a9317c1a502cb7db8050e51c679f6edcf"
|
||||||
|
@ -298,6 +298,7 @@ export class BaseTerminalTabComponent extends BaseTabComponent implements OnInit
|
|||||||
this.frontend.resize$.pipe(first()).subscribe(async ({ columns, rows }) => {
|
this.frontend.resize$.pipe(first()).subscribe(async ({ columns, rows }) => {
|
||||||
this.size = { columns, rows }
|
this.size = { columns, rows }
|
||||||
this.frontendReady.next()
|
this.frontendReady.next()
|
||||||
|
this.frontendReady.complete()
|
||||||
|
|
||||||
this.config.enabledServices(this.decorators).forEach(decorator => {
|
this.config.enabledServices(this.decorators).forEach(decorator => {
|
||||||
try {
|
try {
|
||||||
@ -554,6 +555,7 @@ export class BaseTerminalTabComponent extends BaseTabComponent implements OnInit
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
this.output.complete()
|
this.output.complete()
|
||||||
|
this.frontendReady.complete()
|
||||||
|
|
||||||
super.destroy()
|
super.destroy()
|
||||||
if (this.session?.open) {
|
if (this.session?.open) {
|
||||||
|
@ -84,7 +84,7 @@ Tabby.registerModule('crypto', {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
Tabby.registerMock('dns', {})
|
Tabby.registerMock('dns', {})
|
||||||
Tabby.registerMock('socksv5', {})
|
Tabby.registerMock('@luminati-io/socksv5', {})
|
||||||
Tabby.registerMock('util', require('util/'))
|
Tabby.registerMock('util', require('util/'))
|
||||||
Tabby.registerMock('keytar', {
|
Tabby.registerMock('keytar', {
|
||||||
getPassword: () => null,
|
getPassword: () => null,
|
||||||
|
@ -109,7 +109,7 @@ module.exports = options => {
|
|||||||
'os',
|
'os',
|
||||||
'path',
|
'path',
|
||||||
'readline',
|
'readline',
|
||||||
'socksv5',
|
'@luminati-io/socksv5',
|
||||||
'stream',
|
'stream',
|
||||||
'windows-native-registry',
|
'windows-native-registry',
|
||||||
'windows-process-tree',
|
'windows-process-tree',
|
||||||
|
Loading…
Reference in New Issue
Block a user