@wwkit/cft 1.0.18 → 1.0.20
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/bin/index.js +5 -2
- package/package.json +2 -2
- package/src/config.js +2 -0
- package/src/installer.js +5 -2
- package/src/proxy.js +0 -72
package/bin/index.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import { execSync } from 'node:child_process'
|
|
4
4
|
import { readFileSync } from 'node:fs'
|
|
5
|
-
import { install, status, clear, showConfig, showEnv } from '../src/index.js'
|
|
5
|
+
import { install, status, clear, showConfig, showEnv, getProxy } from '../src/index.js'
|
|
6
6
|
|
|
7
7
|
const pkgVersion = JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf8')).version
|
|
8
8
|
|
|
@@ -45,7 +45,10 @@ if (command === 'install') {
|
|
|
45
45
|
} else if (command === 'env') {
|
|
46
46
|
showEnv()
|
|
47
47
|
} else if (command === 'upgrade') {
|
|
48
|
-
|
|
48
|
+
const proxy = getProxy()
|
|
49
|
+
const npmArgs = ['install', '-g', '@wwkit/cft@latest']
|
|
50
|
+
if (proxy) npmArgs.push('--proxy', proxy)
|
|
51
|
+
execSync(`npm ${npmArgs.join(' ')}`, { stdio: 'inherit', shell: true })
|
|
49
52
|
} else if (command === 'version') {
|
|
50
53
|
console.log(pkgVersion)
|
|
51
54
|
} else if (command === 'help' || command === undefined) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wwkit/cft",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.20",
|
|
4
4
|
"author": "bluesliu <langcai163@163.com>",
|
|
5
5
|
"description": "Chrome for Testing (CFT) installer — download, extract, chmod, dep-check",
|
|
6
6
|
"type": "module",
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
},
|
|
37
37
|
"dependencies": {
|
|
38
38
|
"extract-zip": "^2.0.1",
|
|
39
|
-
"@wwkit/shared": "1.0.
|
|
39
|
+
"@wwkit/shared": "1.0.20"
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|
|
42
42
|
"jest": "^29.7.0"
|
package/src/config.js
CHANGED
|
@@ -9,6 +9,8 @@ const INSTALL_DIR = getXdgConfigDir('cft')
|
|
|
9
9
|
const _loader = createConfigLoader({
|
|
10
10
|
builtinConfigPath: path.join(__dirname, 'config.json5'),
|
|
11
11
|
userConfigDir: INSTALL_DIR,
|
|
12
|
+
envContentVar: 'CFT_CONFIG_CONTENT',
|
|
13
|
+
envDirVar: 'CFT_CONFIG_DIR',
|
|
12
14
|
})
|
|
13
15
|
|
|
14
16
|
export const INSTALL_LOCATION = INSTALL_DIR
|
package/src/installer.js
CHANGED
|
@@ -9,7 +9,7 @@ import { getOs, VALID_OS_TYPES } from './platform.js'
|
|
|
9
9
|
import { find as findExe } from './exefinder.js'
|
|
10
10
|
import { check as checkDeps, mapToPackages, install as installDeps, isSupported as depsSupported, detectPackageManager } from './depchecker.js'
|
|
11
11
|
import { getVersion, getRegistry, getProxy, getLocation, copyBuiltinConfig } from './config.js'
|
|
12
|
-
import { parseProxy, openTunnel } from '
|
|
12
|
+
import { parseProxy, openTunnel } from '@wwkit/shared'
|
|
13
13
|
|
|
14
14
|
const USER_AGENT = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/141.0.0.0 Safari/537.36 cft-installer'
|
|
15
15
|
const MANUAL_DOWNLOAD_HINT = '资源下载失败,请在 http://deepbluenet.com/webwork/#/mirror 手动下载资源,并将资源解压到 ~/.config/cft 目录,然后将地址添加到 ~/.config/webwork/webwork.json5 中的 env.SELENIUM_CFT_DIR 属性上'
|
|
@@ -75,6 +75,9 @@ function checkUrlReachable(url, proxyValue) {
|
|
|
75
75
|
response.resume()
|
|
76
76
|
if (response.statusCode >= 200 && response.statusCode < 400) {
|
|
77
77
|
resolve(true)
|
|
78
|
+
} else if (response.statusCode === 405) {
|
|
79
|
+
// 405 Method Not Allowed — 服务器已响应,仅不支持 HEAD
|
|
80
|
+
resolve(true)
|
|
78
81
|
} else {
|
|
79
82
|
resolve(false)
|
|
80
83
|
}
|
|
@@ -253,7 +256,7 @@ export async function install(opts = {}) {
|
|
|
253
256
|
const registry = getRegistry()
|
|
254
257
|
const location = getLocation()
|
|
255
258
|
const osType = getOs()
|
|
256
|
-
const proxy = opts.proxy
|
|
259
|
+
const proxy = opts.proxy || getProxy()
|
|
257
260
|
|
|
258
261
|
if (!VALID_OS_TYPES.includes(osType)) {
|
|
259
262
|
throw new Error(`Unsupported OS: ${osType}`)
|
package/src/proxy.js
DELETED
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
import net from 'node:net'
|
|
2
|
-
import http from 'node:http'
|
|
3
|
-
|
|
4
|
-
export function parseProxy(raw) {
|
|
5
|
-
if (!raw) return null
|
|
6
|
-
try {
|
|
7
|
-
const u = new URL(raw)
|
|
8
|
-
if (!u.hostname) return null
|
|
9
|
-
if (u.protocol !== 'http:' && u.protocol !== 'https:' && u.protocol !== 'socks5:' && u.protocol !== 'socks:') {
|
|
10
|
-
return null
|
|
11
|
-
}
|
|
12
|
-
return u
|
|
13
|
-
} catch {
|
|
14
|
-
return null
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
function socks5Connect(proxy, host, port) {
|
|
19
|
-
return new Promise((resolve, reject) => {
|
|
20
|
-
const sock = net.connect(Number(proxy.port) || 1080, proxy.hostname)
|
|
21
|
-
let phase = 'greeting'
|
|
22
|
-
const cleanup = (err) => { sock.removeAllListeners(); sock.destroy(); reject(err) }
|
|
23
|
-
sock.once('error', cleanup)
|
|
24
|
-
sock.once('connect', () => {
|
|
25
|
-
sock.write(Buffer.from([0x05, 0x01, 0x00]))
|
|
26
|
-
})
|
|
27
|
-
sock.on('data', (data) => {
|
|
28
|
-
if (phase === 'greeting') {
|
|
29
|
-
if (data[0] !== 0x05 || data[1] !== 0x00) { cleanup(new Error(`socks5 代理拒绝无认证 (method=${data[1]})`)); return }
|
|
30
|
-
phase = 'connect'
|
|
31
|
-
const hostBuf = Buffer.from(host, 'utf8')
|
|
32
|
-
const req = Buffer.alloc(7 + hostBuf.length)
|
|
33
|
-
req[0] = 0x05; req[1] = 0x01; req[2] = 0x00; req[3] = 0x03
|
|
34
|
-
req[4] = hostBuf.length
|
|
35
|
-
hostBuf.copy(req, 5)
|
|
36
|
-
req.writeUInt16BE(port, 5 + hostBuf.length)
|
|
37
|
-
sock.write(req)
|
|
38
|
-
} else if (phase === 'connect') {
|
|
39
|
-
if (data[0] !== 0x05 || data[1] !== 0x00) { cleanup(new Error(`socks5 连接失败 (code=${data[1]})`)); return }
|
|
40
|
-
sock.removeAllListeners('data')
|
|
41
|
-
sock.removeListener('error', cleanup)
|
|
42
|
-
resolve(sock)
|
|
43
|
-
}
|
|
44
|
-
})
|
|
45
|
-
})
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
function httpConnect(proxy, host, port) {
|
|
49
|
-
return new Promise((resolve, reject) => {
|
|
50
|
-
const req = http.request({
|
|
51
|
-
method: 'CONNECT',
|
|
52
|
-
host: proxy.hostname,
|
|
53
|
-
port: Number(proxy.port) || 80,
|
|
54
|
-
path: `${host}:${port}`,
|
|
55
|
-
})
|
|
56
|
-
req.once('error', reject)
|
|
57
|
-
req.once('connect', (res, socket) => {
|
|
58
|
-
if (res.statusCode !== 200) { socket.destroy(); reject(new Error(`HTTP 代理 CONNECT 返回 ${res.statusCode}`)); return }
|
|
59
|
-
resolve(socket)
|
|
60
|
-
})
|
|
61
|
-
req.end()
|
|
62
|
-
})
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
export async function openTunnel(proxy, target) {
|
|
66
|
-
const host = target.hostname
|
|
67
|
-
const port = Number(target.port) || (target.protocol === 'https:' ? 443 : 80)
|
|
68
|
-
if (proxy.protocol === 'socks5:' || proxy.protocol === 'socks:') {
|
|
69
|
-
return socks5Connect(proxy, host, port)
|
|
70
|
-
}
|
|
71
|
-
return httpConnect(proxy, host, port)
|
|
72
|
-
}
|